Một thuật toán để thổi phồng / xì hơi (bù đắp, đệm) đa giác


202

Làm thế nào tôi sẽ "thổi phồng" một đa giác? Đó là, tôi muốn làm một cái gì đó tương tự như thế này:

văn bản thay thế

Yêu cầu là các cạnh / điểm của đa giác mới (bị thổi phồng) đều có cùng khoảng cách không đổi so với các đa giác cũ (nguyên bản) (trên hình ảnh ví dụ mà chúng không có, vì vậy nó sẽ phải sử dụng các cung cho các đỉnh bị thổi phồng, nhưng hãy hãy quên điều đó ngay bây giờ;)).

Thuật ngữ toán học cho những gì tôi đang tìm kiếm thực sự là bù đắp đa giác hướng nội / hướng ngoại . +1 để cân bằng cho việc chỉ ra điều này. Việc đặt tên thay thế là bộ đệm đa giác .

Kết quả tìm kiếm của tôi:

Dưới đây là một số liên kết:


17
Đây hoàn toàn không phải là một câu hỏi tầm thường: nếu giảm phát / lạm phát nhỏ, không có gì nghiêm trọng xảy ra, nhưng đến một lúc nào đó, các đỉnh sẽ biến mất. Có lẽ điều này đã được thực hiện trước đây, vì vậy tôi muốn nói: sử dụng thuật toán của người khác, đừng xây dựng thuật toán của riêng bạn.
Martijn

1
Thật vậy, nếu đa giác của bạn bắt đầu lõm vào (như trong ví dụ ở trên), bạn phải quyết định điều gì sẽ xảy ra tại thời điểm thuật toán ngây thơ muốn tạo một 'đa giác' tự giao nhau ...
AakashM

Vâng, vấn đề chính là các phần lõm của đa giác, đây là nơi phức tạp nằm. Tôi vẫn nghĩ rằng nó không nên là một vấn đề như vậy để tính toán khi một đỉnh nhất định phải được loại bỏ. Câu hỏi chính là loại phức tạp tiệm cận này sẽ yêu cầu gì.
Igor Brejc

Xin chào, đây cũng là vấn đề của tôi, ngoại trừ tôi cần làm điều này trong 3D. Có sự thay thế nào cho các bộ xương thẳng của phương pháp đa diện ba chiều được mô tả trong bài báo arxiv.org/pdf/0805.0022.pdf không?
stephanmg

Câu trả lời:


138

Tôi nghĩ rằng tôi có thể đề cập ngắn gọn về thư viện cắt và bù đắp đa giác của riêng tôi - Clipper .

Mặc dù Clipper được thiết kế chủ yếu cho các hoạt động cắt đa giác, nhưng nó cũng bù đắp đa giác. Thư viện là phần mềm miễn phí mã nguồn mở được viết bằng Delphi, C ++ và C # . Nó có một Boost rất không bị cản trở giấy phép cho phép nó được sử dụng trong cả phần mềm miễn phí và ứng dụng thương mại mà không phải trả phí.

Việc bù đắp đa giác có thể được thực hiện bằng một trong ba kiểu bù - bình phương, tròn và giảm nhẹ.

Kiểu đa giác bù đắp


2
Rất tuyệt! Bạn đã ở đâu 2 năm trước? :) Cuối cùng, tôi đã phải thực hiện logic bù đắp của riêng mình (và mất rất nhiều thời gian cho nó). Thuật toán nào bạn đang sử dụng để bù đắp đa giác, BTW? Tôi đã sử dụng lửa cỏ. Bạn có xử lý các lỗ trong đa giác?
Igor Brejc

2
Cách đây 2 năm, tôi đã tìm kiếm một giải pháp hợp lý cho việc cắt đa giác mà không bị vướng mắc với các vấn đề giấy phép khó khăn :). Việc bù đắp cạnh đạt được bằng cách tạo các chỉ tiêu đơn vị cho tất cả các cạnh. Các phép nối cạnh được thu dọn bởi clipper đa giác của tôi vì các hướng của các giao điểm chồng chéo này ngược với hướng của các đa giác. Các lỗ chắc chắn được xử lý như là đa giác tự giao nhau, vv Không có hạn chế đối với loại hoặc số của chúng. Xem thêm "Polygon Bù trừ bởi Computing số Winding" ở đây: me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf
Angus Johnson

Ái chà! Đừng vì một giây nghĩ rằng câu hỏi này là "bị lãng quên"! Tôi đã nhìn vào đây tuần trước - tôi không mong đợi trở lại điều này! Cảm ơn nhiều!
Chris Burt-Brown

Các tài liệu của Clip
Drew Noakes

5
Đối với bất kỳ ai muốn làm điều này, một cách khác là sử dụng GEOS và nếu bạn sử dụng python, trình bao bọc của GEOS, Shapely. Một ví dụ thực sự hay: toblerity.github.com/shapely/manual.html#object.buffer
pelson

40

Đa giác bạn đang tìm kiếm được gọi là đa giác bù vào / ra ngoài trong hình học tính toán và nó có liên quan chặt chẽ với khung xương thẳng .

Đây là một số đa giác bù cho một đa giác phức tạp:

Và đây là bộ xương thẳng cho một đa giác khác:

Như đã chỉ ra trong các bình luận khác, tùy thuộc vào khoảng cách bạn dự định "thổi phồng / xì hơi" đa giác của bạn, bạn có thể kết thúc với kết nối khác nhau cho đầu ra.

Từ quan điểm tính toán: một khi bạn có bộ xương thẳng, người ta có thể xây dựng các đa giác bù tương đối dễ dàng. Thư viện CGAL mã nguồn mở và (miễn phí cho phi thương mại) có một gói triển khai các cấu trúc này. Xem ví dụ mã này để tính toán các đa giác bù bằng CGAL.

Các tay gói nên cung cấp cho bạn một tốt bắt đầu từ điểm trên làm thế nào để xây dựng các cấu trúc này ngay cả khi bạn đang không sử dụng CGAL, và chứa tham chiếu đến các giấy tờ với các định nghĩa toán học và tính chất:

Hướng dẫn CGAL: 2D Straight Skeleton và Polygon Offs


12

Đối với những loại điều tôi thường sử dụng JTS . Với mục đích trình diễn, tôi đã tạo jsFiddle này sử dụng JSTS (cổng JavaScript của JTS). Bạn chỉ cần chuyển đổi tọa độ bạn có sang tọa độ 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;
}

Kết quả là một cái gì đó như thế này:

nhập mô tả hình ảnh ở đây

Thông tin bổ sung : Tôi thường sử dụng loại lạm phát / xì hơi này (một chút sửa đổi cho mục đích của tôi) để đặt ranh giới với bán kính trên đa giác được vẽ trên bản đồ (với bản đồ Tờ rơi hoặc Google). Bạn chỉ cần chuyển đổi các cặp (lat, lng) thành tọa độ JSTS và mọi thứ khác đều giống nhau. Thí dụ:

nhập mô tả hình ảnh ở đây


9

Âm thanh với tôi như những gì bạn muốn là:

  • Bắt đầu từ một đỉnh, quay mặt ngược chiều kim đồng hồ dọc theo một cạnh liền kề.
  • Thay thế cạnh bằng một cạnh song song mới được đặt ở khoảng cách d với "bên trái" của cạnh cũ.
  • Lặp lại cho tất cả các cạnh.
  • Tìm các giao điểm của các cạnh mới để có các đỉnh mới.
  • Phát hiện nếu bạn trở thành một đa giác chéo và quyết định phải làm gì về nó. Có lẽ thêm một đỉnh mới tại điểm giao nhau và loại bỏ một số đỉnh cũ. Tôi không chắc liệu có cách nào tốt hơn để phát hiện điều này hay không chỉ là so sánh mọi cặp cạnh không liền kề để xem giao điểm của chúng có nằm giữa cả hai cặp đỉnh hay không.

Đa giác kết quả nằm ở khoảng cách yêu cầu từ đa giác cũ "đủ xa" từ các đỉnh. Gần một đỉnh, tập hợp các điểm ở khoảng cách dtừ đa giác cũ, như bạn nói, không phải là đa giác, do đó, yêu cầu như đã nêu không thể được đáp ứng.

Tôi không biết thuật toán này có tên, mã ví dụ trên web hay tối ưu hóa hay không, nhưng tôi nghĩ nó mô tả những gì bạn muốn.


6

Trong thế giới GIS, người ta sử dụng bộ đệm âm cho nhiệm vụ này: http://www-users.cs.umn.edu/~npramod/enc_pdf.pdf

Các thư viện JTS nên làm điều này cho bạn. Xem tài liệu về hoạt động của bộ đệm: http://tsusiatsoftware.net/jts/javadoc/com/vividsolutions/jts/operation/buffer/package-summary.html

Để biết tổng quan sơ bộ, hãy xem Hướng dẫn dành cho nhà phát triển: http://www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf


5

Mỗi dòng nên chia mặt phẳng thành "bên trong" và "phác thảo"; bạn có thể tìm ra điều này bằng cách sử dụng phương pháp sản phẩm bên trong thông thường.

Di chuyển tất cả các dòng ra bên ngoài bởi một số khoảng cách.

Xem xét tất cả các cặp đường lân cận (đường, không phải đoạn thẳng), tìm giao điểm. Đây là những đỉnh mới.

Dọn dẹp đỉnh mới bằng cách loại bỏ bất kỳ phần giao nhau. - chúng tôi có một vài trường hợp ở đây

(a) Trường hợp 1:

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

nếu bạn chi tiêu nó một, bạn sẽ nhận được điều này:

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

7 và 4 trùng nhau .. nếu bạn thấy điều này, bạn xóa điểm này và tất cả các điểm ở giữa.

(b) trường hợp 2

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

nếu bạn tiêu nó bằng hai, bạn đã nhận được điều này:

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

Để giải quyết vấn đề này, đối với từng phân đoạn của dòng, bạn phải kiểm tra xem nó có trùng với các phân đoạn sau không.

(c) trường hợp 3

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

chi bằng 1. đây là trường hợp tổng quát hơn cho trường hợp 1.

(d) trường hợp 4

giống như case3, nhưng chi bằng hai.

Trên thực tế, nếu bạn có thể xử lý trường hợp 4. Tất cả các trường hợp khác chỉ là trường hợp đặc biệt của nó với một số dòng hoặc đỉnh chồng chéo.

Để thực hiện trường hợp 4, bạn giữ một chồng đỉnh .. bạn đẩy khi bạn tìm thấy các dòng trùng với dòng sau, bật nó khi bạn nhận được dòng sau. - giống như những gì bạn làm trong thân lồi.


Bạn có biết bất kỳ thuật toán psedo cho điều này.
EmptyData

5

Đây là một giải pháp thay thế, xem nếu bạn thích điều này tốt hơn.

  1. Thực hiện một tam giác , nó không phải là delaunay - bất kỳ tam giác nào cũng được.

  2. Thổi phồng mỗi tam giác - điều này nên tầm thường. nếu bạn lưu trữ hình tam giác theo thứ tự ngược chiều kim đồng hồ, chỉ cần di chuyển các đường sang phía bên tay phải và thực hiện giao cắt.

  3. Hợp nhất chúng bằng thuật toán cắt Weiler-Atherton đã sửa đổi


Làm thế nào để bạn thổi phồng hình tam giác chính xác? Có đầu ra của bạn phụ thuộc vào tam giác? Với phương pháp này bạn có thể xử lý trường hợp khi bạn thu nhỏ đa giác không?
balint.miklos

Bạn có chắc chắn phương pháp này thực sự hiệu quả đối với lạm phát đa giác? Điều gì xảy ra khi các phần lõm của đa giác bị thổi phồng đến mức mà một số đỉnh phải được loại bỏ. Vấn đề là: khi bạn nhìn những gì xảy ra với hình tam giác sau poly. lạm phát, các hình tam giác không bị thổi phồng, thay vào đó chúng bị biến dạng.
Igor Brejc

1
Igor: Thuật toán cắt Weiler-Atherton có thể xử lý trường hợp "một số đỉnh phải được loại bỏ" một cách chính xác;
J-16 SDiZ

@balint: thổi phồng một hình tam giác là chuyện nhỏ: nếu bạn lưu trữ vertrex theo thứ tự bình thường, phía bên tay phải luôn "hướng ngoại". Chỉ cần coi các phân đoạn dòng đó là các dòng, di chuyển chúng ra ngoài và tìm sự tương tác - chúng là đỉnh mới. Đối với chính tam giác, trên một ý nghĩ thứ hai, tam giác delaunay có thể cho kết quả tốt hơn.
J-16 SDiZ

4
Tôi nghĩ rằng phương pháp này có thể dễ dàng cho kết quả xấu. Ngay cả đối với một ví dụ đơn giản là tứ giác sử dụng đường chéo. Đối với hai hình tam giác mở rộng bạn nhận được: img200.imageshack.us/img200/2640/c gặpm.png và liên minh của họ không phải là thứ bạn đang tìm kiếm. Tôi không thấy phương pháp này hữu ích như thế nào.
balint.miklos

3

Cảm ơn rất nhiều đến Angus Johnson cho thư viện clipper của mình. Có các mẫu mã tốt để thực hiện công cụ cắt tại trang chủ của clipper tại http://www.angusj.com/delphi/clipper.php#code nhưng tôi không thấy ví dụ nào cho việc bù đắp đa giác. Vì vậy, tôi nghĩ rằng có lẽ nó được sử dụng cho ai đó nếu tôi đăng mã của mình:

    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

Một tùy chọn khác là sử dụng boost :: polygon - tài liệu hơi thiếu, nhưng bạn nên thấy rằng các phương thức resizebloat, cũng như +=toán tử quá tải , thực sự thực hiện đệm. Vì vậy, ví dụ tăng kích thước của đa giác (hoặc một tập hợp đa giác) theo một số giá trị có thể đơn giản như:

poly += 2; // buffer polygon by 2

Tôi không hiểu làm thế nào bạn có nghĩa vụ phải làm bất cứ điều gì với boost :: polygon vì nó chỉ hỗ trợ tọa độ nguyên? Nói rằng tôi có một đa giác (tọa độ điểm nổi) chung và tôi muốn mở rộng nó - tôi sẽ làm gì?
David Doria

@DavidDoria: tùy thuộc vào độ phân giải / độ chính xác và dải động bạn cần cho tọa độ của bạn, nhưng bạn có thể sử dụng int 32 bit hoặc 64 bit với hệ số tỷ lệ thích hợp. Tình cờ tôi đã (vô tình) sử dụng boost :: đa giác với tọa độ float trong quá khứ và nó có vẻ hoạt động tốt, nhưng nó có thể không mạnh mẽ 100% (các tài liệu cảnh báo chống lại nó!).
Paul R

Tôi sẽ ổn với "nó sẽ hoạt động hầu hết thời gian" :). Tôi đã thử điều này: ideone.com/XbZeBf nhưng nó không biên dịch - bạn có suy nghĩ gì không?
David Doria

Tôi không thấy bất cứ điều gì rõ ràng sai, nhưng trong trường hợp của tôi, tôi đã sử dụng các chuyên ngành trực tràng (polygon_90) vì vậy tôi không biết liệu điều đó có làm nên sự khác biệt hay không. Đã được một vài năm kể từ khi tôi chơi xung quanh với điều này mặc dù.
Paul R

OK - nó sẽ quay lại với tôi ngay bây giờ - bạn chỉ có thể sử dụng +=với một bộ đa giác , không phải với các đa giác riêng lẻ. Hãy thử nó với một std :: vector của đa giác. (Tất nhiên vectơ chỉ cần chứa một đa giác).
Paul R

1

Dựa trên lời khuyên từ @ JoshO'Brian, nó xuất hiện rGeosgói trong Rngôn ngữ thực hiện thuật toán này. Xem rGeos::gBuffer.


0

Có một vài thư viện người ta có thể sử dụng (Cũng có thể sử dụng cho các bộ dữ liệu 3D).

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

Người ta cũng có thể tìm thấy các ấn phẩm tương ứng cho các thư viện này để hiểu các thuật toán chi tiết hơn.

Cái cuối cùng có ít phụ thuộc nhất và độc lập và có thể đọc trong các tệp .obj.

Lời chúc tốt đẹp nhất, Stephan


0

Tôi sử dụng hình học đơn giản: vectơ và / hoặc lượng giác

  1. Ở mỗi góc tìm vectơ giữa và góc giữa. Vectơ giữa là trung bình số học của hai vectơ đơn vị được xác định bởi các cạnh của góc. Góc giữa là một nửa góc được xác định bởi các cạnh.

  2. Nếu bạn cần mở rộng (hoặc ký hợp đồng) đa giác của mình theo số lượng d từ mỗi cạnh; bạn nên đi ra ngoài (bằng) với số lượng d / sin (midAngle) để có được điểm góc mới.

  3. Lặp lại điều này cho tất cả các góc

*** Hãy cẩn thận về hướng của bạn. Thực hiện Kiểm tra CounterClockWise bằng ba điểm xác định góc; để tìm ra cách nào là ra, hoặc trong.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.