[9730] | 1 | namespace MIConvexHull
|
---|
| 2 | {
|
---|
| 3 | using System;
|
---|
| 4 | using System.Collections.Generic;
|
---|
| 5 | using System.Linq;
|
---|
| 6 |
|
---|
| 7 | internal class ConvexHullInternal
|
---|
| 8 | {
|
---|
| 9 | bool Computed;
|
---|
| 10 | readonly int Dimension;
|
---|
| 11 |
|
---|
| 12 | List<VertexWrap> InputVertices;
|
---|
| 13 | List<VertexWrap> ConvexHull;
|
---|
| 14 | FaceList UnprocessedFaces;
|
---|
| 15 | List<ConvexFaceInternal> ConvexFaces;
|
---|
| 16 |
|
---|
| 17 | #region "Buffers"
|
---|
| 18 | VertexWrap CurrentVertex;
|
---|
| 19 | double MaxDistance;
|
---|
| 20 | VertexWrap FurthestVertex;
|
---|
| 21 |
|
---|
| 22 | /// <summary>
|
---|
| 23 | /// The centroid of the currently computed hull.
|
---|
| 24 | /// </summary>
|
---|
| 25 | double[] Center;
|
---|
| 26 |
|
---|
| 27 | // Buffers for normal computation.
|
---|
| 28 | double[] ntX, ntY, ntZ;
|
---|
| 29 | double[] nDNormalSolveVector;
|
---|
| 30 | double[,] nDMatrix;
|
---|
| 31 | double[][] jaggedNDMatrix;
|
---|
| 32 |
|
---|
| 33 | ConvexFaceInternal[] UpdateBuffer;
|
---|
| 34 | int[] UpdateIndices;
|
---|
| 35 |
|
---|
| 36 |
|
---|
| 37 | Stack<ConvexFaceInternal> TraverseStack;
|
---|
| 38 | Stack<ConvexFaceInternal> RecycledFaceStack;
|
---|
| 39 | Stack<FaceConnector> ConnectorStack;
|
---|
| 40 | Stack<VertexBuffer> EmptyBufferStack;
|
---|
| 41 | VertexBuffer EmptyBuffer; // this is used for VerticesBeyond for faces that are on the convex hull
|
---|
| 42 | VertexBuffer BeyondBuffer;
|
---|
| 43 | List<ConvexFaceInternal> AffectedFaceBuffer;
|
---|
| 44 |
|
---|
| 45 | const int ConnectorTableSize = 2017;
|
---|
| 46 | ConnectorList[] ConnectorTable;
|
---|
| 47 |
|
---|
| 48 | #endregion
|
---|
| 49 |
|
---|
| 50 | /// <summary>
|
---|
| 51 | /// Initialize buffers and lists.
|
---|
| 52 | /// </summary>
|
---|
| 53 | void Initialize()
|
---|
| 54 | {
|
---|
| 55 | ConvexHull = new List<VertexWrap>();
|
---|
| 56 | UnprocessedFaces = new FaceList(); // new LinkedList<ConvexFaceInternal>();
|
---|
| 57 | ConvexFaces = new List<ConvexFaceInternal>();
|
---|
| 58 |
|
---|
| 59 | Center = new double[Dimension];
|
---|
| 60 | ntX = new double[Dimension];
|
---|
| 61 | ntY = new double[Dimension];
|
---|
| 62 | ntZ = new double[Dimension];
|
---|
| 63 | TraverseStack = new Stack<ConvexFaceInternal>();
|
---|
| 64 | UpdateBuffer = new ConvexFaceInternal[Dimension];
|
---|
| 65 | UpdateIndices = new int[Dimension];
|
---|
| 66 | RecycledFaceStack = new Stack<ConvexFaceInternal>();
|
---|
| 67 | ConnectorStack = new Stack<FaceConnector>();
|
---|
| 68 | EmptyBufferStack = new Stack<VertexBuffer>();
|
---|
| 69 | EmptyBuffer = new VertexBuffer();
|
---|
| 70 | AffectedFaceBuffer = new List<ConvexFaceInternal>();
|
---|
| 71 | BeyondBuffer = new VertexBuffer();
|
---|
| 72 |
|
---|
| 73 | ConnectorTable = Enumerable.Range(0, ConnectorTableSize).Select(_ => new ConnectorList()).ToArray();
|
---|
| 74 |
|
---|
| 75 | nDNormalSolveVector = new double[Dimension];
|
---|
| 76 | jaggedNDMatrix = new double[Dimension][];
|
---|
| 77 | for (var i = 0; i < Dimension; i++)
|
---|
| 78 | {
|
---|
| 79 | nDNormalSolveVector[i] = 1.0;
|
---|
| 80 | jaggedNDMatrix[i] = new double[Dimension];
|
---|
| 81 | }
|
---|
| 82 | nDMatrix = new double[Dimension, Dimension];
|
---|
| 83 | }
|
---|
| 84 |
|
---|
| 85 | /// <summary>
|
---|
| 86 | /// Check the dimensionality of the input data.
|
---|
| 87 | /// </summary>
|
---|
| 88 | int DetermineDimension()
|
---|
| 89 | {
|
---|
| 90 | var r = new Random();
|
---|
| 91 | var VCount = InputVertices.Count;
|
---|
| 92 | var dimensions = new List<int>();
|
---|
| 93 | for (var i = 0; i < 10; i++)
|
---|
| 94 | dimensions.Add(InputVertices[r.Next(VCount)].Vertex.Position.Length);
|
---|
| 95 | var dimension = dimensions.Min();
|
---|
| 96 | if (dimension != dimensions.Max()) throw new ArgumentException("Invalid input data (non-uniform dimension).");
|
---|
| 97 | return dimension;
|
---|
| 98 | }
|
---|
| 99 |
|
---|
| 100 | /// <summary>
|
---|
| 101 | /// Create the first faces from (dimension + 1) vertices.
|
---|
| 102 | /// </summary>
|
---|
| 103 | /// <returns></returns>
|
---|
| 104 | ConvexFaceInternal[] InitiateFaceDatabase()
|
---|
| 105 | {
|
---|
| 106 | var faces = new ConvexFaceInternal[Dimension + 1];
|
---|
| 107 |
|
---|
| 108 | for (var i = 0; i < Dimension + 1; i++)
|
---|
| 109 | {
|
---|
| 110 | var vertices = ConvexHull.Where((_, j) => i != j).ToArray(); // Skips the i-th vertex
|
---|
| 111 | var newFace = new ConvexFaceInternal(Dimension, new VertexBuffer());
|
---|
| 112 | newFace.Vertices = vertices;
|
---|
| 113 | Array.Sort(vertices, VertexWrapComparer.Instance);
|
---|
| 114 | CalculateFacePlane(newFace);
|
---|
| 115 | faces[i] = newFace;
|
---|
| 116 | }
|
---|
| 117 |
|
---|
| 118 | // update the adjacency (check all pairs of faces)
|
---|
| 119 | for (var i = 0; i < Dimension; i++)
|
---|
| 120 | {
|
---|
| 121 | for (var j = i + 1; j < Dimension + 1; j++) UpdateAdjacency(faces[i], faces[j]);
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | return faces;
|
---|
| 125 | }
|
---|
| 126 |
|
---|
| 127 | /// <summary>
|
---|
| 128 | /// Calculates the normal and offset of the hyper-plane given by the face's vertices.
|
---|
| 129 | /// </summary>
|
---|
| 130 | /// <param name="face"></param>
|
---|
| 131 | /// <returns></returns>
|
---|
| 132 | private void CalculateFacePlane(ConvexFaceInternal face)
|
---|
| 133 | {
|
---|
| 134 | var vertices = face.Vertices;
|
---|
| 135 | var normal = face.Normal;
|
---|
| 136 | FindNormalVector(vertices, normal);
|
---|
| 137 |
|
---|
| 138 | if (double.IsNaN(normal[0])) ThrowSingular();
|
---|
| 139 |
|
---|
| 140 | double offset = 0.0;
|
---|
| 141 | double centerDistance = 0.0;
|
---|
| 142 | var fi = vertices[0].PositionData;
|
---|
| 143 | for (int i = 0; i < Dimension; i++)
|
---|
| 144 | {
|
---|
| 145 | double n = normal[i];
|
---|
| 146 | offset += n * fi[i];
|
---|
| 147 | centerDistance += n * Center[i];
|
---|
| 148 | }
|
---|
| 149 | face.Offset = -offset;
|
---|
| 150 | centerDistance -= offset;
|
---|
| 151 |
|
---|
| 152 | if (centerDistance > 0)
|
---|
| 153 | {
|
---|
| 154 | for (int i = 0; i < Dimension; i++) normal[i] = -normal[i];
|
---|
| 155 | face.Offset = offset;
|
---|
| 156 | face.IsNormalFlipped = true;
|
---|
| 157 | }
|
---|
| 158 | else face.IsNormalFlipped = false;
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | /// <summary>
|
---|
| 162 | /// Check if the vertex is "visible" from the face.
|
---|
| 163 | /// The vertex is "over face" if the return value is >= 0.
|
---|
| 164 | /// </summary>
|
---|
| 165 | /// <param name="v"></param>
|
---|
| 166 | /// <param name="f"></param>
|
---|
| 167 | /// <returns>The vertex is "over face" if the result is positive.</returns>
|
---|
| 168 | double GetVertexDistance(VertexWrap v, ConvexFaceInternal f)
|
---|
| 169 | {
|
---|
| 170 | double[] normal = f.Normal;
|
---|
| 171 | double[] p = v.PositionData;
|
---|
| 172 | double distance = f.Offset;
|
---|
| 173 | for (int i = 0; i < Dimension; i++) distance += normal[i] * p[i];
|
---|
| 174 | return distance;
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | //unsafe double GetVertexDistance(VertexWrap v, ConvexFaceInternal f)
|
---|
| 178 | //{
|
---|
| 179 | // fixed (double* pNormal = f.Normal)
|
---|
| 180 | // fixed (double* pP = v.PositionData)
|
---|
| 181 | // {
|
---|
| 182 | // double* normal = pNormal;
|
---|
| 183 | // double* p = pP;
|
---|
| 184 |
|
---|
| 185 | // double distance = f.Offset;
|
---|
| 186 | // for (int i = 0; i < Dimension; i++)
|
---|
| 187 | // {
|
---|
| 188 | // distance += (*normal) * (*p);
|
---|
| 189 | // normal++;
|
---|
| 190 | // p++;
|
---|
| 191 | // }
|
---|
| 192 | // return distance;
|
---|
| 193 | // }
|
---|
| 194 | //}
|
---|
| 195 |
|
---|
| 196 | /// <summary>
|
---|
| 197 | /// Tags all faces seen from the current vertex with 1.
|
---|
| 198 | /// </summary>
|
---|
| 199 | /// <param name="currentFace"></param>
|
---|
| 200 | void TagAffectedFaces(ConvexFaceInternal currentFace)
|
---|
| 201 | {
|
---|
| 202 | AffectedFaceBuffer.Clear();
|
---|
| 203 | AffectedFaceBuffer.Add(currentFace);
|
---|
| 204 | TraverseAffectedFaces(currentFace);
|
---|
| 205 | }
|
---|
| 206 |
|
---|
| 207 | /// <summary>
|
---|
| 208 | /// Recursively traverse all the relevant faces.
|
---|
| 209 | /// </summary>
|
---|
| 210 | void TraverseAffectedFaces(ConvexFaceInternal currentFace)
|
---|
| 211 | {
|
---|
| 212 | TraverseStack.Clear();
|
---|
| 213 | TraverseStack.Push(currentFace);
|
---|
| 214 | currentFace.Tag = 1;
|
---|
| 215 |
|
---|
| 216 | while (TraverseStack.Count > 0)
|
---|
| 217 | {
|
---|
| 218 | var top = TraverseStack.Pop();
|
---|
| 219 | for (int i = 0; i < Dimension; i++)
|
---|
| 220 | {
|
---|
| 221 | var adjFace = top.AdjacentFaces[i];
|
---|
| 222 |
|
---|
| 223 | if (adjFace.Tag == 0 && GetVertexDistance(CurrentVertex, adjFace) >= 0)
|
---|
| 224 | {
|
---|
| 225 | AffectedFaceBuffer.Add(adjFace);
|
---|
| 226 | //TraverseAffectedFaces(adjFace);
|
---|
| 227 | adjFace.Tag = 1;
|
---|
| 228 | TraverseStack.Push(adjFace);
|
---|
| 229 | }
|
---|
| 230 | }
|
---|
| 231 | }
|
---|
| 232 |
|
---|
| 233 | ////for (int i = 0; i < Dimension; i++)
|
---|
| 234 | ////{
|
---|
| 235 | //// var adjFace = currentFace.AdjacentFaces[i];
|
---|
| 236 |
|
---|
| 237 | //// if (adjFace.Tag == 0 && GetVertexDistance(CurrentVertex, adjFace) >= 0)
|
---|
| 238 | //// {
|
---|
| 239 | //// AffectedFaceBuffer.Add(adjFace);
|
---|
| 240 | //// TraverseAffectedFaces(adjFace);
|
---|
| 241 | //// }
|
---|
| 242 | ////}
|
---|
| 243 | }
|
---|
| 244 |
|
---|
| 245 | /// <summary>
|
---|
| 246 | /// Check if 2 faces are adjacent and if so, update their AdjacentFaces array.
|
---|
| 247 | /// </summary>
|
---|
| 248 | /// <param name="l"></param>
|
---|
| 249 | /// <param name="r"></param>
|
---|
| 250 | void UpdateAdjacency(ConvexFaceInternal l, ConvexFaceInternal r)
|
---|
| 251 | {
|
---|
| 252 | var lv = l.Vertices;
|
---|
| 253 | var rv = r.Vertices;
|
---|
| 254 | int i;
|
---|
| 255 |
|
---|
| 256 | // reset marks on the 1st face
|
---|
| 257 | for (i = 0; i < Dimension; i++) lv[i].Marked = false;
|
---|
| 258 |
|
---|
| 259 | // mark all vertices on the 2nd face
|
---|
| 260 | for (i = 0; i < Dimension; i++) rv[i].Marked = true;
|
---|
| 261 |
|
---|
| 262 | // find the 1st false index
|
---|
| 263 | for (i = 0; i < Dimension; i++) if (!lv[i].Marked) break;
|
---|
| 264 |
|
---|
| 265 | // no vertex was marked
|
---|
| 266 | if (i == Dimension) return;
|
---|
| 267 |
|
---|
| 268 | // check if only 1 vertex wasn't marked
|
---|
| 269 | for (int j = i + 1; j < Dimension; j++) if (!lv[j].Marked) return;
|
---|
| 270 |
|
---|
| 271 | // if we are here, the two faces share an edge
|
---|
| 272 | l.AdjacentFaces[i] = r;
|
---|
| 273 |
|
---|
| 274 | // update the adj. face on the other face - find the vertex that remains marked
|
---|
| 275 | for (i = 0; i < Dimension; i++) lv[i].Marked = false;
|
---|
| 276 | for (i = 0; i < Dimension; i++)
|
---|
| 277 | {
|
---|
| 278 | if (rv[i].Marked) break;
|
---|
| 279 | }
|
---|
| 280 | r.AdjacentFaces[i] = l;
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | #region Memory stuff.
|
---|
| 284 |
|
---|
| 285 | /// <summary>
|
---|
| 286 | /// Recycle face for future use.
|
---|
| 287 | /// </summary>
|
---|
| 288 | void RecycleFace(ConvexFaceInternal face)
|
---|
| 289 | {
|
---|
| 290 | for (int i = 0; i < Dimension; i++)
|
---|
| 291 | {
|
---|
| 292 | face.AdjacentFaces[i] = null;
|
---|
| 293 | }
|
---|
| 294 | }
|
---|
| 295 |
|
---|
| 296 | /// <summary>
|
---|
| 297 | /// Get a fresh face.
|
---|
| 298 | /// </summary>
|
---|
| 299 | /// <returns></returns>
|
---|
| 300 | ConvexFaceInternal GetNewFace()
|
---|
| 301 | {
|
---|
| 302 | return RecycledFaceStack.Count != 0
|
---|
| 303 | ? RecycledFaceStack.Pop()
|
---|
| 304 | : new ConvexFaceInternal(Dimension, EmptyBufferStack.Count != 0 ? EmptyBufferStack.Pop() : new VertexBuffer());
|
---|
| 305 | }
|
---|
| 306 |
|
---|
| 307 | /// <summary>
|
---|
| 308 | /// Get a new connector.
|
---|
| 309 | /// </summary>
|
---|
| 310 | /// <returns></returns>
|
---|
| 311 | FaceConnector GetNewConnector()
|
---|
| 312 | {
|
---|
| 313 | return ConnectorStack.Count != 0
|
---|
| 314 | ? ConnectorStack.Pop()
|
---|
| 315 | : new FaceConnector(Dimension);
|
---|
| 316 | }
|
---|
| 317 | #endregion
|
---|
| 318 |
|
---|
| 319 | /// <summary>
|
---|
| 320 | /// Connect faces using a connector.
|
---|
| 321 | /// </summary>
|
---|
| 322 | /// <param name="connector"></param>
|
---|
| 323 | void ConnectFace(FaceConnector connector)
|
---|
| 324 | {
|
---|
| 325 | var index = connector.HashCode % ConnectorTableSize;
|
---|
| 326 | var list = ConnectorTable[index];
|
---|
| 327 |
|
---|
| 328 | int count = 0;
|
---|
| 329 | for (var current = list.First; current != null; current = current.Next)
|
---|
| 330 | {
|
---|
| 331 | if (FaceConnector.AreConnectable(connector, current, Dimension))
|
---|
| 332 | {
|
---|
| 333 | list.Remove(current);
|
---|
| 334 | FaceConnector.Connect(current, connector);
|
---|
| 335 | current.Face = null;
|
---|
| 336 | connector.Face = null;
|
---|
| 337 | ConnectorStack.Push(current);
|
---|
| 338 | ConnectorStack.Push(connector);
|
---|
| 339 | return;
|
---|
| 340 | }
|
---|
| 341 | }
|
---|
| 342 |
|
---|
| 343 | list.Add(connector);
|
---|
| 344 | }
|
---|
| 345 |
|
---|
| 346 | /// <summary>
|
---|
| 347 | /// Removes the faces "covered" by the current vertex and adds the newly created ones.
|
---|
| 348 | /// </summary>
|
---|
| 349 | private void CreateCone()
|
---|
| 350 | {
|
---|
| 351 | var oldFaces = AffectedFaceBuffer;
|
---|
| 352 |
|
---|
| 353 | var currentVertexIndex = CurrentVertex.Index;
|
---|
| 354 |
|
---|
| 355 | for (int fIndex = 0; fIndex < oldFaces.Count; fIndex++)
|
---|
| 356 | {
|
---|
| 357 | var oldFace = oldFaces[fIndex];
|
---|
| 358 |
|
---|
| 359 | // Find the faces that need to be updated
|
---|
| 360 | int updateCount = 0;
|
---|
| 361 | for (int i = 0; i < Dimension; i++)
|
---|
| 362 | {
|
---|
| 363 | var af = oldFace.AdjacentFaces[i];
|
---|
| 364 | if (af.Tag == 0) // Tag == 0 when oldFaces does not contain af
|
---|
| 365 | {
|
---|
| 366 | UpdateBuffer[updateCount] = af;
|
---|
| 367 | UpdateIndices[updateCount] = i;
|
---|
| 368 | ++updateCount;
|
---|
| 369 | }
|
---|
| 370 | }
|
---|
| 371 |
|
---|
| 372 | // Recycle the face for future use
|
---|
| 373 | if (updateCount == 0)
|
---|
| 374 | {
|
---|
| 375 | // If the face is present in the unprocessed list, remove it
|
---|
| 376 | UnprocessedFaces.Remove(oldFace);
|
---|
| 377 |
|
---|
| 378 | RecycleFace(oldFace);
|
---|
| 379 | RecycledFaceStack.Push(oldFace);
|
---|
| 380 | }
|
---|
| 381 |
|
---|
| 382 | for (int i = 0; i < updateCount; i++)
|
---|
| 383 | {
|
---|
| 384 | var adjacentFace = UpdateBuffer[i];
|
---|
| 385 |
|
---|
| 386 | int oldFaceAdjacentIndex = 0;
|
---|
| 387 | var adjFaceAdjacency = adjacentFace.AdjacentFaces;
|
---|
| 388 | for (int j = 0; j < Dimension; j++)
|
---|
| 389 | {
|
---|
| 390 | if (object.ReferenceEquals(oldFace, adjFaceAdjacency[j]))
|
---|
| 391 | {
|
---|
| 392 | oldFaceAdjacentIndex = j;
|
---|
| 393 | break;
|
---|
| 394 | }
|
---|
| 395 | }
|
---|
| 396 |
|
---|
| 397 | var forbidden = UpdateIndices[i]; // Index of the face that corresponds to this adjacent face
|
---|
| 398 |
|
---|
| 399 | ConvexFaceInternal newFace;
|
---|
| 400 |
|
---|
| 401 | int oldVertexIndex;
|
---|
| 402 | VertexWrap[] vertices;
|
---|
| 403 |
|
---|
| 404 | // Recycle the oldFace
|
---|
| 405 | if (i == updateCount - 1)
|
---|
| 406 | {
|
---|
| 407 | RecycleFace(oldFace);
|
---|
| 408 | newFace = oldFace;
|
---|
| 409 | vertices = newFace.Vertices;
|
---|
| 410 | oldVertexIndex = vertices[forbidden].Index;
|
---|
| 411 | }
|
---|
| 412 | else // Pop a face from the recycled stack or create a new one
|
---|
| 413 | {
|
---|
| 414 | newFace = GetNewFace();
|
---|
| 415 | vertices = newFace.Vertices;
|
---|
| 416 | for (int j = 0; j < Dimension; j++) vertices[j] = oldFace.Vertices[j];
|
---|
| 417 | oldVertexIndex = vertices[forbidden].Index;
|
---|
| 418 | }
|
---|
| 419 |
|
---|
| 420 | int orderedPivotIndex;
|
---|
| 421 |
|
---|
| 422 | // correct the ordering
|
---|
| 423 | if (currentVertexIndex < oldVertexIndex)
|
---|
| 424 | {
|
---|
| 425 | orderedPivotIndex = 0;
|
---|
| 426 | for (int j = forbidden - 1; j >= 0; j--)
|
---|
| 427 | {
|
---|
| 428 | if (vertices[j].Index > currentVertexIndex) vertices[j + 1] = vertices[j];
|
---|
| 429 | else
|
---|
| 430 | {
|
---|
| 431 | orderedPivotIndex = j + 1;
|
---|
| 432 | break;
|
---|
| 433 | }
|
---|
| 434 | }
|
---|
| 435 | }
|
---|
| 436 | else
|
---|
| 437 | {
|
---|
| 438 | orderedPivotIndex = Dimension - 1;
|
---|
| 439 | for (int j = forbidden + 1; j < Dimension; j++)
|
---|
| 440 | {
|
---|
| 441 | if (vertices[j].Index < currentVertexIndex) vertices[j - 1] = vertices[j];
|
---|
| 442 | else
|
---|
| 443 | {
|
---|
| 444 | orderedPivotIndex = j - 1;
|
---|
| 445 | break;
|
---|
| 446 | }
|
---|
| 447 | }
|
---|
| 448 | }
|
---|
| 449 |
|
---|
| 450 | vertices[orderedPivotIndex] = CurrentVertex;
|
---|
| 451 |
|
---|
| 452 | CalculateFacePlane(newFace);
|
---|
| 453 | newFace.AdjacentFaces[orderedPivotIndex] = adjacentFace;
|
---|
| 454 | adjacentFace.AdjacentFaces[oldFaceAdjacentIndex] = newFace;
|
---|
| 455 |
|
---|
| 456 | // let there be a connection.
|
---|
| 457 | for (int j = 0; j < Dimension; j++)
|
---|
| 458 | {
|
---|
| 459 | if (j == orderedPivotIndex) continue;
|
---|
| 460 | var connector = GetNewConnector();
|
---|
| 461 | connector.Update(newFace, j, Dimension);
|
---|
| 462 | ConnectFace(connector);
|
---|
| 463 | }
|
---|
| 464 |
|
---|
| 465 | // This could slightly help...
|
---|
| 466 | if (adjacentFace.VerticesBeyond.Count < oldFace.VerticesBeyond.Count)
|
---|
| 467 | {
|
---|
| 468 | FindBeyondVertices(newFace, adjacentFace.VerticesBeyond, oldFace.VerticesBeyond);
|
---|
| 469 | }
|
---|
| 470 | else
|
---|
| 471 | {
|
---|
| 472 | FindBeyondVertices(newFace, oldFace.VerticesBeyond, adjacentFace.VerticesBeyond);
|
---|
| 473 | }
|
---|
| 474 |
|
---|
| 475 | // This face will definitely lie on the hull
|
---|
| 476 | if (newFace.VerticesBeyond.Count == 0)
|
---|
| 477 | {
|
---|
| 478 | ConvexFaces.Add(newFace);
|
---|
| 479 | UnprocessedFaces.Remove(newFace);
|
---|
| 480 | EmptyBufferStack.Push(newFace.VerticesBeyond);
|
---|
| 481 | newFace.VerticesBeyond = EmptyBuffer;
|
---|
| 482 | }
|
---|
| 483 | else // Add the face to the list
|
---|
| 484 | {
|
---|
| 485 | UnprocessedFaces.Add(newFace);
|
---|
| 486 | }
|
---|
| 487 | }
|
---|
| 488 | }
|
---|
| 489 | }
|
---|
| 490 |
|
---|
| 491 | /// <summary>
|
---|
| 492 | /// Subtracts vectors x and y and stores the result to target.
|
---|
| 493 | /// </summary>
|
---|
| 494 | /// <param name="x"></param>
|
---|
| 495 | /// <param name="y"></param>
|
---|
| 496 | /// <param name="target"></param>
|
---|
| 497 | void SubtractFast(double[] x, double[] y, double[] target)
|
---|
| 498 | {
|
---|
| 499 | for (int i = 0; i < Dimension; i++)
|
---|
| 500 | {
|
---|
| 501 | target[i] = x[i] - y[i];
|
---|
| 502 | }
|
---|
| 503 | }
|
---|
| 504 |
|
---|
| 505 | /// <summary>
|
---|
| 506 | /// Finds 4D normal vector.
|
---|
| 507 | /// </summary>
|
---|
| 508 | /// <param name="vertices"></param>
|
---|
| 509 | /// <param name="normal"></param>
|
---|
| 510 | void FindNormalVector4D(VertexWrap[] vertices, double[] normal)
|
---|
| 511 | {
|
---|
| 512 | SubtractFast(vertices[1].PositionData, vertices[0].PositionData, ntX);
|
---|
| 513 | SubtractFast(vertices[2].PositionData, vertices[1].PositionData, ntY);
|
---|
| 514 | SubtractFast(vertices[3].PositionData, vertices[2].PositionData, ntZ);
|
---|
| 515 |
|
---|
| 516 | var x = ntX;
|
---|
| 517 | var y = ntY;
|
---|
| 518 | var z = ntZ;
|
---|
| 519 |
|
---|
| 520 | // This was generated using Mathematica
|
---|
| 521 | var nx = x[3] * (y[2] * z[1] - y[1] * z[2])
|
---|
| 522 | + x[2] * (y[1] * z[3] - y[3] * z[1])
|
---|
| 523 | + x[1] * (y[3] * z[2] - y[2] * z[3]);
|
---|
| 524 | var ny = x[3] * (y[0] * z[2] - y[2] * z[0])
|
---|
| 525 | + x[2] * (y[3] * z[0] - y[0] * z[3])
|
---|
| 526 | + x[0] * (y[2] * z[3] - y[3] * z[2]);
|
---|
| 527 | var nz = x[3] * (y[1] * z[0] - y[0] * z[1])
|
---|
| 528 | + x[1] * (y[0] * z[3] - y[3] * z[0])
|
---|
| 529 | + x[0] * (y[3] * z[1] - y[1] * z[3]);
|
---|
| 530 | var nw = x[2] * (y[0] * z[1] - y[1] * z[0])
|
---|
| 531 | + x[1] * (y[2] * z[0] - y[0] * z[2])
|
---|
| 532 | + x[0] * (y[1] * z[2] - y[2] * z[1]);
|
---|
| 533 |
|
---|
| 534 | double norm = System.Math.Sqrt(nx * nx + ny * ny + nz * nz + nw * nw);
|
---|
| 535 |
|
---|
| 536 | double f = 1.0 / norm;
|
---|
| 537 | normal[0] = f * nx;
|
---|
| 538 | normal[1] = f * ny;
|
---|
| 539 | normal[2] = f * nz;
|
---|
| 540 | normal[3] = f * nw;
|
---|
| 541 | }
|
---|
| 542 |
|
---|
| 543 | /// <summary>
|
---|
| 544 | /// Finds 3D normal vector.
|
---|
| 545 | /// </summary>
|
---|
| 546 | /// <param name="vertices"></param>
|
---|
| 547 | /// <param name="normal"></param>
|
---|
| 548 | void FindNormalVector3D(VertexWrap[] vertices, double[] normal)
|
---|
| 549 | {
|
---|
| 550 | SubtractFast(vertices[1].PositionData, vertices[0].PositionData, ntX);
|
---|
| 551 | SubtractFast(vertices[2].PositionData, vertices[1].PositionData, ntY);
|
---|
| 552 |
|
---|
| 553 | var x = ntX;
|
---|
| 554 | var y = ntY;
|
---|
| 555 |
|
---|
| 556 | var nx = x[1] * y[2] - x[2] * y[1];
|
---|
| 557 | var ny = x[2] * y[0] - x[0] * y[2];
|
---|
| 558 | var nz = x[0] * y[1] - x[1] * y[0];
|
---|
| 559 |
|
---|
| 560 | double norm = System.Math.Sqrt(nx * nx + ny * ny + nz * nz);
|
---|
| 561 |
|
---|
| 562 | double f = 1.0 / norm;
|
---|
| 563 | normal[0] = f * nx;
|
---|
| 564 | normal[1] = f * ny;
|
---|
| 565 | normal[2] = f * nz;
|
---|
| 566 | }
|
---|
| 567 |
|
---|
| 568 | /// <summary>
|
---|
| 569 | /// Finds 2D normal vector.
|
---|
| 570 | /// </summary>
|
---|
| 571 | /// <param name="vertices"></param>
|
---|
| 572 | /// <param name="normal"></param>
|
---|
| 573 | void FindNormalVector2D(VertexWrap[] vertices, double[] normal)
|
---|
| 574 | {
|
---|
| 575 | SubtractFast(vertices[1].PositionData, vertices[0].PositionData, ntX);
|
---|
| 576 |
|
---|
| 577 | var x = ntX;
|
---|
| 578 |
|
---|
| 579 | var nx = -x[1];
|
---|
| 580 | var ny = x[0];
|
---|
| 581 |
|
---|
| 582 | double norm = System.Math.Sqrt(nx * nx + ny * ny);
|
---|
| 583 |
|
---|
| 584 | double f = 1.0 / norm;
|
---|
| 585 | normal[0] = f * nx;
|
---|
| 586 | normal[1] = f * ny;
|
---|
| 587 | }
|
---|
| 588 |
|
---|
| 589 | /// <summary>
|
---|
| 590 | /// Finds normal vector of a hyper-plane given by vertices.
|
---|
| 591 | /// Stores the results to normalData.
|
---|
| 592 | /// </summary>
|
---|
| 593 | /// <param name="vertices"></param>
|
---|
| 594 | /// <param name="normalData"></param>
|
---|
| 595 | private void FindNormalVector(VertexWrap[] vertices, double[] normalData)
|
---|
| 596 | {
|
---|
| 597 | switch (Dimension)
|
---|
| 598 | {
|
---|
| 599 | case 2: FindNormalVector2D(vertices, normalData); break;
|
---|
| 600 | case 3: FindNormalVector3D(vertices, normalData); break;
|
---|
| 601 | case 4: FindNormalVector4D(vertices, normalData); break;
|
---|
| 602 | default:
|
---|
| 603 | {
|
---|
| 604 | for (var i = 0; i < Dimension; i++) nDNormalSolveVector[i] = 1.0;
|
---|
| 605 | for (var i = 0; i < Dimension; i++)
|
---|
| 606 | {
|
---|
| 607 | var row = jaggedNDMatrix[i];
|
---|
| 608 | var pos = vertices[i].Vertex.Position;
|
---|
| 609 | for (int j = 0; j < Dimension; j++) row[j] = pos[j];
|
---|
| 610 | }
|
---|
| 611 | StarMath.gaussElimination(Dimension, jaggedNDMatrix, nDNormalSolveVector, normalData);
|
---|
| 612 | StarMath.normalizeInPlace(normalData, Dimension);
|
---|
| 613 | break;
|
---|
| 614 | }
|
---|
| 615 | }
|
---|
| 616 | }
|
---|
| 617 |
|
---|
| 618 | /// <summary>
|
---|
| 619 | /// Check whether the vertex v is beyond the given face. If so, add it to beyondVertices.
|
---|
| 620 | /// </summary>
|
---|
| 621 | /// <param name="face"></param>
|
---|
| 622 | /// <param name="beyondVertices"></param>
|
---|
| 623 | /// <param name="v"></param>
|
---|
| 624 | void IsBeyond(ConvexFaceInternal face, VertexBuffer beyondVertices, VertexWrap v)
|
---|
| 625 | {
|
---|
| 626 | double distance = GetVertexDistance(v, face);
|
---|
| 627 | if (distance >= 0)
|
---|
| 628 | {
|
---|
| 629 | if (distance > MaxDistance)
|
---|
| 630 | {
|
---|
| 631 | MaxDistance = distance;
|
---|
| 632 | FurthestVertex = v;
|
---|
| 633 | }
|
---|
| 634 | beyondVertices.Add(v);
|
---|
| 635 | }
|
---|
| 636 | }
|
---|
| 637 |
|
---|
| 638 | /// <summary>
|
---|
| 639 | /// Used in the "initialization" code.
|
---|
| 640 | /// </summary>
|
---|
| 641 | void FindBeyondVertices(ConvexFaceInternal face)
|
---|
| 642 | {
|
---|
| 643 | var beyondVertices = face.VerticesBeyond;
|
---|
| 644 |
|
---|
| 645 | MaxDistance = double.NegativeInfinity;
|
---|
| 646 | FurthestVertex = null;
|
---|
| 647 |
|
---|
| 648 | int count = InputVertices.Count;
|
---|
| 649 | for (int i = 0; i < count; i++) IsBeyond(face, beyondVertices, InputVertices[i]);
|
---|
| 650 |
|
---|
| 651 | face.FurthestVertex = FurthestVertex;
|
---|
| 652 | //face.FurthestDistance = MaxDistance;
|
---|
| 653 | }
|
---|
| 654 |
|
---|
| 655 | /// <summary>
|
---|
| 656 | /// Used by update faces.
|
---|
| 657 | /// </summary>
|
---|
| 658 | void FindBeyondVertices(ConvexFaceInternal face, VertexBuffer beyond, VertexBuffer beyond1)
|
---|
| 659 | {
|
---|
| 660 | var beyondVertices = BeyondBuffer;
|
---|
| 661 |
|
---|
| 662 | MaxDistance = double.NegativeInfinity;
|
---|
| 663 | FurthestVertex = null;
|
---|
| 664 | VertexWrap v;
|
---|
| 665 |
|
---|
| 666 | int count = beyond1.Count;
|
---|
| 667 | for (int i = 0; i < count; i++) beyond1[i].Marked = true;
|
---|
| 668 | CurrentVertex.Marked = false;
|
---|
| 669 | count = beyond.Count;
|
---|
| 670 | for (int i = 0; i < count; i++)
|
---|
| 671 | {
|
---|
| 672 | v = beyond[i];
|
---|
| 673 | if (object.ReferenceEquals(v, CurrentVertex)) continue;
|
---|
| 674 | v.Marked = false;
|
---|
| 675 | IsBeyond(face, beyondVertices, v);
|
---|
| 676 | }
|
---|
| 677 |
|
---|
| 678 | count = beyond1.Count;
|
---|
| 679 | for (int i = 0; i < count; i++)
|
---|
| 680 | {
|
---|
| 681 | v = beyond1[i];
|
---|
| 682 | if (v.Marked) IsBeyond(face, beyondVertices, v);
|
---|
| 683 | }
|
---|
| 684 |
|
---|
| 685 | face.FurthestVertex = FurthestVertex;
|
---|
| 686 | //face.FurthestDistance = MaxDistance;
|
---|
| 687 |
|
---|
| 688 | // Pull the old switch a roo
|
---|
| 689 | var temp = face.VerticesBeyond;
|
---|
| 690 | face.VerticesBeyond = beyondVertices;
|
---|
| 691 | if (temp.Count > 0) temp.Clear();
|
---|
| 692 | BeyondBuffer = temp;
|
---|
| 693 | }
|
---|
| 694 |
|
---|
| 695 | /// <summary>
|
---|
| 696 | /// Recalculates the centroid of the current hull.
|
---|
| 697 | /// </summary>
|
---|
| 698 | void UpdateCenter()
|
---|
| 699 | {
|
---|
| 700 | for (int i = 0; i < Dimension; i++) Center[i] *= (ConvexHull.Count - 1);
|
---|
| 701 | double f = 1.0 / ConvexHull.Count;
|
---|
| 702 | for (int i = 0; i < Dimension; i++) Center[i] = f * (Center[i] + CurrentVertex.PositionData[i]);
|
---|
| 703 | }
|
---|
| 704 |
|
---|
| 705 | /// <summary>
|
---|
| 706 | /// Find the (dimension+1) initial points and create the simplexes.
|
---|
| 707 | /// </summary>
|
---|
| 708 | void InitConvexHull()
|
---|
| 709 | {
|
---|
| 710 | var extremes = FindExtremes();
|
---|
| 711 | var initialPoints = FindInitialPoints(extremes);
|
---|
| 712 |
|
---|
| 713 | // Add the initial points to the convex hull.
|
---|
| 714 | foreach (var vertex in initialPoints)
|
---|
| 715 | {
|
---|
| 716 | CurrentVertex = vertex;
|
---|
| 717 | ConvexHull.Add(CurrentVertex);
|
---|
| 718 | UpdateCenter();
|
---|
| 719 | InputVertices.Remove(vertex);
|
---|
| 720 |
|
---|
| 721 | // Because of the AklTou heuristic.
|
---|
| 722 | extremes.Remove(vertex);
|
---|
| 723 | }
|
---|
| 724 |
|
---|
| 725 | // Create the initial simplexes.
|
---|
| 726 | var faces = InitiateFaceDatabase();
|
---|
| 727 |
|
---|
| 728 | // Init the vertex beyond buffers.
|
---|
| 729 | foreach (var face in faces)
|
---|
| 730 | {
|
---|
| 731 | FindBeyondVertices(face);
|
---|
| 732 | if (face.VerticesBeyond.Count == 0) ConvexFaces.Add(face); // The face is on the hull
|
---|
| 733 | else UnprocessedFaces.Add(face);
|
---|
| 734 | }
|
---|
| 735 | }
|
---|
| 736 |
|
---|
| 737 | /// <summary>
|
---|
| 738 | /// Finds (dimension + 1) initial points.
|
---|
| 739 | /// </summary>
|
---|
| 740 | /// <param name="extremes"></param>
|
---|
| 741 | /// <returns></returns>
|
---|
| 742 | private List<VertexWrap> FindInitialPoints(List<VertexWrap> extremes)
|
---|
| 743 | {
|
---|
| 744 | List<VertexWrap> initialPoints = new List<VertexWrap>();// { extremes[0], extremes[1] };
|
---|
| 745 |
|
---|
| 746 | VertexWrap first = null, second = null;
|
---|
| 747 | double maxDist = 0;
|
---|
| 748 | for (int i = 0; i < extremes.Count - 1; i++)
|
---|
| 749 | {
|
---|
| 750 | var a = extremes[i];
|
---|
| 751 | for (int j = i + 1; j < extremes.Count; j++)
|
---|
| 752 | {
|
---|
| 753 | var b = extremes[j];
|
---|
| 754 | var dist = StarMath.norm2(StarMath.subtract(a.PositionData, b.PositionData, Dimension), Dimension, true);
|
---|
| 755 | if (dist > maxDist)
|
---|
| 756 | {
|
---|
| 757 | first = a;
|
---|
| 758 | second = b;
|
---|
| 759 | maxDist = dist;
|
---|
| 760 | }
|
---|
| 761 | }
|
---|
| 762 | }
|
---|
| 763 |
|
---|
| 764 | initialPoints.Add(first);
|
---|
| 765 | initialPoints.Add(second);
|
---|
| 766 |
|
---|
| 767 | for (int i = 2; i <= Dimension; i++)
|
---|
| 768 | {
|
---|
| 769 | double maximum = 0.0001;
|
---|
| 770 | VertexWrap maxPoint = null;
|
---|
| 771 | for (int j = 0; j < extremes.Count; j++)
|
---|
| 772 | {
|
---|
| 773 | var extreme = extremes[j];
|
---|
| 774 | if (initialPoints.Contains(extreme)) continue;
|
---|
| 775 |
|
---|
| 776 | var val = GetSimplexVolume(extreme, initialPoints);
|
---|
| 777 |
|
---|
| 778 | if (val > maximum)
|
---|
| 779 | {
|
---|
| 780 | maximum = val;
|
---|
| 781 | maxPoint = extreme;
|
---|
| 782 | }
|
---|
| 783 | }
|
---|
| 784 | if (maxPoint != null) initialPoints.Add(maxPoint);
|
---|
| 785 | else
|
---|
| 786 | {
|
---|
| 787 | int vCount = InputVertices.Count;
|
---|
| 788 | for (int j = 0; j < vCount; j++)
|
---|
| 789 | {
|
---|
| 790 | var point = InputVertices[j];
|
---|
| 791 | if (initialPoints.Contains(point)) continue;
|
---|
| 792 |
|
---|
| 793 | var val = GetSimplexVolume(point, initialPoints);
|
---|
| 794 |
|
---|
| 795 | if (val > maximum)
|
---|
| 796 | {
|
---|
| 797 | maximum = val;
|
---|
| 798 | maxPoint = point;
|
---|
| 799 | }
|
---|
| 800 | }
|
---|
| 801 |
|
---|
| 802 | if (maxPoint != null) initialPoints.Add(maxPoint);
|
---|
| 803 | else ThrowSingular();
|
---|
| 804 | }
|
---|
| 805 | }
|
---|
| 806 | return initialPoints;
|
---|
| 807 | }
|
---|
| 808 |
|
---|
| 809 | /// <summary>
|
---|
| 810 | /// Computes the volume of the (n=initialPoints.Count)D simplex defined by the
|
---|
| 811 | /// pivot and initialPoints.
|
---|
| 812 | /// This is computed as the determinant of the matrix | initialPoints[i] - pivot |
|
---|
| 813 | /// </summary>
|
---|
| 814 | /// <param name="pivot"></param>
|
---|
| 815 | /// <param name="initialPoints"></param>
|
---|
| 816 | /// <returns></returns>
|
---|
| 817 | double GetSimplexVolume(VertexWrap pivot, List<VertexWrap> initialPoints)
|
---|
| 818 | {
|
---|
| 819 | var dim = initialPoints.Count;
|
---|
| 820 | var m = nDMatrix;
|
---|
| 821 |
|
---|
| 822 | for (int i = 0; i < dim; i++)
|
---|
| 823 | {
|
---|
| 824 | var pts = initialPoints[i];
|
---|
| 825 | for (int j = 0; j < dim; j++) m[i, j] = pts.PositionData[j] - pivot.PositionData[j];
|
---|
| 826 | }
|
---|
| 827 |
|
---|
| 828 | return Math.Abs(StarMath.determinantDestructive(m, dim));
|
---|
| 829 | }
|
---|
| 830 |
|
---|
| 831 | /// <summary>
|
---|
| 832 | /// Finds the extremes in all dimensions.
|
---|
| 833 | /// </summary>
|
---|
| 834 | /// <returns></returns>
|
---|
| 835 | private List<VertexWrap> FindExtremes()
|
---|
| 836 | {
|
---|
| 837 | var extremes = new List<VertexWrap>(2 * Dimension);
|
---|
| 838 |
|
---|
| 839 | int vCount = InputVertices.Count;
|
---|
| 840 | for (int i = 0; i < Dimension; i++)
|
---|
| 841 | {
|
---|
| 842 | double min = double.MaxValue, max = double.MinValue;
|
---|
| 843 | int minInd = 0, maxInd = 0;
|
---|
| 844 | for (int j = 0; j < vCount; j++)
|
---|
| 845 | {
|
---|
| 846 | var v = InputVertices[j].PositionData[i];
|
---|
| 847 | if (v < min)
|
---|
| 848 | {
|
---|
| 849 | min = v;
|
---|
| 850 | minInd = j;
|
---|
| 851 | }
|
---|
| 852 | if (v > max)
|
---|
| 853 | {
|
---|
| 854 | max = v;
|
---|
| 855 | maxInd = j;
|
---|
| 856 | }
|
---|
| 857 | }
|
---|
| 858 |
|
---|
| 859 | if (minInd != maxInd)
|
---|
| 860 | {
|
---|
| 861 | extremes.Add(InputVertices[minInd]);
|
---|
| 862 | extremes.Add(InputVertices[maxInd]);
|
---|
| 863 | }
|
---|
| 864 | else extremes.Add(InputVertices[minInd]);
|
---|
| 865 | }
|
---|
| 866 | return extremes;
|
---|
| 867 | }
|
---|
| 868 |
|
---|
| 869 | /// <summary>
|
---|
| 870 | /// The exception thrown if singular input data detected.
|
---|
| 871 | /// </summary>
|
---|
| 872 | void ThrowSingular()
|
---|
| 873 | {
|
---|
| 874 | throw new InvalidOperationException(
|
---|
| 875 | "ConvexHull: Singular input data (i.e. trying to triangulate a data that contain a regular lattice of points).\n"
|
---|
| 876 | + "Introducing some noise to the data might resolve the issue.");
|
---|
| 877 | }
|
---|
| 878 |
|
---|
| 879 | /// <summary>
|
---|
| 880 | /// Fins the convex hull.
|
---|
| 881 | /// </summary>
|
---|
| 882 | void FindConvexHull()
|
---|
| 883 | {
|
---|
| 884 | // Find the (dimension+1) initial points and create the simplexes.
|
---|
| 885 | InitConvexHull();
|
---|
| 886 |
|
---|
| 887 | // Expand the convex hull and faces.
|
---|
| 888 | while (UnprocessedFaces.First != null)
|
---|
| 889 | {
|
---|
| 890 | var currentFace = UnprocessedFaces.First;
|
---|
| 891 | CurrentVertex = currentFace.FurthestVertex;
|
---|
| 892 | ConvexHull.Add(CurrentVertex);
|
---|
| 893 | UpdateCenter();
|
---|
| 894 |
|
---|
| 895 | // The affected faces get tagged
|
---|
| 896 | TagAffectedFaces(currentFace);
|
---|
| 897 |
|
---|
| 898 | // Create the cone from the currentVertex and the affected faces horizon.
|
---|
| 899 | CreateCone();
|
---|
| 900 |
|
---|
| 901 | // Need to reset the tags
|
---|
| 902 | int count = AffectedFaceBuffer.Count;
|
---|
| 903 | for (int i = 0; i < count; i++) AffectedFaceBuffer[i].Tag = 0;
|
---|
| 904 | }
|
---|
| 905 | }
|
---|
| 906 |
|
---|
| 907 | /// <summary>
|
---|
| 908 | /// Wraps the vertices and determines the dimension if it's unknown.
|
---|
| 909 | /// </summary>
|
---|
| 910 | /// <param name="vertices"></param>
|
---|
| 911 | /// <param name="dim"></param>
|
---|
| 912 | private ConvexHullInternal(IEnumerable<IVertex> vertices)
|
---|
| 913 | {
|
---|
| 914 | InputVertices = new List<VertexWrap>(vertices.Select((v, i) => new VertexWrap { Vertex = v, PositionData = v.Position, Index = i }));
|
---|
| 915 | Dimension = DetermineDimension();
|
---|
| 916 | Initialize();
|
---|
| 917 | }
|
---|
| 918 |
|
---|
| 919 | /// <summary>
|
---|
| 920 | /// Finds the vertices on the convex hull and optionally converts them to the TVertex array.
|
---|
| 921 | /// </summary>
|
---|
| 922 | /// <typeparam name="TVertex"></typeparam>
|
---|
| 923 | /// <param name="onlyCompute"></param>
|
---|
| 924 | /// <returns></returns>
|
---|
| 925 | private IEnumerable<TVertex> GetConvexHullInternal<TVertex>(bool onlyCompute = false) where TVertex : IVertex
|
---|
| 926 | {
|
---|
| 927 | if (Computed) return onlyCompute ? null : ConvexHull.Select(v => (TVertex)v.Vertex).ToArray();
|
---|
| 928 |
|
---|
| 929 | if (Dimension < 2) throw new ArgumentException("Dimension of the input must be 2 or greater.");
|
---|
| 930 |
|
---|
| 931 | FindConvexHull();
|
---|
| 932 | Computed = true;
|
---|
| 933 | return onlyCompute ? null : ConvexHull.Select(v => (TVertex)v.Vertex).ToArray();
|
---|
| 934 | }
|
---|
| 935 |
|
---|
| 936 | /// <summary>
|
---|
| 937 | /// Finds the convex hull and creates the TFace objects.
|
---|
| 938 | /// </summary>
|
---|
| 939 | /// <typeparam name="TVertex"></typeparam>
|
---|
| 940 | /// <typeparam name="TFace"></typeparam>
|
---|
| 941 | /// <returns></returns>
|
---|
| 942 | private IEnumerable<TFace> GetConvexFacesInternal<TVertex, TFace>()
|
---|
| 943 | where TFace : ConvexFace<TVertex, TFace>, new()
|
---|
| 944 | where TVertex : IVertex
|
---|
| 945 | {
|
---|
| 946 | if (!Computed) GetConvexHullInternal<TVertex>(true);
|
---|
| 947 |
|
---|
| 948 | var faces = ConvexFaces;
|
---|
| 949 | int cellCount = faces.Count;
|
---|
| 950 | var cells = new TFace[cellCount];
|
---|
| 951 |
|
---|
| 952 | for (int i = 0; i < cellCount; i++)
|
---|
| 953 | {
|
---|
| 954 | var face = faces[i];
|
---|
| 955 | var vertices = new TVertex[Dimension];
|
---|
| 956 | for (int j = 0; j < Dimension; j++) vertices[j] = (TVertex)face.Vertices[j].Vertex;
|
---|
| 957 | cells[i] = new TFace
|
---|
| 958 | {
|
---|
| 959 | Vertices = vertices,
|
---|
| 960 | Adjacency = new TFace[Dimension],
|
---|
| 961 | Normal = face.Normal
|
---|
| 962 | };
|
---|
| 963 | face.Tag = i;
|
---|
| 964 | }
|
---|
| 965 |
|
---|
| 966 | for (int i = 0; i < cellCount; i++)
|
---|
| 967 | {
|
---|
| 968 | var face = faces[i];
|
---|
| 969 | var cell = cells[i];
|
---|
| 970 | for (int j = 0; j < Dimension; j++)
|
---|
| 971 | {
|
---|
| 972 | if (face.AdjacentFaces[j] == null) continue;
|
---|
| 973 | cell.Adjacency[j] = cells[face.AdjacentFaces[j].Tag];
|
---|
| 974 | }
|
---|
| 975 |
|
---|
| 976 | // Fix the vertex orientation.
|
---|
| 977 | if (face.IsNormalFlipped)
|
---|
| 978 | {
|
---|
| 979 | var tempVert = cell.Vertices[0];
|
---|
| 980 | cell.Vertices[0] = cell.Vertices[Dimension - 1];
|
---|
| 981 | cell.Vertices[Dimension - 1] = tempVert;
|
---|
| 982 |
|
---|
| 983 | var tempAdj = cell.Adjacency[0];
|
---|
| 984 | cell.Adjacency[0] = cell.Adjacency[Dimension - 1];
|
---|
| 985 | cell.Adjacency[Dimension - 1] = tempAdj;
|
---|
| 986 | }
|
---|
| 987 | }
|
---|
| 988 |
|
---|
| 989 | return cells;
|
---|
| 990 | }
|
---|
| 991 |
|
---|
| 992 | /// <summary>
|
---|
| 993 | /// This is used by the Delaunay triangulation code.
|
---|
| 994 | /// </summary>
|
---|
| 995 | /// <typeparam name="TVertex"></typeparam>
|
---|
| 996 | /// <typeparam name="TFace"></typeparam>
|
---|
| 997 | /// <param name="data"></param>
|
---|
| 998 | /// <returns></returns>
|
---|
| 999 | internal static List<ConvexFaceInternal> GetConvexFacesInternal<TVertex, TFace>(IEnumerable<TVertex> data)
|
---|
| 1000 | where TFace : ConvexFace<TVertex, TFace>, new()
|
---|
| 1001 | where TVertex : IVertex
|
---|
| 1002 | {
|
---|
| 1003 | ConvexHullInternal ch = new ConvexHullInternal(data.Cast<IVertex>());
|
---|
| 1004 | ch.GetConvexHullInternal<TVertex>(true);
|
---|
| 1005 | return ch.ConvexFaces;
|
---|
| 1006 | }
|
---|
| 1007 |
|
---|
| 1008 | /// <summary>
|
---|
| 1009 | /// This is called by the "ConvexHull" class.
|
---|
| 1010 | /// </summary>
|
---|
| 1011 | /// <typeparam name="TVertex"></typeparam>
|
---|
| 1012 | /// <typeparam name="TFace"></typeparam>
|
---|
| 1013 | /// <param name="data"></param>
|
---|
| 1014 | /// <returns></returns>
|
---|
| 1015 | internal static Tuple<IEnumerable<TVertex>, IEnumerable<TFace>> GetConvexHullAndFaces<TVertex, TFace>(IEnumerable<IVertex> data)
|
---|
| 1016 | where TFace : ConvexFace<TVertex, TFace>, new()
|
---|
| 1017 | where TVertex : IVertex
|
---|
| 1018 | {
|
---|
| 1019 | ConvexHullInternal ch = new ConvexHullInternal(data);
|
---|
| 1020 | return Tuple.Create(
|
---|
| 1021 | ch.GetConvexHullInternal<TVertex>(),
|
---|
| 1022 | ch.GetConvexFacesInternal<TVertex, TFace>());
|
---|
| 1023 | }
|
---|
| 1024 | }
|
---|
| 1025 | }
|
---|