Module CellComplex

Expand source code
# Copyright (C) 2024
# Wassim Jabi <wassim.jabi@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with
# this program. If not, see <https://www.gnu.org/licenses/>.

import topologic
import math
import os
from topologicpy.Topology import Topology
import warnings

try:
    import numpy as np
except:
    print("CellComplex - Installing required numpy library.")
    try:
        os.system("pip install numpy")
    except:
        os.system("pip install numpy --user")
    try:
        import numpy as np
        print("CellComplex - numpy library installed correctly.")
    except:
        warnings.warn("CellComplex - Error: Could not import numpy.")
try:
    from scipy.spatial import Delaunay
    from scipy.spatial import Voronoi
except:
    print("CellComplex - Install required scipy library.")
    try:
        os.system("pip install scipy")
    except:
        os.system("pip install scipy --user")
    try:
        from scipy.spatial import Delaunay
        from scipy.spatial import Voronoi
    except:
        warnings.warn("CellComplex - Error: Could not import scipy.")

class CellComplex(Topology):
    @staticmethod
    def Box(origin: topologic.Vertex = None,
            width: float = 1.0, length: float = 1.0, height: float = 1.0,
            uSides: int = 2, vSides: int = 2, wSides: int = 2,
            direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a box with internal cells.

        Parameters
        ----------
        origin : topologic.Vertex , optional
            The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
        width : float , optional
            The width of the box. The default is 1.
        length : float , optional
            The length of the box. The default is 1.
        height : float , optional
            The height of the box.
        uSides : int , optional
            The number of sides along the width. The default is 1.
        vSides : int, optional
            The number of sides along the length. The default is 1.
        wSides : int , optional
            The number of sides along the height. The default is 1.
        direction : list , optional
            The vector representing the up direction of the box. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the box. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created box.

        """
        return CellComplex.Prism(origin=origin,
                                 width=width, length=length, height=height,
                                 uSides=uSides, vSides=vSides, wSides=wSides,
                                 direction=direction, placement=placement, tolerance=tolerance)
    
    @staticmethod
    def ByCells(cells: list, tolerance: float = 0.0001, silent: bool = False) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the input cells.

        Parameters
        ----------
        cells : list
            The list of input cells.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology

        if not isinstance(cells, list):
            if not silent:
                print("CellComplex.ByCells - Error: The input cells parameter is not a valid list. Returning None.")
            return None
        cells = [x for x in cells if isinstance(x, topologic.Cell)]
        if len(cells) < 1:
            if not silent:
                print("CellComplex.ByCells - Error: The input cells parameter does not contain any valid cells. Returning None.")
            return None
        cellComplex = None
        if len(cells) == 1:
            return topologic.CellComplex.ByCells(cells)
        else:
            try:
                cellComplex = topologic.CellComplex.ByCells(cells)
            except:
                topA = cells[0]
                topB = Cluster.ByTopologies(cells[1:])
                cellComplex = Topology.Merge(topA, topB, tranDict=False, tolerance=tolerance)
        
        if not isinstance(cellComplex, topologic.CellComplex):
            if not silent:
                print("CellComplex.ByCells - Warning: Could not create a CellComplex. Returning object of type topologic.Cluster instead of topologic.CellComplex.")
            return Cluster.ByTopologies(cells)
        else:
            temp_cells = CellComplex.Cells(cellComplex)
            if not isinstance(temp_cells, list):
                if not silent:
                    print("CellComplex.ByCells - Error: The resulting object does not contain any cells. Returning None.")
                return None
            elif len(temp_cells) < 1:
                if silent:
                    print("CellComplex.ByCells - Error: Could not create a CellComplex. Returning None.")
                return None
            elif len(temp_cells) == 1:
                if not silent:
                    print("CellComplex.ByCells - Warning: Resulting object contains only one cell. Returning object of type topologic.Cell instead of topologic.CellComplex.")
                return(temp_cells[0])
        return cellComplex
    
    @staticmethod
    def ByCellsCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the cells within the input cluster.

        Parameters
        ----------
        cluster : topologic.Cluster
            The input cluster of cells.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(cluster, topologic.Cluster):
            print("CellComplex.ByCellsCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        cells = []
        _ = cluster.Cells(None, cells)
        return CellComplex.ByCells(cells, tolerance)

    @staticmethod
    def ByFaces(faces: list, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the input faces.

        Parameters
        ----------
        faces : topologic.Face
            The input faces.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(faces, list):
            print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
            return None
        faces = [x for x in faces if isinstance(x, topologic.Face)]
        if len(faces) < 1:
            print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
            return None
        try:
            cellComplex = topologic.CellComplex.ByFaces(faces, tolerance, False)
        except:
            cellComplex = None
        if not cellComplex:
            print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
            cellComplex = faces[0]
            for i in range(1,len(faces)):
                newCellComplex = None
                try:
                    newCellComplex = cellComplex.Merge(faces[i], False, tolerance)
                except:
                    print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
                if newCellComplex:
                    cellComplex = newCellComplex
            if cellComplex.Type() != 64: #64 is the type of a CellComplex
                print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
                if cellComplex.Type() > 64:
                    returnCellComplexes = []
                    _ = cellComplex.CellComplexes(None, returnCellComplexes)
                    if len(returnCellComplexes) > 0:
                        return returnCellComplexes[0]
                    else:
                        print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
                        return None
                else:
                    print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
                    return None
        else:
            return cellComplex
    
    @staticmethod
    def ByFacesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the faces within the input cluster.

        Parameters
        ----------
        cluster : topologic.Cluster
            The input cluster of faces.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(cluster, topologic.Cluster):
            print("CellComplex.ByFacesCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        faces = []
        _ = cluster.Faces(None, faces)
        return CellComplex.ByFaces(faces, tolerance)

    @staticmethod
    def ByWires(wires: list, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by lofting through the input wires.

        Parameters
        ----------
        wires : list
            The input list of wires. The list should contain a minimum of two wires. All wires must have the same number of edges.
        triangulate : bool , optional
            If set to True, the faces will be triangulated. The default is True.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology

        if not isinstance(wires, list):
            print("CellComplex.ByFaces - Error: The input wires parameter is not a valid list. Returning None.")
            return None
        wires = [x for x in wires if isinstance(x, topologic.Wire)]
        if len(wires) < 2:
            print("CellComplex.ByWires - Error: The input wires parameter contains less than two valid wires. Returning None.")
            return None
        faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
        if triangulate == True:
            triangles = []
            for face in faces:
                if len(Topology.Vertices(face)) > 3:
                    triangles += Face.Triangulate(face, tolerance=tolerance)
                else:
                    triangles += [face]
            faces = triangles
        for i in range(len(wires)-1):
            wire1 = wires[i]
            wire2 = wires[i+1]
            f = Face.ByWire(wire2, tolerance=tolerance)
            if triangulate == True:
                if len(Topology.Vertices(face)) > 3:
                    triangles = Face.Triangulate(face, tolerance=tolerance)
                else:
                    triangles = [face]
                faces += triangles
            else:
                faces.append(f)
            w1_edges = []
            _ = wire1.Edges(None, w1_edges)
            w2_edges = []
            _ = wire2.Edges(None, w2_edges)
            if len(w1_edges) != len(w2_edges):
                print("CellComplex.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
                return None
            for j in range (len(w1_edges)):
                e1 = w1_edges[j]
                e2 = w2_edges[j]
                e3 = None
                e4 = None
                try:
                    e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
                except:
                    try:
                        e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                        f = Face.ByExternalBoundary(Wire.ByEdges([e1, e2, e4], tolerance=tolerance))
                        if triangulate == True:
                            if len(Topology.Vertices(face)) > 3:
                                triangles = Face.Triangulate(face, tolerance=tolerance)
                            else:
                                triangles = [face]
                            faces += triangles
                        else:
                            faces.append(f)
                    except:
                        pass
                try:
                    e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                except:
                    try:
                        e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
                        f = Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance)
                        if triangulate == True:
                            if len(Topology.Vertices(face)) > 3:
                                triangles = Face.Triangulate(face, tolerance=tolerance)
                            else:
                                triangles = [face]
                            faces += triangles
                        else:
                            faces.append(f)
                    except:
                        pass
                if e3 and e4:
                    if triangulate == True:
                        e5 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                        faces.append(Face.ByWire(Wire.ByEdges([e1, e5, e4], tolerance=tolerance), tolerance=tolerance))
                        faces.append(Face.ByWire(Wire.ByEdges([e2, e5, e3], tolerance=tolerance), tolerance=tolerance))
                    else:
                        f = Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance) or Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance)
                        if f:
                            faces.append(f)

                elif e3:
                    faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2], tolerance=tolerance), tolerance=tolerance))
                elif e4:
                    faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2], tolerance=tolerance), tolerance=tolerance))
        return CellComplex.ByFaces(faces, tolerance=tolerance)

    @staticmethod
    def ByWiresCluster(cluster: topologic.Cluster, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by lofting through the wires in the input cluster.

        Parameters
        ----------
        cluster : topologic.Cluster
            The input cluster of wires.
        triangulate : bool , optional
            If set to True, the faces will be triangulated. The default is True.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(cluster, topologic.Cluster):
            print("CellComplex.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        wires = []
        _ = cluster.Wires(None, wires)
        return CellComplex.ByWires(wires, triangulate=triangulate, tolerance=tolerance)

    @staticmethod
    def Cells(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the cells of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of cells.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Cells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        cells = []
        _ = cellComplex.Cells(None, cells)
        return cells

    @staticmethod
    def Decompose(cellComplex: topologic.CellComplex, tiltAngle: float = 10.0, tolerance: float = 0.0001) -> dict:
        """
        Decomposes the input cellComplex into its logical components. This method assumes that the positive Z direction is UP.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            the input cellComplex.
        tiltAngle : float , optional
            The threshold tilt angle in degrees to determine if a face is vertical, horizontal, or tilted. The tilt angle is measured from the nearest cardinal direction. The default is 10.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        dictionary
            A dictionary with the following keys and values:
            1. "cells": list of cells
            2. "externalVerticalFaces": list of external vertical faces
            3. "internalVerticalFaces": list of internal vertical faces
            4. "topHorizontalFaces": list of top horizontal faces
            5. "bottomHorizontalFaces": list of bottom horizontal faces
            6. "internalHorizontalFaces": list of internal horizontal faces
            7. "externalInclinedFaces": list of external inclined faces
            8. "internalInclinedFaces": list of internal inclined faces
            9. "externalVerticalApertures": list of external vertical apertures
            10. "internalVerticalApertures": list of internal vertical apertures
            11. "topHorizontalApertures": list of top horizontal apertures
            12. "bottomHorizontalApertures": list of bottom horizontal apertures
            13. "internalHorizontalApertures": list of internal horizontal apertures
            14. "externalInclinedApertures": list of external inclined apertures
            15. "internalInclinedApertures": list of internal inclined apertures

        """
        from topologicpy.Face import Face
        from topologicpy.Vector import Vector
        from topologicpy.Aperture import Aperture
        from topologicpy.Topology import Topology

        def angleCode(f, up, tiltAngle):
            dirA = Face.NormalAtParameters(f)
            ang = round(Vector.Angle(dirA, up), 2)
            if abs(ang - 90) < tiltAngle:
                code = 0
            elif abs(ang) < tiltAngle:
                code = 1
            elif abs(ang - 180) < tiltAngle:
                code = 2
            else:
                code = 3
            return code

        def getApertures(topology):
            apertures = []
            apTopologies = []
            apertures = Topology.Apertures(topology)
            for aperture in apertures:
                apTopologies.append(Aperture.Topology(aperture))
            return apTopologies

        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Decompose - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        externalVerticalFaces = []
        internalVerticalFaces = []
        topHorizontalFaces = []
        bottomHorizontalFaces = []
        internalHorizontalFaces = []
        externalInclinedFaces = []
        internalInclinedFaces = []
        externalVerticalApertures = []
        internalVerticalApertures = []
        topHorizontalApertures = []
        bottomHorizontalApertures = []
        internalHorizontalApertures = []
        externalInclinedApertures = []
        internalInclinedApertures = []
        tiltAngle = abs(tiltAngle)
        faces = CellComplex.Faces(cellComplex)
        zList = []
        for f in faces:
            zList.append(f.Centroid().Z())
        zMin = min(zList)
        zMax = max(zList)
        up = [0, 0, 1]
        for aFace in faces:
            aCode = angleCode(aFace, up, tiltAngle)
            cells = []
            aFace.Cells(cellComplex, cells)
            n = len(cells)
            if aCode == 0:
                if n == 1:
                    externalVerticalFaces.append(aFace)
                    externalVerticalApertures += getApertures(aFace)
                else:
                    internalVerticalFaces.append(aFace)
                    internalVerticalApertures += getApertures(aFace)
            elif aCode == 1:
                if n == 1:
                    if abs(aFace.Centroid().Z() - zMin) < tolerance:
                        bottomHorizontalFaces.append(aFace)
                        bottomHorizontalApertures += getApertures(aFace)
                    else:
                        topHorizontalFaces.append(aFace)
                        topHorizontalApertures += getApertures(aFace)
                else:
                    internalHorizontalFaces.append(aFace)
                    internalHorizontalApertures += getApertures(aFace)
            elif aCode == 2:
                if n == 1:
                    if abs(aFace.Centroid().Z() - zMax) < tolerance:
                        topHorizontalFaces.append(aFace)
                        topHorizontalApertures += getApertures(aFace)
                    else:
                        bottomHorizontalFaces.append(aFace)
                        bottomHorizontalApertures += getApertures(aFace)
                else:
                    internalHorizontalFaces.append(aFace)
                    internalHorizontalApertures += getApertures(aFace)
            elif aCode == 3:
                if n == 1:
                    externalInclinedFaces.append(aFace)
                    externalInclinedApertures += getApertures(aFace)
                else:
                    internalInclinedFaces.append(aFace)
                    internalInclinedApertures += getApertures(aFace)
        
        cells = Topology.Cells(cellComplex)
        d = {
            "cells" : cells,
            "externalVerticalFaces" : externalVerticalFaces,
            "internalVerticalFaces" : internalVerticalFaces,
            "topHorizontalFaces" : topHorizontalFaces,
            "bottomHorizontalFaces" : bottomHorizontalFaces,
            "internalHorizontalFaces" : internalHorizontalFaces,
            "externalInclinedFaces" : externalInclinedFaces,
            "internalInclinedFaces" : internalInclinedFaces,
            "externalVerticalApertures" : externalVerticalApertures,
            "internalVerticalApertures" : internalVerticalApertures,
            "topHorizontalApertures" : topHorizontalApertures,
            "bottomHorizontalApertures" : bottomHorizontalApertures,
            "internalHorizontalApertures" : internalHorizontalApertures,
            "externalInclinedApertures" : externalInclinedApertures,
            "internalInclinedApertures" : internalInclinedApertures
            }
        return d
    
    @staticmethod
    def Delaunay(vertices: list = None, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Triangulates the input vertices based on the Delaunay method. See https://en.wikipedia.org/wiki/Delaunay_triangulation.

        Parameters
        ----------
        vertices: list , optional 
            The input list of vertices to use for delaunay triangulation. If set to None, the algorithm uses the vertices of the input cell parameter.
            if both are set to none, a unit cube centered around the origin is used.
        tolerance : float , optional
            the desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created delaunay cellComplex.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        from scipy.spatial import Delaunay as SCIDelaunay
        import numpy as np

        if not isinstance(vertices, list):
            cell = Cell.Prism()
            vertices = Topology.Vertices(cell)
        
        vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
        if len(vertices) < 3:
            print("CellComplex/Delaunay - Error: The input vertices parameter does not contain enough valid vertices. Returning None.")
            return None
        # Get the vertices of the input cell
        points = np.array([Vertex.Coordinates(v) for v in vertices])
        # Compute Delaunay triangulation
        triangulation = SCIDelaunay(points, furthest_site=False)

        faces = []
        for simplex in triangulation.simplices:
            tetrahedron_vertices = points[simplex]
            verts = [Vertex.ByCoordinates(list(coord)) for coord in tetrahedron_vertices]
            tri1 = [verts[0], verts[1], verts[2], verts[0]]
            tri2 = [verts[0], verts[2], verts[3], verts[0]]
            tri3 = [verts[0], verts[1], verts[3], verts[0]]
            tri4 = [verts[1], verts[2], verts[3], verts[1]]
            f1 = Face.ByVertices(tri1)
            f2 = Face.ByVertices(tri2)
            f3 = Face.ByVertices(tri3)
            f4 = Face.ByVertices(tri4)
            faces.append(f1)
            faces.append(f2)
            faces.append(f3)
            faces.append(f4)
        cc = Topology.RemoveCoplanarFaces(CellComplex.ByFaces(faces, tolerance=tolerance))
        faces = [Topology.RemoveCollinearEdges(f) for f in Topology.Faces(cc)]
        cc = CellComplex.ByFaces(faces)
        return cc
    
    @staticmethod
    def Edges(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the edges of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of edges.

        """ 
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Edges - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        edges = []
        _ = cellComplex.Edges(None, edges)
        return edges

    @staticmethod
    def ExternalBoundary(cellComplex: topologic.CellComplex) -> topologic.Cell:
        """
        Returns the external boundary (cell) of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        topologic.Cell
            The external boundary of the input cellComplex.

        """
        return cellComplex.ExternalBoundary()

    @staticmethod
    def ExternalFaces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the external faces of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of external faces.

        """
        from topologicpy.Cell import Cell
        cell = cellComplex.ExternalBoundary()
        return Cell.Faces(cell)

    @staticmethod
    def Faces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the faces of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of faces.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Faces - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        faces = []
        _ = cellComplex.Faces(None, faces)
        return faces

    @staticmethod
    def InternalFaces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the internal boundaries (faces) of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of internal faces of the input cellComplex.

        """
        faces = []
        _ = cellComplex.InternalBoundaries(faces)
        return faces
    
    @staticmethod
    def NonManifoldFaces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the non-manifold faces of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of non-manifold faces of the input cellComplex.

        """
        faces = []
        _ = cellComplex.NonManifoldFaces(faces)
        return faces
    
    @staticmethod
    def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Description
        ----------
        Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.

        Parameters
        ----------
        origin : topologic.Vertex , optional
            The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the octahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the octahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the octahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created octahedron.

        """
        
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not isinstance(origin, topologic.Vertex):
            print("CellComplex.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        
        vb1 = Vertex.ByCoordinates(-0.5,0,0)
        vb2 = Vertex.ByCoordinates(0,-0.5,0)
        vb3 = Vertex.ByCoordinates(0.5,0,0)
        vb4 = Vertex.ByCoordinates(0,0.5,0)
        top = Vertex.ByCoordinates(0, 0, 0.5)
        bottom = Vertex.ByCoordinates(0, 0, -0.5)
        f1 = Face.ByVertices([top,vb1,vb2])
        f2 = Face.ByVertices([top,vb2,vb3])
        f3 = Face.ByVertices([top,vb3,vb4])
        f4 = Face.ByVertices([top,vb4,vb1])
        f5 = Face.ByVertices([bottom,vb1,vb2])
        f6 = Face.ByVertices([bottom,vb2,vb3])
        f7 = Face.ByVertices([bottom,vb3,vb4])
        f8 = Face.ByVertices([bottom,vb4,vb1])
        f9 = Face.ByVertices([vb1,vb2,vb3,vb4])

        octahedron = CellComplex.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9], tolerance=tolerance)
        octahedron = Topology.Scale(octahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
        if placement == "bottom":
            octahedron = Topology.Translate(octahedron, 0, 0, radius)
        elif placement == "lowerleft":
            octahedron = Topology.Translate(octahedron, radius, radius, radius)
        octahedron = Topology.Orient(octahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        octahedron = Topology.Place(octahedron, originA=Vertex.Origin(), originB=origin)
        return octahedron
    
    @staticmethod
    def Prism(origin: topologic.Vertex = None,
              width: float = 1.0, length: float = 1.0, height: float = 1.0,
              uSides: int = 2, vSides: int = 2, wSides: int = 2,
              direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a prismatic cellComplex with internal cells.

        Parameters
        ----------
        origin : topologic.Vertex , optional
            The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
        width : float , optional
            The width of the prism. The default is 1.
        length : float , optional
            The length of the prism. The default is 1.
        height : float , optional
            The height of the prism.
        uSides : int , optional
            The number of sides along the width. The default is 1.
        vSides : int , optional
            The number of sides along the length. The default is 1.
        wSides : int , optional
            The number of sides along the height. The default is 1.
        direction : list , optional
            The vector representing the up direction of the prism. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the prism. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created prism.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        
        def bb(topology):
            vertices = []
            _ = topology.Vertices(None, vertices)
            x = []
            y = []
            z = []
            for aVertex in vertices:
                x.append(aVertex.X())
                y.append(aVertex.Y())
                z.append(aVertex.Z())
            minX = min(x)
            minY = min(y)
            minZ = min(z)
            maxX = max(x)
            maxY = max(y)
            maxZ = max(z)
            return [minX, minY, minZ, maxX, maxY, maxZ]
        
        def slice(topology, uSides, vSides, wSides):
            minX, minY, minZ, maxX, maxY, maxZ = bb(topology)
            centroid = Vertex.ByCoordinates(minX+(maxX-minX)*0.5, minY+(maxY-minY)*0.5, minZ+(maxZ-minZ)*0.5)
            wOrigin = Vertex.ByCoordinates(Vertex.X(centroid), Vertex.Y(centroid), minZ)
            wFace = Face.Rectangle(origin=wOrigin, width=(maxX-minX)*1.1, length=(maxY-minY)*1.1)
            wFaces = []
            wOffset = (maxZ-minZ)/wSides
            for i in range(wSides-1):
                wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
            uOrigin = Vertex.ByCoordinates(minX, Vertex.Y(centroid), Vertex.Z(centroid))
            uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-minZ)*1.1, length=(maxY-minY)*1.1, direction=[1,0,0])
            uFaces = []
            uOffset = (maxX-minX)/uSides
            for i in range(uSides-1):
                uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
            vOrigin = Vertex.ByCoordinates(Vertex.X(centroid), minY, Vertex.Z(centroid))
            vFace = Face.Rectangle(origin=vOrigin, width=(maxX-minX)*1.1, length=(maxZ-minZ)*1.1, direction=[0,1,0])
            vFaces = []
            vOffset = (maxY-minY)/vSides
            for i in range(vSides-1):
                vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
            all_faces = uFaces+vFaces+wFaces
            if len(all_faces) > 0:
                f_clus = Cluster.ByTopologies(uFaces+vFaces+wFaces)
                return Topology.Slice(topology, f_clus, tolerance=tolerance)
            else:
                return topologic.CellComplex.ByCells([topology])
        if not isinstance(origin, topologic.Vertex):
            origin = Vertex.ByCoordinates(0, 0, 0)

        c = Cell.Prism(origin=origin, width=width, length=length, height=height, uSides=1, vSides=1, wSides=1, placement=placement, tolerance=tolerance)
        prism = slice(c, uSides=uSides, vSides=vSides, wSides=wSides)
        if prism:
            prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction)
            return prism
        else:
            print("CellComplex.Prism - Error: Could not create a prism. Returning None.")
            return None

    @staticmethod
    def RemoveCollinearEdges(cellComplex: topologic.CellComplex, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
        """
        Removes any collinear edges in the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.
        angTolerance : float , optional
            The desired angular tolerance. The default is 0.1.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellComplex without any collinear edges.

        """
        from topologicpy.Cell import Cell

        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.RemoveCollinearEdges - Error: The input cellComplex parameter is not a valid cellComplex. Returning None.")
            return None
        cells = CellComplex.Cells(cellComplex)
        clean_cells = []
        for cell in cells:
            clean_cells.append(Cell.RemoveCollinearEdges(cell, angTolerance=angTolerance, tolerance=tolerance))
        return CellComplex.ByCells(clean_cells, tolerance=tolerance)
    
    @staticmethod
    def Shells(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the shells of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of shells.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Shells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        shells = []
        _ = cellComplex.Shells(None, shells)
        return shells

    @staticmethod
    def Vertices(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the vertices of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of vertices.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Vertices - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        vertices = []
        _ = cellComplex.Vertices(None, vertices)
        return vertices

    @staticmethod
    def Volume(cellComplex: topologic.CellComplex, mantissa: int = 6) -> float:
        """
        Returns the volume of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.
        manitssa: int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        float
            The volume of the input cellComplex.

        """
        from topologicpy.Cell import Cell
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Volume - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        cells = CellComplex.Cells(cellComplex)
        volume = 0
        for cell in cells:
            volume = Cell.Volume(cell)
            if not volume == None:
                volume += Cell.Volume(cell)
        return round(volume, mantissa)
    
    @staticmethod
    def Voronoi(vertices: list = None, cell: topologic.Cell = None, tolerance: float = 0.0001):
        """
        Partitions the input cell based on the Voronoi method. See https://en.wikipedia.org/wiki/Voronoi_diagram.

        Parameters
        ----------
        vertices: list , optional 
            The input list of vertices to use for voronoi partitioning. If set to None, the algorithm uses the vertices of the input cell parameter.
            if both are set to none, a unit cube centered around the origin is used.
        cell : topologic.Cell , optional
            The input bounding cell. If set to None, an axes-aligned bounding cell is created from the list of vertices. The default is None.
        tolerance : float , optional
            the desired tolerance. The default is 0.0001.
        

        Returns
        -------
        topologic.CellComplex
            The created voronoi cellComplex.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        from scipy.spatial import Voronoi as SCIVoronoi
        import numpy as np

        def fracture_with_voronoi(points):
            # Compute Voronoi tessellation
            vor = SCIVoronoi(points)
            verts = []
            faces = []
            for v in vor.vertices:
                verts.append(Vertex.ByCoordinates(list(v)))
            for region in vor.ridge_vertices:
                temp_list = []
                if -1 not in region and len(region) > 0:
                    for item in region:
                        temp_list.append(verts[item])
                    f = Face.ByVertices(temp_list)
                    if isinstance(f, topologic.Face):
                        faces.append(f)
            if len(faces) < 1:
                return None
            return Cluster.ByTopologies(faces)
        
        if cell == None:
            if not isinstance(vertices, list):
                cell = Cell.Prism(uSides=2, vSides=2, wSides=2)
                vertices = Topology.Vertices(cell)
                vertices.append(Vertex.Origin())
            else:
                vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
                if len(vertices) < 1:
                    print("CellComplex.Voronoi - Error: The input vertices parameter does not contain any valid vertices. Returning None.")
                    return None
                cell = Topology.BoundingBox(Cluster.ByTopologies(vertices))
        if not isinstance(vertices, list):
            if not isinstance(cell, topologic.Cell):
                cell = Cell.Prism()
                vertices = Topology.Vertices(cell)
            else:
                vertices = Topology.Vertices(cell)
        else:
            vertices += Topology.Vertices(cell)
        vertices = [v for v in vertices if (Vertex.IsInternal(v, cell) or not Vertex.Index(v, Topology.Vertices(cell)) == None)]
        if len(vertices) < 1:
            print("CellComplex.Voronoi - Error: The input vertices parame ter does not contain any vertices that are inside the input cell parameter. Returning None.")
            return None
        voronoi_points = np.array([Vertex.Coordinates(v) for v in vertices])
        cluster = fracture_with_voronoi(voronoi_points)
        if cluster == None:
            print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
            return None
        cellComplex = Topology.Slice(cell, cluster)
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
            return None
        return cellComplex
    
    @staticmethod
    def Wires(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the wires of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of wires.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Wires - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        wires = []
        _ = cellComplex.Wires(None, wires)
        return wires

Classes

class CellComplex
Expand source code
class CellComplex(Topology):
    @staticmethod
    def Box(origin: topologic.Vertex = None,
            width: float = 1.0, length: float = 1.0, height: float = 1.0,
            uSides: int = 2, vSides: int = 2, wSides: int = 2,
            direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a box with internal cells.

        Parameters
        ----------
        origin : topologic.Vertex , optional
            The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
        width : float , optional
            The width of the box. The default is 1.
        length : float , optional
            The length of the box. The default is 1.
        height : float , optional
            The height of the box.
        uSides : int , optional
            The number of sides along the width. The default is 1.
        vSides : int, optional
            The number of sides along the length. The default is 1.
        wSides : int , optional
            The number of sides along the height. The default is 1.
        direction : list , optional
            The vector representing the up direction of the box. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the box. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created box.

        """
        return CellComplex.Prism(origin=origin,
                                 width=width, length=length, height=height,
                                 uSides=uSides, vSides=vSides, wSides=wSides,
                                 direction=direction, placement=placement, tolerance=tolerance)
    
    @staticmethod
    def ByCells(cells: list, tolerance: float = 0.0001, silent: bool = False) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the input cells.

        Parameters
        ----------
        cells : list
            The list of input cells.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology

        if not isinstance(cells, list):
            if not silent:
                print("CellComplex.ByCells - Error: The input cells parameter is not a valid list. Returning None.")
            return None
        cells = [x for x in cells if isinstance(x, topologic.Cell)]
        if len(cells) < 1:
            if not silent:
                print("CellComplex.ByCells - Error: The input cells parameter does not contain any valid cells. Returning None.")
            return None
        cellComplex = None
        if len(cells) == 1:
            return topologic.CellComplex.ByCells(cells)
        else:
            try:
                cellComplex = topologic.CellComplex.ByCells(cells)
            except:
                topA = cells[0]
                topB = Cluster.ByTopologies(cells[1:])
                cellComplex = Topology.Merge(topA, topB, tranDict=False, tolerance=tolerance)
        
        if not isinstance(cellComplex, topologic.CellComplex):
            if not silent:
                print("CellComplex.ByCells - Warning: Could not create a CellComplex. Returning object of type topologic.Cluster instead of topologic.CellComplex.")
            return Cluster.ByTopologies(cells)
        else:
            temp_cells = CellComplex.Cells(cellComplex)
            if not isinstance(temp_cells, list):
                if not silent:
                    print("CellComplex.ByCells - Error: The resulting object does not contain any cells. Returning None.")
                return None
            elif len(temp_cells) < 1:
                if silent:
                    print("CellComplex.ByCells - Error: Could not create a CellComplex. Returning None.")
                return None
            elif len(temp_cells) == 1:
                if not silent:
                    print("CellComplex.ByCells - Warning: Resulting object contains only one cell. Returning object of type topologic.Cell instead of topologic.CellComplex.")
                return(temp_cells[0])
        return cellComplex
    
    @staticmethod
    def ByCellsCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the cells within the input cluster.

        Parameters
        ----------
        cluster : topologic.Cluster
            The input cluster of cells.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(cluster, topologic.Cluster):
            print("CellComplex.ByCellsCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        cells = []
        _ = cluster.Cells(None, cells)
        return CellComplex.ByCells(cells, tolerance)

    @staticmethod
    def ByFaces(faces: list, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the input faces.

        Parameters
        ----------
        faces : topologic.Face
            The input faces.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(faces, list):
            print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
            return None
        faces = [x for x in faces if isinstance(x, topologic.Face)]
        if len(faces) < 1:
            print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
            return None
        try:
            cellComplex = topologic.CellComplex.ByFaces(faces, tolerance, False)
        except:
            cellComplex = None
        if not cellComplex:
            print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
            cellComplex = faces[0]
            for i in range(1,len(faces)):
                newCellComplex = None
                try:
                    newCellComplex = cellComplex.Merge(faces[i], False, tolerance)
                except:
                    print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
                if newCellComplex:
                    cellComplex = newCellComplex
            if cellComplex.Type() != 64: #64 is the type of a CellComplex
                print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
                if cellComplex.Type() > 64:
                    returnCellComplexes = []
                    _ = cellComplex.CellComplexes(None, returnCellComplexes)
                    if len(returnCellComplexes) > 0:
                        return returnCellComplexes[0]
                    else:
                        print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
                        return None
                else:
                    print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
                    return None
        else:
            return cellComplex
    
    @staticmethod
    def ByFacesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by merging the faces within the input cluster.

        Parameters
        ----------
        cluster : topologic.Cluster
            The input cluster of faces.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(cluster, topologic.Cluster):
            print("CellComplex.ByFacesCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        faces = []
        _ = cluster.Faces(None, faces)
        return CellComplex.ByFaces(faces, tolerance)

    @staticmethod
    def ByWires(wires: list, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by lofting through the input wires.

        Parameters
        ----------
        wires : list
            The input list of wires. The list should contain a minimum of two wires. All wires must have the same number of edges.
        triangulate : bool , optional
            If set to True, the faces will be triangulated. The default is True.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology

        if not isinstance(wires, list):
            print("CellComplex.ByFaces - Error: The input wires parameter is not a valid list. Returning None.")
            return None
        wires = [x for x in wires if isinstance(x, topologic.Wire)]
        if len(wires) < 2:
            print("CellComplex.ByWires - Error: The input wires parameter contains less than two valid wires. Returning None.")
            return None
        faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
        if triangulate == True:
            triangles = []
            for face in faces:
                if len(Topology.Vertices(face)) > 3:
                    triangles += Face.Triangulate(face, tolerance=tolerance)
                else:
                    triangles += [face]
            faces = triangles
        for i in range(len(wires)-1):
            wire1 = wires[i]
            wire2 = wires[i+1]
            f = Face.ByWire(wire2, tolerance=tolerance)
            if triangulate == True:
                if len(Topology.Vertices(face)) > 3:
                    triangles = Face.Triangulate(face, tolerance=tolerance)
                else:
                    triangles = [face]
                faces += triangles
            else:
                faces.append(f)
            w1_edges = []
            _ = wire1.Edges(None, w1_edges)
            w2_edges = []
            _ = wire2.Edges(None, w2_edges)
            if len(w1_edges) != len(w2_edges):
                print("CellComplex.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
                return None
            for j in range (len(w1_edges)):
                e1 = w1_edges[j]
                e2 = w2_edges[j]
                e3 = None
                e4 = None
                try:
                    e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
                except:
                    try:
                        e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                        f = Face.ByExternalBoundary(Wire.ByEdges([e1, e2, e4], tolerance=tolerance))
                        if triangulate == True:
                            if len(Topology.Vertices(face)) > 3:
                                triangles = Face.Triangulate(face, tolerance=tolerance)
                            else:
                                triangles = [face]
                            faces += triangles
                        else:
                            faces.append(f)
                    except:
                        pass
                try:
                    e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                except:
                    try:
                        e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
                        f = Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance)
                        if triangulate == True:
                            if len(Topology.Vertices(face)) > 3:
                                triangles = Face.Triangulate(face, tolerance=tolerance)
                            else:
                                triangles = [face]
                            faces += triangles
                        else:
                            faces.append(f)
                    except:
                        pass
                if e3 and e4:
                    if triangulate == True:
                        e5 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                        faces.append(Face.ByWire(Wire.ByEdges([e1, e5, e4], tolerance=tolerance), tolerance=tolerance))
                        faces.append(Face.ByWire(Wire.ByEdges([e2, e5, e3], tolerance=tolerance), tolerance=tolerance))
                    else:
                        f = Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance) or Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance)
                        if f:
                            faces.append(f)

                elif e3:
                    faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2], tolerance=tolerance), tolerance=tolerance))
                elif e4:
                    faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2], tolerance=tolerance), tolerance=tolerance))
        return CellComplex.ByFaces(faces, tolerance=tolerance)

    @staticmethod
    def ByWiresCluster(cluster: topologic.Cluster, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a cellcomplex by lofting through the wires in the input cluster.

        Parameters
        ----------
        cluster : topologic.Cluster
            The input cluster of wires.
        triangulate : bool , optional
            If set to True, the faces will be triangulated. The default is True.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellcomplex.

        """

        if not isinstance(cluster, topologic.Cluster):
            print("CellComplex.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        wires = []
        _ = cluster.Wires(None, wires)
        return CellComplex.ByWires(wires, triangulate=triangulate, tolerance=tolerance)

    @staticmethod
    def Cells(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the cells of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of cells.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Cells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        cells = []
        _ = cellComplex.Cells(None, cells)
        return cells

    @staticmethod
    def Decompose(cellComplex: topologic.CellComplex, tiltAngle: float = 10.0, tolerance: float = 0.0001) -> dict:
        """
        Decomposes the input cellComplex into its logical components. This method assumes that the positive Z direction is UP.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            the input cellComplex.
        tiltAngle : float , optional
            The threshold tilt angle in degrees to determine if a face is vertical, horizontal, or tilted. The tilt angle is measured from the nearest cardinal direction. The default is 10.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        dictionary
            A dictionary with the following keys and values:
            1. "cells": list of cells
            2. "externalVerticalFaces": list of external vertical faces
            3. "internalVerticalFaces": list of internal vertical faces
            4. "topHorizontalFaces": list of top horizontal faces
            5. "bottomHorizontalFaces": list of bottom horizontal faces
            6. "internalHorizontalFaces": list of internal horizontal faces
            7. "externalInclinedFaces": list of external inclined faces
            8. "internalInclinedFaces": list of internal inclined faces
            9. "externalVerticalApertures": list of external vertical apertures
            10. "internalVerticalApertures": list of internal vertical apertures
            11. "topHorizontalApertures": list of top horizontal apertures
            12. "bottomHorizontalApertures": list of bottom horizontal apertures
            13. "internalHorizontalApertures": list of internal horizontal apertures
            14. "externalInclinedApertures": list of external inclined apertures
            15. "internalInclinedApertures": list of internal inclined apertures

        """
        from topologicpy.Face import Face
        from topologicpy.Vector import Vector
        from topologicpy.Aperture import Aperture
        from topologicpy.Topology import Topology

        def angleCode(f, up, tiltAngle):
            dirA = Face.NormalAtParameters(f)
            ang = round(Vector.Angle(dirA, up), 2)
            if abs(ang - 90) < tiltAngle:
                code = 0
            elif abs(ang) < tiltAngle:
                code = 1
            elif abs(ang - 180) < tiltAngle:
                code = 2
            else:
                code = 3
            return code

        def getApertures(topology):
            apertures = []
            apTopologies = []
            apertures = Topology.Apertures(topology)
            for aperture in apertures:
                apTopologies.append(Aperture.Topology(aperture))
            return apTopologies

        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Decompose - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        externalVerticalFaces = []
        internalVerticalFaces = []
        topHorizontalFaces = []
        bottomHorizontalFaces = []
        internalHorizontalFaces = []
        externalInclinedFaces = []
        internalInclinedFaces = []
        externalVerticalApertures = []
        internalVerticalApertures = []
        topHorizontalApertures = []
        bottomHorizontalApertures = []
        internalHorizontalApertures = []
        externalInclinedApertures = []
        internalInclinedApertures = []
        tiltAngle = abs(tiltAngle)
        faces = CellComplex.Faces(cellComplex)
        zList = []
        for f in faces:
            zList.append(f.Centroid().Z())
        zMin = min(zList)
        zMax = max(zList)
        up = [0, 0, 1]
        for aFace in faces:
            aCode = angleCode(aFace, up, tiltAngle)
            cells = []
            aFace.Cells(cellComplex, cells)
            n = len(cells)
            if aCode == 0:
                if n == 1:
                    externalVerticalFaces.append(aFace)
                    externalVerticalApertures += getApertures(aFace)
                else:
                    internalVerticalFaces.append(aFace)
                    internalVerticalApertures += getApertures(aFace)
            elif aCode == 1:
                if n == 1:
                    if abs(aFace.Centroid().Z() - zMin) < tolerance:
                        bottomHorizontalFaces.append(aFace)
                        bottomHorizontalApertures += getApertures(aFace)
                    else:
                        topHorizontalFaces.append(aFace)
                        topHorizontalApertures += getApertures(aFace)
                else:
                    internalHorizontalFaces.append(aFace)
                    internalHorizontalApertures += getApertures(aFace)
            elif aCode == 2:
                if n == 1:
                    if abs(aFace.Centroid().Z() - zMax) < tolerance:
                        topHorizontalFaces.append(aFace)
                        topHorizontalApertures += getApertures(aFace)
                    else:
                        bottomHorizontalFaces.append(aFace)
                        bottomHorizontalApertures += getApertures(aFace)
                else:
                    internalHorizontalFaces.append(aFace)
                    internalHorizontalApertures += getApertures(aFace)
            elif aCode == 3:
                if n == 1:
                    externalInclinedFaces.append(aFace)
                    externalInclinedApertures += getApertures(aFace)
                else:
                    internalInclinedFaces.append(aFace)
                    internalInclinedApertures += getApertures(aFace)
        
        cells = Topology.Cells(cellComplex)
        d = {
            "cells" : cells,
            "externalVerticalFaces" : externalVerticalFaces,
            "internalVerticalFaces" : internalVerticalFaces,
            "topHorizontalFaces" : topHorizontalFaces,
            "bottomHorizontalFaces" : bottomHorizontalFaces,
            "internalHorizontalFaces" : internalHorizontalFaces,
            "externalInclinedFaces" : externalInclinedFaces,
            "internalInclinedFaces" : internalInclinedFaces,
            "externalVerticalApertures" : externalVerticalApertures,
            "internalVerticalApertures" : internalVerticalApertures,
            "topHorizontalApertures" : topHorizontalApertures,
            "bottomHorizontalApertures" : bottomHorizontalApertures,
            "internalHorizontalApertures" : internalHorizontalApertures,
            "externalInclinedApertures" : externalInclinedApertures,
            "internalInclinedApertures" : internalInclinedApertures
            }
        return d
    
    @staticmethod
    def Delaunay(vertices: list = None, tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Triangulates the input vertices based on the Delaunay method. See https://en.wikipedia.org/wiki/Delaunay_triangulation.

        Parameters
        ----------
        vertices: list , optional 
            The input list of vertices to use for delaunay triangulation. If set to None, the algorithm uses the vertices of the input cell parameter.
            if both are set to none, a unit cube centered around the origin is used.
        tolerance : float , optional
            the desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created delaunay cellComplex.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        from scipy.spatial import Delaunay as SCIDelaunay
        import numpy as np

        if not isinstance(vertices, list):
            cell = Cell.Prism()
            vertices = Topology.Vertices(cell)
        
        vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
        if len(vertices) < 3:
            print("CellComplex/Delaunay - Error: The input vertices parameter does not contain enough valid vertices. Returning None.")
            return None
        # Get the vertices of the input cell
        points = np.array([Vertex.Coordinates(v) for v in vertices])
        # Compute Delaunay triangulation
        triangulation = SCIDelaunay(points, furthest_site=False)

        faces = []
        for simplex in triangulation.simplices:
            tetrahedron_vertices = points[simplex]
            verts = [Vertex.ByCoordinates(list(coord)) for coord in tetrahedron_vertices]
            tri1 = [verts[0], verts[1], verts[2], verts[0]]
            tri2 = [verts[0], verts[2], verts[3], verts[0]]
            tri3 = [verts[0], verts[1], verts[3], verts[0]]
            tri4 = [verts[1], verts[2], verts[3], verts[1]]
            f1 = Face.ByVertices(tri1)
            f2 = Face.ByVertices(tri2)
            f3 = Face.ByVertices(tri3)
            f4 = Face.ByVertices(tri4)
            faces.append(f1)
            faces.append(f2)
            faces.append(f3)
            faces.append(f4)
        cc = Topology.RemoveCoplanarFaces(CellComplex.ByFaces(faces, tolerance=tolerance))
        faces = [Topology.RemoveCollinearEdges(f) for f in Topology.Faces(cc)]
        cc = CellComplex.ByFaces(faces)
        return cc
    
    @staticmethod
    def Edges(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the edges of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of edges.

        """ 
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Edges - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        edges = []
        _ = cellComplex.Edges(None, edges)
        return edges

    @staticmethod
    def ExternalBoundary(cellComplex: topologic.CellComplex) -> topologic.Cell:
        """
        Returns the external boundary (cell) of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        topologic.Cell
            The external boundary of the input cellComplex.

        """
        return cellComplex.ExternalBoundary()

    @staticmethod
    def ExternalFaces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the external faces of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of external faces.

        """
        from topologicpy.Cell import Cell
        cell = cellComplex.ExternalBoundary()
        return Cell.Faces(cell)

    @staticmethod
    def Faces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the faces of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of faces.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Faces - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        faces = []
        _ = cellComplex.Faces(None, faces)
        return faces

    @staticmethod
    def InternalFaces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the internal boundaries (faces) of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of internal faces of the input cellComplex.

        """
        faces = []
        _ = cellComplex.InternalBoundaries(faces)
        return faces
    
    @staticmethod
    def NonManifoldFaces(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the non-manifold faces of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of non-manifold faces of the input cellComplex.

        """
        faces = []
        _ = cellComplex.NonManifoldFaces(faces)
        return faces
    
    @staticmethod
    def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Description
        ----------
        Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.

        Parameters
        ----------
        origin : topologic.Vertex , optional
            The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the octahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the octahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the octahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created octahedron.

        """
        
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not isinstance(origin, topologic.Vertex):
            print("CellComplex.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        
        vb1 = Vertex.ByCoordinates(-0.5,0,0)
        vb2 = Vertex.ByCoordinates(0,-0.5,0)
        vb3 = Vertex.ByCoordinates(0.5,0,0)
        vb4 = Vertex.ByCoordinates(0,0.5,0)
        top = Vertex.ByCoordinates(0, 0, 0.5)
        bottom = Vertex.ByCoordinates(0, 0, -0.5)
        f1 = Face.ByVertices([top,vb1,vb2])
        f2 = Face.ByVertices([top,vb2,vb3])
        f3 = Face.ByVertices([top,vb3,vb4])
        f4 = Face.ByVertices([top,vb4,vb1])
        f5 = Face.ByVertices([bottom,vb1,vb2])
        f6 = Face.ByVertices([bottom,vb2,vb3])
        f7 = Face.ByVertices([bottom,vb3,vb4])
        f8 = Face.ByVertices([bottom,vb4,vb1])
        f9 = Face.ByVertices([vb1,vb2,vb3,vb4])

        octahedron = CellComplex.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9], tolerance=tolerance)
        octahedron = Topology.Scale(octahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
        if placement == "bottom":
            octahedron = Topology.Translate(octahedron, 0, 0, radius)
        elif placement == "lowerleft":
            octahedron = Topology.Translate(octahedron, radius, radius, radius)
        octahedron = Topology.Orient(octahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        octahedron = Topology.Place(octahedron, originA=Vertex.Origin(), originB=origin)
        return octahedron
    
    @staticmethod
    def Prism(origin: topologic.Vertex = None,
              width: float = 1.0, length: float = 1.0, height: float = 1.0,
              uSides: int = 2, vSides: int = 2, wSides: int = 2,
              direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
        """
        Creates a prismatic cellComplex with internal cells.

        Parameters
        ----------
        origin : topologic.Vertex , optional
            The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
        width : float , optional
            The width of the prism. The default is 1.
        length : float , optional
            The length of the prism. The default is 1.
        height : float , optional
            The height of the prism.
        uSides : int , optional
            The number of sides along the width. The default is 1.
        vSides : int , optional
            The number of sides along the length. The default is 1.
        wSides : int , optional
            The number of sides along the height. The default is 1.
        direction : list , optional
            The vector representing the up direction of the prism. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the prism. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        topologic.CellComplex
            The created prism.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        
        def bb(topology):
            vertices = []
            _ = topology.Vertices(None, vertices)
            x = []
            y = []
            z = []
            for aVertex in vertices:
                x.append(aVertex.X())
                y.append(aVertex.Y())
                z.append(aVertex.Z())
            minX = min(x)
            minY = min(y)
            minZ = min(z)
            maxX = max(x)
            maxY = max(y)
            maxZ = max(z)
            return [minX, minY, minZ, maxX, maxY, maxZ]
        
        def slice(topology, uSides, vSides, wSides):
            minX, minY, minZ, maxX, maxY, maxZ = bb(topology)
            centroid = Vertex.ByCoordinates(minX+(maxX-minX)*0.5, minY+(maxY-minY)*0.5, minZ+(maxZ-minZ)*0.5)
            wOrigin = Vertex.ByCoordinates(Vertex.X(centroid), Vertex.Y(centroid), minZ)
            wFace = Face.Rectangle(origin=wOrigin, width=(maxX-minX)*1.1, length=(maxY-minY)*1.1)
            wFaces = []
            wOffset = (maxZ-minZ)/wSides
            for i in range(wSides-1):
                wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
            uOrigin = Vertex.ByCoordinates(minX, Vertex.Y(centroid), Vertex.Z(centroid))
            uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-minZ)*1.1, length=(maxY-minY)*1.1, direction=[1,0,0])
            uFaces = []
            uOffset = (maxX-minX)/uSides
            for i in range(uSides-1):
                uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
            vOrigin = Vertex.ByCoordinates(Vertex.X(centroid), minY, Vertex.Z(centroid))
            vFace = Face.Rectangle(origin=vOrigin, width=(maxX-minX)*1.1, length=(maxZ-minZ)*1.1, direction=[0,1,0])
            vFaces = []
            vOffset = (maxY-minY)/vSides
            for i in range(vSides-1):
                vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
            all_faces = uFaces+vFaces+wFaces
            if len(all_faces) > 0:
                f_clus = Cluster.ByTopologies(uFaces+vFaces+wFaces)
                return Topology.Slice(topology, f_clus, tolerance=tolerance)
            else:
                return topologic.CellComplex.ByCells([topology])
        if not isinstance(origin, topologic.Vertex):
            origin = Vertex.ByCoordinates(0, 0, 0)

        c = Cell.Prism(origin=origin, width=width, length=length, height=height, uSides=1, vSides=1, wSides=1, placement=placement, tolerance=tolerance)
        prism = slice(c, uSides=uSides, vSides=vSides, wSides=wSides)
        if prism:
            prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction)
            return prism
        else:
            print("CellComplex.Prism - Error: Could not create a prism. Returning None.")
            return None

    @staticmethod
    def RemoveCollinearEdges(cellComplex: topologic.CellComplex, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
        """
        Removes any collinear edges in the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.
        angTolerance : float , optional
            The desired angular tolerance. The default is 0.1.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic.CellComplex
            The created cellComplex without any collinear edges.

        """
        from topologicpy.Cell import Cell

        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.RemoveCollinearEdges - Error: The input cellComplex parameter is not a valid cellComplex. Returning None.")
            return None
        cells = CellComplex.Cells(cellComplex)
        clean_cells = []
        for cell in cells:
            clean_cells.append(Cell.RemoveCollinearEdges(cell, angTolerance=angTolerance, tolerance=tolerance))
        return CellComplex.ByCells(clean_cells, tolerance=tolerance)
    
    @staticmethod
    def Shells(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the shells of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of shells.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Shells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        shells = []
        _ = cellComplex.Shells(None, shells)
        return shells

    @staticmethod
    def Vertices(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the vertices of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of vertices.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Vertices - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        vertices = []
        _ = cellComplex.Vertices(None, vertices)
        return vertices

    @staticmethod
    def Volume(cellComplex: topologic.CellComplex, mantissa: int = 6) -> float:
        """
        Returns the volume of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.
        manitssa: int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        float
            The volume of the input cellComplex.

        """
        from topologicpy.Cell import Cell
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Volume - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        cells = CellComplex.Cells(cellComplex)
        volume = 0
        for cell in cells:
            volume = Cell.Volume(cell)
            if not volume == None:
                volume += Cell.Volume(cell)
        return round(volume, mantissa)
    
    @staticmethod
    def Voronoi(vertices: list = None, cell: topologic.Cell = None, tolerance: float = 0.0001):
        """
        Partitions the input cell based on the Voronoi method. See https://en.wikipedia.org/wiki/Voronoi_diagram.

        Parameters
        ----------
        vertices: list , optional 
            The input list of vertices to use for voronoi partitioning. If set to None, the algorithm uses the vertices of the input cell parameter.
            if both are set to none, a unit cube centered around the origin is used.
        cell : topologic.Cell , optional
            The input bounding cell. If set to None, an axes-aligned bounding cell is created from the list of vertices. The default is None.
        tolerance : float , optional
            the desired tolerance. The default is 0.0001.
        

        Returns
        -------
        topologic.CellComplex
            The created voronoi cellComplex.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        from scipy.spatial import Voronoi as SCIVoronoi
        import numpy as np

        def fracture_with_voronoi(points):
            # Compute Voronoi tessellation
            vor = SCIVoronoi(points)
            verts = []
            faces = []
            for v in vor.vertices:
                verts.append(Vertex.ByCoordinates(list(v)))
            for region in vor.ridge_vertices:
                temp_list = []
                if -1 not in region and len(region) > 0:
                    for item in region:
                        temp_list.append(verts[item])
                    f = Face.ByVertices(temp_list)
                    if isinstance(f, topologic.Face):
                        faces.append(f)
            if len(faces) < 1:
                return None
            return Cluster.ByTopologies(faces)
        
        if cell == None:
            if not isinstance(vertices, list):
                cell = Cell.Prism(uSides=2, vSides=2, wSides=2)
                vertices = Topology.Vertices(cell)
                vertices.append(Vertex.Origin())
            else:
                vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
                if len(vertices) < 1:
                    print("CellComplex.Voronoi - Error: The input vertices parameter does not contain any valid vertices. Returning None.")
                    return None
                cell = Topology.BoundingBox(Cluster.ByTopologies(vertices))
        if not isinstance(vertices, list):
            if not isinstance(cell, topologic.Cell):
                cell = Cell.Prism()
                vertices = Topology.Vertices(cell)
            else:
                vertices = Topology.Vertices(cell)
        else:
            vertices += Topology.Vertices(cell)
        vertices = [v for v in vertices if (Vertex.IsInternal(v, cell) or not Vertex.Index(v, Topology.Vertices(cell)) == None)]
        if len(vertices) < 1:
            print("CellComplex.Voronoi - Error: The input vertices parame ter does not contain any vertices that are inside the input cell parameter. Returning None.")
            return None
        voronoi_points = np.array([Vertex.Coordinates(v) for v in vertices])
        cluster = fracture_with_voronoi(voronoi_points)
        if cluster == None:
            print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
            return None
        cellComplex = Topology.Slice(cell, cluster)
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
            return None
        return cellComplex
    
    @staticmethod
    def Wires(cellComplex: topologic.CellComplex) -> list:
        """
        Returns the wires of the input cellComplex.

        Parameters
        ----------
        cellComplex : topologic.CellComplex
            The input cellComplex.

        Returns
        -------
        list
            The list of wires.

        """
        if not isinstance(cellComplex, topologic.CellComplex):
            print("CellComplex.Wires - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
            return None
        wires = []
        _ = cellComplex.Wires(None, wires)
        return wires

Ancestors

  • topologicpy.Topology.Topology

Static methods

def Box(origin: topologic.Vertex = None, width: float = 1.0, length: float = 1.0, height: float = 1.0, uSides: int = 2, vSides: int = 2, wSides: int = 2, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001) ‑> topologic.CellComplex

Creates a box with internal cells.

Parameters

origin : topologic.Vertex , optional
The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
width : float , optional
The width of the box. The default is 1.
length : float , optional
The length of the box. The default is 1.
height : float , optional
The height of the box.
uSides : int , optional
The number of sides along the width. The default is 1.
vSides : int, optional
The number of sides along the length. The default is 1.
wSides : int , optional
The number of sides along the height. The default is 1.
direction : list , optional
The vector representing the up direction of the box. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the box. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created box.
Expand source code
@staticmethod
def Box(origin: topologic.Vertex = None,
        width: float = 1.0, length: float = 1.0, height: float = 1.0,
        uSides: int = 2, vSides: int = 2, wSides: int = 2,
        direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Creates a box with internal cells.

    Parameters
    ----------
    origin : topologic.Vertex , optional
        The origin location of the box. The default is None which results in the box being placed at (0, 0, 0).
    width : float , optional
        The width of the box. The default is 1.
    length : float , optional
        The length of the box. The default is 1.
    height : float , optional
        The height of the box.
    uSides : int , optional
        The number of sides along the width. The default is 1.
    vSides : int, optional
        The number of sides along the length. The default is 1.
    wSides : int , optional
        The number of sides along the height. The default is 1.
    direction : list , optional
        The vector representing the up direction of the box. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the box. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.
    
    Returns
    -------
    topologic.CellComplex
        The created box.

    """
    return CellComplex.Prism(origin=origin,
                             width=width, length=length, height=height,
                             uSides=uSides, vSides=vSides, wSides=wSides,
                             direction=direction, placement=placement, tolerance=tolerance)
def ByCells(cells: list, tolerance: float = 0.0001, silent: bool = False) ‑> topologic.CellComplex

Creates a cellcomplex by merging the input cells.

Parameters

cells : list
The list of input cells.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created cellcomplex.
Expand source code
@staticmethod
def ByCells(cells: list, tolerance: float = 0.0001, silent: bool = False) -> topologic.CellComplex:
    """
    Creates a cellcomplex by merging the input cells.

    Parameters
    ----------
    cells : list
        The list of input cells.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic.CellComplex
        The created cellcomplex.

    """
    from topologicpy.Cluster import Cluster
    from topologicpy.Topology import Topology

    if not isinstance(cells, list):
        if not silent:
            print("CellComplex.ByCells - Error: The input cells parameter is not a valid list. Returning None.")
        return None
    cells = [x for x in cells if isinstance(x, topologic.Cell)]
    if len(cells) < 1:
        if not silent:
            print("CellComplex.ByCells - Error: The input cells parameter does not contain any valid cells. Returning None.")
        return None
    cellComplex = None
    if len(cells) == 1:
        return topologic.CellComplex.ByCells(cells)
    else:
        try:
            cellComplex = topologic.CellComplex.ByCells(cells)
        except:
            topA = cells[0]
            topB = Cluster.ByTopologies(cells[1:])
            cellComplex = Topology.Merge(topA, topB, tranDict=False, tolerance=tolerance)
    
    if not isinstance(cellComplex, topologic.CellComplex):
        if not silent:
            print("CellComplex.ByCells - Warning: Could not create a CellComplex. Returning object of type topologic.Cluster instead of topologic.CellComplex.")
        return Cluster.ByTopologies(cells)
    else:
        temp_cells = CellComplex.Cells(cellComplex)
        if not isinstance(temp_cells, list):
            if not silent:
                print("CellComplex.ByCells - Error: The resulting object does not contain any cells. Returning None.")
            return None
        elif len(temp_cells) < 1:
            if silent:
                print("CellComplex.ByCells - Error: Could not create a CellComplex. Returning None.")
            return None
        elif len(temp_cells) == 1:
            if not silent:
                print("CellComplex.ByCells - Warning: Resulting object contains only one cell. Returning object of type topologic.Cell instead of topologic.CellComplex.")
            return(temp_cells[0])
    return cellComplex
def ByCellsCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) ‑> topologic.CellComplex

Creates a cellcomplex by merging the cells within the input cluster.

Parameters

cluster : topologic.Cluster
The input cluster of cells.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created cellcomplex.
Expand source code
@staticmethod
def ByCellsCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Creates a cellcomplex by merging the cells within the input cluster.

    Parameters
    ----------
    cluster : topologic.Cluster
        The input cluster of cells.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic.CellComplex
        The created cellcomplex.

    """

    if not isinstance(cluster, topologic.Cluster):
        print("CellComplex.ByCellsCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
        return None
    cells = []
    _ = cluster.Cells(None, cells)
    return CellComplex.ByCells(cells, tolerance)
def ByFaces(faces: list, tolerance: float = 0.0001) ‑> topologic.CellComplex

Creates a cellcomplex by merging the input faces.

Parameters

faces : topologic.Face
The input faces.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created cellcomplex.
Expand source code
@staticmethod
def ByFaces(faces: list, tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Creates a cellcomplex by merging the input faces.

    Parameters
    ----------
    faces : topologic.Face
        The input faces.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic.CellComplex
        The created cellcomplex.

    """

    if not isinstance(faces, list):
        print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
        return None
    faces = [x for x in faces if isinstance(x, topologic.Face)]
    if len(faces) < 1:
        print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
        return None
    try:
        cellComplex = topologic.CellComplex.ByFaces(faces, tolerance, False)
    except:
        cellComplex = None
    if not cellComplex:
        print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
        cellComplex = faces[0]
        for i in range(1,len(faces)):
            newCellComplex = None
            try:
                newCellComplex = cellComplex.Merge(faces[i], False, tolerance)
            except:
                print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
            if newCellComplex:
                cellComplex = newCellComplex
        if cellComplex.Type() != 64: #64 is the type of a CellComplex
            print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
            if cellComplex.Type() > 64:
                returnCellComplexes = []
                _ = cellComplex.CellComplexes(None, returnCellComplexes)
                if len(returnCellComplexes) > 0:
                    return returnCellComplexes[0]
                else:
                    print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
                    return None
            else:
                print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
                return None
    else:
        return cellComplex
def ByFacesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) ‑> topologic.CellComplex

Creates a cellcomplex by merging the faces within the input cluster.

Parameters

cluster : topologic.Cluster
The input cluster of faces.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created cellcomplex.
Expand source code
@staticmethod
def ByFacesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Creates a cellcomplex by merging the faces within the input cluster.

    Parameters
    ----------
    cluster : topologic.Cluster
        The input cluster of faces.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic.CellComplex
        The created cellcomplex.

    """

    if not isinstance(cluster, topologic.Cluster):
        print("CellComplex.ByFacesCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
        return None
    faces = []
    _ = cluster.Faces(None, faces)
    return CellComplex.ByFaces(faces, tolerance)
def ByWires(wires: list, triangulate: bool = True, tolerance: float = 0.0001) ‑> topologic.CellComplex

Creates a cellcomplex by lofting through the input wires.

Parameters

wires : list
The input list of wires. The list should contain a minimum of two wires. All wires must have the same number of edges.
triangulate : bool , optional
If set to True, the faces will be triangulated. The default is True.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created cellcomplex.
Expand source code
@staticmethod
def ByWires(wires: list, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Creates a cellcomplex by lofting through the input wires.

    Parameters
    ----------
    wires : list
        The input list of wires. The list should contain a minimum of two wires. All wires must have the same number of edges.
    triangulate : bool , optional
        If set to True, the faces will be triangulated. The default is True.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic.CellComplex
        The created cellcomplex.

    """
    from topologicpy.Edge import Edge
    from topologicpy.Wire import Wire
    from topologicpy.Face import Face
    from topologicpy.Topology import Topology

    if not isinstance(wires, list):
        print("CellComplex.ByFaces - Error: The input wires parameter is not a valid list. Returning None.")
        return None
    wires = [x for x in wires if isinstance(x, topologic.Wire)]
    if len(wires) < 2:
        print("CellComplex.ByWires - Error: The input wires parameter contains less than two valid wires. Returning None.")
        return None
    faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
    if triangulate == True:
        triangles = []
        for face in faces:
            if len(Topology.Vertices(face)) > 3:
                triangles += Face.Triangulate(face, tolerance=tolerance)
            else:
                triangles += [face]
        faces = triangles
    for i in range(len(wires)-1):
        wire1 = wires[i]
        wire2 = wires[i+1]
        f = Face.ByWire(wire2, tolerance=tolerance)
        if triangulate == True:
            if len(Topology.Vertices(face)) > 3:
                triangles = Face.Triangulate(face, tolerance=tolerance)
            else:
                triangles = [face]
            faces += triangles
        else:
            faces.append(f)
        w1_edges = []
        _ = wire1.Edges(None, w1_edges)
        w2_edges = []
        _ = wire2.Edges(None, w2_edges)
        if len(w1_edges) != len(w2_edges):
            print("CellComplex.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
            return None
        for j in range (len(w1_edges)):
            e1 = w1_edges[j]
            e2 = w2_edges[j]
            e3 = None
            e4 = None
            try:
                e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
            except:
                try:
                    e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                    f = Face.ByExternalBoundary(Wire.ByEdges([e1, e2, e4], tolerance=tolerance))
                    if triangulate == True:
                        if len(Topology.Vertices(face)) > 3:
                            triangles = Face.Triangulate(face, tolerance=tolerance)
                        else:
                            triangles = [face]
                        faces += triangles
                    else:
                        faces.append(f)
                except:
                    pass
            try:
                e4 = Edge.ByStartVertexEndVertex(e1.EndVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
            except:
                try:
                    e3 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.StartVertex(), tolerance=tolerance, silent=True)
                    f = Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance)
                    if triangulate == True:
                        if len(Topology.Vertices(face)) > 3:
                            triangles = Face.Triangulate(face, tolerance=tolerance)
                        else:
                            triangles = [face]
                        faces += triangles
                    else:
                        faces.append(f)
                except:
                    pass
            if e3 and e4:
                if triangulate == True:
                    e5 = Edge.ByStartVertexEndVertex(e1.StartVertex(), e2.EndVertex(), tolerance=tolerance, silent=True)
                    faces.append(Face.ByWire(Wire.ByEdges([e1, e5, e4], tolerance=tolerance), tolerance=tolerance))
                    faces.append(Face.ByWire(Wire.ByEdges([e2, e5, e3], tolerance=tolerance), tolerance=tolerance))
                else:
                    f = Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance) or Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance)
                    if f:
                        faces.append(f)

            elif e3:
                faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2], tolerance=tolerance), tolerance=tolerance))
            elif e4:
                faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2], tolerance=tolerance), tolerance=tolerance))
    return CellComplex.ByFaces(faces, tolerance=tolerance)
def ByWiresCluster(cluster: topologic.Cluster, triangulate: bool = True, tolerance: float = 0.0001) ‑> topologic.CellComplex

Creates a cellcomplex by lofting through the wires in the input cluster.

Parameters

cluster : topologic.Cluster
The input cluster of wires.
triangulate : bool , optional
If set to True, the faces will be triangulated. The default is True.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created cellcomplex.
Expand source code
@staticmethod
def ByWiresCluster(cluster: topologic.Cluster, triangulate: bool = True, tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Creates a cellcomplex by lofting through the wires in the input cluster.

    Parameters
    ----------
    cluster : topologic.Cluster
        The input cluster of wires.
    triangulate : bool , optional
        If set to True, the faces will be triangulated. The default is True.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic.CellComplex
        The created cellcomplex.

    """

    if not isinstance(cluster, topologic.Cluster):
        print("CellComplex.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
        return None
    wires = []
    _ = cluster.Wires(None, wires)
    return CellComplex.ByWires(wires, triangulate=triangulate, tolerance=tolerance)
def Cells(cellComplex: topologic.CellComplex) ‑> list

Returns the cells of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of cells.
Expand source code
@staticmethod
def Cells(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the cells of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of cells.

    """
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Cells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    cells = []
    _ = cellComplex.Cells(None, cells)
    return cells
def Decompose(cellComplex: topologic.CellComplex, tiltAngle: float = 10.0, tolerance: float = 0.0001) ‑> dict

Decomposes the input cellComplex into its logical components. This method assumes that the positive Z direction is UP.

Parameters

cellComplex : topologic.CellComplex
the input cellComplex.
tiltAngle : float , optional
The threshold tilt angle in degrees to determine if a face is vertical, horizontal, or tilted. The tilt angle is measured from the nearest cardinal direction. The default is 10.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

dictionary
A dictionary with the following keys and values: 1. "cells": list of cells 2. "externalVerticalFaces": list of external vertical faces 3. "internalVerticalFaces": list of internal vertical faces 4. "topHorizontalFaces": list of top horizontal faces 5. "bottomHorizontalFaces": list of bottom horizontal faces 6. "internalHorizontalFaces": list of internal horizontal faces 7. "externalInclinedFaces": list of external inclined faces 8. "internalInclinedFaces": list of internal inclined faces 9. "externalVerticalApertures": list of external vertical apertures 10. "internalVerticalApertures": list of internal vertical apertures 11. "topHorizontalApertures": list of top horizontal apertures 12. "bottomHorizontalApertures": list of bottom horizontal apertures 13. "internalHorizontalApertures": list of internal horizontal apertures 14. "externalInclinedApertures": list of external inclined apertures 15. "internalInclinedApertures": list of internal inclined apertures
Expand source code
@staticmethod
def Decompose(cellComplex: topologic.CellComplex, tiltAngle: float = 10.0, tolerance: float = 0.0001) -> dict:
    """
    Decomposes the input cellComplex into its logical components. This method assumes that the positive Z direction is UP.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        the input cellComplex.
    tiltAngle : float , optional
        The threshold tilt angle in degrees to determine if a face is vertical, horizontal, or tilted. The tilt angle is measured from the nearest cardinal direction. The default is 10.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    dictionary
        A dictionary with the following keys and values:
        1. "cells": list of cells
        2. "externalVerticalFaces": list of external vertical faces
        3. "internalVerticalFaces": list of internal vertical faces
        4. "topHorizontalFaces": list of top horizontal faces
        5. "bottomHorizontalFaces": list of bottom horizontal faces
        6. "internalHorizontalFaces": list of internal horizontal faces
        7. "externalInclinedFaces": list of external inclined faces
        8. "internalInclinedFaces": list of internal inclined faces
        9. "externalVerticalApertures": list of external vertical apertures
        10. "internalVerticalApertures": list of internal vertical apertures
        11. "topHorizontalApertures": list of top horizontal apertures
        12. "bottomHorizontalApertures": list of bottom horizontal apertures
        13. "internalHorizontalApertures": list of internal horizontal apertures
        14. "externalInclinedApertures": list of external inclined apertures
        15. "internalInclinedApertures": list of internal inclined apertures

    """
    from topologicpy.Face import Face
    from topologicpy.Vector import Vector
    from topologicpy.Aperture import Aperture
    from topologicpy.Topology import Topology

    def angleCode(f, up, tiltAngle):
        dirA = Face.NormalAtParameters(f)
        ang = round(Vector.Angle(dirA, up), 2)
        if abs(ang - 90) < tiltAngle:
            code = 0
        elif abs(ang) < tiltAngle:
            code = 1
        elif abs(ang - 180) < tiltAngle:
            code = 2
        else:
            code = 3
        return code

    def getApertures(topology):
        apertures = []
        apTopologies = []
        apertures = Topology.Apertures(topology)
        for aperture in apertures:
            apTopologies.append(Aperture.Topology(aperture))
        return apTopologies

    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Decompose - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    externalVerticalFaces = []
    internalVerticalFaces = []
    topHorizontalFaces = []
    bottomHorizontalFaces = []
    internalHorizontalFaces = []
    externalInclinedFaces = []
    internalInclinedFaces = []
    externalVerticalApertures = []
    internalVerticalApertures = []
    topHorizontalApertures = []
    bottomHorizontalApertures = []
    internalHorizontalApertures = []
    externalInclinedApertures = []
    internalInclinedApertures = []
    tiltAngle = abs(tiltAngle)
    faces = CellComplex.Faces(cellComplex)
    zList = []
    for f in faces:
        zList.append(f.Centroid().Z())
    zMin = min(zList)
    zMax = max(zList)
    up = [0, 0, 1]
    for aFace in faces:
        aCode = angleCode(aFace, up, tiltAngle)
        cells = []
        aFace.Cells(cellComplex, cells)
        n = len(cells)
        if aCode == 0:
            if n == 1:
                externalVerticalFaces.append(aFace)
                externalVerticalApertures += getApertures(aFace)
            else:
                internalVerticalFaces.append(aFace)
                internalVerticalApertures += getApertures(aFace)
        elif aCode == 1:
            if n == 1:
                if abs(aFace.Centroid().Z() - zMin) < tolerance:
                    bottomHorizontalFaces.append(aFace)
                    bottomHorizontalApertures += getApertures(aFace)
                else:
                    topHorizontalFaces.append(aFace)
                    topHorizontalApertures += getApertures(aFace)
            else:
                internalHorizontalFaces.append(aFace)
                internalHorizontalApertures += getApertures(aFace)
        elif aCode == 2:
            if n == 1:
                if abs(aFace.Centroid().Z() - zMax) < tolerance:
                    topHorizontalFaces.append(aFace)
                    topHorizontalApertures += getApertures(aFace)
                else:
                    bottomHorizontalFaces.append(aFace)
                    bottomHorizontalApertures += getApertures(aFace)
            else:
                internalHorizontalFaces.append(aFace)
                internalHorizontalApertures += getApertures(aFace)
        elif aCode == 3:
            if n == 1:
                externalInclinedFaces.append(aFace)
                externalInclinedApertures += getApertures(aFace)
            else:
                internalInclinedFaces.append(aFace)
                internalInclinedApertures += getApertures(aFace)
    
    cells = Topology.Cells(cellComplex)
    d = {
        "cells" : cells,
        "externalVerticalFaces" : externalVerticalFaces,
        "internalVerticalFaces" : internalVerticalFaces,
        "topHorizontalFaces" : topHorizontalFaces,
        "bottomHorizontalFaces" : bottomHorizontalFaces,
        "internalHorizontalFaces" : internalHorizontalFaces,
        "externalInclinedFaces" : externalInclinedFaces,
        "internalInclinedFaces" : internalInclinedFaces,
        "externalVerticalApertures" : externalVerticalApertures,
        "internalVerticalApertures" : internalVerticalApertures,
        "topHorizontalApertures" : topHorizontalApertures,
        "bottomHorizontalApertures" : bottomHorizontalApertures,
        "internalHorizontalApertures" : internalHorizontalApertures,
        "externalInclinedApertures" : externalInclinedApertures,
        "internalInclinedApertures" : internalInclinedApertures
        }
    return d
def Delaunay(vertices: list = None, tolerance: float = 0.0001) ‑> topologic.CellComplex

Triangulates the input vertices based on the Delaunay method. See https://en.wikipedia.org/wiki/Delaunay_triangulation.

Parameters

vertices : list , optional
The input list of vertices to use for delaunay triangulation. If set to None, the algorithm uses the vertices of the input cell parameter. if both are set to none, a unit cube centered around the origin is used.
tolerance : float , optional
the desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created delaunay cellComplex.
Expand source code
@staticmethod
def Delaunay(vertices: list = None, tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Triangulates the input vertices based on the Delaunay method. See https://en.wikipedia.org/wiki/Delaunay_triangulation.

    Parameters
    ----------
    vertices: list , optional 
        The input list of vertices to use for delaunay triangulation. If set to None, the algorithm uses the vertices of the input cell parameter.
        if both are set to none, a unit cube centered around the origin is used.
    tolerance : float , optional
        the desired tolerance. The default is 0.0001.
    
    Returns
    -------
    topologic.CellComplex
        The created delaunay cellComplex.

    """
    from topologicpy.Vertex import Vertex
    from topologicpy.Face import Face
    from topologicpy.Cell import Cell
    from topologicpy.Cluster import Cluster
    from topologicpy.Topology import Topology
    from scipy.spatial import Delaunay as SCIDelaunay
    import numpy as np

    if not isinstance(vertices, list):
        cell = Cell.Prism()
        vertices = Topology.Vertices(cell)
    
    vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
    if len(vertices) < 3:
        print("CellComplex/Delaunay - Error: The input vertices parameter does not contain enough valid vertices. Returning None.")
        return None
    # Get the vertices of the input cell
    points = np.array([Vertex.Coordinates(v) for v in vertices])
    # Compute Delaunay triangulation
    triangulation = SCIDelaunay(points, furthest_site=False)

    faces = []
    for simplex in triangulation.simplices:
        tetrahedron_vertices = points[simplex]
        verts = [Vertex.ByCoordinates(list(coord)) for coord in tetrahedron_vertices]
        tri1 = [verts[0], verts[1], verts[2], verts[0]]
        tri2 = [verts[0], verts[2], verts[3], verts[0]]
        tri3 = [verts[0], verts[1], verts[3], verts[0]]
        tri4 = [verts[1], verts[2], verts[3], verts[1]]
        f1 = Face.ByVertices(tri1)
        f2 = Face.ByVertices(tri2)
        f3 = Face.ByVertices(tri3)
        f4 = Face.ByVertices(tri4)
        faces.append(f1)
        faces.append(f2)
        faces.append(f3)
        faces.append(f4)
    cc = Topology.RemoveCoplanarFaces(CellComplex.ByFaces(faces, tolerance=tolerance))
    faces = [Topology.RemoveCollinearEdges(f) for f in Topology.Faces(cc)]
    cc = CellComplex.ByFaces(faces)
    return cc
def Edges(cellComplex: topologic.CellComplex) ‑> list

Returns the edges of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of edges.
Expand source code
@staticmethod
def Edges(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the edges of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of edges.

    """ 
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Edges - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    edges = []
    _ = cellComplex.Edges(None, edges)
    return edges
def ExternalBoundary(cellComplex: topologic.CellComplex) ‑> topologic.Cell

Returns the external boundary (cell) of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

topologic.Cell
The external boundary of the input cellComplex.
Expand source code
@staticmethod
def ExternalBoundary(cellComplex: topologic.CellComplex) -> topologic.Cell:
    """
    Returns the external boundary (cell) of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    topologic.Cell
        The external boundary of the input cellComplex.

    """
    return cellComplex.ExternalBoundary()
def ExternalFaces(cellComplex: topologic.CellComplex) ‑> list

Returns the external faces of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of external faces.
Expand source code
@staticmethod
def ExternalFaces(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the external faces of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of external faces.

    """
    from topologicpy.Cell import Cell
    cell = cellComplex.ExternalBoundary()
    return Cell.Faces(cell)
def Faces(cellComplex: topologic.CellComplex) ‑> list

Returns the faces of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of faces.
Expand source code
@staticmethod
def Faces(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the faces of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of faces.

    """
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Faces - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    faces = []
    _ = cellComplex.Faces(None, faces)
    return faces
def InternalFaces(cellComplex: topologic.CellComplex) ‑> list

Returns the internal boundaries (faces) of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of internal faces of the input cellComplex.
Expand source code
@staticmethod
def InternalFaces(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the internal boundaries (faces) of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of internal faces of the input cellComplex.

    """
    faces = []
    _ = cellComplex.InternalBoundaries(faces)
    return faces
def NonManifoldFaces(cellComplex: topologic.CellComplex) ‑> list

Returns the non-manifold faces of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of non-manifold faces of the input cellComplex.
Expand source code
@staticmethod
def NonManifoldFaces(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the non-manifold faces of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of non-manifold faces of the input cellComplex.

    """
    faces = []
    _ = cellComplex.NonManifoldFaces(faces)
    return faces
def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001) ‑> topologic.CellComplex

Description

Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.

Parameters

origin : topologic.Vertex , optional
The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
radius : float , optional
The radius of the octahedron's circumscribed sphere. The default is 0.5.
direction : list , optional
The vector representing the up direction of the octahedron. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the octahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created octahedron.
Expand source code
@staticmethod
def Octahedron(origin: topologic.Vertex = None, radius: float = 0.5,
              direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Description
    ----------
    Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.

    Parameters
    ----------
    origin : topologic.Vertex , optional
        The origin location of the octahedron. The default is None which results in the octahedron being placed at (0, 0, 0).
    radius : float , optional
        The radius of the octahedron's circumscribed sphere. The default is 0.5.
    direction : list , optional
        The vector representing the up direction of the octahedron. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the octahedron. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.
    
    Returns
    -------
    topologic.CellComplex
        The created octahedron.

    """
    
    from topologicpy.Vertex import Vertex
    from topologicpy.Face import Face
    from topologicpy.Topology import Topology

    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not isinstance(origin, topologic.Vertex):
        print("CellComplex.Octahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    
    vb1 = Vertex.ByCoordinates(-0.5,0,0)
    vb2 = Vertex.ByCoordinates(0,-0.5,0)
    vb3 = Vertex.ByCoordinates(0.5,0,0)
    vb4 = Vertex.ByCoordinates(0,0.5,0)
    top = Vertex.ByCoordinates(0, 0, 0.5)
    bottom = Vertex.ByCoordinates(0, 0, -0.5)
    f1 = Face.ByVertices([top,vb1,vb2])
    f2 = Face.ByVertices([top,vb2,vb3])
    f3 = Face.ByVertices([top,vb3,vb4])
    f4 = Face.ByVertices([top,vb4,vb1])
    f5 = Face.ByVertices([bottom,vb1,vb2])
    f6 = Face.ByVertices([bottom,vb2,vb3])
    f7 = Face.ByVertices([bottom,vb3,vb4])
    f8 = Face.ByVertices([bottom,vb4,vb1])
    f9 = Face.ByVertices([vb1,vb2,vb3,vb4])

    octahedron = CellComplex.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9], tolerance=tolerance)
    octahedron = Topology.Scale(octahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)
    if placement == "bottom":
        octahedron = Topology.Translate(octahedron, 0, 0, radius)
    elif placement == "lowerleft":
        octahedron = Topology.Translate(octahedron, radius, radius, radius)
    octahedron = Topology.Orient(octahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
    octahedron = Topology.Place(octahedron, originA=Vertex.Origin(), originB=origin)
    return octahedron
def Prism(origin: topologic.Vertex = None, width: float = 1.0, length: float = 1.0, height: float = 1.0, uSides: int = 2, vSides: int = 2, wSides: int = 2, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001) ‑> topologic.CellComplex

Creates a prismatic cellComplex with internal cells.

Parameters

origin : topologic.Vertex , optional
The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
width : float , optional
The width of the prism. The default is 1.
length : float , optional
The length of the prism. The default is 1.
height : float , optional
The height of the prism.
uSides : int , optional
The number of sides along the width. The default is 1.
vSides : int , optional
The number of sides along the length. The default is 1.
wSides : int , optional
The number of sides along the height. The default is 1.
direction : list , optional
The vector representing the up direction of the prism. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the prism. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created prism.
Expand source code
@staticmethod
def Prism(origin: topologic.Vertex = None,
          width: float = 1.0, length: float = 1.0, height: float = 1.0,
          uSides: int = 2, vSides: int = 2, wSides: int = 2,
          direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.CellComplex:
    """
    Creates a prismatic cellComplex with internal cells.

    Parameters
    ----------
    origin : topologic.Vertex , optional
        The origin location of the prism. The default is None which results in the prism being placed at (0, 0, 0).
    width : float , optional
        The width of the prism. The default is 1.
    length : float , optional
        The length of the prism. The default is 1.
    height : float , optional
        The height of the prism.
    uSides : int , optional
        The number of sides along the width. The default is 1.
    vSides : int , optional
        The number of sides along the length. The default is 1.
    wSides : int , optional
        The number of sides along the height. The default is 1.
    direction : list , optional
        The vector representing the up direction of the prism. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the prism. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.
    
    Returns
    -------
    topologic.CellComplex
        The created prism.

    """
    from topologicpy.Vertex import Vertex
    from topologicpy.Face import Face
    from topologicpy.Cell import Cell
    from topologicpy.Cluster import Cluster
    from topologicpy.Topology import Topology
    
    def bb(topology):
        vertices = []
        _ = topology.Vertices(None, vertices)
        x = []
        y = []
        z = []
        for aVertex in vertices:
            x.append(aVertex.X())
            y.append(aVertex.Y())
            z.append(aVertex.Z())
        minX = min(x)
        minY = min(y)
        minZ = min(z)
        maxX = max(x)
        maxY = max(y)
        maxZ = max(z)
        return [minX, minY, minZ, maxX, maxY, maxZ]
    
    def slice(topology, uSides, vSides, wSides):
        minX, minY, minZ, maxX, maxY, maxZ = bb(topology)
        centroid = Vertex.ByCoordinates(minX+(maxX-minX)*0.5, minY+(maxY-minY)*0.5, minZ+(maxZ-minZ)*0.5)
        wOrigin = Vertex.ByCoordinates(Vertex.X(centroid), Vertex.Y(centroid), minZ)
        wFace = Face.Rectangle(origin=wOrigin, width=(maxX-minX)*1.1, length=(maxY-minY)*1.1)
        wFaces = []
        wOffset = (maxZ-minZ)/wSides
        for i in range(wSides-1):
            wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
        uOrigin = Vertex.ByCoordinates(minX, Vertex.Y(centroid), Vertex.Z(centroid))
        uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-minZ)*1.1, length=(maxY-minY)*1.1, direction=[1,0,0])
        uFaces = []
        uOffset = (maxX-minX)/uSides
        for i in range(uSides-1):
            uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
        vOrigin = Vertex.ByCoordinates(Vertex.X(centroid), minY, Vertex.Z(centroid))
        vFace = Face.Rectangle(origin=vOrigin, width=(maxX-minX)*1.1, length=(maxZ-minZ)*1.1, direction=[0,1,0])
        vFaces = []
        vOffset = (maxY-minY)/vSides
        for i in range(vSides-1):
            vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
        all_faces = uFaces+vFaces+wFaces
        if len(all_faces) > 0:
            f_clus = Cluster.ByTopologies(uFaces+vFaces+wFaces)
            return Topology.Slice(topology, f_clus, tolerance=tolerance)
        else:
            return topologic.CellComplex.ByCells([topology])
    if not isinstance(origin, topologic.Vertex):
        origin = Vertex.ByCoordinates(0, 0, 0)

    c = Cell.Prism(origin=origin, width=width, length=length, height=height, uSides=1, vSides=1, wSides=1, placement=placement, tolerance=tolerance)
    prism = slice(c, uSides=uSides, vSides=vSides, wSides=wSides)
    if prism:
        prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction)
        return prism
    else:
        print("CellComplex.Prism - Error: Could not create a prism. Returning None.")
        return None
def RemoveCollinearEdges(cellComplex: topologic.CellComplex, angTolerance: float = 0.1, tolerance: float = 0.0001) ‑> topologic.Wire

Removes any collinear edges in the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.
angTolerance : float , optional
The desired angular tolerance. The default is 0.1.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created cellComplex without any collinear edges.
Expand source code
@staticmethod
def RemoveCollinearEdges(cellComplex: topologic.CellComplex, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
    """
    Removes any collinear edges in the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.
    angTolerance : float , optional
        The desired angular tolerance. The default is 0.1.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic.CellComplex
        The created cellComplex without any collinear edges.

    """
    from topologicpy.Cell import Cell

    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.RemoveCollinearEdges - Error: The input cellComplex parameter is not a valid cellComplex. Returning None.")
        return None
    cells = CellComplex.Cells(cellComplex)
    clean_cells = []
    for cell in cells:
        clean_cells.append(Cell.RemoveCollinearEdges(cell, angTolerance=angTolerance, tolerance=tolerance))
    return CellComplex.ByCells(clean_cells, tolerance=tolerance)
def Shells(cellComplex: topologic.CellComplex) ‑> list

Returns the shells of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of shells.
Expand source code
@staticmethod
def Shells(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the shells of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of shells.

    """
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Shells - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    shells = []
    _ = cellComplex.Shells(None, shells)
    return shells
def Vertices(cellComplex: topologic.CellComplex) ‑> list

Returns the vertices of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of vertices.
Expand source code
@staticmethod
def Vertices(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the vertices of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of vertices.

    """
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Vertices - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    vertices = []
    _ = cellComplex.Vertices(None, vertices)
    return vertices
def Volume(cellComplex: topologic.CellComplex, mantissa: int = 6) ‑> float

Returns the volume of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.
manitssa : int , optional
The desired length of the mantissa. The default is 6.

Returns

float
The volume of the input cellComplex.
Expand source code
@staticmethod
def Volume(cellComplex: topologic.CellComplex, mantissa: int = 6) -> float:
    """
    Returns the volume of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.
    manitssa: int , optional
        The desired length of the mantissa. The default is 6.

    Returns
    -------
    float
        The volume of the input cellComplex.

    """
    from topologicpy.Cell import Cell
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Volume - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    cells = CellComplex.Cells(cellComplex)
    volume = 0
    for cell in cells:
        volume = Cell.Volume(cell)
        if not volume == None:
            volume += Cell.Volume(cell)
    return round(volume, mantissa)
def Voronoi(vertices: list = None, cell: topologic.Cell = None, tolerance: float = 0.0001)

Partitions the input cell based on the Voronoi method. See https://en.wikipedia.org/wiki/Voronoi_diagram.

Parameters

vertices : list , optional
The input list of vertices to use for voronoi partitioning. If set to None, the algorithm uses the vertices of the input cell parameter. if both are set to none, a unit cube centered around the origin is used.
cell : topologic.Cell , optional
The input bounding cell. If set to None, an axes-aligned bounding cell is created from the list of vertices. The default is None.
tolerance : float , optional
the desired tolerance. The default is 0.0001.

Returns

topologic.CellComplex
The created voronoi cellComplex.
Expand source code
@staticmethod
def Voronoi(vertices: list = None, cell: topologic.Cell = None, tolerance: float = 0.0001):
    """
    Partitions the input cell based on the Voronoi method. See https://en.wikipedia.org/wiki/Voronoi_diagram.

    Parameters
    ----------
    vertices: list , optional 
        The input list of vertices to use for voronoi partitioning. If set to None, the algorithm uses the vertices of the input cell parameter.
        if both are set to none, a unit cube centered around the origin is used.
    cell : topologic.Cell , optional
        The input bounding cell. If set to None, an axes-aligned bounding cell is created from the list of vertices. The default is None.
    tolerance : float , optional
        the desired tolerance. The default is 0.0001.
    

    Returns
    -------
    topologic.CellComplex
        The created voronoi cellComplex.

    """
    from topologicpy.Vertex import Vertex
    from topologicpy.Face import Face
    from topologicpy.Cell import Cell
    from topologicpy.Cluster import Cluster
    from topologicpy.Topology import Topology
    from scipy.spatial import Voronoi as SCIVoronoi
    import numpy as np

    def fracture_with_voronoi(points):
        # Compute Voronoi tessellation
        vor = SCIVoronoi(points)
        verts = []
        faces = []
        for v in vor.vertices:
            verts.append(Vertex.ByCoordinates(list(v)))
        for region in vor.ridge_vertices:
            temp_list = []
            if -1 not in region and len(region) > 0:
                for item in region:
                    temp_list.append(verts[item])
                f = Face.ByVertices(temp_list)
                if isinstance(f, topologic.Face):
                    faces.append(f)
        if len(faces) < 1:
            return None
        return Cluster.ByTopologies(faces)
    
    if cell == None:
        if not isinstance(vertices, list):
            cell = Cell.Prism(uSides=2, vSides=2, wSides=2)
            vertices = Topology.Vertices(cell)
            vertices.append(Vertex.Origin())
        else:
            vertices = [v for v in vertices if isinstance(v, topologic.Vertex)]
            if len(vertices) < 1:
                print("CellComplex.Voronoi - Error: The input vertices parameter does not contain any valid vertices. Returning None.")
                return None
            cell = Topology.BoundingBox(Cluster.ByTopologies(vertices))
    if not isinstance(vertices, list):
        if not isinstance(cell, topologic.Cell):
            cell = Cell.Prism()
            vertices = Topology.Vertices(cell)
        else:
            vertices = Topology.Vertices(cell)
    else:
        vertices += Topology.Vertices(cell)
    vertices = [v for v in vertices if (Vertex.IsInternal(v, cell) or not Vertex.Index(v, Topology.Vertices(cell)) == None)]
    if len(vertices) < 1:
        print("CellComplex.Voronoi - Error: The input vertices parame ter does not contain any vertices that are inside the input cell parameter. Returning None.")
        return None
    voronoi_points = np.array([Vertex.Coordinates(v) for v in vertices])
    cluster = fracture_with_voronoi(voronoi_points)
    if cluster == None:
        print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
        return None
    cellComplex = Topology.Slice(cell, cluster)
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Voronoi - Error: the operation failed. Returning None.")
        return None
    return cellComplex
def Wires(cellComplex: topologic.CellComplex) ‑> list

Returns the wires of the input cellComplex.

Parameters

cellComplex : topologic.CellComplex
The input cellComplex.

Returns

list
The list of wires.
Expand source code
@staticmethod
def Wires(cellComplex: topologic.CellComplex) -> list:
    """
    Returns the wires of the input cellComplex.

    Parameters
    ----------
    cellComplex : topologic.CellComplex
        The input cellComplex.

    Returns
    -------
    list
        The list of wires.

    """
    if not isinstance(cellComplex, topologic.CellComplex):
        print("CellComplex.Wires - Error: The input cellcomplex parameter is not a valid topologic cellcomplex. Returning None.")
        return None
    wires = []
    _ = cellComplex.Wires(None, wires)
    return wires