Exemplo n.º 1
0
        /// <summary>
        /// Function called before and after triangle merging.
        /// Before merging it calculates the Euler characteristic of the original mesh.
        /// After merging it calculates the Euler characteristic of the merged mesh.
        /// </summary>
        private int CalculateEulerCharacteristic()
        {
            int noVertices = 0;
            int noHoles    = 0; // Stays zero if mesh is triangular
            int noFaces;
            HashSet <IndexSegment> edges = new HashSet <IndexSegment>(new SegmentComparer(true /*compareBothDirections*/));

            if (m_MergedFaceSet.Count != 0)
            {
                // Merging already occurred, calculate new Euler characteristic
                noFaces = m_MergedFaceSet.Count;
                foreach (int mergedFace in m_MergedFaceSet)
                {
                    m_FacesCollDict[mergedFace].OuterAndInnerBoundaries.ToList().ForEach(vp => edges.Add(vp.Value));
                    if (m_FacesCollDict[mergedFace].IndexedInnerBoundaries != null)
                    {
                        noHoles += m_FacesCollDict[mergedFace].IndexedInnerBoundaries.Count;
                    }
                }
                noVertices = m_MeshVertices.Count; // m_MeshVertices doesn't contain isolated vertices
            }
            else
            {
                if (IsMesh)
                {
                    noVertices = m_MeshGeom.Vertices.Count;
                    noFaces    = m_MeshGeom.NumTriangles;
                    for (int faceIdx = 0; faceIdx < noFaces; faceIdx++)
                    {
                        MeshTriangle tri = m_MeshGeom.get_Triangle(faceIdx);
                        edges.Add(new IndexSegment((int)tri.get_Index(0), (int)tri.get_Index(1)));
                        edges.Add(new IndexSegment((int)tri.get_Index(1), (int)tri.get_Index(2)));
                        edges.Add(new IndexSegment((int)tri.get_Index(2), (int)tri.get_Index(0)));
                    }
                }
                else
                {
                    noVertices = m_Geom.VertexCount;
                    noFaces    = m_Geom.TriangleCount;
                    for (int faceIdx = 0; faceIdx < noFaces; faceIdx++)
                    {
                        TriangleInShellComponent tri = m_Geom.GetTriangle(faceIdx);
                        edges.Add(new IndexSegment(tri.VertexIndex0, tri.VertexIndex1));
                        edges.Add(new IndexSegment(tri.VertexIndex1, tri.VertexIndex2));
                        edges.Add(new IndexSegment(tri.VertexIndex2, tri.VertexIndex0));
                    }
                }
            }

            // V - E + F - I
            return(noVertices - edges.Count + noFaces - noHoles);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Function called before and after triangle merging.
        /// Before merging it calculates the Euler characteristic of the original mesh.
        /// After merging it calculates the Euler characteristic of the merged mesh.
        /// </summary>
        private int CalculateEulerCharacteristic()
        {
            int noVertices = (IsMesh) ? _meshGeom.Vertices.Count : _geom.VertexCount;
            int noHoles    = 0; // Stays zero if mesh is triangular
            int noFaces;
            HashSet <IndexSegment> edges = new HashSet <IndexSegment>(new SegmentComparer(true /*compareBothDirections*/));

            if (_mergedFaceList.Count != 0)
            {
                // Merging already occurred, calculate new Euler characteristic
                noFaces = _mergedFaceList.Count;
                foreach (int mergedFace in _mergedFaceList)
                {
                    edges.UnionWith(facesColl[mergedFace].outerAndInnerBoundaries);
                    if (facesColl[mergedFace].indexedInnerBoundaries != null)
                    {
                        noHoles += facesColl[mergedFace].indexedInnerBoundaries.Count;
                    }
                }
            }
            else
            {
                if (IsMesh)
                {
                    noFaces = _meshGeom.NumTriangles;
                    for (int faceIdx = 0; faceIdx < noFaces; faceIdx++)
                    {
                        MeshTriangle tri = _meshGeom.get_Triangle(faceIdx);
                        edges.Add(new IndexSegment((int)tri.get_Index(0), (int)tri.get_Index(1)));
                        edges.Add(new IndexSegment((int)tri.get_Index(1), (int)tri.get_Index(2)));
                        edges.Add(new IndexSegment((int)tri.get_Index(2), (int)tri.get_Index(0)));
                    }
                }
                else
                {
                    noFaces = _geom.TriangleCount;
                    for (int faceIdx = 0; faceIdx < noFaces; faceIdx++)
                    {
                        TriangleInShellComponent tri = _geom.GetTriangle(faceIdx);
                        edges.Add(new IndexSegment(tri.VertexIndex0, tri.VertexIndex1));
                        edges.Add(new IndexSegment(tri.VertexIndex1, tri.VertexIndex2));
                        edges.Add(new IndexSegment(tri.VertexIndex2, tri.VertexIndex0));
                    }
                }
            }

            // V - E + F - I
            return(noVertices - edges.Count + noFaces - noHoles);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created
        /// </summary>
        public void SimplifyAndMergeFaces()
        {
            int noTriangle = _geom.TriangleCount;
            int noVertices = _geom.VertexCount;
            IEqualityComparer <XYZ>       normalComparer     = new vectorCompare();
            Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer);

            for (int ef = 0; ef < noTriangle; ++ef)
            {
                TriangleInShellComponent f = _geom.GetTriangle(ef);
                IList <int> vertIndex      = new List <int>();
                vertIndex.Add(f.VertexIndex0);
                vertIndex.Add(f.VertexIndex1);
                vertIndex.Add(f.VertexIndex2);

                IndexFace intF = new IndexFace(vertIndex);
                facesColl.Add(ef, intF);     // Keep faces in a dictionary and assigns ID
                List <int> fIDList;

                if (!faceSortedByNormal.TryGetValue(intF.normal, out fIDList))
                {
                    fIDList = new List <int>();
                    fIDList.Add(ef);
                    faceSortedByNormal.Add(intF.normal, fIDList);
                }
                else
                {
                    if (!fIDList.Contains(ef))
                    {
                        fIDList.Add(ef);
                    }
                }
            }

            foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal)
            {
                List <int> mergedFaceList = null;
                if (fListDict.Value.Count > 1)
                {
                    TryMergeFaces(fListDict.Value, out mergedFaceList);
                    if (mergedFaceList != null && mergedFaceList.Count > 0)
                    {
                        // insert only new face indexes as the mergedlist from different vertices can be duplicated
                        foreach (int fIdx in mergedFaceList)
                        {
                            if (!_mergedFaceList.Contains(fIdx))
                            {
                                _mergedFaceList.Add(fIdx);
                            }
                        }
                    }
                }
                else if (!_mergedFaceList.Contains(fListDict.Value[0]))
                {
                    _mergedFaceList.Add(fListDict.Value[0]); // No pair face, add it into the mergedList
                }
            }
        }
Exemplo n.º 4
0
        private void ExportSolid(Solid solid)
        {
            SolidOrShellTessellationControls solidOrShellTessellationControls = new SolidOrShellTessellationControls
            {
                LevelOfDetail      = userSetting.LevelOfDetail / 30.0,
                Accuracy           = 0.1,
                MinAngleInTriangle = 0.0001,
                MinExternalAngleBetweenTriangles = 1.0
            };

            try
            {
                TriangulatedSolidOrShell triangulatedSolidOrShell = SolidUtils.TessellateSolidOrShell(solid, solidOrShellTessellationControls);
                int shellComponentCount = triangulatedSolidOrShell.ShellComponentCount;
                for (int i = 0; i < shellComponentCount; i++)
                {
                    TriangulatedShellComponent shellComponent = triangulatedSolidOrShell.GetShellComponent(i);
                    ModelGeometry exportedGeometry            = new ModelGeometry
                    {
                        Transform = transformationStack.Peek(),
                        Points    = new List <XYZ>(shellComponent.VertexCount)
                    };
                    for (int num = 0; num != shellComponent.VertexCount; num++)
                    {
                        exportedGeometry.Points.Add(shellComponent.GetVertex(num));
                    }
                    exportedGeometry.Indices = new List <int>(shellComponent.TriangleCount * 3);
                    for (int j = 0; j < shellComponent.TriangleCount; j++)
                    {
                        TriangleInShellComponent triangle = shellComponent.GetTriangle(j);
                        exportedGeometry.Indices.Add(triangle.VertexIndex0);
                        exportedGeometry.Indices.Add(triangle.VertexIndex1);
                        exportedGeometry.Indices.Add(triangle.VertexIndex2);
                    }
                    exportedGeometry.CalculateNormals(false);
                    exportedGeometry.CalculateUVs(true, false);
                    ElementId materialElementId       = solid.Faces.get_Item(0).MaterialElementId;
                    Tuple <Document, ElementId> tuple = new Tuple <Document, ElementId>(documentStack.Peek(), materialElementId);
                    ChangeCurrentMaterial(tuple);
                    documentAndMaterialIdToGeometries[tuple].Add(exportedGeometry);
                }
            }
            catch (Exception)
            {
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Create a new list of geometry objects from the
        /// given input. As input, we supply the result of
        /// Room.GetClosedShell. The output is the exact
        /// same solid lacking whatever flaws are present
        /// in the input solid.
        /// </summary>
        static IList <GeometryObject> CopyGeometry(
            GeometryElement geo,
            ElementId materialId,
            List <IntPoint3d> coords,
            List <TriangleIndices> indices)
        {
            TessellatedShapeBuilderResult result = null;

            TessellatedShapeBuilder builder
                = new TessellatedShapeBuilder();

            // Need to include the key in the value, otherwise
            // no way to access it later, cf.
            // https://stackoverflow.com/questions/1619090/getting-a-keyvaluepair-directly-from-a-dictionary

            Dictionary <XYZ, KeyValuePair <XYZ, int> > pts
                = new Dictionary <XYZ, KeyValuePair <XYZ, int> >(
                      new XyzEqualityComparer());

            int nSolids = 0;
            //int nFaces = 0;
            int nTriangles = 0;
            //int nVertices = 0;
            List <XYZ> vertices = new List <XYZ>(3);

            foreach (GeometryObject obj in geo)
            {
                Solid solid = obj as Solid;

                if (null != solid)
                {
                    if (0 < solid.Volume)
                    {
                        ++nSolids;

                        builder.OpenConnectedFaceSet(false);

                        #region Create a new solid based on tessellation of the invalid room closed shell solid
#if CREATE_NEW_SOLID_USING_TESSELATION
                        Debug.Assert(
                            SolidUtils.IsValidForTessellation(solid),
                            "expected a valid solid for room closed shell");

                        SolidOrShellTessellationControls controls
                            = new SolidOrShellTessellationControls()
                            {
                            //
                            // Summary:
                            //     A positive real number specifying how accurately a triangulation should approximate
                            //     a solid or shell.
                            //
                            // Exceptions:
                            //   T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException:
                            //     When setting this property: The given value for accuracy must be greater than
                            //     0 and no more than 30000 feet.
                            // This statement is not true. I set Accuracy = 0.003 and an exception was thrown.
                            // Setting it to 0.006 was acceptable. 0.03 is a bit over 9 mm.
                            //
                            // Remarks:
                            //     The maximum distance from a point on the triangulation to the nearest point on
                            //     the solid or shell should be no greater than the specified accuracy. This constraint
                            //     may be approximately enforced.
                            Accuracy = 0.03,
                            //
                            // Summary:
                            //     An number between 0 and 1 (inclusive) specifying the level of detail for the
                            //     triangulation of a solid or shell.
                            //
                            // Exceptions:
                            //   T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException:
                            //     When setting this property: The given value for levelOfDetail must lie between
                            //     0 and 1 (inclusive).
                            //
                            // Remarks:
                            //     Smaller values yield coarser triangulations (fewer triangles), while larger values
                            //     yield finer triangulations (more triangles).
                            LevelOfDetail = 0.1,
                            //
                            // Summary:
                            //     A non-negative real number specifying the minimum allowed angle for any triangle
                            //     in the triangulation, in radians.
                            //
                            // Exceptions:
                            //   T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException:
                            //     When setting this property: The given value for minAngleInTriangle must be at
                            //     least 0 and less than 60 degrees, expressed in radians. The value 0 means to
                            //     ignore the minimum angle constraint.
                            //
                            // Remarks:
                            //     A small value can be useful when triangulating long, thin objects, in order to
                            //     keep the number of triangles small, but it can result in long, thin triangles,
                            //     which are not acceptable for all applications. If the value is too large, this
                            //     constraint may not be satisfiable, causing the triangulation to fail. This constraint
                            //     may be approximately enforced. A value of 0 means to ignore the minimum angle
                            //     constraint.
                            MinAngleInTriangle = 3 * Math.PI / 180.0,
                            //
                            // Summary:
                            //     A positive real number specifying the minimum allowed value for the external
                            //     angle between two adjacent triangles, in radians.
                            //
                            // Exceptions:
                            //   T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException:
                            //     When setting this property: The given value for minExternalAngleBetweenTriangles
                            //     must be greater than 0 and no more than 30000 feet.
                            //
                            // Remarks:
                            //     A small value yields more smoothly curved triangulated surfaces, usually at the
                            //     expense of an increase in the number of triangles. Note that this setting has
                            //     no effect for planar surfaces. This constraint may be approximately enforced.
                            MinExternalAngleBetweenTriangles = 0.2 * Math.PI
                            };

                        TriangulatedSolidOrShell shell
                            = SolidUtils.TessellateSolidOrShell(solid, controls);

                        int n = shell.ShellComponentCount;

                        Debug.Assert(1 == n,
                                     "expected just one shell component in room closed shell");

                        TriangulatedShellComponent component
                            = shell.GetShellComponent(0);

                        int coordsBase  = coords.Count;
                        int indicesBase = indices.Count;

                        n = component.VertexCount;

                        for (int i = 0; i < n; ++i)
                        {
                            XYZ v = component.GetVertex(i);
                            coords.Add(new IntPoint3d(v));
                        }

                        n = component.TriangleCount;

                        for (int i = 0; i < n; ++i)
                        {
                            TriangleInShellComponent t
                                = component.GetTriangle(i);

                            vertices.Clear();

                            vertices.Add(component.GetVertex(t.VertexIndex0));
                            vertices.Add(component.GetVertex(t.VertexIndex1));
                            vertices.Add(component.GetVertex(t.VertexIndex2));

                            indices.Add(new TriangleIndices(
                                            coordsBase + t.VertexIndex0,
                                            coordsBase + t.VertexIndex1,
                                            coordsBase + t.VertexIndex2));

                            TessellatedFace tf = new TessellatedFace(
                                vertices, materialId);

                            if (builder.DoesFaceHaveEnoughLoopsAndVertices(tf))
                            {
                                builder.AddFace(tf);
                                ++nTriangles;
                            }
                        }
#else
                        // Iterate over the individual solid faces

                        foreach (Face f in solid.Faces)
                        {
                            vertices.Clear();

                            #region Use face triangulation
#if USE_FACE_TRIANGULATION
                            Mesh mesh = f.Triangulate();
                            int  n    = mesh.NumTriangles;

                            for (int i = 0; i < n; ++i)
                            {
                                MeshTriangle triangle = mesh.get_Triangle(i);

                                XYZ p1 = triangle.get_Vertex(0);
                                XYZ p2 = triangle.get_Vertex(1);
                                XYZ p3 = triangle.get_Vertex(2);

                                vertices.Clear();
                                vertices.Add(p1);
                                vertices.Add(p2);
                                vertices.Add(p3);

                                TessellatedFace tf
                                    = new TessellatedFace(
                                          vertices, materialId);

                                if (builder.DoesFaceHaveEnoughLoopsAndVertices(tf))
                                {
                                    builder.AddFace(tf);
                                    ++nTriangles;
                                }
                            }
#endif // USE_FACE_TRIANGULATION
                            #endregion // Use face triangulation

                            #region Use original solid and its EdgeLoops
#if USE_EDGELOOPS
                            // This returns arbitrarily ordered and
                            // oriented edges, so no solid can be
                            // generated.

                            foreach (EdgeArray loop in f.EdgeLoops)
                            {
                                foreach (Edge e in loop)
                                {
                                    XYZ p = e.AsCurve().GetEndPoint(0);
                                    XYZ q = p;

                                    if (pts.ContainsKey(p))
                                    {
                                        KeyValuePair <XYZ, int> kv = pts[p];
                                        q = kv.Key;
                                        int n = kv.Value;
                                        pts[p] = new KeyValuePair <XYZ, int>(
                                            q, ++n);

                                        Debug.Print("Ignoring vertex at {0} "
                                                    + "with distance {1} to existing "
                                                    + "vertex {2}",
                                                    p, p.DistanceTo(q), q);
                                    }
                                    else
                                    {
                                        pts[p] = new KeyValuePair <XYZ, int>(
                                            p, 1);
                                    }

                                    vertices.Add(q);
                                    ++nVertices;
                                }
                            }
#endif // USE_EDGELOOPS
                            #endregion // Use original solid and its EdgeLoops

                            #region Use original solid and GetEdgesAsCurveLoops
#if USE_AS_CURVE_LOOPS
                            // The solids generated by this have some weird
                            // normals, so they do not render correctly in
                            // the Forge viewer. Revert to triangles again.

                            IList <CurveLoop> loops
                                = f.GetEdgesAsCurveLoops();

                            foreach (CurveLoop loop in loops)
                            {
                                foreach (Curve c in loop)
                                {
                                    XYZ p = c.GetEndPoint(0);
                                    XYZ q = p;

                                    if (pts.ContainsKey(p))
                                    {
                                        KeyValuePair <XYZ, int> kv = pts[p];
                                        q = kv.Key;
                                        int n = kv.Value;
                                        pts[p] = new KeyValuePair <XYZ, int>(
                                            q, ++n);

                                        Debug.Print("Ignoring vertex at {0} "
                                                    + "with distance {1} to existing "
                                                    + "vertex {2}",
                                                    p, p.DistanceTo(q), q);
                                    }
                                    else
                                    {
                                        pts[p] = new KeyValuePair <XYZ, int>(
                                            p, 1);
                                    }

                                    vertices.Add(q);
                                    ++nVertices;
                                }
                            }
#endif // USE_AS_CURVE_LOOPS
                            #endregion // Use original solid and GetEdgesAsCurveLoops

                            builder.AddFace(new TessellatedFace(
                                                vertices, materialId));

                            ++nFaces;
                        }
#endif // CREATE_NEW_SOLID_USING_TESSELATION
                        #endregion // Create a new solid based on tessellation of the invalid room closed shell solid

                        builder.CloseConnectedFaceSet();
                        builder.Target   = TessellatedShapeBuilderTarget.AnyGeometry; // Solid failed
                        builder.Fallback = TessellatedShapeBuilderFallback.Mesh;      // use Abort if target is Solid
                        builder.Build();
                        result = builder.GetBuildResult();

                        // Debug printout log of current solid's glTF facet data

                        n = coords.Count - coordsBase;

                        Debug.Print("{0} glTF vertex coordinates "
                                    + "in millimetres:", n);

                        Debug.Print(string.Join(" ", coords
                                                .TakeWhile <IntPoint3d>((p, i) => coordsBase <= i)
                                                .Select <IntPoint3d, string>(p => p.ToString())));

                        n = indices.Count - indicesBase;

                        Debug.Print("{0} glTF triangles:", n);

                        Debug.Print(string.Join(" ", indices
                                                .TakeWhile <TriangleIndices>((ti, i) => indicesBase <= i)
                                                .Select <TriangleIndices, string>(ti => ti.ToString())));
                    }
                }
            }
            return(result.GetGeometricalObjects());
        }
Exemplo n.º 6
0
        private static IList<LinkedList<int>> ConvertTrianglesToPlanarFacets(TriangulatedShellComponent component)
        {
            IList<LinkedList<int>> facets = new List<LinkedList<int>>();
            
            int numTriangles = component.TriangleCount;

            // sort triangles by normals.

            // This is a list of triangles whose planes are difficult to calculate, so we won't try to optimize them.
            IList<int> sliverTriangles = new List<int>();

            // PlanarKey allows for planes with almost equal normals and origins to be merged.
            Dictionary<PlanarKey, PlanarInfo> planarGroupings = new Dictionary<PlanarKey, PlanarInfo>();

            for (int ii = 0; ii < numTriangles; ii++)
            {
                TriangleInShellComponent currTriangle = component.GetTriangle(ii);

                // Normalize fails if the length is less than 1e-8 or so.  As such, normalilze the vectors
                // along the way to make sure the CrossProduct length isn't too small. 
                int vertex0 = currTriangle.VertexIndex0;
                int vertex1 = currTriangle.VertexIndex1;
                int vertex2 = currTriangle.VertexIndex2;

                XYZ pt1 = component.GetVertex(vertex0);
                XYZ pt2 = component.GetVertex(vertex1);
                XYZ pt3 = component.GetVertex(vertex2);
                XYZ norm = null;

                try
                {
                    XYZ xDir = (pt2 - pt1).Normalize();
                    norm = xDir.CrossProduct((pt3 - pt1).Normalize());
                    norm = norm.Normalize();
                }
                catch
                {
                    sliverTriangles.Add(ii);
                    continue;
                }

                double distToOrig = norm.DotProduct(pt1);
                XYZ origin = new XYZ(norm.X * distToOrig, norm.Y * distToOrig, norm.Z * distToOrig);

                // Go through map of existing planes and add triangle.
                PlanarInfo planarGrouping = null;
                
                PlanarKey currKey = new PlanarKey(norm, origin);
                if (planarGroupings.TryGetValue(currKey, out planarGrouping))
                {
                    planarGrouping.TriangleList.Add(ii);
                }
                else
                {
                    planarGrouping = new PlanarInfo();
                    planarGrouping.TriangleList.Add(ii);
                    planarGroupings[currKey] = planarGrouping;
                }

                planarGrouping.AddTriangleIndexToVertexGrouping(ii, vertex0);
                planarGrouping.AddTriangleIndexToVertexGrouping(ii, vertex1);
                planarGrouping.AddTriangleIndexToVertexGrouping(ii, vertex2);
            }

            foreach (PlanarInfo planarGroupingInfo in planarGroupings.Values)
            {
                IList<int> planarGrouping = planarGroupingInfo.TriangleList;

                HashSet<int> visitedTriangles = new HashSet<int>();
                int numCurrTriangles = planarGrouping.Count;

                for (int ii = 0; ii < numCurrTriangles; ii++)
                {
                    int idx = planarGrouping[ii];
                    if (visitedTriangles.Contains(idx))
                        continue;

                    TriangleInShellComponent currTriangle = component.GetTriangle(idx);

                    HashSet<int> currFacetVertices = new HashSet<int>();

                    LinkedList<int> currFacet = new LinkedList<int>();
                    currFacet.AddLast(currTriangle.VertexIndex0);
                    currFacet.AddLast(currTriangle.VertexIndex1);
                    currFacet.AddLast(currTriangle.VertexIndex2);

                    currFacetVertices.Add(currTriangle.VertexIndex0);
                    currFacetVertices.Add(currTriangle.VertexIndex1);
                    currFacetVertices.Add(currTriangle.VertexIndex2);

                    visitedTriangles.Add(idx);

                    bool foundTriangle;
                    do
                    {
                        foundTriangle = false;

                        // For each pair of adjacent vertices in the triangle, see if there is a triangle that shares that edge.
                        int sizeOfCurrBoundary = currFacet.Count;
                        foreach (int currVertexIndex in currFacet)
                        {
                            HashSet<int> trianglesAtCurrVertex = planarGroupingInfo.TrianglesAtVertexList[currVertexIndex];
                            foreach (int potentialNeighbor in trianglesAtCurrVertex)
                            {
                                if (visitedTriangles.Contains(potentialNeighbor))
                                    continue;

                                TriangleInShellComponent candidateTriangle = component.GetTriangle(potentialNeighbor);
                                int oldVertex = -1, newVertex = -1;

                                // Same normal, unvisited face - see if we have a matching edge.
                                if (currFacetVertices.Contains(candidateTriangle.VertexIndex0))
                                {
                                    if (currFacetVertices.Contains(candidateTriangle.VertexIndex1))
                                    {
                                        oldVertex = candidateTriangle.VertexIndex1;
                                        newVertex = candidateTriangle.VertexIndex2;
                                    }
                                    else if (currFacetVertices.Contains(candidateTriangle.VertexIndex2))
                                    {
                                        oldVertex = candidateTriangle.VertexIndex0;
                                        newVertex = candidateTriangle.VertexIndex1;
                                    }
                                }
                                else if (currFacetVertices.Contains(candidateTriangle.VertexIndex1))
                                {
                                    if (currFacetVertices.Contains(candidateTriangle.VertexIndex2))
                                    {
                                        oldVertex = candidateTriangle.VertexIndex2;
                                        newVertex = candidateTriangle.VertexIndex0;
                                    }
                                }

                                if (oldVertex == -1 || newVertex == -1)
                                    continue;

                                // Found a matching edge, insert it into the existing list.
                                LinkedListNode<int> newPosition = currFacet.Find(oldVertex);
                                currFacet.AddAfter(newPosition, newVertex);

                                foundTriangle = true;
                                visitedTriangles.Add(potentialNeighbor);
                                currFacetVertices.Add(newVertex);

                                break;
                            }

                            if (foundTriangle)
                                break;
                        }
                    } while (foundTriangle);

                    // Check the validity of the facets.  For now, if we have a duplicated vertex,
                    // revert to the original triangles.  TODO: split the facet into outer and inner
                    // loops and remove unnecessary edges.
                    if (currFacet.Count == currFacetVertices.Count)
                        facets.Add(currFacet);
                    else
                    {
                        foreach (int visitedIdx in visitedTriangles)
                        {
                            TriangleInShellComponent visitedTriangle = component.GetTriangle(visitedIdx);

                            LinkedList<int> visitedFacet = new LinkedList<int>();
                            visitedFacet.AddLast(visitedTriangle.VertexIndex0);
                            visitedFacet.AddLast(visitedTriangle.VertexIndex1);
                            visitedFacet.AddLast(visitedTriangle.VertexIndex2);

                            facets.Add(visitedFacet);
                        }
                    }
                }
            }

            // Add in slivery triangles.
            foreach (int sliverIdx in sliverTriangles)
            {
                TriangleInShellComponent currTriangle = component.GetTriangle(sliverIdx);

                LinkedList<int> currFacet = new LinkedList<int>();
                currFacet.AddLast(currTriangle.VertexIndex0);
                currFacet.AddLast(currTriangle.VertexIndex1);
                currFacet.AddLast(currTriangle.VertexIndex2);

                facets.Add(currFacet);
            }

            return facets;
        }
Exemplo n.º 7
0
        private void _solidToMeshGeometry3D(Solid solid)
        {
            try
            {
                IList <Solid> solids = SolidUtils.SplitVolumes(solid);
                foreach (Solid _solid in solids)
                {
                    if (SolidUtils.IsValidForTessellation(_solid))
                    {
                        var material = this.getMaterial(_solid);
                        TriangulatedSolidOrShell shell = SolidUtils.TessellateSolidOrShell(_solid, control);
                        for (int i = 0; i < shell.ShellComponentCount; i++)
                        {
                            try
                            {
                                TriangulatedShellComponent component = shell.GetShellComponent(i);
                                MeshGeometry3D             WPFMesh   = new MeshGeometry3D();
                                Point3D[] points = new Point3D[component.VertexCount];
                                WPFMesh.TriangleIndices = new System.Windows.Media.Int32Collection();
                                for (int j = 0; j < component.TriangleCount; j++)
                                {
                                    TriangleInShellComponent triangle = component.GetTriangle(j);
                                    WPFMesh.TriangleIndices.Add(triangle.VertexIndex0);
                                    XYZ p = inverse.OfPoint(component.GetVertex(triangle.VertexIndex0));
                                    points[triangle.VertexIndex0] = new Point3D(p.X, p.Y, p.Z);

                                    WPFMesh.TriangleIndices.Add(triangle.VertexIndex1);
                                    p = inverse.OfPoint(component.GetVertex(triangle.VertexIndex1));
                                    points[triangle.VertexIndex1] = new Point3D(p.X, p.Y, p.Z);

                                    WPFMesh.TriangleIndices.Add(triangle.VertexIndex2);
                                    p = inverse.OfPoint(component.GetVertex(triangle.VertexIndex2));
                                    points[triangle.VertexIndex2] = new Point3D(p.X, p.Y, p.Z);
                                }
                                WPFMesh.Positions = new Point3DCollection(points);
                                if (this._territory.IntersectsWith(WPFMesh.Bounds))
                                {
                                    GeometryModel3D geometryModel3D = new GeometryModel3D(WPFMesh, material);
                                    this.WPFGeometries.Add(geometryModel3D);
                                    solidNum++;
                                }
                                else
                                {
                                    outOfTerritory++;
                                }
                            }
                            catch (Exception e)
                            {
                                failedAttempt++;
                                reporter.AppendLine(string.Format("{0}: Failed to parse Solid:\n {1}", failedAttempt.ToString(), e.Report()));
                            }
                        }
                    }
                    else
                    {
                        MessageBox.Show("Solid not valid for tessellation!");
                    }
                }
            }
            catch (Exception e)
            {
                failedAttempt++;
                reporter.AppendLine(string.Format("{0}: Failed to parse S0lid:\n {1}", failedAttempt.ToString(), e.Report()));
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created
        /// </summary>
        public void SimplifyAndMergeFaces()
        {
            int noTriangle = _geom.TriangleCount;
            int noVertices = _geom.VertexCount;

            for (int ef = 0; ef < noTriangle; ++ef)
            {
                TriangleInShellComponent f = _geom.GetTriangle(ef);
                IList <int> vertIndex      = new List <int>();
                vertIndex.Add(f.VertexIndex0);
                vertIndex.Add(f.VertexIndex1);
                vertIndex.Add(f.VertexIndex2);

                IndexFace intF = new IndexFace(vertIndex);
                facesColl.Add(ef, intF);     // Keep faces in a dictionary and assigns ID
                SortVertAndFaces(f.VertexIndex0, ef);
                SortVertAndFaces(f.VertexIndex1, ef);
                SortVertAndFaces(f.VertexIndex2, ef);
            }

            // After the above, we have a sorted polyhedron vertices that contains hashset of faces it belongs to
            // Loop through the dictionary to merge faces that have the same normal (on the same plane)
            foreach (KeyValuePair <int, HashSet <int> > dictItem in sortedFVert)
            {
                IEqualityComparer <XYZ>       normalComparer     = new vectorCompare();
                Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer);
                List <int> fIDList;

                foreach (int fID in dictItem.Value)
                {
                    IndexFace f = facesColl[fID];

                    if (!faceSortedByNormal.TryGetValue(f.normal, out fIDList))
                    {
                        fIDList = new List <int>();
                        fIDList.Add(fID);
                        faceSortedByNormal.Add(f.normal, fIDList);
                    }
                    else
                    {
                        if (!fIDList.Contains(fID))
                        {
                            fIDList.Add(fID);
                        }
                    }
                }

                foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal)
                {
                    List <int> mergedFaceList = null;
                    if (fListDict.Value.Count > 1)
                    {
                        TryMergeFaces(fListDict.Value, out mergedFaceList);
                        if (mergedFaceList != null && mergedFaceList.Count > 0)
                        {
                            // insert only new face indexes as the mergedlist from different vertices can be duplicated
                            foreach (int fIdx in mergedFaceList)
                            {
                                if (!_mergedFaceList.Contains(fIdx))
                                {
                                    _mergedFaceList.Add(fIdx);
                                }
                            }
                        }
                    }
                    else
                    if (!_mergedFaceList.Contains(fListDict.Value[0]))
                    {
                        _mergedFaceList.Add(fListDict.Value[0]); // No pair face, add it into the mergedList
                    }
                }
            }
        }
Exemplo n.º 9
0
        /*
         * void f()
         * {
         * var cx, cy, cz, volume, v, i, x1, y1, z1, x2, y2, z2, x3, y3, z3;
         * volume = 0;
         * cx = 0; cy = 0; cz = 0;
         * // Assuming vertices are in vertX[i], vertY[i], and vertZ[i]
         * // and faces are faces[i, j] where the first index indicates the
         * // face and the second index indicates the vertex of that face
         * // The value in the faces array is an index into the vertex array
         * i = 0;
         * repeat (numFaces) {
         *  x1 = vertX[faces[i, 0]]; y1 = vertY[faces[i, 0]]; z1 = vertZ[faces[i, 0]];
         *  x2 = vertX[faces[i, 1]]; y2 = vertY[faces[i, 1]]; z2 = vertZ[faces[i, 1]];
         *  x3 = vertX[faces[i, 2]]; y3 = vertY[faces[i, 2]]; z3 = vertZ[faces[i, 2]];
         *  v = x1*(y2*z3 - y3*z2) + y1*(z2*x3 - z3*x2) + z1*(x2*y3 - x3*y2);
         *  volume += v;
         *  cx += (x1 + x2 + x3)*v;
         *  cy += (y1 + y2 + y3)*v;
         *  cz += (z1 + z2 + z3)*v;
         *  i += 1;
         * }
         * // Set centroid coordinates to their final value
         * cx /= 4 * volume;
         * cy /= 4 * volume;
         * cz /= 4 * volume;
         * // And, just in case you want to know the total volume of the model:
         * volume /= 6;
         * }
         */

        CentroidVolume GetCentroid(Solid solid)
        {
            CentroidVolume cv = new CentroidVolume();
            double         v;
            XYZ            v0, v1, v2;

            SolidOrShellTessellationControls controls
                = new SolidOrShellTessellationControls();

            controls.LevelOfDetail = 0;

            TriangulatedSolidOrShell triangulation = null;

            try
            {
                triangulation
                    = SolidUtils.TessellateSolidOrShell(
                          solid, controls);
            }
            catch (Autodesk.Revit.Exceptions
                   .InvalidOperationException)
            {
                return(null);
            }

            int n = triangulation.ShellComponentCount;

            for (int i = 0; i < n; ++i)
            {
                TriangulatedShellComponent component
                    = triangulation.GetShellComponent(i);

                int m = component.TriangleCount;

                for (int j = 0; j < m; ++j)
                {
                    TriangleInShellComponent t
                        = component.GetTriangle(j);

                    v0 = component.GetVertex(t.VertexIndex0);
                    v1 = component.GetVertex(t.VertexIndex1);
                    v2 = component.GetVertex(t.VertexIndex2);

                    v = v0.X * (v1.Y * v2.Z - v2.Y * v1.Z)
                        + v0.Y * (v1.Z * v2.X - v2.Z * v1.X)
                        + v0.Z * (v1.X * v2.Y - v2.X * v1.Y);

                    cv.Centroid += v * (v0 + v1 + v2);
                    cv.Volume   += v;
                }
            }

            // Set centroid coordinates to their final value

            cv.Centroid /= 4 * cv.Volume;

            XYZ diffCentroid = cv.Centroid
                               - solid.ComputeCentroid();

            Debug.Assert(0.6 > diffCentroid.GetLength(),
                         "expected centroid approximation to be "
                         + "similar to solid ComputeCentroid result");

            // And, just in case you want to know
            // the total volume of the model:

            cv.Volume /= 6;

            double diffVolume = cv.Volume - solid.Volume;

            Debug.Assert(0.3 > Math.Abs(
                             diffVolume / cv.Volume),
                         "expected volume approximation to be "
                         + "similar to solid Volume property value");

            return(cv);
        }