特定のポイントから垂直な特定のライン上のポイントを計算したい。
線分ABがあり、線分外に点Cがあります。 CDがABに対して垂直になるように、AB上の点Dを計算したいと思います。
ポイントDを見つける必要があります。
this とよく似ていますが、3D空間では正しく表示されないため、Z座標についても検討したいと思います。
証明:ポイントDはABに垂直な線CD上にあり、もちろんDはABに属します。 2つのベクトルCD.AB = 0のドット積を書き留め、DがABに属するという事実をD = A + t(B-A)として表現します。
最終的には3つの方程式になります。
Dx=Ax+t(Bx-Ax)
Dy=Ay+t(By-Ay)
(Dx-Cx)(Bx-Ax)+(Dy-Cy)(By-Ay)=0
3番目の方程式の最初の2つの方程式を代入すると、次のようになります。
(Ax+t(Bx-Ax)-Cx)(Bx-Ax)+(Ay+t(By-Ay)-Cy)(By-Ay)=0
Tを解くために配布すると、次のようになります。
(Ax-Cx)(Bx-Ax)+t(Bx-Ax)(Bx-Ax)+(Ay-Cy)(By-Ay)+t(By-Ay)(By-Ay)=0
それは与える:
t= -[(Ax-Cx)(Bx-Ax)+(Ay-Cy)(By-Ay)]/[(Bx-Ax)^2+(By-Ay)^2]
否定的な兆候を取り除く:
t=[(Cx-Ax)(Bx-Ax)+(Cy-Ay)(By-Ay)]/[(Bx-Ax)^2+(By-Ay)^2]
Tが得られたら、最初の2つの方程式からDの座標を求めることができます。
Dx=Ax+t(Bx-Ax)
Dy=Ay+t(By-Ay)
function getSpPoint(A,B,C){
var x1=A.x, y1=A.y, x2=B.x, y2=B.y, x3=C.x, y3=C.y;
var px = x2-x1, py = y2-y1, dAB = px*px + py*py;
var u = ((x3 - x1) * px + (y3 - y1) * py) / dAB;
var x = x1 + u * px, y = y1 + u * py;
return {x:x, y:y}; //this is D
}
これには、ベクトルドット積を使用した簡単な閉じた形のソリューションがあります(ループや近似は必要ありません)。
点Aが原点(0,0)にあり、他のすべての点がそこから参照されるベクトルとして点を想像してください(すべての点から点Aを引くことにより、点をこの参照フレームに簡単に変換できます)。
この参照フレームでは、ポイントDは、次のように表されるベクトルB上のポイントCの ベクトル投影 です。
// Per wikipedia this is more efficient than the standard (A . Bhat) * Bhat
Vector projection = Vector.DotProduct(A, B) / Vector.DotProduct(B, B) * B
結果ベクトルは、点Aを追加することにより、元の座標系に戻すことができます。
線分AB上の点は、次のようにパラメーター化できます。
M(x)= A + x *(B-A)、x実数。
DCとABが直交するようなD = M(x)が必要です。
dot(B-A、C-M(x))= 0。
つまり、dot(B-A、C-A-x *(B-A))= 0、またはdot(B-A、C-A)= x * dot(B-A、B-A)で、次のようになります。
x = dot(B-A、C-A)/ dot(B-A、B-A)これは、A = Bでない限り定義されます。
あなたがやろうとしていることは ベクトル投影 と呼ばれます
私はこの回答が提供されているのを見ませんでしたが、Ron WarholicはVector Projectionについて素晴らしい提案をしました。 ACDは単なる直角三角形です。
ここで私は答えたコードを "cuixiping"からmatlabコードに変換しました。
function Pr=getSpPoint(Line,Point)
% getSpPoint(): find Perpendicular on a line segment from a given point
x1=Line(1,1);
y1=Line(1,2);
x2=Line(2,1);
y2=Line(2,1);
x3=Point(1,1);
y3=Point(1,2);
px = x2-x1;
py = y2-y1;
dAB = px*px + py*py;
u = ((x3 - x1) * px + (y3 - y1) * py) / dAB;
x = x1 + u * px;
y = y1 + u * py;
Pr=[x,y];
end
使用している言語を指定していないので、一般的な答えを示します。
ループをABセグメントのすべてのポイントを通過させ、それらからCに「セグメントを描画」し、CからDおよびAからDまでの距離を取得し、ピタゴラスの定理を適用するだけです。 AD ^ 2 + CD ^ 2 = AC ^ 2の場合、あなたはあなたのポイントを見つけました。
また、ループを最短の側(AD側とBD側を考慮)から開始することでコードを最適化できます。
これは、Corey Ogburnの this thread からの回答に基づくpython実装です。
ポイントq
を_p1
_および_p2
_結果としてポイントr
。r
がラインセグメントの外にある場合、nullを返します:
_def is_point_on_line(p1, p2, q):
if (p1[0] == p2[0]) and (p1[1] == p2[1]):
p1[0] -= 0.00001
U = ((q[0] - p1[0]) * (p2[0] - p1[0])) + ((q[1] - p1[1]) * (p2[1] - p1[1]))
Udenom = math.pow(p2[0] - p1[0], 2) + math.pow(p2[1] - p1[1], 2)
U /= Udenom
r = [0, 0]
r[0] = p1[0] + (U * (p2[0] - p1[0]))
r[1] = p1[1] + (U * (p2[1] - p1[1]))
minx = min(p1[0], p2[0])
maxx = max(p1[0], p2[0])
miny = min(p1[1], p2[1])
maxy = max(p1[1], p2[1])
is_valid = (minx <= r[0] <= maxx) and (miny <= r[1] <= maxy)
if is_valid:
return r
else:
return None
_