示例#1
0
def booleanOperation(actor1,
                     actor2,
                     operation='plus',
                     c=None,
                     alpha=1,
                     wire=False,
                     bc=None,
                     edges=False,
                     legend=None,
                     texture=None):
    '''Volumetric union, intersection and subtraction of surfaces'''
    try:
        bf = vtk.vtkBooleanOperationPolyDataFilter()
    except AttributeError:
        vio.printc('Boolean operation only possible for vtk version >= 8', 'r')
        return None
    poly1 = vu.polydata(actor1, True)
    poly2 = vu.polydata(actor2, True)
    if operation.lower() == 'plus':
        bf.SetOperationToUnion()
    elif operation.lower() == 'intersect':
        bf.SetOperationToIntersection()
    elif operation.lower() == 'minus':
        bf.SetOperationToDifference()
        bf.ReorientDifferenceCellsOn()
    if vu.vtkMV:
        bf.SetInputData(0, poly1)
        bf.SetInputData(1, poly2)
    else:
        bf.SetInputConnection(0, poly1.GetProducerPort())
        bf.SetInputConnection(1, poly2.GetProducerPort())
    bf.Update()
    actor = vu.makeActor(bf.GetOutput(), c, alpha, wire, bc, edges, legend,
                         texture)
    return actor
示例#2
0
def removeOutliers(points, radius, c='k', alpha=1, legend=None):
    '''
    Remove outliers from a cloud of points within radius search
    '''
    isactor = False
    if isinstance(points, vtk.vtkActor):
        isactor = True
        poly = vu.polydata(points)
    else:
        src = vtk.vtkPointSource()
        src.SetNumberOfPoints(len(points))
        src.Update()
        vpts = src.GetOutput().GetPoints()
        for i, p in enumerate(points):
            vpts.SetPoint(i, p)
        poly = src.GetOutput()

    removal = vtk.vtkRadiusOutlierRemoval()
    vu.setInput(removal, poly)

    removal.SetRadius(radius)
    removal.SetNumberOfNeighbors(5)
    removal.GenerateOutliersOff()
    removal.Update()
    rpoly = removal.GetOutput()
    print("# of removed outlier points: ", removal.GetNumberOfPointsRemoved(),
          '/', poly.GetNumberOfPoints())
    outpts = []
    for i in range(rpoly.GetNumberOfPoints()):
        outpts.append(list(rpoly.GetPoint(i)))
    outpts = np.array(outpts)
    if not isactor: return outpts

    actor = vs.points(outpts, c=c, alpha=alpha, legend=legend)
    return actor  # return same obj for concatenation
示例#3
0
def decimate(actor, fraction=0.5, N=None, verbose=True, boundaries=True):
    '''
    Downsample the number of vertices in a mesh.
        fraction gives the desired target of reduction. 
        E.g. fraction=0.1
             leaves 10% of the original nr of vertices.
    '''
    poly = vu.polydata(actor, True)
    if N:  # N = desired number of points
        Np = poly.GetNumberOfPoints()
        fraction = float(N) / Np
        if fraction >= 1: return actor

    decimate = vtk.vtkDecimatePro()
    vu.setInput(decimate, poly)
    decimate.SetTargetReduction(1. - fraction)
    decimate.PreserveTopologyOff()
    if boundaries: decimate.BoundaryVertexDeletionOn()
    else: decimate.BoundaryVertexDeletionOff()
    decimate.Update()
    if verbose:
        print('Input nr. of pts:', poly.GetNumberOfPoints(), end='')
        print(' output:', decimate.GetOutput().GetNumberOfPoints())
    mapper = actor.GetMapper()
    vu.setInput(mapper, decimate.GetOutput())
    mapper.Update()
    actor.Modified()
    if hasattr(actor, 'poly'): actor.poly = decimate.GetOutput()
    return actor  # return same obj for concatenation
示例#4
0
def subdivide(actor, N=1, method=0, legend=None):
    '''
    Increase the number of points in actor surface
        N = number of subdivisions
        method = 0, Loop
        method = 1, Linear
        method = 2, Adaptive
        method = 3, Butterfly
    '''
    triangles = vtk.vtkTriangleFilter()
    vu.setInput(triangles, vu.polydata(actor))
    triangles.Update()
    originalMesh = triangles.GetOutput()
    if method == 0: sdf = vtk.vtkLoopSubdivisionFilter()
    elif method == 1: sdf = vtk.vtkLinearSubdivisionFilter()
    elif method == 2: sdf = vtk.vtkAdaptiveSubdivisionFilter()
    elif method == 3: sdf = vtk.vtkButterflySubdivisionFilter()
    else:
        vio.printc('Error in subdivide: unknown method.', 'r')
        exit(1)
    if method != 2: sdf.SetNumberOfSubdivisions(N)
    vu.setInput(sdf, originalMesh)
    sdf.Update()
    out = sdf.GetOutput()
    if legend is None and hasattr(actor, 'legend'): legend = actor.legend
    sactor = vu.makeActor(out, legend=legend)
    sactor.GetProperty().SetOpacity(actor.GetProperty().GetOpacity())
    sactor.GetProperty().SetColor(actor.GetProperty().GetColor())
    sactor.GetProperty().SetRepresentation(
        actor.GetProperty().GetRepresentation())
    return sactor
示例#5
0
def surfaceIntersection(actor1,
                        actor2,
                        tol=1e-06,
                        lw=3,
                        c=None,
                        alpha=1,
                        legend=None):
    '''Intersect 2 surfaces and return a line actor'''
    try:
        bf = vtk.vtkIntersectionPolyDataFilter()
    except AttributeError:
        vio.printc('surfaceIntersection only possible for vtk version > 6',
                   'r')
        return None
    poly1 = vu.polydata(actor1, True)
    poly2 = vu.polydata(actor2, True)
    bf.SetInputData(0, poly1)
    bf.SetInputData(1, poly2)
    bf.Update()
    if c is None: c = actor1.GetProperty().GetColor()
    actor = vu.makeActor(bf.GetOutput(), c, alpha, 0, legend=legend)
    actor.GetProperty().SetLineWidth(lw)
    return actor
示例#6
0
def align(source, target, iters=100, legend=None):
    '''
    Return a copy of source actor which is aligned to
    target actor through vtkIterativeClosestPointTransform() method.
    '''
    sprop = source.GetProperty()
    source = vu.polydata(source)
    target = vu.polydata(target)
    icp = vtk.vtkIterativeClosestPointTransform()
    icp.SetSource(source)
    icp.SetTarget(target)
    icp.SetMaximumNumberOfIterations(iters)
    icp.StartByMatchingCentroidsOn()
    icp.Update()
    icpTransformFilter = vtk.vtkTransformPolyDataFilter()
    vu.setInput(icpTransformFilter, source)
    icpTransformFilter.SetTransform(icp)
    icpTransformFilter.Update()
    poly = icpTransformFilter.GetOutput()
    actor = vu.makeActor(poly, legend=legend)
    actor.SetProperty(sprop)
    setattr(actor, 'transform', icp.GetLandmarkTransform())
    return actor
示例#7
0
def intersectWithLine(act, p0, p1):
    '''Return a list of points between p0 and p1 intersecting the actor'''
    if not hasattr(act, 'linelocator'):
        linelocator = vtk.vtkOBBTree()
        linelocator.SetDataSet(vu.polydata(act, True))
        linelocator.BuildLocator()
        setattr(act, 'linelocator', linelocator)

    intersectPoints = vtk.vtkPoints()
    intersection = [0, 0, 0]
    act.linelocator.IntersectWithLine(p0, p1, intersectPoints, None)
    pts = []
    for i in range(intersectPoints.GetNumberOfPoints()):
        intersectPoints.GetPoint(i, intersection)
        pts.append(list(intersection))
    return pts
示例#8
0
def cluster(points, radius, legend=None):
    '''
    Clustering of points in space.
    radius, is the radius of local search.
    Individual subsets can be accessed through actor.clusters
    '''
    if isinstance(points, vtk.vtkActor):
        poly = vu.polydata(points)
    else:
        src = vtk.vtkPointSource()
        src.SetNumberOfPoints(len(points))
        src.Update()
        vpts = src.GetOutput().GetPoints()
        for i, p in enumerate(points):
            vpts.SetPoint(i, p)
        poly = src.GetOutput()

    cluster = vtk.vtkEuclideanClusterExtraction()
    vu.setInput(cluster, poly)
    cluster.SetExtractionModeToAllClusters()
    cluster.SetRadius(radius)
    cluster.ColorClustersOn()
    cluster.Update()

    idsarr = cluster.GetOutput().GetPointData().GetArray('ClusterId')
    Nc = cluster.GetNumberOfExtractedClusters()

    sets = [[] for i in range(Nc)]
    for i, p in enumerate(points):
        sets[idsarr.GetValue(i)].append(p)

    acts = []
    for i, aset in enumerate(sets):
        acts.append(vs.points(aset, c=i))

    actor = vu.makeAssembly(acts, legend=legend)
    setattr(actor, 'clusters', sets)
    print('Nr. of extracted clusters', Nc)
    if Nc > 10: print('First ten:')
    for i in range(Nc):
        if i > 9:
            print('...')
            break
        print('Cluster #' + str(i) + ',  N =', len(sets[i]))
    print('Access individual clusters through attribute: actor.cluster')
    return actor
示例#9
0
def cutPlane(actor, origin=(0, 0, 0), normal=(1, 0, 0), showcut=True):
    '''
    Takes actor and cuts it with the plane defined by a point
    and a normal. 
        showcut  = shows the cut away part as thin wireframe
        showline = marks with a thick line the cut
    '''
    plane = vtk.vtkPlane()
    plane.SetOrigin(origin)
    plane.SetNormal(normal)
    poly = vu.polydata(actor)
    clipper = vtk.vtkClipPolyData()
    vu.setInput(clipper, poly)
    clipper.SetClipFunction(plane)
    clipper.GenerateClippedOutputOn()
    clipper.SetValue(0.)
    clipper.Update()
    if hasattr(actor, 'GetProperty'):
        alpha = actor.GetProperty().GetOpacity()
        c = actor.GetProperty().GetColor()
        bf = actor.GetBackfaceProperty()
    else:
        alpha = 1
        c = 'gold'
        bf = None
    leg = None
    if hasattr(actor, 'legend'): leg = actor.legend
    clipActor = vu.makeActor(clipper.GetOutput(), c=c, alpha=alpha, legend=leg)
    clipActor.SetBackfaceProperty(bf)

    acts = [clipActor]
    if showcut:
        cpoly = clipper.GetClippedOutput()
        restActor = vu.makeActor(cpoly, c=c, alpha=0.05, wire=1)
        acts.append(restActor)

    if len(acts) > 1:
        asse = vu.makeAssembly(acts)
        return asse
    else:
        return clipActor
示例#10
0
def cutterWidget(obj,
                 outputname='clipped.vtk',
                 c=(0.2, 0.2, 1),
                 alpha=1,
                 bc=(0.7, 0.8, 1),
                 legend=None):
    '''Pop up a box widget to cut parts of actor. Return largest part.'''

    apd = vu.polydata(obj)

    planes = vtk.vtkPlanes()
    planes.SetBounds(apd.GetBounds())

    clipper = vtk.vtkClipPolyData()
    vu.setInput(clipper, apd)
    clipper.SetClipFunction(planes)
    clipper.InsideOutOn()
    clipper.GenerateClippedOutputOn()

    # check if color string contains a float, in this case ignore alpha
    al = vc.getAlpha(c)
    if al: alpha = al

    act0Mapper = vtk.vtkPolyDataMapper()  # the part which stays
    act0Mapper.SetInputConnection(clipper.GetOutputPort())
    act0 = vtk.vtkActor()
    act0.SetMapper(act0Mapper)
    act0.GetProperty().SetColor(vc.getColor(c))
    act0.GetProperty().SetOpacity(alpha)
    backProp = vtk.vtkProperty()
    backProp.SetDiffuseColor(vc.getColor(bc))
    backProp.SetOpacity(alpha)
    act0.SetBackfaceProperty(backProp)
    #act0 = makeActor(clipper.GetOutputPort())

    act0.GetProperty().SetInterpolationToFlat()
    vu.assignPhysicsMethods(act0)
    vu.assignConvenienceMethods(act0, legend)

    act1Mapper = vtk.vtkPolyDataMapper()  # the part which is cut away
    act1Mapper.SetInputConnection(clipper.GetClippedOutputPort())
    act1 = vtk.vtkActor()
    act1.SetMapper(act1Mapper)
    act1.GetProperty().SetColor(vc.getColor(c))
    act1.GetProperty().SetOpacity(alpha / 10.)
    act1.GetProperty().SetRepresentationToWireframe()
    act1.VisibilityOn()

    ren = vtk.vtkRenderer()
    ren.SetBackground(1, 1, 1)

    ren.AddActor(act0)
    ren.AddActor(act1)

    renWin = vtk.vtkRenderWindow()
    renWin.AddRenderer(ren)
    renWin.SetSize(600, 700)

    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(renWin)
    istyl = vtk.vtkInteractorStyleSwitch()
    istyl.SetCurrentStyleToTrackballCamera()
    iren.SetInteractorStyle(istyl)

    def SelectPolygons(vobj, event):
        vobj.GetPlanes(planes)

    boxWidget = vtk.vtkBoxWidget()
    boxWidget.OutlineCursorWiresOn()
    boxWidget.GetSelectedOutlineProperty().SetColor(1, 0, 1)
    boxWidget.GetOutlineProperty().SetColor(0.1, 0.1, 0.1)
    boxWidget.GetOutlineProperty().SetOpacity(0.8)
    boxWidget.SetPlaceFactor(1.05)
    boxWidget.SetInteractor(iren)
    vu.setInput(boxWidget, apd)
    boxWidget.PlaceWidget()
    boxWidget.AddObserver("InteractionEvent", SelectPolygons)
    boxWidget.On()

    vio.printc('\nCutterWidget:\n Move handles to cut parts of the actor', 'm')
    vio.printc(' Press q to continue, Escape to exit', 'm')
    vio.printc((" Press X to save file to", outputname), 'm')

    def cwkeypress(obj, event):
        key = obj.GetKeySym()
        if key == "q" or key == "space" or key == "Return":
            iren.ExitCallback()
        elif key == "X":
            confilter = vtk.vtkPolyDataConnectivityFilter()
            vu.setInput(confilter, clipper.GetOutput())
            confilter.SetExtractionModeToLargestRegion()
            confilter.Update()
            cpd = vtk.vtkCleanPolyData()
            vu.setInput(cpd, confilter.GetOutput())
            cpd.Update()
            vio.write(cpd.GetOutput(), outputname)
        elif key == "Escape":
            exit(0)

    iren.Initialize()
    iren.AddObserver("KeyPressEvent", cwkeypress)
    iren.Start()
    boxWidget.Off()
    return act0
示例#11
0
def smoothMLS(actor, f=0.2, decimate=1, recursive=0, showNPlanes=0):
    '''
    Smooth actor or points with a Moving Least Squares variant.
    The list actor.variances contain the residue calculated for each point.
    Input actor's polydata is modified.
    
        f, smoothing factor - typical range s [0,2]
        
        decimate, decimation factor (an integer number) 
        
        recursive, move points while algorithm proceedes
        
        showNPlanes, build an actor showing the fitting plane for N random points            
    '''
    coords = vu.coordinates(actor)
    ncoords = len(coords)
    Ncp = int(ncoords * f / 100)
    nshow = int(ncoords / decimate)
    if showNPlanes: ndiv = int(nshow / showNPlanes * decimate)

    if Ncp < 5:
        vio.printc('Please choose a higher fraction than' + str(f), 1)
        Ncp = 5
    print('smoothMLS: Searching #neighbours, #pt:', Ncp, ncoords)

    poly = vu.polydata(actor, True)
    vpts = poly.GetPoints()
    locator = vtk.vtkPointLocator()
    locator.SetDataSet(poly)
    locator.BuildLocator()
    vtklist = vtk.vtkIdList()
    variances, newsurf, acts = [], [], []
    pb = vio.ProgressBar(0, ncoords)
    for i, p in enumerate(coords):
        pb.print('smoothing...')
        if i % decimate: continue

        locator.FindClosestNPoints(Ncp, p, vtklist)
        points = []
        for j in range(vtklist.GetNumberOfIds()):
            trgp = [0, 0, 0]
            vpts.GetPoint(vtklist.GetId(j), trgp)
            points.append(trgp)
        if len(points) < 5: continue

        points = np.array(points)
        pointsmean = points.mean(axis=0)  # plane center
        uu, dd, vv = np.linalg.svd(points - pointsmean)
        a, b, c = np.cross(vv[0], vv[1])  # normal
        d, e, f = pointsmean  # plane center
        x, y, z = p
        t = (a * d - a * x + b * e - b * y + c * f - c * z)  #/(a*a+b*b+c*c)
        newp = [x + t * a, y + t * b, z + t * c]
        variances.append(dd[2])
        newsurf.append(newp)
        if recursive: vpts.SetPoint(i, newp)

        if showNPlanes and not i % ndiv:
            plane = fitPlane(points, alpha=0.3)  # fitting plane
            iapts = vs.points(points)  # blue points
            acts += [plane, iapts]

    if decimate == 1 and not recursive:
        for i in range(ncoords):
            vpts.SetPoint(i, newsurf[i])

    setattr(actor, 'variances', np.array(variances))

    if showNPlanes:
        apts = vs.points(newsurf, c='r 0.6', r=2)
        ass = vu.makeAssembly([apts] + acts)
        return ass  #NB: a demo actor is returned

    return actor  #NB: original actor is modified
示例#12
0
def fxy(z='sin(3*x)*log(x-y)/3',
        x=[0, 3],
        y=[0, 3],
        zlimits=[None, None],
        showNan=True,
        zlevels=10,
        wire=False,
        c='b',
        bc='aqua',
        alpha=1,
        legend=True,
        texture=None,
        res=100):
    '''
    Build a surface representing the 3D function specified as a string
    or as a reference to an external function.
    Red points indicate where the function does not exist (showNan).

    zlevels will draw the specified number of z-levels contour lines.

    Examples:
        vp = plotter.vtkPlotter()
        vp.fxy('sin(3*x)*log(x-y)/3')
        or
        def z(x,y): return math.sin(x*y)
        vp.fxy(z) # or equivalently:
        vp.fxy(lambda x,y: math.sin(x*y))
    '''
    if isinstance(z, str):
        try:
            z = z.replace('math.', '').replace('np.', '')
            namespace = locals()
            code = "from math import*\ndef zfunc(x,y): return " + z
            exec(code, namespace)
            z = namespace['zfunc']
        except:
            vio.printc('Syntax Error in fxy()', 1)
            return None

    ps = vtk.vtkPlaneSource()
    ps.SetResolution(res, res)
    ps.SetNormal([0, 0, 1])
    ps.Update()
    poly = ps.GetOutput()
    dx = x[1] - x[0]
    dy = y[1] - y[0]
    todel, nans = [], []

    if zlevels:
        tf = vtk.vtkTriangleFilter()
        vu.setInput(tf, poly)
        tf.Update()
        poly = tf.GetOutput()

    for i in range(poly.GetNumberOfPoints()):
        px, py, _ = poly.GetPoint(i)
        xv = (px + .5) * dx + x[0]
        yv = (py + .5) * dy + y[0]
        try:
            zv = z(xv, yv)
            poly.GetPoints().SetPoint(i, [xv, yv, zv])
        except:
            todel.append(i)
            nans.append([xv, yv, 0])

    if len(todel):
        cellIds = vtk.vtkIdList()
        poly.BuildLinks()

        for i in todel:
            poly.GetPointCells(i, cellIds)
            for j in range(cellIds.GetNumberOfIds()):
                poly.DeleteCell(cellIds.GetId(j))  #flag cell

        poly.RemoveDeletedCells()
        cl = vtk.vtkCleanPolyData()
        vu.setInput(cl, poly)
        cl.Update()
        poly = cl.GetOutput()

    if not poly.GetNumberOfPoints():
        vio.printc('Function is not real in the domain', 1)
        return vtk.vtkActor()

    if zlimits[0]:
        a = cutPlane(poly, (0, 0, zlimits[0]), (0, 0, 1), False)
        poly = vu.polydata(a)
    if zlimits[1]:
        a = cutPlane(poly, (0, 0, zlimits[1]), (0, 0, -1), False)
        poly = vu.polydata(a)

    if c is None:
        elev = vtk.vtkElevationFilter()
        vu.setInput(elev, poly)
        elev.Update()
        poly = elev.GetOutput()

    actor = vu.makeActor(poly,
                         c=c,
                         bc=bc,
                         alpha=alpha,
                         wire=wire,
                         legend=legend,
                         texture=texture)
    acts = [actor]
    if zlevels:
        elevation = vtk.vtkElevationFilter()
        vu.setInput(elevation, poly)
        bounds = poly.GetBounds()
        elevation.SetLowPoint(0, 0, bounds[4])
        elevation.SetHighPoint(0, 0, bounds[5])
        elevation.Update()
        bcf = vtk.vtkBandedPolyDataContourFilter()
        vu.setInput(bcf, elevation.GetOutput())
        bcf.SetScalarModeToValue()
        bcf.GenerateContourEdgesOn()
        bcf.GenerateValues(zlevels, elevation.GetScalarRange())
        bcf.Update()
        zpoly = bcf.GetContourEdgesOutput()
        zbandsact = vu.makeActor(zpoly, c='k', alpha=alpha)
        zbandsact.GetProperty().SetLineWidth(1.5)
        acts.append(zbandsact)

    if showNan and len(todel):
        bb = actor.GetBounds()
        zm = (bb[4] + bb[5]) / 2
        nans = np.array(nans) + [0, 0, zm]
        nansact = vs.points(nans, c='red', alpha=alpha / 2)
        acts.append(nansact)

    if len(acts) > 1:
        asse = vu.makeAssembly(acts)
        return asse
    else:
        return actor