Esempio n. 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
Esempio n. 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 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)