Module Cell

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_core as topologic
    
class Cell():
    @staticmethod
    def Area(cell, mantissa: int = 6):
        """
        Returns the surface area of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The cell.
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        float
            The surface area of the input cell.

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

        faces = Topology.Faces(cell)
        area = 0.0
        for aFace in faces:
            area = area + Face.Area(aFace)
        return round(area, mantissa)

    @staticmethod
    def Box(origin = None,
            width: float = 1, length: float = 1, height: float = 1,
            uSides: int = 1, vSides: int = 1, wSides: int = 1,
            direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Creates a box.

        Parameters
        ----------
        origin : topologic_core.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_core.Cell
            The created box.

        """
        return Cell.Prism(origin=origin, width=width, length=length, height=height,
                          uSides=uSides, vSides=vSides, wSides=wSides,
                          direction=direction, placement=placement, tolerance=tolerance)

    @staticmethod
    def ByFaces(faces: list, planarize: bool = False, tolerance: float = 0.0001, silent=False):
        """
        Creates a cell from the input list of faces.

        Parameters
        ----------
        faces : list
            The input list of faces.
        planarize : bool, optional
            If set to True, the input faces are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        silent : bool , optional
            If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

        Returns
        -------
        topologic_core.Cell
            The created cell.

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

        if not isinstance(faces, list):
            if not silent:
                print("Cell.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
                return None
        faceList = [x for x in faces if Topology.IsInstance(x, "Face")]
        if len(faceList) < 1:
            if not silent:
                print("Cell.ByFaces - Error: The input faces parameter does not contain valid faces. Returning None.")
                return None
        # Try the default method
        cell = topologic.Cell.ByFaces(faceList, tolerance) # Hook to Core
        if Topology.IsInstance(cell, "Cell"):
            return cell
        
        # Fuse all the vertices first and rebuild the faces
        all_vertices = []
        wires = []
        for f in faceList:
            w = Face.Wire(f)
            wires.append(w)
            all_vertices += Topology.Vertices(w)
        all_vertices = Vertex.Fuse(all_vertices, tolerance=tolerance)
        new_faces = []
        for w in wires:
            face_vertices = []
            for v in Topology.Vertices(w):
                index = Vertex.Index(v, all_vertices, tolerance=tolerance)
                if not index == None:
                    face_vertices.append(all_vertices[index])
            new_w = Wire.ByVertices(face_vertices)
            if Topology.IsInstance(new_w, "Wire"):
                new_f = Face.ByWire(new_w, silent=True)
                if Topology.IsInstance(new_f, "Face"):
                    new_faces.append(new_f)
                elif isinstance(new_f, list):
                    new_faces += new_f
        faceList = new_faces
        planarizedList = []
        enlargedList = []
        if planarize:
            planarizedList = [Face.Planarize(f, tolerance=tolerance) for f in faceList]
            enlargedList = [Face.ByOffset(f, offset=-tolerance*10) for f in planarizedList]
            cell = topologic.Cell.ByFaces(enlargedList, tolerance) # Hook to Core
            faceList = Topology.SubTopologies(cell, subTopologyType="face")
            finalFaces = []
            for f in faceList:
                centroid = Topology.Centroid(f)
                n = Face.Normal(f)
                v = Topology.Translate(centroid, n[0]*0.01, n[1]*0.01, n[2]*0.01)
                if not Vertex.IsInternal(v, cell):
                    finalFaces.append(f)
            finalFinalFaces = []
            for f in finalFaces:
                vertices = Face.Vertices(f)
                w = Wire.Cycles(Face.ExternalBoundary(f), maxVertices=len(vertices))[0]
                f1 = Face.ByWire(w, tolerance=tolerance, silent=True)
                if Topology.IsInstance(f1, "Face"):
                    finalFinalFaces.append(f1)
                elif isinstance(f1, list):
                    finalFinalFaces += f1
            cell = topologic.Cell.ByFaces(finalFinalFaces, tolerance) # Hook to Core
            if cell == None:
                if not silent:
                    print("Cell.ByFaces - Error: The operation failed. Returning None.")
                    return None
            else:
                return cell
        else:
            cell = topologic.Cell.ByFaces(faces, tolerance) # Hook to Core
            if cell == None:
                if not silent:
                    print("Cell.ByFaces - Error: The operation failed. Returning None.")
                    return None
            else:
                return cell
    @staticmethod
    def ByOffset(cell, offset: float = 1.0, tolerance: float = 0.0001):
        """
        Creates an offset cell from the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        offset : float , optional
            The desired offset distance. The default is 1.0.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        Topology
            The created offset topology. WARNING: This method may fail to create a cell if the offset creates self-intersecting faces. Always check the type being returned by this method.

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

        vertices = Topology.Vertices(cell)
        new_vertices = []
        for v in vertices:
            faces = Topology.SuperTopologies(v, hostTopology=cell, topologyType="face")
            normals = []
            for face in faces:
                normal = Vector.SetMagnitude(Face.Normal(face), offset)
                normals.append(normal)
            sum_normal = Vector.Sum(normals)
            new_v = Topology.TranslateByDirectionDistance(v, direction=sum_normal, distance=Vector.Magnitude(sum_normal))
            new_vertices.append(new_v)
        new_cell = Topology.SelfMerge(Topology.ReplaceVertices(cell, Topology.Vertices(cell), new_vertices), tolerance=tolerance)
        return new_cell
    
    @staticmethod
    def ByShell(shell, planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell from the input shell.

        Parameters
        ----------
        shell : topologic_core.Shell
            The input shell. The shell must be closed for this method to succeed.
        planarize : bool, optional
            If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(shell, "Shell"):
            print("Cell.ByShell - Error: The input shell parameter is not a valid topologic shell. Returning None.")
            return None
        faces = Topology.SubTopologies(shell, subTopologyType="face")
        return Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance)
    
    @staticmethod
    def ByThickenedFace(face, thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
                            planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell by thickening the input face.

        Parameters
        ----------
        face : topologic_core.Face
            The input face to be thickened.
        thickness : float , optional
            The desired thickness. The default is 1.0.
        bothSides : bool
            If True, the cell will be lofted to each side of the face. Otherwise, it will be lofted in the direction of the normal to the input face. The default is True.
        reverse : bool
            If True, the cell will be lofted in the opposite direction of the normal to the face. The default is False.
        planarize : bool, optional
            If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

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

        if not Topology.IsInstance(face, "Face"):
            print("Cell.ByThickenedFace - Error: The input face parameter is not a valid topologic face. Returning None.")
            return None
        if reverse == True and bothSides == False:
            thickness = -thickness
        faceNormal = Face.Normal(face)
        if bothSides:
            bottomFace = Topology.Translate(face, -faceNormal[0]*0.5*thickness, -faceNormal[1]*0.5*thickness, -faceNormal[2]*0.5*thickness)
            topFace = Topology.Translate(face, faceNormal[0]*0.5*thickness, faceNormal[1]*0.5*thickness, faceNormal[2]*0.5*thickness)
        else:
            bottomFace = face
            topFace = Topology.Translate(face, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)

        cellFaces = [bottomFace, topFace]
        bottomEdges = Topology.Edges(bottomFace)
        for bottomEdge in bottomEdges:
            topEdge = Topology.Translate(bottomEdge, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)
            sideEdge1 = Edge.ByVertices([bottomEdge.StartVertex(), topEdge.StartVertex()], tolerance=tolerance, silent=True)
            sideEdge2 = Edge.ByVertices([bottomEdge.EndVertex(), topEdge.EndVertex()], tolerance=tolerance, silent=True)
            cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
            cellFaces.append(Face.ByWire(cellWire, tolerance=tolerance))
        return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)

    @staticmethod
    def ByThickenedShell(shell, direction: list = [0, 0, 1], thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
                            planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell by thickening the input shell. The shell must be open.

        Parameters
        ----------
        shell : topologic_core.Shell
            The input shell to be thickened.
        thickness : float , optional
            The desired thickness. The default is 1.0.
        bothSides : bool
            If True, the cell will be lofted to each side of the shell. Otherwise, it will be lofted along the input direction. The default is True.
        reverse : bool
            If True, the cell will be lofted along the opposite of the input direction. The default is False.
        planarize : bool, optional
            If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Shell import Shell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        if not Topology.IsInstance(shell, "Shell"):
            print("Cell.ByThickenedShell - Error: The input shell parameter is not a valid topologic Shell. Returning None.")
            return None
        if reverse == True and bothSides == False:
            thickness = -thickness
        if bothSides:
            bottomShell = Topology.Translate(shell, -direction[0]*0.5*thickness, -direction[1]*0.5*thickness, -direction[2]*0.5*thickness)
            topShell = Topology.Translate(shell, direction[0]*0.5*thickness, direction[1]*0.5*thickness, direction[2]*0.5*thickness)
        else:
            bottomShell = shell
            topShell = Topology.Translate(shell, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
        cellFaces = Shell.Faces(bottomShell) + Shell.Faces(topShell)
        bottomWire = Shell.ExternalBoundary(bottomShell, tolerance=tolerance)
        bottomEdges = Wire.Edges(bottomWire)
        for bottomEdge in bottomEdges:
            topEdge = Topology.Translate(bottomEdge, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
            sideEdge1 = Edge.ByVertices([Edge.StartVertex(bottomEdge), Edge.StartVertex(topEdge)], tolerance=tolerance, silent=True)
            sideEdge2 = Edge.ByVertices([Edge.EndVertex(bottomEdge), Edge.EndVertex(topEdge)], tolerance=tolerance, silent=True)
            cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
            cellFace = Face.ByWire(cellWire, tolerance=tolerance)
            cellFaces.append(cellFace)
        return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
    
    @staticmethod
    def ByWires(wires: list, close: bool = False, triangulate: bool = True, planarize: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent=False):
        """
        Creates a cell by lofting through the input list of wires.

        Parameters
        ----------
        wires : list
            The input list of wires.
        close : bool , optional
            If set to True, the last wire in the list of input wires will be connected to the first wire in the list of input wires. The default is False.
        triangulate : bool , optional
            If set to True, the faces will be triangulated. The default is True.
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        silent : bool , optional
            If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

        Raises
        ------
        Exception
            Raises an exception if the two wires in the list do not have the same number of edges.

        Returns
        -------
        topologic_core.Cell
            The created cell.

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

        if not isinstance(wires, list):
            if not silent:
                print("Cell.ByWires - Error: The input wires parameter is not a valid list. Returning None.")
                return None
        wires = [w for w in wires if Topology.IsInstance(w, "Wire")]
        if len(wires) < 2:
            if not silent:
                print("Cell.ByWires - Error: The input wires parameter contains less than two valid topologic wires. Returning None.")
                return None
        faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
        if close == True:
            faces.append(Face.ByWire(wires[0], 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]
            w1_edges = Topology.Edges(wire1)
            w2_edges = Topology.Edges(wire2)
            if len(w1_edges) != len(w2_edges):
                if not silent:
                    print("Cell.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
                    return None
            if triangulate == True:
                for j in range (len(w1_edges)):
                    e1 = w1_edges[j]
                    e2 = w2_edges[j]
                    e3 = None
                    e4 = None
                    try:
                        e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e4], tolerance=tolerance), tolerance=tolerance))
                        except:
                            pass
                    try:
                        e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance))
                        except:
                            pass
                    if e3 and e4:
                        e5 = Edge.ByVertices([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:
                for j in range (len(w1_edges)):
                    e1 = w1_edges[j]
                    e2 = w2_edges[j]
                    e3 = None
                    e4 = None
                    try:
                        e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                        except:
                            pass
                    try:
                        e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                        except:
                            pass
                    if e3 and e4:
                        try:
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance))
                        except:
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance))
                    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))
        cell = Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance, silent=silent)
        if not cell:
            shell = Shell.ByFaces(faces, tolerance=tolerance)
            if Topology.IsInstance(shell, "Shell"):
                geom = Topology.Geometry(shell, mantissa=mantissa)
                cell = Topology.ByGeometry(geom['vertices'], geom['edges'], geom['faces'])
            if not Topology.IsInstance(cell, "Cell"):
                print("Cell.ByWires - Error: Could not create a cell. Returning None.")
                return None
        return cell

    @staticmethod
    def ByWiresCluster(cluster, close: bool = False, triangulate: bool = True, planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell by lofting through the input cluster of wires.

        Parameters
        ----------
        cluster : Cluster
            The input Cluster of wires.
        close : bool , optional
            If set to True, the last wire in the cluster of input wires will be connected to the first wire in the cluster of input wires. The default is False.
        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.

        Raises
        ------
        Exception
            Raises an exception if the two wires in the list do not have the same number of edges.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cluster, "Cluster"):
            print("Cell.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        wires = Topology.Wires(cluster)
        return Cell.ByWires(wires, close=close, triangulate=triangulate, planarize=planarize, tolerance=tolerance)

    @staticmethod
    def Capsule(origin = None, radius: float = 0.25, height: float = 1, uSides: int = 16, vSidesEnds:int = 8, vSidesMiddle: int = 1, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a capsule shape. A capsule is a cylinder with hemispherical ends.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
        radius : float , optional
            The radius of the capsule. The default is 0.25.
        height : float , optional
            The height of the capsule. The default is 1.
        uSides : int , optional
            The number of circle segments of the capsule. The default is 16.
        vSidesEnds : int , optional
            The number of vertical segments of the end hemispheres. The default is 8.
        vSidesMiddle : int , optional
            The number of vertical segments of the middle cylinder. The default is 1.
        direction : list , optional
            The vector representing the up direction of the capsule. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the capsule. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

            """
        from topologicpy.Topology import Topology
        from topologicpy.Cell import Cell
        from topologicpy.Vertex import Vertex
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Capsule - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        cyl_height = height - radius*2
        if cyl_height <= 0:
            capsule = Cell.Sphere(origin=Vertex.Origin(), radius=radius, uSides= uSides, vSides=vSidesEnds*2)
        else:
            cyl = Cell.Cylinder(origin=Vertex.Origin(),
                                radius=radius,
                                height=cyl_height,
                                uSides=uSides, vSides=vSidesMiddle, direction=[0, 0, 1], placement="center", tolerance=tolerance)
            o1 = Vertex.ByCoordinates(0, 0, cyl_height*0.5)
            o2 = Vertex.ByCoordinates(0, 0, -cyl_height*0.5)
            s1 = Cell.Sphere(origin=o1, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
            s2 = Cell.Sphere(origin=o2, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
            capsule = Topology.Union(cyl, s1, tolerance=tolerance)
            capsule = Topology.Union(capsule, s2, tolerance=tolerance)
        if placement == "bottom":
            capsule = Topology.Translate(capsule, 0, 0, height/2)
        if placement == "lowerleft":
            capsule = Topology.Translate(capsule, 0, 0, height/2)
            capsule = Topology.Translate(capsule, radius, radius)
        
        capsule = Topology.Orient(capsule, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        capsule = Topology.Place(capsule, originA=Vertex.Origin(), originB=origin)
        return capsule

    @staticmethod
    def Compactness(cell, reference = "sphere", mantissa: int = 6) -> float:
        """
        Returns the compactness measure of the input cell. If the reference is "sphere", this is also known as 'sphericity' (https://en.wikipedia.org/wiki/Sphericity).

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        reference : str , optional
            The desired reference to which to compare this compactness. The options are "sphere" and "cube". It is case insensitive. The default is "sphere".
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        float
            The compactness of the input cell.

        """
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology
        import math

        faces = Topology.Faces(cell)
        area = 0.0
        for aFace in faces:
            area = area + abs(Face.Area(aFace))
        volume = abs(Cell.Volume(cell, mantissa=mantissa))
        compactness  = 0
        #From https://en.wikipedia.org/wiki/Sphericity
        if area > 0:
            if reference.lower() == "sphere":
                compactness = (((math.pi)**(1/3))*((6*volume)**(2/3)))/area
            else:
                compactness = 6*(volume**(2/3))/area
        else:
            print("Cell.Compactness - Error: cell surface area is not positive. Returning None.")
            return None
        return round(compactness, mantissa)
    
    @staticmethod
    def Cone(origin = None, baseRadius: float = 0.5, topRadius: float = 0, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
                 dirZ: float = 1, placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a cone.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the cone. The default is None which results in the cone being placed at (0, 0, 0).
        baseRadius : float , optional
            The radius of the base circle of the cone. The default is 0.5.
        topRadius : float , optional
            The radius of the top circle of the cone. The default is 0.
        height : float , optional
            The height of the cone. The default is 1.
        sides : int , optional
            The number of sides of the cone. The default is 16.
        direction : list , optional
            The vector representing the up direction of the cone. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the cone. 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_core.Cell
            The created cone.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        import math

        def createCone(baseWire, topWire, baseVertex, topVertex, tolerance):
            if baseWire == None and topWire == None:
                raise Exception("Cell.Cone - Error: Both radii of the cone cannot be zero at the same time")
            elif baseWire == None:
                apex = baseVertex
                wire = topWire
            elif topWire == None:
                apex = topVertex
                wire = baseWire
            else:
                return Cell.ByWires([baseWire, topWire])
            vertices = Topology.Vertices(wire)
            faces = [Face.ByWire(wire, tolerance=tolerance)]
            for i in range(0, len(vertices)-1):
                w = Wire.ByVertices([apex, vertices[i], vertices[i+1]])
                f = Face.ByWire(w, tolerance=tolerance)
                faces.append(f)
            w = Wire.ByVertices([apex, vertices[-1], vertices[0]])
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
            return Cell.ByFaces(faces, tolerance=tolerance)
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Cone - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            xOffset = 0
            yOffset = 0
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = max(baseRadius, topRadius)
            yOffset = max(baseRadius, topRadius)
            zOffset = 0

        baseZ = origin.Z() + zOffset
        topZ = origin.Z() + zOffset + height
        baseV = []
        topV = []
        for i in range(uSides):
            angle = math.radians(360/uSides)*i
            if baseRadius > 0:
                baseX = math.cos(angle)*baseRadius + origin.X() + xOffset
                baseY = math.sin(angle)*baseRadius + origin.Y() + yOffset
                baseZ = origin.Z() + zOffset
                baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
            if topRadius > 0:
                topX = math.cos(angle)*topRadius + origin.X() + xOffset
                topY = math.sin(angle)*topRadius + origin.Y() + yOffset
                topV.append(Vertex.ByCoordinates(topX,topY,topZ))
        if baseRadius > 0:
            baseWire = Wire.ByVertices(baseV)
        else:
            baseWire = None
        if topRadius > 0:
            topWire = Wire.ByVertices(topV)
        else:
            topWire = None
        baseVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset)
        topVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset+height)
        cone = createCone(baseWire, topWire, baseVertex, topVertex, tolerance)
        if cone == None:
            print("Cell.Cone - Error: Could not create a cone. Returning None.")
            return None
        
        if vSides > 1:
            cutting_planes = []
            baseX = origin.X() + xOffset
            baseY = origin.Y() + yOffset
            size = max(baseRadius, topRadius)*3
            for i in range(1, vSides):
                baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
                tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
                cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
            cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
            shell = Cell.Shells(cone)[0]
            shell = shell.Slice(cutting_planes_cluster)
            cone = Cell.ByShell(shell)
        cone = Topology.Orient(cone, origin=origin, dirA=[0, 0, 1], dirB=direction)
        return cone
  
    @staticmethod
    def ContainmentStatus(cell, vertex, tolerance: float = 0.0001) -> int:
        """
        Returns the containment status of the input vertex in relationship to the input cell

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        vertex : topologic_core.Vertex
            The input vertex.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        int
            Returns 0 if the vertex is inside, 1 if it is on the boundary of, and 2 if it is outside the input cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.ContainmentStatus - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        if not Topology.IsInstance(vertex, "Vertex"):
            print("Cell.ContainmentStatus - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
            return None
        try:
            status = topologic.CellUtility.Contains(cell, vertex, tolerance) # Hook to Core
            if status == 0:
                return 0
            elif status == 1:
                return 1
            else:
                return 2
        except:
            print("Cell.ContainmentStatus - Error: Could not determine containment status. Returning None.")
            return None
 
    @staticmethod
    def Cylinder(origin = None, radius: float = 0.5, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
                     placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a cylinder.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
        radius : float , optional
            The radius of the cylinder. The default is 0.5.
        height : float , optional
            The height of the cylinder. The default is 1.
        uSides : int , optional
            The number of circle segments of the cylinder. The default is 16.
        vSides : int , optional
            The number of vertical segments of the cylinder. The default is 1.
        direction : list , optional
            The vector representing the up direction of the cylinder. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the cylinder. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.CellComplex import CellComplex
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Cylinder - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = radius
            yOffset = radius
        circle_origin = Vertex.ByCoordinates(origin.X() + xOffset, origin.Y() + yOffset, origin.Z() + zOffset)
        
        baseWire = Wire.Circle(origin=circle_origin, radius=radius, sides=uSides, fromAngle=0, toAngle=360, close=True, direction=[0, 0, 1], placement="center", tolerance=tolerance)
        baseFace = Face.ByWire(baseWire, tolerance=tolerance)
        cylinder = Cell.ByThickenedFace(face=baseFace, thickness=height, bothSides=False, reverse=True,
                            tolerance=tolerance)
        if vSides > 1:
            cutting_planes = []
            baseX = origin.X() + xOffset
            baseY = origin.Y() + yOffset
            size = radius*3
            for i in range(1, vSides):
                baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
                tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
                cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
            cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
            cylinder = CellComplex.ExternalBoundary(cylinder.Slice(cutting_planes_cluster))

        cylinder = Topology.Orient(cylinder, origin=origin, dirA=[0, 0, 1], dirB=direction)
        return cylinder
    
    @staticmethod
    def Decompose(cell, tiltAngle: float = 10, tolerance: float = 0.0001) -> dict:
        """
        Decomposes the input cell into its logical components. This method assumes that the positive Z direction is UP.

        Parameters
        ----------
        cell : topologic_core.Cell
            the input cell.
        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. "verticalFaces": list of vertical faces
            2. "topHorizontalFaces": list of top horizontal faces
            3. "bottomHorizontalFaces": list of bottom horizontal faces
            4. "inclinedFaces": list of inclined faces
            5. "verticalApertures": list of vertical apertures
            6. "topHorizontalApertures": list of top horizontal apertures
            7. "bottomHorizontalApertures": list of bottom horizontal apertures
            8. "inclinedApertures": list of 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):
            apTopologies = []
            apertures = Topology.Apertures(topology)
            if isinstance(apertures, list):
                for aperture in apertures:
                    apTopologies.append(Aperture.Topology(aperture))
            return apTopologies

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Decompose - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        verticalFaces = []
        topHorizontalFaces = []
        bottomHorizontalFaces = []
        inclinedFaces = []
        verticalApertures = []
        topHorizontalApertures = []
        bottomHorizontalApertures = []
        inclinedApertures = []
        tiltAngle = abs(tiltAngle)
        faces = Cell.Faces(cell)
        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)

            if aCode == 0:
                verticalFaces.append(aFace)
                verticalApertures += getApertures(aFace)
            elif aCode == 1:
                if abs(aFace.Centroid().Z() - zMin) < tolerance:
                    bottomHorizontalFaces.append(aFace)
                    bottomHorizontalApertures += getApertures(aFace)
                else:
                    topHorizontalFaces.append(aFace)
                    topHorizontalApertures += getApertures(aFace)
            elif aCode == 2:
                if abs(aFace.Centroid().Z() - zMax) < tolerance:
                    topHorizontalFaces.append(aFace)
                    topHorizontalApertures += getApertures(aFace)
                else:
                    bottomHorizontalFaces.append(aFace)
                    bottomHorizontalApertures += getApertures(aFace)
            elif aCode == 3:
                inclinedFaces.append(aFace)
                inclinedApertures += getApertures(aFace)
        d = {
            "verticalFaces" : verticalFaces,
            "topHorizontalFaces" : topHorizontalFaces,
            "bottomHorizontalFaces" : bottomHorizontalFaces,
            "inclinedFaces" : inclinedFaces,
            "verticalApertures" : verticalApertures,
            "topHorizontalApertures" : topHorizontalApertures,
            "bottomHorizontalApertures" : bottomHorizontalApertures,
            "inclinedApertures" : inclinedApertures
            }
        return d

    @staticmethod
    def Dodecahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates a dodecahedron. See https://en.wikipedia.org/wiki/Dodecahedron.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the dodecahedron. The default is None which results in the dodecahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the dodecahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the dodecahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the dodecahedron. 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_core.Cell
            The created dodecahedron.

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

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        pen = Face.Circle(sides=5, radius=0.5)
        pentagons = [pen]
        edges = Topology.Edges(pen)
        for edge in edges:
            o = Topology.Centroid(edge)
            e_dir = Edge.Direction(edge)
            pentagons.append(Topology.Rotate(pen, origin=o, axis=e_dir, angle=116.565))

        cluster = Cluster.ByTopologies(pentagons)
        
        cluster2 = Topology.Rotate(cluster, origin=Vertex.Origin(), axis=[1, 0, 0], angle=180)
        cluster2 = Topology.Rotate(cluster2, origin=Vertex.Origin(), axis=[0, 0, 1], angle=36)
        vertices = Topology.Vertices(cluster2)
        zList = [Vertex.Z(v) for v in vertices]
        zList = list(set(zList))
        zList.sort()
        zoffset1 = zList[-1] - zList[0]
        zoffset2 = zList[1] - zList[0]
        cluster2 = Topology.Translate(cluster2, 0, 0, -zoffset1-zoffset2)
        pentagons += Topology.Faces(cluster2)
        dodecahedron = Cell.ByFaces(pentagons, tolerance=tolerance)
        centroid = Topology.Centroid(dodecahedron)
        dodecahedron = Topology.Translate(dodecahedron, -Vertex.X(centroid), -Vertex.Y(centroid), -Vertex.Z(centroid))
        vertices = Topology.Vertices(dodecahedron)
        d = Vertex.Distance(Vertex.Origin(), vertices[0])
        dodecahedron = Topology.Scale(dodecahedron, origin=Vertex.Origin(), x=radius/d, y=radius/d, z=radius/d)
        if placement == "bottom":
            dodecahedron = Topology.Translate(dodecahedron, 0, 0, radius)
        elif placement == "lowerleft":
            dodecahedron = Topology.Translate(dodecahedron, radius, radius, radius)
        
        dodecahedron = Topology.Orient(dodecahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        dodecahedron = Topology.Place(dodecahedron, originA=Vertex.Origin(), originB=origin)
        return dodecahedron

    @staticmethod
    def Edges(cell) -> list:
        """
        Returns the edges of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Edges - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        edges = Topology.Edges(cell)
        return edges

    @staticmethod
    def Egg(origin= None, height: float = 1.0, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
                   placement: str = "center", tolerance: float = 0.0001):
        """
        Creates an egg-shaped cell.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the sphere. The default is None which results in the egg-shaped cell being placed at (0, 0, 0).
        height : float , optional
            The desired height of of the egg-shaped cell. The default is 1.0.
        uSides : int , optional
            The desired number of sides along the longitude of the egg-shaped cell. The default is 16.
        vSides : int , optional
            The desired number of sides along the latitude of the egg-shaped cell. The default is 8.
        direction : list , optional
            The vector representing the up direction of the egg-shaped cell. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the egg-shaped cell. 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_core.Cell
            The created egg-shaped cell.

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology
        from topologicpy.Dictionary import Dictionary

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        
        coords = [[0.0, 0.0, -0.5],
                 [0.074748, 0.0, -0.494015],
                 [0.140819, 0.0, -0.473222],
                 [0.204118, 0.0, -0.438358],
                 [0.259512, 0.0, -0.391913],
                 [0.304837, 0.0, -0.335519],
                 [0.338649, 0.0, -0.271416],
                 [0.361307, 0.0, -0.202039],
                 [0.375678, 0.0, -0.129109],
                 [0.381294, 0.0, -0.053696],
                 [0.377694, 0.0, 0.019874],
                 [0.365135, 0.0, 0.091978],
                 [0.341482, 0.0, 0.173973],
                 [0.300154, 0.0, 0.276001],
                 [0.252928, 0.0, 0.355989],
                 [0.206605, 0.0, 0.405813],
                 [0.157529, 0.0, 0.442299],
                 [0.10604, 0.0, 0.472092],
                 [0.05547, 0.0, 0.491784],
                 [0.0, 0.0, 0.5]]
        verts = [Vertex.ByCoordinates(coord) for coord in coords]
        c = Wire.ByVertices(verts, close=False)
        new_verts = []
        for i in range(vSides+1):
            new_verts.append(Wire.VertexByParameter(c, i/vSides))
        c = Wire.ByVertices(new_verts, close=False)
        egg = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
        if Topology.IsInstance(egg, "CellComplex"):
            egg = egg.ExternalBoundary()
        if Topology.IsInstance(egg, "Shell"):
            egg = Cell.ByShell(egg)
        egg = Topology.Scale(egg, origin=Vertex.Origin(), x=height, y=height, z=height)
        if placement.lower() == "bottom":
            egg = Topology.Translate(egg, 0, 0, height/2)
        elif placement.lower() == "lowerleft":
            bb = Cell.BoundingBox(egg)
            d = Topology.Dictionary(bb)
            width = Dictionary.ValueAtKey(d, 'width')
            length = Dictionary.ValueAtKey(d, 'length')
            egg = Topology.Translate(egg, width*0.5, length*0.5, height*0.5)
        egg = Topology.Orient(egg, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        egg = Topology.Place(egg, originA=Vertex.Origin(), originB=origin)
        return egg
    
    @staticmethod
    def ExternalBoundary(cell):
        """
        Returns the external boundary of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

        Returns
        -------
        topologic_core.Shell
            The external boundary of the input cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.ExternalBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        try:
            return cell.ExternalBoundary()
        except:
            print("Cell.ExternalBoundary - Error: Could not compute the external boundary. Returning None.")
            return None
    
    @staticmethod
    def Faces(cell) -> list:
        """
        Returns the faces of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Faces - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        faces = Topology.Faces(cell)
        return faces

    @staticmethod
    def Hyperboloid(origin = None, baseRadius: float = 0.5, topRadius: float = 0.5, height: float = 1, sides: int = 24, direction: list = [0, 0, 1],
                        twist: float = 60, placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a hyperboloid.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the hyperboloid. The default is None which results in the hyperboloid being placed at (0, 0, 0).
        baseRadius : float , optional
            The radius of the base circle of the hyperboloid. The default is 0.5.
        topRadius : float , optional
            The radius of the top circle of the hyperboloid. The default is 0.5.
        height : float , optional
            The height of the cone. The default is 1.
        sides : int , optional
            The number of sides of the cone. The default is 24.
        direction : list , optional
            The vector representing the up direction of the hyperboloid. The default is [0, 0, 1].
        twist : float , optional
            The angle to twist the base cylinder. The default is 60.
        placement : str , optional
            The description of the placement of the origin of the hyperboloid. 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_core.Cell
            The created hyperboloid.

        """
        from topologicpy.Cluster import Cluster
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology
        import math

        def createHyperboloid(baseVertices, topVertices, tolerance):
            baseWire = Wire.ByVertices(baseVertices, close=True)
            topWire = Wire.ByVertices(topVertices, close=True)
            baseFace = Face.ByWire(baseWire, tolerance=tolerance)
            topFace = Face.ByWire(topWire, tolerance=tolerance)
            faces = [baseFace, topFace]
            for i in range(0, len(baseVertices)-1):
                w = Wire.ByVertices([baseVertices[i], topVertices[i], topVertices[i+1]], close=True)
                f = Face.ByWire(w, tolerance=tolerance)
                faces.append(f)
                w = Wire.ByVertices([baseVertices[i+1], baseVertices[i], topVertices[i+1]], close=True)
                f = Face.ByWire(w, tolerance=tolerance)
                faces.append(f)
            w = Wire.ByVertices([baseVertices[-1], topVertices[-1], topVertices[0]], close=True)
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
            w = Wire.ByVertices([baseVertices[0], baseVertices[-1], topVertices[0]], close=True)
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
            returnTopology = Cell.ByFaces(faces, tolerance=tolerance)
            if returnTopology == None:
                returnTopology = Cluster.ByTopologies(faces)
            return returnTopology
        
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Hyperboloid - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        w_origin = Vertex.Origin()
        baseV = []
        topV = []
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = max(baseRadius, topRadius)
            yOffset = max(baseRadius, topRadius)
        baseZ = w_origin.Z() + zOffset
        topZ = w_origin.Z() + zOffset + height
        for i in range(sides):
            angle = math.radians(360/sides)*i
            if baseRadius > 0:
                baseX = math.sin(angle+math.radians(twist))*baseRadius + w_origin.X() + xOffset
                baseY = math.cos(angle+math.radians(twist))*baseRadius + w_origin.Y() + yOffset
                baseZ = w_origin.Z() + zOffset
                baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
            if topRadius > 0:
                topX = math.sin(angle-math.radians(twist))*topRadius + w_origin.X() + xOffset
                topY = math.cos(angle-math.radians(twist))*topRadius + w_origin.Y() + yOffset
                topV.append(Vertex.ByCoordinates(topX,topY,topZ))

        hyperboloid = createHyperboloid(baseV, topV, tolerance)
        if hyperboloid == None:
            print("Cell.Hyperboloid - Error: Could not create a hyperboloid. Returning None.")
            return None
        
        hyperboloid = Topology.Orient(hyperboloid, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        hyperboloid = Topology.Place(hyperboloid, originA=Vertex.Origin(), originB=origin)
        return hyperboloid
    
    @staticmethod
    def Icosahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates an icosahedron. See https://en.wikipedia.org/wiki/Icosahedron.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the icosahedron. The default is None which results in the icosahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the icosahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the icosahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the icosahedron. 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_core.Cell
            The created icosahedron.

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

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        rect1 = Wire.Rectangle(width=(1+math.sqrt(5))/2, length=1)
        rect2 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
        rect2 = Topology.Rotate(rect2, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
        rect3 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
        rect3 = Topology.Rotate(rect3, origin=Vertex.Origin(), axis=[0, 1, 0], angle=90)
        vertices = Topology.Vertices(rect1)
        v1, v2, v3, v4 = vertices
        vertices = Topology.Vertices(rect2)
        v5, v6, v7, v8 = vertices
        vertices = Topology.Vertices(rect3)
        v9, v10, v11, v12 = vertices
        f1 = Face.ByVertices([v1,v8,v4])
        f2 = Face.ByVertices([v1,v4,v5])
        f3 = Face.ByVertices([v3,v2,v6])
        f4 = Face.ByVertices([v2,v3,v7])
        f5 = Face.ByVertices([v10,v9,v2])
        f6 = Face.ByVertices([v10,v9,v1])
        f7 = Face.ByVertices([v12,v11,v4])
        f8 = Face.ByVertices([v12,v11,v3])
        f9 = Face.ByVertices([v8,v7,v9])
        f10 = Face.ByVertices([v8,v7,v12])
        f11 = Face.ByVertices([v5,v6,v10])
        f12 = Face.ByVertices([v5,v6,v11])
        f13 = Face.ByVertices([v8,v1,v9])
        f14 = Face.ByVertices([v9,v2,v7])
        f15 = Face.ByVertices([v7,v3,v12])
        f16 = Face.ByVertices([v8,v12,v4])
        f17 = Face.ByVertices([v1,v5,v10])
        f18 = Face.ByVertices([v10,v2,v6])
        f19 = Face.ByVertices([v6,v3,v11])
        f20 = Face.ByVertices([v11,v4,v5])

        icosahedron = Cell.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,
                        f11,f12,f13,f14,f15,f16,f17,f18,f19,f20], tolerance=tolerance)
        sf = 1.051*0.5 # To insribe it in a sphere of radius 0.5
        icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
        sf = radius/0.5
        icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
        if placement == "bottom":
            icosahedron = Topology.Translate(icosahedron, 0, 0, radius)
        elif placement == "lowerleft":
            icosahedron = Topology.Translate(icosahedron, radius, radius, radius)
        
        icosahedron = Topology.Orient(icosahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        icosahedron = Topology.Place(icosahedron, originA=Vertex.Origin(), originB=origin)
        return icosahedron


    @staticmethod
    def InternalBoundaries(cell) -> list:
        """
        Returns the internal boundaries of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

        Returns
        -------
        list
            The list of internal boundaries ([topologic_core.Shell]).

        """
        shells = []
        _ = cell.InternalBoundaries(shells) # Hook to Core
        return shells
    
    @staticmethod
    def InternalVertex(cell, tolerance: float = 0.0001):
        """
        Creates a vertex that is guaranteed to be inside the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Vertex
            The internal vertex.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.InternalVertex - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        try:
            return topologic.CellUtility.InternalVertex(cell, tolerance) # Hook to Core
        except:
            print("Cell.InternalVertex - Error: Could not create an internal vertex. Returning None.")
            return None
    
    @staticmethod
    def IsOnBoundary(cell, vertex, tolerance: float = 0.0001) -> bool:
        """
        Returns True if the input vertex is on the boundary of the input cell. Returns False otherwise.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        vertex : topologic_core.Vertex
            The input vertex.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        bool
            Returns True if the input vertex is inside the input cell. Returns False otherwise.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.IsOnBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        if not Topology.IsInstance(vertex, "Vertex"):
            print("Cell.IsOnBoundary - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
            return None
        try:
            return (Cell.ContainmentStatus(cell, vertex, tolerance = tolerance) == 1)
        except:
            print("Cell.IsOnBoundary - Error: Could not determine if the input vertex is on the boundary of the input cell. Returning None.")
            return None
    
    @staticmethod
    def Octahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.

        Parameters
        ----------
        origin : topologic_core.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_core.Cell
            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 Topology.IsInstance(origin, "Vertex"):
            print("Cell.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])

        octahedron = Cell.ByFaces([f1, f2, f3, f4, f5, f6, f7, f8], 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 Pipe(edge, profile = None, radius: float = 0.5, sides: int = 16, startOffset: float = 0, endOffset: float = 0, endcapA = None, endcapB = None) -> dict:
        """
        Description
        ----------
        Creates a pipe along the input edge.

        Parameters
        ----------
        edge : topologic_core.Edge
            The centerline of the pipe.
        profile : topologic_core.Wire , optional
            The profile of the pipe. It is assumed that the profile is in the XY plane. If set to None, a circle of radius 0.5 will be used. The default is None.
        radius : float , optional
            The radius of the pipe. The default is 0.5.
        sides : int , optional
            The number of sides of the pipe. The default is 16.
        startOffset : float , optional
            The offset distance from the start vertex of the centerline edge. The default is 0.
        endOffset : float , optional
            The offset distance from the end vertex of the centerline edge. The default is 0.
        endcapA, optional
            The topology to place at the start vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the direction of the centerline edge.
        endcapB, optional
            The topology to place at the end vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the inverse direction of the centerline edge.

        Returns
        -------
        dict
            A dictionary containing the pipe, the start endcap, and the end endcap if they have been specified. The dictionary has the following keys:
            'pipe'
            'endcapA'
            'endcapB'

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology
        import math

        if not Topology.IsInstance(edge, "Edge"):
            print("Cell.Pipe - Error: The input edge parameter is not a valid topologic edge. Returning None.")
            return None
        length = Edge.Length(edge)
        origin = Edge.StartVertex(edge)
        startU = startOffset / length
        endU = 1.0 - (endOffset / length)
        sv = Edge.VertexByParameter(edge, startU)
        ev = Edge.VertexByParameter(edge, endU)
        x1 = sv.X()
        y1 = sv.Y()
        z1 = sv.Z()
        x2 = ev.X()
        y2 = ev.Y()
        z2 = ev.Z()
        dx = x2 - x1
        dy = y2 - y1
        dz = z2 - z1
        dist = math.sqrt(dx**2 + dy**2 + dz**2)
        baseV = []
        topV = []

        if Topology.IsInstance(profile, "Wire"):
            baseWire = Topology.Translate(profile, 0 , 0, sv.Z())
            topWire = Topology.Translate(profile, 0 , 0, sv.Z()+dist)
        else:
            for i in range(sides):
                angle = math.radians(360/sides)*i
                x = math.sin(angle)*radius + sv.X()
                y = math.cos(angle)*radius + sv.Y()
                z = sv.Z()
                baseV.append(Vertex.ByCoordinates(x, y, z))
                topV.append(Vertex.ByCoordinates(x, y, z+dist))

            baseWire = Wire.ByVertices(baseV)
            topWire = Wire.ByVertices(topV)
        wires = [baseWire, topWire]
        pipe = Cell.ByWires(wires)
        phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
        if dist < 0.0001:
            theta = 0
        else:
            theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
        pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 1, 0], angle=theta)
        pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 0, 1], angle=phi)
        zzz = Vertex.ByCoordinates(0, 0, 0)
        if endcapA:
            origin = edge.StartVertex()
            x1 = origin.X()
            y1 = origin.Y()
            z1 = origin.Z()
            x2 = edge.EndVertex().X()
            y2 = edge.EndVertex().Y()
            z2 = edge.EndVertex().Z()
            dx = x2 - x1
            dy = y2 - y1
            dz = z2 - z1    
            dist = math.sqrt(dx**2 + dy**2 + dz**2)
            phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
            if dist < 0.0001:
                theta = 0
            else:
                theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
            endcapA = Topology.Copy(endcapA)
            endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 1, 0], angle=theta)
            endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 0, 1], angle=phi+180)
            endcapA = Topology.Translate(endcapA, origin.X(), origin.Y(), origin.Z())
        if endcapB:
            origin = edge.EndVertex()
            x1 = origin.X()
            y1 = origin.Y()
            z1 = origin.Z()
            x2 = edge.StartVertex().X()
            y2 = edge.StartVertex().Y()
            z2 = edge.StartVertex().Z()
            dx = x2 - x1
            dy = y2 - y1
            dz = z2 - z1    
            dist = math.sqrt(dx**2 + dy**2 + dz**2)
            phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
            if dist < 0.0001:
                theta = 0
            else:
                theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
            endcapB = Topology.Copy(endcapB)
            endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 1, 0], angle=theta)
            endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 0, 1], angle=phi+180)
            endcapB = Topology.Translate(endcapB, origin.X(), origin.Y(), origin.Z())
        return {'pipe': pipe, 'endcapA': endcapA, 'endcapB': endcapB}
    
    @staticmethod
    def Prism(origin= None, width: float = 1, length: float = 1, height: float = 1, uSides: int = 1, vSides: int = 1, wSides: int = 1,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates a prism.

        Parameters
        ----------
        origin : topologic_core.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_core.Cell
            The created prism.

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

        def sliceCell(cell, width, length, height, uSides, vSides, wSides):
            origin = Topology.Centroid(cell)
            shells = Topology.Shells(cell)
            shell = shells[0]
            wRect = Wire.Rectangle(origin=origin, width=width*1.2, length=length*1.2, direction=[0, 0, 1], placement="center")
            sliceFaces = []
            for i in range(1, wSides):
                sliceFaces.append(Topology.Translate(Face.ByWire(wRect, tolerance=tolerance), 0, 0, height/wSides*i - height*0.5))
            uRect = Wire.Rectangle(origin=origin, width=height*1.2, length=length*1.2, direction=[1, 0, 0], placement="center")
            for i in range(1, uSides):
                sliceFaces.append(Topology.Translate(Face.ByWire(uRect, tolerance=tolerance), width/uSides*i - width*0.5, 0, 0))
            vRect = Wire.Rectangle(origin=origin, width=height*1.2, length=width*1.2, direction=[0, 1, 0], placement="center")
            for i in range(1, vSides):
                sliceFaces.append(Topology.Translate(Face.ByWire(vRect, tolerance=tolerance), 0, length/vSides*i - length*0.5, 0))
            if len(sliceFaces) > 0:
                sliceCluster = Cluster.ByTopologies(sliceFaces)
                shell = Topology.Slice(topologyA=shell, topologyB=sliceCluster, tranDict=False, tolerance=tolerance)
                return Cell.ByShell(shell)
            return cell
        
        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 Topology.IsInstance(origin, "Vertex"):
            print("Cell.Prism - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = width*0.5
            yOffset = length*0.5
        vb1 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
        vb2 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
        vb3 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)
        vb4 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)

        baseWire = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True)
        baseFace = Face.ByWire(baseWire, tolerance=tolerance)

        prism = Cell.ByThickenedFace(baseFace, thickness=height, bothSides = False)

        if uSides > 1 or vSides > 1 or wSides > 1:
            prism = sliceCell(prism, width, length, height, uSides, vSides, wSides)
        prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        return prism

    @staticmethod
    def RemoveCollinearEdges(cell, angTolerance: float = 0.1, tolerance: float = 0.0001):
        """
        Removes any collinear edges in the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        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_core.Cell
            The created cell without any collinear edges.

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

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.RemoveCollinearEdges - Error: The input cell parameter is not a valid cell. Returning None.")
            return None
        faces = Cell.Faces(cell)
        clean_faces = []
        for face in faces:
            clean_faces.append(Face.RemoveCollinearEdges(face, angTolerance=angTolerance, tolerance=tolerance))
        return Cell.ByFaces(clean_faces, tolerance=tolerance)
    
    @staticmethod
    def Roof(face, angle: float = 45, epsilon: float = 0.01 , tolerance: float = 0.001):
        """
            Creates a hipped roof through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao <gaoxipeng1998@gmail.com>
            This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel

        Parameters
        ----------
        face : topologic_core.Face
            The input face.
        angle : float , optioal
            The desired angle in degrees of the roof. The default is 45.
        epsilon : float , optional
            The desired epsilon (another form of tolerance for distance from plane). The default is 0.01. (This is set to a larger number as it was found to work better)
        tolerance : float , optional
            The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)

        Returns
        -------
        cell
            The created roof.

        """
        from topologicpy.Shell import Shell
        from topologicpy.Cell import Cell
        from topologicpy.Topology import Topology
        
        shell = Shell.Roof(face=face, angle=angle, epsilon=epsilon, tolerance=tolerance)
        faces = Topology.Faces(shell) + [face]
        cell = Cell.ByFaces(faces, tolerance=tolerance)
        if not cell:
            print("Cell.Roof - Error: Could not create a roof cell. Returning None.")
            return None
        return cell

    @staticmethod
    def Sets(cells: list, superCells: list, tolerance: float = 0.0001) -> list:
        """
            Classifies the input cells into sets based on their enclosure within the input list of super cells. The order of the sets follows the order of the input list of super cells.

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

        Returns
        -------
        list
            The classified list of input cells based on their encolsure within the input list of super cells.

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Topology import Topology

        if not isinstance(cells, list):
            print("Cell.Sets - Error: The input cells parameter is not a valid list. Returning None.")
            return None
        if not isinstance(superCells, list):
            print("Cell.Sets - Error: The input superCells parameter is not a valid list. Returning None.")
            return None
        cells = [c for c in cells if Topology.IsInstance(c, "Cell")]
        if len(cells) < 1:
            print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
            return None
        superCells = [c for c in superCells if Topology.IsInstance(c, "Cell")]
        if len(cells) < 1:
            print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
            return None
        if len(superCells) == 0:
            cluster = cells[0]
            for i in range(1, len(cells)):
                oldCluster = cluster
                cluster = cluster.Union(cells[i])
                del oldCluster
            superCells = Topology.Cells(cluster)
        unused = []
        for i in range(len(cells)):
            unused.append(True)
        sets = []
        for i in range(len(superCells)):
            sets.append([])
        for i in range(len(cells)):
            if unused[i]:
                iv = Topology.InternalVertex(cells[i], tolerance=tolerance)
                for j in range(len(superCells)):
                    if (Vertex.IsInternal(iv, superCells[j], tolerance)):
                        sets[j].append(cells[i])
                        unused[i] = False
        return sets
    
    @staticmethod
    def Shells(cell) -> list:
        """
        Returns the shells of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Shells - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        shells = []
        _ = cell.Shells(None, shells) # Hook to Core
        return shells

    @staticmethod
    def Sphere(origin= None, radius: float = 0.5, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
                   placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a sphere.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the sphere. The default is None which results in the sphere being placed at (0, 0, 0).
        radius : float , optional
            The radius of the sphere. The default is 0.5.
        uSides : int , optional
            The number of sides along the longitude of the sphere. The default is 16.
        vSides : int , optional
            The number of sides along the latitude of the sphere. The default is 8.
        direction : list , optional
            The vector representing the up direction of the sphere. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the sphere. 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_core.Cell
            The created sphere.

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        c = Wire.Circle(origin=Vertex.Origin(), radius=radius, sides=vSides, fromAngle=-90, toAngle=90, close=False, direction=[0, 0, 1], placement="center")
        c = Topology.Rotate(c, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
        sphere = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
        if Topology.Type(sphere) == Topology.TypeID("CellComplex"):
            sphere = sphere.ExternalBoundary()
        if Topology.Type(sphere) == Topology.TypeID("Shell"):
            sphere = Cell.ByShell(sphere)
        if placement.lower() == "bottom":
            sphere = Topology.Translate(sphere, 0, 0, radius)
        elif placement.lower() == "lowerleft":
            sphere = Topology.Translate(sphere, radius, radius, radius)
        sphere = Topology.Orient(sphere, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        sphere = Topology.Place(sphere, originA=Vertex.Origin(), originB=origin)
        return sphere
    
    @staticmethod
    def SurfaceArea(cell, mantissa: int = 6) -> float:
        """
        Returns the surface area of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The cell.
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        area : float
            The surface area of the input cell.

        """
        return Cell.Area(cell=cell, mantissa=mantissa)
    
    @staticmethod
    def Tetrahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates a tetrahedron. See https://en.wikipedia.org/wiki/Tetrahedron.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the tetrahedron. The default is None which results in the tetrahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the tetrahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the tetrahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the tetrahedron. 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_core.Cell
            The created tetrahedron.

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

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Tetrahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None

        vb1 = Vertex.ByCoordinates(math.sqrt(8/9), 0, -1/3)
        vb2 = Vertex.ByCoordinates(-math.sqrt(2/9), math.sqrt(2/3), -1/3)
        vb3 = Vertex.ByCoordinates(-math.sqrt(2/9), -math.sqrt(2/3), -1/3)
        vb4 = Vertex.ByCoordinates(0, 0, 1)
        f1 = Face.ByVertices([vb1, vb2, vb3])
        f2 = Face.ByVertices([vb4, vb1, vb2])
        f3 = Face.ByVertices([vb4, vb2, vb3])
        f4 = Face.ByVertices([vb4, vb3, vb1])
        tetrahedron = Cell.ByFaces([f1, f2, f3, f4])
        tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=0.5, y=0.5, z=0.5)
        tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)

        if placement.lower() == "lowerleft":
            tetrahedron = Topology.Translate(tetrahedron, radius, radius, radius)
        elif placement.lower() == "bottom":
            tetrahedron = Topology.Translate(tetrahedron, 0, 0, radius)
        tetrahedron = Topology.Place(tetrahedron, originA=Vertex.Origin(), originB=origin)
        tetrahedron = Topology.Orient(tetrahedron, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        return tetrahedron
    
    @staticmethod
    def Torus(origin= None, majorRadius: float = 0.5, minorRadius: float = 0.125, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a torus.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the torus. The default is None which results in the torus being placed at (0, 0, 0).
        majorRadius : float , optional
            The major radius of the torus. The default is 0.5.
        minorRadius : float , optional
            The minor radius of the torus. The default is 0.1.
        uSides : int , optional
            The number of sides along the longitude of the torus. The default is 16.
        vSides : int , optional
            The number of sides along the latitude of the torus. The default is 8.
        direction : list , optional
            The vector representing the up direction of the torus. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the torus. 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_core.Cell
            The created torus.

        """
        
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology
        
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Torus - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        c = Wire.Circle(origin=Vertex.Origin(), radius=minorRadius, sides=vSides, fromAngle=0, toAngle=360, close=False, direction=[0, 1, 0], placement="center")
        c = Topology.Translate(c, abs(majorRadius-minorRadius), 0, 0)
        torus = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
        if Topology.Type(torus) == Topology.TypeID("Shell"):
            torus = Cell.ByShell(torus)
        if placement.lower() == "bottom":
            torus = Topology.Translate(torus, 0, 0, minorRadius)
        elif placement.lower() == "lowerleft":
            torus = Topology.Translate(torus, majorRadius, majorRadius, minorRadius)

        torus = Topology.Orient(torus, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        torus = Topology.Place(torus, originA=Vertex.Origin(), originB=origin)
        return torus
    
    @staticmethod
    def Vertices(cell) -> list:
        """
        Returns the vertices of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Vertices - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        vertices = []
        _ = cell.Vertices(None, vertices) # Hook to Core
        return vertices

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

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        manitssa: int , optional
            The desired length of the mantissa. The default is 6.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Volume - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        volume = None
        try:
            volume = round(topologic.CellUtility.Volume(cell), mantissa) # Hook to Core
        except:
            print("Cell.Volume - Error: Could not compute the volume of the input cell. Returning None.")
            volume = None
        return volume

    @staticmethod
    def Wires(cell) -> list:
        """
        Returns the wires of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Wires - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        wires = []
        _ = cell.Wires(None, wires) # Hook to Core
        return wires

Classes

class Cell
Expand source code
class Cell():
    @staticmethod
    def Area(cell, mantissa: int = 6):
        """
        Returns the surface area of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The cell.
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        float
            The surface area of the input cell.

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

        faces = Topology.Faces(cell)
        area = 0.0
        for aFace in faces:
            area = area + Face.Area(aFace)
        return round(area, mantissa)

    @staticmethod
    def Box(origin = None,
            width: float = 1, length: float = 1, height: float = 1,
            uSides: int = 1, vSides: int = 1, wSides: int = 1,
            direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Creates a box.

        Parameters
        ----------
        origin : topologic_core.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_core.Cell
            The created box.

        """
        return Cell.Prism(origin=origin, width=width, length=length, height=height,
                          uSides=uSides, vSides=vSides, wSides=wSides,
                          direction=direction, placement=placement, tolerance=tolerance)

    @staticmethod
    def ByFaces(faces: list, planarize: bool = False, tolerance: float = 0.0001, silent=False):
        """
        Creates a cell from the input list of faces.

        Parameters
        ----------
        faces : list
            The input list of faces.
        planarize : bool, optional
            If set to True, the input faces are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        silent : bool , optional
            If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

        Returns
        -------
        topologic_core.Cell
            The created cell.

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

        if not isinstance(faces, list):
            if not silent:
                print("Cell.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
                return None
        faceList = [x for x in faces if Topology.IsInstance(x, "Face")]
        if len(faceList) < 1:
            if not silent:
                print("Cell.ByFaces - Error: The input faces parameter does not contain valid faces. Returning None.")
                return None
        # Try the default method
        cell = topologic.Cell.ByFaces(faceList, tolerance) # Hook to Core
        if Topology.IsInstance(cell, "Cell"):
            return cell
        
        # Fuse all the vertices first and rebuild the faces
        all_vertices = []
        wires = []
        for f in faceList:
            w = Face.Wire(f)
            wires.append(w)
            all_vertices += Topology.Vertices(w)
        all_vertices = Vertex.Fuse(all_vertices, tolerance=tolerance)
        new_faces = []
        for w in wires:
            face_vertices = []
            for v in Topology.Vertices(w):
                index = Vertex.Index(v, all_vertices, tolerance=tolerance)
                if not index == None:
                    face_vertices.append(all_vertices[index])
            new_w = Wire.ByVertices(face_vertices)
            if Topology.IsInstance(new_w, "Wire"):
                new_f = Face.ByWire(new_w, silent=True)
                if Topology.IsInstance(new_f, "Face"):
                    new_faces.append(new_f)
                elif isinstance(new_f, list):
                    new_faces += new_f
        faceList = new_faces
        planarizedList = []
        enlargedList = []
        if planarize:
            planarizedList = [Face.Planarize(f, tolerance=tolerance) for f in faceList]
            enlargedList = [Face.ByOffset(f, offset=-tolerance*10) for f in planarizedList]
            cell = topologic.Cell.ByFaces(enlargedList, tolerance) # Hook to Core
            faceList = Topology.SubTopologies(cell, subTopologyType="face")
            finalFaces = []
            for f in faceList:
                centroid = Topology.Centroid(f)
                n = Face.Normal(f)
                v = Topology.Translate(centroid, n[0]*0.01, n[1]*0.01, n[2]*0.01)
                if not Vertex.IsInternal(v, cell):
                    finalFaces.append(f)
            finalFinalFaces = []
            for f in finalFaces:
                vertices = Face.Vertices(f)
                w = Wire.Cycles(Face.ExternalBoundary(f), maxVertices=len(vertices))[0]
                f1 = Face.ByWire(w, tolerance=tolerance, silent=True)
                if Topology.IsInstance(f1, "Face"):
                    finalFinalFaces.append(f1)
                elif isinstance(f1, list):
                    finalFinalFaces += f1
            cell = topologic.Cell.ByFaces(finalFinalFaces, tolerance) # Hook to Core
            if cell == None:
                if not silent:
                    print("Cell.ByFaces - Error: The operation failed. Returning None.")
                    return None
            else:
                return cell
        else:
            cell = topologic.Cell.ByFaces(faces, tolerance) # Hook to Core
            if cell == None:
                if not silent:
                    print("Cell.ByFaces - Error: The operation failed. Returning None.")
                    return None
            else:
                return cell
    @staticmethod
    def ByOffset(cell, offset: float = 1.0, tolerance: float = 0.0001):
        """
        Creates an offset cell from the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        offset : float , optional
            The desired offset distance. The default is 1.0.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        
        Returns
        -------
        Topology
            The created offset topology. WARNING: This method may fail to create a cell if the offset creates self-intersecting faces. Always check the type being returned by this method.

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

        vertices = Topology.Vertices(cell)
        new_vertices = []
        for v in vertices:
            faces = Topology.SuperTopologies(v, hostTopology=cell, topologyType="face")
            normals = []
            for face in faces:
                normal = Vector.SetMagnitude(Face.Normal(face), offset)
                normals.append(normal)
            sum_normal = Vector.Sum(normals)
            new_v = Topology.TranslateByDirectionDistance(v, direction=sum_normal, distance=Vector.Magnitude(sum_normal))
            new_vertices.append(new_v)
        new_cell = Topology.SelfMerge(Topology.ReplaceVertices(cell, Topology.Vertices(cell), new_vertices), tolerance=tolerance)
        return new_cell
    
    @staticmethod
    def ByShell(shell, planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell from the input shell.

        Parameters
        ----------
        shell : topologic_core.Shell
            The input shell. The shell must be closed for this method to succeed.
        planarize : bool, optional
            If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(shell, "Shell"):
            print("Cell.ByShell - Error: The input shell parameter is not a valid topologic shell. Returning None.")
            return None
        faces = Topology.SubTopologies(shell, subTopologyType="face")
        return Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance)
    
    @staticmethod
    def ByThickenedFace(face, thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
                            planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell by thickening the input face.

        Parameters
        ----------
        face : topologic_core.Face
            The input face to be thickened.
        thickness : float , optional
            The desired thickness. The default is 1.0.
        bothSides : bool
            If True, the cell will be lofted to each side of the face. Otherwise, it will be lofted in the direction of the normal to the input face. The default is True.
        reverse : bool
            If True, the cell will be lofted in the opposite direction of the normal to the face. The default is False.
        planarize : bool, optional
            If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

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

        if not Topology.IsInstance(face, "Face"):
            print("Cell.ByThickenedFace - Error: The input face parameter is not a valid topologic face. Returning None.")
            return None
        if reverse == True and bothSides == False:
            thickness = -thickness
        faceNormal = Face.Normal(face)
        if bothSides:
            bottomFace = Topology.Translate(face, -faceNormal[0]*0.5*thickness, -faceNormal[1]*0.5*thickness, -faceNormal[2]*0.5*thickness)
            topFace = Topology.Translate(face, faceNormal[0]*0.5*thickness, faceNormal[1]*0.5*thickness, faceNormal[2]*0.5*thickness)
        else:
            bottomFace = face
            topFace = Topology.Translate(face, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)

        cellFaces = [bottomFace, topFace]
        bottomEdges = Topology.Edges(bottomFace)
        for bottomEdge in bottomEdges:
            topEdge = Topology.Translate(bottomEdge, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)
            sideEdge1 = Edge.ByVertices([bottomEdge.StartVertex(), topEdge.StartVertex()], tolerance=tolerance, silent=True)
            sideEdge2 = Edge.ByVertices([bottomEdge.EndVertex(), topEdge.EndVertex()], tolerance=tolerance, silent=True)
            cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
            cellFaces.append(Face.ByWire(cellWire, tolerance=tolerance))
        return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)

    @staticmethod
    def ByThickenedShell(shell, direction: list = [0, 0, 1], thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
                            planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell by thickening the input shell. The shell must be open.

        Parameters
        ----------
        shell : topologic_core.Shell
            The input shell to be thickened.
        thickness : float , optional
            The desired thickness. The default is 1.0.
        bothSides : bool
            If True, the cell will be lofted to each side of the shell. Otherwise, it will be lofted along the input direction. The default is True.
        reverse : bool
            If True, the cell will be lofted along the opposite of the input direction. The default is False.
        planarize : bool, optional
            If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Shell import Shell
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        if not Topology.IsInstance(shell, "Shell"):
            print("Cell.ByThickenedShell - Error: The input shell parameter is not a valid topologic Shell. Returning None.")
            return None
        if reverse == True and bothSides == False:
            thickness = -thickness
        if bothSides:
            bottomShell = Topology.Translate(shell, -direction[0]*0.5*thickness, -direction[1]*0.5*thickness, -direction[2]*0.5*thickness)
            topShell = Topology.Translate(shell, direction[0]*0.5*thickness, direction[1]*0.5*thickness, direction[2]*0.5*thickness)
        else:
            bottomShell = shell
            topShell = Topology.Translate(shell, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
        cellFaces = Shell.Faces(bottomShell) + Shell.Faces(topShell)
        bottomWire = Shell.ExternalBoundary(bottomShell, tolerance=tolerance)
        bottomEdges = Wire.Edges(bottomWire)
        for bottomEdge in bottomEdges:
            topEdge = Topology.Translate(bottomEdge, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
            sideEdge1 = Edge.ByVertices([Edge.StartVertex(bottomEdge), Edge.StartVertex(topEdge)], tolerance=tolerance, silent=True)
            sideEdge2 = Edge.ByVertices([Edge.EndVertex(bottomEdge), Edge.EndVertex(topEdge)], tolerance=tolerance, silent=True)
            cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
            cellFace = Face.ByWire(cellWire, tolerance=tolerance)
            cellFaces.append(cellFace)
        return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
    
    @staticmethod
    def ByWires(wires: list, close: bool = False, triangulate: bool = True, planarize: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent=False):
        """
        Creates a cell by lofting through the input list of wires.

        Parameters
        ----------
        wires : list
            The input list of wires.
        close : bool , optional
            If set to True, the last wire in the list of input wires will be connected to the first wire in the list of input wires. The default is False.
        triangulate : bool , optional
            If set to True, the faces will be triangulated. The default is True.
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.
        silent : bool , optional
            If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

        Raises
        ------
        Exception
            Raises an exception if the two wires in the list do not have the same number of edges.

        Returns
        -------
        topologic_core.Cell
            The created cell.

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

        if not isinstance(wires, list):
            if not silent:
                print("Cell.ByWires - Error: The input wires parameter is not a valid list. Returning None.")
                return None
        wires = [w for w in wires if Topology.IsInstance(w, "Wire")]
        if len(wires) < 2:
            if not silent:
                print("Cell.ByWires - Error: The input wires parameter contains less than two valid topologic wires. Returning None.")
                return None
        faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
        if close == True:
            faces.append(Face.ByWire(wires[0], 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]
            w1_edges = Topology.Edges(wire1)
            w2_edges = Topology.Edges(wire2)
            if len(w1_edges) != len(w2_edges):
                if not silent:
                    print("Cell.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
                    return None
            if triangulate == True:
                for j in range (len(w1_edges)):
                    e1 = w1_edges[j]
                    e2 = w2_edges[j]
                    e3 = None
                    e4 = None
                    try:
                        e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e4], tolerance=tolerance), tolerance=tolerance))
                        except:
                            pass
                    try:
                        e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance))
                        except:
                            pass
                    if e3 and e4:
                        e5 = Edge.ByVertices([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:
                for j in range (len(w1_edges)):
                    e1 = w1_edges[j]
                    e2 = w2_edges[j]
                    e3 = None
                    e4 = None
                    try:
                        e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                        except:
                            pass
                    try:
                        e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                    except:
                        try:
                            e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                        except:
                            pass
                    if e3 and e4:
                        try:
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance))
                        except:
                            faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance))
                    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))
        cell = Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance, silent=silent)
        if not cell:
            shell = Shell.ByFaces(faces, tolerance=tolerance)
            if Topology.IsInstance(shell, "Shell"):
                geom = Topology.Geometry(shell, mantissa=mantissa)
                cell = Topology.ByGeometry(geom['vertices'], geom['edges'], geom['faces'])
            if not Topology.IsInstance(cell, "Cell"):
                print("Cell.ByWires - Error: Could not create a cell. Returning None.")
                return None
        return cell

    @staticmethod
    def ByWiresCluster(cluster, close: bool = False, triangulate: bool = True, planarize: bool = False, tolerance: float = 0.0001):
        """
        Creates a cell by lofting through the input cluster of wires.

        Parameters
        ----------
        cluster : Cluster
            The input Cluster of wires.
        close : bool , optional
            If set to True, the last wire in the cluster of input wires will be connected to the first wire in the cluster of input wires. The default is False.
        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.

        Raises
        ------
        Exception
            Raises an exception if the two wires in the list do not have the same number of edges.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cluster, "Cluster"):
            print("Cell.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
            return None
        wires = Topology.Wires(cluster)
        return Cell.ByWires(wires, close=close, triangulate=triangulate, planarize=planarize, tolerance=tolerance)

    @staticmethod
    def Capsule(origin = None, radius: float = 0.25, height: float = 1, uSides: int = 16, vSidesEnds:int = 8, vSidesMiddle: int = 1, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a capsule shape. A capsule is a cylinder with hemispherical ends.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
        radius : float , optional
            The radius of the capsule. The default is 0.25.
        height : float , optional
            The height of the capsule. The default is 1.
        uSides : int , optional
            The number of circle segments of the capsule. The default is 16.
        vSidesEnds : int , optional
            The number of vertical segments of the end hemispheres. The default is 8.
        vSidesMiddle : int , optional
            The number of vertical segments of the middle cylinder. The default is 1.
        direction : list , optional
            The vector representing the up direction of the capsule. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the capsule. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

            """
        from topologicpy.Topology import Topology
        from topologicpy.Cell import Cell
        from topologicpy.Vertex import Vertex
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Capsule - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        cyl_height = height - radius*2
        if cyl_height <= 0:
            capsule = Cell.Sphere(origin=Vertex.Origin(), radius=radius, uSides= uSides, vSides=vSidesEnds*2)
        else:
            cyl = Cell.Cylinder(origin=Vertex.Origin(),
                                radius=radius,
                                height=cyl_height,
                                uSides=uSides, vSides=vSidesMiddle, direction=[0, 0, 1], placement="center", tolerance=tolerance)
            o1 = Vertex.ByCoordinates(0, 0, cyl_height*0.5)
            o2 = Vertex.ByCoordinates(0, 0, -cyl_height*0.5)
            s1 = Cell.Sphere(origin=o1, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
            s2 = Cell.Sphere(origin=o2, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
            capsule = Topology.Union(cyl, s1, tolerance=tolerance)
            capsule = Topology.Union(capsule, s2, tolerance=tolerance)
        if placement == "bottom":
            capsule = Topology.Translate(capsule, 0, 0, height/2)
        if placement == "lowerleft":
            capsule = Topology.Translate(capsule, 0, 0, height/2)
            capsule = Topology.Translate(capsule, radius, radius)
        
        capsule = Topology.Orient(capsule, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        capsule = Topology.Place(capsule, originA=Vertex.Origin(), originB=origin)
        return capsule

    @staticmethod
    def Compactness(cell, reference = "sphere", mantissa: int = 6) -> float:
        """
        Returns the compactness measure of the input cell. If the reference is "sphere", this is also known as 'sphericity' (https://en.wikipedia.org/wiki/Sphericity).

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        reference : str , optional
            The desired reference to which to compare this compactness. The options are "sphere" and "cube". It is case insensitive. The default is "sphere".
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        float
            The compactness of the input cell.

        """
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology
        import math

        faces = Topology.Faces(cell)
        area = 0.0
        for aFace in faces:
            area = area + abs(Face.Area(aFace))
        volume = abs(Cell.Volume(cell, mantissa=mantissa))
        compactness  = 0
        #From https://en.wikipedia.org/wiki/Sphericity
        if area > 0:
            if reference.lower() == "sphere":
                compactness = (((math.pi)**(1/3))*((6*volume)**(2/3)))/area
            else:
                compactness = 6*(volume**(2/3))/area
        else:
            print("Cell.Compactness - Error: cell surface area is not positive. Returning None.")
            return None
        return round(compactness, mantissa)
    
    @staticmethod
    def Cone(origin = None, baseRadius: float = 0.5, topRadius: float = 0, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
                 dirZ: float = 1, placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a cone.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the cone. The default is None which results in the cone being placed at (0, 0, 0).
        baseRadius : float , optional
            The radius of the base circle of the cone. The default is 0.5.
        topRadius : float , optional
            The radius of the top circle of the cone. The default is 0.
        height : float , optional
            The height of the cone. The default is 1.
        sides : int , optional
            The number of sides of the cone. The default is 16.
        direction : list , optional
            The vector representing the up direction of the cone. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the cone. 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_core.Cell
            The created cone.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        import math

        def createCone(baseWire, topWire, baseVertex, topVertex, tolerance):
            if baseWire == None and topWire == None:
                raise Exception("Cell.Cone - Error: Both radii of the cone cannot be zero at the same time")
            elif baseWire == None:
                apex = baseVertex
                wire = topWire
            elif topWire == None:
                apex = topVertex
                wire = baseWire
            else:
                return Cell.ByWires([baseWire, topWire])
            vertices = Topology.Vertices(wire)
            faces = [Face.ByWire(wire, tolerance=tolerance)]
            for i in range(0, len(vertices)-1):
                w = Wire.ByVertices([apex, vertices[i], vertices[i+1]])
                f = Face.ByWire(w, tolerance=tolerance)
                faces.append(f)
            w = Wire.ByVertices([apex, vertices[-1], vertices[0]])
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
            return Cell.ByFaces(faces, tolerance=tolerance)
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Cone - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            xOffset = 0
            yOffset = 0
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = max(baseRadius, topRadius)
            yOffset = max(baseRadius, topRadius)
            zOffset = 0

        baseZ = origin.Z() + zOffset
        topZ = origin.Z() + zOffset + height
        baseV = []
        topV = []
        for i in range(uSides):
            angle = math.radians(360/uSides)*i
            if baseRadius > 0:
                baseX = math.cos(angle)*baseRadius + origin.X() + xOffset
                baseY = math.sin(angle)*baseRadius + origin.Y() + yOffset
                baseZ = origin.Z() + zOffset
                baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
            if topRadius > 0:
                topX = math.cos(angle)*topRadius + origin.X() + xOffset
                topY = math.sin(angle)*topRadius + origin.Y() + yOffset
                topV.append(Vertex.ByCoordinates(topX,topY,topZ))
        if baseRadius > 0:
            baseWire = Wire.ByVertices(baseV)
        else:
            baseWire = None
        if topRadius > 0:
            topWire = Wire.ByVertices(topV)
        else:
            topWire = None
        baseVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset)
        topVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset+height)
        cone = createCone(baseWire, topWire, baseVertex, topVertex, tolerance)
        if cone == None:
            print("Cell.Cone - Error: Could not create a cone. Returning None.")
            return None
        
        if vSides > 1:
            cutting_planes = []
            baseX = origin.X() + xOffset
            baseY = origin.Y() + yOffset
            size = max(baseRadius, topRadius)*3
            for i in range(1, vSides):
                baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
                tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
                cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
            cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
            shell = Cell.Shells(cone)[0]
            shell = shell.Slice(cutting_planes_cluster)
            cone = Cell.ByShell(shell)
        cone = Topology.Orient(cone, origin=origin, dirA=[0, 0, 1], dirB=direction)
        return cone
  
    @staticmethod
    def ContainmentStatus(cell, vertex, tolerance: float = 0.0001) -> int:
        """
        Returns the containment status of the input vertex in relationship to the input cell

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        vertex : topologic_core.Vertex
            The input vertex.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        int
            Returns 0 if the vertex is inside, 1 if it is on the boundary of, and 2 if it is outside the input cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.ContainmentStatus - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        if not Topology.IsInstance(vertex, "Vertex"):
            print("Cell.ContainmentStatus - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
            return None
        try:
            status = topologic.CellUtility.Contains(cell, vertex, tolerance) # Hook to Core
            if status == 0:
                return 0
            elif status == 1:
                return 1
            else:
                return 2
        except:
            print("Cell.ContainmentStatus - Error: Could not determine containment status. Returning None.")
            return None
 
    @staticmethod
    def Cylinder(origin = None, radius: float = 0.5, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
                     placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a cylinder.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
        radius : float , optional
            The radius of the cylinder. The default is 0.5.
        height : float , optional
            The height of the cylinder. The default is 1.
        uSides : int , optional
            The number of circle segments of the cylinder. The default is 16.
        vSides : int , optional
            The number of vertical segments of the cylinder. The default is 1.
        direction : list , optional
            The vector representing the up direction of the cylinder. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the cylinder. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Cell
            The created cell.

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.CellComplex import CellComplex
        from topologicpy.Cluster import Cluster
        from topologicpy.Topology import Topology
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Cylinder - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = radius
            yOffset = radius
        circle_origin = Vertex.ByCoordinates(origin.X() + xOffset, origin.Y() + yOffset, origin.Z() + zOffset)
        
        baseWire = Wire.Circle(origin=circle_origin, radius=radius, sides=uSides, fromAngle=0, toAngle=360, close=True, direction=[0, 0, 1], placement="center", tolerance=tolerance)
        baseFace = Face.ByWire(baseWire, tolerance=tolerance)
        cylinder = Cell.ByThickenedFace(face=baseFace, thickness=height, bothSides=False, reverse=True,
                            tolerance=tolerance)
        if vSides > 1:
            cutting_planes = []
            baseX = origin.X() + xOffset
            baseY = origin.Y() + yOffset
            size = radius*3
            for i in range(1, vSides):
                baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
                tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
                cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
            cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
            cylinder = CellComplex.ExternalBoundary(cylinder.Slice(cutting_planes_cluster))

        cylinder = Topology.Orient(cylinder, origin=origin, dirA=[0, 0, 1], dirB=direction)
        return cylinder
    
    @staticmethod
    def Decompose(cell, tiltAngle: float = 10, tolerance: float = 0.0001) -> dict:
        """
        Decomposes the input cell into its logical components. This method assumes that the positive Z direction is UP.

        Parameters
        ----------
        cell : topologic_core.Cell
            the input cell.
        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. "verticalFaces": list of vertical faces
            2. "topHorizontalFaces": list of top horizontal faces
            3. "bottomHorizontalFaces": list of bottom horizontal faces
            4. "inclinedFaces": list of inclined faces
            5. "verticalApertures": list of vertical apertures
            6. "topHorizontalApertures": list of top horizontal apertures
            7. "bottomHorizontalApertures": list of bottom horizontal apertures
            8. "inclinedApertures": list of 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):
            apTopologies = []
            apertures = Topology.Apertures(topology)
            if isinstance(apertures, list):
                for aperture in apertures:
                    apTopologies.append(Aperture.Topology(aperture))
            return apTopologies

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Decompose - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        verticalFaces = []
        topHorizontalFaces = []
        bottomHorizontalFaces = []
        inclinedFaces = []
        verticalApertures = []
        topHorizontalApertures = []
        bottomHorizontalApertures = []
        inclinedApertures = []
        tiltAngle = abs(tiltAngle)
        faces = Cell.Faces(cell)
        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)

            if aCode == 0:
                verticalFaces.append(aFace)
                verticalApertures += getApertures(aFace)
            elif aCode == 1:
                if abs(aFace.Centroid().Z() - zMin) < tolerance:
                    bottomHorizontalFaces.append(aFace)
                    bottomHorizontalApertures += getApertures(aFace)
                else:
                    topHorizontalFaces.append(aFace)
                    topHorizontalApertures += getApertures(aFace)
            elif aCode == 2:
                if abs(aFace.Centroid().Z() - zMax) < tolerance:
                    topHorizontalFaces.append(aFace)
                    topHorizontalApertures += getApertures(aFace)
                else:
                    bottomHorizontalFaces.append(aFace)
                    bottomHorizontalApertures += getApertures(aFace)
            elif aCode == 3:
                inclinedFaces.append(aFace)
                inclinedApertures += getApertures(aFace)
        d = {
            "verticalFaces" : verticalFaces,
            "topHorizontalFaces" : topHorizontalFaces,
            "bottomHorizontalFaces" : bottomHorizontalFaces,
            "inclinedFaces" : inclinedFaces,
            "verticalApertures" : verticalApertures,
            "topHorizontalApertures" : topHorizontalApertures,
            "bottomHorizontalApertures" : bottomHorizontalApertures,
            "inclinedApertures" : inclinedApertures
            }
        return d

    @staticmethod
    def Dodecahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates a dodecahedron. See https://en.wikipedia.org/wiki/Dodecahedron.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the dodecahedron. The default is None which results in the dodecahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the dodecahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the dodecahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the dodecahedron. 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_core.Cell
            The created dodecahedron.

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

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        pen = Face.Circle(sides=5, radius=0.5)
        pentagons = [pen]
        edges = Topology.Edges(pen)
        for edge in edges:
            o = Topology.Centroid(edge)
            e_dir = Edge.Direction(edge)
            pentagons.append(Topology.Rotate(pen, origin=o, axis=e_dir, angle=116.565))

        cluster = Cluster.ByTopologies(pentagons)
        
        cluster2 = Topology.Rotate(cluster, origin=Vertex.Origin(), axis=[1, 0, 0], angle=180)
        cluster2 = Topology.Rotate(cluster2, origin=Vertex.Origin(), axis=[0, 0, 1], angle=36)
        vertices = Topology.Vertices(cluster2)
        zList = [Vertex.Z(v) for v in vertices]
        zList = list(set(zList))
        zList.sort()
        zoffset1 = zList[-1] - zList[0]
        zoffset2 = zList[1] - zList[0]
        cluster2 = Topology.Translate(cluster2, 0, 0, -zoffset1-zoffset2)
        pentagons += Topology.Faces(cluster2)
        dodecahedron = Cell.ByFaces(pentagons, tolerance=tolerance)
        centroid = Topology.Centroid(dodecahedron)
        dodecahedron = Topology.Translate(dodecahedron, -Vertex.X(centroid), -Vertex.Y(centroid), -Vertex.Z(centroid))
        vertices = Topology.Vertices(dodecahedron)
        d = Vertex.Distance(Vertex.Origin(), vertices[0])
        dodecahedron = Topology.Scale(dodecahedron, origin=Vertex.Origin(), x=radius/d, y=radius/d, z=radius/d)
        if placement == "bottom":
            dodecahedron = Topology.Translate(dodecahedron, 0, 0, radius)
        elif placement == "lowerleft":
            dodecahedron = Topology.Translate(dodecahedron, radius, radius, radius)
        
        dodecahedron = Topology.Orient(dodecahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        dodecahedron = Topology.Place(dodecahedron, originA=Vertex.Origin(), originB=origin)
        return dodecahedron

    @staticmethod
    def Edges(cell) -> list:
        """
        Returns the edges of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Edges - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        edges = Topology.Edges(cell)
        return edges

    @staticmethod
    def Egg(origin= None, height: float = 1.0, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
                   placement: str = "center", tolerance: float = 0.0001):
        """
        Creates an egg-shaped cell.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the sphere. The default is None which results in the egg-shaped cell being placed at (0, 0, 0).
        height : float , optional
            The desired height of of the egg-shaped cell. The default is 1.0.
        uSides : int , optional
            The desired number of sides along the longitude of the egg-shaped cell. The default is 16.
        vSides : int , optional
            The desired number of sides along the latitude of the egg-shaped cell. The default is 8.
        direction : list , optional
            The vector representing the up direction of the egg-shaped cell. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the egg-shaped cell. 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_core.Cell
            The created egg-shaped cell.

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology
        from topologicpy.Dictionary import Dictionary

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        
        coords = [[0.0, 0.0, -0.5],
                 [0.074748, 0.0, -0.494015],
                 [0.140819, 0.0, -0.473222],
                 [0.204118, 0.0, -0.438358],
                 [0.259512, 0.0, -0.391913],
                 [0.304837, 0.0, -0.335519],
                 [0.338649, 0.0, -0.271416],
                 [0.361307, 0.0, -0.202039],
                 [0.375678, 0.0, -0.129109],
                 [0.381294, 0.0, -0.053696],
                 [0.377694, 0.0, 0.019874],
                 [0.365135, 0.0, 0.091978],
                 [0.341482, 0.0, 0.173973],
                 [0.300154, 0.0, 0.276001],
                 [0.252928, 0.0, 0.355989],
                 [0.206605, 0.0, 0.405813],
                 [0.157529, 0.0, 0.442299],
                 [0.10604, 0.0, 0.472092],
                 [0.05547, 0.0, 0.491784],
                 [0.0, 0.0, 0.5]]
        verts = [Vertex.ByCoordinates(coord) for coord in coords]
        c = Wire.ByVertices(verts, close=False)
        new_verts = []
        for i in range(vSides+1):
            new_verts.append(Wire.VertexByParameter(c, i/vSides))
        c = Wire.ByVertices(new_verts, close=False)
        egg = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
        if Topology.IsInstance(egg, "CellComplex"):
            egg = egg.ExternalBoundary()
        if Topology.IsInstance(egg, "Shell"):
            egg = Cell.ByShell(egg)
        egg = Topology.Scale(egg, origin=Vertex.Origin(), x=height, y=height, z=height)
        if placement.lower() == "bottom":
            egg = Topology.Translate(egg, 0, 0, height/2)
        elif placement.lower() == "lowerleft":
            bb = Cell.BoundingBox(egg)
            d = Topology.Dictionary(bb)
            width = Dictionary.ValueAtKey(d, 'width')
            length = Dictionary.ValueAtKey(d, 'length')
            egg = Topology.Translate(egg, width*0.5, length*0.5, height*0.5)
        egg = Topology.Orient(egg, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        egg = Topology.Place(egg, originA=Vertex.Origin(), originB=origin)
        return egg
    
    @staticmethod
    def ExternalBoundary(cell):
        """
        Returns the external boundary of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

        Returns
        -------
        topologic_core.Shell
            The external boundary of the input cell.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.ExternalBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        try:
            return cell.ExternalBoundary()
        except:
            print("Cell.ExternalBoundary - Error: Could not compute the external boundary. Returning None.")
            return None
    
    @staticmethod
    def Faces(cell) -> list:
        """
        Returns the faces of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Faces - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        faces = Topology.Faces(cell)
        return faces

    @staticmethod
    def Hyperboloid(origin = None, baseRadius: float = 0.5, topRadius: float = 0.5, height: float = 1, sides: int = 24, direction: list = [0, 0, 1],
                        twist: float = 60, placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a hyperboloid.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The location of the origin of the hyperboloid. The default is None which results in the hyperboloid being placed at (0, 0, 0).
        baseRadius : float , optional
            The radius of the base circle of the hyperboloid. The default is 0.5.
        topRadius : float , optional
            The radius of the top circle of the hyperboloid. The default is 0.5.
        height : float , optional
            The height of the cone. The default is 1.
        sides : int , optional
            The number of sides of the cone. The default is 24.
        direction : list , optional
            The vector representing the up direction of the hyperboloid. The default is [0, 0, 1].
        twist : float , optional
            The angle to twist the base cylinder. The default is 60.
        placement : str , optional
            The description of the placement of the origin of the hyperboloid. 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_core.Cell
            The created hyperboloid.

        """
        from topologicpy.Cluster import Cluster
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Topology import Topology
        import math

        def createHyperboloid(baseVertices, topVertices, tolerance):
            baseWire = Wire.ByVertices(baseVertices, close=True)
            topWire = Wire.ByVertices(topVertices, close=True)
            baseFace = Face.ByWire(baseWire, tolerance=tolerance)
            topFace = Face.ByWire(topWire, tolerance=tolerance)
            faces = [baseFace, topFace]
            for i in range(0, len(baseVertices)-1):
                w = Wire.ByVertices([baseVertices[i], topVertices[i], topVertices[i+1]], close=True)
                f = Face.ByWire(w, tolerance=tolerance)
                faces.append(f)
                w = Wire.ByVertices([baseVertices[i+1], baseVertices[i], topVertices[i+1]], close=True)
                f = Face.ByWire(w, tolerance=tolerance)
                faces.append(f)
            w = Wire.ByVertices([baseVertices[-1], topVertices[-1], topVertices[0]], close=True)
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
            w = Wire.ByVertices([baseVertices[0], baseVertices[-1], topVertices[0]], close=True)
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
            returnTopology = Cell.ByFaces(faces, tolerance=tolerance)
            if returnTopology == None:
                returnTopology = Cluster.ByTopologies(faces)
            return returnTopology
        
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Hyperboloid - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        w_origin = Vertex.Origin()
        baseV = []
        topV = []
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = max(baseRadius, topRadius)
            yOffset = max(baseRadius, topRadius)
        baseZ = w_origin.Z() + zOffset
        topZ = w_origin.Z() + zOffset + height
        for i in range(sides):
            angle = math.radians(360/sides)*i
            if baseRadius > 0:
                baseX = math.sin(angle+math.radians(twist))*baseRadius + w_origin.X() + xOffset
                baseY = math.cos(angle+math.radians(twist))*baseRadius + w_origin.Y() + yOffset
                baseZ = w_origin.Z() + zOffset
                baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
            if topRadius > 0:
                topX = math.sin(angle-math.radians(twist))*topRadius + w_origin.X() + xOffset
                topY = math.cos(angle-math.radians(twist))*topRadius + w_origin.Y() + yOffset
                topV.append(Vertex.ByCoordinates(topX,topY,topZ))

        hyperboloid = createHyperboloid(baseV, topV, tolerance)
        if hyperboloid == None:
            print("Cell.Hyperboloid - Error: Could not create a hyperboloid. Returning None.")
            return None
        
        hyperboloid = Topology.Orient(hyperboloid, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        hyperboloid = Topology.Place(hyperboloid, originA=Vertex.Origin(), originB=origin)
        return hyperboloid
    
    @staticmethod
    def Icosahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates an icosahedron. See https://en.wikipedia.org/wiki/Icosahedron.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the icosahedron. The default is None which results in the icosahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the icosahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the icosahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the icosahedron. 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_core.Cell
            The created icosahedron.

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

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        rect1 = Wire.Rectangle(width=(1+math.sqrt(5))/2, length=1)
        rect2 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
        rect2 = Topology.Rotate(rect2, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
        rect3 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
        rect3 = Topology.Rotate(rect3, origin=Vertex.Origin(), axis=[0, 1, 0], angle=90)
        vertices = Topology.Vertices(rect1)
        v1, v2, v3, v4 = vertices
        vertices = Topology.Vertices(rect2)
        v5, v6, v7, v8 = vertices
        vertices = Topology.Vertices(rect3)
        v9, v10, v11, v12 = vertices
        f1 = Face.ByVertices([v1,v8,v4])
        f2 = Face.ByVertices([v1,v4,v5])
        f3 = Face.ByVertices([v3,v2,v6])
        f4 = Face.ByVertices([v2,v3,v7])
        f5 = Face.ByVertices([v10,v9,v2])
        f6 = Face.ByVertices([v10,v9,v1])
        f7 = Face.ByVertices([v12,v11,v4])
        f8 = Face.ByVertices([v12,v11,v3])
        f9 = Face.ByVertices([v8,v7,v9])
        f10 = Face.ByVertices([v8,v7,v12])
        f11 = Face.ByVertices([v5,v6,v10])
        f12 = Face.ByVertices([v5,v6,v11])
        f13 = Face.ByVertices([v8,v1,v9])
        f14 = Face.ByVertices([v9,v2,v7])
        f15 = Face.ByVertices([v7,v3,v12])
        f16 = Face.ByVertices([v8,v12,v4])
        f17 = Face.ByVertices([v1,v5,v10])
        f18 = Face.ByVertices([v10,v2,v6])
        f19 = Face.ByVertices([v6,v3,v11])
        f20 = Face.ByVertices([v11,v4,v5])

        icosahedron = Cell.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,
                        f11,f12,f13,f14,f15,f16,f17,f18,f19,f20], tolerance=tolerance)
        sf = 1.051*0.5 # To insribe it in a sphere of radius 0.5
        icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
        sf = radius/0.5
        icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
        if placement == "bottom":
            icosahedron = Topology.Translate(icosahedron, 0, 0, radius)
        elif placement == "lowerleft":
            icosahedron = Topology.Translate(icosahedron, radius, radius, radius)
        
        icosahedron = Topology.Orient(icosahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        icosahedron = Topology.Place(icosahedron, originA=Vertex.Origin(), originB=origin)
        return icosahedron


    @staticmethod
    def InternalBoundaries(cell) -> list:
        """
        Returns the internal boundaries of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

        Returns
        -------
        list
            The list of internal boundaries ([topologic_core.Shell]).

        """
        shells = []
        _ = cell.InternalBoundaries(shells) # Hook to Core
        return shells
    
    @staticmethod
    def InternalVertex(cell, tolerance: float = 0.0001):
        """
        Creates a vertex that is guaranteed to be inside the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        topologic_core.Vertex
            The internal vertex.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.InternalVertex - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        try:
            return topologic.CellUtility.InternalVertex(cell, tolerance) # Hook to Core
        except:
            print("Cell.InternalVertex - Error: Could not create an internal vertex. Returning None.")
            return None
    
    @staticmethod
    def IsOnBoundary(cell, vertex, tolerance: float = 0.0001) -> bool:
        """
        Returns True if the input vertex is on the boundary of the input cell. Returns False otherwise.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        vertex : topologic_core.Vertex
            The input vertex.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        bool
            Returns True if the input vertex is inside the input cell. Returns False otherwise.

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.IsOnBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        if not Topology.IsInstance(vertex, "Vertex"):
            print("Cell.IsOnBoundary - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
            return None
        try:
            return (Cell.ContainmentStatus(cell, vertex, tolerance = tolerance) == 1)
        except:
            print("Cell.IsOnBoundary - Error: Could not determine if the input vertex is on the boundary of the input cell. Returning None.")
            return None
    
    @staticmethod
    def Octahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.

        Parameters
        ----------
        origin : topologic_core.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_core.Cell
            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 Topology.IsInstance(origin, "Vertex"):
            print("Cell.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])

        octahedron = Cell.ByFaces([f1, f2, f3, f4, f5, f6, f7, f8], 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 Pipe(edge, profile = None, radius: float = 0.5, sides: int = 16, startOffset: float = 0, endOffset: float = 0, endcapA = None, endcapB = None) -> dict:
        """
        Description
        ----------
        Creates a pipe along the input edge.

        Parameters
        ----------
        edge : topologic_core.Edge
            The centerline of the pipe.
        profile : topologic_core.Wire , optional
            The profile of the pipe. It is assumed that the profile is in the XY plane. If set to None, a circle of radius 0.5 will be used. The default is None.
        radius : float , optional
            The radius of the pipe. The default is 0.5.
        sides : int , optional
            The number of sides of the pipe. The default is 16.
        startOffset : float , optional
            The offset distance from the start vertex of the centerline edge. The default is 0.
        endOffset : float , optional
            The offset distance from the end vertex of the centerline edge. The default is 0.
        endcapA, optional
            The topology to place at the start vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the direction of the centerline edge.
        endcapB, optional
            The topology to place at the end vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the inverse direction of the centerline edge.

        Returns
        -------
        dict
            A dictionary containing the pipe, the start endcap, and the end endcap if they have been specified. The dictionary has the following keys:
            'pipe'
            'endcapA'
            'endcapB'

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology
        import math

        if not Topology.IsInstance(edge, "Edge"):
            print("Cell.Pipe - Error: The input edge parameter is not a valid topologic edge. Returning None.")
            return None
        length = Edge.Length(edge)
        origin = Edge.StartVertex(edge)
        startU = startOffset / length
        endU = 1.0 - (endOffset / length)
        sv = Edge.VertexByParameter(edge, startU)
        ev = Edge.VertexByParameter(edge, endU)
        x1 = sv.X()
        y1 = sv.Y()
        z1 = sv.Z()
        x2 = ev.X()
        y2 = ev.Y()
        z2 = ev.Z()
        dx = x2 - x1
        dy = y2 - y1
        dz = z2 - z1
        dist = math.sqrt(dx**2 + dy**2 + dz**2)
        baseV = []
        topV = []

        if Topology.IsInstance(profile, "Wire"):
            baseWire = Topology.Translate(profile, 0 , 0, sv.Z())
            topWire = Topology.Translate(profile, 0 , 0, sv.Z()+dist)
        else:
            for i in range(sides):
                angle = math.radians(360/sides)*i
                x = math.sin(angle)*radius + sv.X()
                y = math.cos(angle)*radius + sv.Y()
                z = sv.Z()
                baseV.append(Vertex.ByCoordinates(x, y, z))
                topV.append(Vertex.ByCoordinates(x, y, z+dist))

            baseWire = Wire.ByVertices(baseV)
            topWire = Wire.ByVertices(topV)
        wires = [baseWire, topWire]
        pipe = Cell.ByWires(wires)
        phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
        if dist < 0.0001:
            theta = 0
        else:
            theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
        pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 1, 0], angle=theta)
        pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 0, 1], angle=phi)
        zzz = Vertex.ByCoordinates(0, 0, 0)
        if endcapA:
            origin = edge.StartVertex()
            x1 = origin.X()
            y1 = origin.Y()
            z1 = origin.Z()
            x2 = edge.EndVertex().X()
            y2 = edge.EndVertex().Y()
            z2 = edge.EndVertex().Z()
            dx = x2 - x1
            dy = y2 - y1
            dz = z2 - z1    
            dist = math.sqrt(dx**2 + dy**2 + dz**2)
            phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
            if dist < 0.0001:
                theta = 0
            else:
                theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
            endcapA = Topology.Copy(endcapA)
            endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 1, 0], angle=theta)
            endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 0, 1], angle=phi+180)
            endcapA = Topology.Translate(endcapA, origin.X(), origin.Y(), origin.Z())
        if endcapB:
            origin = edge.EndVertex()
            x1 = origin.X()
            y1 = origin.Y()
            z1 = origin.Z()
            x2 = edge.StartVertex().X()
            y2 = edge.StartVertex().Y()
            z2 = edge.StartVertex().Z()
            dx = x2 - x1
            dy = y2 - y1
            dz = z2 - z1    
            dist = math.sqrt(dx**2 + dy**2 + dz**2)
            phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
            if dist < 0.0001:
                theta = 0
            else:
                theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
            endcapB = Topology.Copy(endcapB)
            endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 1, 0], angle=theta)
            endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 0, 1], angle=phi+180)
            endcapB = Topology.Translate(endcapB, origin.X(), origin.Y(), origin.Z())
        return {'pipe': pipe, 'endcapA': endcapA, 'endcapB': endcapB}
    
    @staticmethod
    def Prism(origin= None, width: float = 1, length: float = 1, height: float = 1, uSides: int = 1, vSides: int = 1, wSides: int = 1,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates a prism.

        Parameters
        ----------
        origin : topologic_core.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_core.Cell
            The created prism.

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

        def sliceCell(cell, width, length, height, uSides, vSides, wSides):
            origin = Topology.Centroid(cell)
            shells = Topology.Shells(cell)
            shell = shells[0]
            wRect = Wire.Rectangle(origin=origin, width=width*1.2, length=length*1.2, direction=[0, 0, 1], placement="center")
            sliceFaces = []
            for i in range(1, wSides):
                sliceFaces.append(Topology.Translate(Face.ByWire(wRect, tolerance=tolerance), 0, 0, height/wSides*i - height*0.5))
            uRect = Wire.Rectangle(origin=origin, width=height*1.2, length=length*1.2, direction=[1, 0, 0], placement="center")
            for i in range(1, uSides):
                sliceFaces.append(Topology.Translate(Face.ByWire(uRect, tolerance=tolerance), width/uSides*i - width*0.5, 0, 0))
            vRect = Wire.Rectangle(origin=origin, width=height*1.2, length=width*1.2, direction=[0, 1, 0], placement="center")
            for i in range(1, vSides):
                sliceFaces.append(Topology.Translate(Face.ByWire(vRect, tolerance=tolerance), 0, length/vSides*i - length*0.5, 0))
            if len(sliceFaces) > 0:
                sliceCluster = Cluster.ByTopologies(sliceFaces)
                shell = Topology.Slice(topologyA=shell, topologyB=sliceCluster, tranDict=False, tolerance=tolerance)
                return Cell.ByShell(shell)
            return cell
        
        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 Topology.IsInstance(origin, "Vertex"):
            print("Cell.Prism - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        xOffset = 0
        yOffset = 0
        zOffset = 0
        if placement.lower() == "center":
            zOffset = -height*0.5
        elif placement.lower() == "lowerleft":
            xOffset = width*0.5
            yOffset = length*0.5
        vb1 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
        vb2 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
        vb3 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)
        vb4 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)

        baseWire = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True)
        baseFace = Face.ByWire(baseWire, tolerance=tolerance)

        prism = Cell.ByThickenedFace(baseFace, thickness=height, bothSides = False)

        if uSides > 1 or vSides > 1 or wSides > 1:
            prism = sliceCell(prism, width, length, height, uSides, vSides, wSides)
        prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        return prism

    @staticmethod
    def RemoveCollinearEdges(cell, angTolerance: float = 0.1, tolerance: float = 0.0001):
        """
        Removes any collinear edges in the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        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_core.Cell
            The created cell without any collinear edges.

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

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.RemoveCollinearEdges - Error: The input cell parameter is not a valid cell. Returning None.")
            return None
        faces = Cell.Faces(cell)
        clean_faces = []
        for face in faces:
            clean_faces.append(Face.RemoveCollinearEdges(face, angTolerance=angTolerance, tolerance=tolerance))
        return Cell.ByFaces(clean_faces, tolerance=tolerance)
    
    @staticmethod
    def Roof(face, angle: float = 45, epsilon: float = 0.01 , tolerance: float = 0.001):
        """
            Creates a hipped roof through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao <gaoxipeng1998@gmail.com>
            This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel

        Parameters
        ----------
        face : topologic_core.Face
            The input face.
        angle : float , optioal
            The desired angle in degrees of the roof. The default is 45.
        epsilon : float , optional
            The desired epsilon (another form of tolerance for distance from plane). The default is 0.01. (This is set to a larger number as it was found to work better)
        tolerance : float , optional
            The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)

        Returns
        -------
        cell
            The created roof.

        """
        from topologicpy.Shell import Shell
        from topologicpy.Cell import Cell
        from topologicpy.Topology import Topology
        
        shell = Shell.Roof(face=face, angle=angle, epsilon=epsilon, tolerance=tolerance)
        faces = Topology.Faces(shell) + [face]
        cell = Cell.ByFaces(faces, tolerance=tolerance)
        if not cell:
            print("Cell.Roof - Error: Could not create a roof cell. Returning None.")
            return None
        return cell

    @staticmethod
    def Sets(cells: list, superCells: list, tolerance: float = 0.0001) -> list:
        """
            Classifies the input cells into sets based on their enclosure within the input list of super cells. The order of the sets follows the order of the input list of super cells.

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

        Returns
        -------
        list
            The classified list of input cells based on their encolsure within the input list of super cells.

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Topology import Topology

        if not isinstance(cells, list):
            print("Cell.Sets - Error: The input cells parameter is not a valid list. Returning None.")
            return None
        if not isinstance(superCells, list):
            print("Cell.Sets - Error: The input superCells parameter is not a valid list. Returning None.")
            return None
        cells = [c for c in cells if Topology.IsInstance(c, "Cell")]
        if len(cells) < 1:
            print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
            return None
        superCells = [c for c in superCells if Topology.IsInstance(c, "Cell")]
        if len(cells) < 1:
            print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
            return None
        if len(superCells) == 0:
            cluster = cells[0]
            for i in range(1, len(cells)):
                oldCluster = cluster
                cluster = cluster.Union(cells[i])
                del oldCluster
            superCells = Topology.Cells(cluster)
        unused = []
        for i in range(len(cells)):
            unused.append(True)
        sets = []
        for i in range(len(superCells)):
            sets.append([])
        for i in range(len(cells)):
            if unused[i]:
                iv = Topology.InternalVertex(cells[i], tolerance=tolerance)
                for j in range(len(superCells)):
                    if (Vertex.IsInternal(iv, superCells[j], tolerance)):
                        sets[j].append(cells[i])
                        unused[i] = False
        return sets
    
    @staticmethod
    def Shells(cell) -> list:
        """
        Returns the shells of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Shells - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        shells = []
        _ = cell.Shells(None, shells) # Hook to Core
        return shells

    @staticmethod
    def Sphere(origin= None, radius: float = 0.5, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
                   placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a sphere.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the sphere. The default is None which results in the sphere being placed at (0, 0, 0).
        radius : float , optional
            The radius of the sphere. The default is 0.5.
        uSides : int , optional
            The number of sides along the longitude of the sphere. The default is 16.
        vSides : int , optional
            The number of sides along the latitude of the sphere. The default is 8.
        direction : list , optional
            The vector representing the up direction of the sphere. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the sphere. 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_core.Cell
            The created sphere.

        """

        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        c = Wire.Circle(origin=Vertex.Origin(), radius=radius, sides=vSides, fromAngle=-90, toAngle=90, close=False, direction=[0, 0, 1], placement="center")
        c = Topology.Rotate(c, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
        sphere = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
        if Topology.Type(sphere) == Topology.TypeID("CellComplex"):
            sphere = sphere.ExternalBoundary()
        if Topology.Type(sphere) == Topology.TypeID("Shell"):
            sphere = Cell.ByShell(sphere)
        if placement.lower() == "bottom":
            sphere = Topology.Translate(sphere, 0, 0, radius)
        elif placement.lower() == "lowerleft":
            sphere = Topology.Translate(sphere, radius, radius, radius)
        sphere = Topology.Orient(sphere, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        sphere = Topology.Place(sphere, originA=Vertex.Origin(), originB=origin)
        return sphere
    
    @staticmethod
    def SurfaceArea(cell, mantissa: int = 6) -> float:
        """
        Returns the surface area of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The cell.
        mantissa : int , optional
            The desired length of the mantissa. The default is 6.

        Returns
        -------
        area : float
            The surface area of the input cell.

        """
        return Cell.Area(cell=cell, mantissa=mantissa)
    
    @staticmethod
    def Tetrahedron(origin= None, radius: float = 0.5,
                  direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
        """
        Description
        ----------
        Creates a tetrahedron. See https://en.wikipedia.org/wiki/Tetrahedron.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the tetrahedron. The default is None which results in the tetrahedron being placed at (0, 0, 0).
        radius : float , optional
            The radius of the tetrahedron's circumscribed sphere. The default is 0.5.
        direction : list , optional
            The vector representing the up direction of the tetrahedron. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the tetrahedron. 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_core.Cell
            The created tetrahedron.

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

        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Tetrahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None

        vb1 = Vertex.ByCoordinates(math.sqrt(8/9), 0, -1/3)
        vb2 = Vertex.ByCoordinates(-math.sqrt(2/9), math.sqrt(2/3), -1/3)
        vb3 = Vertex.ByCoordinates(-math.sqrt(2/9), -math.sqrt(2/3), -1/3)
        vb4 = Vertex.ByCoordinates(0, 0, 1)
        f1 = Face.ByVertices([vb1, vb2, vb3])
        f2 = Face.ByVertices([vb4, vb1, vb2])
        f3 = Face.ByVertices([vb4, vb2, vb3])
        f4 = Face.ByVertices([vb4, vb3, vb1])
        tetrahedron = Cell.ByFaces([f1, f2, f3, f4])
        tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=0.5, y=0.5, z=0.5)
        tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)

        if placement.lower() == "lowerleft":
            tetrahedron = Topology.Translate(tetrahedron, radius, radius, radius)
        elif placement.lower() == "bottom":
            tetrahedron = Topology.Translate(tetrahedron, 0, 0, radius)
        tetrahedron = Topology.Place(tetrahedron, originA=Vertex.Origin(), originB=origin)
        tetrahedron = Topology.Orient(tetrahedron, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
        return tetrahedron
    
    @staticmethod
    def Torus(origin= None, majorRadius: float = 0.5, minorRadius: float = 0.125, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
        """
        Creates a torus.

        Parameters
        ----------
        origin : topologic_core.Vertex , optional
            The origin location of the torus. The default is None which results in the torus being placed at (0, 0, 0).
        majorRadius : float , optional
            The major radius of the torus. The default is 0.5.
        minorRadius : float , optional
            The minor radius of the torus. The default is 0.1.
        uSides : int , optional
            The number of sides along the longitude of the torus. The default is 16.
        vSides : int , optional
            The number of sides along the latitude of the torus. The default is 8.
        direction : list , optional
            The vector representing the up direction of the torus. The default is [0, 0, 1].
        placement : str , optional
            The description of the placement of the origin of the torus. 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_core.Cell
            The created torus.

        """
        
        from topologicpy.Vertex import Vertex
        from topologicpy.Wire import Wire
        from topologicpy.Topology import Topology
        
        if not origin:
            origin = Vertex.ByCoordinates(0, 0, 0)
        if not Topology.IsInstance(origin, "Vertex"):
            print("Cell.Torus - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
            return None
        c = Wire.Circle(origin=Vertex.Origin(), radius=minorRadius, sides=vSides, fromAngle=0, toAngle=360, close=False, direction=[0, 1, 0], placement="center")
        c = Topology.Translate(c, abs(majorRadius-minorRadius), 0, 0)
        torus = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
        if Topology.Type(torus) == Topology.TypeID("Shell"):
            torus = Cell.ByShell(torus)
        if placement.lower() == "bottom":
            torus = Topology.Translate(torus, 0, 0, minorRadius)
        elif placement.lower() == "lowerleft":
            torus = Topology.Translate(torus, majorRadius, majorRadius, minorRadius)

        torus = Topology.Orient(torus, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
        torus = Topology.Place(torus, originA=Vertex.Origin(), originB=origin)
        return torus
    
    @staticmethod
    def Vertices(cell) -> list:
        """
        Returns the vertices of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Vertices - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        vertices = []
        _ = cell.Vertices(None, vertices) # Hook to Core
        return vertices

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

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.
        manitssa: int , optional
            The desired length of the mantissa. The default is 6.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Volume - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        volume = None
        try:
            volume = round(topologic.CellUtility.Volume(cell), mantissa) # Hook to Core
        except:
            print("Cell.Volume - Error: Could not compute the volume of the input cell. Returning None.")
            volume = None
        return volume

    @staticmethod
    def Wires(cell) -> list:
        """
        Returns the wires of the input cell.

        Parameters
        ----------
        cell : topologic_core.Cell
            The input cell.

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

        """
        from topologicpy.Topology import Topology

        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.Wires - Error: The input cell parameter is not a valid topologic cell. Returning None.")
            return None
        wires = []
        _ = cell.Wires(None, wires) # Hook to Core
        return wires

Static methods

def Area(cell, mantissa: int = 6)

Returns the surface area of the input cell.

Parameters

cell : topologic_core.Cell
The cell.
mantissa : int , optional
The desired length of the mantissa. The default is 6.

Returns

float
The surface area of the input cell.
Expand source code
@staticmethod
def Area(cell, mantissa: int = 6):
    """
    Returns the surface area of the input cell.

    Parameters
    ----------
    cell : topologic_core.Cell
        The cell.
    mantissa : int , optional
        The desired length of the mantissa. The default is 6.

    Returns
    -------
    float
        The surface area of the input cell.

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

    faces = Topology.Faces(cell)
    area = 0.0
    for aFace in faces:
        area = area + Face.Area(aFace)
    return round(area, mantissa)
def Box(origin=None, width: float = 1, length: float = 1, height: float = 1, uSides: int = 1, vSides: int = 1, wSides: int = 1, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Creates a box.

Parameters

origin : topologic_core.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_core.Cell
The created box.
Expand source code
@staticmethod
def Box(origin = None,
        width: float = 1, length: float = 1, height: float = 1,
        uSides: int = 1, vSides: int = 1, wSides: int = 1,
        direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
    """
    Creates a box.

    Parameters
    ----------
    origin : topologic_core.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_core.Cell
        The created box.

    """
    return Cell.Prism(origin=origin, width=width, length=length, height=height,
                      uSides=uSides, vSides=vSides, wSides=wSides,
                      direction=direction, placement=placement, tolerance=tolerance)
def ByFaces(faces: list, planarize: bool = False, tolerance: float = 0.0001, silent=False)

Creates a cell from the input list of faces.

Parameters

faces : list
The input list of faces.
planarize : bool, optional
If set to True, the input faces are planarized before building the cell. Otherwise, they are not. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
silent : bool , optional
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def ByFaces(faces: list, planarize: bool = False, tolerance: float = 0.0001, silent=False):
    """
    Creates a cell from the input list of faces.

    Parameters
    ----------
    faces : list
        The input list of faces.
    planarize : bool, optional
        If set to True, the input faces are planarized before building the cell. Otherwise, they are not. The default is False.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.
    silent : bool , optional
        If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

    Returns
    -------
    topologic_core.Cell
        The created cell.

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

    if not isinstance(faces, list):
        if not silent:
            print("Cell.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
            return None
    faceList = [x for x in faces if Topology.IsInstance(x, "Face")]
    if len(faceList) < 1:
        if not silent:
            print("Cell.ByFaces - Error: The input faces parameter does not contain valid faces. Returning None.")
            return None
    # Try the default method
    cell = topologic.Cell.ByFaces(faceList, tolerance) # Hook to Core
    if Topology.IsInstance(cell, "Cell"):
        return cell
    
    # Fuse all the vertices first and rebuild the faces
    all_vertices = []
    wires = []
    for f in faceList:
        w = Face.Wire(f)
        wires.append(w)
        all_vertices += Topology.Vertices(w)
    all_vertices = Vertex.Fuse(all_vertices, tolerance=tolerance)
    new_faces = []
    for w in wires:
        face_vertices = []
        for v in Topology.Vertices(w):
            index = Vertex.Index(v, all_vertices, tolerance=tolerance)
            if not index == None:
                face_vertices.append(all_vertices[index])
        new_w = Wire.ByVertices(face_vertices)
        if Topology.IsInstance(new_w, "Wire"):
            new_f = Face.ByWire(new_w, silent=True)
            if Topology.IsInstance(new_f, "Face"):
                new_faces.append(new_f)
            elif isinstance(new_f, list):
                new_faces += new_f
    faceList = new_faces
    planarizedList = []
    enlargedList = []
    if planarize:
        planarizedList = [Face.Planarize(f, tolerance=tolerance) for f in faceList]
        enlargedList = [Face.ByOffset(f, offset=-tolerance*10) for f in planarizedList]
        cell = topologic.Cell.ByFaces(enlargedList, tolerance) # Hook to Core
        faceList = Topology.SubTopologies(cell, subTopologyType="face")
        finalFaces = []
        for f in faceList:
            centroid = Topology.Centroid(f)
            n = Face.Normal(f)
            v = Topology.Translate(centroid, n[0]*0.01, n[1]*0.01, n[2]*0.01)
            if not Vertex.IsInternal(v, cell):
                finalFaces.append(f)
        finalFinalFaces = []
        for f in finalFaces:
            vertices = Face.Vertices(f)
            w = Wire.Cycles(Face.ExternalBoundary(f), maxVertices=len(vertices))[0]
            f1 = Face.ByWire(w, tolerance=tolerance, silent=True)
            if Topology.IsInstance(f1, "Face"):
                finalFinalFaces.append(f1)
            elif isinstance(f1, list):
                finalFinalFaces += f1
        cell = topologic.Cell.ByFaces(finalFinalFaces, tolerance) # Hook to Core
        if cell == None:
            if not silent:
                print("Cell.ByFaces - Error: The operation failed. Returning None.")
                return None
        else:
            return cell
    else:
        cell = topologic.Cell.ByFaces(faces, tolerance) # Hook to Core
        if cell == None:
            if not silent:
                print("Cell.ByFaces - Error: The operation failed. Returning None.")
                return None
        else:
            return cell
def ByOffset(cell, offset: float = 1.0, tolerance: float = 0.0001)

Creates an offset cell from the input cell.

Parameters

cell : topologic_core.Cell
The input cell.
offset : float , optional
The desired offset distance. The default is 1.0.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

Topology
The created offset topology. WARNING: This method may fail to create a cell if the offset creates self-intersecting faces. Always check the type being returned by this method.
Expand source code
@staticmethod
def ByOffset(cell, offset: float = 1.0, tolerance: float = 0.0001):
    """
    Creates an offset cell from the input cell.

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.
    offset : float , optional
        The desired offset distance. The default is 1.0.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.
    
    Returns
    -------
    Topology
        The created offset topology. WARNING: This method may fail to create a cell if the offset creates self-intersecting faces. Always check the type being returned by this method.

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

    vertices = Topology.Vertices(cell)
    new_vertices = []
    for v in vertices:
        faces = Topology.SuperTopologies(v, hostTopology=cell, topologyType="face")
        normals = []
        for face in faces:
            normal = Vector.SetMagnitude(Face.Normal(face), offset)
            normals.append(normal)
        sum_normal = Vector.Sum(normals)
        new_v = Topology.TranslateByDirectionDistance(v, direction=sum_normal, distance=Vector.Magnitude(sum_normal))
        new_vertices.append(new_v)
    new_cell = Topology.SelfMerge(Topology.ReplaceVertices(cell, Topology.Vertices(cell), new_vertices), tolerance=tolerance)
    return new_cell
def ByShell(shell, planarize: bool = False, tolerance: float = 0.0001)

Creates a cell from the input shell.

Parameters

shell : topologic_core.Shell
The input shell. The shell must be closed for this method to succeed.
planarize : bool, optional
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def ByShell(shell, planarize: bool = False, tolerance: float = 0.0001):
    """
    Creates a cell from the input shell.

    Parameters
    ----------
    shell : topologic_core.Shell
        The input shell. The shell must be closed for this method to succeed.
    planarize : bool, optional
        If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic_core.Cell
        The created cell.

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(shell, "Shell"):
        print("Cell.ByShell - Error: The input shell parameter is not a valid topologic shell. Returning None.")
        return None
    faces = Topology.SubTopologies(shell, subTopologyType="face")
    return Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance)
def ByThickenedFace(face, thickness: float = 1.0, bothSides: bool = True, reverse: bool = False, planarize: bool = False, tolerance: float = 0.0001)

Creates a cell by thickening the input face.

Parameters

face : topologic_core.Face
The input face to be thickened.
thickness : float , optional
The desired thickness. The default is 1.0.
bothSides : bool
If True, the cell will be lofted to each side of the face. Otherwise, it will be lofted in the direction of the normal to the input face. The default is True.
reverse : bool
If True, the cell will be lofted in the opposite direction of the normal to the face. The default is False.
planarize : bool, optional
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def ByThickenedFace(face, thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
                        planarize: bool = False, tolerance: float = 0.0001):
    """
    Creates a cell by thickening the input face.

    Parameters
    ----------
    face : topologic_core.Face
        The input face to be thickened.
    thickness : float , optional
        The desired thickness. The default is 1.0.
    bothSides : bool
        If True, the cell will be lofted to each side of the face. Otherwise, it will be lofted in the direction of the normal to the input face. The default is True.
    reverse : bool
        If True, the cell will be lofted in the opposite direction of the normal to the face. The default is False.
    planarize : bool, optional
        If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic_core.Cell
        The created cell.

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

    if not Topology.IsInstance(face, "Face"):
        print("Cell.ByThickenedFace - Error: The input face parameter is not a valid topologic face. Returning None.")
        return None
    if reverse == True and bothSides == False:
        thickness = -thickness
    faceNormal = Face.Normal(face)
    if bothSides:
        bottomFace = Topology.Translate(face, -faceNormal[0]*0.5*thickness, -faceNormal[1]*0.5*thickness, -faceNormal[2]*0.5*thickness)
        topFace = Topology.Translate(face, faceNormal[0]*0.5*thickness, faceNormal[1]*0.5*thickness, faceNormal[2]*0.5*thickness)
    else:
        bottomFace = face
        topFace = Topology.Translate(face, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)

    cellFaces = [bottomFace, topFace]
    bottomEdges = Topology.Edges(bottomFace)
    for bottomEdge in bottomEdges:
        topEdge = Topology.Translate(bottomEdge, faceNormal[0]*thickness, faceNormal[1]*thickness, faceNormal[2]*thickness)
        sideEdge1 = Edge.ByVertices([bottomEdge.StartVertex(), topEdge.StartVertex()], tolerance=tolerance, silent=True)
        sideEdge2 = Edge.ByVertices([bottomEdge.EndVertex(), topEdge.EndVertex()], tolerance=tolerance, silent=True)
        cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
        cellFaces.append(Face.ByWire(cellWire, tolerance=tolerance))
    return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
def ByThickenedShell(shell, direction: list = [0, 0, 1], thickness: float = 1.0, bothSides: bool = True, reverse: bool = False, planarize: bool = False, tolerance: float = 0.0001)

Creates a cell by thickening the input shell. The shell must be open.

Parameters

shell : topologic_core.Shell
The input shell to be thickened.
thickness : float , optional
The desired thickness. The default is 1.0.
bothSides : bool
If True, the cell will be lofted to each side of the shell. Otherwise, it will be lofted along the input direction. The default is True.
reverse : bool
If True, the cell will be lofted along the opposite of the input direction. The default is False.
planarize : bool, optional
If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def ByThickenedShell(shell, direction: list = [0, 0, 1], thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
                        planarize: bool = False, tolerance: float = 0.0001):
    """
    Creates a cell by thickening the input shell. The shell must be open.

    Parameters
    ----------
    shell : topologic_core.Shell
        The input shell to be thickened.
    thickness : float , optional
        The desired thickness. The default is 1.0.
    bothSides : bool
        If True, the cell will be lofted to each side of the shell. Otherwise, it will be lofted along the input direction. The default is True.
    reverse : bool
        If True, the cell will be lofted along the opposite of the input direction. The default is False.
    planarize : bool, optional
        If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. The default is False.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic_core.Cell
        The created cell.

    """
    from topologicpy.Edge import Edge
    from topologicpy.Wire import Wire
    from topologicpy.Face import Face
    from topologicpy.Shell import Shell
    from topologicpy.Cluster import Cluster
    from topologicpy.Topology import Topology
    if not Topology.IsInstance(shell, "Shell"):
        print("Cell.ByThickenedShell - Error: The input shell parameter is not a valid topologic Shell. Returning None.")
        return None
    if reverse == True and bothSides == False:
        thickness = -thickness
    if bothSides:
        bottomShell = Topology.Translate(shell, -direction[0]*0.5*thickness, -direction[1]*0.5*thickness, -direction[2]*0.5*thickness)
        topShell = Topology.Translate(shell, direction[0]*0.5*thickness, direction[1]*0.5*thickness, direction[2]*0.5*thickness)
    else:
        bottomShell = shell
        topShell = Topology.Translate(shell, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
    cellFaces = Shell.Faces(bottomShell) + Shell.Faces(topShell)
    bottomWire = Shell.ExternalBoundary(bottomShell, tolerance=tolerance)
    bottomEdges = Wire.Edges(bottomWire)
    for bottomEdge in bottomEdges:
        topEdge = Topology.Translate(bottomEdge, direction[0]*thickness, direction[1]*thickness, direction[2]*thickness)
        sideEdge1 = Edge.ByVertices([Edge.StartVertex(bottomEdge), Edge.StartVertex(topEdge)], tolerance=tolerance, silent=True)
        sideEdge2 = Edge.ByVertices([Edge.EndVertex(bottomEdge), Edge.EndVertex(topEdge)], tolerance=tolerance, silent=True)
        cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
        cellFace = Face.ByWire(cellWire, tolerance=tolerance)
        cellFaces.append(cellFace)
    return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
def ByWires(wires: list, close: bool = False, triangulate: bool = True, planarize: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent=False)

Creates a cell by lofting through the input list of wires.

Parameters

wires : list
The input list of wires.
close : bool , optional
If set to True, the last wire in the list of input wires will be connected to the first wire in the list of input wires. The default is False.
triangulate : bool , optional
If set to True, the faces will be triangulated. The default is True.
mantissa : int , optional
The desired length of the mantissa. The default is 6.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
silent : bool , optional
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

Raises

Exception
Raises an exception if the two wires in the list do not have the same number of edges.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def ByWires(wires: list, close: bool = False, triangulate: bool = True, planarize: bool = False, mantissa: int = 6, tolerance: float = 0.0001, silent=False):
    """
    Creates a cell by lofting through the input list of wires.

    Parameters
    ----------
    wires : list
        The input list of wires.
    close : bool , optional
        If set to True, the last wire in the list of input wires will be connected to the first wire in the list of input wires. The default is False.
    triangulate : bool , optional
        If set to True, the faces will be triangulated. The default is True.
    mantissa : int , optional
        The desired length of the mantissa. The default is 6.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.
    silent : bool , optional
        If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.

    Raises
    ------
    Exception
        Raises an exception if the two wires in the list do not have the same number of edges.

    Returns
    -------
    topologic_core.Cell
        The created cell.

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

    if not isinstance(wires, list):
        if not silent:
            print("Cell.ByWires - Error: The input wires parameter is not a valid list. Returning None.")
            return None
    wires = [w for w in wires if Topology.IsInstance(w, "Wire")]
    if len(wires) < 2:
        if not silent:
            print("Cell.ByWires - Error: The input wires parameter contains less than two valid topologic wires. Returning None.")
            return None
    faces = [Face.ByWire(wires[0], tolerance=tolerance), Face.ByWire(wires[-1], tolerance=tolerance)]
    if close == True:
        faces.append(Face.ByWire(wires[0], 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]
        w1_edges = Topology.Edges(wire1)
        w2_edges = Topology.Edges(wire2)
        if len(w1_edges) != len(w2_edges):
            if not silent:
                print("Cell.ByWires - Error: The input wires parameter contains wires with different number of edges. Returning None.")
                return None
        if triangulate == True:
            for j in range (len(w1_edges)):
                e1 = w1_edges[j]
                e2 = w2_edges[j]
                e3 = None
                e4 = None
                try:
                    e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                except:
                    try:
                        e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                        faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e4], tolerance=tolerance), tolerance=tolerance))
                    except:
                        pass
                try:
                    e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                except:
                    try:
                        e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                        faces.append(Face.ByWire(Wire.ByEdges([e1, e2, e3], tolerance=tolerance), tolerance=tolerance))
                    except:
                        pass
                if e3 and e4:
                    e5 = Edge.ByVertices([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:
            for j in range (len(w1_edges)):
                e1 = w1_edges[j]
                e2 = w2_edges[j]
                e3 = None
                e4 = None
                try:
                    e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                except:
                    try:
                        e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                    except:
                        pass
                try:
                    e4 = Edge.ByVertices([e1.EndVertex(), e2.EndVertex()], tolerance=tolerance, silent=True)
                except:
                    try:
                        e3 = Edge.ByVertices([e1.StartVertex(), e2.StartVertex()], tolerance=tolerance, silent=True)
                    except:
                        pass
                if e3 and e4:
                    try:
                        faces.append(Face.ByWire(Wire.ByEdges([e1, e4, e2, e3], tolerance=tolerance), tolerance=tolerance))
                    except:
                        faces.append(Face.ByWire(Wire.ByEdges([e1, e3, e2, e4], tolerance=tolerance), tolerance=tolerance))
                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))
    cell = Cell.ByFaces(faces, planarize=planarize, tolerance=tolerance, silent=silent)
    if not cell:
        shell = Shell.ByFaces(faces, tolerance=tolerance)
        if Topology.IsInstance(shell, "Shell"):
            geom = Topology.Geometry(shell, mantissa=mantissa)
            cell = Topology.ByGeometry(geom['vertices'], geom['edges'], geom['faces'])
        if not Topology.IsInstance(cell, "Cell"):
            print("Cell.ByWires - Error: Could not create a cell. Returning None.")
            return None
    return cell
def ByWiresCluster(cluster, close: bool = False, triangulate: bool = True, planarize: bool = False, tolerance: float = 0.0001)

Creates a cell by lofting through the input cluster of wires.

Parameters

cluster : Cluster
The input Cluster of wires.
close : bool , optional
If set to True, the last wire in the cluster of input wires will be connected to the first wire in the cluster of input wires. The default is False.
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.

Raises

Exception
Raises an exception if the two wires in the list do not have the same number of edges.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def ByWiresCluster(cluster, close: bool = False, triangulate: bool = True, planarize: bool = False, tolerance: float = 0.0001):
    """
    Creates a cell by lofting through the input cluster of wires.

    Parameters
    ----------
    cluster : Cluster
        The input Cluster of wires.
    close : bool , optional
        If set to True, the last wire in the cluster of input wires will be connected to the first wire in the cluster of input wires. The default is False.
    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.

    Raises
    ------
    Exception
        Raises an exception if the two wires in the list do not have the same number of edges.

    Returns
    -------
    topologic_core.Cell
        The created cell.

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cluster, "Cluster"):
        print("Cell.ByWiresCluster - Error: The input cluster parameter is not a valid topologic cluster. Returning None.")
        return None
    wires = Topology.Wires(cluster)
    return Cell.ByWires(wires, close=close, triangulate=triangulate, planarize=planarize, tolerance=tolerance)
def Capsule(origin=None, radius: float = 0.25, height: float = 1, uSides: int = 16, vSidesEnds: int = 8, vSidesMiddle: int = 1, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Creates a capsule shape. A capsule is a cylinder with hemispherical ends.

Parameters

origin : topologic_core.Vertex , optional
The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
radius : float , optional
The radius of the capsule. The default is 0.25.
height : float , optional
The height of the capsule. The default is 1.
uSides : int , optional
The number of circle segments of the capsule. The default is 16.
vSidesEnds : int , optional
The number of vertical segments of the end hemispheres. The default is 8.
vSidesMiddle : int , optional
The number of vertical segments of the middle cylinder. The default is 1.
direction : list , optional
The vector representing the up direction of the capsule. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the capsule. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def Capsule(origin = None, radius: float = 0.25, height: float = 1, uSides: int = 16, vSidesEnds:int = 8, vSidesMiddle: int = 1, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
    """
    Creates a capsule shape. A capsule is a cylinder with hemispherical ends.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
    radius : float , optional
        The radius of the capsule. The default is 0.25.
    height : float , optional
        The height of the capsule. The default is 1.
    uSides : int , optional
        The number of circle segments of the capsule. The default is 16.
    vSidesEnds : int , optional
        The number of vertical segments of the end hemispheres. The default is 8.
    vSidesMiddle : int , optional
        The number of vertical segments of the middle cylinder. The default is 1.
    direction : list , optional
        The vector representing the up direction of the capsule. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the capsule. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic_core.Cell
        The created cell.

        """
    from topologicpy.Topology import Topology
    from topologicpy.Cell import Cell
    from topologicpy.Vertex import Vertex
    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Capsule - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    cyl_height = height - radius*2
    if cyl_height <= 0:
        capsule = Cell.Sphere(origin=Vertex.Origin(), radius=radius, uSides= uSides, vSides=vSidesEnds*2)
    else:
        cyl = Cell.Cylinder(origin=Vertex.Origin(),
                            radius=radius,
                            height=cyl_height,
                            uSides=uSides, vSides=vSidesMiddle, direction=[0, 0, 1], placement="center", tolerance=tolerance)
        o1 = Vertex.ByCoordinates(0, 0, cyl_height*0.5)
        o2 = Vertex.ByCoordinates(0, 0, -cyl_height*0.5)
        s1 = Cell.Sphere(origin=o1, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
        s2 = Cell.Sphere(origin=o2, radius=radius, uSides=uSides, vSides=vSidesEnds*2, tolerance=tolerance)
        capsule = Topology.Union(cyl, s1, tolerance=tolerance)
        capsule = Topology.Union(capsule, s2, tolerance=tolerance)
    if placement == "bottom":
        capsule = Topology.Translate(capsule, 0, 0, height/2)
    if placement == "lowerleft":
        capsule = Topology.Translate(capsule, 0, 0, height/2)
        capsule = Topology.Translate(capsule, radius, radius)
    
    capsule = Topology.Orient(capsule, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
    capsule = Topology.Place(capsule, originA=Vertex.Origin(), originB=origin)
    return capsule
def Compactness(cell, reference='sphere', mantissa: int = 6) ‑> float

Returns the compactness measure of the input cell. If the reference is "sphere", this is also known as 'sphericity' (https://en.wikipedia.org/wiki/Sphericity).

Parameters

cell : topologic_core.Cell
The input cell.
reference : str , optional
The desired reference to which to compare this compactness. The options are "sphere" and "cube". It is case insensitive. The default is "sphere".
mantissa : int , optional
The desired length of the mantissa. The default is 6.

Returns

float
The compactness of the input cell.
Expand source code
@staticmethod
def Compactness(cell, reference = "sphere", mantissa: int = 6) -> float:
    """
    Returns the compactness measure of the input cell. If the reference is "sphere", this is also known as 'sphericity' (https://en.wikipedia.org/wiki/Sphericity).

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.
    reference : str , optional
        The desired reference to which to compare this compactness. The options are "sphere" and "cube". It is case insensitive. The default is "sphere".
    mantissa : int , optional
        The desired length of the mantissa. The default is 6.

    Returns
    -------
    float
        The compactness of the input cell.

    """
    from topologicpy.Face import Face
    from topologicpy.Topology import Topology
    import math

    faces = Topology.Faces(cell)
    area = 0.0
    for aFace in faces:
        area = area + abs(Face.Area(aFace))
    volume = abs(Cell.Volume(cell, mantissa=mantissa))
    compactness  = 0
    #From https://en.wikipedia.org/wiki/Sphericity
    if area > 0:
        if reference.lower() == "sphere":
            compactness = (((math.pi)**(1/3))*((6*volume)**(2/3)))/area
        else:
            compactness = 6*(volume**(2/3))/area
    else:
        print("Cell.Compactness - Error: cell surface area is not positive. Returning None.")
        return None
    return round(compactness, mantissa)
def Cone(origin=None, baseRadius: float = 0.5, topRadius: float = 0, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1], dirZ: float = 1, placement: str = 'center', tolerance: float = 0.0001)

Creates a cone.

Parameters

origin : topologic_core.Vertex , optional
The location of the origin of the cone. The default is None which results in the cone being placed at (0, 0, 0).
baseRadius : float , optional
The radius of the base circle of the cone. The default is 0.5.
topRadius : float , optional
The radius of the top circle of the cone. The default is 0.
height : float , optional
The height of the cone. The default is 1.
sides : int , optional
The number of sides of the cone. The default is 16.
direction : list , optional
The vector representing the up direction of the cone. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the cone. 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_core.Cell
The created cone.
Expand source code
@staticmethod
def Cone(origin = None, baseRadius: float = 0.5, topRadius: float = 0, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
             dirZ: float = 1, placement: str = "center", tolerance: float = 0.0001):
    """
    Creates a cone.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The location of the origin of the cone. The default is None which results in the cone being placed at (0, 0, 0).
    baseRadius : float , optional
        The radius of the base circle of the cone. The default is 0.5.
    topRadius : float , optional
        The radius of the top circle of the cone. The default is 0.
    height : float , optional
        The height of the cone. The default is 1.
    sides : int , optional
        The number of sides of the cone. The default is 16.
    direction : list , optional
        The vector representing the up direction of the cone. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the cone. 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_core.Cell
        The created cone.

    """
    from topologicpy.Vertex import Vertex
    from topologicpy.Wire import Wire
    from topologicpy.Face import Face
    from topologicpy.Cluster import Cluster
    from topologicpy.Topology import Topology
    import math

    def createCone(baseWire, topWire, baseVertex, topVertex, tolerance):
        if baseWire == None and topWire == None:
            raise Exception("Cell.Cone - Error: Both radii of the cone cannot be zero at the same time")
        elif baseWire == None:
            apex = baseVertex
            wire = topWire
        elif topWire == None:
            apex = topVertex
            wire = baseWire
        else:
            return Cell.ByWires([baseWire, topWire])
        vertices = Topology.Vertices(wire)
        faces = [Face.ByWire(wire, tolerance=tolerance)]
        for i in range(0, len(vertices)-1):
            w = Wire.ByVertices([apex, vertices[i], vertices[i+1]])
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
        w = Wire.ByVertices([apex, vertices[-1], vertices[0]])
        f = Face.ByWire(w, tolerance=tolerance)
        faces.append(f)
        return Cell.ByFaces(faces, tolerance=tolerance)
    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Cone - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    xOffset = 0
    yOffset = 0
    zOffset = 0
    if placement.lower() == "center":
        xOffset = 0
        yOffset = 0
        zOffset = -height*0.5
    elif placement.lower() == "lowerleft":
        xOffset = max(baseRadius, topRadius)
        yOffset = max(baseRadius, topRadius)
        zOffset = 0

    baseZ = origin.Z() + zOffset
    topZ = origin.Z() + zOffset + height
    baseV = []
    topV = []
    for i in range(uSides):
        angle = math.radians(360/uSides)*i
        if baseRadius > 0:
            baseX = math.cos(angle)*baseRadius + origin.X() + xOffset
            baseY = math.sin(angle)*baseRadius + origin.Y() + yOffset
            baseZ = origin.Z() + zOffset
            baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
        if topRadius > 0:
            topX = math.cos(angle)*topRadius + origin.X() + xOffset
            topY = math.sin(angle)*topRadius + origin.Y() + yOffset
            topV.append(Vertex.ByCoordinates(topX,topY,topZ))
    if baseRadius > 0:
        baseWire = Wire.ByVertices(baseV)
    else:
        baseWire = None
    if topRadius > 0:
        topWire = Wire.ByVertices(topV)
    else:
        topWire = None
    baseVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset)
    topVertex = Vertex.ByCoordinates(origin.X()+xOffset, origin.Y()+yOffset, origin.Z()+zOffset+height)
    cone = createCone(baseWire, topWire, baseVertex, topVertex, tolerance)
    if cone == None:
        print("Cell.Cone - Error: Could not create a cone. Returning None.")
        return None
    
    if vSides > 1:
        cutting_planes = []
        baseX = origin.X() + xOffset
        baseY = origin.Y() + yOffset
        size = max(baseRadius, topRadius)*3
        for i in range(1, vSides):
            baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
            tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
            cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
        cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
        shell = Cell.Shells(cone)[0]
        shell = shell.Slice(cutting_planes_cluster)
        cone = Cell.ByShell(shell)
    cone = Topology.Orient(cone, origin=origin, dirA=[0, 0, 1], dirB=direction)
    return cone
def ContainmentStatus(cell, vertex, tolerance: float = 0.0001) ‑> int

Returns the containment status of the input vertex in relationship to the input cell

Parameters

cell : topologic_core.Cell
The input cell.
vertex : topologic_core.Vertex
The input vertex.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

int
Returns 0 if the vertex is inside, 1 if it is on the boundary of, and 2 if it is outside the input cell.
Expand source code
@staticmethod
def ContainmentStatus(cell, vertex, tolerance: float = 0.0001) -> int:
    """
    Returns the containment status of the input vertex in relationship to the input cell

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.
    vertex : topologic_core.Vertex
        The input vertex.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    int
        Returns 0 if the vertex is inside, 1 if it is on the boundary of, and 2 if it is outside the input cell.

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.ContainmentStatus - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    if not Topology.IsInstance(vertex, "Vertex"):
        print("Cell.ContainmentStatus - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
        return None
    try:
        status = topologic.CellUtility.Contains(cell, vertex, tolerance) # Hook to Core
        if status == 0:
            return 0
        elif status == 1:
            return 1
        else:
            return 2
    except:
        print("Cell.ContainmentStatus - Error: Could not determine containment status. Returning None.")
        return None
def Cylinder(origin=None, radius: float = 0.5, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Creates a cylinder.

Parameters

origin : topologic_core.Vertex , optional
The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
radius : float , optional
The radius of the cylinder. The default is 0.5.
height : float , optional
The height of the cylinder. The default is 1.
uSides : int , optional
The number of circle segments of the cylinder. The default is 16.
vSides : int , optional
The number of vertical segments of the cylinder. The default is 1.
direction : list , optional
The vector representing the up direction of the cylinder. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the cylinder. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic_core.Cell
The created cell.
Expand source code
@staticmethod
def Cylinder(origin = None, radius: float = 0.5, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
                 placement: str = "center", tolerance: float = 0.0001):
    """
    Creates a cylinder.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The location of the origin of the cylinder. The default is None which results in the cylinder being placed at (0, 0, 0).
    radius : float , optional
        The radius of the cylinder. The default is 0.5.
    height : float , optional
        The height of the cylinder. The default is 1.
    uSides : int , optional
        The number of circle segments of the cylinder. The default is 16.
    vSides : int , optional
        The number of vertical segments of the cylinder. The default is 1.
    direction : list , optional
        The vector representing the up direction of the cylinder. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the cylinder. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "bottom".
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic_core.Cell
        The created cell.

    """
    from topologicpy.Vertex import Vertex
    from topologicpy.Wire import Wire
    from topologicpy.Face import Face
    from topologicpy.CellComplex import CellComplex
    from topologicpy.Cluster import Cluster
    from topologicpy.Topology import Topology
    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Cylinder - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    xOffset = 0
    yOffset = 0
    zOffset = 0
    if placement.lower() == "center":
        zOffset = -height*0.5
    elif placement.lower() == "lowerleft":
        xOffset = radius
        yOffset = radius
    circle_origin = Vertex.ByCoordinates(origin.X() + xOffset, origin.Y() + yOffset, origin.Z() + zOffset)
    
    baseWire = Wire.Circle(origin=circle_origin, radius=radius, sides=uSides, fromAngle=0, toAngle=360, close=True, direction=[0, 0, 1], placement="center", tolerance=tolerance)
    baseFace = Face.ByWire(baseWire, tolerance=tolerance)
    cylinder = Cell.ByThickenedFace(face=baseFace, thickness=height, bothSides=False, reverse=True,
                        tolerance=tolerance)
    if vSides > 1:
        cutting_planes = []
        baseX = origin.X() + xOffset
        baseY = origin.Y() + yOffset
        size = radius*3
        for i in range(1, vSides):
            baseZ = origin.Z() + zOffset + float(height)/float(vSides)*i
            tool_origin = Vertex.ByCoordinates(baseX, baseY, baseZ)
            cutting_planes.append(Face.ByWire(Wire.Rectangle(origin=tool_origin, width=size, length=size), tolerance=tolerance))
        cutting_planes_cluster = Cluster.ByTopologies(cutting_planes)
        cylinder = CellComplex.ExternalBoundary(cylinder.Slice(cutting_planes_cluster))

    cylinder = Topology.Orient(cylinder, origin=origin, dirA=[0, 0, 1], dirB=direction)
    return cylinder
def Decompose(cell, tiltAngle: float = 10, tolerance: float = 0.0001) ‑> dict

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

Parameters

cell : topologic_core.Cell
the input cell.
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. "verticalFaces": list of vertical faces 2. "topHorizontalFaces": list of top horizontal faces 3. "bottomHorizontalFaces": list of bottom horizontal faces 4. "inclinedFaces": list of inclined faces 5. "verticalApertures": list of vertical apertures 6. "topHorizontalApertures": list of top horizontal apertures 7. "bottomHorizontalApertures": list of bottom horizontal apertures 8. "inclinedApertures": list of inclined apertures
Expand source code
@staticmethod
def Decompose(cell, tiltAngle: float = 10, tolerance: float = 0.0001) -> dict:
    """
    Decomposes the input cell into its logical components. This method assumes that the positive Z direction is UP.

    Parameters
    ----------
    cell : topologic_core.Cell
        the input cell.
    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. "verticalFaces": list of vertical faces
        2. "topHorizontalFaces": list of top horizontal faces
        3. "bottomHorizontalFaces": list of bottom horizontal faces
        4. "inclinedFaces": list of inclined faces
        5. "verticalApertures": list of vertical apertures
        6. "topHorizontalApertures": list of top horizontal apertures
        7. "bottomHorizontalApertures": list of bottom horizontal apertures
        8. "inclinedApertures": list of 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):
        apTopologies = []
        apertures = Topology.Apertures(topology)
        if isinstance(apertures, list):
            for aperture in apertures:
                apTopologies.append(Aperture.Topology(aperture))
        return apTopologies

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.Decompose - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    verticalFaces = []
    topHorizontalFaces = []
    bottomHorizontalFaces = []
    inclinedFaces = []
    verticalApertures = []
    topHorizontalApertures = []
    bottomHorizontalApertures = []
    inclinedApertures = []
    tiltAngle = abs(tiltAngle)
    faces = Cell.Faces(cell)
    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)

        if aCode == 0:
            verticalFaces.append(aFace)
            verticalApertures += getApertures(aFace)
        elif aCode == 1:
            if abs(aFace.Centroid().Z() - zMin) < tolerance:
                bottomHorizontalFaces.append(aFace)
                bottomHorizontalApertures += getApertures(aFace)
            else:
                topHorizontalFaces.append(aFace)
                topHorizontalApertures += getApertures(aFace)
        elif aCode == 2:
            if abs(aFace.Centroid().Z() - zMax) < tolerance:
                topHorizontalFaces.append(aFace)
                topHorizontalApertures += getApertures(aFace)
            else:
                bottomHorizontalFaces.append(aFace)
                bottomHorizontalApertures += getApertures(aFace)
        elif aCode == 3:
            inclinedFaces.append(aFace)
            inclinedApertures += getApertures(aFace)
    d = {
        "verticalFaces" : verticalFaces,
        "topHorizontalFaces" : topHorizontalFaces,
        "bottomHorizontalFaces" : bottomHorizontalFaces,
        "inclinedFaces" : inclinedFaces,
        "verticalApertures" : verticalApertures,
        "topHorizontalApertures" : topHorizontalApertures,
        "bottomHorizontalApertures" : bottomHorizontalApertures,
        "inclinedApertures" : inclinedApertures
        }
    return d
def Dodecahedron(origin=None, radius: float = 0.5, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Description

Creates a dodecahedron. See https://en.wikipedia.org/wiki/Dodecahedron.

Parameters

origin : topologic_core.Vertex , optional
The origin location of the dodecahedron. The default is None which results in the dodecahedron being placed at (0, 0, 0).
radius : float , optional
The radius of the dodecahedron's circumscribed sphere. The default is 0.5.
direction : list , optional
The vector representing the up direction of the dodecahedron. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the dodecahedron. 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_core.Cell
The created dodecahedron.
Expand source code
@staticmethod
def Dodecahedron(origin= None, radius: float = 0.5,
              direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
    """
    Description
    ----------
    Creates a dodecahedron. See https://en.wikipedia.org/wiki/Dodecahedron.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The origin location of the dodecahedron. The default is None which results in the dodecahedron being placed at (0, 0, 0).
    radius : float , optional
        The radius of the dodecahedron's circumscribed sphere. The default is 0.5.
    direction : list , optional
        The vector representing the up direction of the dodecahedron. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the dodecahedron. 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_core.Cell
        The created dodecahedron.

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

    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    pen = Face.Circle(sides=5, radius=0.5)
    pentagons = [pen]
    edges = Topology.Edges(pen)
    for edge in edges:
        o = Topology.Centroid(edge)
        e_dir = Edge.Direction(edge)
        pentagons.append(Topology.Rotate(pen, origin=o, axis=e_dir, angle=116.565))

    cluster = Cluster.ByTopologies(pentagons)
    
    cluster2 = Topology.Rotate(cluster, origin=Vertex.Origin(), axis=[1, 0, 0], angle=180)
    cluster2 = Topology.Rotate(cluster2, origin=Vertex.Origin(), axis=[0, 0, 1], angle=36)
    vertices = Topology.Vertices(cluster2)
    zList = [Vertex.Z(v) for v in vertices]
    zList = list(set(zList))
    zList.sort()
    zoffset1 = zList[-1] - zList[0]
    zoffset2 = zList[1] - zList[0]
    cluster2 = Topology.Translate(cluster2, 0, 0, -zoffset1-zoffset2)
    pentagons += Topology.Faces(cluster2)
    dodecahedron = Cell.ByFaces(pentagons, tolerance=tolerance)
    centroid = Topology.Centroid(dodecahedron)
    dodecahedron = Topology.Translate(dodecahedron, -Vertex.X(centroid), -Vertex.Y(centroid), -Vertex.Z(centroid))
    vertices = Topology.Vertices(dodecahedron)
    d = Vertex.Distance(Vertex.Origin(), vertices[0])
    dodecahedron = Topology.Scale(dodecahedron, origin=Vertex.Origin(), x=radius/d, y=radius/d, z=radius/d)
    if placement == "bottom":
        dodecahedron = Topology.Translate(dodecahedron, 0, 0, radius)
    elif placement == "lowerleft":
        dodecahedron = Topology.Translate(dodecahedron, radius, radius, radius)
    
    dodecahedron = Topology.Orient(dodecahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
    dodecahedron = Topology.Place(dodecahedron, originA=Vertex.Origin(), originB=origin)
    return dodecahedron
def Edges(cell) ‑> list

Returns the edges of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.

Returns

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

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.

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

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.Edges - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    edges = Topology.Edges(cell)
    return edges
def Egg(origin=None, height: float = 1.0, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Creates an egg-shaped cell.

Parameters

origin : topologic_core.Vertex , optional
The origin location of the sphere. The default is None which results in the egg-shaped cell being placed at (0, 0, 0).
height : float , optional
The desired height of of the egg-shaped cell. The default is 1.0.
uSides : int , optional
The desired number of sides along the longitude of the egg-shaped cell. The default is 16.
vSides : int , optional
The desired number of sides along the latitude of the egg-shaped cell. The default is 8.
direction : list , optional
The vector representing the up direction of the egg-shaped cell. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the egg-shaped cell. 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_core.Cell
The created egg-shaped cell.
Expand source code
@staticmethod
def Egg(origin= None, height: float = 1.0, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
               placement: str = "center", tolerance: float = 0.0001):
    """
    Creates an egg-shaped cell.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The origin location of the sphere. The default is None which results in the egg-shaped cell being placed at (0, 0, 0).
    height : float , optional
        The desired height of of the egg-shaped cell. The default is 1.0.
    uSides : int , optional
        The desired number of sides along the longitude of the egg-shaped cell. The default is 16.
    vSides : int , optional
        The desired number of sides along the latitude of the egg-shaped cell. The default is 8.
    direction : list , optional
        The vector representing the up direction of the egg-shaped cell. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the egg-shaped cell. 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_core.Cell
        The created egg-shaped cell.

    """

    from topologicpy.Vertex import Vertex
    from topologicpy.Wire import Wire
    from topologicpy.Topology import Topology
    from topologicpy.Dictionary import Dictionary

    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    
    coords = [[0.0, 0.0, -0.5],
             [0.074748, 0.0, -0.494015],
             [0.140819, 0.0, -0.473222],
             [0.204118, 0.0, -0.438358],
             [0.259512, 0.0, -0.391913],
             [0.304837, 0.0, -0.335519],
             [0.338649, 0.0, -0.271416],
             [0.361307, 0.0, -0.202039],
             [0.375678, 0.0, -0.129109],
             [0.381294, 0.0, -0.053696],
             [0.377694, 0.0, 0.019874],
             [0.365135, 0.0, 0.091978],
             [0.341482, 0.0, 0.173973],
             [0.300154, 0.0, 0.276001],
             [0.252928, 0.0, 0.355989],
             [0.206605, 0.0, 0.405813],
             [0.157529, 0.0, 0.442299],
             [0.10604, 0.0, 0.472092],
             [0.05547, 0.0, 0.491784],
             [0.0, 0.0, 0.5]]
    verts = [Vertex.ByCoordinates(coord) for coord in coords]
    c = Wire.ByVertices(verts, close=False)
    new_verts = []
    for i in range(vSides+1):
        new_verts.append(Wire.VertexByParameter(c, i/vSides))
    c = Wire.ByVertices(new_verts, close=False)
    egg = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
    if Topology.IsInstance(egg, "CellComplex"):
        egg = egg.ExternalBoundary()
    if Topology.IsInstance(egg, "Shell"):
        egg = Cell.ByShell(egg)
    egg = Topology.Scale(egg, origin=Vertex.Origin(), x=height, y=height, z=height)
    if placement.lower() == "bottom":
        egg = Topology.Translate(egg, 0, 0, height/2)
    elif placement.lower() == "lowerleft":
        bb = Cell.BoundingBox(egg)
        d = Topology.Dictionary(bb)
        width = Dictionary.ValueAtKey(d, 'width')
        length = Dictionary.ValueAtKey(d, 'length')
        egg = Topology.Translate(egg, width*0.5, length*0.5, height*0.5)
    egg = Topology.Orient(egg, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
    egg = Topology.Place(egg, originA=Vertex.Origin(), originB=origin)
    return egg
def ExternalBoundary(cell)

Returns the external boundary of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.

Returns

topologic_core.Shell
The external boundary of the input cell.
Expand source code
@staticmethod
def ExternalBoundary(cell):
    """
    Returns the external boundary of the input cell.

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.

    Returns
    -------
    topologic_core.Shell
        The external boundary of the input cell.

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.ExternalBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    try:
        return cell.ExternalBoundary()
    except:
        print("Cell.ExternalBoundary - Error: Could not compute the external boundary. Returning None.")
        return None
def Faces(cell) ‑> list

Returns the faces of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.

Returns

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

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.

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

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.Faces - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    faces = Topology.Faces(cell)
    return faces
def Hyperboloid(origin=None, baseRadius: float = 0.5, topRadius: float = 0.5, height: float = 1, sides: int = 24, direction: list = [0, 0, 1], twist: float = 60, placement: str = 'center', tolerance: float = 0.0001)

Creates a hyperboloid.

Parameters

origin : topologic_core.Vertex , optional
The location of the origin of the hyperboloid. The default is None which results in the hyperboloid being placed at (0, 0, 0).
baseRadius : float , optional
The radius of the base circle of the hyperboloid. The default is 0.5.
topRadius : float , optional
The radius of the top circle of the hyperboloid. The default is 0.5.
height : float , optional
The height of the cone. The default is 1.
sides : int , optional
The number of sides of the cone. The default is 24.
direction : list , optional
The vector representing the up direction of the hyperboloid. The default is [0, 0, 1].
twist : float , optional
The angle to twist the base cylinder. The default is 60.
placement : str , optional
The description of the placement of the origin of the hyperboloid. 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_core.Cell
The created hyperboloid.
Expand source code
@staticmethod
def Hyperboloid(origin = None, baseRadius: float = 0.5, topRadius: float = 0.5, height: float = 1, sides: int = 24, direction: list = [0, 0, 1],
                    twist: float = 60, placement: str = "center", tolerance: float = 0.0001):
    """
    Creates a hyperboloid.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The location of the origin of the hyperboloid. The default is None which results in the hyperboloid being placed at (0, 0, 0).
    baseRadius : float , optional
        The radius of the base circle of the hyperboloid. The default is 0.5.
    topRadius : float , optional
        The radius of the top circle of the hyperboloid. The default is 0.5.
    height : float , optional
        The height of the cone. The default is 1.
    sides : int , optional
        The number of sides of the cone. The default is 24.
    direction : list , optional
        The vector representing the up direction of the hyperboloid. The default is [0, 0, 1].
    twist : float , optional
        The angle to twist the base cylinder. The default is 60.
    placement : str , optional
        The description of the placement of the origin of the hyperboloid. 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_core.Cell
        The created hyperboloid.

    """
    from topologicpy.Cluster import Cluster
    from topologicpy.Vertex import Vertex
    from topologicpy.Wire import Wire
    from topologicpy.Face import Face
    from topologicpy.Topology import Topology
    import math

    def createHyperboloid(baseVertices, topVertices, tolerance):
        baseWire = Wire.ByVertices(baseVertices, close=True)
        topWire = Wire.ByVertices(topVertices, close=True)
        baseFace = Face.ByWire(baseWire, tolerance=tolerance)
        topFace = Face.ByWire(topWire, tolerance=tolerance)
        faces = [baseFace, topFace]
        for i in range(0, len(baseVertices)-1):
            w = Wire.ByVertices([baseVertices[i], topVertices[i], topVertices[i+1]], close=True)
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
            w = Wire.ByVertices([baseVertices[i+1], baseVertices[i], topVertices[i+1]], close=True)
            f = Face.ByWire(w, tolerance=tolerance)
            faces.append(f)
        w = Wire.ByVertices([baseVertices[-1], topVertices[-1], topVertices[0]], close=True)
        f = Face.ByWire(w, tolerance=tolerance)
        faces.append(f)
        w = Wire.ByVertices([baseVertices[0], baseVertices[-1], topVertices[0]], close=True)
        f = Face.ByWire(w, tolerance=tolerance)
        faces.append(f)
        returnTopology = Cell.ByFaces(faces, tolerance=tolerance)
        if returnTopology == None:
            returnTopology = Cluster.ByTopologies(faces)
        return returnTopology
    
    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Hyperboloid - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    w_origin = Vertex.Origin()
    baseV = []
    topV = []
    xOffset = 0
    yOffset = 0
    zOffset = 0
    if placement.lower() == "center":
        zOffset = -height*0.5
    elif placement.lower() == "lowerleft":
        xOffset = max(baseRadius, topRadius)
        yOffset = max(baseRadius, topRadius)
    baseZ = w_origin.Z() + zOffset
    topZ = w_origin.Z() + zOffset + height
    for i in range(sides):
        angle = math.radians(360/sides)*i
        if baseRadius > 0:
            baseX = math.sin(angle+math.radians(twist))*baseRadius + w_origin.X() + xOffset
            baseY = math.cos(angle+math.radians(twist))*baseRadius + w_origin.Y() + yOffset
            baseZ = w_origin.Z() + zOffset
            baseV.append(Vertex.ByCoordinates(baseX,baseY,baseZ))
        if topRadius > 0:
            topX = math.sin(angle-math.radians(twist))*topRadius + w_origin.X() + xOffset
            topY = math.cos(angle-math.radians(twist))*topRadius + w_origin.Y() + yOffset
            topV.append(Vertex.ByCoordinates(topX,topY,topZ))

    hyperboloid = createHyperboloid(baseV, topV, tolerance)
    if hyperboloid == None:
        print("Cell.Hyperboloid - Error: Could not create a hyperboloid. Returning None.")
        return None
    
    hyperboloid = Topology.Orient(hyperboloid, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
    hyperboloid = Topology.Place(hyperboloid, originA=Vertex.Origin(), originB=origin)
    return hyperboloid
def Icosahedron(origin=None, radius: float = 0.5, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Description

Creates an icosahedron. See https://en.wikipedia.org/wiki/Icosahedron.

Parameters

origin : topologic_core.Vertex , optional
The origin location of the icosahedron. The default is None which results in the icosahedron being placed at (0, 0, 0).
radius : float , optional
The radius of the icosahedron's circumscribed sphere. The default is 0.5.
direction : list , optional
The vector representing the up direction of the icosahedron. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the icosahedron. 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_core.Cell
The created icosahedron.
Expand source code
@staticmethod
def Icosahedron(origin= None, radius: float = 0.5,
              direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
    """
    Description
    ----------
    Creates an icosahedron. See https://en.wikipedia.org/wiki/Icosahedron.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The origin location of the icosahedron. The default is None which results in the icosahedron being placed at (0, 0, 0).
    radius : float , optional
        The radius of the icosahedron's circumscribed sphere. The default is 0.5.
    direction : list , optional
        The vector representing the up direction of the icosahedron. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the icosahedron. 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_core.Cell
        The created icosahedron.

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

    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Dodecahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    rect1 = Wire.Rectangle(width=(1+math.sqrt(5))/2, length=1)
    rect2 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
    rect2 = Topology.Rotate(rect2, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
    rect3 = Wire.Rectangle(width=1, length=(1+math.sqrt(5))/2)
    rect3 = Topology.Rotate(rect3, origin=Vertex.Origin(), axis=[0, 1, 0], angle=90)
    vertices = Topology.Vertices(rect1)
    v1, v2, v3, v4 = vertices
    vertices = Topology.Vertices(rect2)
    v5, v6, v7, v8 = vertices
    vertices = Topology.Vertices(rect3)
    v9, v10, v11, v12 = vertices
    f1 = Face.ByVertices([v1,v8,v4])
    f2 = Face.ByVertices([v1,v4,v5])
    f3 = Face.ByVertices([v3,v2,v6])
    f4 = Face.ByVertices([v2,v3,v7])
    f5 = Face.ByVertices([v10,v9,v2])
    f6 = Face.ByVertices([v10,v9,v1])
    f7 = Face.ByVertices([v12,v11,v4])
    f8 = Face.ByVertices([v12,v11,v3])
    f9 = Face.ByVertices([v8,v7,v9])
    f10 = Face.ByVertices([v8,v7,v12])
    f11 = Face.ByVertices([v5,v6,v10])
    f12 = Face.ByVertices([v5,v6,v11])
    f13 = Face.ByVertices([v8,v1,v9])
    f14 = Face.ByVertices([v9,v2,v7])
    f15 = Face.ByVertices([v7,v3,v12])
    f16 = Face.ByVertices([v8,v12,v4])
    f17 = Face.ByVertices([v1,v5,v10])
    f18 = Face.ByVertices([v10,v2,v6])
    f19 = Face.ByVertices([v6,v3,v11])
    f20 = Face.ByVertices([v11,v4,v5])

    icosahedron = Cell.ByFaces([f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,
                    f11,f12,f13,f14,f15,f16,f17,f18,f19,f20], tolerance=tolerance)
    sf = 1.051*0.5 # To insribe it in a sphere of radius 0.5
    icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
    sf = radius/0.5
    icosahedron = Topology.Scale(icosahedron, origin=Vertex.Origin(), x=sf, y=sf, z=sf)
    if placement == "bottom":
        icosahedron = Topology.Translate(icosahedron, 0, 0, radius)
    elif placement == "lowerleft":
        icosahedron = Topology.Translate(icosahedron, radius, radius, radius)
    
    icosahedron = Topology.Orient(icosahedron, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
    icosahedron = Topology.Place(icosahedron, originA=Vertex.Origin(), originB=origin)
    return icosahedron
def InternalBoundaries(cell) ‑> list

Returns the internal boundaries of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.

Returns

list
The list of internal boundaries ([topologic_core.Shell]).
Expand source code
@staticmethod
def InternalBoundaries(cell) -> list:
    """
    Returns the internal boundaries of the input cell.

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.

    Returns
    -------
    list
        The list of internal boundaries ([topologic_core.Shell]).

    """
    shells = []
    _ = cell.InternalBoundaries(shells) # Hook to Core
    return shells
def InternalVertex(cell, tolerance: float = 0.0001)

Creates a vertex that is guaranteed to be inside the input cell.

Parameters

cell : topologic_core.Cell
The input cell.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

topologic_core.Vertex
The internal vertex.
Expand source code
@staticmethod
def InternalVertex(cell, tolerance: float = 0.0001):
    """
    Creates a vertex that is guaranteed to be inside the input cell.

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    topologic_core.Vertex
        The internal vertex.

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.InternalVertex - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    try:
        return topologic.CellUtility.InternalVertex(cell, tolerance) # Hook to Core
    except:
        print("Cell.InternalVertex - Error: Could not create an internal vertex. Returning None.")
        return None
def IsOnBoundary(cell, vertex, tolerance: float = 0.0001) ‑> bool

Returns True if the input vertex is on the boundary of the input cell. Returns False otherwise.

Parameters

cell : topologic_core.Cell
The input cell.
vertex : topologic_core.Vertex
The input vertex.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

bool
Returns True if the input vertex is inside the input cell. Returns False otherwise.
Expand source code
@staticmethod
def IsOnBoundary(cell, vertex, tolerance: float = 0.0001) -> bool:
    """
    Returns True if the input vertex is on the boundary of the input cell. Returns False otherwise.

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.
    vertex : topologic_core.Vertex
        The input vertex.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    bool
        Returns True if the input vertex is inside the input cell. Returns False otherwise.

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.IsOnBoundary - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    if not Topology.IsInstance(vertex, "Vertex"):
        print("Cell.IsOnBoundary - Error: The input vertex parameter is not a valid topologic vertex. Returning None.")
        return None
    try:
        return (Cell.ContainmentStatus(cell, vertex, tolerance = tolerance) == 1)
    except:
        print("Cell.IsOnBoundary - Error: Could not determine if the input vertex is on the boundary of the input cell. Returning None.")
        return None
def Octahedron(origin=None, radius: float = 0.5, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Description

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

Parameters

origin : topologic_core.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_core.Cell
The created octahedron.
Expand source code
@staticmethod
def Octahedron(origin= None, radius: float = 0.5,
              direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
    """
    Description
    ----------
    Creates an octahedron. See https://en.wikipedia.org/wiki/Octahedron.

    Parameters
    ----------
    origin : topologic_core.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_core.Cell
        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 Topology.IsInstance(origin, "Vertex"):
        print("Cell.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])

    octahedron = Cell.ByFaces([f1, f2, f3, f4, f5, f6, f7, f8], 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 Pipe(edge, profile=None, radius: float = 0.5, sides: int = 16, startOffset: float = 0, endOffset: float = 0, endcapA=None, endcapB=None) ‑> dict

Description

Creates a pipe along the input edge.

Parameters

edge : topologic_core.Edge
The centerline of the pipe.
profile : topologic_core.Wire , optional
The profile of the pipe. It is assumed that the profile is in the XY plane. If set to None, a circle of radius 0.5 will be used. The default is None.
radius : float , optional
The radius of the pipe. The default is 0.5.
sides : int , optional
The number of sides of the pipe. The default is 16.
startOffset : float , optional
The offset distance from the start vertex of the centerline edge. The default is 0.
endOffset : float , optional
The offset distance from the end vertex of the centerline edge. The default is 0.
endcapA, optional
The topology to place at the start vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the direction of the centerline edge.
endcapB, optional
The topology to place at the end vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the inverse direction of the centerline edge.

Returns

dict
A dictionary containing the pipe, the start endcap, and the end endcap if they have been specified. The dictionary has the following keys: 'pipe' 'endcapA' 'endcapB'
Expand source code
@staticmethod
def Pipe(edge, profile = None, radius: float = 0.5, sides: int = 16, startOffset: float = 0, endOffset: float = 0, endcapA = None, endcapB = None) -> dict:
    """
    Description
    ----------
    Creates a pipe along the input edge.

    Parameters
    ----------
    edge : topologic_core.Edge
        The centerline of the pipe.
    profile : topologic_core.Wire , optional
        The profile of the pipe. It is assumed that the profile is in the XY plane. If set to None, a circle of radius 0.5 will be used. The default is None.
    radius : float , optional
        The radius of the pipe. The default is 0.5.
    sides : int , optional
        The number of sides of the pipe. The default is 16.
    startOffset : float , optional
        The offset distance from the start vertex of the centerline edge. The default is 0.
    endOffset : float , optional
        The offset distance from the end vertex of the centerline edge. The default is 0.
    endcapA, optional
        The topology to place at the start vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the direction of the centerline edge.
    endcapB, optional
        The topology to place at the end vertex of the centerline edge. The positive Z direction of the end cap will be oriented in the inverse direction of the centerline edge.

    Returns
    -------
    dict
        A dictionary containing the pipe, the start endcap, and the end endcap if they have been specified. The dictionary has the following keys:
        'pipe'
        'endcapA'
        'endcapB'

    """

    from topologicpy.Vertex import Vertex
    from topologicpy.Edge import Edge
    from topologicpy.Wire import Wire
    from topologicpy.Topology import Topology
    import math

    if not Topology.IsInstance(edge, "Edge"):
        print("Cell.Pipe - Error: The input edge parameter is not a valid topologic edge. Returning None.")
        return None
    length = Edge.Length(edge)
    origin = Edge.StartVertex(edge)
    startU = startOffset / length
    endU = 1.0 - (endOffset / length)
    sv = Edge.VertexByParameter(edge, startU)
    ev = Edge.VertexByParameter(edge, endU)
    x1 = sv.X()
    y1 = sv.Y()
    z1 = sv.Z()
    x2 = ev.X()
    y2 = ev.Y()
    z2 = ev.Z()
    dx = x2 - x1
    dy = y2 - y1
    dz = z2 - z1
    dist = math.sqrt(dx**2 + dy**2 + dz**2)
    baseV = []
    topV = []

    if Topology.IsInstance(profile, "Wire"):
        baseWire = Topology.Translate(profile, 0 , 0, sv.Z())
        topWire = Topology.Translate(profile, 0 , 0, sv.Z()+dist)
    else:
        for i in range(sides):
            angle = math.radians(360/sides)*i
            x = math.sin(angle)*radius + sv.X()
            y = math.cos(angle)*radius + sv.Y()
            z = sv.Z()
            baseV.append(Vertex.ByCoordinates(x, y, z))
            topV.append(Vertex.ByCoordinates(x, y, z+dist))

        baseWire = Wire.ByVertices(baseV)
        topWire = Wire.ByVertices(topV)
    wires = [baseWire, topWire]
    pipe = Cell.ByWires(wires)
    phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
    if dist < 0.0001:
        theta = 0
    else:
        theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
    pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 1, 0], angle=theta)
    pipe = Topology.Rotate(pipe, origin=sv, axis=[0, 0, 1], angle=phi)
    zzz = Vertex.ByCoordinates(0, 0, 0)
    if endcapA:
        origin = edge.StartVertex()
        x1 = origin.X()
        y1 = origin.Y()
        z1 = origin.Z()
        x2 = edge.EndVertex().X()
        y2 = edge.EndVertex().Y()
        z2 = edge.EndVertex().Z()
        dx = x2 - x1
        dy = y2 - y1
        dz = z2 - z1    
        dist = math.sqrt(dx**2 + dy**2 + dz**2)
        phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
        if dist < 0.0001:
            theta = 0
        else:
            theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
        endcapA = Topology.Copy(endcapA)
        endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 1, 0], angle=theta)
        endcapA = Topology.Rotate(endcapA, origin=zzz, axis=[0, 0, 1], angle=phi+180)
        endcapA = Topology.Translate(endcapA, origin.X(), origin.Y(), origin.Z())
    if endcapB:
        origin = edge.EndVertex()
        x1 = origin.X()
        y1 = origin.Y()
        z1 = origin.Z()
        x2 = edge.StartVertex().X()
        y2 = edge.StartVertex().Y()
        z2 = edge.StartVertex().Z()
        dx = x2 - x1
        dy = y2 - y1
        dz = z2 - z1    
        dist = math.sqrt(dx**2 + dy**2 + dz**2)
        phi = math.degrees(math.atan2(dy, dx)) # Rotation around Y-Axis
        if dist < 0.0001:
            theta = 0
        else:
            theta = math.degrees(math.acos(dz/dist)) # Rotation around Z-Axis
        endcapB = Topology.Copy(endcapB)
        endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 1, 0], angle=theta)
        endcapB = Topology.Rotate(endcapB, origin=zzz, axis=[0, 0, 1], angle=phi+180)
        endcapB = Topology.Translate(endcapB, origin.X(), origin.Y(), origin.Z())
    return {'pipe': pipe, 'endcapA': endcapA, 'endcapB': endcapB}
def Prism(origin=None, width: float = 1, length: float = 1, height: float = 1, uSides: int = 1, vSides: int = 1, wSides: int = 1, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Description

Creates a prism.

Parameters

origin : topologic_core.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_core.Cell
The created prism.
Expand source code
@staticmethod
def Prism(origin= None, width: float = 1, length: float = 1, height: float = 1, uSides: int = 1, vSides: int = 1, wSides: int = 1,
              direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
    """
    Description
    ----------
    Creates a prism.

    Parameters
    ----------
    origin : topologic_core.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_core.Cell
        The created prism.

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

    def sliceCell(cell, width, length, height, uSides, vSides, wSides):
        origin = Topology.Centroid(cell)
        shells = Topology.Shells(cell)
        shell = shells[0]
        wRect = Wire.Rectangle(origin=origin, width=width*1.2, length=length*1.2, direction=[0, 0, 1], placement="center")
        sliceFaces = []
        for i in range(1, wSides):
            sliceFaces.append(Topology.Translate(Face.ByWire(wRect, tolerance=tolerance), 0, 0, height/wSides*i - height*0.5))
        uRect = Wire.Rectangle(origin=origin, width=height*1.2, length=length*1.2, direction=[1, 0, 0], placement="center")
        for i in range(1, uSides):
            sliceFaces.append(Topology.Translate(Face.ByWire(uRect, tolerance=tolerance), width/uSides*i - width*0.5, 0, 0))
        vRect = Wire.Rectangle(origin=origin, width=height*1.2, length=width*1.2, direction=[0, 1, 0], placement="center")
        for i in range(1, vSides):
            sliceFaces.append(Topology.Translate(Face.ByWire(vRect, tolerance=tolerance), 0, length/vSides*i - length*0.5, 0))
        if len(sliceFaces) > 0:
            sliceCluster = Cluster.ByTopologies(sliceFaces)
            shell = Topology.Slice(topologyA=shell, topologyB=sliceCluster, tranDict=False, tolerance=tolerance)
            return Cell.ByShell(shell)
        return cell
    
    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 Topology.IsInstance(origin, "Vertex"):
        print("Cell.Prism - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    xOffset = 0
    yOffset = 0
    zOffset = 0
    if placement.lower() == "center":
        zOffset = -height*0.5
    elif placement.lower() == "lowerleft":
        xOffset = width*0.5
        yOffset = length*0.5
    vb1 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
    vb2 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()-length*0.5+yOffset,origin.Z()+zOffset)
    vb3 = Vertex.ByCoordinates(origin.X()+width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)
    vb4 = Vertex.ByCoordinates(origin.X()-width*0.5+xOffset,origin.Y()+length*0.5+yOffset,origin.Z()+zOffset)

    baseWire = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True)
    baseFace = Face.ByWire(baseWire, tolerance=tolerance)

    prism = Cell.ByThickenedFace(baseFace, thickness=height, bothSides = False)

    if uSides > 1 or vSides > 1 or wSides > 1:
        prism = sliceCell(prism, width, length, height, uSides, vSides, wSides)
    prism = Topology.Orient(prism, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
    return prism
def RemoveCollinearEdges(cell, angTolerance: float = 0.1, tolerance: float = 0.0001)

Removes any collinear edges in the input cell.

Parameters

cell : topologic_core.Cell
The input cell.
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_core.Cell
The created cell without any collinear edges.
Expand source code
@staticmethod
def RemoveCollinearEdges(cell, angTolerance: float = 0.1, tolerance: float = 0.0001):
    """
    Removes any collinear edges in the input cell.

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.
    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_core.Cell
        The created cell without any collinear edges.

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

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.RemoveCollinearEdges - Error: The input cell parameter is not a valid cell. Returning None.")
        return None
    faces = Cell.Faces(cell)
    clean_faces = []
    for face in faces:
        clean_faces.append(Face.RemoveCollinearEdges(face, angTolerance=angTolerance, tolerance=tolerance))
    return Cell.ByFaces(clean_faces, tolerance=tolerance)
def Roof(face, angle: float = 45, epsilon: float = 0.01, tolerance: float = 0.001)

Creates a hipped roof through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao gaoxipeng1998@gmail.com This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel

Parameters

face : topologic_core.Face
The input face.
angle : float , optioal
The desired angle in degrees of the roof. The default is 45.
epsilon : float , optional
The desired epsilon (another form of tolerance for distance from plane). The default is 0.01. (This is set to a larger number as it was found to work better)
tolerance : float , optional
The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)

Returns

cell
The created roof.
Expand source code
@staticmethod
def Roof(face, angle: float = 45, epsilon: float = 0.01 , tolerance: float = 0.001):
    """
        Creates a hipped roof through a straight skeleton. This method is contributed by 高熙鹏 xipeng gao <gaoxipeng1998@gmail.com>
        This algorithm depends on the polyskel code which is included in the library. Polyskel code is found at: https://github.com/Botffy/polyskel

    Parameters
    ----------
    face : topologic_core.Face
        The input face.
    angle : float , optioal
        The desired angle in degrees of the roof. The default is 45.
    epsilon : float , optional
        The desired epsilon (another form of tolerance for distance from plane). The default is 0.01. (This is set to a larger number as it was found to work better)
    tolerance : float , optional
        The desired tolerance. The default is 0.001. (This is set to a larger number as it was found to work better)

    Returns
    -------
    cell
        The created roof.

    """
    from topologicpy.Shell import Shell
    from topologicpy.Cell import Cell
    from topologicpy.Topology import Topology
    
    shell = Shell.Roof(face=face, angle=angle, epsilon=epsilon, tolerance=tolerance)
    faces = Topology.Faces(shell) + [face]
    cell = Cell.ByFaces(faces, tolerance=tolerance)
    if not cell:
        print("Cell.Roof - Error: Could not create a roof cell. Returning None.")
        return None
    return cell
def Sets(cells: list, superCells: list, tolerance: float = 0.0001) ‑> list

Classifies the input cells into sets based on their enclosure within the input list of super cells. The order of the sets follows the order of the input list of super cells.

Parameters

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

Returns

list
The classified list of input cells based on their encolsure within the input list of super cells.
Expand source code
@staticmethod
def Sets(cells: list, superCells: list, tolerance: float = 0.0001) -> list:
    """
        Classifies the input cells into sets based on their enclosure within the input list of super cells. The order of the sets follows the order of the input list of super cells.

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

    Returns
    -------
    list
        The classified list of input cells based on their encolsure within the input list of super cells.

    """

    from topologicpy.Vertex import Vertex
    from topologicpy.Topology import Topology

    if not isinstance(cells, list):
        print("Cell.Sets - Error: The input cells parameter is not a valid list. Returning None.")
        return None
    if not isinstance(superCells, list):
        print("Cell.Sets - Error: The input superCells parameter is not a valid list. Returning None.")
        return None
    cells = [c for c in cells if Topology.IsInstance(c, "Cell")]
    if len(cells) < 1:
        print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
        return None
    superCells = [c for c in superCells if Topology.IsInstance(c, "Cell")]
    if len(cells) < 1:
        print("Cell.Sets - Error: The input cells parameter does not contain any valid cells. Returning None.")
        return None
    if len(superCells) == 0:
        cluster = cells[0]
        for i in range(1, len(cells)):
            oldCluster = cluster
            cluster = cluster.Union(cells[i])
            del oldCluster
        superCells = Topology.Cells(cluster)
    unused = []
    for i in range(len(cells)):
        unused.append(True)
    sets = []
    for i in range(len(superCells)):
        sets.append([])
    for i in range(len(cells)):
        if unused[i]:
            iv = Topology.InternalVertex(cells[i], tolerance=tolerance)
            for j in range(len(superCells)):
                if (Vertex.IsInternal(iv, superCells[j], tolerance)):
                    sets[j].append(cells[i])
                    unused[i] = False
    return sets
def Shells(cell) ‑> list

Returns the shells of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.

Returns

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

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.

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

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.Shells - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    shells = []
    _ = cell.Shells(None, shells) # Hook to Core
    return shells
def Sphere(origin=None, radius: float = 0.5, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Creates a sphere.

Parameters

origin : topologic_core.Vertex , optional
The origin location of the sphere. The default is None which results in the sphere being placed at (0, 0, 0).
radius : float , optional
The radius of the sphere. The default is 0.5.
uSides : int , optional
The number of sides along the longitude of the sphere. The default is 16.
vSides : int , optional
The number of sides along the latitude of the sphere. The default is 8.
direction : list , optional
The vector representing the up direction of the sphere. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the sphere. 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_core.Cell
The created sphere.
Expand source code
@staticmethod
def Sphere(origin= None, radius: float = 0.5, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1],
               placement: str = "center", tolerance: float = 0.0001):
    """
    Creates a sphere.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The origin location of the sphere. The default is None which results in the sphere being placed at (0, 0, 0).
    radius : float , optional
        The radius of the sphere. The default is 0.5.
    uSides : int , optional
        The number of sides along the longitude of the sphere. The default is 16.
    vSides : int , optional
        The number of sides along the latitude of the sphere. The default is 8.
    direction : list , optional
        The vector representing the up direction of the sphere. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the sphere. 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_core.Cell
        The created sphere.

    """

    from topologicpy.Vertex import Vertex
    from topologicpy.Wire import Wire
    from topologicpy.Topology import Topology

    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Sphere - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    c = Wire.Circle(origin=Vertex.Origin(), radius=radius, sides=vSides, fromAngle=-90, toAngle=90, close=False, direction=[0, 0, 1], placement="center")
    c = Topology.Rotate(c, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
    sphere = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
    if Topology.Type(sphere) == Topology.TypeID("CellComplex"):
        sphere = sphere.ExternalBoundary()
    if Topology.Type(sphere) == Topology.TypeID("Shell"):
        sphere = Cell.ByShell(sphere)
    if placement.lower() == "bottom":
        sphere = Topology.Translate(sphere, 0, 0, radius)
    elif placement.lower() == "lowerleft":
        sphere = Topology.Translate(sphere, radius, radius, radius)
    sphere = Topology.Orient(sphere, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
    sphere = Topology.Place(sphere, originA=Vertex.Origin(), originB=origin)
    return sphere
def SurfaceArea(cell, mantissa: int = 6) ‑> float

Returns the surface area of the input cell.

Parameters

cell : topologic_core.Cell
The cell.
mantissa : int , optional
The desired length of the mantissa. The default is 6.

Returns

area : float
The surface area of the input cell.
Expand source code
@staticmethod
def SurfaceArea(cell, mantissa: int = 6) -> float:
    """
    Returns the surface area of the input cell.

    Parameters
    ----------
    cell : topologic_core.Cell
        The cell.
    mantissa : int , optional
        The desired length of the mantissa. The default is 6.

    Returns
    -------
    area : float
        The surface area of the input cell.

    """
    return Cell.Area(cell=cell, mantissa=mantissa)
def Tetrahedron(origin=None, radius: float = 0.5, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Description

Creates a tetrahedron. See https://en.wikipedia.org/wiki/Tetrahedron.

Parameters

origin : topologic_core.Vertex , optional
The origin location of the tetrahedron. The default is None which results in the tetrahedron being placed at (0, 0, 0).
radius : float , optional
The radius of the tetrahedron's circumscribed sphere. The default is 0.5.
direction : list , optional
The vector representing the up direction of the tetrahedron. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the tetrahedron. 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_core.Cell
The created tetrahedron.
Expand source code
@staticmethod
def Tetrahedron(origin= None, radius: float = 0.5,
              direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
    """
    Description
    ----------
    Creates a tetrahedron. See https://en.wikipedia.org/wiki/Tetrahedron.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The origin location of the tetrahedron. The default is None which results in the tetrahedron being placed at (0, 0, 0).
    radius : float , optional
        The radius of the tetrahedron's circumscribed sphere. The default is 0.5.
    direction : list , optional
        The vector representing the up direction of the tetrahedron. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the tetrahedron. 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_core.Cell
        The created tetrahedron.

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

    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Tetrahedron - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None

    vb1 = Vertex.ByCoordinates(math.sqrt(8/9), 0, -1/3)
    vb2 = Vertex.ByCoordinates(-math.sqrt(2/9), math.sqrt(2/3), -1/3)
    vb3 = Vertex.ByCoordinates(-math.sqrt(2/9), -math.sqrt(2/3), -1/3)
    vb4 = Vertex.ByCoordinates(0, 0, 1)
    f1 = Face.ByVertices([vb1, vb2, vb3])
    f2 = Face.ByVertices([vb4, vb1, vb2])
    f3 = Face.ByVertices([vb4, vb2, vb3])
    f4 = Face.ByVertices([vb4, vb3, vb1])
    tetrahedron = Cell.ByFaces([f1, f2, f3, f4])
    tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=0.5, y=0.5, z=0.5)
    tetrahedron = Topology.Scale(tetrahedron, origin=Vertex.Origin(), x=radius/0.5, y=radius/0.5, z=radius/0.5)

    if placement.lower() == "lowerleft":
        tetrahedron = Topology.Translate(tetrahedron, radius, radius, radius)
    elif placement.lower() == "bottom":
        tetrahedron = Topology.Translate(tetrahedron, 0, 0, radius)
    tetrahedron = Topology.Place(tetrahedron, originA=Vertex.Origin(), originB=origin)
    tetrahedron = Topology.Orient(tetrahedron, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
    return tetrahedron
def Torus(origin=None, majorRadius: float = 0.5, minorRadius: float = 0.125, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = 'center', tolerance: float = 0.0001)

Creates a torus.

Parameters

origin : topologic_core.Vertex , optional
The origin location of the torus. The default is None which results in the torus being placed at (0, 0, 0).
majorRadius : float , optional
The major radius of the torus. The default is 0.5.
minorRadius : float , optional
The minor radius of the torus. The default is 0.1.
uSides : int , optional
The number of sides along the longitude of the torus. The default is 16.
vSides : int , optional
The number of sides along the latitude of the torus. The default is 8.
direction : list , optional
The vector representing the up direction of the torus. The default is [0, 0, 1].
placement : str , optional
The description of the placement of the origin of the torus. 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_core.Cell
The created torus.
Expand source code
@staticmethod
def Torus(origin= None, majorRadius: float = 0.5, minorRadius: float = 0.125, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
    """
    Creates a torus.

    Parameters
    ----------
    origin : topologic_core.Vertex , optional
        The origin location of the torus. The default is None which results in the torus being placed at (0, 0, 0).
    majorRadius : float , optional
        The major radius of the torus. The default is 0.5.
    minorRadius : float , optional
        The minor radius of the torus. The default is 0.1.
    uSides : int , optional
        The number of sides along the longitude of the torus. The default is 16.
    vSides : int , optional
        The number of sides along the latitude of the torus. The default is 8.
    direction : list , optional
        The vector representing the up direction of the torus. The default is [0, 0, 1].
    placement : str , optional
        The description of the placement of the origin of the torus. 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_core.Cell
        The created torus.

    """
    
    from topologicpy.Vertex import Vertex
    from topologicpy.Wire import Wire
    from topologicpy.Topology import Topology
    
    if not origin:
        origin = Vertex.ByCoordinates(0, 0, 0)
    if not Topology.IsInstance(origin, "Vertex"):
        print("Cell.Torus - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
        return None
    c = Wire.Circle(origin=Vertex.Origin(), radius=minorRadius, sides=vSides, fromAngle=0, toAngle=360, close=False, direction=[0, 1, 0], placement="center")
    c = Topology.Translate(c, abs(majorRadius-minorRadius), 0, 0)
    torus = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
    if Topology.Type(torus) == Topology.TypeID("Shell"):
        torus = Cell.ByShell(torus)
    if placement.lower() == "bottom":
        torus = Topology.Translate(torus, 0, 0, minorRadius)
    elif placement.lower() == "lowerleft":
        torus = Topology.Translate(torus, majorRadius, majorRadius, minorRadius)

    torus = Topology.Orient(torus, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
    torus = Topology.Place(torus, originA=Vertex.Origin(), originB=origin)
    return torus
def Vertices(cell) ‑> list

Returns the vertices of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.

Returns

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

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.

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

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.Vertices - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    vertices = []
    _ = cell.Vertices(None, vertices) # Hook to Core
    return vertices
def Volume(cell, mantissa: int = 6) ‑> float

Returns the volume of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.
manitssa : int , optional
The desired length of the mantissa. The default is 6.

Returns

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

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.
    manitssa: int , optional
        The desired length of the mantissa. The default is 6.

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

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.Volume - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    volume = None
    try:
        volume = round(topologic.CellUtility.Volume(cell), mantissa) # Hook to Core
    except:
        print("Cell.Volume - Error: Could not compute the volume of the input cell. Returning None.")
        volume = None
    return volume
def Wires(cell) ‑> list

Returns the wires of the input cell.

Parameters

cell : topologic_core.Cell
The input cell.

Returns

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

    Parameters
    ----------
    cell : topologic_core.Cell
        The input cell.

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

    """
    from topologicpy.Topology import Topology

    if not Topology.IsInstance(cell, "Cell"):
        print("Cell.Wires - Error: The input cell parameter is not a valid topologic cell. Returning None.")
        return None
    wires = []
    _ = cell.Wires(None, wires) # Hook to Core
    return wires