Skip to content

Fix the Point concept #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Oct 14, 2020
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