วิธีการตรวจจับเส้น 2D บนการชนกันของเส้น


13

ฉันเป็นนักพัฒนาเกมแอ็คชั่นแฟลชผู้ซึ่งย้อนหลังไปกับคณิตศาสตร์อยู่บ้าง แต่ฉันคิดว่าฟิสิกส์น่าสนใจและเท่ห์มาก

สำหรับการอ้างอิงนี้เป็นเกมที่คล้ายกับเกมที่ฉันทำ: เกมแฟลชแก้

ฉันได้สร้างเกมที่ไม่พันกันนี้จนเกือบเต็มไปด้วยตรรกะ แต่เมื่อเส้นสองเส้นตัดกันฉันต้องการเส้นตัดเหล่านั้นหรือ 'พันกัน' เพื่อแสดงสีที่แตกต่างกัน สีแดง

มันจะเป็นคนที่ใจดีจริงๆถ้าคุณสามารถแนะนำอัลกอริทึมสำหรับการตรวจจับการชนกันของส่วนของเส้น ฉันเป็นคนที่ชอบคิดว่า 'เห็น' มากกว่า 'เลขคณิต' :)

แก้ไข: ฉันต้องการเพิ่มไดอะแกรมสองสามรายการเพื่อให้สื่อความคิดชัดเจนยิ่งขึ้น

ไม่มีทางแยก ไม่มีทางแยก การตัด ไม่มีทางแยก

ป.ล. ฉันพยายามทำหน้าที่เป็น

private function isIntersecting(A:Point, B:Point, C:Point, D:Point):Boolean

ขอบคุณล่วงหน้า.


6
นี่คือคำอธิบายที่ผิดหวังไม่ใช่ภาพของปัญหาที่เกิดขึ้น แต่มันเป็นอัลกอริทึมและมันจะทำให้รู้สึกว่าคุณสามารถนำตัวเองให้อ่านคณิตศาสตร์ของพวกเขาlocal.wasp.uwa.edu.au/~pbourke/geometry/lineline2dมันอาจจะ จะหนักถ้าคณิตศาสตร์เวกเตอร์ของคุณอ่อนแอ ฉันเข้าใจ - ฉันชอบคำอธิบายด้วยภาพ ฉันจะพยายามหาเวลาในภายหลังเพื่อขีดเส้นขยุกขยิกนี้ แต่ถ้าใครบางคนที่มีความโน้มเอียงทางศิลปะเห็นลิงค์นี้และมีเวลาก่อนที่ฉันจะไปที่มัน!
Anko

คำตอบ:


18

ฉันใช้วิธีการต่อไปนี้ซึ่งเป็นเพียงการใช้อัลกอริทึมนี้ มันอยู่ใน C # แต่การแปลเป็น ActionScript น่าจะเป็นเรื่องเล็กน้อย

bool IsIntersecting(Point a, Point b, Point c, Point d)
{
    float denominator = ((b.X - a.X) * (d.Y - c.Y)) - ((b.Y - a.Y) * (d.X - c.X));
    float numerator1 = ((a.Y - c.Y) * (d.X - c.X)) - ((a.X - c.X) * (d.Y - c.Y));
    float numerator2 = ((a.Y - c.Y) * (b.X - a.X)) - ((a.X - c.X) * (b.Y - a.Y));

    // Detect coincident lines (has a problem, read below)
    if (denominator == 0) return numerator1 == 0 && numerator2 == 0;

    float r = numerator1 / denominator;
    float s = numerator2 / denominator;

    return (r >= 0 && r <= 1) && (s >= 0 && s <= 1);
}

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

แก้ไข

ฉันไม่ได้รับผลลัพธ์จากอัลกอริทึมนี้ขออภัย!

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

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


ฉันไม่ได้รับผลลัพธ์จากอัลกอริทึมนี้ขออภัย!
พระนารายณ์

4
@Vish ปัญหาอะไรที่คุณมี? ฉันทดสอบสำเนาอัลกอริทึมที่แน่นอนนี้ก่อนโพสต์และทำงานได้อย่างไร้ที่ติยกเว้นกรณีเดียวที่อธิบายไว้
David Gouveia

จากนั้นให้ฉันลองอีกครั้งฉันอาจผสมคณิตศาสตร์เข้าด้วยกัน ฉันจะแจ้งให้คุณทราบในไม่ช้า ขอบคุณตัน, nyways :)
พระนารายณ์

1
ฉันได้ผลลัพธ์ที่ต้องการจากอัลกอริทึมของคุณขอบคุณ @DavidGouveia
พระนารายณ์

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

4

ไม่มีส่วนงาน! ดังนั้นจึงไม่มีปัญหาเรื่องความแม่นยำหรือการหารด้วยศูนย์

ส่วนของบรรทัดที่ 1 คือ A ถึง B ส่วนของบรรทัดที่ 2 คือ C ถึง D

เส้นคือเส้นที่ไม่มีที่สิ้นสุดส่วนของเส้นตรงเป็นส่วนที่กำหนดไว้ของเส้นนั้น

ตรวจสอบว่ากล่องทั้งสองถูกตัดกันหรือไม่: ถ้าไม่มีทางแยก -> ไม่มีกากบาท! (การคำนวณเสร็จแล้วส่งคืน false)

ตรวจสอบว่าบรรทัด seg 1 straddles บรรทัด seg 2 และถ้า seg seg บรรทัด 2 straddles บรรทัดที่ 1 (เช่น. ส่วนของบรรทัดที่ 1 อยู่ทั้งสองด้านของบรรทัดที่กำหนดโดยบรรทัดส่วนที่ 2)

สิ่งนี้สามารถทำได้โดยการแปลคะแนนทั้งหมดโดย -A (เช่นคุณย้าย 2 บรรทัดดังนั้น A อยู่ใน origo (0,0))

จากนั้นคุณตรวจสอบว่าจุด C และ D อยู่ที่ด้านต่าง ๆ ของบรรทัดที่กำหนดโดย 0,0 ถึง B

//Cross Product (hope I got it right here)
float fC= (B.x*C.y) - (B.y*C.x); //<0 == to the left, >0 == to the right
float fD= (B.x*D.y) - (B.y*D.x);

if( (fc<0) && (fd<0)) //both to the left  -> No Cross!
if( (fc>0) && (fd>0)) //both to the right -> No Cross!

หากคุณยังไม่ได้รับ "No Cross" ให้ใช้ไม่ใช่ A, B กับ C, D แต่ C, D กับ A, B (calcs เดียวกันเพียงแค่สลับ A และ C, B และ D) หากไม่มี "ไม่ข้าม!" ถ้าอย่างนั้นคุณมีทางแยก!

ฉันค้นหาการคำนวณที่แน่นอนสำหรับผลิตภัณฑ์ไขว้และพบว่าโพสต์บล็อกนี้อธิบายถึงวิธีการเช่นกัน


1
ฉันขอโทษ แต่ฉันไม่ค่อยดีกับคณิตศาสตร์แบบเวกเตอร์ฉันใช้อัลกอริทึมนี้เช่นนี้ แต่ไม่ได้ผลขอโทษ!
พระนารายณ์

1
มันควรจะทำงานถ้าคุณสามารถแสดงรหัสของคุณให้เราเห็นเราสามารถช่วยคุณได้
Valmond

ดี! อย่างไรก็ตามลิงก์เสีย
clabe45

มีบางสิ่งที่คุณสามารถเพิ่มลงในสิ่งนี้เพื่อให้ได้จุดตัดกันหรือไม่?
SeanRamey

1

ฉันแค่อยากบอกว่าฉันต้องการเกม Gamemaker Studio ของฉันและมันทำงานได้ดี:

///scr_line_collision(x1,y1,x2,y2,x3,y3,x4,y4)

var denominator= ((argument2 - argument0) * (argument7 - argument5)) - ((argument3 - argument1) * (argument6 - argument4));
var numerator1 = ((argument1 - argument5) * (argument6 - argument4)) - ((argument0 - argument4) * (argument7 - argument5));
var numerator2 = ((argument1 - argument5) * (argument2 - argument0)) - ((argument0 - argument4) * (argument3 - argument1));

// Detect coincident lines
if (denominator == 0) {return (numerator1 == 0 && numerator2 == 0)}

var r = numerator1 / denominator;
var s = numerator2 / denominator;

return ((r >= 0 && r <= 1) && (s >= 0 && s <= 1));

ฉันคิดว่าคำตอบนี้สามารถปรับปรุงได้จริง ๆ ถ้าคุณอธิบายว่ารหัสทำอะไร
TomTsagk

1

คำตอบที่ยอมรับให้คำตอบที่ผิดในกรณีนี้:

x1 = 0;
y1 = 0;
x2 = 10;
y2 = 10;

x3 = 10.1;
y3 = 10.1;
x4 = 15;
y4 = 15;

เห็นได้ชัดว่าเส้นเหล่านี้ไม่ตัดกัน แต่ตามฟังก์ชั่นใน "คำตอบที่ถูกต้อง" เส้นจะตัดกัน

นี่คือสิ่งที่ฉันใช้:

function do_lines_intersect(px1,py1,px2,py2,px3,py3,px4,py4) {
  var ua = 0.0;
  var ub = 0.0;
  var ud = (py4 - py3) * (px2 - px1) - (px4 - px3) * (py2 - py1);


  if (ud != 0) {
    ua = ((px4 - px3) * (py1 - py3) - (py4 - py3) * (px1 - px3)) / ud;
    ub = ((px2 - px1) * (py1 - py3) - (py2 - py1) * (px1 - px3)) / ud;
        if (ua < 0.0 || ua > 1.0 || ub < 0.0 || ub > 1.0) ua = 0.0;
  }

  return ua;
}

ส่งคืน 0 = บรรทัดไม่ตัดกัน

ส่งคืน> 0 = เส้นตัดกัน


อัปเดตเพื่อตอบคำถาม:

ฉันไม่ได้สร้างรหัสนี้เอง อายุมากกว่า 5 ปีและฉันไม่รู้ว่าแหล่งดั้งเดิมคืออะไร แต่..

ฉันคิดว่าค่าตอบแทนเป็นตำแหน่งสัมพัทธ์ของบรรทัดแรกที่ข้าม (เพื่ออธิบายไม่ดี) ในการคำนวณจุดตัดที่คุณอาจใช้ lerp ดังนี้:

l = do_lines_intersect(...)
if (l > 0) {
    intersect_pos_x = l * (px2-px1);
    intersect_pos_y = l * (py2-py1);
} else {
    // lines do not cross
}

(ฉันไม่ได้ทดสอบนี้)


มีรุ่นนี้ที่ส่งกลับจุดตัดหรือไม่
SeanRamey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.