Skip to content

Commit

Permalink
Merge pull request #1 from bavix/develop
Browse files Browse the repository at this point in the history
boxpacker3 init
  • Loading branch information
rez1dent3 authored Aug 2, 2023
2 parents cbc153b + f36b857 commit 953a682
Show file tree
Hide file tree
Showing 11 changed files with 782 additions and 1 deletion.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/

# Go workspace file
go.work

.idea/
39 changes: 39 additions & 0 deletions bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package boxpacker3_test

import (
rand2 "crypto/rand"
"math/big"
"testing"

"github.com/google/uuid"

"github.com/bavix/boxpacker3"
)

func BenchmarkPacker(b *testing.B) {
items := make(boxpacker3.ItemSlice, 0, 100)

for x := 0; x < 100; x++ {
w, _ := rand2.Int(rand2.Reader, big.NewInt(150))
l, _ := rand2.Int(rand2.Reader, big.NewInt(150))
h, _ := rand2.Int(rand2.Reader, big.NewInt(150))
w2, _ := rand2.Int(rand2.Reader, big.NewInt(100))

items = append(items, boxpacker3.NewItem(
uuid.New().String(),
float64(w.Int64()),
float64(l.Int64()),
float64(h.Int64()),
float64(w2.Int64()),
))
}

boxes := NewDefaultBoxList()
packer := boxpacker3.NewPacker()

b.ResetTimer()

for i := 0; i < b.N; i++ {
_, _ = packer.Pack(boxes, items)
}
}
114 changes: 114 additions & 0 deletions box.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package boxpacker3

type Box struct {
ID string
Width float64
Height float64
Depth float64
MaxWeight float64
Volume float64
Items []*Item

itemsVolume float64
itemsWeight float64
}

type BoxSlice []*Box

func (bs BoxSlice) Len() int {
return len(bs)
}

func (bs BoxSlice) Less(i, j int) bool {
return bs[i].GetVolume() < bs[j].GetVolume()
}

func (bs BoxSlice) Swap(i, j int) {
bs[i], bs[j] = bs[j], bs[i]
}

func NewBox(id string, w, h, d, mw float64) *Box {
return &Box{
ID: id,
Width: w,
Height: h,
Depth: d,
MaxWeight: mw,
Volume: w * h * d,
Items: make([]*Item, 0),
}
}

func (b *Box) GetID() string {
return b.ID
}

func (b *Box) GetWidth() float64 {
return b.Width
}

func (b *Box) GetHeight() float64 {
return b.Height
}

func (b *Box) GetDepth() float64 {
return b.Depth
}

func (b *Box) GetVolume() float64 {
return b.Volume
}

func (b *Box) GetMaxWeight() float64 {
return b.MaxWeight
}

// PutItem Пытается поместить элемент в опорную точку p коробки b.
func (b *Box) PutItem(item *Item, p Pivot) bool {
fit := false

if b.itemsVolume+item.GetVolume() > b.GetVolume() {
return false
}

if b.itemsWeight+item.GetWeight() > b.GetMaxWeight() {
return false
}

item.Position = p
for rt := RotationTypeWhd; rt <= RotationTypeWdh; rt++ {
item.RotationType = rt
d := item.GetDimension()

if b.GetWidth() < p[WidthAxis]+d[WidthAxis] || b.GetHeight() < p[HeightAxis]+d[HeightAxis] || b.GetDepth() < p[DepthAxis]+d[DepthAxis] {
continue
}

fit = true

for _, ib := range b.Items {
if ib.Intersect(item) {
fit = false

break
}
}

if fit {
b.insert(item)

return fit
}

break
}

return fit
}

func (b *Box) insert(item *Item) {
b.Items = append(b.Items, item)

b.itemsVolume += item.GetVolume()
b.itemsWeight += item.GetWeight()
}
16 changes: 16 additions & 0 deletions clone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package boxpacker3

func copyPtr[T any](original *T) *T {
copyOfValue := *original

return &copyOfValue
}

func copySlicePtr[T any](data []*T) []*T {
result := make([]*T, len(data))
for i := range data {
result[i] = copyPtr(data[i])
}

return result
}
33 changes: 33 additions & 0 deletions clone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package boxpacker3

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestCopyPtr(t *testing.T) {
a := 1
b := struct{ a int }{}

require.Same(t, &a, &a)
require.Same(t, &b, &b)
require.NotSame(t, &a, copyPtr(&a))
require.NotSame(t, &b, copyPtr(&b))
}

func TestCopySlicePtr(t *testing.T) {
a := struct{ a int }{}
b := struct{ a int }{}
c := []*struct{ a int }{&a, &b}

d := make([]*struct{ a int }, len(c))
copy(d, c)

e := copySlicePtr(c)

for i := range d {
require.Same(t, c[i], d[i])
require.NotSame(t, c[i], e[i])
}
}
25 changes: 25 additions & 0 deletions costs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package boxpacker3

type RotationType int

const (
RotationTypeWhd RotationType = iota
RotationTypeHwd
RotationTypeHdw
RotationTypeDhw
RotationTypeDwh
RotationTypeWdh
)

type Axis int

const (
WidthAxis Axis = iota
HeightAxis
DepthAxis
)

type (
Pivot [3]float64
Dimension [3]float64
)
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/bavix/boxpacker3

go 1.20

require (
github.com/google/uuid v1.3.0
github.com/stretchr/testify v1.8.4
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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/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=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
109 changes: 109 additions & 0 deletions item.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package boxpacker3

import (
"math"
)

type Item struct {
ID string
Width float64
Height float64
Depth float64
Weight float64
Volume float64

RotationType RotationType
Position Pivot
}

type ItemSlice []*Item

func (is ItemSlice) Len() int {
return len(is)
}

func (is ItemSlice) Less(i, j int) bool {
return is[i].GetVolume() < is[j].GetVolume()
}

func (is ItemSlice) Swap(i, j int) {
is[i], is[j] = is[j], is[i]
}

func NewItem(id string, w, h, d, wg float64) *Item {
return &Item{
ID: id,
Width: w,
Height: h,
Depth: d,
Weight: wg,
Volume: w * h * d,
}
}

func (i *Item) GetID() string {
return i.ID
}

func (i *Item) GetWidth() float64 {
return i.Width
}

func (i *Item) GetHeight() float64 {
return i.Height
}

func (i *Item) GetDepth() float64 {
return i.Depth
}

func (i *Item) GetVolume() float64 {
return i.Volume
}

func (i *Item) GetWeight() float64 {
return i.Weight
}

//nolint:nonamedreturns
func (i *Item) GetDimension() (d Dimension) {
switch i.RotationType {
case RotationTypeWhd:
d = Dimension{i.GetWidth(), i.GetHeight(), i.GetDepth()}
case RotationTypeHwd:
d = Dimension{i.GetHeight(), i.GetWidth(), i.GetDepth()}
case RotationTypeHdw:
d = Dimension{i.GetHeight(), i.GetDepth(), i.GetWidth()}
case RotationTypeDhw:
d = Dimension{i.GetDepth(), i.GetHeight(), i.GetWidth()}
case RotationTypeDwh:
d = Dimension{i.GetDepth(), i.GetWidth(), i.GetHeight()}
case RotationTypeWdh:
d = Dimension{i.GetWidth(), i.GetDepth(), i.GetHeight()}
}

return
}

// Intersect Проверяет пересечения между элементом i и элементом it.
func (i *Item) Intersect(it *Item) bool {
d1 := i.GetDimension()
d2 := it.GetDimension()

return rectIntersect(d1, d2, i, it, WidthAxis, HeightAxis) &&
rectIntersect(d1, d2, i, it, HeightAxis, DepthAxis) &&
rectIntersect(d1, d2, i, it, WidthAxis, DepthAxis)
}

// rectIntersect Проверяет пересекаются ли два прямоугольника от осей x и y элементов i1 и i2.
func rectIntersect(d1, d2 Dimension, i1, i2 *Item, x, y Axis) bool {
cx1 := i1.Position[x] + d1[x]/2 //nolint:gomnd
cy1 := i1.Position[y] + d1[y]/2 //nolint:gomnd
cx2 := i2.Position[x] + d2[x]/2 //nolint:gomnd
cy2 := i2.Position[y] + d2[y]/2 //nolint:gomnd

ix := math.Max(cx1, cx2) - math.Min(cx1, cx2)
iy := math.Max(cy1, cy2) - math.Min(cy1, cy2)

return ix < (d1[x]+d2[x])/2 && iy < (d1[y]+d2[y])/2
}
Loading

0 comments on commit 953a682

Please sign in to comment.