Toggle navigation
Sign Up
Log In
Explore
Works
Folders
Tools
Collections
Artists
Groups
Groups
Topics
Tasks
Tasks
Jobs
Teams
Jobs
Recommendation
More Effects...
ActionScript
/** * Sketch of Voronoi * I will recommend seeing in "FULL SCREEN" mode. * http://alumican.net/#/c/cells * * based on 超速ボロノイ図 by fumix * @see http://wonderfl.net/c/3TKq/ * * Fortune's algorithm - Wikipedia, the free encyclopedia * @see http://en.wikipedia.org/wiki/Fortune's_algorithm * * Controul > Speedy Voronoi diagrams in as3/flash * @see http://blog.controul.com/2009/05/speedy-voronoi-diagrams-in-as3flash/ * * @author Yukiya Okuda
*/ package { import flash.display.Graphics; import flash.display.Shape; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.events.Event; public class Main extends Sprite { private const N:int = 700; private const PI:Number = Math.PI; private const PI2:Number = PI * 2; private const TO_DEGREE:Number = 180 / PI; private const TO_RADIAN:Number = PI / 180; private var _fortune:Fortune; private var _points:Vector.
; private var _pointCount:int; private var _first:Number2; private var _oldMouseX:Number; private var _oldMouseY:Number; private var _background:Shape; private var _canvas:Shape; private var _range:Number; public function Main():void { addEventListener(Event.ADDED_TO_STAGE, _init); } private function _init(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, _init); stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; stage.quality = StageQuality.MEDIUM; stage.frameRate = 60; var sw:Number = stage.stageWidth, sh:Number = stage.stageHeight; _fortune = new Fortune(); _background = addChild( new Shape() ) as Shape; _background.graphics.beginFill(0x0); _background.graphics.drawRect(0, 0, sw, sh); _background.graphics.endFill(); _canvas = addChild( new Shape() ) as Shape; _points = new Vector.
(N); var p:Number2; var old:Number2; for (var i:int = 0; i < N; ++i) { _points[i] = p = new Number2(); if (_first == null) { old = _first = p; } else { old.next = p; old = p; } } _oldMouseX = mouseX; _oldMouseY = mouseY; addEventListener(Event.ENTER_FRAME, _update); stage.addEventListener(Event.RESIZE, _resize); _resize(null); } private function _resize(e:Event):void { var sw:Number = stage.stageWidth, sh:Number = stage.stageHeight; _background.width = sw; _background.height = sh; _range = 100 * (Math.min(sw, sh) / 465); var p:Number2 = _first; do { var px:Number = p.x = Math.random() * sw; var py:Number = p.y = Math.random() * sh; var angle:Number = Math.atan2(py - sh * 0.5, px - sw * 0.5); p.defaultVx = p.vx = 0.3 * Math.cos(angle); p.defaultVy = p.vy = 0.3 * Math.sin(angle); } while (p = p.next); } private function _update(e:Event):void { _interaction(); _draw(); } private function _interaction():void { var sw:Number = stage.stageWidth, sh:Number = stage.stageHeight, cx:Number = sw * 0.5, cy:Number = sh * 0.5, mx:Number = mouseX, my:Number = mouseY, dx:Number, dy:Number, dist2:Number, px:Number, py:Number, mvx:Number = mx - _oldMouseX, mvy:Number = my - _oldMouseY, ms2:Number = Math.sqrt(mvx * mvx + mvy * mvy), angle:Number, power:Number, mag:Number = _range * 0.5; var p:Number2 = _first; do { px = p.x; py = p.y; if (px < 0 || px > sw || py < 0 || py > sh) { angle = Math.random() * PI2; p.x = cx + (Math.random() - 0.5) * 5; p.y = cy + (Math.random() - 0.5) * 5; p.vx = (Math.random() - 0.5) * 1 * Math.cos(angle); p.vy = (Math.random() - 0.5) * 1 * Math.sin(angle); p.defaultVx = p.vx; p.defaultVy = p.vy; } else { dx = px - mx; dy = py - my; dist2 = dx * dx + dy * dy; angle = Math.atan2(dy, dx); power = mag / dist2 * ms2; p.vx += power * Math.cos(angle); p.vy += power * Math.sin(angle); } p.defaultVx *= 1.005; p.defaultVy *= 1.005; p.vx += (p.defaultVx - p.vx) * 0.05; p.vy += (p.defaultVy - p.vy) * 0.05; p.x += p.vx; p.y += p.vy; } while (p = p.next); _fortune.points = _points; _oldMouseX = mx; _oldMouseY = my; } private function _draw():void { var segments:Vector.
= _fortune.compute(), g:Graphics = _canvas.graphics, p:Number2 = _first, i:int, n:int = segments.length, st:Number2, ed:Number2, dx:Number, dy:Number, dist2:Number, thickness:Number, range:Number = _range, mx:Number = mouseX, my:Number = mouseY, stX:Number, stY:Number, edX:Number, edY:Number; g.clear(); for (i = 0; i < n; i += 2) { st = segments[i ]; ed = segments[i + 1]; stX = st.x; stY = st.y; edX = ed.x; edY = ed.y; dx = (edX + stX) * 0.5 - mx; dy = (edY + stY) * 0.5 - my; dist2 = Math.sqrt(dx * dx + dy * dy); thickness = (dist2 < range) ? (10 * (range - dist2) / range) : 0; g.lineStyle(thickness, 0x333333); g.moveTo(stX, stY); g.lineTo(edX, edY); } g.lineStyle(0, 0xffffff, 0.8); do { g.drawCircle(p.x, p.y, 1); } while (p = p.next); } } } //package //{ /*public*/ class Number2 { public var x:Number; public var y:Number; public var vx:Number; public var vy:Number; public var next:Number2; public var defaultVx:Number; public var defaultVy:Number; public function Number2(x:Number = 0, y:Number = 0, vx:Number = 0, vy:Number = 0):void { this.x = x; this.y = y; this.defaultVx = this.vx = vx; this.defaultVy = this.vy = vy; } } //} //package //{ /*public*/ class Arc { public var p : Number2; public var next : Arc; public var prev : Arc; // public var s0 : Seg; // public var s1 : Seg; public var v0 : Number2; public var v1 : Number2; // Circle event data : public var left : Arc; public var right : Arc; public var endX : Number; public var endP : Number2; } //} //package //{ /* * An implementation of Steve Fortune's algorithm for computing voronoi diagrams. * @author Matt Brubeck * * Modifications and optimisations: * ** Data structures are adapted to what's available to as3. * ** The beachline intersection lookup is optimised, * intersecting begins only near the probable intersection. * ** Functions are massively inlined. * * Todo list: * ** Provide for bounding box intersection, * ** Inline the 'intersection' method. * ** Design a good datastructure for the solution, which would * ideally contain enough neighbourhood info for region rendering, * extrusion and intrusion of edges and pathfinding. * * Original c++ code * http://www.cs.hmc.edu/~mbrubeck/voronoi.html */ /*public*/ class Fortune { ///////// ///////// ///////// Datastructures. // Points are provided as a vector, which is sorted by x (increasing) before the sweep. public var points : Vector.
; // Bounding box. private var x0 : Number; // private var x1 : Number; // private var y0 : Number; // private var y1 : Number; // Root of the frontline and next arc to be removed. private var root : Arc; private var next : Arc; // Reusable objects and pools. private var o : Number2 = new Number2; private static var arcPoolD : Arc; //////// //////// //////// API. /** * Computes the Voronoi decomposition of the plane. * @return A vector or vertices in pairs, describing segments ready for drawing. */ public function compute () : Vector.
{ // // // Clear the output. var out : Vector.
= new Vector.
, len : int = 0; // // // Clear the state. root = null; next = null; // // // Read the pools. var key : * , arcPool : Arc = arcPoolD; // // // Vars: var i : int, j : int, w : Number, x : Number, a : Arc, b : Arc, // s : Seg, z : Number2, p : Number2 = points [ 0 ], points : Vector.
= points, n : int = points.length, // Circle events check inlined. circle : Boolean, eventX : Number, c : Arc, d : Arc, aa : Number2, bb : Number2, cc : Number2, A : Number, B : Number, C : Number, D : Number, E : Number, F : Number, G : Number; // // y0 = p.y; // y1 = p.y; // // // Sort points by x coord, compute the bounding box. ///// Currently insertion sort. Quicksort? w = points [ 0 ].x; for ( i = 1; i < n; i ++ ) { p = points [ i ]; // Keep track of the bounding box. // if ( p.y < y0 ) // y0 = p.y; // else if ( p.y > y1 ) // y1 = p.y; // Insertion sort. x = p.x; if ( x < w ) { j = i; while ( ( j > 0 ) && ( points [ int ( j - 1 ) ].x > x ) ) { points [ j ] = points [ int ( j - 1 ) ]; j--; } points [ j ] = p; } else w = x; } // Get x bounds. x0 = points [ 0 ].x; // x1 = points [ n - 1 ].x; // Add margins to the bounding box. /* var dx : Number = (x1 - x0 + 1) / 5.0, dy : Number = dy = (y1 - y0 + 1) / 5.0; x0 -= dx; x1 += dx; y0 -= dy; y1 += dy; // trace ( x0, x1, y0, y1 ); //*/ // // // Process. i = 0; p = points [ 0 ]; x = p.x; for ( ;; ) { // // Check circle events. ///////////////////////// if ( a ) { // Check for arc a. circle = false; if ( a.prev && a.next ) { aa = a.prev.p, bb = a.p, cc = a.next.p; // Algorithm from O'Rourke 2ed p. 189. A = bb.x - aa.x, B = bb.y - aa.y, C = cc.x - aa.x, D = cc.y - aa.y; // Check that bc is a "right turn" from ab. if ( A * D - C * B <= 0 ) { E = A * ( aa.x + bb.x ) + B * ( aa.y + bb.y ), F = C * ( aa.x + cc.x ) + D * ( aa.y + cc.y ), G = 2 * ( A * ( cc.y - bb.y ) - B * ( cc.x - bb.x ) ); // Check for colinearity. // if ( G > 0.000000001 || G < -0.000000001 ) if ( G ) { // Point o is the center of the circle. o.x = ( D * E - B * F ) / G; o.y = ( A * F - C * E ) / G; // o.x plus radius equals max x coordinate. A = aa.x - o.x; B = aa.y - o.y; eventX = o.x + Math.sqrt ( A * A + B * B ); if ( eventX >= w ) circle = true; } } } // Remove from queue. if ( a.right ) a.right.left = a.left; if ( a.left ) a.left.right = a.right; if ( a == next ) next = a.right; // Record event. if ( circle ) { a.endX = eventX; if ( a.endP ) { a.endP.x = o.x; a.endP.y = o.y; } else { a.endP = o; o = new Number2; } d = next; if ( !d ) { next = a; } else for ( ;; ) { if ( d.endX >= eventX ) { a.left = d.left; if ( d.left ) d.left.right = a; if ( next == d ) next = a; a.right = d; d.left = a; break; } if ( !d.right ) { d.right = a; a.left = d; a.right = null; break; } d = d.right; } } else { a.endX = NaN; a.endP = null; a.left = null; a.right = null; } // Push next arc to check. if ( b ) { a = b; b = null; continue; } if ( c ) { a = c; c = null; continue; } a = null; } ////////////////////////////////////////////////// // if ( next && next.endX <= x ) { // // Handle next circle event. // Get the next event from the queue. /////////// a = next; next = a.right; if ( next ) next.left = null; a.right = null; //DEBUG*/ Debug.frontRemove ( a, root ); // Start a new edge. // s = new Seg; // s.start = a.endP; // Remove the associated arc from the front. if ( a.prev ) { a.prev.next = a.next; // a.prev.s1 = s; a.prev.v1 = a.endP; } if ( a.next ) { a.next.prev = a.prev; // a.next.s0 = s; a.next.v0 = a.endP; } // Finish the edges before and after a. /* if ( a.s0 && !a.s0.done ) { a.s0.done = true; // a.s0.end = a.endP; out [ len ++ ] = a.s0.start; out [ len ++ ] = a.endP; } if ( a.s1 && !a.s1.done ) { a.s1.done = true; // a.s1.end = a.endP; out [ len ++ ] = a.s1.start; out [ len ++ ] = a.endP; } */ if ( a.v0 ) { out [ len ++ ] = a.v0; a.v0 = null; out [ len ++ ] = a.endP; } if ( a.v1 ) { out [ len ++ ] = a.v1; a.v1 = null; out [ len ++ ] = a.endP; } // Keep a ref for collection. d = a; // Recheck circle events on either side of p: w = a.endX; if ( a.prev ) { b = a.prev; a = a.next; } else { a = a.next; b = null; } c = null; // Collect. d.v0 = null; d.v1 = null; d.p = null; d.prev = null; d.endX = NaN; d.endP = null; d.next = arcPool; arcPool = d; ////////////////////////////////////////////////// // } else { if ( !p ) break; // // Handle next site event. ////////////////////// if ( !root ) { // root = new Arc; if ( arcPool ) { root = arcPool; arcPool = arcPool.next; root.next = null; } else root = new Arc; root.p = p; //DEBUG*/ Debug.frontInsert ( root, root ); } else { z = new Number2; // Find the first arc with a point below p, // and start searching for the intersection around it. a = root.next; if ( a ) { while ( a.next ) { a = a.next; if ( a.p.y >= p.y ) break; } // Find the intersecting curve. intersection ( a.prev.p, a.p, p.x, z ); if ( z.y <= p.y ) { // Search for the intersection to the south of i. while ( a.next ) { a = a.next; intersection ( a.prev.p, a.p, p.x, z ); if ( z.y >= p.y ) { a = a.prev; break; } } } else { // Search for the intersection above i. a = a.prev; while ( a.prev ) { a = a.prev; intersection ( a.p, a.next.p, p.x, z ); if ( z.y <= p.y ) { a = a.next; break; } } } } else a = root; // New parabola will intersect arc a. Duplicate a. if ( a.next ) { // b = new Arc; if ( arcPool ) { b = arcPool; arcPool = arcPool.next; b.next = null; } else b = new Arc; b.p = a.p; b.prev = a; b.next = a.next; a.next.prev = b; a.next = b; } else { // b = new Arc; if ( arcPool ) { b = arcPool; arcPool = arcPool.next; b.next = null; } else b = new Arc; b.p = a.p; b.prev = a; a.next = b; } // a.next.s1 = a.s1; a.next.v1 = a.v1; // Find the point of intersection. z.y = p.y; z.x = ( a.p.x * a.p.x + ( a.p.y - p.y ) * ( a.p.y - p.y ) - p.x * p.x ) / ( 2 * a.p.x - 2 * p.x ); // Add p between i and i->next. // b = new Arc; if ( arcPool ) { b = arcPool; arcPool = arcPool.next; b.next = null; } else b = new Arc; b.p = p; b.prev = a; b.next = a.next; a.next.prev = b; a.next = b; a = a.next; // Now a points to the new arc. // Add new half-edges connected to i's endpoints. /* s = new Seg; s.start = z; a.prev.s1 = a.s0 = s; s = new Seg; s.start = z; a.next.s0 = a.s1 = s; */ a.prev.v1 = z; a.next.v0 = z; a.v0 = z; a.v1 = z; // Check for new circle events around the new arc: b = a.next; a = a.prev; c = null; w = p.x; //DEBUG*/ Debug.frontInsert ( a, root ); } ////////////////////////////////////////////////// // i ++; if ( i >= n ) { p = null; x = Number.MAX_VALUE; } else { p = points [ i ]; x = p.x; } } } //*/ /* // Clean up dangling edges. // Advance the sweep line so no parabolas can cross the bounding box. x = x1; x = x1 + ( x1 - x0 ) + ( y1 - y0 ); x *= 2; // Extend each remaining segment to the new parabola intersections. var arc : Arc = root; for ( ;; ) { if ( arc.s1 ) arc.s1.finish ( intersection ( arc.p, arc.next.p, x, new Number2 ) ); arc = arc.next; if ( !arc.next ) break; } //*/ // // // Store the pools. arcPoolD = arcPool; // // // Return the result ready for drawing. return out; } /** * Where do two parabolas intersect? * @param p0 A Number2 object describing the site for the first parabola. * @param p1 A Number2 object describing the site for the second parabola. * @param l The location of the sweep line. * @param res A Number2 object in which to store the intersection. * @return The point of intersection. */ public function intersection ( p0 : Number2, p1 : Number2, l : Number, res : Number2 ) : Number2 { var p : Number2 = p0, ll : Number = l * l; if ( p0.x == p1.x ) res.y = ( p0.y + p1.y ) / 2; else if ( p1.x == l ) res.y = p1.y; else if ( p0.x == l ) { res.y = p0.y; p = p1; } else { // Use the quadratic formula. var z0 : Number = 0.5 / ( p0.x - l ); // 1 / ( 2*(p0.x - l) ) var z1 : Number = 0.5 / ( p1.x - l ); // 1 / ( 2*(p1.x - l) ) var a : Number = z0 - z1; var b : Number = -2 * ( p0.y * z0 - p1.y * z1 ); var c : Number = ( p0.y * p0.y + p0.x * p0.x - ll ) * z0 - ( p1.y * p1.y + p1.x * p1.x - ll ) * z1; res.y = ( - b - Math.sqrt ( b * b - 4 * a * c ) ) / ( 2 * a ); } // Plug back into one of the parabola equations. res.x = ( p.x * p.x + ( p.y - res.y ) * ( p.y - res.y ) - ll ) / ( 2 * p.x - 2 * l ); return res; } } //}
Join Effecthub.com
Working with Global Gaming Artists and Developers!
Login
Sign Up
Or Login with Your Email Address:
Email
Password
Remember
Or Sign Up with Your Email Address:
Your Email
This field must contain a valid email
Set Password
Password should be at least 1 character
Stay informed via email