มีรายการคะแนนฉันจะค้นหาว่าพวกเขาอยู่ในลำดับตามเข็มนาฬิกาได้อย่างไร
ตัวอย่างเช่น:
point[0] = (5,0)
point[1] = (6,4)
point[2] = (4,5)
point[3] = (1,5)
point[4] = (1,0)
จะบอกว่ามันเป็นทวนเข็มนาฬิกา (หรือทวนเข็มนาฬิกาสำหรับบางคน)
มีรายการคะแนนฉันจะค้นหาว่าพวกเขาอยู่ในลำดับตามเข็มนาฬิกาได้อย่างไร
ตัวอย่างเช่น:
point[0] = (5,0)
point[1] = (6,4)
point[2] = (4,5)
point[3] = (1,5)
point[4] = (1,0)
จะบอกว่ามันเป็นทวนเข็มนาฬิกา (หรือทวนเข็มนาฬิกาสำหรับบางคน)
คำตอบ:
วิธีที่แนะนำบางอย่างจะล้มเหลวในกรณีของรูปหลายเหลี่ยมที่ไม่นูนเช่นรูปเสี้ยว ต่อไปนี้เป็นสิ่งง่าย ๆ ที่จะทำงานกับรูปหลายเหลี่ยมที่ไม่นูน (มันจะทำงานร่วมกับรูปหลายเหลี่ยมที่ตัดกันของตัวเองเหมือนรูปที่แปดโดยบอกคุณว่าส่วนใหญ่เป็นตามเข็มนาฬิกา)
รวมกับขอบ (x 2 - x 1 ) (y 2 + y 1 ) หากผลลัพธ์เป็นค่าบวกเส้นโค้งจะเป็นตามเข็มนาฬิกาหากเป็นลบเส้นโค้งจะหมุนตามเข็มนาฬิกา (ผลลัพธ์จะเป็นสองเท่าของพื้นที่ปิดล้อมด้วยการประชุม +/-)
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
Sum( (x[(i+1) mod N] - x[i]) * (y[i] + y[(i+1) mod N]) )
สำหรับ i = 0 ถึง N-1 คือต้องใช้ดัชนี Modulo N ( N ≡ 0
) สูตรนี้ใช้ได้กับรูปหลายเหลี่ยมปิดเท่านั้น รูปหลายเหลี่ยมไม่มีขอบจินตภาพ
สินค้าข้ามวัดระดับของการตั้งฉาก-Ness สองเวกเตอร์ ลองจินตนาการว่าขอบรูปหลายเหลี่ยมแต่ละอันของคุณเป็นเวกเตอร์ในระนาบ xy ของพื้นที่ xyz แบบสามมิติ (3-D) จากนั้นครอสโปรดัคของขอบต่อเนื่องสองอันนั้นคือเวกเตอร์ในทิศทาง z, (บวก z- ทิศทางถ้าส่วนที่สองเป็นตามเข็มนาฬิกา, ลบด้วยทิศ z ถ้ามันทวนเข็มนาฬิกา) ขนาดของเวกเตอร์นี้เป็นสัดส่วนกับไซน์ของมุมระหว่างขอบดั้งเดิมสองค่าดังนั้นมันจะไปถึงค่าสูงสุดเมื่อมันตั้งฉากและเรียวออกไปจะหายไปเมื่อขอบมี collinear (ขนาน)
ดังนั้นสำหรับแต่ละจุดยอด (จุด) ของรูปหลายเหลี่ยมให้คำนวณขนาดของผลิตภัณฑ์ข้ามของขอบที่อยู่ติดกันสองอัน:
Using your data:
point[0] = (5, 0)
point[1] = (6, 4)
point[2] = (4, 5)
point[3] = (1, 5)
point[4] = (1, 0)
ดังนั้นป้ายขอบติดต่อกันเป็น
edgeA
เป็นส่วนจากpoint0
การpoint1
และ
edgeB
ระหว่างpoint1
ที่จะpoint2
...
edgeE
อยู่ระหว่างและ point4
point0
Vertex A ( point0
) อยู่ระหว่าง
edgeE
[จาก point4
ถึงpoint0
]
edgeA
[จาก point0
ถึง `จุด 1 '
ขอบทั้งสองนี้เป็นเวกเตอร์ซึ่งพิกัด x และ y สามารถกำหนดได้โดยการลบพิกัดของจุดเริ่มต้นและจุดสิ้นสุด:
edgeE
= point0
- point4
= (1, 0) - (5, 0)
= (-4, 0)
และ
edgeA
= point1
- point0
= (6, 4) - (1, 0)
= (5, 4)
และ
และสินค้าข้ามของทั้งสองขอบที่อยู่ติดกันจะคำนวณโดยใช้ปัจจัยของเมทริกซ์ต่อไปนี้ซึ่งถูกสร้างโดยการใส่พิกัดของสองเวกเตอร์ด้านล่างสัญลักษณ์ที่เป็นตัวแทนของสามประสานงานแกน ( i
, j
และk
) พิกัดที่สาม (ศูนย์) ที่มีค่าอยู่ที่นั่นเพราะแนวคิดผลิตภัณฑ์ Cross เป็นโครงสร้างสามมิติดังนั้นเราจึงขยายเวกเตอร์ 2 มิติเหล่านี้เป็น 3 มิติเพื่อใช้ผลิตภัณฑ์ข้าม:
i j k
-4 0 0
1 4 0
เนื่องจาก cross-product ทั้งหมดสร้างเวกเตอร์ตั้งฉากกับระนาบของเวกเตอร์สองตัวที่ถูกคูณตัวกำหนดของเมทริกซ์ด้านบนมีเพียงk
ส่วนประกอบ, (หรือแกน z)
สูตรการคำนวณขนาดของk
ส่วนประกอบหรือแกน z คือ
a1*b2 - a2*b1 = -4* 4 - 0* 1
= -16
ขนาดของค่านี้ ( -16
) คือการวัดไซน์ของมุมระหว่างเวกเตอร์ดั้งเดิม 2 ตัวคูณด้วยผลคูณของขนาดของเวกเตอร์ 2 ตัว ที่จริงสูตรสำหรับความคุ้มค่าอีกอย่างก็คือ
A X B (Cross Product) = |A| * |B| * sin(AB)
เพื่อกลับไปที่การวัดมุมคุณต้องหารค่านี้, ( -16
), โดยผลคูณของขนาดของเวกเตอร์สองตัว
|A| * |B|
= 4 * Sqrt(17)
=16.4924...
ดังนั้นขนาดของบาป (AB) = -16 / 16.4924
=-.97014...
นี่คือการวัดว่าส่วนต่อไปหลังจากจุดยอดมีการโค้งงอไปทางซ้ายหรือขวาและเท่าไหร่ ไม่จำเป็นต้องอาร์คไซน์ สิ่งที่เราจะใส่ใจคือขนาดของมันและแน่นอนเครื่องหมายของมัน (บวกหรือลบ)!
ทำเช่นนี้สำหรับแต่ละจุด 4 จุดอื่น ๆ รอบเส้นทางที่ปิดและเพิ่มค่าจากการคำนวณนี้ในแต่ละจุดยอด
หากผลรวมสุดท้ายเป็นบวกคุณจะหมุนตามเข็มนาฬิกาลบทวนเข็มนาฬิกา
ฉันเดาว่านี่เป็นคำถามที่ค่อนข้างเก่า แต่ฉันจะทิ้งโซลูชันอื่นต่อไปเพราะมันตรงไปตรงมาและไม่เข้มข้นทางคณิตศาสตร์ - มันใช้พีชคณิตพื้นฐาน คำนวณพื้นที่ที่ลงนามของรูปหลายเหลี่ยม ถ้ามันเป็นลบคะแนนจะเรียงตามเข็มนาฬิกาถ้ามันเป็นบวกมันจะทวนเข็มนาฬิกา (ซึ่งคล้ายกับโซลูชันของ Beta มาก)
คำนวณพื้นที่ที่ลงนาม: 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 )
หรือในรหัสหลอก:
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
โปรดทราบว่าหากคุณเพียงตรวจสอบการสั่งซื้อคุณไม่จำเป็นต้องหารด้วย 2
แหล่งที่มา: http://mathworld.wolfram.com/PolygonArea.html
previousPoint
สำหรับการวนซ้ำถัดไป ก่อนเริ่มวนรอบให้ตั้งค่าpreviousPoint
เป็นจุดสุดท้ายของอาร์เรย์ การแลกเปลี่ยนเป็นการคัดลอกตัวแปรพิเศษในท้องถิ่น แต่มีการเข้าถึงอาเรย์น้อยกว่า และที่สำคัญไม่จำเป็นต้องสัมผัสแถวลำดับอินพุต
ค้นหาจุดสุดยอดด้วย y ที่เล็กที่สุด (และ x ที่ใหญ่ที่สุดหากมีความสัมพันธ์) ให้จุดสุดยอดเป็นA
และจุดสุดยอดก่อนหน้านี้ในรายการเป็นและจุดสุดยอดต่อไปในรายการเป็นB
C
ตอนนี้คำนวณเครื่องหมายของผลิตภัณฑ์ข้ามและAB
AC
อ้างอิง:
ฉันจะค้นหาการวางแนวของรูปหลายเหลี่ยมอย่างง่ายได้อย่างไร ใน คำถามที่พบบ่อย: comp.graphics.algorithms
การวางแนวโค้งที่ Wikipedia
O(1)
วิธีการแก้ปัญหา คำตอบอื่น ๆ ให้ผลO(n)
เฉลยสำหรับn
จำนวนจุดรูปหลายเหลี่ยม สำหรับการเพิ่มประสิทธิภาพที่ลึกซึ้งยิ่งขึ้นโปรดดูหัวข้อการพิจารณาในทางปฏิบัติของบทความปฐมนิเทศ Curve ที่ยอดเยี่ยมของ Wikipedia
O(1)
ต่อเมื่อ (A)รูปหลายเหลี่ยมนี้นูน (ในกรณีที่จุดสุดยอดใด ๆ อยู่บนเปลือกนูนและพอเพียง)หรือ (B)คุณรู้จุดยอดที่มีพิกัด Y น้อยที่สุดแล้ว หากไม่ใช่กรณีนี้ (เช่นรูปหลายเหลี่ยมนี้ไม่นูนและคุณไม่รู้อะไรเลย)O(n)
จำเป็นต้องค้นหา เนื่องจากไม่จำเป็นต้องมีการรวมตัว แต่ก็ยังเร็วกว่าโซลูชันอื่น ๆ สำหรับรูปหลายเหลี่ยมอย่างง่าย
นี่คือการใช้ C # อย่างง่ายของอัลกอริทึมตามคำตอบนี้
สมมติว่าเรามีVector
ประเภทที่มีX
และคุณสมบัติของประเภทY
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;
}
%
เป็นตัวดำเนินการโมดูโลหรือส่วนที่เหลือที่ดำเนินการโมดูโลซึ่ง ( ตาม Wikipedia ) พบส่วนที่เหลือหลังจากการหารหมายเลขหนึ่งด้วยอีกหมายเลขหนึ่ง
เริ่มที่หนึ่งในจุดยอดและคำนวณมุมที่ถูกหารด้วยแต่ละด้าน
ครั้งแรกและครั้งสุดท้ายจะเป็นศูนย์ (ดังนั้นข้ามเหล่านั้น); สำหรับส่วนที่เหลือไซน์ของมุมจะถูกกำหนดโดยผลคูณไขว้ของการทำมาตรฐานให้เป็นความยาวหน่วยของ (จุด [n] - จุด [0]) และ (จุด [n-1] - จุด [0]
หากผลรวมของค่าเป็นบวกรูปหลายเหลี่ยมของคุณจะถูกวาดในลักษณะทวนเข็มนาฬิกา
สำหรับสิ่งที่คุ้มค่าฉันใช้ mixin นี้เพื่อคำนวณลำดับการไขลานสำหรับแอป Google Maps API v3
รหัสใช้ประโยชน์จากผลข้างเคียงของพื้นที่รูปหลายเหลี่ยม: ลำดับการหมุนตามเข็มนาฬิกาของจุดยอดให้ผลเป็นพื้นที่บวกในขณะที่ลำดับการหมุนทวนเข็มนาฬิกาของจุดสุดยอดเดียวกันสร้างพื้นที่เดียวกันเป็นค่าลบ รหัสนี้ยังใช้ API ส่วนตัวในห้องสมุดเรขาคณิตของ Google Maps ฉันรู้สึกสบายใจที่จะใช้มัน - ใช้โดยยอมรับความเสี่ยงของคุณเอง
ตัวอย่างการใช้งาน:
var myPolygon = new google.maps.Polygon({/*options*/});
var isCW = myPolygon.isPathClockwise();
ตัวอย่างเต็มรูปแบบกับการทดสอบหน่วย @ 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;
}
})();
การใช้งานคำตอบของฌอนใน 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));
ค่อนข้างแน่ใจว่าสิ่งนี้ถูกต้อง ดูเหมือนว่าจะทำงาน :-)
รูปหลายเหลี่ยมเหล่านั้นมีลักษณะเช่นนี้หากคุณสงสัยว่า:
นี้เป็นฟังก์ชั่นการใช้งานสำหรับOpenLayers 2 เงื่อนไขสำหรับการมีรูปหลายเหลี่ยมตามเข็มนาฬิกาคือarea < 0
มันได้รับการยืนยันโดยการอ้างอิงนี้
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);
}
ถ้าคุณใช้ Matlab ฟังก์ชันispolycw
จะส่งกลับค่าจริงถ้าจุดยอดรูปหลายเหลี่ยมนั้นเรียงตามเข็มนาฬิกา
ขณะที่ยังอธิบายในเรื่องนี้วิกิพีเดียบทความปฐมนิเทศ Curveได้รับ 3 คะแนนp
, q
และr
บนเครื่องบิน (คือมีพิกัด x และ y) คุณสามารถคำนวณสัญญาณของปัจจัยดังต่อไปนี้
หากดีเทอร์มีแนนต์เป็นลบ (เช่นOrient(p, q, r) < 0
) ดังนั้นรูปหลายเหลี่ยมนั้นจะถูกปรับตามเข็มนาฬิกา (CW) ถ้าดีเทอร์มีแนนต์เป็นบวก (เช่นOrient(p, q, r) > 0
) รูปหลายเหลี่ยมนั้นจะถูกทวนเข็มนาฬิกา (CCW) ปัจจัยที่เป็นศูนย์ (เช่นOrient(p, q, r) == 0
) ถ้าจุดp
, q
และr
มีcollinear collinear
ในสูตรข้างต้นเราย่อหน้าคนในด้านหน้าของพิกัดของp
, q
และr
เพราะเราจะใช้เหมือนกันพิกัด
ฉันคิดว่าเพื่อให้บางจุดถูกทวนเข็มนาฬิกาขอบทั้งหมดต้องเป็นค่าบวกไม่ใช่แค่ผลรวมของขอบ ถ้าหนึ่งขอบเป็นลบมากกว่าอย่างน้อย 3 คะแนนจะได้รับทวนเข็มนาฬิกา
โซลูชัน C # / LINQ ของฉันอิงตามคำแนะนำผลิตภัณฑ์ข้ามของ @charlesbretana อยู่ด้านล่าง คุณสามารถระบุการอ้างอิงปกติสำหรับการพัน มันควรจะทำงานตราบใดที่เส้นโค้งส่วนใหญ่อยู่ในระนาบที่กำหนดโดยเวกเตอร์ขึ้น
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;
}
}
}
ด้วยการทดสอบหน่วย
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();
}
}
}
นี่คือทางออกของฉันโดยใช้คำอธิบายในคำตอบอื่น ๆ :
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
วิธีการคำนวณที่ง่ายกว่ามากถ้าคุณรู้จุดภายในรูปหลายเหลี่ยม :
เลือกส่วนของเส้นใดก็ได้จากรูปหลายเหลี่ยมจุดและพิกัดในลำดับนั้น
เพิ่มจุด "ข้างใน" ที่รู้จักและสร้างรูปสามเหลี่ยม
คำนวณ CW หรือ CCW ตามที่แนะนำที่นี่พร้อมทั้งสามคะแนน
หลังจากทดสอบการใช้งานที่ไม่น่าเชื่อถือหลายอย่างอัลกอริทึมที่ให้ผลลัพธ์ที่น่าพอใจเกี่ยวกับการวางแนว CW / CCW ออกมาจากกล่องเป็นสิ่งที่ถูกโพสต์โดย OP ในหัวข้อนี้ (shoelace_formula_3
)
เช่นเคยตัวเลขบวกหมายถึงการวางแนว CW ในขณะที่จำนวนลบ CCW
นี่คือวิธีแก้ปัญหา swift 3.0 ตามคำตอบข้างต้น:
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
ทางออกสำหรับเรื่องนี้;
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;
}
นำทุกจุดเป็นอาร์เรย์แบบนี้
const vertices = [{x: 5, y: 0}, {x: 6, y: 4}, {x: 4, y: 5}, {x: 1, y: 5}, {x: 1, y: 0}];
isClockwise(vertices);
วิธีแก้ปัญหาสำหรับ R เพื่อกำหนดทิศทางและย้อนกลับถ้าตามเข็มนาฬิกา (พบว่าจำเป็นสำหรับวัตถุแบบ 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
ในขณะที่คำตอบเหล่านี้ถูกต้อง แต่ก็มีความรุนแรงทางคณิตศาสตร์มากกว่าที่จำเป็น สมมติว่าพิกัดแผนที่ซึ่งจุดเหนือสุดคือจุดสูงสุดบนแผนที่ ค้นหาจุดเหนือสุดและถ้า 2 แต้มเสมอกันมันคือทิศเหนือสุดและทิศตะวันออกมากที่สุด (นี่คือจุดที่ lhf ใช้ในคำตอบของเขา) ในจุดของคุณ
จุด [0] = (5,0)
จุด [1] = (6,4)
จุด [2] = (4,5)
จุด [3] = (1,5)
จุด [4] = (1,0)
หากเราสมมติว่า P2 เป็นจุดเหนือสุดและทิศตะวันออกมากที่สุดจุดก่อนหน้าหรือถัดไปจะกำหนดตามเข็มนาฬิกา CW หรือ CCW เนื่องจากจุดเหนือสุดอยู่บนทิศเหนือหาก P1 (ก่อนหน้า) ถึง P2 เคลื่อนที่ไปทางทิศตะวันออกทิศทางคือ CW ในกรณีนี้มันเคลื่อนไปทางตะวันตกดังนั้นทิศทางคือทวนเข็มนาฬิกาตามคำตอบที่ยอมรับ หากจุดก่อนหน้าไม่มีการเคลื่อนที่ในแนวนอนระบบเดียวกันจะใช้กับจุดถัดไป P3 ถ้า P3 เป็นทิศตะวันตกของ P2 แสดงว่าการเคลื่อนที่นั้นเป็น CCW หากการเคลื่อนไหว P2 ถึง P3 เป็นทิศตะวันออกก็คือทิศตะวันตกในกรณีนี้การเคลื่อนไหวคือ CW สมมติว่า nte, P2 ในข้อมูลของคุณเป็นจุดเหนือสุดและทิศตะวันออกและ prv คือจุดก่อนหน้า, P1 ในข้อมูลของคุณและ nxt เป็นจุดถัดไป, P3 ในข้อมูลของคุณและ [0] เป็นแนวนอนหรือทิศตะวันออก / ทิศตะวันตกที่ทิศตะวันตกน้อยกว่าตะวันออกและ [1] เป็นแนวตั้ง
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)
.x
และ.y
โครงสร้างแทนที่จะเป็น[0]
และ[1]
ฉันไม่รู้ว่ารหัสของคุณพูดว่าอะไรเป็นครั้งแรกที่ฉันมองไปที่มัน)
รหัส C # เพื่อใช้คำตอบของ 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;
}
นี่คือการใช้งาน Python 3 อย่างง่ายตามคำตอบนี้ (ซึ่งในที่สุดก็ขึ้นอยู่กับโซลูชันที่เสนอในคำตอบที่ยอมรับ )
def is_clockwise(points):
# points is your list (or array) of 2d points.
assert len(points) > 0
s = 0.0
for p1, p2 in zip(points, points[1:] + [points[0]]):
s += (p2[0] - p1[0]) * (p2[1] + p1[1])
return s > 0.0
หาจุดศูนย์กลางมวลของจุดเหล่านี้
สมมติว่ามีเส้นจากจุดนี้ไปยังจุดของคุณ
หามุมระหว่างสองบรรทัดสำหรับ line0 line1
กว่าทำเพื่อ line1 และ line2
...
...
ถ้ามุมนี้เพิ่มขึ้น monotonically มากกว่าทวนเข็มนาฬิกา
มิฉะนั้นถ้าลดความซ้ำซากมันเป็นตามเข็มนาฬิกา
อื่น (ไม่ใช่ความซ้ำซาก)
คุณไม่สามารถตัดสินใจได้ดังนั้นจึงไม่ฉลาด