1Rect {
2	var <>left=0, <>top=0, <>width=0, <>height=0;
3
4	*new { arg left=0, top=0, width=0, height=0;
5		^super.newCopyArgs(left, top, width, height)
6	}
7	*newSides { arg left=0, top=0, right=0, bottom=0;
8		^super.newCopyArgs(left, top, right-left, bottom-top)
9	}
10	*fromPoints { arg pt1, pt2;
11		^super.newCopyArgs(
12			pt1.x min: pt2.x,
13			pt1.y min: pt2.y,
14			absdif(pt1.x, pt2.x),
15			absdif(pt1.y, pt2.y)
16		)
17	}
18	*fromRect { arg rect;
19		^this.new(rect.left, rect.top, rect.width, rect.height)
20	}
21	*fromArray {|array|
22		^this.new(*array)
23	}
24	*aboutPoint { arg point, dx, dy;
25		^this.new(point.x-dx, point.y-dy, 2*dx, 2*dy)
26	}
27
28	set { arg argLeft=0, argTop=0, argWidth=0, argHeight=0;
29		left = argLeft;
30		top = argTop;
31		width = argWidth;
32		height = argHeight;
33	}
34	setExtent { arg argWidth=0, argHeight=0;
35		width = argWidth;
36		height = argHeight;
37	}
38
39	origin { ^Point.new(left, top) }
40	origin_ { | pt | left = pt.x; top = pt.y }
41	extent { ^Point.new(width, height) }
42	extent_ { | pt | width = pt.x; height = pt.y }
43	center { ^Point.new(left + (width * 0.5), top + (height * 0.5)) }
44	center_ { arg center; ^this.class.aboutPoint(center, width * 0.5, height * 0.5) }
45
46	bottom { ^top + height }
47	bottom_ { |b| top = top - (this.bottom - b) }
48	right { ^left + width }
49	right_ { |r| left = left - (this.right - r) }
50
51	leftTop { ^Point.new(this.left, this.top) }
52	rightTop { ^Point.new(this.right, this.top) }
53	leftBottom { ^Point.new(this.left, this.bottom) }
54	rightBottom { ^Point.new(this.right, this.bottom) }
55
56	size { ^Size(width,height) }
57	size_ { |sz| width = sz.width; height = sz.height }
58
59	moveBy { arg h, v;
60		^this.class.new(left + h, top + v, width, height)
61	}
62	moveTo { arg h, v;
63		^this.class.new(h, v, width, height)
64	}
65	moveToPoint { arg aPoint;
66		^this.class.new(aPoint.x, aPoint.y, width, height)
67	}
68	resizeBy { arg h, v;
69		^this.class.new(left, top, width + h, height + (v ? h))
70	}
71	resizeTo { arg h, v;
72		^this.class.new(left, top, h, v)
73	}
74	insetBy { arg h, v;
75		if(v.isNil){ v = h };
76		^this.class.new(left + h, top + v, width - h - h, height - v - v)
77	}
78	insetAll { arg a, b, c, d;
79		^this.class.new(left + a, top + b, width - a - c, height - b - d)
80	}
81	insetByRect { arg r;
82		^this.copy.insetAll(r.left, r.top, r.right, r.bottom)
83	}
84	centerSquare {
85		var pos, center;
86		if (width > height, {
87			pos = (width - height) * 0.5 + left;
88			^Rect(pos, top, height, height)
89		},{
90			pos = (height - width) * 0.5 + top;
91			^Rect(left, pos, width, width)
92		});
93	}
94	centerIn { arg inRect;
95		var pos, spacing;
96		spacing  = (inRect.extent - this.extent) * 0.5;
97		^inRect.origin - this.origin + spacing;
98	}
99
100	contains{ arg anObject;
101		if ( anObject.isKindOf( Point ),
102			{ ^this.containsPoint( anObject ) });
103		if ( anObject.isKindOf( Rect ),
104			{ ^this.containsRect( anObject ) });
105		^false;
106	}
107
108	containsPoint { arg aPoint;
109		^(aPoint.x.inclusivelyBetween(left, left + width)
110			and: { aPoint.y.inclusivelyBetween(top, top + height) })
111	}
112	containsRect { arg aRect;
113		^(this.containsPoint(aRect.leftTop) and: {this.containsPoint(aRect.rightBottom) })
114	}
115	intersects { arg aRect;
116		if (aRect.right <= this.left, { ^false });
117		if (aRect.bottom <= this.top, { ^false });
118		if (aRect.left >= this.right, { ^false });
119		if (aRect.top >= this.bottom, { ^false });
120		^true
121	}
122
123	& { arg aRect; ^this sect: aRect }
124	| { arg aRect; ^this union: aRect }
125
126	union { arg aRect;
127		^this.class.newSides( left min: aRect.left, top min: aRect.top,
128			this.right max: aRect.right, this.bottom max: aRect.bottom)
129	}
130	sect { arg aRect;
131		^this.class.newSides( left max: aRect.left, top max: aRect.top,
132			this.right min: aRect.right, this.bottom min: aRect.bottom)
133	}
134	storeArgs { ^[left,top,width,height] }
135	printOn { arg stream;
136		stream << this.class.name << "(" <<* [left, top, width, height] << ")";
137	}
138
139	// the drawing routine here use Quickdraw.
140	// If you want CoreGraphics drawing, use methods in Pen.
141	draw { arg color, operation=2;
142		_Rect_Draw
143		^this.primitiveFailed
144	}
145
146	asRect { ^this }
147	bounds { ^Rect.new(left, top, width, height) }
148	== { arg that;
149		^this.compareObject(that, #[\left, \top, \width, \height])
150	}
151	hash {
152		^this.instVarHash(#[\left, \top, \width, \height])
153	}
154
155	// deprec
156	layout { arg argBounds;
157		this.set(argBounds.left, argBounds.top, argBounds.width, argBounds.height);
158	}
159
160	asArray { ^[this.left, this.top, this.width, this.height] }
161
162	performBinaryOpOnSomething { |aSelector, thing, adverb|
163		^thing.asRect.perform(aSelector, this, adverb)
164	}
165	+ {|that|
166		var thatRect;
167		thatRect = that.asRect;
168
169		^Rect(
170			this.left + thatRect.left,
171			this.top + thatRect.top,
172			this.width + thatRect.width,
173			this.height + thatRect.height
174		)
175	}
176	- {|that|
177		var thatRect;
178		thatRect = that.asRect;
179
180		^Rect(
181			this.left - thatRect.left,
182			this.top - thatRect.top,
183			this.width - thatRect.width,
184			this.height - thatRect.height
185		)
186	}
187}
188