root / trunk / web / dojo / dojox / gfx.js.uncompressed.js @ 13
History | View | Annotate | Download (30.8 KB)
1 |
/*
|
---|---|
2 |
Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
|
3 |
Available via Academic Free License >= 2.1 OR the modified BSD license.
|
4 |
see: http://dojotoolkit.org/license for details
|
5 |
*/
|
6 |
|
7 |
/*
|
8 |
This is an optimized version of Dojo, built for deployment and not for
|
9 |
development. To get sources and documentation, please visit:
|
10 |
|
11 |
http://dojotoolkit.org
|
12 |
*/
|
13 |
|
14 |
if(!dojo._hasResource["dojox.gfx.matrix"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
15 |
dojo._hasResource["dojox.gfx.matrix"] = true; |
16 |
dojo.provide("dojox.gfx.matrix");
|
17 |
|
18 |
(function(){
|
19 |
var m = dojox.gfx.matrix;
|
20 |
|
21 |
// candidates for dojox.math:
|
22 |
var _degToRadCache = {};
|
23 |
m._degToRad = function(degree){ |
24 |
return _degToRadCache[degree] || (_degToRadCache[degree] = (Math.PI * degree / 180)); |
25 |
}; |
26 |
m._radToDeg = function(radian){ return radian / Math.PI * 180; }; |
27 |
|
28 |
m.Matrix2D = function(arg){ |
29 |
// summary: a 2D matrix object
|
30 |
// description: Normalizes a 2D matrix-like object. If arrays is passed,
|
31 |
// all objects of the array are normalized and multiplied sequentially.
|
32 |
// arg: Object
|
33 |
// a 2D matrix-like object, a number, or an array of such objects
|
34 |
if(arg){
|
35 |
if(typeof arg == "number"){ |
36 |
this.xx = this.yy = arg; |
37 |
}else if(arg instanceof Array){ |
38 |
if(arg.length > 0){ |
39 |
var matrix = m.normalize(arg[0]); |
40 |
// combine matrices
|
41 |
for(var i = 1; i < arg.length; ++i){ |
42 |
var l = matrix, r = dojox.gfx.matrix.normalize(arg[i]);
|
43 |
matrix = new m.Matrix2D();
|
44 |
matrix.xx = l.xx * r.xx + l.xy * r.yx; |
45 |
matrix.xy = l.xx * r.xy + l.xy * r.yy; |
46 |
matrix.yx = l.yx * r.xx + l.yy * r.yx; |
47 |
matrix.yy = l.yx * r.xy + l.yy * r.yy; |
48 |
matrix.dx = l.xx * r.dx + l.xy * r.dy + l.dx; |
49 |
matrix.dy = l.yx * r.dx + l.yy * r.dy + l.dy; |
50 |
} |
51 |
dojo.mixin(this, matrix);
|
52 |
} |
53 |
}else{
|
54 |
dojo.mixin(this, arg);
|
55 |
} |
56 |
} |
57 |
}; |
58 |
|
59 |
// the default (identity) matrix, which is used to fill in missing values
|
60 |
dojo.extend(m.Matrix2D, {xx: 1, xy: 0, yx: 0, yy: 1, dx: 0, dy: 0}); |
61 |
|
62 |
dojo.mixin(m, { |
63 |
// summary: class constants, and methods of dojox.gfx.matrix
|
64 |
|
65 |
// matrix constants
|
66 |
|
67 |
// identity: dojox.gfx.matrix.Matrix2D
|
68 |
// an identity matrix constant: identity * (x, y) == (x, y)
|
69 |
identity: new m.Matrix2D(), |
70 |
|
71 |
// flipX: dojox.gfx.matrix.Matrix2D
|
72 |
// a matrix, which reflects points at x = 0 line: flipX * (x, y) == (-x, y)
|
73 |
flipX: new m.Matrix2D({xx: -1}), |
74 |
|
75 |
// flipY: dojox.gfx.matrix.Matrix2D
|
76 |
// a matrix, which reflects points at y = 0 line: flipY * (x, y) == (x, -y)
|
77 |
flipY: new m.Matrix2D({yy: -1}), |
78 |
|
79 |
// flipXY: dojox.gfx.matrix.Matrix2D
|
80 |
// a matrix, which reflects points at the origin of coordinates: flipXY * (x, y) == (-x, -y)
|
81 |
flipXY: new m.Matrix2D({xx: -1, yy: -1}), |
82 |
|
83 |
// matrix creators
|
84 |
|
85 |
translate: function(a, b){ |
86 |
// summary: forms a translation matrix
|
87 |
// description: The resulting matrix is used to translate (move) points by specified offsets.
|
88 |
// a: Number: an x coordinate value
|
89 |
// b: Number: a y coordinate value
|
90 |
if(arguments.length > 1){ |
91 |
return new m.Matrix2D({dx: a, dy: b}); // dojox.gfx.matrix.Matrix2D |
92 |
} |
93 |
// branch
|
94 |
// a: dojox.gfx.Point: a point-like object, which specifies offsets for both dimensions
|
95 |
// b: null
|
96 |
return new m.Matrix2D({dx: a.x, dy: a.y}); // dojox.gfx.matrix.Matrix2D |
97 |
}, |
98 |
scale: function(a, b){ |
99 |
// summary: forms a scaling matrix
|
100 |
// description: The resulting matrix is used to scale (magnify) points by specified offsets.
|
101 |
// a: Number: a scaling factor used for the x coordinate
|
102 |
// b: Number: a scaling factor used for the y coordinate
|
103 |
if(arguments.length > 1){ |
104 |
return new m.Matrix2D({xx: a, yy: b}); // dojox.gfx.matrix.Matrix2D |
105 |
} |
106 |
if(typeof a == "number"){ |
107 |
// branch
|
108 |
// a: Number: a uniform scaling factor used for the both coordinates
|
109 |
// b: null
|
110 |
return new m.Matrix2D({xx: a, yy: a}); // dojox.gfx.matrix.Matrix2D |
111 |
} |
112 |
// branch
|
113 |
// a: dojox.gfx.Point: a point-like object, which specifies scale factors for both dimensions
|
114 |
// b: null
|
115 |
return new m.Matrix2D({xx: a.x, yy: a.y}); // dojox.gfx.matrix.Matrix2D |
116 |
}, |
117 |
rotate: function(angle){ |
118 |
// summary: forms a rotating matrix
|
119 |
// description: The resulting matrix is used to rotate points
|
120 |
// around the origin of coordinates (0, 0) by specified angle.
|
121 |
// angle: Number: an angle of rotation in radians (>0 for CW)
|
122 |
var c = Math.cos(angle);
|
123 |
var s = Math.sin(angle);
|
124 |
return new m.Matrix2D({xx: c, xy: -s, yx: s, yy: c}); // dojox.gfx.matrix.Matrix2D |
125 |
}, |
126 |
rotateg: function(degree){ |
127 |
// summary: forms a rotating matrix
|
128 |
// description: The resulting matrix is used to rotate points
|
129 |
// around the origin of coordinates (0, 0) by specified degree.
|
130 |
// See dojox.gfx.matrix.rotate() for comparison.
|
131 |
// degree: Number: an angle of rotation in degrees (>0 for CW)
|
132 |
return m.rotate(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D |
133 |
}, |
134 |
skewX: function(angle) { |
135 |
// summary: forms an x skewing matrix
|
136 |
// description: The resulting matrix is used to skew points in the x dimension
|
137 |
// around the origin of coordinates (0, 0) by specified angle.
|
138 |
// angle: Number: an skewing angle in radians
|
139 |
return new m.Matrix2D({xy: Math.tan(angle)}); // dojox.gfx.matrix.Matrix2D |
140 |
}, |
141 |
skewXg: function(degree){ |
142 |
// summary: forms an x skewing matrix
|
143 |
// description: The resulting matrix is used to skew points in the x dimension
|
144 |
// around the origin of coordinates (0, 0) by specified degree.
|
145 |
// See dojox.gfx.matrix.skewX() for comparison.
|
146 |
// degree: Number: an skewing angle in degrees
|
147 |
return m.skewX(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D |
148 |
}, |
149 |
skewY: function(angle){ |
150 |
// summary: forms a y skewing matrix
|
151 |
// description: The resulting matrix is used to skew points in the y dimension
|
152 |
// around the origin of coordinates (0, 0) by specified angle.
|
153 |
// angle: Number: an skewing angle in radians
|
154 |
return new m.Matrix2D({yx: Math.tan(angle)}); // dojox.gfx.matrix.Matrix2D |
155 |
}, |
156 |
skewYg: function(degree){ |
157 |
// summary: forms a y skewing matrix
|
158 |
// description: The resulting matrix is used to skew points in the y dimension
|
159 |
// around the origin of coordinates (0, 0) by specified degree.
|
160 |
// See dojox.gfx.matrix.skewY() for comparison.
|
161 |
// degree: Number: an skewing angle in degrees
|
162 |
return m.skewY(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D |
163 |
}, |
164 |
reflect: function(a, b){ |
165 |
// summary: forms a reflection matrix
|
166 |
// description: The resulting matrix is used to reflect points around a vector,
|
167 |
// which goes through the origin.
|
168 |
// a: dojox.gfx.Point: a point-like object, which specifies a vector of reflection
|
169 |
// b: null
|
170 |
if(arguments.length == 1){ |
171 |
b = a.y; |
172 |
a = a.x; |
173 |
} |
174 |
// branch
|
175 |
// a: Number: an x coordinate value
|
176 |
// b: Number: a y coordinate value
|
177 |
|
178 |
// make a unit vector
|
179 |
var a2 = a * a, b2 = b * b, n2 = a2 + b2, xy = 2 * a * b / n2; |
180 |
return new m.Matrix2D({xx: 2 * a2 / n2 - 1, xy: xy, yx: xy, yy: 2 * b2 / n2 - 1}); // dojox.gfx.matrix.Matrix2D |
181 |
}, |
182 |
project: function(a, b){ |
183 |
// summary: forms an orthogonal projection matrix
|
184 |
// description: The resulting matrix is used to project points orthogonally on a vector,
|
185 |
// which goes through the origin.
|
186 |
// a: dojox.gfx.Point: a point-like object, which specifies a vector of projection
|
187 |
// b: null
|
188 |
if(arguments.length == 1){ |
189 |
b = a.y; |
190 |
a = a.x; |
191 |
} |
192 |
// branch
|
193 |
// a: Number: an x coordinate value
|
194 |
// b: Number: a y coordinate value
|
195 |
|
196 |
// make a unit vector
|
197 |
var a2 = a * a, b2 = b * b, n2 = a2 + b2, xy = a * b / n2;
|
198 |
return new m.Matrix2D({xx: a2 / n2, xy: xy, yx: xy, yy: b2 / n2}); // dojox.gfx.matrix.Matrix2D |
199 |
}, |
200 |
|
201 |
// ensure matrix 2D conformance
|
202 |
normalize: function(matrix){ |
203 |
// summary: converts an object to a matrix, if necessary
|
204 |
// description: Converts any 2D matrix-like object or an array of
|
205 |
// such objects to a valid dojox.gfx.matrix.Matrix2D object.
|
206 |
// matrix: Object: an object, which is converted to a matrix, if necessary
|
207 |
return (matrix instanceof m.Matrix2D) ? matrix : new m.Matrix2D(matrix); // dojox.gfx.matrix.Matrix2D |
208 |
}, |
209 |
|
210 |
// common operations
|
211 |
|
212 |
clone: function(matrix){ |
213 |
// summary: creates a copy of a 2D matrix
|
214 |
// matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object to be cloned
|
215 |
var obj = new m.Matrix2D(); |
216 |
for(var i in matrix){ |
217 |
if(typeof(matrix[i]) == "number" && typeof(obj[i]) == "number" && obj[i] != matrix[i]) obj[i] = matrix[i]; |
218 |
} |
219 |
return obj; // dojox.gfx.matrix.Matrix2D |
220 |
}, |
221 |
invert: function(matrix){ |
222 |
// summary: inverts a 2D matrix
|
223 |
// matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object to be inverted
|
224 |
var M = m.normalize(matrix),
|
225 |
D = M.xx * M.yy - M.xy * M.yx, |
226 |
M = new m.Matrix2D({
|
227 |
xx: M.yy/D, xy: -M.xy/D, |
228 |
yx: -M.yx/D, yy: M.xx/D, |
229 |
dx: (M.xy * M.dy - M.yy * M.dx) / D,
|
230 |
dy: (M.yx * M.dx - M.xx * M.dy) / D
|
231 |
}); |
232 |
return M; // dojox.gfx.matrix.Matrix2D |
233 |
}, |
234 |
_multiplyPoint: function(matrix, x, y){ |
235 |
// summary: applies a matrix to a point
|
236 |
// matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied
|
237 |
// x: Number: an x coordinate of a point
|
238 |
// y: Number: a y coordinate of a point
|
239 |
return {x: matrix.xx * x + matrix.xy * y + matrix.dx, y: matrix.yx * x + matrix.yy * y + matrix.dy}; // dojox.gfx.Point |
240 |
}, |
241 |
multiplyPoint: function(matrix, /* Number||Point */ a, /* Number, optional */ b){ |
242 |
// summary: applies a matrix to a point
|
243 |
// matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied
|
244 |
// a: Number: an x coordinate of a point
|
245 |
// b: Number: a y coordinate of a point
|
246 |
var M = m.normalize(matrix);
|
247 |
if(typeof a == "number" && typeof b == "number"){ |
248 |
return m._multiplyPoint(M, a, b); // dojox.gfx.Point |
249 |
} |
250 |
// branch
|
251 |
// matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied
|
252 |
// a: dojox.gfx.Point: a point
|
253 |
// b: null
|
254 |
return m._multiplyPoint(M, a.x, a.y); // dojox.gfx.Point |
255 |
}, |
256 |
multiply: function(matrix){ |
257 |
// summary: combines matrices by multiplying them sequentially in the given order
|
258 |
// matrix: dojox.gfx.matrix.Matrix2D...: a 2D matrix-like object,
|
259 |
// all subsequent arguments are matrix-like objects too
|
260 |
var M = m.normalize(matrix);
|
261 |
// combine matrices
|
262 |
for(var i = 1; i < arguments.length; ++i){ |
263 |
var l = M, r = m.normalize(arguments[i]); |
264 |
M = new m.Matrix2D();
|
265 |
M.xx = l.xx * r.xx + l.xy * r.yx; |
266 |
M.xy = l.xx * r.xy + l.xy * r.yy; |
267 |
M.yx = l.yx * r.xx + l.yy * r.yx; |
268 |
M.yy = l.yx * r.xy + l.yy * r.yy; |
269 |
M.dx = l.xx * r.dx + l.xy * r.dy + l.dx; |
270 |
M.dy = l.yx * r.dx + l.yy * r.dy + l.dy; |
271 |
} |
272 |
return M; // dojox.gfx.matrix.Matrix2D |
273 |
}, |
274 |
|
275 |
// high level operations
|
276 |
|
277 |
_sandwich: function(matrix, x, y){ |
278 |
// summary: applies a matrix at a centrtal point
|
279 |
// matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object, which is applied at a central point
|
280 |
// x: Number: an x component of the central point
|
281 |
// y: Number: a y component of the central point
|
282 |
return m.multiply(m.translate(x, y), matrix, m.translate(-x, -y)); // dojox.gfx.matrix.Matrix2D |
283 |
}, |
284 |
scaleAt: function(a, b, c, d){ |
285 |
// summary: scales a picture using a specified point as a center of scaling
|
286 |
// description: Compare with dojox.gfx.matrix.scale().
|
287 |
// a: Number: a scaling factor used for the x coordinate
|
288 |
// b: Number: a scaling factor used for the y coordinate
|
289 |
// c: Number: an x component of a central point
|
290 |
// d: Number: a y component of a central point
|
291 |
|
292 |
// accepts several signatures:
|
293 |
// 1) uniform scale factor, Point
|
294 |
// 2) uniform scale factor, x, y
|
295 |
// 3) x scale, y scale, Point
|
296 |
// 4) x scale, y scale, x, y
|
297 |
|
298 |
switch(arguments.length){ |
299 |
case 4: |
300 |
// a and b are scale factor components, c and d are components of a point
|
301 |
return m._sandwich(m.scale(a, b), c, d); // dojox.gfx.matrix.Matrix2D |
302 |
case 3: |
303 |
if(typeof c == "number"){ |
304 |
// branch
|
305 |
// a: Number: a uniform scaling factor used for both coordinates
|
306 |
// b: Number: an x component of a central point
|
307 |
// c: Number: a y component of a central point
|
308 |
// d: null
|
309 |
return m._sandwich(m.scale(a), b, c); // dojox.gfx.matrix.Matrix2D |
310 |
} |
311 |
// branch
|
312 |
// a: Number: a scaling factor used for the x coordinate
|
313 |
// b: Number: a scaling factor used for the y coordinate
|
314 |
// c: dojox.gfx.Point: a central point
|
315 |
// d: null
|
316 |
return m._sandwich(m.scale(a, b), c.x, c.y); // dojox.gfx.matrix.Matrix2D |
317 |
} |
318 |
// branch
|
319 |
// a: Number: a uniform scaling factor used for both coordinates
|
320 |
// b: dojox.gfx.Point: a central point
|
321 |
// c: null
|
322 |
// d: null
|
323 |
return m._sandwich(m.scale(a), b.x, b.y); // dojox.gfx.matrix.Matrix2D |
324 |
}, |
325 |
rotateAt: function(angle, a, b){ |
326 |
// summary: rotates a picture using a specified point as a center of rotation
|
327 |
// description: Compare with dojox.gfx.matrix.rotate().
|
328 |
// angle: Number: an angle of rotation in radians (>0 for CW)
|
329 |
// a: Number: an x component of a central point
|
330 |
// b: Number: a y component of a central point
|
331 |
|
332 |
// accepts several signatures:
|
333 |
// 1) rotation angle in radians, Point
|
334 |
// 2) rotation angle in radians, x, y
|
335 |
|
336 |
if(arguments.length > 2){ |
337 |
return m._sandwich(m.rotate(angle), a, b); // dojox.gfx.matrix.Matrix2D |
338 |
} |
339 |
|
340 |
// branch
|
341 |
// angle: Number: an angle of rotation in radians (>0 for CCW)
|
342 |
// a: dojox.gfx.Point: a central point
|
343 |
// b: null
|
344 |
return m._sandwich(m.rotate(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D |
345 |
}, |
346 |
rotategAt: function(degree, a, b){ |
347 |
// summary: rotates a picture using a specified point as a center of rotation
|
348 |
// description: Compare with dojox.gfx.matrix.rotateg().
|
349 |
// degree: Number: an angle of rotation in degrees (>0 for CW)
|
350 |
// a: Number: an x component of a central point
|
351 |
// b: Number: a y component of a central point
|
352 |
|
353 |
// accepts several signatures:
|
354 |
// 1) rotation angle in degrees, Point
|
355 |
// 2) rotation angle in degrees, x, y
|
356 |
|
357 |
if(arguments.length > 2){ |
358 |
return m._sandwich(m.rotateg(degree), a, b); // dojox.gfx.matrix.Matrix2D |
359 |
} |
360 |
|
361 |
// branch
|
362 |
// degree: Number: an angle of rotation in degrees (>0 for CCW)
|
363 |
// a: dojox.gfx.Point: a central point
|
364 |
// b: null
|
365 |
return m._sandwich(m.rotateg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D |
366 |
}, |
367 |
skewXAt: function(angle, a, b){ |
368 |
// summary: skews a picture along the x axis using a specified point as a center of skewing
|
369 |
// description: Compare with dojox.gfx.matrix.skewX().
|
370 |
// angle: Number: an skewing angle in radians
|
371 |
// a: Number: an x component of a central point
|
372 |
// b: Number: a y component of a central point
|
373 |
|
374 |
// accepts several signatures:
|
375 |
// 1) skew angle in radians, Point
|
376 |
// 2) skew angle in radians, x, y
|
377 |
|
378 |
if(arguments.length > 2){ |
379 |
return m._sandwich(m.skewX(angle), a, b); // dojox.gfx.matrix.Matrix2D |
380 |
} |
381 |
|
382 |
// branch
|
383 |
// angle: Number: an skewing angle in radians
|
384 |
// a: dojox.gfx.Point: a central point
|
385 |
// b: null
|
386 |
return m._sandwich(m.skewX(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D |
387 |
}, |
388 |
skewXgAt: function(degree, a, b){ |
389 |
// summary: skews a picture along the x axis using a specified point as a center of skewing
|
390 |
// description: Compare with dojox.gfx.matrix.skewXg().
|
391 |
// degree: Number: an skewing angle in degrees
|
392 |
// a: Number: an x component of a central point
|
393 |
// b: Number: a y component of a central point
|
394 |
|
395 |
// accepts several signatures:
|
396 |
// 1) skew angle in degrees, Point
|
397 |
// 2) skew angle in degrees, x, y
|
398 |
|
399 |
if(arguments.length > 2){ |
400 |
return m._sandwich(m.skewXg(degree), a, b); // dojox.gfx.matrix.Matrix2D |
401 |
} |
402 |
|
403 |
// branch
|
404 |
// degree: Number: an skewing angle in degrees
|
405 |
// a: dojox.gfx.Point: a central point
|
406 |
// b: null
|
407 |
return m._sandwich(m.skewXg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D |
408 |
}, |
409 |
skewYAt: function(angle, a, b){ |
410 |
// summary: skews a picture along the y axis using a specified point as a center of skewing
|
411 |
// description: Compare with dojox.gfx.matrix.skewY().
|
412 |
// angle: Number: an skewing angle in radians
|
413 |
// a: Number: an x component of a central point
|
414 |
// b: Number: a y component of a central point
|
415 |
|
416 |
// accepts several signatures:
|
417 |
// 1) skew angle in radians, Point
|
418 |
// 2) skew angle in radians, x, y
|
419 |
|
420 |
if(arguments.length > 2){ |
421 |
return m._sandwich(m.skewY(angle), a, b); // dojox.gfx.matrix.Matrix2D |
422 |
} |
423 |
|
424 |
// branch
|
425 |
// angle: Number: an skewing angle in radians
|
426 |
// a: dojox.gfx.Point: a central point
|
427 |
// b: null
|
428 |
return m._sandwich(m.skewY(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D |
429 |
}, |
430 |
skewYgAt: function(/* Number */ degree, /* Number||Point */ a, /* Number, optional */ b){ |
431 |
// summary: skews a picture along the y axis using a specified point as a center of skewing
|
432 |
// description: Compare with dojox.gfx.matrix.skewYg().
|
433 |
// degree: Number: an skewing angle in degrees
|
434 |
// a: Number: an x component of a central point
|
435 |
// b: Number: a y component of a central point
|
436 |
|
437 |
// accepts several signatures:
|
438 |
// 1) skew angle in degrees, Point
|
439 |
// 2) skew angle in degrees, x, y
|
440 |
|
441 |
if(arguments.length > 2){ |
442 |
return m._sandwich(m.skewYg(degree), a, b); // dojox.gfx.matrix.Matrix2D |
443 |
} |
444 |
|
445 |
// branch
|
446 |
// degree: Number: an skewing angle in degrees
|
447 |
// a: dojox.gfx.Point: a central point
|
448 |
// b: null
|
449 |
return m._sandwich(m.skewYg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D |
450 |
} |
451 |
|
452 |
//TODO: rect-to-rect mapping, scale-to-fit (isotropic and anisotropic versions)
|
453 |
|
454 |
}); |
455 |
})(); |
456 |
|
457 |
// propagate Matrix2D up
|
458 |
dojox.gfx.Matrix2D = dojox.gfx.matrix.Matrix2D; |
459 |
|
460 |
} |
461 |
|
462 |
if(!dojo._hasResource["dojox.gfx._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
463 |
dojo._hasResource["dojox.gfx._base"] = true; |
464 |
dojo.provide("dojox.gfx._base");
|
465 |
|
466 |
(function(){
|
467 |
var g = dojox.gfx, b = g._base;
|
468 |
|
469 |
// candidates for dojox.style (work on VML and SVG nodes)
|
470 |
g._hasClass = function(/*DomNode*/node, /*String*/classStr){ |
471 |
// summary:
|
472 |
// Returns whether or not the specified classes are a portion of the
|
473 |
// class list currently applied to the node.
|
474 |
// return (new RegExp('(^|\\s+)'+classStr+'(\\s+|$)')).test(node.className) // Boolean
|
475 |
var cls = node.getAttribute("className"); |
476 |
return cls && (" " + cls + " ").indexOf(" " + classStr + " ") >= 0; // Boolean |
477 |
} |
478 |
g._addClass = function(/*DomNode*/node, /*String*/classStr){ |
479 |
// summary:
|
480 |
// Adds the specified classes to the end of the class list on the
|
481 |
// passed node.
|
482 |
var cls = node.getAttribute("className") || ""; |
483 |
if(!cls || (" " + cls + " ").indexOf(" " + classStr + " ") < 0){ |
484 |
node.setAttribute("className", cls + (cls ? " " : "") + classStr); |
485 |
} |
486 |
} |
487 |
g._removeClass = function(/*DomNode*/node, /*String*/classStr){ |
488 |
// summary: Removes classes from node.
|
489 |
var cls = node.getAttribute("className"); |
490 |
if(cls){
|
491 |
node.setAttribute( |
492 |
"className",
|
493 |
cls.replace(new RegExp('(^|\\s+)' + classStr + '(\\s+|$)'), "$1$2") |
494 |
); |
495 |
} |
496 |
} |
497 |
|
498 |
// candidate for dojox.html.metrics (dynamic font resize handler is not implemented here)
|
499 |
|
500 |
// derived from Morris John's emResized measurer
|
501 |
b._getFontMeasurements = function(){ |
502 |
// summary:
|
503 |
// Returns an object that has pixel equivilents of standard font
|
504 |
// size values.
|
505 |
var heights = {
|
506 |
'1em': 0, '1ex': 0, '100%': 0, '12pt': 0, '16px': 0, 'xx-small': 0, |
507 |
'x-small': 0, 'small': 0, 'medium': 0, 'large': 0, 'x-large': 0, |
508 |
'xx-large': 0 |
509 |
}; |
510 |
|
511 |
if(dojo.isIE){
|
512 |
// we do a font-size fix if and only if one isn't applied already.
|
513 |
// NOTE: If someone set the fontSize on the HTML Element, this will kill it.
|
514 |
dojo.doc.documentElement.style.fontSize="100%";
|
515 |
} |
516 |
|
517 |
// set up the measuring node.
|
518 |
var div = dojo.doc.createElement("div"); |
519 |
var s = div.style;
|
520 |
s.position = "absolute";
|
521 |
s.left = "-100px";
|
522 |
s.top = "0px";
|
523 |
s.width = "30px";
|
524 |
s.height = "1000em";
|
525 |
s.borderWidth = "0px";
|
526 |
s.margin = "0px";
|
527 |
s.padding = "0px";
|
528 |
s.outline = "none";
|
529 |
s.lineHeight = "1";
|
530 |
s.overflow = "hidden";
|
531 |
dojo.body().appendChild(div); |
532 |
|
533 |
// do the measurements.
|
534 |
for(var p in heights){ |
535 |
div.style.fontSize = p; |
536 |
heights[p] = Math.round(div.offsetHeight * 12/16) * 16/12 / 1000; |
537 |
} |
538 |
|
539 |
dojo.body().removeChild(div); |
540 |
div = null;
|
541 |
return heights; // object |
542 |
}; |
543 |
|
544 |
var fontMeasurements = null; |
545 |
|
546 |
b._getCachedFontMeasurements = function(recalculate){ |
547 |
if(recalculate || !fontMeasurements){
|
548 |
fontMeasurements = b._getFontMeasurements(); |
549 |
} |
550 |
return fontMeasurements;
|
551 |
}; |
552 |
|
553 |
// candidate for dojox.html.metrics
|
554 |
|
555 |
var measuringNode = null, empty = {}; |
556 |
b._getTextBox = function( /*String*/ text, |
557 |
/*Object*/ style,
|
558 |
/*String?*/ className){
|
559 |
var m, s, al = arguments.length; |
560 |
if(!measuringNode){
|
561 |
m = measuringNode = dojo.doc.createElement("div");
|
562 |
s = m.style; |
563 |
s.position = "absolute";
|
564 |
s.left = "-10000px";
|
565 |
s.top = "0";
|
566 |
dojo.body().appendChild(m); |
567 |
}else{
|
568 |
m = measuringNode; |
569 |
s = m.style; |
570 |
} |
571 |
// reset styles
|
572 |
m.className = "";
|
573 |
s.borderWidth = "0";
|
574 |
s.margin = "0";
|
575 |
s.padding = "0";
|
576 |
s.outline = "0";
|
577 |
// set new style
|
578 |
if(al > 1 && style){ |
579 |
for(var i in style){ |
580 |
if(i in empty){ continue; } |
581 |
s[i] = style[i]; |
582 |
} |
583 |
} |
584 |
// set classes
|
585 |
if(al > 2 && className){ |
586 |
m.className = className; |
587 |
} |
588 |
// take a measure
|
589 |
m.innerHTML = text; |
590 |
|
591 |
if(m["getBoundingClientRect"]){ |
592 |
var bcr = m.getBoundingClientRect();
|
593 |
return {l: bcr.left, t: bcr.top, w: bcr.width || (bcr.right - bcr.left), h: bcr.height || (bcr.bottom - bcr.top)}; |
594 |
}else{
|
595 |
return dojo.marginBox(m);
|
596 |
} |
597 |
}; |
598 |
|
599 |
// candidate for dojo.dom
|
600 |
|
601 |
var uniqueId = 0; |
602 |
b._getUniqueId = function(){ |
603 |
// summary: returns a unique string for use with any DOM element
|
604 |
var id;
|
605 |
do{
|
606 |
id = dojo._scopeName + "Unique" + (++uniqueId);
|
607 |
}while(dojo.byId(id));
|
608 |
return id;
|
609 |
}; |
610 |
})(); |
611 |
|
612 |
dojo.mixin(dojox.gfx, { |
613 |
// summary:
|
614 |
// defines constants, prototypes, and utility functions
|
615 |
|
616 |
// default shapes, which are used to fill in missing parameters
|
617 |
defaultPath: {
|
618 |
type: "path", path: "" |
619 |
}, |
620 |
defaultPolyline: {
|
621 |
type: "polyline", points: [] |
622 |
}, |
623 |
defaultRect: {
|
624 |
type: "rect", x: 0, y: 0, width: 100, height: 100, r: 0 |
625 |
}, |
626 |
defaultEllipse: {
|
627 |
type: "ellipse", cx: 0, cy: 0, rx: 200, ry: 100 |
628 |
}, |
629 |
defaultCircle: {
|
630 |
type: "circle", cx: 0, cy: 0, r: 100 |
631 |
}, |
632 |
defaultLine: {
|
633 |
type: "line", x1: 0, y1: 0, x2: 100, y2: 100 |
634 |
}, |
635 |
defaultImage: {
|
636 |
type: "image", x: 0, y: 0, width: 0, height: 0, src: "" |
637 |
}, |
638 |
defaultText: {
|
639 |
type: "text", x: 0, y: 0, text: "", align: "start", |
640 |
decoration: "none", rotated: false, kerning: true |
641 |
}, |
642 |
defaultTextPath: {
|
643 |
type: "textpath", text: "", align: "start", |
644 |
decoration: "none", rotated: false, kerning: true |
645 |
}, |
646 |
|
647 |
// default geometric attributes
|
648 |
defaultStroke: {
|
649 |
type: "stroke", color: "black", style: "solid", width: 1, |
650 |
cap: "butt", join: 4 |
651 |
}, |
652 |
defaultLinearGradient: {
|
653 |
type: "linear", x1: 0, y1: 0, x2: 100, y2: 100, |
654 |
colors: [
|
655 |
{ offset: 0, color: "black" }, { offset: 1, color: "white" } |
656 |
] |
657 |
}, |
658 |
defaultRadialGradient: {
|
659 |
type: "radial", cx: 0, cy: 0, r: 100, |
660 |
colors: [
|
661 |
{ offset: 0, color: "black" }, { offset: 1, color: "white" } |
662 |
] |
663 |
}, |
664 |
defaultPattern: {
|
665 |
type: "pattern", x: 0, y: 0, width: 0, height: 0, src: "" |
666 |
}, |
667 |
defaultFont: {
|
668 |
type: "font", style: "normal", variant: "normal", |
669 |
weight: "normal", size: "10pt", family: "serif" |
670 |
}, |
671 |
|
672 |
getDefault: (function(){ |
673 |
var typeCtorCache = {};
|
674 |
// a memoized delegate()
|
675 |
return function(/*String*/ type){ |
676 |
var t = typeCtorCache[type];
|
677 |
if(t){
|
678 |
return new t(); |
679 |
} |
680 |
t = typeCtorCache[type] = new Function;
|
681 |
t.prototype = dojox.gfx[ "default" + type ];
|
682 |
return new t(); |
683 |
} |
684 |
})(), |
685 |
|
686 |
normalizeColor: function(/*Color*/ color){ |
687 |
// summary:
|
688 |
// converts any legal color representation to normalized
|
689 |
// dojo.Color object
|
690 |
return (color instanceof dojo.Color) ? color : new dojo.Color(color); // dojo.Color |
691 |
}, |
692 |
normalizeParameters: function(existed, update){ |
693 |
// summary:
|
694 |
// updates an existing object with properties from an "update"
|
695 |
// object
|
696 |
// existed: Object
|
697 |
// the "target" object to be updated
|
698 |
// update: Object
|
699 |
// the "update" object, whose properties will be used to update
|
700 |
// the existed object
|
701 |
if(update){
|
702 |
var empty = {};
|
703 |
for(var x in existed){ |
704 |
if(x in update && !(x in empty)){ |
705 |
existed[x] = update[x]; |
706 |
} |
707 |
} |
708 |
} |
709 |
return existed; // Object |
710 |
}, |
711 |
makeParameters: function(defaults, update){ |
712 |
// summary:
|
713 |
// copies the original object, and all copied properties from the
|
714 |
// "update" object
|
715 |
// defaults: Object
|
716 |
// the object to be cloned before updating
|
717 |
// update: Object
|
718 |
// the object, which properties are to be cloned during updating
|
719 |
if(!update){
|
720 |
// return dojo.clone(defaults);
|
721 |
return dojo.delegate(defaults);
|
722 |
} |
723 |
var result = {};
|
724 |
for(var i in defaults){ |
725 |
if(!(i in result)){ |
726 |
result[i] = dojo.clone((i in update) ? update[i] : defaults[i]);
|
727 |
} |
728 |
} |
729 |
return result; // Object |
730 |
}, |
731 |
formatNumber: function(x, addSpace){ |
732 |
// summary: converts a number to a string using a fixed notation
|
733 |
// x: Number: number to be converted
|
734 |
// addSpace: Boolean?: if it is true, add a space before a positive number
|
735 |
var val = x.toString();
|
736 |
if(val.indexOf("e") >= 0){ |
737 |
val = x.toFixed(4);
|
738 |
}else{
|
739 |
var point = val.indexOf("."); |
740 |
if(point >= 0 && val.length - point > 5){ |
741 |
val = x.toFixed(4);
|
742 |
} |
743 |
} |
744 |
if(x < 0){ |
745 |
return val; // String |
746 |
} |
747 |
return addSpace ? " " + val : val; // String |
748 |
}, |
749 |
// font operations
|
750 |
makeFontString: function(font){ |
751 |
// summary: converts a font object to a CSS font string
|
752 |
// font: Object: font object (see dojox.gfx.defaultFont)
|
753 |
return font.style + " " + font.variant + " " + font.weight + " " + font.size + " " + font.family; // Object |
754 |
}, |
755 |
splitFontString: function(str){ |
756 |
// summary:
|
757 |
// converts a CSS font string to a font object
|
758 |
// description:
|
759 |
// Converts a CSS font string to a gfx font object. The CSS font
|
760 |
// string components should follow the W3C specified order
|
761 |
// (see http://www.w3.org/TR/CSS2/fonts.html#font-shorthand):
|
762 |
// style, variant, weight, size, optional line height (will be
|
763 |
// ignored), and family.
|
764 |
// str: String
|
765 |
// a CSS font string
|
766 |
var font = dojox.gfx.getDefault("Font"); |
767 |
var t = str.split(/\s+/); |
768 |
do{
|
769 |
if(t.length < 5){ break; } |
770 |
font.style = t[0];
|
771 |
font.variant = t[1];
|
772 |
font.weight = t[2];
|
773 |
var i = t[3].indexOf("/"); |
774 |
font.size = i < 0 ? t[3] : t[3].substring(0, i); |
775 |
var j = 4; |
776 |
if(i < 0){ |
777 |
if(t[4] == "/"){ |
778 |
j = 6;
|
779 |
}else if(t[4].charAt(0) == "/"){ |
780 |
j = 5;
|
781 |
} |
782 |
} |
783 |
if(j < t.length){
|
784 |
font.family = t.slice(j).join(" ");
|
785 |
} |
786 |
}while(false); |
787 |
return font; // Object |
788 |
}, |
789 |
// length operations
|
790 |
cm_in_pt: 72 / 2.54, // Number: points per centimeter |
791 |
mm_in_pt: 7.2 / 2.54, // Number: points per millimeter |
792 |
px_in_pt: function(){ |
793 |
// summary: returns a number of pixels per point
|
794 |
return dojox.gfx._base._getCachedFontMeasurements()["12pt"] / 12; // Number |
795 |
}, |
796 |
pt2px: function(len){ |
797 |
// summary: converts points to pixels
|
798 |
// len: Number: a value in points
|
799 |
return len * dojox.gfx.px_in_pt(); // Number |
800 |
}, |
801 |
px2pt: function(len){ |
802 |
// summary: converts pixels to points
|
803 |
// len: Number: a value in pixels
|
804 |
return len / dojox.gfx.px_in_pt(); // Number |
805 |
}, |
806 |
normalizedLength: function(len) { |
807 |
// summary: converts any length value to pixels
|
808 |
// len: String: a length, e.g., "12pc"
|
809 |
if(len.length == 0) return 0; |
810 |
if(len.length > 2){ |
811 |
var px_in_pt = dojox.gfx.px_in_pt();
|
812 |
var val = parseFloat(len);
|
813 |
switch(len.slice(-2)){ |
814 |
case "px": return val; |
815 |
case "pt": return val * px_in_pt; |
816 |
case "in": return val * 72 * px_in_pt; |
817 |
case "pc": return val * 12 * px_in_pt; |
818 |
case "mm": return val * dojox.gfx.mm_in_pt * px_in_pt; |
819 |
case "cm": return val * dojox.gfx.cm_in_pt * px_in_pt; |
820 |
} |
821 |
} |
822 |
return parseFloat(len); // Number |
823 |
}, |
824 |
|
825 |
// a constant used to split a SVG/VML path into primitive components
|
826 |
pathVmlRegExp: /([A-Za-z]+)|(\d+(\.\d+)?)|(\.\d+)|(-\d+(\.\d+)?)|(-\.\d+)/g, |
827 |
pathSvgRegExp: /([A-Za-z])|(\d+(\.\d+)?)|(\.\d+)|(-\d+(\.\d+)?)|(-\.\d+)/g, |
828 |
|
829 |
equalSources: function(a, b){ |
830 |
// summary: compares event sources, returns true if they are equal
|
831 |
return a && b && a == b;
|
832 |
} |
833 |
}); |
834 |
|
835 |
} |
836 |
|
837 |
if(!dojo._hasResource["dojox.gfx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
838 |
dojo._hasResource["dojox.gfx"] = true; |
839 |
dojo.provide("dojox.gfx");
|
840 |
|
841 |
|
842 |
|
843 |
|
844 |
dojo.loadInit(function(){
|
845 |
//Since loaderInit can be fired before any dojo.provide/require calls,
|
846 |
//make sure the dojox.gfx object exists and only run this logic if dojox.gfx.renderer
|
847 |
//has not been defined yet.
|
848 |
var gfx = dojo.getObject("dojox.gfx", true), sl, flag, match; |
849 |
if(!gfx.renderer){
|
850 |
//Have a way to force a GFX renderer, if so desired.
|
851 |
//Useful for being able to serialize GFX data in a particular format.
|
852 |
if(dojo.config.forceGfxRenderer){
|
853 |
dojox.gfx.renderer = dojo.config.forceGfxRenderer; |
854 |
return;
|
855 |
} |
856 |
var renderers = (typeof dojo.config.gfxRenderer == "string" ? |
857 |
dojo.config.gfxRenderer : "svg,vml,silverlight,canvas").split(","); |
858 |
|
859 |
// mobile platform detection
|
860 |
// TODO: move to the base?
|
861 |
|
862 |
var ua = navigator.userAgent, iPhoneOsBuild = 0, androidVersion = 0; |
863 |
if(dojo.isSafari >= 3){ |
864 |
// detect mobile version of WebKit starting with "version 3"
|
865 |
|
866 |
// comprehensive iPhone test. Have to figure out whether it's SVG or Canvas based on the build.
|
867 |
// iPhone OS build numbers from en.wikipedia.org.
|
868 |
if(ua.indexOf("iPhone") >= 0 || ua.indexOf("iPod") >= 0){ |
869 |
// grab the build out of this. Expression is a little nasty because we want
|
870 |
// to be sure we have the whole version string.
|
871 |
match = ua.match(/Version\/(\d(\.\d)?(\.\d)?)\sMobile\/([^\s]*)\s?/);
|
872 |
if(match){
|
873 |
// grab the build out of the match. Only use the first three because of specific builds.
|
874 |
iPhoneOsBuild = parseInt(match[4].substr(0,3), 16); |
875 |
} |
876 |
} |
877 |
} |
878 |
if(dojo.isWebKit){
|
879 |
// Android detection
|
880 |
if(!iPhoneOsBuild){
|
881 |
match = ua.match(/Android\s+(\d+\.\d+)/);
|
882 |
if(match){
|
883 |
androidVersion = parseFloat(match[1]);
|
884 |
// Android 1.0-1.1 doesn't support SVG but supports Canvas
|
885 |
} |
886 |
} |
887 |
} |
888 |
|
889 |
for(var i = 0; i < renderers.length; ++i){ |
890 |
switch(renderers[i]){
|
891 |
case "svg": |
892 |
// iPhone OS builds greater than 5F1 should have SVG.
|
893 |
if(!dojo.isIE && (!iPhoneOsBuild || iPhoneOsBuild >= 0x5f1) && !androidVersion && !dojo.isAIR){ |
894 |
dojox.gfx.renderer = "svg";
|
895 |
} |
896 |
break;
|
897 |
case "vml": |
898 |
if(dojo.isIE){
|
899 |
dojox.gfx.renderer = "vml";
|
900 |
} |
901 |
break;
|
902 |
case "silverlight": |
903 |
try{
|
904 |
if(dojo.isIE){
|
905 |
sl = new ActiveXObject("AgControl.AgControl"); |
906 |
if(sl && sl.IsVersionSupported("1.0")){ |
907 |
flag = true;
|
908 |
} |
909 |
}else{
|
910 |
if(navigator.plugins["Silverlight Plug-In"]){ |
911 |
flag = true;
|
912 |
} |
913 |
} |
914 |
}catch(e){
|
915 |
flag = false;
|
916 |
}finally{
|
917 |
sl = null;
|
918 |
} |
919 |
if(flag){ dojox.gfx.renderer = "silverlight"; } |
920 |
break;
|
921 |
case "canvas": |
922 |
//TODO: need more comprehensive test for Canvas
|
923 |
if(!dojo.isIE){
|
924 |
dojox.gfx.renderer = "canvas";
|
925 |
} |
926 |
break;
|
927 |
} |
928 |
if(dojox.gfx.renderer){ break; } |
929 |
} |
930 |
if(dojo.config.isDebug){
|
931 |
console.log("gfx renderer = " + dojox.gfx.renderer);
|
932 |
} |
933 |
} |
934 |
}); |
935 |
|
936 |
// include a renderer conditionally
|
937 |
dojo.requireIf(dojox.gfx.renderer == "svg", "dojox.gfx.svg"); |
938 |
dojo.requireIf(dojox.gfx.renderer == "vml", "dojox.gfx.vml"); |
939 |
dojo.requireIf(dojox.gfx.renderer == "silverlight", "dojox.gfx.silverlight"); |
940 |
dojo.requireIf(dojox.gfx.renderer == "canvas", "dojox.gfx.canvas"); |
941 |
|
942 |
} |
943 |
|