/
rectangles.jl
204 lines (175 loc) · 5.48 KB
/
rectangles.jl
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
module Rectangles
using ..Points
import Base: +, -, *, /, copy, ==, convert, isapprox
import Devices
import Devices: AbstractPolygon, Coordinate, GDSMeta, Meta
import Devices: bounds, center, centered, lowerleft, upperright
import Unitful: ustrip
export Rectangle
export height
export width
export isproper
"""
struct Rectangle{T} <: AbstractPolygon{T}
ll::Point{T}
ur::Point{T}
function Rectangle(a,b)
# Ensure ll is lower-left, ur is upper-right.
ll = Point(a.<=b) .* a + Point(b.<=a) .* b
ur = Point(a.<=b) .* b + Point(b.<=a) .* a
new(ll,ur)
end
end
A rectangle, defined by opposing lower-left and upper-right corner coordinates.
Lower-left and upper-right are guaranteed to be such by the inner constructor.
"""
struct Rectangle{T} <: AbstractPolygon{T}
ll::Point{T}
ur::Point{T}
function Rectangle{T}(a,b) where T
# Ensure ll is lower-left, ur is upper-right.
ll = Point(a.<=b) .* a + Point(b.<=a) .* b
ur = Point(a.<=b) .* b + Point(b.<=a) .* a
new{T}(ll,ur)
end
end
"""
Rectangle(ll::Point, ur::Point)
Convenience constructor for `Rectangle` objects.
"""
Rectangle(ll::Point, ur::Point) = rectangle(promote(ll, ur)...)
rectangle(ll::Point{T}, ur::Point{T}) where {T <: Coordinate} = Rectangle{T}(ll,ur)
"""
Rectangle(width, height)
Constructs `Rectangle` objects by specifying the width and height rather than
the lower-left and upper-right corners.
The rectangle will sit with the lower-left corner at the origin. With centered
rectangles we would need to divide width and height by 2 to properly position.
If we wanted an object of `Rectangle{Int}` type, this would not be possible
if either `width` or `height` were odd numbers. This definition ensures type
stability in the constructor.
"""
Rectangle(width, height) = Rectangle(Point(zero(width), zero(height)), Point(width, height))
convert(::Type{Rectangle{T}}, x::Rectangle) where {T} = Rectangle{T}(x.ll, x.ur)
copy(p::Rectangle) = Rectangle(p.ll, p.ur)
==(r1::Rectangle, r2::Rectangle) = (r1.ll == r2.ll) && (r1.ur == r2.ur)
isapprox(r1::Rectangle, r2::Rectangle) = isapprox(r1.ll, r2.ll) && isapprox(r1.ur, r2.ur)
"""
width(r::Rectangle)
Return the width of a rectangle.
"""
width(r::Rectangle) = getx(r.ur) - getx(r.ll)
"""
height(r::Rectangle)
Return the height of a rectangle.
"""
height(r::Rectangle) = gety(r.ur) - gety(r.ll)
"""
isproper(r::Rectangle)
Returns `true` if the rectangle has a non-zero area. Otherwise, returns `false`.
Note that the upper-right and lower-left corners are enforced to be the `ur`
and `ll` fields of a `Rectangle` by the inner constructor.
"""
isproper(r::Rectangle) = (r.ur.x != r.ll.x) && (r.ur.y != r.ll.y)
"""
bounds(r::Rectangle)
No-op (just returns `r`).
"""
bounds(r::Rectangle) = r
"""
center(r::Rectangle)
Returns a [`Point`](@ref) corresponding to the center of the rectangle.
"""
center(r::Rectangle) = (r.ur + r.ll) / 2
"""
centered(r::Rectangle)
Centers a copy of `r`, with promoted coordinates if necessary.
This function will not throw an `InexactError()`, even if `r` had integer
coordinates.
"""
function centered(r::Rectangle)
c = center(r)
Rectangle(r.ll - c, r.ur - c)
end
"""
lowerleft(r::Rectangle)
Returns the lower-left corner of a rectangle (Point object).
"""
lowerleft(r::Rectangle) = r.ll
"""
upperright(r::Rectangle)
Returns the upper-right corner of a rectangle (Point object).
"""
upperright(r::Rectangle) = r.ur
for op in [:+, :-]
@eval function ($op)(r::Rectangle, p::Point)
Rectangle(($op)(r.ll, p), ($op)(r.ur, p))
end
end
@doc """
+(r::Rectangle, p::Point)
Translate a rectangle by `p`.
""" +(::Rectangle, ::Point)
@doc """
-(r::Rectangle, p::Point)
Translate a rectangle by `-p`.
""" -(::Rectangle, ::Point)
*(r::Rectangle, a::Real) = Rectangle(*(r.ll,a), *(r.ur,a))
*(a::Real, r::Rectangle) = *(r,a)
/(r::Rectangle, a::Real) = Rectangle(/(r.ll,a), /(r.ur,a))
"""
abstract Rectangles.Style{T<:Meta}
Implement new rectangle drawing styles by subtyping this. Must have a `meta::Meta` field.
"""
abstract type Style{T<:Meta} end
"""
struct Plain{T} <: Rectangles.Style{T}
meta::T
end
Plain rectangle style. Use this if you are fond for the simpler times when
rectangles were just rectangles.
"""
struct Plain{T} <: Style{T}
meta::T
end
Plain() = Plain(GDSMeta())
"""
struct Rounded{S<:Coordinate,T} <: Rectangles.Style{T}
r::S
meta::T
end
Rounded rectangle style. All corners are rounded off with a given radius `r`.
The bounding box of the unstyled rectangle should remain unaffected.
"""
struct Rounded{S<:Coordinate,T} <: Style{T}
r::S
meta::T
end
Rounded(r) = Rounded(r, GDSMeta())
Rounded(r, meta) = Rounded(r, meta)
"""
struct Undercut{S<:Coordinate,T} <: Rectangles.Style{T}
ucl::S
uct::S
ucr::S
ucb::S
meta::T
undercut_meta::T
end
Undercut rectangles. In each direction around a rectangle (left, top, right, bottom) an
undercut is rendered on .
"""
struct Undercut{S<:Coordinate,T} <: Style{T}
ucl::S
uct::S
ucr::S
ucb::S
meta::T
undercut_meta::T
end
Undercut(ucl, uct, ucr, ucb, meta::T=GDSMeta(), undercut_meta::T=GDSMeta()) where {T <: Meta} =
undercut(promote(ucl, uct, ucr, ucb)..., meta, undercut_meta)
undercut(a::S, b::S, c::S, d::S, e::T, f::T) where {S <: Coordinate,T <: Meta} =
Undercut{S,T}(a,b,c,d,e,f)
ustrip(r::Rectangle) = Rectangle(ustrip(r.ll), ustrip(r.ur))
end