计算机图形学——二维变换

news2024/9/22 17:30:04

二维变换

概念

应用于对象几何描述并改变其位置、方向或者大小的变换叫做几何变换,有时候也被叫做建模变换。而本文仅讨论平面中的几何变换,即二维变换。

矩阵表示和齐次坐标

对于普通的2x2矩阵,我们总是要将平移项与其它变换对应的矩阵写成不同规格,为了统一形式且方便运算,我们需要将2x2的矩阵扩展到3x3。此时,二为坐标必须用三元向量来表示。标准实现技术是将二维坐标 ( x , y ) (x,y) (x,y)扩充到三维 ( x h , y h , h ) (x_h,y_h,h) (xh,yh,h),这称为齐次坐标,这个过程就被叫做齐次化

对于每一个维度有
x = x h h , y = y h h x = \frac{x_h}{h},y=\frac{y_h}{h} x=hxh,y=hyh
其中非零值 h h h被称为齐次参数

显然对于齐次参数,可以有无数个非零值,同样也意味着有无数个等价的齐次表达式。既然如此,为了方便计算,不妨令 h = 1 h=1 h=1

平移变换

对于平移变换,我们可有参数方程
{ x ′ = x + δ x y ′ = y + δ y \begin{cases} x^{'}=x+\delta x \\ y^{'} = y+\delta y \end{cases} {x=x+δxy=y+δy
我们将方程组转换为矩阵的形式
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 0 0 0 1 0 δ x δ y 1 ] \begin{bmatrix} x'& y'& 1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0\\ 0& 1& 0\\ \delta x& \delta y & 1 \end{bmatrix} [xy1]=[xy1] 10δx01δy001

旋转变换

对于一个复杂的图形的旋转,我们可以看做是多个点在同时旋转。因此只需要研究出一个点的旋转变换方法就可以了。

我们不妨令点为平面任意一点,其绕原点进行旋转变换,该点的运动轨迹一定是一个以该点到原点连线为半径的圆弧。那么问题就简单了,对于旋转任意角度,我们只需要用圆的参数方程就能搞定,
{ x = cos ⁡ θ y = sin ⁡ θ \begin{cases} x = \cos{\theta} \\ y = \sin{\theta} \end{cases} {x=cosθy=sinθ
对于我们假设旋转了 α \alpha α的弧度(逆时针为正方向),则有
{ x ′ = cos ⁡ ( θ + α ) = cos ⁡ θ cos ⁡ α − sin ⁡ θ sin ⁡ α = x cos ⁡ α − y sin ⁡ α y ′ = sin ⁡ ( θ + α ) = sin ⁡ θ cos ⁡ α + cos ⁡ θ sin ⁡ α = x sin ⁡ α + y cos ⁡ α \begin{cases} x' = \cos{(\theta+\alpha)}=\cos{\theta}\cos{\alpha}-\sin{\theta}\sin{\alpha}=x\cos{\alpha}-y\sin{\alpha} \\ y' = \sin{(\theta+\alpha)}=\sin{\theta}\cos{\alpha}+\cos{\theta}\sin{\alpha}=x\sin{\alpha}+y\cos{\alpha} \end{cases} {x=cos(θ+α)=cosθcosαsinθsinα=xcosαysinαy=sin(θ+α)=sinθcosα+cosθsinα=xsinα+ycosα
很显然对应矩阵形式为
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ cos ⁡ α sin ⁡ α 0 − sin ⁡ α cos ⁡ α 0 0 0 1 ] \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} \cos{\alpha}& \sin{\alpha}& 0 \\ -\sin{\alpha}& \cos{\alpha}& 0 \\ 0& 0& 1 \end{bmatrix} [xy1]=[xy1] cosαsinα0sinαcosα0001
那么问题来了,如果所绕的旋转点不是原点怎么办呢?

前面已经讲过平移变换了,只需要平移坐标系原点至该点(移轴),再进行旋转,最后平移回去就行了。

不妨令被围绕点坐标为 P ( x 1 , y 1 ) P(x_1,y_1) P(x1,y1),则该流程的矩阵运算如下:
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 0 0 0 1 0 − x 1 − y 1 1 ] ⋅ [ cos ⁡ α sin ⁡ α 0 − sin ⁡ α cos ⁡ α 0 0 0 1 ] ⋅ [ 1 0 0 0 1 0 x 1 y 1 1 ] \begin{bmatrix} x'& y'& 1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0\\ 0& 1& 0\\ -x_1& -y_1 & 1 \end{bmatrix} \cdot \begin{bmatrix} \cos{\alpha}& \sin{\alpha}& 0 \\ -\sin{\alpha}& \cos{\alpha}& 0 \\ 0& 0& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0\\ 0& 1& 0\\ x_1& y_1 & 1 \end{bmatrix} [xy1]=[xy1] 10x101y1001 cosαsinα0sinαcosα0001 10x101y1001
如果你对平移坐标系难以理解,不妨将坐标轴与平面想象成两个分离的东西。以坐标轴往右移动为例,则对于平面上的点来讲,就相当于坐标轴不动点向左平移。(平移坐标系只是移动的轴,不带平面上其他点,否则你会得到相反的结果!)

缩放变换

首先给出参数方程
{ x ′ = s x x y ′ = s y y \begin{cases} x' = s_xx \\ y' = s_yy \end{cases} {x=sxxy=syy
对应矩阵运算为
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ s x 0 0 0 s y 0 0 0 1 ] \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} s_x& 0& 0 \\ 0& s_y& 0 \\ 0& 0& 1 \end{bmatrix} [xy1]=[xy1] sx000sy0001

对称变换

关于对称变换可以是轴对称或者点对称

我们先来看关于y轴对称:只需要纵坐标不变,横坐标取相反数即可。

矩阵运算如下
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ − 1 0 0 0 1 0 0 0 1 ] \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} -1& 0& 0 \\ 0& 1& 0 \\ 0& 0& 1 \end{bmatrix} [xy1]=[xy1] 100010001
同理可得关于y轴对称矩阵运算
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 0 0 0 − 1 0 0 0 1 ] \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0 \\ 0& -1& 0 \\ 0& 0& 1 \end{bmatrix} [xy1]=[xy1] 100010001
那么对于原点对称就有
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ − 1 0 0 0 − 1 0 0 0 1 ] \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} -1& 0& 0 \\ 0& -1& 0 \\ 0& 0& 1 \end{bmatrix} [xy1]=[xy1] 100010001

如果是对于平面中任意一点 P ( x 1 , y 1 ) P(x_1,y_1) P(x1,y1)对称,则平移坐标系原点至该点,然后进行关于原点的对称,再平移回去就行。
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 0 0 0 1 0 − x 1 − y 1 1 ] ⋅ [ − 1 0 0 0 − 1 0 0 0 1 ] ⋅ [ 1 0 0 0 1 0 x 1 y 1 1 ] \begin{bmatrix} x'& y'& 1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0\\ 0& 1& 0\\ -x_1& -y_1 & 1 \end{bmatrix} \cdot \begin{bmatrix} -1& 0& 0 \\ 0& -1& 0 \\ 0& 0& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0\\ 0& 1& 0\\ x_1& y_1 & 1 \end{bmatrix} [xy1]=[xy1] 10x101y1001 100010001 10x101y1001
如果是关于平面内任意一条直线对称,那比较麻烦了,你需要先平移坐标系到直线的一点,并旋转坐标系使x轴与直线重合,然后进行关于x轴的对称变换,再旋转回去,然后再平移回去。

特别地,如果直线斜率不存在,平移后直线与y轴重合,那么直接进行关于y轴对称然后再平移回去即可。

注意该过程的顺序,因为矩阵并不满足交换律!

这里实际上是有两个大坑的,我们不妨假设直线经过的两点分别为
A ( x 1 , y 1 ) , B ( x 2 , y 2 ) A(x_1,y_1),B(x_2,y_2) A(x1,y1),B(x2,y2)
不失一般性,我们令
x 1 < = x 2 x_1<=x_2 x1<=x2
我们在上面已经说过直线斜率不存在的情况了,这里仅讨论直线斜率存在的情况。直线斜率为
k = δ x δ y = y 2 − y 1 x 2 − x 1 k = \frac{\delta x}{\delta y}=\frac{y_2-y_1}{x_2-x_1} k=δyδx=x2x1y2y1
我们想要得到直线与x轴的夹角,可以利用反正切函数
θ = arctan ⁡ k \theta = \arctan{k} θ=arctank
但是事实就是如此吗?注意反正切函数的取值范围为 ( − π 2 , π 2 ) (-\frac{\pi}{2},\frac{\pi}{2}) (2π,2π),而直线倾斜角范围是 [ 0 , π ] [0,\pi] [0,π],即使挖去了 π 2 \frac{\pi}{2} 2π这个点(因为我们不讨论这种情况),范围仍不一致!

我们记倾斜角为 α \alpha α,则有
α = { θ , 0 ≤ θ < π 2 π + θ , θ < 0 \alpha= \begin{cases} \theta,& 0\le \theta<\frac{\pi}{2} \\ \pi+\theta, &\theta<0 \end{cases} α={θ,π+θ,0θ<2πθ<0
现在开始进入第二个坑了,我们进行旋转倾斜角的时候,是顺时针还是逆时针?

我们不妨站在坐标系的角度来看,逆时针旋转倾斜角的角度,就相当于顺时针旋转图形这个角度。

也就是说,我们在进行第一次旋转变换时输入的角度参数应该为 − α -\alpha α

综上所述,矩阵运算为
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 0 0 0 1 0 − x 1 − y 1 1 ] ⋅ [ cos ⁡ α − sin ⁡ α 0 sin ⁡ α cos ⁡ α 0 0 0 1 ] ⋅ [ 1 0 0 0 − 1 0 0 0 1 ] ⋅ [ cos ⁡ α sin ⁡ α 0 − sin ⁡ α cos ⁡ α 0 0 0 1 ] ⋅ [ 1 0 0 0 1 0 x 1 y 1 1 ] \begin{bmatrix} x'& y'& 1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0\\ 0& 1& 0\\ -x_1& -y_1 & 1 \end{bmatrix} \cdot \begin{bmatrix} \cos{\alpha}& -\sin{\alpha}& 0 \\ \sin{\alpha}& \cos{\alpha}& 0 \\ 0& 0& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0 \\ 0& -1& 0 \\ 0& 0& 1 \end{bmatrix} \cdot \begin{bmatrix} \cos{\alpha}& \sin{\alpha}& 0 \\ -\sin{\alpha}& \cos{\alpha}& 0 \\ 0& 0& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0\\ 0& 1& 0\\ x_1& y_1 & 1 \end{bmatrix} [xy1]=[xy1] 10x101y1001 cosαsinα0sinαcosα0001 100010001 cosαsinα0sinαcosα0001 10x101y1001

错切变换

错切变换实际上是物体在投影平面上非垂直投影的结果。一般为水平错切和垂直错切,也可以同时对两个方向进行错切。

我们先来看看沿y轴方向的错切

显然有方程
{ x ′ = x y ′ = y + x tan ⁡ θ \begin{cases} x' = x \\ y' = y+x\tan{\theta} \end{cases} {x=xy=y+xtanθ
有矩阵运算
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 tan ⁡ θ 0 0 1 0 0 0 1 ] \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& \tan{\theta}& 0 \\ 0& 1& 0 \\ 0& 0& 1 \end{bmatrix} [xy1]=[xy1] 100tanθ10001
同理可得沿着x方向的错切矩阵运算
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 0 0 tan ⁡ θ 1 0 0 0 1 ] \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& 0& 0 \\ \tan{\theta}& 1& 0 \\ 0& 0& 1 \end{bmatrix} [xy1]=[xy1] 1tanθ0010001
同时沿着x方向和y方向的错切
[ x ′ y ′ 1 ] = [ x y 1 ] ⋅ [ 1 tan ⁡ α 0 tan ⁡ θ 1 0 0 0 1 ]   α , θ ∈ ( − π 2 , π 2 ) \begin{bmatrix} x'&y'&1 \end{bmatrix} = \begin{bmatrix} x& y& 1 \end{bmatrix} \cdot \begin{bmatrix} 1& \tan{\alpha}& 0 \\ \tan{\theta}& 1& 0 \\ 0& 0& 1 \end{bmatrix} \space \alpha,\theta \in (-\frac{\pi}{2},\frac{\pi}{2}) [xy1]=[xy1] 1tanθ0tanα10001  α,θ(2π,2π)

刚体变换

如果一个矩阵仅包含平移参数和旋转参数,那么它就是一个刚体变换矩阵
$$
\begin{bmatrix}
x’& y’& 1
\end{bmatrix}

\begin{bmatrix}
x& y& 1
\end{bmatrix}
\cdot
\begin{bmatrix}
r_{xx}& r_{yx}& 0\
r_{xy}& r_{yy}& 0\
tr_{x}& tr_{y}& 1
\end{bmatrix}
$$

对于刚体变换左上角的2x2矩阵满足正交矩阵的特性。也就是说对于两个列向量 [ r x x r x y ] T \begin{bmatrix}r_{xx}& r_{xy} \end{bmatrix}^{T} [rxxrxy]T [ r y x r y y ] T \begin{bmatrix}r_{yx}& r_{yy} \end{bmatrix}^{T} [ryxryy]T(或者两个行向量)形成单位向量的正交组,这样的向量也称为正交向量组。

对于每个向量具有单位长度,并且数量积为0:
r x x 2 + r x y 2 = r y x 2 + r y y 2 = 1 r x x r y x + r x y r y y = 0 r_{xx}^2+r_{xy}^2=r_{yx}^2+r_{yy}^2=1 \\ r_{xx}r_{yx}+r_{xy}r_{yy} = 0 rxx2+rxy2=ryx2+ryy2=1rxxryx+rxyryy=0
如果这些向量通过旋转矩阵进行变换,则可得出x方向和y方向的单位向量
[ r x x r x y 1 ] ⋅ [ r x x r y x 0 r x y r y y 0 0 0 1 ] = [ 1 0 1 ] \begin{bmatrix} r_{xx}& r_{xy}& 1 \end{bmatrix} \cdot \begin{bmatrix} r_{xx}& r_{yx}& 0\\ r_{xy}& r_{yy}& 0\\ 0& 0& 1 \end{bmatrix} =\begin{bmatrix} 1& 0& 1 \end{bmatrix} [rxxrxy1] rxxrxy0ryxryy0001 =[101]

[ r y x r y y 1 ] ⋅ [ r x x r y x 0 r x y r y y 0 0 0 1 ] = [ 0 1 1 ] \begin{bmatrix} r_{yx}& r_{yy}& 1 \end{bmatrix} \cdot \begin{bmatrix} r_{xx}& r_{yx}& 0\\ r_{xy}& r_{yy}& 0\\ 0& 0& 1 \end{bmatrix} =\begin{bmatrix} 0& 1& 1 \end{bmatrix} [ryxryy1] rxxrxy0ryxryy0001 =[011]

代码

对于二维变换的代码相对来讲较麻烦一些,我不太喜欢OpenGL所带的矩阵运算操作,因此我自己写了一套简单的矩阵模版类。由于代码相对较长,我这里仅放出这几个变换的代码片段。

class Polygon
{
public:
    Transform::Matrix<GLdouble> *vex;

    Polygon(Pts vex)
    {
        this->vex = new Transform::Matrix<GLdouble>(vex.size(), 3, {{}}, 0);
        for (int i = 0; i < vex.size(); i++)
        {
            (*this->vex)[i][0] = vex[i].x;
            (*this->vex)[i][1] = vex[i].y;
            (*this->vex)[i][2] = 1;
        }
    }
    ~Polygon()
    {
        delete vex;
    }

    Polygon *Translate(const Point &end)
    {
        vector<vector<GLdouble>> temp{{1, 0, 0}, {0, 1, 0}, {end.x, end.y, 1}};
        Transform::Matrix<GLdouble> t(3, 3, temp);
        (*vex) = (*vex) * t;
        return this;
    }

    Polygon *Draw()
    {
        // cout << "Start" << endl;
        glBegin(GL_POLYGON);
        for (GLint i = 0; i < vex->CountRow(); i++)
        {
            // cout << i << ": " << (*vex)[i][0] << " " << (*vex)[i][1] << endl;
            glVertex2d((*vex)[i][0], (*vex)[i][1]);
        }
        glEnd();

        return this;
    }

    Polygon *Rotate(Point p, GLdouble radian)
    {
        Transform::Matrix<GLdouble> t1(3, 3, {{1, 0, 0}, {0, 1, 0}, {-p.x, -p.y, 1}});
        Transform::Matrix<GLdouble> t2(3, 3, {{cos(radian), sin(radian), 0}, {-sin(radian), cos(radian), 0}, {0, 0, 1}});

        Transform::Matrix<GLdouble> t3(3, 3, {{1, 0, 0}, {0, 1, 0}, {p.x, p.y, 1}});

        (*vex) = (*vex) * t1 * t2 * t3;
        return this;
    }

    Polygon *Scale(GLdouble x, GLdouble y)
    {
        if (x <= 0 || y <= 0)
        {
            cout << "WARNING: Invalid parameters" << endl;
            return this;
        }

        Transform::Matrix<GLdouble> t(3, 3, {
                                                {x, 0, 0},
                                                {0, y, 0},
                                                {0, 0, 1},
                                            });

        (*vex) = (*vex) * t;

        return this;
    }

    Polygon *PointReflect(const Point &p)
    {
        Transform::Matrix<GLdouble> t1(3, 3, {{1, 0, 0}, {0, 1, 0}, {-p.x, -p.y, 1}});
        Transform::Matrix<GLdouble> t2(3, 3, {
                                                 {-1, 0, 0},
                                                 {0, -1, 0},
                                                 {0, 0, 1},
                                             });
        Transform::Matrix<GLdouble> t3(3, 3, {{1, 0, 0}, {0, 1, 0}, {p.x, p.y, 1}});

        *vex = (*vex) * t1 * t2 * t3;
        return this;
    }

    Polygon *LineReflect(const Point &start, const Point &end, bool showAxis = false)
    {
        GLdouble x1 = start.x, x2 = end.x, y1 = start.y, y2 = end.y;
        if (x1 > x2)
            swap(x1, x2), swap(y1, y2);

        if (showAxis)
        {
            glBegin(GL_LINES);
            glVertex2d(x1, y1);
            glVertex2d(x2, y2);
            glEnd();
        }

        this->Translate({-x1, -y1});
        GLdouble dy = y2 - y1;
        GLdouble dx = x2 - x1;
        if (dx == 0)
        {
            Transform::Matrix<GLdouble> t(3, 3, {{-1, 0, 0}, {0, 1, 0}, {0, 0, 1}});

            *vex = (*vex) * t;
            this->Translate({x1, y1});
            return this;
        }

        GLdouble rad = atan(dy / dx);
        if (rad < 0)
            rad = 4 * atan(1) + rad;

        this->Rotate({0, 0}, -rad);
        Transform::Matrix<GLdouble> t1(3, 3, {{1, 0, 0}, {0, -1, 0}, {0, 0, 1}});
        *vex = (*vex) * t1;

        this->Rotate({0, 0}, rad);
        this->Translate({x1, y1});

        return this;
    }

    Polygon *Shear(const Point &sh)
    {
        Transform::Matrix<GLdouble> t1(3, 3, {{1, sh.y, 0}, {sh.x, 1, 0}, {0, 0, 1}});

        *vex = (*vex) * t1;
        return this;
    }
};

完整代码请参考我的Github仓库: 2D变换-Github

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

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

相关文章

MYSQL表的连接方式

mysql表连接方式可以分为 1、内连接 2、外连接 3、全连接 4、交叉连接 t1表 t2表 1、内连接&#xff0c;包括自然连接&#xff0c;等值连接&#xff0c;不等值连接 1.1、自然连接&#xff0c;根据相同字段进行连接匹配…

容联七陌百度营销通BCP解决方案,让营销更精准

百度营销通作为一个快速迭代、满足客户多元化营销需求的高效率营销工具成为众多企业的选择&#xff0c;通过百度营销通BCP对接&#xff0c;企业就可以在百度咨询页接入会话&#xff0c;收集百度来源的访客搜索关键词&#xff0c;通过百度推广获取更多的精准客户&#xff0c;从而…

基于JAVA的天猫商场系统设计与实现,springboot+jsp,MySQL数据库,前台用户+后台管理,完美运行,有一万五千字论文

目录 演示视频 基本介绍 论文目录 系统截图 演示视频 基本介绍 基于JAVA的天猫商场系统设计与实现&#xff0c;springbootjsp&#xff0c;MySQL数据库&#xff0c;前台用户后台管理&#xff0c;完美运行&#xff0c;有一万五千字论文。 本系统在HTML和CSS的基础上&#xf…

计算机起源(二)

一、前言 冯诺依曼奠定了计算机体系结构后&#xff0c;早期的计算机是没有操作系统的&#xff0c;还是需要大量的人为参与&#xff0c;比如纸卡打孔。通过纸卡上的方格&#xff0c;打孔来表示二进制数。那么如果能使计算机自动能够进行计算&#xff0c;就需要一个模拟人工的操…

Node学习笔记之使用Express框架开发接口

我们利用nodeexpressmysql开发接口&#xff0c;对数据库数据进行简单增、删、查改等操作。 工具 数据库可视化 接口测试工具Postman 1、初始化 我们这里利用nodeexpressmysql开发一个简单的书城商店API。后面会使用result API规范接口 1、创建项目 新建文件夹server 安装依…

记:2023香山杯-Ez加密器-题目复现和学习记录““

文章目录 前言题目分析and调试过程完整exp 前言 前段时间的比赛&#xff0c;那会刚入门o(╥﹏╥)o都不会写&#xff0c;现在复现一下。 题目分析and调试过程 查壳nie 进入IDA&#xff0c;shiftF12查找下字符串看看&#xff0c;定位过去 先用N重命名一下函数&#xff0c;方便…

GPDB7-新特性-角色创建

GPDB7-新特性-角色创建 9月GPDB7发布了release版本&#xff0c;新增了很多新特性及性能改进&#xff0c;对GPDB用户带来福音。业务在调研GPDB6升级到GPDB7的过程中&#xff0c;生产环境会创建用户&#xff0c;利用这些用户进行迁移。但是出现问题了&#xff0c;竟然会报&#x…

如何使用Linux编写STM32程序并且烧录

前言 &#xff08;1&#xff09;如果有嵌入式企业需要招聘湖南区域日常实习生&#xff0c;任何区域的暑假Linux驱动实习岗位&#xff0c;可C站直接私聊&#xff0c;或者邮件&#xff1a;zhangyixu02gmail.com&#xff0c;此消息至2025年1月1日前均有效 &#xff08;2&#xff0…

Z41H-64C高压闸阀型号解析

Z41H-64C型号字母含义 Z41H-64C型号是德特森阀门常用的高压闸阀型号字母分别代表的意思是: Z——代表阀门类别《闸阀》 4——代表连接方式《法兰》 1——代表结构形式《明杆》 H——代表密封堆焊《硬质合金》 -《分隔键》 64——代表公称压力《6.4MPA》 C——代表阀体材…

RT-Thread 6. ENV增加组件(从服务器下载得到)

键入“Y”、或者空格选中 保存 保存之后&#xff0c;自动修改这个文件 退回到ENV界面&#xff0c;输入&#xff1a;pkgs --update 多了 如果用keil4编译的话&#xff0c;输入&#xff1a;scons --targetmdk4&#xff0c;重新生成keil4工程&#xff0c;双击“project.uvpro…

10-类加载器

类加载器 类与类加载器 判断类是否“相等” 任意一个类&#xff0c;都由加载它的类加载器和这个类本身一同确立其在 Java 虚拟机中的唯一性&#xff0c;每一个类加载器&#xff0c;都有一个独立的类名称空间。 因此&#xff0c;比较两个类是否“相等”&#xff0c;只有在这…

北邮22级信通院数电:Verilog-FPGA(6)第六周实验:全加器(关注我的uu们加群咯~)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 作者建群啦&#xff01;&#xff01;&#xff01;欢…

【计网 P2P】计算机网络 P2P(Peer to Peer)详解:中科大郑烇老师笔记 (七)

目录 0 引言1 C/S 模式 VS P2P模式2 P2P 概述2.1 纯P2P架构2.2 非结构化P2P和结构化&#xff08;DHT&#xff09;P2P2.2.1 非结构化P2P2.2.1 结构化P2P 2.3 P2P需要解决的问题及解决方案2.3.1 集中式目录2.3.2 完全分布式2.3.3 混合体 &#x1f64b;‍♂️ 作者&#xff1a;海码…

智能井盖监测系统功能,万宾科技传感器效果

智能井盖传感器的出现是高科技产品的更新换代&#xff0c;同时也是智慧城市建设中的需求。在智慧城市建设过程之中&#xff0c;高科技产品的应用数不胜数&#xff0c;智能井盖传感器的出现&#xff0c;解决了城市道路安全保护着城市地下生命线&#xff0c;改善着传统井盖带来的…

【Qt样式(qss)-5】qss局部渲染混乱,错乱,不生效的一种原因

前言&#xff1a; 之前写过一些关于qss的文章&#xff1a; 【Qt样式&#xff08;qss&#xff09;-1】手册小结&#xff08;附例&#xff1a;软件深色模式&#xff09;_深蓝色主题qss表-CSDN博客 【Qt样式&#xff08;qss&#xff09;-2】使用小结&#xff08;软件换肤&#…

拆贡献+统计非法可能不统计非法贡献:ARC150D

https://atcoder.jp/contests/arc150/tasks/arc150_d 先拆贡献成每个点&#xff0c;然后就只需要考虑这条链上的情况了 我们现在要求的是&#xff1a; 在所有点选完之前&#xff0c;最后一个点被选了多少次 我们发现这很难做&#xff0c;但有个性质&#xff1a; 在所有点选…

国外创意二维码案例:利马博物馆的二维码艺术展!

今天我们要分享的品牌创意二维码案例来自国外一家博物馆。 2022年12月&#xff0c;位于秘鲁的利马艺术博物馆策划并展出了一场别开生面的艺术展。和以往的展览不同&#xff0c;这次展览的主角是一些艺术画作“雕刻”而成的二维码。 利马博物馆&#xff08;The Lima Art Museum…

OPC UA:工业领域的“HTML”

OPC UA是工业自动化领域的一项重要的通信协议。它的特点是包括了信息模型构建方法。能够建立工业领域各种事物的信息模型。在工业自动化行业&#xff0c;OPCUA 类似互联网行业的HTTP协议和“HTML”语言。能够准确&#xff0c;可靠地描述复杂系统中各个元素&#xff0c;并且实现…

质数(素数)prime :只能被 1 和 它本身整除的自然数,不可再分,(三种方式求出质数)

从 2 开始&#xff0c;到这个数 减 1 结束为止&#xff0c; 都不能被这个数本身整除。例如&#xff1a;5 是否是质数 &#xff1f; 那么 2&#xff0c;3&#xff0c;4&#xff0c;都不能被 5 整除 所以 5 是 质数判断 n 是否是质数&#xff1f; 2&#xff0c;3&#xff0c;4&…

Python基础入门例程12-NP12 格式化输出(二)

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解答&#xff1a; 说明&#xff1a; 描述 牛牛、牛妹和牛可乐都是Nowcoder的用户&#xff0c;某天Nowcoder的管理员希望将他们的用户名以某种格式进行显示&#xff0c; 现在给定他们三个当中的某一个名字name…