def makeSurfaces(self, o, bm): # sverts stands for surface verts sverts = SurfaceVerts(o, bm, self) if not sverts.numVerts: return # now compose the surface out of the vertices <verts> while sverts.numVerts: v, vid = sverts.pop() # node wrapper for the surface vert <v> n = self.nodes[vid] # template vertex tv = n.v # ordered edges for the surface vert <v> edges = n.edges # find the pair of edges where the surface vert <v> is located # vector from the node origin to the location of the surface vert <v> vec = v.co - tv.co # we need the projection of <vec> onto the plane defined by edges of the template vertex <v> vec = projectOntoPlane(vec, n.n) if len(edges) == 2: # the simpliest case for only two edges, no need for any lookup l = tv.link_loops[0] else: e1, e2 = n.getNeighborEdges(vec) # template vertices on the ends of the edges e1 and e2 tv1 = e1[1] tv2 = e2[1] # Get a BMLoop from tv.link_loops for which # BMLoops coming through tv1 and tv2 are the next and previous BMLoops for l in tv.link_loops: if (l.link_loop_next.vert == tv1 and l.link_loop_prev.vert == tv2) or \ (l.link_loop_prev.vert == tv1 and l.link_loop_next.vert == tv2): break # vertices of BMFace for the surface verts = [v] # perform a walk along BMFace containing BMLoop <l> # the initial loop loop = l vec2 = (l.link_loop_next.vert.co - l.vert.co).normalized() while True: l = l.link_loop_next if l == loop: break vec1 = -vec2 vec2 = (l.link_loop_next.vert.co - l.vert.co).normalized() v = sverts.pop(l.vert, vec1, vec2)[0] if v: verts.append(v) # finally, create BMFace for the surface face = bm.faces.new(verts) # check if we need to reverse the normal to the current surface sl = sverts.getSurfaceLayer() if sl in self.o and self.o[sl] == "reversed": bmesh.ops.reverse_faces(bm, faces=(face, ))
def makeSurfaces(self, o, bm): # sverts stands for surface verts sverts = SurfaceVerts(o, bm, self) if not sverts.numVerts: return # now compose the surface out of the vertices <verts> while sverts.numVerts: v, vid = sverts.pop() # node wrapper for the surface vert <v> n = self.nodes[vid] # template vertex tv = n.v # ordered edges for the surface vert <v> edges = n.edges # find the pair of edges where the surface vert <v> is located # vector from the node origin to the location of the surface vert <v> vec = v.co - tv.co # we need the projection of <vec> onto the plane defined by edges of the template vertex <v> vec = projectOntoPlane(vec, n.n) if len(edges) == 2: # the simpliest case for only two edges, no need for any lookup l = tv.link_loops[0] else: e1, e2 = n.getNeighborEdges(vec) # template vertices on the ends of the edges e1 and e2 tv1 = e1[1] tv2 = e2[1] # Get a BMLoop from tv.link_loops for which # BMLoops coming through tv1 and tv2 are the next and previous BMLoops for l in tv.link_loops: if (l.link_loop_next.vert == tv1 and l.link_loop_prev.vert == tv2) or \ (l.link_loop_prev.vert == tv1 and l.link_loop_next.vert == tv2): break # vertices of BMFace for the surface verts = [v] # perform a walk along BMFace containing BMLoop <l> # the initial loop loop = l vec2 = (l.link_loop_next.vert.co - l.vert.co).normalized() while True: l = l.link_loop_next if l == loop: break vec1 = -vec2 vec2 = (l.link_loop_next.vert.co - l.vert.co).normalized() v = sverts.pop(l.vert, vec1, vec2)[0] if v: verts.append(v) # finally, create BMFace for the surface face = bm.faces.new(verts) # check if we need to reverse the normal to the current surface sl = sverts.getSurfaceLayer() if sl in self.o and self.o[sl] == "reversed": bmesh.ops.reverse_faces(bm, faces=(face,))
def pop(self, tv=None, vec1=None, vec2=None): # tv stands for template vertex # If <templateVert>, <vec1> and <vec2> are given, that means pop a surface vert for <tv> # located between vectors <vec1> and <vec2> # If <tv>, <vec1> and <vec2> aren't given, a random surface vertex and its vid are returned sverts = self.sverts # check if there is any layer in <sverts> if not len(sverts): return None, None if self.sl is None: # set the current surface layer self.sl = next(iter(sverts)) sl = self.sl vid = self.template.getVid(tv) if tv else next(iter(sverts[sl])) # if the template vertex <tv> is given, it may not have a related surface vertex if not vid in sverts[sl]: return None, None if len(sverts[sl][vid]) == 1: v = sverts[sl][vid][0] del sverts[sl][vid] if not len(sverts[sl]): del sverts[sl] # remember the surface layer if a reversion of the surface normal is needed self._sl = self.sl self.sl = None else: if tv: # get surface verts for <vid> for v in sverts[sl][vid]: vec = v.co - tv.co # we need the projection of <vec> onto the plane defined by edges of the template vertex <v> vec = projectOntoPlane(vec, self.template.nodes[vid].n) if isVectorBetweenVectors(vec, vec1, vec2): # Stop iteration through surface verts for <vid>, # the required surface vert has been found break sverts[sl][vid].remove(v) else: v = sverts[sl][vid].pop() self.numVerts -= 1 return v, vid
def pop(self, tv=None, vec1=None, vec2=None): # tv stands for template vertex # If <templateVert>, <vec1> and <vec2> are given, that means pop a surface vert for <tv> # located between vectors <vec1> and <vec2> # If <tv>, <vec1> and <vec2> aren't given, a random surface vertex and its vid are returned sverts = self.sverts # check if there is any layer in <sverts> if not len(sverts): return None, None if self.sl is None: # set the current surface layer self.sl = next(iter(sverts)) sl = self.sl vid = self.template.getVid(tv) if tv else next(iter(sverts[sl])) # if the template vertex <tv> is given, it may not have a related surface vertex if not vid in sverts[sl]: return None, None if len(sverts[sl][vid]) == 1: v = sverts[sl][vid][0] del sverts[sl][vid] if not len(sverts[sl]): del sverts[sl] # remember the surface layer if a reversion of the surface normal is needed self._sl = self.sl self.sl = None else: if tv: # get surface verts for <vid> for v in sverts[sl][vid]: vec = v.co-tv.co # we need the projection of <vec> onto the plane defined by edges of the template vertex <v> vec = projectOntoPlane(vec, self.template.nodes[vid].n) if isVectorBetweenVectors(vec, vec1, vec2): # Stop iteration through surface verts for <vid>, # the required surface vert has been found break sverts[sl][vid].remove(v) else: v = sverts[sl][vid].pop() self.numVerts -= 1 return v, vid
def insertAssets(self, context): o = self.o nodes = self.nodes nodeCache = self.nodeCache # scan the template for assets for a in o.children: if a.get("t") != "asset": continue vid1 = a["vid1"] vid2 = a["vid2"] bm = getBmesh(o) vert1 = getVertsForVertexGroup(o, bm, vid1)[0] vert2 = getVertsForVertexGroup(o, bm, vid2)[0] v1 = vert1.co v2 = vert2.co # get the edge connecting two vertices <v1> and <v2> for edge in vert1.link_edges: if vert2 in edge.verts: break location = projectOntoLine(a.location - v1, (v2 - v1).normalized()) # relative location on the edge location = location.length / (v2 - v1).length # If the <edge> is external, take into account offsets for the vertices <v1> and <v2>, # otherwise don't take offsets into account if len(edge.link_faces) == 1: # the only BMLoop for <edge> loop = edge.link_loops[0] # do we walk from <vert2> to <vert1> or in the opposite direction fromVert2 = loop.vert == vert2 if fromVert2: # temporarily swap <vert1> and <vert2> vert1, vert2 = vert2, vert1 # edge vector for the corner of the vertex <vert1> edgeVec1 = getVectorFromEdge(edge, vert1) # edge vector for corner of the <vert2> is the other external edge for <vert2> for edgeVec2 in vert2.link_edges: if edgeVec2 != edge and len(edgeVec2.link_faces) == 1: break edgeVec2 = getVectorFromEdge(edgeVec2, vert2) if fromVert2: # swap the values back vert1, vert2 = vert2, vert1 edgeVec1, edgeVec2 = edgeVec2, edgeVec1 # adding offset to the vectors <v1> and <v2> v1 += self.parentTemplate.childOffsets.get(vid1, edgeVec1) v2 += self.parentTemplate.childOffsets.get(vid2, edgeVec2) location = v1 + location * (v2 - v1) # calculate the offset <tOffset> perpedicular to the vector <v2 - v1> (i.e. transverse offset) # type of the asset t = a.get("t2") def processOffset(tOffset, pVid, sVid): """ A helper function to check if we have an appropriate <tOffset> Args: tOffset (list): Blender EMPTY object serving as asset placeholder pVid (str): Primary vid sVid (str): the other vid Returns: A tuple with <tOffset> (may be set to None) and transformation matrix (may be None) """ # index of the open end of the node Blender object to which the asset is attached e = tOffset.get("e") ends = nodes[pVid].ends["e_" + str(e)] if pVid in ends and sVid in ends: matrix = nodes[pVid].matrix else: matrix = None tOffset = None return tOffset, matrix # check if the asset placeholder is set for the node with <vid1> tOffset = nodeCache.get(o[vid1]).assets[t] if tOffset: tOffset, matrix = processOffset(tOffset, vid1, vid2) if not tOffset: # check if the asset placeholder is set for the node with <vid2> tOffset = nodeCache.get(o[vid2]).assets[t] tOffset, matrix = processOffset(tOffset, vid2, vid1) if tOffset: tOffset = matrix * tOffset.location if matrix else tOffset.location.copy( ) location += projectOntoPlane(tOffset, (v2 - v1).normalized()) bm.free() # parent object for the hierarchy of assets p = createEmptyObject("test", location, True, empty_draw_size=0.01) parent_set(self.meshObject.parent, p) # import asset a = appendFromFile( context, os.path.join(context.scene.prk.baseDirectory, a["path"])) a.location = zeroVector.copy() parent_set(p, a)
def prepareOffsets(self): """ Calculate offsets for intermediary template vertices which don't have an ancestor in the parent template """ p = self.parentTemplate if not p: # nothing to prepare return # iterate through the vertices of the template to find an outer vertex presented in <p.childOffsets> # also find a pair of outer edges connected to the outer vertex vert = None # a variable for the first edge to be found _e = None for v in self.bm.verts: vid = self.getVid(v) if vid in p.nodes: # check if the vertex <v> has an outer edge for e in v.link_edges: if len(e.link_faces) == 1: if _e: # the second edge is simply <e> break else: vert = v # the first edge is found _e = e if vert: break if not vert: # nothing to prepare return _vec = getVectorFromEdge(_e, vert) vec = getVectorFromEdge(e, vert) # the cross product of <_e> and <p> must point in the direction of the normal to <vert> if _vec.cross(vec).dot(vert.normal) > 0: e = _e vec = _vec # the reference edges to get offset from the ChildOffset class is <e> # walk along outer edges starting from <vert> v = vert # the last visited edge _e = e # the unit vector along the current edge _n = None vids = [] # the current offset offset = p.childOffsets.get(vid, vec) while True: # Walk along outer vertices until we encounter a vertex with vid in <p.childOffsets> OR # the direction of the current edge is changed significantly # get the next outer vertex for e in v.link_edges: if len(e.link_faces) == 1 and e != _e: _v = v v = e.verts[1] if e.verts[0] == v else e.verts[0] break vid = self.getVid(v) hasOffset = vid in p.nodes # the unit vector along the edge defined by <_v> and <v> n = (v.co - _v.co).normalized() # check if the direction of the edge has been changed directionChanged = _n and abs(1. - _n.dot(n)) > zero2 if hasOffset: # set the current offset offset = p.childOffsets.get(vid, getVectorFromEdge(e, v)) _offset = None if directionChanged: # set offset only sfor the last vid in <vids> vids = [vids[-1]] if vids: # We need the projection of <offset> vector onto the plane # defined by the normal <n> to the plane _offset = projectOntoPlane(offset, n) # set offset for all outer vertices in <vids> list for vid in vids: self.childOffsets.set(vid, None, _offset) vids = [] _n = None else: if directionChanged: offset = None _n = None vids = [vid] else: if offset: # We need the projection of <offset> vector onto the plane # defined by the normal <n> to the plane _offset = projectOntoPlane(offset, n) self.childOffsets.set(vid, None, _offset) else: vids.append(vid) _n = n # check if need to quit the cycle if v == vert: break _e = e
def insertAssets(self, context): o = self.o nodes = self.nodes nodeCache = self.nodeCache # scan the template for assets for a in o.children: if a.get("t") != "asset": continue vid1 = a["vid1"] vid2 = a["vid2"] bm = getBmesh(o) vert1 = getVertsForVertexGroup(o, bm, vid1)[0] vert2 = getVertsForVertexGroup(o, bm, vid2)[0] v1 = vert1.co v2 = vert2.co # get the edge connecting two vertices <v1> and <v2> for edge in vert1.link_edges: if vert2 in edge.verts: break location = projectOntoLine(a.location-v1, (v2-v1).normalized()) # relative location on the edge location = location.length / (v2 - v1).length # If the <edge> is external, take into account offsets for the vertices <v1> and <v2>, # otherwise don't take offsets into account if len(edge.link_faces) == 1: # the only BMLoop for <edge> loop = edge.link_loops[0] # do we walk from <vert2> to <vert1> or in the opposite direction fromVert2 = loop.vert == vert2 if fromVert2: # temporarily swap <vert1> and <vert2> vert1, vert2 = vert2, vert1 # edge vector for the corner of the vertex <vert1> edgeVec1 = getVectorFromEdge(edge, vert1) # edge vector for corner of the <vert2> is the other external edge for <vert2> for edgeVec2 in vert2.link_edges: if edgeVec2 != edge and len(edgeVec2.link_faces) == 1: break edgeVec2 = getVectorFromEdge(edgeVec2, vert2) if fromVert2: # swap the values back vert1, vert2 = vert2, vert1 edgeVec1, edgeVec2 = edgeVec2, edgeVec1 # adding offset to the vectors <v1> and <v2> v1 += self.parentTemplate.childOffsets.get(vid1, edgeVec1) v2 += self.parentTemplate.childOffsets.get(vid2, edgeVec2) location = v1 + location * (v2 - v1) # calculate the offset <tOffset> perpedicular to the vector <v2 - v1> (i.e. transverse offset) # type of the asset t = a.get("t2") def processOffset(tOffset, pVid, sVid): """ A helper function to check if we have an appropriate <tOffset> Args: tOffset (list): Blender EMPTY object serving as asset placeholder pVid (str): Primary vid sVid (str): the other vid Returns: A tuple with <tOffset> (may be set to None) and transformation matrix (may be None) """ # index of the open end of the node Blender object to which the asset is attached e = tOffset.get("e") ends = nodes[pVid].ends["e_"+str(e)] if pVid in ends and sVid in ends: matrix = nodes[pVid].matrix else: matrix = None tOffset = None return tOffset, matrix # check if the asset placeholder is set for the node with <vid1> tOffset = nodeCache.get(o[vid1]).assets[t] if tOffset: tOffset, matrix = processOffset(tOffset, vid1, vid2) if not tOffset: # check if the asset placeholder is set for the node with <vid2> tOffset = nodeCache.get(o[vid2]).assets[t] tOffset, matrix = processOffset(tOffset, vid2, vid1) if tOffset: tOffset = matrix*tOffset.location if matrix else tOffset.location.copy() location += projectOntoPlane(tOffset, (v2 - v1).normalized()) bm.free() # parent object for the hierarchy of assets p = createEmptyObject("test", location, True, empty_draw_size=0.01) parent_set(self.meshObject.parent, p) # import asset a = appendFromFile(context, os.path.join(context.scene.prk.baseDirectory, a["path"])) a.location = zeroVector.copy() parent_set(p, a)
def prepareOffsets(self): """ Calculate offsets for intermediary template vertices which don't have an ancestor in the parent template """ p = self.parentTemplate if not p: # nothing to prepare return # iterate through the vertices of the template to find an outer vertex presented in <p.childOffsets> # also find a pair of outer edges connected to the outer vertex vert = None # a variable for the first edge to be found _e = None for v in self.bm.verts: vid = self.getVid(v) if vid in p.nodes: # check if the vertex <v> has an outer edge for e in v.link_edges: if len(e.link_faces) == 1: if _e: # the second edge is simply <e> break else: vert = v # the first edge is found _e = e if vert: break if not vert: # nothing to prepare return _vec = getVectorFromEdge(_e, vert) vec = getVectorFromEdge(e, vert) # the cross product of <_e> and <p> must point in the direction of the normal to <vert> if _vec.cross(vec).dot(vert.normal) > 0: e = _e vec = _vec # the reference edges to get offset from the ChildOffset class is <e> # walk along outer edges starting from <vert> v = vert # the last visited edge _e = e # the unit vector along the current edge _n = None vids = [] # the current offset offset = p.childOffsets.get(vid, vec) while True: # Walk along outer vertices until we encounter a vertex with vid in <p.childOffsets> OR # the direction of the current edge is changed significantly # get the next outer vertex for e in v.link_edges: if len(e.link_faces) == 1 and e != _e: _v = v v = e.verts[1] if e.verts[0] == v else e.verts[0] break vid = self.getVid(v) hasOffset = vid in p.nodes # the unit vector along the edge defined by <_v> and <v> n = (v.co - _v.co).normalized() # check if the direction of the edge has been changed directionChanged = _n and abs(1.-_n.dot(n)) > zero2 if hasOffset: # set the current offset offset = p.childOffsets.get(vid, getVectorFromEdge(e, v)) _offset = None if directionChanged: # set offset only sfor the last vid in <vids> vids = [vids[-1]] if vids: # We need the projection of <offset> vector onto the plane # defined by the normal <n> to the plane _offset = projectOntoPlane(offset, n) # set offset for all outer vertices in <vids> list for vid in vids: self.childOffsets.set(vid, None, _offset) vids = [] _n = None else: if directionChanged: offset = None _n = None vids = [vid] else: if offset: # We need the projection of <offset> vector onto the plane # defined by the normal <n> to the plane _offset = projectOntoPlane(offset, n) self.childOffsets.set(vid, None, _offset) else: vids.append(vid) _n = n # check if need to quit the cycle if v == vert: break _e = e