四元素理解

旋转变换_四元数

2017年03月29日 11:59:38 csxiaoshui 阅读数:5686

1.简介

四元数是另一种描述三维旋转的方式,四元数使用4个分量来描述旋转,四元数的描述方式如下:

 

q=s+xi+yj+zk,(s,x,y,z∈ℝ)i2=j2=k2=ijk=−1

 

四元数的由来和复数很很大的关系,因此首先讨论一下关于复数的内容。

1.1 复数

复数可以定义如下:

z=a+bia,b∈ℝi2=−1


复数常用的基本运算如下:

 

Complex
复数中一个比较重要的概念是共轭复数,将复数的虚部取相反数,得到它的共轭复数:

z=a+biz∗=a−bi


复数的模,定义为:
complex2

 

复数还可以使用复平面来表示,复平面分为实轴和虚轴(类似于二维直角坐标系中的x轴和y轴),如下图所示:

Complex3
当我们使用i去乘以一个复数时,当我们把得到的结果绘制在复平面上时,发现得到的位置正好是绕原点旋转90度的效果。
complex4
于是可以猜测,复数的乘法和旋转之间应该有某些关系。
我们可以通过定义一个复数

q=cosθ+isinθ


使用它作为一个旋转的因子,当与复数相乘时,得到:
complex5
写成矩阵的形式是:

[a′b′]=[cosθsinθ−sinθcosθ]∗[ab]


这个公式正好是二维的旋转公式,当把新的到的(a′+b′i)绘制在复平面上时,得到的正好是原来的点(a+bi)旋转θ角之后的位置。

 

2. 四元数

既然使用复数的乘法可以描述二维的旋转,那么拓展一个维度是否能表示三维旋转呢,这个也正是四元数发明者William Hamilton最初的想法,也就是说使用

z=a+ib+jci2=j2=−1


但是很遗憾 “三维的复数”(这仅仅是我按概念杜撰的一个词,并不存在)的乘法并不是闭合的。也就是说有可能两个值相乘得到的结果并不是三维的复数。
William Hamilton经历了无数个日日夜夜,他绞尽脑汁也没想明白这个问题。终于有一天(1843年的一天),他意识到自己所需要的运算在三维空间中是不可能实现的,但在四维空间中是可以的,他是如此的兴奋,以至于把四元数的公式刻在了爱尔兰的一座桥上。

 

quaternions

四元数可以写成下面的方式:

q=[s,v]s∈Rv∈ℝ3


或者写成

q=[s,xi+yj+zk]s,x,y,z∈ℝ

 

2.1 四元数的运算

  • 两四元数相加:
    A(a+bi+cj+dk) + B(e + fi + gj + hk) = C【 (a+e) + (b+f)i + (c+g)j + (d+h)k 】,实现代码:
      // Quat的成员_v[4]代表四元数的(x,y,z,w)inline Quat operator + (const Quat& rhs) const{return Quat(_v[0] + rhs._v[0],_v[1] + rhs._v[1],_v[2] + rhs._v[2],_v[3] + rhs._v[3]);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 两个四元数相减
    (sa,va) - (sb,vb) = (sa-sb,va-vb)
        inline Quat operator - (const Quat& rhs) const{return Quat(_v[0] - rhs._v[0],_v[1] - rhs._v[1],_v[2] - rhs._v[2],_v[3] - rhs._v[3]);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 两个四元数相乘
两个四元数相乘的规则和多项式乘法一样,
(a + i b + j c + k d)*(e + i f + j g + k h)
当有i,j,k参与时,规则如下:
i*i = j*j = k*k = -1
i*j = k,
j*i = -k
j*k = i,
k*j = -i
k*i = j,
i*k = -j
使用多项式乘法展开,可以得到:
a*e - b*f - c*g - d*h
+ i (b*e + a*f + c*h- d*g)
+ j (a*g - b*h+ c*e + d*f)
+ k (a*h + b*g - c*f + d*e)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

实现代码:

        inline const Quat operator*(const Quat& rhs) const{return Quat( rhs._v[3]*_v[0] + rhs._v[0]*_v[3] + rhs._v[1]*_v[2] - rhs._v[2]*_v[1],rhs._v[3]*_v[1] - rhs._v[0]*_v[2] + rhs._v[1]*_v[3] + rhs._v[2]*_v[0],rhs._v[3]*_v[2] + rhs._v[0]*_v[1] - rhs._v[1]*_v[0] + rhs._v[2]*_v[3],rhs._v[3]*_v[3] - rhs._v[0]*_v[0] - rhs._v[1]*_v[1] - rhs._v[2]*_v[2] );}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 两四元数相除
    四元数一般来说不定义除法,因为四元数的乘法运算并不满足交换律。一般有四元数的类定义除法是,其实定义的是q1∗q2−1

,其中
q−1=conj(q)/|q2|,为什么定义这么奇怪的表达式呢,其实是为了让q∗q−1=1

  • ,这个结论很容易推导出来。conj(q)称为q的共轭表达式,
    con(q) = w - xi - yj -zk,只需要四元数向量部分取负即可
    实现如下:
        inline const Quat operator/(const Quat& denom) const{return ( (*this) * denom.inverse() );}/// Conjugateinline Quat conj () const{return Quat( -_v[0], -_v[1], -_v[2], _v[3] );}/// Multiplicative inverse method: q^(-1) = q^*/(q.q^*)inline const Quat inverse () const{return conj() / length2();}value_type length2() const{return _v[0]*_v[0] + _v[1]*_v[1] + _v[2]*_v[2] + _v[3]*_v[3];}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2.2 四元数的旋转

四元数在三维图形学领域的一个重要应用是用它来描述三维旋转,四元数从某种意义上来说是四维空间的旋转,难以想象,了解它的结论和使用场景更加重要。欧拉定理告诉我们任意三维旋转都可以使用一个旋转向量和旋转角度来描述。因此四元数往往是使用旋转轴和旋转角来构造的,构造它的方法如下:


2.2.1 绕向量u旋转角度θ

构造四元数

可以用下面的四元数来表示:

u⃗ =(ux,uy,uz)=uxi+uyj+uzk

 

q=eθ2(uxi+uyj+uzk)=cosθ2+(uxi+uyj+uzk)sinθ2


实现代码如下:

 

   void makeRotate(value_type angle, value_type x, value_type y, value_type z){const value_type epsilon = 1e-7;value_type length = sqrt(x*x + y*y + z*z);if (length < epsilon){*this = Quat();return;}value_type  inversenorm = 1.0 / length;value_type  coshalfangle = cos(0.5*angle);value_type  sinhalfangle = sin(0.5*angle);_v[0] = x * sinhalfangle * inversenorm;_v[1] = y * sinhalfangle * inversenorm;_v[2] = z * sinhalfangle * inversenorm;_v[3] = coshalfangle;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2.2.2 从一个向量旋转到另一个向量构造四元数

按照最原始的想法,从一个向量旋转到另一个向量,那么旋转轴可以通过两个向量的叉乘得到,旋转角度可以通过两个向量间的夹角得到。(向量间的夹角的余弦可以通过两向量点乘去除以它们的模,再通过反余弦函数计算),得到旋转轴和旋转角度之后就转换成2.2.1中的情形了。
也就是说最初的代码如下:

void makeRotate(Vec3<value_type>& u,  Vec3<value_type>& v){u.normalize();v.normalize();double costheta = u*v;double angle = acos(costheta);Vec3<value_type> w = u^v;w.normalize();makeRotate(angle, w.x(), w.y(), w.z());}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

有一种特殊情况需要考虑:两向量共线(包括方向相同和方向相反,也就是夹角是0度和180度的情形)

void makeRotate(const Vec3<value_type>& from, const Vec3<value_type>& to){const value_type epsilon = 1e-7;value_type length1 = from.length();value_type length2 = to.length();value_type cosangle = from*to / (length1*length2);if (fabs(cosangle - 1) < epsilon){makeRotate(0.0, 0.0, 0.0, 1.0);}else if (fabs(cosangle + 1.0) < epsilon){Vec3<value_type> tmp;if ((fabs(from.x())) < fabs(from.y())){if (fabs(from.x()) < fabs(from.z())){tmp.set(1.0, 0.0, 0.0);}else{tmp.set(0.0, 0.0, 1.0);}}else if (fabs(from.y()) < fabs(from.z())){tmp.set(0.0, 1.0, 0.0);}else{tmp.set(0.0, 0.0, 1.0);}Vec3<value_type> fromd(from.x(), from.y(), from.z());Vec3<value_type> axis(fromd^tmp);axis.normalize();_v[0] = axis[0];_v[1] = axis[1];_v[2] = axis[2];_v[3] = 0.0;}else{Vec3<value_type> axis(from^to);value_type angle = acos(cosangle);makeRotate(angle, axis.x(), axis.y(), axis.z());}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

上述的代码改进了之前代码,但是在计算过程中使用了反三角函数(相对比较耗时),可以通过三角函数公式,简化,不需要调用反三角函数:

sinθ2cosθ2=1−cosθ2‾‾‾‾‾‾‾‾‾√=1+cosθ2‾‾‾‾‾‾‾‾‾√


代码可以修改为:

 

//省略部分相同的代码else{Vec3<value_type> axis(from^to);//替换成下面几行//value_type angle = acos(cosangle);//makeRotate(angle, axis.x(), axis.y(), axis.z());axis.normalize();value_type half_cos = sqrt(0.5*(1+cosangle));value_type half_sin = sqrt(0.5*(1-cosangle));_v[0] = axis[0] * half_sin;_v[1] = axis[1] * half_sin;_v[2] = axis[2] * half_sin;_v[3] = half_cos;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这样修改之后,去掉了算法中复杂的三角函数运算,事实上还可以进一步改进计算过程,考虑到代码中多次的归一化(normalize)的操作,需要进行多次开方运算,为了简化,可以考虑:

||u×v||=|u|.|v|.|sinθ|

 

 

sinθ=2sinθ2cosθ2

 

同时有:

sqrt(a)sqrt(b)=sqrt(ab)

 

于是代码可以修改为:

            else{value_type normFromAndTo = sqrt(from.length2()*to.length2());value_type cos_theta = from * to / normFromAndTo;value_type half_cos = sqrt(0.5 * (1+cos_theta));value_type half_sin = sqrt(0.5 * (1-cos_theta));Vec3<value_type> axis = from^to / (normFromAndTo*2*half_sin * half_cos);_v[0] = axis[0]*half_sin;_v[1] = axis[1]*half_sin;_v[2] = axis[2]*half_sin;_v[3] = half_cos;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注意到_v[0]到_v[3]中乘以half_sin,之前axis计算的分母中就有half_sin,也就是说这一项可以被化简掉,于是代码简化成:

        else{value_type normFromAndTo = sqrt(from.length2()*to.length2());value_type cos_theta = from * to / normFromAndTo;value_type half_cos = sqrt(0.5 * (1+cos_theta));Vec3<value_type> axis = from^to / (normFromAndTo*2 * half_cos);_v[0] = axis[0];_v[1] = axis[1];_v[2] = axis[2];_v[3] = half_cos;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.2.3 从四元数获取旋转矩阵和旋转角

这个过程是上面的反过程,根据之前描述的公式反算就可以,得到的公式是:

 //设四元数是 xi+yj+zk+w,那么旋转角度和旋转轴(a,b,c)是:
angle = 2 * acos(w)
a = x / sqrt(1-w*w)
b = y / sqrt(1-w*w)
c = z / sqrt(1-w*w)
  • 1
  • 2
  • 3
  • 4
  • 5

推导过程如下:
有之前的公式可以知道: w=cos(θ/2)

可以得到 角度 θ=2∗acos(w)

 

x=a∗sin(θ/2)y=b∗sin(θ/2)z=c∗sin(θ/2)


先分析x这个等式,带入求出的θ角,得到:

a=x/sin(acos(w))

,参考下图:
sin(acosw)
得到 sin(acosw) = sqrt(1-w*w),同理可以推出其他的结论。
但是还需要考虑其他两个特殊情况:也就是共线的情形(角度θ是0度或者180度)

 

  • 0度的情况:
    当时0度的时候,得到w=1,会导致计算公式中分母是0,除以0出现无穷大,因此需要单独讨论

  • 180度的情况
    当180度是 w=0,可以通过计算得到
    a = x, b=y,c=z
    计算过程是正确的,因此这种情况不需要特殊的去分析。
    综合上面整体的描述,代码如下:

        void getRotate(value_type& angle, value_type& x, value_type& y, value_type& z) const {Quat q1 = *this;if (_v[3] > 1)q1.normalize();angle = 2 * acos(q1.w());value_type s = sqrt(1 - q1.w()*q1.w());if (s < 1e-6){x = q1.x();y = q1.y();z = q1.z();}else{x = q1.x() / s;y = q1.y() / s;z = q1.z() / s;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

2.3 向量使用四元数进行旋转

这么辛苦写了四元数的类,为的就是使用它对顶点和向量进行旋转的操作,也就是说我们需要完成下面的函数实现:

        //Rotate a vector by this quaternionVec3<value_type> operator* (const Vec3<value_type>& v);
  • 1
  • 2

四元数变换向量的算法如下:
1. 创建一个以v为虚部的纯虚的向量,(v.x + v.y + v.z + 0)
2. 左乘四元数 q 接着右乘四元数q的共轭四元数 q*
3. 计算得到的结果也是一个纯的四元数,它的虚部就是变换之后的向量v’
尽管这样做可以得到变换后的向量,如果计算过程完全按照四元数乘法法则去展开计算,计算量略大 ,可以使用下面的方式优化一下:

        Vec3<value_type> operator* (const Vec3<value_type>& v){// nVidia SDK implementationVec3<value_type> uv, uuv;Vec3<value_type> qvec(_v[0], _v[1], _v[2]);uv = qvec ^ v;uuv = qvec ^ uv;uv *= ( 2.0f * _v[3] );uuv *= 2.0f;return v + uv + uuv;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.4 四元数的插值

使用四元数来表示旋转,在插值时非常的方便和平滑,如果使用欧拉角来进行插值运算,除了会出现万向节死锁外,插值的效果显得十分的生硬。四元数的球面插值使用下面的公式:

这里写图片描述

其中:
- qm:插值的四元数
- qa: 插值四元数的第一个值(起点)
- qb:插值四元数的第二个值(终点)
- t: (0.0,1.0)之间的一个数
- θ

: qa和qb夹角的一半

实现如下:

void dslerp( value_type t, const Quat& from, const Quat& to )
{const double epsilon = 0.00001;double omega, cosomega, sinomega, scale_from, scale_to ;osg::Quat quatTo(to);// this is a dot productcosomega = from.asVec4() * to.asVec4();if ( cosomega <0.0 ){cosomega = -cosomega;quatTo = -to;}if( (1.0 - cosomega) > epsilon ){omega= acos(cosomega) ;  // 0 <= omega <= Pi (see man acos)sinomega = sin(omega) ;  // this sinomega should always be +ve so// could try sinomega=sqrt(1-cosomega*cosomega) to avoid a sin()?scale_from = sin((1.0-t)*omega)/sinomega ;scale_to = sin(t*omega)/sinomega ;}else{/* --------------------------------------------------The ends of the vectors are very closewe can use simple linear interpolation - no needto worry about the "spherical" interpolation-------------------------------------------------- */scale_from = 1.0 - t ;scale_to = t ;}*this = (from*scale_from) + (quatTo*scale_to);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

参考文献:

  1. Understanding Quaternions
  2. 如何形象地理解四元数?知乎Yang Eninala的解答
  3. Quaternion
  4. Quaternion
  5. Maths - Quaternions
  6. Beautiful maths simplification: quaternion from two vectors
  7. Rotating vector3 by a quaternion
  8. quaternion vector product四元数变换向量算法的原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251989.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

31、SAM文件中flag含义解释工具--转载

转载&#xff1a;http://www.cnblogs.com/nkwy2012/p/6362996.html SAM是Sequence Alignment/Map 的缩写。像bwa等软件序列比对结果都会输出这样的文件。samtools网站上有专门的文档介绍SAM文件。具体地址&#xff1a;http://samtools.sourceforge.net/SAM1.pdf很多人困惑SAM文…

《Head First设计模式》批注系列(一)——观察者设计模式

最近在读《Head First设计模式》一书&#xff0c;此系列会引用源书内容&#xff0c;但文章内容会更加直接&#xff0c;以及加入一些自己的理解。 观察者模式&#xff08;有时又被称为模型-视图&#xff08;View&#xff09;模式、源-收听者(Listener)模式或从属者模式&#xff…

PYPL 4 月排行:Python 最流行,Java 还行不行?

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; PYPL 发布了 4 月份的编程语言排行榜。 前五的分别是&#xff1a;Python、Java、Javascript、C# 和 PHP。可以看到&#xff0c;榜单没有什么大变化&#xff0c;但是相比去年 4 月份&#xff0c;…

顺序表

一、数据是如何在内存中存储的&#xff1f; 32位系统中char&#xff0c;int型数据在内存中的存储方式&#xff1a; char占1byte&#xff08;8bit&#xff09;int占4byte&#xff08;32bit&#xff09;假设我们有一个int类型的值&#xff0c;它从0x01开始&#xff0c;一个int占据…

四元素的真面目..........简单粗暴

作者&#xff1a;Yang Eninala 链接&#xff1a;https://www.zhihu.com/question/23005815/answer/33971127 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 根据我的理解&#xff0c;大多数人用汉密尔顿四元数就只…

Linemod;理解

Linemod 代码笔记 2019年03月11日 16:18:30 haithink 阅读数&#xff1a;197 最近了解到 Linemod 这个模板匹配算法&#xff0c;印象不错 准备仔细学习一下&#xff0c;先做点代码笔记&#xff0c;免得后面不好回顾 目前的笔记基本上把 核心流程都分析得比较清楚了&#xff0…

手眼标定

Eye-in-hand和Eye-to-hand问题求解和实验 2018年12月07日 00:00:40 百川木易 阅读数 3018 2018/12/5 By Yang Yang&#xff08;yangyangipp.ac.cn&#xff09; 本文所有源码和仿真场景文件全部公开&#xff0c;点击Gitee仓库链接。 文章目录 问题描述Eye-in-hand问题求解公式…

RNN总结

RNN既可以表述为循环神 经网络&#xff08;recurrent neural network&#xff09;&#xff0c;也可以表述为递归神经网络&#xff08;recursive neural network&#xff09;&#xff0c;前者一般用于处理以时间序列为输入的问题&#xff08;比如把一个句子看成词组成的序列&…

linux硬链接与软链接

Linux 系统中有软链接和硬链接两种特殊的“文件”。 软链接可以看作是Windows中的快捷方式&#xff0c;可以让你快速链接到目标档案或目录。 硬链接则透过文件系统的inode来产生新档名&#xff0c;而不是产生新档案。 创建方法都很简单&#xff1a; 软链接&#xff08;符号链接…

企业级区块链现状研究报告:小企业的投资总额是大企业的28倍

根据企业级区块链现状研究报告表明&#xff0c;当前企业采用区块链技术的势头正在逐步增强。参与该报告的企业表示&#xff0c;区块链投资今年共增长了 62% &#xff0c;预计到 2025 年区块链将成为主流技术。其中&#xff0c;有 28% 的企业正在积极开展区块链发展计划。现在看…

特征匹配

Python 使用Opencv实现图像特征检测与匹配 2018-06-13 11:36:58 Xy-Huang 阅读数 19203更多 分类专栏&#xff1a; Python 人工智能 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 本文链接…

bzoj 1015 并查集

代码&#xff1a; //这题可以反着想&#xff0c;把要去掉的点倒着处理变成往图中一个一个的加点&#xff0c;然后用并查集处理联通快就好了。 #include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const in…

画极线

OpenCV学习日记5 2017-05-27 10:44:35 1000sprites 阅读数 2339更多 分类专栏&#xff1a; 计算机视觉 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 本文链接&#xff1a;https://blog.cs…

最近很火的MySQL:抛开复杂的架构设计,MySQL优化思想基本都在这

优化一览图 优化 笔者将优化分为了两大类&#xff1a;软优化和硬优化。软优化一般是操作数据库即可&#xff1b;而硬优化则是操作服务器硬件及参数设置。 1、软优化 1&#xff09;查询语句优化 首先我们可以用EXPLAIN或DESCRIBE(简写:DESC)命令分析一条查询语句的执行信息。 例…

《JAVA与模式》之桥梁模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述桥梁&#xff08;Bridge&#xff09;模式的&#xff1a; 桥梁模式是对象的结构模式。又称为柄体(Handle and Body)模式或接口(Interface)模式。桥梁模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦&#xff0…

Java基础教程:面向对象编程[2]

Java基础教程&#xff1a;面向对象编程[2] 内容大纲 访问修饰符 四种访问修饰符 Java中&#xff0c;可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。 default (即缺省&#xff0c;什么也不写&#xff09;: 在同一包内可见&#xff…

【javascript】异步编年史,从“纯回调”到Promise

异步和分块——程序的分块执行 一开始学习javascript的时候&#xff0c; 我对异步的概念一脸懵逼&#xff0c; 因为当时百度了很多文章&#xff0c;但很多各种文章不负责任的把笼统的描述混杂在一起&#xff0c;让我对这个 JS中的重要概念难以理解&#xff0c; “异步是非阻塞的…

if _name_ == _main_

1.作用 py文件有2种使用方法&#xff0c;第1是自己本脚本自己独立执行&#xff1b;第2是被import到其他文件脚本中执行. if _name_ " _main_" 该语句控制其他下一步的脚本是否执行。如果是自己本脚本独立执行&#xff0c;那就运行该if条件下的脚本&#xff1b;如果…

LLVM完整参考安装

文章目录 一、直接下载编译好的,见图片命令二、下载源代码自己编译安装 下面提供下载并mv完全的文件包三、安装LLVM编译器一、直接下载编译好的,见图片命令 这里使用llvm官网编译好的包, 直接解压即可用LLVM下载官网点击这里下载llvm-6.0.1 下载完成后解压tar -vxf clangllv…

微软正式释出基于 Chromium 的 Edge 预览版本

百度智能云域名服务&#xff0c;.com新用户首购仅需25元 微软基于 Chromium 的全新版本 Edge 一直吸引着开发者与用户的目光&#xff0c;当地时间 8 日&#xff0c;官方终于释出了第一个 Dev 和 Canary 频道构建版本。 Dev 与 Canary build 都是开发者预览版&#xff0c;同属…