Mercurial > hg > digilib
annotate client/digitallibrary/jquery/jquery.digilib.geometry.js @ 800:ac1d6b056a6f jquery
geometry: overlapsRect() plus some comment clarifications
author | hertzhaft |
---|---|
date | Sun, 20 Feb 2011 11:55:51 +0100 |
parents | 912519475259 |
children | 1f9940d4bd35 |
rev | line source |
---|---|
778 | 1 /** required digilib geometry plugin |
2 */ | |
3 | |
4 (function($) { | |
5 //var dlGeometry = function() { | |
6 /* | |
7 * Size class | |
8 */ | |
9 var size = function(w, h) { | |
10 var that; | |
11 if (typeof w === "object") { | |
12 // assume its size | |
13 that = { | |
14 width : w.width, | |
15 height : w.height | |
16 }; | |
17 } else { | |
18 that = { | |
19 width : parseFloat(w), | |
20 height : parseFloat(h) | |
21 }; | |
22 } | |
23 that.equals = function(other) { | |
24 return (this.width === other.width && this.height === other.height); | |
25 }; | |
26 that.toString = function() { | |
27 return (this.width + "x" + this.height); | |
28 }; | |
29 return that; | |
30 }; | |
31 | |
32 /* | |
33 * Position class | |
34 */ | |
35 var position = function(x, y) { | |
36 var that; | |
37 if (typeof x === "object") { | |
38 if (x instanceof jQuery) { | |
39 // jQuery object | |
40 var pos = x.offset(); | |
41 that = { | |
42 x : pos.left, | |
43 y : pos.top | |
44 }; | |
45 } else { | |
46 if (x.x != null) { | |
47 // position object | |
48 that = { | |
49 x : x.x, | |
50 y : x.y | |
51 }; | |
52 } | |
53 if (x.pageX != null) { | |
54 // event object | |
55 that = { | |
56 x : x.pageX, | |
57 y : x.pageY | |
58 }; | |
59 } | |
60 } | |
61 } else { | |
62 that = { | |
63 x : parseFloat(x), | |
64 y : parseFloat(y) | |
65 }; | |
66 } | |
67 that.equals = function(other) { | |
68 return (this.x === other.x && this.y === other.y); | |
69 }; | |
70 // add position other to this | |
71 that.add = function(other) { | |
72 this.x += other.x; | |
73 this.y += other.y; | |
74 return this; | |
75 }; | |
76 // returns negative position | |
77 that.neg = function() { | |
78 return position({ | |
79 x : -this.x, | |
80 y : -this.y | |
81 }); | |
82 }; | |
83 // returns new position that is the difference between this and other | |
84 that.delta = function(other) { | |
85 return position({ | |
86 x : other.x - this.x, | |
87 y : other.y - this.y | |
88 }); | |
89 }; | |
90 // adjusts position $elem to this position | |
91 that.adjustDiv = function($elem) { | |
92 $elem.offset({ | |
93 left : this.x, | |
94 top : this.y | |
95 }); | |
96 }; | |
97 // returns distance of this position to pos (length if pos == null) | |
98 that.distance = function(pos) { | |
99 if (pos == null) { | |
100 pos = { | |
101 x : 0, | |
102 y : 0 | |
103 }; | |
104 } | |
105 var dx = pos.x - this.x; | |
106 var dy = pos.y - this.y; | |
107 return Math.sqrt(dx * dx + dy * dy); | |
108 }; | |
109 that.toString = function() { | |
110 return (this.x + "," + this.y); | |
111 }; | |
112 return that; | |
113 }; | |
114 /* | |
115 * Rectangle class | |
116 */ | |
117 var rectangle = function(x, y, w, h) { | |
118 var that = {}; | |
119 if (typeof x === "object") { | |
120 if (x instanceof jQuery) { | |
121 // jQuery object | |
122 var pos = x.offset(); | |
123 that = { | |
124 x : pos.left, | |
125 y : pos.top, | |
126 width : x.width(), | |
127 height : x.height() | |
128 }; | |
129 } else if (y == null) { | |
130 // assume x is rectangle | |
131 that = { | |
132 x : x.x, | |
133 y : x.y, | |
134 width : x.width, | |
135 height : x.height | |
136 }; | |
137 } else { | |
138 // assume x and y are Position | |
139 that = { | |
140 x : Math.min(x.x, y.x), | |
141 y : Math.min(x.y, y.y), | |
142 width : Math.abs(y.x - x.x), | |
143 height : Math.abs(y.y - x.y) | |
144 }; | |
145 } | |
146 } else { | |
147 that = { | |
148 x : parseFloat(x), | |
149 y : parseFloat(y), | |
150 width : parseFloat(w), | |
151 height : parseFloat(h) | |
152 }; | |
153 } | |
154 // returns a copy of this Rectangle | |
155 that.copy = function() { | |
156 return rectangle(this); | |
157 }; | |
158 // returns the position of this Rectangle | |
159 that.getPosition = function() { | |
160 return position(this); | |
161 }; | |
162 // returns the size of this Rectangle | |
163 that.getSize = function() { | |
164 return size(this); | |
165 }; | |
166 // returns the upper left corner position | |
167 that.getPt1 = that.getPosition; | |
168 // returns the lower right corner position of this Rectangle | |
169 that.getPt2 = function() { | |
170 return position({ | |
171 x : this.x + this.width, | |
172 y : this.y + this.height | |
173 }); | |
174 }; | |
175 // sets the upper left corner position to pos | |
176 that.setPosition = function(pos) { | |
177 this.x = pos.x; | |
178 this.y = pos.y; | |
179 return this; | |
180 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
181 // adds pos to the position |
778 | 182 that.setPt1 = that.setPosition; // TODO: not really the same |
183 that.addPosition = function(pos) { | |
184 this.x += pos.x; | |
185 this.y += pos.y; | |
186 return this; | |
187 }; | |
188 // sets the lower right corner to position pos | |
189 that.setPt2 = function(pos) { | |
190 this.width = pos.x - this.x; | |
191 this.height = pos.y - this.y; | |
192 return this; | |
193 }; | |
194 // returns the center position of this Rectangle | |
195 that.getCenter = function() { | |
196 return position({ | |
197 x : this.x + this.width / 2, | |
198 y : this.y + this.height / 2 | |
199 }); | |
200 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
201 // moves this rectangle's center to position pos |
778 | 202 that.setCenter = function(pos) { |
203 this.x = pos.x - this.width / 2; | |
204 this.y = pos.y - this.height / 2; | |
205 return this; | |
206 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
207 // returns true if both rectangles have equal position and proportion |
778 | 208 that.equals = function(other) { |
209 var eq = (this.x === other.x && this.y === other.y && this.width === other.width); | |
210 return eq; | |
211 }; | |
212 // returns the area of this Rectangle | |
213 that.getArea = function() { | |
214 return (this.width * this.height); | |
215 }; | |
216 // eliminates negative width and height | |
217 that.normalize = function() { | |
218 var p = this.getPt2(); | |
219 this.x = Math.min(this.x, p.x); | |
220 this.y = Math.min(this.y, p.y); | |
221 this.width = Math.abs(this.width); | |
222 this.height = Math.abs(this.height); | |
223 return this; | |
224 }; | |
225 // returns if Position "pos" lies inside of this rectangle | |
226 that.containsPosition = function(pos) { | |
227 var ct = ((pos.x >= this.x) && (pos.y >= this.y) | |
228 && (pos.x <= this.x + this.width) && (pos.y <= this.y | |
229 + this.height)); | |
230 return ct; | |
231 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
232 // returns true if rectangle "rect" is contained in this rectangle |
778 | 233 that.containsRect = function(rect) { |
234 return (this.containsPosition(rect.getPt1()) && this | |
235 .containsPosition(rect.getPt2())); | |
236 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
237 // returns true if rectangle "rect" and this rectangle overlap |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
238 that.overlapsRect = function(rect) { |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
239 return (this.containsPosition(rect.getPt1()) || this |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
240 .containsPosition(rect.getPt2())); |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
241 }; |
778 | 242 // changes this rectangle's x/y values so it stays inside of rectangle |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
243 // "rect", keeping the proportions |
778 | 244 that.stayInside = function(rect) { |
245 if (this.x < rect.x) { | |
246 this.x = rect.x; | |
247 } | |
248 if (this.y < rect.y) { | |
249 this.y = rect.y; | |
250 } | |
251 if (this.x + this.width > rect.x + rect.width) { | |
252 this.x = rect.x + rect.width - this.width; | |
253 } | |
254 if (this.y + this.height > rect.y + rect.height) { | |
255 this.y = rect.y + rect.height - this.height; | |
256 } | |
257 return this; | |
258 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
259 // clips this rectangle so it stays inside of rectangle "rect" |
778 | 260 that.clipTo = function(rect) { |
261 var p1 = rect.getPt1(); | |
262 var p2 = rect.getPt2(); | |
263 var this2 = this.getPt2(); | |
264 this.setPosition(position(Math.max(this.x, p1.x), Math.max(this.y, p1.y))); | |
265 this.setPt2(position(Math.min(this2.x, p2.x), Math.min(this2.y, p2.y))); | |
266 return this; | |
267 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
268 // returns the intersection of rectangle "rect" and this one |
778 | 269 that.intersect = function(rect) { |
270 // FIX ME: not really, it should return null if there is no overlap | |
271 var sec = rect.copy(); | |
272 if (sec.x < this.x) { | |
273 sec.width = sec.width - (this.x - sec.x); | |
274 sec.x = this.x; | |
275 } | |
276 if (sec.y < this.y) { | |
277 sec.height = sec.height - (this.y - sec.y); | |
278 sec.y = this.y; | |
279 } | |
280 if (sec.x + sec.width > this.x + this.width) { | |
281 sec.width = (this.x + this.width) - sec.x; | |
282 } | |
283 if (sec.y + sec.height > this.y + this.height) { | |
284 sec.height = (this.y + this.height) - sec.y; | |
285 } | |
286 return sec; | |
287 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
288 // returns a copy of rectangle "rect" that fits into this one |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
289 // (moving it first) |
778 | 290 that.fit = function(rect) { |
291 var sec = rect.copy(); | |
292 sec.x = Math.max(sec.x, this.x); | |
293 sec.y = Math.max(sec.y, this.x); | |
294 if (sec.x + sec.width > this.x + this.width) { | |
295 sec.x = this.x + this.width - sec.width; | |
296 } | |
297 if (sec.y + sec.height > this.y + this.height) { | |
298 sec.y = this.y + this.height - sec.height; | |
299 } | |
300 return sec.intersect(this); | |
301 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
302 // adjusts position and size of jQuery element "$elem" to this rectangle |
778 | 303 that.adjustDiv = function($elem) { |
304 $elem.offset({ | |
305 left : this.x, | |
306 top : this.y | |
307 }); | |
308 $elem.width(this.width).height(this.height); | |
309 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
310 // returns position and size of this rectangle in css-compatible format |
778 | 311 that.getAsCss = function() { |
312 return { | |
313 left : this.x, | |
314 top : this.y, | |
315 width : this.width, | |
316 height : this.height | |
317 }; | |
318 }; | |
800
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
319 // returns position and size of this rectangle formatted for SVG attributes |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
320 that.getAsSvg = function() { |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
321 return [this.x, this.y, this.width, this.height].join(" "); |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
322 }; |
ac1d6b056a6f
geometry: overlapsRect() plus some comment clarifications
hertzhaft
parents:
786
diff
changeset
|
323 // returns size and position of this rectangle formatted for ??? (w x h@x,y) |
778 | 324 that.toString = function() { |
325 return this.width + "x" + this.height + "@" + this.x + "," + this.y; | |
326 }; | |
327 return that; | |
328 }; | |
329 | |
330 /* | |
331 * Transform class | |
332 * | |
333 * defines a class of affine transformations | |
334 */ | |
335 var transform = function(spec) { | |
336 var that = { | |
337 m00 : 1.0, | |
338 m01 : 0.0, | |
339 m02 : 0.0, | |
340 m10 : 0.0, | |
341 m11 : 1.0, | |
342 m12 : 0.0, | |
343 m20 : 0.0, | |
344 m21 : 0.0, | |
345 m22 : 1.0 | |
346 }; | |
347 if (spec) { | |
348 jQuery.extend(that, spec); | |
349 } | |
350 ; | |
351 that.concat = function(trafA) { | |
352 // add Transform trafA to this Transform (i.e. this = trafC = trafA | |
353 // * this) | |
354 var trafC = {}; | |
355 for ( var i = 0; i < 3; i++) { | |
356 for ( var j = 0; j < 3; j++) { | |
357 var c = 0.0; | |
358 for ( var k = 0; k < 3; k++) { | |
359 c += trafA["m" + i + k] * this["m" + k + j]; | |
360 } | |
361 trafC["m" + i + j] = c; | |
362 } | |
363 } | |
364 jQuery.extend(this, trafC); | |
365 return this; | |
366 }; | |
367 that.transform = function(rect) { | |
368 // returns transformed Rectangle or Position with this Transform | |
369 // applied | |
370 var x = this.m00 * rect.x + this.m01 * rect.y + this.m02; | |
371 var y = this.m10 * rect.x + this.m11 * rect.y + this.m12; | |
372 var pt = position(x, y); | |
373 if (rect.width) { | |
374 // transform the other corner point | |
375 var pt2 = this.transform(rect.getPt2()); | |
376 return rectangle(pt, pt2); | |
377 } | |
378 return pt; | |
379 }; | |
380 that.invtransform = function(rect) { | |
381 // returns transformed Rectangle or Position with the inverse of | |
382 // this Transform applied | |
383 var det = this.m00 * this.m11 - this.m01 * this.m10; | |
384 var x = (this.m11 * rect.x - this.m01 * rect.y - this.m11 | |
385 * this.m02 + this.m01 * this.m12) | |
386 / det; | |
387 var y = (-this.m10 * rect.x + this.m00 * rect.y + this.m10 | |
388 * this.m02 - this.m00 * this.m12) | |
389 / det; | |
390 var pt = position(x, y); | |
391 if (rect.width) { | |
392 // transform the other corner point | |
393 var pt2 = this.invtransform(rect.getPt2()); | |
394 return rectangle(pt, pt2); | |
395 } | |
396 return pt; | |
397 }; | |
398 that.toString = function(pretty) { | |
399 var s = '['; | |
400 if (pretty) | |
401 s += '\n'; | |
402 for ( var i = 0; i < 3; ++i) { | |
403 s += '['; | |
404 for ( var j = 0; j < 3; ++j) { | |
405 if (j) | |
406 s += ','; | |
407 s += this['m' + i + j]; | |
408 } | |
409 s += ']'; | |
410 if (pretty) | |
411 s += '\n'; | |
412 } | |
413 s += ']'; | |
414 if (pretty) | |
415 s += '\n'; | |
416 return s; | |
417 }; | |
418 // add class methods to instance | |
419 that.getRotation = transform.getRotation; | |
420 that.getRotationAround = transform.getRotationAround; | |
421 that.getTranslation = transform.getTranslation; | |
422 that.getMirror = transform.getMirror; | |
423 that.getScale = transform.getScale; | |
424 | |
425 return that; | |
426 }; | |
427 | |
428 transform.getRotation = function(angle) { | |
429 // returns a Transform that is a rotation by angle degrees around [0,0] | |
430 if (angle !== 0) { | |
431 var t = Math.PI * parseFloat(angle) / 180.0; | |
432 var cost = Math.cos(t); | |
433 var sint = Math.sin(t); | |
434 var traf = { | |
435 m00 : cost, | |
436 m01 : -sint, | |
437 m10 : sint, | |
438 m11 : cost | |
439 }; | |
440 return transform(traf); | |
441 } | |
442 return transform(); | |
443 }; | |
444 | |
445 transform.getRotationAround = function(angle, pos) { | |
446 // returns a Transform that is a rotation by angle degrees around pos | |
447 var traf = transform.getTranslation(pos.neg()); | |
448 traf.concat(transform.getRotation(angle)); | |
449 traf.concat(transform.getTranslation(pos)); | |
450 return traf; | |
451 }; | |
452 | |
453 transform.getTranslation = function(pos) { | |
454 // returns a Transform that is a translation by [pos.x, pos,y] | |
455 var traf = { | |
456 m02 : pos.x, | |
457 m12 : pos.y | |
458 }; | |
459 return transform(traf); | |
460 }; | |
461 | |
462 transform.getMirror = function(type) { | |
463 // returns a Transform that is a mirror about the axis type | |
464 if (type === 'x') { | |
465 var traf = { | |
466 m00 : 1, | |
467 m11 : -1 | |
468 }; | |
469 } else { | |
470 var traf = { | |
471 m00 : -1, | |
472 m11 : 1 | |
473 }; | |
474 } | |
475 return transform(traf); | |
476 }; | |
477 | |
478 transform.getScale = function(size) { | |
479 // returns a Transform that is a scale by [size.width, size.height] | |
480 var traf = { | |
481 m00 : size.width, | |
482 m11 : size.height | |
483 }; | |
484 return transform(traf); | |
485 }; | |
486 | |
779 | 487 // export constructor functions to digilib plugin |
786
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
488 var geometry = { |
779 | 489 size : size, |
490 position : position, | |
491 rectangle : rectangle, | |
492 transform : transform | |
786
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
493 }; |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
494 // install function called by digilib on plugin object |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
495 var install = function() { |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
496 // add constructor object to fn |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
497 this.fn.geometry = geometry; |
778 | 498 }; |
786
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
499 // digilib plugin object |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
500 var plugin = { |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
501 name : 'geometry', |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
502 install : install, |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
503 fn : {}, |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
504 // TODO: remove old init |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
505 init : init |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
506 }; |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
507 // TODO: remove old version of init returning contructor |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
508 var init = function () { |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
509 return geometry; |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
510 }; |
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
511 // plug into digilib |
779 | 512 if ($.fn.digilib == null) { |
513 $.error("jquery.digilib.geometry must be loaded after jquery.digilib!"); | |
514 } else { | |
786
912519475259
documentation for new plugin api in jquery-digilib-plugin.txt.
robcast
parents:
781
diff
changeset
|
515 $.fn.digilib('plugin', plugin); |
779 | 516 } |
778 | 517 })(jQuery); |