Skip to content

Commit ea22e45

Browse files
authored
use slightly better packing (#11)
* use slightly better packing * add source
1 parent 850ef06 commit ea22e45

File tree

1 file changed

+57
-37
lines changed

1 file changed

+57
-37
lines changed

src/rectangle.jl

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,69 @@
1-
mutable struct BinaryNode{T}
2-
left::Union{Nothing, T}
3-
right::Union{Nothing, T}
1+
# ported from https://github.com/joelverhagen/2D-Bin-Packing/blob/master/BinPacking/PackingTreeNode.h
42

5-
BinaryNode{T}() where {T} = new{T}(nothing, nothing)
6-
BinaryNode(left::T, right::T) where {T} = new{T}(nothing, nothing)
7-
BinaryNode{T}(left::T, right::T) where {T} = new{T}(nothing, nothing)
3+
mutable struct RectanglePacker{T}
4+
# the rectangle represented by this node
5+
area::Rect{2, T}
6+
filled::Bool
7+
# a vector of child nodes
8+
left::Union{RectanglePacker{T}, Nothing}
9+
right::Union{RectanglePacker{T}, Nothing}
810
end
911

10-
mutable struct RectanglePacker{T}
11-
children::BinaryNode{RectanglePacker{T}}
12-
area::Rect2{T}
12+
function RectanglePacker(area::Rect{2, T}) where {T}
13+
return RectanglePacker{T}(area, false, nothing, nothing)
1314
end
1415

15-
left(a::RectanglePacker) = a.children.left
16-
left(a::RectanglePacker{T}, r::RectanglePacker{T}) where {T} = (a.children.left = r)
17-
right(a::RectanglePacker) = a.children.right
18-
right(a::RectanglePacker{T}, r::RectanglePacker{T}) where {T} = (a.children.right = r)
19-
RectanglePacker(area::Rect2{T}) where {T} = RectanglePacker{T}(BinaryNode{RectanglePacker{T}}(), area)
20-
isleaf(a::RectanglePacker) = (a.children.left) == nothing && (a.children.right == nothing)
21-
# This is rather append, but it seems odd to use another function here.
22-
# Maybe its a bad idea, to call it push regardless!?
2316
function Base.push!(node::RectanglePacker{T}, areas::Vector{Rect2{T}}) where T
2417
sort!(areas, by=GeometryBasics.norm widths)
2518
return RectanglePacker{T}[push!(node, area) for area in areas]
2619
end
2720

28-
function Base.push!(node::RectanglePacker{T}, area::Rect2{T}) where T
29-
if !isleaf(node)
30-
l = push!(left(node), area)
31-
l !== nothing && return l
32-
# if left does not have space, try right
33-
return push!(right(node), area)
34-
end
35-
newarea = RectanglePacker(area).area
36-
if all(widths(newarea) .<= widths(node.area))
37-
neww, newh = widths(newarea)
38-
xmin, ymin = minimum(node.area)
39-
xmax, ymax = maximum(node.area)
40-
w, h = widths(node.area)
41-
oax,oay,oaxw,oayh = xmin + neww, ymin, xmax, ymin + newh
42-
nax,nay,naxw,nayh = xmin, ymin + newh, xmax, ymax
43-
rax,ray,raxw,rayh = xmin, ymin, xmin + neww, ymin + newh
44-
left(node, RectanglePacker(Rect2(oax, oay, oaxw - oax, oayh - oay)))
45-
right(node, RectanglePacker(Rect2(nax, nay, naxw - nax, nayh - nay)))
46-
return RectanglePacker(Rect2(rax, ray, raxw - rax, rayh - ray))
21+
function Base.push!(node::RectanglePacker{T}, new_rect::Rect) where {T}
22+
nwidth, nheight = widths(new_rect)
23+
# if the node is not a leaf (has children)
24+
if !isnothing(node.left) || !isnothing(node.right)
25+
# try inserting into the first child
26+
new_node = push!(node.left, new_rect)
27+
!isnothing(new_node) && return new_node
28+
# no room, insert into the second child
29+
return push!(node.right, new_rect)
30+
else
31+
32+
# if there's already an image here, return
33+
node.filled && return nothing
34+
area = node.area
35+
awidth, aheight = widths(area)
36+
# if the image doesn't fit, return
37+
if nwidth > awidth || nheight > aheight
38+
return nothing
39+
end
40+
41+
# if the image fits perfectly, accept
42+
if nwidth == awidth && nheight == aheight
43+
node.filled = true
44+
return node
45+
end
46+
47+
# otherwise, split the node and create two children
48+
49+
# decide which way to split
50+
51+
left, bottom = minimum(area)
52+
right, top = maximum(area)
53+
54+
dw = awidth - nwidth
55+
dh = aheight - nheight
56+
57+
if dw > dh
58+
left_rectangle = Rect2{T}(left, bottom, nwidth, aheight)
59+
right_rectangle = Rect2{T}(left + nwidth, bottom, awidth - nwidth, aheight)
60+
else
61+
left_rectangle = Rect2{T}(left, bottom, awidth, nheight)
62+
right_rectangle = Rect2{T}(left, bottom + nheight, awidth, aheight - nheight)
63+
end
64+
node.left = RectanglePacker(left_rectangle)
65+
node.right = RectanglePacker(right_rectangle)
66+
# insert into the first child we created
67+
return push!(node.left, new_rect)
4768
end
48-
return nothing
4969
end

0 commit comments

Comments
 (0)