Initial commit
This commit is contained in:
commit
d596db7e29
BIN
__debug_bin1038044772.exe
Normal file
BIN
__debug_bin1038044772.exe
Normal file
Binary file not shown.
BIN
__debug_bin3681850586.exe
Normal file
BIN
__debug_bin3681850586.exe
Normal file
Binary file not shown.
33
go.mod
Normal file
33
go.mod
Normal file
@ -0,0 +1,33 @@
|
||||
module container-packing
|
||||
|
||||
go 1.23.5
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.10.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
79
go.sum
Normal file
79
go.sum
Normal file
@ -0,0 +1,79 @@
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
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/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
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/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
166
index.html
Normal file
166
index.html
Normal file
@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>集装箱装箱优化系统</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
||||
<style>
|
||||
body { margin: 0; }
|
||||
#container { width: 100vw; height: 100vh; }
|
||||
#inputPanel {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
background: rgba(255,255,255,0.8);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
font-family: Arial;
|
||||
}
|
||||
#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; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="inputPanel">
|
||||
<h3>参数输入</h3>
|
||||
<div>
|
||||
集装箱尺寸(mm):<br>
|
||||
长:<input type="number" id="conLen" value="12014"><br>
|
||||
宽:<input type="number" id="conWid" value="2337"><br>
|
||||
高:<input type="number" id="conHei" value="2388"><br>
|
||||
</div>
|
||||
<div>
|
||||
纸箱尺寸(mm):<br>
|
||||
长:<input type="number" id="boxLen" value="685"><br>
|
||||
宽:<input type="number" id="boxWid" value="548"><br>
|
||||
高:<input type="number" id="boxHei" value="489"><br>
|
||||
</div>
|
||||
<button onclick="calculate()">开始计算</button>
|
||||
<div id="result"></div>
|
||||
</div>
|
||||
|
||||
<div id="axisInfo" class="axis">视角:前视图</div>
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
let scene, camera, renderer, controls;
|
||||
let containerMesh, boxMeshes = [];
|
||||
let activeCamera = 'front';
|
||||
|
||||
function initThree() {
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 1, 100000);
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.getElementById('container').appendChild(renderer.domElement);
|
||||
|
||||
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||||
camera.position.set(15000, 5000, 15000);
|
||||
controls.update();
|
||||
|
||||
const gridHelper = new THREE.GridHelper(20000, 20, 0x888888, 0x888888);
|
||||
scene.add(gridHelper);
|
||||
|
||||
const axesHelper = new THREE.AxesHelper(5000);
|
||||
scene.add(axesHelper);
|
||||
|
||||
animate();
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
async function calculate() {
|
||||
const data = {
|
||||
container: {
|
||||
length: parseFloat(document.getElementById('conLen').value),
|
||||
width: parseFloat(document.getElementById('conWid').value),
|
||||
height: parseFloat(document.getElementById('conHei').value)
|
||||
},
|
||||
box: {
|
||||
length: parseFloat(document.getElementById('boxLen').value),
|
||||
width: parseFloat(document.getElementById('boxWid').value),
|
||||
height: parseFloat(document.getElementById('boxHei').value)
|
||||
}
|
||||
};
|
||||
|
||||
const response = await fetch('/calculate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
document.getElementById('result').innerHTML = `最优装箱数:${result.count}`;
|
||||
|
||||
scene.remove(containerMesh);
|
||||
boxMeshes.forEach(mesh => scene.remove(mesh));
|
||||
boxMeshes = [];
|
||||
|
||||
const containerGeo = new THREE.BoxGeometry(
|
||||
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 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);
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
switch(e.key) {
|
||||
case '1':
|
||||
camera.position.set(15000, 5000, 15000);
|
||||
activeCamera = 'front';
|
||||
break;
|
||||
case '2':
|
||||
camera.position.set(-15000, 5000, 15000);
|
||||
activeCamera = 'back';
|
||||
break;
|
||||
case '3':
|
||||
camera.position.set(0, 15000, 0);
|
||||
activeCamera = 'top';
|
||||
break;
|
||||
}
|
||||
document.getElementById('axisInfo').innerHTML = `视角:${activeCamera}`;
|
||||
});
|
||||
|
||||
initThree();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
190
main.go
Normal file
190
main.go
Normal file
@ -0,0 +1,190 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
Length float64 `json:"length"`
|
||||
Width float64 `json:"width"`
|
||||
Height float64 `json:"height"`
|
||||
}
|
||||
|
||||
type Box struct {
|
||||
Length float64 `json:"length"`
|
||||
Width float64 `json:"width"`
|
||||
Height float64 `json:"height"`
|
||||
}
|
||||
|
||||
type Placement struct {
|
||||
X, Y, Z float64 `json:"x"`
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
layout := optimizePacking(data.Container, data.Box)
|
||||
count := len(layout)
|
||||
|
||||
response := struct {
|
||||
Count int `json:"count"`
|
||||
Layout []Placement `json:"layout"`
|
||||
}{
|
||||
Count: count,
|
||||
Layout: layout,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
})
|
||||
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
|
||||
func optimizePacking(con Container, box Box) []Placement {
|
||||
var bestLayout []Placement
|
||||
maxCount := 0
|
||||
|
||||
rotations := generateRotations(box)
|
||||
|
||||
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)
|
||||
if total > maxCount && total > 0 {
|
||||
maxCount = total
|
||||
layout := generateLayout(r, xCount, yCount, zCount, strategy)
|
||||
bestLayout = layout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestLayout
|
||||
}
|
||||
|
||||
func generateRotations(box Box) []Box {
|
||||
return []Box{
|
||||
{box.Length, box.Width, box.Height},
|
||||
{box.Length, box.Height, box.Width},
|
||||
{box.Width, box.Length, box.Height},
|
||||
{box.Width, box.Height, box.Length},
|
||||
{box.Height, box.Length, box.Width},
|
||||
{box.Height, box.Width, box.Length},
|
||||
}
|
||||
}
|
||||
|
||||
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{
|
||||
X: x * r.Length,
|
||||
Y: z * r.Height,
|
||||
Z: y * r.Width,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
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{
|
||||
X: x * r.Length,
|
||||
Y: z * r.Height,
|
||||
Z: y * r.Width,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
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{
|
||||
X: x * r.Width,
|
||||
Y: z * r.Height,
|
||||
Z: y * r.Length,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
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{
|
||||
X: x * r.Width,
|
||||
Y: z * r.Height,
|
||||
Z: y * r.Length,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
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{
|
||||
X: x * r.Width,
|
||||
Y: z * r.Length,
|
||||
Z: y * r.Height,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
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{
|
||||
X: x * r.Width,
|
||||
Y: z * r.Length,
|
||||
Z: y * r.Height,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return layout
|
||||
}
|
||||
66
static/index.html
Normal file
66
static/index.html
Normal file
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>集装箱装箱计算器</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<style>
|
||||
#container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
#controls {
|
||||
width: 300px;
|
||||
padding: 20px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.input-group {
|
||||
margin: 10px 0;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
#visualization {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="controls">
|
||||
<h2>集装箱参数</h2>
|
||||
<div class="input-group">
|
||||
<label>长度: <input type="number" id="contLength" step="0.1"></label>
|
||||
<label>宽度: <input type="number" id="contWidth" step="0.1"></label>
|
||||
<label>高度: <input type="number" id="contHeight" step="0.1"></label>
|
||||
<label>承重上限: <input type="number" id="contWeight" step="0.1"></label>
|
||||
</div>
|
||||
|
||||
<h2>纸箱参数</h2>
|
||||
<div class="input-group">
|
||||
<label>长度: <input type="number" id="boxLength" step="0.1"></label>
|
||||
<label>宽度: <input type="number" id="boxWidth" step="0.1"></label>
|
||||
<label>高度: <input type="number" id="boxHeight" step="0.1"></label>
|
||||
<label>重量: <input type="number" id="boxWeight" step="0.1"></label>
|
||||
</div>
|
||||
|
||||
<button onclick="calculate()">开始计算</button>
|
||||
</div>
|
||||
|
||||
<div id="visualization"></div>
|
||||
</div>
|
||||
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
107
static/main.js
Normal file
107
static/main.js
Normal file
@ -0,0 +1,107 @@
|
||||
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);
|
||||
}
|
||||
4
templates/404.html
Normal file
4
templates/404.html
Normal file
@ -0,0 +1,4 @@
|
||||
</h1>
|
||||
<p>Sorry, the page you are looking for does not exist.</p>
|
||||
<a href="/">Go back to the homepage</a>
|
||||
</div>
|
||||
196
templates/index.html
Normal file
196
templates/index.html
Normal file
@ -0,0 +1,196 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>装货方案计算</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-100">
|
||||
<div class="container mx-auto p-4">
|
||||
<h1 class="text-3xl font-bold text-center text-blue-600 mb-8">装货方案计算</h1>
|
||||
<div class="bg-white p-6 rounded-md shadow-md">
|
||||
<h2 class="text-xl font-bold mb-4">输入纸箱信息</h2>
|
||||
<div id="cartons" class="mb-4">
|
||||
<div class="flex mb-2">
|
||||
<input type="number" placeholder="长" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="宽" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="高" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="重量" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<button onclick="addCartonInput()" class="bg-blue-500 text-white p-2 rounded-md hover:bg-blue-600">添加纸箱</button>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-xl font-bold mb-4">输入集装箱信息</h2>
|
||||
<div id="containers" class="mb-4">
|
||||
<div class="flex mb-2">
|
||||
<input type="number" placeholder="长" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="宽" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="高" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="最大载重" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<button onclick="addContainerInput()" class="bg-blue-500 text-white p-2 rounded-md hover:bg-blue-600">添加集装箱</button>
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="calculate()" class="bg-green-500 text-white p-2 rounded-md hover:bg-green-600">计算最优方案</button>
|
||||
</div>
|
||||
<div id="result" class="mt-8 bg-white p-6 rounded-md shadow-md"></div>
|
||||
<div class="mt-8 bg-white p-6 rounded-md shadow-md">
|
||||
<h3 class="text-xl font-bold mb-4">3D摆放视图</h3>
|
||||
<div id="3d-view"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function addCartonInput() {
|
||||
const cartonsDiv = document.getElementById('cartons');
|
||||
const newCarton = document.createElement('div');
|
||||
newCarton.classList.add('flex', 'mb-2');
|
||||
newCarton.innerHTML = `
|
||||
<input type="number" placeholder="长" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="宽" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="高" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="重量" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
`;
|
||||
cartonsDiv.appendChild(newCarton);
|
||||
}
|
||||
|
||||
function addContainerInput() {
|
||||
const containersDiv = document.getElementById('containers');
|
||||
const newContainer = document.createElement('div');
|
||||
newContainer.classList.add('flex', 'mb-2');
|
||||
newContainer.innerHTML = `
|
||||
<input type="number" placeholder="长" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="宽" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="高" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
<input type="number" placeholder="最大载重" class="border border-gray-300 p-2 mr-2 w-1/4 rounded-md">
|
||||
`;
|
||||
containersDiv.appendChild(newContainer);
|
||||
}
|
||||
|
||||
function calculate() {
|
||||
const cartonsInputs = document.querySelectorAll('#cartons input');
|
||||
const containersInputs = document.querySelectorAll('#containers input');
|
||||
|
||||
let cartons = [];
|
||||
for (let i = 0; i < cartonsInputs.length; i += 4) {
|
||||
const length = parseFloat(cartonsInputs[i].value);
|
||||
const width = parseFloat(cartonsInputs[i + 1].value);
|
||||
const height = parseFloat(cartonsInputs[i + 2].value);
|
||||
const weight = parseFloat(cartonsInputs[i + 3].value);
|
||||
if (!isNaN(length) && !isNaN(width) && !isNaN(height) && !isNaN(weight)) {
|
||||
cartons.push({ length, width, height, weight });
|
||||
}
|
||||
}
|
||||
|
||||
let containers = [];
|
||||
for (let i = 0; i < containersInputs.length; i += 4) {
|
||||
const length = parseFloat(containersInputs[i].value);
|
||||
const width = parseFloat(containersInputs[i + 1].value);
|
||||
const height = parseFloat(containersInputs[i + 2].value);
|
||||
const maxWeight = parseFloat(containersInputs[i + 3].value);
|
||||
if (!isNaN(length) && !isNaN(width) && !isNaN(height) && !isNaN(maxWeight)) {
|
||||
containers.push({ length, width, height, maxWeight });
|
||||
}
|
||||
}
|
||||
|
||||
fetch('/calculate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ cartons, containers })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const resultDiv = document.getElementById('result');
|
||||
resultDiv.innerHTML = '';
|
||||
data.forEach((container, index) => {
|
||||
const containerDiv = document.createElement('div');
|
||||
containerDiv.classList.add('border', 'border-gray-300', 'p-4', 'mb-4', 'rounded-md');
|
||||
containerDiv.innerHTML = `
|
||||
<h3 class="text-lg font-bold">集装箱 ${index + 1}</h3>
|
||||
<p>当前重量: ${container.currentWeight}</p>
|
||||
<h4 class="text-md font-bold">装入的纸箱:</h4>
|
||||
<ul>
|
||||
${container.cartons.map(carton => `<li>长: ${carton.length}, 宽: ${carton.width}, 高: ${carton.height}, 重量: ${carton.weight}</li>`).join('')}
|
||||
</ul>
|
||||
`;
|
||||
resultDiv.appendChild(containerDiv);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script>
|
||||
let scene, camera, renderer;
|
||||
let containerGroup;
|
||||
|
||||
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, 500);
|
||||
document.getElementById('3d-view').appendChild(renderer.domElement);
|
||||
|
||||
// 灯光设置
|
||||
const light = new THREE.DirectionalLight(0xffffff, 1);
|
||||
light.position.set(10, 10, 10);
|
||||
scene.add(light);
|
||||
scene.add(new THREE.AmbientLight(0x404040));
|
||||
|
||||
camera.position.z = 15;
|
||||
camera.position.y = 10;
|
||||
camera.lookAt(0, 0, 0);
|
||||
}
|
||||
|
||||
function renderContainer(container) {
|
||||
if(containerGroup) scene.remove(containerGroup);
|
||||
containerGroup = new THREE.Group();
|
||||
|
||||
// 绘制集装箱
|
||||
const geometry = new THREE.BoxGeometry(
|
||||
container.length,
|
||||
container.height,
|
||||
container.width
|
||||
);
|
||||
const edges = new THREE.EdgesGeometry(geometry);
|
||||
const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x000000 }));
|
||||
containerGroup.add(line);
|
||||
|
||||
// 绘制纸箱
|
||||
container.cartons.forEach(c => {
|
||||
const box = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(c.length, c.height, c.width),
|
||||
new THREE.MeshPhongMaterial({ color: 0x2194f3 })
|
||||
);
|
||||
box.position.set(c.position[0], c.position[1], c.position[2]);
|
||||
containerGroup.add(box);
|
||||
});
|
||||
|
||||
scene.add(containerGroup);
|
||||
stopAnimation();
|
||||
animate();
|
||||
}
|
||||
|
||||
let animationId;
|
||||
function animate() {
|
||||
animationId = requestAnimationFrame(animate);
|
||||
renderer.render(scene, camera);
|
||||
containerGroup.rotation.y += 0.005;
|
||||
}
|
||||
function stopAnimation() {
|
||||
if(animationId) cancelAnimationFrame(animationId);
|
||||
}
|
||||
|
||||
// 初始化3D场景
|
||||
window.addEventListener('load', initThree);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1
templates/login.html
Normal file
1
templates/login.html
Normal file
@ -0,0 +1 @@
|
||||
login
|
||||
Loading…
x
Reference in New Issue
Block a user