From f814a258e1d2afe472566bcccf7c3cc5011a7132 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Tue, 25 Aug 2020 11:11:38 -0400 Subject: [PATCH 1/2] Aded random item and container generation buttons. Aded modal messaging to ensure the user selects at least one algorithm, item, and container before attempting to pack. --- .../Views/Home/Index.cshtml | 27 +++- .../wwwroot/js/container-packing.js | 124 +++++++++++++----- 2 files changed, 117 insertions(+), 34 deletions(-) diff --git a/src/CromulentBisgetti.DemoApp/Views/Home/Index.cshtml b/src/CromulentBisgetti.DemoApp/Views/Home/Index.cshtml index 52f80bb..bcf92a1 100644 --- a/src/CromulentBisgetti.DemoApp/Views/Home/Index.cshtml +++ b/src/CromulentBisgetti.DemoApp/Views/Home/Index.cshtml @@ -1,4 +1,4 @@ -@{ +@{ Layout = null; } @@ -99,7 +99,10 @@

Items to Pack

-
+
+ + +
@@ -150,7 +153,8 @@

Containers

- + +
@@ -255,5 +259,22 @@
+ + + \ No newline at end of file diff --git a/src/CromulentBisgetti.DemoApp/wwwroot/js/container-packing.js b/src/CromulentBisgetti.DemoApp/wwwroot/js/container-packing.js index 395f8fd..3ba99b0 100644 --- a/src/CromulentBisgetti.DemoApp/wwwroot/js/container-packing.js +++ b/src/CromulentBisgetti.DemoApp/wwwroot/js/container-packing.js @@ -18,7 +18,7 @@ function InitializeDrawing() { var container = $('#drawing-container'); scene = new THREE.Scene(); - camera = new THREE.PerspectiveCamera( 50, window.innerWidth/window.innerHeight, 0.1, 1000 ); + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000); camera.lookAt(scene.position); //var axisHelper = new THREE.AxisHelper( 5 ); @@ -26,20 +26,20 @@ function InitializeDrawing() { // LIGHT var light = new THREE.PointLight(0xffffff); - light.position.set(0,150,100); + light.position.set(0, 150, 100); scene.add(light); // Get the item stuff ready. - itemMaterial = new THREE.MeshNormalMaterial( { transparent: true, opacity: 0.6 } ); + itemMaterial = new THREE.MeshNormalMaterial({ transparent: true, opacity: 0.6 }); - renderer = new THREE.WebGLRenderer( { antialias: true } ); // WebGLRenderer CanvasRenderer - renderer.setClearColor( 0xf0f0f0 ); - renderer.setPixelRatio( window.devicePixelRatio ); - renderer.setSize( window.innerWidth / 2, window.innerHeight / 2); - container.append( renderer.domElement ); + renderer = new THREE.WebGLRenderer({ antialias: true }); // WebGLRenderer CanvasRenderer + renderer.setClearColor(0xf0f0f0); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth / 2, window.innerHeight / 2); + container.append(renderer.domElement); - controls = new THREE.OrbitControls( camera, renderer.domElement ); - window.addEventListener( 'resize', onWindowResize, false ); + controls = new THREE.OrbitControls(camera, renderer.domElement); + window.addEventListener('resize', onWindowResize, false); animate(); }; @@ -47,16 +47,16 @@ function InitializeDrawing() { function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); - renderer.setSize( window.innerWidth / 2, window.innerHeight / 2 ); + renderer.setSize(window.innerWidth / 2, window.innerHeight / 2); } // function animate() { - requestAnimationFrame( animate ); + requestAnimationFrame(animate); controls.update(); render(); } function render() { - renderer.render( scene, camera ); + renderer.render(scene, camera); } var ViewModel = function () { @@ -90,7 +90,7 @@ var ViewModel = function () { self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1004, Name: 'Item5', Length: 17, Width: 8, Height: 6, Quantity: 1 })); self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1005, Name: 'Item6', Length: 3, Width: 3, Height: 2, Quantity: 2 })); }; - + self.GenerateContainers = function () { self.Containers([]); self.Containers.push(ko.mapping.fromJS({ ID: 1000, Name: 'Box1', Length: 15, Width: 13, Height: 9, AlgorithmPackingResults: [] })); @@ -108,6 +108,49 @@ var ViewModel = function () { self.Containers.push(ko.mapping.fromJS({ ID: 1012, Name: 'Box13', Length: 60, Width: 60, Height: 60, AlgorithmPackingResults: [] })); }; + self.RandomizeItemsToPack = function () { + self.ItemsToPack([]); + + var itemCount = getRandomInt(1002, 1012) + + for (var x = 1000; x < itemCount; x++) { + var length = getRandomInt(1, 14); + var height = getRandomInt(1, 10); + var width = getRandomInt(1, 10); + var quantity = getRandomInt(1, 10); + var name = "({0}) {1}'L X {2}'W X {3}'H Boxes".format(quantity, length, width, height); + self.ItemsToPack.push(ko.mapping.fromJS({ ID: x, Name: name, Length: length, Width: width, Height: height, Quantity: quantity })); + } + }; + + self.RandomizeContainers = function () { + self.Containers([]); + var containerCount = getRandomInt(1002, 1020) + + for (var x = 1000; x < containerCount; x++) { + var length = getRandomInt(10, 53); + var height = getRandomInt(7, 11); + var width = getRandomInt(7, 11); + var quantity = getRandomInt(1, 10); + var name = "One {0}'L X {1}'W X {2}'H Container".format(length, width, height); + self.Containers.push(ko.mapping.fromJS({ ID: x, Name: name, Length: length, Width: width, Height: height, AlgorithmPackingResults: [] })); + } + }; + + function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min)) + min; + } + + String.prototype.format = function () { + var args = arguments; + return this.replace(/{(\d+)}/g, function (match, number) { + return typeof args[number] != 'undefined' + ? args[number] + : match + ; + }); + }; + self.AddAlgorithmToUse = function () { var algorithmID = $('#algorithm-select option:selected').val(); var algorithmName = $('#algorithm-select option:selected').text(); @@ -151,7 +194,7 @@ var ViewModel = function () { self.AlgorithmsToUse().forEach(algorithm => { algorithmsToUse.push(algorithm.AlgorithmID); }); - + var itemsToPack = []; self.ItemsToPack().forEach(item => { @@ -162,10 +205,10 @@ var ViewModel = function () { Dim3: item.Height(), Quantity: item.Quantity() }; - + itemsToPack.push(itemToPack); }); - + var containers = []; // Send a packing request for each container in the list. @@ -179,14 +222,33 @@ var ViewModel = function () { containers.push(containerToUse); }); - + + // Some validation before packing. + if (algorithmsToUse.length == 0) { + $("#messageContent").text("Please select an algorithm to use for packing."); + $('#messageModal').modal('show'); + return; + } + + if (itemsToPack.length == 0) { + $("#messageContent").text("Please add one or more items to pack."); + $('#messageModal').modal('show'); + return; + } + + if (containers.length == 0) { + $("#messageContent").text("Please add one or more containers to pack items into."); + $('#messageModal').modal('show'); + return; + } + // Build container packing request. var request = { Containers: containers, ItemsToPack: itemsToPack, AlgorithmTypeIDs: algorithmsToUse }; - + PackContainers(JSON.stringify(request)) .then(response => { // Tie this response back to the correct containers. @@ -199,17 +261,17 @@ var ViewModel = function () { }); }); }; - + self.ShowPackingView = function (algorithmPackingResult) { var container = this; var selectedObject = scene.getObjectByName('container'); - scene.remove( selectedObject ); - + scene.remove(selectedObject); + for (var i = 0; i < 1000; i++) { var selectedObject = scene.getObjectByName('cube' + i); scene.remove(selectedObject); } - + camera.position.set(container.Length(), container.Length(), container.Length()); self.ItemsToRender(algorithmPackingResult.PackedItems); @@ -220,12 +282,12 @@ var ViewModel = function () { self.ContainerOriginOffset.z = -1 * container.Width() / 2; var geometry = new THREE.BoxGeometry(container.Length(), container.Height(), container.Width()); - var geo = new THREE.EdgesGeometry( geometry ); // or WireframeGeometry( geometry ) - var mat = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 2 } ); - var wireframe = new THREE.LineSegments( geo, mat ); + var geo = new THREE.EdgesGeometry(geometry); // or WireframeGeometry( geometry ) + var mat = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 2 }); + var wireframe = new THREE.LineSegments(geo, mat); wireframe.position.set(0, 0, 0); wireframe.name = 'container'; - scene.add( wireframe ); + scene.add(wireframe); }; self.AreItemsPacked = function () { @@ -257,14 +319,14 @@ var ViewModel = function () { var cube = new THREE.Mesh(itemGeometry, itemMaterial); cube.position.set(self.ContainerOriginOffset.x + itemOriginOffset.x + self.ItemsToRender()[itemIndex].CoordX, self.ContainerOriginOffset.y + itemOriginOffset.y + self.ItemsToRender()[itemIndex].CoordY, self.ContainerOriginOffset.z + itemOriginOffset.z + self.ItemsToRender()[itemIndex].CoordZ); cube.name = 'cube' + itemIndex; - scene.add( cube ); + scene.add(cube); self.LastItemRenderedIndex(itemIndex); }; self.UnpackItemInRender = function () { var selectedObject = scene.getObjectByName('cube' + self.LastItemRenderedIndex()); - scene.remove( selectedObject ); + scene.remove(selectedObject); self.LastItemRenderedIndex(self.LastItemRenderedIndex() - 1); }; }; @@ -275,7 +337,7 @@ var ItemToPack = function () { this.Length = ''; this.Width = ''; this.Height = '', - this.Quantity = ''; + this.Quantity = ''; } var Container = function () { @@ -288,7 +350,7 @@ var Container = function () { } $(document).ready(() => { - $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').tooltip(); InitializeDrawing(); viewModel = new ViewModel(); From 400da20d82a60e292787fe0cc575282cdc263ba4 Mon Sep 17 00:00:00 2001 From: Usualdosage Date: Tue, 27 Oct 2020 17:05:43 -0400 Subject: [PATCH 2/2] Update README.md --- README.md | 44 ++++++++++++-------------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 11b622a..d2381c1 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,28 @@ ## 3D Container Packing in C# -This is a C# library that can be used to find 3D container packing solutions (also known as 3D bin packing). It includes an implementation of the EB-AFIT packing algorithm originally developed as a master's thesis project by Erhan Baltacıoğlu (EB) at the U.S. Air Force Institute of Technology (AFIT) in 2001. This algorithm is also described in The Distributor's Three-Dimensional Pallet-Packing Problem: A Human Intelligence-Based Heuristic Approach, by Erhan Baltacıoğlu, James T. Moore, and Raymond R. Hill Jr., published in the International Journal of Operational Research in 2006 (volume 1, issue 3). +This is a fork of David Chapman's C# library that can be used to find 3D container packing solutions (also known as 3D bin packing). It includes an implementation of the EB-AFIT packing algorithm originally developed as a master's thesis project by Erhan Baltacıoğlu (EB) at the U.S. Air Force Institute of Technology (AFIT) in 2001 [Link to Abstract](http://betterwaysystems.github.io/packer/reference/AirForceBinPacking.pdf). This algorithm is also described in The Distributor's Three-Dimensional Pallet-Packing Problem: A Human Intelligence-Based Heuristic Approach, by Erhan Baltacıoğlu, James T. Moore, and Raymond R. Hill Jr., published in the International Journal of Operational Research in 2006 (volume 1, issue 3). The EB-AFIT algorithm supports full item rotation and has excellent runtime performance and container utilization. ## Usage -Start by including the ContainerPacking project in your solution. +This extends the original project in the following ways: -Create a list of Container objects, which describes the dimensions of the containers: +* Randomization of packing units and items +* Updates the 3D model to display labels of each item (adds label property) +* Adds gross weight property +* Allows selection of one or more packing units, displaying weight and volume +* Allows selection of stops, so that containers can be loaded according when they need to be removed first - List containers = new List(); - containers.Add(new Container(id, length, width, height)); - ... +Future: -Create a list of items to pack: - - List itemsToPack = new List(); - itemsToPack.Add(new Item(id, dim1, dim2, dim3, quantity)); - ... - -Create a list of algorithm IDs corresponding to the algorithms you would like to use. (Currently EB-AFIT is the only algorithm implemented.) Algorithm IDs are listed in the AlgorithmType enum. - - List algorithms = new List(); - algorithms.Add((int)AlgorithmType.EB_AFIT); - ... - -Call the Pack method on your container list, item list, and algorithm list: - - List result = PackingService.Pack(containers, itemsToPack, algorithms); - -The list of ContainerPackingResults contains a ContainerPackingResult object for each container. Within each ContainerPackingResult is the container ID and a list of AlgorithmPackingResult objects, one for each algorithm requested. Within each algorithm packing result is the name and ID of the algorithm used, a list of items that were successfully packed, a list of items that could not be packed, and a few other packing metrics. The items in the packed list are in pack order and include x, y, and z coordinates and x, y, and z pack dimensions. This information is useful if you want to attach a visualization tool to display the packed items in their proper pack locations and orientations. - -Internally, the Pack() method will try to pack all the containers with all the items using all the requested algorithms in parallel. If you have a list of containers you want to try, but want them to run serially, then you can call Pack() with one container at a time. For example, if you want to run a large set of containers but would like to update the user interface as each one finishes, then you would want to call Pack() multiple times asynchronously and update the UI as each result returns. - -## Demo WebAPI Application - -This project also includes a demo web application that lets the user specify an arbitrary set of items, an arbitrary set of containers, and the packing algorithms to use. AJAX packing requests are sent to the server and handled by a WebAPI controller. Once returned, each pack solution can be viewed in the WebGL visualization tool by clicking the camera icon. - -![Container packing visualization](https://github.com/davidmchapman/3DContainerPacking/blob/master/images/packing-1.gif?raw=true "Container Packing") +* Allow generation of weight heatmap, so loading algorthim favors forward and center loads ## Acknowledgements and Related Projects -This project would not have been possible without the support of Dr. Raymond Hill at the Air Force Institute of Technology. It also leans heavily on the original C code included in Erhan Baltacıoğlu's thesis, which was discovered and resurrected by Bill Knechtel (GitHub user wknechtel), and ported to JavaScript by GitHub user keremdemirer. +This project would not have been possible without the support of Dr. Raymond Hill at the Air Force Institute of Technology. It also leans heavily on the original C code included in Erhan Baltacıoğlu's thesis, which was discovered and resurrected by Bill Knechtel (GitHub user wknechtel), and ported to JavaScript by GitHub user keremdemirer. Also to David Chapman for his work on putting this together in .NET, and letting me carry the torch. + +https://github.com/davidmchapman/3DContainerPacking https://github.com/wknechtel/3d-bin-pack/