12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457 |
- <!DOCTYPE html>
- <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta charset="utf-8" />
- <title></title>
- <div style="height:0">
- <div id="cubics">
- {{{fX=124.70011901855469 fY=9.3718261718750000 } {fX=124.66775026544929 fY=9.3744316215161234 } {fX=124.63530969619751 fY=9.3770743012428284 }{fX=124.60282897949219 fY=9.3797206878662109 } id=10
- {{{fX=124.70011901855469 fY=9.3718004226684570 } {fX=124.66775026544929 fY=9.3744058723095804 } {fX=124.63530969619751 fY=9.3770485520362854 } {fX=124.60282897949219 fY=9.3796949386596680 } id=1
- {{{fX=124.70011901855469 fY=9.3718004226684570 } {fX=124.66786243087600 fY=9.3743968522034287 } {fX=124.63553249625420 fY=9.3770303056986286 } {fX=124.60316467285156 fY=9.3796672821044922 } id=2
- </div>
- </div>
- <script type="text/javascript">
- var testDivs = [
- cubics,
- ];
- var decimal_places = 3;
- var tests = [];
- var testTitles = [];
- var testIndex = 0;
- var ctx;
- var subscale = 1;
- var xmin, xmax, ymin, ymax;
- var hscale, vscale;
- var hinitScale, vinitScale;
- var uniformScale = true;
- var mouseX, mouseY;
- var mouseDown = false;
- var srcLeft, srcTop;
- var screenWidth, screenHeight;
- var drawnPts;
- var curveT = 0;
- var curveW = -1;
- var lastX, lastY;
- var activeCurve = [];
- var activePt;
- var ids = [];
- var focus_on_selection = 0;
- var draw_t = false;
- var draw_w = false;
- var draw_closest_t = false;
- var draw_cubic_red = false;
- var draw_derivative = false;
- var draw_endpoints = 2;
- var draw_id = 0;
- var draw_midpoint = 0;
- var draw_mouse_xy = false;
- var draw_order = false;
- var draw_point_xy = false;
- var draw_ray_intersect = false;
- var draw_quarterpoint = 0;
- var draw_tangents = 1;
- var draw_sortpoint = 0;
- var retina_scale = !!window.devicePixelRatio;
- function parse(test, title) {
- var curveStrs = test.split("{{");
- var pattern = /-?\d+\.*\d*e?-?\d*/g;
- var curves = [];
- for (var c in curveStrs) {
- var curveStr = curveStrs[c];
- var idPart = curveStr.split("id=");
- var id = -1;
- if (idPart.length == 2) {
- id = parseInt(idPart[1]);
- curveStr = idPart[0];
- }
- var points = curveStr.match(pattern);
- var pts = [];
- for (var wd in points) {
- var num = parseFloat(points[wd]);
- if (isNaN(num)) continue;
- pts.push(num);
- }
- if (pts.length > 2) {
- curves.push(pts);
- }
- if (id >= 0) {
- ids.push(id);
- ids.push(pts);
- }
- }
- if (curves.length >= 1) {
- tests.push(curves);
- testTitles.push(title);
- }
- }
- function init(test) {
- var canvas = document.getElementById('canvas');
- if (!canvas.getContext) return;
- ctx = canvas.getContext('2d');
- var resScale = retina_scale && window.devicePixelRatio ? window.devicePixelRatio : 1;
- var unscaledWidth = window.innerWidth - 20;
- var unscaledHeight = window.innerHeight - 20;
- screenWidth = unscaledWidth;
- screenHeight = unscaledHeight;
- canvas.width = unscaledWidth * resScale;
- canvas.height = unscaledHeight * resScale;
- canvas.style.width = unscaledWidth + 'px';
- canvas.style.height = unscaledHeight + 'px';
- if (resScale != 1) {
- ctx.scale(resScale, resScale);
- }
- xmin = Infinity;
- xmax = -Infinity;
- ymin = Infinity;
- ymax = -Infinity;
- for (var curves in test) {
- var curve = test[curves];
- var last = curve.length - (curve.length % 2 == 1 ? 1 : 0);
- for (var idx = 0; idx < last; idx += 2) {
- xmin = Math.min(xmin, curve[idx]);
- xmax = Math.max(xmax, curve[idx]);
- ymin = Math.min(ymin, curve[idx + 1]);
- ymax = Math.max(ymax, curve[idx + 1]);
- }
- }
- xmin -= Math.min(1, Math.max(xmax - xmin, ymax - ymin));
- var testW = xmax - xmin;
- var testH = ymax - ymin;
- subscale = 1;
- while (testW * subscale < 0.1 && testH * subscale < 0.1) {
- subscale *= 10;
- }
- while (testW * subscale > 10 && testH * subscale > 10) {
- subscale /= 10;
- }
- setScale(xmin, xmax, ymin, ymax);
- mouseX = (screenWidth / 2) / hscale + srcLeft;
- mouseY = (screenHeight / 2) / vscale + srcTop;
- hinitScale = hscale;
- vinitScale = vscale;
- }
- function setScale(x0, x1, y0, y1) {
- var srcWidth = x1 - x0;
- var srcHeight = y1 - y0;
- var usableWidth = screenWidth;
- var xDigits = Math.ceil(Math.log(Math.abs(xmax)) / Math.log(10));
- var yDigits = Math.ceil(Math.log(Math.abs(ymax)) / Math.log(10));
- usableWidth -= (xDigits + yDigits) * 10;
- usableWidth -= decimal_places * 10;
- hscale = usableWidth / srcWidth;
- vscale = screenHeight / srcHeight;
- if (uniformScale) {
- hscale = Math.min(hscale, vscale);
- vscale = hscale;
- }
- var hinvScale = 1 / hscale;
- var vinvScale = 1 / vscale;
- var sxmin = x0 - hinvScale * 5;
- var symin = y0 - vinvScale * 10;
- var sxmax = x1 + hinvScale * (6 * decimal_places + 10);
- var symax = y1 + vinvScale * 10;
- srcWidth = sxmax - sxmin;
- srcHeight = symax - symin;
- hscale = usableWidth / srcWidth;
- vscale = screenHeight / srcHeight;
- if (uniformScale) {
- hscale = Math.min(hscale, vscale);
- vscale = hscale;
- }
- srcLeft = sxmin;
- srcTop = symin;
- }
- function dxy_at_t(curve, t) {
- var dxy = {};
- if (curve.length == 6) {
- var a = t - 1;
- var b = 1 - 2 * t;
- var c = t;
- dxy.x = a * curve[0] + b * curve[2] + c * curve[4];
- dxy.y = a * curve[1] + b * curve[3] + c * curve[5];
- } else if (curve.length == 7) {
- var p20x = curve[4] - curve[0];
- var p20y = curve[5] - curve[1];
- var p10xw = (curve[2] - curve[0]) * curve[6];
- var p10yw = (curve[3] - curve[1]) * curve[6];
- var coeff0x = curve[6] * p20x - p20x;
- var coeff0y = curve[6] * p20y - p20y;
- var coeff1x = p20x - 2 * p10xw;
- var coeff1y = p20y - 2 * p10yw;
- dxy.x = t * (t * coeff0x + coeff1x) + p10xw;
- dxy.y = t * (t * coeff0y + coeff1y) + p10yw;
- } else if (curve.length == 8) {
- var one_t = 1 - t;
- var a = curve[0];
- var b = curve[2];
- var c = curve[4];
- var d = curve[6];
- dxy.x = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
- a = curve[1];
- b = curve[3];
- c = curve[5];
- d = curve[7];
- dxy.y = 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
- }
- return dxy;
- }
- var flt_epsilon = 1.19209290E-07;
- function approximately_zero(A) {
- return Math.abs(A) < flt_epsilon;
- }
- function approximately_zero_inverse(A) {
- return Math.abs(A) > (1 / flt_epsilon);
- }
- function quad_real_roots(A, B, C) {
- var s = [];
- var p = B / (2 * A);
- var q = C / A;
- if (approximately_zero(A) && (approximately_zero_inverse(p)
- || approximately_zero_inverse(q))) {
- if (approximately_zero(B)) {
- if (C == 0) {
- s[0] = 0;
- }
- return s;
- }
- s[0] = -C / B;
- return s;
- }
- /* normal form: x^2 + px + q = 0 */
- var p2 = p * p;
- if (!approximately_zero(p2 - q) && p2 < q) {
- return s;
- }
- var sqrt_D = 0;
- if (p2 > q) {
- sqrt_D = Math.sqrt(p2 - q);
- }
- s[0] = sqrt_D - p;
- var flip = -sqrt_D - p;
- if (!approximately_zero(s[0] - flip)) {
- s[1] = flip;
- }
- return s;
- }
- function cubic_real_roots(A, B, C, D) {
- if (approximately_zero(A)) { // we're just a quadratic
- return quad_real_roots(B, C, D);
- }
- if (approximately_zero(D)) { // 0 is one root
- var s = quad_real_roots(A, B, C);
- for (var i = 0; i < s.length; ++i) {
- if (approximately_zero(s[i])) {
- return s;
- }
- }
- s.push(0);
- return s;
- }
- if (approximately_zero(A + B + C + D)) { // 1 is one root
- var s = quad_real_roots(A, A + B, -D);
- for (var i = 0; i < s.length; ++i) {
- if (approximately_zero(s[i] - 1)) {
- return s;
- }
- }
- s.push(1);
- return s;
- }
- var a, b, c;
- var invA = 1 / A;
- a = B * invA;
- b = C * invA;
- c = D * invA;
- var a2 = a * a;
- var Q = (a2 - b * 3) / 9;
- var R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
- var R2 = R * R;
- var Q3 = Q * Q * Q;
- var R2MinusQ3 = R2 - Q3;
- var adiv3 = a / 3;
- var r;
- var roots = [];
- if (R2MinusQ3 < 0) { // we have 3 real roots
- var theta = Math.acos(R / Math.sqrt(Q3));
- var neg2RootQ = -2 * Math.sqrt(Q);
- r = neg2RootQ * Math.cos(theta / 3) - adiv3;
- roots.push(r);
- r = neg2RootQ * Math.cos((theta + 2 * Math.PI) / 3) - adiv3;
- if (!approximately_zero(roots[0] - r)) {
- roots.push(r);
- }
- r = neg2RootQ * Math.cos((theta - 2 * Math.PI) / 3) - adiv3;
- if (!approximately_zero(roots[0] - r) && (roots.length == 1
- || !approximately_zero(roots[1] - r))) {
- roots.push(r);
- }
- } else { // we have 1 real root
- var sqrtR2MinusQ3 = Math.sqrt(R2MinusQ3);
- var A = Math.abs(R) + sqrtR2MinusQ3;
- A = Math.pow(A, 1/3);
- if (R > 0) {
- A = -A;
- }
- if (A != 0) {
- A += Q / A;
- }
- r = A - adiv3;
- roots.push(r);
- if (approximately_zero(R2 - Q3)) {
- r = -A / 2 - adiv3;
- if (!approximately_zero(roots[0] - r)) {
- roots.push(r);
- }
- }
- }
- return roots;
- }
- function approximately_zero_or_more(tValue) {
- return tValue >= -flt_epsilon;
- }
- function approximately_one_or_less(tValue) {
- return tValue <= 1 + flt_epsilon;
- }
- function approximately_less_than_zero(tValue) {
- return tValue < flt_epsilon;
- }
- function approximately_greater_than_one(tValue) {
- return tValue > 1 - flt_epsilon;
- }
- function add_valid_ts(s) {
- var t = [];
- nextRoot:
- for (var index = 0; index < s.length; ++index) {
- var tValue = s[index];
- if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
- if (approximately_less_than_zero(tValue)) {
- tValue = 0;
- } else if (approximately_greater_than_one(tValue)) {
- tValue = 1;
- }
- for (var idx2 = 0; idx2 < t.length; ++idx2) {
- if (approximately_zero(t[idx2] - tValue)) {
- continue nextRoot;
- }
- }
- t.push(tValue);
- }
- }
- return t;
- }
- function quad_roots(A, B, C) {
- var s = quad_real_roots(A, B, C);
- var foundRoots = add_valid_ts(s);
- return foundRoots;
- }
- function cubic_roots(A, B, C, D) {
- var s = cubic_real_roots(A, B, C, D);
- var foundRoots = add_valid_ts(s);
- return foundRoots;
- }
- function ray_curve_intersect(startPt, endPt, curve) {
- var adj = endPt[0] - startPt[0];
- var opp = endPt[1] - startPt[1];
- var r = [];
- var len = (curve.length == 7 ? 6 : curve.length) / 2;
- for (var n = 0; n < len; ++n) {
- r[n] = (curve[n * 2 + 1] - startPt[1]) * adj - (curve[n * 2] - startPt[0]) * opp;
- }
- if (curve.length == 6) {
- var A = r[2];
- var B = r[1];
- var C = r[0];
- A += C - 2 * B; // A = a - 2*b + c
- B -= C; // B = -(b - c)
- return quad_roots(A, 2 * B, C);
- }
- if (curve.length == 7) {
- var A = r[2];
- var B = r[1] * curve[6];
- var C = r[0];
- A += C - 2 * B; // A = a - 2*b + c
- B -= C; // B = -(b - c)
- return quad_roots(A, 2 * B, C);
- }
- var A = r[3]; // d
- var B = r[2] * 3; // 3*c
- var C = r[1] * 3; // 3*b
- var D = r[0]; // a
- A -= D - C + B; // A = -a + 3*b - 3*c + d
- B += 3 * D - 2 * C; // B = 3*a - 6*b + 3*c
- C -= 3 * D; // C = -3*a + 3*b
- return cubic_roots(A, B, C, D);
- }
- function x_at_t(curve, t) {
- var one_t = 1 - t;
- if (curve.length == 4) {
- return one_t * curve[0] + t * curve[2];
- }
- var one_t2 = one_t * one_t;
- var t2 = t * t;
- if (curve.length == 6) {
- return one_t2 * curve[0] + 2 * one_t * t * curve[2] + t2 * curve[4];
- }
- if (curve.length == 7) {
- var numer = one_t2 * curve[0] + 2 * one_t * t * curve[2] * curve[6]
- + t2 * curve[4];
- var denom = one_t2 + 2 * one_t * t * curve[6]
- + t2;
- return numer / denom;
- }
- var a = one_t2 * one_t;
- var b = 3 * one_t2 * t;
- var c = 3 * one_t * t2;
- var d = t2 * t;
- return a * curve[0] + b * curve[2] + c * curve[4] + d * curve[6];
- }
- function y_at_t(curve, t) {
- var one_t = 1 - t;
- if (curve.length == 4) {
- return one_t * curve[1] + t * curve[3];
- }
- var one_t2 = one_t * one_t;
- var t2 = t * t;
- if (curve.length == 6) {
- return one_t2 * curve[1] + 2 * one_t * t * curve[3] + t2 * curve[5];
- }
- if (curve.length == 7) {
- var numer = one_t2 * curve[1] + 2 * one_t * t * curve[3] * curve[6]
- + t2 * curve[5];
- var denom = one_t2 + 2 * one_t * t * curve[6]
- + t2;
- return numer / denom;
- }
- var a = one_t2 * one_t;
- var b = 3 * one_t2 * t;
- var c = 3 * one_t * t2;
- var d = t2 * t;
- return a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
- }
- function drawPointAtT(curve) {
- var x = x_at_t(curve, curveT);
- var y = y_at_t(curve, curveT);
- drawPoint(x, y, false);
- }
- function drawLine(x1, y1, x2, y2) {
- ctx.beginPath();
- ctx.moveTo((x1 - srcLeft) * hscale,
- (y1 - srcTop) * vscale);
- ctx.lineTo((x2 - srcLeft) * hscale,
- (y2 - srcTop) * vscale);
- ctx.stroke();
- }
- function drawPoint(px, py, xend) {
- for (var pts = 0; pts < drawnPts.length; pts += 2) {
- var x = drawnPts[pts];
- var y = drawnPts[pts + 1];
- if (px == x && py == y) {
- return;
- }
- }
- drawnPts.push(px);
- drawnPts.push(py);
- var _px = (px - srcLeft) * hscale;
- var _py = (py - srcTop) * vscale;
- ctx.beginPath();
- if (xend) {
- ctx.moveTo(_px - 3, _py - 3);
- ctx.lineTo(_px + 3, _py + 3);
- ctx.moveTo(_px - 3, _py + 3);
- ctx.lineTo(_px + 3, _py - 3);
- } else {
- ctx.arc(_px, _py, 3, 0, Math.PI * 2, true);
- ctx.closePath();
- }
- ctx.stroke();
- if (draw_point_xy) {
- var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
- ctx.font = "normal 10px Arial";
- ctx.textAlign = "left";
- ctx.fillStyle = "black";
- ctx.fillText(label, _px + 5, _py);
- }
- }
- function drawPointSolid(px, py) {
- drawPoint(px, py, false);
- ctx.fillStyle = "rgba(0,0,0, 0.4)";
- ctx.fill();
- }
- function crossPt(origin, pt1, pt2) {
- return ((pt1[0] - origin[0]) * (pt2[1] - origin[1])
- - (pt1[1] - origin[1]) * (pt2[0] - origin[0])) > 0 ? 0 : 1;
- }
- // may not work well for cubics
- function curveClosestT(curve, x, y) {
- var closest = -1;
- var closestDist = Infinity;
- var l = Infinity, t = Infinity, r = -Infinity, b = -Infinity;
- for (var i = 0; i < 16; ++i) {
- var testX = x_at_t(curve, i / 16);
- l = Math.min(testX, l);
- r = Math.max(testX, r);
- var testY = y_at_t(curve, i / 16);
- t = Math.min(testY, t);
- b = Math.max(testY, b);
- var dx = testX - x;
- var dy = testY - y;
- var dist = dx * dx + dy * dy;
- if (closestDist > dist) {
- closestDist = dist;
- closest = i;
- }
- }
- var boundsX = r - l;
- var boundsY = b - t;
- var boundsDist = boundsX * boundsX + boundsY * boundsY;
- if (closestDist > boundsDist) {
- return -1;
- }
- console.log("closestDist = " + closestDist + " boundsDist = " + boundsDist
- + " t = " + closest / 16);
- return closest / 16;
- }
- var kMaxConicToQuadPOW2 = 5;
- function computeQuadPOW2(curve, tol) {
- var a = curve[6] - 1;
- var k = a / (4 * (2 + a));
- var x = k * (curve[0] - 2 * curve[2] + curve[4]);
- var y = k * (curve[1] - 2 * curve[3] + curve[5]);
- var error = Math.sqrt(x * x + y * y);
- var pow2;
- for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
- if (error <= tol) {
- break;
- }
- error *= 0.25;
- }
- return pow2;
- }
- function subdivide_w_value(w) {
- return Math.sqrt(0.5 + w * 0.5);
- }
- function chop(curve, part1, part2) {
- var w = curve[6];
- var scale = 1 / (1 + w);
- part1[0] = curve[0];
- part1[1] = curve[1];
- part1[2] = (curve[0] + curve[2] * w) * scale;
- part1[3] = (curve[1] + curve[3] * w) * scale;
- part1[4] = part2[0] = (curve[0] + (curve[2] * w) * 2 + curve[4]) * scale * 0.5;
- part1[5] = part2[1] = (curve[1] + (curve[3] * w) * 2 + curve[5]) * scale * 0.5;
- part2[2] = (curve[2] * w + curve[4]) * scale;
- part2[3] = (curve[3] * w + curve[5]) * scale;
- part2[4] = curve[4];
- part2[5] = curve[5];
- part1[6] = part2[6] = subdivide_w_value(w);
- }
- function subdivide(curve, level, pts) {
- if (0 == level) {
- pts.push(curve[2]);
- pts.push(curve[3]);
- pts.push(curve[4]);
- pts.push(curve[5]);
- } else {
- var part1 = [], part2 = [];
- chop(curve, part1, part2);
- --level;
- subdivide(part1, level, pts);
- subdivide(part2, level, pts);
- }
- }
- function chopIntoQuadsPOW2(curve, pow2, pts) {
- subdivide(curve, pow2, pts);
- return 1 << pow2;
- }
- function drawConic(curve, srcLeft, srcTop, hscale, vscale) {
- var tol = 1 / Math.min(hscale, vscale);
- var pow2 = computeQuadPOW2(curve, tol);
- var pts = [];
- chopIntoQuadsPOW2(curve, pow2, pts);
- for (var i = 0; i < pts.length; i += 4) {
- ctx.quadraticCurveTo(
- (pts[i + 0] - srcLeft) * hscale, (pts[i + 1] - srcTop) * vscale,
- (pts[i + 2] - srcLeft) * hscale, (pts[i + 3] - srcTop) * vscale);
- }
- }
- function draw(test, title) {
- ctx.font = "normal 50px Arial";
- ctx.textAlign = "left";
- ctx.fillStyle = "rgba(0,0,0, 0.1)";
- ctx.fillText(title, 50, 50);
- ctx.font = "normal 10px Arial";
- // ctx.lineWidth = "1.001"; "0.999";
- var hullStarts = [];
- var hullEnds = [];
- var midSpokes = [];
- var midDist = [];
- var origin = [];
- var shortSpokes = [];
- var shortDist = [];
- var sweeps = [];
- drawnPts = [];
- for (var curves in test) {
- var curve = test[curves];
- origin.push(curve[0]);
- origin.push(curve[1]);
- var startPt = [];
- startPt.push(curve[2]);
- startPt.push(curve[3]);
- hullStarts.push(startPt);
- var endPt = [];
- if (curve.length == 4) {
- endPt.push(curve[2]);
- endPt.push(curve[3]);
- } else if (curve.length == 6 || curve.length == 7) {
- endPt.push(curve[4]);
- endPt.push(curve[5]);
- } else if (curve.length == 8) {
- endPt.push(curve[6]);
- endPt.push(curve[7]);
- }
- hullEnds.push(endPt);
- var sweep = crossPt(origin, startPt, endPt);
- sweeps.push(sweep);
- var midPt = [];
- midPt.push(x_at_t(curve, 0.5));
- midPt.push(y_at_t(curve, 0.5));
- midSpokes.push(midPt);
- var shortPt = [];
- shortPt.push(x_at_t(curve, 0.25));
- shortPt.push(y_at_t(curve, 0.25));
- shortSpokes.push(shortPt);
- var dx = midPt[0] - origin[0];
- var dy = midPt[1] - origin[1];
- var dist = Math.sqrt(dx * dx + dy * dy);
- midDist.push(dist);
- dx = shortPt[0] - origin[0];
- dy = shortPt[1] - origin[1];
- dist = Math.sqrt(dx * dx + dy * dy);
- shortDist.push(dist);
- }
- var intersect = [];
- var useIntersect = false;
- var maxWidth = Math.max(xmax - xmin, ymax - ymin);
- for (var curves in test) {
- var curve = test[curves];
- if (curve.length >= 6 && curve.length <= 8) {
- var opp = curves == 0 || curves == 1 ? 0 : 1;
- var sects = ray_curve_intersect(origin, hullEnds[opp], curve);
- intersect.push(sects);
- if (sects.length > 1) {
- var intersection = sects[0];
- if (intersection == 0) {
- intersection = sects[1];
- }
- var ix = x_at_t(curve, intersection) - origin[0];
- var iy = y_at_t(curve, intersection) - origin[1];
- var ex = hullEnds[opp][0] - origin[0];
- var ey = hullEnds[opp][1] - origin[1];
- if (ix * ex >= 0 && iy * ey >= 0) {
- var iDist = Math.sqrt(ix * ix + iy * iy);
- var eDist = Math.sqrt(ex * ex + ey * ey);
- var delta = Math.abs(iDist - eDist) / maxWidth;
- if (delta > (curve.length != 8 ? 1e-5 : 1e-4)) {
- useIntersect ^= true;
- }
- }
- }
- }
- }
- var midLeft = curves != 0 ? crossPt(origin, midSpokes[0], midSpokes[1]) : 0;
- var firstInside;
- if (useIntersect) {
- var sect1 = intersect[0].length > 1;
- var sIndex = sect1 ? 0 : 1;
- var sects = intersect[sIndex];
- var intersection = sects[0];
- if (intersection == 0) {
- intersection = sects[1];
- }
- var curve = test[sIndex];
- var ix = x_at_t(curve, intersection) - origin[0];
- var iy = y_at_t(curve, intersection) - origin[1];
- var opp = sect1 ? 1 : 0;
- var ex = hullEnds[opp][0] - origin[0];
- var ey = hullEnds[opp][1] - origin[1];
- var iDist = ix * ix + iy * iy;
- var eDist = ex * ex + ey * ey;
- firstInside = (iDist > eDist) ^ (sIndex == 0) ^ sweeps[0];
- // console.log("iDist=" + iDist + " eDist=" + eDist + " sIndex=" + sIndex
- // + " sweeps[0]=" + sweeps[0]);
- } else {
- // console.log("midLeft=" + midLeft);
- firstInside = midLeft != 0;
- }
- var shorter = midDist[1] < midDist[0];
- var shortLeft = shorter ? crossPt(origin, shortSpokes[0], midSpokes[1])
- : crossPt(origin, midSpokes[0], shortSpokes[1]);
- var startCross = crossPt(origin, hullStarts[0], hullStarts[1]);
- var disallowShort = midLeft == startCross && midLeft == sweeps[0]
- && midLeft == sweeps[1];
- // console.log("midLeft=" + midLeft + " startCross=" + startCross);
- var intersectIndex = 0;
- for (var curves in test) {
- var curve = test[draw_id != 2 ? curves : test.length - curves - 1];
- if (curve.length != 4 && curve.length != 6 && curve.length != 7 && curve.length != 8) {
- continue;
- }
- ctx.lineWidth = 1;
- if (draw_tangents != 0) {
- if (draw_cubic_red ? curve.length == 8 : firstInside == curves) {
- ctx.strokeStyle = "rgba(255,0,0, 0.3)";
- } else {
- ctx.strokeStyle = "rgba(0,0,255, 0.3)";
- }
- drawLine(curve[0], curve[1], curve[2], curve[3]);
- if (draw_tangents != 2) {
- if (curve.length > 4) drawLine(curve[2], curve[3], curve[4], curve[5]);
- if (curve.length == 8) drawLine(curve[4], curve[5], curve[6], curve[7]);
- }
- if (draw_tangents != 1) {
- if (curve.length == 6 || curve.length == 7) {
- drawLine(curve[0], curve[1], curve[4], curve[5]);
- }
- if (curve.length == 8) drawLine(curve[0], curve[1], curve[6], curve[7]);
- }
- }
- ctx.beginPath();
- ctx.moveTo((curve[0] - srcLeft) * hscale, (curve[1] - srcTop) * vscale);
- if (curve.length == 4) {
- ctx.lineTo((curve[2] - srcLeft) * hscale, (curve[3] - srcTop) * vscale);
- } else if (curve.length == 6) {
- ctx.quadraticCurveTo(
- (curve[2] - srcLeft) * hscale, (curve[3] - srcTop) * vscale,
- (curve[4] - srcLeft) * hscale, (curve[5] - srcTop) * vscale);
- } else if (curve.length == 7) {
- drawConic(curve, srcLeft, srcTop, hscale, vscale);
- } else {
- ctx.bezierCurveTo(
- (curve[2] - srcLeft) * hscale, (curve[3] - srcTop) * vscale,
- (curve[4] - srcLeft) * hscale, (curve[5] - srcTop) * vscale,
- (curve[6] - srcLeft) * hscale, (curve[7] - srcTop) * vscale);
- }
- if (draw_cubic_red ? curve.length == 8 : firstInside == curves) {
- ctx.strokeStyle = "rgba(255,0,0, 1)";
- } else {
- ctx.strokeStyle = "rgba(0,0,255, 1)";
- }
- ctx.stroke();
- if (draw_endpoints > 0) {
- drawPoint(curve[0], curve[1], false);
- if (draw_endpoints > 1 || curve.length == 4) {
- drawPoint(curve[2], curve[3], curve.length == 4 && draw_endpoints == 3);
- }
- if (curve.length == 6 || curve.length == 7 ||
- (draw_endpoints > 1 && curve.length == 8)) {
- drawPoint(curve[4], curve[5], (curve.length == 6 || curve.length == 7) && draw_endpoints == 3);
- }
- if (curve.length == 8) {
- drawPoint(curve[6], curve[7], curve.length == 8 && draw_endpoints == 3);
- }
- }
- if (draw_midpoint != 0) {
- if ((curves == 0) == (midLeft == 0)) {
- ctx.strokeStyle = "rgba(0,180,127, 0.6)";
- } else {
- ctx.strokeStyle = "rgba(127,0,127, 0.6)";
- }
- var midX = x_at_t(curve, 0.5);
- var midY = y_at_t(curve, 0.5);
- drawPointSolid(midX, midY);
- if (draw_midpoint > 1) {
- drawLine(curve[0], curve[1], midX, midY);
- }
- }
- if (draw_quarterpoint != 0) {
- if ((curves == 0) == (shortLeft == 0)) {
- ctx.strokeStyle = "rgba(0,191,63, 0.6)";
- } else {
- ctx.strokeStyle = "rgba(63,0,191, 0.6)";
- }
- var midT = (curves == 0) == shorter ? 0.25 : 0.5;
- var midX = x_at_t(curve, midT);
- var midY = y_at_t(curve, midT);
- drawPointSolid(midX, midY);
- if (draw_quarterpoint > 1) {
- drawLine(curve[0], curve[1], midX, midY);
- }
- }
- if (draw_sortpoint != 0) {
- if ((curves == 0) == ((disallowShort == -1 ? midLeft : shortLeft) == 0)) {
- ctx.strokeStyle = "rgba(0,155,37, 0.6)";
- } else {
- ctx.strokeStyle = "rgba(37,0,155, 0.6)";
- }
- var midT = (curves == 0) == shorter && disallowShort != curves ? 0.25 : 0.5;
- console.log("curves=" + curves + " disallowShort=" + disallowShort
- + " midLeft=" + midLeft + " shortLeft=" + shortLeft
- + " shorter=" + shorter + " midT=" + midT);
- var midX = x_at_t(curve, midT);
- var midY = y_at_t(curve, midT);
- drawPointSolid(midX, midY);
- if (draw_sortpoint > 1) {
- drawLine(curve[0], curve[1], midX, midY);
- }
- }
- if (draw_ray_intersect != 0) {
- ctx.strokeStyle = "rgba(75,45,199, 0.6)";
- if (curve.length >= 6 && curve.length <= 8) {
- var intersections = intersect[intersectIndex];
- for (var i in intersections) {
- var intersection = intersections[i];
- var x = x_at_t(curve, intersection);
- var y = y_at_t(curve, intersection);
- drawPointSolid(x, y);
- if (draw_ray_intersect > 1) {
- drawLine(curve[0], curve[1], x, y);
- }
- }
- }
- ++intersectIndex;
- }
- if (draw_order) {
- var px = x_at_t(curve, 0.75);
- var py = y_at_t(curve, 0.75);
- var _px = (px - srcLeft) * hscale;
- var _py = (py - srcTop) * vscale;
- ctx.beginPath();
- ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
- ctx.closePath();
- ctx.fillStyle = "white";
- ctx.fill();
- if (draw_cubic_red ? curve.length == 8 : firstInside == curves) {
- ctx.strokeStyle = "rgba(255,0,0, 1)";
- ctx.fillStyle = "rgba(255,0,0, 1)";
- } else {
- ctx.strokeStyle = "rgba(0,0,255, 1)";
- ctx.fillStyle = "rgba(0,0,255, 1)";
- }
- ctx.stroke();
- ctx.font = "normal 16px Arial";
- ctx.textAlign = "center";
- ctx.fillText(parseInt(curves) + 1, _px, _py + 5);
- }
- if (draw_closest_t) {
- var t = curveClosestT(curve, mouseX, mouseY);
- if (t >= 0) {
- var x = x_at_t(curve, t);
- var y = y_at_t(curve, t);
- drawPointSolid(x, y);
- }
- }
- if (!approximately_zero(hscale - hinitScale)) {
- ctx.font = "normal 20px Arial";
- ctx.fillStyle = "rgba(0,0,0, 0.3)";
- ctx.textAlign = "right";
- var scaleTextOffset = hscale != vscale ? -25 : -5;
- ctx.fillText(hscale.toFixed(decimal_places) + 'x',
- screenWidth - 10, screenHeight - scaleTextOffset);
- if (hscale != vscale) {
- ctx.fillText(vscale.toFixed(decimal_places) + 'y',
- screenWidth - 10, screenHeight - 5);
- }
- }
- if (draw_t) {
- drawPointAtT(curve);
- }
- if (draw_id != 0) {
- var id = -1;
- for (var i = 0; i < ids.length; i += 2) {
- if (ids[i + 1] == curve) {
- id = ids[i];
- break;
- }
- }
- if (id >= 0) {
- var px = x_at_t(curve, 0.5);
- var py = y_at_t(curve, 0.5);
- var _px = (px - srcLeft) * hscale;
- var _py = (py - srcTop) * vscale;
- ctx.beginPath();
- ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
- ctx.closePath();
- ctx.fillStyle = "white";
- ctx.fill();
- ctx.strokeStyle = "rgba(255,0,0, 1)";
- ctx.fillStyle = "rgba(255,0,0, 1)";
- ctx.stroke();
- ctx.font = "normal 16px Arial";
- ctx.textAlign = "center";
- ctx.fillText(id, _px, _py + 5);
- }
- }
- }
- if (draw_t) {
- drawCurveTControl();
- }
- if (draw_w) {
- drawCurveWControl();
- }
- }
- function drawCurveTControl() {
- ctx.lineWidth = 2;
- ctx.strokeStyle = "rgba(0,0,0, 0.3)";
- ctx.beginPath();
- ctx.rect(screenWidth - 80, 40, 28, screenHeight - 80);
- ctx.stroke();
- var ty = 40 + curveT * (screenHeight - 80);
- ctx.beginPath();
- ctx.moveTo(screenWidth - 80, ty);
- ctx.lineTo(screenWidth - 85, ty - 5);
- ctx.lineTo(screenWidth - 85, ty + 5);
- ctx.lineTo(screenWidth - 80, ty);
- ctx.fillStyle = "rgba(0,0,0, 0.6)";
- ctx.fill();
- var num = curveT.toFixed(decimal_places);
- ctx.font = "normal 10px Arial";
- ctx.textAlign = "left";
- ctx.fillText(num, screenWidth - 78, ty);
- }
- function drawCurveWControl() {
- var w = -1;
- var choice = 0;
- for (var curves in tests[testIndex]) {
- var curve = tests[testIndex][curves];
- if (curve.length != 7) {
- continue;
- }
- if (choice == curveW) {
- w = curve[6];
- break;
- }
- ++choice;
- }
- if (w < 0) {
- return;
- }
- ctx.lineWidth = 2;
- ctx.strokeStyle = "rgba(0,0,0, 0.3)";
- ctx.beginPath();
- ctx.rect(screenWidth - 40, 40, 28, screenHeight - 80);
- ctx.stroke();
- var ty = 40 + w * (screenHeight - 80);
- ctx.beginPath();
- ctx.moveTo(screenWidth - 40, ty);
- ctx.lineTo(screenWidth - 45, ty - 5);
- ctx.lineTo(screenWidth - 45, ty + 5);
- ctx.lineTo(screenWidth - 40, ty);
- ctx.fillStyle = "rgba(0,0,0, 0.6)";
- ctx.fill();
- var num = w.toFixed(decimal_places);
- ctx.font = "normal 10px Arial";
- ctx.textAlign = "left";
- ctx.fillText(num, screenWidth - 38, ty);
- }
- function ptInTControl() {
- var e = window.event;
- var tgt = e.target || e.srcElement;
- var left = tgt.offsetLeft;
- var top = tgt.offsetTop;
- var x = (e.clientX - left);
- var y = (e.clientY - top);
- if (x < screenWidth - 80 || x > screenWidth - 50) {
- return false;
- }
- if (y < 40 || y > screenHeight - 80) {
- return false;
- }
- curveT = (y - 40) / (screenHeight - 120);
- if (curveT < 0 || curveT > 1) {
- throw "stop execution";
- }
- return true;
- }
- function ptInWControl() {
- var e = window.event;
- var tgt = e.target || e.srcElement;
- var left = tgt.offsetLeft;
- var top = tgt.offsetTop;
- var x = (e.clientX - left);
- var y = (e.clientY - top);
- if (x < screenWidth - 40 || x > screenWidth - 10) {
- return false;
- }
- if (y < 40 || y > screenHeight - 80) {
- return false;
- }
- var w = (y - 40) / (screenHeight - 120);
- if (w < 0 || w > 1) {
- throw "stop execution";
- }
- var choice = 0;
- for (var curves in tests[testIndex]) {
- var curve = tests[testIndex][curves];
- if (curve.length != 7) {
- continue;
- }
- if (choice == curveW) {
- curve[6] = w;
- break;
- }
- ++choice;
- }
- return true;
- }
- function drawTop() {
- init(tests[testIndex]);
- redraw();
- }
- function redraw() {
- if (focus_on_selection > 0) {
- var focusXmin = focusYmin = Infinity;
- var focusXmax = focusYmax = -Infinity;
- var choice = 0;
- for (var curves in tests[testIndex]) {
- if (++choice != focus_on_selection) {
- continue;
- }
- var curve = tests[testIndex][curves];
- var last = curve.length - (curve.length % 2 == 1 ? 1 : 0);
- for (var idx = 0; idx < last; idx += 2) {
- focusXmin = Math.min(focusXmin, curve[idx]);
- focusXmax = Math.max(focusXmax, curve[idx]);
- focusYmin = Math.min(focusYmin, curve[idx + 1]);
- focusYmax = Math.max(focusYmax, curve[idx + 1]);
- }
- }
- focusXmin -= Math.min(1, Math.max(focusXmax - focusXmin, focusYmax - focusYmin));
- if (focusXmin < focusXmax && focusYmin < focusYmax) {
- setScale(focusXmin, focusXmax, focusYmin, focusYmax);
- }
- }
- ctx.beginPath();
- ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
- ctx.fillStyle = "white";
- ctx.fill();
- draw(tests[testIndex], testTitles[testIndex]);
- }
- function doKeyPress(evt) {
- var char = String.fromCharCode(evt.charCode);
- var focusWasOn = false;
- switch (char) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- decimal_places = char - '0';
- redraw();
- break;
- case '-':
- focusWasOn = focus_on_selection;
- if (focusWasOn) {
- focus_on_selection = false;
- hscale /= 1.2;
- vscale /= 1.2;
- } else {
- hscale /= 2;
- vscale /= 2;
- }
- calcLeftTop();
- redraw();
- focus_on_selection = focusWasOn;
- break;
- case '=':
- case '+':
- focusWasOn = focus_on_selection;
- if (focusWasOn) {
- focus_on_selection = false;
- hscale *= 1.2;
- vscale *= 1.2;
- } else {
- hscale *= 2;
- vscale *= 2;
- }
- calcLeftTop();
- redraw();
- focus_on_selection = focusWasOn;
- break;
- case 'b':
- draw_cubic_red ^= true;
- redraw();
- break;
- case 'c':
- drawTop();
- break;
- case 'd':
- var test = tests[testIndex];
- var testClone = [];
- for (var curves in test) {
- var c = test[curves];
- var cClone = [];
- for (var index = 0; index < c.length; ++index) {
- cClone.push(c[index]);
- }
- testClone.push(cClone);
- }
- tests.push(testClone);
- testTitles.push(testTitles[testIndex] + " copy");
- testIndex = tests.length - 1;
- redraw();
- break;
- case 'e':
- draw_endpoints = (draw_endpoints + 1) % 4;
- redraw();
- break;
- case 'f':
- draw_derivative ^= true;
- redraw();
- break;
- case 'g':
- hscale *= 1.2;
- calcLeftTop();
- redraw();
- break;
- case 'G':
- hscale /= 1.2;
- calcLeftTop();
- redraw();
- break;
- case 'h':
- vscale *= 1.2;
- calcLeftTop();
- redraw();
- break;
- case 'H':
- vscale /= 1.2;
- calcLeftTop();
- redraw();
- break;
- case 'i':
- draw_ray_intersect = (draw_ray_intersect + 1) % 3;
- redraw();
- break;
- case 'l':
- var test = tests[testIndex];
- console.log("<div id=\"" + testTitles[testIndex] + "\" >");
- for (var curves in test) {
- var c = test[curves];
- var s = "{{";
- for (var i = 0; i < c.length; i += 2) {
- s += "{";
- s += c[i] + "," + c[i + 1];
- s += "}";
- if (i + 2 < c.length) {
- s += ", ";
- }
- }
- console.log(s + "}},");
- }
- console.log("</div>");
- break;
- case 'm':
- draw_midpoint = (draw_midpoint + 1) % 3;
- redraw();
- break;
- case 'N':
- testIndex += 9;
- case 'n':
- testIndex = (testIndex + 1) % tests.length;
- drawTop();
- break;
- case 'o':
- draw_order ^= true;
- redraw();
- break;
- case 'P':
- testIndex -= 9;
- case 'p':
- if (--testIndex < 0)
- testIndex = tests.length - 1;
- drawTop();
- break;
- case 'q':
- draw_quarterpoint = (draw_quarterpoint + 1) % 3;
- redraw();
- break;
- case 'r':
- for (var i = 0; i < testDivs.length; ++i) {
- var title = testDivs[i].id.toString();
- if (title == testTitles[testIndex]) {
- var str = testDivs[i].firstChild.data;
- parse(str, title);
- var original = tests.pop();
- testTitles.pop();
- tests[testIndex] = original;
- break;
- }
- }
- redraw();
- break;
- case 's':
- draw_sortpoint = (draw_sortpoint + 1) % 3;
- redraw();
- break;
- case 't':
- draw_t ^= true;
- redraw();
- break;
- case 'u':
- draw_closest_t ^= true;
- redraw();
- break;
- case 'v':
- draw_tangents = (draw_tangents + 1) % 4;
- redraw();
- break;
- case 'w':
- ++curveW;
- var choice = 0;
- draw_w = false;
- for (var curves in tests[testIndex]) {
- var curve = tests[testIndex][curves];
- if (curve.length != 7) {
- continue;
- }
- if (choice == curveW) {
- draw_w = true;
- break;
- }
- ++choice;
- }
- if (!draw_w) {
- curveW = -1;
- }
- redraw();
- break;
- case 'x':
- draw_point_xy ^= true;
- redraw();
- break;
- case 'y':
- draw_mouse_xy ^= true;
- redraw();
- break;
- case '\\':
- retina_scale ^= true;
- drawTop();
- break;
- case '`':
- ++focus_on_selection;
- if (focus_on_selection >= tests[testIndex].length) {
- focus_on_selection = 0;
- }
- setScale(xmin, xmax, ymin, ymax);
- redraw();
- break;
- case '.':
- draw_id = (draw_id + 1) % 3;
- redraw();
- break;
- }
- }
- function doKeyDown(evt) {
- var char = evt.keyCode;
- var preventDefault = false;
- switch (char) {
- case 37: // left arrow
- if (evt.shiftKey) {
- testIndex -= 9;
- }
- if (--testIndex < 0)
- testIndex = tests.length - 1;
- if (evt.ctrlKey) {
- redraw();
- } else {
- drawTop();
- }
- preventDefault = true;
- break;
- case 39: // right arrow
- if (evt.shiftKey) {
- testIndex += 9;
- }
- if (++testIndex >= tests.length)
- testIndex = 0;
- if (evt.ctrlKey) {
- redraw();
- } else {
- drawTop();
- }
- preventDefault = true;
- break;
- }
- if (preventDefault) {
- evt.preventDefault();
- return false;
- }
- return true;
- }
- function calcXY() {
- var e = window.event;
- var tgt = e.target || e.srcElement;
- var left = tgt.offsetLeft;
- var top = tgt.offsetTop;
- mouseX = (e.clientX - left) / hscale + srcLeft;
- mouseY = (e.clientY - top) / vscale + srcTop;
- }
- function calcLeftTop() {
- srcLeft = mouseX - screenWidth / 2 / hscale;
- srcTop = mouseY - screenHeight / 2 / vscale;
- }
- function handleMouseClick() {
- if ((!draw_t || !ptInTControl()) && (!draw_w || !ptInWControl())) {
- calcXY();
- } else {
- redraw();
- }
- }
- function initDown() {
- var test = tests[testIndex];
- var bestDistance = 1000000;
- activePt = -1;
- for (var curves in test) {
- var testCurve = test[curves];
- if (testCurve.length != 4 && (testCurve.length < 6 || testCurve.length > 8)) {
- continue;
- }
- var testMax = testCurve.length == 7 ? 6 : testCurve.length;
- for (var i = 0; i < testMax; i += 2) {
- var testX = testCurve[i];
- var testY = testCurve[i + 1];
- var dx = testX - mouseX;
- var dy = testY - mouseY;
- var dist = dx * dx + dy * dy;
- if (dist > bestDistance) {
- continue;
- }
- activeCurve = testCurve;
- activePt = i;
- bestDistance = dist;
- }
- }
- if (activePt >= 0) {
- lastX = mouseX;
- lastY = mouseY;
- }
- }
- function handleMouseOver() {
- calcXY();
- if (draw_mouse_xy) {
- var num = mouseX.toFixed(decimal_places) + ", " + mouseY.toFixed(decimal_places);
- ctx.beginPath();
- ctx.rect(300, 100, num.length * 6, 10);
- ctx.fillStyle = "white";
- ctx.fill();
- ctx.font = "normal 10px Arial";
- ctx.fillStyle = "black";
- ctx.textAlign = "left";
- ctx.fillText(num, 300, 108);
- }
- if (!mouseDown) {
- activePt = -1;
- return;
- }
- if (activePt < 0) {
- initDown();
- return;
- }
- var deltaX = mouseX - lastX;
- var deltaY = mouseY - lastY;
- lastX = mouseX;
- lastY = mouseY;
- if (activePt == 0) {
- var test = tests[testIndex];
- for (var curves in test) {
- var testCurve = test[curves];
- testCurve[0] += deltaX;
- testCurve[1] += deltaY;
- }
- } else {
- activeCurve[activePt] += deltaX;
- activeCurve[activePt + 1] += deltaY;
- }
- redraw();
- }
- function start() {
- for (var i = 0; i < testDivs.length; ++i) {
- var title = testDivs[i].id.toString();
- var str = testDivs[i].firstChild.data;
- parse(str, title);
- }
- drawTop();
- window.addEventListener('keypress', doKeyPress, true);
- window.addEventListener('keydown', doKeyDown, true);
- window.onresize = function () {
- drawTop();
- }
- }
- </script>
- </head>
- <body onLoad="start();">
- <canvas id="canvas" width="750" height="500"
- onmousedown="mouseDown = true"
- onmouseup="mouseDown = false"
- onmousemove="handleMouseOver()"
- onclick="handleMouseClick()"
- ></canvas >
- </body>
- </html>
|