二维坐标系转换(图纸坐标 -> 设备坐标)

需求场景:不考虑z轴,已知两点的图纸坐标和设备坐标,计算图纸上其他坐标点的设备坐标。
算法核心思想:已知两点的图纸坐标 P 1 P_1 P1 P 2 P_2 P2,及对应的设备坐标 P 1 ′ P_1^{'} P1 P 2 ′ P_2{'} P2。对于图纸上任意一点 P P P,对图纸上两点间的向量 P 1 P ⃗ \vec{P_1P} P1P 进行缩放和旋转得到 P 1 ′ P ′ ⃗ \vec{P_1^{'}P^{'}} P1P ,再将 P 1 ′ P ′ ⃗ \vec{P_1^{'}P^{'}} P1P 平移至 P 1 ′ P_1^{'} P1,即可得到P点的设备坐标 P ′ P^{'} P。至于缩放系数 S S S和旋转角度 θ \theta θ可通过分析 P 1 P 2 ⃗ \vec{P_1P_2} P1P2 P 1 ′ P 2 ′ ⃗ \vec{P_1^{'}P_2^{'}} P1P2 得知。

预备知识

三角和差公式

c o s ( A + B ) = c o s A c o s B − s i n A s i n B s i n ( A + B ) = s i n A c o s B + c o s A s i n B (1) cos(A+B)=cosAcosB - sinAsinB\\ sin(A+B)=sinAcosB + cosAsinB \tag{1} cos(A+B)=cosAcosBsinAsinBsin(A+B)=sinAcosB+cosAsinB(1)

反正切函数

θ = a r c t a n ( y x ) a t a n 2 ( y , x ) = { a r c t a n ( y x ) x > 0 a r c t a n ( y x ) + π y ≥ 0 , x < 0 a r c t a n ( y x ) − π y < 0 , x < 0 + π 2 y > 0 , x = 0 − π 2 y < 0 , x = 0 u n d e f i n e d y = 0 , x = 0 (2) \theta=arctan(\frac{y}{x}) \\ atan2(y,x)= \begin{cases} arctan(\frac{y}{x}) &x>0 \\ arctan(\frac{y}{x})+\pi &y \geq 0,x<0 \\ arctan(\frac{y}{x})-\pi &y <0,x<0 \\ +\frac{\pi}{2} &y>0,x=0 \\ -\frac{\pi}{2} &y<0,x=0 \\ undefined &y=0,x=0 \end{cases} \tag{2} θ=arctan(xy)atan2(y,x)= arctan(xy)arctan(xy)+πarctan(xy)π+2π2πundefinedx>0y0,x<0y<0,x<0y>0,x=0y<0,x=0y=0,x=0(2)

  • θ \theta θ为斜率对应的角度 ( − π 2 , π 2 ) (-\frac{\pi}{2},\frac{\pi}{2}) (2π,2π) a t a n 2 ( y , x ) atan2(y,x) atan2(y,x)求解向量对应的角度(向量角) [ − π , π ] [-\pi,\pi] [π,π]

仿射变换

变换前: P ( x , y ) P(x,y) P(x,y),变换后: P ′ ( X , Y ) P'(X,Y) P(X,Y)

平移

X = x + d x Y = y + d y (3) X=x+dx \\ Y=y+dy \tag{3} X=x+dxY=y+dy(3)

  • d x dx dx d y dy dy分别为点在 x x x方向上的和在 y y y方向上的变换量。

缩放

X = S x x Y = S y y (4) X=S_{x}x \\ Y=S_{y}y \tag{4} X=SxxY=Syy(4)

  • S x S_{x} Sx S y S_{y} Sy分别为点在 x x x方向上的和在 y y y方向上的缩放系数。
  • 在图纸坐标 -> 设备坐标过程中,应保证 S x = S y S_{x}=S_{y} Sx=Sy,若 S x ! = S y S_{x}!=S_{y} Sx!=Sy,可先对设备坐标的y轴进行缩放,使得 S x = S y S_{x}=S_{y} Sx=Sy,最后再缩放回来。

旋转

在这里插入图片描述

旋转前:
x = r ∗ c o s α y = r ∗ s i n α (5) x=r*cos\alpha \\ y=r*sin\alpha \tag{5} x=rcosαy=rsinα(5)
逆时针旋转 θ \theta θ角度后:
X = r ∗ c o s ( α + θ ) Y = r ∗ s i n ( α + θ ) (6) X=r*cos(\alpha+\theta) \\ Y=r*sin(\alpha+\theta) \tag{6} X=rcos(α+θ)Y=rsin(α+θ)(6)
( 2 ) (2) (2) ( 5 ) (5) (5),可将 ( 6 ) (6) (6)化简为:
X = x c o s θ − y s i n θ Y = y c o s θ + x s i n θ (7) X=xcos\theta- ysin\theta \\ Y=ycos\theta + xsin\theta \tag{7} X=xcosθysinθY=ycosθ+xsinθ(7)

参数求解

缩放系数 S S S

∣ P 1 P 2 ∣ ⃗ = ( P 2 x − P 1 x ) 2 + ( P 2 y − P 1 y ) 2 ∣ P 1 ′ P 2 ′ ∣ ⃗ = ( P ′ 2 x − P ′ 1 x ) 2 + ( P ′ 2 y − P ′ 1 y ) 2 S = ∣ P 1 ′ P 2 ′ ∣ ⃗ ∣ P 1 P 2 ∣ ⃗ (8) \vec{|P_1P_2|}=\sqrt{(P_{2x}-P_{1x})^2+(P_{2y}-P_{1y})^2}\\ \vec{|P_1^{'}P_2{'}|}=\sqrt{(P{'}_{2x}-P{'}_{1x})^2+(P{'}_{2y}-P{'}_{1y})^2} \\ S=\frac{\vec{|P_1^{'}P_2{'}|}}{\vec{|P_1P_2|}} \tag{8} P1P2 =(P2xP1x)2+(P2yP1y)2 P1P2 =(P2xP1x)2+(P2yP1y)2 S=P1P2 P1P2 (8)

旋转角度θ

( 2 ) (2) (2)可知:
α = a t a n 2 ( P 2 y − P 1 y , P 2 x − P 1 x ) α ′ = a t a n 2 ( P ′ 2 y − P ′ 1 y , P ′ 2 x − P ′ 1 x ) θ = α ′ − α (9) \alpha=atan2(P_{2y}-P_{1y},P_{2x}-P_{1x}) \\ \alpha^{'}=atan2(P{'}_{2y}-P{'}_{1y},P{'}_{2x}-P{'}_{1x}) \\ \theta =\alpha^{'}-\alpha \tag{9} α=atan2(P2yP1y,P2xP1x)α=atan2(P2yP1y,P2xP1x)θ=αα(9)

  • 这里求得的即是 ( 7 ) (7) (7)中的旋转角度,可直接带入。

完整代码

C++

#include <iostream>
#include <cmath>

struct Point {
    double x, y;
};

//计算向量的模
double norm(Point a, Point b) {
    return std::sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
}

Point transformPoint(Point p1a,Point p1b,Point p2a,Point p2b,Point pa){
    //计算缩放系数(公式8)
    double norm_a=norm(p1a,p2a);
    double norm_b=norm(p1b,p2b);
    double S=norm_b/norm_a;

    //计算旋转角度(公式9)
    double alpha_a=std::atan2(p2a.y-p1a.y, p2a.x-p1a.x);
    double alpha_b=std::atan2(p2b.y-p1b.y, p2b.x-p1b.x);
    double theta=alpha_b-alpha_a;

    //p1a->p2a向量
    Point vec_p1a_pa={pa.x-p1a.x,pa.y-p1a.y};
    
    //应用缩放(公式4)
    Point with_scale;
    with_scale.x=S*vec_p1a_pa.x;
    with_scale.y=S*vec_p1a_pa.y;

    //应用旋转(公式7)
    Point with_rotated;
    with_rotated.x=with_scale.x*std::cos(theta) - with_scale.y*std::sin(theta);
    with_rotated.y=with_scale.y*std::cos(theta) + with_scale.x*std::sin(theta);

    //平移至p1b(公式3)
    Point pb;
    pb.x=with_rotated.x + p1b.x;
    pb.y=with_rotated.y + p1b.y;

    return pb;
}

int main() {
    Point p1a = {5.82, 5.72}, p1b = {687.6,60.9};
    Point p2a = {36.82, 38.92}, p2b = {718.2,94.3};
    Point pa = {26.32,38.92};
    Point pb = transformPoint(p1a,p1b,p2a,p2b,pa);
    std::cout << "Transformed point: (" << pb.x << ", " << pb.y << ")" << std::endl;

    return 0;
}

如果要转换的点非常多,可以使用矩阵的方式,我抽空另开博文。

参考资料

西凉阿土伯_二维图形的坐标变换矩阵推导及齐次坐标的深入理解_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.Crocodile

嘻嘻

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值