อัลกอริทึมสำหรับการพอง / การยุบ (การชดเชยการบัฟเฟอร์) รูปหลายเหลี่ยม


202

ฉันจะ "ขยาย" รูปหลายเหลี่ยมได้อย่างไร นั่นคือฉันต้องการทำสิ่งที่คล้ายกับนี้:

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

ความต้องการคือขอบ / จุดของรูปหลายเหลี่ยมใหม่ (พองตัว) นั้นอยู่ที่ระยะคงที่เท่าเดิมจากรูปหลายเหลี่ยมเก่า (ดั้งเดิม) (บนภาพตัวอย่างที่พวกมันไม่ได้ใช้, เนื่องจากมันจะต้องใช้ส่วนโค้งสำหรับจุดยอดที่สูงเกินจริง) ลืมเรื่องนั้นตอนนี้;))

ระยะทางคณิตศาสตร์สำหรับสิ่งที่ฉันกำลังมองหาเป็นจริงภายใน / ภายนอกรูปหลายเหลี่ยม offseting +1 ไปที่ balint เพื่อชี้สิ่งนี้ การตั้งชื่อที่เป็นทางเลือกรูปหลายเหลี่ยมบัฟเฟอร์

ผลการค้นหาของฉัน:

นี่คือลิงค์บางส่วน:


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

1
แน่นอนถ้ารูปหลายเหลี่ยมของคุณเป็นเว้าที่จะเริ่มต้นด้วย (ตามตัวอย่างข้างต้น) คุณต้องตัดสินใจว่าจะเกิดอะไรขึ้น ณ จุดที่อัลกอริทึมไร้เดียงสาต้องการสร้าง 'รูปหลายเหลี่ยม' ที่ตัดกันด้วยตนเอง ...
AakashM

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

สวัสดีนี่เป็นปัญหาของฉันด้วยยกเว้นฉันต้องทำสิ่งนี้ในแบบ 3 มิติ มีทางเลือกอีกทางหนึ่งสำหรับโครงกระดูกเส้นตรงของวิธีสามมิติที่อธิบายไว้ในบทความarxiv.org/pdf/0805.0022.pdfหรือไม่?
stephanmg

คำตอบ:


138

ผมคิดว่าผมสั้นอาจจะพูดถึงของตัวเองตัดรูปหลายเหลี่ยมและห้องสมุดหักล้าง - ปัตตาเลี่ยน

ในขณะที่ปัตตาเลี่ยนได้รับการออกแบบมาเป็นหลักสำหรับการดำเนินการตัดรูปหลายเหลี่ยม แต่ก็มีรูปหลายเหลี่ยมชดเชยด้วย ห้องสมุดฟรีแวร์โอเพนซอร์สที่เขียนในDelphi, C ++ และ C # มันมีสิทธิ์ใช้งานBoost ที่ไม่มีข้อ จำกัดช่วยให้สามารถใช้งานได้ทั้งฟรีแวร์และแอพพลิเคชั่นเชิงพาณิชย์โดยไม่เสียค่าใช้จ่าย

การชดเชยรูปหลายเหลี่ยมสามารถทำได้โดยใช้หนึ่งในสามของรูปแบบการชดเชย - ยกกำลังสอง, กลมและลดขนาด

รูปแบบการชดเชยรูปหลายเหลี่ยม


2
เจ๋งมาก! คุณอยู่ที่ไหนเมื่อ 2 ปีก่อน? :) ในที่สุดฉันต้องใช้ตรรกะออฟเซ็ตของตัวเอง (และเสียเวลาไปมาก) คุณใช้อัลกอริทึมแบบใดสำหรับการชดเชยรูปหลายเหลี่ยม BTW ฉันใช้ไฟป่า คุณรับมือกับหลุมเป็นรูปหลายเหลี่ยมหรือไม่?
Igor Brejc

2
2 ปีที่แล้วฉันกำลังมองหาทางออกที่ดีในการตัดรูปหลายเหลี่ยมที่ไม่ได้ติดขัดกับปัญหาลิขสิทธิ์ที่ยุ่งยาก :) การชดเชยขอบทำได้โดยการสร้างบรรทัดฐานของหน่วยสำหรับขอบทั้งหมด Edge joins เรียงตาม clipper รูปหลายเหลี่ยมของฉันเนื่องจากการวางแนวของจุดตัดที่ซ้อนทับเหล่านี้อยู่ตรงกันข้ามกับการวางแนวของรูปหลายเหลี่ยม แน่นอนว่ามีการจัดการกับหลุมเช่นเดียวกับรูปหลายเหลี่ยมที่ตัดกันตนเองเป็นต้นไม่มีข้อ จำกัด ประเภทหรือหมายเลข ดูเพิ่มเติม "การชดเชยรูปหลายเหลี่ยมด้วยการคำนวณตัวเลขการหมุน" ที่นี่: me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf
Angus Johnson

โว้ว! อย่าคิดว่าคำถามนี้ "ลืม" ไปสักวินาที! ฉันดูที่นี่เมื่อสัปดาห์ที่แล้ว - ฉันไม่ได้คาดหวังว่าจะได้กลับมาที่นี่อีก! ขอบคุณมัด!
Chris Burt-Brown

เอกสาร Clipper ในบัฟเฟอร์โพลี are here: angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/...
Drew Noakes

5
สำหรับใครก็ตามที่ต้องการทำสิ่งนี้อีกทางเลือกหนึ่งคือใช้ GEOS และถ้าคุณใช้ python ก็ใช้ wrapper ของ GEOS ที่มีหุ่นดี ตัวอย่างที่น่ารักจริงๆ: toblerity.github.com/shapely/manual.html#object.buffer
pelson

40

รูปหลายเหลี่ยมที่คุณกำลังมองหาที่เรียกว่าขาเข้า / ชดเชยออกไปด้านนอกรูปหลายเหลี่ยมในเรขาคณิตการคำนวณและเป็นที่เกี่ยวข้องอย่างใกล้ชิดกับโครงกระดูกตรง

เหล่านี้เป็นรูปหลายเหลี่ยมชดเชยสำหรับรูปหลายเหลี่ยมที่ซับซ้อน:

และนี่คือโครงกระดูกตรงสำหรับรูปหลายเหลี่ยมอีกอัน:

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

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

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

คู่มือ CGAL: 2D Offset Skeleton และ Polygon Offsetting


12

สำหรับประเภทของสิ่งเหล่านี้ผมมักจะใช้เจทีเอส เพื่อวัตถุประสงค์ในการสาธิตฉันสร้างjsFiddleนี้ที่ใช้JSTS (พอร์ต JavaScript ของ JTS) คุณเพียงแค่ต้องแปลงพิกัดที่คุณต้องเป็นพิกัด JSTS:

function vectorCoordinates2JTS (polygon) {
  var coordinates = [];
  for (var i = 0; i < polygon.length; i++) {
    coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
  }
  return coordinates;
}

ผลที่ได้คืออะไรเช่นนี้:

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

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

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


9

ฟังดูเหมือนว่าสิ่งที่คุณต้องการคือ:

  • เริ่มต้นที่จุดยอดหันหน้าทวนเข็มนาฬิกาไปตามขอบที่อยู่ติดกัน
  • แทนที่ขอบด้วยขอบขนานใหม่ที่วางไว้ที่ระยะทาง dเป็น "ซ้าย" ของขอบเก่า
  • ทำซ้ำสำหรับทุกขอบ
  • ค้นหาจุดตัดของขอบใหม่เพื่อรับจุดยอดใหม่
  • ตรวจสอบว่าคุณได้กลายเป็นรูปหลายเหลี่ยมไขว้และตัดสินใจว่าจะทำอย่างไรกับมัน อาจเพิ่มจุดสุดยอดใหม่ที่จุดข้ามและกำจัดบางจุดเก่า ฉันไม่แน่ใจว่าจะมีวิธีที่ดีกว่าในการตรวจจับนี้มากกว่าเพียงเพื่อเปรียบเทียบทุกคู่ของขอบที่ไม่ติดกันเพื่อดูว่าจุดตัดของพวกเขาอยู่ระหว่างจุดยอดทั้งคู่หรือไม่

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

ฉันไม่ทราบว่าอัลกอริทึมนี้มีชื่อรหัสตัวอย่างบนเว็บหรือการเพิ่มประสิทธิภาพโหดเหี้ยม แต่ฉันคิดว่ามันอธิบายสิ่งที่คุณต้องการ


6

ในโลกของ GIS นั้นมีการใช้บัฟเฟอร์เชิงลบสำหรับงานนี้: http://www-users.cs.umn.edu/~npramod/enc_pdf.pdf

ห้องสมุด JTSควรทำเช่นนี้สำหรับคุณ ดูเอกสารประกอบการใช้งานบัฟเฟอร์: http://tsusiatsoftware.net/jts/javadoc/com/vividsolutions/jts/operation/buffer/package-summary.html

สำหรับภาพรวมคร่าวๆดูคู่มือผู้พัฒนา: http://www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf


5

แต่ละบรรทัดควรแบ่งระนาบเป็น "ภายใน" และ "เค้าร่าง"; คุณสามารถค้นหาสิ่งนี้ได้โดยใช้วิธีการภายในผลิตภัณฑ์ตามปกติ

ย้ายทุกเส้นออกไปด้านนอกด้วยระยะทาง

พิจารณาคู่สายเพื่อนบ้านทั้งหมด (เส้นไม่ใช่ส่วนของเส้นตรง) ค้นหาจุดตัด นี่คือจุดสุดยอดใหม่

ทำความสะอาดจุดสุดยอดใหม่โดยการลบส่วนใด ๆ ที่ตัดกัน - เรามีบางกรณีที่นี่

(a) กรณีที่ 1:

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

ถ้าคุณใช้มันไปคุณจะได้:

0----a----3
|    |    |
|    |    |
|    b    |
|         |
|         |
1---------2

7 และ 4 ทับซ้อน .. ถ้าคุณเห็นสิ่งนี้คุณลบจุดนี้และจุดทั้งหมดในระหว่าง

(b) กรณีที่ 2

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

หากคุณใช้จ่ายสองเท่าคุณจะได้รับ:

0----47----3
|    ||    |
|    ||    |
|    ||    |
|    56    |
|          |
|          |
|          |
1----------2

ในการแก้ไขปัญหานี้สำหรับแต่ละส่วนของบรรทัดคุณต้องตรวจสอบว่ามันทับซ้อนกับส่วนหลังหรือไม่

(c) กรณีที่ 3

       4--3
 0--X9 |  |
 |  78 |  |
 |  6--5  |
 |        |
 1--------2

ใช้จ่ายโดย 1. นี่เป็นกรณีทั่วไปมากขึ้นสำหรับกรณีที่ 1

(d) กรณีที่ 4

เช่นเดียวกับ case3 แต่ใช้จ่ายด้วยสอง

ที่จริงแล้วถ้าคุณสามารถจัดการเคส 4 เคสอื่น ๆ ทั้งหมดเป็นเคสพิเศษของมันที่มีเส้นหรือจุดยอดทับซ้อนกัน

ในการทำกรณีที่ 4 คุณจะเก็บยอดจุดยอด .. คุณกดเมื่อคุณพบบรรทัดที่ทับซ้อนกับบรรทัดหลังให้ป๊อปอัปเมื่อคุณได้รับบรรทัดหลัง - เช่นเดียวกับสิ่งที่คุณทำในนูน - ฮัลล์


คุณรู้จักอัลกอริทึม psedo สำหรับสิ่งนี้หรือไม่
EmptyData

5

นี่คือทางเลือกอื่นดูว่าคุณชอบสิ่งนี้ดีกว่า

  1. ทำรูปสามเหลี่ยมมันไม่จำเป็นต้อง delaunay - สามเหลี่ยมใด ๆ ที่จะทำ

  2. ขยายแต่ละสามเหลี่ยม - ควรเป็นเรื่องเล็กน้อย หากคุณเก็บรูปสามเหลี่ยมในลำดับทวนเข็มนาฬิกาเพียงย้ายเส้นไปทางด้านขวามือแล้วทำการแยก

  3. ผสานเข้ากับการใช้อัลกอริทึมการตัด Weiler-Atherton


คุณขยายสามเหลี่ยมอย่างไร? ผลลัพธ์ของคุณขึ้นอยู่กับสมการหรือไม่ ด้วยวิธีการนี้คุณสามารถจัดการกรณีและปัญหาเมื่อคุณลดขนาดของรูปหลายเหลี่ยมได้หรือไม่
balint.miklos

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

1
Igor: อัลกอริทึมการตัด Weiler-Atherton สามารถจัดการกรณี "จุดยอดบางอย่างต้องถูกกำจัด" ได้อย่างถูกต้อง
J-16 SDiZ

@balint: ขยายสามเหลี่ยมเป็นเรื่องไม่สำคัญ: ถ้าคุณเก็บจุดยอดในลำดับปกติทางด้านขวามือจะเป็น "ออกไปด้านนอก" เสมอ เพียงปฏิบัติต่อส่วนของเส้นเหล่านั้นเป็นเส้นเลื่อนออกไปด้านนอกและค้นหาการโต้ตอบ - มันคือจุดสุดยอดใหม่ สำหรับการวิเคราะห์สามเหลี่ยมเองในความคิดที่สองการหาสมการ delaunay อาจให้ผลลัพธ์ที่ดีกว่า
J-16 SDiZ

4
ฉันคิดว่าวิธีการนี้สามารถให้ผลลัพธ์ที่ไม่ดีได้อย่างง่ายดาย แม้กระทั่งตัวอย่างง่ายๆเช่น quad triangulated โดยใช้เส้นทแยงมุม สำหรับรูปสามเหลี่ยมสองรูปขนาดใหญ่ที่คุณได้รับ: img200.imageshack.us/img200/2640/counterm.pngและสหภาพของพวกเขาไม่ใช่สิ่งที่คุณกำลังมองหา ฉันไม่เห็นว่าวิธีการนี้มีประโยชน์อย่างไร
balint.miklos

3

ขอบคุณ Angus Johnson สำหรับห้องสมุดปัตตาเลี่ยนของเขา มีตัวอย่างโค้ดที่ดีสำหรับการทำสิ่งที่ตัดที่หน้าแรกของ clipper ที่http://www.angusj.com/delphi/clipper.php#code แต่ฉันไม่เห็นตัวอย่างสำหรับการชดเชยหลายเหลี่ยม ดังนั้นฉันคิดว่ามันอาจจะเป็นประโยชน์สำหรับใครบางคนถ้าฉันโพสต์รหัสของฉัน:

    public static List<Point> GetOffsetPolygon(List<Point> originalPath, double offset)
    {
        List<Point> resultOffsetPath = new List<Point>();

        List<ClipperLib.IntPoint> polygon = new List<ClipperLib.IntPoint>();
        foreach (var point in originalPath)
        {
            polygon.Add(new ClipperLib.IntPoint(point.X, point.Y));
        }

        ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset();
        co.AddPath(polygon, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);

        List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
        co.Execute(ref solution, offset);

        foreach (var offsetPath in solution)
        {
            foreach (var offsetPathPoint in offsetPath)
            {
                resultOffsetPath.Add(new Point(Convert.ToInt32(offsetPathPoint.X), Convert.ToInt32(offsetPathPoint.Y)));
            }
        }

        return resultOffsetPath;
    }

2

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

poly += 2; // buffer polygon by 2

ฉันไม่เข้าใจว่าคุณควรทำอะไรด้วย boost :: polygon เพราะรองรับเฉพาะจำนวนเต็มพิกัดเท่านั้น สมมติว่าฉันมีรูปหลายเหลี่ยมแบบทั่วไป (พิกัดจุดลอยตัว) และฉันต้องการขยาย - ฉันจะทำอย่างไร
David Doria

@DavidDoria: ขึ้นอยู่กับความละเอียด / ความแม่นยำและช่วงไดนามิกที่คุณต้องการสำหรับพิกัดของคุณ แต่คุณสามารถใช้ int 32 บิตหรือ 64 บิตพร้อมปัจจัยการปรับสเกลที่เหมาะสม บังเอิญฉันใช้ (โดยไม่ได้ตั้งใจ) boost :: รูปหลายเหลี่ยมที่มีพิกัดลอยในอดีตและดูเหมือนว่าจะทำงานได้ดี แต่มันอาจจะไม่สมบูรณ์ 100% (เอกสารเตือนต่อ!
Paul R

ฉันจะโอเคกับ "มันจะทำงานได้เกือบตลอดเวลา" :) ฉันลองทำสิ่งนี้: ideone.com/XbZeBfแต่มันไม่ได้คอมไพล์ - คิดอะไรบ้าง?
David Doria

ฉันไม่เห็นอะไรผิดปกติ แต่ในกรณีของฉันฉันใช้ความเชี่ยวชาญเฉพาะด้าน (polygon_90) ดังนั้นฉันไม่รู้ว่ามันสร้างความแตกต่างหรือไม่ ไม่กี่ปีที่ผ่านมาตั้งแต่ที่ฉันเล่นรอบนี้
Paul R

ตกลง - มันกลับมาหาฉันแล้วตอนนี้คุณสามารถใช้ได้+=กับชุดรูปหลายเหลี่ยมเท่านั้นไม่ใช่กับรูปหลายเหลี่ยมแต่ละรูป ลองด้วย std :: vector ของรูปหลายเหลี่ยม (แน่นอนว่าเวกเตอร์ต้องการเพียงหนึ่งรูปหลายเหลี่ยมเท่านั้น)
Paul R

1

ตามคำแนะนำจาก @ JoshO'Brian จะปรากฏrGeosแพคเกจในRภาษาที่ใช้อัลกอริทึมนี้ rGeos::gBufferดู


0

มีไลบรารี่สองอันที่สามารถใช้ได้ (ใช้ได้กับชุดข้อมูล 3D)

  1. https://github.com/otherlab/openmesh
  2. https://github.com/alecjacobson/nested_cages
  3. http://homepage.tudelft.nl/h05k3/Projects/MeshThickeningProj.htm

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

อันสุดท้ายมีการพึ่งพาน้อยที่สุดและอยู่ในตัวเองและสามารถอ่านในไฟล์. obj

ด้วยความปรารถนาดีสเตฟาน


0

ฉันใช้รูปทรงเรขาคณิตอย่างง่าย: เวกเตอร์และ / หรือตรีโกณมิติ

  1. ที่แต่ละมุมให้หาเวกเตอร์กลางและมุมกลาง Mid vector คือค่าเฉลี่ยเลขคณิตของเวกเตอร์สองหน่วยที่กำหนดโดยขอบของมุม Mid Angle คือครึ่งหนึ่งของมุมที่กำหนดโดยขอบ

  2. หากคุณต้องการขยาย (หรือสัญญา) รูปหลายเหลี่ยมของคุณด้วยจำนวน d จากแต่ละขอบ คุณควรออกไปข้างนอกด้วยจำนวน d / sin (midAngle) เพื่อรับแต้มมุมใหม่

  3. ทำซ้ำขั้นตอนนี้ทุกซอกทุกมุม

*** ระวังทิศทางของคุณ ทำ CounterClockWise Test โดยใช้สามจุดที่กำหนดมุม; เพื่อหาวิธีออกหรือใน

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