1 | /**
|
---|
2 | * Package: svedit.math
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2
|
---|
5 | *
|
---|
6 | * Copyright(c) 2010 Alexis Deveria
|
---|
7 | * Copyright(c) 2010 Jeff Schiller
|
---|
8 | */
|
---|
9 |
|
---|
10 | // Dependencies:
|
---|
11 | // None.
|
---|
12 |
|
---|
13 | var svgedit = svgedit || {};
|
---|
14 |
|
---|
15 | (function() {
|
---|
16 |
|
---|
17 | if (!svgedit.math) {
|
---|
18 | svgedit.math = {};
|
---|
19 | }
|
---|
20 |
|
---|
21 | // Constants
|
---|
22 | var NEAR_ZERO = 1e-14;
|
---|
23 |
|
---|
24 | // Throw away SVGSVGElement used for creating matrices/transforms.
|
---|
25 | var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
---|
26 |
|
---|
27 | // Function: svgedit.math.transformPoint
|
---|
28 | // A (hopefully) quicker function to transform a point by a matrix
|
---|
29 | // (this function avoids any DOM calls and just does the math)
|
---|
30 | //
|
---|
31 | // Parameters:
|
---|
32 | // x - Float representing the x coordinate
|
---|
33 | // y - Float representing the y coordinate
|
---|
34 | // m - Matrix object to transform the point with
|
---|
35 | // Returns a x,y object representing the transformed point
|
---|
36 | svgedit.math.transformPoint = function(x, y, m) {
|
---|
37 | return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f};
|
---|
38 | };
|
---|
39 |
|
---|
40 |
|
---|
41 | // Function: svgedit.math.isIdentity
|
---|
42 | // Helper function to check if the matrix performs no actual transform
|
---|
43 | // (i.e. exists for identity purposes)
|
---|
44 | //
|
---|
45 | // Parameters:
|
---|
46 | // m - The matrix object to check
|
---|
47 | //
|
---|
48 | // Returns:
|
---|
49 | // Boolean indicating whether or not the matrix is 1,0,0,1,0,0
|
---|
50 | svgedit.math.isIdentity = function(m) {
|
---|
51 | return (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0);
|
---|
52 | };
|
---|
53 |
|
---|
54 |
|
---|
55 | // Function: svgedit.math.matrixMultiply
|
---|
56 | // This function tries to return a SVGMatrix that is the multiplication m1*m2.
|
---|
57 | // We also round to zero when it's near zero
|
---|
58 | //
|
---|
59 | // Parameters:
|
---|
60 | // >= 2 Matrix objects to multiply
|
---|
61 | //
|
---|
62 | // Returns:
|
---|
63 | // The matrix object resulting from the calculation
|
---|
64 | svgedit.math.matrixMultiply = function() {
|
---|
65 | var args = arguments, i = args.length, m = args[i-1];
|
---|
66 |
|
---|
67 | while(i-- > 1) {
|
---|
68 | var m1 = args[i-1];
|
---|
69 | m = m1.multiply(m);
|
---|
70 | }
|
---|
71 | if (Math.abs(m.a) < NEAR_ZERO) m.a = 0;
|
---|
72 | if (Math.abs(m.b) < NEAR_ZERO) m.b = 0;
|
---|
73 | if (Math.abs(m.c) < NEAR_ZERO) m.c = 0;
|
---|
74 | if (Math.abs(m.d) < NEAR_ZERO) m.d = 0;
|
---|
75 | if (Math.abs(m.e) < NEAR_ZERO) m.e = 0;
|
---|
76 | if (Math.abs(m.f) < NEAR_ZERO) m.f = 0;
|
---|
77 |
|
---|
78 | return m;
|
---|
79 | };
|
---|
80 |
|
---|
81 | // Function: svgedit.math.hasMatrixTransform
|
---|
82 | // See if the given transformlist includes a non-indentity matrix transform
|
---|
83 | //
|
---|
84 | // Parameters:
|
---|
85 | // tlist - The transformlist to check
|
---|
86 | //
|
---|
87 | // Returns:
|
---|
88 | // Boolean on whether or not a matrix transform was found
|
---|
89 | svgedit.math.hasMatrixTransform = function(tlist) {
|
---|
90 | if(!tlist) return false;
|
---|
91 | var num = tlist.numberOfItems;
|
---|
92 | while (num--) {
|
---|
93 | var xform = tlist.getItem(num);
|
---|
94 | if (xform.type == 1 && !svgedit.math.isIdentity(xform.matrix)) return true;
|
---|
95 | }
|
---|
96 | return false;
|
---|
97 | };
|
---|
98 |
|
---|
99 | // Function: svgedit.math.transformBox
|
---|
100 | // Transforms a rectangle based on the given matrix
|
---|
101 | //
|
---|
102 | // Parameters:
|
---|
103 | // l - Float with the box's left coordinate
|
---|
104 | // t - Float with the box's top coordinate
|
---|
105 | // w - Float with the box width
|
---|
106 | // h - Float with the box height
|
---|
107 | // m - Matrix object to transform the box by
|
---|
108 | //
|
---|
109 | // Returns:
|
---|
110 | // An object with the following values:
|
---|
111 | // * tl - The top left coordinate (x,y object)
|
---|
112 | // * tr - The top right coordinate (x,y object)
|
---|
113 | // * bl - The bottom left coordinate (x,y object)
|
---|
114 | // * br - The bottom right coordinate (x,y object)
|
---|
115 | // * aabox - Object with the following values:
|
---|
116 | // * Float with the axis-aligned x coordinate
|
---|
117 | // * Float with the axis-aligned y coordinate
|
---|
118 | // * Float with the axis-aligned width coordinate
|
---|
119 | // * Float with the axis-aligned height coordinate
|
---|
120 | svgedit.math.transformBox = function(l, t, w, h, m) {
|
---|
121 | var topleft = {x:l,y:t},
|
---|
122 | topright = {x:(l+w),y:t},
|
---|
123 | botright = {x:(l+w),y:(t+h)},
|
---|
124 | botleft = {x:l,y:(t+h)};
|
---|
125 | var transformPoint = svgedit.math.transformPoint;
|
---|
126 | topleft = transformPoint( topleft.x, topleft.y, m );
|
---|
127 | var minx = topleft.x,
|
---|
128 | maxx = topleft.x,
|
---|
129 | miny = topleft.y,
|
---|
130 | maxy = topleft.y;
|
---|
131 | topright = transformPoint( topright.x, topright.y, m );
|
---|
132 | minx = Math.min(minx, topright.x);
|
---|
133 | maxx = Math.max(maxx, topright.x);
|
---|
134 | miny = Math.min(miny, topright.y);
|
---|
135 | maxy = Math.max(maxy, topright.y);
|
---|
136 | botleft = transformPoint( botleft.x, botleft.y, m);
|
---|
137 | minx = Math.min(minx, botleft.x);
|
---|
138 | maxx = Math.max(maxx, botleft.x);
|
---|
139 | miny = Math.min(miny, botleft.y);
|
---|
140 | maxy = Math.max(maxy, botleft.y);
|
---|
141 | botright = transformPoint( botright.x, botright.y, m );
|
---|
142 | minx = Math.min(minx, botright.x);
|
---|
143 | maxx = Math.max(maxx, botright.x);
|
---|
144 | miny = Math.min(miny, botright.y);
|
---|
145 | maxy = Math.max(maxy, botright.y);
|
---|
146 |
|
---|
147 | return {tl:topleft, tr:topright, bl:botleft, br:botright,
|
---|
148 | aabox: {x:minx, y:miny, width:(maxx-minx), height:(maxy-miny)} };
|
---|
149 | };
|
---|
150 |
|
---|
151 | // Function: svgedit.math.transformListToTransform
|
---|
152 | // This returns a single matrix Transform for a given Transform List
|
---|
153 | // (this is the equivalent of SVGTransformList.consolidate() but unlike
|
---|
154 | // that method, this one does not modify the actual SVGTransformList)
|
---|
155 | // This function is very liberal with its min,max arguments
|
---|
156 | //
|
---|
157 | // Parameters:
|
---|
158 | // tlist - The transformlist object
|
---|
159 | // min - Optional integer indicating start transform position
|
---|
160 | // max - Optional integer indicating end transform position
|
---|
161 | //
|
---|
162 | // Returns:
|
---|
163 | // A single matrix transform object
|
---|
164 | svgedit.math.transformListToTransform = function(tlist, min, max) {
|
---|
165 | if(tlist == null) {
|
---|
166 | // Or should tlist = null have been prevented before this?
|
---|
167 | return svg.createSVGTransformFromMatrix(svg.createSVGMatrix());
|
---|
168 | }
|
---|
169 | var min = min == undefined ? 0 : min;
|
---|
170 | var max = max == undefined ? (tlist.numberOfItems-1) : max;
|
---|
171 | min = parseInt(min);
|
---|
172 | max = parseInt(max);
|
---|
173 | if (min > max) { var temp = max; max = min; min = temp; }
|
---|
174 | var m = svg.createSVGMatrix();
|
---|
175 | for (var i = min; i <= max; ++i) {
|
---|
176 | // if our indices are out of range, just use a harmless identity matrix
|
---|
177 | var mtom = (i >= 0 && i < tlist.numberOfItems ?
|
---|
178 | tlist.getItem(i).matrix :
|
---|
179 | svg.createSVGMatrix());
|
---|
180 | m = svgedit.math.matrixMultiply(m, mtom);
|
---|
181 | }
|
---|
182 | return svg.createSVGTransformFromMatrix(m);
|
---|
183 | };
|
---|
184 |
|
---|
185 |
|
---|
186 | // Function: svgedit.math.getMatrix
|
---|
187 | // Get the matrix object for a given element
|
---|
188 | //
|
---|
189 | // Parameters:
|
---|
190 | // elem - The DOM element to check
|
---|
191 | //
|
---|
192 | // Returns:
|
---|
193 | // The matrix object associated with the element's transformlist
|
---|
194 | svgedit.math.getMatrix = function(elem) {
|
---|
195 | var tlist = svgedit.transformlist.getTransformList(elem);
|
---|
196 | return svgedit.math.transformListToTransform(tlist).matrix;
|
---|
197 | };
|
---|
198 |
|
---|
199 |
|
---|
200 | // Function: svgedit.math.snapToAngle
|
---|
201 | // Returns a 45 degree angle coordinate associated with the two given
|
---|
202 | // coordinates
|
---|
203 | //
|
---|
204 | // Parameters:
|
---|
205 | // x1 - First coordinate's x value
|
---|
206 | // x2 - Second coordinate's x value
|
---|
207 | // y1 - First coordinate's y value
|
---|
208 | // y2 - Second coordinate's y value
|
---|
209 | //
|
---|
210 | // Returns:
|
---|
211 | // Object with the following values:
|
---|
212 | // x - The angle-snapped x value
|
---|
213 | // y - The angle-snapped y value
|
---|
214 | // snapangle - The angle at which to snap
|
---|
215 | svgedit.math.snapToAngle = function(x1,y1,x2,y2) {
|
---|
216 | var snap = Math.PI/4; // 45 degrees
|
---|
217 | var dx = x2 - x1;
|
---|
218 | var dy = y2 - y1;
|
---|
219 | var angle = Math.atan2(dy,dx);
|
---|
220 | var dist = Math.sqrt(dx * dx + dy * dy);
|
---|
221 | var snapangle= Math.round(angle/snap)*snap;
|
---|
222 | var x = x1 + dist*Math.cos(snapangle);
|
---|
223 | var y = y1 + dist*Math.sin(snapangle);
|
---|
224 | //console.log(x1,y1,x2,y2,x,y,angle)
|
---|
225 | return {x:x, y:y, a:snapangle};
|
---|
226 | };
|
---|
227 |
|
---|
228 |
|
---|
229 | // Function: rectsIntersect
|
---|
230 | // Check if two rectangles (BBoxes objects) intersect each other
|
---|
231 | //
|
---|
232 | // Paramaters:
|
---|
233 | // r1 - The first BBox-like object
|
---|
234 | // r2 - The second BBox-like object
|
---|
235 | //
|
---|
236 | // Returns:
|
---|
237 | // Boolean that's true if rectangles intersect
|
---|
238 | svgedit.math.rectsIntersect = function(r1, r2) {
|
---|
239 | if (!r1 || !r2) return false;
|
---|
240 | return r2.x < (r1.x+r1.width) &&
|
---|
241 | (r2.x+r2.width) > r1.x &&
|
---|
242 | r2.y < (r1.y+r1.height) &&
|
---|
243 | (r2.y+r2.height) > r1.y;
|
---|
244 | };
|
---|
245 |
|
---|
246 |
|
---|
247 | })(); |
---|