Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
rez1dent3 committed Jul 12, 2024
1 parent fbc72f7 commit 002bafd
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 57 deletions.
8 changes: 2 additions & 6 deletions box.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package boxpacker3

import (
"slices"
)

// Box represents a box that can hold items.
//
// It has fields for the box's dimensions and maximum weight.
Expand Down Expand Up @@ -79,9 +75,9 @@ func NewBox(id string, w, h, d, mw float64) *Box {
height: h,
depth: d,
maxWeight: mw,
maxLength: slices.Max([]float64{w, h, d}),
maxLength: max(w, h, d),
volume: w * h * d,
items: nil,
items: make([]*Item, 0, 1),
}
}

Expand Down
102 changes: 51 additions & 51 deletions packer.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,10 @@ func NewPacker() *Packer {
// asynchronously and handle the context at the same time.
func (p *Packer) PackCtx(ctx context.Context, inputBoxes []*Box, inputItems []*Item) (*Result, error) {
// Create a channel to receive the result of the packing process.
result := make(chan *Result)
defer close(result)
result := make(chan *Result, 1)

// Start a goroutine to perform the packing process.
go func() {
// Perform the packing process.
result <- p.Pack(inputBoxes, inputItems)
}()
go func() { result <- p.Pack(inputBoxes, inputItems) }()

// Wait for the context to be done or the packing process to complete.
select {
Expand Down Expand Up @@ -118,44 +114,47 @@ func (p *Packer) Pack(inputBoxes []*Box, inputItems []*Item) *Result {
}

// If there are still items left, set them as unfit items.
if len(items) > 0 {
result.UnfitItems = append(result.UnfitItems, items...)
}
result.UnfitItems = append(result.UnfitItems, items...)

return result
}

// preferredSort selects the box with the largest volume and weight
// that can accommodate the items.
//
// This function calculates the maximum volume, weight, and maximum length of the items.
// It then iterates through the boxes and checks if a box can accommodate all the items.
// If a box is found that can accommodate the items, it is returned as the preferred box.
// The remaining boxes are sorted after the preferred box.
// If no box can accommodate the items, the original slice of boxes is returned.
//
// Parameters:
// - boxes: a slice of boxes.
// - items: a slice of items.
//
// Returns:
// - a slice of boxes sorted by volume, weight and maxLength.
// - a slice of boxes sorted by volume, weight, and maximum length.
// The first box in the slice is the preferred box.
// The remaining boxes are sorted after the preferred box.
// If there is no box that can accommodate the items, the original
// slice of boxes is returned.
func (p *Packer) preferredSort(boxes boxSlice, items itemSlice) boxSlice {
// Calculate the max volume, weight and maxLength of items.
volume := 0.0
weight := 0.0
maxLength := 0.0
// Calculate the max volume, weight, and maximum length of the items.
var volume, weight, maxLength float64

// Iterate through the items and calculate the total volume, weight, and maximum length.
for _, item := range items {
volume += item.GetVolume()
weight += item.GetWeight()

if maxLength < item.maxLength {
maxLength = item.maxLength
}
maxLength = max(maxLength, item.maxLength)
}

// Find the preferred box
for i, b := range boxes {
// Check if the box can accommodate the items.
// A box can accommodate the items if its volume is greater than or equal to the total volume of the items,
// its maximum weight is greater than or equal to the total weight of the items,
// and its maximum length is greater than or equal to the maximum length of the items.
if b.volume >= volume && b.maxWeight >= weight && b.maxLength >= maxLength {
result := make(boxSlice, 0, len(boxes))

Expand All @@ -171,10 +170,10 @@ func (p *Packer) preferredSort(boxes boxSlice, items itemSlice) boxSlice {

// packToBox Packs goods in a box b. Returns unpackaged goods.
//
// It attempts to pack items into the box. If the box is not big enough
// to accommodate all the items, it tries to pack the items again
// into the box by rotating the box and items.
// If the box is still not big enough, it adds the items to the unpacked slice.
// PackToBox attempts to pack items into the box. If the box is not big enough
// to accommodate all the items, it tries to pack the items again into the box
// by rotating the box and items. If the box is still not big enough, it adds
// the items to the unpacked slice.
//
// Parameters:
// - b: the box to pack items into.
Expand All @@ -189,67 +188,68 @@ func (p *Packer) packToBox(b *Box, items []*Item) []*Item {

unpacked := make([]*Item, 0, len(items))
pv := Pivot{}
index := 0

// if box is empty, first item is put in box
if b.items == nil && len(items) > 0 && b.PutItem(items[0], pv) {
items = items[1:]
// If box is empty, first item is put in box
if b.items == nil && len(items) > 0 && b.PutItem(items[index], pv) {
index++
}

// try to pack all items into the box
for _, i := range items {
// Try to pack all items into the box
for i := index; i < len(items); i++ {
fitted = false

// for each item already in the box
// Try to put item into the box
for j := range b.items {
dimension := b.items[j].GetDimension()

// for each axis
// Try to put item in each axis
for _, axis := range []Axis{WidthAxis, HeightAxis, DepthAxis} {
// calculate pivot position
// Calculate pivot position
pv[WidthAxis] = b.items[j].position[WidthAxis]
pv[HeightAxis] = b.items[j].position[HeightAxis]
pv[DepthAxis] = b.items[j].position[DepthAxis]

// add item dimension to pivot position
// Add item dimension to pivot position
pv[axis] += dimension[axis]

// if item can be put in the box
if b.PutItem(i, pv) {
// If item can be put in the box
if b.PutItem(items[i], pv) {
fitted = true

break
}
}
}

// if item can not be put in the box
// If item cannot be put in the box
if !fitted {
// make a backup of box
// Make a backup of box
backup := CopyPtr(b)
copyItems := CopySlicePtr(b.items)

// clean box
// Clean box
b.Reset()

// try to put item into the box
if b.PutItem(i, Pivot{}) {
// count of items fit in the box
// Try to put item into the box
if b.PutItem(items[i], Pivot{}) {
// Count of items fit in the box
itemsFit := 0

// for each item that was in the box
// Try to put each item in the box
for k := 0; k < len(copyItems) && itemsFit < len(copyItems); k++ {
// try to put item in the box
// Try to put item in the box
for j := len(b.items) - 1; j >= 0; j-- {
dimension := b.items[j].GetDimension()

// for each axis
// Try to put item in each axis
for _, pt := range []Axis{WidthAxis, HeightAxis, DepthAxis} {
// calculate pivot position
// Calculate pivot position
pv[WidthAxis] = b.items[j].position[WidthAxis]
pv[HeightAxis] = b.items[j].position[HeightAxis]
pv[DepthAxis] = b.items[j].position[DepthAxis]

// add item dimension to pivot position
// Add item dimension to pivot position
switch pt {
case WidthAxis:
pv[WidthAxis] += dimension[WidthAxis]
Expand All @@ -259,7 +259,7 @@ func (p *Packer) packToBox(b *Box, items []*Item) []*Item {
pv[DepthAxis] += dimension[DepthAxis]
}

// if item can be put in the box
// If item can be put in the box
if b.PutItem(copyItems[k], pv) {
itemsFit++

Expand All @@ -269,21 +269,21 @@ func (p *Packer) packToBox(b *Box, items []*Item) []*Item {
}
}

// if all items that were in the box now fit in the box
// If all items that were in the box now fit in the box
fitted = itemsFit == len(copyItems)
}

// if not all items that were in the box now fit in the box
// If not all items that were in the box now fit in the box
if !fitted {
// restore box from backup
// Restore box from backup
*b = *backup
}
}

// if item can not be put in the box
// If item cannot be put in the box
if !fitted {
// add item to unpacked slice
unpacked = append(unpacked, i)
// Add item to unpacked slice
unpacked = append(unpacked, items[i])
}
}

Expand Down

0 comments on commit 002bafd

Please sign in to comment.