OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 part of dart.math; | |
5 | |
6 /** | |
7 * A base class for representing two-dimensional axis-aligned rectangles. | |
8 * | |
9 * This rectangle uses a left-handed Cartesian coordinate system, with x | |
10 * directed to the right and y directed down, as per the convention in 2D | |
11 * computer graphics. | |
12 * | |
13 * See also: | |
14 * [W3C Coordinate Systems Specification](http://www.w3.org/TR/SVG/coords.htm
l#InitialCoordinateSystem). | |
15 * | |
16 * The rectangle is the set of points with representable coordinates greater | |
17 * than or equal to left/top, and with distance to left/top no greater than | |
18 * width/height (to the limit of the precission of the coordinates). | |
19 */ | |
20 abstract class _RectangleBase<T extends num> { | |
21 const _RectangleBase(); | |
22 | |
23 /** The x-coordinate of the left edge. */ | |
24 T get left; | |
25 /** The y-coordinate of the top edge. */ | |
26 T get top; | |
27 /** The width of the rectangle. */ | |
28 T get width; | |
29 /** The height of the rectangle. */ | |
30 T get height; | |
31 | |
32 /** The x-coordinate of the right edge. */ | |
33 T get right => left + width; | |
34 /** The y-coordinate of the bottom edge. */ | |
35 T get bottom => top + height; | |
36 | |
37 String toString() { | |
38 return 'Rectangle ($left, $top) $width x $height'; | |
39 } | |
40 | |
41 bool operator ==(other) { | |
42 if (other is !Rectangle) return false; | |
43 // TODO(rnystrom): Type promotion doesn't currently promote the [other] | |
44 // to Rectangle from the above line, so do it explicitly here to avoid a | |
45 // dynamic send and work around: | |
46 // https://github.com/dart-lang/sdk/issues/27551 | |
47 var otherRect = other as Rectangle; | |
48 return left == otherRect.left && | |
49 top == otherRect.top && | |
50 right == otherRect.right && | |
51 bottom == otherRect.bottom; | |
52 } | |
53 | |
54 int get hashCode => _JenkinsSmiHash.hash4(left.hashCode, top.hashCode, | |
55 right.hashCode, bottom.hashCode); | |
56 | |
57 /** | |
58 * Computes the intersection of `this` and [other]. | |
59 * | |
60 * The intersection of two axis-aligned rectangles, if any, is always another | |
61 * axis-aligned rectangle. | |
62 * | |
63 * Returns the intersection of this and `other`, or `null` if they don't | |
64 * intersect. | |
65 */ | |
66 Rectangle<T> intersection(Rectangle<T> other) { | |
67 var x0 = max(left, other.left); | |
68 var x1 = min(left + width, other.left + other.width); | |
69 | |
70 if (x0 <= x1) { | |
71 var y0 = max(top, other.top); | |
72 var y1 = min(top + height, other.top + other.height); | |
73 | |
74 if (y0 <= y1) { | |
75 return new Rectangle<T>(x0, y0, x1 - x0, y1 - y0); | |
76 } | |
77 } | |
78 return null; | |
79 } | |
80 | |
81 | |
82 /** | |
83 * Returns true if `this` intersects [other]. | |
84 */ | |
85 bool intersects(Rectangle<num> other) { | |
86 return (left <= other.left + other.width && | |
87 other.left <= left + width && | |
88 top <= other.top + other.height && | |
89 other.top <= top + height); | |
90 } | |
91 | |
92 /** | |
93 * Returns a new rectangle which completely contains `this` and [other]. | |
94 */ | |
95 Rectangle<T> boundingBox(Rectangle<T> other) { | |
96 var right = max(this.left + this.width, other.left + other.width); | |
97 var bottom = max(this.top + this.height, other.top + other.height); | |
98 | |
99 var left = min(this.left, other.left); | |
100 var top = min(this.top, other.top); | |
101 | |
102 return new Rectangle<T>(left, top, right - left, bottom - top); | |
103 } | |
104 | |
105 /** | |
106 * Tests whether `this` entirely contains [another]. | |
107 */ | |
108 bool containsRectangle(Rectangle<num> another) { | |
109 return left <= another.left && | |
110 left + width >= another.left + another.width && | |
111 top <= another.top && | |
112 top + height >= another.top + another.height; | |
113 } | |
114 | |
115 /** | |
116 * Tests whether [another] is inside or along the edges of `this`. | |
117 */ | |
118 bool containsPoint(Point<num> another) { | |
119 return another.x >= left && | |
120 another.x <= left + width && | |
121 another.y >= top && | |
122 another.y <= top + height; | |
123 } | |
124 | |
125 Point<T> get topLeft => new Point<T>(this.left, this.top); | |
126 Point<T> get topRight => new Point<T>(this.left + this.width, this.top); | |
127 Point<T> get bottomRight => new Point<T>(this.left + this.width, | |
128 this.top + this.height); | |
129 Point<T> get bottomLeft => new Point<T>(this.left, | |
130 this.top + this.height); | |
131 } | |
132 | |
133 | |
134 /** | |
135 * A class for representing two-dimensional rectangles whose properties are | |
136 * immutable. | |
137 */ | |
138 class Rectangle<T extends num> extends _RectangleBase<T> { | |
139 final T left; | |
140 final T top; | |
141 final T width; | |
142 final T height; | |
143 | |
144 /** | |
145 * Create a rectangle spanned by `(left, top)` and `(left+width, top+height)`. | |
146 * | |
147 * The rectangle contains the points | |
148 * with x-coordinate between `left` and `left + width`, and | |
149 * with y-coordinate between `top` and `top + height`, both inclusive. | |
150 * | |
151 * The `width` and `height` should be non-negative. | |
152 * If `width` or `height` are negative, they are clamped to zero. | |
153 * | |
154 * If `width` and `height` are zero, the "rectangle" comprises only the single | |
155 * point `(left, top)`. | |
156 */ | |
157 const Rectangle(this.left, this.top, T width, T height) | |
158 : this.width = (width < 0) ? -width * 0 : width, // Inline _clampToZero. | |
159 this.height = (height < 0) ? -height * 0 : height; | |
160 | |
161 /** | |
162 * Create a rectangle spanned by the points [a] and [b]; | |
163 * | |
164 * The rectangle contains the points | |
165 * with x-coordinate between `a.x` and `b.x`, and | |
166 * with y-coordinate between `a.y` and `b.y`, both inclusive. | |
167 * | |
168 * If the distance between `a.x` and `b.x` is not representable | |
169 * (which can happen if one or both is a double), | |
170 * the actual right edge might be slightly off from `max(a.x, b.x)`. | |
171 * Similar for the y-coordinates and the bottom edge. | |
172 */ | |
173 factory Rectangle.fromPoints(Point<T> a, Point<T> b) { | |
174 T left = min(a.x, b.x); | |
175 T width = max(a.x, b.x) - left; | |
176 T top = min(a.y, b.y); | |
177 T height = max(a.y, b.y) - top; | |
178 return new Rectangle<T>(left, top, width, height); | |
179 } | |
180 } | |
181 | |
182 /** | |
183 * A class for representing two-dimensional axis-aligned rectangles with mutable | |
184 * properties. | |
185 */ | |
186 class MutableRectangle<T extends num> extends _RectangleBase<T> | |
187 implements Rectangle<T> { | |
188 | |
189 /** | |
190 * The x-coordinate of the left edge. | |
191 * | |
192 * Setting the value will move the rectangle without changing its width. | |
193 */ | |
194 T left; | |
195 /** | |
196 * The y-coordinate of the left edge. | |
197 * | |
198 * Setting the value will move the rectangle without changing its height. | |
199 */ | |
200 T top; | |
201 T _width; | |
202 T _height; | |
203 | |
204 /** | |
205 * Create a mutable rectangle spanned by `(left, top)` and | |
206 * `(left+width, top+height)`. | |
207 * | |
208 * The rectangle contains the points | |
209 * with x-coordinate between `left` and `left + width`, and | |
210 * with y-coordinate between `top` and `top + height`, both inclusive. | |
211 * | |
212 * The `width` and `height` should be non-negative. | |
213 * If `width` or `height` are negative, they are clamped to zero. | |
214 * | |
215 * If `width` and `height` are zero, the "rectangle" comprises only the single | |
216 * point `(left, top)`. | |
217 */ | |
218 MutableRectangle(this.left, this.top, T width, T height) | |
219 : this._width = (width < 0) ? _clampToZero/*<T>*/(width) : width, | |
220 this._height = (height < 0) ? _clampToZero/*<T>*/(height) : height; | |
221 | |
222 /** | |
223 * Create a mutable rectangle spanned by the points [a] and [b]; | |
224 * | |
225 * The rectangle contains the points | |
226 * with x-coordinate between `a.x` and `b.x`, and | |
227 * with y-coordinate between `a.y` and `b.y`, both inclusive. | |
228 * | |
229 * If the distance between `a.x` and `b.x` is not representable | |
230 * (which can happen if one or both is a double), | |
231 * the actual right edge might be slightly off from `max(a.x, b.x)`. | |
232 * Similar for the y-coordinates and the bottom edge. | |
233 */ | |
234 factory MutableRectangle.fromPoints(Point<T> a, Point<T> b) { | |
235 T left = min(a.x, b.x); | |
236 T width = max(a.x, b.x) - left; | |
237 T top = min(a.y, b.y); | |
238 T height = max(a.y, b.y) - top; | |
239 return new MutableRectangle<T>(left, top, width, height); | |
240 } | |
241 | |
242 T get width => _width; | |
243 | |
244 /** | |
245 * Sets the width of the rectangle. | |
246 * | |
247 * The width must be non-negative. | |
248 * If a negative width is supplied, it is clamped to zero. | |
249 * | |
250 * Setting the value will change the right edge of the rectangle, | |
251 * but will not change [left]. | |
252 */ | |
253 void set width(T width) { | |
254 if (width < 0) width = _clampToZero/*<T>*/(width); | |
255 _width = width; | |
256 } | |
257 | |
258 T get height => _height; | |
259 | |
260 /** | |
261 * Sets the height of the rectangle. | |
262 * | |
263 * The height must be non-negative. | |
264 * If a negative height is supplied, it is clamped to zero. | |
265 * | |
266 * Setting the value will change the bottom edge of the rectangle, | |
267 * but will not change [top]. | |
268 */ | |
269 void set height(T height) { | |
270 if (height < 0) height = _clampToZero/*<T>*/(height); | |
271 _height = height; | |
272 } | |
273 } | |
274 | |
275 /** | |
276 * Converts a negative [int] or [double] to a zero-value of the same type. | |
277 * | |
278 * Returns `0` if value is int, `0.0` if value is double. | |
279 */ | |
280 num/*=T*/ _clampToZero/*<T extends num>*/(num/*=T*/ value) { | |
281 assert(value < 0); | |
282 return -value * 0; | |
283 } | |
OLD | NEW |