caryclark | 3257c12 | 2015-11-16 13:36:08 -0800 | [diff] [blame] | 1 | <!DOCTYPE html> |
| 2 | <html lang="en"> |
| 3 | <head> |
| 4 | |
| 5 | |
| 6 | <style> |
| 7 | html { |
| 8 | font-family: Helvetica, Arial, sans-serif; |
| 9 | font-size: 100%; |
| 10 | } |
| 11 | |
| 12 | .controls { |
| 13 | margin: 1em 0; |
| 14 | } |
| 15 | |
| 16 | button { |
| 17 | display: inline-block; |
| 18 | border-radius: 3px; |
| 19 | border: none; |
| 20 | font-size: 0.9rem; |
| 21 | padding: 0.4rem 0.8em; |
| 22 | background: #69c773; |
| 23 | border-bottom: 1px solid #498b50; |
| 24 | color: white; |
| 25 | -webkit-font-smoothing: antialiased; |
| 26 | font-weight: bold; |
| 27 | margin: 0 0.25rem; |
| 28 | text-align: center; |
| 29 | } |
| 30 | |
| 31 | button:hover, button:focus { |
| 32 | opacity: 0.75; |
| 33 | cursor: pointer; |
| 34 | } |
| 35 | |
| 36 | button:active { |
| 37 | opacity: 1; |
| 38 | box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.1) inset; |
| 39 | } |
| 40 | |
| 41 | </style> |
| 42 | |
| 43 | <! set height back to 500 /> |
| 44 | <svg id="svg" width="800" height="500" |
| 45 | xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
| 46 | |
| 47 | <defs> |
| 48 | <radialGradient id="grad1" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 49 | <stop offset="0%" style="stop-color:rgb(0,0,255); stop-opacity:0.3" /> |
| 50 | <stop offset="100%" style="stop-color:rgb(0,0,255); stop-opacity:0" /> |
| 51 | </radialGradient> |
| 52 | <radialGradient id="grad2" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 53 | <stop offset="0%" style="stop-color:rgb(0,255,0); stop-opacity:0.3" /> |
| 54 | <stop offset="100%" style="stop-color:rgb(0,255,0); stop-opacity:0" /> |
| 55 | </radialGradient> |
| 56 | <radialGradient id="grad3" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 57 | <stop offset="0%" style="stop-color:rgb(255,0,0); stop-opacity:0.3" /> |
| 58 | <stop offset="100%" style="stop-color:rgb(255,0,0); stop-opacity:0" /> |
| 59 | </radialGradient> |
| 60 | <radialGradient id="grad4" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 61 | <stop offset="0%" style="stop-color:rgb(192,63,192); stop-opacity:0.3" /> |
| 62 | <stop offset="100%" style="stop-color:rgb(192,63,192); stop-opacity:0" /> |
| 63 | </radialGradient> |
| 64 | <radialGradient id="grad5" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 65 | <stop offset="0%" style="stop-color:rgb(127,127,0); stop-opacity:0.3" /> |
| 66 | <stop offset="100%" style="stop-color:rgb(127,127,0); stop-opacity:0" /> |
| 67 | </radialGradient> |
| 68 | <radialGradient id="grad6" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 69 | <stop offset="0%" style="stop-color:rgb(127,0,127); stop-opacity:0.3" /> |
| 70 | <stop offset="100%" style="stop-color:rgb(127,0,127); stop-opacity:0" /> |
| 71 | </radialGradient> |
| 72 | <radialGradient id="grad7" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 73 | <stop offset="0%" style="stop-color:rgb(0,127,127); stop-opacity:0.3" /> |
| 74 | <stop offset="100%" style="stop-color:rgb(0,127,127); stop-opacity:0" /> |
| 75 | </radialGradient> |
| 76 | <radialGradient id="grad8" cx="200" cy="200" r="300" gradientUnits="userSpaceOnUse"> |
| 77 | <stop offset="0%" style="stop-color:rgb(63,192,63); stop-opacity:0.3" /> |
| 78 | <stop offset="100%" style="stop-color:rgb(63,192,63); stop-opacity:0" /> |
| 79 | </radialGradient> |
| 80 | </defs> |
| 81 | |
| 82 | <path id="circleFill" d="M300,200 A 100,100 0,0,0 300,200" fill="#777" fill-opacity="0" /> |
| 83 | <path id="circle" d="M300,200 A 100,100 0,0,0 300,200" fill="none" stroke="black" /> |
| 84 | |
| 85 | <! elements for keyframe 1 /> |
| 86 | <text id="spanWedgeDesc" fill-opacity="0" > |
| 87 | All spans are contained by a wedge. |
| 88 | </text> |
| 89 | <path id="span1" d="M200,200 Q300,300 200,300" fill="none" stroke="black" stroke-opacity="0"/> |
| 90 | <path id="span2" d="M200,200 C100,300 100,400 200,300" fill="none" stroke="black" stroke-opacity="0"/> |
| 91 | <path id="span3" d="M200,200 C300,100 100,400 300,200" fill="none" stroke="black" stroke-opacity="0"/> |
| 92 | <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"/> |
| 93 | <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"/> |
| 94 | <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"/> |
| 95 | |
| 96 | <! keyframe 2 /> |
| 97 | <text id="trivialWedgeDesc1" fill-opacity="0" > |
| 98 | Wedges that don't overlap can be |
| 99 | </text> |
| 100 | <text id="trivialWedgeDesc2" y="240" fill-opacity="0" > |
| 101 | easily sorted. |
| 102 | </text> |
| 103 | <path id="span4" d="M200,200 Q300,300 400,300" fill="none" stroke="black" stroke-opacity="0"/> |
| 104 | <path id="span5" d="M200,200 Q280,320 200,400" fill="none" stroke="black" stroke-opacity="0"/> |
| 105 | <path id="span6" d="M200,200 Q60,340 100,400" fill="none" stroke="black" stroke-opacity="0"/> |
| 106 | <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"/> |
| 107 | <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"/> |
| 108 | <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"/> |
| 109 | |
| 110 | |
| 111 | <! keyframe 3 /> |
| 112 | <text id="sectorDesc1" fill-opacity="0" > |
| 113 | A sector is a wedge of a circle |
| 114 | </text> |
| 115 | <text id="sectorDesc2" y="240" fill-opacity="0" > |
| 116 | containing a range of points. |
| 117 | </text> |
| 118 | <g id="xaxis" stroke-opacity="0" fill-opacity="0"> |
| 119 | <path d="M100,200 L300,200" fill="none" stroke="rgb(191,191,191)"/> |
| 120 | <text x="100" y="220" fill="rgb(191,191,191)">-X</text> |
| 121 | <text x="300" y="220" text-anchor="end" fill="rgb(191,191,191)">+X</text> |
| 122 | </g> |
| 123 | <g id="yaxis" stroke-opacity="0" fill-opacity="0"> |
| 124 | <path d="M200,100 L200,300" fill="none" stroke="rgb(191,191,191)"/> |
| 125 | <text x="205" y="100" alignment-baseline="hanging" fill="rgb(191,191,191)">-Y</text> |
| 126 | <text x="205" y="300" fill="rgb(191,191,191)">+Y</text> |
| 127 | </g> |
| 128 | <text id="sectorDescXYA" x="500" y="310" fill="rgb(0,0,255)" fill-opacity="0"> |
| 129 | X > 0> Y > 0 Y < X</text> |
| 130 | <text id="sectorDescXYB" x="500" y="360" fill="rgb(0,127,0)" fill-opacity="0"> |
| 131 | X < 0 Y > 0 -Y < X</text> |
| 132 | <text id="sectorDescXYC" x="500" y="410" fill="rgb(255,0,0)" fill-opacity="0"> |
| 133 | X < 0 Y < 0 Y < X</text> |
| 134 | <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"/> |
| 135 | <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"/> |
| 136 | <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"/> |
| 137 | |
| 138 | <! keyframe 4 /> |
| 139 | <text id="lineSingleDesc" fill-opacity="0" > |
| 140 | Line spans are contained by a single sector. |
| 141 | </text> |
| 142 | <text id="sectorDescXY1" x="500" y="460" fill="rgb(192,63,192)" fill-opacity="0"> |
| 143 | X > 0 Y < 0 -Y < X</text> |
| 144 | <text id="sectorDescXY2" x="500" y="460" fill="rgb(127,127,0)" fill-opacity="0"> |
| 145 | X > 0 Y < 0 -Y > X</text> |
| 146 | <text id="sectorDescXY3" x="500" y="460" fill="rgb(255,0,0)" fill-opacity="0"> |
| 147 | X < 0 Y < 0 Y < X</text> |
| 148 | <text id="sectorDescXY4" x="500" y="460" fill="rgb(127,0,127)" fill-opacity="0"> |
| 149 | X < 0 Y < 0 Y > X</text> |
| 150 | <text id="sectorDescXY5" x="500" y="460" fill="rgb(0,127,127)" fill-opacity="0"> |
| 151 | X < 0 Y > 0 -Y < X</text> |
| 152 | <text id="sectorDescXY6" x="500" y="460" fill="rgb(0,127,0)" fill-opacity="0"> |
| 153 | X < 0 Y > 0 -Y < X</text> |
| 154 | <text id="sectorDescXY7" x="500" y="460" fill="rgb(63,192,63)" fill-opacity="0"> |
| 155 | X > 0 Y > 0 Y > X</text> |
| 156 | <text id="sectorDescXY8" x="500" y="460" fill="rgb(0,0,255)" fill-opacity="0"> |
| 157 | X > 0 Y > 0 Y < X</text> |
| 158 | <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"/> |
| 159 | <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"/> |
| 160 | <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"/> |
| 161 | <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"/> |
| 162 | <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"/> |
| 163 | <path id="lineSegment" d="M200,200 L200,624.26" fill="none" stroke="black" stroke-opacity="0"/> |
| 164 | |
| 165 | <! keyframe 5 /> |
| 166 | <text id="curveMultipleDesc1" fill-opacity="0" > |
| 167 | A curve span may cover more |
| 168 | </text> |
| 169 | <text id="curveMultipleDesc2" y="240" fill-opacity="0" > |
| 170 | than one sector. |
| 171 | </text> |
| 172 | <path id="curveSegment" d="M200,200 C250,200 300,150 300,100" fill="none" stroke="black" stroke-opacity="0"/> |
| 173 | <path id="curveSegment1" d="M200,200 C250,200 300,150 300,100" fill="none"/> |
| 174 | <path id="curveSegment2" d="M200,200 C250,200 300,150 200,100" fill="none"/> |
| 175 | <path id="curveSegment3" d="M200,200 C350,200 250,-150 170,300" fill="none"/> |
| 176 | |
| 177 | <! keyframe 6 /> |
| 178 | <text id="line1DDest1" fill-opacity="0" > |
| 179 | Some lines occupy one-dimensional |
| 180 | </text> |
| 181 | <text id="line1DDest2" y="240" fill-opacity="0" > |
| 182 | sectors. |
| 183 | </text> |
| 184 | <text id="sectorDescXY9" x="500" y="460" fill="rgb(192,92,31)" fill-opacity="0"> |
| 185 | X > 0 Y == 0</text> |
| 186 | <text id="sectorDescXY10" x="500" y="460" fill="rgb(31,92,192)" fill-opacity="0"> |
| 187 | Y > 0 0 == X</text> |
| 188 | <text id="sectorDescXY11" x="500" y="460" fill="rgb(127,63,127)" fill-opacity="0"> |
| 189 | X < 0 Y == X</text> |
| 190 | <path id="horzSegment" d="M200,200 L341.4,200" fill="none" stroke="rgb(192,92,31)" stroke-width="2" stroke-opacity="0"/> |
| 191 | <path id="vertSegment" d="M200,200 L200,341.4" fill="none" stroke="rgb(31,92,192)" stroke-width="2" stroke-opacity="0"/> |
| 192 | <path id="diagSegment" d="M200,200 L100,100" fill="none" stroke="rgb(127,63,127)" stroke-width="2" stroke-opacity="0"/> |
| 193 | |
| 194 | <! keyframe 7 /> |
| 195 | <text id="curve1dDesc1" fill-opacity="0" > |
| 196 | Some curves initially occupy |
| 197 | </text> |
| 198 | <text id="curve1dDesc2" y="240" fill-opacity="0" > |
| 199 | one-dimensional sectors, then diverge. |
| 200 | </text> |
| 201 | <path id="cubicSegment" fill="none" stroke="black" /> |
| 202 | <path id="cubicSegment1" d="M200,200 C200,200 200,200 200,200" fill="none" /> |
| 203 | <path id="cubicSegment2" d="M200,200 C250,200 300,200 300,100" fill="none"/> |
| 204 | |
| 205 | <text id="sectorNumberDesc" fill-opacity="0" > |
| 206 | Each sector is assigned a number. |
| 207 | </text> |
| 208 | <text id="spanSectorDesc" fill-opacity="0" > |
| 209 | Each span has a bit set for one or more sectors. |
| 210 | </text> |
| 211 | <text id="bitOverDesc" fill-opacity="0" > |
| 212 | Span sets allow rough sorting without angle computation. |
| 213 | </text> |
| 214 | |
| 215 | </svg> |
| 216 | |
| 217 | <! canvas support /> |
| 218 | <script> |
| 219 | |
| 220 | var keyFrameQueue = []; |
| 221 | var animationsPending = []; |
| 222 | var animationsActive = []; |
| 223 | var displayList = []; |
| 224 | var visibleFinished = []; |
| 225 | |
| 226 | var animationState = {}; |
| 227 | animationState.reset = function () { |
| 228 | this.start = null; |
| 229 | this.time = 0; |
| 230 | this.requestID = null; |
| 231 | this.paused = false; |
| 232 | this.displayEngine = 'Canvas'; |
| 233 | } |
| 234 | |
| 235 | circle.center = { x: 200, y: 200 } |
| 236 | circle.radius = 100; |
| 237 | |
| 238 | function assert(condition) { |
| 239 | if (!condition) debugger; |
| 240 | } |
| 241 | |
| 242 | function CanvasGrads(ctx) { |
| 243 | var grad1 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 244 | grad1.addColorStop(0, "rgba(0,0,255, 0.3)"); |
| 245 | grad1.addColorStop(1, "rgba(0,0,255, 0)"); |
| 246 | var grad2 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 247 | grad2.addColorStop(0, "rgba(0,255,0, 0.3)"); |
| 248 | grad2.addColorStop(1, "rgba(0,255,0, 0)"); |
| 249 | var grad3 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 250 | grad3.addColorStop(0, "rgba(255,0,0, 0.3)"); |
| 251 | grad3.addColorStop(1, "rgba(255,0,0, 0)"); |
| 252 | var grad4 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 253 | grad4.addColorStop(0, "rgba(192,63,192, 0.3)"); |
| 254 | grad4.addColorStop(1, "rgba(192,63,192, 0)"); |
| 255 | var grad5 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 256 | grad5.addColorStop(0, "rgba(127,127,0, 0.3)"); |
| 257 | grad5.addColorStop(1, "rgba(127,127,0, 0)"); |
| 258 | var grad6 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 259 | grad6.addColorStop(0, "rgba(127,0,127, 0.3)"); |
| 260 | grad6.addColorStop(1, "rgba(127,0,127, 0)"); |
| 261 | var grad7 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 262 | grad7.addColorStop(0, "rgba(0,127,127, 0.3)"); |
| 263 | grad7.addColorStop(1, "rgba(0,127,127, 0)"); |
| 264 | var grad8 = ctx.createRadialGradient(200, 200, 0, 200, 200, 300); |
| 265 | grad8.addColorStop(0, "rgba(63,192,63, 0.3)"); |
| 266 | grad8.addColorStop(1, "rgba(63,192,63, 0)"); |
| 267 | var data = { |
| 268 | grad1: grad1, |
| 269 | grad2: grad2, |
| 270 | grad3: grad3, |
| 271 | grad4: grad4, |
| 272 | grad5: grad5, |
| 273 | grad6: grad6, |
| 274 | grad7: grad7, |
| 275 | grad8: grad8, |
| 276 | }; |
| 277 | return data; |
| 278 | } |
| 279 | |
| 280 | function skip_sep(data) { |
| 281 | if (!data.length) { |
| 282 | return data; |
| 283 | } |
| 284 | while (data[0] == ' ' || data[0] == ',') { |
| 285 | data = data.substring(1); |
| 286 | } |
| 287 | return data; |
| 288 | } |
| 289 | |
| 290 | function find_points(str, value, count, isRelative, relative) { |
| 291 | var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; |
| 292 | var match; |
| 293 | for (var index = 0; index < count; ++index) { |
| 294 | str = skip_sep(str); |
| 295 | match = numRegEx.exec(str); |
| 296 | assert(match); |
| 297 | var x = Number(match[0]); |
| 298 | str = skip_sep(str); |
| 299 | match = numRegEx.exec(str); |
| 300 | assert(match); |
| 301 | var y = Number(match[0]); |
| 302 | value[index] = { x: x, y : y }; |
| 303 | } |
| 304 | if (isRelative) { |
| 305 | for (var index = 0; index < count; index++) { |
| 306 | value[index].x += relative.x; |
| 307 | value[index].y += relative.y; |
| 308 | } |
| 309 | } |
| 310 | return str.substring(match.index + match[0].length); |
| 311 | } |
| 312 | |
| 313 | function find_scalar(str, obj, isRelative, relative) { |
| 314 | var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; |
| 315 | str = skip_sep(str); |
| 316 | var match = numRegEx.exec(str); |
| 317 | obj.value = Number(match[0]); |
| 318 | if (isRelative) { |
| 319 | obj.value += relative; |
| 320 | } |
| 321 | return str.substring(match.index + match[0].length); |
| 322 | } |
| 323 | |
| 324 | function parse_path(data) { |
| 325 | var path = "ctx.beginPath();\n"; |
| 326 | var f = {x:0, y:0}; |
| 327 | var c = {x:0, y:0}; |
| 328 | var lastc = {x:0, y:0}; |
| 329 | var points = []; |
| 330 | var op = '\0'; |
| 331 | var previousOp = '\0'; |
| 332 | var relative = false; |
| 333 | for (;;) { |
| 334 | data = skip_sep(data); |
| 335 | if (!data.length) { |
| 336 | break; |
| 337 | } |
| 338 | var ch = data[0]; |
| 339 | if (('0' <= ch && ch <= '9') || ch == '-' || ch == '+') { |
| 340 | assert(op != '\0'); |
| 341 | } else if (ch == ' ' || ch == ',') { |
| 342 | data = skip_sep(data); |
| 343 | } else { |
| 344 | op = ch; |
| 345 | relative = false; |
| 346 | if ('a' <= op && op <= 'z') { |
| 347 | op = op.toUpperCase(); |
| 348 | relative = true; |
| 349 | } |
| 350 | data = data.substring(1); |
| 351 | data = skip_sep(data); |
| 352 | } |
| 353 | switch (op) { |
| 354 | case 'A': |
| 355 | var radii = []; |
| 356 | data = find_points(data, radii, 1, false, null); |
| 357 | var xaxisObj = {}; |
| 358 | data = find_scalar(data, xaxisObj, false, null); |
| 359 | var largeArcObj = {}; |
| 360 | data = find_scalar(data, largeArcObj, false, null); |
| 361 | var sweepObj = {}; |
| 362 | data = find_scalar(data, sweepObj, false, null); |
| 363 | data = find_points(data, points, 1, relative, c); |
| 364 | var mid = { x: (c.x + points[0].x) / 2, y: (c.y + points[0].y) / 2 }; |
| 365 | var midVec = { x: mid.x - c.x, y: mid.y - c.y }; |
| 366 | var midLenSqr = midVec.x * midVec.x + midVec.y * midVec.y; |
| 367 | var radius = radii[0].x; |
| 368 | var scale = Math.sqrt(midLenSqr) / Math.sqrt(radius * radius - midLenSqr); |
| 369 | var tangentPt = { x: mid.x + midVec.y * scale, |
| 370 | y: mid.y - midVec.x * scale }; |
| 371 | path += "ctx.arcTo(" + tangentPt.x + "," + tangentPt.y + "," |
| 372 | + points[0].x + "," + points[0].y + "," + radius + ");\n"; |
| 373 | c = points[0]; |
| 374 | break; |
| 375 | case 'M': |
| 376 | data = find_points(data, points, 1, relative, c); |
| 377 | path += "ctx.moveTo(" + points[0].x + "," + points[0].y + ");\n"; |
| 378 | op = 'L'; |
| 379 | c = points[0]; |
| 380 | break; |
| 381 | case 'L': |
| 382 | data = find_points(data, points, 1, relative, c); |
| 383 | path += "ctx.lineTo(" + points[0].x + "," + points[0].y + ");\n"; |
| 384 | c = points[0]; |
| 385 | break; |
| 386 | case 'H': { |
| 387 | var xObj = {}; |
| 388 | data = find_scalar(data, xObj, relative, c.x); |
| 389 | path += "ctx.lineTo(" + xObj.value + "," + c.y + ");\n"; |
| 390 | c.x = xObj.value; |
| 391 | } break; |
| 392 | case 'V': { |
| 393 | var yObj = {}; |
| 394 | data = find_scalar(data, y, relative, c.y); |
| 395 | path += "ctx.lineTo(" + c.x + "," + yObj.value+ ");\n"; |
| 396 | c.y = yObj.value; |
| 397 | } break; |
| 398 | case 'C': |
| 399 | data = find_points(data, points, 3, relative, c); |
| 400 | path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," |
| 401 | + points[1].x + "," + points[1].y + "," |
| 402 | + points[2].x + "," + points[2].y + ");\n"; |
| 403 | lastc = points[1]; |
| 404 | c = points[2]; |
| 405 | break; |
| 406 | case 'S': |
| 407 | var pts2_3 = []; |
| 408 | data = find_points(data, pts2_3, 2, relative, c); |
| 409 | points[0] = c; |
| 410 | points[1] = pts2_3[0]; |
| 411 | points[2] = pts2_3[1]; |
| 412 | if (previousOp == 'C' || previousOp == 'S') { |
| 413 | points[0].x -= lastc.x - c.x; |
| 414 | points[0].y -= lastc.y - c.y; |
| 415 | } |
| 416 | path += "ctx.bezierCurveTo(" + points[0].x + "," + points[0].y + "," |
| 417 | + points[1].x + "," + points[1].y + "," |
| 418 | + points[2].x + "," + points[2].y + ");\n"; |
| 419 | lastc = points[1]; |
| 420 | c = points[2]; |
| 421 | break; |
| 422 | case 'Q': // Quadratic Bezier Curve |
| 423 | data = find_points(data, points, 2, relative, c); |
| 424 | path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," |
| 425 | + points[1].x + "," + points[1].y + ");\n"; |
| 426 | lastc = points[0]; |
| 427 | c = points[1]; |
| 428 | break; |
| 429 | case 'T': |
| 430 | var pts2 = []; |
| 431 | data = find_points(data, pts2, 1, relative, c); |
| 432 | points[0] = pts2[0]; |
| 433 | points[1] = pts2[0]; |
| 434 | if (previousOp == 'Q' || previousOp == 'T') { |
| 435 | points[0].x = c.x * 2 - lastc.x; |
| 436 | points[0].y = c.y * 2 - lastc.y; |
| 437 | } |
| 438 | path += "ctx.quadraticCurveTo(" + points[0].x + "," + points[0].y + "," |
| 439 | + points[1].x + "," + points[1].y + ");\n"; |
| 440 | path.quadTo(points[0], points[1]); |
| 441 | lastc = points[0]; |
| 442 | c = points[1]; |
| 443 | break; |
| 444 | case 'Z': |
| 445 | path += "ctx.closePath();\n"; |
| 446 | c = f; |
| 447 | op = '\0'; |
| 448 | break; |
| 449 | case '~': |
| 450 | var args = []; |
| 451 | data = find_points(data, args, 2, false, null); |
| 452 | path += "moveTo(" + args[0].x + "," + args[0].y + ");\n"; |
| 453 | path += "lineTo(" + args[1].x + "," + args[1].y + ");\n"; |
| 454 | break; |
| 455 | default: |
| 456 | return false; |
| 457 | } |
| 458 | if (previousOp == 0) { |
| 459 | f = c; |
| 460 | } |
| 461 | previousOp = op; |
| 462 | } |
| 463 | return path; |
| 464 | } |
| 465 | |
| 466 | function CanvasPaths(ctx) { |
| 467 | var svgStrs = { |
| 468 | // keyframe 1 |
| 469 | span1: "M200,200 Q300,300 200,300", |
| 470 | span2: "M200,200 C100,300 100,400 200,300", |
| 471 | span3: "M200,200 C300,100 100,400 300,200", |
| 472 | wedge1: "M200,200 L500,500 A 424.26,424.26 0,0,1 200,624.26 z", |
| 473 | wedge2: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 -100,500 z", |
| 474 | wedge3: "M200,200 L500,-100 A 424.26,424.26 0,0,1 240,622.5 z", |
| 475 | // keyframe 2 |
| 476 | span4: "M200,200 Q300,300 400,300", |
| 477 | span5: "M200,200 Q280,320 200,400", |
| 478 | span6: "M200,200 Q60,340 100,400", |
| 479 | wedge4: "M200,200 L500,500 A 424.26,424.26 0,0,1 579.47,389.74 z", |
| 480 | wedge5: "M200,200 L389.74,579.47 A 424.26,424.26 0,0,1 200,500 z", |
| 481 | wedge6: "M200,200 L10.26,579.47 A 424.26,424.26 0,0,1 -100,500 z", |
| 482 | // keyframe 3 |
| 483 | xaxis: "M100,200 L300,200", |
| 484 | yaxis: "M200,100 L200,300", |
| 485 | wedgeXY8: "M200,200 L500,500 A 424.26,424.26 0,0,1 624.26,200 z", |
| 486 | wedgeXY6: "M200,200 L-100,500 A 424.26,424.26 0,0,1 200,624.26 z", |
| 487 | wedgeXY3: "M200,200 L-100,-100 A 424.26,424.26 0,0,1 200,-175.74 z", |
| 488 | // keyframe 4 |
| 489 | wedgeXY1: "M200,200 L500,-100 A 424.26,424.26 0,0,1 624.26,200 z", |
| 490 | wedgeXY2: "M200,200 L200,-175.74 A 424.26,424.26 0,0,1 500,-100 z", |
| 491 | wedgeXY4: "M200,200 L-175.74,200 A 424.26,424.26 0,0,1 -100,-100 z", |
| 492 | wedgeXY5: "M200,200 L-100,500 A 424.26,424.26 0,0,1 -175.74,200 z", |
| 493 | wedgeXY7: "M200,200 L200,624.26 A 424.26,424.26 0,0,1 500,500 z", |
| 494 | lineSegment: "M200,200 L200,624.26", |
| 495 | // keyframe 5 |
| 496 | curveSegment: "M200,200 C250,200 300,150 300,100", |
| 497 | curveSegment1: "M200,200 C250,200 300,150 300,100", |
| 498 | curveSegment2: "M200,200 C250,200 300,150 200,100", |
| 499 | curveSegment3: "M200,200 C350,200 250,-150 170,300", |
| 500 | // keyframe 6 |
| 501 | horzSegment: "M200,200 L341.4,200", |
| 502 | vertSegment: "M200,200 L200,341.4", |
| 503 | diagSegment: "M200,200 L100,100", |
| 504 | // keyframe 7 |
| 505 | cubicSegment: "M200,200 C200,200 200,200 200,200", |
| 506 | cubicSegment1: "M200,200 C200,200 200,200 200,200", |
| 507 | cubicSegment2: "M200,200 C250,200 300,200 300,100", |
| 508 | }; |
| 509 | var paths = []; |
| 510 | var keys = Object.keys(svgStrs); |
| 511 | for (var index in keys) { |
| 512 | var key = keys[index]; |
| 513 | var str = svgStrs[key]; |
| 514 | var path = parse_path(str); |
| 515 | var record = []; |
| 516 | paths[key] = { |
| 517 | str: str, |
| 518 | funcBody: path, |
| 519 | }; |
| 520 | } |
| 521 | return paths; |
| 522 | } |
| 523 | |
| 524 | function canvas_fill_font(record) { |
| 525 | assert(record); |
| 526 | var str = 'ctx.font = "normal 1.3rem Helvetica,Arial";\n'; |
| 527 | if (record.fillStyle) { |
| 528 | str += 'ctx.fillStyle = ' + record.fillStyle + ';\n'; |
| 529 | } |
| 530 | return str; |
| 531 | } |
| 532 | |
| 533 | function canvas_fill_text(record) { |
| 534 | assert(record); |
| 535 | assert(typeof record.fillText == 'string'); |
| 536 | return 'ctx.fillText("' + record.fillText + '"'; |
| 537 | } |
| 538 | |
| 539 | function canvas_xy(record) { |
| 540 | var x = typeof record.x == "number" ? record.x : 400; |
| 541 | var y = typeof record.y == "number" ? record.y : 200; |
| 542 | return ', ' + x + ', ' + y + ');\n'; |
| 543 | } |
| 544 | |
| 545 | function canvas_text_xy(record) { |
| 546 | return canvas_fill_text(record) + canvas_xy(record); |
| 547 | } |
| 548 | |
| 549 | function add_canvas_stroke(paths, data, id, strokeStyle) { |
| 550 | var record = {}; |
| 551 | record.data = paths[id].funcBody; |
| 552 | record.style = 'ctx.strokeStyle = ' + (strokeStyle ? strokeStyle : '"black"') + ';\n'; |
| 553 | record.draw = 'ctx.stroke();\n'; |
| 554 | record.func = new Function('ctx', record.data + record.style + record.draw); |
| 555 | return data[id] = record; |
| 556 | } |
| 557 | |
| 558 | function add_canvas_style(record, style) { |
| 559 | record.style += style; |
| 560 | record.func = new Function('ctx', record.data + record.style + record.draw); |
| 561 | } |
| 562 | |
| 563 | function add_canvas_fill(paths, data, id, fillStyle) { |
| 564 | var record = {}; |
| 565 | record.data = paths[id].funcBody; |
| 566 | record.style = 'ctx.fillStyle = ' + (fillStyle ? fillStyle : '"black"') + ';\n'; |
| 567 | record.draw = 'ctx.fill();\n'; |
| 568 | record.func = new Function('ctx', record.data + record.style + record.draw); |
| 569 | return data[id] = record; |
| 570 | } |
| 571 | |
| 572 | function add_canvas_text(data, id, params) { |
| 573 | var record = {}; |
| 574 | record.style = canvas_fill_font(params); |
| 575 | record.draw = canvas_fill_text(params); |
| 576 | record.position = canvas_xy(params); |
| 577 | record.x = params.x; |
| 578 | record.y = params.y; |
| 579 | record.func = new Function('ctx', record.style + record.draw + record.position); |
| 580 | return data[id] = record; |
| 581 | } |
| 582 | |
| 583 | function keyframe1(grads, paths) { |
| 584 | var data = []; |
| 585 | add_canvas_text(data, "spanWedgeDesc", { fillText:"All spans are contained by a wedge" } ); |
| 586 | add_canvas_stroke(paths, data, "span1"); |
| 587 | add_canvas_stroke(paths, data, "span2"); |
| 588 | add_canvas_stroke(paths, data, "span3"); |
| 589 | add_canvas_fill(paths, data, "wedge1", "grads.grad1"); |
| 590 | add_canvas_fill(paths, data, "wedge2", "grads.grad2"); |
| 591 | add_canvas_fill(paths, data, "wedge3", "grads.grad3"); |
| 592 | return data; |
| 593 | } |
| 594 | |
| 595 | function keyframe2(grads, paths) { |
| 596 | var data = []; |
| 597 | add_canvas_text(data, "trivialWedgeDesc1", { fillText:"Wedges that don't overlap can be" } ); |
| 598 | add_canvas_text(data, "trivialWedgeDesc2", { fillText:"easily sorted.", y:240 } ); |
| 599 | add_canvas_stroke(paths, data, "span4").debug = true; |
| 600 | add_canvas_stroke(paths, data, "span5"); |
| 601 | add_canvas_stroke(paths, data, "span6"); |
| 602 | add_canvas_fill(paths, data, "wedge4", "grads.grad1"); |
| 603 | add_canvas_fill(paths, data, "wedge5", "grads.grad2"); |
| 604 | add_canvas_fill(paths, data, "wedge6", "grads.grad3"); |
| 605 | return data; |
| 606 | } |
| 607 | |
| 608 | function setup_axes(paths, data) { |
| 609 | var color = '"rgb(191,191,191)"'; |
| 610 | var xaxis = add_canvas_stroke(paths, data, "xaxis", color); |
| 611 | xaxis.funcBody = canvas_fill_font( { fillStyle:color } ); |
| 612 | xaxis.funcBody += canvas_text_xy( { fillText:"-X", x:100, y:220 } ); |
| 613 | xaxis.funcBody += "ctx.textAlign = 'right';\n"; |
| 614 | xaxis.funcBody += canvas_text_xy( { fillText:"+X", x:300, y:220 } ); |
| 615 | xaxis.func = new Function('ctx', xaxis.data + xaxis.style + xaxis.draw + xaxis.funcBody); |
| 616 | var yaxis = add_canvas_stroke(paths, data, "yaxis", color); |
| 617 | yaxis.funcBody = canvas_fill_font( { fillStyle:color } ); |
| 618 | yaxis.funcBody += "ctx.textBaseline = 'hanging';\n"; |
| 619 | yaxis.funcBody += canvas_text_xy( { fillText:"-Y", x:205, y:100 } ); |
| 620 | yaxis.funcBody += "ctx.textBaseline = 'alphabetic';\n"; |
| 621 | yaxis.funcBody += canvas_text_xy( { fillText:"+Y", x:205, y:300 } ); |
| 622 | yaxis.func = new Function('ctx', yaxis.data + yaxis.style + yaxis.draw + yaxis.funcBody); |
| 623 | } |
| 624 | |
| 625 | function keyframe3(grads, paths) { |
| 626 | var data = []; |
| 627 | add_canvas_text(data, "sectorDesc1", { fillText:"A sector is a wedge of a circle" } ); |
| 628 | add_canvas_text(data, "sectorDesc2", { fillText:"containing a range of points.", y:240 } ); |
| 629 | setup_axes(paths, data); |
| 630 | add_canvas_text(data, "sectorDescXYA", |
| 631 | { fillText:"X > 0 Y > 0 Y < X", x:500, y:310, fillStyle:'"rgb(0,0,255)"'} ); |
| 632 | add_canvas_text(data, "sectorDescXYB", |
| 633 | { fillText:"X < 0 Y > 0 -Y < X", x:500, y:360, fillStyle:'"rgb(0,127,0)"'} ); |
| 634 | add_canvas_text(data, "sectorDescXYC", |
| 635 | { fillText:"X < 0 Y < 0 Y < X", x:500, y:410, fillStyle:'"rgb(255,0,0)"'} ); |
| 636 | add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); |
| 637 | add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); |
| 638 | add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); |
| 639 | return data; |
| 640 | } |
| 641 | |
| 642 | function keyframe4(grads, paths) { |
| 643 | var data = []; |
| 644 | setup_axes(paths, data); |
| 645 | add_canvas_text(data, "lineSingleDesc", |
| 646 | { fillText:"Line spans are contained by a single sector." } ); |
| 647 | add_canvas_text(data, "sectorDescXY1", |
| 648 | { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); |
| 649 | add_canvas_text(data, "sectorDescXY2", |
| 650 | { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); |
| 651 | add_canvas_text(data, "sectorDescXY3", |
| 652 | { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); |
| 653 | add_canvas_text(data, "sectorDescXY4", |
| 654 | { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); |
| 655 | add_canvas_text(data, "sectorDescXY5", |
| 656 | { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); |
| 657 | add_canvas_text(data, "sectorDescXY6", |
| 658 | { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); |
| 659 | add_canvas_text(data, "sectorDescXY7", |
| 660 | { fillText:"X > 0 Y > 0 Y > X", x:500, y:460, fillStyle:'"rgb(63,192,63)"'} ); |
| 661 | add_canvas_text(data, "sectorDescXY8", |
| 662 | { fillText:"X > 0 Y > 0 Y < X", x:500, y:460, fillStyle:'"rgb(0,0,255)"'} ); |
| 663 | add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); |
| 664 | add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); |
| 665 | add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); |
| 666 | add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); |
| 667 | add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); |
| 668 | add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); |
| 669 | add_canvas_fill(paths, data, "wedgeXY7", "grads.grad8"); |
| 670 | add_canvas_fill(paths, data, "wedgeXY8", "grads.grad1"); |
| 671 | add_canvas_stroke(paths, data, "lineSegment"); |
| 672 | return data; |
| 673 | } |
| 674 | |
| 675 | function keyframe5(grads, paths) { |
| 676 | var data = []; |
| 677 | setup_axes(paths, data); |
| 678 | add_canvas_text(data, "curveMultipleDesc1", |
| 679 | { fillText:"A curve span may cover more" } ); |
| 680 | add_canvas_text(data, "curveMultipleDesc2", |
| 681 | { fillText:"than one sector.", y:240 } ); |
| 682 | add_canvas_stroke(paths, data, "curveSegment"); |
| 683 | add_canvas_text(data, "sectorDescXY1", |
| 684 | { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); |
| 685 | add_canvas_text(data, "sectorDescXY2", |
| 686 | { fillText:"X > 0 Y < 0 -Y > X", x:500, y:460, fillStyle:'"rgb(127,127,0)"'} ); |
| 687 | add_canvas_text(data, "sectorDescXY3", |
| 688 | { fillText:"X < 0 Y < 0 Y < X", x:500, y:460, fillStyle:'"rgb(255,0,0)"'} ); |
| 689 | add_canvas_text(data, "sectorDescXY4", |
| 690 | { fillText:"X < 0 Y < 0 Y > X", x:500, y:460, fillStyle:'"rgb(127,0,127)"'} ); |
| 691 | add_canvas_text(data, "sectorDescXY5", |
| 692 | { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,127)"'} ); |
| 693 | add_canvas_text(data, "sectorDescXY6", |
| 694 | { fillText:"X < 0 Y > 0 -Y < X", x:500, y:460, fillStyle:'"rgb(0,127,0)"'} ); |
| 695 | add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); |
| 696 | add_canvas_fill(paths, data, "wedgeXY2", "grads.grad5"); |
| 697 | add_canvas_fill(paths, data, "wedgeXY3", "grads.grad3"); |
| 698 | add_canvas_fill(paths, data, "wedgeXY4", "grads.grad6"); |
| 699 | add_canvas_fill(paths, data, "wedgeXY5", "grads.grad7"); |
| 700 | add_canvas_fill(paths, data, "wedgeXY6", "grads.grad2"); |
| 701 | return data; |
| 702 | } |
| 703 | |
| 704 | function keyframe6(grads, paths) { |
| 705 | var data = []; |
| 706 | setup_axes(paths, data); |
| 707 | |
| 708 | add_canvas_text(data, "line1DDest1", |
| 709 | { fillText:"Some lines occupy one-dimensional" } ); |
| 710 | add_canvas_text(data, "line1DDest2", |
| 711 | { fillText:"sectors.", y:240 } ); |
| 712 | add_canvas_text(data, "sectorDescXY9", |
| 713 | { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); |
| 714 | add_canvas_text(data, "sectorDescXY10", |
| 715 | { fillText:"Y > 0 0 == X", x:500, y:460, fillStyle:'"rgb(31,92,192)"' } ); |
| 716 | add_canvas_text(data, "sectorDescXY11", |
| 717 | { fillText:"X < 0 Y == X", x:500, y:460, fillStyle:'"rgb(127,63,127)"' } ); |
| 718 | var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); |
| 719 | add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); |
| 720 | var vert = add_canvas_stroke(paths, data, "vertSegment", '"rgb(31,92,192)"'); |
| 721 | add_canvas_style(vert, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); |
| 722 | var diag = add_canvas_stroke(paths, data, "diagSegment", '"rgb(127,63,127)"'); |
| 723 | add_canvas_style(diag, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); |
| 724 | return data; |
| 725 | } |
| 726 | |
| 727 | function keyframe7(grads, paths) { |
| 728 | var data = []; |
| 729 | setup_axes(paths, data); |
| 730 | add_canvas_text(data, "curve1dDesc1", |
| 731 | { fillText:"Some curves initially occupy" } ); |
| 732 | add_canvas_text(data, "curve1dDesc2", |
| 733 | { fillText:"one-dimensional sectors, then diverge.", y:240 } ); |
| 734 | add_canvas_stroke(paths, data, "cubicSegment"); |
| 735 | add_canvas_text(data, "sectorDescXY1", |
| 736 | { fillText:"X > 0 Y < 0 -Y < X", x:500, y:460, fillStyle:'"rgb(192,63,192)"'} ); |
| 737 | add_canvas_text(data, "sectorDescXY9", |
| 738 | { fillText:"X > 0 Y == 0", x:500, y:460, fillStyle:'"rgb(192,92,31)"' } ); |
| 739 | var horz = add_canvas_stroke(paths, data, "horzSegment", '"rgb(192,92,31)"'); |
| 740 | add_canvas_style(horz, "ctx.lineWidth = " + 2 * animationState.resScale + ";\n"); |
| 741 | add_canvas_fill(paths, data, "wedgeXY1", "grads.grad4"); |
| 742 | return data; |
| 743 | } |
| 744 | |
| 745 | var canvasData = null; |
| 746 | |
| 747 | function CanvasInit(keyframe) { |
| 748 | canvasData = window[keyframe](grads, paths); |
| 749 | } |
| 750 | |
| 751 | </script> |
| 752 | |
| 753 | <script> |
| 754 | |
| 755 | function interp(A, B, t) { |
| 756 | return A + (B - A) * t; |
| 757 | } |
| 758 | |
| 759 | function interp_cubic_coords(x1, x2, x3, x4, t) |
| 760 | { |
| 761 | var ab = interp(x1, x2, t); |
| 762 | var bc = interp(x2, x3, t); |
| 763 | var cd = interp(x3, x4, t); |
| 764 | var abc = interp(ab, bc, t); |
| 765 | var bcd = interp(bc, cd, t); |
| 766 | var abcd = interp(abc, bcd, t); |
| 767 | return abcd; |
| 768 | } |
| 769 | |
| 770 | function cubic_partial(value, p) { |
| 771 | var x1 = p[0], y1 = p[1], x2 = p[2], y2 = p[3]; |
| 772 | var x3 = p[4], y3 = p[5], x4 = p[6], y4 = p[7]; |
| 773 | var t1 = 0, t2 = value; |
| 774 | var ax = interp_cubic_coords(x1, x2, x3, x4, t1); |
| 775 | var ay = interp_cubic_coords(y1, y2, y3, y4, t1); |
| 776 | var ex = interp_cubic_coords(x1, x2, x3, x4, (t1*2+t2)/3); |
| 777 | var ey = interp_cubic_coords(y1, y2, y3, y4, (t1*2+t2)/3); |
| 778 | var fx = interp_cubic_coords(x1, x2, x3, x4, (t1+t2*2)/3); |
| 779 | var fy = interp_cubic_coords(y1, y2, y3, y4, (t1+t2*2)/3); |
| 780 | var dx = interp_cubic_coords(x1, x2, x3, x4, t2); |
| 781 | var dy = interp_cubic_coords(y1, y2, y3, y4, t2); |
| 782 | var mx = ex * 27 - ax * 8 - dx; |
| 783 | var my = ey * 27 - ay * 8 - dy; |
| 784 | var nx = fx * 27 - ax - dx * 8; |
| 785 | var ny = fy * 27 - ay - dy * 8; |
| 786 | var bx = (mx * 2 - nx) / 18; |
| 787 | var by = (my * 2 - ny) / 18; |
| 788 | var cx = (nx * 2 - mx) / 18; |
| 789 | var cy = (ny * 2 - my) / 18; |
| 790 | var array = [ |
| 791 | ax, ay, bx, by, cx, cy, dx, dy |
| 792 | ]; |
| 793 | return array; |
| 794 | } |
| 795 | |
| 796 | function evaluate_at(value, p) { |
| 797 | var array = []; |
| 798 | for (var index = 0; index < p.length; ++index) { |
| 799 | var func = new Function('value', 'return ' + p[index] + ';'); |
| 800 | array[index] = func(value); |
| 801 | } |
| 802 | return array; |
| 803 | } |
| 804 | |
| 805 | function interpolate_at(value, p) { |
| 806 | var array = []; |
| 807 | var start = p[0]; |
| 808 | var end = p[1]; |
| 809 | assert(typeof end == typeof start); |
| 810 | switch (typeof start) { |
| 811 | case 'object': |
| 812 | for (var index = 0; index < start.length; ++index) { |
| 813 | array[index] = interp(start[index], end[index], value); |
| 814 | } |
| 815 | break; |
| 816 | case 'number': |
| 817 | array[index] = interp(start, end, value); |
| 818 | break; |
| 819 | default: |
| 820 | debugger; |
| 821 | } |
| 822 | return array; |
| 823 | } |
| 824 | |
| 825 | function AnimationAddCommon(timing, range, attr, inParams) { |
| 826 | var animation = { |
| 827 | timing: timing, |
| 828 | range: range, |
| 829 | attr: attr, |
| 830 | inParams: inParams, |
| 831 | duration: timing[1] - timing[0], |
| 832 | remaining: timing[1] - timing[0], |
| 833 | firstStep: true, |
| 834 | } |
| 835 | animationsPending.push(animation); |
| 836 | return animation; |
| 837 | } |
| 838 | |
| 839 | function AnimationAddSVG(timing, element, range, attr, inParams) { |
| 840 | var animation = AnimationAddCommon(timing, range, attr, inParams); |
| 841 | animation.element = element; |
| 842 | return animation; |
| 843 | } |
| 844 | |
| 845 | function AnimationAddCanvas(timing, element, range, attr, inParams) { |
| 846 | var animation = AnimationAddCommon(timing, range, attr, inParams); |
| 847 | animation.element = canvasData[element]; |
| 848 | assert(animation.element); |
| 849 | animation.firstElement = null; |
| 850 | return animation; |
| 851 | } |
| 852 | |
| 853 | function AnimationAdd(timing, e, range, attr, funct, inParams) { |
| 854 | if (!range) { |
| 855 | range = [0, 1]; |
| 856 | } |
| 857 | if (!attr) { |
| 858 | attr = 'opacity'; |
| 859 | } |
| 860 | if (!funct) { |
| 861 | funct = interpolate_at; |
| 862 | } |
| 863 | var element; |
| 864 | switch (animationState.displayEngine) { |
| 865 | case 'SVG': |
| 866 | element = typeof e == 'string' ? document.getElementById(e) : e; |
| 867 | break; |
| 868 | case 'Canvas': |
| 869 | element = typeof e == 'string' ? e : e.id; |
| 870 | break; |
| 871 | default: |
| 872 | debugger; |
| 873 | } |
| 874 | assert(element); |
| 875 | switch (attr) { |
| 876 | case 'path': |
| 877 | if (!inParams) { |
| 878 | inParams = PathDataArray(element); |
| 879 | } |
| 880 | break; |
| 881 | case 'opacity': |
| 882 | if (!inParams) { |
| 883 | inParams = [0, 1]; |
| 884 | } |
| 885 | break; |
| 886 | default: |
| 887 | debugger; |
| 888 | } |
| 889 | var funcBody = 'var outParams = ' + funct.name + '(value, inParams);\n'; |
| 890 | switch (animationState.displayEngine) { |
| 891 | case 'SVG': |
| 892 | switch (attr) { |
| 893 | case 'path': |
| 894 | var verbArray = PathVerbArray(element); |
| 895 | funcBody += 'return '; |
| 896 | for (var index = 0; index < inParams.length; ++index) { |
| 897 | funcBody += '"' + verbArray[index] + '"'; |
| 898 | funcBody += 'outParams[' + index + '];\n'; |
| 899 | } |
| 900 | if (verbArray.length > inParams.length) { |
| 901 | funcBody += '"' + verbArray[verbArray.length - 1] + '"'; |
| 902 | } |
| 903 | funcBody += ';\n'; |
| 904 | var animation = AnimationAddSVG(timing, element, range, "d", inParams); |
| 905 | animation.func = new Function('value', 'inParams', funcBody); |
| 906 | break; |
| 907 | case 'opacity': |
| 908 | if (animation.element.getAttribute("stroke-opacity")) { |
| 909 | animation = AnimationAddSVG(timing, element, range, "stroke-opacity", inParams); |
| 910 | } |
| 911 | if (animation.element.getAttribute("fill-opacity")) { |
| 912 | animation = AnimationAddSVG(timing, element, range, "fill-opacity", inParams); |
| 913 | } |
| 914 | break; |
| 915 | default: |
| 916 | debugger; |
| 917 | } |
| 918 | case 'Canvas': |
| 919 | switch (attr) { |
| 920 | case 'path': |
| 921 | var verbArray = PathVerbArray(element); |
| 922 | for (var index = 0; index < inParams.length; ++index) { |
| 923 | funcBody += verbArray[index]; |
| 924 | funcBody += 'outParams[' + index + ']'; |
| 925 | } |
| 926 | if (verbArray.length > inParams.length) { |
| 927 | funcBody += verbArray[verbArray.length - 1]; |
| 928 | } |
| 929 | animation = AnimationAddCanvas(timing, element, range, attr, inParams); |
| 930 | funcBody += animation.element.style + animation.element.draw; |
| 931 | animation.func = new Function('ctx', 'value', 'inParams', funcBody); |
| 932 | break; |
| 933 | case 'opacity': |
| 934 | animation = AnimationAddCanvas(timing, element, range, attr, inParams); |
| 935 | break; |
| 936 | default: |
| 937 | debugger; |
| 938 | } |
| 939 | break; |
| 940 | default: |
| 941 | debugger; |
| 942 | } |
| 943 | return animation; |
| 944 | } |
| 945 | |
| 946 | function path_data_common(element, getValues) { |
| 947 | var numRegEx = /-?\d+\.?\d*(?:e-?\d+)?/g; |
| 948 | var data = []; |
| 949 | var match; |
| 950 | var path; |
| 951 | switch (animationState.displayEngine) { |
| 952 | case 'SVG': path = element.getAttribute("d"); break; |
| 953 | case 'Canvas': path = paths[element].funcBody; break; |
| 954 | default: debugger; |
| 955 | } |
| 956 | if (getValues) { |
| 957 | while ((match = numRegEx.exec(path))) { |
| 958 | data.push(Number(match[0])); |
| 959 | } |
| 960 | } else { |
| 961 | var sIndex = 0; |
| 962 | while ((match = numRegEx.exec(path))) { |
| 963 | if (sIndex < match.index) { |
| 964 | data.push(path.substring(sIndex, match.index)); |
| 965 | } |
| 966 | sIndex = match.index + match[0].length; |
| 967 | } |
| 968 | if (sIndex < path.length) { |
| 969 | data.push(path.substring(sIndex, path.length)); |
| 970 | } |
| 971 | } |
| 972 | return data; |
| 973 | } |
| 974 | |
| 975 | function PathDataArray(element) { |
| 976 | return path_data_common(element, true); |
| 977 | } |
| 978 | |
| 979 | function PathVerbArray(element) { |
| 980 | return path_data_common(element, false); |
| 981 | } |
| 982 | |
| 983 | function PathSet(element, funct, value, params) { |
| 984 | var pathVerbs = PathVerbArray(element); |
| 985 | if (funct) { |
| 986 | params = funct(value, params); |
| 987 | } |
| 988 | var setValue = ''; |
| 989 | for (var index = 0; index < params.length; ++index) { |
| 990 | setValue += pathVerbs[index]; |
| 991 | setValue += params[index]; |
| 992 | } |
| 993 | if (pathVerbs.length > params.length) { |
| 994 | setValue += pathVerbs[pathVerbs.length - 1]; |
| 995 | } |
| 996 | switch (animationState.displayEngine) { |
| 997 | case 'SVG': |
| 998 | element.setAttribute('d', setValue); |
| 999 | break; |
| 1000 | case 'Canvas': |
| 1001 | element.func = new Function('ctx', setValue + element.style + element.draw); |
| 1002 | break; |
| 1003 | default: |
| 1004 | debugger; |
| 1005 | } |
| 1006 | } |
| 1007 | |
| 1008 | function RemoveFromArray(array, element) { |
| 1009 | for (var index in array) { |
| 1010 | var record = array[index]; |
| 1011 | if (record.element == element) { |
| 1012 | array.splice(index, 1); |
| 1013 | break; |
| 1014 | } |
| 1015 | } |
| 1016 | } |
| 1017 | |
| 1018 | function EndAnimationCanvas(animation, visibleFinished) { |
| 1019 | var changeAlpha = "opacity" == animation.attr; |
| 1020 | if (!changeAlpha || animation.range[1] > 0) { |
| 1021 | if (changeAlpha) { |
| 1022 | ctx.save(); |
| 1023 | ctx.globalAlpha = animation.range[1]; |
| 1024 | } |
| 1025 | if (animation.func) { |
| 1026 | animation.func(ctx, animation.range[animation.range.length - 1], animation.inParams); |
| 1027 | } else { |
| 1028 | animation.element.func(ctx); |
| 1029 | } |
| 1030 | if (changeAlpha) { |
| 1031 | ctx.restore(); |
| 1032 | } |
| 1033 | // if (visibleFinished) { |
| 1034 | // visibleFinished.push(animation); |
| 1035 | // } |
| 1036 | } else { |
| 1037 | // if (visibleFinished) { |
| 1038 | // RemoveFromArray(visibleFinished, animation.element); |
| 1039 | // } |
| 1040 | } |
| 1041 | } |
| 1042 | |
| 1043 | /* start here |
| 1044 | canvas: |
| 1045 | |
| 1046 | display list : |
| 1047 | for each element (canvas) |
| 1048 | save |
| 1049 | set global alpha (override) |
| 1050 | create geometry (override) |
| 1051 | create style (override) |
| 1052 | draw |
| 1053 | restore |
| 1054 | |
| 1055 | maybe each action should have an override slot |
| 1056 | animations write to the slot |
| 1057 | each element in display list then iterates overrides once the animations complete the frame |
| 1058 | |
| 1059 | so, first -- |
| 1060 | active animations update the display list |
| 1061 | |
| 1062 | next -- |
| 1063 | active animations install themselves in override slots |
| 1064 | |
| 1065 | finally |
| 1066 | display list is iterated, calling override slots |
| 1067 | |
| 1068 | ---------------- |
| 1069 | |
| 1070 | svg: |
| 1071 | display list is implicit |
| 1072 | |
| 1073 | active animations write element attributes |
| 1074 | */ |
| 1075 | |
| 1076 | function EndAnimationSVG(animation, visibleFinished) { |
| 1077 | switch (animation.attr) { |
| 1078 | case "opacity": |
| 1079 | animation.element.setAttribute(animation.attribute, animation.range[1]); |
| 1080 | if (animation.range[1] > 0) { |
| 1081 | visibleFinished.push(animation); |
| 1082 | } else { |
| 1083 | RemoveFromArray(visibleFinished, animation.element); |
| 1084 | } |
| 1085 | break; |
| 1086 | case "path": |
| 1087 | var attrStr = animation.func(animation.range[1], animation.inParams); |
| 1088 | animation.element.setAttribute(animation.attribute, attrStr); |
| 1089 | break; |
| 1090 | default: |
| 1091 | debugger; |
| 1092 | } |
| 1093 | } |
| 1094 | |
| 1095 | function StepAnimationCanvas(animation, value) { |
| 1096 | var endValue = animation.range[animation.range.length - 1]; |
| 1097 | var interp = animation.range[0] + (endValue - animation.range[0]) * (1 - value); |
| 1098 | if (animation.firstStep) { |
| 1099 | RemoveFromArray(visibleFinished, animation.element); |
| 1100 | animation.firstStep = false; |
| 1101 | } |
| 1102 | var changeAlpha = "opacity" == animation.attr; |
| 1103 | if (changeAlpha) { |
| 1104 | ctx.save(); |
| 1105 | ctx.globalAlpha = interp; |
| 1106 | } |
| 1107 | if (animation.func) { |
| 1108 | animation.func(ctx, interp, animation.inParams); |
| 1109 | } else { |
| 1110 | animation.element.func(ctx); |
| 1111 | } |
| 1112 | if (changeAlpha) { |
| 1113 | ctx.restore(); |
| 1114 | } |
| 1115 | } |
| 1116 | |
| 1117 | function StepAnimationSVG(animation, value) { |
| 1118 | var interp = animation.range[0] + (animation.range[1] - animation.range[0]) * (1 - value); |
| 1119 | switch (animation.attr) { |
| 1120 | case "opacity": |
| 1121 | animation.element.setAttribute(animation.attribute, interp); |
| 1122 | break; |
| 1123 | case "path": |
| 1124 | var attrStr = animation.func(interp, animation.inParams); |
| 1125 | animation.element.setAttribute(animation.attribute, attrStr); |
| 1126 | break; |
| 1127 | default: |
| 1128 | debugger; |
| 1129 | } |
| 1130 | } |
| 1131 | |
| 1132 | var animate_frame = 0; |
| 1133 | |
| 1134 | function AnimateList(now) { |
| 1135 | ++animate_frame; |
| 1136 | if (animationState.paused) { |
| 1137 | return; |
| 1138 | } |
| 1139 | if (animationState.start == null) { |
| 1140 | animationState.start = now - animationState.time; |
| 1141 | } |
| 1142 | animationState.time = now - animationState.start; |
| 1143 | var stillPending = []; |
| 1144 | for (var index in animationsPending) { |
| 1145 | var animation = animationsPending[index]; |
| 1146 | var interval = animationState.time - animation.timing[0]; |
| 1147 | if (interval <= 0) { |
| 1148 | stillPending.push(animation); |
| 1149 | continue; |
| 1150 | } |
| 1151 | animationsActive.push(animation); |
| 1152 | var inList = false; |
| 1153 | for (var dlIndex in displayList) { |
| 1154 | var displayable = displayList[dlIndex]; |
| 1155 | if (displayable == animation.element) { |
| 1156 | inList = true; |
| 1157 | break; |
| 1158 | } |
| 1159 | } |
| 1160 | if (!inList) { |
| 1161 | displayList.push(animation.element); |
| 1162 | } |
| 1163 | } |
| 1164 | animationsPending = stillPending; |
| 1165 | var stillAnimating = []; |
| 1166 | if ('Canvas' == animationState.displayEngine) { |
| 1167 | ctx.clearRect(0, 0, canvas.width, canvas.height); |
| 1168 | // for (var index in visibleFinished) { |
| 1169 | // var animation = visibleFinished[index]; |
| 1170 | // animation.endAnimation = false; |
| 1171 | // } |
| 1172 | } |
| 1173 | for (var index in animationsActive) { |
| 1174 | var animation = animationsActive[index]; |
| 1175 | var interval = animationState.time - animation.timing[0]; |
| 1176 | animation.remaining = animation.duration > interval ? animation.duration - interval : 0; |
| 1177 | animation.endAnimation = animation.remaining <= 0; |
| 1178 | if (animation.endAnimation) { |
| 1179 | switch (animationState.displayEngine) { |
| 1180 | case 'SVG': EndAnimationSVG(animation, visibleFinished); break; |
| 1181 | case 'Canvas': EndAnimationCanvas(animation, visibleFinished); break; |
| 1182 | default: debugger; |
| 1183 | } |
| 1184 | continue; |
| 1185 | } |
| 1186 | var value = animation.remaining / animation.duration; |
| 1187 | switch (animationState.displayEngine) { |
| 1188 | case 'SVG': StepAnimationSVG(animation, value); break; |
| 1189 | case 'Canvas': |
| 1190 | if (!animation.firstElement || !animation.firstElement.endAnimation) { |
| 1191 | StepAnimationCanvas(animation, value); |
| 1192 | } |
| 1193 | break; |
| 1194 | default: debugger; |
| 1195 | } |
| 1196 | stillAnimating.push(animation); |
| 1197 | } |
| 1198 | if ('Canvas' == animationState.displayEngine) { |
| 1199 | for (var index in visibleFinished) { |
| 1200 | var animation = visibleFinished[index]; |
| 1201 | if (!animation.endAnimation) { |
| 1202 | EndAnimationCanvas(animation, null); |
| 1203 | } |
| 1204 | } |
| 1205 | } |
| 1206 | animationsActive = stillAnimating; |
| 1207 | if (animationsPending.length || animationsActive.length) { |
| 1208 | animationState.requestID = requestAnimationFrame(AnimateList); |
| 1209 | } |
| 1210 | } |
| 1211 | |
| 1212 | function CancelAnimate(now) { |
| 1213 | if (animationState.start == null) { |
| 1214 | animationState.start = now; |
| 1215 | } |
| 1216 | var time = now - animationState.start; |
| 1217 | var stillAnimating = []; |
| 1218 | for (var index in animationsActive) { |
| 1219 | var animation = animationsActive[index]; |
| 1220 | var remaining = animation.remaining - time; |
| 1221 | var value = remaining / animation.duration; |
| 1222 | switch (animationState.displayEngine) { |
| 1223 | case 'SVG': animation.element.setAttribute(animation.attribute, value); break; |
| 1224 | case 'Canvas': break; |
| 1225 | } |
| 1226 | if (remaining <= 0) { |
| 1227 | continue; |
| 1228 | } |
| 1229 | stillAnimating.push(animation); |
| 1230 | } |
| 1231 | animationsActive = stillAnimating; |
| 1232 | if (animationsActive.length) { |
| 1233 | animationState.requestID = requestAnimationFrame(CancelAnimate); |
| 1234 | return; |
| 1235 | } |
| 1236 | animationsPending = []; |
| 1237 | animationState.reset(); |
| 1238 | if (keyFrameQueue.length > 0) { |
| 1239 | var animationFunc = keyFrameQueue.pop(); |
| 1240 | animationFunc(); |
| 1241 | } |
| 1242 | } |
| 1243 | |
| 1244 | function CancelAnimation() { |
| 1245 | cancelAnimationFrame(animationState.requestID); |
| 1246 | for (var index in animationsActive) { |
| 1247 | var animation = animationsActive[index]; |
| 1248 | switch (animation.attr) { |
| 1249 | case "opacity": |
| 1250 | var tmp = animation.range[0]; animation.range[0] = animation.range[1]; animation[1] = tmp; |
| 1251 | animation.remaining = animation.duration - animation.remaining; |
| 1252 | animation.remaining /= animation.duration / 1000; |
| 1253 | animation.duration = 1000; |
| 1254 | break; |
| 1255 | case "fadeOut": |
| 1256 | RemoveFromArray(visibleFinished, animation.element); |
| 1257 | break; |
| 1258 | case "path": |
| 1259 | break; |
| 1260 | default: |
| 1261 | debugger; |
| 1262 | |
| 1263 | } |
| 1264 | } |
| 1265 | for (var index in visibleFinished) { |
| 1266 | var animation = visibleFinished[index]; |
| 1267 | animation.action = "fadeOut"; |
| 1268 | animation.remaining = animation.duration = 1000; |
| 1269 | animationsActive.push(animation); |
| 1270 | } |
| 1271 | visibleFinished = []; |
| 1272 | animationState.reset(); |
| 1273 | animationState.requestID = requestAnimationFrame(CancelAnimate); |
| 1274 | } |
| 1275 | |
| 1276 | function PauseAnimation() { |
| 1277 | animationState.paused = true; |
| 1278 | } |
| 1279 | |
| 1280 | function QueueAnimation(animationFunc) { |
| 1281 | if (null == animationState.requestID) { |
| 1282 | animationFunc(); |
| 1283 | return; |
| 1284 | } |
| 1285 | keyFrameQueue.push(animationFunc); |
| 1286 | } |
| 1287 | |
| 1288 | function UnpauseAnimation() { |
| 1289 | animationState.paused = false; |
| 1290 | animationState.start = performance.now() - animationState.time; |
| 1291 | animationState.requestID = requestAnimationFrame(AnimateList); |
| 1292 | } |
| 1293 | |
| 1294 | function SetupTextSVG(t, x, y) { |
| 1295 | var text; |
| 1296 | if (typeof t == "string") { |
| 1297 | text = document.getElementById(t); |
| 1298 | } else { |
| 1299 | text = t; |
| 1300 | } |
| 1301 | text.setAttribute("font-family", "Helvetica,Arial"); |
| 1302 | text.setAttribute("font-size", "1.3rem"); |
| 1303 | if (typeof x == 'number') { |
| 1304 | text.setAttribute("x", x); |
| 1305 | } else if (null == text.getAttribute("x")) { |
| 1306 | text.setAttribute("x", 400); |
| 1307 | } |
| 1308 | if (typeof y == 'number') { |
| 1309 | text.setAttribute("y", y); |
| 1310 | } else if (null == text.getAttribute("y")) { |
| 1311 | text.setAttribute("y", 200); |
| 1312 | } |
| 1313 | } |
| 1314 | |
| 1315 | function SetupTextCanvas(t, x, y) { |
| 1316 | var text = typeof t == 'string' ? t : t.id; |
| 1317 | var record = canvasData[text]; |
| 1318 | if (typeof x == 'number') { |
| 1319 | record.x = x; |
| 1320 | } |
| 1321 | if (typeof y == 'number') { |
| 1322 | record.y = y; |
| 1323 | } |
| 1324 | record.position = canvas_xy(record); |
| 1325 | record.func = new Function('ctx', record.style + record.draw + record.position); |
| 1326 | } |
| 1327 | |
| 1328 | function SetupText(t, x, y) { |
| 1329 | switch (animationState.displayEngine) { |
| 1330 | case 'SVG': |
| 1331 | SetupTextSVG(t, x, y); |
| 1332 | break; |
| 1333 | case 'Canvas': |
| 1334 | SetupTextCanvas(t, x, y); |
| 1335 | break; |
| 1336 | default: |
| 1337 | debugger; |
| 1338 | } |
| 1339 | } |
| 1340 | |
| 1341 | function FirstText(text) { |
| 1342 | SetupText(text); |
| 1343 | AnimationAdd([0, 1000], text); |
| 1344 | } |
| 1345 | |
| 1346 | |
| 1347 | function EngineInit(keyframe) { |
| 1348 | displayList = []; |
| 1349 | switch (animationState.displayEngine) { |
| 1350 | case 'SVG': break; |
| 1351 | case 'Canvas': CanvasInit(keyframe); break; |
| 1352 | default: debugger; |
| 1353 | } |
| 1354 | } |
| 1355 | |
| 1356 | function EngineStart() { |
| 1357 | switch (animationState.displayEngine) { |
| 1358 | case 'SVG': break; |
| 1359 | case 'Canvas': |
| 1360 | // associate fadeIn and fadeOut |
| 1361 | for (var outerIndex in animationsPending) { |
| 1362 | var outer = animationsPending[outerIndex]; |
| 1363 | for (var innerIndex in animationsPending) { |
| 1364 | if (outerIndex == innerIndex) { |
| 1365 | continue; |
| 1366 | } |
| 1367 | var inner = animationsPending[innerIndex]; |
| 1368 | if (inner.element == outer.element) { |
| 1369 | inner.firstElement = outer; |
| 1370 | continue; |
| 1371 | } |
| 1372 | } |
| 1373 | } |
| 1374 | break; |
| 1375 | default: debugger; |
| 1376 | } |
| 1377 | animationState.reset(); |
| 1378 | animationState.requestID = requestAnimationFrame(AnimateList); |
| 1379 | } |
| 1380 | |
| 1381 | function AnimateSpanWedge() { |
| 1382 | EngineInit('keyframe1'); |
| 1383 | FirstText(spanWedgeDesc); |
| 1384 | AnimationAdd([1000, 2000], span1); |
| 1385 | AnimationAdd([1500, 3000], wedge1); |
| 1386 | AnimationAdd([3500, 4000], span1, [1, 0]); |
| 1387 | AnimationAdd([3500, 4000], wedge1, [1, 0]); |
| 1388 | AnimationAdd([4000, 5000], span2); |
| 1389 | AnimationAdd([4500, 6000], wedge2); |
| 1390 | AnimationAdd([6500, 7000], span2, [1, 0]); |
| 1391 | AnimationAdd([6500, 7000], wedge2, [1, 0]); |
| 1392 | AnimationAdd([7000, 8000], span3); |
| 1393 | AnimationAdd([7500, 9000], wedge3); |
| 1394 | EngineStart(); |
| 1395 | } |
| 1396 | |
| 1397 | function AnimateTrivialWedge() { |
| 1398 | EngineInit('keyframe2'); |
| 1399 | FirstText(trivialWedgeDesc1); |
| 1400 | FirstText(trivialWedgeDesc2); |
| 1401 | AnimationAdd([2000, 3500], span4); |
| 1402 | AnimationAdd([2000, 3500], wedge4); |
| 1403 | AnimationAdd([2000, 3500], span5); |
| 1404 | AnimationAdd([2000, 3500], wedge5); |
| 1405 | AnimationAdd([2000, 3500], span6); |
| 1406 | AnimationAdd([2000, 3500], wedge6); |
| 1407 | EngineStart(); |
| 1408 | } |
| 1409 | |
| 1410 | function AnimateSectorDesc() { |
| 1411 | EngineInit('keyframe3'); |
| 1412 | FirstText(sectorDesc1); |
| 1413 | FirstText(sectorDesc2); |
| 1414 | AnimationAdd([ 0, 1000], xaxis); |
| 1415 | AnimationAdd([ 500, 1500], yaxis); |
| 1416 | AnimationAdd([2000, 3500], sectorDescXYA); |
| 1417 | AnimationAdd([2000, 3500], wedgeXY8); |
| 1418 | AnimationAdd([3000, 4500], sectorDescXYB); |
| 1419 | AnimationAdd([3000, 4500], wedgeXY6); |
| 1420 | AnimationAdd([4000, 5500], sectorDescXYC); |
| 1421 | AnimationAdd([4000, 5500], wedgeXY3); |
| 1422 | EngineStart(); |
| 1423 | } |
| 1424 | |
| 1425 | function AnimateLineSingle() { |
| 1426 | EngineInit('keyframe4'); |
| 1427 | FirstText(lineSingleDesc); |
| 1428 | for (var i = 1; i <= 8; ++i) { |
| 1429 | SetupText("sectorDescXY" + i, 500, 260); |
| 1430 | } |
| 1431 | AnimationAdd([ 0, 1000], xaxis); |
| 1432 | AnimationAdd([ 0, 1000], yaxis); |
| 1433 | AnimationAdd([1000, 2000], lineSegment); |
| 1434 | AnimationAdd([1000, 3000], lineSegment, [-22.5 * Math.PI / 180], "path", evaluate_at, |
| 1435 | [ circle.center.x, circle.center.y, |
| 1436 | circle.center.x + " + " + circle.radius + " * Math.cos(value)", |
| 1437 | circle.center.y + " + " + circle.radius + " * Math.sin(value)", |
| 1438 | ]); |
| 1439 | AnimationAdd([2000, 3000], sectorDescXY1); |
| 1440 | AnimationAdd([2000, 3000], wedgeXY1); |
| 1441 | AnimationAdd([3000, 7000], lineSegment, [-22.5 * Math.PI / 180, (-22.5 - 360) * Math.PI / 180], |
| 1442 | "path", evaluate_at, |
| 1443 | [ circle.center.x, circle.center.y, |
| 1444 | circle.center.x + " + " + circle.radius + " * Math.cos(value)", |
| 1445 | circle.center.y + " + " + circle.radius + " * Math.sin(value)", |
| 1446 | ]); |
| 1447 | for (var i = 1; i < 8; ++i) { |
| 1448 | AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + (i + 1)); |
| 1449 | AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + (i + 1)); |
| 1450 | AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "sectorDescXY" + i, [1, 0]); |
| 1451 | AnimationAdd([2500 + 500 * i, 3000 + 500 * i], "wedgeXY" + i, [1, 0]); |
| 1452 | } |
| 1453 | AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY1); |
| 1454 | AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY1); |
| 1455 | AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], sectorDescXY8, [1, 0]); |
| 1456 | AnimationAdd([2500 + 500 * 8, 3000 + 500 * 8], wedgeXY8, [1, 0]); |
| 1457 | EngineStart(); |
| 1458 | } |
| 1459 | |
| 1460 | function AnimateCurveMultiple() { |
| 1461 | EngineInit('keyframe5'); |
| 1462 | var cubicStart = PathDataArray(curveSegment1); |
| 1463 | var cubicMid = PathDataArray(curveSegment2); |
| 1464 | var cubicEnd = PathDataArray(curveSegment3); |
| 1465 | FirstText(curveMultipleDesc1); |
| 1466 | FirstText(curveMultipleDesc2); |
| 1467 | for (var i = 1; i <= 6; ++i) { |
| 1468 | SetupText("sectorDescXY" + i, 500, 260 + i * 25); |
| 1469 | } |
| 1470 | AnimationAdd([ 0, 1000], xaxis); |
| 1471 | AnimationAdd([ 0, 1000], yaxis); |
| 1472 | AnimationAdd([1000, 2000], curveSegment); |
| 1473 | AnimationAdd([2000, 3000], sectorDescXY1); |
| 1474 | AnimationAdd([2000, 3000], wedgeXY1); |
| 1475 | AnimationAdd([3000, 4000], curveSegment, [0, 1], "path", interpolate_at, [cubicStart, cubicMid]); |
| 1476 | AnimationAdd([4000, 5000], sectorDescXY2); |
| 1477 | AnimationAdd([4000, 5000], wedgeXY2); |
| 1478 | AnimationAdd([5000, 6000], curveSegment, [0, 1], "path", interpolate_at, [cubicMid, cubicEnd]); |
| 1479 | AnimationAdd([6000, 7000], sectorDescXY3); |
| 1480 | AnimationAdd([6000, 7000], wedgeXY3); |
| 1481 | AnimationAdd([6000, 7000], sectorDescXY4); |
| 1482 | AnimationAdd([6000, 7000], wedgeXY4); |
| 1483 | AnimationAdd([6000, 7000], sectorDescXY5); |
| 1484 | AnimationAdd([6000, 7000], wedgeXY5); |
| 1485 | AnimationAdd([6000, 7000], sectorDescXY6); |
| 1486 | AnimationAdd([6000, 7000], wedgeXY6); |
| 1487 | EngineStart(); |
| 1488 | } |
| 1489 | |
| 1490 | function AnimateOneDLines() { |
| 1491 | EngineInit('keyframe6'); |
| 1492 | FirstText(line1DDest1); |
| 1493 | FirstText(line1DDest2); |
| 1494 | for (var i = 9; i <= 11; ++i) { |
| 1495 | SetupText("sectorDescXY" + i, 500, 260 + (i - 8) * 25); |
| 1496 | } |
| 1497 | AnimationAdd([ 0, 1000], xaxis); |
| 1498 | AnimationAdd([ 0, 1000], yaxis); |
| 1499 | AnimationAdd([2000, 3000], sectorDescXY9); |
| 1500 | AnimationAdd([2000, 3000], horzSegment); |
| 1501 | AnimationAdd([3000, 4000], sectorDescXY10); |
| 1502 | AnimationAdd([3000, 4000], vertSegment); |
| 1503 | AnimationAdd([4000, 5000], sectorDescXY11); |
| 1504 | AnimationAdd([4000, 5000], diagSegment); |
| 1505 | EngineStart(); |
| 1506 | } |
| 1507 | |
| 1508 | function AnimateDiverging() { |
| 1509 | EngineInit('keyframe7'); |
| 1510 | var cubicData = PathDataArray(cubicSegment2); |
| 1511 | FirstText(curve1dDesc1); |
| 1512 | FirstText(curve1dDesc2); |
| 1513 | SetupText("sectorDescXY9", 500, 285); |
| 1514 | SetupText("sectorDescXY1", 500, 320); |
| 1515 | AnimationAdd([ 0, 1000], xaxis); |
| 1516 | AnimationAdd([ 0, 1000], yaxis); |
| 1517 | AnimationAdd([1900, 1900], cubicSegment); |
| 1518 | AnimationAdd([2000, 3000], cubicSegment, [0, 1], "path", cubic_partial, cubicData); |
| 1519 | AnimationAdd([2000, 3000], sectorDescXY9); |
| 1520 | AnimationAdd([2000, 3000], horzSegment); |
| 1521 | AnimationAdd([3000, 4000], sectorDescXY1); |
| 1522 | AnimationAdd([3000, 4000], wedgeXY1); |
| 1523 | EngineStart(); |
| 1524 | } |
| 1525 | |
| 1526 | circle.animate = AnimateCircle; |
| 1527 | circle.start = null; |
| 1528 | |
| 1529 | function AngleToPt(center, radius, degrees) { |
| 1530 | var radians = degrees * Math.PI / 180.0; |
| 1531 | return { |
| 1532 | x: center.x + (radius * Math.cos(radians)), |
| 1533 | y: center.y - (radius * Math.sin(radians)) |
| 1534 | }; |
| 1535 | } |
| 1536 | |
| 1537 | function PtsToSweep(pt1, pt2, center) { // unused |
| 1538 | return { |
| 1539 | start: 180 / Math.PI * Math.atan2(pt1.y - center.y, pt1.x - center.x), |
| 1540 | end: 180 / Math.PI * Math.atan2(pt2.y - center.y, pt2.x - center.x) |
| 1541 | }; |
| 1542 | } |
| 1543 | |
| 1544 | |
| 1545 | function ArcStr(center, radius, startAngle, endAngle) { |
| 1546 | var endPt = AngleToPt(center, radius, endAngle); |
| 1547 | var arcSweep = endAngle - startAngle <= 180 ? "0" : "1"; |
| 1548 | return ["A", radius, radius, 0, arcSweep, 0, endPt.x, endPt.y].join(" "); |
| 1549 | } |
| 1550 | |
| 1551 | function ArcStart(center, radius, startAngle, endAngle) { |
| 1552 | var startPt = AngleToPt(center, radius, startAngle); |
| 1553 | return [ startPt.x, startPt.y, ArcStr(center, radius, startAngle, endAngle) ].join(" "); |
| 1554 | } |
| 1555 | |
| 1556 | function MakeArc(arcStart) { |
| 1557 | return "M" + arcStart; |
| 1558 | } |
| 1559 | |
| 1560 | function MakeWedge(center, arcStart) { |
| 1561 | return ["M", center.x, center.y, "L", arcStart, "z"].join(" "); |
| 1562 | } |
| 1563 | |
| 1564 | function Animate(path, now, dur) { |
| 1565 | if (path.start == null) { |
| 1566 | path.start = now; |
| 1567 | // console.log("start=" + now); |
| 1568 | } |
| 1569 | if (now - path.start < dur) { |
| 1570 | requestAnimationFrame(path.animate); |
| 1571 | return true; |
| 1572 | } |
| 1573 | return false; |
| 1574 | } |
| 1575 | |
| 1576 | function AnimateCircle(now) { |
| 1577 | if (circle.start == null) { |
| 1578 | circleFill.setAttribute("fill-opacity", "0.3"); |
| 1579 | } |
| 1580 | var dur = 2 * 1000; |
| 1581 | var animating = Animate(circle, now, dur); |
| 1582 | // console.log("now=" + now + "circle.start=" + circle.start ) |
| 1583 | var pathStr = ArcStart(circle.center, circle.radius, 0, (now - circle.start) / (dur / 359.9)); |
| 1584 | |
| 1585 | circle.setAttribute("d", MakeArc(pathStr)); |
| 1586 | circleFill.setAttribute("d", MakeWedge(circle.center, pathStr)); |
| 1587 | if (!animating) { |
| 1588 | var delay = dur - (now - circle.start); |
| 1589 | setTimeout(CircleFinal, delay); |
| 1590 | } |
| 1591 | } |
| 1592 | |
| 1593 | function CircleFinal() { |
| 1594 | var firstHalf = ArcStart(circle.center, circle.radius, 0, 180); |
| 1595 | var secondHalf = ArcStr(circle.center, circle.radius, 180, 360); |
| 1596 | circle.setAttribute("d", "M" + firstHalf + secondHalf + "z"); |
| 1597 | circleFill.setAttribute("d", "M" + firstHalf + secondHalf + "z"); |
| 1598 | } |
| 1599 | |
| 1600 | var svgNS = "http://www.w3.org/2000/svg"; |
| 1601 | |
| 1602 | function CreateTextLabels() |
| 1603 | { |
| 1604 | for (var i = 0; i < 32; ++i) { |
| 1605 | var text = document.createElementNS(svgNS, "text"); |
| 1606 | var pt = AngleToPt(circle.center, circle.radius + 80, i * 360 / 32); |
| 1607 | text.setAttribute("id", "t" + i); |
| 1608 | text.setAttribute("x", pt.x); |
| 1609 | text.setAttribute("y", pt.y); |
| 1610 | text.setAttribute("text-anchor", "middle"); |
| 1611 | text.setAttribute("alignment-baseline", "mathematical"); |
| 1612 | var textNode = document.createTextNode(i); |
| 1613 | text.appendChild(textNode); |
| 1614 | document.getElementById("svg").appendChild(text); |
| 1615 | } |
| 1616 | } |
| 1617 | |
| 1618 | // CreateTextLabels(); |
| 1619 | |
| 1620 | var keyframeArray = [ |
| 1621 | AnimateSpanWedge, |
| 1622 | AnimateTrivialWedge, |
| 1623 | AnimateSectorDesc, |
| 1624 | AnimateLineSingle, |
| 1625 | AnimateCurveMultiple, |
| 1626 | AnimateOneDLines, |
| 1627 | AnimateDiverging, |
| 1628 | ]; |
| 1629 | |
| 1630 | var keyframeIndex = 3; // keyframeArray.length - 1; // normally 0 ; set to debug a particular frame |
| 1631 | |
| 1632 | function QueueKeyframe() { |
| 1633 | QueueAnimation(keyframeArray[keyframeIndex]); |
| 1634 | if (keyframeIndex < keyframeArray.length - 1) { |
| 1635 | ++keyframeIndex; |
| 1636 | } |
| 1637 | } |
| 1638 | |
| 1639 | var grads; |
| 1640 | var paths; |
| 1641 | var canvas; |
| 1642 | var ctx; |
| 1643 | |
| 1644 | function canvasSetup() { |
| 1645 | canvas = document.getElementById("canvas"); |
| 1646 | ctx = canvas ? canvas.getContext("2d") : null; |
| 1647 | assert(ctx); |
| 1648 | var resScale = animationState.resScale = window.devicePixelRatio ? window.devicePixelRatio : 1; |
| 1649 | var unscaledWidth = canvas.width; |
| 1650 | var unscaledHeight = canvas.height; |
| 1651 | canvas.width = unscaledWidth * resScale; |
| 1652 | canvas.height = unscaledHeight * resScale; |
| 1653 | canvas.style.width = unscaledWidth + 'px'; |
| 1654 | canvas.style.height = unscaledHeight + 'px'; |
| 1655 | if (resScale != 1) { |
| 1656 | ctx.scale(resScale, resScale); |
| 1657 | } |
| 1658 | |
| 1659 | grads = CanvasGrads(ctx); |
| 1660 | paths = CanvasPaths(ctx); |
| 1661 | } |
| 1662 | |
| 1663 | function Onload() { |
| 1664 | canvasSetup(); |
| 1665 | var startBtn = document.getElementById('startBtn'); |
| 1666 | var stopBtn = document.getElementById('stopBtn'); |
| 1667 | var resetBtn = document.getElementById('resetBtn'); |
| 1668 | |
| 1669 | startBtn.addEventListener('click', function(e) { |
| 1670 | e.preventDefault(); |
| 1671 | e.srcElement.innerText = "Next"; |
| 1672 | CancelAnimation(); |
| 1673 | QueueKeyframe(); |
| 1674 | }); |
| 1675 | |
| 1676 | stopBtn.addEventListener('click', function(e) { |
| 1677 | e.preventDefault(); |
| 1678 | |
| 1679 | if (!animationState.paused) { |
| 1680 | PauseAnimation(); |
| 1681 | e.srcElement.innerText = "Resume"; |
| 1682 | } else { |
| 1683 | UnpauseAnimation(); |
| 1684 | e.srcElement.innerText = "Pause"; |
| 1685 | } |
| 1686 | }); |
| 1687 | |
| 1688 | resetBtn.addEventListener('click', function(e) { |
| 1689 | e.preventDefault(); |
| 1690 | CancelAnimation(); |
| 1691 | keyframeIndex = 0; |
| 1692 | startBtn.innerText = "Start"; |
| 1693 | QueueKeyframe(); |
| 1694 | }); |
| 1695 | } |
| 1696 | |
| 1697 | </script> |
| 1698 | |
| 1699 | </head> |
| 1700 | |
| 1701 | <body onLoad="Onload()"> |
| 1702 | |
| 1703 | <div class="controls"> |
| 1704 | <button type="button" id="startBtn">Start</button> |
| 1705 | <button type="button" id="stopBtn">Pause</button> |
| 1706 | <button type="button" id="resetBtn">Restart</button> |
| 1707 | </div> |
| 1708 | |
| 1709 | <canvas id="canvas" width="800" height="500" /> |
| 1710 | |
| 1711 | </body> |
| 1712 | </html> |