如何计算沿线的镜像点?

时间:2012-01-21 15:49:03

标签: algorithm math graphics geometry computational-geometry

在2D平面中,我有一个点和一条线。如何沿着这条线获得镜像点?

8 个答案:

答案 0 :(得分:16)

当在计算机程序中完成这样的事情时,您可能必须处理的问题之一是仅使用整数算法(或尽可能多)执行这些计算,假设输入是整数。尽可能以整数形式执行此操作是一个单独的问题,我在此不会介绍。

以下是“数学”解决方案,如果按字面实施将需要浮点计算。我不知道你的情况是否可以接受。您可以自己根据自己的喜好进行优化。

(1)

代表您的第L
A * x + B * y + C = 0

方程。请注意,向量(A, B)是此行的法线向量。

例如,如果该行由两个点X1(x1, y1)X2(x2, y2)定义,那么

A = y2 - y1
B = -(x2 - x1)
C = -A * x1 - B * y1

(2)通过将所有系数除以向量(A, B)的长度来对等式进行归一化。即计算长度

M = sqrt(A * A + B * B)

然后计算值

A' = A / M
B' = B / M
C' = C / M

等式

A' * x + B' * y + C' = 0

仍然是行L的等效等式,但现在法线向量(A', B')是单位向量。

(3)取点P(px, py)并计算值

D = A' * px + B' * py + C'

这将为您提供从您的D点到您的行P签名距离L。换句话说,这是从PL上最近点的距离(我们并不真正关心最近点,我们只需要距离)。

该符号表示点L所在的行P。如果P位于同一侧,则向量(A', B')指向(“正”侧),距离为正。如果P位于另一侧(“负”侧),则距离为负。

(4)为了找到您的镜像点P'(px', py'),您需要将您的点P移动到整个线|2 * D|的{​​{1}}。 1}}到另一边。

“跨行”真的意味着,如果L点位于P的“正”侧,那么我们必须将它移向向量L的方向“消极”方面。反之亦然,如果(A', B')点位于P的“负”侧,那么我们必须将它向向L方向移动到“正”侧。

这可以简单地表示为在向量(A', B')的方向上移动-2 * D(注意减号)的距离。

这意味着

(A', B')

为您提供镜像点px' = px - 2 * A' * D py' = py - 2 * B' * D


或者,您可以使用基于在P'(px', py')行找到实际最近点N的方法,然后将您的点L与[{1}}相关联。这已在其他答案中提出,我将描述我是如何做到的。

(1)建立等式

P
完全按照上面第1步所述,为您的第N

。没有必要对这个等式进行标准化。

(2)为穿过A*x + B*y + C = 0 的垂直线构建方程式。假设垂直线由

表示
L

PD*x + E*y + F = 0 系数立即知道

D

虽然可以通过将点E代入等式

来计算D = B E = -A
F

(3)通过求解两个线性方程组找到这两条线的交点

P
在这种情况下,

Cramer's rule效果很好。维基百科的Line intersection文章中给出的公式只不过是将Cramer规则应用于该系统。

该解决方案为您提供了我们正在寻找的最近点F = -D*px - E*py

(4)现在只计算

A*x + B*y = -C
D*x + E*y = -F

找到你的观点N(nx, ny)

请注意,此方法几乎可以完全以整数实现。可能失去精确度的唯一步骤是在步骤3中克莱默规则内部的划分。当然,像往常一样,你必须为“几乎整体”解决方案付出的代价是需要大量算术。即使系数px' = nx + (nx - px) py' = ny + (ny - py) P'(px', py')也会溢出,甚至没有提到克莱默规则公式中的计算。

答案 1 :(得分:6)

假设该行的等式为ax + by + c = 0。现在想象一条垂直于它的线,它可以用-bx + ay + d = 0表示(两条垂直线的斜率乘积为-1)。现在的问题是找到d。将点的坐标放在第二行,您将轻松获得d的值。

第二部分是,在第二行上找到一个与第一行等距离等距的点。为此,您可以找到两条线的交点。计算给定点和交点的xy的差异。现在将它们添加到交叉点的xy值。这给出了你需要的点(你可能需要否定差异 - 这取决于你使用的减法顺序。)

答案 2 :(得分:6)

细节取决于您的线条的表示方式。如果您将该线表示为线上的任意点 P 以及沿线的单位列向量 n ,则镜像点 Q '任何一点 Q 由:

给出
  

Q '= Q + 2(I - nn T )( P - Q

(这里,我是2x2单位矩阵, n T n 的转置(处理 n 作为2x1矩阵), nn T 是由 n 的标准矩阵乘法形成的2x2矩阵, n T 。)如果你在行上的任何地方移动 P ,那么显示 Q '不会改变并不太难。

将其他线表示转换为点/单位矢量表示并不困难。

答案 3 :(得分:2)

计算线上距离相关点最近的点。然后反转这些点之间的矢量方向,并将其添加到线上最近的点。 Voilà,你找到了镜像点。

答案 4 :(得分:1)

我假设你有点的位置,以及你的线的等式,即

P=(x1,y1) and y = ax + b

首先,a = 0(即与x轴平行的线)的明显情况产生

P'(x2,y2), with x2 = x1, y2 = y1 + 2(b-y1)

对于更一般的情况,

  1. 获取与您的正交线的通用等式:y = cx + d,其中ac = -1

    ==> c = -1 / a

  2. 确定b使得P属于正交线:y1 = -x1 / a + d

    ==> d = y1 + x1 / a

  3. 获取两条线之间的交点:y = -x / a + y1 + x1 / a = ax + b

    ==> x =(y1 + x1 / a -b)/(a + 1 / a),y = a(y1 + x1 / a -b)/(a + 1 / a)+ b

  4. 获取交点和点P之间的向量,将其添加到交点以获得镜像点P'。

    ==> P'(x2,y2),带 x2 = x1 + 2((y1 + x1 / a -b)/(a + 1 / a) - x1) y2 = y1 + 2(a(y1 + x1 / a -b)/(a + 1 / a)+ b - y1)

  5. 可以简化一些步骤,但这是一般性的想法。我在打字的时候做了代数,所以可能会有错误。如果您找到,请告诉我。

答案 5 :(得分:1)

我已经为我构建的另一个系统完成了这个..在我的代码中还有很多东西;所以我希望我已经提取了所有必要的位...... Line.ClosestPoint(Point pt)是你想要的方法......

该算法基于以下思想:对于任何给定线,每条线的斜率是给定线的斜率的负乘法倒数。即,如果一条线具有斜率m,则另一条线具有斜率-1 / m。因此,您需要做的就是在斜率等于-1 / m的点上形成一条直线,并找到该直线与原始直线的交点。

public class Line
{
    protected const double epsilon = 1.0e-8;

    public Point Anchor { get; set; }

    public double Slope { get; set; }

    public virtual Point ClosestPoint(Point pt)
    { return Intersection(Make(pt, -1 / Slope)); }

    protected Line(Point anchor, double slope)
    {
        Anchor = anchor;
        Slope = slope;
    }

    public static Line Make(Point anchor, double slope)
    { return new Line(anchor, slope); }

    public virtual Point Intersection(Line line)
    {
        if (lib.Within(line.Slope, Slope, epsilon))              
            if( lib.Within(line.YIntcpt, YIntcpt, epsilon))
                // code for NoInterceptException not included
                throw new NoInterceptException(
                    "The two lines overlap.");
            else return Point.NullPoint;
        // ----------------------------------------
        double tm = Slope, om = line.Slope,
            tx = Anchor.X, ty = Anchor.Y,
            ox = line.Anchor.X, oy = line.Anchor.Y;

        var x = double.IsInfinity(tm) ? tx :
                double.IsInfinity(om) ? ox :
                   (tm * tx - om * ox + oy - ty) /
                          (tm - om);

        var y = double.IsInfinity(tm) ?
               om * (x - ox) + oy :
               tm * (x - tx) + ty;

        return Point.Make(x, y);
    }
}

public struct Point
{
    const double epsilon = 1.0e-12;
    private readonly bool isDef;
    private readonly double y;
    private readonly double x;
    public bool HasValue { get { return isDef; } }
    public bool IsNull { get { return !isDef; } }

    private Point(double xValue, double yValue)
    { x = xValue; y = yValue; isDef = true; }
    public static Point Make(double x, double y)
    { return new Point(x, y); }

    public double X 
    {
        get
        {
                  // code for AlgebraicException not included
            if (IsNull) throw new AlgebraicException("Null Point Object"); 
            return x;
        }
    }
    public double Y
    {
        get
        {
                  // code for AlgebraicException not included
            if (IsNull) throw new AlgebraicException("Null Point Object");
            return y;
        }
    }

    public static Point NullPoint { get { return new Point();}}

    //  Other functionality

} 

答案 6 :(得分:0)

您可以使用一个简单的公式来查找。 首先,您必须使用

编写该行的公式
ax+by+c=0

让我们说您的行是y = x。而您想要反映的点是(3,2)。我们都知道它将成为(2,3) 无论如何,以标准形式写y = x,它会变成这样:

y-x=0

现在使用此公式并插入点的值,而不是x和y。

    (ax+by+c)/a^2+b^2
    (a^2 means a square.)

您将获得一个值,所以我们将其命名为K。在这种情况下,点为(3,2)且线为y-x = 0,则K等于:

(-3+2)/(1+1)= -1/2

现在要计算新坐标,只需在此公式中插入值:

 x-2ak
 y-2bk

其中x和y是原始坐标,a和b是该行的原始公式中使用的值(y-x = 0 a = -1,b = 1,c = 0) 所以我们得到的值是:

3 - 2 x -1 x -1/2 = 3-1 = 2
2 - 2 x +1 x -1/2 = 2+1 = 3

答案 7 :(得分:0)

我开发了从Ted Hop answer计算公式的JS代码: Q '= Q + 2(I- nn T )( P - Q )转换为

  

Q '= Q + 2(I-d 2 vv T )( P - Q

列向量 v = R - P 垂直于行而不是单位的情况(例如 n )其中 P R 是两个任意的线点; vv T outer productd=1/sqrt(vx*vx + vy*vy) v 的反长度。因为我们使用d 2 (代码中= r),所以可以通过省略sqrt

来优化计算

// 2D Points P=[x,y] and R are points on line, 
// Q is point for which we want to find reflection
function mirror(Q,[P,R]) {
  let [vx,vy]= [ R[0]-P[0], R[1]-P[1] ];
  let [x,y]  = [ P[0]-Q[0], P[1]-Q[1] ];
  let r= 1/(vx*vx+vy*vy);
  return [ Q[0] +2*(x -x*vx*vx*r -y*vx*vy*r), 
           Q[1] +2*(y -y*vy*vy*r -x*vx*vy*r)  ];
}

console.log( mirror([3,2], [[2,4],[4,5]]) )