package edu.princeton.cs.algs4.growingtree.framework; /* * @(#)NodeAndLinkAnimatingJPanel.java * * Last Modified: 9/15/01 */ import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; import java.beans.*; /** * *

* @author Corey Sanders * @version 1.4 9/15/01 */ public class NodeAndLinkAnimatingJPanel

extends DrawingJPanel implements ActionListener{ /** * Node kept for the animating current Panel. */ private DrawingTree

animatingNode; /** * Refers to the list of AffineTransforms used to move the animating node. */ private AffineTransformList movingTransforms; /** * Refers to the currentLocation of the animation. */ private double currentLocation = 0; /** * Refers to the currentStep of the animation. */ private double currentStep = .05; /** * Timer for the animation. */ javax.swing.Timer animationTimer; /** * The delay rate for the animation, set to DEFAULT_DELAY. */ private int delayRate; /** * The default delay for animation (90). */ public static final int DEFAULT_DELAY = 90; /** * Boolean flag whether the panel is drawingLines or not. */ private boolean drawingLines; /** * The Paint for the left line. */ Paint leftLinePaint; /** * The Paint for the right line. */ Paint rightLinePaint; /** * Sole Constructor for the JPanel that paints the node using the AnimatingTree drawNode method. * The JPanel only makes a new image on a resize and otherwise, double-buffers the graphics by * leaving the image unmodified. */ public NodeAndLinkAnimatingJPanel() { super(); // Set delay rate setDelayRate(DEFAULT_DELAY); // Initiates timer setAnimationTimer(new javax.swing.Timer(delayRate, this)); movingTransforms = new AffineTransformList(); // Set transforms list AffineTransform currentTransform = new AffineTransform(); // Make initial moving transforms list. movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); movingTransforms.add(currentTransform); getAnimationTimer().start(); } /** ******************** * Accesssor methods* ******************** */ /** * Gets whether the animation is drawing lines or a node. If the panel is drawing * lines, returns true. * * @return true if the panel is drawing lines. */ public boolean isDrawingLines() { return drawingLines; } /** * Gets the head of the tree currently drawn in the Panel. * * @return TreeHead the tree head. */ public DrawingTree

getAnimatingNode() { return animatingNode; } /** * Gets the animation timer for the animation of the panel. * * @return javax.swing.Timer defining the steps of animation. */ protected javax.swing.Timer getAnimationTimer() { return animationTimer; } /** * Gets the paint for the right line drawing. * * @return Paint right Line Paint. */ public Paint getRightLinePaint() { return rightLinePaint; } /** * Gets the paint for the left line drawing. * * @return Paint left Line Paint. */ public Paint getLeftLinePaint() { return leftLinePaint; } /** ******************* * Mutator methods * ******************* */ /** * Sets whether the animation is drawing lines or a node. If the panel is drawing * lines, sets true. * * @param drawingLines true if the panel is drawing lines. */ public void setDrawingLines(boolean drawingLines) { this.drawingLines = drawingLines; } /** * Sets the paint for the right line drawing. * * @param rightLinePaint right Line Paint. */ public void setRightLinePaint(Paint rightLinePaint) { this.rightLinePaint = rightLinePaint; } /** * Sets the paint for the left line drawing. * * @param leftLinePaint left Line Paint. */ public void setLeftLinePaint(Paint leftLinePaint) { this.leftLinePaint = leftLinePaint; } /** * Sets the delay rate for the timer for the animation. * * param t int delay rate. */ public void setDelayRate(int t) { delayRate = (int)((t*-2) + 200); if (getAnimationTimer() != null) { getAnimationTimer().setDelay(delayRate); } } /** * Sets the animation timer for the animation of the panel. * * @param animationTimer javax.swing.Timer defining the steps of animation. */ protected void setAnimationTimer(javax.swing.Timer animationTimer) { this.animationTimer = animationTimer; } /** * Set the node for drawing. This method should be called for each change in node. * @param node node used for drawing within the panel. */ public void setAnimatingNode(DrawingTree

animatingNode) { this.animatingNode = animatingNode; setDrawTree(true); repaint(); } /** * Sets whether the tree is shown or not. * * @param componentShown boolean flag as to whether the tree is shown. */ public void setComponentShown(boolean componentShown) { super.setComponentShown(componentShown); if (componentShown) { getAnimationTimer().start(); } else { getAnimationTimer().stop(); } } /** ******************* * Drawing methods * ******************* */ /** * Makes the drawing graphics. Uses the image made from makeDrawTreeImage. This is overiden, * to remake the transforms. */ protected void makeDrawTreeGraphics() { super.makeDrawTreeGraphics(); // Correct scaling AffineTransform scaleTransform = AffineTransform.getScaleInstance((getDrawTreeGraphics().getClipBounds().getWidth() / (2.0) ), (getDrawTreeGraphics().getClipBounds().getHeight() / (2.0)) ); AffineTransform scaleSmallTransform = ((AffineTransform)scaleTransform.clone()); scaleSmallTransform.scale(.4, .4); AffineTransform scaleLargeTransform = ((AffineTransform)scaleTransform.clone()); scaleLargeTransform.scale(2.5, 2.5); AffineTransform translateBottomTransform = AffineTransform.getTranslateInstance(getDrawTreeGraphics().getClipBounds().getX() + getDrawTreeGraphics().getClipBounds().getWidth()/2.0 - scaleTransform.getScaleX()/2.0, getDrawTreeGraphics().getClipBounds().getY() + getDrawTreeGraphics().getClipBounds().getHeight() - scaleTransform.getScaleY()); AffineTransform translateLeftTransform = AffineTransform.getTranslateInstance(getDrawTreeGraphics().getClipBounds().getX(), getDrawTreeGraphics().getClipBounds().getY() + getDrawTreeGraphics().getClipBounds().getHeight()/2.0 - scaleTransform.getScaleY()/2.0); AffineTransform translateTopTransform = AffineTransform.getTranslateInstance(getDrawTreeGraphics().getClipBounds().getX() + getDrawTreeGraphics().getClipBounds().getWidth()/2.0 - scaleTransform.getScaleX()/2.0, getDrawTreeGraphics().getClipBounds().getY()); AffineTransform translateRightTransform = AffineTransform.getTranslateInstance(getDrawTreeGraphics().getClipBounds().getX() + getDrawTreeGraphics().getClipBounds().getWidth() - scaleTransform.getScaleX(), getDrawTreeGraphics().getClipBounds().getY() + getDrawTreeGraphics().getClipBounds().getHeight()/2.0 - scaleTransform.getScaleY()/2.0); AffineTransform translateCenterTransform = AffineTransform.getTranslateInstance(getDrawTreeGraphics().getClipBounds().getX() + getDrawTreeGraphics().getClipBounds().getWidth()/2.0 - scaleTransform.getScaleX()/2.0, getDrawTreeGraphics().getClipBounds().getY() + getDrawTreeGraphics().getClipBounds().getHeight()/2.0 - scaleTransform.getScaleY()/2.0); AffineTransform translateSmallTransform = AffineTransform.getTranslateInstance(getDrawTreeGraphics().getClipBounds().getX() + getDrawTreeGraphics().getClipBounds().getWidth()/2.0 - scaleSmallTransform.getScaleX()/2.0, getDrawTreeGraphics().getClipBounds().getY() + getDrawTreeGraphics().getClipBounds().getHeight()/2.0 - scaleSmallTransform.getScaleY()/2.0); AffineTransform translateLargeTransform = AffineTransform.getTranslateInstance(getDrawTreeGraphics().getClipBounds().getX() + getDrawTreeGraphics().getClipBounds().getWidth()/2.0 - scaleLargeTransform.getScaleX()/2.0, getDrawTreeGraphics().getClipBounds().getY() + getDrawTreeGraphics().getClipBounds().getHeight()/2.0 - scaleLargeTransform.getScaleY()/2.0); translateBottomTransform.concatenate(scaleTransform); translateLeftTransform.concatenate(scaleTransform); translateTopTransform.concatenate(scaleTransform); translateRightTransform.concatenate(scaleTransform); translateCenterTransform.concatenate(scaleTransform); translateSmallTransform.concatenate(scaleSmallTransform); translateLargeTransform.concatenate(scaleLargeTransform); // Make initial moving transforms list. movingTransforms.set(0, translateBottomTransform); movingTransforms.set(1, translateLeftTransform); movingTransforms.set(2, translateTopTransform); movingTransforms.set(3, translateRightTransform); movingTransforms.set(4, translateBottomTransform); movingTransforms.set(5, translateCenterTransform); movingTransforms.set(6, translateCenterTransform); movingTransforms.set(7, translateSmallTransform); movingTransforms.set(8, translateLargeTransform); } /** * Method actually called to complete the drawing of the panel. The node is animated to fill the * entire graphics given within the panel. * * @param g2 Graphics2D to animate the node within. */ protected void animate(Graphics2D g2) { if (getAnimatingNode() != null) { currentLocation += currentStep; if (currentLocation > 8) { currentStep = -.05; currentLocation += currentStep; } if (currentLocation < 0) { currentStep = .05; currentLocation += currentStep; } AffineTransform currentTransform = movingTransforms.getTransformStep(currentLocation); // Right line Line2D.Double right = new Line2D.Double( (currentTransform.getTranslateX() + (3*currentTransform.getScaleX())/4.0) , (currentTransform.getTranslateY() + currentTransform.getScaleY()/2.0) , g2.getClipBounds().getX() + g2.getClipBounds().getWidth(), g2.getClipBounds().getY() + g2.getClipBounds().getHeight()); g2.setComposite(getAnimatingNode().getSettings().getRightLinkComposite()); g2.setStroke(getAnimatingNode().getSettings().getRightLinkStroke()); g2.setPaint(getAnimatingNode().getSettings().getRightLinkPaint()); g2.draw(right); // Draw the left and right links Line2D.Double left = new Line2D.Double( (currentTransform.getTranslateX() + currentTransform.getScaleX()/4.0) , (currentTransform.getTranslateY() + currentTransform.getScaleY()/2.0) , g2.getClipBounds().getX(), g2.getClipBounds().getY() + g2.getClipBounds().getHeight()); // Set graphics information g2.setComposite(getAnimatingNode().getSettings().getLeftLinkComposite()); g2.setStroke(getAnimatingNode().getSettings().getLeftLinkStroke()); g2.setPaint(getAnimatingNode().getSettings().getLeftLinkPaint()); g2.draw(left); getAnimatingNode().drawNode(g2, currentTransform); if (isDrawingLines()) { drawLines(g2); } } } /** * Draws the lines in the given panel using the Graphics2D. * * @param g2 Graphics2D to draw the lines within. */ public void drawLines(Graphics2D g2) { double distance = (currentLocation/4.0); BasicStroke line = new BasicStroke(5.0f); g2.setStroke(line); if (distance < 1) { // Makes the left dotted line Line2D.Double crossLeft = new Line2D.Double( (g2.getClipBounds().getX()) , (g2.getClipBounds().getY()) , (g2.getClipBounds().getX() + (distance) * g2.getClipBounds().getWidth()) , (g2.getClipBounds().getY() + (distance) * g2.getClipBounds().getHeight()) ); g2.setPaint(getLeftLinePaint()); g2.draw(crossLeft); } else { distance -= 1; // Makes the left dotted line Line2D.Double crossLeft = new Line2D.Double( (g2.getClipBounds().getX()) , (g2.getClipBounds().getY()) , (g2.getClipBounds().getX() + g2.getClipBounds().getWidth()) , (g2.getClipBounds().getY() + g2.getClipBounds().getHeight()) ); g2.setPaint(getLeftLinePaint()); g2.draw(crossLeft); // Makes the right dotted line Line2D.Double crossRight = new Line2D.Double( (g2.getClipBounds().getX() + g2.getClipBounds().getWidth()) , (g2.getClipBounds().getY()) , (g2.getClipBounds().getX() + (1 - distance) * (g2.getClipBounds().getWidth())) , (g2.getClipBounds().getY() + (distance) * g2.getClipBounds().getHeight()) ); g2.setPaint(getRightLinePaint()); g2.draw(crossRight); } } /** * Overides paintComponenet and is called whenever the Panel needs to be painted. The * painting includes painting the image and then painting the animation (if any). * * @param g Graphics to which the component is drawn. */ public void paintComponent(Graphics g) { // Reset Graphics super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; // Rendering hints. (g2).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); animate(g2); } /************************************************/ /* Implements Action Listener */ /************************************************/ /** * Listens to action events. * * @param e ActionEvent that contains information about the tree. */ public void actionPerformed(ActionEvent e) { repaint(); } }