What follows is the highlighted source code with comments. You can also download this file directly: DemoPanel.java.
/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package coordinatechanges; import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import javax.swing.*; /** * * @author pat */ public class DemoPanel<F, I> extends JPanel implements TransformChangedListener, MouseListener, MouseMotionListener { TransformManager transform_manager; Point2D point_plot_location = null; Point2D square_plot_location = null; public DemoPanel() { this.setBackground(Color.BLACK); this.addMouseListener(this); this.addMouseMotionListener(this); setFocusable(true); transform_manager = new TransformManager(this, new Rectangle2D.Double(-2, -2, 4, 4)); transform_manager.setListener(this); } @Override public void transformChanged() { repaint(); } //// FUNCTIONS RELATED TO THE STATUSBAR private JLabel status_bar = null; /** * Set the label to print status updates to. */ public void setStatusBar(JLabel new_bar) { status_bar = new_bar; // This will update the status bar with the current mode of interaction: setInteractionMode(current_mode); } private void postStatus(String status) { if (status_bar != null) { status_bar.setText(status); } } /////////////////////////// Interaction //// INTERACTION MODES public enum InteractionMode { // Add more modes of interaction here... DO_NOTHING, ZOOM_BOX, PLOT_A_POINT, PLOT_A_UNIT_SQUARE; } private InteractionMode current_mode = InteractionMode.DO_NOTHING; public void setInteractionMode(InteractionMode new_mode) { current_mode = new_mode; switch (current_mode) { case DO_NOTHING: postStatus("Welcome to some program with some name!"); break; case ZOOM_BOX: postStatus("Drag your mouse to draw a rectangle to zoom to."); break; case PLOT_A_POINT: postStatus("Click somewhere to plot a point."); break; case PLOT_A_UNIT_SQUARE: postStatus("Click somewhere to plot a unit square."); break; default: postStatus("The programmer forgot to update setInteractionMode with a directions for the user."); break; } repaint(); } /** * Change coordinates so that we show 10% more in each dimension. */ public final void zoomOut() { transform_manager.scale(1.1); this.postStatus("Zoomed out by a factor of 10%."); } /** * Guarantee that the unit square be displayed. */ public void zoomSquare() { transform_manager.setDisplayBox(new Rectangle2D.Double(0, 0, 1, 1)); this.postStatus("Zoomed to the unit square with lower left vertex (0,0)."); } ////////////////// MOUSE // The current location of the mouse in math coordinates. Point2D mouse_position_in_math_coordinates; // The current location of the mouse in screen coordinates. Point2D mouse_position_in_screen_coordinates; // For ZOOM_BOX mode boolean dragging_box = false; Point2D dragging_box_start = new Point2D.Double(0, 0), dragging_box_end = new Point2D.Double(0, 0); /** * Get the rectangle being drawn by the user when in Zoom In mode... */ private Rectangle2D.Double getZoomRectangle() { double minx = Math.min(dragging_box_start.getX(), dragging_box_end.getX()), maxx = Math.max(dragging_box_start.getX(), dragging_box_end.getX()), miny = Math.min(dragging_box_start.getY(), dragging_box_end.getY()), maxy = Math.max(dragging_box_start.getY(), dragging_box_end.getY()); return new Rectangle2D.Double(minx, miny, maxx - minx, maxy - miny); } @Override public void mouseClicked(MouseEvent me) { mouse_position_in_screen_coordinates = me.getPoint(); mouse_position_in_math_coordinates = transform_manager.toMathCoordinates(me.getPoint()); switch (current_mode) { case PLOT_A_POINT: this.point_plot_location = mouse_position_in_math_coordinates; repaint(); break; case PLOT_A_UNIT_SQUARE: this.square_plot_location = mouse_position_in_math_coordinates; repaint(); break; } } @Override public void mouseEntered(MouseEvent me) { mouse_position_in_screen_coordinates = me.getPoint(); mouse_position_in_math_coordinates = transform_manager.toMathCoordinates(me.getPoint()); } @Override public void mouseExited(MouseEvent me) { mouse_position_in_screen_coordinates = me.getPoint(); mouse_position_in_math_coordinates = transform_manager.toMathCoordinates(me.getPoint()); } @Override public void mousePressed(MouseEvent me) { mouse_position_in_screen_coordinates = me.getPoint(); mouse_position_in_math_coordinates = transform_manager.toMathCoordinates(me.getPoint()); switch (current_mode) { case ZOOM_BOX: dragging_box = true; dragging_box_end = dragging_box_start = mouse_position_in_math_coordinates; break; } } @Override public void mouseReleased(MouseEvent me) { mouse_position_in_screen_coordinates = me.getPoint(); mouse_position_in_math_coordinates = transform_manager.toMathCoordinates(me.getPoint()); switch (current_mode) { case ZOOM_BOX: if (dragging_box) { dragging_box_end = mouse_position_in_math_coordinates; Rectangle2D r = getZoomRectangle(); if ((r.getWidth() > 0) && (r.getHeight() > 0)) { transform_manager.setDisplayBox(r); } dragging_box = false; } break; } } @Override public void mouseDragged(MouseEvent me) { mouse_position_in_screen_coordinates = me.getPoint(); mouse_position_in_math_coordinates = transform_manager.toMathCoordinates(me.getPoint()); switch (current_mode) { case ZOOM_BOX: dragging_box_end = mouse_position_in_math_coordinates; repaint(); break; } } @Override public void mouseMoved(MouseEvent me) { mouse_position_in_screen_coordinates = me.getPoint(); mouse_position_in_math_coordinates = transform_manager.toMathCoordinates(me.getPoint()); } // DRAWING ROUTINE: @Override public void paintComponent(Graphics gfx) { // This stuff is standard, and should be in any paintComponent method. super.paintComponent(gfx); Graphics2D g = (Graphics2D) gfx; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // This transformation will convert from math to screen coordinates. AffineTransform t = transform_manager.getTransform(); // Draw lines of thickness 3. g.setStroke(new BasicStroke(3)); // Draw the circle which is inscribed in the unit square. Ellipse2D.Double e = new Ellipse2D.Double(0, 0, 1, 1); g.setColor(Color.WHITE); g.draw(t.createTransformedShape(e)); if (square_plot_location != null) { // The square_plot_location is given in math coordinates. Rectangle2D.Double r = new Rectangle2D.Double( square_plot_location.getX(), square_plot_location.getY(), 1, 1); g.setColor(new Color(0, 0, 255, 127)); // Set color to translucent blue. g.fill(t.createTransformedShape(r)); // fill in the square g.setColor(Color.BLUE); g.draw(t.createTransformedShape(r)); // draw the outline of the square } if (point_plot_location != null) { // point_plot_location is given in math coordinates. Point2D screen_loc=transform_manager.toScreenCoordinates(point_plot_location); g.setColor(Color.RED); // This gives the circle of radius 5 centered at screen_loc: Ellipse2D.Double p = new Ellipse2D.Double(screen_loc.getX()-5, screen_loc.getY()-5, 10, 10); g.fill(p); // Fill in the circle given in screen coordinates. (No transform required.) } switch (current_mode) { case ZOOM_BOX: if (dragging_box) { g.setColor(Color.ORANGE); g.setStroke(new BasicStroke(2)); g.draw(transform_manager.getTransform().createTransformedShape(getZoomRectangle())); } break; } } }