/* * @(#)InsertBSTAnimation.java * * Last Modified: 9/15/01 */ import java.util.*; import java.awt.*; import java.awt.geom.*; import java.text.*; /** * * The Animation object that defines the Insertion into a BSTTree. Two constructors exist, * one setting the animator and animation color Schemes. The addition of nodes that the Animation * must pass include both the AffineTransform and then BSTTree or either separately.
*
* @author Corey Sanders
* @version 2.2 9/15/01
*/
public class InsertBSTAnimation extends AbstractAnimation {
/**
* Previous and next node indeces
*/
protected int previousNodeIndex = 0;
protected int nextNodeIndex = 0;
/**
* Color Scheme used for the animation on left, using one of the NodeSettings Schemes.
*/
private NodeSettings animationSchemeLeft;
/**
* Color Scheme used for the animation on right, using one of the NodeSettings Schemes.
*/
private NodeSettings animationSchemeRight;
/**
* Color Scheme used for the animator, using one of the NodeSettings Schemes.
*/
private NodeSettings animatorScheme;
/**
* Color Scheme used for the key of the animator, using one of the KeySettings Schemes.
*/
private KeySettings keyAnimatorScheme;
/**
* Private doubles used to hold the current and previous location steps.
*/
private double currentLocation = 0.0;
private double previousLocation = 0.0;
/**
* Refers to the list of AffineTransforms used to draw the animation.
*/
private AffineTransformList movements = new AffineTransformList();
/**
* Refers to the linked list which will store the node of each step, used to draw the
* pass of each node.
*/
private LinkedList nodeMovements = new LinkedList();
/**
* Holds the node that is doing the drawing.
*/
private BSTTree insertNode;
/**
* Counts the amount of comparisons made.
*/
protected int comparisonCount = 0;
/**
* Counts the initial insertion size of the tree.
*/
protected int insertionSize = 0;
/**
* The constructor which initiates the status and prepares the colorSchemes. The node
* which is animating must be passed. The first step added is the identity step.
*
* @param node the BSTTree which is animated during the animation.
* @param AnimationSchemeLeft the NodeSettings
associated with a color scheme according to NodeSettings for the left Animation.
* @param AnimationSchemeRight the NodeSettings
associated with a color scheme according to NodeSettings for the right Animation.
* @param AnimatorScheme the NodeSettings
associated with a color scheme according to NodeSettings.
* @param KeyAnimatorScheme the KeySettings
associated with a color scheme according to KeySettings.
* @param startingCmd the Animation command that this should start.
* @param stepTime the time for each step of the Animation. Sets the initial value.
*/
public InsertBSTAnimation(BSTTree node, NodeSettings AnimationSchemeLeft, NodeSettings AnimationSchemeRight, NodeSettings AnimatorScheme, KeySettings KeyAnimatorScheme, String startingCmd, int stepTime) {
super();
// Set defaults if no color schemes exist
if (AnimationSchemeLeft == null) {
AnimationSchemeLeft = new NodeSettings();
}
if (AnimationSchemeRight == null) {
AnimationSchemeRight = new NodeSettings();
}
if (AnimatorScheme == null) {
AnimatorScheme = new NodeSettings();
}
if (KeyAnimatorScheme == null) {
KeyAnimatorScheme = new KeySettings();
}
// Add identity
movements.add(new AffineTransform());
// Add null first node
nodeMovements.add(null);
// Set insertion size.
insertionSize = node.getHead().size();
// Set Animation Schemes
setAnimationSchemeLeft((NodeSettings)AnimationSchemeLeft.clone());
setAnimationSchemeRight((NodeSettings)AnimationSchemeRight.clone());
setAnimatorScheme((NodeSettings)AnimatorScheme.clone());
setKeyAnimatorScheme((KeySettings)KeyAnimatorScheme.clone());
// Set the drawing node.
setInsertNode(node);
setStartingCommand(startingCmd);
setStepTime(stepTime);
}
/**
* The constructor which initiates the status and sets the colorSchemes to null. No colors
* will be change using this animation. The node
* which is animating must be passed. The first step added is the identity step.
*
* @param node the BSTTree which is animated during the animation.
*/
public InsertBSTAnimation(BSTTree node) {
this(node, null, null, null , null , Animation.PLAY, DEFAULT_STEP);
}
/************************/
/* Accessor methods */
/************************/
/**
* Gets the current location of the Insertion.
*
* @return double of the current location of the ndoe currently being inserted and animated.
*/
protected double getCurrentLocation() {
return currentLocation;
}
/**
* Gets the previous location of the Insertion.
*
* @return double of the previous location of the ndoe currently being inserted and animated.
*/
protected double getPreviousLocation() {
return previousLocation;
}
/**
* Gets the nodes of the Insertion.
*
* @return LinkedList of the nodes.
*/
protected LinkedList getNodeMovements() {
return nodeMovements;
}
/**
* Gets the affine transform movements of the Insertion.
*
* @return AffineTransformList of the affine transform movements.
*/
protected AffineTransformList getMovements() {
return movements;
}
/**
* Gets the node currently being drawn during the Insertion.
*
* @return BSTTree of the ndoe currently being inserted and animated.
*/
protected BSTTree getInsertNode() {
return insertNode;
}
/**
* Gets the NodeSettings for the left animation scheme for the insertion.
*
* @return NodeSettings for the node after the animated node passes it to the left.
*/
public NodeSettings getAnimationSchemeLeft() {
return animationSchemeLeft;
}
/**
* Gets the NodeSettings for the right animation scheme for the insertion.
*
* @return NodeSettings for the node after the animated node passes it to the right.
*/
public NodeSettings getAnimationSchemeRight() {
return animationSchemeRight;
}
/**
* Gets the NodeSettings for the animator scheme for the insertion.
*
* @return NodeSettings for the node animating.
*/
public NodeSettings getAnimatorScheme() {
return animatorScheme;
}
/**
* Sets the KeySettings for the animator scheme key for the insertion.
*
* @return KeySettings for the key of the node animating.
*/
public KeySettings getKeyAnimatorScheme() {
return keyAnimatorScheme;
}
/************************/
/* Mutator methods */
/************************/
/**
* Sets the current location of the Insertion.
*
* @return currentLocation double of the current location of the ndoe currently being inserted and animated.
*/
protected void setCurrentLocation(double currentLocation) {
this.currentLocation = currentLocation;
}
/**
* Sets the previous location of the Insertion.
*
* @param previousLocation double of the previous location of the ndoe currently being inserted and animated.
*/
protected void setPreviousLocation(double previousLocation) {
this.previousLocation = previousLocation;
}
/**
* Sets the node currently being drawn during the Insertion.
*
* @param BSTTree of the node currently being inserted and animated.
*/
protected void setInsertNode(BSTTree node) {
insertNode = node;
}
/**
* Sets the NodeSettings for the left animation scheme for the insertion. The settings affect
* the change the node makes after the inserted node passes it to the left.
*
* @param scheme NodeSettings for the node after the animated node passes it to the left.
*/
public void setAnimationSchemeLeft(NodeSettings scheme) {
animationSchemeLeft = scheme;
}
/**
* Sets the NodeSettings for the right animation scheme for the insertion. The settings affect
* the change the node makes after the inserted node passes it to the right.
*
* @param scheme NodeSettings for the node after the animated node passes it to the right.
*/
public void setAnimationSchemeRight(NodeSettings scheme) {
animationSchemeRight = scheme;
}
/**
* Sets the NodeSettings for the animator scheme for the insertion. The settings affect
* the change the node makes as it is animating during the insertion
*
* @param scheme NodeSettings for the node animating.
*/
public void setAnimatorScheme(NodeSettings scheme) {
animatorScheme = scheme;
}
/**
* Sets the KeySettings for the animator scheme key for the insertion. The settings affect
* the change the key of the node makes as it is animating during the insertion
*
* @param scheme KeySettings for the key of the node animating.
*/
public void setKeyAnimatorScheme(KeySettings scheme) {
keyAnimatorScheme = scheme;
}
/****************************/
/* Insert Animation methods */
/****************************/
/**
* Add a step to the InsertAnimation. The step is added with only an AffineTransform,
* which is used to draw that step in the Animation. No node will be affected, even
* if the transform comes from a node.
*
* @param a AffineTransform to be drawn in the following step.
*/
public void add(AffineTransform a) {
this.add(a, null);
}
/**
* Add a step to the InsertAnimation. The step is added with an AffineTransform and a BSTTree
* node. Consequently, when the step is performed, the node will transform to the given AffineTransform,
* and the node passed will be modified (Color Scheme only).
*
* @param a AffineTransform to be drawn in the following step.
* @param node the color scheme is changed when the step is completed.
*/
public void add(AffineTransform a, BSTTree node) {
movements.add(a);
nodeMovements.add(node);
}
/**
* Add a step to the InsertAnimation. The step is added with only a BSTTree which is used to determine
* the AffineTransform for the current step.W hen the step is performed, the node will transform to the passed node's AffineTransform,
* and the node passed will be modified (Color Scheme only).
*
* @param node the color scheme is changed when the step is completed.
*/
public void add(BSTTree node) {
this.add(node.getCurrentTransform(), node);
}
/*********************/
/* Animation methods */
/*********************/
/**
* Draws the animation of the next step, using the status of the animation (Animation.PLAY, Animation.PAUSE and so forth).
* After completing the drawing, the Animation sends an AnimationEvent to all its listeners, indicating
* any information that the listerners may wish to use.
*
* @param g2 the graphics to which the animation step should be drawn.
* @param startingStatus the status used as the starting command of animation, if needed.
*/
public void drawAnimation(Graphics2D g2, String startingStatus) {
setStartingCommand(startingStatus);
// BEGIN status
if (getStatus().equals(Animation.BEGIN)) {
currentLocation = 0;
previousLocation = 0;
//animator scheme exists
getInsertNode().saveSettings();
getInsertNode().setNodeSettings(getAnimatorScheme());
((DrawingKey)insertNode.getValue()).saveSettings();
((DrawingKey)insertNode.getValue()).setKeySettings(getKeyAnimatorScheme());
animationAction();
messageAction(Animation.BEGIN + " Insertion of "+insertNode.getKey().toString());
// set starting status
setStatus(getStartingCommand());
return;
}
// Currently on a step and no changes have occured. Return to starting command
if (getStatus().equals(Animation.STEP)) {
setStatus(getStartingCommand());
}
// PLAY status
if (getStatus().equals(Animation.PLAY)) {
messageAction(Animation.PLAY);
previousLocation = currentLocation;
if(getStep()) { // Skip middle animation steps.
currentLocation = Math.ceil(currentLocation) + getStepSize();
}
else { // Normal step
currentLocation += getStepSize();
}
// Set node positions.
nextNodeIndex = (int)Math.ceil(currentLocation);
previousNodeIndex = (int)Math.floor(currentLocation);
// Completed Animation
if (currentLocation >= (movements.size()-1)) {
setStatus(Animation.FINISH);
drawAnimation(g2, getStartingCommand());
return;
}
// Finished a step in the Animation.
if (Math.ceil(previousLocation) == Math.floor(currentLocation) && previousLocation != 0) {
setStatus(Animation.STEP);
BSTTree previousNode = ((BSTTree)nodeMovements.get(previousNodeIndex));
BSTTree nextNode = ((BSTTree)nodeMovements.get(nextNodeIndex));
// In between two nodes.
if ((previousNode != null) && nextNode != null) {
messageAction("Comparison of "+insertNode.getKey().toString()+" & " + previousNode.getKey().toString()+":");
// Left
if(previousNode.getLeftTree() == nextNode) {
previousNode.saveLeftSettings();
previousNode.getSettings().setNodeSettings(getAnimationSchemeLeft());
previousNode.getSettings().setLeftSettings(getAnimationSchemeLeft());
previousNode.drawNodeAndLeftLink();
messageAction(" ("+insertNode.getKey().toString()+" < " + previousNode.getKey().toString()+")");
messageAction(insertNode.getKey().toString()+" proceeds to the left");
}
// Right
if(previousNode.getRightTree() == nextNode) {
previousNode.saveRightSettings();
previousNode.getSettings().setNodeSettings(getAnimationSchemeLeft());
previousNode.getSettings().setRightSettings(getAnimationSchemeRight());
previousNode.drawNodeAndRightLink();
messageAction(" ("+insertNode.getKey().toString()+" > " + previousNode.getKey().toString()+")");
messageAction(insertNode.getKey().toString()+" proceeds to the right");
}
comparisonCount++;
}
}
}
// REWIND status
if (getStatus().equals(Animation.REWIND)) {
messageAction(Animation.REWIND);
previousLocation = currentLocation;
if(getStep()) { // Skip middle Animation Steps
currentLocation = Math.floor(currentLocation) - getStepSize();
}
else { // Normal Step
currentLocation -= getStepSize();
}
nextNodeIndex = (int)Math.ceil(currentLocation);
previousNodeIndex = (int)Math.floor(currentLocation);
// Beginning of Animation
if (currentLocation <= 0) {
setStatus(Animation.PAUSE);
currentLocation = 0;
animationAction();
return;
}
// Finished a step in the Animation.
if (Math.floor(previousLocation) == Math.ceil(currentLocation)) {
setStatus(Animation.STEP);
// Node after nextNode.
BSTTree nextFollowingNode = ((BSTTree)nodeMovements.get(nextNodeIndex+1));
// Next node.
BSTTree nextNode = ((BSTTree)nodeMovements.get(nextNodeIndex));
// The next two nodes exist
if ((nextNode != null) && (nextFollowingNode != null)) {
// Left
if(nextNode.getLeftTree() == nextFollowingNode) {
nextNode.restoreLeftSettings();
}
// Right
if(nextNode.getRightTree() == nextFollowingNode) {
nextNode.restoreRightSettings();
}
nextNode.eraseNodeAndLink();
nextNode.drawNodeAndLink();
comparisonCount--;
}
}
}
// PAUSE status
if (getStatus().equals(Animation.PAUSE)) {
nextNodeIndex = (int)Math.ceil(currentLocation);
previousNodeIndex = (int)Math.floor(currentLocation);
messageAction(Animation.PAUSE);
}
// STOP status
if (getStatus().equals(Animation.STOP)) {
messageAction(Animation.STOP);
return;
}
// FINISH status
if (getStatus().equals(Animation.FINISH)) {
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(3);
messageAction(Animation.FINISH);
messageAction("*--------Insertion of "+insertNode.getKey().toString()+"--------*\n Comparisons: "+
comparisonCount+"\n Average Insertion: "+nf.format(((BSTTreeHead)insertNode.getHead()).averageInsertion(insertionSize))+
"\n Worst Case Insertion: "+nf.format(((BSTTreeHead)insertNode.getHead()).worstCaseInsertion(insertionSize) ));
animationAction();
restore();
return;
}
// Set movements
movements.set(previousNodeIndex, resetPosition(previousNodeIndex));
movements.set(nextNodeIndex, resetPosition(nextNodeIndex));
// Draw just the node without links.
insertNode.drawNode(g2, movements.getTransformStep(currentLocation));
// Call listeners
animationAction();
}
/**
* Restores the settings of all nodes encountered during the animation. Usually called at
* the end of the animation (Animation.FINISH) to restore all settings changed throughout
* the animation. This also restores the animator node.
*/
protected void restore() {
for(int i=0; (i+1) < nodeMovements.size(); i++) {
if((nodeMovements.get(i) != null)) {
BSTTree thisNode = ((BSTTree)nodeMovements.get(i));
BSTTree nextNode = ((BSTTree)nodeMovements.get(i+1));
if (thisNode.isSettingsSaved()) {
if(thisNode.getLeftTree() == nextNode) {
thisNode.restoreLeftSettings();
thisNode.drawNodeAndLeftLink();
}
if(thisNode.getRightTree() == nextNode) {
thisNode.restoreRightSettings();
thisNode.drawNodeAndRightLink();
}
}
if (((DrawableKey)thisNode.getValue()).isSettingsSaved()) {
((DrawableKey)thisNode.getValue()).restoreSettings();
}
}
}
BSTTree thisNode = (BSTTree)nodeMovements.getLast();
if (thisNode.isSettingsSaved()) {
thisNode.restoreSettings();
}
if (((DrawableKey)thisNode.getValue()).isSettingsSaved()) {
((DrawableKey)thisNode.getValue()).restoreSettings();
}
}
/**
* Returns the AffineTransform after resetting the transform based upon the node's position.
* The node Index calls the node list to get the node for that index. If no node is present,
* then the AffineTransform previously declared for the nodeIndex is returned. Otherwise, the
* AffineTransform associated with the node is returned.
*
* @param nodeIndex index for the resetting AffineTransform.
* @return AffineTransform for the nodeIndex.
*/
protected AffineTransform resetPosition(int nodeIndex) {
if (nodeMovements.get(nodeIndex) == null)
return (AffineTransform)movements.get(nodeIndex);
else
return ((BSTTree)nodeMovements.get(nodeIndex)).getCurrentTransform();
}
/**
* Calls all of the listeners of the current Animation and passed information regarding the
* progress and status of the current Animation. Additionally, the id of the type of animation is
* passed. Within, the animationEventPerformed
method is called.
*
* @param cmd String Animation command passed instead of the current Status.
* @param description String description for messages.
*/
protected void animationAction(String cmd, String description) {
super.animationAction(AnimationEvent.INSERT_BST_ANIMATION, cmd, description, currentLocation / (double)movements.size());
}
}