在2D平面中,我有一个点和一条线。如何沿着这条线获得镜像点?
答案 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
。换句话说,这是从P
到L
上最近点的距离(我们并不真正关心最近点,我们只需要距离)。
该符号表示点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
P
和D*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
的值。
第二部分是,在第二行上找到一个与第一行等距离等距的点。为此,您可以找到两条线的交点。计算给定点和交点的x
和y
的差异。现在将它们添加到交叉点的x
和y
值。这给出了你需要的点(你可能需要否定差异 - 这取决于你使用的减法顺序。)
答案 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)
对于更一般的情况,
获取与您的正交线的通用等式:y = cx + d,其中ac = -1
==> c = -1 / a
确定b使得P属于正交线:y1 = -x1 / a + d
==> d = y1 + x1 / a
获取两条线之间的交点: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
获取交点和点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 :(得分: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 product; d=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]]) )