├── .gitignore ├── LICENSE.md ├── README.md ├── images └── packing-1.gif └── src ├── CromulentBisgetti.ContainerPacking.sln ├── CromulentBisgetti.ContainerPacking ├── Algorithms │ ├── AlgorithmBase.cs │ ├── AlgorithmType.cs │ ├── EB_AFIT.cs │ └── IPackingAlgorithm.cs ├── CromulentBisgetti.ContainerPacking.csproj ├── Entities │ ├── AlgorithmPackingResult.cs │ ├── Container.cs │ ├── ContainerPackingResult.cs │ └── Item.cs └── PackingService.cs ├── CromulentBisgetti.ContainerPackingTests ├── ContainerPackingTests.cs ├── CromulentBisgetti.ContainerPackingTests.csproj └── DataFiles │ └── ORLibrary.txt └── CromulentBisgetti.DemoApp ├── Controllers ├── ContainerPackingController.cs └── HomeController.cs ├── CromulentBisgetti.DemoApp.csproj ├── Models └── ContainerPackingRequest.cs ├── Program.cs ├── Properties └── launchSettings.json ├── Startup.cs ├── Views └── Home │ ├── Index.cshtml │ └── Index.cshtml.cs ├── appsettings.Development.json ├── appsettings.json └── wwwroot ├── css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 └── vendor │ └── bootstrap.min.css └── js ├── container-packing.js └── vendor ├── bootstrap.min.js ├── jquery.js ├── ko-mapping.js ├── ko.js ├── orbit-controls.js └── three-min.js /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 davidmchapman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 3D Container Packing in C# 2 | [![NuGet](https://img.shields.io/nuget/v/CromulentBisgetti.ContainerPacking.svg)](https://www.nuget.org/packages/CromulentBisgetti.ContainerPacking) 3 | 4 | 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). 5 | 6 | The EB-AFIT algorithm supports full item rotation and has excellent runtime performance and container utilization. 7 | 8 | ## Usage 9 | 10 | Start by including the ContainerPacking project in your solution. 11 | 12 | Create a list of Container objects, which describes the dimensions of the containers: 13 | 14 | List containers = new List(); 15 | containers.Add(new Container(id, length, width, height)); 16 | ... 17 | 18 | Create a list of items to pack: 19 | 20 | List itemsToPack = new List(); 21 | itemsToPack.Add(new Item(id, dim1, dim2, dim3, quantity)); 22 | ... 23 | 24 | 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. 25 | 26 | List algorithms = new List(); 27 | algorithms.Add((int)AlgorithmType.EB_AFIT); 28 | ... 29 | 30 | Call the Pack method on your container list, item list, and algorithm list: 31 | 32 | List result = PackingService.Pack(containers, itemsToPack, algorithms); 33 | 34 | 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. 35 | 36 | 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. 37 | 38 | ## Demo WebAPI Application 39 | 40 | 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. 41 | 42 | ![Container packing visualization](https://github.com/davidmchapman/3DContainerPacking/blob/master/images/packing-1.gif?raw=true "Container Packing") 43 | 44 | ## Acknowledgements and Related Projects 45 | 46 | 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. 47 | 48 | https://github.com/wknechtel/3d-bin-pack/ 49 | 50 | https://github.com/keremdemirer/3dbinpackingjs 51 | -------------------------------------------------------------------------------- /images/packing-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmchapman/3DContainerPacking/4a6102f5b6bb67a230a2d12326052b3da6341b51/images/packing-1.gif -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2042 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CromulentBisgetti.ContainerPacking", "CromulentBisgetti.ContainerPacking\CromulentBisgetti.ContainerPacking.csproj", "{49D6AD56-1545-4864-AC6E-80A8922742DF}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CromulentBisgetti.ContainerPackingTests", "CromulentBisgetti.ContainerPackingTests\CromulentBisgetti.ContainerPackingTests.csproj", "{6D1B78A9-06D7-4461-A1C0-C3C09DA54317}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CromulentBisgetti.DemoApp", "CromulentBisgetti.DemoApp\CromulentBisgetti.DemoApp.csproj", "{EB1D3F02-BC70-41B6-A9E0-DD00B3266419}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {49D6AD56-1545-4864-AC6E-80A8922742DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {49D6AD56-1545-4864-AC6E-80A8922742DF}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {49D6AD56-1545-4864-AC6E-80A8922742DF}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {49D6AD56-1545-4864-AC6E-80A8922742DF}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {6D1B78A9-06D7-4461-A1C0-C3C09DA54317}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {6D1B78A9-06D7-4461-A1C0-C3C09DA54317}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {6D1B78A9-06D7-4461-A1C0-C3C09DA54317}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {6D1B78A9-06D7-4461-A1C0-C3C09DA54317}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {EB1D3F02-BC70-41B6-A9E0-DD00B3266419}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {EB1D3F02-BC70-41B6-A9E0-DD00B3266419}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {EB1D3F02-BC70-41B6-A9E0-DD00B3266419}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {EB1D3F02-BC70-41B6-A9E0-DD00B3266419}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {EFD0501E-3C48-42A7-830E-9FFA9FD9F985} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Algorithms/AlgorithmBase.cs: -------------------------------------------------------------------------------- 1 | using CromulentBisgetti.ContainerPacking.Entities; 2 | using System.Collections.Generic; 3 | 4 | namespace CromulentBisgetti.ContainerPacking.Algorithms 5 | { 6 | public abstract class AlgorithmBase 7 | { 8 | public abstract ContainerPackingResult Run(Container container, List items); 9 | } 10 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Algorithms/AlgorithmType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CromulentBisgetti.ContainerPacking.Algorithms 9 | { 10 | [DataContract] 11 | public enum AlgorithmType 12 | { 13 | /// 14 | /// The EB-AFIT packing algorithm type. 15 | /// 16 | [DataMember] 17 | EB_AFIT = 1 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Algorithms/EB_AFIT.cs: -------------------------------------------------------------------------------- 1 | using CromulentBisgetti.ContainerPacking.Entities; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace CromulentBisgetti.ContainerPacking.Algorithms 7 | { 8 | /// 9 | /// A 3D bin packing algorithm originally ported from https://github.com/keremdemirer/3dbinpackingjs, 10 | /// which itself was a JavaScript port of https://github.com/wknechtel/3d-bin-pack/, which is a C reconstruction 11 | /// of a novel algorithm developed in a U.S. Air Force master's thesis by Erhan Baltacioglu in 2001. 12 | /// 13 | public class EB_AFIT : IPackingAlgorithm 14 | { 15 | #region Public Methods 16 | 17 | /// 18 | /// Runs the packing algorithm. 19 | /// 20 | /// The container to pack items into. 21 | /// The items to pack. 22 | /// The bin packing result. 23 | public AlgorithmPackingResult Run(Container container, List items) 24 | { 25 | Initialize(container, items); 26 | ExecuteIterations(container); 27 | Report(container); 28 | 29 | AlgorithmPackingResult result = new AlgorithmPackingResult(); 30 | result.AlgorithmID = (int)AlgorithmType.EB_AFIT; 31 | result.AlgorithmName = "EB-AFIT"; 32 | 33 | for (int i = 1; i <= itemsToPackCount; i++) 34 | { 35 | itemsToPack[i].Quantity = 1; 36 | 37 | if (!itemsToPack[i].IsPacked) 38 | { 39 | result.UnpackedItems.Add(itemsToPack[i]); 40 | } 41 | } 42 | 43 | result.PackedItems = itemsPackedInOrder; 44 | 45 | 46 | 47 | if (result.UnpackedItems.Count == 0) 48 | { 49 | result.IsCompletePack = true; 50 | } 51 | 52 | return result; 53 | } 54 | 55 | #endregion Public Methods 56 | 57 | #region Private Variables 58 | 59 | private List itemsToPack; 60 | private List itemsPackedInOrder; 61 | private List layers; 62 | private ContainerPackingResult result; 63 | 64 | private ScrapPad scrapfirst; 65 | private ScrapPad smallestZ; 66 | private ScrapPad trash; 67 | 68 | private bool evened; 69 | private bool hundredPercentPacked = false; 70 | private bool layerDone; 71 | private bool packing; 72 | private bool packingBest = false; 73 | private bool quit = false; 74 | 75 | private int bboxi; 76 | private int bestIteration; 77 | private int bestVariant; 78 | private int boxi; 79 | private int cboxi; 80 | private int layerListLen; 81 | private int packedItemCount; 82 | private int x; 83 | 84 | private decimal bbfx; 85 | private decimal bbfy; 86 | private decimal bbfz; 87 | private decimal bboxx; 88 | private decimal bboxy; 89 | private decimal bboxz; 90 | private decimal bfx; 91 | private decimal bfy; 92 | private decimal bfz; 93 | private decimal boxx; 94 | private decimal boxy; 95 | private decimal boxz; 96 | private decimal cboxx; 97 | private decimal cboxy; 98 | private decimal cboxz; 99 | private decimal layerinlayer; 100 | private decimal layerThickness; 101 | private decimal lilz; 102 | private decimal packedVolume; 103 | private decimal packedy; 104 | private decimal prelayer; 105 | private decimal prepackedy; 106 | private decimal preremainpy; 107 | private decimal px; 108 | private decimal py; 109 | private decimal pz; 110 | private decimal remainpy; 111 | private decimal remainpz; 112 | private decimal itemsToPackCount; 113 | private decimal totalItemVolume; 114 | private decimal totalContainerVolume; 115 | 116 | #endregion Private Variables 117 | 118 | #region Private Methods 119 | 120 | /// 121 | /// Analyzes each unpacked box to find the best fitting one to the empty space given. 122 | /// 123 | private void AnalyzeBox(decimal hmx, decimal hy, decimal hmy, decimal hz, decimal hmz, decimal dim1, decimal dim2, decimal dim3) 124 | { 125 | if (dim1 <= hmx && dim2 <= hmy && dim3 <= hmz) 126 | { 127 | if (dim2 <= hy) 128 | { 129 | if (hy - dim2 < bfy) 130 | { 131 | boxx = dim1; 132 | boxy = dim2; 133 | boxz = dim3; 134 | bfx = hmx - dim1; 135 | bfy = hy - dim2; 136 | bfz = Math.Abs(hz - dim3); 137 | boxi = x; 138 | } 139 | else if (hy - dim2 == bfy && hmx - dim1 < bfx) 140 | { 141 | boxx = dim1; 142 | boxy = dim2; 143 | boxz = dim3; 144 | bfx = hmx - dim1; 145 | bfy = hy - dim2; 146 | bfz = Math.Abs(hz - dim3); 147 | boxi = x; 148 | } 149 | else if (hy - dim2 == bfy && hmx - dim1 == bfx && Math.Abs(hz - dim3) < bfz) 150 | { 151 | boxx = dim1; 152 | boxy = dim2; 153 | boxz = dim3; 154 | bfx = hmx - dim1; 155 | bfy = hy - dim2; 156 | bfz = Math.Abs(hz - dim3); 157 | boxi = x; 158 | } 159 | } 160 | else 161 | { 162 | if (dim2 - hy < bbfy) 163 | { 164 | bboxx = dim1; 165 | bboxy = dim2; 166 | bboxz = dim3; 167 | bbfx = hmx - dim1; 168 | bbfy = dim2 - hy; 169 | bbfz = Math.Abs(hz - dim3); 170 | bboxi = x; 171 | } 172 | else if (dim2 - hy == bbfy && hmx - dim1 < bbfx) 173 | { 174 | bboxx = dim1; 175 | bboxy = dim2; 176 | bboxz = dim3; 177 | bbfx = hmx - dim1; 178 | bbfy = dim2 - hy; 179 | bbfz = Math.Abs(hz - dim3); 180 | bboxi = x; 181 | } 182 | else if (dim2 - hy == bbfy && hmx - dim1 == bbfx && Math.Abs(hz - dim3) < bbfz) 183 | { 184 | bboxx = dim1; 185 | bboxy = dim2; 186 | bboxz = dim3; 187 | bbfx = hmx - dim1; 188 | bbfy = dim2 - hy; 189 | bbfz = Math.Abs(hz - dim3); 190 | bboxi = x; 191 | } 192 | } 193 | } 194 | } 195 | 196 | /// 197 | /// After finding each box, the candidate boxes and the condition of the layer are examined. 198 | /// 199 | private void CheckFound() 200 | { 201 | evened = false; 202 | 203 | if (boxi != 0) 204 | { 205 | cboxi = boxi; 206 | cboxx = boxx; 207 | cboxy = boxy; 208 | cboxz = boxz; 209 | } 210 | else 211 | { 212 | if ((bboxi > 0) && (layerinlayer != 0 || (smallestZ.Pre == null && smallestZ.Post == null))) 213 | { 214 | if (layerinlayer == 0) 215 | { 216 | prelayer = layerThickness; 217 | lilz = smallestZ.CumZ; 218 | } 219 | 220 | cboxi = bboxi; 221 | cboxx = bboxx; 222 | cboxy = bboxy; 223 | cboxz = bboxz; 224 | layerinlayer = layerinlayer + bboxy - layerThickness; 225 | layerThickness = bboxy; 226 | } 227 | else 228 | { 229 | if (smallestZ.Pre == null && smallestZ.Post == null) 230 | { 231 | layerDone = true; 232 | } 233 | else 234 | { 235 | evened = true; 236 | 237 | if (smallestZ.Pre == null) 238 | { 239 | trash = smallestZ.Post; 240 | smallestZ.CumX = smallestZ.Post.CumX; 241 | smallestZ.CumZ = smallestZ.Post.CumZ; 242 | smallestZ.Post = smallestZ.Post.Post; 243 | if (smallestZ.Post != null) 244 | { 245 | smallestZ.Post.Pre = smallestZ; 246 | } 247 | } 248 | else if (smallestZ.Post == null) 249 | { 250 | smallestZ.Pre.Post = null; 251 | smallestZ.Pre.CumX = smallestZ.CumX; 252 | } 253 | else 254 | { 255 | if (smallestZ.Pre.CumZ == smallestZ.Post.CumZ) 256 | { 257 | smallestZ.Pre.Post = smallestZ.Post.Post; 258 | 259 | if (smallestZ.Post.Post != null) 260 | { 261 | smallestZ.Post.Post.Pre = smallestZ.Pre; 262 | } 263 | 264 | smallestZ.Pre.CumX = smallestZ.Post.CumX; 265 | } 266 | else 267 | { 268 | smallestZ.Pre.Post = smallestZ.Post; 269 | smallestZ.Post.Pre = smallestZ.Pre; 270 | 271 | if (smallestZ.Pre.CumZ < smallestZ.Post.CumZ) 272 | { 273 | smallestZ.Pre.CumX = smallestZ.CumX; 274 | } 275 | } 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | /// 283 | /// Executes the packing algorithm variants. 284 | /// 285 | private void ExecuteIterations(Container container) 286 | { 287 | int itelayer; 288 | int layersIndex; 289 | decimal bestVolume = 0.0M; 290 | 291 | for (int containerOrientationVariant = 1; (containerOrientationVariant <= 6) && !quit; containerOrientationVariant++) 292 | { 293 | switch (containerOrientationVariant) 294 | { 295 | case 1: 296 | px = container.Length; py = container.Height; pz = container.Width; 297 | break; 298 | 299 | case 2: 300 | px = container.Width; py = container.Height; pz = container.Length; 301 | break; 302 | 303 | case 3: 304 | px = container.Width; py = container.Length; pz = container.Height; 305 | break; 306 | 307 | case 4: 308 | px = container.Height; py = container.Length; pz = container.Width; 309 | break; 310 | 311 | case 5: 312 | px = container.Length; py = container.Width; pz = container.Height; 313 | break; 314 | 315 | case 6: 316 | px = container.Height; py = container.Width; pz = container.Length; 317 | break; 318 | } 319 | 320 | layers.Add(new Layer { LayerEval = -1 }); 321 | ListCanditLayers(); 322 | layers = layers.OrderBy(l => l.LayerEval).ToList(); 323 | 324 | for (layersIndex = 1; (layersIndex <= layerListLen) && !quit; layersIndex++) 325 | { 326 | packedVolume = 0.0M; 327 | packedy = 0; 328 | packing = true; 329 | layerThickness = layers[layersIndex].LayerDim; 330 | itelayer = layersIndex; 331 | remainpy = py; 332 | remainpz = pz; 333 | packedItemCount = 0; 334 | 335 | for (x = 1; x <= itemsToPackCount; x++) 336 | { 337 | itemsToPack[x].IsPacked = false; 338 | } 339 | 340 | do 341 | { 342 | layerinlayer = 0; 343 | layerDone = false; 344 | 345 | PackLayer(); 346 | 347 | packedy = packedy + layerThickness; 348 | remainpy = py - packedy; 349 | 350 | if (layerinlayer != 0 && !quit) 351 | { 352 | prepackedy = packedy; 353 | preremainpy = remainpy; 354 | remainpy = layerThickness - prelayer; 355 | packedy = packedy - layerThickness + prelayer; 356 | remainpz = lilz; 357 | layerThickness = layerinlayer; 358 | layerDone = false; 359 | 360 | PackLayer(); 361 | 362 | packedy = prepackedy; 363 | remainpy = preremainpy; 364 | remainpz = pz; 365 | } 366 | 367 | FindLayer(remainpy); 368 | } while (packing && !quit); 369 | 370 | if ((packedVolume > bestVolume) && !quit) 371 | { 372 | bestVolume = packedVolume; 373 | bestVariant = containerOrientationVariant; 374 | bestIteration = itelayer; 375 | } 376 | 377 | if (hundredPercentPacked) break; 378 | } 379 | 380 | if (hundredPercentPacked) break; 381 | 382 | if ((container.Length == container.Height) && (container.Height == container.Width)) containerOrientationVariant = 6; 383 | 384 | layers = new List(); 385 | } 386 | } 387 | 388 | /// 389 | /// Finds the most proper boxes by looking at all six possible orientations, 390 | /// empty space given, adjacent boxes, and pallet limits. 391 | /// 392 | private void FindBox(decimal hmx, decimal hy, decimal hmy, decimal hz, decimal hmz) 393 | { 394 | int y; 395 | bfx = 32767; 396 | bfy = 32767; 397 | bfz = 32767; 398 | bbfx = 32767; 399 | bbfy = 32767; 400 | bbfz = 32767; 401 | boxi = 0; 402 | bboxi = 0; 403 | 404 | for (y = 1; y <= itemsToPackCount; y = y + itemsToPack[y].Quantity) 405 | { 406 | for (x = y; x < x + itemsToPack[y].Quantity - 1; x++) 407 | { 408 | if (!itemsToPack[x].IsPacked) break; 409 | } 410 | 411 | if (itemsToPack[x].IsPacked) continue; 412 | 413 | if (x > itemsToPackCount) return; 414 | 415 | AnalyzeBox(hmx, hy, hmy, hz, hmz, itemsToPack[x].Dim1, itemsToPack[x].Dim2, itemsToPack[x].Dim3); 416 | 417 | if ((itemsToPack[x].Dim1 == itemsToPack[x].Dim3) && (itemsToPack[x].Dim3 == itemsToPack[x].Dim2)) continue; 418 | 419 | AnalyzeBox(hmx, hy, hmy, hz, hmz, itemsToPack[x].Dim1, itemsToPack[x].Dim3, itemsToPack[x].Dim2); 420 | AnalyzeBox(hmx, hy, hmy, hz, hmz, itemsToPack[x].Dim2, itemsToPack[x].Dim1, itemsToPack[x].Dim3); 421 | AnalyzeBox(hmx, hy, hmy, hz, hmz, itemsToPack[x].Dim2, itemsToPack[x].Dim3, itemsToPack[x].Dim1); 422 | AnalyzeBox(hmx, hy, hmy, hz, hmz, itemsToPack[x].Dim3, itemsToPack[x].Dim1, itemsToPack[x].Dim2); 423 | AnalyzeBox(hmx, hy, hmy, hz, hmz, itemsToPack[x].Dim3, itemsToPack[x].Dim2, itemsToPack[x].Dim1); 424 | } 425 | } 426 | 427 | /// 428 | /// Finds the most proper layer height by looking at the unpacked boxes and the remaining empty space available. 429 | /// 430 | private void FindLayer(decimal thickness) 431 | { 432 | decimal exdim = 0; 433 | decimal dimdif; 434 | decimal dimen2 = 0; 435 | decimal dimen3 = 0; 436 | int y; 437 | int z; 438 | decimal layereval; 439 | decimal eval; 440 | layerThickness = 0; 441 | eval = 1000000; 442 | 443 | for (x = 1; x <= itemsToPackCount; x++) 444 | { 445 | if (itemsToPack[x].IsPacked) continue; 446 | 447 | for (y = 1; y <= 3; y++) 448 | { 449 | switch (y) 450 | { 451 | case 1: 452 | exdim = itemsToPack[x].Dim1; 453 | dimen2 = itemsToPack[x].Dim2; 454 | dimen3 = itemsToPack[x].Dim3; 455 | break; 456 | 457 | case 2: 458 | exdim = itemsToPack[x].Dim2; 459 | dimen2 = itemsToPack[x].Dim1; 460 | dimen3 = itemsToPack[x].Dim3; 461 | break; 462 | 463 | case 3: 464 | exdim = itemsToPack[x].Dim3; 465 | dimen2 = itemsToPack[x].Dim1; 466 | dimen3 = itemsToPack[x].Dim2; 467 | break; 468 | } 469 | 470 | layereval = 0; 471 | 472 | if ((exdim <= thickness) && (((dimen2 <= px) && (dimen3 <= pz)) || ((dimen3 <= px) && (dimen2 <= pz)))) 473 | { 474 | for (z = 1; z <= itemsToPackCount; z++) 475 | { 476 | if (!(x == z) && !(itemsToPack[z].IsPacked)) 477 | { 478 | dimdif = Math.Abs(exdim - itemsToPack[z].Dim1); 479 | 480 | if (Math.Abs(exdim - itemsToPack[z].Dim2) < dimdif) 481 | { 482 | dimdif = Math.Abs(exdim - itemsToPack[z].Dim2); 483 | } 484 | 485 | if (Math.Abs(exdim - itemsToPack[z].Dim3) < dimdif) 486 | { 487 | dimdif = Math.Abs(exdim - itemsToPack[z].Dim3); 488 | } 489 | 490 | layereval = layereval + dimdif; 491 | } 492 | } 493 | 494 | if (layereval < eval) 495 | { 496 | eval = layereval; 497 | layerThickness = exdim; 498 | } 499 | } 500 | } 501 | } 502 | 503 | if (layerThickness == 0 || layerThickness > remainpy) packing = false; 504 | } 505 | 506 | /// 507 | /// Finds the first to be packed gap in the layer edge. 508 | /// 509 | private void FindSmallestZ() 510 | { 511 | ScrapPad scrapmemb = scrapfirst; 512 | smallestZ = scrapmemb; 513 | 514 | while (scrapmemb.Post != null) 515 | { 516 | if (scrapmemb.Post.CumZ < smallestZ.CumZ) 517 | { 518 | smallestZ = scrapmemb.Post; 519 | } 520 | 521 | scrapmemb = scrapmemb.Post; 522 | } 523 | } 524 | 525 | /// 526 | /// Initializes everything. 527 | /// 528 | private void Initialize(Container container, List items) 529 | { 530 | itemsToPack = new List(); 531 | itemsPackedInOrder = new List(); 532 | result = new ContainerPackingResult(); 533 | 534 | // The original code uses 1-based indexing everywhere. This fake entry is added to the beginning 535 | // of the list to make that possible. 536 | itemsToPack.Add(new Item(0, 0, 0, 0, 0)); 537 | 538 | layers = new List(); 539 | itemsToPackCount = 0; 540 | 541 | foreach (Item item in items) 542 | { 543 | for (int i = 1; i <= item.Quantity; i++) 544 | { 545 | Item newItem = new Item(item.ID, item.Dim1, item.Dim2, item.Dim3, item.Quantity); 546 | itemsToPack.Add(newItem); 547 | } 548 | 549 | itemsToPackCount += item.Quantity; 550 | } 551 | 552 | itemsToPack.Add(new Item(0, 0, 0, 0, 0)); 553 | 554 | totalContainerVolume = container.Length * container.Height * container.Width; 555 | totalItemVolume = 0.0M; 556 | 557 | for (x = 1; x <= itemsToPackCount; x++) 558 | { 559 | totalItemVolume = totalItemVolume + itemsToPack[x].Volume; 560 | } 561 | 562 | scrapfirst = new ScrapPad(); 563 | 564 | scrapfirst.Pre = null; 565 | scrapfirst.Post = null; 566 | packingBest = false; 567 | hundredPercentPacked = false; 568 | quit = false; 569 | } 570 | 571 | /// 572 | /// Lists all possible layer heights by giving a weight value to each of them. 573 | /// 574 | private void ListCanditLayers() 575 | { 576 | bool same; 577 | decimal exdim = 0; 578 | decimal dimdif; 579 | decimal dimen2 = 0; 580 | decimal dimen3 = 0; 581 | int y; 582 | int z; 583 | int k; 584 | decimal layereval; 585 | 586 | layerListLen = 0; 587 | 588 | for (x = 1; x <= itemsToPackCount; x++) 589 | { 590 | for (y = 1; y <= 3; y++) 591 | { 592 | switch (y) 593 | { 594 | case 1: 595 | exdim = itemsToPack[x].Dim1; 596 | dimen2 = itemsToPack[x].Dim2; 597 | dimen3 = itemsToPack[x].Dim3; 598 | break; 599 | 600 | case 2: 601 | exdim = itemsToPack[x].Dim2; 602 | dimen2 = itemsToPack[x].Dim1; 603 | dimen3 = itemsToPack[x].Dim3; 604 | break; 605 | 606 | case 3: 607 | exdim = itemsToPack[x].Dim3; 608 | dimen2 = itemsToPack[x].Dim1; 609 | dimen3 = itemsToPack[x].Dim2; 610 | break; 611 | } 612 | 613 | if ((exdim > py) || (((dimen2 > px) || (dimen3 > pz)) && ((dimen3 > px) || (dimen2 > pz)))) continue; 614 | 615 | same = false; 616 | 617 | for (k = 1; k <= layerListLen; k++) 618 | { 619 | if (exdim == layers[k].LayerDim) 620 | { 621 | same = true; 622 | continue; 623 | } 624 | } 625 | 626 | if (same) continue; 627 | 628 | layereval = 0; 629 | 630 | for (z = 1; z <= itemsToPackCount; z++) 631 | { 632 | if (!(x == z)) 633 | { 634 | dimdif = Math.Abs(exdim - itemsToPack[z].Dim1); 635 | 636 | if (Math.Abs(exdim - itemsToPack[z].Dim2) < dimdif) 637 | { 638 | dimdif = Math.Abs(exdim - itemsToPack[z].Dim2); 639 | } 640 | if (Math.Abs(exdim - itemsToPack[z].Dim3) < dimdif) 641 | { 642 | dimdif = Math.Abs(exdim - itemsToPack[z].Dim3); 643 | } 644 | layereval = layereval + dimdif; 645 | } 646 | } 647 | 648 | layerListLen++; 649 | 650 | layers.Add(new Layer()); 651 | layers[layerListLen].LayerEval = layereval; 652 | layers[layerListLen].LayerDim = exdim; 653 | } 654 | } 655 | } 656 | 657 | /// 658 | /// Transforms the found coordinate system to the one entered by the user and writes them 659 | /// to the report file. 660 | /// 661 | private void OutputBoxList() 662 | { 663 | decimal packCoordX = 0; 664 | decimal packCoordY = 0; 665 | decimal packCoordZ = 0; 666 | decimal packDimX = 0; 667 | decimal packDimY = 0; 668 | decimal packDimZ = 0; 669 | 670 | switch (bestVariant) 671 | { 672 | case 1: 673 | packCoordX = itemsToPack[cboxi].CoordX; 674 | packCoordY = itemsToPack[cboxi].CoordY; 675 | packCoordZ = itemsToPack[cboxi].CoordZ; 676 | packDimX = itemsToPack[cboxi].PackDimX; 677 | packDimY = itemsToPack[cboxi].PackDimY; 678 | packDimZ = itemsToPack[cboxi].PackDimZ; 679 | break; 680 | 681 | case 2: 682 | packCoordX = itemsToPack[cboxi].CoordZ; 683 | packCoordY = itemsToPack[cboxi].CoordY; 684 | packCoordZ = itemsToPack[cboxi].CoordX; 685 | packDimX = itemsToPack[cboxi].PackDimZ; 686 | packDimY = itemsToPack[cboxi].PackDimY; 687 | packDimZ = itemsToPack[cboxi].PackDimX; 688 | break; 689 | 690 | case 3: 691 | packCoordX = itemsToPack[cboxi].CoordY; 692 | packCoordY = itemsToPack[cboxi].CoordZ; 693 | packCoordZ = itemsToPack[cboxi].CoordX; 694 | packDimX = itemsToPack[cboxi].PackDimY; 695 | packDimY = itemsToPack[cboxi].PackDimZ; 696 | packDimZ = itemsToPack[cboxi].PackDimX; 697 | break; 698 | 699 | case 4: 700 | packCoordX = itemsToPack[cboxi].CoordY; 701 | packCoordY = itemsToPack[cboxi].CoordX; 702 | packCoordZ = itemsToPack[cboxi].CoordZ; 703 | packDimX = itemsToPack[cboxi].PackDimY; 704 | packDimY = itemsToPack[cboxi].PackDimX; 705 | packDimZ = itemsToPack[cboxi].PackDimZ; 706 | break; 707 | 708 | case 5: 709 | packCoordX = itemsToPack[cboxi].CoordX; 710 | packCoordY = itemsToPack[cboxi].CoordZ; 711 | packCoordZ = itemsToPack[cboxi].CoordY; 712 | packDimX = itemsToPack[cboxi].PackDimX; 713 | packDimY = itemsToPack[cboxi].PackDimZ; 714 | packDimZ = itemsToPack[cboxi].PackDimY; 715 | break; 716 | 717 | case 6: 718 | packCoordX = itemsToPack[cboxi].CoordZ; 719 | packCoordY = itemsToPack[cboxi].CoordX; 720 | packCoordZ = itemsToPack[cboxi].CoordY; 721 | packDimX = itemsToPack[cboxi].PackDimZ; 722 | packDimY = itemsToPack[cboxi].PackDimX; 723 | packDimZ = itemsToPack[cboxi].PackDimY; 724 | break; 725 | } 726 | 727 | itemsToPack[cboxi].CoordX = packCoordX; 728 | itemsToPack[cboxi].CoordY = packCoordY; 729 | itemsToPack[cboxi].CoordZ = packCoordZ; 730 | itemsToPack[cboxi].PackDimX = packDimX; 731 | itemsToPack[cboxi].PackDimY = packDimY; 732 | itemsToPack[cboxi].PackDimZ = packDimZ; 733 | 734 | itemsPackedInOrder.Add(itemsToPack[cboxi]); 735 | } 736 | 737 | /// 738 | /// Packs the boxes found and arranges all variables and records properly. 739 | /// 740 | private void PackLayer() 741 | { 742 | decimal lenx; 743 | decimal lenz; 744 | decimal lpz; 745 | 746 | if (layerThickness == 0) 747 | { 748 | packing = false; 749 | return; 750 | } 751 | 752 | scrapfirst.CumX = px; 753 | scrapfirst.CumZ = 0; 754 | 755 | for (; !quit;) 756 | { 757 | FindSmallestZ(); 758 | 759 | if ((smallestZ.Pre == null) && (smallestZ.Post == null)) 760 | { 761 | //*** SITUATION-1: NO BOXES ON THE RIGHT AND LEFT SIDES *** 762 | 763 | lenx = smallestZ.CumX; 764 | lpz = remainpz - smallestZ.CumZ; 765 | FindBox(lenx, layerThickness, remainpy, lpz, lpz); 766 | CheckFound(); 767 | 768 | if (layerDone) break; 769 | if (evened) continue; 770 | 771 | itemsToPack[cboxi].CoordX = 0; 772 | itemsToPack[cboxi].CoordY = packedy; 773 | itemsToPack[cboxi].CoordZ = smallestZ.CumZ; 774 | if (cboxx == smallestZ.CumX) 775 | { 776 | smallestZ.CumZ = smallestZ.CumZ + cboxz; 777 | } 778 | else 779 | { 780 | smallestZ.Post = new ScrapPad(); 781 | 782 | smallestZ.Post.Post = null; 783 | smallestZ.Post.Pre = smallestZ; 784 | smallestZ.Post.CumX = smallestZ.CumX; 785 | smallestZ.Post.CumZ = smallestZ.CumZ; 786 | smallestZ.CumX = cboxx; 787 | smallestZ.CumZ = smallestZ.CumZ + cboxz; 788 | } 789 | } 790 | else if (smallestZ.Pre == null) 791 | { 792 | //*** SITUATION-2: NO BOXES ON THE LEFT SIDE *** 793 | 794 | lenx = smallestZ.CumX; 795 | lenz = smallestZ.Post.CumZ - smallestZ.CumZ; 796 | lpz = remainpz - smallestZ.CumZ; 797 | FindBox(lenx, layerThickness, remainpy, lenz, lpz); 798 | CheckFound(); 799 | 800 | if (layerDone) break; 801 | if (evened) continue; 802 | 803 | itemsToPack[cboxi].CoordY = packedy; 804 | itemsToPack[cboxi].CoordZ = smallestZ.CumZ; 805 | if (cboxx == smallestZ.CumX) 806 | { 807 | itemsToPack[cboxi].CoordX = 0; 808 | 809 | if (smallestZ.CumZ + cboxz == smallestZ.Post.CumZ) 810 | { 811 | smallestZ.CumZ = smallestZ.Post.CumZ; 812 | smallestZ.CumX = smallestZ.Post.CumX; 813 | trash = smallestZ.Post; 814 | smallestZ.Post = smallestZ.Post.Post; 815 | 816 | if (smallestZ.Post != null) 817 | { 818 | smallestZ.Post.Pre = smallestZ; 819 | } 820 | } 821 | else 822 | { 823 | smallestZ.CumZ = smallestZ.CumZ + cboxz; 824 | } 825 | } 826 | else 827 | { 828 | itemsToPack[cboxi].CoordX = smallestZ.CumX - cboxx; 829 | 830 | if (smallestZ.CumZ + cboxz == smallestZ.Post.CumZ) 831 | { 832 | smallestZ.CumX = smallestZ.CumX - cboxx; 833 | } 834 | else 835 | { 836 | smallestZ.Post.Pre = new ScrapPad(); 837 | 838 | smallestZ.Post.Pre.Post = smallestZ.Post; 839 | smallestZ.Post.Pre.Pre = smallestZ; 840 | smallestZ.Post = smallestZ.Post.Pre; 841 | smallestZ.Post.CumX = smallestZ.CumX; 842 | smallestZ.CumX = smallestZ.CumX - cboxx; 843 | smallestZ.Post.CumZ = smallestZ.CumZ + cboxz; 844 | } 845 | } 846 | } 847 | else if (smallestZ.Post == null) 848 | { 849 | //*** SITUATION-3: NO BOXES ON THE RIGHT SIDE *** 850 | 851 | lenx = smallestZ.CumX - smallestZ.Pre.CumX; 852 | lenz = smallestZ.Pre.CumZ - smallestZ.CumZ; 853 | lpz = remainpz - smallestZ.CumZ; 854 | FindBox(lenx, layerThickness, remainpy, lenz, lpz); 855 | CheckFound(); 856 | 857 | if (layerDone) break; 858 | if (evened) continue; 859 | 860 | itemsToPack[cboxi].CoordY = packedy; 861 | itemsToPack[cboxi].CoordZ = smallestZ.CumZ; 862 | itemsToPack[cboxi].CoordX = smallestZ.Pre.CumX; 863 | 864 | if (cboxx == smallestZ.CumX - smallestZ.Pre.CumX) 865 | { 866 | if (smallestZ.CumZ + cboxz == smallestZ.Pre.CumZ) 867 | { 868 | smallestZ.Pre.CumX = smallestZ.CumX; 869 | smallestZ.Pre.Post = null; 870 | } 871 | else 872 | { 873 | smallestZ.CumZ = smallestZ.CumZ + cboxz; 874 | } 875 | } 876 | else 877 | { 878 | if (smallestZ.CumZ + cboxz == smallestZ.Pre.CumZ) 879 | { 880 | smallestZ.Pre.CumX = smallestZ.Pre.CumX + cboxx; 881 | } 882 | else 883 | { 884 | smallestZ.Pre.Post = new ScrapPad(); 885 | 886 | smallestZ.Pre.Post.Pre = smallestZ.Pre; 887 | smallestZ.Pre.Post.Post = smallestZ; 888 | smallestZ.Pre = smallestZ.Pre.Post; 889 | smallestZ.Pre.CumX = smallestZ.Pre.Pre.CumX + cboxx; 890 | smallestZ.Pre.CumZ = smallestZ.CumZ + cboxz; 891 | } 892 | } 893 | } 894 | else if (smallestZ.Pre.CumZ == smallestZ.Post.CumZ) 895 | { 896 | //*** SITUATION-4: THERE ARE BOXES ON BOTH OF THE SIDES *** 897 | 898 | //*** SUBSITUATION-4A: SIDES ARE EQUAL TO EACH OTHER *** 899 | 900 | lenx = smallestZ.CumX - smallestZ.Pre.CumX; 901 | lenz = smallestZ.Pre.CumZ - smallestZ.CumZ; 902 | lpz = remainpz - smallestZ.CumZ; 903 | 904 | FindBox(lenx, layerThickness, remainpy, lenz, lpz); 905 | CheckFound(); 906 | 907 | if (layerDone) break; 908 | if (evened) continue; 909 | 910 | itemsToPack[cboxi].CoordY = packedy; 911 | itemsToPack[cboxi].CoordZ = smallestZ.CumZ; 912 | 913 | if (cboxx == smallestZ.CumX - smallestZ.Pre.CumX) 914 | { 915 | itemsToPack[cboxi].CoordX = smallestZ.Pre.CumX; 916 | 917 | if (smallestZ.CumZ + cboxz == smallestZ.Post.CumZ) 918 | { 919 | smallestZ.Pre.CumX = smallestZ.Post.CumX; 920 | 921 | if (smallestZ.Post.Post != null) 922 | { 923 | smallestZ.Pre.Post = smallestZ.Post.Post; 924 | smallestZ.Post.Post.Pre = smallestZ.Pre; 925 | } 926 | else 927 | { 928 | smallestZ.Pre.Post = null; 929 | } 930 | } 931 | else 932 | { 933 | smallestZ.CumZ = smallestZ.CumZ + cboxz; 934 | } 935 | } 936 | else if (smallestZ.Pre.CumX < px - smallestZ.CumX) 937 | { 938 | if (smallestZ.CumZ + cboxz == smallestZ.Pre.CumZ) 939 | { 940 | smallestZ.CumX = smallestZ.CumX - cboxx; 941 | itemsToPack[cboxi].CoordX = smallestZ.CumX; 942 | } 943 | else 944 | { 945 | itemsToPack[cboxi].CoordX = smallestZ.Pre.CumX; 946 | smallestZ.Pre.Post = new ScrapPad(); 947 | 948 | smallestZ.Pre.Post.Pre = smallestZ.Pre; 949 | smallestZ.Pre.Post.Post = smallestZ; 950 | smallestZ.Pre = smallestZ.Pre.Post; 951 | smallestZ.Pre.CumX = smallestZ.Pre.Pre.CumX + cboxx; 952 | smallestZ.Pre.CumZ = smallestZ.CumZ + cboxz; 953 | } 954 | } 955 | else 956 | { 957 | if (smallestZ.CumZ + cboxz == smallestZ.Pre.CumZ) 958 | { 959 | smallestZ.Pre.CumX = smallestZ.Pre.CumX + cboxx; 960 | itemsToPack[cboxi].CoordX = smallestZ.Pre.CumX; 961 | } 962 | else 963 | { 964 | itemsToPack[cboxi].CoordX = smallestZ.CumX - cboxx; 965 | smallestZ.Post.Pre = new ScrapPad(); 966 | 967 | smallestZ.Post.Pre.Post = smallestZ.Post; 968 | smallestZ.Post.Pre.Pre = smallestZ; 969 | smallestZ.Post = smallestZ.Post.Pre; 970 | smallestZ.Post.CumX = smallestZ.CumX; 971 | smallestZ.Post.CumZ = smallestZ.CumZ + cboxz; 972 | smallestZ.CumX = smallestZ.CumX - cboxx; 973 | } 974 | } 975 | } 976 | else 977 | { 978 | //*** SUBSITUATION-4B: SIDES ARE NOT EQUAL TO EACH OTHER *** 979 | 980 | lenx = smallestZ.CumX - smallestZ.Pre.CumX; 981 | lenz = smallestZ.Pre.CumZ - smallestZ.CumZ; 982 | lpz = remainpz - smallestZ.CumZ; 983 | FindBox(lenx, layerThickness, remainpy, lenz, lpz); 984 | CheckFound(); 985 | 986 | if (layerDone) break; 987 | if (evened) continue; 988 | 989 | itemsToPack[cboxi].CoordY = packedy; 990 | itemsToPack[cboxi].CoordZ = smallestZ.CumZ; 991 | itemsToPack[cboxi].CoordX = smallestZ.Pre.CumX; 992 | 993 | if (cboxx == (smallestZ.CumX - smallestZ.Pre.CumX)) 994 | { 995 | if ((smallestZ.CumZ + cboxz) == smallestZ.Pre.CumZ) 996 | { 997 | smallestZ.Pre.CumX = smallestZ.CumX; 998 | smallestZ.Pre.Post = smallestZ.Post; 999 | smallestZ.Post.Pre = smallestZ.Pre; 1000 | } 1001 | else 1002 | { 1003 | smallestZ.CumZ = smallestZ.CumZ + cboxz; 1004 | } 1005 | } 1006 | else 1007 | { 1008 | if ((smallestZ.CumZ + cboxz) == smallestZ.Pre.CumZ) 1009 | { 1010 | smallestZ.Pre.CumX = smallestZ.Pre.CumX + cboxx; 1011 | } 1012 | else if (smallestZ.CumZ + cboxz == smallestZ.Post.CumZ) 1013 | { 1014 | itemsToPack[cboxi].CoordX = smallestZ.CumX - cboxx; 1015 | smallestZ.CumX = smallestZ.CumX - cboxx; 1016 | } 1017 | else 1018 | { 1019 | smallestZ.Pre.Post = new ScrapPad(); 1020 | 1021 | smallestZ.Pre.Post.Pre = smallestZ.Pre; 1022 | smallestZ.Pre.Post.Post = smallestZ; 1023 | smallestZ.Pre = smallestZ.Pre.Post; 1024 | smallestZ.Pre.CumX = smallestZ.Pre.Pre.CumX + cboxx; 1025 | smallestZ.Pre.CumZ = smallestZ.CumZ + cboxz; 1026 | } 1027 | } 1028 | } 1029 | 1030 | VolumeCheck(); 1031 | } 1032 | } 1033 | 1034 | /// 1035 | /// Using the parameters found, packs the best solution found and 1036 | /// reports to the console. 1037 | /// 1038 | private void Report(Container container) 1039 | { 1040 | quit = false; 1041 | 1042 | switch (bestVariant) 1043 | { 1044 | case 1: 1045 | px = container.Length; py = container.Height; pz = container.Width; 1046 | break; 1047 | 1048 | case 2: 1049 | px = container.Width; py = container.Height; pz = container.Length; 1050 | break; 1051 | 1052 | case 3: 1053 | px = container.Width; py = container.Length; pz = container.Height; 1054 | break; 1055 | 1056 | case 4: 1057 | px = container.Height; py = container.Length; pz = container.Width; 1058 | break; 1059 | 1060 | case 5: 1061 | px = container.Length; py = container.Width; pz = container.Height; 1062 | break; 1063 | 1064 | case 6: 1065 | px = container.Height; py = container.Width; pz = container.Length; 1066 | break; 1067 | } 1068 | 1069 | packingBest = true; 1070 | 1071 | //Print("BEST SOLUTION FOUND AT ITERATION :", bestIteration, "OF VARIANT", bestVariant); 1072 | //Print("TOTAL ITEMS TO PACK :", itemsToPackCount); 1073 | //Print("TOTAL VOLUME OF ALL ITEMS :", totalItemVolume); 1074 | //Print("WHILE CONTAINER ORIENTATION X - Y - Z :", px, py, pz); 1075 | 1076 | layers.Clear(); 1077 | layers.Add(new Layer { LayerEval = -1 }); 1078 | ListCanditLayers(); 1079 | layers = layers.OrderBy(l => l.LayerEval).ToList(); 1080 | packedVolume = 0; 1081 | packedy = 0; 1082 | packing = true; 1083 | layerThickness = layers[bestIteration].LayerDim; 1084 | remainpy = py; 1085 | remainpz = pz; 1086 | 1087 | for (x = 1; x <= itemsToPackCount; x++) 1088 | { 1089 | itemsToPack[x].IsPacked = false; 1090 | } 1091 | 1092 | do 1093 | { 1094 | layerinlayer = 0; 1095 | layerDone = false; 1096 | PackLayer(); 1097 | packedy = packedy + layerThickness; 1098 | remainpy = py - packedy; 1099 | 1100 | if (layerinlayer > 0.0001M) 1101 | { 1102 | prepackedy = packedy; 1103 | preremainpy = remainpy; 1104 | remainpy = layerThickness - prelayer; 1105 | packedy = packedy - layerThickness + prelayer; 1106 | remainpz = lilz; 1107 | layerThickness = layerinlayer; 1108 | layerDone = false; 1109 | PackLayer(); 1110 | packedy = prepackedy; 1111 | remainpy = preremainpy; 1112 | remainpz = pz; 1113 | } 1114 | 1115 | if (!quit) 1116 | { 1117 | FindLayer(remainpy); 1118 | } 1119 | } while (packing && !quit); 1120 | } 1121 | 1122 | /// 1123 | /// After packing of each item, the 100% packing condition is checked. 1124 | /// 1125 | private void VolumeCheck() 1126 | { 1127 | itemsToPack[cboxi].IsPacked = true; 1128 | itemsToPack[cboxi].PackDimX = cboxx; 1129 | itemsToPack[cboxi].PackDimY = cboxy; 1130 | itemsToPack[cboxi].PackDimZ = cboxz; 1131 | packedVolume = packedVolume + itemsToPack[cboxi].Volume; 1132 | packedItemCount++; 1133 | 1134 | if (packingBest) 1135 | { 1136 | OutputBoxList(); 1137 | } 1138 | else if (packedVolume == totalContainerVolume || packedVolume == totalItemVolume) 1139 | { 1140 | packing = false; 1141 | hundredPercentPacked = true; 1142 | } 1143 | } 1144 | 1145 | #endregion Private Methods 1146 | 1147 | #region Private Classes 1148 | 1149 | /// 1150 | /// A list that stores all the different lengths of all item dimensions. 1151 | /// From the master's thesis: 1152 | /// "Each Layerdim value in this array represents a different layer thickness 1153 | /// value with which each iteration can start packing. Before starting iterations, 1154 | /// all different lengths of all box dimensions along with evaluation values are 1155 | /// stored in this array" (p. 3-6). 1156 | /// 1157 | private class Layer 1158 | { 1159 | /// 1160 | /// Gets or sets the layer dimension value, representing a layer thickness. 1161 | /// 1162 | /// 1163 | /// The layer dimension value. 1164 | /// 1165 | public decimal LayerDim { get; set; } 1166 | 1167 | /// 1168 | /// Gets or sets the layer eval value, representing an evaluation weight 1169 | /// value for the corresponding LayerDim value. 1170 | /// 1171 | /// 1172 | /// The layer eval value. 1173 | /// 1174 | public decimal LayerEval { get; set; } 1175 | } 1176 | 1177 | /// 1178 | /// From the master's thesis: 1179 | /// "The double linked list we use keeps the topology of the edge of the 1180 | /// current layer under construction. We keep the x and z coordinates of 1181 | /// each gap's right corner. The program looks at those gaps and tries to 1182 | /// fill them with boxes one at a time while trying to keep the edge of the 1183 | /// layer even" (p. 3-7). 1184 | /// 1185 | private class ScrapPad 1186 | { 1187 | /// 1188 | /// Gets or sets the x coordinate of the gap's right corner. 1189 | /// 1190 | /// 1191 | /// The x coordinate of the gap's right corner. 1192 | /// 1193 | public decimal CumX { get; set; } 1194 | 1195 | /// 1196 | /// Gets or sets the z coordinate of the gap's right corner. 1197 | /// 1198 | /// 1199 | /// The z coordinate of the gap's right corner. 1200 | /// 1201 | public decimal CumZ { get; set; } 1202 | 1203 | /// 1204 | /// Gets or sets the following entry. 1205 | /// 1206 | /// 1207 | /// The following entry. 1208 | /// 1209 | public ScrapPad Post { get; set; } 1210 | 1211 | /// 1212 | /// Gets or sets the previous entry. 1213 | /// 1214 | /// 1215 | /// The previous entry. 1216 | /// 1217 | public ScrapPad Pre { get; set; } 1218 | } 1219 | 1220 | #endregion Private Classes 1221 | } 1222 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Algorithms/IPackingAlgorithm.cs: -------------------------------------------------------------------------------- 1 | using CromulentBisgetti.ContainerPacking.Entities; 2 | using System.Collections.Generic; 3 | 4 | namespace CromulentBisgetti.ContainerPacking.Algorithms 5 | { 6 | /// 7 | /// Interface for the packing algorithms in this project. 8 | /// 9 | public interface IPackingAlgorithm 10 | { 11 | /// 12 | /// Runs the algorithm on the specified container and items. 13 | /// 14 | /// The container. 15 | /// The items to pack. 16 | /// The algorithm packing result. 17 | AlgorithmPackingResult Run(Container container, List items); 18 | } 19 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/CromulentBisgetti.ContainerPacking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Entities/AlgorithmPackingResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.Serialization; 3 | 4 | namespace CromulentBisgetti.ContainerPacking.Entities 5 | { 6 | [DataContract] 7 | public class AlgorithmPackingResult 8 | { 9 | #region Constructors 10 | 11 | public AlgorithmPackingResult() 12 | { 13 | this.PackedItems = new List(); 14 | this.UnpackedItems = new List(); 15 | } 16 | 17 | #endregion Constructors 18 | 19 | #region Public Properties 20 | 21 | [DataMember] 22 | public int AlgorithmID { get; set; } 23 | 24 | [DataMember] 25 | public string AlgorithmName { get; set; } 26 | 27 | /// 28 | /// Gets or sets a value indicating whether all of the items are packed in the container. 29 | /// 30 | /// 31 | /// True if all the items are packed in the container; otherwise, false. 32 | /// 33 | [DataMember] 34 | public bool IsCompletePack { get; set; } 35 | 36 | /// 37 | /// Gets or sets the list of packed items. 38 | /// 39 | /// 40 | /// The list of packed items. 41 | /// 42 | [DataMember] 43 | public List PackedItems { get; set; } 44 | 45 | /// 46 | /// Gets or sets the elapsed pack time in milliseconds. 47 | /// 48 | /// 49 | /// The elapsed pack time in milliseconds. 50 | /// 51 | [DataMember] 52 | public long PackTimeInMilliseconds { get; set; } 53 | 54 | /// 55 | /// Gets or sets the percent of container volume packed. 56 | /// 57 | /// 58 | /// The percent of container volume packed. 59 | /// 60 | [DataMember] 61 | public decimal PercentContainerVolumePacked { get; set; } 62 | 63 | /// 64 | /// Gets or sets the percent of item volume packed. 65 | /// 66 | /// 67 | /// The percent of item volume packed. 68 | /// 69 | [DataMember] 70 | public decimal PercentItemVolumePacked { get; set; } 71 | 72 | /// 73 | /// Gets or sets the list of unpacked items. 74 | /// 75 | /// 76 | /// The list of unpacked items. 77 | /// 78 | [DataMember] 79 | public List UnpackedItems { get; set; } 80 | 81 | #endregion Public Properties 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Entities/Container.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CromulentBisgetti.ContainerPacking.Entities 8 | { 9 | /// 10 | /// The container to pack items into. 11 | /// 12 | public class Container 13 | { 14 | #region Private Variables 15 | 16 | private decimal volume; 17 | 18 | #endregion Private Variables 19 | 20 | #region Constructors 21 | 22 | /// 23 | /// Initializes a new instance of the Container class. 24 | /// 25 | /// The container ID. 26 | /// The container length. 27 | /// The container width. 28 | /// The container height. 29 | public Container(int id, decimal length, decimal width, decimal height) 30 | { 31 | this.ID = id; 32 | this.Length = length; 33 | this.Width = width; 34 | this.Height = height; 35 | this.Volume = length * width * height; 36 | } 37 | 38 | #endregion Constructors 39 | 40 | #region Public Properties 41 | /// 42 | /// Gets or sets the container ID. 43 | /// 44 | /// 45 | /// The container ID. 46 | /// 47 | public int ID { get; set; } 48 | 49 | /// 50 | /// Gets or sets the container length. 51 | /// 52 | /// 53 | /// The container length. 54 | /// 55 | public decimal Length { get; set; } 56 | 57 | /// 58 | /// Gets or sets the container width. 59 | /// 60 | /// 61 | /// The container width. 62 | /// 63 | public decimal Width { get; set; } 64 | 65 | /// 66 | /// Gets or sets the container height. 67 | /// 68 | /// 69 | /// The container height. 70 | /// 71 | public decimal Height { get; set; } 72 | 73 | /// 74 | /// Gets or sets the volume of the container. 75 | /// 76 | /// 77 | /// The volume of the container. 78 | /// 79 | public decimal Volume 80 | { 81 | get 82 | { 83 | return this.volume; 84 | } 85 | set 86 | { 87 | this.volume = value; 88 | } 89 | } 90 | 91 | #endregion Public Properties 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Entities/ContainerPackingResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CromulentBisgetti.ContainerPacking.Entities 9 | { 10 | /// 11 | /// The container packing result. 12 | /// 13 | [DataContract] 14 | public class ContainerPackingResult 15 | { 16 | #region Constructors 17 | 18 | public ContainerPackingResult() 19 | { 20 | this.AlgorithmPackingResults = new List(); 21 | } 22 | 23 | #endregion Constructors 24 | 25 | #region Public Properties 26 | 27 | /// 28 | /// Gets or sets the container ID. 29 | /// 30 | /// 31 | /// The container ID. 32 | /// 33 | [DataMember] 34 | public int ContainerID { get; set; } 35 | 36 | [DataMember] 37 | public List AlgorithmPackingResults { get; set; } 38 | 39 | #endregion Public Properties 40 | } 41 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/Entities/Item.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace CromulentBisgetti.ContainerPacking.Entities 4 | { 5 | /// 6 | /// An item to be packed. Also used to hold post-packing details for the item. 7 | /// 8 | [DataContract] 9 | public class Item 10 | { 11 | #region Private Variables 12 | 13 | private decimal volume; 14 | 15 | #endregion Private Variables 16 | 17 | #region Constructors 18 | 19 | /// 20 | /// Initializes a new instance of the Item class. 21 | /// 22 | /// The item ID. 23 | /// The length of one of the three item dimensions. 24 | /// The length of another of the three item dimensions. 25 | /// The length of the other of the three item dimensions. 26 | /// The item quantity. 27 | public Item(int id, decimal dim1, decimal dim2, decimal dim3, int quantity) 28 | { 29 | this.ID = id; 30 | this.Dim1 = dim1; 31 | this.Dim2 = dim2; 32 | this.Dim3 = dim3; 33 | this.volume = dim1 * dim2 * dim3; 34 | this.Quantity = quantity; 35 | } 36 | 37 | #endregion Constructors 38 | 39 | #region Public Properties 40 | 41 | /// 42 | /// Gets or sets the item ID. 43 | /// 44 | /// 45 | /// The item ID. 46 | /// 47 | [DataMember] 48 | public int ID { get; set; } 49 | 50 | /// 51 | /// Gets or sets a value indicating whether this item has already been packed. 52 | /// 53 | /// 54 | /// True if the item has already been packed; otherwise, false. 55 | /// 56 | [DataMember] 57 | public bool IsPacked { get; set; } 58 | 59 | /// 60 | /// Gets or sets the length of one of the item dimensions. 61 | /// 62 | /// 63 | /// The first item dimension. 64 | /// 65 | [DataMember] 66 | public decimal Dim1 { get; set; } 67 | 68 | /// 69 | /// Gets or sets the length another of the item dimensions. 70 | /// 71 | /// 72 | /// The second item dimension. 73 | /// 74 | [DataMember] 75 | public decimal Dim2 { get; set; } 76 | 77 | /// 78 | /// Gets or sets the third of the item dimensions. 79 | /// 80 | /// 81 | /// The third item dimension. 82 | /// 83 | [DataMember] 84 | public decimal Dim3 { get; set; } 85 | 86 | /// 87 | /// Gets or sets the x coordinate of the location of the packed item within the container. 88 | /// 89 | /// 90 | /// The x coordinate of the location of the packed item within the container. 91 | /// 92 | [DataMember] 93 | public decimal CoordX { get; set; } 94 | 95 | /// 96 | /// Gets or sets the y coordinate of the location of the packed item within the container. 97 | /// 98 | /// 99 | /// The y coordinate of the location of the packed item within the container. 100 | /// 101 | [DataMember] 102 | public decimal CoordY { get; set; } 103 | 104 | /// 105 | /// Gets or sets the z coordinate of the location of the packed item within the container. 106 | /// 107 | /// 108 | /// The z coordinate of the location of the packed item within the container. 109 | /// 110 | [DataMember] 111 | public decimal CoordZ { get; set; } 112 | 113 | /// 114 | /// Gets or sets the item quantity. 115 | /// 116 | /// 117 | /// The item quantity. 118 | /// 119 | public int Quantity { get; set; } 120 | 121 | /// 122 | /// Gets or sets the x dimension of the orientation of the item as it has been packed. 123 | /// 124 | /// 125 | /// The x dimension of the orientation of the item as it has been packed. 126 | /// 127 | [DataMember] 128 | public decimal PackDimX { get; set; } 129 | 130 | /// 131 | /// Gets or sets the y dimension of the orientation of the item as it has been packed. 132 | /// 133 | /// 134 | /// The y dimension of the orientation of the item as it has been packed. 135 | /// 136 | [DataMember] 137 | public decimal PackDimY { get; set; } 138 | 139 | /// 140 | /// Gets or sets the z dimension of the orientation of the item as it has been packed. 141 | /// 142 | /// 143 | /// The z dimension of the orientation of the item as it has been packed. 144 | /// 145 | [DataMember] 146 | public decimal PackDimZ { get; set; } 147 | 148 | /// 149 | /// Gets the item volume. 150 | /// 151 | /// 152 | /// The item volume. 153 | /// 154 | [DataMember] 155 | public decimal Volume 156 | { 157 | get 158 | { 159 | return volume; 160 | } 161 | } 162 | 163 | #endregion Public Properties 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPacking/PackingService.cs: -------------------------------------------------------------------------------- 1 | using CromulentBisgetti.ContainerPacking.Algorithms; 2 | using CromulentBisgetti.ContainerPacking.Entities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace CromulentBisgetti.ContainerPacking 10 | { 11 | /// 12 | /// The container packing service. 13 | /// 14 | public static class PackingService 15 | { 16 | /// 17 | /// Attempts to pack the specified containers with the specified items using the specified algorithms. 18 | /// 19 | /// The list of containers to pack. 20 | /// The items to pack. 21 | /// The list of algorithm type IDs to use for packing. 22 | /// A container packing result with lists of the packed and unpacked items. 23 | public static List Pack(List containers, List itemsToPack, List algorithmTypeIDs) 24 | { 25 | Object sync = new Object { }; 26 | List result = new List(); 27 | 28 | Parallel.ForEach(containers, container => 29 | { 30 | ContainerPackingResult containerPackingResult = new ContainerPackingResult(); 31 | containerPackingResult.ContainerID = container.ID; 32 | 33 | Parallel.ForEach(algorithmTypeIDs, algorithmTypeID => 34 | { 35 | IPackingAlgorithm algorithm = GetPackingAlgorithmFromTypeID(algorithmTypeID); 36 | 37 | // Until I rewrite the algorithm with no side effects, we need to clone the item list 38 | // so the parallel updates don't interfere with each other. 39 | List items = new List(); 40 | 41 | itemsToPack.ForEach(item => 42 | { 43 | items.Add(new Item(item.ID, item.Dim1, item.Dim2, item.Dim3, item.Quantity)); 44 | }); 45 | 46 | Stopwatch stopwatch = new Stopwatch(); 47 | stopwatch.Start(); 48 | AlgorithmPackingResult algorithmResult = algorithm.Run(container, items); 49 | stopwatch.Stop(); 50 | 51 | algorithmResult.PackTimeInMilliseconds = stopwatch.ElapsedMilliseconds; 52 | 53 | decimal containerVolume = container.Length * container.Width * container.Height; 54 | decimal itemVolumePacked = algorithmResult.PackedItems.Sum(i => i.Volume); 55 | decimal itemVolumeUnpacked = algorithmResult.UnpackedItems.Sum(i => i.Volume); 56 | 57 | algorithmResult.PercentContainerVolumePacked = Math.Round(itemVolumePacked / containerVolume * 100, 2); 58 | algorithmResult.PercentItemVolumePacked = Math.Round(itemVolumePacked / (itemVolumePacked + itemVolumeUnpacked) * 100, 2); 59 | 60 | lock (sync) 61 | { 62 | containerPackingResult.AlgorithmPackingResults.Add(algorithmResult); 63 | } 64 | }); 65 | 66 | containerPackingResult.AlgorithmPackingResults = containerPackingResult.AlgorithmPackingResults.OrderBy(r => r.AlgorithmName).ToList(); 67 | 68 | lock (sync) 69 | { 70 | result.Add(containerPackingResult); 71 | } 72 | }); 73 | 74 | return result; 75 | } 76 | 77 | /// 78 | /// Gets the packing algorithm from the specified algorithm type ID. 79 | /// 80 | /// The algorithm type ID. 81 | /// An instance of a packing algorithm implementing AlgorithmBase. 82 | /// Invalid algorithm type. 83 | public static IPackingAlgorithm GetPackingAlgorithmFromTypeID(int algorithmTypeID) 84 | { 85 | switch (algorithmTypeID) 86 | { 87 | case (int)AlgorithmType.EB_AFIT: 88 | return new EB_AFIT(); 89 | 90 | default: 91 | throw new Exception("Invalid algorithm type."); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPackingTests/ContainerPackingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.Reflection; 4 | using System.IO; 5 | using System.Collections.Generic; 6 | using CromulentBisgetti.ContainerPacking; 7 | using CromulentBisgetti.ContainerPacking.Entities; 8 | using CromulentBisgetti.ContainerPacking.Algorithms; 9 | 10 | namespace CromulentBisgetti.ContainerPackingTests 11 | { 12 | [TestClass] 13 | public class ContainerPackingTests 14 | { 15 | [TestMethod] 16 | public void EB_AFIT_Passes_700_Standard_Reference_Tests() 17 | { 18 | // ORLibrary.txt is an Embedded Resource in this project. 19 | string resourceName = "CromulentBisgetti.ContainerPackingTests.DataFiles.ORLibrary.txt"; 20 | Assembly assembly = Assembly.GetExecutingAssembly(); 21 | 22 | using (Stream stream = assembly.GetManifestResourceStream(resourceName)) 23 | { 24 | using (StreamReader reader = new StreamReader(stream)) 25 | { 26 | // Counter to control how many tests are run in dev. 27 | int counter = 1; 28 | 29 | while (reader.ReadLine() != null && counter <= 700) 30 | { 31 | List itemsToPack = new List(); 32 | 33 | // First line in each test case is an ID. Skip it. 34 | 35 | // Second line states the results of the test, as reported in the EB-AFIT master's thesis, appendix E. 36 | string[] testResults = reader.ReadLine().Split(' '); 37 | 38 | // Third line defines the container dimensions. 39 | string[] containerDims = reader.ReadLine().Split(' '); 40 | 41 | // Fourth line states how many distinct item types we are packing. 42 | int itemTypeCount = Convert.ToInt32(reader.ReadLine()); 43 | 44 | for (int i = 0; i < itemTypeCount; i++) 45 | { 46 | string[] itemArray = reader.ReadLine().Split(' '); 47 | 48 | Item item = new Item(0, Convert.ToDecimal(itemArray[1]), Convert.ToDecimal(itemArray[3]), Convert.ToDecimal(itemArray[5]), Convert.ToInt32(itemArray[7])); 49 | itemsToPack.Add(item); 50 | } 51 | 52 | List containers = new List(); 53 | containers.Add(new Container(0, Convert.ToDecimal(containerDims[0]), Convert.ToDecimal(containerDims[1]), Convert.ToDecimal(containerDims[2]))); 54 | 55 | List result = PackingService.Pack(containers, itemsToPack, new List { (int)AlgorithmType.EB_AFIT }); 56 | 57 | // Assert that the number of items we tried to pack equals the number stated in the published reference. 58 | Assert.AreEqual(result[0].AlgorithmPackingResults[0].PackedItems.Count + result[0].AlgorithmPackingResults[0].UnpackedItems.Count, Convert.ToDecimal(testResults[1])); 59 | 60 | // Assert that the number of items successfully packed equals the number stated in the published reference. 61 | Assert.AreEqual(result[0].AlgorithmPackingResults[0].PackedItems.Count, Convert.ToDecimal(testResults[2])); 62 | 63 | // Assert that the packed container volume percentage is equal to the published reference result. 64 | // Make an exception for a couple of tests where this algorithm yields 87.20% and the published result 65 | // was 87.21% (acceptable rounding error). 66 | Assert.IsTrue(result[0].AlgorithmPackingResults[0].PercentContainerVolumePacked == Convert.ToDecimal(testResults[3]) || 67 | (result[0].AlgorithmPackingResults[0].PercentContainerVolumePacked == 87.20M && Convert.ToDecimal(testResults[3]) == 87.21M)); 68 | 69 | // Assert that the packed item volume percentage is equal to the published reference result. 70 | Assert.AreEqual(result[0].AlgorithmPackingResults[0].PercentItemVolumePacked, Convert.ToDecimal(testResults[4])); 71 | 72 | counter++; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.ContainerPackingTests/CromulentBisgetti.ContainerPackingTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Controllers/ContainerPackingController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CromulentBisgetti.ContainerPacking; 3 | using CromulentBisgetti.ContainerPacking.Entities; 4 | using CromulentBisgetti.DemoApp.Models; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace CromulentBisgetti.DemoApp.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | [ApiController] 11 | public class ContainerPackingController : ControllerBase 12 | { 13 | // POST api/values 14 | [HttpPost] 15 | public ActionResult> Post([FromBody]ContainerPackingRequest request) 16 | { 17 | return PackingService.Pack(request.Containers, request.ItemsToPack, request.AlgorithmTypeIDs); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace CromulentBisgetti.DemoApp.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | public IActionResult Index() 8 | { 9 | return View(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/CromulentBisgetti.DemoApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Models/ContainerPackingRequest.cs: -------------------------------------------------------------------------------- 1 | using CromulentBisgetti.ContainerPacking.Entities; 2 | using System.Collections.Generic; 3 | 4 | namespace CromulentBisgetti.DemoApp.Models 5 | { 6 | public class ContainerPackingRequest 7 | { 8 | public List Containers { get; set; } 9 | 10 | public List ItemsToPack { get; set; } 11 | 12 | public List AlgorithmTypeIDs { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace CromulentBisgetti.DemoApp 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:50033", 7 | "sslPort": 44346 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "CromulentBisgetti.DemoApp": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.HttpsPolicy; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | 13 | namespace CromulentBisgetti.DemoApp 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | services.Configure(options => 28 | { 29 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 30 | options.CheckConsentNeeded = context => true; 31 | options.MinimumSameSitePolicy = SameSiteMode.None; 32 | }); 33 | 34 | 35 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) 36 | .AddJsonOptions(options => 37 | { 38 | options.SerializerSettings.ContractResolver = null; 39 | }); 40 | } 41 | 42 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 43 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 44 | { 45 | if (env.IsDevelopment()) 46 | { 47 | app.UseDeveloperExceptionPage(); 48 | } 49 | else 50 | { 51 | app.UseExceptionHandler("/Error"); 52 | app.UseHsts(); 53 | } 54 | 55 | app.UseHttpsRedirection(); 56 | app.UseStaticFiles(); 57 | app.UseCookiePolicy(); 58 | 59 | app.UseMvcWithDefaultRoute(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = null; 3 | } 4 | 5 | 6 | 7 | Container Packing 8 | 9 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 |

3D Container Packing

59 |
60 |
61 |
62 |
63 |

Packing Algorithms

64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
Algorithms to Use

82 |
83 | 96 |
97 |
98 |
99 |
100 |
101 |

Items to Pack

102 |
103 |
104 |
105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 |
NameLWHQty
130 |
131 | 144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |

Containers

152 |
153 | 154 | 155 |
156 |
157 |
158 |
159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 191 | 198 | 205 | 212 | 219 | 226 | 227 | 228 |
NameLWHAlgorithm
Name
Pack Time
(ms)
% Cont.
Used
# Items
Packed
# Items
Unpacked
185 | 186 | 187 | 188 | 189 |

190 |
192 | 193 | 194 | 195 | 196 |

197 |
199 | 200 | 201 | 202 | 203 |

204 |
206 | 207 | 208 | 209 | 210 |

211 |
213 | 214 | 215 | 216 | 217 |

218 |
220 | 221 | 222 | 223 | 224 |
225 |
229 |
230 | 242 |
243 |
244 |
245 |
246 | 247 | 258 | 259 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/Views/Home/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace CromulentBisgetti.DemoApp.Pages 9 | { 10 | public class IndexModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmchapman/3DContainerPacking/4a6102f5b6bb67a230a2d12326052b3da6341b51/src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmchapman/3DContainerPacking/4a6102f5b6bb67a230a2d12326052b3da6341b51/src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmchapman/3DContainerPacking/4a6102f5b6bb67a230a2d12326052b3da6341b51/src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidmchapman/3DContainerPacking/4a6102f5b6bb67a230a2d12326052b3da6341b51/src/CromulentBisgetti.DemoApp/wwwroot/css/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/js/container-packing.js: -------------------------------------------------------------------------------- 1 | var scene; 2 | var camera; 3 | var renderer; 4 | var controls; 5 | var viewModel; 6 | var itemMaterial; 7 | 8 | async function PackContainers(request) { 9 | return $.ajax({ 10 | url: '/api/containerpacking', 11 | type: 'POST', 12 | data: request, 13 | contentType: 'application/json; charset=utf-8' 14 | }); 15 | }; 16 | 17 | function InitializeDrawing() { 18 | var container = $('#drawing-container'); 19 | 20 | scene = new THREE.Scene(); 21 | camera = new THREE.PerspectiveCamera( 50, window.innerWidth/window.innerHeight, 0.1, 1000 ); 22 | camera.lookAt(scene.position); 23 | 24 | //var axisHelper = new THREE.AxisHelper( 5 ); 25 | //scene.add( axisHelper ); 26 | 27 | // LIGHT 28 | var light = new THREE.PointLight(0xffffff); 29 | light.position.set(0,150,100); 30 | scene.add(light); 31 | 32 | // Get the item stuff ready. 33 | itemMaterial = new THREE.MeshNormalMaterial( { transparent: true, opacity: 0.6 } ); 34 | 35 | renderer = new THREE.WebGLRenderer( { antialias: true } ); // WebGLRenderer CanvasRenderer 36 | renderer.setClearColor( 0xf0f0f0 ); 37 | renderer.setPixelRatio( window.devicePixelRatio ); 38 | renderer.setSize( window.innerWidth / 2, window.innerHeight / 2); 39 | container.append( renderer.domElement ); 40 | 41 | controls = new THREE.OrbitControls( camera, renderer.domElement ); 42 | window.addEventListener( 'resize', onWindowResize, false ); 43 | 44 | animate(); 45 | }; 46 | 47 | function onWindowResize() { 48 | camera.aspect = window.innerWidth / window.innerHeight; 49 | camera.updateProjectionMatrix(); 50 | renderer.setSize( window.innerWidth / 2, window.innerHeight / 2 ); 51 | } 52 | // 53 | function animate() { 54 | requestAnimationFrame( animate ); 55 | controls.update(); 56 | render(); 57 | } 58 | function render() { 59 | renderer.render( scene, camera ); 60 | } 61 | 62 | var ViewModel = function () { 63 | var self = this; 64 | 65 | self.ItemCounter = 0; 66 | self.ContainerCounter = 0; 67 | 68 | self.ItemsToRender = ko.observableArray([]); 69 | self.LastItemRenderedIndex = ko.observable(-1); 70 | 71 | self.ContainerOriginOffset = { 72 | x: 0, 73 | y: 0, 74 | z: 0 75 | }; 76 | 77 | self.AlgorithmsToUse = ko.observableArray([]); 78 | self.ItemsToPack = ko.observableArray([]); 79 | self.Containers = ko.observableArray([]); 80 | 81 | self.NewItemToPack = ko.mapping.fromJS(new ItemToPack()); 82 | self.NewContainer = ko.mapping.fromJS(new Container()); 83 | 84 | self.GenerateItemsToPack = function () { 85 | self.ItemsToPack([]); 86 | self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1000, Name: 'Item1', Length: 5, Width: 4, Height: 2, Quantity: 1 })); 87 | self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1001, Name: 'Item2', Length: 2, Width: 1, Height: 1, Quantity: 3 })); 88 | self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1002, Name: 'Item3', Length: 9, Width: 7, Height: 3, Quantity: 4 })); 89 | self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1003, Name: 'Item4', Length: 13, Width: 6, Height: 3, Quantity: 8 })); 90 | self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1004, Name: 'Item5', Length: 17, Width: 8, Height: 6, Quantity: 1 })); 91 | self.ItemsToPack.push(ko.mapping.fromJS({ ID: 1005, Name: 'Item6', Length: 3, Width: 3, Height: 2, Quantity: 2 })); 92 | }; 93 | 94 | self.GenerateContainers = function () { 95 | self.Containers([]); 96 | self.Containers.push(ko.mapping.fromJS({ ID: 1000, Name: 'Box1', Length: 15, Width: 13, Height: 9, AlgorithmPackingResults: [] })); 97 | self.Containers.push(ko.mapping.fromJS({ ID: 1001, Name: 'Box2', Length: 23, Width: 9, Height: 4, AlgorithmPackingResults: [] })); 98 | self.Containers.push(ko.mapping.fromJS({ ID: 1002, Name: 'Box3', Length: 16, Width: 16, Height: 6, AlgorithmPackingResults: [] })); 99 | self.Containers.push(ko.mapping.fromJS({ ID: 1003, Name: 'Box4', Length: 10, Width: 8, Height: 5, AlgorithmPackingResults: [] })); 100 | self.Containers.push(ko.mapping.fromJS({ ID: 1004, Name: 'Box5', Length: 40, Width: 28, Height: 20, AlgorithmPackingResults: [] })); 101 | self.Containers.push(ko.mapping.fromJS({ ID: 1005, Name: 'Box6', Length: 29, Width: 19, Height: 4, AlgorithmPackingResults: [] })); 102 | self.Containers.push(ko.mapping.fromJS({ ID: 1006, Name: 'Box7', Length: 18, Width: 13, Height: 1, AlgorithmPackingResults: [] })); 103 | self.Containers.push(ko.mapping.fromJS({ ID: 1007, Name: 'Box8', Length: 6, Width: 6, Height: 6, AlgorithmPackingResults: [] })); 104 | self.Containers.push(ko.mapping.fromJS({ ID: 1008, Name: 'Box9', Length: 8, Width: 5, Height: 5, AlgorithmPackingResults: [] })); 105 | self.Containers.push(ko.mapping.fromJS({ ID: 1009, Name: 'Box10', Length: 18, Width: 13, Height: 8, AlgorithmPackingResults: [] })); 106 | self.Containers.push(ko.mapping.fromJS({ ID: 1010, Name: 'Box11', Length: 17, Width: 16, Height: 15, AlgorithmPackingResults: [] })); 107 | self.Containers.push(ko.mapping.fromJS({ ID: 1011, Name: 'Box12', Length: 32, Width: 10, Height: 9, AlgorithmPackingResults: [] })); 108 | self.Containers.push(ko.mapping.fromJS({ ID: 1012, Name: 'Box13', Length: 60, Width: 60, Height: 60, AlgorithmPackingResults: [] })); 109 | }; 110 | 111 | self.AddAlgorithmToUse = function () { 112 | var algorithmID = $('#algorithm-select option:selected').val(); 113 | var algorithmName = $('#algorithm-select option:selected').text(); 114 | self.AlgorithmsToUse.push({ AlgorithmID: algorithmID, AlgorithmName: algorithmName }); 115 | }; 116 | 117 | self.RemoveAlgorithmToUse = function (item) { 118 | self.AlgorithmsToUse.remove(item); 119 | }; 120 | 121 | self.AddNewItemToPack = function () { 122 | self.NewItemToPack.ID(self.ItemCounter++); 123 | self.ItemsToPack.push(ko.mapping.fromJS(ko.mapping.toJS(self.NewItemToPack))); 124 | self.NewItemToPack.Name(''); 125 | self.NewItemToPack.Length(''); 126 | self.NewItemToPack.Width(''); 127 | self.NewItemToPack.Height(''); 128 | self.NewItemToPack.Quantity(''); 129 | }; 130 | 131 | self.RemoveItemToPack = function (item) { 132 | self.ItemsToPack.remove(item); 133 | }; 134 | 135 | self.AddNewContainer = function () { 136 | self.NewContainer.ID(self.ContainerCounter++); 137 | self.Containers.push(ko.mapping.fromJS(ko.mapping.toJS(self.NewContainer))); 138 | self.NewContainer.Name(''); 139 | self.NewContainer.Length(''); 140 | self.NewContainer.Width(''); 141 | self.NewContainer.Height(''); 142 | }; 143 | 144 | self.RemoveContainer = function (item) { 145 | self.Containers.remove(item); 146 | }; 147 | 148 | self.PackContainers = function () { 149 | var algorithmsToUse = []; 150 | 151 | self.AlgorithmsToUse().forEach(algorithm => { 152 | algorithmsToUse.push(algorithm.AlgorithmID); 153 | }); 154 | 155 | var itemsToPack = []; 156 | 157 | self.ItemsToPack().forEach(item => { 158 | var itemToPack = { 159 | ID: item.ID(), 160 | Dim1: item.Length(), 161 | Dim2: item.Width(), 162 | Dim3: item.Height(), 163 | Quantity: item.Quantity() 164 | }; 165 | 166 | itemsToPack.push(itemToPack); 167 | }); 168 | 169 | var containers = []; 170 | 171 | // Send a packing request for each container in the list. 172 | self.Containers().forEach(container => { 173 | var containerToUse = { 174 | ID: container.ID(), 175 | Length: container.Length(), 176 | Width: container.Width(), 177 | Height: container.Height() 178 | }; 179 | 180 | containers.push(containerToUse); 181 | }); 182 | 183 | // Build container packing request. 184 | var request = { 185 | Containers: containers, 186 | ItemsToPack: itemsToPack, 187 | AlgorithmTypeIDs: algorithmsToUse 188 | }; 189 | 190 | PackContainers(JSON.stringify(request)) 191 | .then(response => { 192 | // Tie this response back to the correct containers. 193 | response.forEach(containerPackingResult => { 194 | self.Containers().forEach(container => { 195 | if (container.ID() == containerPackingResult.ContainerID) { 196 | container.AlgorithmPackingResults(containerPackingResult.AlgorithmPackingResults); 197 | } 198 | }); 199 | }); 200 | }); 201 | }; 202 | 203 | self.ShowPackingView = function (algorithmPackingResult) { 204 | var container = this; 205 | var selectedObject = scene.getObjectByName('container'); 206 | scene.remove( selectedObject ); 207 | 208 | for (var i = 0; i < 1000; i++) { 209 | var selectedObject = scene.getObjectByName('cube' + i); 210 | scene.remove(selectedObject); 211 | } 212 | 213 | camera.position.set(container.Length(), container.Length(), container.Length()); 214 | 215 | self.ItemsToRender(algorithmPackingResult.PackedItems); 216 | self.LastItemRenderedIndex(-1); 217 | 218 | self.ContainerOriginOffset.x = -1 * container.Length() / 2; 219 | self.ContainerOriginOffset.y = -1 * container.Height() / 2; 220 | self.ContainerOriginOffset.z = -1 * container.Width() / 2; 221 | 222 | var geometry = new THREE.BoxGeometry(container.Length(), container.Height(), container.Width()); 223 | var geo = new THREE.EdgesGeometry( geometry ); // or WireframeGeometry( geometry ) 224 | var mat = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 2 } ); 225 | var wireframe = new THREE.LineSegments( geo, mat ); 226 | wireframe.position.set(0, 0, 0); 227 | wireframe.name = 'container'; 228 | scene.add( wireframe ); 229 | }; 230 | 231 | self.AreItemsPacked = function () { 232 | if (self.LastItemRenderedIndex() > -1) { 233 | return true; 234 | } 235 | 236 | return false; 237 | }; 238 | 239 | self.AreAllItemsPacked = function () { 240 | if (self.ItemsToRender().length === self.LastItemRenderedIndex() + 1) { 241 | return true; 242 | } 243 | 244 | return false; 245 | }; 246 | 247 | self.PackItemInRender = function () { 248 | var itemIndex = self.LastItemRenderedIndex() + 1; 249 | 250 | var itemOriginOffset = { 251 | x: self.ItemsToRender()[itemIndex].PackDimX / 2, 252 | y: self.ItemsToRender()[itemIndex].PackDimY / 2, 253 | z: self.ItemsToRender()[itemIndex].PackDimZ / 2 254 | }; 255 | 256 | var itemGeometry = new THREE.BoxGeometry(self.ItemsToRender()[itemIndex].PackDimX, self.ItemsToRender()[itemIndex].PackDimY, self.ItemsToRender()[itemIndex].PackDimZ); 257 | var cube = new THREE.Mesh(itemGeometry, itemMaterial); 258 | 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); 259 | cube.name = 'cube' + itemIndex; 260 | scene.add( cube ); 261 | 262 | self.LastItemRenderedIndex(itemIndex); 263 | }; 264 | 265 | self.UnpackItemInRender = function () { 266 | var selectedObject = scene.getObjectByName('cube' + self.LastItemRenderedIndex()); 267 | scene.remove( selectedObject ); 268 | self.LastItemRenderedIndex(self.LastItemRenderedIndex() - 1); 269 | }; 270 | }; 271 | 272 | var ItemToPack = function () { 273 | this.ID = ''; 274 | this.Name = ''; 275 | this.Length = ''; 276 | this.Width = ''; 277 | this.Height = '', 278 | this.Quantity = ''; 279 | } 280 | 281 | var Container = function () { 282 | this.ID = ''; 283 | this.Name = ''; 284 | this.Length = ''; 285 | this.Width = ''; 286 | this.Height = ''; 287 | this.AlgorithmPackingResults = []; 288 | } 289 | 290 | $(document).ready(() => { 291 | $('[data-toggle="tooltip"]').tooltip(); 292 | InitializeDrawing(); 293 | 294 | viewModel = new ViewModel(); 295 | ko.applyBindings(viewModel); 296 | }); -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/js/vendor/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/js/vendor/ko-mapping.js: -------------------------------------------------------------------------------- 1 | /// Knockout Mapping plugin v2.4.1 2 | /// (c) 2013 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/ 3 | /// License: MIT (http://www.opensource.org/licenses/mit-license.php) 4 | (function (e) { "function" === typeof require && "object" === typeof exports && "object" === typeof module ? e(require("knockout"), exports) : "function" === typeof define && define.amd ? define(["knockout", "exports"], e) : e(ko, ko.mapping = {}) })(function (e, f) { 5 | function y(b, c) { 6 | var a, d; for (d in c) if (c.hasOwnProperty(d) && c[d]) if (a = f.getType(b[d]), d && b[d] && "array" !== a && "string" !== a) y(b[d], c[d]); else if ("array" === f.getType(b[d]) && "array" === f.getType(c[d])) { 7 | a = b; for (var e = d, l = b[d], n = c[d], t = {}, g = l.length - 1; 0 <= g; --g) t[l[g]] = l[g]; for (g = 8 | n.length - 1; 0 <= g; --g) t[n[g]] = n[g]; l = []; n = void 0; for (n in t) l.push(t[n]); a[e] = l 9 | } else b[d] = c[d] 10 | } function E(b, c) { var a = {}; y(a, b); y(a, c); return a } function z(b, c) { 11 | for (var a = E({}, b), e = L.length - 1; 0 <= e; e--) { var f = L[e]; a[f] && (a[""] instanceof Object || (a[""] = {}), a[""][f] = a[f], delete a[f]) } c && (a.ignore = h(c.ignore, a.ignore), a.include = h(c.include, a.include), a.copy = h(c.copy, a.copy), a.observe = h(c.observe, a.observe)); a.ignore = h(a.ignore, j.ignore); a.include = h(a.include, j.include); a.copy = h(a.copy, j.copy); a.observe = h(a.observe, 12 | j.observe); a.mappedProperties = a.mappedProperties || {}; a.copiedProperties = a.copiedProperties || {}; return a 13 | } function h(b, c) { "array" !== f.getType(b) && (b = "undefined" === f.getType(b) ? [] : [b]); "array" !== f.getType(c) && (c = "undefined" === f.getType(c) ? [] : [c]); return e.utils.arrayGetDistinctValues(b.concat(c)) } function F(b, c, a, d, k, l, n) { 14 | var t = "array" === f.getType(e.utils.unwrapObservable(c)); l = l || ""; if (f.isMapped(b)) { var g = e.utils.unwrapObservable(b)[p]; a = E(g, a) } var j = n || k, h = function () { 15 | return a[d] && a[d].create instanceof 16 | Function 17 | }, x = function (b) { 18 | var f = G, g = e.dependentObservable; e.dependentObservable = function (a, b, c) { c = c || {}; a && "object" == typeof a && (c = a); var d = c.deferEvaluation, M = !1; c.deferEvaluation = !0; a = new H(a, b, c); if (!d) { var g = a, d = e.dependentObservable; e.dependentObservable = H; a = e.isWriteableObservable(g); e.dependentObservable = d; d = H({ read: function () { M || (e.utils.arrayRemoveItem(f, g), M = !0); return g.apply(g, arguments) }, write: a && function (a) { return g(a) }, deferEvaluation: !0 }); d.__DO = g; a = d; f.push(a) } return a }; e.dependentObservable.fn = 19 | H.fn; e.computed = e.dependentObservable; b = e.utils.unwrapObservable(k) instanceof Array ? a[d].create({ data: b || c, parent: j, skip: N }) : a[d].create({ data: b || c, parent: j }); e.dependentObservable = g; e.computed = e.dependentObservable; return b 20 | }, u = function () { return a[d] && a[d].update instanceof Function }, v = function (b, f) { var g = { data: f || c, parent: j, target: e.utils.unwrapObservable(b) }; e.isWriteableObservable(b) && (g.observable = b); return a[d].update(g) }; if (n = I.get(c)) return n; d = d || ""; if (t) { 21 | var t = [], s = !1, m = function (a) { return a }; 22 | a[d] && a[d].key && (m = a[d].key, s = !0); e.isObservable(b) || (b = e.observableArray([]), b.mappedRemove = function (a) { var c = "function" == typeof a ? a : function (b) { return b === m(a) }; return b.remove(function (a) { return c(m(a)) }) }, b.mappedRemoveAll = function (a) { var c = C(a, m); return b.remove(function (a) { return -1 != e.utils.arrayIndexOf(c, m(a)) }) }, b.mappedDestroy = function (a) { var c = "function" == typeof a ? a : function (b) { return b === m(a) }; return b.destroy(function (a) { return c(m(a)) }) }, b.mappedDestroyAll = function (a) { 23 | var c = C(a, m); return b.destroy(function (a) { 24 | return -1 != 25 | e.utils.arrayIndexOf(c, m(a)) 26 | }) 27 | }, b.mappedIndexOf = function (a) { var c = C(b(), m); a = m(a); return e.utils.arrayIndexOf(c, a) }, b.mappedGet = function (a) { return b()[b.mappedIndexOf(a)] }, b.mappedCreate = function (a) { if (-1 !== b.mappedIndexOf(a)) throw Error("There already is an object with the key that you specified."); var c = h() ? x(a) : a; u() && (a = v(c, a), e.isWriteableObservable(c) ? c(a) : c = a); b.push(c); return c }); n = C(e.utils.unwrapObservable(b), m).sort(); g = C(c, m); s && g.sort(); s = e.utils.compareArrays(n, g); n = {}; var J, A = e.utils.unwrapObservable(c), 28 | y = {}, z = !0, g = 0; for (J = A.length; g < J; g++) { var r = m(A[g]); if (void 0 === r || r instanceof Object) { z = !1; break } y[r] = A[g] } var A = [], B = 0, g = 0; for (J = s.length; g < J; g++) { 29 | var r = s[g], q, w = l + "[" + g + "]"; switch (r.status) { 30 | case "added": var D = z ? y[r.value] : K(e.utils.unwrapObservable(c), r.value, m); q = F(void 0, D, a, d, b, w, k); h() || (q = e.utils.unwrapObservable(q)); w = O(e.utils.unwrapObservable(c), D, n); q === N ? B++ : A[w - B] = q; n[w] = !0; break; case "retained": D = z ? y[r.value] : K(e.utils.unwrapObservable(c), r.value, m); q = K(b, r.value, m); F(q, D, a, d, b, w, 31 | k); w = O(e.utils.unwrapObservable(c), D, n); A[w] = q; n[w] = !0; break; case "deleted": q = K(b, r.value, m) 32 | } t.push({ event: r.status, item: q }) 33 | } b(A); a[d] && a[d].arrayChanged && e.utils.arrayForEach(t, function (b) { a[d].arrayChanged(b.event, b.item) }) 34 | } else if (P(c)) { 35 | b = e.utils.unwrapObservable(b); if (!b) { if (h()) return s = x(), u() && (s = v(s)), s; if (u()) return v(s); b = {} } u() && (b = v(b)); I.save(c, b); if (u()) return b; Q(c, function (d) { 36 | var f = l.length ? l + "." + d : d; if (-1 == e.utils.arrayIndexOf(a.ignore, f)) if (-1 != e.utils.arrayIndexOf(a.copy, f)) b[d] = 37 | c[d]; else if ("object" != typeof c[d] && "array" != typeof c[d] && 0 < a.observe.length && -1 == e.utils.arrayIndexOf(a.observe, f)) b[d] = c[d], a.copiedProperties[f] = !0; else { var g = I.get(c[d]), k = F(b[d], c[d], a, d, b, f, b), g = g || k; if (0 < a.observe.length && -1 == e.utils.arrayIndexOf(a.observe, f)) b[d] = g(), a.copiedProperties[f] = !0; else { if (e.isWriteableObservable(b[d])) { if (g = e.utils.unwrapObservable(g), b[d]() !== g) b[d](g) } else g = void 0 === b[d] ? g : e.utils.unwrapObservable(g), b[d] = g; a.mappedProperties[f] = !0 } } 38 | }) 39 | } else switch (f.getType(c)) { 40 | case "function": u() ? 41 | e.isWriteableObservable(c) ? (c(v(c)), b = c) : b = v(c) : b = c; break; default: if (e.isWriteableObservable(b)) return q = u() ? v(b) : e.utils.unwrapObservable(c), b(q), q; h() || u(); b = h() ? x() : e.observable(e.utils.unwrapObservable(c)); u() && b(v(b)) 42 | } return b 43 | } function O(b, c, a) { for (var d = 0, e = b.length; d < e; d++) if (!0 !== a[d] && b[d] === c) return d; return null } function R(b, c) { var a; c && (a = c(b)); "undefined" === f.getType(a) && (a = b); return e.utils.unwrapObservable(a) } function K(b, c, a) { 44 | b = e.utils.unwrapObservable(b); for (var d = 0, f = b.length; d < 45 | f; d++) { var l = b[d]; if (R(l, a) === c) return l } throw Error("When calling ko.update*, the key '" + c + "' was not found!"); 46 | } function C(b, c) { return e.utils.arrayMap(e.utils.unwrapObservable(b), function (a) { return c ? R(a, c) : a }) } function Q(b, c) { if ("array" === f.getType(b)) for (var a = 0; a < b.length; a++) c(a); else for (a in b) c(a) } function P(b) { var c = f.getType(b); return ("object" === c || "array" === c) && null !== b } function T() { 47 | var b = [], c = []; this.save = function (a, d) { var f = e.utils.arrayIndexOf(b, a); 0 <= f ? c[f] = d : (b.push(a), c.push(d)) }; 48 | this.get = function (a) { a = e.utils.arrayIndexOf(b, a); return 0 <= a ? c[a] : void 0 } 49 | } function S() { var b = {}, c = function (a) { var c; try { c = a } catch (e) { c = "$$$" } a = b[c]; void 0 === a && (a = new T, b[c] = a); return a }; this.save = function (a, b) { c(a).save(a, b) }; this.get = function (a) { return c(a).get(a) } } var p = "__ko_mapping__", H = e.dependentObservable, B = 0, G, I, L = ["create", "update", "key", "arrayChanged"], N = {}, x = { include: ["_destroy"], ignore: [], copy: [], observe: [] }, j = x; f.isMapped = function (b) { return (b = e.utils.unwrapObservable(b)) && b[p] }; f.fromJS = 50 | function (b) { if (0 == arguments.length) throw Error("When calling ko.fromJS, pass the object you want to convert."); try { B++ || (G = [], I = new S); var c, a; 2 == arguments.length && (arguments[1][p] ? a = arguments[1] : c = arguments[1]); 3 == arguments.length && (c = arguments[1], a = arguments[2]); a && (c = E(c, a[p])); c = z(c); var d = F(a, b, c); a && (d = a); if (!--B) for (; G.length;) { var e = G.pop(); e && (e(), e.__DO.throttleEvaluation = e.throttleEvaluation) } d[p] = E(d[p], c); return d } catch (f) { throw B = 0, f; } }; f.fromJSON = function (b) { 51 | var c = e.utils.parseJson(b); 52 | arguments[0] = c; return f.fromJS.apply(this, arguments) 53 | }; f.updateFromJS = function () { throw Error("ko.mapping.updateFromJS, use ko.mapping.fromJS instead. Please note that the order of parameters is different!"); }; f.updateFromJSON = function () { throw Error("ko.mapping.updateFromJSON, use ko.mapping.fromJSON instead. Please note that the order of parameters is different!"); }; f.toJS = function (b, c) { 54 | j || f.resetDefaultOptions(); if (0 == arguments.length) throw Error("When calling ko.mapping.toJS, pass the object you want to convert."); 55 | if ("array" !== f.getType(j.ignore)) throw Error("ko.mapping.defaultOptions().ignore should be an array."); if ("array" !== f.getType(j.include)) throw Error("ko.mapping.defaultOptions().include should be an array."); if ("array" !== f.getType(j.copy)) throw Error("ko.mapping.defaultOptions().copy should be an array."); c = z(c, b[p]); return f.visitModel(b, function (a) { return e.utils.unwrapObservable(a) }, c) 56 | }; f.toJSON = function (b, c) { var a = f.toJS(b, c); return e.utils.stringifyJson(a) }; f.defaultOptions = function () { 57 | if (0 < arguments.length) j = 58 | arguments[0]; else return j 59 | }; f.resetDefaultOptions = function () { j = { include: x.include.slice(0), ignore: x.ignore.slice(0), copy: x.copy.slice(0) } }; f.getType = function (b) { if (b && "object" === typeof b) { if (b.constructor === Date) return "date"; if (b.constructor === Array) return "array" } return typeof b }; f.visitModel = function (b, c, a) { 60 | a = a || {}; a.visitedObjects = a.visitedObjects || new S; var d, k = e.utils.unwrapObservable(b); if (P(k)) a = z(a, k[p]), c(b, a.parentName), d = "array" === f.getType(k) ? [] : {}; else return c(b, a.parentName); a.visitedObjects.save(b, 61 | d); var l = a.parentName; Q(k, function (b) { 62 | if (!(a.ignore && -1 != e.utils.arrayIndexOf(a.ignore, b))) { 63 | var j = k[b], g = a, h = l || ""; "array" === f.getType(k) ? l && (h += "[" + b + "]") : (l && (h += "."), h += b); g.parentName = h; if (!(-1 === e.utils.arrayIndexOf(a.copy, b) && -1 === e.utils.arrayIndexOf(a.include, b) && k[p] && k[p].mappedProperties && !k[p].mappedProperties[b] && k[p].copiedProperties && !k[p].copiedProperties[b] && "array" !== f.getType(k))) switch (f.getType(e.utils.unwrapObservable(j))) { 64 | case "object": case "array": case "undefined": g = a.visitedObjects.get(j); 65 | d[b] = "undefined" !== f.getType(g) ? g : f.visitModel(j, c, a); break; default: d[b] = c(j, a.parentName) 66 | } 67 | } 68 | }); return d 69 | } 70 | }); -------------------------------------------------------------------------------- /src/CromulentBisgetti.DemoApp/wwwroot/js/vendor/ko.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Knockout JavaScript library v3.4.2 3 | * (c) The Knockout.js team - http://knockoutjs.com/ 4 | * License: MIT (http://www.opensource.org/licenses/mit-license.php) 5 | */ 6 | 7 | (function() {(function(n){var x=this||(0,eval)("this"),t=x.document,M=x.navigator,u=x.jQuery,H=x.JSON;(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(x.ko={})})(function(N,O){function J(a,c){return null===a||typeof a in R?a===c:!1}function S(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function T(b,c){var d;return function(){clearTimeout(d);d=a.a.setTimeout(b,c)}}function U(a, 8 | c){c&&c!==E?"beforeChange"===c?this.Ob(a):this.Ja(a,c):this.Pb(a)}function V(a,c){null!==c&&c.k&&c.k()}function W(a,c){var d=this.Mc,e=d[s];e.T||(this.ob&&this.Oa[c]?(d.Sb(c,a,this.Oa[c]),this.Oa[c]=null,--this.ob):e.s[c]||d.Sb(c,a,e.t?{$:a}:d.yc(a)),a.Ha&&a.Hc())}function K(b,c,d,e){a.d[b]={init:function(b,g,h,l,m){var k,r;a.m(function(){var q=g(),p=a.a.c(q),p=!d!==!p,A=!r;if(A||c||p!==k)A&&a.xa.Ca()&&(r=a.a.wa(a.f.childNodes(b),!0)),p?(A||a.f.fa(b,a.a.wa(r)),a.hb(e?e(m,q):m,b)):a.f.za(b),k=p},null, 9 | {i:b});return{controlsDescendantBindings:!0}}};a.h.va[b]=!1;a.f.aa[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;fa.a.o(c,b[d])&&c.push(b[d]);return c},ib:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;de?d&&b.push(c):d||b.splice(e,1)},la:f,extend:c,$a:d,ab:f?d:c,D:b,Ea:function(a,b){if(!a)return a;var c={},d;for(d in a)a.hasOwnProperty(d)&&(c[d]=b(a[d],d,a));return c},rb:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},nc:function(b){b=a.a.W(b);for(var c=(b[0]&&b[0].ownerDocument||t).createElement("div"),d=0,e=b.length;dk?a.setAttribute("selected",b):a.selected=b},cb:function(a){return null===a||a===n?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},sd:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},Rc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16== 16 | (b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},qb:function(b){return a.a.Rc(b,b.ownerDocument.documentElement)},Tb:function(b){return!!a.a.Vb(b,a.a.qb)},A:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},Zb:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Zb(b),c)},dc:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},q:function(b, 17 | c,d){var e=a.a.Zb(d);d=k&&m[c];if(a.options.useOnlyNativeEvents||d||!u)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var f=function(a){e.call(b,a)},l="on"+c;b.attachEvent(l,f);a.a.G.qa(b,function(){b.detachEvent(l,f)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else u(b).bind(c,e)},Fa:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"=== 18 | a.a.A(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!u||d)if("function"==typeof t.createEvent)if("function"==typeof b.dispatchEvent)d=t.createEvent(l[c]||"HTMLEvents"),d.initEvent(c,!0,!0,x,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events"); 19 | else u(b).trigger(c)},c:function(b){return a.I(b)?b():b},Bb:function(b){return a.I(b)?b.p():b},fb:function(b,c,d){var k;c&&("object"===typeof b.classList?(k=b.classList[d?"add":"remove"],a.a.r(c.match(r),function(a){k.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},bb:function(b,c){var d=a.a.c(c);if(null===d||d===n)d="";var e=a.f.firstChild(b);!e||3!=e.nodeType||a.f.nextSibling(e)?a.f.fa(b,[b.ownerDocument.createTextNode(d)]):e.data= 20 | d;a.a.Wc(b)},vc:function(a,b){a.name=b;if(7>=k)try{a.mergeAttributes(t.createElement(""),!1)}catch(c){}},Wc:function(a){9<=k&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},Sc:function(a){if(k){var b=a.style.width;a.style.width=0;a.style.width=b}},nd:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},W:function(a){for(var b=[],c=0,d=a.length;c",""],d=[3,"","
"],e=[1,""],f={thead:c,tbody:c,tfoot:c,tr:[2,"","
"],td:d,th:d,option:e,optgroup:e}, 29 | g=8>=a.a.C;a.a.na=function(c,d){var e;if(u)if(u.parseHTML)e=u.parseHTML(c,d)||[];else{if((e=u.clean([c],d))&&e[0]){for(var k=e[0];k.parentNode&&11!==k.parentNode.nodeType;)k=k.parentNode;k.parentNode&&k.parentNode.removeChild(k)}}else{(e=d)||(e=t);var k=e.parentWindow||e.defaultView||x,r=a.a.cb(c).toLowerCase(),q=e.createElement("div"),p;p=(r=r.match(/^<([a-z]+)[ >]/))&&f[r[1]]||b;r=p[0];p="ignored
"+p[1]+c+p[2]+"
";"function"==typeof k.innerShiv?q.appendChild(k.innerShiv(p)):(g&&e.appendChild(q), 30 | q.innerHTML=p,g&&q.parentNode.removeChild(q));for(;r--;)q=q.lastChild;e=a.a.W(q.lastChild.childNodes)}return e};a.a.Eb=function(b,c){a.a.rb(b);c=a.a.c(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),u)u(b).html(c);else for(var d=a.a.na(c,b.ownerDocument),e=0;eb){if(5E3<=++c){g=e;a.a.dc(Error("'Too much recursion' after processing "+c+" task groups."));break}b=e}try{m()}catch(k){a.a.dc(k)}}}function c(){b();g=e=d.length=0}var d=[],e=0,f=1,g=0;return{scheduler:x.MutationObserver?function(a){var b=t.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):t&&"onreadystatechange"in t.createElement("script")?function(a){var b=t.createElement("script");b.onreadystatechange= 34 | function(){b.onreadystatechange=null;t.documentElement.removeChild(b);b=null;a()};t.documentElement.appendChild(b)}:function(a){setTimeout(a,0)},Za:function(b){e||a.Z.scheduler(c);d[e++]=b;return f++},cancel:function(a){a-=f-e;a>=g&&ad[0]?l+d[0]:d[0]),l);for(var l=1===g?l:Math.min(c+(d[1]||0),l),g=c+g-2,G=Math.max(l,g),n=[],s=[],w=2;cc;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.Ac(b);return a.a.Gb(b,c,d)};d.prototype={save:function(b,c){var d=a.a.o(this.keys, 57 | b);0<=d?this.Lb[d]=c:(this.keys.push(b),this.Lb.push(c))},get:function(b){b=a.a.o(this.keys,b);return 0<=b?this.Lb[b]:n}}})();a.b("toJS",a.Ac);a.b("toJSON",a.toJSON);(function(){a.j={u:function(b){switch(a.a.A(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.e.get(b,a.d.options.zb):7>=a.a.C?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex]):n;default:return b.value}},ja:function(b, 58 | c,d){switch(a.a.A(b)){case "option":switch(typeof c){case "string":a.a.e.set(b,a.d.options.zb,n);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.e.set(b,a.d.options.zb,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof c?c:""}break;case "select":if(""===c||null===c)c=n;for(var e=-1,f=0,g=b.options.length,h;f=p){c.push(r&&h.length?{key:r,value:h.join("")}:{unknown:r||h.join("")});r=p=0;h=[];continue}}else if(58===v){if(!p&&!r&&1===h.length){r=h.pop();continue}}else 47=== 60 | v&&A&&1=a.a.C&&b.tagName===c))return c};a.g.Rb=function(c,e,f,g){if(1===e.nodeType){var h=a.g.getComponentNameForNode(e);if(h){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');var l={name:h,params:b(e,f)};c.component=g?function(){return l}: 85 | l}}return c};var c=new a.S;9>a.a.C&&(a.g.register=function(a){return function(b){t.createElement(b);return a.apply(this,arguments)}}(a.g.register),t.createDocumentFragment=function(b){return function(){var c=b(),f=a.g.Ec,g;for(g in f)f.hasOwnProperty(g)&&c.createElement(g);return c}}(t.createDocumentFragment))})();(function(b){function c(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.wa(c);a.f.fa(d,b)}function d(a,b,c,d){var e=a.createViewModel;return e?e.call(a, 86 | d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,g,h,l,m){function k(){var a=r&&r.dispose;"function"===typeof a&&a.call(r);q=r=null}var r,q,p=a.a.W(a.f.childNodes(f));a.a.G.qa(f,k);a.m(function(){var l=a.a.c(g()),h,v;"string"===typeof l?h=l:(h=a.a.c(l.name),v=a.a.c(l.params));if(!h)throw Error("No component name specified");var n=q=++e;a.g.get(h,function(e){if(q===n){k();if(!e)throw Error("Unknown component '"+h+"'");c(h,e,f);var l=d(e,f,p,v);e=m.createChildContext(l,b,function(a){a.$component= 87 | l;a.$componentTemplateNodes=p});r=l;a.hb(e,f)}})},null,{i:f});return{controlsDescendantBindings:!0}}};a.f.aa.component=!0})();var Q={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.D(d,function(c,d){d=a.a.c(d);var g=!1===d||null===d||d===n;g&&b.removeAttribute(c);8>=a.a.C&&c in Q?(c=Q[c],g?b.removeAttribute(c):b[c]=d):g||b.setAttribute(c,d.toString());"name"===c&&a.a.vc(b,g?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b, 88 | c,d){function e(){var e=b.checked,f=p?g():e;if(!a.xa.Va()&&(!l||e)){var h=a.l.w(c);if(k){var m=r?h.p():h;q!==f?(e&&(a.a.ra(m,f,!0),a.a.ra(m,q,!1)),q=f):a.a.ra(m,f,e);r&&a.Da(h)&&h(m)}else a.h.Ga(h,d,"checked",f,!0)}}function f(){var d=a.a.c(c());b.checked=k?0<=a.a.o(d,g()):h?d:g()===d}var g=a.rc(function(){return d.has("checkedValue")?a.a.c(d.get("checkedValue")):d.has("value")?a.a.c(d.get("value")):b.value}),h="checkbox"==b.type,l="radio"==b.type;if(h||l){var m=c(),k=h&&a.a.c(m)instanceof Array, 89 | r=!(k&&m.push&&m.splice),q=k?g():n,p=l||k;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.m(e,null,{i:b});a.a.q(b,"click",e);a.m(f,null,{i:b});m=n}}};a.h.ga.checked=!0;a.d.checkedValue={update:function(b,c){b.value=a.a.c(c())}}})();a.d.css={update:function(b,c){var d=a.a.c(c());null!==d&&"object"==typeof d?a.a.D(d,function(c,d){d=a.a.c(d);a.a.fb(b,c,d)}):(d=a.a.cb(String(d||"")),a.a.fb(b,b.__ko__cssValue,!1),b.__ko__cssValue=d,a.a.fb(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c()); 90 | d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,e,f){var g=c()||{};a.a.D(g,function(g){"string"==typeof g&&a.a.q(b,g,function(b){var m,k=c()[g];if(k){try{var r=a.a.W(arguments);e=f.$data;r.unshift(e);m=k.apply(e,r)}finally{!0!==m&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}}; 91 | a.d.foreach={mc:function(b){return function(){var c=b(),d=a.a.Bb(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.X.vb};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.X.vb}}},init:function(b,c){return a.d.template.init(b,a.d.foreach.mc(c))},update:function(b,c,d,e,f){return a.d.template.update(b,a.d.foreach.mc(c), 92 | d,e,f)}};a.h.va.foreach=!1;a.f.aa.foreach=!0;a.d.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(k){g=f.body}e=g===b}f=c();a.h.Ga(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.q(b,"focus",f);a.a.q(b,"focusin",f);a.a.q(b,"blur",g);a.a.q(b,"focusout",g)},update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue=== 93 | d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.l.w(a.a.Fa,null,[b,d?"focusin":"focusout"]))}};a.h.ga.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.ga.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Eb(b,c())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,c){return a.ac(c)});var L={};a.d.options={init:function(b){if("select"!==a.a.A(b))throw Error("options binding applies only to SELECT elements");for(;0< 94 | b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function e(){return a.a.Ma(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(c,e){if(A&&k)a.j.ja(b,a.a.c(d.get("value")),!0);else if(p.length){var f=0<=a.a.o(p,a.j.u(e[0]));a.a.wc(e[0],f);A&&!f&&a.l.w(a.a.Fa,null,[b,"change"])}}var h=b.multiple,l=0!=b.length&&h?b.scrollTop:null,m=a.a.c(c()),k=d.get("valueAllowUnset")&&d.has("value"),r= 95 | d.get("optionsIncludeDestroyed");c={};var q,p=[];k||(h?p=a.a.ib(e(),a.j.u):0<=b.selectedIndex&&p.push(a.j.u(b.options[b.selectedIndex])));m&&("undefined"==typeof m.length&&(m=[m]),q=a.a.Ma(m,function(b){return r||b===n||null===b||!a.a.c(b._destroy)}),d.has("optionsCaption")&&(m=a.a.c(d.get("optionsCaption")),null!==m&&m!==n&&q.unshift(L)));var A=!1;c.beforeRemove=function(a){b.removeChild(a)};m=g;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(m=function(b,c){g(0,c); 96 | a.l.w(d.get("optionsAfterRender"),null,[c[0],b!==L?b:n])});a.a.Db(b,q,function(c,e,g){g.length&&(p=!k&&g[0].selected?[a.j.u(g[0])]:[],A=!0);e=b.ownerDocument.createElement("option");c===L?(a.a.bb(e,d.get("optionsCaption")),a.j.ja(e,n)):(g=f(c,d.get("optionsValue"),c),a.j.ja(e,a.a.c(g)),c=f(c,d.get("optionsText"),g),a.a.bb(e,c));return[e]},c,m);a.l.w(function(){k?a.j.ja(b,a.a.c(d.get("value")),!0):(h?p.length&&e().lengtha.a.C)var g=a.a.e.J(),h=a.a.e.J(),l=function(b){var c= 100 | this.activeElement;(c=c&&a.a.e.get(c,h))&&c(b)},m=function(b,c){var d=b.ownerDocument;a.a.e.get(d,g)||(a.a.e.set(d,g,!0),a.a.q(d,"selectionchange",l));a.a.e.set(b,h,c)};a.d.textInput={init:function(b,d,g){function l(c,d){a.a.q(b,c,d)}function h(){var c=a.a.c(d());if(null===c||c===n)c="";u!==n&&c===u?a.a.setTimeout(h,4):b.value!==c&&(s=c,b.value=c)}function y(){t||(u=b.value,t=a.a.setTimeout(v,4))}function v(){clearTimeout(t);u=t=n;var c=b.value;s!==c&&(s=c,a.h.Ga(d(),g,"textInput",c))}var s=b.value, 101 | t,u,x=9==a.a.C?y:v;10>a.a.C?(l("propertychange",function(a){"value"===a.propertyName&&x(a)}),8==a.a.C&&(l("keyup",v),l("keydown",v)),8<=a.a.C&&(m(b,x),l("dragend",y))):(l("input",v),5>e&&"textarea"===a.a.A(b)?(l("keydown",y),l("paste",y),l("cut",y)):11>c?l("keydown",y):4>f&&(l("DOMAutoComplete",v),l("dragdrop",v),l("drop",v)));l("change",v);a.m(h,null,{i:b})}};a.h.ga.textInput=!0;a.d.textinput={preprocess:function(a,b,c){c("textInput",a)}}})();a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+ 102 | ++a.d.uniqueName.Nc;a.a.vc(b,d)}}};a.d.uniqueName.Nc=0;a.d.value={after:["options","foreach"],init:function(b,c,d){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=d.get("valueUpdate"),g=!1,h=null;f&&("string"==typeof f&&(f=[f]),a.a.ta(e,f),e=a.a.Wb(e));var l=function(){h=null;g=!1;var e=c(),f=a.j.u(b);a.h.Ga(e,d,"value",f)};!a.a.C||"input"!=b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.o(e,"propertychange")|| 103 | (a.a.q(b,"propertychange",function(){g=!0}),a.a.q(b,"focus",function(){g=!1}),a.a.q(b,"blur",function(){g&&l()}));a.a.r(e,function(c){var d=l;a.a.sd(c,"after")&&(d=function(){h=a.j.u(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.q(b,c,d)});var m=function(){var e=a.a.c(c()),f=a.j.u(b);if(null!==h&&e===h)a.a.setTimeout(m,0);else if(e!==f)if("select"===a.a.A(b)){var g=d.get("valueAllowUnset"),f=function(){a.j.ja(b,e,g)};f();g||e===a.j.u(b)?a.a.setTimeout(f,0):a.l.w(a.a.Fa,null,[b,"change"])}else a.j.ja(b, 104 | e)};a.m(m,null,{i:b})}else a.La(b,{checkedValue:c})},update:function(){}};a.h.ga.value=!0;a.d.visible={update:function(b,c){var d=a.a.c(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(c,d,e,f,g){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click");a.P=function(){};a.P.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");};a.P.prototype.createJavaScriptEvaluatorBlock= 105 | function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.P.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||t;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.v.n(d)}if(1==b.nodeType||8==b.nodeType)return new a.v.sa(b);throw Error("Unknown template type: "+b);};a.P.prototype.renderTemplate=function(a,c,d,e){a=this.makeTemplateSource(a,e);return this.renderTemplateSource(a,c,d,e)};a.P.prototype.isTemplateRewritten=function(a, 106 | c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.P.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.P);a.Ib=function(){function b(b,c,d,h){b=a.h.Ab(b);for(var l=a.h.va,m=0;m]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Tc:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Ib.jd(b, 108 | c)},d)},jd:function(a,f){return a.replace(c,function(a,c,d,e,k){return b(k,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},Jc:function(b,c){return a.N.yb(function(d,h){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.La(l,b,h)})}}}();a.b("__tr_ambtns",a.Ib.Jc);(function(){a.v={};a.v.n=function(b){if(this.n=b){var c=a.a.A(b);this.eb="script"===c?1:"textarea"===c?2:"template"==c&&b.content&&11===b.content.nodeType?3:4}};a.v.n.prototype.text=function(){var b=1=== 109 | this.eb?"text":2===this.eb?"value":"innerHTML";if(0==arguments.length)return this.n[b];var c=arguments[0];"innerHTML"===b?a.a.Eb(this.n,c):this.n[b]=c};var b=a.a.e.J()+"_";a.v.n.prototype.data=function(c){if(1===arguments.length)return a.a.e.get(this.n,b+c);a.a.e.set(this.n,b+c,arguments[1])};var c=a.a.e.J();a.v.n.prototype.nodes=function(){var b=this.n;if(0==arguments.length)return(a.a.e.get(b,c)||{}).mb||(3===this.eb?b.content:4===this.eb?b:n);a.a.e.set(b,c,{mb:arguments[0]})};a.v.sa=function(a){this.n= 110 | a};a.v.sa.prototype=new a.v.n;a.v.sa.prototype.text=function(){if(0==arguments.length){var b=a.a.e.get(this.n,c)||{};b.Jb===n&&b.mb&&(b.Jb=b.mb.innerHTML);return b.Jb}a.a.e.set(this.n,c,{Jb:arguments[0]})};a.b("templateSources",a.v);a.b("templateSources.domElement",a.v.n);a.b("templateSources.anonymousTemplate",a.v.sa)})();(function(){function b(b,c,d){var e;for(c=a.f.nextSibling(c);b&&(e=b)!==c;)b=a.f.nextSibling(e),d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,h= 111 | a.S.instance,n=h.preprocessNode;if(n){b(e,f,function(a,b){var c=a.previousSibling,d=n.call(h,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.Ba(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.Ub(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.N.Cc(b,[d])});a.a.Ba(c,g)}}function d(a){return a.nodeType?a:0a.a.C?0:b.nodes)?b.nodes():null)return a.a.W(c.cloneNode(!0).childNodes);b=b.text(); 122 | return a.a.na(b,e)};a.X.vb=new a.X;a.Fb(a.X.vb);a.b("nativeTemplateEngine",a.X);(function(){a.xb=function(){var a=this.ed=function(){if(!u||!u.tmpl)return 0;try{if(0<=u.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,e,f,g){g=g||t;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=u.template(null,"{{ko_with $item.koBindingContext}}"+ 123 | h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=u.extend({koBindingContext:e},f.templateOptions);e=u.tmpl(h,b,e);e.appendTo(g.createElement("div"));u.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){t.write("