[first cut at interactive euler spline editor
raph.levien@gmail.com**20070520010334] {
addfile ./spiro.html
hunk ./spiro.html 1
+
+
+
+
+
+
+
+Message
+
+
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);
-}
-
hunk ./spiro.js 214
+function fit_euler_ks(th0, th1, chord) {
+ var p = fit_euler(th0, th1);
+ var sc = p.chord / chord;
+ p.k0 = (p.ks[0] - .5 * p.ks[1]) * sc;
+ p.k1 = (p.ks[0] + .5 * p.ks[1]) * sc;
+ return p;
+}
+
hunk ./spiro.js 238
+function Spline(segs, nodes) {
+ this.segs = segs;
+ this.nodes = nodes;
+}
+
+Spline.prototype.show_in_shell = function () {
+ showobj(this.segs);
+ showobj(this.nodes);
+}
+
hunk ./spiro.js 303
- return {segs: segs, nodes: nodes};
+ return new Spline(segs, nodes);
+}
+
+function get_jacobian_g2(node) {
+ var save_dth = node.dth;
+ var delta = 1e-6;
+ node.dth += delta;
+
+ var ths = node.left.get_ths();
+ var lparms = fit_euler_ks(ths[0], ths[1], node.left.chord);
+
+ ths = node.right.get_ths();
+ var rparms = fit_euler_ks(ths[0], ths[1], node.right.chord);
+
+ node.dth = save_dth;
+
+ return [(lparms.k0 - node.left.params.k0) / delta,
+ (rparms.k0 - node.right.params.k0 - lparms.k1 + node.left.params.k1) / delta,
+ (-rparms.k1 + node.right.params.k1) / delta];
hunk ./spiro.js 325
+ var maxerr = 0;
hunk ./spiro.js 331
- seg.params = fit_cloth(ths[0], ths[1]);
+ seg.params = fit_euler_ks(ths[0], ths[1], seg.chord);
hunk ./spiro.js 333
+ var dks = [];
+ var mat = [];
hunk ./spiro.js 338
- var lparams = node.left.params;
- kleft = (lparams.ks[0] + .5 * lparams.ks[1]) * lparams.chord / node.left.chord;
- var rparams = node.right.params;
- kright = (rparams.ks[0] - .5 * rparams.ks[1]) * rparams.chord / node.right.chord;
- print('kleft = ' + String(kleft) + ', kright = ' + String(kright));
+ var kerr = node.right.params.k0 - node.left.params.k1;
+ dks.push(kerr);
+ if (Math.abs(kerr) > maxerr) maxerr = Math.abs(kerr);
+ mat.push({a: get_jacobian_g2(node)});
hunk ./spiro.js 344
+ if (mat.length == 0) return 0;
+ bandec(mat, mat.length, 1);
+ banbks(mat, dks, mat.length, 1);
+ j = 0;
+ for (i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ if (node.left && node.right) {
+ node.dth -= dks[j];
+ j += 1;
+ }
+ }
+ return maxerr;
hunk ./spiro.js 386
+// UI stuff follows
+
+function SpiroUi(canvas) {
+ this.canvas = canvas;
+ this.path = [];
+ this.hit = null;
+ var self = this;
+ canvas.onmousedown = function(evt) { self.down(evt); };
+ canvas.onmousemove = function(evt) { self.move(evt); };
+ canvas.onmouseup = function(evt) { self.up(evt); };
+}
+
+SpiroUi.prototype.paint = function() {
+ var ctx = this.canvas.getContext("2d");
+ ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ var spline = setup_solver(this.path);
+ while (refine_euler(spline) > 1e-6);
+ var nodes = spline.nodes;
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ ctx.beginPath();
+ ctx.arc(node.xy[0], node.xy[1], 2, 0, 2 * Math.PI, 0);
+ ctx.fill();
+ }
+ ctx.beginPath();
+ ctx.moveTo(nodes[0].xy[0], nodes[0].xy[1]);
+ var segs = spline.segs;
+ for (var i = 0; i < segs.length; i++) {
+ var seg = segs[i];
+ var ths = seg.get_ths();
+ var ks = fit_euler(ths[0], ths[1]).ks;
+ ks.push(0); ks.push(0);
+ seg_to_bez(ctx, ks, seg.left.xy[0], seg.left.xy[1], seg.right.xy[0], seg.right.xy[1]);
+ }
+ ctx.stroke();
+}
+
+
+
+SpiroUi.prototype.down = function(evt) {
+ var canvas = this.canvas;
+ var x = evt.offsetX != undefined ? evt.offsetX : evt.pageX - canvas.offsetLeft;
+ var y = evt.offsetY != undefined ? evt.offsetY : evt.pageY - canvas.offsetTop;
+ var hit = null;
+ var hitr2 = null;
+ for (var i = 0; i < this.path.length; i++) {
+ var xy = this.path[i];
+ var r2 = (xy[0] - x) * (xy[0] - x) + (xy[1] - y) * (xy[1] - y);
+ if (r2 < (hitr2 == null ? 10 : hitr2)) {
+ hit = i;
+ hitr2 = r2;
+ }
+ }
+ if (hit == null) {
+ this.path.push([x, y]);
+ hit = this.path.length - 1;
+ }
+ this.hit = hit;
+ evt.preventDefault();
+}
+
+SpiroUi.prototype.move = function(evt) {
+ var canvas = this.canvas;
+ var x = evt.offsetX != undefined ? evt.offsetX : evt.pageX - canvas.offsetLeft;
+ var y = evt.offsetY != undefined ? evt.offsetY : evt.pageY - canvas.offsetTop;
+ if (this.hit != null) {
+ this.path[this.hit] = [x, y];
+ this.paint();
+ }
+ evt.preventDefault();
+}
+
+SpiroUi.prototype.up = function(evt) {
+ this.hit = null;
+ evt.preventDefault();
+}
+
hunk ./test.html 5
+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);
+}
+
hunk ./test.html 63
+function print(s) {
+}
hunk ./test.html 73
- spline.nodes[2].dth = -.2;
+ while (refine_euler(spline) > 1e-6);
}