2025-04-18 13:20:01 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
2025-04-21 14:12:26 +08:00
|
|
|
"fmt"
|
2025-04-18 13:20:01 +08:00
|
|
|
"math"
|
|
|
|
|
"net/http"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Container struct {
|
2025-04-21 14:12:26 +08:00
|
|
|
Length float64 `json:"length"`
|
|
|
|
|
Width float64 `json:"width"`
|
|
|
|
|
Height float64 `json:"height"`
|
|
|
|
|
WeightLimit float64 `json:"weightLimit"`
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Box struct {
|
|
|
|
|
Length float64 `json:"length"`
|
|
|
|
|
Width float64 `json:"width"`
|
|
|
|
|
Height float64 `json:"height"`
|
2025-04-21 14:12:26 +08:00
|
|
|
Weight float64 `json:"weight"`
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Placement struct {
|
2025-04-21 14:12:26 +08:00
|
|
|
X, Y, Z float64 `json:"x"`
|
|
|
|
|
RotationX float64 `json:"rotationX"`
|
|
|
|
|
RotationY float64 `json:"rotationY"`
|
|
|
|
|
RotationZ float64 `json:"rotationZ"`
|
|
|
|
|
BoxNumber int `json:"boxNumber"`
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-21 14:12:26 +08:00
|
|
|
type Layer struct {
|
|
|
|
|
Count int `json:"count"`
|
|
|
|
|
Layout []Placement `json:"layout"`
|
2025-04-18 14:03:36 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 13:20:01 +08:00
|
|
|
func main() {
|
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
http.ServeFile(w, r, "index.html")
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
http.HandleFunc("/calculate", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
var data struct {
|
|
|
|
|
Container Container `json:"container"`
|
|
|
|
|
Box Box `json:"box"`
|
|
|
|
|
}
|
|
|
|
|
json.NewDecoder(r.Body).Decode(&data)
|
|
|
|
|
|
2025-04-21 14:12:26 +08:00
|
|
|
layout, strategy, count := optimizePacking(data.Container, data.Box)
|
2025-04-18 13:20:01 +08:00
|
|
|
|
2025-04-21 14:12:26 +08:00
|
|
|
layerMap := make(map[float64][]Placement)
|
|
|
|
|
for _, pos := range layout {
|
|
|
|
|
layerMap[pos.Z] = append(layerMap[pos.Z], pos)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var layers []Layer
|
|
|
|
|
for _, layer := range layerMap { // 修正:删除未使用的变量 z
|
|
|
|
|
layers = append(layers, Layer{
|
|
|
|
|
Count: len(layer),
|
|
|
|
|
Layout: layer,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response := struct {
|
|
|
|
|
Count int `json:"count"`
|
|
|
|
|
Layers []Layer `json:"layers"`
|
|
|
|
|
Strategy string `json:"strategy"`
|
|
|
|
|
Density float64 `json:"density"`
|
|
|
|
|
SpaceUtilization float64 `json:"spaceUtilization"`
|
|
|
|
|
UsedVolume float64 `json:"usedVolume"`
|
|
|
|
|
TotalWeight float64 `json:"totalWeight"`
|
|
|
|
|
BoxLength float64 `json:"boxLength"`
|
|
|
|
|
BoxWidth float64 `json:"boxWidth"`
|
|
|
|
|
BoxHeight float64 `json:"boxHeight"`
|
|
|
|
|
}{
|
2025-04-18 14:03:36 +08:00
|
|
|
Count: count,
|
2025-04-21 14:12:26 +08:00
|
|
|
Layers: layers,
|
2025-04-18 14:03:36 +08:00
|
|
|
Strategy: strategy,
|
2025-04-21 14:12:26 +08:00
|
|
|
Density: calculateDensity(data.Container, data.Box, count),
|
|
|
|
|
SpaceUtilization: calculateSpaceUtilization(data.Container, data.Box, count),
|
|
|
|
|
UsedVolume: float64(count) * data.Box.Length * data.Box.Width * data.Box.Height,
|
|
|
|
|
TotalWeight: float64(count) * data.Box.Weight,
|
|
|
|
|
BoxLength: data.Box.Length,
|
|
|
|
|
BoxWidth: data.Box.Width,
|
|
|
|
|
BoxHeight: data.Box.Height,
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
http.ListenAndServe(":8080", nil)
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-21 14:12:26 +08:00
|
|
|
func optimizePacking(con Container, box Box) ([]Placement, string, int) {
|
|
|
|
|
rotations := generateRotations(con, box)
|
|
|
|
|
|
|
|
|
|
type candidate struct {
|
|
|
|
|
layout []Placement
|
|
|
|
|
count int
|
|
|
|
|
weight float64
|
|
|
|
|
strategy string
|
|
|
|
|
}
|
2025-04-18 13:20:01 +08:00
|
|
|
|
2025-04-21 14:12:26 +08:00
|
|
|
var candidates []candidate
|
2025-04-18 13:20:01 +08:00
|
|
|
|
|
|
|
|
for _, r := range rotations {
|
|
|
|
|
for _, strategy := range []string{"XY", "XZ", "YX", "YZ", "ZX", "ZY"} {
|
|
|
|
|
var xCount, yCount, zCount float64
|
|
|
|
|
switch strategy {
|
|
|
|
|
case "XY":
|
|
|
|
|
xCount = math.Floor(con.Length / r.Length)
|
|
|
|
|
yCount = math.Floor(con.Width / r.Width)
|
|
|
|
|
zCount = math.Floor(con.Height / r.Height)
|
|
|
|
|
case "XZ":
|
|
|
|
|
xCount = math.Floor(con.Length / r.Length)
|
|
|
|
|
zCount = math.Floor(con.Height / r.Height)
|
|
|
|
|
yCount = math.Floor(con.Width / r.Width)
|
|
|
|
|
case "YX":
|
|
|
|
|
yCount = math.Floor(con.Width / r.Length)
|
|
|
|
|
xCount = math.Floor(con.Length / r.Width)
|
|
|
|
|
zCount = math.Floor(con.Height / r.Height)
|
|
|
|
|
case "YZ":
|
|
|
|
|
yCount = math.Floor(con.Width / r.Length)
|
|
|
|
|
zCount = math.Floor(con.Height / r.Height)
|
|
|
|
|
xCount = math.Floor(con.Length / r.Width)
|
|
|
|
|
case "ZX":
|
|
|
|
|
zCount = math.Floor(con.Height / r.Length)
|
|
|
|
|
xCount = math.Floor(con.Length / r.Width)
|
|
|
|
|
yCount = math.Floor(con.Width / r.Height)
|
|
|
|
|
case "ZY":
|
|
|
|
|
zCount = math.Floor(con.Height / r.Length)
|
|
|
|
|
yCount = math.Floor(con.Width / r.Height)
|
|
|
|
|
xCount = math.Floor(con.Length / r.Width)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
total := int(xCount * yCount * zCount)
|
2025-04-21 14:12:26 +08:00
|
|
|
totalWeight := float64(total) * box.Weight
|
|
|
|
|
|
|
|
|
|
if total > 0 {
|
|
|
|
|
fmt.Printf("Rotation: %v | Strategy: %s | Total: %d | Weight: %.2f kg\n", r, strategy, total, totalWeight)
|
|
|
|
|
candidates = append(candidates, candidate{
|
|
|
|
|
layout: generateLayout(r, xCount, yCount, zCount, strategy),
|
|
|
|
|
count: total,
|
|
|
|
|
weight: totalWeight,
|
|
|
|
|
strategy: strategy,
|
|
|
|
|
})
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-21 14:12:26 +08:00
|
|
|
maxCountUnderLimit := 0
|
|
|
|
|
var finalLayout []Placement
|
|
|
|
|
var finalStrategy string
|
|
|
|
|
|
|
|
|
|
for _, c := range candidates {
|
|
|
|
|
if c.count > maxCountUnderLimit && c.weight <= con.WeightLimit {
|
|
|
|
|
maxCountUnderLimit = c.count
|
|
|
|
|
finalLayout = c.layout
|
|
|
|
|
finalStrategy = c.strategy
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if maxCountUnderLimit == 0 {
|
|
|
|
|
return candidates[0].layout, candidates[0].strategy, candidates[0].count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return finalLayout, finalStrategy, maxCountUnderLimit
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-21 14:12:26 +08:00
|
|
|
func generateRotations(con Container, box Box) []Box {
|
|
|
|
|
validRotations := make([]Box, 0)
|
|
|
|
|
for _, r := range []Box{
|
|
|
|
|
{box.Length, box.Width, box.Height, box.Weight},
|
|
|
|
|
{box.Length, box.Height, box.Width, box.Weight},
|
|
|
|
|
{box.Width, box.Length, box.Height, box.Weight},
|
|
|
|
|
{box.Width, box.Height, box.Length, box.Weight},
|
|
|
|
|
{box.Height, box.Length, box.Width, box.Weight},
|
|
|
|
|
{box.Height, box.Width, box.Length, box.Weight},
|
|
|
|
|
} {
|
|
|
|
|
if r.Length <= con.Length &&
|
|
|
|
|
r.Width <= con.Width &&
|
|
|
|
|
r.Height <= con.Height {
|
|
|
|
|
validRotations = append(validRotations, r)
|
|
|
|
|
}
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
2025-04-21 14:12:26 +08:00
|
|
|
return validRotations
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Placement {
|
|
|
|
|
var layout []Placement
|
|
|
|
|
switch strategy {
|
|
|
|
|
case "XY":
|
|
|
|
|
for x := 0.0; x < xCount; x++ {
|
|
|
|
|
for y := 0.0; y < yCount; y++ {
|
|
|
|
|
for z := 0.0; z < zCount; z++ {
|
|
|
|
|
layout = append(layout, Placement{
|
2025-04-21 14:12:26 +08:00
|
|
|
X: x * r.Length,
|
|
|
|
|
Y: z * r.Height,
|
|
|
|
|
Z: y * r.Width,
|
|
|
|
|
RotationX: 0.0,
|
|
|
|
|
RotationY: 0.0,
|
|
|
|
|
RotationZ: 0.0,
|
2025-04-18 13:20:01 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "XZ":
|
|
|
|
|
for x := 0.0; x < xCount; x++ {
|
|
|
|
|
for z := 0.0; z < zCount; z++ {
|
|
|
|
|
for y := 0.0; y < yCount; y++ {
|
|
|
|
|
layout = append(layout, Placement{
|
2025-04-21 14:12:26 +08:00
|
|
|
X: x * r.Length,
|
|
|
|
|
Y: z * r.Height,
|
|
|
|
|
Z: y * r.Width,
|
|
|
|
|
RotationX: 0.0,
|
|
|
|
|
RotationY: 0.0,
|
|
|
|
|
RotationZ: 0.0,
|
2025-04-18 13:20:01 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "YX":
|
|
|
|
|
for y := 0.0; y < yCount; y++ {
|
|
|
|
|
for x := 0.0; x < xCount; x++ {
|
|
|
|
|
for z := 0.0; z < zCount; z++ {
|
|
|
|
|
layout = append(layout, Placement{
|
2025-04-21 14:12:26 +08:00
|
|
|
X: x * r.Width,
|
|
|
|
|
Y: z * r.Height,
|
|
|
|
|
Z: y * r.Length,
|
|
|
|
|
RotationX: 0.0,
|
|
|
|
|
RotationY: 0.0,
|
|
|
|
|
RotationZ: 0.0,
|
2025-04-18 13:20:01 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "YZ":
|
|
|
|
|
for y := 0.0; y < yCount; y++ {
|
|
|
|
|
for z := 0.0; z < zCount; z++ {
|
|
|
|
|
for x := 0.0; x < xCount; x++ {
|
|
|
|
|
layout = append(layout, Placement{
|
2025-04-21 14:12:26 +08:00
|
|
|
X: x * r.Width,
|
|
|
|
|
Y: z * r.Height,
|
|
|
|
|
Z: y * r.Length,
|
|
|
|
|
RotationX: 0.0,
|
|
|
|
|
RotationY: 0.0,
|
|
|
|
|
RotationZ: 0.0,
|
2025-04-18 13:20:01 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "ZX":
|
|
|
|
|
for z := 0.0; z < zCount; z++ {
|
|
|
|
|
for x := 0.0; x < xCount; x++ {
|
|
|
|
|
for y := 0.0; y < yCount; y++ {
|
|
|
|
|
layout = append(layout, Placement{
|
2025-04-21 14:12:26 +08:00
|
|
|
X: x * r.Width,
|
|
|
|
|
Y: z * r.Length,
|
|
|
|
|
Z: y * r.Height,
|
|
|
|
|
RotationX: 0.0,
|
|
|
|
|
RotationY: 0.0,
|
|
|
|
|
RotationZ: 0.0,
|
2025-04-18 13:20:01 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "ZY":
|
|
|
|
|
for z := 0.0; z < zCount; z++ {
|
|
|
|
|
for y := 0.0; y < yCount; y++ {
|
|
|
|
|
for x := 0.0; x < xCount; x++ {
|
|
|
|
|
layout = append(layout, Placement{
|
2025-04-21 14:12:26 +08:00
|
|
|
X: x * r.Width,
|
|
|
|
|
Y: z * r.Length,
|
|
|
|
|
Z: y * r.Height,
|
|
|
|
|
RotationX: 0.0,
|
|
|
|
|
RotationY: 0.0,
|
|
|
|
|
RotationZ: 0.0,
|
2025-04-18 13:20:01 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-21 14:12:26 +08:00
|
|
|
default:
|
|
|
|
|
fmt.Println("无效排列策略")
|
2025-04-18 13:20:01 +08:00
|
|
|
}
|
|
|
|
|
return layout
|
|
|
|
|
}
|
2025-04-21 14:12:26 +08:00
|
|
|
|
|
|
|
|
func calculateDensity(con Container, box Box, count int) float64 {
|
|
|
|
|
containerVolume := con.Length * con.Width * con.Height
|
|
|
|
|
boxVolume := float64(count) * box.Length * box.Width * box.Height
|
|
|
|
|
return (boxVolume / containerVolume) * 100
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func calculateSpaceUtilization(con Container, box Box, count int) float64 {
|
|
|
|
|
return calculateDensity(con, box, count)
|
|
|
|
|
}
|