Example #1
0
def face_normal(face):
    from OCC.Core.BRepTools import breptools_UVBounds
    umin, umax, vmin, vmax = breptools_UVBounds(face)
    surf = BRep_Tool().Surface(face)
    props = GeomLProp_SLProps(surf, (umin + umax) / 2., (vmin + vmax) / 2., 1,
                              TOLERANCE)
    norm = props.Normal()
    if face.Orientation() == TopAbs_REVERSED:
        norm.Reverse()
    return norm
Example #2
0
def face_normal(face: TopoDS_Face) -> gp_Dir:
    """

    :param face:
    :return:
    """
    umin, umax, vmin, vmax = breptools_UVBounds(face)
    surf = BRep_Tool().Surface(face)
    props = GeomLProp_SLProps(surf, (umin + umax) / 2., (vmin + vmax) / 2., 1,
                              TOLERANCE)
    norm = props.Normal()
    if face.Orientation() == TopAbs_REVERSED:
        norm.Reverse()
    return norm
def second_derivative(h_surf, u=0, v=0):
    p1 = gp_Pnt()
    pu, pv = gp_Vec(), gp_Vec()
    puu, pvv = gp_Vec(), gp_Vec()
    puv = gp_Vec()
    prop = GeomLProp_SLProps(h_surf, u, v, 1, 1)
    GeomLProp_SurfaceTool.D2(h_surf, u, v, p1, pu, pv, puu, pvv, puv)
    e0 = pu.Crossed(pv)
    pu.Normalize()
    pv.Normalize()
    e0.Normalize()
    puu.Normalize()
    pvv.Normalize()
    puv.Normalize()
    print(p1)
    print("pu", pu)
    print("pv", pv)
    print("e0", e0)
    print("puu", puu)
    print("pvv", pvv)
    print("puv", puv)

    first_form = np.array([[pu.Dot(pu), pu.Dot(pv)], [pv.Dot(pu), pv.Dot(pv)]])
    secnd_form = np.array([[e0.Dot(puu), e0.Dot(puv)],
                           [e0.Dot(puv), e0.Dot(pvv)]])

    print(first_form)
    print(secnd_form)
    print(prop.GaussianCurvature())
    print(prop.MeanCurvature())
    d1, d2 = gp_Dir(), gp_Dir()
    prop.CurvatureDirections(d1, d2)
    a1 = gp_Ax3()
    v1 = dir_to_vec(d1)
    v2 = dir_to_vec(d2)
    if pu.IsParallel(v1, 1 / 1000):
        c1 = prop.MaxCurvature()
        c2 = prop.MinCurvature()
        print(v1.Dot(pu), v1.Dot(pv))
        print(v2.Dot(pu), v2.Dot(pv))
    else:
        c1 = prop.MinCurvature()
        c2 = prop.MaxCurvature()
        print(v1.Dot(pu), v1.Dot(pv))
        print(v2.Dot(pu), v2.Dot(pv))
    print(c1, 1 / c1)
    print(c2, 1 / c2)

    px = np.linspace(-1, 1, 100) * 100
    p1_y = px**2 / c1
    p2_y = px**2 / c1
    curv1 = curv_spl(px, p1_y)
    curv2 = curv_spl(px, p2_y)
Example #4
0
    def curvature(self, u,v):
        '''returns the curvature at the u parameter
        the curvature object can be returned too using curvatureType == curvatureType
        curvatureTypes are:
            gaussian
            minimum
            maximum
            mean
            curvatureType
        '''
        if not self._curvature_initiated:
            from OCC.GeomLProp import GeomLProp_SLProps
            self._curvature = GeomLProp_SLProps(self.instance.h_srf, u, v, 1, 1e-6)
        else:
            self._curvature.SetParameters(u,v)
            self._curvature_initiated = True

        return self._curvature
def radius_at_uv(face, u, v):
    '''
    returns the mean radius at a u,v coordinate
    @param face:    surface input
    @param u,v:     u,v coordinate
    '''
    h_srf = BRep_Tool().Surface(face)
    uv_domain = GeomLProp_SurfaceTool().Bounds(h_srf)
    curvature = GeomLProp_SLProps(h_srf, u, v, 1, 1e-6)
    try:
        _crv_min = 1. / curvature.MinCurvature()
    except ZeroDivisionError:
        _crv_min = 0.

    try:
        _crv_max = 1. / curvature.MaxCurvature()
    except ZeroDivisionError:
        _crv_max = 0.
    return abs((_crv_min + _crv_max) / 2.)
Example #6
0
    def curvature(self, u, v):
        '''returns the curvature at the u parameter
        the curvature object can be returned too using
        curvatureType == curvatureType
        curvatureTypes are:
            gaussian
            minimum
            maximum
            mean
            curvatureType
        '''
        if not self._curvature_initiated:
            self._curvature = GeomLProp_SLProps(self.instance.surface_handle,
                                                u, v, 2, 1e-7)

        _domain = self.instance.domain()
        if u in _domain or v in _domain:
            print('<<<CORRECTING DOMAIN...>>>')
            div = 1000
            delta_u, delta_v = (_domain[0] - _domain[1]) / div, (
                _domain[2] - _domain[3]) / div

            if u in _domain:
                low, hi = u - _domain[0], u - _domain[1]
                if low < hi:
                    u = u - delta_u
                else:
                    u = u + delta_u

            if v in _domain:
                low, hi = v - _domain[2], v - _domain[3]
                if low < hi:
                    v = v - delta_v
                else:
                    v = v + delta_v

        self._curvature.SetParameters(u, v)
        self._curvature_initiated = True

        return self._curvature
Example #7
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)')
Example #8
0
class DiffGeomSurface(object):
    def __init__(self, instance):
        self.instance = instance
        self._curvature = None
        self._curvature_initiated = False

    def curvature(self, u, v):
        '''returns the curvature at the u parameter
        the curvature object can be returned too using
        curvatureType == curvatureType
        curvatureTypes are:
            gaussian
            minimum
            maximum
            mean
            curvatureType
        '''
        if not self._curvature_initiated:
            self._curvature = GeomLProp_SLProps(self.instance.surface_handle,
                                                u, v, 2, 1e-7)

        _domain = self.instance.domain()
        if u in _domain or v in _domain:
            print('<<<CORRECTING DOMAIN...>>>')
            div = 1000
            delta_u, delta_v = (_domain[0] - _domain[1]) / div, (
                _domain[2] - _domain[3]) / div

            if u in _domain:
                low, hi = u - _domain[0], u - _domain[1]
                if low < hi:
                    u = u - delta_u
                else:
                    u = u + delta_u

            if v in _domain:
                low, hi = v - _domain[2], v - _domain[3]
                if low < hi:
                    v = v - delta_v
                else:
                    v = v + delta_v

        self._curvature.SetParameters(u, v)
        self._curvature_initiated = True

        return self._curvature

    def gaussian_curvature(self, u, v):
        return self.curvature(u, v).GaussianCurvature()

    def min_curvature(self, u, v):
        return self.curvature(u, v).MinCurvature()

    def mean_curvature(self, u, v):
        return self.curvature(u, v).MeanCurvature()

    def max_curvature(self, u, v):
        return self.curvature(u, v).MaxCurvature()

    def normal(self, u, v):
        # TODO: should make this return a gp_Vec
        curv = self.curvature(u, v)
        if curv.IsNormalDefined():
            return curv.Normal()
        else:
            raise ValueError('normal is not defined at u,v: {0}, {1}'.format(
                u, v))

    def tangent(self, u, v):
        dU, dV = gp_Dir(), gp_Dir()
        curv = self.curvature(u, v)
        if curv.IsTangentUDefined() and curv.IsTangentVDefined():
            curv.TangentU(dU), curv.TangentV(dV)
            return dU, dV
        else:
            return None, None

    def radius(self, u, v):
        '''returns the radius at u
        '''
        # TODO: SHOULD WE RETURN A SIGNED RADIUS? ( get rid of abs() )?
        try:
            _crv_min = 1. / self.min_curvature(u, v)
        except ZeroDivisionError:
            _crv_min = 0.

        try:
            _crv_max = 1. / self.max_curvature(u, v)
        except ZeroDivisionError:
            _crv_max = 0.
        return abs((_crv_min + _crv_max) / 2.)
def evalcurvature(CADsurface, CADu, CADv, finetol, approxoutwardnormal=None):
    """ Evaluate the curvature of the specified CAD surface
    at the given CAD (u,v) coordinates, with the given tolerance.
    Returns two element vector of principal curvatures (in 1/mm),
    followed by corresponding axes (in 3D space). Returns all NaN
    if curvature is undefined at this point. 

    WARNING: OpenCascade does not always select a normal in a consistent
             (i.e. u cross v pointing outside the object boundary) way.
             Set approxoutwardnormal to force the interpretation of the
             sense of the normal direction
             
"""
    # Create props object with derivatives
    Curvature = GeomLProp_SLProps(
        CADsurface,
        CADu,
        CADv,
        2,  # Calculate 2 derivatives
        finetol)

    if not Curvature.IsCurvatureDefined() or not Curvature.IsTangentUDefined(
    ) or not Curvature.IsTangentVDefined():
        # Return all NaN's.
        return (np.array((np.NaN, np.NaN)), np.ones(
            (3, 2), dtype='d') * np.NaN, np.ones(3) * np.NaN,
                np.ones(3) * np.NaN, np.ones(3) * np.NaN)

    Sigma_u = Curvature.D1U()  # vector 1st derivative along U axis
    Sigma_v = Curvature.D1V()  # along V axis
    Sigma_uu = Curvature.D2U()  # vector 2nd derivative along U axis
    Sigma_vv = Curvature.D2V()  # vector 2nd derivative along V axis
    Sigma_uv = Curvature.DUV()  # Vector cross-derivative
    Normal = gp_Vec(Curvature.Normal())
    NormalVec = np.array((Normal.X(), Normal.Y(), Normal.Z()), dtype='d')
    NormalVec = NormalVec / np.linalg.norm(NormalVec)

    if approxoutwardnormal is None:
        approxoutwardnormal = NormalVec
        pass

    TangentU_Dir = gp_Dir()
    Curvature.TangentU(
        TangentU_Dir)  # Store tangent u direction in TangentU_Dir
    TangentU = gp_Vec(TangentU_Dir)

    TangentV_Dir = gp_Dir()
    Curvature.TangentV(
        TangentV_Dir)  # Store tangent v direction in TangentV_Dir
    TangentV = gp_Vec(TangentV_Dir)

    ## if NormalVec is in the wrong direction we interchange U
    ## and V so as to correctly represent the desired right handedness
    ## of the frame with outward vector facing out.
    ## This also involves flipping NormalVec
    if np.inner(NormalVec, approxoutwardnormal) < 0.0:
        NormalVec = -NormalVec
        Normal = Normal.Reversed()

        temp1 = Sigma_u
        Sigma_u = Sigma_v
        Sigma_v = temp1

        temp2 = Sigma_uu
        Sigma_uu = Sigma_vv
        Sigma_vv = temp2

        temp3 = TangentU
        TangentU = TangentV
        TangentV = temp3

        pass

    # First fundamental form:
    # See https://en.wikipedia.org/wiki/Differential_geometry_of_surfaces
    # https://en.wikipedia.org/wiki/First_fundamental_form
    # and http://www.cems.uvm.edu/~gswarrin/math295/Chap6.pdf

    E = Sigma_u.Dot(Sigma_u)
    F = Sigma_u.Dot(Sigma_v)
    G = Sigma_v.Dot(Sigma_v)

    # Second fundamental form:
    L = Sigma_uu.Dot(Normal)
    M = Sigma_uv.Dot(Normal)
    N = Sigma_vv.Dot(Normal)

    # Evaluate shape operator
    # https://en.wikipedia.org/wiki/Differential_geometry_of_surfaces#Shape_operator
    #  where e-> L, f->M, g->N

    S = (1.0 / (E * G - F**2.0)) * np.array(
        ((L * G - M * F, M * G - N * F), (M * E - L * F, N * E - M * F)),
        dtype='d'
    )  #  (see wikipedia + https://math.stackexchange.com/questions/536563/trouble-computing-the-shape-operator) http://mathworld.wolfram.com/WeingartenEquations.html

    # Shape operator in general will only be symmetric if (u,v) basis
    # is orthonormal, which it may not be
    #print(S)

    # Eigenvalue problem seems to be OK anyway so stuff below commented out
    #
    ## Apply Gram-Schmidt orthonormalization to find an ON basis
    Uvec = np.array((TangentU.X(), TangentU.Y(), TangentU.Z()), dtype='d')
    Uvec = Uvec / np.linalg.norm(Uvec)

    Vvec = np.array((TangentV.X(), TangentV.Y(), TangentV.Z()), dtype='d')
    Vvec = Vvec / np.linalg.norm(Vvec)

    # Check that Uvec cross Vvec = + Normal
    # Note that this verifies the handedness of the coordinate frame,
    # i.e. that U cross V gives an outward normal
    # and is thus important
    Wvec = np.cross(Uvec, Vvec)
    Wnormvec = Wvec / np.linalg.norm(Wvec)
    assert (np.abs(
        np.dot(Wnormvec,
               np.array((Normal.X(), Normal.Y(), Normal.Z()), dtype='d')) -
        1.0) < 0.025)

    ## Inner product matrix, for converting between covariant
    ## and contravariant components
    #IPMat = np.array( ((np.inner(Uvec,Uvec),np.inner(Uvec,Vvec)),
    #                   (np.inner(Vvec,Uvec),np.inner(Vvec,Vvec))),dtype='d')
    #
    #IPMatInv=np.linalg.inv(IPMat)
    #
    ##def proj(direc,vec): # Projection operator
    ##    return (np.inner(vec,direc)/np.inner(direc,direc))*direc
    #
    ## Vprimevec=Vvec-proj(Uvec,Vvec)
    #
    #Vprimevec=Vvec - (np.inner(Uvec,Vvec)/np.inner(Uvec,Uvec)) * Uvec
    #
    #Vprimenormfactor=1/np.linalg.norm(Vprimevec)
    #Vprimevec=Vprimevec*Vprimenormfactor
    #
    ## Covariant components of a 3D vector in (U,V)
    ## Upos_cov = Uvec dot 3dcoords
    ## Vpos_cov = Vvec dot 3dcoords
    #
    ## S matrix times contravariant components -> Contravariant components
    ## so S * IPMatInv * (Uvec;Vvec) * 3dcoords -> Contravariant components
    #
    ## Let (Uprimepos,Vprimepos) = (Uvec dot 3dcoords, Vprimevec dot 3dcoords)
    ## ON basis
    #
    ## [ Uprime ] = [           1                                               0 ] [ Uvec  ]
    ## [ Vprime ]   [ -Vprimenormfactor*(Uvec dot Vvec)/(Uvec dot Uvec)    Vprimenormfactor ] [ Vvec  ]
    ##
    ## Therefore
    ## [ Uvec ] =   [          1                                                    0     ]^-1  [ Uprime ]
    ## [ Vvec ] =   [ -Vprimenormfactor*(Uvec dot Vvec)/(Uvec dot Uvec)  Vprimenormfactor ]     [ Vprime ]
    ##
    ## So a vector represented by  some coefficient times Uprime plus
    ## some other coefficient * Vprime, multiplied
    ## on the left by the inverse matrix, gives rise to
    ## some new coefficient * Uvec  plus  some other new coefficient * Vvec

    ## These new coefficients are contravariant components.
    ##
    ## Let A = [           1                       0 ]
    ##       = [ -(Uvec dot Vvec)/(Uvec dot Uvec)  1 ]*Vprimenormfactor
    #
    ## Now A*S*inv(A)  provides a conversion of S to the Uprime,Vprime ON frame.
    ## ... Which should then be symmetric.
    #A = np.array(((1.0,0.0),
    #              (-Vprimenormfactor*np.inner(Uvec,Vvec)/np.inner(Uvec,Uvec),Vprimenormfactor)),dtype='d')
    #
    #Stransformed=np.dot(A,np.dot(S,np.linalg.inv(A)))

    ## Confirm that Stransformed is indeed symmetric to reasonable
    ## precision... Use the trace (twice the mean curvature)
    ## as a reference metric
    #assert(abs(Stransformed[0,1]-Stransformed[1,0]) <= 1e-7*np.trace(Stransformed))

    ## Force symmetry so our eigenvalue problem comes out real
    #Stransformed[0,1]=Stransformed[1,0]

    #(evals,evects) = np.linalg.eig(Stransformed)
    # Should be OK to just take eigenvalues of asymmetric matrix
    # per ShifrinDiffGeo.pdf page 51
    (curvatures, evects) = np.linalg.eig(S)

    ## Now evects * diag(evals) * (evects.T) should be Stransformed
    ## Convert to 3D basis
    ##  [ Uprime Vprime ] * evects * diag(evals) * evects.T * [ Uprime ; Vprime ]
    ## So store [ Uprime Vprime ] * evects  as principal curvature tangent directions
    ##   and evals as principal curvatures
    #curvaturetangents = np.dot(np.array((Uvec,Vprimevec),dtype='d').T,evects)
    curvaturetangents = np.dot(np.array((Uvec, Vvec)).T, evects)
    # per ShifrinDiffGeo.pdf page 52 confirm these tangents are orthogonal
    #if np.dot(curvaturetangents[:,0],curvaturetangents[:,1]) >= 1e-8:
    if np.dot(curvaturetangents[:, 0], curvaturetangents[:, 1]) >= 1e-2:
        raise GeometricInconsistency(
            "Curvature tangents are not orthogonal (inner product = %g)" %
            (np.dot(curvaturetangents[:, 0], curvaturetangents[:, 1])))
    # We don't want the eigenframe to be mirrored relative to the (U,V)
    # frame, for consistency in interpreting positive vs. negative curvature.
    # ...  so if the dot/inner product of (UxV) with (TANGENT0xTANGENT1)
    # is negative, that indicates mirroring
    # Negating one of the eigenvectors will un-mirror it.

    # The exception to this is if our normal and the desired approxoutwardnormal
    # are in opposite directions, then we DO want to mirror it, to
    # correct for OpenCascade's normal being in the wrong direction

    # Note: '^' here operating on booleans acts as a logical XOR operator
    #if not((np.inner(np.cross(Uvec,Vvec),np.cross(curvaturetangents[:,0],curvaturetangents[:,1])) < 0.0) ^ (np.inner(NormalVec,approxoutwardnormal) >= 0.0)):
    if np.inner(np.cross(Uvec, Vvec),
                np.cross(curvaturetangents[:, 0], curvaturetangents[:,
                                                                    1])) < 0.0:
        curvaturetangents[:, 0] = -curvaturetangents[:, 0]
        pass

    ## Likewise, if NormalVec is in the wrong direction we flip it
    ## and UVec so as to correctly represent the desired right handedness
    ## of the frame with outward vector facing out.
    #if np.inner(NormalVec,approxoutwardnormal) < 0.0:
    #    NormalVec=-NormalVec
    #    Uvec=-Uvec
    #    pass

    # transpose curvaturetangents so it is human readable in the serialization
    # axes:  Which Vertex, tangent u or v, coord x,yz
    return (curvatures, curvaturetangents.transpose(1,
                                                    0), NormalVec, Uvec, Vvec)
Example #10
0
class DiffGeomSurface(object):
    def __init__(self, instance):
        self.instance = instance
        self._curvature = None
        self._curvature_initiated = False

    def curvature(self, u,v):
        '''returns the curvature at the u parameter
        the curvature object can be returned too using curvatureType == curvatureType
        curvatureTypes are:
            gaussian
            minimum
            maximum
            mean
            curvatureType
        '''
        if not self._curvature_initiated:
            from OCC.GeomLProp import GeomLProp_SLProps
            self._curvature = GeomLProp_SLProps(self.instance.h_srf, u, v, 1, 1e-6)
        else:
            self._curvature.SetParameters(u,v)
            self._curvature_initiated = True

        return self._curvature

    def gaussian_curvature(self,u,v):
        return self.curvature(u,v).GaussianCurvature()

    def min_curvature(self,u,v):
        return self.curvature(u,v).MinCurvature()

    def mean_curvature(self,u,v):
        return self.curvature(u,v).MeanCurvature()

    def max_curvature(self,u,v):
        return self.curvature(u,v).MaxCurvature()

    def normal(self,u,v):
        # TODO: should make this return a gp_Vec
        curv = self.curvature(u,v)
        if curv.IsNormalDefined():
            return curv.Normal()
        else:
            raise ValueError('normal is not defined at u,v: {0}, {1}'.format(u,v))

    def tangent(self,u,v, recurse=False):
        dU, dV = gp_Dir(), gp_Dir()
        curv = self.curvature(u,v)
        if curv.IsCurvatureDefined():
            curv.TangentU(dU), curv.TangentV(dV)
            return dU, dV
        else:
            # most likely the curvature is just out of bounds...
            # move it a little close to the center of the surface...
            tol = 1e-2

            print 'trying to fix shit...'

            domain = self.instance.domain()
            if u in domain:
                uorv = 'u'
            elif v in domain:
                uorv = 'v'
            else:
                raise ValueError('no curvature defined while sample does not lie on border...')

            if uorv is not None:
                indx = domain.index(u) if uorv == 'u' else domain.index(v)
                if indx in (1,3): # v
                    v = v + tol if indx == 1 else v - tol
                else:
                    u = u + tol if indx == 0 else u - tol
                print 'hopefully fixed it?'
                if not recurse:
                    self.tangent(u,v, True)
                else:
                    return None

    def radius(self, u, v ):
        '''returns the radius at u
        '''
        # TODO: SHOULD WE RETURN A SIGNED RADIUS? ( get rid of abs() )?
        try:
            _crv_min = 1./self.min_curvature(u,v)
        except ZeroDivisionError:
            _crv_min = 0.

        try:
            _crv_max = 1./self.max_curvature(u,v)
        except ZeroDivisionError:
            _crv_max = 0.
        return abs((_crv_min+_crv_max)/2.)

    def frenet_frame(self, u, v):
        '''returns the frenet frame ( the 2 tangency directions + normal ) syntax sugar
        '''
        pass

    def derivative_u(self, u, n):
        '''return n derivatives of u
        '''
        pass

    def derivative_v(self, v, n):
        '''return n derivatives of v
        '''
        pass

    def torsion(self, u, v):
        '''returns the torsion at the parameter
        http://en.wikipedia.org/wiki/Frenet-Serret_formulas
        '''
        pass

    def continuity(self, face):
        '''returns continuity between self and another surface
        '''
        # add dictionary mapping which G / C continuity it is...
        return self.surface.Continuity()

    def inflection_parameters(self):
        """
        :return: a list of tuples (u,v) of parameters
        where there are inflection points on the edge

        returns None if no inflection parameters are found
        """
        pass
Example #11
0
class DiffGeomSurface(object):
    def __init__(self, instance):
        self.instance = instance
        self._curvature = None
        self._curvature_initiated = False

    def curvature(self, u, v):
        '''returns the curvature at the u parameter
        the curvature object can be returned too using curvatureType == curvatureType
        curvatureTypes are:
            gaussian
            minimum
            maximum
            mean
            curvatureType
        '''
        if not self._curvature_initiated:
            self._curvature = GeomLProp_SLProps(self.instance.surface_handle,
                                                u, v, 1, 1e-6)

        _domain = self.instance.domain()
        if u in _domain or v in _domain:
            print '<<<CORRECTING DOMAIN...>>>'
            div = 1000
            delta_u, delta_v = (_domain[0] - _domain[1]) / div, (
                _domain[2] - _domain[3]) / div

            if u in _domain:
                low, hi = u - _domain[0], u - _domain[1]
                if low < hi:
                    u = u - delta_u
                else:
                    u = u + delta_u

            if v in _domain:
                low, hi = v - _domain[2], v - _domain[3]
                if low < hi:
                    v = v - delta_v
                else:
                    v = v + delta_v

        self._curvature.SetParameters(u, v)
        self._curvature_initiated = True

        return self._curvature

    def gaussian_curvature(self, u, v):
        return self.curvature(u, v).GaussianCurvature()

    def min_curvature(self, u, v):
        return self.curvature(u, v).MinCurvature()

    def mean_curvature(self, u, v):
        return self.curvature(u, v).MeanCurvature()

    def max_curvature(self, u, v):
        return self.curvature(u, v).MaxCurvature()

    def normal(self, u, v):
        # TODO: should make this return a gp_Vec
        curv = self.curvature(u, v)
        if curv.IsNormalDefined():
            return curv.Normal()
        else:
            raise ValueError('normal is not defined at u,v: {0}, {1}'.format(
                u, v))

    def tangent(self, u, v):
        dU, dV = gp_Dir(), gp_Dir()
        curv = self.curvature(u, v)
        if curv.IsTangentUDefined() and curv.IsTangentVDefined():
            curv.TangentU(dU), curv.TangentV(dV)
            return dU, dV
        else:
            return None, None

    def radius(self, u, v):
        '''returns the radius at u
        '''
        # TODO: SHOULD WE RETURN A SIGNED RADIUS? ( get rid of abs() )?
        try:
            _crv_min = 1. / self.min_curvature(u, v)
        except ZeroDivisionError:
            _crv_min = 0.

        try:
            _crv_max = 1. / self.max_curvature(u, v)
        except ZeroDivisionError:
            _crv_max = 0.
        return abs((_crv_min + _crv_max) / 2.)

    def frenet_frame(self, u, v):
        '''returns the frenet frame ( the 2 tangency directions + normal ) syntax sugar
        '''
        pass

    def derivative_u(self, u, n):
        '''return n derivatives of u
        '''
        pass

    def derivative_v(self, v, n):
        '''return n derivatives of v
        '''
        pass

    def torsion(self, u, v):
        '''returns the torsion at the parameter
        http://en.wikipedia.org/wiki/Frenet-Serret_formulas
        '''
        pass

    def continuity(self, face):
        '''returns continuity between self and another surface
        '''
        # add dictionary mapping which G / C continuity it is...
        return self.surface.Continuity()

    def inflection_parameters(self):
        """
        :return: a list of tuples (u,v) of parameters
        where there are inflection points on the edge

        returns None if no inflection parameters are found
        """
        pass