Slicker Algorithm (หรือ Shoelace Formula) คำนวณพื้นที่ polygon โดยตรงจากพิกัดจุดโหนด ไม่ต้องแยกเป็นสามเหลี่ยม
สูตรคณิตศาสตร์
สำหรับ polygon ที่มีโหนด (x₀,y₀), (x₁,y₁), ..., (xₙ₋₁,yₙ₋₁) เรียงตามเข็มนาฬิกาหรือทวนเข็มนาฬิกา:
Area = ½ | Σ(xᵢ * yᵢ₊₁ - xᵢ₊₁ * yᵢ) | โดย i = 0 ถึง n-1 และ (xₙ, yₙ) = (x₀, y₀)
คุณสมบัติ
- ทำงานได้กับ polygon ทุกประเภท (convex, concave, self-intersecting)
- ผลลัพธ์เป็น signed area: บวก = counter-clockwise, ลบ = clockwise
- Complexity: O(n) ใช้หน่วยความจำ O(1)
ตัวอย่างโค้ด
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
struct Point {
double x, y;
};
double polygonArea(const vector<Point>& poly) {
int n = poly.size();
if (n < 3) return 0.0;
double area = 0.0;
for (int i = 0; i < n; ++i) {
int j = (i + 1) % n;
area += poly[i].x * poly[j].y - poly[j].x * poly[i].y;
}
return fabs(area) / 2.0;
}
// สigned area (เก็บเครื่องหมายเพื่อรู้ winding order)
double signedPolygonArea(const vector<Point>& poly) {
int n = poly.size();
if (n < 3) return 0.0;
double area = 0.0;
for (int i = 0; i < n; ++i) {
int j = (i + 1) % n;
area += poly[i].x * poly[j].y - poly[j].x * poly[i].y;
}
return area / 2.0;
}
int main() {
// ตัวอย่าง: รูปหกเหลี่ยมไม่สม่ำเสมอ
vector<Point> polygon = {
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 7}
};
cout << "Polygon vertices:" << endl;
for (int i = 0; i < polygon.size(); ++i) {
cout << " " << i << ": (" << polygon[i].x << ", " << polygon[i].y << ")" << endl;
}
double area = polygonArea(polygon);
double signedArea = signedPolygonArea(polygon);
cout << "\nSigned area: " << signedArea << endl;
cout << "Area (absolute): " << area << endl;
cout << "Winding order: " << (signedArea > 0 ? "Counter-clockwise" : "Clockwise") << endl;
// รับ input จากผู้ใช้
cout << "\n--- ป้อน polygon เอง ---" << endl;
int n;
cout << "จำนวนโหนด (น้อยที่สุด 3): ";
cin >> n;
if (n >= 3) {
vector<Point> userPoly(n);
cout << "ป้อนพิกัด x y ของแต่ละโหนด:" << endl;
for (int i = 0; i < n; ++i) {
cout << " โหนด " << i << ": ";
cin >> userPoly[i].x >> userPoly[i].y;
}
double userArea = polygonArea(userPoly);
cout << "\nพื้นที่ Polygon " << n << " โหนด: " << userArea << endl;
}
return 0;
}
ผลลัพธ์ตัวอย่าง
Polygon vertices: 0: (1, 1) 1: (2, 2) 2: (3, 3) 3: (4, 4) 4: (5, 5) 5: (6, 7) Signed area: 2.5 Area (absolute): 2.5 Winding order: Counter-clockwise
การยืนยันสูตร (Verification)
// ทดสอบกับรูปสี่เหลี่ยมจัตุรัส (0,0) (4,0) (4,3) (0,3) -> พื้นที่ = 12
vector<Point> rect = {{0,0}, {4,0}, {4,3}, {0,3}};
cout << "Rectangle area: " << polygonArea(rect) << endl; // 12
// สามเหลี่ยม (0,0) (4,0) (0,3) -> พื้นที่ = 6
vector<Point> tri = {{0,0}, {4,0}, {0,3}};
cout << "Triangle area: " << polygonArea(tri) << endl; // 6
ข้อดีของ Slicker Algorithm
- ไม่ต้อง triangulate (ซับซ้อน O(n log n) หรือ O(n²))
- โค้ดสั้น กระชับ เข้าใจง่าย
- ทำงานได้กับ polygon ที่ self-intersecting (ได้ signed area)
- เหมาะสำหรับ real-time applications
ข้อควรระวัง
- จุดโหนดต้องเรียงลำดับ (CW หรือ CCW) ถูกต้อง
- Polygon ต้องปิด (จุดสุดท้ายเชื่อมจุดแรก) - โค้ดจัดการด้วย modulo
- Self-intersecting polygon จะได้พื้นที่ "net area" (บางส่วนอาจลบกัน)