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 delete(self, segments): self.sequence() old_segments = [x.segment for x in self.edges] index_set = [] for s in segments: try: index_set.append(old_segments.index(s)) except ValueError: pass # It's ok if s isn't in the boundary. Just go on. dead_segments = [old_segments[idx] for idx in index_set] for d in dead_segments: old_segments.remove(d) #Interface branch if self._sequenceable==1: (seg_list, node_list, winding) = skeletonsegment.segSequence(old_segments) # If sequencing worked, then the modified boundary is # topologically OK. It is permissible to modify our edges list. dead_edges = [self.edges[idx] for idx in index_set] for d in dead_edges: d.remove() # Remove yourself from the segment's list of edges. self.edges.remove(d) self.sequence() # Clean up.
def _loadEdgeBoundary(menuitem, skeleton, name, edges, exterior): ## debug.fmsg() # "edges" is a list of pairs of integer indices into skeleton.nodes skelcontext = skeletoncontext.skeletonContexts[skeleton] skeleton = skelcontext.getObject() seglist = [ skeleton.getSegment(*[skeleton.nodes[i] for i in e]) for e in edges ] #Interface branch try: (segs, nodes, winding) = skeletonsegment.segSequence(seglist) startnode = skeleton.nodes[edges[0][0]] skelcontext.createEdgeBoundary(name, seglist, startnode, exterior, autoselect=0) except skeletonsegment.SequenceError: directionlist = [] for i in range(len(edges)): if seglist[i].get_nodes()[0] == skeleton.nodes[edges[i][0]]: directionlist.append(1) else: directionlist.append(-1) skelcontext.createNonsequenceableEdgeBoundary(name, seglist, directionlist, exterior, autoselect=0)
def try_append(self, new_segments): if len(self.edges)==0: return 0 self.sequence() all_segments = new_segments.union([x.segment for x in self.edges]) try: (seg_list, node_list, winding) = skeletonsegment.segSequence(all_segments) return 1 except skeletonsegment.SequenceError: return 0
def loop_check(self): seg_set = self.segmenter(self.skelcontext, self.aggwidget.get_value()) try: (segs, nodes, winding) = skeletonsegment.segSequence(seg_set) except skeletonsegment.SequenceError: return -1 else: if len(nodes) > 0: if nodes[0] == nodes[-1] and winding == [0, 0]: return 1 for partner in nodes[0].getPartners(): if partner == nodes[-1] and winding == [0, 0]: return 1 return 0 return -1 # Zero-length list is also invalid.
def loop_check(self): # seg_set = self.segmenter(self.skelcontext, self.aggwidget.get_value()) if self.interfacewidget.get_value() is None: return -2 skelobj = self.skelcontext.getObject() (seg_set, direction_set) = skelobj.getInterfaceSegments(self.skelcontext, self.interfacewidget.get_value()) try: (segs, nodes, winding) = skeletonsegment.segSequence(seg_set) except skeletonsegment.SequenceError: return -1 else: if len(nodes) > 0: if nodes[0] == nodes[-1] and winding == [0, 0]: return 1 for partner in nodes[0].getPartners(): if partner == nodes[-1] and winding == [0, 0]: return 1 return 0 return -1 # Zero-length list is also invalid.
def loop_check(self): #seg_set = self.segmenter(self.skelcontext, self.aggwidget.get_value()) if self.interfacewidget.get_value() is None: return -2 skelobj = self.skelcontext.getObject() (seg_set, direction_set) = skelobj.getInterfaceSegments( self.skelcontext, self.interfacewidget.get_value()) try: (segs, nodes, winding) = skeletonsegment.segSequence(seg_set) except skeletonsegment.SequenceError: return -1 else: if len(nodes) > 0: if nodes[0] == nodes[-1] and winding == [0, 0]: return 1 for partner in nodes[0].getPartners(): if partner == nodes[-1] and winding == [0, 0]: return 1 return 0 return -1 # Zero-length list is also invalid.
def try_delete(self, segments): self.sequence() old_segments = [x.segment for x in self.edges] index_set = [] for s in segments: try: index_set.append(old_segments.index(s)) except ValueError: pass # It's ok if s isn't in the boundary. Just go on. dead_segments = [old_segments[idx] for idx in index_set] for d in dead_segments: old_segments.remove(d) try: (seg_list, node_list, winding) = skeletonsegment.segSequence(old_segments) return 1 except skeletonsegment.SequenceError: return 0
def _loadEdgeBoundary(menuitem, skeleton, name, edges, exterior): ## debug.fmsg() # "edges" is a list of pairs of integer indices into skeleton.nodes skelcontext = skeletoncontext.skeletonContexts[skeleton] skeleton = skelcontext.getObject() seglist = [skeleton.getSegment( *[skeleton.nodes[i] for i in e] ) for e in edges] #Interface branch try: (segs, nodes, winding) = skeletonsegment.segSequence(seglist) startnode = skeleton.nodes[edges[0][0]] skelcontext.createEdgeBoundary(name, seglist, startnode, exterior, autoselect=0) except skeletonsegment.SequenceError: directionlist=[] for i in range(len(edges)): if seglist[i].get_nodes()[0]==skeleton.nodes[edges[i][0]]: directionlist.append(1) else: directionlist.append(-1) skelcontext.createNonsequenceableEdgeBoundary(name, seglist, directionlist, exterior, autoselect=0)
def _segset2seglist(seg_set, direction, skel): if len(seg_set)==0: raise ooferror.ErrUserError( "Attempt to sequence null segment set.") (seg_list, node_list, winding_number) = skeletonsegment.segSequence(seg_set) # At this point, seg_list is an ordered list of adjacent # segments, and "node_list" is the corresponding set of nodes. # The path is a loop if node_list[0]==node_list[-1], otherwise # it's a simple path. # We may want to reverse this list, depending on the # setting of "direction". firstnode = node_list[0] lastnode = node_list[-1] startnode = firstnode # The winding number is used to handle the cases where the line # crosses a periodic boundary. if (firstnode != lastnode and firstnode not in lastnode.getPartners()) \ or winding_number != [0,0]: # In case the first node and last node are partners, we set them # equal so that we can compare positions properly. if lastnode in firstnode.getPartners(): lastnode = firstnode if direction == director.Director('Left to right'): if firstnode.position().x > \ lastnode.position().x + winding_number[0]*skel.MS.size()[0]: seg_list.reverse() startnode = lastnode elif direction == director.Director('Right to left'): if firstnode.position().x < \ lastnode.position().x + winding_number[0]*skel.MS.size()[0]: seg_list.reverse() startnode = lastnode elif direction == director.Director('Top to bottom'): if firstnode.position().y < \ lastnode.position().y + winding_number[1]*skel.MS.size()[1]: seg_list.reverse() startnode = lastnode elif direction == director.Director('Bottom to top'): if firstnode.position().y > \ lastnode.position().y + winding_number[1]*skel.MS.size()[1]: seg_list.reverse() startnode = lastnode else: # User specified clockwise or counterclockwise for a # non-loop segment set. This is an error. raise ooferror.ErrUserError( "Clockwise or counterclockwise is for closed loops.") # Use the total area swept out by vectors from the origin to the # nodes along the path as we traverse the path in order to # determine if the existing sequence traces a clockwise or # counterclockwise path. If two consecutive nodes in the # node_list are not the endpoints of a segment, we are crossing a # periodic boundary. In this case, use the positions not to find # the area swept out, but to increment the position_adjustment # needed to unwrap the periodic boundary conditions. else: # firstnode==lastnode, loop case. startnode = None area = 0.0 p0 = node_list[0].position() position_adjustment = primitives.Point(0,0) for i in range(1,len(node_list)): p1 = node_list[i].position() + position_adjustment if skel.findSegment(node_list[i],node_list[i-1]) is not None: area += p0.x * p1.y - p1.x * p0.y p0 = p1 else: position_adjustment += p0 - p1 # setting p0 = p1 here will be redundant as we # must now adjust positions by p0 - p1 if direction == director.Director('Clockwise'): if area > 0.0: seg_list.reverse() elif direction == director.Director('Counterclockwise'): if area < 0.0: seg_list.reverse() else: # User specified an endpoint orientation on a loop. raise ooferror.ErrUserError( "Closed loops need clockwise or counterclockwise direction.") return (startnode, seg_list)
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.
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.