/src/com/nodename/Delaunay/Voronoi.as

http://github.com/m0ose/ambient-pixel-action-script · ActionScript · 418 lines · 323 code · 51 blank · 44 comment · 42 complexity · 1560012374d5b85e19c46df404fd7801 MD5 · raw file

  1. /*
  2. * The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T
  3. * Bell Laboratories.
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose without fee is hereby granted, provided that this entire notice
  6. * is included in all copies of any software which is or includes a copy
  7. * or modification of this software and in all copies of the supporting
  8. * documentation for such software.
  9. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
  10. * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
  11. * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
  12. * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
  13. */
  14. package com.nodename.Delaunay
  15. {
  16. import com.nodename.geom.Circle;
  17. import com.nodename.geom.LineSegment;
  18. import flash.display.BitmapData;
  19. import flash.geom.Point;
  20. import flash.geom.Rectangle;
  21. import flash.utils.Dictionary;
  22. public final class Voronoi
  23. {
  24. private var _sites:SiteList;
  25. private var _sitesIndexedByLocation:Dictionary;
  26. public var _triangles:Vector.<Triangle>;
  27. private var _edges:Vector.<Edge>;
  28. // TODO generalize this so it doesn't have to be a rectangle;
  29. // then we can make the fractal voronois-within-voronois
  30. private var _plotBounds:Rectangle;
  31. public function get plotBounds():Rectangle
  32. {
  33. return _plotBounds;
  34. }
  35. public function dispose():void
  36. {
  37. var i:int, n:int;
  38. if (_sites)
  39. {
  40. _sites.dispose();
  41. _sites = null;
  42. }
  43. if (_triangles)
  44. {
  45. n = _triangles.length;
  46. for (i = 0; i < n; ++i)
  47. {
  48. _triangles[i].dispose();
  49. }
  50. _triangles.length = 0;
  51. _triangles = null;
  52. }
  53. if (_edges)
  54. {
  55. n = _edges.length;
  56. for (i = 0; i < n; ++i)
  57. {
  58. _edges[i].dispose();
  59. }
  60. _edges.length = 0;
  61. _edges = null;
  62. }
  63. _plotBounds = null;
  64. _sitesIndexedByLocation = null;
  65. }
  66. public function Voronoi(points:Vector.<Point>, colors:Vector.<uint>, plotBounds:Rectangle)
  67. {
  68. _sites = new SiteList();
  69. _sitesIndexedByLocation = new Dictionary(true);
  70. addSites(points, colors);
  71. _plotBounds = plotBounds;
  72. _triangles = new Vector.<Triangle>();
  73. _edges = new Vector.<Edge>();
  74. fortunesAlgorithm();
  75. }
  76. private function addSites(points:Vector.<Point>, colors:Vector.<uint>):void
  77. {
  78. var length:uint = points.length;
  79. for (var i:uint = 0; i < length; ++i)
  80. {
  81. addSite(points[i], colors ? colors[i] : 0, i);
  82. }
  83. }
  84. private function addSite(p:Point, color:uint, index:int):void
  85. {
  86. var weight:Number = Math.random() * 100;
  87. var site:Site = Site.create(p, index, weight, color);
  88. _sites.push(site);
  89. _sitesIndexedByLocation[p] = site;
  90. }
  91. public function region(p:Point):Vector.<Point>
  92. {
  93. var site:Site = _sitesIndexedByLocation[p];
  94. if (!site)
  95. {
  96. return new Vector.<Point>();
  97. }
  98. return site.region(_plotBounds);
  99. }
  100. public function neighborSitesForSite(coord:Point):Vector.<Point>
  101. {
  102. var points:Vector.<Point> = new Vector.<Point>();
  103. var site:Site = _sitesIndexedByLocation[coord];
  104. if (!site)
  105. {
  106. return points;
  107. }
  108. var sites:Vector.<Site> = site.neighborSites();
  109. var neighbor:Site;
  110. for each (neighbor in sites)
  111. {
  112. points.push(neighbor.coord);
  113. }
  114. return points;
  115. }
  116. public function circles():Vector.<Circle>
  117. {
  118. return _sites.circles();
  119. }
  120. public function voronoiBoundaryForSite(coord:Point):Vector.<LineSegment>
  121. {
  122. return visibleLineSegments(selectEdgesForSitePoint(coord, _edges));
  123. }
  124. public function delaunayLinesForSite(coord:Point):Vector.<LineSegment>
  125. {
  126. return delaunayLinesForEdges(selectEdgesForSitePoint(coord, _edges));
  127. }
  128. public function voronoiDiagram():Vector.<LineSegment>
  129. {
  130. return visibleLineSegments(_edges);
  131. }
  132. public function delaunayTriangulation(keepOutMask:BitmapData = null):Vector.<LineSegment>
  133. {
  134. return delaunayLinesForEdges(selectNonIntersectingEdges(keepOutMask, _edges));
  135. }
  136. public function hull():Vector.<LineSegment>
  137. {
  138. return delaunayLinesForEdges(hullEdges());
  139. }
  140. private function hullEdges():Vector.<Edge>
  141. {
  142. return _edges.filter(myTest);
  143. function myTest(edge:Edge, index:int, vector:Vector.<Edge>):Boolean
  144. {
  145. return (edge.isPartOfConvexHull());
  146. }
  147. }
  148. public function hullPointsInOrder():Vector.<Point>
  149. {
  150. var hullEdges:Vector.<Edge> = hullEdges();
  151. var points:Vector.<Point> = new Vector.<Point>();
  152. if (hullEdges.length == 0)
  153. {
  154. return points;
  155. }
  156. var reorderer:EdgeReorderer = new EdgeReorderer(hullEdges, Site);
  157. hullEdges = reorderer.edges;
  158. var orientations:Vector.<LR> = reorderer.edgeOrientations;
  159. reorderer.dispose();
  160. var orientation:LR;
  161. var n:int = hullEdges.length;
  162. for (var i:int = 0; i < n; ++i)
  163. {
  164. var edge:Edge = hullEdges[i];
  165. orientation = orientations[i];
  166. points.push(edge.site(orientation).coord);
  167. }
  168. return points;
  169. }
  170. public function spanningTree(type:String = "minimum", keepOutMask:BitmapData = null):Vector.<LineSegment>
  171. {
  172. var edges:Vector.<Edge> = selectNonIntersectingEdges(keepOutMask, _edges);
  173. var segments:Vector.<LineSegment> = delaunayLinesForEdges(edges);
  174. return kruskal(segments, type);
  175. }
  176. public function regions():Vector.<Vector.<Point>>
  177. {
  178. return _sites.regions(_plotBounds);
  179. }
  180. public function siteColors(referenceImage:BitmapData = null):Vector.<uint>
  181. {
  182. return _sites.siteColors(referenceImage);
  183. }
  184. /**
  185. *
  186. * @param proximityMap a BitmapData whose regions are filled with the site index values; see PlanePointsCanvas::fillRegions()
  187. * @param x
  188. * @param y
  189. * @return coordinates of nearest Site to (x, y)
  190. *
  191. */
  192. public function nearestSitePoint(proximityMap:BitmapData, x:Number, y:Number):Point
  193. {
  194. return _sites.nearestSitePoint(proximityMap, x, y);
  195. }
  196. public function siteCoords():Vector.<Point>
  197. {
  198. return _sites.siteCoords();
  199. }
  200. private function fortunesAlgorithm():void
  201. {
  202. var newSite:Site, bottomSite:Site, topSite:Site, tempSite:Site;
  203. var v:Vertex, vertex:Vertex;
  204. var newintstar:Point;
  205. var leftRight:LR;
  206. var lbnd:Halfedge, rbnd:Halfedge, llbnd:Halfedge, rrbnd:Halfedge, bisector:Halfedge;
  207. var edge:Edge;
  208. var dataBounds:Rectangle = _sites.getSitesBounds();
  209. var sqrt_nsites:int = int(Math.sqrt(_sites.length + 4));
  210. var heap:HalfedgePriorityQueue = new HalfedgePriorityQueue(dataBounds.y, dataBounds.height, sqrt_nsites);
  211. var edgeList:EdgeList = new EdgeList(dataBounds.x, dataBounds.width, sqrt_nsites);
  212. var halfEdges:Vector.<Halfedge> = new Vector.<Halfedge>();
  213. var vertices:Vector.<Vertex> = new Vector.<Vertex>();
  214. var bottomMostSite:Site = _sites.next();
  215. newSite = _sites.next();
  216. for (;;)
  217. {
  218. if (heap.empty() == false)
  219. {
  220. newintstar = heap.min();
  221. }
  222. if (newSite != null
  223. && (heap.empty() || compareByYThenX(newSite, newintstar) < 0))
  224. {
  225. /* new site is smallest */
  226. //trace("smallest: new site " + newSite);
  227. // Step 8:
  228. lbnd = edgeList.edgeListLeftNeighbor(newSite.coord); // the Halfedge just to the left of newSite
  229. //trace("lbnd: " + lbnd);
  230. rbnd = lbnd.edgeListRightNeighbor; // the Halfedge just to the right
  231. //trace("rbnd: " + rbnd);
  232. bottomSite = rightRegion(lbnd); // this is the same as leftRegion(rbnd)
  233. // this Site determines the region containing the new site
  234. //trace("new Site is in region of existing site: " + bottomSite);
  235. // Step 9:
  236. edge = Edge.createBisectingEdge(bottomSite, newSite);
  237. //trace("new edge: " + edge);
  238. _edges.push(edge);
  239. bisector = Halfedge.create(edge, LR.LEFT);
  240. halfEdges.push(bisector);
  241. // inserting two Halfedges into edgeList constitutes Step 10:
  242. // insert bisector to the right of lbnd:
  243. edgeList.insert(lbnd, bisector);
  244. // first half of Step 11:
  245. if ((vertex = Vertex.intersect(lbnd, bisector)) != null)
  246. {
  247. vertices.push(vertex);
  248. heap.remove(lbnd);
  249. lbnd.vertex = vertex;
  250. lbnd.ystar = vertex.y + newSite.dist(vertex);
  251. heap.insert(lbnd);
  252. }
  253. lbnd = bisector;
  254. bisector = Halfedge.create(edge, LR.RIGHT);
  255. halfEdges.push(bisector);
  256. // second Halfedge for Step 10:
  257. // insert bisector to the right of lbnd:
  258. edgeList.insert(lbnd, bisector);
  259. // second half of Step 11:
  260. if ((vertex = Vertex.intersect(bisector, rbnd)) != null)
  261. {
  262. vertices.push(vertex);
  263. bisector.vertex = vertex;
  264. bisector.ystar = vertex.y + newSite.dist(vertex);
  265. heap.insert(bisector);
  266. }
  267. newSite = _sites.next();
  268. }
  269. else if (heap.empty() == false)
  270. {
  271. /* intersection is smallest */
  272. lbnd = heap.extractMin();
  273. llbnd = lbnd.edgeListLeftNeighbor;
  274. rbnd = lbnd.edgeListRightNeighbor;
  275. rrbnd = rbnd.edgeListRightNeighbor;
  276. bottomSite = leftRegion(lbnd);
  277. topSite = rightRegion(rbnd);
  278. // these three sites define a Delaunay triangle
  279. // (not actually using these for anything...)
  280. _triangles.push(new Triangle(bottomSite, topSite, rightRegion(lbnd)));
  281. v = lbnd.vertex;
  282. v.setIndex();
  283. lbnd.edge.setVertex(lbnd.leftRight, v);
  284. rbnd.edge.setVertex(rbnd.leftRight, v);
  285. edgeList.remove(lbnd);
  286. heap.remove(rbnd);
  287. edgeList.remove(rbnd);
  288. leftRight = LR.LEFT;
  289. if (bottomSite.y > topSite.y)
  290. {
  291. tempSite = bottomSite; bottomSite = topSite; topSite = tempSite; leftRight = LR.RIGHT;
  292. }
  293. edge = Edge.createBisectingEdge(bottomSite, topSite);
  294. _edges.push(edge);
  295. bisector = Halfedge.create(edge, leftRight);
  296. halfEdges.push(bisector);
  297. edgeList.insert(llbnd, bisector);
  298. edge.setVertex(LR.other(leftRight), v);
  299. if ((vertex = Vertex.intersect(llbnd, bisector)) != null)
  300. {
  301. vertices.push(vertex);
  302. heap.remove(llbnd);
  303. llbnd.vertex = vertex;
  304. llbnd.ystar = vertex.y + bottomSite.dist(vertex);
  305. heap.insert(llbnd);
  306. }
  307. if ((vertex = Vertex.intersect(bisector, rrbnd)) != null)
  308. {
  309. vertices.push(vertex);
  310. bisector.vertex = vertex;
  311. bisector.ystar = vertex.y + bottomSite.dist(vertex);
  312. heap.insert(bisector);
  313. }
  314. }
  315. else
  316. {
  317. break;
  318. }
  319. }
  320. // heap should be empty now
  321. heap.dispose();
  322. edgeList.dispose();
  323. for each (var halfEdge:Halfedge in halfEdges)
  324. {
  325. halfEdge.reallyDispose();
  326. }
  327. halfEdges.length = 0;
  328. // we need the vertices to clip the edges
  329. for each (edge in _edges)
  330. {
  331. edge.clipVertices(_plotBounds);
  332. }
  333. // but we don't actually ever use them again!
  334. for each (vertex in vertices)
  335. {
  336. vertex.dispose();
  337. }
  338. vertices.length = 0;
  339. function leftRegion(he:Halfedge):Site
  340. {
  341. var edge:Edge = he.edge;
  342. if (edge == null)
  343. {
  344. return bottomMostSite;
  345. }
  346. return edge.site(he.leftRight);
  347. }
  348. function rightRegion(he:Halfedge):Site
  349. {
  350. var edge:Edge = he.edge;
  351. if (edge == null)
  352. {
  353. return bottomMostSite;
  354. }
  355. return edge.site(LR.other(he.leftRight));
  356. }
  357. }
  358. internal static function compareByYThenX(s1:Site, s2:*):Number
  359. {
  360. if (s1.y < s2.y) return -1;
  361. if (s1.y > s2.y) return 1;
  362. if (s1.x < s2.x) return -1;
  363. if (s1.x > s2.x) return 1;
  364. return 0;
  365. }
  366. }
  367. }