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
1 change: 0 additions & 1 deletion 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
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
64 changes: 64 additions & 0 deletions src/points.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
abstract type AbstractPoint{N,T} end

"""
Point{N,T}

A point in `N`-dimensional space with coordinates of type `T`.
The coordinates of the point provided upon construction are with
respect to the canonical Euclidean basis. See [`vunit`](@ref).

## Example

```julia
O = Point(0.0, 0.0) # origin of 2D Euclidean space
```

### Notes

- Type aliases are `Point2`, `Point3`, `Point2f`, `Point3f`
"""
struct Point{N,T} <: AbstractPoint{N,T}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no inherit from StaticArrays? That would give us all functionality, and you could still get rid of the macro and rework the overloaded functions.
This will make it very unlikely to get merged, since you're dropping tons of functionality

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What functionality you are missing? It would be helpful to understand why you are resistant.

I appreciate if you can communicate more explicitly the design issues you have in your mind so that we can both work together on a satisfying solution. Currently, we don't have a satisfying point implementation, and I can't see how inheriting from StaticArrays.jl would help.

coords::SVector{N,T}
end

# convenience constructors
Point(coords::NTuple{N,T}) where {N,T} = Point{N,T}(SVector(coords))
Point(coords::Vararg{T,N}) where {N,T} = Point{N,T}(SVector(coords))

# coordinate type conversions
Point{N,T}(coords::NTuple{N,V}) where {N,T,V} = Point(T.(coords))
Point{N,T}(coords::Vararg{V,N}) where {N,T,V} = Point(T.(coords))
Base.convert(::Type{Point{N,T}}, coords) where {N,T} = Point{N,T}(coords)
Base.convert(::Type{Point{N,T}}, p::Point) where {N,T} = Point{N,T}(p.coords)

# type aliases for convenience
const Point2 = Point{2,Float64}
const Point3 = Point{3,Float64}
const Point2f = Point{2,Float32}
const Point3f = Point{3,Float32}

"""
coordinates(A::Point)

Return the coordinates of the point with respect to the
canonical Euclidean basis. See [`vunit`](@ref).
"""
coordinates(A::Point) = A.coords

"""
-(A::Point, B::Point)

Return the [`Vec`](@ref) associated with the direction
from point `A` to point `B`.
"""
-(A::Point, B::Point) = Vec(A.coords - B.coords)

"""
+(A::Point, v::Vec)
+(v::Vec, A::Point)

Return the point at the end of the vector `v` placed
at a reference (or start) point `A`.
"""
+(A::Point, v::Vec) = Point(A.coords + v)
+(v::Vec, A::Point) = A + v
3 changes: 2 additions & 1 deletion src/primitives/rectangles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,9 @@ Check if a point is contained in a Rect. This will return true if
the point is on a face of the Rect.
"""
function Base.in(pt::Point, b1::Rect{N,T}) where {T,N}
cs = coordinates(pt)
for i in 1:N
pt[i] <= maximum(b1)[i] && pt[i] >= minimum(b1)[i] || return false
cs[i] <= maximum(b1)[i] && cs[i] >= minimum(b1)[i] || return false
end
return true
end
Expand Down
54 changes: 31 additions & 23 deletions src/primitives/spheres.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@
HyperSphere{N, T}

A `HyperSphere` is a generalization of a sphere into N-dimensions.
A `center` and radius, `r`, must be specified.
A `center` and `radius` must be specified.
"""
struct HyperSphere{N,T} <: GeometryPrimitive{N,T}
center::Point{N,T}
r::T
radius::T
end

origin(s::HyperSphere) = s.center
radius(s::HyperSphere) = s.radius

# TODO: review these
widths(s::HyperSphere{N,T}) where {N,T} = vfill(Vec{N,T}, 2s.radius)
Base.minimum(s::HyperSphere) = coordinates(s.center) .- s.radius
Base.maximum(s::HyperSphere) = coordinates(s.center) .+ s.radius

"""
Circle{T}

Expand All @@ -22,41 +31,40 @@ An alias for a HyperSphere of dimension 3. (i.e. `HyperSphere{3, T}`)
"""
const Sphere{T} = HyperSphere{3,T}

HyperSphere{N}(p::Point{N,T}, number) where {N,T} = HyperSphere{N,T}(p, convert(T, number))

widths(c::HyperSphere{N,T}) where {N,T} = Vec{N,T}(radius(c) * 2)
radius(c::HyperSphere) = c.r
origin(c::HyperSphere) = c.center

Base.minimum(c::HyperSphere{N,T}) where {N,T} = Vec{N,T}(origin(c)) - Vec{N,T}(radius(c))
Base.maximum(c::HyperSphere{N,T}) where {N,T} = Vec{N,T}(origin(c)) + Vec{N,T}(radius(c))
function Base.in(p::AbstractPoint{2}, c::Circle)
x = coordinates(p)
o = coordinates(c.center)
sum(abs2, x - o) ≤ c.radius
end

function Base.in(x::AbstractPoint{2}, c::Circle)
@inbounds ox, oy = origin(c)
xD = abs(ox - x)
yD = abs(oy - y)
return xD <= c.r && yD <= c.r
function centered(S::Type{HyperSphere{N,T}}) where {N,T}
center = Point{N,T}(ntuple(i->zero(T),N))
radius = T(0.5)
S(center, radius)
end

centered(S::Type{HyperSphere{N,T}}) where {N,T} = S(Vec{N,T}(0), T(0.5))
function centered(::Type{T}) where {T<:HyperSphere}
return centered(HyperSphere{ndims_or(T, 3),eltype_or(T, Float32)})
end

function coordinates(s::Circle, nvertices=64)
rad = radius(s)
inner(fi) = Point(rad * sin(fi + pi), rad * cos(fi + pi)) .+ origin(s)
return (inner(fi) for fi in LinRange(0, 2pi, nvertices))
o = coordinates(s.center)
r = s.radius
φ = LinRange(0, 2pi, nvertices)
inner(φ) = Vec(r*sin(φ+pi), r*cos(φ+pi)) + o
return (inner(φ) for φ in φ)
end

function texturecoordinates(s::Circle, nvertices=64)
return coordinates(Circle(Point2f0(0.5), 0.5f0), nvertices)
return coordinates(Circle(Point2f0(0.5,0.5), 0.5f0), nvertices)
end

function coordinates(s::Sphere, nvertices=24)
o = coordinates(s.center)
r = s.radius
θ = LinRange(0, pi, nvertices)
φ = LinRange(0, 2pi, nvertices)
inner(θ, φ) = Point(cos(φ) * sin(θ), sin(φ) * sin(θ), cos(θ)) .* s.r .+ s.center
inner(θ, φ) = Vec(r*cos(φ)*sin(θ), r*sin(φ)*sin(θ), r*cos(θ)) + o
return ivec((inner(θ, φ) for θ in θ, φ in φ))
end

Expand All @@ -65,10 +73,10 @@ function texturecoordinates(s::Sphere, nvertices=24)
return ivec(((φ, θ) for θ in reverse(ux), φ in ux))
end

function faces(sphere::Sphere, nvertices=24)
function faces(::Sphere, nvertices=24)
return faces(Rect(0, 0, 1, 1), (nvertices, nvertices))
end

function normals(s::Sphere{T}, nvertices=24) where {T}
function normals(::Sphere{T}, nvertices=24) where {T}
return coordinates(Sphere(Point{3,T}(0), 1), nvertices)
end
12 changes: 7 additions & 5 deletions src/triangulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ function area(contour::AbstractVector{<:AbstractPoint{N,T}}) where {N,T}
p = lastindex(contour)
q = firstindex(contour)
while q <= n
A += cross(contour[p], contour[q])
pc = coordinates(contour[p])
qc = coordinates(contour[q])
A += cross(pc, qc)
p = q
q += 1
end
Expand Down Expand Up @@ -82,9 +84,9 @@ function Base.in(P::T, triangle::Triangle) where {T<:AbstractPoint}
end

function snip(contour::AbstractVector{<:AbstractPoint{N,T}}, u, v, w, n, V) where {N,T}
A = contour[V[u]]
B = contour[V[v]]
C = contour[V[w]]
A = coordinates(contour[V[u]])
B = coordinates(contour[V[v]])
C = coordinates(contour[V[w]])
x = (((B[1] - A[1]) * (C[2] - A[2])) - ((B[2] - A[2]) * (C[1] - A[1])))
if 0.0000000001f0 > x
return false
Expand Down Expand Up @@ -112,7 +114,7 @@ function decompose(::Type{FaceType},
result = FaceType[]

# the algorithm doesn't like closed contours
contour = if isapprox(last(points), first(points))
contour = if isapprox(coordinates(last(points)), coordinates(first(points)))
@view points[1:(end - 1)]
else
@view points[1:end]
Expand Down
50 changes: 27 additions & 23 deletions test/geometrytypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ end

@testset "Cylinder" begin
@testset "constructors" begin
o, extr, r = Point2f0(1, 2), Point2f0(3, 4), 5.0f0
o, extr, r = Point2f(1, 2), Point2f(3, 4), 5.0f0
s = Cylinder(o, extr, r)
@test typeof(s) == Cylinder{2,Float32}
@test typeof(s) == Cylinder2{Float32}
Expand All @@ -22,7 +22,7 @@ end
h = norm(o - extr)
@test isapprox(height(s), h)
#@test norm(direction(s) - Point{2,Float32}([2,2]./norm([1,2]-[3,4])))<1e-5
@test isapprox(direction(s), Point2f0(2, 2) ./ h)
@test isapprox(direction(s), Point2f(2, 2) ./ h)
v1 = rand(Point{3,Float64})
v2 = rand(Point{3,Float64})
R = rand()
Expand All @@ -39,21 +39,22 @@ end

@testset "decompose" begin

o, extr, r = Point2f0(1, 2), Point2f0(3, 4), 5.0f0
o, extr, r = Point2f(1, 2), Point2f(3, 4), 5.0f0
s = Cylinder(o, extr, r)
positions = Point{3,Float32}[(-0.7677671, 3.767767, 0.0),
(2.767767, 0.23223293, 0.0),
(0.23223293, 4.767767, 0.0),
(3.767767, 1.2322329, 0.0), (1.2322329, 5.767767, 0.0),
(4.767767, 2.232233, 0.0)]
@test decompose(Point3f0, Tesselation(s, (2, 3))) ≈ positions
positions = Point3f[(-0.7677671, 3.767767, 0.0),
(2.767767, 0.23223293, 0.0),
(0.23223293, 4.767767, 0.0),
(3.767767, 1.2322329, 0.0),
(1.2322329, 5.767767, 0.0),
(4.767767, 2.232233, 0.0)]
@test decompose(Point3f, Tesselation(s, (2, 3))) ≈ positions

FT = TriangleFace{Int}
faces = FT[(1, 2, 4), (1, 4, 3), (3, 4, 6), (3, 6, 5)]
@test faces == decompose(FT, Tesselation(s, (2, 3)))

v1 = Point{3,Float64}(1, 2, 3)
v2 = Point{3,Float64}(4, 5, 6)
v1 = Point3(1, 2, 3)
v2 = Point3(4, 5, 6)
R = 5.0
s = Cylinder(v1, v2, R)
positions = Point{3,Float64}[(4.535533905932738, -1.5355339059327373, 3.0),
Expand Down Expand Up @@ -95,7 +96,7 @@ end
pt_expa = Point{2,Int}[(0, 0), (1, 0), (0, 1), (1, 1)]
@test decompose(Point{2,Int}, a) == pt_expa
mesh = normal_mesh(a)
@test decompose(Point2f0, mesh) == pt_expa
@test decompose(Point2f, mesh) == pt_expa

b = Rect(Vec(1, 1, 1), Vec(1, 1, 1))
pt_expb = Point{3,Int64}[[1, 1, 1], [1, 1, 2], [1, 2, 2], [1, 2, 1], [1, 1, 1],
Expand Down Expand Up @@ -147,27 +148,30 @@ end
end

@testset "HyperSphere" begin
sphere = Sphere{Float32}(Point3f0(0), 1.0f0)
sphere = Sphere{Float32}(Point3f(0,0,0), 1.0f0)

points = decompose(Point, Tesselation(sphere, 3))
point_target = Point{3,Float32}[[0.0, 0.0, 1.0], [1.0, 0.0, 6.12323e-17],
[1.22465e-16, 0.0, -1.0], [-0.0, 0.0, 1.0],
[-1.0, 1.22465e-16, 6.12323e-17],
[-1.22465e-16, 1.49976e-32, -1.0], [0.0, -0.0, 1.0],
[1.0, -2.44929e-16, 6.12323e-17],
[1.22465e-16, -2.99952e-32, -1.0]]
@test points ≈ point_target
target = Point3f[(0.0, 0.0, 1.0),
(1.0, 0.0, 6.12323e-17),
(1.22465e-16, 0.0, -1.0),
(-0.0, 0.0, 1.0),
(-1.0, 1.22465e-16, 6.12323e-17),
(-1.22465e-16, 1.49976e-32, -1.0),
(0.0, -0.0, 1.0),
(1.0, -2.44929e-16, 6.12323e-17),
(1.22465e-16, -2.99952e-32, -1.0)]
@test all(coordinates(p) ≈ coordinates(t) for (p, t) in zip(points, target))

f = decompose(TriangleFace{Int}, Tesselation(sphere, 3))
face_target = TriangleFace{Int}[[1, 2, 5], [1, 5, 4], [2, 3, 6], [2, 6, 5], [4, 5, 8],
[4, 8, 7], [5, 6, 9], [5, 9, 8]]
@test f == face_target
circle = Circle(Point2f0(0), 1.0f0)
points = decompose(Point2f0, Tesselation(circle, 20))
circle = HyperSphere(Point2f(0, 0), 1.0f0)
points = decompose(Point2f, Tesselation(circle, 20))
@test length(points) == 20
tess_circle = Tesselation(circle, 32)
mesh = triangle_mesh(tess_circle)
@test decompose(Point2f0, mesh) ≈ decompose(Point2f0, tess_circle)
@test decompose(Point2f, mesh) ≈ decompose(Point2f, tess_circle)
end

@testset "Rectangles" begin
Expand Down
Loading