source: other-projects/nz-flag-design/trunk/design-2d/Original editor.method.ac/editor/src/path.js@ 29468

Last change on this file since 29468 was 29468, checked in by sjs49, 9 years ago

Initial commit for editor.method.ac for flag design

  • Property svn:executable set to *
File size: 25.5 KB
Line 
1/**
2 * Package: svgedit.path
3 *
4 * Licensed under the Apache License, Version 2
5 *
6 * Copyright(c) 2011 Alexis Deveria
7 * Copyright(c) 2011 Jeff Schiller
8 */
9
10// Dependencies:
11// 1) jQuery
12// 2) browser.js
13// 3) math.js
14// 4) svgutils.js
15
16var svgedit = svgedit || {};
17
18(function() {
19
20if (!svgedit.path) {
21 svgedit.path = {};
22}
23
24var svgns = "http://www.w3.org/2000/svg";
25
26var uiStrings = {
27 "pathNodeTooltip": "Drag node to move it. Double-click node to change segment type",
28 "pathCtrlPtTooltip": "Drag control point to adjust curve properties"
29};
30
31var segData = {
32 2: ['x','y'],
33 4: ['x','y'],
34 6: ['x','y','x1','y1','x2','y2'],
35 8: ['x','y','x1','y1'],
36 10: ['x','y','r1','r2','angle','largeArcFlag','sweepFlag'],
37 12: ['x'],
38 14: ['y'],
39 16: ['x','y','x2','y2'],
40 18: ['x','y']
41};
42
43var pathFuncs = [];
44
45var link_control_pts = false;
46
47// Stores references to paths via IDs.
48// TODO: Make this cross-document happy.
49var pathData = {};
50
51svgedit.path.setLinkControlPoints = function(lcp) {
52 link_control_pts = lcp;
53};
54
55svgedit.path.path = null;
56
57var editorContext_ = null;
58
59svgedit.path.init = function(editorContext) {
60 editorContext_ = editorContext;
61
62 pathFuncs = [0,'ClosePath'];
63 var pathFuncsStrs = ['Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc',
64 'LinetoHorizontal', 'LinetoVertical','CurvetoCubicSmooth','CurvetoQuadraticSmooth'];
65 $.each(pathFuncsStrs, function(i,s) {
66 pathFuncs.push(s+'Abs');
67 pathFuncs.push(s+'Rel');
68 });
69};
70
71svgedit.path.insertItemBefore = function(elem, newseg, index) {
72 // Support insertItemBefore on paths for FF2
73 var list = elem.pathSegList;
74
75 if(svgedit.browser.supportsPathInsertItemBefore()) {
76 list.insertItemBefore(newseg, index);
77 return;
78 }
79 //TODO!!!
80 var len = list.numberOfItems;
81 var arr = [];
82 for(var i=0; i<len; i++) {
83 var cur_seg = list.getItem(i);
84 arr.push(cur_seg)
85 }
86 list.clear();
87 for(var i=0; i<len; i++) {
88 if(i == index) { //index+1
89 list.appendItem(newseg);
90 }
91 list.appendItem(arr[i]);
92 }
93};
94
95// TODO: See if this should just live in replacePathSeg
96svgedit.path.ptObjToArr = function(type, seg_item) {
97 var arr = segData[type], len = arr.length;
98 var out = Array(len);
99 for(var i=0; i<len; i++) {
100 out[i] = seg_item[arr[i]];
101 }
102 return out;
103};
104
105svgedit.path.getGripPt = function(seg, alt_pt) {
106 var out = {
107 x: alt_pt? alt_pt.x : seg.item.x,
108 y: alt_pt? alt_pt.y : seg.item.y
109 }, path = seg.path;
110
111 if(path.matrix) {
112 var pt = svgedit.math.transformPoint(out.x, out.y, path.matrix);
113 out = pt;
114 }
115
116 out.x *= editorContext_.getCurrentZoom();
117 out.y *= editorContext_.getCurrentZoom();
118
119 return out;
120};
121
122svgedit.path.getPointFromGrip = function(pt, path) {
123 var out = {
124 x: pt.x,
125 y: pt.y
126 }
127
128 if(path.matrix) {
129 var pt = svgedit.math.transformPoint(out.x, out.y, path.imatrix);
130 out.x = pt.x;
131 out.y = pt.y;
132 }
133
134 out.x /= editorContext_.getCurrentZoom();
135 out.y /= editorContext_.getCurrentZoom();
136
137 return out;
138};
139
140svgedit.path.addPointGrip = function(index, x, y) {
141 // create the container of all the point grips
142 var pointGripContainer = svgedit.path.getGripContainer();
143
144 var pointGrip = svgedit.utilities.getElem("pathpointgrip_"+index);
145 // create it
146 if (!pointGrip) {
147 pointGrip = document.createElementNS(svgns, "rect");
148 svgedit.utilities.assignAttributes(pointGrip, {
149 'id': "pathpointgrip_" + index,
150 'display': "none",
151 'width': svgedit.browser.isTouch() ? 30 : 5,
152 'height': svgedit.browser.isTouch() ? 30 : 5,
153 'fill': "#fff",
154 'stroke': "#4F80FF",
155 'shape-rendering': "crispEdges",
156 'stroke-width': 1,
157 'cursor': 'move',
158 'style': 'pointer-events:all',
159 'xlink:title': uiStrings.pathNodeTooltip
160 });
161 pointGrip = pointGripContainer.appendChild(pointGrip);
162
163 var grip = $('#pathpointgrip_'+index);
164 grip.dblclick(function() {
165 if(svgedit.path.path) svgedit.path.path.setSegType();
166 });
167 }
168 if(x && y) {
169 // set up the point grip element and display it
170 svgedit.utilities.assignAttributes(pointGrip, {
171 'x': x-(svgedit.browser.isTouch() ? 15 : 2.5),
172 'y': y-(svgedit.browser.isTouch() ? 15 : 2.5),
173 'display': "inline"
174 });
175 }
176 return pointGrip;
177};
178
179svgedit.path.getGripContainer = function() {
180 var c = svgedit.utilities.getElem("pathpointgrip_container");
181 if (!c) {
182 var parent = svgedit.utilities.getElem("selectorParentGroup");
183 c = parent.appendChild(document.createElementNS(svgns, "g"));
184 c.id = "pathpointgrip_container";
185 }
186 return c;
187};
188
189svgedit.path.addCtrlGrip = function(id) {
190 var pointGrip = svgedit.utilities.getElem("ctrlpointgrip_"+id);
191 if(pointGrip) return pointGrip;
192
193 pointGrip = document.createElementNS(svgns, "circle");
194 svgedit.utilities.assignAttributes(pointGrip, {
195 'id': "ctrlpointgrip_" + id,
196 'display': "none",
197 'r': svgedit.browser.isTouch() ? 15 : 3,
198 'fill': "#4F80FF",
199 'stroke': '#4F80FF',
200 'stroke-opacity': 0,
201 'stroke-width': '3',
202 'cursor': 'move',
203 'style': 'pointer-events:all',
204 'xlink:title': uiStrings.pathCtrlPtTooltip
205 });
206 svgedit.path.getGripContainer().appendChild(pointGrip);
207 return pointGrip;
208};
209
210svgedit.path.getCtrlLine = function(id) {
211 var ctrlLine = svgedit.utilities.getElem("ctrlLine_"+id);
212 if(ctrlLine) return ctrlLine;
213
214 ctrlLine = document.createElementNS(svgns, "line");
215 svgedit.utilities.assignAttributes(ctrlLine, {
216 'id': "ctrlLine_"+id,
217 'stroke': "#4F80FF",
218 'stroke-width': 1,
219 "style": "pointer-events:none"
220 });
221 svgedit.path.getGripContainer().appendChild(ctrlLine);
222 return ctrlLine;
223};
224
225svgedit.path.getPointGrip = function(seg, update) {
226 var index = seg.index;
227 var pointGrip = svgedit.path.addPointGrip(index);
228 if(update) {
229 var pt = svgedit.path.getGripPt(seg);
230 svgedit.utilities.assignAttributes(pointGrip, {
231 'x': pt.x-(svgedit.browser.isTouch() ? 15 : 2.5),
232 'y': pt.y-(svgedit.browser.isTouch() ? 15 : 2.5),
233 'display': "inline"
234 });
235 }
236
237 return pointGrip;
238};
239
240svgedit.path.getControlPoints = function(seg) {
241 var item = seg.item;
242 var index = seg.index;
243 if(!item || !("x1" in item) || !("x2" in item)) return null;
244 var cpt = {};
245 var pointGripContainer = svgedit.path.getGripContainer();
246
247 // Note that this is intentionally not seg.prev.item
248 var prev = svgedit.path.path.segs[index-1].item;
249
250 var seg_items = [prev, item];
251
252 for(var i=1; i<3; i++) {
253 var id = index + 'c' + i;
254
255 var ctrlLine = cpt['c' + i + '_line'] = svgedit.path.getCtrlLine(id);
256
257 var pt = svgedit.path.getGripPt(seg, {x:item['x' + i], y:item['y' + i]});
258 var gpt = svgedit.path.getGripPt(seg, {x:seg_items[i-1].x, y:seg_items[i-1].y});
259
260 svgedit.utilities.assignAttributes(ctrlLine, {
261 'x1': pt.x,
262 'y1': pt.y,
263 'x2': gpt.x,
264 'y2': gpt.y,
265 'display': "inline"
266 });
267
268 cpt['c' + i + '_line'] = ctrlLine;
269
270 // create it
271 pointGrip = cpt['c' + i] = svgedit.path.addCtrlGrip(id);
272 svgedit.utilities.assignAttributes(pointGrip, {
273 'cx': pt.x,
274 'cy': pt.y,
275 'display': "inline"
276 });
277 cpt['c' + i] = pointGrip;
278 }
279 return cpt;
280};
281
282// This replaces the segment at the given index. Type is given as number.
283svgedit.path.replacePathSeg = function(type, index, pts, elem) {
284 var path = elem || svgedit.path.path.elem;
285 var func = 'createSVGPathSeg' + pathFuncs[type];
286 var seg = path[func].apply(path, pts);
287
288 if(svgedit.browser.supportsPathReplaceItem()) {
289 path.pathSegList.replaceItem(seg, index);
290 } else {
291 var segList = path.pathSegList;
292 var len = segList.numberOfItems;
293 var arr = [];
294 for(var i=0; i<len; i++) {
295 var cur_seg = segList.getItem(i);
296 arr.push(cur_seg)
297 }
298 segList.clear();
299 for(var i=0; i<len; i++) {
300 if(i == index) {
301 segList.appendItem(seg);
302 } else {
303 segList.appendItem(arr[i]);
304 }
305 }
306 }
307};
308
309svgedit.path.getSegSelector = function(seg, update) {
310 var index = seg.index;
311 var segLine = svgedit.utilities.getElem("segline_" + index);
312 if(!segLine) {
313 var pointGripContainer = svgedit.path.getGripContainer();
314 // create segline
315 segLine = document.createElementNS(svgns, "path");
316 svgedit.utilities.assignAttributes(segLine, {
317 'id': "segline_" + index,
318 'display': 'none',
319 'fill': "none",
320 'stroke': "#0ff",
321 'stroke-opacity': 1,
322 "shape-rendering": "crispEdges",
323 'stroke-width': 2,
324 'style':'pointer-events:none',
325 'd': 'M0,0 0,0'
326 });
327 pointGripContainer.appendChild(segLine);
328 }
329
330 if(update) {
331 var prev = seg.prev;
332 if(!prev) {
333 segLine.setAttribute("display", "none");
334 return segLine;
335 }
336
337 var pt = svgedit.path.getGripPt(prev);
338 // Set start point
339 svgedit.path.replacePathSeg(2, 0, [pt.x, pt.y], segLine);
340
341 var pts = svgedit.path.ptObjToArr(seg.type, seg.item, true);
342 for(var i=0; i < pts.length; i+=2) {
343 var pt = svgedit.path.getGripPt(seg, {x:pts[i], y:pts[i+1]});
344 pts[i] = pt.x;
345 pts[i+1] = pt.y;
346 }
347
348 svgedit.path.replacePathSeg(seg.type, 1, pts, segLine);
349 }
350 return segLine;
351};
352
353// Function: smoothControlPoints
354// Takes three points and creates a smoother line based on them
355//
356// Parameters:
357// ct1 - Object with x and y values (first control point)
358// ct2 - Object with x and y values (second control point)
359// pt - Object with x and y values (third point)
360//
361// Returns:
362// Array of two "smoothed" point objects
363svgedit.path.smoothControlPoints = this.smoothControlPoints = function(ct1, ct2, pt) {
364 // each point must not be the origin
365 var x1 = ct1.x - pt.x,
366 y1 = ct1.y - pt.y,
367 x2 = ct2.x - pt.x,
368 y2 = ct2.y - pt.y;
369
370 if ( (x1 != 0 || y1 != 0) && (x2 != 0 || y2 != 0) ) {
371 var anglea = Math.atan2(y1,x1),
372 angleb = Math.atan2(y2,x2),
373 r1 = Math.sqrt(x1*x1+y1*y1),
374 r2 = Math.sqrt(x2*x2+y2*y2),
375 nct1 = editorContext_.getSVGRoot().createSVGPoint(),
376 nct2 = editorContext_.getSVGRoot().createSVGPoint();
377 if (anglea < 0) { anglea += 2*Math.PI; }
378 if (angleb < 0) { angleb += 2*Math.PI; }
379
380 var angleBetween = Math.abs(anglea - angleb),
381 angleDiff = Math.abs(Math.PI - angleBetween)/2;
382
383 var new_anglea, new_angleb;
384 if (anglea - angleb > 0) {
385 new_anglea = angleBetween < Math.PI ? (anglea + angleDiff) : (anglea - angleDiff);
386 new_angleb = angleBetween < Math.PI ? (angleb - angleDiff) : (angleb + angleDiff);
387 }
388 else {
389 new_anglea = angleBetween < Math.PI ? (anglea - angleDiff) : (anglea + angleDiff);
390 new_angleb = angleBetween < Math.PI ? (angleb + angleDiff) : (angleb - angleDiff);
391 }
392
393 // rotate the points
394 nct1.x = r1 * Math.cos(new_anglea) + pt.x;
395 nct1.y = r1 * Math.sin(new_anglea) + pt.y;
396 nct2.x = r2 * Math.cos(new_angleb) + pt.x;
397 nct2.y = r2 * Math.sin(new_angleb) + pt.y;
398
399 return [nct1, nct2];
400 }
401 return undefined;
402};
403
404svgedit.path.Segment = function(index, item) {
405 this.selected = false;
406 this.index = index;
407 this.item = item;
408 this.type = item.pathSegType;
409
410 this.ctrlpts = [];
411 this.ptgrip = null;
412 this.segsel = null;
413};
414
415svgedit.path.Segment.prototype.showCtrlPts = function(y) {
416 for (var i in this.ctrlpts) {
417 this.ctrlpts[i].setAttribute("display", y ? "inline" : "none");
418 }
419};
420
421svgedit.path.Segment.prototype.selectCtrls = function(y) {
422 $('#ctrlpointgrip_' + this.index + 'c1, #ctrlpointgrip_' + this.index + 'c2').
423 attr('fill', '#4F80FF');
424};
425
426svgedit.path.Segment.prototype.show = function(y) {
427 if(this.ptgrip) {
428 this.ptgrip.setAttribute("display", y ? "inline" : "none");
429 this.segsel.setAttribute("display", y ? "inline" : "none");
430 // Show/hide all control points if available
431 this.showCtrlPts(y);
432 }
433};
434
435svgedit.path.Segment.prototype.select = function(y) {
436 if(this.ptgrip) {
437 this.ptgrip.setAttribute("stroke", y ? "#4F80FF" : "#4F80FF");
438 this.ptgrip.setAttribute("fill", y ? "#4F80FF" : "#fff");
439 this.segsel.setAttribute("display", y ? "inline" : "none");
440 if(this.ctrlpts) {
441 this.selectCtrls(y);
442 }
443 this.selected = y;
444 }
445};
446
447svgedit.path.Segment.prototype.addGrip = function() {
448 this.ptgrip = svgedit.path.getPointGrip(this, true);
449 this.ctrlpts = svgedit.path.getControlPoints(this, true);
450 this.segsel = svgedit.path.getSegSelector(this, true);
451};
452
453svgedit.path.Segment.prototype.update = function(full) {
454 if(this.ptgrip) {
455 var pt = svgedit.path.getGripPt(this);
456 var reposition = (svgedit.browser.isTouch() ? 15 : 2.5)
457 var properties = (this.ptgrip.nodeName == "rect") ? {'x': pt.x-reposition, 'y': pt.y-reposition} : {'cx': pt.x, 'cy': pt.y};
458 svgedit.utilities.assignAttributes(this.ptgrip, properties);
459 svgedit.path.getSegSelector(this, true);
460
461 if(this.ctrlpts) {
462 if(full) {
463 this.item = svgedit.path.path.elem.pathSegList.getItem(this.index);
464 this.type = this.item.pathSegType;
465 }
466 svgedit.path.getControlPoints(this);
467 }
468 // this.segsel.setAttribute("display", y?"inline":"none");
469 }
470};
471
472svgedit.path.Segment.prototype.move = function(dx, dy) {
473 var item = this.item;
474 if(this.ctrlpts) {
475 var cur_pts = [item.x += dx, item.y += dy,
476 item.x1, item.y1, item.x2 += dx, item.y2 += dy];
477 } else {
478 var cur_pts = [item.x += dx, item.y += dy];
479 }
480 svgedit.path.replacePathSeg(this.type, this.index, cur_pts);
481
482 if(this.next && this.next.ctrlpts) {
483 var next = this.next.item;
484 var next_pts = [next.x, next.y,
485 next.x1 += dx, next.y1 += dy, next.x2, next.y2];
486 svgedit.path.replacePathSeg(this.next.type, this.next.index, next_pts);
487 }
488
489 if(this.mate) {
490 // The last point of a closed subpath has a "mate",
491 // which is the "M" segment of the subpath
492 var item = this.mate.item;
493 var pts = [item.x += dx, item.y += dy];
494 svgedit.path.replacePathSeg(this.mate.type, this.mate.index, pts);
495 // Has no grip, so does not need "updating"?
496 }
497
498 this.update(true);
499 if(this.next) this.next.update(true);
500};
501
502svgedit.path.Segment.prototype.setLinked = function(num) {
503 var seg, anum, pt;
504 if (num == 2) {
505 anum = 1;
506 seg = this.next;
507 if(!seg) return;
508 pt = this.item;
509 } else {
510 anum = 2;
511 seg = this.prev;
512 if(!seg) return;
513 pt = seg.item;
514 }
515
516 var item = seg.item;
517 item['x' + anum] = pt.x + pt.x - this.item['x' + num];
518 item['y' + anum] = pt.y + pt.y - this.item['y' + num];
519
520 var pts = [item.x, item.y,
521 item.x1, item.y1,
522 item.x2, item.y2];
523 svgedit.path.replacePathSeg(seg.type, seg.index, pts);
524 seg.update(true);
525};
526
527svgedit.path.Segment.prototype.moveCtrl = function(num, dx, dy) {
528 var item = this.item;
529
530 item['x' + num] += dx;
531 item['y' + num] += dy;
532
533 var pts = [item.x,item.y,
534 item.x1,item.y1, item.x2,item.y2];
535
536 svgedit.path.replacePathSeg(this.type, this.index, pts);
537 this.update(true);
538};
539
540svgedit.path.Segment.prototype.setType = function(new_type, pts) {
541 svgedit.path.replacePathSeg(new_type, this.index, pts);
542 this.type = new_type;
543 this.item = svgedit.path.path.elem.pathSegList.getItem(this.index);
544 this.showCtrlPts(new_type === 6);
545 this.ctrlpts = svgedit.path.getControlPoints(this);
546 this.update(true);
547};
548
549svgedit.path.Path = function(elem) {
550 if(!elem || elem.tagName !== "path") {
551 throw "svgedit.path.Path constructed without a <path> element";
552 }
553
554 this.elem = elem;
555 this.segs = [];
556 this.selected_pts = [];
557 svgedit.path.path = this;
558
559 this.init();
560};
561
562// Reset path data
563svgedit.path.Path.prototype.init = function() {
564 // Hide all grips, etc
565 $(svgedit.path.getGripContainer()).find("*").attr("display", "none");
566 var segList = this.elem.pathSegList;
567 var len = segList.numberOfItems;
568 this.segs = [];
569 this.selected_pts = [];
570 this.first_seg = null;
571
572 // Set up segs array
573 for(var i=0; i < len; i++) {
574 var item = segList.getItem(i);
575 var segment = new svgedit.path.Segment(i, item);
576 segment.path = this;
577 this.segs.push(segment);
578 }
579
580 var segs = this.segs;
581 var start_i = null;
582
583 for(var i=0; i < len; i++) {
584 var seg = segs[i];
585 var next_seg = (i+1) >= len ? null : segs[i+1];
586 var prev_seg = (i-1) < 0 ? null : segs[i-1];
587
588 if(seg.type === 2) {
589 if(prev_seg && prev_seg.type !== 1) {
590 // New sub-path, last one is open,
591 // so add a grip to last sub-path's first point
592 var start_seg = segs[start_i];
593 start_seg.next = segs[start_i+1];
594 start_seg.next.prev = start_seg;
595 start_seg.addGrip();
596 }
597 // Remember that this is a starter seg
598 start_i = i;
599 } else if(next_seg && next_seg.type === 1) {
600 // This is the last real segment of a closed sub-path
601 // Next is first seg after "M"
602 seg.next = segs[start_i+1];
603
604 // First seg after "M"'s prev is this
605 seg.next.prev = seg;
606 seg.mate = segs[start_i];
607 seg.addGrip();
608 if(this.first_seg == null) {
609 this.first_seg = seg;
610 }
611 } else if(!next_seg) {
612 if(seg.type !== 1) {
613 // Last seg, doesn't close so add a grip
614 // to last sub-path's first point
615 var start_seg = segs[start_i];
616 start_seg.next = segs[start_i+1];
617 start_seg.next.prev = start_seg;
618 start_seg.addGrip();
619 seg.addGrip();
620
621 if(!this.first_seg) {
622 // Open path, so set first as real first and add grip
623 this.first_seg = segs[start_i];
624 }
625 }
626 } else if(seg.type !== 1){
627 // Regular segment, so add grip and its "next"
628 seg.addGrip();
629
630 // Don't set its "next" if it's an "M"
631 if(next_seg && next_seg.type !== 2) {
632 seg.next = next_seg;
633 seg.next.prev = seg;
634 }
635 }
636 }
637 return this;
638};
639
640svgedit.path.Path.prototype.eachSeg = function(fn) {
641 var len = this.segs.length
642 for(var i=0; i < len; i++) {
643 var ret = fn.call(this.segs[i], i);
644 if(ret === false) break;
645 }
646};
647
648svgedit.path.Path.prototype.addSeg = function(index) {
649 // Adds a new segment
650 var seg = this.segs[index];
651 if(!seg.prev) return;
652
653 var prev = seg.prev;
654 var newseg;
655 switch(seg.item.pathSegType) {
656 case 4:
657 var new_x = (seg.item.x + prev.item.x) / 2;
658 var new_y = (seg.item.y + prev.item.y) / 2;
659 newseg = this.elem.createSVGPathSegLinetoAbs(new_x, new_y);
660 break;
661 case 6: //make it a curved segment to preserve the shape (WRS)
662 // http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm#Geometric_interpretation
663 var p0_x = (prev.item.x + seg.item.x1)/2;
664 var p1_x = (seg.item.x1 + seg.item.x2)/2;
665 var p2_x = (seg.item.x2 + seg.item.x)/2;
666 var p01_x = (p0_x + p1_x)/2;
667 var p12_x = (p1_x + p2_x)/2;
668 var new_x = (p01_x + p12_x)/2;
669 var p0_y = (prev.item.y + seg.item.y1)/2;
670 var p1_y = (seg.item.y1 + seg.item.y2)/2;
671 var p2_y = (seg.item.y2 + seg.item.y)/2;
672 var p01_y = (p0_y + p1_y)/2;
673 var p12_y = (p1_y + p2_y)/2;
674 var new_y = (p01_y + p12_y)/2;
675 newseg = this.elem.createSVGPathSegCurvetoCubicAbs(new_x,new_y, p0_x,p0_y, p01_x,p01_y);
676 var pts = [seg.item.x,seg.item.y,p12_x,p12_y,p2_x,p2_y];
677 svgedit.path.replacePathSeg(seg.type,index,pts);
678 break;
679 }
680
681 svgedit.path.insertItemBefore(this.elem, newseg, index);
682};
683
684svgedit.path.Path.prototype.deleteSeg = function(index) {
685 var seg = this.segs[index];
686 var list = this.elem.pathSegList;
687
688 seg.show(false);
689 var next = seg.next;
690 if(seg.mate) {
691 // Make the next point be the "M" point
692 var pt = [next.item.x, next.item.y];
693 svgedit.path.replacePathSeg(2, next.index, pt);
694
695 // Reposition last node
696 svgedit.path.replacePathSeg(4, seg.index, pt);
697
698 list.removeItem(seg.mate.index);
699 } else if(!seg.prev) {
700 // First node of open path, make next point the M
701 var item = seg.item;
702 var pt = [next.item.x, next.item.y];
703 svgedit.path.replacePathSeg(2, seg.next.index, pt);
704 list.removeItem(index);
705
706 } else {
707 list.removeItem(index);
708 }
709};
710
711svgedit.path.Path.prototype.subpathIsClosed = function(index) {
712 var closed = false;
713 // Check if subpath is already open
714 svgedit.path.path.eachSeg(function(i) {
715 if(i <= index) return true;
716 if(this.type === 2) {
717 // Found M first, so open
718 return false;
719 } else if(this.type === 1) {
720 // Found Z first, so closed
721 closed = true;
722 return false;
723 }
724 });
725
726 return closed;
727};
728
729svgedit.path.Path.prototype.removePtFromSelection = function(index) {
730 var pos = this.selected_pts.indexOf(index);
731 if(pos == -1) {
732 return;
733 }
734 this.segs[index].select(false);
735 this.selected_pts.splice(pos, 1);
736};
737
738svgedit.path.Path.prototype.clearSelection = function() {
739 this.eachSeg(function(i) {
740 // 'this' is the segment here
741 this.select(false);
742 });
743 this.selected_pts = [];
744};
745
746svgedit.path.Path.prototype.storeD = function() {
747 this.last_d = this.elem.getAttribute('d');
748};
749
750svgedit.path.Path.prototype.show = function(y) {
751 // Shows this path's segment grips
752 this.eachSeg(function() {
753 // 'this' is the segment here
754 this.show(y);
755 });
756 if(y) {
757 this.selectPt(this.first_seg.index);
758 }
759 return this;
760};
761
762// Move selected points
763svgedit.path.Path.prototype.movePts = function(d_x, d_y) {
764 var i = this.selected_pts.length;
765 while(i--) {
766 var seg = this.segs[this.selected_pts[i]];
767 seg.move(d_x, d_y);
768 }
769};
770
771svgedit.path.Path.prototype.moveCtrl = function(d_x, d_y) {
772 var seg = this.segs[this.selected_pts[0]];
773 seg.moveCtrl(this.dragctrl, d_x, d_y);
774 if(link_control_pts) {
775 seg.setLinked(this.dragctrl);
776 }
777};
778
779svgedit.path.Path.prototype.setSegType = function(new_type) {
780 this.storeD();
781 var i = this.selected_pts.length;
782 var text;
783 while(i--) {
784 var sel_pt = this.selected_pts[i];
785
786 // Selected seg
787 var cur = this.segs[sel_pt];
788 var prev = cur.prev;
789 if(!prev) continue;
790
791 if(!new_type) { // double-click, so just toggle
792 text = "Toggle Path Segment Type";
793
794 // Toggle segment to curve/straight line
795 var old_type = cur.type;
796
797 new_type = (old_type == 6) ? 4 : 6;
798 }
799
800 new_type = new_type-0;
801
802 var cur_x = cur.item.x;
803 var cur_y = cur.item.y;
804 var prev_x = prev.item.x;
805 var prev_y = prev.item.y;
806 var points;
807 switch ( new_type ) {
808 case 6:
809 if(cur.olditem) {
810 var old = cur.olditem;
811 points = [cur_x,cur_y, old.x1,old.y1, old.x2,old.y2];
812 } else {
813 var diff_x = cur_x - prev_x;
814 var diff_y = cur_y - prev_y;
815 // get control points from straight line segment
816 /*
817 var ct1_x = (prev_x + (diff_y/2));
818 var ct1_y = (prev_y - (diff_x/2));
819 var ct2_x = (cur_x + (diff_y/2));
820 var ct2_y = (cur_y - (diff_x/2));
821 */
822 //create control points on the line to preserve the shape (WRS)
823 var ct1_x = (prev_x + (diff_x/3));
824 var ct1_y = (prev_y + (diff_y/3));
825 var ct2_x = (cur_x - (diff_x/3));
826 var ct2_y = (cur_y - (diff_y/3));
827 points = [cur_x,cur_y, ct1_x,ct1_y, ct2_x,ct2_y];
828 }
829 break;
830 case 4:
831 points = [cur_x,cur_y];
832
833 // Store original prevve segment nums
834 cur.olditem = cur.item;
835 break;
836 }
837
838 cur.setType(new_type, points);
839 }
840 svgedit.path.path.endChanges(text);
841};
842
843svgedit.path.Path.prototype.selectPt = function(pt, ctrl_num) {
844 this.clearSelection();
845 if(pt == null) {
846 this.eachSeg(function(i) {
847 // 'this' is the segment here.
848 if(this.prev) {
849 pt = i;
850 }
851 });
852 }
853 this.addPtsToSelection(pt);
854 if(ctrl_num) {
855 this.dragctrl = ctrl_num;
856
857 if(link_control_pts) {
858 this.segs[pt].setLinked(ctrl_num);
859 }
860 }
861};
862
863// Update position of all points
864svgedit.path.Path.prototype.update = function() {
865 var elem = this.elem;
866 if(svgedit.utilities.getRotationAngle(elem)) {
867 this.matrix = svgedit.math.getMatrix(elem);
868 this.imatrix = this.matrix.inverse();
869 } else {
870 this.matrix = null;
871 this.imatrix = null;
872 }
873
874 this.eachSeg(function(i) {
875 this.item = elem.pathSegList.getItem(i);
876 this.update();
877 });
878
879 return this;
880};
881
882svgedit.path.getPath_ = function(elem) {
883 var p = pathData[elem.id];
884 if(!p) p = pathData[elem.id] = new svgedit.path.Path(elem);
885 return p;
886};
887
888svgedit.path.removePath_ = function(id) {
889 if(id in pathData) delete pathData[id];
890};
891
892var getRotVals = function(x, y, oldcx, oldcy, newcx, newcy, angle) {
893 dx = x - oldcx;
894 dy = y - oldcy;
895
896 // rotate the point around the old center
897 r = Math.sqrt(dx*dx + dy*dy);
898 theta = Math.atan2(dy,dx) + angle;
899 dx = r * Math.cos(theta) + oldcx;
900 dy = r * Math.sin(theta) + oldcy;
901
902 // dx,dy should now hold the actual coordinates of each
903 // point after being rotated
904
905 // now we want to rotate them around the new center in the reverse direction
906 dx -= newcx;
907 dy -= newcy;
908
909 r = Math.sqrt(dx*dx + dy*dy);
910 theta = Math.atan2(dy,dx) - angle;
911 return {'x':(r * Math.cos(theta) + newcx)/1,
912 'y':(r * Math.sin(theta) + newcy)/1};
913};
914
915// If the path was rotated, we must now pay the piper:
916// Every path point must be rotated into the rotated coordinate system of
917// its old center, then determine the new center, then rotate it back
918// This is because we want the path to remember its rotation
919
920// TODO: This is still using ye olde transform methods, can probably
921// be optimized or even taken care of by recalculateDimensions
922svgedit.path.recalcRotatedPath = function() {
923 var current_path = svgedit.path.path.elem;
924 var angle = svgedit.utilities.getRotationAngle(current_path, true);
925 if(!angle) return;
926// selectedBBoxes[0] = svgedit.path.path.oldbbox;
927 var box = svgedit.utilities.getBBox(current_path),
928 oldbox = svgedit.path.path.oldbbox,//selectedBBoxes[0],
929 oldcx = oldbox.x + oldbox.width/2,
930 oldcy = oldbox.y + oldbox.height/2,
931 newcx = box.x + box.width/2,
932 newcy = box.y + box.height/2,
933
934 // un-rotate the new center to the proper position
935 dx = newcx - oldcx,
936 dy = newcy - oldcy,
937 r = Math.sqrt(dx*dx + dy*dy),
938 theta = Math.atan2(dy,dx) + angle;
939
940 newcx = r * Math.cos(theta) + oldcx;
941 newcy = r * Math.sin(theta) + oldcy;
942
943 var list = current_path.pathSegList,
944 i = list.numberOfItems;
945 while (i) {
946 i -= 1;
947 var seg = list.getItem(i),
948 type = seg.pathSegType;
949 if(type == 1) continue;
950
951 var rvals = getRotVals(seg.x,seg.y, oldcx, oldcy, newcx, newcy, angle),
952 points = [rvals.x, rvals.y];
953 if(seg.x1 != null && seg.x2 != null) {
954 c_vals1 = getRotVals(seg.x1, seg.y1, oldcx, oldcy, newcx, newcy, angle);
955 c_vals2 = getRotVals(seg.x2, seg.y2, oldcx, oldcy, newcx, newcy, angle);
956 points.splice(points.length, 0, c_vals1.x , c_vals1.y, c_vals2.x, c_vals2.y);
957 }
958 svgedit.path.replacePathSeg(type, i, points);
959 } // loop for each point
960
961 box = svgedit.utilities.getBBox(current_path);
962// selectedBBoxes[0].x = box.x; selectedBBoxes[0].y = box.y;
963// selectedBBoxes[0].width = box.width; selectedBBoxes[0].height = box.height;
964
965 // now we must set the new transform to be rotated around the new center
966 var R_nc = svgroot.createSVGTransform(),
967 tlist = svgedit.transformlist.getTransformList(current_path);
968 R_nc.setRotate((angle * 180.0 / Math.PI), newcx, newcy);
969 tlist.replaceItem(R_nc,0);
970};
971
972// ====================================
973// Public API starts here
974
975svgedit.path.clearData = function() {
976 pathData = {};
977};
978
979})();
Note: See TracBrowser for help on using the repository browser.