From 9085832fbf6bed6d814b83e21483ba37ca7d8446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 31 Aug 2020 11:50:20 -0300 Subject: [PATCH 1/4] Primitives in separate folder --- src/GeometryBasics.jl | 2 +- src/geometry_primitives.jl | 227 +---------------------------- src/primitives.jl | 8 + src/primitives/cylinders.jl | 102 +++++++++++++ src/primitives/particles.jl | 4 + src/primitives/pyramids.jl | 27 ++++ src/{ => primitives}/rectangles.jl | 11 ++ src/primitives/spheres.jl | 71 +++++++++ 8 files changed, 225 insertions(+), 227 deletions(-) create mode 100644 src/primitives.jl create mode 100644 src/primitives/cylinders.jl create mode 100644 src/primitives/particles.jl create mode 100644 src/primitives/pyramids.jl rename src/{ => primitives}/rectangles.jl (98%) create mode 100644 src/primitives/spheres.jl diff --git a/src/GeometryBasics.jl b/src/GeometryBasics.jl index 183a4cf1..d496d101 100644 --- a/src/GeometryBasics.jl +++ b/src/GeometryBasics.jl @@ -8,11 +8,11 @@ module GeometryBasics include("fixed_arrays.jl") include("offsetintegers.jl") include("basic_types.jl") + include("primitives.jl") include("interfaces.jl") include("metadata.jl") include("viewtypes.jl") include("geometry_primitives.jl") - include("rectangles.jl") include("meshes.jl") include("triangulation.jl") include("lines.jl") diff --git a/src/geometry_primitives.jl b/src/geometry_primitives.jl index 9b1564c3..c50ed54e 100644 --- a/src/geometry_primitives.jl +++ b/src/geometry_primitives.jl @@ -114,229 +114,4 @@ function normals(vertices::AbstractVector{<: AbstractPoint{3, T}}, end normals_result .= normalize.(normals_result) return normals_result -end - -## -# Some more primitive types - -""" - HyperSphere{N, T} - -A `HyperSphere` is a generalization of a sphere into N-dimensions. -A `center` and radius, `r`, must be specified. -""" -struct HyperSphere{N, T} <: GeometryPrimitive{N, T} - center::Point{N, T} - r::T -end -""" - Circle{T} - -An alias for a HyperSphere of dimension 2. (i.e. `HyperSphere{2, T}`) -""" -const Circle{T} = HyperSphere{2, T} - -""" - Sphere{T} - -An alias for a HyperSphere of dimension 3. (i.e. `HyperSphere{3, T}`) -""" -const Sphere{T} = HyperSphere{3, T} - -""" - Quad{T} - -A rectangle in 3D space. -""" -struct Quad{T} <: GeometryPrimitive{3, T} - downleft::Vec{3, T} - width ::Vec{3, T} - height ::Vec{3, T} -end - -struct Pyramid{T} <: GeometryPrimitive{3, T} - middle::Point{3, T} - length::T - width ::T -end - -struct Particle{N, T} <: GeometryPrimitive{N, T} - position::Point{N, T} - velocity::Vec{N, T} -end - -""" - Cylinder{N, T} - -A `Cylinder` is a 2D rectangle or a 3D cylinder defined by its origin point, -its extremity and a radius. `origin`, `extremity` and `r`, must be specified. -""" -struct Cylinder{N, T} <: GeometryPrimitive{N, T} - origin::Point{N,T} - extremity::Point{N,T} - r::T -end - -""" - Cylinder2{T} - Cylinder3{T} - -A `Cylinder2` or `Cylinder3` is a 2D/3D cylinder defined by its origin point, -its extremity and a radius. `origin`, `extremity` and `r`, must be specified. -""" -const Cylinder2{T} = Cylinder{2, T} -const Cylinder3{T} = Cylinder{3, T} - -origin(c::Cylinder{N, T}) where {N, T} = c.origin -extremity(c::Cylinder{N, T}) where {N, T} = c.extremity -radius(c::Cylinder{N, T}) where {N, T} = c.r -height(c::Cylinder{N, T}) where {N, T} = norm(c.extremity - c.origin) -direction(c::Cylinder{N, T}) where {N, T} = (c.extremity .- c.origin) ./ height(c) - -function rotation(c::Cylinder{2, T}) where T - d2 = direction(c); u = @SVector [d2[1], d2[2], T(0)] - v = @MVector [u[2], -u[1], T(0)] - normalize!(v) - return hcat(v, u, @SVector T[0, 0, 1]) -end - -function rotation(c::Cylinder{3, T}) where T - d3 = direction(c); u = @SVector [d3[1], d3[2], d3[3]] - if abs(u[1]) > 0 || abs(u[2]) > 0 - v = @MVector [u[2], -u[1], T(0)] - else - v = @MVector [T(0), -u[3], u[2]] - end - normalize!(v) - w = @SVector [u[2] * v[3] - u[3] * v[2], -u[1] * v[3] + u[3] * v[1], u[1] * v[2] - u[2] * v[1]] - return hcat(v, w, u) -end - -function coordinates(c::Cylinder{2, T}, nvertices=(2, 2)) where T - r = Rect(c.origin[1] - c.r/2, c.origin[2], c.r, height(c)) - M = rotation(c) - points = coordinates(r, nvertices) - vo = to_pointn(Point3{T}, origin(c)) - return (M * (to_pointn(Point3{T}, point) .- vo) .+ vo for point in points) -end - -function faces(sphere::Cylinder{2}, nvertices=(2, 2)) - return faces(Rect(0, 0, 1, 1), nvertices) -end - -function coordinates(c::Cylinder{3, T}, nvertices=30) where T - if isodd(nvertices) - nvertices = 2 * (nvertices ÷ 2) - end - nvertices = max(8, nvertices); - nbv = nvertices ÷ 2 - - M = rotation(c) - h = height(c) - range = 1:(2 * nbv + 2) - function inner(i) - if i == length(range) - return c.extremity - elseif i == length(range) - 1 - return origin(c) - else - phi = T((2π * (((i + 1) ÷ 2) - 1)) / nbv) - up = ifelse(isodd(i), 0, h) - return (M * Point(c.r * cos(phi), c.r * sin(phi), up)) .+ c.origin - end - end - - return (inner(i) for i in range) -end - -function faces(c::Cylinder{3}, facets=30) - isodd(facets) ? facets = 2 * div(facets, 2) : nothing - facets < 8 ? facets = 8 : nothing; nbv = Int(facets / 2) - indexes = Vector{TriangleFace{Int}}(undef, facets) - index = 1 - for j = 1:(nbv-1) - indexes[index] = (index + 2, index + 1, index) - indexes[index + 1] = ( index + 3, index + 1, index + 2) - index += 2 - end - indexes[index] = (1, index + 1, index) - indexes[index + 1] = (2, index + 1, 1) - - for i = 1:length(indexes) - i%2 == 1 ? push!(indexes, (indexes[i][1], indexes[i][3], 2*nbv+1)) : push!(indexes,(indexes[i][2], indexes[i][1], 2*nbv+2)) - end - return indexes -end - -## -# Sphere - -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(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 -end - -centered(S::Type{HyperSphere{N, T}}) where {N, T} = S(Vec{N,T}(0), T(0.5)) -centered(::Type{T}) where {T <: HyperSphere} = centered(HyperSphere{ndims_or(T, 3), eltype_or(T, Float32)}) - -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)) -end - -function texturecoordinates(s::Circle, nvertices=64) - return coordinates(Circle(Point2f0(0.5), 0.5f0), nvertices) -end - -function coordinates(s::Sphere, nvertices=24) - θ = LinRange(0, pi, nvertices); φ = LinRange(0, 2pi, nvertices) - inner(θ, φ) = Point(cos(φ)*sin(θ), sin(φ)*sin(θ), cos(θ)) .* s.r .+ s.center - return ivec((inner(θ, φ) for θ in θ, φ in φ)) -end - -function texturecoordinates(s::Sphere, nvertices=24) - ux = LinRange(0, 1, nvertices) - return ivec(((φ, θ) for θ in reverse(ux), φ in ux)) -end - -function faces(sphere::Sphere, nvertices=24) - return faces(Rect(0, 0, 1, 1), (nvertices, nvertices)) -end - -function normals(s::Sphere{T}, nvertices=24) where {T} - return coordinates(Sphere(Point{3, T}(0), 1), nvertices) -end - -function coordinates(p::Pyramid{T}, nvertices=nothing) where {T} - leftup = Point{3, T}(-p.width , p.width, 0) / 2 - leftdown = Point(-p.width, -p.width, 0) / 2 - tip = Point{3, T}(p.middle + Point{3, T}(0, 0, p.length)) - lu = Point{3, T}(p.middle + leftup) - ld = Point{3, T}(p.middle + leftdown) - ru = Point{3, T}(p.middle - leftdown) - rd = Point{3, T}(p.middle - leftup) - return Point{3, T}[ - tip, rd, ru, - tip, ru, lu, - tip, lu, ld, - tip, ld, rd, - rd, ru, lu, - lu, ld, rd - ] -end - -function faces(r::Pyramid, nvertices=nothing) where FT - return (TriangleFace(triangle) for triangle in TupleView{3}(1:18)) -end +end \ No newline at end of file diff --git a/src/primitives.jl b/src/primitives.jl new file mode 100644 index 00000000..e399c170 --- /dev/null +++ b/src/primitives.jl @@ -0,0 +1,8 @@ +# TODO: define common API for all primitives + +# include implementations +include("primitives/rectangles.jl") +include("primitives/spheres.jl") +include("primitives/cylinders.jl") +include("primitives/pyramids.jl") +include("primitives/particles.jl") \ No newline at end of file diff --git a/src/primitives/cylinders.jl b/src/primitives/cylinders.jl new file mode 100644 index 00000000..713bd023 --- /dev/null +++ b/src/primitives/cylinders.jl @@ -0,0 +1,102 @@ +""" + Cylinder{N, T} + +A `Cylinder` is a 2D rectangle or a 3D cylinder defined by its origin point, +its extremity and a radius. `origin`, `extremity` and `r`, must be specified. +""" +struct Cylinder{N, T} <: GeometryPrimitive{N, T} + origin::Point{N,T} + extremity::Point{N,T} + r::T +end + +""" + Cylinder2{T} + Cylinder3{T} + +A `Cylinder2` or `Cylinder3` is a 2D/3D cylinder defined by its origin point, +its extremity and a radius. `origin`, `extremity` and `r`, must be specified. +""" +const Cylinder2{T} = Cylinder{2, T} +const Cylinder3{T} = Cylinder{3, T} + +origin(c::Cylinder{N, T}) where {N, T} = c.origin +extremity(c::Cylinder{N, T}) where {N, T} = c.extremity +radius(c::Cylinder{N, T}) where {N, T} = c.r +height(c::Cylinder{N, T}) where {N, T} = norm(c.extremity - c.origin) +direction(c::Cylinder{N, T}) where {N, T} = (c.extremity .- c.origin) ./ height(c) + +function rotation(c::Cylinder{2, T}) where T + d2 = direction(c); u = @SVector [d2[1], d2[2], T(0)] + v = @MVector [u[2], -u[1], T(0)] + normalize!(v) + return hcat(v, u, @SVector T[0, 0, 1]) +end + +function rotation(c::Cylinder{3, T}) where T + d3 = direction(c); u = @SVector [d3[1], d3[2], d3[3]] + if abs(u[1]) > 0 || abs(u[2]) > 0 + v = @MVector [u[2], -u[1], T(0)] + else + v = @MVector [T(0), -u[3], u[2]] + end + normalize!(v) + w = @SVector [u[2] * v[3] - u[3] * v[2], -u[1] * v[3] + u[3] * v[1], u[1] * v[2] - u[2] * v[1]] + return hcat(v, w, u) +end + +function coordinates(c::Cylinder{2, T}, nvertices=(2, 2)) where T + r = Rect(c.origin[1] - c.r/2, c.origin[2], c.r, height(c)) + M = rotation(c) + points = coordinates(r, nvertices) + vo = to_pointn(Point3{T}, origin(c)) + return (M * (to_pointn(Point3{T}, point) .- vo) .+ vo for point in points) +end + +function faces(sphere::Cylinder{2}, nvertices=(2, 2)) + return faces(Rect(0, 0, 1, 1), nvertices) +end + +function coordinates(c::Cylinder{3, T}, nvertices=30) where T + if isodd(nvertices) + nvertices = 2 * (nvertices ÷ 2) + end + nvertices = max(8, nvertices); + nbv = nvertices ÷ 2 + + M = rotation(c) + h = height(c) + range = 1:(2 * nbv + 2) + function inner(i) + if i == length(range) + return c.extremity + elseif i == length(range) - 1 + return origin(c) + else + phi = T((2π * (((i + 1) ÷ 2) - 1)) / nbv) + up = ifelse(isodd(i), 0, h) + return (M * Point(c.r * cos(phi), c.r * sin(phi), up)) .+ c.origin + end + end + + return (inner(i) for i in range) +end + +function faces(c::Cylinder{3}, facets=30) + isodd(facets) ? facets = 2 * div(facets, 2) : nothing + facets < 8 ? facets = 8 : nothing; nbv = Int(facets / 2) + indexes = Vector{TriangleFace{Int}}(undef, facets) + index = 1 + for j = 1:(nbv-1) + indexes[index] = (index + 2, index + 1, index) + indexes[index + 1] = ( index + 3, index + 1, index + 2) + index += 2 + end + indexes[index] = (1, index + 1, index) + indexes[index + 1] = (2, index + 1, 1) + + for i = 1:length(indexes) + i%2 == 1 ? push!(indexes, (indexes[i][1], indexes[i][3], 2*nbv+1)) : push!(indexes,(indexes[i][2], indexes[i][1], 2*nbv+2)) + end + return indexes +end \ No newline at end of file diff --git a/src/primitives/particles.jl b/src/primitives/particles.jl new file mode 100644 index 00000000..1606d3c2 --- /dev/null +++ b/src/primitives/particles.jl @@ -0,0 +1,4 @@ +struct Particle{N, T} <: GeometryPrimitive{N, T} + position::Point{N, T} + velocity::Vec{N, T} +end \ No newline at end of file diff --git a/src/primitives/pyramids.jl b/src/primitives/pyramids.jl new file mode 100644 index 00000000..fe393f34 --- /dev/null +++ b/src/primitives/pyramids.jl @@ -0,0 +1,27 @@ +struct Pyramid{T} <: GeometryPrimitive{3, T} + middle::Point{3, T} + length::T + width ::T +end + +function coordinates(p::Pyramid{T}, nvertices=nothing) where {T} + leftup = Point{3, T}(-p.width , p.width, 0) / 2 + leftdown = Point(-p.width, -p.width, 0) / 2 + tip = Point{3, T}(p.middle + Point{3, T}(0, 0, p.length)) + lu = Point{3, T}(p.middle + leftup) + ld = Point{3, T}(p.middle + leftdown) + ru = Point{3, T}(p.middle - leftdown) + rd = Point{3, T}(p.middle - leftup) + return Point{3, T}[ + tip, rd, ru, + tip, ru, lu, + tip, lu, ld, + tip, ld, rd, + rd, ru, lu, + lu, ld, rd + ] +end + +function faces(r::Pyramid, nvertices=nothing) where FT + return (TriangleFace(triangle) for triangle in TupleView{3}(1:18)) +end \ No newline at end of file diff --git a/src/rectangles.jl b/src/primitives/rectangles.jl similarity index 98% rename from src/rectangles.jl rename to src/primitives/rectangles.jl index 8c7c7a47..a628b226 100644 --- a/src/rectangles.jl +++ b/src/primitives/rectangles.jl @@ -578,3 +578,14 @@ function faces(rect::Rect3D) (21,22,23,24), ] end + +""" + Quad{T} + +A rectangle in 3D space. +""" +struct Quad{T} <: GeometryPrimitive{3, T} + downleft::Vec{3, T} + width ::Vec{3, T} + height ::Vec{3, T} +end \ No newline at end of file diff --git a/src/primitives/spheres.jl b/src/primitives/spheres.jl new file mode 100644 index 00000000..1ef58f24 --- /dev/null +++ b/src/primitives/spheres.jl @@ -0,0 +1,71 @@ +""" + HyperSphere{N, T} + +A `HyperSphere` is a generalization of a sphere into N-dimensions. +A `center` and radius, `r`, must be specified. +""" +struct HyperSphere{N, T} <: GeometryPrimitive{N, T} + center::Point{N, T} + r::T +end +""" + Circle{T} + +An alias for a HyperSphere of dimension 2. (i.e. `HyperSphere{2, T}`) +""" +const Circle{T} = HyperSphere{2, T} + +""" + Sphere{T} + +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(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 +end + +centered(S::Type{HyperSphere{N, T}}) where {N, T} = S(Vec{N,T}(0), T(0.5)) +centered(::Type{T}) where {T <: HyperSphere} = centered(HyperSphere{ndims_or(T, 3), eltype_or(T, Float32)}) + +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)) +end + +function texturecoordinates(s::Circle, nvertices=64) + return coordinates(Circle(Point2f0(0.5), 0.5f0), nvertices) +end + +function coordinates(s::Sphere, nvertices=24) + θ = LinRange(0, pi, nvertices); φ = LinRange(0, 2pi, nvertices) + inner(θ, φ) = Point(cos(φ)*sin(θ), sin(φ)*sin(θ), cos(θ)) .* s.r .+ s.center + return ivec((inner(θ, φ) for θ in θ, φ in φ)) +end + +function texturecoordinates(s::Sphere, nvertices=24) + ux = LinRange(0, 1, nvertices) + return ivec(((φ, θ) for θ in reverse(ux), φ in ux)) +end + +function faces(sphere::Sphere, nvertices=24) + return faces(Rect(0, 0, 1, 1), (nvertices, nvertices)) +end + +function normals(s::Sphere{T}, nvertices=24) where {T} + return coordinates(Sphere(Point{3, T}(0), 1), nvertices) +end From 37484c633d4c4ed0e53c3dbd6017098b12604892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 7 Sep 2020 17:47:41 -0300 Subject: [PATCH 2/4] Fix EOF --- src/geometry_primitives.jl | 2 +- src/primitives.jl | 2 +- src/primitives/pyramids.jl | 2 +- src/primitives/rectangles.jl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/geometry_primitives.jl b/src/geometry_primitives.jl index c50ed54e..a023bda5 100644 --- a/src/geometry_primitives.jl +++ b/src/geometry_primitives.jl @@ -114,4 +114,4 @@ function normals(vertices::AbstractVector{<: AbstractPoint{3, T}}, end normals_result .= normalize.(normals_result) return normals_result -end \ No newline at end of file +end diff --git a/src/primitives.jl b/src/primitives.jl index e399c170..7c8de634 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -5,4 +5,4 @@ include("primitives/rectangles.jl") include("primitives/spheres.jl") include("primitives/cylinders.jl") include("primitives/pyramids.jl") -include("primitives/particles.jl") \ No newline at end of file +include("primitives/particles.jl") diff --git a/src/primitives/pyramids.jl b/src/primitives/pyramids.jl index fe393f34..4207cc1f 100644 --- a/src/primitives/pyramids.jl +++ b/src/primitives/pyramids.jl @@ -24,4 +24,4 @@ end function faces(r::Pyramid, nvertices=nothing) where FT return (TriangleFace(triangle) for triangle in TupleView{3}(1:18)) -end \ No newline at end of file +end diff --git a/src/primitives/rectangles.jl b/src/primitives/rectangles.jl index a628b226..753be8b3 100644 --- a/src/primitives/rectangles.jl +++ b/src/primitives/rectangles.jl @@ -588,4 +588,4 @@ struct Quad{T} <: GeometryPrimitive{3, T} downleft::Vec{3, T} width ::Vec{3, T} height ::Vec{3, T} -end \ No newline at end of file +end From 56c754b769a82591d3dcf5346d3d46f3a18a46fb Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 10 Sep 2020 18:42:18 +0200 Subject: [PATCH 3/4] remove unnecessary file --- src/GeometryBasics.jl | 8 +++++++- src/primitives.jl | 8 -------- 2 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 src/primitives.jl diff --git a/src/GeometryBasics.jl b/src/GeometryBasics.jl index d496d101..1cf38250 100644 --- a/src/GeometryBasics.jl +++ b/src/GeometryBasics.jl @@ -8,7 +8,13 @@ module GeometryBasics include("fixed_arrays.jl") include("offsetintegers.jl") include("basic_types.jl") - include("primitives.jl") + + include("primitives/rectangles.jl") + include("primitives/spheres.jl") + include("primitives/cylinders.jl") + include("primitives/pyramids.jl") + include("primitives/particles.jl") + include("interfaces.jl") include("metadata.jl") include("viewtypes.jl") diff --git a/src/primitives.jl b/src/primitives.jl deleted file mode 100644 index 7c8de634..00000000 --- a/src/primitives.jl +++ /dev/null @@ -1,8 +0,0 @@ -# TODO: define common API for all primitives - -# include implementations -include("primitives/rectangles.jl") -include("primitives/spheres.jl") -include("primitives/cylinders.jl") -include("primitives/pyramids.jl") -include("primitives/particles.jl") From 003851d3916e56abc63f0c9cb134a8032e738185 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 10 Sep 2020 19:48:53 +0200 Subject: [PATCH 4/4] run juliaformatter --- src/GeometryBasics.jl | 116 ++++++------ src/basic_types.jl | 243 ++++++++++++-------------- src/boundingboxes.jl | 38 ++-- src/fixed_arrays.jl | 235 +++++++++++++------------ src/geometry_primitives.jl | 53 +++--- src/interfaces.jl | 51 +++--- src/lines.jl | 50 +++--- src/meshes.jl | 75 ++++---- src/metadata.jl | 117 +++++++------ src/offsetintegers.jl | 45 ++--- src/primitives/cylinders.jl | 59 ++++--- src/primitives/particles.jl | 8 +- src/primitives/pyramids.jl | 30 ++-- src/primitives/rectangles.jl | 330 ++++++++++++++++------------------- src/primitives/spheres.jl | 31 ++-- src/triangulation.jl | 134 +++++++------- src/viewtypes.jl | 81 +++++---- 17 files changed, 850 insertions(+), 846 deletions(-) diff --git a/src/GeometryBasics.jl b/src/GeometryBasics.jl index 1cf38250..c3388279 100644 --- a/src/GeometryBasics.jl +++ b/src/GeometryBasics.jl @@ -1,69 +1,71 @@ module GeometryBasics - using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra - using EarCut_jll +using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra +using EarCut_jll - using Base: @propagate_inbounds +using Base: @propagate_inbounds - include("fixed_arrays.jl") - include("offsetintegers.jl") - include("basic_types.jl") +include("fixed_arrays.jl") +include("offsetintegers.jl") +include("basic_types.jl") - include("primitives/rectangles.jl") - include("primitives/spheres.jl") - include("primitives/cylinders.jl") - include("primitives/pyramids.jl") - include("primitives/particles.jl") +include("primitives/rectangles.jl") +include("primitives/spheres.jl") +include("primitives/cylinders.jl") +include("primitives/pyramids.jl") +include("primitives/particles.jl") - include("interfaces.jl") - include("metadata.jl") - include("viewtypes.jl") - include("geometry_primitives.jl") - include("meshes.jl") - include("triangulation.jl") - include("lines.jl") - include("boundingboxes.jl") +include("interfaces.jl") +include("metadata.jl") +include("viewtypes.jl") +include("geometry_primitives.jl") +include("meshes.jl") +include("triangulation.jl") +include("lines.jl") +include("boundingboxes.jl") - export AbstractGeometry, GeometryPrimitive - export Mat, Point, Vec - export LineFace, Polytope, Line, NgonFace, convert_simplex - export LineString, AbstractPolygon, Polygon, MultiPoint, MultiLineString, MultiPolygon - export Simplex, connect, Triangle, NSimplex, Tetrahedron - export QuadFace, metafree, coordinates, TetrahedronFace - export TupleView, SimplexFace, Mesh, meta - export Triangle, TriangleP - export AbstractFace, TriangleFace, QuadFace, GLTriangleFace - export OffsetInteger, ZeroIndex, OneIndex, GLIndex - export FaceView, SimpleFaceView - export AbstractPoint, PointMeta, PointWithUV - export PolygonMeta, MultiPointMeta, MultiLineStringMeta, MeshMeta, LineStringMeta, MultiPolygonMeta - export decompose, coordinates, faces, normals, decompose_uv, decompose_normals, texturecoordinates - export Tesselation, pointmeta, Normal, UV, UVW - export GLTriangleFace, GLNormalMesh3D, GLPlainTriangleMesh, GLUVMesh3D, GLUVNormalMesh3D - export AbstractMesh, Mesh, TriangleMesh - export GLNormalMesh2D, PlainTriangleMesh - export MetaT, meta_table +export AbstractGeometry, GeometryPrimitive +export Mat, Point, Vec +export LineFace, Polytope, Line, NgonFace, convert_simplex +export LineString, AbstractPolygon, Polygon, MultiPoint, MultiLineString, MultiPolygon +export Simplex, connect, Triangle, NSimplex, Tetrahedron +export QuadFace, metafree, coordinates, TetrahedronFace +export TupleView, SimplexFace, Mesh, meta +export Triangle, TriangleP +export AbstractFace, TriangleFace, QuadFace, GLTriangleFace +export OffsetInteger, ZeroIndex, OneIndex, GLIndex +export FaceView, SimpleFaceView +export AbstractPoint, PointMeta, PointWithUV +export PolygonMeta, MultiPointMeta, MultiLineStringMeta, MeshMeta, LineStringMeta, + MultiPolygonMeta +export decompose, coordinates, faces, normals, decompose_uv, decompose_normals, + texturecoordinates +export Tesselation, pointmeta, Normal, UV, UVW +export GLTriangleFace, GLNormalMesh3D, GLPlainTriangleMesh, GLUVMesh3D, GLUVNormalMesh3D +export AbstractMesh, Mesh, TriangleMesh +export GLNormalMesh2D, PlainTriangleMesh +export MetaT, meta_table - # all the different predefined mesh types - # Note: meshes can contain arbitrary meta information, - export AbstractMesh, TriangleMesh, PlainMesh, GLPlainMesh, GLPlainMesh2D, GLPlainMesh3D - export UVMesh, GLUVMesh, GLUVMesh2D, GLUVMesh3D - export NormalMesh, GLNormalMesh, GLNormalMesh2D, GLNormalMesh3D - export NormalUVMesh, GLNormalUVMesh, GLNormalUVMesh2D, GLNormalUVMesh3D - export NormalUVWMesh, GLNormalUVWMesh, GLNormalUVWMesh2D, GLNormalUVWMesh3D +# all the different predefined mesh types +# Note: meshes can contain arbitrary meta information, +export AbstractMesh, TriangleMesh, PlainMesh, GLPlainMesh, GLPlainMesh2D, GLPlainMesh3D +export UVMesh, GLUVMesh, GLUVMesh2D, GLUVMesh3D +export NormalMesh, GLNormalMesh, GLNormalMesh2D, GLNormalMesh3D +export NormalUVMesh, GLNormalUVMesh, GLNormalUVMesh2D, GLNormalUVMesh3D +export NormalUVWMesh, GLNormalUVWMesh, GLNormalUVWMesh2D, GLNormalUVWMesh3D - # mesh creation functions - export triangle_mesh, triangle_mesh, uv_mesh - export uv_mesh, normal_mesh, uv_normal_mesh +# mesh creation functions +export triangle_mesh, triangle_mesh, uv_mesh +export uv_mesh, normal_mesh, uv_normal_mesh - export height, origin, radius, width, widths, xwidth, yheight - export HyperSphere, Circle, Sphere - export Cylinder, Cylinder2, Cylinder3, Pyramid, extremity - export Rect, Rect2D, Rect3D, IRect, IRect2D, IRect3D, FRect, FRect2D, FRect3D - export before, during, isinside, isoutside, meets, overlaps, intersects, finishes - export centered, direction, area, update - export max_dist_dim, max_euclidean, max_euclideansq, min_dist_dim, min_euclidean - export min_euclideansq, minmax_dist_dim, minmax_euclidean, minmax_euclideansq - export self_intersections, split_intersections +export height, origin, radius, width, widths, xwidth, yheight +export HyperSphere, Circle, Sphere +export Cylinder, Cylinder2, Cylinder3, Pyramid, extremity +export Rect, Rect2D, Rect3D, IRect, IRect2D, IRect3D, FRect, FRect2D, FRect3D +export before, during, isinside, isoutside, meets, overlaps, intersects, finishes +export centered, direction, area, update +export max_dist_dim, max_euclidean, max_euclideansq, min_dist_dim, min_euclidean +export min_euclideansq, minmax_dist_dim, minmax_euclidean, minmax_euclideansq +export self_intersections, split_intersections end # module diff --git a/src/basic_types.jl b/src/basic_types.jl index ef3055aa..9b35129d 100644 --- a/src/basic_types.jl +++ b/src/basic_types.jl @@ -1,48 +1,48 @@ """ Abstract Geometry in R{Dim} with Number type T """ -abstract type AbstractGeometry{Dim, T <: Number} end -abstract type GeometryPrimitive{Dim, T} <: AbstractGeometry{Dim, T} end -Base.ndims(x::AbstractGeometry{Dim}) where Dim = Dim +abstract type AbstractGeometry{Dim,T<:Number} end +abstract type GeometryPrimitive{Dim,T} <: AbstractGeometry{Dim,T} end +Base.ndims(x::AbstractGeometry{Dim}) where {Dim} = Dim """ Geometry made of N connected points. Connected as one flat geometry, it makes a Ngon / Polygon. Connected as volume it will be a Simplex / Tri / Cube. Note That `Polytope{N} where N == 3` denotes a Triangle both as a Simplex or Ngon. """ -abstract type Polytope{Dim, T} <: AbstractGeometry{Dim, T} end -abstract type AbstractPolygon{Dim, T} <: Polytope{Dim, T} end +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 +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 -abstract type AbstractSimplex{Dim, N, T} <: StaticVector{Dim, T} end +abstract type AbstractSimplex{Dim,N,T} <: StaticVector{Dim,T} end """ Face index, connecting points to form a simplex """ @fixed_vector SimplexFace AbstractSimplexFace -const TetrahedronFace{T} = SimplexFace{4, T} -Face(::Type{<: SimplexFace{N}}, ::Type{T}) where {N, T} = SimplexFace{N, T} +const TetrahedronFace{T} = SimplexFace{4,T} +Face(::Type{<:SimplexFace{N}}, ::Type{T}) where {N,T} = SimplexFace{N,T} """ Face index, connecting points to form an Ngon """ @fixed_vector NgonFace AbstractNgonFace -const LineFace{T} = NgonFace{2, T} -const TriangleFace{T} = NgonFace{3, T} -const QuadFace{T} = NgonFace{4, T} +const LineFace{T} = NgonFace{2,T} +const TriangleFace{T} = NgonFace{3,T} +const QuadFace{T} = NgonFace{4,T} -function Base.show(io::IO, x::TriangleFace{T}) where T - print(io, "TriangleFace(", join(x, ", "), ")") +function Base.show(io::IO, x::TriangleFace{T}) where {T} + return print(io, "TriangleFace(", join(x, ", "), ")") end -Face(::Type{<: NgonFace{N}}, ::Type{T}) where {N, T} = NgonFace{N, T} -Face(F::Type{NgonFace{N, FT}}, ::Type{T}) where {FT, N, T} = F +Face(::Type{<:NgonFace{N}}, ::Type{T}) where {N,T} = NgonFace{N,T} +Face(F::Type{NgonFace{N,FT}}, ::Type{T}) where {FT,N,T} = F @propagate_inbounds Base.getindex(x::Polytope, i::Integer) = coordinates(x)[i] @propagate_inbounds Base.iterate(x::Polytope) = iterate(coordinates(x)) @@ -56,62 +56,60 @@ Fixed Size Polygon, e.g. - N = 5 : Pentagon - ... """ -struct Ngon{ - Dim, T <: Real, - N, - Point <: AbstractPoint{Dim, T} - } <: AbstractPolygon{Dim, T} +struct Ngon{Dim,T<:Real,N,Point<:AbstractPoint{Dim,T}} <: AbstractPolygon{Dim,T} - points::SVector{N, Point} + points::SVector{N,Point} end -const NNgon{N} = Ngon{Dim, T, N, P} where {Dim, T, P} +const NNgon{N} = Ngon{Dim,T,N,P} where {Dim,T,P} -function (::Type{<: NNgon{N}})(points::Vararg{P, N}) where {P <: AbstractPoint{Dim, T}, N} where {Dim, T} - return Ngon{Dim, T, N, P}(SVector(points)) +function (::Type{<:NNgon{N}})(points::Vararg{P,N}) where {P<:AbstractPoint{Dim,T}, + N} where {Dim,T} + return Ngon{Dim,T,N,P}(SVector(points)) end -Base.show(io::IO, x::NNgon{N}) where N = print(io, "Ngon{$N}(", join(x, ", "), ")") +Base.show(io::IO, x::NNgon{N}) where {N} = print(io, "Ngon{$N}(", join(x, ", "), ")") # Interfaces coordinates(x::Ngon) = x.points # Base Array interface -Base.length(::Type{<: NNgon{N}}) where N = N -Base.length(::NNgon{N}) where N = N +Base.length(::Type{<:NNgon{N}}) where {N} = N +Base.length(::NNgon{N}) where {N} = N """ The Ngon Polytope element type when indexing an array of points with a SimplexFace """ -function Polytope(P::Type{<: AbstractPoint{Dim, T}}, ::Type{<: AbstractNgonFace{N, IT}}) where {N, Dim, T, IT} - return Ngon{Dim, T, N, P} +function Polytope(P::Type{<:AbstractPoint{Dim,T}}, + ::Type{<:AbstractNgonFace{N,IT}}) where {N,Dim,T,IT} + return Ngon{Dim,T,N,P} end """ The fully concrete Ngon type, when constructed from a point type! """ -function Polytope(::Type{<: NNgon{N}}, P::Type{<: AbstractPoint{NDim, T}}) where {N, NDim, T} - return Ngon{NDim, T, N, P} +function Polytope(::Type{<:NNgon{N}}, P::Type{<:AbstractPoint{NDim,T}}) where {N,NDim,T} + return Ngon{NDim,T,N,P} end -const LineP{Dim, T, P <: AbstractPoint{Dim, T}} = Ngon{Dim, T, 2, P} -const Line{Dim, T} = LineP{Dim, T, Point{Dim, T}} +const LineP{Dim,T,P<:AbstractPoint{Dim,T}} = Ngon{Dim,T,2,P} +const Line{Dim,T} = LineP{Dim,T,Point{Dim,T}} # Simplex{D, T, 3} & Ngon{D, T, 3} are both representing a triangle. # Since Ngon is supposed to be flat and a triangle is flat, lets prefer Ngon # for triangle: -const TriangleP{Dim, T, P <: AbstractPoint{Dim, T}} = Ngon{Dim, T, 3, P} -const Triangle{Dim, T} = TriangleP{Dim, T, Point{Dim, T}} -const Triangle3d{T} = Triangle{3, T} +const TriangleP{Dim,T,P<:AbstractPoint{Dim,T}} = Ngon{Dim,T,3,P} +const Triangle{Dim,T} = TriangleP{Dim,T,Point{Dim,T}} +const Triangle3d{T} = Triangle{3,T} Base.show(io::IO, x::TriangleP) = print(io, "Triangle(", join(x, ", "), ")") -Base.summary(io::IO, x::Type{<: TriangleP}) = print(io, "Triangle") +Base.summary(io::IO, x::Type{<:TriangleP}) = print(io, "Triangle") -const Quadrilateral{Dim, T} = Ngon{Dim, T, 4, P} where P <: AbstractPoint{Dim, T} +const Quadrilateral{Dim,T} = Ngon{Dim,T,4,P} where {P<:AbstractPoint{Dim,T}} Base.show(io::IO, x::Quadrilateral) = print(io, "Quad(", join(x, ", "), ")") -Base.summary(io::IO, x::Type{<: Quadrilateral}) = print(io, "Quad") +Base.summary(io::IO, x::Type{<:Quadrilateral}) = print(io, "Quad") -function coordinates(lines::AbstractArray{LineP{Dim, T, PointType}}) where {Dim, T, PointType} - if lines isa Base.ReinterpretArray +function coordinates(lines::AbstractArray{LineP{Dim,T,PointType}}) where {Dim,T,PointType} + return if lines isa Base.ReinterpretArray return coordinates(lines.parent) else result = PointType[] @@ -138,43 +136,41 @@ This is for a simpler implementation. It applies to infinite dimensions. The structure of this type is designed to allow embedding in higher-order spaces by parameterizing on `T`. """ -struct Simplex{ - Dim, T <: Real, - N, - Point <: AbstractPoint{Dim, T}, - } <: Polytope{Dim, T} +struct Simplex{Dim,T<:Real,N,Point<:AbstractPoint{Dim,T}} <: Polytope{Dim,T} - points::SVector{N, Point} + points::SVector{N,Point} end -const NSimplex{N} = Simplex{Dim, T, N, P} where {Dim, T, P} -const TetrahedronP{T, P <: AbstractPoint{3, T}} = Simplex{3, T, 4, P} -const Tetrahedron{T} = TetrahedronP{T, Point{3, T}} +const NSimplex{N} = Simplex{Dim,T,N,P} where {Dim,T,P} +const TetrahedronP{T,P<:AbstractPoint{3,T}} = Simplex{3,T,4,P} +const Tetrahedron{T} = TetrahedronP{T,Point{3,T}} Base.show(io::IO, x::TetrahedronP) = print(io, "Tetrahedron(", join(x, ", "), ")") coordinates(x::Simplex) = x.points -function (::Type{<: NSimplex{N}})(points::Vararg{P, N}) where {P <: AbstractPoint{Dim, T}, N} where {Dim, T} - return Simplex{Dim, T, N, P}(SVector(points)) +function (::Type{<:NSimplex{N}})(points::Vararg{P,N}) where {P<:AbstractPoint{Dim,T}, + N} where {Dim,T} + return Simplex{Dim,T,N,P}(SVector(points)) end # Base Array interface -Base.length(::Type{<: NSimplex{N}}) where N = N -Base.length(::NSimplex{N}) where N = N +Base.length(::Type{<:NSimplex{N}}) where {N} = N +Base.length(::NSimplex{N}) where {N} = N """ The Simplex Polytope element type when indexing an array of points with a SimplexFace """ -function Polytope(P::Type{<: AbstractPoint{Dim, T}}, ::Type{<: AbstractSimplexFace{N}}) where {N, Dim, T} - return Simplex{Dim, T, N, P} +function Polytope(P::Type{<:AbstractPoint{Dim,T}}, + ::Type{<:AbstractSimplexFace{N}}) where {N,Dim,T} + return Simplex{Dim,T,N,P} end """ The fully concrete Simplex type, when constructed from a point type! """ -function Polytope(::Type{<: NSimplex{N}}, P::Type{<: AbstractPoint{NDim, T}}) where {N, NDim, T} - return Simplex{NDim, T, N, P} +function Polytope(::Type{<:NSimplex{N}}, P::Type{<:AbstractPoint{NDim,T}}) where {N,NDim,T} + return Simplex{NDim,T,N,P} end Base.show(io::IO, x::LineP) = print(io, "Line(", x[1], " => ", x[2], ")") @@ -183,11 +179,8 @@ Base.show(io::IO, x::LineP) = print(io, "Line(", x[1], " => ", x[2], ")") A LineString is a geometry of connected line segments """ -struct LineString{ - Dim, T <: Real, - P <: AbstractPoint, - V <: AbstractVector{<: LineP{Dim, T, P}} - } <: AbstractVector{LineP{Dim, T, P}} +struct LineString{Dim,T<:Real,P<:AbstractPoint,V<:AbstractVector{<:LineP{Dim,T,P}}} <: + AbstractVector{LineP{Dim,T,P}} points::V end @@ -197,8 +190,8 @@ Base.copy(x::LineString) = LineString(copy(x.points)) Base.size(x::LineString) = size(getfield(x, :points)) Base.getindex(x::LineString, i) = getindex(getfield(x, :points), i) -function LineString(points::AbstractVector{LineP{Dim, T, P}}) where {Dim, T, P} - return LineString{Dim, T, P, typeof(points)}(points) +function LineString(points::AbstractVector{LineP{Dim,T,P}}) where {Dim,T,P} + return LineString{Dim,T,P,typeof(points)}(points) end """ @@ -212,15 +205,16 @@ linestring = LineString(points) @assert linestring == LineString([a => b, b => c, c => d]) ``` """ -function LineString(points::AbstractVector{<: AbstractPoint}, skip = 1) +function LineString(points::AbstractVector{<:AbstractPoint}, skip=1) return LineString(connect(points, LineP, skip)) end -function LineString(points::AbstractVector{<: Pair{P, P}}) where P <: AbstractPoint{N, T} where {N, T} - return LineString(reinterpret(LineP{N, T, P}, points)) +function LineString(points::AbstractVector{<:Pair{P,P}}) where {P<:AbstractPoint{N,T}} where {N, T} + return LineString(reinterpret(LineP{N,T,P}, points)) end -function LineString(points::AbstractVector{<: AbstractPoint}, faces::AbstractVector{<: LineFace}) +function LineString(points::AbstractVector{<:AbstractPoint}, + faces::AbstractVector{<:LineFace}) return LineString(connect(points, faces)) end @@ -243,66 +237,70 @@ linestring = LineString(points, faces, 2) @assert linestring == LineString([a => b, c => d]) ``` """ -function LineString(points::AbstractVector{<: AbstractPoint}, indices::AbstractVector{<: Integer}, skip = 1) +function LineString(points::AbstractVector{<:AbstractPoint}, + indices::AbstractVector{<:Integer}, skip=1) faces = connect(indices, LineFace, skip) return LineString(points, faces) end - """ Polygon(exterior::AbstractVector{<:Point}) Polygon(exterior::AbstractVector{<:Point}, interiors::Vector{<:AbstractVector{<:AbstractPoint}}) """ -struct Polygon{ - Dim, T <: Real, - P <: AbstractPoint{Dim, T}, - L <: AbstractVector{<: LineP{Dim, T, P}}, - V <: AbstractVector{L} - } <: AbstractPolygon{Dim, T} +struct Polygon{Dim,T<:Real,P<:AbstractPoint{Dim,T},L<:AbstractVector{<:LineP{Dim,T,P}}, + V<:AbstractVector{L}} <: AbstractPolygon{Dim,T} exterior::L interiors::V end Base.copy(x::Polygon) = Polygon(copy(x.exterior), copy(x.interiors)) -Base.:(==)(a::Polygon, b::Polygon) = (a.exterior == b.exterior) && (a.interiors == b.interiors) +function Base.:(==)(a::Polygon, b::Polygon) + return (a.exterior == b.exterior) && (a.interiors == b.interiors) +end -function Polygon(exterior::E, interiors::AbstractVector{E}) where E <: AbstractVector{LineP{Dim, T, P}} where {Dim, T, P} - return Polygon{Dim, T, P, typeof(exterior), typeof(interiors)}(exterior, interiors) +function Polygon(exterior::E, + interiors::AbstractVector{E}) where + {E<:AbstractVector{LineP{Dim,T,P}}} where {Dim, T, P} + return Polygon{Dim,T,P,typeof(exterior),typeof(interiors)}(exterior, interiors) end -Polygon(exterior::L) where L <: AbstractVector{<: LineP} = Polygon(exterior, L[]) +Polygon(exterior::L) where {L<:AbstractVector{<:LineP}} = Polygon(exterior, L[]) -function Polygon(exterior::AbstractVector{P}, skip::Int = 1) where P <: AbstractPoint{Dim, T} where {Dim, T} +function Polygon(exterior::AbstractVector{P}, + skip::Int=1) where {P<:AbstractPoint{Dim,T}} where {Dim,T} return Polygon(LineString(exterior, skip)) end -function Polygon(exterior::AbstractVector{P}, faces::AbstractVector{<: Integer}, skip::Int = 1) where P <: AbstractPoint{Dim, T} where {Dim, T} +function Polygon(exterior::AbstractVector{P}, faces::AbstractVector{<:Integer}, + skip::Int=1) where {P<:AbstractPoint{Dim,T}} where {Dim,T} return Polygon(LineString(exterior, faces, skip)) end -function Polygon(exterior::AbstractVector{P}, faces::AbstractVector{<: LineFace}) where P <: AbstractPoint{Dim, T} where {Dim, T} +function Polygon(exterior::AbstractVector{P}, + faces::AbstractVector{<:LineFace}) where {P<:AbstractPoint{Dim,T}} where {Dim, T} return Polygon(LineString(exterior, faces)) end -function Polygon(exterior::AbstractVector{P}, interior::AbstractVector{<:AbstractVector{P}}) where P <: AbstractPoint{Dim, T} where {Dim, T} +function Polygon(exterior::AbstractVector{P}, + interior::AbstractVector{<:AbstractVector{P}}) where {P<:AbstractPoint{Dim, T}} where {Dim, T} ext = LineString(exterior) # We need to take extra care for empty interiors, since # if we just map over it, it won't infer the element type correctly! int = typeof(ext)[] - foreach(x-> push!(int, LineString(x)), interior) + foreach(x -> push!(int, LineString(x)), interior) return Polygon(ext, int) end -function coordinates(polygon::Polygon{N, T, PointType}) where {N, T, PointType} +function coordinates(polygon::Polygon{N,T,PointType}) where {N,T,PointType} exterior = coordinates(polygon.exterior) - if isempty(polygon.interiors) + return if isempty(polygon.interiors) return exterior else result = PointType[] append!(result, exterior) - foreach(x-> append!(result, coordinates(x)), polygon.interiors) + foreach(x -> append!(result, coordinates(x)), polygon.interiors) return result end end @@ -310,54 +308,47 @@ end """ MultiPolygon(polygons::AbstractPolygon) """ -struct MultiPolygon{ - Dim, T <: Real, - Element <: AbstractPolygon{Dim, T}, - A <: AbstractVector{Element} - } <: AbstractVector{Element} +struct MultiPolygon{Dim,T<:Real,Element<:AbstractPolygon{Dim,T}, + A<:AbstractVector{Element}} <: AbstractVector{Element} polygons::A end -function MultiPolygon(polygons::AbstractVector{P}; kw...) where P <: AbstractPolygon{Dim, T} where {Dim, T} +function MultiPolygon(polygons::AbstractVector{P}; + kw...) where {P<:AbstractPolygon{Dim,T}} where {Dim,T} return MultiPolygon(meta(polygons; kw...)) end Base.getindex(mp::MultiPolygon, i) = mp.polygons[i] Base.size(mp::MultiPolygon) = size(mp.polygons) -struct MultiLineString{ - Dim, T <: Real, - Element <: LineString{Dim, T}, - A <: AbstractVector{Element} - } <: AbstractVector{Element} +struct MultiLineString{Dim,T<:Real,Element<:LineString{Dim,T},A<:AbstractVector{Element}} <: + AbstractVector{Element} linestrings::A end -function MultiLineString(linestrings::AbstractVector{L}; kw...) where L <: AbstractVector{LineP{Dim, T, P}} where {Dim, T, P} +function MultiLineString(linestrings::AbstractVector{L}; + kw...) where {L<:AbstractVector{LineP{Dim,T,P}}} where {Dim,T,P} return MultiLineString(meta(linestrings; kw...)) end Base.getindex(ms::MultiLineString, i) = ms.linestrings[i] Base.size(ms::MultiLineString) = size(ms.linestrings) - """ MultiPoint(points::AbstractVector{AbstractPoint}) A collection of points """ -struct MultiPoint{ - Dim, T <: Real, - P <: AbstractPoint{Dim, T}, - A <: AbstractVector{P} - } <: AbstractVector{P} +struct MultiPoint{Dim,T<:Real,P<:AbstractPoint{Dim,T},A<:AbstractVector{P}} <: + AbstractVector{P} points::A end -function MultiPoint(points::AbstractVector{P}; kw...) where P <: AbstractPoint{Dim, T} where {Dim, T} +function MultiPoint(points::AbstractVector{P}; + kw...) where {P<:AbstractPoint{Dim,T}} where {Dim,T} return MultiPoint(meta(points; kw...)) end @@ -377,18 +368,15 @@ abstract type AbstractMesh{Element<:Polytope} <: AbstractVector{Element} end Mesh <: AbstractVector{Element} The conrecte AbstractMesh implementation """ -struct Mesh{ - Dim, T <: Number, - Element <: Polytope{Dim, T}, - V <: AbstractVector{Element} - } <: AbstractMesh{Element} +struct Mesh{Dim,T<:Number,Element<:Polytope{Dim,T},V<:AbstractVector{Element}} <: + AbstractMesh{Element} simplices::V # usually a FaceView, to connect a set of points via a set of faces. end Tables.schema(mesh::Mesh) = Tables.schema(getfield(mesh, :simplices)) function Base.getproperty(mesh::Mesh, name::Symbol) - if name === :position + return if name === :position # a mesh always has position defined by coordinates... return metafree(coordinates(mesh)) else @@ -398,7 +386,7 @@ end function Base.propertynames(mesh::Mesh) names = propertynames(getfield(mesh, :simplices)) - if :position in names + return if :position in names return names else # a mesh always has positions! @@ -406,26 +394,25 @@ function Base.propertynames(mesh::Mesh) end end -function Base.summary(io::IO, ::Mesh{Dim, T, Element}) where {Dim, T, Element} +function Base.summary(io::IO, ::Mesh{Dim,T,Element}) where {Dim,T,Element} print(io, "Mesh{$Dim, $T, ") summary(io, Element) - print(io, "}") + return print(io, "}") end Base.size(mesh::Mesh) = size(getfield(mesh, :simplices)) Base.getindex(mesh::Mesh, i::Integer) = getfield(mesh, :simplices)[i] -function Mesh(elements::AbstractVector{<: Polytope{Dim, T}}) where {Dim, T} - return Mesh{Dim, T, eltype(elements), typeof(elements)}(elements) +function Mesh(elements::AbstractVector{<:Polytope{Dim,T}}) where {Dim,T} + return Mesh{Dim,T,eltype(elements),typeof(elements)}(elements) end -function Mesh(points::AbstractVector{<: AbstractPoint}, faces::AbstractVector{<: AbstractFace}) +function Mesh(points::AbstractVector{<:AbstractPoint}, + faces::AbstractVector{<:AbstractFace}) return Mesh(connect(points, faces)) end -function Mesh( - points::AbstractVector{<: AbstractPoint}, faces::AbstractVector{<: Integer}, - facetype = TriangleFace, skip = 1 - ) +function Mesh(points::AbstractVector{<:AbstractPoint}, faces::AbstractVector{<:Integer}, + facetype=TriangleFace, skip=1) return Mesh(connect(points, connect(faces, facetype, skip))) end diff --git a/src/boundingboxes.jl b/src/boundingboxes.jl index 480f8457..fa19d296 100644 --- a/src/boundingboxes.jl +++ b/src/boundingboxes.jl @@ -1,46 +1,46 @@ -function Rect(geometry::AbstractArray{<: Point{N, T}}) where {N,T} +function Rect(geometry::AbstractArray{<:Point{N,T}}) where {N,T} return Rect{N,T}(geometry) end """ Construct a HyperRectangle enclosing all points. """ -function Rect{N1, T1}(geometry::AbstractArray{PT}) where {N1, T1, PT <: AbstractPoint} +function Rect{N1,T1}(geometry::AbstractArray{PT}) where {N1,T1,PT<:AbstractPoint} N2, T2 = length(PT), eltype(PT) @assert N1 >= N2 - vmin = Point{N2, T2}(typemax(T2)) - vmax = Point{N2, T2}(typemin(T2)) + vmin = Point{N2,T2}(typemax(T2)) + vmax = Point{N2,T2}(typemin(T2)) for p in geometry vmin, vmax = minmax(p, vmin, vmax) end o = vmin w = vmax - vmin - if N1 > N2 - z = zero(Vec{N1 - N2, T1}) - return Rect{N1, T1}(vcat(o, z), vcat(w, z)) + return if N1 > N2 + z = zero(Vec{N1 - N2,T1}) + Rect{N1,T1}(vcat(o, z), vcat(w, z)) else - return Rect{N1, T1}(o, w) - end + Rect{N1,T1}(o, w) + end end -function Rect(primitive::GeometryPrimitive{N, T}) where {N, T} - return Rect{N, T}(primitive) +function Rect(primitive::GeometryPrimitive{N,T}) where {N,T} + return Rect{N,T}(primitive) end -function Rect{T}(primitive::GeometryPrimitive{N, T}) where {N, T} - return Rect{N, T}(primitive) +function Rect{T}(primitive::GeometryPrimitive{N,T}) where {N,T} + return Rect{N,T}(primitive) end -function Rect{T}(a::Pyramid) where T - w,h = a.width / T(2), a.length +function Rect{T}(a::Pyramid) where {T} + w, h = a.width / T(2), a.length m = Vec{3,T}(a.middle) - return Rect{T}(m .- Vec{3,T}(w,w,0), m .+ Vec{3,T}(w, w, h)) + return Rect{T}(m .- Vec{3,T}(w, w, 0), m .+ Vec{3,T}(w, w, h)) end -function Rect{T}(a::Sphere) where T +function Rect{T}(a::Sphere) where {T} mini, maxi = extrema(a) return Rect{T}(mini, maxi .- mini) end -Rect{T}(a) where T = Rect{T}(coordinates(a)) -Rect{N, T}(a) where {N, T} = Rect{N, T}(coordinates(a)) +Rect{T}(a) where {T} = Rect{T}(coordinates(a)) +Rect{N,T}(a) where {N,T} = Rect{N,T}(coordinates(a)) diff --git a/src/fixed_arrays.jl b/src/fixed_arrays.jl index afe07ceb..a90d6c9e 100644 --- a/src/fixed_arrays.jl +++ b/src/fixed_arrays.jl @@ -1,129 +1,142 @@ -function unit(::Type{T}, i::Integer) where T <: StaticVector - T(ntuple(Val(length(T))) do j - ifelse(i == j, 1, 0) - end) +function unit(::Type{T}, i::Integer) where {T<:StaticVector} + tup = ntuple(Val(length(T))) do j + return ifelse(i == j, 1, 0) + end + return T(tup) end macro fixed_vector(name, parent) - esc(quote - - struct $(name){S, T} <: $(parent){S, T} - data::NTuple{S, T} - - function $(name){S, T}(x::NTuple{S,T}) where {S, T} - new{S, T}(x) - end - - function $(name){S, T}(x::NTuple{S,Any}) where {S, T} - new{S, T}(StaticArrays.convert_ntuple(T, x)) - end - end - - size_or(::Type{$(name)}, or) = or - eltype_or(::Type{$(name)}, or) = or - eltype_or(::Type{$(name){S, T} where S}, or) where {T} = T - eltype_or(::Type{$(name){S, T} where T}, or) where {S} = or - eltype_or(::Type{$(name){S, T}}, or) where {S, T} = T - - size_or(::Type{$(name){S, T} where S}, or) where {T} = or - size_or(::Type{$(name){S, T} where T}, or) where {S} = Size{(S,)}() - size_or(::Type{$(name){S, T}}, or) where {S, T} = (S,) - - # Array constructor - function $(name){S}(x::AbstractVector{T}) where {S, T} - @assert S <= length(x) - $(name){S, T}(ntuple(i-> x[i], Val(S))) - end - - function $(name){S, T1}(x::AbstractVector{T2}) where {S, T1, T2} - @assert S <= length(x) - $(name){S, T1}(ntuple(i-> T1(x[i]), Val(S))) - end - - function $(name){S, T}(x) where {S, T} - $(name){S, T}(ntuple(i-> T(x), Val(S))) - end - - - $(name){S}(x::T) where {S, T} = $(name){S, T}(ntuple(i-> x, Val(S))) - $(name){1, T}(x::T) where T = $(name){1, T}((x,)) - $(name)(x::NTuple{S}) where {S} = $(name){S}(x) - $(name)(x::T) where {S, T <: Tuple{Vararg{Any, S}}} = $(name){S, StaticArrays.promote_tuple_eltype(T)}(x) - - $(name){S}(x::T) where {S, T<:Tuple} = $(name){S, StaticArrays.promote_tuple_eltype(T)}(x) - $(name){S, T}(x::StaticVector) where {S, T} = $(name){S, T}(Tuple(x)) - - @generated function (::Type{$(name){S, T}})(x::$(name)) where {S, T} - idx = [:(x[$i]) for i = 1:S] - quote - $($(name)){S, T}($(idx...)) - end - end - - @generated function Base.convert(::Type{$(name){S, T}}, x::$(name)) where {S, T} - idx = [:(x[$i]) for i = 1:S] - quote - $($(name)){S, T}($(idx...)) - end - end - - @generated function (::Type{SV})(x::StaticVector) where SV <: $(name) - len = size_or(SV, size(x))[1] - if length(x) == len - :(SV(Tuple(x))) - elseif length(x) > len - elems = [:(x[$i]) for i = 1:len] - :(SV($(Expr(:tuple, elems...)))) - else - error("Static Vector too short: $x, target type: $SV") - end - end - - Base.@pure StaticArrays.Size(::Type{$(name){S, Any}}) where {S} = Size(S) - Base.@pure StaticArrays.Size(::Type{$(name){S, T}}) where {S,T} = Size(S) - - Base.@propagate_inbounds function Base.getindex(v::$(name){S, T}, i::Int) where {S, T} - v.data[i] - end - - Base.Tuple(v::$(name)) = v.data - Base.convert(::Type{$(name){S, T}}, x::NTuple{S, T}) where {S, T} = $(name){S, T}(x) - function Base.convert(::Type{$(name){S, T}}, x::Tuple) where {S, T} - return $(name){S, T}(convert(NTuple{S, T}, x)) - end - - @generated function StaticArrays.similar_type(::Type{SV}, ::Type{T}, s::Size{S}) where {SV <: $(name), T, S} - if length(S) === 1 - $(name){S[1], T} - else - StaticArrays.default_similar_type(T,s(),Val{length(S)}) - end - end - - Base.:(*)(a::$name, b::$name) = a .* b - Base.broadcasted(f, a::AbstractArray{T}, b::$name) where T <: $name = Base.broadcasted(f, a, (b,)) - end) + expr = quote + struct $(name){S,T} <: $(parent){S,T} + data::NTuple{S,T} + + function $(name){S,T}(x::NTuple{S,T}) where {S,T} + return new{S,T}(x) + end + + function $(name){S,T}(x::NTuple{S,Any}) where {S,T} + return new{S,T}(StaticArrays.convert_ntuple(T, x)) + end + end + + size_or(::Type{$(name)}, or) = or + eltype_or(::Type{$(name)}, or) = or + eltype_or(::Type{$(name){S,T} where S}, or) where {T} = T + eltype_or(::Type{$(name){S,T} where T}, or) where {S} = or + eltype_or(::Type{$(name){S,T}}, or) where {S,T} = T + + size_or(::Type{$(name){S,T} where S}, or) where {T} = or + size_or(::Type{$(name){S,T} where T}, or) where {S} = Size{(S,)}() + size_or(::Type{$(name){S,T}}, or) where {S,T} = (S,) + + # Array constructor + function $(name){S}(x::AbstractVector{T}) where {S,T} + @assert S <= length(x) + return $(name){S,T}(ntuple(i -> x[i], Val(S))) + end + + function $(name){S,T1}(x::AbstractVector{T2}) where {S,T1,T2} + @assert S <= length(x) + return $(name){S,T1}(ntuple(i -> T1(x[i]), Val(S))) + end + + function $(name){S,T}(x) where {S,T} + return $(name){S,T}(ntuple(i -> T(x), Val(S))) + end + + $(name){S}(x::T) where {S,T} = $(name){S,T}(ntuple(i -> x, Val(S))) + $(name){1,T}(x::T) where {T} = $(name){1,T}((x,)) + $(name)(x::NTuple{S}) where {S} = $(name){S}(x) + function $(name)(x::T) where {S,T<:Tuple{Vararg{Any,S}}} + return $(name){S,StaticArrays.promote_tuple_eltype(T)}(x) + end + + function $(name){S}(x::T) where {S,T<:Tuple} + return $(name){S,StaticArrays.promote_tuple_eltype(T)}(x) + end + $(name){S,T}(x::StaticVector) where {S,T} = $(name){S,T}(Tuple(x)) + + @generated function (::Type{$(name){S,T}})(x::$(name)) where {S,T} + idx = [:(x[$i]) for i in 1:S] + return quote + $($(name)){S,T}($(idx...)) + end + end + + @generated function Base.convert(::Type{$(name){S,T}}, + x::$(name)) where {S,T} + idx = [:(x[$i]) for i in 1:S] + return quote + $($(name)){S,T}($(idx...)) + end + end + + @generated function (::Type{SV})(x::StaticVector) where {SV<:$(name)} + len = size_or(SV, size(x))[1] + return if length(x) == len + :(SV(Tuple(x))) + elseif length(x) > len + elems = [:(x[$i]) for i in 1:len] + :(SV($(Expr(:tuple, elems...)))) + else + error("Static Vector too short: $x, target type: $SV") + end + end + + Base.@pure StaticArrays.Size(::Type{$(name){S,Any}}) where {S} = Size(S) + Base.@pure StaticArrays.Size(::Type{$(name){S,T}}) where {S,T} = Size(S) + + Base.@propagate_inbounds function Base.getindex(v::$(name){S,T}, + i::Int) where {S,T} + return v.data[i] + end + + Base.Tuple(v::$(name)) = v.data + function Base.convert(::Type{$(name){S,T}}, x::NTuple{S,T}) where {S,T} + return $(name){S,T}(x) + end + function Base.convert(::Type{$(name){S,T}}, x::Tuple) where {S,T} + return $(name){S,T}(convert(NTuple{S,T}, x)) + end + + @generated function StaticArrays.similar_type(::Type{SV}, ::Type{T}, + s::Size{S}) where {SV<:$(name), + T,S} + return if length(S) === 1 + $(name){S[1],T} + else + StaticArrays.default_similar_type(T, s(), Val{length(S)}) + end + end + + Base.:(*)(a::$name, b::$name) = a .* b + function Base.broadcasted(f, a::AbstractArray{T}, + b::$name) where {T<:$name} + return Base.broadcasted(f, a, (b,)) + end + end + return esc(expr) end -abstract type AbstractPoint{Dim, T} <: StaticVector{Dim, T} end +abstract type AbstractPoint{Dim,T} <: StaticVector{Dim,T} end @fixed_vector Point AbstractPoint @fixed_vector Vec StaticVector const Mat = SMatrix -const VecTypes{N, T} = Union{StaticVector{N, T}, NTuple{N, T}} -const Vecf0{N} = Vec{N, Float32} -const Pointf0{N} = Point{N, Float32} -Base.isnan(p::StaticVector) = any(x-> isnan(x), p) +const VecTypes{N,T} = Union{StaticVector{N,T},NTuple{N,T}} +const Vecf0{N} = Vec{N,Float32} +const Pointf0{N} = Point{N,Float32} +Base.isnan(p::StaticVector) = any(x -> isnan(x), p) #Create constes like Mat4f0, Point2, Point2f0 -for i=1:4 - for T=[:Point, :Vec] +for i in 1:4 + for T in [:Point, :Vec] name = Symbol("$T$i") namef0 = Symbol("$T$(i)f0") @eval begin const $name = $T{$i} - const $namef0 = $T{$i, Float32} + const $namef0 = $T{$i,Float32} export $name export $namef0 end @@ -131,7 +144,7 @@ for i=1:4 name = Symbol("Mat$i") namef0 = Symbol("Mat$(i)f0") @eval begin - const $name{T} = $Mat{$i,$i, T, $(i*i)} + const $name{T} = $Mat{$i,$i,T,$(i * i)} const $namef0 = $name{Float32} export $name export $namef0 diff --git a/src/geometry_primitives.jl b/src/geometry_primitives.jl index a023bda5..b4b1248f 100644 --- a/src/geometry_primitives.jl +++ b/src/geometry_primitives.jl @@ -8,25 +8,31 @@ end ## # conversion & decompose -convert_simplex(::Type{T}, x::T) where T = (x,) +convert_simplex(::Type{T}, x::T) where {T} = (x,) -function convert_simplex(NFT::Type{NgonFace{N, T1}}, f::Union{NgonFace{N, T2}}) where {T1, T2, N} +function convert_simplex(NFT::Type{NgonFace{N,T1}}, + f::Union{NgonFace{N,T2}}) where {T1,T2,N} return (convert(NFT, f),) end -convert_simplex(NFT::Type{NgonFace{3,T}}, f::NgonFace{3,T2}) where {T, T2} = (convert(NFT, f),) -convert_simplex(NFT::Type{NgonFace{2,T}}, f::NgonFace{2,T2}) where {T, T2} = (convert(NFT, f),) +function convert_simplex(NFT::Type{NgonFace{3,T}}, f::NgonFace{3,T2}) where {T,T2} + return (convert(NFT, f),) +end +function convert_simplex(NFT::Type{NgonFace{2,T}}, f::NgonFace{2,T2}) where {T,T2} + return (convert(NFT, f),) +end """ convert_simplex(::Type{Face{3}}, f::Face{N}) Triangulate an N-Face into a tuple of triangular faces. """ -@generated function convert_simplex(::Type{TriangleFace{T}}, f::Union{SimplexFace{N}, NgonFace{N}}) where {T, N} +@generated function convert_simplex(::Type{TriangleFace{T}}, + f::Union{SimplexFace{N},NgonFace{N}}) where {T,N} 3 <= N || error("decompose not implemented for N <= 3 yet. N: $N")# other wise degenerate v = Expr(:tuple) - for i = 3:N - push!(v.args, :(TriangleFace{T}(f[1], f[$(i-1)], f[$i]))) + for i in 3:N + push!(v.args, :(TriangleFace{T}(f[1], f[$(i - 1)], f[$i]))) end return v end @@ -36,37 +42,38 @@ end Extract all line segments in a Face. """ -@generated function convert_simplex(::Type{LineFace{T}}, f::Union{SimplexFace{N}, NgonFace{N}}) where {T, N} +@generated function convert_simplex(::Type{LineFace{T}}, + f::Union{SimplexFace{N},NgonFace{N}}) where {T,N} 2 <= N || error("decompose not implented for N <= 2 yet. N: $N")# other wise degenerate v = Expr(:tuple) - for i = 1:N-1 - push!(v.args, :(LineFace{$T}(f[$i], f[$(i+1)]))) + for i in 1:(N - 1) + push!(v.args, :(LineFace{$T}(f[$i], f[$(i + 1)]))) end # connect vertices N and 1 push!(v.args, :(LineFace{$T}(f[$N], f[1]))) return v end -to_pointn(::Type{T}, x) where T<:Point = convert_simplex(T, x)[1] +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} +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)),) + return (Point{N,T}(ntuple(i -> i <= N2 ? T(x[i]) : T(0), N)),) end -function convert_simplex(::Type{Vec{N, T}}, x) where {N, T} +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)),) + 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) +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 +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 ;) @@ -81,7 +88,6 @@ function collect_with_eltype(::Type{T}, iter) where T return result end - """ The unnormalized normal of three vertices. """ @@ -99,15 +105,14 @@ normals{VT,FD,FT,FO}(vertices::Vector{Point{3, VT}}, ``` Compute all vertex normals. """ -function normals(vertices::AbstractVector{<: AbstractPoint{3, T}}, - faces::AbstractVector{F}; - normaltype=Vec{3, T}) where {T, F <: NgonFace} +function normals(vertices::AbstractVector{<:AbstractPoint{3,T}}, faces::AbstractVector{F}; + normaltype=Vec{3,T}) where {T,F<:NgonFace} normals_result = zeros(normaltype, length(vertices)) # initilize with same type as verts but with 0 for face in faces v = metafree.(vertices[face]) # we can get away with two edges since faces are planar. n = orthogonal_vector(v[1], v[2], v[3]) - for i =1:length(F) + for i in 1:length(F) fi = face[i] normals_result[fi] = normals_result[fi] + n end diff --git a/src/interfaces.jl b/src/interfaces.jl index 16a5e662..a90bb7c6 100644 --- a/src/interfaces.jl +++ b/src/interfaces.jl @@ -53,13 +53,14 @@ For grid based tesselation, you can also use a tuple: rect = Rect2D(0, 0, 1, 1) Tesselation(rect, (5, 5)) """ -struct Tesselation{Dim, T, Primitive, NGrid} +struct Tesselation{Dim,T,Primitive,NGrid} primitive::Primitive - nvertices::NTuple{NGrid, Int} + nvertices::NTuple{NGrid,Int} end -function Tesselation(primitive::GeometryPrimitive{Dim, T}, nvertices::NTuple{N, <:Integer}) where {Dim, T, N} - return Tesselation{Dim, T, typeof(primitive), N}(primitive, Int.(nvertices)) +function Tesselation(primitive::GeometryPrimitive{Dim,T}, + nvertices::NTuple{N,<:Integer}) where {Dim,T,N} + return Tesselation{Dim,T,typeof(primitive),N}(primitive, Int.(nvertices)) end Tesselation(primitive, nvertices::Integer) = Tesselation(primitive, (nvertices,)) @@ -68,29 +69,34 @@ Tesselation(primitive, nvertices::Integer) = Tesselation(primitive, (nvertices,) # to directly work on Tesselation - but this way it's backward compatible and less # refactor work :D nvertices(tesselation::Tesselation) = tesselation.nvertices -nvertices(tesselation::Tesselation{T, N, P, 1}) where {T, N, P} = tesselation.nvertices[1] +nvertices(tesselation::Tesselation{T,N,P,1}) where {T,N,P} = tesselation.nvertices[1] -coordinates(tesselation::Tesselation) = coordinates(tesselation.primitive, nvertices(tesselation)) +function coordinates(tesselation::Tesselation) + return coordinates(tesselation.primitive, nvertices(tesselation)) +end faces(tesselation::Tesselation) = faces(tesselation.primitive, nvertices(tesselation)) normals(tesselation::Tesselation) = normals(tesselation.primitive, nvertices(tesselation)) -texturecoordinates(tesselation::Tesselation) = texturecoordinates(tesselation.primitive, nvertices(tesselation)) +function texturecoordinates(tesselation::Tesselation) + return texturecoordinates(tesselation.primitive, nvertices(tesselation)) +end ## Decompose methods # Dispatch type to make `decompose(UV{Vec2f0}, 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{Tesselation{Dim,T},Mesh{Dim,T},AbstractPolygon{Dim,T}, + GeometryPrimitive{Dim,T}, + AbstractVector{<:AbstractPoint{Dim,T}}} struct UV{T} end -UV(::Type{T}) where T = UV{T}() +UV(::Type{T}) where {T} = UV{T}() UV() = UV(Vec2f0) struct UVW{T} end -UVW(::Type{T}) where T = UVW{T}() +UVW(::Type{T}) where {T} = UVW{T}() UVW() = UVW(Vec3f0) struct Normal{T} end -Normal(::Type{T}) where T = Normal{T}() +Normal(::Type{T}) where {T} = Normal{T}() Normal() = Normal(Vec3f0) function decompose(::Type{F}, primitive) where {F<:AbstractFace} @@ -103,12 +109,12 @@ function decompose(::Type{P}, primitive) where {P<:AbstractPoint} return collect_with_eltype(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{Point}, primitive::Meshable{Dim,T}) where {Dim,T} + return collect_with_eltype(Point{Dim,T}, metafree(coordinates(primitive))) end -function decompose(::Type{Point}, primitive::LineString{Dim, T}) where {Dim, T} - return collect_with_eltype(Point{Dim, T}, metafree(coordinates(primitive))) +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} @@ -119,7 +125,7 @@ decompose_uv(primitive) = decompose(UV(), primitive) decompose_uvw(primitive) = decompose(UVW(), primitive) decompose_normals(primitive) = decompose(Normal(), primitive) -function decompose(NT::Normal{T}, primitive) where T +function decompose(NT::Normal{T}, primitive) where {T} n = normals(primitive) if n === nothing return collect_with_eltype(T, normals(coordinates(primitive), faces(primitive))) @@ -127,7 +133,7 @@ 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) where {T} # 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) @@ -142,9 +148,10 @@ function decompose(UVT::Union{UV{T}, UVW{T}}, primitive) where T return collect_with_eltype(T, uv) end -function decompose(UVT::Union{UV{T}, UVW{T}}, positions::AbstractVector{<:VecTypes}) where T +function decompose(UVT::Union{UV{T},UVW{T}}, + positions::AbstractVector{<:VecTypes}) where {T} N = length(T) - positions_nd = decompose(Point{N, eltype(T)}, positions) + positions_nd = decompose(Point{N,eltype(T)}, positions) bb = Rect(positions_nd) # Make sure we get this as points return map(positions_nd) do p return (p .- minimum(bb)) ./ widths(bb) @@ -153,4 +160,6 @@ end # Stay backward compatible: -decompose(::Type{T}, primitive::Meshable, nvertices) where T = decompose(T, Tesselation(primitive, nvertices)) +function decompose(::Type{T}, primitive::Meshable, nvertices) where {T} + return decompose(T, Tesselation(primitive, nvertices)) +end diff --git a/src/lines.jl b/src/lines.jl index c7ee72c7..260826c9 100644 --- a/src/lines.jl +++ b/src/lines.jl @@ -5,9 +5,10 @@ Intersection of 2 line segmens `a` and `b`. Returns intersection_found::Bool, intersection_point """ -function intersects(a::Line{2, T1}, b::Line{2, T2}) where {T1, T2} +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 = a + v3, v4 = b MT = Mat{2,2,T,4} p0 = zero(Point2{T}) @@ -36,20 +37,23 @@ function intersects(a::Line{2, T1}, b::Line{2, T2}) where {T1, T2} v4 = rotation * v4 end - a = det(MT(v1[1] - v2[1], v1[2] - v2[2], - v3[1] - v4[1], v3[2] - v4[2])) + a = det(MT(v1[1] - v2[1], v1[2] - v2[2], v3[1] - v4[1], v3[2] - v4[2])) (abs(a) < eps(T)) && return false, p0 # Lines are parallel d1 = det(MT(v1[1], v1[2], v2[1], v2[2])) d2 = det(MT(v3[1], v3[2], v4[1], v4[2])) - x = det(MT(d1, v1[1] - v2[1], d2, v3[1] - v4[1])) / a; - y = det(MT(d1, v1[2] - v2[2], d2, v3[2] - v4[2])) / a; - - (x < prevfloat(min(v1[1], v2[1])) || x > nextfloat(max(v1[1], v2[1]))) && return false, p0 - (y < prevfloat(min(v1[2], v2[2])) || y > nextfloat(max(v1[2], v2[2]))) && return false, p0 - (x < prevfloat(min(v3[1], v4[1])) || x > nextfloat(max(v3[1], v4[1]))) && return false, p0 - (y < prevfloat(min(v3[2], v4[2])) || y > nextfloat(max(v3[2], v4[2]))) && return false, p0 + x = det(MT(d1, v1[1] - v2[1], d2, v3[1] - v4[1])) / a + y = det(MT(d1, v1[2] - v2[2], d2, v3[2] - v4[2])) / a + + (x < prevfloat(min(v1[1], v2[1])) || x > nextfloat(max(v1[1], v2[1]))) && + return false, p0 + (y < prevfloat(min(v1[2], v2[2])) || y > nextfloat(max(v1[2], v2[2]))) && + return false, p0 + (x < prevfloat(min(v3[1], v4[1])) || x > nextfloat(max(v3[1], v4[1]))) && + return false, p0 + (y < prevfloat(min(v3[2], v4[2])) || y > nextfloat(max(v3[2], v4[2]))) && + return false, p0 point = Point2{T}(x, y) # don't forget to rotate the answer back @@ -60,7 +64,7 @@ function intersects(a::Line{2, T1}, b::Line{2, T2}) where {T1, T2} return true, point end -function simple_concat(vec::AbstractVector, range, endpoint::P) where P +function simple_concat(vec::AbstractVector, range, endpoint::P) where {P} result = Vector{P}(undef, length(range) + 1) for (i, j) in enumerate(range) result[i] = vec[mod1(j, length(vec))] @@ -71,7 +75,7 @@ end function consecutive_pairs(arr) n = length(arr) - zip(view(arr, 1:n-1), view(arr, 2:n)) + return zip(view(arr, 1:(n - 1)), view(arr, 2:n)) end """ @@ -85,11 +89,15 @@ function self_intersections(points::AbstractVector{<:AbstractPoint}) wraparound(i) = mod1(i, length(points) - 1) - for (i, (a,b)) in enumerate(consecutive_pairs(points)) + for (i, (a, b)) in enumerate(consecutive_pairs(points)) for (j, (a2, b2)) in enumerate(consecutive_pairs(points)) - is1, is2 = wraparound(i+1), wraparound(i-1) - if i != j && is1 != j && is2 != j && !(i in intersections) && !(j in intersections) - intersected, p = intersects(Line(a,b), Line(a2, b2)) + is1, is2 = wraparound(i + 1), wraparound(i - 1) + if i != j && + is1 != j && + is2 != j && + !(i in intersections) && + !(j in intersections) + intersected, p = intersects(Line(a, b), Line(a2, b2)) if intersected push!(intersections, i, j) push!(sections, p) @@ -108,14 +116,14 @@ is handled right now. """ function split_intersections(points::AbstractVector{<:AbstractPoint}) intersections, sections = self_intersections(points) - if isempty(intersections) + return if isempty(intersections) return [points] elseif length(intersections) == 2 && length(sections) == 1 a, b = intersections p = sections[1] - a,b = min(a,b), max(a,b) - poly1 = simple_concat(points, (a+1):(b-1), p) - poly2 = simple_concat(points, (b+1):(length(points)+a), p) + a, b = min(a, b), max(a, b) + poly1 = simple_concat(points, (a + 1):(b - 1), p) + poly2 = simple_concat(points, (b + 1):(length(points) + a), p) return [poly1, poly2] else error("More than 1 intersections can't be handled currently. Found: $intersections, $sections") diff --git a/src/meshes.jl b/src/meshes.jl index 8477f4bc..4a16dcdd 100644 --- a/src/meshes.jl +++ b/src/meshes.jl @@ -1,4 +1,4 @@ -const FaceMesh{Dim, T, Element} = Mesh{Dim, T, Element, <: FaceView{Element}} +const FaceMesh{Dim,T,Element} = Mesh{Dim,T,Element,<:FaceView{Element}} coordinates(mesh::FaceMesh) = coordinates(getfield(mesh, :simplices)) faces(mesh::FaceMesh) = faces(getfield(mesh, :simplices)) @@ -14,27 +14,29 @@ function normals(mesh::AbstractMesh) return nothing end -const GLTriangleElement = Triangle{3, Float32} +const GLTriangleElement = Triangle{3,Float32} const GLTriangleFace = TriangleFace{GLIndex} -const PointWithUV{Dim, T} = PointMeta{Dim, T, Point{Dim, T}, (:uv,), Tuple{Vec{2, T}}} -const PointWithNormal{Dim, T} = PointMeta{Dim, T, Point{Dim, T}, (:normals,), Tuple{Vec{3, T}}} -const PointWithUVNormal{Dim, T} = PointMeta{Dim, T, Point{Dim, T}, (:normals, :uv), Tuple{Vec{3, T}, Vec{2, T}}} -const PointWithUVWNormal{Dim, T} = PointMeta{Dim, T, Point{Dim, T}, (:normals, :uvw), Tuple{Vec{3, T}, Vec{3, T}}} +const PointWithUV{Dim,T} = PointMeta{Dim,T,Point{Dim,T},(:uv,),Tuple{Vec{2,T}}} +const PointWithNormal{Dim,T} = PointMeta{Dim,T,Point{Dim,T},(:normals,),Tuple{Vec{3,T}}} +const PointWithUVNormal{Dim,T} = PointMeta{Dim,T,Point{Dim,T},(:normals, :uv), + Tuple{Vec{3,T},Vec{2,T}}} +const PointWithUVWNormal{Dim,T} = PointMeta{Dim,T,Point{Dim,T},(:normals, :uvw), + Tuple{Vec{3,T},Vec{3,T}}} """ TriangleMesh{Dim, T, PointType} Abstract Mesh with triangle elements of eltype `T`. """ -const TriangleMesh{Dim, T, PointType} = AbstractMesh{TriangleP{Dim, T, PointType}} +const TriangleMesh{Dim,T,PointType} = AbstractMesh{TriangleP{Dim,T,PointType}} """ PlainMesh{Dim, T} Triangle mesh with no meta information (just points + triangle faces) """ -const PlainMesh{Dim, T} = TriangleMesh{Dim, T, Point{Dim, T}} -const GLPlainMesh{Dim} = PlainMesh{Dim, Float32} +const PlainMesh{Dim,T} = TriangleMesh{Dim,T,Point{Dim,T}} +const GLPlainMesh{Dim} = PlainMesh{Dim,Float32} const GLPlainMesh2D = GLPlainMesh{2} const GLPlainMesh3D = GLPlainMesh{3} @@ -44,8 +46,8 @@ const GLPlainMesh3D = GLPlainMesh{3} PlainMesh with texture coordinates meta at each point. `uvmesh.uv isa AbstractVector{Vec2f0}` """ -const UVMesh{Dim, T} = TriangleMesh{Dim, T, PointWithUV{Dim, T}} -const GLUVMesh{Dim} = UVMesh{Dim, Float32} +const UVMesh{Dim,T} = TriangleMesh{Dim,T,PointWithUV{Dim,T}} +const GLUVMesh{Dim} = UVMesh{Dim,Float32} const GLUVMesh2D = UVMesh{2} const GLUVMesh3D = UVMesh{3} @@ -55,8 +57,8 @@ const GLUVMesh3D = UVMesh{3} PlainMesh with normals meta at each point. `normalmesh.normals isa AbstractVector{Vec3f0}` """ -const NormalMesh{Dim, T} = TriangleMesh{Dim, T, PointWithNormal{Dim, T}} -const GLNormalMesh{Dim} = NormalMesh{Dim, Float32} +const NormalMesh{Dim,T} = TriangleMesh{Dim,T,PointWithNormal{Dim,T}} +const GLNormalMesh{Dim} = NormalMesh{Dim,Float32} const GLNormalMesh2D = GLNormalMesh{2} const GLNormalMesh3D = GLNormalMesh{3} @@ -67,8 +69,8 @@ PlainMesh with normals and uv meta at each point. `normalmesh.normals isa AbstractVector{Vec3f0}` `normalmesh.uv isa AbstractVector{Vec2f0}` """ -const NormalUVMesh{Dim, T} = TriangleMesh{Dim, T, PointWithUVNormal{Dim, T}} -const GLNormalUVMesh{Dim} = NormalUVMesh{Dim, Float32} +const NormalUVMesh{Dim,T} = TriangleMesh{Dim,T,PointWithUVNormal{Dim,T}} +const GLNormalUVMesh{Dim} = NormalUVMesh{Dim,Float32} const GLNormalUVMesh2D = GLNormalUVMesh{2} const GLNormalUVMesh3D = GLNormalUVMesh{3} @@ -79,8 +81,8 @@ PlainMesh with normals and uvw (texture coordinates in 3D) meta at each point. `normalmesh.normals isa AbstractVector{Vec3f0}` `normalmesh.uvw isa AbstractVector{Vec3f0}` """ -const NormalUVWMesh{Dim, T} = TriangleMesh{Dim, T, PointWithUVWNormal{Dim, T}} -const GLNormalUVWMesh{Dim} = NormalUVWMesh{Dim, Float32} +const NormalUVWMesh{Dim,T} = TriangleMesh{Dim,T,PointWithUVWNormal{Dim,T}} +const GLNormalUVWMesh{Dim} = NormalUVWMesh{Dim,Float32} const GLNormalUVWMesh2D = GLNormalUVWMesh{2} const GLNormalUVWMesh3D = GLNormalUVWMesh{3} @@ -96,9 +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; pointtype=Point, facetype=GLTriangleFace, uv=nothing, + normaltype=nothing) positions = decompose(pointtype, primitive) faces = decompose(facetype, primitive) @@ -143,11 +144,12 @@ Polygon triangluation! function mesh(polygon::AbstractVector{P}; pointtype=P, facetype=GLTriangleFace, normaltype=nothing) where {P<:AbstractPoint{2}} - return mesh(Polygon(polygon); pointtype=pointtype, facetype=facetype, normaltype=normaltype) + return mesh(Polygon(polygon); pointtype=pointtype, facetype=facetype, + normaltype=normaltype) end -function mesh(polygon::AbstractPolygon{Dim, T}; pointtype=Point{Dim, T}, facetype=GLTriangleFace, - normaltype=nothing) where {Dim, T} +function mesh(polygon::AbstractPolygon{Dim,T}; pointtype=Point{Dim,T}, + facetype=GLTriangleFace, normaltype=nothing) where {Dim,T} faces = decompose(facetype, polygon) positions = decompose(pointtype, polygon) @@ -164,17 +166,15 @@ function triangle_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}, facetype=GLTriangleFace) + return mesh(primitive; pointtype=Point{N,Float32}, facetype=GLTriangleFace) end - -function uv_mesh(primitive::Meshable{N, T}) where {N, T} - return mesh(primitive; pointtype=Point{N, Float32}, uv=Vec2f0, - facetype=GLTriangleFace) +function uv_mesh(primitive::Meshable{N,T}) where {N,T} + return mesh(primitive; pointtype=Point{N,Float32}, uv=Vec2f0, facetype=GLTriangleFace) end function uv_normal_mesh(primitive::Meshable{N}) where {N} - return mesh(primitive; pointtype=Point{N, Float32}, uv=Vec2f0, normaltype=Vec3f0, + return mesh(primitive; pointtype=Point{N,Float32}, uv=Vec2f0, normaltype=Vec3f0, facetype=GLTriangleFace) end @@ -190,7 +190,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=Vec3f0, + return mesh(primitive; pointtype=Point{N,Float32}, normaltype=Vec3f0, facetype=GLTriangleFace) end @@ -203,7 +203,7 @@ surface is right. function volume(triangle::Triangle) where {VT,FT} v1, v2, v3 = triangle sig = sign(orthogonal_vector(v1, v2, v3) ⋅ v1) - return sig * abs(v1 ⋅ ( v2 × v3 )) / 6 + return sig * abs(v1 ⋅ (v2 × v3)) / 6 end """ @@ -212,12 +212,12 @@ end Calculate the signed volume of all tetrahedra. Be sure the orientation of your surface is right. """ -function volume(mesh::Mesh) where {VT, FT} +function volume(mesh::Mesh) where {VT,FT} return sum(volume, mesh) end -function Base.merge(meshes::AbstractVector{<: Mesh}) - if isempty(meshes) +function Base.merge(meshes::AbstractVector{<:Mesh}) + return if isempty(meshes) error("No meshes to merge") elseif length(meshes) == 1 return meshes[1] @@ -226,7 +226,7 @@ function Base.merge(meshes::AbstractVector{<: Mesh}) ps = copy(coordinates(m1)) fs = copy(faces(m1)) for mesh in Iterators.drop(meshes, 1) - append!(fs, map(f-> f .+ length(ps), faces(mesh))) + append!(fs, map(f -> f .+ length(ps), faces(mesh))) append!(ps, coordinates(mesh)) end return Mesh(ps, fs) @@ -243,7 +243,7 @@ function pointmeta(mesh::Mesh; meta_data...) attr = attributes(points) delete!(attr, :position) # position == metafree(points) # delete overlapping attributes so we can replace with `meta_data` - foreach(k-> delete!(attr, k), keys(meta_data)) + foreach(k -> delete!(attr, k), keys(meta_data)) return Mesh(meta(metafree(points); attr..., meta_data...), faces(mesh)) end @@ -279,5 +279,6 @@ function facemeta(mesh::Mesh; meta_data...) end function attributes(hasmeta::Mesh) - return Dict{Symbol, Any}((name => getproperty(hasmeta, name) for name in propertynames(hasmeta))) + return Dict{Symbol,Any}((name => getproperty(hasmeta, name) + for name in propertynames(hasmeta))) end diff --git a/src/metadata.jl b/src/metadata.jl index 4badeb16..ea90bc46 100644 --- a/src/metadata.jl +++ b/src/metadata.jl @@ -11,11 +11,12 @@ Needs to be overloaded, and returns empty dict for non overloaded types! Gets overloaded by default for all Meta types. """ function attributes(hasmeta) - return Dict{Symbol, Any}() + return Dict{Symbol,Any}() end function attributes(hasmeta::StructArray) - return Dict{Symbol, Any}((name => getproperty(hasmeta, name) for name in propertynames(hasmeta))) + return Dict{Symbol,Any}((name => getproperty(hasmeta, name) + for name in propertynames(hasmeta))) end """ @@ -39,7 +40,7 @@ E.g: MetaType(Point) == PointMeta ``` """ -MetaType(::Type{T}) where T = error("No Meta Type for $T") +MetaType(::Type{T}) where {T} = error("No Meta Type for $T") """ MetaFree(::Type{T}) @@ -50,16 +51,16 @@ E.g: MetaFree(PointMeta) == Point ``` """ -MetaFree(::Type{T}) where T = error("No meta free Type for $T") +MetaFree(::Type{T}) where {T} = error("No meta free Type for $T") """ meta(x::MetaObject) Returns the metadata of `x` """ -meta(x::T) where T = error("$T has no meta!") +meta(x::T) where {T} = error("$T has no meta!") -metafree(x::T) where T = x +metafree(x::T) where {T} = x macro meta_type(name, mainfield, supertype, params...) MetaName = Symbol("$(name)Meta") @@ -69,48 +70,53 @@ macro meta_type(name, mainfield, supertype, params...) params_sym = map(params) do param param isa Symbol && return param param isa Expr && param.head == :(<:) && return param.args[1] - error("Unsupported type parameter: $(param)") + return error("Unsupported type parameter: $(param)") end expr = quote - struct $MetaName{$(params...), Typ <: $supertype{$(params_sym...)}, Names, Types} <: $supertype{$(params_sym...)} + struct $MetaName{$(params...),Typ<:$supertype{$(params_sym...)},Names,Types} <: + $supertype{$(params_sym...)} main::Typ - meta::NamedTuple{Names, Types} + meta::NamedTuple{Names,Types} end - const $NoParams{Typ, Names, Types} = $MetaName{$(params_sym...), Typ, Names, Types} where {$(params_sym...)} + const $NoParams{Typ,Names,Types} = $MetaName{$(params_sym...),Typ,Names, + Types} where {$(params_sym...)} - function Base.getproperty(x::$MetaName{$(params_sym...), Typ, Names, Types}, - field::Symbol) where {$(params...), Typ, Names, Types} + function Base.getproperty(x::$MetaName{$(params_sym...),Typ,Names,Types}, + field::Symbol) where {$(params...),Typ,Names,Types} field === $field && return getfield(x, :main) field === :main && return getfield(x, :main) Base.sym_in(field, Names) && return getfield(getfield(x, :meta), field) - error("Field $field not part of Element") + return error("Field $field not part of Element") end - function GeometryBasics.MetaType(XX::Type{<: $supertype{$(params_sym...)} where {$(params...)}}) + function GeometryBasics.MetaType(XX::Type{<:$supertype{$(params_sym...)} where {$(params...)}}) return $MetaName end - function GeometryBasics.MetaType( - ST::Type{<: $supertype{$(params_sym...)}}, - ::Type{NamedTuple{Names, Types}}) where {$(params...), Names, Types} - return $MetaName{$(params_sym...), ST, Names, Types} + function GeometryBasics.MetaType(ST::Type{<:$supertype{$(params_sym...)}}, + ::Type{NamedTuple{Names,Types}}) where {$(params...), + Names, + Types} + return $MetaName{$(params_sym...),ST,Names,Types} end - GeometryBasics.MetaFree(::Type{<: $MetaName{Typ}}) where Typ = Typ - GeometryBasics.MetaFree(::Type{<: $MetaName}) = $name + GeometryBasics.MetaFree(::Type{<:$MetaName{Typ}}) where {Typ} = Typ + GeometryBasics.MetaFree(::Type{<:$MetaName}) = $name GeometryBasics.metafree(x::$MetaName) = getfield(x, :main) - GeometryBasics.metafree(x::AbstractVector{<: $MetaName}) = getproperty(x, $field) + GeometryBasics.metafree(x::AbstractVector{<:$MetaName}) = getproperty(x, $field) GeometryBasics.meta(x::$MetaName) = getfield(x, :meta) - GeometryBasics.meta(x::AbstractVector{<: $MetaName}) = getproperty(x, :meta) + GeometryBasics.meta(x::AbstractVector{<:$MetaName}) = getproperty(x, :meta) - function GeometryBasics.meta(main::$supertype{$(params_sym...)}; meta...) where {$(params...)} + function GeometryBasics.meta(main::$supertype{$(params_sym...)}; + meta...) where {$(params...)} isempty(meta) && return elements # no meta to add! return $MetaName(main; meta...) end - function GeometryBasics.meta(elements::AbstractVector{XX}; meta...) where XX <: $supertype{$(params_sym...)} where {$(params...)} + function GeometryBasics.meta(elements::AbstractVector{XX}; + meta...) where {XX<:$supertype{$(params_sym...)}} where {$(params...)} isempty(meta) && return elements # no meta to add! n = length(elements) for (k, v) in meta @@ -126,37 +132,42 @@ macro meta_type(name, mainfield, supertype, params...) # get the first element to get the per element named tuple type ElementNT = typeof(map(first, nt)) - return StructArray{MetaType(XX, ElementNT)}(($(mainfield) = elements, nt...)) + return StructArray{MetaType(XX, ElementNT)}(($(mainfield)=elements, nt...)) end function GeometryBasics.attributes(hasmeta::$MetaName) - return Dict{Symbol, Any}((name => getproperty(hasmeta, name) for name in propertynames(hasmeta))) + return Dict{Symbol,Any}((name => getproperty(hasmeta, name) + for name in propertynames(hasmeta))) end - function (MT::Type{<: $MetaName})(args...; meta...) + function (MT::Type{<:$MetaName})(args...; meta...) nt = values(meta) obj = MetaFree(MT)(args...) return MT(obj, nt) end - function (MT::Type{<: $MetaName})(main::$(name); meta...) + function (MT::Type{<:$MetaName})(main::$(name); meta...) nt = values(meta) return MT(main, nt) end - function Base.propertynames(::$MetaName{$(params_sym...), Typ, Names, Types}) where {$(params...), Typ, Names, Types} + function Base.propertynames(::$MetaName{$(params_sym...),Typ,Names,Types}) where {$(params...), + Typ, + Names, + Types} return ($field, Names...) end - function StructArrays.staticschema(::Type{$MetaName{$(params_sym...), Typ, Names, Types}}) where {$(params...), Typ, Names, Types} - NamedTuple{($field, Names...), Base.tuple_type_cons(Typ, Types)} + function StructArrays.staticschema(::Type{$MetaName{$(params_sym...),Typ,Names, + Types}}) where {$(params...), + Typ,Names,Types} + return NamedTuple{($field, Names...),Base.tuple_type_cons(Typ, Types)} end - function StructArrays.createinstance( - ::Type{$MetaName{$(params_sym...), Typ, Names, Types}}, - metafree, args... - ) where {$(params...), Typ, Names, Types} - $MetaName(metafree, NamedTuple{Names, Types}(args)) + function StructArrays.createinstance(::Type{$MetaName{$(params_sym...),Typ,Names, + Types}}, metafree, + args...) where {$(params...),Typ,Names,Types} + return $MetaName(metafree, NamedTuple{Names,Types}(args)) end end return esc(expr) @@ -193,7 +204,6 @@ Base.size(x::MultiPolygonMeta) = size(metafree(x)) Base.getindex(x::MeshMeta, idx::Int) = getindex(metafree(x), idx) Base.size(x::MeshMeta) = size(metafree(x)) - """ MetaT(geometry, meta::NamedTuple) @@ -214,9 +224,9 @@ julia> MetaT(Point(1, 2), city = "Mumbai") MetaT{Point{2,Int64},(:city,),Tuple{String}}([1, 2], (city = "Mumbai",)) ``` """ -struct MetaT{T, Names, Types} +struct MetaT{T,Names,Types} main::T - meta::NamedTuple{Names, Types} + meta::NamedTuple{Names,Types} end MetaT(x; kwargs...) = MetaT(x, values(kwargs)) @@ -230,9 +240,9 @@ Free the MetaT from metadata i.e. returns the geometry/array of geometries """ function metafree(x::MetaT) - getfield(x, :main) + return getfield(x, :main) end -metafree(x::AbstractVector{<: MetaT}) = map(metafree, x) +metafree(x::AbstractVector{<:MetaT}) = map(metafree, x) """ @@ -242,13 +252,13 @@ metafree(x::AbstractVector{<: MetaT}) = map(metafree, x) Returns the metadata of a `MetaT` """ function meta(x::MetaT) - getfield(x, :meta) + return getfield(x, :meta) end -meta(x::AbstractVector{<: MetaT}) = map(meta, x) +meta(x::AbstractVector{<:MetaT}) = map(meta, x) # helper methods function Base.getproperty(x::MetaT, field::Symbol) - if field == :main + return if field == :main metafree(x) elseif field == :meta meta(x) @@ -258,18 +268,18 @@ function Base.getproperty(x::MetaT, field::Symbol) end Base.propertynames(x::MetaT) = (:main, propertynames(meta(x))...) -getnamestypes(::Type{MetaT{T, Names, Types}}) where {T, Names, Types} = (T, Names, Types) +getnamestypes(::Type{MetaT{T,Names,Types}}) where {T,Names,Types} = (T, Names, Types) # explicitly give the "schema" of the object to StructArrays -function StructArrays.staticschema(::Type{F}) where {F<:MetaT} +function StructArrays.staticschema(::Type{F}) where {F<:MetaT} T, names, types = getnamestypes(F) - NamedTuple{(:main, names...), Base.tuple_type_cons(T, types)} + return NamedTuple{(:main, names...),Base.tuple_type_cons(T, types)} end # generate an instance of MetaT type -function StructArrays.createinstance(::Type{F}, x, args...) where {F<:MetaT} - T , names, types = getnamestypes(F) - MetaT(x, NamedTuple{names, types}(args)) +function StructArrays.createinstance(::Type{F}, x, args...) where {F<:MetaT} + T, names, types = getnamestypes(F) + return MetaT(x, NamedTuple{names,types}(args)) end """ @@ -277,14 +287,13 @@ Puts an iterable of MetaT's into a StructArray """ function meta_table(iter) cols = Tables.columntable(iter) - meta_table(first(cols), Base.tail(cols)) + return meta_table(first(cols), Base.tail(cols)) end -function meta_table(main, meta::NamedTuple{names, types}) where {names, types} - F = MetaT{eltype(main), names, StructArrays.eltypes(types)} +function meta_table(main, meta::NamedTuple{names,types}) where {names,types} + F = MetaT{eltype(main),names,StructArrays.eltypes(types)} return StructArray{F}(; main=main, meta...) end Base.getindex(x::MetaT, idx::Int) = getindex(metafree(x), idx) Base.size(x::MetaT) = size(metafree(x)) - diff --git a/src/offsetintegers.jl b/src/offsetintegers.jl index f61f3e0d..63d3decf 100644 --- a/src/offsetintegers.jl +++ b/src/offsetintegers.jl @@ -6,39 +6,44 @@ OffsetInteger type mainly for indexing. * `O` - The offset relative to Julia arrays. This helps reduce copying when communicating with 0-indexed systems such as OpenGL. """ -struct OffsetInteger{O, T <: Integer} <: Integer +struct OffsetInteger{O,T<:Integer} <: Integer i::T - OffsetInteger{O, T}(x::Integer) where {O, T <: Integer} = new{O, T}(T(x + O)) + OffsetInteger{O,T}(x::Integer) where {O,T<:Integer} = new{O,T}(T(x + O)) end -const ZeroIndex{T <: Integer} = OffsetInteger{-1, T} -const OneIndex{T <: Integer} = OffsetInteger{0, T} +const ZeroIndex{T<:Integer} = OffsetInteger{-1,T} +const OneIndex{T<:Integer} = OffsetInteger{0,T} const GLIndex = ZeroIndex{Cuint} raw(x::OffsetInteger) = x.i raw(x::Integer) = x -value(x::OffsetInteger{O, T}) where {O, T} = raw(x) - O +value(x::OffsetInteger{O,T}) where {O,T} = raw(x) - O value(x::Integer) = x -function show(io::IO, oi::OffsetInteger{O, T}) where {O, T} - print(io, "|$(raw(oi)) (indexes as $(value(oi))|") +function show(io::IO, oi::OffsetInteger{O,T}) where {O,T} + return print(io, "|$(raw(oi)) (indexes as $(value(oi))|") end -Base.eltype(::Type{OffsetInteger{O, T}}) where {O, T} = T +Base.eltype(::Type{OffsetInteger{O,T}}) where {O,T} = T Base.eltype(oi::OffsetInteger) = eltype(typeof(oi)) # constructors and conversion -OffsetInteger{O1, T1}(x::OffsetInteger{O2, T2}) where {O1, O2, T1 <: Integer, T2 <: Integer} = OffsetInteger{O1, T1}(T2(x)) +function OffsetInteger{O1,T1}(x::OffsetInteger{O2,T2}) where {O1,O2,T1<:Integer,T2<:Integer} + return OffsetInteger{O1,T1}(T2(x)) +end -OffsetInteger{O}(x::Integer) where {O} = OffsetInteger{O, eltype(x)}(x) -OffsetInteger{O}(x::OffsetInteger) where {O} = OffsetInteger{O, eltype(x)}(x) +OffsetInteger{O}(x::Integer) where {O} = OffsetInteger{O,eltype(x)}(x) +OffsetInteger{O}(x::OffsetInteger) where {O} = OffsetInteger{O,eltype(x)}(x) # This constructor has a massive method invalidation as a consequence, # and doesn't seem to be needed, so let's remove it! -(::Type{IT})(x::OffsetInteger) where {IT <: Integer} = IT(value(x)) +(::Type{IT})(x::OffsetInteger) where {IT<:Integer} = IT(value(x)) Base.@pure pure_max(x1, x2) = x1 > x2 ? x1 : x2 -Base.promote_rule(::Type{T1}, ::Type{OffsetInteger{O, T2}}) where {T1 <: Integer, O, T2} = T1 -Base.promote_rule(::Type{OffsetInteger{O1, T1}}, ::Type{OffsetInteger{O2, T2}}) where {O1, O2, T1, T2} = OffsetInteger{pure_max(O1, O2), promote_type(T1, T2)} +Base.promote_rule(::Type{T1}, ::Type{OffsetInteger{O,T2}}) where {T1<:Integer,O,T2} = T1 +function Base.promote_rule(::Type{OffsetInteger{O1,T1}}, + ::Type{OffsetInteger{O2,T2}}) where {O1,O2,T1,T2} + return OffsetInteger{pure_max(O1, O2),promote_type(T1, T2)} +end #Need to convert to Int here because of: https://github.com/JuliaLang/julia/issues/35038 Base.to_index(I::OffsetInteger) = convert(Int, raw(OneIndex(I))) @@ -46,21 +51,21 @@ Base.to_index(I::OffsetInteger{0}) = convert(Int, raw(I)) # basic operators for op in (:(-), :abs) - @eval Base.$op(x::T) where {T <: OffsetInteger} = T($(op)(value(x))) + @eval Base.$op(x::T) where {T<:OffsetInteger} = T($(op)(value(x))) end for op in (:(+), :(-), :(*), :(/), :div) @eval begin - @inline function Base.$op(x::OffsetInteger{O}, y::OffsetInteger{O}) where O - OffsetInteger{O}($op(value(x), value(y))) + @inline function Base.$op(x::OffsetInteger{O}, y::OffsetInteger{O}) where {O} + return OffsetInteger{O}($op(value(x), value(y))) end end end -for op in (:(==), :(>=), :(<=), :(<) , :(>), :sub_with_overflow) +for op in (:(==), :(>=), :(<=), :(<), :(>), :sub_with_overflow) @eval begin - @inline function Base.$op(x::OffsetInteger{O}, y::OffsetInteger{O}) where O - $op(x.i, y.i) + @inline function Base.$op(x::OffsetInteger{O}, y::OffsetInteger{O}) where {O} + return $op(x.i, y.i) end end end diff --git a/src/primitives/cylinders.jl b/src/primitives/cylinders.jl index 713bd023..9804cf53 100644 --- a/src/primitives/cylinders.jl +++ b/src/primitives/cylinders.jl @@ -4,7 +4,7 @@ A `Cylinder` is a 2D rectangle or a 3D cylinder defined by its origin point, its extremity and a radius. `origin`, `extremity` and `r`, must be specified. """ -struct Cylinder{N, T} <: GeometryPrimitive{N, T} +struct Cylinder{N,T} <: GeometryPrimitive{N,T} origin::Point{N,T} extremity::Point{N,T} r::T @@ -17,36 +17,39 @@ end A `Cylinder2` or `Cylinder3` is a 2D/3D cylinder defined by its origin point, its extremity and a radius. `origin`, `extremity` and `r`, must be specified. """ -const Cylinder2{T} = Cylinder{2, T} -const Cylinder3{T} = Cylinder{3, T} +const Cylinder2{T} = Cylinder{2,T} +const Cylinder3{T} = Cylinder{3,T} -origin(c::Cylinder{N, T}) where {N, T} = c.origin -extremity(c::Cylinder{N, T}) where {N, T} = c.extremity -radius(c::Cylinder{N, T}) where {N, T} = c.r -height(c::Cylinder{N, T}) where {N, T} = norm(c.extremity - c.origin) -direction(c::Cylinder{N, T}) where {N, T} = (c.extremity .- c.origin) ./ height(c) +origin(c::Cylinder{N,T}) where {N,T} = c.origin +extremity(c::Cylinder{N,T}) where {N,T} = c.extremity +radius(c::Cylinder{N,T}) where {N,T} = c.r +height(c::Cylinder{N,T}) where {N,T} = norm(c.extremity - c.origin) +direction(c::Cylinder{N,T}) where {N,T} = (c.extremity .- c.origin) ./ height(c) -function rotation(c::Cylinder{2, T}) where T - d2 = direction(c); u = @SVector [d2[1], d2[2], T(0)] +function rotation(c::Cylinder{2,T}) where {T} + d2 = direction(c) + u = @SVector [d2[1], d2[2], T(0)] v = @MVector [u[2], -u[1], T(0)] normalize!(v) return hcat(v, u, @SVector T[0, 0, 1]) end -function rotation(c::Cylinder{3, T}) where T - d3 = direction(c); u = @SVector [d3[1], d3[2], d3[3]] +function rotation(c::Cylinder{3,T}) where {T} + d3 = direction(c) + u = @SVector [d3[1], d3[2], d3[3]] if abs(u[1]) > 0 || abs(u[2]) > 0 v = @MVector [u[2], -u[1], T(0)] else v = @MVector [T(0), -u[3], u[2]] end normalize!(v) - w = @SVector [u[2] * v[3] - u[3] * v[2], -u[1] * v[3] + u[3] * v[1], u[1] * v[2] - u[2] * v[1]] + w = @SVector [u[2] * v[3] - u[3] * v[2], -u[1] * v[3] + u[3] * v[1], + u[1] * v[2] - u[2] * v[1]] return hcat(v, w, u) end -function coordinates(c::Cylinder{2, T}, nvertices=(2, 2)) where T - r = Rect(c.origin[1] - c.r/2, c.origin[2], c.r, height(c)) +function coordinates(c::Cylinder{2,T}, nvertices=(2, 2)) where {T} + r = Rect(c.origin[1] - c.r / 2, c.origin[2], c.r, height(c)) M = rotation(c) points = coordinates(r, nvertices) vo = to_pointn(Point3{T}, origin(c)) @@ -57,25 +60,25 @@ function faces(sphere::Cylinder{2}, nvertices=(2, 2)) return faces(Rect(0, 0, 1, 1), nvertices) end -function coordinates(c::Cylinder{3, T}, nvertices=30) where T +function coordinates(c::Cylinder{3,T}, nvertices=30) where {T} if isodd(nvertices) nvertices = 2 * (nvertices ÷ 2) end - nvertices = max(8, nvertices); + nvertices = max(8, nvertices) nbv = nvertices ÷ 2 M = rotation(c) h = height(c) range = 1:(2 * nbv + 2) function inner(i) - if i == length(range) - return c.extremity + return if i == length(range) + c.extremity elseif i == length(range) - 1 - return origin(c) + origin(c) else phi = T((2π * (((i + 1) ÷ 2) - 1)) / nbv) up = ifelse(isodd(i), 0, h) - return (M * Point(c.r * cos(phi), c.r * sin(phi), up)) .+ c.origin + (M * Point(c.r * cos(phi), c.r * sin(phi), up)) .+ c.origin end end @@ -84,19 +87,21 @@ end function faces(c::Cylinder{3}, facets=30) isodd(facets) ? facets = 2 * div(facets, 2) : nothing - facets < 8 ? facets = 8 : nothing; nbv = Int(facets / 2) + facets < 8 ? facets = 8 : nothing + nbv = Int(facets / 2) indexes = Vector{TriangleFace{Int}}(undef, facets) index = 1 - for j = 1:(nbv-1) + for j in 1:(nbv - 1) indexes[index] = (index + 2, index + 1, index) - indexes[index + 1] = ( index + 3, index + 1, index + 2) + indexes[index + 1] = (index + 3, index + 1, index + 2) index += 2 end indexes[index] = (1, index + 1, index) indexes[index + 1] = (2, index + 1, 1) - for i = 1:length(indexes) - i%2 == 1 ? push!(indexes, (indexes[i][1], indexes[i][3], 2*nbv+1)) : push!(indexes,(indexes[i][2], indexes[i][1], 2*nbv+2)) + for i in 1:length(indexes) + i % 2 == 1 ? push!(indexes, (indexes[i][1], indexes[i][3], 2 * nbv + 1)) : + push!(indexes, (indexes[i][2], indexes[i][1], 2 * nbv + 2)) end return indexes -end \ No newline at end of file +end diff --git a/src/primitives/particles.jl b/src/primitives/particles.jl index 1606d3c2..e3d80357 100644 --- a/src/primitives/particles.jl +++ b/src/primitives/particles.jl @@ -1,4 +1,4 @@ -struct Particle{N, T} <: GeometryPrimitive{N, T} - position::Point{N, T} - velocity::Vec{N, T} -end \ No newline at end of file +struct Particle{N,T} <: GeometryPrimitive{N,T} + position::Point{N,T} + velocity::Vec{N,T} +end diff --git a/src/primitives/pyramids.jl b/src/primitives/pyramids.jl index 4207cc1f..531d22d0 100644 --- a/src/primitives/pyramids.jl +++ b/src/primitives/pyramids.jl @@ -1,27 +1,21 @@ -struct Pyramid{T} <: GeometryPrimitive{3, T} - middle::Point{3, T} +struct Pyramid{T} <: GeometryPrimitive{3,T} + middle::Point{3,T} length::T - width ::T + width::T end function coordinates(p::Pyramid{T}, nvertices=nothing) where {T} - leftup = Point{3, T}(-p.width , p.width, 0) / 2 + leftup = Point{3,T}(-p.width, p.width, 0) / 2 leftdown = Point(-p.width, -p.width, 0) / 2 - tip = Point{3, T}(p.middle + Point{3, T}(0, 0, p.length)) - lu = Point{3, T}(p.middle + leftup) - ld = Point{3, T}(p.middle + leftdown) - ru = Point{3, T}(p.middle - leftdown) - rd = Point{3, T}(p.middle - leftup) - return Point{3, T}[ - tip, rd, ru, - tip, ru, lu, - tip, lu, ld, - tip, ld, rd, - rd, ru, lu, - lu, ld, rd - ] + tip = Point{3,T}(p.middle + Point{3,T}(0, 0, p.length)) + lu = Point{3,T}(p.middle + leftup) + ld = Point{3,T}(p.middle + leftdown) + ru = Point{3,T}(p.middle - leftdown) + rd = Point{3,T}(p.middle - leftup) + return Point{3,T}[tip, rd, ru, tip, ru, lu, tip, lu, ld, tip, ld, rd, rd, ru, lu, lu, + ld, rd] end -function faces(r::Pyramid, nvertices=nothing) where FT +function faces(r::Pyramid, nvertices=nothing) where {FT} return (TriangleFace(triangle) for triangle in TupleView{3}(1:18)) end diff --git a/src/primitives/rectangles.jl b/src/primitives/rectangles.jl index 753be8b3..0232dfcc 100644 --- a/src/primitives/rectangles.jl +++ b/src/primitives/rectangles.jl @@ -6,62 +6,61 @@ A `HyperRectangle` is a generalization of a rectangle into N-dimensions. Formally it is the cartesian product of intervals, which is represented by the `origin` and `width` fields, whose indices correspond to each of the `N` axes. """ -struct HyperRectangle{N, T} <: GeometryPrimitive{N, T} - origin::Vec{N, T} - widths::Vec{N, T} +struct HyperRectangle{N,T} <: GeometryPrimitive{N,T} + origin::Vec{N,T} + widths::Vec{N,T} end ## # Constructors & typealiases -const Rect{N, T} = HyperRectangle{N, T} -const Rect2D{T} = Rect{2, T} -const Rect3D{T} = Rect{3, T} -const TRect{T} = Rect{N, T} where N +const Rect{N,T} = HyperRectangle{N,T} +const Rect2D{T} = Rect{2,T} +const Rect3D{T} = Rect{3,T} +const TRect{T} = Rect{N,T} where {N} -const FRect{N} = Rect{N, Float32} +const FRect{N} = Rect{N,Float32} const FRect2D = Rect2D{Float32} const FRect3D = Rect3D{Float32} -const IRect{N} = HyperRectangle{N, Int} +const IRect{N} = HyperRectangle{N,Int} const IRect2D = Rect2D{Int} const IRect3D = Rect3D{Int} -Rect() = Rect{2, Float32}() +Rect() = Rect{2,Float32}() -TRect{T}() where {T} = Rect{2, T}() +TRect{T}() where {T} = Rect{2,T}() -Rect{N}() where {N} = Rect{N, Float32}() +Rect{N}() where {N} = Rect{N,Float32}() -function Rect{N, T}() where {T,N} +function Rect{N,T}() where {T,N} # empty constructor such that update will always include the first point - return Rect{N, T}(Vec{N,T}(typemax(T)), Vec{N,T}(typemin(T))) + return Rect{N,T}(Vec{N,T}(typemax(T)), Vec{N,T}(typemin(T))) end # conversion from other Rects -function Rect{N,T1}(a::Rect{N, T2}) where {N,T1,T2} - return Rect(Vec{N, T1}(minimum(a)), Vec{N, T1}(widths(a))) +function Rect{N,T1}(a::Rect{N,T2}) where {N,T1,T2} + return Rect(Vec{N,T1}(minimum(a)), Vec{N,T1}(widths(a))) end -function Rect(v1::Vec{N, T1}, v2::Vec{N, T2}) where {N,T1,T2} +function Rect(v1::Vec{N,T1}, v2::Vec{N,T2}) where {N,T1,T2} T = promote_type(T1, T2) - return Rect{N,T}(Vec{N, T}(v1), Vec{N, T}(v2)) + return Rect{N,T}(Vec{N,T}(v1), Vec{N,T}(v2)) end -function TRect{T}(v1::VecTypes{N, T1}, v2::VecTypes{N, T2}) where {N,T,T1,T2} - if T <: Integer - Rect{N, T}(round.(T, v1), round.(T, v2)) +function TRect{T}(v1::VecTypes{N,T1}, v2::VecTypes{N,T2}) where {N,T,T1,T2} + return if T <: Integer + Rect{N,T}(round.(T, v1), round.(T, v2)) else - return Rect{N, T}(Vec{N, T}(v1), Vec{N, T}(v2)) + return Rect{N,T}(Vec{N,T}(v1), Vec{N,T}(v2)) end end -function Rect{N}(v1::VecTypes{N, T1}, v2::VecTypes{N, T2}) where {N,T1,T2} +function Rect{N}(v1::VecTypes{N,T1}, v2::VecTypes{N,T2}) where {N,T1,T2} T = promote_type(T1, T2) - return Rect{N,T}(Vec{N, T}(v1), Vec{N, T}(v2)) + return Rect{N,T}(Vec{N,T}(v1), Vec{N,T}(v2)) end - """ Rect(vals::Number...) @@ -76,42 +75,42 @@ width == Vec(1,2) # Generated so we get goodish codegen on each signature n = length(vals) @assert iseven(n) - mid = div(n,2) + mid = div(n, 2) v1 = Expr(:call, :Vec) v2 = Expr(:call, :Vec) # TODO this can be inbounds - append!(v1.args, [:(vals[$i]) for i = 1:mid]) - append!(v2.args, [:(vals[$i]) for i = mid+1:length(vals)]) + append!(v1.args, [:(vals[$i]) for i in 1:mid]) + append!(v2.args, [:(vals[$i]) for i in (mid + 1):length(vals)]) return Expr(:call, :Rect, v1, v2) end -Rect3D(a::Vararg{Number, 6}) = Rect3D(Vec{3}(a[1], a[2], a[3]), Vec{3}(a[4], a[5], a[6])) -Rect3D(args::Vararg{Number, 4}) = Rect3D(Rect{2}(args...)) +Rect3D(a::Vararg{Number,6}) = Rect3D(Vec{3}(a[1], a[2], a[3]), Vec{3}(a[4], a[5], a[6])) +Rect3D(args::Vararg{Number,4}) = Rect3D(Rect{2}(args...)) #= From different args =# -function (Rect)(args::Vararg{Number, 4}) +function (Rect)(args::Vararg{Number,4}) args_prom = promote(args...) return Rect2D{typeof(args_prom[1])}(args_prom...) end -function (Rect2D)(args::Vararg{Number, 4}) +function (Rect2D)(args::Vararg{Number,4}) args_prom = promote(args...) return Rect2D{typeof(args_prom[1])}(args_prom...) end -function (Rect{2, T})(args::Vararg{Number, 4}) where {T} +function (Rect{2,T})(args::Vararg{Number,4}) where {T} x, y, w, h = T <: Integer ? round.(T, args) : args - return Rect2D{T}(Vec{2, T}(x, y), Vec{2, T}(w, h)) + return Rect2D{T}(Vec{2,T}(x, y), Vec{2,T}(w, h)) end -function TRect{T}(args::Vararg{Number, 4}) where {T} +function TRect{T}(args::Vararg{Number,4}) where {T} x, y, w, h = T <: Integer ? round.(T, args) : args - return Rect2D{T}(Vec{2, T}(x, y), Vec{2, T}(w, h)) + return Rect2D{T}(Vec{2,T}(x, y), Vec{2,T}(w, h)) end -function FRect3D(x::Rect2D{T}) where T - return Rect{3, T}(Vec{3, T}(minimum(x)..., 0), Vec{3, T}(widths(x)..., 0.0)) +function FRect3D(x::Rect2D{T}) where {T} + return Rect{3,T}(Vec{3,T}(minimum(x)..., 0), Vec{3,T}(widths(x)..., 0.0)) end function Rect2D{T}(a::Rect2D) where {T} @@ -122,8 +121,8 @@ function TRect{T}(a::Rect2D) where {T} return Rect2D{T}(minimum(a), widths(a)) end -function Rect{N, T}(a::GeometryPrimitive) where {N, T} - return Rect{N, T}(Vec{N, T}(minimum(a)), Vec{N, T}(widths(a))) +function Rect{N,T}(a::GeometryPrimitive) where {N,T} + return Rect{N,T}(Vec{N,T}(minimum(a)), Vec{N,T}(widths(a))) end function Rect2D(xy::VecTypes{2}, w::Number, h::Number) @@ -142,24 +141,24 @@ function TRect{T}(x::Number, y::Number, wh::VecTypes{2}) where {T} return Rect2D{T}(x, y, wh...) end - # TODO These are kinda silly function Rect2D(xy::NamedTuple{(:x, :y)}, wh::NamedTuple{(:width, :height)}) return Rect2D(xy.x, xy.y, wh.width, wh.height) end -function FRect3D(x::Tuple{Tuple{<: Number, <: Number}, Tuple{<: Number, <: Number}}) - FRect3D(Vec3f0(x[1]..., 0), Vec3f0(x[2]..., 0)) +function FRect3D(x::Tuple{Tuple{<:Number,<:Number},Tuple{<:Number,<:Number}}) + return FRect3D(Vec3f0(x[1]..., 0), Vec3f0(x[2]..., 0)) end -function FRect3D(x::Tuple{Tuple{<: Number, <: Number, <: Number}, Tuple{<: Number, <: Number, <: Number}}) - FRect3D(Vec3f0(x[1]...), Vec3f0(x[2]...)) +function FRect3D(x::Tuple{Tuple{<:Number,<:Number,<:Number}, + Tuple{<:Number,<:Number,<:Number}}) + return FRect3D(Vec3f0(x[1]...), Vec3f0(x[2]...)) end origin(prim::Rect) = prim.origin Base.maximum(prim::Rect) = origin(prim) + widths(prim) Base.minimum(prim::Rect) = origin(prim) -Base.length(prim::Rect{N, T}) where {T, N} = N +Base.length(prim::Rect{N,T}) where {T,N} = N widths(prim::Rect) = prim.widths width(prim::Rect) = prim.widths[1] @@ -172,14 +171,13 @@ Splits an Rect into two along an axis at a given location. """ split(b::Rect, axis, value::Integer) = _split(b, axis, value) split(b::Rect, axis, value::Number) = _split(b, axis, value) -function _split(b::H, axis, value) where H<:Rect +function _split(b::H, axis, value) where {H<:Rect} bmin = minimum(b) bmax = maximum(b) b1max = setindex(bmax, value, axis) b2min = setindex(bmin, value, axis) - return H(bmin, b1max-bmin), - H(b2min, bmax-b2min) + return H(bmin, b1max - bmin), H(b2min, bmax - b2min) end ### @@ -191,7 +189,7 @@ end Transform a `Rect` using a matrix. Maintains axis-align properties so a significantly larger Rect may be generated. """ -function Base.:(*)(m::Mat{N1, N1, T1}, h::Rect{N2, T2}) where {N1,N2,T1,T2} +function Base.:(*)(m::Mat{N1,N1,T1}, h::Rect{N2,T2}) where {N1,N2,T1,T2} # TypeVar constants T = promote_type(T1, T2) @@ -200,31 +198,31 @@ function Base.:(*)(m::Mat{N1, N1, T1}, h::Rect{N2, T2}) where {N1,N2,T1,T2} # get all points on the Rect d = decompose(Point, h) # make sure our points are sized for the tranform - pts = (Vec{N1, T}[vcat(pt, ones(Vec{D, T})) for pt in d]...,)::NTuple{2^N2,Vec{N1,T}} + pts = (Vec{N1,T}[vcat(pt, ones(Vec{D,T})) for pt in d]...,)::NTuple{2^N2,Vec{N1,T}} - vmin = Vec{N1, T}(typemax(T)) - vmax = Vec{N1, T}(typemin(T)) + vmin = Vec{N1,T}(typemax(T)) + vmax = Vec{N1,T}(typemin(T)) # tranform all points, tracking min and max points for pt in pts pn = m * pt vmin = min.(pn, vmin) vmax = max.(pn, vmax) end - return Rect{N2, T}(vmin, vmax - vmin) + return Rect{N2,T}(vmin, vmax - vmin) end # equal dimension case function Base.:(*)(m::Mat{N,N,T1}, h::Rect{N,T2}) where {N,T1,T2} # TypeVar constants - T = promote_type(T1,T2) + T = promote_type(T1, T2) # get all points on the Rect pts = decompose(Point, h) # make sure our points are sized for the tranform - vmin = Vec{N, T}(typemax(T)) - vmax = Vec{N, T}(typemin(T)) + vmin = Vec{N,T}(typemax(T)) + vmax = Vec{N,T}(typemin(T)) # tranform all points, tracking min and max points for pt in pts @@ -232,70 +230,62 @@ function Base.:(*)(m::Mat{N,N,T1}, h::Rect{N,T2}) where {N,T1,T2} vmin = min.(pn, vmin) vmax = max.(pn, vmax) end - Rect{N,T}(vmin, vmax-vmin) + return Rect{N,T}(vmin, vmax - vmin) end # fast path. TODO make other versions fast without code duplications like now -function Base.:(*)(m::Mat{4,4,T}, h::Rect{3,T}) where T +function Base.:(*)(m::Mat{4,4,T}, h::Rect{3,T}) where {T} # equal dimension case # get all points on the Rect - pts = ( - Vec{4,T}(0.0,0.0,0.0, 1.0), - Vec{4,T}(1.0,0.0,0.0, 1.0), - Vec{4,T}(0.0,1.0,0.0, 1.0), - Vec{4,T}(1.0,1.0,0.0, 1.0), - Vec{4,T}(0.0,0.0,1.0, 1.0), - Vec{4,T}(1.0,0.0,1.0, 1.0), - Vec{4,T}(0.0,1.0,1.0, 1.0), - Vec{4,T}(1.0,1.0,1.0, 1.0) - ) + pts = (Vec{4,T}(0.0, 0.0, 0.0, 1.0), Vec{4,T}(1.0, 0.0, 0.0, 1.0), + Vec{4,T}(0.0, 1.0, 0.0, 1.0), Vec{4,T}(1.0, 1.0, 0.0, 1.0), + Vec{4,T}(0.0, 0.0, 1.0, 1.0), Vec{4,T}(1.0, 0.0, 1.0, 1.0), + Vec{4,T}(0.0, 1.0, 1.0, 1.0), Vec{4,T}(1.0, 1.0, 1.0, 1.0)) # make sure our points are sized for the tranform - vmin = Vec{4, T}(typemax(T)) - vmax = Vec{4, T}(typemin(T)) + vmin = Vec{4,T}(typemax(T)) + vmax = Vec{4,T}(typemin(T)) o, w = origin(h), widths(h) - _o = Vec{4, T}(o[1], o[2], o[3], T(0)) - _w = Vec{4, T}(w[1], w[2], w[3], T(1)) + _o = Vec{4,T}(o[1], o[2], o[3], T(0)) + _w = Vec{4,T}(w[1], w[2], w[3], T(1)) # tranform all points, tracking min and max points for pt in pts pn = m * (_o + (pt .* _w)) vmin = min.(pn, vmin) vmax = max.(pn, vmax) end - _vmin = Vec{3, T}(vmin[1], vmin[2], vmin[3]) - _vmax = Vec{3, T}(vmax[1], vmax[2], vmax[3]) - Rect{3,T}(_vmin, _vmax - _vmin) + _vmin = Vec{3,T}(vmin[1], vmin[2], vmin[3]) + _vmax = Vec{3,T}(vmax[1], vmax[2], vmax[3]) + return Rect{3,T}(_vmin, _vmax - _vmin) end -Base.:(-)(h::Rect{N, T}, move::Number) where {N,T} = h - Vec{N, T}(move) -Base.:(+)(h::Rect{N, T}, move::Number) where {N,T} = h + Vec{N, T}(move) +Base.:(-)(h::Rect{N,T}, move::Number) where {N,T} = h - Vec{N,T}(move) +Base.:(+)(h::Rect{N,T}, move::Number) where {N,T} = h + Vec{N,T}(move) - -function Base.:(-)(h::Rect{N, T}, move::StaticVector{N}) where {N,T} - Rect{N, T}(minimum(h) .- move, widths(h)) +function Base.:(-)(h::Rect{N,T}, move::StaticVector{N}) where {N,T} + return Rect{N,T}(minimum(h) .- move, widths(h)) end -function Base.:(+)(h::Rect{N, T}, move::StaticVector{N}) where {N,T} - Rect{N, T}(minimum(h) .+ move, widths(h)) +function Base.:(+)(h::Rect{N,T}, move::StaticVector{N}) where {N,T} + return Rect{N,T}(minimum(h) .+ move, widths(h)) end - -function Base.:(*)(rect::Rect, scaling::Union{Number, StaticVector}) +function Base.:(*)(rect::Rect, scaling::Union{Number,StaticVector}) return Rect(minimum(rect) .* scaling, widths(rect) .* scaling) end # Enables rectangular indexing into a matrix -function Base.to_indices(A::AbstractMatrix{T}, I::Tuple{Rect2D{IT}}) where {T, IT<:Integer} +function Base.to_indices(A::AbstractMatrix{T}, I::Tuple{Rect2D{IT}}) where {T,IT<:Integer} rect = I[1] mini = minimum(rect) wh = widths(rect) - return (mini[1] + 1 : (mini[1] + wh[1]), mini[2] + 1 : (mini[2] + wh[2])) + return ((mini[1] + 1):(mini[1] + wh[1]), (mini[2] + 1):(mini[2] + wh[2])) end function minmax(p::StaticVector, vmin, vmax) isnan(p) && return (vmin, vmax) - min.(p, vmin), max.(p, vmax) + return min.(p, vmin), max.(p, vmax) end # Annoying special case for view(Vector{Point}, Vector{Face}) @@ -305,14 +295,14 @@ function minmax(tup::Tuple, vmin, vmax) vmin = min.(p, vmin) vmax = max.(p, vmax) end - vmin, vmax + return vmin, vmax end -function positive_widths(rect::Rect{N, T}) where {N, T} +function positive_widths(rect::Rect{N,T}) where {N,T} mini, maxi = minimum(rect), maximum(rect) realmin = min.(mini, maxi) realmax = max.(mini, maxi) - Rect{N, T}(realmin, realmax .- realmin) + return Rect{N,T}(realmin, realmax .- realmin) end ### @@ -321,10 +311,10 @@ end """ Perform a union between two Rects. """ -function Base.union(h1::Rect{N}, h2::Rect{N}) where N +function Base.union(h1::Rect{N}, h2::Rect{N}) where {N} m = min.(minimum(h1), minimum(h2)) mm = max.(maximum(h1), maximum(h2)) - Rect{N}(m, mm - m) + return Rect{N}(m, mm - m) end """ @@ -339,119 +329,114 @@ diff(h1::Rect, h2::Rect) = h1 Perform a intersection between two Rects. """ -function intersect(h1::Rect{N}, h2::Rect{N}) where N +function intersect(h1::Rect{N}, h2::Rect{N}) where {N} m = max.(minimum(h1), minimum(h2)) mm = min.(maximum(h1), maximum(h2)) - Rect{N}(m, mm - m) + return Rect{N}(m, mm - m) end - -function update(b::Rect{N, T}, v::Vec{N, T2}) where {N, T, T2} - update(b, Vec{N, T}(v)) +function update(b::Rect{N,T}, v::Vec{N,T2}) where {N,T,T2} + return update(b, Vec{N,T}(v)) end -function update(b::Rect{N, T}, v::Vec{N, T}) where {N, T} +function update(b::Rect{N,T}, v::Vec{N,T}) where {N,T} m = min.(minimum(b), v) maxi = maximum(b) mm = if isnan(maxi) - v-m + v - m else max.(v, maxi) - m end - Rect{N, T}(m, mm) + return Rect{N,T}(m, mm) end # Min maximum distance functions between hrectangle and point for a given dimension -function min_dist_dim(rect::Rect{N, T}, p::Vec{N, T}, dim::Int) where {N, T} +function min_dist_dim(rect::Rect{N,T}, p::Vec{N,T}, dim::Int) where {N,T} return max(zero(T), max(minimum(rect)[dim] - p[dim], p[dim] - maximum(rect)[dim])) end -function max_dist_dim(rect::Rect{N, T}, p::Vec{N, T}, dim::Int) where {N, T} +function max_dist_dim(rect::Rect{N,T}, p::Vec{N,T}, dim::Int) where {N,T} return max(maximum(rect)[dim] - p[dim], p[dim] - minimum(rect)[dim]) end -function min_dist_dim(rect1::Rect{N, T}, - rect2::Rect{N, T}, - dim::Int) where {N, T} - return max(zero(T), max(minimum(rect1)[dim] - maximum(rect2)[dim], - minimum(rect2)[dim] - maximum(rect1)[dim])) +function min_dist_dim(rect1::Rect{N,T}, rect2::Rect{N,T}, dim::Int) where {N,T} + return max(zero(T), + max(minimum(rect1)[dim] - maximum(rect2)[dim], + minimum(rect2)[dim] - maximum(rect1)[dim])) end -function max_dist_dim(rect1::Rect{N, T}, - rect2::Rect{N, T}, - dim::Int) where {N, T} +function max_dist_dim(rect1::Rect{N,T}, rect2::Rect{N,T}, dim::Int) where {N,T} return max(maximum(rect1)[dim] - minimum(rect2)[dim], maximum(rect2)[dim] - minimum(rect1)[dim]) end # Total minimum maximum distance functions -function min_euclideansq(rect::Rect{N, T}, - p::Union{Vec{N, T}, - Rect{N, T}}) where {N, T} +function min_euclideansq(rect::Rect{N,T}, p::Union{Vec{N,T},Rect{N,T}}) where {N,T} minimum_dist = T(0.0) for dim in 1:length(p) d = min_dist_dim(rect, p, dim) - minimum_dist += d*d + minimum_dist += d * d end return minimum_dist end -function max_euclideansq(rect::Rect{N, T}, p::Union{Vec{N, T}, Rect{N, T}}) where {N, T} +function max_euclideansq(rect::Rect{N,T}, p::Union{Vec{N,T},Rect{N,T}}) where {N,T} maximum_dist = T(0.0) for dim in 1:length(p) d = max_dist_dim(rect, p, dim) - maximum_dist += d*d + maximum_dist += d * d end return maximum_dist end -function min_euclidean(rect::Rect{N, T}, p::Union{Vec{N, T}, Rect{N, T}}) where {N, T} +function min_euclidean(rect::Rect{N,T}, p::Union{Vec{N,T},Rect{N,T}}) where {N,T} return sqrt(min_euclideansq(rect, p)) end -function max_euclidean(rect::Rect{N, T}, p::Union{Vec{N, T}, Rect{N, T}}) where {N, T} +function max_euclidean(rect::Rect{N,T}, p::Union{Vec{N,T},Rect{N,T}}) where {N,T} return sqrt(max_euclideansq(rect, p)) end # Functions that return both minimum and maximum for convenience -function minmax_dist_dim(rect::Rect{N, T}, p::Union{Vec{N, T}, Rect{N, T}}, dim::Int) where {N, T} +function minmax_dist_dim(rect::Rect{N,T}, p::Union{Vec{N,T},Rect{N,T}}, + dim::Int) where {N,T} minimum_d = min_dist_dim(rect, p, dim) maximum_d = max_dist_dim(rect, p, dim) return minimum_d, maximum_d end -function minmax_euclideansq(rect::Rect{N, T}, p::Union{Vec{N, T}, Rect{N, T}}) where {N, T} +function minmax_euclideansq(rect::Rect{N,T}, p::Union{Vec{N,T},Rect{N,T}}) where {N,T} minimum_dist = min_euclideansq(rect, p) maximum_dist = max_euclideansq(rect, p) return minimum_dist, maximum_dist end -function minmax_euclidean(rect::Rect{N, T}, p::Union{Vec{N, T}, Rect{N, T}}) where {N, T} +function minmax_euclidean(rect::Rect{N,T}, p::Union{Vec{N,T},Rect{N,T}}) where {N,T} minimumsq, maximumsq = minmax_euclideansq(rect, p) return sqrt(minimumsq), sqrt(maximumsq) end # http://en.wikipedia.org/wiki/Allen%27s_interval_algebra -function before(b1::Rect{N}, b2::Rect{N}) where N - for i = 1:N +function before(b1::Rect{N}, b2::Rect{N}) where {N} + for i in 1:N maximum(b1)[i] < minimum(b2)[i] || return false end - true + return true end -meets(b1::Rect{N}, b2::Rect{N}) where N = maximum(b1) == minimum(b2) +meets(b1::Rect{N}, b2::Rect{N}) where {N} = maximum(b1) == minimum(b2) -function overlaps(b1::Rect{N}, b2::Rect{N}) where N - for i = 1:N +function overlaps(b1::Rect{N}, b2::Rect{N}) where {N} + for i in 1:N maximum(b2)[i] > maximum(b1)[i] > minimum(b2)[i] && - minimum(b1)[i] < minimum(b2)[i] || return false + minimum(b1)[i] < minimum(b2)[i] || return false end - true + return true end -function starts(b1::Rect{N}, b2::Rect{N}) where N - if minimum(b1) == minimum(b2) - for i = 1:N +function starts(b1::Rect{N}, b2::Rect{N}) where {N} + return if minimum(b1) == minimum(b2) + for i in 1:N maximum(b1)[i] < maximum(b2)[i] || return false end return true @@ -460,17 +445,16 @@ function starts(b1::Rect{N}, b2::Rect{N}) where N end end -function during(b1::Rect{N}, b2::Rect{N}) where N - for i = 1:N - maximum(b1)[i] < maximum(b2)[i] && minimum(b1)[i] > minimum(b2)[i] || - return false +function during(b1::Rect{N}, b2::Rect{N}) where {N} + for i in 1:N + maximum(b1)[i] < maximum(b2)[i] && minimum(b1)[i] > minimum(b2)[i] || return false end - true + return true end -function finishes(b1::Rect{N}, b2::Rect{N}) where N - if maximum(b1) == maximum(b2) - for i = 1:N +function finishes(b1::Rect{N}, b2::Rect{N}) where {N} + return if maximum(b1) == maximum(b2) + for i in 1:N minimum(b1)[i] > minimum(b2)[i] || return false end return true @@ -489,11 +473,9 @@ Check if Rect `b1` is contained in `b2`. This does not use strict inequality, so Rects may share faces and this will still return true. """ -function Base.in(b1::Rect{N}, b2::Rect{N}) where N - for i = 1:N - maximum(b1)[i] <= maximum(b2)[i] && - minimum(b1)[i] >= minimum(b2)[i] || - return false +function Base.in(b1::Rect{N}, b2::Rect{N}) where {N} + for i in 1:N + maximum(b1)[i] <= maximum(b2)[i] && minimum(b1)[i] >= minimum(b2)[i] || return false end return true end @@ -504,8 +486,8 @@ end 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::VecTypes, b1::Rect{N, T}) where {T, N} - for i = 1:N +function Base.in(pt::VecTypes, b1::Rect{N,T}) where {T,N} + for i in 1:N pt[i] <= maximum(b1)[i] && pt[i] >= minimum(b1)[i] || return false end return true @@ -518,7 +500,7 @@ Base.:(==)(b1::Rect, b2::Rect) = minimum(b1) == minimum(b2) && widths(b1) == wid Base.isequal(b1::Rect, b2::Rect) = b1 == b2 -centered(R::Type{Rect{N,T}}) where {N, T} = R(Vec{N,T}(-0.5), Vec{N,T}(1)) +centered(R::Type{Rect{N,T}}) where {N,T} = R(Vec{N,T}(-0.5), Vec{N,T}(1)) centered(R::Type{Rect{N}}) where {N} = R(Vec{N,Float32}(-0.5), Vec{N,Float32}(1)) centered(R::Type{Rect}) where {N} = R(Vec{2,Float32}(-0.5), Vec{2,Float32}(1)) @@ -528,23 +510,23 @@ centered(R::Type{Rect}) where {N} = R(Vec{2,Float32}(-0.5), Vec{2,Float32}(1)) function faces(rect::Rect2D, nvertices=(2, 2)) w, h = nvertices idx = LinearIndices(nvertices) - quad(i, j) = QuadFace{Int}(idx[i, j], idx[i+1, j], idx[i+1, j+1], idx[i, j+1]) - return ivec((quad(i, j) for i=1:(w-1), j=1:(h-1))) + quad(i, j) = QuadFace{Int}(idx[i, j], idx[i + 1, j], idx[i + 1, j + 1], idx[i, j + 1]) + return ivec((quad(i, j) for i in 1:(w - 1), j in 1:(h - 1))) end -function coordinates(rect::Rect2D, nvertices=(2,2)) +function coordinates(rect::Rect2D, nvertices=(2, 2)) mini, maxi = extrema(rect) xrange, yrange = LinRange.(mini, maxi, nvertices) - return ivec(((x,y) for x in xrange, y in yrange)) + return ivec(((x, y) for x in xrange, y in yrange)) end -function texturecoordinates(rect::Rect2D, nvertices=(2,2)) +function texturecoordinates(rect::Rect2D, nvertices=(2, 2)) xrange, yrange = LinRange.((0, 1), (1, 0), nvertices) - return ivec(((x,y) for x in xrange, y in yrange)) + return ivec(((x, y) for x in xrange, y in yrange)) end -function normals(rect::Rect2D, nvertices=(2,2)) - return Iterators.repeated((0,0,1), prod(nvertices)) +function normals(rect::Rect2D, nvertices=(2, 2)) + return Iterators.repeated((0, 0, 1), prod(nvertices)) end ## @@ -553,30 +535,20 @@ function coordinates(rect::Rect3D) # TODO use n w = widths(rect) o = origin(rect) - return ((x .* w .+ o) for x in Point{3, Int}[ - (0, 0, 0), (0, 0, 1), (0, 1, 1), (0, 1, 0), - (0, 0, 0), (1, 0, 0), (1, 0, 1), (0, 0, 1), - (0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0), - - (1, 1, 1), (0, 1, 1), (0, 0, 1), (1, 0, 1), - (1, 1, 1), (1, 0, 1), (1, 0, 0), (1, 1, 0), - (1, 1, 1), (1, 1, 0), (0, 1, 0), (0, 1, 1) - ]) + points = Point{3,Int}[(0, 0, 0), (0, 0, 1), (0, 1, 1), (0, 1, 0), (0, 0, 0), (1, 0, 0), + (1, 0, 1), (0, 0, 1), (0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0), + (1, 1, 1), (0, 1, 1), (0, 0, 1), (1, 0, 1), (1, 1, 1), (1, 0, 1), + (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 1, 0), (0, 1, 0), (0, 1, 1)] + return ((x .* w .+ o) for x in points) end function texturecoordinates(rect::Rect3D) - return coordinates(Rect3D(0,0,0,1,1,1)) + return coordinates(Rect3D(0, 0, 0, 1, 1, 1)) end function faces(rect::Rect3D) - return QuadFace{Int}[ - (1,2,3,4), - (5,6,7,8), - (9,10,11,12), - (13,14,15,16), - (17,18,19,20), - (21,22,23,24), - ] + return QuadFace{Int}[(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12), (13, 14, 15, 16), + (17, 18, 19, 20), (21, 22, 23, 24),] end """ @@ -584,8 +556,8 @@ end A rectangle in 3D space. """ -struct Quad{T} <: GeometryPrimitive{3, T} - downleft::Vec{3, T} - width ::Vec{3, T} - height ::Vec{3, T} +struct Quad{T} <: GeometryPrimitive{3,T} + downleft::Vec{3,T} + width::Vec{3,T} + height::Vec{3,T} end diff --git a/src/primitives/spheres.jl b/src/primitives/spheres.jl index 1ef58f24..7e3df377 100644 --- a/src/primitives/spheres.jl +++ b/src/primitives/spheres.jl @@ -4,8 +4,8 @@ A `HyperSphere` is a generalization of a sphere into N-dimensions. A `center` and radius, `r`, must be specified. """ -struct HyperSphere{N, T} <: GeometryPrimitive{N, T} - center::Point{N, T} +struct HyperSphere{N,T} <: GeometryPrimitive{N,T} + center::Point{N,T} r::T end """ @@ -13,23 +13,23 @@ end An alias for a HyperSphere of dimension 2. (i.e. `HyperSphere{2, T}`) """ -const Circle{T} = HyperSphere{2, T} +const Circle{T} = HyperSphere{2,T} """ Sphere{T} An alias for a HyperSphere of dimension 3. (i.e. `HyperSphere{3, T}`) """ -const Sphere{T} = 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)) +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) +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)) +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(x::AbstractPoint{2}, c::Circle) @inbounds ox, oy = origin(c) @@ -38,12 +38,14 @@ function Base.in(x::AbstractPoint{2}, c::Circle) return xD <= c.r && yD <= c.r end -centered(S::Type{HyperSphere{N, T}}) where {N, T} = S(Vec{N,T}(0), T(0.5)) -centered(::Type{T}) where {T <: HyperSphere} = centered(HyperSphere{ndims_or(T, 3), eltype_or(T, Float32)}) +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) + inner(fi) = Point(rad * sin(fi + pi), rad * cos(fi + pi)) .+ origin(s) return (inner(fi) for fi in LinRange(0, 2pi, nvertices)) end @@ -52,8 +54,9 @@ function texturecoordinates(s::Circle, nvertices=64) end function coordinates(s::Sphere, nvertices=24) - θ = LinRange(0, pi, nvertices); φ = LinRange(0, 2pi, nvertices) - inner(θ, φ) = Point(cos(φ)*sin(θ), sin(φ)*sin(θ), cos(θ)) .* s.r .+ s.center + θ = LinRange(0, pi, nvertices) + φ = LinRange(0, 2pi, nvertices) + inner(θ, φ) = Point(cos(φ) * sin(θ), sin(φ) * sin(θ), cos(θ)) .* s.r .+ s.center return ivec((inner(θ, φ) for θ in θ, φ in φ)) end @@ -67,5 +70,5 @@ function faces(sphere::Sphere, nvertices=24) end function normals(s::Sphere{T}, nvertices=24) where {T} - return coordinates(Sphere(Point{3, T}(0), 1), nvertices) + return coordinates(Sphere(Point{3,T}(0), 1), nvertices) end diff --git a/src/triangulation.jl b/src/triangulation.jl index 51efa8b2..6aced8f7 100644 --- a/src/triangulation.jl +++ b/src/triangulation.jl @@ -17,7 +17,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI Calculate the area of one triangle. """ -function area(vertices::AbstractVector{<:AbstractPoint{3, VT}}, face::TriangleFace{FT}) where {VT,FT} +function area(vertices::AbstractVector{<:AbstractPoint{3,VT}}, + face::TriangleFace{FT}) where {VT,FT} v1, v2, v3 = vertices[face] return 0.5 * norm(orthogonal_vector(v1, v2, v3)) end @@ -27,14 +28,12 @@ end Calculate the area of all triangles. """ -function area( - vertices::AbstractVector{<:AbstractPoint{3, VT}}, - faces::AbstractVector{TriangleFace{FT}} - ) where {VT,FT} - return sum(x->area(vertices, x), faces) +function area(vertices::AbstractVector{<:AbstractPoint{3,VT}}, + faces::AbstractVector{TriangleFace{FT}}) where {VT,FT} + return sum(x -> area(vertices, x), faces) end -function area(contour::AbstractVector{<:AbstractPoint{N, T}}) where {N, T} +function area(contour::AbstractVector{<:AbstractPoint{N,T}}) where {N,T} n = length(contour) n < 3 && return zero(T) A = zero(T) @@ -42,18 +41,19 @@ function area(contour::AbstractVector{<:AbstractPoint{N, T}}) where {N, T} q = firstindex(contour) while q <= n A += cross(contour[p], contour[q]) - p = q; q +=1 + p = q + q += 1 end return A * T(0.5) end -function area(contour::AbstractVector{Point{3, T}}) where {T} +function area(contour::AbstractVector{Point{3,T}}) where {T} A = zero(eltype(contour)) o = contour[1] - for i in (firstindex(contour)+1):(lastindex(contour)-1) - A += cross(contour[i] - o, contour[i+1] - o) + for i in (firstindex(contour) + 1):(lastindex(contour) - 1) + A += cross(contour[i] - o, contour[i + 1] - o) end - return norm(A)*T(0.5) + return norm(A) * T(0.5) end """ @@ -62,7 +62,7 @@ end InsideTriangle decides if a point P is Inside of the triangle defined by A, B, C. """ -function Base.in(P::T, triangle::Triangle) where T <: AbstractPoint +function Base.in(P::T, triangle::Triangle) where {T<:AbstractPoint} A, B, C = coordinates(triangle) a = C .- B b = A .- C @@ -70,7 +70,7 @@ function Base.in(P::T, triangle::Triangle) where T <: AbstractPoint ap = P .- A bp = P .- B - cp = P .-C + cp = P .- C a_bp = a[1] * bp[2] - a[2] * bp[1] c_ap = c[1] * ap[2] - c[2] * ap[1] @@ -81,28 +81,23 @@ function Base.in(P::T, triangle::Triangle) where T <: AbstractPoint return ((a_bp >= t0) && (b_cp >= t0) && (c_ap >= t0)) end -function snip( - contour::AbstractVector{<: AbstractPoint{N, T}}, u, v, w, n, V - ) where {N, T} +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]] - x = ( - ((B[1]-A[1])*(C[2]-A[2])) - - ((B[2]-A[2])*(C[1]-A[1])) - ) + x = (((B[1] - A[1]) * (C[2] - A[2])) - ((B[2] - A[2]) * (C[1] - A[1]))) if 0.0000000001f0 > x return false end - for p = 1:n - ((p == u) || (p == v) || (p == w)) && continue; + for p in 1:n + ((p == u) || (p == v) || (p == w)) && continue P = contour[V[p]] if P in Triangle(A, B, C) - return false; + return false end end - return true; + return true end """ @@ -111,13 +106,14 @@ end Triangulates a Polygon given as an `AbstractArray{Point}` without holes. It will return a Vector{`facetype`}, defining indexes into `contour` """ -function decompose(::Type{FaceType}, points::AbstractArray{P}) where {P<:AbstractPoint, FaceType <: AbstractFace} +function decompose(::Type{FaceType}, + points::AbstractArray{P}) where {P<:AbstractPoint,FaceType<:AbstractFace} #= allocate and initialize list of Vertices in polygon =# result = FaceType[] # the algorithm doesn't like closed contours contour = if isapprox(last(points), first(points)) - @view points[1:end-1] + @view points[1:(end - 1)] else @view points[1:end] end @@ -129,15 +125,15 @@ function decompose(::Type{FaceType}, points::AbstractArray{P}) where {P<:Abstrac #= we want a counter-clockwise polygon in V =# if 0 < area(contour) - V = Int[i for i=1:n] + V = Int[i for i in 1:n] else - V = Int[i for i=n:-1:1] + V = Int[i for i in n:-1:1] end nv = n #= remove nv-2 Vertices, creating 1 triangle every time =# - count = 2*nv #= error detection =# + count = 2 * nv #= error detection =# v = nv while nv > 2 #= if we loop, it is probably a non-simple polygon =# @@ -147,75 +143,65 @@ function decompose(::Type{FaceType}, points::AbstractArray{P}) where {P<:Abstrac count -= 1 #= three consecutive vertices in current polygon, =# - u = v; (u > nv) && (u = 1) #= previous =# - v = u+1; (v > nv) && (v = 1) #= new v =# - w = v+1; (w > nv) && (w = 1) #= next =# + u = v + (u > nv) && (u = 1) #= previous =# + v = u + 1 + (v > nv) && (v = 1) #= new v =# + w = v + 1 + (w > nv) && (w = 1) #= next =# if snip(contour, u, v, w, nv, V) #= true names of the vertices =# - a = V[u]; b = V[v]; c = V[w]; + a = V[u] + b = V[v] + c = V[w] #= output Triangle =# push!(result, convert_simplex(FaceType, TriangleFace(a, b, c))...) #= remove v from remaining polygon =# - s = v; t = v+1 - while t<=nv + s = v + t = v + 1 + while t <= nv V[s] = V[t] - s += 1; t += 1 + s += 1 + t += 1 end nv -= 1 #= resest error detection counter =# - count = 2*nv + count = 2 * nv end end return result end - -function earcut_triangulate(polygon::Vector{Vector{Point{2, Float64}}}) - lengths = map(x-> UInt32(length(x)), polygon) +function earcut_triangulate(polygon::Vector{Vector{Point{2,Float64}}}) + lengths = map(x -> UInt32(length(x)), polygon) len = UInt32(length(lengths)) - array = ccall( - (:u32_triangulate_f64, libearcut), - Tuple{Ptr{GLTriangleFace}, Cint}, - (Ptr{Ptr{Float64}}, Ptr{UInt32}, UInt32), - polygon, lengths, len - ) + array = ccall((:u32_triangulate_f64, libearcut), Tuple{Ptr{GLTriangleFace},Cint}, + (Ptr{Ptr{Float64}}, Ptr{UInt32}, UInt32), polygon, lengths, len) return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2]) end -function earcut_triangulate(polygon::Vector{Vector{Point{2, Float32}}}) - lengths = map(x-> UInt32(length(x)), polygon) +function earcut_triangulate(polygon::Vector{Vector{Point{2,Float32}}}) + lengths = map(x -> UInt32(length(x)), polygon) len = UInt32(length(lengths)) - array = ccall( - (:u32_triangulate_f32, libearcut), - Tuple{Ptr{GLTriangleFace}, Cint}, - (Ptr{Ptr{Float32}}, Ptr{UInt32}, UInt32), - polygon, lengths, len - ) + array = ccall((:u32_triangulate_f32, libearcut), Tuple{Ptr{GLTriangleFace},Cint}, + (Ptr{Ptr{Float32}}, Ptr{UInt32}, UInt32), polygon, lengths, len) return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2]) end -function earcut_triangulate(polygon::Vector{Vector{Point{2, Int64}}}) - lengths = map(x-> UInt32(length(x)), polygon) +function earcut_triangulate(polygon::Vector{Vector{Point{2,Int64}}}) + lengths = map(x -> UInt32(length(x)), polygon) len = UInt32(length(lengths)) - array = ccall( - (:u32_triangulate_i64, libearcut), - Tuple{Ptr{GLTriangleFace}, Cint}, - (Ptr{Ptr{Int64}}, Ptr{UInt32}, UInt32), - polygon, lengths, len - ) + array = ccall((:u32_triangulate_i64, libearcut), Tuple{Ptr{GLTriangleFace},Cint}, + (Ptr{Ptr{Int64}}, Ptr{UInt32}, UInt32), polygon, lengths, len) return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2]) end -function earcut_triangulate(polygon::Vector{Vector{Point{2, Int32}}}) - lengths = map(x-> UInt32(length(x)), polygon) +function earcut_triangulate(polygon::Vector{Vector{Point{2,Int32}}}) + lengths = map(x -> UInt32(length(x)), polygon) len = UInt32(length(lengths)) - array = ccall( - (:u32_triangulate_i32, libearcut), - Tuple{Ptr{GLTriangleFace}, Cint}, - (Ptr{Ptr{Int32}}, Ptr{UInt32}, UInt32), - polygon, lengths, len - ) + array = ccall((:u32_triangulate_i32, libearcut), Tuple{Ptr{GLTriangleFace},Cint}, + (Ptr{Ptr{Int32}}, Ptr{UInt32}, UInt32), polygon, lengths, len) return unsafe_wrap(Vector{GLTriangleFace}, array[1], array[2]) end @@ -226,9 +212,9 @@ best_earcut_eltype(::Type{Int64}) = Int64 best_earcut_eltype(::Type{Int32}) = Int32 best_earcut_eltype(::Type{<:Integer}) = Int64 -function faces(polygon::Polygon{Dim, T}) where {Dim, T} - PT = Point{Dim, best_earcut_eltype(T)} +function faces(polygon::Polygon{Dim,T}) where {Dim,T} + PT = Point{Dim,best_earcut_eltype(T)} points = [decompose(PT, polygon.exterior)] - foreach(x-> push!(points, decompose(PT, x)), polygon.interiors) + foreach(x -> push!(points, decompose(PT, x)), polygon.interiors) return earcut_triangulate(points) end diff --git a/src/viewtypes.jl b/src/viewtypes.jl index 7b4fdd46..e2917728 100644 --- a/src/viewtypes.jl +++ b/src/viewtypes.jl @@ -31,31 +31,35 @@ y = reinterpret(Point{2, Int}, TupleView{2, 1}(x)) ``` """ -struct TupleView{T, N, Skip, A} <: AbstractVector{T} +struct TupleView{T,N,Skip,A} <: AbstractVector{T} data::A connect::Bool end coordinates(tw::TupleView) = coordinates(tw.data) -function Base.size(x::TupleView{T, N, M}) where {T, N, M} +function Base.size(x::TupleView{T,N,M}) where {T,N,M} nitems = length(x.data) ÷ (N - (N - M)) nitems = nitems - max(N - M, 0) - (nitems + x.connect,) # plus one item if we connect + return (nitems + x.connect,) # plus one item if we connect end -function Base.getindex(x::TupleView{T, N, M}, index::Integer) where {T, N, M} - return ntuple(i-> x.data[mod1(((index - 1) * M) + i, length(x.data))], N) +function Base.getindex(x::TupleView{T,N,M}, index::Integer) where {T,N,M} + return ntuple(i -> x.data[mod1(((index - 1) * M) + i, length(x.data))], N) end -TupleView{N}(x::AbstractVector; connect = false) where N = TupleView{N, N}(x, connect = connect) - -function TupleView{N, M}(x::AbstractVector{T}; connect = false) where {T, N, M} - TupleView{NTuple{N, T}, N, M, typeof(x)}(x, connect) +function TupleView{N}(x::AbstractVector; connect=false) where {N} + return TupleView{N,N}(x, connect=connect) end +function TupleView{N,M}(x::AbstractVector{T}; connect=false) where {T,N,M} + return TupleView{NTuple{N,T},N,M,typeof(x)}(x, connect) +end -@inline connected_line(points::AbstractVector{<: AbstractPoint{N}}, skip = N) where N = connect(points, Line, skip) +@inline function connected_line(points::AbstractVector{<:AbstractPoint{N}}, + skip=N) where {N} + return connect(points, Line, skip) +end """ connect(points::AbstractVector{<: AbstractPoint}, P::Type{<: Polytype{N}}, skip::Int = N) @@ -67,28 +71,32 @@ Example: x = connect(Point[(1, 2), (3, 4), (5, 6), (7, 8)], Line, 2) x == [Line(Point(1, 2), Point(3, 4)), Line(Point(5, 6), Point(7, 8))] """ -@inline function connect(points::AbstractVector{Point}, P::Type{<: Polytope{N, T} where {N, T}}, skip::Int = length(P)) where Point <: AbstractPoint - reinterpret(Polytope(P, Point), TupleView{length(P), skip}(points)) +@inline function connect(points::AbstractVector{Point}, + P::Type{<:Polytope{N,T} where {N,T}}, + skip::Int=length(P)) where {Point<:AbstractPoint} + return reinterpret(Polytope(P, Point), TupleView{length(P),skip}(points)) end -@inline function connect(points::AbstractVector{T}, P::Type{<: Point{N}}, skip::Int = N) where {T <: Real, N} - reinterpret(Point{N, T}, TupleView{N, skip}(points)) +@inline function connect(points::AbstractVector{T}, P::Type{<:Point{N}}, + skip::Int=N) where {T<:Real,N} + return reinterpret(Point{N,T}, TupleView{N,skip}(points)) end -@inline function connect(points::AbstractVector{T}, P::Type{<: AbstractFace{N}}, skip::Int = N) where {T <: Real, N} - reinterpret(Face(P, T), TupleView{N, skip}(points)) +@inline function connect(points::AbstractVector{T}, P::Type{<:AbstractFace{N}}, + skip::Int=N) where {T<:Real,N} + return reinterpret(Face(P, T), TupleView{N,skip}(points)) end - -@inline function connect(points::AbstractMatrix{T}, P::Type{<: AbstractPoint{N}}) where {T <: Real, N} - if size(points, 1) === N - return reinterpret(Point{N, T}, points) +@inline function connect(points::AbstractMatrix{T}, + P::Type{<:AbstractPoint{N}}) where {T<:Real,N} + return if size(points, 1) === N + return reinterpret(Point{N,T}, points) elseif size(points, 2) === N seglen = size(points, 1) columns = ntuple(N) do i - view(points, ((i-1) * seglen + 1):(i * seglen)) + return view(points, ((i - 1) * seglen + 1):(i * seglen)) end - return StructArray{Point{N, T}}((StructArray{NTuple{N, T}}(columns),)) + return StructArray{Point{N,T}}((StructArray{NTuple{N,T}}(columns),)) else error("Dim 1 or 2 must be equal to the point dimension!") end @@ -112,20 +120,15 @@ linestring = FaceView(points, LineFace[...]) Polygon(linestring) ``` """ -struct FaceView{ - Element, - Point <: AbstractPoint, - Face <: AbstractFace, - P <: AbstractVector{Point}, - F <: AbstractVector{Face} - } <: AbstractVector{Element} +struct FaceView{Element,Point<:AbstractPoint,Face<:AbstractFace,P<:AbstractVector{Point}, + F<:AbstractVector{Face}} <: AbstractVector{Element} elements::P faces::F end -const SimpleFaceView{Dim, T, NFace, IndexType, PointType<:AbstractPoint{Dim, T}, FaceType<:AbstractFace{NFace, IndexType}} = FaceView{ - Ngon{Dim, T, NFace, PointType}, PointType, FaceType, Vector{PointType}, Vector{FaceType}} +const SimpleFaceView{Dim,T,NFace,IndexType,PointType<:AbstractPoint{Dim,T}, + FaceType<:AbstractFace{NFace,IndexType}} = FaceView{Ngon{Dim,T,NFace,PointType},PointType,FaceType,Vector{PointType},Vector{FaceType}} function Base.getproperty(faceview::FaceView, name::Symbol) return getproperty(getfield(faceview, :elements), name) @@ -139,19 +142,20 @@ Tables.schema(faceview::FaceView) = Tables.schema(getfield(faceview, :elements)) Base.size(faceview::FaceView) = size(getfield(faceview, :faces)) -function Base.show(io::IO, ::Type{<: FaceView{Element}}) where Element - if @isdefined Element +function Base.show(io::IO, ::Type{<:FaceView{Element}}) where {Element} + return if @isdefined Element print(io, "FaceView{", Element, "}") else print(io, "FaceView{T}") end end -@propagate_inbounds function Base.getindex(x::FaceView{Element}, i) where Element - return Element(map(idx-> coordinates(x)[idx], faces(x)[i])) +@propagate_inbounds function Base.getindex(x::FaceView{Element}, i) where {Element} + return Element(map(idx -> coordinates(x)[idx], faces(x)[i])) end -@propagate_inbounds function Base.setindex!(x::FaceView{Element}, element::Element, i) where Element +@propagate_inbounds function Base.setindex!(x::FaceView{Element}, element::Element, + i) where {Element} face = faces(x)[i] for (i, f) in enumerate(face) # TODO unroll!? coordinates(x)[face[i]] = element[i] @@ -159,8 +163,9 @@ end return element end -function connect(points::AbstractVector{P}, faces::AbstractVector{F}) where {P <: AbstractPoint, F <: AbstractFace} - FaceView{Polytope(P, F), P, F, typeof(points), typeof(faces)}(points, faces) +function connect(points::AbstractVector{P}, + faces::AbstractVector{F}) where {P<:AbstractPoint,F<:AbstractFace} + return FaceView{Polytope(P, F),P,F,typeof(points),typeof(faces)}(points, faces) end coordinates(mesh::FaceView) = getfield(mesh, :elements)