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