<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:geom="components.*"
xmlns="http://www.degrafa.com/2007"
layout="absolute"
width="600" height="500"
pageTitle="Quad Bezier Arc Length"
applicationComplete="test()" xmlns:degrafa="http://www.degrafa.com/2007" viewSourceURL="srcview/index.html">
<mx:Style source="assets/style/style.css"/>
<mx:Canvas id="background" x="50" y="90" width="500" height="320" backgroundColor="#FFFFFF" >
<mx:Label x="10" y="292" text="Move the 'A', 'B', or 'C' control points to view arc length" width="480" color="#000000" fontSize="12"/>
</mx:Canvas>
<mx:Label text="Quad. Bezier Arc Length" x="250" y="30" width="300" styleName="title"/>
<geom:Triangle id="triad" point1="{pointA}" point2="{pointB}" point3="{pointC}" />
<mx:Canvas id="bezierLayer" x="{background.x}" y="{background.y}" width="{background.width}" height="{background.height}" />
<geom:InteractivePoint id="pointA" x="90" y="250" pointLabel="A" radius="5" color="0x00ff00" width="100" height="20" />
<geom:InteractivePoint id="pointB" x="150" y="150" pointLabel="B" radius="5" color="0x00ff00" width="100" height="20" />
<geom:InteractivePoint id="pointC" x="290" y="200" pointLabel="C" radius="5" color="0x00ff00" width="100" height="20" />
<QuadraticBezier id="bezier" graphicsTarget="{[bezierLayer]}">
<stroke>
<SolidStroke weight="2" color="#0000FF"/>
</stroke>
</QuadraticBezier>
<mx:Label x="50" y="420" text="Segment Arc Length:" width="500" fontSize="12" color="#FFFFFF" id="__segments__"/>
<mx:Label x="50" y="435" text="Degrafa Arc Length:" width="500" fontSize="12" color="#FFFFFF" id="__degrafa__"/>
<mx:Label x="50" y="450" text="Elliptic Integral:" width="500" fontSize="12" color="#FFFFFF" id="__elliptic__"/>
<mx:Label x="50" y="465" text="Gauss-Legendre:" width="500" fontSize="12" color="#FFFFFF" id="__gauss__"/>
<mx:Script>
<![CDATA[
import mx.events.PropertyChangeEvent;
import com.degrafa.GraphicPointEX;
import com.degrafa.core.collections.GraphicPointCollection;
import com.degrafa.utilities.math.Gauss;
private var __c0X:Number;
private var __c0Y:Number;
private var __c1X:Number;
private var __c1Y:Number;
private var __c2X:Number;
private var __c2Y:Number;
private var __integrate:Gauss = new Gauss();
private function test():void
{
var bounds:Rectangle = new Rectangle(background.x, background.y, background.width, background.height);
pointA.restrict = bounds;
pointB.restrict = bounds;
pointC.restrict = bounds;
pointA.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChanged);
pointB.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChanged);
pointC.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChanged);
assignBezierControlPoints();
}
private function assignBezierControlPoints():void
{
bezier.x0 = pointA.x - bezierLayer.x;
bezier.y0 = pointA.y - bezierLayer.y;
bezier.cx = pointB.x - bezierLayer.x;
bezier.cy = pointB.y - bezierLayer.y;
bezier.x1 = pointC.x - bezierLayer.x;
bezier.y1 = pointC.y - bezierLayer.y;
}
private function onPropertyChanged(_e:PropertyChangeEvent):void
{
switch( _e.property )
{
case InteractivePoint.MOUSE_DOWN:
addEventListener(Event.ENTER_FRAME, onPointMove);
break;
case InteractivePoint.MOUSE_UP:
removeEventListener(Event.ENTER_FRAME, onPointMove);
displayArcLength();
break;
}
}
private function onPointMove(_e:Event):void
{
triad.redraw();
assignBezierControlPoints();
}
private function displayArcLength():void
{
__segments__.text = "Segment Arc Length: " + arcLengthBySegments().toString();
__degrafa__.text = "Degrafa Arc Length: " + bezier.geometricLength.toString();
__elliptic__.text = "Elliptic Integral: " + arcLengthByIntegral().toString();
__gauss__.text = "Gauss-Legendre: " + approxArcLength().toString();
}
private function arcLengthBySegments():Number
{
var length:Number = 0;
var tPrevious:Number = 0;
var p:Point = bezier.pointAt(0);
var prevX:Number = p.x;
var prevY:Number = p.y;
for( var t:Number=0.005; t<=1.0; t+=0.005 )
{
p = bezier.pointAt(t);
var deltaX:Number = p.x - prevX;
var deltaY:Number = p.y - prevY;
length += Math.sqrt(deltaX*deltaX + deltaY*deltaY);
prevX = p.x;
prevY = p.y;
}
return length;
}
private function arcLengthByIntegral():Number
{
var ax:Number = pointA.x - 2*pointB.x + pointC.x;
var ay:Number = pointA.y - 2*pointB.y + pointC.y;
var bx:Number = 2*pointB.x - 2*pointA.x;
var by:Number = 2*pointB.y - 2*pointA.y;
var a:Number = 4*(ax*ax + ay*ay);
var b:Number = 4*(ax*bx + ay*by);
var c:Number = bx*bx + by*by;
var abc:Number = 2*Math.sqrt(a+b+c);
var a2:Number = Math.sqrt(a);
var a32:Number = 2*a*a2;
var c2:Number = 2*Math.sqrt(c);
var ba:Number = b/a2;
return (a32*abc + a2*b*(abc-c2) + (4*c*a-b*b)*Math.log((2*a2+ba+abc)/(ba+c2)))/(4*a32);
}
private function computeBezierCoef():void
{
__c0X = pointA.x;
__c0Y = pointA.y;;
__c1X = 2.0*(pointB.x-pointA.x);
__c1Y = 2.0*(pointB.y-pointA.y);
__c2X = pointA.x-2.0*pointB.x+pointC.x;
__c2Y = pointA.y-2.0*pointB.y+pointC.y;
}
private function approxArcLength():Number
{
computeBezierCoef();
return __integrate.eval(integrand, 0, 1, 5);
}
private function integrand(_t:Number):Number
{
var xPrime:Number = __c1X + 2.0*__c2X*_t;
var yPrime:Number = __c1Y + 2.0*__c2Y*_t;
return Math.sqrt( xPrime*xPrime + yPrime*yPrime );
}
]]>
</mx:Script>
</mx:Application>