Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
rez1dent3 committed Jul 12, 2024
1 parent 85bbee8 commit c82ddbc
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 72 deletions.
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ updates:
schedule:
interval: daily
open-pull-requests-limit: 10
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ issues:
exclude-rules:
- path: (.+)_test.go
linters:
- gochecknoglobals
- dupl
3 changes: 2 additions & 1 deletion bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
func BenchmarkPacker(b *testing.B) {
items := make([]*boxpacker3.Item, 0, 100)

for range 100 {
for range cap(items) {
w, _ := rand.Int(rand.Reader, big.NewInt(150))
l, _ := rand.Int(rand.Reader, big.NewInt(150))
h, _ := rand.Int(rand.Reader, big.NewInt(150))
Expand All @@ -31,6 +31,7 @@ func BenchmarkPacker(b *testing.B) {
boxes := NewDefaultBoxList()
packer := boxpacker3.NewPacker()

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
Expand Down
158 changes: 138 additions & 20 deletions box.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,73 @@ import (
"slices"
)

// Box represents a box that can hold items.
//
// It has fields for the box's dimensions and maximum weight.
// It also has fields for tracking the box's current items and their volume and weight.
type Box struct {
id string
width float64
height float64
depth float64
// id is the box's unique identifier.
id string

// width is the box's width.
width float64

// height is the box's height.
height float64

// depth is the box's depth.
depth float64

// maxWeight is the maximum weight the box can hold.
maxWeight float64
volume float64
items []*Item

maxLength float64
// volume is the box's volume (width * height * depth).
volume float64

// items is a slice of items currently in the box.
items []*Item

// maxLength is the length of the box's longest side.
maxLength float64

// itemsVolume is the total volume of the items in the box.
itemsVolume float64

// itemsWeight is the total weight of the items in the box.
itemsWeight float64
}

// boxSlice is a slice of boxes.
//
// It implements the sort.Interface by defining Len, Less and Swap methods.
type boxSlice []*Box

// Len returns the length of the boxSlice.
func (bs boxSlice) Len() int {
return len(bs)
}

// Less compares two boxes by volume.
func (bs boxSlice) Less(i, j int) bool {
return bs[i].volume < bs[j].volume
}

// Swap swaps two boxes in the boxSlice.
func (bs boxSlice) Swap(i, j int) {
bs[i], bs[j] = bs[j], bs[i]
}

// NewBox creates a new Box with the given id, dimensions, and maximum weight.
//
// Parameters:
// - id: a unique identifier for the box.
// - w: the width of the box.
// - h: the height of the box.
// - d: the depth of the box.
// - mw: the maximum weight the box can hold.
//
// Returns:
// - A pointer to the newly created Box.
func NewBox(id string, w, h, d, mw float64) *Box {
//nolint:exhaustruct
return &Box{
Expand All @@ -46,76 +85,155 @@ func NewBox(id string, w, h, d, mw float64) *Box {
}
}

// GetID returns the unique identifier of the box.
func (b *Box) GetID() string {
return b.id
}

// GetWidth returns the width of the box.
func (b *Box) GetWidth() float64 {
return b.width
}

// GetHeight returns the height of the box.
func (b *Box) GetHeight() float64 {
return b.height
}

// GetDepth returns the depth of the box.
func (b *Box) GetDepth() float64 {
return b.depth
}

// GetVolume returns the volume of the box.
func (b *Box) GetVolume() float64 {
return b.volume
}

// GetMaxWeight returns the maximum weight the box can hold.
func (b *Box) GetMaxWeight() float64 {
return b.maxWeight
}

// GetItems returns a slice of pointers to the items currently in the box.
//
// The slice is a copy and not a reference to the original slice, so modifying
// the slice returned by this function will not affect the contents of the box.
func (b *Box) GetItems() []*Item {
return b.items
}

// PutItem Attempts to place an element at anchor point p of box b.
return append([]*Item(nil), b.items...)
}

// PutItem Attempts to place the given item at the specified anchor point within the box.
//
// Attempts to place the given item at the specified anchor point within the box.
//
// It tries to place the item at the given anchor point by iterating through each
// rotation type (Whd, Hwd, Hdw, Dhw, Dwh, Wdh) and checks if the item can be
// placed within the box without intersecting with any of the other items in the box.
// If the item can be placed, it inserts the item into the box and returns true.
// If the item cannot be placed, it returns false.
//
// Parameters:
// - item: The item to be placed in the box.
// - p: The anchor point at which to attempt placing the item within the box.
//
// Returns:
// - bool: True if the item was successfully placed within the box, false otherwise.
func (b *Box) PutItem(item *Item, p Pivot) bool {
// Check if the item can fit in the box based on volume and weight quotas.
if !b.canQuota(item) {
return false
}

// Set the item's position to the anchor point.
item.position = p

// Iterate through each rotation type to find a suitable placement.
for rt := RotationTypeWhd; rt <= RotationTypeWdh; rt++ {
// Set the item's rotation type to the current rotation type.
item.rotationType = rt
d := item.GetDimension()

if b.width < p[WidthAxis]+d[WidthAxis] || b.height < p[HeightAxis]+d[HeightAxis] || b.depth < p[DepthAxis]+d[DepthAxis] {
// Get the dimensions of the item in its current rotation type.
itemDimensions := item.GetDimension()

// Check if the box has enough dimensions to accommodate the item.
if b.width < p[WidthAxis]+itemDimensions[WidthAxis] ||
b.height < p[HeightAxis]+itemDimensions[HeightAxis] ||
b.depth < p[DepthAxis]+itemDimensions[DepthAxis] {
continue
}

for _, ib := range b.items {
if ib.Intersect(item) {
return false
}
// Check if the item intersects with any other items in the box.
if b.itemsIntersect(item) {
continue
}

// Insert the item into the box and return true.
b.insert(item)

return true
}

// If no suitable placement is found, return false.
return false
}

// itemsIntersect checks if any of the items in the box intersect with the given item.
// It iterates through each item in the box and calls the Intersect method on the item.
// If an intersection is found, it returns true.
// If no intersection is found, it returns false.
func (b *Box) itemsIntersect(item *Item) bool {
for _, ib := range b.items {
if ib.Intersect(item) {
return true
}
}

return false
}

// canQuota checks if the box can accommodate the given item based on both volume and weight quotas.
//
// It calls the canFitVolume and canFitWeight methods to check if the box has enough room for the
// item's volume and weight. If both conditions are true, it returns true. Otherwise, it returns false.
func (b *Box) canQuota(item *Item) bool {
return b.itemsVolume+item.volume <= b.volume && b.itemsWeight+item.weight <= b.maxWeight
return b.canFitVolume(item) && b.canFitWeight(item)
}

// canFitVolume checks if the box can accommodate the given item based on volume.
//
// It compares the sum of the item's volume and the current volume of items in the box
// to the box's total volume. If the sum is less than or equal to the box's total volume,
// it returns true. Otherwise, it returns false.
func (b *Box) canFitVolume(item *Item) bool {
return b.itemsVolume+item.volume <= b.volume
}

// canFitWeight checks if the box can accommodate the given item based on weight.
//
// It compares the sum of the item's weight and the current weight of items in the box
// to the box's maximum weight. If the sum is less than or equal to the box's maximum weight,
// it returns true. Otherwise, it returns false.
func (b *Box) canFitWeight(item *Item) bool {
return b.itemsWeight+item.weight <= b.maxWeight
}

// insert inserts an item into the box and updates the total volume and weight.
//
// It appends the item to the box's items slice and adds the item's volume and weight to the
// box's total volume and weight.
func (b *Box) insert(item *Item) {
b.items = append(b.items, item)
b.itemsVolume += item.volume
b.itemsWeight += item.weight
}

func (b *Box) purge() {
b.items = make([]*Item, 0, len(b.items))
// Reset clears the box and resets the volume and weight.
//
// It removes all items from the box by slicing the items slice to an empty slice.
// It sets the total volume and weight of items in the box to 0.
func (b *Box) Reset() {
b.items = b.items[:0]
b.itemsVolume = 0
b.itemsWeight = 0
}
20 changes: 16 additions & 4 deletions consts.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
package boxpacker3

// RotationType represents the type of rotation for an item.
type RotationType int

// RotationTypeWhd represents the rotation type where the width is the longest dimension.
const (
RotationTypeWhd RotationType = iota
// RotationTypeHwd represents the rotation type where the height is the longest dimension.
RotationTypeHwd
// RotationTypeHdw represents the rotation type where the depth is the longest dimension.
RotationTypeHdw
// RotationTypeDhw represents the rotation type where the depth is the longest dimension.
RotationTypeDhw
// RotationTypeDwh represents the rotation type where the width is the longest dimension.
RotationTypeDwh
// RotationTypeWdh represents the rotation type where the height is the longest dimension.
RotationTypeWdh
)

// Axis represents the axis of a dimension.
type Axis int

// WidthAxis represents the width axis.
const (
WidthAxis Axis = iota
// HeightAxis represents the height axis.
HeightAxis
// DepthAxis represents the depth axis.
DepthAxis
)

type (
Pivot [3]float64
Dimension [3]float64
)
// Pivot represents the position of an item within a box.
type Pivot [3]float64

// Dimension represents the dimensions of an item or a box.
type Dimension [3]float64
12 changes: 12 additions & 0 deletions copyptr.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
package boxpacker3

// CopyPtr creates a copy of a pointer.
//
// It takes a pointer to a generic type T as an argument and returns a new pointer
// to a copy of the original value. If the original pointer is nil, it returns nil.
func CopyPtr[T any](original *T) *T {
// If the original pointer is nil, return nil.
if original == nil {
return nil
}

// Create a copy of the value pointed to by the original pointer.
copyOfValue := *original

// Return a new pointer to the copied value.
return &copyOfValue
}

// CopySlicePtr creates a copy of a slice of pointers.
//
// It takes a slice of pointers as an argument and returns a new slice with the same
// elements, but with each element being a copy of the original.
func CopySlicePtr[T any](data []*T) []*T {
// Create a new slice with the same length as the original.
result := make([]*T, len(data))

// Iterate over the original slice and copy each element to the new slice.
for i, item := range data {
result[i] = CopyPtr(item)
}

// Return the new slice.
return result
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.22
require (
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240707233637-46b078467d37
)

require (
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
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=
Expand Down
Loading

0 comments on commit c82ddbc

Please sign in to comment.