Làm thế nào để xác định nếu một danh sách các điểm đa giác theo thứ tự chiều kim đồng hồ?


260

Có một danh sách các điểm, làm thế nào để tôi tìm thấy nếu chúng theo thứ tự theo chiều kim đồng hồ?

Ví dụ:

point[0] = (5,0)
point[1] = (6,4)
point[2] = (4,5)
point[3] = (1,5)
point[4] = (1,0)

sẽ nói rằng nó ngược chiều kim đồng hồ (hoặc ngược chiều kim đồng hồ, đối với một số người).


5
XIN LƯU Ý: Câu trả lời được chấp nhận và nhiều câu trả lời sau đó, yêu cầu rất nhiều phép cộng và phép nhân (chúng dựa trên các tính toán diện tích kết thúc âm hoặc dương; ví dụ: "công thức dây giày"). Trước khi thực hiện một trong những điều đó, hãy xem xét câu trả lời của lhf , đơn giản hơn / nhanh hơn - dựa trên wiki - định hướng của đa giác đơn giản .
ToolmakerSteve

Tôi luôn nghĩ về nó theo sản phẩm chéo của hai vectơ liền kề. Nếu tôi đi bộ quanh chu vi của đa giác, đầu tôi chỉ ra khỏi mặt phẳng. Tôi băng qua vectơ ngoài mặt phẳng vào vectơ chỉ đường đi bộ của tôi để có hướng thứ ba trong hệ tọa độ của tôi. Nếu vectơ đó chỉ điểm sao cho phần bên trong nằm bên trái tôi thì ngược chiều kim đồng hồ; nếu nội thất bên phải tôi thì theo chiều kim đồng hồ.
duffymo

Câu trả lời:


416

Một số phương pháp được đề xuất sẽ thất bại trong trường hợp đa giác không lồi, chẳng hạn như hình lưỡi liềm. Đây là một đơn giản sẽ hoạt động với đa giác không lồi (thậm chí nó sẽ hoạt động với đa giác tự giao nhau như hình số tám, cho bạn biết liệu nó có chủ yếu theo chiều kim đồng hồ không).

Tổng các cạnh, (x 2 - x 1 ) (y 2 + y 1 ). Nếu kết quả là dương thì đường cong theo chiều kim đồng hồ, nếu nó âm thì đường cong ngược chiều kim đồng hồ. (Kết quả là hai lần diện tích kèm theo, với quy ước +/-.)

point[0] = (5,0)   edge[0]: (6-5)(4+0) =   4
point[1] = (6,4)   edge[1]: (4-6)(5+4) = -18
point[2] = (4,5)   edge[2]: (1-4)(5+5) = -30
point[3] = (1,5)   edge[3]: (1-1)(0+5) =   0
point[4] = (1,0)   edge[4]: (5-1)(0+0) =   0
                                         ---
                                         -44  counter-clockwise

28
Đó là tính toán áp dụng cho một trường hợp đơn giản. (Tôi không có kỹ năng đăng đồ họa.) Vùng bên dưới một đoạn thẳng bằng chiều cao trung bình của nó (y2 + y1) / 2 lần chiều dài ngang của nó (x2 - x1). Lưu ý quy ước dấu trong x. Hãy thử điều này với một số hình tam giác và bạn sẽ sớm thấy nó hoạt động như thế nào.
Beta

72
Một cảnh báo nhỏ: câu trả lời này giả định một hệ tọa độ Descartes bình thường. Lý do đáng nói đến là một số bối cảnh phổ biến, như khung vẽ HTML5, sử dụng trục Y ngược. Sau đó, quy tắc phải được lật: nếu khu vực là âm , đường cong là chiều kim đồng hồ.
LarsH

8
@ Mr.Qbs: Vì vậy, phương pháp của tôi hoạt động, nhưng nếu bạn bỏ qua một phần quan trọng , thì nó không hoạt động. Đây không phải là tin tức.
Beta

11
@ Mr.Qbs: Bạn luôn phải liên kết điểm cuối cùng với điểm đầu tiên. Nếu bạn có N điểm được đánh số từ 0 đến N-1, thì bạn phải tính toán: Sum( (x[(i+1) mod N] - x[i]) * (y[i] + y[(i+1) mod N]) )for i = 0 đến N-1. Tức là, phải lấy chỉ số Modulo N ( N ≡ 0) Công thức chỉ hoạt động đối với đa giác đóng . Đa giác không có cạnh tưởng tượng.
Olivier Jacot-Descombes

4
Đây blog.element84.com/polygon-winding.html giải thích đơn giản tiếng anh tại sao giải pháp này hoạt động.
David Zorychta

49

Các sản phẩm chéo đo lường mức độ vuông góc-Ness của hai vectơ. Hãy tưởng tượng rằng mỗi cạnh của đa giác của bạn là một vectơ trong mặt phẳng xy của không gian xyz ba chiều (3-D). Khi đó tích chéo của hai cạnh liên tiếp là một vectơ theo hướng z, (hướng z dương nếu đoạn thứ hai theo chiều kim đồng hồ, trừ hướng z nếu ngược chiều kim đồng hồ). Độ lớn của vectơ này tỷ lệ với sin của góc giữa hai cạnh ban đầu, do đó nó đạt cực đại khi chúng vuông góc và tắt đi khi biến mất các cạnh (song song).

Vì vậy, với mỗi đỉnh (điểm) của đa giác, hãy tính độ lớn sản phẩm chéo của hai cạnh liền kề:

Using your data:
point[0] = (5, 0)
point[1] = (6, 4)
point[2] = (4, 5)
point[3] = (1, 5)
point[4] = (1, 0)

Vì vậy, Dán nhãn các cạnh liên tiếp
edgeAlà phân đoạn từ point0đến point1
edgeBgiữa point1đến point2
...
edgeEnằm giữa point4point0.

Sau đó, Vertex A ( point0) nằm giữa
edgeE[Từ point4đến point0]
edgeA[Từ point0đến `point1 '

Hai cạnh này là các vectơ, có thể xác định tọa độ x và y bằng cách trừ đi tọa độ của điểm bắt đầu và điểm kết thúc của chúng:

edgeE= point0- point4= (1, 0) - (5, 0)= (-4, 0)
edgeA= point1- point0= (6, 4) - (1, 0)= (5, 4)

Và sản phẩm chéo của hai cạnh liền kề được tính bằng cách sử dụng yếu tố quyết định của ma trận sau đây, được xây dựng bằng cách đặt tọa độ của hai vectơ bên dưới biểu trưng đại diện cho ba phối hợp trục ( i, j, & k). Tọa độ thứ ba (không) có giá trị là có bởi vì khái niệm sản phẩm chéo là cấu trúc 3-D, và vì vậy chúng tôi mở rộng các vectơ 2-D này thành 3-D để áp dụng sản phẩm chéo:

 i    j    k 
-4    0    0
 1    4    0    

Cho rằng tất cả các sản phẩm chéo tạo ra một vectơ vuông góc với mặt phẳng của hai vectơ được nhân, định thức của ma trận trên chỉ có thành phần k(hoặc trục z).
Công thức tính độ lớn của thành phần khoặc trục z là
a1*b2 - a2*b1 = -4* 4 - 0* 1 = -16

Độ lớn của giá trị này ( -16), là số đo sin của góc giữa 2 vectơ gốc, nhân với tích của độ lớn của 2 vectơ.
Trên thực tế, một công thức khác cho giá trị của nó là
A X B (Cross Product) = |A| * |B| * sin(AB).

Vì vậy, để quay lại chỉ một thước đo góc, bạn cần chia giá trị này, ( -16), bằng tích của độ lớn của hai vectơ.

|A| * |B| = 4 * Sqrt(17) = =16.4924...

Vậy số đo của sin (AB) = -16 / 16.4924=-.97014...

Đây là thước đo xem phân đoạn tiếp theo sau đỉnh đã uốn cong sang trái hay phải và bao nhiêu. Không cần phải lấy sin-sin. Tất cả chúng ta sẽ quan tâm là độ lớn của nó, và tất nhiên dấu hiệu của nó (tích cực hoặc tiêu cực)!

Làm điều này cho mỗi 4 điểm khác xung quanh đường dẫn kín và cộng các giá trị từ phép tính này ở mỗi đỉnh ..

Nếu tổng cuối cùng là dương, bạn đã đi theo chiều kim đồng hồ, âm, ngược chiều kim đồng hồ.


3
Trên thực tế, giải pháp này là một giải pháp khác với giải pháp được chấp nhận. Chúng có tương đương hay không là câu hỏi tôi đang tìm hiểu, nhưng tôi nghi ngờ chúng không phải ... Câu trả lời được chấp nhận sẽ tính diện tích của đa giác, bằng cách lấy sự khác biệt giữa khu vực dưới cạnh trên của đa giác và khu vực bên dưới cạnh dưới của đa giác. Một cái sẽ là âm (cái mà bạn đi qua từ trái sang phải), và cái kia sẽ âm. Khi di chuyển ngang theo chiều kim đồng hồ, cạnh trên được dịch chuyển từ trái sang phải và lớn hơn, do đó tổng số là dương.
Charles Bretana

1
Giải pháp của tôi đo lường tổng số các thay đổi về góc cạnh ở mỗi đỉnh. Điều này sẽ là tích cực khi đi qua chiều kim đồng hồ và tiêu cực khi đi ngược chiều kim đồng hồ.
Charles Bretana

2
Dường như với cách tiếp cận này, bạn KHÔNG cần phải sử dụng arcsin, trừ khi bạn giả sử độ lồi (trong trường hợp đó bạn chỉ cần kiểm tra một đỉnh)
agentp

2
Bạn cần phải lấy arcsin. Hãy thử nó trên một loạt các đa giác không lồi ngẫu nhiên, và bạn sẽ thấy thử nghiệm sẽ thất bại đối với một số đa giác nếu bạn không dùng arcsin.
Luke Hutchison

1
@CharlesBretana - trong khi tôi chưa chạy thử nghiệm của Luke, tôi tin rằng anh ấy đúng. Đó là bản chất của tổng kết hợp với thang đo phi tuyến [không có arcsin so với arcsin]. Xem xét những gì marsbear đề nghị, mà bạn từ chối chính xác. Ông đề nghị bạn "chỉ cần đếm" và bạn chỉ ra rằng một số ít các giá trị lớn có thể lớn hơn một số lượng lớn các giá trị nhỏ. Bây giờ hãy xem xét arcsin của từng giá trị so với không. Có phải vẫn là trường hợp không dùng arcsin mang lại trọng lượng không chính xác cho từng giá trị, do đó có cùng một lỗ hổng (mặc dù ít hơn rất nhiều)?
ToolmakerSteve

47

Tôi đoán đây là một câu hỏi khá cũ, nhưng dù sao tôi cũng sẽ đưa ra một giải pháp khác, bởi vì nó đơn giản và không chuyên sâu về mặt toán học - nó chỉ sử dụng đại số cơ bản. Tính diện tích đã ký của đa giác. Nếu nó âm thì các điểm theo thứ tự theo chiều kim đồng hồ, nếu nó dương thì chúng ngược chiều kim đồng hồ. (Điều này rất giống với giải pháp của Beta.)

Tính diện tích đã ký: A = 1/2 * (x 1 * y 2 - x 2 * y 1 + x 2 * y 3 - x 3 * y 2 + ... + x n * y 1 - x 1 * y n )

Hoặc trong mã giả:

signedArea = 0
for each point in points:
    x1 = point[0]
    y1 = point[1]
    if point is last point
        x2 = firstPoint[0]
        y2 = firstPoint[1]
    else
        x2 = nextPoint[0]
        y2 = nextPoint[1]
    end if

    signedArea += (x1 * y2 - x2 * y1)
end for
return signedArea / 2

Lưu ý rằng nếu bạn chỉ kiểm tra đơn đặt hàng, bạn không cần phải chia cho 2.

Nguồn: http://mathworld.wolfram.com/PolygonArea.html


Đó có phải là một lỗi đánh máy trong công thức khu vực đã ký của bạn ở trên không? Nó kết thúc bằng "xn * y1 - x1 * yn"; khi tôi tin rằng nó phải là "x_n y_ {n + 1} - y_n x_ {n-1}" (ít nhất là trong LaTeX). Mặt khác, đã mười năm kể từ khi tôi tham gia bất kỳ lớp đại số tuyến tính nào.
Michael Eric Oberlin

Không. Nếu bạn kiểm tra nguồn , bạn sẽ thấy rằng công thức thực tế tham chiếu lại điểm đầu tiên trong thuật ngữ cuối cùng (y1 và x1). (Xin lỗi, tôi không quen thuộc lắm với LaTeX, nhưng tôi đã định dạng các mục đăng ký để làm cho chúng dễ đọc hơn.)
Sean the Bean

Tôi đã sử dụng giải pháp này và nó hoạt động hoàn hảo cho việc sử dụng của tôi. Lưu ý rằng nếu bạn có thể lập kế hoạch trước và dự phòng và thêm hai vectơ trong mảng của mình, bạn có thể thoát khỏi so sánh (hoặc%) bằng cách thêm vectơ đầu tiên ở đuôi của mảng. Bằng cách đó, bạn chỉ cần lặp qua tất cả các phần tử, ngoại trừ phần tử cuối cùng (chiều dài-2 thay vì chiều dài-1).
Eric Fortier

2
@EricFortier - FWIW, thay vì thay đổi kích thước một mảng lớn có thể, một sự thay thế hiệu quả là cho mỗi lần lặp để lưu điểm của nó như previousPointcho lần lặp tiếp theo. Trước khi bắt đầu vòng lặp, hãy đặt thành previousPointđiểm cuối cùng của mảng. Trao đổi là thêm bản sao biến cục bộ nhưng truy cập mảng ít hơn. Và quan trọng nhất, không phải chạm vào mảng đầu vào.
ToolmakerSteve

2
@MichaelEricOberlin - cần thiết để đóng đa giác, bằng cách bao gồm đoạn đường từ điểm cuối đến điểm đầu tiên. (Một phép tính chính xác sẽ giống nhau, bất kể điểm nào bắt đầu đa giác đóng.)
ToolmakerSteve

38

Tìm đỉnh có y nhỏ nhất (và x lớn nhất nếu có quan hệ). Đặt đỉnh là Avà đỉnh trước trong danh sách Bvà đỉnh tiếp theo trong danh sách là C. Bây giờ hãy tính dấu của sản phẩm chéo của ABAC.


Người giới thiệu:


7
Điều này cũng được giải thích trong en.wikipedia.org/wiki/Curve_orientation . Vấn đề là điểm tìm thấy phải nằm trên thân lồi và chỉ cần nhìn cục bộ vào một điểm duy nhất trên thân lồi (và các lân cận trực tiếp của nó) để xác định hướng của toàn bộ đa giác.
M Katz

1
Sốc và nhận ra điều này đã không nhận được nhiều sự ủng hộ hơn. Đối với đa giác đơn giản ( hầu hết là đa giác trong một số lĩnh vực ), câu trả lời này mang lại một O(1)giải pháp. Tất cả các câu trả lời khác mang lại O(n)giải pháp cho nsố lượng điểm đa giác. Để biết tối ưu hóa sâu hơn nữa, hãy xem phần phụ Xem xét thực tế của bài viết định hướng Đường cong tuyệt vời của Wikipedia .
Cecil Curry

8
Làm rõ: giải pháp nàyO(1)chỉ khi (A) đa giác này lồi (trong trường hợp đó bất kỳ đỉnh tùy ý nào nằm trên thân lồi và do đó đủ) hoặc (B) bạn đã biết đỉnh có tọa độ Y nhỏ nhất. Nếu đây không phải là trường hợp (nghĩa là đa giác này không phải là lồi và bạn không biết gì về nó),O(n)thì cần phải tìm kiếm. Vì không cần tổng kết, tuy nhiên, điều này vẫn nhanh hơn đáng kể so với bất kỳ giải pháp nào khác cho đa giác đơn giản.
Cecil Curry


1
@CecilCurry Tôi nghĩ rằng bình luận thứ 2 của bạn giải thích lý do tại sao điều này không nhận được nhiều sự ủng hộ hơn. Nó mang lại câu trả lời sai trong các kịch bản nhất định, mà không đề cập đến những hạn chế đó.
LarsH

24

Dưới đây là một triển khai C # đơn giản của thuật toán dựa trên câu trả lời này .

Giả sử rằng chúng ta có một Vectorloại có XYthuộc tính của loại double.

public bool IsClockwise(IList<Vector> vertices)
{
    double sum = 0.0;
    for (int i = 0; i < vertices.Count; i++) {
        Vector v1 = vertices[i];
        Vector v2 = vertices[(i + 1) % vertices.Count];
        sum += (v2.X - v1.X) * (v2.Y + v1.Y);
    }
    return sum > 0.0;
}

%là toán tử modulo hoặc phần còn lại thực hiện thao tác modulo mà ( theo Wikipedia ) tìm thấy phần còn lại sau khi chia một số cho một số khác.


6

Bắt đầu tại một trong các đỉnh và tính toán góc phụ của mỗi bên.

Đầu tiên và cuối cùng sẽ bằng không (vì vậy bỏ qua những cái đó); đối với phần còn lại, sin của góc sẽ được cho bởi tích chéo của các chuẩn hóa thành đơn vị chiều dài của (điểm [n] -point [0]) và (điểm [n-1] -point [0]).

Nếu tổng các giá trị là dương, thì đa giác của bạn được vẽ theo nghĩa ngược chiều kim đồng hồ.


Xem như cách mà sản phẩm chéo về cơ bản đạt được một hệ số tỷ lệ dương nhân với sin của góc, có lẽ tốt hơn là chỉ làm một sản phẩm chéo. Nó sẽ nhanh hơn và ít phức tạp hơn.
ReaperUnreal

4

Để biết giá trị của nó, tôi đã sử dụng mixin này để tính thứ tự quanh co cho các ứng dụng Google Maps API v3.

Mã này tận dụng hiệu ứng phụ của các khu vực đa giác: thứ tự các cuộn dây theo chiều kim đồng hồ mang lại một vùng dương, trong khi thứ tự cuộn ngược chiều kim đồng hồ của cùng một đỉnh tạo ra cùng một diện tích với giá trị âm. Mã này cũng sử dụng một loại API riêng trong thư viện hình học Google Maps. Tôi cảm thấy thoải mái khi sử dụng nó - sử dụng có nguy cơ của riêng bạn.

Sử dụng mẫu:

var myPolygon = new google.maps.Polygon({/*options*/});
var isCW = myPolygon.isPathClockwise();

Ví dụ đầy đủ với các bài kiểm tra đơn vị @ http://jsfiddle.net/stevejansen/bq2ec/

/** Mixin to extend the behavior of the Google Maps JS API Polygon type
 *  to determine if a polygon path has clockwise of counter-clockwise winding order.
 *  
 *  Tested against v3.14 of the GMaps API.
 *
 *  @author  stevejansen_github@icloud.com
 *
 *  @license http://opensource.org/licenses/MIT
 *
 *  @version 1.0
 *
 *  @mixin
 *  
 *  @param {(number|Array|google.maps.MVCArray)} [path] - an optional polygon path; defaults to the first path of the polygon
 *  @returns {boolean} true if the path is clockwise; false if the path is counter-clockwise
 */
(function() {
  var category = 'google.maps.Polygon.isPathClockwise';
     // check that the GMaps API was already loaded
  if (null == google || null == google.maps || null == google.maps.Polygon) {
    console.error(category, 'Google Maps API not found');
    return;
  }
  if (typeof(google.maps.geometry.spherical.computeArea) !== 'function') {
    console.error(category, 'Google Maps geometry library not found');
    return;
  }

  if (typeof(google.maps.geometry.spherical.computeSignedArea) !== 'function') {
    console.error(category, 'Google Maps geometry library private function computeSignedArea() is missing; this may break this mixin');
  }

  function isPathClockwise(path) {
    var self = this,
        isCounterClockwise;

    if (null === path)
      throw new Error('Path is optional, but cannot be null');

    // default to the first path
    if (arguments.length === 0)
        path = self.getPath();

    // support for passing an index number to a path
    if (typeof(path) === 'number')
        path = self.getPaths().getAt(path);

    if (!path instanceof Array && !path instanceof google.maps.MVCArray)
      throw new Error('Path must be an Array or MVCArray');

    // negative polygon areas have counter-clockwise paths
    isCounterClockwise = (google.maps.geometry.spherical.computeSignedArea(path) < 0);

    return (!isCounterClockwise);
  }

  if (typeof(google.maps.Polygon.prototype.isPathClockwise) !== 'function') {
    google.maps.Polygon.prototype.isPathClockwise = isPathClockwise;
  }
})();

Thử điều này tôi nhận được kết quả ngược lại, một đa giác được vẽ theo chiều kim đồng hồ mang lại một vùng âm, trong khi một hình vẽ ngược chiều kim đồng hồ mang lại kết quả dương. Trong cả hai trường hợp, đoạn trích này vẫn siêu hữu ích 5 năm, cảm ơn bạn.
Cameron Roberts

@CameronRoberts Định mức (xem IETF nói riêng cho GeoJson) là tuân theo 'quy tắc bàn tay phải'. Tôi đoán rằng Google đang phàn nàn với. Trong trường hợp đó, vòng ngoài phải ngược chiều kim đồng hồ (cho diện tích dương) và các vòng trong (lỗ) được cuộn theo chiều kim đồng hồ (khu vực âm được loại bỏ khỏi khu vực chính).
allez

4

Việc triển khai câu trả lời của Sean trong JavaScript:

function calcArea(poly) {
    if(!poly || poly.length < 3) return null;
    let end = poly.length - 1;
    let sum = poly[end][0]*poly[0][1] - poly[0][0]*poly[end][1];
    for(let i=0; i<end; ++i) {
        const n=i+1;
        sum += poly[i][0]*poly[n][1] - poly[n][0]*poly[i][1];
    }
    return sum;
}

function isClockwise(poly) {
    return calcArea(poly) > 0;
}

let poly = [[352,168],[305,208],[312,256],[366,287],[434,248],[416,186]];

console.log(isClockwise(poly));

let poly2 = [[618,186],[650,170],[701,179],[716,207],[708,247],[666,259],[637,246],[615,219]];

console.log(isClockwise(poly2));

Khá chắc chắn rằng điều này là đúng. Nó dường như được làm việc :-)

Những đa giác đó trông như thế này, nếu bạn đang tự hỏi:


3

Đây là chức năng được triển khai cho OpenLayers 2 . Điều kiện để có một đa giác theo chiều kim đồng hồ là area < 0, nó được xác nhận bởi tham chiếu này .

function IsClockwise(feature)
{
    if(feature.geometry == null)
        return -1;

    var vertices = feature.geometry.getVertices();
    var area = 0;

    for (var i = 0; i < (vertices.length); i++) {
        j = (i + 1) % vertices.length;

        area += vertices[i].x * vertices[j].y;
        area -= vertices[j].x * vertices[i].y;
        // console.log(area);
    }

    return (area < 0);
}

Openlayers là thư viện quản lý bản đồ dựa trên javascript như googlemaps và nó đã được viết và sử dụng trong openlayers 2.
MSS

Bạn có thể giải thích một chút về mã của bạn làm gì và tại sao bạn lại làm như vậy không?
nbro

@nbro mã này thực hiện câu trả lời lhf . Thật dễ dàng để giữ phần không OpenLayer trong một hàm javascript thuần túy bằng cách có các đỉnh trực tiếp làm tham số. Nó hoạt động tốt và có thể được điều chỉnh phù hợp với trường hợp của multiPolygon .
allez

2

Nếu bạn sử dụng Matlab, hàm ispolycwsẽ trả về true nếu các đỉnh đa giác theo thứ tự theo chiều kim đồng hồ.


1

Cũng như giải thích trong bài viết này Wikipedia định hướng đường cong , cho 3 điểm p, qrtrên máy bay (ví dụ với tọa độ x và y), bạn có thể tính toán dấu hiệu của yếu tố quyết định sau

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

Nếu định thức là âm (nghĩa là Orient(p, q, r) < 0), thì đa giác được định hướng theo chiều kim đồng hồ (CW). Nếu định thức là dương (nghĩa là Orient(p, q, r) > 0), đa giác được định hướng ngược chiều kim đồng hồ (CCW). Yếu tố quyết định là zero (ví dụ Orient(p, q, r) == 0) nếu điểm p, qrthẳng hàng .

Trong công thức trên, chúng ta thêm vào trước những người ở phía trước của tọa độ p, qrbởi vì chúng ta đang sử dụng tọa độ đồng nhất .


@tibetty Bạn có thể giải thích tại sao phương pháp này sẽ không hoạt động trong nhiều tình huống nếu đa giác bị lõm?
nbro

1
Xin vui lòng nhìn vào bảng cuối cùng trong tài liệu tham khảo wiki trong bài viết của bạn. Thật dễ dàng để tôi đưa ra một ví dụ sai nhưng khó để chứng minh điều đó.
tibetty

1
Xin vui lòng nhìn vào bảng cuối cùng trong tài liệu tham khảo wiki trong bài viết của bạn. Thật dễ dàng để tôi đưa ra một ví dụ sai nhưng khó để chứng minh điều đó.
tibetty

1
@tibetty đúng. Bạn không thể chỉ mất ba điểm dọc theo đa giác; bạn có thể ở trong vùng lồi hoặc lõm của đa giác đó. Đọc wiki cẩn thận, người ta phải mất ba điểm dọc theo thân lồi bao quanh đa giác . Từ "những cân nhắc thực tế": "Người ta không cần xây dựng vỏ lồi của một đa giác để tìm một đỉnh phù hợp. Một lựa chọn phổ biến là đỉnh của đa giác có tọa độ X nhỏ nhất. Nếu có một vài trong số chúng, thì một với tọa độ Y nhỏ nhất được chọn. Nó được đảm bảo là [a] đỉnh của thân lồi của đa giác. "
ToolmakerSteve

1
Do đó , câu trả lời trước đó của lhf , tương tự và tham chiếu cùng một bài viết wiki, nhưng chỉ định một điểm như vậy. [Rõ ràng không quan trọng việc người ta lấy nhỏ nhất hay lớn nhất, x hay y, miễn là người đó tránh được ở giữa; thực sự người ta đang làm việc từ một cạnh của khung bao quanh đa giác, để đảm bảo trong một vùng lõm.]
ToolmakerSteve

0

Tôi nghĩ rằng để một số điểm được đưa ra theo chiều kim đồng hồ, tất cả các cạnh cần phải dương chứ không chỉ tổng các cạnh. Nếu một cạnh âm hơn ít nhất 3 điểm được đưa ra ngược chiều kim đồng hồ.


Đúng, nhưng bạn hiểu sai khái niệm trật tự quanh co của đa giác (theo chiều kim đồng hồ hoặc ngược chiều kim đồng hồ). Trong một đa giác lồi hoàn toàn, góc tại tất cả các điểm sẽ theo chiều kim đồng hồ hoặc tất cả sẽ ngược chiều kim đồng hồ [như trong câu đầu tiên của bạn]. Trong một đa giác có (các) vùng lõm, "hang động" sẽ ở hướng ngược lại, nhưng toàn bộ đa giác vẫn có phần bên trong được xác định rõ và được xem xét theo chiều kim đồng hồ hoặc ngược chiều kim đồng hồ. Xem en.wikipedia.org/wiki/ Kẻ
ToolmakerSteve

0

Giải pháp C # / LINQ của tôi dựa trên lời khuyên về sản phẩm chéo của @charlesbretana bên dưới. Bạn có thể chỉ định một tài liệu tham khảo bình thường cho cuộn dây. Nó sẽ hoạt động miễn là đường cong chủ yếu nằm trong mặt phẳng được xác định bởi vectơ lên.

using System.Collections.Generic;
using System.Linq;
using System.Numerics;

namespace SolidworksAddinFramework.Geometry
{
    public static class PlanePolygon
    {
        /// <summary>
        /// Assumes that polygon is closed, ie first and last points are the same
        /// </summary>
       public static bool Orientation
           (this IEnumerable<Vector3> polygon, Vector3 up)
        {
            var sum = polygon
                .Buffer(2, 1) // from Interactive Extensions Nuget Pkg
                .Where(b => b.Count == 2)
                .Aggregate
                  ( Vector3.Zero
                  , (p, b) => p + Vector3.Cross(b[0], b[1])
                                  /b[0].Length()/b[1].Length());

            return Vector3.Dot(up, sum) > 0;

        } 

    }
}

với một bài kiểm tra đơn vị

namespace SolidworksAddinFramework.Spec.Geometry
{
    public class PlanePolygonSpec
    {
        [Fact]
        public void OrientationShouldWork()
        {

            var points = Sequences.LinSpace(0, Math.PI*2, 100)
                .Select(t => new Vector3((float) Math.Cos(t), (float) Math.Sin(t), 0))
                .ToList();

            points.Orientation(Vector3.UnitZ).Should().BeTrue();
            points.Reverse();
            points.Orientation(Vector3.UnitZ).Should().BeFalse();



        } 
    }
}

0

Đây là giải pháp của tôi bằng cách sử dụng các giải thích trong các câu trả lời khác:

def segments(poly):
    """A sequence of (x,y) numeric coordinates pairs """
    return zip(poly, poly[1:] + [poly[0]])

def check_clockwise(poly):
    clockwise = False
    if (sum(x0*y1 - x1*y0 for ((x0, y0), (x1, y1)) in segments(poly))) < 0:
        clockwise = not clockwise
    return clockwise

poly = [(2,2),(6,2),(6,6),(2,6)]
check_clockwise(poly)
False

poly = [(2, 6), (6, 6), (6, 2), (2, 2)]
check_clockwise(poly)
True

1
Bạn có thể chỉ định câu trả lời khác chính xác câu trả lời này dựa trên?
nbro

0

Một phương pháp tính toán đơn giản hơn nhiều, nếu bạn đã biết một điểm bên trong đa giác :

  1. Chọn bất kỳ phân đoạn dòng nào từ đa giác ban đầu, các điểm và tọa độ của chúng theo thứ tự đó.

  2. Thêm một điểm "bên trong" đã biết và tạo thành một hình tam giác.

  3. Tính CW hoặc CCW theo đề xuất ở đây với ba điểm đó.


Có thể điều này hoạt động nếu đa giác hoàn toàn lồi. Nó chắc chắn không đáng tin cậy nếu có bất kỳ vùng lõm nào - thật dễ dàng để chọn một điểm nằm ở phía "sai" của một trong các cạnh của hang động, sau đó kết nối nó với cạnh đó. Sẽ nhận được câu trả lời sai.
ToolmakerSteve

Nó hoạt động ngay cả khi đa giác là lõm. Điểm cần phải nằm trong đa giác lõm đó. Tuy nhiên tôi không chắc chắn về đa giác phức tạp (không kiểm tra.)
Venkata Goli

"Nó hoạt động ngay cả khi đa giác là lõm." - Mẫu thử: poly (0,0), (1,1), (0,2), (2,2), (2,0). Đoạn đường (1,1), (0, 2). Nếu bạn chọn một điểm bên trong trong (1,1), (0,2), (1,2) để tạo thành tam giác -> (1,1), (0,2), (0,5,1,5)), bạn sẽ nhận được ngược lại so với nếu bạn chọn một điểm bên trong trong (0,0), (1,1), (1,0)> (1,1), (0,2), (0,5,0,5). Cả hai đều là nội thất của đa giác ban đầu, nhưng có cuộn dây ngược lại. Do đó, một trong số họ đưa ra câu trả lời sai.
ToolmakerSteve

Nói chung, nếu đa giác có bất kỳ vùng lõm nào, hãy chọn một đoạn trong vùng lõm. Vì nó là lõm, bạn có thể tìm thấy hai điểm "bên trong" nằm ở hai phía đối diện của đường thẳng đó. Bởi vì chúng ở hai phía đối diện của đường thẳng đó, các hình tam giác được hình thành có cuộn dây ngược nhau. Kết thúc bằng chứng.
ToolmakerSteve

0

Sau khi thử nghiệm một số triển khai không đáng tin cậy, thuật toán cung cấp kết quả thỏa đáng liên quan đến định hướng CW / CCW ra khỏi hộp là cái được đăng bởi OP trong luồng này ( shoelace_formula_3).

Như mọi khi, số dương biểu thị hướng CW, trong khi số âm CCW.


0

Đây là giải pháp swift 3.0 dựa trên các câu trả lời ở trên:

    for (i, point) in allPoints.enumerated() {
        let nextPoint = i == allPoints.count - 1 ? allPoints[0] : allPoints[i+1]
        signedArea += (point.x * nextPoint.y - nextPoint.x * point.y)
    }

    let clockwise  = signedArea < 0

0

Một giải pháp khác cho việc này;

const isClockwise = (vertices=[]) => {
    const len = vertices.length;
    const sum = vertices.map(({x, y}, index) => {
        let nextIndex = index + 1;
        if (nextIndex === len) nextIndex = 0;

        return {
            x1: x,
            x2: vertices[nextIndex].x,
            y1: x,
            y2: vertices[nextIndex].x
        }
    }).map(({ x1, x2, y1, y2}) => ((x2 - x1) * (y1 + y2))).reduce((a, b) => a + b);

    if (sum > -1) return true;
    if (sum < 0) return false;
}

Lấy tất cả các đỉnh như một mảng như thế này;

const vertices = [{x: 5, y: 0}, {x: 6, y: 4}, {x: 4, y: 5}, {x: 1, y: 5}, {x: 1, y: 0}];
isClockwise(vertices);

0

Giải pháp cho R để xác định hướng và đảo ngược nếu theo chiều kim đồng hồ (thấy cần thiết cho các đối tượng owin):

coords <- cbind(x = c(5,6,4,1,1),y = c(0,4,5,5,0))
a <- numeric()
for (i in 1:dim(coords)[1]){
  #print(i)
  q <- i + 1
  if (i == (dim(coords)[1])) q <- 1
  out <- ((coords[q,1]) - (coords[i,1])) * ((coords[q,2]) + (coords[i,2]))
  a[q] <- out
  rm(q,out)
} #end i loop

rm(i)

a <- sum(a) #-ve is anti-clockwise

b <- cbind(x = rev(coords[,1]), y = rev(coords[,2]))

if (a>0) coords <- b #reverses coords if polygon not traced in anti-clockwise direction

0

Trong khi những câu trả lời này là chính xác, chúng có cường độ toán học cao hơn mức cần thiết. Giả sử tọa độ bản đồ, trong đó điểm cực bắc là điểm cao nhất trên bản đồ. Tìm điểm cực bắc nhất và nếu 2 điểm hòa nhau, đó là điểm cực bắc nhất thì hướng đông nhất (đây là điểm mà lhf sử dụng trong câu trả lời của mình). Theo quan điểm của bạn,

điểm [0] = (5,0)

điểm [1] = (6,4)

điểm [2] = (4,5)

điểm [3] = (1,5)

điểm [4] = (1,0)

Nếu chúng ta giả sử rằng P2 là điểm cực bắc thì điểm phía đông hoặc điểm trước hoặc điểm tiếp theo xác định theo chiều kim đồng hồ, CW hoặc CCW. Vì điểm cực bắc nằm ở mặt phía bắc, nếu P1 (trước đó) đến P2 di chuyển về hướng đông là CW. Trong trường hợp này, nó di chuyển về phía tây, vì vậy hướng là CCW như câu trả lời được chấp nhận nói. Nếu điểm trước không có chuyển động ngang, thì cùng một hệ thống áp dụng cho điểm tiếp theo, P3. Nếu P3 ở phía tây P2, thì chuyển động là CCW. Nếu chuyển động P2 đến P3 ở hướng đông, thì trong trường hợp này, hướng tây là CW. Giả sử nte, P2 trong dữ liệu của bạn, là điểm cực bắc nhất phía đông và prv là điểm trước đó, P1 trong dữ liệu của bạn và nxt là điểm tiếp theo, P3 trong dữ liệu của bạn và [0] là ngang hoặc hướng đông / phía tây nơi phía tây ít hơn phía đông và [1] là dọc.

if (nte[0] >= prv[0] && nxt[0] >= nte[0]) return(CW);
if (nte[0] <= prv[0] && nxt[0] <= nte[0]) return(CCW);
// Okay, it's not easy-peasy, so now, do the math
if (nte[0] * nxt[1] - nte[1] * nxt[0] - prv[0] * (nxt[1] - crt[1]) + prv[1] * (nxt[0] - nte[0]) >= 0) return(CCW); // For quadrant 3 return(CW)
return(CW) // For quadrant 3 return (CCW)

IMHO, sẽ an toàn hơn khi bám vào toán học cơ bản thể hiện trong câu trả lời của lhf - cảm ơn bạn đã đề cập đến anh ấy. Thách thức trong việc giảm nó xuống góc phần tư là một số lượng công việc hợp lý để chứng minh rằng công thức của bạn là chính xác trong mọi trường hợp. Bạn đã tính toán chính xác "nhiều tây" chưa? Trong một đa giác lõm trong đó cả [1] và [3] là "tây và nam" của [2]? Bạn đã xử lý chính xác các độ dài khác nhau của [1] và [3] trong tình huống đó chưa? Tôi không biết, trong khi nếu tôi tính trực tiếp góc đó (hoặc định thức của nó), tôi đang sử dụng các công thức nổi tiếng.
ToolmakerSteve 5/2/19

@ToolmakerSteve các câu lệnh if luôn hoạt động nếu 3 điểm lồi. Các câu lệnh if sẽ trả về, sau đó bạn sẽ có câu trả lời đúng. Các câu lệnh if sẽ không trả về, nếu hình dạng lõm và cực trị. Đó là khi bạn phải làm toán. Hầu hết các hình ảnh có một góc phần tư, vì vậy phần đó là dễ dàng. Hơn 99% các cuộc gọi chương trình con của tôi được xử lý bởi các câu lệnh if.
VectorVortec

Điều đó không giải quyết mối quan tâm của tôi. Công thức đó là gì? Đây có phải là yếu tố quyết định định hướng như được đưa ra trong liên kết wiki từ câu trả lời của lhf không? Nếu vậy thì nói như vậy. Giải thích rằng những gì bạn đang làm là thực hiện kiểm tra nhanh xử lý hầu hết các trường hợp, để tránh toán học chuẩn. Nếu đó là như vậy, thì câu trả lời của bạn bây giờ có ý nghĩa với tôi. (Tiểu nit: sẽ dễ dàng hơn để đọc nếu bạn sử dụng .x.ycủa một cấu trúc, thay vì [0][1]tôi không biết những gì mã của bạn đã nói, lần đầu tiên tôi liếc nhìn nó..)
ToolmakerSteve

Vì tôi không tin tưởng vào cách tiếp cận của bạn, tôi đã thực hiện cách tiếp cận của lhf ; công thức từ liên kết của mình. Phần chậm là tìm kiếm đỉnh thích hợp - tìm kiếm O (N). Sau khi tìm thấy, định thức là một phép toán O (1), sử dụng 6 bội số với 5 lần thêm. Phần cuối cùng đó là những gì bạn đã tối ưu hóa; nhưng bạn đã làm như vậy bằng cách thêm các bài kiểm tra if bổ sung. Cá nhân tôi không thể biện minh cho việc thực hiện một cách tiếp cận không chuẩn - sẽ cần xác minh từng bước là chính xác - Nhưng cảm ơn bạn đã phân tích thú vị về góc phần tư!
ToolmakerSteve

0

Mã C # để thực hiện câu trả lời của lhf :

// https://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon
public static WindingOrder DetermineWindingOrder(IList<Vector2> vertices)
{
    int nVerts = vertices.Count;
    // If vertices duplicates first as last to represent closed polygon,
    // skip last.
    Vector2 lastV = vertices[nVerts - 1];
    if (lastV.Equals(vertices[0]))
        nVerts -= 1;
    int iMinVertex = FindCornerVertex(vertices);
    // Orientation matrix:
    //     [ 1  xa  ya ]
    // O = | 1  xb  yb |
    //     [ 1  xc  yc ]
    Vector2 a = vertices[WrapAt(iMinVertex - 1, nVerts)];
    Vector2 b = vertices[iMinVertex];
    Vector2 c = vertices[WrapAt(iMinVertex + 1, nVerts)];
    // determinant(O) = (xb*yc + xa*yb + ya*xc) - (ya*xb + yb*xc + xa*yc)
    double detOrient = (b.X * c.Y + a.X * b.Y + a.Y * c.X) - (a.Y * b.X + b.Y * c.X + a.X * c.Y);

    // TBD: check for "==0", in which case is not defined?
    // Can that happen?  Do we need to check other vertices / eliminate duplicate vertices?
    WindingOrder result = detOrient > 0
            ? WindingOrder.Clockwise
            : WindingOrder.CounterClockwise;
    return result;
}

public enum WindingOrder
{
    Clockwise,
    CounterClockwise
}

// Find vertex along one edge of bounding box.
// In this case, we find smallest y; in case of tie also smallest x.
private static int FindCornerVertex(IList<Vector2> vertices)
{
    int iMinVertex = -1;
    float minY = float.MaxValue;
    float minXAtMinY = float.MaxValue;
    for (int i = 0; i < vertices.Count; i++)
    {
        Vector2 vert = vertices[i];
        float y = vert.Y;
        if (y > minY)
            continue;
        if (y == minY)
            if (vert.X >= minXAtMinY)
                continue;

        // Minimum so far.
        iMinVertex = i;
        minY = y;
        minXAtMinY = vert.X;
    }

    return iMinVertex;
}

// Return value in (0..n-1).
// Works for i in (-n..+infinity).
// If need to allow more negative values, need more complex formula.
private static int WrapAt(int i, int n)
{
    // "+n": Moves (-n..) up to (0..).
    return (i + n) % n;
}

1
Điều này dường như là cho tọa độ Y xuống dương. Lật CW / CCW cho tọa độ chuẩn.
Warwick Allison


-4

tìm trung tâm khối lượng của các điểm này.

giả sử có những dòng từ điểm này đến điểm của bạn.

tìm góc giữa hai dòng cho line0 line1

hơn là làm điều đó cho dòng1 và dòng2

...

...

nếu góc này tăng đơn điệu so với ngược chiều kim đồng hồ,

khác nếu giảm đơn điệu thì theo chiều kim đồng hồ

khác (nó không đơn điệu)

bạn không thể quyết định, vì vậy nó không khôn ngoan


bởi "trung tâm của khối lượng" Tôi nghĩ bạn có nghĩa là "centroid"?
Vicky Chijwani

Có thể hoạt động nếu đa giác hoàn toàn lồi. Nhưng tốt hơn là thay vào đó sử dụng một câu trả lời sẽ hoạt động cho đa giác không lồi.
ToolmakerSteve
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.