diff --git a/__debug_bin1038044772.exe b/__debug_bin1038044772.exe
deleted file mode 100644
index 4a7e2de..0000000
Binary files a/__debug_bin1038044772.exe and /dev/null differ
diff --git a/__debug_bin3681850586.exe b/__debug_bin3681850586.exe
deleted file mode 100644
index bc646ab..0000000
Binary files a/__debug_bin3681850586.exe and /dev/null differ
diff --git a/index.html b/index.html
index 27a7b90..eb471f4 100644
--- a/index.html
+++ b/index.html
@@ -18,6 +18,16 @@
}
#result { margin-top: 20px; font-weight: bold; }
.axis { position: absolute; bottom: 20px; left: 20px; background: rgba(255,255,255,0.8); padding: 10px; border-radius: 4px; }
+ .tooltip {
+ position: absolute;
+ background: rgba(0,0,0,0.7);
+ color: white;
+ padding: 8px;
+ border-radius: 4px;
+ opacity: 0;
+ transition: opacity 0.3s;
+ }
+ .layer-checkbox { margin-bottom: 10px; }
@@ -37,6 +47,8 @@
开始计算
+
+
视角:前视图
@@ -46,6 +58,9 @@
let scene, camera, renderer, controls;
let containerMesh, boxMeshes = [];
let activeCamera = 'front';
+ let tooltip = document.createElement('div');
+ tooltip.className = 'tooltip';
+ let result = {};
function initThree() {
scene = new THREE.Scene();
@@ -71,6 +86,13 @@
requestAnimationFrame(animate);
controls.update();
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() {
@@ -93,13 +115,26 @@
body: JSON.stringify(data)
});
- const result = await response.json();
+ result = await response.json();
document.getElementById('result').innerHTML = `最优装箱数:${result.count}`;
+
+ // 显示装箱说明
+ const instructions = `
+ 装箱方案说明:
+ 1. 选择最优旋转方式:长${result.boxLength}mm × 宽${result.boxWidth}mm × 高${result.boxHeight}mm
+ 2. 排列策略:${result.strategy}
+ 3. 排列密度:${result.density.toFixed(2)}%
+ 4. 空间利用率:${result.spaceUtilization.toFixed(2)}%
+ 5. 实际占用体积:${(result.usedVolume/1000000000).toFixed(2)} m³
+ `;
+ document.getElementById('instructions').innerHTML = instructions;
+ // 清理现有场景
scene.remove(containerMesh);
boxMeshes.forEach(mesh => scene.remove(mesh));
boxMeshes = [];
+ // 创建集装箱
const containerGeo = new THREE.BoxGeometry(
data.container.length,
data.container.height,
@@ -115,31 +150,99 @@
containerMesh.position.set(data.container.length/2, data.container.height/2, data.container.width/2);
scene.add(containerMesh);
+ // 创建分层控制
+ const layerCheckboxes = document.querySelector('.layer-checkboxes');
+ layerCheckboxes.innerHTML = '';
+ 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);
+ });
+
+ // 初始显示所有层
+ updateVisibility();
+
+ // 添加光源
+ const light = new THREE.DirectionalLight(0xffffff, 1);
+ light.position.set(10000, 10000, 10000);
+ scene.add(light);
+ }
+
+ function updateVisibility() {
+ // 移除现有箱子
+ boxMeshes.forEach(mesh => scene.remove(mesh));
+ boxMeshes = [];
+
const boxMat = new THREE.MeshLambertMaterial({
color: 0x00ff00,
transparent: true,
opacity: 0.6
});
-
- result.layout.forEach(pos => {
- const boxGeo = new THREE.BoxGeometry(
- data.box.length,
- data.box.height,
- data.box.width
- );
- const box = new THREE.Mesh(boxGeo, boxMat);
- box.position.set(
- pos.x + data.box.length/2,
- pos.y + data.box.height/2,
- pos.z + data.box.width/2
- );
- scene.add(box);
- boxMeshes.push(box);
- });
- const light = new THREE.DirectionalLight(0xffffff, 1);
- light.position.set(10000, 10000, 10000);
- scene.add(light);
+ // 根据选中层创建箱子
+ const checkedLayers = Array.from(document.querySelectorAll('.layer-checkboxes input:checked'))
+ .map(cb => parseInt(cb.id.split('-')[1]));
+
+ checkedLayers.forEach(layerIndex => {
+ const layer = result.layers[layerIndex];
+ layer.layout.forEach(pos => {
+ const boxGeo = new THREE.BoxGeometry(
+ result.boxLength,
+ result.boxHeight,
+ result.boxWidth
+ );
+ const box = new THREE.Mesh(boxGeo, boxMat);
+ box.position.set(
+ pos.x + result.boxLength/2,
+ pos.y + result.boxHeight/2,
+ pos.z + result.boxWidth/2
+ );
+ box.rotation.set(
+ THREE.MathUtils.degToRad(pos.rotationX),
+ THREE.MathUtils.degToRad(pos.rotationY),
+ THREE.MathUtils.degToRad(pos.rotationZ)
+ );
+ box.userData.index = pos.boxNumber;
+ box.on('pointerover', showTooltip);
+ box.on('pointerout', hideTooltip);
+ scene.add(box);
+ boxMeshes.push(box);
+ });
+ });
+ }
+
+ function showTooltip(event) {
+ const box = event.target;
+ const position = box.position;
+ const rotation = box.rotation;
+ const dimensions = box.geometry.parameters;
+
+ tooltip.innerHTML = `
+ 箱号:${box.userData.index}
+ 位置:(${position.x.toFixed(0)}, ${position.y.toFixed(0)}, ${position.z.toFixed(0)})
+ 尺寸:${dimensions.width}×${dimensions.height}×${dimensions.depth}
+ 旋转:X=${(rotation.x * 180/Math.PI).toFixed(0)}°,
+ Y=${(rotation.y * 180/Math.PI).toFixed(0)}°,
+ Z=${(rotation.z * 180/Math.PI).toFixed(0)}°
+ `;
+ tooltip.style.left = `${event.clientX + 10}px`;
+ tooltip.style.top = `${event.clientY - 30}px`;
+ tooltip.style.opacity = 1;
+ }
+
+ function hideTooltip() {
+ tooltip.style.opacity = 0;
}
window.addEventListener('keydown', (e) => {
diff --git a/main.go b/main.go
index e8b10ab..d81a016 100644
--- a/main.go
+++ b/main.go
@@ -22,6 +22,18 @@ type Placement struct {
X, Y, Z float64 `json:"x"`
}
+type Response struct {
+ Count int `json:"count"`
+ 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() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
@@ -34,15 +46,19 @@ func main() {
}
json.NewDecoder(r.Body).Decode(&data)
- layout := optimizePacking(data.Container, data.Box)
+ layout, bestRotation, strategy, density, spaceUtilization, usedVolume := optimizePacking(data.Container, data.Box)
count := len(layout)
- response := struct {
- Count int `json:"count"`
- Layout []Placement `json:"layout"`
- }{
- Count: count,
- Layout: layout,
+ response := Response{
+ Count: count,
+ Layout: layout,
+ BoxLength: bestRotation.Length,
+ BoxWidth: bestRotation.Width,
+ BoxHeight: bestRotation.Height,
+ Strategy: strategy,
+ Density: density,
+ SpaceUtilization: spaceUtilization,
+ UsedVolume: usedVolume,
}
json.NewEncoder(w).Encode(response)
@@ -51,11 +67,17 @@ func main() {
http.ListenAndServe(":8080", nil)
}
-func optimizePacking(con Container, box Box) []Placement {
+func optimizePacking(con Container, box Box) ([]Placement, Box, string, float64, float64, float64) {
var bestLayout []Placement
+ var bestRotation Box
+ var bestStrategy string
maxCount := 0
+ var bestDensity float64
+ var bestSpaceUtilization float64
+ var bestUsedVolume float64
rotations := generateRotations(box)
+ conVolume := con.Length * con.Width * con.Height
for _, r := range rotations {
for _, strategy := range []string{"XY", "XZ", "YX", "YZ", "ZX", "ZY"} {
@@ -90,13 +112,19 @@ func optimizePacking(con Container, box Box) []Placement {
total := int(xCount * yCount * zCount)
if total > maxCount && total > 0 {
maxCount = total
- layout := generateLayout(r, xCount, yCount, zCount, strategy)
- bestLayout = layout
+ bestRotation = r
+ bestStrategy = strategy
+ bestLayout = generateLayout(r, xCount, yCount, zCount, strategy)
+
+ boxVolume := r.Length * r.Width * r.Height
+ bestUsedVolume = float64(total) * boxVolume
+ bestDensity = (bestUsedVolume / conVolume) * 100
+ bestSpaceUtilization = bestDensity
}
}
}
- return bestLayout
+ return bestLayout, bestRotation, bestStrategy, bestDensity, bestSpaceUtilization, bestUsedVolume
}
func generateRotations(box Box) []Box {
diff --git a/static/index.html b/static/index.html
deleted file mode 100644
index dc972c0..0000000
--- a/static/index.html
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
- 集装箱装箱计算器
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/static/main.js b/static/main.js
deleted file mode 100644
index 114ba80..0000000
--- a/static/main.js
+++ /dev/null
@@ -1,107 +0,0 @@
-let scene, camera, renderer, controls;
-
-function initThree() {
- // 初始化场景
- scene = new THREE.Scene();
- camera = new THREE.PerspectiveCamera(75,
- window.innerWidth / window.innerHeight, 0.1, 1000);
- renderer = new THREE.WebGLRenderer({ antialias: true });
- renderer.setSize(800, 600);
- document.getElementById('visualization').appendChild(renderer.domElement);
-
- // 添加光源
- const light = new THREE.AmbientLight(0xffffff, 0.8);
- scene.add(light);
- const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
- directionalLight.position.set(0, 1, 1);
- scene.add(directionalLight);
-
- // 设置相机位置
- camera.position.set(10, 10, 10);
- camera.lookAt(0, 0, 0);
-
- // 添加轨道控制器
- controls = new THREE.OrbitControls(camera, renderer.domElement);
-}
-
-function clearScene() {
- while(scene.children.length > 0){
- scene.remove(scene.children[0]);
- }
-}
-
-function renderContainer(container) {
- // 绘制集装箱线框
- const geometry = new THREE.BoxGeometry(
- container.Length,
- container.Width,
- container.Height
- );
- const edges = new THREE.EdgesGeometry(geometry);
- const line = new THREE.LineSegments(
- edges,
- new THREE.LineBasicMaterial({ color: 0x000000 })
- );
- scene.add(line);
-}
-
-function renderBoxes(placements) {
- placements.forEach(pos => {
- const geometry = new THREE.BoxGeometry(pos.Length, pos.Width, pos.Height);
- const material = new THREE.MeshPhongMaterial({
- color: 0x00ff00,
- transparent: true,
- opacity: 0.7
- });
- const cube = new THREE.Mesh(geometry, material);
- cube.position.set(
- pos.X + pos.Length/2,
- pos.Y + pos.Width/2,
- pos.Z + pos.Height/2
- );
- scene.add(cube);
- });
-}
-
-async function calculate() {
- // 获取输入值
- const container = {
- Length: parseFloat(document.getElementById('contLength').value),
- Width: parseFloat(document.getElementById('contWidth').value),
- Height: parseFloat(document.getElementById('contHeight').value),
- MaxWeight: parseFloat(document.getElementById('contWeight').value)
- };
-
- const box = {
- Length: parseFloat(document.getElementById('boxLength').value),
- Width: parseFloat(document.getElementById('boxWidth').value),
- Height: parseFloat(document.getElementById('boxHeight').value),
- Weight: parseFloat(document.getElementById('boxWeight').value)
- };
-
- // 发送请求
- const response = await fetch('/api/calculate', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ container, box })
- });
-
- const result = await response.json();
-
- // 更新可视化
- clearScene();
- renderContainer(container);
- renderBoxes(result.placements);
-}
-
-// 初始化
-initThree();
-animate();
-
-function animate() {
- requestAnimationFrame(animate);
- controls.update();
- renderer.render(scene, camera);
-}
\ No newline at end of file
diff --git a/templates/404.html b/templates/404.html
deleted file mode 100644
index 81362d3..0000000
--- a/templates/404.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Sorry, the page you are looking for does not exist.
- Go back to the homepage
-
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
deleted file mode 100644
index a1806d7..0000000
--- a/templates/index.html
+++ /dev/null
@@ -1,196 +0,0 @@
-
-
-
-
-
-
- 装货方案计算
-
-
-
-
-
-
-
-
装货方案计算
-
-
输入纸箱信息
-
-
输入集装箱信息
-
-
计算最优方案
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/templates/login.html b/templates/login.html
deleted file mode 100644
index c2e13f0..0000000
--- a/templates/login.html
+++ /dev/null
@@ -1 +0,0 @@
-login
\ No newline at end of file