<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:comp="components.*"
    xmlns:degrafa="com.degrafa.*"
    xmlns:paint="com.degrafa.paint.*"
    xmlns:geom="com.degrafa.geometry.*"
    layout="absolute"
    width="600" height="500" 
    pageTitle="Catmull-Rom Spline Utility"
    applicationComplete="test()" 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 text="Catmull-Rom Spline Utility" x="250" y="30" width="300" styleName="title"/>
  <mx:TextArea x="50" y="417" width="500" id="__explanation__" height="35" fontSize="12" color="#000000" wordWrap="true" editable="false" enabled="true" borderStyle="none">
       <mx:text><![CDATA[Click to define at least three points, then click the space bar to animate a simple ellipse along the path.]]></mx:text>
        </mx:TextArea>
     
  <paint:SolidFill id="blue" color="#62ABCD" alpha=".6"/>

     <mx:Canvas id="knotLayer" />
     <mx:Canvas id="splineLayer" />
     <mx:Canvas id="knotOverlay" />
     <mx:Canvas id="markers" />
     <mx:Canvas id="animate" />
     
     <!-- If you change the width/height, change the initial x/y - this is hardcoded since it's quick-n-dirty :) -->
     <geom:Ellipse id="myEllipse" width="20" height="10" x="-10" y="-5" fill="{blue}" graphicsTarget="{[animate]}" visible="false" />
      
  <comp:CrossHair id="marker" width="20" height="20"/>
  
  <mx:Script>
    <![CDATA[
      import flash.events.MouseEvent;
      import com.degrafa.utilities.math.CatmullRomUtility;
        
      private static const RAD_TO_DEG:Number = 180/Math.PI;
      
      private var __count:uint = 0;  // count the number of knots
      private var __s:Number   = 0;  // (approximate) arc length along spline
      private var __spline:CatmullRomUtility = new CatmullRomUtility();
      
      private function test():void
      { 
        activateCrossHair();
      }
        
      // activate the crosshair
      private function activateCrossHair():void
      {
        marker.visible = true;
        marker.x       = stage.mouseX;
        marker.y       = stage.mouseY;
           
        stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
        stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
      }
        
      // deactivate crosshair
      private function deactivateCrosshair():void
      {
        marker.visible = false;
          
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      }
     
      // listen for any keypress
      private function onKeyDown(_e:KeyboardEvent):void
      {
        deactivateCrosshair();
        stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        
        // plot the spline old-school (this is a utility, not a spline meant to be folded into the Degrafa geometry pipeline for plotting)
        var g:Graphics = splineLayer.graphics;
        g.clear();
        g.lineStyle(2,0x0000ff);
        
        // sampling is over arc length
        var myX:Number = __spline.getX(0);
        var myY:Number = __spline.getY(0);
        g.moveTo(myX,myY);
        
        var delta:Number = 0.02;
        for( var s:Number=delta; s<1.0; s+=delta )
        {
          myX = __spline.getX(s);
          myY = __spline.getY(s);
          g.lineTo(myX, myY);
        }
        
        myX = __spline.getX(1.0);
        myY = __spline.getY(1.0);
        g.lineTo(myX,myY);
        
        // position some more markers at equal intervals of arc length
        g = markers.graphics;
        g.clear();
        g.beginFill(0xff0000, 0.5);
        
        for( s=0.1; s<=0.9; s+=0.1 )
        {
          myX = __spline.getX(s);
          myY = __spline.getY(s);
          g.drawCircle(myX, myY, 4);  
        }
        
        // place the ellipse - can't do it directly; need to transform the container or graphics target
        myX           = __spline.getX(0);
        myY           = __spline.getY(0);
        var dX:Number = __spline.getX(0.025) - myX;
        var dY:Number = __spline.getY(0.025) - myY;
        animate.x     = myX;
        animate.y     = myY;
       
        animate.rotation  = Math.atan2(dY, dX)*RAD_TO_DEG;
        myEllipse.visible = true;
        
        __s = 0;
        addEventListener(Event.ENTER_FRAME, __animate);
      }
        
      private function onMouseMove(_e:MouseEvent):void
      {
        marker.x = stage.mouseX;
        marker.y = stage.mouseY;
      }
        
      private function onMouseDown(_e:MouseEvent):void
      {
        var pX:Number = stage.mouseX;
        var pY:Number = stage.mouseY;
       
        var g:Graphics = knotLayer.graphics;
        g.lineStyle(1, 0x000000);
        if( __count == 0 )
        {
          g.moveTo(pX,pY);
        }
        else
        {
          g.lineTo(pX,pY);
        }
       
        g = knotOverlay.graphics;
           g.lineStyle(1,0x000000);
           g.beginFill(0x00ff00);
           g.drawCircle(pX, pY, 4);
           g.endFill();
           
        __count++;
        __spline.addControlPoint(pX, pY);
      }
      
      private function __animate(_e:Event):void
      {
        __s += 0.02;
        if( __s < 1 )
        {
          var myX:Number = __spline.getX(__s);
          var myY:Number = __spline.getY(__s);
         
          var dX:Number = __spline.getX(__s+0.025) - myX;
          var dY:Number = __spline.getY(__s+0.025) - myY;
          animate.x     = myX;
          animate.y     = myY;
       
          animate.rotation  = Math.atan2(dY, dX)*RAD_TO_DEG; 
        }
        else
        {
          // use prior orientation as 'close enough for govt. work' or a quick-n-dirty demo :)
          myX       = __spline.getX(1.0);
          myY       = __spline.getY(1.0);
          animate.x = myX;
          animate.y = myY;
          
          removeEventListener(Event.ENTER_FRAME, __animate);
        }
      }
        
     ]]>
   </mx:Script>
    
</mx:Application>