ฉันจะทราบได้อย่างไรว่าจุด 2D นั้นอยู่ในรูปหลายเหลี่ยมหรือไม่


497

ฉันกำลังพยายามสร้างจุด 2D ที่รวดเร็วภายในอัลกอริทึมรูปหลายเหลี่ยมเพื่อใช้ในการทดสอบการตี (เช่นPolygon.contains(p:Point)) ข้อเสนอแนะสำหรับเทคนิคที่มีประสิทธิภาพจะได้รับการชื่นชม


คุณลืมที่จะบอกเราเกี่ยวกับการรับรู้ของคุณเกี่ยวกับคำถามของถนัดขวาหรือซ้าย - ซึ่งสามารถตีความได้ว่า "ข้างใน" vs "นอก" - RT
Richard T

13
ใช่ฉันรู้แล้วว่าคำถามนี้ยังไม่ระบุรายละเอียดมากมาย แต่ถึงตอนนี้ฉันก็สนใจที่จะเห็นการตอบสนองที่หลากหลาย
Scott Evernden

4
รูปหลายเหลี่ยม 90 ด้านเรียกว่า enneacontagon และรูปหลายเหลี่ยมด้าน 10,000 เรียกว่ารูปหลายเหลี่ยม

"สง่างามที่สุด" อยู่นอกเป้าหมายเนื่องจากฉันมีปัญหากับการหา "งานทั้งหมด" - อัลกอริทึม ฉันต้องคิดออกเอง: stackoverflow.com/questions/14818567/…
davidkonrad

คำตอบ:


732

สำหรับกราฟิกฉันไม่ชอบจำนวนเต็ม ระบบจำนวนมากใช้จำนวนเต็มสำหรับการวาด UI (พิกเซลเป็น ints ทั้งหมด) แต่ macOS เช่นใช้การลอยสำหรับทุกสิ่ง macOS รู้จุดเท่านั้นและจุดหนึ่งสามารถแปลเป็นหนึ่งพิกเซลได้ แต่ขึ้นอยู่กับความละเอียดของจอภาพมันอาจแปลเป็นอย่างอื่น บนหน้าจอเรตินาครึ่งจุด (0.5 / 0.5) คือพิกเซล ถึงกระนั้นฉันไม่เคยสังเกตว่า macOS UIs ช้ากว่า UIs อื่น ๆ อย่างมีนัยสำคัญ หลังจาก 3D APIs ทั้งหมด (OpenGL หรือ Direct3D) ยังทำงานร่วมกับการลอยและไลบรารีกราฟิกที่ทันสมัยมักใช้ประโยชน์จากการเร่งความเร็วของ GPU

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

(9/1), (4/3), (2/7), (8/2), (3/6)เช่นคุณมีจุด ซึ่งหมายความว่า Xmin คือ 2, Xmax คือ 9, Ymin คือ 1 และ Ymax คือ 7 จุดนอกสี่เหลี่ยมที่มีขอบทั้งสอง (2/1) และ (9/7) ไม่สามารถอยู่ในรูปหลายเหลี่ยมได้

// p is your point, p.x is the x coord, p.y is the y coord
if (p.x < Xmin || p.x > Xmax || p.y < Ymin || p.y > Ymax) {
    // Definitely not within the polygon!
}

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

รูปหลายเหลี่ยมที่ไม่มีรู

และนี่คืออันที่มีรู:

รูปหลายเหลี่ยมมีรู

สีเขียวมีรูตรงกลาง!

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

แสดงให้เห็นว่ารังสีตัดผ่านรูปหลายเหลี่ยมได้อย่างไร

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

คุณยังมีกล่องของขอบเขตด้านบนจำได้ไหม เพียงแค่เลือกจุดนอกกรอบและใช้เป็นจุดเริ่มต้นสำหรับรังสีของคุณ เช่นจุด(Xmin - e/p.y)อยู่นอกรูปหลายเหลี่ยมได้อย่างแน่นอน

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

ตอนนี้เรามีรังสีพร้อมพิกัดเริ่มต้นและจุดสิ้นสุดปัญหาการเลื่อนจาก " คือจุดที่อยู่ภายในรูปหลายเหลี่ยม " เป็น " ความถี่ที่รังสีตัดกับด้านรูปหลายเหลี่ยม " ดังนั้นเราจึงไม่สามารถทำงานกับคะแนนรูปหลายเหลี่ยมเหมือนก่อนตอนนี้เราต้องการด้านที่แท้จริง ด้านถูกกำหนดโดยจุดสองจุดเสมอ

side 1: (X1/Y1)-(X2/Y2)
side 2: (X2/Y2)-(X3/Y3)
side 3: (X3/Y3)-(X4/Y4)
:

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

// Test the ray against all sides
int intersections = 0;
for (side = 0; side < numberOfSides; side++) {
    // Test if current side intersects with ray.
    // If yes, intersections++;
}
if ((intersections & 1) == 1) {
    // Inside of polygon
} else {
    // Outside of polygon
}

จนถึงตอนนี้ แต่คุณจะทดสอบว่าเวกเตอร์สองตัวตัดกันได้อย่างไร นี่คือรหัส C (ไม่ผ่านการทดสอบ) ที่ควรทำเคล็ดลับ:

#define NO 0
#define YES 1
#define COLLINEAR 2

int areIntersecting(
    float v1x1, float v1y1, float v1x2, float v1y2,
    float v2x1, float v2y1, float v2x2, float v2y2
) {
    float d1, d2;
    float a1, a2, b1, b2, c1, c2;

    // Convert vector 1 to a line (line 1) of infinite length.
    // We want the line in linear equation standard form: A*x + B*y + C = 0
    // See: http://en.wikipedia.org/wiki/Linear_equation
    a1 = v1y2 - v1y1;
    b1 = v1x1 - v1x2;
    c1 = (v1x2 * v1y1) - (v1x1 * v1y2);

    // Every point (x,y), that solves the equation above, is on the line,
    // every point that does not solve it, is not. The equation will have a
    // positive result if it is on one side of the line and a negative one 
    // if is on the other side of it. We insert (x1,y1) and (x2,y2) of vector
    // 2 into the equation above.
    d1 = (a1 * v2x1) + (b1 * v2y1) + c1;
    d2 = (a1 * v2x2) + (b1 * v2y2) + c1;

    // If d1 and d2 both have the same sign, they are both on the same side
    // of our line 1 and in that case no intersection is possible. Careful, 
    // 0 is a special case, that's why we don't test ">=" and "<=", 
    // but "<" and ">".
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // The fact that vector 2 intersected the infinite line 1 above doesn't 
    // mean it also intersects the vector 1. Vector 1 is only a subset of that
    // infinite line 1, so it may have intersected that line before the vector
    // started or after it ended. To know for sure, we have to repeat the
    // the same test the other way round. We start by calculating the 
    // infinite line 2 in linear equation standard form.
    a2 = v2y2 - v2y1;
    b2 = v2x1 - v2x2;
    c2 = (v2x2 * v2y1) - (v2x1 * v2y2);

    // Calculate d1 and d2 again, this time using points of vector 1.
    d1 = (a2 * v1x1) + (b2 * v1y1) + c2;
    d2 = (a2 * v1x2) + (b2 * v1y2) + c2;

    // Again, if both have the same sign (and neither one is 0),
    // no intersection is possible.
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // If we get here, only two possibilities are left. Either the two
    // vectors intersect in exactly one point or they are collinear, which
    // means they intersect in any number of points from zero to infinite.
    if ((a1 * b2) - (a2 * b1) == 0.0f) return COLLINEAR;

    // If they are not collinear, they must intersect in exactly one point.
    return YES;
}

ค่าอินพุตคือจุดปลายสองจุดของเวกเตอร์ 1 ( v1x1/v1y1และv1x2/v1y2) และเวกเตอร์ 2 ( v2x1/v2y1และv2x2/v2y2) คุณมีเวกเตอร์ 2 ตัว, 4 คะแนน, 8 พิกัด YESและNOมีความชัดเจน YESเพิ่มทางแยกNOไม่ทำอะไรเลย

แล้ว COLLINEAR ล่ะ? มันหมายถึงเวกเตอร์ทั้งสองอยู่ในแนวอนันต์เดียวกันทั้งนี้ขึ้นอยู่กับตำแหน่งและความยาวพวกเขาไม่ได้ตัดกันเลยหรือพวกเขาตัดกันในจำนวนไม่รู้จบ ฉันไม่แน่ใจว่าจะจัดการกับกรณีนี้ได้อย่างไรฉันจะไม่นับเป็นจุดตัดทั้งสองทาง กรณีนี้ค่อนข้างหายากในทางปฏิบัติแล้วเนื่องจากข้อผิดพลาดในการปัดเศษทศนิยม โค้ดที่ดีกว่าอาจจะไม่ทดสอบ== 0.0fแต่แทนที่จะเป็นอย่าง< epsilonepsilon ซึ่งมีจำนวนน้อย

หากคุณต้องการทดสอบจำนวนจุดที่มากขึ้นคุณสามารถเร่งความเร็วของสิ่งต่าง ๆ ได้อย่างแน่นอนโดยการรักษารูปแบบมาตรฐานสมการเชิงเส้นของด้านรูปหลายเหลี่ยมในหน่วยความจำดังนั้นคุณไม่จำเป็นต้องคำนวณใหม่ทุกครั้ง การทำเช่นนี้จะช่วยให้คุณได้การคูณทศนิยมแบบสองจุดและการลบจุดลอยสามจุดในการทดสอบทุกครั้งเพื่อแลกกับการเก็บค่าจุดลอยตัวสามค่าต่อด้านรูปหลายเหลี่ยมในหน่วยความจำ มันเป็นช่วงเวลาปกติของการคำนวณเทียบกับหน่วยความจำ

สุดท้าย แต่ไม่ท้ายสุด: หากคุณอาจใช้ฮาร์ดแวร์ 3D เพื่อแก้ไขปัญหามีทางเลือกที่น่าสนใจ ปล่อยให้ GPU ทำทุกอย่างให้คุณ สร้างพื้นผิวการทาสีที่อยู่นอกหน้าจอ เติมให้สมบูรณ์ด้วยสีดำ ตอนนี้ให้ OpenGL หรือ Direct3D วาดรูปหลายเหลี่ยมของคุณ (หรือแม้กระทั่งรูปหลายเหลี่ยมทั้งหมดของคุณหากคุณต้องการทดสอบว่าจุดนั้นอยู่ในรูปใดรูปหนึ่งหรือไม่ แต่คุณไม่สนใจว่ารูปใดมีรูปหลายเหลี่ยมที่แตกต่างกัน สีเช่นสีขาว ในการตรวจสอบว่าจุดหนึ่งอยู่ในรูปหลายเหลี่ยมหรือไม่ให้รับสีของจุดนี้จากพื้นผิวการวาด นี่เป็นเพียงการดึงหน่วยความจำ O (1)

แน่นอนว่าวิธีนี้ใช้ได้เฉพาะเมื่อพื้นผิวการวาดของคุณไม่จำเป็นต้องมีขนาดใหญ่มาก หากไม่สามารถใส่ลงในหน่วยความจำ GPU ได้วิธีนี้จะช้ากว่าการทำบน CPU ถ้ามันจะต้องมีขนาดใหญ่และ GPU ของคุณรองรับเฉดสีที่ทันสมัยคุณยังคงสามารถใช้ GPU ได้โดยการใช้การหล่อเรย์ที่แสดงด้านบนเป็นตัวแบ่ง GPU ซึ่งเป็นไปได้อย่างแน่นอน สำหรับรูปหลายเหลี่ยมที่มีขนาดใหญ่กว่าหรือมีจำนวนมากในการทดสอบสิ่งนี้จะจ่ายออกไปพิจารณาว่า GPU บางรุ่นจะสามารถทดสอบ 64 ถึง 256 จุดในแบบคู่ขนาน โปรดทราบว่าการถ่ายโอนข้อมูลจาก CPU ไปยัง GPU และด้านหลังนั้นมีราคาแพงเสมอดังนั้นสำหรับการทดสอบจุดสองสามจุดกับรูปหลายเหลี่ยมแบบง่าย ๆ ซึ่งจุดหรือรูปหลายเหลี่ยมนั้นเป็นแบบไดนามิกและจะเปลี่ยนบ่อยวิธี GPU จะไม่จ่ายเงิน ปิด


26
+1 คำตอบที่ยอดเยี่ยม ในการใช้ฮาร์ดแวร์ในการทำฉันได้อ่านในที่อื่น ๆ ซึ่งอาจช้าเพราะคุณต้องรับข้อมูลกลับจากการ์ดกราฟิก แต่ฉันชอบหลักการของการถอด CPU มาก ใครบ้างมีการอ้างอิงที่ดีสำหรับวิธีการนี้อาจทำใน OpenGL?
Gavin

3
+1 เพราะมันง่ายมาก! ปัญหาหลักคือถ้ารูปหลายเหลี่ยมและจุดทดสอบของคุณเรียงกันบนตาราง (ไม่ใช่เรื่องแปลก) จากนั้นคุณต้องจัดการกับจุดตัด "ซ้ำซ้อน" เช่นผ่านจุดรูปหลายเหลี่ยม! (ให้ผลเท่ากับสองแทนหนึ่ง) เข้าสู่พื้นที่ประหลาดนี้: stackoverflow.com/questions/2255842/… . คอมพิวเตอร์กราฟิกส์เต็มไปด้วยกรณีพิเศษเหล่านี้ง่าย ๆ ในทางทฤษฎีมีขนดกในทางปฏิบัติ
Jared Updike

7
@Rrisrisey: ทำไมคุณคิดเช่นนั้น? ฉันล้มเหลวที่จะดูว่ามันจะล้มเหลวสำหรับรูปหลายเหลี่ยมเว้า เรย์อาจออกและใส่รูปหลายเหลี่ยมซ้ำหลาย ๆ ครั้งเมื่อรูปหลายเหลี่ยมนั้นเป็นรูปเว้า แต่ในท้ายที่สุดตัวนับที่เข้าชมจะเป็นเลขคี่ถ้าจุดนั้นอยู่ข้างในและแม้ว่ามันจะอยู่ข้างนอกก็ตาม
Mecki

6
'อัลกอริธึมจำนวนคดเคี้ยวเร็ว' ซึ่งอธิบายไว้ที่softsurfer.com/Archive/algorithm_0103/algorithm_0103.htmทำงานได้อย่างรวดเร็ว ...
SP

10
การใช้ / เพื่อแยกพิกัด x และ y นั้นทำให้เกิดความสับสนมันจะอ่านว่า x หารด้วย y มันชัดเจนกว่าการใช้ x, y (เช่น x comma y) โดยรวมแล้วคำตอบที่มีประโยชน์
Ash

583

ฉันคิดว่ารหัสต่อไปนี้เป็นทางออกที่ดีที่สุด (นำมาจากที่นี่ ):

int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
     (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

ข้อโต้แย้ง

  • nvert : จำนวนจุดยอดในรูปหลายเหลี่ยม ว่าจะทำซ้ำจุดสุดยอดครั้งแรกในตอนท้ายได้รับการกล่าวถึงในบทความที่อ้างถึงข้างต้น
  • vertx, verty : อาร์เรย์ที่มีพิกัด x และ y- ของจุดยอดของรูปหลายเหลี่ยม
  • testx, testy : X- และพิกัด y ของจุดทดสอบ

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

แนวคิดเบื้องหลังเรื่องนี้ค่อนข้างเรียบง่าย ผู้เขียนอธิบายมันดังต่อไปนี้:

ฉันใช้รังสีกึ่งไม่มีที่สิ้นสุดในแนวนอน (เพิ่ม x, คงที่ y) ออกจากจุดทดสอบและนับว่ามันข้ามขอบจำนวนเท่าใด ในการข้ามแต่ละครั้งรังสีจะสลับระหว่างภายในและภายนอก นี่เรียกว่าทฤษฎีโค้งของจอร์แดน

ตัวแปร c เปลี่ยนจาก 0 เป็น 1 และ 1 เป็น 0 ทุกครั้งที่รังสีแนวนอนตัดผ่านขอบใด ๆ โดยพื้นฐานแล้วมันจะคอยติดตามว่าจำนวนของขอบไขว้นั้นเท่ากันหรือแปลก 0 หมายถึงเลขคู่และ 1 หมายถึงเลขคี่


5
คำถาม. อะไรคือตัวแปรที่ฉันผ่านมันไป? พวกเขาเป็นตัวแทนอะไร
tekknolagi

9
@ มิกมันตรวจสอบว่าverty[i]และverty[j]เป็นทั้งสองด้านtestyดังนั้นพวกเขาจะไม่เท่ากัน
Peter Wood

4
รหัสนี้ไม่แข็งแรงและฉันไม่แนะนำให้ใช้ นี่คือลิงค์ที่ให้รายละเอียดการวิเคราะห์: www-ma2.upc.es/geoc/Schirra-pointPolygon.pdf
Mikola

13
วิธีการนี้จริง ๆ แล้วมีข้อ จำกัด (กรณีขอบ): การตรวจสอบจุด (15,20) ในรูปหลายเหลี่ยม [(10,10), (10,20), (20,20), (20,10)] จะกลับมา false แทนที่จะเป็น true เหมือนกันกับ (10,20) หรือ (20,15) สำหรับกรณีอื่น ๆ อัลกอริทึมทำงานได้อย่างสมบูรณ์แบบและกรณีที่เป็นค่าลบในขอบกรณีนั้นใช้ได้สำหรับแอปพลิเคชันของฉัน
Alexander Pacha

10
@Alexander นี่คือความจริงโดยการออกแบบ: โดยการจัดการขอบเขตซ้ายและล่างในความหมายตรงกันข้ามกับขอบเขตบนและขวาหากรูปหลายเหลี่ยมสองอันที่แตกต่างกันแบ่งปันขอบใด ๆ ตามขอบนี้จะอยู่ในรูปหลายเหลี่ยมหนึ่งเดียว ..a คุณสมบัติที่มีประโยชน์
wardw

69

นี่คือคำตอบของ nirgรุ่น C # ซึ่งมาจากศาสตราจารย์ RPIนี้ โปรดทราบว่าการใช้รหัสจากแหล่ง RPI นั้นต้องการการระบุแหล่งที่มา

ตรวจสอบกล่องขอบเขตได้รับการเพิ่มที่ด้านบน อย่างไรก็ตามเจมส์บราวน์ชี้ให้เห็นว่ารหัสหลักเกือบจะเร็วเท่ากับกล่องตรวจสอบตัวเองดังนั้นการตรวจสอบกล่องขอบเขตสามารถชะลอการดำเนินงานโดยรวมได้ในกรณีที่จุดตรวจสอบส่วนใหญ่อยู่ในกล่องเชื่อมโยง . ดังนั้นคุณสามารถออกจากกล่องของขอบเขตหรือตรวจสอบทางเลือกที่จะ precompute กล่องขอบเขตของรูปหลายเหลี่ยมของคุณถ้าพวกเขาไม่เปลี่ยนรูปร่างบ่อยเกินไป

public bool IsPointInPolygon( Point p, Point[] polygon )
{
    double minX = polygon[ 0 ].X;
    double maxX = polygon[ 0 ].X;
    double minY = polygon[ 0 ].Y;
    double maxY = polygon[ 0 ].Y;
    for ( int i = 1 ; i < polygon.Length ; i++ )
    {
        Point q = polygon[ i ];
        minX = Math.Min( q.X, minX );
        maxX = Math.Max( q.X, maxX );
        minY = Math.Min( q.Y, minY );
        maxY = Math.Max( q.Y, maxY );
    }

    if ( p.X < minX || p.X > maxX || p.Y < minY || p.Y > maxY )
    {
        return false;
    }

    // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
    bool inside = false;
    for ( int i = 0, j = polygon.Length - 1 ; i < polygon.Length ; j = i++ )
    {
        if ( ( polygon[ i ].Y > p.Y ) != ( polygon[ j ].Y > p.Y ) &&
             p.X < ( polygon[ j ].X - polygon[ i ].X ) * ( p.Y - polygon[ i ].Y ) / ( polygon[ j ].Y - polygon[ i ].Y ) + polygon[ i ].X )
        {
            inside = !inside;
        }
    }

    return inside;
}

5
ใช้งานได้ดีมากขอบคุณฉันเปลี่ยนเป็น JavaScript stackoverflow.com/questions/217578/…
Philipp Lenssen

2
นี่คือ> เร็วกว่า 1000 เท่าเมื่อใช้ GraphicsPath.IsVisible !! การทำเครื่องหมายในช่องทำเครื่องหมายจะทำให้การทำงานช้าลงประมาณ 70%
James Brown

ไม่เพียง GraphicsPath.IsVisible () ช้าลง แต่ยังใช้งานไม่ได้กับรูปหลายเหลี่ยมขนาดเล็กที่มีด้านในช่วง 0.01f
Patrick จากทีม NDepend

50

นี่คือตัวแปร JavaScript ของคำตอบโดย M. Katz ตามแนวทางของ Nirg:

function pointIsInPoly(p, polygon) {
    var isInside = false;
    var minX = polygon[0].x, maxX = polygon[0].x;
    var minY = polygon[0].y, maxY = polygon[0].y;
    for (var n = 1; n < polygon.length; n++) {
        var q = polygon[n];
        minX = Math.min(q.x, minX);
        maxX = Math.max(q.x, maxX);
        minY = Math.min(q.y, minY);
        maxY = Math.max(q.y, maxY);
    }

    if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
        return false;
    }

    var i = 0, j = polygon.length - 1;
    for (i, j; i < polygon.length; j = i++) {
        if ( (polygon[i].y > p.y) != (polygon[j].y > p.y) &&
                p.x < (polygon[j].x - polygon[i].x) * (p.y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x ) {
            isInside = !isInside;
        }
    }

    return isInside;
}

32

คำนวณผลรวมเชิงของมุมระหว่างจุด p และแต่ละจุดของรูปหลายเหลี่ยม หากมุมที่มุ่งเน้นทั้งหมดคือ 360 องศาจุดนั้นจะอยู่ภายใน หากผลรวมเป็น 0 จุดนั้นจะอยู่ด้านนอก

ฉันชอบวิธีนี้ดีกว่าเพราะมันมีประสิทธิภาพมากกว่าและขึ้นอยู่กับความแม่นยำของตัวเลข

วิธีที่คำนวณความสมดุลของจำนวนจุดตัดมี จำกัด เนื่องจากคุณสามารถ 'ยอด' จุดสุดยอดในระหว่างการคำนวณจำนวนจุดตัด

แก้ไข: โดยวิธีการวิธีนี้ทำงานร่วมกับเว้าและนูนรูปหลายเหลี่ยม

แก้ไข: ฉันเพิ่งพบบทความ Wikipediaทั้งหมดในหัวข้อ


1
ไม่นี่ไม่เป็นความจริง วิธีนี้ใช้ได้ผลโดยไม่คำนึงถึงความนูนของรูปหลายเหลี่ยม
David Segonds

2
@DarenW: เพียงหนึ่ง acos ต่อจุดสุดยอด; ในทางตรงกันข้ามอัลกอริทึมนี้ควรจะง่ายที่สุดที่จะใช้ใน SIMD เพราะมันไม่มีการแตกแขนง
Jasper Bekkers

1
@emilio ถ้าจุดนั้นอยู่ห่างจากสามเหลี่ยมมากนักฉันไม่เห็นว่ามุมที่เกิดขึ้นจากจุดนั้นและสองส่วนของสามเหลี่ยมจะเป็น 90 องศาได้อย่างไร
David Segonds

2
ใช้การตรวจสอบกล่องที่มีขอบเขตเพื่อแก้ไขกรณี "จุดไกล" สำหรับตรีโกณมิติคุณสามารถใช้ตารางที่สร้างไว้ล่วงหน้าได้
JOM

3
นี่เป็นทางออกที่ดีที่สุดเนื่องจากเป็น O (n) และต้องการการคำนวณน้อยที่สุด ใช้งานได้กับรูปหลายเหลี่ยมทั้งหมด ฉันค้นคว้าโซลูชันนี้เมื่อ 30 ปีก่อนที่งาน IBM ครั้งแรกของฉัน พวกเขาลงนามในมันและยังคงใช้มันในวันนี้ในเทคโนโลยี GIS ของพวกเขา
Dominic Cerisano

24

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

ให้ x เป็นจุดเป้าหมาย ให้อาร์เรย์ [0, 1, .... n] เป็นจุดทั้งหมดของพื้นที่ เชื่อมต่อจุดเป้าหมายกับทุกจุดเส้นขอบด้วยเส้น หากจุดเป้าหมายอยู่ภายในพื้นที่นี้ ผลรวมของทุกมุมจะเป็น 360 องศา ถ้าไม่ใช่มุมจะน้อยกว่า 360

อ้างอิงภาพนี้เพื่อทำความเข้าใจแนวคิดพื้นฐาน: ป้อนคำอธิบายรูปภาพที่นี่

อัลกอริทึมของฉันถือว่าทวนเข็มนาฬิกาเป็นทิศทางบวก นี่คืออินพุตที่มีศักยภาพ:

[[-122.402015, 48.225216], [-117.032049, 48.999931], [-116.919132, 45.995175], [-124.079107, 46.267259], [-124.717175, 48.377557], [-122.92315, 47.047963], [-122.402015, 48.225216]]

ต่อไปนี้เป็นรหัสไพ ธ อนที่ใช้แนวคิด:

def isInside(self, border, target):
degree = 0
for i in range(len(border) - 1):
    a = border[i]
    b = border[i + 1]

    # calculate distance of vector
    A = getDistance(a[0], a[1], b[0], b[1]);
    B = getDistance(target[0], target[1], a[0], a[1])
    C = getDistance(target[0], target[1], b[0], b[1])

    # calculate direction of vector
    ta_x = a[0] - target[0]
    ta_y = a[1] - target[1]
    tb_x = b[0] - target[0]
    tb_y = b[1] - target[1]

    cross = tb_y * ta_x - tb_x * ta_y
    clockwise = cross < 0

    # calculate sum of angles
    if(clockwise):
        degree = degree + math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))
    else:
        degree = degree - math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))

if(abs(round(degree) - 360) <= 3):
    return True
return False

21

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

อย่างไรก็ตามมันเป็นวันแรก แต่การลงคะแนนของฉันไปที่วิธี "ข้าม" ซึ่งเป็นสิ่งที่สวยมาก Mecki อธิบายฉันคิดว่า อย่างไรก็ตามฉันพบว่ามันอธิบายและประมวลผลโดย David Bourkeได้สำเร็จที่สุด ฉันชอบที่ไม่จำเป็นต้องใช้ตรีโกณมิติจริง ๆ และใช้งานได้กับนูนและเว้าและทำงานได้ดีพอสมควรเมื่อจำนวนด้านเพิ่มขึ้น

อย่างไรก็ตามนี่เป็นหนึ่งในตารางการปฏิบัติงานจากบทความของ Eric Haines ที่สนใจทำการทดสอบรูปหลายเหลี่ยมแบบสุ่ม

                       number of edges per polygon
                         3       4      10      100    1000
MacMartin               2.9     3.2     5.9     50.6    485
Crossings               3.1     3.4     6.8     60.0    624
Triangle Fan+edge sort  1.1     1.8     6.5     77.6    787
Triangle Fan            1.2     2.1     7.3     85.4    865
Barycentric             2.1     3.8    13.8    160.7   1665
Angle Summation        56.2    70.4   153.6   1403.8  14693

Grid (100x100)          1.5     1.5     1.6      2.1      9.8
Grid (20x20)            1.7     1.7     1.9      5.7     42.2
Bins (100)              1.8     1.9     2.7     15.1    117
Bins (20)               2.1     2.2     3.7     26.3    278

11

คำตอบเวอร์ชั่นที่รวดเร็วโดย nirg :

extension CGPoint {
    func isInsidePolygon(vertices: [CGPoint]) -> Bool {
        guard !vertices.isEmpty else { return false }
        var j = vertices.last!, c = false
        for i in vertices {
            let a = (i.y > y) != (j.y > y)
            let b = (x < (j.x - i.x) * (y - i.y) / (j.y - i.y) + i.x)
            if a && b { c = !c }
            j = i
        }
        return c
    }
}

สิ่งนี้มีการหารที่เป็นไปได้โดยไม่มีปัญหาในการคำนวณ b ต้องคำนวณเฉพาะ "b" และบรรทัดถัดไปด้วย "c" หากการคำนวณสำหรับ "a" แสดงว่ามีความเป็นไปได้ที่จะตัดกัน ไม่มีความเป็นไปได้หากคะแนนทั้งสองอยู่เหนือหรือทั้งสองจุดด้านล่าง - ซึ่งอธิบายโดยการคำนวณสำหรับ "a"
anorskdev

11

ชอบวิธีแก้ปัญหาที่โพสต์โดย Nirg และแก้ไขโดย bobobobo ฉันเพิ่งทำให้เป็นจาวาสคริปต์ที่เป็นมิตรและอ่านง่ายขึ้นเล็กน้อยสำหรับการใช้งานของฉัน:

function insidePoly(poly, pointx, pointy) {
    var i, j;
    var inside = false;
    for (i = 0, j = poly.length - 1; i < poly.length; j = i++) {
        if(((poly[i].y > pointy) != (poly[j].y > pointy)) && (pointx < (poly[j].x-poly[i].x) * (pointy-poly[i].y) / (poly[j].y-poly[i].y) + poly[i].x) ) inside = !inside;
    }
    return inside;
}

8

ฉันทำงานด้านนี้เมื่อฉันเป็นนักวิจัยภายใต้Michael Stonebraker - คุณก็รู้ศาสตราจารย์ผู้คิดค้น Ingres , PostgreSQLเป็นต้น

เราตระหนักว่าวิธีที่เร็วที่สุดคือการทำกล่องผูกไว้ก่อนเพราะมันเร็วมาก หากอยู่นอกกรอบก็จะอยู่นอก ไม่งั้นคุณจะทำงานให้หนักขึ้น ...

หากคุณต้องการอัลกอริธึมที่ยอดเยี่ยมให้ดูโครงการโอเพ่นซอร์สของ PostgreSQL สำหรับงานทางภูมิศาสตร์ ...

ฉันต้องการชี้ให้เห็นว่าเราไม่เคยได้รับข้อมูลเชิงลึกเกี่ยวกับความถนัดซ้ายและซ้ายมือ (ยังแสดงให้เห็นว่าเป็นปัญหา "ภายใน" vs "นอก" ...


UPDATE

ลิงก์ของ BKB ให้อัลกอริธึมที่เหมาะสมจำนวนมาก ฉันทำงานเกี่ยวกับปัญหา Earth Science และดังนั้นจึงจำเป็นต้องมีวิธีแก้ปัญหาที่ทำงานในละติจูด / ลองจิจูดและมันมีปัญหาที่แปลกประหลาดของการถนัดซ้าย - เป็นพื้นที่ภายในพื้นที่ขนาดเล็กหรือพื้นที่ขนาดใหญ่กว่าหรือไม่? คำตอบก็คือ "ทิศทาง" ของจุดยอดมีความสำคัญ - เป็นทางซ้ายหรือทางขวาและด้วยวิธีนี้คุณสามารถระบุพื้นที่ว่า "ภายใน" รูปหลายเหลี่ยมที่กำหนด ดังนั้นงานของฉันจึงใช้วิธีแก้ปัญหาสามข้อที่แจกแจงในหน้านั้น

นอกจากนี้งานของฉันยังใช้ฟังก์ชันแยกต่างหากสำหรับการทดสอบ "ออนไลน์"

... เนื่องจากมีคนถามว่า: เราคิดว่าการทดสอบกล่อง bounding นั้นดีที่สุดเมื่อจำนวนจุดยอดเกินกว่าจำนวน - ทำแบบทดสอบอย่างรวดเร็วมากก่อนที่จะทำการทดสอบอีกต่อไปหากจำเป็น ... กล่องขอบเขตถูกสร้างขึ้นโดยเพียงแค่การ ที่ใหญ่ที่สุด x ที่เล็กที่สุด x ที่เล็กที่สุด y และ y ที่เล็กที่สุดและรวมเข้าด้วยกันเพื่อทำสี่จุดของกล่อง ...

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


ถ้าฉันไม่ได้มีกล่องกระโดด :)
Scott Evernden

8
คุณสามารถสร้างขอบเขตได้อย่างง่ายดาย - เป็นเพียงสี่จุดที่ใช้เครื่องหมาย x ที่ยิ่งใหญ่ที่สุดและน้อยที่สุดและยิ่งใหญ่ที่สุดและน้อยที่สุด ขึ้นเร็ว ๆ นี้.
Richard T

"... หลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้นเมื่อพันเส้นลองจิจูด 180 เส้นหนึ่งและเมื่อจัดการพื้นที่ขั้วโลก" คุณสามารถอธิบายรายละเอียดเพิ่มเติมนี้ได้ไหม ฉันคิดว่าฉันสามารถหาวิธีที่จะย้ายทุกอย่าง 'ขึ้น' เพื่อหลีกเลี่ยงรูปหลายเหลี่ยมของฉันข้าม 0 ลองจิจูด แต่ฉันไม่ชัดเจนเกี่ยวกับวิธีการจัดการรูปหลายเหลี่ยมที่มีทั้งสองเสา ...
239191

6

คำตอบของ David Segond นั้นเป็นคำตอบทั่วไปที่ค่อนข้างธรรมดาและ Richard T's นั้นเป็นการเพิ่มประสิทธิภาพที่พบได้บ่อยที่สุด การเพิ่มประสิทธิภาพที่แข็งแกร่งอื่น ๆ ขึ้นอยู่กับการแก้ปัญหาทั่วไปน้อยลง ตัวอย่างเช่นหากคุณกำลังตรวจสอบรูปหลายเหลี่ยมเดียวกันกับจำนวนมากการหารูปหลายเหลี่ยมนั้นสามารถเร่งความเร็วของสิ่งต่างๆได้อย่างมหาศาลเนื่องจากมีอัลกอริธึมการค้นหา TIN จำนวนมากที่รวดเร็ว อีกอย่างคือถ้ารูปหลายเหลี่ยมและจุดอยู่บนระนาบ จำกัด ที่ความละเอียดต่ำพูดถึงหน้าจอคุณสามารถวาดรูปหลายเหลี่ยมบนบัฟเฟอร์หน่วยความจำที่แมปหน่วยความจำในสีที่กำหนดและตรวจสอบสีของพิกเซลที่กำหนดเพื่อดูว่ามันอยู่ ในรูปหลายเหลี่ยม

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

การทำงานในสาขานี้ฉันพบว่าเรขาคณิตการคำนวณ Joeseph O'Rourkes ใน C 'ไอ 0-521-44034-3 เป็นความช่วยเหลือที่ดีมาก


4

วิธีแก้ปัญหาเล็ก ๆ น้อย ๆ คือการแบ่งรูปหลายเหลี่ยมเป็นรูปสามเหลี่ยมและทดสอบรูปสามเหลี่ยมดังที่อธิบายไว้ที่นี่

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


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

1
มีการอ้างอิงใด ๆ เกี่ยวกับ curvigons เหล่านี้หรือไม่
shoosh

ฉันจะรักการอ้างอิงสำหรับ curvigons ด้วย
Joel ในGö

คุณพลาดวิธีแก้ปัญหาที่สำคัญสำหรับกรณีของรูปหลายเหลี่ยมนูน: โดยการเปรียบเทียบจุดกับเส้นทแยงมุมคุณสามารถลดจำนวนจุดยอดได้ครึ่งหนึ่ง และทำซ้ำขั้นตอนนี้คุณจะลดเป็นสามเหลี่ยมเดียวในการดำเนินการ Log (N) มากกว่า N
Yves Daoust

4

ฉันรู้ว่านี่เก่า แต่นี่เป็นอัลกอริทึมการฉายรังสีที่ใช้ในโกโก้ในกรณีที่ใครสนใจ ไม่แน่ใจว่ามันเป็นวิธีที่มีประสิทธิภาพที่สุดในการทำสิ่งต่าง ๆ แต่มันอาจช่วยใครซักคน

- (BOOL)shape:(NSBezierPath *)path containsPoint:(NSPoint)point
{
    NSBezierPath *currentPath = [path bezierPathByFlatteningPath];
    BOOL result;
    float aggregateX = 0; //I use these to calculate the centroid of the shape
    float aggregateY = 0;
    NSPoint firstPoint[1];
    [currentPath elementAtIndex:0 associatedPoints:firstPoint];
    float olderX = firstPoint[0].x;
    float olderY = firstPoint[0].y;
    NSPoint interPoint;
    int noOfIntersections = 0;

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];
        [currentPath elementAtIndex:n associatedPoints:points];
        aggregateX += points[0].x;
        aggregateY += points[0].y;
    }

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];

        [currentPath elementAtIndex:n associatedPoints:points];
        //line equations in Ax + By = C form
        float _A_FOO = (aggregateY/[currentPath elementCount]) - point.y;  
        float _B_FOO = point.x - (aggregateX/[currentPath elementCount]);
        float _C_FOO = (_A_FOO * point.x) + (_B_FOO * point.y);

        float _A_BAR = olderY - points[0].y;
        float _B_BAR = points[0].x - olderX;
        float _C_BAR = (_A_BAR * olderX) + (_B_BAR * olderY);

        float det = (_A_FOO * _B_BAR) - (_A_BAR * _B_FOO);
        if (det != 0) {
            //intersection points with the edges
            float xIntersectionPoint = ((_B_BAR * _C_FOO) - (_B_FOO * _C_BAR)) / det;
            float yIntersectionPoint = ((_A_FOO * _C_BAR) - (_A_BAR * _C_FOO)) / det;
            interPoint = NSMakePoint(xIntersectionPoint, yIntersectionPoint);
            if (olderX <= points[0].x) {
                //doesn't matter in which direction the ray goes, so I send it right-ward.
                if ((interPoint.x >= olderX && interPoint.x <= points[0].x) && (interPoint.x > point.x)) {  
                    noOfIntersections++;
                }
            } else {
                if ((interPoint.x >= points[0].x && interPoint.x <= olderX) && (interPoint.x > point.x)) {
                     noOfIntersections++;
                } 
            }
        }
        olderX = points[0].x;
        olderY = points[0].y;
    }
    if (noOfIntersections % 2 == 0) {
        result = FALSE;
    } else {
        result = TRUE;
    }
    return result;
}

5
โปรดทราบว่าหากคุณกำลังทำจริงใน Cocoa คุณสามารถใช้วิธี [NSBezierPath containPoint:]
ThomasW

4

คำตอบของ nirg รุ่น Obj-C พร้อมวิธีตัวอย่างสำหรับคะแนนการทดสอบ คำตอบของ Nirg ทำงานได้ดีสำหรับฉัน

- (BOOL)isPointInPolygon:(NSArray *)vertices point:(CGPoint)test {
    NSUInteger nvert = [vertices count];
    NSInteger i, j, c = 0;
    CGPoint verti, vertj;

    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        verti = [(NSValue *)[vertices objectAtIndex:i] CGPointValue];
        vertj = [(NSValue *)[vertices objectAtIndex:j] CGPointValue];
        if (( (verti.y > test.y) != (vertj.y > test.y) ) &&
        ( test.x < ( vertj.x - verti.x ) * ( test.y - verti.y ) / ( vertj.y - verti.y ) + verti.x) )
            c = !c;
    }

    return (c ? YES : NO);
}

- (void)testPoint {

    NSArray *polygonVertices = [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:CGPointMake(13.5, 41.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 56.5)],
        [NSValue valueWithCGPoint:CGPointMake(39.5, 69.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 84.5)],
        [NSValue valueWithCGPoint:CGPointMake(13.5, 100.0)],
        [NSValue valueWithCGPoint:CGPointMake(6.0, 70.5)],
        nil
    ];

    CGPoint tappedPoint = CGPointMake(23.0, 70.0);

    if ([self isPointInPolygon:polygonVertices point:tappedPoint]) {
        NSLog(@"YES");
    } else {
        NSLog(@"NO");
    }
}

รูปหลายเหลี่ยมตัวอย่าง


2
แน่นอนใน Objective-C CGPathContainsPoint()คือเพื่อนของคุณ
Zev Eisenberg

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

4

ไม่มีอะไรจะยิ่งใหญ่ไปกว่าการนิยามอุปนัยของปัญหา เพื่อความสมบูรณ์ที่นี่คุณมีเวอร์ชันในภาษาอารัมภบทซึ่งอาจอธิบายความคิดเบื้องหลังการฉายรังสี :

อ้างอิงจากการจำลองอัลกอริธึมความเรียบง่ายในhttp://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

ผู้ช่วยบางภาคแสดง:

exor(A,B):- \+A,B;A,\+B.
in_range(Coordinate,CA,CB) :- exor((CA>Coordinate),(CB>Coordinate)).

inside(false).
inside(_,[_|[]]).
inside(X:Y, [X1:Y1,X2:Y2|R]) :- in_range(Y,Y1,Y2), X > ( ((X2-X1)*(Y-Y1))/(Y2-Y1) +      X1),toggle_ray, inside(X:Y, [X2:Y2|R]); inside(X:Y, [X2:Y2|R]).

get_line(_,_,[]).
get_line([XA:YA,XB:YB],[X1:Y1,X2:Y2|R]):- [XA:YA,XB:YB]=[X1:Y1,X2:Y2]; get_line([XA:YA,XB:YB],[X2:Y2|R]).

สมการของเส้นที่กำหนด 2 จุด A และ B (เส้น (A, B)) คือ:

                    (YB-YA)
           Y - YA = ------- * (X - XA) 
                    (XB-YB) 

มันเป็นสิ่งสำคัญที่ทิศทางการหมุนของสายจะถูกกำหนดเป็นนาฬิกาที่มีขอบเขตและการป้องกันนาฬิกาที่มีรู เราจะตรวจสอบว่าจุด (X, Y) คือจุดทดสอบอยู่ที่ครึ่งระนาบด้านซ้ายของเส้น (มันเป็นเรื่องของรสนิยมมันอาจเป็นด้านขวา แต่ก็เป็นทิศทางของขอบเขตด้วย เส้นจะต้องมีการเปลี่ยนแปลงในกรณีนี้) นี่คือการฉายรังสีจากจุดไปทางขวา (หรือซ้าย) และรับทราบจุดตัดกับเส้น เราเลือกที่จะฉายรังสีในแนวนอน (อีกครั้งมันเป็นเรื่องของรสนิยมมันก็สามารถทำได้ในแนวตั้งด้วยข้อ จำกัด ที่คล้ายกัน) ดังนั้นเราจึงมี:

               (XB-XA)
           X < ------- * (Y - YA) + XA
               (YB-YA) 

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

is_left_half_plane(_,[],[],_).
is_left_half_plane(X:Y,[XA:YA,XB:YB], [[X1:Y1,X2:Y2]|R], Test) :- [XA:YA, XB:YB] =  [X1:Y1, X2:Y2], call(Test, X , (((XB - XA) * (Y - YA)) / (YB - YA) + XA)); 
                                                        is_left_half_plane(X:Y, [XA:YA, XB:YB], R, Test).

in_y_range_at_poly(Y,[XA:YA,XB:YB],Polygon) :- get_line([XA:YA,XB:YB],Polygon),  in_range(Y,YA,YB).
all_in_range(Coordinate,Polygon,Lines) :- aggregate(bag(Line),    in_y_range_at_poly(Coordinate,Line,Polygon), Lines).

traverses_ray(X:Y, Lines, Count) :- aggregate(bag(Line), is_left_half_plane(X:Y, Line, Lines, <), IntersectingLines), length(IntersectingLines, Count).

% This is the entry point predicate
inside_poly(X:Y,Polygon,Answer) :- all_in_range(Y,Polygon,Lines), traverses_ray(X:Y, Lines, Count), (1 is mod(Count,2)->Answer=inside;Answer=outside).

3

คำตอบของ nirg รุ่น C # อยู่ที่นี่: ฉันจะแบ่งปันรหัส มันอาจช่วยคนบางคนเวลา

public static bool IsPointInPolygon(IList<Point> polygon, Point testPoint) {
            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++) {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y) {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X) {
                        result = !result;
                    }
                }
                j = i;
            }
            return result;
        }

ใช้งานได้ในหลายกรณี แต่มันผิดและไม่สามารถทำงานได้อย่างถูกต้องเสมอ! ใช้วิธีแก้ปัญหาจาก M Katz ซึ่งถูกต้อง
Lukas Hanacek

3

เวอร์ชั่น Java:

public class Geocode {
    private float latitude;
    private float longitude;

    public Geocode() {
    }

    public Geocode(float latitude, float longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public float getLatitude() {
        return latitude;
    }

    public void setLatitude(float latitude) {
        this.latitude = latitude;
    }

    public float getLongitude() {
        return longitude;
    }

    public void setLongitude(float longitude) {
        this.longitude = longitude;
    }
}

public class GeoPolygon {
    private ArrayList<Geocode> points;

    public GeoPolygon() {
        this.points = new ArrayList<Geocode>();
    }

    public GeoPolygon(ArrayList<Geocode> points) {
        this.points = points;
    }

    public GeoPolygon add(Geocode geo) {
        points.add(geo);
        return this;
    }

    public boolean inside(Geocode geo) {
        int i, j;
        boolean c = false;
        for (i = 0, j = points.size() - 1; i < points.size(); j = i++) {
            if (((points.get(i).getLongitude() > geo.getLongitude()) != (points.get(j).getLongitude() > geo.getLongitude())) &&
                    (geo.getLatitude() < (points.get(j).getLatitude() - points.get(i).getLatitude()) * (geo.getLongitude() - points.get(i).getLongitude()) / (points.get(j).getLongitude() - points.get(i).getLongitude()) + points.get(i).getLatitude()))
                c = !c;
        }
        return c;
    }

}

2

. สุทธิพอร์ต:

    static void Main(string[] args)
    {

        Console.Write("Hola");
        List<double> vertx = new List<double>();
        List<double> verty = new List<double>();

        int i, j, c = 0;

        vertx.Add(1);
        vertx.Add(2);
        vertx.Add(1);
        vertx.Add(4);
        vertx.Add(4);
        vertx.Add(1);

        verty.Add(1);
        verty.Add(2);
        verty.Add(4);
        verty.Add(4);
        verty.Add(1);
        verty.Add(1);

        int nvert = 6;  //Vértices del poligono

        double testx = 2;
        double testy = 5;


        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if (((verty[i] > testy) != (verty[j] > testy)) &&
             (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                c = 1;
        }
    }

2

VBA รุ่น:

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

โมดูลระดับ: CPoint

Private pXValue As Double
Private pYValue As Double

'''''X Value Property'''''

Public Property Get X() As Double
    X = pXValue
End Property

Public Property Let X(Value As Double)
    pXValue = Value
End Property

'''''Y Value Property'''''

Public Property Get Y() As Double
    Y = pYValue
End Property

Public Property Let Y(Value As Double)
    pYValue = Value
End Property

โมดูล:

Public Function isPointInPolygon(p As CPoint, polygon() As CPoint) As Boolean

    Dim i As Integer
    Dim j As Integer
    Dim q As Object
    Dim minX As Double
    Dim maxX As Double
    Dim minY As Double
    Dim maxY As Double
    minX = polygon(0).X
    maxX = polygon(0).X
    minY = polygon(0).Y
    maxY = polygon(0).Y

    For i = 1 To UBound(polygon)
        Set q = polygon(i)
        minX = vbMin(q.X, minX)
        maxX = vbMax(q.X, maxX)
        minY = vbMin(q.Y, minY)
        maxY = vbMax(q.Y, maxY)
    Next i

    If p.X < minX Or p.X > maxX Or p.Y < minY Or p.Y > maxY Then
        isPointInPolygon = False
        Exit Function
    End If


    ' SOURCE: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

    isPointInPolygon = False
    i = 0
    j = UBound(polygon)

    Do While i < UBound(polygon) + 1
        If (polygon(i).Y > p.Y) Then
            If (polygon(j).Y < p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        ElseIf (polygon(i).Y < p.Y) Then
            If (polygon(j).Y > p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        End If
        j = i
        i = i + 1
    Loop   
End Function

Function vbMax(n1, n2) As Double
    vbMax = IIf(n1 > n2, n1, n2)
End Function

Function vbMin(n1, n2) As Double
    vbMin = IIf(n1 > n2, n2, n1)
End Function


Sub TestPointInPolygon()

    Dim i As Integer
    Dim InPolygon As Boolean

'   MARKER Object
    Dim p As CPoint
    Set p = New CPoint
    p.X = <ENTER X VALUE HERE>
    p.Y = <ENTER Y VALUE HERE>

'   POLYGON OBJECT
    Dim polygon() As CPoint
    ReDim polygon(<ENTER VALUE HERE>) 'Amount of vertices in polygon - 1
    For i = 0 To <ENTER VALUE HERE> 'Same value as above
       Set polygon(i) = New CPoint
       polygon(i).X = <ASSIGN X VALUE HERE> 'Source a list of values that can be looped through
       polgyon(i).Y = <ASSIGN Y VALUE HERE> 'Source a list of values that can be looped through
    Next i

    InPolygon = isPointInPolygon(p, polygon)
    MsgBox InPolygon

End Sub

2

ฉันใช้ Python เพื่อนำไปใช้กับรหัส c ++ ของnirg :

ปัจจัยการผลิต

  • bounding_points:โหนดที่ประกอบเป็นรูปหลายเหลี่ยม
  • bounding_box_positions:คะแนนผู้สมัครที่จะกรอง (ในการใช้งานของฉันสร้างขึ้นจากกล่องขอบเขต

    (ปัจจัยการผลิตคือรายการของ tuples ในรูปแบบ: [(xcord, ycord), ...])

ผลตอบแทน

  • จุดทั้งหมดที่อยู่ภายในรูปหลายเหลี่ยม
def polygon_ray_casting(self, bounding_points, bounding_box_positions):
    # Arrays containing the x- and y-coordinates of the polygon's vertices.
    vertx = [point[0] for point in bounding_points]
    verty = [point[1] for point in bounding_points]
    # Number of vertices in the polygon
    nvert = len(bounding_points)
    # Points that are inside
    points_inside = []

    # For every candidate position within the bounding box
    for idx, pos in enumerate(bounding_box_positions):
        testx, testy = (pos[0], pos[1])
        c = 0
        for i in range(0, nvert):
            j = i - 1 if i != 0 else nvert - 1
            if( ((verty[i] > testy ) != (verty[j] > testy))   and
                    (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]) ):
                c += 1
        # If odd, that means that we are inside the polygon
        if c % 2 == 1: 
            points_inside.append(pos)


    return points_inside

อีกครั้งความคิดที่นำมาจากที่นี่


2

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

สิ่งที่คุณกำลังมองหาคือ:

db.ne Neighborhoods.findOne ({เรขาคณิต: {$ geoIntersects: {$ เรขาคณิต: {ประเภท: "จุด", พิกัด: ["ลองจิจูด", "ละติจูด"]}}}})

Neighborhoodsคือคอลเล็กชันที่เก็บรูปหลายเหลี่ยมหนึ่งรูปแบบขึ้นไปในรูปแบบ GeoJson มาตรฐาน ถ้าแบบสอบถามส่งกลับค่า null มันจะไม่ intersected มิฉะนั้นเป็น

มีเอกสารที่ดีมากที่นี่: https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/

ประสิทธิภาพมากกว่า 6,000 คะแนนที่จัดอยู่ในตารางรูปหลายเหลี่ยมผิดปกติ 330 ตัวนั้นน้อยกว่าหนึ่งนาทีโดยไม่มีการปรับให้เหมาะสมเลยและรวมถึงเวลาในการอัปเดตเอกสารด้วยรูปหลายเหลี่ยมที่เกี่ยวข้อง


1

เป็นจุดในการทดสอบรูปหลายเหลี่ยมใน C ที่ไม่ได้ใช้การหล่อเรย์ และมันสามารถใช้งานได้สำหรับพื้นที่ที่ทับซ้อนกัน (ทางแยกด้วยตนเอง) ดูuse_holesอาร์กิวเมนต์

/* math lib (defined below) */
static float dot_v2v2(const float a[2], const float b[2]);
static float angle_signed_v2v2(const float v1[2], const float v2[2]);
static void copy_v2_v2(float r[2], const float a[2]);

/* intersection function */
bool isect_point_poly_v2(const float pt[2], const float verts[][2], const unsigned int nr,
                         const bool use_holes)
{
    /* we do the angle rule, define that all added angles should be about zero or (2 * PI) */
    float angletot = 0.0;
    float fp1[2], fp2[2];
    unsigned int i;
    const float *p1, *p2;

    p1 = verts[nr - 1];

    /* first vector */
    fp1[0] = p1[0] - pt[0];
    fp1[1] = p1[1] - pt[1];

    for (i = 0; i < nr; i++) {
        p2 = verts[i];

        /* second vector */
        fp2[0] = p2[0] - pt[0];
        fp2[1] = p2[1] - pt[1];

        /* dot and angle and cross */
        angletot += angle_signed_v2v2(fp1, fp2);

        /* circulate */
        copy_v2_v2(fp1, fp2);
        p1 = p2;
    }

    angletot = fabsf(angletot);
    if (use_holes) {
        const float nested = floorf((angletot / (float)(M_PI * 2.0)) + 0.00001f);
        angletot -= nested * (float)(M_PI * 2.0);
        return (angletot > 4.0f) != ((int)nested % 2);
    }
    else {
        return (angletot > 4.0f);
    }
}

/* math lib */

static float dot_v2v2(const float a[2], const float b[2])
{
    return a[0] * b[0] + a[1] * b[1];
}

static float angle_signed_v2v2(const float v1[2], const float v2[2])
{
    const float perp_dot = (v1[1] * v2[0]) - (v1[0] * v2[1]);
    return atan2f(perp_dot, dot_v2v2(v1, v2));
}

static void copy_v2_v2(float r[2], const float a[2])
{
    r[0] = a[0];
    r[1] = a[1];
}

หมายเหตุ: นี่เป็นหนึ่งในวิธีการที่เหมาะสมที่สุดเนื่องจากมีการเรียกใช้จำนวนมากatan2fแต่อาจเป็นที่สนใจของนักพัฒนาที่อ่านหัวข้อนี้


0

สำหรับการตรวจจับการชนรูปหลายเหลี่ยมเราจำเป็นต้องทดสอบสองสิ่ง:

  1. ถ้า Point อยู่ในพื้นที่รูปหลายเหลี่ยม (สามารถทำได้โดยอัลกอริธึมการหล่อ)
  2. ถ้า Point อยู่บนเส้นขอบรูปหลายเหลี่ยม (สามารถทำได้โดยอัลกอริทึมเดียวกันซึ่งใช้สำหรับการตรวจจับจุดบนเส้นหลายเส้น (เส้น))

0

ในการจัดการกับกรณีพิเศษต่อไปนี้ในอัลกอริทึมการคัดเลือกเรย์ :

  1. รังสีซ้อนทับด้านใดด้านหนึ่งของรูปหลายเหลี่ยม
  2. จุดนั้นอยู่ด้านในของรูปหลายเหลี่ยมและรังสีจะผ่านจุดสุดยอดของรูปหลายเหลี่ยม
  3. จุดนั้นอยู่นอกรูปหลายเหลี่ยมและรังสีก็แค่แตะที่มุมหนึ่งของรูปหลายเหลี่ยม

ตรวจสอบการตัดสินว่าจุดหนึ่งอยู่ภายในรูปหลายเหลี่ยมที่ซับซ้อนหรือไม่ บทความนี้ให้วิธีง่ายๆในการแก้ไขปัญหาดังกล่าวดังนั้นจึงไม่จำเป็นต้องได้รับการดูแลเป็นพิเศษสำหรับกรณีข้างต้น


0

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

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

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

คุณสามารถโพสต์คำถามในชุมชนคณิตศาสตร์ได้เช่นกัน ฉันเดิมพันพวกเขามีหนึ่งล้านวิธีในการทำเช่นนั้น


0

หากคุณกำลังมองหาไลบรารีจาวาสคริปต์มี javascript google maps v3 extension สำหรับคลาส Polygon เพื่อตรวจสอบว่ามีจุดอยู่ภายในหรือไม่

var polygon = new google.maps.Polygon([], "#000000", 1, 1, "#336699", 0.3);
var isWithinPolygon = polygon.containsLatLng(40, -90);

Google Extit Github



0

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

สำหรับรูปหลายเหลี่ยมอย่างง่ายอัลกอริธึมที่ดีที่สุดคืออัลกอริธึมการเรย์ (หมายเลขข้าม) สำหรับรูปหลายเหลี่ยมที่ซับซ้อนอัลกอริทึมนี้จะไม่ตรวจจับจุดที่อยู่ภายในขอบเขตที่ทับซ้อนกัน ดังนั้นสำหรับรูปหลายเหลี่ยมที่ซับซ้อนคุณต้องใช้อัลกอริทึมหมายเลข Winding

นี่คือบทความที่ยอดเยี่ยมด้วยการใช้ C ของอัลกอริทึมทั้งสอง ฉันลองพวกเขาและพวกเขาทำงานได้ดี

http://geomalgorithms.com/a03-_inclusion.html


0

โซลูชันรุ่นสกาล่าโดย nirg (สมมติว่ามีการตรวจสอบขอบเขตสี่เหลี่ยมผืนผ้าล่วงหน้าแยกต่างหาก):

def inside(p: Point, polygon: Array[Point], bounds: Bounds): Boolean = {

  val length = polygon.length

  @tailrec
  def oddIntersections(i: Int, j: Int, tracker: Boolean): Boolean = {
    if (i == length)
      tracker
    else {
      val intersects = (polygon(i).y > p.y) != (polygon(j).y > p.y) && p.x < (polygon(j).x - polygon(i).x) * (p.y - polygon(i).y) / (polygon(j).y - polygon(i).y) + polygon(i).x
      oddIntersections(i + 1, i, if (intersects) !tracker else tracker)
    }
  }

  oddIntersections(0, length - 1, tracker = false)
}

0

นี่คือรุ่น golang ของคำตอบ @nirg (แรงบันดาลใจจากรหัส C # โดย @@ m-katz)

func isPointInPolygon(polygon []point, testp point) bool {
    minX := polygon[0].X
    maxX := polygon[0].X
    minY := polygon[0].Y
    maxY := polygon[0].Y

    for _, p := range polygon {
        minX = min(p.X, minX)
        maxX = max(p.X, maxX)
        minY = min(p.Y, minY)
        maxY = max(p.Y, maxY)
    }

    if testp.X < minX || testp.X > maxX || testp.Y < minY || testp.Y > maxY {
        return false
    }

    inside := false
    j := len(polygon) - 1
    for i := 0; i < len(polygon); i++ {
        if (polygon[i].Y > testp.Y) != (polygon[j].Y > testp.Y) && testp.X < (polygon[j].X-polygon[i].X)*(testp.Y-polygon[i].Y)/(polygon[j].Y-polygon[i].Y)+polygon[i].X {
            inside = !inside
        }
        j = i
    }

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