diff --git a/box.go b/box.go index ca5c472..a127c85 100644 --- a/box.go +++ b/box.go @@ -76,11 +76,10 @@ func (b *Box) GetItems() []*Item { // PutItem Attempts to place an element at anchor point p of box b. func (b *Box) PutItem(item *Item, p Pivot) bool { - if !b.canTryToPlace(item) { + if !b.canQuota(item) { return false } - fit := false item.position = p for rt := RotationTypeWhd; rt <= RotationTypeWdh; rt++ { @@ -91,34 +90,22 @@ func (b *Box) PutItem(item *Item, p Pivot) bool { continue } - fit = true - for _, ib := range b.items { if ib.Intersect(item) { return false } } - if fit { - b.insert(item) + b.insert(item) - break - } + return true } - return fit + return false } -func (b *Box) canTryToPlace(item *Item) bool { - if b.itemsVolume+item.volume > b.volume { - return false - } - - if b.itemsWeight+item.weight > b.maxWeight { - return false - } - - return true +func (b *Box) canQuota(item *Item) bool { + return b.itemsVolume+item.volume <= b.volume && b.itemsWeight+item.weight <= b.maxWeight } func (b *Box) insert(item *Item) { @@ -128,7 +115,7 @@ func (b *Box) insert(item *Item) { } func (b *Box) purge() { - b.items = b.items[:0] // keep allocated memory + b.items = make([]*Item, 0, len(b.items)) b.itemsVolume = 0 b.itemsWeight = 0 } diff --git a/go.mod b/go.mod index 937aae4..429ddca 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/bavix/boxpacker3 go 1.20 require ( + github.com/g3n/engine v0.2.0 github.com/google/uuid v1.3.0 github.com/stretchr/testify v1.8.4 golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b @@ -10,6 +11,10 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2ffca3b..b1a79d1 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/g3n/engine v0.2.0 h1:7dmj4c+3xHcBnYrVmRuVf/oZ2JycxJU9Y+2FQj1Af2Y= +github.com/g3n/engine v0.2.0/go.mod h1:rnj8jiLdKEDI8VbveKhmdL4rovjjy+uxNP5YROg2x8g= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb h1:T6gaWBvRzJjuOrdCtg8fXXjKai2xSDqWTcKFUPuw8Tw= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -8,7 +14,13 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI= golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9 h1:D0iM1dTCbD5Dg1CbuvLC/v/agLc79efSj/L35Q3Vqhs= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/visualize3d/main.go b/internal/visualize3d/main.go new file mode 100644 index 0000000..3f8233a --- /dev/null +++ b/internal/visualize3d/main.go @@ -0,0 +1,127 @@ +//nolint:all +package main + +import ( + "math/rand" + "time" + + "github.com/g3n/engine/app" + "github.com/g3n/engine/camera" + "github.com/g3n/engine/core" + "github.com/g3n/engine/geometry" + "github.com/g3n/engine/gls" + "github.com/g3n/engine/graphic" + "github.com/g3n/engine/light" + "github.com/g3n/engine/material" + "github.com/g3n/engine/math32" + "github.com/g3n/engine/renderer" + "github.com/google/uuid" + + "github.com/bavix/boxpacker3" +) + +const ( + BoxTypeF = "8ec81501-11a4-4b3f-9a52-7cd2f9c8370c" + BoxTypeE = "9c69baf8-1ca3-46a0-9fc2-6f15ad9fef9a" + BoxTypeG = "2c5279d3-48ad-451b-b673-f6d9be7fc6f6" + BoxTypeC = "7f1cc68f-d554-4094-8734-c68df5c13154" + BoxTypeB = "76cede41-86bb-4487-bfb0-9513f032d53e" + BoxTypeA = "8e10cebf-cee6-4136-b060-1587b993d083" + BoxTypeStd = "ba973206-aa64-493b-b37a-c53192cde8fd" + BoxTypeNotStd1 = "cb1ed5b8-7405-48c5-bfd0-d86f75c99261" + BoxTypeNotStd2 = "d91e2661-aebb-4a55-bfb5-4ff9c6e3c008" + BoxTypeNotStd3 = "a0ecd730-375a-4313-bbe8-820710606b3d" + BoxTypeNotStd4 = "6dff37f0-4dd1-4143-abdc-c19ab94f2e68" + BoxTypeNotStd5 = "abac6d59-b51f-4d62-a338-42aca7afe1cc" + BoxTypeNotStd6 = "981ffb30-a7b9-4d9e-820e-04de2145763e" +) + +func NewDefaultBoxList() []*boxpacker3.Box { + return []*boxpacker3.Box{ + boxpacker3.NewBox(BoxTypeF, 220, 185, 50, 20000), // 0 + boxpacker3.NewBox(BoxTypeE, 165, 215, 100, 20000), // 1 + boxpacker3.NewBox(BoxTypeG, 265, 165, 190, 20000), // 2 + boxpacker3.NewBox(BoxTypeC, 425, 165, 190, 20000), // 3 + boxpacker3.NewBox(BoxTypeB, 425, 265, 190, 20000), // 4 + boxpacker3.NewBox(BoxTypeA, 425, 265, 380, 20000), // 5 + boxpacker3.NewBox(BoxTypeStd, 530, 380, 265, 20000), // 6 + boxpacker3.NewBox(BoxTypeNotStd1, 1000, 500, 500, 20000), // 7 + boxpacker3.NewBox(BoxTypeNotStd2, 1000, 1000, 1000, 20000), // 8 + boxpacker3.NewBox(BoxTypeNotStd3, 2000, 500, 500, 20000), // 9 + boxpacker3.NewBox(BoxTypeNotStd4, 2000, 2000, 2000, 20000), // 10 + boxpacker3.NewBox(BoxTypeNotStd5, 2500, 2500, 2500, 20000), // 11 + boxpacker3.NewBox(BoxTypeNotStd6, 3000, 3000, 3000, 20000), // 12 + } +} + +func main() { + a := app.App() + scene := core.NewNode() + + cam := camera.NewPerspective(1, 1, 3000, 60, camera.Vertical) + cam.SetPosition(0, 0, 3) + scene.Add(cam) + camera.NewOrbitControl(cam) + + packer := boxpacker3.NewPacker() + + boxes := NewDefaultBoxList() + items := []*boxpacker3.Item{ + // 5 + boxpacker3.NewItem(uuid.New().String(), 100, 100, 5, 2690), + boxpacker3.NewItem(uuid.New().String(), 100, 5, 100, 2690), + boxpacker3.NewItem(uuid.New().String(), 5, 100, 100, 2690), + boxpacker3.NewItem(uuid.New().String(), 5, 100, 100, 2690), + boxpacker3.NewItem(uuid.New().String(), 5, 100, 100, 2690), + boxpacker3.NewItem(uuid.New().String(), 5, 100, 100, 2690), + + // 35 + boxpacker3.NewItem(uuid.New().String(), 35, 100, 100, 2690), + boxpacker3.NewItem(uuid.New().String(), 35, 100, 100, 2690), + } + + packResult, _ := packer.Pack(boxes, items) + + for _, box := range packResult.Boxes { + if len(box.GetItems()) == 0 { + continue + } + + boxGeo := geometry.NewBox(float32(box.GetWidth()), float32(box.GetHeight()), float32(box.GetDepth())) + mat := material.NewStandard(math32.NewColor("DarkGreen")) + mat.SetOpacity(0.25) + boxMesh := graphic.NewMesh(boxGeo, mat) + boxMesh.SetPosition(0, 0, 0) + scene.Add(boxMesh) + + for _, item := range box.GetItems() { + dimension := item.GetDimension() + + itemGeo := geometry.NewBox(float32(dimension[0]), float32(dimension[1]), float32(dimension[2])) + mat := material.NewStandard(math32.NewColorHex(uint(rand.Uint32()))) + itemMesh := graphic.NewMesh(itemGeo, mat) + mat.SetOpacity(0.7) + + itemMesh.SetPosition( + float32(item.GetPosition()[boxpacker3.WidthAxis])-float32(box.GetWidth())/2+float32(dimension[0])/2, + float32(item.GetPosition()[boxpacker3.HeightAxis])-float32(box.GetHeight())/2+float32(dimension[1])/2, + float32(item.GetPosition()[boxpacker3.DepthAxis])-float32(box.GetDepth())/2+float32(dimension[2])/2) + + scene.Add(itemMesh) + } + + break + } + + scene.Add(light.NewAmbient(&math32.Color{1.0, 1.0, 1.0}, 0.8)) + pointLight := light.NewPoint(&math32.Color{1, 1, 0}, 5.0) + pointLight.SetPosition(1, 0, 2) + scene.Add(pointLight) + + a.Gls().ClearColor(0.5, 0.5, 0.5, 1.0) + + a.Run(func(renderer *renderer.Renderer, deltaTime time.Duration) { + a.Gls().Clear(gls.DEPTH_BUFFER_BIT | gls.STENCIL_BUFFER_BIT | gls.COLOR_BUFFER_BIT) + renderer.Render(scene, cam) + }) +} diff --git a/item.go b/item.go index 49ecf53..d7961d4 100644 --- a/item.go +++ b/item.go @@ -26,7 +26,7 @@ func (it itemSlice) Len() int { } func (it itemSlice) Less(i, j int) bool { - return it[i].GetVolume() < it[j].GetVolume() + return it[i].volume < it[j].volume } func (it itemSlice) Swap(i, j int) { diff --git a/packer.go b/packer.go index 6d10d75..943e7b0 100644 --- a/packer.go +++ b/packer.go @@ -59,7 +59,7 @@ func (p *Packer) preferredSort(boxes boxSlice, items itemSlice) boxSlice { for i, b := range boxes { if b.volume >= volume && b.maxWeight >= weight && b.maxLength >= maxLength { - return append(boxSlice{b}, slices.Delete(boxes, i, i)...) + return append(boxSlice{b}, slices.Delete(boxes, i, i+1)...) } } @@ -94,18 +94,14 @@ func (p *Packer) packToBox(b *Box, items []*Item) []*Item { switch pt { case WidthAxis: - pv[WidthAxis] += +dimension[WidthAxis] + pv[WidthAxis] += dimension[WidthAxis] case HeightAxis: pv[HeightAxis] += dimension[HeightAxis] case DepthAxis: pv[DepthAxis] += dimension[DepthAxis] } - if b.PutItem(i, pv) { - fitted = true - - break - } + fitted = b.PutItem(i, pv) } } @@ -131,7 +127,7 @@ func (p *Packer) packToBox(b *Box, items []*Item) []*Item { switch pt { case WidthAxis: - pv[WidthAxis] += +dimension[WidthAxis] + pv[WidthAxis] += dimension[WidthAxis] case HeightAxis: pv[HeightAxis] += dimension[HeightAxis] case DepthAxis: