| 7 | | |
| 8 | | function mouseDown(event) { |
| 9 | | var point = new Point(event.clientX, event.clientY); |
| 10 | | } |
| 11 | | |
| 12 | | function mouseMove(event) { |
| 13 | | if (editing) moveDot(editing, event.clientX, event.clientY); |
| 14 | | } |
| 15 | | |
| 16 | | function mouseUp() { |
| 17 | | editing = null; |
| 18 | | } |
| 19 | | |
| 20 | | |
| 21 | | function Point(x, y) { |
| 22 | | |
| 23 | | var point = dot(x, y, 10); |
| 24 | | point.g = document.createElementNS("http://www.w3.org/2000/svg", "g"); |
| 25 | | addClass(point.g, "point"); |
| 26 | | |
| 27 | | point.handle1 = createHandle(point); |
| 28 | | point.handle2 = createHandle(point); |
| 29 | | |
| 30 | | point.handle1.linkedHandle = point.handle2; |
| 31 | | point.handle2.linkedHandle = point.handle1; |
| 32 | | |
| 33 | | editing = point.handle2; |
| 34 | | |
| 35 | | if (!curve) { |
| 36 | | startCurve(point); |
| 37 | | } else { |
| 38 | | extendCurve(point); |
| 39 | | } |
| 40 | | |
| 41 | | $(point.g).append(point); |
| 42 | | $(curve.g).append(point.g); |
| | 7 | Object.prototype.beget = function () { |
| | 8 | function F() {} |
| | 9 | F.prototype = this; |
| | 10 | return new F(); |
| | 11 | }; |
| | 12 | |
| | 13 | function callback(instance, method) { |
| | 14 | return function() { |
| | 15 | method.apply(instance, arguments); |
| | 16 | } |
| 70 | | function dot(x, y, size) { |
| 71 | | var dot = document.createElementNS("http://www.w3.org/2000/svg", "circle"); |
| 72 | | dot.cx.baseVal.value = x; |
| 73 | | dot.cy.baseVal.value = y; |
| 74 | | dot.r.baseVal.value = size/2; |
| 75 | | $(dot).mousedown(function(event) { |
| 76 | | event.stopPropagation(); |
| 77 | | editing = dot; |
| 78 | | addClass(dot, "editing"); |
| 79 | | |
| 80 | | // alt unlinks two handles |
| 81 | | if (event.altKey && dot.linkedHandle) { |
| 82 | | dot.linkedHandle.linkedHandle = null; |
| 83 | | dot.linkedHandle = null; |
| 84 | | } |
| 85 | | }); |
| 86 | | $(dot).mouseup(function(event) { |
| 87 | | editing = null; |
| 88 | | removeClass(dot, "editing"); |
| 89 | | }); |
| 90 | | return dot; |
| 91 | | } |
| 92 | | |
| 93 | | function startCurve(point) { |
| 94 | | curve = document.createElementNS("http://www.w3.org/2000/svg", "path"); |
| 95 | | curve.g = document.createElementNS("http://www.w3.org/2000/svg", "g"); |
| 96 | | addClass(curve, "stroke"); |
| 97 | | |
| 98 | | point.curveTo = curve.createSVGPathSegMovetoAbs(point.cx.baseVal.value, point.cy.baseVal.value); |
| 99 | | curve.pathSegList.appendItem(point.curveTo); |
| 100 | | curve.start = point; |
| 101 | | curve.end = point; |
| 102 | | |
| 103 | | $(curve.g).append(curve); |
| 104 | | $("#canvas").append(curve.g); |
| 105 | | } |
| 106 | | |
| 107 | | function extendCurve(point) { |
| 108 | | point.curveTo = curve.createSVGPathSegCurvetoCubicAbs( |
| 109 | | point.cx.baseVal.value, |
| 110 | | point.cy.baseVal.value, |
| 111 | | curve.end.handle2.cx.baseVal.value, |
| 112 | | curve.end.handle2.cy.baseVal.value, |
| 113 | | point.handle1.cx.baseVal.value, |
| 114 | | point.handle1.cy.baseVal.value |
| 115 | | ); |
| 116 | | curve.pathSegList.appendItem(point.curveTo); |
| 117 | | point.handle1.leading = point; |
| 118 | | curve.end.handle2.trailing = point; |
| 119 | | curve.end = point; |
| 120 | | } |
| 121 | | |
| 122 | | function closeCurve() { |
| 123 | | curve.pathSegList.appendItem(curve.createSVGPathSegClosePath()); |
| 124 | | curve = null; |
| 125 | | } |
| 126 | | |
| 127 | | function endCurve() { |
| 128 | | curve = null; |
| 129 | | } |
| 130 | | |
| 131 | | function createHandle(point) { |
| 132 | | var handle = dot(point.cx.baseVal.value, point.cy.baseVal.value, 6); |
| 133 | | addClass(handle, "handle"); |
| 134 | | handle.point = point; |
| 135 | | handle.line = document.createElementNS("http://www.w3.org/2000/svg", "path"); |
| 136 | | |
| 137 | | handle.moveTo = handle.line.createSVGPathSegMovetoAbs(handle.cx.baseVal.value, handle.cy.baseVal.value); |
| 138 | | handle.lineTo = handle.line.createSVGPathSegLinetoAbs(point.cx.baseVal.value, point.cy.baseVal.value); |
| 139 | | handle.line.pathSegList.appendItem(handle.moveTo); |
| 140 | | handle.line.pathSegList.appendItem(handle.lineTo); |
| 141 | | $(point.g).append(handle.line); |
| 142 | | $(point.g).append(handle); |
| 143 | | return handle; |
| 144 | | } |
| 145 | | |
| 146 | | |
| 147 | | |
| 148 | | function moveDot(dot, x, y) { |
| 149 | | if (dot.line) { |
| 150 | | moveHandle(dot, x, y); |
| | 44 | function Dot(x, y, size) { |
| | 45 | this.dot = document.createElementNS("http://www.w3.org/2000/svg", "circle"); |
| | 46 | this.x(x); |
| | 47 | this.y(y); |
| | 48 | this.dot.r.baseVal.value = size/2; |
| | 49 | |
| | 50 | $(this.dot).mousedown(callback(this, this.mouseDown)); |
| | 51 | //this.dot.addEventListener("mouseup", this.mouseUp, false); |
| | 52 | } |
| | 53 | Dot.prototype.x = function(value) { |
| | 54 | if (value) { |
| | 55 | this.dot.cx.baseVal.value = value; |
| 152 | | movePoint(dot, x, y); |
| 153 | | } |
| 154 | | } |
| 155 | | |
| 156 | | function movePoint(point, x, y) { |
| 157 | | var dx = x - point.cx.baseVal.value; |
| 158 | | var dy = y - point.cy.baseVal.value; |
| | 57 | return this.dot.cx.baseVal.value; |
| | 58 | } |
| | 59 | }; |
| | 60 | Dot.prototype.y = function(value) { |
| | 61 | if (value) { |
| | 62 | this.dot.cy.baseVal.value = value; |
| | 63 | } else { |
| | 64 | return this.dot.cy.baseVal.value; |
| | 65 | } |
| | 66 | }; |
| | 67 | Dot.prototype.mouseDown = function(event) { |
| | 68 | editing = this; |
| | 69 | addClass(event.target, "editing"); |
| | 70 | event.stopPropagation(); |
| | 71 | }; |
| | 72 | Dot.prototype.move = function(event) { |
| | 73 | alert("abstract"); |
| | 74 | } |
| | 75 | |
| | 76 | Point.prototype = new Dot(); |
| | 77 | function Point(x, y) { |
| | 78 | Dot.call(this, x, y, 10); |
| | 79 | |
| | 80 | this.g = document.createElementNS("http://www.w3.org/2000/svg", "g"); |
| | 81 | addClass(this.g, "point"); |
| | 82 | |
| | 83 | this.handle1 = new Handle(this); |
| | 84 | this.handle2 = new Handle(this); |
| | 85 | |
| | 86 | this.handle1.linkedHandle = this.handle2; |
| | 87 | this.handle2.linkedHandle = this.handle1; |
| | 88 | |
| | 89 | editing = this.handle2; |
| | 90 | |
| | 91 | if (!curve) { |
| | 92 | curve = new Curve(this); |
| | 93 | } else { |
| | 94 | curve.extend(this); |
| | 95 | } |
| | 96 | |
| | 97 | $(this.g).append(this.dot); |
| | 98 | $(curve.g).append(this.g); |
| | 99 | |
| | 100 | } |
| | 101 | Point.prototype.move = function(x, y) { |
| | 102 | var dx = x - this.x(); |
| | 103 | var dy = y - this.y(); |
| 165 | | if (point.handle1) { |
| 166 | | var hx = point.handle1.cx.baseVal.value+dx; |
| 167 | | var hy = point.handle1.cy.baseVal.value+dy; |
| 168 | | moveHandle(point.handle1, hx, hy, true); |
| 169 | | var hx = point.handle2.cx.baseVal.value+dx; |
| 170 | | var hy = point.handle2.cy.baseVal.value+dy; |
| 171 | | moveHandle(point.handle2, hx, hy, true); |
| | 110 | if (this.handle1) { |
| | 111 | var hx = this.handle1.x()+dx; |
| | 112 | var hy = this.handle1.y()+dy; |
| | 113 | this.handle1.move(hx, hy, true); |
| | 114 | var hx = this.handle2.x()+dx; |
| | 115 | var hy = this.handle2.y()+dy; |
| | 116 | this.handle2.move(hx, hy, true); |
| 175 | | if (point.curveTo) { |
| 176 | | point.curveTo.x = x; |
| 177 | | point.curveTo.y = y; |
| 178 | | } |
| 179 | | } |
| 180 | | |
| 181 | | function moveHandle(handle, x, y, stop) { |
| 182 | | |
| 183 | | // move linked handle |
| 184 | | if ((!stop) && handle.linkedHandle) { |
| 185 | | var hx = handle.linkedHandle.cx.baseVal.value- (x-handle.cx.baseVal.value); |
| 186 | | var hy = handle.linkedHandle.cy.baseVal.value- (y-handle.cy.baseVal.value); |
| 187 | | moveHandle(handle.linkedHandle, hx, hy, true); |
| | 120 | if (this.curveTo) { |
| | 121 | this.curveTo.x = x; |
| | 122 | this.curveTo.y = y; |
| | 123 | } |
| | 124 | }; |
| | 125 | |
| | 126 | |
| | 127 | Handle.prototype = new Dot(); |
| | 128 | function Handle(point) { |
| | 129 | |
| | 130 | Dot.call(this, point.x(), point.y(), 6); |
| | 131 | addClass(this.dot, "handle"); |
| | 132 | this.point = point; |
| | 133 | this.line = document.createElementNS("http://www.w3.org/2000/svg", "path"); |
| | 134 | |
| | 135 | this.moveTo = this.line.createSVGPathSegMovetoAbs(this.x(), this.y()); |
| | 136 | this.lineTo = this.line.createSVGPathSegLinetoAbs(this.x(), this.y()); |
| | 137 | this.line.pathSegList.appendItem(this.moveTo); |
| | 138 | this.line.pathSegList.appendItem(this.lineTo); |
| | 139 | $(point.g).append(this.line); |
| | 140 | $(point.g).append(this.dot); |
| | 141 | |
| | 142 | } |
| | 143 | |
| | 144 | Handle.prototype.move = function(x, y, stop) { |
| | 145 | |
| | 146 | // move linked this |
| | 147 | if ((!stop) && this.linkedHandle) { |
| | 148 | var hx = this.linkedHandle.x()- (x-this.x()); |
| | 149 | var hy = this.linkedHandle.y()- (y-this.y()); |
| | 150 | this.linkedHandle.move(hx, hy, true); |
| 201 | | if (handle.leading) { |
| 202 | | handle.leading.curveTo.x2 = x; |
| 203 | | handle.leading.curveTo.y2 = y; |
| 204 | | } else if (handle.trailing) { |
| 205 | | handle.trailing.curveTo.x1 = x; |
| 206 | | handle.trailing.curveTo.y1 = y; |
| 207 | | } |
| 208 | | } |
| 209 | | |
| | 164 | if (this.leading) { |
| | 165 | this.leading.curveTo.x2 = x; |
| | 166 | this.leading.curveTo.y2 = y; |
| | 167 | } else if (this.trailing) { |
| | 168 | this.trailing.curveTo.x1 = x; |
| | 169 | this.trailing.curveTo.y1 = y; |
| | 170 | } |
| | 171 | }; |
| | 172 | Handle.prototype.mouseDown = function(event) { |
| | 173 | Dot.prototype.mouseDown.call(this, event); |
| | 174 | |
| | 175 | // alt unlinks two handles |
| | 176 | if (event.altKey && dot.linkedHandle) { |
| | 177 | this.linkedHandle.linkedHandle = null; |
| | 178 | this.linkedHandle = null; |
| | 179 | } |
| | 180 | }; |
| | 181 | |
| | 182 | |
| | 183 | function Curve(point) { |
| | 184 | |
| | 185 | this.extend = function(point) { |
| | 186 | point.curveTo = this.curve.createSVGPathSegCurvetoCubicAbs( |
| | 187 | point.x(), |
| | 188 | point.y(), |
| | 189 | this.end.handle2.x(), |
| | 190 | this.end.handle2.y(), |
| | 191 | point.handle1.x(), |
| | 192 | point.handle1.y() |
| | 193 | ); |
| | 194 | this.curve.pathSegList.appendItem(point.curveTo); |
| | 195 | point.handle1.leading = point; |
| | 196 | this.end.handle2.trailing = point; |
| | 197 | this.end = point; |
| | 198 | } |
| | 199 | |
| | 200 | this.close = function() { |
| | 201 | this.curve.pathSegList.appendItem(this.curve.createSVGPathSegClosePath()); |
| | 202 | curve = null; |
| | 203 | } |
| | 204 | |
| | 205 | this.cut = function() { |
| | 206 | curve = null; |
| | 207 | } |
| | 208 | |
| | 209 | this.curve = document.createElementNS("http://www.w3.org/2000/svg", "path"); |
| | 210 | this.g = document.createElementNS("http://www.w3.org/2000/svg", "g"); |
| | 211 | addClass(this.curve, "stroke"); |
| | 212 | |
| | 213 | point.curveTo = this.curve.createSVGPathSegMovetoAbs(point.x(), point.y()); |
| | 214 | this.curve.pathSegList.appendItem(point.curveTo); |
| | 215 | this.start = point; |
| | 216 | this.end = point; |
| | 217 | |
| | 218 | $(this.g).append(this.curve); |
| | 219 | $("#canvas").append(this.g); |
| | 220 | } |
| | 221 | |
| | 222 | // global events |
| | 223 | |
| | 224 | function mouseDown(event) { |
| | 225 | var point = new Point(event.clientX, event.clientY); |
| | 226 | } |
| | 227 | |
| | 228 | function mouseMove(event) { |
| | 229 | if (editing) { |
| | 230 | editing.move(event.clientX, event.clientY); |
| | 231 | } |
| | 232 | } |
| | 233 | |
| | 234 | function mouseUp() { |
| | 235 | removeClass(editing, "editing"); |
| | 236 | editing = null; |
| | 237 | } |