1712 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			1712 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
<head>
 | 
						|
 | 
						|
 | 
						|
<style>
 | 
						|
html {
 | 
						|
  font-family: Helvetica, Arial, sans-serif;
 | 
						|
  font-size: 100%;
 | 
						|
}
 | 
						|
 | 
						|
.controls {
 | 
						|
  margin: 1em 0;
 | 
						|
}
 | 
						|
 | 
						|
button {
 | 
						|
  display: inline-block;
 | 
						|
  border-radius: 3px;
 | 
						|
  border: none;
 | 
						|
  font-size: 0.9rem;
 | 
						|
  padding: 0.4rem 0.8em;
 | 
						|
  background: #69c773;
 | 
						|
  border-bottom: 1px solid #498b50;
 | 
						|
  color: white;
 | 
						|
  -webkit-font-smoothing: antialiased;
 | 
						|
  font-weight: bold;
 | 
						|
  margin: 0 0.25rem;
 | 
						|
  text-align: center;
 | 
						|
}
 | 
						|
 | 
						|
button:hover, button:focus {
 | 
						|
  opacity: 0.75;
 | 
						|
  cursor: pointer;
 | 
						|
}
 | 
						|
 | 
						|
button:active {
 | 
						|
  opacity: 1;
 | 
						|
  box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.1) inset;
 | 
						|
}
 | 
						|
 | 
						|
</style>
 | 
						|
 | 
						|
<! set height back to 500 />
 | 
						|
<svg id="svg" width="800" height="500"
 | 
						|
    xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 | 
						|
 | 
						|
<defs>
 | 
						|
    <radialGradient id="grad1" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(0,0,255); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(0,0,255); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
    <radialGradient id="grad2" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(0,255,0); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(0,255,0); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
    <radialGradient id="grad3" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(255,0,0); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(255,0,0); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
    <radialGradient id="grad4" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(192,63,192); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(192,63,192); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
    <radialGradient id="grad5" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(127,127,0); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(127,127,0); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
    <radialGradient id="grad6" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(127,0,127); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(127,0,127); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
    <radialGradient id="grad7" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(0,127,127); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(0,127,127); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
    <radialGradient id="grad8" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse">
 | 
						|
      <stop offset="0%"   style="stop-color:rgb(63,192,63); stop-opacity:0.3" />
 | 
						|
      <stop offset="100%" style="stop-color:rgb(63,192,63); stop-opacity:0" />
 | 
						|
    </radialGradient>
 | 
						|
</defs>
 | 
						|
 | 
						|
<path id="circleFill" d="M300,200 A 100,100 0,0,0 300,200" fill="#777" fill-opacity="0" />
 | 
						|
<path id="circle" d="M300,200 A 100,100 0,0,0 300,200" fill="none" stroke="black" />
 | 
						|
 | 
						|
<! elements for keyframe 1 />
 | 
						|
<text id="spanWedgeDesc" fill-opacity="0" >
 | 
						|
All spans are contained by a wedge.
 | 
						|
</text>
 | 
						|
<path id="span1" d="M200,200 Q300,300 200,300" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
<path id="span2" d="M200,200 C100,300 100,400 200,300" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
<path id="span3" d="M200,200 C300,100 100,400 300,200" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
<path id="wedge1" d="M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z" fill="url(#grad1)" fill-opacity="0"/>
 | 
						|
<path id="wedge2" d="M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z" fill="url(#grad2)" fill-opacity="0"/>
 | 
						|
<path id="wedge3" d="M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z" fill="url(#grad3)" fill-opacity="0"/>
 | 
						|
 | 
						|
<! keyframe 2 />
 | 
						|
<text id="trivialWedgeDesc1" fill-opacity="0" >
 | 
						|
Wedges that don't overlap can be
 | 
						|
</text>
 | 
						|
<text id="trivialWedgeDesc2" y="240" fill-opacity="0" >
 | 
						|
easily sorted.
 | 
						|
</text>
 | 
						|
<path id="span4" d="M200,200 Q300,300 400,300" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
<path id="span5" d="M200,200 Q280,320 200,400" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
<path id="span6" d="M200,200 Q60,340 100,400" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
<path id="wedge4" d="M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z" fill="url(#grad1)" fill-opacity="0"/>
 | 
						|
<path id="wedge5" d="M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z" fill="url(#grad2)" fill-opacity="0"/>
 | 
						|
<path id="wedge6" d="M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z" fill="url(#grad3)" fill-opacity="0"/>
 | 
						|
 | 
						|
 | 
						|
<! keyframe 3 />
 | 
						|
<text id="sectorDesc1" fill-opacity="0" >
 | 
						|
A sector is a wedge of a circle
 | 
						|
</text>
 | 
						|
<text id="sectorDesc2" y="240" fill-opacity="0" >
 | 
						|
containing a range of points.
 | 
						|
</text>
 | 
						|
<g id="xaxis" stroke-opacity="0" fill-opacity="0">
 | 
						|
    <path d="M100,200 L300,200" fill="none" stroke="rgb(191,191,191)"/>
 | 
						|
    <text x="100" y="220" fill="rgb(191,191,191)">-X</text>
 | 
						|
    <text x="300" y="220" text-anchor="end" fill="rgb(191,191,191)">+X</text>
 | 
						|
</g>
 | 
						|
<g id="yaxis" stroke-opacity="0" fill-opacity="0">
 | 
						|
    <path d="M200,100 L200,300" fill="none" stroke="rgb(191,191,191)"/>
 | 
						|
    <text x="205" y="100" alignment-baseline="hanging" fill="rgb(191,191,191)">-Y</text>
 | 
						|
    <text x="205" y="300" fill="rgb(191,191,191)">+Y</text>
 | 
						|
</g>
 | 
						|
<text id="sectorDescXYA" x="500" y="310" fill="rgb(0,0,255)" fill-opacity="0">
 | 
						|
X > 0>  Y > 0    Y < X</text>
 | 
						|
<text id="sectorDescXYB" x="500" y="360" fill="rgb(0,127,0)" fill-opacity="0">
 | 
						|
X < 0   Y > 0   -Y < X</text>
 | 
						|
<text id="sectorDescXYC" x="500" y="410" fill="rgb(255,0,0)" fill-opacity="0">
 | 
						|
X < 0   Y < 0    Y < X</text>
 | 
						|
<path id="wedgeXY8" d="M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z" fill="url(#grad1)" fill-opacity="0"/>
 | 
						|
<path id="wedgeXY6" d="M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z" fill="url(#grad2)" fill-opacity="0"/>
 | 
						|
<path id="wedgeXY3" d="M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z" fill="url(#grad3)" fill-opacity="0"/>
 | 
						|
 | 
						|
<! keyframe 4 />
 | 
						|
<text id="lineSingleDesc" fill-opacity="0" >
 | 
						|
Line spans are contained by a single sector.
 | 
						|
</text>
 | 
						|
<text id="sectorDescXY1" x="500" y="460" fill="rgb(192,63,192)" fill-opacity="0">
 | 
						|
X > 0   Y < 0   -Y < X</text>
 | 
						|
<text id="sectorDescXY2" x="500" y="460" fill="rgb(127,127,0)" fill-opacity="0">
 | 
						|
X > 0   Y < 0   -Y > X</text>
 | 
						|
<text id="sectorDescXY3" x="500" y="460" fill="rgb(255,0,0)" fill-opacity="0">
 | 
						|
X < 0   Y < 0    Y < X</text>
 | 
						|
<text id="sectorDescXY4" x="500" y="460" fill="rgb(127,0,127)" fill-opacity="0">
 | 
						|
X < 0   Y < 0    Y > X</text>
 | 
						|
<text id="sectorDescXY5" x="500" y="460" fill="rgb(0,127,127)" fill-opacity="0">
 | 
						|
X < 0   Y > 0   -Y < X</text>
 | 
						|
<text id="sectorDescXY6" x="500" y="460" fill="rgb(0,127,0)" fill-opacity="0">
 | 
						|
X < 0   Y > 0   -Y < X</text>
 | 
						|
<text id="sectorDescXY7" x="500" y="460" fill="rgb(63,192,63)" fill-opacity="0">
 | 
						|
X > 0   Y > 0    Y > X</text>
 | 
						|
<text id="sectorDescXY8" x="500" y="460" fill="rgb(0,0,255)" fill-opacity="0">
 | 
						|
X > 0   Y > 0    Y < X</text>
 | 
						|
<path id="wedgeXY1" d="M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z" fill="url(#grad4)" fill-opacity="0"/>
 | 
						|
<path id="wedgeXY2" d="M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z" fill="url(#grad5)" fill-opacity="0"/>
 | 
						|
<path id="wedgeXY4" d="M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z" fill="url(#grad6)" fill-opacity="0"/>
 | 
						|
<path id="wedgeXY5" d="M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z" fill="url(#grad7)" fill-opacity="0"/>
 | 
						|
<path id="wedgeXY7" d="M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z" fill="url(#grad8)" fill-opacity="0"/>
 | 
						|
<path id="lineSegment" d="M200,200 L200,624.26" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
 | 
						|
<! keyframe 5 />
 | 
						|
<text id="curveMultipleDesc1" fill-opacity="0" >
 | 
						|
A curve span may cover more
 | 
						|
</text>
 | 
						|
<text id="curveMultipleDesc2" y="240" fill-opacity="0" >
 | 
						|
than one sector.
 | 
						|
</text>
 | 
						|
<path id="curveSegment" d="M200,200 C250,200 300,150 300,100" fill="none" stroke="black" stroke-opacity="0"/>
 | 
						|
<path id="curveSegment1" d="M200,200 C250,200 300,150 300,100" fill="none"/>
 | 
						|
<path id="curveSegment2" d="M200,200 C250,200 300,150 200,100" fill="none"/>
 | 
						|
<path id="curveSegment3" d="M200,200 C350,200 250,-150 170,300" fill="none"/>
 | 
						|
 | 
						|
<! keyframe 6 />
 | 
						|
<text id="line1DDest1" fill-opacity="0" >
 | 
						|
Some lines occupy one-dimensional
 | 
						|
</text>
 | 
						|
<text id="line1DDest2" y="240" fill-opacity="0" >
 | 
						|
sectors.
 | 
						|
</text>
 | 
						|
<text id="sectorDescXY9" x="500" y="460" fill="rgb(192,92,31)" fill-opacity="0">
 | 
						|
X > 0   Y == 0</text>
 | 
						|
<text id="sectorDescXY10" x="500" y="460" fill="rgb(31,92,192)" fill-opacity="0">
 | 
						|
Y > 0   0 == X</text>
 | 
						|
<text id="sectorDescXY11" x="500" y="460" fill="rgb(127,63,127)" fill-opacity="0">
 | 
						|
X < 0   Y == X</text>
 | 
						|
<path id="horzSegment" d="M200,200 L341.4,200" fill="none" stroke="rgb(192,92,31)" stroke-width="2" stroke-opacity="0"/>
 | 
						|
<path id="vertSegment" d="M200,200 L200,341.4" fill="none" stroke="rgb(31,92,192)" stroke-width="2" stroke-opacity="0"/>
 | 
						|
<path id="diagSegment" d="M200,200 L100,100"   fill="none" stroke="rgb(127,63,127)" stroke-width="2" stroke-opacity="0"/>
 | 
						|
 | 
						|
<! keyframe 7 />
 | 
						|
<text id="curve1dDesc1" fill-opacity="0" >
 | 
						|
Some curves initially occupy
 | 
						|
</text>
 | 
						|
<text id="curve1dDesc2" y="240" fill-opacity="0" >
 | 
						|
one-dimensional sectors, then diverge.
 | 
						|
</text>
 | 
						|
<path id="cubicSegment" fill="none" stroke="black" />
 | 
						|
<path id="cubicSegment1" d="M200,200 C200,200 200,200 200,200" fill="none" />
 | 
						|
<path id="cubicSegment2" d="M200,200 C250,200 300,200 300,100" fill="none"/>
 | 
						|
 | 
						|
<text id="sectorNumberDesc" fill-opacity="0" >
 | 
						|
Each sector is assigned a number.
 | 
						|
</text>
 | 
						|
<text id="spanSectorDesc" fill-opacity="0" >
 | 
						|
Each span has a bit set for one or more sectors.
 | 
						|
</text>
 | 
						|
<text id="bitOverDesc" fill-opacity="0" >
 | 
						|
Span sets allow rough sorting without angle computation.
 | 
						|
</text>
 | 
						|
 | 
						|
</svg>
 | 
						|
 | 
						|
<! canvas support />
 | 
						|
<script>
 | 
						|
 | 
						|
var keyFrameQueue = [];
 | 
						|
var animationsPending = [];
 | 
						|
var animationsActive = [];
 | 
						|
var displayList = [];
 | 
						|
var visibleFinished = [];
 | 
						|
 | 
						|
var animationState = {};
 | 
						|
animationState.reset = function () {
 | 
						|
    this.start = null;
 | 
						|
    this.time = 0;
 | 
						|
    this.requestID = null;
 | 
						|
    this.paused = false;
 | 
						|
    this.displayEngine = 'Canvas';
 | 
						|
}
 | 
						|
 | 
						|
circle.center = { x: 200, y: 200 }
 | 
						|
circle.radius = 100;
 | 
						|
 | 
						|
function assert(condition) {
 | 
						|
    if (!condition) debugger;
 | 
						|
}
 | 
						|
 | 
						|
function CanvasGrads(ctx) {
 | 
						|
    var grad1 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad1.addColorStop(0, "rgba(0,0,255, 0.3)");
 | 
						|
    grad1.addColorStop(1, "rgba(0,0,255, 0)");
 | 
						|
    var grad2 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad2.addColorStop(0, "rgba(0,255,0, 0.3)");
 | 
						|
    grad2.addColorStop(1, "rgba(0,255,0, 0)");
 | 
						|
    var grad3 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad3.addColorStop(0, "rgba(255,0,0, 0.3)");
 | 
						|
    grad3.addColorStop(1, "rgba(255,0,0, 0)");
 | 
						|
    var grad4 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad4.addColorStop(0, "rgba(192,63,192, 0.3)");
 | 
						|
    grad4.addColorStop(1, "rgba(192,63,192, 0)");
 | 
						|
    var grad5 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad5.addColorStop(0, "rgba(127,127,0, 0.3)");
 | 
						|
    grad5.addColorStop(1, "rgba(127,127,0, 0)");
 | 
						|
    var grad6 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad6.addColorStop(0, "rgba(127,0,127, 0.3)");
 | 
						|
    grad6.addColorStop(1, "rgba(127,0,127, 0)");
 | 
						|
    var grad7 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad7.addColorStop(0, "rgba(0,127,127, 0.3)");
 | 
						|
    grad7.addColorStop(1, "rgba(0,127,127, 0)");
 | 
						|
    var grad8 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300);
 | 
						|
    grad8.addColorStop(0, "rgba(63,192,63, 0.3)");
 | 
						|
    grad8.addColorStop(1, "rgba(63,192,63, 0)");
 | 
						|
    var data = {
 | 
						|
        grad1: grad1,
 | 
						|
        grad2: grad2,
 | 
						|
        grad3: grad3,
 | 
						|
        grad4: grad4,
 | 
						|
        grad5: grad5,
 | 
						|
        grad6: grad6,
 | 
						|
        grad7: grad7,
 | 
						|
        grad8: grad8,
 | 
						|
    };
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function skip_sep(data) {
 | 
						|
    if (!data.length) {
 | 
						|
        return data;
 | 
						|
    }
 | 
						|
    while (data[0] == ' ' || data[0] == ',') {
 | 
						|
        data = data.substring(1);
 | 
						|
    }
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function find_points(str, value, count, isRelative, relative) {
 | 
						|
    var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g;
 | 
						|
    var match;
 | 
						|
    for (var index = 0; index < count; ++index) {
 | 
						|
        str = skip_sep(str);
 | 
						|
        match = numRegEx.exec(str);
 | 
						|
        assert(match);
 | 
						|
        var x = Number(match[0]);
 | 
						|
        str = skip_sep(str);
 | 
						|
        match = numRegEx.exec(str);
 | 
						|
        assert(match);
 | 
						|
        var y = Number(match[0]);
 | 
						|
        value[index] = { x: x, y : y };
 | 
						|
    }
 | 
						|
    if (isRelative) {
 | 
						|
        for (var index = 0; index < count; index++) {
 | 
						|
            value[index].x += relative.x;
 | 
						|
            value[index].y += relative.y;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return str.substring(match.index + match[0].length);
 | 
						|
}
 | 
						|
 | 
						|
function find_scalar(str, obj, isRelative, relative) {
 | 
						|
    var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g;
 | 
						|
    str = skip_sep(str);
 | 
						|
    var match = numRegEx.exec(str);
 | 
						|
    obj.value = Number(match[0]);
 | 
						|
    if (isRelative) {
 | 
						|
        obj.value += relative;
 | 
						|
    }
 | 
						|
    return str.substring(match.index + match[0].length);
 | 
						|
}
 | 
						|
 | 
						|
function parse_path(data) {
 | 
						|
    var path = "ctx.beginPath();\n";
 | 
						|
    var f = {x:0, y:0};
 | 
						|
    var c = {x:0, y:0};
 | 
						|
    var lastc = {x:0, y:0};
 | 
						|
    var points = [];
 | 
						|
    var op = '\0';
 | 
						|
    var previousOp = '\0';
 | 
						|
    var relative = false;
 | 
						|
    for (;;) {
 | 
						|
        data = skip_sep(data);
 | 
						|
        if (!data.length) {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        var ch = data[0];
 | 
						|
        if (('0' <= ch && ch <= '9') || ch == '-' || ch == '+') {
 | 
						|
            assert(op != '\0');
 | 
						|
        } else if (ch == ' ' || ch == ',') {
 | 
						|
            data = skip_sep(data);
 | 
						|
        } else {
 | 
						|
            op = ch;
 | 
						|
            relative = false;
 | 
						|
            if ('a' <= op && op <= 'z') {
 | 
						|
                op = op.toUpperCase();
 | 
						|
                relative = true;
 | 
						|
            }
 | 
						|
            data = data.substring(1);
 | 
						|
            data = skip_sep(data);
 | 
						|
        }
 | 
						|
        switch (op) {
 | 
						|
            case 'A':
 | 
						|
                var radii = [];
 | 
						|
                data = find_points(data, radii, 1, false, null);
 | 
						|
                var xaxisObj = {};
 | 
						|
                data = find_scalar(data, xaxisObj, false, null);
 | 
						|
                var largeArcObj = {};
 | 
						|
                data = find_scalar(data, largeArcObj, false, null);
 | 
						|
                var sweepObj = {};
 | 
						|
                data = find_scalar(data, sweepObj, false, null);
 | 
						|
                data = find_points(data, points, 1, relative, c);
 | 
						|
                var mid = { x: (c.x + points[0].x) / 2, y: (c.y + points[0].y) / 2 };
 | 
						|
                var midVec = { x: mid.x - c.x, y: mid.y - c.y };
 | 
						|
                var midLenSqr = midVec.x * midVec.x + midVec.y * midVec.y;
 | 
						|
                var radius = radii[0].x;
 | 
						|
                var scale = Math.sqrt(midLenSqr) / Math.sqrt(radius * radius - midLenSqr);
 | 
						|
                var tangentPt = { x: mid.x + midVec.y * scale,
 | 
						|
                                  y: mid.y - midVec.x * scale };
 | 
						|
                path += "ctx.arcTo(" + tangentPt.x + "," + tangentPt.y + ","
 | 
						|
                    + points[0].x + "," + points[0].y + "," + radius + ");\n";
 | 
						|
                c = points[0];
 | 
						|
                break;
 | 
						|
            case 'M':
 | 
						|
                data = find_points(data, points, 1, relative, c);
 | 
						|
                path += "ctx.moveTo(" + points[0].x + "," + points[0].y + ");\n";
 | 
						|
                op = 'L';
 | 
						|
                c = points[0];
 | 
						|
                break;
 | 
						|
            case 'L':
 | 
						|
                data = find_points(data, points, 1, relative, c);
 | 
						|
                path += "ctx.lineTo(" + points[0].x + "," + points[0].y + ");\n";
 | 
						|
                c = points[0];
 | 
						|
                break;
 | 
						|
            case 'H': {
 | 
						|
                var xObj = {};
 | 
						|
                data = find_scalar(data, xObj, relative, c.x);
 | 
						|
                path += "ctx.lineTo(" + xObj.value + "," + c.y + ");\n";
 | 
						|
                c.x = xObj.value;
 | 
						|
            } break;
 | 
						|
            case 'V': {
 | 
						|
                var yObj = {};
 | 
						|
                data = find_scalar(data, y, relative, c.y);
 | 
						|
                path += "ctx.lineTo(" + c.x + "," + yObj.value+ ");\n";
 | 
						|
                c.y = yObj.value;
 | 
						|
            } break;
 | 
						|
            case 'C':
 | 
						|
                data = find_points(data, points, 3, relative, c);
 | 
						|
                path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + ","
 | 
						|
                    + points[1].x + "," + points[1].y + ","
 | 
						|
                    + points[2].x + "," + points[2].y + ");\n";
 | 
						|
                lastc = points[1];
 | 
						|
                c = points[2];
 | 
						|
                break;
 | 
						|
            case 'S':
 | 
						|
                var pts2_3 = [];
 | 
						|
                data = find_points(data, pts2_3, 2, relative, c);
 | 
						|
                points[0] = c;
 | 
						|
                points[1] = pts2_3[0];
 | 
						|
                points[2] = pts2_3[1];
 | 
						|
                if (previousOp == 'C' || previousOp == 'S') {
 | 
						|
                    points[0].x -= lastc.x - c.x;
 | 
						|
                    points[0].y -= lastc.y - c.y;
 | 
						|
                }
 | 
						|
                path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + ","
 | 
						|
                    + points[1].x + "," + points[1].y + ","
 | 
						|
                    + points[2].x + "," + points[2].y + ");\n";
 | 
						|
                lastc = points[1];
 | 
						|
                c = points[2];
 | 
						|
                break;
 | 
						|
            case 'Q':  // Quadratic Bezier Curve
 | 
						|
                data = find_points(data, points, 2, relative, c);
 | 
						|
                path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + ","
 | 
						|
                    + points[1].x + "," + points[1].y + ");\n";
 | 
						|
                lastc = points[0];
 | 
						|
                c = points[1];
 | 
						|
                break;
 | 
						|
            case 'T':
 | 
						|
                var pts2 = [];
 | 
						|
                data = find_points(data, pts2, 1, relative, c);
 | 
						|
                points[0] = pts2[0];
 | 
						|
                points[1] = pts2[0];
 | 
						|
                if (previousOp == 'Q' || previousOp == 'T') {
 | 
						|
                    points[0].x = c.x * 2 - lastc.x;
 | 
						|
                    points[0].y = c.y * 2 - lastc.y;
 | 
						|
                }
 | 
						|
                path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + ","
 | 
						|
                    + points[1].x + "," + points[1].y + ");\n";
 | 
						|
                path.quadTo(points[0], points[1]);
 | 
						|
                lastc = points[0];
 | 
						|
                c = points[1];
 | 
						|
                break;
 | 
						|
            case 'Z':
 | 
						|
                path += "ctx.closePath();\n";
 | 
						|
                c = f;
 | 
						|
                op = '\0';
 | 
						|
                break;
 | 
						|
            case '~':
 | 
						|
                var args = [];
 | 
						|
                data = find_points(data, args, 2, false, null);
 | 
						|
                path += "moveTo(" + args[0].x + "," + args[0].y + ");\n";
 | 
						|
                path += "lineTo(" + args[1].x + "," + args[1].y + ");\n";
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                return false;
 | 
						|
        }
 | 
						|
        if (previousOp == 0) {
 | 
						|
            f = c;
 | 
						|
        }
 | 
						|
        previousOp = op;
 | 
						|
    }
 | 
						|
    return path;
 | 
						|
}
 | 
						|
 | 
						|
function CanvasPaths(ctx) {
 | 
						|
    var svgStrs = {
 | 
						|
    // keyframe 1
 | 
						|
        span1:   "M200,200 Q300,300 200,300",
 | 
						|
        span2:   "M200,200 C100,300 100,400 200,300",
 | 
						|
        span3:   "M200,200 C300,100 100,400 300,200",
 | 
						|
        wedge1:  "M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z",
 | 
						|
        wedge2:  "M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z",
 | 
						|
        wedge3:  "M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z",
 | 
						|
    // keyframe 2
 | 
						|
        span4:   "M200,200 Q300,300 400,300",
 | 
						|
        span5:   "M200,200 Q280,320 200,400",
 | 
						|
        span6:   "M200,200 Q60,340 100,400",
 | 
						|
        wedge4:  "M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z",
 | 
						|
        wedge5:  "M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z",
 | 
						|
        wedge6:  "M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z",
 | 
						|
    // keyframe 3
 | 
						|
        xaxis:    "M100,200 L300,200",
 | 
						|
        yaxis:    "M200,100 L200,300",
 | 
						|
        wedgeXY8: "M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z",
 | 
						|
        wedgeXY6: "M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z",
 | 
						|
        wedgeXY3: "M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z",
 | 
						|
    // keyframe 4
 | 
						|
        wedgeXY1: "M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z",
 | 
						|
        wedgeXY2: "M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z",
 | 
						|
        wedgeXY4: "M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z",
 | 
						|
        wedgeXY5: "M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z",
 | 
						|
        wedgeXY7: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z",
 | 
						|
        lineSegment: "M200,200 L200,624.26",
 | 
						|
    // keyframe 5
 | 
						|
        curveSegment:  "M200,200 C250,200 300,150 300,100",
 | 
						|
        curveSegment1: "M200,200 C250,200 300,150 300,100",
 | 
						|
        curveSegment2: "M200,200 C250,200 300,150 200,100",
 | 
						|
        curveSegment3: "M200,200 C350,200 250,-150 170,300",
 | 
						|
    // keyframe 6
 | 
						|
        horzSegment: "M200,200 L341.4,200",
 | 
						|
        vertSegment: "M200,200 L200,341.4",
 | 
						|
        diagSegment: "M200,200 L100,100",
 | 
						|
    // keyframe 7
 | 
						|
        cubicSegment:  "M200,200 C200,200 200,200 200,200",
 | 
						|
        cubicSegment1: "M200,200 C200,200 200,200 200,200",
 | 
						|
        cubicSegment2: "M200,200 C250,200 300,200 300,100",
 | 
						|
    };
 | 
						|
    var paths = [];
 | 
						|
    var keys = Object.keys(svgStrs);
 | 
						|
    for (var index in keys) {
 | 
						|
        var key = keys[index];
 | 
						|
        var str = svgStrs[key];
 | 
						|
        var path = parse_path(str);
 | 
						|
        var record = [];
 | 
						|
        paths[key] = {
 | 
						|
            str: str,
 | 
						|
            funcBody: path,
 | 
						|
        };
 | 
						|
    }
 | 
						|
    return paths;
 | 
						|
}
 | 
						|
 | 
						|
function canvas_fill_font(record) {
 | 
						|
    assert(record);
 | 
						|
    var str = 'ctx.font = "normal 1.3rem Helvetica,Arial";\n';
 | 
						|
    if (record.fillStyle) {
 | 
						|
        str += 'ctx.fillStyle = ' + record.fillStyle + ';\n';
 | 
						|
    }
 | 
						|
    return str;
 | 
						|
}
 | 
						|
 | 
						|
function canvas_fill_text(record) {
 | 
						|
    assert(record);
 | 
						|
    assert(typeof record.fillText == 'string');
 | 
						|
    return 'ctx.fillText("' + record.fillText + '"';
 | 
						|
}
 | 
						|
 | 
						|
function canvas_xy(record) {
 | 
						|
    var x = typeof record.x == "number" ? record.x : 400;
 | 
						|
    var y = typeof record.y == "number" ? record.y : 200;
 | 
						|
    return ', ' + x + ', ' + y + ');\n';
 | 
						|
}
 | 
						|
 | 
						|
function canvas_text_xy(record) {
 | 
						|
    return canvas_fill_text(record) + canvas_xy(record);
 | 
						|
}
 | 
						|
 | 
						|
function add_canvas_stroke(paths, data, id, strokeStyle) {
 | 
						|
    var record = {};
 | 
						|
    record.data = paths[id].funcBody;
 | 
						|
    record.style = 'ctx.strokeStyle = ' + (strokeStyle ? strokeStyle : '"black"') + ';\n';
 | 
						|
    record.draw = 'ctx.stroke();\n';
 | 
						|
    record.func = new Function('ctx', record.data + record.style + record.draw);
 | 
						|
    return data[id] = record;
 | 
						|
}
 | 
						|
 | 
						|
function add_canvas_style(record, style) {
 | 
						|
    record.style += style;
 | 
						|
    record.func = new Function('ctx', record.data + record.style + record.draw);
 | 
						|
}
 | 
						|
 | 
						|
function add_canvas_fill(paths, data, id, fillStyle) {
 | 
						|
    var record = {};
 | 
						|
    record.data = paths[id].funcBody;
 | 
						|
    record.style = 'ctx.fillStyle = ' + (fillStyle ? fillStyle : '"black"') + ';\n';
 | 
						|
    record.draw = 'ctx.fill();\n';
 | 
						|
    record.func = new Function('ctx', record.data + record.style + record.draw);
 | 
						|
    return data[id] = record;
 | 
						|
}
 | 
						|
 | 
						|
function add_canvas_text(data, id, params) {
 | 
						|
    var record = {};
 | 
						|
    record.style = canvas_fill_font(params);
 | 
						|
    record.draw = canvas_fill_text(params);
 | 
						|
    record.position = canvas_xy(params);
 | 
						|
    record.x = params.x;
 | 
						|
    record.y = params.y;
 | 
						|
    record.func = new Function('ctx', record.style + record.draw + record.position);
 | 
						|
    return data[id] = record;
 | 
						|
}
 | 
						|
 | 
						|
function keyframe1(grads, paths) {
 | 
						|
    var data = [];
 | 
						|
    add_canvas_text(data, "spanWedgeDesc", { fillText:"All spans are contained by a wedge" } );
 | 
						|
    add_canvas_stroke(paths, data, "span1");
 | 
						|
    add_canvas_stroke(paths, data, "span2");
 | 
						|
    add_canvas_stroke(paths, data, "span3");
 | 
						|
    add_canvas_fill(paths, data, "wedge1", "grads.grad1");
 | 
						|
    add_canvas_fill(paths, data, "wedge2", "grads.grad2");
 | 
						|
    add_canvas_fill(paths, data, "wedge3", "grads.grad3");
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function keyframe2(grads, paths) {
 | 
						|
    var data = [];
 | 
						|
    add_canvas_text(data, "trivialWedgeDesc1", { fillText:"Wedges that don't overlap can be" } );
 | 
						|
    add_canvas_text(data, "trivialWedgeDesc2", { fillText:"easily sorted.", y:240 } );
 | 
						|
    add_canvas_stroke(paths, data, "span4").debug = true;
 | 
						|
    add_canvas_stroke(paths, data, "span5");
 | 
						|
    add_canvas_stroke(paths, data, "span6");
 | 
						|
    add_canvas_fill(paths, data, "wedge4", "grads.grad1");
 | 
						|
    add_canvas_fill(paths, data, "wedge5", "grads.grad2");
 | 
						|
    add_canvas_fill(paths, data, "wedge6", "grads.grad3");
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function setup_axes(paths, data) {
 | 
						|
    var color = '"rgb(191,191,191)"';
 | 
						|
    var xaxis = add_canvas_stroke(paths, data, "xaxis", color);
 | 
						|
    xaxis.funcBody = canvas_fill_font( { fillStyle:color } );
 | 
						|
    xaxis.funcBody += canvas_text_xy( { fillText:"-X", x:100, y:220 } );
 | 
						|
    xaxis.funcBody += "ctx.textAlign = 'right';\n";
 | 
						|
    xaxis.funcBody += canvas_text_xy( { fillText:"+X", x:300, y:220 } );
 | 
						|
    xaxis.func = new Function('ctx', xaxis.data + xaxis.style + xaxis.draw + xaxis.funcBody);
 | 
						|
    var yaxis = add_canvas_stroke(paths, data, "yaxis", color);
 | 
						|
    yaxis.funcBody = canvas_fill_font( { fillStyle:color } );
 | 
						|
    yaxis.funcBody += "ctx.textBaseline = 'hanging';\n";
 | 
						|
    yaxis.funcBody += canvas_text_xy( { fillText:"-Y", x:205, y:100 } );
 | 
						|
    yaxis.funcBody += "ctx.textBaseline = 'alphabetic';\n";
 | 
						|
    yaxis.funcBody += canvas_text_xy( { fillText:"+Y", x:205, y:300 } );
 | 
						|
    yaxis.func = new Function('ctx', yaxis.data + yaxis.style + yaxis.draw + yaxis.funcBody);
 | 
						|
}
 | 
						|
 | 
						|
function keyframe3(grads, paths) {
 | 
						|
    var data = [];
 | 
						|
    add_canvas_text(data, "sectorDesc1", { fillText:"A sector is a wedge of a circle" } );
 | 
						|
    add_canvas_text(data, "sectorDesc2", { fillText:"containing a range of points.", y:240 } );
 | 
						|
    setup_axes(paths, data);
 | 
						|
    add_canvas_text(data, "sectorDescXYA",
 | 
						|
        { fillText:"X > 0   Y > 0    Y < X", x:500, y:310, fillStyle:'"rgb(0,0,255)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXYB",
 | 
						|
        { fillText:"X < 0   Y > 0   -Y < X", x:500, y:360, fillStyle:'"rgb(0,127,0)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXYC",
 | 
						|
        { fillText:"X < 0   Y < 0    Y < X", x:500, y:410, fillStyle:'"rgb(255,0,0)"'} );
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3");
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function keyframe4(grads, paths) {
 | 
						|
    var data = [];
 | 
						|
    setup_axes(paths, data);
 | 
						|
    add_canvas_text(data, "lineSingleDesc",
 | 
						|
        { fillText:"Line spans are contained by a single sector." } );
 | 
						|
    add_canvas_text(data, "sectorDescXY1",
 | 
						|
        { fillText:"X > 0   Y < 0   -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY2",
 | 
						|
        { fillText:"X > 0   Y < 0   -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY3",
 | 
						|
        { fillText:"X < 0   Y < 0    Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY4",
 | 
						|
        { fillText:"X < 0   Y < 0    Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY5",
 | 
						|
        { fillText:"X < 0   Y > 0   -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY6",
 | 
						|
        { fillText:"X < 0   Y > 0   -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY7",
 | 
						|
        { fillText:"X > 0   Y > 0    Y > X", x:500, y:460, fillStyle:'"rgb(63,192,63)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY8",
 | 
						|
        { fillText:"X > 0   Y > 0    Y < X", x:500, y:460, fillStyle:'"rgb(0,0,255)"'} );
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY7", "grads.grad8");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1");
 | 
						|
    add_canvas_stroke(paths, data, "lineSegment");
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function keyframe5(grads, paths) {
 | 
						|
    var data = [];
 | 
						|
    setup_axes(paths, data);
 | 
						|
    add_canvas_text(data, "curveMultipleDesc1",
 | 
						|
        { fillText:"A curve span may cover more" } );
 | 
						|
    add_canvas_text(data, "curveMultipleDesc2",
 | 
						|
        { fillText:"than one sector.", y:240 } );
 | 
						|
    add_canvas_stroke(paths, data, "curveSegment");
 | 
						|
    add_canvas_text(data, "sectorDescXY1",
 | 
						|
        { fillText:"X > 0   Y < 0   -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY2",
 | 
						|
        { fillText:"X > 0   Y < 0   -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY3",
 | 
						|
        { fillText:"X < 0   Y < 0    Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY4",
 | 
						|
        { fillText:"X < 0   Y < 0    Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY5",
 | 
						|
        { fillText:"X < 0   Y > 0   -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY6",
 | 
						|
        { fillText:"X < 0   Y > 0   -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} );
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2");
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function keyframe6(grads, paths) {
 | 
						|
    var data = [];
 | 
						|
    setup_axes(paths, data);
 | 
						|
 | 
						|
    add_canvas_text(data, "line1DDest1",
 | 
						|
        { fillText:"Some lines occupy one-dimensional" } );
 | 
						|
    add_canvas_text(data, "line1DDest2",
 | 
						|
        { fillText:"sectors.", y:240 } );
 | 
						|
    add_canvas_text(data, "sectorDescXY9",
 | 
						|
        { fillText:"X > 0   Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } );
 | 
						|
    add_canvas_text(data, "sectorDescXY10",
 | 
						|
        { fillText:"Y > 0   0 == X", x:500, y:460, fillStyle:'"rgb(31,92,192)"' } );
 | 
						|
    add_canvas_text(data, "sectorDescXY11",
 | 
						|
        { fillText:"X < 0   Y == X", x:500, y:460, fillStyle:'"rgb(127,63,127)"' } );
 | 
						|
    var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"');
 | 
						|
    add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n");
 | 
						|
    var vert = add_canvas_stroke(paths, data, "vertSegment", '"rgb(31,92,192)"');
 | 
						|
    add_canvas_style(vert, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n");
 | 
						|
    var diag = add_canvas_stroke(paths, data, "diagSegment", '"rgb(127,63,127)"');
 | 
						|
    add_canvas_style(diag, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n");
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function keyframe7(grads, paths) {
 | 
						|
    var data = [];
 | 
						|
    setup_axes(paths, data);
 | 
						|
    add_canvas_text(data, "curve1dDesc1",
 | 
						|
        { fillText:"Some curves initially occupy" } );
 | 
						|
    add_canvas_text(data, "curve1dDesc2",
 | 
						|
        { fillText:"one-dimensional sectors, then diverge.", y:240 } );
 | 
						|
    add_canvas_stroke(paths, data, "cubicSegment");
 | 
						|
    add_canvas_text(data, "sectorDescXY1",
 | 
						|
        { fillText:"X > 0   Y < 0   -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} );
 | 
						|
    add_canvas_text(data, "sectorDescXY9",
 | 
						|
        { fillText:"X > 0   Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } );
 | 
						|
    var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"');
 | 
						|
    add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n");
 | 
						|
    add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4");
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
var canvasData = null;
 | 
						|
 | 
						|
function CanvasInit(keyframe) {
 | 
						|
    canvasData = window[keyframe](grads, paths);
 | 
						|
}
 | 
						|
 | 
						|
</script>
 | 
						|
 | 
						|
<script>
 | 
						|
 | 
						|
function interp(A, B, t) {
 | 
						|
    return A + (B - A) * t;
 | 
						|
}
 | 
						|
 | 
						|
function interp_cubic_coords(x1, x2, x3, x4, t)
 | 
						|
{
 | 
						|
    var ab = interp(x1, x2, t);
 | 
						|
    var bc = interp(x2, x3, t);
 | 
						|
    var cd = interp(x3, x4, t);
 | 
						|
    var abc = interp(ab, bc, t);
 | 
						|
    var bcd = interp(bc, cd, t);
 | 
						|
    var abcd = interp(abc, bcd, t);
 | 
						|
    return abcd;
 | 
						|
}
 | 
						|
 | 
						|
function cubic_partial(value, p) {
 | 
						|
    var x1 = p[0], y1 = p[1], x2 = p[2], y2 = p[3];
 | 
						|
    var x3 = p[4], y3 = p[5], x4 = p[6], y4 = p[7];
 | 
						|
    var t1 = 0, t2 = value;
 | 
						|
    var ax = interp_cubic_coords(x1, x2, x3, x4, t1);
 | 
						|
    var ay = interp_cubic_coords(y1, y2, y3, y4, t1);
 | 
						|
    var ex = interp_cubic_coords(x1, x2, x3, x4, (t1*2+t2)/3);
 | 
						|
    var ey = interp_cubic_coords(y1, y2, y3, y4, (t1*2+t2)/3);
 | 
						|
    var fx = interp_cubic_coords(x1, x2, x3, x4, (t1+t2*2)/3);
 | 
						|
    var fy = interp_cubic_coords(y1, y2, y3, y4, (t1+t2*2)/3);
 | 
						|
    var dx = interp_cubic_coords(x1, x2, x3, x4, t2);
 | 
						|
    var dy = interp_cubic_coords(y1, y2, y3, y4, t2);
 | 
						|
    var mx = ex * 27 - ax * 8 - dx;
 | 
						|
    var my = ey * 27 - ay * 8 - dy;
 | 
						|
    var nx = fx * 27 - ax - dx * 8;
 | 
						|
    var ny = fy * 27 - ay - dy * 8;
 | 
						|
    var bx = (mx * 2 - nx) / 18;
 | 
						|
    var by = (my * 2 - ny) / 18;
 | 
						|
    var cx = (nx * 2 - mx) / 18;
 | 
						|
    var cy = (ny * 2 - my) / 18;
 | 
						|
    var array = [
 | 
						|
        ax, ay, bx, by, cx, cy, dx, dy
 | 
						|
    ];
 | 
						|
    return array;
 | 
						|
}
 | 
						|
 | 
						|
function evaluate_at(value, p) {
 | 
						|
    var array = [];
 | 
						|
    for (var index = 0; index < p.length; ++index) {
 | 
						|
        var func = new Function('value', 'return ' + p[index] + ';');
 | 
						|
        array[index] = func(value);
 | 
						|
    }
 | 
						|
    return array;
 | 
						|
}
 | 
						|
 | 
						|
function interpolate_at(value, p) {
 | 
						|
    var array = [];
 | 
						|
    var start = p[0];
 | 
						|
    var end = p[1];
 | 
						|
    assert(typeof end == typeof start);
 | 
						|
    switch (typeof start) {
 | 
						|
        case 'object':
 | 
						|
            for (var index = 0; index < start.length; ++index) {
 | 
						|
                array[index] = interp(start[index], end[index], value);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 'number':
 | 
						|
            array[index] = interp(start, end, value);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
    return array;
 | 
						|
}
 | 
						|
 | 
						|
function AnimationAddCommon(timing, range, attr, inParams) {
 | 
						|
    var animation = {
 | 
						|
        timing: timing,
 | 
						|
        range: range,
 | 
						|
        attr: attr,
 | 
						|
        inParams: inParams,
 | 
						|
        duration: timing[1] - timing[0],
 | 
						|
        remaining: timing[1] - timing[0],
 | 
						|
        firstStep: true,
 | 
						|
    }
 | 
						|
    animationsPending.push(animation);
 | 
						|
    return animation;
 | 
						|
}
 | 
						|
 | 
						|
function AnimationAddSVG(timing, element, range, attr, inParams) {
 | 
						|
    var animation = AnimationAddCommon(timing, range, attr, inParams);
 | 
						|
    animation.element = element;
 | 
						|
    return animation;
 | 
						|
}
 | 
						|
 | 
						|
function AnimationAddCanvas(timing, element, range, attr, inParams) {
 | 
						|
    var animation = AnimationAddCommon(timing, range, attr, inParams);
 | 
						|
    animation.element = canvasData[element];
 | 
						|
    assert(animation.element);
 | 
						|
    animation.firstElement = null;
 | 
						|
    return animation;
 | 
						|
}
 | 
						|
 | 
						|
function AnimationAdd(timing, e, range, attr, funct, inParams) {
 | 
						|
    if (!range) {
 | 
						|
        range = [0, 1];
 | 
						|
    }
 | 
						|
    if (!attr) {
 | 
						|
        attr = 'opacity';
 | 
						|
    }
 | 
						|
    if (!funct) {
 | 
						|
        funct = interpolate_at;
 | 
						|
    }
 | 
						|
    var element;
 | 
						|
    switch (animationState.displayEngine) {
 | 
						|
        case 'SVG':
 | 
						|
            element = typeof e == 'string' ? document.getElementById(e) : e;
 | 
						|
            break;
 | 
						|
        case 'Canvas':
 | 
						|
            element = typeof e == 'string' ? e : e.id;
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
    assert(element);
 | 
						|
    switch (attr) {
 | 
						|
        case 'path':
 | 
						|
            if (!inParams) {
 | 
						|
                inParams = PathDataArray(element);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 'opacity':
 | 
						|
            if (!inParams) {
 | 
						|
                inParams = [0, 1];
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
    var funcBody = 'var outParams = '  + funct.name + '(value, inParams);\n';
 | 
						|
    switch (animationState.displayEngine) {
 | 
						|
        case 'SVG':
 | 
						|
            switch (attr) {
 | 
						|
                case 'path':
 | 
						|
                    var verbArray = PathVerbArray(element);
 | 
						|
                    funcBody += 'return ';
 | 
						|
                    for (var index = 0; index < inParams.length; ++index) {
 | 
						|
                        funcBody += '"' + verbArray[index] + '"';
 | 
						|
                        funcBody += 'outParams[' + index + '];\n';
 | 
						|
                    }
 | 
						|
                    if (verbArray.length > inParams.length) {
 | 
						|
                        funcBody += '"' + verbArray[verbArray.length - 1] + '"';
 | 
						|
                    }
 | 
						|
                    funcBody += ';\n';
 | 
						|
                    var animation = AnimationAddSVG(timing, element, range, "d", inParams);
 | 
						|
                    animation.func = new Function('value', 'inParams', funcBody);
 | 
						|
                    break;
 | 
						|
               case 'opacity':
 | 
						|
                    if (animation.element.getAttribute("stroke-opacity")) {
 | 
						|
                        animation = AnimationAddSVG(timing, element, range, "stroke-opacity", inParams);
 | 
						|
                    }
 | 
						|
                    if (animation.element.getAttribute("fill-opacity")) {
 | 
						|
                        animation = AnimationAddSVG(timing, element, range, "fill-opacity", inParams);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    debugger;
 | 
						|
            }
 | 
						|
        case 'Canvas':
 | 
						|
            switch (attr) {
 | 
						|
                case 'path':
 | 
						|
                    var verbArray = PathVerbArray(element);
 | 
						|
                    for (var index = 0; index < inParams.length; ++index) {
 | 
						|
                        funcBody += verbArray[index];
 | 
						|
                        funcBody += 'outParams[' + index + ']';
 | 
						|
                    }
 | 
						|
                    if (verbArray.length > inParams.length) {
 | 
						|
                        funcBody += verbArray[verbArray.length - 1];
 | 
						|
                    }
 | 
						|
                    animation = AnimationAddCanvas(timing, element, range, attr, inParams);
 | 
						|
                    funcBody += animation.element.style + animation.element.draw;
 | 
						|
                    animation.func = new Function('ctx', 'value', 'inParams', funcBody);
 | 
						|
                    break;
 | 
						|
               case 'opacity':
 | 
						|
                    animation = AnimationAddCanvas(timing, element, range, attr, inParams);
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    debugger;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
    return animation;
 | 
						|
}
 | 
						|
 | 
						|
function path_data_common(element, getValues) {
 | 
						|
    var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g;
 | 
						|
    var data = [];
 | 
						|
    var match;
 | 
						|
    var path;
 | 
						|
    switch (animationState.displayEngine) {
 | 
						|
        case 'SVG': path = element.getAttribute("d"); break;
 | 
						|
        case 'Canvas': path = paths[element].funcBody; break;
 | 
						|
        default: debugger;
 | 
						|
    }
 | 
						|
    if (getValues) {
 | 
						|
        while ((match = numRegEx.exec(path))) {
 | 
						|
            data.push(Number(match[0]));
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        var sIndex = 0;
 | 
						|
        while ((match = numRegEx.exec(path))) {
 | 
						|
            if (sIndex < match.index) {
 | 
						|
                data.push(path.substring(sIndex, match.index));
 | 
						|
            }
 | 
						|
            sIndex = match.index + match[0].length;
 | 
						|
        }
 | 
						|
        if (sIndex < path.length) {
 | 
						|
            data.push(path.substring(sIndex, path.length));
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
function PathDataArray(element) {
 | 
						|
    return path_data_common(element, true);
 | 
						|
}
 | 
						|
 | 
						|
function PathVerbArray(element) {
 | 
						|
    return path_data_common(element, false);
 | 
						|
}
 | 
						|
 | 
						|
function PathSet(element, funct, value, params) {
 | 
						|
    var pathVerbs = PathVerbArray(element);
 | 
						|
    if (funct) {
 | 
						|
        params = funct(value, params);
 | 
						|
    }
 | 
						|
   var setValue = '';
 | 
						|
    for (var index = 0; index < params.length; ++index) {
 | 
						|
        setValue += pathVerbs[index];
 | 
						|
        setValue += params[index];
 | 
						|
    }
 | 
						|
    if (pathVerbs.length > params.length) {
 | 
						|
        setValue += pathVerbs[pathVerbs.length - 1];
 | 
						|
    }
 | 
						|
    switch (animationState.displayEngine) {
 | 
						|
        case 'SVG':
 | 
						|
            element.setAttribute('d', setValue);
 | 
						|
            break;
 | 
						|
        case 'Canvas':
 | 
						|
            element.func = new Function('ctx', setValue + element.style + element.draw);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function RemoveFromArray(array, element) {
 | 
						|
    for (var index in array) {
 | 
						|
        var record = array[index];
 | 
						|
        if (record.element == element) {
 | 
						|
            array.splice(index, 1);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function EndAnimationCanvas(animation, visibleFinished) {
 | 
						|
    var changeAlpha = "opacity" == animation.attr;
 | 
						|
    if (!changeAlpha || animation.range[1] > 0) {
 | 
						|
        if (changeAlpha) {
 | 
						|
            ctx.save();
 | 
						|
            ctx.globalAlpha = animation.range[1];
 | 
						|
        }
 | 
						|
        if (animation.func) {
 | 
						|
            animation.func(ctx, animation.range[animation.range.length - 1], animation.inParams);
 | 
						|
        } else {
 | 
						|
            animation.element.func(ctx);
 | 
						|
        }
 | 
						|
        if (changeAlpha) {
 | 
						|
            ctx.restore();
 | 
						|
        }
 | 
						|
//        if (visibleFinished) {
 | 
						|
//            visibleFinished.push(animation);
 | 
						|
//        }
 | 
						|
    } else {
 | 
						|
 //       if (visibleFinished) {
 | 
						|
 //           RemoveFromArray(visibleFinished, animation.element);
 | 
						|
 //       }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* start here
 | 
						|
canvas:
 | 
						|
 | 
						|
display list :
 | 
						|
    for each element (canvas)
 | 
						|
        save
 | 
						|
        set global alpha (override)
 | 
						|
        create geometry (override)
 | 
						|
        create style (override)
 | 
						|
        draw
 | 
						|
        restore
 | 
						|
        
 | 
						|
maybe each action should have an override slot
 | 
						|
animations write to the slot
 | 
						|
each element in display list then iterates overrides once the animations complete the frame
 | 
						|
 | 
						|
so, first -- 
 | 
						|
    active animations update the display list
 | 
						|
    
 | 
						|
next --
 | 
						|
    active animations install themselves in override slots
 | 
						|
    
 | 
						|
finally
 | 
						|
    display list is iterated, calling override slots
 | 
						|
 | 
						|
----------------
 | 
						|
    
 | 
						|
svg:
 | 
						|
    display list is implicit
 | 
						|
    
 | 
						|
    active animations write element attributes
 | 
						|
 */
 | 
						|
 | 
						|
function EndAnimationSVG(animation, visibleFinished) {
 | 
						|
    switch (animation.attr) {
 | 
						|
        case "opacity":
 | 
						|
            animation.element.setAttribute(animation.attribute, animation.range[1]);
 | 
						|
            if (animation.range[1] > 0) {
 | 
						|
                visibleFinished.push(animation);
 | 
						|
            } else {
 | 
						|
                RemoveFromArray(visibleFinished, animation.element);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case "path":
 | 
						|
            var attrStr = animation.func(animation.range[1], animation.inParams);
 | 
						|
            animation.element.setAttribute(animation.attribute, attrStr);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function StepAnimationCanvas(animation, value) {
 | 
						|
    var endValue = animation.range[animation.range.length - 1];
 | 
						|
    var interp = animation.range[0] + (endValue - animation.range[0]) * (1 - value);
 | 
						|
    if (animation.firstStep) {
 | 
						|
        RemoveFromArray(visibleFinished, animation.element);
 | 
						|
        animation.firstStep = false;
 | 
						|
    }
 | 
						|
    var changeAlpha = "opacity" == animation.attr;
 | 
						|
    if (changeAlpha) {
 | 
						|
        ctx.save();
 | 
						|
        ctx.globalAlpha = interp;
 | 
						|
    }
 | 
						|
    if (animation.func) {
 | 
						|
        animation.func(ctx, interp, animation.inParams);
 | 
						|
    } else {
 | 
						|
        animation.element.func(ctx);
 | 
						|
    }
 | 
						|
    if (changeAlpha) {
 | 
						|
        ctx.restore();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function StepAnimationSVG(animation, value) {
 | 
						|
    var interp = animation.range[0] + (animation.range[1] - animation.range[0]) * (1 - value);
 | 
						|
    switch (animation.attr) {
 | 
						|
        case "opacity":
 | 
						|
            animation.element.setAttribute(animation.attribute, interp);
 | 
						|
            break;
 | 
						|
        case "path":
 | 
						|
            var attrStr = animation.func(interp, animation.inParams);
 | 
						|
            animation.element.setAttribute(animation.attribute, attrStr);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
var animate_frame = 0;
 | 
						|
 | 
						|
function AnimateList(now) {
 | 
						|
    ++animate_frame;
 | 
						|
    if (animationState.paused) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    if (animationState.start == null) {
 | 
						|
        animationState.start = now - animationState.time;
 | 
						|
    }
 | 
						|
    animationState.time = now - animationState.start;
 | 
						|
    var stillPending = [];
 | 
						|
    for (var index in animationsPending) {
 | 
						|
        var animation = animationsPending[index];
 | 
						|
        var interval = animationState.time - animation.timing[0];
 | 
						|
        if (interval <= 0) {
 | 
						|
            stillPending.push(animation);
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        animationsActive.push(animation);
 | 
						|
        var inList = false;
 | 
						|
        for (var dlIndex in displayList) {
 | 
						|
            var displayable = displayList[dlIndex];
 | 
						|
            if (displayable == animation.element) {
 | 
						|
                inList = true;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (!inList) {
 | 
						|
            displayList.push(animation.element);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    animationsPending = stillPending;
 | 
						|
    var stillAnimating = [];
 | 
						|
    if ('Canvas' == animationState.displayEngine) {
 | 
						|
        ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
						|
//        for (var index in visibleFinished) {
 | 
						|
//           var animation = visibleFinished[index];
 | 
						|
//           animation.endAnimation = false;
 | 
						|
//        }
 | 
						|
    }
 | 
						|
    for (var index in animationsActive) {
 | 
						|
        var animation = animationsActive[index];
 | 
						|
        var interval = animationState.time - animation.timing[0];
 | 
						|
        animation.remaining = animation.duration > interval ? animation.duration - interval : 0;
 | 
						|
        animation.endAnimation = animation.remaining <= 0;
 | 
						|
        if (animation.endAnimation) {
 | 
						|
            switch (animationState.displayEngine) {
 | 
						|
                case 'SVG':  EndAnimationSVG(animation, visibleFinished); break;
 | 
						|
                case 'Canvas': EndAnimationCanvas(animation, visibleFinished); break;
 | 
						|
                default: debugger;
 | 
						|
            }
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        var value = animation.remaining / animation.duration;
 | 
						|
        switch (animationState.displayEngine) {
 | 
						|
            case 'SVG': StepAnimationSVG(animation, value); break;
 | 
						|
            case 'Canvas':
 | 
						|
                if (!animation.firstElement || !animation.firstElement.endAnimation) {
 | 
						|
                    StepAnimationCanvas(animation, value);
 | 
						|
                }
 | 
						|
            break;
 | 
						|
            default: debugger;
 | 
						|
        }
 | 
						|
        stillAnimating.push(animation);
 | 
						|
    }
 | 
						|
    if ('Canvas' == animationState.displayEngine) {
 | 
						|
        for (var index in visibleFinished) {
 | 
						|
            var animation = visibleFinished[index];
 | 
						|
            if (!animation.endAnimation) {
 | 
						|
                EndAnimationCanvas(animation, null);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    animationsActive = stillAnimating;
 | 
						|
    if (animationsPending.length || animationsActive.length) {
 | 
						|
        animationState.requestID = requestAnimationFrame(AnimateList);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function CancelAnimate(now) {
 | 
						|
    if (animationState.start == null) {
 | 
						|
        animationState.start = now;
 | 
						|
    }
 | 
						|
    var time = now - animationState.start;
 | 
						|
    var stillAnimating = [];
 | 
						|
    for (var index in animationsActive) {
 | 
						|
        var animation = animationsActive[index];
 | 
						|
        var remaining = animation.remaining - time;
 | 
						|
        var value = remaining / animation.duration;
 | 
						|
        switch (animationState.displayEngine) {
 | 
						|
            case 'SVG': animation.element.setAttribute(animation.attribute, value); break;
 | 
						|
            case 'Canvas': break;
 | 
						|
        }
 | 
						|
        if (remaining <= 0) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        stillAnimating.push(animation);
 | 
						|
    }
 | 
						|
    animationsActive = stillAnimating;
 | 
						|
    if (animationsActive.length) {
 | 
						|
        animationState.requestID = requestAnimationFrame(CancelAnimate);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    animationsPending = [];
 | 
						|
    animationState.reset();
 | 
						|
    if (keyFrameQueue.length > 0) {
 | 
						|
        var animationFunc = keyFrameQueue.pop();
 | 
						|
        animationFunc();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function CancelAnimation() {
 | 
						|
    cancelAnimationFrame(animationState.requestID);
 | 
						|
    for (var index in animationsActive) {
 | 
						|
        var animation = animationsActive[index];
 | 
						|
        switch (animation.attr) {
 | 
						|
            case "opacity":
 | 
						|
                var tmp = animation.range[0]; animation.range[0] = animation.range[1]; animation[1] = tmp;
 | 
						|
                animation.remaining = animation.duration - animation.remaining;
 | 
						|
                animation.remaining /= animation.duration / 1000;
 | 
						|
                animation.duration = 1000;
 | 
						|
                break;
 | 
						|
            case "fadeOut":
 | 
						|
                RemoveFromArray(visibleFinished, animation.element);
 | 
						|
                break;
 | 
						|
            case "path":
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                debugger;
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
    for (var index in visibleFinished) {
 | 
						|
        var animation = visibleFinished[index];
 | 
						|
        animation.action = "fadeOut";
 | 
						|
        animation.remaining = animation.duration = 1000;
 | 
						|
        animationsActive.push(animation);
 | 
						|
    }
 | 
						|
    visibleFinished = [];
 | 
						|
    animationState.reset();
 | 
						|
    animationState.requestID = requestAnimationFrame(CancelAnimate);
 | 
						|
}
 | 
						|
 | 
						|
function PauseAnimation() {
 | 
						|
    animationState.paused = true;
 | 
						|
}
 | 
						|
 | 
						|
function QueueAnimation(animationFunc) {
 | 
						|
    if (null == animationState.requestID) {
 | 
						|
        animationFunc();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    keyFrameQueue.push(animationFunc);
 | 
						|
}
 | 
						|
 | 
						|
function UnpauseAnimation() {
 | 
						|
    animationState.paused = false;
 | 
						|
    animationState.start = performance.now() - animationState.time;
 | 
						|
    animationState.requestID = requestAnimationFrame(AnimateList);
 | 
						|
}
 | 
						|
 | 
						|
function SetupTextSVG(t, x, y) {
 | 
						|
    var text;
 | 
						|
    if (typeof t == "string") {
 | 
						|
        text = document.getElementById(t);
 | 
						|
    } else {
 | 
						|
        text = t;
 | 
						|
    }
 | 
						|
    text.setAttribute("font-family", "Helvetica,Arial");
 | 
						|
    text.setAttribute("font-size", "1.3rem");
 | 
						|
    if (typeof x == 'number') {
 | 
						|
        text.setAttribute("x", x);
 | 
						|
    } else if (null == text.getAttribute("x")) {
 | 
						|
        text.setAttribute("x", 400);
 | 
						|
    }
 | 
						|
    if (typeof y == 'number') {
 | 
						|
        text.setAttribute("y", y);
 | 
						|
    } else if (null == text.getAttribute("y")) {
 | 
						|
        text.setAttribute("y", 200);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function SetupTextCanvas(t, x, y) {
 | 
						|
    var text = typeof t == 'string' ? t : t.id;
 | 
						|
    var record = canvasData[text];
 | 
						|
    if (typeof x == 'number') {
 | 
						|
        record.x = x;
 | 
						|
    }
 | 
						|
    if (typeof y == 'number') {
 | 
						|
        record.y = y;
 | 
						|
    }
 | 
						|
    record.position = canvas_xy(record);
 | 
						|
    record.func = new Function('ctx', record.style + record.draw + record.position);
 | 
						|
}
 | 
						|
 | 
						|
function SetupText(t, x, y) {
 | 
						|
    switch (animationState.displayEngine) {
 | 
						|
        case 'SVG':
 | 
						|
            SetupTextSVG(t, x, y);
 | 
						|
            break;
 | 
						|
        case 'Canvas':
 | 
						|
            SetupTextCanvas(t, x, y);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            debugger;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function FirstText(text) {
 | 
						|
    SetupText(text);
 | 
						|
    AnimationAdd([0, 1000], text);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
function EngineInit(keyframe) {
 | 
						|
    displayList = [];
 | 
						|
    switch (animationState.displayEngine) {
 | 
						|
        case 'SVG': break;
 | 
						|
        case 'Canvas': CanvasInit(keyframe); break;
 | 
						|
        default: debugger;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function EngineStart() {
 | 
						|
    switch (animationState.displayEngine) {
 | 
						|
        case 'SVG': break;
 | 
						|
        case 'Canvas':
 | 
						|
            // associate fadeIn and fadeOut
 | 
						|
            for (var outerIndex in animationsPending) {
 | 
						|
                var outer = animationsPending[outerIndex];
 | 
						|
                for (var innerIndex in animationsPending) {
 | 
						|
                    if (outerIndex == innerIndex) {
 | 
						|
                        continue;
 | 
						|
                    }
 | 
						|
                    var inner = animationsPending[innerIndex];
 | 
						|
                    if (inner.element == outer.element) {
 | 
						|
                        inner.firstElement = outer;
 | 
						|
                        continue;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        default: debugger;
 | 
						|
    }
 | 
						|
    animationState.reset();
 | 
						|
    animationState.requestID = requestAnimationFrame(AnimateList);
 | 
						|
}
 | 
						|
 | 
						|
function AnimateSpanWedge() {
 | 
						|
    EngineInit('keyframe1');
 | 
						|
    FirstText(spanWedgeDesc);
 | 
						|
    AnimationAdd([1000, 2000], span1);
 | 
						|
    AnimationAdd([1500, 3000], wedge1);
 | 
						|
    AnimationAdd([3500, 4000], span1,  [1, 0]);
 | 
						|
    AnimationAdd([3500, 4000], wedge1, [1, 0]);
 | 
						|
    AnimationAdd([4000, 5000], span2);
 | 
						|
    AnimationAdd([4500, 6000], wedge2);
 | 
						|
    AnimationAdd([6500, 7000], span2,  [1, 0]);
 | 
						|
    AnimationAdd([6500, 7000], wedge2, [1, 0]);
 | 
						|
    AnimationAdd([7000, 8000], span3);
 | 
						|
    AnimationAdd([7500, 9000], wedge3);
 | 
						|
    EngineStart();
 | 
						|
}
 | 
						|
 | 
						|
function AnimateTrivialWedge() {
 | 
						|
    EngineInit('keyframe2');
 | 
						|
    FirstText(trivialWedgeDesc1);
 | 
						|
    FirstText(trivialWedgeDesc2);
 | 
						|
    AnimationAdd([2000, 3500], span4);
 | 
						|
    AnimationAdd([2000, 3500], wedge4);
 | 
						|
    AnimationAdd([2000, 3500], span5);
 | 
						|
    AnimationAdd([2000, 3500], wedge5);
 | 
						|
    AnimationAdd([2000, 3500], span6);
 | 
						|
    AnimationAdd([2000, 3500], wedge6);
 | 
						|
    EngineStart();
 | 
						|
}
 | 
						|
 | 
						|
function AnimateSectorDesc() {
 | 
						|
    EngineInit('keyframe3');
 | 
						|
    FirstText(sectorDesc1);
 | 
						|
    FirstText(sectorDesc2);
 | 
						|
    AnimationAdd([   0, 1000], xaxis);
 | 
						|
    AnimationAdd([ 500, 1500], yaxis);
 | 
						|
    AnimationAdd([2000, 3500], sectorDescXYA);
 | 
						|
    AnimationAdd([2000, 3500], wedgeXY8);
 | 
						|
    AnimationAdd([3000, 4500], sectorDescXYB);
 | 
						|
    AnimationAdd([3000, 4500], wedgeXY6);
 | 
						|
    AnimationAdd([4000, 5500], sectorDescXYC);
 | 
						|
    AnimationAdd([4000, 5500], wedgeXY3);
 | 
						|
    EngineStart();
 | 
						|
}
 | 
						|
 | 
						|
function AnimateLineSingle() {
 | 
						|
    EngineInit('keyframe4');
 | 
						|
    FirstText(lineSingleDesc);
 | 
						|
    for (var i = 1; i <= 8; ++i) {
 | 
						|
        SetupText("sectorDescXY" + i, 500, 260);
 | 
						|
    }
 | 
						|
    AnimationAdd([   0, 1000], xaxis);
 | 
						|
    AnimationAdd([   0, 1000], yaxis);
 | 
						|
    AnimationAdd([1000, 2000], lineSegment);
 | 
						|
    AnimationAdd([1000, 3000], lineSegment, [-22.5 * Math.PI / 180], "path", evaluate_at,
 | 
						|
            [ circle.center.x, circle.center.y,
 | 
						|
              circle.center.x + " + " + circle.radius + " * Math.cos(value)",
 | 
						|
              circle.center.y + " + " + circle.radius + " * Math.sin(value)",
 | 
						|
            ]);
 | 
						|
    AnimationAdd([2000, 3000], sectorDescXY1);
 | 
						|
    AnimationAdd([2000, 3000], wedgeXY1);
 | 
						|
    AnimationAdd([3000, 7000], lineSegment, [-22.5 * Math.PI / 180, (-22.5 - 360) * Math.PI / 180],
 | 
						|
            "path", evaluate_at,
 | 
						|
            [ circle.center.x, circle.center.y,
 | 
						|
              circle.center.x + " + " + circle.radius + " * Math.cos(value)",
 | 
						|
              circle.center.y + " + " + circle.radius + " * Math.sin(value)",
 | 
						|
            ]);
 | 
						|
    for (var i = 1; i < 8; ++i) {
 | 
						|
        AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + (i + 1));
 | 
						|
        AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + (i + 1));
 | 
						|
        AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + i,       [1, 0]);
 | 
						|
        AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + i,            [1, 0]);
 | 
						|
    }
 | 
						|
    AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY1);
 | 
						|
    AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY1);
 | 
						|
    AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY8, [1, 0]);
 | 
						|
    AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY8,      [1, 0]);
 | 
						|
    EngineStart();
 | 
						|
}
 | 
						|
 | 
						|
function AnimateCurveMultiple() {
 | 
						|
    EngineInit('keyframe5');
 | 
						|
    var cubicStart = PathDataArray(curveSegment1);
 | 
						|
    var cubicMid = PathDataArray(curveSegment2);
 | 
						|
    var cubicEnd = PathDataArray(curveSegment3);
 | 
						|
    FirstText(curveMultipleDesc1);
 | 
						|
    FirstText(curveMultipleDesc2);
 | 
						|
    for (var i = 1; i <= 6; ++i) {
 | 
						|
        SetupText("sectorDescXY" + i, 500, 260 + i * 25);
 | 
						|
    }
 | 
						|
    AnimationAdd([   0, 1000], xaxis);
 | 
						|
    AnimationAdd([   0, 1000], yaxis);
 | 
						|
    AnimationAdd([1000, 2000], curveSegment);
 | 
						|
    AnimationAdd([2000, 3000], sectorDescXY1);
 | 
						|
    AnimationAdd([2000, 3000], wedgeXY1);
 | 
						|
    AnimationAdd([3000, 4000], curveSegment, [0, 1], "path", interpolate_at, [cubicStart, cubicMid]);
 | 
						|
    AnimationAdd([4000, 5000], sectorDescXY2);
 | 
						|
    AnimationAdd([4000, 5000], wedgeXY2);
 | 
						|
    AnimationAdd([5000, 6000], curveSegment, [0, 1], "path", interpolate_at, [cubicMid, cubicEnd]);
 | 
						|
    AnimationAdd([6000, 7000], sectorDescXY3);
 | 
						|
    AnimationAdd([6000, 7000], wedgeXY3);
 | 
						|
    AnimationAdd([6000, 7000], sectorDescXY4);
 | 
						|
    AnimationAdd([6000, 7000], wedgeXY4);
 | 
						|
    AnimationAdd([6000, 7000], sectorDescXY5);
 | 
						|
    AnimationAdd([6000, 7000], wedgeXY5);
 | 
						|
    AnimationAdd([6000, 7000], sectorDescXY6);
 | 
						|
    AnimationAdd([6000, 7000], wedgeXY6);
 | 
						|
    EngineStart();
 | 
						|
}
 | 
						|
 | 
						|
function AnimateOneDLines() {
 | 
						|
    EngineInit('keyframe6');
 | 
						|
    FirstText(line1DDest1);
 | 
						|
    FirstText(line1DDest2);
 | 
						|
    for (var i = 9; i <= 11; ++i) {
 | 
						|
        SetupText("sectorDescXY" + i, 500, 260 + (i - 8) * 25);
 | 
						|
    }
 | 
						|
    AnimationAdd([   0, 1000], xaxis);
 | 
						|
    AnimationAdd([   0, 1000], yaxis);
 | 
						|
    AnimationAdd([2000, 3000], sectorDescXY9);
 | 
						|
    AnimationAdd([2000, 3000], horzSegment);
 | 
						|
    AnimationAdd([3000, 4000], sectorDescXY10);
 | 
						|
    AnimationAdd([3000, 4000], vertSegment);
 | 
						|
    AnimationAdd([4000, 5000], sectorDescXY11);
 | 
						|
    AnimationAdd([4000, 5000], diagSegment);
 | 
						|
    EngineStart();
 | 
						|
}
 | 
						|
 | 
						|
function AnimateDiverging() {
 | 
						|
    EngineInit('keyframe7');
 | 
						|
    var cubicData = PathDataArray(cubicSegment2);
 | 
						|
    FirstText(curve1dDesc1);
 | 
						|
    FirstText(curve1dDesc2);
 | 
						|
    SetupText("sectorDescXY9", 500, 285);
 | 
						|
    SetupText("sectorDescXY1", 500, 320);
 | 
						|
    AnimationAdd([   0, 1000], xaxis);
 | 
						|
    AnimationAdd([   0, 1000], yaxis);
 | 
						|
    AnimationAdd([1900, 1900], cubicSegment);
 | 
						|
    AnimationAdd([2000, 3000], cubicSegment, [0, 1], "path", cubic_partial, cubicData);
 | 
						|
    AnimationAdd([2000, 3000], sectorDescXY9);
 | 
						|
    AnimationAdd([2000, 3000], horzSegment);
 | 
						|
    AnimationAdd([3000, 4000], sectorDescXY1);
 | 
						|
    AnimationAdd([3000, 4000], wedgeXY1);
 | 
						|
    EngineStart();
 | 
						|
}
 | 
						|
 | 
						|
circle.animate = AnimateCircle;
 | 
						|
circle.start = null;
 | 
						|
 | 
						|
function AngleToPt(center, radius, degrees) {
 | 
						|
    var radians = degrees * Math.PI / 180.0;
 | 
						|
    return {
 | 
						|
        x: center.x + (radius * Math.cos(radians)),
 | 
						|
        y: center.y - (radius * Math.sin(radians))
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
function PtsToSweep(pt1, pt2, center) {  // unused
 | 
						|
    return {
 | 
						|
     start: 180 / Math.PI * Math.atan2(pt1.y - center.y, pt1.x - center.x),
 | 
						|
     end:   180 / Math.PI * Math.atan2(pt2.y - center.y, pt2.x - center.x)
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
function ArcStr(center, radius, startAngle, endAngle) {
 | 
						|
    var endPt = AngleToPt(center, radius, endAngle);
 | 
						|
    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";
 | 
						|
    return ["A", radius, radius, 0, arcSweep, 0, endPt.x, endPt.y].join(" ");
 | 
						|
}
 | 
						|
 | 
						|
function ArcStart(center, radius, startAngle, endAngle) {
 | 
						|
    var startPt = AngleToPt(center, radius, startAngle);
 | 
						|
    return [ startPt.x, startPt.y, ArcStr(center, radius, startAngle, endAngle) ].join(" ");
 | 
						|
}
 | 
						|
 | 
						|
function MakeArc(arcStart) {
 | 
						|
    return "M" + arcStart;
 | 
						|
}
 | 
						|
 | 
						|
function MakeWedge(center, arcStart) {
 | 
						|
    return ["M", center.x, center.y, "L", arcStart, "z"].join(" ");
 | 
						|
}
 | 
						|
 | 
						|
function Animate(path, now, dur) {
 | 
						|
    if (path.start == null) {
 | 
						|
        path.start = now;
 | 
						|
//        console.log("start=" + now);
 | 
						|
    }
 | 
						|
    if (now - path.start < dur) {
 | 
						|
        requestAnimationFrame(path.animate);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
function AnimateCircle(now) {
 | 
						|
    if (circle.start == null) {
 | 
						|
        circleFill.setAttribute("fill-opacity", "0.3");
 | 
						|
    }
 | 
						|
    var dur = 2 * 1000;
 | 
						|
    var animating = Animate(circle, now, dur);
 | 
						|
//    console.log("now=" + now + "circle.start=" + circle.start )
 | 
						|
    var pathStr = ArcStart(circle.center, circle.radius, 0, (now - circle.start) / (dur / 359.9));
 | 
						|
 | 
						|
    circle.setAttribute("d", MakeArc(pathStr));
 | 
						|
    circleFill.setAttribute("d", MakeWedge(circle.center, pathStr));
 | 
						|
    if (!animating) {
 | 
						|
        var delay = dur - (now - circle.start);
 | 
						|
        setTimeout(CircleFinal, delay);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function CircleFinal() {
 | 
						|
    var firstHalf = ArcStart(circle.center, circle.radius, 0, 180);
 | 
						|
    var secondHalf = ArcStr(circle.center, circle.radius, 180, 360);
 | 
						|
    circle.setAttribute("d", "M" + firstHalf + secondHalf + "z");
 | 
						|
    circleFill.setAttribute("d", "M" + firstHalf + secondHalf + "z");
 | 
						|
}
 | 
						|
 | 
						|
var svgNS = "http://www.w3.org/2000/svg";
 | 
						|
 | 
						|
function CreateTextLabels()
 | 
						|
{
 | 
						|
    for (var i = 0; i < 32; ++i) {
 | 
						|
        var text = document.createElementNS(svgNS, "text");
 | 
						|
        var pt = AngleToPt(circle.center, circle.radius + 80, i * 360 / 32);
 | 
						|
        text.setAttribute("id", "t" + i);
 | 
						|
        text.setAttribute("x", pt.x);
 | 
						|
        text.setAttribute("y", pt.y);
 | 
						|
        text.setAttribute("text-anchor", "middle");
 | 
						|
        text.setAttribute("alignment-baseline", "mathematical");
 | 
						|
        var textNode = document.createTextNode(i);
 | 
						|
        text.appendChild(textNode);
 | 
						|
        document.getElementById("svg").appendChild(text);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// CreateTextLabels();
 | 
						|
 | 
						|
var keyframeArray = [
 | 
						|
    AnimateSpanWedge,
 | 
						|
    AnimateTrivialWedge,
 | 
						|
    AnimateSectorDesc,
 | 
						|
    AnimateLineSingle,
 | 
						|
    AnimateCurveMultiple,
 | 
						|
    AnimateOneDLines,
 | 
						|
    AnimateDiverging,
 | 
						|
];
 | 
						|
 | 
						|
var keyframeIndex = 3; // keyframeArray.length - 1;  // normally 0 ; set to debug a particular frame
 | 
						|
 | 
						|
function QueueKeyframe() {
 | 
						|
    QueueAnimation(keyframeArray[keyframeIndex]);
 | 
						|
    if (keyframeIndex < keyframeArray.length - 1) {
 | 
						|
        ++keyframeIndex;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
var grads;
 | 
						|
var paths;
 | 
						|
var canvas;
 | 
						|
var ctx;
 | 
						|
 | 
						|
function canvasSetup() {
 | 
						|
    canvas = document.getElementById("canvas");
 | 
						|
    ctx = canvas ? canvas.getContext("2d") : null;
 | 
						|
    assert(ctx);
 | 
						|
    var resScale = animationState.resScale = window.devicePixelRatio ? window.devicePixelRatio : 1;
 | 
						|
    var unscaledWidth = canvas.width;
 | 
						|
    var unscaledHeight = canvas.height;
 | 
						|
    canvas.width = unscaledWidth * resScale;
 | 
						|
    canvas.height = unscaledHeight * resScale;
 | 
						|
    canvas.style.width = unscaledWidth + 'px';
 | 
						|
    canvas.style.height = unscaledHeight + 'px';
 | 
						|
    if (resScale != 1) {
 | 
						|
        ctx.scale(resScale, resScale);
 | 
						|
    }
 | 
						|
 | 
						|
    grads = CanvasGrads(ctx);
 | 
						|
    paths = CanvasPaths(ctx);
 | 
						|
}
 | 
						|
 | 
						|
function Onload() {
 | 
						|
    canvasSetup();
 | 
						|
    var startBtn = document.getElementById('startBtn');
 | 
						|
    var stopBtn = document.getElementById('stopBtn');
 | 
						|
    var resetBtn = document.getElementById('resetBtn');
 | 
						|
 | 
						|
    startBtn.addEventListener('click', function(e) {
 | 
						|
        e.preventDefault();
 | 
						|
        e.srcElement.innerText = "Next";
 | 
						|
        CancelAnimation();
 | 
						|
        QueueKeyframe();
 | 
						|
    });
 | 
						|
 | 
						|
    stopBtn.addEventListener('click', function(e) {
 | 
						|
      e.preventDefault();
 | 
						|
 | 
						|
      if (!animationState.paused) {
 | 
						|
        PauseAnimation();
 | 
						|
        e.srcElement.innerText = "Resume";
 | 
						|
      } else {
 | 
						|
        UnpauseAnimation();
 | 
						|
        e.srcElement.innerText = "Pause";
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    resetBtn.addEventListener('click', function(e) {
 | 
						|
        e.preventDefault();
 | 
						|
        CancelAnimation();
 | 
						|
        keyframeIndex = 0;
 | 
						|
        startBtn.innerText = "Start";
 | 
						|
        QueueKeyframe();
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
</script>
 | 
						|
 | 
						|
</head>
 | 
						|
 | 
						|
<body onLoad="Onload()">
 | 
						|
 | 
						|
<div class="controls">
 | 
						|
      <button type="button" id="startBtn">Start</button>
 | 
						|
      <button type="button" id="stopBtn">Pause</button>
 | 
						|
      <button type="button" id="resetBtn">Restart</button>
 | 
						|
</div>
 | 
						|
 | 
						|
<canvas id="canvas" width="800" height="500" />
 | 
						|
 | 
						|
</body>
 | 
						|
</html> |