[13605] | 1 | /* HeuristicLab
|
---|
| 2 | * Copyright (C) 2002-2015 Joseph Helm and Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
| 3 | *
|
---|
| 4 | * This file is part of HeuristicLab.
|
---|
| 5 | *
|
---|
| 6 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
| 7 | * it under the terms of the GNU General Public License as published by
|
---|
| 8 | * the Free Software Foundation, either version 3 of the License, or
|
---|
| 9 | * (at your option) any later version.
|
---|
| 10 | *
|
---|
| 11 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 14 | * GNU General Public License for more details.
|
---|
| 15 | *
|
---|
| 16 | * You should have received a copy of the GNU General Public License
|
---|
| 17 | * along with HeuristicLab. If not, see<http://www.gnu.org/licenses/> .
|
---|
| 18 | */
|
---|
| 19 |
|
---|
| 20 | using System;
|
---|
[13532] | 21 | using System.Linq;
|
---|
| 22 | using System.Windows;
|
---|
| 23 | using System.Windows.Controls;
|
---|
| 24 | using System.Windows.Input;
|
---|
| 25 | using System.Windows.Media.Media3D;
|
---|
[14046] | 26 | using HeuristicLab.Problems.BinPacking3D;
|
---|
[13532] | 27 |
|
---|
[13576] | 28 | namespace HeuristicLab.Problems.BinPacking.Views {
|
---|
[13532] | 29 | public partial class Container3DView : UserControl {
|
---|
| 30 | private Point startPos;
|
---|
| 31 | private bool mouseDown = false;
|
---|
| 32 | private double startAngleX;
|
---|
| 33 | private double startAngleY;
|
---|
[13558] | 34 | private int selectedItemKey;
|
---|
[13532] | 35 |
|
---|
[14049] | 36 | private BinPacking<BinPacking3D.PackingPosition, PackingShape, PackingItem> packing;
|
---|
| 37 | public BinPacking<BinPacking3D.PackingPosition, PackingShape, PackingItem> Packing {
|
---|
[13558] | 38 | get { return packing; }
|
---|
| 39 | set {
|
---|
| 40 | if (packing != value) {
|
---|
| 41 | this.packing = value;
|
---|
| 42 | ClearSelection(); // also updates visualization
|
---|
| 43 | }
|
---|
| 44 | }
|
---|
| 45 | }
|
---|
| 46 |
|
---|
[13532] | 47 | public Container3DView() {
|
---|
| 48 | InitializeComponent();
|
---|
[14154] | 49 | camMain.Position = new Point3D(0.5, 3, 3); // for design time we use a different camera position
|
---|
[13532] | 50 | Clear();
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 |
|
---|
[13558] | 54 | protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) {
|
---|
| 55 | base.OnRenderSizeChanged(sizeInfo);
|
---|
| 56 | var s = Math.Min(sizeInfo.NewSize.Height, sizeInfo.NewSize.Width);
|
---|
| 57 | var mySize = new Size(s, s);
|
---|
| 58 | viewport3D1.RenderSize = mySize;
|
---|
| 59 | }
|
---|
[13532] | 60 |
|
---|
[13558] | 61 | public void SelectItem(int itemKey) {
|
---|
| 62 | // selection of an item should make all other items semi-transparent
|
---|
| 63 | selectedItemKey = itemKey;
|
---|
| 64 | UpdateVisualization();
|
---|
[13532] | 65 | }
|
---|
[13558] | 66 | public void ClearSelection() {
|
---|
| 67 | // remove all transparency
|
---|
| 68 | selectedItemKey = -1;
|
---|
| 69 | UpdateVisualization();
|
---|
| 70 | }
|
---|
[13532] | 71 |
|
---|
[13558] | 72 | private void UpdateVisualization() {
|
---|
| 73 | Clear();
|
---|
| 74 | if (packing == null) return; // nothing to display
|
---|
[13532] | 75 |
|
---|
[13558] | 76 | // draw all items
|
---|
| 77 | // order by Z position to reduce artifacts (because of transparent objects)
|
---|
| 78 | // TODO: improve code to reduce artifacts
|
---|
| 79 | // - from triangle definitions and lighting
|
---|
| 80 | // - from rotation and Z-ordering
|
---|
[13532] | 81 |
|
---|
[14154] | 82 | foreach (var item in packing.Items.OrderBy(i => packing.Positions[i.Key].Z)) {
|
---|
| 83 | var position = packing.Positions[item.Key];
|
---|
[13532] | 84 |
|
---|
[13558] | 85 | var w = position.Rotated ? item.Value.Depth : item.Value.Width;
|
---|
| 86 | var h = item.Value.Height;
|
---|
| 87 | var d = position.Rotated ? item.Value.Width : item.Value.Depth;
|
---|
| 88 |
|
---|
[13532] | 89 | // ignore the item.Material
|
---|
| 90 |
|
---|
[13558] | 91 | // if nothing is selected then draw all cubes opaque
|
---|
| 92 | // otherwise draw only the selected cube opaque and all others transparent
|
---|
| 93 | if (selectedItemKey < 0 || selectedItemKey == item.Key) {
|
---|
| 94 | AddCube(meshMain, position.X, position.Y, position.Z, w, h, d);
|
---|
| 95 | } else {
|
---|
| 96 | AddCube(meshTransparent, position.X, position.Y, position.Z, w, h, d, addInsideTriangles: true);
|
---|
| 97 | }
|
---|
[13532] | 98 | }
|
---|
| 99 |
|
---|
[14154] | 100 | var container = packing.BinShape;
|
---|
[13558] | 101 | // draw a transparent container
|
---|
| 102 | AddCube(meshTransparent, container.Origin.X, container.Origin.Y, container.Origin.Z, container.Width, container.Height, container.Depth, addInsideTriangles: true);
|
---|
| 103 |
|
---|
| 104 | // TODO: support cuboids with different side lengths
|
---|
| 105 | // apply scaling so that the container fits into the unit cube (necessary for the transformations)
|
---|
| 106 | scale.ScaleX = 1.0 / (container.Width);
|
---|
| 107 | scale.ScaleY = 1.0 / (container.Height);
|
---|
| 108 | scale.ScaleZ = 1.0 / (container.Depth);
|
---|
[13532] | 109 | }
|
---|
| 110 |
|
---|
[13558] | 111 |
|
---|
| 112 | private void Clear() {
|
---|
| 113 | meshInsides.Positions.Clear();
|
---|
| 114 | meshInsides.TriangleIndices.Clear();
|
---|
| 115 |
|
---|
| 116 | meshMain.Positions.Clear();
|
---|
| 117 | meshMain.TriangleIndices.Clear();
|
---|
| 118 |
|
---|
| 119 | meshTransparent.Positions.Clear();
|
---|
| 120 | meshTransparent.TriangleIndices.Clear();
|
---|
| 121 |
|
---|
| 122 | mouseDown = false;
|
---|
| 123 | startAngleX = 0;
|
---|
| 124 | startAngleY = 0;
|
---|
[13532] | 125 | }
|
---|
| 126 |
|
---|
[13558] | 127 | private void Container3DView_MouseMove(object sender, MouseEventArgs e) {
|
---|
[13532] | 128 | if (!mouseDown) return;
|
---|
| 129 | var pos = e.GetPosition((IInputElement)this);
|
---|
| 130 | rotateX.Angle = startAngleX + (pos.X - startPos.X) / 4;
|
---|
| 131 | rotateY.Angle = startAngleY + (pos.Y - startPos.Y) / 4;
|
---|
| 132 | }
|
---|
| 133 |
|
---|
[13558] | 134 | private void Container3DView_MouseDown(object sender, MouseButtonEventArgs e) {
|
---|
[13532] | 135 | startAngleX = rotateX.Angle;
|
---|
| 136 | startAngleY = rotateY.Angle;
|
---|
| 137 | this.startPos = e.GetPosition((IInputElement)this);
|
---|
| 138 | this.mouseDown = true;
|
---|
| 139 | }
|
---|
| 140 |
|
---|
[13558] | 141 | private void Container3DView_MouseUp(object sender, MouseButtonEventArgs e) {
|
---|
[13532] | 142 | mouseDown = false;
|
---|
| 143 | }
|
---|
| 144 |
|
---|
[13558] | 145 | private void Container3DView_OnMouseWheel(object sender, MouseWheelEventArgs e) {
|
---|
| 146 | if (e.Delta > 0) {
|
---|
| 147 | scaleZoom.ScaleX *= 1.1;
|
---|
| 148 | scaleZoom.ScaleY *= 1.1;
|
---|
| 149 | scaleZoom.ScaleZ *= 1.1;
|
---|
| 150 | } else if (e.Delta < 0) {
|
---|
| 151 | scaleZoom.ScaleX /= 1.1;
|
---|
| 152 | scaleZoom.ScaleY /= 1.1;
|
---|
| 153 | scaleZoom.ScaleZ /= 1.1;
|
---|
| 154 | }
|
---|
| 155 | }
|
---|
[13532] | 156 |
|
---|
[13558] | 157 |
|
---|
[13532] | 158 | #region helper for cubes
|
---|
| 159 |
|
---|
[13558] | 160 | private void AddCube(MeshGeometry3D mesh, int x, int y, int z, int width, int height, int depth, bool addInsideTriangles = false) {
|
---|
| 161 | AddOutsideTriangles(mesh, AddPoints(mesh, x, y, z, width, height, depth));
|
---|
| 162 | if (addInsideTriangles) AddInsideTriangles(meshInsides, AddPoints(meshInsides, x, y, z, width, height, depth));
|
---|
[13532] | 163 | }
|
---|
| 164 |
|
---|
[13558] | 165 | private void AddOutsideTriangles(MeshGeometry3D mesh, int[] pointIdx) {
|
---|
| 166 | // point indices counter-clockwise
|
---|
[13532] | 167 | // back side
|
---|
[13558] | 168 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 169 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 170 | mesh.TriangleIndices.Add(pointIdx[1]);
|
---|
[13532] | 171 |
|
---|
[13558] | 172 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 173 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 174 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
[13532] | 175 |
|
---|
[13558] | 176 | // bottom side
|
---|
| 177 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
| 178 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 179 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 180 |
|
---|
| 181 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
| 182 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 183 | mesh.TriangleIndices.Add(pointIdx[1]);
|
---|
| 184 |
|
---|
[13532] | 185 | // right side
|
---|
[13558] | 186 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 187 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 188 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
[13532] | 189 |
|
---|
[13558] | 190 | mesh.TriangleIndices.Add(pointIdx[1]);
|
---|
| 191 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 192 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
[13532] | 193 |
|
---|
| 194 | // left side
|
---|
[13558] | 195 | mesh.TriangleIndices.Add(pointIdx[7]);
|
---|
| 196 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 197 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
[13532] | 198 |
|
---|
[13558] | 199 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 200 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 201 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
[13532] | 202 |
|
---|
[13558] | 203 | // top side
|
---|
| 204 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 205 | mesh.TriangleIndices.Add(pointIdx[7]);
|
---|
| 206 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 207 |
|
---|
| 208 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 209 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 210 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 211 |
|
---|
[13532] | 212 | // front side
|
---|
[13558] | 213 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 214 | mesh.TriangleIndices.Add(pointIdx[7]);
|
---|
| 215 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
[13532] | 216 |
|
---|
[13558] | 217 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 218 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 219 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
| 220 | }
|
---|
[13532] | 221 |
|
---|
[13558] | 222 | private void AddInsideTriangles(MeshGeometry3D mesh, int[] pointIdx) {
|
---|
| 223 | // for each cube we also draw the triangles facing inside because they are visible when a cube is transparent
|
---|
| 224 | // point indices clockwise
|
---|
[13532] | 225 |
|
---|
[13558] | 226 | // back side
|
---|
| 227 | mesh.TriangleIndices.Add(pointIdx[1]);
|
---|
| 228 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 229 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
[13532] | 230 |
|
---|
[13558] | 231 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 232 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 233 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 234 |
|
---|
[13532] | 235 | // bottom side
|
---|
[13558] | 236 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 237 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 238 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
[13532] | 239 |
|
---|
[13558] | 240 | mesh.TriangleIndices.Add(pointIdx[1]);
|
---|
| 241 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 242 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
[13532] | 243 |
|
---|
[13558] | 244 | // right side
|
---|
| 245 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
| 246 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 247 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
[13532] | 248 |
|
---|
[13558] | 249 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
| 250 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 251 | mesh.TriangleIndices.Add(pointIdx[1]);
|
---|
| 252 |
|
---|
| 253 | // left side
|
---|
| 254 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 255 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 256 | mesh.TriangleIndices.Add(pointIdx[7]);
|
---|
| 257 |
|
---|
| 258 | mesh.TriangleIndices.Add(pointIdx[0]);
|
---|
| 259 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 260 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 261 |
|
---|
| 262 | // top side
|
---|
| 263 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 264 | mesh.TriangleIndices.Add(pointIdx[7]);
|
---|
| 265 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 266 |
|
---|
| 267 | mesh.TriangleIndices.Add(pointIdx[2]);
|
---|
| 268 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 269 | mesh.TriangleIndices.Add(pointIdx[3]);
|
---|
| 270 |
|
---|
| 271 | // front side
|
---|
| 272 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 273 | mesh.TriangleIndices.Add(pointIdx[7]);
|
---|
| 274 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
| 275 |
|
---|
| 276 | mesh.TriangleIndices.Add(pointIdx[5]);
|
---|
| 277 | mesh.TriangleIndices.Add(pointIdx[4]);
|
---|
| 278 | mesh.TriangleIndices.Add(pointIdx[6]);
|
---|
[13532] | 279 | }
|
---|
| 280 |
|
---|
[13558] | 281 | private int[] AddPoints(MeshGeometry3D mesh, int x, int y, int z, int w, int h, int d) {
|
---|
[13532] | 282 | // ground
|
---|
[13558] | 283 | mesh.Positions.Add(new Point3D(x, y, z));
|
---|
| 284 | mesh.Positions.Add(new Point3D(x + w, y, z));
|
---|
| 285 | mesh.Positions.Add(new Point3D(x + w, y + h, z));
|
---|
| 286 | mesh.Positions.Add(new Point3D(x, y + h, z));
|
---|
[13532] | 287 | // top
|
---|
[13558] | 288 | mesh.Positions.Add(new Point3D(x, y, z + d));
|
---|
| 289 | mesh.Positions.Add(new Point3D(x + w, y, z + d));
|
---|
| 290 | mesh.Positions.Add(new Point3D(x + w, y + h, z + d));
|
---|
| 291 | mesh.Positions.Add(new Point3D(x, y + h, z + d));
|
---|
[13532] | 292 |
|
---|
[13558] | 293 | return Enumerable.Range(mesh.Positions.Count - 8, 8).ToArray();
|
---|
[13532] | 294 | }
|
---|
| 295 | #endregion
|
---|
[13558] | 296 |
|
---|
| 297 | private void Container3DView_OnMouseEnter(object sender, MouseEventArgs e) {
|
---|
| 298 | Focus(); // for mouse wheel events
|
---|
| 299 | }
|
---|
[13532] | 300 | }
|
---|
| 301 | }
|
---|