Skip to content

Commit

Permalink
dynamiccollisions example
Browse files Browse the repository at this point in the history
  • Loading branch information
guo-yong-zhi committed Dec 28, 2021
1 parent cbce0a9 commit 42a8e79
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[![CI](https://github.com/guo-yong-zhi/Stuffing.jl/actions/workflows/ci.yml/badge.svg)](https://github.com/guo-yong-zhi/Stuffing.jl/actions/workflows/ci.yml) [![CI-nightly](https://github.com/guo-yong-zhi/Stuffing.jl/actions/workflows/ci-nightly.yml/badge.svg)](https://github.com/guo-yong-zhi/Stuffing.jl/actions/workflows/ci-nightly.yml) [![codecov](https://codecov.io/gh/guo-yong-zhi/Stuffing.jl/branch/main/graph/badge.svg?token=43TOrL25V7)](https://codecov.io/gh/guo-yong-zhi/Stuffing.jl) [![DOI](https://zenodo.org/badge/349631351.svg)](https://zenodo.org/badge/latestdoi/349631351)
This's an algorithm for solving **2D irregular nesting problems** (also known as cutting problems or packing problems).
The algorithm accepts arbitrary **binary raster masks** as inputs and is good at handling the nesting problems of many gadgets. The implementation is based on quadtree & gradient optimization. Also, it can be parallelized if you start `julia` with `julia --threads k`. This package is used by [WordCloud.jl](https://github.com/guo-yong-zhi/WordCloud.jl).
Examples: [collision detection](./examples/collision.jl), [packing](./examples/packing.jl)
Examples: [collision detection](./examples/collision.jl), [dynamic collision detection](./examples/dynamiccollisions.jl), [packing](./examples/packing.jl)
Benchmark: [benchmark](https://github.com/guo-yong-zhi/WordCloud/blob/master/examples/benchmark.jl)
***
```
Expand Down
40 changes: 40 additions & 0 deletions examples/dynamiccollisions.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Stuffing
qts = Stuffing.randqtrees(300);
println("visualization:")
println(repr("text/plain", overlap(qts)))

println("""To get all collisions via `partialcollisions`, the set `updated` should contains all collided labels in last round.
And the labels of other updated objects should be contained too.
`partialcollisions` is faster than `totalcollisions` when `updated` is small.
""")
updated = Set(1:length(qts));
sptree = linked_spacial_qtree(qts);
for i in 1:10
C1 = partialcollisions(qts, sptree, updated);
union!(updated, first.(C1) |> Iterators.flatten) #all collided labels
C2 = QTrees.totalcollisions_native(qts);
@assert length(C1) == length(C2)
@assert Set(Set.(first.(C1))) == Set(Set.(first.(C2)))
Stuffing.Trainer.batchsteps!(qts, C1)
QTrees.shift!(qts[3], 1, 1, 1)
QTrees.shift!(qts[7], 1, -1, -1)
union!(updated, [3, 7]) #other updated labels
end
println("And things are similar for `dynamiccollisions`. ")
println("`dynamiccollisions` is faster than `partialcollisions` when `updated` is not that small.")
updated = UpdatedSet(length(qts));
sptree = linked_spacial_qtree(qts);
for i in 1:10
C1 = dynamiccollisions(qts, sptree, updated);
union!(updated, first.(C1) |> Iterators.flatten) #all collided labels
C2 = QTrees.totalcollisions_native(qts);
C3 = QTrees.totalcollisions(qts);
#sometimes C2!=C3. When objects are out of the scope, the `totalcollisions_native` will miss them.
#But `totalcollisions` may not (not promise).
@assert length(C1) == length(C2) || length(C1) == length(C3)
@assert Set(Set.(first.(C1))) == Set(Set.(first.(C2))) || Set(Set.(first.(C1))) == Set(Set.(first.(C3)))
Stuffing.Trainer.batchsteps!(qts, C1)
QTrees.shift!(qts[3], 1, -1, -1)
QTrees.shift!(qts[7], 1, 1, 1)
union!(updated, [3, 7]) #other updated labels
end
2 changes: 1 addition & 1 deletion src/Stuffing.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Stuffing
export qtree, maskqtree, qtrees, place!, overlap!, overlap, getpositions, setpositions!, packing, packing!
export QTrees, getshift, getcenter, setshift!, setcenter!, outofbounds, outofkernelbounds,
partialcollisions, totalcollisions, collision, findroom_uniform, findroom_gathering,
dynamiccollisions, partialcollisions, totalcollisions, collision, findroom_uniform, findroom_gathering,
UpdatedSet, locate!, linked_spacial_qtree, hash_spacial_qtree
export Trainer, train!, fit!, Momentum, SGD
include("linkedlist.jl")
Expand Down
2 changes: 1 addition & 1 deletion src/fit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function step_index!(qtrees, i1, i2, collisionpoint, optimiser)
end
end

function batchsteps!(qtrees, colist::Vector{QTrees.CoItem}, optimiser)
function batchsteps!(qtrees, colist::Vector{QTrees.CoItem}, optimiser=(t, Δ) -> Δ ./ 6)
for ((i1, i2), cp) in shuffle!(colist)
# @show cp
# @assert cp[1] > 0
Expand Down
29 changes: 28 additions & 1 deletion test/test_qtrees.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,34 @@ testqtree = Stuffing.testqtree
c1 = QTrees.partialcollisions(qts, sptree, Set(labels))
c2set = Set([Set(p) for p in first.(QTrees.totalcollisions(qts)) if !isdisjoint(p, labels)])
@test c2set == Set(Set.(first.(c1)))

# dynamic
updated = Set(1:length(qts));
sptree = linked_spacial_qtree(qts);
for i in 1:10
C1 = partialcollisions(qts, sptree, updated);
union!(updated, first.(C1) |> Iterators.flatten) #all collided labels
C2 = QTrees.totalcollisions_native(qts);
@test length(C1) == length(C2)
@test Set(Set.(first.(C1))) == Set(Set.(first.(C2)))
Stuffing.Trainer.batchsteps!(qts, C1)
QTrees.shift!(qts[3], 1, 1, 1)
QTrees.shift!(qts[7], 1, -1, -1)
union!(updated, [3, 7]) #other updated labels
end
updated = UpdatedSet(length(qts));
sptree = linked_spacial_qtree(qts);
for i in 1:10
C1 = dynamiccollisions(qts, sptree, updated);
union!(updated, first.(C1) |> Iterators.flatten) #all collided labels
C2 = QTrees.totalcollisions_native(qts);
C3 = QTrees.totalcollisions(qts);
@test length(C1) == length(C2) || length(C1) == length(C3)
@test Set(Set.(first.(C1))) == Set(Set.(first.(C2))) || Set(Set.(first.(C1))) == Set(Set.(first.(C3)))
Stuffing.Trainer.batchsteps!(qts, C1)
QTrees.shift!(qts[3], 1, -1, -1)
QTrees.shift!(qts[7], 1, 1, 1)
union!(updated, [3, 7]) #other updated labels
end
#edge cases
# batch
@test QTrees.totalcollisions_spacial(Vector{QTrees.U8SQTree}()) |> isempty
Expand Down

0 comments on commit 42a8e79

Please sign in to comment.