Ejemplo n.º 1
0
def what_is_face(face):
    ''' Returns all class names for which this class can be downcasted
    '''
    if not face.ShapeType() == TopAbs_FACE:
        print('%s is not a TopAbs_FACE. Conversion impossible')
        return None
    hs = BRep_Tool_Surface(face)
    obj = hs.GetObject()
    result = []
    for elem in classes:
        if (elem.startswith('Geom') and not 'swig' in elem):
            geom_classes.append(elem)
    # Run the test for each class
    for geom_class in geom_classes:
        if obj.IsKind(geom_class) and not geom_class in result:
            result.append(geom_class)
    return result
Ejemplo n.º 2
0
    def testRemovedByRefFeature(self):
        ''' test that arguments returned by ref transormation is ok
        '''
        from OCC.BRepPrimAPI import BRepPrimAPI_MakeSphere
        from OCC.BRep import BRep_Tool_Surface
        from OCC.GeomLProp import GeomLProp_SLProps
        from OCC.gp import gp_Pnt
        sphere_shape = BRepPrimAPI_MakeSphere(40.).Shape()
        # build a surface from this sphere
        from OCC.Utils.Topology import Topo
        t = Topo(sphere_shape)
        for f in t.faces():
            face = f

        surf = BRep_Tool_Surface(face)
        lprop = GeomLProp_SLProps(0, 1e-12)
        lprop.SetSurface(surf)

        # evaluate_uv_coordinates
        coords = []
        p = 0.0
        # first point
        u, v = [0, 0]
        lprop.SetParameters(u, v)
        pnt = lprop.Value()
        print 'First point coords : ', pnt.Coord()
        print surf.GetObject().Value(u, v).Coord()
        # This one is [40.0,0.,0.]
        self.assertEqual(str(pnt.Coord()), '(40.0, 0.0, 0.0)')
        coords.append(pnt)
        #second point
        u, v = [0.5, 0.5]
        lprop.SetParameters(u, v)
        pnt2 = lprop.Value()
        # check then that the value has not changed (it does if returned by ref)
        self.assertEqual(str(pnt.Coord()), '(40.0, 0.0, 0.0)')
Ejemplo n.º 3
0
class Face(TopoDS_Face, BaseObject):
    """high level surface API
    object is a Face if part of a Solid
    otherwise the same methods do apply, apart from the topology obviously
    """
    def __init__(self, face):
        '''
        '''
        assert isinstance(
            face, TopoDS_Face), 'need a TopoDS_Face, got a %s' % face.__class__
        assert not face.IsNull()
        super(Face, self).__init__()
        BaseObject.__init__(self, 'face')
        # we need to copy the base shape using the following three
        # lines
        assert self.IsNull()
        self.TShape(face.TShape())
        self.Location(face.Location())
        self.Orientation(face.Orientation())
        assert not self.IsNull()

        # cooperative classes
        self.DiffGeom = DiffGeomSurface(self)

        # STATE; whether cooperative classes are yet initialized
        self._curvature_initiated = False
        self._geometry_lookup_init = False

        #===================================================================
        # properties
        #===================================================================
        self._h_srf = None
        self._srf = None
        self._adaptor = None
        self._adaptor_handle = None
        self._classify_uv = None  # cache the u,v classifier, no need to rebuild for every sample
        self._topo = None

        # aliasing of useful methods
        def is_u_periodic(self):
            return self.adaptor.IsUPeriodic()

        def is_v_periodic(self):
            return self.adaptor.IsVPeriodic()

        def is_u_closed(self):
            return self.adaptor.IsUClosed()

        def is_v_closed(self):
            return self.adaptor.IsVClosed()

        def is_u_rational(self):
            return self.adaptor.IsURational()

        def is_v_rational(self):
            return self.adaptor.IsVRational()

        def u_degree(self):
            return self.adaptor.UDegree()

        def v_degree(self):
            return self.adaptor.VDegree()

        def u_continuity(self):
            return self.adaptor.UContinuity()

        def v_continuity(self):
            return self.adaptor.VContinuity()

    def domain(self):
        '''the u,v domain of the curve
        :return: UMin, UMax, VMin, VMax
        '''
        return breptools_UVBounds(self)

    def mid_point(self):
        """
        :return: the parameter at the mid point of the face,
        and its corresponding gp_Pnt
        """
        u_min, u_max, v_min, v_max = self.domain()
        u_mid = (u_min + u_max) / 2.
        v_mid = (v_min + v_max) / 2.
        return ((u_mid, v_mid), self.adaptor.Value(u_mid, v_mid))

    @property
    def topo(self):
        if self._topo is not None:
            return self._topo
        else:
            self._topo = Topo(self)
            return self._topo

    @property
    def surface(self):
        if self._srf is None or self.is_dirty:
            self._h_srf = BRep_Tool_Surface(self)
            self._srf = self._h_srf.GetObject()
        return self._srf

    @property
    def surface_handle(self):
        if self._h_srf is None or self.is_dirty:
            self.surface  # force building handle
        return self._h_srf

    @property
    def adaptor(self):
        if self._adaptor is not None and not self.is_dirty:
            pass
        else:
            self._adaptor = BRepAdaptor_Surface(self)
            self._adaptor_handle = BRepAdaptor_HSurface()
            self._adaptor_handle.Set(self._adaptor)
        return self._adaptor

    @property
    def adaptor_handle(self):
        if self._adaptor_handle is not None and not self.is_dirty:
            pass
        else:
            self.adaptor
        return self._adaptor_handle

    def is_closed(self):
        sa = ShapeAnalysis_Surface(self.surface_handle)
        # sa.GetBoxUF()
        return sa.IsUClosed(), sa.IsVClosed()

    def is_planar(self, tol=TOLERANCE):
        '''checks if the surface is planar within a tolerance
        :return: bool, gp_Pln
        '''
        print(self.surface_handle)
        is_planar_surface = GeomLib_IsPlanarSurface(self.surface_handle, tol)
        return is_planar_surface.IsPlanar()

    def is_trimmed(self):
        """
        :return: True if the Wire delimiting the Face lies on the bounds
        of the surface
        if this is not the case, the wire represents a contour that delimits
        the face [ think cookie cutter ]
        and implies that the surface is trimmed
        """
        _round = lambda x: round(x, 3)
        a = map(_round, breptools_UVBounds(self))
        b = map(_round, self.adaptor.Surface().Surface().GetObject().Bounds())
        if a != b:
            print('a,b', a, b)
            return True
        return False

    def on_trimmed(self, u, v):
        '''tests whether the surface at the u,v parameter has been trimmed
        '''
        if self._classify_uv is None:
            self._classify_uv = BRepTopAdaptor_FClass2d(self, 1e-9)  ##1e-9
        uv = gp_Pnt2d(u, v)
        if self._classify_uv.Perform(uv) == TopAbs_IN:  #IN
            return True
        else:
            return False

    def parameter_to_point(self, u, v):
        '''returns the coordinate at u,v
        '''
        return self.surface.Value(u, v)

    def point_to_parameter(self, pt):
        '''
        returns the uv value of a point on a surface
        @param pt:
        '''
        sas = ShapeAnalysis_Surface(self.surface_handle)
        uv = sas.ValueOfUV(pt, self.tolerance)
        return uv.Coord()

    def continuity_edge_face(self, edge, face):
        """
        compute the continuity between two faces at :edge:

        :param edge: an Edge or TopoDS_Edge from :face:
        :param face: a Face or TopoDS_Face
        :return: bool, GeomAbs_Shape if it has continuity, otherwise
         False, None
        """
        bt = BRep_Tool()
        if bt.HasContinuity(edge, self, face):
            continuity = bt.Continuity(edge, self, face)
            return True, continuity
        else:
            return False, None


#===========================================================================
#    Surface.project
#    project curve, point on face
#===========================================================================

    def project_vertex(self, pnt, tol=TOLERANCE):
        '''projects self with a point, curve, edge, face, solid
        method wraps dealing with the various topologies

        if other is a point:
            returns uv, point

        '''
        if isinstance(pnt, TopoDS_Vertex):
            pnt = BRep_Tool.Pnt(pnt)

        proj = GeomAPI_ProjectPointOnSurf(pnt, self.surface_handle, tol)
        uv = proj.LowerDistanceParameters()
        proj_pnt = proj.NearestPoint()

        return uv, proj_pnt

    def project_curve(self, other):
        # this way Geom_Circle and alike are valid too
        if (isinstance(other, TopoDS_Edge) or isinstance(other, Geom_Curve)
                or issubclass(other, Geom_Curve)):
            # convert edge to curve
            first, last = topexp.FirstVertex(other), topexp.LastVertex(other)
            lbound, ubound = BRep_Tool().Parameter(
                first, other), BRep_Tool().Parameter(last, other)
            other = BRep_Tool.Curve(other, lbound, ubound).GetObject()
            return geomprojlib.Project(other, self.surface_handle)

    def project_edge(self, edg):
        if hasattr(edg, 'adaptor'):
            return self.project_curve(self, self.adaptor)
        return self.project_curve(self, to_adaptor_3d(edg))

    def iso_curve(self, u_or_v, param):
        """
        get the iso curve from a u,v + parameter
        :param u_or_v:
        :param param:
        :return:
        """
        uv = 0 if u_or_v == 'u' else 1
        iso = Adaptor3d_IsoCurve(self.adaptor_handle.GetHandle(), uv, param)
        return iso

    def edges(self):
        return [
            Edge(i)
            for i in WireExplorer(next(self.topo.wires())).ordered_edges()
        ]

    def __repr__(self):
        return self.name

    def __str__(self):
        return self.__repr__()
Ejemplo n.º 4
0
class Face(KbeObject):
    """high level surface API
    object is a Face iff part of a Solid
    otherwise the same methods do apply, apart from the topology obviously
    """
    def __init__(self, face):
        '''
        '''
        from OCC.TopoDS import  TopoDS_Face
        KbeObject.__init__(self, name='face')
        self._topo_type = TopoDS_Face
        self.topo = face


        # cooperative classes
        self.GlobalProperties = GlobalProperties(self)
        self.DiffGeom = DiffGeomSurface(self)

        # STATE; whether cooperative classes are yet initialized
        self._curvature_initiated = False
        self._geometry_lookup_init = False

        self._h_srf           = None
        self._srf             = None
        self._adaptor         = None
        self._adaptor_handle  = None
        self._classify_uv     = None # cache the u,v classifier, no need to rebuild for every sample

        # aliasing of useful methods
        self.is_u_periodic = self.adaptor.IsUPeriodic
        self.is_v_periodic = self.adaptor.IsVPeriodic
        self.is_u_closed   = self.adaptor.IsUClosed
        self.is_v_closed   = self.adaptor.IsVClosed
        self.is_u_rational = self.adaptor.IsURational
        self.is_u_rational = self.adaptor.IsVRational
        self.u_degree       = self.adaptor.UDegree
        self.v_degree       = self.adaptor.VDegree
        self.u_continuity   = self.adaptor.UContinuity
        self.v_continuity   = self.adaptor.VContinuity

        # meh, RuntimeError...
        self.nb_u_knots     = self.adaptor.NbUKnots
        self.nb_v_knots     = self.adaptor.NbVKnots
        self.nb_u_poles     = self.adaptor.NbUPoles
        self.nb_v_poles     = self.adaptor.NbVPoles

    def show(self, *args, **kwargs):
        # TODO: factor out once inheriting from KbeObject
        Display()(self.topo, *args, **kwargs)

    def check(self):
        '''
        interesting for valdating the state of self
        '''
        from OCC.BRepCheck import BRepCheck_Face
        bcf = BRepCheck_Face(self.topo)
        return bcf

    def domain(self):
        '''returns the u,v domain of the curve'''
        from OCC.BRepTools import BRepTools
        return BRepTools.UVBounds(self.topo)

    @property
    def surface(self):
        if self._srf is not None and not self.is_dirty:
            pass
        else:
            self._h_srf = BRep_Tool_Surface(self.topo)
            self._srf = self._h_srf.GetObject()
        return self._srf

    @property
    def surface_handle(self):
        if self._h_srf is not None and not self.is_dirty:
            pass
        else:
            self.surface
        return self._h_srf

    @property
    def adaptor(self):
        if self._adaptor is not None and not self.is_dirty:
            pass
        else:
            from OCC.BRepAdaptor import BRepAdaptor_Surface, BRepAdaptor_HSurface
            self._adaptor = BRepAdaptor_Surface(self.topo)
            self._adaptor_handle = BRepAdaptor_HSurface()
            self._adaptor_handle.Set(self._adaptor)
        return self._adaptor

    @property
    def adaptor_handle(self):
        if self._adaptor_handle is not None and not self.is_dirty:
            pass
        else:
            self.adaptor
        return self._adaptor_handle

    def weight(self, indx):
        '''sets or gets the weight of a control point at the index

        '''
        # TODO: somehow its hard to get a Geom_SplineSurface object from a face
        # nessecary to get control points and weights

        raise NotImplementedError

    def close(self):
        '''if possible, close self'''
        raise NotImplementedError

    @property
    def type(self):
        return 'face'

    def kind(self):
        if not self._geometry_lookup_init:
            self._geometry_lookup = GeometryTypeLookup()
            self._geometry_lookup_init = True
        return self._geometry_lookup[self.topo]

    def is_closed(self):
        from OCC.ShapeAnalysis import ShapeAnalysis_Surface
        sa = ShapeAnalysis_Surface(self.surface_handle)
        sa.GetBoxUF()
        return sa.IsUClosed(), sa.IsVClosed()

    def is_planar(self):
        '''checks if the surface is planar within a tolerance
        :return: bool, gp_Pln
        '''
        aaa = GeomLib_IsPlanarSurface(self.surface_handle, self.tolerance)
        if aaa.IsPlanar():
            return aaa.IsPlanar(), aaa.Plan()
        else:
            return aaa.IsPlanar(), None

    def on_trimmed(self, u, v):
        '''tests whether the surface at the u,v parameter has been trimmed
        '''
        if self._classify_uv is None:
            self._classify_uv  = BRepTopAdaptor_FClass2d(self.topo, 1e-9)
        uv  = gp_Pnt2d(u,v)
        if self._classify_uv.Perform(uv) == TopAbs_IN:
            return True
        else:
            return False

    def parameter_to_point(self, u, v):
        '''returns the coordinate at u,v
        '''
        return self.surface.Value(u,v)

    def point_to_parameter(self, pt):
        '''
        returns the uv value of a point on a surface
        @param pt:
        '''
        from OCC.ShapeAnalysis import ShapeAnalysis_Surface
        sas = ShapeAnalysis_Surface(self.surface_handle)
        uv  = sas.ValueOfUV(pt, self.tolerance)
        return uv.Coord()

    def transform(self, transform):
        '''affine transform
        '''
        raise NotImplementedError

    def continuity_edge_face(self, edge, face):
        """
        compute the continuity between two faces at :edge:

        :param edge: an Edge or TopoDS_Edge from :face:
        :param face: a Face or TopoDS_Face
        :return: bool, GeomAbs_Shape if it has continuity, otherwise
         False, None
        """
        edge = edge if not isinstance(edge,KbeObject) else edge.topo
        face = face if not isinstance(face, KbeObject) else face.topo
        bt = BRep_Tool()
        if bt.HasContinuity(edge, self.topo, face):
            continuity = bt.Continuity(edge, self.topo, face)
            return True, continuity
        else:
            return False, None

#===============================================================================
#    Surface.project
#    project curve, point on face
#===============================================================================

    def project_vertex( self, other ):
        '''projects self with a point, curve, edge, face, solid
        method wraps dealing with the various topologies

        if other is a point:
            returns uv, point

        '''
        if isinstance(other, TopoDS_Face):
            raise AssertionError, 'Cannot project a face on another face'

        elif isinstance(other, TopoDS_Vertex):
            pt = BRep_Tool.Pnt(other)
            proj = GeomAPI_ProjectPointOnSurf(pt, self.surface_handle)
            # SHOULD USE THIS!!!
            #proj.LowerDistanceParameters()
            ext = proj.Extrema()
            for i in range(ext.NbExt()):
                if proj.Point().Coord() == ext.Point(i).Value().Coord():
                    result = ext.Point(i)
            uv = result.Parameter()
            pt = result.Value()
            return uv, pt

    def project_curve(self, other):
        # this way Geom_Circle and alike are valid too
        if isinstance(other, TopoDS_Edge) or\
             isinstance(other, Geom_Curve)  or\
             issubclass(other, Geom_Curve):
                if isinstance(other, TopoDS_Edge):
                    # convert edge to curve
                    first, last = TopExp.FirstVertex(other), TopExp.LastVertex(other)
                    lbound, ubound  = BRep_Tool().Parameter(first, other), BRep_Tool().Parameter(first, other)
                    other = BRep_Tool.Curve(other, lbound, ubound).GetObject()

                from OCC.GeomProjLib import GeomProjLib
                return GeomProjLib().Project(other, self.surface_handle)

    def project_edge(self, edg):
        return self.project_cur

    def iso_curve(self, u_or_v, param):
        """
        :return: an iso curve at parameter `param`
        :param u_or_v: "u" or "v"
        :param param:  the parameter where the iso curve lies
        """
        pass

    def Edges(self):
        return [Edge(i) for i in Topo(self.topo).edges()]
Ejemplo n.º 5
0
class Face(TopoDS_Face, BaseObject):
    """high level surface API
    object is a Face if part of a Solid
    otherwise the same methods do apply, apart from the topology obviously
    """
    def __init__(self, face:TopoDS_Face, **kwargs):
        """
        """
        assert isinstance(face, TopoDS_Face), 'need a TopoDS_Face, got a %s' % face.__class__
        assert not face.IsNull()
        super(Face, self).__init__()
        BaseObject.__init__(self, 'face', **kwargs)
        # we need to copy the base shape using the following three
        # lines
        assert self.IsNull()
        self.TShape(face.TShape())
        self.Location(face.Location())
        self.Orientation(face.Orientation())
        assert not self.IsNull()

        # cooperative classes
        self.DiffGeom = DiffGeomSurface(self)

        # STATE; whether cooperative classes are yet initialized
        self._curvature_initiated = False
        self._geometry_lookup_init = False

        # ===================================================================
        # properties
        # ===================================================================
        self._h_srf = None
        self._srf = None
        self._adaptor = None
        self._adaptor_handle = None
        self._classify_uv = None  # cache the u,v classifier, no need to rebuild for every sample
        self._topo = None

        # aliasing of useful methods
        def is_u_periodic(self):
            return self.adaptor.IsUPeriodic()

        def is_v_periodic(self):
            return self.adaptor.IsVPeriodic()

        def is_u_closed(self):
            return self.adaptor.IsUClosed()

        def is_v_closed(self):
            return self.adaptor.IsVClosed()

        def is_u_rational(self):
            return self.adaptor.IsURational()

        def is_v_rational(self):
            return self.adaptor.IsVRational()

        def u_degree(self):
            return self.adaptor.UDegree()

        def v_degree(self):
            return self.adaptor.VDegree()

        def u_continuity(self):
            return self.adaptor.UContinuity()

        def v_continuity(self):
            return self.adaptor.VContinuity()

    def domain(self) -> Tuple[float, float, float, float]:
        """the u,v domain of the curve
        :return: UMin, UMax, VMin, VMax
        """
        return breptools_UVBounds(self)

    @property
    def area(self, tolerance=1e-5):
        prop = GProp_GProps()
        brepgprop.SurfaceProperties(self, prop, tolerance)
        return prop.Mass()

    def mid_point(self) -> Tuple[Tuple[float, float], gp_Pnt]:
        """
        :return: the parameter at the mid point of the face,
        and its corresponding gp_Pnt
        """
        u_min, u_max, v_min, v_max = self.domain()
        u_mid = (u_min + u_max) / 2.
        v_mid = (v_min + v_max) / 2.
        return (u_mid, v_mid), self.adaptor.Value(u_mid, v_mid)

    @property
    def centroid(self):
        _, m = self.mid_point()
        return m

    @property
    def normal(self) -> gp_Dir:
        return face_normal(self.Shape())

    @property
    def topo(self):
        if self._topo is not None:
            return self._topo
        else:
            self._topo = Topo(self)
            return self._topo

    def Shape(self) -> TopoDS_Face:
        return self.topo.Shape()

    @property
    def surface(self):
        if self._srf is None or self.is_dirty:
            self._h_srf = BRep_Tool_Surface(self)
            self._srf = self._h_srf.GetObject()
        return self._srf

    @property
    def surface_handle(self):
        if self._h_srf is None or self.is_dirty:
            self.surface  # force building handle 
        return self._h_srf

    # ------------------------------------------------------
    @property
    def adaptor(self):
        if self._adaptor is not None and not self.is_dirty:
            pass
        else:
            self._adaptor = BRepAdaptor_Surface(self)
            self._adaptor_handle = BRepAdaptor_HSurface()
            self._adaptor_handle.Set(self._adaptor)
        return self._adaptor

    @property
    def adaptor_handle(self):
        if self._adaptor_handle is not None and not self.is_dirty:
            pass
        else:
            self.adaptor
        return self._adaptor_handle

    def is_closed(self) -> Tuple[bool, bool]:
        sa = ShapeAnalysis_Surface(self.surface_handle)
        # sa.GetBoxUF()
        return sa.IsUClosed(), sa.IsVClosed()

    def is_planar(self, tol=TOLERANCE):
        """checks if the surface is planar within a tolerance
        :return: bool, gp_Pln
        """
        # print(self.surface_handle)
        is_planar_surface = GeomLib_IsPlanarSurface(self.surface_handle, tol)
        return is_planar_surface.IsPlanar()

    def is_trimmed(self):
        """
        :return: True if the Wire delimiting the Face lies on the bounds
        of the surface
        if this is not the case, the wire represents a contour that delimits
        the face [ think cookie cutter ]
        and implies that the surface is trimmed
        """
        _round = lambda x: round(x, 3)
        a = map(_round, breptools_UVBounds(self))
        b = map(_round, self.adaptor.Surface().Surface().GetObject().Bounds())
        if a != b:
            print('a,b', a, b)
            return True
        return False

    def on_trimmed(self, u, v):
        """tests whether the surface at the u,v parameter has been trimmed
        """
        if self._classify_uv is None:
            self._classify_uv = BRepTopAdaptor_FClass2d(self, 1e-9)
        uv = gp_Pnt2d(u, v)
        if self._classify_uv.Perform(uv) == TopAbs_IN:
            return True
        else:
            return False

    def parameter_to_point(self, u, v):
        """returns the coordinate at u,v"""
        return self.surface.Value(u, v)

    def point_to_parameter(self, pt):
        """
        returns the uv value of a point on a surface
        @param pt:
        """
        sas = ShapeAnalysis_Surface(self.surface_handle)
        uv = sas.ValueOfUV(pt, self.tolerance)
        return uv.Coord()

    def continuity_edge_face(self, edge, face):
        """
        compute the continuity between two faces at :edge:

        :param edge: an Edge or TopoDS_Edge from :face:
        :param face: a Face or TopoDS_Face
        :return: bool, GeomAbs_Shape if it has continuity, otherwise
         False, None
        """
        bt = BRep_Tool()
        if bt.HasContinuity(edge, self, face):
            continuity = bt.Continuity(edge, self, face)
            return True, continuity
        else:
            return False, None

    # ===========================================================================
    #    Surface.project
    #    project curve, point on face
    # ===========================================================================
    def project_vertex(self, pnt, tol=TOLERANCE) -> gp_Pnt:
        """projects self with a point, curve, edge, face, solid
        method wraps dealing with the various topologies

        if other is a point:
            returns uv, point

        """
        if isinstance(pnt, TopoDS_Vertex):
            pnt = BRep_Tool.Pnt(pnt)

        proj = GeomAPI_ProjectPointOnSurf(pnt, self.surface_handle, tol)
        # uv = proj.LowerDistanceParameters()
        proj_pnt = proj.NearestPoint()
        return proj_pnt

    def project_curve(self, other):
        # this way Geom_Circle and alike are valid too
        if (isinstance(other, TopoDS_Edge) or
            isinstance(other, Geom_Curve) or
           issubclass(other, Geom_Curve)):
                # convert edge to curve
                first, last = topexp.FirstVertex(other), topexp.LastVertex(other)
                lbound, ubound = BRep_Tool().Parameter(first, other), BRep_Tool().Parameter(last, other)
                other = BRep_Tool.Curve(other, lbound, ubound).GetObject()
                return geomprojlib.Project(other, self.surface_handle)

    def project_edge(self, edg):
        if hasattr(edg, 'adaptor'):
            return self.project_curve(self, self.adaptor)
        return self.project_curve(self, to_adaptor_3d(edg))

    @classmethod
    def create(cls, args):
        """
        * Not done.
        :type F: TopoDS_Face &

        :type P: gp_Pln
        :type UMin: (Optional) float
        :type UMax: (Optional) float
        :type VMin: (Optional) float
        :type VMax: (Optional) float

        :type C: gp_Cylinder
        :type UMin: (Optional) float
        :type UMax: (Optional) float
        :type VMin: (Optional) float
        :type VMax: (Optional) float

        :type C: gp_Cone
        :type UMin: (Optional) float
        :type UMax: (Optional) float
        :type VMin: (Optional) float
        :type VMax: (Optional) float

        :type S: gp_Sphere
        :type UMin: (Optional) float
        :type UMax: (Optional) float
        :type VMin: (Optional) float
        :type VMax: (Optional) float

        :type C: gp_Torus
        :type UMin: (Optional) float
        :type UMax: (Optional) float
        :type VMin: (Optional) float
        :type VMax: (Optional) float

        * Make a face from a Surface. Accepts tolerance value (TolDegen)
        for resolution of degenerated edges.

        :type S: Handle_Geom_Surface &
        :type TolDegen: float

        * Make a face from a Surface. Accepts tolerance value (TolDegen)
        for resolution of degenerated edges.

        :type S: Handle_Geom_Surface &
        :type UMin: (Optional) float
        :type UMax: (Optional) float
        :type VMin: (Optional) float
        :type VMax: (Optional) float
        :type TolDegen: float

        * Find a surface from the wire and make a face.
        if <OnlyPlane> is true, the computed surface will be a plane.
        If it is not possible to find a plane, the flag NotDone will be set.

        :type W: TopoDS_Wire &
        :param OnlyPlane: bool - default value is Standard_False

        * Make a face from a plane and a wire.

        :type P: gp_Pln
        :type W: TopoDS_Wire &
        :param Inside: default value is Standard_True
        :type Inside: bool

        * Make a face from a cylinder and a wire.

        :type C: gp_Cylinder
        :type W: TopoDS_Wire &
        :param Inside: default value is Standard_True
        :type Inside: bool

        * Make a face from a cone and a wire.

        :type C: gp_Cone
        :type W: TopoDS_Wire &
        :param Inside: default value is Standard_True
        :type Inside: bool

        * Make a face from a sphere and a wire.

        :type S: gp_Sphere
        :type W: TopoDS_Wire &
        :param Inside: default value is Standard_True
        :type Inside: bool

        * Make a face from a torus and a wire.

        :type C: gp_Torus
        :type W: TopoDS_Wire &
        :param Inside: default value is Standard_True
        :type Inside: bool

        * Make a face from a Surface and a wire.

        :type S: Handle_Geom_Surface &
        :type W: TopoDS_Wire &
        :param Inside: default value is Standard_True
        :type Inside: bool

        * Adds the wire <W> in the face <F> A general method to create a
        face is to give - a surface S as the support
        (the geometric domain) of the face, - and a wire W to bound it.
        The bounds of the face can also be defined by four parameter values umin, umax, vmin, vmax which determine isoparametric limitations on the parametric space of the surface. In this way, a patch is defined. The parameter values are optional.
        If they are omitted, the natural bounds of the surface are used. A wire is automatically built using the defined bounds. Up to four edges and four vertices are created with this wire (no edge is created when the corresponding parameter value is infinite). Wires can then be added using the function Add to define other restrictions on the face. These restrictions represent holes. More than one wire may be added by this way, provided that the wires do not cross each other and that they define only one area on the surface. (Be careful, however, as this is not checked).
        Forbidden addition of wires Note that in this schema, the third case is valid if edges of the wire W are declared internal to the face. As a result, these edges are no longer bounds of the face. A default tolerance (Precision::Confusion()) is given to the face, this tolerance may be increased during construction of the face using various algorithms. Rules applied to the arguments For the surface: - The surface must not be a 'null handle'. - If the surface is a trimmed surface, the basis surface is used. - For the wire: the wire is composed of connected edges, each edge having a parametric curve description in the parametric domain of the surface;
        in other words, as a pcurve. For the parameters: - The parameter values must be in the parametric range of the surface (or the basis surface, if the surface is trimmed).
        If this condition is not satisfied, the face is not built, and the Error function will return BRepBuilderAPI_ParametersOutOfRange. - The bounding parameters p1 and p2 are adjusted on a periodic surface in a given parametric direction by adding or subtracting the period to obtain p1 in the parametric range of the surface and such p2, that p2 - p1 <= Period, where Period is the period of the surface in this parametric direction. - A parameter value may be infinite. There will be no edge and no vertex in the corresponding direction.

        :type F: TopoDS_Face &
        :type W: TopoDS_Wire &

        """
        # BRepBuilderAPI_MakeFace
        # if isinstance(args, Topo):

        return

    @classmethod
    def from_wire(cls, topo: Union[Topo, TopoDS_Shape], plane: Optional[gp_Pln] = None) -> TopoDS_Face:
        """ create face from compound of edges """
        # if topo.number_of_wires() == 0:
        from .Construct import assert_isdone

        wire_builder = BRepBuilderAPI_MakeWire()
        if isinstance(topo, TopoDS_Shape):
            top = Topo(topo)
        else:
            top = topo

        for edge in top.edges():
            wire_builder.Add(edge)

        with assert_isdone(wire_builder, 'wire'):
            wire_builder.Build()
            w = wire_builder.Wire()

            if plane is not None:
                bface = BRepBuilderAPI_MakeFace(plane)

            else:
                # todo - this doesnt work ----
                _face = TopoDS_Face()
                bface = BRepBuilderAPI_MakeFace(_face)

            bface.Add(w)
            with assert_isdone(bface, 'face'):
                bface.Build()
                face = bface.Shape()
                return face

    @classmethod
    def from_wires(cls, topo: List[TopoDS_Wire], plane: Optional[gp_Pln] = None) -> TopoDS_Face:
        """ create face from compound of edges """
        # if topo.number_of_wires() == 0:
        wire = BRepBuilderAPI_MakeFace()
        for edge in topo:
            wire.Add(edge)
        wire.Build()
        w = wire.Face()
        return w

    @classmethod
    def from_topo_edges(cls, topo: Topo, plane: Optional[gp_Pln]=None) -> TopoDS_Face:
        """ create face from compound of edges """
        # if topo.number_of_wires() == 0:
        wire = BRepBuilderAPI_MakeWire()
        for edge in topo.edges():
            wire.Add(edge)
        wire.Build()
        w = wire.Wire()

        if plane is not None:
            bface = BRepBuilderAPI_MakeFace(plane)

        else:
            # todo - this doesnt work ----
            _face = TopoDS_Face()
            bface = BRepBuilderAPI_MakeFace(_face)

        bface.Add(w)
        bface.Build()
        face = bface.Shape()
        return face

    def project_onto_plane(self, plane: gp_Pln):

        return

    @property
    def outter_wire(self):
        return breptools_OuterWire(self)

    @property
    def inner_wires(self):
        ow = breptools_OuterWire(self)
        inner = []
        topo = Topo(self)
        for wire in topo.wires():
            if wire.IsSame(ow):
                continue
            inner.append(wire)
        return inner

    def iso_curve(self, u_or_v, param):
        """
        get the iso curve from a u,v + parameter
        :param u_or_v:
        :param param:
        :return:
        """
        uv = 0 if u_or_v == 'u' else 1
        iso = Adaptor3d_IsoCurve(self.adaptor_handle.GetHandle(), uv, param)
        return iso

    def edges(self):
        return [Edge(i) for i in WireExplorer(next(self.topo.wires())).ordered_edges()]

    def wires(self):
        topo = Topo(self)
        return [w for w in topo.wires()]

    def __repr__(self):
        return self.name

    def __str__(self):
        return self.__repr__()

    def plane(self):
        from OCC.Geom import Handle_Geom_Plane_DownCast
        _, center = self.mid_point()
        surf = BRep_Tool.Surface(self)
        obj = surf.GetObject()

        # Get resulting shapes
        if obj.DynamicType().GetObject().Name() != "Geom_Plane":
            # todo handle projection of cylindrical surfaces
            # 'expeced Geom_Plane, got {}'.format(obj.DynamicType().GetObject().Name())
            return

        plane = Handle_Geom_Plane_DownCast(surf).GetObject()
        return plane.Pln()