<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns="http://www.degrafa.com/2007"
    layout="absolute"
    width="600" height="500" 
    pageTitle="Degrafa Natural Cubic Spline Demo 3"
    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="400" backgroundColor="#FFFFFF" >
          <mx:Button x="10" y="378" label="Show Function" id="__show__" enabled="true" click="__onShowFunction()"/>
          <mx:Label x="6" y="362" text="-5"/>
          <mx:Label x="242" y="362" text="0" textAlign="center"/>
          <mx:Label x="480" y="362" text="5" textAlign="center"/>
          <mx:Label x="234" y="7" text="1" textAlign="center"/>
          <mx:Button x="152" y="378" label="Show Highlight" id="__highlight__" click="__onShowHighlight()"/>
     </mx:Canvas>
     <mx:Label text="Natural Cubic Spline 3" x="250" y="30" width="300" styleName="title"/>
    
     <mx:Canvas id="gridLayer" />
     <mx:Canvas id="theFunction" visible="false" />
     <mx:Canvas id="splineLayer" />
     <mx:Canvas id="splineOverlay" />
     
  <NaturalCubicSpline id="cubicSpline" graphicsTarget="{[splineLayer]}" >
    <stroke> 
     <SolidStroke weight="2" color="#0000FF" alpha="0.5"/>
    </stroke>
     </NaturalCubicSpline>
    
    <mx:Script>
      <![CDATA[
        import com.degrafa.geometry.splines.QuadData;
        import com.degrafa.core.collections.GraphicPointCollection;
        import com.degrafa.GraphicPoint;
        
        import com.degrafa.events.DegrafaEvent;
        
        private var __count:uint  = 0;
        private var __quads:Array = null;
        
        // this delta is hardcoded to the graph range
        private var __delta:Number = 10;
        private var __curX:Number  = 0;
        
        // px per unit x and y also very hardcoded to graph range
        private var __pxPerUnitX:Number = 48;
        private var __pxPerUnitY:Number = 350;
        
        private function test():void
        {
          // draw some axes, representing the range from -5 to 5 horizontally and 0 to 1 vertically
          __drawAxes();
          
          // Chebyshev nodes, hardcoded node count
          var denom:Number = 20;  // 2n+2
          var base:Array   = new Array();
          var indx:int     = 8;
          
          // this is hardcoded to the graph range and node count
          for( var i:Number=0; i<4; i++ )
          {
            var myX:Number = Math.cos( Math.PI*((2*(i+1)+1)/denom) );
            base[indx--]   = myX;
          }
          
          base[4]   = 0;
          var k:int = 5;
          for( var j:int=3; j>=0; j-- )
          {
            base[j] = -base[k++];
          }
          
          // exactly cover the graph endpoints
          base.splice(0,0,-1);
          base.push(1);
          
          // sample the function at these nodes in the interval [-5,5] to create the knots
          for( j=0; j<base.length; ++j )
          {
            myX            = 5*base[j]; // this only works if the interval is symmetric about the origin
            var myY:Number = 1/(1 + myX*myX);
            
            // convert function coordinates to hardcoded graph coordinates - don't change the graph in any way and expect this to work :)
            myX = 300 + myX*__pxPerUnitX;
            myY = 450 - myY*__pxPerUnitY;
            
            cubicSpline.addControlPoint(myX, myY);  // does force redundant redraws, so be warned
          }
          
          var g:Graphics = theFunction.graphics;
          g.lineStyle(2,0xff0000);
          myX = -5;
          myY = 1/(1 + 25);
          
          g.moveTo(300 + myX*__pxPerUnitX, 450 - myY*__pxPerUnitY);
          
          // plot the function, point-to-point
          while( myX < 5 )
          {
            myX += 0.125;
            myY  = 1/(1 + myX*myX);
            
            g.lineTo(300 + myX*__pxPerUnitX, 450 - myY*__pxPerUnitY); 
          }
          
          // cleanup
          myX = 5;
          myY = 1/(1 + 25);
          
          g.lineTo(300 + myX*__pxPerUnitX, 450 - myY*__pxPerUnitY); 
        }
        
        // draw a simple (partial) grid and some axes
        private function __drawAxes():void
        {
          var g:Graphics = gridLayer.graphics;
          g.lineStyle(2,0x000000);
       
       g.moveTo(300,100);
       g.lineTo(300,450);
       g.moveTo(60 ,450);
       g.lineTo(540,450);
        }
        
        private function __onShowFunction():void
        {
          theFunction.visible = !theFunction.visible;
        }
        
        private function __onShowHighlight():void
        {
          __highlight__.enabled = false;
          
          __quads = cubicSpline.quadApproximation;
          
          // some interval not part of the knot set, but make sure not to confuse function units and screen units (knots are in latter coordinates)
          var a:Number = -1;
          var b:Number = 1;
          
          a = 300 + a*__pxPerUnitX;
          b = 300 + b*__pxPerUnitX;
          
       __drawFill(a,b);
        }
        
        private function __drawFill(_a:Number, _b:Number):void
        {
          // no error-checking here, so beware :)
          var q:Array = cubicSpline.approximateInterval(_a, _b);
          if( q == null )
          {
            return;
          }
          
          var g:Graphics = splineOverlay.graphics;
          g.clear();
          g.lineStyle(2,0x00ff00);
          g.beginFill(0x00ff00,0.5);
            
          var qb:QuadData = q[0];
          var x0:Number   = qb.x0;
          var y0:Number   = qb.y0;
          g.moveTo(x0, y0);
          
          for( var i:uint=0; i<q.length; ++i )
          {
            qb = q[i];
           
            g.curveTo(qb.cx, qb.cy, qb.x1, qb.y1 );
          }
          
          // oh, no ... mr bill ... more hardcoding :)
          g.lineTo(qb.x1, 450);
          g.lineTo(x0   , 450);
          g.lineTo(x0   , y0 );
          
          g.endFill();
        }
        
      ]]>
    </mx:Script>

</mx:Application>