<?xml version="1.0" encoding="utf-8"?>
 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
       xmlns="http://www.degrafa.com/2007"
    xmlns:comp="components.*"
    xmlns:myDecorators="decorators.*"
    xmlns:paint="com.degrafa.paint.*"
    layout="absolute"
    width="600" height="500" 
    pageTitle="Quadratic Hermite Curve"
    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="Quad. Hermite End Tangent" x="250" y="30" width="300" styleName="title"/>
  
  <paint:SolidStroke id="bluestroke" weight="2" color="#00FF00">
  </paint:SolidStroke>
  
  <paint:SolidStroke id="redstroke" weight="2" color="#FF0000">
  </paint:SolidStroke>
   
  <mx:Canvas id="myCanvas" />
  <mx:Canvas id="sketch" />
  <mx:Canvas id="tanline" />
  <comp:InteractivePoint id="p0" x="90" y="250" pointLabel="P0" radius="5" color="0x00ff00" width="100" height="20" />
  <comp:InteractivePoint id="p1" x="250" y="230" pointLabel="P1" radius="5" color="0x00ff00" width="100" height="20" />
  <Polyline id="outline" graphicsTarget="{[sketch]}">
    <stroke> 
      <SolidStroke weight="1" color="#0000FF"/>
    </stroke>
    <decorators>
      <myDecorators:DashedLineDecorator id="dashDecorator" />
    </decorators>
  </Polyline>
  <Line id="tangentLine" graphicsTarget="{[tanline]}" stroke="{redstroke}"/>
  <comp:Tangent id="tangent" />
  
  <mx:Script>
    <![CDATA[
      import mx.events.PropertyChangeEvent;
      import com.degrafa.GraphicPointEX;
      import com.degrafa.core.collections.GraphicPointCollection;
      
      private var __quad:QuadraticHermiteCurve = new QuadraticHermiteCurve();
      private var __first:Boolean = true;
      
      private function test():void
      {    
        // restrict dragging for each point
        var bounds:Rectangle = new Rectangle(background.x, background.y, background.width, background.height);
        p0.restrict          = bounds;
        p1.restrict          = bounds;
        
        // call this function whenever either of the interactive points is moved
        p0.registerCallBack(onPointMoved);
        p1.registerCallBack(onPointMoved);
        
        // handle 'move' event on the tangent
        tangent.addEventListener("moved", onTangentMoved);
        
        tangent.initialPoint(p0.x, p0.y);
        tangent.endPoint(p0.x+30, p0.y+75);
        
        // initialize the quad. Hermite curve - some hardcoding here, so be careful when making changes
        __quad.x0 = p0.x;
        __quad.y0 = p0.y;
        __quad.x1 = p1.x;
        __quad.y1 = p1.y;
        __quad.tx = p0.x+30;
        __quad.ty = p0.y+75;
        
        drawCurve();
      }
      
      private function onTangentMoved(_e:Event):void
      {
        __quad.tx = tangent.tangentX;
        __quad.ty = tangent.tangentY;
        
        drawCurve();
      }
      
      private function drawCurve():void
      {
        var g:Graphics = myCanvas.graphics;
        g.clear();
        g.lineStyle(2,0x0000ff);
        g.moveTo(p0.x, p0.y);
        
        var delta:Number = 0.02;
        for( var t:Number=delta; t<1.0; t+=delta )
        {
          g.lineTo(__quad.getX(t), __quad.getY(t));
        }
        
        g.lineTo(__quad.getX(1.0), __quad.getY(1.0));
        
        // compute the endpoint of the end-tangent vector
        var dx:Number   = tangent.tangentX - p0.x;
        var dy:Number   = tangent.tangentY - p0.y;
        var e1X:Number  = 2*p1.x - p0.x;
        var e1Y:Number  = 2*p1.y - p0.y;
        var endX:Number = e1X - dx;
        var endY:Number = e1Y - dy;
         
        var points:GraphicPointCollection = outline.pointCollection;
        
        // first time drawing the polyline? 
        if( __first )
        { 
          points.addItemAt(new GraphicPointEX(p0.x, p0.y), 0);
          points.addItemAt(new GraphicPointEX(e1X, e1Y)  , 1);
          points.addItemAt(new GraphicPointEX(endX, endY), 2);
          points.addItemAt(new GraphicPointEX(p0.x, p0.y), 3);
          
          __first = false;
        }
        else
        {
          var pts:Array = points.items;
           
          var p:GraphicPointEX = pts[0];
          p.x                  = p0.x;
          p.y                  = p0.y;
          
          p   = pts[1];
          p.x = e1X;
          p.y = e1Y;
          
          p   = pts[2];
          p.x = endX;
          p.y = endY;
          
          
          p   = pts[3];
          p.x = p0.x;
          p.y = p0.y;
          
          outline.drawToTargets();
        }
        
        dx              = endX - p0.x;
        dy              = endY - p0.y;
        var newX:Number = p1.x + dx;
        var newY:Number = p1.y + dy;
        
        tangentLine.x  = p1.x;
        tangentLine.y  = p1.y;
        tangentLine.x1 = newX;
        tangentLine.y1 = newY;
      }
      
      private function onPointMoved(_p:InteractivePoint):void
      {
        switch( _p.pointLabel )
        {
          case "P0":
            tangent.initialPoint(p0.x, p0.y);
        
            __quad.x0 = p0.x;
            __quad.y0 = p0.y;
        
            drawCurve();
          break;
          
          case "P1":
            __quad.x1 = p1.x;
            __quad.y1 = p1.y;
        
            drawCurve();
          break;
        }
      }
    ]]>
  </mx:Script>
</mx:Application>