package { import flash.display.Sprite; import flash.geom.Point; /** * Class to represent an arc on the beach line * @author Chad Nelson */ public class Parabola extends Sprite { public var site : VoronoiSite; public var circleEvent : VoronoiCircleEvent = null; // Tuples public var left : Parabola = null; public var right : Parabola = null; public var edge_right : VoronoiEdge = null; // Private variables describing the parabola protected var p : Number; protected var directrix : Number = 0; protected var vertex : Point; protected var a : Number; protected var b : Number; protected var c : Number; /** * Initializes this arc in the Beachline * @param site * @param parent */ public function Parabola(site : VoronoiSite) { this.site = site; } /** * Gets the x-coordinate of the breakpoint between this arc and the one on the left * @param directrix * @return */ public function getLeftBreakpoint():Number { var para : Parabola = left; if (directrix != BeachLine.directrix) update(); var intersectionA : Number; var intersectionB : Number; var d : Number; if (para == null) return Number.NEGATIVE_INFINITY; else if (para.directrix != BeachLine.directrix) para.update(); // Quickly check for site event as the breakpoint if (para.directrix == para.site.y) return para.site.x; if (directrix == site.y) return site.x; // Calculate Intersection Points d = Utility.calcDiscriminant(a - para.a, b - para.b, c - para.c); intersectionA = ((para.b - b) - Math.pow(d, 0.5)) / (2 * (a - para.a)); intersectionB = ((para.b - b) + Math.pow(d, 0.5)) / (2 * (a - para.a)); if (para.site.y > site.y) { // Return the intersection point to the left if (intersectionA < intersectionB) return intersectionA else return intersectionB; } else if (para.site.y < site.y) { // Return the intersection point to the right if (intersectionA > intersectionB) return intersectionA else return intersectionB; } else { // Return the midpoint between the two sites return (site.x + para.site.x) / 2; } } /** * Gets the x-coordinate of the breakpoint between this arc and the one on the right * @param directrix * @return */ public function getRightBreakpoint():Number { var para : Parabola = right; if (directrix != BeachLine.directrix) update(); var intersectionA : Number; var intersectionB : Number; var d : Number; if (para == null) return Number.POSITIVE_INFINITY; else if (para.directrix != BeachLine.directrix) para.update(); // Quickly check for site event as the breakpoint if (para.directrix == para.site.y) return para.site.x; if (directrix == site.y) return site.x; // Calculate Intersection Points d = Utility.calcDiscriminant(a - para.a, b - para.b, c - para.c); intersectionA = ((para.b - b) - Math.pow(d, 0.5)) / (2 * (a - para.a)); intersectionB = ((para.b - b) + Math.pow(d, 0.5)) / (2 * (a - para.a)); if (para.site.y > site.y) { // Return the intersection point to the right if (intersectionA > intersectionB) return intersectionA else return intersectionB; } else if (para.site.y < site.y) { // Return the intersection point to the left if (intersectionA < intersectionB) return intersectionA else return intersectionB; } else { // Return the midpoint between the two sites return (site.x + para.site.x) / 2; } } public function update():void { // Update mathematical properties directrix = BeachLine.directrix; p = (site.y - directrix)/2; vertex = new Point(site.x, directrix + p); a = 1/(4*p); b = -2*a*vertex.x; c = a * Math.pow(vertex.x, 2) + vertex.y; } public function draw():void { update(); // Update graphics graphics.clear(); graphics.lineStyle(2, 0xDD0000, 1); var left : Point = new Point(); var right : Point = new Point(); var control : Point; var bpl_x : Number = getLeftBreakpoint(); var bpr_x : Number = getRightBreakpoint(); left.x = Math.max(0, bpl_x); left.y = valueAt(left.x); right.x = Math.min(stage.stageWidth, bpr_x); right.y = valueAt(right.x); if (directrix == site.y) { // Draw line for this site event if (this.left != null) graphics.moveTo(site.x, stage.stageHeight - this.left.valueAt(site.x)); else if (this.right != null) graphics.moveTo(site.x, stage.stageHeight - this.right.valueAt(site.x)); else graphics.moveTo(site.x, 0); graphics.lineTo(site.x, stage.stageHeight - site.y); return; } else if (a > 0) { // Draw arc if (circleEvent != null) graphics.lineStyle(3, 0x00FF22, 1); graphics.moveTo(left.x, stage.stageHeight - left.y); control = controlPoint(left, right); graphics.curveTo(control.x, stage.stageHeight - control.y, right.x, stage.stageHeight - right.y); } } public function valueAt(xp : Number) : Number { return a*Math.pow(xp, 2) + b*xp + c; } protected function slopeAt(xp : Number) : Number { return 2 * a * xp + b; } protected function controlPoint(p1 : Point, p2 : Point) : Point { var control : Point = new Point(); var s1 : Number = slopeAt(p1.x); var s2 : Number = slopeAt(p2.x); control.x = ( -p1.y + (p1.x * s1) + p2.y - (p2.x * s2)) / (s1 - s2); control.y = s1 * (control.x - p1.x) + p1.y; return control; } } }