修复纸箱不展示问题

This commit is contained in:
admin 2025-04-22 18:59:06 +08:00
parent 3bcbffb16e
commit 8238d35d3a
2 changed files with 77 additions and 91 deletions

View File

@ -59,9 +59,6 @@
<script> <script>
let scene, camera, renderer, controls; let scene, camera, renderer, controls;
let containerMesh, boxMeshes = []; let containerMesh, boxMeshes = [];
let activeCamera = 'front';
let tooltip = document.createElement('div');
tooltip.className = 'tooltip';
let result = {}; let result = {};
function initThree() { function initThree() {
@ -78,19 +75,23 @@
// 初始化相机位置 // 初始化相机位置
camera.position.set(15000, 5000, 15000); camera.position.set(15000, 5000, 15000);
controls.target.set(0, 0, 0); controls.target.set(0, 0, 0);
controls.update();
// 添加网格辅助线 // 添加网格辅助线
const gridHelper = new THREE.GridHelper(20000, 20, 0xffffff, 0x444444); const gridHelper = new THREE.GridHelper(20000, 20, 0xffffff, 0x444444);
gridHelper.material.opacity = 0.5; gridHelper.material.opacity = 0.5;
gridHelper.material.transparent = true;
scene.add(gridHelper); scene.add(gridHelper);
// 添加坐标轴辅助线 // 添加坐标轴
const axesHelper = new THREE.AxesHelper(5000); const axesHelper = new THREE.AxesHelper(5000);
axesHelper.material.color.set(0xff0000); // 红色坐标轴
scene.add(axesHelper); scene.add(axesHelper);
// 自适应窗口
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate(); animate();
} }
@ -98,12 +99,6 @@
requestAnimationFrame(animate); requestAnimationFrame(animate);
controls.update(); controls.update();
renderer.render(scene, camera); renderer.render(scene, camera);
// 更新提示信息
if (document.querySelector('.tooltip')) {
document.body.removeChild(document.querySelector('.tooltip'));
}
document.body.appendChild(tooltip);
tooltip.style.opacity = 0;
} }
async function calculate() { async function calculate() {
@ -121,17 +116,15 @@
weight: parseFloat(document.getElementById('boxWeight').value) weight: parseFloat(document.getElementById('boxWeight').value)
} }
}; };
try { try {
const response = await fetch('/calculate', { const response = await fetch('/calculate', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
if (!response.ok) { if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
throw new Error(`HTTP error! Status: ${response.status}`);
}
result = await response.json(); result = await response.json();
console.log(result); // 添加日志
document.getElementById('result').innerHTML = `最优装箱数:${result.count}`; document.getElementById('result').innerHTML = `最优装箱数:${result.count}`;
// 显示说明 // 显示说明
@ -157,9 +150,8 @@
data.container.width data.container.width
); );
const containerMat = new THREE.MeshBasicMaterial({ const containerMat = new THREE.MeshBasicMaterial({
color: 0xAAAAAA, // 浅灰色线框 color: 0xAAAAAA,
wireframe: true, wireframe: true
opacity: 0.5
}); });
containerMesh = new THREE.Mesh(containerGeo, containerMat); containerMesh = new THREE.Mesh(containerGeo, containerMat);
containerMesh.position.set( containerMesh.position.set(
@ -191,9 +183,9 @@
updateVisibility(); updateVisibility();
// 添加光源 // 添加光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); const light = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10000, 10000, 10000); light.position.set(1, 1, 1).normalize();
scene.add(directionalLight); scene.add(light);
const ambientLight = new THREE.AmbientLight(0x404040, 0.5); const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight); scene.add(ambientLight);
} catch (error) { } catch (error) {
@ -206,12 +198,6 @@
boxMeshes.forEach(mesh => scene.remove(mesh)); boxMeshes.forEach(mesh => scene.remove(mesh));
boxMeshes = []; boxMeshes = [];
// 纸箱材质:鲜艳的红色,不透明
const boxMat = new THREE.MeshLambertMaterial({
color: 0xff0000, // 红色
transparent: false
});
const checkedLayers = Array.from(document.querySelectorAll('.layer-checkboxes input:checked')) const checkedLayers = Array.from(document.querySelectorAll('.layer-checkboxes input:checked'))
.map(cb => parseInt(cb.id.split('-')[1])); .map(cb => parseInt(cb.id.split('-')[1]));
@ -223,23 +209,24 @@
result.boxHeight, result.boxHeight,
result.boxWidth result.boxWidth
); );
const boxMat = new THREE.MeshLambertMaterial({ color: 0xff0000 });
const box = new THREE.Mesh(boxGeo, boxMat); const box = new THREE.Mesh(boxGeo, boxMat);
// 设置箱子中心位置 // 设置中心坐标Three.js坐标系
box.position.set( box.position.set(
pos.x + result.boxLength / 2, pos.x + result.boxLength / 2,
pos.y + result.boxHeight / 2, pos.y + result.boxHeight / 2,
pos.z + result.boxWidth / 2 pos.z + result.boxWidth / 2
); );
// 应用旋转角度 // 应用旋转角度(弧度转角度)
box.rotation.set( box.rotation.set(
THREE.MathUtils.degToRad(pos.rotationX), pos.rotationX * Math.PI / 180,
THREE.MathUtils.degToRad(pos.rotationY), pos.rotationY * Math.PI / 180,
THREE.MathUtils.degToRad(pos.rotationZ) pos.rotationZ * Math.PI / 180
); );
box.userData.index = pos.boxNumber; box.userData.index = pos.boxNumber;
box.addEventListener('pointerover', showTooltip); box.addEventListener('pointerover', showTooltip);
box.addEventListener('pointerout', hideTooltip); box.addEventListener('pointerout', hideTooltip);
scene.add(box); scene.add(box);
@ -253,6 +240,8 @@
const position = box.position; const position = box.position;
const rotation = box.rotation; const rotation = box.rotation;
const dimensions = box.geometry.parameters; const dimensions = box.geometry.parameters;
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.innerHTML = ` tooltip.innerHTML = `
箱号:${box.userData.index}<br> 箱号:${box.userData.index}<br>
位置:(${position.x.toFixed(0)}, ${position.y.toFixed(0)}, ${position.z.toFixed(0)})<br> 位置:(${position.x.toFixed(0)}, ${position.y.toFixed(0)}, ${position.z.toFixed(0)})<br>
@ -261,13 +250,15 @@
Y=${(rotation.y * 180 / Math.PI).toFixed(0)}°, Y=${(rotation.y * 180 / Math.PI).toFixed(0)}°,
Z=${(rotation.z * 180 / Math.PI).toFixed(0)}° Z=${(rotation.z * 180 / Math.PI).toFixed(0)}°
`; `;
document.body.appendChild(tooltip);
tooltip.style.left = `${event.clientX + 10}px`; tooltip.style.left = `${event.clientX + 10}px`;
tooltip.style.top = `${event.clientY - 30}px`; tooltip.style.top = `${event.clientY - 30}px`;
tooltip.style.opacity = 1; tooltip.style.opacity = 1;
} }
function hideTooltip() { function hideTooltip() {
tooltip.style.opacity = 0; const tooltips = document.querySelectorAll('.tooltip');
tooltips.forEach(t => t.remove());
} }
window.addEventListener('keydown', (e) => { window.addEventListener('keydown', (e) => {
@ -280,23 +271,30 @@
}; };
switch (e.key) { switch (e.key) {
case '1': // 前视图 case '1': // 前视图
camera.position.set(data.container.length * 2, data.container.height / 2, data.container.width / 2); camera.position.set(
activeCamera = 'front'; data.container.length * 2,
data.container.height / 2,
data.container.width / 2
);
break; break;
case '2': // 后视图 case '2': // 顶视图
camera.position.set(-data.container.length * 2, data.container.height / 2, data.container.width / 2); camera.position.set(
activeCamera = 'back'; data.container.length / 2,
data.container.height * 2,
data.container.width / 2
);
break; break;
case '3': // 顶视图 case '3': // 右视图
camera.position.set(data.container.length / 2, data.container.height * 2, data.container.width / 2); camera.position.set(
activeCamera = 'top'; data.container.length / 2,
data.container.height / 2,
data.container.width * 2
);
break; break;
} }
controls.update(); controls.update();
document.getElementById('axisInfo').innerHTML = `视角:${activeCamera}`;
}); });
// 确保在页面加载完成后初始化Three.js
window.onload = function() { window.onload = function() {
initThree(); initThree();
}; };

58
main.go
View File

@ -22,7 +22,9 @@ type Box struct {
} }
type Placement struct { type Placement struct {
X, Y, Z float64 `json:"x"` X float64 `json:"x"`
Y float64 `json:"y"`
Z float64 `json:"z"`
RotationX float64 `json:"rotationX"` RotationX float64 `json:"rotationX"`
RotationY float64 `json:"rotationY"` RotationY float64 `json:"rotationY"`
RotationZ float64 `json:"rotationZ"` RotationZ float64 `json:"rotationZ"`
@ -38,29 +40,24 @@ func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html") http.ServeFile(w, r, "index.html")
}) })
http.HandleFunc("/calculate", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/calculate", func(w http.ResponseWriter, r *http.Request) {
var data struct { var data struct {
Container Container `json:"container"` Container Container `json:"container"`
Box Box `json:"box"` Box Box `json:"box"`
} }
json.NewDecoder(r.Body).Decode(&data) json.NewDecoder(r.Body).Decode(&data)
layout, strategy, count := optimizePacking(data.Container, data.Box) layout, strategy, count := optimizePacking(data.Container, data.Box)
layerMap := make(map[float64][]Placement) layerMap := make(map[float64][]Placement)
for _, pos := range layout { for _, pos := range layout {
layerMap[pos.Z] = append(layerMap[pos.Z], pos) layerMap[pos.Z] = append(layerMap[pos.Z], pos)
} }
var layers []Layer var layers []Layer
for _, layer := range layerMap { // 修正:删除未使用的变量 z for _, layer := range layerMap {
layers = append(layers, Layer{ layers = append(layers, Layer{
Count: len(layer), Count: len(layer),
Layout: layer, Layout: layer,
}) })
} }
response := struct { response := struct {
Count int `json:"count"` Count int `json:"count"`
Layers []Layer `json:"layers"` Layers []Layer `json:"layers"`
@ -77,31 +74,26 @@ func main() {
Layers: layers, Layers: layers,
Strategy: strategy, Strategy: strategy,
Density: calculateDensity(data.Container, data.Box, count), Density: calculateDensity(data.Container, data.Box, count),
SpaceUtilization: calculateSpaceUtilization(data.Container, data.Box, count), SpaceUtilization: calculateDensity(data.Container, data.Box, count),
UsedVolume: float64(count) * data.Box.Length * data.Box.Width * data.Box.Height, UsedVolume: float64(count) * data.Box.Length * data.Box.Width * data.Box.Height,
TotalWeight: float64(count) * data.Box.Weight, TotalWeight: float64(count) * data.Box.Weight,
BoxLength: data.Box.Length, BoxLength: data.Box.Length,
BoxWidth: data.Box.Width, BoxWidth: data.Box.Width,
BoxHeight: data.Box.Height, BoxHeight: data.Box.Height,
} }
json.NewEncoder(w).Encode(response) json.NewEncoder(w).Encode(response)
}) })
http.ListenAndServe(":8080", nil) http.ListenAndServe(":8080", nil)
} }
func optimizePacking(con Container, box Box) ([]Placement, string, int) { func optimizePacking(con Container, box Box) ([]Placement, string, int) {
rotations := generateRotations(con, box) rotations := generateRotations(con, box)
type candidate struct { type candidate struct {
layout []Placement layout []Placement
count int count int
strategy string strategy string
} }
var candidates []candidate var candidates []candidate
for _, r := range rotations { for _, r := range rotations {
for _, strategy := range []string{"XY", "XZ", "YX", "YZ", "ZX", "ZY"} { for _, strategy := range []string{"XY", "XZ", "YX", "YZ", "ZX", "ZY"} {
var xCount, yCount, zCount float64 var xCount, yCount, zCount float64
@ -131,14 +123,12 @@ func optimizePacking(con Container, box Box) ([]Placement, string, int) {
yCount = math.Floor(con.Width / r.Height) yCount = math.Floor(con.Width / r.Height)
xCount = math.Floor(con.Length / r.Width) xCount = math.Floor(con.Length / r.Width)
} }
totalByVolume := int(xCount * yCount * zCount) totalByVolume := int(xCount * yCount * zCount)
maxCountByWeight := int(math.Floor(con.WeightLimit / box.Weight)) maxCountByWeight := int(math.Floor(con.WeightLimit / box.Weight))
actualCount := totalByVolume actualCount := totalByVolume
if actualCount > maxCountByWeight { if actualCount > maxCountByWeight {
actualCount = maxCountByWeight actualCount = maxCountByWeight
} }
if actualCount > 0 { if actualCount > 0 {
candidates = append(candidates, candidate{ candidates = append(candidates, candidate{
layout: generateLayout(r, xCount, yCount, zCount, strategy)[:actualCount], layout: generateLayout(r, xCount, yCount, zCount, strategy)[:actualCount],
@ -148,15 +138,12 @@ func optimizePacking(con Container, box Box) ([]Placement, string, int) {
} }
} }
} }
if len(candidates) == 0 { if len(candidates) == 0 {
return nil, "", 0 return nil, "", 0
} }
maxCount := 0 maxCount := 0
var finalLayout []Placement var finalLayout []Placement
var finalStrategy string var finalStrategy string
for _, c := range candidates { for _, c := range candidates {
if c.count > maxCount { if c.count > maxCount {
maxCount = c.count maxCount = c.count
@ -164,7 +151,6 @@ func optimizePacking(con Container, box Box) ([]Placement, string, int) {
finalStrategy = c.strategy finalStrategy = c.strategy
} }
} }
return finalLayout, finalStrategy, maxCount return finalLayout, finalStrategy, maxCount
} }
@ -196,11 +182,12 @@ func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Pl
for z := 0.0; z < zCount; z++ { for z := 0.0; z < zCount; z++ {
layout = append(layout, Placement{ layout = append(layout, Placement{
X: x * r.Length, X: x * r.Length,
Y: z * r.Height, Y: y * r.Width,
Z: y * r.Width, Z: z * r.Height,
RotationX: 0.0, RotationX: 0.0,
RotationY: 0.0, RotationY: 0.0,
RotationZ: 0.0, RotationZ: 0.0,
BoxNumber: len(layout),
}) })
} }
} }
@ -211,11 +198,12 @@ func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Pl
for y := 0.0; y < yCount; y++ { for y := 0.0; y < yCount; y++ {
layout = append(layout, Placement{ layout = append(layout, Placement{
X: x * r.Length, X: x * r.Length,
Y: z * r.Height, Y: y * r.Width,
Z: y * r.Width, Z: z * r.Height,
RotationX: 0.0, RotationX: 0.0,
RotationY: 0.0, RotationY: 0.0,
RotationZ: 0.0, RotationZ: 0.0,
BoxNumber: len(layout),
}) })
} }
} }
@ -226,11 +214,12 @@ func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Pl
for z := 0.0; z < zCount; z++ { for z := 0.0; z < zCount; z++ {
layout = append(layout, Placement{ layout = append(layout, Placement{
X: x * r.Width, X: x * r.Width,
Y: z * r.Height, Y: y * r.Length,
Z: y * r.Length, Z: z * r.Height,
RotationX: 0.0, RotationX: 0.0,
RotationY: 0.0, RotationY: 0.0,
RotationZ: 0.0, RotationZ: 0.0,
BoxNumber: len(layout),
}) })
} }
} }
@ -241,11 +230,12 @@ func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Pl
for x := 0.0; x < xCount; x++ { for x := 0.0; x < xCount; x++ {
layout = append(layout, Placement{ layout = append(layout, Placement{
X: x * r.Width, X: x * r.Width,
Y: z * r.Height, Y: y * r.Length,
Z: y * r.Length, Z: z * r.Height,
RotationX: 0.0, RotationX: 0.0,
RotationY: 0.0, RotationY: 0.0,
RotationZ: 0.0, RotationZ: 0.0,
BoxNumber: len(layout),
}) })
} }
} }
@ -256,11 +246,12 @@ func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Pl
for y := 0.0; y < yCount; y++ { for y := 0.0; y < yCount; y++ {
layout = append(layout, Placement{ layout = append(layout, Placement{
X: x * r.Width, X: x * r.Width,
Y: z * r.Length, Y: y * r.Height,
Z: y * r.Height, Z: z * r.Length,
RotationX: 0.0, RotationX: 0.0,
RotationY: 0.0, RotationY: 0.0,
RotationZ: 0.0, RotationZ: 0.0,
BoxNumber: len(layout),
}) })
} }
} }
@ -271,11 +262,12 @@ func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Pl
for x := 0.0; x < xCount; x++ { for x := 0.0; x < xCount; x++ {
layout = append(layout, Placement{ layout = append(layout, Placement{
X: x * r.Width, X: x * r.Width,
Y: z * r.Length, Y: y * r.Height,
Z: y * r.Height, Z: z * r.Length,
RotationX: 0.0, RotationX: 0.0,
RotationY: 0.0, RotationY: 0.0,
RotationZ: 0.0, RotationZ: 0.0,
BoxNumber: len(layout),
}) })
} }
} }
@ -291,7 +283,3 @@ func calculateDensity(con Container, box Box, count int) float64 {
boxVolume := float64(count) * box.Length * box.Width * box.Height boxVolume := float64(count) * box.Length * box.Width * box.Height
return (boxVolume / containerVolume) * 100 return (boxVolume / containerVolume) * 100
} }
func calculateSpaceUtilization(con Container, box Box, count int) float64 {
return calculateDensity(con, box, count)
}