Пример #1
0
 /// <summary>
 /// Shows the specified tessellated solid in a Helix toolkit window.
 /// </summary>
 /// <param name="tessellatedSolid">The tessellated solid.</param>
 public static void Show(TessellatedSolid tessellatedSolid)
 {
     var window = new MainWindow();
     window.view1.Children.Add(MakeModelVisual3D(tessellatedSolid));
     window.view1.ZoomExtentsWhenLoaded = true;
     window.ShowDialog();
 }
Пример #2
0
 /// <summary>
 /// When the tessellated solid is sliced at the specified plane, the contact surfaces are
 /// described by the return ContactData object. This is a non-destructive function typically
 /// used to find the shape and size of 2D surface on the prescribed plane..
 /// </summary>
 /// <param name="plane">The plane.</param>
 /// <param name="ts">The ts.</param>
 /// <returns>ContactData.</returns>
 /// <exception cref="System.Exception">Contact Edges found that are not contained in loop.</exception>
 public static ContactData DefineContact(Flat plane, TessellatedSolid ts)
 {
     var vertexDistancesToPlane = new double[ts.NumberOfVertices];
     for (int i = 0; i < ts.NumberOfVertices; i++)
         vertexDistancesToPlane[i] = ts.Vertices[i].Position.dotProduct(plane.Normal) - plane.DistanceToOrigin;
     // the edges serve as the easiest way to identify where the solid is interacting with the plane.
     // Instead of a foreach, the while loop lets us look ahead to known edges that are irrelevant.
     var edgeHashSet = new HashSet<Edge>(ts.Edges);
     // Contact elements are constructed and then later arranged into loops. Loops make up the returned object, ContactData.
     var straddleContactElts = new List<ContactElement>();
     var inPlaneContactElts = new List<CoincidentEdgeContactElement>();
     while (edgeHashSet.Any())
     {
         // instead of the foreach, we have this while statement and these first 2 lines to enumerate over the edges.
         var edge = edgeHashSet.First();
         edgeHashSet.Remove(edge);
         var toDistance = vertexDistancesToPlane[edge.To.IndexInList];
         var fromDistance = vertexDistancesToPlane[edge.From.IndexInList];
         if (StarMath.IsNegligible(toDistance) && StarMath.IsNegligible(fromDistance))
             ContactElement.MakeInPlaneContactElement(plane, edge, edgeHashSet, vertexDistancesToPlane,
                 inPlaneContactElts);
         else if ((toDistance > 0 && fromDistance < 0)
                  || (toDistance < 0 && fromDistance > 0))
             straddleContactElts.Add(new ThroughFaceContactElement(plane, edge, toDistance));
     }
     foreach (var contactElement in inPlaneContactElts)
     {
         // next, we find any additional vertices that just touch the plane but don't have in-plane edges
         // to facilitate this we negate all vertices already captures in the inPlaneContactElts
         vertexDistancesToPlane[contactElement.StartVertex.IndexInList] = double.NaN;
         vertexDistancesToPlane[contactElement.EndVertex.IndexInList] = double.NaN;
     }
     for (int i = 0; i < ts.NumberOfVertices; i++)
     {
         if (!StarMath.IsNegligible(vertexDistancesToPlane[i])) continue;
         var v = ts.Vertices[i];
         PolygonalFace negativeFace, positiveFace;
         if (ThroughVertexContactElement.FindNegativeAndPositiveFaces(plane, v, vertexDistancesToPlane,
             out negativeFace, out positiveFace))
             straddleContactElts.Add(new ThroughVertexContactElement(v, negativeFace, positiveFace));
     }
     straddleContactElts.AddRange(inPlaneContactElts);
     var loops = new List<Loop>();
     var numberOfTries = 0;
     while (straddleContactElts.Any() && numberOfTries < straddleContactElts.Count)
     {
         // now build loops from stringing together contact elements
         var loop = FindLoop(plane, straddleContactElts, vertexDistancesToPlane);
         if (loop != null)
         {
             Debug.WriteLine(loops.Count + ": " + loop.MakeDebugContactString() + "  ");
             loops.Add(loop);
             numberOfTries = 0;
         }
         else numberOfTries++;
     }
     if (straddleContactElts.Any()) Debug.WriteLine("Contact Edges found that are not contained in loop.");
     return new ContactData(loops);
 }
Пример #3
0
        private static Visual3D MakeModelVisual3D(TessellatedSolid ts)
        {
            var defaultMaterial = MaterialHelper.CreateMaterial(
                    new Color
                    {
                        A = ts.SolidColor.A,
                        B = ts.SolidColor.B,
                        G = ts.SolidColor.G,
                        R = ts.SolidColor.R
                    });
            if (ts.HasUniformColor)
            {
                var positions = ts.Faces.SelectMany(f => f.Vertices.Select(v => new Point3D(v.Position[0], v.Position[1], v.Position[2])));
                var normals = ts.Faces.SelectMany(f => f.Vertices.Select(v => new Vector3D(f.Normal[0], f.Normal[1], f.Normal[2])));
                return new ModelVisual3D
                {
                    Content =
                        new GeometryModel3D
                        {
                            Geometry = new MeshGeometry3D
                            {
                                Positions = new Point3DCollection(positions),
                                // TriangleIndices = new Int32Collection(triIndices),
                                Normals = new Vector3DCollection(normals)
                            },
                            Material = defaultMaterial
                        }
                };
            }
            var result = new ModelVisual3D();
            foreach (var f in ts.Faces)
            {
                var vOrder = new Point3DCollection();
                for (var i = 0; i < 3; i++)
                    vOrder.Add(new Point3D(f.Vertices[i].X, f.Vertices[i].Y, f.Vertices[i].Z));

                var c = (f.color == null)
                    ? defaultMaterial
                    : MaterialHelper.CreateMaterial(new Color { A = f.color.A, B = f.color.B, G = f.color.G, R = f.color.R });
                result.Children.Add(new ModelVisual3D
                {
                    Content =
                        new GeometryModel3D
                        {
                            Geometry = new MeshGeometry3D { Positions = vOrder },
                            Material = c
                        }
                });
            }
            return result;
        }
Пример #4
0
        /// <summary>
        /// Divides up contact.
        /// </summary>
        /// <param name="ts">The ts.</param>
        /// <param name="contactData">The contact data.</param>
        /// <param name="plane">The plane.</param>
        /// <exception cref="System.Exception">face is supposed to be split at plane but lives only on one side</exception>
        private static void DivideUpContact(TessellatedSolid ts, ContactData contactData, Flat plane)
        {
            var edgesToAdd = new List<Edge>();
            var facesToAdd = new List<PolygonalFace>();
            var verticesToAdd = new List<Vertex>();
            var edgesToDelete = new List<Edge>();
            var facesToDelete = new List<PolygonalFace>();
            var edgesToModify = new List<Edge>();
            foreach (var loop in contactData.AllLoops)
            {
                for (int i = 0; i < loop.Count; i++)
                {
                    var ce = loop[i];
                    if (ce is CoincidentEdgeContactElement)
                        // If the contact element is at a coincident edge, then there is nothing to do in this stage. When contact element was
                        // created, it properly defined SplitFacePositive and SplitFaceNegative.
                        continue;
                    edgesToAdd.Add(ce.ContactEdge); // the contact edge is a new edge for the solid
                    edgesToModify.Add(ce.ContactEdge); // the contact edge will need to be linked to vertices and faces further down.
                    var faceToSplit = ce.SplitFacePositive; //faceToSplit will be removed, but before we do that, we use
                    facesToDelete.Add(faceToSplit);         // use it to build the new 2 to 3 triangles

                    PolygonalFace positiveFace, negativeFace;
                    if (ce is ThroughVertexContactElement)
                    {
                        var vertPlaneDistances =              //signed distances of faceToSplit's vertices from the plane
                        faceToSplit.Vertices.Select(
                            v => v.Position.dotProduct(plane.Normal) - plane.DistanceToOrigin).ToArray();
                        var maxIndex = vertPlaneDistances.FindIndex(vertPlaneDistances.Max());
                        var maxVert = faceToSplit.Vertices[maxIndex];
                        var minIndex = vertPlaneDistances.FindIndex(vertPlaneDistances.Min());
                        var minVert = faceToSplit.Vertices[minIndex];
                        positiveFace = new PolygonalFace(new[] { ce.ContactEdge.From, ce.ContactEdge.To, maxVert });
                        facesToAdd.Add(positiveFace);
                        negativeFace = new PolygonalFace(new[] { ce.ContactEdge.To, ce.ContactEdge.From, minVert });
                        facesToAdd.Add(negativeFace);
                    } //#+1 add v to f           (both of these are done in the preceding PolygonalFace
                    //#+2 add f to v            constructors as well as the one for thirdFace below)
                    else // then ce is a ThroughFaceContactElement
                    {
                        var tfce = (ThroughFaceContactElement)ce; // ce is renamed and recast as tfce
                        edgesToDelete.Add(tfce.SplitEdge);
                        verticesToAdd.Add(tfce.StartVertex);
                        Vertex positiveVertex, negativeVertex;
                        if (tfce.SplitEdge.To.Position.dotProduct(plane.Normal) > plane.DistanceToOrigin)
                        {
                            positiveVertex = tfce.SplitEdge.To;
                            negativeVertex = tfce.SplitEdge.From;
                        }
                        else
                        {
                            positiveVertex = tfce.SplitEdge.From;
                            negativeVertex = tfce.SplitEdge.To;
                        }
                        positiveFace =
                           new PolygonalFace(new[] { ce.ContactEdge.From, ce.ContactEdge.To, positiveVertex });
                        facesToAdd.Add(positiveFace);
                        negativeFace =
                           new PolygonalFace(new[] { ce.ContactEdge.To, ce.ContactEdge.From, negativeVertex });
                        facesToAdd.Add(negativeFace);
                        var positiveEdge = new Edge(positiveVertex, ce.ContactEdge.From, positiveFace, null, true,
                            true);
                        edgesToAdd.Add(positiveEdge);
                        edgesToModify.Add(positiveEdge);
                        var negativeEdge = new Edge(ce.ContactEdge.From, negativeVertex, negativeFace, null, true,
                            true);
                        edgesToAdd.Add(negativeEdge);
                        edgesToModify.Add(negativeEdge);

                        var otherVertex = faceToSplit.Vertices.First(v => v != positiveVertex && v != negativeVertex);
                        PolygonalFace thirdFace;
                        if (otherVertex.Position.dotProduct(plane.Normal) > plane.DistanceToOrigin)
                        {
                            thirdFace = new PolygonalFace(new[] { ce.ContactEdge.To, otherVertex, positiveVertex });
                            facesToAdd.Add(thirdFace);
                            edgesToAdd.Add(new Edge(ce.ContactEdge.To, positiveVertex, positiveFace, thirdFace, true, true));
                        }
                        else
                        {
                            thirdFace = new PolygonalFace(new[] { ce.ContactEdge.To, negativeVertex, otherVertex });
                            facesToAdd.Add(thirdFace);
                            edgesToAdd.Add(new Edge(negativeVertex, ce.ContactEdge.To, negativeFace, thirdFace, true, true));
                        }
                        // for the new edges in a through face this line accomplishes: +3 add f to e; +4 add e to f; +5 add v to e;
                        //    +6 add e to v
                    }
                    loop[i] = new CoincidentEdgeContactElement
                    {
                        ContactEdge = ce.ContactEdge,
                        EndVertex = ce.ContactEdge.To,
                        StartVertex = ce.ContactEdge.From,
                        SplitFaceNegative = negativeFace,
                        SplitFacePositive = positiveFace
                    };
                }
            }
            // -1 remove v from f - no need to do this as no v's are removed
            foreach (var face in facesToDelete)
            {
                foreach (var vertex in face.Vertices)
                    vertex.Faces.Remove(face); //-2 remove f from v
                foreach (var edge in face.Edges)
                {
                    if (edgesToDelete.Contains(edge)) continue;
                    edgesToModify.Add(edge);
                    if (edge.OwnedFace == face) edge.OwnedFace = null; //-3 remove f from e
                    else edge.OtherFace = null;
                }
            }
            //-4 remove e from f - no need to do as the only edges deleted are the ones between deleted faces
            ts.RemoveFaces(facesToDelete);
            // -5 remove v from e - not needed as no vertices are deleted (like -1 above)
            foreach (var edge in edgesToDelete)
            {
                edge.From.Edges.Remove(edge); //-6 remove e from v
                edge.To.Edges.Remove(edge);
            }
            ts.RemoveEdges(edgesToDelete);
            // now to add new faces to modified edges
            ts.AddVertices(verticesToAdd);
            ts.AddFaces(facesToAdd);

            foreach (var edge in edgesToModify)
            {
                var facesToAttach = facesToAdd.Where(f => f.Vertices.Contains(edge.To) && f.Vertices.Contains(edge.From)
                    && !f.Edges.Contains(edge));
                if (facesToAttach.Count() > 2) throw new Exception();
                foreach (var face in facesToAttach)
                {
                    face.Edges.Add(edge); //+4 add e to f
                    var fromIndex = face.Vertices.IndexOf(edge.From);
                    if ((fromIndex == face.Vertices.Count - 1 && face.Vertices[0] == edge.To)
                        || (fromIndex < face.Vertices.Count - 1 && face.Vertices[fromIndex + 1] == edge.To))
                        edge.OwnedFace = face; //+3 add f to e
                    else edge.OtherFace = face;
                }
            }
            ts.AddEdges(edgesToAdd);
        }
Пример #5
0
 private static List<TessellatedSolid> convertFaceListsToSolids(TessellatedSolid ts, List<List<PolygonalFace>> facesLists,
     List<Loop> loops, Boolean onPositiveSide, Flat plane)
 {
     List<TessellatedSolid> solids = new List<TessellatedSolid>();
     foreach (var facesList in facesLists)
     {
         // get a list of the vertex indices from the original solid
         var vertIndices = facesList.SelectMany(f => f.Vertices.Select(v => v.IndexInList))
             .Distinct().OrderBy(index => index).ToArray();
         var numVertices = vertIndices.Count();
         // get the set of connected loops for this list of faces. it could be one or it could be all
         var connectedLoops = loops.Where(loop =>
             (onPositiveSide && loop.Any(ce => facesList.Contains(ce.SplitFacePositive)))
             || (!onPositiveSide && loop.Any(ce => facesList.Contains(ce.SplitFaceNegative))))
             .ToList();
         // put the vertices from vertIndices in subSolidVertices, except those that are on the loop.
         // you'll need to copy those.
         var subSolidVertices = new Vertex[numVertices];
         var indicesToCopy = connectedLoops.SelectMany(loop => loop.Select(ce => ce.StartVertex.IndexInList))
             .OrderBy(index => index).ToArray();
         var numIndicesToCopy = indicesToCopy.GetLength(0);
         var newEdgeVertices = new Vertex[connectedLoops.Count][];
         for (int i = 0; i < connectedLoops.Count; i++)
             newEdgeVertices[i] = new Vertex[connectedLoops[i].Count];
         var copyIndex = 0;
         for (int i = 0; i < numVertices; i++)
         {
             Vertex vertexCopy;
             if (copyIndex < numIndicesToCopy && vertIndices[i] == indicesToCopy[copyIndex])
             {
                 var oldVertex = ts.Vertices[vertIndices[i]];
                 vertexCopy = oldVertex.Copy();
                 for (int j = 0; j < connectedLoops.Count; j++)
                 {
                     var k = connectedLoops[j].FindIndex(ce => ce.StartVertex == oldVertex);
                     newEdgeVertices[j][k] = vertexCopy;
                 }
                 foreach (var face in oldVertex.Faces.Where(face => facesList.Contains(face)))
                 {
                     face.Vertices.Remove(oldVertex);
                     face.Vertices.Add(vertexCopy);
                     vertexCopy.Faces.Add(face);
                 }
                 while (copyIndex < numIndicesToCopy && vertIndices[i] >= indicesToCopy[copyIndex])
                     copyIndex++;
             }
             else vertexCopy = ts.Vertices[vertIndices[i]];
             vertexCopy.IndexInList = i;
             subSolidVertices[i] = vertexCopy;
         }
         solids.Add(new TessellatedSolid(facesList, subSolidVertices, newEdgeVertices, onPositiveSide ? plane.Normal.multiply(-1) : plane.Normal,
             connectedLoops.Select(loop => loop.IsPositive).ToArray()));
     }
     return solids;
 }
Пример #6
0
        /// <summary>
        /// Performs the slicing operation on the prescribed flat plane. This destructively alters
        /// the tessellated solid into one or more solids which are returned in the "out" parameter
        /// lists.
        /// </summary>
        /// <param name="oldSolid">The old solid.</param>
        /// <param name="plane">The plane.</param>
        /// <param name="positiveSideSolids">The solids that are on the positive side of the plane
        /// This means that are on the side that the normal faces.</param>
        /// <param name="negativeSideSolids">The solids on the negative side of the plane.</param>
        public static void OnFlat(TessellatedSolid ts, Flat plane,
            out List<TessellatedSolid> positiveSideSolids, out List<TessellatedSolid> negativeSideSolids)
        {
            var contactData = DefineContact(plane, ts);

            DivideUpContact(ts, contactData, plane);
            var loops =
                contactData.AllLoops.Where(loop => loop.All(
                            ce => !(ce.ContactEdge.Curvature == CurvatureType.Convex && ce is CoincidentEdgeContactElement))).ToList();
            var allNegativeStartingFaces =
               loops.SelectMany(loop => loop.Select(ce => ce.SplitFaceNegative)).ToList();
            var allPositiveStartingFaces =
                loops.SelectMany(loop => loop.Select(ce => ce.SplitFacePositive)).ToList();

            var negativeSideFaceList = FindAllSolidsWithTheseFaces(allNegativeStartingFaces, allPositiveStartingFaces);
            var positiveSideFaceList = FindAllSolidsWithTheseFaces(allPositiveStartingFaces, allNegativeStartingFaces);

            negativeSideSolids = convertFaceListsToSolids(ts, negativeSideFaceList, loops, false, plane);
            positiveSideSolids = convertFaceListsToSolids(ts, positiveSideFaceList, loops, true, plane);
        }
Пример #7
0
 private static BoundingBox Find_via_MC_ApproachOne(TessellatedSolid ts)
 {
     BoundingBox minBox = new BoundingBox();
     var minVolume = double.PositiveInfinity;
     foreach (var convexHullEdge in ts.ConvexHullEdges)
     {
         var rotAxis = convexHullEdge.Vector.normalize();
         var n = convexHullEdge.OwnedFace.Normal;
         var numSamples = (int)Math.Ceiling((Math.PI - convexHullEdge.InternalAngle) / MaxDeltaAngle);
         var deltaAngle = (Math.PI - convexHullEdge.InternalAngle) / numSamples;
         var edgeBBs = new BoundingBox[numSamples];
         for (var i = 0; i < numSamples; i++)
         {
             double[] direction;
             if (i == 0) direction = n;
             else
             {
                 var angleChange = i * deltaAngle;
                 var invCrossMatrix = new[,]
                 {
                     {n[0]*n[0], n[0]*n[1], n[0]*n[2]},
                     {n[1]*n[0], n[1]*n[1], n[1]*n[2]},
                     {n[2]*n[0], n[2]*n[1], n[2]*n[2]}
                 };
                 direction = invCrossMatrix.multiply(rotAxis.multiply(Math.Sin(angleChange)));
             }
             edgeBBs[i] = FindOBBAlongDirection(ts.ConvexHullVertices, direction);
             if (edgeBBs[i].Volume < minVolume)
             {
                 minBox = edgeBBs[i];
                 minVolume = minBox.Volume;
             }
         }
     }
     return minBox;
 }
Пример #8
0
 private static BoundingBox Find_via_PCA_Approach(TessellatedSolid ts)
 {
     throw new NotImplementedException();
 }
Пример #9
0
 /// <summary>
 /// Orienteds the bounding box.
 /// </summary>
 /// <param name="ts">The ts.</param>
 /// <returns>BoundingBox.</returns>
 public static BoundingBox OrientedBoundingBox(TessellatedSolid ts)
 {
     return Find_via_MC_ApproachOne(ts);
 }
Пример #10
0
 /// <summary>
 ///     Copies this instance.
 /// </summary>
 /// <returns>TessellatedSolid.</returns>
 public TessellatedSolid Copy()
 {
     var copyOfFaces = new PolygonalFace[NumberOfFaces];
     for (var i = 0; i < NumberOfFaces; i++)
         copyOfFaces[i] = Faces[i].Copy();
     var copyOfVertices = new Vertex[NumberOfVertices];
     for (var i = 0; i < NumberOfVertices; i++)
         copyOfVertices[i] = Vertices[i].Copy();
     for (var fIndex = 0; fIndex < NumberOfFaces; fIndex++)
     {
         var thisFace = copyOfFaces[fIndex];
         var oldFace = Faces[fIndex];
         var vertexIndices = new List<int>();
         foreach (var oldVertex in oldFace.Vertices)
         {
             var vIndex = oldVertex.IndexInList;
             vertexIndices.Add(vIndex);
             var thisVertex = copyOfVertices[vIndex];
             thisFace.Vertices.Add(thisVertex);
             thisVertex.Faces.Add(thisFace);
         }
     }
     Edge[] copyOfEdges = MakeEdges(copyOfFaces, copyOfVertices);
     var copy = new TessellatedSolid
        {
        SurfaceArea = SurfaceArea,
        Center = (double[])Center.Clone(),
        Faces = copyOfFaces,
        Vertices = copyOfVertices,
        Edges = copyOfEdges,
        Name = Name,
        NumberOfFaces = NumberOfFaces,
        NumberOfVertices = NumberOfVertices,
        Volume = Volume,
        XMax = XMax,
        XMin = XMin,
        YMax = YMax,
        YMin = YMin,
        ZMax = ZMax,
        ZMin = ZMin
        };
     copy.CreateConvexHull();
     return copy;
 }
Пример #11
0
        private static void TestSlice(TessellatedSolid ts)
        {
            //var a= ContactData.Divide(new Flat { DistanceToOrigin = 40 , Normal = new []{0,1.0,0} }, ts).Area;
            //                          Debug.WriteLine(a);
            //Console.ReadKey();
            //return;
            var now = DateTime.Now;
            Debug.WriteLine("start...");
            var crossAreas = new double[3][,];

            List<TessellatedSolid> positiveSideSolids, negativeSideSolids;
            Slice.OnFlat(ts, new Flat(0, new[] { 1.0, 0, 0 }), out positiveSideSolids, out negativeSideSolids);
        }
Пример #12
0
 //private static void TestClassification(TessellatedSolid ts)
 //{
 //    TesselationToPrimitives.Run(ts);
 //}
 private static void TestOBB(TessellatedSolid ts)
 {
     var obb = MinimumEnclosure.OrientedBoundingBox(ts);
 }
Пример #13
0
        private static void TestXSections(TessellatedSolid ts)
        {
            var now = DateTime.Now;
            Debug.WriteLine("start...");
            var crossAreas = new double[3][,];
            var maxSlices = 100;
            var delta = Math.Max((ts.Bounds[1][0] - ts.Bounds[0][0]) / maxSlices,
                Math.Max((ts.Bounds[1][1] - ts.Bounds[0][1]) / maxSlices,
                    (ts.Bounds[1][2] - ts.Bounds[0][2]) / maxSlices));
            //Parallel.For(0, 3, i =>
            for (int i = 0; i < 3; i++)
            {
                //var max = ts.Bounds[1][i];
                //var min = ts.Bounds[0][i];
                //var numSteps = (int)Math.Ceiling((max - min) / delta);
                var coordValues = ts.Vertices.Select(v => v.Position[i]).Distinct().OrderBy(x => x).ToList();
                var numSteps = coordValues.Count;
                var direction = new double[3];
                direction[i] = 1.0;
                crossAreas[i] = new double[numSteps, 2];
                for (var j = 0; j < numSteps; j++)
                {
                    var dist = crossAreas[i][j, 0] = coordValues[j];
                    Console.WriteLine("slice at Coord " + i + " at " + coordValues[j]);
                    crossAreas[i][j, 1] = Slice.DefineContact(new Flat(dist, direction), ts).Area;
                }
            }//);
            Debug.WriteLine("end...Time Elapsed = " + (DateTime.Now - now));

            Console.ReadKey();
            for (var i = 0; i < 3; i++)
            {
                Debug.WriteLine("\nfor direction " + i);
                for (var j = 0; j < crossAreas[i].GetLength(0); j++)
                {
                    Debug.WriteLine(crossAreas[i][j, 0] + ", " + crossAreas[i][j, 1]);
                }
            }
        }