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