<?xml version="1.0" encoding="utf-8"?>
 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:geom="components.*"
    layout="absolute"
    width="600" height="500" 
    pageTitle="Quadratic Hermite Join" 
    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 Join" x="250" y="30" width="300" styleName="title"/>
  
  <mx:Canvas id="myCanvas" />
  <mx:Canvas id="myTangent" />
  <geom:InteractivePoint id="p0" x="90" y="250" pointLabel="P0" radius="5" color="0x00ff00" width="100" height="20" />
  <geom:InteractivePoint id="p1" x="250" y="230" pointLabel="P1" radius="5" color="0x00ff00" width="100" height="20" />
  <geom:Tangent id="tangent" />
  <geom:CrossHair id="marker" width="20" height="20"/>
    
  <mx:Script>
    <![CDATA[
      import mx.events.PropertyChangeEvent;
      import mx.events.SliderEvent;
       
      private var __quad:QuadraticHermiteCurve  = new QuadraticHermiteCurve();
      private var __quad2:QuadraticHermiteCurve = null;
      private var __t:Number                    = 0;  // derivative drawn at this t-parameter
      
      // end tangent of first segment
      private var __endTanX:Number;
      private var __endTanY:Number;
      
      // third data point (join is at P1, so this is P2)
      private var __p2X:Number;
      private var __p2Y:Number;
        
      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();
        
        // activate the marker (crosshair)
        marker.visible = true;
        marker.x       = stage.mouseX;
        marker.y       = stage.mouseY;
          
        stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      }
      
      private function onTangentMoved(_e:Event):void
      {
        __quad.tx = tangent.tangentX;
        __quad.ty = tangent.tangentY;
        
        drawCurve();
      }
      
      // draw the curve and the tangent
      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;
          
        dx        = endX - p0.x;
        dy        = endY - p0.y;
        __endTanX = p1.x + dx;
        __endTanY = p1.y + dy;
        
        if( __quad2 != null )
        {
          // draw the common tangent at the join point
          var canvas:Graphics = myTangent.graphics;
          canvas.clear();
          canvas.lineStyle(2,0xff0000);
          canvas.moveTo(p1.x, p1.y);
          canvas.lineTo(__endTanX, __endTanY);
          
          // define the second quad. Hermite segment
          __quad2.x0 = p1.x;
          __quad2.y0 = p1.y;
          __quad2.x1 = __p2X;
          __quad2.y1 = __p2Y;
          __quad2.tx = __endTanX;
          __quad2.ty = __endTanY;
        
          // join is as P1, so seccond quad. Hermite curve begins here  
          g.moveTo(p1.x, p1.y);
        
          for( t=delta; t<1.0; t+=delta )
          {
            g.lineTo(__quad2.getX(t), __quad2.getY(t));
          }
        
          g.lineTo(__quad2.getX(1.0), __quad2.getY(1.0));
        }
      }
      
      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;
        }
      }
      
      private function onMouseMove(_e:MouseEvent):void
      {
        marker.x = stage.mouseX;
        marker.y = stage.mouseY;
      }
        
      private function onMouseDown(_e:MouseEvent):void
      {
        __p2X = stage.mouseX;
        __p2Y = stage.mouseY;
        deactivateMarker();
          
        if( __quad2 == null )
        {
          __quad2 = new QuadraticHermiteCurve();
        }
        
        drawCurve();
      }
      
      // deactivate marker
      private function deactivateMarker():void
      {
        marker.visible = false;
          
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      }
      
    ]]>
  </mx:Script>
</mx:Application>