Skip to content
This repository was archived by the owner on Nov 22, 2023. It is now read-only.

add Polygons + do long standing refactors #166

Merged
merged 11 commits into from
Mar 2, 2020
1 change: 0 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
julia 0.7
IterTools
StaticArrays 0.9.1
ColorTypes 0.3.1
FixedPointNumbers 0.3
14 changes: 8 additions & 6 deletions src/GeometryTypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ using StaticArrays
using ColorTypes
using LinearAlgebra

import FixedPointNumbers # U8
import FixedPointNumbers
using FixedPointNumbers: N0f8

import IterTools
using IterTools: partition

import Base: ==,
*,
Expand All @@ -28,7 +27,9 @@ import Base: ==,
size,
split,
union,
unique
unique,
iterate,
eltype

include("FixedSizeArrays.jl")
using .FixedSizeArrays
Expand All @@ -53,11 +54,11 @@ include("setops.jl")
include("display.jl")
include("slice.jl")
include("decompose.jl")
include("deprecated.jl")
include("center.jl")
include("convexhulls.jl")
include("gjk.jl")
include("polygons.jl")
include("polygon.jl")
include("polygon_triangulations.jl")
include("lines.jl")
include("cylinder.jl")

Expand Down Expand Up @@ -103,6 +104,7 @@ export AABB,
HyperCube,
HyperSphere,
intersects,
isinside,
LineSegment,
Mat,
Mesh2D,
Expand Down
Empty file removed src/deprecated.jl
Empty file.
91 changes: 71 additions & 20 deletions src/hyperrectangles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,25 @@ function _split(b::H, axis, value) where H<:HyperRectangle
end

# empty constructor such that update will always include the first point
function (HR::Type{HyperRectangle{N,T}})() where {T,N}
function (HR::Type{Rect{N, T}})() where {T,N}
HR(Vec{N,T}(typemax(T)), Vec{N,T}(typemin(T)))
end

# conversion from other HyperRectangles
function (HR::Type{HyperRectangle{N,T1}})(a::HyperRectangle{N,T2}) where {N,T1,T2}
function (HR::Type{Rect{N,T1}})(a::Rect{N, T2}) where {N,T1,T2}
HR(Vec{N, T1}(minimum(a)), Vec{N, T1}(widths(a)))
end

function HyperRectangle(v1::Vec{N,T1}, v2::Vec{N,T2}) where {N,T1,T2}
T = promote_type(T1,T2)
HyperRectangle{N,T}(Vec{N,T}(v1), Vec{N,T}(v2))
function Rect(v1::Vec{N, T1}, v2::Vec{N, T2}) where {N,T1,T2}
T = promote_type(T1, T2)
Rect{N,T}(Vec{N, T}(v1), Vec{N, T}(v2))
end


function (HR::Type{HyperRectangle{N,T}})(a::GeometryPrimitive) where {N,T}
HR(Vec{N, T}(minimum(a)), Vec{N, T}(widths(a)))
function Rect{N, T}(a::GeometryPrimitive) where {N, T}
Rect{N, T}(Vec{N, T}(minimum(a)), Vec{N, T}(widths(a)))
end

"""
```
HyperRectangle(vals::Number...)
Expand All @@ -59,20 +60,63 @@ width == Vec(1,2)
Expr(:call, :HyperRectangle, v1, v2)
end

function HyperRectangle(r::SimpleRectangle{T}) where T
HyperRectangle{2,T}(r)
end
Rect(r::SimpleRectangle{T}) where T = HyperRectangle{2, T}(r)
Rect{N}(r::SimpleRectangle{T}) where {N, T} = Rect{N, T}(r)

function HyperRectangle{N,T}(r::SimpleRectangle) where {N,T}
if N > 2
return HyperRectangle(Vec{N, T}(T(r.x), T(r.y), Vec{N-2,T}(zero(T))...),
Vec{N, T}(T(r.w), T(r.h), Vec{N-2,T}(zero(T))...))
function Rect{N, T}(r::SimpleRectangle) where {N, T}
if N === 2
return Rect(Vec{N, T}(T(r.x), T(r.y)), Vec{N, T}(T(r.w), T(r.h)))
else
return HyperRectangle(Vec{N, T}(T(r.x), T(r.y)),
Vec{N, T}(T(r.w), T(r.h)))
return Rect(
Vec{N, T}(r.x, r.y, Vec{N - 2}(zero(T))...),
Vec{N, T}(r.w, r.h, Vec{N - 2}(zero(T))...)
)
end
end

#=
From other types
=#
function Rect2D(xy::NamedTuple{(:x, :y)}, wh::NamedTuple{(:width, :height)})
Rect2D(xy.x, xy.y, wh.width, wh.height)
end


function FRect3D(x::Rect2D{T}) where T
FRect3D{T}(Vec{3, T}(minimum(x)..., 0), Vec{3, T}(widths(x)..., 0.0))
end

#=
From different args
=#
function Rect2D(x::Number, y::Number, w::Number, h::Number)
args = promote(x, y, w, h)
FRect2D{2, eltype(args)}(args...)
end
function Rect2D{T}(args::Vararg{Number, 4}) where T
x, y, w, h = T <: Integer ? round.(T, args) : args
FRect2D{T}(Vec{2, T}(x, y), Vec{2, T}(w, h))
end

function Rect2D(xy::VecTypes{2}, w::Number, h::Number)
Rect2D(xy..., w, h)
end

function Rect2D(x::Number, y::Number, wh::VecTypes{2})
Rect2D(x, y, wh...)
end

#=
From limits
=#
function FRect3D(x::Tuple{Tuple{<: Number, <: Number}, Tuple{<: Number, <: Number}})
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]...))
end


"""
Transform a `HyperRectangle` using a matrix. Maintains axis-align properties
so a significantly larger HyperRectangle may be generated.
Expand Down Expand Up @@ -238,12 +282,19 @@ AbsoluteRectangle(mini::Vec{N,T}, maxi::Vec{N,T}) where {N,T} = HyperRectangle{N

AABB(a) = AABB{Float32}(a)

function (B::Type{AABB{T}})(a::Pyramid) where T
function Rect{T}(a::Pyramid) where T
w,h = a.width/T(2), a.length
m = Vec{3,T}(a.middle)
B(m-Vec{3,T}(w,w,0), m+Vec{3,T}(w, w, h))
Rect{T}(m-Vec{3,T}(w,w,0), m+Vec{3,T}(w, w, h))
end

(B::Type{AABB{T}})(a::Cube) where {T} = B(origin(a), widths(a))
Rect{T}(a::Cube) where T = Rect{T}(origin(a), widths(a))

(B::Type{AABB{T}})(a::AbstractMesh) where {T} = B(vertices(a))
Rect{T}(a::AbstractMesh) where T = Rect{T}(vertices(a))

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)
end
60 changes: 29 additions & 31 deletions src/lines.jl
Original file line number Diff line number Diff line change
@@ -1,25 +1,4 @@
using GeometryTypes
# function intersects{N,T}(a::LineSegment{Point{N,T}}, b::LineSegment{Point{N,T}})
# x12 = a[1][1] - a[2][1]
# x34 = b[1][1] - b[2][1]
# y12 = a[1][2] - a[2][2]
# y34 = b[1][2] - b[2][2]
#
# c = x12 * y34 - y12 * x34;
# if abs(c) < 0.01
# # No intersection
# return false, Point{2,T}(0,0);
# else
# # Intersection
# ai = a[1][1] * a[2][2] - a[1][2] * a[2][1];
# bi = b[1][1] * b[2][2] - b[1][2] * b[2][1];
#
# x = (ai * x34 - bi * x12) / c;
# y = (ai * y34 - bi * y12) / c;
#
# return true, Point{2,T}(x,y)
# end
# end

"""
Intersection of 2 line segmens `a` and `b`.
Returns intersection_found::Bool, intersection_point
Expand All @@ -29,10 +8,10 @@ function intersects(a::LineSegment{Point{N,T}}, b::LineSegment{Point{N,T}}) wher

verticalA = v1[1] == v2[1]
verticalB = v3[1] == v4[1]

# if a segment is vertical the linear algebra might have trouble
# so we will rotate the segments such that neither is vertical
dorotation = verticalA || verticalB
dorotation = verticalA || verticalB

if dorotation
θ = T(0.0)
Expand Down Expand Up @@ -67,7 +46,7 @@ function intersects(a::LineSegment{Point{N,T}}, b::LineSegment{Point{N,T}}) wher
(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

# don't forget to rotate the answer back
if dorotation
(x, y) = transpose(rotation)*[x, y]
Expand All @@ -76,24 +55,44 @@ function intersects(a::LineSegment{Point{N,T}}, b::LineSegment{Point{N,T}}) wher
end


function simple_concat(vec, range, endpoint::P) where P
result = Vector{P}(undef, length(range)+1)
for (i,j) in enumerate(range)
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))]
end
result[end] = endpoint
result
end

struct Partition{N, T}
data::T
connect::Bool # build a pair connecting to first elements
end
Partition{N}(data, connect = false) where N = Partition{N, typeof(data)}(data, connect)

# one less if not connect to first elements
length(x::Partition{N}) where N = (length(x.data) ÷ (N - 1)) - (!x.connect)
eltype(itr::Partition{N}) where N = NTuple{N, eltype(itr.data)}

function iterate(iter::Partition{N}, state = 1) where N
state > length(iter) && return nothing
let data = iter.data, s = state
tup = ntuple(Val(N)) do i
data[mod1(((s - 1) * (N - 1)) + i, length(data))]
end
return tup, state + 1
end
end

"""
Finds all self intersections of polygon `points`
"""
function self_intersections(points::Vector{Point{N,T}}) where {N,T}
sections = Point{N,T}[]
intersections = Int[]
wraparound = i-> mod1(i, length(points) - 1)
for (i, (a,b)) in enumerate(partition(points, 2, 1))
for (j, (a2, b2)) in enumerate(partition(points, 2, 1))
for (i, (a, b)) in enumerate(Partition{2}(points))
for (j, (a2, b2)) in enumerate(Partition{2}(points))
is1, is2 = wraparound(i+1), wraparound(i-1)
if i!=j && is1!=j && is2!=j && !(i in intersections) && !(j in intersections)
intersected, p = GeometryTypes.intersects(LineSegment(a,b), LineSegment(a2, b2))
Expand Down Expand Up @@ -126,4 +125,3 @@ function split_intersections(points::Vector{Point{N,T}}) where {N,T}
error("More than 1 intersections can't be handled currently. Found: $intersections, $sections")
end
end

2 changes: 1 addition & 1 deletion src/meshes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ function merge(
faces = copy(m1.faces)
attribs = Dict{Symbol, Any}(filter(((k,v),) -> k != :color, attributes_noVF(m1)))
attribs = Dict{Symbol, Any}([(k, copy(v)) for (k,v) in attribs])
color_attrib = RGBA{U8}[RGBA{U8}(m1.color)]
color_attrib = RGBA{N0f8}[RGBA{N0f8}(m1.color)]
index = Float32[length(color_attrib)-1 for i=1:length(m1.vertices)]
for mesh in meshes
append!(faces, mesh.faces .+ length(vertices))
Expand Down
89 changes: 89 additions & 0 deletions src/polygon.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
function Polygon(
points::AbstractVector;
boundingbox = Rect2D(points),
isconvex = isconvex(points),
ishole = false
)
ps = convert(AbstractVector{Point}, points)
NumType = eltype(eltype(ps))
Polygon{NumType, typeof(ps)}(ps, boundingbox, isconvex, ishole)
end

iterate(polygons::MultiPolygon) = iterate(polygons.polygons)
iterate(polygons::MultiPolygon, state) = iterate(polygons.polygons, state)

vertices(poly::Polygon) = poly.points
boundingbox(poly::Polygon) = poly.boundingbox
ishole(poly::Polygon) = poly.ishole
isconvex(poly::Polygon) = poly.isconvex


"""
in(point::AbstractPoint, poly::Polygon)

# An implementation of Hormann-Agathos (2001) Point in Polygon algorithm
# See: http://www.sciencedirect.com/science/article/pii/S0925772101000128
# Code segment adapted from PolygonClipping.jl
"""
function in(point::AbstractPoint, poly::Polygon)
(point in boundingbox(poly)) || return false
c = false
detq(q1, q2, point) = (q1[1]-point[1])*(q2[2]-point[2])-(q2[1]-point[1])*(q1[2]-point[2])
for (q1, q2) in Partition{2}(poly)
if q1 == point
@warn("point on polygon vertex - returning false")
return false
end
if q2[2] == point[2]
if q2[1] == point[1]
@warn("point on polygon vertex - returning false")
return false
elseif (q1[2] == point[2]) && ((q2[1] > x) == (q1[1] < point[1]))
@warn("point on edge - returning false")
return false
end
end
if (q1[2] < point[2]) != (q2[2] < point[2]) # crossing
if q1[1] >= point[1]
if q2[1] > point[1]
c = !c
elseif ((detq(q1,q2,point) > 0) == (q2[2] > q1[2])) # right crossing
c = !c
end
elseif q2[1] > point[1]
if ((detq(q1,q2,point) > 0) == (q2[2] > q1[2])) # right crossing
c = !c
end
end
end
end
return c
end

function in(point::AbstractPoint, polygons::MultiPolygon)
point in boundingbox(polygons) || return false
for polygon in polygons
if ishole(polygon)
point in polygon && return false
else
point in polygon && return true
end
end
return false
end

function isconvex(points::AbstractVector{<: AbstractPoint})
length(points) < 4 && return true
sign = false
for (i, (a, b, c)) in enumerate(Partition{3}(points, true))
Δ1 = c .- b
Δ2 = a .- b
det = cross(Δ1, Δ2)
if i == 1
sign = det > 0
elseif (sign != (det > 0))
return false
end
end
return true
end
Loading