【C# / Algorithm】任意圓形和三角形相交部分面積的計算方法
阿新 • • 發佈:2019-01-10
採用運算子過載技術,設計一個名為"&"的運算子,使用該運算子可以計算出一個圓與一個三角形的公共部分的面積,並給出示例程式碼。已知資料:圓的圓心座標和半徑,三角形的三個頂點座標。
今天無意中在貼吧發現了這樣一道題,很顯然難點在後半題。
於是開始考慮圓形和三角形的相交面積計算的通用方法。
我的思路:
三角形和圓形相交的情況比較多,按交點數就有0到6個,每種情況還有很多不同的細分情況要考慮。
將圓形所在的正方形,無限細分成NxN的小矩形(畫素點?),然後一次判斷每個矩形是否即在圓形內又在三角形內,則該矩形為相交部分。
統計所有的矩形,則可以求出相交部分的近似面積。
如果分割的足夠細,則結果精度會比較高。
測試Demo截圖:
以下是演算法核心程式碼(不包含winform介面程式碼)
using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace CrossArea { /// <summary> /// 面積計算 /// </summary> public static class Area { /// <summary> /// 計算交叉部分面積 /// </summary> /// <param name="p">圓心座標</param> /// <param name="r">圓半徑</param> /// <param name="a">三角形A點座標</param> /// <param name="b">三角形B點座標</param> /// <param name="c">三角形C點座標</param> /// <param name="accuracy">計算精度</param> /// <returns></returns> public static double CrossArea(PointF p, float r, PointF a, PointF b, PointF c, float accuracy) { // 計算縮放精度 p = new PointF(p.X * accuracy, p.Y * accuracy); r = r * accuracy; a = new PointF(a.X * accuracy, a.Y * accuracy); b = new PointF(b.X * accuracy, b.Y * accuracy); c = new PointF(c.X * accuracy, c.Y * accuracy); // 掃描起點 PointF p_start = new PointF(p.X - r, p.Y - r); // 掃描終點 PointF p_end = new PointF(p.X + r, p.Y + r); // 掃描統計 double count = 0; // 掃描圓形所在的正方形內所有的點 for (int i = (int)p_start.X; i <= (int)p_end.X; i++) { for (int j = (int)p_start.Y; j <= (int)p_end.Y; j++) { Point pt = new Point(i,j); // 如果掃描當前點在圓形和三角形內 if (IsInCircle(p, r, pt) && IsInTriangle(a, b, c, pt)) { count++; } } } // 計算結果 double answer = count / (accuracy * accuracy); return answer; } /// <summary> /// 計算點到點的距離 /// </summary> /// <param name="point1">點1</param> /// <param name="point2">點2</param> /// <returns>距離</returns> public static double LengthFromPointToPoint(PointF point1, PointF point2) { return Math.Sqrt(Math.Pow(point1.X - point2.X, 2) + Math.Pow(point1.Y - point2.Y, 2)); } /// <summary> /// 點T是否在圓P內 /// </summary> /// <param name="p">圓心座標</param> /// <param name="r">圓形半徑</param> /// <param name="t">點T</param> /// <returns></returns> public static bool IsInCircle(PointF p, float r, PointF t) { float length = (float)LengthFromPointToPoint(p, t); if (length <= r) return true; else return false; } /// <summary> /// 點T是否在三角形內 /// </summary> /// <param name="a">三角形A點座標</param> /// <param name="b">三角形B點座標</param> /// <param name="c">三角形C點座標</param> /// <param name="t">點T</param> /// <returns></returns> public static bool IsInTriangle(PointF a, PointF b, PointF c, PointF t) { bool temp1 = IsInIntercept(a, b, c, t); bool temp2 = IsInIntercept(a, c, b, t); bool temp3 = IsInIntercept(b, c, a, t); if (temp1 && temp2 && temp3) return true; else return false; } /// <summary> /// 點T是否線上AB和C的截距範圍內 /// </summary> /// <param name="a">線端點A</param> /// <param name="b">線端點B</param> /// <param name="c">三角形的另一個點C</param> /// <param name="t">點T</param> /// <returns></returns> public static bool IsInIntercept(PointF a, PointF b, PointF c, PointF t) { // AB延長線在Y軸上的截點,AB過C和T平行線在座標軸上的截點 float p1, p2, pt; // 斜率不存在時 if (a.X == b.X) { p1 = a.X; p2 = c.X; pt = t.X; } else { // 斜率為0時 if (a.Y == b.Y) { p1 = a.Y; p2 = c.Y; pt = t.Y; } // 斜率不為0時 else { // 斜率 float k = (a.Y - b.Y) / (a.X - b.X); float bb = a.Y - k * a.X; // Y軸的截距 p1 = bb; p2 = c.Y - k * c.X; pt = t.Y - k * t.X; } } if ((pt <= p2 && pt >= p1) || (pt <= p1 && pt >= p2)) return true; else return false; } } }