''' MorphingGUI.py This is an application for setting up Morphing sequences. A morphing sequence involves an overall source image, an overall destination image, and a set of in-between frames. The in-between frames are computed from the overall source and destination images. The warping is controlled by a set of line segment pairs, and these are drawn by the user using this interface. They should be drawn on the overall source and destination images. To create a line segment pair, press the mouse and drag. To select a line that is already drawn, click on it. A selected line can be deleted by right-clicking on it, and then selecting "delete" from the popup menu. A selected line can be moved by dragging on its "handles". Note that moving a line segment in one image does not move it in the other image, except when the line is first drawn. A set of line segment pairs can be saved to a file and loaded in a later session for further editing or application. Morphing may be performed either in Swing panels or in PixelMath image windows. ''' from javax.swing import JMenu, JMenuItem, JCheckBoxMenuItem, JMenuBar, \ JPopupMenu, JFrame, JPanel, JFileChooser, JScrollPane import javax.imageio.ImageIO as ImageIO from java.awt import Color, BorderLayout, FlowLayout, GridLayout, Dimension, Font from java.awt.event import MouseAdapter, MouseMotionAdapter, MouseEvent import math import BlendedWarps from SetOfSegmentPairs import SetOfLineSegmentPairs, Segment, Pair USE_PIXELMATH_IMAGE_FRAMES = False class MorphControlBoard(JFrame): # Constructor for the class. def __init__(self, image1, image2, nframes): self.setTitle("Morph Control Board") self.imfn1 = image1; self.imfn2 = image2 self.nframes = nframes self.dirty = False # no edits yet need saving self.path = "Untitled.SSP" # the SSP extension means Set of Segment Pairs. self.titleIsSet = False # True if a filename has been associated with the current set of line seg. pairs. self.setUpMenu() self.sourcePanel = MorphControlBoard.ImagePanel(image1, self) initialSetOfPairs = SetOfLineSegmentPairs() self.sourcePanel.setSetOfSegmentPairs(initialSetOfPairs, True) self.destinationPanel = MorphControlBoard.ImagePanel(image2, self) self.destinationPanel.setSetOfSegmentPairs(initialSetOfPairs, False) self.upperPanel = JPanel() self.upperPanel.add(self.sourcePanel) self.upperPanel.add(self.destinationPanel) cp = self.getContentPane() cp.setLayout(BorderLayout()) cp.add(self.upperPanel, "North") self.initializeInterpolatedFrames() cp.add(self.scrollingPanel, "Center") dim = self.getDimensions() self.setSize(Dimension(dim[0],dim[1])) self.helpWindow = False self.jfc = False def initializeInterpolatedFrames(self): self.lowerPanel = JPanel() self.lowerPanel.setLayout(GridLayout(3,1)) if not USE_PIXELMATH_IMAGE_FRAMES: self.warpedSourceFrames = [] self.warpedDestFrames = [] self.wsfPanel = JPanel() self.wdfPanel = JPanel() self.infPanel = JPanel() self.interpolatedFrames = [] for i in range(self.nframes): if not USE_PIXELMATH_IMAGE_FRAMES: self.warpedSourceFrames.append(MorphControlBoard.ImagePanel(self.imfn1, self)) self.wsfPanel.add(self.warpedSourceFrames[i]) self.warpedDestFrames.append(MorphControlBoard.ImagePanel(self.imfn1, self)) self.wdfPanel.add(self.warpedDestFrames[i]) self.interpolatedFrames.append(MorphControlBoard.ImagePanel(self.imfn1, self)) self.infPanel.add(self.interpolatedFrames[i]) self.lowerPanel.add(self.infPanel) if not USE_PIXELMATH_IMAGE_FRAMES: self.lowerPanel.add(self.wsfPanel) self.lowerPanel.add(self.wdfPanel) self.lowerPanel.setSize(Dimension(int(self.sourcePanel.imageWidth*self.nframes*1.1), int(self.sourcePanel.imageHeight*3.1))) self.scrollingPanel = JScrollPane(self.lowerPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) self.scrollingPanel.setPreferredSize(Dimension(500,400)) # Determine the board's overall width and height. # If not using PixelMath Image Frames, then the board is bigger, since it has to # contain Swing image panels to show the results. def getDimensions(self): if not USE_PIXELMATH_IMAGE_FRAMES: return (int(min(self.sourcePanel.w*self.nframes*1.1,800)), int(min(self.sourcePanel.h*3.2, 600))) else: return (int(self.sourcePanel.w*self.nframes*1.1), int(self.sourcePanel.h*2.2)) def setUpMenu(self): fileMenu = JMenu("File") fileMenu.add(JMenuItem("New Line Segment Set", actionPerformed=self.new)) fileMenu.add(JMenuItem("Open Line Segment Set...", actionPerformed=self.openFile)) fileMenu.add(JMenuItem("Save", actionPerformed=self.save)) fileMenu.add(JMenuItem("Save Line Segment Set as...", actionPerformed=self.saveAs)) fileMenu.add(JMenuItem("Quit", actionPerformed=self.quitProgram)) morphMenu = JMenu("Morph") morphMenu.add(JMenuItem("Interpolate Lines", actionPerformed=self.interpolateLines)) morphMenu.add(JMenuItem("Warp Source", actionPerformed=self.warpSource)) morphMenu.add(JMenuItem("Warp Destination", actionPerformed=self.warpDestination)) morphMenu.add(JMenuItem("Warp Sequence (Source)", actionPerformed=self.warpSeqSource)) morphMenu.add(JMenuItem("Warp Sequence (Destination)", actionPerformed=self.warpSeqDest)) self.UPIFMenuItem = JCheckBoxMenuItem("Use PixelMath Image Frames", actionPerformed=self.toggleUsePMIF) morphMenu.add(self.UPIFMenuItem) morphMenu.add(JMenuItem("Create Morph Sequence", actionPerformed=self.createMorphSeq)) helpMenu = JMenu("Help") helpMenu.add(JMenuItem("Help", actionPerformed=self.showHelp)) menuBar = JMenuBar() menuBar.add(fileMenu) menuBar.add(morphMenu) menuBar.add(helpMenu) self.setJMenuBar(menuBar) def new(self, event): if self.dirty: cancel = self.promptToSave() if cancel: return print "Ready to create new set of line segment pairs." # create new self.titleIsSet = False self.sourcePanel.ssp = SetOfLineSegmentPairs() self.destinationPanel.ssp = self.sourcePanel.ssp self.path = "Untitled.SSP" def openFile(self, event): if self.dirty: cancel = self.promptToSave() if cancel: return print "Ready to open a file containing a set of line segment pairs." if not self.jfc: self.jfc = JFileChooser('.') jfc = self.jfc cancel = jfc.showOpenDialog(self) if cancel: return cancel file = jfc.getSelectedFile() # Here file is a java.io.File, not a Python File. self.path = file.getAbsolutePath() try: f = open(self.path, "r") # f is a Python File. print "Successfully opened the file for reading: "+self.path s= f.read() f.close() self.sourcePanel.ssp = SetOfLineSegmentPairs() self.sourcePanel.ssp.fromString(s) self.destinationPanel.ssp = self.sourcePanel.ssp print "Successfully read the data." self.dirty = False self.titleIsSet = True self.sourcePanel.repaint() self.destinationPanel.repaint() return False except e: print "Could not find or could not correctly process the file: "+self.path return True def save(self, event): #print "Ready to save a set of line segment pairs in the (hopefully) existing file." # print "self.titleIsSet is "+str(self.titleIsSet) if self.titleIsSet: self.performSave() else: self.promptToSave() def saveAs(self, event): print "Ready to save a set of line segment pairs in a (probably) new file." self.promptToSave() def quitProgram(self, event): if self.dirty: cancel = self.promptToSave() if cancel: return self.setVisible(False) self.destroy() def promptToSave(self): #print "Putting up a dialog box prompting to save or cancel" if not self.jfc: self.jfc = JFileChooser('.') jfc = self.jfc #jfc.setSelectedFile(self.filename) # actually we need an actual java.io.File here. cancel = jfc.showSaveDialog(self) if cancel: return cancel file = jfc.getSelectedFile() # Here file is a java.io.File, not a Python File. self.path = file.getAbsolutePath() self.performSave() return False def performSave(self): f = open(self.path, "w") # f is a Python File. print "Successfully opened the file for writing: "+self.path try: f.write(self.sourcePanel.ssp.toString()) except e: print "Had trouble encoding or writing the set of segments file... "+str(e) f.close() print "Successfully wrote the data." self.dirty = False self.titleIsSet = True def interpolateLines(self, event): print "Preparing to interpolate lines" for f_idx in range(self.nframes): frac = (f_idx + 1.0)/(self.nframes + 1.0) thisFrame = self.interpolatedFrames[f_idx] thisFrame.ssp = self.sourcePanel.ssp.interpolate(frac) thisFrame.repaint() def getPMSourcePixel(self, x, y): # Get a source pixel from a PixelMath Image Frame return pmGetPixel(self.srcWin, x, y) def getPMDestPixel(self, x, y): # Get a "destination" frame pixel from a PixelMath Image Frame return pmGetPixel(self.dstWin, x, y) def putPMPixel(self, x, y, r, g, b): # Put a pixel's (r,g,b) values into the target PixelMath Image Frame pmSetPixel(self.tgtWin, x, y, r, g, b) def warpSource(self, event): print "Preparing to warp source using the main set of line segments" #print "Source image: "+str(self.sourcePanel.image) #print "First source pixel (test) value is: "+str(self.sourcePanel.getPixel(0, 0)) if USE_PIXELMATH_IMAGE_FRAMES: self.srcWin = self.getSeparateSourceWindow() self.tgtWin = self.getSeparateTargetWindow() BlendedWarps.warp(self.getPMSourcePixel, self.putPMPixel, \ self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, self.sourcePanel.ssp) else: BlendedWarps.warp( self.sourcePanel.getPanelPixel, self.warpedSourceFrames[0].putPanelPixel, \ self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, self.sourcePanel.ssp) self.interpolatedFrames[0].repaint() def warpDestination(self, event): if USE_PIXELMATH_IMAGE_FRAMES: self.srcWin = self.getSeparateSourceWindow() self.tgtWin = self.getSeparateTargetWindow() BlendedWarps.warp(self.getPMSourcePixel, self.putPMPixel, \ self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, self.sourcePanel.ssp) else: BlendedWarps.warp( self.sourcePanel.getPanelPixel, self.warpedDestFrames[0].putPanelPixel, \ self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, self.sourcePanel.ssp.reverse()) self.interpolatedFrames[0].repaint() def warpSeqSource(self, event): print "Creating sequence of warped source images" if USE_PIXELMATH_IMAGE_FRAMES: self.srcWin = self.getSeparateSourceWindow() self.dstWin = self.getSeparateDestWindow() self.warpedSourceImages = [] for f_idx in range(self.nframes): frac = (f_idx + 1.0)/(self.nframes + 1.0) thisFrame = self.warpedSourceFrames[f_idx] # If no set of line segment pairs already exists for this frame, create one. # (We don't want to overwrite existing -- they may have been hand-edited.) if len(thisFrame.ssp.pairs)==0: thisFrame.ssp = self.sourcePanel.ssp.interpolate(frac) if USE_PIXELMATH_IMAGE_FRAMES: self.tgtWin = self.getSeparateTargetWindow() BlendedWarps.warp(self.getPMSourcePixel, self.putPMPixel, \ self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, thisFrame.ssp) self.warpedSourceImages.append(self.tgtWin) else: BlendedWarps.warp( self.sourcePanel.getPanelPixel, \ thisFrame.putPanelPixel, self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, thisFrame.ssp) thisFrame.repaint() def warpSeqDest(self, event): print "Creating sequence of warped destination images" if USE_PIXELMATH_IMAGE_FRAMES: self.srcWin = self.getSeparateSourceWindow() self.dstWin = self.getSeparateDestWindow() self.warpedDestImages = [] for f_idx in range(self.nframes): frac = ((f_idx + 1.0)/(self.nframes + 1.0)) thisFrame = self.warpedDestFrames[self.nframes - f_idx - 1] # If no set of line segment pairs already exists for this frame, create one. # (We don't want to overwrite existing -- they may have been hand-edited.) if len(thisFrame.ssp.pairs)==0: thisFrame.ssp = self.sourcePanel.ssp.interpolate(frac) if USE_PIXELMATH_IMAGE_FRAMES: self.tgtWin = self.getSeparateTargetWindow() BlendedWarps.warp(self.getPMDestPixel, self.putPMPixel, \ self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, self.warpedDestFrames[f_idx].ssp.reverse()) self.warpedDestImages.append(self.tgtWin) else: BlendedWarps.warp( self.destinationPanel.getPanelPixel, \ thisFrame.putPanelPixel, self.sourcePanel.imageWidth, self.sourcePanel.imageHeight, thisFrame.ssp.reverse()) thisFrame.repaint() def toggleUsePMIF(self, event): global USE_PIXELMATH_IMAGE_FRAMES if USE_PIXELMATH_IMAGE_FRAMES: USE_PIXELMATH_IMAGE_FRAMES = False else: USE_PIXELMATH_IMAGE_FRAMES = True self.UPIFMenuItem.setState(USE_PIXELMATH_IMAGE_FRAMES) def createMorphSeq(self, event): print "Preparing to create morph sequence" # First compute warped sequences for source and destination: self.warpSeqSource(event) self.warpSeqDest(event) # Now produce cross-fade between them: for f_idx in range(self.nframes): print "Working on Frame "+str(f_idx) frac = (f_idx + 1.0)/(self.nframes + 1.0) thisFrame = self.interpolatedFrames[f_idx] # If no set of line segment pairs already exists for this frame, create one. # (We don't want to overwrite existing -- they may have been hand-edited.) #if len(thisFrame.ssp.pairs)==0: # thisFrame.ssp = self.sourcePanel.ssp.interpolate(frac) if USE_PIXELMATH_IMAGE_FRAMES: tgtWin = self.getSeparateTargetWindow() pmSetSource1(self.warpedSourceImages[f_idx]) pmSetSource2(self.warpedDestImages[f_idx]) pmSetDestination(tgtWin) pmSetFormula(str(1-frac)+"*S1(x,y) + "+str(frac)+"*S2(x,y)") pmCompute() else: a = frac b = 1.0 - frac for y in range(self.sourcePanel.imageHeight): for x in range(self.sourcePanel.imageWidth): (r1,g1,b1) = self.warpedSourceFrames[f_idx].getPanelPixel(x, y) (r2,g2,b2) = self.warpedDestFrames[f_idx].getPanelPixel(x, y) thisFrame.putPanelPixel(x, y, int(b*r1+a*r2), int(b*g1+a*g2), int(b*b1+a*b2)) thisFrame.repaint() def getSeparateSourceWindow(self): return pmOpenImage(0, self.imfn1) def getSeparateTargetWindow(self): return pmOpenImage(0, self.imfn1) def getSeparateDestWindow(self): return pmOpenImage(0, self.imfn2) def showHelp(self, event): if not self.helpWindow: d = JDialog() d.setTitle("Morphing Help Window") d.setLayout(BorderLayout()) j = JTextPane(editable=False) j.setText('''Morphing Control Board -- by S. Tanimoto This program supports feature-based metamorphosis using the method of Beier and Neely. Create a morphing transition between the source image and destination image as follows: Draw a line segment on the source image. A copy is automatically created on the destination image. Align the segment with key features in each image. You can select a segment by clicking on it. When selected, handles appear on the endpoints and midpoint. Click and drag to adjust the endpoints, or using the midpoint, move the entire segment. Be careful not to click accidentally and draw a zero-length segment, as it will mess up the morphing. It is possible, however, to delete such segments. If they are visible, right click on them and select Delete. If invisible, save the line segments as a file and then use Wordpad to edit the file. The file menu is all about saving and loading sets of line segment pairs. The morph menu is all about applying warps to the images in various ways. The interpolated frames are shown in the lower, scrollable panel. The first row shows both the interpolated line segments and the final morph sequence, if requested. The second row shows a sequence of warped source images on request. The third row shows a sequence of warped destination images on request. As an alternative to using the three rows of images, you can use PixelMath image frames to hold the generated images. Then you can easily zoom, save the images, etc. If you choose to use PixelMath image frames for all the intermediate images, you will typically get a lot of windows. You can remove them all by using the PixelMath Python window's File menu item to remove all image windows. To use different images or to change the number of in-between frames, edit the parameters at the end of the source file. ''') s = JScrollPane(j) d.add(s, BorderLayout.CENTER) d.pack() d.setVisible(True) self.helpWindow = d else: self.helpWindow.setVisible(True) #The ImagePanel class is used to hold a copy of an image, and to support # drawing line segments on it.''' class ImagePanel(JPanel): def __init__(self, filename, parent): if filename==None: return self.parentFrame = parent self.w = 300 self.h = 300 self.image = self.loadImage(filename) self.imageWidth = self.image.getWidth() self.imageHeight = self.image.getHeight() self.setPreferredSize(Dimension(self.w, self.h)) # Associate a mouse handler with the board. self.addMouseListener(MorphControlBoard.ImagePanel.ImageMouseAdapter()) self.addMouseMotionListener(MorphControlBoard.ImagePanel.ImageMouseMotionAdapter()) self.setSetOfSegmentPairs(SetOfLineSegmentPairs(), False) # initialize as Empty and Destination. self.drawingMode = 'Ready' # Note that Swing panels use a coordinate system where y increases downwards, but # PixelMath needs coordinates where y increases upwards. So we correct the y values # of line segments as they are constructed, # and we map them back to swing coordinates when displaying on the panels. def correctY(self, yMouse): return (self.imageHeight - 1 - yMouse) def yToSwingY(self, y): return (self.imageHeight - 1 - y) def setSetOfSegmentPairs(self, sosp, srcOrDest): self.ssp = sosp self.srcOrDest = srcOrDest # Here we override the JPanel paintComponent method so that # we control how the board is rendered. def paintComponent(self, g): i = 0; j = 0 k = 0 g.drawImage(self.image, 0, 0, self.w, self.h, self) indexOfSelected = self.ssp.indexOfSelected idx = 0 for pair in self.ssp.pairs: if self.srcOrDest: seg = pair.sSeg else: seg = pair.dSeg if idx==indexOfSelected: g.setColor(Color.red) else: g.setColor(Color.green) g.drawLine(seg.P[0], self.yToSwingY(seg.P[1]), seg.Q[0], self.yToSwingY(seg.Q[1])) if idx==indexOfSelected: # draw handles: g.setColor(Color.blue) side = 8; halfSide = side/2 g.drawRect(seg.P[0]-halfSide, self.yToSwingY(seg.P[1])-halfSide, side, side) g.drawRect(seg.Q[0]-halfSide, self.yToSwingY(seg.Q[1])-halfSide, side, side) seg.updateMidpoint() g.drawRect(seg.midX-halfSide, self.yToSwingY(seg.midY)-halfSide, side, side) idx += 1 # To load one image from a file, we use the Java ImageIO class. def loadImage(self, filename): try: #print "Attempting to load image: "+filename f = open(filename,'r') image = ImageIO.read(f) #print "Read the file " self.w = image.getWidth() self.h = image.getHeight() return image except IOError: print "Could not read an image named: "+filename print " There could be a path problem, "+\ "or the image may need to be created." raise def getDrawingMode(self): return self.drawingMode def setDrawingMode(self, newMode): self.drawingMode = newMode # print "drawingMode is now "+newMode def updateEndpoint1(self, x, y): self.LS.P = (x,y) self.repaint() self.parentFrame.dirty = True def updateEndpoint2(self, x, y): #print "In updateEndpoint2" self.LS.Q = (x,y) self.repaint() self.parentFrame.dirty = True def getSelfLSandOther(self): if len(self.ssp.pairs)==0: print "Cannot set set LS because no pairs yet." return idx = self.ssp.indexOfSelected pair = self.ssp.pairs[idx] if self.srcOrDest: self.LS = pair.sSeg self.otherLS = pair.dSeg self.otherImagePanel = self.parentFrame.destinationPanel else: self.LS = pair.dSeg self.otherLS = pair.sSeg self.otherImagePanel = self.parentFrame.sourcePanel self.otherImagePanel.otherImagePanel = self def updateEndpoint2InBoth(self, x, y): self.LS.Q = (x,y) self.otherLS.Q = (x,y) #self.reportLS() self.repaint() self.otherImagePanel.repaint() self.parentFrame.dirty = True def moveMidpoint(self, x, y): self.LS.updateMidpoint() dx = x - self.LS.midX dy = y - self.LS.midY currentPX = self.LS.P[0] currentPY = self.LS.P[1] currentQX = self.LS.Q[0] currentQY = self.LS.Q[1] self.LS.P = (currentPX + dx, currentPY + dy) self.LS.Q = (currentQX + dx, currentQY + dy) self.repaint() self.parentFrame.dirty = True def reportLS(self): print "LS = "+str(self.LS) def addNewPair(self, x1, y1, x2, y2): #print "Preparing to add new pair" seg1 = Segment((x1, y1), (x2, y2)) seg2 = Segment((x1, y1), (x2, y2)) #print "Segments created" pair = Pair(seg1, seg2) self.ssp.pairs.append(pair) #print "New pair appended" if self.srcOrDest: self.LS = seg2 self.otherLS = seg1 self.otherImagePanel = self.parentFrame.destinationPanel else: self.LS = seg1 self.otherLS = seg2 self.otherImagePanel = self.parentFrame.sourcePanel self.otherImagePanel.otherImagePanel = self # print "Added the new pair" #self.reportLS() self.parentFrame.dirty = True # unselect any selected segment self.ssp.unselect() self.repaint() self.otherImagePanel.repaint() def handleDelete(self, event): self.ssp.deleteSelected() print "Deletion finished" self.repaint() self.otherImagePanel.repaint() def moveToBack(self, event): #This is where we handle a move-to-back operation. self.ssp.moveSelectedToBack() self.repaint() self.otherImagePanel.repaint() print "MoveToBack finished" def getPanelPixel(self, x, y): val = self.image.getRGB(x, self.yToSwingY(y)) r = (val >> 16) & 0xff g = (val >> 8) & 0xff b = (val) & 0xff return (r,g,b) return val def putPanelPixel(self, x, y, r, g, b): val = (r << 16) + (g << 8) + b self.image.setRGB(x, self.yToSwingY(y), val) class ImageMouseAdapter(MouseAdapter): def mousePressed(self, event): targetImage = event.getSource() x = event.getX(); y = targetImage.correctY(event.getY()) # print "In mousePressed, y is now defined" # See if there is a hit, and if so, set the selection. try: (hitType, hitIdx) = targetImage.ssp.findAnyHit(x, y, targetImage.srcOrDest) print "(hitType, hitIdx) = "+str((hitType, hitIdx)) #hitIdx = targetImage.ssp.findFirstHit(x, y, targetImage.srcOrDest) except e: print e if hitIdx < 0: # print "No existing line segment was hit." # Now we can assume we are beginning a new segment. targetImage.setDrawingMode("DraggingEndpoint2InSrcAndDest") # Create a new line segment targetImage.addNewPair(x, y, x, y) else: # print "There was a hit on item: "+str(hitIdx) # Set the selection: targetImage.ssp.indexOfSelected = hitIdx targetImage.getSelfLSandOther() # Did the user Right-click on the segment? if event.getButton() == MouseEvent.BUTTON3: # print "You pressed the mouse button #3 on the image." # Bring up a popup menu: try: rightClickMenu = MorphControlBoard.ImagePanel.LineEditingPopupMenu(targetImage) rightClickMenu.show(targetImage, x, y) #print "The popup menu should now be visible." except e: print "There was a problem creating the popup menu: "+str(e) #raise targetImage.repaint() targetImage.otherImagePanel.repaint() if hitType == "Endpoint1": targetImage.setDrawingMode("DraggingEndpoint1") if hitType == "Endpoint2": targetImage.setDrawingMode("DraggingEndpoint2") if hitType == "Midpoint": targetImage.setDrawingMode("DraggingMidpoint") def mouseReleased(self, event): #print "You released the mouse on the image." targetImage = event.getSource() targetImage.setDrawingMode("Ready") def mouseClicked(self, event): #print "Clicked!" pass class ImageMouseMotionAdapter(MouseMotionAdapter): def mouseDragged(self, event): #print "dragging" targetImage = event.getSource() cx = event.getX() cy = targetImage.correctY(event.getY()) if targetImage.getDrawingMode()=="DraggingEndpoint1": targetImage.updateEndpoint1(cx, cy) if targetImage.getDrawingMode()=="DraggingEndpoint2": targetImage.updateEndpoint2(cx, cy) if targetImage.getDrawingMode()=="DraggingEndpoint2InSrcAndDest": targetImage.updateEndpoint2InBoth(cx, cy) if targetImage.getDrawingMode()=="DraggingMidpoint": targetImage.moveMidpoint(cx, cy) #print "dragging update done." '''Drawing protocol: Clicking away from any existing line segment starts a new line segment. Right clicking on any line segment brings up a popup menu with the option to (1) delete, (2) split, or (3) move to back. It also selects the line segment and highlights it and its "mate" in the other image. When selected, handles appear on the line segment at its endpoints and at its midpoint. Clicking on an endpoint handle allows moving that endpoint while the other endpoint remains fixed. Clicking on a midpoint handle allows picking up the segment and translating it (moving both endpoints, too). ''' class LineEditingPopupMenu(JPopupMenu): def __init__(self, parentPanel): #print "Initializing a LineEditingPopupMenu instance." self.parentPanel = parentPanel #print "parentPanel is "+str(parentPanel) menuItem1 = JMenuItem("Delete", actionPerformed=self.parentPanel.handleDelete) #print "menuItem1 created." self.add(menuItem1); menuItem2 = JMenuItem("Move to back of priority list", actionPerformed=self.parentPanel.moveToBack); self.add(menuItem2); filename1 = "Mona128.jpg" filename2 = "tiger128.jpg" theGUI = MorphControlBoard(filename1, filename2, 5) theGUI.show()