Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

A few updates #10

Merged
merged 10 commits into from
Feb 2, 2015
Merged
77 changes: 65 additions & 12 deletions src/Clipper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,24 @@ typealias __ClipperIntPoint Union(pcpp"ClipperLib::IntPoint",
rcpp"ClipperLib::IntPoint")

# TODO: Remove this when https://github.com/Keno/Cxx.jl/issues/72 is closed
typealias __ClipperPath Union(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppRef{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppPtr{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppPtr{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppRef{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)})


typealias __ClipperPaths Union(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)},
typealias __ClipperPath Union(
Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppRef{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppPtr{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppRef{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppPtr{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},
)


typealias __ClipperPaths Union(
Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppRef{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppPtr{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppRef{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppPtr{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)},
Cxx.CppRef{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::vector")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},Cxx.CppValue{Cxx.CppTemplate{Cxx.CppBaseType{symbol("std::__1::allocator")},(Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::IntPoint")},(false,false,false)},)},(false,false,false)})},(false,false,false)},)},(false,false,false)})},(false,false,false)})
)

typealias __ClipperClipperBase Union(pcpp"ClipperLib::ClipperBase",
cpcpp"ClipperLib::ClipperBase",
Expand Down Expand Up @@ -306,6 +310,16 @@ function Path(ct::Integer=0)
@cxx ClipperLib::Path(ct)
end

@doc """
This function builds a Path structure from a Vector of Tuples.
""" ->
function Path(pts::Vector{(Int, Int)})
p = Path()
for point in pts
push!(p, IntPoint(point...))
end
p
end

@doc """
This structure is fundamental to the Clipper Library. It's a list or array of
Expand All @@ -324,6 +338,17 @@ function Paths(ct::Integer=0)
@cxx ClipperLib::Paths(ct)
end

@doc """
This function builds a Paths structure from a Vector of Path objects.
""" ->
function Paths{T<:__ClipperPath}(paths::Vector{T})
pths = Paths()
for path in paths
push!(pths, path)
end
pths
end

@doc """
This function converts a PolyTree structure into a Paths structure.
""" ->
Expand Down Expand Up @@ -534,6 +559,10 @@ function Base.length(c::__ClipperPolyTree)
@cxx c->Total()
end

function child_count(c::__ClipperPolyTree)
@cxx c->ChildCount()
end

Copy link
Member

Choose a reason for hiding this comment

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

This needs tests.

Apparently the PolyTree is just an array of PolyNodes: https://github.com/Voxel8/Clipper.jl/blob/6a3cce9ea409d7b4977024e3bbee1830955cb205/src/clipper_cpp.jl#L138

It would be convenient if we could define getindex too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wrote a test for child_count.

I am struggling with implementing getindex. Here is what I tried:

@inline function Base.getindex(p::__ClipperPolyTree, i::Integer)
    icxx"$p[$i-1];"
end

I get the following error when I try to call getindex

ERROR: LoadError: MethodError: `getindex` has no method matching getindex(::Cxx.CppValue{Cxx.CppBaseType{symbol("ClipperLib::PolyTree")},(false,false,false)}, ::Int64)
Closest candidates are:
  getindex(::(Any...,), ::Int64)
  getindex(::(Any...,), ::Real)
  getindex{T}(::FloatRange{T}, ::Integer)

Choose a reason for hiding this comment

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

PolyTree is a subclass of PolyNode, so I don't think getindex makes sense. See definition here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed getindex from this PR. If we later decide we want it we can add it back in in another PR.

@doc """
The returned Polynode will be the first child if any, otherwise the next
sibling, otherwise the next sibling of the Parent etc.
Expand Down Expand Up @@ -926,6 +955,10 @@ end
@cxx a->push_back(b)
end

@inline function Base.push!(a::__ClipperPath, b::(Int, Int))
push!(a, IntPoint(b...))
end

@inline function Base.push!(a::__ClipperPaths,
b::__ClipperPath)
@cxx a->push_back(b)
Expand Down Expand Up @@ -979,6 +1012,14 @@ end
length(p)
end

function ==(a::__ClipperPath, b::__ClipperPath)
length(a) != length(b) && return false
for i = 1:length(a)
a[i] != b[i] && return false
end
return true
end

function Base.show(io::IO, v::__ClipperIntPoint)
print(io, string("(", x(v),",", y(v),")"))
end
Expand All @@ -991,16 +1032,28 @@ function Base.show(io::IO, p::__ClipperPath)
if isempty(p)
return
end
print(io, "Path([")
for i = 1:length(p)-1
print(io, string("(", x(p[i]), ",", y(p[i]), "), "))
end
print(io, string("(", x(p[end]), ",", y(p[end]), ")"))
print(io, "])")
end

function Base.show(io::IO, p::__ClipperPaths)
for i = 1:length(p)
show(io, p[i])
len = length(p)
if len == 0
print(io, "Paths()")
return
end
print(io, "Paths([\n ")
for i = 1:len
print(io, p[i])
if i < len
print(io, ",\n ")

Choose a reason for hiding this comment

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

I don't think it's good practice to put newlines in show() output. If it's nested inside another object, the whole output is broken.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Julia's arrays use newlines in their output:

julia> a = [1 2;3 4]
2x2 Array{Int64,2}:
 1  2
 3  4

I think newlines make the arrays significantly easier to read, and I think the same can be said about Paths()

Copy link
Member

Choose a reason for hiding this comment

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

I think under the covers the REPL's 'P' is println:

julia> 6
6

julia> print(6)
6
julia> 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to this show() is being deprecated in favor of write() anyway. It seems like println() just calls show() under the covers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@show still uses newlines for arrays:

julia> @show a
a = [1 2
 3 4]

end

Choose a reason for hiding this comment

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

I like that you made show(::Path) output code that can construct it. Can you do the same for Paths?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have a show method that works, but I am having trouble writing a constructor for Paths() that takes an array of Path() objects. It appears I can't specify an array of a type alias, @sjkelly does that sound correct?

end
print(io, "\n)")
end

# Clipper basic interface
Expand Down
90 changes: 83 additions & 7 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,27 @@ using Base.Test
a = Path()
b = IntPoint(1,0)
push!(a, b)
println(a)
push!(a, b)
push!(a, b)
println(a)
o = IOBuffer()
show(o, b)
@test ASCIIString(o.data) == "(1,0)"
o = IOBuffer()
show(o, a)
@test ASCIIString(o.data) == "Path([(1,0), (1,0), (1,0)])"

#test path constructor
p1 = Path()
push!(p1, IntPoint(0, 0))
push!(p1, IntPoint(0, 10))
push!(p1, IntPoint(10, 10))
p2 = Path([(0,0), (0,10), (10,10)])
@test p1 == p2

#test push shortcut
p = Path()
push!(p, (0, 0))
@test p == Path([(0,0)])

# test setindex
p = Path(2)
Expand Down Expand Up @@ -167,7 +184,6 @@ bottom!(ir,4)
@test right(ir) == 2
@test top(ir) == 3
@test bottom(ir) == 4
println(ir)

# test Clip
println("Testing Clip...")
Expand Down Expand Up @@ -232,12 +248,73 @@ execute!(c, ctIntersection, sol)
@test length(sol) == 1
f = first(sol)
@test length(Path(f)) == 4
@show children(f)
@test !is_hole(f)
@test !is_open(f)
@test child_count(f) == 0
@show next(f)
@show parent(f)
@test child_count(sol) == 1

# path equality
println("Testing path equality")
p1 = Path()
push!(p1, IntPoint(0,0))
push!(p1, IntPoint(10,0))
push!(p1, IntPoint(10,10))
p2 = Path()
push!(p2, IntPoint(0,0))
push!(p2, IntPoint(10,0))
push!(p2, IntPoint(10,10))
@test p1 == p2

# offsetting a polygon with holes.
println("Testing offset of a polygon with holes...")
perimeter = Path()
push!(perimeter, IntPoint(-10,-10))
push!(perimeter, IntPoint(60,-10))
push!(perimeter, IntPoint(60,60))
push!(perimeter, IntPoint(-10,60))

hole = Path()
push!(hole, IntPoint(4,4))
push!(hole, IntPoint(4,8))
push!(hole, IntPoint(8,8))
push!(hole, IntPoint(8,4))

hole2 = Path()
push!(hole2, IntPoint(12,4))
push!(hole2, IntPoint(12,8))
push!(hole2, IntPoint(16,8))
push!(hole2, IntPoint(16,4))

o = Offset()
paths = Paths()
push!(paths, perimeter)
push!(paths, hole)
push!(paths, hole2)
add!(o, paths, jtMiter, etClosedPolygon)

outpaths = Paths()
execute!(o, outpaths, -2)
@test length(outpaths) == 2
@test outpaths[1] == Path([(58,58), (-8,58), (-8,-8), (58,-8)])
@test outpaths[2] == Path([(18,2), (2,2), (2,10), (18,10)])

# test Paths() construction from a Vector of Path objects
paths = Paths([
Path([(0,0)]),
Path([(10,10)]),
])
@test length(paths) == 2
@test paths[1] == Path([(0,0)])
@test paths[2] == Path([(10,10)])

# test Paths() printing
o = IOBuffer()
show(o, outpaths)
expected = """Paths([
Path([(58,58), (-8,58), (-8,-8), (58,-8)]),
Path([(18,2), (2,2), (2,10), (18,10)])
)"""
@test ASCIIString(o.data) == expected

p = Path()
push!(p, IntPoint(0,0))
Expand All @@ -247,4 +324,3 @@ push!(p, IntPoint(0,10))
ps = Paths()
push!(ps, p)
pt = Clipper.Basic.offset(ps, 2)
@show pt