''' SetOfSegmentPairs.py This file defines a class that represents a set of pairs of line segments that can be used in image morphing. The class provides a variety of methods useful in managing the sets, including input and output, and editing. A line segment pair consists of a destination segment (Pd, Qd) and a source segment (Ps, Qs). They are stored in that order. ''' from pointLineDist import pointLineDist tol = 3 # Tolerance for drawing "hits" class SetOfLineSegmentPairs: def __init__(self): self.pairs = [] self.indexOfSelected = -1 def findFirstHit(self, x, y, srcOrDest): try: #print "Trying to find first 'hit' " idx = 0 for pair in self.pairs: if srcOrDest: seg = pair.sSeg else: seg = pair.dSeg if seg.segmentHit(x, y, tol): #print "Found a hit on item "+str(idx) return idx idx += 1 return -1 except e: print "There was a problem in SetOfLineSegmentPairs.findFirstHit: "+str(e) def findAnyHit(self, x, y, srcOrDest): try: # print "Trying to find any endpoint1 or midpoint hit. " idx = 0 for pair in self.pairs: if srcOrDest: seg = pair.sSeg else: seg = pair.dSeg if idx==self.indexOfSelected: # print "Trying endpoint and midpoint hits on item "+str(idx) result = seg.segmentHitOnEndOrMidpoint(x, y, tol) else: # print "Trying segmentHit on item "+str(idx) result = seg.segmentHit(x, y, tol) if result: # print "Found a segment hit on item "+str(idx) return (result, idx) idx += 1 return ("No-Hit", -1) except e: print "There was a problem in SetOfLineSegmentPairs.findFirstHit: "+str(e) def deleteSelected(self): if self.indexOfSelected < 0: print "Nothing selected. Cannot delete selected" return # print "Performing the delete of item "+str(self.indexOfSelected) self.pairs.__delitem__(self.indexOfSelected) self.indexOfSelected = -1 # No selection after deletion. def moveSelectedToBack(self): # print "Moving selected to back" if self.indexOfSelected < 0: print "Nothing selected. Cannot move anything to back." return selected = self.pairs[self.indexOfSelected] self.pairs.__delitem__(self.indexOfSelected) self.pairs.append(selected) self.indexOfSelected = -1 # Item no longer selected. def toString(self): pairs = self.pairs # print "pairs = "+str(pairs) listOfPairStrings = [pair.toString() for pair in self.pairs] return reduce(concatWithNewline, listOfPairStrings) def fromString(self, s): lines = s.split("\n") self.pairs = [self.newPairFromString(line) for line in lines] def newPairFromString(self, s): # print "In newPairFromString, s = "+s seg0 = Segment((0,0),(0,0)) seg1 = Segment((0,0),(0,0)) pair = Pair(seg0, seg1) pair.fromString(s) return pair def reverse(self): '''Return a new SetOfLineSegmentPairs in which each pair has its two segments interchanged.''' result = SetOfLineSegmentPairs() result.pairs = map(lambda p: p.reverse(), self.pairs) return result def interpolate(self, a): '''Return a new SetOfLineSegmentPairs in which the destination segment in each pair is a weighted average of the original source and destination segments.''' result = SetOfLineSegmentPairs() result.pairs = map(lambda p: p.interpolate(a), self.pairs) return result def concatWithNewline(x,y): return x+"\n"+y class Pair: def __init__(self, destSeg, sourceSeg): self.dSeg = destSeg self.sSeg = sourceSeg def toString(self): return self.dSeg.toString()+";"+self.sSeg.toString() def fromString(self, s): halves = s.split(";") # print "In Pair.fromString, halves="+str(halves) self.dSeg = Segment((0,0),(0,0)) self.dSeg.fromString(halves[0]) self.sSeg = Segment((0,0),(0,0)) self.sSeg.fromString(halves[1]) def reverse(self): '''Return a new line segment pair in which the segments have been interchanged.''' return Pair(self.sSeg, self.dSeg) def interpolate(self, a): '''Return a new line segment pair in which the destination segment is a weighted average of the original source and destination segments.''' xp = int((1-a)*self.sSeg.P[0] + a*self.dSeg.P[0]) yp = int((1-a)*self.sSeg.P[1] + a*self.dSeg.P[1]) xq = int((1-a)*self.sSeg.Q[0] + a*self.dSeg.Q[0]) yq = int((1-a)*self.sSeg.Q[1] + a*self.dSeg.Q[1]) newSeg = Segment((xp,yp),(xq,yq)) return Pair(newSeg, self.sSeg.copy()) class Segment: def __init__(self, P, Q): self.P = P self.Q = Q self.updateMidpoint() def copy(self): return Segment(self.P, self.Q) def toString(self): return str((self.P, self.Q)) def fromString(self, s): (P,Q) = eval(s) self.P = P self.Q = Q def updateMidpoint(self): self.midX = (self.P[0]+self.Q[0])/2 self.midY = (self.P[1]+self.Q[1])/2 def midpointHit(self, x, y, tol): if abs(self.midX - x) > tol: return False if abs(self.midY - y) > tol: return False return True def endpoint1Hit(self, x, y, tol): if abs(self.P[0] - x) > tol: return False if abs(self.P[1] - y) > tol: return False return True def endpoint2Hit(self, x, y, tol): if abs(self.Q[0] - x) > tol: return False if abs(self.Q[1] - y) > tol: return False return True def segmentHit(self, x, y, tol): #print "In segmentHit, (P, Q, x, y, tol)="+str((self.P, self.Q, x, y, tol)) yes = "Segment" if self.endpoint1Hit(x, y, tol): return yes if self.endpoint2Hit(x, y, tol): return yes (u, v) = pointLineDist(x, y, self.P[0], self.P[1], self.Q[0], self.Q[1]) #print "In SetOfSetmentPair.segmentHit, (u, v) = "+str((u,v)) if abs(v) > tol: return False if u < 0 or u > 1: return False return yes def segmentHitOnEndOrMidpoint(self, x, y, tol): if self.endpoint1Hit(x, y, tol): return "Endpoint1" if self.endpoint2Hit(x, y, tol): return "Endpoint2" if self.midpointHit(x, y, tol): return "Midpoint" return False def test(): seg00 = Segment((0,0),(0,0)) seg01 = Segment((0,0),(0,0)) pair0 = Pair(seg00, seg01) pair0.fromString("(35,46),(52,31);(27,21),(85,47)") seg1 = Segment((0,2),(7,11)) seg2 = Segment((40,45), (50,55)) pair1 = Pair(seg1, seg2) sosp1 = SetOfLineSegmentPairs() sosp1.pairs.append(pair0) sosp1.pairs.append(pair1) bigString = sosp1.toString() print "The set of segment pairs is "+ bigString sosp2 = SetOfLineSegmentPairs() sosp2.fromString(bigString) newString = sosp2.toString() print "newString = "+newString sosp1.indexOfSelected = 0 print sosp1.findAnyHit( 50, 55, False) print seg00 #newPair = pair1.interpolate(0.2) #print "pair1: "+str(pair1) #print "newPair: "+str(newPair) newSosp = sosp1.interpolate(0.5) print "newSosp: "+str(newSosp) #test()