SeidelDecomposer.cs 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using Microsoft.Xna.Framework;
  5. namespace FarseerPhysics.Common.Decomposition
  6. {
  7. //From the Poly2Tri project by Mason Green: http://code.google.com/p/poly2tri/source/browse?repo=archive#hg/scala/src/org/poly2tri/seidel
  8. /// <summary>
  9. /// Convex decomposition algorithm based on Raimund Seidel's paper "A simple and fast incremental randomized
  10. /// algorithm for computing trapezoidal decompositions and for triangulating polygons"
  11. /// See also: "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
  12. /// "Computational Geometry in C", 2nd edition, by Joseph O'Rourke
  13. /// </summary>
  14. public static class SeidelDecomposer
  15. {
  16. /// <summary>
  17. /// Decompose the polygon into several smaller non-concave polygon.
  18. /// </summary>
  19. /// <param name="vertices">The polygon to decompose.</param>
  20. /// <param name="sheer">The sheer to use. If you get bad results, try using a higher value. The default value is 0.001</param>
  21. /// <returns>A list of triangles</returns>
  22. public static List<Vertices> ConvexPartition(Vertices vertices, float sheer)
  23. {
  24. List<Point> compatList = new List<Point>(vertices.Count);
  25. foreach (Vector2 vertex in vertices)
  26. {
  27. compatList.Add(new Point(vertex.X, vertex.Y));
  28. }
  29. Triangulator t = new Triangulator(compatList, sheer);
  30. List<Vertices> list = new List<Vertices>();
  31. foreach (List<Point> triangle in t.Triangles)
  32. {
  33. Vertices verts = new Vertices(triangle.Count);
  34. foreach (Point point in triangle)
  35. {
  36. verts.Add(new Vector2(point.X, point.Y));
  37. }
  38. list.Add(verts);
  39. }
  40. return list;
  41. }
  42. /// <summary>
  43. /// Decompose the polygon into several smaller non-concave polygon.
  44. /// </summary>
  45. /// <param name="vertices">The polygon to decompose.</param>
  46. /// <param name="sheer">The sheer to use. If you get bad results, try using a higher value. The default value is 0.001</param>
  47. /// <returns>A list of trapezoids</returns>
  48. public static List<Vertices> ConvexPartitionTrapezoid(Vertices vertices, float sheer)
  49. {
  50. List<Point> compatList = new List<Point>(vertices.Count);
  51. foreach (Vector2 vertex in vertices)
  52. {
  53. compatList.Add(new Point(vertex.X, vertex.Y));
  54. }
  55. Triangulator t = new Triangulator(compatList, sheer);
  56. List<Vertices> list = new List<Vertices>();
  57. foreach (Trapezoid trapezoid in t.Trapezoids)
  58. {
  59. Vertices verts = new Vertices();
  60. List<Point> points = trapezoid.Vertices();
  61. foreach (Point point in points)
  62. {
  63. verts.Add(new Vector2(point.X, point.Y));
  64. }
  65. list.Add(verts);
  66. }
  67. return list;
  68. }
  69. }
  70. internal class MonotoneMountain
  71. {
  72. private const float PiSlop = 3.1f;
  73. public List<List<Point>> Triangles;
  74. private HashSet<Point> _convexPoints;
  75. private Point _head;
  76. // Monotone mountain points
  77. private List<Point> _monoPoly;
  78. // Triangles that constitute the mountain
  79. // Used to track which side of the line we are on
  80. private bool _positive;
  81. private int _size;
  82. private Point _tail;
  83. // Almost Pi!
  84. public MonotoneMountain()
  85. {
  86. _size = 0;
  87. _tail = null;
  88. _head = null;
  89. _positive = false;
  90. _convexPoints = new HashSet<Point>();
  91. _monoPoly = new List<Point>();
  92. Triangles = new List<List<Point>>();
  93. }
  94. // Append a point to the list
  95. public void Add(Point point)
  96. {
  97. if (_size == 0)
  98. {
  99. _head = point;
  100. _size = 1;
  101. }
  102. else if (_size == 1)
  103. {
  104. // Keep repeat points out of the list
  105. _tail = point;
  106. _tail.Prev = _head;
  107. _head.Next = _tail;
  108. _size = 2;
  109. }
  110. else
  111. {
  112. // Keep repeat points out of the list
  113. _tail.Next = point;
  114. point.Prev = _tail;
  115. _tail = point;
  116. _size += 1;
  117. }
  118. }
  119. // Remove a point from the list
  120. public void Remove(Point point)
  121. {
  122. Point next = point.Next;
  123. Point prev = point.Prev;
  124. point.Prev.Next = next;
  125. point.Next.Prev = prev;
  126. _size -= 1;
  127. }
  128. // Partition a x-monotone mountain into triangles O(n)
  129. // See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
  130. public void Process()
  131. {
  132. // Establish the proper sign
  133. _positive = AngleSign();
  134. // create monotone polygon - for dubug purposes
  135. GenMonoPoly();
  136. // Initialize internal angles at each nonbase vertex
  137. // Link strictly convex vertices into a list, ignore reflex vertices
  138. Point p = _head.Next;
  139. while (p.Neq(_tail))
  140. {
  141. float a = Angle(p);
  142. // If the point is almost colinear with it's neighbor, remove it!
  143. if (a >= PiSlop || a <= -PiSlop || a == 0.0)
  144. Remove(p);
  145. else if (IsConvex(p))
  146. _convexPoints.Add(p);
  147. p = p.Next;
  148. }
  149. Triangulate();
  150. }
  151. private void Triangulate()
  152. {
  153. while (_convexPoints.Count != 0)
  154. {
  155. IEnumerator<Point> e = _convexPoints.GetEnumerator();
  156. e.MoveNext();
  157. Point ear = e.Current;
  158. _convexPoints.Remove(ear);
  159. Point a = ear.Prev;
  160. Point b = ear;
  161. Point c = ear.Next;
  162. List<Point> triangle = new List<Point>(3);
  163. triangle.Add(a);
  164. triangle.Add(b);
  165. triangle.Add(c);
  166. Triangles.Add(triangle);
  167. // Remove ear, update angles and convex list
  168. Remove(ear);
  169. if (Valid(a))
  170. _convexPoints.Add(a);
  171. if (Valid(c))
  172. _convexPoints.Add(c);
  173. }
  174. Debug.Assert(_size <= 3, "Triangulation bug, please report");
  175. }
  176. private bool Valid(Point p)
  177. {
  178. return p.Neq(_head) && p.Neq(_tail) && IsConvex(p);
  179. }
  180. // Create the monotone polygon
  181. private void GenMonoPoly()
  182. {
  183. Point p = _head;
  184. while (p != null)
  185. {
  186. _monoPoly.Add(p);
  187. p = p.Next;
  188. }
  189. }
  190. private float Angle(Point p)
  191. {
  192. Point a = (p.Next - p);
  193. Point b = (p.Prev - p);
  194. return (float)Math.Atan2(a.Cross(b), a.Dot(b));
  195. }
  196. private bool AngleSign()
  197. {
  198. Point a = (_head.Next - _head);
  199. Point b = (_tail - _head);
  200. return Math.Atan2(a.Cross(b), a.Dot(b)) >= 0;
  201. }
  202. // Determines if the inslide angle is convex or reflex
  203. private bool IsConvex(Point p)
  204. {
  205. if (_positive != (Angle(p) >= 0))
  206. return false;
  207. return true;
  208. }
  209. }
  210. // Node for a Directed Acyclic graph (DAG)
  211. internal abstract class Node
  212. {
  213. protected Node LeftChild;
  214. public List<Node> ParentList;
  215. protected Node RightChild;
  216. protected Node(Node left, Node right)
  217. {
  218. ParentList = new List<Node>();
  219. LeftChild = left;
  220. RightChild = right;
  221. if (left != null)
  222. left.ParentList.Add(this);
  223. if (right != null)
  224. right.ParentList.Add(this);
  225. }
  226. public abstract Sink Locate(Edge s);
  227. // Replace a node in the graph with this node
  228. // Make sure parent pointers are updated
  229. public void Replace(Node node)
  230. {
  231. foreach (Node parent in node.ParentList)
  232. {
  233. // Select the correct node to replace (left or right child)
  234. if (parent.LeftChild == node)
  235. parent.LeftChild = this;
  236. else
  237. parent.RightChild = this;
  238. }
  239. ParentList.AddRange(node.ParentList);
  240. }
  241. }
  242. // Directed Acyclic graph (DAG)
  243. // See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
  244. internal class QueryGraph
  245. {
  246. private Node _head;
  247. public QueryGraph(Node head)
  248. {
  249. _head = head;
  250. }
  251. private Trapezoid Locate(Edge edge)
  252. {
  253. return _head.Locate(edge).Trapezoid;
  254. }
  255. public List<Trapezoid> FollowEdge(Edge edge)
  256. {
  257. List<Trapezoid> trapezoids = new List<Trapezoid>();
  258. trapezoids.Add(Locate(edge));
  259. int j = 0;
  260. while (edge.Q.X > trapezoids[j].RightPoint.X)
  261. {
  262. if (edge.IsAbove(trapezoids[j].RightPoint))
  263. {
  264. trapezoids.Add(trapezoids[j].UpperRight);
  265. }
  266. else
  267. {
  268. trapezoids.Add(trapezoids[j].LowerRight);
  269. }
  270. j += 1;
  271. }
  272. return trapezoids;
  273. }
  274. private void Replace(Sink sink, Node node)
  275. {
  276. if (sink.ParentList.Count == 0)
  277. _head = node;
  278. else
  279. node.Replace(sink);
  280. }
  281. public void Case1(Sink sink, Edge edge, Trapezoid[] tList)
  282. {
  283. YNode yNode = new YNode(edge, Sink.Isink(tList[1]), Sink.Isink(tList[2]));
  284. XNode qNode = new XNode(edge.Q, yNode, Sink.Isink(tList[3]));
  285. XNode pNode = new XNode(edge.P, Sink.Isink(tList[0]), qNode);
  286. Replace(sink, pNode);
  287. }
  288. public void Case2(Sink sink, Edge edge, Trapezoid[] tList)
  289. {
  290. YNode yNode = new YNode(edge, Sink.Isink(tList[1]), Sink.Isink(tList[2]));
  291. XNode pNode = new XNode(edge.P, Sink.Isink(tList[0]), yNode);
  292. Replace(sink, pNode);
  293. }
  294. public void Case3(Sink sink, Edge edge, Trapezoid[] tList)
  295. {
  296. YNode yNode = new YNode(edge, Sink.Isink(tList[0]), Sink.Isink(tList[1]));
  297. Replace(sink, yNode);
  298. }
  299. public void Case4(Sink sink, Edge edge, Trapezoid[] tList)
  300. {
  301. YNode yNode = new YNode(edge, Sink.Isink(tList[0]), Sink.Isink(tList[1]));
  302. XNode qNode = new XNode(edge.Q, yNode, Sink.Isink(tList[2]));
  303. Replace(sink, qNode);
  304. }
  305. }
  306. internal class Sink : Node
  307. {
  308. public Trapezoid Trapezoid;
  309. private Sink(Trapezoid trapezoid)
  310. : base(null, null)
  311. {
  312. Trapezoid = trapezoid;
  313. trapezoid.Sink = this;
  314. }
  315. public static Sink Isink(Trapezoid trapezoid)
  316. {
  317. if (trapezoid.Sink == null)
  318. return new Sink(trapezoid);
  319. return trapezoid.Sink;
  320. }
  321. public override Sink Locate(Edge edge)
  322. {
  323. return this;
  324. }
  325. }
  326. // See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
  327. internal class TrapezoidalMap
  328. {
  329. // Trapezoid container
  330. public HashSet<Trapezoid> Map;
  331. // AABB margin
  332. // Bottom segment that spans multiple trapezoids
  333. private Edge _bCross;
  334. // Top segment that spans multiple trapezoids
  335. private Edge _cross;
  336. private float _margin;
  337. public TrapezoidalMap()
  338. {
  339. Map = new HashSet<Trapezoid>();
  340. _margin = 50.0f;
  341. _bCross = null;
  342. _cross = null;
  343. }
  344. public void Clear()
  345. {
  346. _bCross = null;
  347. _cross = null;
  348. }
  349. // Case 1: segment completely enclosed by trapezoid
  350. // break trapezoid into 4 smaller trapezoids
  351. public Trapezoid[] Case1(Trapezoid t, Edge e)
  352. {
  353. Trapezoid[] trapezoids = new Trapezoid[4];
  354. trapezoids[0] = new Trapezoid(t.LeftPoint, e.P, t.Top, t.Bottom);
  355. trapezoids[1] = new Trapezoid(e.P, e.Q, t.Top, e);
  356. trapezoids[2] = new Trapezoid(e.P, e.Q, e, t.Bottom);
  357. trapezoids[3] = new Trapezoid(e.Q, t.RightPoint, t.Top, t.Bottom);
  358. trapezoids[0].UpdateLeft(t.UpperLeft, t.LowerLeft);
  359. trapezoids[1].UpdateLeftRight(trapezoids[0], null, trapezoids[3], null);
  360. trapezoids[2].UpdateLeftRight(null, trapezoids[0], null, trapezoids[3]);
  361. trapezoids[3].UpdateRight(t.UpperRight, t.LowerRight);
  362. return trapezoids;
  363. }
  364. // Case 2: Trapezoid contains point p, q lies outside
  365. // break trapezoid into 3 smaller trapezoids
  366. public Trapezoid[] Case2(Trapezoid t, Edge e)
  367. {
  368. Point rp;
  369. if (e.Q.X == t.RightPoint.X)
  370. rp = e.Q;
  371. else
  372. rp = t.RightPoint;
  373. Trapezoid[] trapezoids = new Trapezoid[3];
  374. trapezoids[0] = new Trapezoid(t.LeftPoint, e.P, t.Top, t.Bottom);
  375. trapezoids[1] = new Trapezoid(e.P, rp, t.Top, e);
  376. trapezoids[2] = new Trapezoid(e.P, rp, e, t.Bottom);
  377. trapezoids[0].UpdateLeft(t.UpperLeft, t.LowerLeft);
  378. trapezoids[1].UpdateLeftRight(trapezoids[0], null, t.UpperRight, null);
  379. trapezoids[2].UpdateLeftRight(null, trapezoids[0], null, t.LowerRight);
  380. _bCross = t.Bottom;
  381. _cross = t.Top;
  382. e.Above = trapezoids[1];
  383. e.Below = trapezoids[2];
  384. return trapezoids;
  385. }
  386. // Case 3: Trapezoid is bisected
  387. public Trapezoid[] Case3(Trapezoid t, Edge e)
  388. {
  389. Point lp;
  390. if (e.P.X == t.LeftPoint.X)
  391. lp = e.P;
  392. else
  393. lp = t.LeftPoint;
  394. Point rp;
  395. if (e.Q.X == t.RightPoint.X)
  396. rp = e.Q;
  397. else
  398. rp = t.RightPoint;
  399. Trapezoid[] trapezoids = new Trapezoid[2];
  400. if (_cross == t.Top)
  401. {
  402. trapezoids[0] = t.UpperLeft;
  403. trapezoids[0].UpdateRight(t.UpperRight, null);
  404. trapezoids[0].RightPoint = rp;
  405. }
  406. else
  407. {
  408. trapezoids[0] = new Trapezoid(lp, rp, t.Top, e);
  409. trapezoids[0].UpdateLeftRight(t.UpperLeft, e.Above, t.UpperRight, null);
  410. }
  411. if (_bCross == t.Bottom)
  412. {
  413. trapezoids[1] = t.LowerLeft;
  414. trapezoids[1].UpdateRight(null, t.LowerRight);
  415. trapezoids[1].RightPoint = rp;
  416. }
  417. else
  418. {
  419. trapezoids[1] = new Trapezoid(lp, rp, e, t.Bottom);
  420. trapezoids[1].UpdateLeftRight(e.Below, t.LowerLeft, null, t.LowerRight);
  421. }
  422. _bCross = t.Bottom;
  423. _cross = t.Top;
  424. e.Above = trapezoids[0];
  425. e.Below = trapezoids[1];
  426. return trapezoids;
  427. }
  428. // Case 4: Trapezoid contains point q, p lies outside
  429. // break trapezoid into 3 smaller trapezoids
  430. public Trapezoid[] Case4(Trapezoid t, Edge e)
  431. {
  432. Point lp;
  433. if (e.P.X == t.LeftPoint.X)
  434. lp = e.P;
  435. else
  436. lp = t.LeftPoint;
  437. Trapezoid[] trapezoids = new Trapezoid[3];
  438. if (_cross == t.Top)
  439. {
  440. trapezoids[0] = t.UpperLeft;
  441. trapezoids[0].RightPoint = e.Q;
  442. }
  443. else
  444. {
  445. trapezoids[0] = new Trapezoid(lp, e.Q, t.Top, e);
  446. trapezoids[0].UpdateLeft(t.UpperLeft, e.Above);
  447. }
  448. if (_bCross == t.Bottom)
  449. {
  450. trapezoids[1] = t.LowerLeft;
  451. trapezoids[1].RightPoint = e.Q;
  452. }
  453. else
  454. {
  455. trapezoids[1] = new Trapezoid(lp, e.Q, e, t.Bottom);
  456. trapezoids[1].UpdateLeft(e.Below, t.LowerLeft);
  457. }
  458. trapezoids[2] = new Trapezoid(e.Q, t.RightPoint, t.Top, t.Bottom);
  459. trapezoids[2].UpdateLeftRight(trapezoids[0], trapezoids[1], t.UpperRight, t.LowerRight);
  460. return trapezoids;
  461. }
  462. // Create an AABB around segments
  463. public Trapezoid BoundingBox(List<Edge> edges)
  464. {
  465. Point max = edges[0].P + _margin;
  466. Point min = edges[0].Q - _margin;
  467. foreach (Edge e in edges)
  468. {
  469. if (e.P.X > max.X) max = new Point(e.P.X + _margin, max.Y);
  470. if (e.P.Y > max.Y) max = new Point(max.X, e.P.Y + _margin);
  471. if (e.Q.X > max.X) max = new Point(e.Q.X + _margin, max.Y);
  472. if (e.Q.Y > max.Y) max = new Point(max.X, e.Q.Y + _margin);
  473. if (e.P.X < min.X) min = new Point(e.P.X - _margin, min.Y);
  474. if (e.P.Y < min.Y) min = new Point(min.X, e.P.Y - _margin);
  475. if (e.Q.X < min.X) min = new Point(e.Q.X - _margin, min.Y);
  476. if (e.Q.Y < min.Y) min = new Point(min.X, e.Q.Y - _margin);
  477. }
  478. Edge top = new Edge(new Point(min.X, max.Y), new Point(max.X, max.Y));
  479. Edge bottom = new Edge(new Point(min.X, min.Y), new Point(max.X, min.Y));
  480. Point left = bottom.P;
  481. Point right = top.Q;
  482. return new Trapezoid(left, right, top, bottom);
  483. }
  484. }
  485. internal class Point
  486. {
  487. // Pointers to next and previous points in Monontone Mountain
  488. public Point Next, Prev;
  489. public float X, Y;
  490. public Point(float x, float y)
  491. {
  492. X = x;
  493. Y = y;
  494. Next = null;
  495. Prev = null;
  496. }
  497. public static Point operator -(Point p1, Point p2)
  498. {
  499. return new Point(p1.X - p2.X, p1.Y - p2.Y);
  500. }
  501. public static Point operator +(Point p1, Point p2)
  502. {
  503. return new Point(p1.X + p2.X, p1.Y + p2.Y);
  504. }
  505. public static Point operator -(Point p1, float f)
  506. {
  507. return new Point(p1.X - f, p1.Y - f);
  508. }
  509. public static Point operator +(Point p1, float f)
  510. {
  511. return new Point(p1.X + f, p1.Y + f);
  512. }
  513. public float Cross(Point p)
  514. {
  515. return X * p.Y - Y * p.X;
  516. }
  517. public float Dot(Point p)
  518. {
  519. return X * p.X + Y * p.Y;
  520. }
  521. public bool Neq(Point p)
  522. {
  523. return p.X != X || p.Y != Y;
  524. }
  525. public float Orient2D(Point pb, Point pc)
  526. {
  527. float acx = X - pc.X;
  528. float bcx = pb.X - pc.X;
  529. float acy = Y - pc.Y;
  530. float bcy = pb.Y - pc.Y;
  531. return acx * bcy - acy * bcx;
  532. }
  533. }
  534. internal class Edge
  535. {
  536. // Pointers used for building trapezoidal map
  537. public Trapezoid Above;
  538. public float B;
  539. public Trapezoid Below;
  540. // Equation of a line: y = m*x + b
  541. // Slope of the line (m)
  542. // Montone mountain points
  543. public HashSet<Point> MPoints;
  544. public Point P;
  545. public Point Q;
  546. public float Slope;
  547. // Y intercept
  548. public Edge(Point p, Point q)
  549. {
  550. P = p;
  551. Q = q;
  552. if (q.X - p.X != 0)
  553. Slope = (q.Y - p.Y) / (q.X - p.X);
  554. else
  555. Slope = 0;
  556. B = p.Y - (p.X * Slope);
  557. Above = null;
  558. Below = null;
  559. MPoints = new HashSet<Point>();
  560. MPoints.Add(p);
  561. MPoints.Add(q);
  562. }
  563. public bool IsAbove(Point point)
  564. {
  565. return P.Orient2D(Q, point) < 0;
  566. }
  567. public bool IsBelow(Point point)
  568. {
  569. return P.Orient2D(Q, point) > 0;
  570. }
  571. public void AddMpoint(Point point)
  572. {
  573. foreach (Point mp in MPoints)
  574. if (!mp.Neq(point))
  575. return;
  576. MPoints.Add(point);
  577. }
  578. }
  579. internal class Trapezoid
  580. {
  581. public Edge Bottom;
  582. public bool Inside;
  583. public Point LeftPoint;
  584. // Neighbor pointers
  585. public Trapezoid LowerLeft;
  586. public Trapezoid LowerRight;
  587. public Point RightPoint;
  588. public Sink Sink;
  589. public Edge Top;
  590. public Trapezoid UpperLeft;
  591. public Trapezoid UpperRight;
  592. public Trapezoid(Point leftPoint, Point rightPoint, Edge top, Edge bottom)
  593. {
  594. LeftPoint = leftPoint;
  595. RightPoint = rightPoint;
  596. Top = top;
  597. Bottom = bottom;
  598. UpperLeft = null;
  599. UpperRight = null;
  600. LowerLeft = null;
  601. LowerRight = null;
  602. Inside = true;
  603. Sink = null;
  604. }
  605. // Update neighbors to the left
  606. public void UpdateLeft(Trapezoid ul, Trapezoid ll)
  607. {
  608. UpperLeft = ul;
  609. if (ul != null) ul.UpperRight = this;
  610. LowerLeft = ll;
  611. if (ll != null) ll.LowerRight = this;
  612. }
  613. // Update neighbors to the right
  614. public void UpdateRight(Trapezoid ur, Trapezoid lr)
  615. {
  616. UpperRight = ur;
  617. if (ur != null) ur.UpperLeft = this;
  618. LowerRight = lr;
  619. if (lr != null) lr.LowerLeft = this;
  620. }
  621. // Update neighbors on both sides
  622. public void UpdateLeftRight(Trapezoid ul, Trapezoid ll, Trapezoid ur, Trapezoid lr)
  623. {
  624. UpperLeft = ul;
  625. if (ul != null) ul.UpperRight = this;
  626. LowerLeft = ll;
  627. if (ll != null) ll.LowerRight = this;
  628. UpperRight = ur;
  629. if (ur != null) ur.UpperLeft = this;
  630. LowerRight = lr;
  631. if (lr != null) lr.LowerLeft = this;
  632. }
  633. // Recursively trim outside neighbors
  634. public void TrimNeighbors()
  635. {
  636. if (Inside)
  637. {
  638. Inside = false;
  639. if (UpperLeft != null) UpperLeft.TrimNeighbors();
  640. if (LowerLeft != null) LowerLeft.TrimNeighbors();
  641. if (UpperRight != null) UpperRight.TrimNeighbors();
  642. if (LowerRight != null) LowerRight.TrimNeighbors();
  643. }
  644. }
  645. // Determines if this point lies inside the trapezoid
  646. public bool Contains(Point point)
  647. {
  648. return (point.X > LeftPoint.X && point.X < RightPoint.X && Top.IsAbove(point) && Bottom.IsBelow(point));
  649. }
  650. public List<Point> Vertices()
  651. {
  652. List<Point> verts = new List<Point>(4);
  653. verts.Add(LineIntersect(Top, LeftPoint.X));
  654. verts.Add(LineIntersect(Bottom, LeftPoint.X));
  655. verts.Add(LineIntersect(Bottom, RightPoint.X));
  656. verts.Add(LineIntersect(Top, RightPoint.X));
  657. return verts;
  658. }
  659. private Point LineIntersect(Edge edge, float x)
  660. {
  661. float y = edge.Slope * x + edge.B;
  662. return new Point(x, y);
  663. }
  664. // Add points to monotone mountain
  665. public void AddPoints()
  666. {
  667. if (LeftPoint != Bottom.P)
  668. {
  669. Bottom.AddMpoint(LeftPoint);
  670. }
  671. if (RightPoint != Bottom.Q)
  672. {
  673. Bottom.AddMpoint(RightPoint);
  674. }
  675. if (LeftPoint != Top.P)
  676. {
  677. Top.AddMpoint(LeftPoint);
  678. }
  679. if (RightPoint != Top.Q)
  680. {
  681. Top.AddMpoint(RightPoint);
  682. }
  683. }
  684. }
  685. internal class XNode : Node
  686. {
  687. private Point _point;
  688. public XNode(Point point, Node lChild, Node rChild)
  689. : base(lChild, rChild)
  690. {
  691. _point = point;
  692. }
  693. public override Sink Locate(Edge edge)
  694. {
  695. if (edge.P.X >= _point.X)
  696. // Move to the right in the graph
  697. return RightChild.Locate(edge);
  698. // Move to the left in the graph
  699. return LeftChild.Locate(edge);
  700. }
  701. }
  702. internal class YNode : Node
  703. {
  704. private Edge _edge;
  705. public YNode(Edge edge, Node lChild, Node rChild)
  706. : base(lChild, rChild)
  707. {
  708. _edge = edge;
  709. }
  710. public override Sink Locate(Edge edge)
  711. {
  712. if (_edge.IsAbove(edge.P))
  713. // Move down the graph
  714. return RightChild.Locate(edge);
  715. if (_edge.IsBelow(edge.P))
  716. // Move up the graph
  717. return LeftChild.Locate(edge);
  718. // s and segment share the same endpoint, p
  719. if (edge.Slope < _edge.Slope)
  720. // Move down the graph
  721. return RightChild.Locate(edge);
  722. // Move up the graph
  723. return LeftChild.Locate(edge);
  724. }
  725. }
  726. internal class Triangulator
  727. {
  728. // Trapezoid decomposition list
  729. public List<Trapezoid> Trapezoids;
  730. public List<List<Point>> Triangles;
  731. // Initialize trapezoidal map and query structure
  732. private Trapezoid _boundingBox;
  733. private List<Edge> _edgeList;
  734. private QueryGraph _queryGraph;
  735. private float _sheer = 0.001f;
  736. private TrapezoidalMap _trapezoidalMap;
  737. private List<MonotoneMountain> _xMonoPoly;
  738. public Triangulator(List<Point> polyLine, float sheer)
  739. {
  740. _sheer = sheer;
  741. Triangles = new List<List<Point>>();
  742. Trapezoids = new List<Trapezoid>();
  743. _xMonoPoly = new List<MonotoneMountain>();
  744. _edgeList = InitEdges(polyLine);
  745. _trapezoidalMap = new TrapezoidalMap();
  746. _boundingBox = _trapezoidalMap.BoundingBox(_edgeList);
  747. _queryGraph = new QueryGraph(Sink.Isink(_boundingBox));
  748. Process();
  749. }
  750. // Build the trapezoidal map and query graph
  751. private void Process()
  752. {
  753. foreach (Edge edge in _edgeList)
  754. {
  755. List<Trapezoid> traps = _queryGraph.FollowEdge(edge);
  756. // Remove trapezoids from trapezoidal Map
  757. foreach (Trapezoid t in traps)
  758. {
  759. _trapezoidalMap.Map.Remove(t);
  760. bool cp = t.Contains(edge.P);
  761. bool cq = t.Contains(edge.Q);
  762. Trapezoid[] tList;
  763. if (cp && cq)
  764. {
  765. tList = _trapezoidalMap.Case1(t, edge);
  766. _queryGraph.Case1(t.Sink, edge, tList);
  767. }
  768. else if (cp && !cq)
  769. {
  770. tList = _trapezoidalMap.Case2(t, edge);
  771. _queryGraph.Case2(t.Sink, edge, tList);
  772. }
  773. else if (!cp && !cq)
  774. {
  775. tList = _trapezoidalMap.Case3(t, edge);
  776. _queryGraph.Case3(t.Sink, edge, tList);
  777. }
  778. else
  779. {
  780. tList = _trapezoidalMap.Case4(t, edge);
  781. _queryGraph.Case4(t.Sink, edge, tList);
  782. }
  783. // Add new trapezoids to map
  784. foreach (Trapezoid y in tList)
  785. {
  786. _trapezoidalMap.Map.Add(y);
  787. }
  788. }
  789. _trapezoidalMap.Clear();
  790. }
  791. // Mark outside trapezoids
  792. foreach (Trapezoid t in _trapezoidalMap.Map)
  793. {
  794. MarkOutside(t);
  795. }
  796. // Collect interior trapezoids
  797. foreach (Trapezoid t in _trapezoidalMap.Map)
  798. {
  799. if (t.Inside)
  800. {
  801. Trapezoids.Add(t);
  802. t.AddPoints();
  803. }
  804. }
  805. // Generate the triangles
  806. CreateMountains();
  807. }
  808. // Build a list of x-monotone mountains
  809. private void CreateMountains()
  810. {
  811. foreach (Edge edge in _edgeList)
  812. {
  813. if (edge.MPoints.Count > 2)
  814. {
  815. MonotoneMountain mountain = new MonotoneMountain();
  816. // Sorting is a perfromance hit. Literature says this can be accomplised in
  817. // linear time, although I don't see a way around using traditional methods
  818. // when using a randomized incremental algorithm
  819. // Insertion sort is one of the fastest algorithms for sorting arrays containing
  820. // fewer than ten elements, or for lists that are already mostly sorted.
  821. List<Point> points = new List<Point>(edge.MPoints);
  822. points.Sort((p1, p2) => p1.X.CompareTo(p2.X));
  823. foreach (Point p in points)
  824. mountain.Add(p);
  825. // Triangulate monotone mountain
  826. mountain.Process();
  827. // Extract the triangles into a single list
  828. foreach (List<Point> t in mountain.Triangles)
  829. {
  830. Triangles.Add(t);
  831. }
  832. _xMonoPoly.Add(mountain);
  833. }
  834. }
  835. }
  836. // Mark the outside trapezoids surrounding the polygon
  837. private void MarkOutside(Trapezoid t)
  838. {
  839. if (t.Top == _boundingBox.Top || t.Bottom == _boundingBox.Bottom)
  840. t.TrimNeighbors();
  841. }
  842. // Create segments and connect end points; update edge event pointer
  843. private List<Edge> InitEdges(List<Point> points)
  844. {
  845. List<Edge> edges = new List<Edge>();
  846. for (int i = 0; i < points.Count - 1; i++)
  847. {
  848. edges.Add(new Edge(points[i], points[i + 1]));
  849. }
  850. edges.Add(new Edge(points[0], points[points.Count - 1]));
  851. return OrderSegments(edges);
  852. }
  853. private List<Edge> OrderSegments(List<Edge> edgeInput)
  854. {
  855. // Ignore vertical segments!
  856. List<Edge> edges = new List<Edge>();
  857. foreach (Edge e in edgeInput)
  858. {
  859. Point p = ShearTransform(e.P);
  860. Point q = ShearTransform(e.Q);
  861. // Point p must be to the left of point q
  862. if (p.X > q.X)
  863. {
  864. edges.Add(new Edge(q, p));
  865. }
  866. else if (p.X < q.X)
  867. {
  868. edges.Add(new Edge(p, q));
  869. }
  870. }
  871. // Randomized triangulation improves performance
  872. // See Seidel's paper, or O'Rourke's book, p. 57
  873. Shuffle(edges);
  874. return edges;
  875. }
  876. private static void Shuffle<T>(IList<T> list)
  877. {
  878. Random rng = new Random();
  879. int n = list.Count;
  880. while (n > 1)
  881. {
  882. n--;
  883. int k = rng.Next(n + 1);
  884. T value = list[k];
  885. list[k] = list[n];
  886. list[n] = value;
  887. }
  888. }
  889. // Prevents any two distinct endpoints from lying on a common vertical line, and avoiding
  890. // the degenerate case. See Mark de Berg et al, Chapter 6.3
  891. private Point ShearTransform(Point point)
  892. {
  893. return new Point(point.X + _sheer * point.Y, point.Y);
  894. }
  895. }
  896. }