Skip to content
Merged
12 changes: 10 additions & 2 deletions src/GeometryBasics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ using EarCut_jll

using Base: @propagate_inbounds

include("fixed_arrays.jl")
import Base: +, -

# basic concepts
include("vectors.jl")
include("matrices.jl")
include("points.jl")

include("fixed_arrays.jl")
include("offsetintegers.jl")
include("basic_types.jl")

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

# points
export AbstractPoint, Point, PointMeta, PointWithUV
export Point, Point2, Point3, Point2f, Point3f

# vectors
export Vec, Vec2, Vec3, Vec2f, Vec3f
export vunit, vfill

# TODO: review these
export AbstractPoint, PointMeta, PointWithUV

# geometries
export AbstractGeometry, GeometryPrimitive
export LineFace, Polytope, Line, NgonFace, convert_simplex
Expand Down
2 changes: 0 additions & 2 deletions src/basic_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ Note That `Polytope{N} where N == 3` denotes a Triangle both as a Simplex or Ngo
abstract type Polytope{Dim,T} <: AbstractGeometry{Dim,T} end
abstract type AbstractPolygon{Dim,T} <: Polytope{Dim,T} end

abstract type AbstractPoint{Dim,T} <: StaticVector{Dim,T} end
abstract type AbstractFace{N,T} <: StaticVector{N,T} end
abstract type AbstractSimplexFace{N,T} <: AbstractFace{N,T} end
abstract type AbstractNgonFace{N,T} <: AbstractFace{N,T} end
Expand Down Expand Up @@ -57,7 +56,6 @@ Fixed Size Polygon, e.g.
- ...
"""
struct Ngon{Dim,T<:Real,N,Point<:AbstractPoint{Dim,T}} <: AbstractPolygon{Dim,T}

points::SVector{N,Point}
end

Expand Down
6 changes: 3 additions & 3 deletions src/boundingboxes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ boundingbox(geom) = boundingbox(coordinates(geom))
# fallback implementation treats geometry as
# a set of points (i.e. coordinates)
function boundingbox(geometry::AbstractArray{<:AbstractPoint{N,T}}) where {N,T}
vmin = Point{N,T}(typemax(T))
vmax = Point{N,T}(typemin(T))
vmin = vfill(Vec{N,T}, typemax(T))
vmax = vfill(Vec{N,T}, typemin(T))
for p in geometry
vmin, vmax = minmax(p, vmin, vmax)
vmin, vmax = minmax(coordinates(p), vmin, vmax)
end
Rect{N,T}(vmin, vmax - vmin)
end
Expand Down
20 changes: 0 additions & 20 deletions src/fixed_arrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,3 @@ macro fixed_vector(name, parent)
end
return esc(expr)
end

abstract type AbstractPoint{Dim,T} <: StaticVector{Dim,T} end
@fixed_vector Point AbstractPoint

const Pointf0{N} = Point{N,Float32}

for i in 1:4
for T in [:Point]
name = Symbol("$T$i")
namef0 = Symbol("$T$(i)f0")
@eval begin
const $name = $T{$i}
const $namef0 = $T{$i,Float32}
export $name
export $namef0
end
end
end

export Pointf0
35 changes: 8 additions & 27 deletions src/geometry_primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,37 +55,18 @@ Extract all line segments in a Face.
return v
end

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

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

function convert_simplex(::Type{Vec{N,T}}, x) where {N,T}
N2 = length(x)
return (Vec{N,T}(ntuple(i -> i <= N2 ? T(x[i]) : T(0), N)),)
end

collect_with_eltype(::Type{T}, vec::Vector{T}) where {T} = vec
collect_with_eltype(::Type{T}, vec::AbstractVector{T}) where {T} = collect(vec)

function collect_with_eltype(::Type{T}, iter) where {T}
# TODO we could be super smart about allocating the right length
# but its kinda annoying, since e.g. T == Triangle and first(iter) isa Quad
# will need double the length etc - but could all be figured out ;)
result = T[]
for element in iter
# convert_simplex always returns a tuple,
# so that e.g. convert(Triangle, quad) can return 2 elements
for telement in convert_simplex(T, element)
push!(result, telement)
end
end
return result
# TODO: review these
function convert_simplex(::Type{Vec{N,T}}, v::Vec{M,V}) where {N,T,M,V}
return (Vec(ntuple(i -> i <= M ? T(v[i]) : T(0), N)),)
end

"""
Expand Down
92 changes: 52 additions & 40 deletions src/interfaces.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# TODO: review these
"""
coordinates(geometry)
Returns the edges/vertices/coordinates of a geometry. Is allowed to return lazy iterators!
Use `decompose(ConcretePointType, geometry)` to get `Vector{ConcretePointType}` with
`ConcretePointType` to be something like `Point{3, Float32}`.
Returns the vertices of a geometry.
"""
function coordinates(points::AbstractVector{<:AbstractPoint})
return points
Expand Down Expand Up @@ -43,7 +42,7 @@ To transport this information to the various decompose methods, you can wrap it
in the Tesselation object e.g. like this:

```julia
sphere = Sphere(Point3f0(0), 1)
sphere = Sphere(Point3f(0,0,0), 1.0f0)
m1 = mesh(sphere) # uses a default value for tesselation
m2 = mesh(Tesselation(sphere, 64)) # uses 64 for tesselation
length(coordinates(m1)) != length(coordinates(m2))
Expand Down Expand Up @@ -80,46 +79,56 @@ function texturecoordinates(tesselation::Tesselation)
return texturecoordinates(tesselation.primitive, nvertices(tesselation))
end

## Decompose methods
# Dispatch type to make `decompose(UV{Vec2f}, primitive)` work
# and to pass through tesselation information

# Types that can be converted to a mesh via the functions below
const Meshable{Dim,T} = Union{Tesselation{Dim,T},Mesh{Dim,T},AbstractPolygon{Dim,T},
GeometryPrimitive{Dim,T},
AbstractVector{<:AbstractPoint{Dim,T}}}
const Meshable{Dim,T} = Union{Mesh{Dim,T},
Tesselation{Dim,T},
AbstractPolygon{Dim,T},
GeometryPrimitive{Dim,T}}

struct UV{T} end
UV(::Type{T}) where {T} = UV{T}()
UV() = UV(Vec2f)
struct UVW{T} end
UVW(::Type{T}) where {T} = UVW{T}()
UVW() = UVW(Vec3f)
struct Normal{T} end
Normal(::Type{T}) where {T} = Normal{T}()
Normal() = Normal(Vec3f)
"""
decompose(T, meshable)

function decompose(::Type{F}, primitive) where {F<:AbstractFace}
f = faces(primitive)
f === nothing && return nothing
return collect_with_eltype(F, f)
Decompose a `meshable` object (e.g. Polygon) into elements of type `T`

## Example

```julia
decompose(Point3, Rect3D())
```
"""
function decompose(::Type{T}, primitive) where {T}
return collect_with_eltype(T, primitive)
end

# Specializations

function decompose(::Type{P}, primitive) where {P<:AbstractPoint}
return collect_with_eltype(P, metafree(coordinates(primitive)))
convert.(P, metafree(coordinates(primitive)))
end

function decompose(::Type{Point}, primitive::Meshable{Dim,T}) where {Dim,T}
return collect_with_eltype(Point{Dim,T}, metafree(coordinates(primitive)))
function decompose(::Type{F}, primitive) where {F<:AbstractFace}
f = faces(primitive)
f === nothing && return nothing
return collect_with_eltype(F, f)
end

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

function decompose(::Type{T}, primitive) where {T}
return collect_with_eltype(T, primitive)
end
# TODO: review these
struct UV{T} end
UV(::Type{T}) where {T} = UV{T}()
UV() = UV(Vec2f)

struct UVW{T} end
UVW(::Type{T}) where {T} = UVW{T}()
UVW() = UVW(Vec3f)

struct Normal{T} end
Normal(::Type{T}) where {T} = Normal{T}()
Normal() = Normal(Vec3f)

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

function decompose(UVT::Union{UV{T},UVW{T}}, primitive) where {T}
function decompose(UVT::Union{UV{T},UVW{T}}, primitive::Meshable{Dim,V}) where {Dim,T,V}
# This is the fallback for texture coordinates if a primitive doesn't overload them
# We just take the positions and normalize them
uv = texturecoordinates(primitive)
if uv === nothing
# If the primitive doesn't even have coordinates, we're out of options and return
# nothing, indicating that texturecoordinates aren't implemented
positions = decompose(Point, primitive)
positions = decompose(Point{Dim,V}, primitive)
positions === nothing && return nothing
# Let this overlord do the work
return decompose(UVT, positions)
end
return collect_with_eltype(T, uv)
end

function decompose(UVT::Union{UV{T},UVW{T}},
positions::AbstractVector{<:Point}) where {T}
function decompose(UVT::Union{UV{T},UVW{T}}, positions::AbstractVector{<:AbstractPoint}) where {T}
N = length(T)
bb = boundingbox(positions) # Make sure we get this as points
bb = boundingbox(positions)
return map(positions) do p
return (p .- minimum(bb)) ./ widths(bb)
return (coordinates(p) - minimum(bb)) ./ widths(bb)
end
end

# Stay backward compatible:

function decompose(::Type{T}, primitive::Meshable, nvertices) where {T}
return decompose(T, Tesselation(primitive, nvertices))
function collect_with_eltype(::Type{T}, iter) where {T}
result = T[]
for element in iter
for telement in convert_simplex(T, element)
push!(result, telement)
end
end
return result
end
10 changes: 5 additions & 5 deletions src/lines.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ Returns intersection_found::Bool, intersection_point
"""
function intersects(a::Line{2,T1}, b::Line{2,T2}) where {T1,T2}
T = promote_type(T1, T2)
v1, v2 = a
v3, v4 = b
v1, v2 = coordinates.(a)
v3, v4 = coordinates.(b)
MT = Mat{2,2,T,4}
p0 = zero(Point2{T})
p0 = Point{2,T}(0, 0)

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

point = Point2{T}(x, y)
point = Point{2,T}(x, y)
# don't forget to rotate the answer back
if dorotation
point = transpose(rotation) * point
point = Point(transpose(rotation) * coordinates(point))
end

return true, point
Expand Down
32 changes: 17 additions & 15 deletions src/meshes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ const GLNormalUVWMesh2D = GLNormalUVWMesh{2}
const GLNormalUVWMesh3D = GLNormalUVWMesh{3}

"""
mesh(primitive::GeometryPrimitive;
pointtype=Point, facetype=GLTriangle,
mesh(primitive::Meshable{N,T};
pointtype=Point{N,T}, facetype=GLTriangle,
uvtype=nothing, normaltype=nothing)

Creates a mesh from `primitive`.
Expand All @@ -98,8 +98,8 @@ Note, that this can be an `Int` or `Tuple{Int, Int}``, when the primitive is gri
It also only losely correlates to the number of vertices, depending on the algorithm used.
#TODO: find a better number here!
"""
function mesh(primitive::Meshable; pointtype=Point, facetype=GLTriangleFace, uv=nothing,
normaltype=nothing)
function mesh(primitive::Meshable{N,T}; pointtype=Point{N,T}, facetype=GLTriangleFace,
uv=nothing, normaltype=nothing) where {N,T}

positions = decompose(pointtype, primitive)
faces = decompose(facetype, primitive)
Expand All @@ -124,7 +124,7 @@ function mesh(primitive::Meshable; pointtype=Point, facetype=GLTriangleFace, uv=
if normaltype !== nothing
primitive_normals = normals(primitive)
if primitive_normals !== nothing
attrs[:normals] = decompose(normaltype, primitive_normals)
attrs[:normals] = convert.(normaltype, primitive_normals)
else
# Normals not implemented for primitive, so we calculate them!
n = normals(positions, faces; normaltype=normaltype)
Expand Down Expand Up @@ -161,26 +161,29 @@ function mesh(polygon::AbstractPolygon{Dim,T}; pointtype=Point{Dim,T},
return Mesh(positions, faces)
end

function triangle_mesh(primitive::Meshable{N}; nvertices=nothing) where {N}
function triangle_mesh(primitive::Meshable{N,T}; nvertices=nothing) where {N,T}
if nvertices !== nothing
@warn("nvertices argument deprecated. Wrap primitive in `Tesselation(primitive, nvertices)`")
primitive = Tesselation(primitive, nvertices)
end
return mesh(primitive; pointtype=Point{N,Float32}, facetype=GLTriangleFace)
return mesh(primitive; pointtype=Point{N,T}, facetype=GLTriangleFace)
end

function triangle_mesh(points::AbstractVector{P}; nvertices=nothing) where {P<:AbstractPoint}
triangle_mesh(Polygon(points), nvertices=nvertices)
end

function uv_mesh(primitive::Meshable{N,T}) where {N,T}
return mesh(primitive; pointtype=Point{N,Float32}, uv=Vec2f, facetype=GLTriangleFace)
mesh(primitive; pointtype=Point{N,T}, uv=Vec{2,T}, facetype=GLTriangleFace)
end

function uv_normal_mesh(primitive::Meshable{N}) where {N}
return mesh(primitive; pointtype=Point{N,Float32}, uv=Vec2f, normaltype=Vec3f,
facetype=GLTriangleFace)
function uv_normal_mesh(primitive::Meshable{N,T}) where {N,T}
mesh(primitive; pointtype=Point{N,T}, uv=Vec{2,T}, normaltype=Vec{3,T}, facetype=GLTriangleFace)
end

function normal_mesh(points::AbstractVector{<:AbstractPoint},
faces::AbstractVector{<:AbstractFace})
_points = decompose(Point3f0, points)
_points = decompose(Point3f, points)
_faces = decompose(GLTriangleFace, faces)
return Mesh(meta(_points; normals=normals(_points, _faces)), _faces)
end
Expand All @@ -190,8 +193,7 @@ function normal_mesh(primitive::Meshable{N}; nvertices=nothing) where {N}
@warn("nvertices argument deprecated. Wrap primitive in `Tesselation(primitive, nvertices)`")
primitive = Tesselation(primitive, nvertices)
end
return mesh(primitive; pointtype=Point{N,Float32}, normaltype=Vec3f,
facetype=GLTriangleFace)
return mesh(primitive; pointtype=Point{N,Float32}, normaltype=Vec3f, facetype=GLTriangleFace)
end

"""
Expand All @@ -201,7 +203,7 @@ Calculate the signed volume of one tetrahedron. Be sure the orientation of your
surface is right.
"""
function volume(triangle::Triangle) where {VT,FT}
v1, v2, v3 = triangle
v1, v2, v3 = coordinates.(triangle)
sig = sign(orthogonal_vector(v1, v2, v3) ⋅ v1)
return sig * abs(v1 ⋅ (v2 × v3)) / 6
end
Expand Down
Loading