blob: 9872b3868235a07aa52ec15a3cf71fd8bfa86ac0 [file] [log] [blame]
caryclarkdac1d172014-06-17 05:15:38 -07001<html>
2<head>
3<div height="0" hidden="true">
caryclarkdac1d172014-06-17 05:15:38 -07004
caryclarke4097e32014-06-18 07:24:19 -07005<div id="skpwww_argus_presse_fr_41">
6 RunTestSet [skpwww_argus_presse_fr_41]
caryclarkdac1d172014-06-17 05:15:38 -07007
caryclarke4097e32014-06-18 07:24:19 -07008{{1000,343}, {165,343}},
9{{165,343}, {165,364.869873}},
10{{165,364.869873}, {1000,364.869873}},
11{{1000,364.869873}, {1000,343}},
12op intersect
13{{165,343.000031}, {1000,343.000031}},
14{{1000,343.000031}, {1000,364.869904}},
15{{1000,364.869904}, {165,364.869904}},
16{{165,364.869904}, {165,343.000031}},
17debugShowLineIntersection wtTs[0]=0 {{165,343}, {165,364.869873}} {{165,343}} wnTs[0]=1 {{1000,343}, {165,343}}
18debugShowLineIntersection wtTs[0]=1 {{1000,364.869873}, {1000,343}} {{1000,343}} wnTs[0]=0 {{1000,343}, {165,343}}
19debugShowLineIntersection wtTs[0]=0 {{165,364.869873}, {1000,364.869873}} {{165,364.869873}} wnTs[0]=1 {{165,343}, {165,364.869873}}
20debugShowLineIntersection wtTs[0]=0 {{1000,364.869873}, {1000,343}} {{1000,364.869873}} wnTs[0]=1 {{165,364.869873}, {1000,364.869873}}
21debugShowLineIntersection wtTs[0]=0 {{165,343.000031}, {1000,343.000031}} {{165,343}} wtTs[1]=1 {{1000,343}} wnTs[0]=1 {{1000,343}, {165,343}} wnTs[1]=0
22debugShowLineIntersection wtTs[0]=0 {{1000,343.000031}, {1000,364.869904}} {{1000,343.000031}} wnTs[0]=0 {{1000,343}, {165,343}}
23debugShowLineIntersection wtTs[0]=1 {{165,364.869904}, {165,343.000031}} {{165,343.000031}} wnTs[0]=1 {{1000,343}, {165,343}}
24debugShowLineIntersection wtTs[0]=0 {{165,343.000031}, {1000,343.000031}} {{165,343}} wnTs[0]=0 {{165,343}, {165,364.869873}}
25debugShowLineIntersection wtTs[0]=1 {{1000,364.869904}, {165,364.869904}} {{165,364.869873}} wnTs[0]=1 {{165,343}, {165,364.869873}}
26debugShowLineIntersection wtTs[0]=0 {{165,364.869904}, {165,343.000031}} {{165,364.869904}} wtTs[1]=1 {{165,343.000031}} wnTs[0]=1 {{165,343}, {165,364.869873}} wnTs[1]=1.39541634e-006
27debugShowLineIntersection wtTs[0]=1 {{1000,343.000031}, {1000,364.869904}} {{1000,364.869904}} wnTs[0]=1 {{165,364.869873}, {1000,364.869873}}
28debugShowLineIntersection wtTs[0]=0 {{1000,364.869904}, {165,364.869904}} {{1000,364.869873}} wtTs[1]=1 {{165,364.869873}} wnTs[0]=1 {{165,364.869873}, {1000,364.869873}} wnTs[1]=0
29debugShowLineIntersection wtTs[0]=0 {{165,364.869904}, {165,343.000031}} {{165,364.869904}} wnTs[0]=0 {{165,364.869873}, {1000,364.869873}}
30debugShowLineIntersection wtTs[0]=1 {{165,343.000031}, {1000,343.000031}} {{1000,343}} wnTs[0]=1 {{1000,364.869873}, {1000,343}}
31debugShowLineIntersection wtTs[0]=0 {{1000,343.000031}, {1000,364.869904}} {{1000,343.000031}} wtTs[1]=1 {{1000,364.869904}} wnTs[0]=0.999999 {{1000,364.869873}, {1000,343}} wnTs[1]=0
32debugShowLineIntersection wtTs[0]=0 {{1000,364.869904}, {165,364.869904}} {{1000,364.869873}} wnTs[0]=0 {{1000,364.869873}, {1000,343}}
33debugShowLineIntersection wtTs[0]=0 {{1000,343.000031}, {1000,364.869904}} {{1000,343.000031}} wnTs[0]=1 {{165,343.000031}, {1000,343.000031}}
34debugShowLineIntersection wtTs[0]=1 {{165,364.869904}, {165,343.000031}} {{165,343.000031}} wnTs[0]=0 {{165,343.000031}, {1000,343.000031}}
35debugShowLineIntersection wtTs[0]=0 {{1000,364.869904}, {165,364.869904}} {{1000,364.869904}} wnTs[0]=1 {{1000,343.000031}, {1000,364.869904}}
36debugShowLineIntersection wtTs[0]=0 {{165,364.869904}, {165,343.000031}} {{165,364.869904}} wnTs[0]=1 {{1000,364.869904}, {165,364.869904}}
37SkOpSegment::debugShowTs - id=0 [o=3,5 t=0 1000,343.000031 w=1 o=0] [o=7,1 t=1 165,343 w=1 o=0]
38SkOpSegment::debugShowTs o id=4 [o=7,1 t=0 165,343 w=1 o=0] [o=3,5 t=1 1000,343.000031 w=1 o=0] operand
39SkOpSegment::debugShowTs + id=0 [o=3,5 t=0 1000,343.000031 w=1 o=0] [o=7,1 t=1 165,343 w=1 o=0]
40SkOpSegment::debugShowTs o id=4 [o=7,1 t=0 165,343 w=1 o=0] [o=3,5 t=1 1000,343.000031 w=1 o=0] operand
41SkOpSegment::debugShowTs - id=1 [o=4,0 t=0 165,343 w=1 o=0] [o=6,2 t=1 165,364.869873 w=1 o=0]
42SkOpSegment::debugShowTs o id=7 [o=6,2 t=0 165,364.869904 w=1 o=0] [o=4,0 t=1 165,343.000031 w=1 o=0] operand
43SkOpSegment::addTPair addTPair this=1 1.39541634e-006 other=7 1
44SkOpSegment::addTPair addTPair this=7 0 other=1 1
45SkOpSegment::debugShowTs + id=1 [o=4,0 t=0 165,343 w=1 o=0] [o=7 t=1.4e-006 165,343.000031 w=1 o=0] [o=7,6,2 t=1 165,364.869873 w=1 o=0]
46SkOpSegment::debugShowTs o id=7 [o=1,6,2 t=0 165,364.869904 w=1 o=0] [o=1,4,0 t=1 165,343.000031 w=1 o=0] operand
47SkOpSegment::debugShowTs - id=2 [o=1,7 t=0 165,364.869904 w=1 o=0] [o=5,3 t=1 1000,364.869873 w=1 o=0]
48SkOpSegment::debugShowTs o id=6 [o=5,3 t=0 1000,364.869873 w=1 o=0] [o=1,7 t=1 165,364.869904 w=1 o=0] operand
49SkOpSegment::debugShowTs + id=2 [o=1,7 t=0 165,364.869904 w=1 o=0] [o=5,3 t=1 1000,364.869873 w=1 o=0]
50SkOpSegment::debugShowTs o id=6 [o=5,3 t=0 1000,364.869873 w=1 o=0] [o=1,7 t=1 165,364.869904 w=1 o=0] operand
51SkOpSegment::debugShowTs - id=3 [o=6,2 t=0 1000,364.869873 w=1 o=0] [o=4,0 t=1 1000,343 w=1 o=0]
52SkOpSegment::debugShowTs o id=5 [o=4,0 t=0 1000,343.000031 w=1 o=0] [o=6,2 t=1 1000,364.869904 w=1 o=0] operand
53SkOpSegment::addTPair addTPair this=3 0 other=5 1
54SkOpSegment::addTPair addTPair this=5 0 other=3 0.999998605
55SkOpSegment::debugShowTs + id=3 [o=6,2,5 t=0 1000,364.869904 w=1 o=0] [o=5 t=1 1000,343.000031 w=1 o=0] [o=4,0 t=1 1000,343 w=1 o=0]
56SkOpSegment::debugShowTs o id=5 [o=3,4,0 t=0 1000,343.000031 w=1 o=0] [o=3,6,2 t=1 1000,364.869904 w=1 o=0] operand
57SkOpContour::calcCoincidentWinding count=4
58SkOpSegment::debugShowTs p id=0 [o=3,5 t=0 1000,343.000031 w=1 o=-1] [o=7,1 t=1 165,343 w=1 o=0]
59SkOpSegment::debugShowTs o id=4 [o=7,1 t=0 165,343 w=0 o=0] [o=3,5 t=1 1000,343.000031 w=1 o=0] operand done
60SkOpSegment::debugShowTs p id=1 [o=4,0 t=0 165,343 w=1 o=0] [o=7 t=1.4e-006 165,343.000031 w=1 o=-1] [o=7,6,2 t=1 165,364.869873 w=1 o=0]
61SkOpSegment::debugShowTs o id=7 [o=1,6,2 t=0 165,364.869904 w=0 o=0] [o=1,4,0 t=1 165,343.000031 w=1 o=0] operand done
62SkOpSegment::debugShowTs p id=2 [o=1,7 t=0 165,364.869904 w=1 o=-1] [o=5,3 t=1 1000,364.869873 w=1 o=0]
63SkOpSegment::debugShowTs o id=6 [o=5,3 t=0 1000,364.869873 w=0 o=0] [o=1,7 t=1 165,364.869904 w=1 o=0] operand done
64SkOpSegment::debugShowTs p id=3 [o=6,2,5 t=0 1000,364.869904 w=1 o=-1] [o=5 t=1 1000,343.000031 w=1 o=0] [o=4,0 t=1 1000,343 w=1 o=0]
65SkOpSegment::debugShowTs o id=5 [o=3,4,0 t=0 1000,343.000031 w=0 o=0] [o=3,6,2 t=1 1000,364.869904 w=1 o=0] operand done
66SkOpSegment::addTPair addTPair this=0 0 other=4 1
67SkOpSegment::addTPair addTPair this=0 1 other=4 0
68SkOpSegment::addTPair addTPair this=6 1 other=2 0
69SkOpSegment::addTPair addTPair duplicate this=2 0 other=6 1
70SkOpSegment::addTPair addTPair this=2 1 other=6 0
71SkOpContour::joinCoincidence count=4
72SkOpSegment::sortAngles [0] tStart=0 [0]
73SkOpSegment::sortAngles [0] tStart=1 [5]
74SkOpSegment::sortAngles [1] tStart=1.39541634e-006 [2]
75SkOpSegment::sortAngles [1] tStart=1 [5]
76SkOpSegment::sortAngles [2] tStart=1 [5]
77SkOpSegment::sortAngles [3] tStart=0.999998605 [3]
78SkOpSegment::debugShowActiveSpans id=0 (1000,343 165,343) t=0 (1000,343) tEnd=1 other=3 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=-1
79SkOpSegment::debugShowActiveSpans id=1 (165,343 165,364.869873) t=1.39541634e-006 (165,343.000031) tEnd=1 other=7 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=-1
80SkOpSegment::debugShowActiveSpans id=2 (165,364.869873 1000,364.869873) t=0 (165,364.869873) tEnd=1 other=6 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=-1
81SkOpSegment::debugShowActiveSpans id=3 (1000,364.869873 1000,343) t=0 (1000,364.869873) tEnd=0.999998605 other=6 otherT=0 otherIndex=2 windSum=? windValue=1 oppValue=-1
82Assemble
caryclarkdac1d172014-06-17 05:15:38 -070083
caryclarkdac1d172014-06-17 05:15:38 -070084</div>
85
86</div>
87
88<script type="text/javascript">
89
90var testDivs = [
caryclarke4097e32014-06-18 07:24:19 -070091 skpwww_argus_presse_fr_41,
caryclarkdac1d172014-06-17 05:15:38 -070092];
93
94var decimal_places = 3; // make this 3 to show more precision
95
96var tests = [];
97var testLines = [];
98var testTitles = [];
99var testIndex = 0;
100var ctx;
101
102var xmin, xmax, focusXmin, focusXmax;
103var ymin, ymax, focusYmin, focusYmax;
104var scale;
105var mouseX, mouseY;
106var srcLeft, srcTop;
107var screenWidth, screenHeight;
108var drawnPts, drawnLines, drawnQuads, drawnCubics;
109var curveT = 0;
110
111var pt_labels = 2;
112var collect_bounds = false;
113var control_lines = 0;
114var curve_t = false;
115var debug_xy = 1;
116var focus_enabled = false;
117var focus_on_selection = false;
118var step_limit = 0;
119var draw_active = false;
120var draw_add = false;
121var draw_angle = 0;
122var draw_deriviatives = 0;
123var draw_hints = false;
124var draw_hodo = 0;
125var draw_id = false;
126var draw_intersection = 0;
127var draw_intersectT = false;
128var draw_legend = true;
129var draw_log = false;
130var draw_mark = false;
131var draw_midpoint = false;
132var draw_op = 0;
133var draw_sequence = false;
134var draw_sort = 0;
135var draw_path = 3;
136var draw_computed = 0;
137var retina_scale = !!window.devicePixelRatio;
138
139var activeCount = 0;
140var addCount = 0;
141var angleCount = 0;
142var opCount = 0;
143var sectCount = 0;
144var sortCount = 0;
145var markCount = 0;
146var activeMax = 0;
147var addMax = 0;
148var angleMax = 0;
149var sectMax = 0;
150var sectMax2 = 0;
151var sortMax = 0;
152var markMax = 0;
153var opMax = 0;
154var stepMax = 0;
155var lastIndex = 0;
156var hasPath = false;
157var hasComputedPath = false;
158
159var firstActiveSpan = -1;
160var logStart = -1;
161var logRange = 0;
162
163var SPAN_ID = 0;
164var SPAN_X1 = SPAN_ID + 1;
165var SPAN_Y1 = SPAN_X1 + 1;
166var SPAN_X2 = SPAN_Y1 + 1;
167var SPAN_Y2 = SPAN_X2 + 1;
168var SPAN_L_T = SPAN_Y2 + 1;
169var SPAN_L_TX = SPAN_L_T + 1;
170var SPAN_L_TY = SPAN_L_TX + 1;
171var SPAN_L_TEND = SPAN_L_TY + 1;
172var SPAN_L_OTHER = SPAN_L_TEND + 1;
173var SPAN_L_OTHERT = SPAN_L_OTHER + 1;
174var SPAN_L_OTHERI = SPAN_L_OTHERT + 1;
175var SPAN_L_SUM = SPAN_L_OTHERI + 1;
176var SPAN_L_VAL = SPAN_L_SUM + 1;
177var SPAN_L_OPP = SPAN_L_VAL + 1;
178
179var SPAN_X3 = SPAN_Y2 + 1;
180var SPAN_Y3 = SPAN_X3 + 1;
181var SPAN_Q_T = SPAN_Y3 + 1;
182var SPAN_Q_TX = SPAN_Q_T + 1;
183var SPAN_Q_TY = SPAN_Q_TX + 1;
184var SPAN_Q_TEND = SPAN_Q_TY + 1;
185var SPAN_Q_OTHER = SPAN_Q_TEND + 1;
186var SPAN_Q_OTHERT = SPAN_Q_OTHER + 1;
187var SPAN_Q_OTHERI = SPAN_Q_OTHERT + 1;
188var SPAN_Q_SUM = SPAN_Q_OTHERI + 1;
189var SPAN_Q_VAL = SPAN_Q_SUM + 1;
190var SPAN_Q_OPP = SPAN_Q_VAL + 1;
191
192var SPAN_X4 = SPAN_Y3 + 1;
193var SPAN_Y4 = SPAN_X4 + 1;
194var SPAN_C_T = SPAN_Y4 + 1;
195var SPAN_C_TX = SPAN_C_T + 1;
196var SPAN_C_TY = SPAN_C_TX + 1;
197var SPAN_C_TEND = SPAN_C_TY + 1;
198var SPAN_C_OTHER = SPAN_C_TEND + 1;
199var SPAN_C_OTHERT = SPAN_C_OTHER + 1;
200var SPAN_C_OTHERI = SPAN_C_OTHERT + 1;
201var SPAN_C_SUM = SPAN_C_OTHERI + 1;
202var SPAN_C_VAL = SPAN_C_SUM + 1;
203var SPAN_C_OPP = SPAN_C_VAL + 1;
204
205var ACTIVE_LINE_SPAN = 1;
206var ACTIVE_QUAD_SPAN = ACTIVE_LINE_SPAN + 1;
207var ACTIVE_CUBIC_SPAN = ACTIVE_QUAD_SPAN + 1;
208
209var ADD_MOVETO = ACTIVE_CUBIC_SPAN + 1;
210var ADD_LINETO = ADD_MOVETO + 1;
211var ADD_QUADTO = ADD_LINETO + 1;
212var ADD_CUBICTO = ADD_QUADTO + 1;
213var ADD_CLOSE = ADD_CUBICTO + 1;
214var ADD_FILL = ADD_CLOSE + 1;
215
216var PATH_LINE = ADD_FILL + 1;
217var PATH_QUAD = PATH_LINE + 1;
218var PATH_CUBIC = PATH_QUAD + 1;
219
220var INTERSECT_LINE = PATH_CUBIC + 1;
221var INTERSECT_LINE_2 = INTERSECT_LINE + 1;
222var INTERSECT_LINE_NO = INTERSECT_LINE_2 + 1;
223var INTERSECT_QUAD_LINE = INTERSECT_LINE_NO + 1;
224var INTERSECT_QUAD_LINE_2 = INTERSECT_QUAD_LINE + 1;
225var INTERSECT_QUAD_LINE_NO = INTERSECT_QUAD_LINE_2 + 1;
226var INTERSECT_QUAD = INTERSECT_QUAD_LINE_NO + 1;
227var INTERSECT_QUAD_2 = INTERSECT_QUAD + 1;
228var INTERSECT_QUAD_NO = INTERSECT_QUAD_2 + 1;
229var INTERSECT_SELF_CUBIC = INTERSECT_QUAD_NO + 1;
230var INTERSECT_SELF_CUBIC_NO = INTERSECT_SELF_CUBIC + 1;
231var INTERSECT_CUBIC_LINE = INTERSECT_SELF_CUBIC_NO + 1;
232var INTERSECT_CUBIC_LINE_2 = INTERSECT_CUBIC_LINE + 1;
233var INTERSECT_CUBIC_LINE_3 = INTERSECT_CUBIC_LINE_2 + 1;
234var INTERSECT_CUBIC_LINE_NO = INTERSECT_CUBIC_LINE_3 + 1;
235var INTERSECT_CUBIC_QUAD = INTERSECT_CUBIC_LINE_NO + 1;
236var INTERSECT_CUBIC_QUAD_2 = INTERSECT_CUBIC_QUAD + 1;
237var INTERSECT_CUBIC_QUAD_3 = INTERSECT_CUBIC_QUAD_2 + 1;
238var INTERSECT_CUBIC_QUAD_4 = INTERSECT_CUBIC_QUAD_3 + 1;
239var INTERSECT_CUBIC_QUAD_NO = INTERSECT_CUBIC_QUAD_4 + 1;
240var INTERSECT_CUBIC = INTERSECT_CUBIC_QUAD_NO + 1;
241var INTERSECT_CUBIC_2 = INTERSECT_CUBIC + 1;
242var INTERSECT_CUBIC_3 = INTERSECT_CUBIC_2 + 1;
243var INTERSECT_CUBIC_4 = INTERSECT_CUBIC_3 + 1;
244// FIXME: add cubic 5- 9
245var INTERSECT_CUBIC_NO = INTERSECT_CUBIC_4 + 1;
246
247var SORT_UNARY = INTERSECT_CUBIC_NO + 1;
248var SORT_BINARY = SORT_UNARY + 1;
249
250var OP_DIFFERENCE = SORT_BINARY + 1;
251var OP_INTERSECT = OP_DIFFERENCE + 1;
252var OP_UNION = OP_INTERSECT + 1;
253var OP_XOR = OP_UNION + 1;
254
255var MARK_LINE = OP_XOR + 1;
256var MARK_QUAD = MARK_LINE + 1;
257var MARK_CUBIC = MARK_QUAD + 1;
258var MARK_DONE_LINE = MARK_CUBIC + 1;
259var MARK_DONE_QUAD = MARK_DONE_LINE + 1;
260var MARK_DONE_CUBIC = MARK_DONE_QUAD + 1;
261var MARK_UNSORTABLE_LINE = MARK_DONE_CUBIC + 1;
262var MARK_UNSORTABLE_QUAD = MARK_UNSORTABLE_LINE + 1;
263var MARK_UNSORTABLE_CUBIC = MARK_UNSORTABLE_QUAD + 1;
264var MARK_SIMPLE_LINE = MARK_UNSORTABLE_CUBIC + 1;
265var MARK_SIMPLE_QUAD = MARK_SIMPLE_LINE + 1;
266var MARK_SIMPLE_CUBIC = MARK_SIMPLE_QUAD + 1;
267var MARK_SIMPLE_DONE_LINE = MARK_SIMPLE_CUBIC + 1;
268var MARK_SIMPLE_DONE_QUAD = MARK_SIMPLE_DONE_LINE + 1;
269var MARK_SIMPLE_DONE_CUBIC = MARK_SIMPLE_DONE_QUAD + 1;
270var MARK_DONE_UNARY_LINE = MARK_SIMPLE_DONE_CUBIC + 1;
271var MARK_DONE_UNARY_QUAD = MARK_DONE_UNARY_LINE + 1;
272var MARK_DONE_UNARY_CUBIC = MARK_DONE_UNARY_QUAD + 1;
273var MARK_ANGLE_LAST = MARK_DONE_UNARY_CUBIC + 1;
274
275var COMPUTED_SET_1 = MARK_ANGLE_LAST + 1;
276var COMPUTED_SET_2 = COMPUTED_SET_1 + 1;
277
278var ANGLE_AFTER = COMPUTED_SET_2;
279var ANGLE_AFTER2 = ANGLE_AFTER + 1;
280
281var ACTIVE_OP = ANGLE_AFTER2 + 1;
282
283var FRAG_TYPE_LAST = ACTIVE_OP;
284
285var REC_TYPE_UNKNOWN = -1;
286var REC_TYPE_PATH = 0;
287var REC_TYPE_SECT = 1;
288var REC_TYPE_ACTIVE = 2;
289var REC_TYPE_ADD = 3;
290var REC_TYPE_SORT = 4;
291var REC_TYPE_OP = 5;
292var REC_TYPE_MARK = 6;
293var REC_TYPE_COMPUTED = 7;
294var REC_TYPE_COIN = 8;
295var REC_TYPE_ANGLE = 9;
296var REC_TYPE_ACTIVE_OP = 10;
297var REC_TYPE_LAST = REC_TYPE_ACTIVE_OP;
298
299function strs_to_nums(strs) {
300 var result = [];
301 for (var idx = 1; idx < strs.length; ++idx) {
302 var str = strs[idx];
303 var num = parseFloat(str);
304 if (isNaN(num)) {
305 result.push(str);
306 } else {
307 result.push(num);
308 }
309 }
310 return result;
311}
312
313function filter_str_by(id, str, regex, array) {
314 if (regex.test(str)) {
315 var strs = regex.exec(str);
316 var result = strs_to_nums(strs);
317 array.push(id);
318 array.push(result);
319 return true;
320 }
321 return false;
322}
323
324function construct_regexp2(pattern) {
325 var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
326 escape = escape.replace(/UNSORTABLE/g, "\\*\\*\\* UNSORTABLE \\*\\*\\*");
327 escape = escape.replace(/CUBIC_VAL/g, "\\(P_VAL P_VAL P_VAL P_VAL\\)");
328 escape = escape.replace(/QUAD_VAL/g, "\\(P_VAL P_VAL P_VAL\\)");
329 escape = escape.replace(/LINE_VAL/g, "\\(P_VAL P_VAL\\)");
330 escape = escape.replace(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
331 escape = escape.replace(/PT_VAL/g, "\\(P_VAL\\)");
332 escape = escape.replace(/P_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?, ?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
333 escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
334 escape = escape.replace(/PATH/g, "pathB?");
335 escape = escape.replace(/IDX/g, "(\\d+)");
336 escape = escape.replace(/NUM/g, "(-?\\d+)");
337 escape = escape.replace(/OPT/g, "(\\?|-?\\d+)");
338 return new RegExp(escape, 'i');
339}
340
341function construct_regexp2c(pattern) {
342 var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
343 escape = escape.replace(/UNSORTABLE/g, "\\*\\*\\* UNSORTABLE \\*\\*\\*");
344 escape = escape.replace(/CUBIC_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}");
345 escape = escape.replace(/QUAD_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}");
346 escape = escape.replace(/LINE_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}\\}");
347 escape = escape.replace(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
348 escape = escape.replace(/PT_VAL/g, "\\{\\{P_VAL\\}\\}");
349 escape = escape.replace(/P_VAL/g, "(?:f?[xX] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?,(?: f?[yY] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
350 escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
351 escape = escape.replace(/OPER/g, "[a-z]+");
352 escape = escape.replace(/PATH/g, "pathB?");
353 escape = escape.replace(/T_F/g, "([TF])");
354 escape = escape.replace(/IDX/g, "(\\d+)");
355 escape = escape.replace(/NUM/g, "(-?\\d+)");
356 escape = escape.replace(/OPT/g, "(\\?|-?\\d+)");
357 return new RegExp(escape, 'i');
358}
359
360function match_regexp(str, lineNo, array, id, pattern) {
361 var regex = construct_regexp2(pattern);
362 if (filter_str_by(id, str, regex, array)) {
363 return true;
364 }
365 regex = construct_regexp2c(pattern);
366 return filter_str_by(id, str, regex, array);
367}
368
369function endsWith(str, suffix) {
370 return str.indexOf(suffix, str.length - suffix.length) !== -1;
371}
372
373function parse_all(test) {
374 var lines = test.match(/[^\r\n]+/g);
375 var records = []; // a rec can be the original paths, a set of intersections, a set of active spans, a sort, or a path add
376 var record = [];
377 var recType = REC_TYPE_UNKNOWN;
378 var lastLineNo;
379 var moveX, moveY;
380 for (var lineNo = 0; lineNo < lines.length; ++lineNo) {
381 var line = lines[lineNo];
382 if (line.length == 0) {
383 continue;
384 }
385 var opStart = "SkOpSegment::";
386 if (line.lastIndexOf(opStart, 0) === 0) {
387 line = line.substr(opStart.length);
388 }
389 var angleStart = "SkOpAngle::";
390 if (line.lastIndexOf(angleStart, 0) === 0) {
391 line = line.substr(angleStart.length);
392 }
393 var type = line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE
394 : line.lastIndexOf("debugShowTs", 0) === 0 ? REC_TYPE_COIN
395 : line.lastIndexOf("debugShow", 0) === 0 ? REC_TYPE_SECT
396 : line.lastIndexOf("activeOp", 0) === 0 ? REC_TYPE_ACTIVE_OP
397 : line.lastIndexOf("computed", 0) === 0 ? REC_TYPE_COMPUTED
398 : line.lastIndexOf("debugOne", 0) === 0 ? REC_TYPE_SORT
399 : line.lastIndexOf("dumpOne", 0) === 0 ? REC_TYPE_SORT
400 : line.lastIndexOf("pathB.", 0) === 0 ? REC_TYPE_ADD
401 : line.lastIndexOf("path.", 0) === 0 ? REC_TYPE_ADD
402 : line.lastIndexOf("after", 0) === 0 ? REC_TYPE_ANGLE
403 : line.lastIndexOf("mark", 0) === 0 ? REC_TYPE_MARK
404 : line.lastIndexOf(" {{", 0) === 0 ? REC_TYPE_COMPUTED
405 : line.lastIndexOf("{{", 0) === 0 ? REC_TYPE_PATH
406 : line.lastIndexOf("op", 0) === 0 ? REC_TYPE_OP
407 : line.lastIndexOf("$", 0) === 0 ? REC_TYPE_PATH
408 : REC_TYPE_UNKNOWN;
409 if (recType != type || recType == REC_TYPE_ADD || recType == REC_TYPE_SECT
410 || recType == REC_TYPE_ACTIVE_OP || recType == REC_TYPE_ANGLE) {
411 if (recType != REC_TYPE_UNKNOWN) {
412 records.push(recType);
413 records.push(lastLineNo);
414 records.push(record);
415 }
416 record = [];
417 recType = type;
418 lastLineNo = lineNo;
419 }
420 var found = false;
421 switch (recType) {
422 case REC_TYPE_ACTIVE:
423 found = match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" +
424" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
425 ) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
426" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
427 ) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
428" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
429 );
430 break;
431 case REC_TYPE_ACTIVE_OP:
432 found = match_regexp(line, lineNo, record, ACTIVE_OP, "activeOp" +
433" id=IDX t=T_VAL tEnd=T_VAL op=OPER miFrom=NUM miTo=NUM suFrom=NUM suTo=NUM result=IDX"
434 );
435 break;
436 case REC_TYPE_ADD:
437 if (match_regexp(line, lineNo, record, ADD_MOVETO, "PATH.moveTo(P_VAL);")) {
438 moveX = record[1][0];
439 moveY = record[1][1];
440 found = true;
441 } else if (match_regexp(line, lineNo, record, ADD_LINETO, "PATH.lineTo(P_VAL);")) {
442 record[1].unshift(moveY);
443 record[1].unshift(moveX);
444 moveX = record[1][2];
445 moveY = record[1][3];
446 found = true;
447 } else if (match_regexp(line, lineNo, record, ADD_QUADTO, "PATH.quadTo(P_VAL, P_VAL);")) {
448 record[1].unshift(moveY);
449 record[1].unshift(moveX);
450 moveX = record[1][4];
451 moveY = record[1][5];
452 found = true;
453 } else if (match_regexp(line, lineNo, record, ADD_CUBICTO, "PATH.cubicTo(P_VAL, P_VAL, P_VAL);")) {
454 record[1].unshift(moveY);
455 record[1].unshift(moveX);
456 moveX = record[1][6];
457 moveY = record[1][7];
458 found = true;
459 } else if (match_regexp(line, lineNo, record, ADD_FILL, "PATH.setFillType(FILL_TYPE);")) {
460 found = true;
461 } else {
462 found = match_regexp(line, lineNo, record, ADD_CLOSE, "PATH.close();");
463 }
464 break;
465 case REC_TYPE_ANGLE:
466 found = match_regexp(line, lineNo, record, ANGLE_AFTER, "after " +
467"id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL < id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL < id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL T_F IDX");
468 if (found) {
469 break;
470 }
471 found = match_regexp(line, lineNo, record, ANGLE_AFTER2, "after " +
472"[IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL T_F IDX");
473 break;
474 case REC_TYPE_COIN:
475 found = true;
476 break;
477 case REC_TYPE_COMPUTED:
478 found = line == "computed quadratics given"
479 || match_regexp(line, lineNo, record, COMPUTED_SET_1, "computed quadratics set 1"
480 ) || match_regexp(line, lineNo, record, COMPUTED_SET_2, "computed quadratics set 2"
481 ) || match_regexp(line, lineNo, record, PATH_QUAD, " QUAD_VAL,"
482 ) || match_regexp(line, lineNo, record, PATH_CUBIC, " CUBIC_VAL,"
483 );
484 break;
485 case REC_TYPE_PATH:
486 found = match_regexp(line, lineNo, record, PATH_LINE, "LINE_VAL"
487 ) || match_regexp(line, lineNo, record, PATH_QUAD, "QUAD_VAL"
488 ) || match_regexp(line, lineNo, record, PATH_CUBIC, "CUBIC_VAL"
489 );
490 break;
491 case REC_TYPE_SECT:
492 found = match_regexp(line, lineNo, record, INTERSECT_LINE, "debugShowLineIntersection" +
493" wtTs[0]=T_VAL LINE_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
494 ) || match_regexp(line, lineNo, record, INTERSECT_LINE_2, "debugShowLineIntersection" +
495" wtTs[0]=T_VAL LINE_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
496 ) || match_regexp(line, lineNo, record, INTERSECT_LINE_NO, "debugShowLineIntersection" +
497" no intersect LINE_VAL LINE_VAL"
498 ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE, "debugShowQuadLineIntersection" +
499" wtTs[0]=T_VAL QUAD_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
500 ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE_2, "debugShowQuadLineIntersection" +
501" wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
502 ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE_NO, "debugShowQuadLineIntersection" +
503" no intersect QUAD_VAL LINE_VAL"
504 ) || match_regexp(line, lineNo, record, INTERSECT_QUAD, "debugShowQuadIntersection" +
505" wtTs[0]=T_VAL QUAD_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL"
506 ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_2, "debugShowQuadIntersection" +
507" wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
508 ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_NO, "debugShowQuadIntersection" +
509" no intersect QUAD_VAL QUAD_VAL"
510 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE, "debugShowCubicLineIntersection" +
511" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
512 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_2, "debugShowCubicLineIntersection" +
513" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
514 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_3, "debugShowCubicLineIntersection" +
515" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL"
516 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_NO, "debugShowCubicLineIntersection" +
517" no intersect CUBIC_VAL LINE_VAL"
518 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD, "debugShowCubicQuadIntersection" +
519" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL"
520 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_2, "debugShowCubicQuadIntersection" +
521" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
522 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_3, "debugShowCubicQuadIntersection" +
523" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL"
524 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_4, "debugShowCubicQuadIntersection" +
525" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL wtTs[3]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL wnTs[3]=T_VAL"
526 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_QUAD_NO, "debugShowCubicQuadIntersection" +
527" no intersect CUBIC_VAL QUAD_VAL"
528 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC, "debugShowCubicIntersection" +
529" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL"
530 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_2, "debugShowCubicIntersection" +
531" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL"
532 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_3, "debugShowCubicIntersection" +
533" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL"
534 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_4, "debugShowCubicIntersection" +
535" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wtTs[3]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL wnTs[3]=T_VAL"
536 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_NO, "debugShowCubicIntersection" +
537" no intersect CUBIC_VAL CUBIC_VAL"
538 ) || match_regexp(line, lineNo, record, INTERSECT_SELF_CUBIC, "debugShowCubicIntersection" +
539" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL"
540 ) || match_regexp(line, lineNo, record, INTERSECT_SELF_CUBIC_NO, "debugShowCubicIntersection" +
541" no self intersect CUBIC_VAL"
542 );
543 break;
544 case REC_TYPE_SORT:
545 var hasDone = / done/.test(line);
546 var hasUnorderable = / unorderable/.test(line);
547 var hasSmall = / small/.test(line);
548 var hasTiny = / tiny/.test(line);
549 var hasOperand = / operand/.test(line);
550 var hasStop = / stop/.test(line);
551 line.replace(/[ a-z]+$/, "");
552 found = match_regexp(line, lineNo, record, SORT_UNARY, "debugOne" +
553" [IDX/IDX] next=IDX/IDX sect=IDX/IDX s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT"
554 ) || match_regexp(line, lineNo, record, SORT_BINARY, "debugOne" +
555" [IDX/IDX] next=IDX/IDX sect=IDX/IDX s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT oppVal=IDX oppSum=OPT"
556 ) || match_regexp(line, lineNo, record, SORT_UNARY, "dumpOne" +
557" [IDX/IDX] next=IDX/IDX sect=NUM/NUM s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT"
558 ) || match_regexp(line, lineNo, record, SORT_BINARY, "dumpOne" +
559" [IDX/IDX] next=IDX/IDX sect=NUM/NUM s=T_VAL [IDX] e=T_VAL [IDX] sgn=NUM windVal=IDX windSum=OPT oppVal=IDX oppSum=OPT"
560 );
561 if (found) {
562 record[1].push(hasDone);
563 record[1].push(hasUnorderable);
564 record[1].push(hasSmall);
565 record[1].push(hasTiny);
566 record[1].push(hasOperand);
567 record[1].push(hasStop);
568 }
569 break;
570 case REC_TYPE_MARK:
571 found = match_regexp(line, lineNo, record, MARK_LINE, "markWinding" +
572" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
573 ) || match_regexp(line, lineNo, record, MARK_QUAD, "markWinding" +
574" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
575 ) || match_regexp(line, lineNo, record, MARK_CUBIC, "markWinding" +
576" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
577 ) || match_regexp(line, lineNo, record, MARK_DONE_LINE, "markDoneBinary" +
578" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
579 ) || match_regexp(line, lineNo, record, MARK_DONE_QUAD, "markDoneBinary" +
580" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
581 ) || match_regexp(line, lineNo, record, MARK_DONE_CUBIC, "markDoneBinary" +
582" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
583 ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_LINE, "markUnsortable" +
584" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
585 ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_QUAD, "markUnsortable" +
586" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
587 ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_CUBIC, "markUnsortable" +
588" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
589 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_LINE, "markWinding" +
590" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
591 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_QUAD, "markWinding" +
592" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
593 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_CUBIC, "markWinding" +
594" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
595 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_LINE, "markDone" +
596" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
597 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_QUAD, "markDone" +
598" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
599 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_CUBIC, "markDone" +
600" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
601 ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_LINE, "markDoneUnary" +
602" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
603 ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_QUAD, "markDoneUnary" +
604" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
605 ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_CUBIC, "markDoneUnary" +
606" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
607 ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
608" last id=IDX windSum=OPT small=IDX");
609 break;
610 case REC_TYPE_OP:
611 if (line.lastIndexOf("oppSign oppSign=", 0) === 0
612 || line.lastIndexOf("operator<", 0) === 0) {
613 found = true;
614 break;
615 }
616 found = match_regexp(line, lineNo, record, OP_DIFFERENCE, "op difference"
617 ) || match_regexp(line, lineNo, record, OP_INTERSECT, "op intersect"
618 ) || match_regexp(line, lineNo, record, OP_UNION, "op union"
619 ) || match_regexp(line, lineNo, record, OP_XOR, "op xor"
620 );
621 break;
622 case REC_TYPE_UNKNOWN:
623 found = true;
624 break;
625 }
626 if (!found) {
627 console.log(line + " [" + lineNo + "] of type " + type + " not found");
628 }
629 }
630 if (recType != REC_TYPE_UNKNOWN) {
631 records.push(recType);
632 records.push(lastLineNo);
633 records.push(record);
634 }
635 if (records.length >= 1) {
636 tests[testIndex] = records;
637 testLines[testIndex] = lines;
638 }
639}
640
641function init(test) {
642 var canvas = document.getElementById('canvas');
643 if (!canvas.getContext) return;
644 ctx = canvas.getContext('2d');
645 var resScale = retina_scale && window.devicePixelRatio ? window.devicePixelRatio : 1;
646 var unscaledWidth = window.innerWidth - 20;
647 var unscaledHeight = window.innerHeight - 20;
648 screenWidth = unscaledWidth;
649 screenHeight = unscaledHeight;
650 canvas.width = unscaledWidth * resScale;
651 canvas.height = unscaledHeight * resScale;
652 canvas.style.width = unscaledWidth + 'px';
653 canvas.style.height = unscaledHeight + 'px';
654 if (resScale != 1) {
655 ctx.scale(resScale, resScale);
656 }
657 xmin = Infinity;
658 xmax = -Infinity;
659 ymin = Infinity;
660 ymax = -Infinity;
661 hasPath = hasComputedPath = false;
662 firstActiveSpan = -1;
663 for (var tIndex = 0; tIndex < test.length; tIndex += 3) {
664 var recType = test[tIndex];
665 if (!typeof recType == 'number' || recType < REC_TYPE_UNKNOWN || recType > REC_TYPE_LAST) {
666 console.log("unknown rec type: " + recType);
667 throw "stop execution";
668 }
669 var records = test[tIndex + 2];
670 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
671 var fragType = records[recordIndex];
672 if (!typeof fragType == 'number' || fragType < 1 || fragType > FRAG_TYPE_LAST) {
673 console.log("unknown in range frag type: " + fragType);
674 throw "stop execution";
675 }
676 var frags = records[recordIndex + 1];
677 var first = 0;
678 var last = -1;
679 var first2 = 0;
680 var last2 = 0;
681 switch (recType) {
682 case REC_TYPE_COMPUTED:
683 if (fragType == COMPUTED_SET_1 || fragType == COMPUTED_SET_2) {
684 break;
685 }
686 hasComputedPath = true;
687 case REC_TYPE_PATH:
688 switch (fragType) {
689 case PATH_LINE:
690 last = 4;
691 break;
692 case PATH_QUAD:
693 last = 6;
694 break;
695 case PATH_CUBIC:
696 last = 8;
697 break;
698 default:
699 console.log("unknown " + (recType == REC_TYPE_PATH ? "REC_TYPE_PATH"
700 : "REC_TYPE_COMPUTED") + " frag type:" + fragType);
701 throw "stop execution";
702 }
703 if (recType == REC_TYPE_PATH) {
704 hasPath = true;
705 }
706 break;
707 case REC_TYPE_ACTIVE:
708 if (firstActiveSpan < 0) {
709 firstActiveSpan = tIndex;
710 }
711 first = 1;
712 switch (fragType) {
713 case ACTIVE_LINE_SPAN:
714 last = 5;
715 break;
716 case ACTIVE_QUAD_SPAN:
717 last = 7;
718 break;
719 case ACTIVE_CUBIC_SPAN:
720 last = 9;
721 break;
722 default:
723 console.log("unknown REC_TYPE_ACTIVE frag type: " + fragType);
724 throw "stop execution";
725 }
726 break;
727 case REC_TYPE_ADD:
728 switch (fragType) {
729 case ADD_MOVETO:
730 break;
731 case ADD_LINETO:
732 last = 4;
733 break;
734 case ADD_QUADTO:
735 last = 6;
736 break;
737 case ADD_CUBICTO:
738 last = 8;
739 break;
740 case ADD_CLOSE:
741 case ADD_FILL:
742 break;
743 default:
744 console.log("unknown REC_TYPE_ADD frag type: " + fragType);
745 throw "stop execution";
746 }
747 break;
748 case REC_TYPE_SECT:
749 switch (fragType) {
750 case INTERSECT_LINE:
751 first = 1; last = 5; first2 = 8; last2 = 12;
752 break;
753 case INTERSECT_LINE_2:
754 first = 1; last = 5; first2 = 11; last2 = 15;
755 break;
756 case INTERSECT_LINE_NO:
757 first = 0; last = 4; first2 = 4; last2 = 8;
758 break;
759 case INTERSECT_QUAD_LINE:
760 first = 1; last = 7; first2 = 10; last2 = 14;
761 break;
762 case INTERSECT_QUAD_LINE_2:
763 first = 1; last = 7; first2 = 13; last2 = 17;
764 break;
765 case INTERSECT_QUAD_LINE_NO:
766 first = 0; last = 6; first2 = 6; last2 = 10;
767 break;
768 case INTERSECT_QUAD:
769 first = 1; last = 7; first2 = 10; last2 = 16;
770 break;
771 case INTERSECT_QUAD_2:
772 first = 1; last = 7; first2 = 13; last2 = 19;
773 break;
774 case INTERSECT_QUAD_NO:
775 first = 0; last = 6; first2 = 6; last2 = 12;
776 break;
777 case INTERSECT_SELF_CUBIC:
778 first = 1; last = 9;
779 break;
780 case INTERSECT_SELF_CUBIC_NO:
781 first = 0; last = 8;
782 break;
783 case INTERSECT_CUBIC_LINE:
784 first = 1; last = 9; first2 = 12; last2 = 16;
785 break;
786 case INTERSECT_CUBIC_LINE_2:
787 first = 1; last = 9; first2 = 15; last2 = 19;
788 break;
789 case INTERSECT_CUBIC_LINE_3:
790 first = 1; last = 9; first2 = 18; last2 = 22;
791 break;
792 case INTERSECT_CUBIC_LINE_NO:
793 first = 0; last = 8; first2 = 8; last2 = 12;
794 break;
795 case INTERSECT_CUBIC_QUAD:
796 first = 1; last = 9; first2 = 12; last2 = 18;
797 break;
798 case INTERSECT_CUBIC_QUAD_2:
799 first = 1; last = 9; first2 = 15; last2 = 21;
800 break;
801 case INTERSECT_CUBIC_QUAD_3:
802 first = 1; last = 9; first2 = 18; last2 = 24;
803 break;
804 case INTERSECT_CUBIC_QUAD_4:
805 first = 1; last = 9; first2 = 21; last2 = 27;
806 break;
807 case INTERSECT_CUBIC_QUAD_NO:
808 first = 0; last = 8; first2 = 8; last2 = 14;
809 break;
810 case INTERSECT_CUBIC:
811 first = 1; last = 9; first2 = 12; last2 = 20;
812 break;
813 case INTERSECT_CUBIC_2:
814 first = 1; last = 9; first2 = 15; last2 = 23;
815 break;
816 case INTERSECT_CUBIC_3:
817 first = 1; last = 9; first2 = 18; last2 = 26;
818 break;
819 case INTERSECT_CUBIC_4:
820 first = 1; last = 9; first2 = 21; last2 = 29;
821 break;
822 case INTERSECT_CUBIC_NO:
823 first = 0; last = 8; first2 = 8; last2 = 16;
824 break;
825 default:
826 console.log("unknown REC_TYPE_SECT frag type: " + fragType);
827 throw "stop execution";
828 }
829 break;
830 default:
831 continue;
832 }
833 for (var idx = first; idx < last; idx += 2) {
834 xmin = Math.min(xmin, frags[idx]);
835 xmax = Math.max(xmax, frags[idx]);
836 ymin = Math.min(ymin, frags[idx + 1]);
837 ymax = Math.max(ymax, frags[idx + 1]);
838 }
839 for (var idx = first2; idx < last2; idx += 2) {
840 xmin = Math.min(xmin, frags[idx]);
841 xmax = Math.max(xmax, frags[idx]);
842 ymin = Math.min(ymin, frags[idx + 1]);
843 ymax = Math.max(ymax, frags[idx + 1]);
844 }
845 }
846 }
847 var angleBounds = [Infinity, Infinity, -Infinity, -Infinity];
848 for (var tIndex = 0; tIndex < test.length; tIndex += 3) {
849 var recType = test[tIndex];
850 var records = test[tIndex + 2];
851 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
852 var fragType = records[recordIndex];
853 var frags = records[recordIndex + 1];
854 switch (recType) {
855 case REC_TYPE_ACTIVE_OP:
856 if (!draw_op) {
857 break;
858 }
859 {
860 var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
861 curve_extremes(curve, angleBounds);
862 }
863 break;
864 case REC_TYPE_ANGLE:
865 if (!draw_angle) {
866 break;
867 }
868 if (fragType == ANGLE_AFTER) {
869 var curve = curvePartialByID(test, frags[0], frags[3], frags[4]);
870 curve_extremes(curve, angleBounds);
871 curve = curvePartialByID(test, frags[5], frags[8], frags[9]);
872 curve_extremes(curve, angleBounds);
873 curve = curvePartialByID(test, frags[10], frags[13], frags[14]);
874 } else if (fragType == ANGLE_AFTER2) {
875 var curve = curvePartialByID(test, frags[0], frags[4], frags[5]);
876 curve_extremes(curve, angleBounds);
877 curve = curvePartialByID(test, frags[6], frags[10], frags[11]);
878 curve_extremes(curve, angleBounds);
879 curve = curvePartialByID(test, frags[12], frags[16], frags[17]);
880 }
881 break;
882 case REC_TYPE_SORT:
883 if (!draw_sort) {
884 break;
885 }
886 if (fragType == SORT_UNARY || fragType == SORT_BINARY) {
887 var curve = curvePartialByID(test, frags[0], frags[6], frags[8]);
888 curve_extremes(curve, angleBounds);
889 }
890 break;
891 }
892 }
893 }
894 xmin = Math.min(xmin, angleBounds[0]);
895 ymin = Math.min(ymin, angleBounds[1]);
896 xmax = Math.max(xmax, angleBounds[2]);
897 ymax = Math.max(ymax, angleBounds[3]);
898 setScale(xmin, xmax, ymin, ymax);
899 if (hasPath == false && hasComputedPath == true && !draw_computed) {
900 draw_computed = 3; // show both quadratics and cubics
901 }
902 if (hasPath == true && hasComputedPath == false && draw_computed) {
903 draw_computed = 0;
904 }
905}
906
907function curveByID(test, id) {
908 var tIndex = firstActiveSpan;
909 if (tIndex < 0) {
910 return [];
911 }
912 while (tIndex < test.length) {
913 var recType = test[tIndex];
914 if (recType != REC_TYPE_ACTIVE) {
915 return [];
916 }
917 var records = test[tIndex + 2];
918 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
919 var fragType = records[recordIndex];
920 var frags = records[recordIndex + 1];
921 if (frags[0] == id) {
922 switch (fragType) {
923 case ACTIVE_LINE_SPAN:
924 return [frags[1], frags[2], frags[3], frags[4]];
925 case ACTIVE_QUAD_SPAN:
926 return [frags[1], frags[2], frags[3], frags[4],
927 frags[5], frags[6]];
928 case ACTIVE_CUBIC_SPAN:
929 return [frags[1], frags[2], frags[3], frags[4],
930 frags[5], frags[6], frags[7], frags[8]];
931 }
932 }
933 }
934 tIndex += 3;
935 }
936 return [];
937}
938
939function curvePartialByID(test, id, t0, t1) {
940 var tIndex = firstActiveSpan;
941 if (tIndex < 0) {
942 return [];
943 }
944 while (tIndex < test.length) {
945 var recType = test[tIndex];
946 if (recType != REC_TYPE_ACTIVE) {
947 return [];
948 }
949 var records = test[tIndex + 2];
950 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
951 var fragType = records[recordIndex];
952 var frags = records[recordIndex + 1];
953 if (frags[0] == id) {
954 switch (fragType) {
955 case ACTIVE_LINE_SPAN:
956 return linePartial(frags[1], frags[2], frags[3], frags[4], t0, t1);
957 case ACTIVE_QUAD_SPAN:
958 return quadPartial(frags[1], frags[2], frags[3], frags[4],
959 frags[5], frags[6], t0, t1);
960 case ACTIVE_CUBIC_SPAN:
961 return cubicPartial(frags[1], frags[2], frags[3], frags[4],
962 frags[5], frags[6], frags[7], frags[8], t0, t1);
963 }
964 }
965 }
966 tIndex += 3;
967 }
968 return [];
969}
970
971function idByCurve(test, frag, type) {
972 var tIndex = firstActiveSpan;
973 if (tIndex < 0) {
974 return -1;
975 }
976 while (tIndex < test.length) {
977 var recType = test[tIndex];
978 if (recType != REC_TYPE_ACTIVE) {
979 return -1;
980 }
981 var records = test[tIndex + 2];
982 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
983 var fragType = records[recordIndex];
984 var frags = records[recordIndex + 1];
985 switch (fragType) {
986 case ACTIVE_LINE_SPAN:
987 if (type != PATH_LINE) {
988 continue;
989 }
990 if (frag[0] != frags[1] || frag[1] != frags[2]
991 || frag[2] != frags[3] || frag[3] != frags[4]) {
992 continue;
993 }
994 return frags[0];
995 case ACTIVE_QUAD_SPAN:
996 if (type != PATH_QUAD) {
997 continue;
998 }
999 if (frag[0] != frags[1] || frag[1] != frags[2]
1000 || frag[2] != frags[3] || frag[3] != frags[4]
1001 || frag[4] != frags[5] || frag[5] != frags[6]) {
1002 continue;
1003 }
1004 return frags[0];
1005 case ACTIVE_CUBIC_SPAN:
1006 if (type != PATH_CUBIC) {
1007 continue;
1008 }
1009 if (frag[0] != frags[1] || frag[1] != frags[2]
1010 || frag[2] != frags[3] || frag[3] != frags[4]
1011 || frag[4] != frags[5] || frag[5] != frags[6]
1012 || frag[6] != frags[7] || frag[7] != frags[8]) {
1013 continue;
1014 }
1015 return frags[0];
1016 }
1017 }
1018 ++tIndex;
1019 }
1020 return -1;
1021}
1022
1023function curve_extremes(curve, bounds) {
1024 for (var index = 0; index < curve.length; index += 2) {
1025 var x = curve[index];
1026 var y = curve[index + 1];
1027 bounds[0] = Math.min(bounds[0], x);
1028 bounds[1] = Math.min(bounds[1], y);
1029 bounds[2] = Math.max(bounds[2], x);
1030 bounds[3] = Math.max(bounds[3], y);
1031 }
1032}
1033
1034function setScale(x0, x1, y0, y1) {
1035 var srcWidth = x1 - x0;
1036 var srcHeight = y1 - y0;
1037 var usableWidth = screenWidth;
1038 var xDigits = Math.ceil(Math.log(Math.abs(xmax)) / Math.log(10));
1039 var yDigits = Math.ceil(Math.log(Math.abs(ymax)) / Math.log(10));
1040 usableWidth -= (xDigits + yDigits) * 10;
1041 usableWidth -= decimal_places * 10;
1042 if (draw_legend) {
1043 usableWidth -= 40;
1044 }
1045 var hscale = usableWidth / srcWidth;
1046 var vscale = screenHeight / srcHeight;
1047 scale = Math.min(hscale, vscale);
1048 var invScale = 1 / scale;
1049 var sxmin = x0 - invScale * 5;
1050 var symin = y0 - invScale * 10;
1051 var sxmax = x1 + invScale * (6 * decimal_places + 10);
1052 var symax = y1 + invScale * 10;
1053 srcWidth = sxmax - sxmin;
1054 srcHeight = symax - symin;
1055 hscale = usableWidth / srcWidth;
1056 vscale = screenHeight / srcHeight;
1057 scale = Math.min(hscale, vscale);
1058 srcLeft = sxmin;
1059 srcTop = symin;
1060}
1061
1062function drawArc(curve, op, from, to) {
1063 var type = PATH_LINE + (curve.length / 2 - 2);
1064 var pt = pointAtT(curve, type, op ? 0.4 : 0.6);
1065 var dy = pt.y - curve[1];
1066 var dx = pt.x - curve[0];
1067 var dist = Math.sqrt(dy * dy + dx * dx);
1068 var _dist = dist * scale;
1069 var angle = Math.atan2(dy, dx);
1070 var _px = (curve[0] - srcLeft) * scale;
1071 var _py = (curve[1] - srcTop) * scale;
1072 var divisor = 4;
1073 var endDist;
1074 do {
1075 var ends = [];
1076 for (var index = -1; index <= 1; index += 2) {
1077 var px = Math.cos(index * Math.PI / divisor);
1078 var py = Math.sin(index * Math.PI / divisor);
1079 ends.push(px);
1080 ends.push(py);
1081 }
1082 var endDx = (ends[2] - ends[0]) * scale * dist;
1083 var endDy = (ends[3] - ends[1]) * scale * dist;
1084 endDist = Math.sqrt(endDx * endDx + endDy * endDy);
1085 if (endDist < 100) {
1086 break;
1087 }
1088 divisor *= 2;
1089 } while (true);
1090 if (endDist < 30) {
1091 return;
1092 }
1093 if (op) {
1094 divisor *= 2;
1095 }
1096 ctx.strokeStyle = op ? "rgba(210,0,45, 0.4)" : "rgba(90,90,90, 0.5)";
1097 ctx.beginPath();
1098 ctx.arc(_px, _py, _dist, angle - Math.PI / divisor, angle + Math.PI / divisor, false);
1099 ctx.stroke();
1100 var saveAlign = ctx.textAlign;
1101 var saveStyle = ctx.fillStyle;
1102 var saveFont = ctx.font;
1103 ctx.textAlign = "center";
1104 ctx.fillStyle = "black";
1105 ctx.font = "normal 24px Arial";
1106 divisor *= 0.8;
1107 for (var index = -1; index <= 1; index += 2) {
1108 var px = curve[0] + Math.cos(angle + index * Math.PI / divisor) * dist;
1109 var py = curve[1] + Math.sin(angle + index * Math.PI / divisor) * dist;
1110 var _px = (px - srcLeft) * scale;
1111 var _py = (py - srcTop) * scale;
1112 ctx.fillText(index < 0 ? to.toString() : from.toString(), _px, _py + 8);
1113 }
1114 ctx.textAlign = saveAlign;
1115 ctx.fillStyle = saveStyle;
1116 ctx.font = saveFont;
1117}
1118
1119function drawPoint(px, py, end) {
1120 for (var pts = 0; pts < drawnPts.length; pts += 2) {
1121 var x = drawnPts[pts];
1122 var y = drawnPts[pts + 1];
1123 if (px == x && py == y) {
1124 return;
1125 }
1126 }
1127 drawnPts.push(px);
1128 drawnPts.push(py);
1129 var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
1130 var _px = (px - srcLeft) * scale;
1131 var _py = (py - srcTop) * scale;
1132 ctx.beginPath();
1133 ctx.arc(_px, _py, 3, 0, Math.PI*2, true);
1134 ctx.closePath();
1135 if (end) {
1136 ctx.fill();
1137 } else {
1138 ctx.stroke();
1139 }
1140 if (debug_xy) {
1141 ctx.textAlign = "left";
1142 ctx.fillText(label, _px + 5, _py);
1143 }
1144}
1145
1146function drawPoints(ptArray, curveType, drawControls) {
1147 var count = (curveType - PATH_LINE + 2) * 2;
1148 for (var idx = 0; idx < count; idx += 2) {
1149 if (!drawControls && idx != 0 && idx != count - 2) {
1150 continue;
1151 }
1152 drawPoint(ptArray[idx], ptArray[idx + 1], idx == 0 || idx == count - 2);
1153 }
1154}
1155
1156function drawControlLines(curve, curveType, drawEnd) {
1157 if (curveType == PATH_LINE) {
1158 return;
1159 }
1160 ctx.strokeStyle = "rgba(0,0,0, 0.3)";
1161 drawLine(curve[0], curve[1], curve[2], curve[3]);
1162 drawLine(curve[2], curve[3], curve[4], curve[5]);
1163 if (curveType == PATH_CUBIC) {
1164 drawLine(curve[4], curve[5], curve[6], curve[7]);
1165 if (drawEnd > 1) {
1166 drawLine(curve[6], curve[7], curve[0], curve[1]);
1167 if (drawEnd > 2) {
1168 drawLine(curve[0], curve[1], curve[4], curve[5]);
1169 drawLine(curve[6], curve[7], curve[2], curve[3]);
1170 }
1171 }
1172 } else if (drawEnd > 1) {
1173 drawLine(curve[4], curve[5], curve[0], curve[1]);
1174 }
1175}
1176
1177function pointAtT(curve, curveType, t) {
1178 var xy = {};
1179 switch (curveType) {
1180 case PATH_LINE:
1181 var a = 1 - t;
1182 var b = t;
1183 xy.x = a * curve[0] + b * curve[2];
1184 xy.y = a * curve[1] + b * curve[3];
1185 break;
1186 case PATH_QUAD:
1187 var one_t = 1 - t;
1188 var a = one_t * one_t;
1189 var b = 2 * one_t * t;
1190 var c = t * t;
1191 xy.x = a * curve[0] + b * curve[2] + c * curve[4];
1192 xy.y = a * curve[1] + b * curve[3] + c * curve[5];
1193 break;
1194 case PATH_CUBIC:
1195 var one_t = 1 - t;
1196 var one_t2 = one_t * one_t;
1197 var a = one_t2 * one_t;
1198 var b = 3 * one_t2 * t;
1199 var t2 = t * t;
1200 var c = 3 * one_t * t2;
1201 var d = t2 * t;
1202 xy.x = a * curve[0] + b * curve[2] + c * curve[4] + d * curve[6];
1203 xy.y = a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
1204 break;
1205 }
1206 return xy;
1207}
1208
1209function drawPointAtT(curve, curveType) {
1210 var x, y;
1211 var xy = pointAtT(curve, curveType, curveT);
1212 drawPoint(xy.x, xy.y, true);
1213 if (!draw_intersectT) {
1214 return;
1215 }
1216 ctx.fillStyle = "red";
1217 drawTAtPointUp(xy.x, xy.y, curveT);
1218}
1219
1220function drawTAtPointUp(px, py, t) {
1221 var label = t.toFixed(decimal_places);
1222 var _px = (px - srcLeft)* scale;
1223 var _py = (py - srcTop) * scale;
1224 ctx.fillText(label, _px + 5, _py - 10);
1225}
1226
1227function drawTAtPointDown(px, py, t) {
1228 var label = t.toFixed(decimal_places);
1229 var _px = (px - srcLeft)* scale;
1230 var _py = (py - srcTop) * scale;
1231 ctx.fillText(label, _px + 5, _py + 10);
1232}
1233
1234function alreadyDrawnLine(x1, y1, x2, y2) {
1235 if (collect_bounds) {
1236 if (focus_enabled) {
1237 focusXmin = Math.min(focusXmin, x1, x2);
1238 focusYmin = Math.min(focusYmin, y1, y2);
1239 focusXmax = Math.max(focusXmax, x1, x2);
1240 focusYmax = Math.max(focusYmax, y1, y2);
1241 }
1242 return true;
1243 }
1244 for (var pts = 0; pts < drawnLines.length; pts += 4) {
1245 if (x1 == drawnLines[pts] && y1 == drawnLines[pts + 1]
1246 && x2 == drawnLines[pts + 2] && y2 == drawnLines[pts + 3]) {
1247 return true;
1248 }
1249 }
1250 drawnLines.push(x1);
1251 drawnLines.push(y1);
1252 drawnLines.push(x2);
1253 drawnLines.push(y2);
1254 return false;
1255}
1256
1257function drawLine(x1, y1, x2, y2) {
1258 if (alreadyDrawnLine(x1, y1, x2, y2)) {
1259 return;
1260 }
1261 ctx.beginPath();
1262 ctx.moveTo((x1 - srcLeft) * scale,
1263 (y1 - srcTop) * scale);
1264 ctx.lineTo((x2 - srcLeft) * scale,
1265 (y2 - srcTop) * scale);
1266 ctx.stroke();
1267}
1268
1269function linePartial(x1, y1, x2, y2, t1, t2) {
1270 var dx = x1 - x2;
1271 var dy = y1 - y2;
1272 var array = [
1273 x1 - t1 * dx,
1274 y1 - t1 * dy,
1275 x1 - t2 * dx,
1276 y1 - t2 * dy
1277 ];
1278 return array;
1279}
1280
1281function drawLinePartial(x1, y1, x2, y2, t1, t2) {
1282 var a = linePartial(x1, y1, x2, y2, t1, t2);
1283 var ax = a[0];
1284 var ay = a[1];
1285 var bx = a[2];
1286 var by = a[3];
1287 if (alreadyDrawnLine(ax, ay, bx, by)) {
1288 return;
1289 }
1290 ctx.beginPath();
1291 ctx.moveTo((ax - srcLeft) * scale,
1292 (ay - srcTop) * scale);
1293 ctx.lineTo((bx - srcLeft) * scale,
1294 (by - srcTop) * scale);
1295 ctx.stroke();
1296}
1297
1298function alreadyDrawnQuad(x1, y1, x2, y2, x3, y3) {
1299 if (collect_bounds) {
1300 if (focus_enabled) {
1301 focusXmin = Math.min(focusXmin, x1, x2, x3);
1302 focusYmin = Math.min(focusYmin, y1, y2, y3);
1303 focusXmax = Math.max(focusXmax, x1, x2, x3);
1304 focusYmax = Math.max(focusYmax, y1, y2, y3);
1305 }
1306 return true;
1307 }
1308 for (var pts = 0; pts < drawnQuads.length; pts += 6) {
1309 if (x1 == drawnQuads[pts] && y1 == drawnQuads[pts + 1]
1310 && x2 == drawnQuads[pts + 2] && y2 == drawnQuads[pts + 3]
1311 && x3 == drawnQuads[pts + 4] && y3 == drawnQuads[pts + 5]) {
1312 return true;
1313 }
1314 }
1315 drawnQuads.push(x1);
1316 drawnQuads.push(y1);
1317 drawnQuads.push(x2);
1318 drawnQuads.push(y2);
1319 drawnQuads.push(x3);
1320 drawnQuads.push(y3);
1321 return false;
1322}
1323
1324function drawQuad(x1, y1, x2, y2, x3, y3) {
1325 if (alreadyDrawnQuad(x1, y1, x2, y2, x3, y3)) {
1326 return;
1327 }
1328 ctx.beginPath();
1329 ctx.moveTo((x1 - srcLeft) * scale,
1330 (y1 - srcTop) * scale);
1331 ctx.quadraticCurveTo((x2 - srcLeft) * scale,
1332 (y2 - srcTop) * scale,
1333 (x3 - srcLeft) * scale,
1334 (y3 - srcTop) * scale);
1335 ctx.stroke();
1336}
1337
1338function interp(A, B, t) {
1339 return A + (B - A) * t;
1340}
1341
1342function interp_quad_coords(x1, x2, x3, t)
1343{
1344 var ab = interp(x1, x2, t);
1345 var bc = interp(x2, x3, t);
1346 var abc = interp(ab, bc, t);
1347 return abc;
1348}
1349
1350function quadPartial(x1, y1, x2, y2, x3, y3, t1, t2) {
1351 var ax = interp_quad_coords(x1, x2, x3, t1);
1352 var ay = interp_quad_coords(y1, y2, y3, t1);
1353 var dx = interp_quad_coords(x1, x2, x3, (t1 + t2) / 2);
1354 var dy = interp_quad_coords(y1, y2, y3, (t1 + t2) / 2);
1355 var cx = interp_quad_coords(x1, x2, x3, t2);
1356 var cy = interp_quad_coords(y1, y2, y3, t2);
1357 var bx = 2*dx - (ax + cx)/2;
1358 var by = 2*dy - (ay + cy)/2;
1359 var array = [
1360 ax, ay, bx, by, cx, cy
1361 ];
1362 return array;
1363}
1364
1365function drawQuadPartial(x1, y1, x2, y2, x3, y3, t1, t2) {
1366 var a = quadPartial(x1, y1, x2, y2, x3, y3, t1, t2);
1367 var ax = a[0];
1368 var ay = a[1];
1369 var bx = a[2];
1370 var by = a[3];
1371 var cx = a[4];
1372 var cy = a[5];
1373 if (alreadyDrawnQuad(ax, ay, bx, by, cx, cy)) {
1374 return;
1375 }
1376 ctx.beginPath();
1377 ctx.moveTo((ax - srcLeft) * scale,
1378 (ay - srcTop) * scale);
1379 ctx.quadraticCurveTo((bx - srcLeft) * scale,
1380 (by - srcTop) * scale,
1381 (cx - srcLeft) * scale,
1382 (cy - srcTop) * scale);
1383 ctx.stroke();
1384}
1385
1386function alreadyDrawnCubic(x1, y1, x2, y2, x3, y3, x4, y4) {
1387 if (collect_bounds) {
1388 if (focus_enabled) {
1389 focusXmin = Math.min(focusXmin, x1, x2, x3, x4);
1390 focusYmin = Math.min(focusYmin, y1, y2, y3, y4);
1391 focusXmax = Math.max(focusXmax, x1, x2, x3, x4);
1392 focusYmax = Math.max(focusYmax, y1, y2, y3, y4);
1393 }
1394 return true;
1395 }
1396 for (var pts = 0; pts < drawnCubics.length; pts += 8) {
1397 if (x1 == drawnCubics[pts] && y1 == drawnCubics[pts + 1]
1398 && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3]
1399 && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5]
1400 && x4 == drawnCubics[pts + 6] && y4 == drawnCubics[pts + 7]) {
1401 return true;
1402 }
1403 }
1404 drawnCubics.push(x1);
1405 drawnCubics.push(y1);
1406 drawnCubics.push(x2);
1407 drawnCubics.push(y2);
1408 drawnCubics.push(x3);
1409 drawnCubics.push(y3);
1410 drawnCubics.push(x4);
1411 drawnCubics.push(y4);
1412 return false;
1413}
1414
1415function drawCubic(x1, y1, x2, y2, x3, y3, x4, y4) {
1416 if (alreadyDrawnCubic(x1, y1, x2, y2, x3, y3, x4, y4)) {
1417 return;
1418 }
1419 ctx.beginPath();
1420 ctx.moveTo((x1 - srcLeft) * scale,
1421 (y1 - srcTop) * scale);
1422 ctx.bezierCurveTo((x2 - srcLeft) * scale,
1423 (y2 - srcTop) * scale,
1424 (x3 - srcLeft) * scale,
1425 (y3 - srcTop) * scale,
1426 (x4 - srcLeft) * scale,
1427 (y4 - srcTop) * scale);
1428 ctx.stroke();
1429}
1430
1431function interp_cubic_coords(x1, x2, x3, x4, t)
1432{
1433 var ab = interp(x1, x2, t);
1434 var bc = interp(x2, x3, t);
1435 var cd = interp(x3, x4, t);
1436 var abc = interp(ab, bc, t);
1437 var bcd = interp(bc, cd, t);
1438 var abcd = interp(abc, bcd, t);
1439 return abcd;
1440}
1441
1442function cubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
1443 var ax = interp_cubic_coords(x1, x2, x3, x4, t1);
1444 var ay = interp_cubic_coords(y1, y2, y3, y4, t1);
1445 var ex = interp_cubic_coords(x1, x2, x3, x4, (t1*2+t2)/3);
1446 var ey = interp_cubic_coords(y1, y2, y3, y4, (t1*2+t2)/3);
1447 var fx = interp_cubic_coords(x1, x2, x3, x4, (t1+t2*2)/3);
1448 var fy = interp_cubic_coords(y1, y2, y3, y4, (t1+t2*2)/3);
1449 var dx = interp_cubic_coords(x1, x2, x3, x4, t2);
1450 var dy = interp_cubic_coords(y1, y2, y3, y4, t2);
1451 var mx = ex * 27 - ax * 8 - dx;
1452 var my = ey * 27 - ay * 8 - dy;
1453 var nx = fx * 27 - ax - dx * 8;
1454 var ny = fy * 27 - ay - dy * 8;
1455 var bx = (mx * 2 - nx) / 18;
1456 var by = (my * 2 - ny) / 18;
1457 var cx = (nx * 2 - mx) / 18;
1458 var cy = (ny * 2 - my) / 18;
1459 var array = [
1460 ax, ay, bx, by, cx, cy, dx, dy
1461 ];
1462 return array;
1463}
1464
1465function drawCubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
1466 var a = cubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2);
1467 var ax = a[0];
1468 var ay = a[1];
1469 var bx = a[2];
1470 var by = a[3];
1471 var cx = a[4];
1472 var cy = a[5];
1473 var dx = a[6];
1474 var dy = a[7];
1475 if (alreadyDrawnCubic(ax, ay, bx, by, cx, cy, dx, dy)) {
1476 return;
1477 }
1478 ctx.beginPath();
1479 ctx.moveTo((ax - srcLeft) * scale,
1480 (ay - srcTop) * scale);
1481 ctx.bezierCurveTo((bx - srcLeft) * scale,
1482 (by - srcTop) * scale,
1483 (cx - srcLeft) * scale,
1484 (cy - srcTop) * scale,
1485 (dx - srcLeft) * scale,
1486 (dy - srcTop) * scale);
1487 ctx.stroke();
1488}
1489
1490function drawCurve(c) {
1491 switch (c.length) {
1492 case 4:
1493 drawLine(c[0], c[1], c[2], c[3]);
1494 break;
1495 case 6:
1496 drawQuad(c[0], c[1], c[2], c[3], c[4], c[5]);
1497 break;
1498 case 8:
1499 drawCubic(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
1500 break;
1501 }
1502}
1503
1504function boundsWidth(pts) {
1505 var min = pts[0];
1506 var max = pts[0];
1507 for (var idx = 2; idx < pts.length; idx += 2) {
1508 min = Math.min(min, pts[idx]);
1509 max = Math.max(max, pts[idx]);
1510 }
1511 return max - min;
1512}
1513
1514function boundsHeight(pts) {
1515 var min = pts[1];
1516 var max = pts[1];
1517 for (var idx = 3; idx < pts.length; idx += 2) {
1518 min = Math.min(min, pts[idx]);
1519 max = Math.max(max, pts[idx]);
1520 }
1521 return max - min;
1522}
1523
1524function tangent(pts) {
1525 var dx = pts[2] - pts[0];
1526 var dy = pts[3] - pts[1];
1527 if (dx == 0 && dy == 0 && pts.length > 4) {
1528 dx = pts[4] - pts[0];
1529 dy = pts[5] - pts[1];
1530 if (dx == 0 && dy == 0 && pts.length > 6) {
1531 dx = pts[6] - pts[0];
1532 dy = pts[7] - pts[1];
1533 }
1534 }
1535 return Math.atan2(-dy, dx);
1536}
1537
1538function hodograph(cubic) {
1539 var hodo = [];
1540 hodo[0] = 3 * (cubic[2] - cubic[0]);
1541 hodo[1] = 3 * (cubic[3] - cubic[1]);
1542 hodo[2] = 3 * (cubic[4] - cubic[2]);
1543 hodo[3] = 3 * (cubic[5] - cubic[3]);
1544 hodo[4] = 3 * (cubic[6] - cubic[4]);
1545 hodo[5] = 3 * (cubic[7] - cubic[5]);
1546 return hodo;
1547}
1548
1549function hodograph2(cubic) {
1550 var quad = hodograph(cubic);
1551 var hodo = [];
1552 hodo[0] = 2 * (quad[2] - quad[0]);
1553 hodo[1] = 2 * (quad[3] - quad[1]);
1554 hodo[2] = 2 * (quad[4] - quad[2]);
1555 hodo[3] = 2 * (quad[5] - quad[3]);
1556 return hodo;
1557}
1558
1559function quadraticRootsReal(A, B, C, s) {
1560 if (A == 0) {
1561 if (B == 0) {
1562 s[0] = 0;
1563 return C == 0;
1564 }
1565 s[0] = -C / B;
1566 return 1;
1567 }
1568 /* normal form: x^2 + px + q = 0 */
1569 var p = B / (2 * A);
1570 var q = C / A;
1571 var p2 = p * p;
1572 if (p2 < q) {
1573 return 0;
1574 }
1575 var sqrt_D = 0;
1576 if (p2 > q) {
1577 sqrt_D = sqrt(p2 - q);
1578 }
1579 s[0] = sqrt_D - p;
1580 s[1] = -sqrt_D - p;
1581 return 1 + s[0] != s[1];
1582}
1583
1584function add_valid_ts(s, realRoots, t) {
1585 var foundRoots = 0;
1586 for (var index = 0; index < realRoots; ++index) {
1587 var tValue = s[index];
1588 if (tValue >= 0 && tValue <= 1) {
1589 for (var idx2 = 0; idx2 < foundRoots; ++idx2) {
1590 if (t[idx2] != tValue) {
1591 t[foundRoots++] = tValue;
1592 }
1593 }
1594 }
1595 }
1596 return foundRoots;
1597}
1598
1599function quadraticRootsValidT(a, b, c, t) {
1600 var s = [];
1601 var realRoots = quadraticRootsReal(A, B, C, s);
1602 var foundRoots = add_valid_ts(s, realRoots, t);
1603 return foundRoots != 0;
1604}
1605
1606function find_cubic_inflections(cubic, tValues) {
1607 var Ax = src[2] - src[0];
1608 var Ay = src[3] - src[1];
1609 var Bx = src[4] - 2 * src[2] + src[0];
1610 var By = src[5] - 2 * src[3] + src[1];
1611 var Cx = src[6] + 3 * (src[2] - src[4]) - src[0];
1612 var Cy = src[7] + 3 * (src[3] - src[5]) - src[1];
1613 return quadraticRootsValidT(Bx * Cy - By * Cx, (Ax * Cy - Ay * Cx),
1614 Ax * By - Ay * Bx, tValues);
1615}
1616
1617function dxy_at_t(curve, type, t) {
1618 var dxy = {};
1619 if (type == PATH_QUAD) {
1620 var a = t - 1;
1621 var b = 1 - 2 * t;
1622 var c = t;
1623 dxy.x = a * curve[0] + b * curve[2] + c * curve[4];
1624 dxy.y = a * curve[1] + b * curve[3] + c * curve[5];
1625 } else if (type == PATH_CUBIC) {
1626 var one_t = 1 - t;
1627 var a = curve[0];
1628 var b = curve[2];
1629 var c = curve[4];
1630 var d = curve[6];
1631 dxy.x = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
1632 a = curve[1];
1633 b = curve[3];
1634 c = curve[5];
1635 d = curve[7];
1636 dxy.y = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
1637 }
1638 return dxy;
1639}
1640
1641function drawLabel(num, px, py) {
1642 ctx.beginPath();
1643 ctx.arc(px, py, 8, 0, Math.PI*2, true);
1644 ctx.closePath();
1645 ctx.strokeStyle = "rgba(0,0,0, 0.4)";
1646 ctx.lineWidth = num == 0 || num == 3 ? 2 : 1;
1647 ctx.stroke();
1648 ctx.fillStyle = "black";
1649 ctx.font = "normal 10px Arial";
1650 // ctx.rotate(0.001);
1651 ctx.fillText(num, px - 2, py + 3);
1652 // ctx.rotate(-0.001);
1653}
1654
1655function drawLabelX(ymin, num, loc) {
1656 var px = (loc - srcLeft) * scale;
1657 var py = (ymin - srcTop) * scale - 20;
1658 drawLabel(num, px, py);
1659}
1660
1661function drawLabelY(xmin, num, loc) {
1662 var px = (xmin - srcLeft) * scale - 20;
1663 var py = (loc - srcTop) * scale;
1664 drawLabel(num, px, py);
1665}
1666
1667function drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY) {
1668 ctx.beginPath();
1669 ctx.moveTo(hx, hy - 100);
1670 ctx.lineTo(hx, hy);
1671 ctx.strokeStyle = hMinY < 0 ? "green" : "blue";
1672 ctx.stroke();
1673 ctx.beginPath();
1674 ctx.moveTo(hx, hy);
1675 ctx.lineTo(hx, hy + 100);
1676 ctx.strokeStyle = hMaxY > 0 ? "green" : "blue";
1677 ctx.stroke();
1678 ctx.beginPath();
1679 ctx.moveTo(hx - 100, hy);
1680 ctx.lineTo(hx, hy);
1681 ctx.strokeStyle = hMinX < 0 ? "green" : "blue";
1682 ctx.stroke();
1683 ctx.beginPath();
1684 ctx.moveTo(hx, hy);
1685 ctx.lineTo(hx + 100, hy);
1686 ctx.strokeStyle = hMaxX > 0 ? "green" : "blue";
1687 ctx.stroke();
1688}
1689
1690function scalexy(x, y, mag) {
1691 var length = Math.sqrt(x * x + y * y);
1692 return mag / length;
1693}
1694
1695function drawArrow(x, y, dx, dy) {
1696 var dscale = scalexy(dx, dy, 1 / scale * 100);
1697 dx *= dscale;
1698 dy *= dscale;
1699 ctx.beginPath();
1700 ctx.moveTo((x - srcLeft) * scale, (y - srcTop) * scale);
1701 x += dx;
1702 y += dy;
1703 ctx.lineTo((x - srcLeft) * scale, (y - srcTop) * scale);
1704 dx /= 10;
1705 dy /= 10;
1706 ctx.lineTo((x - dy - srcLeft) * scale, (y + dx - srcTop) * scale);
1707 ctx.lineTo((x + dx * 2 - srcLeft) * scale, (y + dy * 2 - srcTop) * scale);
1708 ctx.lineTo((x + dy - srcLeft) * scale, (y - dx - srcTop) * scale);
1709 ctx.lineTo((x - srcLeft) * scale, (y - srcTop) * scale);
1710 ctx.strokeStyle = "rgba(0,75,0, 0.4)";
1711 ctx.stroke();
1712}
1713
1714function x_at_t(curve, t) {
1715 var one_t = 1 - t;
1716 if (curve.length == 4) {
1717 return one_t * curve[0] + t * curve[2];
1718 }
1719 var one_t2 = one_t * one_t;
1720 var t2 = t * t;
1721 if (curve.length == 6) {
1722 return one_t2 * curve[0] + 2 * one_t * t * curve[2] + t2 * curve[4];
1723 }
1724 var a = one_t2 * one_t;
1725 var b = 3 * one_t2 * t;
1726 var c = 3 * one_t * t2;
1727 var d = t2 * t;
1728 return a * curve[0] + b * curve[2] + c * curve[4] + d * curve[6];
1729}
1730
1731function y_at_t(curve, t) {
1732 var one_t = 1 - t;
1733 if (curve.length == 4) {
1734 return one_t * curve[1] + t * curve[3];
1735 }
1736 var one_t2 = one_t * one_t;
1737 var t2 = t * t;
1738 if (curve.length == 6) {
1739 return one_t2 * curve[1] + 2 * one_t * t * curve[3] + t2 * curve[5];
1740 }
1741 var a = one_t2 * one_t;
1742 var b = 3 * one_t2 * t;
1743 var c = 3 * one_t * t2;
1744 var d = t2 * t;
1745 return a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
1746}
1747
1748function drawOrder(curve, label) {
1749 var px = x_at_t(curve, 0.75);
1750 var py = y_at_t(curve, 0.75);
1751 var _px = (px - srcLeft) * scale;
1752 var _py = (py - srcTop) * scale;
1753 ctx.beginPath();
1754 ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
1755 ctx.closePath();
1756 ctx.fillStyle = "white";
1757 ctx.fill();
1758 if (label == 'L') {
1759 ctx.strokeStyle = "rgba(255,0,0, 1)";
1760 ctx.fillStyle = "rgba(255,0,0, 1)";
1761 } else {
1762 ctx.strokeStyle = "rgba(0,0,255, 1)";
1763 ctx.fillStyle = "rgba(0,0,255, 1)";
1764 }
1765 ctx.stroke();
1766 ctx.font = "normal 16px Arial";
1767 ctx.textAlign = "center";
1768 ctx.fillText(label, _px, _py + 5);
1769 ctx.font = "normal 10px Arial";
1770}
1771
1772function drawID(curve, id) {
1773 var px = x_at_t(curve, 0.5);
1774 var py = y_at_t(curve, 0.5);
1775 var _px = (px - srcLeft) * scale;
1776 var _py = (py - srcTop) * scale;
1777 draw_id_at(id, _px, _py);
1778}
1779
1780function draw_id_at(id, _px, _py) {
1781 ctx.beginPath();
1782 ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
1783 ctx.closePath();
1784 ctx.fillStyle = "white";
1785 ctx.fill();
1786 ctx.strokeStyle = "rgba(127,127,0, 1)";
1787 ctx.fillStyle = "rgba(127,127,0, 1)";
1788 ctx.stroke();
1789 ctx.font = "normal 16px Arial";
1790 ctx.textAlign = "center";
1791 ctx.fillText(id, _px, _py + 5);
1792 ctx.font = "normal 10px Arial";
1793}
1794
1795function drawLinePartialID(id, x1, y1, x2, y2, t1, t2) {
1796 var curve = [x1, y1, x2, y2];
1797 drawCurvePartialID(id, curve, t1, t2);
1798}
1799
1800function drawQuadPartialID(id, x1, y1, x2, y2, x3, y3, t1, t2) {
1801 var curve = [x1, y1, x2, y2, x3, y3];
1802 drawCurvePartialID(id, curve, t1, t2);
1803}
1804
1805function drawCubicPartialID(id, x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
1806 var curve = [x1, y1, x2, y2, x3, y3, x4, y4];
1807 drawCurvePartialID(id, curve, t1, t2);
1808}
1809
1810function drawCurvePartialID(id, curve, t1, t2) {
1811 var px = x_at_t(curve, (t1 + t2) / 2);
1812 var py = y_at_t(curve, (t1 + t2) / 2);
1813 var _px = (px - srcLeft) * scale;
1814 var _py = (py - srcTop) * scale;
1815 draw_id_at(id, _px, _py);
1816}
1817
1818function drawCurveSpecials(test, curve, type) {
1819 if (pt_labels) {
1820 drawPoints(curve, type, pt_labels == 2);
1821 }
1822 if (control_lines != 0) {
1823 drawControlLines(curve, type, control_lines);
1824 }
1825 if (curve_t) {
1826 drawPointAtT(curve, type);
1827 }
1828 if (draw_midpoint) {
1829 var mid = pointAtT(curve, type, 0.5);
1830 drawPoint(mid.x, mid.y, true);
1831 }
1832 if (draw_id) {
1833 var id = idByCurve(test, curve, type);
1834 if (id >= 0) {
1835 drawID(curve, id);
1836 }
1837 }
1838 if (type == PATH_LINE) {
1839 return;
1840 }
1841 if (draw_deriviatives > 0) {
1842 var d = dxy_at_t(curve, type, 0);
1843 drawArrow(curve[0], curve[1], d.x, d.y);
1844 if (draw_deriviatives == 2) {
1845 d = dxy_at_t(curve, type, 1);
1846 if (type == PATH_CUBIC) {
1847 drawArrow(curve[6], curve[7], d.x, d.y);
1848 } else {
1849 drawArrow(curve[4], curve[5], d.x, d.y);
1850 }
1851 }
1852 if (draw_midpoint) {
1853 var mid = pointAtT(curve, type, 0.5);
1854 d = dxy_at_t(curve, type, 0.5);
1855 drawArrow(mid.x, mid.y, d.x, d.y);
1856 }
1857 }
1858 if (type != PATH_CUBIC) {
1859 return;
1860 }
1861 if (draw_hodo == 1 || draw_hodo == 2) {
1862 var hodo = hodograph(curve);
1863 var hMinX = Math.min(0, hodo[0], hodo[2], hodo[4]);
1864 var hMinY = Math.min(0, hodo[1], hodo[3], hodo[5]);
1865 var hMaxX = Math.max(0, hodo[0], hodo[2], hodo[4]);
1866 var hMaxY = Math.max(0, hodo[1], hodo[3], hodo[5]);
1867 var hScaleX = hMaxX - hMinX > 0 ? screenWidth / (hMaxX - hMinX) : 1;
1868 var hScaleY = hMaxY - hMinY > 0 ? screenHeight / (hMaxY - hMinY) : 1;
1869 var hUnit = Math.min(hScaleX, hScaleY);
1870 hUnit /= 2;
1871 var hx = xoffset - hMinX * hUnit;
1872 var hy = yoffset - hMinY * hUnit;
1873 ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit);
1874 ctx.quadraticCurveTo(
1875 hx + hodo[2] * hUnit, hy + hodo[3] * hUnit,
1876 hx + hodo[4] * hUnit, hy + hodo[5] * hUnit);
1877 ctx.strokeStyle = "red";
1878 ctx.stroke();
1879 if (draw_hodo == 1) {
1880 drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY);
1881 }
1882 }
1883 if (draw_hodo == 3) {
1884 var hodo = hodograph2(curve);
1885 var hMinX = Math.min(0, hodo[0], hodo[2]);
1886 var hMinY = Math.min(0, hodo[1], hodo[3]);
1887 var hMaxX = Math.max(0, hodo[0], hodo[2]);
1888 var hMaxY = Math.max(0, hodo[1], hodo[3]);
1889 var hScaleX = hMaxX - hMinX > 0 ? screenWidth / (hMaxX - hMinX) : 1;
1890 var hScaleY = hMaxY - hMinY > 0 ? screenHeight / (hMaxY - hMinY) : 1;
1891 var hUnit = Math.min(hScaleX, hScaleY);
1892 hUnit /= 2;
1893 var hx = xoffset - hMinX * hUnit;
1894 var hy = yoffset - hMinY * hUnit;
1895 ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit);
1896 ctx.lineTo(hx + hodo[2] * hUnit, hy + hodo[3] * hUnit);
1897 ctx.strokeStyle = "red";
1898 ctx.stroke();
1899 drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY);
1900 }
1901 if (draw_sequence) {
1902 var ymin = Math.min(curve[1], curve[3], curve[5], curve[7]);
1903 for (var i = 0; i < 8; i+= 2) {
1904 drawLabelX(ymin, i >> 1, curve[i]);
1905 }
1906 var xmin = Math.min(curve[0], curve[2], curve[4], curve[6]);
1907 for (var i = 1; i < 8; i+= 2) {
1908 drawLabelY(xmin, i >> 1, curve[i]);
1909 }
1910 }
1911}
1912
1913function logCurves(test) {
1914 for (curves in test) {
1915 var curve = test[curves];
1916 dumpCurve(curve);
1917 }
1918}
1919
1920function curveToString(curve) {
1921 var str = "{{";
1922 for (i = 0; i < curve.length; i += 2) {
1923 str += curve[i].toFixed(decimal_places) + "," + curve[i + 1].toFixed(decimal_places);
1924 if (i < curve.length - 2) {
1925 str += "}, {";
1926 }
1927 }
1928 str += "}}";
1929 return str;
1930}
1931
1932function dumpCurve(curve) {
1933 console.log(curveToString(curve));
1934}
1935
1936function draw(test, lines, title) {
1937 ctx.fillStyle = "rgba(0,0,0, 0.1)";
1938 ctx.font = "normal 50px Arial";
1939 ctx.textAlign = "left";
1940 ctx.fillText(title, 50, 50);
1941 ctx.font = "normal 10px Arial";
1942 ctx.lineWidth = "1.001"; "0.999";
1943 var secondPath = test.length;
1944 var closeCount = 0;
1945 logStart = -1;
1946 logRange = 0;
1947 // find last active rec type at this step
1948 var curType = test[0];
1949 var curStep = 0;
1950 var hasOp = false;
1951 var lastActive = 0;
1952 var lastAdd = 0;
1953 var lastSect = 0;
1954 var lastSort = 0;
1955 var lastMark = 0;
1956 activeCount = 0;
1957 addCount = 0;
1958 angleCount = 0;
1959 opCount = 0;
1960 sectCount = 0;
1961 sortCount = 0;
1962 markCount = 0;
1963 activeMax = 0;
1964 addMax = 0;
1965 angleMax = 0;
1966 opMax = 0;
1967 sectMax = 0;
1968 sectMax2 = 0;
1969 sortMax = 0;
1970 markMax = 0;
1971 lastIndex = test.length - 3;
1972 for (var tIndex = 0; tIndex < test.length; tIndex += 3) {
1973 var recType = test[tIndex];
1974 if (!typeof recType == 'number' || recType < REC_TYPE_UNKNOWN || recType > REC_TYPE_LAST) {
1975 console.log("unknown rec type: " + recType);
1976 throw "stop execution";
1977 }
1978 // if (curType == recType && curType != REC_TYPE_ADD) {
1979 // continue;
1980 // }
1981 var inStepRange = step_limit == 0 || curStep < step_limit;
1982 curType = recType;
1983 if (recType == REC_TYPE_OP) {
1984 hasOp = true;
1985 continue;
1986 }
1987 if (recType == REC_TYPE_UNKNOWN) {
1988 // these types do not advance step
1989 continue;
1990 }
1991 var bumpStep = false;
1992 var records = test[tIndex + 2];
1993 var fragType = records[0];
1994 if (recType == REC_TYPE_ADD) {
1995 if (records.length != 2) {
1996 console.log("expect only two elements: " + records.length);
1997 throw "stop execution";
1998 }
1999 if (fragType == ADD_MOVETO || fragType == ADD_CLOSE) {
2000 continue;
2001 }
2002 ++addMax;
2003 if (!draw_add || !inStepRange) {
2004 continue;
2005 }
2006 lastAdd = tIndex;
2007 ++addCount;
2008 bumpStep = true;
2009 }
2010 if (recType == REC_TYPE_PATH && hasOp) {
2011 secondPath = tIndex;
2012 }
2013 if (recType == REC_TYPE_ACTIVE) {
2014 ++activeMax;
2015 if (!draw_active || !inStepRange) {
2016 continue;
2017 }
2018 lastActive = tIndex;
2019 ++activeCount;
2020 bumpStep = true;
2021 }
2022 if (recType == REC_TYPE_ACTIVE_OP) {
2023 ++opMax;
2024 if (!draw_op || !inStepRange) {
2025 continue;
2026 }
2027 lastOp = tIndex;
2028 ++opCount;
2029 bumpStep = true;
2030 }
2031 if (recType == REC_TYPE_ANGLE) {
2032 ++angleMax;
2033 if (!draw_angle || !inStepRange) {
2034 continue;
2035 }
2036 lastAngle = tIndex;
2037 ++angleCount;
2038 bumpStep = true;
2039 }
2040 if (recType == REC_TYPE_SECT) {
2041 if (records.length != 2) {
2042 console.log("expect only two elements: " + records.length);
2043 throw "stop execution";
2044 }
2045 ++sectMax;
2046 var sectBump = 1;
2047 switch (fragType) {
2048 case INTERSECT_LINE:
2049 case INTERSECT_QUAD_LINE:
2050 case INTERSECT_QUAD:
2051 case INTERSECT_SELF_CUBIC:
2052 case INTERSECT_CUBIC_LINE:
2053 case INTERSECT_CUBIC_QUAD:
2054 case INTERSECT_CUBIC:
2055 sectBump = 1;
2056 break;
2057 case INTERSECT_LINE_2:
2058 case INTERSECT_QUAD_LINE_2:
2059 case INTERSECT_QUAD_2:
2060 case INTERSECT_CUBIC_LINE_2:
2061 case INTERSECT_CUBIC_QUAD_2:
2062 case INTERSECT_CUBIC_2:
2063 sectBump = 2;
2064 break;
2065 case INTERSECT_LINE_NO:
2066 case INTERSECT_QUAD_LINE_NO:
2067 case INTERSECT_QUAD_NO:
2068 case INTERSECT_SELF_CUBIC_NO:
2069 case INTERSECT_CUBIC_LINE_NO:
2070 case INTERSECT_CUBIC_QUAD_NO:
2071 case INTERSECT_CUBIC_NO:
2072 sectBump = 0;
2073 break;
2074 case INTERSECT_CUBIC_LINE_3:
2075 case INTERSECT_CUBIC_QUAD_3:
2076 case INTERSECT_CUBIC_3:
2077 sectBump = 3;
2078 break;
2079 case INTERSECT_CUBIC_QUAD_4:
2080 case INTERSECT_CUBIC_4:
2081 sectBump = 4;
2082 break;
2083 default:
2084 console.log("missing case " + records.length);
2085 throw "stop execution";
2086 }
2087 sectMax2 += sectBump;
2088 if (draw_intersection <= 1 || !inStepRange) {
2089 continue;
2090 }
2091 lastSect = tIndex;
2092 sectCount += sectBump;
2093 bumpStep = true;
2094 }
2095 if (recType == REC_TYPE_SORT) {
2096 ++sortMax;
2097 if (!draw_sort || !inStepRange) {
2098 continue;
2099 }
2100 lastSort = tIndex;
2101 ++sortCount;
2102 bumpStep = true;
2103 }
2104 if (recType == REC_TYPE_MARK) {
2105 ++markMax;
2106 if (!draw_mark || !inStepRange) {
2107 continue;
2108 }
2109 lastMark = tIndex;
2110 ++markCount;
2111 bumpStep = true;
2112 }
2113 if (bumpStep) {
2114 lastIndex = tIndex;
2115 logStart = test[tIndex + 1];
2116 logRange = records.length / 2;
2117 ++curStep;
2118 }
2119 }
2120 stepMax = (draw_add ? addMax : 0)
2121 + (draw_active ? activeMax : 0)
2122 + (draw_op ? opMax : 0)
2123 + (draw_angle ? angleMax : 0)
2124 + (draw_sort ? sortMax : 0)
2125 + (draw_mark ? markMax : 0)
2126 + (draw_intersection == 2 ? sectMax : draw_intersection == 3 ? sectMax2 : 0);
2127 if (stepMax == 0) {
2128 stepMax = addMax + activeMax + angleMax + opMax + sortMax + markMax;
2129 }
2130 drawnPts = [];
2131 drawnLines = [];
2132 drawnQuads = [];
2133 drawnCubics = [];
2134 focusXmin = focusYmin = Infinity;
2135 focusXmax = focusYmax = -Infinity;
2136 var pathIndex = 0;
2137 var opLetter = 'S';
2138 for (var tIndex = lastIndex; tIndex >= 0; tIndex -= 3) {
2139 var recType = test[tIndex];
2140 var records = test[tIndex + 2];
2141 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
2142 var fragType = records[recordIndex];
2143 if (!typeof fragType == 'number' || fragType < 1 || fragType > FRAG_TYPE_LAST) {
2144 console.log("unknown in range frag type: " + fragType);
2145 throw "stop execution";
2146 }
2147 var frags = records[recordIndex + 1];
2148 focus_enabled = false;
2149 switch (recType) {
2150 case REC_TYPE_COMPUTED:
2151 if (draw_computed == 0) {
2152 continue;
2153 }
2154 ctx.lineWidth = 1;
2155 ctx.strokeStyle = pathIndex == 0 ? "black" : "red";
2156 ctx.fillStyle = "blue";
2157 var drawThis = false;
2158 switch (fragType) {
2159 case PATH_QUAD:
2160 if ((draw_computed & 5) == 1 || ((draw_computed & 4) != 0
2161 && (draw_computed & 1) == pathIndex)) {
2162 drawQuad(frags[0], frags[1], frags[2], frags[3],
2163 frags[4], frags[5]);
2164 drawThis = true;
2165 }
2166 break;
2167 case PATH_CUBIC:
2168 if ((draw_computed & 6) == 2 || ((draw_computed & 4) != 0
2169 && (draw_computed & 1) != pathIndex)) {
2170 drawCubic(frags[0], frags[1], frags[2], frags[3],
2171 frags[4], frags[5], frags[6], frags[7]);
2172 drawThis = true;
2173 }
2174 ++pathIndex;
2175 break;
2176 case COMPUTED_SET_1:
2177 pathIndex = 0;
2178 break;
2179 case COMPUTED_SET_2:
2180 pathIndex = 1;
2181 break;
2182 default:
2183 console.log("unknown REC_TYPE_COMPUTED frag type: " + fragType);
2184 throw "stop execution";
2185 }
2186 if (!drawThis || collect_bounds) {
2187 break;
2188 }
2189 drawCurveSpecials(test, frags, fragType);
2190 break;
2191 case REC_TYPE_PATH:
2192 if (!draw_path) {
2193 continue;
2194 }
2195 var firstPath = tIndex < secondPath;
2196 if ((draw_path & (firstPath ? 1 : 2)) == 0) {
2197 continue;
2198 }
2199 ctx.lineWidth = 1;
2200 ctx.strokeStyle = firstPath ? "black" : "red";
2201 ctx.fillStyle = "blue";
2202 switch (fragType) {
2203 case PATH_LINE:
2204 drawLine(frags[0], frags[1], frags[2], frags[3]);
2205 break;
2206 case PATH_QUAD:
2207 drawQuad(frags[0], frags[1], frags[2], frags[3],
2208 frags[4], frags[5]);
2209 break;
2210 case PATH_CUBIC:
2211 drawCubic(frags[0], frags[1], frags[2], frags[3],
2212 frags[4], frags[5], frags[6], frags[7]);
2213 break;
2214 default:
2215 console.log("unknown REC_TYPE_PATH frag type: " + fragType);
2216 throw "stop execution";
2217 }
2218 if (collect_bounds) {
2219 break;
2220 }
2221 drawCurveSpecials(test, frags, fragType);
2222 break;
2223 case REC_TYPE_OP:
2224 switch (fragType) {
2225 case OP_INTERSECT: opLetter = 'I'; break;
2226 case OP_DIFFERENCE: opLetter = 'D'; break;
2227 case OP_UNION: opLetter = 'U'; break;
2228 case OP_XOR: opLetter = 'X'; break;
2229 default:
2230 console.log("unknown REC_TYPE_OP frag type: " + fragType);
2231 throw "stop execution";
2232 }
2233 break;
2234 case REC_TYPE_ACTIVE:
2235 if (!draw_active || (step_limit > 0 && tIndex < lastActive)) {
2236 continue;
2237 }
2238 var x1 = frags[SPAN_X1];
2239 var y1 = frags[SPAN_Y1];
2240 var x2 = frags[SPAN_X2];
2241 var y2 = frags[SPAN_Y2];
2242 var x3, y3, x3, y4, t1, t2;
2243 ctx.lineWidth = 3;
2244 ctx.strokeStyle = "rgba(0,0,255, 0.3)";
2245 focus_enabled = true;
2246 switch (fragType) {
2247 case ACTIVE_LINE_SPAN:
2248 t1 = frags[SPAN_L_T];
2249 t2 = frags[SPAN_L_TEND];
2250 drawLinePartial(x1, y1, x2, y2, t1, t2);
2251 if (draw_id) {
2252 drawLinePartialID(frags[0], x1, y1, x2, y2, t1, t2);
2253 }
2254 break;
2255 case ACTIVE_QUAD_SPAN:
2256 x3 = frags[SPAN_X3];
2257 y3 = frags[SPAN_Y3];
2258 t1 = frags[SPAN_Q_T];
2259 t2 = frags[SPAN_Q_TEND];
2260 drawQuadPartial(x1, y1, x2, y2, x3, y3, t1, t2);
2261 if (draw_id) {
2262 drawQuadPartialID(frags[0], x1, y1, x2, y2, x3, y3, t1, t2);
2263 }
2264 break;
2265 case ACTIVE_CUBIC_SPAN:
2266 x3 = frags[SPAN_X3];
2267 y3 = frags[SPAN_Y3];
2268 x4 = frags[SPAN_X4];
2269 y4 = frags[SPAN_Y4];
2270 t1 = frags[SPAN_C_T];
2271 t2 = frags[SPAN_C_TEND];
2272 drawCubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2);
2273 if (draw_id) {
2274 drawCubicPartialID(frags[0], x1, y1, x2, y2, x3, y3, x4, y4, t1, t2);
2275 }
2276 break;
2277 default:
2278 console.log("unknown REC_TYPE_ACTIVE frag type: " + fragType);
2279 throw "stop execution";
2280 }
2281 break;
2282 case REC_TYPE_ACTIVE_OP:
2283 if (!draw_op || (step_limit > 0 && tIndex < lastOp)) {
2284 continue;
2285 }
2286 focus_enabled = true;
2287 ctx.lineWidth = 3;
2288 var activeSpan = frags[7] == "1";
2289 ctx.strokeStyle = activeSpan ? "rgba(45,160,0, 0.3)" : "rgba(255,45,0, 0.5)";
2290 var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
2291 drawCurve(curve);
2292 if (draw_op > 1) {
2293 drawArc(curve, false, frags[3], frags[4]);
2294 drawArc(curve, true, frags[5], frags[6]);
2295 }
2296 break;
2297 case REC_TYPE_ADD:
2298 if (!draw_add) {
2299 continue;
2300 }
2301 ctx.lineWidth = 3;
2302 ctx.strokeStyle = closeCount == 0 ? "rgba(0,0,255, 0.3)"
2303 : closeCount == 1 ? "rgba(0,127,0, 0.3)"
2304 : closeCount == 2 ? "rgba(0,127,127, 0.3)"
2305 : closeCount == 3 ? "rgba(127,127,0, 0.3)"
2306 : "rgba(127,0,127, 0.3)";
2307 focus_enabled = true;
2308 switch (fragType) {
2309 case ADD_MOVETO:
2310 break;
2311 case ADD_LINETO:
2312 if (step_limit == 0 || tIndex >= lastAdd) {
2313 drawLine(frags[0], frags[1], frags[2], frags[3]);
2314 }
2315 break;
2316 case ADD_QUADTO:
2317 if (step_limit == 0 || tIndex >= lastAdd) {
2318 drawQuad(frags[0], frags[1], frags[2], frags[3], frags[4], frags[5]);
2319 }
2320 break;
2321 case ADD_CUBICTO:
2322 if (step_limit == 0 || tIndex >= lastAdd) {
2323 drawCubic(frags[0], frags[1], frags[2], frags[3],
2324 frags[4], frags[5], frags[6], frags[7]);
2325 }
2326 break;
2327 case ADD_CLOSE:
2328 ++closeCount;
2329 break;
2330 case ADD_FILL:
2331 break;
2332 default:
2333 console.log("unknown REC_TYPE_ADD frag type: " + fragType);
2334 throw "stop execution";
2335 }
2336 break;
2337 case REC_TYPE_ANGLE:
2338 if (!draw_angle || (step_limit > 0 && tIndex < lastAngle)) {
2339 continue;
2340 }
2341 if (fragType != ANGLE_AFTER && fragType != ANGLE_AFTER2) {
2342 continue;
2343 }
2344 focus_enabled = true;
2345 ctx.lineWidth = 3;
2346 ctx.strokeStyle = "rgba(127,45,127, 0.3)";
2347 var leftCurve, midCurve, rightCurve;
2348 if (fragType == ANGLE_AFTER) {
2349 leftCurve = curvePartialByID(test, frags[0], frags[3], frags[4]);
2350 midCurve = curvePartialByID(test, frags[5], frags[8], frags[9]);
2351 rightCurve = curvePartialByID(test, frags[10], frags[13], frags[14]);
2352 } else {
2353 leftCurve = curvePartialByID(test, frags[0], frags[4], frags[5]);
2354 midCurve = curvePartialByID(test, frags[6], frags[10], frags[11]);
2355 rightCurve = curvePartialByID(test, frags[12], frags[16], frags[17]);
2356 }
2357 drawCurve(leftCurve);
2358 drawCurve(rightCurve);
2359 var inBetween = frags[fragType == ANGLE_AFTER ? 15 : 18] == "T";
2360 ctx.strokeStyle = inBetween ? "rgba(0,160,45, 0.3)" : "rgba(255,0,45, 0.5)";
2361 drawCurve(midCurve);
2362 if (draw_angle > 1) {
2363 drawOrder(leftCurve, 'L');
2364 drawOrder(rightCurve, 'R');
2365 }
2366 break;
2367 case REC_TYPE_SECT:
2368 if (!draw_intersection) {
2369 continue;
2370 }
2371 if (draw_intersection != 1 && (step_limit > 0 && tIndex < lastSect)) {
2372 continue;
2373 }
2374 // draw_intersection == 1 : show all
2375 // draw_intersection == 2 : step == 0 ? show all : show intersection line #step
2376 // draw_intersection == 3 : step == 0 ? show all : show intersection #step
2377 ctx.lineWidth = 1;
2378 ctx.strokeStyle = "rgba(0,0,255, 0.3)";
2379 ctx.fillStyle = "blue";
2380 focus_enabled = true;
2381 var f = [];
2382 var c1s;
2383 var c1l;
2384 var c2s;
2385 var c2l;
2386 switch (fragType) {
2387 case INTERSECT_LINE:
2388 f.push(5, 6, 0, 7);
2389 c1s = 1; c1l = 4; c2s = 8; c2l = 4;
2390 break;
2391 case INTERSECT_LINE_2:
2392 f.push(5, 6, 0, 10);
2393 f.push(8, 9, 7, 15);
2394 c1s = 1; c1l = 4; c2s = 11; c2l = 4;
2395 break;
2396 case INTERSECT_LINE_NO:
2397 c1s = 0; c1l = 4; c2s = 4; c2l = 4;
2398 break;
2399 case INTERSECT_QUAD_LINE:
2400 f.push(7, 8, 0, 9);
2401 c1s = 1; c1l = 6; c2s = 10; c2l = 4;
2402 break;
2403 case INTERSECT_QUAD_LINE_2:
2404 f.push(7, 8, 0, 12);
2405 f.push(10, 11, 9, 17);
2406 c1s = 1; c1l = 6; c2s = 13; c2l = 4;
2407 break;
2408 case INTERSECT_QUAD_LINE_NO:
2409 c1s = 0; c1l = 6; c2s = 6; c2l = 4;
2410 break;
2411 case INTERSECT_QUAD:
2412 f.push(7, 8, 0, 9);
2413 c1s = 1; c1l = 6; c2s = 10; c2l = 6;
2414 break;
2415 case INTERSECT_QUAD_2:
2416 f.push(7, 8, 0, 12);
2417 f.push(10, 11, 9, 19);
2418 c1s = 1; c1l = 6; c2s = 13; c2l = 6;
2419 break;
2420 case INTERSECT_QUAD_NO:
2421 c1s = 0; c1l = 6; c2s = 6; c2l = 6;
2422 break;
2423 case INTERSECT_SELF_CUBIC:
2424 f.push(9, 10, 0, 11);
2425 c1s = 1; c1l = 8; c2s = 0; c2l = 0;
2426 break;
2427 case INTERSECT_SELF_CUBIC_NO:
2428 c1s = 0; c1l = 8; c2s = 0; c2l = 0;
2429 break;
2430 case INTERSECT_CUBIC_LINE:
2431 f.push(9, 10, 0, 11);
2432 c1s = 1; c1l = 8; c2s = 12; c2l = 4;
2433 break;
2434 case INTERSECT_CUBIC_LINE_2:
2435 f.push(9, 10, 0, 14);
2436 f.push(12, 13, 11, 19);
2437 c1s = 1; c1l = 8; c2s = 15; c2l = 4;
2438 break;
2439 case INTERSECT_CUBIC_LINE_3:
2440 f.push(9, 10, 0, 17);
2441 f.push(12, 13, 11, 22);
2442 f.push(15, 16, 14, 23);
2443 c1s = 1; c1l = 8; c2s = 18; c2l = 4;
2444 break;
2445 case INTERSECT_CUBIC_QUAD_NO:
2446 c1s = 0; c1l = 8; c2s = 8; c2l = 6;
2447 break;
2448 case INTERSECT_CUBIC_QUAD:
2449 f.push(9, 10, 0, 11);
2450 c1s = 1; c1l = 8; c2s = 12; c2l = 6;
2451 break;
2452 case INTERSECT_CUBIC_QUAD_2:
2453 f.push(9, 10, 0, 14);
2454 f.push(12, 13, 11, 21);
2455 c1s = 1; c1l = 8; c2s = 15; c2l = 6;
2456 break;
2457 case INTERSECT_CUBIC_QUAD_3:
2458 f.push(9, 10, 0, 17);
2459 f.push(12, 13, 11, 24);
2460 f.push(15, 16, 14, 25);
2461 c1s = 1; c1l = 8; c2s = 18; c2l = 6;
2462 break;
2463 case INTERSECT_CUBIC_QUAD_4:
2464 f.push(9, 10, 0, 20);
2465 f.push(12, 13, 11, 27);
2466 f.push(15, 16, 14, 28);
2467 f.push(18, 19, 17, 29);
2468 c1s = 1; c1l = 8; c2s = 21; c2l = 6;
2469 break;
2470 case INTERSECT_CUBIC_LINE_NO:
2471 c1s = 0; c1l = 8; c2s = 8; c2l = 4;
2472 break;
2473 case INTERSECT_CUBIC:
2474 f.push(9, 10, 0, 11);
2475 c1s = 1; c1l = 8; c2s = 12; c2l = 8;
2476 break;
2477 case INTERSECT_CUBIC_2:
2478 f.push(9, 10, 0, 14);
2479 f.push(12, 13, 11, 23);
2480 c1s = 1; c1l = 8; c2s = 15; c2l = 8;
2481 break;
2482 case INTERSECT_CUBIC_3:
2483 f.push(9, 10, 0, 17);
2484 f.push(12, 13, 11, 26);
2485 f.push(15, 16, 14, 27);
2486 c1s = 1; c1l = 8; c2s = 18; c2l = 8;
2487 break;
2488 case INTERSECT_CUBIC_4:
2489 f.push(9, 10, 0, 20);
2490 f.push(12, 13, 11, 29);
2491 f.push(15, 16, 14, 30);
2492 f.push(18, 19, 17, 31);
2493 c1s = 1; c1l = 8; c2s = 21; c2l = 8;
2494 break;
2495 case INTERSECT_CUBIC_NO:
2496 c1s = 0; c1l = 8; c2s = 8; c2l = 8;
2497 break;
2498 default:
2499 console.log("unknown REC_TYPE_SECT frag type: " + fragType);
2500 throw "stop execution";
2501 }
2502 if (draw_intersection != 1) {
2503 var id = -1;
2504 var curve;
2505 switch (c1l) {
2506 case 4:
2507 drawLine(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3]);
2508 if (draw_id) {
2509 curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3]];
2510 id = idByCurve(test, curve, PATH_LINE);
2511 }
2512 break;
2513 case 6:
2514 drawQuad(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
2515 frags[c1s + 4], frags[c1s + 5]);
2516 if (draw_id) {
2517 curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
2518 frags[c1s + 4], frags[c1s + 5]];
2519 id = idByCurve(test, curve, PATH_QUAD);
2520 }
2521 break;
2522 case 8:
2523 drawCubic(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
2524 frags[c1s + 4], frags[c1s + 5], frags[c1s + 6], frags[c1s + 7]);
2525 if (draw_id) {
2526 curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
2527 frags[c1s + 4], frags[c1s + 5], frags[c1s + 6], frags[c1s + 7]];
2528 id = idByCurve(test, curve, PATH_CUBIC);
2529 }
2530 break;
2531 }
2532 if (id >= 0) {
2533 drawID(curve, id);
2534 }
2535 id = -1;
2536 switch (c2l) {
2537 case 0:
2538 break;
2539 case 4:
2540 drawLine(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3]);
2541 if (draw_id) {
2542 curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3]];
2543 id = idByCurve(test, curve, PATH_LINE);
2544 }
2545 break;
2546 case 6:
2547 drawQuad(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
2548 frags[c2s + 4], frags[c2s + 5]);
2549 if (draw_id) {
2550 curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
2551 frags[c2s + 4], frags[c2s + 5]];
2552 id = idByCurve(test, curve, PATH_QUAD);
2553 }
2554 break;
2555 case 8:
2556 drawCubic(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
2557 frags[c2s + 4], frags[c2s + 5], frags[c2s + 6], frags[c2s + 7]);
2558 if (draw_id) {
2559 curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
2560 frags[c2s + 4], frags[c2s + 5], frags[c2s + 6], frags[c2s + 7]];
2561 id = idByCurve(test, curve, PATH_CUBIC);
2562 }
2563 break;
2564 }
2565 if (id >= 0) {
2566 drawID(curve, id);
2567 }
2568 }
2569 if (collect_bounds) {
2570 break;
2571 }
2572 for (var idx = 0; idx < f.length; idx += 4) {
2573 if (draw_intersection != 3 || idx == lastSect - tIndex) {
2574 drawPoint(frags[f[idx]], frags[f[idx + 1]], true);
2575 }
2576 }
2577 if (!draw_intersectT) {
2578 break;
2579 }
2580 ctx.fillStyle = "red";
2581 for (var idx = 0; idx < f.length; idx += 4) {
2582 if (draw_intersection != 3 || idx == lastSect - tIndex) {
2583 drawTAtPointUp(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 2]]);
2584 drawTAtPointDown(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 3]]);
2585 }
2586 }
2587 break;
2588 case REC_TYPE_SORT:
2589 if (!draw_sort || (step_limit > 0 && tIndex < lastSort)) {
2590 continue;
2591 }
2592 ctx.lineWidth = 3;
2593 ctx.strokeStyle = "rgba(127,127,0, 0.5)";
2594 focus_enabled = true;
2595 switch (fragType) {
2596 case SORT_UNARY:
2597 case SORT_BINARY:
2598 var curve = curvePartialByID(test, frags[0], frags[6], frags[8]);
2599 drawCurve(curve);
2600 break;
2601 default:
2602 console.log("unknown REC_TYPE_SORT frag type: " + fragType);
2603 throw "stop execution";
2604 }
2605 break;
2606 case REC_TYPE_MARK:
2607 if (!draw_mark || (step_limit > 0 && tIndex < lastMark)) {
2608 continue;
2609 }
2610 ctx.lineWidth = 3;
2611 ctx.strokeStyle = fragType >= MARK_DONE_LINE ?
2612 "rgba(127,0,127, 0.5)" : "rgba(127,127,0, 0.5)";
2613 focus_enabled = true;
2614 switch (fragType) {
2615 case MARK_LINE:
2616 case MARK_DONE_LINE:
2617 case MARK_UNSORTABLE_LINE:
2618 case MARK_SIMPLE_LINE:
2619 case MARK_SIMPLE_DONE_LINE:
2620 case MARK_DONE_UNARY_LINE:
2621 drawLinePartial(frags[1], frags[2], frags[3], frags[4],
2622 frags[5], frags[9]);
2623 if (draw_id) {
2624 drawLinePartialID(frags[0], frags[1], frags[2], frags[3], frags[4],
2625 frags[5], frags[9]);
2626 }
2627 break;
2628 case MARK_QUAD:
2629 case MARK_DONE_QUAD:
2630 case MARK_UNSORTABLE_QUAD:
2631 case MARK_SIMPLE_QUAD:
2632 case MARK_SIMPLE_DONE_QUAD:
2633 case MARK_DONE_UNARY_QUAD:
2634 drawQuadPartial(frags[1], frags[2], frags[3], frags[4],
2635 frags[5], frags[6], frags[7], frags[11]);
2636 if (draw_id) {
2637 drawQuadPartialID(frags[0], frags[1], frags[2], frags[3], frags[4],
2638 frags[5], frags[6], frags[7], frags[11]);
2639 }
2640 break;
2641 case MARK_CUBIC:
2642 case MARK_DONE_CUBIC:
2643 case MARK_UNSORTABLE_CUBIC:
2644 case MARK_SIMPLE_CUBIC:
2645 case MARK_SIMPLE_DONE_CUBIC:
2646 case MARK_DONE_UNARY_CUBIC:
2647 drawCubicPartial(frags[1], frags[2], frags[3], frags[4],
2648 frags[5], frags[6], frags[7], frags[8], frags[9], frags[13]);
2649 if (draw_id) {
2650 drawCubicPartialID(frags[0], frags[1], frags[2], frags[3], frags[4],
2651 frags[5], frags[6], frags[7], frags[8], frags[9], frags[13]);
2652 }
2653 break;
2654 case MARK_ANGLE_LAST:
2655 // FIXME: ignored for now
2656 break;
2657 default:
2658 console.log("unknown REC_TYPE_MARK frag type: " + fragType);
2659 throw "stop execution";
2660 }
2661 break;
2662 default:
2663 continue;
2664 }
2665 }
2666 switch (recType) {
2667 case REC_TYPE_SORT:
2668 if (!draw_sort || (step_limit > 0 && tIndex < lastSort)) {
2669 break;
2670 }
2671 var angles = []; // use tangent lines to describe arcs
2672 var windFrom = [];
2673 var windTo = [];
2674 var opp = [];
2675 var minXY = Number.MAX_VALUE;
2676 var partial;
2677 focus_enabled = true;
2678 var someUnsortable = false;
2679 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
2680 var fragType = records[recordIndex];
2681 var frags = records[recordIndex + 1];
2682 var unsortable = (fragType == SORT_UNARY && frags[14]) ||
2683 (fragType == SORT_BINARY && frags[16]);
2684 someUnsortable |= unsortable;
2685 switch (fragType) {
2686 case SORT_UNARY:
2687 case SORT_BINARY:
2688 partial = curvePartialByID(test, frags[0], frags[6], frags[8]);
2689 break;
2690 default:
2691 console.log("unknown REC_TYPE_SORT frag type: " + fragType);
2692 throw "stop execution";
2693 }
2694 var dx = boundsWidth(partial);
2695 var dy = boundsHeight(partial);
2696 minXY = Math.min(minXY, dx * dx + dy * dy);
2697 if (collect_bounds) {
2698 continue;
2699 }
2700 angles.push(tangent(partial));
2701 var from = frags[12];
2702 var to = frags[12];
2703 var sgn = frags[10];
2704 if (sgn < 0) {
2705 from -= frags[11];
2706 } else if (sgn > 0) {
2707 to -= frags[11];
2708 }
2709 windFrom.push(from + (unsortable ? "!" : ""));
2710 windTo.push(to + (unsortable ? "!" : ""));
2711 opp.push(fragType == SORT_BINARY);
2712 if (draw_sort == 1) {
2713 drawOrder(partial, frags[12]);
2714 } else {
2715 drawOrder(partial, (recordIndex / 2) + 1);
2716 }
2717 }
2718 var radius = Math.sqrt(minXY) / 2 * scale;
2719 radius = Math.min(50, radius);
2720 var scaledRadius = radius / scale;
2721 var centerX = partial[0];
2722 var centerY = partial[1];
2723 if (collect_bounds) {
2724 if (focus_enabled) {
2725 focusXmin = Math.min(focusXmin, centerX - scaledRadius);
2726 focusYmin = Math.min(focusYmin, centerY - scaledRadius);
2727 focusXmax = Math.max(focusXmax, centerX + scaledRadius);
2728 focusYmax = Math.max(focusYmax, centerY + scaledRadius);
2729 }
2730 break;
2731 }
2732 break;
2733 default:
2734 break;
2735 }
2736 }
2737 if (collect_bounds) {
2738 return;
2739 }
2740 if (draw_log && logStart >= 0) {
2741 ctx.font = "normal 10px Arial";
2742 ctx.textAlign = "left";
2743 ctx.beginPath();
2744 var top = screenHeight - 20 - (logRange + 2) * 10;
2745 ctx.rect(50, top, screenWidth - 100, (logRange + 2) * 10);
2746 ctx.fillStyle = "white";
2747 ctx.fill();
2748 ctx.fillStyle = "rgba(0,0,0, 0.5)";
2749 if (logStart > 0) {
2750 ctx.fillText(lines[logStart - 1], 50, top + 8);
2751 }
2752 ctx.fillStyle = "black";
2753 for (var idx = 0; idx < logRange; ++idx) {
2754 ctx.fillText(lines[logStart + idx], 50, top + 18 + 10 * idx);
2755 }
2756 ctx.fillStyle = "rgba(0,0,0, 0.5)";
2757 if (logStart + logRange < lines.length) {
2758 ctx.fillText(lines[logStart + logRange], 50, top + 18 + 10 * logRange);
2759 }
2760 }
2761 if (draw_legend) {
2762 var pos = 0;
2763 var drawSomething = draw_add | draw_active | draw_sort | draw_mark;
2764 // drawBox(pos++, "yellow", "black", opLetter, true, '');
2765 drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_intersection > 1 ? sectCount : sectMax2, draw_intersection, intersectionKey);
2766 drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_add ? addCount : addMax, draw_add, addKey);
2767 drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_active ? activeCount : activeMax, draw_active, activeKey);
2768 drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_angle ? angleCount : angleMax, draw_angle, angleKey);
2769 drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_op ? opCount : opMax, draw_op, opKey);
2770 drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_sort ? sortCount : sortMax, draw_sort, sortKey);
2771 drawBox(pos++, "rgba(127,0,127, 0.3)", "black", draw_mark ? markCount : markMax, draw_mark, markKey);
2772 drawBox(pos++, "black", "white",
2773 (new Array('P', 'P1', 'P2', 'P'))[draw_path], draw_path != 0, pathKey);
2774 drawBox(pos++, "rgba(0,63,0, 0.7)", "white",
2775 (new Array('Q', 'Q', 'C', 'QC', 'Qc', 'Cq'))[draw_computed],
2776 draw_computed != 0, computedKey);
2777 drawBox(pos++, "green", "black", step_limit, drawSomething, '');
2778 drawBox(pos++, "green", "black", stepMax, drawSomething, '');
2779 drawBox(pos++, "rgba(255,0,0, 0.6)", "black", lastIndex, drawSomething & draw_log, '');
2780 drawBox(pos++, "rgba(255,0,0, 0.6)", "black", test.length - 1, drawSomething & draw_log, '');
2781 if (curve_t) {
2782 drawCurveTControl();
2783 }
2784 ctx.font = "normal 20px Arial";
2785 ctx.fillStyle = "rgba(0,0,0, 0.3)";
2786 ctx.textAlign = "right";
2787 ctx.fillText(scale.toFixed(decimal_places) + 'x' , screenWidth - 10, screenHeight - 5);
2788 }
2789 if (draw_hints) {
2790 ctx.font = "normal 10px Arial";
2791 ctx.fillStyle = "rgba(0,0,0, 0.5)";
2792 ctx.textAlign = "right";
2793 var y = 4;
2794 ctx.fillText("control lines : " + controlLinesKey, ctx.screenWidthwidth - 10, pos * 50 + y++ * 10);
2795 ctx.fillText("curve t : " + curveTKey, screenWidth - 10, pos * 50 + y++ * 10);
2796 ctx.fillText("deriviatives : " + deriviativesKey, screenWidth - 10, pos * 50 + y++ * 10);
2797 ctx.fillText("intersect t : " + intersectTKey, screenWidth - 10, pos * 50 + y++ * 10);
2798 ctx.fillText("hodo : " + hodoKey, screenWidth - 10, pos * 50 + y++ * 10);
2799 ctx.fillText("log : " + logKey, screenWidth - 10, pos * 50 + y++ * 10);
2800 ctx.fillText("log curve : " + logCurvesKey, screenWidth - 10, pos * 50 + y++ * 10);
2801 ctx.fillText("mid point : " + midpointKey, screenWidth - 10, pos * 50 + y++ * 10);
2802 ctx.fillText("points : " + ptsKey, screenWidth - 10, pos * 50 + y++ * 10);
2803 ctx.fillText("sequence : " + sequenceKey, screenWidth - 10, pos * 50 + y++ * 10);
2804 ctx.fillText("xy : " + xyKey, screenWidth - 10, pos * 50 + y++ * 10);
2805 }
2806}
2807
2808function drawBox(y, backC, foreC, str, enable, label) {
2809 ctx.beginPath();
2810 ctx.fillStyle = backC;
2811 ctx.rect(screenWidth - 40, y * 50 + 10, 40, 30);
2812 ctx.fill();
2813 ctx.font = "normal 16px Arial";
2814 ctx.fillStyle = foreC;
2815 ctx.textAlign = "center";
2816 ctx.fillText(str, screenWidth - 20, y * 50 + 32);
2817 if (!enable) {
2818 ctx.fillStyle = "rgba(255,255,255, 0.5)";
2819 ctx.fill();
2820 }
2821 if (label != '') {
2822 ctx.font = "normal 9px Arial";
2823 ctx.fillStyle = "black";
2824 ctx.fillText(label, screenWidth - 47, y * 50 + 40);
2825 }
2826}
2827
2828function drawCurveTControl() {
2829 ctx.lineWidth = 2;
2830 ctx.strokeStyle = "rgba(0,0,0, 0.3)";
2831 ctx.beginPath();
2832 ctx.rect(screenWidth - 80, 40, 28, screenHeight - 80);
2833 ctx.stroke();
2834 var ty = 40 + curveT * (screenHeight - 80);
2835 ctx.beginPath();
2836 ctx.moveTo(screenWidth - 80, ty);
2837 ctx.lineTo(screenWidth - 85, ty - 5);
2838 ctx.lineTo(screenWidth - 85, ty + 5);
2839 ctx.lineTo(screenWidth - 80, ty);
2840 ctx.fillStyle = "rgba(0,0,0, 0.6)";
2841 ctx.fill();
2842 var num = curveT.toFixed(decimal_places);
2843 ctx.font = "normal 10px Arial";
2844 ctx.textAlign = "left";
2845 ctx.fillText(num, screenWidth - 78, ty);
2846}
2847
2848function ptInTControl() {
2849 var e = window.event;
2850 var tgt = e.target || e.srcElement;
2851 var left = tgt.offsetLeft;
2852 var top = tgt.offsetTop;
2853 var x = (e.clientX - left);
2854 var y = (e.clientY - top);
2855 if (x < screenWidth - 80 || x > screenWidth - 50) {
2856 return false;
2857 }
2858 if (y < 40 || y > screenHeight - 80) {
2859 return false;
2860 }
2861 curveT = (y - 40) / (screenHeight - 120);
2862 if (curveT < 0 || curveT > 1) {
2863 throw "stop execution";
2864 }
2865 return true;
2866}
2867
2868function drawTop() {
2869 if (tests[testIndex] == null) {
2870 var str = testDivs[testIndex].textContent;
2871 parse_all(str);
2872 var title = testDivs[testIndex].id.toString();
2873 testTitles[testIndex] = title;
2874 }
2875 init(tests[testIndex]);
2876 redraw();
2877}
2878
2879function redraw() {
2880 if (focus_on_selection) {
2881 collect_bounds = true;
2882 draw(tests[testIndex], testLines[testIndex], testTitles[testIndex]);
2883 collect_bounds = false;
2884 if (focusXmin < focusXmax && focusYmin < focusYmax) {
2885 setScale(focusXmin, focusXmax, focusYmin, focusYmax);
2886 }
2887 }
2888 ctx.beginPath();
2889 ctx.fillStyle = "white";
2890 ctx.rect(0, 0, screenWidth, screenHeight);
2891 ctx.fill();
2892 draw(tests[testIndex], testLines[testIndex], testTitles[testIndex]);
2893}
2894
2895function dumpCurvePartial(test, id, t0, t1) {
2896 var curve = curveByID(test, id);
2897 var name = ["line", "quad", "cubic"][curve.length / 2 - 2];
2898 console.log("id=" + id + " " + name + "=" + curveToString(curve)
2899 + " t0=" + t0 + " t1=" + t1
2900 + " partial=" + curveToString(curvePartialByID(test, id, t0, t1)));
2901}
2902
2903function dumpAngleTest(test, id, t0, t1) {
2904 var curve = curveByID(test, id);
2905 console.log(" { {" + curveToString(curve) + "}, "
2906 + curve.length / 2 + ", " + t0 + ", " + t1 + ", {} }, //");
2907}
2908
2909function dumpLogToConsole() {
2910 if (logStart < 0) {
2911 return;
2912 }
2913 var test = tests[testIndex];
2914 var recType = REC_TYPE_UNKNOWN;
2915 var records;
2916 for (var index = 0; index < test.length; index += 3) {
2917 var lastLineNo = test[index + 1];
2918 if (lastLineNo >= logStart && lastLineNo < logStart + logRange) {
2919 recType = test[index];
2920 records = test[index + 2];
2921 break;
2922 }
2923 }
2924 if (recType == REC_TYPE_UNKNOWN) {
2925 return;
2926 }
2927 var lines = testLines[testIndex];
2928 for (var idx = 0; idx < logRange; ++idx) {
2929 var line = lines[logStart + idx];
2930 console.log(line);
2931 for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
2932 var fragType = records[recordIndex];
2933 var frags = records[recordIndex + 1];
2934 if (recType == REC_TYPE_ANGLE && fragType == ANGLE_AFTER) {
2935 dumpCurvePartial(test, frags[0], frags[3], frags[4]);
2936 dumpCurvePartial(test, frags[5], frags[8], frags[9]);
2937 dumpCurvePartial(test, frags[10], frags[13], frags[14]);
2938 console.log("\nstatic IntersectData intersectDataSet[] = {");
2939 dumpAngleTest(test, frags[0], frags[3], frags[4]);
2940 dumpAngleTest(test, frags[5], frags[8], frags[9]);
2941 dumpAngleTest(test, frags[10], frags[13], frags[14]);
2942 console.log("};");
2943 } else if (recType == REC_TYPE_ANGLE && fragType == ANGLE_AFTER2) {
2944 dumpCurvePartial(test, frags[0], frags[4], frags[5]);
2945 dumpCurvePartial(test, frags[6], frags[10], frags[11]);
2946 dumpCurvePartial(test, frags[12], frags[16], frags[17]);
2947 console.log("\nstatic IntersectData intersectDataSet[] = { //");
2948 dumpAngleTest(test, frags[0], frags[4], frags[5]);
2949 dumpAngleTest(test, frags[6], frags[10], frags[11]);
2950 dumpAngleTest(test, frags[12], frags[16], frags[17]);
2951 console.log("}; //");
2952 }
2953 }
2954 }
2955}
2956
2957var activeKey = 'a';
2958var pathKey = 'b';
2959var pathBackKey = 'B';
2960var centerKey = 'c';
2961var addKey = 'd';
2962var deriviativesKey = 'f';
2963var angleKey = 'g';
2964var angleBackKey = 'G';
2965var hodoKey = 'h';
2966var intersectionKey = 'i';
2967var intersectionBackKey = 'I';
2968var sequenceKey = 'j';
2969var midpointKey = 'k';
2970var logKey = 'l';
2971var logToConsoleKey = 'L';
2972var markKey = 'm';
2973var sortKey = 'o';
2974var opKey = 'p';
2975var opBackKey = 'P';
2976var computedKey = 'q';
2977var computedBackKey = 'Q';
2978var stepKey = 's';
2979var stepBackKey = 'S';
2980var intersectTKey = 't';
2981var curveTKey = 'u';
2982var controlLinesBackKey = 'V';
2983var controlLinesKey = 'v';
2984var ptsKey = 'x';
2985var xyKey = 'y';
2986var logCurvesKey = 'z';
2987var focusKey = '`';
2988var idKey = '.';
2989var retinaKey = '\\';
2990
2991function doKeyPress(evt) {
2992 var char = String.fromCharCode(evt.charCode);
2993 var focusWasOn = false;
2994 switch (char) {
2995 case '0':
2996 case '1':
2997 case '2':
2998 case '3':
2999 case '4':
3000 case '5':
3001 case '6':
3002 case '7':
3003 case '8':
3004 case '9':
3005 decimal_places = char - '0';
3006 redraw();
3007 break;
3008 case activeKey:
3009 draw_active ^= true;
3010 redraw();
3011 break;
3012 case addKey:
3013 draw_add ^= true;
3014 redraw();
3015 break;
3016 case angleKey:
3017 draw_angle = (draw_angle + 1) % 3;
3018 redraw();
3019 break;
3020 case angleBackKey:
3021 draw_angle = (draw_angle + 2) % 3;
3022 redraw();
3023 break;
3024 case centerKey:
3025 setScale(xmin, xmax, ymin, ymax);
3026 redraw();
3027 break;
3028 case controlLinesBackKey:
3029 control_lines = (control_lines + 3) % 4;
3030 redraw();
3031 break;
3032 case controlLinesKey:
3033 control_lines = (control_lines + 1) % 4;
3034 redraw();
3035 break;
3036 case computedBackKey:
3037 draw_computed = (draw_computed + 5) % 6;
3038 redraw();
3039 break;
3040 case computedKey:
3041 draw_computed = (draw_computed + 1) % 6;
3042 redraw();
3043 break;
3044 case curveTKey:
3045 curve_t ^= true;
3046 if (curve_t) {
3047 draw_legend = true;
3048 }
3049 redraw();
3050 break;
3051 case deriviativesKey:
3052 draw_deriviatives = (draw_deriviatives + 1) % 3;
3053 redraw();
3054 break;
3055 case focusKey:
3056 focus_on_selection ^= true;
3057 setScale(xmin, xmax, ymin, ymax);
3058 redraw();
3059 break;
3060 case hodoKey:
3061 draw_hodo = (draw_hodo + 1) % 4;
3062 redraw();
3063 break;
3064 case idKey:
3065 draw_id ^= true;
3066 redraw();
3067 break;
3068 case intersectionBackKey:
3069 draw_intersection = (draw_intersection + 3) % 4;
3070 redraw();
3071 break;
3072 case intersectionKey:
3073 draw_intersection = (draw_intersection + 1) % 4;
3074 redraw();
3075 break;
3076 case intersectTKey:
3077 draw_intersectT ^= true;
3078 redraw();
3079 break;
3080 case logCurvesKey:
3081 logCurves(tests[testIndex]);
3082 break;
3083 case logKey:
3084 draw_log ^= true;
3085 redraw();
3086 break;
3087 case logToConsoleKey:
3088 if (draw_log) {
3089 dumpLogToConsole();
3090 }
3091 break;
3092 case markKey:
3093 draw_mark ^= true;
3094 redraw();
3095 break;
3096 case midpointKey:
3097 draw_midpoint ^= true;
3098 redraw();
3099 break;
3100 case opKey:
3101 draw_op = (draw_op + 1) % 3;
3102 redraw();
3103 break;
3104 case opBackKey:
3105 draw_op = (draw_op + 2) % 3;
3106 redraw();
3107 break;
3108 case pathKey:
3109 draw_path = (draw_path + 1) % 4;
3110 redraw();
3111 break;
3112 case pathBackKey:
3113 draw_path = (draw_path + 3) % 4;
3114 redraw();
3115 break;
3116 case ptsKey:
3117 pt_labels = (pt_labels + 1) % 3;
3118 redraw();
3119 break;
3120 case retinaKey:
3121 retina_scale ^= true;
3122 drawTop();
3123 break;
3124 case sequenceKey:
3125 draw_sequence ^= true;
3126 redraw();
3127 break;
3128 case sortKey:
3129 draw_sort = (draw_sort + 1) % 3;
3130 drawTop();
3131 break;
3132 case stepKey:
3133 step_limit++;
3134 if (step_limit > stepMax) {
3135 step_limit = stepMax;
3136 }
3137 redraw();
3138 break;
3139 case stepBackKey:
3140 step_limit--;
3141 if (step_limit < 0) {
3142 step_limit = 0;
3143 }
3144 redraw();
3145 break;
3146 case xyKey:
3147 debug_xy = (debug_xy + 1) % 3;
3148 redraw();
3149 break;
3150 case '-':
3151 focusWasOn = focus_on_selection;
3152 if (focusWasOn) {
3153 focus_on_selection = false;
3154 scale /= 1.2;
3155 } else {
3156 scale /= 2;
3157 calcLeftTop();
3158 }
3159 redraw();
3160 focus_on_selection = focusWasOn;
3161 break;
3162 case '=':
3163 case '+':
3164 focusWasOn = focus_on_selection;
3165 if (focusWasOn) {
3166 focus_on_selection = false;
3167 scale *= 1.2;
3168 } else {
3169 scale *= 2;
3170 calcLeftTop();
3171 }
3172 redraw();
3173 focus_on_selection = focusWasOn;
3174 break;
3175 case '?':
3176 draw_hints ^= true;
3177 if (draw_hints && !draw_legend) {
3178 draw_legend = true;
3179 }
3180 redraw();
3181 break;
3182 case '/':
3183 draw_legend ^= true;
3184 redraw();
3185 break;
3186 }
3187}
3188
3189function doKeyDown(evt) {
3190 var char = evt.keyCode;
3191 var preventDefault = false;
3192 switch (char) {
3193 case 37: // left arrow
3194 if (evt.shiftKey) {
3195 testIndex -= 9;
3196 }
3197 if (--testIndex < 0)
3198 testIndex = tests.length - 1;
3199 drawTop();
3200 preventDefault = true;
3201 break;
3202 case 39: // right arrow
3203 if (evt.shiftKey) {
3204 testIndex += 9;
3205 }
3206 if (++testIndex >= tests.length)
3207 testIndex = 0;
3208 drawTop();
3209 preventDefault = true;
3210 break;
3211 }
3212 if (preventDefault) {
3213 evt.preventDefault();
3214 return false;
3215 }
3216 return true;
3217}
3218
3219(function() {
3220 var hidden = "hidden";
3221
3222 // Standards:
3223 if (hidden in document)
3224 document.addEventListener("visibilitychange", onchange);
3225 else if ((hidden = "mozHidden") in document)
3226 document.addEventListener("mozvisibilitychange", onchange);
3227 else if ((hidden = "webkitHidden") in document)
3228 document.addEventListener("webkitvisibilitychange", onchange);
3229 else if ((hidden = "msHidden") in document)
3230 document.addEventListener("msvisibilitychange", onchange);
3231 // IE 9 and lower:
3232 else if ('onfocusin' in document)
3233 document.onfocusin = document.onfocusout = onchange;
3234 // All others:
3235 else
3236 window.onpageshow = window.onpagehide
3237 = window.onfocus = window.onblur = onchange;
3238
3239 function onchange (evt) {
3240 var v = 'visible', h = 'hidden',
3241 evtMap = {
3242 focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
3243 };
3244
3245 evt = evt || window.event;
3246 if (evt.type in evtMap)
3247 document.body.className = evtMap[evt.type];
3248 else
3249 document.body.className = this[hidden] ? "hidden" : "visible";
3250 }
3251})();
3252
3253function calcXY() {
3254 var e = window.event;
3255 var tgt = e.target || e.srcElement;
3256 var left = tgt.offsetLeft;
3257 var top = tgt.offsetTop;
3258 mouseX = (e.clientX - left) / scale + srcLeft;
3259 mouseY = (e.clientY - top) / scale + srcTop;
3260}
3261
3262function calcLeftTop() {
3263 srcLeft = mouseX - screenWidth / 2 / scale;
3264 srcTop = mouseY - screenHeight / 2 / scale;
3265}
3266
3267var disableClick = false;
3268
3269function handleMouseClick() {
3270 if (disableClick) {
3271 return;
3272 }
3273 if (!curve_t || !ptInTControl()) {
3274 calcXY();
3275 calcLeftTop();
3276 }
3277 redraw();
3278// if (!curve_t || !ptInTControl()) {
3279// mouseX = screenWidth / 2 / scale + srcLeft;
3280// mouseY = screenHeight / 2 / scale + srcTop;
3281// }
3282}
3283
3284function handleMouseOver() {
3285 calcXY();
3286 if (debug_xy != 2) {
3287 return;
3288 }
3289 var num = mouseX.toFixed(decimal_places) + ", " + mouseY.toFixed(decimal_places);
3290 ctx.beginPath();
3291 ctx.rect(300,100,num.length * 6,10);
3292 ctx.fillStyle="white";
3293 ctx.fill();
3294 ctx.font = "normal 10px Arial";
3295 ctx.fillStyle="black";
3296 ctx.textAlign = "left";
3297 ctx.fillText(num, 300, 108);
3298}
3299
3300function start() {
3301 for (var i = 0; i < testDivs.length; ++i) {
3302 tests[i] = null;
3303 }
3304 testIndex = 0;
3305 drawTop();
3306 window.addEventListener('keypress', doKeyPress, true);
3307 window.addEventListener('keydown', doKeyDown, true);
3308 window.onresize = function() {
3309 drawTop();
3310 }
3311 /*
3312 window.onpagehide = function() {
3313 disableClick = true;
3314 }
3315 */
3316 window.onpageshow = function () {
3317 disableClick = false;
3318 }
3319}
3320
3321</script>
3322</head>
3323
3324<body onLoad="start();">
3325<canvas id="canvas" width="750" height="500"
3326 onmousemove="handleMouseOver()"
3327 onclick="handleMouseClick()"
3328 ></canvas >
3329</body>
3330</html>