Skip to content

Commit 8a87f75

Browse files
Knuesel refactor f0 rect (#9)
* Replace IRect2D with Rect2i, etc. * Bump GeometryBasics version * add gh-actions Co-authored-by: Jeremie Knuesel <knuesel@gmail.com>
1 parent f03fb1b commit 8a87f75

File tree

10 files changed

+123
-58
lines changed

10 files changed

+123
-58
lines changed

.github/workflows/CompatHelper.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: CompatHelper
2+
on:
3+
schedule:
4+
- cron: 0 0 * * *
5+
workflow_dispatch:
6+
jobs:
7+
CompatHelper:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Pkg.add("CompatHelper")
11+
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
12+
- name: CompatHelper.main()
13+
env:
14+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15+
COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
16+
run: julia -e 'using CompatHelper; CompatHelper.main()'

.github/workflows/RegisterAction.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: RegisterAction
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
version:
6+
description: Version to register or component to bump
7+
required: true
8+
jobs:
9+
register:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: julia-actions/RegisterAction@latest
13+
with:
14+
token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/TagBot.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
name: TagBot
22
on:
3-
schedule:
4-
- cron: 0 * * * *
3+
issue_comment:
4+
types:
5+
- created
6+
workflow_dispatch:
57
jobs:
68
TagBot:
9+
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
710
runs-on: ubuntu-latest
811
steps:
912
- uses: JuliaRegistries/TagBot@v1
1013
with:
1114
token: ${{ secrets.GITHUB_TOKEN }}
15+
ssh: ${{ secrets.DOCUMENTER_KEY }}

.github/workflows/ci.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
name: CI
3+
on:
4+
- push
5+
- pull_request
6+
jobs:
7+
test:
8+
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
9+
runs-on: ${{ matrix.os }}
10+
strategy:
11+
fail-fast: false
12+
matrix:
13+
version:
14+
- '1.4'
15+
- '1.6'
16+
os:
17+
- ubuntu-latest
18+
arch:
19+
- x64
20+
steps:
21+
- uses: actions/checkout@v2
22+
- uses: julia-actions/setup-julia@v1
23+
with:
24+
version: ${{ matrix.version }}
25+
arch: ${{ matrix.arch }}
26+
- uses: actions/cache@v1
27+
env:
28+
cache-name: cache-artifacts
29+
with:
30+
path: ~/.julia/artifacts
31+
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
32+
restore-keys: |
33+
${{ runner.os }}-test-${{ env.cache-name }}-
34+
${{ runner.os }}-test-
35+
${{ runner.os }}-
36+
- uses: julia-actions/julia-buildpkg@v1
37+
- uses: julia-actions/julia-runtest@v1
38+
- uses: julia-actions/julia-processcoverage@v1
39+
- uses: codecov/codecov-action@v1
40+
with:
41+
file: lcov.info

.travis.yml

Lines changed: 0 additions & 10 deletions
This file was deleted.

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ version = "0.4.1"
66
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
77

88
[compat]
9-
GeometryBasics = "0.2, 0.3"
9+
GeometryBasics = "0.4.1"
1010
julia = "1"
1111

1212
[extras]

src/guillotine.jl

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ struct GuillotinePacker{T}
2424
merge::Bool
2525
rect_choice::FreeRectChoiceHeuristic
2626
split_method::GuillotineSplitHeuristic
27-
free_rectangles::Vector{Rect2D{T}}
28-
used_rectangles::Vector{Rect2D{T}}
27+
free_rectangles::Vector{Rect2{T}}
28+
used_rectangles::Vector{Rect2{T}}
2929

3030
end
3131

@@ -34,12 +34,12 @@ function GuillotinePacker(width::T, height::T; merge::Bool=true,
3434
split_method::GuillotineSplitHeuristic=SplitMinimizeArea) where T
3535

3636
packer = GuillotinePacker(width, height, merge, rect_choice, split_method,
37-
Rect2D{T}[], Rect2D{T}[])
38-
push!(packer.free_rectangles, Rect2D(T(0), T(0), width, height))
37+
Rect2{T}[], Rect2{T}[])
38+
push!(packer.free_rectangles, Rect2(T(0), T(0), width, height))
3939
return packer
4040
end
4141

42-
function Base.push!(packer::GuillotinePacker, rects::AbstractVector{Rect2D{T}}) where T
42+
function Base.push!(packer::GuillotinePacker, rects::AbstractVector{Rect2{T}}) where T
4343
rects = copy(rects)
4444
free_rectangles = packer.free_rectangles
4545
used_rectangles = packer.used_rectangles
@@ -104,9 +104,9 @@ function Base.push!(packer::GuillotinePacker, rects::AbstractVector{Rect2D{T}})
104104
# Otherwise, we're good to go and do the actual packing.
105105
xy = minimum(free_rectangles[best_free_rect])
106106
if best_flipped
107-
new_node = Rect2D(xy, reverse(widths(rects[best_rect])))
107+
new_node = Rect2(xy, reverse(widths(rects[best_rect])))
108108
else
109-
new_node = Rect2D(xy, widths(rects[best_rect]))
109+
new_node = Rect2(xy, widths(rects[best_rect]))
110110
end
111111

112112
# Remove the free space we lost in the bin.
@@ -126,7 +126,7 @@ function Base.push!(packer::GuillotinePacker, rects::AbstractVector{Rect2D{T}})
126126
end
127127
end
128128

129-
function Base.push!(packer::GuillotinePacker, rect::Rect2D)
129+
function Base.push!(packer::GuillotinePacker, rect::Rect2)
130130
w, h = widths(rect)
131131
free_rectangles = packer.free_rectangles
132132
used_rectangles = packer.used_rectangles
@@ -164,7 +164,7 @@ function Occupancy(packer::GuillotinePacker)
164164
end
165165

166166
#/ Returns the heuristic score value for placing a rectangle of size width*height into free_rect. Does not try to rotate.
167-
function score_by_heuristic(width, height, free_rect::Rect2D, rect_choice::FreeRectChoiceHeuristic)
167+
function score_by_heuristic(width, height, free_rect::Rect2, rect_choice::FreeRectChoiceHeuristic)
168168
rect_choice == RectBestAreaFit && return ScoreBestAreaFit(width, height, free_rect)
169169
rect_choice == RectBestShortSideFit && return ScoreBestShortSideFit(width, height, free_rect)
170170
rect_choice == RectBestLongSideFit && return ScoreBestLongSideFit(width, height, free_rect)
@@ -173,69 +173,69 @@ function score_by_heuristic(width, height, free_rect::Rect2D, rect_choice::FreeR
173173
rect_choice == RectWorstLongSideFit && return ScoreWorstLongSideFit(width, height, free_rect)
174174
end
175175

176-
function ScoreBestAreaFit(w, h, free_rect::Rect2D)
176+
function ScoreBestAreaFit(w, h, free_rect::Rect2)
177177
return width(free_rect) * height(free_rect) - w * h
178178
end
179179

180-
function ScoreBestShortSideFit(w, h, free_rect::Rect2D)
180+
function ScoreBestShortSideFit(w, h, free_rect::Rect2)
181181
leftoverHoriz = abs(width(free_rect) - w)
182182
leftoverVert = abs(height(free_rect) - h)
183183
leftover = min(leftoverHoriz, leftoverVert)
184184
return leftover
185185
end
186186

187-
function ScoreBestLongSideFit(w, h, free_rect::Rect2D)
187+
function ScoreBestLongSideFit(w, h, free_rect::Rect2)
188188
leftoverHoriz = abs(width(free_rect) - w)
189189
leftoverVert = abs(height(free_rect) - h)
190190
leftover = max(leftoverHoriz, leftoverVert)
191191
return leftover
192192

193193
end
194194

195-
function ScoreWorstAreaFit(width, height, free_rect::Rect2D)
195+
function ScoreWorstAreaFit(width, height, free_rect::Rect2)
196196
return -ScoreBestAreaFit(width, height, free_rect)
197197
end
198198

199-
function ScoreWorstShortSideFit(width, height, free_rect::Rect2D)
199+
function ScoreWorstShortSideFit(width, height, free_rect::Rect2)
200200
return -ScoreBestShortSideFit(width, height, free_rect)
201201
end
202202

203-
function ScoreWorstLongSideFit(width, height, free_rect::Rect2D)
203+
function ScoreWorstLongSideFit(width, height, free_rect::Rect2)
204204
return -ScoreBestLongSideFit(width, height, free_rect)
205205
end
206206

207207
function FindPositionForNewNode(packer::GuillotinePacker, w, h, rect_choice::FreeRectChoiceHeuristic)
208208
free_rectangles = packer.free_rectangles
209-
best_node = Rect2D(0,0,0,0)
209+
best_node = Rect2(0,0,0,0)
210210
free_node_idx = 0
211211
best_score = typemax(Int)
212212
#/ Try each free rectangle to find the best one for placement.
213213
for i in 1:length(free_rectangles)
214214
rect_i = free_rectangles[i]
215215
# If this is a perfect fit upright, choose it immediately.
216216
if (w == width(rect_i) && h == height(rect_i))
217-
best_node = Rect2D(minimum(rect_i), Vec(w, h))
217+
best_node = Rect2(minimum(rect_i), Vec(w, h))
218218
free_node_idx = i
219219
break
220220
# If this is a perfect fit sideways, choose it.
221221
elseif (h == width(rect_i) && w == height(rect_i))
222-
best_node = Rect2D(minimum(rect_i), Vec(h, w))
222+
best_node = Rect2(minimum(rect_i), Vec(h, w))
223223
best_score = typemin(Int)
224224
free_node_idx = i
225225
break
226226
# Does the rectangle fit upright?
227227
elseif (w <= width(rect_i) && h <= height(rect_i))
228228
score = score_by_heuristic(w, h, rect_i, rect_choice)
229229
if (score < best_score)
230-
best_node = Rect2D(minimum(rect_i), Vec(w, h))
230+
best_node = Rect2(minimum(rect_i), Vec(w, h))
231231
best_score = score
232232
free_node_idx = i
233233
end
234234
# Does the rectangle fit sideways?
235235
elseif (h <= width(rect_i) && w <= height(rect_i))
236236
score = score_by_heuristic(h, w, rect_i, rect_choice)
237237
if (score < best_score)
238-
best_node = Rect2D(minimum(rect_i), Vec(h, w))
238+
best_node = Rect2(minimum(rect_i), Vec(h, w))
239239
best_score = score
240240
free_node_idx = i
241241
end
@@ -244,7 +244,7 @@ function FindPositionForNewNode(packer::GuillotinePacker, w, h, rect_choice::Fre
244244
return best_node, free_node_idx
245245
end
246246

247-
function split_freerect_by_heuristic(packer::GuillotinePacker, free_rect::Rect2D, placed_rect::Rect2D, method::GuillotineSplitHeuristic)
247+
function split_freerect_by_heuristic(packer::GuillotinePacker, free_rect::Rect2, placed_rect::Rect2, method::GuillotineSplitHeuristic)
248248
# Compute the lengths of the leftover area.
249249
w, h = widths(free_rect) .- widths(placed_rect)
250250

@@ -283,16 +283,16 @@ end
283283

284284
#/ This function will add the two generated rectangles into the free_rectangles array. The caller is expected to
285285
#/ remove the original rectangle from the free_rectangles array after that.
286-
function split_freerect_along_axis(packer::GuillotinePacker, free_rect::Rect2D, placed_rect::Rect2D, split_horizontal::Bool)
286+
function split_freerect_along_axis(packer::GuillotinePacker, free_rect::Rect2, placed_rect::Rect2, split_horizontal::Bool)
287287
free_rectangles = packer.free_rectangles
288288
# Form the two new rectangles.
289289
x, y = minimum(free_rect)
290290
if split_horizontal
291-
bottom = Rect2D(x, y + height(placed_rect), width(free_rect), height(free_rect) - height(placed_rect))
292-
right = Rect2D(x + width(placed_rect), y, width(free_rect) - width(placed_rect), height(placed_rect))
291+
bottom = Rect2(x, y + height(placed_rect), width(free_rect), height(free_rect) - height(placed_rect))
292+
right = Rect2(x + width(placed_rect), y, width(free_rect) - width(placed_rect), height(placed_rect))
293293
else # Split vertically
294-
bottom = Rect2D(x, y + height(placed_rect), width(placed_rect), height(free_rect) - height(placed_rect))
295-
right = Rect2D(x + width(placed_rect), y, width(free_rect) - width(placed_rect), height(free_rect))
294+
bottom = Rect2(x, y + height(placed_rect), width(placed_rect), height(free_rect) - height(placed_rect))
295+
right = Rect2(x + width(placed_rect), y, width(free_rect) - width(placed_rect), height(free_rect))
296296
end
297297

298298
# Add the new rectangles into the free rectangle pool if they weren't degenerate.
@@ -318,25 +318,25 @@ function MergeFreeList(packer::GuillotinePacker)
318318
if (minimum(rect_i)[2] == maximum(free_rectangles[j])[2])
319319
new_y = minimum(rect_i)[2] - height(free_rectangles[j])
320320
new_h = height(rect_i) + height(free_rectangles[j])
321-
free_rectangles[i] = Rect2D(minimum(rect_i)[1], new_y, Vec(width(rect_i), new_h))
321+
free_rectangles[i] = Rect2(minimum(rect_i)[1], new_y, Vec(width(rect_i), new_h))
322322
splice!(free_rectangles, j)
323323
j -= 1
324324
elseif (maximum(rect_i)[2] == minimum(free_rectangles[j])[2])
325325
new_h = height(rect_i) + height(free_rectangles[j])
326-
free_rectangles[i] = Rect2D(minimum(rect_i), Vec(width(rect_i), new_h))
326+
free_rectangles[i] = Rect2(minimum(rect_i), Vec(width(rect_i), new_h))
327327
splice!(free_rectangles, j)
328328
j -= 1
329329
end
330330
elseif (height(rect_i) == height(free_rectangles[j]) && minimum(rect_i)[2] == minimum(free_rectangles[j])[2])
331331
if (minimum(rect_i)[1] == minimum(free_rectangles[j])[1] + width(free_rectangles[j]))
332332
new_x = minimum(rect_i)[1] - width(free_rectangles[j])
333333
new_w = width(rect_i) + width(free_rectangles[j])
334-
free_rectangles[i] = Rect2D(new_x, minimum(rect_i)[2], new_w, height(rect_i))
334+
free_rectangles[i] = Rect2(new_x, minimum(rect_i)[2], new_w, height(rect_i))
335335
splice!(free_rectangles, j)
336336
j -= 1
337337
elseif maximum(rect_i)[1] == minimum(free_rectangles[j])[1]
338338
new_w = Vec(width(rect_i) + width(free_rectangles[j]), height(rect_i))
339-
free_rectangles[i] = Rect2D(minimum(rect_i), new_w)
339+
free_rectangles[i] = Rect2(minimum(rect_i), new_w)
340340
splice!(free_rectangles, j)
341341
j -= 1
342342
end

src/rectangle.jl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@ end
99

1010
mutable struct RectanglePacker{T}
1111
children::BinaryNode{RectanglePacker{T}}
12-
area::Rect2D{T}
12+
area::Rect2{T}
1313
end
1414

1515
left(a::RectanglePacker) = a.children.left
1616
left(a::RectanglePacker{T}, r::RectanglePacker{T}) where {T} = (a.children.left = r)
1717
right(a::RectanglePacker) = a.children.right
1818
right(a::RectanglePacker{T}, r::RectanglePacker{T}) where {T} = (a.children.right = r)
19-
RectanglePacker(area::Rect2D{T}) where {T} = RectanglePacker{T}(BinaryNode{RectanglePacker{T}}(), area)
19+
RectanglePacker(area::Rect2{T}) where {T} = RectanglePacker{T}(BinaryNode{RectanglePacker{T}}(), area)
2020
isleaf(a::RectanglePacker) = (a.children.left) == nothing && (a.children.right == nothing)
2121
# This is rather append, but it seems odd to use another function here.
2222
# Maybe its a bad idea, to call it push regardless!?
23-
function Base.push!(node::RectanglePacker{T}, areas::Vector{Rect2D{T}}) where T
23+
function Base.push!(node::RectanglePacker{T}, areas::Vector{Rect2{T}}) where T
2424
sort!(areas, by=GeometryBasics.norm widths)
2525
return RectanglePacker{T}[push!(node, area) for area in areas]
2626
end
2727

28-
function Base.push!(node::RectanglePacker{T}, area::Rect2D{T}) where T
28+
function Base.push!(node::RectanglePacker{T}, area::Rect2{T}) where T
2929
if !isleaf(node)
3030
l = push!(left(node), area)
3131
l !== nothing && return l
@@ -41,9 +41,9 @@ function Base.push!(node::RectanglePacker{T}, area::Rect2D{T}) where T
4141
oax,oay,oaxw,oayh = xmin + neww, ymin, xmax, ymin + newh
4242
nax,nay,naxw,nayh = xmin, ymin + newh, xmax, ymax
4343
rax,ray,raxw,rayh = xmin, ymin, xmin + neww, ymin + newh
44-
left(node, RectanglePacker(Rect2D(oax, oay, oaxw - oax, oayh - oay)))
45-
right(node, RectanglePacker(Rect2D(nax, nay, naxw - nax, nayh - nay)))
46-
return RectanglePacker(Rect2D(rax, ray, raxw - rax, rayh - ray))
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))
4747
end
4848
return nothing
4949
end

test/guillotine.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ push!(packer, rects)
44
push!(packer, Rect(0, 0, 100, 100))
55

66
# using Makie
7-
# linesegments(FRect(0, 0, 1024, 1024))
7+
# linesegments(Rectf(0, 0, 1024, 1024))
88
# poly!(packer.used_rectangles, color = (:red, 0.1), strokewidth=1, strokecolor=:black)

test/test_rectangle.jl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
root = RectanglePacker(Rect2D(0,0,1024,1024))
2-
push!(root, Rect2D(0,0,20,20))
3-
push!(root, Rect2D(0,0,20,20))
4-
push!(root, [Rect2D(0,0, rand(5:50), rand(5:50)) for i=1:20])
1+
root = RectanglePacker(Rect2(0,0,1024,1024))
2+
push!(root, Rect2(0,0,20,20))
3+
push!(root, Rect2(0,0,20,20))
4+
push!(root, [Rect2(0,0, rand(5:50), rand(5:50)) for i=1:20])
55

66

7-
# function get_rectangles(packer::Nothing, rectangles=IRect2D[])
7+
# function get_rectangles(packer::Nothing, rectangles=Rect2i[])
88
# return rectangles
99
# end
1010
#
@@ -15,14 +15,14 @@ push!(root, [Rect2D(0,0, rand(5:50), rand(5:50)) for i=1:20])
1515
# return rectangles
1616
# end
1717
# function get_rectangles(packer)
18-
# rectangles = IRect2D[]
18+
# rectangles = Rect2i[]
1919
# get_rectangles(packer.children.left, rectangles)
2020
# get_rectangles(packer.children.right, rectangles)
2121
# return rectangles
2222
# end
2323
#
2424
# rectangles = get_rectangles(root)
2525
# using Makie
26-
# linesegments(FRect(0, 0, 1024, 1024))
26+
# linesegments(Rectf(0, 0, 1024, 1024))
2727
# poly!(rectangles, color = (:red, 0.1), strokewidth=1, strokecolor=:black)
2828
#

0 commit comments

Comments
 (0)