-
Notifications
You must be signed in to change notification settings - Fork 560
/
rect-manager.js
181 lines (165 loc) · 6.16 KB
/
rect-manager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
var Crafty = require('../core/core.js');
/**@
* #Crafty.rectManager
* @category 2D
* @kind CoreObj
*
* Collection of methods for handling rectangles
*/
Crafty.extend({
/** recManager: an object for managing dirty rectangles. */
rectManager: {
/** Finds smallest rectangles that overlaps a and b, merges them into target */
merge: function (a, b, target) {
if (typeof target === 'undefined')
target = {};
// Doing it in this order means we can use either a or b as the target, with no conflict
target._h = Math.max(a._y + a._h, b._y + b._h);
target._w = Math.max(a._x + a._w, b._x + b._w);
target._x = Math.min(a._x, b._x);
target._y = Math.min(a._y, b._y);
target._w -= target._x;
target._h -= target._y;
return target;
},
/**@
* #Crafty.rectManager.overlap
* @comp Crafty.rectManager
* @kind Method
*
* @sign public Boolean Crafty.rectManager.overlap(Object rectA, Object rectA)
* @param rectA - An object that must have the `_x, _y, _w, _h` values as properties
* @param rectB - An object that must have the `_x, _y, _w, _h` values as properties
* @return true if the rectangles overlap; false otherwise
*
* Checks whether two rectangles overlap.
*/
overlap: function (rectA, rectB) {
return (rectA._x < rectB._x + rectB._w && rectA._x + rectA._w > rectB._x &&
rectA._y < rectB._y + rectB._h && rectA._y + rectA._h > rectB._y);
},
/**@
* #Crafty.rectManager.integerBounds
* @comp Crafty.rectManager
* @kind Method
*
* @sign public Boolean Crafty.rectManager.integerBounds(Object rect)
* @param rect - An object that must have the `_x, _y, _w, _h` values as properties
* @return An enclosing rectangle with integer coordinates
*
* Calculate the smallest rectangle with integer coordinates that encloses the specified rectangle,
* modifying the passed object to have those bounds.
*/
integerBounds: function(rect) {
// Truncate to next, lower integer, but don't modify if already integer
rect._x = Math.floor(rect._x);
rect._y = Math.floor(rect._y);
// Ceil to next, higher integer, but don't modify if already integer
rect._w = Math.ceil(rect._w);
rect._h = Math.ceil(rect._h);
return rect;
},
/**@
* #Crafty.rectManager.mergeSet
* @comp Crafty.rectManager
* @kind Method
*
* @sign public Object Crafty.rectManager.mergeSet(Object set)
* @param set - an array of rectangular regions
*
* Merge any consecutive, overlapping rects into each other.
* Its an optimization for the redraw regions.
*
* The order of set isn't strictly meaningful,
* but overlapping objects will often cause each other to change,
* and so might be consecutive.
*/
mergeSet: function (set) {
if (set.length < 2) return set;
var i = set.length - 1;
while (i--) {
// If current and next overlap, merge them together into the first, removing the second
if (this.overlap(set[i], set[i + 1])) {
this.merge(set[i], set[i + 1], set[i]);
set.splice(i + 1, 1);
}
}
return set;
},
/**@
* #Crafty.rectManager.boundingRect
* @comp Crafty.rectManager
* @kind Method
*
* @sign public Crafty.rectManager.boundingRect(set)
* @param set - An array of rectangles
*
* - Calculate the common bounding rect of multiple canvas entities.
* - Returns coords
*/
boundingRect: function (set) {
if (!set || !set.length) return;
var i = 1,
l = set.length,
current, master = set[0],
tmp;
master = [master._x, master._y, master._x + master._w, master._y + master._h];
while (i < l) {
current = set[i];
tmp = [current._x, current._y, current._x + current._w, current._y + current._h];
if (tmp[0] < master[0]) master[0] = tmp[0];
if (tmp[1] < master[1]) master[1] = tmp[1];
if (tmp[2] > master[2]) master[2] = tmp[2];
if (tmp[3] > master[3]) master[3] = tmp[3];
i++;
}
tmp = master;
master = {
_x: tmp[0],
_y: tmp[1],
_w: tmp[2] - tmp[0],
_h: tmp[3] - tmp[1]
};
return master;
},
// Crafty.rectManager._rectPool
//
// This is a private object used internally by 2D methods
// Cascade and _attr need to keep track of an entity's old position,
// but we want to avoid creating temp objects every time an attribute is set.
// The solution is to have a pool of objects that can be reused.
//
// The current implementation makes a BIG ASSUMPTION: that if multiple rectangles are requested,
// the later one is recycled before any preceding ones. This matches how they are used in the code.
// Each rect is created by a triggered event, and will be recycled by the time the event is complete.
_pool: (function () {
var pool = [],
pointer = 0;
return {
get: function (x, y, w, h) {
if (pool.length <= pointer)
pool.push({});
var r = pool[pointer++];
r._x = x;
r._y = y;
r._w = w;
r._h = h;
return r;
},
copy: function (o) {
if (pool.length <= pointer)
pool.push({});
var r = pool[pointer++];
r._x = o._x;
r._y = o._y;
r._w = o._w;
r._h = o._h;
return r;
},
recycle: function (o) {
pointer--;
}
};
})(),
}
});