zui53 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +9 -0
- data/LICENSE +20 -0
- data/README.markdown +25 -0
- data/Rakefile +25 -0
- data/build/zui53.js +4014 -0
- data/demos/css_surface.html +30 -0
- data/demos/svg_surface.html +33 -0
- data/lib/assets/javascripts/zui53/.DS_Store +0 -0
- data/lib/assets/javascripts/zui53/helper.js +43 -0
- data/lib/assets/javascripts/zui53/index.js +4 -0
- data/lib/assets/javascripts/zui53/surfaces/css_surface.js.coffee +27 -0
- data/lib/assets/javascripts/zui53/surfaces/svg_surface.js.coffee +16 -0
- data/lib/assets/javascripts/zui53/tools/pan_tool.js.coffee +76 -0
- data/lib/assets/javascripts/zui53/tools/toolset.js.coffee +84 -0
- data/lib/assets/javascripts/zui53/tools/zoom_tool.js.coffee +167 -0
- data/lib/assets/javascripts/zui53/zui53.js.coffee +257 -0
- data/lib/generators/zui53/install_generator.rb +21 -0
- data/lib/zui53.rb +7 -0
- data/vendor/assets/javascripts/jquery.mousewheel.js +78 -0
- data/vendor/assets/javascripts/jquery.transform.js +1961 -0
- data/vendor/assets/javascripts/sylvester.js +1254 -0
- data/zui53.gemspec +20 -0
- metadata +75 -0
data/build/zui53.js
ADDED
@@ -0,0 +1,4014 @@
|
|
1
|
+
// # Sylvester
|
2
|
+
// Vector and Matrix mathematics modules for JavaScript
|
3
|
+
// Copyright (c) 2007 James Coglan
|
4
|
+
//
|
5
|
+
// Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
// a copy of this software and associated documentation files (the "Software"),
|
7
|
+
// to deal in the Software without restriction, including without limitation
|
8
|
+
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
9
|
+
// and/or sell copies of the Software, and to permit persons to whom the
|
10
|
+
// Software is furnished to do so, subject to the following conditions:
|
11
|
+
//
|
12
|
+
// The above copyright notice and this permission notice shall be included
|
13
|
+
// in all copies or substantial portions of the Software.
|
14
|
+
//
|
15
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
16
|
+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
18
|
+
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
21
|
+
// DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
var Sylvester = {
|
24
|
+
version: '0.1.3',
|
25
|
+
precision: 1e-6
|
26
|
+
};
|
27
|
+
|
28
|
+
function Vector() {}
|
29
|
+
Vector.prototype = {
|
30
|
+
|
31
|
+
// Returns element i of the vector
|
32
|
+
e: function(i) {
|
33
|
+
return (i < 1 || i > this.elements.length) ? null : this.elements[i-1];
|
34
|
+
},
|
35
|
+
|
36
|
+
// Returns the number of elements the vector has
|
37
|
+
dimensions: function() {
|
38
|
+
return this.elements.length;
|
39
|
+
},
|
40
|
+
|
41
|
+
// Returns the modulus ('length') of the vector
|
42
|
+
modulus: function() {
|
43
|
+
return Math.sqrt(this.dot(this));
|
44
|
+
},
|
45
|
+
|
46
|
+
// Returns true iff the vector is equal to the argument
|
47
|
+
eql: function(vector) {
|
48
|
+
var n = this.elements.length;
|
49
|
+
var V = vector.elements || vector;
|
50
|
+
if (n != V.length) { return false; }
|
51
|
+
do {
|
52
|
+
if (Math.abs(this.elements[n-1] - V[n-1]) > Sylvester.precision) { return false; }
|
53
|
+
} while (--n);
|
54
|
+
return true;
|
55
|
+
},
|
56
|
+
|
57
|
+
// Returns a copy of the vector
|
58
|
+
dup: function() {
|
59
|
+
return Vector.create(this.elements);
|
60
|
+
},
|
61
|
+
|
62
|
+
// Maps the vector to another vector according to the given function
|
63
|
+
map: function(fn) {
|
64
|
+
var elements = [];
|
65
|
+
this.each(function(x, i) {
|
66
|
+
elements.push(fn(x, i));
|
67
|
+
});
|
68
|
+
return Vector.create(elements);
|
69
|
+
},
|
70
|
+
|
71
|
+
// Calls the iterator for each element of the vector in turn
|
72
|
+
each: function(fn) {
|
73
|
+
var n = this.elements.length, k = n, i;
|
74
|
+
do { i = k - n;
|
75
|
+
fn(this.elements[i], i+1);
|
76
|
+
} while (--n);
|
77
|
+
},
|
78
|
+
|
79
|
+
// Returns a new vector created by normalizing the receiver
|
80
|
+
toUnitVector: function() {
|
81
|
+
var r = this.modulus();
|
82
|
+
if (r === 0) { return this.dup(); }
|
83
|
+
return this.map(function(x) { return x/r; });
|
84
|
+
},
|
85
|
+
|
86
|
+
// Returns the angle between the vector and the argument (also a vector)
|
87
|
+
angleFrom: function(vector) {
|
88
|
+
var V = vector.elements || vector;
|
89
|
+
var n = this.elements.length, k = n, i;
|
90
|
+
if (n != V.length) { return null; }
|
91
|
+
var dot = 0, mod1 = 0, mod2 = 0;
|
92
|
+
// Work things out in parallel to save time
|
93
|
+
this.each(function(x, i) {
|
94
|
+
dot += x * V[i-1];
|
95
|
+
mod1 += x * x;
|
96
|
+
mod2 += V[i-1] * V[i-1];
|
97
|
+
});
|
98
|
+
mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2);
|
99
|
+
if (mod1*mod2 === 0) { return null; }
|
100
|
+
var theta = dot / (mod1*mod2);
|
101
|
+
if (theta < -1) { theta = -1; }
|
102
|
+
if (theta > 1) { theta = 1; }
|
103
|
+
return Math.acos(theta);
|
104
|
+
},
|
105
|
+
|
106
|
+
// Returns true iff the vector is parallel to the argument
|
107
|
+
isParallelTo: function(vector) {
|
108
|
+
var angle = this.angleFrom(vector);
|
109
|
+
return (angle === null) ? null : (angle <= Sylvester.precision);
|
110
|
+
},
|
111
|
+
|
112
|
+
// Returns true iff the vector is antiparallel to the argument
|
113
|
+
isAntiparallelTo: function(vector) {
|
114
|
+
var angle = this.angleFrom(vector);
|
115
|
+
return (angle === null) ? null : (Math.abs(angle - Math.PI) <= Sylvester.precision);
|
116
|
+
},
|
117
|
+
|
118
|
+
// Returns true iff the vector is perpendicular to the argument
|
119
|
+
isPerpendicularTo: function(vector) {
|
120
|
+
var dot = this.dot(vector);
|
121
|
+
return (dot === null) ? null : (Math.abs(dot) <= Sylvester.precision);
|
122
|
+
},
|
123
|
+
|
124
|
+
// Returns the result of adding the argument to the vector
|
125
|
+
add: function(vector) {
|
126
|
+
var V = vector.elements || vector;
|
127
|
+
if (this.elements.length != V.length) { return null; }
|
128
|
+
return this.map(function(x, i) { return x + V[i-1]; });
|
129
|
+
},
|
130
|
+
|
131
|
+
// Returns the result of subtracting the argument from the vector
|
132
|
+
subtract: function(vector) {
|
133
|
+
var V = vector.elements || vector;
|
134
|
+
if (this.elements.length != V.length) { return null; }
|
135
|
+
return this.map(function(x, i) { return x - V[i-1]; });
|
136
|
+
},
|
137
|
+
|
138
|
+
// Returns the result of multiplying the elements of the vector by the argument
|
139
|
+
multiply: function(k) {
|
140
|
+
return this.map(function(x) { return x*k; });
|
141
|
+
},
|
142
|
+
|
143
|
+
x: function(k) { return this.multiply(k); },
|
144
|
+
|
145
|
+
// Returns the scalar product of the vector with the argument
|
146
|
+
// Both vectors must have equal dimensionality
|
147
|
+
dot: function(vector) {
|
148
|
+
var V = vector.elements || vector;
|
149
|
+
var i, product = 0, n = this.elements.length;
|
150
|
+
if (n != V.length) { return null; }
|
151
|
+
do { product += this.elements[n-1] * V[n-1]; } while (--n);
|
152
|
+
return product;
|
153
|
+
},
|
154
|
+
|
155
|
+
// Returns the vector product of the vector with the argument
|
156
|
+
// Both vectors must have dimensionality 3
|
157
|
+
cross: function(vector) {
|
158
|
+
var B = vector.elements || vector;
|
159
|
+
if (this.elements.length != 3 || B.length != 3) { return null; }
|
160
|
+
var A = this.elements;
|
161
|
+
return Vector.create([
|
162
|
+
(A[1] * B[2]) - (A[2] * B[1]),
|
163
|
+
(A[2] * B[0]) - (A[0] * B[2]),
|
164
|
+
(A[0] * B[1]) - (A[1] * B[0])
|
165
|
+
]);
|
166
|
+
},
|
167
|
+
|
168
|
+
// Returns the (absolute) largest element of the vector
|
169
|
+
max: function() {
|
170
|
+
var m = 0, n = this.elements.length, k = n, i;
|
171
|
+
do { i = k - n;
|
172
|
+
if (Math.abs(this.elements[i]) > Math.abs(m)) { m = this.elements[i]; }
|
173
|
+
} while (--n);
|
174
|
+
return m;
|
175
|
+
},
|
176
|
+
|
177
|
+
// Returns the index of the first match found
|
178
|
+
indexOf: function(x) {
|
179
|
+
var index = null, n = this.elements.length, k = n, i;
|
180
|
+
do { i = k - n;
|
181
|
+
if (index === null && this.elements[i] == x) {
|
182
|
+
index = i + 1;
|
183
|
+
}
|
184
|
+
} while (--n);
|
185
|
+
return index;
|
186
|
+
},
|
187
|
+
|
188
|
+
// Returns a diagonal matrix with the vector's elements as its diagonal elements
|
189
|
+
toDiagonalMatrix: function() {
|
190
|
+
return Matrix.Diagonal(this.elements);
|
191
|
+
},
|
192
|
+
|
193
|
+
// Returns the result of rounding the elements of the vector
|
194
|
+
round: function() {
|
195
|
+
return this.map(function(x) { return Math.round(x); });
|
196
|
+
},
|
197
|
+
|
198
|
+
// Returns a copy of the vector with elements set to the given value if they
|
199
|
+
// differ from it by less than Sylvester.precision
|
200
|
+
snapTo: function(x) {
|
201
|
+
return this.map(function(y) {
|
202
|
+
return (Math.abs(y - x) <= Sylvester.precision) ? x : y;
|
203
|
+
});
|
204
|
+
},
|
205
|
+
|
206
|
+
// Returns the vector's distance from the argument, when considered as a point in space
|
207
|
+
distanceFrom: function(obj) {
|
208
|
+
if (obj.anchor) { return obj.distanceFrom(this); }
|
209
|
+
var V = obj.elements || obj;
|
210
|
+
if (V.length != this.elements.length) { return null; }
|
211
|
+
var sum = 0, part;
|
212
|
+
this.each(function(x, i) {
|
213
|
+
part = x - V[i-1];
|
214
|
+
sum += part * part;
|
215
|
+
});
|
216
|
+
return Math.sqrt(sum);
|
217
|
+
},
|
218
|
+
|
219
|
+
// Returns true if the vector is point on the given line
|
220
|
+
liesOn: function(line) {
|
221
|
+
return line.contains(this);
|
222
|
+
},
|
223
|
+
|
224
|
+
// Return true iff the vector is a point in the given plane
|
225
|
+
liesIn: function(plane) {
|
226
|
+
return plane.contains(this);
|
227
|
+
},
|
228
|
+
|
229
|
+
// Rotates the vector about the given object. The object should be a
|
230
|
+
// point if the vector is 2D, and a line if it is 3D. Be careful with line directions!
|
231
|
+
rotate: function(t, obj) {
|
232
|
+
var V, R, x, y, z;
|
233
|
+
switch (this.elements.length) {
|
234
|
+
case 2:
|
235
|
+
V = obj.elements || obj;
|
236
|
+
if (V.length != 2) { return null; }
|
237
|
+
R = Matrix.Rotation(t).elements;
|
238
|
+
x = this.elements[0] - V[0];
|
239
|
+
y = this.elements[1] - V[1];
|
240
|
+
return Vector.create([
|
241
|
+
V[0] + R[0][0] * x + R[0][1] * y,
|
242
|
+
V[1] + R[1][0] * x + R[1][1] * y
|
243
|
+
]);
|
244
|
+
break;
|
245
|
+
case 3:
|
246
|
+
if (!obj.direction) { return null; }
|
247
|
+
var C = obj.pointClosestTo(this).elements;
|
248
|
+
R = Matrix.Rotation(t, obj.direction).elements;
|
249
|
+
x = this.elements[0] - C[0];
|
250
|
+
y = this.elements[1] - C[1];
|
251
|
+
z = this.elements[2] - C[2];
|
252
|
+
return Vector.create([
|
253
|
+
C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z,
|
254
|
+
C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z,
|
255
|
+
C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z
|
256
|
+
]);
|
257
|
+
break;
|
258
|
+
default:
|
259
|
+
return null;
|
260
|
+
}
|
261
|
+
},
|
262
|
+
|
263
|
+
// Returns the result of reflecting the point in the given point, line or plane
|
264
|
+
reflectionIn: function(obj) {
|
265
|
+
if (obj.anchor) {
|
266
|
+
// obj is a plane or line
|
267
|
+
var P = this.elements.slice();
|
268
|
+
var C = obj.pointClosestTo(P).elements;
|
269
|
+
return Vector.create([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);
|
270
|
+
} else {
|
271
|
+
// obj is a point
|
272
|
+
var Q = obj.elements || obj;
|
273
|
+
if (this.elements.length != Q.length) { return null; }
|
274
|
+
return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); });
|
275
|
+
}
|
276
|
+
},
|
277
|
+
|
278
|
+
// Utility to make sure vectors are 3D. If they are 2D, a zero z-component is added
|
279
|
+
to3D: function() {
|
280
|
+
var V = this.dup();
|
281
|
+
switch (V.elements.length) {
|
282
|
+
case 3: break;
|
283
|
+
case 2: V.elements.push(0); break;
|
284
|
+
default: return null;
|
285
|
+
}
|
286
|
+
return V;
|
287
|
+
},
|
288
|
+
|
289
|
+
// Returns a string representation of the vector
|
290
|
+
inspect: function() {
|
291
|
+
return '[' + this.elements.join(', ') + ']';
|
292
|
+
},
|
293
|
+
|
294
|
+
// Set vector's elements from an array
|
295
|
+
setElements: function(els) {
|
296
|
+
this.elements = (els.elements || els).slice();
|
297
|
+
return this;
|
298
|
+
}
|
299
|
+
};
|
300
|
+
|
301
|
+
// Constructor function
|
302
|
+
Vector.create = function(elements) {
|
303
|
+
var V = new Vector();
|
304
|
+
return V.setElements(elements);
|
305
|
+
};
|
306
|
+
|
307
|
+
// i, j, k unit vectors
|
308
|
+
Vector.i = Vector.create([1,0,0]);
|
309
|
+
Vector.j = Vector.create([0,1,0]);
|
310
|
+
Vector.k = Vector.create([0,0,1]);
|
311
|
+
|
312
|
+
// Random vector of size n
|
313
|
+
Vector.Random = function(n) {
|
314
|
+
var elements = [];
|
315
|
+
do { elements.push(Math.random());
|
316
|
+
} while (--n);
|
317
|
+
return Vector.create(elements);
|
318
|
+
};
|
319
|
+
|
320
|
+
// Vector filled with zeros
|
321
|
+
Vector.Zero = function(n) {
|
322
|
+
var elements = [];
|
323
|
+
do { elements.push(0);
|
324
|
+
} while (--n);
|
325
|
+
return Vector.create(elements);
|
326
|
+
};
|
327
|
+
|
328
|
+
|
329
|
+
|
330
|
+
function Matrix() {}
|
331
|
+
Matrix.prototype = {
|
332
|
+
|
333
|
+
// Returns element (i,j) of the matrix
|
334
|
+
e: function(i,j) {
|
335
|
+
if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; }
|
336
|
+
return this.elements[i-1][j-1];
|
337
|
+
},
|
338
|
+
|
339
|
+
// Returns row k of the matrix as a vector
|
340
|
+
row: function(i) {
|
341
|
+
if (i > this.elements.length) { return null; }
|
342
|
+
return Vector.create(this.elements[i-1]);
|
343
|
+
},
|
344
|
+
|
345
|
+
// Returns column k of the matrix as a vector
|
346
|
+
col: function(j) {
|
347
|
+
if (j > this.elements[0].length) { return null; }
|
348
|
+
var col = [], n = this.elements.length, k = n, i;
|
349
|
+
do { i = k - n;
|
350
|
+
col.push(this.elements[i][j-1]);
|
351
|
+
} while (--n);
|
352
|
+
return Vector.create(col);
|
353
|
+
},
|
354
|
+
|
355
|
+
// Returns the number of rows/columns the matrix has
|
356
|
+
dimensions: function() {
|
357
|
+
return {rows: this.elements.length, cols: this.elements[0].length};
|
358
|
+
},
|
359
|
+
|
360
|
+
// Returns the number of rows in the matrix
|
361
|
+
rows: function() {
|
362
|
+
return this.elements.length;
|
363
|
+
},
|
364
|
+
|
365
|
+
// Returns the number of columns in the matrix
|
366
|
+
cols: function() {
|
367
|
+
return this.elements[0].length;
|
368
|
+
},
|
369
|
+
|
370
|
+
// Returns true iff the matrix is equal to the argument. You can supply
|
371
|
+
// a vector as the argument, in which case the receiver must be a
|
372
|
+
// one-column matrix equal to the vector.
|
373
|
+
eql: function(matrix) {
|
374
|
+
var M = matrix.elements || matrix;
|
375
|
+
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
|
376
|
+
if (this.elements.length != M.length ||
|
377
|
+
this.elements[0].length != M[0].length) { return false; }
|
378
|
+
var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
|
379
|
+
do { i = ki - ni;
|
380
|
+
nj = kj;
|
381
|
+
do { j = kj - nj;
|
382
|
+
if (Math.abs(this.elements[i][j] - M[i][j]) > Sylvester.precision) { return false; }
|
383
|
+
} while (--nj);
|
384
|
+
} while (--ni);
|
385
|
+
return true;
|
386
|
+
},
|
387
|
+
|
388
|
+
// Returns a copy of the matrix
|
389
|
+
dup: function() {
|
390
|
+
return Matrix.create(this.elements);
|
391
|
+
},
|
392
|
+
|
393
|
+
// Maps the matrix to another matrix (of the same dimensions) according to the given function
|
394
|
+
map: function(fn) {
|
395
|
+
var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
|
396
|
+
do { i = ki - ni;
|
397
|
+
nj = kj;
|
398
|
+
els[i] = [];
|
399
|
+
do { j = kj - nj;
|
400
|
+
els[i][j] = fn(this.elements[i][j], i + 1, j + 1);
|
401
|
+
} while (--nj);
|
402
|
+
} while (--ni);
|
403
|
+
return Matrix.create(els);
|
404
|
+
},
|
405
|
+
|
406
|
+
// Returns true iff the argument has the same dimensions as the matrix
|
407
|
+
isSameSizeAs: function(matrix) {
|
408
|
+
var M = matrix.elements || matrix;
|
409
|
+
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
|
410
|
+
return (this.elements.length == M.length &&
|
411
|
+
this.elements[0].length == M[0].length);
|
412
|
+
},
|
413
|
+
|
414
|
+
// Returns the result of adding the argument to the matrix
|
415
|
+
add: function(matrix) {
|
416
|
+
var M = matrix.elements || matrix;
|
417
|
+
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
|
418
|
+
if (!this.isSameSizeAs(M)) { return null; }
|
419
|
+
return this.map(function(x, i, j) { return x + M[i-1][j-1]; });
|
420
|
+
},
|
421
|
+
|
422
|
+
// Returns the result of subtracting the argument from the matrix
|
423
|
+
subtract: function(matrix) {
|
424
|
+
var M = matrix.elements || matrix;
|
425
|
+
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
|
426
|
+
if (!this.isSameSizeAs(M)) { return null; }
|
427
|
+
return this.map(function(x, i, j) { return x - M[i-1][j-1]; });
|
428
|
+
},
|
429
|
+
|
430
|
+
// Returns true iff the matrix can multiply the argument from the left
|
431
|
+
canMultiplyFromLeft: function(matrix) {
|
432
|
+
var M = matrix.elements || matrix;
|
433
|
+
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
|
434
|
+
// this.columns should equal matrix.rows
|
435
|
+
return (this.elements[0].length == M.length);
|
436
|
+
},
|
437
|
+
|
438
|
+
// Returns the result of multiplying the matrix from the right by the argument.
|
439
|
+
// If the argument is a scalar then just multiply all the elements. If the argument is
|
440
|
+
// a vector, a vector is returned, which saves you having to remember calling
|
441
|
+
// col(1) on the result.
|
442
|
+
multiply: function(matrix) {
|
443
|
+
if (!matrix.elements) {
|
444
|
+
return this.map(function(x) { return x * matrix; });
|
445
|
+
}
|
446
|
+
var returnVector = matrix.modulus ? true : false;
|
447
|
+
var M = matrix.elements || matrix;
|
448
|
+
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
|
449
|
+
if (!this.canMultiplyFromLeft(M)) { return null; }
|
450
|
+
var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;
|
451
|
+
var cols = this.elements[0].length, elements = [], sum, nc, c;
|
452
|
+
do { i = ki - ni;
|
453
|
+
elements[i] = [];
|
454
|
+
nj = kj;
|
455
|
+
do { j = kj - nj;
|
456
|
+
sum = 0;
|
457
|
+
nc = cols;
|
458
|
+
do { c = cols - nc;
|
459
|
+
sum += this.elements[i][c] * M[c][j];
|
460
|
+
} while (--nc);
|
461
|
+
elements[i][j] = sum;
|
462
|
+
} while (--nj);
|
463
|
+
} while (--ni);
|
464
|
+
var M = Matrix.create(elements);
|
465
|
+
return returnVector ? M.col(1) : M;
|
466
|
+
},
|
467
|
+
|
468
|
+
x: function(matrix) { return this.multiply(matrix); },
|
469
|
+
|
470
|
+
// Returns a submatrix taken from the matrix
|
471
|
+
// Argument order is: start row, start col, nrows, ncols
|
472
|
+
// Element selection wraps if the required index is outside the matrix's bounds, so you could
|
473
|
+
// use this to perform row/column cycling or copy-augmenting.
|
474
|
+
minor: function(a, b, c, d) {
|
475
|
+
var elements = [], ni = c, i, nj, j;
|
476
|
+
var rows = this.elements.length, cols = this.elements[0].length;
|
477
|
+
do { i = c - ni;
|
478
|
+
elements[i] = [];
|
479
|
+
nj = d;
|
480
|
+
do { j = d - nj;
|
481
|
+
elements[i][j] = this.elements[(a+i-1)%rows][(b+j-1)%cols];
|
482
|
+
} while (--nj);
|
483
|
+
} while (--ni);
|
484
|
+
return Matrix.create(elements);
|
485
|
+
},
|
486
|
+
|
487
|
+
// Returns the transpose of the matrix
|
488
|
+
transpose: function() {
|
489
|
+
var rows = this.elements.length, cols = this.elements[0].length;
|
490
|
+
var elements = [], ni = cols, i, nj, j;
|
491
|
+
do { i = cols - ni;
|
492
|
+
elements[i] = [];
|
493
|
+
nj = rows;
|
494
|
+
do { j = rows - nj;
|
495
|
+
elements[i][j] = this.elements[j][i];
|
496
|
+
} while (--nj);
|
497
|
+
} while (--ni);
|
498
|
+
return Matrix.create(elements);
|
499
|
+
},
|
500
|
+
|
501
|
+
// Returns true iff the matrix is square
|
502
|
+
isSquare: function() {
|
503
|
+
return (this.elements.length == this.elements[0].length);
|
504
|
+
},
|
505
|
+
|
506
|
+
// Returns the (absolute) largest element of the matrix
|
507
|
+
max: function() {
|
508
|
+
var m = 0, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
|
509
|
+
do { i = ki - ni;
|
510
|
+
nj = kj;
|
511
|
+
do { j = kj - nj;
|
512
|
+
if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; }
|
513
|
+
} while (--nj);
|
514
|
+
} while (--ni);
|
515
|
+
return m;
|
516
|
+
},
|
517
|
+
|
518
|
+
// Returns the indeces of the first match found by reading row-by-row from left to right
|
519
|
+
indexOf: function(x) {
|
520
|
+
var index = null, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
|
521
|
+
do { i = ki - ni;
|
522
|
+
nj = kj;
|
523
|
+
do { j = kj - nj;
|
524
|
+
if (this.elements[i][j] == x) { return {i: i+1, j: j+1}; }
|
525
|
+
} while (--nj);
|
526
|
+
} while (--ni);
|
527
|
+
return null;
|
528
|
+
},
|
529
|
+
|
530
|
+
// If the matrix is square, returns the diagonal elements as a vector.
|
531
|
+
// Otherwise, returns null.
|
532
|
+
diagonal: function() {
|
533
|
+
if (!this.isSquare) { return null; }
|
534
|
+
var els = [], n = this.elements.length, k = n, i;
|
535
|
+
do { i = k - n;
|
536
|
+
els.push(this.elements[i][i]);
|
537
|
+
} while (--n);
|
538
|
+
return Vector.create(els);
|
539
|
+
},
|
540
|
+
|
541
|
+
// Make the matrix upper (right) triangular by Gaussian elimination.
|
542
|
+
// This method only adds multiples of rows to other rows. No rows are
|
543
|
+
// scaled up or switched, and the determinant is preserved.
|
544
|
+
toRightTriangular: function() {
|
545
|
+
var M = this.dup(), els;
|
546
|
+
var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p;
|
547
|
+
do { i = k - n;
|
548
|
+
if (M.elements[i][i] == 0) {
|
549
|
+
for (j = i + 1; j < k; j++) {
|
550
|
+
if (M.elements[j][i] != 0) {
|
551
|
+
els = []; np = kp;
|
552
|
+
do { p = kp - np;
|
553
|
+
els.push(M.elements[i][p] + M.elements[j][p]);
|
554
|
+
} while (--np);
|
555
|
+
M.elements[i] = els;
|
556
|
+
break;
|
557
|
+
}
|
558
|
+
}
|
559
|
+
}
|
560
|
+
if (M.elements[i][i] != 0) {
|
561
|
+
for (j = i + 1; j < k; j++) {
|
562
|
+
var multiplier = M.elements[j][i] / M.elements[i][i];
|
563
|
+
els = []; np = kp;
|
564
|
+
do { p = kp - np;
|
565
|
+
// Elements with column numbers up to an including the number
|
566
|
+
// of the row that we're subtracting can safely be set straight to
|
567
|
+
// zero, since that's the point of this routine and it avoids having
|
568
|
+
// to loop over and correct rounding errors later
|
569
|
+
els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);
|
570
|
+
} while (--np);
|
571
|
+
M.elements[j] = els;
|
572
|
+
}
|
573
|
+
}
|
574
|
+
} while (--n);
|
575
|
+
return M;
|
576
|
+
},
|
577
|
+
|
578
|
+
toUpperTriangular: function() { return this.toRightTriangular(); },
|
579
|
+
|
580
|
+
// Returns the determinant for square matrices
|
581
|
+
determinant: function() {
|
582
|
+
if (!this.isSquare()) { return null; }
|
583
|
+
var M = this.toRightTriangular();
|
584
|
+
var det = M.elements[0][0], n = M.elements.length - 1, k = n, i;
|
585
|
+
do { i = k - n + 1;
|
586
|
+
det = det * M.elements[i][i];
|
587
|
+
} while (--n);
|
588
|
+
return det;
|
589
|
+
},
|
590
|
+
|
591
|
+
det: function() { return this.determinant(); },
|
592
|
+
|
593
|
+
// Returns true iff the matrix is singular
|
594
|
+
isSingular: function() {
|
595
|
+
return (this.isSquare() && this.determinant() === 0);
|
596
|
+
},
|
597
|
+
|
598
|
+
// Returns the trace for square matrices
|
599
|
+
trace: function() {
|
600
|
+
if (!this.isSquare()) { return null; }
|
601
|
+
var tr = this.elements[0][0], n = this.elements.length - 1, k = n, i;
|
602
|
+
do { i = k - n + 1;
|
603
|
+
tr += this.elements[i][i];
|
604
|
+
} while (--n);
|
605
|
+
return tr;
|
606
|
+
},
|
607
|
+
|
608
|
+
tr: function() { return this.trace(); },
|
609
|
+
|
610
|
+
// Returns the rank of the matrix
|
611
|
+
rank: function() {
|
612
|
+
var M = this.toRightTriangular(), rank = 0;
|
613
|
+
var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
|
614
|
+
do { i = ki - ni;
|
615
|
+
nj = kj;
|
616
|
+
do { j = kj - nj;
|
617
|
+
if (Math.abs(M.elements[i][j]) > Sylvester.precision) { rank++; break; }
|
618
|
+
} while (--nj);
|
619
|
+
} while (--ni);
|
620
|
+
return rank;
|
621
|
+
},
|
622
|
+
|
623
|
+
rk: function() { return this.rank(); },
|
624
|
+
|
625
|
+
// Returns the result of attaching the given argument to the right-hand side of the matrix
|
626
|
+
augment: function(matrix) {
|
627
|
+
var M = matrix.elements || matrix;
|
628
|
+
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
|
629
|
+
var T = this.dup(), cols = T.elements[0].length;
|
630
|
+
var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j;
|
631
|
+
if (ni != M.length) { return null; }
|
632
|
+
do { i = ki - ni;
|
633
|
+
nj = kj;
|
634
|
+
do { j = kj - nj;
|
635
|
+
T.elements[i][cols + j] = M[i][j];
|
636
|
+
} while (--nj);
|
637
|
+
} while (--ni);
|
638
|
+
return T;
|
639
|
+
},
|
640
|
+
|
641
|
+
// Returns the inverse (if one exists) using Gauss-Jordan
|
642
|
+
inverse: function() {
|
643
|
+
if (!this.isSquare() || this.isSingular()) { return null; }
|
644
|
+
var ni = this.elements.length, ki = ni, i, j;
|
645
|
+
var M = this.augment(Matrix.I(ni)).toRightTriangular();
|
646
|
+
var np, kp = M.elements[0].length, p, els, divisor;
|
647
|
+
var inverse_elements = [], new_element;
|
648
|
+
// Matrix is non-singular so there will be no zeros on the diagonal
|
649
|
+
// Cycle through rows from last to first
|
650
|
+
do { i = ni - 1;
|
651
|
+
// First, normalise diagonal elements to 1
|
652
|
+
els = []; np = kp;
|
653
|
+
inverse_elements[i] = [];
|
654
|
+
divisor = M.elements[i][i];
|
655
|
+
do { p = kp - np;
|
656
|
+
new_element = M.elements[i][p] / divisor;
|
657
|
+
els.push(new_element);
|
658
|
+
// Shuffle of the current row of the right hand side into the results
|
659
|
+
// array as it will not be modified by later runs through this loop
|
660
|
+
if (p >= ki) { inverse_elements[i].push(new_element); }
|
661
|
+
} while (--np);
|
662
|
+
M.elements[i] = els;
|
663
|
+
// Then, subtract this row from those above it to
|
664
|
+
// give the identity matrix on the left hand side
|
665
|
+
for (j = 0; j < i; j++) {
|
666
|
+
els = []; np = kp;
|
667
|
+
do { p = kp - np;
|
668
|
+
els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);
|
669
|
+
} while (--np);
|
670
|
+
M.elements[j] = els;
|
671
|
+
}
|
672
|
+
} while (--ni);
|
673
|
+
return Matrix.create(inverse_elements);
|
674
|
+
},
|
675
|
+
|
676
|
+
inv: function() { return this.inverse(); },
|
677
|
+
|
678
|
+
// Returns the result of rounding all the elements
|
679
|
+
round: function() {
|
680
|
+
return this.map(function(x) { return Math.round(x); });
|
681
|
+
},
|
682
|
+
|
683
|
+
// Returns a copy of the matrix with elements set to the given value if they
|
684
|
+
// differ from it by less than Sylvester.precision
|
685
|
+
snapTo: function(x) {
|
686
|
+
return this.map(function(p) {
|
687
|
+
return (Math.abs(p - x) <= Sylvester.precision) ? x : p;
|
688
|
+
});
|
689
|
+
},
|
690
|
+
|
691
|
+
// Returns a string representation of the matrix
|
692
|
+
inspect: function() {
|
693
|
+
var matrix_rows = [];
|
694
|
+
var n = this.elements.length, k = n, i;
|
695
|
+
do { i = k - n;
|
696
|
+
matrix_rows.push(Vector.create(this.elements[i]).inspect());
|
697
|
+
} while (--n);
|
698
|
+
return matrix_rows.join('\n');
|
699
|
+
},
|
700
|
+
|
701
|
+
// Set the matrix's elements from an array. If the argument passed
|
702
|
+
// is a vector, the resulting matrix will be a single column.
|
703
|
+
setElements: function(els) {
|
704
|
+
var i, elements = els.elements || els;
|
705
|
+
if (typeof(elements[0][0]) != 'undefined') {
|
706
|
+
var ni = elements.length, ki = ni, nj, kj, j;
|
707
|
+
this.elements = [];
|
708
|
+
do { i = ki - ni;
|
709
|
+
nj = elements[i].length; kj = nj;
|
710
|
+
this.elements[i] = [];
|
711
|
+
do { j = kj - nj;
|
712
|
+
this.elements[i][j] = elements[i][j];
|
713
|
+
} while (--nj);
|
714
|
+
} while(--ni);
|
715
|
+
return this;
|
716
|
+
}
|
717
|
+
var n = elements.length, k = n;
|
718
|
+
this.elements = [];
|
719
|
+
do { i = k - n;
|
720
|
+
this.elements.push([elements[i]]);
|
721
|
+
} while (--n);
|
722
|
+
return this;
|
723
|
+
}
|
724
|
+
};
|
725
|
+
|
726
|
+
// Constructor function
|
727
|
+
Matrix.create = function(elements) {
|
728
|
+
var M = new Matrix();
|
729
|
+
return M.setElements(elements);
|
730
|
+
};
|
731
|
+
|
732
|
+
// Identity matrix of size n
|
733
|
+
Matrix.I = function(n) {
|
734
|
+
var els = [], k = n, i, nj, j;
|
735
|
+
do { i = k - n;
|
736
|
+
els[i] = []; nj = k;
|
737
|
+
do { j = k - nj;
|
738
|
+
els[i][j] = (i == j) ? 1 : 0;
|
739
|
+
} while (--nj);
|
740
|
+
} while (--n);
|
741
|
+
return Matrix.create(els);
|
742
|
+
};
|
743
|
+
|
744
|
+
// Diagonal matrix - all off-diagonal elements are zero
|
745
|
+
Matrix.Diagonal = function(elements) {
|
746
|
+
var n = elements.length, k = n, i;
|
747
|
+
var M = Matrix.I(n);
|
748
|
+
do { i = k - n;
|
749
|
+
M.elements[i][i] = elements[i];
|
750
|
+
} while (--n);
|
751
|
+
return M;
|
752
|
+
};
|
753
|
+
|
754
|
+
// Rotation matrix about some axis. If no axis is
|
755
|
+
// supplied, assume we're after a 2D transform
|
756
|
+
Matrix.Rotation = function(theta, a) {
|
757
|
+
if (!a) {
|
758
|
+
return Matrix.create([
|
759
|
+
[Math.cos(theta), -Math.sin(theta)],
|
760
|
+
[Math.sin(theta), Math.cos(theta)]
|
761
|
+
]);
|
762
|
+
}
|
763
|
+
var axis = a.dup();
|
764
|
+
if (axis.elements.length != 3) { return null; }
|
765
|
+
var mod = axis.modulus();
|
766
|
+
var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod;
|
767
|
+
var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c;
|
768
|
+
// Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp
|
769
|
+
// That proof rotates the co-ordinate system so theta
|
770
|
+
// becomes -theta and sin becomes -sin here.
|
771
|
+
return Matrix.create([
|
772
|
+
[ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],
|
773
|
+
[ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],
|
774
|
+
[ t*x*z - s*y, t*y*z + s*x, t*z*z + c ]
|
775
|
+
]);
|
776
|
+
};
|
777
|
+
|
778
|
+
// Special case rotations
|
779
|
+
Matrix.RotationX = function(t) {
|
780
|
+
var c = Math.cos(t), s = Math.sin(t);
|
781
|
+
return Matrix.create([
|
782
|
+
[ 1, 0, 0 ],
|
783
|
+
[ 0, c, -s ],
|
784
|
+
[ 0, s, c ]
|
785
|
+
]);
|
786
|
+
};
|
787
|
+
Matrix.RotationY = function(t) {
|
788
|
+
var c = Math.cos(t), s = Math.sin(t);
|
789
|
+
return Matrix.create([
|
790
|
+
[ c, 0, s ],
|
791
|
+
[ 0, 1, 0 ],
|
792
|
+
[ -s, 0, c ]
|
793
|
+
]);
|
794
|
+
};
|
795
|
+
Matrix.RotationZ = function(t) {
|
796
|
+
var c = Math.cos(t), s = Math.sin(t);
|
797
|
+
return Matrix.create([
|
798
|
+
[ c, -s, 0 ],
|
799
|
+
[ s, c, 0 ],
|
800
|
+
[ 0, 0, 1 ]
|
801
|
+
]);
|
802
|
+
};
|
803
|
+
|
804
|
+
// Random matrix of n rows, m columns
|
805
|
+
Matrix.Random = function(n, m) {
|
806
|
+
return Matrix.Zero(n, m).map(
|
807
|
+
function() { return Math.random(); }
|
808
|
+
);
|
809
|
+
};
|
810
|
+
|
811
|
+
// Matrix filled with zeros
|
812
|
+
Matrix.Zero = function(n, m) {
|
813
|
+
var els = [], ni = n, i, nj, j;
|
814
|
+
do { i = n - ni;
|
815
|
+
els[i] = [];
|
816
|
+
nj = m;
|
817
|
+
do { j = m - nj;
|
818
|
+
els[i][j] = 0;
|
819
|
+
} while (--nj);
|
820
|
+
} while (--ni);
|
821
|
+
return Matrix.create(els);
|
822
|
+
};
|
823
|
+
|
824
|
+
|
825
|
+
|
826
|
+
function Line() {}
|
827
|
+
Line.prototype = {
|
828
|
+
|
829
|
+
// Returns true if the argument occupies the same space as the line
|
830
|
+
eql: function(line) {
|
831
|
+
return (this.isParallelTo(line) && this.contains(line.anchor));
|
832
|
+
},
|
833
|
+
|
834
|
+
// Returns a copy of the line
|
835
|
+
dup: function() {
|
836
|
+
return Line.create(this.anchor, this.direction);
|
837
|
+
},
|
838
|
+
|
839
|
+
// Returns the result of translating the line by the given vector/array
|
840
|
+
translate: function(vector) {
|
841
|
+
var V = vector.elements || vector;
|
842
|
+
return Line.create([
|
843
|
+
this.anchor.elements[0] + V[0],
|
844
|
+
this.anchor.elements[1] + V[1],
|
845
|
+
this.anchor.elements[2] + (V[2] || 0)
|
846
|
+
], this.direction);
|
847
|
+
},
|
848
|
+
|
849
|
+
// Returns true if the line is parallel to the argument. Here, 'parallel to'
|
850
|
+
// means that the argument's direction is either parallel or antiparallel to
|
851
|
+
// the line's own direction. A line is parallel to a plane if the two do not
|
852
|
+
// have a unique intersection.
|
853
|
+
isParallelTo: function(obj) {
|
854
|
+
if (obj.normal) { return obj.isParallelTo(this); }
|
855
|
+
var theta = this.direction.angleFrom(obj.direction);
|
856
|
+
return (Math.abs(theta) <= Sylvester.precision || Math.abs(theta - Math.PI) <= Sylvester.precision);
|
857
|
+
},
|
858
|
+
|
859
|
+
// Returns the line's perpendicular distance from the argument,
|
860
|
+
// which can be a point, a line or a plane
|
861
|
+
distanceFrom: function(obj) {
|
862
|
+
if (obj.normal) { return obj.distanceFrom(this); }
|
863
|
+
if (obj.direction) {
|
864
|
+
// obj is a line
|
865
|
+
if (this.isParallelTo(obj)) { return this.distanceFrom(obj.anchor); }
|
866
|
+
var N = this.direction.cross(obj.direction).toUnitVector().elements;
|
867
|
+
var A = this.anchor.elements, B = obj.anchor.elements;
|
868
|
+
return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
|
869
|
+
} else {
|
870
|
+
// obj is a point
|
871
|
+
var P = obj.elements || obj;
|
872
|
+
var A = this.anchor.elements, D = this.direction.elements;
|
873
|
+
var PA1 = P[0] - A[0], PA2 = P[1] - A[1], PA3 = (P[2] || 0) - A[2];
|
874
|
+
var modPA = Math.sqrt(PA1*PA1 + PA2*PA2 + PA3*PA3);
|
875
|
+
if (modPA === 0) return 0;
|
876
|
+
// Assumes direction vector is normalized
|
877
|
+
var cosTheta = (PA1 * D[0] + PA2 * D[1] + PA3 * D[2]) / modPA;
|
878
|
+
var sin2 = 1 - cosTheta*cosTheta;
|
879
|
+
return Math.abs(modPA * Math.sqrt(sin2 < 0 ? 0 : sin2));
|
880
|
+
}
|
881
|
+
},
|
882
|
+
|
883
|
+
// Returns true iff the argument is a point on the line
|
884
|
+
contains: function(point) {
|
885
|
+
var dist = this.distanceFrom(point);
|
886
|
+
return (dist !== null && dist <= Sylvester.precision);
|
887
|
+
},
|
888
|
+
|
889
|
+
// Returns true iff the line lies in the given plane
|
890
|
+
liesIn: function(plane) {
|
891
|
+
return plane.contains(this);
|
892
|
+
},
|
893
|
+
|
894
|
+
// Returns true iff the line has a unique point of intersection with the argument
|
895
|
+
intersects: function(obj) {
|
896
|
+
if (obj.normal) { return obj.intersects(this); }
|
897
|
+
return (!this.isParallelTo(obj) && this.distanceFrom(obj) <= Sylvester.precision);
|
898
|
+
},
|
899
|
+
|
900
|
+
// Returns the unique intersection point with the argument, if one exists
|
901
|
+
intersectionWith: function(obj) {
|
902
|
+
if (obj.normal) { return obj.intersectionWith(this); }
|
903
|
+
if (!this.intersects(obj)) { return null; }
|
904
|
+
var P = this.anchor.elements, X = this.direction.elements,
|
905
|
+
Q = obj.anchor.elements, Y = obj.direction.elements;
|
906
|
+
var X1 = X[0], X2 = X[1], X3 = X[2], Y1 = Y[0], Y2 = Y[1], Y3 = Y[2];
|
907
|
+
var PsubQ1 = P[0] - Q[0], PsubQ2 = P[1] - Q[1], PsubQ3 = P[2] - Q[2];
|
908
|
+
var XdotQsubP = - X1*PsubQ1 - X2*PsubQ2 - X3*PsubQ3;
|
909
|
+
var YdotPsubQ = Y1*PsubQ1 + Y2*PsubQ2 + Y3*PsubQ3;
|
910
|
+
var XdotX = X1*X1 + X2*X2 + X3*X3;
|
911
|
+
var YdotY = Y1*Y1 + Y2*Y2 + Y3*Y3;
|
912
|
+
var XdotY = X1*Y1 + X2*Y2 + X3*Y3;
|
913
|
+
var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);
|
914
|
+
return Vector.create([P[0] + k*X1, P[1] + k*X2, P[2] + k*X3]);
|
915
|
+
},
|
916
|
+
|
917
|
+
// Returns the point on the line that is closest to the given point or line
|
918
|
+
pointClosestTo: function(obj) {
|
919
|
+
if (obj.direction) {
|
920
|
+
// obj is a line
|
921
|
+
if (this.intersects(obj)) { return this.intersectionWith(obj); }
|
922
|
+
if (this.isParallelTo(obj)) { return null; }
|
923
|
+
var D = this.direction.elements, E = obj.direction.elements;
|
924
|
+
var D1 = D[0], D2 = D[1], D3 = D[2], E1 = E[0], E2 = E[1], E3 = E[2];
|
925
|
+
// Create plane containing obj and the shared normal and intersect this with it
|
926
|
+
// Thank you: http://www.cgafaq.info/wiki/Line-line_distance
|
927
|
+
var x = (D3 * E1 - D1 * E3), y = (D1 * E2 - D2 * E1), z = (D2 * E3 - D3 * E2);
|
928
|
+
var N = Vector.create([x * E3 - y * E2, y * E1 - z * E3, z * E2 - x * E1]);
|
929
|
+
var P = Plane.create(obj.anchor, N);
|
930
|
+
return P.intersectionWith(this);
|
931
|
+
} else {
|
932
|
+
// obj is a point
|
933
|
+
var P = obj.elements || obj;
|
934
|
+
if (this.contains(P)) { return Vector.create(P); }
|
935
|
+
var A = this.anchor.elements, D = this.direction.elements;
|
936
|
+
var D1 = D[0], D2 = D[1], D3 = D[2], A1 = A[0], A2 = A[1], A3 = A[2];
|
937
|
+
var x = D1 * (P[1]-A2) - D2 * (P[0]-A1), y = D2 * ((P[2] || 0) - A3) - D3 * (P[1]-A2),
|
938
|
+
z = D3 * (P[0]-A1) - D1 * ((P[2] || 0) - A3);
|
939
|
+
var V = Vector.create([D2 * x - D3 * z, D3 * y - D1 * x, D1 * z - D2 * y]);
|
940
|
+
var k = this.distanceFrom(P) / V.modulus();
|
941
|
+
return Vector.create([
|
942
|
+
P[0] + V.elements[0] * k,
|
943
|
+
P[1] + V.elements[1] * k,
|
944
|
+
(P[2] || 0) + V.elements[2] * k
|
945
|
+
]);
|
946
|
+
}
|
947
|
+
},
|
948
|
+
|
949
|
+
// Returns a copy of the line rotated by t radians about the given line. Works by
|
950
|
+
// finding the argument's closest point to this line's anchor point (call this C) and
|
951
|
+
// rotating the anchor about C. Also rotates the line's direction about the argument's.
|
952
|
+
// Be careful with this - the rotation axis' direction affects the outcome!
|
953
|
+
rotate: function(t, line) {
|
954
|
+
// If we're working in 2D
|
955
|
+
if (typeof(line.direction) == 'undefined') { line = Line.create(line.to3D(), Vector.k); }
|
956
|
+
var R = Matrix.Rotation(t, line.direction).elements;
|
957
|
+
var C = line.pointClosestTo(this.anchor).elements;
|
958
|
+
var A = this.anchor.elements, D = this.direction.elements;
|
959
|
+
var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
|
960
|
+
var x = A1 - C1, y = A2 - C2, z = A3 - C3;
|
961
|
+
return Line.create([
|
962
|
+
C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
|
963
|
+
C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
|
964
|
+
C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
|
965
|
+
], [
|
966
|
+
R[0][0] * D[0] + R[0][1] * D[1] + R[0][2] * D[2],
|
967
|
+
R[1][0] * D[0] + R[1][1] * D[1] + R[1][2] * D[2],
|
968
|
+
R[2][0] * D[0] + R[2][1] * D[1] + R[2][2] * D[2]
|
969
|
+
]);
|
970
|
+
},
|
971
|
+
|
972
|
+
// Returns the line's reflection in the given point or line
|
973
|
+
reflectionIn: function(obj) {
|
974
|
+
if (obj.normal) {
|
975
|
+
// obj is a plane
|
976
|
+
var A = this.anchor.elements, D = this.direction.elements;
|
977
|
+
var A1 = A[0], A2 = A[1], A3 = A[2], D1 = D[0], D2 = D[1], D3 = D[2];
|
978
|
+
var newA = this.anchor.reflectionIn(obj).elements;
|
979
|
+
// Add the line's direction vector to its anchor, then mirror that in the plane
|
980
|
+
var AD1 = A1 + D1, AD2 = A2 + D2, AD3 = A3 + D3;
|
981
|
+
var Q = obj.pointClosestTo([AD1, AD2, AD3]).elements;
|
982
|
+
var newD = [Q[0] + (Q[0] - AD1) - newA[0], Q[1] + (Q[1] - AD2) - newA[1], Q[2] + (Q[2] - AD3) - newA[2]];
|
983
|
+
return Line.create(newA, newD);
|
984
|
+
} else if (obj.direction) {
|
985
|
+
// obj is a line - reflection obtained by rotating PI radians about obj
|
986
|
+
return this.rotate(Math.PI, obj);
|
987
|
+
} else {
|
988
|
+
// obj is a point - just reflect the line's anchor in it
|
989
|
+
var P = obj.elements || obj;
|
990
|
+
return Line.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.direction);
|
991
|
+
}
|
992
|
+
},
|
993
|
+
|
994
|
+
// Set the line's anchor point and direction.
|
995
|
+
setVectors: function(anchor, direction) {
|
996
|
+
// Need to do this so that line's properties are not
|
997
|
+
// references to the arguments passed in
|
998
|
+
anchor = Vector.create(anchor);
|
999
|
+
direction = Vector.create(direction);
|
1000
|
+
if (anchor.elements.length == 2) {anchor.elements.push(0); }
|
1001
|
+
if (direction.elements.length == 2) { direction.elements.push(0); }
|
1002
|
+
if (anchor.elements.length > 3 || direction.elements.length > 3) { return null; }
|
1003
|
+
var mod = direction.modulus();
|
1004
|
+
if (mod === 0) { return null; }
|
1005
|
+
this.anchor = anchor;
|
1006
|
+
this.direction = Vector.create([
|
1007
|
+
direction.elements[0] / mod,
|
1008
|
+
direction.elements[1] / mod,
|
1009
|
+
direction.elements[2] / mod
|
1010
|
+
]);
|
1011
|
+
return this;
|
1012
|
+
}
|
1013
|
+
};
|
1014
|
+
|
1015
|
+
|
1016
|
+
// Constructor function
|
1017
|
+
Line.create = function(anchor, direction) {
|
1018
|
+
var L = new Line();
|
1019
|
+
return L.setVectors(anchor, direction);
|
1020
|
+
};
|
1021
|
+
|
1022
|
+
// Axes
|
1023
|
+
Line.X = Line.create(Vector.Zero(3), Vector.i);
|
1024
|
+
Line.Y = Line.create(Vector.Zero(3), Vector.j);
|
1025
|
+
Line.Z = Line.create(Vector.Zero(3), Vector.k);
|
1026
|
+
|
1027
|
+
|
1028
|
+
|
1029
|
+
function Plane() {}
|
1030
|
+
Plane.prototype = {
|
1031
|
+
|
1032
|
+
// Returns true iff the plane occupies the same space as the argument
|
1033
|
+
eql: function(plane) {
|
1034
|
+
return (this.contains(plane.anchor) && this.isParallelTo(plane));
|
1035
|
+
},
|
1036
|
+
|
1037
|
+
// Returns a copy of the plane
|
1038
|
+
dup: function() {
|
1039
|
+
return Plane.create(this.anchor, this.normal);
|
1040
|
+
},
|
1041
|
+
|
1042
|
+
// Returns the result of translating the plane by the given vector
|
1043
|
+
translate: function(vector) {
|
1044
|
+
var V = vector.elements || vector;
|
1045
|
+
return Plane.create([
|
1046
|
+
this.anchor.elements[0] + V[0],
|
1047
|
+
this.anchor.elements[1] + V[1],
|
1048
|
+
this.anchor.elements[2] + (V[2] || 0)
|
1049
|
+
], this.normal);
|
1050
|
+
},
|
1051
|
+
|
1052
|
+
// Returns true iff the plane is parallel to the argument. Will return true
|
1053
|
+
// if the planes are equal, or if you give a line and it lies in the plane.
|
1054
|
+
isParallelTo: function(obj) {
|
1055
|
+
var theta;
|
1056
|
+
if (obj.normal) {
|
1057
|
+
// obj is a plane
|
1058
|
+
theta = this.normal.angleFrom(obj.normal);
|
1059
|
+
return (Math.abs(theta) <= Sylvester.precision || Math.abs(Math.PI - theta) <= Sylvester.precision);
|
1060
|
+
} else if (obj.direction) {
|
1061
|
+
// obj is a line
|
1062
|
+
return this.normal.isPerpendicularTo(obj.direction);
|
1063
|
+
}
|
1064
|
+
return null;
|
1065
|
+
},
|
1066
|
+
|
1067
|
+
// Returns true iff the receiver is perpendicular to the argument
|
1068
|
+
isPerpendicularTo: function(plane) {
|
1069
|
+
var theta = this.normal.angleFrom(plane.normal);
|
1070
|
+
return (Math.abs(Math.PI/2 - theta) <= Sylvester.precision);
|
1071
|
+
},
|
1072
|
+
|
1073
|
+
// Returns the plane's distance from the given object (point, line or plane)
|
1074
|
+
distanceFrom: function(obj) {
|
1075
|
+
if (this.intersects(obj) || this.contains(obj)) { return 0; }
|
1076
|
+
if (obj.anchor) {
|
1077
|
+
// obj is a plane or line
|
1078
|
+
var A = this.anchor.elements, B = obj.anchor.elements, N = this.normal.elements;
|
1079
|
+
return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
|
1080
|
+
} else {
|
1081
|
+
// obj is a point
|
1082
|
+
var P = obj.elements || obj;
|
1083
|
+
var A = this.anchor.elements, N = this.normal.elements;
|
1084
|
+
return Math.abs((A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]);
|
1085
|
+
}
|
1086
|
+
},
|
1087
|
+
|
1088
|
+
// Returns true iff the plane contains the given point or line
|
1089
|
+
contains: function(obj) {
|
1090
|
+
if (obj.normal) { return null; }
|
1091
|
+
if (obj.direction) {
|
1092
|
+
return (this.contains(obj.anchor) && this.contains(obj.anchor.add(obj.direction)));
|
1093
|
+
} else {
|
1094
|
+
var P = obj.elements || obj;
|
1095
|
+
var A = this.anchor.elements, N = this.normal.elements;
|
1096
|
+
var diff = Math.abs(N[0]*(A[0] - P[0]) + N[1]*(A[1] - P[1]) + N[2]*(A[2] - (P[2] || 0)));
|
1097
|
+
return (diff <= Sylvester.precision);
|
1098
|
+
}
|
1099
|
+
},
|
1100
|
+
|
1101
|
+
// Returns true iff the plane has a unique point/line of intersection with the argument
|
1102
|
+
intersects: function(obj) {
|
1103
|
+
if (typeof(obj.direction) == 'undefined' && typeof(obj.normal) == 'undefined') { return null; }
|
1104
|
+
return !this.isParallelTo(obj);
|
1105
|
+
},
|
1106
|
+
|
1107
|
+
// Returns the unique intersection with the argument, if one exists. The result
|
1108
|
+
// will be a vector if a line is supplied, and a line if a plane is supplied.
|
1109
|
+
intersectionWith: function(obj) {
|
1110
|
+
if (!this.intersects(obj)) { return null; }
|
1111
|
+
if (obj.direction) {
|
1112
|
+
// obj is a line
|
1113
|
+
var A = obj.anchor.elements, D = obj.direction.elements,
|
1114
|
+
P = this.anchor.elements, N = this.normal.elements;
|
1115
|
+
var multiplier = (N[0]*(P[0]-A[0]) + N[1]*(P[1]-A[1]) + N[2]*(P[2]-A[2])) / (N[0]*D[0] + N[1]*D[1] + N[2]*D[2]);
|
1116
|
+
return Vector.create([A[0] + D[0]*multiplier, A[1] + D[1]*multiplier, A[2] + D[2]*multiplier]);
|
1117
|
+
} else if (obj.normal) {
|
1118
|
+
// obj is a plane
|
1119
|
+
var direction = this.normal.cross(obj.normal).toUnitVector();
|
1120
|
+
// To find an anchor point, we find one co-ordinate that has a value
|
1121
|
+
// of zero somewhere on the intersection, and remember which one we picked
|
1122
|
+
var N = this.normal.elements, A = this.anchor.elements,
|
1123
|
+
O = obj.normal.elements, B = obj.anchor.elements;
|
1124
|
+
var solver = Matrix.Zero(2,2), i = 0;
|
1125
|
+
while (solver.isSingular()) {
|
1126
|
+
i++;
|
1127
|
+
solver = Matrix.create([
|
1128
|
+
[ N[i%3], N[(i+1)%3] ],
|
1129
|
+
[ O[i%3], O[(i+1)%3] ]
|
1130
|
+
]);
|
1131
|
+
}
|
1132
|
+
// Then we solve the simultaneous equations in the remaining dimensions
|
1133
|
+
var inverse = solver.inverse().elements;
|
1134
|
+
var x = N[0]*A[0] + N[1]*A[1] + N[2]*A[2];
|
1135
|
+
var y = O[0]*B[0] + O[1]*B[1] + O[2]*B[2];
|
1136
|
+
var intersection = [
|
1137
|
+
inverse[0][0] * x + inverse[0][1] * y,
|
1138
|
+
inverse[1][0] * x + inverse[1][1] * y
|
1139
|
+
];
|
1140
|
+
var anchor = [];
|
1141
|
+
for (var j = 1; j <= 3; j++) {
|
1142
|
+
// This formula picks the right element from intersection by
|
1143
|
+
// cycling depending on which element we set to zero above
|
1144
|
+
anchor.push((i == j) ? 0 : intersection[(j + (5 - i)%3)%3]);
|
1145
|
+
}
|
1146
|
+
return Line.create(anchor, direction);
|
1147
|
+
}
|
1148
|
+
},
|
1149
|
+
|
1150
|
+
// Returns the point in the plane closest to the given point
|
1151
|
+
pointClosestTo: function(point) {
|
1152
|
+
var P = point.elements || point;
|
1153
|
+
var A = this.anchor.elements, N = this.normal.elements;
|
1154
|
+
var dot = (A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2];
|
1155
|
+
return Vector.create([P[0] + N[0] * dot, P[1] + N[1] * dot, (P[2] || 0) + N[2] * dot]);
|
1156
|
+
},
|
1157
|
+
|
1158
|
+
// Returns a copy of the plane, rotated by t radians about the given line
|
1159
|
+
// See notes on Line#rotate.
|
1160
|
+
rotate: function(t, line) {
|
1161
|
+
var R = Matrix.Rotation(t, line.direction).elements;
|
1162
|
+
var C = line.pointClosestTo(this.anchor).elements;
|
1163
|
+
var A = this.anchor.elements, N = this.normal.elements;
|
1164
|
+
var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
|
1165
|
+
var x = A1 - C1, y = A2 - C2, z = A3 - C3;
|
1166
|
+
return Plane.create([
|
1167
|
+
C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
|
1168
|
+
C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
|
1169
|
+
C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
|
1170
|
+
], [
|
1171
|
+
R[0][0] * N[0] + R[0][1] * N[1] + R[0][2] * N[2],
|
1172
|
+
R[1][0] * N[0] + R[1][1] * N[1] + R[1][2] * N[2],
|
1173
|
+
R[2][0] * N[0] + R[2][1] * N[1] + R[2][2] * N[2]
|
1174
|
+
]);
|
1175
|
+
},
|
1176
|
+
|
1177
|
+
// Returns the reflection of the plane in the given point, line or plane.
|
1178
|
+
reflectionIn: function(obj) {
|
1179
|
+
if (obj.normal) {
|
1180
|
+
// obj is a plane
|
1181
|
+
var A = this.anchor.elements, N = this.normal.elements;
|
1182
|
+
var A1 = A[0], A2 = A[1], A3 = A[2], N1 = N[0], N2 = N[1], N3 = N[2];
|
1183
|
+
var newA = this.anchor.reflectionIn(obj).elements;
|
1184
|
+
// Add the plane's normal to its anchor, then mirror that in the other plane
|
1185
|
+
var AN1 = A1 + N1, AN2 = A2 + N2, AN3 = A3 + N3;
|
1186
|
+
var Q = obj.pointClosestTo([AN1, AN2, AN3]).elements;
|
1187
|
+
var newN = [Q[0] + (Q[0] - AN1) - newA[0], Q[1] + (Q[1] - AN2) - newA[1], Q[2] + (Q[2] - AN3) - newA[2]];
|
1188
|
+
return Plane.create(newA, newN);
|
1189
|
+
} else if (obj.direction) {
|
1190
|
+
// obj is a line
|
1191
|
+
return this.rotate(Math.PI, obj);
|
1192
|
+
} else {
|
1193
|
+
// obj is a point
|
1194
|
+
var P = obj.elements || obj;
|
1195
|
+
return Plane.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.normal);
|
1196
|
+
}
|
1197
|
+
},
|
1198
|
+
|
1199
|
+
// Sets the anchor point and normal to the plane. If three arguments are specified,
|
1200
|
+
// the normal is calculated by assuming the three points should lie in the same plane.
|
1201
|
+
// If only two are sepcified, the second is taken to be the normal. Normal vector is
|
1202
|
+
// normalised before storage.
|
1203
|
+
setVectors: function(anchor, v1, v2) {
|
1204
|
+
anchor = Vector.create(anchor);
|
1205
|
+
anchor = anchor.to3D(); if (anchor === null) { return null; }
|
1206
|
+
v1 = Vector.create(v1);
|
1207
|
+
v1 = v1.to3D(); if (v1 === null) { return null; }
|
1208
|
+
if (typeof(v2) == 'undefined') {
|
1209
|
+
v2 = null;
|
1210
|
+
} else {
|
1211
|
+
v2 = Vector.create(v2);
|
1212
|
+
v2 = v2.to3D(); if (v2 === null) { return null; }
|
1213
|
+
}
|
1214
|
+
var A1 = anchor.elements[0], A2 = anchor.elements[1], A3 = anchor.elements[2];
|
1215
|
+
var v11 = v1.elements[0], v12 = v1.elements[1], v13 = v1.elements[2];
|
1216
|
+
var normal, mod;
|
1217
|
+
if (v2 !== null) {
|
1218
|
+
var v21 = v2.elements[0], v22 = v2.elements[1], v23 = v2.elements[2];
|
1219
|
+
normal = Vector.create([
|
1220
|
+
(v12 - A2) * (v23 - A3) - (v13 - A3) * (v22 - A2),
|
1221
|
+
(v13 - A3) * (v21 - A1) - (v11 - A1) * (v23 - A3),
|
1222
|
+
(v11 - A1) * (v22 - A2) - (v12 - A2) * (v21 - A1)
|
1223
|
+
]);
|
1224
|
+
mod = normal.modulus();
|
1225
|
+
if (mod === 0) { return null; }
|
1226
|
+
normal = Vector.create([normal.elements[0] / mod, normal.elements[1] / mod, normal.elements[2] / mod]);
|
1227
|
+
} else {
|
1228
|
+
mod = Math.sqrt(v11*v11 + v12*v12 + v13*v13);
|
1229
|
+
if (mod === 0) { return null; }
|
1230
|
+
normal = Vector.create([v1.elements[0] / mod, v1.elements[1] / mod, v1.elements[2] / mod]);
|
1231
|
+
}
|
1232
|
+
this.anchor = anchor;
|
1233
|
+
this.normal = normal;
|
1234
|
+
return this;
|
1235
|
+
}
|
1236
|
+
};
|
1237
|
+
|
1238
|
+
// Constructor function
|
1239
|
+
Plane.create = function(anchor, v1, v2) {
|
1240
|
+
var P = new Plane();
|
1241
|
+
return P.setVectors(anchor, v1, v2);
|
1242
|
+
};
|
1243
|
+
|
1244
|
+
// X-Y-Z planes
|
1245
|
+
Plane.XY = Plane.create(Vector.Zero(3), Vector.k);
|
1246
|
+
Plane.YZ = Plane.create(Vector.Zero(3), Vector.i);
|
1247
|
+
Plane.ZX = Plane.create(Vector.Zero(3), Vector.j);
|
1248
|
+
Plane.YX = Plane.XY; Plane.ZY = Plane.YZ; Plane.XZ = Plane.ZX;
|
1249
|
+
|
1250
|
+
// Utility functions
|
1251
|
+
var $V = Vector.create;
|
1252
|
+
var $M = Matrix.create;
|
1253
|
+
var $L = Line.create;
|
1254
|
+
var $P = Plane.create;
|
1255
|
+
/*!
|
1256
|
+
* jQuery 2d Transform v0.9.3
|
1257
|
+
* http://wiki.github.com/heygrady/transform/
|
1258
|
+
*
|
1259
|
+
* Copyright 2010, Grady Kuhnline
|
1260
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
1261
|
+
* http://jquery.org/license
|
1262
|
+
*
|
1263
|
+
* Date: Sat Dec 4 15:46:09 2010 -0800
|
1264
|
+
*/
|
1265
|
+
///////////////////////////////////////////////////////
|
1266
|
+
// Transform
|
1267
|
+
///////////////////////////////////////////////////////
|
1268
|
+
(function($, window, document, undefined) {
|
1269
|
+
/**
|
1270
|
+
* @var Regex identify the matrix filter in IE
|
1271
|
+
*/
|
1272
|
+
var rmatrix = /progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/,
|
1273
|
+
rfxnum = /^([\+\-]=)?([\d+.\-]+)(.*)$/,
|
1274
|
+
rperc = /%/;
|
1275
|
+
|
1276
|
+
// Steal some code from Modernizr
|
1277
|
+
var m = document.createElement( 'modernizr' ),
|
1278
|
+
m_style = m.style;
|
1279
|
+
|
1280
|
+
function stripUnits(arg) {
|
1281
|
+
return parseFloat(arg);
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
/**
|
1285
|
+
* Find the prefix that this browser uses
|
1286
|
+
*/
|
1287
|
+
function getVendorPrefix() {
|
1288
|
+
var property = {
|
1289
|
+
transformProperty : '',
|
1290
|
+
MozTransform : '-moz-',
|
1291
|
+
WebkitTransform : '-webkit-',
|
1292
|
+
OTransform : '-o-',
|
1293
|
+
msTransform : '-ms-'
|
1294
|
+
};
|
1295
|
+
for (var p in property) {
|
1296
|
+
if (typeof m_style[p] != 'undefined') {
|
1297
|
+
return property[p];
|
1298
|
+
}
|
1299
|
+
}
|
1300
|
+
return null;
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
function supportCssTransforms() {
|
1304
|
+
if (typeof(window.Modernizr) !== 'undefined') {
|
1305
|
+
return Modernizr.csstransforms;
|
1306
|
+
}
|
1307
|
+
|
1308
|
+
var props = [ 'transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ];
|
1309
|
+
for ( var i in props ) {
|
1310
|
+
if ( m_style[ props[i] ] !== undefined ) {
|
1311
|
+
return true;
|
1312
|
+
}
|
1313
|
+
}
|
1314
|
+
}
|
1315
|
+
|
1316
|
+
// Capture some basic properties
|
1317
|
+
var vendorPrefix = getVendorPrefix(),
|
1318
|
+
transformProperty = vendorPrefix !== null ? vendorPrefix + 'transform' : false,
|
1319
|
+
transformOriginProperty = vendorPrefix !== null ? vendorPrefix + 'transform-origin' : false;
|
1320
|
+
|
1321
|
+
// store support in the jQuery Support object
|
1322
|
+
$.support.csstransforms = supportCssTransforms();
|
1323
|
+
|
1324
|
+
// IE9 public preview 6 requires the DOM names
|
1325
|
+
if (vendorPrefix == '-ms-') {
|
1326
|
+
transformProperty = 'msTransform';
|
1327
|
+
transformOriginProperty = 'msTransformOrigin';
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
/**
|
1331
|
+
* Class for creating cross-browser transformations
|
1332
|
+
* @constructor
|
1333
|
+
*/
|
1334
|
+
$.extend({
|
1335
|
+
transform: function(elem) {
|
1336
|
+
// Cache the transform object on the element itself
|
1337
|
+
elem.transform = this;
|
1338
|
+
|
1339
|
+
/**
|
1340
|
+
* The element we're working with
|
1341
|
+
* @var jQueryCollection
|
1342
|
+
*/
|
1343
|
+
this.$elem = $(elem);
|
1344
|
+
|
1345
|
+
/**
|
1346
|
+
* Remember the matrix we're applying to help the safeOuterLength func
|
1347
|
+
*/
|
1348
|
+
this.applyingMatrix = false;
|
1349
|
+
this.matrix = null;
|
1350
|
+
|
1351
|
+
/**
|
1352
|
+
* Remember the css height and width to save time
|
1353
|
+
* This is only really used in IE
|
1354
|
+
* @var Number
|
1355
|
+
*/
|
1356
|
+
this.height = null;
|
1357
|
+
this.width = null;
|
1358
|
+
this.outerHeight = null;
|
1359
|
+
this.outerWidth = null;
|
1360
|
+
|
1361
|
+
/**
|
1362
|
+
* We need to know the box-sizing in IE for building the outerHeight and outerWidth
|
1363
|
+
* @var string
|
1364
|
+
*/
|
1365
|
+
this.boxSizingValue = null;
|
1366
|
+
this.boxSizingProperty = null;
|
1367
|
+
|
1368
|
+
this.attr = null;
|
1369
|
+
this.transformProperty = transformProperty;
|
1370
|
+
this.transformOriginProperty = transformOriginProperty;
|
1371
|
+
}
|
1372
|
+
});
|
1373
|
+
|
1374
|
+
$.extend($.transform, {
|
1375
|
+
/**
|
1376
|
+
* @var Array list of all valid transform functions
|
1377
|
+
*/
|
1378
|
+
funcs: ['matrix', 'origin', 'reflect', 'reflectX', 'reflectXY', 'reflectY', 'rotate', 'scale', 'scaleX', 'scaleY', 'skew', 'skewX', 'skewY', 'translate', 'translateX', 'translateY']
|
1379
|
+
});
|
1380
|
+
|
1381
|
+
/**
|
1382
|
+
* Create Transform as a jQuery plugin
|
1383
|
+
* @param Object funcs
|
1384
|
+
* @param Object options
|
1385
|
+
*/
|
1386
|
+
$.fn.transform = function(funcs, options) {
|
1387
|
+
return this.each(function() {
|
1388
|
+
var t = this.transform || new $.transform(this);
|
1389
|
+
if (funcs) {
|
1390
|
+
t.exec(funcs, options);
|
1391
|
+
}
|
1392
|
+
});
|
1393
|
+
};
|
1394
|
+
|
1395
|
+
$.transform.prototype = {
|
1396
|
+
/**
|
1397
|
+
* Applies all of the transformations
|
1398
|
+
* @param Object funcs
|
1399
|
+
* @param Object options
|
1400
|
+
* forceMatrix - uses the matrix in all browsers
|
1401
|
+
* preserve - tries to preserve the values from previous runs
|
1402
|
+
*/
|
1403
|
+
exec: function(funcs, options) {
|
1404
|
+
// extend options
|
1405
|
+
options = $.extend(true, {
|
1406
|
+
forceMatrix: false,
|
1407
|
+
preserve: false
|
1408
|
+
}, options);
|
1409
|
+
|
1410
|
+
// preserve the funcs from the previous run
|
1411
|
+
this.attr = null;
|
1412
|
+
if (options.preserve) {
|
1413
|
+
funcs = $.extend(true, this.getAttrs(true, true), funcs);
|
1414
|
+
} else {
|
1415
|
+
funcs = $.extend(true, {}, funcs); // copy the object to prevent weirdness
|
1416
|
+
}
|
1417
|
+
|
1418
|
+
// Record the custom attributes on the element itself
|
1419
|
+
this.setAttrs(funcs);
|
1420
|
+
|
1421
|
+
// apply the funcs
|
1422
|
+
if ($.support.csstransforms && !options.forceMatrix) {
|
1423
|
+
// CSS3 is supported
|
1424
|
+
return this.execFuncs(funcs);
|
1425
|
+
} else if ($.browser.msie || ($.support.csstransforms && options.forceMatrix)) {
|
1426
|
+
// Internet Explorer or Forced matrix
|
1427
|
+
return this.execMatrix(funcs);
|
1428
|
+
}
|
1429
|
+
return false;
|
1430
|
+
},
|
1431
|
+
|
1432
|
+
/**
|
1433
|
+
* Applies all of the transformations as functions
|
1434
|
+
* @param Object funcs
|
1435
|
+
*/
|
1436
|
+
execFuncs: function(funcs) {
|
1437
|
+
var values = [];
|
1438
|
+
|
1439
|
+
// construct a CSS string
|
1440
|
+
for (var func in funcs) {
|
1441
|
+
// handle origin separately
|
1442
|
+
if (func == 'origin') {
|
1443
|
+
this[func].apply(this, $.isArray(funcs[func]) ? funcs[func] : [funcs[func]]);
|
1444
|
+
} else if ($.inArray(func, $.transform.funcs) !== -1) {
|
1445
|
+
values.push(this.createTransformFunc(func, funcs[func]));
|
1446
|
+
}
|
1447
|
+
}
|
1448
|
+
this.$elem.css(transformProperty, values.join(' '));
|
1449
|
+
return true;
|
1450
|
+
},
|
1451
|
+
|
1452
|
+
/**
|
1453
|
+
* Applies all of the transformations as a matrix
|
1454
|
+
* @param Object funcs
|
1455
|
+
*/
|
1456
|
+
execMatrix: function(funcs) {
|
1457
|
+
var matrix,
|
1458
|
+
tempMatrix,
|
1459
|
+
args;
|
1460
|
+
|
1461
|
+
var elem = this.$elem[0],
|
1462
|
+
_this = this;
|
1463
|
+
function normalPixels(val, i) {
|
1464
|
+
if (rperc.test(val)) {
|
1465
|
+
// this really only applies to translation
|
1466
|
+
return parseFloat(val) / 100 * _this['safeOuter' + (i ? 'Height' : 'Width')]();
|
1467
|
+
}
|
1468
|
+
return toPx(elem, val);
|
1469
|
+
}
|
1470
|
+
|
1471
|
+
var rtranslate = /translate[X|Y]?/,
|
1472
|
+
trans = [];
|
1473
|
+
|
1474
|
+
for (var func in funcs) {
|
1475
|
+
switch ($.type(funcs[func])) {
|
1476
|
+
case 'array': args = funcs[func]; break;
|
1477
|
+
case 'string': args = $.map(funcs[func].split(','), $.trim); break;
|
1478
|
+
default: args = [funcs[func]];
|
1479
|
+
}
|
1480
|
+
|
1481
|
+
if ($.matrix[func]) {
|
1482
|
+
|
1483
|
+
if ($.cssAngle[func]) {
|
1484
|
+
// normalize on degrees
|
1485
|
+
args = $.map(args, $.angle.toDegree);
|
1486
|
+
} else if (!$.cssNumber[func]) {
|
1487
|
+
// normalize to pixels
|
1488
|
+
args = $.map(args, normalPixels);
|
1489
|
+
} else {
|
1490
|
+
// strip units
|
1491
|
+
args = $.map(args, stripUnits);
|
1492
|
+
}
|
1493
|
+
|
1494
|
+
tempMatrix = $.matrix[func].apply(this, args);
|
1495
|
+
if (rtranslate.test(func)) {
|
1496
|
+
//defer translation
|
1497
|
+
trans.push(tempMatrix);
|
1498
|
+
} else {
|
1499
|
+
matrix = matrix ? matrix.x(tempMatrix) : tempMatrix;
|
1500
|
+
}
|
1501
|
+
} else if (func == 'origin') {
|
1502
|
+
this[func].apply(this, args);
|
1503
|
+
}
|
1504
|
+
}
|
1505
|
+
|
1506
|
+
// check that we have a matrix
|
1507
|
+
matrix = matrix || $.matrix.identity();
|
1508
|
+
|
1509
|
+
// Apply translation
|
1510
|
+
$.each(trans, function(i, val) { matrix = matrix.x(val); });
|
1511
|
+
|
1512
|
+
// pull out the relevant values
|
1513
|
+
var a = parseFloat(matrix.e(1,1).toFixed(6)),
|
1514
|
+
b = parseFloat(matrix.e(2,1).toFixed(6)),
|
1515
|
+
c = parseFloat(matrix.e(1,2).toFixed(6)),
|
1516
|
+
d = parseFloat(matrix.e(2,2).toFixed(6)),
|
1517
|
+
tx = matrix.rows === 3 ? parseFloat(matrix.e(1,3).toFixed(6)) : 0,
|
1518
|
+
ty = matrix.rows === 3 ? parseFloat(matrix.e(2,3).toFixed(6)) : 0;
|
1519
|
+
|
1520
|
+
//apply the transform to the element
|
1521
|
+
if ($.support.csstransforms && vendorPrefix === '-moz-') {
|
1522
|
+
// -moz-
|
1523
|
+
this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + 'px, ' + ty + 'px)');
|
1524
|
+
} else if ($.support.csstransforms) {
|
1525
|
+
// -webkit, -o-, w3c
|
1526
|
+
// NOTE: WebKit and Opera don't allow units on the translate variables
|
1527
|
+
this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
|
1528
|
+
} else if ($.browser.msie) {
|
1529
|
+
// IE requires the special transform Filter
|
1530
|
+
|
1531
|
+
//TODO: Use Nearest Neighbor during animation FilterType=\'nearest neighbor\'
|
1532
|
+
var filterType = ', FilterType=\'nearest neighbor\''; //bilinear
|
1533
|
+
var style = this.$elem[0].style;
|
1534
|
+
var matrixFilter = 'progid:DXImageTransform.Microsoft.Matrix(' +
|
1535
|
+
'M11=' + a + ', M12=' + c + ', M21=' + b + ', M22=' + d +
|
1536
|
+
', sizingMethod=\'auto expand\'' + filterType + ')';
|
1537
|
+
var filter = style.filter || $.curCSS( this.$elem[0], "filter" ) || "";
|
1538
|
+
style.filter = rmatrix.test(filter) ? filter.replace(rmatrix, matrixFilter) : filter ? filter + ' ' + matrixFilter : matrixFilter;
|
1539
|
+
|
1540
|
+
// Let's know that we're applying post matrix fixes and the height/width will be static for a bit
|
1541
|
+
this.applyingMatrix = true;
|
1542
|
+
this.matrix = matrix;
|
1543
|
+
|
1544
|
+
// IE can't set the origin or translate directly
|
1545
|
+
this.fixPosition(matrix, tx, ty);
|
1546
|
+
|
1547
|
+
this.applyingMatrix = false;
|
1548
|
+
this.matrix = null;
|
1549
|
+
}
|
1550
|
+
return true;
|
1551
|
+
},
|
1552
|
+
|
1553
|
+
/**
|
1554
|
+
* Sets the transform-origin
|
1555
|
+
* This really needs to be percentages
|
1556
|
+
* @param Number x length
|
1557
|
+
* @param Number y length
|
1558
|
+
*/
|
1559
|
+
origin: function(x, y) {
|
1560
|
+
// use CSS in supported browsers
|
1561
|
+
if ($.support.csstransforms) {
|
1562
|
+
if (typeof y === 'undefined') {
|
1563
|
+
this.$elem.css(transformOriginProperty, x);
|
1564
|
+
} else {
|
1565
|
+
this.$elem.css(transformOriginProperty, x + ' ' + y);
|
1566
|
+
}
|
1567
|
+
return true;
|
1568
|
+
}
|
1569
|
+
|
1570
|
+
// correct for keyword lengths
|
1571
|
+
switch (x) {
|
1572
|
+
case 'left': x = '0'; break;
|
1573
|
+
case 'right': x = '100%'; break;
|
1574
|
+
case 'center': // no break
|
1575
|
+
case undefined: x = '50%';
|
1576
|
+
}
|
1577
|
+
switch (y) {
|
1578
|
+
case 'top': y = '0'; break;
|
1579
|
+
case 'bottom': y = '100%'; break;
|
1580
|
+
case 'center': // no break
|
1581
|
+
case undefined: y = '50%'; //TODO: does this work?
|
1582
|
+
}
|
1583
|
+
|
1584
|
+
// store mixed values with units, assumed pixels
|
1585
|
+
this.setAttr('origin', [
|
1586
|
+
rperc.test(x) ? x : toPx(this.$elem[0], x) + 'px',
|
1587
|
+
rperc.test(y) ? y : toPx(this.$elem[0], y) + 'px'
|
1588
|
+
]);
|
1589
|
+
//console.log(this.getAttr('origin'));
|
1590
|
+
return true;
|
1591
|
+
},
|
1592
|
+
|
1593
|
+
/**
|
1594
|
+
* Create a function suitable for a CSS value
|
1595
|
+
* @param string func
|
1596
|
+
* @param Mixed value
|
1597
|
+
*/
|
1598
|
+
createTransformFunc: function(func, value) {
|
1599
|
+
if (func.substr(0, 7) === 'reflect') {
|
1600
|
+
// let's fake reflection, false value
|
1601
|
+
// falsey sets an identity matrix
|
1602
|
+
var m = value ? $.matrix[func]() : $.matrix.identity();
|
1603
|
+
return 'matrix(' + m.e(1,1) + ', ' + m.e(2,1) + ', ' + m.e(1,2) + ', ' + m.e(2,2) + ', 0, 0)';
|
1604
|
+
}
|
1605
|
+
|
1606
|
+
//value = _correctUnits(func, value);
|
1607
|
+
|
1608
|
+
if (func == 'matrix') {
|
1609
|
+
if (vendorPrefix === '-moz-') {
|
1610
|
+
value[4] = value[4] ? value[4] + 'px' : 0;
|
1611
|
+
value[5] = value[5] ? value[5] + 'px' : 0;
|
1612
|
+
}
|
1613
|
+
}
|
1614
|
+
return func + '(' + ($.isArray(value) ? value.join(', ') : value) + ')';
|
1615
|
+
},
|
1616
|
+
|
1617
|
+
/**
|
1618
|
+
* @param Matrix matrix
|
1619
|
+
* @param Number tx
|
1620
|
+
* @param Number ty
|
1621
|
+
* @param Number height
|
1622
|
+
* @param Number width
|
1623
|
+
*/
|
1624
|
+
fixPosition: function(matrix, tx, ty, height, width) {
|
1625
|
+
// now we need to fix it!
|
1626
|
+
var calc = new $.matrix.calc(matrix, this.safeOuterHeight(), this.safeOuterWidth()),
|
1627
|
+
origin = this.getAttr('origin'); // mixed percentages and px
|
1628
|
+
|
1629
|
+
// translate a 0, 0 origin to the current origin
|
1630
|
+
var offset = calc.originOffset(new $.matrix.V2(
|
1631
|
+
rperc.test(origin[0]) ? parseFloat(origin[0])/100*calc.outerWidth : parseFloat(origin[0]),
|
1632
|
+
rperc.test(origin[1]) ? parseFloat(origin[1])/100*calc.outerHeight : parseFloat(origin[1])
|
1633
|
+
));
|
1634
|
+
|
1635
|
+
// IE glues the top-most and left-most pixels of the transformed object to top/left of the original object
|
1636
|
+
//TODO: This seems wrong in the calculations
|
1637
|
+
var sides = calc.sides();
|
1638
|
+
|
1639
|
+
// Protect against an item that is already positioned
|
1640
|
+
var cssPosition = this.$elem.css('position');
|
1641
|
+
if (cssPosition == 'static') {
|
1642
|
+
cssPosition = 'relative';
|
1643
|
+
}
|
1644
|
+
|
1645
|
+
//TODO: if the element is already positioned, we should attempt to respect it (somehow)
|
1646
|
+
//NOTE: we could preserve our offset top and left in an attr on the elem
|
1647
|
+
var pos = {top: 0, left: 0};
|
1648
|
+
|
1649
|
+
// Approximates transform-origin, tx, and ty
|
1650
|
+
var css = {
|
1651
|
+
'position': cssPosition,
|
1652
|
+
'top': (offset.top + ty + sides.top + pos.top) + 'px',
|
1653
|
+
'left': (offset.left + tx + sides.left + pos.left) + 'px',
|
1654
|
+
'zoom': 1
|
1655
|
+
};
|
1656
|
+
|
1657
|
+
this.$elem.css(css);
|
1658
|
+
}
|
1659
|
+
};
|
1660
|
+
|
1661
|
+
/**
|
1662
|
+
* Ensure that values have the appropriate units on them
|
1663
|
+
* @param string func
|
1664
|
+
* @param Mixed value
|
1665
|
+
*/
|
1666
|
+
function toPx(elem, val) {
|
1667
|
+
var parts = rfxnum.exec($.trim(val));
|
1668
|
+
|
1669
|
+
if (parts[3] && parts[3] !== 'px') {
|
1670
|
+
var prop = 'paddingBottom',
|
1671
|
+
orig = $.style( elem, prop );
|
1672
|
+
|
1673
|
+
$.style( elem, prop, val );
|
1674
|
+
val = cur( elem, prop );
|
1675
|
+
$.style( elem, prop, orig );
|
1676
|
+
return val;
|
1677
|
+
}
|
1678
|
+
return parseFloat( val );
|
1679
|
+
}
|
1680
|
+
|
1681
|
+
function cur(elem, prop) {
|
1682
|
+
if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
|
1683
|
+
return elem[ prop ];
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
var r = parseFloat( $.css( elem, prop ) );
|
1687
|
+
return r && r > -10000 ? r : 0;
|
1688
|
+
}
|
1689
|
+
})(jQuery, this, this.document);
|
1690
|
+
|
1691
|
+
|
1692
|
+
///////////////////////////////////////////////////////
|
1693
|
+
// Safe Outer Length
|
1694
|
+
///////////////////////////////////////////////////////
|
1695
|
+
(function($, window, document, undefined) {
|
1696
|
+
$.extend($.transform.prototype, {
|
1697
|
+
/**
|
1698
|
+
* @param void
|
1699
|
+
* @return Number
|
1700
|
+
*/
|
1701
|
+
safeOuterHeight: function() {
|
1702
|
+
return this.safeOuterLength('height');
|
1703
|
+
},
|
1704
|
+
|
1705
|
+
/**
|
1706
|
+
* @param void
|
1707
|
+
* @return Number
|
1708
|
+
*/
|
1709
|
+
safeOuterWidth: function() {
|
1710
|
+
return this.safeOuterLength('width');
|
1711
|
+
},
|
1712
|
+
|
1713
|
+
/**
|
1714
|
+
* Returns reliable outer dimensions for an object that may have been transformed.
|
1715
|
+
* Only use this if the matrix isn't handy
|
1716
|
+
* @param String dim height or width
|
1717
|
+
* @return Number
|
1718
|
+
*/
|
1719
|
+
safeOuterLength: function(dim) {
|
1720
|
+
var funcName = 'outer' + (dim == 'width' ? 'Width' : 'Height');
|
1721
|
+
|
1722
|
+
if (!$.support.csstransforms && $.browser.msie) {
|
1723
|
+
// make the variables more generic
|
1724
|
+
dim = dim == 'width' ? 'width' : 'height';
|
1725
|
+
|
1726
|
+
// if we're transforming and have a matrix; we can shortcut.
|
1727
|
+
// the true outerHeight is the transformed outerHeight divided by the ratio.
|
1728
|
+
// the ratio is equal to the height of a 1px by 1px box that has been transformed by the same matrix.
|
1729
|
+
if (this.applyingMatrix && !this[funcName] && this.matrix) {
|
1730
|
+
// calculate and return the correct size
|
1731
|
+
var calc = new $.matrix.calc(this.matrix, 1, 1),
|
1732
|
+
ratio = calc.offset(),
|
1733
|
+
length = this.$elem[funcName]() / ratio[dim];
|
1734
|
+
this[funcName] = length;
|
1735
|
+
|
1736
|
+
return length;
|
1737
|
+
} else if (this.applyingMatrix && this[funcName]) {
|
1738
|
+
// return the cached calculation
|
1739
|
+
return this[funcName];
|
1740
|
+
}
|
1741
|
+
|
1742
|
+
// map dimensions to box sides
|
1743
|
+
var side = {
|
1744
|
+
height: ['top', 'bottom'],
|
1745
|
+
width: ['left', 'right']
|
1746
|
+
};
|
1747
|
+
|
1748
|
+
// setup some variables
|
1749
|
+
var elem = this.$elem[0],
|
1750
|
+
outerLen = parseFloat($.curCSS(elem, dim, true)), //TODO: this can be cached on animations that do not animate height/width
|
1751
|
+
boxSizingProp = this.boxSizingProperty,
|
1752
|
+
boxSizingValue = this.boxSizingValue;
|
1753
|
+
|
1754
|
+
// IE6 && IE7 will never have a box-sizing property, so fake it
|
1755
|
+
if (!this.boxSizingProperty) {
|
1756
|
+
boxSizingProp = this.boxSizingProperty = _findBoxSizingProperty() || 'box-sizing';
|
1757
|
+
boxSizingValue = this.boxSizingValue = this.$elem.css(boxSizingProp) || 'content-box';
|
1758
|
+
}
|
1759
|
+
|
1760
|
+
// return it immediately if we already know it
|
1761
|
+
if (this[funcName] && this[dim] == outerLen) {
|
1762
|
+
return this[funcName];
|
1763
|
+
} else {
|
1764
|
+
this[dim] = outerLen;
|
1765
|
+
}
|
1766
|
+
|
1767
|
+
// add in the padding and border
|
1768
|
+
if (boxSizingProp && (boxSizingValue == 'padding-box' || boxSizingValue == 'content-box')) {
|
1769
|
+
outerLen += parseFloat($.curCSS(elem, 'padding-' + side[dim][0], true)) || 0 +
|
1770
|
+
parseFloat($.curCSS(elem, 'padding-' + side[dim][1], true)) || 0;
|
1771
|
+
}
|
1772
|
+
if (boxSizingProp && boxSizingValue == 'content-box') {
|
1773
|
+
outerLen += parseFloat($.curCSS(elem, 'border-' + side[dim][0] + '-width', true)) || 0 +
|
1774
|
+
parseFloat($.curCSS(elem, 'border-' + side[dim][1] + '-width', true)) || 0;
|
1775
|
+
}
|
1776
|
+
|
1777
|
+
// remember and return the outerHeight
|
1778
|
+
this[funcName] = outerLen;
|
1779
|
+
return outerLen;
|
1780
|
+
}
|
1781
|
+
return this.$elem[funcName]();
|
1782
|
+
}
|
1783
|
+
});
|
1784
|
+
|
1785
|
+
/**
|
1786
|
+
* Determine the correct property for checking the box-sizing property
|
1787
|
+
* @param void
|
1788
|
+
* @return string
|
1789
|
+
*/
|
1790
|
+
var _boxSizingProperty = null;
|
1791
|
+
function _findBoxSizingProperty () {
|
1792
|
+
if (_boxSizingProperty) {
|
1793
|
+
return _boxSizingProperty;
|
1794
|
+
}
|
1795
|
+
|
1796
|
+
var property = {
|
1797
|
+
boxSizing : 'box-sizing',
|
1798
|
+
MozBoxSizing : '-moz-box-sizing',
|
1799
|
+
WebkitBoxSizing : '-webkit-box-sizing',
|
1800
|
+
OBoxSizing : '-o-box-sizing'
|
1801
|
+
},
|
1802
|
+
elem = document.body;
|
1803
|
+
|
1804
|
+
for (var p in property) {
|
1805
|
+
if (typeof elem.style[p] != 'undefined') {
|
1806
|
+
_boxSizingProperty = property[p];
|
1807
|
+
return _boxSizingProperty;
|
1808
|
+
}
|
1809
|
+
}
|
1810
|
+
return null;
|
1811
|
+
}
|
1812
|
+
})(jQuery, this, this.document);
|
1813
|
+
|
1814
|
+
|
1815
|
+
///////////////////////////////////////////////////////
|
1816
|
+
// Attr
|
1817
|
+
///////////////////////////////////////////////////////
|
1818
|
+
(function($, window, document, undefined) {
|
1819
|
+
var rfuncvalue = /([\w\-]*?)\((.*?)\)/g, // with values
|
1820
|
+
attr = 'data-transform',
|
1821
|
+
rspace = /\s/,
|
1822
|
+
rcspace = /,\s?/;
|
1823
|
+
|
1824
|
+
$.extend($.transform.prototype, {
|
1825
|
+
/**
|
1826
|
+
* This overrides all of the attributes
|
1827
|
+
* @param Object funcs a list of transform functions to store on this element
|
1828
|
+
* @return void
|
1829
|
+
*/
|
1830
|
+
setAttrs: function(funcs) {
|
1831
|
+
var string = '',
|
1832
|
+
value;
|
1833
|
+
for (var func in funcs) {
|
1834
|
+
value = funcs[func];
|
1835
|
+
if ($.isArray(value)) {
|
1836
|
+
value = value.join(', ');
|
1837
|
+
}
|
1838
|
+
string += ' ' + func + '(' + value + ')';
|
1839
|
+
}
|
1840
|
+
this.attr = $.trim(string);
|
1841
|
+
this.$elem.attr(attr, this.attr);
|
1842
|
+
},
|
1843
|
+
|
1844
|
+
/**
|
1845
|
+
* This sets only a specific atribute
|
1846
|
+
* @param string func name of a transform function
|
1847
|
+
* @param mixed value with proper units
|
1848
|
+
* @return void
|
1849
|
+
*/
|
1850
|
+
setAttr: function(func, value) {
|
1851
|
+
// stringify the value
|
1852
|
+
if ($.isArray(value)) {
|
1853
|
+
value = value.join(', ');
|
1854
|
+
}
|
1855
|
+
|
1856
|
+
// pull from a local variable to look it up
|
1857
|
+
var transform = this.attr || this.$elem.attr(attr);
|
1858
|
+
if (!transform || transform.indexOf(func) == -1) {
|
1859
|
+
// we don't have any existing values, save it
|
1860
|
+
// we don't have this function yet, save it
|
1861
|
+
this.attr = $.trim(transform + ' ' + func + '(' + value + ')');
|
1862
|
+
this.$elem.attr(attr, this.attr);
|
1863
|
+
} else {
|
1864
|
+
// replace the existing value
|
1865
|
+
var funcs = [], parts;
|
1866
|
+
|
1867
|
+
// regex split
|
1868
|
+
rfuncvalue.lastIndex = 0; // reset the regex pointer
|
1869
|
+
while (parts = rfuncvalue.exec(transform)) {
|
1870
|
+
if (func == parts[1]) {
|
1871
|
+
funcs.push(func + '(' + value + ')');
|
1872
|
+
} else {
|
1873
|
+
funcs.push(parts[0]);
|
1874
|
+
}
|
1875
|
+
}
|
1876
|
+
this.attr = funcs.join(' ');
|
1877
|
+
this.$elem.attr(attr, this.attr);
|
1878
|
+
}
|
1879
|
+
},
|
1880
|
+
|
1881
|
+
/**
|
1882
|
+
* @return Object
|
1883
|
+
*/
|
1884
|
+
getAttrs: function() {
|
1885
|
+
var transform = this.attr || this.$elem.attr(attr);
|
1886
|
+
if (!transform) {
|
1887
|
+
// We don't have any existing values, return empty object
|
1888
|
+
return {};
|
1889
|
+
}
|
1890
|
+
|
1891
|
+
// replace the existing value
|
1892
|
+
var attrs = {}, parts, value;
|
1893
|
+
|
1894
|
+
rfuncvalue.lastIndex = 0; // reset the regex pointer
|
1895
|
+
while ((parts = rfuncvalue.exec(transform)) !== null) {
|
1896
|
+
if (parts) {
|
1897
|
+
value = parts[2].split(rcspace);
|
1898
|
+
attrs[parts[1]] = value.length == 1 ? value[0] : value;
|
1899
|
+
}
|
1900
|
+
}
|
1901
|
+
return attrs;
|
1902
|
+
},
|
1903
|
+
|
1904
|
+
/**
|
1905
|
+
* @param String func
|
1906
|
+
* @return mixed
|
1907
|
+
*/
|
1908
|
+
getAttr: function(func) {
|
1909
|
+
var attrs = this.getAttrs();
|
1910
|
+
if (typeof attrs[func] !== 'undefined') {
|
1911
|
+
return attrs[func];
|
1912
|
+
}
|
1913
|
+
|
1914
|
+
//TODO: move the origin to a function
|
1915
|
+
if (func === 'origin' && $.support.csstransforms) {
|
1916
|
+
// supported browsers return percentages always
|
1917
|
+
return this.$elem.css(this.transformOriginProperty).split(rspace);
|
1918
|
+
} else if (func === 'origin') {
|
1919
|
+
// just force IE to also return a percentage
|
1920
|
+
return ['50%', '50%'];
|
1921
|
+
}
|
1922
|
+
|
1923
|
+
return $.cssDefault[func] || 0;
|
1924
|
+
}
|
1925
|
+
});
|
1926
|
+
|
1927
|
+
// Define
|
1928
|
+
if (typeof($.cssAngle) == 'undefined') {
|
1929
|
+
$.cssAngle = {};
|
1930
|
+
}
|
1931
|
+
$.extend($.cssAngle, {
|
1932
|
+
rotate: true,
|
1933
|
+
skew: true,
|
1934
|
+
skewX: true,
|
1935
|
+
skewY: true
|
1936
|
+
});
|
1937
|
+
|
1938
|
+
// Define default values
|
1939
|
+
if (typeof($.cssDefault) == 'undefined') {
|
1940
|
+
$.cssDefault = {};
|
1941
|
+
}
|
1942
|
+
|
1943
|
+
$.extend($.cssDefault, {
|
1944
|
+
scale: [1, 1],
|
1945
|
+
scaleX: 1,
|
1946
|
+
scaleY: 1,
|
1947
|
+
matrix: [1, 0, 0, 1, 0, 0],
|
1948
|
+
origin: ['50%', '50%'], // TODO: allow this to be a function, like get
|
1949
|
+
reflect: [1, 0, 0, 1, 0, 0],
|
1950
|
+
reflectX: [1, 0, 0, 1, 0, 0],
|
1951
|
+
reflectXY: [1, 0, 0, 1, 0, 0],
|
1952
|
+
reflectY: [1, 0, 0, 1, 0, 0]
|
1953
|
+
});
|
1954
|
+
|
1955
|
+
// Define functons with multiple values
|
1956
|
+
if (typeof($.cssMultipleValues) == 'undefined') {
|
1957
|
+
$.cssMultipleValues = {};
|
1958
|
+
}
|
1959
|
+
$.extend($.cssMultipleValues, {
|
1960
|
+
matrix: 6,
|
1961
|
+
origin: {
|
1962
|
+
length: 2,
|
1963
|
+
duplicate: true
|
1964
|
+
},
|
1965
|
+
reflect: 6,
|
1966
|
+
reflectX: 6,
|
1967
|
+
reflectXY: 6,
|
1968
|
+
reflectY: 6,
|
1969
|
+
scale: {
|
1970
|
+
length: 2,
|
1971
|
+
duplicate: true
|
1972
|
+
},
|
1973
|
+
skew: 2,
|
1974
|
+
translate: 2
|
1975
|
+
});
|
1976
|
+
|
1977
|
+
// specify unitless funcs
|
1978
|
+
$.extend($.cssNumber, {
|
1979
|
+
matrix: true,
|
1980
|
+
reflect: true,
|
1981
|
+
reflectX: true,
|
1982
|
+
reflectXY: true,
|
1983
|
+
reflectY: true,
|
1984
|
+
scale: true,
|
1985
|
+
scaleX: true,
|
1986
|
+
scaleY: true
|
1987
|
+
});
|
1988
|
+
|
1989
|
+
// override all of the css functions
|
1990
|
+
$.each($.transform.funcs, function(i, func) {
|
1991
|
+
$.cssHooks[func] = {
|
1992
|
+
set: function(elem, value) {
|
1993
|
+
var transform = elem.transform || new $.transform(elem),
|
1994
|
+
funcs = {};
|
1995
|
+
funcs[func] = value;
|
1996
|
+
transform.exec(funcs, {preserve: true});
|
1997
|
+
},
|
1998
|
+
get: function(elem, computed) {
|
1999
|
+
var transform = elem.transform || new $.transform(elem);
|
2000
|
+
return transform.getAttr(func);
|
2001
|
+
}
|
2002
|
+
};
|
2003
|
+
});
|
2004
|
+
|
2005
|
+
// Support Reflection animation better by returning a matrix
|
2006
|
+
$.each(['reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
|
2007
|
+
$.cssHooks[func].get = function(elem, computed) {
|
2008
|
+
var transform = elem.transform || new $.transform(elem);
|
2009
|
+
return transform.getAttr('matrix') || $.cssDefault[func];
|
2010
|
+
};
|
2011
|
+
});
|
2012
|
+
})(jQuery, this, this.document);
|
2013
|
+
///////////////////////////////////////////////////////
|
2014
|
+
// Animation
|
2015
|
+
///////////////////////////////////////////////////////
|
2016
|
+
(function($, window, document, undefined) {
|
2017
|
+
/**
|
2018
|
+
* @var Regex looks for units on a string
|
2019
|
+
*/
|
2020
|
+
var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
|
2021
|
+
|
2022
|
+
/**
|
2023
|
+
* Doctors prop values in the event that they contain spaces
|
2024
|
+
* @param Object prop
|
2025
|
+
* @param String speed
|
2026
|
+
* @param String easing
|
2027
|
+
* @param Function callback
|
2028
|
+
* @return bool
|
2029
|
+
*/
|
2030
|
+
var _animate = $.fn.animate;
|
2031
|
+
$.fn.animate = function( prop, speed, easing, callback ) {
|
2032
|
+
var optall = $.speed(speed, easing, callback),
|
2033
|
+
mv = $.cssMultipleValues;
|
2034
|
+
|
2035
|
+
// Speed always creates a complete function that must be reset
|
2036
|
+
optall.complete = optall.old;
|
2037
|
+
|
2038
|
+
// Capture multiple values
|
2039
|
+
if (!$.isEmptyObject(prop)) {
|
2040
|
+
if (typeof optall.original === 'undefined') {
|
2041
|
+
optall.original = {};
|
2042
|
+
}
|
2043
|
+
$.each( prop, function( name, val ) {
|
2044
|
+
if (mv[name]
|
2045
|
+
|| $.cssAngle[name]
|
2046
|
+
|| (!$.cssNumber[name] && $.inArray(name, $.transform.funcs) !== -1)) {
|
2047
|
+
|
2048
|
+
// Handle special easing
|
2049
|
+
var specialEasing = null;
|
2050
|
+
if (jQuery.isArray(prop[name])) {
|
2051
|
+
var mvlen = 1, len = val.length;
|
2052
|
+
if (mv[name]) {
|
2053
|
+
mvlen = (typeof mv[name].length === 'undefined' ? mv[name] : mv[name].length);
|
2054
|
+
}
|
2055
|
+
if ( len > mvlen
|
2056
|
+
|| (len < mvlen && len == 2)
|
2057
|
+
|| (len == 2 && mvlen == 2 && isNaN(parseFloat(val[len - 1])))) {
|
2058
|
+
|
2059
|
+
specialEasing = val[len - 1];
|
2060
|
+
val.splice(len - 1, 1);
|
2061
|
+
}
|
2062
|
+
}
|
2063
|
+
|
2064
|
+
// Store the original values onto the optall
|
2065
|
+
optall.original[name] = val.toString();
|
2066
|
+
|
2067
|
+
// reduce to a unitless number (to trick animate)
|
2068
|
+
prop[name] = parseFloat(val);
|
2069
|
+
}
|
2070
|
+
} );
|
2071
|
+
}
|
2072
|
+
|
2073
|
+
//NOTE: we edited prop above to trick animate
|
2074
|
+
//NOTE: we pre-convert to an optall so we can doctor it
|
2075
|
+
return _animate.apply(this, [arguments[0], optall]);
|
2076
|
+
};
|
2077
|
+
|
2078
|
+
var prop = 'paddingBottom';
|
2079
|
+
function cur(elem, prop) {
|
2080
|
+
if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
|
2081
|
+
//return elem[ prop ];
|
2082
|
+
}
|
2083
|
+
|
2084
|
+
var r = parseFloat( $.css( elem, prop ) );
|
2085
|
+
return r && r > -10000 ? r : 0;
|
2086
|
+
}
|
2087
|
+
|
2088
|
+
var _custom = $.fx.prototype.custom;
|
2089
|
+
$.fx.prototype.custom = function(from, to, unit) {
|
2090
|
+
var multiple = $.cssMultipleValues[this.prop],
|
2091
|
+
angle = $.cssAngle[this.prop];
|
2092
|
+
|
2093
|
+
//TODO: simply check for the existence of CSS Hooks?
|
2094
|
+
if (multiple || (!$.cssNumber[this.prop] && $.inArray(this.prop, $.transform.funcs) !== -1)) {
|
2095
|
+
this.values = [];
|
2096
|
+
|
2097
|
+
if (!multiple) {
|
2098
|
+
multiple = 1;
|
2099
|
+
}
|
2100
|
+
|
2101
|
+
// Pull out the known values
|
2102
|
+
var values = this.options.original[this.prop],
|
2103
|
+
currentValues = $(this.elem).css(this.prop),
|
2104
|
+
defaultValues = $.cssDefault[this.prop] || 0;
|
2105
|
+
|
2106
|
+
// make sure the current css value is an array
|
2107
|
+
if (!$.isArray(currentValues)) {
|
2108
|
+
currentValues = [currentValues];
|
2109
|
+
}
|
2110
|
+
|
2111
|
+
// make sure the new values are an array
|
2112
|
+
if (!$.isArray(values)) {
|
2113
|
+
if ($.type(values) === 'string') {
|
2114
|
+
values = values.split(',');
|
2115
|
+
} else {
|
2116
|
+
values = [values];
|
2117
|
+
}
|
2118
|
+
}
|
2119
|
+
|
2120
|
+
// make sure we have enough new values
|
2121
|
+
var length = multiple.length || multiple, i = 0;
|
2122
|
+
while (values.length < length) {
|
2123
|
+
values.push(multiple.duplicate ? values[0] : defaultValues[i] || 0);
|
2124
|
+
i++;
|
2125
|
+
}
|
2126
|
+
|
2127
|
+
// calculate a start, end and unit for each new value
|
2128
|
+
var start, parts, end, //unit,
|
2129
|
+
fx = this,
|
2130
|
+
transform = fx.elem.transform;
|
2131
|
+
orig = $.style(fx.elem, prop);
|
2132
|
+
|
2133
|
+
$.each(values, function(i, val) {
|
2134
|
+
// find a sensible start value
|
2135
|
+
if (currentValues[i]) {
|
2136
|
+
start = currentValues[i];
|
2137
|
+
} else if (defaultValues[i] && !multiple.duplicate) {
|
2138
|
+
start = defaultValues[i];
|
2139
|
+
} else if (multiple.duplicate) {
|
2140
|
+
start = currentValues[0];
|
2141
|
+
} else {
|
2142
|
+
start = 0;
|
2143
|
+
}
|
2144
|
+
|
2145
|
+
// Force the correct unit on the start
|
2146
|
+
if (angle) {
|
2147
|
+
start = $.angle.toDegree(start);
|
2148
|
+
} else if (!$.cssNumber[fx.prop]) {
|
2149
|
+
parts = rfxnum.exec($.trim(start));
|
2150
|
+
if (parts[3] && parts[3] !== 'px') {
|
2151
|
+
if (parts[3] === '%') {
|
2152
|
+
start = parseFloat( parts[2] ) / 100 * transform['safeOuter' + (i ? 'Height' : 'Width')]();
|
2153
|
+
} else {
|
2154
|
+
$.style( fx.elem, prop, start);
|
2155
|
+
start = cur(fx.elem, prop);
|
2156
|
+
$.style( fx.elem, prop, orig);
|
2157
|
+
}
|
2158
|
+
}
|
2159
|
+
}
|
2160
|
+
start = parseFloat(start);
|
2161
|
+
|
2162
|
+
// parse the value with a regex
|
2163
|
+
parts = rfxnum.exec($.trim(val));
|
2164
|
+
|
2165
|
+
if (parts) {
|
2166
|
+
// we found a sensible value and unit
|
2167
|
+
end = parseFloat( parts[2] );
|
2168
|
+
unit = parts[3] || "px"; //TODO: change to an appropriate default unit
|
2169
|
+
|
2170
|
+
if (angle) {
|
2171
|
+
end = $.angle.toDegree(end + unit);
|
2172
|
+
unit = 'deg';
|
2173
|
+
} else if (!$.cssNumber[fx.prop] && unit === '%') {
|
2174
|
+
start = (start / transform['safeOuter' + (i ? 'Height' : 'Width')]()) * 100;
|
2175
|
+
} else if (!$.cssNumber[fx.prop] && unit !== 'px') {
|
2176
|
+
$.style( fx.elem, prop, (end || 1) + unit);
|
2177
|
+
start = ((end || 1) / cur(fx.elem, prop)) * start;
|
2178
|
+
$.style( fx.elem, prop, orig);
|
2179
|
+
}
|
2180
|
+
|
2181
|
+
// If a +=/-= token was provided, we're doing a relative animation
|
2182
|
+
if (parts[1]) {
|
2183
|
+
end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
|
2184
|
+
}
|
2185
|
+
} else {
|
2186
|
+
// I don't know when this would happen
|
2187
|
+
end = val;
|
2188
|
+
unit = '';
|
2189
|
+
}
|
2190
|
+
|
2191
|
+
// Save the values
|
2192
|
+
fx.values.push({
|
2193
|
+
start: start,
|
2194
|
+
end: end,
|
2195
|
+
unit: unit
|
2196
|
+
});
|
2197
|
+
});
|
2198
|
+
}
|
2199
|
+
return _custom.apply(this, arguments);
|
2200
|
+
};
|
2201
|
+
|
2202
|
+
/**
|
2203
|
+
* Animates a multi value attribute
|
2204
|
+
* @param Object fx
|
2205
|
+
* @return null
|
2206
|
+
*/
|
2207
|
+
$.fx.multipleValueStep = {
|
2208
|
+
_default: function(fx) {
|
2209
|
+
$.each(fx.values, function(i, val) {
|
2210
|
+
fx.values[i].now = val.start + ((val.end - val.start) * fx.pos);
|
2211
|
+
});
|
2212
|
+
}
|
2213
|
+
};
|
2214
|
+
$.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
|
2215
|
+
$.fx.multipleValueStep[func] = function(fx) {
|
2216
|
+
var d = fx.decomposed,
|
2217
|
+
$m = $.matrix;
|
2218
|
+
m = $m.identity();
|
2219
|
+
|
2220
|
+
d.now = {};
|
2221
|
+
|
2222
|
+
// increment each part of the decomposition and recompose it
|
2223
|
+
$.each(d.start, function(k) {
|
2224
|
+
// calculate the current value
|
2225
|
+
d.now[k] = parseFloat(d.start[k]) + ((parseFloat(d.end[k]) - parseFloat(d.start[k])) * fx.pos);
|
2226
|
+
|
2227
|
+
// skip functions that won't affect the transform
|
2228
|
+
if (((k === 'scaleX' || k === 'scaleY') && d.now[k] === 1) ||
|
2229
|
+
(k !== 'scaleX' && k !== 'scaleY' && d.now[k] === 0)) {
|
2230
|
+
return true;
|
2231
|
+
}
|
2232
|
+
|
2233
|
+
// calculating
|
2234
|
+
m = m.x($m[k](d.now[k]));
|
2235
|
+
});
|
2236
|
+
|
2237
|
+
// save the correct matrix values for the value of now
|
2238
|
+
var val;
|
2239
|
+
$.each(fx.values, function(i) {
|
2240
|
+
switch (i) {
|
2241
|
+
case 0: val = parseFloat(m.e(1, 1).toFixed(6)); break;
|
2242
|
+
case 1: val = parseFloat(m.e(2, 1).toFixed(6)); break;
|
2243
|
+
case 2: val = parseFloat(m.e(1, 2).toFixed(6)); break;
|
2244
|
+
case 3: val = parseFloat(m.e(2, 2).toFixed(6)); break;
|
2245
|
+
case 4: val = parseFloat(m.e(1, 3).toFixed(6)); break;
|
2246
|
+
case 5: val = parseFloat(m.e(2, 3).toFixed(6)); break;
|
2247
|
+
}
|
2248
|
+
fx.values[i].now = val;
|
2249
|
+
});
|
2250
|
+
};
|
2251
|
+
});
|
2252
|
+
/**
|
2253
|
+
* Step for animating tranformations
|
2254
|
+
*/
|
2255
|
+
$.each($.transform.funcs, function(i, func) {
|
2256
|
+
$.fx.step[func] = function(fx) {
|
2257
|
+
var transform = fx.elem.transform || new $.transform(fx.elem),
|
2258
|
+
funcs = {};
|
2259
|
+
|
2260
|
+
if ($.cssMultipleValues[func] || (!$.cssNumber[func] && $.inArray(func, $.transform.funcs) !== -1)) {
|
2261
|
+
($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
|
2262
|
+
funcs[fx.prop] = [];
|
2263
|
+
$.each(fx.values, function(i, val) {
|
2264
|
+
funcs[fx.prop].push(val.now + ($.cssNumber[fx.prop] ? '' : val.unit));
|
2265
|
+
});
|
2266
|
+
} else {
|
2267
|
+
funcs[fx.prop] = fx.now + ($.cssNumber[fx.prop] ? '' : fx.unit);
|
2268
|
+
}
|
2269
|
+
|
2270
|
+
transform.exec(funcs, {preserve: true});
|
2271
|
+
};
|
2272
|
+
});
|
2273
|
+
|
2274
|
+
// Support matrix animation
|
2275
|
+
$.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
|
2276
|
+
$.fx.step[func] = function(fx) {
|
2277
|
+
var transform = fx.elem.transform || new $.transform(fx.elem),
|
2278
|
+
funcs = {};
|
2279
|
+
|
2280
|
+
if (!fx.initialized) {
|
2281
|
+
fx.initialized = true;
|
2282
|
+
|
2283
|
+
// Reflections need a sensible end value set
|
2284
|
+
if (func !== 'matrix') {
|
2285
|
+
var values = $.matrix[func]().elements;
|
2286
|
+
var val;
|
2287
|
+
$.each(fx.values, function(i) {
|
2288
|
+
switch (i) {
|
2289
|
+
case 0: val = values[0]; break;
|
2290
|
+
case 1: val = values[2]; break;
|
2291
|
+
case 2: val = values[1]; break;
|
2292
|
+
case 3: val = values[3]; break;
|
2293
|
+
default: val = 0;
|
2294
|
+
}
|
2295
|
+
fx.values[i].end = val;
|
2296
|
+
});
|
2297
|
+
}
|
2298
|
+
|
2299
|
+
// Decompose the start and end
|
2300
|
+
fx.decomposed = {};
|
2301
|
+
var v = fx.values;
|
2302
|
+
|
2303
|
+
fx.decomposed.start = $.matrix.matrix(v[0].start, v[1].start, v[2].start, v[3].start, v[4].start, v[5].start).decompose();
|
2304
|
+
fx.decomposed.end = $.matrix.matrix(v[0].end, v[1].end, v[2].end, v[3].end, v[4].end, v[5].end).decompose();
|
2305
|
+
}
|
2306
|
+
|
2307
|
+
($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
|
2308
|
+
funcs.matrix = [];
|
2309
|
+
$.each(fx.values, function(i, val) {
|
2310
|
+
funcs.matrix.push(val.now);
|
2311
|
+
});
|
2312
|
+
|
2313
|
+
transform.exec(funcs, {preserve: true});
|
2314
|
+
};
|
2315
|
+
});
|
2316
|
+
})(jQuery, this, this.document);
|
2317
|
+
///////////////////////////////////////////////////////
|
2318
|
+
// Angle
|
2319
|
+
///////////////////////////////////////////////////////
|
2320
|
+
(function($, window, document, undefined) {
|
2321
|
+
/**
|
2322
|
+
* Converting a radian to a degree
|
2323
|
+
* @const
|
2324
|
+
*/
|
2325
|
+
var RAD_DEG = 180/Math.PI;
|
2326
|
+
|
2327
|
+
/**
|
2328
|
+
* Converting a radian to a grad
|
2329
|
+
* @const
|
2330
|
+
*/
|
2331
|
+
var RAD_GRAD = 200/Math.PI;
|
2332
|
+
|
2333
|
+
/**
|
2334
|
+
* Converting a degree to a radian
|
2335
|
+
* @const
|
2336
|
+
*/
|
2337
|
+
var DEG_RAD = Math.PI/180;
|
2338
|
+
|
2339
|
+
/**
|
2340
|
+
* Converting a degree to a grad
|
2341
|
+
* @const
|
2342
|
+
*/
|
2343
|
+
var DEG_GRAD = 2/1.8;
|
2344
|
+
|
2345
|
+
/**
|
2346
|
+
* Converting a grad to a degree
|
2347
|
+
* @const
|
2348
|
+
*/
|
2349
|
+
var GRAD_DEG = 0.9;
|
2350
|
+
|
2351
|
+
/**
|
2352
|
+
* Converting a grad to a radian
|
2353
|
+
* @const
|
2354
|
+
*/
|
2355
|
+
var GRAD_RAD = Math.PI/200;
|
2356
|
+
|
2357
|
+
|
2358
|
+
var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
|
2359
|
+
|
2360
|
+
/**
|
2361
|
+
* Functions for converting angles
|
2362
|
+
* @var Object
|
2363
|
+
*/
|
2364
|
+
$.extend({
|
2365
|
+
angle: {
|
2366
|
+
/**
|
2367
|
+
* available units for an angle
|
2368
|
+
* @var Regex
|
2369
|
+
*/
|
2370
|
+
runit: /(deg|g?rad)/,
|
2371
|
+
|
2372
|
+
/**
|
2373
|
+
* Convert a radian into a degree
|
2374
|
+
* @param Number rad
|
2375
|
+
* @return Number
|
2376
|
+
*/
|
2377
|
+
radianToDegree: function(rad) {
|
2378
|
+
return rad * RAD_DEG;
|
2379
|
+
},
|
2380
|
+
|
2381
|
+
/**
|
2382
|
+
* Convert a radian into a degree
|
2383
|
+
* @param Number rad
|
2384
|
+
* @return Number
|
2385
|
+
*/
|
2386
|
+
radianToGrad: function(rad) {
|
2387
|
+
return rad * RAD_GRAD;
|
2388
|
+
},
|
2389
|
+
|
2390
|
+
/**
|
2391
|
+
* Convert a degree into a radian
|
2392
|
+
* @param Number deg
|
2393
|
+
* @return Number
|
2394
|
+
*/
|
2395
|
+
degreeToRadian: function(deg) {
|
2396
|
+
return deg * DEG_RAD;
|
2397
|
+
},
|
2398
|
+
|
2399
|
+
/**
|
2400
|
+
* Convert a degree into a radian
|
2401
|
+
* @param Number deg
|
2402
|
+
* @return Number
|
2403
|
+
*/
|
2404
|
+
degreeToGrad: function(deg) {
|
2405
|
+
return deg * DEG_GRAD;
|
2406
|
+
},
|
2407
|
+
|
2408
|
+
/**
|
2409
|
+
* Convert a grad into a degree
|
2410
|
+
* @param Number grad
|
2411
|
+
* @return Number
|
2412
|
+
*/
|
2413
|
+
gradToDegree: function(grad) {
|
2414
|
+
return grad * GRAD_DEG;
|
2415
|
+
},
|
2416
|
+
|
2417
|
+
/**
|
2418
|
+
* Convert a grad into a radian
|
2419
|
+
* @param Number grad
|
2420
|
+
* @return Number
|
2421
|
+
*/
|
2422
|
+
gradToRadian: function(grad) {
|
2423
|
+
return grad * GRAD_RAD;
|
2424
|
+
},
|
2425
|
+
|
2426
|
+
/**
|
2427
|
+
* Convert an angle with a unit to a degree
|
2428
|
+
* @param String val angle with a unit
|
2429
|
+
* @return Number
|
2430
|
+
*/
|
2431
|
+
toDegree: function (val) {
|
2432
|
+
var parts = rfxnum.exec(val);
|
2433
|
+
if (parts) {
|
2434
|
+
val = parseFloat( parts[2] );
|
2435
|
+
switch (parts[3] || 'deg') {
|
2436
|
+
case 'grad':
|
2437
|
+
val = $.angle.gradToDegree(val);
|
2438
|
+
break;
|
2439
|
+
case 'rad':
|
2440
|
+
val = $.angle.radianToDegree(val);
|
2441
|
+
break;
|
2442
|
+
}
|
2443
|
+
return val;
|
2444
|
+
}
|
2445
|
+
return 0;
|
2446
|
+
}
|
2447
|
+
}
|
2448
|
+
});
|
2449
|
+
})(jQuery, this, this.document);
|
2450
|
+
///////////////////////////////////////////////////////
|
2451
|
+
// Matrix
|
2452
|
+
///////////////////////////////////////////////////////
|
2453
|
+
(function($, window, document, undefined) {
|
2454
|
+
/**
|
2455
|
+
* Matrix object for creating matrices relevant for 2d Transformations
|
2456
|
+
* @var Object
|
2457
|
+
*/
|
2458
|
+
if (typeof($.matrix) == 'undefined') {
|
2459
|
+
$.extend({
|
2460
|
+
matrix: {}
|
2461
|
+
});
|
2462
|
+
}
|
2463
|
+
var $m = $.matrix;
|
2464
|
+
|
2465
|
+
$.extend( $m, {
|
2466
|
+
/**
|
2467
|
+
* A 2-value vector
|
2468
|
+
* @param Number x
|
2469
|
+
* @param Number y
|
2470
|
+
* @constructor
|
2471
|
+
*/
|
2472
|
+
V2: function(x, y){
|
2473
|
+
if ($.isArray(arguments[0])) {
|
2474
|
+
this.elements = arguments[0].slice(0, 2);
|
2475
|
+
} else {
|
2476
|
+
this.elements = [x, y];
|
2477
|
+
}
|
2478
|
+
this.length = 2;
|
2479
|
+
},
|
2480
|
+
|
2481
|
+
/**
|
2482
|
+
* A 2-value vector
|
2483
|
+
* @param Number x
|
2484
|
+
* @param Number y
|
2485
|
+
* @param Number z
|
2486
|
+
* @constructor
|
2487
|
+
*/
|
2488
|
+
V3: function(x, y, z){
|
2489
|
+
if ($.isArray(arguments[0])) {
|
2490
|
+
this.elements = arguments[0].slice(0, 3);
|
2491
|
+
} else {
|
2492
|
+
this.elements = [x, y, z];
|
2493
|
+
}
|
2494
|
+
this.length = 3;
|
2495
|
+
},
|
2496
|
+
|
2497
|
+
/**
|
2498
|
+
* A 2x2 Matrix, useful for 2D-transformations without translations
|
2499
|
+
* @param Number mn
|
2500
|
+
* @constructor
|
2501
|
+
*/
|
2502
|
+
M2x2: function(m11, m12, m21, m22) {
|
2503
|
+
if ($.isArray(arguments[0])) {
|
2504
|
+
this.elements = arguments[0].slice(0, 4);
|
2505
|
+
} else {
|
2506
|
+
this.elements = Array.prototype.slice.call(arguments).slice(0, 4);
|
2507
|
+
}
|
2508
|
+
this.rows = 2;
|
2509
|
+
this.cols = 2;
|
2510
|
+
},
|
2511
|
+
|
2512
|
+
/**
|
2513
|
+
* A 3x3 Matrix, useful for 3D-transformations without translations
|
2514
|
+
* @param Number mn
|
2515
|
+
* @constructor
|
2516
|
+
*/
|
2517
|
+
M3x3: function(m11, m12, m13, m21, m22, m23, m31, m32, m33) {
|
2518
|
+
if ($.isArray(arguments[0])) {
|
2519
|
+
this.elements = arguments[0].slice(0, 9);
|
2520
|
+
} else {
|
2521
|
+
this.elements = Array.prototype.slice.call(arguments).slice(0, 9);
|
2522
|
+
}
|
2523
|
+
this.rows = 3;
|
2524
|
+
this.cols = 3;
|
2525
|
+
}
|
2526
|
+
});
|
2527
|
+
|
2528
|
+
/** generic matrix prototype */
|
2529
|
+
var Matrix = {
|
2530
|
+
/**
|
2531
|
+
* Return a specific element from the matrix
|
2532
|
+
* @param Number row where 1 is the 0th row
|
2533
|
+
* @param Number col where 1 is the 0th column
|
2534
|
+
* @return Number
|
2535
|
+
*/
|
2536
|
+
e: function(row, col) {
|
2537
|
+
var rows = this.rows,
|
2538
|
+
cols = this.cols;
|
2539
|
+
|
2540
|
+
// return 0 on nonsense rows and columns
|
2541
|
+
if (row > rows || col > rows || row < 1 || col < 1) {
|
2542
|
+
return 0;
|
2543
|
+
}
|
2544
|
+
|
2545
|
+
return this.elements[(row - 1) * cols + col - 1];
|
2546
|
+
},
|
2547
|
+
|
2548
|
+
/**
|
2549
|
+
* Taken from Zoomooz
|
2550
|
+
* https://github.com/jaukia/zoomooz/blob/c7a37b9a65a06ba730bd66391bbd6fe8e55d3a49/js/jquery.zoomooz.js
|
2551
|
+
*/
|
2552
|
+
decompose: function() {
|
2553
|
+
var a = this.e(1, 1),
|
2554
|
+
b = this.e(2, 1),
|
2555
|
+
c = this.e(1, 2),
|
2556
|
+
d = this.e(2, 2),
|
2557
|
+
e = this.e(1, 3),
|
2558
|
+
f = this.e(2, 3);
|
2559
|
+
|
2560
|
+
// In case the matrix can't be decomposed
|
2561
|
+
if (Math.abs(a * d - b * c) < 0.01) {
|
2562
|
+
return {
|
2563
|
+
rotate: 0 + 'deg',
|
2564
|
+
skewX: 0 + 'deg',
|
2565
|
+
scaleX: 1,
|
2566
|
+
scaleY: 1,
|
2567
|
+
translateX: 0 + 'px',
|
2568
|
+
translateY: 0 + 'px'
|
2569
|
+
};
|
2570
|
+
}
|
2571
|
+
|
2572
|
+
// Translate is easy
|
2573
|
+
var tx = e, ty = f;
|
2574
|
+
|
2575
|
+
// factor out the X scale
|
2576
|
+
var sx = Math.sqrt(a * a + b * b);
|
2577
|
+
a = a/sx;
|
2578
|
+
b = b/sx;
|
2579
|
+
|
2580
|
+
// factor out the skew
|
2581
|
+
var k = a * c + b * d;
|
2582
|
+
c -= a * k;
|
2583
|
+
d -= b * k;
|
2584
|
+
|
2585
|
+
// factor out the Y scale
|
2586
|
+
var sy = Math.sqrt(c * c + d * d);
|
2587
|
+
c = c / sy;
|
2588
|
+
d = d / sy;
|
2589
|
+
k = k / sy;
|
2590
|
+
|
2591
|
+
// account for negative scale
|
2592
|
+
if ((a * d - b * c) < 0.0) {
|
2593
|
+
a = -a;
|
2594
|
+
b = -b;
|
2595
|
+
//c = -c; // accomplishes nothing to negate it
|
2596
|
+
//d = -d; // accomplishes nothing to negate it
|
2597
|
+
sx = -sx;
|
2598
|
+
//sy = -sy //Scale Y shouldn't ever be negated
|
2599
|
+
}
|
2600
|
+
|
2601
|
+
// calculate the rotation angle and skew angle
|
2602
|
+
var rad2deg = $.angle.radianToDegree;
|
2603
|
+
var r = rad2deg(Math.atan2(b, a));
|
2604
|
+
k = rad2deg(Math.atan(k));
|
2605
|
+
|
2606
|
+
return {
|
2607
|
+
rotate: r + 'deg',
|
2608
|
+
skewX: k + 'deg',
|
2609
|
+
scaleX: sx,
|
2610
|
+
scaleY: sy,
|
2611
|
+
translateX: tx + 'px',
|
2612
|
+
translateY: ty + 'px'
|
2613
|
+
};
|
2614
|
+
}
|
2615
|
+
};
|
2616
|
+
|
2617
|
+
/** Extend all of the matrix types with the same prototype */
|
2618
|
+
$.extend($m.M2x2.prototype, Matrix, {
|
2619
|
+
toM3x3: function() {
|
2620
|
+
var a = this.elements;
|
2621
|
+
return new $m.M3x3(
|
2622
|
+
a[0], a[1], 0,
|
2623
|
+
a[2], a[3], 0,
|
2624
|
+
0, 0, 1
|
2625
|
+
);
|
2626
|
+
},
|
2627
|
+
|
2628
|
+
/**
|
2629
|
+
* Multiply a 2x2 matrix by a similar matrix or a vector
|
2630
|
+
* @param M2x2 | V2 matrix
|
2631
|
+
* @return M2x2 | V2
|
2632
|
+
*/
|
2633
|
+
x: function(matrix) {
|
2634
|
+
var isVector = typeof(matrix.rows) === 'undefined';
|
2635
|
+
|
2636
|
+
// Ensure the right-sized matrix
|
2637
|
+
if (!isVector && matrix.rows == 3) {
|
2638
|
+
return this.toM3x3().x(matrix);
|
2639
|
+
}
|
2640
|
+
|
2641
|
+
var a = this.elements,
|
2642
|
+
b = matrix.elements;
|
2643
|
+
|
2644
|
+
if (isVector && b.length == 2) {
|
2645
|
+
// b is actually a vector
|
2646
|
+
return new $m.V2(
|
2647
|
+
a[0] * b[0] + a[1] * b[1],
|
2648
|
+
a[2] * b[0] + a[3] * b[1]
|
2649
|
+
);
|
2650
|
+
} else if (b.length == a.length) {
|
2651
|
+
// b is a 2x2 matrix
|
2652
|
+
return new $m.M2x2(
|
2653
|
+
a[0] * b[0] + a[1] * b[2],
|
2654
|
+
a[0] * b[1] + a[1] * b[3],
|
2655
|
+
|
2656
|
+
a[2] * b[0] + a[3] * b[2],
|
2657
|
+
a[2] * b[1] + a[3] * b[3]
|
2658
|
+
);
|
2659
|
+
}
|
2660
|
+
return false; // fail
|
2661
|
+
},
|
2662
|
+
|
2663
|
+
/**
|
2664
|
+
* Generates an inverse of the current matrix
|
2665
|
+
* @param void
|
2666
|
+
* @return M2x2
|
2667
|
+
* @link http://www.dr-lex.be/random/matrix_inv.html
|
2668
|
+
*/
|
2669
|
+
inverse: function() {
|
2670
|
+
var d = 1/this.determinant(),
|
2671
|
+
a = this.elements;
|
2672
|
+
return new $m.M2x2(
|
2673
|
+
d * a[3], d * -a[1],
|
2674
|
+
d * -a[2], d * a[0]
|
2675
|
+
);
|
2676
|
+
},
|
2677
|
+
|
2678
|
+
/**
|
2679
|
+
* Calculates the determinant of the current matrix
|
2680
|
+
* @param void
|
2681
|
+
* @return Number
|
2682
|
+
* @link http://www.dr-lex.be/random/matrix_inv.html
|
2683
|
+
*/
|
2684
|
+
determinant: function() {
|
2685
|
+
var a = this.elements;
|
2686
|
+
return a[0] * a[3] - a[1] * a[2];
|
2687
|
+
}
|
2688
|
+
});
|
2689
|
+
|
2690
|
+
$.extend($m.M3x3.prototype, Matrix, {
|
2691
|
+
/**
|
2692
|
+
* Multiply a 3x3 matrix by a similar matrix or a vector
|
2693
|
+
* @param M3x3 | V3 matrix
|
2694
|
+
* @return M3x3 | V3
|
2695
|
+
*/
|
2696
|
+
x: function(matrix) {
|
2697
|
+
var isVector = typeof(matrix.rows) === 'undefined';
|
2698
|
+
|
2699
|
+
// Ensure the right-sized matrix
|
2700
|
+
if (!isVector && matrix.rows < 3) {
|
2701
|
+
matrix = matrix.toM3x3();
|
2702
|
+
}
|
2703
|
+
|
2704
|
+
var a = this.elements,
|
2705
|
+
b = matrix.elements;
|
2706
|
+
|
2707
|
+
if (isVector && b.length == 3) {
|
2708
|
+
// b is actually a vector
|
2709
|
+
return new $m.V3(
|
2710
|
+
a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
|
2711
|
+
a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
|
2712
|
+
a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
|
2713
|
+
);
|
2714
|
+
} else if (b.length == a.length) {
|
2715
|
+
// b is a 3x3 matrix
|
2716
|
+
return new $m.M3x3(
|
2717
|
+
a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
|
2718
|
+
a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
|
2719
|
+
a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
|
2720
|
+
|
2721
|
+
a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
|
2722
|
+
a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
|
2723
|
+
a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
|
2724
|
+
|
2725
|
+
a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
|
2726
|
+
a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
|
2727
|
+
a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
|
2728
|
+
);
|
2729
|
+
}
|
2730
|
+
return false; // fail
|
2731
|
+
},
|
2732
|
+
|
2733
|
+
/**
|
2734
|
+
* Generates an inverse of the current matrix
|
2735
|
+
* @param void
|
2736
|
+
* @return M3x3
|
2737
|
+
* @link http://www.dr-lex.be/random/matrix_inv.html
|
2738
|
+
*/
|
2739
|
+
inverse: function() {
|
2740
|
+
var d = 1/this.determinant(),
|
2741
|
+
a = this.elements;
|
2742
|
+
return new $m.M3x3(
|
2743
|
+
d * ( a[8] * a[4] - a[7] * a[5]),
|
2744
|
+
d * (-(a[8] * a[1] - a[7] * a[2])),
|
2745
|
+
d * ( a[5] * a[1] - a[4] * a[2]),
|
2746
|
+
|
2747
|
+
d * (-(a[8] * a[3] - a[6] * a[5])),
|
2748
|
+
d * ( a[8] * a[0] - a[6] * a[2]),
|
2749
|
+
d * (-(a[5] * a[0] - a[3] * a[2])),
|
2750
|
+
|
2751
|
+
d * ( a[7] * a[3] - a[6] * a[4]),
|
2752
|
+
d * (-(a[7] * a[0] - a[6] * a[1])),
|
2753
|
+
d * ( a[4] * a[0] - a[3] * a[1])
|
2754
|
+
);
|
2755
|
+
},
|
2756
|
+
|
2757
|
+
/**
|
2758
|
+
* Calculates the determinant of the current matrix
|
2759
|
+
* @param void
|
2760
|
+
* @return Number
|
2761
|
+
* @link http://www.dr-lex.be/random/matrix_inv.html
|
2762
|
+
*/
|
2763
|
+
determinant: function() {
|
2764
|
+
var a = this.elements;
|
2765
|
+
return a[0] * (a[8] * a[4] - a[7] * a[5]) - a[3] * (a[8] * a[1] - a[7] * a[2]) + a[6] * (a[5] * a[1] - a[4] * a[2]);
|
2766
|
+
}
|
2767
|
+
});
|
2768
|
+
|
2769
|
+
/** generic vector prototype */
|
2770
|
+
var Vector = {
|
2771
|
+
/**
|
2772
|
+
* Return a specific element from the vector
|
2773
|
+
* @param Number i where 1 is the 0th value
|
2774
|
+
* @return Number
|
2775
|
+
*/
|
2776
|
+
e: function(i) {
|
2777
|
+
return this.elements[i - 1];
|
2778
|
+
}
|
2779
|
+
};
|
2780
|
+
|
2781
|
+
/** Extend all of the vector types with the same prototype */
|
2782
|
+
$.extend($m.V2.prototype, Vector);
|
2783
|
+
$.extend($m.V3.prototype, Vector);
|
2784
|
+
})(jQuery, this, this.document);
|
2785
|
+
///////////////////////////////////////////////////////
|
2786
|
+
// Matrix Calculations
|
2787
|
+
///////////////////////////////////////////////////////
|
2788
|
+
(function($, window, document, undefined) {
|
2789
|
+
/**
|
2790
|
+
* Matrix object for creating matrices relevant for 2d Transformations
|
2791
|
+
* @var Object
|
2792
|
+
*/
|
2793
|
+
if (typeof($.matrix) == 'undefined') {
|
2794
|
+
$.extend({
|
2795
|
+
matrix: {}
|
2796
|
+
});
|
2797
|
+
}
|
2798
|
+
|
2799
|
+
$.extend( $.matrix, {
|
2800
|
+
/**
|
2801
|
+
* Class for calculating coordinates on a matrix
|
2802
|
+
* @param Matrix matrix
|
2803
|
+
* @param Number outerHeight
|
2804
|
+
* @param Number outerWidth
|
2805
|
+
* @constructor
|
2806
|
+
*/
|
2807
|
+
calc: function(matrix, outerHeight, outerWidth) {
|
2808
|
+
/**
|
2809
|
+
* @var Matrix
|
2810
|
+
*/
|
2811
|
+
this.matrix = matrix;
|
2812
|
+
|
2813
|
+
/**
|
2814
|
+
* @var Number
|
2815
|
+
*/
|
2816
|
+
this.outerHeight = outerHeight;
|
2817
|
+
|
2818
|
+
/**
|
2819
|
+
* @var Number
|
2820
|
+
*/
|
2821
|
+
this.outerWidth = outerWidth;
|
2822
|
+
}
|
2823
|
+
});
|
2824
|
+
|
2825
|
+
$.matrix.calc.prototype = {
|
2826
|
+
/**
|
2827
|
+
* Calculate a coord on the new object
|
2828
|
+
* @return Object
|
2829
|
+
*/
|
2830
|
+
coord: function(x, y, z) {
|
2831
|
+
//default z and w
|
2832
|
+
z = typeof(z) !== 'undefined' ? z : 0;
|
2833
|
+
|
2834
|
+
var matrix = this.matrix,
|
2835
|
+
vector;
|
2836
|
+
|
2837
|
+
switch (matrix.rows) {
|
2838
|
+
case 2:
|
2839
|
+
vector = matrix.x(new $.matrix.V2(x, y));
|
2840
|
+
break;
|
2841
|
+
case 3:
|
2842
|
+
vector = matrix.x(new $.matrix.V3(x, y, z));
|
2843
|
+
break;
|
2844
|
+
}
|
2845
|
+
|
2846
|
+
return vector;
|
2847
|
+
},
|
2848
|
+
|
2849
|
+
/**
|
2850
|
+
* Calculate the corners of the new object
|
2851
|
+
* @return Object
|
2852
|
+
*/
|
2853
|
+
corners: function(x, y) {
|
2854
|
+
// Try to save the corners if this is called a lot
|
2855
|
+
var save = !(typeof(x) !=='undefined' || typeof(y) !=='undefined'),
|
2856
|
+
c;
|
2857
|
+
if (!this.c || !save) {
|
2858
|
+
y = y || this.outerHeight;
|
2859
|
+
x = x || this.outerWidth;
|
2860
|
+
|
2861
|
+
c = {
|
2862
|
+
tl: this.coord(0, 0),
|
2863
|
+
bl: this.coord(0, y),
|
2864
|
+
tr: this.coord(x, 0),
|
2865
|
+
br: this.coord(x, y)
|
2866
|
+
};
|
2867
|
+
} else {
|
2868
|
+
c = this.c;
|
2869
|
+
}
|
2870
|
+
|
2871
|
+
if (save) {
|
2872
|
+
this.c = c;
|
2873
|
+
}
|
2874
|
+
return c;
|
2875
|
+
},
|
2876
|
+
|
2877
|
+
/**
|
2878
|
+
* Calculate the sides of the new object
|
2879
|
+
* @return Object
|
2880
|
+
*/
|
2881
|
+
sides: function(corners) {
|
2882
|
+
// The corners of the box
|
2883
|
+
var c = corners || this.corners();
|
2884
|
+
|
2885
|
+
return {
|
2886
|
+
top: Math.min(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
|
2887
|
+
bottom: Math.max(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
|
2888
|
+
left: Math.min(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1)),
|
2889
|
+
right: Math.max(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1))
|
2890
|
+
};
|
2891
|
+
},
|
2892
|
+
|
2893
|
+
/**
|
2894
|
+
* Calculate the offset of the new object
|
2895
|
+
* @return Object
|
2896
|
+
*/
|
2897
|
+
offset: function(corners) {
|
2898
|
+
// The corners of the box
|
2899
|
+
var s = this.sides(corners);
|
2900
|
+
|
2901
|
+
// return size
|
2902
|
+
return {
|
2903
|
+
height: Math.abs(s.bottom - s.top),
|
2904
|
+
width: Math.abs(s.right - s.left)
|
2905
|
+
};
|
2906
|
+
},
|
2907
|
+
|
2908
|
+
/**
|
2909
|
+
* Calculate the area of the new object
|
2910
|
+
* @return Number
|
2911
|
+
* @link http://en.wikipedia.org/wiki/Quadrilateral#Area_of_a_convex_quadrilateral
|
2912
|
+
*/
|
2913
|
+
area: function(corners) {
|
2914
|
+
// The corners of the box
|
2915
|
+
var c = corners || this.corners();
|
2916
|
+
|
2917
|
+
// calculate the two diagonal vectors
|
2918
|
+
var v1 = {
|
2919
|
+
x: c.tr.e(1) - c.tl.e(1) + c.br.e(1) - c.bl.e(1),
|
2920
|
+
y: c.tr.e(2) - c.tl.e(2) + c.br.e(2) - c.bl.e(2)
|
2921
|
+
},
|
2922
|
+
v2 = {
|
2923
|
+
x: c.bl.e(1) - c.tl.e(1) + c.br.e(1) - c.tr.e(1),
|
2924
|
+
y: c.bl.e(2) - c.tl.e(2) + c.br.e(2) - c.tr.e(2)
|
2925
|
+
};
|
2926
|
+
|
2927
|
+
return 0.25 * Math.abs(v1.e(1) * v2.e(2) - v1.e(2) * v2.e(1));
|
2928
|
+
},
|
2929
|
+
|
2930
|
+
/**
|
2931
|
+
* Calculate the non-affinity of the new object
|
2932
|
+
* @return Number
|
2933
|
+
*/
|
2934
|
+
nonAffinity: function() {
|
2935
|
+
// The corners of the box
|
2936
|
+
var sides = this.sides(),
|
2937
|
+
xDiff = sides.top - sides.bottom,
|
2938
|
+
yDiff = sides.left - sides.right;
|
2939
|
+
|
2940
|
+
return parseFloat(parseFloat(Math.abs(
|
2941
|
+
(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) /
|
2942
|
+
(sides.top * sides.bottom + sides.left * sides.right)
|
2943
|
+
)).toFixed(8));
|
2944
|
+
},
|
2945
|
+
|
2946
|
+
/**
|
2947
|
+
* Calculate a proper top and left for IE
|
2948
|
+
* @param Object toOrigin
|
2949
|
+
* @param Object fromOrigin
|
2950
|
+
* @return Object
|
2951
|
+
*/
|
2952
|
+
originOffset: function(toOrigin, fromOrigin) {
|
2953
|
+
// the origin to translate to
|
2954
|
+
toOrigin = toOrigin ? toOrigin : new $.matrix.V2(
|
2955
|
+
this.outerWidth * 0.5,
|
2956
|
+
this.outerHeight * 0.5
|
2957
|
+
);
|
2958
|
+
|
2959
|
+
// the origin to translate from (IE has a fixed origin of 0, 0)
|
2960
|
+
fromOrigin = fromOrigin ? fromOrigin : new $.matrix.V2(
|
2961
|
+
0,
|
2962
|
+
0
|
2963
|
+
);
|
2964
|
+
|
2965
|
+
// transform the origins
|
2966
|
+
var toCenter = this.coord(toOrigin.e(1), toOrigin.e(2));
|
2967
|
+
var fromCenter = this.coord(fromOrigin.e(1), fromOrigin.e(2));
|
2968
|
+
|
2969
|
+
// return the offset
|
2970
|
+
return {
|
2971
|
+
top: (fromCenter.e(2) - fromOrigin.e(2)) - (toCenter.e(2) - toOrigin.e(2)),
|
2972
|
+
left: (fromCenter.e(1) - fromOrigin.e(1)) - (toCenter.e(1) - toOrigin.e(1))
|
2973
|
+
};
|
2974
|
+
}
|
2975
|
+
};
|
2976
|
+
})(jQuery, this, this.document);
|
2977
|
+
///////////////////////////////////////////////////////
|
2978
|
+
// 2d Matrix Functions
|
2979
|
+
///////////////////////////////////////////////////////
|
2980
|
+
(function($, window, document, undefined) {
|
2981
|
+
/**
|
2982
|
+
* Matrix object for creating matrices relevant for 2d Transformations
|
2983
|
+
* @var Object
|
2984
|
+
*/
|
2985
|
+
if (typeof($.matrix) == 'undefined') {
|
2986
|
+
$.extend({
|
2987
|
+
matrix: {}
|
2988
|
+
});
|
2989
|
+
}
|
2990
|
+
var $m = $.matrix,
|
2991
|
+
$m2x2 = $m.M2x2,
|
2992
|
+
$m3x3 = $m.M3x3;
|
2993
|
+
|
2994
|
+
$.extend( $m, {
|
2995
|
+
/**
|
2996
|
+
* Identity matrix
|
2997
|
+
* @param Number size
|
2998
|
+
* @return Matrix
|
2999
|
+
*/
|
3000
|
+
identity: function(size) {
|
3001
|
+
size = size || 2;
|
3002
|
+
var length = size * size,
|
3003
|
+
elements = new Array(length),
|
3004
|
+
mod = size + 1;
|
3005
|
+
for (var i = 0; i < length; i++) {
|
3006
|
+
elements[i] = (i % mod) === 0 ? 1 : 0;
|
3007
|
+
}
|
3008
|
+
return new $m['M'+size+'x'+size](elements);
|
3009
|
+
},
|
3010
|
+
|
3011
|
+
/**
|
3012
|
+
* Matrix
|
3013
|
+
* @return Matrix
|
3014
|
+
*/
|
3015
|
+
matrix: function() {
|
3016
|
+
var args = Array.prototype.slice.call(arguments);
|
3017
|
+
// arguments are in column-major order
|
3018
|
+
switch (arguments.length) {
|
3019
|
+
case 4:
|
3020
|
+
return new $m2x2(
|
3021
|
+
args[0], args[2],
|
3022
|
+
args[1], args[3]
|
3023
|
+
);
|
3024
|
+
case 6:
|
3025
|
+
return new $m3x3(
|
3026
|
+
args[0], args[2], args[4],
|
3027
|
+
args[1], args[3], args[5],
|
3028
|
+
0, 0, 1
|
3029
|
+
);
|
3030
|
+
}
|
3031
|
+
},
|
3032
|
+
|
3033
|
+
/**
|
3034
|
+
* Reflect (same as rotate(180))
|
3035
|
+
* @return Matrix
|
3036
|
+
*/
|
3037
|
+
reflect: function() {
|
3038
|
+
return new $m2x2(
|
3039
|
+
-1, 0,
|
3040
|
+
0, -1
|
3041
|
+
);
|
3042
|
+
},
|
3043
|
+
|
3044
|
+
/**
|
3045
|
+
* Reflect across the x-axis (mirrored upside down)
|
3046
|
+
* @return Matrix
|
3047
|
+
*/
|
3048
|
+
reflectX: function() {
|
3049
|
+
return new $m2x2(
|
3050
|
+
1, 0,
|
3051
|
+
0, -1
|
3052
|
+
);
|
3053
|
+
},
|
3054
|
+
|
3055
|
+
/**
|
3056
|
+
* Reflect by swapping x an y (same as reflectX + rotate(-90))
|
3057
|
+
* @return Matrix
|
3058
|
+
*/
|
3059
|
+
reflectXY: function() {
|
3060
|
+
return new $m2x2(
|
3061
|
+
0, 1,
|
3062
|
+
1, 0
|
3063
|
+
);
|
3064
|
+
},
|
3065
|
+
|
3066
|
+
/**
|
3067
|
+
* Reflect across the y-axis (mirrored)
|
3068
|
+
* @return Matrix
|
3069
|
+
*/
|
3070
|
+
reflectY: function() {
|
3071
|
+
return new $m2x2(
|
3072
|
+
-1, 0,
|
3073
|
+
0, 1
|
3074
|
+
);
|
3075
|
+
},
|
3076
|
+
|
3077
|
+
/**
|
3078
|
+
* Rotates around the origin
|
3079
|
+
* @param Number deg
|
3080
|
+
* @return Matrix
|
3081
|
+
* @link http://www.w3.org/TR/SVG/coords.html#RotationDefined
|
3082
|
+
*/
|
3083
|
+
rotate: function(deg) {
|
3084
|
+
//TODO: detect units
|
3085
|
+
var rad = $.angle.degreeToRadian(deg),
|
3086
|
+
costheta = Math.cos(rad),
|
3087
|
+
sintheta = Math.sin(rad);
|
3088
|
+
|
3089
|
+
var a = costheta,
|
3090
|
+
b = sintheta,
|
3091
|
+
c = -sintheta,
|
3092
|
+
d = costheta;
|
3093
|
+
|
3094
|
+
return new $m2x2(
|
3095
|
+
a, c,
|
3096
|
+
b, d
|
3097
|
+
);
|
3098
|
+
},
|
3099
|
+
|
3100
|
+
/**
|
3101
|
+
* Scale
|
3102
|
+
* @param Number sx
|
3103
|
+
* @param Number sy
|
3104
|
+
* @return Matrix
|
3105
|
+
* @link http://www.w3.org/TR/SVG/coords.html#ScalingDefined
|
3106
|
+
*/
|
3107
|
+
scale: function (sx, sy) {
|
3108
|
+
sx = sx || sx === 0 ? sx : 1;
|
3109
|
+
sy = sy || sy === 0 ? sy : sx;
|
3110
|
+
|
3111
|
+
return new $m2x2(
|
3112
|
+
sx, 0,
|
3113
|
+
0, sy
|
3114
|
+
);
|
3115
|
+
},
|
3116
|
+
|
3117
|
+
/**
|
3118
|
+
* Scale on the X-axis
|
3119
|
+
* @param Number sx
|
3120
|
+
* @return Matrix
|
3121
|
+
*/
|
3122
|
+
scaleX: function (sx) {
|
3123
|
+
return $m.scale(sx, 1);
|
3124
|
+
},
|
3125
|
+
|
3126
|
+
/**
|
3127
|
+
* Scale on the Y-axis
|
3128
|
+
* @param Number sy
|
3129
|
+
* @return Matrix
|
3130
|
+
*/
|
3131
|
+
scaleY: function (sy) {
|
3132
|
+
return $m.scale(1, sy);
|
3133
|
+
},
|
3134
|
+
|
3135
|
+
/**
|
3136
|
+
* Skews on the X-axis and Y-axis
|
3137
|
+
* @param Number degX
|
3138
|
+
* @param Number degY
|
3139
|
+
* @return Matrix
|
3140
|
+
*/
|
3141
|
+
skew: function (degX, degY) {
|
3142
|
+
degX = degX || 0;
|
3143
|
+
degY = degY || 0;
|
3144
|
+
|
3145
|
+
//TODO: detect units
|
3146
|
+
var radX = $.angle.degreeToRadian(degX),
|
3147
|
+
radY = $.angle.degreeToRadian(degY),
|
3148
|
+
x = Math.tan(radX),
|
3149
|
+
y = Math.tan(radY);
|
3150
|
+
|
3151
|
+
return new $m2x2(
|
3152
|
+
1, x,
|
3153
|
+
y, 1
|
3154
|
+
);
|
3155
|
+
},
|
3156
|
+
|
3157
|
+
/**
|
3158
|
+
* Skews on the X-axis
|
3159
|
+
* @param Number degX
|
3160
|
+
* @return Matrix
|
3161
|
+
* @link http://www.w3.org/TR/SVG/coords.html#SkewXDefined
|
3162
|
+
*/
|
3163
|
+
skewX: function (degX) {
|
3164
|
+
return $m.skew(degX);
|
3165
|
+
},
|
3166
|
+
|
3167
|
+
/**
|
3168
|
+
* Skews on the Y-axis
|
3169
|
+
* @param Number degY
|
3170
|
+
* @return Matrix
|
3171
|
+
* @link http://www.w3.org/TR/SVG/coords.html#SkewYDefined
|
3172
|
+
*/
|
3173
|
+
skewY: function (degY) {
|
3174
|
+
return $m.skew(0, degY);
|
3175
|
+
},
|
3176
|
+
|
3177
|
+
/**
|
3178
|
+
* Translate
|
3179
|
+
* @param Number tx
|
3180
|
+
* @param Number ty
|
3181
|
+
* @return Matrix
|
3182
|
+
* @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
|
3183
|
+
*/
|
3184
|
+
translate: function (tx, ty) {
|
3185
|
+
tx = tx || 0;
|
3186
|
+
ty = ty || 0;
|
3187
|
+
|
3188
|
+
return new $m3x3(
|
3189
|
+
1, 0, tx,
|
3190
|
+
0, 1, ty,
|
3191
|
+
0, 0, 1
|
3192
|
+
);
|
3193
|
+
},
|
3194
|
+
|
3195
|
+
/**
|
3196
|
+
* Translate on the X-axis
|
3197
|
+
* @param Number tx
|
3198
|
+
* @return Matrix
|
3199
|
+
* @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
|
3200
|
+
*/
|
3201
|
+
translateX: function (tx) {
|
3202
|
+
return $m.translate(tx);
|
3203
|
+
},
|
3204
|
+
|
3205
|
+
/**
|
3206
|
+
* Translate on the Y-axis
|
3207
|
+
* @param Number ty
|
3208
|
+
* @return Matrix
|
3209
|
+
* @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
|
3210
|
+
*/
|
3211
|
+
translateY: function (ty) {
|
3212
|
+
return $m.translate(0, ty);
|
3213
|
+
}
|
3214
|
+
});
|
3215
|
+
})(jQuery, this, this.document);
|
3216
|
+
/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
|
3217
|
+
* Licensed under the MIT License (LICENSE.txt).
|
3218
|
+
*
|
3219
|
+
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
|
3220
|
+
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
|
3221
|
+
* Thanks to: Seamus Leahy for adding deltaX and deltaY
|
3222
|
+
*
|
3223
|
+
* Version: 3.0.4
|
3224
|
+
*
|
3225
|
+
* Requires: 1.2.2+
|
3226
|
+
*/
|
3227
|
+
|
3228
|
+
|
3229
|
+
(function($) {
|
3230
|
+
|
3231
|
+
var types = ['DOMMouseScroll', 'mousewheel'];
|
3232
|
+
|
3233
|
+
$.event.special.mousewheel = {
|
3234
|
+
setup: function() {
|
3235
|
+
if ( this.addEventListener ) {
|
3236
|
+
for ( var i=types.length; i; ) {
|
3237
|
+
this.addEventListener( types[--i], handler, false );
|
3238
|
+
}
|
3239
|
+
} else {
|
3240
|
+
this.onmousewheel = handler;
|
3241
|
+
}
|
3242
|
+
},
|
3243
|
+
|
3244
|
+
teardown: function() {
|
3245
|
+
if ( this.removeEventListener ) {
|
3246
|
+
for ( var i=types.length; i; ) {
|
3247
|
+
this.removeEventListener( types[--i], handler, false );
|
3248
|
+
}
|
3249
|
+
} else {
|
3250
|
+
this.onmousewheel = null;
|
3251
|
+
}
|
3252
|
+
}
|
3253
|
+
};
|
3254
|
+
|
3255
|
+
$.fn.extend({
|
3256
|
+
mousewheel: function(fn) {
|
3257
|
+
return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
|
3258
|
+
},
|
3259
|
+
|
3260
|
+
unmousewheel: function(fn) {
|
3261
|
+
return this.unbind("mousewheel", fn);
|
3262
|
+
}
|
3263
|
+
});
|
3264
|
+
|
3265
|
+
|
3266
|
+
function handler(event) {
|
3267
|
+
var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
|
3268
|
+
event = $.event.fix(orgEvent);
|
3269
|
+
event.type = "mousewheel";
|
3270
|
+
|
3271
|
+
// Old school scrollwheel delta
|
3272
|
+
if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
|
3273
|
+
if ( event.detail ) { delta = -event.detail/3; }
|
3274
|
+
|
3275
|
+
// New school multidimensional scroll (touchpads) deltas
|
3276
|
+
deltaY = delta;
|
3277
|
+
|
3278
|
+
// Gecko
|
3279
|
+
if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
|
3280
|
+
deltaY = 0;
|
3281
|
+
deltaX = -1*delta;
|
3282
|
+
}
|
3283
|
+
|
3284
|
+
// Webkit
|
3285
|
+
if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
|
3286
|
+
if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
|
3287
|
+
|
3288
|
+
// Add event and delta to the front of the arguments
|
3289
|
+
args.unshift(event, delta, deltaX, deltaY);
|
3290
|
+
|
3291
|
+
return $.event.handle.apply(this, args);
|
3292
|
+
}
|
3293
|
+
|
3294
|
+
})(jQuery);
|
3295
|
+
// # ZUI53
|
3296
|
+
//
|
3297
|
+
// Copyright (c) 2011 Florian Günther
|
3298
|
+
//
|
3299
|
+
// Permission is hereby granted, free of charge, to any person obtaining
|
3300
|
+
// a copy of this software and associated documentation files (the
|
3301
|
+
// "Software"), to deal in the Software without restriction, including
|
3302
|
+
// without limitation the rights to use, copy, modify, merge, publish,
|
3303
|
+
// distribute, sublicense, and/or sell copies of the Software, and to
|
3304
|
+
// permit persons to whom the Software is furnished to do so, subject to
|
3305
|
+
// the following conditions:
|
3306
|
+
//
|
3307
|
+
// The above copyright notice and this permission notice shall be
|
3308
|
+
// included in all copies or substantial portions of the Software.
|
3309
|
+
//
|
3310
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
3311
|
+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
3312
|
+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
3313
|
+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
3314
|
+
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
3315
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
3316
|
+
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
3317
|
+
//
|
3318
|
+
|
3319
|
+
function registerNS(ns)
|
3320
|
+
{
|
3321
|
+
var nsParts = ns.split(".");
|
3322
|
+
var root = window;
|
3323
|
+
|
3324
|
+
for(var i=0; i<nsParts.length; i++)
|
3325
|
+
{
|
3326
|
+
if(typeof root[nsParts[i]] == "undefined")
|
3327
|
+
root[nsParts[i]] = new Object();
|
3328
|
+
|
3329
|
+
root = root[nsParts[i]];
|
3330
|
+
}
|
3331
|
+
}
|
3332
|
+
|
3333
|
+
function namespace(name, callback)
|
3334
|
+
{
|
3335
|
+
registerNS(name);
|
3336
|
+
callback( eval(name) );
|
3337
|
+
}
|
3338
|
+
;
|
3339
|
+
(function() {
|
3340
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
3341
|
+
namespace('ZUI53.Tools', function(exports) {
|
3342
|
+
exports.Base = (function() {
|
3343
|
+
function Base() {
|
3344
|
+
this.stopEvent = __bind(this.stopEvent, this);
|
3345
|
+
this.makeUnexclusive = __bind(this.makeUnexclusive, this);
|
3346
|
+
this.makeExclusive = __bind(this.makeExclusive, this);
|
3347
|
+
this.detach = __bind(this.detach, this);
|
3348
|
+
this.attach = __bind(this.attach, this); this.set = null;
|
3349
|
+
this.group = null;
|
3350
|
+
this.attached = false;
|
3351
|
+
}
|
3352
|
+
Base.prototype.attach = function() {
|
3353
|
+
if (this.group) {
|
3354
|
+
this.group.attach(this);
|
3355
|
+
}
|
3356
|
+
return this.attached = true;
|
3357
|
+
};
|
3358
|
+
Base.prototype.detach = function() {
|
3359
|
+
return this.attached = false;
|
3360
|
+
};
|
3361
|
+
Base.prototype.makeExclusive = function() {
|
3362
|
+
if (this.set) {
|
3363
|
+
this.set.exclusive(this);
|
3364
|
+
}
|
3365
|
+
return this.attach();
|
3366
|
+
};
|
3367
|
+
Base.prototype.makeUnexclusive = function() {
|
3368
|
+
if (this.set) {
|
3369
|
+
return this.set.unexclusive();
|
3370
|
+
}
|
3371
|
+
};
|
3372
|
+
Base.prototype.stopEvent = function(e) {
|
3373
|
+
e.preventDefault();
|
3374
|
+
if (e.stopImmediatePropagation != null) {
|
3375
|
+
e.stopImmediatePropagation();
|
3376
|
+
}
|
3377
|
+
return false;
|
3378
|
+
};
|
3379
|
+
return Base;
|
3380
|
+
})();
|
3381
|
+
exports.SetGroup = (function() {
|
3382
|
+
function SetGroup() {
|
3383
|
+
this.requestUnexclusive = __bind(this.requestUnexclusive, this);
|
3384
|
+
this.requestExclusive = __bind(this.requestExclusive, this);
|
3385
|
+
this.attach = __bind(this.attach, this);
|
3386
|
+
this.add = __bind(this.add, this); this.tools = [];
|
3387
|
+
this.current = null;
|
3388
|
+
this.beforeExclusive = null;
|
3389
|
+
}
|
3390
|
+
SetGroup.prototype.add = function(tool) {
|
3391
|
+
tool.group = this;
|
3392
|
+
this.tools.push(tool);
|
3393
|
+
if (this.tools.length === 1) {
|
3394
|
+
return tool.attach();
|
3395
|
+
}
|
3396
|
+
};
|
3397
|
+
SetGroup.prototype.attach = function(tool) {
|
3398
|
+
var t, _i, _len, _ref, _results;
|
3399
|
+
this.current = tool;
|
3400
|
+
_ref = this.tools;
|
3401
|
+
_results = [];
|
3402
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
3403
|
+
t = _ref[_i];
|
3404
|
+
_results.push(t !== tool ? t.detach() : void 0);
|
3405
|
+
}
|
3406
|
+
return _results;
|
3407
|
+
};
|
3408
|
+
SetGroup.prototype.requestExclusive = function(tool) {
|
3409
|
+
if (this.current && this.current !== tool) {
|
3410
|
+
this.current.detach();
|
3411
|
+
}
|
3412
|
+
return this.beforeExclusive = this.current;
|
3413
|
+
};
|
3414
|
+
SetGroup.prototype.requestUnexclusive = function() {
|
3415
|
+
this.current = this.beforeExclusive;
|
3416
|
+
if (this.current) {
|
3417
|
+
return this.current.attach();
|
3418
|
+
}
|
3419
|
+
};
|
3420
|
+
return SetGroup;
|
3421
|
+
})();
|
3422
|
+
return exports.Set = (function() {
|
3423
|
+
function Set(default_tool) {
|
3424
|
+
this.default_tool = default_tool;
|
3425
|
+
this.unexclusive = __bind(this.unexclusive, this);
|
3426
|
+
this.exclusive = __bind(this.exclusive, this);
|
3427
|
+
this.add = __bind(this.add, this);
|
3428
|
+
this.groups = [new exports.SetGroup()];
|
3429
|
+
this.default_tool.set = this;
|
3430
|
+
if (this.default_tool) {
|
3431
|
+
this.default_tool.attach();
|
3432
|
+
}
|
3433
|
+
}
|
3434
|
+
Set.prototype.add = function(tool) {
|
3435
|
+
this.groups[0].add(tool);
|
3436
|
+
return tool.set = this;
|
3437
|
+
};
|
3438
|
+
Set.prototype.exclusive = function(tool) {
|
3439
|
+
var g, _i, _len, _ref;
|
3440
|
+
_ref = this.groups;
|
3441
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
3442
|
+
g = _ref[_i];
|
3443
|
+
g.requestExclusive(tool);
|
3444
|
+
}
|
3445
|
+
if (this.default_tool !== tool && this.default_tool) {
|
3446
|
+
return this.default_tool.detach();
|
3447
|
+
}
|
3448
|
+
};
|
3449
|
+
Set.prototype.unexclusive = function() {
|
3450
|
+
var g, _i, _len, _ref;
|
3451
|
+
_ref = this.groups;
|
3452
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
3453
|
+
g = _ref[_i];
|
3454
|
+
g.requestUnexclusive();
|
3455
|
+
}
|
3456
|
+
if (this.default_tool) {
|
3457
|
+
return this.default_tool.attach();
|
3458
|
+
}
|
3459
|
+
};
|
3460
|
+
return Set;
|
3461
|
+
})();
|
3462
|
+
});
|
3463
|
+
}).call(this);
|
3464
|
+
(function() {
|
3465
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
|
3466
|
+
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
|
3467
|
+
function ctor() { this.constructor = child; }
|
3468
|
+
ctor.prototype = parent.prototype;
|
3469
|
+
child.prototype = new ctor;
|
3470
|
+
child.__super__ = parent.prototype;
|
3471
|
+
return child;
|
3472
|
+
};
|
3473
|
+
namespace('ZUI53.Tools', function(exports) {
|
3474
|
+
return exports.Pan = (function() {
|
3475
|
+
__extends(Pan, exports.Base);
|
3476
|
+
function Pan(zui) {
|
3477
|
+
this._start_with = __bind(this._start_with, this);
|
3478
|
+
this._pan_with = __bind(this._pan_with, this);
|
3479
|
+
this.touch_stop = __bind(this.touch_stop, this);
|
3480
|
+
this.touch_move = __bind(this.touch_move, this);
|
3481
|
+
this.touch_start = __bind(this.touch_start, this);
|
3482
|
+
this.stop = __bind(this.stop, this);
|
3483
|
+
this.pan = __bind(this.pan, this);
|
3484
|
+
this.start = __bind(this.start, this);
|
3485
|
+
this.detach = __bind(this.detach, this);
|
3486
|
+
this.attach = __bind(this.attach, this); this.vp = zui;
|
3487
|
+
this.eventDispatcher = zui.viewport;
|
3488
|
+
}
|
3489
|
+
Pan.prototype.attach = function() {
|
3490
|
+
$('body').addClass('pan');
|
3491
|
+
$(this.eventDispatcher).bind('mousedown', this.start);
|
3492
|
+
return $(this.eventDispatcher).bind('touchstart', this.touch_start);
|
3493
|
+
};
|
3494
|
+
Pan.prototype.detach = function() {
|
3495
|
+
$('body').removeClass('pan');
|
3496
|
+
this.touch_stop(null);
|
3497
|
+
$(this.eventDispatcher).unbind('mousedown', this.start);
|
3498
|
+
return $(this.eventDispatcher).unbind('touchstart', this.touch_start);
|
3499
|
+
};
|
3500
|
+
Pan.prototype.start = function(e) {
|
3501
|
+
$('body').addClass('panning');
|
3502
|
+
this._start_with(e.screenX, e.screenY);
|
3503
|
+
window.addEventListener('mousemove', this.pan, true);
|
3504
|
+
window.addEventListener('mouseup', this.stop, true);
|
3505
|
+
return this.stopEvent(e);
|
3506
|
+
};
|
3507
|
+
Pan.prototype.pan = function(e) {
|
3508
|
+
return this._pan_with(e.screenX, e.screenY);
|
3509
|
+
};
|
3510
|
+
Pan.prototype.stop = function(e) {
|
3511
|
+
$('body').removeClass('panning');
|
3512
|
+
window.removeEventListener('mousemove', this.pan, true);
|
3513
|
+
window.removeEventListener('mouseup', this.stop, true);
|
3514
|
+
window.removeEventListener('touchmove', this.pan, true);
|
3515
|
+
window.removeEventListener('touchend', this.stop, true);
|
3516
|
+
return this.stopEvent(e);
|
3517
|
+
};
|
3518
|
+
Pan.prototype.touch_start = function(e) {
|
3519
|
+
this._start_with(e.originalEvent.touches[0].clientX, e.originalEvent.touches[0].clientY);
|
3520
|
+
this.eventDispatcher.addEventListener('touchmove', this.touch_move, true);
|
3521
|
+
this.eventDispatcher.addEventListener('touchend', this.touch_stop, true);
|
3522
|
+
return e.originalEvent.preventDefault();
|
3523
|
+
};
|
3524
|
+
Pan.prototype.touch_move = function(e) {
|
3525
|
+
return this._pan_with(e.touches[0].clientX, e.touches[0].clientY);
|
3526
|
+
};
|
3527
|
+
Pan.prototype.touch_stop = function(e) {
|
3528
|
+
this.eventDispatcher.removeEventListener('touchmove', this.touch_move, true);
|
3529
|
+
return this.eventDispatcher.removeEventListener('touchend', this.touch_stop, true);
|
3530
|
+
};
|
3531
|
+
Pan.prototype._pan_with = function(x, y) {
|
3532
|
+
var dX, dY;
|
3533
|
+
dX = x - this.startX;
|
3534
|
+
dY = y - this.startY;
|
3535
|
+
this.startX = x;
|
3536
|
+
this.startY = y;
|
3537
|
+
return this.vp.panBy(dX, dY);
|
3538
|
+
};
|
3539
|
+
Pan.prototype._start_with = function(x, y) {
|
3540
|
+
this.startX = x;
|
3541
|
+
return this.startY = y;
|
3542
|
+
};
|
3543
|
+
return Pan;
|
3544
|
+
})();
|
3545
|
+
});
|
3546
|
+
}).call(this);
|
3547
|
+
(function() {
|
3548
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
|
3549
|
+
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
|
3550
|
+
function ctor() { this.constructor = child; }
|
3551
|
+
ctor.prototype = parent.prototype;
|
3552
|
+
child.prototype = new ctor;
|
3553
|
+
child.__super__ = parent.prototype;
|
3554
|
+
return child;
|
3555
|
+
};
|
3556
|
+
namespace('ZUI53.Tools', function(exports) {
|
3557
|
+
return exports.Zoom = (function() {
|
3558
|
+
__extends(Zoom, exports.Base);
|
3559
|
+
function Zoom(zui) {
|
3560
|
+
this.find_distance = __bind(this.find_distance, this);
|
3561
|
+
this.find_midpoint = __bind(this.find_midpoint, this);
|
3562
|
+
this.touch_move = __bind(this.touch_move, this);
|
3563
|
+
this._internal_gesture_end = __bind(this._internal_gesture_end, this);
|
3564
|
+
this._internal_gesture_start = __bind(this._internal_gesture_start, this);
|
3565
|
+
this.gesture_end = __bind(this.gesture_end, this);
|
3566
|
+
this.gesture_move = __bind(this.gesture_move, this);
|
3567
|
+
this.gesture_start = __bind(this.gesture_start, this);
|
3568
|
+
this.zoom = __bind(this.zoom, this);
|
3569
|
+
this.moz_touch_up = __bind(this.moz_touch_up, this);
|
3570
|
+
this.moz_touch_move = __bind(this.moz_touch_move, this);
|
3571
|
+
this.moz_touch_down = __bind(this.moz_touch_down, this);
|
3572
|
+
this.create_touch_index = __bind(this.create_touch_index, this);
|
3573
|
+
this.update_moz_touch = __bind(this.update_moz_touch, this);
|
3574
|
+
this.fetch_touch = __bind(this.fetch_touch, this);
|
3575
|
+
this.detach = __bind(this.detach, this);
|
3576
|
+
this.attach = __bind(this.attach, this); this.vp = zui;
|
3577
|
+
this.eventDispatcher = zui.viewport;
|
3578
|
+
this.use_capture = true;
|
3579
|
+
this.t1 = null;
|
3580
|
+
this.t2 = null;
|
3581
|
+
this.touch = {
|
3582
|
+
touches: [],
|
3583
|
+
touch_ids: []
|
3584
|
+
};
|
3585
|
+
}
|
3586
|
+
Zoom.prototype.attach = function() {
|
3587
|
+
$(this.eventDispatcher).mousewheel(this.zoom);
|
3588
|
+
this.eventDispatcher.addEventListener('gesturestart', this.gesture_start, this.use_capture);
|
3589
|
+
this.eventDispatcher.addEventListener('MozTouchDown', this.moz_touch_down, this.use_capture);
|
3590
|
+
return this.eventDispatcher.addEventListener('MozTouchUp', this.moz_touch_up, this.use_capture);
|
3591
|
+
};
|
3592
|
+
Zoom.prototype.detach = function() {
|
3593
|
+
$(this.eventDispatcher).unmousewheel(this.zoom);
|
3594
|
+
this.eventDispatcher.removeEventListener('gesturestart', this.gesture_start, this.use_capture);
|
3595
|
+
this.eventDispatcher.removeEventListener('MozTouchDown', this.moz_touch_down, this.use_capture);
|
3596
|
+
return this.eventDispatcher.removeEventListener('MozTouchUp', this.moz_touch_up, this.use_capture);
|
3597
|
+
};
|
3598
|
+
Zoom.prototype.fetch_touch = function(e, value) {
|
3599
|
+
if (this.t1 && this.t1.streamId === e.streamId) {
|
3600
|
+
this.t1 = value || e;
|
3601
|
+
} else {
|
3602
|
+
this.t2 = value || e;
|
3603
|
+
}
|
3604
|
+
return this.update_moz_touch();
|
3605
|
+
};
|
3606
|
+
Zoom.prototype.update_moz_touch = function() {
|
3607
|
+
var mp;
|
3608
|
+
if (this.t1 && this.t2) {
|
3609
|
+
try {
|
3610
|
+
return mp = this.find_midpoint({
|
3611
|
+
touches: [this.t1, this.t2]
|
3612
|
+
});
|
3613
|
+
} catch (e) {
|
3614
|
+
return console.log(e);
|
3615
|
+
}
|
3616
|
+
} else if (this.t1 || this.t2) {
|
3617
|
+
return console.log('only one');
|
3618
|
+
}
|
3619
|
+
};
|
3620
|
+
Zoom.prototype.create_touch_index = function(streamId) {
|
3621
|
+
var i;
|
3622
|
+
i = this.touch.touch_ids.indexOf(streamId);
|
3623
|
+
if (i < 0) {
|
3624
|
+
i = this.touch.touch_ids.length;
|
3625
|
+
this.touch.touch_ids[i] = streamId;
|
3626
|
+
}
|
3627
|
+
return i;
|
3628
|
+
};
|
3629
|
+
Zoom.prototype.moz_touch_down = function(e) {
|
3630
|
+
var i;
|
3631
|
+
this.touch_df = null;
|
3632
|
+
try {
|
3633
|
+
i = this.create_touch_index(e.streamId);
|
3634
|
+
this.touch.touches[i] = e;
|
3635
|
+
if (this.touch.touches.length === 2) {
|
3636
|
+
this._internal_gesture_start();
|
3637
|
+
return this.eventDispatcher.addEventListener('MozTouchMove', this.moz_touch_move, this.use_capture);
|
3638
|
+
}
|
3639
|
+
} catch (e) {
|
3640
|
+
return console.log(e);
|
3641
|
+
}
|
3642
|
+
};
|
3643
|
+
Zoom.prototype.moz_touch_move = function(e) {
|
3644
|
+
var d, i, s;
|
3645
|
+
i = this.create_touch_index(e.streamId);
|
3646
|
+
this.touch.touches[i] = e;
|
3647
|
+
this.touch_move(this.touch);
|
3648
|
+
d = this.find_distance(this.touch);
|
3649
|
+
if (this.touch_df) {
|
3650
|
+
s = this.touch_df * d;
|
3651
|
+
return this.gesture_move({
|
3652
|
+
scale: s
|
3653
|
+
});
|
3654
|
+
} else {
|
3655
|
+
return this.touch_df = 1 / d;
|
3656
|
+
}
|
3657
|
+
};
|
3658
|
+
Zoom.prototype.moz_touch_up = function(e) {
|
3659
|
+
var i;
|
3660
|
+
i = this.touch.touch_ids.indexOf(e.streamId);
|
3661
|
+
if (i > 0) {
|
3662
|
+
console.log("Removed: " + i);
|
3663
|
+
if (this.touch.touches.length === 2) {
|
3664
|
+
this._internal_gesture_end();
|
3665
|
+
this.eventDispatcher.removeEventListener('MozTouchMove', this.moz_touch_move, this.use_capture);
|
3666
|
+
}
|
3667
|
+
this.touch.touches.splice(i, 1);
|
3668
|
+
return this.touch.touch_ids.splice(i, 1);
|
3669
|
+
}
|
3670
|
+
};
|
3671
|
+
Zoom.prototype.zoom = function(e) {
|
3672
|
+
var delta, f;
|
3673
|
+
delta = e.wheelDelta || (e.detail * -1);
|
3674
|
+
f = 0.05;
|
3675
|
+
if (delta < 0) {
|
3676
|
+
f *= -1;
|
3677
|
+
}
|
3678
|
+
this.vp.zoomBy(f, e.clientX, e.clientY);
|
3679
|
+
return this.stopEvent(e);
|
3680
|
+
};
|
3681
|
+
Zoom.prototype.gesture_start = function(e) {
|
3682
|
+
this._internal_gesture_start();
|
3683
|
+
this.eventDispatcher.addEventListener('gesturechange', this.gesture_move, this.use_capture);
|
3684
|
+
this.eventDispatcher.addEventListener('gestureend', this.gesture_end, this.use_capture);
|
3685
|
+
this.eventDispatcher.addEventListener('touchmove', this.touch_move, this.use_capture);
|
3686
|
+
return e.preventDefault();
|
3687
|
+
};
|
3688
|
+
Zoom.prototype.gesture_move = function(e) {
|
3689
|
+
if (this.last_touch_p) {
|
3690
|
+
return this.vp.zoomSet(this.start_scale * e.scale, this.last_touch_p.e(1), this.last_touch_p.e(2));
|
3691
|
+
}
|
3692
|
+
};
|
3693
|
+
Zoom.prototype.gesture_end = function(e) {
|
3694
|
+
this.eventDispatcher.removeEventListener('touchmove', this.touch_move, this.use_capture);
|
3695
|
+
this.eventDispatcher.removeEventListener('gesturechange', this.gesture_move, this.use_capture);
|
3696
|
+
this.eventDispatcher.removeEventListener('gestureend', this.gesture_end, this.use_capture);
|
3697
|
+
return this._internal_gesture_end();
|
3698
|
+
};
|
3699
|
+
Zoom.prototype._internal_gesture_start = function() {
|
3700
|
+
this.makeExclusive();
|
3701
|
+
this.last_touch_p = null;
|
3702
|
+
return this.start_scale = this.vp.scale;
|
3703
|
+
};
|
3704
|
+
Zoom.prototype._internal_gesture_end = function() {
|
3705
|
+
return this.makeUnexclusive();
|
3706
|
+
};
|
3707
|
+
Zoom.prototype.touch_move = function(e) {
|
3708
|
+
var d, new_touch_p;
|
3709
|
+
if (this.last_touch_p) {
|
3710
|
+
new_touch_p = this.find_midpoint(e);
|
3711
|
+
d = new_touch_p.subtract(this.last_touch_p);
|
3712
|
+
this.last_touch_p = new_touch_p;
|
3713
|
+
return this.vp.panBy(d.e(1), d.e(2));
|
3714
|
+
} else {
|
3715
|
+
return this.last_touch_p = this.find_midpoint(e);
|
3716
|
+
}
|
3717
|
+
};
|
3718
|
+
Zoom.prototype.find_midpoint = function(e) {
|
3719
|
+
var d, p, p1, p2, t1, t2;
|
3720
|
+
t1 = e.touches[0];
|
3721
|
+
t2 = e.touches[1];
|
3722
|
+
p1 = $V([t1.clientX, t1.clientY, 1]);
|
3723
|
+
p2 = $V([t2.clientX, t2.clientY, 1]);
|
3724
|
+
d = p2.subtract(p1).multiply(0.5);
|
3725
|
+
return p = p1.add(d);
|
3726
|
+
};
|
3727
|
+
Zoom.prototype.find_distance = function(e) {
|
3728
|
+
var p1, p2, t1, t2;
|
3729
|
+
t1 = e.touches[0];
|
3730
|
+
t2 = e.touches[1];
|
3731
|
+
p1 = $V([t1.clientX, t1.clientY, 1]);
|
3732
|
+
p2 = $V([t2.clientX, t2.clientY, 1]);
|
3733
|
+
return p2.distanceFrom(p1);
|
3734
|
+
};
|
3735
|
+
return Zoom;
|
3736
|
+
})();
|
3737
|
+
});
|
3738
|
+
}).call(this);
|
3739
|
+
(function() {
|
3740
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
3741
|
+
namespace('ZUI53.Surfaces', function(exports) {
|
3742
|
+
return exports.SVG = (function() {
|
3743
|
+
function SVG(node) {
|
3744
|
+
this.node = node;
|
3745
|
+
this.apply = __bind(this.apply, this);
|
3746
|
+
}
|
3747
|
+
SVG.prototype.limits = function() {
|
3748
|
+
return [0.0001, 20000];
|
3749
|
+
};
|
3750
|
+
SVG.prototype.apply = function(panX, panY, scale) {
|
3751
|
+
var singleSVG;
|
3752
|
+
singleSVG = "translate(" + panX + ", " + panY + ") scale(" + scale + ", " + scale + ")";
|
3753
|
+
return $(this.node).attr("transform", singleSVG);
|
3754
|
+
};
|
3755
|
+
return SVG;
|
3756
|
+
})();
|
3757
|
+
});
|
3758
|
+
}).call(this);
|
3759
|
+
(function() {
|
3760
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
3761
|
+
namespace('ZUI53.Surfaces', function(exports) {
|
3762
|
+
return exports.CSS = (function() {
|
3763
|
+
function CSS(node) {
|
3764
|
+
this.node = node;
|
3765
|
+
this.apply = __bind(this.apply, this);
|
3766
|
+
$(this.node).transform({
|
3767
|
+
origin: ['0', '0']
|
3768
|
+
});
|
3769
|
+
$(this.node).css({
|
3770
|
+
'position': 'absolute'
|
3771
|
+
});
|
3772
|
+
}
|
3773
|
+
CSS.prototype.limits = function() {
|
3774
|
+
return null;
|
3775
|
+
};
|
3776
|
+
CSS.prototype.apply = function(panX, panY, scale) {
|
3777
|
+
return $(this.node).transform({
|
3778
|
+
matrix: [scale, 0.0, 0.0, scale, panX, panY]
|
3779
|
+
});
|
3780
|
+
};
|
3781
|
+
return CSS;
|
3782
|
+
})();
|
3783
|
+
});
|
3784
|
+
}).call(this);
|
3785
|
+
(function() {
|
3786
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
3787
|
+
namespace('ZUI53', function(exports) {
|
3788
|
+
return exports.Viewport = (function() {
|
3789
|
+
function Viewport(vp) {
|
3790
|
+
this.setTransformString = __bind(this.setTransformString, this);
|
3791
|
+
this.getTransformString = __bind(this.getTransformString, this);
|
3792
|
+
this.setPanAndScale = __bind(this.setPanAndScale, this);
|
3793
|
+
this.getPanAndScale = __bind(this.getPanAndScale, this);
|
3794
|
+
this.showBounds = __bind(this.showBounds, this);
|
3795
|
+
this.avp = __bind(this.avp, this);
|
3796
|
+
this.translateSurface = __bind(this.translateSurface, this);
|
3797
|
+
this.fitToLimits = __bind(this.fitToLimits, this);
|
3798
|
+
this.zoomSet = __bind(this.zoomSet, this);
|
3799
|
+
this.zoomBy = __bind(this.zoomBy, this);
|
3800
|
+
this.panBy = __bind(this.panBy, this);
|
3801
|
+
this.updateSurface = __bind(this.updateSurface, this);
|
3802
|
+
this.surfaceToLayer = __bind(this.surfaceToLayer, this);
|
3803
|
+
this.surfaceToClient = __bind(this.surfaceToClient, this);
|
3804
|
+
this.layerToSurface = __bind(this.layerToSurface, this);
|
3805
|
+
this.clientToSurface = __bind(this.clientToSurface, this);
|
3806
|
+
this.addLimits = __bind(this.addLimits, this);
|
3807
|
+
this.removeSurface = __bind(this.removeSurface, this);
|
3808
|
+
this.addSurface = __bind(this.addSurface, this);
|
3809
|
+
this.reset = __bind(this.reset, this);
|
3810
|
+
this.updateOffset = __bind(this.updateOffset, this); this.min_scale = null;
|
3811
|
+
this.max_scale = null;
|
3812
|
+
this.viewport = this.styleViewport(vp);
|
3813
|
+
this.surfaces = [];
|
3814
|
+
this.updateOffset();
|
3815
|
+
this.reset();
|
3816
|
+
$(vp).scroll(__bind(function(e) {
|
3817
|
+
var jVP;
|
3818
|
+
jVP = $(this.viewport);
|
3819
|
+
this.panBy(-jVP.scrollLeft(), -jVP.scrollTop());
|
3820
|
+
return jVP.scrollTop(0).scrollLeft(0);
|
3821
|
+
}, this));
|
3822
|
+
this.toolset = new ZUI53.Tools.Set(new ZUI53.Tools.Zoom(this));
|
3823
|
+
}
|
3824
|
+
Viewport.prototype.styleViewport = function(vp) {
|
3825
|
+
$(vp).css({
|
3826
|
+
'position': 'relative',
|
3827
|
+
'overflow': 'hidden',
|
3828
|
+
'width': '100%',
|
3829
|
+
'height': '100%'
|
3830
|
+
});
|
3831
|
+
return vp;
|
3832
|
+
};
|
3833
|
+
Viewport.prototype.updateOffset = function() {
|
3834
|
+
this.vpOffset = $(this.viewport).offset();
|
3835
|
+
this.vpOffset.left -= Number($(window.document).scrollLeft());
|
3836
|
+
this.vpOffset.top -= Number($(window.document).scrollTop());
|
3837
|
+
this.vpOffM = $M([[1, 0, this.vpOffset.left], [0, 1, this.vpOffset.top], [0, 0, 1]]);
|
3838
|
+
return this.vpOffM;
|
3839
|
+
};
|
3840
|
+
Viewport.prototype.reset = function() {
|
3841
|
+
this.zoomPos = 0;
|
3842
|
+
this.scale = 1.0;
|
3843
|
+
this.surfaceM = $M([[1, 0, 0], [0, 1, 0], [0, 0, 1]]);
|
3844
|
+
return this.updateSurface();
|
3845
|
+
};
|
3846
|
+
Viewport.prototype.addSurface = function(surface) {
|
3847
|
+
this.surfaces.push(surface);
|
3848
|
+
return this.addLimits(surface.limits());
|
3849
|
+
};
|
3850
|
+
Viewport.prototype.removeSurface = function(surface) {
|
3851
|
+
var i;
|
3852
|
+
i = this.surfaces.indexOf(surface);
|
3853
|
+
if (i >= 0) {
|
3854
|
+
return this.surfaces.splice(i, 1);
|
3855
|
+
}
|
3856
|
+
};
|
3857
|
+
Viewport.prototype.addLimits = function(limits) {
|
3858
|
+
if (!limits) {
|
3859
|
+
return;
|
3860
|
+
}
|
3861
|
+
if (this.min_scale || this.max_scale) {
|
3862
|
+
if (limits[0]) {
|
3863
|
+
this.min_scale = Math.max(limits[0], this.min_scale);
|
3864
|
+
}
|
3865
|
+
if (limits[1]) {
|
3866
|
+
return this.max_scale = Math.min(limits[1], this.max_scale);
|
3867
|
+
}
|
3868
|
+
} else {
|
3869
|
+
this.min_scale = limits[0];
|
3870
|
+
return this.max_scale = limits[1];
|
3871
|
+
}
|
3872
|
+
};
|
3873
|
+
Viewport.prototype.clientToSurface = function(x, y) {
|
3874
|
+
var sV, v;
|
3875
|
+
v = $V([x, y, 1]);
|
3876
|
+
return sV = this.surfaceM.inverse().multiply(this.updateOffset().inverse().multiply(v));
|
3877
|
+
};
|
3878
|
+
Viewport.prototype.layerToSurface = function(x, y) {
|
3879
|
+
var sV, v;
|
3880
|
+
v = $V([x, y, 1]);
|
3881
|
+
return sV = this.surfaceM.inverse().multiply(v);
|
3882
|
+
};
|
3883
|
+
Viewport.prototype.surfaceToClient = function(v) {
|
3884
|
+
return this.updateOffset().multiply(this.surfaceM.multiply(v));
|
3885
|
+
};
|
3886
|
+
Viewport.prototype.surfaceToLayer = function(v) {
|
3887
|
+
return this.surfaceM.multiply(v);
|
3888
|
+
};
|
3889
|
+
Viewport.prototype.updateSurface = function() {
|
3890
|
+
var node, v, _i, _len, _ref;
|
3891
|
+
v = this.getPanAndScale();
|
3892
|
+
_ref = this.surfaces;
|
3893
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
3894
|
+
node = _ref[_i];
|
3895
|
+
node.apply(v[0], v[1], v[2]);
|
3896
|
+
}
|
3897
|
+
return true;
|
3898
|
+
};
|
3899
|
+
Viewport.prototype.panBy = function(x, y) {
|
3900
|
+
this.translateSurface(x, y);
|
3901
|
+
return this.updateSurface();
|
3902
|
+
};
|
3903
|
+
Viewport.prototype.zoomBy = function(byF, clientX, clientY) {
|
3904
|
+
var newScale;
|
3905
|
+
newScale = this._pos_to_scale(this.zoomPos + byF);
|
3906
|
+
return this.zoomSet(newScale, clientX, clientY);
|
3907
|
+
};
|
3908
|
+
Viewport.prototype.zoomSet = function(newScale, clientX, clientY) {
|
3909
|
+
var c, dX, dY, scaleBy, sf;
|
3910
|
+
newScale = this.fitToLimits(newScale);
|
3911
|
+
this.zoomPos = this._scale_to_pos(newScale);
|
3912
|
+
if (newScale !== this.scale) {
|
3913
|
+
sf = this.clientToSurface(clientX, clientY);
|
3914
|
+
scaleBy = newScale / this.scale;
|
3915
|
+
this.surfaceM = this._scaleMatrix(this.surfaceM, scaleBy);
|
3916
|
+
this.scale = newScale;
|
3917
|
+
c = this.surfaceToClient(sf);
|
3918
|
+
dX = clientX - c.e(1);
|
3919
|
+
dY = clientY - c.e(2);
|
3920
|
+
this.translateSurface(dX, dY);
|
3921
|
+
}
|
3922
|
+
return this.updateSurface();
|
3923
|
+
};
|
3924
|
+
Viewport.prototype.fitToLimits = function(s) {
|
3925
|
+
if (this.min_scale && s < this.min_scale) {
|
3926
|
+
s = this.min_scale;
|
3927
|
+
} else if (this.max_scale && s > this.max_scale) {
|
3928
|
+
s = this.max_scale;
|
3929
|
+
}
|
3930
|
+
return s;
|
3931
|
+
};
|
3932
|
+
Viewport.prototype.translateSurface = function(x, y) {
|
3933
|
+
return this.surfaceM = this._translateMatrix(this.surfaceM, x, y);
|
3934
|
+
};
|
3935
|
+
Viewport.prototype._translateMatrix = function(m, x, y) {
|
3936
|
+
return m.add($M([[0, 0, x], [0, 0, y], [0, 0, 0]]));
|
3937
|
+
};
|
3938
|
+
Viewport.prototype._scaleMatrix = function(m, s) {
|
3939
|
+
return m.multiply($M([[s, 0, 0], [0, s, 0], [0, 0, 1]]));
|
3940
|
+
};
|
3941
|
+
Viewport.prototype._pos_to_scale = function(pos) {
|
3942
|
+
return Math.exp(pos);
|
3943
|
+
};
|
3944
|
+
Viewport.prototype._scale_to_pos = function(s) {
|
3945
|
+
return Math.log(s);
|
3946
|
+
};
|
3947
|
+
Viewport.prototype.avp = function() {
|
3948
|
+
var del, max, min;
|
3949
|
+
this.updateOffset();
|
3950
|
+
min = this.clientToSurface(this.vpOffset.left, this.vpOffset.top);
|
3951
|
+
max = this.clientToSurface(this.vpOffset.left + $(this.viewport).width(), this.vpOffset.top + $(this.viewport).height());
|
3952
|
+
del = max.subtract(min);
|
3953
|
+
return {
|
3954
|
+
x: min.e(1),
|
3955
|
+
y: min.e(2),
|
3956
|
+
width: del.e(1),
|
3957
|
+
height: del.e(2)
|
3958
|
+
};
|
3959
|
+
};
|
3960
|
+
Viewport.prototype._boundsCenter = function(b) {
|
3961
|
+
return {
|
3962
|
+
x: b.x + b.width / 2,
|
3963
|
+
y: b.y + b.height / 2
|
3964
|
+
};
|
3965
|
+
};
|
3966
|
+
Viewport.prototype.showBounds = function(evp) {
|
3967
|
+
var aC, avp, eC, exp, s;
|
3968
|
+
if (evp.width === 0 || evp.height === 0) {
|
3969
|
+
return;
|
3970
|
+
}
|
3971
|
+
avp = this.avp();
|
3972
|
+
s = Math.min(avp.width / evp.width, avp.height / evp.height);
|
3973
|
+
exp = 50 / s;
|
3974
|
+
evp.x -= exp;
|
3975
|
+
evp.y -= exp;
|
3976
|
+
evp.width += 2 * exp;
|
3977
|
+
evp.height += 2 * exp;
|
3978
|
+
s = Math.min(avp.width / evp.width, avp.height / evp.height);
|
3979
|
+
s = this.fitToLimits(s);
|
3980
|
+
eC = this._boundsCenter(evp);
|
3981
|
+
aC = this._boundsCenter(avp);
|
3982
|
+
this.setPanAndScale(-eC.x * s, -eC.y * s, s);
|
3983
|
+
this.translateSurface($(this.viewport).width() / 2, $(this.viewport).height() / 2);
|
3984
|
+
return this.updateSurface();
|
3985
|
+
};
|
3986
|
+
Viewport.prototype.getPanAndScale = function() {
|
3987
|
+
return [this.surfaceM.e(1, 3), this.surfaceM.e(2, 3), this.surfaceM.e(1, 1)];
|
3988
|
+
};
|
3989
|
+
Viewport.prototype.setPanAndScale = function(panX, panY, scale) {
|
3990
|
+
this.surfaceM = $M([[1, 0, 0], [0, 1, 0], [0, 0, 1]]);
|
3991
|
+
this.translateSurface(panX, panY);
|
3992
|
+
this.surfaceM = this._scaleMatrix(this.surfaceM, scale);
|
3993
|
+
this.scale = scale;
|
3994
|
+
return this.zoomPos = this._scale_to_pos(scale);
|
3995
|
+
};
|
3996
|
+
Viewport.prototype.getTransformString = function() {
|
3997
|
+
return this.getPanAndScale().join(',');
|
3998
|
+
};
|
3999
|
+
Viewport.prototype.setTransformString = function(str) {
|
4000
|
+
var panX, panY, scale, v;
|
4001
|
+
if (!str) {
|
4002
|
+
return;
|
4003
|
+
}
|
4004
|
+
v = str.split(',');
|
4005
|
+
panX = Number(v[0]);
|
4006
|
+
panY = Number(v[1]);
|
4007
|
+
scale = Number(v[2]);
|
4008
|
+
this.setPanAndScale(panX, panY, scale);
|
4009
|
+
return this.updateSurface();
|
4010
|
+
};
|
4011
|
+
return Viewport;
|
4012
|
+
})();
|
4013
|
+
});
|
4014
|
+
}).call(this);
|