After losing some work due to HDD problems I ran into a lot of issues with the previously posted Knife SOP

So I tried again using the normal Knife and got the same issues as it does not work iteratively and then I bugfixed (or rewrote largely) the previous knife I made, hopefully more functional this time. It retains face order, it does now calculate point attributes for new points and it transfters custom prim attributes (the split prims duplicate the attribute vaues). Consider this a snippet dump…

node = hou.pwd() geo = node.geometry() #parse parameters target = node.evalParm("target") origin = hou.Vector3( node.evalParm("originx"), node.evalParm("originy"), node.evalParm("originz") ) distance = node.evalParm("dist") direction = hou.Vector3( node.evalParm("dirx"), node.evalParm("diry"), node.evalParm("dirz") ).normalized() #distance really just moves the origin origin += direction*distance def rayPlaneIntersect(rayorigin, in_raydirection, planeorigin, in_planenormal): ''''' @returns: Vector3, intersectionPoint-rayOrigin ''' raydirection = in_raydirection.normalized() planenormal = in_planenormal.normalized() distanceToPlane = (rayorigin-planeorigin).dot(planenormal) triangleHeight = raydirection.dot(-planenormal) if not distanceToPlane: #ray origin lies in the plane return rayorigin-planeorigin if not triangleHeight: #ray is parallel to plane return None return raydirection * distanceToPlane * (1.0/triangleHeight) def getPolygonsWithEdge(geom, edgeids): ''''' @param geo: hou.Geometry, geometry to search @param edgeids: tuple of 2 ints, point numbers describing the edge to find shared faces for @returns: list of hou.Prim, all primitives sharing this edge if the points are not connected by an edge (adjacent in the vertex list of any primitive) the result is an empty list ''' out = [] for poly in geom.prims(): verts = poly.vertices() for i in range(poly.numVertices()): if verts[i].point().number() in edgeids and\ verts[(i+1)%poly.numVertices()].point().number() in edgeids: out.append(poly) return out #stub primitive, I use this for the cut faces so I can actually add the polygons in the end to not disturb primitive order / numbers class notPrim(): def __init__(self): self.points = [] def addVertex( self, inPoint ): self.points.append( inPoint ) def addToGeo( self, inGeo ): poly = inGeo.createPolygon() for point in self.points: poly.addVertex(point) return poly ### Cut target ### verts = geo.iterPrims()[target].vertices() nverts = len(verts) cutEdges = [] adjFaces = [] #foreach edge for i in range(nverts): pt0 = verts[i].point() pt1 = verts[(i+1)%nverts].point() edgedirection = pt1.position()-pt0.position() #find interseciton on edge intersectpt = rayPlaneIntersect(pt0.position(), edgedirection, origin, direction) if not intersectpt: #edge is parallel to cutting plane continue #check if intersection is on the edge (line-segment) param = intersectpt.dot(edgedirection.normalized()) if param > 0 and param < edgedirection.length(): #store the cut pt = geo.createPoint() #propagate point attribs for attrib in geo.pointAttribs(): val0 = pt0.attribValue(attrib) val1 = pt1.attribValue(attrib) if type(val0) in (int, float): pt.setAttribValue( attrib, val0*(1-param)+val1*param ) if type(val0) == tuple: val = [] for i in range(len(val0)): if type(val0[i]) in (int, float): val.append( val0[i]*(1-param)+val1[i]*param ) pt.setAttribValue( attrib, val ) pt.setPosition( intersectpt+pt0.position() ) cutEdges.append( [pt0, pt1, pt] ) #store the face(s) influenced by this cut adjFaces.extend( getPolygonsWithEdge( geo, (pt0.number(), pt1.number()) ) ) ### Rebuild geometry ### delete = [] polys = [] #rebuild all prims for i in range(len(geo.iterPrims())): prim = geo.iterPrims()[i] delete.append(prim) #remove all old prims poly = geo.createPolygon() #create new prim #duplicate prim attribs for attrib in geo.primAttribs(): val = prim.attribValue(attrib) poly.setAttribValue( attrib, val ) #build cut faces if i == target: ### Create cut face ### #iterate over edges to build new polygons cuts = 0 wrap = False polys.append( poly ) #the first split primitive keeps the original primitive nr for j in range(prim.numVertices()): cut = None vtx = prim.vertices()[j] #all cuts added, finish the first polygon if wrap: polys[0].addVertex(vtx.point()) continue #find edge points nxtvtx = prim.vertices()[(j+1)%prim.numVertices()] polys[-1].addVertex(vtx.point()) for edge in cutEdges: if vtx.point() in edge and nxtvtx.point() in edge: cut=edge #if edge is a cut edge if cut: polys[-1].addVertex(cut[2]) cuts += 1 if cuts == len(cutEdges): #wrap to first polygon at last cut wrap = True polys[0].addVertex(cut[2]) continue polys.append(notPrim()) #add stub primitive and start building vertex list for it polys[-1].addVertex(cut[2]) else: #or just build the primitive again for j in range(prim.numVertices()): vtx = prim.vertices()[j] poly.addVertex(vtx.point()) if prim in adjFaces: #if influenced by cut, then cut the right edge for edge in cutEdges: #when at start of the split edge, known because next vertex is it's end point if vtx.point() in edge: nxtvtx = prim.vertices()[(j+1)%prim.numVertices()] if nxtvtx.point() in edge: #add the intersection point poly.addVertex(edge[2]) break ### append stub split faces at the very end### for i in range(1,len(polys),1): poly = polys[i].addToGeo( geo ) src = geo.iterPrims()[target] #duplicate prim attribs for attrib in geo.primAttribs(): val = src.attribValue(attrib) poly.setAttribValue( attrib, val ) #remove all old prims geo.deletePrims(delete, True)