-
Notifications
You must be signed in to change notification settings - Fork 0
/
box.go
235 lines (197 loc) · 6.87 KB
/
box.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package boxpacker3
// 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 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 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{
id: id,
width: w,
height: h,
depth: d,
maxWeight: mw,
maxLength: max(w, h, d),
volume: w * h * d,
items: make([]*Item, 0, 1),
}
}
// 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 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
// 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
}
// 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.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
}
// 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
}