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