diff --git a/core/src/main/java/edu/umd/cs/piccolo/PCamera.java b/core/src/main/java/edu/umd/cs/piccolo/PCamera.java index af1def3..eb63f44 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/PCamera.java +++ b/core/src/main/java/edu/umd/cs/piccolo/PCamera.java @@ -111,7 +111,9 @@ /** Denotes that the view has no constraints. */ public static final int VIEW_CONSTRAINT_NONE = 0; + /** Enforces that the view be able to see all nodes in the scene. */ public static final int VIEW_CONSTRAINT_ALL = 1; + /** Constrains the the view to be centered on the scene's full bounds. */ public static final int VIEW_CONSTRAINT_CENTER = 2; /** diff --git a/core/src/main/java/edu/umd/cs/piccolo/PCanvas.java b/core/src/main/java/edu/umd/cs/piccolo/PCanvas.java index 34f5706..629be5b 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/PCanvas.java +++ b/core/src/main/java/edu/umd/cs/piccolo/PCanvas.java @@ -130,10 +130,10 @@ private int interactingRenderQuality; /** The one and only pan handler. */ - private PPanEventHandler panEventHandler; + private transient PPanEventHandler panEventHandler; /** The one and only ZoomEventHandler. */ - private PZoomEventHandler zoomEventHandler; + private transient PZoomEventHandler zoomEventHandler; private boolean paintingImmediately; @@ -388,11 +388,11 @@ * when it is not interacting or animating. The default value is * PPaintContext. HIGH_QUALITY_RENDERING. * - * @param normalRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING + * @param defaultRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING * or PPaintContext.LOW_QUALITY_RENDERING */ - public void setDefaultRenderQuality(final int normalRenderQuality) { - this.normalRenderQuality = normalRenderQuality; + public void setDefaultRenderQuality(final int defaultRenderQuality) { + this.normalRenderQuality = defaultRenderQuality; repaint(); } @@ -490,209 +490,22 @@ */ protected void installInputSources() { if (mouseListener == null) { - mouseListener = new MouseListener() { - /** {@inheritDoc} */ - public void mouseClicked(final MouseEvent e) { - sendInputEventToInputManager(e, MouseEvent.MOUSE_CLICKED); - } - - /** {@inheritDoc} */ - public void mouseEntered(final MouseEvent e) { - MouseEvent simulated = null; - - if (isAnyButtonDown(e)) { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); - } - else { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); - } - - sendInputEventToInputManager(e, MouseEvent.MOUSE_ENTERED); - sendInputEventToInputManager(simulated, simulated.getID()); - } - - /** {@inheritDoc} */ - public void mouseExited(final MouseEvent e) { - MouseEvent simulated = null; - - if (isAnyButtonDown(e)) { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); - } - else { - simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); - } - - sendInputEventToInputManager(simulated, simulated.getID()); - sendInputEventToInputManager(e, MouseEvent.MOUSE_EXITED); - } - - /** {@inheritDoc} */ - public void mousePressed(final MouseEvent rawEvent) { - requestFocus(); - - boolean shouldBalanceEvent = false; - - final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_PRESSED); - - switch (event.getButton()) { - case MouseEvent.BUTTON1: - if (isButton1Pressed) { - shouldBalanceEvent = true; - } - isButton1Pressed = true; - break; - - case MouseEvent.BUTTON2: - if (isButton2Pressed) { - shouldBalanceEvent = true; - } - isButton2Pressed = true; - break; - - case MouseEvent.BUTTON3: - if (isButton3Pressed) { - shouldBalanceEvent = true; - } - isButton3Pressed = true; - break; - default: - throw new RuntimeException("mousePressed without buttons specified"); - - } - - if (shouldBalanceEvent) { - sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_RELEASED); - } - - sendInputEventToInputManager(event, MouseEvent.MOUSE_PRESSED); - } - - /** {@inheritDoc} */ - public void mouseReleased(final MouseEvent rawEvent) { - boolean shouldBalanceEvent = false; - - final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_RELEASED); - - switch (event.getButton()) { - case MouseEvent.BUTTON1: - if (!isButton1Pressed) { - shouldBalanceEvent = true; - } - isButton1Pressed = false; - break; - - case MouseEvent.BUTTON2: - if (!isButton2Pressed) { - shouldBalanceEvent = true; - } - isButton2Pressed = false; - break; - - case MouseEvent.BUTTON3: - if (!isButton3Pressed) { - shouldBalanceEvent = true; - } - isButton3Pressed = false; - break; - default: - throw new RuntimeException("mouseReleased without buttons specified"); - } - - if (shouldBalanceEvent) { - sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_PRESSED); - } - - sendInputEventToInputManager(event, MouseEvent.MOUSE_RELEASED); - } - - private boolean isAnyButtonDown(final MouseEvent e) { - return (e.getModifiersEx() & (InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK)) != 0; - } - - private MouseEvent copyButtonsFromModifiers(final MouseEvent rawEvent, final int eventType) { - if (rawEvent.getButton() != MouseEvent.NOBUTTON) { - return rawEvent; - } - - int newButton = 0; - - if (hasButtonModifier(rawEvent, InputEvent.BUTTON1_MASK)) { - newButton = MouseEvent.BUTTON1; - } - else if (hasButtonModifier(rawEvent, InputEvent.BUTTON2_MASK)) { - newButton = MouseEvent.BUTTON2; - } - else if (hasButtonModifier(rawEvent, InputEvent.BUTTON3_MASK)) { - newButton = MouseEvent.BUTTON3; - } - - return buildModifiedMouseEvent(rawEvent, eventType, newButton); - } - - private boolean hasButtonModifier(final MouseEvent event, final int buttonMask) { - return (event.getModifiers() & buttonMask) == buttonMask; - } - - public MouseEvent buildRetypedMouseEvent(final MouseEvent e, final int newType) { - return buildModifiedMouseEvent(e, newType, e.getButton()); - } - - public MouseEvent buildModifiedMouseEvent(final MouseEvent e, final int newType, final int newButton) { - return new MouseEvent((Component) e.getSource(), newType, e.getWhen(), e.getModifiers(), e.getX(), - e.getY(), e.getClickCount(), e.isPopupTrigger(), newButton); - } - - private void sendRetypedMouseEventToInputManager(final MouseEvent e, final int newType) { - final MouseEvent retypedEvent = buildRetypedMouseEvent(e, newType); - sendInputEventToInputManager(retypedEvent, newType); - } - }; + mouseListener = new MouseEventInputSource(); addMouseListener(mouseListener); } if (mouseMotionListener == null) { - mouseMotionListener = new MouseMotionListener() { - /** {@inheritDoc} */ - public void mouseDragged(final MouseEvent e) { - sendInputEventToInputManager(e, MouseEvent.MOUSE_DRAGGED); - } - - /** {@inheritDoc} */ - public void mouseMoved(final MouseEvent e) { - sendInputEventToInputManager(e, MouseEvent.MOUSE_MOVED); - } - }; + mouseMotionListener = new MouseMotionInputSourceListener(); addMouseMotionListener(mouseMotionListener); } if (mouseWheelListener == null) { - mouseWheelListener = new MouseWheelListener() { - /** {@inheritDoc} */ - public void mouseWheelMoved(final MouseWheelEvent e) { - sendInputEventToInputManager(e, e.getScrollType()); - if (!e.isConsumed() && getParent() != null) { - getParent().dispatchEvent(e); - } - } - }; + mouseWheelListener = new MouseWheelInputSourceListener(); addMouseWheelListener(mouseWheelListener); } if (keyEventPostProcessor == null) { - keyEventPostProcessor = new KeyEventPostProcessor() { - /** {@inheritDoc} */ - public boolean postProcessKeyEvent(final KeyEvent keyEvent) { - Component owner = FocusManager.getCurrentManager().getFocusOwner(); - while (owner != null) { - if (owner == PCanvas.this) { - sendInputEventToInputManager(keyEvent, keyEvent.getID()); - return true; - } - owner = owner.getParent(); - } - return false; - } - }; + keyEventPostProcessor = new KeyEventInputSourceListener(); KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(keyEventPostProcessor); } } @@ -885,7 +698,7 @@ getCamera().setBounds(layerBounds); final double clipRatio = clippingRect.getWidth() / clippingRect.getHeight(); - final double nodeRatio = ((double)getWidth()) / ((double)getHeight()); + final double nodeRatio = ((double) getWidth()) / ((double) getHeight()); final double scale; if (nodeRatio <= clipRatio) { scale = clippingRect.getHeight() / getCamera().getHeight(); @@ -902,4 +715,203 @@ getCamera().setBounds(originalCameraBounds); } + + private final class MouseMotionInputSourceListener implements MouseMotionListener { + /** {@inheritDoc} */ + public void mouseDragged(final MouseEvent e) { + sendInputEventToInputManager(e, MouseEvent.MOUSE_DRAGGED); + } + + /** {@inheritDoc} */ + public void mouseMoved(final MouseEvent e) { + sendInputEventToInputManager(e, MouseEvent.MOUSE_MOVED); + } + } + + private final class MouseEventInputSource implements MouseListener { + /** {@inheritDoc} */ + public void mouseClicked(final MouseEvent e) { + sendInputEventToInputManager(e, MouseEvent.MOUSE_CLICKED); + } + + /** {@inheritDoc} */ + public void mouseEntered(final MouseEvent e) { + MouseEvent simulated = null; + + if (isAnyButtonDown(e)) { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); + } + else { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); + } + + sendInputEventToInputManager(e, MouseEvent.MOUSE_ENTERED); + sendInputEventToInputManager(simulated, simulated.getID()); + } + + /** {@inheritDoc} */ + public void mouseExited(final MouseEvent e) { + MouseEvent simulated = null; + + if (isAnyButtonDown(e)) { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_DRAGGED); + } + else { + simulated = buildRetypedMouseEvent(e, MouseEvent.MOUSE_MOVED); + } + + sendInputEventToInputManager(simulated, simulated.getID()); + sendInputEventToInputManager(e, MouseEvent.MOUSE_EXITED); + } + + /** {@inheritDoc} */ + public void mousePressed(final MouseEvent rawEvent) { + requestFocus(); + + boolean shouldBalanceEvent = false; + + final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_PRESSED); + + switch (event.getButton()) { + case MouseEvent.BUTTON1: + if (isButton1Pressed) { + shouldBalanceEvent = true; + } + isButton1Pressed = true; + break; + + case MouseEvent.BUTTON2: + if (isButton2Pressed) { + shouldBalanceEvent = true; + } + isButton2Pressed = true; + break; + + case MouseEvent.BUTTON3: + if (isButton3Pressed) { + shouldBalanceEvent = true; + } + isButton3Pressed = true; + break; + default: + throw new RuntimeException("mousePressed without buttons specified"); + + } + + if (shouldBalanceEvent) { + sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_RELEASED); + } + + sendInputEventToInputManager(event, MouseEvent.MOUSE_PRESSED); + } + + /** {@inheritDoc} */ + public void mouseReleased(final MouseEvent rawEvent) { + boolean shouldBalanceEvent = false; + + final MouseEvent event = copyButtonsFromModifiers(rawEvent, MouseEvent.MOUSE_RELEASED); + + switch (event.getButton()) { + case MouseEvent.BUTTON1: + if (!isButton1Pressed) { + shouldBalanceEvent = true; + } + isButton1Pressed = false; + break; + + case MouseEvent.BUTTON2: + if (!isButton2Pressed) { + shouldBalanceEvent = true; + } + isButton2Pressed = false; + break; + + case MouseEvent.BUTTON3: + if (!isButton3Pressed) { + shouldBalanceEvent = true; + } + isButton3Pressed = false; + break; + default: + throw new RuntimeException("mouseReleased without buttons specified"); + } + + if (shouldBalanceEvent) { + sendRetypedMouseEventToInputManager(event, MouseEvent.MOUSE_PRESSED); + } + + sendInputEventToInputManager(event, MouseEvent.MOUSE_RELEASED); + } + + private boolean isAnyButtonDown(final MouseEvent e) { + return (e.getModifiersEx() & (InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK)) != 0; + } + + private MouseEvent copyButtonsFromModifiers(final MouseEvent rawEvent, final int eventType) { + if (rawEvent.getButton() != MouseEvent.NOBUTTON) { + return rawEvent; + } + + int newButton = 0; + + if (hasButtonModifier(rawEvent, InputEvent.BUTTON1_MASK)) { + newButton = MouseEvent.BUTTON1; + } + else if (hasButtonModifier(rawEvent, InputEvent.BUTTON2_MASK)) { + newButton = MouseEvent.BUTTON2; + } + else if (hasButtonModifier(rawEvent, InputEvent.BUTTON3_MASK)) { + newButton = MouseEvent.BUTTON3; + } + + return buildModifiedMouseEvent(rawEvent, eventType, newButton); + } + + private boolean hasButtonModifier(final MouseEvent event, final int buttonMask) { + return (event.getModifiers() & buttonMask) == buttonMask; + } + + public MouseEvent buildRetypedMouseEvent(final MouseEvent e, final int newType) { + return buildModifiedMouseEvent(e, newType, e.getButton()); + } + + public MouseEvent buildModifiedMouseEvent(final MouseEvent e, final int newType, final int newButton) { + return new MouseEvent((Component) e.getSource(), newType, e.getWhen(), e.getModifiers(), e.getX(), + e.getY(), e.getClickCount(), e.isPopupTrigger(), newButton); + } + + private void sendRetypedMouseEventToInputManager(final MouseEvent e, final int newType) { + final MouseEvent retypedEvent = buildRetypedMouseEvent(e, newType); + sendInputEventToInputManager(retypedEvent, newType); + } + } + + /** + * Class responsible for sending key events to the the InputManager. + */ + private final class KeyEventInputSourceListener implements KeyEventPostProcessor { + /** {@inheritDoc} */ + public boolean postProcessKeyEvent(final KeyEvent keyEvent) { + Component owner = FocusManager.getCurrentManager().getFocusOwner(); + while (owner != null) { + if (owner == PCanvas.this) { + sendInputEventToInputManager(keyEvent, keyEvent.getID()); + return true; + } + owner = owner.getParent(); + } + return false; + } + } + + private final class MouseWheelInputSourceListener implements MouseWheelListener { + /** {@inheritDoc} */ + public void mouseWheelMoved(final MouseWheelEvent e) { + sendInputEventToInputManager(e, e.getScrollType()); + if (!e.isConsumed() && getParent() != null) { + getParent().dispatchEvent(e); + } + } + } + } \ No newline at end of file diff --git a/core/src/main/java/edu/umd/cs/piccolo/PInputManager.java b/core/src/main/java/edu/umd/cs/piccolo/PInputManager.java index 0741777..c172713 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/PInputManager.java +++ b/core/src/main/java/edu/umd/cs/piccolo/PInputManager.java @@ -89,8 +89,7 @@ * Creates a PInputManager and sets positions (last, current) to the origin * (0,0). */ - public PInputManager() { - super(); + public PInputManager() { lastCanvasPosition = new Point2D.Double(); currentCanvasPosition = new Point2D.Double(); } diff --git a/core/src/main/java/edu/umd/cs/piccolo/PNode.java b/core/src/main/java/edu/umd/cs/piccolo/PNode.java index a701c8a..1121896 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/PNode.java +++ b/core/src/main/java/edu/umd/cs/piccolo/PNode.java @@ -96,6 +96,14 @@ */ public class PNode implements Cloneable, Serializable, Printable { /** + * The minimum difference in transparency required before the transparency + * is allowed to change. Done for efficiency reasons. I doubt very much that + * the human eye could tell the difference between 0.01 and 0.02 + * transparency. + */ + private static final float TRANSPARENCY_RESOLUTION = 0.01f; + + /** * Allows for future serialization code to understand versioned binary * formats. */ @@ -843,19 +851,8 @@ */ public Iterator getClientPropertyKeysIterator() { final Enumeration enumeration = getClientPropertyKeysEnumeration(); - return new Iterator() { - public boolean hasNext() { - return enumeration.hasMoreElements(); - } - public Object next() { - return enumeration.nextElement(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; + return new ClientPropertyKeyIterator(enumeration); } // **************************************************************** @@ -2770,7 +2767,7 @@ * transparent, 1f = fully opaque */ public void setTransparency(final float newTransparency) { - if (Math.abs(transparency - newTransparency) > 0.01f) { + if (Math.abs(transparency - newTransparency) > TRANSPARENCY_RESOLUTION) { final float oldTransparency = transparency; transparency = newTransparency; invalidatePaint(); @@ -2901,7 +2898,7 @@ * @return a rendering of this image and its descendants onto the specified * image */ - public Image toImage(final BufferedImage image, final Paint backGroundPaint, int fillStrategy) { + public Image toImage(final BufferedImage image, final Paint backGroundPaint, final int fillStrategy) { final int imageWidth = image.getWidth(); final int imageHeight = image.getHeight(); final Graphics2D g2 = image.createGraphics(); @@ -3698,6 +3695,26 @@ return result; } + private static final class ClientPropertyKeyIterator implements Iterator { + private final Enumeration enumeration; + + private ClientPropertyKeyIterator(final Enumeration enumeration) { + this.enumeration = enumeration; + } + + public boolean hasNext() { + return enumeration.hasMoreElements(); + } + + public Object next() { + return enumeration.nextElement(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + /** * PSceneGraphDelegate is an interface to receive low level node * events. It together with PNode.SCENE_GRAPH_DELEGATE gives Piccolo2d users diff --git a/core/src/main/java/edu/umd/cs/piccolo/PRoot.java b/core/src/main/java/edu/umd/cs/piccolo/PRoot.java index e9b6009..4fe6bb3 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/PRoot.java +++ b/core/src/main/java/edu/umd/cs/piccolo/PRoot.java @@ -164,15 +164,7 @@ * activities instead of using this method. */ public void waitForActivities() { - final PNodeFilter cameraWithCanvas = new PNodeFilter() { - public boolean accept(final PNode aNode) { - return aNode instanceof PCamera && ((PCamera) aNode).getComponent() != null; - } - - public boolean acceptChildrenOf(final PNode aNode) { - return true; - } - }; + final PNodeFilter cameraWithCanvas = new CameraWithCanvasFilter(); while (activityScheduler.getActivitiesReference().size() > 0) { processInputs(); @@ -395,6 +387,16 @@ } } + private static final class CameraWithCanvasFilter implements PNodeFilter { + public boolean accept(final PNode aNode) { + return aNode instanceof PCamera && ((PCamera) aNode).getComponent() != null; + } + + public boolean acceptChildrenOf(final PNode aNode) { + return true; + } + } + /** * This interfaces is for advanced use only. If you want to implement a * different kind of input framework then Piccolo2D provides you can hook it diff --git a/core/src/main/java/edu/umd/cs/piccolo/activities/PActivityScheduler.java b/core/src/main/java/edu/umd/cs/piccolo/activities/PActivityScheduler.java index 2c85679..5aa0c77 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/PActivityScheduler.java +++ b/core/src/main/java/edu/umd/cs/piccolo/activities/PActivityScheduler.java @@ -69,7 +69,7 @@ * @param rootNode root node of all activities to be performed. All nodes * being animated should have this node as an ancestor. */ - public PActivityScheduler(final PRoot rootNode) { + public PActivityScheduler(final PRoot rootNode) { root = rootNode; activities = new ArrayList(); processingActivities = new ArrayList(); diff --git a/core/src/main/java/edu/umd/cs/piccolo/activities/PColorActivity.java b/core/src/main/java/edu/umd/cs/piccolo/activities/PColorActivity.java index 86d4667..deca564 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/PColorActivity.java +++ b/core/src/main/java/edu/umd/cs/piccolo/activities/PColorActivity.java @@ -57,7 +57,7 @@ * * @param color the color to assign to the target */ - public void setColor(Color color); + void setColor(Color color); /** * This method is called right before the color activity starts. That @@ -65,7 +65,7 @@ * * @return the target's current color. */ - public Color getColor(); + Color getColor(); } /** @@ -84,8 +84,9 @@ /** * Constructs a color activity for the given target that will animate for - * the duration provided at an interval of stepRate from the target's starting color to the destination color. - * + * the duration provided at an interval of stepRate from the target's + * starting color to the destination color. + * * @param duration duration in milliseconds that the animation should last * @param stepRate the time between interpolations * @param aTarget the target onto which the animation is being performed @@ -136,7 +137,7 @@ * Set the final color that will be set on the color activities target when * the activity stops stepping. * - * @param changes this activity's destination color + * @param newDestination to animate towards */ public void setDestinationColor(final Color newDestination) { destination = newDestination; diff --git a/core/src/main/java/edu/umd/cs/piccolo/activities/PInterpolatingActivity.java b/core/src/main/java/edu/umd/cs/piccolo/activities/PInterpolatingActivity.java index 6eb0a92..c129c4b 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/PInterpolatingActivity.java +++ b/core/src/main/java/edu/umd/cs/piccolo/activities/PInterpolatingActivity.java @@ -69,10 +69,28 @@ private int loopCount; private boolean firstLoop; + /** + * Constructs an interpolating activity that will last the duration given + * and will update its target at the given rate. + * + * @param duration duration in milliseconds of the entire activity + * @param stepRate interval in milliseconds between updates to target + */ public PInterpolatingActivity(final long duration, final long stepRate) { this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION); } + /** + * Constructs an interpolating activity that will last the duration given + * and will update its target at the given rate. Once done, it will repeat + * the loopCount times. + * + * @param duration duration in milliseconds of the entire activity + * @param stepRate interval in milliseconds between updates to target + * @param loopCount # of times to repeat this activity. + * @param mode controls the direction of the interpolation (source to destination, + * destination to source, or source to destination back to source) + */ public PInterpolatingActivity(final long duration, final long stepRate, final int loopCount, final int mode) { this(duration, stepRate, System.currentTimeMillis(), loopCount, mode); } @@ -84,7 +102,7 @@ * @param duration the length of one loop of the activity * @param stepRate the amount of time between steps of the activity * @param startTime the time (relative to System.currentTimeMillis()) that - * this activity should start. + * this activity should start. This value can be in the future. * @param loopCount number of times the activity should reschedule itself * @param mode defines how the activity interpolates between states */ @@ -101,13 +119,15 @@ * Set the amount of time that this activity should take to complete, after * the startStepping method is called. The duration must be greater then * zero so that the interpolation value can be computed. + * + * @param duration new duration of this activity */ - public void setDuration(final long aDuration) { - if (aDuration <= 0) { + public void setDuration(final long duration) { + if (duration <= 0) { throw new IllegalArgumentException("Duration for PInterpolatingActivity must be greater then 0"); } - super.setDuration(aDuration); + super.setDuration(duration); } // **************************************************************** @@ -115,15 +135,24 @@ // **************************************************************** /** - * Return the mode that defines how the activity interpolates between - * states. + * Return the mode used for interpolation. + * + * Acceptable values are: SOURCE_TO_DESTINATION, DESTINATION_TO_SOURCE and + * SOURCE_TO_DESTINATION_TO_SOURCE + * + * @return current mode of this activity */ public int getMode() { return mode; } /** - * Set the mode that defines how the activity interpolates between states. + * Set the direction in which interpolation is going to occur. + * + * Acceptable values are: SOURCE_TO_DESTINATION, DESTINATION_TO_SOURCE and + * SOURCE_TO_DESTINATION_TO_SOURCE + * + * @param mode the new mode to use when interpolating */ public void setMode(final int mode) { this.mode = mode; @@ -231,8 +260,8 @@ } /** - * Called whenever the activity finishes. Reschedules it if the - * value of loopCount is > 0. + * Called whenever the activity finishes. Reschedules it if the value of + * loopCount is > 0. */ protected void activityFinished() { setRelativeTargetValueAdjustingForMode(1); @@ -262,6 +291,8 @@ /** * Subclasses should override this method and set the value on their target * (the object that they are modifying) accordingly. + * + * @param zeroToOne relative completion of task. */ public void setRelativeTargetValue(final float zeroToOne) { } @@ -288,25 +319,28 @@ * * @param zeroToOne Percentage of activity completed */ - protected void setRelativeTargetValueAdjustingForMode(float zeroToOne) { + protected void setRelativeTargetValueAdjustingForMode(final float zeroToOne) { + final float adjustedZeroToOne; switch (mode) { - case SOURCE_TO_DESTINATION: - break; - case DESTINATION_TO_SOURCE: - zeroToOne = 1 - zeroToOne; + adjustedZeroToOne = 1 - zeroToOne; break; case SOURCE_TO_DESTINATION_TO_SOURCE: if (zeroToOne <= 0.5) { - zeroToOne *= 2; + adjustedZeroToOne = zeroToOne * 2; } else { - zeroToOne = 1 - (zeroToOne - 0.5f) * 2; + adjustedZeroToOne = 1 - (zeroToOne - 0.5f) * 2; } break; + case SOURCE_TO_DESTINATION: + default: + // Just treat the zeroToOne as how far along the interpolation + // we are. + adjustedZeroToOne = zeroToOne; } - setRelativeTargetValue(zeroToOne); + setRelativeTargetValue(adjustedZeroToOne); } } diff --git a/core/src/main/java/edu/umd/cs/piccolo/activities/PTransformActivity.java b/core/src/main/java/edu/umd/cs/piccolo/activities/PTransformActivity.java index bc79182..fcdb835 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/PTransformActivity.java +++ b/core/src/main/java/edu/umd/cs/piccolo/activities/PTransformActivity.java @@ -63,7 +63,7 @@ * * @param aTransform the transform to be applied to the target. */ - public void setTransform(AffineTransform aTransform); + void setTransform(AffineTransform aTransform); /** * This method is called right before the transform activity starts. @@ -71,16 +71,42 @@ * * @param aSource array to be populated with the target's gurrent matrix */ - public void getSourceMatrix(double[] aSource); + void getSourceMatrix(double[] aSource); } - public PTransformActivity(final long duration, final long stepRate, final Target aTarget) { - this(duration, stepRate, aTarget, null); + /** + * Constructs a transform activity that will last for the specified + * duration, will update at the given step rate and will be applied to the + * target. + * + * TODO: document what the destination transform is set to when not + * specified. (Looks like the Zero vector, but that can't be right, can it?) + * + * @param duration duration in milliseconds of the entire activity + * @param stepRate interval in milliseconds between successive animation + * steps + * @param target the target of the activity + */ + public PTransformActivity(final long duration, final long stepRate, final Target target) { + this(duration, stepRate, target, null); } - public PTransformActivity(final long duration, final long stepRate, final Target aTarget, - final AffineTransform aDestination) { - this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, aTarget, aDestination); + /** + * Constructs a activity that will change the target's transform in the + * destination transform. It will last for the specified duration, will + * update at the given step rate. + * + * @param duration duration in milliseconds of the entire activity + * @param stepRate interval in milliseconds between successive animation + * steps + * @param target the target of the activity + * @param destination transform that the target will be after the ativity is + * finished + */ + + public PTransformActivity(final long duration, final long stepRate, final Target target, + final AffineTransform destination) { + this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, target, destination); } /** @@ -91,21 +117,26 @@ * @param stepRate the amount of time between steps of the activity * @param loopCount number of times the activity should reschedule itself * @param mode defines how the activity interpolates between states - * @param aTarget the object that the activity will be applied to and where + * @param target the object that the activity will be applied to and where * the source state will be taken from. - * @param aDestination the destination color state + * @param destination the destination color state */ public PTransformActivity(final long duration, final long stepRate, final int loopCount, final int mode, - final Target aTarget, final AffineTransform aDestination) { + final Target target, final AffineTransform destination) { super(duration, stepRate, loopCount, mode); source = new double[6]; - destination = new double[6]; - target = aTarget; - if (aDestination != null) { - aDestination.getMatrix(destination); + this.destination = new double[6]; + this.target = target; + if (destination != null) { + destination.getMatrix(this.destination); } } + /** + * Whether each step invalidates paint. + * + * @return true since a node transform affects it's node's display + */ protected boolean isAnimation() { return true; } @@ -113,6 +144,8 @@ /** * Return the final transform that will be set on the transform activities * target when the transform activity stops stepping. + * + * @return returns the final transform as an array of doubles */ public double[] getDestinationTransform() { if (destination == null) { @@ -126,6 +159,9 @@ /** * Set the final transform that will be set on the transform activities * target when the transform activity stops stepping. + * + * @param newDestination an array of doubles representing the destination + * transform */ public void setDestinationTransform(final double[] newDestination) { if (newDestination == null) { @@ -136,6 +172,10 @@ } } + /** + * Is invoked when the activity is started. Ensures that setTransform is + * called on the target even before the first step. + */ protected void activityStarted() { if (getFirstLoop()) { target.getSourceMatrix(source); @@ -143,6 +183,17 @@ super.activityStarted(); } + /** + * Set's the target value to be the interpolation between the source and + * destination transforms. + * + * A value of 0 for zeroToOne means that the target should have the source + * transform. A value of 1 for zeroToOne means that the target should have + * the destination transform. + * + * @param zeroToOne how far along the activity has progressed. 0 = not at + * all, 1 = completed + */ public void setRelativeTargetValue(final float zeroToOne) { super.setRelativeTargetValue(zeroToOne); diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PBasicInputEventHandler.java b/core/src/main/java/edu/umd/cs/piccolo/event/PBasicInputEventHandler.java index 20bd02c..5be3093 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PBasicInputEventHandler.java +++ b/core/src/main/java/edu/umd/cs/piccolo/event/PBasicInputEventHandler.java @@ -48,11 +48,21 @@ private PInputEventFilter eventFilter; + /** + * Constructs a PBasicInputEventHandler with a wide open event filter. + */ public PBasicInputEventHandler() { super(); eventFilter = new PInputEventFilter(); } + /** + * Dispatches a generic event to a more specific method. Sparing subclasses + * from the dispatch logic. + * + * @param event the event to be dispatched + * @param type Swing event type of the underlying Swing event + */ public void processEvent(final PInputEvent event, final int type) { if (!acceptsEvent(event, type)) { return; @@ -127,66 +137,178 @@ // default case) then it accepts all events. // **************************************************************** + /** + * Returns true if the event would be dispatched if passed to processEvent. + * + * @param event event being tested for acceptance + * @param type Swing event type of underlying swing event + * + * @return true if the event would be dispatched + */ public boolean acceptsEvent(final PInputEvent event, final int type) { return eventFilter.acceptsEvent(event, type); } + /** + * Returns the event filter responsible for filtering incoming events. + * + * @return this handler's InputEventFilter + */ public PInputEventFilter getEventFilter() { return eventFilter; } + /** + * Changes this event handler's filter to the one provided. + * + * @param newEventFilter filter to use for this input event handler + */ public void setEventFilter(final PInputEventFilter newEventFilter) { eventFilter = newEventFilter; } - // **************************************************************** - // Events - Methods for handling events sent to the event listener. - // **************************************************************** - + /** + * Will get called whenever a key has been pressed down. Subclasses should + * override this method to implement their own behavior. + * + * @param event the event representing the keystroke + */ public void keyPressed(final PInputEvent event) { } + /** + * Will get called whenever a key has been released. Subclasses should + * override this method to implement their own behavior. + * + * @param event the event representing the keystroke + */ public void keyReleased(final PInputEvent event) { } + /** + * Will be called at the end of a full keystroke (down then up). Subclasses + * should override this method to implement their own behavior. + * + * @param event object which can be queried for the event's details + */ public void keyTyped(final PInputEvent event) { } + /** + * Will be called at the end of a full click (mouse pressed followed by + * mouse released). Subclasses should override this method to implement + * their own behavior. + * + * @param event object which can be queried for the event's details + */ public void mouseClicked(final PInputEvent event) { } + /** + * Will be called when a mouse button is pressed down. Should two buttons be + * pressed simultaneously, it will dispatch two of these in an unspecified + * order. Subclasses should override this method to implement their own + * behavior. + * + * @param event object which can be queried for the event's details + */ public void mousePressed(final PInputEvent event) { } + /** + * Will be called when a drag is occurring. This is system dependent. + * Subclasses should override this method to implement their own behavior. + * + * @param event object which can be queried for the event's details + */ public void mouseDragged(final PInputEvent event) { } + /** + * Will be invoked when the mouse enters a specified region. Subclasses + * should override this method to implement their own behavior. + * + * @param event object which can be queried for the event's details + */ public void mouseEntered(final PInputEvent event) { } + /** + * Will be invoked when the mouse leaves a specified region. Subclasses + * should override this method to implement their own behavior. + * + * @param event object which can be queried for the event's details + */ public void mouseExited(final PInputEvent event) { } + /** + * Will be called when the mouse is moved. Subclasses should override this + * method to implement their own behavior. + * + * @param event object which can be queried for event details + */ public void mouseMoved(final PInputEvent event) { } + /** + * Will be called when any mouse button is released. Should two or more + * buttons be released simultaneously, this method will be called multiple + * times. Subclasses should override this method to implement their own + * behavior. + * + * @param event object which can be queried for event details + */ public void mouseReleased(final PInputEvent event) { } + /** + * This method is invoked when the mouse wheel is rotated. Subclasses should + * override this method to implement their own behavior. + * + * @param event an object that can be queries to discover the event's + * details + */ public void mouseWheelRotated(final PInputEvent event) { } + /** + * This method is invoked when the mouse wheel is rotated by a block. + * Subclasses should override this method to implement their own behavior. + * + * TODO: check that this means 1 tick of the wheel. + * + * @param event an object that can be queries to discover the event's + * details + */ + public void mouseWheelRotatedByBlock(final PInputEvent event) { } + /** + * This method is invoked when a node gains the keyboard focus. Subclasses + * should override this method to implement their own behavior. + * + * @param event an object that can be queries to discover the event's + * details + */ public void keyboardFocusGained(final PInputEvent event) { } + /** + * This method is invoked when a node loses the keyboard focus. Subclasses + * should override this method to implement their own behavior. + * + * @param event an object that can be queries to discover the event's + * details + */ public void keyboardFocusLost(final PInputEvent event) { } /** * @deprecated see http://code.google.com/p/piccolo2d/issues/detail?id=99 + * + * @return empty string since this method is deprecated */ protected String paramString() { return ""; diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PDragEventHandler.java b/core/src/main/java/edu/umd/cs/piccolo/event/PDragEventHandler.java index bcbd643..ed8b846 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PDragEventHandler.java +++ b/core/src/main/java/edu/umd/cs/piccolo/event/PDragEventHandler.java @@ -34,9 +34,8 @@ import edu.umd.cs.piccolo.util.PDimension; /** - * PDragEventHandler is a simple event handler for dragging a node on the + * PDragEventHandler is a simple event handler for dragging a node on the * canvas. - *
* * @version 1.0 * @author Jesse Grosjean @@ -44,25 +43,54 @@ public class PDragEventHandler extends PDragSequenceEventHandler { private PNode draggedNode; - private boolean moveToFrontOnPress = false; + private boolean moveToFrontOnPress; + /** + * Constructs a drag event handler which defaults to not moving the node to + * the front on drag. + */ public PDragEventHandler() { - super(); + draggedNode = null; + moveToFrontOnPress = false; + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); } + /** + * Returns the node that is currently being dragged, or null if none. + * + * @return node being dragged or null + */ protected PNode getDraggedNode() { return draggedNode; } + /** + * Set's the node that is currently being dragged. + * + * @param draggedNode node to be flagged as this handler's current drag node + */ protected void setDraggedNode(final PNode draggedNode) { this.draggedNode = draggedNode; } + /** + * Returns whether the given event should be start a drag interaction. + * + * @param event the event being tested + * + * @return true if event is a valid start drag event + */ protected boolean shouldStartDragInteraction(final PInputEvent event) { return super.shouldStartDragInteraction(event) && event.getPickedNode() != event.getTopCamera(); } + /** + * Starts a drag event and moves the dragged node to the front if this + * handler has been directed to do so with a call to setMoveToFrontOnDrag. + * + * @param event The Event responsible for the start of the drag + */ protected void startDrag(final PInputEvent event) { super.startDrag(event); draggedNode = event.getPickedNode(); @@ -71,6 +99,11 @@ } } + /** + * Moves the dragged node in proportion to the drag distance. + * + * @param event event representing the drag + */ protected void drag(final PInputEvent event) { super.drag(event); final PDimension d = event.getDeltaRelativeTo(draggedNode); @@ -78,15 +111,34 @@ draggedNode.offset(d.getWidth(), d.getHeight()); } + /** + * Clears the current drag node. + * + * @param event Event reponsible for the end of the drag. Usually a + * "Mouse Up" event. + */ protected void endDrag(final PInputEvent event) { super.endDrag(event); draggedNode = null; } + /** + * Returns whether this drag event handler has been informed to move nodes + * to the front of all other on drag. + * + * @return true if dragging a node will move it to the front + */ public boolean getMoveToFrontOnPress() { return moveToFrontOnPress; } + /** + * Informs this drag event handler whether it should move nodes to the front + * when they are dragged. Default is false. + * + * @param moveToFrontOnPress true if dragging a node should move it to the + * front + */ public void setMoveToFrontOnPress(final boolean moveToFrontOnPress) { this.moveToFrontOnPress = moveToFrontOnPress; } diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PDragSequenceEventHandler.java b/core/src/main/java/edu/umd/cs/piccolo/event/PDragSequenceEventHandler.java index 637f291..8962cc6 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PDragSequenceEventHandler.java +++ b/core/src/main/java/edu/umd/cs/piccolo/event/PDragSequenceEventHandler.java @@ -56,28 +56,45 @@ private transient PInputEvent dragEvent; private transient int sequenceInitiatedButton = MouseEvent.NOBUTTON; + /** Constructs a drag sequence event handler instance. */ public PDragSequenceEventHandler() { } - // **************************************************************** - // Basics - // **************************************************************** - + /** + * Returns true if this event handler is in the process of handling a drag. + * + * @return true if handling a drag + */ public boolean isDragging() { return isDragging; } + /** + * Used to inform this handler that it is in the process of handling a drag. + * + * @param isDragging true if handler is processing a drag + */ public void setIsDragging(final boolean isDragging) { this.isDragging = isDragging; } + /** + * Returns the minimum distance (in screen coordinates) before a pressed + * mouse movement is registered as a drag event. The smaller this value the + * more clicks will be incorrectly recognized as drag events. + * + * @return minimum distance a pressed mouse must move before it is + * registered as a drag + */ public double getMinDragStartDistance() { return minDragStartDistance; } /** * Set the minimum distance that the mouse should be dragged (in screen - * coords) before a new drag sequence is initiate. + * coordinates) before a new drag sequence is initiate. + * + * @param minDistance in screen coordinates */ public void setMinDragStartDistance(final double minDistance) { minDragStartDistance = minDistance; @@ -85,6 +102,8 @@ /** * Return the point in canvas coordinates where the mouse was last pressed. + * + * @return point in canvas coordinates of last mouse press */ public Point2D getMousePressedCanvasPoint() { if (mousePressedCanvasPoint == null) { @@ -101,37 +120,54 @@ * Subclasses should override this method to get notified of the start of a * new drag sequence. Note that that overriding methods must still call * super.startDrag() for correct behavior. + * + * @param event event that started the drag sequence */ - protected void startDrag(final PInputEvent e) { - dragEvent = e; - startDragActivity(e); + protected void startDrag(final PInputEvent event) { + dragEvent = event; + startDragActivity(event); setIsDragging(true); - e.getComponent().setInteracting(true); + event.getComponent().setInteracting(true); } /** * Subclasses should override this method to get notified of the drag events * in a drag sequence. Note that that overriding methods must still call * super.startDrag() for correct behavior. + * + * @param event event that caused the drag */ - protected void drag(final PInputEvent e) { - dragEvent = e; + protected void drag(final PInputEvent event) { + dragEvent = event; } /** * Subclasses should override this method to get notified of the end event * in a drag sequence. Note that that overriding methods must still call * super.startDrag() for correct behavior. + * + * @param event event that ended the drag sequence */ - protected void endDrag(final PInputEvent e) { - stopDragActivity(e); + protected void endDrag(final PInputEvent event) { + stopDragActivity(event); dragEvent = null; - e.getComponent().setInteracting(false); + event.getComponent().setInteracting(false); setIsDragging(false); } - protected boolean shouldStartDragInteraction(final PInputEvent e) { - return getMousePressedCanvasPoint().distance(e.getCanvasPosition()) >= getMinDragStartDistance(); + /** + * Returns true if the provided event represents a valid start for a drag + * sequence. + * + * Subclasses should override this method to add criteria for the start of a + * drag sequence. Subclasses are still responsible for calling + * super.shouldStartDragInteraction() + * + * @param event event being tested + * @return true if provided event is a good start to a drag sequence + */ + protected boolean shouldStartDragInteraction(final PInputEvent event) { + return getMousePressedCanvasPoint().distance(event.getCanvasPosition()) >= getMinDragStartDistance(); } // **************************************************************** @@ -140,11 +176,27 @@ // using this. // **************************************************************** + /** + * Returns the scheduled activity that's updating the scene as a result to + * the current drag activity (if any). + * + * @return scheduled activity that's updating the scene as a result to the + * drag activity + */ protected PActivity getDragActivity() { return dragActivity; } - protected void startDragActivity(final PInputEvent aEvent) { + /** + * Schedules the "infinite" drag activity so that auto-panning and zooming + * will continue to update the scene even if there are no further drag + * events fired. For example, if the mouse is dragged to the right while + * pressing the right mouse button and then paused for a while, the scene + * should continue to zoom in. + * + * @param event the event that's responsible for the start of the activity + */ + protected void startDragActivity(final PInputEvent event) { dragActivity = new PActivity(-1, PUtil.DEFAULT_ACTIVITY_STEP_RATE); dragActivity.setDelegate(new PActivity.PActivityDelegate() { public void activityStarted(final PActivity activity) { @@ -160,19 +212,27 @@ } }); - aEvent.getCamera().getRoot().addActivity(dragActivity); + event.getCamera().getRoot().addActivity(dragActivity); } - protected void stopDragActivity(final PInputEvent aEvent) { + /** + * Stops the activity responsible for updating the scene. + * + * @param event The event responsible for stopping the drag activity + */ + protected void stopDragActivity(final PInputEvent event) { dragActivity.terminate(); dragActivity = null; } /** - * Override this method to get notified when the drag activity starts - * stepping. + * Subclasses override this method to get notified when the drag activity + * starts stepping. + * + * @param event the event responsible for the first step in the drag + * activity */ - protected void dragActivityFirstStep(final PInputEvent aEvent) { + protected void dragActivityFirstStep(final PInputEvent event) { } /** @@ -181,57 +241,72 @@ * additional behavior that is not driven directly by mouse events. For * example PZoomEventHandler uses it for zooming and PPanEventHandler uses * it for auto panning. + * + * @param event the event encapsulating the callback context for the + * activity step */ - protected void dragActivityStep(final PInputEvent aEvent) { + protected void dragActivityStep(final PInputEvent event) { } /** - * Override this method to get notified when the drag activity stops - * stepping. + * Subclasses should override this method to get notified when the drag + * activity stops stepping. + * + * @param aEvent the event responsible for ending the activity */ protected void dragActivityFinalStep(final PInputEvent aEvent) { } - // **************************************************************** - // Events - subclasses should not override these methods, instead - // override the appropriate drag method. - // **************************************************************** - - public void mousePressed(final PInputEvent e) { - super.mousePressed(e); + /** + * Subclasses should not override this method, instead they should + * override the appropriate drag callbacks. + * + * @param event The event to be queried about the details of the mouse press + */ + public void mousePressed(final PInputEvent event) { + super.mousePressed(event); if (sequenceInitiatedButton == MouseEvent.NOBUTTON) { - sequenceInitiatedButton = e.getButton(); - } - else { - return; - } - - getMousePressedCanvasPoint().setLocation(e.getCanvasPosition()); - if (!isDragging() && shouldStartDragInteraction(e)) { - startDrag(e); + sequenceInitiatedButton = event.getButton(); + + getMousePressedCanvasPoint().setLocation(event.getCanvasPosition()); + if (!isDragging() && shouldStartDragInteraction(event)) { + startDrag(event); + } } } - public void mouseDragged(final PInputEvent e) { - super.mouseDragged(e); + /** + * Subclasses should not override this method, instead they should + * override the appropriate drag method. + * + * @param event The event to be queried about the details of the mouse press + */ + public void mouseDragged(final PInputEvent event) { + super.mouseDragged(event); if (sequenceInitiatedButton != MouseEvent.NOBUTTON) { if (!isDragging()) { - if (shouldStartDragInteraction(e)) { - startDrag(e); + if (shouldStartDragInteraction(event)) { + startDrag(event); } return; } - drag(e); + drag(event); } } - public void mouseReleased(final PInputEvent e) { - super.mouseReleased(e); - if (sequenceInitiatedButton == e.getButton()) { + /** + * Subclasses should not override this method, instead they should + * override the appropriate drag method. + * + * @param event The event to be queried about the details of the mouse release + */ + public void mouseReleased(final PInputEvent event) { + super.mouseReleased(event); + if (sequenceInitiatedButton == event.getButton()) { if (isDragging()) { - endDrag(e); + endDrag(event); } sequenceInitiatedButton = MouseEvent.NOBUTTON; } diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PInputEvent.java b/core/src/main/java/edu/umd/cs/piccolo/event/PInputEvent.java index 388f547..08cf879 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PInputEvent.java +++ b/core/src/main/java/edu/umd/cs/piccolo/event/PInputEvent.java @@ -79,8 +79,7 @@ * @param inputManager source of PInputEvent * @param event underlying swing event */ - public PInputEvent(final PInputManager inputManager, final InputEvent event) { - super(); + public PInputEvent(final PInputManager inputManager, final InputEvent event) { inputEvent = event; this.inputManager = inputManager; } @@ -184,6 +183,9 @@ * @return the currently picked node of this mouse event */ public PNode getPickedNode() { + if (pickPath == null) { + return null; + } return pickPath.getPickedNode(); } @@ -499,7 +501,6 @@ * * @return boolean, true if this event triggers a popup menu for this * platform - * @throws IllegalStateException if this event is not a mouse event */ public boolean isPopupTrigger() { if (isMouseEvent()) { @@ -544,6 +545,9 @@ * @return mouse position relative to the provided node on pick path */ public Point2D getPositionRelativeTo(final PNode nodeOnPath) { + if (pickPath == null) { + throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); + } final Point2D r = getCanvasPosition(); return pickPath.canvasToLocal(r, nodeOnPath); } @@ -557,6 +561,9 @@ * path */ public PDimension getDeltaRelativeTo(final PNode nodeOnPath) { + if (pickPath == null) { + throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); + } final PDimension r = getCanvasDelta(); return (PDimension) pickPath.canvasToLocal(r, nodeOnPath); } @@ -568,6 +575,9 @@ * @return mouse position as measured by the bottom camera */ public Point2D getPosition() { + if (pickPath == null) { + throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); + } final Point2D r = getCanvasPosition(); pickPath.canvasToLocal(r, getCamera()); return getCamera().localToView(r); @@ -581,6 +591,9 @@ * bottom camera */ public PDimension getDelta() { + if (pickPath == null) { + throw new RuntimeException("Attempting to use pickPath for a non-mouse event."); + } final PDimension r = getCanvasDelta(); pickPath.canvasToLocal(r, getCamera()); return (PDimension) getCamera().localToView(r); diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PPanEventHandler.java b/core/src/main/java/edu/umd/cs/piccolo/event/PPanEventHandler.java index 65a82b5..0e0caf1 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PPanEventHandler.java +++ b/core/src/main/java/edu/umd/cs/piccolo/event/PPanEventHandler.java @@ -48,27 +48,43 @@ */ public class PPanEventHandler extends PDragSequenceEventHandler { - private boolean autopan; - private double minAutopanSpeed = 250; - private double maxAutopanSpeed = 750; + private static final int DEFAULT_MAX_AUTOPAN_SPEED = 750; + private static final int DEFAULT_MIN_AUTOPAN_SPEED = 250; + private boolean autopan; + private double minAutopanSpeed = DEFAULT_MIN_AUTOPAN_SPEED; + private double maxAutopanSpeed = DEFAULT_MAX_AUTOPAN_SPEED; + + /** + * Constructs a Pan Event Handler that will by default perform auto-panning. + */ public PPanEventHandler() { super(); setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); setAutopan(true); } - protected void drag(final PInputEvent e) { - super.drag(e); - pan(e); + /** + * Updates the view in response to a user initiated drag event. + * + * @param event event responsible for the drag + */ + protected void drag(final PInputEvent event) { + super.drag(event); + pan(event); } - protected void pan(final PInputEvent e) { - final PCamera c = e.getCamera(); - final Point2D l = e.getPosition(); + /** + * Pans the camera in response to the pan event provided. + * + * @param event contains details about the drag used to translate the view + */ + protected void pan(final PInputEvent event) { + final PCamera c = event.getCamera(); + final Point2D l = event.getPosition(); if (c.getViewBounds().contains(l)) { - final PDimension d = e.getDelta(); + final PDimension d = event.getDelta(); c.translateView(d.getWidth(), d.getHeight()); } } @@ -77,10 +93,20 @@ // Auto Pan // **************************************************************** + /** + * Determines if auto-panning will occur or not. + * + * @param autopan true if auto-panning functionality will be active + */ public void setAutopan(final boolean autopan) { this.autopan = autopan; } + /** + * Returns whether the auto-panning functoinality is enabled. + * + * @return true if auto-panning is enabled + */ public boolean getAutopan() { return autopan; } @@ -88,16 +114,18 @@ /** * Set the minAutoPan speed in pixels per second. * - * @param minAutopanSpeed + * @param minAutopanSpeed number of pixels to assign as the minimum the + * autopan feature can pan the view */ public void setMinAutopanSpeed(final double minAutopanSpeed) { this.minAutopanSpeed = minAutopanSpeed; } /** - * Set the maxAutoPan speed in pixes per second. + * Set the maxAutoPan speed in pixels per second. * - * @param maxAutopanSpeed + * @param maxAutopanSpeed number of pixels to assign as the maximum the + * autopan feature can pan the view */ public void setMaxAutopanSpeed(final double maxAutopanSpeed) { this.maxAutopanSpeed = maxAutopanSpeed; @@ -106,7 +134,7 @@ /** * Returns the minAutoPan speed in pixels per second. * - * @return minAutopanSpeed in pixels + * @return minimum distance the autopan feature can pan the view */ public double getMinAutoPanSpeed() { return minAutopanSpeed; @@ -115,23 +143,25 @@ /** * Returns the maxAutoPan speed in pixels per second. * - * @return maxAutopanSpeed in pixels + * @return max distance the autopan feature can pan the view by */ public double getMaxAutoPanSpeed() { return maxAutopanSpeed; } /** - * Do auto panning even when the mouse is not moving. + * Performs auto-panning if enabled, even when the mouse is not moving. + * + * @param event current drag relevant details about the drag activity */ - protected void dragActivityStep(final PInputEvent aEvent) { + protected void dragActivityStep(final PInputEvent event) { if (!autopan) { return; } - final PCamera c = aEvent.getCamera(); + final PCamera c = event.getCamera(); final PBounds b = c.getBoundsReference(); - final Point2D l = aEvent.getPositionRelativeTo(c); + final Point2D l = event.getPositionRelativeTo(c); final int outcode = b.outcode(l); final PDimension delta = new PDimension(); @@ -156,21 +186,37 @@ } } - protected double validatePanningSpeed(double delta) { - final double minDelta = minAutopanSpeed / (1000d / getDragActivity().getStepRate()); - final double maxDelta = maxAutopanSpeed / (1000d / getDragActivity().getStepRate()); + /** + * Clips the panning speed to the minimum and maximum auto-pan speeds + * assigned. If delta is below the threshold, it will be increased. If + * above, it will be decreased. + * + * @param delta auto-pan delta to be clipped + * @return clipped delta value. + */ + protected double validatePanningSpeed(final double delta) { + final double stepsPerSecond = 1000d / getDragActivity().getStepRate(); + final double minDelta = minAutopanSpeed / stepsPerSecond; + final double maxDelta = maxAutopanSpeed / stepsPerSecond; - final boolean deltaNegative = delta < 0; - delta = Math.abs(delta); - if (delta < minDelta) { - delta = minDelta; + final double absDelta = Math.abs(delta); + + final double clippedDelta; + if (absDelta < minDelta) { + clippedDelta = minDelta; } - if (delta > maxDelta) { - delta = maxDelta; + else if (absDelta > maxDelta) { + clippedDelta = maxDelta; } - if (deltaNegative) { - delta = -delta; + else { + clippedDelta = delta; } - return delta; + + if (delta < 0) { + return -clippedDelta; + } + else { + return clippedDelta; + } } } diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PZoomEventHandler.java b/core/src/main/java/edu/umd/cs/piccolo/event/PZoomEventHandler.java index 640b3de..11251c2 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PZoomEventHandler.java +++ b/core/src/main/java/edu/umd/cs/piccolo/event/PZoomEventHandler.java @@ -59,6 +59,11 @@ */ public class PZoomEventHandler extends PDragSequenceEventHandler { + /** + * A constant used to adjust how sensitive the zooming will be to mouse + * movement. The larger the number, the more each delta pixel will affect zooming. + */ + private static final double ZOOM_SENSITIVITY = 0.001; private double minScale = 0; private double maxScale = Double.MAX_VALUE; private Point2D viewZoomPoint; @@ -118,15 +123,27 @@ this.maxScale = maxScale; } - protected void dragActivityFirstStep(final PInputEvent aEvent) { - viewZoomPoint = aEvent.getPosition(); - super.dragActivityFirstStep(aEvent); + /** + * Records the start point of the zoom. Used when calculating the delta for + * zoom speed. + * + * @param event event responsible for starting the zoom interaction + */ + protected void dragActivityFirstStep(final PInputEvent event) { + viewZoomPoint = event.getPosition(); + super.dragActivityFirstStep(event); } - protected void dragActivityStep(final PInputEvent aEvent) { - final PCamera camera = aEvent.getCamera(); - final double dx = aEvent.getCanvasPosition().getX() - getMousePressedCanvasPoint().getX(); - double scaleDelta = 1.0 + 0.001 * dx; + /** + * Updates the current zoom periodically, regardless of whether the mouse + * has moved recently. + * + * @param event contains information about the current state of the mouse + */ + protected void dragActivityStep(final PInputEvent event) { + final PCamera camera = event.getCamera(); + final double dx = event.getCanvasPosition().getX() - getMousePressedCanvasPoint().getX(); + double scaleDelta = 1.0 + ZOOM_SENSITIVITY * dx; final double currentScale = camera.getViewScale(); final double newScale = currentScale * scaleDelta; diff --git a/core/src/main/java/edu/umd/cs/piccolo/nodes/PHtmlView.java b/core/src/main/java/edu/umd/cs/piccolo/nodes/PHtmlView.java index 215355e..51a0476 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/nodes/PHtmlView.java +++ b/core/src/main/java/edu/umd/cs/piccolo/nodes/PHtmlView.java @@ -230,15 +230,13 @@ * This text color is used to render the HTML text if not otherwise * specified via CSS. * - *
* This is a bound property. - *
* * @param textColor text color for this HTML text node */ - public void setTextColor(final Color color) { + public void setTextColor(final Color textColor) { final Color oldColor = label.getForeground(); - label.setForeground(color); + label.setForeground(textColor); repaint(); firePropertyChange(PROPERTY_CODE_TEXT_COLOR, PROPERTY_TEXT_COLOR, oldColor, label.getForeground()); } diff --git a/core/src/main/java/edu/umd/cs/piccolo/nodes/PImage.java b/core/src/main/java/edu/umd/cs/piccolo/nodes/PImage.java index 7426f54..bc6c625 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/nodes/PImage.java +++ b/core/src/main/java/edu/umd/cs/piccolo/nodes/PImage.java @@ -69,47 +69,57 @@ * to Image objects in any property change event. */ public static final String PROPERTY_IMAGE = "image"; + /** + * The property code that identifies a change of this node's image (see + * {@link #getImage getImage}). Both old and new value will be set correctly + * to Image objects in any property change event. + */ + public static final int PROPERTY_CODE_IMAGE = 1 << 15; private transient Image image; + /** Constructs a PImage without a java.awt.Image attached. */ public PImage() { - super(); - } - - /** - * Construct a new PImage wrapping the given java.awt.Image. - */ - public PImage(final Image newImage) { - this(); - setImage(newImage); } /** * Construct a new PImage by loading the given fileName and wrapping the * resulting java.awt.Image. + * + * @param fileName of the image to wrap */ public PImage(final String fileName) { this(Toolkit.getDefaultToolkit().getImage(fileName)); } /** + * Construct a new PImage wrapping the given java.awt.Image. + * + * @param image image that this PImage will wrap + */ + public PImage(final Image image) { + setImage(image); + } + + /** * Construct a new PImage by loading the given url and wrapping the * resulting java.awt.Image. If the url isnull
, create an
* empty PImage; this behaviour is useful when fetching resources that may
* be missing.
+ *
+ * @param url URL of image resource to load
*/
public PImage(final java.net.URL url) {
- this();
if (url != null) {
setImage(Toolkit.getDefaultToolkit().getImage(url));
}
}
/**
- * Returns the image that is shown by this node.
+ * Returns the image that is shown by this node, or null if none.
*
- * @return the image that is shown by this node
+ * @return java.awt.Image being wrapped by this node
*/
public Image getImage() {
return image;
@@ -118,6 +128,8 @@
/**
* Set the image that is wrapped by this PImage node. This method will also
* load the image using a MediaTracker before returning.
+ *
+ * @param fileName file to be wrapped by this PImage
*/
public void setImage(final String fileName) {
setImage(Toolkit.getDefaultToolkit().getImage(fileName));
@@ -126,6 +138,8 @@
/**
* Set the image that is wrapped by this PImage node. This method will also
* load the image using a MediaTracker before returning.
+ *
+ * @param newImage image to be displayed by this PImage
*/
public void setImage(final Image newImage) {
final Image oldImage = image;
@@ -146,7 +160,7 @@
}
/**
- * Ensures the image is loaded enough (loading is fine)
+ * Ensures the image is loaded enough (loading is fine).
*
* @param newImage to check
* @return image or null if not loaded enough.
@@ -155,7 +169,6 @@
final ImageIcon imageLoader = new ImageIcon(newImage);
switch (imageLoader.getImageLoadStatus()) {
case MediaTracker.LOADING:
- return imageLoader.getImage();
case MediaTracker.COMPLETE:
return imageLoader.getImage();
default:
@@ -163,6 +176,12 @@
}
}
+ /**
+ * Renders the wrapped Image, stretching it appropriately if the bounds of
+ * this PImage doesn't match the bounds of the image.
+ *
+ * @param paintContext context into which the rendering will occur
+ */
protected void paint(final PPaintContext paintContext) {
if (getImage() == null) {
return;
@@ -187,13 +206,12 @@
}
- // ****************************************************************
- // Serialization
- // ****************************************************************
-
/**
- * The java.awt.Image wrapped by this PImage is converted into a
- * BufferedImage when serialized.
+ * Serializes this PImage to the stream provided. The java.awt.Image wrapped
+ * by this PImage is converted into a BufferedImage when serialized.
+ *
+ * @param out stream into which serialized object will be serialized
+ * @throws IOException if error occurs while writing to the output stream
*/
private void writeObject(final ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
@@ -203,19 +221,28 @@
}
}
+ /**
+ * Deserializes a PImage from the input stream provided.
+ *
+ * @param in stream from which the PImage should be read
+ * @throws IOException if problem occurs while reading from input stream
+ * @throws ClassNotFoundException occurs is no mapping from the bytes in the
+ * stream can be found to classes available
+ */
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
image = ImageIO.read(in);
}
- // ****************************************************************
- // Util
- // ****************************************************************
-
/**
- * If alwaysCreateCopy is false then if the image is already a buffered
- * image it will not be copied and instead the original image will just be
- * returned.
+ * Converts the provided image into a BufferedImage. If alwaysCreateCopy is
+ * false then if the image is already a buffered image it will not be copied
+ * and instead the original image will just be returned.
+ *
+ * @param image the image to be converted
+ * @param alwaysCreateCopy if true, will create a copy even if image is
+ * already a BufferedImage
+ * @return a BufferedImage equivalent to the Image provided
*/
public static BufferedImage toBufferedImage(final Image image, final boolean alwaysCreateCopy) {
if (image == null) {
diff --git a/core/src/main/java/edu/umd/cs/piccolo/nodes/PPath.java b/core/src/main/java/edu/umd/cs/piccolo/nodes/PPath.java
index abf7c10..c62dee4 100644
--- a/core/src/main/java/edu/umd/cs/piccolo/nodes/PPath.java
+++ b/core/src/main/java/edu/umd/cs/piccolo/nodes/PPath.java
@@ -244,10 +244,10 @@
}
/**
- * Creates an PPath in the given shape with the default paint and stroke.
- *
- * @param aShape the desired shape
- */
+ * Creates an PPath in the given shape with the default paint and stroke.
+ *
+ * @param aShape the desired shape
+ */
public PPath(final Shape aShape) {
this(aShape, DEFAULT_STROKE);
}
@@ -272,10 +272,6 @@
}
}
- // ****************************************************************
- // Stroke
- // ****************************************************************
-
/**
* Returns the stroke paint of the PPath.
*
@@ -285,17 +281,32 @@
return strokePaint;
}
- public void setStrokePaint(final Paint aPaint) {
- final Paint old = strokePaint;
- strokePaint = aPaint;
+ /**
+ * Sets the stroke paint of the path.
+ *
+ * @param newStrokePaint the paint to use as this path's stroke paint
+ */
+ public void setStrokePaint(final Paint newStrokePaint) {
+ final Paint oldStrokePaint = strokePaint;
+ strokePaint = newStrokePaint;
invalidatePaint();
- firePropertyChange(PROPERTY_CODE_STROKE_PAINT, PROPERTY_STROKE_PAINT, old, strokePaint);
+ firePropertyChange(PROPERTY_CODE_STROKE_PAINT, PROPERTY_STROKE_PAINT, oldStrokePaint, strokePaint);
}
+ /**
+ * Returns the stroke to use when drawing the path.
+ *
+ * @return current stroke of path
+ */
public Stroke getStroke() {
return stroke;
}
+ /**
+ * Sets the stroke to use when drawing the path.
+ *
+ * @param aStroke stroke to use when drawing the path
+ */
public void setStroke(final Stroke aStroke) {
final Stroke old = stroke;
stroke = aStroke;
@@ -304,14 +315,12 @@
firePropertyChange(PROPERTY_CODE_STROKE, PROPERTY_STROKE, old, stroke);
}
- // ****************************************************************
- // Bounds
- // ****************************************************************
-
+ /** Stores the original size of the path before resizing started. */
public void startResizeBounds() {
resizePath = new GeneralPath(path);
}
+ /** Clears the size of the path before resizing. */
public void endResizeBounds() {
resizePath = null;
}
@@ -322,8 +331,13 @@
* base bounds get too small then it is impossible to expand the path shape
* again since all its numbers have tended to zero, so application code may
* need to take this into consideration.
+ *
+ * @param x new left position of bounds
+ * @param y new top position of bounds
+ * @param width the new width of the bounds
+ * @param height the new height of the bounds
*/
- protected void internalUpdateBounds(double x, double y, double width, double height) {
+ protected void internalUpdateBounds(final double x, final double y, final double width, final double height) {
if (updatingBoundsFromPath || path == null) {
return;
}
@@ -339,22 +353,42 @@
.getHeight()
- pathBounds.getHeight());
- x += strokeOutset / 2;
- y += strokeOutset / 2;
- width -= strokeOutset;
- height -= strokeOutset;
+ double adjustedX = x + strokeOutset / 2;
+ double adjustedY = y + strokeOutset / 2;
+ double adjustedWidth = width - strokeOutset;
+ double adjustedHeight = height - strokeOutset;
- final double scaleX = width == 0 || pathBounds.getWidth() == 0 ? 1 : width / pathBounds.getWidth();
- final double scaleY = height == 0 || pathBounds.getHeight() == 0 ? 1 : height / pathBounds.getHeight();
+ final double scaleX;
+ if (adjustedWidth == 0 || pathBounds.getWidth() == 0) {
+ scaleX = 1;
+ }
+ else {
+ scaleX = adjustedWidth / pathBounds.getWidth();
+ }
+
+ final double scaleY;
+ if (adjustedHeight == 0 || pathBounds.getHeight() == 0) {
+ scaleY = 1;
+ }
+ else {
+ scaleY = adjustedHeight / pathBounds.getHeight();
+ }
TEMP_TRANSFORM.setToIdentity();
- TEMP_TRANSFORM.translate(x, y);
+ TEMP_TRANSFORM.translate(adjustedX, adjustedY);
TEMP_TRANSFORM.scale(scaleX, scaleY);
TEMP_TRANSFORM.translate(-pathBounds.getX(), -pathBounds.getY());
path.transform(TEMP_TRANSFORM);
}
+ /**
+ * Returns true if path crosses the provided bounds. Takes visibility of
+ * path into account.
+ *
+ * @param aBounds bounds being tested for intersection
+ * @return true if path visibly crosses bounds
+ */
public boolean intersects(final Rectangle2D aBounds) {
if (super.intersects(aBounds)) {
if (getPaint() != null && path.intersects(aBounds)) {
@@ -367,6 +401,11 @@
return false;
}
+ /**
+ * Calculates the path's bounds taking stroke into account.
+ *
+ * @return bounds of the path taking stroke width into account
+ */
public Rectangle2D getPathBoundsWithStroke() {
if (stroke != null) {
return stroke.createStrokedShape(path).getBounds2D();
@@ -376,6 +415,9 @@
}
}
+ /**
+ * Recomputes the bounds taking stroke into account.
+ */
public void updateBoundsFromPath() {
updatingBoundsFromPath = true;
if (path == null) {
@@ -388,10 +430,15 @@
updatingBoundsFromPath = false;
}
- // ****************************************************************
- // Painting
- // ****************************************************************
-
+ /**
+ * Paints the path in the provided paintContext. Can perform very
+ * differently depending on whether the path is being drawn using its stroke
+ * or its paint.
+ *
+ * It both are provided to the path, fun ensues.
+ *
+ * @param paintContext context in which painting is occurring
+ */
protected void paint(final PPaintContext paintContext) {
final Paint p = getPaint();
final Graphics2D g2 = paintContext.getGraphics();
@@ -408,15 +455,21 @@
}
}
- // ****************************************************************
- // Path Support set java.awt.GeneralPath documentation for more
- // information on using these methods.
- // ****************************************************************
-
+ /**
+ * Provides direct access to the underlying GeneralPath object.
+ *
+ * @return underlying GeneralPath
+ */
public GeneralPath getPathReference() {
return path;
}
+ /**
+ * Appends a "move" operation to the end of the path.
+ *
+ * @param x the x component of the point to move to
+ * @param y the y component of the point to move to
+ */
public void moveTo(final float x, final float y) {
path.moveTo(x, y);
firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
@@ -424,6 +477,12 @@
invalidatePaint();
}
+ /**
+ * Draws a line from the last point in the path to point provided.
+ *
+ * @param x the x component of the point
+ * @param y the y component of the point
+ */
public void lineTo(final float x, final float y) {
path.lineTo(x, y);
firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
@@ -431,6 +490,14 @@
invalidatePaint();
}
+ /**
+ * Appends a quad line to the end of the path.
+ *
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ */
public void quadTo(final float x1, final float y1, final float x2, final float y2) {
path.quadTo(x1, y1, x2, y2);
firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
@@ -438,6 +505,16 @@
invalidatePaint();
}
+ /**
+ * Appends a curve to the end of the path.
+ *
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ * @param x3
+ * @param y3
+ */
public void curveTo(final float x1, final float y1, final float x2, final float y2, final float x3, final float y3) {
path.curveTo(x1, y1, x2, y2, x3, y3);
firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
@@ -445,6 +522,14 @@
invalidatePaint();
}
+ /**
+ * Appends the provided shape to the end of this path, it may conditionally
+ * connect them together if they are disjoint.
+ *
+ * @param aShape shape to append
+ * @param connect whether to perform a lineTo operation to the beginning of
+ * the shape before appending
+ */
public void append(final Shape aShape, final boolean connect) {
path.append(aShape, connect);
firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
@@ -452,21 +537,48 @@
invalidatePaint();
}
+ /**
+ * Replaces this PPath's path with the one provided.
+ *
+ * @param aShape shape to replace the current one with
+ */
public void setPathTo(final Shape aShape) {
path.reset();
append(aShape, false);
}
+ /**
+ * Resets the path to a rectangle with the dimensions and position provided.
+ *
+ * @param x left of the rectangle
+ * @param y top of te rectangle
+ * @param width width of the rectangle
+ * @param height height of the rectangle
+ */
public void setPathToRectangle(final float x, final float y, final float width, final float height) {
TEMP_RECTANGLE.setFrame(x, y, width, height);
setPathTo(TEMP_RECTANGLE);
}
+ /**
+ * Resets the path to an ellipse positioned at the coordinate provided with
+ * the dimensions provided.
+ *
+ * @param x left of the ellipse
+ * @param y top of the ellipse
+ * @param width width of the ellipse
+ * @param height height of the ellipse
+ */
public void setPathToEllipse(final float x, final float y, final float width, final float height) {
TEMP_ELLIPSE.setFrame(x, y, width, height);
setPathTo(TEMP_ELLIPSE);
}
+ /**
+ * Sets the path to a sequence of segments described by the points.
+ *
+ * @param points points to that lie along the generated path
+ */
public void setPathToPolyline(final Point2D[] points) {
path.reset();
path.moveTo((float) points[0].getX(), (float) points[0].getY());
@@ -478,6 +590,13 @@
invalidatePaint();
}
+ /**
+ * Sets the path to a sequence of segments described by the point components
+ * provided.
+ *
+ * @param xp the x components of the points along the path
+ * @param yp the y components of the points along the path
+ */
public void setPathToPolyline(final float[] xp, final float[] yp) {
path.reset();
path.moveTo(xp[0], yp[0]);
@@ -489,6 +608,9 @@
invalidatePaint();
}
+ /**
+ * Marks the path as closed. Making changes to it impossible.
+ */
public void closePath() {
path.closePath();
firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
@@ -496,6 +618,9 @@
invalidatePaint();
}
+ /**
+ * Empties the path.
+ */
public void reset() {
path.reset();
firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
@@ -503,16 +628,28 @@
invalidatePaint();
}
- // ****************************************************************
- // Serialization
- // ****************************************************************
-
+ /**
+ * Writes this PPath object to the output stream provided. Necessary since
+ * stroke and path are not serializable by default.
+ *
+ * @param out output stream into which objects are to be serialized
+ * @throws IOException if serialiazing to output stream fails
+ */
private void writeObject(final ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
PUtil.writeStroke(stroke, out);
PUtil.writePath(path, out);
}
+ /**
+ * Deserializes a PPath object from the provided input stream. This method
+ * is required since Strokes and GeneralPaths are not serializable by
+ * default.
+ *
+ * @param in stream from which to read this PPath's state
+ * @throws IOException when exception occurs reading from input stream
+ * @throws ClassNotFoundException
+ */
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
stroke = PUtil.readStroke(in);
diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PBounds.java b/core/src/main/java/edu/umd/cs/piccolo/util/PBounds.java
index 4c95b76..b7b4276 100644
--- a/core/src/main/java/edu/umd/cs/piccolo/util/PBounds.java
+++ b/core/src/main/java/edu/umd/cs/piccolo/util/PBounds.java
@@ -115,7 +115,7 @@
/**
* Returns a clone of this node.
*/
- public Object clone() {
+ public Object clone() {
return new PBounds(this);
}
diff --git a/core/src/test/java/edu/umd/cs/piccolo/PNodeTest.java b/core/src/test/java/edu/umd/cs/piccolo/PNodeTest.java
index 8137d6a..bacf518 100644
--- a/core/src/test/java/edu/umd/cs/piccolo/PNodeTest.java
+++ b/core/src/test/java/edu/umd/cs/piccolo/PNodeTest.java
@@ -194,18 +194,55 @@
assertEquals(node, parent.getChild(2));
}
- public void testCopy() {
+ public void testCloneCopiesAllProperties() {
+ node.setBounds(1, 2, 3, 4);
+ node.setChildPaintInvalid(true);
+ node.setChildrenPickable(false);
node.setPaint(Color.yellow);
+ node.setPaintInvalid(true);
+ node.setPickable(false);
+ node.setPropertyChangeParentMask(PNode.PROPERTY_CODE_PAINT);
+ node.setVisible(false);
+
+ final PNode clonedNode = (PNode) node.clone();
+ assertEquals(1, clonedNode.getX(), Double.MIN_VALUE);
+ assertEquals(2, clonedNode.getY(), Double.MIN_VALUE);
+ assertEquals(3, clonedNode.getWidth(), Double.MIN_VALUE);
+ assertEquals(4, clonedNode.getHeight(), Double.MIN_VALUE);
+ assertTrue(clonedNode.getChildPaintInvalid());
+ assertFalse(clonedNode.getChildrenPickable());
+ assertEquals(Color.YELLOW, clonedNode.getPaint());
+
+ assertFalse(clonedNode.getPickable());
+ assertEquals(PNode.PROPERTY_CODE_PAINT, node.getPropertyChangeParentMask());
+ assertFalse(clonedNode.getVisible());
+ }
+
+ public void testCloneCopiesTransforms() {
+ node.setScale(0.5);
+ node.setRotation(Math.PI/8d);
+ node.setOffset(5,6);
+
+ final PNode clonedNode = (PNode) node.clone();
+
+ assertEquals(0.5, clonedNode.getScale(), 0.00001);
+ assertEquals(Math.PI/8d, clonedNode.getRotation(), 0.00001);
+ assertEquals(5, clonedNode.getXOffset(), Double.MIN_VALUE);
+ assertEquals(6, clonedNode.getYOffset(), Double.MIN_VALUE);
+ }
+
+ public void testCloneClonesChildrenAswell() {
final PNode child = new PNode();
node.addChild(child);
final PNode clonedNode = (PNode) node.clone();
-
- assertEquals(clonedNode.getPaint(), Color.yellow);
+
assertEquals(clonedNode.getChildrenCount(), 1);
+ assertNotSame(child, clonedNode.getChild(0));
}
+
public void testLocalToGlobal() {
final PNode aParent = new PNode();
final PNode aChild = new PNode();
@@ -1043,10 +1080,10 @@
}
- public void testToImageScalesAccordingToAspectCoverStrategy() throws IOException {
+ public void testToImageScalesAccordingToAspectCoverStrategy() throws IOException {
node.setBounds(0, 0, 10, 10);
node.setPaint(Color.RED);
-
+
PNode blueSquare = new PNode();
blueSquare.setPaint(Color.BLUE);
blueSquare.setBounds(0, 0, 5, 5);
@@ -1057,7 +1094,6 @@
greenSquare.setBounds(5, 5, 5, 5);
node.addChild(greenSquare);
-
final BufferedImage img = (BufferedImage) node.toImage(new BufferedImage(20, 40, BufferedImage.TYPE_INT_RGB),
Color.BLUE, PNode.FILL_STRATEGY_EXACT_FIT);
@@ -1065,18 +1101,18 @@
assertEquals(Color.RED.getRGB(), img.getRGB(9, 20));
assertEquals(Color.RED.getRGB(), img.getRGB(0, 20));
assertEquals(Color.RED.getRGB(), img.getRGB(9, 39));
-
+
assertEquals(Color.BLUE.getRGB(), img.getRGB(9, 19));
assertEquals(Color.BLUE.getRGB(), img.getRGB(0, 0));
assertEquals(Color.BLUE.getRGB(), img.getRGB(0, 19));
assertEquals(Color.BLUE.getRGB(), img.getRGB(9, 0));
-
- assertEquals(Color.GREEN.getRGB(), img.getRGB(10, 20));
+
+ assertEquals(Color.GREEN.getRGB(), img.getRGB(10, 20));
assertEquals(Color.GREEN.getRGB(), img.getRGB(19, 20));
assertEquals(Color.GREEN.getRGB(), img.getRGB(10, 39));
assertEquals(Color.GREEN.getRGB(), img.getRGB(19, 39));
}
-
+
public void testGetPickableShouldDefaultToTrue() {
assertTrue(node.getPickable());
}
@@ -1381,8 +1417,8 @@
final PPickPath path = canvas.getCamera().pick(5, 5, 5);
assertSame(node1, path.getPickedNode());
}
-
+
public void testToImageDoesNotClip() {
-
+
}
}