ระยะทางที่สั้นที่สุดระหว่างจุดและส่วนของเส้น


358

ฉันต้องการฟังก์ชั่นพื้นฐานเพื่อค้นหาระยะทางที่สั้นที่สุดระหว่างจุดและส่วนของเส้น รู้สึกอิสระที่จะเขียนวิธีการแก้ปัญหาในภาษาใด ๆ ที่คุณต้องการ; ฉันสามารถแปลมันเป็นสิ่งที่ฉันใช้ (Javascript)

แก้ไข: ส่วนของเส้นของฉันถูกกำหนดโดยจุดปลายสองจุด ดังนั้นส่วนของเส้นของฉันABถูกกำหนดโดยสองจุดและA (x1,y1) B (x2,y2)ฉันพยายามค้นหาระยะห่างระหว่างส่วนของเส้นตรงนี้กับจุดC (x3,y3)หนึ่ง ทักษะทางเรขาคณิตของฉันเป็นสนิมดังนั้นตัวอย่างที่ฉันเห็นมีความสับสนฉันขอโทษที่ต้องยอมรับ


ฉันไม่รู้ว่าคุณเป็นตัวแทนเส้นและจุดอย่างไร แต่นี่คือคณิตศาสตร์ทั้งหมดที่คุณต้องเริ่มต้น ไม่ควรยากเกินไปที่จะเข้าใจว่าคุณต้องทำอะไร
mandaleeka

4
@ArthurKalliokoski: ลิงก์นั้นตายไปแล้ว แต่ฉันพบสำเนาแล้ว: paulbourke.net/geometry/pointline
Gunther Struyf

11
@GuntherStruyf: ลิงก์นั้นก็ตายเช่นกัน แต่ลิงก์ที่คล้ายกันนี้ใช้งานได้: paulbourke.net/geometry/pointlineplane
Michael

1
หากใครบางคนกำลังมองหาระยะห่างระหว่างจุดและเส้นไม่ใช่จุดและเส้นแบ่งส่วนตรวจสอบลิงค์นี้: gist.github.com/rhyolight/2846020
นิค Budden

1
ลิงค์ด้านบนเสียชีวิต นี่คือนามแฝงรหัสและ C ++ ตัวอย่างการอธิบายและมาตามรายละเอียดที่เป็นตำรา geomalgorithms.com/a02-_lines.html
เอริค

คำตอบ:


447

อีไลรหัสที่คุณตัดสินไม่ถูกต้อง จุดใกล้เส้นที่เซ็กเมนต์อยู่ แต่ไกลออกไปทางปลายด้านหนึ่งของเซกเมนต์จะถูกตัดสินอย่างไม่ถูกต้องใกล้กับส่วนนั้น อัปเดต: คำตอบที่ไม่ถูกต้องไม่ได้เป็นคำตอบที่ยอมรับอีกต่อไป

นี่คือรหัสที่ถูกต้องใน C ++ มันทึกทักชั้น 2D เวกเตอร์class vec2 {float x,y;}เป็นหลักกับผู้ประกอบการเพื่อเพิ่ม subract, ขนาด, ฯลฯ และฟังก์ชั่นระยะทางและจุดสินค้า (คือx1 x2 + y1 y2)

float minimum_distance(vec2 v, vec2 w, vec2 p) {
  // Return minimum distance between line segment vw and point p
  const float l2 = length_squared(v, w);  // i.e. |w-v|^2 -  avoid a sqrt
  if (l2 == 0.0) return distance(p, v);   // v == w case
  // Consider the line extending the segment, parameterized as v + t (w - v).
  // We find projection of point p onto the line. 
  // It falls where t = [(p-v) . (w-v)] / |w-v|^2
  // We clamp t from [0,1] to handle points outside the segment vw.
  const float t = max(0, min(1, dot(p - v, w - v) / l2));
  const vec2 projection = v + t * (w - v);  // Projection falls on the segment
  return distance(p, projection);
}

แก้ไข: ฉันต้องการการใช้งาน Javascript ดังนั้นนี่คือไม่มีการอ้างอิง (หรือความคิดเห็น แต่เป็นพอร์ตโดยตรงด้านบน) คะแนนจะถูกแสดงเป็นวัตถุด้วยxและyคุณลักษณะ

function sqr(x) { return x * x }
function dist2(v, w) { return sqr(v.x - w.x) + sqr(v.y - w.y) }
function distToSegmentSquared(p, v, w) {
  var l2 = dist2(v, w);
  if (l2 == 0) return dist2(p, v);
  var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
  t = Math.max(0, Math.min(1, t));
  return dist2(p, { x: v.x + t * (w.x - v.x),
                    y: v.y + t * (w.y - v.y) });
}
function distToSegment(p, v, w) { return Math.sqrt(distToSegmentSquared(p, v, w)); }

แก้ไข 2: ฉันต้องการรุ่น Java แต่ที่สำคัญกว่านั้นฉันต้องการเป็นแบบ 3 มิติแทนที่จะเป็น 2d

float dist_to_segment_squared(float px, float py, float pz, float lx1, float ly1, float lz1, float lx2, float ly2, float lz2) {
  float line_dist = dist_sq(lx1, ly1, lz1, lx2, ly2, lz2);
  if (line_dist == 0) return dist_sq(px, py, pz, lx1, ly1, lz1);
  float t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1) + (pz - lz1) * (lz2 - lz1)) / line_dist;
  t = constrain(t, 0, 1);
  return dist_sq(px, py, pz, lx1 + t * (lx2 - lx1), ly1 + t * (ly2 - ly1), lz1 + t * (lz2 - lz1));
}

1
ฉันได้เพิ่มคำตอบที่แยกออกมาจากเนื้อหนังแล้ว
M Katz

4
ขอบคุณ @Grumdrig โซลูชัน javascript ของคุณเป็นที่รู้จักและประหยัดเวลาได้มาก ฉันส่งต่อโซลูชันของคุณไปยัง Objective-C และเพิ่มไว้ด้านล่าง
awolf

1
เราแค่พยายามหลีกเลี่ยงการหารด้วยศูนย์ตรงนั้น
Grumdrig

9
ประมาณการของจุดบนเส้นเป็นจุดบนเส้นใกล้เคียงกับp p(และตั้งฉากกับเส้นตรงที่เส้นโครงฉายจะผ่านไปp) จำนวนtคือระยะทางตามส่วนของเส้นตรงvจนถึงwเส้นโครงตก ดังนั้นถ้าtเป็น 0 ฉายตกอยู่ทางด้านขวาบนv; ถ้าหากมันเป็น 1 จะอยู่ในw; ตัวอย่างเช่นถ้าเป็น 0.5 แล้วก็อยู่กึ่งกลางระหว่าง หากtน้อยกว่า 0 หรือมากกว่า 1 มันจะตกอยู่ในบรรทัดที่ผ่านปลายด้านหนึ่งหรืออีกส่วนหนึ่งของส่วน ในกรณีนั้นระยะทางไปยังเซกเมนต์จะเป็นระยะทางไปจนสุดใกล้
Grumdrig

1
อ๊ะ - ไม่ได้สังเกตว่ามีคนส่งมอบรุ่น 3 มิติ @RogiSolorzano คุณจะต้องแปลงละติจูดและลองจิจูดเป็นพิกัด x, y, z ใน 3 ช่องว่างก่อน
Grumdrig

110

นี่คือรหัสที่สมบูรณ์แบบที่สุดใน Javascript

x, y คือเป้าหมายของคุณและ x1, y1 ถึง x2, y2 คือส่วนของเส้นตรงของคุณ

อัปเดต: แก้ไขปัญหา 0 ความยาวบรรทัดจากความคิดเห็น

function pDistance(x, y, x1, y1, x2, y2) {

  var A = x - x1;
  var B = y - y1;
  var C = x2 - x1;
  var D = y2 - y1;

  var dot = A * C + B * D;
  var len_sq = C * C + D * D;
  var param = -1;
  if (len_sq != 0) //in case of 0 length line
      param = dot / len_sq;

  var xx, yy;

  if (param < 0) {
    xx = x1;
    yy = y1;
  }
  else if (param > 1) {
    xx = x2;
    yy = y2;
  }
  else {
    xx = x1 + param * C;
    yy = y1 + param * D;
  }

  var dx = x - xx;
  var dy = y - yy;
  return Math.sqrt(dx * dx + dy * dy);
}

ภาพเพื่อช่วยให้เห็นภาพการแก้ปัญหา


8
ของรหัสทั้งหมดที่ฉันเห็นเพื่อแก้ปัญหานี้ฉันชอบที่ดีที่สุด ชัดเจนและอ่านง่าย คณิตศาสตร์ที่อยู่เบื้องหลังมันเป็นสิ่งลึกลับเล็กน้อย จุดผลิตภัณฑ์หารด้วยความยาวยกกำลังสองหมายถึงอะไรเช่น
user1815201

2
ผลิตภัณฑ์ดอทหารด้วยความยาวกำลังสองให้ระยะฉายภาพจาก (x1, y1) นี่คือส่วนของเส้นที่จุด (x, y) ใกล้เคียงที่สุด ขอให้สังเกตประโยคสุดท้ายที่มีการคำนวณ (xx, yy) - นี่คือการฉายภาพของจุด (x, y) ไปยังส่วน (X1, y1) - (x2, y2)
รถกระบะโลแกน

4
การตรวจสอบส่วนของเส้นที่มีความยาว 0 อยู่ไกลเกินไปในรหัส 'len_sq' จะเป็นศูนย์และรหัสจะหารด้วย 0 ก่อนที่จะได้รับการตรวจสอบความปลอดภัย
HostedMetrics.com

17
เมตร มันถูกส่งคืนในหน่วยเมตร
Joshua

1
@ ไม่ทราบว่ามาเรียกจุด p0 ของเราและจุดที่กำหนดเส้นเป็น p1 และ p2 จากนั้นคุณจะได้เวกเตอร์ A = p0 - p1 และ B = p2 - p1 Param คือค่าสเกลาร์ที่เมื่อคูณกับ B จะให้จุดบนบรรทัดที่ใกล้เคียงกับ p0 มากที่สุด ถ้า param <= 0 จุดที่ใกล้ที่สุดคือ p1 ถ้า param> = 1 จุดที่ใกล้ที่สุดคือ p1 ถ้าอยู่ระหว่าง 0 ถึง 1 มันอยู่ที่ไหนสักแห่งระหว่าง p1 และ p2 ดังนั้นเราจึงทำการประมาณ XX และ YY เป็นจุดที่ใกล้ที่สุดในส่วนของเส้นตรง dx / dy คือเวกเตอร์จาก p0 ถึงจุดนั้นและในที่สุดเราก็กลับความยาวของเวกเตอร์นั้น
ฌอน

70

นี่คือการใช้งานที่สร้างขึ้นสำหรับการแยกกลุ่ม FINITE ไม่ใช่บรรทัดที่ไม่สิ้นสุดเหมือนกับฟังก์ชั่นอื่น ๆ ส่วนใหญ่ที่นี่ดูเหมือนจะเป็น (นั่นคือเหตุผลที่ฉันทำสิ่งนี้)

การดำเนินการของทฤษฎีโดยพอลบอร์ก

งูหลาม:

def dist(x1, y1, x2, y2, x3, y3): # x3,y3 is the point
    px = x2-x1
    py = y2-y1

    norm = px*px + py*py

    u =  ((x3 - x1) * px + (y3 - y1) * py) / float(norm)

    if u > 1:
        u = 1
    elif u < 0:
        u = 0

    x = x1 + u * px
    y = y1 + u * py

    dx = x - x3
    dy = y - y3

    # Note: If the actual distance does not matter,
    # if you only want to compare what this function
    # returns to other results of this function, you
    # can just return the squared distance instead
    # (i.e. remove the sqrt) to gain a little performance

    dist = (dx*dx + dy*dy)**.5

    return dist

AS3:

public static function segmentDistToPoint(segA:Point, segB:Point, p:Point):Number
{
    var p2:Point = new Point(segB.x - segA.x, segB.y - segA.y);
    var something:Number = p2.x*p2.x + p2.y*p2.y;
    var u:Number = ((p.x - segA.x) * p2.x + (p.y - segA.y) * p2.y) / something;

    if (u > 1)
        u = 1;
    else if (u < 0)
        u = 0;

    var x:Number = segA.x + u * p2.x;
    var y:Number = segA.y + u * p2.y;

    var dx:Number = x - p.x;
    var dy:Number = y - p.y;

    var dist:Number = Math.sqrt(dx*dx + dy*dy);

    return dist;
}

ชวา

private double shortestDistance(float x1,float y1,float x2,float y2,float x3,float y3)
    {
        float px=x2-x1;
        float py=y2-y1;
        float temp=(px*px)+(py*py);
        float u=((x3 - x1) * px + (y3 - y1) * py) / (temp);
        if(u>1){
            u=1;
        }
        else if(u<0){
            u=0;
        }
        float x = x1 + u * px;
        float y = y1 + u * py;

        float dx = x - x3;
        float dy = y - y3;
        double dist = Math.sqrt(dx*dx + dy*dy);
        return dist;

    }

2
ขออภัยฉันลองทำสิ่งนี้และมันก็ยังให้ผลลัพธ์กับฉันราวกับว่าบรรทัดนั้นขยายไปถึงอินฟินิตี้ ฉันได้พบคำตอบของ Grumdig ในการทำงานแล้ว
Frederik

1
ในกรณีนี้คุณใช้ผิดหรือหมายถึงสิ่งอื่นที่ไม่มีสิ้นสุด ดูตัวอย่างของรหัสนี้ได้ที่นี่: boomie.se/upload/Drawdebug.swf
quano

ดูเหมือนว่าผิดพลาดในรหัสหรือบางสิ่งฉันได้รับผลลัพธ์เช่นเดียวกับ Frederik /
Kromster

30
ตัวเลือกของชื่อตัวแปรอยู่ไกลจากดี (p2, บางสิ่ง, u, ... )
miguelSantirso

2
ฉันได้ลองใช้ฟังก์ชั่น Python แล้วและพบว่ามันแสดงผลลัพธ์ไม่ถูกต้องหากพารามิเตอร์เป็นจำนวนเต็ม distAnother(0, 0, 4, 0, 2, 2)ให้ 2.8284271247461903 (ไม่ถูกต้อง) distAnother(0., 0., 4., 0., 2., 2.)ให้ 2.0 (ถูกต้อง) โปรดระวังสิ่งนี้ ฉันคิดว่าสามารถปรับปรุงโค้ดให้มีการแปลงแบบลอยได้
Vladimir Obrizan

22

ในกระทู้คำถามของฉันเองวิธีการคำนวณระยะทาง 2D ที่สั้นที่สุดระหว่างจุดและส่วนของเส้นตรงในทุกกรณีใน C, C # / .NET 2.0 หรือ Java ฉันถูกขอให้ใส่คำตอบ C # ที่นี่เมื่อฉันพบหนึ่ง: ดังนั้นนี่คือแก้ไขจากhttp://www.topcoder.com/tc?d1=tutorials&d2=geometry1&module=Static :

//Compute the dot product AB . BC
private double DotProduct(double[] pointA, double[] pointB, double[] pointC)
{
    double[] AB = new double[2];
    double[] BC = new double[2];
    AB[0] = pointB[0] - pointA[0];
    AB[1] = pointB[1] - pointA[1];
    BC[0] = pointC[0] - pointB[0];
    BC[1] = pointC[1] - pointB[1];
    double dot = AB[0] * BC[0] + AB[1] * BC[1];

    return dot;
}

//Compute the cross product AB x AC
private double CrossProduct(double[] pointA, double[] pointB, double[] pointC)
{
    double[] AB = new double[2];
    double[] AC = new double[2];
    AB[0] = pointB[0] - pointA[0];
    AB[1] = pointB[1] - pointA[1];
    AC[0] = pointC[0] - pointA[0];
    AC[1] = pointC[1] - pointA[1];
    double cross = AB[0] * AC[1] - AB[1] * AC[0];

    return cross;
}

//Compute the distance from A to B
double Distance(double[] pointA, double[] pointB)
{
    double d1 = pointA[0] - pointB[0];
    double d2 = pointA[1] - pointB[1];

    return Math.Sqrt(d1 * d1 + d2 * d2);
}

//Compute the distance from AB to C
//if isSegment is true, AB is a segment, not a line.
double LineToPointDistance2D(double[] pointA, double[] pointB, double[] pointC, 
    bool isSegment)
{
    double dist = CrossProduct(pointA, pointB, pointC) / Distance(pointA, pointB);
    if (isSegment)
    {
        double dot1 = DotProduct(pointA, pointB, pointC);
        if (dot1 > 0) 
            return Distance(pointB, pointC);

        double dot2 = DotProduct(pointB, pointA, pointC);
        if (dot2 > 0) 
            return Distance(pointA, pointC);
    }
    return Math.Abs(dist);
} 

ฉัน @ ดังนั้นไม่ตอบ แต่ถามคำถามดังนั้นฉันหวังว่าฉันจะไม่ได้รับการลงคะแนนล้านด้วยเหตุผลบางอย่าง แต่สร้างนักวิจารณ์ ฉันแค่ต้องการ (และได้รับการสนับสนุน) แบ่งปันความคิดของคนอื่นเนื่องจากคำตอบในหัวข้อนี้มีทั้งภาษาแปลกใหม่ (Fortran, Mathematica) หรือติดแท็กเป็นความผิดพลาดโดยใครบางคน มีประโยชน์อย่างเดียว (โดย Grumdrig) สำหรับฉันเขียนด้วย C ++ และไม่มีใครแท็กมันผิด แต่มันขาดวิธีการ (จุดเป็นต้น) ที่เรียกว่า


1
ขอขอบคุณที่โพสต์สิ่งนี้ แต่ดูเหมือนว่ามีวิธีการเพิ่มประสิทธิภาพที่ชัดเจนที่สุดที่เป็นไปได้ในวิธีการล่าสุด: อย่าคำนวณระยะทางจนกระทั่งหลังจากได้รับการพิจารณาว่าจำเป็น
RenniePet

2
ความคิดเห็นเกี่ยวกับ DotProduct กล่าวว่ากำลังประมวลผล AB.AC แต่กำลังประมวลผล AB.BC
Metal450

ครอสโปรดัคตามนิยามส่งคืนเวกเตอร์ แต่ส่งคืนสเกลาร์ที่นี่
SteakOverflow

21

ใน F # ระยะทางจากจุดcถึงส่วนของเส้นแบ่งระหว่างaและbถูกกำหนดโดย:

let pointToLineSegmentDistance (a: Vector, b: Vector) (c: Vector) =
  let d = b - a
  let s = d.Length
  let lambda = (c - a) * d / s
  let p = (lambda |> max 0.0 |> min s) * d / s
  (a + p - c).Length

เวกเตอร์dชี้จากaไปbตามส่วนของเส้น ผลิตภัณฑ์ของจุดd/sที่มีให้พารามิเตอร์ของจุดเข้าใกล้ระหว่างบรรทัดที่ไม่มีที่สิ้นสุดและจุดc-a และฟังก์ชั่นที่ใช้ในการยึดพารามิเตอร์นี้เพื่อช่วงเพื่อให้จุดอยู่ระหว่างและ ในที่สุดความยาวของคือระยะทางจากไปยังจุดที่ใกล้ที่สุดในส่วนของเส้นcminmax0..saba+p-cc

ตัวอย่างการใช้:

pointToLineSegmentDistance (Vector(0.0, 0.0), Vector(1.0, 0.0)) (Vector(-1.0, 1.0))

1
ฉันคิดว่าบรรทัดสุดท้ายนั้นไม่ถูกต้องและควรอ่าน:(a + p - c).Length
Blair Holloway

ที่ยังไม่สามารถแก้ไขปัญหาได้อย่างเต็มที่ วิธีหนึ่งในการแก้ไขฟังก์ชั่นคือการกำหนดใหม่lambdaและpเป็นlet lambda = (c - a) * d / (s * s)และlet p = a + (lambda |> max 0.0 |> min 1.0) * dตามลำดับ หลังจากที่ฟังก์ชั่นส่งกลับเช่นระยะทางที่ถูกต้องสำหรับกรณีที่a = (0,1), และb = (1,0) c = (1,1)
mikkoma

20

สำหรับผู้ที่สนใจนี่คือการแปลงรหัส Javascript ของ Joshua เป็น Objective-C เล็กน้อย:

- (double)distanceToPoint:(CGPoint)p fromLineSegmentBetween:(CGPoint)l1 and:(CGPoint)l2
{
    double A = p.x - l1.x;
    double B = p.y - l1.y;
    double C = l2.x - l1.x;
    double D = l2.y - l1.y;

    double dot = A * C + B * D;
    double len_sq = C * C + D * D;
    double param = dot / len_sq;

    double xx, yy;

    if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
        xx = l1.x;
        yy = l1.y;
    }
    else if (param > 1) {
        xx = l2.x;
        yy = l2.y;
    }
    else {
        xx = l1.x + param * C;
        yy = l1.y + param * D;
    }

    double dx = p.x - xx;
    double dy = p.y - yy;

    return sqrtf(dx * dx + dy * dy);
}

ฉันต้องการโซลูชันนี้เพื่อทำงานร่วมกับMKMapPointฉันดังนั้นฉันจะแบ่งปันในกรณีที่คนอื่นต้องการมัน เพียงแค่การเปลี่ยนแปลงเล็กน้อยและสิ่งนี้จะคืนค่าระยะทางเป็นเมตร:

- (double)distanceToPoint:(MKMapPoint)p fromLineSegmentBetween:(MKMapPoint)l1 and:(MKMapPoint)l2
{
    double A = p.x - l1.x;
    double B = p.y - l1.y;
    double C = l2.x - l1.x;
    double D = l2.y - l1.y;

    double dot = A * C + B * D;
    double len_sq = C * C + D * D;
    double param = dot / len_sq;

    double xx, yy;

    if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
        xx = l1.x;
        yy = l1.y;
    }
    else if (param > 1) {
        xx = l2.x;
        yy = l2.y;
    }
    else {
        xx = l1.x + param * C;
        yy = l1.y + param * D;
    }

    return MKMetersBetweenMapPoints(p, MKMapPointMake(xx, yy));
}

ดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน ขอบคุณสำหรับการแปลง
Gregir

มันน่าสังเกตว่า (xx, yy) เป็นที่ตั้งของจุดที่ใกล้ที่สุด ฉันได้แก้ไขบิตรหัสของคุณจึงกลับมาทั้งสองจุดและระยะทางชื่อ refactored เพื่อให้พวกเขาอธิบายว่าเป็นสิ่งที่มีให้เช่นที่: stackoverflow.com/a/28028023/849616
Vive

20

ใน Mathematica

มันใช้คำอธิบายพารามิเตอร์ของส่วนและโครงการจุดลงในบรรทัดที่กำหนดโดยส่วน เมื่อพารามิเตอร์ไปจาก 0 ถึง 1 ในส่วนหากการฉายภาพอยู่นอกขอบเขตนี้เราคำนวณระยะทางไปยังจุดที่สอดคล้องกันแทนเส้นตรงปกติไปยังส่วน

Clear["Global`*"];
 distance[{start_, end_}, pt_] := 
   Module[{param},
   param = ((pt - start).(end - start))/Norm[end - start]^2; (*parameter. the "."
                                                       here means vector product*)

   Which[
    param < 0, EuclideanDistance[start, pt],                 (*If outside bounds*)
    param > 1, EuclideanDistance[end, pt],
    True, EuclideanDistance[pt, start + param (end - start)] (*Normal distance*)
    ]
   ];  

ผลการพล็อต:

Plot3D[distance[{{0, 0}, {1, 0}}, {xp, yp}], {xp, -1, 2}, {yp, -1, 2}]

ข้อความแสดงแทน

พล็อตจุดเหล่านั้นใกล้กว่าระยะทางลัด :

ข้อความแสดงแทน

Contour Contour:

ป้อนคำอธิบายรูปภาพที่นี่


11

เฮ้ฉันเพิ่งเขียนเมื่อวานนี้ มันอยู่ใน Actionscript 3.0 ซึ่งโดยพื้นฐานแล้วก็คือจาวาสคริปต์ แต่คุณอาจจะไม่ได้เรียนในคลาสเดียวกัน

//st = start of line segment
//b = the line segment (as in: st + b = end of line segment)
//pt = point to test
//Returns distance from point to line segment.  
//Note: nearest point on the segment to the test point is right there if we ever need it
public static function linePointDist( st:Point, b:Point, pt:Point ):Number
{
    var nearestPt:Point; //closest point on seqment to pt

    var keyDot:Number = dot( b, pt.subtract( st ) ); //key dot product
    var bLenSq:Number = dot( b, b ); //Segment length squared

    if( keyDot <= 0 )  //pt is "behind" st, use st
    {
        nearestPt = st  
    }
    else if( keyDot >= bLenSq ) //pt is "past" end of segment, use end (notice we are saving twin sqrts here cuz)
    {
        nearestPt = st.add(b);
    }
    else //pt is inside segment, reuse keyDot and bLenSq to get percent of seqment to move in to find closest point
    {
        var keyDotToPctOfB:Number = keyDot/bLenSq; //REM dot product comes squared
        var partOfB:Point = new Point( b.x * keyDotToPctOfB, b.y * keyDotToPctOfB );
        nearestPt = st.add(partOfB);
    }

    var dist:Number = (pt.subtract(nearestPt)).length;

    return dist;
}

นอกจากนี้ยังมีการอภิปรายปัญหาที่สมบูรณ์และสามารถอ่านได้ที่นี่: notejot.com


ขอบคุณ - นี่คือรหัสที่ฉันต้องการ ฉันโพสต์คำตอบของฉันเองด้านล่างเนื่องจากฉันสามารถรวบรวมบางสิ่งบางอย่างเข้าด้วยกันที่ทำงานในยุคปัจจุบันเบราว์เซอร์ - Javascript แต่ฉันทำเครื่องหมายคำตอบของคุณเป็นที่ยอมรับเพราะเรียบง่ายเขียนดีเข้าใจง่าย และชื่นชมมาก
Eli Courtwright

สิ่งนี้ไม่ได้หายไปจาก dot-method หรือไม่? ไม่ว่าในกรณีใด ๆ มันเป็นเรื่องง่ายที่จะคำนวณ: vec1.x * vec2.x + vec1.y * vec2.y
quano

11

สำหรับคนขี้เกียจนี่คือพอร์ต Objective-C ของฉันที่คำตอบของ @ Grumdrig ด้านบน:

CGFloat sqr(CGFloat x) { return x*x; }
CGFloat dist2(CGPoint v, CGPoint w) { return sqr(v.x - w.x) + sqr(v.y - w.y); }
CGFloat distanceToSegmentSquared(CGPoint p, CGPoint v, CGPoint w)
{
    CGFloat l2 = dist2(v, w);
    if (l2 == 0.0f) return dist2(p, v);

    CGFloat t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
    if (t < 0.0f) return dist2(p, v);
    if (t > 1.0f) return dist2(p, w);
    return dist2(p, CGPointMake(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
}
CGFloat distanceToSegment(CGPoint point, CGPoint segmentPointV, CGPoint segmentPointW)
{
    return sqrtf(distanceToSegmentSquared(point, segmentPointV, segmentPointW));
}

ฉันได้รับ 'nan' กลับมาจากบรรทัดนี้ มีความคิดอะไรบ้าง (ขอบคุณสำหรับการพิมพ์สิ่งนี้ใน Obj-C โดยวิธี!) return dist2(p, CGPointMake(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)))
Gregir

sqrtf () กำลังสอง x ไม่ได้รับสแควร์รูท
Senseful

@Senseful ไม่แน่ใจว่าคุณหมายถึงอะไร sqrtf คือรากที่สอง developer.apple.com/library/mac/documentation/Darwin/Reference/
......

@awolf: ดูบรรทัดแรกของรหัสด้านบน sqrtf(x) = x*xมันกำหนดวิธีการ
Senseful

@Senseful ขอบคุณมันผิดมากกว่าการดำเนินการผิด
awolf

10

ไม่สามารถต้านทานการเข้ารหัสในหลามได้ :)

from math import sqrt, fabs
def pdis(a, b, c):
    t = b[0]-a[0], b[1]-a[1]           # Vector ab
    dd = sqrt(t[0]**2+t[1]**2)         # Length of ab
    t = t[0]/dd, t[1]/dd               # unit vector of ab
    n = -t[1], t[0]                    # normal unit vector to ab
    ac = c[0]-a[0], c[1]-a[1]          # vector ac
    return fabs(ac[0]*n[0]+ac[1]*n[1]) # Projection of ac to n (the minimum distance)

print pdis((1,1), (2,2), (2,0))        # Example (answer is 1.414)


เหมือนกันสำหรับ fortran :)

real function pdis(a, b, c)
    real, dimension(0:1), intent(in) :: a, b, c
    real, dimension(0:1) :: t, n, ac
    real :: dd
    t = b - a                          ! Vector ab
    dd = sqrt(t(0)**2+t(1)**2)         ! Length of ab
    t = t/dd                           ! unit vector of ab
    n = (/-t(1), t(0)/)                ! normal unit vector to ab
    ac = c - a                         ! vector ac
    pdis = abs(ac(0)*n(0)+ac(1)*n(1))  ! Projection of ac to n (the minimum distance)
end function pdis


program test
    print *, pdis((/1.0,1.0/), (/2.0,2.0/), (/2.0,0.0/))   ! Example (answer is 1.414)
end program test

10
นี่ไม่ใช่การคำนวณระยะทางของจุดหนึ่งเส้นแทนส่วนหรือไม่
balint.miklos

6
นี่คือระยะทางไปยังบรรทัดที่กลุ่มเปิดอยู่ไม่ใช่กับกลุ่ม
Grumdrig

12
ดูเหมือนจะใช้งานไม่ได้ หากคุณมีเซ็กเมนต์ (0,0) และ (5,0) และลองเทียบกับจุด (7,0) มันจะคืนค่า 0 ซึ่งไม่เป็นความจริง ระยะทางควรเป็น 2
quano

8
เขาล้มเหลวในการพิจารณากรณีที่การฉายจุดไปยังเซ็กเมนต์อยู่นอกช่วงจาก A ถึง B นั่นอาจเป็นสิ่งที่ผู้ถามต้องการ แต่ไม่ใช่สิ่งที่เขาถาม
phkahler

5
นี่ไม่ใช่สิ่งที่ถูกถามในตอนแรก
Sambatyon

10

นี่คือการสะกดที่สมบูรณ์ยิ่งขึ้นจากโซลูชันของ Grumdrig รุ่นนี้ยังส่งกลับจุดที่ใกล้เคียงที่สุด

#include "stdio.h"
#include "math.h"

class Vec2
{
public:
    float _x;
    float _y;

    Vec2()
    {
        _x = 0;
        _y = 0;
    }

    Vec2( const float x, const float y )
    {
        _x = x;
        _y = y;
    }

    Vec2 operator+( const Vec2 &v ) const
    {
        return Vec2( this->_x + v._x, this->_y + v._y );
    }

    Vec2 operator-( const Vec2 &v ) const
    {
        return Vec2( this->_x - v._x, this->_y - v._y );
    }

    Vec2 operator*( const float f ) const
    {
        return Vec2( this->_x * f, this->_y * f );
    }

    float DistanceToSquared( const Vec2 p ) const
    {
        const float dX = p._x - this->_x;
        const float dY = p._y - this->_y;

        return dX * dX + dY * dY;
    }

    float DistanceTo( const Vec2 p ) const
    {
        return sqrt( this->DistanceToSquared( p ) );
    }

    float DotProduct( const Vec2 p ) const
    {
        return this->_x * p._x + this->_y * p._y;
    }
};

// return minimum distance between line segment vw and point p, and the closest point on the line segment, q
float DistanceFromLineSegmentToPoint( const Vec2 v, const Vec2 w, const Vec2 p, Vec2 * const q )
{
    const float distSq = v.DistanceToSquared( w ); // i.e. |w-v|^2 ... avoid a sqrt
    if ( distSq == 0.0 )
    {
        // v == w case
        (*q) = v;

        return v.DistanceTo( p );
    }

    // consider the line extending the segment, parameterized as v + t (w - v)
    // we find projection of point p onto the line
    // it falls where t = [(p-v) . (w-v)] / |w-v|^2

    const float t = ( p - v ).DotProduct( w - v ) / distSq;
    if ( t < 0.0 )
    {
        // beyond the v end of the segment
        (*q) = v;

        return v.DistanceTo( p );
    }
    else if ( t > 1.0 )
    {
        // beyond the w end of the segment
        (*q) = w;

        return w.DistanceTo( p );
    }

    // projection falls on the segment
    const Vec2 projection = v + ( ( w - v ) * t );

    (*q) = projection;

    return p.DistanceTo( projection );
}

float DistanceFromLineSegmentToPoint( float segmentX1, float segmentY1, float segmentX2, float segmentY2, float pX, float pY, float *qX, float *qY )
{
    Vec2 q;

    float distance = DistanceFromLineSegmentToPoint( Vec2( segmentX1, segmentY1 ), Vec2( segmentX2, segmentY2 ), Vec2( pX, pY ), &q );

    (*qX) = q._x;
    (*qY) = q._y;

    return distance;
}

void TestDistanceFromLineSegmentToPoint( float segmentX1, float segmentY1, float segmentX2, float segmentY2, float pX, float pY )
{
    float qX;
    float qY;
    float d = DistanceFromLineSegmentToPoint( segmentX1, segmentY1, segmentX2, segmentY2, pX, pY, &qX, &qY );
    printf( "line segment = ( ( %f, %f ), ( %f, %f ) ), p = ( %f, %f ), distance = %f, q = ( %f, %f )\n",
            segmentX1, segmentY1, segmentX2, segmentY2, pX, pY, d, qX, qY );
}

void TestDistanceFromLineSegmentToPoint()
{
    TestDistanceFromLineSegmentToPoint( 0, 0, 1, 1, 1, 0 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, 5, 4 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, 30, 15 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, -30, 15 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 10, 0, 5, 1 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 0, 10, 1, 5 );
}

ขอขอบคุณที่โพสต์สิ่งนี้ มีโครงสร้างที่ดีและแสดงความคิดเห็นและจัดรูปแบบ - เกือบทำให้ฉันลืมว่าฉันไม่ชอบ C ++ มากแค่ไหน ฉันใช้สิ่งนี้เพื่อสร้างเวอร์ชัน C # ที่สอดคล้องกันซึ่งตอนนี้ฉันได้โพสต์ที่นี่
RenniePet

10

โซลูชันหนึ่งบรรทัดที่ใช้อาร์คแทนเจนต์:

ความคิดคือการย้ายAถึง (0, 0) และหมุนสามเหลี่ยมตามเข็มนาฬิกาเพื่อให้Cวางบนแกน X เมื่อเกิดเหตุการณ์นี้ขึ้นโดยจะเป็นระยะทาง

  1. a angle = Atan (Cy - Ay, Cx - Axe);
  2. b angle = Atan (By - Ay, Bx - Axe);
  3. AB length = Sqrt ((Bx - Axe) ^ 2 + (โดย - Ay) ^ 2)
  4. โดย = Sin (bAngle - aAngle) * ABLength

ค#

public double Distance(Point a, Point b, Point c)
{
    // normalize points
    Point cn = new Point(c.X - a.X, c.Y - a.Y);
    Point bn = new Point(b.X - a.X, b.Y - a.Y);

    double angle = Math.Atan2(bn.Y, bn.X) - Math.Atan2(cn.Y, cn.X);
    double abLength = Math.Sqrt(bn.X*bn.X + bn.Y*bn.Y);

    return Math.Sin(angle)*abLength;
}

หนึ่งบรรทัด C # (ถูกแปลงเป็น SQL)

double distance = Math.Sin(Math.Atan2(b.Y - a.Y, b.X - a.X) - Math.Atan2(c.Y - a.Y, c.X - a.X)) * Math.Sqrt((b.X - a.X) * (b.X - a.X) + (b.Y - a.Y) * (b.Y - a.Y))

7

ลองพิจารณาการดัดแปลงนี้กับคำตอบของ Grumdrig ด้านบน หลายครั้งที่คุณพบว่าการไม่ระบุจุดลอยตัวอาจทำให้เกิดปัญหาได้ ฉันใช้คู่ผสมในรุ่นด้านล่าง แต่คุณสามารถเปลี่ยนเป็นลอยได้อย่างง่ายดาย ส่วนที่สำคัญคือมันใช้ epsilon เพื่อจัดการกับ "slop" นอกจากนี้คุณจะต้องการรู้ว่าจุดแยกเกิดขึ้นที่ไหนหรือเกิดอะไรขึ้นบ้าง หาก t ที่ส่งคืนคือ <0.0 หรือ> 1.0 จะไม่มีการชนกัน อย่างไรก็ตามแม้ว่าจะไม่มีการชนกันเกิดขึ้นหลายครั้งที่คุณต้องการทราบว่าจุดที่ใกล้ที่สุดในเซ็กเมนต์ไปยัง P คืออะไรฉันจึงใช้ qx และ qy เพื่อส่งคืนตำแหน่งนี้

double PointSegmentDistanceSquared( double px, double py,
                                    double p1x, double p1y,
                                    double p2x, double p2y,
                                    double& t,
                                    double& qx, double& qy)
{
    static const double kMinSegmentLenSquared = 0.00000001;  // adjust to suit.  If you use float, you'll probably want something like 0.000001f
    static const double kEpsilon = 1.0E-14;  // adjust to suit.  If you use floats, you'll probably want something like 1E-7f
    double dx = p2x - p1x;
    double dy = p2y - p1y;
    double dp1x = px - p1x;
    double dp1y = py - p1y;
    const double segLenSquared = (dx * dx) + (dy * dy);
    if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared)
    {
        // segment is a point.
        qx = p1x;
        qy = p1y;
        t = 0.0;
        return ((dp1x * dp1x) + (dp1y * dp1y));
    }
    else
    {
        // Project a line from p to the segment [p1,p2].  By considering the line
        // extending the segment, parameterized as p1 + (t * (p2 - p1)),
        // we find projection of point p onto the line. 
        // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2
        t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared;
        if (t < kEpsilon)
        {
            // intersects at or to the "left" of first segment vertex (p1x, p1y).  If t is approximately 0.0, then
            // intersection is at p1.  If t is less than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t > -kEpsilon)
            {
                // intersects at 1st segment vertex
                t = 0.0;
            }
            // set our 'intersection' point to p1.
            qx = p1x;
            qy = p1y;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)).
        }
        else if (t > (1.0 - kEpsilon))
        {
            // intersects at or to the "right" of second segment vertex (p2x, p2y).  If t is approximately 1.0, then
            // intersection is at p2.  If t is greater than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t < (1.0 + kEpsilon))
            {
                // intersects at 2nd segment vertex
                t = 1.0;
            }
            // set our 'intersection' point to p2.
            qx = p2x;
            qy = p2y;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)).
        }
        else
        {
            // The projection of the point to the point on the segment that is perpendicular succeeded and the point
            // is 'within' the bounds of the segment.  Set the intersection point as that projected point.
            qx = p1x + (t * dx);
            qy = p1y + (t * dy);
        }
        // return the squared distance from p to the intersection point.  Note that we return the squared distance
        // as an optimization because many times you just need to compare relative distances and the squared values
        // works fine for that.  If you want the ACTUAL distance, just take the square root of this value.
        double dpqx = px - qx;
        double dpqy = py - qy;
        return ((dpqx * dpqx) + (dpqy * dpqy));
    }
}

6

ฉันสมมติว่าคุณต้องการหาที่สั้นที่สุดระยะห่างระหว่างจุดและส่วนของเส้น; ในการทำเช่นนี้คุณจะต้องค้นหาบรรทัด (lineA) ซึ่งตั้งฉากกับส่วนของบรรทัด (lineB) ที่ผ่านจุดของคุณกำหนดจุดตัดระหว่างบรรทัดนั้น (lineA) และบรรทัดที่ผ่านส่วนของบรรทัด (lineB) ; หากจุดนั้นอยู่ระหว่างสองจุดในส่วนของเส้นของคุณระยะทางคือระยะทางระหว่างจุดของคุณกับจุดที่คุณเพิ่งพบซึ่งเป็นจุดตัดของ lineA และ lineB; หากจุดไม่ได้อยู่ระหว่างสองจุดในส่วนของเส้นของคุณคุณจะต้องได้ระยะห่างระหว่างจุดของคุณและใกล้กับปลายทั้งสองของส่วนของเส้น สิ่งนี้สามารถทำได้อย่างง่ายดายโดยการหาระยะห่างของสแควร์ (เพื่อหลีกเลี่ยงการรากที่สอง) ระหว่างจุดและจุดสองจุดของส่วนของเส้น; ไม่ว่าจะอยู่ใกล้ไหนจงเอาสแควร์รูทของอันนั้น


6

การใช้ C ++ / JavaScript ของ Grumdrig นั้นมีประโยชน์มากสำหรับฉันดังนั้นฉันจึงได้จัดเตรียมพอร์ต Python โดยตรงที่ฉันใช้ รหัสที่สมบูรณ์ที่นี่

class Point(object):
  def __init__(self, x, y):
    self.x = float(x)
    self.y = float(y)

def square(x):
  return x * x

def distance_squared(v, w):
  return square(v.x - w.x) + square(v.y - w.y)

def distance_point_segment_squared(p, v, w):
  # Segment length squared, |w-v|^2
  d2 = distance_squared(v, w) 
  if d2 == 0: 
    # v == w, return distance to v
    return distance_squared(p, v)
  # Consider the line extending the segment, parameterized as v + t (w - v).
  # We find projection of point p onto the line.
  # It falls where t = [(p-v) . (w-v)] / |w-v|^2
  t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / d2;
  if t < 0:
    # Beyond v end of the segment
    return distance_squared(p, v)
  elif t > 1.0:
    # Beyond w end of the segment
    return distance_squared(p, w)
  else:
    # Projection falls on the segment.
    proj = Point(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y))
    # print proj.x, proj.y
    return distance_squared(p, proj)

5

รหัส Matlab ซึ่งมี "การทดสอบตัวเอง" ในตัวหากพวกเขาเรียกใช้ฟังก์ชันโดยไม่มีข้อโต้แย้ง:

function r = distPointToLineSegment( xy0, xy1, xyP )
% r = distPointToLineSegment( xy0, xy1, xyP )

if( nargin < 3 )
    selfTest();
    r=0;
else
    vx = xy0(1)-xyP(1);
    vy = xy0(2)-xyP(2);
    ux = xy1(1)-xy0(1);
    uy = xy1(2)-xy0(2);
    lenSqr= (ux*ux+uy*uy);
    detP= -vx*ux + -vy*uy;

    if( detP < 0 )
        r = norm(xy0-xyP,2);
    elseif( detP > lenSqr )
        r = norm(xy1-xyP,2);
    else
        r = abs(ux*vy-uy*vx)/sqrt(lenSqr);
    end
end


    function selfTest()
        %#ok<*NASGU>
        disp(['invalid args, distPointToLineSegment running (recursive)  self-test...']);

        ptA = [1;1]; ptB = [-1;-1];
        ptC = [1/2;1/2];  % on the line
        ptD = [-2;-1.5];  % too far from line segment
        ptE = [1/2;0];    % should be same as perpendicular distance to line
        ptF = [1.5;1.5];      % along the A-B but outside of the segment

        distCtoAB = distPointToLineSegment(ptA,ptB,ptC)
        distDtoAB = distPointToLineSegment(ptA,ptB,ptD)
        distEtoAB = distPointToLineSegment(ptA,ptB,ptE)
        distFtoAB = distPointToLineSegment(ptA,ptB,ptF)
        figure(1); clf;
        circle = @(x, y, r, c) rectangle('Position', [x-r, y-r, 2*r, 2*r], ...
            'Curvature', [1 1], 'EdgeColor', c);
        plot([ptA(1) ptB(1)],[ptA(2) ptB(2)],'r-x'); hold on;
        plot(ptC(1),ptC(2),'b+'); circle(ptC(1),ptC(2), 0.5e-1, 'b');
        plot(ptD(1),ptD(2),'g+'); circle(ptD(1),ptD(2), distDtoAB, 'g');
        plot(ptE(1),ptE(2),'k+'); circle(ptE(1),ptE(2), distEtoAB, 'k');
        plot(ptF(1),ptF(2),'m+'); circle(ptF(1),ptF(2), distFtoAB, 'm');
        hold off;
        axis([-3 3 -3 3]); axis equal;
    end

end

ขอขอบคุณรหัส Matlab นี้จะคำนวณระยะทางที่สั้นที่สุดไปยังSEGMENTเส้นที่สั้นที่สุดและไม่ใช่ระยะทางไปยังเส้นที่ไม่มีที่สิ้นสุดซึ่งส่วนนั้นอยู่
Rudolf Meijering

4

และตอนนี้โซลูชันของฉันก็เช่นกัน ...... (Javascript)

มันเร็วมากเพราะฉันพยายามหลีกเลี่ยงฟังก์ชั่น Math.pow

อย่างที่คุณเห็นในตอนท้ายของฟังก์ชั่นฉันมีระยะห่างของเส้นตรง

รหัสมาจาก lib http://www.draw2d.org/graphiti/jsdoc/#!/example

/**
 * Static util function to determine is a point(px,py) on the line(x1,y1,x2,y2)
 * A simple hit test.
 * 
 * @return {boolean}
 * @static
 * @private
 * @param {Number} coronaWidth the accepted corona for the hit test
 * @param {Number} X1 x coordinate of the start point of the line
 * @param {Number} Y1 y coordinate of the start point of the line
 * @param {Number} X2 x coordinate of the end point of the line
 * @param {Number} Y2 y coordinate of the end point of the line
 * @param {Number} px x coordinate of the point to test
 * @param {Number} py y coordinate of the point to test
 **/
graphiti.shape.basic.Line.hit= function( coronaWidth, X1, Y1,  X2,  Y2, px, py)
{
  // Adjust vectors relative to X1,Y1
  // X2,Y2 becomes relative vector from X1,Y1 to end of segment
  X2 -= X1;
  Y2 -= Y1;
  // px,py becomes relative vector from X1,Y1 to test point
  px -= X1;
  py -= Y1;
  var dotprod = px * X2 + py * Y2;
  var projlenSq;
  if (dotprod <= 0.0) {
      // px,py is on the side of X1,Y1 away from X2,Y2
      // distance to segment is length of px,py vector
      // "length of its (clipped) projection" is now 0.0
      projlenSq = 0.0;
  } else {
      // switch to backwards vectors relative to X2,Y2
      // X2,Y2 are already the negative of X1,Y1=>X2,Y2
      // to get px,py to be the negative of px,py=>X2,Y2
      // the dot product of two negated vectors is the same
      // as the dot product of the two normal vectors
      px = X2 - px;
      py = Y2 - py;
      dotprod = px * X2 + py * Y2;
      if (dotprod <= 0.0) {
          // px,py is on the side of X2,Y2 away from X1,Y1
          // distance to segment is length of (backwards) px,py vector
          // "length of its (clipped) projection" is now 0.0
          projlenSq = 0.0;
      } else {
          // px,py is between X1,Y1 and X2,Y2
          // dotprod is the length of the px,py vector
          // projected on the X2,Y2=>X1,Y1 vector times the
          // length of the X2,Y2=>X1,Y1 vector
          projlenSq = dotprod * dotprod / (X2 * X2 + Y2 * Y2);
      }
  }
    // Distance to line is now the length of the relative point
    // vector minus the length of its projection onto the line
    // (which is zero if the projection falls outside the range
    //  of the line segment).
    var lenSq = px * px + py * py - projlenSq;
    if (lenSq < 0) {
        lenSq = 0;
    }
    return Math.sqrt(lenSq)<coronaWidth;
};

4

รหัสใน t-sql

จุดคือ (@px, @py) และส่วนของเส้นจะเริ่มจาก (@ax, @ay) ถึง (@bx, @by)

create function fn_sqr (@NumberToSquare decimal(18,10)) 
returns decimal(18,10)
as 
begin
    declare @Result decimal(18,10)
    set @Result = @NumberToSquare * @NumberToSquare
    return @Result
end
go

create function fn_Distance(@ax decimal (18,10) , @ay decimal (18,10), @bx decimal(18,10),  @by decimal(18,10)) 
returns decimal(18,10)
as
begin
    declare @Result decimal(18,10)
    set @Result = (select dbo.fn_sqr(@ax - @bx) + dbo.fn_sqr(@ay - @by) )
    return @Result
end
go

create function fn_DistanceToSegmentSquared(@px decimal(18,10), @py decimal(18,10), @ax decimal(18,10), @ay decimal(18,10), @bx decimal(18,10), @by decimal(18,10)) 
returns decimal(18,10)
as 
begin
    declare @l2 decimal(18,10)
    set @l2 = (select dbo.fn_Distance(@ax, @ay, @bx, @by))
    if @l2 = 0
        return dbo.fn_Distance(@px, @py, @ax, @ay)
    declare @t decimal(18,10)
    set @t = ((@px - @ax) * (@bx - @ax) + (@py - @ay) * (@by - @ay)) / @l2
    if (@t < 0) 
        return dbo.fn_Distance(@px, @py, @ax, @ay);
    if (@t > 1) 
        return dbo.fn_Distance(@px, @py, @bx, @by);
    return dbo.fn_Distance(@px, @py,  @ax + @t * (@bx - @ax),  @ay + @t * (@by - @ay))
end
go

create function fn_DistanceToSegment(@px decimal(18,10), @py decimal(18,10), @ax decimal(18,10), @ay decimal(18,10), @bx decimal(18,10), @by decimal(18,10)) 
returns decimal(18,10)
as 
begin
    return sqrt(dbo.fn_DistanceToSegmentSquared(@px, @py , @ax , @ay , @bx , @by ))
end
go

--example execution for distance from a point at (6,1) to line segment that runs from (4,2) to (2,1)
select dbo.fn_DistanceToSegment(6, 1, 4, 2, 2, 1) 
--result = 2.2360679775

--example execution for distance from a point at (-3,-2) to line segment that runs from (0,-2) to (-2,1)
select dbo.fn_DistanceToSegment(-3, -2, 0, -2, -2, 1) 
--result = 2.4961508830

--example execution for distance from a point at (0,-2) to line segment that runs from (0,-2) to (-2,1)
select dbo.fn_DistanceToSegment(0,-2, 0, -2, -2, 1) 
--result = 0.0000000000

4

ดูเหมือนว่าทุกคนใน StackOverflow ได้ให้คำตอบ (23 คำตอบ) ดังนั้นนี่คือการบริจาคของฉันสำหรับ C # ส่วนใหญ่จะขึ้นอยู่กับคำตอบของ M. Katz ซึ่งจะขึ้นอยู่กับคำตอบของ Grumdrig

   public struct MyVector
   {
      private readonly double _x, _y;


      // Constructor
      public MyVector(double x, double y)
      {
         _x = x;
         _y = y;
      }


      // Distance from this point to another point, squared
      private double DistanceSquared(MyVector otherPoint)
      {
         double dx = otherPoint._x - this._x;
         double dy = otherPoint._y - this._y;
         return dx * dx + dy * dy;
      }


      // Find the distance from this point to a line segment (which is not the same as from this 
      //  point to anywhere on an infinite line). Also returns the closest point.
      public double DistanceToLineSegment(MyVector lineSegmentPoint1, MyVector lineSegmentPoint2,
                                          out MyVector closestPoint)
      {
         return Math.Sqrt(DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2, 
                          out closestPoint));
      }


      // Same as above, but avoid using Sqrt(), saves a new nanoseconds in cases where you only want 
      //  to compare several distances to find the smallest or largest, but don't need the distance
      public double DistanceToLineSegmentSquared(MyVector lineSegmentPoint1, 
                                              MyVector lineSegmentPoint2, out MyVector closestPoint)
      {
         // Compute length of line segment (squared) and handle special case of coincident points
         double segmentLengthSquared = lineSegmentPoint1.DistanceSquared(lineSegmentPoint2);
         if (segmentLengthSquared < 1E-7f)  // Arbitrary "close enough for government work" value
         {
            closestPoint = lineSegmentPoint1;
            return this.DistanceSquared(closestPoint);
         }

         // Use the magic formula to compute the "projection" of this point on the infinite line
         MyVector lineSegment = lineSegmentPoint2 - lineSegmentPoint1;
         double t = (this - lineSegmentPoint1).DotProduct(lineSegment) / segmentLengthSquared;

         // Handle the two cases where the projection is not on the line segment, and the case where 
         //  the projection is on the segment
         if (t <= 0)
            closestPoint = lineSegmentPoint1;
         else if (t >= 1)
            closestPoint = lineSegmentPoint2;
         else 
            closestPoint = lineSegmentPoint1 + (lineSegment * t);
         return this.DistanceSquared(closestPoint);
      }


      public double DotProduct(MyVector otherVector)
      {
         return this._x * otherVector._x + this._y * otherVector._y;
      }

      public static MyVector operator +(MyVector leftVector, MyVector rightVector)
      {
         return new MyVector(leftVector._x + rightVector._x, leftVector._y + rightVector._y);
      }

      public static MyVector operator -(MyVector leftVector, MyVector rightVector)
      {
         return new MyVector(leftVector._x - rightVector._x, leftVector._y - rightVector._y);
      }

      public static MyVector operator *(MyVector aVector, double aScalar)
      {
         return new MyVector(aVector._x * aScalar, aVector._y * aScalar);
      }

      // Added using ReSharper due to CodeAnalysis nagging

      public bool Equals(MyVector other)
      {
         return _x.Equals(other._x) && _y.Equals(other._y);
      }

      public override bool Equals(object obj)
      {
         if (ReferenceEquals(null, obj)) return false;
         return obj is MyVector && Equals((MyVector) obj);
      }

      public override int GetHashCode()
      {
         unchecked
         {
            return (_x.GetHashCode()*397) ^ _y.GetHashCode();
         }
      }

      public static bool operator ==(MyVector left, MyVector right)
      {
         return left.Equals(right);
      }

      public static bool operator !=(MyVector left, MyVector right)
      {
         return !left.Equals(right);
      }
   }

และนี่คือโปรแกรมทดสอบเล็กน้อย

   public static class JustTesting
   {
      public static void Main()
      {
         Stopwatch stopwatch = new Stopwatch();
         stopwatch.Start();

         for (int i = 0; i < 10000000; i++)
         {
            TestIt(1, 0, 0, 0, 1, 1, 0.70710678118654757);
            TestIt(5, 4, 0, 0, 20, 10, 1.3416407864998738);
            TestIt(30, 15, 0, 0, 20, 10, 11.180339887498949);
            TestIt(-30, 15, 0, 0, 20, 10, 33.541019662496844);
            TestIt(5, 1, 0, 0, 10, 0, 1.0);
            TestIt(1, 5, 0, 0, 0, 10, 1.0);
         }

         stopwatch.Stop();
         TimeSpan timeSpan = stopwatch.Elapsed;
      }


      private static void TestIt(float aPointX, float aPointY, 
                                 float lineSegmentPoint1X, float lineSegmentPoint1Y, 
                                 float lineSegmentPoint2X, float lineSegmentPoint2Y, 
                                 double expectedAnswer)
      {
         // Katz
         double d1 = DistanceFromPointToLineSegment(new MyVector(aPointX, aPointY), 
                                              new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y), 
                                              new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y));
         Debug.Assert(d1 == expectedAnswer);

         /*
         // Katz using squared distance
         double d2 = DistanceFromPointToLineSegmentSquared(new MyVector(aPointX, aPointY), 
                                              new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y), 
                                              new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y));
         Debug.Assert(Math.Abs(d2 - expectedAnswer * expectedAnswer) < 1E-7f);
          */

         /*
         // Matti (optimized)
         double d3 = FloatVector.DistanceToLineSegment(new PointF(aPointX, aPointY), 
                                                new PointF(lineSegmentPoint1X, lineSegmentPoint1Y), 
                                                new PointF(lineSegmentPoint2X, lineSegmentPoint2Y));
         Debug.Assert(Math.Abs(d3 - expectedAnswer) < 1E-7f);
          */
      }

      private static double DistanceFromPointToLineSegment(MyVector aPoint, 
                                             MyVector lineSegmentPoint1, MyVector lineSegmentPoint2)
      {
         MyVector closestPoint;  // Not used
         return aPoint.DistanceToLineSegment(lineSegmentPoint1, lineSegmentPoint2, 
                                             out closestPoint);
      }

      private static double DistanceFromPointToLineSegmentSquared(MyVector aPoint, 
                                             MyVector lineSegmentPoint1, MyVector lineSegmentPoint2)
      {
         MyVector closestPoint;  // Not used
         return aPoint.DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2, 
                                                    out closestPoint);
      }
   }

อย่างที่คุณเห็นฉันพยายามวัดความแตกต่างระหว่างการใช้เวอร์ชันที่หลีกเลี่ยงวิธี Sqrt () และเวอร์ชันปกติ การทดสอบของฉันระบุว่าคุณสามารถบันทึกได้ประมาณ 2.5% แต่ฉันไม่แน่ใจด้วยซ้ำ - ความหลากหลายของการทดสอบที่หลากหลายนั้นมีขนาดเท่ากัน ฉันยังลองวัดรุ่นที่โพสต์โดย Matti (บวกกับการเพิ่มประสิทธิภาพที่ชัดเจน) และรุ่นนั้นดูเหมือนจะช้ากว่ารุ่นประมาณ 4% ตามรหัส Katz / Grumdrig

แก้ไข: บังเอิญฉันยังลองวัดวิธีที่ค้นหาระยะทางไปยังเส้นไม่สิ้นสุด (ไม่ใช่ส่วนของเส้น) โดยใช้ผลิตภัณฑ์ข้าม (และ Sqrt ()) และมันเร็วขึ้นประมาณ 32%


3

นี่คือเวอร์ชัน C ++ ของ devnullicus ที่แปลงเป็น C # สำหรับการติดตั้งของฉันฉันจำเป็นต้องรู้จุดตัดและพบวิธีแก้ปัญหาของเขาเพื่อให้ทำงานได้ดี

public static bool PointSegmentDistanceSquared(PointF point, PointF lineStart, PointF lineEnd, out double distance, out PointF intersectPoint)
{
    const double kMinSegmentLenSquared = 0.00000001; // adjust to suit.  If you use float, you'll probably want something like 0.000001f
    const double kEpsilon = 1.0E-14; // adjust to suit.  If you use floats, you'll probably want something like 1E-7f
    double dX = lineEnd.X - lineStart.X;
    double dY = lineEnd.Y - lineStart.Y;
    double dp1X = point.X - lineStart.X;
    double dp1Y = point.Y - lineStart.Y;
    double segLenSquared = (dX * dX) + (dY * dY);
    double t = 0.0;

    if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared)
    {
        // segment is a point.
        intersectPoint = lineStart;
        t = 0.0;
        distance = ((dp1X * dp1X) + (dp1Y * dp1Y));
    }
    else
    {
        // Project a line from p to the segment [p1,p2].  By considering the line
        // extending the segment, parameterized as p1 + (t * (p2 - p1)),
        // we find projection of point p onto the line. 
        // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2
        t = ((dp1X * dX) + (dp1Y * dY)) / segLenSquared;
        if (t < kEpsilon)
        {
            // intersects at or to the "left" of first segment vertex (lineStart.X, lineStart.Y).  If t is approximately 0.0, then
            // intersection is at p1.  If t is less than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t > -kEpsilon)
            {
                // intersects at 1st segment vertex
                t = 0.0;
            }
            // set our 'intersection' point to p1.
            intersectPoint = lineStart;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then intersectPoint.X would be (lineStart.X + (t * dx)) and intersectPoint.Y would be (lineStart.Y + (t * dy)).
        }
        else if (t > (1.0 - kEpsilon))
        {
            // intersects at or to the "right" of second segment vertex (lineEnd.X, lineEnd.Y).  If t is approximately 1.0, then
            // intersection is at p2.  If t is greater than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t < (1.0 + kEpsilon))
            {
                // intersects at 2nd segment vertex
                t = 1.0;
            }
            // set our 'intersection' point to p2.
            intersectPoint = lineEnd;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then intersectPoint.X would be (lineStart.X + (t * dx)) and intersectPoint.Y would be (lineStart.Y + (t * dy)).
        }
        else
        {
            // The projection of the point to the point on the segment that is perpendicular succeeded and the point
            // is 'within' the bounds of the segment.  Set the intersection point as that projected point.
            intersectPoint = new PointF((float)(lineStart.X + (t * dX)), (float)(lineStart.Y + (t * dY)));
        }
        // return the squared distance from p to the intersection point.  Note that we return the squared distance
        // as an optimization because many times you just need to compare relative distances and the squared values
        // works fine for that.  If you want the ACTUAL distance, just take the square root of this value.
        double dpqX = point.X - intersectPoint.X;
        double dpqY = point.Y - intersectPoint.Y;

        distance = ((dpqX * dpqX) + (dpqY * dpqY));
    }

    return true;
}

ทำงานเหมือนจับใจ !! ช่วยฉันด้วยเวลานับไม่ถ้วน ขอบคุณมาก!!
สตีฟจอห์นสัน

3

นี่คือการใช้ Swift

    /* Distance from a point (p1) to line l1 l2 */
func distanceFromPoint(p: CGPoint, toLineSegment l1: CGPoint, and l2: CGPoint) -> CGFloat {
    let A = p.x - l1.x
    let B = p.y - l1.y
    let C = l2.x - l1.x
    let D = l2.y - l1.y

    let dot = A * C + B * D
    let len_sq = C * C + D * D
    let param = dot / len_sq

    var xx, yy: CGFloat

    if param < 0 || (l1.x == l2.x && l1.y == l2.y) {
        xx = l1.x
        yy = l1.y
    } else if param > 1 {
        xx = l2.x
        yy = l2.y
    } else {
        xx = l1.x + param * C
        yy = l1.y + param * D
    }

    let dx = p.x - xx
    let dy = p.y - yy

    return sqrt(dx * dx + dy * dy)
}

3

ค#

ดัดแปลงจาก@Grumdrig

public static double MinimumDistanceToLineSegment(this Point p,
    Line line)
{
    var v = line.StartPoint;
    var w = line.EndPoint;

    double lengthSquared = DistanceSquared(v, w);

    if (lengthSquared == 0.0)
        return Distance(p, v);

    double t = Math.Max(0, Math.Min(1, DotProduct(p - v, w - v) / lengthSquared));
    var projection = v + t * (w - v);

    return Distance(p, projection);
}

public static double Distance(Point a, Point b)
{
    return Math.Sqrt(DistanceSquared(a, b));
}

public static double DistanceSquared(Point a, Point b)
{
    var d = a - b;
    return DotProduct(d, d);
}

public static double DotProduct(Point a, Point b)
{
    return (a.X * b.X) + (a.Y * b.Y);
}

พยายามใช้รหัสนี้ดูเหมือนว่าทำงานไม่ถูกต้อง ดูเหมือนว่าจะได้รับระยะทางที่ผิดบางครั้ง
WDUK

3

โซลูชัน 2D และ 3D

พิจารณาการเปลี่ยนแปลงของพื้นฐานดังกล่าวว่าส่วนของเส้นจะกลายเป็นและจุด(0, 0, 0)-(d, 0, 0) (u, v, 0)ระยะทางที่สั้นที่สุดเกิดขึ้นในระนาบนั้นและกำหนดโดย

    u ≤ 0 -> d(A, C)
0 ≤ u ≤ d -> |v|
d ≤ u     -> d(B, C)

(ระยะทางถึงหนึ่งในจุดสิ้นสุดหรือเส้นที่รองรับขึ้นอยู่กับการฉายไปที่เส้นนั้นสถานที่ iso- ระยะทางที่ทำจากสองครึ่งวงกลมและสองส่วนของเส้น)

ป้อนคำอธิบายรูปภาพที่นี่

ในนิพจน์ด้านบน d คือความยาวของเซ็กเมนต์ AB และ u, v เป็นผลิตภัณฑ์เซนต์คิตส์และ (โมดูลัสของ) ผลิตภัณฑ์ข้ามของ AB / d (เวกเตอร์หน่วยในทิศทางของ AB) และ AC ดังนั้นเวกเตอร์

AB.AC ≤ 0             -> |AC|
    0 ≤ AB.AC ≤ AB²   -> |ABxAC|/|AB|
          AB² ≤ AB.AC -> |BC|

2

ดูกล่องเครื่องมือ Matlab GEOMETRY ในเว็บไซต์ต่อไปนี้: http://people.sc.fsu.edu/~jburkardt/m_src/geometry/geometry.html

ctrl + f และพิมพ์ "ส่วน" เพื่อค้นหาฟังก์ชั่นที่เกี่ยวข้องกับส่วนของเส้นตรง ฟังก์ชั่น "segment_point_dist_2d.m" และ "segment_point_dist_3d.m" เป็นสิ่งที่คุณต้องการ

รหัส GEOMETRY มีให้ในรุ่น C และรุ่น C ++ และ FORTRAN77 และ FORTRAN90 รุ่นและรุ่น MATLAB


2

รุ่น AutoHotkeys อ้างอิงจาก Javascript ของ Joshua:

plDist(x, y, x1, y1, x2, y2) {
    A:= x - x1
    B:= y - y1
    C:= x2 - x1
    D:= y2 - y1

    dot:= A*C + B*D
    sqLen:= C*C + D*D
    param:= dot / sqLen

    if (param < 0 || ((x1 = x2) && (y1 = y2))) {
        xx:= x1
        yy:= y1
    } else if (param > 1) {
        xx:= x2
        yy:= y2
    } else {
        xx:= x1 + param*C
        yy:= y1 + param*D
    }

    dx:= x - xx
    dy:= y - yy

    return sqrt(dx*dx + dy*dy)
}

2

ไม่เห็นการใช้งาน Java ที่นี่ดังนั้นฉันจึงแปลฟังก์ชั่น Javascript จากคำตอบที่ยอมรับไปยังโค้ด Java:

static double sqr(double x) {
    return x * x;
}
static double dist2(DoublePoint v, DoublePoint w) {
    return sqr(v.x - w.x) + sqr(v.y - w.y);
}
static double distToSegmentSquared(DoublePoint p, DoublePoint v, DoublePoint w) {
    double l2 = dist2(v, w);
    if (l2 == 0) return dist2(p, v);
    double t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
    if (t < 0) return dist2(p, v);
    if (t > 1) return dist2(p, w);
    return dist2(p, new DoublePoint(
            v.x + t * (w.x - v.x),
            v.y + t * (w.y - v.y)
    ));
}
static double distToSegment(DoublePoint p, DoublePoint v, DoublePoint w) {
    return Math.sqrt(distToSegmentSquared(p, v, w));
}
static class DoublePoint {
    public double x;
    public double y;

    public DoublePoint(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

2

รุ่น WPF:

public class LineSegment
{
    private readonly Vector _offset;
    private readonly Vector _vector;

    public LineSegment(Point start, Point end)
    {
        _offset = (Vector)start;
        _vector = (Vector)(end - _offset);
    }

    public double DistanceTo(Point pt)
    {
        var v = (Vector)pt - _offset;

        // first, find a projection point on the segment in parametric form (0..1)
        var p = (v * _vector) / _vector.LengthSquared;

        // and limit it so it lays inside the segment
        p = Math.Min(Math.Max(p, 0), 1);

        // now, find the distance from that point to our point
        return (_vector * p - v).Length;
    }
}

1

นี่คือรหัสที่ฉันเขียน {x:5, y:7}รหัสนี้อนุมานว่าเป็นจุดที่กำหนดไว้ในรูปแบบของ โปรดทราบว่านี่ไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุด แต่เป็นโค้ดที่ง่ายและเข้าใจง่ายที่สุดที่ฉันสามารถหาได้

// a, b, and c in the code below are all points

function distance(a, b)
{
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return Math.sqrt(dx*dx + dy*dy);
}

function Segment(a, b)
{
    var ab = {
        x: b.x - a.x,
        y: b.y - a.y
    };
    var length = distance(a, b);

    function cross(c) {
        return ab.x * (c.y-a.y) - ab.y * (c.x-a.x);
    };

    this.distanceFrom = function(c) {
        return Math.min(distance(a,c),
                        distance(b,c),
                        Math.abs(cross(c) / length));
    };
}

1
รหัสนี้มีข้อบกพร่อง จุดใกล้เส้นที่เซ็กเมนต์อยู่ แต่ไกลออกไปทางปลายด้านหนึ่งของเซกเมนต์จะถูกตัดสินอย่างไม่ถูกต้องว่าอยู่ใกล้กับเซกเมนต์
Grumdrig

น่าสนใจฉันจะตรวจสอบเรื่องนี้ในครั้งต่อไปที่ฉันทำงานกับ codebase นี้เพื่อยืนยันการยืนยันของคุณ ขอบคุณสำหรับทิป.
Eli Courtwright

1

ฟังก์ชั่นด้านบนไม่ทำงานบนเส้นแนวตั้ง นี่คือฟังก์ชั่นที่ใช้งานได้ดี! สอดคล้องกับคะแนน p1, p2 และ CheckPoint คือ p;

public float DistanceOfPointToLine2(PointF p1, PointF p2, PointF p)
{
  //          (y1-y2)x + (x2-x1)y + (x1y2-x2y1)
  //d(P,L) = --------------------------------
  //         sqrt( (x2-x1)pow2 + (y2-y1)pow2 )

  double ch = (p1.Y - p2.Y) * p.X + (p2.X - p1.X) * p.Y + (p1.X * p2.Y - p2.X * p1.Y);
  double del = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
  double d = ch / del;
  return (float)d;
}

ไม่ตอบคำถาม ใช้งานได้เฉพาะกับเส้น (เส้นที่ขยายได้ไม่ จำกัด พื้นที่) ไม่ใช่ส่วนของเส้น (ซึ่งมีความยาว จำกัด )
ตรินิแดด

"ฟังก์ชั่นด้านบน" เป็นการอ้างอิงที่ไม่ชัดเจน (ระคายเคืองฉันเพราะบางครั้งคำตอบนี้จะปรากฏใต้คำตอบของฉัน.)
RenniePet

1

นี่คือสิ่งเดียวกับคำตอบ C ++ แต่ส่งไปยังปาสกาล ลำดับของพารามิเตอร์พอยน์มีการเปลี่ยนแปลงเพื่อให้เหมาะกับรหัสของฉัน แต่เป็นสิ่งเดียวกัน

function Dot(const p1, p2: PointF): double;
begin
  Result := p1.x * p2.x + p1.y * p2.y;
end;
function SubPoint(const p1, p2: PointF): PointF;
begin
  result.x := p1.x - p2.x;
  result.y := p1.y - p2.y;
end;

function ShortestDistance2(const p,v,w : PointF) : double;
var
  l2,t : double;
  projection,tt: PointF;
begin
  // Return minimum distance between line segment vw and point p
  //l2 := length_squared(v, w);  // i.e. |w-v|^2 -  avoid a sqrt
  l2 := Distance(v,w);
  l2 := MPower(l2,2);
  if (l2 = 0.0) then begin
    result:= Distance(p, v);   // v == w case
    exit;
  end;
  // Consider the line extending the segment, parameterized as v + t (w - v).
  // We find projection of point p onto the line.
  // It falls where t = [(p-v) . (w-v)] / |w-v|^2
  t := Dot(SubPoint(p,v),SubPoint(w,v)) / l2;
  if (t < 0.0) then begin
    result := Distance(p, v);       // Beyond the 'v' end of the segment
    exit;
  end
  else if (t > 1.0) then begin
    result := Distance(p, w);  // Beyond the 'w' end of the segment
    exit;
  end;
  //projection := v + t * (w - v);  // Projection falls on the segment
  tt.x := v.x + t * (w.x - v.x);
  tt.y := v.y + t * (w.y - v.y);
  result := Distance(p, tt);
end;

มีปัญหาหลายอย่างกับคำตอบนี้: ชนิด PointF ไม่ได้ถูกประกาศ (อาจเป็นประเภทมาตรฐานในการใช้งาน Pascal บางอย่าง) อาจเป็นระเบียน x, y: double; จบ; 2. ฟังก์ชั่นระยะทางและ MPower ไม่ได้ถูกประกาศและไม่มีคำอธิบายสิ่งที่พวกเขาทำ (เราสามารถเดาได้ใช่) 3. มีการประกาศการฉายตัวแปร แต่ไม่เคยใช้ โดยรวมแล้วทำให้เป็นคำตอบที่ค่อนข้างแย่
dummzeuch
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.