def edgesFromSegs(edge, target_segs, direction): if len(target_segs)==0: return [] # If the target segments are forked or otherwise complex, # return an empty list. try: (seg_list, node_list, winding) = skeletonsegment.segSequence( target_segs ) except skeletonsegment.SequenceError: return [] ordered_nodes = edge.get_nodes() if direction==MAP_UP: target_start = ordered_nodes[0].getParents() else: # direction==MAP_DOWN target_start = ordered_nodes[0].getChildren() # the problem is that target_start is not in the "target" if node_list[0] in target_start: pass elif node_list[-1] in target_start: seg_list.reverse() node_list.reverse() else: # In cases of boundaries that are loops, when no starting node # is passed in, segSequence makes an arbitrary choice for the # starting node that can be incorrect, but otherwise sequences # the boundary correctly. In this case, we must re-sequence # the list. The alternative is to pass in a target start to # segSequence and then handle the case where the target start # is not in the node list in there, but that seems more complicated. # calculate overlap between node_list and target_start overlap = [node for node in node_list if node in target_start] if len(overlap): # elif node_list.intersection(target_start) try: (seg_list, node_list, winding) = skeletonsegment.segSequence( target_segs, overlap[0] ) except skeletonsegment.SequenceError: return [] else: raise ooferror.ErrPyProgrammingError( "Malformed segment sequence -- node counterpart not found.") target_list = [] for (s, n) in map(None, seg_list, node_list[:-1]): # if the node_list constitutes a loop, it will be longer than # needed, producing an extra item in the list returned by map. # We can ignore the extra item in this case. if s is None: pass elif s.nodes()[0]==n: target_list.append(skeletonsegment.SkeletonEdge(s,direction=1)) else: target_list.append(skeletonsegment.SkeletonEdge(s,direction=-1)) return target_list
def set_boundaries(self): global _rank # point boundaries pbInfo = {'names':[], 'nodes':[], 'exterior':[]} for pbname, pb in self.dummy.pointboundaries.items(): nodes = [] for nd in pb.nodes: if _rank in nd.owners(): nodes.append(self.indexMap[(_rank, nd.getIndex())]) #if nodes: # something in it if 1: #RCL: admit boundaries without any nodes touching them. pbInfo['names'].append(pbname) pbInfo['nodes'].append(nodes) pbInfo['exterior'].append(pb.exterior()) # edge boundaries ebInfo = {'names':[], 'edges':[], 'exterior':[]} for ebname, eb in self.dummy.edgeboundaries.items(): edges = [] for ed in eb.edges: nd0, nd1 = ed.get_nodes() if _rank in nd0.owners() and _rank in nd1.owners(): edges.append([self.indexMap[(_rank, nd0.getIndex())], self.indexMap[(_rank, nd1.getIndex())]]) #if edges: # something in it if 1: #RCL: admit boundaries without any nodes touching them. ebInfo['names'].append(ebname) ebInfo['edges'].append(edges) ebInfo['exterior'].append(eb.exterior()) # point boundaries for name, nodes, exterior in zip(pbInfo['names'], pbInfo['nodes'], pbInfo['exterior']): pb = self.skeleton.getPointBoundary(name, exterior=exterior) for nd in nodes: pb.addNode(self.skeleton.getNodeWithIndex(nd)) # edge boundaries for name, edges, exterior in zip(ebInfo['names'], ebInfo['edges'], ebInfo['exterior']): eb = self.skeleton.getEdgeBoundary(name, exterior=exterior) for ed in edges: seg = self.skeleton.findSegment( self.skeleton.getNodeWithIndex(ed[0]), self.skeleton.getNodeWithIndex(ed[1])) direction = (seg.nodeIndices() == ed) edge = skeletonsegment.SkeletonEdge(seg, direction=direction) eb.addEdge(edge)
def append(self, new_segments): if len(self.edges)==0: raise ooferror.ErrUserError( "Cannot append to an empty boundary.") self.sequence() # Make sure we're in order, first. all_segments = new_segments.union([x.segment for x in self.edges]) (seg_list, node_list, winding) = skeletonsegment.segSequence(all_segments) # Explicitly check for the loop case -- if it occurs, the # seg_list needs to be "manually" brought into correspondence # with the current edge list, otherwise the following code can # be confused by the wrap-around and fail to add the new # segments. if node_list[0]==node_list[-1]: # First, check orientation -- only meaningful if there was # more than one edge to begin with. if len(self.edges)>1: s0_idx = seg_list.index(self.edges[0].segment) s1_idx = seg_list.index(self.edges[1].segment) # If s1 is the successor of s0, mod the length, it's OK. if ((s0_idx+1) % len(seg_list)) != s1_idx: seg_list.reverse() # Now rotate the seg_list so that self.edges[0].segment # is at the beginning. s0_idx = seg_list.index(self.edges[0].segment) seg_list = seg_list[s0_idx:]+seg_list[:s0_idx] # At this point, seg_list is topologically OK -- either it's a # loop starting from s0, or it's a line straight from the # sequencer. In the latter case, it still might be backwards. s0 = self.edges[0].segment s1 = self.edges[-1].segment # If it is backwards, fix it. if seg_list.index(s0) > seg_list.index(s1): seg_list.reverse() first_original_edge = self.edges[0] last_original_edge = self.edges[-1] # Check the front. idx = seg_list.index(s0) if idx!=0: # There are edges to prepend to the front. contact_node = first_original_edge.get_nodes()[0] for jdx in range(idx-1,-1,-1): # Count down to zero. seg = seg_list[jdx] nodes = seg.get_nodes() if nodes[1]==contact_node: self.addEdge( skeletonsegment.SkeletonEdge(seg,direction=1)) contact_node = nodes[0] elif nodes[0]==contact_node: self.addEdge( skeletonsegment.SkeletonEdge(seg,direction=-1)) contact_node = nodes[1] else: # If there's no place to put the segment, then # something's gone badly wrong. raise ooferror.ErrPyProgrammingError( "No adjacent node for new segment in boundary!") # Now check the back. idx = seg_list.index(s1) if idx<len(seg_list): contact_node = last_original_edge.get_nodes()[1] for jdx in range(idx+1,len(seg_list)): seg = seg_list[jdx] nodes = seg.get_nodes() if nodes[0]==contact_node: self.addEdge( skeletonsegment.SkeletonEdge(seg,direction=1)) contact_node = nodes[1] elif nodes[1]==contact_node: self.addEdge( skeletonsegment.SkeletonEdge(seg,direction=-1)) contact_node = nodes[0] else: raise ooferror.ErrPyProgrammingError( "No adjacent node for new segment in boundary!") self.sequence() # Clean up.
def map(self, skeleton, new_skeleton, direction=MAP_DOWN, exterior=None, local=1): old_bdy = self.boundaryset[skeleton] if old_bdy._sequenceable==1: # TODO: use a boolean new_bdy = new_skeleton.makeEdgeBoundary( self.name, exterior=exterior) else: new_bdy = new_skeleton.makeNonsequenceableEdgeBoundary( self.name, exterior=exterior) if local: self.boundaryset[new_skeleton] = new_bdy #TODO: Interface branch. Should this be modified to handle #non-sequenceable segments? Gut feeling is it already does. # Deduce the corresponding edges in the new mesh. old_edges = old_bdy.edges[:] while len(old_edges) > 0: # Start from the back, so list removal is efficient. e = old_edges[-1] # HERE: With a too-small undo buffer, m.source is mangled here, # causing map_end to be null, causing boundaries not to be # propagated. ## TODO: Does the above comment refer to an existing ## problem which still needs to be fixed? if direction==MAP_DOWN: m = e.segment.map() map_start = m.source map_end = m.target else: # direction==MAP_UP if len(e.segment.getParents())==0: del old_edges[-1] continue m = e.segment.getParents()[0].map() map_start = m.target map_end = m.source if len(map_start)==1: # One-to-many case -- Sequence and edge-ify. new_eset = edgesFromSegs(e, map_end, direction) for new_edge in new_eset: new_bdy.addEdge(new_edge) del old_edges[-1] else: # Many-to-one or many-to-many case. # First, find all the edges of this bdy in the parent part. source_eset = set( [e] + [edg for seg in map_start for edg in seg.edges if edg in old_edges]) # Remove them from the old-edge list. # This assumes that the old edges are contiguous # at the end of the list, which is true if the # boundary is sequenceable. TODO: is it always? del old_edges[-len(source_eset):] # Sequence the source edges -- this is the path the # original boundary takes through the source part # of the map. try: (edge_seq, node_seq, winding) = skeletonsegment.segSequence( source_eset) except skeletonsegment.SequenceError: continue # Re-enter the while loop. # Direct the resulting edge-sequence correctly. Edges # always return their nodes in order. if node_seq[0] != edge_seq[0].get_nodes()[0]: edge_seq.reverse() node_seq.reverse() # Extract the subset of the target that has some # relation to the boundary part of the parent. sub_target = [] for e in edge_seq: if direction==MAP_DOWN: for c in e.segment.getChildren(): if c not in sub_target: sub_target.append(c) else: # direction==MAP_UP: for p in e.segment.getParents(): if p not in sub_target: sub_target.append(p) # Exterior check -- for the exterior case, the only # valid target segments are those which have exactly one # element. if exterior: narrow_target = [] for s in sub_target: if s.nElements()==1: narrow_target.append(s) sub_target = narrow_target # Find the counterparts of the endpoints of the path, if # they exist and are unique, otherwise fail. if direction==MAP_DOWN: start_shadow = node_seq[0].getChildren() end_shadow = node_seq[-1].getChildren() else: start_shadow = node_seq[0].getParents() end_shadow = node_seq[-1].getParents() if len(start_shadow)==1: target_start = start_shadow[0] else: continue if len(end_shadow)==1: target_end = end_shadow[0] else: continue # If the points are not distinct, fail. if target_start == target_end: continue # Find the paths in the target subset segment set from # the start node to the end node. The returned path # is sequenced. path_set = skeletonsegment.segPath( target_start, target_end, sub_target) # If the path is not unique, fail. if len(path_set)!=1: continue # Success! We have a single unique sequenced set of # segments from child_start to child_end. Convert # them to edges and add them to the boundary. n1 = target_start for seg in path_set[0]: nodes = seg.get_nodes() if nodes[0]==n1: e = skeletonsegment.SkeletonEdge(seg, direction=1) n2 = nodes[1] else: e = skeletonsegment.SkeletonEdge(seg, direction=-1) n2 = nodes[0] new_bdy.addEdge(e) n1 = n2 # Reset n1 for next iteration.