diff --git a/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java b/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java index b344631..6db74e7 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java @@ -28,9 +28,11 @@ */ package edu.umd.cs.piccolox; +import java.awt.Dimension; import java.awt.DisplayMode; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; +import java.awt.Point; import java.awt.Rectangle; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; @@ -56,6 +58,10 @@ * @author Jesse Grosjean */ public class PFrame extends JFrame { + private static final Dimension DEFAULT_FRAME_DIMENSION = new Dimension(400,400); + + private static final Point DEFAULT_FRAME_POSITION = new Point(100, 100); + /** Used to allow versioned binary streams for serializations. */ private static final long serialVersionUID = 1L; @@ -92,15 +98,15 @@ * displayed on the provided device. * * @param title title to display at the top of the frame - * @param aDevice device onto which PFrame is to be displayed - * @param fullScreenMode whether to display a full screen frame or not + * @param device device onto which PFrame is to be displayed + * @param fullScreen whether to display a full screen frame or not * @param canvas to embed in the frame, may be null. If so, it'll create a * default PCanvas */ - public PFrame(final String title, final GraphicsDevice aDevice, final boolean fullScreenMode, final PCanvas canvas) { - super(title, aDevice.getDefaultConfiguration()); + public PFrame(final String title, final GraphicsDevice device, final boolean fullScreen, final PCanvas canvas) { + super(title, device.getDefaultConfiguration()); - graphicsDevice = aDevice; + graphicsDevice = device; setBackground(null); setBounds(getDefaultFrameBounds()); @@ -122,7 +128,7 @@ setContentPane(canvas); validate(); - setFullScreenMode(fullScreenMode); + setFullScreenMode(fullScreen); canvas.requestFocus(); beforeInitialize(); @@ -154,7 +160,7 @@ * @return default frame bounds */ public Rectangle getDefaultFrameBounds() { - return new Rectangle(100, 100, 400, 400); + return new Rectangle(DEFAULT_FRAME_POSITION, DEFAULT_FRAME_DIMENSION); } /** @@ -324,6 +330,13 @@ public void initialize() { } + /** + * Method for testing the creating of PFrame. + * + * @deprecated since it's not terribly useful + * + * @param argv command line arguments + */ public static void main(final String[] argv) { new PFrame(); } diff --git a/extras/src/main/java/edu/umd/cs/piccolox/activities/PPathActivity.java b/extras/src/main/java/edu/umd/cs/piccolox/activities/PPathActivity.java index 65129ac..b52255c 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/activities/PPathActivity.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/activities/PPathActivity.java @@ -51,22 +51,58 @@ */ public abstract class PPathActivity extends PInterpolatingActivity { + /** + * The "knots" that define this path's activity timing through its activity + * and should be an monotonously increasing array starting where each value + * is >=0 and ending at 1f. + */ protected float[] knots; + /** + * Constructs a PPathActivity that will last the specified duration, will + * animate every stepRate and will progress according to the knots provided. + * + * @param duration duration in milliseconds that this activity should last + * @param stepRate interval in milliseconds between animation steps + * @param knots array defining the speed of the animation alongs it's + * animation + */ public PPathActivity(final long duration, final long stepRate, final float[] knots) { this(duration, stepRate, 0, PInterpolatingActivity.SOURCE_TO_DESTINATION, knots); } + /** + * Constructs a PPathActivity that will repeat the specified number of + * times, last the specified duration, will animate every stepRate and will + * progress according to the knots provided. + * + * @param duration duration in milliseconds that this activity should last + * @param stepRate interval in milliseconds between animation steps + * @param knots array defining the speed of the animation alongs it's + * animation + * @param loopCount # of times activity should repeat + * @param mode controls easing of the activity + */ public PPathActivity(final long duration, final long stepRate, final int loopCount, final int mode, final float[] knots) { super(duration, stepRate, loopCount, mode); setKnots(knots); } + /** + * Returns the number of knots that define the timing of this activity. + * + * @return # of knots + */ public int getKnotsLength() { return knots.length; } + /** + * Changes the knots that define the timing of this activity. + * + * @param newKnots the new knots to assign to this activity + */ public void setKnots(final float[] newKnots) { if (newKnots == null) { this.knots = null; @@ -76,6 +112,11 @@ } } + /** + * Return the knots that define the timing of this activity. + * + * @return new knots + */ public float[] getKnots() { if (knots == null) { return null; @@ -83,14 +124,32 @@ return (float[]) knots.clone(); } + /** + * Changes the knot at the given index. + * + * @param index index of knot to change + * @param knot new value to assign to the knot + */ public void setKnot(final int index, final float knot) { knots[index] = knot; } + /** + * Returns the value of the knot at the given index. + * + * @param index index of desired knot + * @return value of knot at given index + */ public float getKnot(final int index) { return knots[index]; } + /** + * Sets the target's value taking knot timing into account. + * + * @param zeroToOne how much of this activity has elapsed 0=none, + * 1=completed + */ public void setRelativeTargetValue(final float zeroToOne) { int currentKnotIndex = 0; @@ -119,5 +178,13 @@ setRelativeTargetValue(normalizedPointOnRange, startKnot, endKnot); } + /** + * An abstract method that allows subclasses to define what target value + * matches the given progress and knots. + * + * @param zeroToOne how far between the knots the activity is + * @param startKnot knot that defines the start of this particular interpolation + * @param endKnot knot that defines the end of this particular interpolation + */ public abstract void setRelativeTargetValue(float zeroToOne, int startKnot, int endKnot); } diff --git a/extras/src/main/java/edu/umd/cs/piccolox/activities/PPositionPathActivity.java b/extras/src/main/java/edu/umd/cs/piccolox/activities/PPositionPathActivity.java index d48ea29..2679e7f 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/activities/PPositionPathActivity.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/activities/PPositionPathActivity.java @@ -42,54 +42,134 @@ * @author Jesse Grosjean */ public class PPositionPathActivity extends PPathActivity { - + /** Points that define the animation's path. */ protected Point2D[] positions; + + /** An abstract representation of the thing being positioned. */ protected Target target; + /** + * Interface that objects must conform to in order to have their position + * animated. + */ public interface Target { - public void setPosition(double x, double y); + /** + * Set's the target's position to the coordinate provided. + * + * @param x the x component of the new position + * @param y the y component of the new position + */ + void setPosition(double x, double y); } - public PPositionPathActivity(final long duration, final long stepRate, final Target aTarget) { - this(duration, stepRate, aTarget, null, new Point2D[0]); + /** + * Constructs a position activity that acts on the given target for the + * duration provided and will update it's position at the given stepRate. + * + * @param duration milliseconds of animation + * @param stepRate milliseconds between successive position updates + * @param target abstract representation of thing being animated + */ + public PPositionPathActivity(final long duration, final long stepRate, final Target target) { + this(duration, stepRate, target, null, new Point2D[0]); } - public PPositionPathActivity(final long duration, final long stepRate, final Target aTarget, final float[] knots, + /** + * Constructs a position activity that acts on the given target for the + * duration provided and will update it's position at the given stepRate. It + * will follow the path defined by the knots and positions arguments. + * + * @param duration milliseconds of animation + * @param stepRate milliseconds between successive position updates + * @param target abstract representation of thing being animated + * @param knots timing to use when animating + * @param positions points along the path + */ + public PPositionPathActivity(final long duration, final long stepRate, final Target target, final float[] knots, final Point2D[] positions) { - this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, aTarget, knots, positions); + this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, target, knots, positions); } + /** + * Constructs a position activity that will repeat the number of times + * specified. It will act on the given target for the duration provided and + * will update it's position at the given stepRate. It will follow the path + * defined by the knots and positions arguments. + * + * @param duration milliseconds of animation + * @param stepRate milliseconds between successive position updates + * @param loopCount number of times this activity should repeat + * @param mode how easing is handled on this activity + * @param target abstract representation of thing being animated + * @param knots timing to use when animating + * @param positions points along the path + */ public PPositionPathActivity(final long duration, final long stepRate, final int loopCount, final int mode, - final Target aTarget, final float[] knots, final Point2D[] positions) { + final Target target, final float[] knots, final Point2D[] positions) { super(duration, stepRate, loopCount, mode, knots); - target = aTarget; + this.target = target; this.positions = (Point2D[]) positions.clone(); } + /** + * Returns true since this activity modifies the view and so cause a + * repaint. + * + * @return always true + */ protected boolean isAnimation() { return true; } + /** + * Returns a copy of the path's points. + * + * @return array of points on the path + */ public Point2D[] getPositions() { return (Point2D[]) positions.clone(); } + /** + * Returns the point at the given index. + * + * @param index desired position index + * @return point at the given index + */ public Point2D getPosition(final int index) { return positions[index]; } + /** + * Changes all positions that define where along the target is being + * positioned during the animation. + * + * @param positions new animation positions + */ public void setPositions(final Point2D[] positions) { this.positions = (Point2D[]) positions.clone(); } + /** + * Sets the position of the point at the given index. + * + * @param index index of the point to change + * @param position point defining the new position + */ public void setPosition(final int index, final Point2D position) { positions[index] = position; } + /** + * Extracts positions from a GeneralPath and uses them to define this + * activity's animation points. + * + * @param path source of points + */ public void setPositions(final GeneralPath path) { final PathIterator pi = path.getPathIterator(null, 1); final ArrayList points = new ArrayList(); - final float point[] = new float[6]; + final float[] point = new float[6]; float distanceSum = 0; float lastMoveToX = 0; float lastMoveToY = 0; @@ -115,6 +195,8 @@ case PathIterator.SEG_QUADTO: case PathIterator.SEG_CUBICTO: throw new RuntimeException(); + default: + // ok to do nothing it'll just be skipped } if (points.size() > 1) { @@ -127,8 +209,8 @@ } final int size = points.size(); - final Point2D newPositions[] = new Point2D[size]; - final float newKnots[] = new float[size]; + final Point2D[] newPositions = new Point2D[size]; + final float[] newKnots = new float[size]; for (int i = 0; i < size; i++) { newPositions[i] = (Point2D) points.get(i); @@ -142,6 +224,15 @@ setKnots(newKnots); } + /** + * Overridden to interpret position at correct point along animation. + * + * TODO: improve these comments + * + * @param zeroToOne how far along the activity we are + * @param startKnot the index of the startKnot + * @param endKnot the index of the endKnot + */ public void setRelativeTargetValue(final float zeroToOne, final int startKnot, final int endKnot) { final Point2D start = getPosition(startKnot); final Point2D end = getPosition(endKnot); diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PNavigationEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PNavigationEventHandler.java index 5408e29..714aa25 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PNavigationEventHandler.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/event/PNavigationEventHandler.java @@ -59,12 +59,18 @@ * @author Jesse Grosjean */ public class PNavigationEventHandler extends PBasicInputEventHandler { - + private static final int NAVIGATION_DURATION = 500; + /** The UP direction on the screen. */ public static final int NORTH = 0; + /** The DOWN direction on the screen. */ public static final int SOUTH = 1; + /** The RIGHT direction on the screen. */ public static final int EAST = 2; + /** The LEFT direction on the screen. */ public static final int WEST = 3; + /** The IN direction on the scene. */ public static final int IN = 4; + /** The OUT direction on the scene. */ public static final int OUT = 5; private static Hashtable NODE_TO_GLOBAL_NODE_CENTER_MAPPING = new Hashtable(); @@ -72,6 +78,10 @@ private PNode focusNode; private PTransformActivity navigationActivity; + /** + * Constructs a Navigation Event Handler that will only accepts left mouse + * clicks. + */ public PNavigationEventHandler() { super(); setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); @@ -81,50 +91,62 @@ // Focus Change Events. // **************************************************************** - public void keyPressed(final PInputEvent e) { + /** + * Processes key pressed events. + * + * @param event event representing the key press + */ + public void keyPressed(final PInputEvent event) { final PNode oldLocation = focusNode; - switch (e.getKeyCode()) { + switch (event.getKeyCode()) { case KeyEvent.VK_LEFT: - moveFocusLeft(e); + moveFocusLeft(event); break; case KeyEvent.VK_RIGHT: - moveFocusRight(e); + moveFocusRight(event); break; case KeyEvent.VK_UP: case KeyEvent.VK_PAGE_UP: - if (e.isAltDown()) { - moveFocusOut(e); + if (event.isAltDown()) { + moveFocusOut(event); } else { - moveFocusUp(e); + moveFocusUp(event); } break; case KeyEvent.VK_DOWN: case KeyEvent.VK_PAGE_DOWN: - if (e.isAltDown()) { - moveFocusIn(e); + if (event.isAltDown()) { + moveFocusIn(event); } else { - moveFocusDown(e); + moveFocusDown(event); } break; + default: + // Pressed key is not a navigation key. } if (focusNode != null && oldLocation != focusNode) { - directCameraViewToFocus(e.getCamera(), focusNode, 500); + directCameraViewToFocus(event.getCamera(), focusNode, NAVIGATION_DURATION); } } - public void mousePressed(final PInputEvent aEvent) { - moveFocusToMouseOver(aEvent); + /** + * Animates the camera to the node that has been pressed. + * + * @param event event representing the mouse press + */ + public void mousePressed(final PInputEvent event) { + moveFocusToMouseOver(event); if (focusNode != null) { - directCameraViewToFocus(aEvent.getCamera(), focusNode, 500); - aEvent.getInputManager().setKeyboardFocus(aEvent.getPath()); + directCameraViewToFocus(event.getCamera(), focusNode, NAVIGATION_DURATION); + event.getInputManager().setKeyboardFocus(event.getPath()); } } @@ -136,30 +158,69 @@ // move the focus to the parent of the current focus. // **************************************************************** - public void moveFocusDown(final PInputEvent e) { + /** + * Moves the focus in the downward direction. Animating the camera + * accordingly. + * + * @param event ignored + */ + public void moveFocusDown(final PInputEvent event) { moveFocusInDirection(SOUTH); } - public void moveFocusIn(final PInputEvent e) { + /** + * Moves the focus "into" the scene. So smaller nodes appear larger on + * screen. Animates the camera accordingly. + * + * @param event ignored + */ + public void moveFocusIn(final PInputEvent event) { moveFocusInDirection(IN); } - public void moveFocusLeft(final PInputEvent e) { + /** + * Moves the focus in the left direction. Animating the camera accordingly. + * + * @param event ignored + */ + public void moveFocusLeft(final PInputEvent event) { moveFocusInDirection(WEST); } - public void moveFocusOut(final PInputEvent e) { + /** + * Moves the focus "out" of scene. So larger nodes appear smaller on screen. + * Animates the camera accordingly. + * + * @param event ignored + */ + public void moveFocusOut(final PInputEvent event) { moveFocusInDirection(OUT); } - public void moveFocusRight(final PInputEvent e) { + /** + * Moves the focus in the right direction. Animating the camera accordingly. + * + * @param event ignored + */ + public void moveFocusRight(final PInputEvent event) { moveFocusInDirection(EAST); } - public void moveFocusUp(final PInputEvent e) { + /** + * Moves the focus in the up direction. Animating the camera accordingly. + * + * @param event ignored + */ + public void moveFocusUp(final PInputEvent event) { moveFocusInDirection(NORTH); } + /** + * Moves the focus to the nearest node in the direction specified. Animating + * the camera appropriately. + * + * @param direction one of NORTH, SOUTH, EAST, WEST, IN, OUT + */ private void moveFocusInDirection(final int direction) { final PNode n = getNeighborInDirection(direction); @@ -168,14 +229,27 @@ } } - public void moveFocusToMouseOver(final PInputEvent e) { - final PNode focus = e.getPickedNode(); + /** + * Moves the focus to the mouse under the mouse. Animating the camera + * appropriately. + * + * @param event mouse event + */ + public void moveFocusToMouseOver(final PInputEvent event) { + final PNode focus = event.getPickedNode(); if (!(focus instanceof PCamera)) { focusNode = focus; } } - public PNode getNeighborInDirection(final int aDirection) { + /** + * Returns the nearest node in the given direction. + * + * @param direction direction in which to look the nearest node + * + * @return nearest node in the given direction + */ + public PNode getNeighborInDirection(final int direction) { if (focusNode == null) { return null; } @@ -191,7 +265,7 @@ final Iterator i = l.iterator(); while (i.hasNext()) { final PNode each = (PNode) i.next(); - if (nodeIsNeighborInDirection(each, aDirection)) { + if (nodeIsNeighborInDirection(each, direction)) { return each; } } @@ -199,6 +273,12 @@ return null; } + /** + * Returns all pickable nodes that are 1 hop away from the currently focused + * node. This includes, parent, children, and siblings. + * + * @return list of nodes that are 1 hop away from the current focusNode + */ public List getNeighbors() { final ArrayList result = new ArrayList(); if (focusNode == null || focusNode.getParent() == null) { @@ -221,30 +301,39 @@ return result; } - public boolean nodeIsNeighborInDirection(final PNode aNode, final int aDirection) { - switch (aDirection) { + /** + * Returns true if the given node is a neighbor in the given direction + * relative to the current focus. + * + * @param node the node being tested + * @param direction the direction in which we're testing + * + * @return true if node is a neighbor in the direction provided + */ + public boolean nodeIsNeighborInDirection(final PNode node, final int direction) { + switch (direction) { case IN: { - return aNode.isDescendentOf(focusNode); + return node.isDescendentOf(focusNode); } case OUT: { - return aNode.isAncestorOf(focusNode); + return node.isAncestorOf(focusNode); } default: { - if (aNode.isAncestorOf(focusNode) || aNode.isDescendentOf(focusNode)) { + if (node.isAncestorOf(focusNode) || node.isDescendentOf(focusNode)) { return false; } } } final Point2D highlightCenter = (Point2D) NODE_TO_GLOBAL_NODE_CENTER_MAPPING.get(focusNode); - final Point2D nodeCenter = (Point2D) NODE_TO_GLOBAL_NODE_CENTER_MAPPING.get(aNode); + final Point2D nodeCenter = (Point2D) NODE_TO_GLOBAL_NODE_CENTER_MAPPING.get(node); final double ytest1 = nodeCenter.getX() - highlightCenter.getX() + highlightCenter.getY(); final double ytest2 = -nodeCenter.getX() + highlightCenter.getX() + highlightCenter.getY(); - switch (aDirection) { + switch (direction) { case NORTH: { return nodeCenter.getY() < highlightCenter.getY() && nodeCenter.getY() < ytest1 && nodeCenter.getY() < ytest2; @@ -263,12 +352,20 @@ return nodeCenter.getX() < highlightCenter.getX() && nodeCenter.getY() > ytest1 && nodeCenter.getY() < ytest2; } + default: + return false; } - return false; } - public void sortNodesByDistanceFromPoint(final List aNodesList, final Point2D aPoint) { - Collections.sort(aNodesList, new Comparator() { + /** + * Modifies the array so that it's sorted in ascending order based on the + * distance from the given point. + * + * @param nodes list of nodes to be sorted + * @param point point from which distance is being computed + */ + public void sortNodesByDistanceFromPoint(final List nodes, final Point2D point) { + Collections.sort(nodes, new Comparator() { public int compare(final Object o1, final Object o2) { return compare((PNode) o1, (PNode) o2); } @@ -280,7 +377,7 @@ NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(each1, center1); NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(each2, center2); - return Double.compare(aPoint.distance(center1), aPoint.distance(center2)); + return Double.compare(point.distance(center1), point.distance(center2)); } }); } @@ -290,7 +387,18 @@ // focus remains visible on the screen at 100 percent scale. // **************************************************************** - protected PActivity animateCameraViewTransformTo(final PCamera aCamera, final AffineTransform aTransform, + /** + * Animates the camera's view transform into the provided one over the + * duration provided. + * + * @param camera camera being animated + * @param targetTransform the transform to which the camera's transform will + * be animated + * @param duration the number of milliseconds the animation should last + * + * @return an activity object that represents the animation + */ + protected PActivity animateCameraViewTransformTo(final PCamera camera, final AffineTransform targetTransform, final int duration) { boolean wasOldAnimation = false; @@ -301,67 +409,82 @@ } if (duration == 0) { - aCamera.setViewTransform(aTransform); + camera.setViewTransform(targetTransform); return null; } - final AffineTransform source = aCamera.getViewTransformReference(); + final AffineTransform source = camera.getViewTransformReference(); - if (source.equals(aTransform)) { + if (source.equals(targetTransform)) { return null; } - navigationActivity = aCamera.animateViewToTransform(aTransform, duration); + navigationActivity = camera.animateViewToTransform(targetTransform, duration); navigationActivity.setSlowInSlowOut(!wasOldAnimation); return navigationActivity; } - public PActivity directCameraViewToFocus(final PCamera aCamera, final PNode aFocusNode, final int duration) { - focusNode = aFocusNode; - final AffineTransform originalViewTransform = aCamera.getViewTransform(); + /** + * Animates the Camera's view so that it contains the new focus node. + * + * @param camera The camera to be animated + * @param newFocus the node that will gain focus + * @param duration number of milliseconds that animation should last for + * + * @return an activity object representing the scheduled animation + */ + public PActivity directCameraViewToFocus(final PCamera camera, final PNode newFocus, final int duration) { + focusNode = newFocus; + final AffineTransform originalViewTransform = camera.getViewTransform(); // Scale the canvas to include final PDimension d = new PDimension(1, 0); focusNode.globalToLocal(d); - final double scaleFactor = d.getWidth() / aCamera.getViewScale(); + final double scaleFactor = d.getWidth() / camera.getViewScale(); final Point2D scalePoint = focusNode.getGlobalFullBounds().getCenter2D(); if (Math.abs(1f - scaleFactor) < 0.0001) { - aCamera.scaleViewAboutPoint(scaleFactor, scalePoint.getX(), scalePoint.getY()); + camera.scaleViewAboutPoint(scaleFactor, scalePoint.getX(), scalePoint.getY()); } // Pan the canvas to include the view bounds with minimal canvas // movement. - aCamera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); + camera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); // Get rid of any white space. The canvas may be panned and // zoomed in to do this. But make sure not stay constrained by max // magnification. // fillViewWhiteSpace(aCamera); - final AffineTransform resultingTransform = aCamera.getViewTransform(); - aCamera.setViewTransform(originalViewTransform); + final AffineTransform resultingTransform = camera.getViewTransform(); + camera.setViewTransform(originalViewTransform); // Animate the canvas so that it ends up with the given // view transform. - return animateCameraViewTransformTo(aCamera, resultingTransform, duration); + return animateCameraViewTransformTo(camera, resultingTransform, duration); } - protected void fillViewWhiteSpace(final PCamera aCamera) { - final PBounds rootBounds = aCamera.getRoot().getFullBoundsReference(); - PBounds viewBounds = aCamera.getViewBounds(); + /** + * Instantaneously transforms the provided camera so that it does not + * contain any extra white space. + * + * @param camera the camera to be transformed + */ + protected void fillViewWhiteSpace(final PCamera camera) { + final PBounds rootBounds = camera.getRoot().getFullBoundsReference(); + PBounds viewBounds = camera.getViewBounds(); - if (rootBounds.contains(aCamera.getViewBounds())) { + if (rootBounds.contains(camera.getViewBounds())) { return; } - aCamera.animateViewToPanToBounds(rootBounds, 0); - aCamera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); + camera.animateViewToPanToBounds(rootBounds, 0); + camera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); // center content. double dx = 0; double dy = 0; - viewBounds = aCamera.getViewBounds(); + viewBounds = camera.getViewBounds(); if (viewBounds.getWidth() > rootBounds.getWidth()) { // then center along x axis. @@ -373,6 +496,6 @@ dy = rootBounds.getCenterX() - viewBounds.getCenterX(); } - aCamera.translateView(dx, dy); + camera.translateView(dx, dy); } } diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PNotificationCenter.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PNotificationCenter.java index d639681..26e9185 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PNotificationCenter.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/event/PNotificationCenter.java @@ -70,6 +70,11 @@ protected HashMap listenersMap; protected ReferenceQueue keyQueue; + /** + * Singleton accessor for the PNotificationCenter. + * + * @return singleton instance of PNotificationCenter + */ public static PNotificationCenter defaultCenter() { if (DEFAULT_CENTER == null) { DEFAULT_CENTER = new PNotificationCenter(); @@ -95,44 +100,33 @@ * matching 'object'. If 'object' is null the listener will receive all * notifications with the name 'notificationName'. * - * @return whether or not the listener has been added - * @throws SecurityException + * @param listener object to be notified of notifications + * @param callbackMethodName method to be invoked on the listener + * @param notificationName name of notifications to filter on + * @param object source of notification messages that this listener is + * interested in + * @return true if listener has been added + * @throws SecurityException when attempting to register method as listener + * that is not accessible */ public boolean addListener(final Object listener, final String callbackMethodName, final String notificationName, - Object object) throws SecurityException { + final Object object) throws SecurityException { processKeyQueue(); - Object name = notificationName; - Method method = null; + Object name = nullify(notificationName); + Object sanitizedObject = nullify(object); - try { - method = listener.getClass().getMethod(callbackMethodName, new Class[] { PNotification.class }); - } - catch (final NoSuchMethodException e) { + Method method = extractCallbackMethod(listener, callbackMethodName); + if (method == null) return false; - } - final int modifiers = method.getModifiers(); - - if (!Modifier.isPublic(modifiers)) { - return false; - } - - if (name == null) { - name = NULL_MARKER; - } - - if (object == null) { - object = NULL_MARKER; - } - - final Object key = new NotificationKey(name, object); - final Object notificationTarget = new NotificationTarget(listener, method); + final NotificationKey key = new NotificationKey(name, sanitizedObject); + final NotificationTarget notificationTarget = new NotificationTarget(listener, method); List list = (List) listenersMap.get(key); if (list == null) { list = new ArrayList(); - listenersMap.put(new NotificationKey(name, object, keyQueue), list); + listenersMap.put(new NotificationKey(name, sanitizedObject, keyQueue), list); } if (!list.contains(notificationTarget)) { @@ -142,13 +136,40 @@ return true; } + private Method extractCallbackMethod(final Object listener, final String methodName) { + Method method = null; + try { + method = listener.getClass().getMethod(methodName, new Class[] { PNotification.class }); + } + catch (final NoSuchMethodException e) { + return null; + } + + final int modifiers = method.getModifiers(); + if (!Modifier.isPublic(modifiers)) { + return null; + } + + return method; + } + + private Object nullify(final Object object) { + if (object == null) { + return NULL_MARKER; + } + + return object; + } + // **************************************************************** // Remove Listener Methods // **************************************************************** /** - * Removes the listener so that it no longer recives notfications from this - * notfication center. + * Removes the listener so that it no longer receives notfications from this + * notification center. + * + * @param listener listener to be removed from this notification center */ public void removeListener(final Object listener) { processKeyQueue(); @@ -160,12 +181,21 @@ } /** - * Removes the listeners as the listener of notifications matching - * notificationName and object. If listener is null all listeners matching - * notificationName and object are removed. If notificationName is null the - * listener will be removed from all notifications containing the object. If - * the object is null then the listener will be removed from all + * Unregisters the listener as a listener for the specified kind of + * notification. + * + * If listener is null all listeners matching notificationName and object + * are removed. + * + * If notificationName is null the listener will be removed from all + * notifications containing the object. + * + * If the object is null then the listener will be removed from all * notifications matching notficationName. + * + * @param listener listener to be removed + * @param notificationName name of notifications or null for all + * @param object notification source or null for all */ public void removeListener(final Object listener, final String notificationName, final Object object) { processKeyQueue(); @@ -182,8 +212,11 @@ // **************************************************************** /** - * Post a new notfication with notificationName and object. The object is + * Post a new notification with notificationName and object. The object is * typically the object posting the notification. The object may be null. + * + * @param notificationName name of notification to post + * @param object source of the notification, null signifies unknown */ public void postNotification(final String notificationName, final Object object) { postNotification(notificationName, object, null); @@ -192,89 +225,98 @@ /** * Creates a notification with the name notificationName, associates it with * the object, and posts it to this notification center. The object is - * typically the object posting the notification. It may be nil. + * typically the object posting the notification. It may be null. + * + * @param notificationName name of notification being posted + * @param object source of the notification, may be null + * @param properties properties associated with the notification */ - public void postNotification(final String notificationName, final Object object, final Map userInfo) { - postNotification(new PNotification(notificationName, object, userInfo)); + public void postNotification(final String notificationName, final Object object, final Map properties) { + postNotification(new PNotification(notificationName, object, properties)); } /** * Post the notification to this notification center. Most often clients * will instead use one of this classes convenience postNotifcations * methods. + * + * @param notification notification to be dispatched to appropriate + * listeners */ - public void postNotification(final PNotification aNotification) { + public void postNotification(final PNotification notification) { final List mergedListeners = new LinkedList(); - List listenersList; - final Object name = aNotification.getName(); - final Object object = aNotification.getObject(); + final Object name = notification.getName(); + final Object object = notification.getObject(); - if (name != null) { - if (object == null) {// object is null - listenersList = (List) listenersMap.get(new NotificationKey(name, NULL_MARKER)); - if (listenersList != null) { - mergedListeners.addAll(listenersList); - } - } - else { // both are specified - listenersList = (List) listenersMap.get(new NotificationKey(name, object)); - if (listenersList != null) { - mergedListeners.addAll(listenersList); - } - listenersList = (List) listenersMap.get(new NotificationKey(name, NULL_MARKER)); - if (listenersList != null) { - mergedListeners.addAll(listenersList); - } - listenersList = (List) listenersMap.get(new NotificationKey(NULL_MARKER, object)); - if (listenersList != null) { - mergedListeners.addAll(listenersList); - } - } + if (name != null && object != null) { + fillWithMatchingListeners(name, object, mergedListeners); + fillWithMatchingListeners(null, object, mergedListeners); + fillWithMatchingListeners(name, null, mergedListeners); } - else if (object != null) { // name is null - listenersList = (List) listenersMap.get(new NotificationKey(NULL_MARKER, object)); - if (listenersList != null) { - mergedListeners.addAll(listenersList); - } + else if (name != null) { + fillWithMatchingListeners(name, null, mergedListeners); + } + else if (object != null) { + fillWithMatchingListeners(null, object, mergedListeners); } - final Object key = new NotificationKey(NULL_MARKER, NULL_MARKER); - listenersList = (List) listenersMap.get(key); - if (listenersList != null) { - mergedListeners.addAll(listenersList); - } + fillWithMatchingListeners(null, null, mergedListeners); - dispatchNotifications(aNotification, mergedListeners); + dispatchNotifications(notification, mergedListeners); } - private void dispatchNotifications(final PNotification aNotification, final List listeners) { - NotificationTarget listener; - final Iterator it = listeners.iterator(); + /** + * Adds all listeners that are registered to receive notifications to the + * end of the list provided. + * + * @param notificationName name of the notification being emitted + * @param object source of the notification + * @param listeners list to append listeners to + */ + private void fillWithMatchingListeners(final Object notificationName, final Object object, final List listeners) { + final Object key = new NotificationKey(nullify(notificationName), nullify(object)); + final List globalListeners = (List) listenersMap.get(key); + if (globalListeners != null) { + listeners.addAll(globalListeners); + } + } - while (it.hasNext()) { - listener = (NotificationTarget) it.next(); + private void dispatchNotifications(final PNotification notification, final List listeners) { + NotificationTarget listener; + final Iterator listenerIterator = listeners.iterator(); + + while (listenerIterator.hasNext()) { + listener = (NotificationTarget) listenerIterator.next(); if (listener.get() == null) { - it.remove(); + listenerIterator.remove(); } else { - try { - listener.getMethod().invoke(listener.get(), new Object[] { aNotification }); - } - catch (final IllegalAccessException e) { - throw new RuntimeException("Impossible Situation: invoking inaccessible method on listener", e); - } - catch (final InvocationTargetException e) { - throw new RuntimeException(e); - } + notifyListener(notification, listener); } } } - // **************************************************************** - // Implementation classes and methods - // **************************************************************** + private void notifyListener(final PNotification notification, NotificationTarget listener) { + try { + listener.getMethod().invoke(listener.get(), new Object[] { notification }); + } + catch (final IllegalAccessException e) { + throw new RuntimeException("Impossible Situation: invoking inaccessible method on listener", e); + } + catch (final InvocationTargetException e) { + throw new RuntimeException(e); + } + } + /** + * Returns a list of keys with the given name and object. + * + * @param name name of key + * @param object key associated with the object + * + * @return list of matching keys + */ protected List matchingKeys(final String name, final Object object) { final List result = new LinkedList(); @@ -290,6 +332,13 @@ return result; } + /** + * Removes the given listener from receiving notifications with the given + * key. + * + * @param listener the listener being unregistered + * @param key the key that identifies the listener + */ protected void removeListener(final Object listener, final Object key) { if (listener == null) { listenersMap.remove(key); @@ -322,7 +371,6 @@ } protected static class NotificationKey extends WeakReference { - private final Object name; private final int hashCode; @@ -378,18 +426,34 @@ public NotificationTarget(final Object object, final Method method) { super(object); - hashCode = object.hashCode(); + hashCode = object.hashCode() + method.hashCode(); this.method = method; } + /** + * Returns the method that will be invoked on the listener object. + * + * @return method to be invoked with notification is to be dispatched + */ public Method getMethod() { return method; } + /** + * Returns hash code for this notification target. + * + * @return hash code + */ public int hashCode() { return hashCode; } + /** + * Returns true if this object is logically equivalent to the one passed + * in. For this to happen they must have the same method and object. + * + * @return true if logically equivalent + */ public boolean equals(final Object object) { if (this == object) { return true; @@ -409,6 +473,12 @@ return o != null && o == target.get(); } + /** + * Returns a string representation of this NotificationTarget for + * debugging purposes. + * + * @return string representation + */ public String toString() { return "[CompoundValue:" + get() + ":" + getMethod().getName() + "]"; } diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PSelectionEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PSelectionEventHandler.java index faeaa1e..32fb907 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PSelectionEventHandler.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/event/PSelectionEventHandler.java @@ -91,7 +91,7 @@ /** Used within drag handler temporarily. */ private HashMap allItems = null; - /** Used within drag handler temporarily */ + /** Used within drag handler temporarily. */ private ArrayList unselectList = null; private HashMap marqueeMap = null; @@ -136,8 +136,14 @@ init(); } + /** + * Initializes the PSelectionEventHandler with a marquee stroke. + */ protected void init() { - final float[] dash = { DASH_WIDTH, DASH_WIDTH }; + final float[] dash = new float[2]; + dash[0] = DASH_WIDTH; + dash[1] = DASH_WIDTH; + strokes = new Stroke[NUM_STROKES]; for (int i = 0; i < NUM_STROKES; i++) { strokes[i] = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, dash, i); @@ -289,12 +295,7 @@ * @return true if succeeded */ public boolean isSelected(final PNode node) { - if (node != null && selection.containsKey(node)) { - return true; - } - else { - return false; - } + return (node != null && selection.containsKey(node)); } /** @@ -303,8 +304,7 @@ * @return copy of selection */ public Collection getSelection() { - final ArrayList sel = new ArrayList(selection.keySet()); - return sel; + return new ArrayList(selection.keySet()); } /** @@ -402,6 +402,12 @@ // The overridden methods from PDragSequenceEventHandler // ////////////////////////////////////////////////////// + /** + * Overrides method in PDragSequenceEventHandler so that, selections have + * marquees. + * + * @param e the event that started the drag + */ protected void startDrag(final PInputEvent e) { super.startDrag(e); @@ -427,32 +433,42 @@ } } - protected void drag(final PInputEvent e) { - super.drag(e); + /** + * Updates the marquee to the new bounds caused by the drag. + * + * @param event drag event + */ + protected void drag(final PInputEvent event) { + super.drag(event); - if (isMarqueeSelection(e)) { - updateMarquee(e); + if (isMarqueeSelection(event)) { + updateMarquee(event); - if (!isOptionSelection(e)) { - computeMarqueeSelection(e); + if (!isOptionSelection(event)) { + computeMarqueeSelection(event); } else { - computeOptionMarqueeSelection(e); + computeOptionMarqueeSelection(event); } } else { - dragStandardSelection(e); + dragStandardSelection(event); } } - protected void endDrag(final PInputEvent e) { - super.endDrag(e); + /** + * Ends the selection marquee when the drag is ended. + * + * @param event the event responsible for ending the drag + */ + protected void endDrag(final PInputEvent event) { + super.endDrag(event); - if (isMarqueeSelection(e)) { - endMarqueeSelection(e); + if (isMarqueeSelection(event)) { + endMarqueeSelection(event); } else { - endStandardSelection(e); + endStandardSelection(event); } } @@ -512,19 +528,32 @@ unselectAll(); } + /** + * If the pressed node is not selected unselect all nodes and select the + * pressed node if it allows it. + * + * @param pie event that started the selection + */ protected void startStandardSelection(final PInputEvent pie) { // Option indicator not down - clear selection, and start fresh - if (!isSelected(pressNode)) { - unselectAll(); + if (isSelected(pressNode)) { + return; + } - if (isSelectable(pressNode)) { - select(pressNode); - } + unselectAll(); + + if (isSelectable(pressNode)) { + select(pressNode); } } + /** + * Toggle the current selection on the node that was just pressed, but leave + * the rest of the selected nodes unchanged. + * + * @param pie event responsible for the change in selection + */ protected void startStandardOptionSelection(final PInputEvent pie) { - // Option indicator is down, toggle selection if (isSelectable(pressNode)) { if (isSelected(pressNode)) { unselect(pressNode); @@ -535,6 +564,11 @@ } } + /** + * Updates the marquee rectangle as the result of a drag. + * + * @param pie event responsible for the change in the marquee + */ protected void updateMarquee(final PInputEvent pie) { final PBounds b = new PBounds(); @@ -577,6 +611,11 @@ } } + /** + * Sets the selection to be all nodes under the marquee. + * + * @param pie event responsible for the new selection + */ protected void computeMarqueeSelection(final PInputEvent pie) { unselectList.clear(); // Make just the items in the list selected @@ -605,6 +644,11 @@ select(allItems); } + /** + * Extends the selection to include all nodes under the marquee. + * + * @param pie event responsible for the change in selection + */ protected void computeOptionMarqueeSelection(final PInputEvent pie) { unselectList.clear(); Iterator selectionEn = selection.keySet().iterator(); @@ -632,10 +676,23 @@ select(allItems); } + /** + * Creates a node filter that will filter all nodes not touching the bounds + * provided. + * + * @param bounds will be used to filter matches + * + * @return newly created filter + */ protected PNodeFilter createNodeFilter(final PBounds bounds) { return new BoundsFilter(bounds); } + /** + * Returns the bounds of the current selection marquee. + * + * @return bounds of current selection marquee + */ protected PBounds getMarqueeBounds() { if (marquee != null) { return marquee.getBounds(); @@ -643,6 +700,11 @@ return new PBounds(); } + /** + * Drag selected nodes. + * + * @param e event responsible for the drag + */ protected void dragStandardSelection(final PInputEvent e) { // There was a press node, so drag selection final PDimension d = e.getCanvasDelta(); @@ -659,6 +721,11 @@ } } + /** + * Removes marquee and clears selection. + * + * @param e event responsible for the end of the selection + */ protected void endMarqueeSelection(final PInputEvent e) { // Remove marquee allItems.clear(); @@ -667,6 +734,11 @@ marquee = null; } + /** + * Ends the "pressed" state of the previously pressed node (if any) + * + * @param e event responsible for the end in the selection + */ protected void endStandardSelection(final PInputEvent e) { pressNode = null; } @@ -674,6 +746,8 @@ /** * This gets called continuously during the drag, and is used to animate the * marquee + * + * @param aEvent event responsible for this step in the drag sequence */ protected void dragActivityStep(final PInputEvent aEvent) { if (marquee != null) { @@ -689,6 +763,8 @@ /** * Delete selection when delete key is pressed (if enabled) + * + * @param e the key press event */ public void keyPressed(final PInputEvent e) { switch (e.getKeyCode()) { @@ -704,33 +780,54 @@ } } + /** + * Returns whether the delete key is a supported action. + * + * @return true if delete is allowed + */ public boolean getSupportDeleteKey() { return deleteKeyActive; } + /** + * Returns whether the delete key is a supported action. + * + * @return true if delete is allowed + */ public boolean isDeleteKeyActive() { return deleteKeyActive; } /** * Specifies if the DELETE key should delete the selection + * + * @param deleteKeyActive state to set for the delete action true = enabled */ public void setDeleteKeyActive(final boolean deleteKeyActive) { this.deleteKeyActive = deleteKeyActive; } - // //////////////////// - // Inner classes - // //////////////////// - + /** + * Class used to filter nodes that intersect with the marquee's bounds. + */ protected class BoundsFilter implements PNodeFilter { - PBounds localBounds = new PBounds(); - PBounds bounds; + private final PBounds localBounds = new PBounds(); + private final PBounds bounds; + /** + * Constructs a BoundsFilter for the given bounds. + * + * @param bounds bounds to be used when testing nodes for intersection + */ protected BoundsFilter(final PBounds bounds) { this.bounds = bounds; } + /** + * Returns true if the node intersects with this Filter's configured bounds. + * + * @param node node being tested + */ public boolean accept(final PNode node) { localBounds.setRect(bounds); node.globalToLocal(localBounds); @@ -741,10 +838,19 @@ && !isCameraLayer(node); } + /** + * Returns whether this filter should accept all children of a node. + */ public boolean acceptChildrenOf(final PNode node) { return selectableParents.contains(node) || isCameraLayer(node); } + /** + * Tests a node to see if it's a layer that has an attached camera. + * + * @param node node being tested + * @return true if node is a layer with a camera attached + */ public boolean isCameraLayer(final PNode node) { if (node instanceof PLayer) { for (final Iterator i = selectableParents.iterator(); i.hasNext();) { diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java index 8c56920..a58729c 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java @@ -57,8 +57,8 @@ * @author Sam Reid */ public class PSwingEventHandler implements PInputEventListener { - - private PNode listenNode = null; // used to listen to for events + /** Used to listen for events */ + private PNode listenNode = null; /** Tracks whether this event handler is active. */ private boolean active = false; diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/XYArray.java b/extras/src/main/java/edu/umd/cs/piccolox/util/XYArray.java index b1e7f57..8c55b2e 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/XYArray.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/util/XYArray.java @@ -33,31 +33,52 @@ import java.awt.geom.Rectangle2D; public class XYArray implements MutablePoints, Cloneable { - // the coordinates are stored as alternating x and y pairs + /** The coordinates of the points, specifically 2x the number of points. */ private double[] points = null; - // the number of valid x, y pairs, - // i.e. not the length of the points array - + /** the number of valid x, y pairs */ private int numPoints = 0; + /** + * Constructs an XYArray wrapping the given points. + * + * @param points array of coordinates defining the points + */ public XYArray(final double[] points) { initPoints(points, points.length / 2); } + /** + * Constructs an XYArray of the given size. + * + * @param n numbe rof points XYArray should contain + */ public XYArray(final int n) { initPoints(null, n); } + /** + * Constructs an empty XYArray. + */ public XYArray() { this(0); } + /** + * Returns the number of points this XYArray represents. + * + * @return number of points + */ public int getPointCount() { return numPoints; } - // normalize an index, negative counts from end + /** + * Converts negative indexes to positive ones by adding numPoints to it. + * + * @param i index to be normalized + * @return normalized index + */ private int normalize(final int i) { if (i >= numPoints) { @@ -67,42 +88,98 @@ return i < 0 ? numPoints + i : i; } + /** + * Returns the x component of the point at the given index. + * + * @param i index of point + * @return x component of point at given index + */ public double getX(final int i) { return points[normalize(i) * 2]; } + /** + * Returns the y component of the point at the given index. + * + * @param i index of point + * @return y component of point at given index + */ public double getY(final int i) { return points[normalize(i) * 2 + 1]; } + /** + * Returns modified point representing the wrapped point at the given index. + * + * @param i index of desired point + * @param dst point to be modified + * @return dst + */ public Point2D getPoint(final int i, final Point2D dst) { final int pointIndex = normalize(i); dst.setLocation(points[pointIndex * 2], points[pointIndex * 2 + 1]); return dst; } + /** + * Sets the x component of the point at the given index. + * + * @param i index of point to modify + * @param x new x component + */ public void setX(final int i, final double x) { points[normalize(i) * 2] = x; } + /** + * Sets the y component of the point at the given index. + * + * @param i index of point to modify + * @param y new y component + */ public void setY(final int i, final double y) { points[normalize(i) * 2 + 1] = y; } + /** + * Sets the coordinates of the point at the given index. + * + * @param i index of point to modify + * @param x new x component + * @param y new y component + */ public void setPoint(final int i, final double x, final double y) { final int pointIndex = normalize(i); points[pointIndex * 2] = x; points[pointIndex * 2 + 1] = y; } + /** + * Sets the coordinates of the point at the given index. + * + * @param i index of point to modify + * @param pt point from which coordinate is to be extracted + */ public void setPoint(final int i, final Point2D pt) { setPoint(i, pt.getX(), pt.getY()); } + /** + * Applies the given transform to all points represented by this XYArray. + * + * @param t transform to apply + */ public void transformPoints(final AffineTransform t) { t.transform(points, 0, points, 0, numPoints); } + /** + * Modifies dst to be the bounding box of the points represented by this + * XYArray. + * + * @param dst rectangle to be modified + * @return the bounding rectangle + */ public Rectangle2D getBounds(final Rectangle2D dst) { int i = 0; if (dst.isEmpty() && getPointCount() > 0) { @@ -116,6 +193,17 @@ return dst; } + /** + * Constructs an array of point coordinates for n points and copies the old + * values if provided. + * + * @param points array to populate with point values, or null to generate a + * new array + * @param n number of points + * @param old old values to repopulate the array with, or null if not + * desired + * @return initialized points + */ public static double[] initPoints(double[] points, final int n, final double[] old) { if (points == null || n * 2 > points.length) { points = new double[n * 2]; @@ -126,54 +214,105 @@ return points; } + /** + * Constructs an array of point coordinates for n points. + * + * @param points array to populate with point values, or null to generate a + * new array + * @param n number of points + */ private void initPoints(final double[] points, final int n) { this.points = initPoints(points, n, this.points); numPoints = points != null ? points.length / 2 : 0; } - public void addPoints(final int pos, final Points pts, int start, int end) { + /** + * Adds a subsequence of the points provided at the given position. + * + * @param index position at which the points should be inserted + * @param newPoints points from which to extract the subsequence of points + * @param start the start index within newPoints to start extracting points + * @param end the end index within newPoints to finish extracting points + */ + public void addPoints(final int index, final Points newPoints, int start, int end) { if (end < 0) { - end = pts.getPointCount() + end + 1; + end = newPoints.getPointCount() + end + 1; } final int n = numPoints + end - start; points = initPoints(points, n, points); - final int pos1 = pos * 2; - final int pos2 = (pos + end - start) * 2; - final int len = (numPoints - pos) * 2; + final int pos1 = index * 2; + final int pos2 = (index + end - start) * 2; + final int len = (numPoints - index) * 2; System.arraycopy(points, pos1, points, pos2, len); numPoints = n; - if (pts != null) { + if (newPoints != null) { for (int count = 0; start < end; count++, start++) { - setPoint(pos + count, pts.getX(start), pts.getY(start)); + setPoint(index + count, newPoints.getX(start), newPoints.getY(start)); } } } + /** + * Inserts all the provided points at the given position. + * + * @param pos index at which to insert the points + * @param pts points to be inserted + */ public void addPoints(final int pos, final Points pts) { addPoints(pos, pts, 0, pts.getPointCount()); } + /** + * Adds the provided points to the end of the points. + * + * @param pts points to be added + */ public void appendPoints(final Points pts) { addPoints(numPoints, pts); } + /** + * Creates an XYArray representing the given points. + * + * @param pts points to copy + * @return XYArray representing the points provided + */ public static XYArray copyPoints(final Points pts) { final XYArray newList = new XYArray(pts.getPointCount()); newList.appendPoints(pts); return newList; } + /** + * Adds a point to the index provided. + * + * @param pos index at which to add the point + * @param x x coordinate of new point + * @param y y coordinate of new point + */ public void addPoint(final int pos, final double x, final double y) { addPoints(pos, null, 0, 1); setPoint(pos, x, y); } + /** + * Inserts the given point at the given index. + * + * @param pos index at which to add the point + * @param pt point to be inserted * + */ public void addPoint(final int pos, final Point2D pt) { addPoint(pos, pt.getX(), pt.getY()); } + /** + * Remove a subsequence of points from this XYArray starting as pos. + * + * @param pos the position to start removing points + * @param num the number of points to remove + */ public void removePoints(final int pos, int num) { num = Math.min(num, numPoints - pos); if (num <= 0) { @@ -183,10 +322,19 @@ numPoints -= num; } + /** + * Remove all points from this XYArray. + */ public void removeAllPoints() { removePoints(0, numPoints); } + /** + * Returns a clone of this XYArray ensuring a deep copy of coordinates is + * made. + * + * @return cloned XYArray + */ public Object clone() { XYArray ps = null;