Move getPathBBox() and getBBox() into svgutils.js

git-svn-id: http://svg-edit.googlecode.com/svn/trunk@1840 eee81c28-f429-11dd-99c0-75d572ba1ddd
This commit is contained in:
Jeff Schiller
2010-11-05 15:29:30 +00:00
parent 0ce7e2adb7
commit b104277fa1
2 changed files with 172 additions and 166 deletions

View File

@@ -187,11 +187,8 @@ var support = svgedit.BrowserSupport,
dimensions: [640, 480]
};
// Much faster than running getBBox() every time
var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use';
var visElems_arr = visElems.split(',');
// var hidElems = 'clipPath,defs,desc,feGaussianBlur,filter,linearGradient,marker,mask,metadata,pattern,radialGradient,stop,switch,symbol,title,textPath';
var ref_attrs = ["clip-path", "fill", "filter", "marker-end", "marker-mid", "marker-start", "mask", "stroke"];
@@ -200,25 +197,7 @@ var support = svgedit.BrowserSupport,
if(config) {
$.extend(curConfig, config);
}
// Static class for various utility functions
// See svgutils.js.
var Utils = this.Utils = svgedit.Utilities;
// Function: snapToGrid
// round value to for snapping
// NOTE: This function did not move to svgutils.js since it depends on unit_types and curConfig.
Utils.snapToGrid = function(value){
var stepSize = curConfig.snappingStep;
var unit = curConfig.baseUnit;
if(unit !== "px") {
stepSize *= unit_types[unit];
}
value = Math.round(value/stepSize)*stepSize;
return value;
};
var snapToGrid = Utils.snapToGrid;
var elData = $.data;
// TODO: declare the variables and set them as null, then move this setup stuff to
@@ -250,7 +229,8 @@ var canvas = this,
dimensions = curConfig.dimensions;
// Create Root SVG element. This is a container for the document being edited, not the document itself.
var svgroot = svgdoc.importNode(Utils.text2xml('<svg id="svgroot" xmlns="' + svgns + '" xlinkns="' + xlinkns + '" ' +
var svgroot = svgdoc.importNode(svgedit.Utilities.text2xml(
'<svg id="svgroot" xmlns="' + svgns + '" xlinkns="' + xlinkns + '" ' +
'width="' + dimensions[0] + '" height="' + dimensions[1] + '" x="' + dimensions[0] + '" y="' + dimensions[1] + '" overflow="visible">' +
'<defs>' +
'<filter id="canvashadow" filterUnits="objectBoundingBox">' +
@@ -1432,16 +1412,36 @@ var SelectorManager;
};
}());
// import svgtransformlist.js
var SVGEditTransformList = svgedit.transformlist.SVGTransformList;
var getTransformList = this.getTransformList = svgedit.transformlist.getTransformList;
// import from svgutils.js
var walkTree = this.walkTree = svgedit.Utilities.walkTree;
var walkTreePost = this.walkTreePost = svgedit.Utilities.walkTreePost;
var getUrlFromAttr = this.getUrlFromAttr = svgedit.Utilities.getUrlFromAttr;
var getHref = this.getHref = svgedit.Utilities.getHref;
var setHref = this.setHref = svgedit.Utilities.setHref;
var getPathBBox = svgedit.Utilities.getPathBBox;
var bboxToObj = svgedit.Utilities.bboxToObj;
var getBBox = this.getBBox = svgedit.Utilities.getBBox;
// Function: snapToGrid
// round value to for snapping
// NOTE: This function did not move to svgutils.js since it depends on unit_types and curConfig.
svgedit.Utilities.snapToGrid = function(value){
var stepSize = curConfig.snappingStep;
var unit = curConfig.baseUnit;
if(unit !== "px") {
stepSize *= unit_types[unit];
}
value = Math.round(value/stepSize)*stepSize;
return value;
};
var snapToGrid = svgedit.Utilities.snapToGrid;
// import from math.js.
var transformPoint = svgedit.math.transformPoint;
@@ -1787,7 +1787,7 @@ var getIntersectionList = this.getIntersectionList = function(rect) {
var i = curBBoxes.length;
while (i--) {
if(!rubberBBox.width || !rubberBBox.width) continue;
if (Utils.rectsIntersect(rubberBBox, curBBoxes[i].bbox)) {
if (svgedit.Utilities.rectsIntersect(rubberBBox, curBBoxes[i].bbox)) {
resultList.push(curBBoxes[i].elem);
}
}
@@ -1798,6 +1798,7 @@ var getIntersectionList = this.getIntersectionList = function(rect) {
return resultList;
};
// TODO(codedread): Migrate this into svgutils.js
// Function: getStrokedBBox
// Get the bounding box for one or more stroked and/or transformed elements
//
@@ -2289,59 +2290,6 @@ var getRefElem = this.getRefElem = function(attrVal) {
return getElem(getUrlFromAttr(attrVal).substr(1));
}
var bboxToObj = Utils.bboxToObj;
// Function: getBBox
// Get the given/selected element's bounding box object, convert it to be more
// usable when necessary
//
// Parameters:
// elem - Optional DOM element to get the BBox for
var getBBox = this.getBBox = function(elem) {
var selected = elem || selectedElements[0];
if (elem.nodeType != 1) return null;
var ret = null;
var elname = selected.nodeName;
if(elname === 'text' && selected.textContent === '') {
selected.textContent = 'a'; // Some character needed for the selector to use.
ret = selected.getBBox();
selected.textContent = '';
} else if(elname === 'path' && isWebkit) {
ret = getPathBBox(selected);
} else if(elname === 'use' && !isWebkit || elname === 'foreignObject') {
ret = selected.getBBox();
var bb = {};
bb.width = ret.width;
bb.height = ret.height;
bb.x = ret.x + parseFloat(selected.getAttribute('x')||0);
bb.y = ret.y + parseFloat(selected.getAttribute('y')||0);
ret = bb;
} else if(~visElems_arr.indexOf(elname)) {
try { ret = selected.getBBox();}
catch(e) {
// Check if element is child of a foreignObject
var fo = $(selected).closest("foreignObject");
if(fo.length) {
try {
ret = fo[0].getBBox();
} catch(e) {
ret = null;
}
} else {
ret = null;
}
}
}
if(ret) {
ret = bboxToObj(ret);
}
// get the bounding box from the DOM (which is in that element's coordinate system)
return ret;
};
// Function: ffClone
// Hack for Firefox bugs where text element features aren't updated.
// This function clones the element and re-selects it
@@ -2361,86 +2309,6 @@ var ffClone = function(elem) {
return clone;
}
// Function: getPathBBox
// Get correct BBox for a path in Webkit
// Converted from code found here:
// http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
//
// Parameters:
// path - The path DOM element to get the BBox for
//
// Returns:
// A BBox-like object
var getPathBBox = function(path) {
var seglist = path.pathSegList;
var tot = seglist.numberOfItems;
var bounds = [[], []];
var start = seglist.getItem(0);
var P0 = [start.x, start.y];
for(var i=0; i < tot; i++) {
var seg = seglist.getItem(i);
if(!seg.x) continue;
// Add actual points to limits
bounds[0].push(P0[0]);
bounds[1].push(P0[1]);
if(seg.x1) {
var P1 = [seg.x1, seg.y1],
P2 = [seg.x2, seg.y2],
P3 = [seg.x, seg.y];
for(var j=0; j < 2; j++) {
var calc = function(t) {
return Math.pow(1-t,3) * P0[j]
+ 3 * Math.pow(1-t,2) * t * P1[j]
+ 3 * (1-t) * Math.pow(t,2) * P2[j]
+ Math.pow(t,3) * P3[j];
};
var b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j];
var a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j];
var c = 3 * P1[j] - 3 * P0[j];
if(a == 0) {
if(b == 0) {
continue;
}
var t = -c / b;
if(0 < t && t < 1) {
bounds[j].push(calc(t));
}
continue;
}
var b2ac = Math.pow(b,2) - 4 * c * a;
if(b2ac < 0) continue;
var t1 = (-b + Math.sqrt(b2ac))/(2 * a);
if(0 < t1 && t1 < 1) bounds[j].push(calc(t1));
var t2 = (-b - Math.sqrt(b2ac))/(2 * a);
if(0 < t2 && t2 < 1) bounds[j].push(calc(t2));
}
P0 = P3;
} else {
bounds[0].push(seg.x);
bounds[1].push(seg.y);
}
}
var x = Math.min.apply(null, bounds[0]);
var w = Math.max.apply(null, bounds[0]) - x;
var y = Math.min.apply(null, bounds[1]);
var h = Math.max.apply(null, bounds[1]) - y;
return {
'x': x,
'y': y,
'width': w,
'height': h
};
}
// this.each is deprecated, if any extension used this it can be recreated by doing this:
// $(canvas.getRootElem()).children().each(...)
@@ -2452,6 +2320,7 @@ var getPathBBox = function(path) {
// Group: Element Transforms
// TODO(codedread): Migrate this into svgutils.js
// Function: getRotationAngle
// Get the rotation angle of the given/selected DOM element
//
@@ -5249,7 +5118,7 @@ var textActions = canvas.textActions = function() {
// TODO: Find a way to make this work: Use transformed BBox instead of evt.target
// if(last_x === mouse_x && last_y === mouse_y
// && !Utils.rectsIntersect(transbb, {x: pt.x, y: pt.y, width:0, height:0})) {
// && !svgedit.Utilities.rectsIntersect(transbb, {x: pt.x, y: pt.y, width:0, height:0})) {
// textActions.toSelectMode(true);
// }
if(last_x === mouse_x && last_y === mouse_y && evt.target !== curtext) {
@@ -6712,7 +6581,7 @@ var pathActions = this.pathActions = function() {
height: 0
};
var sel = Utils.rectsIntersect(rbb, pt_bb);
var sel = svgedit.Utilities.rectsIntersect(rbb, pt_bb);
this.select(sel);
//Note that addPtsToSelection is not being run
@@ -7449,7 +7318,7 @@ var svgCanvasToString = this.svgCanvasToString = function() {
// Returns:
// String with the given element as an SVG tag
var svgToString = this.svgToString = function(elem, indent) {
var out = new Array(), toXml = Utils.toXml;
var out = new Array(), toXml = svgedit.Utilities.toXml;
var unit = curConfig.baseUnit
var unit_re = new RegExp('^-?[\\d\\.]+' + unit + '$');
@@ -7891,7 +7760,6 @@ var convertGradients = this.convertGradients = function(elem) {
});
}
// Function: convertToGroup
// Converts selected/given <use> or child SVG element to a group
var convertToGroup = this.convertToGroup = function(elem) {
@@ -8005,7 +7873,7 @@ var convertToGroup = this.convertToGroup = function(elem) {
this.setSvgString = function(xmlString) {
try {
// convert string into XML document
var newDoc = Utils.text2xml(xmlString);
var newDoc = svgedit.Utilities.text2xml(xmlString);
// run it through our sanitizer to remove anything we do not support
sanitizeSvg(newDoc.documentElement);
@@ -8188,7 +8056,7 @@ this.setSvgString = function(xmlString) {
this.importSvgString = function(xmlString) {
try {
// convert string into XML document
var newDoc = Utils.text2xml(xmlString);
var newDoc = svgedit.Utilities.text2xml(xmlString);
// run it through our sanitizer to remove anything we do not support
sanitizeSvg(newDoc.documentElement);
@@ -8462,7 +8330,7 @@ this.getCurrentLayer = function() {
// Returns:
// true if the current layer was switched, otherwise false
this.setCurrentLayer = function(name) {
name = Utils.toXml(name);
name = svgedit.Utilities.toXml(name);
for (var i = 0; i < all_layers.length; ++i) {
if (name == all_layers[i][0]) {
if (current_layer != all_layers[i][1]) {
@@ -8498,7 +8366,7 @@ this.renameCurrentLayer = function(newname) {
if (all_layers[i][1] == oldLayer) break;
}
var oldname = all_layers[i][0];
all_layers[i][0] = Utils.toXml(newname);
all_layers[i][0] = svgedit.Utilities.toXml(newname);
// now change the underlying title element contents
var len = oldLayer.childNodes.length;

View File

@@ -1,5 +1,5 @@
/**
* SVG-edit Utilities
* Package: svgedit.Utilities
*
* Licensed under the Apache License, Version 2
*
@@ -9,6 +9,7 @@
// Dependencies:
// 1) jQuery
// 2) browsersupport.js
(function() {
@@ -26,6 +27,11 @@ if (!svgedit.Utilities) {
var KEYSTR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var XLINKNS = "http://www.w3.org/1999/xlink";
// Much faster than running getBBox() every time
var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use';
var visElems_arr = visElems.split(',');
//var hidElems = 'clipPath,defs,desc,feGaussianBlur,filter,linearGradient,marker,mask,metadata,pattern,radialGradient,stop,switch,symbol,title,textPath';
// Function: svgedit.Utilities.toXml
// Converts characters in a string to XML-friendly entities.
//
@@ -325,4 +331,136 @@ svgedit.Utilities.findDefs = function(svgElement) {
return defs;
};
// Function: svgedit.Utilities.getPathBBox
// Get correct BBox for a path in Webkit
// Converted from code found here:
// http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
//
// Parameters:
// path - The path DOM element to get the BBox for
//
// Returns:
// A BBox-like object
svgedit.Utilities.getPathBBox = function(path) {
var seglist = path.pathSegList;
var tot = seglist.numberOfItems;
var bounds = [[], []];
var start = seglist.getItem(0);
var P0 = [start.x, start.y];
for(var i=0; i < tot; i++) {
var seg = seglist.getItem(i);
if(!seg.x) continue;
// Add actual points to limits
bounds[0].push(P0[0]);
bounds[1].push(P0[1]);
if(seg.x1) {
var P1 = [seg.x1, seg.y1],
P2 = [seg.x2, seg.y2],
P3 = [seg.x, seg.y];
for(var j=0; j < 2; j++) {
var calc = function(t) {
return Math.pow(1-t,3) * P0[j]
+ 3 * Math.pow(1-t,2) * t * P1[j]
+ 3 * (1-t) * Math.pow(t,2) * P2[j]
+ Math.pow(t,3) * P3[j];
};
var b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j];
var a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j];
var c = 3 * P1[j] - 3 * P0[j];
if(a == 0) {
if(b == 0) {
continue;
}
var t = -c / b;
if(0 < t && t < 1) {
bounds[j].push(calc(t));
}
continue;
}
var b2ac = Math.pow(b,2) - 4 * c * a;
if(b2ac < 0) continue;
var t1 = (-b + Math.sqrt(b2ac))/(2 * a);
if(0 < t1 && t1 < 1) bounds[j].push(calc(t1));
var t2 = (-b - Math.sqrt(b2ac))/(2 * a);
if(0 < t2 && t2 < 1) bounds[j].push(calc(t2));
}
P0 = P3;
} else {
bounds[0].push(seg.x);
bounds[1].push(seg.y);
}
}
var x = Math.min.apply(null, bounds[0]);
var w = Math.max.apply(null, bounds[0]) - x;
var y = Math.min.apply(null, bounds[1]);
var h = Math.max.apply(null, bounds[1]) - y;
return {
'x': x,
'y': y,
'width': w,
'height': h
};
};
// Function: svgedit.Utilities.getBBox
// Get the given/selected element's bounding box object, convert it to be more
// usable when necessary
//
// Parameters:
// elem - Optional DOM element to get the BBox for
svgedit.Utilities.getBBox = function(elem) {
var selected = elem || selectedElements[0];
if (elem.nodeType != 1) return null;
var ret = null;
var elname = selected.nodeName;
if(elname === 'text' && selected.textContent === '') {
selected.textContent = 'a'; // Some character needed for the selector to use.
ret = selected.getBBox();
selected.textContent = '';
} else if(elname === 'path' && svgedit.BrowserSupport.isWebkit) {
ret = svgedit.Utilities.getPathBBox(selected);
} else if(elname === 'use' && !svgedit.BrowserSupport.isWebkit || elname === 'foreignObject') {
ret = selected.getBBox();
var bb = {};
bb.width = ret.width;
bb.height = ret.height;
bb.x = ret.x + parseFloat(selected.getAttribute('x')||0);
bb.y = ret.y + parseFloat(selected.getAttribute('y')||0);
ret = bb;
} else if(~visElems_arr.indexOf(elname)) {
try { ret = selected.getBBox();}
catch(e) {
// Check if element is child of a foreignObject
var fo = $(selected).closest("foreignObject");
if(fo.length) {
try {
ret = fo[0].getBBox();
} catch(e) {
ret = null;
}
} else {
ret = null;
}
}
}
if(ret) {
ret = svgedit.Utilities.bboxToObj(ret);
}
// get the bounding box from the DOM (which is in that element's coordinate system)
return ret;
};
})();