/
shapes.jl
184 lines (138 loc) · 5.33 KB
/
shapes.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
"""
Abstract idea which defines the external boundary of object.
"""
abstract type Shape{Dim} end
Symmetry(::Shape{Dim}) where {Dim} = WithoutSymmetry{Dim}()
dim(::S) where {Dim,S<:Shape{Dim}} = Dim
"""
origin(shape::Shape)::SVector
Origin of shape, typically the center
"""
origin(shape::Shape) = shape.origin
"""
number_type(shape::Shape)::DataType
Number type which is used to describe shape, defaults to the eltype of the origin vector.
"""
number_type(shape::Shape) = eltype(origin(shape))
"""
Shape(shape::Shape; addtodimensions = 0.0, vector_translation = zeros(...), kws...)
Alter the shape depending on the keywords.
"""
Shape(shape::Shape) = shape
"""
iscongruent(p1::Shape, p2::Shape)::Bool
≅(p1::Shape, p2::Shape)::Bool
True if shapes are the same but in different positions (origins), standard mathematical definition.
"""
iscongruent(s1::Shape, s2::Shape) = false # false by default, overload in specific examples
# Define synonym for iscongruent ≅, and add documentation
≅(s1::Shape, s2::Shape) = iscongruent(s1, s2)
@doc (@doc iscongruent(::Shape, ::Shape)) (≅(::Shape, ::Shape))
"""
congruent(s::Shape, x)::Shape
Create shape congruent to `s` but with origin at `x`
"""
function congruent end
"Generic helper function which tests if boundary coordinate is between 0 and 1"
function check_boundary_coord_range(t)
if t < 0 || t > 1
throw(DomainError("Boundary coordinate must be between 0 and 1"))
end
end
# Concrete shapes
# include("rectangle.jl")
include("box.jl")
include("sphere.jl")
include("halfspace.jl")
include("plate.jl")
include("time_of_flight.jl")
include("time_of_flight_from_point.jl")
include("empty_shape.jl")
"""
points_in_shape(Shape; resolution = 20, xres = resolution, yres = resolution,
exclude_region = EmptyShape(region), kws...)
returns `(x_vec, region_inds)` where `x_vec` is a vector of points that cover a box which bounds `Shape`, and `region_inds` is an array of linear indices such that `x_vec[region_inds]` are points contained `Shape`. For 3D we use `zres` instead of `yres`.
"""
function points_in_shape(region::Shape{2};
resolution::Number = 20, res::Number = resolution, xres::Number = res, yres::Number = res,
exclude_region::Shape = EmptyShape(region),
kws...)
rect = bounding_box(region)
#Size of the step in x and y direction
x_vec_step = rect.dimensions ./ [xres, yres]
bl = bottomleft(rect)
x_vec = [SVector{2}(bl + x_vec_step .* [i,j]) for i=0:xres, j=0:yres][:]
region_inds = findall(x -> !(x ∈ exclude_region) && x ∈ region, x_vec)
return x_vec, region_inds
end
function points_in_shape(region::Shape{3};
y = zero(number_type(region)),
resolution::Number = 20,
res::Number = resolution, xres::Number = res, zres::Number = res,
exclude_region::Shape = EmptyShape(region))
box = bounding_box(region)
#Size of the step in x and z direction
x_vec_step = [box.dimensions[1] / xres, zero(number_type(region)), box.dimensions[3] / zres]
bl = corners(box)[1]
x_vec = [SVector{3}(bl + x_vec_step .* [i,y,j]) for i = 0:xres, j = 0:zres][:]
region_inds = findall(x -> !(x ∈ exclude_region) && x ∈ region, x_vec)
return x_vec, region_inds
end
"Returns a set of points on the boundary of a 2D shape."
function boundary_points(shape2D::Shape{2}, num_points::Int = 4; dr = 0)
T = number_type(shape2D)
x, y = boundary_functions(shape2D)
v(τ) = SVector(x(τ),y(τ)) + dr * (SVector(x(τ),y(τ)) - origin(shape2D))
return [ v(τ) for τ in LinRange(zero(T),one(T),num_points+1)[1:end-1] ]
end
"Returns a set of points on the boundary of a 3D shape."
function boundary_points(shape3D::Shape{3}, num_points::Int = 4; dr = 0)
T = number_type(shape3D)
x, y, z = boundary_functions(shape3D)
v(τ,s) = SVector(x(τ,s),y(τ,s),z(τ,s)) + dr * (SVector(x(τ,s),y(τ,s),z(τ,s)) - origin(shape3D))
mesh = LinRange(zero(T),one(T),num_points+1)[1:end-1]
return [v(τ,s) for τ in mesh, s in mesh]
end
"Returns box which completely encloses the shapes"
bounding_box(shape1::Shape, shape2::Shape) = bounding_box([shape1, shape2])
# Create a box which bounds an array of shapes
function bounding_box(shapes::Vector{S}) where S<:Shape
boxes = bounding_box.(shapes)
corners_mat = hcat(vcat((corners.(boxes))...)...)
maxdims = maximum(corners_mat, dims=2)
mindims = minimum(corners_mat, dims=2)
c = (maxdims + mindims) ./ 2
dimensions = maxdims - mindims
return Box(c[:], dimensions[:])
end
# Docstrings
"""
name(shape::Shape)::String
Name of a shape
"""
function name end
"""
outer_radius(shape::Shape)
The radius of a circle which completely contains the shape
"""
function outer_radius end
"""
volume(shape::Shape)
Volume of a shape
"""
function volume end
"""
volume(shape::Shape)::NTuple{Function,Dim)
Returns Tuple of Dim Functions which define outer boundary of shape when given boundary coordinate t∈[0,1]
"""
function boundary_functions end
"""
issubset(shape1, shape2)::Bool
Returns true if shape1 is entirely contained within shape2, false otherwise (also works with particles).
"""
function issubset(s1::Shape,s2::Shape) throw(MethodError(issubset, (s1,s2))) end
"""
in(vector, shape)::Bool
Returns true if vector is in interior of shape, false otherwise.
"""
function in(::AbstractVector,::Shape) end