Voronoi.hx 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706
  1. /*
  2. Port by Nicolas Cannasse
  3. Copyright (C) 2010-2013 Raymond Hill
  4. MIT License: See https://github.com/gorhill/Javascript-Voronoi/LICENSE.md
  5. Author: Raymond Hill ([email protected])
  6. Contributor: Jesse Morgan ([email protected])
  7. File: rhill-voronoi-core.js
  8. Version: 0.98
  9. Date: January 21, 2013
  10. Description: This is my personal Javascript implementation of
  11. Steven Fortune's algorithm to compute Voronoi diagrams.
  12. License: See https://github.com/gorhill/Javascript-Voronoi/LICENSE.md
  13. Credits: See https://github.com/gorhill/Javascript-Voronoi/CREDITS.md
  14. History: See https://github.com/gorhill/Javascript-Voronoi/CHANGELOG.md
  15. */
  16. package h2d.col;
  17. import hxd.Math;
  18. // ---------------------------------------------------------------------------
  19. // Red-Black tree code (based on C version of "rbtree" by Franck Bui-Huu
  20. // https://github.com/fbuihuu/libtree/blob/master/rb.c
  21. private class RBNode<T:RBNode<T>> {
  22. public var rbRed : Bool;
  23. public var rbLeft : T;
  24. public var rbRight : T;
  25. public var rbParent : T;
  26. public var rbNext : T;
  27. public var rbPrevious : T;
  28. }
  29. @:generic private class RBTree<T:RBNode<T>> {
  30. public var root : T;
  31. public function new() {
  32. this.root = null;
  33. }
  34. public function rbInsertSuccessor(node : T, successor : T) {
  35. var parent;
  36. if (node != null) {
  37. // >>> rhill 2011-05-27: Performance: cache previous/next nodes
  38. successor.rbPrevious = node;
  39. successor.rbNext = node.rbNext;
  40. if (node.rbNext != null ) {
  41. node.rbNext.rbPrevious = successor;
  42. }
  43. node.rbNext = successor;
  44. // <<<
  45. if (node.rbRight != null) {
  46. // in-place expansion of node.rbRight.getFirst();
  47. node = node.rbRight;
  48. while (node.rbLeft != null) {node = node.rbLeft;}
  49. node.rbLeft = successor;
  50. }
  51. else {
  52. node.rbRight = successor;
  53. }
  54. parent = node;
  55. }
  56. // rhill 2011-06-07: if node is null, successor must be inserted
  57. // to the left-most part of the tree
  58. else if (this.root != null) {
  59. node = this.getFirst(this.root);
  60. // >>> Performance: cache previous/next nodes
  61. successor.rbPrevious = null;
  62. successor.rbNext = node;
  63. node.rbPrevious = successor;
  64. // <<<
  65. node.rbLeft = successor;
  66. parent = node;
  67. }
  68. else {
  69. // >>> Performance: cache previous/next nodes
  70. successor.rbPrevious = successor.rbNext = null;
  71. // <<<
  72. this.root = successor;
  73. parent = null;
  74. }
  75. successor.rbLeft = successor.rbRight = null;
  76. successor.rbParent = parent;
  77. successor.rbRed = true;
  78. // Fixup the modified tree by recoloring nodes and performing
  79. // rotations (2 at most) hence the red-black tree properties are
  80. // preserved.
  81. var grandpa, uncle;
  82. node = successor;
  83. while (parent != null && parent.rbRed) {
  84. grandpa = parent.rbParent;
  85. if (parent == grandpa.rbLeft) {
  86. uncle = grandpa.rbRight;
  87. if (uncle != null && uncle.rbRed) {
  88. parent.rbRed = uncle.rbRed = false;
  89. grandpa.rbRed = true;
  90. node = grandpa;
  91. }
  92. else {
  93. if (node == parent.rbRight) {
  94. this.rbRotateLeft(parent);
  95. node = parent;
  96. parent = node.rbParent;
  97. }
  98. parent.rbRed = false;
  99. grandpa.rbRed = true;
  100. this.rbRotateRight(grandpa);
  101. }
  102. }
  103. else {
  104. uncle = grandpa.rbLeft;
  105. if (uncle != null && uncle.rbRed) {
  106. parent.rbRed = uncle.rbRed = false;
  107. grandpa.rbRed = true;
  108. node = grandpa;
  109. }
  110. else {
  111. if (node == parent.rbLeft) {
  112. this.rbRotateRight(parent);
  113. node = parent;
  114. parent = node.rbParent;
  115. }
  116. parent.rbRed = false;
  117. grandpa.rbRed = true;
  118. this.rbRotateLeft(grandpa);
  119. }
  120. }
  121. parent = node.rbParent;
  122. }
  123. this.root.rbRed = false;
  124. }
  125. public function rbRemoveNode(node:T) {
  126. // >>> rhill 2011-05-27: Performance: cache previous/next nodes
  127. if (node.rbNext != null) {
  128. node.rbNext.rbPrevious = node.rbPrevious;
  129. }
  130. if (node.rbPrevious != null) {
  131. node.rbPrevious.rbNext = node.rbNext;
  132. }
  133. node.rbNext = node.rbPrevious = null;
  134. // <<<
  135. var parent = node.rbParent,
  136. left = node.rbLeft,
  137. right = node.rbRight,
  138. next;
  139. if (left == null) {
  140. next = right;
  141. }
  142. else if (right == null) {
  143. next = left;
  144. }
  145. else {
  146. next = this.getFirst(right);
  147. }
  148. if (parent != null) {
  149. if (parent.rbLeft == node) {
  150. parent.rbLeft = next;
  151. }
  152. else {
  153. parent.rbRight = next;
  154. }
  155. }
  156. else {
  157. this.root = next;
  158. }
  159. // enforce red-black rules
  160. var isRed;
  161. if (left != null && right != null) {
  162. isRed = next.rbRed;
  163. next.rbRed = node.rbRed;
  164. next.rbLeft = left;
  165. left.rbParent = next;
  166. if (next != right) {
  167. parent = next.rbParent;
  168. next.rbParent = node.rbParent;
  169. node = next.rbRight;
  170. parent.rbLeft = node;
  171. next.rbRight = right;
  172. right.rbParent = next;
  173. }
  174. else {
  175. next.rbParent = parent;
  176. parent = next;
  177. node = next.rbRight;
  178. }
  179. }
  180. else {
  181. isRed = node.rbRed;
  182. node = next;
  183. }
  184. // 'node' is now the sole successor's child and 'parent' its
  185. // new parent (since the successor can have been moved)
  186. if (node != null) {
  187. node.rbParent = parent;
  188. }
  189. // the 'easy' cases
  190. if (isRed) {return;}
  191. if (node != null && node.rbRed) {
  192. node.rbRed = false;
  193. return;
  194. }
  195. // the other cases
  196. var sibling;
  197. do {
  198. if (node == this.root) {
  199. break;
  200. }
  201. if (node == parent.rbLeft) {
  202. sibling = parent.rbRight;
  203. if (sibling.rbRed) {
  204. sibling.rbRed = false;
  205. parent.rbRed = true;
  206. this.rbRotateLeft(parent);
  207. sibling = parent.rbRight;
  208. }
  209. if ((sibling.rbLeft != null && sibling.rbLeft.rbRed) || (sibling.rbRight != null && sibling.rbRight.rbRed)) {
  210. if (sibling.rbRight == null || !sibling.rbRight.rbRed) {
  211. sibling.rbLeft.rbRed = false;
  212. sibling.rbRed = true;
  213. this.rbRotateRight(sibling);
  214. sibling = parent.rbRight;
  215. }
  216. sibling.rbRed = parent.rbRed;
  217. parent.rbRed = sibling.rbRight.rbRed = false;
  218. this.rbRotateLeft(parent);
  219. node = this.root;
  220. break;
  221. }
  222. }
  223. else {
  224. sibling = parent.rbLeft;
  225. if (sibling.rbRed) {
  226. sibling.rbRed = false;
  227. parent.rbRed = true;
  228. this.rbRotateRight(parent);
  229. sibling = parent.rbLeft;
  230. }
  231. if ((sibling.rbLeft != null && sibling.rbLeft.rbRed) || (sibling.rbRight != null && sibling.rbRight.rbRed)) {
  232. if (sibling.rbLeft == null || !sibling.rbLeft.rbRed) {
  233. sibling.rbRight.rbRed = false;
  234. sibling.rbRed = true;
  235. this.rbRotateLeft(sibling);
  236. sibling = parent.rbLeft;
  237. }
  238. sibling.rbRed = parent.rbRed;
  239. parent.rbRed = sibling.rbLeft.rbRed = false;
  240. this.rbRotateRight(parent);
  241. node = this.root;
  242. break;
  243. }
  244. }
  245. sibling.rbRed = true;
  246. node = parent;
  247. parent = parent.rbParent;
  248. } while (!node.rbRed);
  249. if (node != null) {node.rbRed = false;}
  250. }
  251. function rbRotateLeft(node:T) {
  252. var p = node,
  253. q = node.rbRight, // can't be null
  254. parent = p.rbParent;
  255. if (parent != null) {
  256. if (parent.rbLeft == p) {
  257. parent.rbLeft = q;
  258. }
  259. else {
  260. parent.rbRight = q;
  261. }
  262. }
  263. else {
  264. this.root = q;
  265. }
  266. q.rbParent = parent;
  267. p.rbParent = q;
  268. p.rbRight = q.rbLeft;
  269. if (p.rbRight != null) {
  270. p.rbRight.rbParent = p;
  271. }
  272. q.rbLeft = p;
  273. }
  274. function rbRotateRight(node:T) {
  275. var p = node,
  276. q = node.rbLeft, // can't be null
  277. parent = p.rbParent;
  278. if (parent != null) {
  279. if (parent.rbLeft == p) {
  280. parent.rbLeft = q;
  281. }
  282. else {
  283. parent.rbRight = q;
  284. }
  285. }
  286. else {
  287. this.root = q;
  288. }
  289. q.rbParent = parent;
  290. p.rbParent = q;
  291. p.rbLeft = q.rbRight;
  292. if (p.rbLeft != null) {
  293. p.rbLeft.rbParent = p;
  294. }
  295. q.rbRight = p;
  296. }
  297. public function getFirst(node:T) {
  298. while(node.rbLeft != null)
  299. node = node.rbLeft;
  300. return node;
  301. }
  302. public function getLast(node:T) {
  303. while( node.rbRight != null )
  304. node = node.rbRight;
  305. return node;
  306. }
  307. }
  308. /**
  309. The resulting cell inside the Voronoi diagram.
  310. **/
  311. class Cell {
  312. /**
  313. The unique ID/Index of the cell.
  314. **/
  315. public var id : Int;
  316. /**
  317. The source seed point of the cell.
  318. **/
  319. public var point : Point;
  320. /**
  321. The list of the edges of the cell.
  322. **/
  323. public var halfedges : Array<Halfedge>;
  324. public var closeMe : Bool;
  325. @:dox(hide) @:noCompletion
  326. public function new(id, point) {
  327. this.id = id;
  328. this.point = point;
  329. this.halfedges = [];
  330. this.closeMe = false;
  331. }
  332. /**
  333. Returns an enclosing circle collider of the Cell.
  334. _Implementation note_: Not the best possible solution and may produce artifacts.
  335. **/
  336. public function getCircle() {
  337. // still not the best enclosing circle
  338. // would require implementing http://www.personal.kent.edu/~rmuhamma/Compgeometry/MyCG/CG-Applets/Center/centercli.htm for complete solution
  339. var p = new Point(), ec = 0;
  340. for( e in halfedges ) {
  341. var ep = e.getStartpoint();
  342. p.x += ep.x;
  343. p.y += ep.y;
  344. ec++;
  345. }
  346. p.x /= ec;
  347. p.y /= ec;
  348. var r = 0.;
  349. for( e in halfedges ) {
  350. var d = p.distanceSq(e.getStartpoint());
  351. if( d > r ) r = d;
  352. }
  353. return new Circle(p.x, p.y, Math.sqrt(r));
  354. }
  355. @:dox(hide) @:noCompletion
  356. public function prepare() {
  357. var halfedges = this.halfedges, iHalfedge = halfedges.length, edge;
  358. // get rid of unused halfedges
  359. // rhill 2011-05-27: Keep it simple, no point here in trying
  360. // to be fancy: dangling edges are a typically a minority.
  361. while (iHalfedge-- != 0) {
  362. edge = halfedges[iHalfedge].edge;
  363. if (edge.vb == null || edge.va == null) {
  364. halfedges.splice(iHalfedge,1);
  365. }
  366. }
  367. // rhill 2011-05-26: I tried to use a binary search at insertion
  368. // time to keep the array sorted on-the-fly (in Cell.addHalfedge()).
  369. // There was no real benefits in doing so, performance on
  370. // Firefox 3.6 was improved marginally, while performance on
  371. // Opera 11 was penalized marginally.
  372. halfedges.sort(sortByAngle);
  373. return halfedges.length;
  374. }
  375. static function sortByAngle(a:Halfedge, b:Halfedge) {
  376. return b.angle > a.angle ? 1 : (b.angle < a.angle ? -1 : 0);
  377. }
  378. /**
  379. Returns a list of the neighboring cells.
  380. **/
  381. public function getNeighbors() {
  382. var neighbors = [],
  383. iHalfedge = this.halfedges.length,
  384. edge;
  385. while (iHalfedge-- != 0){
  386. edge = this.halfedges[iHalfedge].edge;
  387. // NC : changes pointId check to object == check
  388. if (edge.lPoint != null && edge.lPoint != this.point) {
  389. neighbors.push(edge.lPoint);
  390. }
  391. else if (edge.rPoint != null && edge.rPoint != this.point){
  392. neighbors.push(edge.rPoint);
  393. }
  394. }
  395. return neighbors;
  396. }
  397. /**
  398. Returns a list of the neighbor Cell indexes.
  399. **/
  400. public function getNeighborIndexes() {
  401. var neighbors = [],
  402. iHalfedge = this.halfedges.length,
  403. edge;
  404. while (iHalfedge-- != 0){
  405. edge = this.halfedges[iHalfedge].edge;
  406. // NC : changes pointId check to object == check
  407. if (edge.lPoint != null && edge.lPoint != this.point) {
  408. neighbors.push(edge.lCell.id);
  409. }
  410. else if (edge.rPoint != null && edge.rPoint != this.point){
  411. neighbors.push(edge.rCell.id);
  412. }
  413. }
  414. return neighbors;
  415. }
  416. /**
  417. Returns a bounding box of the Cell.
  418. **/
  419. public function getBbox() {
  420. var halfedges = this.halfedges,
  421. iHalfedge = halfedges.length,
  422. xmin = Math.POSITIVE_INFINITY,
  423. ymin = Math.POSITIVE_INFINITY,
  424. xmax = Math.NEGATIVE_INFINITY,
  425. ymax = Math.NEGATIVE_INFINITY;
  426. while (iHalfedge-- != 0) {
  427. var v = halfedges[iHalfedge].getStartpoint();
  428. var vx = v.x;
  429. var vy = v.y;
  430. if (vx < xmin) {xmin = vx;}
  431. if (vy < ymin) {ymin = vy;}
  432. if (vx > xmax) {xmax = vx;}
  433. if (vy > ymax) {ymax = vy;}
  434. // we dont need to take into account end point,
  435. // since each end point matches a start point
  436. }
  437. return {
  438. x: xmin,
  439. y: ymin,
  440. width: xmax-xmin,
  441. height: ymax-ymin
  442. };
  443. }
  444. /**
  445. Tests if given position is inside, on, or outside of the cell.
  446. @returns
  447. * -1: point is outside the perimeter of the cell
  448. * 0: point is on the perimeter of the cell
  449. * 1: point is inside the perimeter of the cell
  450. **/
  451. public function pointIntersection(x:Float, y:Float) {
  452. // Check if point in polygon. Since all polygons of a Voronoi
  453. // diagram are convex, then:
  454. // http://paulbourke.net/geometry/polygonmesh/
  455. // Solution 3 (2D):
  456. // "If the polygon is convex then one can consider the polygon
  457. // "as a 'path' from the first vertex. A point is on the interior
  458. // "of this polygons if it is always on the same side of all the
  459. // "line segments making up the path. ...
  460. // "(y - y0) (x1 - x0) - (x - x0) (y1 - y0)
  461. // "if it is less than 0 then P is to the right of the line segment,
  462. // "if greater than 0 it is to the left, if equal to 0 then it lies
  463. // "on the line segment"
  464. var halfedges = this.halfedges,
  465. iHalfedge = halfedges.length,
  466. p0, p1, r;
  467. while (iHalfedge-- != 0) {
  468. var halfedge = halfedges[iHalfedge];
  469. p0 = halfedge.getStartpoint();
  470. p1 = halfedge.getEndpoint();
  471. r = (y-p0.y)*(p1.x-p0.x)-(x-p0.x)*(p1.y-p0.y);
  472. if (r == 0) {
  473. return 0;
  474. }
  475. if (r > 0) {
  476. return -1;
  477. }
  478. }
  479. return 1;
  480. }
  481. }
  482. /**
  483. The resulting edge inside the Voronoi diagram.
  484. **/
  485. class Edge {
  486. /**
  487. The unique ID/Index of the edge.
  488. **/
  489. public var id : Int;
  490. /**
  491. The left-hand seed point.
  492. **/
  493. public var lPoint : Point;
  494. /**
  495. The right-hand seed point.
  496. **/
  497. public var rPoint : Point;
  498. /**
  499. The left-hand cell along the edge.
  500. **/
  501. public var lCell : Null<Cell>;
  502. /**
  503. The right-hand cell along the edge.
  504. **/
  505. public var rCell : Null<Cell>;
  506. /**
  507. The first position of the edge segment.
  508. **/
  509. public var va : Null<Point>;
  510. /**
  511. The second position of the edge segment.
  512. **/
  513. public var vb : Null<Point>;
  514. @:dox(hide) @:noCompletion
  515. public function new(lPoint, rPoint) {
  516. this.lPoint = lPoint;
  517. this.rPoint = rPoint;
  518. this.va = this.vb = null;
  519. }
  520. }
  521. /**
  522. The edge attached to a Voronoi `Cell`.
  523. **/
  524. class Halfedge {
  525. /**
  526. The seed Point of the Cell this edge is attached to.
  527. **/
  528. public var point : Point;
  529. /**
  530. The Edge this half-edge is attached to.
  531. **/
  532. public var edge : Edge;
  533. /**
  534. The perpendicular angle to the edge segment pointing in the direction of either neighboring Cell of the border.
  535. **/
  536. public var angle : Float;
  537. @:dox(hide) @:noCompletion
  538. public function new(edge, lPoint:Point, rPoint:Point) {
  539. this.point = lPoint;
  540. this.edge = edge;
  541. // 'angle' is a value to be used for properly sorting the
  542. // halfsegments counterclockwise. By convention, we will
  543. // use the angle of the line defined by the 'point to the left'
  544. // to the 'point to the right'.
  545. // However, border edges have no 'point to the right': thus we
  546. // use the angle of line perpendicular to the halfsegment (the
  547. // edge should have both end points defined in such case.)
  548. if (rPoint != null) {
  549. this.angle = Math.atan2(rPoint.y-lPoint.y, rPoint.x-lPoint.x);
  550. } else {
  551. var va = edge.va,
  552. vb = edge.vb;
  553. // rhill 2011-05-31: used to call getStartpoint()/getEndpoint(),
  554. // but for performance purpose, these are expanded in place here.
  555. this.angle = edge.lPoint == lPoint
  556. ? Math.atan2(vb.x-va.x, va.y-vb.y)
  557. : Math.atan2(va.x-vb.x, vb.y-va.y);
  558. }
  559. }
  560. /**
  561. Returns the starting point of the edge segment.
  562. **/
  563. public inline function getStartpoint() {
  564. return this.edge.lPoint == this.point ? this.edge.va : this.edge.vb;
  565. }
  566. /**
  567. Returns the end point of the edge segment.
  568. **/
  569. public inline function getEndpoint() {
  570. return this.edge.lPoint == this.point ? this.edge.vb : this.edge.va;
  571. }
  572. /**
  573. Returns the neighboring Cell of this half-edge or null if it's a border edge.
  574. **/
  575. public inline function getTarget() {
  576. return this.edge.lCell != null && this.edge.lCell.point != point ? this.edge.lCell : this.edge.rCell;
  577. }
  578. }
  579. /**
  580. The resulting diagram of the `Voronoi.compute`.
  581. **/
  582. class Diagram {
  583. /**
  584. The list of the generated cells.
  585. **/
  586. public var cells : Array<Cell>;
  587. /**
  588. The list of the diagram seed points.
  589. **/
  590. public var points : Array<Point>;
  591. /**
  592. The list of edges between diagram cells.
  593. **/
  594. public var edges : Array<Edge>;
  595. /**
  596. The duration it took to compute this diagram.
  597. **/
  598. public var execTime : Float;
  599. @:dox(hide) @:noCompletion
  600. public function new() {
  601. }
  602. }
  603. private class Beachsection extends RBNode<Beachsection> {
  604. public var point : Point;
  605. public var edge : Edge;
  606. public var circleEvent : CircleEvent;
  607. public function new() {
  608. }
  609. }
  610. private class CircleEvent extends RBNode<CircleEvent> {
  611. public var point : Point;
  612. public var arc : Beachsection;
  613. public var x : Float;
  614. public var y : Float;
  615. public var ycenter : Float;
  616. public function new() {
  617. }
  618. }
  619. /**
  620. A Steven Fortune's algorithm to compute Voronoi diagram from given set of Points and a bounding box.
  621. The implementation is a port from JS library: https://github.com/gorhill/Javascript-Voronoi
  622. **/
  623. class Voronoi {
  624. var epsilon : Float;
  625. var beachline : RBTree<Beachsection>;
  626. var vertices : Array<Point>;
  627. var edges : Array<Edge>;
  628. var cells : Array<Cell>;
  629. var beachsectionJunkyard : Array<Beachsection>;
  630. var circleEventJunkyard : Array<CircleEvent>;
  631. var circleEvents : RBTree<CircleEvent>;
  632. var firstCircleEvent : CircleEvent;
  633. var pointCell : Map<Point,Cell>;
  634. /**
  635. Create a new Voronoi algorithm calculator.
  636. **/
  637. public function new( epsilon = 1e-9 ) {
  638. this.epsilon = epsilon;
  639. this.vertices = null;
  640. this.edges = null;
  641. this.cells = null;
  642. this.beachsectionJunkyard = [];
  643. this.circleEventJunkyard = [];
  644. }
  645. /**
  646. Clean up the calculator from previous operation, and prepare for a new one.
  647. Not required to be called manually, as it's invoked by `Voronoi.compute`.
  648. **/
  649. public function reset() {
  650. if( this.beachline == null )
  651. this.beachline = new RBTree<Beachsection>();
  652. // Move leftover beachsections to the beachsection junkyard.
  653. if (this.beachline.root != null) {
  654. var beachsection = this.beachline.getFirst(this.beachline.root);
  655. while (beachsection != null) {
  656. this.beachsectionJunkyard.push(beachsection); // mark for reuse
  657. beachsection = beachsection.rbNext;
  658. }
  659. }
  660. this.beachline.root = null;
  661. if (this.circleEvents == null) {
  662. this.circleEvents = new RBTree<CircleEvent>();
  663. }
  664. pointCell = new Map();
  665. this.circleEvents.root = this.firstCircleEvent = null;
  666. this.vertices = [];
  667. this.edges = [];
  668. this.cells = [];
  669. }
  670. static inline function abs(x:Float) return x < 0 ? -x : x;
  671. inline function equalWithepsilon(a:Float,b:Float) return abs(a-b)<epsilon;
  672. inline function greaterThanWithepsilon(a:Float,b:Float) return a-b>epsilon;
  673. inline function greaterThanOrEqualWithepsilon(a:Float,b:Float) return b-a<epsilon;
  674. inline function lessThanWithepsilon(a:Float,b:Float) return b-a>epsilon;
  675. inline function lessThanOrEqualWithepsilon(a:Float,b:Float) return a-b<epsilon;
  676. function createVertex(x, y) {
  677. var v = new Point(x, y);
  678. this.vertices.push(v);
  679. return v;
  680. }
  681. // this create and add an edge to internal collection, and also create
  682. // two halfedges which are added to each point's counterclockwise array
  683. // of halfedges.
  684. function createEdge(lPoint, rPoint, va = null, vb = null) {
  685. var edge = new Edge(lPoint, rPoint);
  686. this.edges.push(edge);
  687. if (va != null) {
  688. this.setEdgeStartpoint(edge, lPoint, rPoint, va);
  689. }
  690. if (vb != null) {
  691. this.setEdgeEndpoint(edge, lPoint, rPoint, vb);
  692. }
  693. pointCell.get(lPoint).halfedges.push(new Halfedge(edge, lPoint, rPoint));
  694. pointCell.get(rPoint).halfedges.push(new Halfedge(edge, rPoint, lPoint));
  695. return edge;
  696. }
  697. function createBorderEdge(lPoint, va, vb) {
  698. var edge = new Edge(lPoint, null);
  699. edge.va = va;
  700. edge.vb = vb;
  701. this.edges.push(edge);
  702. return edge;
  703. }
  704. function setEdgeStartpoint(edge:Edge, lPoint, rPoint, vertex) {
  705. if (edge.va == null && edge.vb == null) {
  706. edge.va = vertex;
  707. edge.lPoint = lPoint;
  708. edge.rPoint = rPoint;
  709. }
  710. else if (edge.lPoint == rPoint) {
  711. edge.vb = vertex;
  712. }
  713. else {
  714. edge.va = vertex;
  715. }
  716. }
  717. function setEdgeEndpoint(edge, lPoint, rPoint, vertex) {
  718. this.setEdgeStartpoint(edge, rPoint, lPoint, vertex);
  719. }
  720. // rhill 2011-06-02: A lot of Beachsection instanciations
  721. // occur during the computation of the Voronoi diagram,
  722. // somewhere between the number of points and twice the
  723. // number of points, while the number of Beachsections on the
  724. // beachline at any given time is comparatively low. For this
  725. // reason, we reuse already created Beachsections, in order
  726. // to avoid new memory allocation. This resulted in a measurable
  727. // performance gain.
  728. function createBeachsection(point:Point) {
  729. var beachsection = this.beachsectionJunkyard.pop();
  730. if ( beachsection == null )
  731. beachsection = new Beachsection();
  732. beachsection.point = point;
  733. return beachsection;
  734. }
  735. // calculate the left break point of a particular beach section,
  736. // given a particular sweep line
  737. function leftBreakPoint(arc:Beachsection, directrix:Float) {
  738. // http://en.wikipedia.org/wiki/Parabola
  739. // http://en.wikipedia.org/wiki/Quadratic_equation
  740. // h1 = x1,
  741. // k1 = (y1+directrix)/2,
  742. // h2 = x2,
  743. // k2 = (y2+directrix)/2,
  744. // p1 = k1-directrix,
  745. // a1 = 1/(4*p1),
  746. // b1 = -h1/(2*p1),
  747. // c1 = h1*h1/(4*p1)+k1,
  748. // p2 = k2-directrix,
  749. // a2 = 1/(4*p2),
  750. // b2 = -h2/(2*p2),
  751. // c2 = h2*h2/(4*p2)+k2,
  752. // x = (-(b2-b1) + Math.sqrt((b2-b1)*(b2-b1) - 4*(a2-a1)*(c2-c1))) / (2*(a2-a1))
  753. // When x1 become the x-origin:
  754. // h1 = 0,
  755. // k1 = (y1+directrix)/2,
  756. // h2 = x2-x1,
  757. // k2 = (y2+directrix)/2,
  758. // p1 = k1-directrix,
  759. // a1 = 1/(4*p1),
  760. // b1 = 0,
  761. // c1 = k1,
  762. // p2 = k2-directrix,
  763. // a2 = 1/(4*p2),
  764. // b2 = -h2/(2*p2),
  765. // c2 = h2*h2/(4*p2)+k2,
  766. // x = (-b2 + Math.sqrt(b2*b2 - 4*(a2-a1)*(c2-k1))) / (2*(a2-a1)) + x1
  767. // change code below at your own risk: care has been taken to
  768. // reduce errors due to computers' finite arithmetic precision.
  769. // Maybe can still be improved, will see if any more of this
  770. // kind of errors pop up again.
  771. var point = arc.point,
  772. rfocx = point.x,
  773. rfocy = point.y,
  774. pby2 = rfocy-directrix;
  775. // parabola in degenerate case where focus is on directrix
  776. if (pby2 == 0) {
  777. return rfocx;
  778. }
  779. var lArc = arc.rbPrevious;
  780. if (lArc == null) {
  781. return Math.NEGATIVE_INFINITY;
  782. }
  783. point = lArc.point;
  784. var lfocx = point.x,
  785. lfocy = point.y,
  786. plby2 = lfocy-directrix;
  787. // parabola in degenerate case where focus is on directrix
  788. if (plby2 == 0) {
  789. return lfocx;
  790. }
  791. var hl = lfocx-rfocx,
  792. aby2 = 1/pby2-1/plby2,
  793. b = hl/plby2;
  794. if (aby2 != 0) {
  795. return (-b+Math.sqrt(b*b-2*aby2*(hl*hl/(-2*plby2)-lfocy+plby2/2+rfocy-pby2/2)))/aby2+rfocx;
  796. }
  797. // both parabolas have same distance to directrix, thus break point is midway
  798. return (rfocx+lfocx)/2;
  799. }
  800. // calculate the right break point of a particular beach section,
  801. // given a particular directrix
  802. function rightBreakPoint(arc:Beachsection, directrix) {
  803. var rArc = arc.rbNext;
  804. if (rArc != null) {
  805. return this.leftBreakPoint(rArc, directrix);
  806. }
  807. var point = arc.point;
  808. return point.y == directrix ? point.x : Math.POSITIVE_INFINITY;
  809. }
  810. function detachBeachsection(beachsection) {
  811. this.detachCircleEvent(beachsection); // detach potentially attached circle event
  812. this.beachline.rbRemoveNode(beachsection); // remove from RB-tree
  813. this.beachsectionJunkyard.push(beachsection); // mark for reuse
  814. }
  815. function removeBeachsection(beachsection:Beachsection) {
  816. var circle = beachsection.circleEvent,
  817. x = circle.x,
  818. y = circle.ycenter,
  819. vertex = this.createVertex(x, y),
  820. previous = beachsection.rbPrevious,
  821. next = beachsection.rbNext,
  822. disappearingTransitions = [beachsection];
  823. // remove collapsed beachsection from beachline
  824. this.detachBeachsection(beachsection);
  825. // there could be more than one empty arc at the deletion point, this
  826. // happens when more than two edges are linked by the same vertex,
  827. // so we will collect all those edges by looking up both sides of
  828. // the deletion point.
  829. // by the way, there is *always* a predecessor/successor to any collapsed
  830. // beach section, it's just impossible to have a collapsing first/last
  831. // beach sections on the beachline, since they obviously are unconstrained
  832. // on their left/right side.
  833. // look left
  834. var lArc = previous;
  835. while (lArc.circleEvent != null && abs(x-lArc.circleEvent.x)<epsilon && abs(y-lArc.circleEvent.ycenter)<epsilon) {
  836. previous = lArc.rbPrevious;
  837. disappearingTransitions.unshift(lArc);
  838. this.detachBeachsection(lArc); // mark for reuse
  839. lArc = previous;
  840. }
  841. // even though it is not disappearing, I will also add the beach section
  842. // immediately to the left of the left-most collapsed beach section, for
  843. // convenience, since we need to refer to it later as this beach section
  844. // is the 'left' point of an edge for which a start point is set.
  845. disappearingTransitions.unshift(lArc);
  846. this.detachCircleEvent(lArc);
  847. // look right
  848. var rArc = next;
  849. while (rArc.circleEvent != null && abs(x-rArc.circleEvent.x)<epsilon && abs(y-rArc.circleEvent.ycenter)<epsilon) {
  850. next = rArc.rbNext;
  851. disappearingTransitions.push(rArc);
  852. this.detachBeachsection(rArc); // mark for reuse
  853. rArc = next;
  854. }
  855. // we also have to add the beach section immediately to the right of the
  856. // right-most collapsed beach section, since there is also a disappearing
  857. // transition representing an edge's start point on its left.
  858. disappearingTransitions.push(rArc);
  859. this.detachCircleEvent(rArc);
  860. // walk through all the disappearing transitions between beach sections and
  861. // set the start point of their (implied) edge.
  862. var nArcs = disappearingTransitions.length,
  863. iArc;
  864. for( iArc in 1...nArcs ) {
  865. rArc = disappearingTransitions[iArc];
  866. lArc = disappearingTransitions[iArc-1];
  867. this.setEdgeStartpoint(rArc.edge, lArc.point, rArc.point, vertex);
  868. }
  869. // create a new edge as we have now a new transition between
  870. // two beach sections which were previously not adjacent.
  871. // since this edge appears as a new vertex is defined, the vertex
  872. // actually define an end point of the edge (relative to the point
  873. // on the left)
  874. lArc = disappearingTransitions[0];
  875. rArc = disappearingTransitions[nArcs-1];
  876. rArc.edge = this.createEdge(lArc.point, rArc.point, null, vertex);
  877. // create circle events if any for beach sections left in the beachline
  878. // adjacent to collapsed sections
  879. this.attachCircleEvent(lArc);
  880. this.attachCircleEvent(rArc);
  881. }
  882. function addBeachsection(point:Point) {
  883. var x = point.x,
  884. directrix = point.y;
  885. // find the left and right beach sections which will surround the newly
  886. // created beach section.
  887. // rhill 2011-06-01: This loop is one of the most often executed,
  888. // hence we expand in-place the comparison-against-epsilon calls.
  889. var lArc = null, rArc = null,
  890. dxl, dxr,
  891. node = this.beachline.root;
  892. while (node != null) {
  893. dxl = this.leftBreakPoint(node,directrix)-x;
  894. // x lessThanWithepsilon xl => falls somewhere before the left edge of the beachsection
  895. if (dxl > epsilon) {
  896. // this case should never happen
  897. // if (!node.rbLeft) {
  898. // rArc = node.rbLeft;
  899. // break;
  900. // }
  901. node = node.rbLeft;
  902. }
  903. else {
  904. dxr = x-this.rightBreakPoint(node,directrix);
  905. // x greaterThanWithepsilon xr => falls somewhere after the right edge of the beachsection
  906. if (dxr > epsilon) {
  907. if (node.rbRight == null) {
  908. lArc = node;
  909. break;
  910. }
  911. node = node.rbRight;
  912. }
  913. else {
  914. // x equalWithepsilon xl => falls exactly on the left edge of the beachsection
  915. if (dxl > -epsilon) {
  916. lArc = node.rbPrevious;
  917. rArc = node;
  918. }
  919. // x equalWithepsilon xr => falls exactly on the right edge of the beachsection
  920. else if (dxr > -epsilon) {
  921. lArc = node;
  922. rArc = node.rbNext;
  923. }
  924. // falls exactly somewhere in the middle of the beachsection
  925. else {
  926. lArc = rArc = node;
  927. }
  928. break;
  929. }
  930. }
  931. }
  932. // at this point, keep in mind that lArc and/or rArc could be
  933. // undefined or null.
  934. // create a new beach section object for the point and add it to RB-tree
  935. var newArc = this.createBeachsection(point);
  936. this.beachline.rbInsertSuccessor(lArc, newArc);
  937. // cases:
  938. //
  939. // [null,null]
  940. // least likely case: new beach section is the first beach section on the
  941. // beachline.
  942. // This case means:
  943. // no new transition appears
  944. // no collapsing beach section
  945. // new beachsection become root of the RB-tree
  946. if (lArc == null && rArc == null) {
  947. return;
  948. }
  949. // [lArc,rArc] where lArc == rArc
  950. // most likely case: new beach section split an existing beach
  951. // section.
  952. // This case means:
  953. // one new transition appears
  954. // the left and right beach section might be collapsing as a result
  955. // two new nodes added to the RB-tree
  956. if (lArc == rArc) {
  957. // invalidate circle event of split beach section
  958. this.detachCircleEvent(lArc);
  959. // split the beach section into two separate beach sections
  960. rArc = this.createBeachsection(lArc.point);
  961. this.beachline.rbInsertSuccessor(newArc, rArc);
  962. // since we have a new transition between two beach sections,
  963. // a new edge is born
  964. newArc.edge = rArc.edge = this.createEdge(lArc.point, newArc.point);
  965. // check whether the left and right beach sections are collapsing
  966. // and if so create circle events, to be notified when the point of
  967. // collapse is reached.
  968. this.attachCircleEvent(lArc);
  969. this.attachCircleEvent(rArc);
  970. return;
  971. }
  972. // [lArc,null]
  973. // even less likely case: new beach section is the *last* beach section
  974. // on the beachline -- this can happen *only* if *all* the previous beach
  975. // sections currently on the beachline share the same y value as
  976. // the new beach section.
  977. // This case means:
  978. // one new transition appears
  979. // no collapsing beach section as a result
  980. // new beach section become right-most node of the RB-tree
  981. if (lArc != null && rArc == null) {
  982. newArc.edge = this.createEdge(lArc.point,newArc.point);
  983. return;
  984. }
  985. // [null,rArc]
  986. // impossible case: because points are strictly processed from top to bottom,
  987. // and left to right, which guarantees that there will always be a beach section
  988. // on the left -- except of course when there are no beach section at all on
  989. // the beach line, which case was handled above.
  990. // rhill 2011-06-02: No point testing in non-debug version
  991. //if (!lArc && rArc) {
  992. // throw "Voronoi.addBeachsection(): What is this I don't even";
  993. // }
  994. // [lArc,rArc] where lArc != rArc
  995. // somewhat less likely case: new beach section falls *exactly* in between two
  996. // existing beach sections
  997. // This case means:
  998. // one transition disappears
  999. // two new transitions appear
  1000. // the left and right beach section might be collapsing as a result
  1001. // only one new node added to the RB-tree
  1002. if (lArc != rArc) {
  1003. // invalidate circle events of left and right points
  1004. this.detachCircleEvent(lArc);
  1005. this.detachCircleEvent(rArc);
  1006. // an existing transition disappears, meaning a vertex is defined at
  1007. // the disappearance point.
  1008. // since the disappearance is caused by the new beachsection, the
  1009. // vertex is at the center of the circumscribed circle of the left,
  1010. // new and right beachsections.
  1011. // http://mathforum.org/library/drmath/view/55002.html
  1012. // Except that I bring the origin at A to simplify
  1013. // calculation
  1014. var lPoint = lArc.point,
  1015. ax = lPoint.x,
  1016. ay = lPoint.y,
  1017. bx=point.x-ax,
  1018. by=point.y-ay,
  1019. rPoint = rArc.point,
  1020. cx=rPoint.x-ax,
  1021. cy=rPoint.y-ay,
  1022. d=2*(bx*cy-by*cx),
  1023. hb=bx*bx+by*by,
  1024. hc=cx*cx+cy*cy,
  1025. vertex = this.createVertex((cy*hb-by*hc)/d+ax, (bx*hc-cx*hb)/d+ay);
  1026. // one transition disappear
  1027. this.setEdgeStartpoint(rArc.edge, lPoint, rPoint, vertex);
  1028. // two new transitions appear at the new vertex location
  1029. newArc.edge = this.createEdge(lPoint, point, null, vertex);
  1030. rArc.edge = this.createEdge(point, rPoint, null, vertex);
  1031. // check whether the left and right beach sections are collapsing
  1032. // and if so create circle events, to handle the point of collapse.
  1033. this.attachCircleEvent(lArc);
  1034. this.attachCircleEvent(rArc);
  1035. return;
  1036. }
  1037. }
  1038. function attachCircleEvent(arc:Beachsection) {
  1039. var lArc = arc.rbPrevious,
  1040. rArc = arc.rbNext;
  1041. if (lArc == null || rArc == null) {return;} // does that ever happen?
  1042. var lPoint = lArc.point,
  1043. cPoint = arc.point,
  1044. rPoint = rArc.point;
  1045. // If point of left beachsection is same as point of
  1046. // right beachsection, there can't be convergence
  1047. if (lPoint==rPoint) {return;}
  1048. // Find the circumscribed circle for the three points associated
  1049. // with the beachsection triplet.
  1050. // rhill 2011-05-26: It is more efficient to calculate in-place
  1051. // rather than getting the resulting circumscribed circle from an
  1052. // object returned by calling Voronoi.circumcircle()
  1053. // http://mathforum.org/library/drmath/view/55002.html
  1054. // Except that I bring the origin at cPoint to simplify calculations.
  1055. // The bottom-most part of the circumcircle is our Fortune 'circle
  1056. // event', and its center is a vertex potentially part of the final
  1057. // Voronoi diagram.
  1058. var bx = cPoint.x,
  1059. by = cPoint.y,
  1060. ax = lPoint.x-bx,
  1061. ay = lPoint.y-by,
  1062. cx = rPoint.x-bx,
  1063. cy = rPoint.y-by;
  1064. // If points l->c->r are clockwise, then center beach section does not
  1065. // collapse, hence it can't end up as a vertex (we reuse 'd' here, which
  1066. // sign is reverse of the orientation, hence we reverse the test.
  1067. // http://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon
  1068. // rhill 2011-05-21: Nasty finite precision error which caused circumcircle() to
  1069. // return infinites: 1e-12 seems to fix the problem.
  1070. var d = 2*(ax*cy-ay*cx);
  1071. if (d >= -2e-12){return;}
  1072. var ha = ax*ax+ay*ay,
  1073. hc = cx*cx+cy*cy,
  1074. x = (cy*ha-ay*hc)/d,
  1075. y = (ax*hc-cx*ha)/d,
  1076. ycenter = y+by;
  1077. // Important: ybottom should always be under or at sweep, so no need
  1078. // to waste CPU cycles by checking
  1079. // recycle circle event object if possible
  1080. var circleEvent = this.circleEventJunkyard.pop();
  1081. if (circleEvent == null) {
  1082. circleEvent = new CircleEvent();
  1083. }
  1084. circleEvent.arc = arc;
  1085. circleEvent.point = cPoint;
  1086. circleEvent.x = x+bx;
  1087. circleEvent.y = ycenter+Math.sqrt(x*x+y*y); // y bottom
  1088. circleEvent.ycenter = ycenter;
  1089. arc.circleEvent = circleEvent;
  1090. // find insertion point in RB-tree: circle events are ordered from
  1091. // smallest to largest
  1092. var predecessor = null,
  1093. node = this.circleEvents.root;
  1094. while (node != null) {
  1095. if (circleEvent.y < node.y || (circleEvent.y == node.y && circleEvent.x <= node.x)) {
  1096. if (node.rbLeft != null) {
  1097. node = node.rbLeft;
  1098. }
  1099. else {
  1100. predecessor = node.rbPrevious;
  1101. break;
  1102. }
  1103. }
  1104. else {
  1105. if (node.rbRight != null) {
  1106. node = node.rbRight;
  1107. }
  1108. else {
  1109. predecessor = node;
  1110. break;
  1111. }
  1112. }
  1113. }
  1114. this.circleEvents.rbInsertSuccessor(predecessor, circleEvent);
  1115. if (predecessor == null) {
  1116. this.firstCircleEvent = circleEvent;
  1117. }
  1118. }
  1119. function detachCircleEvent(arc:Beachsection) {
  1120. var circle = arc.circleEvent;
  1121. if (circle != null) {
  1122. if (circle.rbPrevious == null) {
  1123. this.firstCircleEvent = circle.rbNext;
  1124. }
  1125. this.circleEvents.rbRemoveNode(circle); // remove from RB-tree
  1126. this.circleEventJunkyard.push(circle);
  1127. arc.circleEvent = null;
  1128. }
  1129. }
  1130. // ---------------------------------------------------------------------------
  1131. // Diagram completion methods
  1132. // connect dangling edges (not if a cursory test tells us
  1133. // it is not going to be visible.
  1134. // return value:
  1135. // false: the dangling endpoint couldn't be connected
  1136. // true: the dangling endpoint could be connected
  1137. function connectEdge(edge:Edge, bbox:Bounds) {
  1138. // skip if end point already connected
  1139. var vb = edge.vb;
  1140. if (vb != null) {return true;}
  1141. // make local copy for performance purpose
  1142. var va = edge.va,
  1143. xl = bbox.xMin,
  1144. xr = bbox.xMax,
  1145. yt = bbox.yMin,
  1146. yb = bbox.yMax,
  1147. lPoint = edge.lPoint,
  1148. rPoint = edge.rPoint,
  1149. lx = lPoint.x,
  1150. ly = lPoint.y,
  1151. rx = rPoint.x,
  1152. ry = rPoint.y,
  1153. fx = (lx+rx)/2,
  1154. fy = (ly+ry)/2,
  1155. fm = 0., fb = 0.;
  1156. pointCell.get(lPoint).closeMe = true;
  1157. pointCell.get(rPoint).closeMe = true;
  1158. // get the line equation of the bisector if line is not vertical
  1159. if (ry != ly) {
  1160. fm = (lx-rx)/(ry-ly);
  1161. fb = fy-fm*fx;
  1162. }
  1163. // remember, direction of line (relative to left point):
  1164. // upward: left.x < right.x
  1165. // downward: left.x > right.x
  1166. // horizontal: left.x == right.x
  1167. // upward: left.x < right.x
  1168. // rightward: left.y < right.y
  1169. // leftward: left.y > right.y
  1170. // vertical: left.y == right.y
  1171. // depending on the direction, find the best side of the
  1172. // bounding box to use to determine a reasonable start point
  1173. // special case: vertical line
  1174. if (ry == ly) {
  1175. // doesn't intersect with viewport
  1176. if (fx < xl || fx >= xr) {return false;}
  1177. // downward
  1178. if (lx > rx) {
  1179. if (va == null || va.y < yt) {
  1180. va = this.createVertex(fx, yt);
  1181. }
  1182. else if (va.y >= yb) {
  1183. return false;
  1184. }
  1185. vb = this.createVertex(fx, yb);
  1186. }
  1187. // upward
  1188. else {
  1189. if (va == null || va.y > yb) {
  1190. va = this.createVertex(fx, yb);
  1191. }
  1192. else if (va.y < yt) {
  1193. return false;
  1194. }
  1195. vb = this.createVertex(fx, yt);
  1196. }
  1197. }
  1198. // closer to vertical than horizontal, connect start point to the
  1199. // top or bottom side of the bounding box
  1200. else if (fm < -1 || fm > 1) {
  1201. // downward
  1202. if (lx > rx) {
  1203. if (va == null || va.y < yt) {
  1204. va = this.createVertex((yt-fb)/fm, yt);
  1205. }
  1206. else if (va.y >= yb) {
  1207. return false;
  1208. }
  1209. vb = this.createVertex((yb-fb)/fm, yb);
  1210. }
  1211. // upward
  1212. else {
  1213. if (va == null || va.y > yb) {
  1214. va = this.createVertex((yb-fb)/fm, yb);
  1215. }
  1216. else if (va.y < yt) {
  1217. return false;
  1218. }
  1219. vb = this.createVertex((yt-fb)/fm, yt);
  1220. }
  1221. }
  1222. // closer to horizontal than vertical, connect start point to the
  1223. // left or right side of the bounding box
  1224. else {
  1225. // rightward
  1226. if (ly < ry) {
  1227. if (va == null || va.x < xl) {
  1228. va = this.createVertex(xl, fm*xl+fb);
  1229. }
  1230. else if (va.x >= xr) {
  1231. return false;
  1232. }
  1233. vb = this.createVertex(xr, fm*xr+fb);
  1234. }
  1235. // leftward
  1236. else {
  1237. if (va == null || va.x > xr) {
  1238. va = this.createVertex(xr, fm*xr+fb);
  1239. }
  1240. else if (va.x < xl) {
  1241. return false;
  1242. }
  1243. vb = this.createVertex(xl, fm*xl+fb);
  1244. }
  1245. }
  1246. edge.va = va;
  1247. edge.vb = vb;
  1248. return true;
  1249. }
  1250. // line-clipping code taken from:
  1251. // Liang-Barsky function by Daniel White
  1252. // http://www.skytopia.com/project/articles/compsci/clipping.html
  1253. // Thanks!
  1254. // A bit modified to minimize code paths
  1255. function clipEdge(edge:Edge, bbox:Bounds) {
  1256. var ax = edge.va.x,
  1257. ay = edge.va.y,
  1258. bx = edge.vb.x,
  1259. by = edge.vb.y,
  1260. t0 = 0.,
  1261. t1 = 1.,
  1262. dx = bx-ax,
  1263. dy = by-ay;
  1264. // left
  1265. var q = ax-bbox.xMin;
  1266. if (dx==0 && q<0) {return false;}
  1267. var r = -q/dx;
  1268. if (dx<0) {
  1269. if (r<t0) {return false;}
  1270. if (r<t1) {t1=r;}
  1271. }
  1272. else if (dx>0) {
  1273. if (r>t1) {return false;}
  1274. if (r>t0) {t0=r;}
  1275. }
  1276. // right
  1277. q = bbox.xMax-ax;
  1278. if (dx==0 && q<0) {return false;}
  1279. r = q/dx;
  1280. if (dx<0) {
  1281. if (r>t1) {return false;}
  1282. if (r>t0) {t0=r;}
  1283. }
  1284. else if (dx>0) {
  1285. if (r<t0) {return false;}
  1286. if (r<t1) {t1=r;}
  1287. }
  1288. // top
  1289. q = ay-bbox.yMin;
  1290. if (dy==0 && q<0) {return false;}
  1291. r = -q/dy;
  1292. if (dy<0) {
  1293. if (r<t0) {return false;}
  1294. if (r<t1) {t1=r;}
  1295. }
  1296. else if (dy>0) {
  1297. if (r>t1) {return false;}
  1298. if (r>t0) {t0=r;}
  1299. }
  1300. // bottom
  1301. q = bbox.yMax-ay;
  1302. if (dy==0 && q<0) {return false;}
  1303. r = q/dy;
  1304. if (dy<0) {
  1305. if (r>t1) {return false;}
  1306. if (r>t0) {t0=r;}
  1307. }
  1308. else if (dy>0) {
  1309. if (r<t0) {return false;}
  1310. if (r<t1) {t1=r;}
  1311. }
  1312. // if we reach this point, Voronoi edge is within bbox
  1313. // if t0 > 0, va needs to change
  1314. // rhill 2011-06-03: we need to create a new vertex rather
  1315. // than modifying the existing one, since the existing
  1316. // one is likely shared with at least another edge
  1317. if (t0 > 0) {
  1318. edge.va = this.createVertex(ax+t0*dx, ay+t0*dy);
  1319. }
  1320. // if t1 < 1, vb needs to change
  1321. // rhill 2011-06-03: we need to create a new vertex rather
  1322. // than modifying the existing one, since the existing
  1323. // one is likely shared with at least another edge
  1324. if (t1 < 1) {
  1325. edge.vb = this.createVertex(ax+t1*dx, ay+t1*dy);
  1326. }
  1327. // va and/or vb were clipped, thus we will need to close
  1328. // cells which use this edge.
  1329. if ( t0 > 0 || t1 < 1 ) {
  1330. pointCell.get(edge.lPoint).closeMe = true;
  1331. pointCell.get(edge.rPoint).closeMe = true;
  1332. }
  1333. return true;
  1334. }
  1335. // Connect/cut edges at bounding box
  1336. function clipEdges(bbox:Bounds) {
  1337. // connect all dangling edges to bounding box
  1338. // or get rid of them if it can't be done
  1339. var edges = this.edges,
  1340. iEdge = edges.length,
  1341. edge;
  1342. // iterate backward so we can splice safely
  1343. while (iEdge-- != 0) {
  1344. edge = edges[iEdge];
  1345. // edge is removed if:
  1346. // it is wholly outside the bounding box
  1347. // it is actually a point rather than a line
  1348. if (!this.connectEdge(edge, bbox) || !this.clipEdge(edge, bbox) || (abs(edge.va.x-edge.vb.x)<epsilon && abs(edge.va.y-edge.vb.y)<epsilon)) {
  1349. edge.va = edge.vb = null;
  1350. edges.splice(iEdge,1);
  1351. }
  1352. }
  1353. }
  1354. // Close the cells.
  1355. // The cells are bound by the supplied bounding box.
  1356. // Each cell refers to its associated point, and a list
  1357. // of halfedges ordered counterclockwise.
  1358. function closeCells(bbox:Bounds) {
  1359. // prune, order halfedges, then add missing ones
  1360. // required to close cells
  1361. var xl = bbox.xMin,
  1362. xr = bbox.xMax,
  1363. yt = bbox.yMin,
  1364. yb = bbox.yMax,
  1365. cells = this.cells,
  1366. iCell = cells.length,
  1367. cell,
  1368. iLeft,
  1369. halfedges, nHalfedges,
  1370. edge,
  1371. lastBorderSegment,
  1372. va, vb, vz = null;
  1373. while (iCell-- != 0) {
  1374. cell = cells[iCell];
  1375. // trim non fully-defined halfedges and sort them counterclockwise
  1376. if (cell.prepare() == 0) continue;
  1377. if (!cell.closeMe) continue;
  1378. // close open cells
  1379. // step 1: find first 'unclosed' point, if any.
  1380. // an 'unclosed' point will be the end point of a halfedge which
  1381. // does not match the start point of the following halfedge
  1382. halfedges = cell.halfedges;
  1383. nHalfedges = halfedges.length;
  1384. // special case: only one point, in which case, the viewport is the cell
  1385. // ...
  1386. // all other cases
  1387. iLeft = 0;
  1388. while (iLeft < nHalfedges) {
  1389. va = halfedges[iLeft].getEndpoint();
  1390. vz = halfedges[(iLeft+1) % nHalfedges].getStartpoint();
  1391. // if end point is not equal to start point, we need to add the missing
  1392. // halfedge(s) to close the cell
  1393. if (abs(va.x-vz.x)>=epsilon || abs(va.y-vz.y)>=epsilon) {
  1394. // rhill 2013-12-02:
  1395. // "Holes" in the halfedges are not necessarily always adjacent.
  1396. // https://github.com/gorhill/Javascript-Voronoi/issues/16
  1397. // find entry point:
  1398. do {
  1399. // walk downward along left side
  1400. if (this.equalWithepsilon(va.x,xl) && this.lessThanWithepsilon(va.y,yb)) {
  1401. lastBorderSegment = this.equalWithepsilon(vz.x,xl);
  1402. vb = this.createVertex(xl, lastBorderSegment ? vz.y : yb);
  1403. edge = this.createBorderEdge(cell.point, va, vb);
  1404. iLeft++;
  1405. halfedges.insert(iLeft, new Halfedge(edge, cell.point, null));
  1406. nHalfedges++;
  1407. if ( lastBorderSegment ) { break; }
  1408. va = vb;
  1409. // fall through
  1410. }
  1411. if (this.equalWithepsilon(va.y,yb) && this.lessThanWithepsilon(va.x,xr)) {
  1412. lastBorderSegment = this.equalWithepsilon(vz.y,yb);
  1413. vb = this.createVertex(lastBorderSegment ? vz.x : xr, yb);
  1414. edge = this.createBorderEdge(cell.point, va, vb);
  1415. iLeft++;
  1416. halfedges.insert(iLeft, new Halfedge(edge, cell.point, null));
  1417. nHalfedges++;
  1418. if ( lastBorderSegment ) { break; }
  1419. va = vb;
  1420. // fall through
  1421. }
  1422. if (this.equalWithepsilon(va.x,xr) && this.greaterThanWithepsilon(va.y,yt)) {
  1423. lastBorderSegment = this.equalWithepsilon(vz.x,xr);
  1424. vb = this.createVertex(xr, lastBorderSegment ? vz.y : yt);
  1425. edge = this.createBorderEdge(cell.point, va, vb);
  1426. iLeft++;
  1427. halfedges.insert(iLeft, new Halfedge(edge, cell.point, null));
  1428. nHalfedges++;
  1429. if ( lastBorderSegment ) { break; }
  1430. va = vb;
  1431. // fall through
  1432. }
  1433. if (this.equalWithepsilon(va.y,yt) && this.greaterThanWithepsilon(va.x,xl)) {
  1434. lastBorderSegment = this.equalWithepsilon(vz.y,yt);
  1435. vb = this.createVertex(lastBorderSegment ? vz.x : xl, yt);
  1436. edge = this.createBorderEdge(cell.point, va, vb);
  1437. iLeft++;
  1438. halfedges.insert(iLeft, new Halfedge(edge, cell.point, null));
  1439. nHalfedges++;
  1440. if ( lastBorderSegment ) { break; }
  1441. va = vb;
  1442. // fall through
  1443. // walk downward along left side
  1444. lastBorderSegment = this.equalWithepsilon(vz.x,xl);
  1445. vb = this.createVertex(xl, lastBorderSegment ? vz.y : yb);
  1446. edge = this.createBorderEdge(cell.point, va, vb);
  1447. iLeft++;
  1448. halfedges.insert(iLeft, new Halfedge(edge, cell.point, null));
  1449. nHalfedges++;
  1450. if ( lastBorderSegment ) { break; }
  1451. va = vb;
  1452. // fall through
  1453. // walk rightward along bottom side
  1454. lastBorderSegment = this.equalWithepsilon(vz.y,yb);
  1455. vb = this.createVertex(lastBorderSegment ? vz.x : xr, yb);
  1456. edge = this.createBorderEdge(cell.point, va, vb);
  1457. iLeft++;
  1458. halfedges.insert(iLeft, new Halfedge(edge, cell.point, null));
  1459. nHalfedges++;
  1460. if ( lastBorderSegment ) { break; }
  1461. va = vb;
  1462. // fall through
  1463. // walk upward along right side
  1464. lastBorderSegment = this.equalWithepsilon(vz.x,xr);
  1465. vb = this.createVertex(xr, lastBorderSegment ? vz.y : yt);
  1466. edge = this.createBorderEdge(cell.point, va, vb);
  1467. iLeft++;
  1468. halfedges.insert(iLeft, new Halfedge(edge, cell.point, null));
  1469. nHalfedges++;
  1470. if ( lastBorderSegment ) { break; }
  1471. // fall through
  1472. }
  1473. throw "Voronoi.closeCells() > this makes no sense!";
  1474. } while (false);
  1475. }
  1476. iLeft++;
  1477. }
  1478. cell.closeMe = false;
  1479. }
  1480. }
  1481. // ---------------------------------------------------------------------------
  1482. // Top-level Fortune loop
  1483. static function sortByXY(a:Point, b:Point) {
  1484. var r = b.y - a.y;
  1485. return r < 0 ? -1 : (r > 0 ? 1 : (b.x > a.x ? 1 : b.x < a.x ? -1 : 0));
  1486. }
  1487. // rhill 2011-05-19:
  1488. // Voronoi points are kept client-side now, to allow
  1489. // user to freely modify content. At compute time,
  1490. // *references* to points are copied locally.
  1491. /**
  1492. Compute the Voronoi diagram based on given list of points and bounding box.
  1493. **/
  1494. public function compute(points:Array<Point>, bbox:Bounds) {
  1495. // to measure execution time
  1496. var startTime = haxe.Timer.stamp();
  1497. // init internal state
  1498. this.reset();
  1499. // Initialize point event queue
  1500. var pointEvents = points.slice(0);
  1501. pointEvents.sort(sortByXY);
  1502. // process queue
  1503. var point = pointEvents.pop(),
  1504. pointid = 0,
  1505. xpointx = Math.NEGATIVE_INFINITY, // to avoid duplicate points
  1506. xpointy = Math.NEGATIVE_INFINITY,
  1507. cells = this.cells,
  1508. circle;
  1509. // main loop
  1510. while( true ) {
  1511. // we need to figure whether we handle a point or circle event
  1512. // for this we find out if there is a point event and it is
  1513. // 'earlier' than the circle event
  1514. circle = this.firstCircleEvent;
  1515. // add beach section
  1516. if (point != null && (circle == null || point.y < circle.y || (point.y == circle.y && point.x < circle.x))) {
  1517. // only if point is not a duplicate
  1518. if (point.x != xpointx || point.y != xpointy) {
  1519. // first create cell for new point
  1520. var c = new Cell(pointid, point);
  1521. cells[pointid] = c;
  1522. pointCell.set(point, c);
  1523. pointid++;
  1524. // then create a beachsection for that point
  1525. this.addBeachsection(point);
  1526. // remember last point coords to detect duplicate
  1527. xpointy = point.y;
  1528. xpointx = point.x;
  1529. }
  1530. point = pointEvents.pop();
  1531. }
  1532. // remove beach section
  1533. else if (circle != null) {
  1534. this.removeBeachsection(circle.arc);
  1535. }
  1536. // all done, quit
  1537. else
  1538. break;
  1539. }
  1540. // wrapping-up:
  1541. // connect dangling edges to bounding box
  1542. // cut edges as per bounding box
  1543. // discard edges completely outside bounding box
  1544. // discard edges which are point-like
  1545. this.clipEdges(bbox);
  1546. // add missing edges in order to close opened cells
  1547. this.closeCells(bbox);
  1548. var eid = 0;
  1549. for( e in edges ) {
  1550. e.id = eid++;
  1551. e.lCell = e.lPoint == null ? null : pointCell.get(e.lPoint);
  1552. e.rCell = e.rPoint == null ? null : pointCell.get(e.rPoint);
  1553. }
  1554. // to measure execution time
  1555. var stopTime = haxe.Timer.stamp();
  1556. // prepare return values
  1557. var diagram = new Diagram();
  1558. diagram.cells = this.cells;
  1559. diagram.edges = this.edges;
  1560. diagram.points = this.vertices;
  1561. diagram.execTime = stopTime - startTime;
  1562. // clean up
  1563. this.reset();
  1564. return diagram;
  1565. }
  1566. }