[initial commit of spiro js app in progress raph.levien@gmail.com**20070427063406] { addfile ./shell.html hunk ./shell.html 1 + + + + + + +
+

Raph's JS Shell.

+
+
+ + addfile ./spiro.js hunk ./spiro.js 1 +function draw_spiro(ctx, k0, k1, k2, k3) { + if (!this.x) { this.x = .5 } + this.x += .1; + ctx.moveTo(-.5, 0); + ctx.lineTo(.5, 0); +} +function show_xy_evt(evt, style) { + var canvas = document.getElementById("canvas"); + var ctx = canvas.getContext("2d"); + var x = evt.offsetX != undefined ? evt.offsetX : evt.pageX - canvas.offsetLeft; + var y = evt.offsetY != undefined ? evt.offsetY : evt.pageY - canvas.offsetTop; + ctx.beginPath(); + ctx.fillStyle = style; + ctx.moveTo(x-1, y-1); + ctx.lineTo(x+1, y-1); + ctx.lineTo(x+1, y+1); + ctx.lineTo(x-1, y+1); + ctx.fill(); +} +function show_evt(evt) { + var str = "["; + for (x in evt) { + str += x + ": " + try { + str += evt[x] + ", "; + } catch (e) { + str += ""; + } + } + str += "]"; + var msgel = document.getElementById("msg"); + msgel.appendChild(document.createTextNode(str)); +} +function hook_events(canvas) { + show_evt(canvas); + canvas.onmouseover = function(evt) { show_evt(evt, "red"); evt.preventDefault(); }; + canvas.onmouseup = function(evt) { show_evt(evt, "red"); evt.preventDefault(); }; + canvas.onmousedown = function(evt) { show_xy_evt(evt, "red"); evt.preventDefault(); }; + //canvas.onmousemove = function(evt) { show_xy_evt(evt, "black"); evt.preventDefault(); }; + canvas.ondblclick = function() { alert("varclick") }; + window.addEventListener("keypress", function(evt){ + show_evt(evt); + evt.preventDefault(); + }, false); +} + +// -- actual spiro code + +function integ_spiro(k0, k1, k2, k3) { + var n = 4; + var th1 = k0; + var th2 = .5 * k1; + var th3 = (1./6) * k2; + var th4 = (1./24) * k3; + var x, y; + var ds = 1. / n; + var ds2 = ds * ds; + var ds3 = ds2 * ds; + var s = .5 * ds - .5; + + k0 *= ds; + k1 *= ds; + k2 *= ds; + k3 *= ds; + + x = 0; + y = 0; + + for (var i = 0; i < n; i++) { + var u, v; + var km0, km1, km2, km3; + + if (n == 1) { + km0 = k0; + km1 = k1 * ds; + km2 = k2 * ds2; + } else { + km0 = (((1./6) * k3 * s + .5 * k2) * s + k1) * s + k0; + km1 = ((.5 * k3 * s + k2) * s + k1) * ds; + km2 = (k3 * s + k2) * ds2; + } + km3 = k3 * ds3; + + var t1_1 = km0; + var t1_2 = .5 * km1; + var t1_3 = (1./6) * km2; + var t1_4 = (1./24) * km3; + var t2_2 = t1_1 * t1_1; + var t2_3 = 2 * (t1_1 * t1_2); + var t2_4 = 2 * (t1_1 * t1_3) + t1_2 * t1_2; + var t2_5 = 2 * (t1_1 * t1_4 + t1_2 * t1_3); + var t2_6 = 2 * (t1_2 * t1_4) + t1_3 * t1_3; + var t2_7 = 2 * (t1_3 * t1_4); + var t2_8 = t1_4 * t1_4; + var t3_4 = t2_2 * t1_2 + t2_3 * t1_1; + var t3_6 = t2_2 * t1_4 + t2_3 * t1_3 + t2_4 * t1_2 + t2_5 * t1_1; + var t3_8 = t2_4 * t1_4 + t2_5 * t1_3 + t2_6 * t1_2 + t2_7 * t1_1; + var t3_10 = t2_6 * t1_4 + t2_7 * t1_3 + t2_8 * t1_2; + var t4_4 = t2_2 * t2_2; + var t4_5 = 2 * (t2_2 * t2_3); + var t4_6 = 2 * (t2_2 * t2_4) + t2_3 * t2_3; + var t4_7 = 2 * (t2_2 * t2_5 + t2_3 * t2_4); + var t4_8 = 2 * (t2_2 * t2_6 + t2_3 * t2_5) + t2_4 * t2_4; + var t4_9 = 2 * (t2_2 * t2_7 + t2_3 * t2_6 + t2_4 * t2_5); + var t4_10 = 2 * (t2_2 * t2_8 + t2_3 * t2_7 + t2_4 * t2_6) + t2_5 * t2_5; + var t5_6 = t4_4 * t1_2 + t4_5 * t1_1; + var t5_8 = t4_4 * t1_4 + t4_5 * t1_3 + t4_6 * t1_2 + t4_7 * t1_1; + var t5_10 = t4_6 * t1_4 + t4_7 * t1_3 + t4_8 * t1_2 + t4_9 * t1_1; + var t6_6 = t4_4 * t2_2; + var t6_7 = t4_4 * t2_3 + t4_5 * t2_2; + var t6_8 = t4_4 * t2_4 + t4_5 * t2_3 + t4_6 * t2_2; + var t6_9 = t4_4 * t2_5 + t4_5 * t2_4 + t4_6 * t2_3 + t4_7 * t2_2; + var t6_10 = t4_4 * t2_6 + t4_5 * t2_5 + t4_6 * t2_4 + t4_7 * t2_3 + t4_8 * t2_2; + var t7_8 = t6_6 * t1_2 + t6_7 * t1_1; + var t7_10 = t6_6 * t1_4 + t6_7 * t1_3 + t6_8 * t1_2 + t6_9 * t1_1; + var t8_8 = t6_6 * t2_2; + var t8_9 = t6_6 * t2_3 + t6_7 * t2_2; + var t8_10 = t6_6 * t2_4 + t6_7 * t2_3 + t6_8 * t2_2; + var t9_10 = t8_8 * t1_2 + t8_9 * t1_1; + var t10_10 = t8_8 * t2_2; + u = 1; + v = 0; + v += (1./12) * t1_2 + (1./80) * t1_4; + u -= (1./24) * t2_2 + (1./160) * t2_4 + (1./896) * t2_6 + (1./4608) * t2_8; + v -= (1./480) * t3_4 + (1./2688) * t3_6 + (1./13824) * t3_8 + (1./67584) * t3_10; + u += (1./1920) * t4_4 + (1./10752) * t4_6 + (1./55296) * t4_8 + (1./270336) * t4_10; + v += (1./53760) * t5_6 + (1./276480) * t5_8 + (1./1.35168e+06) * t5_10; + u -= (1./322560) * t6_6 + (1./1.65888e+06) * t6_8 + (1./8.11008e+06) * t6_10; + v -= (1./1.16122e+07) * t7_8 + (1./5.67706e+07) * t7_10; + u += (1./9.28973e+07) * t8_8 + (1./4.54164e+08) * t8_10; + v += (1./4.08748e+09) * t9_10; + u -= (1./4.08748e+10) * t10_10; + if (n == 1) { + x = u; + y = v; + } else { + var th = (((th4 * s + th3) * s + th2) * s + th1) * s; + var cth = Math.cos(th); + var sth = Math.sin(th); + + x += cth * u - sth * v; + y += cth * v + sth * u; + s += ds; + } + } + return [x * ds, y * ds]; +} + +function seg_to_bez(ctx, ks, x0, y0, x1, y1) { + var bend = Math.abs(ks[0]) + Math.abs(.5 * ks[1]) + Math.abs(.125 * ks[2]) + Math.abs((1./48) * ks[3]); + + if (bend < 1e-8) { + ctx.lineTo(x1, y1); + } else { + var seg_ch = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); + var seg_th = Math.atan2(y1 - y0, x1 - x0); + + var xy = integ_spiro(ks[0], ks[1], ks[2], ks[3]); + var ch = Math.sqrt(xy[0] * xy[0] + xy[1] * xy[1]); + var th = Math.atan2(xy[1], xy[0]); + var scale = seg_ch / ch; + var rot = seg_th - th; + if (bend < 1) { + var th_even = (1./384) * ks[3] + (1./8) * ks[1] + rot; + var th_odd = (1./48) * ks[2] + .5 * ks[0]; + var scale3 = scale * (1./3); + var ul = scale3 * Math.cos(th_even - th_odd); + var vl = scale3 * Math.sin(th_even - th_odd); + var ur = scale3 * Math.cos(th_even + th_odd); + var vr = scale3 * Math.sin(th_even + th_odd); + ctx.bezierCurveTo(x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1); + } else { + /* subdivide */ + var ksub = + [.5 * ks[0] - .125 * ks[1] + (1./64) * ks[2] - (1./768) * ks[3], + .25 * ks[1] - (1./16) * ks[2] + (1./128) * ks[3], + .125 * ks[2] - (1./32) * ks[3], + (1./16) * ks[3] + ]; + var thsub = rot - .25 * ks[0] + (1./32) * ks[1] - (1./384) * ks[2] + (1./6144) * ks[3]; + var cth = .5 * scale * Math.cos(thsub); + var sth = .5 * scale * Math.sin(thsub); + var xysub = integ_spiro(ksub[0], ksub[1], ksub[2], ksub[3]); + var xmid = x0 + cth * xysub[0] - sth * xysub[1]; + var ymid = y0 + cth * xysub[1] + sth * xysub[0]; + seg_to_bez(ctx, ksub, x0, y0, xmid, ymid); + ksub[0] += .25 * ks[1] + (1./384) * ks[3]; + ksub[1] += .125 * ks[2]; + ksub[2] += (1./16) * ks[3]; + seg_to_bez(ctx, ksub, xmid, ymid, x1, y1); + } + } +} + +function compute_dth(k0, k1) { + xy = integ_spiro(k0, k1, 0, 0); + return .25 * k1 - 2 * Math.atan2(xy[1], xy[0]); +} + +/* return [k0, k1] */ +function fit_cloth(th0, th1) { + var k0 = th0 + th1; + var k1 = 6 * (th1 - th0); + var error = (th1 - th0) - compute_dth(k0, k1); + var k1_old = k1; + var error_old = error; + k1 += 6 * error; + for (var i = 0; i < 10; i++) { + error = (th1 - th0) - compute_dth(k0, k1); + if (Math.abs(error) < 1e-9) break; + var new_k1 = k1 + (k1_old - k1) * error / (error - error_old); + k1_old = k1; + error_old = error; + k1 = new_k1; + } + if (i == 10) { alert("fit_cloth diverges at " + k0 + ", " + k1); } + return [k0, k1]; +} + +function get_ths_straight() { + return [0, 0]; +} + +function get_ths_left() { + return [this.init_th1 + this.right.dth, this.init_th1 + this.right.dth]; +} + +function get_ths_right() { + return [this.init_th0 - this.left.dth, this.init_th0 - this.left.dth]; +} + +function get_ths_g2() { + return [this.init_th0 - this.left.dth, this.init_th1 + this.right.dth]; +} + +function setup_solver(path) { + var segs = new Array; + var nodes = new Array; + + for (var i = 0; i < path.length - 1; i++) { + var seg = {}; + var dx = path[i + 1][0] - path[i][0]; + var dy = path[i + 1][1] - path[i][1]; + seg.th = Math.atan2(dy, dx); + seg.chord = Math.sqrt(dx * dx + dy * dy); + segs.push(seg); + } + for (i = 0; i < path.length; i++) { + var node = {}; + node.xy = path[i]; + node.dth = 0; + if (i > 0) { + node.left = segs[i - 1]; + } + if (i < path.length - 1) { + node.right = segs[i]; + } + if (node.left) node.left.right = node; + if (node.right) node.right.left = node; + if (node.left && node.right) { + var th = node.right.th - node.left.th; + if (th > Math.PI) th -= 2 * Math.PI; + if (th < -Math.PI) th += 2 * Math.PI; + node.th = th; + var chord_sum = node.left.chord + node.right.chord; + node.left.init_th1 = th * node.left.chord / chord_sum; + node.right.init_th0 = th * node.right.chord / chord_sum; + } + nodes.push(node); + } + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if (seg.init_th0 == undefined) { + if (seg.init_th1 == undefined) { + seg.init_th0 = 0; + seg.init_th1 = 0; + seg.get_ths = get_ths_straight; + } else { + seg.init_th0 = seg.init_th1; + seg.get_ths = get_ths_left; + } + } else { + if (seg.init_th1 == undefined) { + seg.init_th1 = seg.init_th0; + seg.get_ths = get_ths_right; + } else { + seg.get_ths = get_ths_g2; + } + } + } + return [segs, nodes]; +} + +xp = [[10, 100], [50, 20], [100, 120], [150, 100]] addfile ./test.html hunk ./test.html 1 + + + + + + + +

Message

+ + }