修复超重问题
This commit is contained in:
parent
441a7d1bd7
commit
ae1d3459d9
BIN
__debug_bin1826320692.exe
Normal file
BIN
__debug_bin1826320692.exe
Normal file
Binary file not shown.
BIN
container-packing.exe
Normal file
BIN
container-packing.exe
Normal file
Binary file not shown.
2
go.mod
2
go.mod
@ -23,6 +23,8 @@ require (
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
github.com/vugu/vugu v0.4.0 // indirect
|
||||||
|
github.com/vugu/xxhash v0.0.0-20191111030615-ed24d0179019 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -56,6 +56,10 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
github.com/vugu/vugu v0.4.0 h1:ijU94l+qELec5f+Bb9R8m4fvZkhm4sKhawfmQVCV9a8=
|
||||||
|
github.com/vugu/vugu v0.4.0/go.mod h1:JU3YFROwFOqodkf1q8c1IRcx92j/dq258cqpaCsDlM8=
|
||||||
|
github.com/vugu/xxhash v0.0.0-20191111030615-ed24d0179019 h1:8NGiD5gWbVGObr+lnqcbM2rcOQBO6mr+m19BIblCdho=
|
||||||
|
github.com/vugu/xxhash v0.0.0-20191111030615-ed24d0179019/go.mod h1:PrBK6+LJXwb+3EnJTHo43Uh4FhjFFwvN4jKk4Zc5zZ8=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
|||||||
222
index.html
222
index.html
@ -28,6 +28,8 @@
|
|||||||
transition: opacity 0.3s;
|
transition: opacity 0.3s;
|
||||||
}
|
}
|
||||||
.layer-checkbox { margin-bottom: 10px; }
|
.layer-checkbox { margin-bottom: 10px; }
|
||||||
|
#weightLimit { margin-top: 10px; }
|
||||||
|
.layer-checkboxes { margin-top: 20px; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -38,22 +40,22 @@
|
|||||||
长:<input type="number" id="conLen" value="12014"><br>
|
长:<input type="number" id="conLen" value="12014"><br>
|
||||||
宽:<input type="number" id="conWid" value="2337"><br>
|
宽:<input type="number" id="conWid" value="2337"><br>
|
||||||
高:<input type="number" id="conHei" value="2388"><br>
|
高:<input type="number" id="conHei" value="2388"><br>
|
||||||
|
承重上限(kg):<input type="number" id="weightLimit" value="2000"><br>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
纸箱尺寸(mm):<br>
|
纸箱尺寸(mm):<br>
|
||||||
长:<input type="number" id="boxLen" value="685"><br>
|
长:<input type="number" id="boxLen" value="685"><br>
|
||||||
宽:<input type="number" id="boxWid" value="548"><br>
|
宽:<input type="number" id="boxWid" value="548"><br>
|
||||||
高:<input type="number" id="boxHei" value="489"><br>
|
高:<input type="number" id="boxHei" value="489"><br>
|
||||||
|
重量(kg):<input type="number" id="boxWeight" value="10"><br>
|
||||||
</div>
|
</div>
|
||||||
<button onclick="calculate()">开始计算</button>
|
<button onclick="calculate()">开始计算</button>
|
||||||
<div id="result"></div>
|
<div id="result"></div>
|
||||||
<div id="instructions" style="margin-top:20px; max-width:300px; line-height:1.5;"></div>
|
<div id="instructions" style="margin-top:20px; max-width:300px; line-height:1.5;"></div>
|
||||||
<div class="layer-checkboxes"></div>
|
<div class="layer-checkboxes"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="axisInfo" class="axis">视角:前视图</div>
|
<div id="axisInfo" class="axis">视角:前视图</div>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let scene, camera, renderer, controls;
|
let scene, camera, renderer, controls;
|
||||||
let containerMesh, boxMeshes = [];
|
let containerMesh, boxMeshes = [];
|
||||||
@ -64,19 +66,29 @@
|
|||||||
|
|
||||||
function initThree() {
|
function initThree() {
|
||||||
scene = new THREE.Scene();
|
scene = new THREE.Scene();
|
||||||
camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 1, 100000);
|
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 100000);
|
||||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
document.getElementById('container').appendChild(renderer.domElement);
|
document.getElementById('container').appendChild(renderer.domElement);
|
||||||
|
|
||||||
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||||||
|
controls.enableZoom = true;
|
||||||
|
controls.enablePan = true;
|
||||||
|
controls.enableRotate = true;
|
||||||
|
|
||||||
|
// 初始化相机位置
|
||||||
camera.position.set(15000, 5000, 15000);
|
camera.position.set(15000, 5000, 15000);
|
||||||
|
controls.target.set(0, 0, 0);
|
||||||
controls.update();
|
controls.update();
|
||||||
|
|
||||||
const gridHelper = new THREE.GridHelper(20000, 20, 0x888888, 0x888888);
|
// 添加网格辅助线
|
||||||
|
const gridHelper = new THREE.GridHelper(20000, 20, 0xffffff, 0x444444);
|
||||||
|
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);
|
||||||
|
|
||||||
animate();
|
animate();
|
||||||
@ -86,7 +98,6 @@
|
|||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
controls.update();
|
controls.update();
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
|
|
||||||
// 更新提示信息
|
// 更新提示信息
|
||||||
if (document.querySelector('.tooltip')) {
|
if (document.querySelector('.tooltip')) {
|
||||||
document.body.removeChild(document.querySelector('.tooltip'));
|
document.body.removeChild(document.querySelector('.tooltip'));
|
||||||
@ -100,97 +111,107 @@
|
|||||||
container: {
|
container: {
|
||||||
length: parseFloat(document.getElementById('conLen').value),
|
length: parseFloat(document.getElementById('conLen').value),
|
||||||
width: parseFloat(document.getElementById('conWid').value),
|
width: parseFloat(document.getElementById('conWid').value),
|
||||||
height: parseFloat(document.getElementById('conHei').value)
|
height: parseFloat(document.getElementById('conHei').value),
|
||||||
|
weightLimit: parseFloat(document.getElementById('weightLimit').value)
|
||||||
},
|
},
|
||||||
box: {
|
box: {
|
||||||
length: parseFloat(document.getElementById('boxLen').value),
|
length: parseFloat(document.getElementById('boxLen').value),
|
||||||
width: parseFloat(document.getElementById('boxWid').value),
|
width: parseFloat(document.getElementById('boxWid').value),
|
||||||
height: parseFloat(document.getElementById('boxHei').value)
|
height: parseFloat(document.getElementById('boxHei').value),
|
||||||
|
weight: parseFloat(document.getElementById('boxWeight').value)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
|
const response = await fetch('/calculate', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
|
}
|
||||||
|
result = await response.json();
|
||||||
|
console.log(result); // 添加日志
|
||||||
|
document.getElementById('result').innerHTML = `最优装箱数:${result.count}`;
|
||||||
|
|
||||||
const response = await fetch('/calculate', {
|
// 显示说明
|
||||||
method: 'POST',
|
const instructions = `
|
||||||
headers: { 'Content-Type': 'application/json' },
|
装箱方案说明:<br>
|
||||||
body: JSON.stringify(data)
|
1. 纸箱尺寸:长${result.boxLength}mm × 宽${result.boxWidth}mm × 高${result.boxHeight}mm<br>
|
||||||
});
|
2. 排列策略:${result.strategy}<br>
|
||||||
|
3. 空间利用率:${result.spaceUtilization.toFixed(2)}%<br>
|
||||||
|
4. 总重量:${result.totalWeight.toFixed(2)} kg(承重上限:${data.container.weightLimit} kg)<br>
|
||||||
|
${result.totalWeight > data.container.weightLimit ? "⚠️ 超重!" : ""}
|
||||||
|
`;
|
||||||
|
document.getElementById('instructions').innerHTML = instructions;
|
||||||
|
|
||||||
result = await response.json();
|
// 清理场景
|
||||||
document.getElementById('result').innerHTML = `最优装箱数:${result.count}`;
|
scene.remove(containerMesh);
|
||||||
|
boxMeshes.forEach(mesh => scene.remove(mesh));
|
||||||
|
boxMeshes = [];
|
||||||
|
|
||||||
// 显示装箱说明
|
// 创建集装箱
|
||||||
const instructions = `
|
const containerGeo = new THREE.BoxGeometry(
|
||||||
装箱方案说明:<br>
|
data.container.length,
|
||||||
1. 选择最优旋转方式:长${result.boxLength}mm × 宽${result.boxWidth}mm × 高${result.boxHeight}mm<br>
|
data.container.height,
|
||||||
2. 排列策略:${result.strategy}<br>
|
data.container.width
|
||||||
3. 排列密度:${result.density.toFixed(2)}%<br>
|
);
|
||||||
4. 空间利用率:${result.spaceUtilization.toFixed(2)}%<br>
|
const containerMat = new THREE.MeshBasicMaterial({
|
||||||
5. 实际占用体积:${(result.usedVolume/1000000000).toFixed(2)} m³
|
color: 0xAAAAAA, // 浅灰色线框
|
||||||
`;
|
wireframe: true,
|
||||||
document.getElementById('instructions').innerHTML = instructions;
|
opacity: 0.5
|
||||||
|
});
|
||||||
|
containerMesh = new THREE.Mesh(containerGeo, containerMat);
|
||||||
|
containerMesh.position.set(
|
||||||
|
data.container.length / 2,
|
||||||
|
data.container.height / 2,
|
||||||
|
data.container.width / 2
|
||||||
|
);
|
||||||
|
scene.add(containerMesh);
|
||||||
|
|
||||||
// 清理现有场景
|
// 创建分层控制
|
||||||
scene.remove(containerMesh);
|
const layerCheckboxes = document.querySelector('.layer-checkboxes');
|
||||||
boxMeshes.forEach(mesh => scene.remove(mesh));
|
layerCheckboxes.innerHTML = '';
|
||||||
boxMeshes = [];
|
result.layers.forEach((layer, index) => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
const checkbox = document.createElement('input');
|
||||||
|
checkbox.type = 'checkbox';
|
||||||
|
checkbox.id = `layer-${index}`;
|
||||||
|
checkbox.checked = true;
|
||||||
|
checkbox.addEventListener('change', updateVisibility);
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.htmlFor = `layer-${index}`;
|
||||||
|
label.textContent = `第${index + 1}层 (${layer.count}箱)`;
|
||||||
|
div.appendChild(checkbox);
|
||||||
|
div.appendChild(label);
|
||||||
|
layerCheckboxes.appendChild(div);
|
||||||
|
});
|
||||||
|
|
||||||
// 创建集装箱
|
// 显示所有层
|
||||||
const containerGeo = new THREE.BoxGeometry(
|
updateVisibility();
|
||||||
data.container.length,
|
|
||||||
data.container.height,
|
|
||||||
data.container.width
|
|
||||||
);
|
|
||||||
const containerMat = new THREE.MeshBasicMaterial({
|
|
||||||
color: 0x333333,
|
|
||||||
wireframe: true,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.3
|
|
||||||
});
|
|
||||||
containerMesh = new THREE.Mesh(containerGeo, containerMat);
|
|
||||||
containerMesh.position.set(data.container.length/2, data.container.height/2, data.container.width/2);
|
|
||||||
scene.add(containerMesh);
|
|
||||||
|
|
||||||
// 创建分层控制
|
// 添加光源
|
||||||
const layerCheckboxes = document.querySelector('.layer-checkboxes');
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
|
||||||
layerCheckboxes.innerHTML = '';
|
directionalLight.position.set(10000, 10000, 10000);
|
||||||
result.layers.forEach((layer, index) => {
|
scene.add(directionalLight);
|
||||||
const div = document.createElement('div');
|
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
|
||||||
const checkbox = document.createElement('input');
|
scene.add(ambientLight);
|
||||||
checkbox.type = 'checkbox';
|
} catch (error) {
|
||||||
checkbox.id = `layer-${index}`;
|
console.error('Error:', error);
|
||||||
checkbox.checked = true;
|
document.getElementById('result').innerHTML = '计算失败,请检查输入参数。';
|
||||||
checkbox.addEventListener('change', updateVisibility);
|
}
|
||||||
|
|
||||||
const label = document.createElement('label');
|
|
||||||
label.htmlFor = `layer-${index}`;
|
|
||||||
label.textContent = `第${index+1}层 (${layer.count}箱)`;
|
|
||||||
|
|
||||||
div.appendChild(checkbox);
|
|
||||||
div.appendChild(label);
|
|
||||||
layerCheckboxes.appendChild(div);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始显示所有层
|
|
||||||
updateVisibility();
|
|
||||||
|
|
||||||
// 添加光源
|
|
||||||
const light = new THREE.DirectionalLight(0xffffff, 1);
|
|
||||||
light.position.set(10000, 10000, 10000);
|
|
||||||
scene.add(light);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateVisibility() {
|
function updateVisibility() {
|
||||||
// 移除现有箱子
|
|
||||||
boxMeshes.forEach(mesh => scene.remove(mesh));
|
boxMeshes.forEach(mesh => scene.remove(mesh));
|
||||||
boxMeshes = [];
|
boxMeshes = [];
|
||||||
|
|
||||||
|
// 纸箱材质:鲜艳的红色,不透明
|
||||||
const boxMat = new THREE.MeshLambertMaterial({
|
const boxMat = new THREE.MeshLambertMaterial({
|
||||||
color: 0x00ff00,
|
color: 0xff0000, // 红色
|
||||||
transparent: true,
|
transparent: false
|
||||||
opacity: 0.6
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 根据选中层创建箱子
|
|
||||||
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]));
|
||||||
|
|
||||||
@ -203,19 +224,24 @@
|
|||||||
result.boxWidth
|
result.boxWidth
|
||||||
);
|
);
|
||||||
const box = new THREE.Mesh(boxGeo, boxMat);
|
const box = new THREE.Mesh(boxGeo, boxMat);
|
||||||
|
|
||||||
|
// 设置箱子中心位置
|
||||||
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),
|
THREE.MathUtils.degToRad(pos.rotationX),
|
||||||
THREE.MathUtils.degToRad(pos.rotationY),
|
THREE.MathUtils.degToRad(pos.rotationY),
|
||||||
THREE.MathUtils.degToRad(pos.rotationZ)
|
THREE.MathUtils.degToRad(pos.rotationZ)
|
||||||
);
|
);
|
||||||
|
|
||||||
box.userData.index = pos.boxNumber;
|
box.userData.index = pos.boxNumber;
|
||||||
box.on('pointerover', showTooltip);
|
box.addEventListener('pointerover', showTooltip);
|
||||||
box.on('pointerout', hideTooltip);
|
box.addEventListener('pointerout', hideTooltip);
|
||||||
scene.add(box);
|
scene.add(box);
|
||||||
boxMeshes.push(box);
|
boxMeshes.push(box);
|
||||||
});
|
});
|
||||||
@ -227,14 +253,13 @@
|
|||||||
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;
|
||||||
|
|
||||||
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>
|
||||||
尺寸:${dimensions.width}×${dimensions.height}×${dimensions.depth}<br>
|
尺寸:${dimensions.width}×${dimensions.height}×${dimensions.depth}<br>
|
||||||
旋转:X=${(rotation.x * 180/Math.PI).toFixed(0)}°,
|
旋转:X=${(rotation.x * 180 / Math.PI).toFixed(0)}°,
|
||||||
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)}°
|
||||||
`;
|
`;
|
||||||
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`;
|
||||||
@ -246,24 +271,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('keydown', (e) => {
|
window.addEventListener('keydown', (e) => {
|
||||||
switch(e.key) {
|
const data = {
|
||||||
case '1':
|
container: {
|
||||||
camera.position.set(15000, 5000, 15000);
|
length: parseFloat(document.getElementById('conLen').value),
|
||||||
|
width: parseFloat(document.getElementById('conWid').value),
|
||||||
|
height: parseFloat(document.getElementById('conHei').value),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch (e.key) {
|
||||||
|
case '1': // 前视图
|
||||||
|
camera.position.set(data.container.length * 2, data.container.height / 2, data.container.width / 2);
|
||||||
activeCamera = 'front';
|
activeCamera = 'front';
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2': // 后视图
|
||||||
camera.position.set(-15000, 5000, 15000);
|
camera.position.set(-data.container.length * 2, data.container.height / 2, data.container.width / 2);
|
||||||
activeCamera = 'back';
|
activeCamera = 'back';
|
||||||
break;
|
break;
|
||||||
case '3':
|
case '3': // 顶视图
|
||||||
camera.position.set(0, 15000, 0);
|
camera.position.set(data.container.length / 2, data.container.height * 2, data.container.width / 2);
|
||||||
activeCamera = 'top';
|
activeCamera = 'top';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
controls.update();
|
||||||
document.getElementById('axisInfo').innerHTML = `视角:${activeCamera}`;
|
document.getElementById('axisInfo').innerHTML = `视角:${activeCamera}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
initThree();
|
// 确保在页面加载完成后初始化Three.js
|
||||||
|
window.onload = function() {
|
||||||
|
initThree();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
218
main.go
218
main.go
@ -2,36 +2,36 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
Length float64 `json:"length"`
|
Length float64 `json:"length"`
|
||||||
Width float64 `json:"width"`
|
Width float64 `json:"width"`
|
||||||
Height float64 `json:"height"`
|
Height float64 `json:"height"`
|
||||||
|
WeightLimit float64 `json:"weightLimit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Box struct {
|
type Box struct {
|
||||||
Length float64 `json:"length"`
|
Length float64 `json:"length"`
|
||||||
Width float64 `json:"width"`
|
Width float64 `json:"width"`
|
||||||
Height float64 `json:"height"`
|
Height float64 `json:"height"`
|
||||||
|
Weight float64 `json:"weight"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Placement struct {
|
type Placement struct {
|
||||||
X, Y, Z float64 `json:"x"`
|
X, Y, Z float64 `json:"x"`
|
||||||
|
RotationX float64 `json:"rotationX"`
|
||||||
|
RotationY float64 `json:"rotationY"`
|
||||||
|
RotationZ float64 `json:"rotationZ"`
|
||||||
|
BoxNumber int `json:"boxNumber"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Layer struct {
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
Layout []Placement `json:"layout"`
|
Layout []Placement `json:"layout"`
|
||||||
BoxLength float64 `json:"boxLength"`
|
|
||||||
BoxWidth float64 `json:"boxWidth"`
|
|
||||||
BoxHeight float64 `json:"boxHeight"`
|
|
||||||
Strategy string `json:"strategy"`
|
|
||||||
Density float64 `json:"density"`
|
|
||||||
SpaceUtilization float64 `json:"spaceUtilization"`
|
|
||||||
UsedVolume float64 `json:"usedVolume"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -46,19 +46,43 @@ func main() {
|
|||||||
}
|
}
|
||||||
json.NewDecoder(r.Body).Decode(&data)
|
json.NewDecoder(r.Body).Decode(&data)
|
||||||
|
|
||||||
layout, bestRotation, strategy, density, spaceUtilization, usedVolume := optimizePacking(data.Container, data.Box)
|
layout, strategy, count := optimizePacking(data.Container, data.Box)
|
||||||
count := len(layout)
|
|
||||||
|
|
||||||
response := Response{
|
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"`
|
||||||
|
}{
|
||||||
Count: count,
|
Count: count,
|
||||||
Layout: layout,
|
Layers: layers,
|
||||||
BoxLength: bestRotation.Length,
|
|
||||||
BoxWidth: bestRotation.Width,
|
|
||||||
BoxHeight: bestRotation.Height,
|
|
||||||
Strategy: strategy,
|
Strategy: strategy,
|
||||||
Density: density,
|
Density: calculateDensity(data.Container, data.Box, count),
|
||||||
SpaceUtilization: spaceUtilization,
|
SpaceUtilization: calculateSpaceUtilization(data.Container, data.Box, count),
|
||||||
UsedVolume: usedVolume,
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
@ -67,17 +91,17 @@ func main() {
|
|||||||
http.ListenAndServe(":8080", nil)
|
http.ListenAndServe(":8080", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func optimizePacking(con Container, box Box) ([]Placement, Box, string, float64, float64, float64) {
|
func optimizePacking(con Container, box Box) ([]Placement, string, int) {
|
||||||
var bestLayout []Placement
|
rotations := generateRotations(con, box)
|
||||||
var bestRotation Box
|
|
||||||
var bestStrategy string
|
|
||||||
maxCount := 0
|
|
||||||
var bestDensity float64
|
|
||||||
var bestSpaceUtilization float64
|
|
||||||
var bestUsedVolume float64
|
|
||||||
|
|
||||||
rotations := generateRotations(box)
|
type candidate struct {
|
||||||
conVolume := con.Length * con.Width * con.Height
|
layout []Placement
|
||||||
|
count int
|
||||||
|
weight float64
|
||||||
|
strategy string
|
||||||
|
}
|
||||||
|
|
||||||
|
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"} {
|
||||||
@ -110,32 +134,56 @@ func optimizePacking(con Container, box Box) ([]Placement, Box, string, float64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
total := int(xCount * yCount * zCount)
|
total := int(xCount * yCount * zCount)
|
||||||
if total > maxCount && total > 0 {
|
totalWeight := float64(total) * box.Weight
|
||||||
maxCount = total
|
|
||||||
bestRotation = r
|
|
||||||
bestStrategy = strategy
|
|
||||||
bestLayout = generateLayout(r, xCount, yCount, zCount, strategy)
|
|
||||||
|
|
||||||
boxVolume := r.Length * r.Width * r.Height
|
if total > 0 {
|
||||||
bestUsedVolume = float64(total) * boxVolume
|
fmt.Printf("Rotation: %v | Strategy: %s | Total: %d | Weight: %.2f kg\n", r, strategy, total, totalWeight)
|
||||||
bestDensity = (bestUsedVolume / conVolume) * 100
|
candidates = append(candidates, candidate{
|
||||||
bestSpaceUtilization = bestDensity
|
layout: generateLayout(r, xCount, yCount, zCount, strategy),
|
||||||
|
count: total,
|
||||||
|
weight: totalWeight,
|
||||||
|
strategy: strategy,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestLayout, bestRotation, bestStrategy, bestDensity, bestSpaceUtilization, bestUsedVolume
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateRotations(box Box) []Box {
|
func generateRotations(con Container, box Box) []Box {
|
||||||
return []Box{
|
validRotations := make([]Box, 0)
|
||||||
{box.Length, box.Width, box.Height},
|
for _, r := range []Box{
|
||||||
{box.Length, box.Height, box.Width},
|
{box.Length, box.Width, box.Height, box.Weight},
|
||||||
{box.Width, box.Length, box.Height},
|
{box.Length, box.Height, box.Width, box.Weight},
|
||||||
{box.Width, box.Height, box.Length},
|
{box.Width, box.Length, box.Height, box.Weight},
|
||||||
{box.Height, box.Length, box.Width},
|
{box.Width, box.Height, box.Length, box.Weight},
|
||||||
{box.Height, box.Width, box.Length},
|
{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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return validRotations
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Placement {
|
func generateLayout(r Box, xCount, yCount, zCount float64, strategy string) []Placement {
|
||||||
@ -146,9 +194,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++ {
|
||||||
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: z * r.Height,
|
||||||
Z: y * r.Width,
|
Z: y * r.Width,
|
||||||
|
RotationX: 0.0,
|
||||||
|
RotationY: 0.0,
|
||||||
|
RotationZ: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,9 +209,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++ {
|
||||||
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: z * r.Height,
|
||||||
Z: y * r.Width,
|
Z: y * r.Width,
|
||||||
|
RotationX: 0.0,
|
||||||
|
RotationY: 0.0,
|
||||||
|
RotationZ: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,9 +224,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++ {
|
||||||
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: z * r.Height,
|
||||||
Z: y * r.Length,
|
Z: y * r.Length,
|
||||||
|
RotationX: 0.0,
|
||||||
|
RotationY: 0.0,
|
||||||
|
RotationZ: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,9 +239,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++ {
|
||||||
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: z * r.Height,
|
||||||
Z: y * r.Length,
|
Z: y * r.Length,
|
||||||
|
RotationX: 0.0,
|
||||||
|
RotationY: 0.0,
|
||||||
|
RotationZ: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,9 +254,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++ {
|
||||||
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: z * r.Length,
|
||||||
Z: y * r.Height,
|
Z: y * r.Height,
|
||||||
|
RotationX: 0.0,
|
||||||
|
RotationY: 0.0,
|
||||||
|
RotationZ: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,13 +269,28 @@ 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++ {
|
||||||
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: z * r.Length,
|
||||||
Z: y * r.Height,
|
Z: y * r.Height,
|
||||||
|
RotationX: 0.0,
|
||||||
|
RotationY: 0.0,
|
||||||
|
RotationZ: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
fmt.Println("无效排列策略")
|
||||||
}
|
}
|
||||||
return layout
|
return layout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user