blob: 65868cdcfdc693642e869bd75710a8c258930fab [file] [log] [blame]
caryclarkdac1d172014-06-17 05:15:38 -07001<!DOCTYPE html>
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002
3<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
4<head>
5 <meta charset="utf-8" />
6 <title></title>
7<div style="height:0">
caryclark1049f122015-04-20 08:31:59 -07008
caryclark55888e42016-07-18 10:01:36 -07009<div id="angle">
10{{{2, 6, 1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f}}}
11{{{1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f, 2, 6}}}
12{{{1.995156049728393555, 5.980457782745361328}, {2.08147298604749853, 5.917692615073925744}, {2.164281118403629023, 5.850987095630077128}, {2.242042064666748047, 5.780299663543701172}}}
13{{{1.995156049728393555, 5.980457782745361328}, {1.82665117196054072, 6.185735619599722845}, {1.80264212281170999, 5.19703332512428684}, {1.994958639144897461, 5.979661464691162109}}}
14{{{1.995156049728393555, 5.980457782745361328}, {1.825196881732315868, 6.187507280789372288}, {1.801190554235020613, 5.204762216940081565}, {2, 6}}}
caryclarked0935a2015-10-22 07:23:52 -070015</div>
16
caryclark55888e42016-07-18 10:01:36 -070017<div id="ref">
18{{{0.7153972983360290527, 4.712343692779541016}, {0.2472269223126296878, 4.55502436068874772}, {-0.1220090791716240131, 4.244018092892478222}, {0, 4}}},
19{{{0.7153972983360290527, 4.712343692779541016}, {0.1339736781445877156, 4.133975051508096854}, {0.7320473976783675729, 3.63397630116081638}, {2, 3}}},
20{{fX=-0.0012699038296868359 fY=-0.0012605104293301750 } {fX=-0.0025337575085910835 fY=-0.0025229424048465177 }
caryclark65f55312014-11-13 06:58:52 -080021</div>
22
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000023</div>
24
25<script type="text/javascript">
26
caryclark54359292015-03-26 07:52:43 -070027var testDivs = [
caryclark55888e42016-07-18 10:01:36 -070028 angle,
29 ref,
caryclark1049f122015-04-20 08:31:59 -070030];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000031
caryclark54359292015-03-26 07:52:43 -070032 var decimal_places = 3;
33
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000034 var tests = [];
35 var testTitles = [];
36 var testIndex = 0;
37 var ctx;
caryclark54359292015-03-26 07:52:43 -070038
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000039 var subscale = 1;
40 var xmin, xmax, ymin, ymax;
41 var scale;
42 var initScale;
43 var mouseX, mouseY;
44 var mouseDown = false;
45 var srcLeft, srcTop;
46 var screenWidth, screenHeight;
47 var drawnPts;
48 var curveT = 0;
caryclark1049f122015-04-20 08:31:59 -070049 var curveW = -1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000050
51 var lastX, lastY;
52 var activeCurve = [];
53 var activePt;
caryclark54359292015-03-26 07:52:43 -070054 var ids = [];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000055
caryclark54359292015-03-26 07:52:43 -070056 var focus_on_selection = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000057 var draw_t = false;
caryclark1049f122015-04-20 08:31:59 -070058 var draw_w = false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000059 var draw_closest_t = false;
caryclarkfa6d6562014-07-29 12:13:28 -070060 var draw_cubic_red = false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000061 var draw_derivative = false;
caryclark54359292015-03-26 07:52:43 -070062 var draw_endpoints = 2;
caryclark1049f122015-04-20 08:31:59 -070063 var draw_id = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000064 var draw_midpoint = 0;
65 var draw_mouse_xy = false;
66 var draw_order = false;
67 var draw_point_xy = false;
68 var draw_ray_intersect = false;
69 var draw_quarterpoint = 0;
70 var draw_tangents = 1;
71 var draw_sortpoint = 0;
72 var retina_scale = !!window.devicePixelRatio;
73
74 function parse(test, title) {
75 var curveStrs = test.split("{{");
76 var pattern = /-?\d+\.*\d*e?-?\d*/g;
77 var curves = [];
78 for (var c in curveStrs) {
79 var curveStr = curveStrs[c];
caryclark54359292015-03-26 07:52:43 -070080 var idPart = curveStr.split("id=");
81 var id = -1;
82 if (idPart.length == 2) {
83 id = parseInt(idPart[1]);
84 curveStr = idPart[0];
85 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000086 var points = curveStr.match(pattern);
87 var pts = [];
88 for (var wd in points) {
89 var num = parseFloat(points[wd]);
90 if (isNaN(num)) continue;
91 pts.push(num);
92 }
caryclark54359292015-03-26 07:52:43 -070093 if (pts.length > 2) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000094 curves.push(pts);
caryclark54359292015-03-26 07:52:43 -070095 }
96 if (id >= 0) {
97 ids.push(id);
98 ids.push(pts);
99 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000100 }
101 if (curves.length >= 1) {
102 tests.push(curves);
103 testTitles.push(title);
104 }
105 }
106
107 function init(test) {
108 var canvas = document.getElementById('canvas');
109 if (!canvas.getContext) return;
110 ctx = canvas.getContext('2d');
111 var resScale = retina_scale && window.devicePixelRatio ? window.devicePixelRatio : 1;
112 var unscaledWidth = window.innerWidth - 20;
113 var unscaledHeight = window.innerHeight - 20;
114 screenWidth = unscaledWidth;
115 screenHeight = unscaledHeight;
116 canvas.width = unscaledWidth * resScale;
117 canvas.height = unscaledHeight * resScale;
118 canvas.style.width = unscaledWidth + 'px';
119 canvas.style.height = unscaledHeight + 'px';
120 if (resScale != 1) {
121 ctx.scale(resScale, resScale);
122 }
123 xmin = Infinity;
124 xmax = -Infinity;
125 ymin = Infinity;
126 ymax = -Infinity;
127 for (var curves in test) {
128 var curve = test[curves];
caryclark1049f122015-04-20 08:31:59 -0700129 var last = curve.length - (curve.length % 2 == 1 ? 1 : 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000130 for (var idx = 0; idx < last; idx += 2) {
131 xmin = Math.min(xmin, curve[idx]);
132 xmax = Math.max(xmax, curve[idx]);
133 ymin = Math.min(ymin, curve[idx + 1]);
134 ymax = Math.max(ymax, curve[idx + 1]);
135 }
136 }
caryclark54359292015-03-26 07:52:43 -0700137 xmin -= Math.min(1, Math.max(xmax - xmin, ymax - ymin));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000138 var testW = xmax - xmin;
139 var testH = ymax - ymin;
140 subscale = 1;
141 while (testW * subscale < 0.1 && testH * subscale < 0.1) {
142 subscale *= 10;
143 }
144 while (testW * subscale > 10 && testH * subscale > 10) {
145 subscale /= 10;
146 }
147 setScale(xmin, xmax, ymin, ymax);
148 mouseX = (screenWidth / 2) / scale + srcLeft;
149 mouseY = (screenHeight / 2) / scale + srcTop;
150 initScale = scale;
151 }
152
153 function setScale(x0, x1, y0, y1) {
154 var srcWidth = x1 - x0;
155 var srcHeight = y1 - y0;
156 var usableWidth = screenWidth;
157 var xDigits = Math.ceil(Math.log(Math.abs(xmax)) / Math.log(10));
158 var yDigits = Math.ceil(Math.log(Math.abs(ymax)) / Math.log(10));
159 usableWidth -= (xDigits + yDigits) * 10;
160 usableWidth -= decimal_places * 10;
161 var hscale = usableWidth / srcWidth;
162 var vscale = screenHeight / srcHeight;
163 scale = Math.min(hscale, vscale);
164 var invScale = 1 / scale;
165 var sxmin = x0 - invScale * 5;
166 var symin = y0 - invScale * 10;
167 var sxmax = x1 + invScale * (6 * decimal_places + 10);
168 var symax = y1 + invScale * 10;
169 srcWidth = sxmax - sxmin;
170 srcHeight = symax - symin;
171 hscale = usableWidth / srcWidth;
172 vscale = screenHeight / srcHeight;
173 scale = Math.min(hscale, vscale);
174 srcLeft = sxmin;
175 srcTop = symin;
176 }
177
178function dxy_at_t(curve, t) {
179 var dxy = {};
180 if (curve.length == 6) {
181 var a = t - 1;
182 var b = 1 - 2 * t;
183 var c = t;
184 dxy.x = a * curve[0] + b * curve[2] + c * curve[4];
185 dxy.y = a * curve[1] + b * curve[3] + c * curve[5];
caryclark1049f122015-04-20 08:31:59 -0700186 } else if (curve.length == 7) {
187 var p20x = curve[4] - curve[0];
188 var p20y = curve[5] - curve[1];
189 var p10xw = (curve[2] - curve[0]) * curve[6];
190 var p10yw = (curve[3] - curve[1]) * curve[6];
191 var coeff0x = curve[6] * p20x - p20x;
192 var coeff0y = curve[6] * p20y - p20y;
193 var coeff1x = p20x - 2 * p10xw;
194 var coeff1y = p20y - 2 * p10yw;
195 dxy.x = t * (t * coeff0x + coeff1x) + p10xw;
196 dxy.y = t * (t * coeff0y + coeff1y) + p10yw;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000197 } else if (curve.length == 8) {
198 var one_t = 1 - t;
199 var a = curve[0];
200 var b = curve[2];
201 var c = curve[4];
202 var d = curve[6];
203 dxy.x = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
204 a = curve[1];
205 b = curve[3];
206 c = curve[5];
207 d = curve[7];
208 dxy.y = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
209 }
210 return dxy;
211}
212
213 var flt_epsilon = 1.19209290E-07;
214
215 function approximately_zero(A) {
216 return Math.abs(A) < flt_epsilon;
217 }
218
219 function approximately_zero_inverse(A) {
220 return Math.abs(A) > (1 / flt_epsilon);
221 }
222
223 function quad_real_roots(A, B, C) {
224 var s = [];
225 var p = B / (2 * A);
226 var q = C / A;
227 if (approximately_zero(A) && (approximately_zero_inverse(p)
228 || approximately_zero_inverse(q))) {
229 if (approximately_zero(B)) {
230 if (C == 0) {
231 s[0] = 0;
232 }
233 return s;
234 }
235 s[0] = -C / B;
236 return s;
237 }
238 /* normal form: x^2 + px + q = 0 */
239 var p2 = p * p;
240 if (!approximately_zero(p2 - q) && p2 < q) {
241 return s;
242 }
243 var sqrt_D = 0;
244 if (p2 > q) {
245 sqrt_D = Math.sqrt(p2 - q);
246 }
247 s[0] = sqrt_D - p;
248 var flip = -sqrt_D - p;
249 if (!approximately_zero(s[0] - flip)) {
250 s[1] = flip;
251 }
252 return s;
253 }
254
255 function cubic_real_roots(A, B, C, D) {
256 if (approximately_zero(A)) { // we're just a quadratic
257 return quad_real_roots(B, C, D);
258 }
259 if (approximately_zero(D)) { // 0 is one root
260 var s = quad_real_roots(A, B, C);
261 for (var i = 0; i < s.length; ++i) {
262 if (approximately_zero(s[i])) {
263 return s;
264 }
265 }
266 s.push(0);
267 return s;
268 }
269 if (approximately_zero(A + B + C + D)) { // 1 is one root
270 var s = quad_real_roots(A, A + B, -D);
271 for (var i = 0; i < s.length; ++i) {
272 if (approximately_zero(s[i] - 1)) {
273 return s;
274 }
275 }
276 s.push(1);
277 return s;
278 }
279 var a, b, c;
280 var invA = 1 / A;
281 a = B * invA;
282 b = C * invA;
283 c = D * invA;
284 var a2 = a * a;
285 var Q = (a2 - b * 3) / 9;
286 var R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
287 var R2 = R * R;
288 var Q3 = Q * Q * Q;
289 var R2MinusQ3 = R2 - Q3;
290 var adiv3 = a / 3;
291 var r;
292 var roots = [];
293 if (R2MinusQ3 < 0) { // we have 3 real roots
294 var theta = Math.acos(R / Math.sqrt(Q3));
295 var neg2RootQ = -2 * Math.sqrt(Q);
296 r = neg2RootQ * Math.cos(theta / 3) - adiv3;
297 roots.push(r);
298 r = neg2RootQ * Math.cos((theta + 2 * Math.PI) / 3) - adiv3;
299 if (!approximately_zero(roots[0] - r)) {
300 roots.push(r);
301 }
302 r = neg2RootQ * Math.cos((theta - 2 * Math.PI) / 3) - adiv3;
303 if (!approximately_zero(roots[0] - r) && (roots.length == 1
304 || !approximately_zero(roots[1] - r))) {
305 roots.push(r);
306 }
307 } else { // we have 1 real root
308 var sqrtR2MinusQ3 = Math.sqrt(R2MinusQ3);
309 var A = Math.abs(R) + sqrtR2MinusQ3;
310 A = Math.pow(A, 1/3);
311 if (R > 0) {
312 A = -A;
313 }
314 if (A != 0) {
315 A += Q / A;
316 }
317 r = A - adiv3;
318 roots.push(r);
319 if (approximately_zero(R2 - Q3)) {
320 r = -A / 2 - adiv3;
caryclark1049f122015-04-20 08:31:59 -0700321 if (!approximately_zero(roots[0] - r)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000322 roots.push(r);
323 }
324 }
325 }
326 return roots;
327 }
328
329 function approximately_zero_or_more(tValue) {
330 return tValue >= -flt_epsilon;
331 }
332
333 function approximately_one_or_less(tValue) {
334 return tValue <= 1 + flt_epsilon;
335 }
336
337 function approximately_less_than_zero(tValue) {
338 return tValue < flt_epsilon;
339 }
340
341 function approximately_greater_than_one(tValue) {
342 return tValue > 1 - flt_epsilon;
343 }
344
345 function add_valid_ts(s) {
346 var t = [];
347 nextRoot:
348 for (var index = 0; index < s.length; ++index) {
349 var tValue = s[index];
350 if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
351 if (approximately_less_than_zero(tValue)) {
352 tValue = 0;
353 } else if (approximately_greater_than_one(tValue)) {
354 tValue = 1;
355 }
356 for (var idx2 = 0; idx2 < t.length; ++idx2) {
357 if (approximately_zero(t[idx2] - tValue)) {
358 continue nextRoot;
359 }
360 }
361 t.push(tValue);
362 }
363 }
364 return t;
365 }
366
367 function quad_roots(A, B, C) {
368 var s = quad_real_roots(A, B, C);
369 var foundRoots = add_valid_ts(s);
370 return foundRoots;
371 }
372
373 function cubic_roots(A, B, C, D) {
374 var s = cubic_real_roots(A, B, C, D);
375 var foundRoots = add_valid_ts(s);
376 return foundRoots;
377 }
378
379 function ray_curve_intersect(startPt, endPt, curve) {
380 var adj = endPt[0] - startPt[0];
381 var opp = endPt[1] - startPt[1];
382 var r = [];
caryclark1049f122015-04-20 08:31:59 -0700383 var len = (curve.length == 7 ? 6 : curve.length) / 2;
384 for (var n = 0; n < len; ++n) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000385 r[n] = (curve[n * 2 + 1] - startPt[1]) * adj - (curve[n * 2] - startPt[0]) * opp;
386 }
387 if (curve.length == 6) {
388 var A = r[2];
389 var B = r[1];
390 var C = r[0];
391 A += C - 2 * B; // A = a - 2*b + c
392 B -= C; // B = -(b - c)
393 return quad_roots(A, 2 * B, C);
394 }
caryclark1049f122015-04-20 08:31:59 -0700395 if (curve.length == 7) {
396 var A = r[2];
397 var B = r[1] * curve[6];
398 var C = r[0];
399 A += C - 2 * B; // A = a - 2*b + c
400 B -= C; // B = -(b - c)
401 return quad_roots(A, 2 * B, C);
402 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000403 var A = r[3]; // d
404 var B = r[2] * 3; // 3*c
405 var C = r[1] * 3; // 3*b
406 var D = r[0]; // a
407 A -= D - C + B; // A = -a + 3*b - 3*c + d
408 B += 3 * D - 2 * C; // B = 3*a - 6*b + 3*c
409 C -= 3 * D; // C = -3*a + 3*b
410 return cubic_roots(A, B, C, D);
411 }
412
413 function x_at_t(curve, t) {
414 var one_t = 1 - t;
415 if (curve.length == 4) {
416 return one_t * curve[0] + t * curve[2];
417 }
418 var one_t2 = one_t * one_t;
419 var t2 = t * t;
420 if (curve.length == 6) {
421 return one_t2 * curve[0] + 2 * one_t * t * curve[2] + t2 * curve[4];
422 }
caryclark1049f122015-04-20 08:31:59 -0700423 if (curve.length == 7) {
424 var numer = one_t2 * curve[0] + 2 * one_t * t * curve[2] * curve[6]
425 + t2 * curve[4];
426 var denom = one_t2 + 2 * one_t * t * curve[6]
427 + t2;
428 return numer / denom;
429 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000430 var a = one_t2 * one_t;
431 var b = 3 * one_t2 * t;
432 var c = 3 * one_t * t2;
433 var d = t2 * t;
434 return a * curve[0] + b * curve[2] + c * curve[4] + d * curve[6];
435 }
436
437 function y_at_t(curve, t) {
438 var one_t = 1 - t;
439 if (curve.length == 4) {
440 return one_t * curve[1] + t * curve[3];
441 }
442 var one_t2 = one_t * one_t;
443 var t2 = t * t;
444 if (curve.length == 6) {
445 return one_t2 * curve[1] + 2 * one_t * t * curve[3] + t2 * curve[5];
446 }
caryclark1049f122015-04-20 08:31:59 -0700447 if (curve.length == 7) {
448 var numer = one_t2 * curve[1] + 2 * one_t * t * curve[3] * curve[6]
449 + t2 * curve[5];
450 var denom = one_t2 + 2 * one_t * t * curve[6]
451 + t2;
452 return numer / denom;
453 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000454 var a = one_t2 * one_t;
455 var b = 3 * one_t2 * t;
456 var c = 3 * one_t * t2;
457 var d = t2 * t;
458 return a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
459 }
460
461 function drawPointAtT(curve) {
462 var x = x_at_t(curve, curveT);
463 var y = y_at_t(curve, curveT);
caryclark55888e42016-07-18 10:01:36 -0700464 drawPoint(x, y, false);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000465 }
466
467 function drawLine(x1, y1, x2, y2) {
468 ctx.beginPath();
469 ctx.moveTo((x1 - srcLeft) * scale,
470 (y1 - srcTop) * scale);
471 ctx.lineTo((x2 - srcLeft) * scale,
472 (y2 - srcTop) * scale);
473 ctx.stroke();
474 }
475
caryclark55888e42016-07-18 10:01:36 -0700476 function drawPoint(px, py, xend) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000477 for (var pts = 0; pts < drawnPts.length; pts += 2) {
478 var x = drawnPts[pts];
479 var y = drawnPts[pts + 1];
480 if (px == x && py == y) {
481 return;
482 }
483 }
484 drawnPts.push(px);
485 drawnPts.push(py);
486 var _px = (px - srcLeft) * scale;
487 var _py = (py - srcTop) * scale;
488 ctx.beginPath();
caryclark55888e42016-07-18 10:01:36 -0700489 if (xend) {
490 ctx.moveTo(_px - 3, _py - 3);
491 ctx.lineTo(_px + 3, _py + 3);
492 ctx.moveTo(_px - 3, _py + 3);
493 ctx.lineTo(_px + 3, _py - 3);
494 } else {
495 ctx.arc(_px, _py, 3, 0, Math.PI * 2, true);
496 ctx.closePath();
497 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000498 ctx.stroke();
499 if (draw_point_xy) {
500 var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
501 ctx.font = "normal 10px Arial";
502 ctx.textAlign = "left";
503 ctx.fillStyle = "black";
504 ctx.fillText(label, _px + 5, _py);
505 }
506 }
507
508 function drawPointSolid(px, py) {
caryclark55888e42016-07-18 10:01:36 -0700509 drawPoint(px, py, false);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000510 ctx.fillStyle = "rgba(0,0,0, 0.4)";
511 ctx.fill();
512 }
513
514 function crossPt(origin, pt1, pt2) {
515 return ((pt1[0] - origin[0]) * (pt2[1] - origin[1])
516 - (pt1[1] - origin[1]) * (pt2[0] - origin[0])) > 0 ? 0 : 1;
517 }
518
519 // may not work well for cubics
520 function curveClosestT(curve, x, y) {
521 var closest = -1;
522 var closestDist = Infinity;
523 var l = Infinity, t = Infinity, r = -Infinity, b = -Infinity;
524 for (var i = 0; i < 16; ++i) {
525 var testX = x_at_t(curve, i / 16);
526 l = Math.min(testX, l);
527 r = Math.max(testX, r);
528 var testY = y_at_t(curve, i / 16);
529 t = Math.min(testY, t);
530 b = Math.max(testY, b);
531 var dx = testX - x;
532 var dy = testY - y;
533 var dist = dx * dx + dy * dy;
534 if (closestDist > dist) {
535 closestDist = dist;
536 closest = i;
537 }
538 }
539 var boundsX = r - l;
540 var boundsY = b - t;
541 var boundsDist = boundsX * boundsX + boundsY * boundsY;
542 if (closestDist > boundsDist) {
543 return -1;
544 }
545 console.log("closestDist = " + closestDist + " boundsDist = " + boundsDist
546 + " t = " + closest / 16);
547 return closest / 16;
548 }
549
caryclark1049f122015-04-20 08:31:59 -0700550 var kMaxConicToQuadPOW2 = 5;
551
552 function computeQuadPOW2(curve, tol) {
553 var a = curve[6] - 1;
554 var k = a / (4 * (2 + a));
555 var x = k * (curve[0] - 2 * curve[2] + curve[4]);
556 var y = k * (curve[1] - 2 * curve[3] + curve[5]);
557
558 var error = Math.sqrt(x * x + y * y);
559 var pow2;
560 for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
561 if (error <= tol) {
562 break;
563 }
564 error *= 0.25;
565 }
566 return pow2;
567 }
568
569 function subdivide_w_value(w) {
570 return Math.sqrt(0.5 + w * 0.5);
571 }
572
573 function chop(curve, part1, part2) {
574 var w = curve[6];
575 var scale = 1 / (1 + w);
576 part1[0] = curve[0];
577 part1[1] = curve[1];
578 part1[2] = (curve[0] + curve[2] * w) * scale;
579 part1[3] = (curve[1] + curve[3] * w) * scale;
580 part1[4] = part2[0] = (curve[0] + (curve[2] * w) * 2 + curve[4]) * scale * 0.5;
581 part1[5] = part2[1] = (curve[1] + (curve[3] * w) * 2 + curve[5]) * scale * 0.5;
582 part2[2] = (curve[2] * w + curve[4]) * scale;
583 part2[3] = (curve[3] * w + curve[5]) * scale;
584 part2[4] = curve[4];
585 part2[5] = curve[5];
586 part1[6] = part2[6] = subdivide_w_value(w);
587 }
588
589 function subdivide(curve, level, pts) {
590 if (0 == level) {
591 pts.push(curve[2]);
592 pts.push(curve[3]);
593 pts.push(curve[4]);
594 pts.push(curve[5]);
595 } else {
596 var part1 = [], part2 = [];
597 chop(curve, part1, part2);
598 --level;
599 subdivide(part1, level, pts);
600 subdivide(part2, level, pts);
601 }
602 }
603
604 function chopIntoQuadsPOW2(curve, pow2, pts) {
605 subdivide(curve, pow2, pts);
606 return 1 << pow2;
607 }
608
609 function drawConic(curve, srcLeft, srcTop, scale) {
610 var tol = 1 / scale;
611 var pow2 = computeQuadPOW2(curve, tol);
612 var pts = [];
613 chopIntoQuadsPOW2(curve, pow2, pts);
614 for (var i = 0; i < pts.length; i += 4) {
615 ctx.quadraticCurveTo(
616 (pts[i + 0] - srcLeft) * scale, (pts[i + 1] - srcTop) * scale,
617 (pts[i + 2] - srcLeft) * scale, (pts[i + 3] - srcTop) * scale);
618 }
619 }
620
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000621 function draw(test, title) {
622 ctx.font = "normal 50px Arial";
623 ctx.textAlign = "left";
624 ctx.fillStyle = "rgba(0,0,0, 0.1)";
625 ctx.fillText(title, 50, 50);
626 ctx.font = "normal 10px Arial";
627 // ctx.lineWidth = "1.001"; "0.999";
628 var hullStarts = [];
629 var hullEnds = [];
630 var midSpokes = [];
631 var midDist = [];
632 var origin = [];
633 var shortSpokes = [];
634 var shortDist = [];
635 var sweeps = [];
636 drawnPts = [];
637 for (var curves in test) {
638 var curve = test[curves];
639 origin.push(curve[0]);
640 origin.push(curve[1]);
641 var startPt = [];
642 startPt.push(curve[2]);
643 startPt.push(curve[3]);
644 hullStarts.push(startPt);
645 var endPt = [];
646 if (curve.length == 4) {
647 endPt.push(curve[2]);
648 endPt.push(curve[3]);
caryclark1049f122015-04-20 08:31:59 -0700649 } else if (curve.length == 6 || curve.length == 7) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000650 endPt.push(curve[4]);
651 endPt.push(curve[5]);
652 } else if (curve.length == 8) {
653 endPt.push(curve[6]);
654 endPt.push(curve[7]);
655 }
656 hullEnds.push(endPt);
657 var sweep = crossPt(origin, startPt, endPt);
658 sweeps.push(sweep);
659 var midPt = [];
660 midPt.push(x_at_t(curve, 0.5));
661 midPt.push(y_at_t(curve, 0.5));
662 midSpokes.push(midPt);
663 var shortPt = [];
664 shortPt.push(x_at_t(curve, 0.25));
665 shortPt.push(y_at_t(curve, 0.25));
666 shortSpokes.push(shortPt);
667 var dx = midPt[0] - origin[0];
668 var dy = midPt[1] - origin[1];
669 var dist = Math.sqrt(dx * dx + dy * dy);
670 midDist.push(dist);
671 dx = shortPt[0] - origin[0];
672 dy = shortPt[1] - origin[1];
673 dist = Math.sqrt(dx * dx + dy * dy);
674 shortDist.push(dist);
675 }
676 var intersect = [];
677 var useIntersect = false;
678 var maxWidth = Math.max(xmax - xmin, ymax - ymin);
679 for (var curves in test) {
680 var curve = test[curves];
caryclark1049f122015-04-20 08:31:59 -0700681 if (curve.length >= 6 && curve.length <= 8) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000682 var opp = curves == 0 || curves == 1 ? 0 : 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000683 var sects = ray_curve_intersect(origin, hullEnds[opp], curve);
684 intersect.push(sects);
685 if (sects.length > 1) {
686 var intersection = sects[0];
687 if (intersection == 0) {
688 intersection = sects[1];
689 }
690 var ix = x_at_t(curve, intersection) - origin[0];
691 var iy = y_at_t(curve, intersection) - origin[1];
692 var ex = hullEnds[opp][0] - origin[0];
693 var ey = hullEnds[opp][1] - origin[1];
694 if (ix * ex >= 0 && iy * ey >= 0) {
695 var iDist = Math.sqrt(ix * ix + iy * iy);
696 var eDist = Math.sqrt(ex * ex + ey * ey);
697 var delta = Math.abs(iDist - eDist) / maxWidth;
caryclark1049f122015-04-20 08:31:59 -0700698 if (delta > (curve.length != 8 ? 1e-5 : 1e-4)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000699 useIntersect ^= true;
700 }
701 }
702 }
703 }
704 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000705 var midLeft = curves != 0 ? crossPt(origin, midSpokes[0], midSpokes[1]) : 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000706 var firstInside;
707 if (useIntersect) {
708 var sect1 = intersect[0].length > 1;
709 var sIndex = sect1 ? 0 : 1;
710 var sects = intersect[sIndex];
711 var intersection = sects[0];
712 if (intersection == 0) {
713 intersection = sects[1];
714 }
715 var curve = test[sIndex];
716 var ix = x_at_t(curve, intersection) - origin[0];
717 var iy = y_at_t(curve, intersection) - origin[1];
718 var opp = sect1 ? 1 : 0;
719 var ex = hullEnds[opp][0] - origin[0];
720 var ey = hullEnds[opp][1] - origin[1];
721 var iDist = ix * ix + iy * iy;
722 var eDist = ex * ex + ey * ey;
723 firstInside = (iDist > eDist) ^ (sIndex == 0) ^ sweeps[0];
724// console.log("iDist=" + iDist + " eDist=" + eDist + " sIndex=" + sIndex
725 // + " sweeps[0]=" + sweeps[0]);
726 } else {
727 // console.log("midLeft=" + midLeft);
728 firstInside = midLeft != 0;
729 }
730 var shorter = midDist[1] < midDist[0];
731 var shortLeft = shorter ? crossPt(origin, shortSpokes[0], midSpokes[1])
732 : crossPt(origin, midSpokes[0], shortSpokes[1]);
733 var startCross = crossPt(origin, hullStarts[0], hullStarts[1]);
734 var disallowShort = midLeft == startCross && midLeft == sweeps[0]
735 && midLeft == sweeps[1];
736
737 // console.log("midLeft=" + midLeft + " startCross=" + startCross);
738 var intersectIndex = 0;
739 for (var curves in test) {
caryclark1049f122015-04-20 08:31:59 -0700740 var curve = test[draw_id != 2 ? curves : test.length - curves - 1];
741 if (curve.length != 4 && curve.length != 6 && curve.length != 7 && curve.length != 8) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000742 continue;
743 }
744 ctx.lineWidth = 1;
745 if (draw_tangents != 0) {
caryclarkfa6d6562014-07-29 12:13:28 -0700746 if (draw_cubic_red ? curve.length == 8 : firstInside == curves) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000747 ctx.strokeStyle = "rgba(255,0,0, 0.3)";
748 } else {
749 ctx.strokeStyle = "rgba(0,0,255, 0.3)";
750 }
751 drawLine(curve[0], curve[1], curve[2], curve[3]);
752 if (draw_tangents != 2) {
753 if (curve.length > 4) drawLine(curve[2], curve[3], curve[4], curve[5]);
caryclark1049f122015-04-20 08:31:59 -0700754 if (curve.length == 8) drawLine(curve[4], curve[5], curve[6], curve[7]);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000755 }
756 if (draw_tangents != 1) {
caryclark1049f122015-04-20 08:31:59 -0700757 if (curve.length == 6 || curve.length == 7) {
758 drawLine(curve[0], curve[1], curve[4], curve[5]);
759 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000760 if (curve.length == 8) drawLine(curve[0], curve[1], curve[6], curve[7]);
761 }
762 }
763 ctx.beginPath();
764 ctx.moveTo((curve[0] - srcLeft) * scale, (curve[1] - srcTop) * scale);
765 if (curve.length == 4) {
766 ctx.lineTo((curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale);
767 } else if (curve.length == 6) {
768 ctx.quadraticCurveTo(
769 (curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale,
770 (curve[4] - srcLeft) * scale, (curve[5] - srcTop) * scale);
caryclark1049f122015-04-20 08:31:59 -0700771 } else if (curve.length == 7) {
772 drawConic(curve, srcLeft, srcTop, scale);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000773 } else {
774 ctx.bezierCurveTo(
775 (curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale,
776 (curve[4] - srcLeft) * scale, (curve[5] - srcTop) * scale,
777 (curve[6] - srcLeft) * scale, (curve[7] - srcTop) * scale);
778 }
caryclarkfa6d6562014-07-29 12:13:28 -0700779 if (draw_cubic_red ? curve.length == 8 : firstInside == curves) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000780 ctx.strokeStyle = "rgba(255,0,0, 1)";
781 } else {
782 ctx.strokeStyle = "rgba(0,0,255, 1)";
783 }
784 ctx.stroke();
caryclark54359292015-03-26 07:52:43 -0700785 if (draw_endpoints > 0) {
caryclark55888e42016-07-18 10:01:36 -0700786 drawPoint(curve[0], curve[1], false);
caryclark54359292015-03-26 07:52:43 -0700787 if (draw_endpoints > 1 || curve.length == 4) {
caryclark55888e42016-07-18 10:01:36 -0700788 drawPoint(curve[2], curve[3], curve.length == 4 && draw_endpoints == 3);
caryclark54359292015-03-26 07:52:43 -0700789 }
caryclark1049f122015-04-20 08:31:59 -0700790 if (curve.length == 6 || curve.length == 7 ||
791 (draw_endpoints > 1 && curve.length == 8)) {
caryclark55888e42016-07-18 10:01:36 -0700792 drawPoint(curve[4], curve[5], (curve.length == 6 || curve.length == 7) && draw_endpoints == 3);
caryclark54359292015-03-26 07:52:43 -0700793 }
caryclark55888e42016-07-18 10:01:36 -0700794 if (curve.length == 8) {
795 drawPoint(curve[6], curve[7], curve.length == 8 && draw_endpoints == 3);
796 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000797 }
798 if (draw_midpoint != 0) {
799 if ((curves == 0) == (midLeft == 0)) {
800 ctx.strokeStyle = "rgba(0,180,127, 0.6)";
801 } else {
802 ctx.strokeStyle = "rgba(127,0,127, 0.6)";
803 }
804 var midX = x_at_t(curve, 0.5);
805 var midY = y_at_t(curve, 0.5);
806 drawPointSolid(midX, midY);
807 if (draw_midpoint > 1) {
808 drawLine(curve[0], curve[1], midX, midY);
809 }
810 }
811 if (draw_quarterpoint != 0) {
812 if ((curves == 0) == (shortLeft == 0)) {
813 ctx.strokeStyle = "rgba(0,191,63, 0.6)";
814 } else {
815 ctx.strokeStyle = "rgba(63,0,191, 0.6)";
816 }
817 var midT = (curves == 0) == shorter ? 0.25 : 0.5;
818 var midX = x_at_t(curve, midT);
819 var midY = y_at_t(curve, midT);
820 drawPointSolid(midX, midY);
821 if (draw_quarterpoint > 1) {
822 drawLine(curve[0], curve[1], midX, midY);
823 }
824 }
825 if (draw_sortpoint != 0) {
826 if ((curves == 0) == ((disallowShort == -1 ? midLeft : shortLeft) == 0)) {
827 ctx.strokeStyle = "rgba(0,155,37, 0.6)";
828 } else {
829 ctx.strokeStyle = "rgba(37,0,155, 0.6)";
830 }
831 var midT = (curves == 0) == shorter && disallowShort != curves ? 0.25 : 0.5;
832 console.log("curves=" + curves + " disallowShort=" + disallowShort
833 + " midLeft=" + midLeft + " shortLeft=" + shortLeft
834 + " shorter=" + shorter + " midT=" + midT);
835 var midX = x_at_t(curve, midT);
836 var midY = y_at_t(curve, midT);
837 drawPointSolid(midX, midY);
838 if (draw_sortpoint > 1) {
839 drawLine(curve[0], curve[1], midX, midY);
840 }
841 }
842 if (draw_ray_intersect != 0) {
843 ctx.strokeStyle = "rgba(75,45,199, 0.6)";
caryclark1049f122015-04-20 08:31:59 -0700844 if (curve.length >= 6 && curve.length <= 8) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000845 var intersections = intersect[intersectIndex];
846 for (var i in intersections) {
847 var intersection = intersections[i];
848 var x = x_at_t(curve, intersection);
849 var y = y_at_t(curve, intersection);
850 drawPointSolid(x, y);
851 if (draw_ray_intersect > 1) {
852 drawLine(curve[0], curve[1], x, y);
853 }
854 }
855 }
856 ++intersectIndex;
857 }
858 if (draw_order) {
859 var px = x_at_t(curve, 0.75);
860 var py = y_at_t(curve, 0.75);
861 var _px = (px - srcLeft) * scale;
862 var _py = (py - srcTop) * scale;
863 ctx.beginPath();
864 ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
865 ctx.closePath();
866 ctx.fillStyle = "white";
867 ctx.fill();
caryclarkfa6d6562014-07-29 12:13:28 -0700868 if (draw_cubic_red ? curve.length == 8 : firstInside == curves) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000869 ctx.strokeStyle = "rgba(255,0,0, 1)";
870 ctx.fillStyle = "rgba(255,0,0, 1)";
871 } else {
872 ctx.strokeStyle = "rgba(0,0,255, 1)";
873 ctx.fillStyle = "rgba(0,0,255, 1)";
874 }
875 ctx.stroke();
876 ctx.font = "normal 16px Arial";
877 ctx.textAlign = "center";
878 ctx.fillText(parseInt(curves) + 1, _px, _py + 5);
879 }
880 if (draw_closest_t) {
881 var t = curveClosestT(curve, mouseX, mouseY);
882 if (t >= 0) {
883 var x = x_at_t(curve, t);
884 var y = y_at_t(curve, t);
885 drawPointSolid(x, y);
886 }
887 }
888 if (!approximately_zero(scale - initScale)) {
889 ctx.font = "normal 20px Arial";
890 ctx.fillStyle = "rgba(0,0,0, 0.3)";
891 ctx.textAlign = "right";
892 ctx.fillText(scale.toFixed(decimal_places) + 'x',
893 screenWidth - 10, screenHeight - 5);
894 }
895 if (draw_t) {
896 drawPointAtT(curve);
897 }
caryclark1049f122015-04-20 08:31:59 -0700898 if (draw_id != 0) {
caryclark54359292015-03-26 07:52:43 -0700899 var id = -1;
900 for (var i = 0; i < ids.length; i += 2) {
901 if (ids[i + 1] == curve) {
902 id = ids[i];
903 break;
904 }
905 }
906 if (id >= 0) {
907 var px = x_at_t(curve, 0.5);
908 var py = y_at_t(curve, 0.5);
909 var _px = (px - srcLeft) * scale;
910 var _py = (py - srcTop) * scale;
911 ctx.beginPath();
912 ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
913 ctx.closePath();
914 ctx.fillStyle = "white";
915 ctx.fill();
916 ctx.strokeStyle = "rgba(255,0,0, 1)";
917 ctx.fillStyle = "rgba(255,0,0, 1)";
918 ctx.stroke();
919 ctx.font = "normal 16px Arial";
920 ctx.textAlign = "center";
921 ctx.fillText(id, _px, _py + 5);
922 }
923 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000924 }
925 if (draw_t) {
926 drawCurveTControl();
927 }
caryclark1049f122015-04-20 08:31:59 -0700928 if (draw_w) {
929 drawCurveWControl();
930 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000931 }
932
933 function drawCurveTControl() {
934 ctx.lineWidth = 2;
935 ctx.strokeStyle = "rgba(0,0,0, 0.3)";
936 ctx.beginPath();
937 ctx.rect(screenWidth - 80, 40, 28, screenHeight - 80);
938 ctx.stroke();
939 var ty = 40 + curveT * (screenHeight - 80);
940 ctx.beginPath();
941 ctx.moveTo(screenWidth - 80, ty);
942 ctx.lineTo(screenWidth - 85, ty - 5);
943 ctx.lineTo(screenWidth - 85, ty + 5);
944 ctx.lineTo(screenWidth - 80, ty);
945 ctx.fillStyle = "rgba(0,0,0, 0.6)";
946 ctx.fill();
947 var num = curveT.toFixed(decimal_places);
948 ctx.font = "normal 10px Arial";
949 ctx.textAlign = "left";
950 ctx.fillText(num, screenWidth - 78, ty);
951 }
952
caryclark1049f122015-04-20 08:31:59 -0700953 function drawCurveWControl() {
954 var w = -1;
955 var choice = 0;
956 for (var curves in tests[testIndex]) {
957 var curve = tests[testIndex][curves];
958 if (curve.length != 7) {
959 continue;
960 }
961 if (choice == curveW) {
962 w = curve[6];
963 break;
964 }
965 ++choice;
966 }
967 if (w < 0) {
968 return;
969 }
970 ctx.lineWidth = 2;
971 ctx.strokeStyle = "rgba(0,0,0, 0.3)";
972 ctx.beginPath();
973 ctx.rect(screenWidth - 40, 40, 28, screenHeight - 80);
974 ctx.stroke();
975 var ty = 40 + w * (screenHeight - 80);
976 ctx.beginPath();
977 ctx.moveTo(screenWidth - 40, ty);
978 ctx.lineTo(screenWidth - 45, ty - 5);
979 ctx.lineTo(screenWidth - 45, ty + 5);
980 ctx.lineTo(screenWidth - 40, ty);
981 ctx.fillStyle = "rgba(0,0,0, 0.6)";
982 ctx.fill();
983 var num = w.toFixed(decimal_places);
984 ctx.font = "normal 10px Arial";
985 ctx.textAlign = "left";
986 ctx.fillText(num, screenWidth - 38, ty);
987 }
988
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000989 function ptInTControl() {
990 var e = window.event;
991 var tgt = e.target || e.srcElement;
992 var left = tgt.offsetLeft;
993 var top = tgt.offsetTop;
994 var x = (e.clientX - left);
995 var y = (e.clientY - top);
996 if (x < screenWidth - 80 || x > screenWidth - 50) {
997 return false;
998 }
999 if (y < 40 || y > screenHeight - 80) {
1000 return false;
1001 }
1002 curveT = (y - 40) / (screenHeight - 120);
1003 if (curveT < 0 || curveT > 1) {
1004 throw "stop execution";
1005 }
1006 return true;
1007 }
1008
caryclark1049f122015-04-20 08:31:59 -07001009 function ptInWControl() {
1010 var e = window.event;
1011 var tgt = e.target || e.srcElement;
1012 var left = tgt.offsetLeft;
1013 var top = tgt.offsetTop;
1014 var x = (e.clientX - left);
1015 var y = (e.clientY - top);
1016 if (x < screenWidth - 40 || x > screenWidth - 10) {
1017 return false;
1018 }
1019 if (y < 40 || y > screenHeight - 80) {
1020 return false;
1021 }
1022 var w = (y - 40) / (screenHeight - 120);
1023 if (w < 0 || w > 1) {
1024 throw "stop execution";
1025 }
1026 var choice = 0;
1027 for (var curves in tests[testIndex]) {
1028 var curve = tests[testIndex][curves];
1029 if (curve.length != 7) {
1030 continue;
1031 }
1032 if (choice == curveW) {
1033 curve[6] = w;
1034 break;
1035 }
1036 ++choice;
1037 }
1038 return true;
1039 }
1040
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001041 function drawTop() {
1042 init(tests[testIndex]);
1043 redraw();
1044 }
1045
1046 function redraw() {
caryclark54359292015-03-26 07:52:43 -07001047 if (focus_on_selection > 0) {
1048 var focusXmin = focusYmin = Infinity;
1049 var focusXmax = focusYmax = -Infinity;
1050 var choice = 0;
1051 for (var curves in tests[testIndex]) {
1052 if (++choice != focus_on_selection) {
1053 continue;
1054 }
1055 var curve = tests[testIndex][curves];
caryclark1049f122015-04-20 08:31:59 -07001056 var last = curve.length - (curve.length % 2 == 1 ? 1 : 0);
caryclark54359292015-03-26 07:52:43 -07001057 for (var idx = 0; idx < last; idx += 2) {
1058 focusXmin = Math.min(focusXmin, curve[idx]);
1059 focusXmax = Math.max(focusXmax, curve[idx]);
1060 focusYmin = Math.min(focusYmin, curve[idx + 1]);
1061 focusYmax = Math.max(focusYmax, curve[idx + 1]);
1062 }
1063 }
1064 focusXmin -= Math.min(1, Math.max(focusXmax - focusXmin, focusYmax - focusYmin));
1065 if (focusXmin < focusXmax && focusYmin < focusYmax) {
1066 setScale(focusXmin, focusXmax, focusYmin, focusYmax);
1067 }
1068 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001069 ctx.beginPath();
1070 ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
1071 ctx.fillStyle = "white";
1072 ctx.fill();
1073 draw(tests[testIndex], testTitles[testIndex]);
1074 }
1075
1076 function doKeyPress(evt) {
1077 var char = String.fromCharCode(evt.charCode);
caryclark54359292015-03-26 07:52:43 -07001078 var focusWasOn = false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001079 switch (char) {
1080 case '0':
1081 case '1':
1082 case '2':
1083 case '3':
1084 case '4':
1085 case '5':
1086 case '6':
1087 case '7':
1088 case '8':
1089 case '9':
1090 decimal_places = char - '0';
1091 redraw();
1092 break;
1093 case '-':
caryclark54359292015-03-26 07:52:43 -07001094 focusWasOn = focus_on_selection;
1095 if (focusWasOn) {
1096 focus_on_selection = false;
1097 scale /= 1.2;
1098 } else {
1099 scale /= 2;
1100 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001101 calcLeftTop();
1102 redraw();
caryclark54359292015-03-26 07:52:43 -07001103 focus_on_selection = focusWasOn;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001104 break;
1105 case '=':
1106 case '+':
caryclark54359292015-03-26 07:52:43 -07001107 focusWasOn = focus_on_selection;
1108 if (focusWasOn) {
1109 focus_on_selection = false;
1110 scale *= 1.2;
1111 } else {
1112 scale *= 2;
1113 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001114 calcLeftTop();
1115 redraw();
caryclark54359292015-03-26 07:52:43 -07001116 focus_on_selection = focusWasOn;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001117 break;
caryclarkfa6d6562014-07-29 12:13:28 -07001118 case 'b':
1119 draw_cubic_red ^= true;
1120 redraw();
1121 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001122 case 'c':
1123 drawTop();
1124 break;
1125 case 'd':
1126 var test = tests[testIndex];
1127 var testClone = [];
1128 for (var curves in test) {
1129 var c = test[curves];
1130 var cClone = [];
1131 for (var index = 0; index < c.length; ++index) {
1132 cClone.push(c[index]);
1133 }
1134 testClone.push(cClone);
1135 }
1136 tests.push(testClone);
1137 testTitles.push(testTitles[testIndex] + " copy");
1138 testIndex = tests.length - 1;
1139 redraw();
1140 break;
1141 case 'e':
caryclark55888e42016-07-18 10:01:36 -07001142 draw_endpoints = (draw_endpoints + 1) % 4;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001143 redraw();
1144 break;
1145 case 'f':
1146 draw_derivative ^= true;
1147 redraw();
1148 break;
1149 case 'i':
1150 draw_ray_intersect = (draw_ray_intersect + 1) % 3;
1151 redraw();
1152 break;
1153 case 'l':
1154 var test = tests[testIndex];
1155 console.log("<div id=\"" + testTitles[testIndex] + "\" >");
1156 for (var curves in test) {
1157 var c = test[curves];
1158 var s = "{{";
1159 for (var i = 0; i < c.length; i += 2) {
1160 s += "{";
1161 s += c[i] + "," + c[i + 1];
1162 s += "}";
1163 if (i + 2 < c.length) {
1164 s += ", ";
1165 }
1166 }
1167 console.log(s + "}},");
1168 }
1169 console.log("</div>");
1170 break;
1171 case 'm':
1172 draw_midpoint = (draw_midpoint + 1) % 3;
1173 redraw();
1174 break;
1175 case 'N':
1176 testIndex += 9;
1177 case 'n':
1178 testIndex = (testIndex + 1) % tests.length;
1179 drawTop();
1180 break;
1181 case 'o':
1182 draw_order ^= true;
1183 redraw();
1184 break;
1185 case 'P':
1186 testIndex -= 9;
1187 case 'p':
1188 if (--testIndex < 0)
1189 testIndex = tests.length - 1;
1190 drawTop();
1191 break;
1192 case 'q':
1193 draw_quarterpoint = (draw_quarterpoint + 1) % 3;
1194 redraw();
1195 break;
1196 case 'r':
1197 for (var i = 0; i < testDivs.length; ++i) {
1198 var title = testDivs[i].id.toString();
1199 if (title == testTitles[testIndex]) {
1200 var str = testDivs[i].firstChild.data;
1201 parse(str, title);
1202 var original = tests.pop();
1203 testTitles.pop();
1204 tests[testIndex] = original;
1205 break;
1206 }
1207 }
1208 redraw();
1209 break;
1210 case 's':
1211 draw_sortpoint = (draw_sortpoint + 1) % 3;
1212 redraw();
1213 break;
1214 case 't':
1215 draw_t ^= true;
1216 redraw();
1217 break;
1218 case 'u':
1219 draw_closest_t ^= true;
1220 redraw();
1221 break;
1222 case 'v':
1223 draw_tangents = (draw_tangents + 1) % 4;
1224 redraw();
1225 break;
caryclark1049f122015-04-20 08:31:59 -07001226 case 'w':
1227 ++curveW;
1228 var choice = 0;
1229 draw_w = false;
1230 for (var curves in tests[testIndex]) {
1231 var curve = tests[testIndex][curves];
1232 if (curve.length != 7) {
1233 continue;
1234 }
1235 if (choice == curveW) {
1236 draw_w = true;
1237 break;
1238 }
1239 ++choice;
1240 }
1241 if (!draw_w) {
1242 curveW = -1;
1243 }
1244 redraw();
1245 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001246 case 'x':
1247 draw_point_xy ^= true;
1248 redraw();
1249 break;
1250 case 'y':
1251 draw_mouse_xy ^= true;
1252 redraw();
1253 break;
1254 case '\\':
1255 retina_scale ^= true;
1256 drawTop();
1257 break;
caryclark54359292015-03-26 07:52:43 -07001258 case '`':
1259 ++focus_on_selection;
1260 if (focus_on_selection >= tests[testIndex].length) {
1261 focus_on_selection = 0;
1262 }
1263 setScale(xmin, xmax, ymin, ymax);
1264 redraw();
1265 break;
1266 case '.':
caryclark1049f122015-04-20 08:31:59 -07001267 draw_id = (draw_id + 1) % 3;
caryclark54359292015-03-26 07:52:43 -07001268 redraw();
1269 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001270 }
1271 }
1272
1273 function doKeyDown(evt) {
1274 var char = evt.keyCode;
1275 var preventDefault = false;
1276 switch (char) {
1277 case 37: // left arrow
1278 if (evt.shiftKey) {
1279 testIndex -= 9;
1280 }
1281 if (--testIndex < 0)
1282 testIndex = tests.length - 1;
caryclark54359292015-03-26 07:52:43 -07001283 if (evt.ctrlKey) {
1284 redraw();
1285 } else {
1286 drawTop();
1287 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001288 preventDefault = true;
1289 break;
1290 case 39: // right arrow
1291 if (evt.shiftKey) {
1292 testIndex += 9;
1293 }
1294 if (++testIndex >= tests.length)
1295 testIndex = 0;
caryclark54359292015-03-26 07:52:43 -07001296 if (evt.ctrlKey) {
1297 redraw();
1298 } else {
1299 drawTop();
1300 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001301 preventDefault = true;
1302 break;
1303 }
1304 if (preventDefault) {
1305 evt.preventDefault();
1306 return false;
1307 }
1308 return true;
1309 }
1310
1311 function calcXY() {
1312 var e = window.event;
1313 var tgt = e.target || e.srcElement;
1314 var left = tgt.offsetLeft;
1315 var top = tgt.offsetTop;
1316 mouseX = (e.clientX - left) / scale + srcLeft;
1317 mouseY = (e.clientY - top) / scale + srcTop;
1318 }
1319
1320 function calcLeftTop() {
1321 srcLeft = mouseX - screenWidth / 2 / scale;
1322 srcTop = mouseY - screenHeight / 2 / scale;
1323 }
1324
1325 function handleMouseClick() {
caryclark1049f122015-04-20 08:31:59 -07001326 if ((!draw_t || !ptInTControl()) && (!draw_w || !ptInWControl())) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001327 calcXY();
1328 } else {
1329 redraw();
1330 }
1331 }
1332
1333 function initDown() {
1334 var test = tests[testIndex];
1335 var bestDistance = 1000000;
1336 activePt = -1;
1337 for (var curves in test) {
1338 var testCurve = test[curves];
caryclark1049f122015-04-20 08:31:59 -07001339 if (testCurve.length != 4 && (testCurve.length < 6 || testCurve.length > 8)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001340 continue;
1341 }
caryclark1049f122015-04-20 08:31:59 -07001342 var testMax = testCurve.length == 7 ? 6 : testCurve.length;
1343 for (var i = 0; i < testMax; i += 2) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001344 var testX = testCurve[i];
1345 var testY = testCurve[i + 1];
1346 var dx = testX - mouseX;
1347 var dy = testY - mouseY;
1348 var dist = dx * dx + dy * dy;
1349 if (dist > bestDistance) {
1350 continue;
1351 }
1352 activeCurve = testCurve;
1353 activePt = i;
1354 bestDistance = dist;
1355 }
1356 }
1357 if (activePt >= 0) {
1358 lastX = mouseX;
1359 lastY = mouseY;
1360 }
1361 }
1362
1363 function handleMouseOver() {
1364 calcXY();
1365 if (draw_mouse_xy) {
1366 var num = mouseX.toFixed(decimal_places) + ", " + mouseY.toFixed(decimal_places);
1367 ctx.beginPath();
1368 ctx.rect(300, 100, num.length * 6, 10);
1369 ctx.fillStyle = "white";
1370 ctx.fill();
1371 ctx.font = "normal 10px Arial";
1372 ctx.fillStyle = "black";
1373 ctx.textAlign = "left";
1374 ctx.fillText(num, 300, 108);
1375 }
1376 if (!mouseDown) {
1377 activePt = -1;
1378 return;
1379 }
1380 if (activePt < 0) {
1381 initDown();
1382 return;
1383 }
1384 var deltaX = mouseX - lastX;
1385 var deltaY = mouseY - lastY;
1386 lastX = mouseX;
1387 lastY = mouseY;
1388 if (activePt == 0) {
1389 var test = tests[testIndex];
1390 for (var curves in test) {
1391 var testCurve = test[curves];
1392 testCurve[0] += deltaX;
1393 testCurve[1] += deltaY;
1394 }
1395 } else {
1396 activeCurve[activePt] += deltaX;
1397 activeCurve[activePt + 1] += deltaY;
1398 }
1399 redraw();
1400 }
1401
1402 function start() {
1403 for (var i = 0; i < testDivs.length; ++i) {
1404 var title = testDivs[i].id.toString();
1405 var str = testDivs[i].firstChild.data;
1406 parse(str, title);
1407 }
1408 drawTop();
1409 window.addEventListener('keypress', doKeyPress, true);
1410 window.addEventListener('keydown', doKeyDown, true);
1411 window.onresize = function () {
1412 drawTop();
1413 }
1414 }
1415
1416</script>
1417</head>
1418
1419<body onLoad="start();">
1420
1421<canvas id="canvas" width="750" height="500"
1422 onmousedown="mouseDown = true"
1423 onmouseup="mouseDown = false"
1424 onmousemove="handleMouseOver()"
1425 onclick="handleMouseClick()"
1426 ></canvas >
1427</body>
1428</html>