Skip to content

Commit 3919901

Browse files
authored
Merge pull request #101 from JuliaGeometry/points
Fix the Point concept
2 parents 752cce2 + fe1836c commit 3919901

16 files changed

+396
-331
lines changed

src/GeometryBasics.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ using EarCut_jll
55

66
using Base: @propagate_inbounds
77

8-
include("fixed_arrays.jl")
8+
import Base: +, -
9+
10+
# basic concepts
911
include("vectors.jl")
1012
include("matrices.jl")
13+
include("points.jl")
14+
15+
include("fixed_arrays.jl")
1116
include("offsetintegers.jl")
1217
include("basic_types.jl")
1318

@@ -26,12 +31,15 @@ include("lines.jl")
2631
include("boundingboxes.jl")
2732

2833
# points
29-
export AbstractPoint, Point, PointMeta, PointWithUV
34+
export Point, Point2, Point3, Point2f, Point3f
3035

3136
# vectors
3237
export Vec, Vec2, Vec3, Vec2f, Vec3f
3338
export vunit, vfill
3439

40+
# TODO: review these
41+
export AbstractPoint, PointMeta, PointWithUV
42+
3543
# geometries
3644
export AbstractGeometry, GeometryPrimitive
3745
export LineFace, Polytope, Line, NgonFace, convert_simplex

src/basic_types.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ Note That `Polytope{N} where N == 3` denotes a Triangle both as a Simplex or Ngo
1313
abstract type Polytope{Dim,T} <: AbstractGeometry{Dim,T} end
1414
abstract type AbstractPolygon{Dim,T} <: Polytope{Dim,T} end
1515

16-
abstract type AbstractPoint{Dim,T} <: StaticVector{Dim,T} end
1716
abstract type AbstractFace{N,T} <: StaticVector{N,T} end
1817
abstract type AbstractSimplexFace{N,T} <: AbstractFace{N,T} end
1918
abstract type AbstractNgonFace{N,T} <: AbstractFace{N,T} end
@@ -57,7 +56,6 @@ Fixed Size Polygon, e.g.
5756
- ...
5857
"""
5958
struct Ngon{Dim,T<:Real,N,Point<:AbstractPoint{Dim,T}} <: AbstractPolygon{Dim,T}
60-
6159
points::SVector{N,Point}
6260
end
6361

src/boundingboxes.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ boundingbox(geom) = boundingbox(coordinates(geom))
88
# fallback implementation treats geometry as
99
# a set of points (i.e. coordinates)
1010
function boundingbox(geometry::AbstractArray{<:AbstractPoint{N,T}}) where {N,T}
11-
vmin = Point{N,T}(typemax(T))
12-
vmax = Point{N,T}(typemin(T))
11+
vmin = vfill(Vec{N,T}, typemax(T))
12+
vmax = vfill(Vec{N,T}, typemin(T))
1313
for p in geometry
14-
vmin, vmax = minmax(p, vmin, vmax)
14+
vmin, vmax = minmax(coordinates(p), vmin, vmax)
1515
end
1616
Rect{N,T}(vmin, vmax - vmin)
1717
end

src/fixed_arrays.jl

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,3 @@ macro fixed_vector(name, parent)
106106
end
107107
return esc(expr)
108108
end
109-
110-
abstract type AbstractPoint{Dim,T} <: StaticVector{Dim,T} end
111-
@fixed_vector Point AbstractPoint
112-
113-
const Pointf0{N} = Point{N,Float32}
114-
115-
for i in 1:4
116-
for T in [:Point]
117-
name = Symbol("$T$i")
118-
namef0 = Symbol("$T$(i)f0")
119-
@eval begin
120-
const $name = $T{$i}
121-
const $namef0 = $T{$i,Float32}
122-
export $name
123-
export $namef0
124-
end
125-
end
126-
end
127-
128-
export Pointf0

src/geometry_primitives.jl

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,37 +55,18 @@ Extract all line segments in a Face.
5555
return v
5656
end
5757

58+
# TODO: review these
5859
to_pointn(::Type{T}, x) where {T<:Point} = convert_simplex(T, x)[1]
5960

60-
# disambiguation method overlords
61-
convert_simplex(::Type{Point}, x::Point) = (x,)
62-
convert_simplex(::Type{Point{N,T}}, p::Point{N,T}) where {N,T} = (p,)
63-
function convert_simplex(::Type{Point{N,T}}, x) where {N,T}
64-
N2 = length(x)
65-
return (Point{N,T}(ntuple(i -> i <= N2 ? T(x[i]) : T(0), N)),)
61+
# TODO: why increase the dimension of the point?
62+
function convert_simplex(::Type{Point{N,T}}, p::Point{M,V}) where {N,T,M,V}
63+
x = coordinates(p)
64+
return (Point(ntuple(i -> i <= M ? T(x[i]) : T(0), N)),)
6665
end
6766

68-
function convert_simplex(::Type{Vec{N,T}}, x) where {N,T}
69-
N2 = length(x)
70-
return (Vec{N,T}(ntuple(i -> i <= N2 ? T(x[i]) : T(0), N)),)
71-
end
72-
73-
collect_with_eltype(::Type{T}, vec::Vector{T}) where {T} = vec
74-
collect_with_eltype(::Type{T}, vec::AbstractVector{T}) where {T} = collect(vec)
75-
76-
function collect_with_eltype(::Type{T}, iter) where {T}
77-
# TODO we could be super smart about allocating the right length
78-
# but its kinda annoying, since e.g. T == Triangle and first(iter) isa Quad
79-
# will need double the length etc - but could all be figured out ;)
80-
result = T[]
81-
for element in iter
82-
# convert_simplex always returns a tuple,
83-
# so that e.g. convert(Triangle, quad) can return 2 elements
84-
for telement in convert_simplex(T, element)
85-
push!(result, telement)
86-
end
87-
end
88-
return result
67+
# TODO: review these
68+
function convert_simplex(::Type{Vec{N,T}}, v::Vec{M,V}) where {N,T,M,V}
69+
return (Vec(ntuple(i -> i <= M ? T(v[i]) : T(0), N)),)
8970
end
9071

9172
"""

src/interfaces.jl

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1+
# TODO: review these
12
"""
23
coordinates(geometry)
3-
Returns the edges/vertices/coordinates of a geometry. Is allowed to return lazy iterators!
4-
Use `decompose(ConcretePointType, geometry)` to get `Vector{ConcretePointType}` with
5-
`ConcretePointType` to be something like `Point{3, Float32}`.
4+
Returns the vertices of a geometry.
65
"""
76
function coordinates(points::AbstractVector{<:AbstractPoint})
87
return points
@@ -43,7 +42,7 @@ To transport this information to the various decompose methods, you can wrap it
4342
in the Tesselation object e.g. like this:
4443
4544
```julia
46-
sphere = Sphere(Point3f0(0), 1)
45+
sphere = Sphere(Point3f(0,0,0), 1.0f0)
4746
m1 = mesh(sphere) # uses a default value for tesselation
4847
m2 = mesh(Tesselation(sphere, 64)) # uses 64 for tesselation
4948
length(coordinates(m1)) != length(coordinates(m2))
@@ -80,46 +79,56 @@ function texturecoordinates(tesselation::Tesselation)
8079
return texturecoordinates(tesselation.primitive, nvertices(tesselation))
8180
end
8281

83-
## Decompose methods
84-
# Dispatch type to make `decompose(UV{Vec2f}, primitive)` work
85-
# and to pass through tesselation information
86-
8782
# Types that can be converted to a mesh via the functions below
88-
const Meshable{Dim,T} = Union{Tesselation{Dim,T},Mesh{Dim,T},AbstractPolygon{Dim,T},
89-
GeometryPrimitive{Dim,T},
90-
AbstractVector{<:AbstractPoint{Dim,T}}}
83+
const Meshable{Dim,T} = Union{Mesh{Dim,T},
84+
Tesselation{Dim,T},
85+
AbstractPolygon{Dim,T},
86+
GeometryPrimitive{Dim,T}}
9187

92-
struct UV{T} end
93-
UV(::Type{T}) where {T} = UV{T}()
94-
UV() = UV(Vec2f)
95-
struct UVW{T} end
96-
UVW(::Type{T}) where {T} = UVW{T}()
97-
UVW() = UVW(Vec3f)
98-
struct Normal{T} end
99-
Normal(::Type{T}) where {T} = Normal{T}()
100-
Normal() = Normal(Vec3f)
88+
"""
89+
decompose(T, meshable)
10190
102-
function decompose(::Type{F}, primitive) where {F<:AbstractFace}
103-
f = faces(primitive)
104-
f === nothing && return nothing
105-
return collect_with_eltype(F, f)
91+
Decompose a `meshable` object (e.g. Polygon) into elements of type `T`
92+
93+
## Example
94+
95+
```julia
96+
decompose(Point3, Rect3D())
97+
```
98+
"""
99+
function decompose(::Type{T}, primitive) where {T}
100+
return collect_with_eltype(T, primitive)
106101
end
107102

103+
# Specializations
104+
108105
function decompose(::Type{P}, primitive) where {P<:AbstractPoint}
109-
return collect_with_eltype(P, metafree(coordinates(primitive)))
106+
convert.(P, metafree(coordinates(primitive)))
110107
end
111108

112-
function decompose(::Type{Point}, primitive::Meshable{Dim,T}) where {Dim,T}
113-
return collect_with_eltype(Point{Dim,T}, metafree(coordinates(primitive)))
109+
function decompose(::Type{F}, primitive) where {F<:AbstractFace}
110+
f = faces(primitive)
111+
f === nothing && return nothing
112+
return collect_with_eltype(F, f)
114113
end
115114

115+
# TODO: review these
116116
function decompose(::Type{Point}, primitive::LineString{Dim,T}) where {Dim,T}
117117
return collect_with_eltype(Point{Dim,T}, metafree(coordinates(primitive)))
118118
end
119119

120-
function decompose(::Type{T}, primitive) where {T}
121-
return collect_with_eltype(T, primitive)
122-
end
120+
# TODO: review these
121+
struct UV{T} end
122+
UV(::Type{T}) where {T} = UV{T}()
123+
UV() = UV(Vec2f)
124+
125+
struct UVW{T} end
126+
UVW(::Type{T}) where {T} = UVW{T}()
127+
UVW() = UVW(Vec3f)
128+
129+
struct Normal{T} end
130+
Normal(::Type{T}) where {T} = Normal{T}()
131+
Normal() = Normal(Vec3f)
123132

124133
decompose_uv(primitive) = decompose(UV(), primitive)
125134
decompose_uvw(primitive) = decompose(UVW(), primitive)
@@ -133,32 +142,35 @@ function decompose(NT::Normal{T}, primitive) where {T}
133142
return collect_with_eltype(T, n)
134143
end
135144

136-
function decompose(UVT::Union{UV{T},UVW{T}}, primitive) where {T}
145+
function decompose(UVT::Union{UV{T},UVW{T}}, primitive::Meshable{Dim,V}) where {Dim,T,V}
137146
# This is the fallback for texture coordinates if a primitive doesn't overload them
138147
# We just take the positions and normalize them
139148
uv = texturecoordinates(primitive)
140149
if uv === nothing
141150
# If the primitive doesn't even have coordinates, we're out of options and return
142151
# nothing, indicating that texturecoordinates aren't implemented
143-
positions = decompose(Point, primitive)
152+
positions = decompose(Point{Dim,V}, primitive)
144153
positions === nothing && return nothing
145154
# Let this overlord do the work
146155
return decompose(UVT, positions)
147156
end
148157
return collect_with_eltype(T, uv)
149158
end
150159

151-
function decompose(UVT::Union{UV{T},UVW{T}},
152-
positions::AbstractVector{<:Point}) where {T}
160+
function decompose(UVT::Union{UV{T},UVW{T}}, positions::AbstractVector{<:AbstractPoint}) where {T}
153161
N = length(T)
154-
bb = boundingbox(positions) # Make sure we get this as points
162+
bb = boundingbox(positions)
155163
return map(positions) do p
156-
return (p .- minimum(bb)) ./ widths(bb)
164+
return (coordinates(p) - minimum(bb)) ./ widths(bb)
157165
end
158166
end
159167

160-
# Stay backward compatible:
161-
162-
function decompose(::Type{T}, primitive::Meshable, nvertices) where {T}
163-
return decompose(T, Tesselation(primitive, nvertices))
168+
function collect_with_eltype(::Type{T}, iter) where {T}
169+
result = T[]
170+
for element in iter
171+
for telement in convert_simplex(T, element)
172+
push!(result, telement)
173+
end
174+
end
175+
return result
164176
end

src/lines.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ Returns intersection_found::Bool, intersection_point
77
"""
88
function intersects(a::Line{2,T1}, b::Line{2,T2}) where {T1,T2}
99
T = promote_type(T1, T2)
10-
v1, v2 = a
11-
v3, v4 = b
10+
v1, v2 = coordinates.(a)
11+
v3, v4 = coordinates.(b)
1212
MT = Mat{2,2,T,4}
13-
p0 = zero(Point2{T})
13+
p0 = Point{2,T}(0, 0)
1414

1515
verticalA = v1[1] == v2[1]
1616
verticalB = v3[1] == v4[1]
@@ -55,10 +55,10 @@ function intersects(a::Line{2,T1}, b::Line{2,T2}) where {T1,T2}
5555
(y < prevfloat(min(v3[2], v4[2])) || y > nextfloat(max(v3[2], v4[2]))) &&
5656
return false, p0
5757

58-
point = Point2{T}(x, y)
58+
point = Point{2,T}(x, y)
5959
# don't forget to rotate the answer back
6060
if dorotation
61-
point = transpose(rotation) * point
61+
point = Point(transpose(rotation) * coordinates(point))
6262
end
6363

6464
return true, point

src/meshes.jl

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ const GLNormalUVWMesh2D = GLNormalUVWMesh{2}
8787
const GLNormalUVWMesh3D = GLNormalUVWMesh{3}
8888

8989
"""
90-
mesh(primitive::GeometryPrimitive;
91-
pointtype=Point, facetype=GLTriangle,
90+
mesh(primitive::Meshable{N,T};
91+
pointtype=Point{N,T}, facetype=GLTriangle,
9292
uvtype=nothing, normaltype=nothing)
9393
9494
Creates a mesh from `primitive`.
@@ -98,8 +98,8 @@ Note, that this can be an `Int` or `Tuple{Int, Int}``, when the primitive is gri
9898
It also only losely correlates to the number of vertices, depending on the algorithm used.
9999
#TODO: find a better number here!
100100
"""
101-
function mesh(primitive::Meshable; pointtype=Point, facetype=GLTriangleFace, uv=nothing,
102-
normaltype=nothing)
101+
function mesh(primitive::Meshable{N,T}; pointtype=Point{N,T}, facetype=GLTriangleFace,
102+
uv=nothing, normaltype=nothing) where {N,T}
103103

104104
positions = decompose(pointtype, primitive)
105105
faces = decompose(facetype, primitive)
@@ -124,7 +124,7 @@ function mesh(primitive::Meshable; pointtype=Point, facetype=GLTriangleFace, uv=
124124
if normaltype !== nothing
125125
primitive_normals = normals(primitive)
126126
if primitive_normals !== nothing
127-
attrs[:normals] = decompose(normaltype, primitive_normals)
127+
attrs[:normals] = convert.(normaltype, primitive_normals)
128128
else
129129
# Normals not implemented for primitive, so we calculate them!
130130
n = normals(positions, faces; normaltype=normaltype)
@@ -161,26 +161,29 @@ function mesh(polygon::AbstractPolygon{Dim,T}; pointtype=Point{Dim,T},
161161
return Mesh(positions, faces)
162162
end
163163

164-
function triangle_mesh(primitive::Meshable{N}; nvertices=nothing) where {N}
164+
function triangle_mesh(primitive::Meshable{N,T}; nvertices=nothing) where {N,T}
165165
if nvertices !== nothing
166166
@warn("nvertices argument deprecated. Wrap primitive in `Tesselation(primitive, nvertices)`")
167167
primitive = Tesselation(primitive, nvertices)
168168
end
169-
return mesh(primitive; pointtype=Point{N,Float32}, facetype=GLTriangleFace)
169+
return mesh(primitive; pointtype=Point{N,T}, facetype=GLTriangleFace)
170+
end
171+
172+
function triangle_mesh(points::AbstractVector{P}; nvertices=nothing) where {P<:AbstractPoint}
173+
triangle_mesh(Polygon(points), nvertices=nvertices)
170174
end
171175

172176
function uv_mesh(primitive::Meshable{N,T}) where {N,T}
173-
return mesh(primitive; pointtype=Point{N,Float32}, uv=Vec2f, facetype=GLTriangleFace)
177+
mesh(primitive; pointtype=Point{N,T}, uv=Vec{2,T}, facetype=GLTriangleFace)
174178
end
175179

176-
function uv_normal_mesh(primitive::Meshable{N}) where {N}
177-
return mesh(primitive; pointtype=Point{N,Float32}, uv=Vec2f, normaltype=Vec3f,
178-
facetype=GLTriangleFace)
180+
function uv_normal_mesh(primitive::Meshable{N,T}) where {N,T}
181+
mesh(primitive; pointtype=Point{N,T}, uv=Vec{2,T}, normaltype=Vec{3,T}, facetype=GLTriangleFace)
179182
end
180183

181184
function normal_mesh(points::AbstractVector{<:AbstractPoint},
182185
faces::AbstractVector{<:AbstractFace})
183-
_points = decompose(Point3f0, points)
186+
_points = decompose(Point3f, points)
184187
_faces = decompose(GLTriangleFace, faces)
185188
return Mesh(meta(_points; normals=normals(_points, _faces)), _faces)
186189
end
@@ -190,8 +193,7 @@ function normal_mesh(primitive::Meshable{N}; nvertices=nothing) where {N}
190193
@warn("nvertices argument deprecated. Wrap primitive in `Tesselation(primitive, nvertices)`")
191194
primitive = Tesselation(primitive, nvertices)
192195
end
193-
return mesh(primitive; pointtype=Point{N,Float32}, normaltype=Vec3f,
194-
facetype=GLTriangleFace)
196+
return mesh(primitive; pointtype=Point{N,Float32}, normaltype=Vec3f, facetype=GLTriangleFace)
195197
end
196198

197199
"""
@@ -201,7 +203,7 @@ Calculate the signed volume of one tetrahedron. Be sure the orientation of your
201203
surface is right.
202204
"""
203205
function volume(triangle::Triangle) where {VT,FT}
204-
v1, v2, v3 = triangle
206+
v1, v2, v3 = coordinates.(triangle)
205207
sig = sign(orthogonal_vector(v1, v2, v3) v1)
206208
return sig * abs(v1 (v2 × v3)) / 6
207209
end

0 commit comments

Comments
 (0)