diff --git a/core/src/main/java/edu/umd/cs/piccolo/PCamera.java b/core/src/main/java/edu/umd/cs/piccolo/PCamera.java deleted file mode 100644 index 96c5737..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/PCamera.java +++ /dev/null @@ -1,971 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import java.awt.geom.Dimension2D; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import edu.umd.cs.piccolo.activities.PTransformActivity; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDebug; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PObjectOutputStream; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PPickPath; -import edu.umd.cs.piccolo.util.PUtil; - -/** - * PCamera represents a viewport onto a list of layer nodes. Each camera - * maintains a view transform through which it views these layers. Translating - * and scaling this view transform is how zooming and panning are implemented. - *
- * Cameras are also the point through which all PInputEvents enter Piccolo. The - * canvas coordinate system and the local coordinate system of the topmost - * camera should always be the same. - *
- * - * @see PLayer - * @version 1.0 - * @author Jesse Grosjean - */ -public class PCamera extends PNode { - - /** Default serial version UID. */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in the set of this camera's - * layers (see {@link #getLayer getLayer}, {@link #getLayerCount - * getLayerCount}, {@link #getLayersReference getLayersReference}). A - * property change event's new value will be a reference to the list of this - * nodes layers, but old value will always be null. - */ - public static final String PROPERTY_LAYERS = "layers"; - - /** - * The property code that identifies a change in the set of this camera's - * layers (see {@link #getLayer getLayer}, {@link #getLayerCount - * getLayerCount}, {@link #getLayersReference getLayersReference}). A - * property change event's new value will be a reference to the list of this - * nodes layers, but old value will always be null. - */ - public static final int PROPERTY_CODE_LAYERS = 1 << 11; - - /** - * The property name that identifies a change in this camera's view - * transform (see {@link #getViewTransform getViewTransform}, - * {@link #getViewTransformReference getViewTransformReference}). A property - * change event's new value will be a reference to the view transform, but - * old value will always be null. - */ - public static final String PROPERTY_VIEW_TRANSFORM = "viewTransform"; - - /** - * The property code that identifies a change in this camera's view - * transform (see {@link #getViewTransform getViewTransform}, - * {@link #getViewTransformReference getViewTransformReference}). A property - * change event's new value will be a reference to the view transform, but - * old value will always be null. - */ - public static final int PROPERTY_CODE_VIEW_TRANSFORM = 1 << 12; - - /** 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; - - /** Component which receives repaint notification from this camera. */ - private transient PComponent component; - - /** List of layers viewed by this camera. */ - private transient List/*null
if no
- * component has been associated with this camera, as may be the case for
- * internal cameras.
- *
- * @return the component for this camera, or null
if no such
- * component exists
- */
- public PComponent getComponent() {
- return component;
- }
-
- /**
- * Set the component for this camera to component
. The
- * component, if non-null, receives repaint notification from this camera.
- *
- * @param component component for this camera
- */
- public void setComponent(final PComponent component) {
- this.component = component;
- invalidatePaint();
- }
-
- /**
- * Repaint this camera and forward the repaint request to the component
- * for this camera, if it is non-null.
- *
- * @param localBounds bounds that require repainting, in local coordinates
- * @param sourceNode node from which the repaint message originates, may
- * be the camera itself
- */
- public void repaintFrom(final PBounds localBounds, final PNode sourceNode) {
- if (getParent() != null) {
- if (sourceNode != this) {
- localToParent(localBounds);
- }
- if (component != null) {
- component.repaint(localBounds);
- }
- getParent().repaintFrom(localBounds, this);
- }
- }
-
- /**
- * Repaint from one of the camera's layers. The repaint region needs to be
- * transformed from view to local in this case. Unlike most repaint methods
- * in Piccolo2D this one must not modify the viewBounds
- * parameter.
- *
- * @since 1.3
- * @param viewBounds bounds that require repainting, in view coordinates
- * @param repaintedLayer layer dispatching the repaint notification
- */
- public void repaintFromLayer(final PBounds viewBounds, final PLayer repaintedLayer) {
- TEMP_REPAINT_RECT.setRect(viewBounds);
- viewToLocal(TEMP_REPAINT_RECT);
- if (getBoundsReference().intersects(TEMP_REPAINT_RECT)) {
- Rectangle2D.intersect(TEMP_REPAINT_RECT, getBoundsReference(), TEMP_REPAINT_RECT);
- repaintFrom(TEMP_REPAINT_RECT, repaintedLayer);
- }
- }
-
- /**
- * Return a reference to the list of layers viewed by this camera.
- *
- * @return the list of layers viewed by this camera
- */
- public List/*index < 0 || index >= getLayerCount()
)
- */
- public PLayer getLayer(final int index) {
- return (PLayer) layers.get(index);
- }
-
- /**
- * Return the index of the first occurrence of the specified layer in the
- * list of layers viewed by this camera, or -1
if the list of layers
- * viewed by this camera does not contain the specified layer.
- *
- * @param layer layer to search for
- * @return the index of the first occurrence of the specified layer in the
- * list of layers viewed by this camera, or -1
if the list of
- * layers viewed by this camera does not contain the specified layer
- */
- public int indexOfLayer(final PLayer layer) {
- return layers.indexOf(layer);
- }
-
- /**
- * Inserts the specified layer at the end of the list of layers viewed by this camera.
- * Layers may be viewed by multiple cameras at once.
- *
- * @param layer layer to add
- */
- public void addLayer(final PLayer layer) {
- addLayer(layers.size(), layer);
- }
-
- /**
- * Inserts the specified layer at the specified position in the list of layers viewed by this camera.
- * Layers may be viewed by multiple cameras at once.
- *
- * @param index index at which the specified layer is to be inserted
- * @param layer layer to add
- * @throws IndexOutOfBoundsException if the specified index is out of range
- * (index < 0 || index >= getLayerCount()
)
- */
- public void addLayer(final int index, final PLayer layer) {
- layers.add(index, layer);
- layer.addCamera(this);
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers);
- }
-
- /**
- * Removes the first occurrence of the specified layer from the list of
- * layers viewed by this camera, if it is present.
- *
- * @param layer layer to be removed
- * @return the specified layer
- */
- public PLayer removeLayer(final PLayer layer) {
- layer.removeCamera(this);
- if (layers.remove(layer)) {
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers);
- }
- return layer;
- }
-
- /**
- * Removes the element at the specified position from the list of layers
- * viewed by this camera.
- *
- * @param index index of the layer to remove
- * @return the layer previously at the specified position
- * @throws IndexOutOfBoundsException if the specified index is out of range
- * (index < 0 || index >= getLayerCount()
)
- */
- public PLayer removeLayer(final int index) {
- final PLayer layer = (PLayer) layers.remove(index);
- layer.removeCamera(this);
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers);
- return layer;
- }
-
- /**
- * Return the union of the full bounds of each layer in the list of layers
- * viewed by this camera, or empty bounds if the list of layers viewed by
- * this camera is empty.
- *
- * @return the union of the full bounds of each layer in the list of layers
- * viewed by this camera, or empty bounds if the list of layers viewed
- * by this camera is empty
- */
- public PBounds getUnionOfLayerFullBounds() {
- final PBounds result = new PBounds();
- final int size = layers.size();
- for (int i = 0; i < size; i++) {
- final PLayer each = (PLayer) layers.get(i);
- result.add(each.getFullBoundsReference());
- }
- return result;
- }
-
- /**
- * Paint this camera and then paint this camera's view through its view
- * transform.
- *
- * @param paintContext context in which painting occurs
- */
- protected void paint(final PPaintContext paintContext) {
- super.paint(paintContext);
-
- paintContext.pushClip(getBoundsReference());
- paintContext.pushTransform(viewTransform);
-
- paintCameraView(paintContext);
- paintDebugInfo(paintContext);
-
- paintContext.popTransform(viewTransform);
- paintContext.popClip(getBoundsReference());
- }
-
- /**
- * Paint all the layers in the list of layers viewed by this camera. This method
- * is called after the view transform and clip have been applied to the
- * specified paint context.
- *
- * @param paintContext context in which painting occurs
- */
- protected void paintCameraView(final PPaintContext paintContext) {
- final int size = layers.size();
- for (int i = 0; i < size; i++) {
- final PLayer each = (PLayer) layers.get(i);
- each.fullPaint(paintContext);
- }
- }
-
- /**
- * Renders debug info onto the newly painted scene. Things like full bounds
- * and bounds are painted as filled and outlines.
- *
- * @param paintContext context in which painting occurs
- */
- protected void paintDebugInfo(final PPaintContext paintContext) {
- if (PDebug.debugBounds || PDebug.debugFullBounds) {
- final Graphics2D g2 = paintContext.getGraphics();
- paintContext.setRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
- g2.setStroke(new BasicStroke(0));
- final ArrayList nodes = new ArrayList();
- final PBounds nodeBounds = new PBounds();
-
- final Color boundsColor = Color.red;
- final Color fullBoundsColor = new Color(1.0f, 0f, 0f, 0.2f);
-
- final int size = layers.size();
- for (int i = 0; i < size; i++) {
- ((PLayer) layers.get(i)).getAllNodes(null, nodes);
- }
-
- final Iterator i = getAllNodes(null, nodes).iterator();
-
- while (i.hasNext()) {
- final PNode each = (PNode) i.next();
-
- if (PDebug.debugBounds) {
- g2.setPaint(boundsColor);
- nodeBounds.setRect(each.getBoundsReference());
-
- if (!nodeBounds.isEmpty()) {
- each.localToGlobal(nodeBounds);
- globalToLocal(nodeBounds);
- if (each == this || each.isDescendentOf(this)) {
- localToView(nodeBounds);
- }
- g2.draw(nodeBounds);
- }
- }
-
- if (PDebug.debugFullBounds) {
- g2.setPaint(fullBoundsColor);
- nodeBounds.setRect(each.getFullBoundsReference());
-
- if (!nodeBounds.isEmpty()) {
- if (each.getParent() != null) {
- each.getParent().localToGlobal(nodeBounds);
- }
- globalToLocal(nodeBounds);
- if (each == this || each.isDescendentOf(this)) {
- localToView(nodeBounds);
- }
- g2.fill(nodeBounds);
- }
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * - * Pushes this camera onto the specified paint context so that it - * can be accessed later by {@link PPaintContext#getCamera}. - *
- */ - public void fullPaint(final PPaintContext paintContext) { - paintContext.pushCamera(this); - super.fullPaint(paintContext); - paintContext.popCamera(); - } - - /** - * Generate and return a PPickPath for the point x,y specified in the local - * coord system of this camera. Picking is done with a rectangle, halo - * specifies how large that rectangle will be. - * - * @param x the x coordinate of the pick path given in local coordinates - * @param y the y coordinate of the pick path given in local coordinates - * @param halo the distance from the x,y coordinate that is considered for - * inclusion in the pick path - * - * @return the picked path - */ - public PPickPath pick(final double x, final double y, final double halo) { - final PBounds b = new PBounds(new Point2D.Double(x, y), -halo, -halo); - final PPickPath result = new PPickPath(this, b); - - fullPick(result); - - // make sure this camera is pushed. - if (result.getNodeStackReference().size() == 0) { - result.pushNode(this); - result.pushTransform(getTransformReference(false)); - } - - return result; - } - - /** - * {@inheritDoc} - * - *- * After the direct children of this camera have been given a chance to be - * picked all of the layers in the list of layers viewed by this camera are - * given a chance to be picked. - *
- * - * @return true if any of the layers in the list of layers viewed by this - * camera were picked - */ - protected boolean pickAfterChildren(final PPickPath pickPath) { - if (intersects(pickPath.getPickBounds())) { - pickPath.pushTransform(viewTransform); - - if (pickCameraView(pickPath)) { - return true; - } - - pickPath.popTransform(viewTransform); - return true; - } - return false; - } - - /** - * Try to pick all of the layers in the list of layers viewed by this - * camera. This method is called after the view transform has been applied - * to the specified pick path. - * - * @param pickPath pick path - * @return true if any of the layers in the list of layers viewed by this - * camera were picked - */ - protected boolean pickCameraView(final PPickPath pickPath) { - final int size = layers.size(); - for (int i = size - 1; i >= 0; i--) { - final PLayer each = (PLayer) layers.get(i); - if (each.fullPick(pickPath)) { - return true; - } - } - return false; - } - - // **************************************************************** - // View Transform - Methods for accessing the view transform. The - // view transform is applied before painting and picking the cameras - // layers. But not before painting or picking its direct children. - // - // Changing the view transform is how zooming and panning are - // accomplished. - // **************************************************************** - - /** - * Return the bounds of this camera in the view coordinate system. - * - * @return the bounds of this camera in the view coordinate system - */ - public PBounds getViewBounds() { - return (PBounds) localToView(getBounds()); - } - - /** - * Animates the camera's view so that the given bounds (in camera layer's - * coordinate system) are centered within the cameras view bounds. Use this - * method to point the camera at a given location. - * - * @param centerBounds the targetBounds - */ - public void setViewBounds(final Rectangle2D centerBounds) { - animateViewToCenterBounds(centerBounds, true, 0); - } - - /** - * Return the scale applied by the view transform to the list of layers - * viewed by this camera. - * - * @return the scale applied by the view transform to the list of layers - * viewed by this camera - */ - public double getViewScale() { - return viewTransform.getScale(); - } - - /** - * Scale the view transform applied to the list of layers viewed by this - * camera byscale
about the point [0, 0]
.
- *
- * @param scale view transform scale
- */
- public void scaleView(final double scale) {
- scaleViewAboutPoint(scale, 0, 0);
- }
-
- /**
- * Scale the view transform applied to the list of layers viewed by this
- * camera by scale
about the specified point
- * [x, y]
.
- *
- * @param scale view transform scale
- * @param x scale about point, x coordinate
- * @param y scale about point, y coordinate
- */
- public void scaleViewAboutPoint(final double scale, final double x, final double y) {
- viewTransform.scaleAboutPoint(scale, x, y);
- applyViewConstraints();
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform);
- }
-
- /**
- * Set the scale applied by the view transform to the list of layers
- * viewed by this camera to scale
.
- *
- * @param scale view transform scale
- */
- public void setViewScale(final double scale) {
- scaleView(scale / getViewScale());
- }
-
- /**
- * Translate the view transform applied to the list of layers viewed by this
- * camera by [dx, dy]
.
- *
- * @param dx translate delta x
- * @param dy translate delta y
- */
- public void translateView(final double dx, final double dy) {
- viewTransform.translate(dx, dy);
- applyViewConstraints();
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform);
- }
-
- /**
- * Offset the view transform applied to the list of layers viewed by this camera by [dx, dy]
. This is
- * NOT effected by the view transform's current scale or rotation. This is implemented by directly adding dx to the
- * m02 position and dy to the m12 position in the affine transform.
- *
- * @param dx offset delta x
- * @param dy offset delta y
- */
- /*
- public void offsetView(final double dx, final double dy) {
- setViewOffset(viewTransform.getTranslateX() + dx, viewTransform.getTranslateY() + dy);
- }
- */
-
- /**
- * Set the offset for the view transform applied to the list of layers
- * viewed by this camera to [x, y]
.
- *
- * @param x offset x
- * @param y offset y
- */
- public void setViewOffset(final double x, final double y) {
- viewTransform.setOffset(x, y);
- applyViewConstraints();
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform);
- }
-
- /**
- * Return a copy of the view transform applied to the list of layers
- * viewed by this camera.
- *
- * @return a copy of the view transform applied to the list of layers
- * viewed by this camera
- */
- public PAffineTransform getViewTransform() {
- return (PAffineTransform) viewTransform.clone();
- }
-
- /**
- * Return a reference to the view transform applied to the list of layers
- * viewed by this camera.
- *
- * @return the view transform applied to the list of layers
- * viewed by this camera
- */
- public PAffineTransform getViewTransformReference() {
- return viewTransform;
- }
-
- /**
- * Set the view transform applied to the list of layers
- * viewed by this camera to viewTransform
.
- *
- * @param viewTransform view transform applied to the list of layers
- * viewed by this camera
- */
- public void setViewTransform(final AffineTransform viewTransform) {
- this.viewTransform.setTransform(viewTransform);
- applyViewConstraints();
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, this.viewTransform);
- }
-
- /**
- * Animate the camera's view from its current transform when the activity
- * starts to a new transform that centers the given bounds in the camera
- * layer's coordinate system into the cameras view bounds. If the duration is
- * 0 then the view will be transformed immediately, and null will be
- * returned. Else a new PTransformActivity will get returned that is set to
- * animate the camera's view transform to the new bounds. If shouldScale is
- * true, then the camera will also scale its view so that the given bounds
- * fit fully within the cameras view bounds, else the camera will maintain
- * its original scale.
- *
- * @param centerBounds the bounds which the animation will pace at the
- * center of the view
- * @param shouldScaleToFit whether the camera should scale the view while
- * animating to it
- * @param duration how many milliseconds the animations should take
- *
- * @return the scheduled PTransformActivity
- */
- public PTransformActivity animateViewToCenterBounds(final Rectangle2D centerBounds, final boolean shouldScaleToFit,
- final long duration) {
- final PBounds viewBounds = getViewBounds();
- final PDimension delta = viewBounds.deltaRequiredToCenter(centerBounds);
- final PAffineTransform newTransform = getViewTransform();
- newTransform.translate(delta.width, delta.height);
-
- if (shouldScaleToFit) {
- final double s = Math.min(viewBounds.getWidth() / centerBounds.getWidth(), viewBounds.getHeight()
- / centerBounds.getHeight());
- if (s != Double.POSITIVE_INFINITY && s != 0) {
- newTransform.scaleAboutPoint(s, centerBounds.getCenterX(), centerBounds.getCenterY());
- }
- }
-
- return animateViewToTransform(newTransform, duration);
- }
-
- /**
- * Pan the camera's view from its current transform when the activity starts
- * to a new transform so that the view bounds will contain (if possible,
- * intersect if not possible) the new bounds in the camera layers coordinate
- * system. If the duration is 0 then the view will be transformed
- * immediately, and null will be returned. Else a new PTransformActivity
- * will get returned that is set to animate the camera's view transform to
- * the new bounds.
- *
- * @param panToBounds the bounds to which the view will animate to
- * @param duration the duration of the animation given in milliseconds
- *
- * @return the scheduled PTransformActivity
- */
- public PTransformActivity animateViewToPanToBounds(final Rectangle2D panToBounds, final long duration) {
- final PBounds viewBounds = getViewBounds();
- final PDimension delta = viewBounds.deltaRequiredToContain(panToBounds);
-
- if (delta.width != 0 || delta.height != 0) {
- if (duration == 0) {
- translateView(-delta.width, -delta.height);
- }
- else {
- final AffineTransform at = getViewTransform();
- at.translate(-delta.width, -delta.height);
- return animateViewToTransform(at, duration);
- }
- }
-
- return null;
- }
-
- /**
- * Animate the cameras view transform from its current value when the
- * activity starts to the new destination transform value.
- *
- * @param destination the transform to which the view should be transformed
- * into
- * @param duration the duraiton in milliseconds the animation should take
- *
- * @return the scheduled PTransformActivity
- */
- public PTransformActivity animateViewToTransform(final AffineTransform destination, final long duration) {
- if (duration == 0) {
- setViewTransform(destination);
- return null;
- }
-
- final PTransformActivity.Target t = new PTransformActivity.Target() {
- /** {@inheritDoc} */
- public void setTransform(final AffineTransform aTransform) {
- PCamera.this.setViewTransform(aTransform);
- }
-
- /** {@inheritDoc} */
- public void getSourceMatrix(final double[] aSource) {
- viewTransform.getMatrix(aSource);
- }
- };
-
- final PTransformActivity transformActivity = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE,
- t, destination);
-
- final PRoot r = getRoot();
- if (r != null) {
- r.getActivityScheduler().addActivity(transformActivity);
- }
-
- return transformActivity;
- }
-
- // ****************************************************************
- // View Transform Constraints - Methods for setting and applying
- // constraints to the view transform.
- // ****************************************************************
-
- /**
- * Return the constraint applied to the view. The view constraint will be one of {@link #VIEW_CONSTRAINT_NONE},
- * {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}. Defaults to {@link #VIEW_CONSTRAINT_NONE}.
- *
- * @return the view constraint being applied to the view
- */
- public int getViewConstraint() {
- return viewConstraint;
- }
-
- /**
- * Set the view constraint to apply to the view to viewConstraint
. The view constraint must be one of
- * {@link #VIEW_CONSTRAINT_NONE}, {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}.
- *
- * @param viewConstraint constraint to apply to the view
- * @throws IllegalArgumentException if viewConstraint
is not one of {@link #VIEW_CONSTRAINT_NONE},
- * {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}
- */
- public void setViewConstraint(final int viewConstraint) {
- if (viewConstraint != VIEW_CONSTRAINT_NONE && viewConstraint != VIEW_CONSTRAINT_CENTER
- && viewConstraint != VIEW_CONSTRAINT_ALL) {
- throw new IllegalArgumentException("view constraint must be one "
- + "of VIEW_CONSTRAINT_NONE, VIEW_CONSTRAINT_CENTER, or VIEW_CONSTRAINT_ALL");
- }
- this.viewConstraint = viewConstraint;
- applyViewConstraints();
- }
-
- /**
- * Transforms the view so that it conforms to the given constraint.
- */
- protected void applyViewConstraints() {
- if (VIEW_CONSTRAINT_NONE == viewConstraint) {
- return;
- }
- final PBounds viewBounds = getViewBounds();
- final PBounds layerBounds = (PBounds) globalToLocal(getUnionOfLayerFullBounds());
-
- if (VIEW_CONSTRAINT_CENTER == viewConstraint) {
- layerBounds.setRect(layerBounds.getCenterX(), layerBounds.getCenterY(), 0, 0);
- }
- PDimension constraintDelta = viewBounds.deltaRequiredToContain(layerBounds);
- viewTransform.translate(-constraintDelta.width, -constraintDelta.height);
- }
-
- // ****************************************************************
- // Camera View Coord System Conversions - Methods to translate from
- // the camera's local coord system (above the camera's view transform) to
- // the
- // camera view coord system (below the camera's view transform). When
- // converting geometry from one of the canvas's layers you must go
- // through the view transform.
- // ****************************************************************
-
- /**
- * Convert the point from the camera's view coordinate system to the
- * camera's local coordinate system. The given point is modified by this.
- *
- * @param viewPoint the point to transform to the local coordinate system
- * from the view's coordinate system
- * @return the transformed point
- */
- public Point2D viewToLocal(final Point2D viewPoint) {
- return viewTransform.transform(viewPoint, viewPoint);
- }
-
- /**
- * Convert the dimension from the camera's view coordinate system to the
- * camera's local coordinate system. The given dimension is modified by
- * this.
- *
- * @param viewDimension the dimension to transform from the view system to
- * the local coordinate system
- *
- * @return returns the transformed dimension
- */
- public Dimension2D viewToLocal(final Dimension2D viewDimension) {
- return viewTransform.transform(viewDimension, viewDimension);
- }
-
- /**
- * Convert the rectangle from the camera's view coordinate system to the
- * camera's local coordinate system. The given rectangle is modified by this
- * method.
- *
- * @param viewRectangle the rectangle to transform from view to local
- * coordinate System
- * @return the transformed rectangle
- */
- public Rectangle2D viewToLocal(final Rectangle2D viewRectangle) {
- return viewTransform.transform(viewRectangle, viewRectangle);
- }
-
- /**
- * Convert the point from the camera's local coordinate system to the
- * camera's view coordinate system. The given point is modified by this
- * method.
- *
- * @param localPoint point to transform from local to view coordinate system
- * @return the transformed point
- */
- public Point2D localToView(final Point2D localPoint) {
- return viewTransform.inverseTransform(localPoint, localPoint);
- }
-
- /**
- * Convert the dimension from the camera's local coordinate system to the
- * camera's view coordinate system. The given dimension is modified by this
- * method.
- *
- * @param localDimension the dimension to transform from local to view
- * coordinate systems
- * @return the transformed dimension
- */
- public Dimension2D localToView(final Dimension2D localDimension) {
- return viewTransform.inverseTransform(localDimension, localDimension);
- }
-
- /**
- * Convert the rectangle from the camera's local coordinate system to the
- * camera's view coordinate system. The given rectangle is modified by this
- * method.
- *
- * @param localRectangle the rectangle to transform from local to view
- * coordinate system
- * @return the transformed rectangle
- */
- public Rectangle2D localToView(final Rectangle2D localRectangle) {
- return viewTransform.inverseTransform(localRectangle, localRectangle);
- }
-
- // ****************************************************************
- // Serialization - Cameras conditionally serialize their layers.
- // This means that only the layer references that were unconditionally
- // (using writeObject) serialized by someone else will be restored
- // when the camera is unserialized.
- // ****************************************************************/
-
- /**
- * Write this camera and all its children out to the given stream. Note that
- * the cameras layers are written conditionally, so they will only get
- * written out if someone else writes them unconditionally.
- *
- * @param out the PObjectOutputStream to which this camera should be
- * serialized
- * @throws IOException if an error occured writing to the output stream
- */
- private void writeObject(final ObjectOutputStream out) throws IOException {
- if (!(out instanceof PObjectOutputStream)) {
- throw new RuntimeException("cannot serialize PCamera to a non PObjectOutputStream");
- }
- out.defaultWriteObject();
-
- final int count = getLayerCount();
- for (int i = 0; i < count; i++) {
- ((PObjectOutputStream) out).writeConditionalObject(layers.get(i));
- }
-
- out.writeObject(Boolean.FALSE);
- ((PObjectOutputStream) out).writeConditionalObject(component);
- }
-
- /**
- * Deserializes this PCamera from the ObjectInputStream.
- *
- * @param in the source ObjectInputStream
- * @throws IOException when error occurs during read
- * @throws ClassNotFoundException if the stream attempts to deserialize a
- * missing class
- */
- private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
-
- layers = new ArrayList();
-
- while (true) {
- final Object each = in.readObject();
- if (each != null) {
- if (each.equals(Boolean.FALSE)) {
- break;
- }
- else {
- layers.add(each);
- }
- }
- }
-
- component = (PComponent) in.readObject();
- }
-}
diff --git a/core/src/main/java/edu/umd/cs/piccolo/PCanvas.java b/core/src/main/java/edu/umd/cs/piccolo/PCanvas.java
deleted file mode 100644
index ae44506..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/PCanvas.java
+++ /dev/null
@@ -1,907 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Cursor;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.KeyEventPostProcessor;
-import java.awt.KeyboardFocusManager;
-import java.awt.event.ActionListener;
-import java.awt.event.HierarchyEvent;
-import java.awt.event.HierarchyListener;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.awt.event.MouseWheelEvent;
-import java.awt.event.MouseWheelListener;
-
-import javax.swing.FocusManager;
-import javax.swing.JComponent;
-import javax.swing.RepaintManager;
-import javax.swing.Timer;
-
-import edu.umd.cs.piccolo.event.PInputEventListener;
-import edu.umd.cs.piccolo.event.PPanEventHandler;
-import edu.umd.cs.piccolo.event.PZoomEventHandler;
-import edu.umd.cs.piccolo.util.PBounds;
-import edu.umd.cs.piccolo.util.PDebug;
-import edu.umd.cs.piccolo.util.PPaintContext;
-import edu.umd.cs.piccolo.util.PStack;
-import edu.umd.cs.piccolo.util.PUtil;
-
-/**
- * PCanvas is a simple Swing component that can be used to embed Piccolo
- * into a Java Swing application. Canvases view the Piccolo scene graph through
- * a camera. The canvas manages screen updates coming from this camera, and
- * forwards swing mouse and keyboard events to the camera.
- * - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PCanvas extends JComponent implements PComponent { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in the interacting state. - * - * @since 1.3 - */ - public static final String PROPERTY_INTERACTING = "INTERACTING_CHANGED_NOTIFICATION"; - - /** The camera though which this Canvas is viewing. */ - private PCamera camera; - - /** - * Stack of cursors used to keep track of cursors as they change through - * interactions. - */ - private final PStack cursorStack; - - /** - * Whether the canvas is considered to be interacting, will probably mean - * worse render quality. - */ - private int interacting; - /** - * The render quality to use when the scene is not being interacted or - * animated. - */ - private int normalRenderQuality; - - /** The quality to use while the scene is being animated. */ - private int animatingRenderQuality; - - /** The quality to use while the scene is being interacted with. */ - private int interactingRenderQuality; - - /** The one and only pan handler. */ - private transient PPanEventHandler panEventHandler; - - /** The one and only ZoomEventHandler. */ - private transient PZoomEventHandler zoomEventHandler; - - private boolean paintingImmediately; - - /** Used to track whether the last paint operation was during an animation. */ - private boolean animatingOnLastPaint; - - /** The mouse listener that is registered for large scale mouse events. */ - private transient MouseListener mouseListener; - - /** Remembers the key processor. */ - private transient KeyEventPostProcessor keyEventPostProcessor; - - /** The mouse wheel listeners that's registered to receive wheel events. */ - private transient MouseWheelListener mouseWheelListener; - /** - * The mouse listener that is registered to receive small scale mouse events - * (like motion). - */ - private transient MouseMotionListener mouseMotionListener; - - private static final int ALL_BUTTONS_MASK = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK - | InputEvent.BUTTON3_DOWN_MASK; - - /** - * Construct a canvas with the basic scene graph consisting of a root, - * camera, and layer. Zooming and panning are automatically installed. - */ - public PCanvas() { - cursorStack = new PStack(); - setCamera(createDefaultCamera()); - setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); - setAnimatingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); - setInteractingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); - setPanEventHandler(new PPanEventHandler()); - setZoomEventHandler(new PZoomEventHandler()); - setBackground(Color.WHITE); - setOpaque(true); - - addHierarchyListener(new HierarchyListener() { - public void hierarchyChanged(final HierarchyEvent e) { - if (e.getComponent() == PCanvas.this) { - if (getParent() == null) { - removeInputSources(); - } - else if (isEnabled()) { - installInputSources(); - } - } - } - }); - } - - /** - * Creates and returns a basic Scene Graph. - * - * @return a built PCamera scene - */ - protected PCamera createDefaultCamera() { - return PUtil.createBasicScenegraph(); - } - - // **************************************************************** - // Basic - Methods for accessing common piccolo nodes. - // **************************************************************** - - /** - * Get the pan event handler associated with this canvas. This event handler - * is set up to get events from the camera associated with this canvas by - * default. - * - * @return the current pan event handler, may be null - */ - public PPanEventHandler getPanEventHandler() { - return panEventHandler; - } - - /** - * Set the pan event handler associated with this canvas. - * - * @param handler the new zoom event handler - */ - public void setPanEventHandler(final PPanEventHandler handler) { - if (panEventHandler != null) { - removeInputEventListener(panEventHandler); - } - - panEventHandler = handler; - - if (panEventHandler != null) { - addInputEventListener(panEventHandler); - } - } - - /** - * Get the zoom event handler associated with this canvas. This event - * handler is set up to get events from the camera associated with this - * canvas by default. - * - * @return the current zoom event handler, may be null - */ - public PZoomEventHandler getZoomEventHandler() { - return zoomEventHandler; - } - - /** - * Set the zoom event handler associated with this canvas. - * - * @param handler the new zoom event handler - */ - public void setZoomEventHandler(final PZoomEventHandler handler) { - if (zoomEventHandler != null) { - removeInputEventListener(zoomEventHandler); - } - - zoomEventHandler = handler; - - if (zoomEventHandler != null) { - addInputEventListener(zoomEventHandler); - } - } - - /** - * Return the camera associated with this canvas. All input events from this - * canvas go through this camera. And this is the camera that paints this - * canvas. - * - * @return camera through which this PCanvas views the scene - */ - public PCamera getCamera() { - return camera; - } - - /** - * Set the camera associated with this canvas. All input events from this - * canvas go through this camera. And this is the camera that paints this - * canvas. - * - * @param newCamera the camera which this PCanvas should view the scene - */ - public void setCamera(final PCamera newCamera) { - if (camera != null) { - camera.setComponent(null); - } - - camera = newCamera; - - if (camera != null) { - camera.setComponent(this); - camera.setBounds(getBounds()); - } - } - - /** - * Return root for this canvas. - * - * @return the root PNode at the "bottom" of the scene - */ - public PRoot getRoot() { - return camera.getRoot(); - } - - /** - * Return layer for this canvas. - * - * @return the first layer attached to this camera - */ - public PLayer getLayer() { - return camera.getLayer(0); - } - - /** - * Add an input listener to the camera associated with this canvas. - * - * @param listener listener to register for event notifications - */ - public void addInputEventListener(final PInputEventListener listener) { - getCamera().addInputEventListener(listener); - } - - /** - * Remove an input listener to the camera associated with this canvas. - * - * @param listener listener to unregister from event notifications - */ - public void removeInputEventListener(final PInputEventListener listener) { - getCamera().removeInputEventListener(listener); - } - - // **************************************************************** - // Painting - // **************************************************************** - - /** - * Return true if this canvas has been marked as interacting, or whether - * it's root is interacting. If so the canvas will normally render at a - * lower quality that is faster. - * - * @return whether the canvas has been flagged as being interacted with - */ - public boolean getInteracting() { - return interacting > 0 || getRoot().getInteracting(); - } - - /** - * Return true if any activities that respond with true to the method - * isAnimating were run in the last PRoot.processInputs() loop. This values - * is used by this canvas to determine the render quality to use for the - * next paint. - * - * @return whether the PCanvas is currently being animated - */ - public boolean getAnimating() { - return getRoot().getActivityScheduler().getAnimating(); - } - - /** - * Set if this canvas is interacting. If so the canvas will normally render - * at a lower quality that is faster. Also repaints the canvas if the render - * quality should change. - * - * @param isInteracting whether the PCanvas should be considered interacting - */ - public void setInteracting(final boolean isInteracting) { - final boolean wasInteracting = getInteracting(); - - if (isInteracting) { - interacting++; - } - else { - interacting--; - } - - if (!getInteracting()) { // determine next render quality and repaint if - // it's greater then the old - // interacting render quality. - int nextRenderQuality = normalRenderQuality; - if (getAnimating()) { - nextRenderQuality = animatingRenderQuality; - } - if (nextRenderQuality > interactingRenderQuality) { - repaint(); - } - } - - final boolean newInteracting = getInteracting(); - - if (wasInteracting != newInteracting) { - firePropertyChange(PROPERTY_INTERACTING, wasInteracting, newInteracting); - } - } - - /** - * Set the render quality that should be used when rendering this canvas - * when it is not interacting or animating. The default value is - * PPaintContext. HIGH_QUALITY_RENDERING. - * - * @param defaultRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING - * or PPaintContext.LOW_QUALITY_RENDERING - */ - public void setDefaultRenderQuality(final int defaultRenderQuality) { - this.normalRenderQuality = defaultRenderQuality; - repaint(); - } - - /** - * Set the render quality that should be used when rendering this canvas - * when it is animating. The default value is - * PPaintContext.LOW_QUALITY_RENDERING. - * - * @param animatingRenderQuality supports - * PPaintContext.HIGH_QUALITY_RENDERING or - * PPaintContext.LOW_QUALITY_RENDERING - */ - public void setAnimatingRenderQuality(final int animatingRenderQuality) { - this.animatingRenderQuality = animatingRenderQuality; - if (getAnimating()) { - repaint(); - } - } - - /** - * Set the render quality that should be used when rendering this canvas - * when it is interacting. The default value is - * PPaintContext.LOW_QUALITY_RENDERING. - * - * @param interactingRenderQuality supports - * PPaintContext.HIGH_QUALITY_RENDERING or - * PPaintContext.LOW_QUALITY_RENDERING - */ - public void setInteractingRenderQuality(final int interactingRenderQuality) { - this.interactingRenderQuality = interactingRenderQuality; - if (getInteracting()) { - repaint(); - } - } - - /** - * Set the canvas cursor, and remember the previous cursor on the cursor - * stack. - * - * @param cursor the cursor to push onto the cursor stack - */ - public void pushCursor(final Cursor cursor) { - cursorStack.push(getCursor()); - setCursor(cursor); - } - - /** - * Pop the cursor on top of the cursorStack and set it as the canvas cursor. - */ - public void popCursor() { - if (!cursorStack.isEmpty()) { - setCursor((Cursor) cursorStack.pop()); - } - } - - // **************************************************************** - // Code to manage connection to Swing. There appears to be a bug in - // swing where it will occasionally send too many mouse pressed or mouse - // released events. Below we attempt to filter out those cases before - // they get delivered to the Piccolo framework. - // **************************************************************** - - /** - * Tracks whether button1 of the mouse is down. - */ - private boolean isButton1Pressed; - /** - * Tracks whether button2 of the mouse is down. - */ - private boolean isButton2Pressed; - /** - * Tracks whether button3 of the mouse is down. - */ - private boolean isButton3Pressed; - - /** - * Override setEnabled to install/remove canvas input sources as needed. - * - * @param enabled new enable status of the Pcanvas - */ - public void setEnabled(final boolean enabled) { - super.setEnabled(enabled); - - if (isEnabled() && getParent() != null) { - installInputSources(); - } - else { - removeInputSources(); - } - } - - /** - * This method installs mouse and key listeners on the canvas that forward - * those events to piccolo. - */ - protected void installInputSources() { - if (mouseListener == null) { - mouseListener = new MouseEventInputSource(); - addMouseListener(mouseListener); - } - - if (mouseMotionListener == null) { - mouseMotionListener = new MouseMotionInputSourceListener(); - addMouseMotionListener(mouseMotionListener); - } - - if (mouseWheelListener == null) { - mouseWheelListener = new MouseWheelInputSourceListener(); - addMouseWheelListener(mouseWheelListener); - } - - if (keyEventPostProcessor == null) { - keyEventPostProcessor = new KeyEventInputSourceListener(); - KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(keyEventPostProcessor); - } - } - - /** - * This method removes mouse and key listeners on the canvas that forward - * those events to piccolo. - */ - protected void removeInputSources() { - removeMouseListener(mouseListener); - removeMouseMotionListener(mouseMotionListener); - removeMouseWheelListener(mouseWheelListener); - KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(keyEventPostProcessor); - - mouseListener = null; - mouseMotionListener = null; - mouseWheelListener = null; - keyEventPostProcessor = null; - } - - /** - * Sends the given input event with the given type to the current - * InputManager. - * - * @param event event to dispatch - * @param type type of event being dispatched - */ - protected void sendInputEventToInputManager(final InputEvent event, final int type) { - getRoot().getDefaultInputManager().processEventFromCamera(event, type, getCamera()); - } - - /** - * Updates the bounds of the component and updates the camera accordingly. - * - * @param x left of bounds - * @param y top of bounds - * @param width width of bounds - * @param height height of bounds - */ - public void setBounds(final int x, final int y, final int width, final int height) { - camera.setBounds(camera.getX(), camera.getY(), width, height); - super.setBounds(x, y, width, height); - } - - /** - * {@inheritDoc} - */ - public void repaint(final PBounds bounds) { - PDebug.processRepaint(); - - bounds.expandNearestIntegerDimensions(); - bounds.inset(-1, -1); - - repaint((int) bounds.x, (int) bounds.y, (int) bounds.width, (int) bounds.height); - } - - private PBounds repaintBounds = new PBounds(); - - /** - * {@inheritDoc} - */ - public void paintComponent(final Graphics g) { - PDebug.startProcessingOutput(); - - final Graphics2D g2 = (Graphics2D) g.create(); - - // support for non-opaque canvases - // see - // http://groups.google.com/group/piccolo2d-dev/browse_thread/thread/134e2792d3a54cf - if (isOpaque()) { - g2.setColor(getBackground()); - g2.fillRect(0, 0, getWidth(), getHeight()); - } - - if (getAnimating()) { - repaintBounds.add(g2.getClipBounds()); - } - - // create new paint context and set render quality to lowest common - // denominator render quality. - final PPaintContext paintContext = new PPaintContext(g2); - if (getInteracting() || getAnimating()) { - if (interactingRenderQuality < animatingRenderQuality) { - paintContext.setRenderQuality(interactingRenderQuality); - } - else { - paintContext.setRenderQuality(animatingRenderQuality); - } - } - else { - paintContext.setRenderQuality(normalRenderQuality); - } - - camera.fullPaint(paintContext); - - // if switched state from animating to not animating invalidate the - // repaint bounds so that it will be drawn with the default instead of - // animating render quality. - if (!getAnimating() && animatingOnLastPaint) { - repaint(repaintBounds); - repaintBounds.reset(); - } - - animatingOnLastPaint = getAnimating(); - - PDebug.endProcessingOutput(g2); - } - - /** - * If not painting immediately, send paint notification to RepaintManager, - * otherwise does nothing. - */ - public void paintImmediately() { - if (paintingImmediately) { - return; - } - - paintingImmediately = true; - RepaintManager.currentManager(this).paintDirtyRegions(); - paintingImmediately = false; - } - - /** - * Helper for creating a timer. It's an extension point for subclasses to - * install their own timers. - * - * @param delay the number of milliseconds to wait before invoking the - * listener - * @param listener the listener to invoke after the delay - * - * @return the created Timer - */ - public Timer createTimer(final int delay, final ActionListener listener) { - return new Timer(delay, listener); - } - - /** - * Returns the quality to use when not animating or interacting. - * - * @since 1.3 - * @return the render quality to use when not animating or interacting - */ - public int getNormalRenderQuality() { - return normalRenderQuality; - } - - /** - * Returns the quality to use when animating. - * - * @since 1.3 - * @return Returns the quality to use when animating - */ - public int getAnimatingRenderQuality() { - return animatingRenderQuality; - } - - /** - * Returns the quality to use when interacting. - * - * @since 1.3 - * @return Returns the quality to use when interacting - */ - public int getInteractingRenderQuality() { - return interactingRenderQuality; - } - - /** - * Returns the input event listeners registered to receive input events. - * - * @since 1.3 - * @return array or input event listeners - */ - public PInputEventListener[] getInputEventListeners() { - return camera.getInputEventListeners(); - } - - /** - * Prints the entire scene regardless of what the viewable area is. - * - * @param graphics Graphics context onto which to paint the scene for printing - */ - public void printAll(final Graphics graphics) { - if (!(graphics instanceof Graphics2D)) { - throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); - } - - final Graphics2D g2 = (Graphics2D) graphics; - - final PBounds clippingRect = new PBounds(graphics.getClipBounds()); - clippingRect.expandNearestIntegerDimensions(); - - final PBounds originalCameraBounds = getCamera().getBounds(); - final PBounds layerBounds = getCamera().getUnionOfLayerFullBounds(); - getCamera().setBounds(layerBounds); - - final double clipRatio = clippingRect.getWidth() / clippingRect.getHeight(); - final double nodeRatio = ((double) getWidth()) / ((double) getHeight()); - final double scale; - if (nodeRatio <= clipRatio) { - scale = clippingRect.getHeight() / getCamera().getHeight(); - } - else { - scale = clippingRect.getWidth() / getCamera().getWidth(); - } - g2.scale(scale, scale); - g2.translate(-clippingRect.x, -clippingRect.y); - - final PPaintContext pc = new PPaintContext(g2); - pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); - getCamera().fullPaint(pc); - - 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 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); - } - } - - private boolean isAnyButtonDown(final MouseEvent e) { - return (e.getModifiersEx() & ALL_BUTTONS_MASK) != 0; - } - - /** - * 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; - } - } - - /** - * Class responsible for sending mouse events to the the InputManager. - */ - 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/PComponent.java b/core/src/main/java/edu/umd/cs/piccolo/PComponent.java deleted file mode 100644 index 2865bb7..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/PComponent.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.Cursor; - -import edu.umd.cs.piccolo.util.PBounds; - -/** - * Interface that a component needs to implement if it wants to act as a Piccolo - * canvas. - * - * @version 1.0 - * @author Lance Good - */ -public interface PComponent { - - /** - * Called to notify PComponent that given bounds need repainting. - * - * @param bounds bounds needing repaint - */ - void repaint(PBounds bounds); - - /** - * Sends a repaint notification the repaint manager if PComponent is not - * already painting immediately. - */ - void paintImmediately(); - - /** - * Pushes the given cursor onto the cursor stack and sets the current cursor - * to the one provided. - * - * @param cursor The cursor to set as the current one and push - */ - void pushCursor(Cursor cursor); - - /** - * Pops the topmost cursor from the stack and sets it as the current one. - */ - void popCursor(); - - /** - * Sets whether the component is currently being interacted with. - * - * @param interacting whether the component is currently being interacted - * with - */ - void setInteracting(boolean interacting); -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/PInputManager.java b/core/src/main/java/edu/umd/cs/piccolo/PInputManager.java deleted file mode 100644 index 0bba45b..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/PInputManager.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.event.FocusEvent; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; -import java.awt.geom.Point2D; - -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventListener; -import edu.umd.cs.piccolo.util.PPickPath; - -/** - * PInputManager is responsible for dispatching PInputEvents to node's - * event listeners. Events are dispatched from PRoot's processInputs method. - *
- * - * @see edu.umd.cs.piccolo.event.PInputEvent - * @see PRoot - * @version 1.0 - * @author Jesse Grosjean - */ -public class PInputManager extends PBasicInputEventHandler implements PRoot.InputSource { - - /** Records the last known mouse position on the canvas. */ - private final Point2D lastCanvasPosition; - - /** Records the current known mouse position on the canvas. */ - private final Point2D currentCanvasPosition; - - /** The next InputEvent that needs to be processed. */ - private InputEvent nextInput; - - /** The type of the next InputEvent that needs to be processed. */ - private int nextType; - - /** The Input Source the next event to process came from. */ - private PCamera nextInputSource; - - /** The current mouse focus. */ - private PPickPath mouseFocus; - - /** The previous mouse focus. */ - private PPickPath previousMouseFocus; - - /** Tracks where the mouse is right now on the canvas. */ - private PPickPath mouseOver; - - /** Tracks the previous location of the mouse on the canvas. */ - private PPickPath previousMouseOver; - - /** Tracks the input event listener that should receive keyboard events. */ - private PInputEventListener keyboardFocus; - - /** Tracks the number mouse buttons currently pressed. */ - private int buttonsPressed; - - /** - * Creates a PInputManager and sets positions (last, current) to the origin - * (0,0). - */ - public PInputManager() { - lastCanvasPosition = new Point2D.Double(); - currentCanvasPosition = new Point2D.Double(); - } - - /** - * Return the node that currently has the keyboard focus. This node receives - * the key events. - * - * @return the current keyboard focus - */ - public PInputEventListener getKeyboardFocus() { - return keyboardFocus; - } - - /** - * Set the node that should receive key events. - * - * @param eventHandler sets the keyboard event focus, may be null - */ - public void setKeyboardFocus(final PInputEventListener eventHandler) { - final PInputEvent focusEvent = new PInputEvent(this, null); - - if (keyboardFocus != null) { - dispatchEventToListener(focusEvent, FocusEvent.FOCUS_LOST, keyboardFocus); - } - - keyboardFocus = eventHandler; - - if (keyboardFocus != null) { - dispatchEventToListener(focusEvent, FocusEvent.FOCUS_GAINED, keyboardFocus); - } - } - - /** - * Return the current Pick Path under the mouse focus. This will return the - * path that received the current mouse pressed event, or null if the mouse - * is not pressed. The mouse focus gets mouse dragged events even what the - * mouse is not over the mouse focus. - * - * @return the current Pick Path under the mouse focus - */ - public PPickPath getMouseFocus() { - return mouseFocus; - } - - /** - * Sets the current Pick Path under the mouse focus. The mouse focus gets - * mouse dragged events even when the mouse is not over the mouse focus. - * - * @param path the new mouse focus - */ - public void setMouseFocus(final PPickPath path) { - previousMouseFocus = mouseFocus; - mouseFocus = path; - } - - /** - * Return the node the the mouse is currently over. - * - * @return the path over which the mouse currently is - */ - public PPickPath getMouseOver() { - return mouseOver; - } - - /** - * Records the path which is directly below the mouse. - * - * @param path path over which the mouse has been moved - */ - public void setMouseOver(final PPickPath path) { - mouseOver = path; - } - - /** - * Returns the position on the Canvas of the last event. - * - * @return position of last canvas event - */ - public Point2D getLastCanvasPosition() { - return lastCanvasPosition; - } - - /** - * Returns the position of the current canvas event. - * - * @return position of current canvas event - */ - public Point2D getCurrentCanvasPosition() { - return currentCanvasPosition; - } - - // **************************************************************** - // Event Handling - Methods for handling events - // - // The dispatch manager updates the focus nodes based on the - // incoming events, and dispatches those events to the appropriate - // focus nodes. - // **************************************************************** - - /** {@inheritDoc} */ - public void keyPressed(final PInputEvent event) { - dispatchEventToListener(event, KeyEvent.KEY_PRESSED, keyboardFocus); - } - - /** {@inheritDoc} */ - public void keyReleased(final PInputEvent event) { - dispatchEventToListener(event, KeyEvent.KEY_RELEASED, keyboardFocus); - } - - /** {@inheritDoc} */ - public void keyTyped(final PInputEvent event) { - dispatchEventToListener(event, KeyEvent.KEY_TYPED, keyboardFocus); - } - - /** {@inheritDoc} */ - public void mouseClicked(final PInputEvent event) { - dispatchEventToListener(event, MouseEvent.MOUSE_CLICKED, previousMouseFocus); - } - - /** {@inheritDoc} */ - public void mouseWheelRotated(final PInputEvent event) { - setMouseFocus(getMouseOver()); - dispatchEventToListener(event, MouseWheelEvent.WHEEL_UNIT_SCROLL, mouseOver); - } - - /** {@inheritDoc} */ - public void mouseWheelRotatedByBlock(final PInputEvent event) { - setMouseFocus(getMouseOver()); - dispatchEventToListener(event, MouseWheelEvent.WHEEL_BLOCK_SCROLL, mouseOver); - } - - /** {@inheritDoc} */ - public void mouseDragged(final PInputEvent event) { - checkForMouseEnteredAndExited(event); - dispatchEventToListener(event, MouseEvent.MOUSE_DRAGGED, mouseFocus); - } - - /** {@inheritDoc} */ - public void mouseEntered(final PInputEvent event) { - dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); - } - - /** {@inheritDoc} */ - public void mouseExited(final PInputEvent event) { - dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); - } - - /** {@inheritDoc} */ - public void mouseMoved(final PInputEvent event) { - checkForMouseEnteredAndExited(event); - dispatchEventToListener(event, MouseEvent.MOUSE_MOVED, mouseOver); - } - - /** {@inheritDoc} */ - public void mousePressed(final PInputEvent event) { - if (buttonsPressed == 0) { - setMouseFocus(getMouseOver()); - } - buttonsPressed++; - dispatchEventToListener(event, MouseEvent.MOUSE_PRESSED, mouseFocus); - if (buttonsPressed < 1 || buttonsPressed > 3) { - System.err.println("invalid pressedCount on mouse pressed: " + buttonsPressed); - } - } - - /** {@inheritDoc} */ - public void mouseReleased(final PInputEvent event) { - buttonsPressed--; - checkForMouseEnteredAndExited(event); - dispatchEventToListener(event, MouseEvent.MOUSE_RELEASED, mouseFocus); - if (buttonsPressed == 0) { - setMouseFocus(null); - } - if (buttonsPressed < 0 || buttonsPressed > 2) { - System.err.println("invalid pressedCount on mouse released: " + buttonsPressed); - } - } - - /** - * Fires events whenever the mouse moves from PNode to PNode. - * - * @param event to check to see if the top node has changed. - */ - protected void checkForMouseEnteredAndExited(final PInputEvent event) { - final PNode currentNode = getPickedNode(mouseOver); - final PNode previousNode = getPickedNode(previousMouseOver); - - if (currentNode != previousNode) { - dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); - dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); - previousMouseOver = mouseOver; - } - } - - /** - * Returns picked node on pickPath if pickPath is not null, or null. - * - * @param pickPath from which to extract picked node - * - * @return the picked node or null if pickPath is null - */ - private PNode getPickedNode(final PPickPath pickPath) { - if (pickPath == null) { - return null; - } - else { - return pickPath.getPickedNode(); - } - } - - // **************************************************************** - // Event Dispatch. - // **************************************************************** - /** {@inheritDoc} */ - public void processInput() { - if (nextInput == null) { - return; - } - - final PInputEvent e = new PInputEvent(this, nextInput); - - Point2D newCurrentCanvasPosition = null; - Point2D newLastCanvasPosition = null; - - if (e.isMouseEvent()) { - if (e.isMouseEnteredOrMouseExited()) { - final PPickPath aPickPath = nextInputSource.pick(((MouseEvent) nextInput).getX(), - ((MouseEvent) nextInput).getY(), 1); - setMouseOver(aPickPath); - previousMouseOver = aPickPath; - newCurrentCanvasPosition = (Point2D) currentCanvasPosition.clone(); - newLastCanvasPosition = (Point2D) lastCanvasPosition.clone(); - } - else { - lastCanvasPosition.setLocation(currentCanvasPosition); - currentCanvasPosition.setLocation(((MouseEvent) nextInput).getX(), ((MouseEvent) nextInput).getY()); - final PPickPath aPickPath = nextInputSource.pick(currentCanvasPosition.getX(), currentCanvasPosition - .getY(), 1); - setMouseOver(aPickPath); - } - } - - nextInput = null; - nextInputSource = null; - - processEvent(e, nextType); - - if (newCurrentCanvasPosition != null && newLastCanvasPosition != null) { - currentCanvasPosition.setLocation(newCurrentCanvasPosition); - lastCanvasPosition.setLocation(newLastCanvasPosition); - } - } - - /** - * Flags the given event as needing to be processed. - * - * @param event the event to be processed - * @param type type of event to be processed - * @param camera camera from which the event was dispatched - */ - public void processEventFromCamera(final InputEvent event, final int type, final PCamera camera) { - // queue input - nextInput = event; - nextType = type; - nextInputSource = camera; - - // tell root to process queued inputs - camera.getRoot().processInputs(); - } - - /** - * Dispatches the given event to the listener, or does nothing if listener - * is null. - * - * @param event event to be dispatched - * @param type type of event to dispatch - * @param listener target of dispatch - */ - private void dispatchEventToListener(final PInputEvent event, final int type, final PInputEventListener listener) { - if (listener != null) { - // clear the handled bit since the same event object is used to send - // multiple events such as mouseEntered/mouseExited and mouseMove. - event.setHandled(false); - listener.processEvent(event, type); - } - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/PLayer.java b/core/src/main/java/edu/umd/cs/piccolo/PLayer.java deleted file mode 100644 index 868e92d..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/PLayer.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.List; - -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PObjectOutputStream; - -/** - * PLayer is a node that can be viewed directly by multiple camera nodes. - * Generally child nodes are added to a layer to give the viewing cameras - * something to look at. - *
- * A single layer node may be viewed through multiple cameras with each camera - * using its own view transform. This means that any node (since layers can have - * children) may be visible through multiple cameras at the same time. - *
- * - * @see PCamera - * @see edu.umd.cs.piccolo.event.PInputEvent - * @see edu.umd.cs.piccolo.util.PPickPath - * @version 1.0 - * @author Jesse Grosjean - */ -public class PLayer extends PNode { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in the set of this layer's - * cameras (see {@link #getCamera getCamera}, {@link #getCameraCount - * getCameraCount}, {@link #getCamerasReference getCamerasReference}). In - * any property change event the new value will be a reference to the list - * of cameras, but old value will always be null. - */ - public static final String PROPERTY_CAMERAS = "cameras"; - - /** - * The property code that identifies a change in the set of this layer's - * cameras (see {@link #getCamera getCamera}, {@link #getCameraCount - * getCameraCount}, {@link #getCamerasReference getCamerasReference}). In - * any property change event the new value will be a reference to the list - * of cameras, but old value will always be null. - */ - public static final int PROPERTY_CODE_CAMERAS = 1 << 13; - - /** - * Cameras which are registered as viewers of this PLayer. - */ - private transient List cameras; - - /** - * Creates a PLayer without any cameras attached to it. - */ - public PLayer() { - super(); - cameras = new ArrayList(); - } - - // **************************************************************** - // Cameras - Maintain the list of cameras that are viewing this - // layer. - // **************************************************************** - - /** - * Get the list of cameras viewing this layer. - * - * @return direct reference to registered cameras - */ - public List getCamerasReference() { - return cameras; - } - - /** - * Get the number of cameras viewing this layer. - * - * @return the number of cameras attached to this layer - */ - public int getCameraCount() { - if (cameras == null) { - return 0; - } - return cameras.size(); - } - - /** - * Get the camera in this layer's camera list at the specified index. - * - * @param index index of camera to fetch - * @return camera at the given index - */ - public PCamera getCamera(final int index) { - return (PCamera) cameras.get(index); - } - - /** - * Add a camera to this layer's camera list. This method it called - * automatically when a layer is added to a camera. - * - * @param camera the camera to add to this layer - */ - public void addCamera(final PCamera camera) { - addCamera(cameras.size(), camera); - } - - /** - * Add a camera to this layer's camera list at the specified index. This - * method it called automatically when a layer is added to a camera. - * - * @param index index at which the camera should be inserted - * @param camera Camera to add to layer - */ - public void addCamera(final int index, final PCamera camera) { - cameras.add(index, camera); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); - } - - /** - * Remove the camera from this layer's camera list. - * - * @param camera the camera to remove from the layer, does nothing if not - * found - * @return camera that was passed in - */ - public PCamera removeCamera(final PCamera camera) { - if (cameras.remove(camera)) { - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); - } - return camera; - } - - /** - * Remove the camera at the given index from this layer's camera list. - * - * @param index the index of the camera we wish to remove - * - * @return camera that was removed - */ - public PCamera removeCamera(final int index) { - final PCamera result = (PCamera) cameras.remove(index); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); - return result; - } - - // **************************************************************** - // Camera Repaint Notifications - Layer nodes must forward their - // repaints to each camera that is viewing them so that the camera - // views will also get repainted. - // **************************************************************** - - /** - * Override repaints and forward them to the cameras that are viewing this - * layer. - * - * @param localBounds bounds flagged as needing repainting - * @param repaintSource the source of the repaint notification - */ - public void repaintFrom(final PBounds localBounds, final PNode repaintSource) { - if (repaintSource != this) { - localToParent(localBounds); - } - - notifyCameras(localBounds); - - if (getParent() != null) { - getParent().repaintFrom(localBounds, repaintSource); - } - } - - /** - * Dispatches repaint notification to all registered cameras. - * - * @param parentBounds bounds needing repainting in parent coordinate system - */ - protected void notifyCameras(final PBounds parentBounds) { - final int count = getCameraCount(); - for (int i = 0; i < count; i++) { - final PCamera each = (PCamera) cameras.get(i); - each.repaintFromLayer(parentBounds, this); - } - } - - // **************************************************************** - // Serialization - Layers conditionally serialize their cameras. - // This means that only the camera references that were unconditionally - // (using writeObject) serialized by someone else will be restored - // when the layer is unserialized. - // **************************************************************** - - /** - * Write this layer and all its children out to the given stream. Note that - * the layer writes out any cameras that are viewing it conditionally, so - * they will only get written out if someone else writes them - * unconditionally. - * - * @param out object to which the layer should be streamed - * @throws IOException may occur while serializing to stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - if (!(out instanceof PObjectOutputStream)) { - throw new RuntimeException("May not serialize PLayer to a non PObjectOutputStream"); - } - out.defaultWriteObject(); - - final int count = getCameraCount(); - for (int i = 0; i < count; i++) { - ((PObjectOutputStream) out).writeConditionalObject(cameras.get(i)); - } - - out.writeObject(Boolean.FALSE); - } - - /** - * Deserializes PLayer from the provided ObjectInputStream. - * - * @param in stream from which PLayer should be read - * - * @throws IOException since it involves quite a bit of IO - * @throws ClassNotFoundException may occur is serialized stream has been - * renamed after serialization - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - - cameras = new ArrayList(); - - while (true) { - final Object each = in.readObject(); - if (each != null) { - if (each.equals(Boolean.FALSE)) { - break; - } - else { - cameras.add(each); - } - } - } - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/PNode.java b/core/src/main/java/edu/umd/cs/piccolo/PNode.java deleted file mode 100644 index 2968280..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/PNode.java +++ /dev/null @@ -1,3657 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsEnvironment; -import java.awt.Image; -import java.awt.Paint; -import java.awt.Transparency; -import java.awt.geom.AffineTransform; -import java.awt.geom.Dimension2D; -import java.awt.geom.NoninvertibleTransformException; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.awt.print.Book; -import java.awt.print.PageFormat; -import java.awt.print.Printable; -import java.awt.print.PrinterException; -import java.awt.print.PrinterJob; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.EventListener; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import javax.swing.event.EventListenerList; -import javax.swing.event.SwingPropertyChangeSupport; -import javax.swing.text.MutableAttributeSet; -import javax.swing.text.SimpleAttributeSet; - -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.activities.PColorActivity; -import edu.umd.cs.piccolo.activities.PInterpolatingActivity; -import edu.umd.cs.piccolo.activities.PTransformActivity; -import edu.umd.cs.piccolo.event.PInputEventListener; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PAffineTransformException; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PNodeFilter; -import edu.umd.cs.piccolo.util.PObjectOutputStream; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PPickPath; -import edu.umd.cs.piccolo.util.PUtil; - -/** - * PNode is the central abstraction in Piccolo. All objects that are - * visible on the screen are instances of the node class. All nodes may have - * other "child" nodes added to them. - *
- * See edu.umd.piccolo.examples.NodeExample.java for demonstrations of how nodes - * can be used and how new types of nodes can be created. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -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. - */ - private static final long serialVersionUID = 1L; - - /** - * The property name that identifies a change in this node's client - * propertie (see {@link #getClientProperty getClientProperty}). In an - * property change event the new value will be a reference to the map of - * client properties but old value will always be null. - */ - public static final String PROPERTY_CLIENT_PROPERTIES = "clientProperties"; - - /** - * The property code that identifies a change in this node's client - * propertie (see {@link #getClientProperty getClientProperty}). In an - * property change event the new value will be a reference to the map of - * client properties but old value will always be null. - */ - public static final int PROPERTY_CODE_CLIENT_PROPERTIES = 1 << 0; - - /** - * The property name that identifies a change of this node's bounds (see - * {@link #getBounds getBounds}, {@link #getBoundsReference - * getBoundsReference}). In any property change event the new value will be - * a reference to this node's bounds, but old value will always be null. - */ - public static final String PROPERTY_BOUNDS = "bounds"; - - /** - * The property code that identifies a change of this node's bounds (see - * {@link #getBounds getBounds}, {@link #getBoundsReference - * getBoundsReference}). In any property change event the new value will be - * a reference to this node's bounds, but old value will always be null. - */ - public static final int PROPERTY_CODE_BOUNDS = 1 << 1; - - /** - * The property name that identifies a change of this node's full bounds - * (see {@link #getFullBounds getFullBounds}, - * {@link #getFullBoundsReference getFullBoundsReference}). In any property - * change event the new value will be a reference to this node's full bounds - * cache, but old value will always be null. - */ - public static final String PROPERTY_FULL_BOUNDS = "fullBounds"; - - /** - * The property code that identifies a change of this node's full bounds - * (see {@link #getFullBounds getFullBounds}, - * {@link #getFullBoundsReference getFullBoundsReference}). In any property - * change event the new value will be a reference to this node's full bounds - * cache, but old value will always be null. - */ - public static final int PROPERTY_CODE_FULL_BOUNDS = 1 << 2; - - /** - * The property name that identifies a change of this node's transform (see - * {@link #getTransform getTransform}, {@link #getTransformReference - * getTransformReference}). In any property change event the new value will - * be a reference to this node's transform, but old value will always be - * null. - */ - public static final String PROPERTY_TRANSFORM = "transform"; - - /** - * The property code that identifies a change of this node's transform (see - * {@link #getTransform getTransform}, {@link #getTransformReference - * getTransformReference}). In any property change event the new value will - * be a reference to this node's transform, but old value will always be - * null. - */ - public static final int PROPERTY_CODE_TRANSFORM = 1 << 3; - - /** - * The property name that identifies a change of this node's visibility (see - * {@link #getVisible getVisible}). Both old value and new value will be - * null in any property change event. - */ - public static final String PROPERTY_VISIBLE = "visible"; - - /** - * The property code that identifies a change of this node's visibility (see - * {@link #getVisible getVisible}). Both old value and new value will be - * null in any property change event. - */ - public static final int PROPERTY_CODE_VISIBLE = 1 << 4; - - /** - * The property name that identifies a change of this node's paint (see - * {@link #getPaint getPaint}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final String PROPERTY_PAINT = "paint"; - - /** - * The property code that identifies a change of this node's paint (see - * {@link #getPaint getPaint}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final int PROPERTY_CODE_PAINT = 1 << 5; - - /** - * The property name that identifies a change of this node's transparency - * (see {@link #getTransparency getTransparency}). Both old value and new - * value will be null in any property change event. - */ - public static final String PROPERTY_TRANSPARENCY = "transparency"; - - /** - * The property code that identifies a change of this node's transparency - * (see {@link #getTransparency getTransparency}). Both old value and new - * value will be null in any property change event. - */ - public static final int PROPERTY_CODE_TRANSPARENCY = 1 << 6; - - /** - * The property name that identifies a change of this node's pickable status - * (see {@link #getPickable getPickable}). Both old value and new value will - * be null in any property change event. - */ - public static final String PROPERTY_PICKABLE = "pickable"; - /** - * The property code that identifies a change of this node's pickable status - * (see {@link #getPickable getPickable}). Both old value and new value will - * be null in any property change event. - */ - public static final int PROPERTY_CODE_PICKABLE = 1 << 7; - - /** - * The property name that identifies a change of this node's children - * pickable status (see {@link #getChildrenPickable getChildrenPickable}). - * Both old value and new value will be null in any property change event. - */ - public static final String PROPERTY_CHILDREN_PICKABLE = "childrenPickable"; - - /** - * The property code that identifies a change of this node's children - * pickable status (see {@link #getChildrenPickable getChildrenPickable}). - * Both old value and new value will be null in any property change event. - */ - public static final int PROPERTY_CODE_CHILDREN_PICKABLE = 1 << 8; - - /** - * The property name that identifies a change in the set of this node's - * direct children (see {@link #getChildrenReference getChildrenReference}, - * {@link #getChildrenIterator getChildrenIterator}). In any property change - * event the new value will be a reference to this node's children, but old - * value will always be null. - */ - public static final String PROPERTY_CHILDREN = "children"; - - /** - * The property code that identifies a change in the set of this node's - * direct children (see {@link #getChildrenReference getChildrenReference}, - * {@link #getChildrenIterator getChildrenIterator}). In any property change - * event the new value will be a reference to this node's children, but old - * value will always be null. - */ - public static final int PROPERTY_CODE_CHILDREN = 1 << 9; - - /** - * The property name that identifies a change of this node's parent (see - * {@link #getParent getParent}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final String PROPERTY_PARENT = "parent"; - - /** - * The property code that identifies a change of this node's parent (see - * {@link #getParent getParent}). Both old value and new value will be set - * correctly in any property change event. - */ - public static final int PROPERTY_CODE_PARENT = 1 << 10; - - /** Is an optimization for use during repaints. */ - private static final PBounds TEMP_REPAINT_BOUNDS = new PBounds(); - - /** The single scene graph delegate that receives low level node events. */ - public static PSceneGraphDelegate SCENE_GRAPH_DELEGATE = null; - - /** Tracks the parent of this node, may be null. */ - private transient PNode parent; - - /** Tracks all immediate child nodes. */ - private List children; - - /** Bounds of the PNode. */ - private final PBounds bounds; - - /** Transform that applies to this node in relation to its parent. */ - private PAffineTransform transform; - - /** The paint to use for the background of this node. */ - private Paint paint; - - /** - * How Opaque this node should be 1f = fully opaque, 0f = completely - * transparent. - */ - private float transparency; - - /** A modifiable set of client properties. */ - private MutableAttributeSet clientProperties; - - /** - * An optimization that remembers the full bounds of a node rather than - * computing it every time. - */ - private PBounds fullBoundsCache; - - /** - * Mask used when deciding whether to bubble up property change events to - * parents. - */ - private int propertyChangeParentMask = 0; - - /** Used to handle property change listeners. */ - private transient SwingPropertyChangeSupport changeSupport; - - /** List of event listeners. */ - private transient EventListenerList listenerList; - - /** Whether this node is pickable or not. */ - private boolean pickable; - - /** - * Whether to stop processing pick at this node and not bother drilling down - * into children. - */ - private boolean childrenPickable; - - /** Whether this node will be rendered. */ - private boolean visible; - - private boolean childBoundsVolatile; - - /** Whether this node needs to be repainted. */ - private boolean paintInvalid; - - /** Whether children need to be repainted. */ - private boolean childPaintInvalid; - - /** Whether this node's bounds have changed, and so needs to be relaid out. */ - private boolean boundsChanged; - - /** Whether this node's full bounds need to be recomputed. */ - private boolean fullBoundsInvalid; - - /** Whether this node's child bounds need to be recomputed. */ - private boolean childBoundsInvalid; - - private boolean occluded; - - /** Stores the name associated to this node. */ - private String name; - - /** - * toImage fill strategy that stretches the node be as large as possible - * while still retaining its aspect ratio. - * - * @since 1.3 - */ - public static final int FILL_STRATEGY_ASPECT_FIT = 1; - - /** - * toImage fill strategy that stretches the node be large enough to cover - * the image, and centers it. - * - * @since 1.3 - */ - public static final int FILL_STRATEGY_ASPECT_COVER = 2; - - /** - * toImage fill strategy that stretches the node to be exactly the - * dimensions of the image. Will result in distortion if the aspect ratios - * are different. - * - * @since 1.3 - */ - public static final int FILL_STRATEGY_EXACT_FIT = 4; - - /** - * Creates a new PNode with the given name. - * - * @since 1.3 - * @param newName name to assign to node - */ - public PNode(final String newName) { - this(); - setName(newName); - } - - /** - * Constructs a new PNode. - *
- * By default a node's paint is null, and bounds are empty. These values
- * must be set for the node to show up on the screen once it's added to a
- * scene graph.
- */
- public PNode() {
- bounds = new PBounds();
- fullBoundsCache = new PBounds();
- transparency = 1.0f;
- pickable = true;
- childrenPickable = true;
- visible = true;
- }
-
- // ****************************************************************
- // Animation - Methods to animate this node.
- //
- // Note that animation is implemented by activities (PActivity),
- // so if you need more control over your animation look at the
- // activities package. Each animate method creates an animation that
- // will animate the node from its current state to the new state
- // specified over the given duration. These methods will try to
- // automatically schedule the new activity, but if the node does not
- // descend from the root node when the method is called then the
- // activity will not be scheduled and you must schedule it manually.
- // ****************************************************************
-
- /**
- * Animate this node's bounds from their current location when the activity
- * starts to the specified bounds. If this node descends from the root then
- * the activity will be scheduled, else the returned activity should be
- * scheduled manually. If two different transform activities are scheduled
- * for the same node at the same time, they will both be applied to the
- * node, but the last one scheduled will be applied last on each frame, so
- * it will appear to have replaced the original. Generally you will not want
- * to do that. Note this method animates the node's bounds, but does not
- * change the node's transform. Use animateTransformToBounds() to animate
- * the node's transform instead.
- *
- * @param x left of target bounds
- * @param y top of target bounds
- * @param width width of target bounds
- * @param height height of target bounds
- * @param duration amount of time that the animation should take
- * @return the newly scheduled activity
- */
- public PInterpolatingActivity animateToBounds(final double x, final double y, final double width,
- final double height, final long duration) {
- if (duration == 0) {
- setBounds(x, y, width, height);
- return null;
- }
-
- final PBounds dst = new PBounds(x, y, width, height);
-
- final PInterpolatingActivity interpolatingActivity = new PInterpolatingActivity(duration,
- PUtil.DEFAULT_ACTIVITY_STEP_RATE) {
- private PBounds src;
-
- protected void activityStarted() {
- src = getBounds();
- startResizeBounds();
- super.activityStarted();
- }
-
- public void setRelativeTargetValue(final float zeroToOne) {
- PNode.this.setBounds(src.x + zeroToOne * (dst.x - src.x), src.y + zeroToOne * (dst.y - src.y),
- src.width + zeroToOne * (dst.width - src.width), src.height + zeroToOne
- * (dst.height - src.height));
- }
-
- protected void activityFinished() {
- super.activityFinished();
- endResizeBounds();
- }
- };
-
- addActivity(interpolatingActivity);
- return interpolatingActivity;
- }
-
- /**
- * Animate this node from it's current transform when the activity starts a
- * new transform that will fit the node into the given bounds. If this node
- * descends from the root then the activity will be scheduled, else the
- * returned activity should be scheduled manually. If two different
- * transform activities are scheduled for the same node at the same time,
- * they will both be applied to the node, but the last one scheduled will be
- * applied last on each frame, so it will appear to have replaced the
- * original. Generally you will not want to do that. Note this method
- * animates the node's transform, but does not directly change the node's
- * bounds rectangle. Use animateToBounds() to animate the node's bounds
- * rectangle instead.
- *
- * @param x left of target bounds
- * @param y top of target bounds
- * @param width width of target bounds
- * @param height height of target bounds
- * @param duration amount of time that the animation should take
- * @return the newly scheduled activity
- */
- public PTransformActivity animateTransformToBounds(final double x, final double y, final double width,
- final double height, final long duration) {
- final PAffineTransform t = new PAffineTransform();
- t.setToScale(width / getWidth(), height / getHeight());
- final double scale = t.getScale();
- t.setOffset(x - getX() * scale, y - getY() * scale);
- return animateToTransform(t, duration);
- }
-
- /**
- * Animate this node's transform from its current location when the activity
- * starts to the specified location, scale, and rotation. If this node
- * descends from the root then the activity will be scheduled, else the
- * returned activity should be scheduled manually. If two different
- * transform activities are scheduled for the same node at the same time,
- * they will both be applied to the node, but the last one scheduled will be
- * applied last on each frame, so it will appear to have replaced the
- * original. Generally you will not want to do that.
- *
- * @param x the final target x position of node
- * @param y the final target y position of node
- * @param duration amount of time that the animation should take
- * @param scale the final scale for the duration
- * @param theta final theta value (in radians) for the animation
- * @return the newly scheduled activity
- */
- public PTransformActivity animateToPositionScaleRotation(final double x, final double y, final double scale,
- final double theta, final long duration) {
- final PAffineTransform t = getTransform();
- t.setOffset(x, y);
- t.setScale(scale);
- t.setRotation(theta);
- return animateToTransform(t, duration);
- }
-
- /**
- * Animate this node's transform from its current values when the activity
- * starts to the new values specified in the given transform. If this node
- * descends from the root then the activity will be scheduled, else the
- * returned activity should be scheduled manually. If two different
- * transform activities are scheduled for the same node at the same time,
- * they will both be applied to the node, but the last one scheduled will be
- * applied last on each frame, so it will appear to have replaced the
- * original. Generally you will not want to do that.
- *
- * @param destTransform the final transform value
- * @param duration amount of time that the animation should take
- * @return the newly scheduled activity
- */
- public PTransformActivity animateToTransform(final AffineTransform destTransform, final long duration) {
- if (duration == 0) {
- setTransform(destTransform);
- return null;
- }
- else {
- final PTransformActivity.Target t = new PTransformActivity.Target() {
- public void setTransform(final AffineTransform aTransform) {
- PNode.this.setTransform(aTransform);
- }
-
- public void getSourceMatrix(final double[] aSource) {
- PNode.this.getTransformReference(true).getMatrix(aSource);
- }
- };
-
- final PTransformActivity ta = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t,
- destTransform);
- addActivity(ta);
- return ta;
- }
- }
-
- /**
- * Animate this node's color from its current value to the new value
- * specified. This meathod assumes that this nodes paint property is of type
- * color. If this node descends from the root then the activity will be
- * scheduled, else the returned activity should be scheduled manually. If
- * two different color activities are scheduled for the same node at the
- * same time, they will both be applied to the node, but the last one
- * scheduled will be applied last on each frame, so it will appear to have
- * replaced the original. Generally you will not want to do that.
- *
- * @param destColor final color value.
- * @param duration amount of time that the animation should take
- * @return the newly scheduled activity
- */
- public PInterpolatingActivity animateToColor(final Color destColor, final long duration) {
- if (duration == 0) {
- setPaint(destColor);
- return null;
- }
- else {
- final PColorActivity.Target t = new PColorActivity.Target() {
- public Color getColor() {
- return (Color) getPaint();
- }
-
- public void setColor(final Color color) {
- setPaint(color);
- }
- };
-
- final PColorActivity ca = new PColorActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, destColor);
- addActivity(ca);
- return ca;
- }
- }
-
- /**
- * Animate this node's transparency from its current value to the new value
- * specified. Transparency values must range from zero to one. If this node
- * descends from the root then the activity will be scheduled, else the
- * returned activity should be scheduled manually. If two different
- * transparency activities are scheduled for the same node at the same time,
- * they will both be applied to the node, but the last one scheduled will be
- * applied last on each frame, so it will appear to have replaced the
- * original. Generally you will not want to do that.
- *
- * @param zeroToOne final transparency value.
- * @param duration amount of time that the animation should take
- * @return the newly scheduled activity
- */
- public PInterpolatingActivity animateToTransparency(final float zeroToOne, final long duration) {
- if (duration == 0) {
- setTransparency(zeroToOne);
- return null;
- }
- else {
- final float dest = zeroToOne;
-
- final PInterpolatingActivity ta = new PInterpolatingActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE) {
- private float source;
-
- protected void activityStarted() {
- source = getTransparency();
- super.activityStarted();
- }
-
- public void setRelativeTargetValue(final float zeroToOne) {
- PNode.this.setTransparency(source + zeroToOne * (dest - source));
- }
- };
-
- addActivity(ta);
- return ta;
- }
- }
-
- /**
- * Schedule the given activity with the root, note that only scheduled
- * activities will be stepped. If the activity is successfully added true is
- * returned, else false.
- *
- * @param activity new activity to schedule
- * @return true if the activity is successfully scheduled.
- */
- public boolean addActivity(final PActivity activity) {
- final PRoot r = getRoot();
- if (r != null) {
- return r.addActivity(activity);
- }
- return false;
- }
-
- // ****************************************************************
- // Client Properties - Methods for managing client properties for
- // this node.
- //
- // Client properties provide a way for programmers to attach
- // extra information to a node without having to subclass it and
- // add new instance variables.
- // ****************************************************************
-
- /**
- * Return mutable attributed set of client properties associated with this
- * node.
- *
- * @return the client properties associated to this node
- */
- public MutableAttributeSet getClientProperties() {
- if (clientProperties == null) {
- clientProperties = new SimpleAttributeSet();
- }
- return clientProperties;
- }
-
- /**
- * Returns the value of the client attribute with the specified key. Only
- * attributes added with addAttribute
will return a non-null
- * value.
- *
- * @param key key to use while fetching client attribute
- *
- * @return the value of this attribute or null
- */
- public Object getAttribute(final Object key) {
- if (clientProperties == null || key == null) {
- return null;
- }
- else {
- return clientProperties.getAttribute(key);
- }
- }
-
- /**
- * Add an arbitrary key/value to this node.
- *
- * The get/add attribute
methods provide access to a small
- * per-instance attribute set. Callers can use get/add attribute to annotate
- * nodes that were created by another module.
- *
- * If value is null this method will remove the attribute.
- *
- * @param key to use when adding the attribute
- * @param value value to associate to the new attribute
- */
- public void addAttribute(final Object key, final Object value) {
- if (value == null && clientProperties == null) {
- return;
- }
-
- final Object oldValue = getAttribute(key);
-
- if (value != oldValue) {
- if (clientProperties == null) {
- clientProperties = new SimpleAttributeSet();
- }
-
- if (value == null) {
- clientProperties.removeAttribute(key);
- }
- else {
- clientProperties.addAttribute(key, value);
- }
-
- if (clientProperties.getAttributeCount() == 0 && clientProperties.getResolveParent() == null) {
- clientProperties = null;
- }
-
- firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, PROPERTY_CLIENT_PROPERTIES, null, clientProperties);
- firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, key.toString(), oldValue, value);
- }
- }
-
- /**
- * Returns an enumeration of all keys maped to attribute values values.
- *
- * @return an Enumeration over attribute keys
- */
- public Enumeration getClientPropertyKeysEnumeration() {
- if (clientProperties == null) {
- return PUtil.NULL_ENUMERATION;
- }
- else {
- return clientProperties.getAttributeNames();
- }
- }
-
- // convenience methods for attributes
-
- /**
- * Fetches the value of the requested attribute, returning defaultValue is
- * not found.
- *
- * @param key attribute to search for
- * @param defaultValue value to return if attribute is not found
- *
- * @return value of attribute or defaultValue if not found
- */
- public Object getAttribute(final Object key, final Object defaultValue) {
- final Object value = getAttribute(key);
- if (value == null) {
- return defaultValue;
- }
-
- return value;
- }
-
- /**
- * Fetches the boolean value of the requested attribute, returning
- * defaultValue is not found.
- *
- * @param key attribute to search for
- * @param defaultValue value to return if attribute is not found
- *
- * @return value of attribute or defaultValue if not found
- */
- public boolean getBooleanAttribute(final Object key, final boolean defaultValue) {
- final Boolean value = (Boolean) getAttribute(key);
- if (value == null) {
- return defaultValue;
- }
-
- return value.booleanValue();
- }
-
- /**
- * Fetches the integer value of the requested attribute, returning
- * defaultValue is not found.
- *
- * @param key attribute to search for
- * @param defaultValue value to return if attribute is not found
- *
- * @return value of attribute or defaultValue if not found
- */
- public int getIntegerAttribute(final Object key, final int defaultValue) {
- final Number value = (Number) getAttribute(key);
- if (value == null) {
- return defaultValue;
- }
-
- return value.intValue();
- }
-
- /**
- * Fetches the double value of the requested attribute, returning
- * defaultValue is not found.
- *
- * @param key attribute to search for
- * @param defaultValue value to return if attribute is not found
- *
- * @return value of attribute or defaultValue if not found
- */
- public double getDoubleAttribute(final Object key, final double defaultValue) {
- final Number value = (Number) getAttribute(key);
- if (value == null) {
- return defaultValue;
- }
-
- return value.doubleValue();
- }
-
- // ****************************************************************
- // Copying - Methods for copying this node and its descendants.
- // Copying is implemented in terms of serialization.
- // ****************************************************************
-
- /**
- * The copy method copies this node and all of its descendants. Note that
- * copying is implemented in terms of java serialization. See the
- * serialization notes for more information.
- *
- * @return new copy of this node or null if the node was not serializable
- */
- public Object clone() {
- try {
- final byte[] ser = PObjectOutputStream.toByteArray(this);
- return new ObjectInputStream(new ByteArrayInputStream(ser)).readObject();
- }
- catch (final IOException e) {
- return null;
- }
- catch (final ClassNotFoundException e) {
- return null;
- }
- }
-
- // ****************************************************************
- // Coordinate System Conversions - Methods for converting
- // geometry between this nodes local coordinates and the other
- // major coordinate systems.
- //
- // Each nodes has an affine transform that it uses to define its
- // own coordinate system. For example if you create a new node and
- // add it to the canvas it will appear in the upper right corner. Its
- // coordinate system matches the coordinate system of its parent
- // (the root node) at this point. But if you move this node by calling
- // node.translate() the nodes affine transform will be modified and the
- // node will appear at a different location on the screen. The node
- // coordinate system no longer matches the coordinate system of its
- // parent.
- //
- // This is useful because it means that the node's methods for
- // rendering and picking don't need to worry about the fact that
- // the node has been moved to another position on the screen, they
- // keep working just like they did when it was in the upper right
- // hand corner of the screen.
- //
- // The problem is now that each node defines its own coordinate
- // system it is difficult to compare the positions of two node with
- // each other. These methods are all meant to help solve that problem.
- //
- // The terms used in the methods are as follows:
- //
- // local - The local or base coordinate system of a node.
- // parent - The coordinate system of a node's parent
- // global - The topmost coordinate system, above the root node.
- //
- // Normally when comparing the positions of two nodes you will
- // convert the local position of each node to the global coordinate
- // system, and then compare the positions in that common coordinate
- // system.
- // ***************************************************************
-
- /**
- * Transform the given point from this node's local coordinate system to its
- * parent's local coordinate system. Note that this will modify the point
- * parameter.
- *
- * @param localPoint point in local coordinate system to be transformed.
- * @return point in parent's local coordinate system
- */
- public Point2D localToParent(final Point2D localPoint) {
- if (transform == null) {
- return localPoint;
- }
- return transform.transform(localPoint, localPoint);
- }
-
- /**
- * Transform the given dimension from this node's local coordinate system to
- * its parent's local coordinate system. Note that this will modify the
- * dimension parameter.
- *
- * @param localDimension dimension in local coordinate system to be
- * transformed.
- * @return dimension in parent's local coordinate system
- */
- public Dimension2D localToParent(final Dimension2D localDimension) {
- if (transform == null) {
- return localDimension;
- }
- return transform.transform(localDimension, localDimension);
- }
-
- /**
- * Transform the given rectangle from this node's local coordinate system to
- * its parent's local coordinate system. Note that this will modify the
- * rectangle parameter.
- *
- * @param localRectangle rectangle in local coordinate system to be
- * transformed.
- * @return rectangle in parent's local coordinate system
- */
- public Rectangle2D localToParent(final Rectangle2D localRectangle) {
- if (transform == null) {
- return localRectangle;
- }
- return transform.transform(localRectangle, localRectangle);
- }
-
- /**
- * Transform the given point from this node's parent's local coordinate
- * system to the local coordinate system of this node. Note that this will
- * modify the point parameter.
- *
- * @param parentPoint point in parent's coordinate system to be transformed.
- * @return point in this node's local coordinate system
- */
- public Point2D parentToLocal(final Point2D parentPoint) {
- if (transform == null) {
- return parentPoint;
- }
-
- return transform.inverseTransform(parentPoint, parentPoint);
- }
-
- /**
- * Transform the given dimension from this node's parent's local coordinate
- * system to the local coordinate system of this node. Note that this will
- * modify the dimension parameter.
- *
- * @param parentDimension dimension in parent's coordinate system to be
- * transformed.
- * @return dimension in this node's local coordinate system
- */
- public Dimension2D parentToLocal(final Dimension2D parentDimension) {
- if (transform == null) {
- return parentDimension;
- }
- return transform.inverseTransform(parentDimension, parentDimension);
- }
-
- /**
- * Transform the given rectangle from this node's parent's local coordinate
- * system to the local coordinate system of this node. Note that this will
- * modify the rectangle parameter.
- *
- * @param parentRectangle rectangle in parent's coordinate system to be
- * transformed.
- * @return rectangle in this node's local coordinate system
- */
- public Rectangle2D parentToLocal(final Rectangle2D parentRectangle) {
- if (transform == null) {
- return parentRectangle;
- }
- return transform.inverseTransform(parentRectangle, parentRectangle);
- }
-
- /**
- * Transform the given point from this node's local coordinate system to the
- * global coordinate system. Note that this will modify the point parameter.
- *
- * @param localPoint point in local coordinate system to be transformed.
- * @return point in global coordinates
- */
- public Point2D localToGlobal(final Point2D localPoint) {
- PNode n = this;
- while (n != null) {
- n.localToParent(localPoint);
- n = n.parent;
- }
- return localPoint;
- }
-
- /**
- * Transform the given dimension from this node's local coordinate system to
- * the global coordinate system. Note that this will modify the dimension
- * parameter.
- *
- * @param localDimension dimension in local coordinate system to be
- * transformed.
- * @return dimension in global coordinates
- */
- public Dimension2D localToGlobal(final Dimension2D localDimension) {
- PNode n = this;
- while (n != null) {
- n.localToParent(localDimension);
- n = n.parent;
- }
- return localDimension;
- }
-
- /**
- * Transform the given rectangle from this node's local coordinate system to
- * the global coordinate system. Note that this will modify the rectangle
- * parameter.
- *
- * @param localRectangle rectangle in local coordinate system to be
- * transformed.
- * @return rectangle in global coordinates
- */
- public Rectangle2D localToGlobal(final Rectangle2D localRectangle) {
- PNode n = this;
- while (n != null) {
- n.localToParent(localRectangle);
- n = n.parent;
- }
- return localRectangle;
- }
-
- /**
- * Transform the given point from global coordinates to this node's local
- * coordinate system. Note that this will modify the point parameter.
- *
- * @param globalPoint point in global coordinates to be transformed.
- * @return point in this node's local coordinate system.
- */
- public Point2D globalToLocal(final Point2D globalPoint) {
- final PAffineTransform globalTransform = computeGlobalTransform(this);
- return globalTransform.inverseTransform(globalPoint, globalPoint);
- }
-
- private PAffineTransform computeGlobalTransform(final PNode node) {
- if (node == null) {
- return new PAffineTransform();
- }
-
- final PAffineTransform parentGlobalTransform = computeGlobalTransform(node.parent);
- if (node.transform != null) {
- parentGlobalTransform.concatenate(node.transform);
- }
- return parentGlobalTransform;
- }
-
- /**
- * Transform the given dimension from global coordinates to this node's
- * local coordinate system. Note that this will modify the dimension
- * parameter.
- *
- * @param globalDimension dimension in global coordinates to be transformed.
- * @return dimension in this node's local coordinate system.
- */
- public Dimension2D globalToLocal(final Dimension2D globalDimension) {
- if (parent != null) {
- parent.globalToLocal(globalDimension);
- }
- return parentToLocal(globalDimension);
- }
-
- /**
- * Transform the given rectangle from global coordinates to this node's
- * local coordinate system. Note that this will modify the rectangle
- * parameter.
- *
- * @param globalRectangle rectangle in global coordinates to be transformed.
- * @return rectangle in this node's local coordinate system.
- */
- public Rectangle2D globalToLocal(final Rectangle2D globalRectangle) {
- if (parent != null) {
- parent.globalToLocal(globalRectangle);
- }
- return parentToLocal(globalRectangle);
- }
-
- /**
- * Return the transform that converts local coordinates at this node to the
- * global coordinate system.
- *
- * @param dest PAffineTransform to transform to global coordinates
- * @return The concatenation of transforms from the top node down to this
- * node.
- */
- public PAffineTransform getLocalToGlobalTransform(final PAffineTransform dest) {
- PAffineTransform result = dest;
- if (parent != null) {
- result = parent.getLocalToGlobalTransform(result);
- if (transform != null) {
- result.concatenate(transform);
- }
- }
- else if (dest == null) {
- result = getTransform();
- }
- else if (transform != null) {
- result.setTransform(transform);
- }
- else {
- result.setToIdentity();
- }
-
- return result;
- }
-
- /**
- * Return the transform that converts global coordinates to local
- * coordinates of this node.
- *
- * @param dest PAffineTransform to transform from global to local
- *
- * @return The inverse of the concatenation of transforms from the root down
- * to this node.
- */
- public PAffineTransform getGlobalToLocalTransform(final PAffineTransform dest) {
- PAffineTransform result = getLocalToGlobalTransform(dest);
- try {
- result.setTransform(result.createInverse());
- }
- catch (final NoninvertibleTransformException e) {
- throw new PAffineTransformException(e, result);
- }
- return result;
- }
-
- // ****************************************************************
- // Event Listeners - Methods for adding and removing event listeners
- // from a node.
- //
- // Here methods are provided to add property change listeners and
- // input event listeners. The property change listeners are notified
- // when certain properties of this node change, and the input event
- // listeners are notified when the nodes receives new key and mouse
- // events.
- // ****************************************************************
-
- /**
- * Return the list of event listeners associated with this node.
- *
- * @return event listener list or null
- */
- public EventListenerList getListenerList() {
- return listenerList;
- }
-
- /**
- * Adds the specified input event listener to receive input events from this
- * node.
- *
- * @param listener the new input listener
- */
- public void addInputEventListener(final PInputEventListener listener) {
- if (listenerList == null) {
- listenerList = new EventListenerList();
- }
- getListenerList().add(PInputEventListener.class, listener);
- }
-
- /**
- * Removes the specified input event listener so that it no longer receives
- * input events from this node.
- *
- * @param listener the input listener to remove
- */
- public void removeInputEventListener(final PInputEventListener listener) {
- if (listenerList == null) {
- return;
- }
- getListenerList().remove(PInputEventListener.class, listener);
- if (listenerList.getListenerCount() == 0) {
- listenerList = null;
- }
- }
-
- /**
- * Add a PropertyChangeListener to the listener list. The listener is
- * registered for all properties. See the fields in PNode and subclasses
- * that start with PROPERTY_ to find out which properties exist.
- *
- * @param listener The PropertyChangeListener to be added
- */
- public void addPropertyChangeListener(final PropertyChangeListener listener) {
- if (changeSupport == null) {
- changeSupport = new SwingPropertyChangeSupport(this);
- }
- changeSupport.addPropertyChangeListener(listener);
- }
-
- /**
- * Add a PropertyChangeListener for a specific property. The listener will
- * be invoked only when a call on firePropertyChange names that specific
- * property. See the fields in PNode and subclasses that start with
- * PROPERTY_ to find out which properties are supported.
- *
- * @param propertyName The name of the property to listen on.
- * @param listener The PropertyChangeListener to be added
- */
- public void addPropertyChangeListener(final String propertyName, final PropertyChangeListener listener) {
- if (listener == null) {
- return;
- }
- if (changeSupport == null) {
- changeSupport = new SwingPropertyChangeSupport(this);
- }
- changeSupport.addPropertyChangeListener(propertyName, listener);
- }
-
- /**
- * Remove a PropertyChangeListener from the listener list. This removes a
- * PropertyChangeListener that was registered for all properties.
- *
- * @param listener The PropertyChangeListener to be removed
- */
- public void removePropertyChangeListener(final PropertyChangeListener listener) {
- if (changeSupport != null) {
- changeSupport.removePropertyChangeListener(listener);
- }
- }
-
- /**
- * Remove a PropertyChangeListener for a specific property.
- *
- * @param propertyName The name of the property that was listened on.
- * @param listener The PropertyChangeListener to be removed
- */
- public void removePropertyChangeListener(final String propertyName, final PropertyChangeListener listener) {
- if (listener == null) {
- return;
- }
- if (changeSupport == null) {
- return;
- }
- changeSupport.removePropertyChangeListener(propertyName, listener);
- }
-
- /**
- * Return the propertyChangeParentMask that determines which property change
- * events are forwared to this nodes parent so that its property change
- * listeners will also be notified.
- *
- * @return mask used for deciding whether to bubble property changes up to
- * parent
- */
- public int getPropertyChangeParentMask() {
- return propertyChangeParentMask;
- }
-
- /**
- * Set the propertyChangeParentMask that determines which property change
- * events are forwared to this nodes parent so that its property change
- * listeners will also be notified.
- *
- * @param propertyChangeParentMask new mask for property change bubble up
- */
- public void setPropertyChangeParentMask(final int propertyChangeParentMask) {
- this.propertyChangeParentMask = propertyChangeParentMask;
- }
-
- /**
- * Report a bound property update to any registered listeners. No event is
- * fired if old and new are equal and non-null. If the propertyCode exists
- * in this node's propertyChangeParentMask then a property change event will
- * also be fired on this nodes parent.
- *
- * @param propertyCode The code of the property changed.
- * @param propertyName The name of the property that was changed.
- * @param oldValue The old value of the property.
- * @param newValue The new value of the property.
- */
- protected void firePropertyChange(final int propertyCode, final String propertyName, final Object oldValue,
- final Object newValue) {
- PropertyChangeEvent event = null;
-
- if (changeSupport != null) {
- event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
- changeSupport.firePropertyChange(event);
- }
- if (parent != null && (propertyCode & propertyChangeParentMask) != 0) {
- if (event == null) {
- event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
- }
- parent.fireChildPropertyChange(event, propertyCode);
- }
- }
-
- /**
- * Called by child node to forward property change events up the node tree
- * so that property change listeners registered with this node will be
- * notified of property changes of its children nodes. For performance
- * reason only propertyCodes listed in the propertyChangeParentMask are
- * forwarded.
- *
- * @param event The property change event containing source node and changed
- * values.
- * @param propertyCode The code of the property changed.
- */
- protected void fireChildPropertyChange(final PropertyChangeEvent event, final int propertyCode) {
- if (changeSupport != null) {
- changeSupport.firePropertyChange(event);
- }
- if (parent != null && (propertyCode & propertyChangeParentMask) != 0) {
- parent.fireChildPropertyChange(event, propertyCode);
- }
- }
-
- // ****************************************************************
- // Bounds Geometry - Methods for setting and querying the bounds
- // of this node.
- //
- // The bounds of a node store the node's position and size in
- // the nodes local coordinate system. Many node subclasses will need
- // to override the setBounds method so that they can update their
- // internal state appropriately. See PPath for an example.
- //
- // Since the bounds are stored in the local coordinate system
- // they WILL NOT change if the node is scaled, translated, or rotated.
- //
- // The bounds may be accessed with either getBounds, or
- // getBoundsReference. The former returns a copy of the bounds
- // the latter returns a reference to the nodes bounds that should
- // normally not be modified. If a node is marked as volatile then
- // it may modify its bounds before returning them from getBoundsReference,
- // otherwise it may not.
- // ****************************************************************
-
- /**
- * Return a copy of this node's bounds. These bounds are stored in the local
- * coordinate system of this node and do not include the bounds of any of
- * this node's children.
- *
- * @return copy of this node's local bounds
- */
- public PBounds getBounds() {
- return (PBounds) getBoundsReference().clone();
- }
-
- /**
- * Return a direct reference to this node's bounds. These bounds are stored
- * in the local coordinate system of this node and do not include the bounds
- * of any of this node's children. The value returned should not be
- * modified.
- *
- * @return direct reference to local bounds
- */
- public PBounds getBoundsReference() {
- return bounds;
- }
-
- /**
- * Notify this node that you will begin to repeatedly call setBounds
- *
. When you
- * are done call endResizeBounds
to let the node know that you
- * are done.
- */
- public void startResizeBounds() {
- }
-
- /**
- * Notify this node that you have finished a resize bounds sequence.
- */
- public void endResizeBounds() {
- }
-
- /**
- * Set's this node's bounds left position, leaving y, width, and height
- * unchanged.
- *
- * @param x new x position of bounds
- *
- * @return whether the change was successful
- */
- public boolean setX(final double x) {
- return setBounds(x, getY(), getWidth(), getHeight());
- }
-
- /**
- * Set's this node's bounds top position, leaving x, width, and height
- * unchanged.
- *
- * @param y new y position of bounds
- *
- * @return whether the change was successful
- */
- public boolean setY(final double y) {
- return setBounds(getX(), y, getWidth(), getHeight());
- }
-
- /**
- * Set's this node's bounds width, leaving x, y, and height unchanged.
- *
- * @param width new width position of bounds
- *
- * @return whether the change was successful
- */
- public boolean setWidth(final double width) {
- return setBounds(getX(), getY(), width, getHeight());
- }
-
- /**
- * Set's this node's bounds height, leaving x, y, and width unchanged.
- *
- * @param height new height position of bounds
- *
- * @return whether the change was successful
- */
- public boolean setHeight(final double height) {
- return setBounds(getX(), getY(), getWidth(), height);
- }
-
- /**
- * Set the bounds of this node to the given value. These bounds are stored
- * in the local coordinate system of this node.
- *
- * @param newBounds bounds to apply to this node
- *
- * @return true if the bounds changed.
- */
- public boolean setBounds(final Rectangle2D newBounds) {
- return setBounds(newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight());
- }
-
- /**
- * Set the bounds of this node to the given position and size. These bounds
- * are stored in the local coordinate system of this node.
- *
- * If the width or height is less then or equal to zero then the bound's
- * empty bit will be set to true.
- *
- * Subclasses must call the super.setBounds() method.
- *
- * @param x x position of bounds
- * @param y y position of bounds
- * @param width width to apply to the bounds
- * @param height height to apply to the bounds
- *
- * @return true if the bounds changed.
- */
- public boolean setBounds(final double x, final double y, final double width, final double height) {
- if (bounds.x != x || bounds.y != y || bounds.width != width || bounds.height != height) {
- bounds.setRect(x, y, width, height);
-
- if (width <= 0 || height <= 0) {
- bounds.reset();
- }
-
- internalUpdateBounds(x, y, width, height);
- invalidatePaint();
- signalBoundsChanged();
- return true;
- }
- // Don't put any invalidating code here or else nodes with volatile
- // bounds will
- // create a soft infinite loop (calling Swing.invokeLater()) when they
- // validate
- // their bounds.
- return false;
- }
-
- /**
- * Gives nodes a chance to update their internal structure before bounds
- * changed notifications are sent. When this message is recived the nodes
- * bounds field will contain the new value.
- *
- * See PPath for an example that uses this method.
- *
- * @param x x position of bounds
- * @param y y position of bounds
- * @param width width to apply to the bounds
- * @param height height to apply to the bounds
- */
- protected void internalUpdateBounds(final double x, final double y, final double width, final double height) {
- }
-
- /**
- * Set the empty bit of this bounds to true.
- */
- public void resetBounds() {
- setBounds(0, 0, 0, 0);
- }
-
- /**
- * Return the x position (in local coords) of this node's bounds.
- *
- * @return local x position of bounds
- */
- public double getX() {
- return getBoundsReference().getX();
- }
-
- /**
- * Return the y position (in local coords) of this node's bounds.
- *
- * @return local y position of bounds
- */
- public double getY() {
- return getBoundsReference().getY();
- }
-
- /**
- * Return the width (in local coords) of this node's bounds.
- *
- * @return local width of bounds
- */
- public double getWidth() {
- return getBoundsReference().getWidth();
- }
-
- /**
- * Return the height (in local coords) of this node's bounds.
- *
- * @return local width of bounds
- */
- public double getHeight() {
- return getBoundsReference().getHeight();
- }
-
- /**
- * Return a copy of the bounds of this node in the global coordinate system.
- *
- * @return the bounds in global coordinate system.
- */
- public PBounds getGlobalBounds() {
- return (PBounds) localToGlobal(getBounds());
- }
-
- /**
- * Center the bounds of this node so that they are centered on the given
- * point specified on the local coordinates of this node. Note that this
- * method will modify the nodes bounds, while centerFullBoundsOnPoint will
- * modify the nodes transform.
- *
- * @param localX x position of point around which to center bounds
- * @param localY y position of point around which to center bounds
- *
- * @return true if the bounds changed.
- */
- public boolean centerBoundsOnPoint(final double localX, final double localY) {
- final double dx = localX - bounds.getCenterX();
- final double dy = localY - bounds.getCenterY();
- return setBounds(bounds.x + dx, bounds.y + dy, bounds.width, bounds.height);
- }
-
- /**
- * Center the full bounds of this node so that they are centered on the
- * given point specified on the local coordinates of this nodes parent. Note
- * that this method will modify the nodes transform, while
- * centerBoundsOnPoint will modify the nodes bounds.
- *
- * @param parentX x position around which to center full bounds
- * @param parentY y position around which to center full bounds
- */
- public void centerFullBoundsOnPoint(final double parentX, final double parentY) {
- final double dx = parentX - getFullBoundsReference().getCenterX();
- final double dy = parentY - getFullBoundsReference().getCenterY();
- offset(dx, dy);
- }
-
- /**
- * Return true if this node intersects the given rectangle specified in
- * local bounds. If the geometry of this node is complex this method can
- * become expensive, it is therefore recommended that
- * fullIntersects
is used for quick rejects before calling this
- * method.
- *
- * @param localBounds the bounds to test for intersection against
- * @return true if the given rectangle intersects this nodes geometry.
- */
- public boolean intersects(final Rectangle2D localBounds) {
- if (localBounds == null) {
- return true;
- }
- return getBoundsReference().intersects(localBounds);
- }
-
- // ****************************************************************
- // Full Bounds - Methods for computing and querying the
- // full bounds of this node.
- //
- // The full bounds of a node store the nodes bounds
- // together with the union of the bounds of all the
- // node's descendants. The full bounds are stored in the parent
- // coordinate system of this node, the full bounds DOES change
- // when you translate, scale, or rotate this node.
- //
- // The full bounds may be accessed with either getFullBounds, or
- // getFullBoundsReference. The former returns a copy of the full bounds
- // the latter returns a reference to the node's full bounds that should
- // not be modified.
- // ****************************************************************
-
- /**
- * Return a copy of this node's full bounds. These bounds are stored in the
- * parent coordinate system of this node and they include the union of this
- * node's bounds and all the bounds of it's descendants.
- *
- * @return a copy of this node's full bounds.
- */
- public PBounds getFullBounds() {
- return (PBounds) getFullBoundsReference().clone();
- }
-
- /**
- * Return a reference to this node's full bounds cache. These bounds are
- * stored in the parent coordinate system of this node and they include the
- * union of this node's bounds and all the bounds of it's descendants. The
- * bounds returned by this method should not be modified.
- *
- * @return a reference to this node's full bounds cache.
- */
- public PBounds getFullBoundsReference() {
- validateFullBounds();
- return fullBoundsCache;
- }
-
- /**
- * Compute and return the full bounds of this node. If the dstBounds
- * parameter is not null then it will be used to return the results instead
- * of creating a new PBounds.
- *
- * @param dstBounds if not null the new bounds will be stored here
- * @return the full bounds in the parent coordinate system of this node
- */
- public PBounds computeFullBounds(final PBounds dstBounds) {
- final PBounds result = getUnionOfChildrenBounds(dstBounds);
- result.add(getBoundsReference());
- localToParent(result);
- return result;
- }
-
- /**
- * Compute and return the union of the full bounds of all the children of
- * this node. If the dstBounds parameter is not null then it will be used to
- * return the results instead of creating a new PBounds.
- *
- * @param dstBounds if not null the new bounds will be stored here
- * @return union of children bounds
- */
- public PBounds getUnionOfChildrenBounds(final PBounds dstBounds) {
- PBounds resultBounds;
- if (dstBounds == null) {
- resultBounds = new PBounds();
- }
- else {
- resultBounds = dstBounds;
- resultBounds.resetToZero();
- }
-
- final int count = getChildrenCount();
- for (int i = 0; i < count; i++) {
- final PNode each = (PNode) children.get(i);
- resultBounds.add(each.getFullBoundsReference());
- }
-
- return resultBounds;
- }
-
- /**
- * Return a copy of the full bounds of this node in the global coordinate
- * system.
- *
- * @return the full bounds in global coordinate system.
- */
- public PBounds getGlobalFullBounds() {
- final PBounds b = getFullBounds();
- if (parent != null) {
- parent.localToGlobal(b);
- }
- return b;
- }
-
- /**
- * Return true if the full bounds of this node intersects with the specified
- * bounds.
- *
- * @param parentBounds the bounds to test for intersection against
- * (specified in parent's coordinate system)
- * @return true if this nodes full bounds intersect the given bounds.
- */
- public boolean fullIntersects(final Rectangle2D parentBounds) {
- if (parentBounds == null) {
- return true;
- }
- return getFullBoundsReference().intersects(parentBounds);
- }
-
- // ****************************************************************
- // Bounds Damage Management - Methods used to invalidate and validate
- // the bounds of nodes.
- // ****************************************************************
-
- /**
- * Return true if this nodes bounds may change at any time. The default
- * behavior is to return false, subclasses that override this method to
- * return true should also override getBoundsReference() and compute their
- * volatile bounds there before returning the reference.
- *
- * @return true if this node has volatile bounds
- */
- protected boolean getBoundsVolatile() {
- return false;
- }
-
- /**
- * Return true if this node has a child with volatile bounds.
- *
- * @return true if this node has a child with volatile bounds
- */
- protected boolean getChildBoundsVolatile() {
- return childBoundsVolatile;
- }
-
- /**
- * Set if this node has a child with volatile bounds. This should normally
- * be managed automatically by the bounds validation process.
- *
- * @param childBoundsVolatile true if this node has a descendant with
- * volatile bounds
- */
- protected void setChildBoundsVolatile(final boolean childBoundsVolatile) {
- this.childBoundsVolatile = childBoundsVolatile;
- }
-
- /**
- * Return true if this node's bounds have recently changed. This flag will
- * be reset on the next call of validateFullBounds.
- *
- * @return true if this node's bounds have changed.
- */
- protected boolean getBoundsChanged() {
- return boundsChanged;
- }
-
- /**
- * Set the bounds chnaged flag. This flag will be reset on the next call of
- * validateFullBounds.
- *
- * @param boundsChanged true if this nodes bounds have changed.
- */
- protected void setBoundsChanged(final boolean boundsChanged) {
- this.boundsChanged = boundsChanged;
- }
-
- /**
- * Return true if the full bounds of this node are invalid. This means that
- * the full bounds of this node have changed and need to be recomputed.
- *
- * @return true if the full bounds of this node are invalid
- */
- protected boolean getFullBoundsInvalid() {
- return fullBoundsInvalid;
- }
-
- /**
- * Set the full bounds invalid flag. This flag is set when the full bounds
- * of this node need to be recomputed as is the case when this node is
- * transformed or when one of this node's children changes geometry.
- *
- * @param fullBoundsInvalid true=invalid, false=valid
- */
- protected void setFullBoundsInvalid(final boolean fullBoundsInvalid) {
- this.fullBoundsInvalid = fullBoundsInvalid;
- }
-
- /**
- * Return true if one of this node's descendants has invalid bounds.
- *
- * @return whether child bounds are invalid
- */
- protected boolean getChildBoundsInvalid() {
- return childBoundsInvalid;
- }
-
- /**
- * Set the flag indicating that one of this node's descendants has invalid
- * bounds.
- *
- * @param childBoundsInvalid true=invalid, false=valid
- */
- protected void setChildBoundsInvalid(final boolean childBoundsInvalid) {
- this.childBoundsInvalid = childBoundsInvalid;
- }
-
- /**
- * This method should be called when the bounds of this node are changed. It
- * invalidates the full bounds of this node, and also notifies each of this
- * nodes children that their parent's bounds have changed. As a result of
- * this method getting called this nodes layoutChildren will be called.
- */
- public void signalBoundsChanged() {
- invalidateFullBounds();
- setBoundsChanged(true);
- firePropertyChange(PROPERTY_CODE_BOUNDS, PROPERTY_BOUNDS, null, bounds);
-
- final int count = getChildrenCount();
- for (int i = 0; i < count; i++) {
- final PNode each = (PNode) children.get(i);
- each.parentBoundsChanged();
- }
- }
-
- /**
- * Invalidate this node's layout, so that later layoutChildren will get
- * called.
- */
- public void invalidateLayout() {
- invalidateFullBounds();
- }
-
- /**
- * A notification that the bounds of this node's parent have changed.
- */
- protected void parentBoundsChanged() {
- }
-
- /**
- * Invalidates the full bounds of this node, and sets the child bounds
- * invalid flag on each of this node's ancestors.
- */
- public void invalidateFullBounds() {
- setFullBoundsInvalid(true);
-
- PNode n = parent;
- while (n != null && !n.getChildBoundsInvalid()) {
- n.setChildBoundsInvalid(true);
- n = n.parent;
- }
-
- if (SCENE_GRAPH_DELEGATE != null) {
- SCENE_GRAPH_DELEGATE.nodeFullBoundsInvalidated(this);
- }
- }
-
- /**
- * This method is called to validate the bounds of this node and all of its
- * descendants. It returns true if this nodes bounds or the bounds of any of
- * its descendants are marked as volatile.
- *
- * @return true if this node or any of its descendants have volatile bounds
- */
- protected boolean validateFullBounds() {
- final boolean boundsVolatile = getBoundsVolatile();
-
- // 1. Only compute new bounds if invalid flags are set.
- if (fullBoundsInvalid || childBoundsInvalid || boundsVolatile || childBoundsVolatile) {
-
- // 2. If my bounds are volatile and they have not been changed then
- // signal a change.
- //
- // For most cases this will do nothing, but if a nodes bounds depend
- // on its model, then
- // validate bounds has the responsibility of making the bounds match
- // the models value.
- // For example PPaths validateBounds method makes sure that the
- // bounds are equal to the
- // bounds of the GeneralPath model.
- if (boundsVolatile && !boundsChanged) {
- signalBoundsChanged();
- }
-
- // 3. If the bounds of on of my decendents are invalidate then
- // validate the bounds of all of my children.
- if (childBoundsInvalid || childBoundsVolatile) {
- childBoundsVolatile = false;
- final int count = getChildrenCount();
- for (int i = 0; i < count; i++) {
- final PNode each = (PNode) children.get(i);
- childBoundsVolatile |= each.validateFullBounds();
- }
- }
-
- // 4. Now that my children's bounds are valid and my own bounds are
- // valid run any layout algorithm here. Note that if you try to
- // layout volatile
- // children piccolo will most likely start a "soft" infinite loop.
- // It won't freeze
- // your program, but it will make an infinite number of calls to
- // SwingUtilities
- // invoke later. You don't want to do that.
- layoutChildren();
-
- // 5. If the full bounds cache is invalid then recompute the full
- // bounds cache here after our own bounds and the children's bounds
- // have been computed above.
- if (fullBoundsInvalid) {
- // 6. This will call getFullBoundsReference on all of the
- // children. So if the above
- // layoutChildren method changed the bounds of any of the
- // children they will be
- // validated again here.
- PBounds oldFullBoundsCache = fullBoundsCache;
- fullBoundsCache = computeFullBounds(fullBoundsCache);
-
- final boolean fullBoundsChanged = !oldFullBoundsCache.equals(fullBoundsCache);
-
- // 7. If the new full bounds cache differs from the previous
- // cache then
- // tell our parent to invalidate their full bounds. This is how
- // bounds changes
- // deep in the tree percolate up.
- if (fullBoundsChanged) {
- if (parent != null) {
- parent.invalidateFullBounds();
- }
-
- firePropertyChange(PROPERTY_CODE_FULL_BOUNDS, PROPERTY_FULL_BOUNDS, null, fullBoundsCache);
-
- // 8. If our paint was invalid make sure to repaint our old
- // full bounds. The
- // new bounds will be computed later in the validatePaint
- // pass.
- if (paintInvalid && !oldFullBoundsCache.isEmpty()) {
- TEMP_REPAINT_BOUNDS.setRect(oldFullBoundsCache.x, oldFullBoundsCache.getY(), oldFullBoundsCache
- .getWidth(), oldFullBoundsCache.getHeight());
- repaintFrom(TEMP_REPAINT_BOUNDS, this);
- }
- }
- }
-
- // 9. Clear the invalid bounds flags.
- boundsChanged = false;
- fullBoundsInvalid = false;
- childBoundsInvalid = false;
- }
-
- return boundsVolatile || childBoundsVolatile;
- }
-
- /**
- * Nodes that apply layout constraints to their children should override
- * this method and do the layout there.
- */
- protected void layoutChildren() {
- }
-
- // ****************************************************************
- // Node Transform - Methods to manipulate the node's transform.
- //
- // Each node has a transform that is used to define the nodes
- // local coordinate system. IE it is applied before picking and
- // rendering the node.
- //
- // The usual way to move nodes about on the canvas is to manipulate
- // this transform, as opposed to changing the bounds of the
- // node.
- //
- // Since this transform defines the local coordinate system of this
- // node the following methods with affect the global position both
- // this node and all of its descendants.
- // ****************************************************************
-
- /**
- * Returns the rotation applied by this node's transform in radians. This
- * rotation affects this node and all its descendants. The value returned
- * will be between 0 and 2pi radians.
- *
- * @return rotation in radians.
- */
- public double getRotation() {
- if (transform == null) {
- return 0;
- }
- return transform.getRotation();
- }
-
- /**
- * Sets the rotation of this nodes transform in radians. This will affect
- * this node and all its descendents.
- *
- * @param theta rotation in radians
- */
- public void setRotation(final double theta) {
- rotate(theta - getRotation());
- }
-
- /**
- * Rotates this node by theta (in radians) about the 0,0 point. This will
- * affect this node and all its descendants.
- *
- * @param theta the amount to rotate by in radians
- */
- public void rotate(final double theta) {
- rotateAboutPoint(theta, 0, 0);
- }
-
- /**
- * Rotates this node by theta (in radians), and then translates the node so
- * that the x, y position of its fullBounds stays constant.
- *
- * @param theta the amount to rotate by in radians
- */
- public void rotateInPlace(final double theta) {
- PBounds b = getFullBoundsReference();
- final double px = b.x;
- final double py = b.y;
- rotateAboutPoint(theta, 0, 0);
- b = getFullBoundsReference();
- offset(px - b.x, py - b.y);
- }
-
- /**
- * Rotates this node by theta (in radians) about the given point. This will
- * affect this node and all its descendants.
- *
- * @param theta the amount to rotate by in radians
- * @param point the point about which to rotate
- */
- public void rotateAboutPoint(final double theta, final Point2D point) {
- rotateAboutPoint(theta, point.getX(), point.getY());
- }
-
- /**
- * Rotates this node by theta (in radians) about the given point. This will
- * affect this node and all its descendants.
- *
- * @param theta the amount to rotate by in radians
- * @param x the x coordinate of the point around which to rotate
- * @param y the y coordinate of the point around which to rotate
- */
- public void rotateAboutPoint(final double theta, final double x, final double y) {
- getTransformReference(true).rotate(theta, x, y);
- invalidatePaint();
- invalidateFullBounds();
- firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
- }
-
- /**
- * Return the total amount of rotation applied to this node by its own
- * transform together with the transforms of all its ancestors. The value
- * returned will be between 0 and 2pi radians.
- *
- * @return the total amount of rotation applied to this node in radians
- */
- public double getGlobalRotation() {
- return getLocalToGlobalTransform(null).getRotation();
- }
-
- /**
- * Set the global rotation (in radians) of this node. This is implemented by
- * rotating this nodes transform the required amount so that the nodes
- * global rotation is as requested.
- *
- * @param theta the amount to rotate by in radians relative to the global
- * coordinate system.
- */
- public void setGlobalRotation(final double theta) {
- if (parent != null) {
- setRotation(theta - parent.getGlobalRotation());
- }
- else {
- setRotation(theta);
- }
- }
-
- /**
- * Return the scale applied by this node's transform. The scale is effecting
- * this node and all its descendants.
- *
- * @return scale applied by this nodes transform.
- */
- public double getScale() {
- if (transform == null) {
- return 1;
- }
- return transform.getScale();
- }
-
- /**
- * Set the scale of this node's transform. The scale will affect this node
- * and all its descendants.
- *
- * @param scale the scale to set the transform to
- */
- public void setScale(final double scale) {
- if (scale == 0) {
- throw new RuntimeException("Can't set scale to 0");
- }
- scale(scale / getScale());
- }
-
- /**
- * Scale this nodes transform by the given amount. This will affect this
- * node and all of its descendants.
- *
- * @param scale the amount to scale by
- */
- public void scale(final double scale) {
- scaleAboutPoint(scale, 0, 0);
- }
-
- /**
- * Scale this nodes transform by the given amount about the specified point.
- * This will affect this node and all of its descendants.
- *
- * @param scale the amount to scale by
- * @param point the point to scale about
- */
- public void scaleAboutPoint(final double scale, final Point2D point) {
- scaleAboutPoint(scale, point.getX(), point.getY());
- }
-
- /**
- * Scale this nodes transform by the given amount about the specified point.
- * This will affect this node and all of its descendants.
- *
- * @param scale the amount to scale by
- * @param x the x coordinate of the point around which to scale
- * @param y the y coordinate of the point around which to scale
- */
- public void scaleAboutPoint(final double scale, final double x, final double y) {
- getTransformReference(true).scaleAboutPoint(scale, x, y);
- invalidatePaint();
- invalidateFullBounds();
- firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
- }
-
- /**
- * Return the global scale that is being applied to this node by its
- * transform together with the transforms of all its ancestors.
- *
- * @return global scale of this node
- */
- public double getGlobalScale() {
- return getLocalToGlobalTransform(null).getScale();
- }
-
- /**
- * Set the global scale of this node. This is implemented by scaling this
- * nodes transform the required amount so that the nodes global scale is as
- * requested.
- *
- * @param scale the desired global scale
- */
- public void setGlobalScale(final double scale) {
- if (parent != null) {
- setScale(scale / parent.getGlobalScale());
- }
- else {
- setScale(scale);
- }
- }
-
- /**
- * Returns the x offset of this node as applied by its transform.
- *
- * @return x offset of this node as applied by its transform
- */
- public double getXOffset() {
- if (transform == null) {
- return 0;
- }
- return transform.getTranslateX();
- }
-
- /**
- * Returns the y offset of this node as applied by its transform.
- *
- * @return y offset of this node as applied by its transform
- */
- public double getYOffset() {
- if (transform == null) {
- return 0;
- }
- return transform.getTranslateY();
- }
-
- /**
- * Return the offset that is being applied to this node by its transform.
- * This offset effects this node and all of its descendants and is specified
- * in the parent coordinate system. This returns the values that are in the
- * m02 and m12 positions in the affine transform.
- *
- * @return a point representing the x and y offset
- */
- public Point2D getOffset() {
- if (transform == null) {
- return new Point2D.Double();
- }
- return new Point2D.Double(transform.getTranslateX(), transform.getTranslateY());
- }
-
- /**
- * Set the offset that is being applied to this node by its transform. This
- * offset effects this node and all of its descendants and is specified in
- * the nodes parent coordinate system. This directly sets the values of the
- * m02 and m12 positions in the affine transform. Unlike "PNode.translate()"
- * it is not effected by the transforms scale.
- *
- * @param point value of new offset
- */
- public void setOffset(final Point2D point) {
- setOffset(point.getX(), point.getY());
- }
-
- /**
- * Set the offset that is being applied to this node by its transform. This
- * offset effects this node and all of its descendants and is specified in
- * the nodes parent coordinate system. This directly sets the values of the
- * m02 and m12 positions in the affine transform. Unlike "PNode.translate()"
- * it is not effected by the transforms scale.
- *
- * @param x amount of x offset
- * @param y amount of y offset
- */
- public void setOffset(final double x, final double y) {
- getTransformReference(true).setOffset(x, y);
- invalidatePaint();
- invalidateFullBounds();
- firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
- }
-
- /**
- * Offset this node relative to the parents coordinate system, and is NOT
- * effected by this nodes current scale or rotation. This is implemented by
- * directly adding dx to the m02 position and dy to the m12 position in the
- * affine transform.
- *
- * @param dx amount to add to this nodes current x Offset
- * @param dy amount to add to this nodes current y Offset
- */
- public void offset(final double dx, final double dy) {
- getTransformReference(true);
- setOffset(transform.getTranslateX() + dx, transform.getTranslateY() + dy);
- }
-
- /**
- * Translate this node's transform by the given amount, using the standard
- * affine transform translate method. This translation effects this node and
- * all of its descendants.
- *
- * @param dx amount to add to this nodes current x translation
- * @param dy amount to add to this nodes current y translation
- */
- public void translate(final double dx, final double dy) {
- getTransformReference(true).translate(dx, dy);
- invalidatePaint();
- invalidateFullBounds();
- firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
- }
-
- /**
- * Return the global translation that is being applied to this node by its
- * transform together with the transforms of all its ancestors.
- *
- * @return the global translation applied to this node
- */
- public Point2D getGlobalTranslation() {
- final Point2D p = getOffset();
- if (parent != null) {
- parent.localToGlobal(p);
- }
- return p;
- }
-
- /**
- * Set the global translation of this node. This is implemented by
- * translating this nodes transform the required amount so that the nodes
- * global scale is as requested.
- *
- * @param globalPoint the desired global translation
- */
- public void setGlobalTranslation(final Point2D globalPoint) {
- if (parent != null) {
- parent.getGlobalToLocalTransform(null).transform(globalPoint, globalPoint);
- }
- setOffset(globalPoint);
- }
-
- /**
- * Transform this nodes transform by the given transform.
- *
- * @param aTransform the transform to apply.
- */
- public void transformBy(final AffineTransform aTransform) {
- getTransformReference(true).concatenate(aTransform);
- invalidatePaint();
- invalidateFullBounds();
- firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
- }
-
- /**
- * Linearly interpolates between a and b, based on t. Specifically, it
- * computes lerp(a, b, t) = a + t*(b - a). This produces a result that
- * changes from a (when t = 0) to b (when t = 1).
- *
- * @param t variable 'time' parameter
- * @param a from point
- * @param b to Point
- *
- * @return linear interpolation between and b at time interval t (given as #
- * between 0f and 1f)
- */
- public static double lerp(final double t, final double a, final double b) {
- return a + t * (b - a);
- }
-
- /**
- * This will calculate the necessary transform in order to make this node
- * appear at a particular position relative to the specified bounding box.
- * The source point specifies a point in the unit square (0, 0) - (1, 1)
- * that represents an anchor point on the corresponding node to this
- * transform. The destination point specifies an anchor point on the
- * reference node. The position method then computes the transform that
- * results in transforming this node so that the source anchor point
- * coincides with the reference anchor point. This can be useful for layout
- * algorithms as it is straightforward to position one object relative to
- * another.
- *
- * For example, If you have two nodes, A and B, and you call - * - *
- * Point2D srcPt = new Point2D.Double(1.0, 0.0); - * Point2D destPt = new Point2D.Double(0.0, 0.0); - * A.position(srcPt, destPt, B.getGlobalBounds(), 750, null); - *- * - * The result is that A will move so that its upper-right corner is at the - * same place as the upper-left corner of B, and the transition will be - * smoothly animated over a period of 750 milliseconds. - * - * @since 1.3 - * @param srcPt The anchor point on this transform's node (normalized to a - * unit square) - * @param destPt The anchor point on destination bounds (normalized to a - * unit square) - * @param destBounds The bounds (in global coordinates) used to calculate - * this transform's node - * @param millis Number of milliseconds over which to perform the animation - * - * @return newly scheduled activity or node if activity could not be - * scheduled - */ - public PActivity animateToRelativePosition(final Point2D srcPt, final Point2D destPt, final Rectangle2D destBounds, - final int millis) { - double srcx, srcy; - double destx, desty; - double dx, dy; - Point2D pt1, pt2; - - if (parent == null) { - return null; - } - else { - // First compute translation amount in global coordinates - final Rectangle2D srcBounds = getGlobalFullBounds(); - srcx = lerp(srcPt.getX(), srcBounds.getX(), srcBounds.getX() + srcBounds.getWidth()); - srcy = lerp(srcPt.getY(), srcBounds.getY(), srcBounds.getY() + srcBounds.getHeight()); - destx = lerp(destPt.getX(), destBounds.getX(), destBounds.getX() + destBounds.getWidth()); - desty = lerp(destPt.getY(), destBounds.getY(), destBounds.getY() + destBounds.getHeight()); - - // Convert vector to local coordinates - pt1 = new Point2D.Double(srcx, srcy); - globalToLocal(pt1); - pt2 = new Point2D.Double(destx, desty); - globalToLocal(pt2); - dx = pt2.getX() - pt1.getX(); - dy = pt2.getY() - pt1.getY(); - - // Finally, animate change - final PAffineTransform at = new PAffineTransform(getTransformReference(true)); - at.translate(dx, dy); - return animateToTransform(at, millis); - } - } - - /** - * Return a copy of the transform associated with this node. - * - * @return copy of this node's transform - */ - public PAffineTransform getTransform() { - if (transform == null) { - return new PAffineTransform(); - } - else { - return (PAffineTransform) transform.clone(); - } - } - - /** - * Return a reference to the transform associated with this node. This - * returned transform should not be modified. PNode transforms are created - * lazily when needed. If you access the transform reference before the - * transform has been created it may return null. The - * createNewTransformIfNull parameter is used to specify that the PNode - * should create a new transform (and assign that transform to the nodes - * local transform variable) instead of returning null. - * - * @param createNewTransformIfNull if the transform has not been - * initialised, should it be? - * - * @return reference to this node's transform - */ - public PAffineTransform getTransformReference(final boolean createNewTransformIfNull) { - if (transform == null && createNewTransformIfNull) { - transform = new PAffineTransform(); - } - return transform; - } - - /** - * Return an inverted copy of the transform associated with this node. - * - * @return inverted copy of this node's transform - */ - public PAffineTransform getInverseTransform() { - if (transform == null) { - return new PAffineTransform(); - } - - try { - return new PAffineTransform(transform.createInverse()); - } - catch (final NoninvertibleTransformException e) { - throw new PAffineTransformException(e, transform); - } - } - - /** - * Set the transform applied to this node. - * - * @param transform the new transform value - */ - public void setTransform(final AffineTransform transform) { - if (transform == null) { - this.transform = null; - } - else { - getTransformReference(true).setTransform(transform); - } - - invalidatePaint(); - invalidateFullBounds(); - firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, this.transform); - } - - // **************************************************************** - // Paint Damage Management - Methods used to invalidate the areas of - // the screen that this node appears in so that they will later get - // painted. - // - // Generally you will not need to call these invalidate methods - // when starting out with Piccolo2d because methods such as setPaint - // already automatically call them for you. You will need to call - // them when you start creating your own nodes. - // - // When you do create you own nodes the only method that you will - // normally need to call is invalidatePaint. This method marks the - // nodes as having invalid paint, the root node's UI cycle will then - // later discover this damage and report it to the Java repaint manager. - // - // Repainting is normally done with PNode.invalidatePaint() instead of - // directly calling PNode.repaint() because PNode.repaint() requires - // the nodes bounds to be computed right away. But with invalidatePaint - // the bounds computation can be delayed until the end of the root's UI - // cycle, and this can add up to a bit savings when modifying a - // large number of nodes all at once. - // - // The other methods here will rarely be called except internally - // from the framework. - // **************************************************************** - - /** - * Return true if this nodes paint is invalid, in which case the node needs - * to be repainted. - * - * @return true if this node needs to be repainted - */ - public boolean getPaintInvalid() { - return paintInvalid; - } - - /** - * Mark this node as having invalid paint. If this is set the node will - * later be repainted. Node this method is most often used internally. - * - * @param paintInvalid true if this node should be repainted - */ - public void setPaintInvalid(final boolean paintInvalid) { - this.paintInvalid = paintInvalid; - } - - /** - * Return true if this node has a child with invalid paint. - * - * @return true if this node has a child with invalid paint - */ - public boolean getChildPaintInvalid() { - return childPaintInvalid; - } - - /** - * Mark this node as having a child with invalid paint. - * - * @param childPaintInvalid true if this node has a child with invalid paint - */ - public void setChildPaintInvalid(final boolean childPaintInvalid) { - this.childPaintInvalid = childPaintInvalid; - } - - /** - * Invalidate this node's paint, and mark all of its ancestors as having a - * node with invalid paint. - */ - public void invalidatePaint() { - setPaintInvalid(true); - - PNode n = parent; - while (n != null && !n.getChildPaintInvalid()) { - n.setChildPaintInvalid(true); - n = n.parent; - } - - if (SCENE_GRAPH_DELEGATE != null) { - SCENE_GRAPH_DELEGATE.nodePaintInvalidated(this); - } - } - - /** - * Repaint this node and any of its descendants if they have invalid paint. - */ - public void validateFullPaint() { - if (getPaintInvalid()) { - repaint(); - setPaintInvalid(false); - } - - if (getChildPaintInvalid()) { - final int count = getChildrenCount(); - for (int i = 0; i < count; i++) { - final PNode each = (PNode) children.get(i); - each.validateFullPaint(); - } - setChildPaintInvalid(false); - } - } - - /** - * Mark the area on the screen represented by this nodes full bounds as - * needing a repaint. - */ - public void repaint() { - TEMP_REPAINT_BOUNDS.setRect(getFullBoundsReference()); - repaintFrom(TEMP_REPAINT_BOUNDS, this); - } - - /** - * Pass the given repaint request up the tree, so that any cameras can - * invalidate that region on their associated canvas. - * - * @param localBounds the bounds to repaint - * @param childOrThis if childOrThis does not equal this then this nodes - * transform will be applied to the localBounds param - */ - public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { - if (parent != null) { - if (childOrThis != this) { - localToParent(localBounds); - } - else if (!getVisible()) { - return; - } - parent.repaintFrom(localBounds, this); - } - } - - // **************************************************************** - // Occluding - Methods to support occluding optimisation. Not yet - // complete. - // **************************************************************** - - /** - * Returns whether this node is Opaque. - * - * @param boundary boundary to check and see if this node covers completely. - * - * @return true if opaque - */ - public boolean isOpaque(final Rectangle2D boundary) { - return false; - } - - /** - * Returns whether this node has been flagged as occluded. - * - * @return true if occluded - */ - public boolean getOccluded() { - return occluded; - } - - /** - * Flags this node as occluded. - * - * @param occluded new value for occluded - */ - public void setOccluded(final boolean occluded) { - this.occluded = occluded; - } - - // **************************************************************** - // Painting - Methods for painting this node and its children - // - // Painting is how a node defines its visual representation on the - // screen, and is done in the local coordinate system of the node. - // - // The default painting behavior is to first paint the node, and - // then paint the node's children on top of the node. If a node - // needs wants specialised painting behavior it can override: - // - // paint() - Painting here will happen before the children - // are painted, so the children will be painted on top of painting done - // here. - // paintAfterChildren() - Painting here will happen after the children - // are painted, so it will paint on top of them. - // - // Note that you should not normally need to override fullPaint(). - // - // The visible flag can be used to make a node invisible so that - // it will never get painted. - // **************************************************************** - - /** - * Return true if this node is visible, that is if it will paint itself and - * descendants. - * - * @return true if this node and its descendants are visible. - */ - public boolean getVisible() { - return visible; - } - - /** - * Set the visibility of this node and its descendants. - * - * @param isVisible true if this node and its descendants are visible - */ - public void setVisible(final boolean isVisible) { - if (getVisible() != isVisible) { - if (!isVisible) { - repaint(); - } - visible = isVisible; - firePropertyChange(PROPERTY_CODE_VISIBLE, PROPERTY_VISIBLE, null, null); - invalidatePaint(); - } - } - - /** - * Return the paint used while painting this node. This value may be null. - * - * @return the paint used while painting this node. - */ - public Paint getPaint() { - return paint; - } - - /** - * Set the paint used to paint this node, which may be null. - * - * @param newPaint paint that this node should use when painting itself. - */ - public void setPaint(final Paint newPaint) { - if (paint == newPaint) { - return; - } - - final Paint oldPaint = paint; - paint = newPaint; - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_PAINT, PROPERTY_PAINT, oldPaint, paint); - } - - /** - * Return the transparency used when painting this node. Note that this - * transparency is also applied to all of the node's descendants. - * - * @return how transparent this node is 0f = completely transparent, 1f = - * completely opaque - */ - public float getTransparency() { - return transparency; - } - - /** - * Set the transparency used to paint this node. Note that this transparency - * applies to this node and all of its descendants. - * - * @param newTransparency transparency value for this node. 0f = fully - * transparent, 1f = fully opaque - */ - public void setTransparency(final float newTransparency) { - if (Math.abs(transparency - newTransparency) > TRANSPARENCY_RESOLUTION) { - final float oldTransparency = transparency; - transparency = newTransparency; - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_TRANSPARENCY, PROPERTY_TRANSPARENCY, new Float(oldTransparency), - new Float(newTransparency)); - } - } - - /** - * Paint this node behind any of its children nodes. Subclasses that define - * a different appearance should override this method and paint themselves - * there. - * - * @param paintContext the paint context to use for painting the node - */ - protected void paint(final PPaintContext paintContext) { - if (paint != null) { - final Graphics2D g2 = paintContext.getGraphics(); - g2.setPaint(paint); - g2.fill(getBoundsReference()); - } - } - - /** - * Paint this node and all of its descendants. Most subclasses do not need - * to override this method, they should override
paint
or
- * paintAfterChildren
instead.
- *
- * @param paintContext the paint context to use for painting this node and
- * its children
- */
- public void fullPaint(final PPaintContext paintContext) {
- if (getVisible() && fullIntersects(paintContext.getLocalClip())) {
- paintContext.pushTransform(transform);
- paintContext.pushTransparency(transparency);
-
- if (!getOccluded()) {
- paint(paintContext);
- }
-
- final int count = getChildrenCount();
- for (int i = 0; i < count; i++) {
- final PNode each = (PNode) children.get(i);
- each.fullPaint(paintContext);
- }
-
- paintAfterChildren(paintContext);
-
- paintContext.popTransparency(transparency);
- paintContext.popTransform(transform);
- }
- }
-
- /**
- * Subclasses that wish to do additional painting after their children are
- * painted should override this method and do that painting here.
- *
- * @param paintContext the paint context to sue for painting after the
- * children are painted
- */
- protected void paintAfterChildren(final PPaintContext paintContext) {
- }
-
- /**
- * Return a new Image representing this node and all of its children. The
- * image size will be equal to the size of this nodes full bounds.
- *
- * @return a new image representing this node and its descendants
- */
- public Image toImage() {
- final PBounds b = getFullBoundsReference();
- return toImage((int) Math.ceil(b.getWidth()), (int) Math.ceil(b.getHeight()), null);
- }
-
- /**
- * Return a new Image of the requested size representing this node and all
- * of its children. If backGroundPaint is null the resulting image will have
- * transparent regions, otherwise those regions will be filled with the
- * backgroundPaint.
- *
- * @param width pixel width of the resulting image
- * @param height pixel height of the resulting image
- * @param backgroundPaint paint to fill the image with before drawing this
- * node, may be null
- *
- * @return a new image representing this node and its descendants
- */
- public Image toImage(final int width, final int height, final Paint backgroundPaint) {
- BufferedImage result;
-
- if (GraphicsEnvironment.isHeadless()) {
- result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- }
- else {
- final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment()
- .getDefaultScreenDevice().getDefaultConfiguration();
- result = graphicsConfiguration.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
- }
-
- return toImage(result, backgroundPaint);
- }
-
- /**
- * Paint a representation of this node into the specified buffered image. If
- * background, paint is null, then the image will not be filled with a color
- * prior to rendering
- *
- * @param image Image onto which this node will be painted
- * @param backGroundPaint will fill background of image with this. May be
- * null.
- * @return a rendering of this image and its descendants onto the specified
- * image
- */
- public Image toImage(final BufferedImage image, final Paint backGroundPaint) {
- return toImage(image, backGroundPaint, FILL_STRATEGY_ASPECT_FIT);
- }
-
- /**
- * Paint a representation of this node into the specified buffered image. If
- * background, paint is null, then the image will not be filled with a color
- * prior to rendering
- *
- * @since 1.3
- * @param image Image onto which this node will be painted
- * @param backGroundPaint will fill background of image with this. May be
- * null.
- * @param fillStrategy strategy to use regarding how node will cover the
- * image
- * @return a rendering of this image and its descendants onto the specified
- * image
- */
- 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();
-
- if (backGroundPaint != null) {
- g2.setPaint(backGroundPaint);
- g2.fillRect(0, 0, imageWidth, imageHeight);
- }
- g2.setClip(0, 0, imageWidth, imageHeight);
-
- final PBounds nodeBounds = getFullBounds();
- nodeBounds.expandNearestIntegerDimensions();
-
- final double nodeWidth = nodeBounds.getWidth();
- final double nodeHeight = nodeBounds.getHeight();
-
- double imageRatio = imageWidth / (imageHeight * 1.0);
- double nodeRatio = nodeWidth / nodeHeight;
- double scale;
- switch (fillStrategy) {
- case FILL_STRATEGY_ASPECT_FIT:
- // scale the graphics so node's full bounds fit in the imageable
- // bounds but aspect ration is retained
-
- if (nodeRatio <= imageRatio) {
- scale = image.getHeight() / nodeHeight;
- }
- else {
- scale = image.getWidth() / nodeWidth;
- }
- g2.scale(scale, scale);
- g2.translate(-nodeBounds.x, -nodeBounds.y);
- break;
- case FILL_STRATEGY_ASPECT_COVER:
- // scale the graphics so node completely covers the imageable
- // area, but retains its aspect ratio.
- if (nodeRatio <= imageRatio) {
- scale = image.getWidth() / nodeWidth;
- }
- else {
- scale = image.getHeight() / nodeHeight;
- }
- g2.scale(scale, scale);
- break;
- case FILL_STRATEGY_EXACT_FIT:
- // scale the node so that it covers then entire image,
- // distorting it if necessary.
- g2.scale(image.getWidth() / nodeWidth, image.getHeight() / nodeHeight);
- g2.translate(-nodeBounds.x, -nodeBounds.y);
- break;
- default:
- throw new IllegalArgumentException("Fill strategy provided is invalid");
- }
-
- final PPaintContext pc = new PPaintContext(g2);
- pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
- fullPaint(pc);
- return image;
- }
-
- /**
- * Constructs a new PrinterJob, allows the user to select which printer to
- * print to, And then prints the node.
- * @throws PrinterException
- */
- public void print() throws PrinterException {
- final PrinterJob printJob = PrinterJob.getPrinterJob();
- final PageFormat pageFormat = printJob.defaultPage();
- final Book book = new Book();
- book.append(this, pageFormat);
- printJob.setPageable(book);
-
- if (printJob.printDialog()) {
- printJob.print();
- }
- }
-
- /**
- * Prints the node into the given Graphics context using the specified
- * format. The zero based index of the requested page is specified by
- * pageIndex. If the requested page does not exist then this method returns
- * NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned. If the printable object
- * aborts the print job then it throws a PrinterException.
- *
- * @param graphics the context into which the node is drawn
- * @param pageFormat the size and orientation of the page
- * @param pageIndex the zero based index of the page to be drawn
- *
- * @return Either NO_SUCH_PAGE or PAGE_EXISTS
- */
- public int print(final Graphics graphics, final PageFormat pageFormat, final int pageIndex) {
- if (pageIndex != 0) {
- return NO_SUCH_PAGE;
- }
-
- if (!(graphics instanceof Graphics2D)) {
- throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object");
- }
-
- final Graphics2D g2 = (Graphics2D) graphics;
- final PBounds imageBounds = getFullBounds();
-
- imageBounds.expandNearestIntegerDimensions();
-
- g2.setClip(0, 0, (int) pageFormat.getWidth(), (int) pageFormat.getHeight());
- g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
-
- // scale the graphics so node's full bounds fit in the imageable bounds.
- double scale = pageFormat.getImageableWidth() / imageBounds.getWidth();
- if (pageFormat.getImageableHeight() / imageBounds.getHeight() < scale) {
- scale = pageFormat.getImageableHeight() / imageBounds.getHeight();
- }
-
- g2.scale(scale, scale);
- g2.translate(-imageBounds.x, -imageBounds.y);
-
- final PPaintContext pc = new PPaintContext(g2);
- pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
- fullPaint(pc);
-
- return PAGE_EXISTS;
- }
-
- // ****************************************************************
- // Picking - Methods for picking this node and its children.
- //
- // Picking is used to determine the node that intersects a point or
- // rectangle on the screen. It is most frequently used by the
- // PInputManager to determine the node that the cursor is over.
- //
- // The intersects() method is used to determine if a node has
- // been picked or not. The default implementation just test to see
- // if the pick bounds intersects the bounds of the node. Subclasses
- // whose geometry (a circle for example) does not match up exactly with
- // the bounds should override the intersects() method.
- //
- // The default picking behavior is to first try to pick the nodes
- // children, and then try to pick the nodes own bounds. If a node
- // wants specialized picking behavior it can override:
- //
- // pick() - Pick nodes here that should be picked before the nodes
- // children are picked.
- // pickAfterChildren() - Pick nodes here that should be picked after the
- // node's children are picked.
- //
- // Note that fullPick should not normally be overridden.
- //
- // The pickable and childrenPickable flags can be used to make a
- // node or it children not pickable even if their geometry does
- // intersect the pick bounds.
- // ****************************************************************
-
- /**
- * Return true if this node is pickable. Only pickable nodes can receive
- * input events. Nodes are pickable by default.
- *
- * @return true if this node is pickable
- */
- public boolean getPickable() {
- return pickable;
- }
-
- /**
- * Set the pickable flag for this node. Only pickable nodes can receive
- * input events. Nodes are pickable by default.
- *
- * @param isPickable true if this node is pickable
- */
- public void setPickable(final boolean isPickable) {
- if (getPickable() != isPickable) {
- pickable = isPickable;
- firePropertyChange(PROPERTY_CODE_PICKABLE, PROPERTY_PICKABLE, null, null);
- }
- }
-
- /**
- * Return true if the children of this node should be picked. If this flag
- * is false then this node will not try to pick its children. Children are
- * pickable by default.
- *
- * @return true if this node tries to pick its children
- */
- public boolean getChildrenPickable() {
- return childrenPickable;
- }
-
- /**
- * Set the children pickable flag. If this flag is false then this node will
- * not try to pick its children. Children are pickable by default.
- *
- * @param areChildrenPickable true if this node tries to pick its children
- */
- public void setChildrenPickable(final boolean areChildrenPickable) {
- if (getChildrenPickable() != areChildrenPickable) {
- childrenPickable = areChildrenPickable;
- firePropertyChange(PROPERTY_CODE_CHILDREN_PICKABLE, PROPERTY_CHILDREN_PICKABLE, null, null);
- }
- }
-
- /**
- * Try to pick this node before its children have had a chance to be picked.
- * Nodes that paint on top of their children may want to override this
- * method to if the pick path intersects that paint.
- *
- * @param pickPath the pick path used for the pick operation
- * @return true if this node was picked
- */
- protected boolean pick(final PPickPath pickPath) {
- return false;
- }
-
- /**
- * Try to pick this node and all of its descendants. Most subclasses should
- * not need to override this method. Instead they should override
- * pick
or pickAfterChildren
.
- *
- * @param pickPath the pick path to add the node to if its picked
- * @return true if this node or one of its descendants was picked.
- */
- public boolean fullPick(final PPickPath pickPath) {
- if (getVisible() && (getPickable() || getChildrenPickable()) && fullIntersects(pickPath.getPickBounds())) {
- pickPath.pushNode(this);
- pickPath.pushTransform(transform);
-
- final boolean thisPickable = getPickable() && pickPath.acceptsNode(this);
-
- if (thisPickable && pick(pickPath)) {
- return true;
- }
-
- if (getChildrenPickable()) {
- final int count = getChildrenCount();
- for (int i = count - 1; i >= 0; i--) {
- final PNode each = (PNode) children.get(i);
- if (each.fullPick(pickPath)) {
- return true;
- }
- }
- }
-
- if (thisPickable && pickAfterChildren(pickPath)) {
- return true;
- }
-
- pickPath.popTransform(transform);
- pickPath.popNode(this);
- }
-
- return false;
- }
-
- /**
- * Finds all descendants of this node that intersect with the given bounds
- * and adds them to the results array.
- *
- * @param fullBounds bounds to compare against
- * @param results array into which to add matches
- */
- public void findIntersectingNodes(final Rectangle2D fullBounds, final ArrayList results) {
- if (fullIntersects(fullBounds)) {
- final Rectangle2D localBounds = parentToLocal((Rectangle2D) fullBounds.clone());
-
- if (intersects(localBounds)) {
- results.add(this);
- }
-
- final int count = getChildrenCount();
- for (int i = count - 1; i >= 0; i--) {
- final PNode each = (PNode) children.get(i);
- each.findIntersectingNodes(localBounds, results);
- }
- }
- }
-
- /**
- * Try to pick this node after its children have had a chance to be picked.
- * Most subclasses the define a different geometry will need to override
- * this method.
- *
- * @param pickPath the pick path used for the pick operation
- * @return true if this node was picked
- */
- protected boolean pickAfterChildren(final PPickPath pickPath) {
- if (intersects(pickPath.getPickBounds())) {
- return true;
- }
- return false;
- }
-
- // ****************************************************************
- // Structure - Methods for manipulating and traversing the
- // parent child relationship
- //
- // Most of these methods won't need to be overridden by subclasses
- // but you will use them frequently to build up your node structures.
- // ****************************************************************
-
- /**
- * Add a node to be a new child of this node. The new node is added to the
- * end of the list of this node's children. If child was previously a child
- * of another node, it is removed from that first.
- *
- * @param child the new child to add to this node
- */
- public void addChild(final PNode child) {
- int insertIndex = getChildrenCount();
- if (child.parent == this) {
- insertIndex--;
- }
- addChild(insertIndex, child);
- }
-
- /**
- * Add a node to be a new child of this node at the specified index. If
- * child was previously a child of another node, it is removed from that
- * node first.
- *
- * @param index where in the children list to insert the child
- * @param child the new child to add to this node
- */
- public void addChild(final int index, final PNode child) {
- final PNode oldParent = child.getParent();
-
- if (oldParent != null) {
- oldParent.removeChild(child);
- }
-
- child.setParent(this);
- getChildrenReference().add(index, child);
- child.invalidatePaint();
- invalidateFullBounds();
-
- firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children);
- }
-
- /**
- * Add a collection of nodes to be children of this node. If these nodes
- * already have parents they will first be removed from those parents.
- *
- * @param nodes a collection of nodes to be added to this node
- */
- public void addChildren(final Collection nodes) {
- final Iterator i = nodes.iterator();
- while (i.hasNext()) {
- final PNode each = (PNode) i.next();
- addChild(each);
- }
- }
-
- /**
- * Return true if this node is an ancestor of the parameter node.
- *
- * @param node a possible descendant node
- * @return true if this node is an ancestor of the given node
- */
- public boolean isAncestorOf(final PNode node) {
- PNode p = node.parent;
- while (p != null) {
- if (p == this) {
- return true;
- }
- p = p.parent;
- }
- return false;
- }
-
- /**
- * Return true if this node is a descendant of the parameter node.
- *
- * @param node a possible ancestor node
- * @return true if this nodes descends from the given node
- */
- public boolean isDescendentOf(final PNode node) {
- PNode p = parent;
- while (p != null) {
- if (p == node) {
- return true;
- }
- p = p.parent;
- }
- return false;
- }
-
- /**
- * Return true if this node descends from the root.
- *
- * @return whether this node descends from root node
- */
- public boolean isDescendentOfRoot() {
- return getRoot() != null;
- }
-
- /**
- * Change the order of this node in its parent's children list so that it
- * will draw in back of all of its other sibling nodes.
- */
- public void moveToBack() {
- final PNode p = parent;
- if (p != null) {
- p.removeChild(this);
- p.addChild(0, this);
- }
- }
-
- /**
- * Change the order of this node in its parent's children list so that it
- * will draw in back of the specified sibling node.
- *
- * @param sibling sibling to move in back of
- */
- public void moveInBackOf(final PNode sibling) {
- final PNode p = parent;
- if (p != null && p == sibling.getParent()) {
- p.removeChild(this);
- final int index = p.indexOfChild(sibling);
- p.addChild(index, this);
- }
- }
-
- /**
- * Change the order of this node in its parent's children list so that it
- * will draw in front of all of its other sibling nodes.
- */
- public void moveToFront() {
- final PNode p = parent;
- if (p != null) {
- p.removeChild(this);
- p.addChild(this);
- }
- }
-
- /**
- * Change the order of this node in its parent's children list so that it
- * will draw in front of the specified sibling node.
- *
- * @param sibling sibling to move in front of
- */
- public void moveInFrontOf(final PNode sibling) {
- final PNode p = parent;
- if (p != null && p == sibling.getParent()) {
- p.removeChild(this);
- final int index = p.indexOfChild(sibling);
- p.addChild(index + 1, this);
- }
- }
-
- /**
- * Return the parent of this node. This will be null if this node has not
- * been added to a parent yet.
- *
- * @return this nodes parent or null
- */
- public PNode getParent() {
- return parent;
- }
-
- /**
- * Set the parent of this node. Note this is set automatically when adding
- * and removing children.
- *
- * @param newParent the parent to which this node should be added
- */
- public void setParent(final PNode newParent) {
- final PNode old = parent;
- parent = newParent;
- firePropertyChange(PROPERTY_CODE_PARENT, PROPERTY_PARENT, old, parent);
- }
-
- /**
- * Return the index where the given child is stored.
- *
- * @param child child so search for
- * @return index of child or -1 if not found
- */
- public int indexOfChild(final PNode child) {
- if (children == null) {
- return -1;
- }
- return children.indexOf(child);
- }
-
- /**
- * Remove the given child from this node's children list. Any subsequent
- * children are shifted to the left (one is subtracted from their indices).
- * The removed child's parent is set to null.
- *
- * @param child the child to remove
- * @return the removed child
- */
- public PNode removeChild(final PNode child) {
- final int index = indexOfChild(child);
- if (index == -1) {
- return null;
- }
- return removeChild(index);
- }
-
- /**
- * Remove the child at the specified position of this group node's children.
- * Any subsequent children are shifted to the left (one is subtracted from
- * their indices). The removed child's parent is set to null.
- *
- * @param index the index of the child to remove
- * @return the removed child
- */
- public PNode removeChild(final int index) {
- if (children == null) {
- return null;
- }
- final PNode child = (PNode) children.remove(index);
-
- if (children.size() == 0) {
- children = null;
- }
-
- child.repaint();
- child.setParent(null);
- invalidateFullBounds();
-
- firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children);
-
- return child;
- }
-
- /**
- * Remove all the children in the given collection from this node's list of
- * children. All removed nodes will have their parent set to null.
- *
- * @param childrenNodes the collection of children to remove
- */
- public void removeChildren(final Collection childrenNodes) {
- final Iterator i = childrenNodes.iterator();
- while (i.hasNext()) {
- final PNode each = (PNode) i.next();
- removeChild(each);
- }
- }
-
- /**
- * Remove all the children from this node. Node this method is more
- * efficient then removing each child individually.
- */
- public void removeAllChildren() {
- if (children != null) {
- final int count = children.size();
- for (int i = 0; i < count; i++) {
- final PNode each = (PNode) children.get(i);
- each.setParent(null);
- }
- children = null;
- invalidatePaint();
- invalidateFullBounds();
-
- firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children);
- }
- }
-
- /**
- * Delete this node by removing it from its parent's list of children.
- */
- public void removeFromParent() {
- if (parent != null) {
- parent.removeChild(this);
- }
- }
-
- /**
- * Set the parent of this node, and transform the node in such a way that it
- * doesn't move in global coordinates.
- *
- * @param newParent The new parent of this node.
- */
- public void reparent(final PNode newParent) {
- final AffineTransform originalTransform = getLocalToGlobalTransform(null);
- final AffineTransform newTransform = newParent.getGlobalToLocalTransform(null);
- newTransform.concatenate(originalTransform);
-
- removeFromParent();
- setTransform(newTransform);
- newParent.addChild(this);
- computeFullBounds(fullBoundsCache);
- }
-
- /**
- * Swaps this node out of the scene graph tree, and replaces it with the
- * specified replacement node. This node is left dangling, and it is up to
- * the caller to manage it. The replacement node will be added to this
- * node's parent in the same position as this was. That is, if this was the
- * 3rd child of its parent, then after calling replaceWith(), the
- * replacement node will also be the 3rd child of its parent. If this node
- * has no parent when replace is called, then nothing will be done at all.
- *
- * @param replacementNode the new node that replaces the current node in the
- * scene graph tree.
- */
- public void replaceWith(final PNode replacementNode) {
- if (parent != null) {
- final PNode p = parent;
- final int index = p.getChildrenReference().indexOf(this);
- p.removeChild(this);
- p.addChild(index, replacementNode);
- }
- }
-
- /**
- * Sets the name of this null, may be null.
- *
- * @since 1.3
- * @param name new name for this node
- */
- public void setName(final String name) {
- this.name = name;
- }
-
- /**
- * Returns the name given to this node.
- *
- * @since 1.3
- * @return name given to this node, may be null
- */
- public String getName() {
- return name;
- }
-
- /**
- * Return the number of children that this node has.
- *
- * @return the number of children
- */
- public int getChildrenCount() {
- if (children == null) {
- return 0;
- }
- return children.size();
- }
-
- /**
- * Return the child node at the specified index.
- *
- * @param index a child index
- * @return the child node at the specified index
- */
- public PNode getChild(final int index) {
- return (PNode) children.get(index);
- }
-
- /**
- * Return a reference to the list used to manage this node's children. This
- * list should not be modified.
- *
- * @return reference to the children list
- */
- public List getChildrenReference() {
- if (children == null) {
- children = new ArrayList();
- }
- return children;
- }
-
- /**
- * Return an iterator over this node's direct descendant children.
- *
- * @return iterator over this nodes children
- */
- public ListIterator getChildrenIterator() {
- if (children == null) {
- return Collections.EMPTY_LIST.listIterator();
- }
- return Collections.unmodifiableList(children).listIterator();
- }
-
- /**
- * Return the root node (instance of PRoot). If this node does not descend
- * from a PRoot then null will be returned.
- *
- * @return root element of this node, or null if this node does not descend
- * from a PRoot
- */
- public PRoot getRoot() {
- if (parent != null) {
- return parent.getRoot();
- }
- return null;
- }
-
- /**
- * Return a collection containing this node and all of its descendant nodes.
- *
- * @return a new collection containing this node and all descendants
- */
- public Collection getAllNodes() {
- return getAllNodes(null, null);
- }
-
- /**
- * Return a collection containing the subset of this node and all of its
- * descendant nodes that are accepted by the given node filter. If the
- * filter is null then all nodes will be accepted. If the results parameter
- * is not null then it will be used to collect this subset instead of
- * creating a new collection.
- *
- * @param filter the filter used to determine the subset
- * @param resultantNodes where matching nodes should be added
- * @return a collection containing this node and all descendants
- */
- public Collection getAllNodes(final PNodeFilter filter, final Collection resultantNodes) {
- Collection results;
- if (resultantNodes == null) {
- results = new ArrayList();
- }
- else {
- results = resultantNodes;
- }
-
- if (filter == null || filter.accept(this)) {
- results.add(this);
- }
-
- if (filter == null || filter.acceptChildrenOf(this)) {
- final int count = getChildrenCount();
- for (int i = 0; i < count; i++) {
- final PNode each = (PNode) children.get(i);
- each.getAllNodes(filter, results);
- }
- }
-
- return results;
- }
-
- // ****************************************************************
- // Serialization - Nodes conditionally serialize their parent.
- // This means that only the parents that were unconditionally
- // (using writeObject) serialized by someone else will be restored
- // when the node is unserialized.
- // ****************************************************************
-
- /**
- * Write this node and all of its descendant nodes to the given outputsteam.
- * This stream must be an instance of PObjectOutputStream or serialization
- * will fail. This nodes parent is written out conditionally, that is it
- * will only be written out if someone else writes it out unconditionally.
- *
- * @param out the output stream to write to, must be an instance of
- * PObjectOutputStream
- * @throws IOException when an error occurs speaking to underlying
- * ObjectOutputStream
- */
- private void writeObject(final ObjectOutputStream out) throws IOException {
- if (!(out instanceof PObjectOutputStream)) {
- throw new IllegalArgumentException("PNode.writeObject may only be used with PObjectOutputStreams");
- }
- out.defaultWriteObject();
- ((PObjectOutputStream) out).writeConditionalObject(parent);
- }
-
- /**
- * Read this node and all of its descendants in from the given input stream.
- *
- * @param in the stream to read from
- *
- * @throws IOException when an error occurs speaking to underlying
- * ObjectOutputStream
- * @throws ClassNotFoundException when a class is deserialized that no
- * longer exists. This can happen if it's renamed or deleted.
- */
- private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- parent = (PNode) in.readObject();
- }
-
- /**
- * Returns an array of input event listeners that are attached to this node.
- *
- * @since 1.3
- * @return event listeners attached to this node
- */
- public PInputEventListener[] getInputEventListeners() {
- if (listenerList == null || listenerList.getListenerCount() == 0) {
- return new PInputEventListener[] {};
- }
-
- final EventListener[] listeners = listenerList.getListeners(PInputEventListener.class);
-
- final PInputEventListener[] result = new PInputEventListener[listeners.length];
- for (int i = 0; i < listeners.length; i++) {
- result[i] = (PInputEventListener) listeners[i];
- }
- return result;
- }
-
- /**
- * PSceneGraphDelegate is an interface to receive low level node
- * events. It together with PNode.SCENE_GRAPH_DELEGATE gives Piccolo2d users
- * an efficient way to learn about low level changes in Piccolo's scene
- * graph. Most users will not need to use this.
- */
- public interface PSceneGraphDelegate {
- /**
- * Called to notify delegate that the node needs repainting.
- *
- * @param node node needing repaint
- */
- void nodePaintInvalidated(PNode node);
-
- /**
- * Called to notify delegate that the node and all it's children need
- * repainting.
- *
- * @param node node needing repaint
- */
- void nodeFullBoundsInvalidated(PNode node);
- }
-}
diff --git a/core/src/main/java/edu/umd/cs/piccolo/POffscreenCanvas.java b/core/src/main/java/edu/umd/cs/piccolo/POffscreenCanvas.java
deleted file mode 100644
index cb8aa9b..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/POffscreenCanvas.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo;
-
-import java.awt.Color;
-import java.awt.Cursor;
-import java.awt.Graphics2D;
-
-import edu.umd.cs.piccolo.util.PBounds;
-import edu.umd.cs.piccolo.util.PPaintContext;
-import edu.umd.cs.piccolo.util.PUtil;
-
-/**
- * Offscreen canvas.
- *
- * @since 1.3
- */
-public final class POffscreenCanvas implements PComponent {
-
- /** Default render quality, PPaintContext.HIGH_QUALITY_RENDERING
. */
- static final int DEFAULT_RENDER_QUALITY = PPaintContext.HIGH_QUALITY_RENDERING;
-
- /** Bounds of this offscreen canvas. */
- private final PBounds bounds;
-
- /** Camera for this offscreen canvas. */
- private PCamera camera;
-
- /** Render quality. */
- private int renderQuality = DEFAULT_RENDER_QUALITY;
-
- /** True if this offscreen canvas is opaque. */
- private boolean opaque;
-
- /** Background color for this offscreen canvas. */
- private Color backgroundColor;
-
-
- /**
- * Create a new offscreen canvas the specified width and height.
- *
- * @param width width of this offscreen canvas, must be at least zero
- * @param height height of this offscreen canvas, must be at least zero
- */
- public POffscreenCanvas(final int width, final int height) {
- if (width < 0) {
- throw new IllegalArgumentException("width must be at least zero, was " + width);
- }
- if (height < 0) {
- throw new IllegalArgumentException("height must be at least zero, was " + height);
- }
- bounds = new PBounds(0.0d, 0.0d, width, height);
- setCamera(PUtil.createBasicScenegraph());
-
- opaque = false;
- backgroundColor = null;
- }
-
-
- /**
- * Render this offscreen canvas to the specified graphics.
- *
- * @param graphics graphics to render this offscreen canvas to, must not be null
- */
- public void render(final Graphics2D graphics) {
- if (graphics == null) {
- throw new IllegalArgumentException("graphics must not be null");
- }
-
- if (opaque && backgroundColor != null) {
- graphics.setBackground(backgroundColor);
- graphics.clearRect(0, 0, (int) bounds.getWidth(), (int) bounds.getHeight());
- }
-
- final PPaintContext paintContext = new PPaintContext(graphics);
- paintContext.setRenderQuality(renderQuality);
- camera.fullPaint(paintContext);
- }
-
- /**
- * Set the camera for this offscreen canvas to camera
.
- *
- * @param camera camera for this offscreen canvas
- */
- public void setCamera(final PCamera camera) {
- if (this.camera != null) {
- this.camera.setComponent(null);
- }
- this.camera = camera;
- if (camera != null) {
- camera.setComponent(this);
- camera.setBounds((PBounds) bounds.clone());
- }
- }
-
- /**
- * Return the camera for this offscreen canvas.
- *
- * @return the camera for this offscreen canvas
- */
- public PCamera getCamera() {
- return camera;
- }
-
- /**
- * Set the render quality hint for this offscreen canvas to
- * renderQuality
.
- *
- * @param renderQuality render quality hint, must be one of
- * PPaintContext.HIGH_QUALITY_RENDERING
or
- * PPaintContext.LOW_QUALITY_RENDERING
- */
- public void setRenderQuality(final int renderQuality) {
- if (renderQuality == PPaintContext.HIGH_QUALITY_RENDERING
- || renderQuality == PPaintContext.LOW_QUALITY_RENDERING) {
- this.renderQuality = renderQuality;
- }
- else {
- throw new IllegalArgumentException("renderQuality must be one of PPaintContext.HIGH_QUALITY_RENDERING"
- + " or PPaintContext.LOW_QUALITY_RENDERING, was " + renderQuality);
- }
- }
-
- /**
- * Return the render quality hint for this offscreen canvas.
- *
- * @return the render quality hint for this offscreen canvas
- */
- public int getRenderQuality() {
- return renderQuality;
- }
-
- /** {@inheritDoc} */
- public void paintImmediately() {
- // empty
- }
-
- /** {@inheritDoc} */
- public void popCursor() {
- // empty
- }
-
- /** {@inheritDoc} */
- public void pushCursor(final Cursor cursor) {
- // empty
- }
-
- /** {@inheritDoc} */
- public void repaint(final PBounds repaintBounds) {
- // empty
- }
-
- /** {@inheritDoc} */
- public void setInteracting(final boolean interacting) {
- // empty
- }
-
- /**
- * Return the root node of the scene graph for this offscreen canvas. The
- * root node will be null if the camera for this offscreen canvas is null.
- *
- * @return the root node of the scene graph for this offscreen canvas
- */
- public PRoot getRoot() {
- return camera == null ? null : camera.getRoot();
- }
-
- /**
- * Return true if this offscreen canvas is opaque. Defaults to false
.
- *
- * @return true if this offscreen canvas is opaque
- */
- public boolean isOpaque() {
- return opaque;
- }
-
- /**
- * Set to true if this offscreen canvas is opaque.
- *
- * @param opaque true if this offscreen canvas is opaque
- */
- public void setOpaque(final boolean opaque) {
- this.opaque = opaque;
- }
-
- /**
- * Return the background color for this offscreen canvas. If this
- * offscreen canvas is opaque, the background color will be painted
- * before the contents of the scene are rendered.
- *
- * @see #isOpaque
- * @return the background color for this offscreen canvas
- */
- public Color getBackground() {
- return backgroundColor;
- }
-
- /**
- * Set the background color for this offscreen canvas to backgroundColor
.
- * If this offscreen canvas is opaque, the background color will be painted
- * before the contents of the scene are rendered.
- *
- * @see #isOpaque
- * @param backgroundColor background color for this offscreen canvas
- */
- public void setBackground(final Color backgroundColor) {
- this.backgroundColor = backgroundColor;
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/edu/umd/cs/piccolo/PRoot.java b/core/src/main/java/edu/umd/cs/piccolo/PRoot.java
deleted file mode 100644
index 71735cc..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/PRoot.java
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo;
-
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.swing.SwingUtilities;
-import javax.swing.Timer;
-
-import edu.umd.cs.piccolo.activities.PActivity;
-import edu.umd.cs.piccolo.activities.PActivityScheduler;
-import edu.umd.cs.piccolo.util.PDebug;
-import edu.umd.cs.piccolo.util.PNodeFilter;
-
-/**
- * PRoot serves as the top node in Piccolo2D's runtime structure. The
- * PRoot responsible for running the main UI loop that processes input from
- * activities and external events.
- *
- *
- * @version 1.1
- * @author Jesse Grosjean
- */
-public class PRoot extends PNode {
-
- /**
- * Allows for future serialization code to understand versioned binary
- * formats.
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * The property name that identifies a change in the set of this root's
- * input sources (see {@link InputSource InputSource}). In any property
- * change event the new value will be a reference to the list of this root's
- * input sources, but old value will always be null.
- */
- public static final String PROPERTY_INPUT_SOURCES = "inputSources";
-
- /**
- * The property code that identifies a change in the set of this root's
- * input sources (see {@link InputSource InputSource}). In any property
- * change event the new value will be a reference to the list of this root's
- * input sources, but old value will always be null.
- */
- public static final int PROPERTY_CODE_INPUT_SOURCES = 1 << 14;
-
- /**
- * The property name that identifies a change in this node's interacting
- * state.
- *
- * @since 1.3
- */
- public static final String PROPERTY_INTERACTING_CHANGED = "INTERACTING_CHANGED_NOTIFICATION";
-
- /**
- * The property code that identifies a change in this node's interacting
- * state.
- *
- * @since 1.3
- */
- public static final int PROPERTY_CODE_INTERACTING_CHANGED = 1 << 13;
-
- /** Whether this not is currently processing inputs. */
- protected transient boolean processingInputs;
-
- /** Whether this node needs to have its inputs processed. */
- protected transient boolean processInputsScheduled;
-
- /** The number of interactions this node is currently participating in. */
- private transient int interacting;
-
- /**
- * The singleton instance of the default input manager.
- */
- private transient PInputManager defaultInputManager;
-
- /** The Input Sources that are registered to this node. */
- private final transient List inputSources;
-
- /**
- * Used to provide a consistent clock time to activities as they are being
- * processed.
- *
- * Should it happen that an activity step take longer than a millisecond,
- * the next step will be unaffected by the change in clock had it used
- * System.currentMillis().
- */
- private transient long globalTime;
-
- /**
- * Object responsible for scheduling activities, regardless of where in the
- * scene they take place.
- */
- private final PActivityScheduler activityScheduler;
-
- /**
- * Construct a new PRoot(). Note the PCanvas already creates a basic scene
- * graph for you so often you will not need to construct your own roots.
- */
- public PRoot() {
- super();
- inputSources = new ArrayList();
- globalTime = System.currentTimeMillis();
- activityScheduler = new PActivityScheduler(this);
- }
-
- // ****************************************************************
- // Activities
- // ****************************************************************
-
- /**
- * Add an activity to the activity scheduler associated with this root.
- * Activities are given a chance to run during each call to the roots
- * processInputs
method. When the activity has finished running
- * it will automatically get removed.
- *
- * @param activity Activity that should be scheduled
- * @return whether it has been scheduled (always true)
- */
- public boolean addActivity(final PActivity activity) {
- getActivityScheduler().addActivity(activity);
- return true;
- }
-
- /**
- * Get the activity scheduler associated with this root.
- *
- * @return associated scheduler
- */
- public PActivityScheduler getActivityScheduler() {
- return activityScheduler;
- }
-
- /**
- * Wait for all scheduled activities to finish before returning from this
- * method. This will freeze out user input, and so it is generally
- * recommended that you use PActivities.setTriggerTime() to offset
- * activities instead of using this method.
- */
- public void waitForActivities() {
- final PNodeFilter cameraWithCanvas = new CameraWithCanvasFilter();
-
- while (activityScheduler.getActivitiesReference().size() > 0) {
- processInputs();
- final Iterator i = getAllNodes(cameraWithCanvas, null).iterator();
- while (i.hasNext()) {
- final PCamera each = (PCamera) i.next();
- each.getComponent().paintImmediately();
- }
- }
- }
-
- /**
- * Since getRoot is handled recursively, and root is the lowest point in the
- * hierarchy, simply returns itself.
- *
- * @return itself
- */
- public PRoot getRoot() {
- return this;
- }
-
- /**
- * Get the default input manager to be used when processing input events.
- * PCanvas's use this method when they forward new swing input events to the
- * PInputManager.
- *
- * @return a singleton instance of PInputManager
- */
- public PInputManager getDefaultInputManager() {
- if (defaultInputManager == null) {
- defaultInputManager = new PInputManager();
- addInputSource(defaultInputManager);
- }
- return defaultInputManager;
- }
-
- /**
- * Return true if this root has been marked as interacting. If so the root
- * will normally render at a lower quality that is faster.
- *
- * @since 1.3
- * @return true if this root has user interaction taking place
- */
- public boolean getInteracting() {
- return interacting > 0;
- }
-
- /**
- * Set if this root is interacting. If so the root will normally render at a
- * lower quality that is faster. Also repaints the root if the the
- * interaction has ended.
- *
processInputs
method.
- * Activities should usually use this global time instead of System.
- * currentTimeMillis() so that multiple activities will be synchronized.
- *
- * @return time as recorded at the beginning of activity scheduling
- */
- public long getGlobalTime() {
- return globalTime;
- }
-
- /**
- * This is the heartbeat of the Piccolo2D framework. Pending input events
- * are processed. Activities are given a chance to run, and the bounds
- * caches and any paint damage is validated.
- */
- public void processInputs() {
- PDebug.startProcessingInput();
- processingInputs = true;
-
- globalTime = System.currentTimeMillis();
- if (inputSources.size() > 0) {
- final Iterator inputSourceIterator = inputSources.iterator();
- while (inputSourceIterator.hasNext()) {
- final InputSource each = (InputSource) inputSourceIterator.next();
- each.processInput();
- }
- }
-
- activityScheduler.processActivities(globalTime);
- validateFullBounds();
- validateFullPaint();
-
- processingInputs = false;
- PDebug.endProcessingInput();
- }
-
- /** {@inheritDoc} */
- public void setFullBoundsInvalid(final boolean fullLayoutInvalid) {
- super.setFullBoundsInvalid(fullLayoutInvalid);
- scheduleProcessInputsIfNeeded();
- }
-
- /** {@inheritDoc} */
- public void setChildBoundsInvalid(final boolean childLayoutInvalid) {
- super.setChildBoundsInvalid(childLayoutInvalid);
- scheduleProcessInputsIfNeeded();
- }
-
- /** {@inheritDoc} */
- public void setPaintInvalid(final boolean paintInvalid) {
- super.setPaintInvalid(paintInvalid);
- scheduleProcessInputsIfNeeded();
- }
-
- /** {@inheritDoc} */
- public void setChildPaintInvalid(final boolean childPaintInvalid) {
- super.setChildPaintInvalid(childPaintInvalid);
- scheduleProcessInputsIfNeeded();
- }
-
- /**
- * Schedule process inputs if needed.
- */
- public void scheduleProcessInputsIfNeeded() {
- /*
- * The reason for the special case here (when not in the event dispatch
- * thread) is that the SwingUtilitiles.invokeLater code below only
- * invokes later with respect to the event dispatch thread, it will
- * invoke concurrently with other threads.
- */
- if (!SwingUtilities.isEventDispatchThread()) {
- /*
- * Piccolo2D is not thread safe and should almost always be called
- * from the Swing event dispatch thread. It should only reach this
- * point when a new canvas is being created.
- */
- return;
- }
-
- PDebug.scheduleProcessInputs();
-
- if (!processInputsScheduled && !processingInputs
- && (getFullBoundsInvalid() || getChildBoundsInvalid() || getPaintInvalid() || getChildPaintInvalid())) {
-
- processInputsScheduled = true;
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- processInputs();
- processInputsScheduled = false;
- }
- });
- }
- }
-
- 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
- * in here.
- */
- public static interface InputSource {
- /** Causes the system to process any pending Input Events. */
- void processInput();
- }
-}
diff --git a/core/src/main/java/edu/umd/cs/piccolo/activities/PActivity.java b/core/src/main/java/edu/umd/cs/piccolo/activities/PActivity.java
deleted file mode 100644
index c890c4c..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/activities/PActivity.java
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo.activities;
-
-import edu.umd.cs.piccolo.util.PUtil;
-
-/**
- * PActivity controls some time dependent aspect of Piccolo, such as
- * animation. Once created activities must be scheduled with the
- * PActivityScheduler managed by the PRoot to run. They are automatically
- * removed from the scheduler when the animation has finished.
- * - * See the PNode.animate*() methods for an example of how to set up and run - * different activities. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PActivity { - /** - * Parameter for terminate that signifies that activity should bail out - * immediately without flagging activity as finished. - */ - public static final int TERMINATE_WITHOUT_FINISHING = 0; - - /** - * Parameter for terminate that signifies that activity should bail out - * immediately, but flag activity as finished. - */ - public static final int TERMINATE_AND_FINISH = 1; - - /** - * Parameter for terminate that signifies that activity should bail out - * immediately, if currently active. - */ - public static final int TERMINATE_AND_FINISH_IF_STEPPING = 2; - - /** Activity scheduler that this activity is bound to. */ - private PActivityScheduler scheduler; - - /** Time at which this activity should start in PRoot global time. */ - private long startTime; - - /** Duration in milliseconds that this activity should last. */ - private long duration; - - /** How many milliseconds should pass between steps. */ - private long stepRate; - - private PActivityDelegate delegate; - - /** Whether this activity is currently active. */ - private boolean stepping; - - /** Next time at which step should occur. */ - private long nextStepTime; - - /** - * Constructs a new PActivity. - * - * @param aDuration the amount of time that this activity should take to - * complete, -1 for infinite. - */ - public PActivity(final long aDuration) { - this(aDuration, PUtil.DEFAULT_ACTIVITY_STEP_RATE); - } - - /** - * Constructs a new PActivity. - * - * @param aDuration the amount of time that this activity should take to - * complete, -1 for infinite. - * @param aStepRate the maximum rate that this activity should receive step - * events. - */ - public PActivity(final long aDuration, final long aStepRate) { - this(aDuration, aStepRate, System.currentTimeMillis()); - } - - /** - * Constructs a new PActivity. - * - * @param aDuration the amount of time that this activity should take to - * complete, -1 for infinite. - * @param aStepRate the maximum rate that this activity should receive step - * events. - * @param aStartTime the time (relative to System.currentTimeMillis()) that - * this activity should start. - */ - public PActivity(final long aDuration, final long aStepRate, final long aStartTime) { - duration = aDuration; - stepRate = aStepRate; - startTime = aStartTime; - nextStepTime = aStartTime; - stepping = false; - } - - // **************************************************************** - // Basics - // **************************************************************** - - /** - * Return the time that this activity should start running in PRoot global - * time. When this time is reached (or soon after) this activity will have - * its startStepping() method called. - * - * @return time at which this activity should start in PRoot global time. - */ - public long getStartTime() { - return startTime; - } - - /** - * Set the time that this activity should start running in PRoot global - * time. When this time is reached (or soon after) this activity will have - * its startStepping() method called. - * - * @param aTriggerTime time at which you want this activity to begin in - * PRoot global time - */ - public void setStartTime(final long aTriggerTime) { - startTime = aTriggerTime; - } - - /** - * Return the amount of time that this activity should delay between steps. - * - * @return the desired milliseconds between steps - */ - public long getStepRate() { - return stepRate; - } - - /** - * Set the amount of time that this activity should delay between steps. - * - * @param aStepRate desired step rate in milliseconds between steps - */ - public void setStepRate(final long aStepRate) { - stepRate = aStepRate; - } - - /** - * Gets the next step time desired for this activity. Exists since some - * steps might eat into the step rate otherwise. - * - * @return next calculated step time - */ - public long getNextStepTime() { - return nextStepTime; - } - - /** - * Return the amount of time that this activity should take to complete, - * after the startStepping method is called. - * - * @return time that this activity should take to complete - */ - public long getDuration() { - return duration; - } - - /** - * Set the amount of time that this activity should take to complete, after - * the startStepping method is called. - * - * @param aDuration desired duration this activity should take (-1 for - * infinite) once it begins stepping - */ - public void setDuration(final long aDuration) { - duration = aDuration; - } - - /** - * Returns the activity scheduler associated with this activity. - * - * @return associated scheduler - */ - public PActivityScheduler getActivityScheduler() { - return scheduler; - } - - /** - * Informs the activity of the scheduler that will be responsible for - * scheduling it. - * - * @param aScheduler scheduler to associate with this activity - */ - public void setActivityScheduler(final PActivityScheduler aScheduler) { - scheduler = aScheduler; - } - - // **************************************************************** - // Stepping - // **************************************************************** - - /** - * Return true if this activity is stepping. - * - * @return whether this activity is stepping - */ - public boolean isStepping() { - return stepping; - } - - /** - * Return true if this activity is performing an animation. This is used by - * the PCanvas to determine if it should set the render quality to - * PCanvas.animatingRenderQuality or not for each frame it renders. - * - * @return whether this activity is an animation, subclasses can override - * this. - */ - protected boolean isAnimation() { - return false; - } - - /** - * This method is called right before an activity is scheduled to start - * running. After this method is called step() will be called until the - * activity finishes. - */ - protected void activityStarted() { - if (delegate != null) { - delegate.activityStarted(this); - } - } - - /** - * This is the method that most activities override to perform their - * behavior. It will be called repeatedly when the activity is running. - * - * @param elapsedTime the amount of time that has passed relative to the - * activities startTime. - */ - protected void activityStep(final long elapsedTime) { - if (delegate != null) { - delegate.activityStepped(this); - } - } - - /** - * This method is called after an activity is has finished running and the - * activity has been removed from the PActivityScheduler queue. - */ - protected void activityFinished() { - if (delegate != null) { - delegate.activityFinished(this); - } - } - - /** - * Get the delegate for this activity. The delegate is notified when the - * activity starts and stops stepping. - * - * @return delegate of this activity, may be null - */ - public PActivityDelegate getDelegate() { - return delegate; - } - - /** - * Set the delegate for this activity. The delegate is notified when the - * activity starts and stops stepping. - * - * @param delegate delegate that should be informed of activity events - */ - public void setDelegate(final PActivityDelegate delegate) { - this.delegate = delegate; - } - - // **************************************************************** - // Controlling - // **************************************************************** - - /** - * Schedules this activity to start after the first activity has finished. - * Note that no link is created between these activities, if the startTime - * or duration of the first activity is later changed this activities start - * time will not be updated to reflect that change. - * - * @param first activity after which this activity should be scheduled - */ - public void startAfter(final PActivity first) { - setStartTime(first.getStartTime() + first.getDuration()); - } - - /** - * Stop this activity immediately, and remove it from the activity - * scheduler. The default termination behavior is call activityFinished if - * the activity is currently stepping. Use terminate(terminationBehavior) - * use a different termination behavior. - */ - public void terminate() { - terminate(TERMINATE_AND_FINISH_IF_STEPPING); - } - - /** - * Stop this activity immediately, and remove it from the activity - * scheduler. The termination behavior determines when and if - * activityStarted and activityFinished get called. The possible termination - * behaviors are as follow: - * - * TERMINATE_WITHOUT_FINISHING - The method activityFinished will never get - * called and so the activity will be terminated midway. - * TERMINATE_AND_FINISH - The method activityFinished will always get - * called. And so the activity will always end in it's completed state. If - * the activity has not yet started the method activityStarted will also be - * called. TERMINATE_AND_FINISH_IF_STEPPING - The method activityFinished - * will only be called if the activity has previously started. - * - * @param terminationBehavior behavior to use regarding delegate - * notification and event firing - */ - public void terminate(final int terminationBehavior) { - if (scheduler != null) { - scheduler.removeActivity(this); - } - - switch (terminationBehavior) { - case TERMINATE_WITHOUT_FINISHING: - stepping = false; - break; - - case TERMINATE_AND_FINISH: - if (stepping) { - stepping = false; - activityFinished(); - } - else { - activityStarted(); - activityFinished(); - } - - break; - - case TERMINATE_AND_FINISH_IF_STEPPING: - if (stepping) { - stepping = false; - activityFinished(); - } - break; - default: - throw new RuntimeException("Invalid termination behaviour provided to PActivity.terminate"); - } - } - - /** - * The activity scheduler calls this method and it is here that the activity - * decides if it should do a step or not for the given time. - * - * @param currentTime in global root time - * @return number of milliseconds in global root time before processStep - * should be called again, -1 if never - */ - public long processStep(final long currentTime) { - // if before start time - if (currentTime < startTime) { - return startTime - currentTime; - } - - // if past stop time - if (currentTime > getStopTime()) { - if (stepping) { - stepping = false; - scheduler.removeActivity(this); - activityFinished(); - } - else { - activityStarted(); - scheduler.removeActivity(this); - activityFinished(); - } - return -1; - } - - // else should be stepping - if (!stepping) { - activityStarted(); - stepping = true; - } - - if (currentTime >= nextStepTime) { - activityStep(currentTime - startTime); - nextStepTime = currentTime + stepRate; - } - - return stepRate; - } - - /** - * Return the time when this activity should finish running. At this time - * (or soon after) the stoppedStepping method will be called - * - * @return time at which this activity should be stopped - */ - public long getStopTime() { - if (duration == -1) { - return Long.MAX_VALUE; - } - return startTime + duration; - } - - /** - * PActivityDelegate is used by classes to learn about and act on the - * different states that a PActivity goes through, such as when the activity - * starts and stops stepping. - */ - public interface PActivityDelegate { - /** - * Gets called when the activity starts. - * - * @param activity activity that started - */ - void activityStarted(PActivity activity); - - /** - * Gets called for each step of the activity. - * - * @param activity activity that is stepping - */ - void activityStepped(PActivity activity); - - /** - * Gets called when the activity finishes. - * - * @param activity activity that finished - */ - void activityFinished(PActivity activity); - } - -} 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 deleted file mode 100644 index 3610b2d..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/PActivityScheduler.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.activities; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.Timer; - -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.util.PUtil; - -/** - * PActivityScheduler is responsible for maintaining a list of - * activities. It is given a chance to process these activities from the PRoot's - * processInputs() method. Most users will not need to use the - * PActivityScheduler directly, instead you should look at: - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PColorActivity extends PInterpolatingActivity { - - private Color source; - private Color destination; - private final Target target; - - /** - * Target Objects that want their color to be set by the color - * activity must implement this interface. - */ - public interface Target { - - /** - * This will be called by the color activity for each new interpolated - * color that it computes while it is stepping. - * - * @param color the color to assign to the target - */ - void setColor(Color color); - - /** - * This method is called right before the color activity starts. That - * way an object's color is always animated from its current color. - * - * @return the target's current color. - */ - Color getColor(); - } - - /** - * Constructs a color activity for the given target that will animate for - * the duration provided at an interval of stepRate. - * - * Destination color must be assigned later. - * - * @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 - */ - public PColorActivity(final long duration, final long stepRate, final Target aTarget) { - this(duration, stepRate, aTarget, null); - } - - /** - * 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. - * - * @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 - * @param aDestination the color to which the animation is aiming at - */ - public PColorActivity(final long duration, final long stepRate, final Target aTarget, final Color aDestination) { - this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, aTarget, aDestination); - } - - /** - * Create a new PColorActivity. - * - * @param duration the length of one loop of the activity - * @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 - * the source state will be taken from. - * @param aDestination the destination color state - */ - public PColorActivity(final long duration, final long stepRate, final int loopCount, final int mode, - final Target aTarget, final Color aDestination) { - super(duration, stepRate, loopCount, mode); - target = aTarget; - destination = aDestination; - } - - /** - * Returns true since all PColorActivities animate the scene. - * - * @return always returns true - */ - protected boolean isAnimation() { - return true; - } - - /** - * Return the final color that will be set on the color activities target - * when the activity stops stepping. - * - * @return the final color for this color activity - */ - public Color getDestinationColor() { - return destination; - } - - /** - * Set the final color that will be set on the color activities target when - * the activity stops stepping. - * - * @param newDestination to animate towards - */ - public void setDestinationColor(final Color newDestination) { - destination = newDestination; - } - - /** - * Overrides it's parent to ensure that the source color is the color of the - * node being animated. - */ - protected void activityStarted() { - if (getFirstLoop()) { - source = target.getColor(); - } - super.activityStarted(); - } - - /** - * Interpolates the target node's color by mixing the source color and the - * destination color. - * - * @param zeroToOne 0 = all source color, 1 = all destination color - */ - public void setRelativeTargetValue(final float zeroToOne) { - super.setRelativeTargetValue(zeroToOne); - final float red = source.getRed() + zeroToOne * (destination.getRed() - source.getRed()); - final float green = source.getGreen() + zeroToOne * (destination.getGreen() - source.getGreen()); - final float blue = source.getBlue() + zeroToOne * (destination.getBlue() - source.getBlue()); - final float alpha = source.getAlpha() + zeroToOne * (destination.getAlpha() - source.getAlpha()); - target.setColor(new Color((int) red, (int) green, (int) blue, (int) alpha)); - } -} 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 deleted file mode 100644 index 8c40843..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/PInterpolatingActivity.java +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.activities; - -import edu.umd.cs.piccolo.util.PUtil; - -/** - * PInterpolatingActivity interpolates between two states (source and - * destination) over the duration of the activity. The interpolation can be - * either linear or slow- in, slow-out. - *
- * The mode determines how the activity interpolates between the two states. The - * default mode interpolates from source to destination, but you can also go - * from destination to source, and from source to destination to source. - *
- * A loopCount of greater then one will make the activity reschedule itself when - * it has finished. This makes the activity loop between the two states. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PInterpolatingActivity extends PActivity { - - /** - * Specifies that interpolation will be from the source value to the - * destination value. - */ - public static final int SOURCE_TO_DESTINATION = 1; - - /** - * Specifies that interpolation will be from the destination value to the - * source value. - */ - public static final int DESTINATION_TO_SOURCE = 2; - - /** - * Specifies that interpolation proceed from the source to the destination - * then back to the source. Can be used to perform flashes. source value. - */ - public static final int SOURCE_TO_DESTINATION_TO_SOURCE = 3; - - private int mode; - private boolean slowInSlowOut; - private int loopCount; - private boolean firstLoop; - - /** - * Constructs an interpolating activity that will last the duration given. - * - * @since 1.3 - * @param duration duration in milliseconds of the entire activity - */ - public PInterpolatingActivity(final long duration) { - this(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION); - } - - /** - * 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); - } - - /** - * Create a new PInterpolatingActivity. - *
- * - * @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 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 - */ - public PInterpolatingActivity(final long duration, final long stepRate, final long startTime, final int loopCount, - final int mode) { - super(duration, stepRate, startTime); - this.loopCount = loopCount; - this.mode = mode; - slowInSlowOut = true; - firstLoop = true; - } - - /** - * 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 duration) { - if (duration <= 0) { - throw new IllegalArgumentException("Duration for PInterpolatingActivity must be greater then 0"); - } - - super.setDuration(duration); - } - - // **************************************************************** - // Basics. - // **************************************************************** - - /** - * 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 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; - } - - /** - * Return the number of times the activity should automatically reschedule - * itself after it has finished. - * - * @return number of times to repeat this activity - */ - public int getLoopCount() { - return loopCount; - } - - /** - * Set the number of times the activity should automatically reschedule - * itself after it has finished. - * - * @param loopCount number of times to repeat this activity - */ - public void setLoopCount(final int loopCount) { - this.loopCount = loopCount; - } - - /** - * Return true if the activity is executing its first loop. Subclasses - * normally initialize their source state on the first loop. - * - * @return true if executing first loop - */ - public boolean getFirstLoop() { - return firstLoop; - } - - /** - * Set if the activity is executing its first loop. Subclasses normally - * initialize their source state on the first loop. This method will rarely - * need to be called, unless your are reusing activities. - * - * @param firstLoop true if executing first loop - */ - public void setFirstLoop(final boolean firstLoop) { - this.firstLoop = firstLoop; - } - - /** - * Returns whether this interpolation accelerates and then decelerates as it - * interpolates. - * - * @return true if accelerations are being applied apply - */ - public boolean getSlowInSlowOut() { - return slowInSlowOut; - } - - /** - * Sets whether this interpolation accelerates and then decelerates as it - * interpolates. - * - * @param isSlowInSlowOut true if this interpolation inovolves some - * accelerations - */ - public void setSlowInSlowOut(final boolean isSlowInSlowOut) { - slowInSlowOut = isSlowInSlowOut; - } - - // **************************************************************** - // Stepping - Instead of overriding the step methods subclasses - // of this activity will normally override setRelativeTargetValue(). - // This method will be called for every step of the activity with - // a value ranging from 0,0 (for the first step) to 1.0 (for the - // final step). See PTransformActivity for an example. - // **************************************************************** - - /** - * Called when activity is started. Makes sure target value is set properly - * for start of activity. - */ - protected void activityStarted() { - super.activityStarted(); - setRelativeTargetValueAdjustingForMode(0); - } - - /** - * Called at each step of the activity. Sets the current position taking - * mode into account. - * - * @param elapsedTime number of milliseconds since the activity began - */ - - protected void activityStep(final long elapsedTime) { - super.activityStep(elapsedTime); - - float t = elapsedTime / (float) getDuration(); - - t = Math.min(1, t); - t = Math.max(0, t); - - if (getSlowInSlowOut()) { - t = computeSlowInSlowOut(t); - } - - setRelativeTargetValueAdjustingForMode(t); - } - - /** - * Called whenever the activity finishes. Reschedules it if the value of - * loopCount is > 0. - */ - protected void activityFinished() { - setRelativeTargetValueAdjustingForMode(1); - super.activityFinished(); - - final PActivityScheduler scheduler = getActivityScheduler(); - if (loopCount > 1) { - if (loopCount != Integer.MAX_VALUE) { - loopCount--; - } - firstLoop = false; - setStartTime(scheduler.getRoot().getGlobalTime()); - scheduler.addActivity(this); - } - } - - /** - * Stop this activity immediately, and remove it from the activity - * scheduler. If this activity is currently running then stoppedStepping - * will be called after it has been removed from the activity scheduler. - */ - public void terminate() { - loopCount = 0; // set to zero so that we don't reschedule self. - super.terminate(); - } - - /** - * 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) { - } - - /** - * Computes percent or linear interpolation to apply when taking - * acceleration into account. - * - * @param zeroToOne Percentage of activity completed - * @return strength of acceleration - */ - public float computeSlowInSlowOut(final float zeroToOne) { - if (zeroToOne < 0.5f) { - return 2.0f * zeroToOne * zeroToOne; - } - else { - final float complement = 1.0f - zeroToOne; - return 1.0f - 2.0f * complement * complement; - } - } - - /** - * Assigns relative target value taking the mode into account. - * - * @param zeroToOne Percentage of activity completed - */ - protected void setRelativeTargetValueAdjustingForMode(final float zeroToOne) { - final float adjustedZeroToOne; - switch (mode) { - case DESTINATION_TO_SOURCE: - adjustedZeroToOne = 1 - zeroToOne; - break; - - case SOURCE_TO_DESTINATION_TO_SOURCE: - if (zeroToOne <= 0.5f) { - adjustedZeroToOne = zeroToOne * 2; - } - else { - adjustedZeroToOne = 2 * (1 - zeroToOne); - } - break; - case SOURCE_TO_DESTINATION: - default: - // Just treat the zeroToOne as how far along the interpolation - // we are. - adjustedZeroToOne = 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 deleted file mode 100644 index dafa467..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/PTransformActivity.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.activities; - -import java.awt.geom.AffineTransform; - -import edu.umd.cs.piccolo.util.PAffineTransform; - -/** - * PTransformActivity interpolates between two transforms setting its - * target's transform as it goes. See PNode. animate*() for an example of this - * activity in used. The source transform is retrieved from the target just - * before the animation is scheduled to start. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PTransformActivity extends PInterpolatingActivity { - private static final PAffineTransform STATIC_TRANSFORM = new PAffineTransform(); - - private final double[] source; - private double[] destination; - private final Target target; - - /** - * Target Objects that want to get transformed by the transform - * activity must implement this interface. See PNode.animateToTransform() - * for one way to do this. - */ - public interface Target { - - /** - * This will be called by the transform activity for each new transform - * that it computes while it is stepping. - * - * @param aTransform the transform to be applied to the target. - */ - void setTransform(AffineTransform aTransform); - - /** - * This method is called right before the transform activity starts. - * That way an object is always animated from its current position. - * - * @param aSource array to be populated with the target's gurrent matrix - */ - void getSourceMatrix(double[] aSource); - } - - /** - * 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. - * - * Requires that the developer follow up with a setDestinationTransform - * call, otherwise the transition is undefined. - * - * @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); - } - - /** - * 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); - } - - /** - * Create a new PTransformActivity. - *
- * - * @param duration the length of one loop of the activity - * @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 target the object that the activity will be applied to and where - * the source state will be taken from. - * @param destination the destination color state - */ - public PTransformActivity(final long duration, final long stepRate, final int loopCount, final int mode, - final Target target, final AffineTransform destination) { - super(duration, stepRate, loopCount, mode); - source = new double[6]; - 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; - } - - /** - * 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) { - return null; - } - else { - return (double[]) destination.clone(); - } - } - - /** - * 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) { - destination = null; - } - else { - destination = (double[]) newDestination.clone(); - } - } - - /** - * 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); - } - 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); - - STATIC_TRANSFORM.setTransform(source[0] + zeroToOne * (destination[0] - source[0]), source[1] + zeroToOne - * (destination[1] - source[1]), source[2] + zeroToOne * (destination[2] - source[2]), source[3] - + zeroToOne * (destination[3] - source[3]), source[4] + zeroToOne * (destination[4] - source[4]), - source[5] + zeroToOne * (destination[5] - source[5])); - - target.setTransform(STATIC_TRANSFORM); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/activities/package.html b/core/src/main/java/edu/umd/cs/piccolo/activities/package.html deleted file mode 100644 index b857b42..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/activities/package.html +++ /dev/null @@ -1,4 +0,0 @@ -
-This package supports Piccolo activities. Activities are used to control some time -dependent aspect of Piccolo such as animation. - 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 deleted file mode 100644 index 4e1da54..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PBasicInputEventHandler.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.event.FocusEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; - -/** - * PBasicInputEventHandler is the standard class in Piccolo that is used - * to register for mouse and keyboard events on a PNode. Note the events that - * you get depends on the node that you have registered with. For example you - * will only get mouse moved events when the mouse is over the node that you - * have registered with, not when the mouse is over some other node. - *- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PBasicInputEventHandler implements PInputEventListener { - - 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; - } - - switch (type) { - case KeyEvent.KEY_PRESSED: - keyPressed(event); - break; - - case KeyEvent.KEY_RELEASED: - keyReleased(event); - break; - - case KeyEvent.KEY_TYPED: - keyTyped(event); - break; - - case MouseEvent.MOUSE_CLICKED: - mouseClicked(event); - break; - - case MouseEvent.MOUSE_DRAGGED: - mouseDragged(event); - break; - - case MouseEvent.MOUSE_ENTERED: - mouseEntered(event); - break; - - case MouseEvent.MOUSE_EXITED: - mouseExited(event); - break; - - case MouseEvent.MOUSE_MOVED: - mouseMoved(event); - break; - - case MouseEvent.MOUSE_PRESSED: - mousePressed(event); - break; - - case MouseEvent.MOUSE_RELEASED: - mouseReleased(event); - break; - - case MouseWheelEvent.WHEEL_UNIT_SCROLL: - mouseWheelRotated(event); - break; - - case MouseWheelEvent.WHEEL_BLOCK_SCROLL: - mouseWheelRotatedByBlock(event); - break; - - case FocusEvent.FOCUS_GAINED: - keyboardFocusGained(event); - break; - - case FocusEvent.FOCUS_LOST: - keyboardFocusLost(event); - break; - - default: - throw new RuntimeException("Bad Event Type"); - } - } - - // **************************************************************** - // Event Filter - All this event listener can be associated with a event - // filter. The filter accepts and rejects events based on their modifier - // flags and type. If the filter is null (the - // 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; - } - - /** - * 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. - * - * @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) { - } -} 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 deleted file mode 100644 index c28e5aa..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PDragEventHandler.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.event.InputEvent; - -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PDimension; - -/** - * PDragEventHandler is a simple event handler for dragging a node on the - * canvas. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PDragEventHandler extends PDragSequenceEventHandler { - - private PNode draggedNode; - private boolean moveToFrontOnPress; - - /** - * Constructs a drag event handler which defaults to not moving the node to - * the front on drag. - */ - public PDragEventHandler() { - 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(); - if (moveToFrontOnPress) { - draggedNode.moveToFront(); - } - } - - /** - * 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); - draggedNode.localToParent(d); - 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 deleted file mode 100644 index a7782aa..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PDragSequenceEventHandler.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; - -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.util.PUtil; - -/** - * PDragSequenceEventHandler is designed to support mouse pressed, - * dragged, and released interaction sequences. Support is also provided for - * running a continuous activity during the drag sequence. - *
- * PDragSequenceEventHandler should be subclassed by a concrete event handler - * that implements a particular interaction. See PPanEventHandler, - * PZoomEventHandler, and PDragEventHandler for examples. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public abstract class PDragSequenceEventHandler extends PBasicInputEventHandler { - - private double minDragStartDistance = 0; - private transient boolean isDragging = false; - private transient Point2D mousePressedCanvasPoint; - private transient PActivity dragActivity; - private transient PInputEvent dragEvent; - private transient int sequenceInitiatedButton = MouseEvent.NOBUTTON; - - /** Constructs a drag sequence event handler instance. */ - public PDragSequenceEventHandler() { - } - - /** - * 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 - * coordinates) before a new drag sequence is initiate. - * - * @param minDistance in screen coordinates - */ - public void setMinDragStartDistance(final double minDistance) { - minDragStartDistance = minDistance; - } - - /** - * 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) { - mousePressedCanvasPoint = new Point2D.Double(); - } - return mousePressedCanvasPoint; - } - - // **************************************************************** - // Dragging - Methods to indicate the stages of the drag sequence. - // **************************************************************** - - /** - * 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 event) { - dragEvent = event; - startDragActivity(event); - setIsDragging(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 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 event) { - stopDragActivity(event); - dragEvent = null; - event.getComponent().setInteracting(false); - setIsDragging(false); - } - - /** - * 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(); - } - - // **************************************************************** - // Drag Activity - Used for scheduling an activity during a drag - // sequence. For example zooming and auto panning are implemented - // 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; - } - - /** - * 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) { - dragActivityFirstStep(dragEvent); - } - - public void activityStepped(final PActivity activity) { - dragActivityStep(dragEvent); - } - - public void activityFinished(final PActivity activity) { - dragActivityFinalStep(dragEvent); - } - }); - - event.getCamera().getRoot().addActivity(dragActivity); - } - - /** - * 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; - } - - /** - * 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 event) { - } - - /** - * During a drag sequence an activity is scheduled that runs continuously - * while the drag sequence is active. This can be used to support some - * 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 event) { - } - - /** - * 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) { - } - - /** - * 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 = event.getButton(); - - getMousePressedCanvasPoint().setLocation(event.getCanvasPosition()); - if (!isDragging() && shouldStartDragInteraction(event)) { - startDrag(event); - } - } - } - - /** - * 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(event)) { - startDrag(event); - } - return; - } - drag(event); - } - } - - /** - * 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(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 deleted file mode 100644 index b15024f..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PInputEvent.java +++ /dev/null @@ -1,619 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.Cursor; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; -import java.awt.geom.Point2D; - -import javax.swing.SwingUtilities; - -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PComponent; -import edu.umd.cs.piccolo.PInputManager; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PPickPath; - -/** - * PInputEvent is used to notify PInputEventListeners of keyboard and - * mouse input. It has methods for normal event properties such as event - * modifier keys and event canvas location. - *
- * In addition is has methods to get the mouse position and delta in a variety - * of coordinate systems. - *
- * Last of all it provides access to the dispatch manager that can be queried to - * find the current mouse over, mouse focus, and keyboard focus. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PInputEvent { - /** The underlying Swing Event. */ - private final InputEvent inputEvent; - - /** Path relating to the current mouse event. */ - private PPickPath pickPath; - - /** Input manager responsible for the creation of this event. */ - private final PInputManager inputManager; - - /** Flag used to identify this event as handled. */ - private boolean handled; - - /** - * Create an event with the given inputManager and based on the given swing - * event. - * - * @param inputManager source of PInputEvent - * @param event underlying swing event - */ - public PInputEvent(final PInputManager inputManager, final InputEvent event) { - inputEvent = event; - this.inputManager = inputManager; - } - - /** - * Changes the cursor to the one provided and stores it on the cursor stack - * for later retrieval. - * - * @param cursor cursor to push on cursor stack - */ - public void pushCursor(final Cursor cursor) { - final PComponent component = getTopCamera().getComponent(); - component.pushCursor(cursor); - } - - /** - * Removes the top most cursor from the cursor stack and sets it as the - * current cursor. - */ - public void popCursor() { - final PComponent component = getTopCamera().getComponent(); - component.popCursor(); - } - - // **************************************************************** - // Accessing Picked Objects - Methods to access the objects associated - // with this event. - //
- // Cameras can view layers that have - // other cameras on them, so events may be arriving through a stack - // of many cameras. The getCamera() method returns the bottommost - // camera on that stack. The getTopCamera method returns the topmost - // camera on that stack, this is also the camera through which the - // event originated. - // **************************************************************** - - /** - * Return the bottom most camera that is currently painting. If you are - * using internal cameras this may be different then what is returned by - * getTopCamera. - * - * @return the current PickPath's bottom camera. - */ - public PCamera getCamera() { - return getPath().getBottomCamera(); - } - - /** - * Return the topmost camera this is painting. This is the camera associated - * with the PCanvas that requested the current repaint. - * - * @return topmost camera on the pick path - */ - public PCamera getTopCamera() { - return getPath().getTopCamera(); - } - - /** - * Get the canvas associated with the top camera. This is the canvas where - * the originating swing event came from. - * - * @return component attached to the top camera of the current pick path - */ - public PComponent getComponent() { - return getTopCamera().getComponent(); - } - - /** - * Return the input manager that dispatched this event. You can use this - * input manager to find the current mouse focus, mouse over, and key focus - * nodes. You can also set a new key focus node. - * - * @return input manager that dispatched this event - */ - public PInputManager getInputManager() { - return inputManager; - } - - /** - * Return the PPickPath associated with this input event. - * - * @return pick path associated with this event (may be null) - */ - public PPickPath getPath() { - return pickPath; - } - - /** - * Sets the PIckPath associated with this mouse event. - * - * @param path path to associate with this mouse event - */ - public void setPath(final PPickPath path) { - pickPath = path; - } - - /** - * Return the bottom node on the current pickpath, that is the picked node - * furthest from the root node. - * - * @return the currently picked node of this mouse event - */ - public PNode getPickedNode() { - if (pickPath == null) { - return null; - } - return pickPath.getPickedNode(); - } - - // **************************************************************** - // Basics - // **************************************************************** - - /** - * Returns the key code associated with a key event. - * - * @return key code associated with a key event - */ - public int getKeyCode() { - if (isKeyEvent()) { - final KeyEvent e = (KeyEvent) inputEvent; - return e.getKeyCode(); - } - throw new IllegalStateException("Can't get keycode from mouse event"); - } - - /** - * Returns the character associated with a key event. - * - * @return char associated with a key event - */ - public char getKeyChar() { - if (isKeyEvent()) { - final KeyEvent e = (KeyEvent) inputEvent; - return e.getKeyChar(); - } - throw new IllegalStateException("Can't get keychar from mouse event"); - } - - /** - * Returns the location on the keyboard from which the key stroke - * originated. - * - * @return location on keyboard from which stroke originated. - */ - public int getKeyLocation() { - if (isKeyEvent()) { - final KeyEvent e = (KeyEvent) inputEvent; - return e.getKeyLocation(); - } - throw new IllegalStateException("Can't get keylocation from mouse event"); - } - - /** - * Returns whether the key event involves the action key. - * - * @return true if key involved is the action key - */ - public boolean isActionKey() { - if (isKeyEvent()) { - final KeyEvent e = (KeyEvent) inputEvent; - return e.isActionKey(); - } - throw new IllegalStateException("Can't get isActionKey from mouse event"); - } - - /** - * Returns the modifiers provided for the input event by swing. - * - * @return modifier flags for the input event - */ - public int getModifiers() { - if (!isFocusEvent()) { - return inputEvent.getModifiers(); - } - throw new IllegalStateException("Can't get modifiers from focus event"); - } - - /** - * Returns the extended modifiers provided for the input event by swing. - * - * @return extended modifies of input event - */ - public int getModifiersEx() { - if (!isFocusEvent()) { - return inputEvent.getModifiersEx(); - } - throw new IllegalStateException("Can't get modifiers ex from focus event"); - } - - /** - * Returns the click count of the mouse event. - * - * @return click count of mouse event - */ - public int getClickCount() { - if (isMouseEvent()) { - return ((MouseEvent) inputEvent).getClickCount(); - } - throw new IllegalStateException("Can't get clickcount from key event"); - } - - /** - * Returns the time at which the event was emitted. - * - * @return time at which the vent was emitted - */ - public long getWhen() { - if (!isFocusEvent()) { - return inputEvent.getWhen(); - } - throw new IllegalStateException("Can't get when from focus event"); - } - - /** - * Returns whether the alt key is currently down. - * - * @return true if alt key is down - */ - public boolean isAltDown() { - if (!isFocusEvent()) { - return inputEvent.isAltDown(); - } - throw new IllegalStateException("Can't get altdown from focus event"); - } - - /** - * Returns whether the control key is currently down. - * - * @return true if control key is down - */ - public boolean isControlDown() { - if (!isFocusEvent()) { - return inputEvent.isControlDown(); - } - throw new IllegalStateException("Can't get controldown from focus event"); - } - - /** - * Returns whether the meta key is currently down. - * - * @return true if meta key is down - */ - public boolean isMetaDown() { - if (!isFocusEvent()) { - return inputEvent.isMetaDown(); - } - throw new IllegalStateException("Can't get modifiers from focus event"); - } - - /** - * Returns whether the shift key is currently down. - * - * @return true if shift key is down - */ - public boolean isShiftDown() { - if (!isFocusEvent()) { - return inputEvent.isShiftDown(); - } - throw new IllegalStateException("Can't get shiftdown from focus event"); - } - - /** - * Returns whether the mouse event involves the left mouse button. - * - * @return true if left mouse button is involved the mouse event - */ - public boolean isLeftMouseButton() { - if (isMouseEvent()) { - return SwingUtilities.isLeftMouseButton((MouseEvent) getSourceSwingEvent()); - } - throw new IllegalStateException("Can't get isLeftMouseButton from focus event"); - } - - /** - * Returns whether the mouse event involves the middle mouse button. - * - * @return true if middle mouse button is involved the mouse event - */ - public boolean isMiddleMouseButton() { - if (isMouseEvent()) { - return SwingUtilities.isMiddleMouseButton((MouseEvent) getSourceSwingEvent()); - } - throw new IllegalStateException("Can't get isMiddleMouseButton from focus event"); - } - - /** - * Returns whether the mouse event involves the right mouse button. - * - * @return true if right mouse button is involved the mouse event - */ - public boolean isRightMouseButton() { - if (isMouseEvent()) { - return SwingUtilities.isRightMouseButton((MouseEvent) getSourceSwingEvent()); - } - throw new IllegalStateException("Can't get isRightMouseButton from focus event"); - } - - /** - * Return true if another event handler has already handled this event. - * Event handlers should use this as a hint before handling the event - * themselves and possibly reject events that have already been handled. - * - * @return true if event has been marked as handled - */ - public boolean isHandled() { - return handled; - } - - /** - * Set that this event has been handled by an event handler. This is a - * relaxed for of consuming events. The event will continue to get - * dispatched to event handlers even after it is marked as handled, but - * other event handlers that might conflict are expected to ignore events - * that have already been handled. - * - * @param handled whether the event is marked - */ - public void setHandled(final boolean handled) { - this.handled = handled; - } - - /** - * Returns the mouse button value of the underlying mouse event. - * - * @return button value of underlying mouse event - */ - public int getButton() { - if (isMouseEvent()) { - return ((MouseEvent) inputEvent).getButton(); - } - throw new IllegalStateException("Can't get button from key event"); - } - - /** - * Returns the current value of the wheel rotation on Mouse Wheel Rotation - * events. - * - * @return wheel rotation value - */ - public int getWheelRotation() { - if (isMouseWheelEvent()) { - return ((MouseWheelEvent) inputEvent).getWheelRotation(); - } - throw new IllegalStateException("Can't get wheel rotation from non-wheel event"); - } - - /** - * Returns the underlying swing event that this PInputEvent is wrapping. - * - * @return underlying swing event - */ - public InputEvent getSourceSwingEvent() { - return inputEvent; - } - - // **************************************************************** - // Classification - Methods to distinguish between mouse and key - // events. - // **************************************************************** - - /** - * Returns whether the underlying event is a KeyEvent. - * - * @return true if is key event - */ - public boolean isKeyEvent() { - return inputEvent instanceof KeyEvent; - } - - /** - * Returns whether the underlying event is a MouseEvent. - * - * @return true if is mouse event - */ - public boolean isMouseEvent() { - return inputEvent instanceof MouseEvent; - } - - /** - * Returns whether the underlying event is a Mouse Wheel Event. - * - * @return true if is a mouse wheel event - */ - - public boolean isMouseWheelEvent() { - return inputEvent instanceof MouseWheelEvent; - } - - /** - * Returns whether the underlying event is a Focus Event. - * - * @return true if is focus event - */ - public boolean isFocusEvent() { - return inputEvent == null; - } - - /** - * Returns whether the underlying event is a mouse entered or exited event. - * - * @return true if is a mouse entered or exited event - */ - public boolean isMouseEnteredOrMouseExited() { - if (isMouseEvent()) { - return inputEvent.getID() == MouseEvent.MOUSE_ENTERED || inputEvent.getID() == MouseEvent.MOUSE_EXITED; - } - return false; - } - - /** - * Returns whether or not this event is a popup menu trigger event for the - * platform. Must not be called if this event isn't a mouse event. - *
- * Note: Popup menus are triggered differently on different systems.
- * Therefore, isPopupTrigger
should be checked in both
- * mousePressed
and mouseReleased
for proper
- * cross-platform functionality.
- *
- * @return boolean, true if this event triggers a popup menu for this
- * platform
- */
- public boolean isPopupTrigger() {
- if (isMouseEvent()) {
- return ((MouseEvent) inputEvent).isPopupTrigger();
- }
- throw new IllegalStateException("Can't get clickcount from key event");
- }
-
- // ****************************************************************
- // Coordinate Systems - Methods for getting mouse location data
- // These methods are only designed for use with PInputEvents that
- // return true to the isMouseEvent method.
- // ****************************************************************
-
- /**
- * Return the mouse position in PCanvas coordinates.
- *
- * @return mouse position in PCanvas coordinates
- */
- public Point2D getCanvasPosition() {
- return (Point2D) inputManager.getCurrentCanvasPosition().clone();
- }
-
- /**
- * Return the delta between the last and current mouse position in PCanvas
- * coordinates.
- *
- * @return delta between last and current mouse position as measured by the
- * PCanvas
- */
- public PDimension getCanvasDelta() {
- final Point2D last = inputManager.getLastCanvasPosition();
- final Point2D current = inputManager.getCurrentCanvasPosition();
- return new PDimension(current.getX() - last.getX(), current.getY() - last.getY());
- }
-
- /**
- * Return the mouse position relative to a given node on the pick path.
- *
- * @param nodeOnPath node on the current PPickPath
- *
- * @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);
- }
-
- /**
- * Return the delta between the last and current mouse positions relative to
- * a given node on the pick path.
- *
- * @param nodeOnPath node from which to measure
- * @return delta between current mouse position and a given node on the pick
- * 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);
- }
-
- /**
- * Return the mouse position transformed through the view transform of the
- * bottom camera.
- *
- * @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);
- }
-
- /**
- * Return the delta between the last and current mouse positions transformed
- * through the view transform of the bottom camera.
- *
- * @return delta between last and current mouse position as measured by the
- * 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);
- }
-
- /**
- * Returns a string representation of this object for debugging purposes.
- *
- * @return string representation of this object
- */
- public String toString() {
- final StringBuffer result = new StringBuffer();
-
- result.append(super.toString().replaceAll(".*\\.", ""));
- result.append('[');
- if (handled) {
- result.append("handled");
- }
- result.append(']');
-
- return result.toString();
- }
-}
diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PInputEventFilter.java b/core/src/main/java/edu/umd/cs/piccolo/event/PInputEventFilter.java
deleted file mode 100644
index 0f510f3..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/event/PInputEventFilter.java
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo.event;
-
-import java.awt.event.FocusEvent;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseWheelEvent;
-
-/**
- * PInputEventFilter is a class that filters input events based on the
- * events modifiers and type. Any PBasicInputEventHandler that is associated
- * with an event filter will only receive events that pass through the filter.
- *
- * To be accepted events must contain all the modifiers listed in the andMask, - * at least one of the modifiers listed in the orMask, and none of the modifiers - * listed in the notMask. The event filter also lets you specify specific event - * types (mousePressed, released, ...) to accept or reject. - *
- * If the event filter is set to consume, then it will call consume on any event - * that it successfully accepts. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PInputEventFilter { - /** Mask representing all possible modifiers. */ - public static final int ALL_MODIFIERS_MASK = InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK - | InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK | InputEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK - | InputEvent.META_MASK; - - /** If event modifiers don't match this exactly, event it filtered. */ - private int andMask; - - /** If event modifiers have no bits from orMask enabled, event is filtered. */ - private int orMask; - - /** If event modifier has any of the notMask bits on, it is not accepted. */ - private int notMask; - - /** Number of clicks that an incoming event must have to be accepted. */ - private short clickCount = -1; - - /** Whether accepted events should be marked as handled. */ - private boolean marksAcceptedEventsAsHandled = false; - - /** Whether handled events should be immediately filtered. */ - private boolean acceptsAlreadyHandledEvents = false; - - /** Whether key pressed events are accepted. */ - private boolean acceptsKeyPressed = true; - - /** Whether key released events are accepted. */ - private boolean acceptsKeyReleased = true; - - /** Whether key typed events are accepted. */ - private boolean acceptsKeyTyped = true; - - /** Whether mouse clicked events are accepted. */ - private boolean acceptsMouseClicked = true; - - /** Whether mouse dragged events are accepted. */ - private boolean acceptsMouseDragged = true; - - /** Whether mouse entered events are accepted. */ - private boolean acceptsMouseEntered = true; - - /** Whether mouse exited events are accepted. */ - private boolean acceptsMouseExited = true; - - /** Whether mouse moved events are accepted. */ - private boolean acceptsMouseMoved = true; - - /** Whether mouse pressed events are accepted. */ - private boolean acceptsMousePressed = true; - - /** Whether mouse released events are accepted. */ - private boolean acceptsMouseReleased = true; - - /** Whether mouse wheel rotated events are accepted. */ - private boolean acceptsMouseWheelRotated = true; - - /** Whether focus events are accepted. */ - private boolean acceptsFocusEvents = true; - - /** - * Creates a PInputEventFilter that accepts everything. - */ - public PInputEventFilter() { - acceptEverything(); - } - - /** - * Creates a PInputEventFilter that will accept events if they have the - * given andMask. - * - * @param andMask exact pattern event modifiers must be to get accepted - */ - public PInputEventFilter(final int andMask) { - this(); - this.andMask = andMask; - } - - /** - * Creates a PInputEventFilter that will accept events if they have the - * given andMask and do not contain any of the bits in the notMask. - * - * @param andMask exact pattern event modifiers must be to get accepted - * @param notMask if any or these bits are on event is not accepted - */ - public PInputEventFilter(final int andMask, final int notMask) { - this(andMask); - this.notMask = notMask; - } - - /** - * Returns true if the passed event is one that is accepted. - * - * @param event Event under consideration - * @param type The type of event encoded as the PInputEvent - * @return true if event is accepted - */ - public boolean acceptsEvent(final PInputEvent event, final int type) { - boolean aResult = false; - int modifiers = 0; - - if (!event.isFocusEvent()) { - modifiers = event.getModifiers(); - } - - if (event.isHandled() && !acceptsAlreadyHandledEvents) { - return false; - } - - if (modifiers != 0) { - if ((modifiers & andMask) != andMask || (modifiers & notMask) != 0) { - return false; - } - - if (orMask != ALL_MODIFIERS_MASK && (modifiers & orMask) == 0) { - return false; - } - } - - if (event.isMouseEvent() && clickCount != -1 && clickCount != event.getClickCount()) { - return false; - } - - switch (type) { - case KeyEvent.KEY_PRESSED: - aResult = getAcceptsKeyPressed(); - break; - - case KeyEvent.KEY_RELEASED: - aResult = getAcceptsKeyReleased(); - break; - - case KeyEvent.KEY_TYPED: - aResult = getAcceptsKeyTyped(); - break; - - case MouseEvent.MOUSE_CLICKED: - aResult = getAcceptsMouseClicked(); - break; - - case MouseEvent.MOUSE_DRAGGED: - aResult = getAcceptsMouseDragged(); - break; - - case MouseEvent.MOUSE_ENTERED: - aResult = getAcceptsMouseEntered(); - break; - - case MouseEvent.MOUSE_EXITED: - aResult = getAcceptsMouseExited(); - break; - - case MouseEvent.MOUSE_MOVED: - aResult = getAcceptsMouseMoved(); - break; - - case MouseEvent.MOUSE_PRESSED: - aResult = getAcceptsMousePressed(); - break; - - case MouseEvent.MOUSE_RELEASED: - aResult = getAcceptsMouseReleased(); - break; - - case MouseWheelEvent.WHEEL_UNIT_SCROLL: - case MouseWheelEvent.WHEEL_BLOCK_SCROLL: - aResult = getAcceptsMouseWheelRotated(); - break; - - case FocusEvent.FOCUS_GAINED: - case FocusEvent.FOCUS_LOST: - aResult = getAcceptsFocusEvents(); - break; - - default: - throw new RuntimeException("PInputEvent with bad ID"); - } - - if (aResult && getMarksAcceptedEventsAsHandled()) { - event.setHandled(true); - } - - return aResult; - } - - /** - * Makes this filter accept all mouse click combinations. - */ - public void acceptAllClickCounts() { - clickCount = -1; - } - - /** - * Makes the filter accept all event types. - */ - public void acceptAllEventTypes() { - acceptsKeyPressed = true; - acceptsKeyReleased = true; - acceptsKeyTyped = true; - acceptsMouseClicked = true; - acceptsMouseDragged = true; - acceptsMouseEntered = true; - acceptsMouseExited = true; - acceptsMouseMoved = true; - acceptsMousePressed = true; - acceptsMouseReleased = true; - acceptsMouseWheelRotated = true; - acceptsFocusEvents = true; - } - - /** - * Makes this filter accept absolutely everything. - */ - public void acceptEverything() { - acceptAllEventTypes(); - setAndMask(0); - setOrMask(ALL_MODIFIERS_MASK); - setNotMask(0); - acceptAllClickCounts(); - } - - /** - * Returns whether this filter accepts key pressed events. - * - * @return true if filter accepts key pressed events - */ - public boolean getAcceptsKeyPressed() { - return acceptsKeyPressed; - } - - /** - * Returns whether this filter accepts key released events. - * - * @return true if filter accepts key released events - */ - public boolean getAcceptsKeyReleased() { - return acceptsKeyReleased; - } - - /** - * Returns whether this filter accepts key typed events. - * - * @return true if filter accepts key typed events - */ - public boolean getAcceptsKeyTyped() { - return acceptsKeyTyped; - } - - /** - * Returns whether this filter accepts mouse clicked events. - * - * @return true if filter accepts mouse clicked events - */ - public boolean getAcceptsMouseClicked() { - return acceptsMouseClicked; - } - - /** - * Returns whether this filter accepts mouse dragged events. - * - * @return true if filter accepts mouse dragged events - */ - public boolean getAcceptsMouseDragged() { - return acceptsMouseDragged; - } - - /** - * Returns whether this filter accepts mouse entered events. - * - * @return true if filter accepts mouse entered events - */ - public boolean getAcceptsMouseEntered() { - return acceptsMouseEntered; - } - - /** - * Returns whether this filter accepts mouse exited events. - * - * @return true if filter accepts mouse exited events - */ - public boolean getAcceptsMouseExited() { - return acceptsMouseExited; - } - - /** - * Returns whether this filter accepts mouse moved events. - * - * @return true if filter accepts mouse moved events - */ - public boolean getAcceptsMouseMoved() { - return acceptsMouseMoved; - } - - /** - * Returns whether this filter accepts mouse pressed events. - * - * @return true if filter accepts mouse pressed events - */ - public boolean getAcceptsMousePressed() { - return acceptsMousePressed; - } - - /** - * Returns whether this filter accepts mouse released events. - * - * @return true if filter accepts mouse released events - */ - public boolean getAcceptsMouseReleased() { - return acceptsMouseReleased; - } - - /** - * Returns whether this filter accepts mouse wheel rotated events. - * - * @return true if filter accepts mouse wheel rotated events - */ - public boolean getAcceptsMouseWheelRotated() { - return acceptsMouseWheelRotated; - } - - /** - * Returns whether this filter accepts focus events. - * - * @return true if filter accepts focus events - */ - public boolean getAcceptsFocusEvents() { - return acceptsFocusEvents; - } - - /** - * Returns whether this filter accepts events that have already been flagged - * as handled. - * - * @return true if filter accepts events that have already been flagged as - * handled - */ - public boolean getAcceptsAlreadyHandledEvents() { - return acceptsAlreadyHandledEvents; - } - - /** - * Returns whether this filter marks events as handled if they are accepted. - * - * @return true if filter will mark events as filtered if they are accepted - */ - public boolean getMarksAcceptedEventsAsHandled() { - return marksAcceptedEventsAsHandled; - } - - /** - * Flags all mouse click events as disallowed, regardless of button - * configuration. - */ - public void rejectAllClickCounts() { - clickCount = Short.MAX_VALUE; - } - - /** - * Configures filter so that no events will ever get accepted. By itself not - * terribly useful, but it's a more restrictive starting point than - * acceptAllEvents(); - */ - public void rejectAllEventTypes() { - acceptsKeyPressed = false; - acceptsKeyReleased = false; - acceptsKeyTyped = false; - acceptsMouseClicked = false; - acceptsMouseDragged = false; - acceptsMouseEntered = false; - acceptsMouseExited = false; - acceptsMouseMoved = false; - acceptsMousePressed = false; - acceptsMouseReleased = false; - acceptsMouseWheelRotated = false; - acceptsFocusEvents = false; - } - - /** - * Sets the number of clicks that an incoming event must have to be accepted. - * - * @param aClickCount number clicks that an incoming event must have to be accepted - */ - public void setAcceptClickCount(final short aClickCount) { - clickCount = aClickCount; - } - - /** - * Sets whether this filter accepts key pressed events. - * - * @param aBoolean whether filter should accept key pressed events - */ - public void setAcceptsKeyPressed(final boolean aBoolean) { - acceptsKeyPressed = aBoolean; - } - - /** - * Sets whether this filter accepts key released events. - * - * @param aBoolean whether filter should accept key released events - */ - public void setAcceptsKeyReleased(final boolean aBoolean) { - acceptsKeyReleased = aBoolean; - } - - /** - * Sets whether this filter accepts key typed events. - * - * @param aBoolean whether filter should accept key typed events - */ - - public void setAcceptsKeyTyped(final boolean aBoolean) { - acceptsKeyTyped = aBoolean; - } - - /** - * Sets whether this filter accepts mouse clicked events. - * - * @param aBoolean whether filter should accept mouse clicked events - */ - public void setAcceptsMouseClicked(final boolean aBoolean) { - acceptsMouseClicked = aBoolean; - } - - /** - * Sets whether this filter accepts mouse dragged events. - * - * @param aBoolean whether filter should accept mouse dragged events - */ - public void setAcceptsMouseDragged(final boolean aBoolean) { - acceptsMouseDragged = aBoolean; - } - - /** - * Sets whether this filter accepts mouse entered events. - * - * @param aBoolean whether filter should accept mouse entered events - */ - public void setAcceptsMouseEntered(final boolean aBoolean) { - acceptsMouseEntered = aBoolean; - } - - /** - * Sets whether this filter accepts mouse exited events. - * - * @param aBoolean whether filter should accept mouse exited events - */ - public void setAcceptsMouseExited(final boolean aBoolean) { - acceptsMouseExited = aBoolean; - } - - /** - * Sets whether this filter accepts mouse moved events. - * - * @param aBoolean whether filter should accept mouse moved events - */ - public void setAcceptsMouseMoved(final boolean aBoolean) { - acceptsMouseMoved = aBoolean; - } - - /** - * Sets whether this filter accepts mouse pressed events. - * - * @param aBoolean whether filter should accept mouse pressed events - */ - public void setAcceptsMousePressed(final boolean aBoolean) { - acceptsMousePressed = aBoolean; - } - - /** - * Sets whether this filter accepts mouse released events. - * - * @param aBoolean whether filter should accept mouse released events - */ - public void setAcceptsMouseReleased(final boolean aBoolean) { - acceptsMouseReleased = aBoolean; - } - - /** - * Sets whether this filter accepts mouse wheel rotation events. - * - * @param aBoolean whether filter should accept mouse wheel rotated events - */ - public void setAcceptsMouseWheelRotated(final boolean aBoolean) { - acceptsMouseWheelRotated = aBoolean; - } - - /** - * Sets whether this filter accepts focus events. - * - * @param aBoolean whether filter should accept focus events - */ - public void setAcceptsFocusEvents(final boolean aBoolean) { - acceptsFocusEvents = aBoolean; - } - - /** - * Sets and mask used to filter events. All bits of the andMask must be 1s - * for the event to be accepted. - * - * @param aAndMask the and mask to use for filtering events - */ - public void setAndMask(final int aAndMask) { - andMask = aAndMask; - } - - /** - * Sets whether already handled events should be accepted. - * - * @param aBoolean whether already handled events should be accepted - */ - public void setAcceptsAlreadyHandledEvents(final boolean aBoolean) { - acceptsAlreadyHandledEvents = aBoolean; - } - - /** - * Sets whether events will be marked as dirty once accepted. - * - * @param aBoolean whether events will be marked as dirty once accepted - */ - public void setMarksAcceptedEventsAsHandled(final boolean aBoolean) { - marksAcceptedEventsAsHandled = aBoolean; - } - - /** - * Sets not mask used to filter events. If any of the not bits are enabled, - * then the event is not accepted. - * - * @param aNotMask the not mask to use for filtering events - */ - public void setNotMask(final int aNotMask) { - notMask = aNotMask; - } - - /** - * Sets or mask used to filter events. If any of the or bits are enabled, - * then the event is accepted. - * - * @param aOrMask the or mask to use for filtering events - */ - public void setOrMask(final int aOrMask) { - orMask = aOrMask; - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/PInputEventListener.java b/core/src/main/java/edu/umd/cs/piccolo/event/PInputEventListener.java deleted file mode 100644 index 2e567f5..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PInputEventListener.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.util.EventListener; - -/** - * PInputEventListener defines the most basic interface for objects that - * want to listen to PNodes for input events. This interface is very simple so - * that others may extend Piccolo's input management system. If you are just - * using Piccolo's default input management system then you will most often use - * PBasicInputEventHandler to register with a node for input events. - *
- * - * @see PBasicInputEventHandler - * @version 1.0 - * @author Jesse Grosjean - */ -public interface PInputEventListener extends EventListener { - /** - * Called whenever an event is emitted. Used to notify listeners that an - * event is available for proecessing. - * - * @param event event that was emitted - * @param type type of event - */ - void processEvent(PInputEvent event, int type); -} 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 deleted file mode 100644 index 113354f..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PPanEventHandler.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.event.InputEvent; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; - -/** - * PPanEventHandler provides event handlers for basic panning of the - * canvas view with the left mouse. The interaction is that clicking and - * dragging the mouse translates the view so that the point on the surface stays - * under the mouse. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PPanEventHandler extends PDragSequenceEventHandler { - - 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); - } - - /** - * 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); - } - - /** - * 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 = event.getDelta(); - c.translateView(d.getWidth(), d.getHeight()); - } - } - - // **************************************************************** - // 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; - } - - /** - * Set the minAutoPan speed in pixels per second. - * - * @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 pixels per second. - * - * @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; - } - - /** - * Returns the minAutoPan speed in pixels per second. - * - * @since 1.3 - * @return minimum distance the autopan feature can pan the view - */ - public double getMinAutoPanSpeed() { - return minAutopanSpeed; - } - - /** - * Returns the maxAutoPan speed in pixels per second. - * - * @since 1.3 - * @return max distance the autopan feature can pan the view by - */ - public double getMaxAutoPanSpeed() { - return maxAutopanSpeed; - } - - /** - * 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 event) { - if (!autopan) { - return; - } - - final PCamera c = event.getCamera(); - final PBounds b = c.getBoundsReference(); - final Point2D l = event.getPositionRelativeTo(c); - final int outcode = b.outcode(l); - final PDimension delta = new PDimension(); - - if ((outcode & Rectangle2D.OUT_TOP) != 0) { - delta.height = validatePanningSpeed(-1.0 - 0.5 * Math.abs(l.getY() - b.getY())); - } - else if ((outcode & Rectangle2D.OUT_BOTTOM) != 0) { - delta.height = validatePanningSpeed(1.0 + 0.5 * Math.abs(l.getY() - (b.getY() + b.getHeight()))); - } - - if ((outcode & Rectangle2D.OUT_RIGHT) != 0) { - delta.width = validatePanningSpeed(1.0 + 0.5 * Math.abs(l.getX() - (b.getX() + b.getWidth()))); - } - else if ((outcode & Rectangle2D.OUT_LEFT) != 0) { - delta.width = validatePanningSpeed(-1.0 - 0.5 * Math.abs(l.getX() - b.getX())); - } - - c.localToView(delta); - - if (delta.width != 0 || delta.height != 0) { - c.translateView(delta.width, delta.height); - } - } - - /** - * 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 double absDelta = Math.abs(delta); - - final double clippedDelta; - if (absDelta < minDelta) { - clippedDelta = minDelta; - } - else if (absDelta > maxDelta) { - clippedDelta = maxDelta; - } - else { - clippedDelta = 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 deleted file mode 100644 index e0569af..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/event/PZoomEventHandler.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.event.InputEvent; -import java.awt.geom.Point2D; - -import edu.umd.cs.piccolo.PCamera; - -/** - * ZoomEventhandler provides event handlers for basic zooming of the - * canvas view with the right (third) button. The interaction is that the - * initial mouse press defines the zoom anchor point, and then moving the mouse - * to the right zooms with a speed proportional to the amount the mouse is moved - * to the right of the anchor point. Similarly, if the mouse is moved to the - * left, the the view is zoomed out. - *
- * On a Mac with its single mouse button one may wish to change the standard - * right mouse button zooming behavior. This can be easily done with the - * PInputEventFilter. For example to zoom with button one and shift you would do - * this: - *
- *
- *
- *
- * zoomEventHandler.getEventFilter().setAndMask(InputEvent.BUTTON1_MASK |
- * InputEvent.SHIFT_MASK);
- *
- *
- *
- * @version 1.0
- * @author Jesse Grosjean
- */
-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;
-
- /**
- * Creates a new zoom handler.
- */
- public PZoomEventHandler() {
- super();
- setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_MASK));
- }
-
- // ****************************************************************
- // Zooming
- // ****************************************************************
-
- /**
- * Returns the minimum view magnification factor that this event handler is
- * bound by. The default is 0.
- *
- * @return the minimum camera view scale
- */
- public double getMinScale() {
- return minScale;
- }
-
- /**
- * Sets the minimum view magnification factor that this event handler is
- * bound by. The camera is left at its current scale even if
- * minScale
is larger than the current scale.
- *
- * @param minScale the minimum scale, must not be negative.
- */
- public void setMinScale(final double minScale) {
- this.minScale = minScale;
- }
-
- /**
- * Returns the maximum view magnification factor that this event handler is
- * bound by. The default is Double.MAX_VALUE.
- *
- * @return the maximum camera view scale
- */
- public double getMaxScale() {
- return maxScale;
- }
-
- /**
- * Sets the maximum view magnification factor that this event handler is
- * bound by. The camera is left at its current scale even if
- * maxScale
is smaller than the current scale. Use
- * Double.MAX_VALUE to specify the largest possible scale.
- *
- * @param maxScale the maximum scale, must not be negative.
- */
- public void setMaxScale(final double maxScale) {
- this.maxScale = maxScale;
- }
-
- /**
- * 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);
- }
-
- /**
- * 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;
-
- if (newScale < minScale) {
- scaleDelta = minScale / currentScale;
- }
- if (maxScale > 0 && newScale > maxScale) {
- scaleDelta = maxScale / currentScale;
- }
-
- camera.scaleViewAboutPoint(scaleDelta, viewZoomPoint.getX(), viewZoomPoint.getY());
- }
-}
diff --git a/core/src/main/java/edu/umd/cs/piccolo/event/package.html b/core/src/main/java/edu/umd/cs/piccolo/event/package.html
deleted file mode 100644
index 9fa7ad1..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/event/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
"SansSerif"
.
- */
- // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF,
- // Font.PLAIN, 12); jdk 1.6+
- public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 12);
-
- /**
- * Default text color if not otherwise specified in the HTML text,
- * Color.BLACK
.
- */
- public static final Color DEFAULT_TEXT_COLOR = Color.BLACK;
-
- /**
- * The property name that identifies a change of this node's font (see
- * {@link #getFont getFont}). Both old and new value will be set in any
- * property change event.
- */
- public static final String PROPERTY_FONT = "font";
-
- /**
- * The property code that identifies a change of this node's font (see
- * {@link #getFont getFont}). Both old and new value will be set in any
- * property change event.
- */
- public static final int PROPERTY_CODE_FONT = 1 << 20;
-
- /**
- * The property name that identifies a change of this node's HTML text (see
- * {@link #getText getText}). Both old and new value will be set in any
- * property change event.
- */
- public static final String PROPERTY_TEXT = "text";
-
- /**
- * The property code that identifies a change of this node's HTML text (see
- * {@link #getText getText}). Both old and new value will be set in any
- * property change event.
- */
- public static final int PROPERTY_CODE_TEXT = 1 << 21;
-
- /**
- * The property name that identifies a change of this node's HTML text color
- * (see {@link #getTextColor getTextColor}). Both old and new value will be set
- * in any property change event.
- */
- public static final String PROPERTY_TEXT_COLOR = "text color";
-
- /**
- * The property code that identifies a change of this node's HTML text color
- * (see {@link #getTextColor getTextColor}). Both old and new value will be set
- * in any property change event.
- */
- public static final int PROPERTY_CODE_TEXT_COLOR = 1 << 22;
-
- /** Underlying JLabel used to handle the rendering logic. */
- private final JLabel label;
-
- /** Object that encapsulates the HTML rendering logic. */
- private transient View htmlView;
-
- /**
- * Create an empty HTML text node with the default font and text color.
- */
- public PHtmlView() {
- this(null, DEFAULT_FONT, DEFAULT_TEXT_COLOR);
- }
-
- /**
- * Create a HTML text node with the specified HTML text and the default font
- * and text color.
- *
- * @param text HTML text for this HTML text node
- */
- public PHtmlView(final String text) {
- this(text, DEFAULT_FONT, DEFAULT_TEXT_COLOR);
- }
-
- /**
- * Create a HTML text node with the specified HTML text, font, and text
- * color. The font and text color are used to render the HTML text if not
- * otherwise specified via CSS.
- *
- * @param text HTML text for this HTML text node
- * @param font font for this HTML text node
- * @param textColor text color for this HTML text node
- */
- public PHtmlView(final String text, final Font font, final Color textColor) {
- label = new JLabel(text);
- label.setFont(font);
- label.setForeground(textColor);
- super.setBounds(0, 0, label.getPreferredSize().getWidth(), label.getPreferredSize().getHeight());
- update();
- }
-
- /**
- * Return the HTML text for this HTML text node.
- *
- * @return the HTML text for this HTML text node
- */
- public String getText() {
- return label.getText();
- }
-
- /**
- * Set the HTML text for this HTML text node to text
.
- *
- * - * This is a bound property. - *
- * - * @param text HTML text for this HTML text node - */ - public void setText(final String text) { - final String oldText = label.getText(); - - if (oldText == null && text == null) { - return; - } - - if (oldText == null || !oldText.equals(text)) { - label.setText(text); - - update(); - firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, oldText, label.getText()); - } - } - - /** - * Return the font for this HTML text node. This font is used to render the - * HTML text if not otherwise specified via CSS. Defaults to - * {@link #DEFAULT_FONT}. - * - * @return the font for this HTML text node - */ - public Font getFont() { - return label.getFont(); - } - - /** - * Set the font for this HTML text node tofont
. This font is
- * used to render the HTML text if not otherwise specified via CSS.
- *
- * - * This is a bound property. - *
- * - * @param font font for this HTML text node - */ - public void setFont(final Font font) { - final Font oldFont = label.getFont(); - label.setFont(font); - update(); - firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, oldFont, label.getFont()); - } - - /** - * Return the text color for this HTML text node. This text color is used to - * render the HTML text if not otherwise specified via CSS. Defaults to - * {@link #DEFAULT_TEXT_COLOR}. - * - * @return the text color for this HTML text node - */ - public Color getTextColor() { - return label.getForeground(); - } - - /** - * Set the text color for this HTML text node totextColor
.
- * 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 textColor) {
- final Color oldColor = label.getForeground();
- label.setForeground(textColor);
- repaint();
- firePropertyChange(PROPERTY_CODE_TEXT_COLOR, PROPERTY_TEXT_COLOR, oldColor, label.getForeground());
- }
-
- /**
- * Applies all properties to the underlying JLabel, creates an htmlView and
- * updates bounds.
- */
- private void update() {
- String htmlContent = label.getText();
- if (htmlContent == null) {
- htmlContent = "";
- }
-
- htmlView = BasicHTML.createHTMLView(label, htmlContent);
- fitHeightToHtmlContent();
-
- repaint();
- }
-
- /**
- * Resizes the height to be as tall as its rendered html. Takes wrapping
- * into account.
- */
- private void fitHeightToHtmlContent() {
- if (getWidth() > 0) {
- htmlView.setSize((float) getWidth(), 0f);
-
- float wrapHeight = htmlView.getPreferredSpan(View.Y_AXIS);
- label.setSize(new Dimension((int) getWidth(), (int) wrapHeight));
-
- if (getHeight() < wrapHeight) {
- System.out.println(getHeight());
- System.out.println(wrapHeight);
- super.setBounds(getX(), getY(), getWidth(), wrapHeight);
- }
- }
- }
-
- /** {@inheritDoc} */
- public boolean setBounds(final double x, final double y, final double width, final double height) {
- final boolean boundsChanged = super.setBounds(x, y, width, height);
- update();
- return boundsChanged;
- }
-
- /** {@inheritDoc} */
- public boolean setBounds(final Rectangle2D newBounds) {
- final boolean boundsChanged = super.setBounds(newBounds);
- update();
- return boundsChanged;
- }
-
- /**
- * {@inheritDoc}
- *
- * - * The HTML text is painted last, so it will appear on top of any child - * nodes. - *
- */ - protected void paint(final PPaintContext paintContext) { - super.paint(paintContext); - paintContext.pushClip(getBounds()); - final Graphics2D g2 = paintContext.getGraphics(); - htmlView.paint(g2, getBounds().getBounds()); - paintContext.popClip(getBounds()); - } - - /** - * Return the address specified in the HTML link at the specified point in - * this node's local coordinate system, if any. - * - * @param point point in this node's local coordinate system - * @return the address specified in the HTML link at the specified point in - * this node's local coordinate system, ornull
if no
- * such HTML link exists
- */
- public String getLinkAddressAt(final Point2D point) {
- return getLinkAddressAt(point.getX(), point.getY());
- }
-
- /**
- * Return the address specified in the HTML link at the specified x and y
- * coordinates in this node's local coordinate system, if any.
- *
- * @param x x coordinate in this node's local coordinate system
- * @param y y coordinate in this node's local coordinate system
- * @return the address specified in the HTML link at the specified x and y
- * coordinates in this node's local coordinate system, or
- * null
if no such HTML link exists
- */
- public String getLinkAddressAt(final double x, final double y) {
- int position = pointToModelIndex(x, y);
-
- final String text = label.getText();
-
- String address = null;
-
- int currentPos = 0;
- while (currentPos < text.length()) {
- currentPos = text.indexOf('<', currentPos);
- if (currentPos == -1 || position < currentPos) {
- break;
- }
-
- final int tagStart = currentPos;
- final int tagEnd = findTagEnd(text, currentPos);
-
- if (tagEnd == -1) {
- return null;
- }
-
- currentPos = tagEnd + 1;
-
- final String tag = text.substring(tagStart, currentPos);
-
- position += tag.length();
-
- if ("".equals(tag)) {
- address = null;
- }
- else if (tag.startsWith("-1 if the end of the string was encountered
- * before the end of the tag was encountered.
- *
- * @param text HTML text being searched
- * @param startPos where in the string to start searching for ">"
- * @return index after the ">" character
- */
- private int findTagEnd(final String text, final int startPos) {
- int currentPos = startPos;
-
- currentPos++;
-
- while (currentPos > 0 && currentPos < text.length() && text.charAt(currentPos) != '>') {
- if (text.charAt(currentPos) == '\"') {
- currentPos = text.indexOf('\"', currentPos + 1);
- }
- else if (text.charAt(currentPos) == '\'') {
- currentPos = text.indexOf('\'', currentPos + 1);
- }
- currentPos++;
- }
-
- if (currentPos == 0 || currentPos >= text.length()) {
- return -1;
- }
-
- return currentPos + 1;
- }
-
- /**
- * Given a tag, extracts the value of the href attribute or returns null if
- * none was found.
- *
- * @param tag from which to extract the href value
- * @return href value without quotes or null
if not found
- */
- private String extractHref(final String tag) {
- int currentPos = 0;
-
- final String href = null;
-
- while (currentPos >= 0 && currentPos < tag.length() - 1) {
- currentPos = tag.indexOf('=', currentPos + 1);
- if (currentPos != -1 && isHrefAttributeAssignment(tag, currentPos)) {
- return extractHrefValue(tag, currentPos + 1);
- }
- }
- return href;
- }
-
- /**
- * Starting at the character after the equal sign of an href=..., it extract
- * the value. Handles single, double, and no quotes.
- *
- * @param tag tag
- * @param startPos start position
- * @return value of href or null if not found.
- */
- private String extractHrefValue(final String tag, final int startPos) {
- int currentPos = startPos;
-
- if (tag.charAt(currentPos) == '\"') {
- final int startHref = currentPos + 1;
- currentPos = tag.indexOf('\"', startHref);
- if (currentPos == -1) {
- return null;
- }
- return tag.substring(startHref, currentPos);
- }
- else if (currentPos < tag.length() && tag.charAt(currentPos) == '\'') {
- final int startHref = currentPos + 1;
- currentPos = tag.indexOf('\'', startHref);
- if (currentPos == -1) {
- return null;
- }
- return tag.substring(startHref, currentPos);
- }
- else {
- final int startHref = currentPos;
-
- if (currentPos < tag.length()) {
- do {
- currentPos++;
- } while (currentPos < tag.length() && tag.charAt(currentPos) != ' ' && tag.charAt(currentPos) != '>');
- }
- return tag.substring(startHref, currentPos);
- }
- }
-
- /**
- * Given the position in a string returns whether it points to the equal
- * sign of an href attribute.
- *
- * @param tag html code of the tag
- * @param equalPos the index of the assignment
- * @return true if to left of assignment is href
- */
- private boolean isHrefAttributeAssignment(final String tag, final int equalPos) {
- return tag.charAt(equalPos) == '=' && equalPos > 4 && " href".equals(tag.substring(equalPos - 5, equalPos));
- }
-}
\ No newline at end of file
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
deleted file mode 100644
index 1eca3d1..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/nodes/PImage.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo.nodes;
-
-import java.awt.Graphics2D;
-import java.awt.GraphicsConfiguration;
-import java.awt.GraphicsEnvironment;
-import java.awt.Image;
-import java.awt.MediaTracker;
-import java.awt.Toolkit;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-import javax.imageio.ImageIO;
-import javax.swing.ImageIcon;
-
-import edu.umd.cs.piccolo.PNode;
-import edu.umd.cs.piccolo.util.PBounds;
-import edu.umd.cs.piccolo.util.PPaintContext;
-
-/**
- * PImage is a wrapper around a java.awt.Image. If this node is copied or
- * serialized that image will be converted into a BufferedImage if it is not
- * already one.
- *
- *
- * @version 1.0
- * @author Jesse Grosjean
- */
-public class PImage extends PNode {
-
- /**
- * Allows for future serialization code to understand versioned binary
- * formats.
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * The property name 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 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() {
- }
-
- /**
- * 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 is null
, 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) {
- if (url != null) {
- setImage(Toolkit.getDefaultToolkit().getImage(url));
- }
- }
-
- /**
- * Returns the image that is shown by this node, or null if none.
- *
- * @return java.awt.Image being wrapped by this node
- */
- public Image getImage() {
- return image;
- }
-
- /**
- * 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));
- }
-
- /**
- * 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;
-
- if (newImage == null || newImage instanceof BufferedImage) {
- image = newImage;
- }
- else {
- image = getLoadedImage(newImage);
- }
-
- if (image != null) {
- setBounds(0, 0, getImage().getWidth(null), getImage().getHeight(null));
- invalidatePaint();
- }
-
- firePropertyChange(PROPERTY_CODE_IMAGE, PROPERTY_IMAGE, oldImage, image);
- }
-
- /**
- * Ensures the image is loaded enough (loading is fine).
- *
- * @param newImage to check
- * @return image or null if not loaded enough.
- */
- private Image getLoadedImage(final Image newImage) {
- final ImageIcon imageLoader = new ImageIcon(newImage);
- switch (imageLoader.getImageLoadStatus()) {
- case MediaTracker.LOADING:
- case MediaTracker.COMPLETE:
- return imageLoader.getImage();
- default:
- return null;
- }
- }
-
- /**
- * 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;
- }
-
- final double iw = image.getWidth(null);
- final double ih = image.getHeight(null);
-
- final PBounds b = getBoundsReference();
- final Graphics2D g2 = paintContext.getGraphics();
-
- if (b.x != 0 || b.y != 0 || b.width != iw || b.height != ih) {
- g2.translate(b.x, b.y);
- g2.scale(b.width / iw, b.height / ih);
- g2.drawImage(image, 0, 0, null);
- g2.scale(iw / b.width, ih / b.height);
- g2.translate(-b.x, -b.y);
- }
- else {
- g2.drawImage(image, 0, 0, null);
- }
-
- }
-
- /**
- * 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();
- final BufferedImage bufferedImage = toBufferedImage(image, false);
- if (bufferedImage != null) {
- ImageIO.write(bufferedImage, "png", out);
- }
- }
-
- /**
- * 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);
- }
-
- /**
- * 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) {
- return null;
- }
-
- if (!alwaysCreateCopy && image instanceof BufferedImage) {
- return (BufferedImage) image;
- }
-
- BufferedImage result;
-
- if (GraphicsEnvironment.isHeadless()) {
- result = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
- }
- else {
- final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment()
- .getDefaultScreenDevice().getDefaultConfiguration();
- result = graphicsConfiguration.createCompatibleImage(image.getWidth(null), image.getHeight(null));
- }
-
- final Graphics2D g2 = result.createGraphics();
- g2.drawImage(image, 0, 0, null);
- g2.dispose();
- return result;
- }
-}
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
deleted file mode 100644
index 7a69e41..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/nodes/PPath.java
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo.nodes;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RoundRectangle2D;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-import edu.umd.cs.piccolo.PNode;
-import edu.umd.cs.piccolo.util.PAffineTransform;
-import edu.umd.cs.piccolo.util.PPaintContext;
-import edu.umd.cs.piccolo.util.PUtil;
-
-/**
- * PPath is a wrapper around a java.awt.geom.GeneralPath. The setBounds
- * method works by scaling the path to fit into the specified bounds. This
- * normally works well, but if the specified 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.
- *
- * One option that applications have is to call startResizeBounds
- * before starting an interaction that may make the bounds very small, and
- * calling endResizeBounds
when this interaction is finished. When
- * this is done PPath will use a copy of the original path to do the resizing so
- * the numbers in the path wont loose resolution.
- *
- * This class also provides methods for constructing common shapes using a - * general path. - *
- *
- * @version 1.0
- * @author Jesse Grosjean
- */
-public class PPath extends PNode {
-
- /**
- * Allows for future serialization code to understand versioned binary
- * formats.
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * The property name that identifies a change of this node's stroke paint
- * (see {@link #getStrokePaint getStrokePaint}). Both old and new value will
- * be set correctly to Paint objects in any property change event.
- */
- public static final String PROPERTY_STROKE_PAINT = "strokePaint";
-
- /**
- * The property code that identifies a change of this node's stroke paint
- * (see {@link #getStrokePaint getStrokePaint}). Both old and new value will
- * be set correctly to Paint objects in any property change event.
- */
- public static final int PROPERTY_CODE_STROKE_PAINT = 1 << 16;
-
- /**
- * The property name that identifies a change of this node's stroke (see
- * {@link #getStroke getStroke}). Both old and new value will be set
- * correctly to Stroke objects in any property change event.
- */
- public static final String PROPERTY_STROKE = "stroke";
-
- /**
- * The property code that identifies a change of this node's stroke (see
- * {@link #getStroke getStroke}). Both old and new value will be set
- * correctly to Stroke objects in any property change event.
- */
- public static final int PROPERTY_CODE_STROKE = 1 << 17;
-
- /**
- * The property name that identifies a change of this node's path (see
- * {@link #getPathReference getPathReference}). In any property change event
- * the new value will be a reference to this node's path, but old value will
- * always be null.
- */
- public static final String PROPERTY_PATH = "path";
-
- /**
- * The property code that identifies a change of this node's path (see
- * {@link #getPathReference getPathReference}). In any property change event
- * the new value will be a reference to this node's path, but old value will
- * always be null.
- */
- public static final int PROPERTY_CODE_PATH = 1 << 18;
-
- private static final Rectangle2D.Float TEMP_RECTANGLE = new Rectangle2D.Float();
- private static final RoundRectangle2D.Float TEMP_ROUNDRECTANGLE = new RoundRectangle2D.Float();
- private static final Ellipse2D.Float TEMP_ELLIPSE = new Ellipse2D.Float();
- private static final PAffineTransform TEMP_TRANSFORM = new PAffineTransform();
- private static final BasicStroke DEFAULT_STROKE = new BasicStroke(1.0f);
- private static final Color DEFAULT_STROKE_PAINT = Color.black;
-
- private transient GeneralPath path;
- private transient GeneralPath resizePath;
- private transient Stroke stroke;
- private transient boolean updatingBoundsFromPath;
- private Paint strokePaint;
-
- /**
- * Creates a PPath object in the shape of a rectangle.
- *
- * @param x left of the rectangle
- * @param y top of the rectangle
- * @param width width of the rectangle
- * @param height height of the rectangle
- *
- * @return created rectangle
- */
- public static PPath createRectangle(final float x, final float y, final float width, final float height) {
- TEMP_RECTANGLE.setFrame(x, y, width, height);
- final PPath result = new PPath(TEMP_RECTANGLE);
- result.setPaint(Color.white);
- return result;
- }
-
- /**
- * Creates a PPath object in the shape of a rounded rectangle.
- *
- * @param x left of the rectangle
- * @param y top of the rectangle
- * @param width width of the rectangle
- * @param height height of the rectangle
- * @param arcWidth the arc width at the corners of the rectangle
- * @param arcHeight the arc height at the corners of the rectangle
- *
- * @return created rounded rectangle
- */
- public static PPath createRoundRectangle(final float x, final float y, final float width, final float height,
- final float arcWidth, final float arcHeight) {
- TEMP_ROUNDRECTANGLE.setRoundRect(x, y, width, height, arcWidth, arcHeight);
- final PPath result = new PPath(TEMP_ROUNDRECTANGLE);
- result.setPaint(Color.white);
- return result;
- }
-
- /**
- * Creates a PPath object in the shape of an ellipse.
- *
- * @param x left of the ellipse
- * @param y top of the ellipse
- * @param width width of the ellipse
- * @param height height of the ellipse
- *
- * @return created ellipse
- */
- public static PPath createEllipse(final float x, final float y, final float width, final float height) {
- TEMP_ELLIPSE.setFrame(x, y, width, height);
- final PPath result = new PPath(TEMP_ELLIPSE);
- result.setPaint(Color.white);
- return result;
- }
-
- /**
- * Creates a PPath in the shape of a line.
- *
- * @param x1 x component of the first point
- * @param y1 y component of the first point
- * @param x2 x component of the second point
- * @param y2 y component of the second point
- *
- * @return created line
- */
- public static PPath createLine(final float x1, final float y1, final float x2, final float y2) {
- final PPath result = new PPath();
- result.moveTo(x1, y1);
- result.lineTo(x2, y2);
- result.setPaint(Color.white);
- return result;
- }
-
- /**
- * Creates a PPath for the poly-line for the given points.
- *
- * @param points array of points for the point lines
- *
- * @return created poly-line for the given points
- */
- public static PPath createPolyline(final Point2D[] points) {
- final PPath result = new PPath();
- result.setPathToPolyline(points);
- result.setPaint(Color.white);
- return result;
- }
-
- /**
- * Creates a PPath for the poly-line for the given points.
- *
- * @param xp array of x components of the points of the poly-lines
- * @param yp array of y components of the points of the poly-lines
- *
- * @return created poly-line for the given points
- */
- public static PPath createPolyline(final float[] xp, final float[] yp) {
- final PPath result = new PPath();
- result.setPathToPolyline(xp, yp);
- result.setPaint(Color.white);
- return result;
- }
-
- /**
- * Creates an empty PPath with the default paint and stroke.
- */
- public PPath() {
- strokePaint = DEFAULT_STROKE_PAINT;
- stroke = DEFAULT_STROKE;
- path = new GeneralPath();
- }
-
- /**
- * 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);
- }
-
- /**
- * Construct this path with the given shape and stroke. This method may be
- * used to optimize the creation of a large number of PPaths. Normally
- * PPaths have a default stroke of width one, but when a path has a non null
- * stroke it takes significantly longer to compute its bounds. This method
- * allows you to override that default stroke before the bounds are ever
- * calculated, so if you pass in a null stroke here you won't ever have to
- * pay that bounds calculation price if you don't need to.
- *
- * @param aShape desired shape or null if you desire an empty path
- * @param aStroke desired stroke
- */
- public PPath(final Shape aShape, final Stroke aStroke) {
- this();
- stroke = aStroke;
- if (aShape != null) {
- append(aShape, false);
- }
- }
-
- /**
- * Returns the stroke paint of the PPath.
- *
- * @return stroke paint of the PPath
- */
- public Paint getStrokePaint() {
- return strokePaint;
- }
-
- /**
- * 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, 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;
- updateBoundsFromPath();
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_STROKE, PROPERTY_STROKE, old, stroke);
- }
-
- /** 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;
- }
-
- /**
- * Set the bounds of this path. This method works by scaling the path to fit
- * into the specified bounds. This normally works well, but if the specified
- * 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(final double x, final double y, final double width, final double height) {
- if (updatingBoundsFromPath || path == null) {
- return;
- }
-
- if (resizePath != null) {
- path.reset();
- path.append(resizePath, false);
- }
-
- final Rectangle2D pathBounds = path.getBounds2D();
- final Rectangle2D pathStrokeBounds = getPathBoundsWithStroke();
- final double strokeOutset = Math.max(pathStrokeBounds.getWidth() - pathBounds.getWidth(), pathStrokeBounds
- .getHeight()
- - pathBounds.getHeight());
-
- double adjustedX = x + strokeOutset / 2;
- double adjustedY = y + strokeOutset / 2;
- double adjustedWidth = width - strokeOutset;
- double adjustedHeight = height - strokeOutset;
-
- 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(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)) {
- return true;
- }
- else if (stroke != null && strokePaint != null) {
- return stroke.createStrokedShape(path).intersects(aBounds);
- }
- }
- 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();
- }
- else {
- return path.getBounds2D();
- }
- }
-
- /**
- * Recomputes the bounds taking stroke into account.
- */
- public void updateBoundsFromPath() {
- updatingBoundsFromPath = true;
- if (path == null) {
- resetBounds();
- }
- else {
- final Rectangle2D b = getPathBoundsWithStroke();
- setBounds(b.getX(), b.getY(), b.getWidth(), b.getHeight());
- }
- updatingBoundsFromPath = false;
- }
-
- /**
- * 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();
-
- if (p != null) {
- g2.setPaint(p);
- g2.fill(path);
- }
-
- if (stroke != null && strokePaint != null) {
- g2.setPaint(strokePaint);
- g2.setStroke(stroke);
- g2.draw(path);
- }
- }
-
- /**
- * 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);
- updateBoundsFromPath();
- 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);
- updateBoundsFromPath();
- invalidatePaint();
- }
-
- /**
- * Adds a curved segment, defined by two new points, to the path by drawing
- * a Quadratic curve that intersects both the current coordinates and the
- * coordinates (x2, y2), using the specified point (x1, y1) as a quadratic
- * parametric control point.
- *
- * @param x1 x component of quadratic parametric control point
- * @param y1 y component of quadratic parametric control point
- * @param x2 x component of point through which quad curve will pass
- * @param y2 y component of point through which quad curve will pass
- */
- 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);
- updateBoundsFromPath();
- invalidatePaint();
- }
-
- /**
- * Adds a curved segment, defined by three new points, to the path by
- * drawing a Bézier curve that intersects both the current coordinates and
- * the coordinates (x3, y3), using the specified points (x1, y1) and (x2,
- * y2) as Bézier control points.
- *
- * @param x1 x component of first Bézier control point
- * @param y1 y component of first Bézier control point
- * @param x2 x component of second Bézier control point
- * @param y2 y component of second Bézier control point
- * @param x3 x component of point through which curve must pass
- * @param y3 y component of point through which curve must pass
- */
- 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);
- updateBoundsFromPath();
- 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);
- updateBoundsFromPath();
- 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());
- for (int i = 1; i < points.length; i++) {
- path.lineTo((float) points[i].getX(), (float) points[i].getY());
- }
- firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
- updateBoundsFromPath();
- 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]);
- for (int i = 1; i < xp.length; i++) {
- path.lineTo(xp[i], yp[i]);
- }
- firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
- updateBoundsFromPath();
- invalidatePaint();
- }
-
- /**
- * Marks the path as closed. Making changes to it impossible.
- */
- public void closePath() {
- path.closePath();
- firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
- updateBoundsFromPath();
- invalidatePaint();
- }
-
- /**
- * Empties the path.
- */
- public void reset() {
- path.reset();
- firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
- updateBoundsFromPath();
- invalidatePaint();
- }
-
- /**
- * 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);
- path = PUtil.readPath(in);
- }
-}
diff --git a/core/src/main/java/edu/umd/cs/piccolo/nodes/PText.java b/core/src/main/java/edu/umd/cs/piccolo/nodes/PText.java
deleted file mode 100644
index 30c8098..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/nodes/PText.java
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo.nodes;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.font.LineBreakMeasurer;
-import java.awt.font.TextAttribute;
-import java.awt.font.TextLayout;
-import java.text.AttributedCharacterIterator;
-import java.text.AttributedString;
-import java.util.ArrayList;
-
-import edu.umd.cs.piccolo.PNode;
-import edu.umd.cs.piccolo.util.PPaintContext;
-
-/**
- * PText is a multi-line text node. The text will flow to base on the
- * width of the node's bounds.
- *
- * @version 1.1
- * @author Jesse Grosjean
- */
-public class PText extends PNode {
-
- /**
- * Allows for future serialization code to understand versioned binary
- * formats.
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * The property name that identifies a change of this node's text (see
- * {@link #getText getText}). Both old and new value will be set in any
- * property change event.
- */
- public static final String PROPERTY_TEXT = "text";
-
- /**
- * The property code that identifies a change of this node's text (see
- * {@link #getText getText}). Both old and new value will be set in any
- * property change event.
- */
- public static final int PROPERTY_CODE_TEXT = 1 << 19;
-
- /**
- * The property name that identifies a change of this node's font (see
- * {@link #getFont getFont}). Both old and new value will be set in any
- * property change event.
- */
- public static final String PROPERTY_FONT = "font";
-
- /**
- * The property code that identifies a change of this node's font (see
- * {@link #getFont getFont}). Both old and new value will be set in any
- * property change event.
- */
- public static final int PROPERTY_CODE_FONT = 1 << 20;
-
- /**
- * The property name that identifies a change of this node's text paint (see
- * {@link #getTextPaint getTextPaint}). Both old and new value will be set
- * in any property change event.
- *
- * @since 1.3
- */
- public static final String PROPERTY_TEXT_PAINT = "text paint";
-
- /**
- * The property code that identifies a change of this node's text paint (see
- * {@link #getTextPaint getTextPaint}). Both old and new value will be set
- * in any property change event.
- *
- * @since 1.3
- */
- public static final int PROPERTY_CODE_TEXT_PAINT = 1 << 21;
-
- /**
- * Default font, 12 point "SansSerif"
. Will be made final in
- * version 2.0.
- */
- // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF,
- // Font.PLAIN, 12); jdk 1.6+
- public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 12);
-
- /**
- * Default greek threshold, 5.5d
. Will be made final in version
- * 2.0.
- */
- public static final double DEFAULT_GREEK_THRESHOLD = 5.5d;
-
- /**
- * Default horizontal alignment, Component.LEFT_ALIGNMENT
.
- *
- * @since 1.3
- */
- public static final float DEFAULT_HORIZONTAL_ALIGNMENT = Component.LEFT_ALIGNMENT;
-
- /**
- * Default text, ""
.
- *
- * @since 1.3
- */
- public static final String DEFAULT_TEXT = "";
-
- /**
- * Default text paint, Color.BLACK
.
- *
- * @since 1.3
- */
- public static final Paint DEFAULT_TEXT_PAINT = Color.BLACK;
-
- /** Empty text layout array. */
- private static final TextLayout[] EMPTY_TEXT_LAYOUT_ARRAY = new TextLayout[0];
-
- /** Text for this text node. */
- private String text = DEFAULT_TEXT;
-
- /** Text paint for this text node. */
- private Paint textPaint = DEFAULT_TEXT_PAINT;
-
- /** Font for this text node. */
- private Font font = DEFAULT_FONT;
-
- /**
- * Greek threshold in screen font size for this text node. Will be made
- * private in version 2.0.
- */
- protected double greekThreshold = DEFAULT_GREEK_THRESHOLD;
-
- /** Horizontal alignment for this text node. */
- private float horizontalAlignment = DEFAULT_HORIZONTAL_ALIGNMENT;
-
- /**
- * True if this text node should constrain its height to the height of its
- * text.
- */
- private boolean constrainHeightToTextHeight = true;
-
- /**
- * True if this text node should constrain its height to the height of its
- * text.
- */
- private boolean constrainWidthToTextWidth = true;
-
- /** One or more lines of text layout. */
- private transient TextLayout[] lines;
-
- /**
- * Create a new text node with no text (""
).
- */
- public PText() {
- super();
- setText(DEFAULT_TEXT);
- }
-
- /**
- * Create a new text node with the specified text.
- *
- * @param text text for this text node
- */
- public PText(final String text) {
- this();
- setText(text);
- }
-
- /**
- * Return the horizontal alignment for this text node. The horizontal
- * alignment will be one of Component.LEFT_ALIGNMENT
,
- * Component.CENTER_ALIGNMENT
, or
- * Component.RIGHT_ALIGNMENT
. Defaults to
- * {@link #DEFAULT_HORIZONTAL_ALIGNMENT}.
- *
- * @since 1.3
- * @return the horizontal alignment for this text node
- */
- public float getHorizontalAlignment() {
- return horizontalAlignment;
- }
-
- /**
- * Set the horizontal alignment for this text node to
- * horizontalAlignment
.
- *
- * @since 1.3
- * @param horizontalAlignment horizontal alignment, must be one of
- * Component.LEFT_ALIGNMENT
,
- * Component.CENTER_ALIGNMENT
, or
- * Component.RIGHT_ALIGNMENT
- */
- public void setHorizontalAlignment(final float horizontalAlignment) {
- if (!validHorizontalAlignment(horizontalAlignment)) {
- throw new IllegalArgumentException("horizontalAlignment must be one of Component.LEFT_ALIGNMENT, "
- + "Component.CENTER_ALIGNMENT, or Component.RIGHT_ALIGNMENT");
- }
- this.horizontalAlignment = horizontalAlignment;
- }
-
- /**
- * Return true if the specified horizontal alignment is one of
- * Component.LEFT_ALIGNMENT
,
- * Component.CENTER_ALIGNMENT
, or
- * Component.RIGHT_ALIGNMENT
.
- *
- * @param horizontalAlignment horizontal alignment
- * @return true if the specified horizontal alignment is one of
- * Component.LEFT_ALIGNMENT
,
- * Component.CENTER_ALIGNMENT
, or
- * Component.RIGHT_ALIGNMENT
- */
- private static boolean validHorizontalAlignment(final float horizontalAlignment) {
- return Component.LEFT_ALIGNMENT == horizontalAlignment || Component.CENTER_ALIGNMENT == horizontalAlignment
- || Component.RIGHT_ALIGNMENT == horizontalAlignment;
- }
-
- /**
- * Return the paint used to paint this node's text.
- *
- * @return the paint used to paint this node's text
- */
- public Paint getTextPaint() {
- return textPaint;
- }
-
- /**
- * Set the paint used to paint this node's text to textPaint
.
- *
- *
- * This is a bound property. - *
- * - * @param textPaint text paint - */ - public void setTextPaint(final Paint textPaint) { - if (textPaint == this.textPaint) { - return; - } - final Paint oldTextPaint = this.textPaint; - this.textPaint = textPaint; - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_TEXT_PAINT, PROPERTY_TEXT_PAINT, oldTextPaint, this.textPaint); - } - - /** - * Return true if this text node should constrain its width to the width of - * its text. Defaults totrue
.
- *
- * @return true if this text node should constrain its width to the width of
- * its text
- */
- public boolean isConstrainWidthToTextWidth() {
- return constrainWidthToTextWidth;
- }
-
- /**
- * Set to true
if this text node should constrain its width to
- * the width of its text.
- *
- * @param constrainWidthToTextWidth true if this text node should constrain
- * its width to the width of its text
- */
- public void setConstrainWidthToTextWidth(final boolean constrainWidthToTextWidth) {
- this.constrainWidthToTextWidth = constrainWidthToTextWidth;
- recomputeLayout();
- }
-
- /**
- * Return true if this text node should constrain its height to the height
- * of its text. Defaults to true
.
- *
- * @return true if this text node should constrain its height to the height
- * of its text
- */
- public boolean isConstrainHeightToTextHeight() {
- return constrainHeightToTextHeight;
- }
-
- /**
- * Set to true
if this text node should constrain its height to
- * the height of its text.
- *
- * @param constrainHeightToTextHeight true if this text node should
- * constrain its height to the width of its text
- */
- public void setConstrainHeightToTextHeight(final boolean constrainHeightToTextHeight) {
- this.constrainHeightToTextHeight = constrainHeightToTextHeight;
- recomputeLayout();
- }
-
- /**
- * Return the greek threshold in screen font size. When the screen font size
- * will be below this threshold the text is rendered as 'greek' instead of
- * drawing the text glyphs. Defaults to {@link #DEFAULT_GREEK_THRESHOLD}.
- *
- * @see PText#paintGreek(PPaintContext)
- * @return the current greek threshold in screen font size
- */
- public double getGreekThreshold() {
- return greekThreshold;
- }
-
- /**
- * Set the greek threshold in screen font size to
- * greekThreshold
. When the screen font size will be below this
- * threshold the text is rendered as 'greek' instead of drawing the text
- * glyphs.
- *
- * @see PText#paintGreek(PPaintContext)
- * @param greekThreshold greek threshold in screen font size
- */
- public void setGreekThreshold(final double greekThreshold) {
- this.greekThreshold = greekThreshold;
- invalidatePaint();
- }
-
- /**
- * Return the text for this text node. Defaults to {@link #DEFAULT_TEXT}.
- *
- * @return the text for this text node
- */
- public String getText() {
- return text;
- }
-
- /**
- * Set the text for this node to text
. The text will be broken
- * up into multiple lines based on the size of the text and the bounds width
- * of this node.
- *
- * - * This is a bound property. - *
- * - * @param newText text for this text node - */ - public void setText(final String newText) { - if (newText == null && text == null || newText != null && newText.equals(text)) { - return; - } - - final String oldText = text; - if (newText == null) { - text = DEFAULT_TEXT; - } - else { - text = newText; - } - lines = null; - recomputeLayout(); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, oldText, text); - } - - /** - * Return the font for this text node. Defaults to {@link #DEFAULT_FONT}. - * - * @return the font for this text node - */ - public Font getFont() { - return font; - } - - /** - * Set the font for this text node tofont
. Note that in
- * Piccolo if you want to change the size of a text object it's often a
- * better idea to scale the PText node instead of changing the font size to
- * get that same effect. Using very large font sizes can slow performance.
- *
- * - * This is a bound property. - *
- * - * @param font font for this text node - */ - public void setFont(final Font font) { - if (font == this.font) { - return; - } - final Font oldFont = this.font; - if (font == null) { - this.font = DEFAULT_FONT; - } - else { - this.font = font; - } - - lines = null; - recomputeLayout(); - invalidatePaint(); - firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, oldFont, this.font); - } - - /** - * Compute the bounds of the text wrapped by this node. The text layout is - * wrapped based on the bounds of this node. - */ - public void recomputeLayout() { - final ArrayList linesList = new ArrayList(); - double textWidth = 0; - double textHeight = 0; - - if (text != null && text.length() > 0) { - final AttributedString atString = new AttributedString(text); - atString.addAttribute(TextAttribute.FONT, getFont()); - final AttributedCharacterIterator itr = atString.getIterator(); - final LineBreakMeasurer measurer = new LineBreakMeasurer(itr, PPaintContext.RENDER_QUALITY_HIGH_FRC); - final float availableWidth; - if (constrainWidthToTextWidth) { - availableWidth = Float.MAX_VALUE; - } - else { - availableWidth = (float) getWidth(); - } - - int nextLineBreakOffset = text.indexOf('\n'); - if (nextLineBreakOffset == -1) { - nextLineBreakOffset = Integer.MAX_VALUE; - } - else { - nextLineBreakOffset++; - } - - while (measurer.getPosition() < itr.getEndIndex()) { - final TextLayout aTextLayout = computeNextLayout(measurer, availableWidth, nextLineBreakOffset); - - if (nextLineBreakOffset == measurer.getPosition()) { - nextLineBreakOffset = text.indexOf('\n', measurer.getPosition()); - if (nextLineBreakOffset == -1) { - nextLineBreakOffset = Integer.MAX_VALUE; - } - else { - nextLineBreakOffset++; - } - } - - linesList.add(aTextLayout); - textHeight += aTextLayout.getAscent(); - textHeight += aTextLayout.getDescent() + aTextLayout.getLeading(); - textWidth = Math.max(textWidth, aTextLayout.getAdvance()); - } - } - - lines = (TextLayout[]) linesList.toArray(EMPTY_TEXT_LAYOUT_ARRAY); - - if (constrainWidthToTextWidth || constrainHeightToTextHeight) { - double newWidth = getWidth(); - double newHeight = getHeight(); - - if (constrainWidthToTextWidth) { - newWidth = textWidth; - } - - if (constrainHeightToTextHeight) { - newHeight = textHeight; - } - - super.setBounds(getX(), getY(), newWidth, newHeight); - } - } - - /** - * Compute the next layout using the specified line break measurer, - * available width, and next line break offset. - * - * @param lineBreakMeasurer line break measurer - * @param availableWidth available width - * @param nextLineBreakOffset next line break offset - * @return the next layout computed using the specified line break measurer, - * available width, and next line break offset - */ - protected TextLayout computeNextLayout(final LineBreakMeasurer lineBreakMeasurer, final float availableWidth, - final int nextLineBreakOffset) { - return lineBreakMeasurer.nextLayout(availableWidth, nextLineBreakOffset, false); - } - - /** - * Paint greek with the specified paint context. - * - * @since 1.3 - * @param paintContext paint context - */ - protected void paintGreek(final PPaintContext paintContext) { - // empty - } - - /** - * Paint text with the specified paint context. - * - * @since 1.3 - * @param paintContext paint context - */ - protected void paintText(final PPaintContext paintContext) { - final float x = (float) getX(); - float y = (float) getY(); - final float bottomY = (float) getHeight() + y; - - final Graphics2D g2 = paintContext.getGraphics(); - - if (lines == null) { - recomputeLayout(); - repaint(); - return; - } - - g2.setPaint(textPaint); - - for (int i = 0; i < lines.length; i++) { - final TextLayout tl = lines[i]; - y += tl.getAscent(); - - if (bottomY < y) { - return; - } - - final float offset = (float) (getWidth() - tl.getAdvance()) * horizontalAlignment; - - tl.draw(g2, x + offset, y); - - y += tl.getDescent() + tl.getLeading(); - } - } - - /** {@inheritDoc} */ - protected void paint(final PPaintContext paintContext) { - super.paint(paintContext); - if (textPaint == null) { - return; - } - final float screenFontSize = getFont().getSize() * (float) paintContext.getScale(); - if (screenFontSize <= greekThreshold) { - paintGreek(paintContext); - } - paintText(paintContext); - } - - /** {@inheritDoc} */ - protected void internalUpdateBounds(final double x, final double y, final double width, final double height) { - recomputeLayout(); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/nodes/package.html b/core/src/main/java/edu/umd/cs/piccolo/nodes/package.html deleted file mode 100644 index 1039fc0..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/nodes/package.html +++ /dev/null @@ -1,5 +0,0 @@ - -This package contains nodes that may be useful for Piccolo applications. These -nodes are intended to be useful, but not definitive. Many applications will also -end up defining their nodes which can be used along with these. - diff --git a/core/src/main/java/edu/umd/cs/piccolo/package.html b/core/src/main/java/edu/umd/cs/piccolo/package.html deleted file mode 100644 index be1fa0e..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/package.html +++ /dev/null @@ -1,18 +0,0 @@ - -Piccolo is a general-purpose Java-based engine that supports 2D visualizations. -A primary characteristic of Piccolo is that it is designed to support zoomable -information spaces, although any particular applications may or may not -take advantage of this feature. Piccolo is implemented entirely in Java 2 (1.4), -and as such runs identically on any platform that supports Java 2.- -Piccolo is not an application in itself, but rather it is an engine that is designed -to support applications that require the ability to create, manipulate, and render -object-oriented graphics. If you are familiar with the terminology of 3D graphics, -Piccolo supports a scenegraph. This is a data structure that represents a hierarchy -of graphical objects. Piccolo uses a tuned run-time system to render the scenegraph -as quickly as possible to support interactive applications.
-
-This is the root package for all Jazz classes. It contains the core scenegraph
-classes itself, and in addition, contains the activities
, event
,
-nodes
and util
packages that are used to build Jazz applications.
-
diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PAffineTransform.java b/core/src/main/java/edu/umd/cs/piccolo/util/PAffineTransform.java
deleted file mode 100644
index afc2a9c..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/util/PAffineTransform.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo.util;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Dimension2D;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-
-/**
- * PAffineTransform is a subclass of AffineTransform that has been
- * extended with convenience methods.
- *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PAffineTransform extends AffineTransform { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** Used internally to speed up computation. */ - private static final double[] PTS1 = new double[8]; - - /** Used internally to speed up computation. */ - private static final double[] PTS2 = new double[8]; - - /** - * Constructs a new AffineTransform representing the Identity - * transformation. - */ - public PAffineTransform() { - super(); - } - - /** - * Constructs a new AffineTransform from an array of double precision values - * representing either the 4 non-translation entries or the 6 specifiable - * entries of the 3x3 transformation matrix. The values are retrieved from - * the array as { m00 m10 m01 m11 [m02 m12]}. - * - * @param flatmatrix the double array containing the values to be set in the - * new AffineTransform object. The length of the array is assumed - * to be at least 4. If the length of the array is less than 6, - * only the first 4 values are taken. If the length of the array - * is greater than 6, the first 6 values are taken. - */ - public PAffineTransform(final double[] flatmatrix) { - super(flatmatrix); - } - - /** - * Constructs a new AffineTransform from an array of floating point values - * representing either the 4 non-translation entries or the 6 specifiable - * entries of the 3x3 transformation matrix. The values are retrieved from - * the array as { m00 m10 m01 m11 [m02 m12]}. - * - * @param flatmatrix the float array containing the values to be set in the - * new AffineTransform object. The length of the array is assumed - * to be at least 4. If the length of the array is less than 6, - * only the first 4 values are taken. If the length of the array - * is greater than 6, the first 6 values are taken. - */ - public PAffineTransform(final float[] flatmatrix) { - super(flatmatrix); - } - - /** - * Constructs a new AffineTransform from 6 double precision values - * representing the 6 specifiable entries of the 3x3 transformation matrix. - * - * @param m00 the X coordinate scaling element of the 3x3 matrix - * @param m10 the Y coordinate shearing element of the 3x3 matrix - * @param m01 the X coordinate shearing element of the 3x3 matrix - * @param m11 the Y coordinate scaling element of the 3x3 matrix - * @param m02 the X coordinate translation element of the 3x3 matrix - * @param m12 the Y coordinate translation element of the 3x3 matrix - */ - public PAffineTransform(final double m00, final double m10, final double m01, final double m11, final double m02, - final double m12) { - super(m00, m10, m01, m11, m02, m12); - } - - /** - * Constructs a new AffineTransform from 6 floating point values - * representing the 6 specifiable entries of the 3x3 transformation matrix. - * - * @param m00 the X coordinate scaling element of the 3x3 matrix - * @param m10 the Y coordinate shearing element of the 3x3 matrix - * @param m01 the X coordinate shearing element of the 3x3 matrix - * @param m11 the Y coordinate scaling element of the 3x3 matrix - * @param m02 the X coordinate translation element of the 3x3 matrix - * @param m12 the Y coordinate translation element of the 3x3 matrix - */ - public PAffineTransform(final float m00, final float m10, final float m01, final float m11, final float m02, - final float m12) { - super(m00, m10, m01, m11, m02, m12); - } - - /** - * Constructs a new AffineTransform that is a copy of the specified - * AffineTransform object. - * - * @param tx transform to copy - */ - public PAffineTransform(final AffineTransform tx) { - super(tx); - } - - /** - * Scales the transform about the given point by the given scale. - * - * @param scale to transform the transform by - * @param x x coordinate around which the scale should take place - * @param y y coordinate around which the scale should take place - */ - public void scaleAboutPoint(final double scale, final double x, final double y) { - translate(x, y); - scale(scale, scale); - translate(-x, -y); - } - - /** - * Returns the scale applied to this transform. Note that it does so by - * computing the change in length of a unit segment after being passed - * through the transform. This means that a transform that a transform that - * doesn't scale the in the x but doubles the y will be reported as 2. - * - * @return the different in length of a unit segment after being - * transformed. - */ - public double getScale() { - PTS1[0] = 0; // x1 - PTS1[1] = 0; // y1 - PTS1[2] = 1; // x2 - PTS1[3] = 0; // y2 - transform(PTS1, 0, PTS2, 0, 2); - return Point2D.distance(PTS2[0], PTS2[1], PTS2[2], PTS2[3]); - } - - /** - * Sets the scale about to the origin of this transform to the scale - * provided. - * - * @param scale The desired resulting scale - */ - public void setScale(final double scale) { - if (scale == 0) { - throw new PAffineTransformException("Can't set scale to 0", this); - } - - scaleAboutPoint(scale / getScale(), 0, 0); - } - - /** - * Applies modifies the transform so that it translates by the given offset. - * - * @param tx x translation of resulting transform - * @param ty y translation of resulting transform - */ - public void setOffset(final double tx, final double ty) { - setTransform(getScaleX(), getShearY(), getShearX(), getScaleY(), tx, ty); - } - - /** - * Returns the rotation applied to this affine transform in radians. The - * value returned will be between 0 and 2pi. - * - * @return rotation in radians - */ - public double getRotation() { - PTS1[0] = 0; // x1 - PTS1[1] = 0; // y1 - PTS1[2] = 1; // x2 - PTS1[3] = 0; // y2 - - transform(PTS1, 0, PTS2, 0, 2); - - final double dy = Math.abs(PTS2[3] - PTS2[1]); - final double l = Point2D.distance(PTS2[0], PTS2[1], PTS2[2], PTS2[3]); - double rotation = Math.asin(dy / l); - - // correct for quadrant - if (PTS2[3] - PTS2[1] > 0) { - if (PTS2[2] - PTS2[0] < 0) { - rotation = Math.PI - rotation; - } - } - else if (PTS2[2] - PTS2[0] > 0) { - rotation = 2 * Math.PI - rotation; - } - else { - rotation = rotation + Math.PI; - } - - return rotation; - } - - /** - * Set rotation in radians. This is not cumulative. - * - * @param theta desired rotation in radians. - */ - public void setRotation(final double theta) { - rotate(theta - getRotation()); - } - - /** - * Applies the transform to the provided dimension. - * - * @param dimSrc source dimension - * @param dimDst will be changed to be the transformed dimension, may be - * null - * @return the transformed dimension - */ - public Dimension2D transform(final Dimension2D dimSrc, final Dimension2D dimDst) { - final Dimension2D result; - if (dimDst == null) { - result = (Dimension2D) dimSrc.clone(); - } - else { - result = dimDst; - } - - PTS1[0] = dimSrc.getWidth(); - PTS1[1] = dimSrc.getHeight(); - deltaTransform(PTS1, 0, PTS2, 0, 1); - result.setSize(PTS2[0], PTS2[1]); - return result; - } - - /** - * Applies the inverse of this transform to the source point if possible. - * - * @since 1.3 - * @param ptSrc point to be transformed - * @param ptDst result of transform will be placed in this point - * - * @return the transformed point - */ - public Point2D inverseTransform(final Point2D ptSrc, final Point2D ptDst) { - try { - return super.inverseTransform(ptSrc, ptDst); - } - catch (final NoninvertibleTransformException e) { - throw new PAffineTransformException("Could not invert Transform", e, this); - } - } - - /** - * Applies the inverse of this transform to the source dimension if - * possible. - * - * @param dimSrc dimension to be transformed - * @param dimDst result of transform will be placed in this dimension - * - * @return the transformed dimension - */ - public Dimension2D inverseTransform(final Dimension2D dimSrc, final Dimension2D dimDst) { - final Dimension2D result; - if (dimDst == null) { - result = (Dimension2D) dimSrc.clone(); - } - else { - result = dimDst; - } - - final double width = dimSrc.getWidth(); - final double height = dimSrc.getHeight(); - final double m00 = getScaleX(); - final double m11 = getScaleY(); - final double m01 = getShearX(); - final double m10 = getShearY(); - final double det = m00 * m11 - m01 * m10; - - if (Math.abs(det) > Double.MIN_VALUE) { - result.setSize((width * m11 - height * m01) / det, (height * m00 - width * m10) / det); - } - else { - throw new PAffineTransformException("Could not invert transform", this); - } - - return result; - } - - /** - * Applies this transform to the source rectangle and stores the result in - * rectDst. - * - * @param rectSrc rectangle to be transformed - * @param rectDst result of transform will be placed in this rectangle - * - * @return the transformed rectangle - */ - public Rectangle2D transform(final Rectangle2D rectSrc, final Rectangle2D rectDst) { - final Rectangle2D result; - if (rectDst == null) { - result = (Rectangle2D) rectSrc.clone(); - } - else { - result = rectDst; - } - - if (rectSrc.isEmpty()) { - result.setRect(rectSrc); - if (result instanceof PBounds) { - ((PBounds) result).reset(); - } - return result; - } - - double scale; - - switch (getType()) { - case AffineTransform.TYPE_IDENTITY: - if (rectSrc != result) { - result.setRect(rectSrc); - } - break; - - case AffineTransform.TYPE_TRANSLATION: - result.setRect(rectSrc.getX() + getTranslateX(), rectSrc.getY() + getTranslateY(), rectSrc.getWidth(), - rectSrc.getHeight()); - break; - - case AffineTransform.TYPE_UNIFORM_SCALE: - scale = getScaleX(); - result.setRect(rectSrc.getX() * scale, rectSrc.getY() * scale, rectSrc.getWidth() * scale, rectSrc - .getHeight() - * scale); - break; - - case AffineTransform.TYPE_TRANSLATION | AffineTransform.TYPE_UNIFORM_SCALE: - scale = getScaleX(); - result.setRect(rectSrc.getX() * scale + getTranslateX(), rectSrc.getY() * scale + getTranslateY(), - rectSrc.getWidth() * scale, rectSrc.getHeight() * scale); - break; - - default: - final double[] pts = rectToArray(rectSrc); - transform(pts, 0, pts, 0, 4); - rectFromArray(result, pts); - break; - } - - return result; - } - - /** - * Applies the inverse of this transform to the source rectangle and stores - * the result in rectDst. - * - * @param rectSrc rectangle to be transformed - * @param rectDst result of transform will be placed in this rectangle - * - * @return the transformed rectangle - */ - public Rectangle2D inverseTransform(final Rectangle2D rectSrc, final Rectangle2D rectDst) { - final Rectangle2D result; - if (rectDst == null) { - result = (Rectangle2D) rectSrc.clone(); - } - else { - result = rectDst; - } - - if (rectSrc.isEmpty()) { - result.setRect(rectSrc); - if (result instanceof PBounds) { - ((PBounds) result).reset(); - } - return result; - } - - double scale; - - switch (getType()) { - case AffineTransform.TYPE_IDENTITY: - if (rectSrc != result) { - result.setRect(rectSrc); - } - break; - - case AffineTransform.TYPE_TRANSLATION: - result.setRect(rectSrc.getX() - getTranslateX(), rectSrc.getY() - getTranslateY(), rectSrc.getWidth(), - rectSrc.getHeight()); - break; - - case AffineTransform.TYPE_UNIFORM_SCALE: - scale = getScaleX(); - if (scale == 0) { - throw new PAffineTransformException("Could not invertTransform rectangle", this); - } - - result.setRect(rectSrc.getX() / scale, rectSrc.getY() / scale, rectSrc.getWidth() / scale, rectSrc - .getHeight() - / scale); - break; - - case AffineTransform.TYPE_TRANSLATION | AffineTransform.TYPE_UNIFORM_SCALE: - scale = getScaleX(); - if (scale == 0) { - throw new PAffineTransformException("Could not invertTransform rectangle", this); - } - result.setRect((rectSrc.getX() - getTranslateX()) / scale, (rectSrc.getY() - getTranslateY()) / scale, - rectSrc.getWidth() / scale, rectSrc.getHeight() / scale); - break; - - default: - final double[] pts = rectToArray(rectSrc); - try { - inverseTransform(pts, 0, pts, 0, 4); - } - catch (final NoninvertibleTransformException e) { - throw new PAffineTransformException("Could not invert transform", e, this); - } - rectFromArray(result, pts); - break; - } - - return result; - } - - /** - * Builds an array of coordinates from an source rectangle. - * - * @param aRectangle rectangle from which points coordinates will be - * extracted - * - * @return coordinate array - */ - private static double[] rectToArray(final Rectangle2D aRectangle) { - PTS1[0] = aRectangle.getX(); - PTS1[1] = aRectangle.getY(); - PTS1[2] = PTS1[0] + aRectangle.getWidth(); - PTS1[3] = PTS1[1]; - PTS1[4] = PTS1[0] + aRectangle.getWidth(); - PTS1[5] = PTS1[1] + aRectangle.getHeight(); - PTS1[6] = PTS1[0]; - PTS1[7] = PTS1[1] + aRectangle.getHeight(); - return PTS1; - } - - /** - * Creates a rectangle from an array of coordinates. - * - * @param aRectangle rectangle into which coordinates will be stored - * @param pts coordinate source - */ - private static void rectFromArray(final Rectangle2D aRectangle, final double[] pts) { - double minX = pts[0]; - double minY = pts[1]; - double maxX = pts[0]; - double maxY = pts[1]; - - double x; - double y; - - for (int i = 1; i < 4; i++) { - x = pts[2 * i]; - y = pts[2 * i + 1]; - - if (x < minX) { - minX = x; - } - if (y < minY) { - minY = y; - } - if (x > maxX) { - maxX = x; - } - if (y > maxY) { - maxY = y; - } - } - aRectangle.setRect(minX, minY, maxX - minX, maxY - minY); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PAffineTransformException.java b/core/src/main/java/edu/umd/cs/piccolo/util/PAffineTransformException.java deleted file mode 100644 index 59e82a3..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PAffineTransformException.java +++ /dev/null @@ -1,77 +0,0 @@ -package edu.umd.cs.piccolo.util; - -/** - * This class is used to encapsulate exceptions that may occur while performing transform operations. - * - * @since 1.3 - */ -public class PAffineTransformException extends RuntimeException { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - private final PAffineTransform errantTransform; - - /** - * Constructs an Exception that represents an error with the - * errantTransform. - * - * @param errantTransform transform that caused the error - */ - public PAffineTransformException(final PAffineTransform errantTransform) { - this.errantTransform = errantTransform; - } - - /** - * Constructs an Exception that represents an error with the - * errantTransform. - * - * @param message Text message provided by the programmer about the context - * of the error - * @param errantTransform transform that caused the error - */ - public PAffineTransformException(final String message, final PAffineTransform errantTransform) { - super(message); - this.errantTransform = errantTransform; - } - - /** - * Constructs an Exception that wraps another and records the errant - * transform. - * - * @param throwable the root cause of the exception - * @param errantTransform transform that's related to the error - */ - public PAffineTransformException(final Throwable throwable, final PAffineTransform errantTransform) { - super(throwable); - this.errantTransform = errantTransform; - } - - /** - * Constructs an Exception that wraps another and records the errant - * transform and provides a human readable message about the exception's - * context. - * - * @param message Text message provided by the programmer about the context - * of the error - * @param throwable the root cause of the exception - * @param errantTransform transform that's related to the error - */ - public PAffineTransformException(final String message, final Throwable throwable, - final PAffineTransform errantTransform) { - super(message, throwable); - this.errantTransform = errantTransform; - } - - /** - * Used to access the transform related to this exception. - * - * @return transform related to the exception - */ - public PAffineTransform getErrantTransform() { - return errantTransform; - } - -} 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 deleted file mode 100644 index 597755b..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PBounds.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.geom.Dimension2D; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -/** - * PBounds is simply a Rectangle2D.Double with extra methods that more - * properly deal with the case when the rectangle is "empty". A PBounds has an - * extra bit to store emptiness. In this state, adding new geometry replaces the - * current geometry. A PBounds is emptied with the reset() method. A useful side - * effect of the reset method is that it only modifies the fIsEmpty variable, - * the other x, y, with, height variables are left alone. This is used by - * Piccolo's layout management system to see if a the full bounds of a node has - * really changed when it is recomputed. See PNode.validateLayout. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PBounds extends Rectangle2D.Double implements Serializable { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - private boolean isEmpty = true; - - /** - * Creates an empty bounds. - */ - public PBounds() { - super(); - } - - /** - * Creates a bounds identical to the one provided. - * - * @param aBounds bounds to be copied - */ - public PBounds(final PBounds aBounds) { - this(aBounds.x, aBounds.y, aBounds.width, aBounds.height); - isEmpty = aBounds.isEmpty(); - } - - /** - * Creates a bounds with the same shape as the rectangle provided. - * - * @param aBounds rectangle to be copied - */ - public PBounds(final Rectangle2D aBounds) { - this(aBounds.getX(), aBounds.getY(), aBounds.getWidth(), aBounds.getHeight()); - isEmpty = aBounds.isEmpty(); - } - - /** - * Constructs a PBounds object with the given center point and the specified - * insets. - * - * @param aCenterPoint resulting center point of the PBounds object - * @param insetX distance from left and right the center should be - * @param insetY distance from top and bottom the center should be - */ - public PBounds(final Point2D aCenterPoint, final double insetX, final double insetY) { - this(aCenterPoint.getX(), aCenterPoint.getY(), 0, 0); - inset(insetX, insetY); - } - - /** - * Constructs a PBounds object at the given coordinates with the given - * dimensions. - * - * @param x left of bounds - * @param y top of bounds - * @param width width of bounds - * @param height height of bounds - */ - public PBounds(final double x, final double y, final double width, final double height) { - super(x, y, width, height); - isEmpty = false; - } - - /** - * Returns a clone of this node. - * - * @return cloned copy of this bounds - */ - public Object clone() { - return new PBounds(this); - } - - /** - * Returns true if this bounds has been flagged as empty. Not necessarily if - * it is empty. - * - * @return true if bounds marked as empty - */ - public boolean isEmpty() { - return isEmpty; - } - - /** - * Flags this bounds as empty. - * - * @return itself for chaining - */ - public PBounds reset() { - isEmpty = true; - return this; - } - - /** - * Resets the bounds to (0,0,0,0) and flags it as empty. - * - * @return itself for chaining - */ - public PBounds resetToZero() { - x = 0; - y = 0; - width = 0; - height = 0; - isEmpty = true; - return this; - } - - /** - * Sets the bounds to the same shape as the rectangle. And flags the bounds - * as not empty. - * - * @param r rectangle to copy - */ - public void setRect(final Rectangle2D r) { - super.setRect(r); - isEmpty = false; - } - - /** - * Sets the bounds to the same shape as the bounds provided. And flags the - * bounds as not empty. - * - * @param b bounds to copy - */ - public void setRect(final PBounds b) { - isEmpty = b.isEmpty; - x = b.x; - y = b.y; - width = b.width; - height = b.height; - } - - /** - * Sets the shape of the bounds to the position and dimension provided. - * - * @param x new left of bounds - * @param y new top of bounds - * @param width new width of bounds - * @param height new height of bounds - */ - public void setRect(final double x, final double y, final double width, final double height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - isEmpty = false; - } - - /** - * Grows the bounds to contain the coordinate provided. - * - * @param newx x component of point - * @param newy y component of point - */ - public void add(final double newx, final double newy) { - if (isEmpty) { - setRect(newx, newy, 0, 0); - isEmpty = false; - } - else { - super.add(newx, newy); - } - } - - /** - * Grows bounds to contain the rectangle if needed. - * - * @param r rectangle being added - */ - public void add(final Rectangle2D r) { - if (isEmpty) { - setRect(r); - } - else { - super.add(r); - } - } - - /** - * Changes this bounds to contain the provided bounds. - * - * @param bounds bounds being added - */ - public void add(final PBounds bounds) { - if (bounds.isEmpty) { - return; - } - else if (isEmpty) { - x = bounds.x; - y = bounds.y; - width = bounds.width; - height = bounds.height; - isEmpty = false; - } - else { - final double x1 = Math.min(x, bounds.x); - final double y1 = Math.min(y, bounds.y); - final double x2 = Math.max(x + width, bounds.x + bounds.width); - final double y2 = Math.max(y + height, bounds.y + bounds.height); - - x = x1; - y = y1; - width = x2 - x1; - height = y2 - y1; - isEmpty = false; - } - } - - /** - * Returns the x,y coordinate of the bounds. - * - * @return coordinate of the bounds - */ - public Point2D getOrigin() { - return new Point2D.Double(x, y); - } - - /** - * Changes the origin of these bounds. And flags it as non-empty. - * - * @param x new x component of bounds - * @param y new y component of the bounds - * @return the modified PBounds with its new origin - */ - public PBounds setOrigin(final double x, final double y) { - this.x = x; - this.y = y; - isEmpty = false; - return this; - } - - /** - * Returns the size of the bounds. - * - * @return size of the bounds - */ - public Dimension2D getSize() { - return new PDimension(width, height); - } - - /** - * Changes the size of the bounds, but retains the origin. - * - * @param width new width of the bounds - * @param height new height of the bounds - */ - public void setSize(final double width, final double height) { - setRect(x, y, width, height); - } - - /** - * Returns the midpoint of the bounds. - * - * @return midpoint of the bounds - */ - public Point2D getCenter2D() { - return new Point2D.Double(getCenterX(), getCenterY()); - } - - /** - * Translates the bounds by the given deltas. - * - * @param dx amount to move x - * @param dy amount to move y - * @return itself for chaining - */ - public PBounds moveBy(final double dx, final double dy) { - setOrigin(x + dx, y + dy); - return this; - } - - /** - * Rounds the rectangle to the next largest bounds who's measurements are - * integers. Note: this is not the same as rounding its measurements. - */ - public void expandNearestIntegerDimensions() { - x = Math.floor(x); - y = Math.floor(y); - width = Math.ceil(width); - height = Math.ceil(height); - } - - /** - * Adjust the measurements of this bounds so that they are the amounts given - * "in" from their previous border. - * - * @param dx amount to move in from border along horizontal axis - * @param dy amount to move in from border along vertical axis - * @return itself for chaining - */ - public PBounds inset(final double dx, final double dy) { - setRect(x + dx, y + dy, width - dx * 2, height - dy * 2); - return this; - } - - /** - * Returns the required translation in order for this bounds origin to sit - * on the center of the provided rectangle. - * - * @param targetBounds rectangle to measure the center of - * @return the delta required to move to center of the targetBounds - */ - public PDimension deltaRequiredToCenter(final Rectangle2D targetBounds) { - final PDimension result = new PDimension(); - final double xDelta = getCenterX() - targetBounds.getCenterX(); - final double yDelta = getCenterY() - targetBounds.getCenterY(); - result.setSize(xDelta, yDelta); - return result; - } - - /** - * Returns the required translation in order for these to contain the bounds - * provided. - * - * @param targetBounds rectangle to measure the center of - * @return the delta required in order for the bounds to overlap completely - * the targetBounds - */ - public PDimension deltaRequiredToContain(final Rectangle2D targetBounds) { - final PDimension result = new PDimension(); - - if (contains(targetBounds)) { - return result; - } - - final double targetMaxX = targetBounds.getMaxX(); - final double targetMinX = targetBounds.getMinX(); - final double targetMaxY = targetBounds.getMaxY(); - final double targetMinY = targetBounds.getMinY(); - final double maxX = getMaxX(); - final double minX = getMinX(); - final double maxY = getMaxY(); - final double minY = getMinY(); - - if (targetMaxX > maxX ^ targetMinX < minX) { - final double difMaxX = targetMaxX - maxX; - final double difMinX = targetMinX - minX; - if (Math.abs(difMaxX) < Math.abs(difMinX)) { - result.width = difMaxX; - } - else { - result.width = difMinX; - } - } - - if (targetMaxY > maxY ^ targetMinY < minY) { - final double difMaxY = targetMaxY - maxY; - final double difMinY = targetMinY - minY; - if (Math.abs(difMaxY) < Math.abs(difMinY)) { - result.height = difMaxY; - } - else { - result.height = difMinY; - } - } - - return result; - } - - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeDouble(x); - out.writeDouble(y); - out.writeDouble(width); - out.writeDouble(height); - } - - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - x = in.readDouble(); - y = in.readDouble(); - width = in.readDouble(); - height = in.readDouble(); - } - - /** - * Returns a string representation of this PBounds for debugging purposes. - * - * @return string representation of this PBounds - */ - public String toString() { - final StringBuffer result = new StringBuffer(); - - result.append(getClass().getName().replaceAll(".*\\.", "")); - result.append('['); - - if (isEmpty) { - result.append("EMPTY"); - } - else { - result.append("x="); - result.append(x); - result.append(",y="); - result.append(y); - result.append(",width="); - result.append(width); - result.append(",height="); - result.append(height); - } - - result.append(']'); - - return result.toString(); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PDebug.java b/core/src/main/java/edu/umd/cs/piccolo/util/PDebug.java deleted file mode 100644 index bd89a85..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PDebug.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; - -import javax.swing.SwingUtilities; - -/** - * PDebug is used to set framework wide debugging flags. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PDebug { - /** Set to true to display clip bounds boxes. */ - public static boolean debugRegionManagement = false; - - /** - * Set to true if you want to display common errors with painting and - * threading. - */ - public static boolean debugPaintCalls = false; - - /** Set to true to display frame rate in the console. */ - public static boolean debugPrintFrameRate = false; - - /** Set to true to display used memory in console. */ - public static boolean debugPrintUsedMemory = false; - - /** Displays bounding boxes around nodes. Used in PCamera. */ - public static boolean debugBounds = false; - - /** Displays a tint to all shapes within a bounding box. */ - public static boolean debugFullBounds = false; - - /** Whether to complain whenever common threading issues occur. */ - public static boolean debugThreads = false; - - /** How often in frames result info should be printed to the console. */ - public static int printResultsFrameRate = 10; - - private static int debugPaintColor; - private static long framesProcessed; - private static long startProcessingOutputTime; - private static long startProcessingInputTime; - private static long processOutputTime; - private static long processInputTime; - private static boolean processingOutput; - - private PDebug() { - super(); - } - - /** - * Generates a color for use while debugging. - * - * @return a color for use while debugging. - */ - public static Color getDebugPaintColor() { - final int color = 100 + debugPaintColor++ % 10 * 10; - return new Color(color, color, color, 150); - } - - /** - * Checks that process inputs is being doing from the Swing Dispatch Thread. - */ - public static void scheduleProcessInputs() { - if (debugThreads && !SwingUtilities.isEventDispatchThread()) { - System.out.println("scene graph manipulated on wrong thread"); - } - } - - /** - * Ensures that painting is not invalidating paint regions and that it's - * being called from the dispatch thread. - */ - public static void processRepaint() { - if (processingOutput && debugPaintCalls) { - System.err - .println("Got repaint while painting scene. This can result in a recursive process that degrades performance."); - } - - if (debugThreads && !SwingUtilities.isEventDispatchThread()) { - System.out.println("repaint called on wrong thread"); - } - } - - /** - * Returns whether output is being processed. - * - * @return whether output is being processed - */ - public static boolean getProcessingOutput() { - return processingOutput; - } - - /** - * Records that processing of ouptut has begun. - */ - public static void startProcessingOutput() { - processingOutput = true; - startProcessingOutputTime = System.currentTimeMillis(); - } - - /** - * Flags processing of output as finished. Updates all stats in the process. - * - * @param g graphics context in which processing has finished - */ - public static void endProcessingOutput(final Graphics g) { - processOutputTime += System.currentTimeMillis() - startProcessingOutputTime; - framesProcessed++; - - if (framesProcessed % printResultsFrameRate == 0) { - if (PDebug.debugPrintFrameRate) { - System.out.println("Process output frame rate: " + getOutputFPS() + " fps"); - System.out.println("Process input frame rate: " + getInputFPS() + " fps"); - System.out.println("Total frame rate: " + getTotalFPS() + " fps"); - System.out.println(); - resetFPSTiming(); - } - - if (PDebug.debugPrintUsedMemory) { - System.out.println("Approximate used memory: " + getApproximateUsedMemory() / 1024 + " k"); - } - } - - if (PDebug.debugRegionManagement) { - final Graphics2D g2 = (Graphics2D) g; - g2.setColor(PDebug.getDebugPaintColor()); - g2.fill(g.getClipBounds().getBounds2D()); - } - - processingOutput = false; - } - - /** - * Records that processing of input has started. - */ - public static void startProcessingInput() { - startProcessingInputTime = System.currentTimeMillis(); - } - - /** - * Records that processing of input has finished. - */ - public static void endProcessingInput() { - processInputTime += System.currentTimeMillis() - startProcessingInputTime; - } - - /** - * Return how many frames are processed and painted per second. Note that - * since piccolo doesn't paint continuously this rate will be slow unless - * you are interacting with the system or have activities scheduled. - * - * @return frame rate achieved - */ - public static double getTotalFPS() { - if (framesProcessed > 0) { - return 1000.0 / ((processInputTime + processOutputTime) / (double) framesProcessed); - } - else { - return 0; - } - } - - /** - * Return the frames per second used to process input events and activities. - * - * @return # of frames per second that were allocated to processing input - */ - public static double getInputFPS() { - if (processInputTime > 0 && framesProcessed > 0) { - return 1000.0 / (processInputTime / (double) framesProcessed); - } - else { - return 0; - } - } - - /** - * Return the frames per seconds used to paint graphics to the screen. - * - * @return # of frames per second that were used up to processing output - */ - public static double getOutputFPS() { - if (processOutputTime > 0 && framesProcessed > 0) { - return 1000.0 / (processOutputTime / (double) framesProcessed); - } - else { - return 0; - } - } - - /** - * Return the number of frames that have been processed since the last time - * resetFPSTiming was called. - * - * @return total number of frames processed - */ - public long getFramesProcessed() { - return framesProcessed; - } - - /** - * Reset the variables used to track FPS. If you reset seldom they you will - * get good average FPS values, if you reset more often only the frames - * recorded after the last reset will be taken into consideration. - */ - public static void resetFPSTiming() { - framesProcessed = 0; - processInputTime = 0; - processOutputTime = 0; - } - - /** - * Returns an approximation of the amount of memory that is being used. - * - * Not that this call might affecting timings. - * - * @return approximate # of bytes of memory used - */ - public static long getApproximateUsedMemory() { - System.gc(); - System.runFinalization(); - final long totalMemory = Runtime.getRuntime().totalMemory(); - final long free = Runtime.getRuntime().freeMemory(); - return totalMemory - free; - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PDimension.java b/core/src/main/java/edu/umd/cs/piccolo/util/PDimension.java deleted file mode 100644 index d20016d..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PDimension.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.geom.Dimension2D; -import java.awt.geom.Point2D; -import java.io.Serializable; - -/** - * PDimension this class should be removed once a concrete Dimension2D - * that supports doubles is added to java. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PDimension extends Dimension2D implements Serializable { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** The width of the dimension. */ - public double width; - - /** The height of the dimension. */ - public double height; - - /** - * Returns a dimension with no width or height. - */ - public PDimension() { - super(); - } - - /** - * Copies the provided dimension. - * - * @param aDimension dimension to copy - */ - public PDimension(final Dimension2D aDimension) { - this(aDimension.getWidth(), aDimension.getHeight()); - } - - /** - * Creates a dimension with the provided dimensions. - * - * @param aWidth desired width - * @param aHeight desired height - */ - public PDimension(final double aWidth, final double aHeight) { - super(); - width = aWidth; - height = aHeight; - } - - /** - * Creates a dimension that's the size of a rectangel with the points - * provided as opposite corners. - * - * @param p1 first point on rectangle - * @param p2 point diagonally across from p1 - */ - public PDimension(final Point2D p1, final Point2D p2) { - width = p2.getX() - p1.getX(); - height = p2.getY() - p1.getY(); - } - - /** - * Returns the height of the dimension. - * - * @return height height of the dimension - */ - public double getHeight() { - return height; - } - - /** - * Returns the width of the dimension. - * - * @return width width of the dimension - */ - public double getWidth() { - return width; - } - - /** - * Resizes the dimension to have the dimensions provided. - * - * @param aWidth desired width - * @param aHeight desired height - */ - public void setSize(final double aWidth, final double aHeight) { - width = aWidth; - height = aHeight; - } - - /** - * Returns a string representation of this dimension object. - * - * @return string representation of this dimension object. - */ - public String toString() { - final StringBuffer result = new StringBuffer(); - - result.append(super.toString().replaceAll(".*\\.", "")); - result.append('['); - result.append("width="); - result.append(width); - result.append(",height="); - result.append(height); - result.append(']'); - - return result.toString(); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PNodeFilter.java b/core/src/main/java/edu/umd/cs/piccolo/util/PNodeFilter.java deleted file mode 100644 index 98a6821..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PNodeFilter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import edu.umd.cs.piccolo.PNode; - -/** - * PNodeFilter is a interface that filters (accepts or rejects) nodes. - * Its main use is to retrieve all the children of a node the meet some criteria - * by using the method PNode.getAllNodes(collection, filter); - *
- *
- * @version 1.0
- * @author Jesse Grosjean
- */
-public interface PNodeFilter {
-
- /**
- * Return true if the filter should accept the given node.
- *
- * @param aNode node under test
- * @return true if node should be accepted
- */
- boolean accept(PNode aNode);
-
- /**
- * Return true if the filter should test the children of the given node for
- * acceptance.
- *
- * @param aNode parent being tested
- * @return true if children should be tested for acceptance
- */
- boolean acceptChildrenOf(PNode aNode);
-}
diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PObjectOutputStream.java b/core/src/main/java/edu/umd/cs/piccolo/util/PObjectOutputStream.java
deleted file mode 100644
index eb0a817..0000000
--- a/core/src/main/java/edu/umd/cs/piccolo/util/PObjectOutputStream.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
- * Copyright (c) 1998-2008, University of Maryland
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided
- * that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of conditions
- * and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
- * and the following disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
- * contributors may be used to endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package edu.umd.cs.piccolo.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.util.HashMap;
-
-/**
- * PObjectOutputStream is an extension of ObjectOutputStream to handle
- * optional elements. This is similar to the concept of Java's
- * "weak references", but applied to object serialization rather than garbage
- * collection. Here, PObjectOutputStream provides a method,
- * writeConditionalObject
, which only serializes the specified
- * object to the stream if there is a strong reference (if it has been written
- * somewhere else using writeObject()) to that object elsewhere in the stream.
- *
- * To discover strong references to objects, PObjectOutputStream uses a
- * two-phase writing process. First, a "discovery" phase is used to find out
- * what objects are about to be serialized. This works by effectively
- * serializing the object graph to /dev/null, recording which objects are
- * unconditionally written using the standard writeObject method. Then, in the
- * second "write" phase, ObjectOutputStream actually serializes the data to the
- * output stream. During this phase, calls to writeConditionalObject() will only
- * write the specified object if the object was found to be serialized during
- * the discovery stage. If the object was not recorded during the discovery
- * stage, a an optional null (the default) is unconditionally written in place
- * of the object. To skip writting out the null use
- * writeConditionalObject(object, false)
- *
- * By careful implementation of readObject and writeObject methods, streams - * serialized using PObjectOutputStream can be deserialized using the standard - * ObjectInputStream. - *
- * - * @version 1.0 - * @author Jon Meyer - * @author Jesse Grosjean - */ -public class PObjectOutputStream extends ObjectOutputStream { - - private boolean writingRoot; - private final HashMap unconditionallyWritten; - - /** - * Transform the given object into an array of bytes. - * - * @param object the object to be transformed - * @return array of bytes representing the given object - * @throws IOException when serialization system throws one - */ - public static byte[] toByteArray(final Object object) throws IOException { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final PObjectOutputStream zout = new PObjectOutputStream(out); - zout.writeObjectTree(object); - return out.toByteArray(); - } - - /** - * Constructs a PObjectOutputStream that wraps the provided OutputStream. - * - * @param out underlying outputstream that will receive the serialized - * objects - * - * @throws IOException when underlying subsystem throws one - */ - public PObjectOutputStream(final OutputStream out) throws IOException { - super(out); - unconditionallyWritten = new HashMap(); - } - - /** - * Writes the provided object to the underlying stream like an ordination - * ObjectOutputStream except that it does not record duplicates at all. - * - * @param object object to be serialized - * - * @throws IOException when underlying subsystem throws one - */ - public void writeObjectTree(final Object object) throws IOException { - writingRoot = true; - recordUnconditionallyWritten(object); // record pass - writeObject(object); // write pass - writingRoot = false; - } - - /** - * Writes the given object, but only if it was not in the object tree - * multiple times. - * - * @param object object to write to the stream. - * @throws IOException when underlying subsystem throws one - */ - public void writeConditionalObject(final Object object) throws IOException { - if (!writingRoot) { - throw new RuntimeException( - "writeConditionalObject() may only be called when a root object has been written."); - } - - if (unconditionallyWritten.containsKey(object)) { - writeObject(object); - } - else { - writeObject(null); - } - } - - /** - * Resets the ObjectOutputStream clearing any memory about objects already - * being written while it's at it. - * - * @throws IOException when underlying subsystem throws one - */ - public void reset() throws IOException { - super.reset(); - unconditionallyWritten.clear(); - } - - /** - * Performs a scan of objects that can be serialized once. - * - * @param aRoot Object from which to start the scan - * @throws IOException when serialization fails - */ - protected void recordUnconditionallyWritten(final Object aRoot) throws IOException { - class ZMarkObjectOutputStream extends PObjectOutputStream { - public ZMarkObjectOutputStream() throws IOException { - super(NULL_OUTPUT_STREAM); - enableReplaceObject(true); - } - - public Object replaceObject(final Object object) { - unconditionallyWritten.put(object, Boolean.TRUE); - return object; - } - - public void writeConditionalObject(final Object object) throws IOException { - } - } - new ZMarkObjectOutputStream().writeObject(aRoot); - } - - private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() { - public void close() { - } - - public void flush() { - } - - public void write(final byte[] b) { - } - - public void write(final byte[] b, final int off, final int len) { - } - - public void write(final int b) { - } - }; -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PPaintContext.java b/core/src/main/java/edu/umd/cs/piccolo/util/PPaintContext.java deleted file mode 100644 index d5d103a..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PPaintContext.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.AlphaComposite; -import java.awt.Composite; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.Shape; -import java.awt.font.FontRenderContext; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -import edu.umd.cs.piccolo.PCamera; - -/** - * PPaintContext is used by piccolo nodes to paint themselves on the - * screen. PPaintContext wraps a Graphics2D to implement painting. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PPaintContext { - /** Used for lowering quality of rendering when requested. */ - public static final int LOW_QUALITY_RENDERING = 0; - - /** Used for improving quality of rendering when requested. */ - public static final int HIGH_QUALITY_RENDERING = 1; - - /** Font context to use while in low quality rendering. */ - public static final FontRenderContext RENDER_QUALITY_LOW_FRC = new FontRenderContext(null, false, true); - - /** Font context to use while in high quality rendering. */ - public static final FontRenderContext RENDER_QUALITY_HIGH_FRC = new FontRenderContext(null, true, true); - - /** Used while calculating scale at which rendering is occurring. */ - private static final double[] PTS = new double[4]; - - /** PaintContext is associated with this graphics context. */ - private final Graphics2D graphics; - - /** Used while computing transparency. */ - protected PStack compositeStack; - - /** Used to optimize clipping region. */ - protected PStack clipStack; - - /** Tracks clipping region in local coordinate system. */ - protected PStack localClipStack; - - /** Stack of cameras through which the node being painted is being viewed. */ - protected PStack cameraStack; - - /** Stack of transforms being applied to the drawing context. */ - protected PStack transformStack; - - /** The current render quality that all rendering should be done in. */ - protected int renderQuality; - - /** - * Creates a PPaintContext associated with the given graphics context. - * - * @param graphics graphics context to associate with this paint context - */ - public PPaintContext(final Graphics2D graphics) { - this.graphics = graphics; - compositeStack = new PStack(); - clipStack = new PStack(); - localClipStack = new PStack(); - cameraStack = new PStack(); - transformStack = new PStack(); - renderQuality = HIGH_QUALITY_RENDERING; - - Shape clip = graphics.getClip(); - if (clip == null) { - clip = new PBounds(-Integer.MAX_VALUE / 2, -Integer.MAX_VALUE / 2, Integer.MAX_VALUE, Integer.MAX_VALUE); - graphics.setClip(clip); - } - - localClipStack.push(clip.getBounds2D()); - } - - /** - * Returns the graphics context associated with this paint context. - * - * @return graphics context associated with this paint context - */ - public Graphics2D getGraphics() { - return graphics; - } - - /** - * Returns the clipping region in the local coordinate system applied by - * graphics. - * - * @return clipping region in the local coordinate system applied by - * graphics - */ - public Rectangle2D getLocalClip() { - return (Rectangle2D) localClipStack.peek(); - } - - /** - * Returns scale of the current graphics context. By calculating how a unit - * segment gets transformed after transforming it by the graphics context's - * transform. - * - * @return scale of the current graphics context's transformation - */ - public double getScale() { - // x1, y1, x2, y2 - PTS[0] = 0; - PTS[1] = 0; - PTS[2] = 1; - PTS[3] = 0; - graphics.getTransform().transform(PTS, 0, PTS, 0, 2); - return Point2D.distance(PTS[0], PTS[1], PTS[2], PTS[3]); - } - - /** - * Pushes the camera onto the camera stack. - * - * @param aCamera camera to push onto the stack - */ - public void pushCamera(final PCamera aCamera) { - cameraStack.push(aCamera); - } - - /** - * Removes the camera at the top of the camera stack. - * - * @since 1.3 - */ - public void popCamera() { - cameraStack.pop(); - } - - /** - * Returns the camera at the top of the camera stack, or null if stack is - * empty. - * - * @return topmost camera on camera stack or null if stack is empty - */ - public PCamera getCamera() { - return (PCamera) cameraStack.peek(); - } - - /** - * Pushes the given clip to the pain context. - * - * @param clip clip to be pushed - */ - public void pushClip(final Shape clip) { - final Shape currentClip = graphics.getClip(); - clipStack.push(currentClip); - graphics.clip(clip); - final Rectangle2D newLocalClip = clip.getBounds2D(); - Rectangle2D.intersect(getLocalClip(), newLocalClip, newLocalClip); - localClipStack.push(newLocalClip); - } - - /** - * Removes the topmost clipping region from the clipping stack. - * - * @param clip not used in this method - */ - public void popClip(final Shape clip) { - final Shape newClip = (Shape) clipStack.pop(); - graphics.setClip(newClip); - localClipStack.pop(); - } - - /** - * Pushes the provided transparency onto the transparency stack if - * necessary. If the transparency is fully opaque, then it does nothing. - * - * @param transparency transparency to be pushed onto the transparency stack - */ - public void pushTransparency(final float transparency) { - if (transparency == 1.0f) { - return; - } - final Composite current = graphics.getComposite(); - float currentAlaph = 1.0f; - compositeStack.push(current); - - if (current instanceof AlphaComposite) { - currentAlaph = ((AlphaComposite) current).getAlpha(); - } - final AlphaComposite newComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, currentAlaph - * transparency); - graphics.setComposite(newComposite); - } - - /** - * Removes the topmost transparency if the given transparency is not opaque - * (1f). - * - * @param transparency transparency to be popped - */ - public void popTransparency(final float transparency) { - if (transparency == 1.0f) { - return; - } - final Composite c = (Composite) compositeStack.pop(); - graphics.setComposite(c); - } - - /** - * Pushed the provided transform onto the transform stack if it is not null. - * - * @param transform will be pushed onto the transform stack if not null - */ - public void pushTransform(final PAffineTransform transform) { - if (transform != null) { - final Rectangle2D newLocalClip = (Rectangle2D) getLocalClip().clone(); - transform.inverseTransform(newLocalClip, newLocalClip); - transformStack.push(graphics.getTransform()); - localClipStack.push(newLocalClip); - graphics.transform(transform); - } - } - - /** - * Pops the topmost Transform from the top of the transform if the passed in - * transform is not null. - * - * @param transform transform that should be at the top of the stack - */ - public void popTransform(final PAffineTransform transform) { - if (transform != null) { - graphics.setTransform((AffineTransform) transformStack.pop()); - localClipStack.pop(); - } - } - - /** - * Return the render quality used by this paint context. - * - * @return the current render quality - */ - public int getRenderQuality() { - return renderQuality; - } - - /** - * Set the rendering hints for this paint context. The render quality is - * most often set by the rendering PCanvas. Use PCanvas.setRenderQuality() - * and PCanvas.setInteractingRenderQuality() to set these values. - * - * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or - * PPaintContext.LOW_QUALITY_RENDERING - */ - public void setRenderQuality(final int requestedQuality) { - renderQuality = requestedQuality; - - switch (renderQuality) { - case HIGH_QUALITY_RENDERING: - setRenderQualityToHigh(); - break; - - case LOW_QUALITY_RENDERING: - setRenderQualityToLow(); - break; - - default: - throw new RuntimeException("Quality must be either HIGH_QUALITY_RENDERING or LOW_QUALITY_RENDERING"); - } - } - - private void setRenderQualityToLow() { - graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); - graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); - graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); - } - - private void setRenderQualityToHigh() { - graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PPickPath.java b/core/src/main/java/edu/umd/cs/piccolo/util/PPickPath.java deleted file mode 100644 index a23e8b6..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PPickPath.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.geom.Dimension2D; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.util.HashMap; - -import javax.swing.event.EventListenerList; - -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventListener; - -/** - * PPickPath represents a ordered list of nodes that have been picked. - * The topmost ancestor node is the first node in the list (and should be a - * camera), the bottommost child node is at the end of the list. It is this - * bottom node that is given first chance to handle events, and that any active - * event handlers usually manipulate. - *
- * Note that because of layers (which can be picked by multiple camera's) the - * ordered list of nodes in a pick path do not all share a parent child - * relationship with the nodes in the list next to them. This means that the - * normal localToGlobal methods don't work when trying to transform geometry up - * and down the pick path, instead you should use the pick paths canvasToLocal - * methods to get the mouse event points into your local coord system. - *
- * Note that PInputEvent wraps most of the useful PPickPath methods, so often - * you can use a PInputEvent directly instead of having to access its pick path. - *
- * - * @see edu.umd.cs.piccolo.event.PInputEvent - * @version 1.0 - * @author Jesse Grosjean - */ -public class PPickPath implements PInputEventListener { - /** Global pick path. */ - public static PPickPath CURRENT_PICK_PATH; - - /** Used when calculating the scale. */ - private static final double[] PTS = new double[4]; - - /** Stack of nodes representing all picked nodes. */ - private PStack nodeStack; - - private final PCamera topCamera; - private PStack transformStack; - private PStack pickBoundsStack; - private PCamera bottomCamera; - private HashMap excludedNodes; - - /** - * Creates a pick pack originating from the provided camera and with the - * given screen pick bounds. - * - * @param camera camera from which the pickpath originates - * @param aScreenPickBounds bounds of pick area - */ - public PPickPath(final PCamera camera, final PBounds aScreenPickBounds) { - super(); - pickBoundsStack = new PStack(); - topCamera = camera; - nodeStack = new PStack(); - transformStack = new PStack(); - pickBoundsStack.push(aScreenPickBounds); - - CURRENT_PICK_PATH = this; - } - - /** - * Returns the bounds of the entire PickPath taken as a whole. - * - * @return bounds of the entire PickPath - */ - public PBounds getPickBounds() { - return (PBounds) pickBoundsStack.peek(); - } - - /** - * Determines if the passed node has been excluded from being a member of - * the pickpath. - * - * @param node node being tested - * @return true if node is acceptable to the path - */ - public boolean acceptsNode(final PNode node) { - return excludedNodes == null || !excludedNodes.containsKey(node); - } - - // **************************************************************** - // Picked Nodes - // **************************************************************** - - /** - * Pushes the provided node to the top of the pick path. - * - * @param node node to be added to the pick path - */ - public void pushNode(final PNode node) { - nodeStack.push(node); - } - - /** - * Removes the topmost node from the node stack. - * - * @param node completely unused in this method, but is passed in so that - * subclasses may be informed of it. - */ - public void popNode(final PNode node) { - nodeStack.pop(); - } - - /** - * Get the bottom node on the pick path node stack. That is the last node to - * be picked. - * - * @return the bottom node on the pick path - */ - public PNode getPickedNode() { - return (PNode) nodeStack.peek(); - } - - // **************************************************************** - // Iterating over picked nodes. - // **************************************************************** - - /** - * Return the next node that will be picked after the current picked node. - * For instance of you have two overlapping children nodes then the topmost - * child will always be picked first, use this method to find the covered - * child. Return the camera when no more visual will be picked. - * - * @return next node to picked after the picked node - */ - public PNode nextPickedNode() { - final PNode picked = getPickedNode(); - - if (picked == topCamera) { - return null; - } - if (excludedNodes == null) { - excludedNodes = new HashMap(); - } - - // exclude current picked node - excludedNodes.put(picked, picked); - - final Object screenPickBounds = pickBoundsStack.get(0); - - // reset path state - pickBoundsStack = new PStack(); - nodeStack = new PStack(); - transformStack = new PStack(); - pickBoundsStack = new PStack(); - - pickBoundsStack.push(screenPickBounds); - - // pick again - topCamera.fullPick(this); - - // make sure top camera is pushed. - if (getNodeStackReference().size() == 0) { - pushNode(topCamera); - pushTransform(topCamera.getTransformReference(false)); - } - - return getPickedNode(); - } - - /** - * Get the top camera on the pick path. This is the camera that originated - * the pick action. - * - * @return the topmost camera of this pick pack - */ - public PCamera getTopCamera() { - return topCamera; - } - - /** - * Get the bottom camera on the pick path. This may be different then the - * top camera if internal cameras are in use. - * - * @return the camera closest to the picked node - */ - public PCamera getBottomCamera() { - if (bottomCamera == null) { - bottomCamera = calculateBottomCamera(); - } - return bottomCamera; - } - - private PCamera calculateBottomCamera() { - for (int i = nodeStack.size() - 1; i >= 0; i--) { - final PNode each = (PNode) nodeStack.get(i); - if (each instanceof PCamera) { - return (PCamera) each; - } - } - return null; - } - - /** - * Returns a reference to the node stack. Be Careful! - * - * @return the node stack - */ - public PStack getNodeStackReference() { - return nodeStack; - } - - // **************************************************************** - // Path Transform - // **************************************************************** - - /** - * Returns the resulting scale of applying the transforms of the entire pick - * path. In essence it gives you the scale at which interaction is - * occurring. - * - * @return scale at which interaction is occurring. - */ - public double getScale() { - // x1, y1, x2, y3 - PTS[0] = 0; - PTS[1] = 0; - PTS[2] = 1; - PTS[3] = 0; - - final int count = transformStack.size(); - for (int i = 0; i < count; i++) { - final PAffineTransform each = ((PTuple) transformStack.get(i)).transform; - if (each != null) { - each.transform(PTS, 0, PTS, 0, 2); - } - } - - return Point2D.distance(PTS[0], PTS[1], PTS[2], PTS[3]); - } - - /** - * Adds the transform to the pick path's transform. This is used when - * determining the context of the current interaction. - * - * @param transform transform to be added to applied to the pickpath. - */ - public void pushTransform(final PAffineTransform transform) { - transformStack.push(new PTuple(getPickedNode(), transform)); - if (transform != null) { - final Rectangle2D newPickBounds = (Rectangle2D) getPickBounds().clone(); - transform.inverseTransform(newPickBounds, newPickBounds); - pickBoundsStack.push(newPickBounds); - } - } - - /** - * Pops the top most transform from the pick path. - * - * @param transform unused in this method - */ - public void popTransform(final PAffineTransform transform) { - transformStack.pop(); - if (transform != null) { - pickBoundsStack.pop(); - } - } - - /** - * Calculates the context at which the given node is being interacted with. - * - * @param nodeOnPath a node currently on the pick path. An exception will be - * thrown if the node cannot be found. - * - * @return Transform at which the given node is being interacted with. - */ - public PAffineTransform getPathTransformTo(final PNode nodeOnPath) { - final PAffineTransform aTransform = new PAffineTransform(); - - final int count = transformStack.size(); - for (int i = 0; i < count; i++) { - final PTuple each = (PTuple) transformStack.get(i); - if (each.transform != null) { - aTransform.concatenate(each.transform); - } - if (nodeOnPath == each.node) { - return aTransform; - } - } - - throw new RuntimeException("Node could not be found on pick path"); - } - - /** - * Process Events - Give each node in the pick path, starting at the bottom - * most one, a chance to handle the event. - * - * @param event event to be processed - * @param eventType the type of event being processed - */ - public void processEvent(final PInputEvent event, final int eventType) { - event.setPath(this); - - for (int i = nodeStack.size() - 1; i >= 0; i--) { - final PNode each = (PNode) nodeStack.get(i); - - final EventListenerList list = each.getListenerList(); - - if (list != null) { - final Object[] listeners = list.getListeners(PInputEventListener.class); - - for (int j = 0; j < listeners.length; j++) { - final PInputEventListener listener = (PInputEventListener) listeners[j]; - listener.processEvent(event, eventType); - if (event.isHandled()) { - return; - } - } - } - } - } - - // **************************************************************** - // Transforming Geometry - Methods to transform geometry through - // this path. - //
- // Note that this is different that just using the - // PNode.localToGlobal (an other coord system transform methods). - // The PNode coord system transform methods always go directly up - // through their parents. The PPickPath coord system transform - // methods go up through the list of picked nodes instead. And since - // cameras can pick their layers in addition to their children these - // two paths may be different. - // **************************************************************** - - /** - * Convert the given point from the canvas coordinates, down through the - * pick path (and through any camera view transforms applied to the path) to - * the local coordinates of the given node. - * - * @param canvasPoint point to be transformed - * @param nodeOnPath node into which the point is to be transformed - * iteratively through the pick path - * - * @return transformed canvasPoint in local coordinates of the picked node - */ - public Point2D canvasToLocal(final Point2D canvasPoint, final PNode nodeOnPath) { - return getPathTransformTo(nodeOnPath).inverseTransform(canvasPoint, canvasPoint); - } - - /** - * Convert the given dimension from the canvas coordinates, down through the - * pick path (and through any camera view transforms applied to the path) to - * the local coordinates of the given node. - * - * @param canvasDimension dimension to be transformed - * @param nodeOnPath node into which the dimension is to be transformed - * iteratively through the stack - * - * @return transformed canvasDimension in local coordinates of the picked - * node - */ - public Dimension2D canvasToLocal(final Dimension2D canvasDimension, final PNode nodeOnPath) { - return getPathTransformTo(nodeOnPath).inverseTransform(canvasDimension, canvasDimension); - } - - /** - * Convert the given rectangle from the canvas coordinates, down through the - * pick path (and through any camera view transforms applied to the path) to - * the local coordinates of the given node. - * - * @param canvasRectangle rectangle to be transformed - * @param nodeOnPath node into which the rectangle is to be transformed - * iteratively through the stack - * @return transformed canvasRectangle in local coordinates of the picked - * node - */ - public Rectangle2D canvasToLocal(final Rectangle2D canvasRectangle, final PNode nodeOnPath) { - return getPathTransformTo(nodeOnPath).inverseTransform(canvasRectangle, canvasRectangle); - } - - /** - * Used to associated nodes with their transforms on the transform stack. - */ - private static class PTuple { - public PNode node; - public PAffineTransform transform; - - public PTuple(final PNode n, final PAffineTransform t) { - node = n; - transform = t; - } - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PStack.java b/core/src/main/java/edu/umd/cs/piccolo/util/PStack.java deleted file mode 100644 index 2995046..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PStack.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.util.ArrayList; - -/** - * PStack this class should be removed when a non thread safe stack is - * added to the java class libraries. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PStack extends ArrayList { - /** - * Allows for future serialization code to understand versioned binary - * formats. - */ - private static final long serialVersionUID = 1L; - - /** - * Creates an empty stack. - */ - public PStack() { - } - - /** - * Pushes the provided object onto the top of the stack. - * - * @param o object to add to the stack - */ - public void push(final Object o) { - add(o); - } - - /** - * Returns topmost element on the stack, or null if stack is empty. - * - * @return topmost element on the stack, or null if empty - */ - public Object peek() { - final int s = size(); - if (s == 0) { - return null; - } - else { - return get(s - 1); - } - } - - /** - * Removes top element on the stack and returns it. - * - * @return topmost element on stack. - */ - public Object pop() { - return remove(size() - 1); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/PUtil.java b/core/src/main/java/edu/umd/cs/piccolo/util/PUtil.java deleted file mode 100644 index a5634be..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/PUtil.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.BasicStroke; -import java.awt.Stroke; -import java.awt.geom.GeneralPath; -import java.awt.geom.PathIterator; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; - -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PRoot; - -/** - * PUtil util methods for the Piccolo framework. - *
- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PUtil { - /** - * PActivities are broken into steps, this is how many milliseconds should - * pass between steps. - */ - public static final long DEFAULT_ACTIVITY_STEP_RATE = 20; - - /** Rate in milliseconds at which the activity timer will get invoked. */ - public static final int ACTIVITY_SCHEDULER_FRAME_DELAY = 10; - - /** An iterator that iterates over an empty collection. */ - public static final Iterator NULL_ITERATOR = Collections.EMPTY_LIST.iterator(); - - /** - * Used when persisting paths to an object stream. Used to mark the end of - * the path. - */ - private static final int PATH_TERMINATOR = -1; - - /** A utility enumeration with no elements. */ - public static final Enumeration NULL_ENUMERATION = new Enumeration() { - public boolean hasMoreElements() { - return false; - } - - public Object nextElement() { - return null; - } - }; - - /** - * Creates the simplest possible scene graph. 1 Camera, 1 Layer, 1 Root - * - * @return a basic scene with 1 camera, layer and root - */ - public static PCamera createBasicScenegraph() { - final PRoot root = new PRoot(); - final PLayer layer = new PLayer(); - final PCamera camera = new PCamera(); - - root.addChild(camera); - root.addChild(layer); - camera.addLayer(layer); - - return camera; - } - - /** - * Serializes the given stroke object to the object output stream provided. - * By default strokes are not serializable. This method solves that problem. - * - * @param stroke stroke to be serialize - * @param out stream to which the stroke is to be serialized - * @throws IOException can occur if exception occurs with underlying output - * stream - */ - public static void writeStroke(final Stroke stroke, final ObjectOutputStream out) throws IOException { - if (stroke instanceof Serializable) { - out.writeBoolean(true); - out.writeBoolean(true); - out.writeObject(stroke); - } - else if (stroke instanceof BasicStroke) { - out.writeBoolean(true); - out.writeBoolean(false); - writeBasicStroke((BasicStroke) stroke, out); - } - else { - out.writeBoolean(false); - } - } - - private static void writeBasicStroke(final BasicStroke basicStroke, final ObjectOutputStream out) - throws IOException { - final float[] dash = basicStroke.getDashArray(); - - if (dash == null) { - out.write(0); - } - else { - out.write(dash.length); - for (int i = 0; i < dash.length; i++) { - out.writeFloat(dash[i]); - } - } - - out.writeFloat(basicStroke.getLineWidth()); - out.writeInt(basicStroke.getEndCap()); - out.writeInt(basicStroke.getLineJoin()); - out.writeFloat(basicStroke.getMiterLimit()); - out.writeFloat(basicStroke.getDashPhase()); - } - - /** - * Reconstitutes a stroke from the provided Object Input Stream. According - * to the scheme found in writeStroke. By default strokes are not - * serializable. - * - * @param in stream from which Stroke is to be read - * @return a stroke object - * @throws IOException occurs if an exception occurs reading from in stream - * @throws ClassNotFoundException should never happen, but can if somehow - * the stroke class is not on the classpath - */ - public static Stroke readStroke(final ObjectInputStream in) throws IOException, ClassNotFoundException { - final boolean wroteStroke = in.readBoolean(); - if (!wroteStroke) { - return null; - } - - final boolean serializedStroke = in.readBoolean(); - if (serializedStroke) { - return (Stroke) in.readObject(); - } - - return readBasicStroke(in); - } - - private static Stroke readBasicStroke(final ObjectInputStream in) throws IOException { - float[] dash = null; - final int dashLength = in.read(); - - if (dashLength != 0) { - dash = new float[dashLength]; - for (int i = 0; i < dashLength; i++) { - dash[i] = in.readFloat(); - } - } - - final float lineWidth = in.readFloat(); - final int endCap = in.readInt(); - final int lineJoin = in.readInt(); - final float miterLimit = in.readFloat(); - final float dashPhase = in.readFloat(); - - return new BasicStroke(lineWidth, endCap, lineJoin, miterLimit, dash, dashPhase); - } - - /** - * Reads a path from the provided inputStream in accordance with the - * serialization policy defined in writePath. - * - * @param in stream from which to read the path. - * @return reconstituted path - * @throws IOException if an unknown path type is read from the stream - * @throws ClassNotFoundException should never happen, but can if somehow - * the classpath is seriously messed up - */ - public static GeneralPath readPath(final ObjectInputStream in) throws IOException, ClassNotFoundException { - final GeneralPath path = new GeneralPath(); - - while (true) { - final int segType = in.readInt(); - - switch (segType) { - case PathIterator.SEG_MOVETO: - path.moveTo(in.readFloat(), in.readFloat()); - break; - - case PathIterator.SEG_LINETO: - path.lineTo(in.readFloat(), in.readFloat()); - break; - - case PathIterator.SEG_QUADTO: - path.quadTo(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); - break; - - case PathIterator.SEG_CUBICTO: - path.curveTo(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in - .readFloat()); - break; - - case PathIterator.SEG_CLOSE: - path.closePath(); - break; - - case PATH_TERMINATOR: - return path; - - default: - throw new IOException("Unknown path type encountered while deserializing path."); - } - } - } - - /** - * Serializes the given path to the provided Object Output Stream. - * - * @param path path to be serialized - * @param out stream to which the path should be serialized - * @throws IOException if unknown path segment type is encountered, or an - * exception occurs writing to the output stream - */ - public static void writePath(final GeneralPath path, final ObjectOutputStream out) throws IOException { - final PathIterator i = path.getPathIterator(null); - final float[] data = new float[6]; - - while (!i.isDone()) { - switch (i.currentSegment(data)) { - case PathIterator.SEG_MOVETO: - out.writeInt(PathIterator.SEG_MOVETO); - out.writeFloat(data[0]); - out.writeFloat(data[1]); - break; - - case PathIterator.SEG_LINETO: - out.writeInt(PathIterator.SEG_LINETO); - out.writeFloat(data[0]); - out.writeFloat(data[1]); - break; - - case PathIterator.SEG_QUADTO: - out.writeInt(PathIterator.SEG_QUADTO); - out.writeFloat(data[0]); - out.writeFloat(data[1]); - out.writeFloat(data[2]); - out.writeFloat(data[3]); - break; - - case PathIterator.SEG_CUBICTO: - out.writeInt(PathIterator.SEG_CUBICTO); - out.writeFloat(data[0]); - out.writeFloat(data[1]); - out.writeFloat(data[2]); - out.writeFloat(data[3]); - out.writeFloat(data[4]); - out.writeFloat(data[5]); - break; - - case PathIterator.SEG_CLOSE: - out.writeInt(PathIterator.SEG_CLOSE); - break; - - default: - throw new IOException("Unknown path type encountered while serializing path."); - } - - i.next(); - } - - out.writeInt(PATH_TERMINATOR); - } -} diff --git a/core/src/main/java/edu/umd/cs/piccolo/util/package.html b/core/src/main/java/edu/umd/cs/piccolo/util/package.html deleted file mode 100644 index c6af443..0000000 --- a/core/src/main/java/edu/umd/cs/piccolo/util/package.html +++ /dev/null @@ -1,5 +0,0 @@ -
-This package defines several utility classes that are likely -to be useful for Piccolo applications. These utility classes are -also used within the implementation of Piccolo. - diff --git a/core/src/main/java/org/piccolo2d/PCamera.java b/core/src/main/java/org/piccolo2d/PCamera.java new file mode 100644 index 0000000..f1911ac --- /dev/null +++ b/core/src/main/java/org/piccolo2d/PCamera.java @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.piccolo2d.activities.PTransformActivity; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDebug; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PObjectOutputStream; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PPickPath; +import org.piccolo2d.util.PUtil; + + +/** + * PCamera represents a viewport onto a list of layer nodes. Each camera + * maintains a view transform through which it views these layers. Translating + * and scaling this view transform is how zooming and panning are implemented. + *+ * Cameras are also the point through which all PInputEvents enter Piccolo. The + * canvas coordinate system and the local coordinate system of the topmost + * camera should always be the same. + *
+ * + * @see PLayer + * @version 1.0 + * @author Jesse Grosjean + */ +public class PCamera extends PNode { + + /** Default serial version UID. */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in the set of this camera's + * layers (see {@link #getLayer getLayer}, {@link #getLayerCount + * getLayerCount}, {@link #getLayersReference getLayersReference}). A + * property change event's new value will be a reference to the list of this + * nodes layers, but old value will always be null. + */ + public static final String PROPERTY_LAYERS = "layers"; + + /** + * The property code that identifies a change in the set of this camera's + * layers (see {@link #getLayer getLayer}, {@link #getLayerCount + * getLayerCount}, {@link #getLayersReference getLayersReference}). A + * property change event's new value will be a reference to the list of this + * nodes layers, but old value will always be null. + */ + public static final int PROPERTY_CODE_LAYERS = 1 << 11; + + /** + * The property name that identifies a change in this camera's view + * transform (see {@link #getViewTransform getViewTransform}, + * {@link #getViewTransformReference getViewTransformReference}). A property + * change event's new value will be a reference to the view transform, but + * old value will always be null. + */ + public static final String PROPERTY_VIEW_TRANSFORM = "viewTransform"; + + /** + * The property code that identifies a change in this camera's view + * transform (see {@link #getViewTransform getViewTransform}, + * {@link #getViewTransformReference getViewTransformReference}). A property + * change event's new value will be a reference to the view transform, but + * old value will always be null. + */ + public static final int PROPERTY_CODE_VIEW_TRANSFORM = 1 << 12; + + /** 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; + + /** Component which receives repaint notification from this camera. */ + private transient PComponent component; + + /** List of layers viewed by this camera. */ + private transient List/*null
if no
+ * component has been associated with this camera, as may be the case for
+ * internal cameras.
+ *
+ * @return the component for this camera, or null
if no such
+ * component exists
+ */
+ public PComponent getComponent() {
+ return component;
+ }
+
+ /**
+ * Set the component for this camera to component
. The
+ * component, if non-null, receives repaint notification from this camera.
+ *
+ * @param component component for this camera
+ */
+ public void setComponent(final PComponent component) {
+ this.component = component;
+ invalidatePaint();
+ }
+
+ /**
+ * Repaint this camera and forward the repaint request to the component
+ * for this camera, if it is non-null.
+ *
+ * @param localBounds bounds that require repainting, in local coordinates
+ * @param sourceNode node from which the repaint message originates, may
+ * be the camera itself
+ */
+ public void repaintFrom(final PBounds localBounds, final PNode sourceNode) {
+ if (getParent() != null) {
+ if (sourceNode != this) {
+ localToParent(localBounds);
+ }
+ if (component != null) {
+ component.repaint(localBounds);
+ }
+ getParent().repaintFrom(localBounds, this);
+ }
+ }
+
+ /**
+ * Repaint from one of the camera's layers. The repaint region needs to be
+ * transformed from view to local in this case. Unlike most repaint methods
+ * in Piccolo2D this one must not modify the viewBounds
+ * parameter.
+ *
+ * @since 1.3
+ * @param viewBounds bounds that require repainting, in view coordinates
+ * @param repaintedLayer layer dispatching the repaint notification
+ */
+ public void repaintFromLayer(final PBounds viewBounds, final PLayer repaintedLayer) {
+ TEMP_REPAINT_RECT.setRect(viewBounds);
+ viewToLocal(TEMP_REPAINT_RECT);
+ if (getBoundsReference().intersects(TEMP_REPAINT_RECT)) {
+ Rectangle2D.intersect(TEMP_REPAINT_RECT, getBoundsReference(), TEMP_REPAINT_RECT);
+ repaintFrom(TEMP_REPAINT_RECT, repaintedLayer);
+ }
+ }
+
+ /**
+ * Return a reference to the list of layers viewed by this camera.
+ *
+ * @return the list of layers viewed by this camera
+ */
+ public List/*index < 0 || index >= getLayerCount()
)
+ */
+ public PLayer getLayer(final int index) {
+ return (PLayer) layers.get(index);
+ }
+
+ /**
+ * Return the index of the first occurrence of the specified layer in the
+ * list of layers viewed by this camera, or -1
if the list of layers
+ * viewed by this camera does not contain the specified layer.
+ *
+ * @param layer layer to search for
+ * @return the index of the first occurrence of the specified layer in the
+ * list of layers viewed by this camera, or -1
if the list of
+ * layers viewed by this camera does not contain the specified layer
+ */
+ public int indexOfLayer(final PLayer layer) {
+ return layers.indexOf(layer);
+ }
+
+ /**
+ * Inserts the specified layer at the end of the list of layers viewed by this camera.
+ * Layers may be viewed by multiple cameras at once.
+ *
+ * @param layer layer to add
+ */
+ public void addLayer(final PLayer layer) {
+ addLayer(layers.size(), layer);
+ }
+
+ /**
+ * Inserts the specified layer at the specified position in the list of layers viewed by this camera.
+ * Layers may be viewed by multiple cameras at once.
+ *
+ * @param index index at which the specified layer is to be inserted
+ * @param layer layer to add
+ * @throws IndexOutOfBoundsException if the specified index is out of range
+ * (index < 0 || index >= getLayerCount()
)
+ */
+ public void addLayer(final int index, final PLayer layer) {
+ layers.add(index, layer);
+ layer.addCamera(this);
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers);
+ }
+
+ /**
+ * Removes the first occurrence of the specified layer from the list of
+ * layers viewed by this camera, if it is present.
+ *
+ * @param layer layer to be removed
+ * @return the specified layer
+ */
+ public PLayer removeLayer(final PLayer layer) {
+ layer.removeCamera(this);
+ if (layers.remove(layer)) {
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers);
+ }
+ return layer;
+ }
+
+ /**
+ * Removes the element at the specified position from the list of layers
+ * viewed by this camera.
+ *
+ * @param index index of the layer to remove
+ * @return the layer previously at the specified position
+ * @throws IndexOutOfBoundsException if the specified index is out of range
+ * (index < 0 || index >= getLayerCount()
)
+ */
+ public PLayer removeLayer(final int index) {
+ final PLayer layer = (PLayer) layers.remove(index);
+ layer.removeCamera(this);
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_LAYERS, PROPERTY_LAYERS, null, layers);
+ return layer;
+ }
+
+ /**
+ * Return the union of the full bounds of each layer in the list of layers
+ * viewed by this camera, or empty bounds if the list of layers viewed by
+ * this camera is empty.
+ *
+ * @return the union of the full bounds of each layer in the list of layers
+ * viewed by this camera, or empty bounds if the list of layers viewed
+ * by this camera is empty
+ */
+ public PBounds getUnionOfLayerFullBounds() {
+ final PBounds result = new PBounds();
+ final int size = layers.size();
+ for (int i = 0; i < size; i++) {
+ final PLayer each = (PLayer) layers.get(i);
+ result.add(each.getFullBoundsReference());
+ }
+ return result;
+ }
+
+ /**
+ * Paint this camera and then paint this camera's view through its view
+ * transform.
+ *
+ * @param paintContext context in which painting occurs
+ */
+ protected void paint(final PPaintContext paintContext) {
+ super.paint(paintContext);
+
+ paintContext.pushClip(getBoundsReference());
+ paintContext.pushTransform(viewTransform);
+
+ paintCameraView(paintContext);
+ paintDebugInfo(paintContext);
+
+ paintContext.popTransform(viewTransform);
+ paintContext.popClip(getBoundsReference());
+ }
+
+ /**
+ * Paint all the layers in the list of layers viewed by this camera. This method
+ * is called after the view transform and clip have been applied to the
+ * specified paint context.
+ *
+ * @param paintContext context in which painting occurs
+ */
+ protected void paintCameraView(final PPaintContext paintContext) {
+ final int size = layers.size();
+ for (int i = 0; i < size; i++) {
+ final PLayer each = (PLayer) layers.get(i);
+ each.fullPaint(paintContext);
+ }
+ }
+
+ /**
+ * Renders debug info onto the newly painted scene. Things like full bounds
+ * and bounds are painted as filled and outlines.
+ *
+ * @param paintContext context in which painting occurs
+ */
+ protected void paintDebugInfo(final PPaintContext paintContext) {
+ if (PDebug.debugBounds || PDebug.debugFullBounds) {
+ final Graphics2D g2 = paintContext.getGraphics();
+ paintContext.setRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
+ g2.setStroke(new BasicStroke(0));
+ final ArrayList nodes = new ArrayList();
+ final PBounds nodeBounds = new PBounds();
+
+ final Color boundsColor = Color.red;
+ final Color fullBoundsColor = new Color(1.0f, 0f, 0f, 0.2f);
+
+ final int size = layers.size();
+ for (int i = 0; i < size; i++) {
+ ((PLayer) layers.get(i)).getAllNodes(null, nodes);
+ }
+
+ final Iterator i = getAllNodes(null, nodes).iterator();
+
+ while (i.hasNext()) {
+ final PNode each = (PNode) i.next();
+
+ if (PDebug.debugBounds) {
+ g2.setPaint(boundsColor);
+ nodeBounds.setRect(each.getBoundsReference());
+
+ if (!nodeBounds.isEmpty()) {
+ each.localToGlobal(nodeBounds);
+ globalToLocal(nodeBounds);
+ if (each == this || each.isDescendentOf(this)) {
+ localToView(nodeBounds);
+ }
+ g2.draw(nodeBounds);
+ }
+ }
+
+ if (PDebug.debugFullBounds) {
+ g2.setPaint(fullBoundsColor);
+ nodeBounds.setRect(each.getFullBoundsReference());
+
+ if (!nodeBounds.isEmpty()) {
+ if (each.getParent() != null) {
+ each.getParent().localToGlobal(nodeBounds);
+ }
+ globalToLocal(nodeBounds);
+ if (each == this || each.isDescendentOf(this)) {
+ localToView(nodeBounds);
+ }
+ g2.fill(nodeBounds);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * + * Pushes this camera onto the specified paint context so that it + * can be accessed later by {@link PPaintContext#getCamera}. + *
+ */ + public void fullPaint(final PPaintContext paintContext) { + paintContext.pushCamera(this); + super.fullPaint(paintContext); + paintContext.popCamera(); + } + + /** + * Generate and return a PPickPath for the point x,y specified in the local + * coord system of this camera. Picking is done with a rectangle, halo + * specifies how large that rectangle will be. + * + * @param x the x coordinate of the pick path given in local coordinates + * @param y the y coordinate of the pick path given in local coordinates + * @param halo the distance from the x,y coordinate that is considered for + * inclusion in the pick path + * + * @return the picked path + */ + public PPickPath pick(final double x, final double y, final double halo) { + final PBounds b = new PBounds(new Point2D.Double(x, y), -halo, -halo); + final PPickPath result = new PPickPath(this, b); + + fullPick(result); + + // make sure this camera is pushed. + if (result.getNodeStackReference().size() == 0) { + result.pushNode(this); + result.pushTransform(getTransformReference(false)); + } + + return result; + } + + /** + * {@inheritDoc} + * + *+ * After the direct children of this camera have been given a chance to be + * picked all of the layers in the list of layers viewed by this camera are + * given a chance to be picked. + *
+ * + * @return true if any of the layers in the list of layers viewed by this + * camera were picked + */ + protected boolean pickAfterChildren(final PPickPath pickPath) { + if (intersects(pickPath.getPickBounds())) { + pickPath.pushTransform(viewTransform); + + if (pickCameraView(pickPath)) { + return true; + } + + pickPath.popTransform(viewTransform); + return true; + } + return false; + } + + /** + * Try to pick all of the layers in the list of layers viewed by this + * camera. This method is called after the view transform has been applied + * to the specified pick path. + * + * @param pickPath pick path + * @return true if any of the layers in the list of layers viewed by this + * camera were picked + */ + protected boolean pickCameraView(final PPickPath pickPath) { + final int size = layers.size(); + for (int i = size - 1; i >= 0; i--) { + final PLayer each = (PLayer) layers.get(i); + if (each.fullPick(pickPath)) { + return true; + } + } + return false; + } + + // **************************************************************** + // View Transform - Methods for accessing the view transform. The + // view transform is applied before painting and picking the cameras + // layers. But not before painting or picking its direct children. + // + // Changing the view transform is how zooming and panning are + // accomplished. + // **************************************************************** + + /** + * Return the bounds of this camera in the view coordinate system. + * + * @return the bounds of this camera in the view coordinate system + */ + public PBounds getViewBounds() { + return (PBounds) localToView(getBounds()); + } + + /** + * Animates the camera's view so that the given bounds (in camera layer's + * coordinate system) are centered within the cameras view bounds. Use this + * method to point the camera at a given location. + * + * @param centerBounds the targetBounds + */ + public void setViewBounds(final Rectangle2D centerBounds) { + animateViewToCenterBounds(centerBounds, true, 0); + } + + /** + * Return the scale applied by the view transform to the list of layers + * viewed by this camera. + * + * @return the scale applied by the view transform to the list of layers + * viewed by this camera + */ + public double getViewScale() { + return viewTransform.getScale(); + } + + /** + * Scale the view transform applied to the list of layers viewed by this + * camera byscale
about the point [0, 0]
.
+ *
+ * @param scale view transform scale
+ */
+ public void scaleView(final double scale) {
+ scaleViewAboutPoint(scale, 0, 0);
+ }
+
+ /**
+ * Scale the view transform applied to the list of layers viewed by this
+ * camera by scale
about the specified point
+ * [x, y]
.
+ *
+ * @param scale view transform scale
+ * @param x scale about point, x coordinate
+ * @param y scale about point, y coordinate
+ */
+ public void scaleViewAboutPoint(final double scale, final double x, final double y) {
+ viewTransform.scaleAboutPoint(scale, x, y);
+ applyViewConstraints();
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform);
+ }
+
+ /**
+ * Set the scale applied by the view transform to the list of layers
+ * viewed by this camera to scale
.
+ *
+ * @param scale view transform scale
+ */
+ public void setViewScale(final double scale) {
+ scaleView(scale / getViewScale());
+ }
+
+ /**
+ * Translate the view transform applied to the list of layers viewed by this
+ * camera by [dx, dy]
.
+ *
+ * @param dx translate delta x
+ * @param dy translate delta y
+ */
+ public void translateView(final double dx, final double dy) {
+ viewTransform.translate(dx, dy);
+ applyViewConstraints();
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform);
+ }
+
+ /**
+ * Offset the view transform applied to the list of layers viewed by this camera by [dx, dy]
. This is
+ * NOT effected by the view transform's current scale or rotation. This is implemented by directly adding dx to the
+ * m02 position and dy to the m12 position in the affine transform.
+ *
+ * @param dx offset delta x
+ * @param dy offset delta y
+ */
+ /*
+ public void offsetView(final double dx, final double dy) {
+ setViewOffset(viewTransform.getTranslateX() + dx, viewTransform.getTranslateY() + dy);
+ }
+ */
+
+ /**
+ * Set the offset for the view transform applied to the list of layers
+ * viewed by this camera to [x, y]
.
+ *
+ * @param x offset x
+ * @param y offset y
+ */
+ public void setViewOffset(final double x, final double y) {
+ viewTransform.setOffset(x, y);
+ applyViewConstraints();
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, viewTransform);
+ }
+
+ /**
+ * Return a copy of the view transform applied to the list of layers
+ * viewed by this camera.
+ *
+ * @return a copy of the view transform applied to the list of layers
+ * viewed by this camera
+ */
+ public PAffineTransform getViewTransform() {
+ return (PAffineTransform) viewTransform.clone();
+ }
+
+ /**
+ * Return a reference to the view transform applied to the list of layers
+ * viewed by this camera.
+ *
+ * @return the view transform applied to the list of layers
+ * viewed by this camera
+ */
+ public PAffineTransform getViewTransformReference() {
+ return viewTransform;
+ }
+
+ /**
+ * Set the view transform applied to the list of layers
+ * viewed by this camera to viewTransform
.
+ *
+ * @param viewTransform view transform applied to the list of layers
+ * viewed by this camera
+ */
+ public void setViewTransform(final AffineTransform viewTransform) {
+ this.viewTransform.setTransform(viewTransform);
+ applyViewConstraints();
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_VIEW_TRANSFORM, PROPERTY_VIEW_TRANSFORM, null, this.viewTransform);
+ }
+
+ /**
+ * Animate the camera's view from its current transform when the activity
+ * starts to a new transform that centers the given bounds in the camera
+ * layer's coordinate system into the cameras view bounds. If the duration is
+ * 0 then the view will be transformed immediately, and null will be
+ * returned. Else a new PTransformActivity will get returned that is set to
+ * animate the camera's view transform to the new bounds. If shouldScale is
+ * true, then the camera will also scale its view so that the given bounds
+ * fit fully within the cameras view bounds, else the camera will maintain
+ * its original scale.
+ *
+ * @param centerBounds the bounds which the animation will pace at the
+ * center of the view
+ * @param shouldScaleToFit whether the camera should scale the view while
+ * animating to it
+ * @param duration how many milliseconds the animations should take
+ *
+ * @return the scheduled PTransformActivity
+ */
+ public PTransformActivity animateViewToCenterBounds(final Rectangle2D centerBounds, final boolean shouldScaleToFit,
+ final long duration) {
+ final PBounds viewBounds = getViewBounds();
+ final PDimension delta = viewBounds.deltaRequiredToCenter(centerBounds);
+ final PAffineTransform newTransform = getViewTransform();
+ newTransform.translate(delta.width, delta.height);
+
+ if (shouldScaleToFit) {
+ final double s = Math.min(viewBounds.getWidth() / centerBounds.getWidth(), viewBounds.getHeight()
+ / centerBounds.getHeight());
+ if (s != Double.POSITIVE_INFINITY && s != 0) {
+ newTransform.scaleAboutPoint(s, centerBounds.getCenterX(), centerBounds.getCenterY());
+ }
+ }
+
+ return animateViewToTransform(newTransform, duration);
+ }
+
+ /**
+ * Pan the camera's view from its current transform when the activity starts
+ * to a new transform so that the view bounds will contain (if possible,
+ * intersect if not possible) the new bounds in the camera layers coordinate
+ * system. If the duration is 0 then the view will be transformed
+ * immediately, and null will be returned. Else a new PTransformActivity
+ * will get returned that is set to animate the camera's view transform to
+ * the new bounds.
+ *
+ * @param panToBounds the bounds to which the view will animate to
+ * @param duration the duration of the animation given in milliseconds
+ *
+ * @return the scheduled PTransformActivity
+ */
+ public PTransformActivity animateViewToPanToBounds(final Rectangle2D panToBounds, final long duration) {
+ final PBounds viewBounds = getViewBounds();
+ final PDimension delta = viewBounds.deltaRequiredToContain(panToBounds);
+
+ if (delta.width != 0 || delta.height != 0) {
+ if (duration == 0) {
+ translateView(-delta.width, -delta.height);
+ }
+ else {
+ final AffineTransform at = getViewTransform();
+ at.translate(-delta.width, -delta.height);
+ return animateViewToTransform(at, duration);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Animate the cameras view transform from its current value when the
+ * activity starts to the new destination transform value.
+ *
+ * @param destination the transform to which the view should be transformed
+ * into
+ * @param duration the duraiton in milliseconds the animation should take
+ *
+ * @return the scheduled PTransformActivity
+ */
+ public PTransformActivity animateViewToTransform(final AffineTransform destination, final long duration) {
+ if (duration == 0) {
+ setViewTransform(destination);
+ return null;
+ }
+
+ final PTransformActivity.Target t = new PTransformActivity.Target() {
+ /** {@inheritDoc} */
+ public void setTransform(final AffineTransform aTransform) {
+ PCamera.this.setViewTransform(aTransform);
+ }
+
+ /** {@inheritDoc} */
+ public void getSourceMatrix(final double[] aSource) {
+ viewTransform.getMatrix(aSource);
+ }
+ };
+
+ final PTransformActivity transformActivity = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE,
+ t, destination);
+
+ final PRoot r = getRoot();
+ if (r != null) {
+ r.getActivityScheduler().addActivity(transformActivity);
+ }
+
+ return transformActivity;
+ }
+
+ // ****************************************************************
+ // View Transform Constraints - Methods for setting and applying
+ // constraints to the view transform.
+ // ****************************************************************
+
+ /**
+ * Return the constraint applied to the view. The view constraint will be one of {@link #VIEW_CONSTRAINT_NONE},
+ * {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}. Defaults to {@link #VIEW_CONSTRAINT_NONE}.
+ *
+ * @return the view constraint being applied to the view
+ */
+ public int getViewConstraint() {
+ return viewConstraint;
+ }
+
+ /**
+ * Set the view constraint to apply to the view to viewConstraint
. The view constraint must be one of
+ * {@link #VIEW_CONSTRAINT_NONE}, {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}.
+ *
+ * @param viewConstraint constraint to apply to the view
+ * @throws IllegalArgumentException if viewConstraint
is not one of {@link #VIEW_CONSTRAINT_NONE},
+ * {@link #VIEW_CONSTRAINT_CENTER}, or {@link #VIEW_CONSTRAINT_CENTER}
+ */
+ public void setViewConstraint(final int viewConstraint) {
+ if (viewConstraint != VIEW_CONSTRAINT_NONE && viewConstraint != VIEW_CONSTRAINT_CENTER
+ && viewConstraint != VIEW_CONSTRAINT_ALL) {
+ throw new IllegalArgumentException("view constraint must be one "
+ + "of VIEW_CONSTRAINT_NONE, VIEW_CONSTRAINT_CENTER, or VIEW_CONSTRAINT_ALL");
+ }
+ this.viewConstraint = viewConstraint;
+ applyViewConstraints();
+ }
+
+ /**
+ * Transforms the view so that it conforms to the given constraint.
+ */
+ protected void applyViewConstraints() {
+ if (VIEW_CONSTRAINT_NONE == viewConstraint) {
+ return;
+ }
+ final PBounds viewBounds = getViewBounds();
+ final PBounds layerBounds = (PBounds) globalToLocal(getUnionOfLayerFullBounds());
+
+ if (VIEW_CONSTRAINT_CENTER == viewConstraint) {
+ layerBounds.setRect(layerBounds.getCenterX(), layerBounds.getCenterY(), 0, 0);
+ }
+ PDimension constraintDelta = viewBounds.deltaRequiredToContain(layerBounds);
+ viewTransform.translate(-constraintDelta.width, -constraintDelta.height);
+ }
+
+ // ****************************************************************
+ // Camera View Coord System Conversions - Methods to translate from
+ // the camera's local coord system (above the camera's view transform) to
+ // the
+ // camera view coord system (below the camera's view transform). When
+ // converting geometry from one of the canvas's layers you must go
+ // through the view transform.
+ // ****************************************************************
+
+ /**
+ * Convert the point from the camera's view coordinate system to the
+ * camera's local coordinate system. The given point is modified by this.
+ *
+ * @param viewPoint the point to transform to the local coordinate system
+ * from the view's coordinate system
+ * @return the transformed point
+ */
+ public Point2D viewToLocal(final Point2D viewPoint) {
+ return viewTransform.transform(viewPoint, viewPoint);
+ }
+
+ /**
+ * Convert the dimension from the camera's view coordinate system to the
+ * camera's local coordinate system. The given dimension is modified by
+ * this.
+ *
+ * @param viewDimension the dimension to transform from the view system to
+ * the local coordinate system
+ *
+ * @return returns the transformed dimension
+ */
+ public Dimension2D viewToLocal(final Dimension2D viewDimension) {
+ return viewTransform.transform(viewDimension, viewDimension);
+ }
+
+ /**
+ * Convert the rectangle from the camera's view coordinate system to the
+ * camera's local coordinate system. The given rectangle is modified by this
+ * method.
+ *
+ * @param viewRectangle the rectangle to transform from view to local
+ * coordinate System
+ * @return the transformed rectangle
+ */
+ public Rectangle2D viewToLocal(final Rectangle2D viewRectangle) {
+ return viewTransform.transform(viewRectangle, viewRectangle);
+ }
+
+ /**
+ * Convert the point from the camera's local coordinate system to the
+ * camera's view coordinate system. The given point is modified by this
+ * method.
+ *
+ * @param localPoint point to transform from local to view coordinate system
+ * @return the transformed point
+ */
+ public Point2D localToView(final Point2D localPoint) {
+ return viewTransform.inverseTransform(localPoint, localPoint);
+ }
+
+ /**
+ * Convert the dimension from the camera's local coordinate system to the
+ * camera's view coordinate system. The given dimension is modified by this
+ * method.
+ *
+ * @param localDimension the dimension to transform from local to view
+ * coordinate systems
+ * @return the transformed dimension
+ */
+ public Dimension2D localToView(final Dimension2D localDimension) {
+ return viewTransform.inverseTransform(localDimension, localDimension);
+ }
+
+ /**
+ * Convert the rectangle from the camera's local coordinate system to the
+ * camera's view coordinate system. The given rectangle is modified by this
+ * method.
+ *
+ * @param localRectangle the rectangle to transform from local to view
+ * coordinate system
+ * @return the transformed rectangle
+ */
+ public Rectangle2D localToView(final Rectangle2D localRectangle) {
+ return viewTransform.inverseTransform(localRectangle, localRectangle);
+ }
+
+ // ****************************************************************
+ // Serialization - Cameras conditionally serialize their layers.
+ // This means that only the layer references that were unconditionally
+ // (using writeObject) serialized by someone else will be restored
+ // when the camera is unserialized.
+ // ****************************************************************/
+
+ /**
+ * Write this camera and all its children out to the given stream. Note that
+ * the cameras layers are written conditionally, so they will only get
+ * written out if someone else writes them unconditionally.
+ *
+ * @param out the PObjectOutputStream to which this camera should be
+ * serialized
+ * @throws IOException if an error occured writing to the output stream
+ */
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ if (!(out instanceof PObjectOutputStream)) {
+ throw new RuntimeException("cannot serialize PCamera to a non PObjectOutputStream");
+ }
+ out.defaultWriteObject();
+
+ final int count = getLayerCount();
+ for (int i = 0; i < count; i++) {
+ ((PObjectOutputStream) out).writeConditionalObject(layers.get(i));
+ }
+
+ out.writeObject(Boolean.FALSE);
+ ((PObjectOutputStream) out).writeConditionalObject(component);
+ }
+
+ /**
+ * Deserializes this PCamera from the ObjectInputStream.
+ *
+ * @param in the source ObjectInputStream
+ * @throws IOException when error occurs during read
+ * @throws ClassNotFoundException if the stream attempts to deserialize a
+ * missing class
+ */
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ layers = new ArrayList();
+
+ while (true) {
+ final Object each = in.readObject();
+ if (each != null) {
+ if (each.equals(Boolean.FALSE)) {
+ break;
+ }
+ else {
+ layers.add(each);
+ }
+ }
+ }
+
+ component = (PComponent) in.readObject();
+ }
+}
diff --git a/core/src/main/java/org/piccolo2d/PCanvas.java b/core/src/main/java/org/piccolo2d/PCanvas.java
new file mode 100644
index 0000000..d614beb
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/PCanvas.java
@@ -0,0 +1,908 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.KeyEventPostProcessor;
+import java.awt.KeyboardFocusManager;
+import java.awt.event.ActionListener;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.HierarchyListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+
+import javax.swing.FocusManager;
+import javax.swing.JComponent;
+import javax.swing.RepaintManager;
+import javax.swing.Timer;
+
+import org.piccolo2d.event.PInputEventListener;
+import org.piccolo2d.event.PPanEventHandler;
+import org.piccolo2d.event.PZoomEventHandler;
+import org.piccolo2d.util.PBounds;
+import org.piccolo2d.util.PDebug;
+import org.piccolo2d.util.PPaintContext;
+import org.piccolo2d.util.PStack;
+import org.piccolo2d.util.PUtil;
+
+
+/**
+ * PCanvas is a simple Swing component that can be used to embed Piccolo
+ * into a Java Swing application. Canvases view the Piccolo scene graph through
+ * a camera. The canvas manages screen updates coming from this camera, and
+ * forwards swing mouse and keyboard events to the camera.
+ * + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PCanvas extends JComponent implements PComponent { + /** + * Allows for future serialization code to understand versioned binary + * formats. + */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in the interacting state. + * + * @since 1.3 + */ + public static final String PROPERTY_INTERACTING = "INTERACTING_CHANGED_NOTIFICATION"; + + /** The camera though which this Canvas is viewing. */ + private PCamera camera; + + /** + * Stack of cursors used to keep track of cursors as they change through + * interactions. + */ + private final PStack cursorStack; + + /** + * Whether the canvas is considered to be interacting, will probably mean + * worse render quality. + */ + private int interacting; + /** + * The render quality to use when the scene is not being interacted or + * animated. + */ + private int normalRenderQuality; + + /** The quality to use while the scene is being animated. */ + private int animatingRenderQuality; + + /** The quality to use while the scene is being interacted with. */ + private int interactingRenderQuality; + + /** The one and only pan handler. */ + private transient PPanEventHandler panEventHandler; + + /** The one and only ZoomEventHandler. */ + private transient PZoomEventHandler zoomEventHandler; + + private boolean paintingImmediately; + + /** Used to track whether the last paint operation was during an animation. */ + private boolean animatingOnLastPaint; + + /** The mouse listener that is registered for large scale mouse events. */ + private transient MouseListener mouseListener; + + /** Remembers the key processor. */ + private transient KeyEventPostProcessor keyEventPostProcessor; + + /** The mouse wheel listeners that's registered to receive wheel events. */ + private transient MouseWheelListener mouseWheelListener; + /** + * The mouse listener that is registered to receive small scale mouse events + * (like motion). + */ + private transient MouseMotionListener mouseMotionListener; + + private static final int ALL_BUTTONS_MASK = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK + | InputEvent.BUTTON3_DOWN_MASK; + + /** + * Construct a canvas with the basic scene graph consisting of a root, + * camera, and layer. Zooming and panning are automatically installed. + */ + public PCanvas() { + cursorStack = new PStack(); + setCamera(createDefaultCamera()); + setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + setAnimatingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + setInteractingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + setPanEventHandler(new PPanEventHandler()); + setZoomEventHandler(new PZoomEventHandler()); + setBackground(Color.WHITE); + setOpaque(true); + + addHierarchyListener(new HierarchyListener() { + public void hierarchyChanged(final HierarchyEvent e) { + if (e.getComponent() == PCanvas.this) { + if (getParent() == null) { + removeInputSources(); + } + else if (isEnabled()) { + installInputSources(); + } + } + } + }); + } + + /** + * Creates and returns a basic Scene Graph. + * + * @return a built PCamera scene + */ + protected PCamera createDefaultCamera() { + return PUtil.createBasicScenegraph(); + } + + // **************************************************************** + // Basic - Methods for accessing common piccolo nodes. + // **************************************************************** + + /** + * Get the pan event handler associated with this canvas. This event handler + * is set up to get events from the camera associated with this canvas by + * default. + * + * @return the current pan event handler, may be null + */ + public PPanEventHandler getPanEventHandler() { + return panEventHandler; + } + + /** + * Set the pan event handler associated with this canvas. + * + * @param handler the new zoom event handler + */ + public void setPanEventHandler(final PPanEventHandler handler) { + if (panEventHandler != null) { + removeInputEventListener(panEventHandler); + } + + panEventHandler = handler; + + if (panEventHandler != null) { + addInputEventListener(panEventHandler); + } + } + + /** + * Get the zoom event handler associated with this canvas. This event + * handler is set up to get events from the camera associated with this + * canvas by default. + * + * @return the current zoom event handler, may be null + */ + public PZoomEventHandler getZoomEventHandler() { + return zoomEventHandler; + } + + /** + * Set the zoom event handler associated with this canvas. + * + * @param handler the new zoom event handler + */ + public void setZoomEventHandler(final PZoomEventHandler handler) { + if (zoomEventHandler != null) { + removeInputEventListener(zoomEventHandler); + } + + zoomEventHandler = handler; + + if (zoomEventHandler != null) { + addInputEventListener(zoomEventHandler); + } + } + + /** + * Return the camera associated with this canvas. All input events from this + * canvas go through this camera. And this is the camera that paints this + * canvas. + * + * @return camera through which this PCanvas views the scene + */ + public PCamera getCamera() { + return camera; + } + + /** + * Set the camera associated with this canvas. All input events from this + * canvas go through this camera. And this is the camera that paints this + * canvas. + * + * @param newCamera the camera which this PCanvas should view the scene + */ + public void setCamera(final PCamera newCamera) { + if (camera != null) { + camera.setComponent(null); + } + + camera = newCamera; + + if (camera != null) { + camera.setComponent(this); + camera.setBounds(getBounds()); + } + } + + /** + * Return root for this canvas. + * + * @return the root PNode at the "bottom" of the scene + */ + public PRoot getRoot() { + return camera.getRoot(); + } + + /** + * Return layer for this canvas. + * + * @return the first layer attached to this camera + */ + public PLayer getLayer() { + return camera.getLayer(0); + } + + /** + * Add an input listener to the camera associated with this canvas. + * + * @param listener listener to register for event notifications + */ + public void addInputEventListener(final PInputEventListener listener) { + getCamera().addInputEventListener(listener); + } + + /** + * Remove an input listener to the camera associated with this canvas. + * + * @param listener listener to unregister from event notifications + */ + public void removeInputEventListener(final PInputEventListener listener) { + getCamera().removeInputEventListener(listener); + } + + // **************************************************************** + // Painting + // **************************************************************** + + /** + * Return true if this canvas has been marked as interacting, or whether + * it's root is interacting. If so the canvas will normally render at a + * lower quality that is faster. + * + * @return whether the canvas has been flagged as being interacted with + */ + public boolean getInteracting() { + return interacting > 0 || getRoot().getInteracting(); + } + + /** + * Return true if any activities that respond with true to the method + * isAnimating were run in the last PRoot.processInputs() loop. This values + * is used by this canvas to determine the render quality to use for the + * next paint. + * + * @return whether the PCanvas is currently being animated + */ + public boolean getAnimating() { + return getRoot().getActivityScheduler().getAnimating(); + } + + /** + * Set if this canvas is interacting. If so the canvas will normally render + * at a lower quality that is faster. Also repaints the canvas if the render + * quality should change. + * + * @param isInteracting whether the PCanvas should be considered interacting + */ + public void setInteracting(final boolean isInteracting) { + final boolean wasInteracting = getInteracting(); + + if (isInteracting) { + interacting++; + } + else { + interacting--; + } + + if (!getInteracting()) { // determine next render quality and repaint if + // it's greater then the old + // interacting render quality. + int nextRenderQuality = normalRenderQuality; + if (getAnimating()) { + nextRenderQuality = animatingRenderQuality; + } + if (nextRenderQuality > interactingRenderQuality) { + repaint(); + } + } + + final boolean newInteracting = getInteracting(); + + if (wasInteracting != newInteracting) { + firePropertyChange(PROPERTY_INTERACTING, wasInteracting, newInteracting); + } + } + + /** + * Set the render quality that should be used when rendering this canvas + * when it is not interacting or animating. The default value is + * PPaintContext. HIGH_QUALITY_RENDERING. + * + * @param defaultRenderQuality supports PPaintContext.HIGH_QUALITY_RENDERING + * or PPaintContext.LOW_QUALITY_RENDERING + */ + public void setDefaultRenderQuality(final int defaultRenderQuality) { + this.normalRenderQuality = defaultRenderQuality; + repaint(); + } + + /** + * Set the render quality that should be used when rendering this canvas + * when it is animating. The default value is + * PPaintContext.LOW_QUALITY_RENDERING. + * + * @param animatingRenderQuality supports + * PPaintContext.HIGH_QUALITY_RENDERING or + * PPaintContext.LOW_QUALITY_RENDERING + */ + public void setAnimatingRenderQuality(final int animatingRenderQuality) { + this.animatingRenderQuality = animatingRenderQuality; + if (getAnimating()) { + repaint(); + } + } + + /** + * Set the render quality that should be used when rendering this canvas + * when it is interacting. The default value is + * PPaintContext.LOW_QUALITY_RENDERING. + * + * @param interactingRenderQuality supports + * PPaintContext.HIGH_QUALITY_RENDERING or + * PPaintContext.LOW_QUALITY_RENDERING + */ + public void setInteractingRenderQuality(final int interactingRenderQuality) { + this.interactingRenderQuality = interactingRenderQuality; + if (getInteracting()) { + repaint(); + } + } + + /** + * Set the canvas cursor, and remember the previous cursor on the cursor + * stack. + * + * @param cursor the cursor to push onto the cursor stack + */ + public void pushCursor(final Cursor cursor) { + cursorStack.push(getCursor()); + setCursor(cursor); + } + + /** + * Pop the cursor on top of the cursorStack and set it as the canvas cursor. + */ + public void popCursor() { + if (!cursorStack.isEmpty()) { + setCursor((Cursor) cursorStack.pop()); + } + } + + // **************************************************************** + // Code to manage connection to Swing. There appears to be a bug in + // swing where it will occasionally send too many mouse pressed or mouse + // released events. Below we attempt to filter out those cases before + // they get delivered to the Piccolo framework. + // **************************************************************** + + /** + * Tracks whether button1 of the mouse is down. + */ + private boolean isButton1Pressed; + /** + * Tracks whether button2 of the mouse is down. + */ + private boolean isButton2Pressed; + /** + * Tracks whether button3 of the mouse is down. + */ + private boolean isButton3Pressed; + + /** + * Override setEnabled to install/remove canvas input sources as needed. + * + * @param enabled new enable status of the Pcanvas + */ + public void setEnabled(final boolean enabled) { + super.setEnabled(enabled); + + if (isEnabled() && getParent() != null) { + installInputSources(); + } + else { + removeInputSources(); + } + } + + /** + * This method installs mouse and key listeners on the canvas that forward + * those events to piccolo. + */ + protected void installInputSources() { + if (mouseListener == null) { + mouseListener = new MouseEventInputSource(); + addMouseListener(mouseListener); + } + + if (mouseMotionListener == null) { + mouseMotionListener = new MouseMotionInputSourceListener(); + addMouseMotionListener(mouseMotionListener); + } + + if (mouseWheelListener == null) { + mouseWheelListener = new MouseWheelInputSourceListener(); + addMouseWheelListener(mouseWheelListener); + } + + if (keyEventPostProcessor == null) { + keyEventPostProcessor = new KeyEventInputSourceListener(); + KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(keyEventPostProcessor); + } + } + + /** + * This method removes mouse and key listeners on the canvas that forward + * those events to piccolo. + */ + protected void removeInputSources() { + removeMouseListener(mouseListener); + removeMouseMotionListener(mouseMotionListener); + removeMouseWheelListener(mouseWheelListener); + KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(keyEventPostProcessor); + + mouseListener = null; + mouseMotionListener = null; + mouseWheelListener = null; + keyEventPostProcessor = null; + } + + /** + * Sends the given input event with the given type to the current + * InputManager. + * + * @param event event to dispatch + * @param type type of event being dispatched + */ + protected void sendInputEventToInputManager(final InputEvent event, final int type) { + getRoot().getDefaultInputManager().processEventFromCamera(event, type, getCamera()); + } + + /** + * Updates the bounds of the component and updates the camera accordingly. + * + * @param x left of bounds + * @param y top of bounds + * @param width width of bounds + * @param height height of bounds + */ + public void setBounds(final int x, final int y, final int width, final int height) { + camera.setBounds(camera.getX(), camera.getY(), width, height); + super.setBounds(x, y, width, height); + } + + /** + * {@inheritDoc} + */ + public void repaint(final PBounds bounds) { + PDebug.processRepaint(); + + bounds.expandNearestIntegerDimensions(); + bounds.inset(-1, -1); + + repaint((int) bounds.x, (int) bounds.y, (int) bounds.width, (int) bounds.height); + } + + private PBounds repaintBounds = new PBounds(); + + /** + * {@inheritDoc} + */ + public void paintComponent(final Graphics g) { + PDebug.startProcessingOutput(); + + final Graphics2D g2 = (Graphics2D) g.create(); + + // support for non-opaque canvases + // see + // http://groups.google.com/group/piccolo2d-dev/browse_thread/thread/134e2792d3a54cf + if (isOpaque()) { + g2.setColor(getBackground()); + g2.fillRect(0, 0, getWidth(), getHeight()); + } + + if (getAnimating()) { + repaintBounds.add(g2.getClipBounds()); + } + + // create new paint context and set render quality to lowest common + // denominator render quality. + final PPaintContext paintContext = new PPaintContext(g2); + if (getInteracting() || getAnimating()) { + if (interactingRenderQuality < animatingRenderQuality) { + paintContext.setRenderQuality(interactingRenderQuality); + } + else { + paintContext.setRenderQuality(animatingRenderQuality); + } + } + else { + paintContext.setRenderQuality(normalRenderQuality); + } + + camera.fullPaint(paintContext); + + // if switched state from animating to not animating invalidate the + // repaint bounds so that it will be drawn with the default instead of + // animating render quality. + if (!getAnimating() && animatingOnLastPaint) { + repaint(repaintBounds); + repaintBounds.reset(); + } + + animatingOnLastPaint = getAnimating(); + + PDebug.endProcessingOutput(g2); + } + + /** + * If not painting immediately, send paint notification to RepaintManager, + * otherwise does nothing. + */ + public void paintImmediately() { + if (paintingImmediately) { + return; + } + + paintingImmediately = true; + RepaintManager.currentManager(this).paintDirtyRegions(); + paintingImmediately = false; + } + + /** + * Helper for creating a timer. It's an extension point for subclasses to + * install their own timers. + * + * @param delay the number of milliseconds to wait before invoking the + * listener + * @param listener the listener to invoke after the delay + * + * @return the created Timer + */ + public Timer createTimer(final int delay, final ActionListener listener) { + return new Timer(delay, listener); + } + + /** + * Returns the quality to use when not animating or interacting. + * + * @since 1.3 + * @return the render quality to use when not animating or interacting + */ + public int getNormalRenderQuality() { + return normalRenderQuality; + } + + /** + * Returns the quality to use when animating. + * + * @since 1.3 + * @return Returns the quality to use when animating + */ + public int getAnimatingRenderQuality() { + return animatingRenderQuality; + } + + /** + * Returns the quality to use when interacting. + * + * @since 1.3 + * @return Returns the quality to use when interacting + */ + public int getInteractingRenderQuality() { + return interactingRenderQuality; + } + + /** + * Returns the input event listeners registered to receive input events. + * + * @since 1.3 + * @return array or input event listeners + */ + public PInputEventListener[] getInputEventListeners() { + return camera.getInputEventListeners(); + } + + /** + * Prints the entire scene regardless of what the viewable area is. + * + * @param graphics Graphics context onto which to paint the scene for printing + */ + public void printAll(final Graphics graphics) { + if (!(graphics instanceof Graphics2D)) { + throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); + } + + final Graphics2D g2 = (Graphics2D) graphics; + + final PBounds clippingRect = new PBounds(graphics.getClipBounds()); + clippingRect.expandNearestIntegerDimensions(); + + final PBounds originalCameraBounds = getCamera().getBounds(); + final PBounds layerBounds = getCamera().getUnionOfLayerFullBounds(); + getCamera().setBounds(layerBounds); + + final double clipRatio = clippingRect.getWidth() / clippingRect.getHeight(); + final double nodeRatio = ((double) getWidth()) / ((double) getHeight()); + final double scale; + if (nodeRatio <= clipRatio) { + scale = clippingRect.getHeight() / getCamera().getHeight(); + } + else { + scale = clippingRect.getWidth() / getCamera().getWidth(); + } + g2.scale(scale, scale); + g2.translate(-clippingRect.x, -clippingRect.y); + + final PPaintContext pc = new PPaintContext(g2); + pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + getCamera().fullPaint(pc); + + 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 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); + } + } + + private boolean isAnyButtonDown(final MouseEvent e) { + return (e.getModifiersEx() & ALL_BUTTONS_MASK) != 0; + } + + /** + * 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; + } + } + + /** + * Class responsible for sending mouse events to the the InputManager. + */ + 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/org/piccolo2d/PComponent.java b/core/src/main/java/org/piccolo2d/PComponent.java new file mode 100644 index 0000000..69ea705 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/PComponent.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.Cursor; + +import org.piccolo2d.util.PBounds; + + +/** + * Interface that a component needs to implement if it wants to act as a Piccolo + * canvas. + * + * @version 1.0 + * @author Lance Good + */ +public interface PComponent { + + /** + * Called to notify PComponent that given bounds need repainting. + * + * @param bounds bounds needing repaint + */ + void repaint(PBounds bounds); + + /** + * Sends a repaint notification the repaint manager if PComponent is not + * already painting immediately. + */ + void paintImmediately(); + + /** + * Pushes the given cursor onto the cursor stack and sets the current cursor + * to the one provided. + * + * @param cursor The cursor to set as the current one and push + */ + void pushCursor(Cursor cursor); + + /** + * Pops the topmost cursor from the stack and sets it as the current one. + */ + void popCursor(); + + /** + * Sets whether the component is currently being interacted with. + * + * @param interacting whether the component is currently being interacted + * with + */ + void setInteracting(boolean interacting); +} diff --git a/core/src/main/java/org/piccolo2d/PInputManager.java b/core/src/main/java/org/piccolo2d/PInputManager.java new file mode 100644 index 0000000..551e412 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/PInputManager.java @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; + +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.util.PPickPath; + + +/** + * PInputManager is responsible for dispatching PInputEvents to node's + * event listeners. Events are dispatched from PRoot's processInputs method. + *
+ * + * @see org.piccolo2d.event.PInputEvent + * @see PRoot + * @version 1.0 + * @author Jesse Grosjean + */ +public class PInputManager extends PBasicInputEventHandler implements PRoot.InputSource { + + /** Records the last known mouse position on the canvas. */ + private final Point2D lastCanvasPosition; + + /** Records the current known mouse position on the canvas. */ + private final Point2D currentCanvasPosition; + + /** The next InputEvent that needs to be processed. */ + private InputEvent nextInput; + + /** The type of the next InputEvent that needs to be processed. */ + private int nextType; + + /** The Input Source the next event to process came from. */ + private PCamera nextInputSource; + + /** The current mouse focus. */ + private PPickPath mouseFocus; + + /** The previous mouse focus. */ + private PPickPath previousMouseFocus; + + /** Tracks where the mouse is right now on the canvas. */ + private PPickPath mouseOver; + + /** Tracks the previous location of the mouse on the canvas. */ + private PPickPath previousMouseOver; + + /** Tracks the input event listener that should receive keyboard events. */ + private PInputEventListener keyboardFocus; + + /** Tracks the number mouse buttons currently pressed. */ + private int buttonsPressed; + + /** + * Creates a PInputManager and sets positions (last, current) to the origin + * (0,0). + */ + public PInputManager() { + lastCanvasPosition = new Point2D.Double(); + currentCanvasPosition = new Point2D.Double(); + } + + /** + * Return the node that currently has the keyboard focus. This node receives + * the key events. + * + * @return the current keyboard focus + */ + public PInputEventListener getKeyboardFocus() { + return keyboardFocus; + } + + /** + * Set the node that should receive key events. + * + * @param eventHandler sets the keyboard event focus, may be null + */ + public void setKeyboardFocus(final PInputEventListener eventHandler) { + final PInputEvent focusEvent = new PInputEvent(this, null); + + if (keyboardFocus != null) { + dispatchEventToListener(focusEvent, FocusEvent.FOCUS_LOST, keyboardFocus); + } + + keyboardFocus = eventHandler; + + if (keyboardFocus != null) { + dispatchEventToListener(focusEvent, FocusEvent.FOCUS_GAINED, keyboardFocus); + } + } + + /** + * Return the current Pick Path under the mouse focus. This will return the + * path that received the current mouse pressed event, or null if the mouse + * is not pressed. The mouse focus gets mouse dragged events even what the + * mouse is not over the mouse focus. + * + * @return the current Pick Path under the mouse focus + */ + public PPickPath getMouseFocus() { + return mouseFocus; + } + + /** + * Sets the current Pick Path under the mouse focus. The mouse focus gets + * mouse dragged events even when the mouse is not over the mouse focus. + * + * @param path the new mouse focus + */ + public void setMouseFocus(final PPickPath path) { + previousMouseFocus = mouseFocus; + mouseFocus = path; + } + + /** + * Return the node the the mouse is currently over. + * + * @return the path over which the mouse currently is + */ + public PPickPath getMouseOver() { + return mouseOver; + } + + /** + * Records the path which is directly below the mouse. + * + * @param path path over which the mouse has been moved + */ + public void setMouseOver(final PPickPath path) { + mouseOver = path; + } + + /** + * Returns the position on the Canvas of the last event. + * + * @return position of last canvas event + */ + public Point2D getLastCanvasPosition() { + return lastCanvasPosition; + } + + /** + * Returns the position of the current canvas event. + * + * @return position of current canvas event + */ + public Point2D getCurrentCanvasPosition() { + return currentCanvasPosition; + } + + // **************************************************************** + // Event Handling - Methods for handling events + // + // The dispatch manager updates the focus nodes based on the + // incoming events, and dispatches those events to the appropriate + // focus nodes. + // **************************************************************** + + /** {@inheritDoc} */ + public void keyPressed(final PInputEvent event) { + dispatchEventToListener(event, KeyEvent.KEY_PRESSED, keyboardFocus); + } + + /** {@inheritDoc} */ + public void keyReleased(final PInputEvent event) { + dispatchEventToListener(event, KeyEvent.KEY_RELEASED, keyboardFocus); + } + + /** {@inheritDoc} */ + public void keyTyped(final PInputEvent event) { + dispatchEventToListener(event, KeyEvent.KEY_TYPED, keyboardFocus); + } + + /** {@inheritDoc} */ + public void mouseClicked(final PInputEvent event) { + dispatchEventToListener(event, MouseEvent.MOUSE_CLICKED, previousMouseFocus); + } + + /** {@inheritDoc} */ + public void mouseWheelRotated(final PInputEvent event) { + setMouseFocus(getMouseOver()); + dispatchEventToListener(event, MouseWheelEvent.WHEEL_UNIT_SCROLL, mouseOver); + } + + /** {@inheritDoc} */ + public void mouseWheelRotatedByBlock(final PInputEvent event) { + setMouseFocus(getMouseOver()); + dispatchEventToListener(event, MouseWheelEvent.WHEEL_BLOCK_SCROLL, mouseOver); + } + + /** {@inheritDoc} */ + public void mouseDragged(final PInputEvent event) { + checkForMouseEnteredAndExited(event); + dispatchEventToListener(event, MouseEvent.MOUSE_DRAGGED, mouseFocus); + } + + /** {@inheritDoc} */ + public void mouseEntered(final PInputEvent event) { + dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); + } + + /** {@inheritDoc} */ + public void mouseExited(final PInputEvent event) { + dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); + } + + /** {@inheritDoc} */ + public void mouseMoved(final PInputEvent event) { + checkForMouseEnteredAndExited(event); + dispatchEventToListener(event, MouseEvent.MOUSE_MOVED, mouseOver); + } + + /** {@inheritDoc} */ + public void mousePressed(final PInputEvent event) { + if (buttonsPressed == 0) { + setMouseFocus(getMouseOver()); + } + buttonsPressed++; + dispatchEventToListener(event, MouseEvent.MOUSE_PRESSED, mouseFocus); + if (buttonsPressed < 1 || buttonsPressed > 3) { + System.err.println("invalid pressedCount on mouse pressed: " + buttonsPressed); + } + } + + /** {@inheritDoc} */ + public void mouseReleased(final PInputEvent event) { + buttonsPressed--; + checkForMouseEnteredAndExited(event); + dispatchEventToListener(event, MouseEvent.MOUSE_RELEASED, mouseFocus); + if (buttonsPressed == 0) { + setMouseFocus(null); + } + if (buttonsPressed < 0 || buttonsPressed > 2) { + System.err.println("invalid pressedCount on mouse released: " + buttonsPressed); + } + } + + /** + * Fires events whenever the mouse moves from PNode to PNode. + * + * @param event to check to see if the top node has changed. + */ + protected void checkForMouseEnteredAndExited(final PInputEvent event) { + final PNode currentNode = getPickedNode(mouseOver); + final PNode previousNode = getPickedNode(previousMouseOver); + + if (currentNode != previousNode) { + dispatchEventToListener(event, MouseEvent.MOUSE_EXITED, previousMouseOver); + dispatchEventToListener(event, MouseEvent.MOUSE_ENTERED, mouseOver); + previousMouseOver = mouseOver; + } + } + + /** + * Returns picked node on pickPath if pickPath is not null, or null. + * + * @param pickPath from which to extract picked node + * + * @return the picked node or null if pickPath is null + */ + private PNode getPickedNode(final PPickPath pickPath) { + if (pickPath == null) { + return null; + } + else { + return pickPath.getPickedNode(); + } + } + + // **************************************************************** + // Event Dispatch. + // **************************************************************** + /** {@inheritDoc} */ + public void processInput() { + if (nextInput == null) { + return; + } + + final PInputEvent e = new PInputEvent(this, nextInput); + + Point2D newCurrentCanvasPosition = null; + Point2D newLastCanvasPosition = null; + + if (e.isMouseEvent()) { + if (e.isMouseEnteredOrMouseExited()) { + final PPickPath aPickPath = nextInputSource.pick(((MouseEvent) nextInput).getX(), + ((MouseEvent) nextInput).getY(), 1); + setMouseOver(aPickPath); + previousMouseOver = aPickPath; + newCurrentCanvasPosition = (Point2D) currentCanvasPosition.clone(); + newLastCanvasPosition = (Point2D) lastCanvasPosition.clone(); + } + else { + lastCanvasPosition.setLocation(currentCanvasPosition); + currentCanvasPosition.setLocation(((MouseEvent) nextInput).getX(), ((MouseEvent) nextInput).getY()); + final PPickPath aPickPath = nextInputSource.pick(currentCanvasPosition.getX(), currentCanvasPosition + .getY(), 1); + setMouseOver(aPickPath); + } + } + + nextInput = null; + nextInputSource = null; + + processEvent(e, nextType); + + if (newCurrentCanvasPosition != null && newLastCanvasPosition != null) { + currentCanvasPosition.setLocation(newCurrentCanvasPosition); + lastCanvasPosition.setLocation(newLastCanvasPosition); + } + } + + /** + * Flags the given event as needing to be processed. + * + * @param event the event to be processed + * @param type type of event to be processed + * @param camera camera from which the event was dispatched + */ + public void processEventFromCamera(final InputEvent event, final int type, final PCamera camera) { + // queue input + nextInput = event; + nextType = type; + nextInputSource = camera; + + // tell root to process queued inputs + camera.getRoot().processInputs(); + } + + /** + * Dispatches the given event to the listener, or does nothing if listener + * is null. + * + * @param event event to be dispatched + * @param type type of event to dispatch + * @param listener target of dispatch + */ + private void dispatchEventToListener(final PInputEvent event, final int type, final PInputEventListener listener) { + if (listener != null) { + // clear the handled bit since the same event object is used to send + // multiple events such as mouseEntered/mouseExited and mouseMove. + event.setHandled(false); + listener.processEvent(event, type); + } + } +} diff --git a/core/src/main/java/org/piccolo2d/PLayer.java b/core/src/main/java/org/piccolo2d/PLayer.java new file mode 100644 index 0000000..65f7097 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/PLayer.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PObjectOutputStream; + + +/** + * PLayer is a node that can be viewed directly by multiple camera nodes. + * Generally child nodes are added to a layer to give the viewing cameras + * something to look at. + *
+ * A single layer node may be viewed through multiple cameras with each camera + * using its own view transform. This means that any node (since layers can have + * children) may be visible through multiple cameras at the same time. + *
+ * + * @see PCamera + * @see org.piccolo2d.event.PInputEvent + * @see org.piccolo2d.util.PPickPath + * @version 1.0 + * @author Jesse Grosjean + */ +public class PLayer extends PNode { + /** + * Allows for future serialization code to understand versioned binary + * formats. + */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in the set of this layer's + * cameras (see {@link #getCamera getCamera}, {@link #getCameraCount + * getCameraCount}, {@link #getCamerasReference getCamerasReference}). In + * any property change event the new value will be a reference to the list + * of cameras, but old value will always be null. + */ + public static final String PROPERTY_CAMERAS = "cameras"; + + /** + * The property code that identifies a change in the set of this layer's + * cameras (see {@link #getCamera getCamera}, {@link #getCameraCount + * getCameraCount}, {@link #getCamerasReference getCamerasReference}). In + * any property change event the new value will be a reference to the list + * of cameras, but old value will always be null. + */ + public static final int PROPERTY_CODE_CAMERAS = 1 << 13; + + /** + * Cameras which are registered as viewers of this PLayer. + */ + private transient List cameras; + + /** + * Creates a PLayer without any cameras attached to it. + */ + public PLayer() { + super(); + cameras = new ArrayList(); + } + + // **************************************************************** + // Cameras - Maintain the list of cameras that are viewing this + // layer. + // **************************************************************** + + /** + * Get the list of cameras viewing this layer. + * + * @return direct reference to registered cameras + */ + public List getCamerasReference() { + return cameras; + } + + /** + * Get the number of cameras viewing this layer. + * + * @return the number of cameras attached to this layer + */ + public int getCameraCount() { + if (cameras == null) { + return 0; + } + return cameras.size(); + } + + /** + * Get the camera in this layer's camera list at the specified index. + * + * @param index index of camera to fetch + * @return camera at the given index + */ + public PCamera getCamera(final int index) { + return (PCamera) cameras.get(index); + } + + /** + * Add a camera to this layer's camera list. This method it called + * automatically when a layer is added to a camera. + * + * @param camera the camera to add to this layer + */ + public void addCamera(final PCamera camera) { + addCamera(cameras.size(), camera); + } + + /** + * Add a camera to this layer's camera list at the specified index. This + * method it called automatically when a layer is added to a camera. + * + * @param index index at which the camera should be inserted + * @param camera Camera to add to layer + */ + public void addCamera(final int index, final PCamera camera) { + cameras.add(index, camera); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); + } + + /** + * Remove the camera from this layer's camera list. + * + * @param camera the camera to remove from the layer, does nothing if not + * found + * @return camera that was passed in + */ + public PCamera removeCamera(final PCamera camera) { + if (cameras.remove(camera)) { + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); + } + return camera; + } + + /** + * Remove the camera at the given index from this layer's camera list. + * + * @param index the index of the camera we wish to remove + * + * @return camera that was removed + */ + public PCamera removeCamera(final int index) { + final PCamera result = (PCamera) cameras.remove(index); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_CAMERAS, PROPERTY_CAMERAS, null, cameras); + return result; + } + + // **************************************************************** + // Camera Repaint Notifications - Layer nodes must forward their + // repaints to each camera that is viewing them so that the camera + // views will also get repainted. + // **************************************************************** + + /** + * Override repaints and forward them to the cameras that are viewing this + * layer. + * + * @param localBounds bounds flagged as needing repainting + * @param repaintSource the source of the repaint notification + */ + public void repaintFrom(final PBounds localBounds, final PNode repaintSource) { + if (repaintSource != this) { + localToParent(localBounds); + } + + notifyCameras(localBounds); + + if (getParent() != null) { + getParent().repaintFrom(localBounds, repaintSource); + } + } + + /** + * Dispatches repaint notification to all registered cameras. + * + * @param parentBounds bounds needing repainting in parent coordinate system + */ + protected void notifyCameras(final PBounds parentBounds) { + final int count = getCameraCount(); + for (int i = 0; i < count; i++) { + final PCamera each = (PCamera) cameras.get(i); + each.repaintFromLayer(parentBounds, this); + } + } + + // **************************************************************** + // Serialization - Layers conditionally serialize their cameras. + // This means that only the camera references that were unconditionally + // (using writeObject) serialized by someone else will be restored + // when the layer is unserialized. + // **************************************************************** + + /** + * Write this layer and all its children out to the given stream. Note that + * the layer writes out any cameras that are viewing it conditionally, so + * they will only get written out if someone else writes them + * unconditionally. + * + * @param out object to which the layer should be streamed + * @throws IOException may occur while serializing to stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + if (!(out instanceof PObjectOutputStream)) { + throw new RuntimeException("May not serialize PLayer to a non PObjectOutputStream"); + } + out.defaultWriteObject(); + + final int count = getCameraCount(); + for (int i = 0; i < count; i++) { + ((PObjectOutputStream) out).writeConditionalObject(cameras.get(i)); + } + + out.writeObject(Boolean.FALSE); + } + + /** + * Deserializes PLayer from the provided ObjectInputStream. + * + * @param in stream from which PLayer should be read + * + * @throws IOException since it involves quite a bit of IO + * @throws ClassNotFoundException may occur is serialized stream has been + * renamed after serialization + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + + cameras = new ArrayList(); + + while (true) { + final Object each = in.readObject(); + if (each != null) { + if (each.equals(Boolean.FALSE)) { + break; + } + else { + cameras.add(each); + } + } + } + } +} diff --git a/core/src/main/java/org/piccolo2d/PNode.java b/core/src/main/java/org/piccolo2d/PNode.java new file mode 100644 index 0000000..401f735 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/PNode.java @@ -0,0 +1,3658 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Transparency; +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.print.Book; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import javax.swing.event.EventListenerList; +import javax.swing.event.SwingPropertyChangeSupport; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; + +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.activities.PColorActivity; +import org.piccolo2d.activities.PInterpolatingActivity; +import org.piccolo2d.activities.PTransformActivity; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PAffineTransformException; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PNodeFilter; +import org.piccolo2d.util.PObjectOutputStream; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PPickPath; +import org.piccolo2d.util.PUtil; + + +/** + * PNode is the central abstraction in Piccolo. All objects that are + * visible on the screen are instances of the node class. All nodes may have + * other "child" nodes added to them. + *
+ * See edu.umd.piccolo.examples.NodeExample.java for demonstrations of how nodes + * can be used and how new types of nodes can be created. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +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. + */ + private static final long serialVersionUID = 1L; + + /** + * The property name that identifies a change in this node's client + * propertie (see {@link #getClientProperty getClientProperty}). In an + * property change event the new value will be a reference to the map of + * client properties but old value will always be null. + */ + public static final String PROPERTY_CLIENT_PROPERTIES = "clientProperties"; + + /** + * The property code that identifies a change in this node's client + * propertie (see {@link #getClientProperty getClientProperty}). In an + * property change event the new value will be a reference to the map of + * client properties but old value will always be null. + */ + public static final int PROPERTY_CODE_CLIENT_PROPERTIES = 1 << 0; + + /** + * The property name that identifies a change of this node's bounds (see + * {@link #getBounds getBounds}, {@link #getBoundsReference + * getBoundsReference}). In any property change event the new value will be + * a reference to this node's bounds, but old value will always be null. + */ + public static final String PROPERTY_BOUNDS = "bounds"; + + /** + * The property code that identifies a change of this node's bounds (see + * {@link #getBounds getBounds}, {@link #getBoundsReference + * getBoundsReference}). In any property change event the new value will be + * a reference to this node's bounds, but old value will always be null. + */ + public static final int PROPERTY_CODE_BOUNDS = 1 << 1; + + /** + * The property name that identifies a change of this node's full bounds + * (see {@link #getFullBounds getFullBounds}, + * {@link #getFullBoundsReference getFullBoundsReference}). In any property + * change event the new value will be a reference to this node's full bounds + * cache, but old value will always be null. + */ + public static final String PROPERTY_FULL_BOUNDS = "fullBounds"; + + /** + * The property code that identifies a change of this node's full bounds + * (see {@link #getFullBounds getFullBounds}, + * {@link #getFullBoundsReference getFullBoundsReference}). In any property + * change event the new value will be a reference to this node's full bounds + * cache, but old value will always be null. + */ + public static final int PROPERTY_CODE_FULL_BOUNDS = 1 << 2; + + /** + * The property name that identifies a change of this node's transform (see + * {@link #getTransform getTransform}, {@link #getTransformReference + * getTransformReference}). In any property change event the new value will + * be a reference to this node's transform, but old value will always be + * null. + */ + public static final String PROPERTY_TRANSFORM = "transform"; + + /** + * The property code that identifies a change of this node's transform (see + * {@link #getTransform getTransform}, {@link #getTransformReference + * getTransformReference}). In any property change event the new value will + * be a reference to this node's transform, but old value will always be + * null. + */ + public static final int PROPERTY_CODE_TRANSFORM = 1 << 3; + + /** + * The property name that identifies a change of this node's visibility (see + * {@link #getVisible getVisible}). Both old value and new value will be + * null in any property change event. + */ + public static final String PROPERTY_VISIBLE = "visible"; + + /** + * The property code that identifies a change of this node's visibility (see + * {@link #getVisible getVisible}). Both old value and new value will be + * null in any property change event. + */ + public static final int PROPERTY_CODE_VISIBLE = 1 << 4; + + /** + * The property name that identifies a change of this node's paint (see + * {@link #getPaint getPaint}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final String PROPERTY_PAINT = "paint"; + + /** + * The property code that identifies a change of this node's paint (see + * {@link #getPaint getPaint}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final int PROPERTY_CODE_PAINT = 1 << 5; + + /** + * The property name that identifies a change of this node's transparency + * (see {@link #getTransparency getTransparency}). Both old value and new + * value will be null in any property change event. + */ + public static final String PROPERTY_TRANSPARENCY = "transparency"; + + /** + * The property code that identifies a change of this node's transparency + * (see {@link #getTransparency getTransparency}). Both old value and new + * value will be null in any property change event. + */ + public static final int PROPERTY_CODE_TRANSPARENCY = 1 << 6; + + /** + * The property name that identifies a change of this node's pickable status + * (see {@link #getPickable getPickable}). Both old value and new value will + * be null in any property change event. + */ + public static final String PROPERTY_PICKABLE = "pickable"; + /** + * The property code that identifies a change of this node's pickable status + * (see {@link #getPickable getPickable}). Both old value and new value will + * be null in any property change event. + */ + public static final int PROPERTY_CODE_PICKABLE = 1 << 7; + + /** + * The property name that identifies a change of this node's children + * pickable status (see {@link #getChildrenPickable getChildrenPickable}). + * Both old value and new value will be null in any property change event. + */ + public static final String PROPERTY_CHILDREN_PICKABLE = "childrenPickable"; + + /** + * The property code that identifies a change of this node's children + * pickable status (see {@link #getChildrenPickable getChildrenPickable}). + * Both old value and new value will be null in any property change event. + */ + public static final int PROPERTY_CODE_CHILDREN_PICKABLE = 1 << 8; + + /** + * The property name that identifies a change in the set of this node's + * direct children (see {@link #getChildrenReference getChildrenReference}, + * {@link #getChildrenIterator getChildrenIterator}). In any property change + * event the new value will be a reference to this node's children, but old + * value will always be null. + */ + public static final String PROPERTY_CHILDREN = "children"; + + /** + * The property code that identifies a change in the set of this node's + * direct children (see {@link #getChildrenReference getChildrenReference}, + * {@link #getChildrenIterator getChildrenIterator}). In any property change + * event the new value will be a reference to this node's children, but old + * value will always be null. + */ + public static final int PROPERTY_CODE_CHILDREN = 1 << 9; + + /** + * The property name that identifies a change of this node's parent (see + * {@link #getParent getParent}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final String PROPERTY_PARENT = "parent"; + + /** + * The property code that identifies a change of this node's parent (see + * {@link #getParent getParent}). Both old value and new value will be set + * correctly in any property change event. + */ + public static final int PROPERTY_CODE_PARENT = 1 << 10; + + /** Is an optimization for use during repaints. */ + private static final PBounds TEMP_REPAINT_BOUNDS = new PBounds(); + + /** The single scene graph delegate that receives low level node events. */ + public static PSceneGraphDelegate SCENE_GRAPH_DELEGATE = null; + + /** Tracks the parent of this node, may be null. */ + private transient PNode parent; + + /** Tracks all immediate child nodes. */ + private List children; + + /** Bounds of the PNode. */ + private final PBounds bounds; + + /** Transform that applies to this node in relation to its parent. */ + private PAffineTransform transform; + + /** The paint to use for the background of this node. */ + private Paint paint; + + /** + * How Opaque this node should be 1f = fully opaque, 0f = completely + * transparent. + */ + private float transparency; + + /** A modifiable set of client properties. */ + private MutableAttributeSet clientProperties; + + /** + * An optimization that remembers the full bounds of a node rather than + * computing it every time. + */ + private PBounds fullBoundsCache; + + /** + * Mask used when deciding whether to bubble up property change events to + * parents. + */ + private int propertyChangeParentMask = 0; + + /** Used to handle property change listeners. */ + private transient SwingPropertyChangeSupport changeSupport; + + /** List of event listeners. */ + private transient EventListenerList listenerList; + + /** Whether this node is pickable or not. */ + private boolean pickable; + + /** + * Whether to stop processing pick at this node and not bother drilling down + * into children. + */ + private boolean childrenPickable; + + /** Whether this node will be rendered. */ + private boolean visible; + + private boolean childBoundsVolatile; + + /** Whether this node needs to be repainted. */ + private boolean paintInvalid; + + /** Whether children need to be repainted. */ + private boolean childPaintInvalid; + + /** Whether this node's bounds have changed, and so needs to be relaid out. */ + private boolean boundsChanged; + + /** Whether this node's full bounds need to be recomputed. */ + private boolean fullBoundsInvalid; + + /** Whether this node's child bounds need to be recomputed. */ + private boolean childBoundsInvalid; + + private boolean occluded; + + /** Stores the name associated to this node. */ + private String name; + + /** + * toImage fill strategy that stretches the node be as large as possible + * while still retaining its aspect ratio. + * + * @since 1.3 + */ + public static final int FILL_STRATEGY_ASPECT_FIT = 1; + + /** + * toImage fill strategy that stretches the node be large enough to cover + * the image, and centers it. + * + * @since 1.3 + */ + public static final int FILL_STRATEGY_ASPECT_COVER = 2; + + /** + * toImage fill strategy that stretches the node to be exactly the + * dimensions of the image. Will result in distortion if the aspect ratios + * are different. + * + * @since 1.3 + */ + public static final int FILL_STRATEGY_EXACT_FIT = 4; + + /** + * Creates a new PNode with the given name. + * + * @since 1.3 + * @param newName name to assign to node + */ + public PNode(final String newName) { + this(); + setName(newName); + } + + /** + * Constructs a new PNode. + *
+ * By default a node's paint is null, and bounds are empty. These values
+ * must be set for the node to show up on the screen once it's added to a
+ * scene graph.
+ */
+ public PNode() {
+ bounds = new PBounds();
+ fullBoundsCache = new PBounds();
+ transparency = 1.0f;
+ pickable = true;
+ childrenPickable = true;
+ visible = true;
+ }
+
+ // ****************************************************************
+ // Animation - Methods to animate this node.
+ //
+ // Note that animation is implemented by activities (PActivity),
+ // so if you need more control over your animation look at the
+ // activities package. Each animate method creates an animation that
+ // will animate the node from its current state to the new state
+ // specified over the given duration. These methods will try to
+ // automatically schedule the new activity, but if the node does not
+ // descend from the root node when the method is called then the
+ // activity will not be scheduled and you must schedule it manually.
+ // ****************************************************************
+
+ /**
+ * Animate this node's bounds from their current location when the activity
+ * starts to the specified bounds. If this node descends from the root then
+ * the activity will be scheduled, else the returned activity should be
+ * scheduled manually. If two different transform activities are scheduled
+ * for the same node at the same time, they will both be applied to the
+ * node, but the last one scheduled will be applied last on each frame, so
+ * it will appear to have replaced the original. Generally you will not want
+ * to do that. Note this method animates the node's bounds, but does not
+ * change the node's transform. Use animateTransformToBounds() to animate
+ * the node's transform instead.
+ *
+ * @param x left of target bounds
+ * @param y top of target bounds
+ * @param width width of target bounds
+ * @param height height of target bounds
+ * @param duration amount of time that the animation should take
+ * @return the newly scheduled activity
+ */
+ public PInterpolatingActivity animateToBounds(final double x, final double y, final double width,
+ final double height, final long duration) {
+ if (duration == 0) {
+ setBounds(x, y, width, height);
+ return null;
+ }
+
+ final PBounds dst = new PBounds(x, y, width, height);
+
+ final PInterpolatingActivity interpolatingActivity = new PInterpolatingActivity(duration,
+ PUtil.DEFAULT_ACTIVITY_STEP_RATE) {
+ private PBounds src;
+
+ protected void activityStarted() {
+ src = getBounds();
+ startResizeBounds();
+ super.activityStarted();
+ }
+
+ public void setRelativeTargetValue(final float zeroToOne) {
+ PNode.this.setBounds(src.x + zeroToOne * (dst.x - src.x), src.y + zeroToOne * (dst.y - src.y),
+ src.width + zeroToOne * (dst.width - src.width), src.height + zeroToOne
+ * (dst.height - src.height));
+ }
+
+ protected void activityFinished() {
+ super.activityFinished();
+ endResizeBounds();
+ }
+ };
+
+ addActivity(interpolatingActivity);
+ return interpolatingActivity;
+ }
+
+ /**
+ * Animate this node from it's current transform when the activity starts a
+ * new transform that will fit the node into the given bounds. If this node
+ * descends from the root then the activity will be scheduled, else the
+ * returned activity should be scheduled manually. If two different
+ * transform activities are scheduled for the same node at the same time,
+ * they will both be applied to the node, but the last one scheduled will be
+ * applied last on each frame, so it will appear to have replaced the
+ * original. Generally you will not want to do that. Note this method
+ * animates the node's transform, but does not directly change the node's
+ * bounds rectangle. Use animateToBounds() to animate the node's bounds
+ * rectangle instead.
+ *
+ * @param x left of target bounds
+ * @param y top of target bounds
+ * @param width width of target bounds
+ * @param height height of target bounds
+ * @param duration amount of time that the animation should take
+ * @return the newly scheduled activity
+ */
+ public PTransformActivity animateTransformToBounds(final double x, final double y, final double width,
+ final double height, final long duration) {
+ final PAffineTransform t = new PAffineTransform();
+ t.setToScale(width / getWidth(), height / getHeight());
+ final double scale = t.getScale();
+ t.setOffset(x - getX() * scale, y - getY() * scale);
+ return animateToTransform(t, duration);
+ }
+
+ /**
+ * Animate this node's transform from its current location when the activity
+ * starts to the specified location, scale, and rotation. If this node
+ * descends from the root then the activity will be scheduled, else the
+ * returned activity should be scheduled manually. If two different
+ * transform activities are scheduled for the same node at the same time,
+ * they will both be applied to the node, but the last one scheduled will be
+ * applied last on each frame, so it will appear to have replaced the
+ * original. Generally you will not want to do that.
+ *
+ * @param x the final target x position of node
+ * @param y the final target y position of node
+ * @param duration amount of time that the animation should take
+ * @param scale the final scale for the duration
+ * @param theta final theta value (in radians) for the animation
+ * @return the newly scheduled activity
+ */
+ public PTransformActivity animateToPositionScaleRotation(final double x, final double y, final double scale,
+ final double theta, final long duration) {
+ final PAffineTransform t = getTransform();
+ t.setOffset(x, y);
+ t.setScale(scale);
+ t.setRotation(theta);
+ return animateToTransform(t, duration);
+ }
+
+ /**
+ * Animate this node's transform from its current values when the activity
+ * starts to the new values specified in the given transform. If this node
+ * descends from the root then the activity will be scheduled, else the
+ * returned activity should be scheduled manually. If two different
+ * transform activities are scheduled for the same node at the same time,
+ * they will both be applied to the node, but the last one scheduled will be
+ * applied last on each frame, so it will appear to have replaced the
+ * original. Generally you will not want to do that.
+ *
+ * @param destTransform the final transform value
+ * @param duration amount of time that the animation should take
+ * @return the newly scheduled activity
+ */
+ public PTransformActivity animateToTransform(final AffineTransform destTransform, final long duration) {
+ if (duration == 0) {
+ setTransform(destTransform);
+ return null;
+ }
+ else {
+ final PTransformActivity.Target t = new PTransformActivity.Target() {
+ public void setTransform(final AffineTransform aTransform) {
+ PNode.this.setTransform(aTransform);
+ }
+
+ public void getSourceMatrix(final double[] aSource) {
+ PNode.this.getTransformReference(true).getMatrix(aSource);
+ }
+ };
+
+ final PTransformActivity ta = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t,
+ destTransform);
+ addActivity(ta);
+ return ta;
+ }
+ }
+
+ /**
+ * Animate this node's color from its current value to the new value
+ * specified. This meathod assumes that this nodes paint property is of type
+ * color. If this node descends from the root then the activity will be
+ * scheduled, else the returned activity should be scheduled manually. If
+ * two different color activities are scheduled for the same node at the
+ * same time, they will both be applied to the node, but the last one
+ * scheduled will be applied last on each frame, so it will appear to have
+ * replaced the original. Generally you will not want to do that.
+ *
+ * @param destColor final color value.
+ * @param duration amount of time that the animation should take
+ * @return the newly scheduled activity
+ */
+ public PInterpolatingActivity animateToColor(final Color destColor, final long duration) {
+ if (duration == 0) {
+ setPaint(destColor);
+ return null;
+ }
+ else {
+ final PColorActivity.Target t = new PColorActivity.Target() {
+ public Color getColor() {
+ return (Color) getPaint();
+ }
+
+ public void setColor(final Color color) {
+ setPaint(color);
+ }
+ };
+
+ final PColorActivity ca = new PColorActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, destColor);
+ addActivity(ca);
+ return ca;
+ }
+ }
+
+ /**
+ * Animate this node's transparency from its current value to the new value
+ * specified. Transparency values must range from zero to one. If this node
+ * descends from the root then the activity will be scheduled, else the
+ * returned activity should be scheduled manually. If two different
+ * transparency activities are scheduled for the same node at the same time,
+ * they will both be applied to the node, but the last one scheduled will be
+ * applied last on each frame, so it will appear to have replaced the
+ * original. Generally you will not want to do that.
+ *
+ * @param zeroToOne final transparency value.
+ * @param duration amount of time that the animation should take
+ * @return the newly scheduled activity
+ */
+ public PInterpolatingActivity animateToTransparency(final float zeroToOne, final long duration) {
+ if (duration == 0) {
+ setTransparency(zeroToOne);
+ return null;
+ }
+ else {
+ final float dest = zeroToOne;
+
+ final PInterpolatingActivity ta = new PInterpolatingActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE) {
+ private float source;
+
+ protected void activityStarted() {
+ source = getTransparency();
+ super.activityStarted();
+ }
+
+ public void setRelativeTargetValue(final float zeroToOne) {
+ PNode.this.setTransparency(source + zeroToOne * (dest - source));
+ }
+ };
+
+ addActivity(ta);
+ return ta;
+ }
+ }
+
+ /**
+ * Schedule the given activity with the root, note that only scheduled
+ * activities will be stepped. If the activity is successfully added true is
+ * returned, else false.
+ *
+ * @param activity new activity to schedule
+ * @return true if the activity is successfully scheduled.
+ */
+ public boolean addActivity(final PActivity activity) {
+ final PRoot r = getRoot();
+ if (r != null) {
+ return r.addActivity(activity);
+ }
+ return false;
+ }
+
+ // ****************************************************************
+ // Client Properties - Methods for managing client properties for
+ // this node.
+ //
+ // Client properties provide a way for programmers to attach
+ // extra information to a node without having to subclass it and
+ // add new instance variables.
+ // ****************************************************************
+
+ /**
+ * Return mutable attributed set of client properties associated with this
+ * node.
+ *
+ * @return the client properties associated to this node
+ */
+ public MutableAttributeSet getClientProperties() {
+ if (clientProperties == null) {
+ clientProperties = new SimpleAttributeSet();
+ }
+ return clientProperties;
+ }
+
+ /**
+ * Returns the value of the client attribute with the specified key. Only
+ * attributes added with addAttribute
will return a non-null
+ * value.
+ *
+ * @param key key to use while fetching client attribute
+ *
+ * @return the value of this attribute or null
+ */
+ public Object getAttribute(final Object key) {
+ if (clientProperties == null || key == null) {
+ return null;
+ }
+ else {
+ return clientProperties.getAttribute(key);
+ }
+ }
+
+ /**
+ * Add an arbitrary key/value to this node.
+ *
+ * The get/add attribute
methods provide access to a small
+ * per-instance attribute set. Callers can use get/add attribute to annotate
+ * nodes that were created by another module.
+ *
+ * If value is null this method will remove the attribute.
+ *
+ * @param key to use when adding the attribute
+ * @param value value to associate to the new attribute
+ */
+ public void addAttribute(final Object key, final Object value) {
+ if (value == null && clientProperties == null) {
+ return;
+ }
+
+ final Object oldValue = getAttribute(key);
+
+ if (value != oldValue) {
+ if (clientProperties == null) {
+ clientProperties = new SimpleAttributeSet();
+ }
+
+ if (value == null) {
+ clientProperties.removeAttribute(key);
+ }
+ else {
+ clientProperties.addAttribute(key, value);
+ }
+
+ if (clientProperties.getAttributeCount() == 0 && clientProperties.getResolveParent() == null) {
+ clientProperties = null;
+ }
+
+ firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, PROPERTY_CLIENT_PROPERTIES, null, clientProperties);
+ firePropertyChange(PROPERTY_CODE_CLIENT_PROPERTIES, key.toString(), oldValue, value);
+ }
+ }
+
+ /**
+ * Returns an enumeration of all keys maped to attribute values values.
+ *
+ * @return an Enumeration over attribute keys
+ */
+ public Enumeration getClientPropertyKeysEnumeration() {
+ if (clientProperties == null) {
+ return PUtil.NULL_ENUMERATION;
+ }
+ else {
+ return clientProperties.getAttributeNames();
+ }
+ }
+
+ // convenience methods for attributes
+
+ /**
+ * Fetches the value of the requested attribute, returning defaultValue is
+ * not found.
+ *
+ * @param key attribute to search for
+ * @param defaultValue value to return if attribute is not found
+ *
+ * @return value of attribute or defaultValue if not found
+ */
+ public Object getAttribute(final Object key, final Object defaultValue) {
+ final Object value = getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ }
+
+ return value;
+ }
+
+ /**
+ * Fetches the boolean value of the requested attribute, returning
+ * defaultValue is not found.
+ *
+ * @param key attribute to search for
+ * @param defaultValue value to return if attribute is not found
+ *
+ * @return value of attribute or defaultValue if not found
+ */
+ public boolean getBooleanAttribute(final Object key, final boolean defaultValue) {
+ final Boolean value = (Boolean) getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ }
+
+ return value.booleanValue();
+ }
+
+ /**
+ * Fetches the integer value of the requested attribute, returning
+ * defaultValue is not found.
+ *
+ * @param key attribute to search for
+ * @param defaultValue value to return if attribute is not found
+ *
+ * @return value of attribute or defaultValue if not found
+ */
+ public int getIntegerAttribute(final Object key, final int defaultValue) {
+ final Number value = (Number) getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ }
+
+ return value.intValue();
+ }
+
+ /**
+ * Fetches the double value of the requested attribute, returning
+ * defaultValue is not found.
+ *
+ * @param key attribute to search for
+ * @param defaultValue value to return if attribute is not found
+ *
+ * @return value of attribute or defaultValue if not found
+ */
+ public double getDoubleAttribute(final Object key, final double defaultValue) {
+ final Number value = (Number) getAttribute(key);
+ if (value == null) {
+ return defaultValue;
+ }
+
+ return value.doubleValue();
+ }
+
+ // ****************************************************************
+ // Copying - Methods for copying this node and its descendants.
+ // Copying is implemented in terms of serialization.
+ // ****************************************************************
+
+ /**
+ * The copy method copies this node and all of its descendants. Note that
+ * copying is implemented in terms of java serialization. See the
+ * serialization notes for more information.
+ *
+ * @return new copy of this node or null if the node was not serializable
+ */
+ public Object clone() {
+ try {
+ final byte[] ser = PObjectOutputStream.toByteArray(this);
+ return new ObjectInputStream(new ByteArrayInputStream(ser)).readObject();
+ }
+ catch (final IOException e) {
+ return null;
+ }
+ catch (final ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ // ****************************************************************
+ // Coordinate System Conversions - Methods for converting
+ // geometry between this nodes local coordinates and the other
+ // major coordinate systems.
+ //
+ // Each nodes has an affine transform that it uses to define its
+ // own coordinate system. For example if you create a new node and
+ // add it to the canvas it will appear in the upper right corner. Its
+ // coordinate system matches the coordinate system of its parent
+ // (the root node) at this point. But if you move this node by calling
+ // node.translate() the nodes affine transform will be modified and the
+ // node will appear at a different location on the screen. The node
+ // coordinate system no longer matches the coordinate system of its
+ // parent.
+ //
+ // This is useful because it means that the node's methods for
+ // rendering and picking don't need to worry about the fact that
+ // the node has been moved to another position on the screen, they
+ // keep working just like they did when it was in the upper right
+ // hand corner of the screen.
+ //
+ // The problem is now that each node defines its own coordinate
+ // system it is difficult to compare the positions of two node with
+ // each other. These methods are all meant to help solve that problem.
+ //
+ // The terms used in the methods are as follows:
+ //
+ // local - The local or base coordinate system of a node.
+ // parent - The coordinate system of a node's parent
+ // global - The topmost coordinate system, above the root node.
+ //
+ // Normally when comparing the positions of two nodes you will
+ // convert the local position of each node to the global coordinate
+ // system, and then compare the positions in that common coordinate
+ // system.
+ // ***************************************************************
+
+ /**
+ * Transform the given point from this node's local coordinate system to its
+ * parent's local coordinate system. Note that this will modify the point
+ * parameter.
+ *
+ * @param localPoint point in local coordinate system to be transformed.
+ * @return point in parent's local coordinate system
+ */
+ public Point2D localToParent(final Point2D localPoint) {
+ if (transform == null) {
+ return localPoint;
+ }
+ return transform.transform(localPoint, localPoint);
+ }
+
+ /**
+ * Transform the given dimension from this node's local coordinate system to
+ * its parent's local coordinate system. Note that this will modify the
+ * dimension parameter.
+ *
+ * @param localDimension dimension in local coordinate system to be
+ * transformed.
+ * @return dimension in parent's local coordinate system
+ */
+ public Dimension2D localToParent(final Dimension2D localDimension) {
+ if (transform == null) {
+ return localDimension;
+ }
+ return transform.transform(localDimension, localDimension);
+ }
+
+ /**
+ * Transform the given rectangle from this node's local coordinate system to
+ * its parent's local coordinate system. Note that this will modify the
+ * rectangle parameter.
+ *
+ * @param localRectangle rectangle in local coordinate system to be
+ * transformed.
+ * @return rectangle in parent's local coordinate system
+ */
+ public Rectangle2D localToParent(final Rectangle2D localRectangle) {
+ if (transform == null) {
+ return localRectangle;
+ }
+ return transform.transform(localRectangle, localRectangle);
+ }
+
+ /**
+ * Transform the given point from this node's parent's local coordinate
+ * system to the local coordinate system of this node. Note that this will
+ * modify the point parameter.
+ *
+ * @param parentPoint point in parent's coordinate system to be transformed.
+ * @return point in this node's local coordinate system
+ */
+ public Point2D parentToLocal(final Point2D parentPoint) {
+ if (transform == null) {
+ return parentPoint;
+ }
+
+ return transform.inverseTransform(parentPoint, parentPoint);
+ }
+
+ /**
+ * Transform the given dimension from this node's parent's local coordinate
+ * system to the local coordinate system of this node. Note that this will
+ * modify the dimension parameter.
+ *
+ * @param parentDimension dimension in parent's coordinate system to be
+ * transformed.
+ * @return dimension in this node's local coordinate system
+ */
+ public Dimension2D parentToLocal(final Dimension2D parentDimension) {
+ if (transform == null) {
+ return parentDimension;
+ }
+ return transform.inverseTransform(parentDimension, parentDimension);
+ }
+
+ /**
+ * Transform the given rectangle from this node's parent's local coordinate
+ * system to the local coordinate system of this node. Note that this will
+ * modify the rectangle parameter.
+ *
+ * @param parentRectangle rectangle in parent's coordinate system to be
+ * transformed.
+ * @return rectangle in this node's local coordinate system
+ */
+ public Rectangle2D parentToLocal(final Rectangle2D parentRectangle) {
+ if (transform == null) {
+ return parentRectangle;
+ }
+ return transform.inverseTransform(parentRectangle, parentRectangle);
+ }
+
+ /**
+ * Transform the given point from this node's local coordinate system to the
+ * global coordinate system. Note that this will modify the point parameter.
+ *
+ * @param localPoint point in local coordinate system to be transformed.
+ * @return point in global coordinates
+ */
+ public Point2D localToGlobal(final Point2D localPoint) {
+ PNode n = this;
+ while (n != null) {
+ n.localToParent(localPoint);
+ n = n.parent;
+ }
+ return localPoint;
+ }
+
+ /**
+ * Transform the given dimension from this node's local coordinate system to
+ * the global coordinate system. Note that this will modify the dimension
+ * parameter.
+ *
+ * @param localDimension dimension in local coordinate system to be
+ * transformed.
+ * @return dimension in global coordinates
+ */
+ public Dimension2D localToGlobal(final Dimension2D localDimension) {
+ PNode n = this;
+ while (n != null) {
+ n.localToParent(localDimension);
+ n = n.parent;
+ }
+ return localDimension;
+ }
+
+ /**
+ * Transform the given rectangle from this node's local coordinate system to
+ * the global coordinate system. Note that this will modify the rectangle
+ * parameter.
+ *
+ * @param localRectangle rectangle in local coordinate system to be
+ * transformed.
+ * @return rectangle in global coordinates
+ */
+ public Rectangle2D localToGlobal(final Rectangle2D localRectangle) {
+ PNode n = this;
+ while (n != null) {
+ n.localToParent(localRectangle);
+ n = n.parent;
+ }
+ return localRectangle;
+ }
+
+ /**
+ * Transform the given point from global coordinates to this node's local
+ * coordinate system. Note that this will modify the point parameter.
+ *
+ * @param globalPoint point in global coordinates to be transformed.
+ * @return point in this node's local coordinate system.
+ */
+ public Point2D globalToLocal(final Point2D globalPoint) {
+ final PAffineTransform globalTransform = computeGlobalTransform(this);
+ return globalTransform.inverseTransform(globalPoint, globalPoint);
+ }
+
+ private PAffineTransform computeGlobalTransform(final PNode node) {
+ if (node == null) {
+ return new PAffineTransform();
+ }
+
+ final PAffineTransform parentGlobalTransform = computeGlobalTransform(node.parent);
+ if (node.transform != null) {
+ parentGlobalTransform.concatenate(node.transform);
+ }
+ return parentGlobalTransform;
+ }
+
+ /**
+ * Transform the given dimension from global coordinates to this node's
+ * local coordinate system. Note that this will modify the dimension
+ * parameter.
+ *
+ * @param globalDimension dimension in global coordinates to be transformed.
+ * @return dimension in this node's local coordinate system.
+ */
+ public Dimension2D globalToLocal(final Dimension2D globalDimension) {
+ if (parent != null) {
+ parent.globalToLocal(globalDimension);
+ }
+ return parentToLocal(globalDimension);
+ }
+
+ /**
+ * Transform the given rectangle from global coordinates to this node's
+ * local coordinate system. Note that this will modify the rectangle
+ * parameter.
+ *
+ * @param globalRectangle rectangle in global coordinates to be transformed.
+ * @return rectangle in this node's local coordinate system.
+ */
+ public Rectangle2D globalToLocal(final Rectangle2D globalRectangle) {
+ if (parent != null) {
+ parent.globalToLocal(globalRectangle);
+ }
+ return parentToLocal(globalRectangle);
+ }
+
+ /**
+ * Return the transform that converts local coordinates at this node to the
+ * global coordinate system.
+ *
+ * @param dest PAffineTransform to transform to global coordinates
+ * @return The concatenation of transforms from the top node down to this
+ * node.
+ */
+ public PAffineTransform getLocalToGlobalTransform(final PAffineTransform dest) {
+ PAffineTransform result = dest;
+ if (parent != null) {
+ result = parent.getLocalToGlobalTransform(result);
+ if (transform != null) {
+ result.concatenate(transform);
+ }
+ }
+ else if (dest == null) {
+ result = getTransform();
+ }
+ else if (transform != null) {
+ result.setTransform(transform);
+ }
+ else {
+ result.setToIdentity();
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the transform that converts global coordinates to local
+ * coordinates of this node.
+ *
+ * @param dest PAffineTransform to transform from global to local
+ *
+ * @return The inverse of the concatenation of transforms from the root down
+ * to this node.
+ */
+ public PAffineTransform getGlobalToLocalTransform(final PAffineTransform dest) {
+ PAffineTransform result = getLocalToGlobalTransform(dest);
+ try {
+ result.setTransform(result.createInverse());
+ }
+ catch (final NoninvertibleTransformException e) {
+ throw new PAffineTransformException(e, result);
+ }
+ return result;
+ }
+
+ // ****************************************************************
+ // Event Listeners - Methods for adding and removing event listeners
+ // from a node.
+ //
+ // Here methods are provided to add property change listeners and
+ // input event listeners. The property change listeners are notified
+ // when certain properties of this node change, and the input event
+ // listeners are notified when the nodes receives new key and mouse
+ // events.
+ // ****************************************************************
+
+ /**
+ * Return the list of event listeners associated with this node.
+ *
+ * @return event listener list or null
+ */
+ public EventListenerList getListenerList() {
+ return listenerList;
+ }
+
+ /**
+ * Adds the specified input event listener to receive input events from this
+ * node.
+ *
+ * @param listener the new input listener
+ */
+ public void addInputEventListener(final PInputEventListener listener) {
+ if (listenerList == null) {
+ listenerList = new EventListenerList();
+ }
+ getListenerList().add(PInputEventListener.class, listener);
+ }
+
+ /**
+ * Removes the specified input event listener so that it no longer receives
+ * input events from this node.
+ *
+ * @param listener the input listener to remove
+ */
+ public void removeInputEventListener(final PInputEventListener listener) {
+ if (listenerList == null) {
+ return;
+ }
+ getListenerList().remove(PInputEventListener.class, listener);
+ if (listenerList.getListenerCount() == 0) {
+ listenerList = null;
+ }
+ }
+
+ /**
+ * Add a PropertyChangeListener to the listener list. The listener is
+ * registered for all properties. See the fields in PNode and subclasses
+ * that start with PROPERTY_ to find out which properties exist.
+ *
+ * @param listener The PropertyChangeListener to be added
+ */
+ public void addPropertyChangeListener(final PropertyChangeListener listener) {
+ if (changeSupport == null) {
+ changeSupport = new SwingPropertyChangeSupport(this);
+ }
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Add a PropertyChangeListener for a specific property. The listener will
+ * be invoked only when a call on firePropertyChange names that specific
+ * property. See the fields in PNode and subclasses that start with
+ * PROPERTY_ to find out which properties are supported.
+ *
+ * @param propertyName The name of the property to listen on.
+ * @param listener The PropertyChangeListener to be added
+ */
+ public void addPropertyChangeListener(final String propertyName, final PropertyChangeListener listener) {
+ if (listener == null) {
+ return;
+ }
+ if (changeSupport == null) {
+ changeSupport = new SwingPropertyChangeSupport(this);
+ }
+ changeSupport.addPropertyChangeListener(propertyName, listener);
+ }
+
+ /**
+ * Remove a PropertyChangeListener from the listener list. This removes a
+ * PropertyChangeListener that was registered for all properties.
+ *
+ * @param listener The PropertyChangeListener to be removed
+ */
+ public void removePropertyChangeListener(final PropertyChangeListener listener) {
+ if (changeSupport != null) {
+ changeSupport.removePropertyChangeListener(listener);
+ }
+ }
+
+ /**
+ * Remove a PropertyChangeListener for a specific property.
+ *
+ * @param propertyName The name of the property that was listened on.
+ * @param listener The PropertyChangeListener to be removed
+ */
+ public void removePropertyChangeListener(final String propertyName, final PropertyChangeListener listener) {
+ if (listener == null) {
+ return;
+ }
+ if (changeSupport == null) {
+ return;
+ }
+ changeSupport.removePropertyChangeListener(propertyName, listener);
+ }
+
+ /**
+ * Return the propertyChangeParentMask that determines which property change
+ * events are forwared to this nodes parent so that its property change
+ * listeners will also be notified.
+ *
+ * @return mask used for deciding whether to bubble property changes up to
+ * parent
+ */
+ public int getPropertyChangeParentMask() {
+ return propertyChangeParentMask;
+ }
+
+ /**
+ * Set the propertyChangeParentMask that determines which property change
+ * events are forwared to this nodes parent so that its property change
+ * listeners will also be notified.
+ *
+ * @param propertyChangeParentMask new mask for property change bubble up
+ */
+ public void setPropertyChangeParentMask(final int propertyChangeParentMask) {
+ this.propertyChangeParentMask = propertyChangeParentMask;
+ }
+
+ /**
+ * Report a bound property update to any registered listeners. No event is
+ * fired if old and new are equal and non-null. If the propertyCode exists
+ * in this node's propertyChangeParentMask then a property change event will
+ * also be fired on this nodes parent.
+ *
+ * @param propertyCode The code of the property changed.
+ * @param propertyName The name of the property that was changed.
+ * @param oldValue The old value of the property.
+ * @param newValue The new value of the property.
+ */
+ protected void firePropertyChange(final int propertyCode, final String propertyName, final Object oldValue,
+ final Object newValue) {
+ PropertyChangeEvent event = null;
+
+ if (changeSupport != null) {
+ event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
+ changeSupport.firePropertyChange(event);
+ }
+ if (parent != null && (propertyCode & propertyChangeParentMask) != 0) {
+ if (event == null) {
+ event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
+ }
+ parent.fireChildPropertyChange(event, propertyCode);
+ }
+ }
+
+ /**
+ * Called by child node to forward property change events up the node tree
+ * so that property change listeners registered with this node will be
+ * notified of property changes of its children nodes. For performance
+ * reason only propertyCodes listed in the propertyChangeParentMask are
+ * forwarded.
+ *
+ * @param event The property change event containing source node and changed
+ * values.
+ * @param propertyCode The code of the property changed.
+ */
+ protected void fireChildPropertyChange(final PropertyChangeEvent event, final int propertyCode) {
+ if (changeSupport != null) {
+ changeSupport.firePropertyChange(event);
+ }
+ if (parent != null && (propertyCode & propertyChangeParentMask) != 0) {
+ parent.fireChildPropertyChange(event, propertyCode);
+ }
+ }
+
+ // ****************************************************************
+ // Bounds Geometry - Methods for setting and querying the bounds
+ // of this node.
+ //
+ // The bounds of a node store the node's position and size in
+ // the nodes local coordinate system. Many node subclasses will need
+ // to override the setBounds method so that they can update their
+ // internal state appropriately. See PPath for an example.
+ //
+ // Since the bounds are stored in the local coordinate system
+ // they WILL NOT change if the node is scaled, translated, or rotated.
+ //
+ // The bounds may be accessed with either getBounds, or
+ // getBoundsReference. The former returns a copy of the bounds
+ // the latter returns a reference to the nodes bounds that should
+ // normally not be modified. If a node is marked as volatile then
+ // it may modify its bounds before returning them from getBoundsReference,
+ // otherwise it may not.
+ // ****************************************************************
+
+ /**
+ * Return a copy of this node's bounds. These bounds are stored in the local
+ * coordinate system of this node and do not include the bounds of any of
+ * this node's children.
+ *
+ * @return copy of this node's local bounds
+ */
+ public PBounds getBounds() {
+ return (PBounds) getBoundsReference().clone();
+ }
+
+ /**
+ * Return a direct reference to this node's bounds. These bounds are stored
+ * in the local coordinate system of this node and do not include the bounds
+ * of any of this node's children. The value returned should not be
+ * modified.
+ *
+ * @return direct reference to local bounds
+ */
+ public PBounds getBoundsReference() {
+ return bounds;
+ }
+
+ /**
+ * Notify this node that you will begin to repeatedly call setBounds
+ *
. When you
+ * are done call endResizeBounds
to let the node know that you
+ * are done.
+ */
+ public void startResizeBounds() {
+ }
+
+ /**
+ * Notify this node that you have finished a resize bounds sequence.
+ */
+ public void endResizeBounds() {
+ }
+
+ /**
+ * Set's this node's bounds left position, leaving y, width, and height
+ * unchanged.
+ *
+ * @param x new x position of bounds
+ *
+ * @return whether the change was successful
+ */
+ public boolean setX(final double x) {
+ return setBounds(x, getY(), getWidth(), getHeight());
+ }
+
+ /**
+ * Set's this node's bounds top position, leaving x, width, and height
+ * unchanged.
+ *
+ * @param y new y position of bounds
+ *
+ * @return whether the change was successful
+ */
+ public boolean setY(final double y) {
+ return setBounds(getX(), y, getWidth(), getHeight());
+ }
+
+ /**
+ * Set's this node's bounds width, leaving x, y, and height unchanged.
+ *
+ * @param width new width position of bounds
+ *
+ * @return whether the change was successful
+ */
+ public boolean setWidth(final double width) {
+ return setBounds(getX(), getY(), width, getHeight());
+ }
+
+ /**
+ * Set's this node's bounds height, leaving x, y, and width unchanged.
+ *
+ * @param height new height position of bounds
+ *
+ * @return whether the change was successful
+ */
+ public boolean setHeight(final double height) {
+ return setBounds(getX(), getY(), getWidth(), height);
+ }
+
+ /**
+ * Set the bounds of this node to the given value. These bounds are stored
+ * in the local coordinate system of this node.
+ *
+ * @param newBounds bounds to apply to this node
+ *
+ * @return true if the bounds changed.
+ */
+ public boolean setBounds(final Rectangle2D newBounds) {
+ return setBounds(newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight());
+ }
+
+ /**
+ * Set the bounds of this node to the given position and size. These bounds
+ * are stored in the local coordinate system of this node.
+ *
+ * If the width or height is less then or equal to zero then the bound's
+ * empty bit will be set to true.
+ *
+ * Subclasses must call the super.setBounds() method.
+ *
+ * @param x x position of bounds
+ * @param y y position of bounds
+ * @param width width to apply to the bounds
+ * @param height height to apply to the bounds
+ *
+ * @return true if the bounds changed.
+ */
+ public boolean setBounds(final double x, final double y, final double width, final double height) {
+ if (bounds.x != x || bounds.y != y || bounds.width != width || bounds.height != height) {
+ bounds.setRect(x, y, width, height);
+
+ if (width <= 0 || height <= 0) {
+ bounds.reset();
+ }
+
+ internalUpdateBounds(x, y, width, height);
+ invalidatePaint();
+ signalBoundsChanged();
+ return true;
+ }
+ // Don't put any invalidating code here or else nodes with volatile
+ // bounds will
+ // create a soft infinite loop (calling Swing.invokeLater()) when they
+ // validate
+ // their bounds.
+ return false;
+ }
+
+ /**
+ * Gives nodes a chance to update their internal structure before bounds
+ * changed notifications are sent. When this message is recived the nodes
+ * bounds field will contain the new value.
+ *
+ * See PPath for an example that uses this method.
+ *
+ * @param x x position of bounds
+ * @param y y position of bounds
+ * @param width width to apply to the bounds
+ * @param height height to apply to the bounds
+ */
+ protected void internalUpdateBounds(final double x, final double y, final double width, final double height) {
+ }
+
+ /**
+ * Set the empty bit of this bounds to true.
+ */
+ public void resetBounds() {
+ setBounds(0, 0, 0, 0);
+ }
+
+ /**
+ * Return the x position (in local coords) of this node's bounds.
+ *
+ * @return local x position of bounds
+ */
+ public double getX() {
+ return getBoundsReference().getX();
+ }
+
+ /**
+ * Return the y position (in local coords) of this node's bounds.
+ *
+ * @return local y position of bounds
+ */
+ public double getY() {
+ return getBoundsReference().getY();
+ }
+
+ /**
+ * Return the width (in local coords) of this node's bounds.
+ *
+ * @return local width of bounds
+ */
+ public double getWidth() {
+ return getBoundsReference().getWidth();
+ }
+
+ /**
+ * Return the height (in local coords) of this node's bounds.
+ *
+ * @return local width of bounds
+ */
+ public double getHeight() {
+ return getBoundsReference().getHeight();
+ }
+
+ /**
+ * Return a copy of the bounds of this node in the global coordinate system.
+ *
+ * @return the bounds in global coordinate system.
+ */
+ public PBounds getGlobalBounds() {
+ return (PBounds) localToGlobal(getBounds());
+ }
+
+ /**
+ * Center the bounds of this node so that they are centered on the given
+ * point specified on the local coordinates of this node. Note that this
+ * method will modify the nodes bounds, while centerFullBoundsOnPoint will
+ * modify the nodes transform.
+ *
+ * @param localX x position of point around which to center bounds
+ * @param localY y position of point around which to center bounds
+ *
+ * @return true if the bounds changed.
+ */
+ public boolean centerBoundsOnPoint(final double localX, final double localY) {
+ final double dx = localX - bounds.getCenterX();
+ final double dy = localY - bounds.getCenterY();
+ return setBounds(bounds.x + dx, bounds.y + dy, bounds.width, bounds.height);
+ }
+
+ /**
+ * Center the full bounds of this node so that they are centered on the
+ * given point specified on the local coordinates of this nodes parent. Note
+ * that this method will modify the nodes transform, while
+ * centerBoundsOnPoint will modify the nodes bounds.
+ *
+ * @param parentX x position around which to center full bounds
+ * @param parentY y position around which to center full bounds
+ */
+ public void centerFullBoundsOnPoint(final double parentX, final double parentY) {
+ final double dx = parentX - getFullBoundsReference().getCenterX();
+ final double dy = parentY - getFullBoundsReference().getCenterY();
+ offset(dx, dy);
+ }
+
+ /**
+ * Return true if this node intersects the given rectangle specified in
+ * local bounds. If the geometry of this node is complex this method can
+ * become expensive, it is therefore recommended that
+ * fullIntersects
is used for quick rejects before calling this
+ * method.
+ *
+ * @param localBounds the bounds to test for intersection against
+ * @return true if the given rectangle intersects this nodes geometry.
+ */
+ public boolean intersects(final Rectangle2D localBounds) {
+ if (localBounds == null) {
+ return true;
+ }
+ return getBoundsReference().intersects(localBounds);
+ }
+
+ // ****************************************************************
+ // Full Bounds - Methods for computing and querying the
+ // full bounds of this node.
+ //
+ // The full bounds of a node store the nodes bounds
+ // together with the union of the bounds of all the
+ // node's descendants. The full bounds are stored in the parent
+ // coordinate system of this node, the full bounds DOES change
+ // when you translate, scale, or rotate this node.
+ //
+ // The full bounds may be accessed with either getFullBounds, or
+ // getFullBoundsReference. The former returns a copy of the full bounds
+ // the latter returns a reference to the node's full bounds that should
+ // not be modified.
+ // ****************************************************************
+
+ /**
+ * Return a copy of this node's full bounds. These bounds are stored in the
+ * parent coordinate system of this node and they include the union of this
+ * node's bounds and all the bounds of it's descendants.
+ *
+ * @return a copy of this node's full bounds.
+ */
+ public PBounds getFullBounds() {
+ return (PBounds) getFullBoundsReference().clone();
+ }
+
+ /**
+ * Return a reference to this node's full bounds cache. These bounds are
+ * stored in the parent coordinate system of this node and they include the
+ * union of this node's bounds and all the bounds of it's descendants. The
+ * bounds returned by this method should not be modified.
+ *
+ * @return a reference to this node's full bounds cache.
+ */
+ public PBounds getFullBoundsReference() {
+ validateFullBounds();
+ return fullBoundsCache;
+ }
+
+ /**
+ * Compute and return the full bounds of this node. If the dstBounds
+ * parameter is not null then it will be used to return the results instead
+ * of creating a new PBounds.
+ *
+ * @param dstBounds if not null the new bounds will be stored here
+ * @return the full bounds in the parent coordinate system of this node
+ */
+ public PBounds computeFullBounds(final PBounds dstBounds) {
+ final PBounds result = getUnionOfChildrenBounds(dstBounds);
+ result.add(getBoundsReference());
+ localToParent(result);
+ return result;
+ }
+
+ /**
+ * Compute and return the union of the full bounds of all the children of
+ * this node. If the dstBounds parameter is not null then it will be used to
+ * return the results instead of creating a new PBounds.
+ *
+ * @param dstBounds if not null the new bounds will be stored here
+ * @return union of children bounds
+ */
+ public PBounds getUnionOfChildrenBounds(final PBounds dstBounds) {
+ PBounds resultBounds;
+ if (dstBounds == null) {
+ resultBounds = new PBounds();
+ }
+ else {
+ resultBounds = dstBounds;
+ resultBounds.resetToZero();
+ }
+
+ final int count = getChildrenCount();
+ for (int i = 0; i < count; i++) {
+ final PNode each = (PNode) children.get(i);
+ resultBounds.add(each.getFullBoundsReference());
+ }
+
+ return resultBounds;
+ }
+
+ /**
+ * Return a copy of the full bounds of this node in the global coordinate
+ * system.
+ *
+ * @return the full bounds in global coordinate system.
+ */
+ public PBounds getGlobalFullBounds() {
+ final PBounds b = getFullBounds();
+ if (parent != null) {
+ parent.localToGlobal(b);
+ }
+ return b;
+ }
+
+ /**
+ * Return true if the full bounds of this node intersects with the specified
+ * bounds.
+ *
+ * @param parentBounds the bounds to test for intersection against
+ * (specified in parent's coordinate system)
+ * @return true if this nodes full bounds intersect the given bounds.
+ */
+ public boolean fullIntersects(final Rectangle2D parentBounds) {
+ if (parentBounds == null) {
+ return true;
+ }
+ return getFullBoundsReference().intersects(parentBounds);
+ }
+
+ // ****************************************************************
+ // Bounds Damage Management - Methods used to invalidate and validate
+ // the bounds of nodes.
+ // ****************************************************************
+
+ /**
+ * Return true if this nodes bounds may change at any time. The default
+ * behavior is to return false, subclasses that override this method to
+ * return true should also override getBoundsReference() and compute their
+ * volatile bounds there before returning the reference.
+ *
+ * @return true if this node has volatile bounds
+ */
+ protected boolean getBoundsVolatile() {
+ return false;
+ }
+
+ /**
+ * Return true if this node has a child with volatile bounds.
+ *
+ * @return true if this node has a child with volatile bounds
+ */
+ protected boolean getChildBoundsVolatile() {
+ return childBoundsVolatile;
+ }
+
+ /**
+ * Set if this node has a child with volatile bounds. This should normally
+ * be managed automatically by the bounds validation process.
+ *
+ * @param childBoundsVolatile true if this node has a descendant with
+ * volatile bounds
+ */
+ protected void setChildBoundsVolatile(final boolean childBoundsVolatile) {
+ this.childBoundsVolatile = childBoundsVolatile;
+ }
+
+ /**
+ * Return true if this node's bounds have recently changed. This flag will
+ * be reset on the next call of validateFullBounds.
+ *
+ * @return true if this node's bounds have changed.
+ */
+ protected boolean getBoundsChanged() {
+ return boundsChanged;
+ }
+
+ /**
+ * Set the bounds chnaged flag. This flag will be reset on the next call of
+ * validateFullBounds.
+ *
+ * @param boundsChanged true if this nodes bounds have changed.
+ */
+ protected void setBoundsChanged(final boolean boundsChanged) {
+ this.boundsChanged = boundsChanged;
+ }
+
+ /**
+ * Return true if the full bounds of this node are invalid. This means that
+ * the full bounds of this node have changed and need to be recomputed.
+ *
+ * @return true if the full bounds of this node are invalid
+ */
+ protected boolean getFullBoundsInvalid() {
+ return fullBoundsInvalid;
+ }
+
+ /**
+ * Set the full bounds invalid flag. This flag is set when the full bounds
+ * of this node need to be recomputed as is the case when this node is
+ * transformed or when one of this node's children changes geometry.
+ *
+ * @param fullBoundsInvalid true=invalid, false=valid
+ */
+ protected void setFullBoundsInvalid(final boolean fullBoundsInvalid) {
+ this.fullBoundsInvalid = fullBoundsInvalid;
+ }
+
+ /**
+ * Return true if one of this node's descendants has invalid bounds.
+ *
+ * @return whether child bounds are invalid
+ */
+ protected boolean getChildBoundsInvalid() {
+ return childBoundsInvalid;
+ }
+
+ /**
+ * Set the flag indicating that one of this node's descendants has invalid
+ * bounds.
+ *
+ * @param childBoundsInvalid true=invalid, false=valid
+ */
+ protected void setChildBoundsInvalid(final boolean childBoundsInvalid) {
+ this.childBoundsInvalid = childBoundsInvalid;
+ }
+
+ /**
+ * This method should be called when the bounds of this node are changed. It
+ * invalidates the full bounds of this node, and also notifies each of this
+ * nodes children that their parent's bounds have changed. As a result of
+ * this method getting called this nodes layoutChildren will be called.
+ */
+ public void signalBoundsChanged() {
+ invalidateFullBounds();
+ setBoundsChanged(true);
+ firePropertyChange(PROPERTY_CODE_BOUNDS, PROPERTY_BOUNDS, null, bounds);
+
+ final int count = getChildrenCount();
+ for (int i = 0; i < count; i++) {
+ final PNode each = (PNode) children.get(i);
+ each.parentBoundsChanged();
+ }
+ }
+
+ /**
+ * Invalidate this node's layout, so that later layoutChildren will get
+ * called.
+ */
+ public void invalidateLayout() {
+ invalidateFullBounds();
+ }
+
+ /**
+ * A notification that the bounds of this node's parent have changed.
+ */
+ protected void parentBoundsChanged() {
+ }
+
+ /**
+ * Invalidates the full bounds of this node, and sets the child bounds
+ * invalid flag on each of this node's ancestors.
+ */
+ public void invalidateFullBounds() {
+ setFullBoundsInvalid(true);
+
+ PNode n = parent;
+ while (n != null && !n.getChildBoundsInvalid()) {
+ n.setChildBoundsInvalid(true);
+ n = n.parent;
+ }
+
+ if (SCENE_GRAPH_DELEGATE != null) {
+ SCENE_GRAPH_DELEGATE.nodeFullBoundsInvalidated(this);
+ }
+ }
+
+ /**
+ * This method is called to validate the bounds of this node and all of its
+ * descendants. It returns true if this nodes bounds or the bounds of any of
+ * its descendants are marked as volatile.
+ *
+ * @return true if this node or any of its descendants have volatile bounds
+ */
+ protected boolean validateFullBounds() {
+ final boolean boundsVolatile = getBoundsVolatile();
+
+ // 1. Only compute new bounds if invalid flags are set.
+ if (fullBoundsInvalid || childBoundsInvalid || boundsVolatile || childBoundsVolatile) {
+
+ // 2. If my bounds are volatile and they have not been changed then
+ // signal a change.
+ //
+ // For most cases this will do nothing, but if a nodes bounds depend
+ // on its model, then
+ // validate bounds has the responsibility of making the bounds match
+ // the models value.
+ // For example PPaths validateBounds method makes sure that the
+ // bounds are equal to the
+ // bounds of the GeneralPath model.
+ if (boundsVolatile && !boundsChanged) {
+ signalBoundsChanged();
+ }
+
+ // 3. If the bounds of on of my decendents are invalidate then
+ // validate the bounds of all of my children.
+ if (childBoundsInvalid || childBoundsVolatile) {
+ childBoundsVolatile = false;
+ final int count = getChildrenCount();
+ for (int i = 0; i < count; i++) {
+ final PNode each = (PNode) children.get(i);
+ childBoundsVolatile |= each.validateFullBounds();
+ }
+ }
+
+ // 4. Now that my children's bounds are valid and my own bounds are
+ // valid run any layout algorithm here. Note that if you try to
+ // layout volatile
+ // children piccolo will most likely start a "soft" infinite loop.
+ // It won't freeze
+ // your program, but it will make an infinite number of calls to
+ // SwingUtilities
+ // invoke later. You don't want to do that.
+ layoutChildren();
+
+ // 5. If the full bounds cache is invalid then recompute the full
+ // bounds cache here after our own bounds and the children's bounds
+ // have been computed above.
+ if (fullBoundsInvalid) {
+ // 6. This will call getFullBoundsReference on all of the
+ // children. So if the above
+ // layoutChildren method changed the bounds of any of the
+ // children they will be
+ // validated again here.
+ PBounds oldFullBoundsCache = fullBoundsCache;
+ fullBoundsCache = computeFullBounds(fullBoundsCache);
+
+ final boolean fullBoundsChanged = !oldFullBoundsCache.equals(fullBoundsCache);
+
+ // 7. If the new full bounds cache differs from the previous
+ // cache then
+ // tell our parent to invalidate their full bounds. This is how
+ // bounds changes
+ // deep in the tree percolate up.
+ if (fullBoundsChanged) {
+ if (parent != null) {
+ parent.invalidateFullBounds();
+ }
+
+ firePropertyChange(PROPERTY_CODE_FULL_BOUNDS, PROPERTY_FULL_BOUNDS, null, fullBoundsCache);
+
+ // 8. If our paint was invalid make sure to repaint our old
+ // full bounds. The
+ // new bounds will be computed later in the validatePaint
+ // pass.
+ if (paintInvalid && !oldFullBoundsCache.isEmpty()) {
+ TEMP_REPAINT_BOUNDS.setRect(oldFullBoundsCache.x, oldFullBoundsCache.getY(), oldFullBoundsCache
+ .getWidth(), oldFullBoundsCache.getHeight());
+ repaintFrom(TEMP_REPAINT_BOUNDS, this);
+ }
+ }
+ }
+
+ // 9. Clear the invalid bounds flags.
+ boundsChanged = false;
+ fullBoundsInvalid = false;
+ childBoundsInvalid = false;
+ }
+
+ return boundsVolatile || childBoundsVolatile;
+ }
+
+ /**
+ * Nodes that apply layout constraints to their children should override
+ * this method and do the layout there.
+ */
+ protected void layoutChildren() {
+ }
+
+ // ****************************************************************
+ // Node Transform - Methods to manipulate the node's transform.
+ //
+ // Each node has a transform that is used to define the nodes
+ // local coordinate system. IE it is applied before picking and
+ // rendering the node.
+ //
+ // The usual way to move nodes about on the canvas is to manipulate
+ // this transform, as opposed to changing the bounds of the
+ // node.
+ //
+ // Since this transform defines the local coordinate system of this
+ // node the following methods with affect the global position both
+ // this node and all of its descendants.
+ // ****************************************************************
+
+ /**
+ * Returns the rotation applied by this node's transform in radians. This
+ * rotation affects this node and all its descendants. The value returned
+ * will be between 0 and 2pi radians.
+ *
+ * @return rotation in radians.
+ */
+ public double getRotation() {
+ if (transform == null) {
+ return 0;
+ }
+ return transform.getRotation();
+ }
+
+ /**
+ * Sets the rotation of this nodes transform in radians. This will affect
+ * this node and all its descendents.
+ *
+ * @param theta rotation in radians
+ */
+ public void setRotation(final double theta) {
+ rotate(theta - getRotation());
+ }
+
+ /**
+ * Rotates this node by theta (in radians) about the 0,0 point. This will
+ * affect this node and all its descendants.
+ *
+ * @param theta the amount to rotate by in radians
+ */
+ public void rotate(final double theta) {
+ rotateAboutPoint(theta, 0, 0);
+ }
+
+ /**
+ * Rotates this node by theta (in radians), and then translates the node so
+ * that the x, y position of its fullBounds stays constant.
+ *
+ * @param theta the amount to rotate by in radians
+ */
+ public void rotateInPlace(final double theta) {
+ PBounds b = getFullBoundsReference();
+ final double px = b.x;
+ final double py = b.y;
+ rotateAboutPoint(theta, 0, 0);
+ b = getFullBoundsReference();
+ offset(px - b.x, py - b.y);
+ }
+
+ /**
+ * Rotates this node by theta (in radians) about the given point. This will
+ * affect this node and all its descendants.
+ *
+ * @param theta the amount to rotate by in radians
+ * @param point the point about which to rotate
+ */
+ public void rotateAboutPoint(final double theta, final Point2D point) {
+ rotateAboutPoint(theta, point.getX(), point.getY());
+ }
+
+ /**
+ * Rotates this node by theta (in radians) about the given point. This will
+ * affect this node and all its descendants.
+ *
+ * @param theta the amount to rotate by in radians
+ * @param x the x coordinate of the point around which to rotate
+ * @param y the y coordinate of the point around which to rotate
+ */
+ public void rotateAboutPoint(final double theta, final double x, final double y) {
+ getTransformReference(true).rotate(theta, x, y);
+ invalidatePaint();
+ invalidateFullBounds();
+ firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
+ }
+
+ /**
+ * Return the total amount of rotation applied to this node by its own
+ * transform together with the transforms of all its ancestors. The value
+ * returned will be between 0 and 2pi radians.
+ *
+ * @return the total amount of rotation applied to this node in radians
+ */
+ public double getGlobalRotation() {
+ return getLocalToGlobalTransform(null).getRotation();
+ }
+
+ /**
+ * Set the global rotation (in radians) of this node. This is implemented by
+ * rotating this nodes transform the required amount so that the nodes
+ * global rotation is as requested.
+ *
+ * @param theta the amount to rotate by in radians relative to the global
+ * coordinate system.
+ */
+ public void setGlobalRotation(final double theta) {
+ if (parent != null) {
+ setRotation(theta - parent.getGlobalRotation());
+ }
+ else {
+ setRotation(theta);
+ }
+ }
+
+ /**
+ * Return the scale applied by this node's transform. The scale is effecting
+ * this node and all its descendants.
+ *
+ * @return scale applied by this nodes transform.
+ */
+ public double getScale() {
+ if (transform == null) {
+ return 1;
+ }
+ return transform.getScale();
+ }
+
+ /**
+ * Set the scale of this node's transform. The scale will affect this node
+ * and all its descendants.
+ *
+ * @param scale the scale to set the transform to
+ */
+ public void setScale(final double scale) {
+ if (scale == 0) {
+ throw new RuntimeException("Can't set scale to 0");
+ }
+ scale(scale / getScale());
+ }
+
+ /**
+ * Scale this nodes transform by the given amount. This will affect this
+ * node and all of its descendants.
+ *
+ * @param scale the amount to scale by
+ */
+ public void scale(final double scale) {
+ scaleAboutPoint(scale, 0, 0);
+ }
+
+ /**
+ * Scale this nodes transform by the given amount about the specified point.
+ * This will affect this node and all of its descendants.
+ *
+ * @param scale the amount to scale by
+ * @param point the point to scale about
+ */
+ public void scaleAboutPoint(final double scale, final Point2D point) {
+ scaleAboutPoint(scale, point.getX(), point.getY());
+ }
+
+ /**
+ * Scale this nodes transform by the given amount about the specified point.
+ * This will affect this node and all of its descendants.
+ *
+ * @param scale the amount to scale by
+ * @param x the x coordinate of the point around which to scale
+ * @param y the y coordinate of the point around which to scale
+ */
+ public void scaleAboutPoint(final double scale, final double x, final double y) {
+ getTransformReference(true).scaleAboutPoint(scale, x, y);
+ invalidatePaint();
+ invalidateFullBounds();
+ firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
+ }
+
+ /**
+ * Return the global scale that is being applied to this node by its
+ * transform together with the transforms of all its ancestors.
+ *
+ * @return global scale of this node
+ */
+ public double getGlobalScale() {
+ return getLocalToGlobalTransform(null).getScale();
+ }
+
+ /**
+ * Set the global scale of this node. This is implemented by scaling this
+ * nodes transform the required amount so that the nodes global scale is as
+ * requested.
+ *
+ * @param scale the desired global scale
+ */
+ public void setGlobalScale(final double scale) {
+ if (parent != null) {
+ setScale(scale / parent.getGlobalScale());
+ }
+ else {
+ setScale(scale);
+ }
+ }
+
+ /**
+ * Returns the x offset of this node as applied by its transform.
+ *
+ * @return x offset of this node as applied by its transform
+ */
+ public double getXOffset() {
+ if (transform == null) {
+ return 0;
+ }
+ return transform.getTranslateX();
+ }
+
+ /**
+ * Returns the y offset of this node as applied by its transform.
+ *
+ * @return y offset of this node as applied by its transform
+ */
+ public double getYOffset() {
+ if (transform == null) {
+ return 0;
+ }
+ return transform.getTranslateY();
+ }
+
+ /**
+ * Return the offset that is being applied to this node by its transform.
+ * This offset effects this node and all of its descendants and is specified
+ * in the parent coordinate system. This returns the values that are in the
+ * m02 and m12 positions in the affine transform.
+ *
+ * @return a point representing the x and y offset
+ */
+ public Point2D getOffset() {
+ if (transform == null) {
+ return new Point2D.Double();
+ }
+ return new Point2D.Double(transform.getTranslateX(), transform.getTranslateY());
+ }
+
+ /**
+ * Set the offset that is being applied to this node by its transform. This
+ * offset effects this node and all of its descendants and is specified in
+ * the nodes parent coordinate system. This directly sets the values of the
+ * m02 and m12 positions in the affine transform. Unlike "PNode.translate()"
+ * it is not effected by the transforms scale.
+ *
+ * @param point value of new offset
+ */
+ public void setOffset(final Point2D point) {
+ setOffset(point.getX(), point.getY());
+ }
+
+ /**
+ * Set the offset that is being applied to this node by its transform. This
+ * offset effects this node and all of its descendants and is specified in
+ * the nodes parent coordinate system. This directly sets the values of the
+ * m02 and m12 positions in the affine transform. Unlike "PNode.translate()"
+ * it is not effected by the transforms scale.
+ *
+ * @param x amount of x offset
+ * @param y amount of y offset
+ */
+ public void setOffset(final double x, final double y) {
+ getTransformReference(true).setOffset(x, y);
+ invalidatePaint();
+ invalidateFullBounds();
+ firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
+ }
+
+ /**
+ * Offset this node relative to the parents coordinate system, and is NOT
+ * effected by this nodes current scale or rotation. This is implemented by
+ * directly adding dx to the m02 position and dy to the m12 position in the
+ * affine transform.
+ *
+ * @param dx amount to add to this nodes current x Offset
+ * @param dy amount to add to this nodes current y Offset
+ */
+ public void offset(final double dx, final double dy) {
+ getTransformReference(true);
+ setOffset(transform.getTranslateX() + dx, transform.getTranslateY() + dy);
+ }
+
+ /**
+ * Translate this node's transform by the given amount, using the standard
+ * affine transform translate method. This translation effects this node and
+ * all of its descendants.
+ *
+ * @param dx amount to add to this nodes current x translation
+ * @param dy amount to add to this nodes current y translation
+ */
+ public void translate(final double dx, final double dy) {
+ getTransformReference(true).translate(dx, dy);
+ invalidatePaint();
+ invalidateFullBounds();
+ firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
+ }
+
+ /**
+ * Return the global translation that is being applied to this node by its
+ * transform together with the transforms of all its ancestors.
+ *
+ * @return the global translation applied to this node
+ */
+ public Point2D getGlobalTranslation() {
+ final Point2D p = getOffset();
+ if (parent != null) {
+ parent.localToGlobal(p);
+ }
+ return p;
+ }
+
+ /**
+ * Set the global translation of this node. This is implemented by
+ * translating this nodes transform the required amount so that the nodes
+ * global scale is as requested.
+ *
+ * @param globalPoint the desired global translation
+ */
+ public void setGlobalTranslation(final Point2D globalPoint) {
+ if (parent != null) {
+ parent.getGlobalToLocalTransform(null).transform(globalPoint, globalPoint);
+ }
+ setOffset(globalPoint);
+ }
+
+ /**
+ * Transform this nodes transform by the given transform.
+ *
+ * @param aTransform the transform to apply.
+ */
+ public void transformBy(final AffineTransform aTransform) {
+ getTransformReference(true).concatenate(aTransform);
+ invalidatePaint();
+ invalidateFullBounds();
+ firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, transform);
+ }
+
+ /**
+ * Linearly interpolates between a and b, based on t. Specifically, it
+ * computes lerp(a, b, t) = a + t*(b - a). This produces a result that
+ * changes from a (when t = 0) to b (when t = 1).
+ *
+ * @param t variable 'time' parameter
+ * @param a from point
+ * @param b to Point
+ *
+ * @return linear interpolation between and b at time interval t (given as #
+ * between 0f and 1f)
+ */
+ public static double lerp(final double t, final double a, final double b) {
+ return a + t * (b - a);
+ }
+
+ /**
+ * This will calculate the necessary transform in order to make this node
+ * appear at a particular position relative to the specified bounding box.
+ * The source point specifies a point in the unit square (0, 0) - (1, 1)
+ * that represents an anchor point on the corresponding node to this
+ * transform. The destination point specifies an anchor point on the
+ * reference node. The position method then computes the transform that
+ * results in transforming this node so that the source anchor point
+ * coincides with the reference anchor point. This can be useful for layout
+ * algorithms as it is straightforward to position one object relative to
+ * another.
+ *
+ * For example, If you have two nodes, A and B, and you call + * + *
+ * Point2D srcPt = new Point2D.Double(1.0, 0.0); + * Point2D destPt = new Point2D.Double(0.0, 0.0); + * A.position(srcPt, destPt, B.getGlobalBounds(), 750, null); + *+ * + * The result is that A will move so that its upper-right corner is at the + * same place as the upper-left corner of B, and the transition will be + * smoothly animated over a period of 750 milliseconds. + * + * @since 1.3 + * @param srcPt The anchor point on this transform's node (normalized to a + * unit square) + * @param destPt The anchor point on destination bounds (normalized to a + * unit square) + * @param destBounds The bounds (in global coordinates) used to calculate + * this transform's node + * @param millis Number of milliseconds over which to perform the animation + * + * @return newly scheduled activity or node if activity could not be + * scheduled + */ + public PActivity animateToRelativePosition(final Point2D srcPt, final Point2D destPt, final Rectangle2D destBounds, + final int millis) { + double srcx, srcy; + double destx, desty; + double dx, dy; + Point2D pt1, pt2; + + if (parent == null) { + return null; + } + else { + // First compute translation amount in global coordinates + final Rectangle2D srcBounds = getGlobalFullBounds(); + srcx = lerp(srcPt.getX(), srcBounds.getX(), srcBounds.getX() + srcBounds.getWidth()); + srcy = lerp(srcPt.getY(), srcBounds.getY(), srcBounds.getY() + srcBounds.getHeight()); + destx = lerp(destPt.getX(), destBounds.getX(), destBounds.getX() + destBounds.getWidth()); + desty = lerp(destPt.getY(), destBounds.getY(), destBounds.getY() + destBounds.getHeight()); + + // Convert vector to local coordinates + pt1 = new Point2D.Double(srcx, srcy); + globalToLocal(pt1); + pt2 = new Point2D.Double(destx, desty); + globalToLocal(pt2); + dx = pt2.getX() - pt1.getX(); + dy = pt2.getY() - pt1.getY(); + + // Finally, animate change + final PAffineTransform at = new PAffineTransform(getTransformReference(true)); + at.translate(dx, dy); + return animateToTransform(at, millis); + } + } + + /** + * Return a copy of the transform associated with this node. + * + * @return copy of this node's transform + */ + public PAffineTransform getTransform() { + if (transform == null) { + return new PAffineTransform(); + } + else { + return (PAffineTransform) transform.clone(); + } + } + + /** + * Return a reference to the transform associated with this node. This + * returned transform should not be modified. PNode transforms are created + * lazily when needed. If you access the transform reference before the + * transform has been created it may return null. The + * createNewTransformIfNull parameter is used to specify that the PNode + * should create a new transform (and assign that transform to the nodes + * local transform variable) instead of returning null. + * + * @param createNewTransformIfNull if the transform has not been + * initialised, should it be? + * + * @return reference to this node's transform + */ + public PAffineTransform getTransformReference(final boolean createNewTransformIfNull) { + if (transform == null && createNewTransformIfNull) { + transform = new PAffineTransform(); + } + return transform; + } + + /** + * Return an inverted copy of the transform associated with this node. + * + * @return inverted copy of this node's transform + */ + public PAffineTransform getInverseTransform() { + if (transform == null) { + return new PAffineTransform(); + } + + try { + return new PAffineTransform(transform.createInverse()); + } + catch (final NoninvertibleTransformException e) { + throw new PAffineTransformException(e, transform); + } + } + + /** + * Set the transform applied to this node. + * + * @param transform the new transform value + */ + public void setTransform(final AffineTransform transform) { + if (transform == null) { + this.transform = null; + } + else { + getTransformReference(true).setTransform(transform); + } + + invalidatePaint(); + invalidateFullBounds(); + firePropertyChange(PROPERTY_CODE_TRANSFORM, PROPERTY_TRANSFORM, null, this.transform); + } + + // **************************************************************** + // Paint Damage Management - Methods used to invalidate the areas of + // the screen that this node appears in so that they will later get + // painted. + // + // Generally you will not need to call these invalidate methods + // when starting out with Piccolo2d because methods such as setPaint + // already automatically call them for you. You will need to call + // them when you start creating your own nodes. + // + // When you do create you own nodes the only method that you will + // normally need to call is invalidatePaint. This method marks the + // nodes as having invalid paint, the root node's UI cycle will then + // later discover this damage and report it to the Java repaint manager. + // + // Repainting is normally done with PNode.invalidatePaint() instead of + // directly calling PNode.repaint() because PNode.repaint() requires + // the nodes bounds to be computed right away. But with invalidatePaint + // the bounds computation can be delayed until the end of the root's UI + // cycle, and this can add up to a bit savings when modifying a + // large number of nodes all at once. + // + // The other methods here will rarely be called except internally + // from the framework. + // **************************************************************** + + /** + * Return true if this nodes paint is invalid, in which case the node needs + * to be repainted. + * + * @return true if this node needs to be repainted + */ + public boolean getPaintInvalid() { + return paintInvalid; + } + + /** + * Mark this node as having invalid paint. If this is set the node will + * later be repainted. Node this method is most often used internally. + * + * @param paintInvalid true if this node should be repainted + */ + public void setPaintInvalid(final boolean paintInvalid) { + this.paintInvalid = paintInvalid; + } + + /** + * Return true if this node has a child with invalid paint. + * + * @return true if this node has a child with invalid paint + */ + public boolean getChildPaintInvalid() { + return childPaintInvalid; + } + + /** + * Mark this node as having a child with invalid paint. + * + * @param childPaintInvalid true if this node has a child with invalid paint + */ + public void setChildPaintInvalid(final boolean childPaintInvalid) { + this.childPaintInvalid = childPaintInvalid; + } + + /** + * Invalidate this node's paint, and mark all of its ancestors as having a + * node with invalid paint. + */ + public void invalidatePaint() { + setPaintInvalid(true); + + PNode n = parent; + while (n != null && !n.getChildPaintInvalid()) { + n.setChildPaintInvalid(true); + n = n.parent; + } + + if (SCENE_GRAPH_DELEGATE != null) { + SCENE_GRAPH_DELEGATE.nodePaintInvalidated(this); + } + } + + /** + * Repaint this node and any of its descendants if they have invalid paint. + */ + public void validateFullPaint() { + if (getPaintInvalid()) { + repaint(); + setPaintInvalid(false); + } + + if (getChildPaintInvalid()) { + final int count = getChildrenCount(); + for (int i = 0; i < count; i++) { + final PNode each = (PNode) children.get(i); + each.validateFullPaint(); + } + setChildPaintInvalid(false); + } + } + + /** + * Mark the area on the screen represented by this nodes full bounds as + * needing a repaint. + */ + public void repaint() { + TEMP_REPAINT_BOUNDS.setRect(getFullBoundsReference()); + repaintFrom(TEMP_REPAINT_BOUNDS, this); + } + + /** + * Pass the given repaint request up the tree, so that any cameras can + * invalidate that region on their associated canvas. + * + * @param localBounds the bounds to repaint + * @param childOrThis if childOrThis does not equal this then this nodes + * transform will be applied to the localBounds param + */ + public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { + if (parent != null) { + if (childOrThis != this) { + localToParent(localBounds); + } + else if (!getVisible()) { + return; + } + parent.repaintFrom(localBounds, this); + } + } + + // **************************************************************** + // Occluding - Methods to support occluding optimisation. Not yet + // complete. + // **************************************************************** + + /** + * Returns whether this node is Opaque. + * + * @param boundary boundary to check and see if this node covers completely. + * + * @return true if opaque + */ + public boolean isOpaque(final Rectangle2D boundary) { + return false; + } + + /** + * Returns whether this node has been flagged as occluded. + * + * @return true if occluded + */ + public boolean getOccluded() { + return occluded; + } + + /** + * Flags this node as occluded. + * + * @param occluded new value for occluded + */ + public void setOccluded(final boolean occluded) { + this.occluded = occluded; + } + + // **************************************************************** + // Painting - Methods for painting this node and its children + // + // Painting is how a node defines its visual representation on the + // screen, and is done in the local coordinate system of the node. + // + // The default painting behavior is to first paint the node, and + // then paint the node's children on top of the node. If a node + // needs wants specialised painting behavior it can override: + // + // paint() - Painting here will happen before the children + // are painted, so the children will be painted on top of painting done + // here. + // paintAfterChildren() - Painting here will happen after the children + // are painted, so it will paint on top of them. + // + // Note that you should not normally need to override fullPaint(). + // + // The visible flag can be used to make a node invisible so that + // it will never get painted. + // **************************************************************** + + /** + * Return true if this node is visible, that is if it will paint itself and + * descendants. + * + * @return true if this node and its descendants are visible. + */ + public boolean getVisible() { + return visible; + } + + /** + * Set the visibility of this node and its descendants. + * + * @param isVisible true if this node and its descendants are visible + */ + public void setVisible(final boolean isVisible) { + if (getVisible() != isVisible) { + if (!isVisible) { + repaint(); + } + visible = isVisible; + firePropertyChange(PROPERTY_CODE_VISIBLE, PROPERTY_VISIBLE, null, null); + invalidatePaint(); + } + } + + /** + * Return the paint used while painting this node. This value may be null. + * + * @return the paint used while painting this node. + */ + public Paint getPaint() { + return paint; + } + + /** + * Set the paint used to paint this node, which may be null. + * + * @param newPaint paint that this node should use when painting itself. + */ + public void setPaint(final Paint newPaint) { + if (paint == newPaint) { + return; + } + + final Paint oldPaint = paint; + paint = newPaint; + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_PAINT, PROPERTY_PAINT, oldPaint, paint); + } + + /** + * Return the transparency used when painting this node. Note that this + * transparency is also applied to all of the node's descendants. + * + * @return how transparent this node is 0f = completely transparent, 1f = + * completely opaque + */ + public float getTransparency() { + return transparency; + } + + /** + * Set the transparency used to paint this node. Note that this transparency + * applies to this node and all of its descendants. + * + * @param newTransparency transparency value for this node. 0f = fully + * transparent, 1f = fully opaque + */ + public void setTransparency(final float newTransparency) { + if (Math.abs(transparency - newTransparency) > TRANSPARENCY_RESOLUTION) { + final float oldTransparency = transparency; + transparency = newTransparency; + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_TRANSPARENCY, PROPERTY_TRANSPARENCY, new Float(oldTransparency), + new Float(newTransparency)); + } + } + + /** + * Paint this node behind any of its children nodes. Subclasses that define + * a different appearance should override this method and paint themselves + * there. + * + * @param paintContext the paint context to use for painting the node + */ + protected void paint(final PPaintContext paintContext) { + if (paint != null) { + final Graphics2D g2 = paintContext.getGraphics(); + g2.setPaint(paint); + g2.fill(getBoundsReference()); + } + } + + /** + * Paint this node and all of its descendants. Most subclasses do not need + * to override this method, they should override
paint
or
+ * paintAfterChildren
instead.
+ *
+ * @param paintContext the paint context to use for painting this node and
+ * its children
+ */
+ public void fullPaint(final PPaintContext paintContext) {
+ if (getVisible() && fullIntersects(paintContext.getLocalClip())) {
+ paintContext.pushTransform(transform);
+ paintContext.pushTransparency(transparency);
+
+ if (!getOccluded()) {
+ paint(paintContext);
+ }
+
+ final int count = getChildrenCount();
+ for (int i = 0; i < count; i++) {
+ final PNode each = (PNode) children.get(i);
+ each.fullPaint(paintContext);
+ }
+
+ paintAfterChildren(paintContext);
+
+ paintContext.popTransparency(transparency);
+ paintContext.popTransform(transform);
+ }
+ }
+
+ /**
+ * Subclasses that wish to do additional painting after their children are
+ * painted should override this method and do that painting here.
+ *
+ * @param paintContext the paint context to sue for painting after the
+ * children are painted
+ */
+ protected void paintAfterChildren(final PPaintContext paintContext) {
+ }
+
+ /**
+ * Return a new Image representing this node and all of its children. The
+ * image size will be equal to the size of this nodes full bounds.
+ *
+ * @return a new image representing this node and its descendants
+ */
+ public Image toImage() {
+ final PBounds b = getFullBoundsReference();
+ return toImage((int) Math.ceil(b.getWidth()), (int) Math.ceil(b.getHeight()), null);
+ }
+
+ /**
+ * Return a new Image of the requested size representing this node and all
+ * of its children. If backGroundPaint is null the resulting image will have
+ * transparent regions, otherwise those regions will be filled with the
+ * backgroundPaint.
+ *
+ * @param width pixel width of the resulting image
+ * @param height pixel height of the resulting image
+ * @param backgroundPaint paint to fill the image with before drawing this
+ * node, may be null
+ *
+ * @return a new image representing this node and its descendants
+ */
+ public Image toImage(final int width, final int height, final Paint backgroundPaint) {
+ BufferedImage result;
+
+ if (GraphicsEnvironment.isHeadless()) {
+ result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ }
+ else {
+ final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment()
+ .getDefaultScreenDevice().getDefaultConfiguration();
+ result = graphicsConfiguration.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
+ }
+
+ return toImage(result, backgroundPaint);
+ }
+
+ /**
+ * Paint a representation of this node into the specified buffered image. If
+ * background, paint is null, then the image will not be filled with a color
+ * prior to rendering
+ *
+ * @param image Image onto which this node will be painted
+ * @param backGroundPaint will fill background of image with this. May be
+ * null.
+ * @return a rendering of this image and its descendants onto the specified
+ * image
+ */
+ public Image toImage(final BufferedImage image, final Paint backGroundPaint) {
+ return toImage(image, backGroundPaint, FILL_STRATEGY_ASPECT_FIT);
+ }
+
+ /**
+ * Paint a representation of this node into the specified buffered image. If
+ * background, paint is null, then the image will not be filled with a color
+ * prior to rendering
+ *
+ * @since 1.3
+ * @param image Image onto which this node will be painted
+ * @param backGroundPaint will fill background of image with this. May be
+ * null.
+ * @param fillStrategy strategy to use regarding how node will cover the
+ * image
+ * @return a rendering of this image and its descendants onto the specified
+ * image
+ */
+ 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();
+
+ if (backGroundPaint != null) {
+ g2.setPaint(backGroundPaint);
+ g2.fillRect(0, 0, imageWidth, imageHeight);
+ }
+ g2.setClip(0, 0, imageWidth, imageHeight);
+
+ final PBounds nodeBounds = getFullBounds();
+ nodeBounds.expandNearestIntegerDimensions();
+
+ final double nodeWidth = nodeBounds.getWidth();
+ final double nodeHeight = nodeBounds.getHeight();
+
+ double imageRatio = imageWidth / (imageHeight * 1.0);
+ double nodeRatio = nodeWidth / nodeHeight;
+ double scale;
+ switch (fillStrategy) {
+ case FILL_STRATEGY_ASPECT_FIT:
+ // scale the graphics so node's full bounds fit in the imageable
+ // bounds but aspect ration is retained
+
+ if (nodeRatio <= imageRatio) {
+ scale = image.getHeight() / nodeHeight;
+ }
+ else {
+ scale = image.getWidth() / nodeWidth;
+ }
+ g2.scale(scale, scale);
+ g2.translate(-nodeBounds.x, -nodeBounds.y);
+ break;
+ case FILL_STRATEGY_ASPECT_COVER:
+ // scale the graphics so node completely covers the imageable
+ // area, but retains its aspect ratio.
+ if (nodeRatio <= imageRatio) {
+ scale = image.getWidth() / nodeWidth;
+ }
+ else {
+ scale = image.getHeight() / nodeHeight;
+ }
+ g2.scale(scale, scale);
+ break;
+ case FILL_STRATEGY_EXACT_FIT:
+ // scale the node so that it covers then entire image,
+ // distorting it if necessary.
+ g2.scale(image.getWidth() / nodeWidth, image.getHeight() / nodeHeight);
+ g2.translate(-nodeBounds.x, -nodeBounds.y);
+ break;
+ default:
+ throw new IllegalArgumentException("Fill strategy provided is invalid");
+ }
+
+ final PPaintContext pc = new PPaintContext(g2);
+ pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
+ fullPaint(pc);
+ return image;
+ }
+
+ /**
+ * Constructs a new PrinterJob, allows the user to select which printer to
+ * print to, And then prints the node.
+ * @throws PrinterException
+ */
+ public void print() throws PrinterException {
+ final PrinterJob printJob = PrinterJob.getPrinterJob();
+ final PageFormat pageFormat = printJob.defaultPage();
+ final Book book = new Book();
+ book.append(this, pageFormat);
+ printJob.setPageable(book);
+
+ if (printJob.printDialog()) {
+ printJob.print();
+ }
+ }
+
+ /**
+ * Prints the node into the given Graphics context using the specified
+ * format. The zero based index of the requested page is specified by
+ * pageIndex. If the requested page does not exist then this method returns
+ * NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned. If the printable object
+ * aborts the print job then it throws a PrinterException.
+ *
+ * @param graphics the context into which the node is drawn
+ * @param pageFormat the size and orientation of the page
+ * @param pageIndex the zero based index of the page to be drawn
+ *
+ * @return Either NO_SUCH_PAGE or PAGE_EXISTS
+ */
+ public int print(final Graphics graphics, final PageFormat pageFormat, final int pageIndex) {
+ if (pageIndex != 0) {
+ return NO_SUCH_PAGE;
+ }
+
+ if (!(graphics instanceof Graphics2D)) {
+ throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object");
+ }
+
+ final Graphics2D g2 = (Graphics2D) graphics;
+ final PBounds imageBounds = getFullBounds();
+
+ imageBounds.expandNearestIntegerDimensions();
+
+ g2.setClip(0, 0, (int) pageFormat.getWidth(), (int) pageFormat.getHeight());
+ g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
+
+ // scale the graphics so node's full bounds fit in the imageable bounds.
+ double scale = pageFormat.getImageableWidth() / imageBounds.getWidth();
+ if (pageFormat.getImageableHeight() / imageBounds.getHeight() < scale) {
+ scale = pageFormat.getImageableHeight() / imageBounds.getHeight();
+ }
+
+ g2.scale(scale, scale);
+ g2.translate(-imageBounds.x, -imageBounds.y);
+
+ final PPaintContext pc = new PPaintContext(g2);
+ pc.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
+ fullPaint(pc);
+
+ return PAGE_EXISTS;
+ }
+
+ // ****************************************************************
+ // Picking - Methods for picking this node and its children.
+ //
+ // Picking is used to determine the node that intersects a point or
+ // rectangle on the screen. It is most frequently used by the
+ // PInputManager to determine the node that the cursor is over.
+ //
+ // The intersects() method is used to determine if a node has
+ // been picked or not. The default implementation just test to see
+ // if the pick bounds intersects the bounds of the node. Subclasses
+ // whose geometry (a circle for example) does not match up exactly with
+ // the bounds should override the intersects() method.
+ //
+ // The default picking behavior is to first try to pick the nodes
+ // children, and then try to pick the nodes own bounds. If a node
+ // wants specialized picking behavior it can override:
+ //
+ // pick() - Pick nodes here that should be picked before the nodes
+ // children are picked.
+ // pickAfterChildren() - Pick nodes here that should be picked after the
+ // node's children are picked.
+ //
+ // Note that fullPick should not normally be overridden.
+ //
+ // The pickable and childrenPickable flags can be used to make a
+ // node or it children not pickable even if their geometry does
+ // intersect the pick bounds.
+ // ****************************************************************
+
+ /**
+ * Return true if this node is pickable. Only pickable nodes can receive
+ * input events. Nodes are pickable by default.
+ *
+ * @return true if this node is pickable
+ */
+ public boolean getPickable() {
+ return pickable;
+ }
+
+ /**
+ * Set the pickable flag for this node. Only pickable nodes can receive
+ * input events. Nodes are pickable by default.
+ *
+ * @param isPickable true if this node is pickable
+ */
+ public void setPickable(final boolean isPickable) {
+ if (getPickable() != isPickable) {
+ pickable = isPickable;
+ firePropertyChange(PROPERTY_CODE_PICKABLE, PROPERTY_PICKABLE, null, null);
+ }
+ }
+
+ /**
+ * Return true if the children of this node should be picked. If this flag
+ * is false then this node will not try to pick its children. Children are
+ * pickable by default.
+ *
+ * @return true if this node tries to pick its children
+ */
+ public boolean getChildrenPickable() {
+ return childrenPickable;
+ }
+
+ /**
+ * Set the children pickable flag. If this flag is false then this node will
+ * not try to pick its children. Children are pickable by default.
+ *
+ * @param areChildrenPickable true if this node tries to pick its children
+ */
+ public void setChildrenPickable(final boolean areChildrenPickable) {
+ if (getChildrenPickable() != areChildrenPickable) {
+ childrenPickable = areChildrenPickable;
+ firePropertyChange(PROPERTY_CODE_CHILDREN_PICKABLE, PROPERTY_CHILDREN_PICKABLE, null, null);
+ }
+ }
+
+ /**
+ * Try to pick this node before its children have had a chance to be picked.
+ * Nodes that paint on top of their children may want to override this
+ * method to if the pick path intersects that paint.
+ *
+ * @param pickPath the pick path used for the pick operation
+ * @return true if this node was picked
+ */
+ protected boolean pick(final PPickPath pickPath) {
+ return false;
+ }
+
+ /**
+ * Try to pick this node and all of its descendants. Most subclasses should
+ * not need to override this method. Instead they should override
+ * pick
or pickAfterChildren
.
+ *
+ * @param pickPath the pick path to add the node to if its picked
+ * @return true if this node or one of its descendants was picked.
+ */
+ public boolean fullPick(final PPickPath pickPath) {
+ if (getVisible() && (getPickable() || getChildrenPickable()) && fullIntersects(pickPath.getPickBounds())) {
+ pickPath.pushNode(this);
+ pickPath.pushTransform(transform);
+
+ final boolean thisPickable = getPickable() && pickPath.acceptsNode(this);
+
+ if (thisPickable && pick(pickPath)) {
+ return true;
+ }
+
+ if (getChildrenPickable()) {
+ final int count = getChildrenCount();
+ for (int i = count - 1; i >= 0; i--) {
+ final PNode each = (PNode) children.get(i);
+ if (each.fullPick(pickPath)) {
+ return true;
+ }
+ }
+ }
+
+ if (thisPickable && pickAfterChildren(pickPath)) {
+ return true;
+ }
+
+ pickPath.popTransform(transform);
+ pickPath.popNode(this);
+ }
+
+ return false;
+ }
+
+ /**
+ * Finds all descendants of this node that intersect with the given bounds
+ * and adds them to the results array.
+ *
+ * @param fullBounds bounds to compare against
+ * @param results array into which to add matches
+ */
+ public void findIntersectingNodes(final Rectangle2D fullBounds, final ArrayList results) {
+ if (fullIntersects(fullBounds)) {
+ final Rectangle2D localBounds = parentToLocal((Rectangle2D) fullBounds.clone());
+
+ if (intersects(localBounds)) {
+ results.add(this);
+ }
+
+ final int count = getChildrenCount();
+ for (int i = count - 1; i >= 0; i--) {
+ final PNode each = (PNode) children.get(i);
+ each.findIntersectingNodes(localBounds, results);
+ }
+ }
+ }
+
+ /**
+ * Try to pick this node after its children have had a chance to be picked.
+ * Most subclasses the define a different geometry will need to override
+ * this method.
+ *
+ * @param pickPath the pick path used for the pick operation
+ * @return true if this node was picked
+ */
+ protected boolean pickAfterChildren(final PPickPath pickPath) {
+ if (intersects(pickPath.getPickBounds())) {
+ return true;
+ }
+ return false;
+ }
+
+ // ****************************************************************
+ // Structure - Methods for manipulating and traversing the
+ // parent child relationship
+ //
+ // Most of these methods won't need to be overridden by subclasses
+ // but you will use them frequently to build up your node structures.
+ // ****************************************************************
+
+ /**
+ * Add a node to be a new child of this node. The new node is added to the
+ * end of the list of this node's children. If child was previously a child
+ * of another node, it is removed from that first.
+ *
+ * @param child the new child to add to this node
+ */
+ public void addChild(final PNode child) {
+ int insertIndex = getChildrenCount();
+ if (child.parent == this) {
+ insertIndex--;
+ }
+ addChild(insertIndex, child);
+ }
+
+ /**
+ * Add a node to be a new child of this node at the specified index. If
+ * child was previously a child of another node, it is removed from that
+ * node first.
+ *
+ * @param index where in the children list to insert the child
+ * @param child the new child to add to this node
+ */
+ public void addChild(final int index, final PNode child) {
+ final PNode oldParent = child.getParent();
+
+ if (oldParent != null) {
+ oldParent.removeChild(child);
+ }
+
+ child.setParent(this);
+ getChildrenReference().add(index, child);
+ child.invalidatePaint();
+ invalidateFullBounds();
+
+ firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children);
+ }
+
+ /**
+ * Add a collection of nodes to be children of this node. If these nodes
+ * already have parents they will first be removed from those parents.
+ *
+ * @param nodes a collection of nodes to be added to this node
+ */
+ public void addChildren(final Collection nodes) {
+ final Iterator i = nodes.iterator();
+ while (i.hasNext()) {
+ final PNode each = (PNode) i.next();
+ addChild(each);
+ }
+ }
+
+ /**
+ * Return true if this node is an ancestor of the parameter node.
+ *
+ * @param node a possible descendant node
+ * @return true if this node is an ancestor of the given node
+ */
+ public boolean isAncestorOf(final PNode node) {
+ PNode p = node.parent;
+ while (p != null) {
+ if (p == this) {
+ return true;
+ }
+ p = p.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Return true if this node is a descendant of the parameter node.
+ *
+ * @param node a possible ancestor node
+ * @return true if this nodes descends from the given node
+ */
+ public boolean isDescendentOf(final PNode node) {
+ PNode p = parent;
+ while (p != null) {
+ if (p == node) {
+ return true;
+ }
+ p = p.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Return true if this node descends from the root.
+ *
+ * @return whether this node descends from root node
+ */
+ public boolean isDescendentOfRoot() {
+ return getRoot() != null;
+ }
+
+ /**
+ * Change the order of this node in its parent's children list so that it
+ * will draw in back of all of its other sibling nodes.
+ */
+ public void moveToBack() {
+ final PNode p = parent;
+ if (p != null) {
+ p.removeChild(this);
+ p.addChild(0, this);
+ }
+ }
+
+ /**
+ * Change the order of this node in its parent's children list so that it
+ * will draw in back of the specified sibling node.
+ *
+ * @param sibling sibling to move in back of
+ */
+ public void moveInBackOf(final PNode sibling) {
+ final PNode p = parent;
+ if (p != null && p == sibling.getParent()) {
+ p.removeChild(this);
+ final int index = p.indexOfChild(sibling);
+ p.addChild(index, this);
+ }
+ }
+
+ /**
+ * Change the order of this node in its parent's children list so that it
+ * will draw in front of all of its other sibling nodes.
+ */
+ public void moveToFront() {
+ final PNode p = parent;
+ if (p != null) {
+ p.removeChild(this);
+ p.addChild(this);
+ }
+ }
+
+ /**
+ * Change the order of this node in its parent's children list so that it
+ * will draw in front of the specified sibling node.
+ *
+ * @param sibling sibling to move in front of
+ */
+ public void moveInFrontOf(final PNode sibling) {
+ final PNode p = parent;
+ if (p != null && p == sibling.getParent()) {
+ p.removeChild(this);
+ final int index = p.indexOfChild(sibling);
+ p.addChild(index + 1, this);
+ }
+ }
+
+ /**
+ * Return the parent of this node. This will be null if this node has not
+ * been added to a parent yet.
+ *
+ * @return this nodes parent or null
+ */
+ public PNode getParent() {
+ return parent;
+ }
+
+ /**
+ * Set the parent of this node. Note this is set automatically when adding
+ * and removing children.
+ *
+ * @param newParent the parent to which this node should be added
+ */
+ public void setParent(final PNode newParent) {
+ final PNode old = parent;
+ parent = newParent;
+ firePropertyChange(PROPERTY_CODE_PARENT, PROPERTY_PARENT, old, parent);
+ }
+
+ /**
+ * Return the index where the given child is stored.
+ *
+ * @param child child so search for
+ * @return index of child or -1 if not found
+ */
+ public int indexOfChild(final PNode child) {
+ if (children == null) {
+ return -1;
+ }
+ return children.indexOf(child);
+ }
+
+ /**
+ * Remove the given child from this node's children list. Any subsequent
+ * children are shifted to the left (one is subtracted from their indices).
+ * The removed child's parent is set to null.
+ *
+ * @param child the child to remove
+ * @return the removed child
+ */
+ public PNode removeChild(final PNode child) {
+ final int index = indexOfChild(child);
+ if (index == -1) {
+ return null;
+ }
+ return removeChild(index);
+ }
+
+ /**
+ * Remove the child at the specified position of this group node's children.
+ * Any subsequent children are shifted to the left (one is subtracted from
+ * their indices). The removed child's parent is set to null.
+ *
+ * @param index the index of the child to remove
+ * @return the removed child
+ */
+ public PNode removeChild(final int index) {
+ if (children == null) {
+ return null;
+ }
+ final PNode child = (PNode) children.remove(index);
+
+ if (children.size() == 0) {
+ children = null;
+ }
+
+ child.repaint();
+ child.setParent(null);
+ invalidateFullBounds();
+
+ firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children);
+
+ return child;
+ }
+
+ /**
+ * Remove all the children in the given collection from this node's list of
+ * children. All removed nodes will have their parent set to null.
+ *
+ * @param childrenNodes the collection of children to remove
+ */
+ public void removeChildren(final Collection childrenNodes) {
+ final Iterator i = childrenNodes.iterator();
+ while (i.hasNext()) {
+ final PNode each = (PNode) i.next();
+ removeChild(each);
+ }
+ }
+
+ /**
+ * Remove all the children from this node. Node this method is more
+ * efficient then removing each child individually.
+ */
+ public void removeAllChildren() {
+ if (children != null) {
+ final int count = children.size();
+ for (int i = 0; i < count; i++) {
+ final PNode each = (PNode) children.get(i);
+ each.setParent(null);
+ }
+ children = null;
+ invalidatePaint();
+ invalidateFullBounds();
+
+ firePropertyChange(PROPERTY_CODE_CHILDREN, PROPERTY_CHILDREN, null, children);
+ }
+ }
+
+ /**
+ * Delete this node by removing it from its parent's list of children.
+ */
+ public void removeFromParent() {
+ if (parent != null) {
+ parent.removeChild(this);
+ }
+ }
+
+ /**
+ * Set the parent of this node, and transform the node in such a way that it
+ * doesn't move in global coordinates.
+ *
+ * @param newParent The new parent of this node.
+ */
+ public void reparent(final PNode newParent) {
+ final AffineTransform originalTransform = getLocalToGlobalTransform(null);
+ final AffineTransform newTransform = newParent.getGlobalToLocalTransform(null);
+ newTransform.concatenate(originalTransform);
+
+ removeFromParent();
+ setTransform(newTransform);
+ newParent.addChild(this);
+ computeFullBounds(fullBoundsCache);
+ }
+
+ /**
+ * Swaps this node out of the scene graph tree, and replaces it with the
+ * specified replacement node. This node is left dangling, and it is up to
+ * the caller to manage it. The replacement node will be added to this
+ * node's parent in the same position as this was. That is, if this was the
+ * 3rd child of its parent, then after calling replaceWith(), the
+ * replacement node will also be the 3rd child of its parent. If this node
+ * has no parent when replace is called, then nothing will be done at all.
+ *
+ * @param replacementNode the new node that replaces the current node in the
+ * scene graph tree.
+ */
+ public void replaceWith(final PNode replacementNode) {
+ if (parent != null) {
+ final PNode p = parent;
+ final int index = p.getChildrenReference().indexOf(this);
+ p.removeChild(this);
+ p.addChild(index, replacementNode);
+ }
+ }
+
+ /**
+ * Sets the name of this null, may be null.
+ *
+ * @since 1.3
+ * @param name new name for this node
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the name given to this node.
+ *
+ * @since 1.3
+ * @return name given to this node, may be null
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Return the number of children that this node has.
+ *
+ * @return the number of children
+ */
+ public int getChildrenCount() {
+ if (children == null) {
+ return 0;
+ }
+ return children.size();
+ }
+
+ /**
+ * Return the child node at the specified index.
+ *
+ * @param index a child index
+ * @return the child node at the specified index
+ */
+ public PNode getChild(final int index) {
+ return (PNode) children.get(index);
+ }
+
+ /**
+ * Return a reference to the list used to manage this node's children. This
+ * list should not be modified.
+ *
+ * @return reference to the children list
+ */
+ public List getChildrenReference() {
+ if (children == null) {
+ children = new ArrayList();
+ }
+ return children;
+ }
+
+ /**
+ * Return an iterator over this node's direct descendant children.
+ *
+ * @return iterator over this nodes children
+ */
+ public ListIterator getChildrenIterator() {
+ if (children == null) {
+ return Collections.EMPTY_LIST.listIterator();
+ }
+ return Collections.unmodifiableList(children).listIterator();
+ }
+
+ /**
+ * Return the root node (instance of PRoot). If this node does not descend
+ * from a PRoot then null will be returned.
+ *
+ * @return root element of this node, or null if this node does not descend
+ * from a PRoot
+ */
+ public PRoot getRoot() {
+ if (parent != null) {
+ return parent.getRoot();
+ }
+ return null;
+ }
+
+ /**
+ * Return a collection containing this node and all of its descendant nodes.
+ *
+ * @return a new collection containing this node and all descendants
+ */
+ public Collection getAllNodes() {
+ return getAllNodes(null, null);
+ }
+
+ /**
+ * Return a collection containing the subset of this node and all of its
+ * descendant nodes that are accepted by the given node filter. If the
+ * filter is null then all nodes will be accepted. If the results parameter
+ * is not null then it will be used to collect this subset instead of
+ * creating a new collection.
+ *
+ * @param filter the filter used to determine the subset
+ * @param resultantNodes where matching nodes should be added
+ * @return a collection containing this node and all descendants
+ */
+ public Collection getAllNodes(final PNodeFilter filter, final Collection resultantNodes) {
+ Collection results;
+ if (resultantNodes == null) {
+ results = new ArrayList();
+ }
+ else {
+ results = resultantNodes;
+ }
+
+ if (filter == null || filter.accept(this)) {
+ results.add(this);
+ }
+
+ if (filter == null || filter.acceptChildrenOf(this)) {
+ final int count = getChildrenCount();
+ for (int i = 0; i < count; i++) {
+ final PNode each = (PNode) children.get(i);
+ each.getAllNodes(filter, results);
+ }
+ }
+
+ return results;
+ }
+
+ // ****************************************************************
+ // Serialization - Nodes conditionally serialize their parent.
+ // This means that only the parents that were unconditionally
+ // (using writeObject) serialized by someone else will be restored
+ // when the node is unserialized.
+ // ****************************************************************
+
+ /**
+ * Write this node and all of its descendant nodes to the given outputsteam.
+ * This stream must be an instance of PObjectOutputStream or serialization
+ * will fail. This nodes parent is written out conditionally, that is it
+ * will only be written out if someone else writes it out unconditionally.
+ *
+ * @param out the output stream to write to, must be an instance of
+ * PObjectOutputStream
+ * @throws IOException when an error occurs speaking to underlying
+ * ObjectOutputStream
+ */
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ if (!(out instanceof PObjectOutputStream)) {
+ throw new IllegalArgumentException("PNode.writeObject may only be used with PObjectOutputStreams");
+ }
+ out.defaultWriteObject();
+ ((PObjectOutputStream) out).writeConditionalObject(parent);
+ }
+
+ /**
+ * Read this node and all of its descendants in from the given input stream.
+ *
+ * @param in the stream to read from
+ *
+ * @throws IOException when an error occurs speaking to underlying
+ * ObjectOutputStream
+ * @throws ClassNotFoundException when a class is deserialized that no
+ * longer exists. This can happen if it's renamed or deleted.
+ */
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ parent = (PNode) in.readObject();
+ }
+
+ /**
+ * Returns an array of input event listeners that are attached to this node.
+ *
+ * @since 1.3
+ * @return event listeners attached to this node
+ */
+ public PInputEventListener[] getInputEventListeners() {
+ if (listenerList == null || listenerList.getListenerCount() == 0) {
+ return new PInputEventListener[] {};
+ }
+
+ final EventListener[] listeners = listenerList.getListeners(PInputEventListener.class);
+
+ final PInputEventListener[] result = new PInputEventListener[listeners.length];
+ for (int i = 0; i < listeners.length; i++) {
+ result[i] = (PInputEventListener) listeners[i];
+ }
+ return result;
+ }
+
+ /**
+ * PSceneGraphDelegate is an interface to receive low level node
+ * events. It together with PNode.SCENE_GRAPH_DELEGATE gives Piccolo2d users
+ * an efficient way to learn about low level changes in Piccolo's scene
+ * graph. Most users will not need to use this.
+ */
+ public interface PSceneGraphDelegate {
+ /**
+ * Called to notify delegate that the node needs repainting.
+ *
+ * @param node node needing repaint
+ */
+ void nodePaintInvalidated(PNode node);
+
+ /**
+ * Called to notify delegate that the node and all it's children need
+ * repainting.
+ *
+ * @param node node needing repaint
+ */
+ void nodeFullBoundsInvalidated(PNode node);
+ }
+}
diff --git a/core/src/main/java/org/piccolo2d/POffscreenCanvas.java b/core/src/main/java/org/piccolo2d/POffscreenCanvas.java
new file mode 100644
index 0000000..f3b5d2c
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/POffscreenCanvas.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics2D;
+
+import org.piccolo2d.util.PBounds;
+import org.piccolo2d.util.PPaintContext;
+import org.piccolo2d.util.PUtil;
+
+
+/**
+ * Offscreen canvas.
+ *
+ * @since 1.3
+ */
+public final class POffscreenCanvas implements PComponent {
+
+ /** Default render quality, PPaintContext.HIGH_QUALITY_RENDERING
. */
+ static final int DEFAULT_RENDER_QUALITY = PPaintContext.HIGH_QUALITY_RENDERING;
+
+ /** Bounds of this offscreen canvas. */
+ private final PBounds bounds;
+
+ /** Camera for this offscreen canvas. */
+ private PCamera camera;
+
+ /** Render quality. */
+ private int renderQuality = DEFAULT_RENDER_QUALITY;
+
+ /** True if this offscreen canvas is opaque. */
+ private boolean opaque;
+
+ /** Background color for this offscreen canvas. */
+ private Color backgroundColor;
+
+
+ /**
+ * Create a new offscreen canvas the specified width and height.
+ *
+ * @param width width of this offscreen canvas, must be at least zero
+ * @param height height of this offscreen canvas, must be at least zero
+ */
+ public POffscreenCanvas(final int width, final int height) {
+ if (width < 0) {
+ throw new IllegalArgumentException("width must be at least zero, was " + width);
+ }
+ if (height < 0) {
+ throw new IllegalArgumentException("height must be at least zero, was " + height);
+ }
+ bounds = new PBounds(0.0d, 0.0d, width, height);
+ setCamera(PUtil.createBasicScenegraph());
+
+ opaque = false;
+ backgroundColor = null;
+ }
+
+
+ /**
+ * Render this offscreen canvas to the specified graphics.
+ *
+ * @param graphics graphics to render this offscreen canvas to, must not be null
+ */
+ public void render(final Graphics2D graphics) {
+ if (graphics == null) {
+ throw new IllegalArgumentException("graphics must not be null");
+ }
+
+ if (opaque && backgroundColor != null) {
+ graphics.setBackground(backgroundColor);
+ graphics.clearRect(0, 0, (int) bounds.getWidth(), (int) bounds.getHeight());
+ }
+
+ final PPaintContext paintContext = new PPaintContext(graphics);
+ paintContext.setRenderQuality(renderQuality);
+ camera.fullPaint(paintContext);
+ }
+
+ /**
+ * Set the camera for this offscreen canvas to camera
.
+ *
+ * @param camera camera for this offscreen canvas
+ */
+ public void setCamera(final PCamera camera) {
+ if (this.camera != null) {
+ this.camera.setComponent(null);
+ }
+ this.camera = camera;
+ if (camera != null) {
+ camera.setComponent(this);
+ camera.setBounds((PBounds) bounds.clone());
+ }
+ }
+
+ /**
+ * Return the camera for this offscreen canvas.
+ *
+ * @return the camera for this offscreen canvas
+ */
+ public PCamera getCamera() {
+ return camera;
+ }
+
+ /**
+ * Set the render quality hint for this offscreen canvas to
+ * renderQuality
.
+ *
+ * @param renderQuality render quality hint, must be one of
+ * PPaintContext.HIGH_QUALITY_RENDERING
or
+ * PPaintContext.LOW_QUALITY_RENDERING
+ */
+ public void setRenderQuality(final int renderQuality) {
+ if (renderQuality == PPaintContext.HIGH_QUALITY_RENDERING
+ || renderQuality == PPaintContext.LOW_QUALITY_RENDERING) {
+ this.renderQuality = renderQuality;
+ }
+ else {
+ throw new IllegalArgumentException("renderQuality must be one of PPaintContext.HIGH_QUALITY_RENDERING"
+ + " or PPaintContext.LOW_QUALITY_RENDERING, was " + renderQuality);
+ }
+ }
+
+ /**
+ * Return the render quality hint for this offscreen canvas.
+ *
+ * @return the render quality hint for this offscreen canvas
+ */
+ public int getRenderQuality() {
+ return renderQuality;
+ }
+
+ /** {@inheritDoc} */
+ public void paintImmediately() {
+ // empty
+ }
+
+ /** {@inheritDoc} */
+ public void popCursor() {
+ // empty
+ }
+
+ /** {@inheritDoc} */
+ public void pushCursor(final Cursor cursor) {
+ // empty
+ }
+
+ /** {@inheritDoc} */
+ public void repaint(final PBounds repaintBounds) {
+ // empty
+ }
+
+ /** {@inheritDoc} */
+ public void setInteracting(final boolean interacting) {
+ // empty
+ }
+
+ /**
+ * Return the root node of the scene graph for this offscreen canvas. The
+ * root node will be null if the camera for this offscreen canvas is null.
+ *
+ * @return the root node of the scene graph for this offscreen canvas
+ */
+ public PRoot getRoot() {
+ return camera == null ? null : camera.getRoot();
+ }
+
+ /**
+ * Return true if this offscreen canvas is opaque. Defaults to false
.
+ *
+ * @return true if this offscreen canvas is opaque
+ */
+ public boolean isOpaque() {
+ return opaque;
+ }
+
+ /**
+ * Set to true if this offscreen canvas is opaque.
+ *
+ * @param opaque true if this offscreen canvas is opaque
+ */
+ public void setOpaque(final boolean opaque) {
+ this.opaque = opaque;
+ }
+
+ /**
+ * Return the background color for this offscreen canvas. If this
+ * offscreen canvas is opaque, the background color will be painted
+ * before the contents of the scene are rendered.
+ *
+ * @see #isOpaque
+ * @return the background color for this offscreen canvas
+ */
+ public Color getBackground() {
+ return backgroundColor;
+ }
+
+ /**
+ * Set the background color for this offscreen canvas to backgroundColor
.
+ * If this offscreen canvas is opaque, the background color will be painted
+ * before the contents of the scene are rendered.
+ *
+ * @see #isOpaque
+ * @param backgroundColor background color for this offscreen canvas
+ */
+ public void setBackground(final Color backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/piccolo2d/PRoot.java b/core/src/main/java/org/piccolo2d/PRoot.java
new file mode 100644
index 0000000..6692f1e
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/PRoot.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d;
+
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+
+import org.piccolo2d.activities.PActivity;
+import org.piccolo2d.activities.PActivityScheduler;
+import org.piccolo2d.util.PDebug;
+import org.piccolo2d.util.PNodeFilter;
+
+
+/**
+ * PRoot serves as the top node in Piccolo2D's runtime structure. The
+ * PRoot responsible for running the main UI loop that processes input from
+ * activities and external events.
+ *
+ *
+ * @version 1.1
+ * @author Jesse Grosjean
+ */
+public class PRoot extends PNode {
+
+ /**
+ * Allows for future serialization code to understand versioned binary
+ * formats.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The property name that identifies a change in the set of this root's
+ * input sources (see {@link InputSource InputSource}). In any property
+ * change event the new value will be a reference to the list of this root's
+ * input sources, but old value will always be null.
+ */
+ public static final String PROPERTY_INPUT_SOURCES = "inputSources";
+
+ /**
+ * The property code that identifies a change in the set of this root's
+ * input sources (see {@link InputSource InputSource}). In any property
+ * change event the new value will be a reference to the list of this root's
+ * input sources, but old value will always be null.
+ */
+ public static final int PROPERTY_CODE_INPUT_SOURCES = 1 << 14;
+
+ /**
+ * The property name that identifies a change in this node's interacting
+ * state.
+ *
+ * @since 1.3
+ */
+ public static final String PROPERTY_INTERACTING_CHANGED = "INTERACTING_CHANGED_NOTIFICATION";
+
+ /**
+ * The property code that identifies a change in this node's interacting
+ * state.
+ *
+ * @since 1.3
+ */
+ public static final int PROPERTY_CODE_INTERACTING_CHANGED = 1 << 13;
+
+ /** Whether this not is currently processing inputs. */
+ protected transient boolean processingInputs;
+
+ /** Whether this node needs to have its inputs processed. */
+ protected transient boolean processInputsScheduled;
+
+ /** The number of interactions this node is currently participating in. */
+ private transient int interacting;
+
+ /**
+ * The singleton instance of the default input manager.
+ */
+ private transient PInputManager defaultInputManager;
+
+ /** The Input Sources that are registered to this node. */
+ private final transient List inputSources;
+
+ /**
+ * Used to provide a consistent clock time to activities as they are being
+ * processed.
+ *
+ * Should it happen that an activity step take longer than a millisecond,
+ * the next step will be unaffected by the change in clock had it used
+ * System.currentMillis().
+ */
+ private transient long globalTime;
+
+ /**
+ * Object responsible for scheduling activities, regardless of where in the
+ * scene they take place.
+ */
+ private final PActivityScheduler activityScheduler;
+
+ /**
+ * Construct a new PRoot(). Note the PCanvas already creates a basic scene
+ * graph for you so often you will not need to construct your own roots.
+ */
+ public PRoot() {
+ super();
+ inputSources = new ArrayList();
+ globalTime = System.currentTimeMillis();
+ activityScheduler = new PActivityScheduler(this);
+ }
+
+ // ****************************************************************
+ // Activities
+ // ****************************************************************
+
+ /**
+ * Add an activity to the activity scheduler associated with this root.
+ * Activities are given a chance to run during each call to the roots
+ * processInputs
method. When the activity has finished running
+ * it will automatically get removed.
+ *
+ * @param activity Activity that should be scheduled
+ * @return whether it has been scheduled (always true)
+ */
+ public boolean addActivity(final PActivity activity) {
+ getActivityScheduler().addActivity(activity);
+ return true;
+ }
+
+ /**
+ * Get the activity scheduler associated with this root.
+ *
+ * @return associated scheduler
+ */
+ public PActivityScheduler getActivityScheduler() {
+ return activityScheduler;
+ }
+
+ /**
+ * Wait for all scheduled activities to finish before returning from this
+ * method. This will freeze out user input, and so it is generally
+ * recommended that you use PActivities.setTriggerTime() to offset
+ * activities instead of using this method.
+ */
+ public void waitForActivities() {
+ final PNodeFilter cameraWithCanvas = new CameraWithCanvasFilter();
+
+ while (activityScheduler.getActivitiesReference().size() > 0) {
+ processInputs();
+ final Iterator i = getAllNodes(cameraWithCanvas, null).iterator();
+ while (i.hasNext()) {
+ final PCamera each = (PCamera) i.next();
+ each.getComponent().paintImmediately();
+ }
+ }
+ }
+
+ /**
+ * Since getRoot is handled recursively, and root is the lowest point in the
+ * hierarchy, simply returns itself.
+ *
+ * @return itself
+ */
+ public PRoot getRoot() {
+ return this;
+ }
+
+ /**
+ * Get the default input manager to be used when processing input events.
+ * PCanvas's use this method when they forward new swing input events to the
+ * PInputManager.
+ *
+ * @return a singleton instance of PInputManager
+ */
+ public PInputManager getDefaultInputManager() {
+ if (defaultInputManager == null) {
+ defaultInputManager = new PInputManager();
+ addInputSource(defaultInputManager);
+ }
+ return defaultInputManager;
+ }
+
+ /**
+ * Return true if this root has been marked as interacting. If so the root
+ * will normally render at a lower quality that is faster.
+ *
+ * @since 1.3
+ * @return true if this root has user interaction taking place
+ */
+ public boolean getInteracting() {
+ return interacting > 0;
+ }
+
+ /**
+ * Set if this root is interacting. If so the root will normally render at a
+ * lower quality that is faster. Also repaints the root if the the
+ * interaction has ended.
+ *
processInputs
method.
+ * Activities should usually use this global time instead of System.
+ * currentTimeMillis() so that multiple activities will be synchronized.
+ *
+ * @return time as recorded at the beginning of activity scheduling
+ */
+ public long getGlobalTime() {
+ return globalTime;
+ }
+
+ /**
+ * This is the heartbeat of the Piccolo2D framework. Pending input events
+ * are processed. Activities are given a chance to run, and the bounds
+ * caches and any paint damage is validated.
+ */
+ public void processInputs() {
+ PDebug.startProcessingInput();
+ processingInputs = true;
+
+ globalTime = System.currentTimeMillis();
+ if (inputSources.size() > 0) {
+ final Iterator inputSourceIterator = inputSources.iterator();
+ while (inputSourceIterator.hasNext()) {
+ final InputSource each = (InputSource) inputSourceIterator.next();
+ each.processInput();
+ }
+ }
+
+ activityScheduler.processActivities(globalTime);
+ validateFullBounds();
+ validateFullPaint();
+
+ processingInputs = false;
+ PDebug.endProcessingInput();
+ }
+
+ /** {@inheritDoc} */
+ public void setFullBoundsInvalid(final boolean fullLayoutInvalid) {
+ super.setFullBoundsInvalid(fullLayoutInvalid);
+ scheduleProcessInputsIfNeeded();
+ }
+
+ /** {@inheritDoc} */
+ public void setChildBoundsInvalid(final boolean childLayoutInvalid) {
+ super.setChildBoundsInvalid(childLayoutInvalid);
+ scheduleProcessInputsIfNeeded();
+ }
+
+ /** {@inheritDoc} */
+ public void setPaintInvalid(final boolean paintInvalid) {
+ super.setPaintInvalid(paintInvalid);
+ scheduleProcessInputsIfNeeded();
+ }
+
+ /** {@inheritDoc} */
+ public void setChildPaintInvalid(final boolean childPaintInvalid) {
+ super.setChildPaintInvalid(childPaintInvalid);
+ scheduleProcessInputsIfNeeded();
+ }
+
+ /**
+ * Schedule process inputs if needed.
+ */
+ public void scheduleProcessInputsIfNeeded() {
+ /*
+ * The reason for the special case here (when not in the event dispatch
+ * thread) is that the SwingUtilitiles.invokeLater code below only
+ * invokes later with respect to the event dispatch thread, it will
+ * invoke concurrently with other threads.
+ */
+ if (!SwingUtilities.isEventDispatchThread()) {
+ /*
+ * Piccolo2D is not thread safe and should almost always be called
+ * from the Swing event dispatch thread. It should only reach this
+ * point when a new canvas is being created.
+ */
+ return;
+ }
+
+ PDebug.scheduleProcessInputs();
+
+ if (!processInputsScheduled && !processingInputs
+ && (getFullBoundsInvalid() || getChildBoundsInvalid() || getPaintInvalid() || getChildPaintInvalid())) {
+
+ processInputsScheduled = true;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ processInputs();
+ processInputsScheduled = false;
+ }
+ });
+ }
+ }
+
+ 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
+ * in here.
+ */
+ public static interface InputSource {
+ /** Causes the system to process any pending Input Events. */
+ void processInput();
+ }
+}
diff --git a/core/src/main/java/org/piccolo2d/activities/PActivity.java b/core/src/main/java/org/piccolo2d/activities/PActivity.java
new file mode 100644
index 0000000..0d169d4
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/activities/PActivity.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.activities;
+
+import org.piccolo2d.util.PUtil;
+
+/**
+ * PActivity controls some time dependent aspect of Piccolo, such as
+ * animation. Once created activities must be scheduled with the
+ * PActivityScheduler managed by the PRoot to run. They are automatically
+ * removed from the scheduler when the animation has finished.
+ * + * See the PNode.animate*() methods for an example of how to set up and run + * different activities. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PActivity { + /** + * Parameter for terminate that signifies that activity should bail out + * immediately without flagging activity as finished. + */ + public static final int TERMINATE_WITHOUT_FINISHING = 0; + + /** + * Parameter for terminate that signifies that activity should bail out + * immediately, but flag activity as finished. + */ + public static final int TERMINATE_AND_FINISH = 1; + + /** + * Parameter for terminate that signifies that activity should bail out + * immediately, if currently active. + */ + public static final int TERMINATE_AND_FINISH_IF_STEPPING = 2; + + /** Activity scheduler that this activity is bound to. */ + private PActivityScheduler scheduler; + + /** Time at which this activity should start in PRoot global time. */ + private long startTime; + + /** Duration in milliseconds that this activity should last. */ + private long duration; + + /** How many milliseconds should pass between steps. */ + private long stepRate; + + private PActivityDelegate delegate; + + /** Whether this activity is currently active. */ + private boolean stepping; + + /** Next time at which step should occur. */ + private long nextStepTime; + + /** + * Constructs a new PActivity. + * + * @param aDuration the amount of time that this activity should take to + * complete, -1 for infinite. + */ + public PActivity(final long aDuration) { + this(aDuration, PUtil.DEFAULT_ACTIVITY_STEP_RATE); + } + + /** + * Constructs a new PActivity. + * + * @param aDuration the amount of time that this activity should take to + * complete, -1 for infinite. + * @param aStepRate the maximum rate that this activity should receive step + * events. + */ + public PActivity(final long aDuration, final long aStepRate) { + this(aDuration, aStepRate, System.currentTimeMillis()); + } + + /** + * Constructs a new PActivity. + * + * @param aDuration the amount of time that this activity should take to + * complete, -1 for infinite. + * @param aStepRate the maximum rate that this activity should receive step + * events. + * @param aStartTime the time (relative to System.currentTimeMillis()) that + * this activity should start. + */ + public PActivity(final long aDuration, final long aStepRate, final long aStartTime) { + duration = aDuration; + stepRate = aStepRate; + startTime = aStartTime; + nextStepTime = aStartTime; + stepping = false; + } + + // **************************************************************** + // Basics + // **************************************************************** + + /** + * Return the time that this activity should start running in PRoot global + * time. When this time is reached (or soon after) this activity will have + * its startStepping() method called. + * + * @return time at which this activity should start in PRoot global time. + */ + public long getStartTime() { + return startTime; + } + + /** + * Set the time that this activity should start running in PRoot global + * time. When this time is reached (or soon after) this activity will have + * its startStepping() method called. + * + * @param aTriggerTime time at which you want this activity to begin in + * PRoot global time + */ + public void setStartTime(final long aTriggerTime) { + startTime = aTriggerTime; + } + + /** + * Return the amount of time that this activity should delay between steps. + * + * @return the desired milliseconds between steps + */ + public long getStepRate() { + return stepRate; + } + + /** + * Set the amount of time that this activity should delay between steps. + * + * @param aStepRate desired step rate in milliseconds between steps + */ + public void setStepRate(final long aStepRate) { + stepRate = aStepRate; + } + + /** + * Gets the next step time desired for this activity. Exists since some + * steps might eat into the step rate otherwise. + * + * @return next calculated step time + */ + public long getNextStepTime() { + return nextStepTime; + } + + /** + * Return the amount of time that this activity should take to complete, + * after the startStepping method is called. + * + * @return time that this activity should take to complete + */ + public long getDuration() { + return duration; + } + + /** + * Set the amount of time that this activity should take to complete, after + * the startStepping method is called. + * + * @param aDuration desired duration this activity should take (-1 for + * infinite) once it begins stepping + */ + public void setDuration(final long aDuration) { + duration = aDuration; + } + + /** + * Returns the activity scheduler associated with this activity. + * + * @return associated scheduler + */ + public PActivityScheduler getActivityScheduler() { + return scheduler; + } + + /** + * Informs the activity of the scheduler that will be responsible for + * scheduling it. + * + * @param aScheduler scheduler to associate with this activity + */ + public void setActivityScheduler(final PActivityScheduler aScheduler) { + scheduler = aScheduler; + } + + // **************************************************************** + // Stepping + // **************************************************************** + + /** + * Return true if this activity is stepping. + * + * @return whether this activity is stepping + */ + public boolean isStepping() { + return stepping; + } + + /** + * Return true if this activity is performing an animation. This is used by + * the PCanvas to determine if it should set the render quality to + * PCanvas.animatingRenderQuality or not for each frame it renders. + * + * @return whether this activity is an animation, subclasses can override + * this. + */ + protected boolean isAnimation() { + return false; + } + + /** + * This method is called right before an activity is scheduled to start + * running. After this method is called step() will be called until the + * activity finishes. + */ + protected void activityStarted() { + if (delegate != null) { + delegate.activityStarted(this); + } + } + + /** + * This is the method that most activities override to perform their + * behavior. It will be called repeatedly when the activity is running. + * + * @param elapsedTime the amount of time that has passed relative to the + * activities startTime. + */ + protected void activityStep(final long elapsedTime) { + if (delegate != null) { + delegate.activityStepped(this); + } + } + + /** + * This method is called after an activity is has finished running and the + * activity has been removed from the PActivityScheduler queue. + */ + protected void activityFinished() { + if (delegate != null) { + delegate.activityFinished(this); + } + } + + /** + * Get the delegate for this activity. The delegate is notified when the + * activity starts and stops stepping. + * + * @return delegate of this activity, may be null + */ + public PActivityDelegate getDelegate() { + return delegate; + } + + /** + * Set the delegate for this activity. The delegate is notified when the + * activity starts and stops stepping. + * + * @param delegate delegate that should be informed of activity events + */ + public void setDelegate(final PActivityDelegate delegate) { + this.delegate = delegate; + } + + // **************************************************************** + // Controlling + // **************************************************************** + + /** + * Schedules this activity to start after the first activity has finished. + * Note that no link is created between these activities, if the startTime + * or duration of the first activity is later changed this activities start + * time will not be updated to reflect that change. + * + * @param first activity after which this activity should be scheduled + */ + public void startAfter(final PActivity first) { + setStartTime(first.getStartTime() + first.getDuration()); + } + + /** + * Stop this activity immediately, and remove it from the activity + * scheduler. The default termination behavior is call activityFinished if + * the activity is currently stepping. Use terminate(terminationBehavior) + * use a different termination behavior. + */ + public void terminate() { + terminate(TERMINATE_AND_FINISH_IF_STEPPING); + } + + /** + * Stop this activity immediately, and remove it from the activity + * scheduler. The termination behavior determines when and if + * activityStarted and activityFinished get called. The possible termination + * behaviors are as follow: + * + * TERMINATE_WITHOUT_FINISHING - The method activityFinished will never get + * called and so the activity will be terminated midway. + * TERMINATE_AND_FINISH - The method activityFinished will always get + * called. And so the activity will always end in it's completed state. If + * the activity has not yet started the method activityStarted will also be + * called. TERMINATE_AND_FINISH_IF_STEPPING - The method activityFinished + * will only be called if the activity has previously started. + * + * @param terminationBehavior behavior to use regarding delegate + * notification and event firing + */ + public void terminate(final int terminationBehavior) { + if (scheduler != null) { + scheduler.removeActivity(this); + } + + switch (terminationBehavior) { + case TERMINATE_WITHOUT_FINISHING: + stepping = false; + break; + + case TERMINATE_AND_FINISH: + if (stepping) { + stepping = false; + activityFinished(); + } + else { + activityStarted(); + activityFinished(); + } + + break; + + case TERMINATE_AND_FINISH_IF_STEPPING: + if (stepping) { + stepping = false; + activityFinished(); + } + break; + default: + throw new RuntimeException("Invalid termination behaviour provided to PActivity.terminate"); + } + } + + /** + * The activity scheduler calls this method and it is here that the activity + * decides if it should do a step or not for the given time. + * + * @param currentTime in global root time + * @return number of milliseconds in global root time before processStep + * should be called again, -1 if never + */ + public long processStep(final long currentTime) { + // if before start time + if (currentTime < startTime) { + return startTime - currentTime; + } + + // if past stop time + if (currentTime > getStopTime()) { + if (stepping) { + stepping = false; + scheduler.removeActivity(this); + activityFinished(); + } + else { + activityStarted(); + scheduler.removeActivity(this); + activityFinished(); + } + return -1; + } + + // else should be stepping + if (!stepping) { + activityStarted(); + stepping = true; + } + + if (currentTime >= nextStepTime) { + activityStep(currentTime - startTime); + nextStepTime = currentTime + stepRate; + } + + return stepRate; + } + + /** + * Return the time when this activity should finish running. At this time + * (or soon after) the stoppedStepping method will be called + * + * @return time at which this activity should be stopped + */ + public long getStopTime() { + if (duration == -1) { + return Long.MAX_VALUE; + } + return startTime + duration; + } + + /** + * PActivityDelegate is used by classes to learn about and act on the + * different states that a PActivity goes through, such as when the activity + * starts and stops stepping. + */ + public interface PActivityDelegate { + /** + * Gets called when the activity starts. + * + * @param activity activity that started + */ + void activityStarted(PActivity activity); + + /** + * Gets called for each step of the activity. + * + * @param activity activity that is stepping + */ + void activityStepped(PActivity activity); + + /** + * Gets called when the activity finishes. + * + * @param activity activity that finished + */ + void activityFinished(PActivity activity); + } + +} diff --git a/core/src/main/java/org/piccolo2d/activities/PActivityScheduler.java b/core/src/main/java/org/piccolo2d/activities/PActivityScheduler.java new file mode 100644 index 0000000..532a4fa --- /dev/null +++ b/core/src/main/java/org/piccolo2d/activities/PActivityScheduler.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.activities; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.Timer; + +import org.piccolo2d.PRoot; +import org.piccolo2d.util.PUtil; + + +/** + * PActivityScheduler is responsible for maintaining a list of + * activities. It is given a chance to process these activities from the PRoot's + * processInputs() method. Most users will not need to use the + * PActivityScheduler directly, instead you should look at: + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PColorActivity extends PInterpolatingActivity { + + private Color source; + private Color destination; + private final Target target; + + /** + * Target Objects that want their color to be set by the color + * activity must implement this interface. + */ + public interface Target { + + /** + * This will be called by the color activity for each new interpolated + * color that it computes while it is stepping. + * + * @param color the color to assign to the target + */ + void setColor(Color color); + + /** + * This method is called right before the color activity starts. That + * way an object's color is always animated from its current color. + * + * @return the target's current color. + */ + Color getColor(); + } + + /** + * Constructs a color activity for the given target that will animate for + * the duration provided at an interval of stepRate. + * + * Destination color must be assigned later. + * + * @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 + */ + public PColorActivity(final long duration, final long stepRate, final Target aTarget) { + this(duration, stepRate, aTarget, null); + } + + /** + * 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. + * + * @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 + * @param aDestination the color to which the animation is aiming at + */ + public PColorActivity(final long duration, final long stepRate, final Target aTarget, final Color aDestination) { + this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, aTarget, aDestination); + } + + /** + * Create a new PColorActivity. + * + * @param duration the length of one loop of the activity + * @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 + * the source state will be taken from. + * @param aDestination the destination color state + */ + public PColorActivity(final long duration, final long stepRate, final int loopCount, final int mode, + final Target aTarget, final Color aDestination) { + super(duration, stepRate, loopCount, mode); + target = aTarget; + destination = aDestination; + } + + /** + * Returns true since all PColorActivities animate the scene. + * + * @return always returns true + */ + protected boolean isAnimation() { + return true; + } + + /** + * Return the final color that will be set on the color activities target + * when the activity stops stepping. + * + * @return the final color for this color activity + */ + public Color getDestinationColor() { + return destination; + } + + /** + * Set the final color that will be set on the color activities target when + * the activity stops stepping. + * + * @param newDestination to animate towards + */ + public void setDestinationColor(final Color newDestination) { + destination = newDestination; + } + + /** + * Overrides it's parent to ensure that the source color is the color of the + * node being animated. + */ + protected void activityStarted() { + if (getFirstLoop()) { + source = target.getColor(); + } + super.activityStarted(); + } + + /** + * Interpolates the target node's color by mixing the source color and the + * destination color. + * + * @param zeroToOne 0 = all source color, 1 = all destination color + */ + public void setRelativeTargetValue(final float zeroToOne) { + super.setRelativeTargetValue(zeroToOne); + final float red = source.getRed() + zeroToOne * (destination.getRed() - source.getRed()); + final float green = source.getGreen() + zeroToOne * (destination.getGreen() - source.getGreen()); + final float blue = source.getBlue() + zeroToOne * (destination.getBlue() - source.getBlue()); + final float alpha = source.getAlpha() + zeroToOne * (destination.getAlpha() - source.getAlpha()); + target.setColor(new Color((int) red, (int) green, (int) blue, (int) alpha)); + } +} diff --git a/core/src/main/java/org/piccolo2d/activities/PInterpolatingActivity.java b/core/src/main/java/org/piccolo2d/activities/PInterpolatingActivity.java new file mode 100644 index 0000000..23ef03e --- /dev/null +++ b/core/src/main/java/org/piccolo2d/activities/PInterpolatingActivity.java @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.activities; + +import org.piccolo2d.util.PUtil; + +/** + * PInterpolatingActivity interpolates between two states (source and + * destination) over the duration of the activity. The interpolation can be + * either linear or slow- in, slow-out. + *
+ * The mode determines how the activity interpolates between the two states. The + * default mode interpolates from source to destination, but you can also go + * from destination to source, and from source to destination to source. + *
+ * A loopCount of greater then one will make the activity reschedule itself when + * it has finished. This makes the activity loop between the two states. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PInterpolatingActivity extends PActivity { + + /** + * Specifies that interpolation will be from the source value to the + * destination value. + */ + public static final int SOURCE_TO_DESTINATION = 1; + + /** + * Specifies that interpolation will be from the destination value to the + * source value. + */ + public static final int DESTINATION_TO_SOURCE = 2; + + /** + * Specifies that interpolation proceed from the source to the destination + * then back to the source. Can be used to perform flashes. source value. + */ + public static final int SOURCE_TO_DESTINATION_TO_SOURCE = 3; + + private int mode; + private boolean slowInSlowOut; + private int loopCount; + private boolean firstLoop; + + /** + * Constructs an interpolating activity that will last the duration given. + * + * @since 1.3 + * @param duration duration in milliseconds of the entire activity + */ + public PInterpolatingActivity(final long duration) { + this(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION); + } + + /** + * 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); + } + + /** + * Create a new PInterpolatingActivity. + *
+ * + * @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 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 + */ + public PInterpolatingActivity(final long duration, final long stepRate, final long startTime, final int loopCount, + final int mode) { + super(duration, stepRate, startTime); + this.loopCount = loopCount; + this.mode = mode; + slowInSlowOut = true; + firstLoop = true; + } + + /** + * 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 duration) { + if (duration <= 0) { + throw new IllegalArgumentException("Duration for PInterpolatingActivity must be greater then 0"); + } + + super.setDuration(duration); + } + + // **************************************************************** + // Basics. + // **************************************************************** + + /** + * 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 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; + } + + /** + * Return the number of times the activity should automatically reschedule + * itself after it has finished. + * + * @return number of times to repeat this activity + */ + public int getLoopCount() { + return loopCount; + } + + /** + * Set the number of times the activity should automatically reschedule + * itself after it has finished. + * + * @param loopCount number of times to repeat this activity + */ + public void setLoopCount(final int loopCount) { + this.loopCount = loopCount; + } + + /** + * Return true if the activity is executing its first loop. Subclasses + * normally initialize their source state on the first loop. + * + * @return true if executing first loop + */ + public boolean getFirstLoop() { + return firstLoop; + } + + /** + * Set if the activity is executing its first loop. Subclasses normally + * initialize their source state on the first loop. This method will rarely + * need to be called, unless your are reusing activities. + * + * @param firstLoop true if executing first loop + */ + public void setFirstLoop(final boolean firstLoop) { + this.firstLoop = firstLoop; + } + + /** + * Returns whether this interpolation accelerates and then decelerates as it + * interpolates. + * + * @return true if accelerations are being applied apply + */ + public boolean getSlowInSlowOut() { + return slowInSlowOut; + } + + /** + * Sets whether this interpolation accelerates and then decelerates as it + * interpolates. + * + * @param isSlowInSlowOut true if this interpolation inovolves some + * accelerations + */ + public void setSlowInSlowOut(final boolean isSlowInSlowOut) { + slowInSlowOut = isSlowInSlowOut; + } + + // **************************************************************** + // Stepping - Instead of overriding the step methods subclasses + // of this activity will normally override setRelativeTargetValue(). + // This method will be called for every step of the activity with + // a value ranging from 0,0 (for the first step) to 1.0 (for the + // final step). See PTransformActivity for an example. + // **************************************************************** + + /** + * Called when activity is started. Makes sure target value is set properly + * for start of activity. + */ + protected void activityStarted() { + super.activityStarted(); + setRelativeTargetValueAdjustingForMode(0); + } + + /** + * Called at each step of the activity. Sets the current position taking + * mode into account. + * + * @param elapsedTime number of milliseconds since the activity began + */ + + protected void activityStep(final long elapsedTime) { + super.activityStep(elapsedTime); + + float t = elapsedTime / (float) getDuration(); + + t = Math.min(1, t); + t = Math.max(0, t); + + if (getSlowInSlowOut()) { + t = computeSlowInSlowOut(t); + } + + setRelativeTargetValueAdjustingForMode(t); + } + + /** + * Called whenever the activity finishes. Reschedules it if the value of + * loopCount is > 0. + */ + protected void activityFinished() { + setRelativeTargetValueAdjustingForMode(1); + super.activityFinished(); + + final PActivityScheduler scheduler = getActivityScheduler(); + if (loopCount > 1) { + if (loopCount != Integer.MAX_VALUE) { + loopCount--; + } + firstLoop = false; + setStartTime(scheduler.getRoot().getGlobalTime()); + scheduler.addActivity(this); + } + } + + /** + * Stop this activity immediately, and remove it from the activity + * scheduler. If this activity is currently running then stoppedStepping + * will be called after it has been removed from the activity scheduler. + */ + public void terminate() { + loopCount = 0; // set to zero so that we don't reschedule self. + super.terminate(); + } + + /** + * 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) { + } + + /** + * Computes percent or linear interpolation to apply when taking + * acceleration into account. + * + * @param zeroToOne Percentage of activity completed + * @return strength of acceleration + */ + public float computeSlowInSlowOut(final float zeroToOne) { + if (zeroToOne < 0.5f) { + return 2.0f * zeroToOne * zeroToOne; + } + else { + final float complement = 1.0f - zeroToOne; + return 1.0f - 2.0f * complement * complement; + } + } + + /** + * Assigns relative target value taking the mode into account. + * + * @param zeroToOne Percentage of activity completed + */ + protected void setRelativeTargetValueAdjustingForMode(final float zeroToOne) { + final float adjustedZeroToOne; + switch (mode) { + case DESTINATION_TO_SOURCE: + adjustedZeroToOne = 1 - zeroToOne; + break; + + case SOURCE_TO_DESTINATION_TO_SOURCE: + if (zeroToOne <= 0.5f) { + adjustedZeroToOne = zeroToOne * 2; + } + else { + adjustedZeroToOne = 2 * (1 - zeroToOne); + } + break; + case SOURCE_TO_DESTINATION: + default: + // Just treat the zeroToOne as how far along the interpolation + // we are. + adjustedZeroToOne = zeroToOne; + } + + setRelativeTargetValue(adjustedZeroToOne); + } +} diff --git a/core/src/main/java/org/piccolo2d/activities/PTransformActivity.java b/core/src/main/java/org/piccolo2d/activities/PTransformActivity.java new file mode 100644 index 0000000..7a1ec04 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/activities/PTransformActivity.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.activities; + +import java.awt.geom.AffineTransform; + +import org.piccolo2d.util.PAffineTransform; + + +/** + * PTransformActivity interpolates between two transforms setting its + * target's transform as it goes. See PNode. animate*() for an example of this + * activity in used. The source transform is retrieved from the target just + * before the animation is scheduled to start. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PTransformActivity extends PInterpolatingActivity { + private static final PAffineTransform STATIC_TRANSFORM = new PAffineTransform(); + + private final double[] source; + private double[] destination; + private final Target target; + + /** + * Target Objects that want to get transformed by the transform + * activity must implement this interface. See PNode.animateToTransform() + * for one way to do this. + */ + public interface Target { + + /** + * This will be called by the transform activity for each new transform + * that it computes while it is stepping. + * + * @param aTransform the transform to be applied to the target. + */ + void setTransform(AffineTransform aTransform); + + /** + * This method is called right before the transform activity starts. + * That way an object is always animated from its current position. + * + * @param aSource array to be populated with the target's gurrent matrix + */ + void getSourceMatrix(double[] aSource); + } + + /** + * 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. + * + * Requires that the developer follow up with a setDestinationTransform + * call, otherwise the transition is undefined. + * + * @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); + } + + /** + * 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); + } + + /** + * Create a new PTransformActivity. + *
+ * + * @param duration the length of one loop of the activity + * @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 target the object that the activity will be applied to and where + * the source state will be taken from. + * @param destination the destination color state + */ + public PTransformActivity(final long duration, final long stepRate, final int loopCount, final int mode, + final Target target, final AffineTransform destination) { + super(duration, stepRate, loopCount, mode); + source = new double[6]; + 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; + } + + /** + * 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) { + return null; + } + else { + return (double[]) destination.clone(); + } + } + + /** + * 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) { + destination = null; + } + else { + destination = (double[]) newDestination.clone(); + } + } + + /** + * 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); + } + 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); + + STATIC_TRANSFORM.setTransform(source[0] + zeroToOne * (destination[0] - source[0]), source[1] + zeroToOne + * (destination[1] - source[1]), source[2] + zeroToOne * (destination[2] - source[2]), source[3] + + zeroToOne * (destination[3] - source[3]), source[4] + zeroToOne * (destination[4] - source[4]), + source[5] + zeroToOne * (destination[5] - source[5])); + + target.setTransform(STATIC_TRANSFORM); + } +} diff --git a/core/src/main/java/org/piccolo2d/activities/package.html b/core/src/main/java/org/piccolo2d/activities/package.html new file mode 100644 index 0000000..b857b42 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/activities/package.html @@ -0,0 +1,4 @@ +
+This package supports Piccolo activities. Activities are used to control some time +dependent aspect of Piccolo such as animation. + diff --git a/core/src/main/java/org/piccolo2d/event/PBasicInputEventHandler.java b/core/src/main/java/org/piccolo2d/event/PBasicInputEventHandler.java new file mode 100644 index 0000000..d22d4d5 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PBasicInputEventHandler.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; + +/** + * PBasicInputEventHandler is the standard class in Piccolo that is used + * to register for mouse and keyboard events on a PNode. Note the events that + * you get depends on the node that you have registered with. For example you + * will only get mouse moved events when the mouse is over the node that you + * have registered with, not when the mouse is over some other node. + *+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PBasicInputEventHandler implements PInputEventListener { + + 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; + } + + switch (type) { + case KeyEvent.KEY_PRESSED: + keyPressed(event); + break; + + case KeyEvent.KEY_RELEASED: + keyReleased(event); + break; + + case KeyEvent.KEY_TYPED: + keyTyped(event); + break; + + case MouseEvent.MOUSE_CLICKED: + mouseClicked(event); + break; + + case MouseEvent.MOUSE_DRAGGED: + mouseDragged(event); + break; + + case MouseEvent.MOUSE_ENTERED: + mouseEntered(event); + break; + + case MouseEvent.MOUSE_EXITED: + mouseExited(event); + break; + + case MouseEvent.MOUSE_MOVED: + mouseMoved(event); + break; + + case MouseEvent.MOUSE_PRESSED: + mousePressed(event); + break; + + case MouseEvent.MOUSE_RELEASED: + mouseReleased(event); + break; + + case MouseWheelEvent.WHEEL_UNIT_SCROLL: + mouseWheelRotated(event); + break; + + case MouseWheelEvent.WHEEL_BLOCK_SCROLL: + mouseWheelRotatedByBlock(event); + break; + + case FocusEvent.FOCUS_GAINED: + keyboardFocusGained(event); + break; + + case FocusEvent.FOCUS_LOST: + keyboardFocusLost(event); + break; + + default: + throw new RuntimeException("Bad Event Type"); + } + } + + // **************************************************************** + // Event Filter - All this event listener can be associated with a event + // filter. The filter accepts and rejects events based on their modifier + // flags and type. If the filter is null (the + // 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; + } + + /** + * 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. + * + * @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) { + } +} diff --git a/core/src/main/java/org/piccolo2d/event/PDragEventHandler.java b/core/src/main/java/org/piccolo2d/event/PDragEventHandler.java new file mode 100644 index 0000000..9717683 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PDragEventHandler.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.event.InputEvent; + +import org.piccolo2d.PNode; +import org.piccolo2d.util.PDimension; + + +/** + * PDragEventHandler is a simple event handler for dragging a node on the + * canvas. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PDragEventHandler extends PDragSequenceEventHandler { + + private PNode draggedNode; + private boolean moveToFrontOnPress; + + /** + * Constructs a drag event handler which defaults to not moving the node to + * the front on drag. + */ + public PDragEventHandler() { + 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(); + if (moveToFrontOnPress) { + draggedNode.moveToFront(); + } + } + + /** + * 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); + draggedNode.localToParent(d); + 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/org/piccolo2d/event/PDragSequenceEventHandler.java b/core/src/main/java/org/piccolo2d/event/PDragSequenceEventHandler.java new file mode 100644 index 0000000..fd2877e --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PDragSequenceEventHandler.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.util.PUtil; + + +/** + * PDragSequenceEventHandler is designed to support mouse pressed, + * dragged, and released interaction sequences. Support is also provided for + * running a continuous activity during the drag sequence. + *
+ * PDragSequenceEventHandler should be subclassed by a concrete event handler + * that implements a particular interaction. See PPanEventHandler, + * PZoomEventHandler, and PDragEventHandler for examples. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public abstract class PDragSequenceEventHandler extends PBasicInputEventHandler { + + private double minDragStartDistance = 0; + private transient boolean isDragging = false; + private transient Point2D mousePressedCanvasPoint; + private transient PActivity dragActivity; + private transient PInputEvent dragEvent; + private transient int sequenceInitiatedButton = MouseEvent.NOBUTTON; + + /** Constructs a drag sequence event handler instance. */ + public PDragSequenceEventHandler() { + } + + /** + * 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 + * coordinates) before a new drag sequence is initiate. + * + * @param minDistance in screen coordinates + */ + public void setMinDragStartDistance(final double minDistance) { + minDragStartDistance = minDistance; + } + + /** + * 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) { + mousePressedCanvasPoint = new Point2D.Double(); + } + return mousePressedCanvasPoint; + } + + // **************************************************************** + // Dragging - Methods to indicate the stages of the drag sequence. + // **************************************************************** + + /** + * 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 event) { + dragEvent = event; + startDragActivity(event); + setIsDragging(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 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 event) { + stopDragActivity(event); + dragEvent = null; + event.getComponent().setInteracting(false); + setIsDragging(false); + } + + /** + * 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(); + } + + // **************************************************************** + // Drag Activity - Used for scheduling an activity during a drag + // sequence. For example zooming and auto panning are implemented + // 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; + } + + /** + * 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) { + dragActivityFirstStep(dragEvent); + } + + public void activityStepped(final PActivity activity) { + dragActivityStep(dragEvent); + } + + public void activityFinished(final PActivity activity) { + dragActivityFinalStep(dragEvent); + } + }); + + event.getCamera().getRoot().addActivity(dragActivity); + } + + /** + * 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; + } + + /** + * 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 event) { + } + + /** + * During a drag sequence an activity is scheduled that runs continuously + * while the drag sequence is active. This can be used to support some + * 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 event) { + } + + /** + * 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) { + } + + /** + * 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 = event.getButton(); + + getMousePressedCanvasPoint().setLocation(event.getCanvasPosition()); + if (!isDragging() && shouldStartDragInteraction(event)) { + startDrag(event); + } + } + } + + /** + * 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(event)) { + startDrag(event); + } + return; + } + drag(event); + } + } + + /** + * 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(event); + } + sequenceInitiatedButton = MouseEvent.NOBUTTON; + } + } +} diff --git a/core/src/main/java/org/piccolo2d/event/PInputEvent.java b/core/src/main/java/org/piccolo2d/event/PInputEvent.java new file mode 100644 index 0000000..b89ac7c --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PInputEvent.java @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.Cursor; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; + +import javax.swing.SwingUtilities; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PComponent; +import org.piccolo2d.PInputManager; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPickPath; + + +/** + * PInputEvent is used to notify PInputEventListeners of keyboard and + * mouse input. It has methods for normal event properties such as event + * modifier keys and event canvas location. + *
+ * In addition is has methods to get the mouse position and delta in a variety + * of coordinate systems. + *
+ * Last of all it provides access to the dispatch manager that can be queried to + * find the current mouse over, mouse focus, and keyboard focus. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PInputEvent { + /** The underlying Swing Event. */ + private final InputEvent inputEvent; + + /** Path relating to the current mouse event. */ + private PPickPath pickPath; + + /** Input manager responsible for the creation of this event. */ + private final PInputManager inputManager; + + /** Flag used to identify this event as handled. */ + private boolean handled; + + /** + * Create an event with the given inputManager and based on the given swing + * event. + * + * @param inputManager source of PInputEvent + * @param event underlying swing event + */ + public PInputEvent(final PInputManager inputManager, final InputEvent event) { + inputEvent = event; + this.inputManager = inputManager; + } + + /** + * Changes the cursor to the one provided and stores it on the cursor stack + * for later retrieval. + * + * @param cursor cursor to push on cursor stack + */ + public void pushCursor(final Cursor cursor) { + final PComponent component = getTopCamera().getComponent(); + component.pushCursor(cursor); + } + + /** + * Removes the top most cursor from the cursor stack and sets it as the + * current cursor. + */ + public void popCursor() { + final PComponent component = getTopCamera().getComponent(); + component.popCursor(); + } + + // **************************************************************** + // Accessing Picked Objects - Methods to access the objects associated + // with this event. + //
+ // Cameras can view layers that have + // other cameras on them, so events may be arriving through a stack + // of many cameras. The getCamera() method returns the bottommost + // camera on that stack. The getTopCamera method returns the topmost + // camera on that stack, this is also the camera through which the + // event originated. + // **************************************************************** + + /** + * Return the bottom most camera that is currently painting. If you are + * using internal cameras this may be different then what is returned by + * getTopCamera. + * + * @return the current PickPath's bottom camera. + */ + public PCamera getCamera() { + return getPath().getBottomCamera(); + } + + /** + * Return the topmost camera this is painting. This is the camera associated + * with the PCanvas that requested the current repaint. + * + * @return topmost camera on the pick path + */ + public PCamera getTopCamera() { + return getPath().getTopCamera(); + } + + /** + * Get the canvas associated with the top camera. This is the canvas where + * the originating swing event came from. + * + * @return component attached to the top camera of the current pick path + */ + public PComponent getComponent() { + return getTopCamera().getComponent(); + } + + /** + * Return the input manager that dispatched this event. You can use this + * input manager to find the current mouse focus, mouse over, and key focus + * nodes. You can also set a new key focus node. + * + * @return input manager that dispatched this event + */ + public PInputManager getInputManager() { + return inputManager; + } + + /** + * Return the PPickPath associated with this input event. + * + * @return pick path associated with this event (may be null) + */ + public PPickPath getPath() { + return pickPath; + } + + /** + * Sets the PIckPath associated with this mouse event. + * + * @param path path to associate with this mouse event + */ + public void setPath(final PPickPath path) { + pickPath = path; + } + + /** + * Return the bottom node on the current pickpath, that is the picked node + * furthest from the root node. + * + * @return the currently picked node of this mouse event + */ + public PNode getPickedNode() { + if (pickPath == null) { + return null; + } + return pickPath.getPickedNode(); + } + + // **************************************************************** + // Basics + // **************************************************************** + + /** + * Returns the key code associated with a key event. + * + * @return key code associated with a key event + */ + public int getKeyCode() { + if (isKeyEvent()) { + final KeyEvent e = (KeyEvent) inputEvent; + return e.getKeyCode(); + } + throw new IllegalStateException("Can't get keycode from mouse event"); + } + + /** + * Returns the character associated with a key event. + * + * @return char associated with a key event + */ + public char getKeyChar() { + if (isKeyEvent()) { + final KeyEvent e = (KeyEvent) inputEvent; + return e.getKeyChar(); + } + throw new IllegalStateException("Can't get keychar from mouse event"); + } + + /** + * Returns the location on the keyboard from which the key stroke + * originated. + * + * @return location on keyboard from which stroke originated. + */ + public int getKeyLocation() { + if (isKeyEvent()) { + final KeyEvent e = (KeyEvent) inputEvent; + return e.getKeyLocation(); + } + throw new IllegalStateException("Can't get keylocation from mouse event"); + } + + /** + * Returns whether the key event involves the action key. + * + * @return true if key involved is the action key + */ + public boolean isActionKey() { + if (isKeyEvent()) { + final KeyEvent e = (KeyEvent) inputEvent; + return e.isActionKey(); + } + throw new IllegalStateException("Can't get isActionKey from mouse event"); + } + + /** + * Returns the modifiers provided for the input event by swing. + * + * @return modifier flags for the input event + */ + public int getModifiers() { + if (!isFocusEvent()) { + return inputEvent.getModifiers(); + } + throw new IllegalStateException("Can't get modifiers from focus event"); + } + + /** + * Returns the extended modifiers provided for the input event by swing. + * + * @return extended modifies of input event + */ + public int getModifiersEx() { + if (!isFocusEvent()) { + return inputEvent.getModifiersEx(); + } + throw new IllegalStateException("Can't get modifiers ex from focus event"); + } + + /** + * Returns the click count of the mouse event. + * + * @return click count of mouse event + */ + public int getClickCount() { + if (isMouseEvent()) { + return ((MouseEvent) inputEvent).getClickCount(); + } + throw new IllegalStateException("Can't get clickcount from key event"); + } + + /** + * Returns the time at which the event was emitted. + * + * @return time at which the vent was emitted + */ + public long getWhen() { + if (!isFocusEvent()) { + return inputEvent.getWhen(); + } + throw new IllegalStateException("Can't get when from focus event"); + } + + /** + * Returns whether the alt key is currently down. + * + * @return true if alt key is down + */ + public boolean isAltDown() { + if (!isFocusEvent()) { + return inputEvent.isAltDown(); + } + throw new IllegalStateException("Can't get altdown from focus event"); + } + + /** + * Returns whether the control key is currently down. + * + * @return true if control key is down + */ + public boolean isControlDown() { + if (!isFocusEvent()) { + return inputEvent.isControlDown(); + } + throw new IllegalStateException("Can't get controldown from focus event"); + } + + /** + * Returns whether the meta key is currently down. + * + * @return true if meta key is down + */ + public boolean isMetaDown() { + if (!isFocusEvent()) { + return inputEvent.isMetaDown(); + } + throw new IllegalStateException("Can't get modifiers from focus event"); + } + + /** + * Returns whether the shift key is currently down. + * + * @return true if shift key is down + */ + public boolean isShiftDown() { + if (!isFocusEvent()) { + return inputEvent.isShiftDown(); + } + throw new IllegalStateException("Can't get shiftdown from focus event"); + } + + /** + * Returns whether the mouse event involves the left mouse button. + * + * @return true if left mouse button is involved the mouse event + */ + public boolean isLeftMouseButton() { + if (isMouseEvent()) { + return SwingUtilities.isLeftMouseButton((MouseEvent) getSourceSwingEvent()); + } + throw new IllegalStateException("Can't get isLeftMouseButton from focus event"); + } + + /** + * Returns whether the mouse event involves the middle mouse button. + * + * @return true if middle mouse button is involved the mouse event + */ + public boolean isMiddleMouseButton() { + if (isMouseEvent()) { + return SwingUtilities.isMiddleMouseButton((MouseEvent) getSourceSwingEvent()); + } + throw new IllegalStateException("Can't get isMiddleMouseButton from focus event"); + } + + /** + * Returns whether the mouse event involves the right mouse button. + * + * @return true if right mouse button is involved the mouse event + */ + public boolean isRightMouseButton() { + if (isMouseEvent()) { + return SwingUtilities.isRightMouseButton((MouseEvent) getSourceSwingEvent()); + } + throw new IllegalStateException("Can't get isRightMouseButton from focus event"); + } + + /** + * Return true if another event handler has already handled this event. + * Event handlers should use this as a hint before handling the event + * themselves and possibly reject events that have already been handled. + * + * @return true if event has been marked as handled + */ + public boolean isHandled() { + return handled; + } + + /** + * Set that this event has been handled by an event handler. This is a + * relaxed for of consuming events. The event will continue to get + * dispatched to event handlers even after it is marked as handled, but + * other event handlers that might conflict are expected to ignore events + * that have already been handled. + * + * @param handled whether the event is marked + */ + public void setHandled(final boolean handled) { + this.handled = handled; + } + + /** + * Returns the mouse button value of the underlying mouse event. + * + * @return button value of underlying mouse event + */ + public int getButton() { + if (isMouseEvent()) { + return ((MouseEvent) inputEvent).getButton(); + } + throw new IllegalStateException("Can't get button from key event"); + } + + /** + * Returns the current value of the wheel rotation on Mouse Wheel Rotation + * events. + * + * @return wheel rotation value + */ + public int getWheelRotation() { + if (isMouseWheelEvent()) { + return ((MouseWheelEvent) inputEvent).getWheelRotation(); + } + throw new IllegalStateException("Can't get wheel rotation from non-wheel event"); + } + + /** + * Returns the underlying swing event that this PInputEvent is wrapping. + * + * @return underlying swing event + */ + public InputEvent getSourceSwingEvent() { + return inputEvent; + } + + // **************************************************************** + // Classification - Methods to distinguish between mouse and key + // events. + // **************************************************************** + + /** + * Returns whether the underlying event is a KeyEvent. + * + * @return true if is key event + */ + public boolean isKeyEvent() { + return inputEvent instanceof KeyEvent; + } + + /** + * Returns whether the underlying event is a MouseEvent. + * + * @return true if is mouse event + */ + public boolean isMouseEvent() { + return inputEvent instanceof MouseEvent; + } + + /** + * Returns whether the underlying event is a Mouse Wheel Event. + * + * @return true if is a mouse wheel event + */ + + public boolean isMouseWheelEvent() { + return inputEvent instanceof MouseWheelEvent; + } + + /** + * Returns whether the underlying event is a Focus Event. + * + * @return true if is focus event + */ + public boolean isFocusEvent() { + return inputEvent == null; + } + + /** + * Returns whether the underlying event is a mouse entered or exited event. + * + * @return true if is a mouse entered or exited event + */ + public boolean isMouseEnteredOrMouseExited() { + if (isMouseEvent()) { + return inputEvent.getID() == MouseEvent.MOUSE_ENTERED || inputEvent.getID() == MouseEvent.MOUSE_EXITED; + } + return false; + } + + /** + * Returns whether or not this event is a popup menu trigger event for the + * platform. Must not be called if this event isn't a mouse event. + *
+ * Note: Popup menus are triggered differently on different systems.
+ * Therefore, isPopupTrigger
should be checked in both
+ * mousePressed
and mouseReleased
for proper
+ * cross-platform functionality.
+ *
+ * @return boolean, true if this event triggers a popup menu for this
+ * platform
+ */
+ public boolean isPopupTrigger() {
+ if (isMouseEvent()) {
+ return ((MouseEvent) inputEvent).isPopupTrigger();
+ }
+ throw new IllegalStateException("Can't get clickcount from key event");
+ }
+
+ // ****************************************************************
+ // Coordinate Systems - Methods for getting mouse location data
+ // These methods are only designed for use with PInputEvents that
+ // return true to the isMouseEvent method.
+ // ****************************************************************
+
+ /**
+ * Return the mouse position in PCanvas coordinates.
+ *
+ * @return mouse position in PCanvas coordinates
+ */
+ public Point2D getCanvasPosition() {
+ return (Point2D) inputManager.getCurrentCanvasPosition().clone();
+ }
+
+ /**
+ * Return the delta between the last and current mouse position in PCanvas
+ * coordinates.
+ *
+ * @return delta between last and current mouse position as measured by the
+ * PCanvas
+ */
+ public PDimension getCanvasDelta() {
+ final Point2D last = inputManager.getLastCanvasPosition();
+ final Point2D current = inputManager.getCurrentCanvasPosition();
+ return new PDimension(current.getX() - last.getX(), current.getY() - last.getY());
+ }
+
+ /**
+ * Return the mouse position relative to a given node on the pick path.
+ *
+ * @param nodeOnPath node on the current PPickPath
+ *
+ * @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);
+ }
+
+ /**
+ * Return the delta between the last and current mouse positions relative to
+ * a given node on the pick path.
+ *
+ * @param nodeOnPath node from which to measure
+ * @return delta between current mouse position and a given node on the pick
+ * 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);
+ }
+
+ /**
+ * Return the mouse position transformed through the view transform of the
+ * bottom camera.
+ *
+ * @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);
+ }
+
+ /**
+ * Return the delta between the last and current mouse positions transformed
+ * through the view transform of the bottom camera.
+ *
+ * @return delta between last and current mouse position as measured by the
+ * 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);
+ }
+
+ /**
+ * Returns a string representation of this object for debugging purposes.
+ *
+ * @return string representation of this object
+ */
+ public String toString() {
+ final StringBuffer result = new StringBuffer();
+
+ result.append(super.toString().replaceAll(".*\\.", ""));
+ result.append('[');
+ if (handled) {
+ result.append("handled");
+ }
+ result.append(']');
+
+ return result.toString();
+ }
+}
diff --git a/core/src/main/java/org/piccolo2d/event/PInputEventFilter.java b/core/src/main/java/org/piccolo2d/event/PInputEventFilter.java
new file mode 100644
index 0000000..0fa54cf
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/event/PInputEventFilter.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.event;
+
+import java.awt.event.FocusEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+
+/**
+ * PInputEventFilter is a class that filters input events based on the
+ * events modifiers and type. Any PBasicInputEventHandler that is associated
+ * with an event filter will only receive events that pass through the filter.
+ *
+ * To be accepted events must contain all the modifiers listed in the andMask, + * at least one of the modifiers listed in the orMask, and none of the modifiers + * listed in the notMask. The event filter also lets you specify specific event + * types (mousePressed, released, ...) to accept or reject. + *
+ * If the event filter is set to consume, then it will call consume on any event + * that it successfully accepts. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PInputEventFilter { + /** Mask representing all possible modifiers. */ + public static final int ALL_MODIFIERS_MASK = InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK + | InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK | InputEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK + | InputEvent.META_MASK; + + /** If event modifiers don't match this exactly, event it filtered. */ + private int andMask; + + /** If event modifiers have no bits from orMask enabled, event is filtered. */ + private int orMask; + + /** If event modifier has any of the notMask bits on, it is not accepted. */ + private int notMask; + + /** Number of clicks that an incoming event must have to be accepted. */ + private short clickCount = -1; + + /** Whether accepted events should be marked as handled. */ + private boolean marksAcceptedEventsAsHandled = false; + + /** Whether handled events should be immediately filtered. */ + private boolean acceptsAlreadyHandledEvents = false; + + /** Whether key pressed events are accepted. */ + private boolean acceptsKeyPressed = true; + + /** Whether key released events are accepted. */ + private boolean acceptsKeyReleased = true; + + /** Whether key typed events are accepted. */ + private boolean acceptsKeyTyped = true; + + /** Whether mouse clicked events are accepted. */ + private boolean acceptsMouseClicked = true; + + /** Whether mouse dragged events are accepted. */ + private boolean acceptsMouseDragged = true; + + /** Whether mouse entered events are accepted. */ + private boolean acceptsMouseEntered = true; + + /** Whether mouse exited events are accepted. */ + private boolean acceptsMouseExited = true; + + /** Whether mouse moved events are accepted. */ + private boolean acceptsMouseMoved = true; + + /** Whether mouse pressed events are accepted. */ + private boolean acceptsMousePressed = true; + + /** Whether mouse released events are accepted. */ + private boolean acceptsMouseReleased = true; + + /** Whether mouse wheel rotated events are accepted. */ + private boolean acceptsMouseWheelRotated = true; + + /** Whether focus events are accepted. */ + private boolean acceptsFocusEvents = true; + + /** + * Creates a PInputEventFilter that accepts everything. + */ + public PInputEventFilter() { + acceptEverything(); + } + + /** + * Creates a PInputEventFilter that will accept events if they have the + * given andMask. + * + * @param andMask exact pattern event modifiers must be to get accepted + */ + public PInputEventFilter(final int andMask) { + this(); + this.andMask = andMask; + } + + /** + * Creates a PInputEventFilter that will accept events if they have the + * given andMask and do not contain any of the bits in the notMask. + * + * @param andMask exact pattern event modifiers must be to get accepted + * @param notMask if any or these bits are on event is not accepted + */ + public PInputEventFilter(final int andMask, final int notMask) { + this(andMask); + this.notMask = notMask; + } + + /** + * Returns true if the passed event is one that is accepted. + * + * @param event Event under consideration + * @param type The type of event encoded as the PInputEvent + * @return true if event is accepted + */ + public boolean acceptsEvent(final PInputEvent event, final int type) { + boolean aResult = false; + int modifiers = 0; + + if (!event.isFocusEvent()) { + modifiers = event.getModifiers(); + } + + if (event.isHandled() && !acceptsAlreadyHandledEvents) { + return false; + } + + if (modifiers != 0) { + if ((modifiers & andMask) != andMask || (modifiers & notMask) != 0) { + return false; + } + + if (orMask != ALL_MODIFIERS_MASK && (modifiers & orMask) == 0) { + return false; + } + } + + if (event.isMouseEvent() && clickCount != -1 && clickCount != event.getClickCount()) { + return false; + } + + switch (type) { + case KeyEvent.KEY_PRESSED: + aResult = getAcceptsKeyPressed(); + break; + + case KeyEvent.KEY_RELEASED: + aResult = getAcceptsKeyReleased(); + break; + + case KeyEvent.KEY_TYPED: + aResult = getAcceptsKeyTyped(); + break; + + case MouseEvent.MOUSE_CLICKED: + aResult = getAcceptsMouseClicked(); + break; + + case MouseEvent.MOUSE_DRAGGED: + aResult = getAcceptsMouseDragged(); + break; + + case MouseEvent.MOUSE_ENTERED: + aResult = getAcceptsMouseEntered(); + break; + + case MouseEvent.MOUSE_EXITED: + aResult = getAcceptsMouseExited(); + break; + + case MouseEvent.MOUSE_MOVED: + aResult = getAcceptsMouseMoved(); + break; + + case MouseEvent.MOUSE_PRESSED: + aResult = getAcceptsMousePressed(); + break; + + case MouseEvent.MOUSE_RELEASED: + aResult = getAcceptsMouseReleased(); + break; + + case MouseWheelEvent.WHEEL_UNIT_SCROLL: + case MouseWheelEvent.WHEEL_BLOCK_SCROLL: + aResult = getAcceptsMouseWheelRotated(); + break; + + case FocusEvent.FOCUS_GAINED: + case FocusEvent.FOCUS_LOST: + aResult = getAcceptsFocusEvents(); + break; + + default: + throw new RuntimeException("PInputEvent with bad ID"); + } + + if (aResult && getMarksAcceptedEventsAsHandled()) { + event.setHandled(true); + } + + return aResult; + } + + /** + * Makes this filter accept all mouse click combinations. + */ + public void acceptAllClickCounts() { + clickCount = -1; + } + + /** + * Makes the filter accept all event types. + */ + public void acceptAllEventTypes() { + acceptsKeyPressed = true; + acceptsKeyReleased = true; + acceptsKeyTyped = true; + acceptsMouseClicked = true; + acceptsMouseDragged = true; + acceptsMouseEntered = true; + acceptsMouseExited = true; + acceptsMouseMoved = true; + acceptsMousePressed = true; + acceptsMouseReleased = true; + acceptsMouseWheelRotated = true; + acceptsFocusEvents = true; + } + + /** + * Makes this filter accept absolutely everything. + */ + public void acceptEverything() { + acceptAllEventTypes(); + setAndMask(0); + setOrMask(ALL_MODIFIERS_MASK); + setNotMask(0); + acceptAllClickCounts(); + } + + /** + * Returns whether this filter accepts key pressed events. + * + * @return true if filter accepts key pressed events + */ + public boolean getAcceptsKeyPressed() { + return acceptsKeyPressed; + } + + /** + * Returns whether this filter accepts key released events. + * + * @return true if filter accepts key released events + */ + public boolean getAcceptsKeyReleased() { + return acceptsKeyReleased; + } + + /** + * Returns whether this filter accepts key typed events. + * + * @return true if filter accepts key typed events + */ + public boolean getAcceptsKeyTyped() { + return acceptsKeyTyped; + } + + /** + * Returns whether this filter accepts mouse clicked events. + * + * @return true if filter accepts mouse clicked events + */ + public boolean getAcceptsMouseClicked() { + return acceptsMouseClicked; + } + + /** + * Returns whether this filter accepts mouse dragged events. + * + * @return true if filter accepts mouse dragged events + */ + public boolean getAcceptsMouseDragged() { + return acceptsMouseDragged; + } + + /** + * Returns whether this filter accepts mouse entered events. + * + * @return true if filter accepts mouse entered events + */ + public boolean getAcceptsMouseEntered() { + return acceptsMouseEntered; + } + + /** + * Returns whether this filter accepts mouse exited events. + * + * @return true if filter accepts mouse exited events + */ + public boolean getAcceptsMouseExited() { + return acceptsMouseExited; + } + + /** + * Returns whether this filter accepts mouse moved events. + * + * @return true if filter accepts mouse moved events + */ + public boolean getAcceptsMouseMoved() { + return acceptsMouseMoved; + } + + /** + * Returns whether this filter accepts mouse pressed events. + * + * @return true if filter accepts mouse pressed events + */ + public boolean getAcceptsMousePressed() { + return acceptsMousePressed; + } + + /** + * Returns whether this filter accepts mouse released events. + * + * @return true if filter accepts mouse released events + */ + public boolean getAcceptsMouseReleased() { + return acceptsMouseReleased; + } + + /** + * Returns whether this filter accepts mouse wheel rotated events. + * + * @return true if filter accepts mouse wheel rotated events + */ + public boolean getAcceptsMouseWheelRotated() { + return acceptsMouseWheelRotated; + } + + /** + * Returns whether this filter accepts focus events. + * + * @return true if filter accepts focus events + */ + public boolean getAcceptsFocusEvents() { + return acceptsFocusEvents; + } + + /** + * Returns whether this filter accepts events that have already been flagged + * as handled. + * + * @return true if filter accepts events that have already been flagged as + * handled + */ + public boolean getAcceptsAlreadyHandledEvents() { + return acceptsAlreadyHandledEvents; + } + + /** + * Returns whether this filter marks events as handled if they are accepted. + * + * @return true if filter will mark events as filtered if they are accepted + */ + public boolean getMarksAcceptedEventsAsHandled() { + return marksAcceptedEventsAsHandled; + } + + /** + * Flags all mouse click events as disallowed, regardless of button + * configuration. + */ + public void rejectAllClickCounts() { + clickCount = Short.MAX_VALUE; + } + + /** + * Configures filter so that no events will ever get accepted. By itself not + * terribly useful, but it's a more restrictive starting point than + * acceptAllEvents(); + */ + public void rejectAllEventTypes() { + acceptsKeyPressed = false; + acceptsKeyReleased = false; + acceptsKeyTyped = false; + acceptsMouseClicked = false; + acceptsMouseDragged = false; + acceptsMouseEntered = false; + acceptsMouseExited = false; + acceptsMouseMoved = false; + acceptsMousePressed = false; + acceptsMouseReleased = false; + acceptsMouseWheelRotated = false; + acceptsFocusEvents = false; + } + + /** + * Sets the number of clicks that an incoming event must have to be accepted. + * + * @param aClickCount number clicks that an incoming event must have to be accepted + */ + public void setAcceptClickCount(final short aClickCount) { + clickCount = aClickCount; + } + + /** + * Sets whether this filter accepts key pressed events. + * + * @param aBoolean whether filter should accept key pressed events + */ + public void setAcceptsKeyPressed(final boolean aBoolean) { + acceptsKeyPressed = aBoolean; + } + + /** + * Sets whether this filter accepts key released events. + * + * @param aBoolean whether filter should accept key released events + */ + public void setAcceptsKeyReleased(final boolean aBoolean) { + acceptsKeyReleased = aBoolean; + } + + /** + * Sets whether this filter accepts key typed events. + * + * @param aBoolean whether filter should accept key typed events + */ + + public void setAcceptsKeyTyped(final boolean aBoolean) { + acceptsKeyTyped = aBoolean; + } + + /** + * Sets whether this filter accepts mouse clicked events. + * + * @param aBoolean whether filter should accept mouse clicked events + */ + public void setAcceptsMouseClicked(final boolean aBoolean) { + acceptsMouseClicked = aBoolean; + } + + /** + * Sets whether this filter accepts mouse dragged events. + * + * @param aBoolean whether filter should accept mouse dragged events + */ + public void setAcceptsMouseDragged(final boolean aBoolean) { + acceptsMouseDragged = aBoolean; + } + + /** + * Sets whether this filter accepts mouse entered events. + * + * @param aBoolean whether filter should accept mouse entered events + */ + public void setAcceptsMouseEntered(final boolean aBoolean) { + acceptsMouseEntered = aBoolean; + } + + /** + * Sets whether this filter accepts mouse exited events. + * + * @param aBoolean whether filter should accept mouse exited events + */ + public void setAcceptsMouseExited(final boolean aBoolean) { + acceptsMouseExited = aBoolean; + } + + /** + * Sets whether this filter accepts mouse moved events. + * + * @param aBoolean whether filter should accept mouse moved events + */ + public void setAcceptsMouseMoved(final boolean aBoolean) { + acceptsMouseMoved = aBoolean; + } + + /** + * Sets whether this filter accepts mouse pressed events. + * + * @param aBoolean whether filter should accept mouse pressed events + */ + public void setAcceptsMousePressed(final boolean aBoolean) { + acceptsMousePressed = aBoolean; + } + + /** + * Sets whether this filter accepts mouse released events. + * + * @param aBoolean whether filter should accept mouse released events + */ + public void setAcceptsMouseReleased(final boolean aBoolean) { + acceptsMouseReleased = aBoolean; + } + + /** + * Sets whether this filter accepts mouse wheel rotation events. + * + * @param aBoolean whether filter should accept mouse wheel rotated events + */ + public void setAcceptsMouseWheelRotated(final boolean aBoolean) { + acceptsMouseWheelRotated = aBoolean; + } + + /** + * Sets whether this filter accepts focus events. + * + * @param aBoolean whether filter should accept focus events + */ + public void setAcceptsFocusEvents(final boolean aBoolean) { + acceptsFocusEvents = aBoolean; + } + + /** + * Sets and mask used to filter events. All bits of the andMask must be 1s + * for the event to be accepted. + * + * @param aAndMask the and mask to use for filtering events + */ + public void setAndMask(final int aAndMask) { + andMask = aAndMask; + } + + /** + * Sets whether already handled events should be accepted. + * + * @param aBoolean whether already handled events should be accepted + */ + public void setAcceptsAlreadyHandledEvents(final boolean aBoolean) { + acceptsAlreadyHandledEvents = aBoolean; + } + + /** + * Sets whether events will be marked as dirty once accepted. + * + * @param aBoolean whether events will be marked as dirty once accepted + */ + public void setMarksAcceptedEventsAsHandled(final boolean aBoolean) { + marksAcceptedEventsAsHandled = aBoolean; + } + + /** + * Sets not mask used to filter events. If any of the not bits are enabled, + * then the event is not accepted. + * + * @param aNotMask the not mask to use for filtering events + */ + public void setNotMask(final int aNotMask) { + notMask = aNotMask; + } + + /** + * Sets or mask used to filter events. If any of the or bits are enabled, + * then the event is accepted. + * + * @param aOrMask the or mask to use for filtering events + */ + public void setOrMask(final int aOrMask) { + orMask = aOrMask; + } +} diff --git a/core/src/main/java/org/piccolo2d/event/PInputEventListener.java b/core/src/main/java/org/piccolo2d/event/PInputEventListener.java new file mode 100644 index 0000000..289539d --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PInputEventListener.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.util.EventListener; + +/** + * PInputEventListener defines the most basic interface for objects that + * want to listen to PNodes for input events. This interface is very simple so + * that others may extend Piccolo's input management system. If you are just + * using Piccolo's default input management system then you will most often use + * PBasicInputEventHandler to register with a node for input events. + *
+ * + * @see PBasicInputEventHandler + * @version 1.0 + * @author Jesse Grosjean + */ +public interface PInputEventListener extends EventListener { + /** + * Called whenever an event is emitted. Used to notify listeners that an + * event is available for proecessing. + * + * @param event event that was emitted + * @param type type of event + */ + void processEvent(PInputEvent event, int type); +} diff --git a/core/src/main/java/org/piccolo2d/event/PPanEventHandler.java b/core/src/main/java/org/piccolo2d/event/PPanEventHandler.java new file mode 100644 index 0000000..dcb7826 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PPanEventHandler.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.event.InputEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.piccolo2d.PCamera; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + + +/** + * PPanEventHandler provides event handlers for basic panning of the + * canvas view with the left mouse. The interaction is that clicking and + * dragging the mouse translates the view so that the point on the surface stays + * under the mouse. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PPanEventHandler extends PDragSequenceEventHandler { + + 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); + } + + /** + * 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); + } + + /** + * 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 = event.getDelta(); + c.translateView(d.getWidth(), d.getHeight()); + } + } + + // **************************************************************** + // 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; + } + + /** + * Set the minAutoPan speed in pixels per second. + * + * @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 pixels per second. + * + * @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; + } + + /** + * Returns the minAutoPan speed in pixels per second. + * + * @since 1.3 + * @return minimum distance the autopan feature can pan the view + */ + public double getMinAutoPanSpeed() { + return minAutopanSpeed; + } + + /** + * Returns the maxAutoPan speed in pixels per second. + * + * @since 1.3 + * @return max distance the autopan feature can pan the view by + */ + public double getMaxAutoPanSpeed() { + return maxAutopanSpeed; + } + + /** + * 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 event) { + if (!autopan) { + return; + } + + final PCamera c = event.getCamera(); + final PBounds b = c.getBoundsReference(); + final Point2D l = event.getPositionRelativeTo(c); + final int outcode = b.outcode(l); + final PDimension delta = new PDimension(); + + if ((outcode & Rectangle2D.OUT_TOP) != 0) { + delta.height = validatePanningSpeed(-1.0 - 0.5 * Math.abs(l.getY() - b.getY())); + } + else if ((outcode & Rectangle2D.OUT_BOTTOM) != 0) { + delta.height = validatePanningSpeed(1.0 + 0.5 * Math.abs(l.getY() - (b.getY() + b.getHeight()))); + } + + if ((outcode & Rectangle2D.OUT_RIGHT) != 0) { + delta.width = validatePanningSpeed(1.0 + 0.5 * Math.abs(l.getX() - (b.getX() + b.getWidth()))); + } + else if ((outcode & Rectangle2D.OUT_LEFT) != 0) { + delta.width = validatePanningSpeed(-1.0 - 0.5 * Math.abs(l.getX() - b.getX())); + } + + c.localToView(delta); + + if (delta.width != 0 || delta.height != 0) { + c.translateView(delta.width, delta.height); + } + } + + /** + * 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 double absDelta = Math.abs(delta); + + final double clippedDelta; + if (absDelta < minDelta) { + clippedDelta = minDelta; + } + else if (absDelta > maxDelta) { + clippedDelta = maxDelta; + } + else { + clippedDelta = delta; + } + + if (delta < 0) { + return -clippedDelta; + } + else { + return clippedDelta; + } + } +} diff --git a/core/src/main/java/org/piccolo2d/event/PZoomEventHandler.java b/core/src/main/java/org/piccolo2d/event/PZoomEventHandler.java new file mode 100644 index 0000000..8a8df4c --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PZoomEventHandler.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.event.InputEvent; +import java.awt.geom.Point2D; + +import org.piccolo2d.PCamera; + + +/** + * ZoomEventhandler provides event handlers for basic zooming of the + * canvas view with the right (third) button. The interaction is that the + * initial mouse press defines the zoom anchor point, and then moving the mouse + * to the right zooms with a speed proportional to the amount the mouse is moved + * to the right of the anchor point. Similarly, if the mouse is moved to the + * left, the the view is zoomed out. + *
+ * On a Mac with its single mouse button one may wish to change the standard + * right mouse button zooming behavior. This can be easily done with the + * PInputEventFilter. For example to zoom with button one and shift you would do + * this: + *
+ *
+ *
+ *
+ * zoomEventHandler.getEventFilter().setAndMask(InputEvent.BUTTON1_MASK |
+ * InputEvent.SHIFT_MASK);
+ *
+ *
+ *
+ * @version 1.0
+ * @author Jesse Grosjean
+ */
+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;
+
+ /**
+ * Creates a new zoom handler.
+ */
+ public PZoomEventHandler() {
+ super();
+ setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_MASK));
+ }
+
+ // ****************************************************************
+ // Zooming
+ // ****************************************************************
+
+ /**
+ * Returns the minimum view magnification factor that this event handler is
+ * bound by. The default is 0.
+ *
+ * @return the minimum camera view scale
+ */
+ public double getMinScale() {
+ return minScale;
+ }
+
+ /**
+ * Sets the minimum view magnification factor that this event handler is
+ * bound by. The camera is left at its current scale even if
+ * minScale
is larger than the current scale.
+ *
+ * @param minScale the minimum scale, must not be negative.
+ */
+ public void setMinScale(final double minScale) {
+ this.minScale = minScale;
+ }
+
+ /**
+ * Returns the maximum view magnification factor that this event handler is
+ * bound by. The default is Double.MAX_VALUE.
+ *
+ * @return the maximum camera view scale
+ */
+ public double getMaxScale() {
+ return maxScale;
+ }
+
+ /**
+ * Sets the maximum view magnification factor that this event handler is
+ * bound by. The camera is left at its current scale even if
+ * maxScale
is smaller than the current scale. Use
+ * Double.MAX_VALUE to specify the largest possible scale.
+ *
+ * @param maxScale the maximum scale, must not be negative.
+ */
+ public void setMaxScale(final double maxScale) {
+ this.maxScale = maxScale;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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;
+
+ if (newScale < minScale) {
+ scaleDelta = minScale / currentScale;
+ }
+ if (maxScale > 0 && newScale > maxScale) {
+ scaleDelta = maxScale / currentScale;
+ }
+
+ camera.scaleViewAboutPoint(scaleDelta, viewZoomPoint.getX(), viewZoomPoint.getY());
+ }
+}
diff --git a/core/src/main/java/org/piccolo2d/event/package.html b/core/src/main/java/org/piccolo2d/event/package.html
new file mode 100644
index 0000000..9fa7ad1
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/event/package.html
@@ -0,0 +1,5 @@
+
"SansSerif"
.
+ */
+ // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF,
+ // Font.PLAIN, 12); jdk 1.6+
+ public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 12);
+
+ /**
+ * Default text color if not otherwise specified in the HTML text,
+ * Color.BLACK
.
+ */
+ public static final Color DEFAULT_TEXT_COLOR = Color.BLACK;
+
+ /**
+ * The property name that identifies a change of this node's font (see
+ * {@link #getFont getFont}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final String PROPERTY_FONT = "font";
+
+ /**
+ * The property code that identifies a change of this node's font (see
+ * {@link #getFont getFont}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final int PROPERTY_CODE_FONT = 1 << 20;
+
+ /**
+ * The property name that identifies a change of this node's HTML text (see
+ * {@link #getText getText}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final String PROPERTY_TEXT = "text";
+
+ /**
+ * The property code that identifies a change of this node's HTML text (see
+ * {@link #getText getText}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final int PROPERTY_CODE_TEXT = 1 << 21;
+
+ /**
+ * The property name that identifies a change of this node's HTML text color
+ * (see {@link #getTextColor getTextColor}). Both old and new value will be set
+ * in any property change event.
+ */
+ public static final String PROPERTY_TEXT_COLOR = "text color";
+
+ /**
+ * The property code that identifies a change of this node's HTML text color
+ * (see {@link #getTextColor getTextColor}). Both old and new value will be set
+ * in any property change event.
+ */
+ public static final int PROPERTY_CODE_TEXT_COLOR = 1 << 22;
+
+ /** Underlying JLabel used to handle the rendering logic. */
+ private final JLabel label;
+
+ /** Object that encapsulates the HTML rendering logic. */
+ private transient View htmlView;
+
+ /**
+ * Create an empty HTML text node with the default font and text color.
+ */
+ public PHtmlView() {
+ this(null, DEFAULT_FONT, DEFAULT_TEXT_COLOR);
+ }
+
+ /**
+ * Create a HTML text node with the specified HTML text and the default font
+ * and text color.
+ *
+ * @param text HTML text for this HTML text node
+ */
+ public PHtmlView(final String text) {
+ this(text, DEFAULT_FONT, DEFAULT_TEXT_COLOR);
+ }
+
+ /**
+ * Create a HTML text node with the specified HTML text, font, and text
+ * color. The font and text color are used to render the HTML text if not
+ * otherwise specified via CSS.
+ *
+ * @param text HTML text for this HTML text node
+ * @param font font for this HTML text node
+ * @param textColor text color for this HTML text node
+ */
+ public PHtmlView(final String text, final Font font, final Color textColor) {
+ label = new JLabel(text);
+ label.setFont(font);
+ label.setForeground(textColor);
+ super.setBounds(0, 0, label.getPreferredSize().getWidth(), label.getPreferredSize().getHeight());
+ update();
+ }
+
+ /**
+ * Return the HTML text for this HTML text node.
+ *
+ * @return the HTML text for this HTML text node
+ */
+ public String getText() {
+ return label.getText();
+ }
+
+ /**
+ * Set the HTML text for this HTML text node to text
.
+ *
+ * + * This is a bound property. + *
+ * + * @param text HTML text for this HTML text node + */ + public void setText(final String text) { + final String oldText = label.getText(); + + if (oldText == null && text == null) { + return; + } + + if (oldText == null || !oldText.equals(text)) { + label.setText(text); + + update(); + firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, oldText, label.getText()); + } + } + + /** + * Return the font for this HTML text node. This font is used to render the + * HTML text if not otherwise specified via CSS. Defaults to + * {@link #DEFAULT_FONT}. + * + * @return the font for this HTML text node + */ + public Font getFont() { + return label.getFont(); + } + + /** + * Set the font for this HTML text node tofont
. This font is
+ * used to render the HTML text if not otherwise specified via CSS.
+ *
+ * + * This is a bound property. + *
+ * + * @param font font for this HTML text node + */ + public void setFont(final Font font) { + final Font oldFont = label.getFont(); + label.setFont(font); + update(); + firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, oldFont, label.getFont()); + } + + /** + * Return the text color for this HTML text node. This text color is used to + * render the HTML text if not otherwise specified via CSS. Defaults to + * {@link #DEFAULT_TEXT_COLOR}. + * + * @return the text color for this HTML text node + */ + public Color getTextColor() { + return label.getForeground(); + } + + /** + * Set the text color for this HTML text node totextColor
.
+ * 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 textColor) {
+ final Color oldColor = label.getForeground();
+ label.setForeground(textColor);
+ repaint();
+ firePropertyChange(PROPERTY_CODE_TEXT_COLOR, PROPERTY_TEXT_COLOR, oldColor, label.getForeground());
+ }
+
+ /**
+ * Applies all properties to the underlying JLabel, creates an htmlView and
+ * updates bounds.
+ */
+ private void update() {
+ String htmlContent = label.getText();
+ if (htmlContent == null) {
+ htmlContent = "";
+ }
+
+ htmlView = BasicHTML.createHTMLView(label, htmlContent);
+ fitHeightToHtmlContent();
+
+ repaint();
+ }
+
+ /**
+ * Resizes the height to be as tall as its rendered html. Takes wrapping
+ * into account.
+ */
+ private void fitHeightToHtmlContent() {
+ if (getWidth() > 0) {
+ htmlView.setSize((float) getWidth(), 0f);
+
+ float wrapHeight = htmlView.getPreferredSpan(View.Y_AXIS);
+ label.setSize(new Dimension((int) getWidth(), (int) wrapHeight));
+
+ if (getHeight() < wrapHeight) {
+ System.out.println(getHeight());
+ System.out.println(wrapHeight);
+ super.setBounds(getX(), getY(), getWidth(), wrapHeight);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean setBounds(final double x, final double y, final double width, final double height) {
+ final boolean boundsChanged = super.setBounds(x, y, width, height);
+ update();
+ return boundsChanged;
+ }
+
+ /** {@inheritDoc} */
+ public boolean setBounds(final Rectangle2D newBounds) {
+ final boolean boundsChanged = super.setBounds(newBounds);
+ update();
+ return boundsChanged;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * + * The HTML text is painted last, so it will appear on top of any child + * nodes. + *
+ */ + protected void paint(final PPaintContext paintContext) { + super.paint(paintContext); + paintContext.pushClip(getBounds()); + final Graphics2D g2 = paintContext.getGraphics(); + htmlView.paint(g2, getBounds().getBounds()); + paintContext.popClip(getBounds()); + } + + /** + * Return the address specified in the HTML link at the specified point in + * this node's local coordinate system, if any. + * + * @param point point in this node's local coordinate system + * @return the address specified in the HTML link at the specified point in + * this node's local coordinate system, ornull
if no
+ * such HTML link exists
+ */
+ public String getLinkAddressAt(final Point2D point) {
+ return getLinkAddressAt(point.getX(), point.getY());
+ }
+
+ /**
+ * Return the address specified in the HTML link at the specified x and y
+ * coordinates in this node's local coordinate system, if any.
+ *
+ * @param x x coordinate in this node's local coordinate system
+ * @param y y coordinate in this node's local coordinate system
+ * @return the address specified in the HTML link at the specified x and y
+ * coordinates in this node's local coordinate system, or
+ * null
if no such HTML link exists
+ */
+ public String getLinkAddressAt(final double x, final double y) {
+ int position = pointToModelIndex(x, y);
+
+ final String text = label.getText();
+
+ String address = null;
+
+ int currentPos = 0;
+ while (currentPos < text.length()) {
+ currentPos = text.indexOf('<', currentPos);
+ if (currentPos == -1 || position < currentPos) {
+ break;
+ }
+
+ final int tagStart = currentPos;
+ final int tagEnd = findTagEnd(text, currentPos);
+
+ if (tagEnd == -1) {
+ return null;
+ }
+
+ currentPos = tagEnd + 1;
+
+ final String tag = text.substring(tagStart, currentPos);
+
+ position += tag.length();
+
+ if ("null
if not found
+ */
+ private String extractHref(final String tag) {
+ int currentPos = 0;
+
+ final String href = null;
+
+ while (currentPos >= 0 && currentPos < tag.length() - 1) {
+ currentPos = tag.indexOf('=', currentPos + 1);
+ if (currentPos != -1 && isHrefAttributeAssignment(tag, currentPos)) {
+ return extractHrefValue(tag, currentPos + 1);
+ }
+ }
+ return href;
+ }
+
+ /**
+ * Starting at the character after the equal sign of an href=..., it extract
+ * the value. Handles single, double, and no quotes.
+ *
+ * @param tag tag
+ * @param startPos start position
+ * @return value of href or null if not found.
+ */
+ private String extractHrefValue(final String tag, final int startPos) {
+ int currentPos = startPos;
+
+ if (tag.charAt(currentPos) == '\"') {
+ final int startHref = currentPos + 1;
+ currentPos = tag.indexOf('\"', startHref);
+ if (currentPos == -1) {
+ return null;
+ }
+ return tag.substring(startHref, currentPos);
+ }
+ else if (currentPos < tag.length() && tag.charAt(currentPos) == '\'') {
+ final int startHref = currentPos + 1;
+ currentPos = tag.indexOf('\'', startHref);
+ if (currentPos == -1) {
+ return null;
+ }
+ return tag.substring(startHref, currentPos);
+ }
+ else {
+ final int startHref = currentPos;
+
+ if (currentPos < tag.length()) {
+ do {
+ currentPos++;
+ } while (currentPos < tag.length() && tag.charAt(currentPos) != ' ' && tag.charAt(currentPos) != '>');
+ }
+ return tag.substring(startHref, currentPos);
+ }
+ }
+
+ /**
+ * Given the position in a string returns whether it points to the equal
+ * sign of an href attribute.
+ *
+ * @param tag html code of the tag
+ * @param equalPos the index of the assignment
+ * @return true if to left of assignment is href
+ */
+ private boolean isHrefAttributeAssignment(final String tag, final int equalPos) {
+ return tag.charAt(equalPos) == '=' && equalPos > 4 && " href".equals(tag.substring(equalPos - 5, equalPos));
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/piccolo2d/nodes/PImage.java b/core/src/main/java/org/piccolo2d/nodes/PImage.java
new file mode 100644
index 0000000..bfad349
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/nodes/PImage.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.nodes;
+
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Toolkit;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import javax.imageio.ImageIO;
+import javax.swing.ImageIcon;
+
+import org.piccolo2d.PNode;
+import org.piccolo2d.util.PBounds;
+import org.piccolo2d.util.PPaintContext;
+
+
+/**
+ * PImage is a wrapper around a java.awt.Image. If this node is copied or
+ * serialized that image will be converted into a BufferedImage if it is not
+ * already one.
+ *
+ *
+ * @version 1.0
+ * @author Jesse Grosjean
+ */
+public class PImage extends PNode {
+
+ /**
+ * Allows for future serialization code to understand versioned binary
+ * formats.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The property name 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 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() {
+ }
+
+ /**
+ * 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 is null
, 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) {
+ if (url != null) {
+ setImage(Toolkit.getDefaultToolkit().getImage(url));
+ }
+ }
+
+ /**
+ * Returns the image that is shown by this node, or null if none.
+ *
+ * @return java.awt.Image being wrapped by this node
+ */
+ public Image getImage() {
+ return image;
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * 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;
+
+ if (newImage == null || newImage instanceof BufferedImage) {
+ image = newImage;
+ }
+ else {
+ image = getLoadedImage(newImage);
+ }
+
+ if (image != null) {
+ setBounds(0, 0, getImage().getWidth(null), getImage().getHeight(null));
+ invalidatePaint();
+ }
+
+ firePropertyChange(PROPERTY_CODE_IMAGE, PROPERTY_IMAGE, oldImage, image);
+ }
+
+ /**
+ * Ensures the image is loaded enough (loading is fine).
+ *
+ * @param newImage to check
+ * @return image or null if not loaded enough.
+ */
+ private Image getLoadedImage(final Image newImage) {
+ final ImageIcon imageLoader = new ImageIcon(newImage);
+ switch (imageLoader.getImageLoadStatus()) {
+ case MediaTracker.LOADING:
+ case MediaTracker.COMPLETE:
+ return imageLoader.getImage();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ final double iw = image.getWidth(null);
+ final double ih = image.getHeight(null);
+
+ final PBounds b = getBoundsReference();
+ final Graphics2D g2 = paintContext.getGraphics();
+
+ if (b.x != 0 || b.y != 0 || b.width != iw || b.height != ih) {
+ g2.translate(b.x, b.y);
+ g2.scale(b.width / iw, b.height / ih);
+ g2.drawImage(image, 0, 0, null);
+ g2.scale(iw / b.width, ih / b.height);
+ g2.translate(-b.x, -b.y);
+ }
+ else {
+ g2.drawImage(image, 0, 0, null);
+ }
+
+ }
+
+ /**
+ * 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();
+ final BufferedImage bufferedImage = toBufferedImage(image, false);
+ if (bufferedImage != null) {
+ ImageIO.write(bufferedImage, "png", out);
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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) {
+ return null;
+ }
+
+ if (!alwaysCreateCopy && image instanceof BufferedImage) {
+ return (BufferedImage) image;
+ }
+
+ BufferedImage result;
+
+ if (GraphicsEnvironment.isHeadless()) {
+ result = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
+ }
+ else {
+ final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment()
+ .getDefaultScreenDevice().getDefaultConfiguration();
+ result = graphicsConfiguration.createCompatibleImage(image.getWidth(null), image.getHeight(null));
+ }
+
+ final Graphics2D g2 = result.createGraphics();
+ g2.drawImage(image, 0, 0, null);
+ g2.dispose();
+ return result;
+ }
+}
diff --git a/core/src/main/java/org/piccolo2d/nodes/PPath.java b/core/src/main/java/org/piccolo2d/nodes/PPath.java
new file mode 100644
index 0000000..fa3a075
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/nodes/PPath.java
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.nodes;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.piccolo2d.PNode;
+import org.piccolo2d.util.PAffineTransform;
+import org.piccolo2d.util.PPaintContext;
+import org.piccolo2d.util.PUtil;
+
+
+/**
+ * PPath is a wrapper around a java.awt.geom.GeneralPath. The setBounds
+ * method works by scaling the path to fit into the specified bounds. This
+ * normally works well, but if the specified 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.
+ *
+ * One option that applications have is to call startResizeBounds
+ * before starting an interaction that may make the bounds very small, and
+ * calling endResizeBounds
when this interaction is finished. When
+ * this is done PPath will use a copy of the original path to do the resizing so
+ * the numbers in the path wont loose resolution.
+ *
+ * This class also provides methods for constructing common shapes using a + * general path. + *
+ *
+ * @version 1.0
+ * @author Jesse Grosjean
+ */
+public class PPath extends PNode {
+
+ /**
+ * Allows for future serialization code to understand versioned binary
+ * formats.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The property name that identifies a change of this node's stroke paint
+ * (see {@link #getStrokePaint getStrokePaint}). Both old and new value will
+ * be set correctly to Paint objects in any property change event.
+ */
+ public static final String PROPERTY_STROKE_PAINT = "strokePaint";
+
+ /**
+ * The property code that identifies a change of this node's stroke paint
+ * (see {@link #getStrokePaint getStrokePaint}). Both old and new value will
+ * be set correctly to Paint objects in any property change event.
+ */
+ public static final int PROPERTY_CODE_STROKE_PAINT = 1 << 16;
+
+ /**
+ * The property name that identifies a change of this node's stroke (see
+ * {@link #getStroke getStroke}). Both old and new value will be set
+ * correctly to Stroke objects in any property change event.
+ */
+ public static final String PROPERTY_STROKE = "stroke";
+
+ /**
+ * The property code that identifies a change of this node's stroke (see
+ * {@link #getStroke getStroke}). Both old and new value will be set
+ * correctly to Stroke objects in any property change event.
+ */
+ public static final int PROPERTY_CODE_STROKE = 1 << 17;
+
+ /**
+ * The property name that identifies a change of this node's path (see
+ * {@link #getPathReference getPathReference}). In any property change event
+ * the new value will be a reference to this node's path, but old value will
+ * always be null.
+ */
+ public static final String PROPERTY_PATH = "path";
+
+ /**
+ * The property code that identifies a change of this node's path (see
+ * {@link #getPathReference getPathReference}). In any property change event
+ * the new value will be a reference to this node's path, but old value will
+ * always be null.
+ */
+ public static final int PROPERTY_CODE_PATH = 1 << 18;
+
+ private static final Rectangle2D.Float TEMP_RECTANGLE = new Rectangle2D.Float();
+ private static final RoundRectangle2D.Float TEMP_ROUNDRECTANGLE = new RoundRectangle2D.Float();
+ private static final Ellipse2D.Float TEMP_ELLIPSE = new Ellipse2D.Float();
+ private static final PAffineTransform TEMP_TRANSFORM = new PAffineTransform();
+ private static final BasicStroke DEFAULT_STROKE = new BasicStroke(1.0f);
+ private static final Color DEFAULT_STROKE_PAINT = Color.black;
+
+ private transient GeneralPath path;
+ private transient GeneralPath resizePath;
+ private transient Stroke stroke;
+ private transient boolean updatingBoundsFromPath;
+ private Paint strokePaint;
+
+ /**
+ * Creates a PPath object in the shape of a rectangle.
+ *
+ * @param x left of the rectangle
+ * @param y top of the rectangle
+ * @param width width of the rectangle
+ * @param height height of the rectangle
+ *
+ * @return created rectangle
+ */
+ public static PPath createRectangle(final float x, final float y, final float width, final float height) {
+ TEMP_RECTANGLE.setFrame(x, y, width, height);
+ final PPath result = new PPath(TEMP_RECTANGLE);
+ result.setPaint(Color.white);
+ return result;
+ }
+
+ /**
+ * Creates a PPath object in the shape of a rounded rectangle.
+ *
+ * @param x left of the rectangle
+ * @param y top of the rectangle
+ * @param width width of the rectangle
+ * @param height height of the rectangle
+ * @param arcWidth the arc width at the corners of the rectangle
+ * @param arcHeight the arc height at the corners of the rectangle
+ *
+ * @return created rounded rectangle
+ */
+ public static PPath createRoundRectangle(final float x, final float y, final float width, final float height,
+ final float arcWidth, final float arcHeight) {
+ TEMP_ROUNDRECTANGLE.setRoundRect(x, y, width, height, arcWidth, arcHeight);
+ final PPath result = new PPath(TEMP_ROUNDRECTANGLE);
+ result.setPaint(Color.white);
+ return result;
+ }
+
+ /**
+ * Creates a PPath object in the shape of an ellipse.
+ *
+ * @param x left of the ellipse
+ * @param y top of the ellipse
+ * @param width width of the ellipse
+ * @param height height of the ellipse
+ *
+ * @return created ellipse
+ */
+ public static PPath createEllipse(final float x, final float y, final float width, final float height) {
+ TEMP_ELLIPSE.setFrame(x, y, width, height);
+ final PPath result = new PPath(TEMP_ELLIPSE);
+ result.setPaint(Color.white);
+ return result;
+ }
+
+ /**
+ * Creates a PPath in the shape of a line.
+ *
+ * @param x1 x component of the first point
+ * @param y1 y component of the first point
+ * @param x2 x component of the second point
+ * @param y2 y component of the second point
+ *
+ * @return created line
+ */
+ public static PPath createLine(final float x1, final float y1, final float x2, final float y2) {
+ final PPath result = new PPath();
+ result.moveTo(x1, y1);
+ result.lineTo(x2, y2);
+ result.setPaint(Color.white);
+ return result;
+ }
+
+ /**
+ * Creates a PPath for the poly-line for the given points.
+ *
+ * @param points array of points for the point lines
+ *
+ * @return created poly-line for the given points
+ */
+ public static PPath createPolyline(final Point2D[] points) {
+ final PPath result = new PPath();
+ result.setPathToPolyline(points);
+ result.setPaint(Color.white);
+ return result;
+ }
+
+ /**
+ * Creates a PPath for the poly-line for the given points.
+ *
+ * @param xp array of x components of the points of the poly-lines
+ * @param yp array of y components of the points of the poly-lines
+ *
+ * @return created poly-line for the given points
+ */
+ public static PPath createPolyline(final float[] xp, final float[] yp) {
+ final PPath result = new PPath();
+ result.setPathToPolyline(xp, yp);
+ result.setPaint(Color.white);
+ return result;
+ }
+
+ /**
+ * Creates an empty PPath with the default paint and stroke.
+ */
+ public PPath() {
+ strokePaint = DEFAULT_STROKE_PAINT;
+ stroke = DEFAULT_STROKE;
+ path = new GeneralPath();
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Construct this path with the given shape and stroke. This method may be
+ * used to optimize the creation of a large number of PPaths. Normally
+ * PPaths have a default stroke of width one, but when a path has a non null
+ * stroke it takes significantly longer to compute its bounds. This method
+ * allows you to override that default stroke before the bounds are ever
+ * calculated, so if you pass in a null stroke here you won't ever have to
+ * pay that bounds calculation price if you don't need to.
+ *
+ * @param aShape desired shape or null if you desire an empty path
+ * @param aStroke desired stroke
+ */
+ public PPath(final Shape aShape, final Stroke aStroke) {
+ this();
+ stroke = aStroke;
+ if (aShape != null) {
+ append(aShape, false);
+ }
+ }
+
+ /**
+ * Returns the stroke paint of the PPath.
+ *
+ * @return stroke paint of the PPath
+ */
+ public Paint getStrokePaint() {
+ return strokePaint;
+ }
+
+ /**
+ * 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, 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;
+ updateBoundsFromPath();
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_STROKE, PROPERTY_STROKE, old, stroke);
+ }
+
+ /** 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;
+ }
+
+ /**
+ * Set the bounds of this path. This method works by scaling the path to fit
+ * into the specified bounds. This normally works well, but if the specified
+ * 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(final double x, final double y, final double width, final double height) {
+ if (updatingBoundsFromPath || path == null) {
+ return;
+ }
+
+ if (resizePath != null) {
+ path.reset();
+ path.append(resizePath, false);
+ }
+
+ final Rectangle2D pathBounds = path.getBounds2D();
+ final Rectangle2D pathStrokeBounds = getPathBoundsWithStroke();
+ final double strokeOutset = Math.max(pathStrokeBounds.getWidth() - pathBounds.getWidth(), pathStrokeBounds
+ .getHeight()
+ - pathBounds.getHeight());
+
+ double adjustedX = x + strokeOutset / 2;
+ double adjustedY = y + strokeOutset / 2;
+ double adjustedWidth = width - strokeOutset;
+ double adjustedHeight = height - strokeOutset;
+
+ 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(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)) {
+ return true;
+ }
+ else if (stroke != null && strokePaint != null) {
+ return stroke.createStrokedShape(path).intersects(aBounds);
+ }
+ }
+ 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();
+ }
+ else {
+ return path.getBounds2D();
+ }
+ }
+
+ /**
+ * Recomputes the bounds taking stroke into account.
+ */
+ public void updateBoundsFromPath() {
+ updatingBoundsFromPath = true;
+ if (path == null) {
+ resetBounds();
+ }
+ else {
+ final Rectangle2D b = getPathBoundsWithStroke();
+ setBounds(b.getX(), b.getY(), b.getWidth(), b.getHeight());
+ }
+ updatingBoundsFromPath = false;
+ }
+
+ /**
+ * 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();
+
+ if (p != null) {
+ g2.setPaint(p);
+ g2.fill(path);
+ }
+
+ if (stroke != null && strokePaint != null) {
+ g2.setPaint(strokePaint);
+ g2.setStroke(stroke);
+ g2.draw(path);
+ }
+ }
+
+ /**
+ * 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);
+ updateBoundsFromPath();
+ 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);
+ updateBoundsFromPath();
+ invalidatePaint();
+ }
+
+ /**
+ * Adds a curved segment, defined by two new points, to the path by drawing
+ * a Quadratic curve that intersects both the current coordinates and the
+ * coordinates (x2, y2), using the specified point (x1, y1) as a quadratic
+ * parametric control point.
+ *
+ * @param x1 x component of quadratic parametric control point
+ * @param y1 y component of quadratic parametric control point
+ * @param x2 x component of point through which quad curve will pass
+ * @param y2 y component of point through which quad curve will pass
+ */
+ 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);
+ updateBoundsFromPath();
+ invalidatePaint();
+ }
+
+ /**
+ * Adds a curved segment, defined by three new points, to the path by
+ * drawing a Bézier curve that intersects both the current coordinates and
+ * the coordinates (x3, y3), using the specified points (x1, y1) and (x2,
+ * y2) as Bézier control points.
+ *
+ * @param x1 x component of first Bézier control point
+ * @param y1 y component of first Bézier control point
+ * @param x2 x component of second Bézier control point
+ * @param y2 y component of second Bézier control point
+ * @param x3 x component of point through which curve must pass
+ * @param y3 y component of point through which curve must pass
+ */
+ 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);
+ updateBoundsFromPath();
+ 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);
+ updateBoundsFromPath();
+ 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());
+ for (int i = 1; i < points.length; i++) {
+ path.lineTo((float) points[i].getX(), (float) points[i].getY());
+ }
+ firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
+ updateBoundsFromPath();
+ 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]);
+ for (int i = 1; i < xp.length; i++) {
+ path.lineTo(xp[i], yp[i]);
+ }
+ firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
+ updateBoundsFromPath();
+ invalidatePaint();
+ }
+
+ /**
+ * Marks the path as closed. Making changes to it impossible.
+ */
+ public void closePath() {
+ path.closePath();
+ firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
+ updateBoundsFromPath();
+ invalidatePaint();
+ }
+
+ /**
+ * Empties the path.
+ */
+ public void reset() {
+ path.reset();
+ firePropertyChange(PROPERTY_CODE_PATH, PROPERTY_PATH, null, path);
+ updateBoundsFromPath();
+ invalidatePaint();
+ }
+
+ /**
+ * 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);
+ path = PUtil.readPath(in);
+ }
+}
diff --git a/core/src/main/java/org/piccolo2d/nodes/PText.java b/core/src/main/java/org/piccolo2d/nodes/PText.java
new file mode 100644
index 0000000..1eaa2fb
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/nodes/PText.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.nodes;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.font.LineBreakMeasurer;
+import java.awt.font.TextAttribute;
+import java.awt.font.TextLayout;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.util.ArrayList;
+
+import org.piccolo2d.PNode;
+import org.piccolo2d.util.PPaintContext;
+
+
+/**
+ * PText is a multi-line text node. The text will flow to base on the
+ * width of the node's bounds.
+ *
+ * @version 1.1
+ * @author Jesse Grosjean
+ */
+public class PText extends PNode {
+
+ /**
+ * Allows for future serialization code to understand versioned binary
+ * formats.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The property name that identifies a change of this node's text (see
+ * {@link #getText getText}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final String PROPERTY_TEXT = "text";
+
+ /**
+ * The property code that identifies a change of this node's text (see
+ * {@link #getText getText}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final int PROPERTY_CODE_TEXT = 1 << 19;
+
+ /**
+ * The property name that identifies a change of this node's font (see
+ * {@link #getFont getFont}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final String PROPERTY_FONT = "font";
+
+ /**
+ * The property code that identifies a change of this node's font (see
+ * {@link #getFont getFont}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final int PROPERTY_CODE_FONT = 1 << 20;
+
+ /**
+ * The property name that identifies a change of this node's text paint (see
+ * {@link #getTextPaint getTextPaint}). Both old and new value will be set
+ * in any property change event.
+ *
+ * @since 1.3
+ */
+ public static final String PROPERTY_TEXT_PAINT = "text paint";
+
+ /**
+ * The property code that identifies a change of this node's text paint (see
+ * {@link #getTextPaint getTextPaint}). Both old and new value will be set
+ * in any property change event.
+ *
+ * @since 1.3
+ */
+ public static final int PROPERTY_CODE_TEXT_PAINT = 1 << 21;
+
+ /**
+ * Default font, 12 point "SansSerif"
. Will be made final in
+ * version 2.0.
+ */
+ // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF,
+ // Font.PLAIN, 12); jdk 1.6+
+ public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 12);
+
+ /**
+ * Default greek threshold, 5.5d
. Will be made final in version
+ * 2.0.
+ */
+ public static final double DEFAULT_GREEK_THRESHOLD = 5.5d;
+
+ /**
+ * Default horizontal alignment, Component.LEFT_ALIGNMENT
.
+ *
+ * @since 1.3
+ */
+ public static final float DEFAULT_HORIZONTAL_ALIGNMENT = Component.LEFT_ALIGNMENT;
+
+ /**
+ * Default text, ""
.
+ *
+ * @since 1.3
+ */
+ public static final String DEFAULT_TEXT = "";
+
+ /**
+ * Default text paint, Color.BLACK
.
+ *
+ * @since 1.3
+ */
+ public static final Paint DEFAULT_TEXT_PAINT = Color.BLACK;
+
+ /** Empty text layout array. */
+ private static final TextLayout[] EMPTY_TEXT_LAYOUT_ARRAY = new TextLayout[0];
+
+ /** Text for this text node. */
+ private String text = DEFAULT_TEXT;
+
+ /** Text paint for this text node. */
+ private Paint textPaint = DEFAULT_TEXT_PAINT;
+
+ /** Font for this text node. */
+ private Font font = DEFAULT_FONT;
+
+ /**
+ * Greek threshold in screen font size for this text node. Will be made
+ * private in version 2.0.
+ */
+ protected double greekThreshold = DEFAULT_GREEK_THRESHOLD;
+
+ /** Horizontal alignment for this text node. */
+ private float horizontalAlignment = DEFAULT_HORIZONTAL_ALIGNMENT;
+
+ /**
+ * True if this text node should constrain its height to the height of its
+ * text.
+ */
+ private boolean constrainHeightToTextHeight = true;
+
+ /**
+ * True if this text node should constrain its height to the height of its
+ * text.
+ */
+ private boolean constrainWidthToTextWidth = true;
+
+ /** One or more lines of text layout. */
+ private transient TextLayout[] lines;
+
+ /**
+ * Create a new text node with no text (""
).
+ */
+ public PText() {
+ super();
+ setText(DEFAULT_TEXT);
+ }
+
+ /**
+ * Create a new text node with the specified text.
+ *
+ * @param text text for this text node
+ */
+ public PText(final String text) {
+ this();
+ setText(text);
+ }
+
+ /**
+ * Return the horizontal alignment for this text node. The horizontal
+ * alignment will be one of Component.LEFT_ALIGNMENT
,
+ * Component.CENTER_ALIGNMENT
, or
+ * Component.RIGHT_ALIGNMENT
. Defaults to
+ * {@link #DEFAULT_HORIZONTAL_ALIGNMENT}.
+ *
+ * @since 1.3
+ * @return the horizontal alignment for this text node
+ */
+ public float getHorizontalAlignment() {
+ return horizontalAlignment;
+ }
+
+ /**
+ * Set the horizontal alignment for this text node to
+ * horizontalAlignment
.
+ *
+ * @since 1.3
+ * @param horizontalAlignment horizontal alignment, must be one of
+ * Component.LEFT_ALIGNMENT
,
+ * Component.CENTER_ALIGNMENT
, or
+ * Component.RIGHT_ALIGNMENT
+ */
+ public void setHorizontalAlignment(final float horizontalAlignment) {
+ if (!validHorizontalAlignment(horizontalAlignment)) {
+ throw new IllegalArgumentException("horizontalAlignment must be one of Component.LEFT_ALIGNMENT, "
+ + "Component.CENTER_ALIGNMENT, or Component.RIGHT_ALIGNMENT");
+ }
+ this.horizontalAlignment = horizontalAlignment;
+ }
+
+ /**
+ * Return true if the specified horizontal alignment is one of
+ * Component.LEFT_ALIGNMENT
,
+ * Component.CENTER_ALIGNMENT
, or
+ * Component.RIGHT_ALIGNMENT
.
+ *
+ * @param horizontalAlignment horizontal alignment
+ * @return true if the specified horizontal alignment is one of
+ * Component.LEFT_ALIGNMENT
,
+ * Component.CENTER_ALIGNMENT
, or
+ * Component.RIGHT_ALIGNMENT
+ */
+ private static boolean validHorizontalAlignment(final float horizontalAlignment) {
+ return Component.LEFT_ALIGNMENT == horizontalAlignment || Component.CENTER_ALIGNMENT == horizontalAlignment
+ || Component.RIGHT_ALIGNMENT == horizontalAlignment;
+ }
+
+ /**
+ * Return the paint used to paint this node's text.
+ *
+ * @return the paint used to paint this node's text
+ */
+ public Paint getTextPaint() {
+ return textPaint;
+ }
+
+ /**
+ * Set the paint used to paint this node's text to textPaint
.
+ *
+ *
+ * This is a bound property. + *
+ * + * @param textPaint text paint + */ + public void setTextPaint(final Paint textPaint) { + if (textPaint == this.textPaint) { + return; + } + final Paint oldTextPaint = this.textPaint; + this.textPaint = textPaint; + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_TEXT_PAINT, PROPERTY_TEXT_PAINT, oldTextPaint, this.textPaint); + } + + /** + * Return true if this text node should constrain its width to the width of + * its text. Defaults totrue
.
+ *
+ * @return true if this text node should constrain its width to the width of
+ * its text
+ */
+ public boolean isConstrainWidthToTextWidth() {
+ return constrainWidthToTextWidth;
+ }
+
+ /**
+ * Set to true
if this text node should constrain its width to
+ * the width of its text.
+ *
+ * @param constrainWidthToTextWidth true if this text node should constrain
+ * its width to the width of its text
+ */
+ public void setConstrainWidthToTextWidth(final boolean constrainWidthToTextWidth) {
+ this.constrainWidthToTextWidth = constrainWidthToTextWidth;
+ recomputeLayout();
+ }
+
+ /**
+ * Return true if this text node should constrain its height to the height
+ * of its text. Defaults to true
.
+ *
+ * @return true if this text node should constrain its height to the height
+ * of its text
+ */
+ public boolean isConstrainHeightToTextHeight() {
+ return constrainHeightToTextHeight;
+ }
+
+ /**
+ * Set to true
if this text node should constrain its height to
+ * the height of its text.
+ *
+ * @param constrainHeightToTextHeight true if this text node should
+ * constrain its height to the width of its text
+ */
+ public void setConstrainHeightToTextHeight(final boolean constrainHeightToTextHeight) {
+ this.constrainHeightToTextHeight = constrainHeightToTextHeight;
+ recomputeLayout();
+ }
+
+ /**
+ * Return the greek threshold in screen font size. When the screen font size
+ * will be below this threshold the text is rendered as 'greek' instead of
+ * drawing the text glyphs. Defaults to {@link #DEFAULT_GREEK_THRESHOLD}.
+ *
+ * @see PText#paintGreek(PPaintContext)
+ * @return the current greek threshold in screen font size
+ */
+ public double getGreekThreshold() {
+ return greekThreshold;
+ }
+
+ /**
+ * Set the greek threshold in screen font size to
+ * greekThreshold
. When the screen font size will be below this
+ * threshold the text is rendered as 'greek' instead of drawing the text
+ * glyphs.
+ *
+ * @see PText#paintGreek(PPaintContext)
+ * @param greekThreshold greek threshold in screen font size
+ */
+ public void setGreekThreshold(final double greekThreshold) {
+ this.greekThreshold = greekThreshold;
+ invalidatePaint();
+ }
+
+ /**
+ * Return the text for this text node. Defaults to {@link #DEFAULT_TEXT}.
+ *
+ * @return the text for this text node
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Set the text for this node to text
. The text will be broken
+ * up into multiple lines based on the size of the text and the bounds width
+ * of this node.
+ *
+ * + * This is a bound property. + *
+ * + * @param newText text for this text node + */ + public void setText(final String newText) { + if (newText == null && text == null || newText != null && newText.equals(text)) { + return; + } + + final String oldText = text; + if (newText == null) { + text = DEFAULT_TEXT; + } + else { + text = newText; + } + lines = null; + recomputeLayout(); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, oldText, text); + } + + /** + * Return the font for this text node. Defaults to {@link #DEFAULT_FONT}. + * + * @return the font for this text node + */ + public Font getFont() { + return font; + } + + /** + * Set the font for this text node tofont
. Note that in
+ * Piccolo if you want to change the size of a text object it's often a
+ * better idea to scale the PText node instead of changing the font size to
+ * get that same effect. Using very large font sizes can slow performance.
+ *
+ * + * This is a bound property. + *
+ * + * @param font font for this text node + */ + public void setFont(final Font font) { + if (font == this.font) { + return; + } + final Font oldFont = this.font; + if (font == null) { + this.font = DEFAULT_FONT; + } + else { + this.font = font; + } + + lines = null; + recomputeLayout(); + invalidatePaint(); + firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, oldFont, this.font); + } + + /** + * Compute the bounds of the text wrapped by this node. The text layout is + * wrapped based on the bounds of this node. + */ + public void recomputeLayout() { + final ArrayList linesList = new ArrayList(); + double textWidth = 0; + double textHeight = 0; + + if (text != null && text.length() > 0) { + final AttributedString atString = new AttributedString(text); + atString.addAttribute(TextAttribute.FONT, getFont()); + final AttributedCharacterIterator itr = atString.getIterator(); + final LineBreakMeasurer measurer = new LineBreakMeasurer(itr, PPaintContext.RENDER_QUALITY_HIGH_FRC); + final float availableWidth; + if (constrainWidthToTextWidth) { + availableWidth = Float.MAX_VALUE; + } + else { + availableWidth = (float) getWidth(); + } + + int nextLineBreakOffset = text.indexOf('\n'); + if (nextLineBreakOffset == -1) { + nextLineBreakOffset = Integer.MAX_VALUE; + } + else { + nextLineBreakOffset++; + } + + while (measurer.getPosition() < itr.getEndIndex()) { + final TextLayout aTextLayout = computeNextLayout(measurer, availableWidth, nextLineBreakOffset); + + if (nextLineBreakOffset == measurer.getPosition()) { + nextLineBreakOffset = text.indexOf('\n', measurer.getPosition()); + if (nextLineBreakOffset == -1) { + nextLineBreakOffset = Integer.MAX_VALUE; + } + else { + nextLineBreakOffset++; + } + } + + linesList.add(aTextLayout); + textHeight += aTextLayout.getAscent(); + textHeight += aTextLayout.getDescent() + aTextLayout.getLeading(); + textWidth = Math.max(textWidth, aTextLayout.getAdvance()); + } + } + + lines = (TextLayout[]) linesList.toArray(EMPTY_TEXT_LAYOUT_ARRAY); + + if (constrainWidthToTextWidth || constrainHeightToTextHeight) { + double newWidth = getWidth(); + double newHeight = getHeight(); + + if (constrainWidthToTextWidth) { + newWidth = textWidth; + } + + if (constrainHeightToTextHeight) { + newHeight = textHeight; + } + + super.setBounds(getX(), getY(), newWidth, newHeight); + } + } + + /** + * Compute the next layout using the specified line break measurer, + * available width, and next line break offset. + * + * @param lineBreakMeasurer line break measurer + * @param availableWidth available width + * @param nextLineBreakOffset next line break offset + * @return the next layout computed using the specified line break measurer, + * available width, and next line break offset + */ + protected TextLayout computeNextLayout(final LineBreakMeasurer lineBreakMeasurer, final float availableWidth, + final int nextLineBreakOffset) { + return lineBreakMeasurer.nextLayout(availableWidth, nextLineBreakOffset, false); + } + + /** + * Paint greek with the specified paint context. + * + * @since 1.3 + * @param paintContext paint context + */ + protected void paintGreek(final PPaintContext paintContext) { + // empty + } + + /** + * Paint text with the specified paint context. + * + * @since 1.3 + * @param paintContext paint context + */ + protected void paintText(final PPaintContext paintContext) { + final float x = (float) getX(); + float y = (float) getY(); + final float bottomY = (float) getHeight() + y; + + final Graphics2D g2 = paintContext.getGraphics(); + + if (lines == null) { + recomputeLayout(); + repaint(); + return; + } + + g2.setPaint(textPaint); + + for (int i = 0; i < lines.length; i++) { + final TextLayout tl = lines[i]; + y += tl.getAscent(); + + if (bottomY < y) { + return; + } + + final float offset = (float) (getWidth() - tl.getAdvance()) * horizontalAlignment; + + tl.draw(g2, x + offset, y); + + y += tl.getDescent() + tl.getLeading(); + } + } + + /** {@inheritDoc} */ + protected void paint(final PPaintContext paintContext) { + super.paint(paintContext); + if (textPaint == null) { + return; + } + final float screenFontSize = getFont().getSize() * (float) paintContext.getScale(); + if (screenFontSize <= greekThreshold) { + paintGreek(paintContext); + } + paintText(paintContext); + } + + /** {@inheritDoc} */ + protected void internalUpdateBounds(final double x, final double y, final double width, final double height) { + recomputeLayout(); + } +} diff --git a/core/src/main/java/org/piccolo2d/nodes/package.html b/core/src/main/java/org/piccolo2d/nodes/package.html new file mode 100644 index 0000000..1039fc0 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/nodes/package.html @@ -0,0 +1,5 @@ + +This package contains nodes that may be useful for Piccolo applications. These +nodes are intended to be useful, but not definitive. Many applications will also +end up defining their nodes which can be used along with these. + diff --git a/core/src/main/java/org/piccolo2d/package.html b/core/src/main/java/org/piccolo2d/package.html new file mode 100644 index 0000000..be1fa0e --- /dev/null +++ b/core/src/main/java/org/piccolo2d/package.html @@ -0,0 +1,18 @@ + +Piccolo is a general-purpose Java-based engine that supports 2D visualizations. +A primary characteristic of Piccolo is that it is designed to support zoomable +information spaces, although any particular applications may or may not +take advantage of this feature. Piccolo is implemented entirely in Java 2 (1.4), +and as such runs identically on any platform that supports Java 2.+ +Piccolo is not an application in itself, but rather it is an engine that is designed +to support applications that require the ability to create, manipulate, and render +object-oriented graphics. If you are familiar with the terminology of 3D graphics, +Piccolo supports a scenegraph. This is a data structure that represents a hierarchy +of graphical objects. Piccolo uses a tuned run-time system to render the scenegraph +as quickly as possible to support interactive applications.
+
+This is the root package for all Jazz classes. It contains the core scenegraph
+classes itself, and in addition, contains the activities
, event
,
+nodes
and util
packages that are used to build Jazz applications.
+
diff --git a/core/src/main/java/org/piccolo2d/util/PAffineTransform.java b/core/src/main/java/org/piccolo2d/util/PAffineTransform.java
new file mode 100644
index 0000000..1b0f93d
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/util/PAffineTransform.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.util;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * PAffineTransform is a subclass of AffineTransform that has been
+ * extended with convenience methods.
+ *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PAffineTransform extends AffineTransform { + /** + * Allows for future serialization code to understand versioned binary + * formats. + */ + private static final long serialVersionUID = 1L; + + /** Used internally to speed up computation. */ + private static final double[] PTS1 = new double[8]; + + /** Used internally to speed up computation. */ + private static final double[] PTS2 = new double[8]; + + /** + * Constructs a new AffineTransform representing the Identity + * transformation. + */ + public PAffineTransform() { + super(); + } + + /** + * Constructs a new AffineTransform from an array of double precision values + * representing either the 4 non-translation entries or the 6 specifiable + * entries of the 3x3 transformation matrix. The values are retrieved from + * the array as { m00 m10 m01 m11 [m02 m12]}. + * + * @param flatmatrix the double array containing the values to be set in the + * new AffineTransform object. The length of the array is assumed + * to be at least 4. If the length of the array is less than 6, + * only the first 4 values are taken. If the length of the array + * is greater than 6, the first 6 values are taken. + */ + public PAffineTransform(final double[] flatmatrix) { + super(flatmatrix); + } + + /** + * Constructs a new AffineTransform from an array of floating point values + * representing either the 4 non-translation entries or the 6 specifiable + * entries of the 3x3 transformation matrix. The values are retrieved from + * the array as { m00 m10 m01 m11 [m02 m12]}. + * + * @param flatmatrix the float array containing the values to be set in the + * new AffineTransform object. The length of the array is assumed + * to be at least 4. If the length of the array is less than 6, + * only the first 4 values are taken. If the length of the array + * is greater than 6, the first 6 values are taken. + */ + public PAffineTransform(final float[] flatmatrix) { + super(flatmatrix); + } + + /** + * Constructs a new AffineTransform from 6 double precision values + * representing the 6 specifiable entries of the 3x3 transformation matrix. + * + * @param m00 the X coordinate scaling element of the 3x3 matrix + * @param m10 the Y coordinate shearing element of the 3x3 matrix + * @param m01 the X coordinate shearing element of the 3x3 matrix + * @param m11 the Y coordinate scaling element of the 3x3 matrix + * @param m02 the X coordinate translation element of the 3x3 matrix + * @param m12 the Y coordinate translation element of the 3x3 matrix + */ + public PAffineTransform(final double m00, final double m10, final double m01, final double m11, final double m02, + final double m12) { + super(m00, m10, m01, m11, m02, m12); + } + + /** + * Constructs a new AffineTransform from 6 floating point values + * representing the 6 specifiable entries of the 3x3 transformation matrix. + * + * @param m00 the X coordinate scaling element of the 3x3 matrix + * @param m10 the Y coordinate shearing element of the 3x3 matrix + * @param m01 the X coordinate shearing element of the 3x3 matrix + * @param m11 the Y coordinate scaling element of the 3x3 matrix + * @param m02 the X coordinate translation element of the 3x3 matrix + * @param m12 the Y coordinate translation element of the 3x3 matrix + */ + public PAffineTransform(final float m00, final float m10, final float m01, final float m11, final float m02, + final float m12) { + super(m00, m10, m01, m11, m02, m12); + } + + /** + * Constructs a new AffineTransform that is a copy of the specified + * AffineTransform object. + * + * @param tx transform to copy + */ + public PAffineTransform(final AffineTransform tx) { + super(tx); + } + + /** + * Scales the transform about the given point by the given scale. + * + * @param scale to transform the transform by + * @param x x coordinate around which the scale should take place + * @param y y coordinate around which the scale should take place + */ + public void scaleAboutPoint(final double scale, final double x, final double y) { + translate(x, y); + scale(scale, scale); + translate(-x, -y); + } + + /** + * Returns the scale applied to this transform. Note that it does so by + * computing the change in length of a unit segment after being passed + * through the transform. This means that a transform that a transform that + * doesn't scale the in the x but doubles the y will be reported as 2. + * + * @return the different in length of a unit segment after being + * transformed. + */ + public double getScale() { + PTS1[0] = 0; // x1 + PTS1[1] = 0; // y1 + PTS1[2] = 1; // x2 + PTS1[3] = 0; // y2 + transform(PTS1, 0, PTS2, 0, 2); + return Point2D.distance(PTS2[0], PTS2[1], PTS2[2], PTS2[3]); + } + + /** + * Sets the scale about to the origin of this transform to the scale + * provided. + * + * @param scale The desired resulting scale + */ + public void setScale(final double scale) { + if (scale == 0) { + throw new PAffineTransformException("Can't set scale to 0", this); + } + + scaleAboutPoint(scale / getScale(), 0, 0); + } + + /** + * Applies modifies the transform so that it translates by the given offset. + * + * @param tx x translation of resulting transform + * @param ty y translation of resulting transform + */ + public void setOffset(final double tx, final double ty) { + setTransform(getScaleX(), getShearY(), getShearX(), getScaleY(), tx, ty); + } + + /** + * Returns the rotation applied to this affine transform in radians. The + * value returned will be between 0 and 2pi. + * + * @return rotation in radians + */ + public double getRotation() { + PTS1[0] = 0; // x1 + PTS1[1] = 0; // y1 + PTS1[2] = 1; // x2 + PTS1[3] = 0; // y2 + + transform(PTS1, 0, PTS2, 0, 2); + + final double dy = Math.abs(PTS2[3] - PTS2[1]); + final double l = Point2D.distance(PTS2[0], PTS2[1], PTS2[2], PTS2[3]); + double rotation = Math.asin(dy / l); + + // correct for quadrant + if (PTS2[3] - PTS2[1] > 0) { + if (PTS2[2] - PTS2[0] < 0) { + rotation = Math.PI - rotation; + } + } + else if (PTS2[2] - PTS2[0] > 0) { + rotation = 2 * Math.PI - rotation; + } + else { + rotation = rotation + Math.PI; + } + + return rotation; + } + + /** + * Set rotation in radians. This is not cumulative. + * + * @param theta desired rotation in radians. + */ + public void setRotation(final double theta) { + rotate(theta - getRotation()); + } + + /** + * Applies the transform to the provided dimension. + * + * @param dimSrc source dimension + * @param dimDst will be changed to be the transformed dimension, may be + * null + * @return the transformed dimension + */ + public Dimension2D transform(final Dimension2D dimSrc, final Dimension2D dimDst) { + final Dimension2D result; + if (dimDst == null) { + result = (Dimension2D) dimSrc.clone(); + } + else { + result = dimDst; + } + + PTS1[0] = dimSrc.getWidth(); + PTS1[1] = dimSrc.getHeight(); + deltaTransform(PTS1, 0, PTS2, 0, 1); + result.setSize(PTS2[0], PTS2[1]); + return result; + } + + /** + * Applies the inverse of this transform to the source point if possible. + * + * @since 1.3 + * @param ptSrc point to be transformed + * @param ptDst result of transform will be placed in this point + * + * @return the transformed point + */ + public Point2D inverseTransform(final Point2D ptSrc, final Point2D ptDst) { + try { + return super.inverseTransform(ptSrc, ptDst); + } + catch (final NoninvertibleTransformException e) { + throw new PAffineTransformException("Could not invert Transform", e, this); + } + } + + /** + * Applies the inverse of this transform to the source dimension if + * possible. + * + * @param dimSrc dimension to be transformed + * @param dimDst result of transform will be placed in this dimension + * + * @return the transformed dimension + */ + public Dimension2D inverseTransform(final Dimension2D dimSrc, final Dimension2D dimDst) { + final Dimension2D result; + if (dimDst == null) { + result = (Dimension2D) dimSrc.clone(); + } + else { + result = dimDst; + } + + final double width = dimSrc.getWidth(); + final double height = dimSrc.getHeight(); + final double m00 = getScaleX(); + final double m11 = getScaleY(); + final double m01 = getShearX(); + final double m10 = getShearY(); + final double det = m00 * m11 - m01 * m10; + + if (Math.abs(det) > Double.MIN_VALUE) { + result.setSize((width * m11 - height * m01) / det, (height * m00 - width * m10) / det); + } + else { + throw new PAffineTransformException("Could not invert transform", this); + } + + return result; + } + + /** + * Applies this transform to the source rectangle and stores the result in + * rectDst. + * + * @param rectSrc rectangle to be transformed + * @param rectDst result of transform will be placed in this rectangle + * + * @return the transformed rectangle + */ + public Rectangle2D transform(final Rectangle2D rectSrc, final Rectangle2D rectDst) { + final Rectangle2D result; + if (rectDst == null) { + result = (Rectangle2D) rectSrc.clone(); + } + else { + result = rectDst; + } + + if (rectSrc.isEmpty()) { + result.setRect(rectSrc); + if (result instanceof PBounds) { + ((PBounds) result).reset(); + } + return result; + } + + double scale; + + switch (getType()) { + case AffineTransform.TYPE_IDENTITY: + if (rectSrc != result) { + result.setRect(rectSrc); + } + break; + + case AffineTransform.TYPE_TRANSLATION: + result.setRect(rectSrc.getX() + getTranslateX(), rectSrc.getY() + getTranslateY(), rectSrc.getWidth(), + rectSrc.getHeight()); + break; + + case AffineTransform.TYPE_UNIFORM_SCALE: + scale = getScaleX(); + result.setRect(rectSrc.getX() * scale, rectSrc.getY() * scale, rectSrc.getWidth() * scale, rectSrc + .getHeight() + * scale); + break; + + case AffineTransform.TYPE_TRANSLATION | AffineTransform.TYPE_UNIFORM_SCALE: + scale = getScaleX(); + result.setRect(rectSrc.getX() * scale + getTranslateX(), rectSrc.getY() * scale + getTranslateY(), + rectSrc.getWidth() * scale, rectSrc.getHeight() * scale); + break; + + default: + final double[] pts = rectToArray(rectSrc); + transform(pts, 0, pts, 0, 4); + rectFromArray(result, pts); + break; + } + + return result; + } + + /** + * Applies the inverse of this transform to the source rectangle and stores + * the result in rectDst. + * + * @param rectSrc rectangle to be transformed + * @param rectDst result of transform will be placed in this rectangle + * + * @return the transformed rectangle + */ + public Rectangle2D inverseTransform(final Rectangle2D rectSrc, final Rectangle2D rectDst) { + final Rectangle2D result; + if (rectDst == null) { + result = (Rectangle2D) rectSrc.clone(); + } + else { + result = rectDst; + } + + if (rectSrc.isEmpty()) { + result.setRect(rectSrc); + if (result instanceof PBounds) { + ((PBounds) result).reset(); + } + return result; + } + + double scale; + + switch (getType()) { + case AffineTransform.TYPE_IDENTITY: + if (rectSrc != result) { + result.setRect(rectSrc); + } + break; + + case AffineTransform.TYPE_TRANSLATION: + result.setRect(rectSrc.getX() - getTranslateX(), rectSrc.getY() - getTranslateY(), rectSrc.getWidth(), + rectSrc.getHeight()); + break; + + case AffineTransform.TYPE_UNIFORM_SCALE: + scale = getScaleX(); + if (scale == 0) { + throw new PAffineTransformException("Could not invertTransform rectangle", this); + } + + result.setRect(rectSrc.getX() / scale, rectSrc.getY() / scale, rectSrc.getWidth() / scale, rectSrc + .getHeight() + / scale); + break; + + case AffineTransform.TYPE_TRANSLATION | AffineTransform.TYPE_UNIFORM_SCALE: + scale = getScaleX(); + if (scale == 0) { + throw new PAffineTransformException("Could not invertTransform rectangle", this); + } + result.setRect((rectSrc.getX() - getTranslateX()) / scale, (rectSrc.getY() - getTranslateY()) / scale, + rectSrc.getWidth() / scale, rectSrc.getHeight() / scale); + break; + + default: + final double[] pts = rectToArray(rectSrc); + try { + inverseTransform(pts, 0, pts, 0, 4); + } + catch (final NoninvertibleTransformException e) { + throw new PAffineTransformException("Could not invert transform", e, this); + } + rectFromArray(result, pts); + break; + } + + return result; + } + + /** + * Builds an array of coordinates from an source rectangle. + * + * @param aRectangle rectangle from which points coordinates will be + * extracted + * + * @return coordinate array + */ + private static double[] rectToArray(final Rectangle2D aRectangle) { + PTS1[0] = aRectangle.getX(); + PTS1[1] = aRectangle.getY(); + PTS1[2] = PTS1[0] + aRectangle.getWidth(); + PTS1[3] = PTS1[1]; + PTS1[4] = PTS1[0] + aRectangle.getWidth(); + PTS1[5] = PTS1[1] + aRectangle.getHeight(); + PTS1[6] = PTS1[0]; + PTS1[7] = PTS1[1] + aRectangle.getHeight(); + return PTS1; + } + + /** + * Creates a rectangle from an array of coordinates. + * + * @param aRectangle rectangle into which coordinates will be stored + * @param pts coordinate source + */ + private static void rectFromArray(final Rectangle2D aRectangle, final double[] pts) { + double minX = pts[0]; + double minY = pts[1]; + double maxX = pts[0]; + double maxY = pts[1]; + + double x; + double y; + + for (int i = 1; i < 4; i++) { + x = pts[2 * i]; + y = pts[2 * i + 1]; + + if (x < minX) { + minX = x; + } + if (y < minY) { + minY = y; + } + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + } + aRectangle.setRect(minX, minY, maxX - minX, maxY - minY); + } +} diff --git a/core/src/main/java/org/piccolo2d/util/PAffineTransformException.java b/core/src/main/java/org/piccolo2d/util/PAffineTransformException.java new file mode 100644 index 0000000..72ad07a --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PAffineTransformException.java @@ -0,0 +1,77 @@ +package org.piccolo2d.util; + +/** + * This class is used to encapsulate exceptions that may occur while performing transform operations. + * + * @since 1.3 + */ +public class PAffineTransformException extends RuntimeException { + /** + * Allows for future serialization code to understand versioned binary + * formats. + */ + private static final long serialVersionUID = 1L; + + private final PAffineTransform errantTransform; + + /** + * Constructs an Exception that represents an error with the + * errantTransform. + * + * @param errantTransform transform that caused the error + */ + public PAffineTransformException(final PAffineTransform errantTransform) { + this.errantTransform = errantTransform; + } + + /** + * Constructs an Exception that represents an error with the + * errantTransform. + * + * @param message Text message provided by the programmer about the context + * of the error + * @param errantTransform transform that caused the error + */ + public PAffineTransformException(final String message, final PAffineTransform errantTransform) { + super(message); + this.errantTransform = errantTransform; + } + + /** + * Constructs an Exception that wraps another and records the errant + * transform. + * + * @param throwable the root cause of the exception + * @param errantTransform transform that's related to the error + */ + public PAffineTransformException(final Throwable throwable, final PAffineTransform errantTransform) { + super(throwable); + this.errantTransform = errantTransform; + } + + /** + * Constructs an Exception that wraps another and records the errant + * transform and provides a human readable message about the exception's + * context. + * + * @param message Text message provided by the programmer about the context + * of the error + * @param throwable the root cause of the exception + * @param errantTransform transform that's related to the error + */ + public PAffineTransformException(final String message, final Throwable throwable, + final PAffineTransform errantTransform) { + super(message, throwable); + this.errantTransform = errantTransform; + } + + /** + * Used to access the transform related to this exception. + * + * @return transform related to the exception + */ + public PAffineTransform getErrantTransform() { + return errantTransform; + } + +} diff --git a/core/src/main/java/org/piccolo2d/util/PBounds.java b/core/src/main/java/org/piccolo2d/util/PBounds.java new file mode 100644 index 0000000..112e8d6 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PBounds.java @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * PBounds is simply a Rectangle2D.Double with extra methods that more + * properly deal with the case when the rectangle is "empty". A PBounds has an + * extra bit to store emptiness. In this state, adding new geometry replaces the + * current geometry. A PBounds is emptied with the reset() method. A useful side + * effect of the reset method is that it only modifies the fIsEmpty variable, + * the other x, y, with, height variables are left alone. This is used by + * Piccolo's layout management system to see if a the full bounds of a node has + * really changed when it is recomputed. See PNode.validateLayout. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PBounds extends Rectangle2D.Double implements Serializable { + /** + * Allows for future serialization code to understand versioned binary + * formats. + */ + private static final long serialVersionUID = 1L; + + private boolean isEmpty = true; + + /** + * Creates an empty bounds. + */ + public PBounds() { + super(); + } + + /** + * Creates a bounds identical to the one provided. + * + * @param aBounds bounds to be copied + */ + public PBounds(final PBounds aBounds) { + this(aBounds.x, aBounds.y, aBounds.width, aBounds.height); + isEmpty = aBounds.isEmpty(); + } + + /** + * Creates a bounds with the same shape as the rectangle provided. + * + * @param aBounds rectangle to be copied + */ + public PBounds(final Rectangle2D aBounds) { + this(aBounds.getX(), aBounds.getY(), aBounds.getWidth(), aBounds.getHeight()); + isEmpty = aBounds.isEmpty(); + } + + /** + * Constructs a PBounds object with the given center point and the specified + * insets. + * + * @param aCenterPoint resulting center point of the PBounds object + * @param insetX distance from left and right the center should be + * @param insetY distance from top and bottom the center should be + */ + public PBounds(final Point2D aCenterPoint, final double insetX, final double insetY) { + this(aCenterPoint.getX(), aCenterPoint.getY(), 0, 0); + inset(insetX, insetY); + } + + /** + * Constructs a PBounds object at the given coordinates with the given + * dimensions. + * + * @param x left of bounds + * @param y top of bounds + * @param width width of bounds + * @param height height of bounds + */ + public PBounds(final double x, final double y, final double width, final double height) { + super(x, y, width, height); + isEmpty = false; + } + + /** + * Returns a clone of this node. + * + * @return cloned copy of this bounds + */ + public Object clone() { + return new PBounds(this); + } + + /** + * Returns true if this bounds has been flagged as empty. Not necessarily if + * it is empty. + * + * @return true if bounds marked as empty + */ + public boolean isEmpty() { + return isEmpty; + } + + /** + * Flags this bounds as empty. + * + * @return itself for chaining + */ + public PBounds reset() { + isEmpty = true; + return this; + } + + /** + * Resets the bounds to (0,0,0,0) and flags it as empty. + * + * @return itself for chaining + */ + public PBounds resetToZero() { + x = 0; + y = 0; + width = 0; + height = 0; + isEmpty = true; + return this; + } + + /** + * Sets the bounds to the same shape as the rectangle. And flags the bounds + * as not empty. + * + * @param r rectangle to copy + */ + public void setRect(final Rectangle2D r) { + super.setRect(r); + isEmpty = false; + } + + /** + * Sets the bounds to the same shape as the bounds provided. And flags the + * bounds as not empty. + * + * @param b bounds to copy + */ + public void setRect(final PBounds b) { + isEmpty = b.isEmpty; + x = b.x; + y = b.y; + width = b.width; + height = b.height; + } + + /** + * Sets the shape of the bounds to the position and dimension provided. + * + * @param x new left of bounds + * @param y new top of bounds + * @param width new width of bounds + * @param height new height of bounds + */ + public void setRect(final double x, final double y, final double width, final double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + isEmpty = false; + } + + /** + * Grows the bounds to contain the coordinate provided. + * + * @param newx x component of point + * @param newy y component of point + */ + public void add(final double newx, final double newy) { + if (isEmpty) { + setRect(newx, newy, 0, 0); + isEmpty = false; + } + else { + super.add(newx, newy); + } + } + + /** + * Grows bounds to contain the rectangle if needed. + * + * @param r rectangle being added + */ + public void add(final Rectangle2D r) { + if (isEmpty) { + setRect(r); + } + else { + super.add(r); + } + } + + /** + * Changes this bounds to contain the provided bounds. + * + * @param bounds bounds being added + */ + public void add(final PBounds bounds) { + if (bounds.isEmpty) { + return; + } + else if (isEmpty) { + x = bounds.x; + y = bounds.y; + width = bounds.width; + height = bounds.height; + isEmpty = false; + } + else { + final double x1 = Math.min(x, bounds.x); + final double y1 = Math.min(y, bounds.y); + final double x2 = Math.max(x + width, bounds.x + bounds.width); + final double y2 = Math.max(y + height, bounds.y + bounds.height); + + x = x1; + y = y1; + width = x2 - x1; + height = y2 - y1; + isEmpty = false; + } + } + + /** + * Returns the x,y coordinate of the bounds. + * + * @return coordinate of the bounds + */ + public Point2D getOrigin() { + return new Point2D.Double(x, y); + } + + /** + * Changes the origin of these bounds. And flags it as non-empty. + * + * @param x new x component of bounds + * @param y new y component of the bounds + * @return the modified PBounds with its new origin + */ + public PBounds setOrigin(final double x, final double y) { + this.x = x; + this.y = y; + isEmpty = false; + return this; + } + + /** + * Returns the size of the bounds. + * + * @return size of the bounds + */ + public Dimension2D getSize() { + return new PDimension(width, height); + } + + /** + * Changes the size of the bounds, but retains the origin. + * + * @param width new width of the bounds + * @param height new height of the bounds + */ + public void setSize(final double width, final double height) { + setRect(x, y, width, height); + } + + /** + * Returns the midpoint of the bounds. + * + * @return midpoint of the bounds + */ + public Point2D getCenter2D() { + return new Point2D.Double(getCenterX(), getCenterY()); + } + + /** + * Translates the bounds by the given deltas. + * + * @param dx amount to move x + * @param dy amount to move y + * @return itself for chaining + */ + public PBounds moveBy(final double dx, final double dy) { + setOrigin(x + dx, y + dy); + return this; + } + + /** + * Rounds the rectangle to the next largest bounds who's measurements are + * integers. Note: this is not the same as rounding its measurements. + */ + public void expandNearestIntegerDimensions() { + x = Math.floor(x); + y = Math.floor(y); + width = Math.ceil(width); + height = Math.ceil(height); + } + + /** + * Adjust the measurements of this bounds so that they are the amounts given + * "in" from their previous border. + * + * @param dx amount to move in from border along horizontal axis + * @param dy amount to move in from border along vertical axis + * @return itself for chaining + */ + public PBounds inset(final double dx, final double dy) { + setRect(x + dx, y + dy, width - dx * 2, height - dy * 2); + return this; + } + + /** + * Returns the required translation in order for this bounds origin to sit + * on the center of the provided rectangle. + * + * @param targetBounds rectangle to measure the center of + * @return the delta required to move to center of the targetBounds + */ + public PDimension deltaRequiredToCenter(final Rectangle2D targetBounds) { + final PDimension result = new PDimension(); + final double xDelta = getCenterX() - targetBounds.getCenterX(); + final double yDelta = getCenterY() - targetBounds.getCenterY(); + result.setSize(xDelta, yDelta); + return result; + } + + /** + * Returns the required translation in order for these to contain the bounds + * provided. + * + * @param targetBounds rectangle to measure the center of + * @return the delta required in order for the bounds to overlap completely + * the targetBounds + */ + public PDimension deltaRequiredToContain(final Rectangle2D targetBounds) { + final PDimension result = new PDimension(); + + if (contains(targetBounds)) { + return result; + } + + final double targetMaxX = targetBounds.getMaxX(); + final double targetMinX = targetBounds.getMinX(); + final double targetMaxY = targetBounds.getMaxY(); + final double targetMinY = targetBounds.getMinY(); + final double maxX = getMaxX(); + final double minX = getMinX(); + final double maxY = getMaxY(); + final double minY = getMinY(); + + if (targetMaxX > maxX ^ targetMinX < minX) { + final double difMaxX = targetMaxX - maxX; + final double difMinX = targetMinX - minX; + if (Math.abs(difMaxX) < Math.abs(difMinX)) { + result.width = difMaxX; + } + else { + result.width = difMinX; + } + } + + if (targetMaxY > maxY ^ targetMinY < minY) { + final double difMaxY = targetMaxY - maxY; + final double difMinY = targetMinY - minY; + if (Math.abs(difMaxY) < Math.abs(difMinY)) { + result.height = difMaxY; + } + else { + result.height = difMinY; + } + } + + return result; + } + + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeDouble(x); + out.writeDouble(y); + out.writeDouble(width); + out.writeDouble(height); + } + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + x = in.readDouble(); + y = in.readDouble(); + width = in.readDouble(); + height = in.readDouble(); + } + + /** + * Returns a string representation of this PBounds for debugging purposes. + * + * @return string representation of this PBounds + */ + public String toString() { + final StringBuffer result = new StringBuffer(); + + result.append(getClass().getName().replaceAll(".*\\.", "")); + result.append('['); + + if (isEmpty) { + result.append("EMPTY"); + } + else { + result.append("x="); + result.append(x); + result.append(",y="); + result.append(y); + result.append(",width="); + result.append(width); + result.append(",height="); + result.append(height); + } + + result.append(']'); + + return result.toString(); + } +} diff --git a/core/src/main/java/org/piccolo2d/util/PDebug.java b/core/src/main/java/org/piccolo2d/util/PDebug.java new file mode 100644 index 0000000..287f17c --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PDebug.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; + +import javax.swing.SwingUtilities; + +/** + * PDebug is used to set framework wide debugging flags. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PDebug { + /** Set to true to display clip bounds boxes. */ + public static boolean debugRegionManagement = false; + + /** + * Set to true if you want to display common errors with painting and + * threading. + */ + public static boolean debugPaintCalls = false; + + /** Set to true to display frame rate in the console. */ + public static boolean debugPrintFrameRate = false; + + /** Set to true to display used memory in console. */ + public static boolean debugPrintUsedMemory = false; + + /** Displays bounding boxes around nodes. Used in PCamera. */ + public static boolean debugBounds = false; + + /** Displays a tint to all shapes within a bounding box. */ + public static boolean debugFullBounds = false; + + /** Whether to complain whenever common threading issues occur. */ + public static boolean debugThreads = false; + + /** How often in frames result info should be printed to the console. */ + public static int printResultsFrameRate = 10; + + private static int debugPaintColor; + private static long framesProcessed; + private static long startProcessingOutputTime; + private static long startProcessingInputTime; + private static long processOutputTime; + private static long processInputTime; + private static boolean processingOutput; + + private PDebug() { + super(); + } + + /** + * Generates a color for use while debugging. + * + * @return a color for use while debugging. + */ + public static Color getDebugPaintColor() { + final int color = 100 + debugPaintColor++ % 10 * 10; + return new Color(color, color, color, 150); + } + + /** + * Checks that process inputs is being doing from the Swing Dispatch Thread. + */ + public static void scheduleProcessInputs() { + if (debugThreads && !SwingUtilities.isEventDispatchThread()) { + System.out.println("scene graph manipulated on wrong thread"); + } + } + + /** + * Ensures that painting is not invalidating paint regions and that it's + * being called from the dispatch thread. + */ + public static void processRepaint() { + if (processingOutput && debugPaintCalls) { + System.err + .println("Got repaint while painting scene. This can result in a recursive process that degrades performance."); + } + + if (debugThreads && !SwingUtilities.isEventDispatchThread()) { + System.out.println("repaint called on wrong thread"); + } + } + + /** + * Returns whether output is being processed. + * + * @return whether output is being processed + */ + public static boolean getProcessingOutput() { + return processingOutput; + } + + /** + * Records that processing of ouptut has begun. + */ + public static void startProcessingOutput() { + processingOutput = true; + startProcessingOutputTime = System.currentTimeMillis(); + } + + /** + * Flags processing of output as finished. Updates all stats in the process. + * + * @param g graphics context in which processing has finished + */ + public static void endProcessingOutput(final Graphics g) { + processOutputTime += System.currentTimeMillis() - startProcessingOutputTime; + framesProcessed++; + + if (framesProcessed % printResultsFrameRate == 0) { + if (PDebug.debugPrintFrameRate) { + System.out.println("Process output frame rate: " + getOutputFPS() + " fps"); + System.out.println("Process input frame rate: " + getInputFPS() + " fps"); + System.out.println("Total frame rate: " + getTotalFPS() + " fps"); + System.out.println(); + resetFPSTiming(); + } + + if (PDebug.debugPrintUsedMemory) { + System.out.println("Approximate used memory: " + getApproximateUsedMemory() / 1024 + " k"); + } + } + + if (PDebug.debugRegionManagement) { + final Graphics2D g2 = (Graphics2D) g; + g2.setColor(PDebug.getDebugPaintColor()); + g2.fill(g.getClipBounds().getBounds2D()); + } + + processingOutput = false; + } + + /** + * Records that processing of input has started. + */ + public static void startProcessingInput() { + startProcessingInputTime = System.currentTimeMillis(); + } + + /** + * Records that processing of input has finished. + */ + public static void endProcessingInput() { + processInputTime += System.currentTimeMillis() - startProcessingInputTime; + } + + /** + * Return how many frames are processed and painted per second. Note that + * since piccolo doesn't paint continuously this rate will be slow unless + * you are interacting with the system or have activities scheduled. + * + * @return frame rate achieved + */ + public static double getTotalFPS() { + if (framesProcessed > 0) { + return 1000.0 / ((processInputTime + processOutputTime) / (double) framesProcessed); + } + else { + return 0; + } + } + + /** + * Return the frames per second used to process input events and activities. + * + * @return # of frames per second that were allocated to processing input + */ + public static double getInputFPS() { + if (processInputTime > 0 && framesProcessed > 0) { + return 1000.0 / (processInputTime / (double) framesProcessed); + } + else { + return 0; + } + } + + /** + * Return the frames per seconds used to paint graphics to the screen. + * + * @return # of frames per second that were used up to processing output + */ + public static double getOutputFPS() { + if (processOutputTime > 0 && framesProcessed > 0) { + return 1000.0 / (processOutputTime / (double) framesProcessed); + } + else { + return 0; + } + } + + /** + * Return the number of frames that have been processed since the last time + * resetFPSTiming was called. + * + * @return total number of frames processed + */ + public long getFramesProcessed() { + return framesProcessed; + } + + /** + * Reset the variables used to track FPS. If you reset seldom they you will + * get good average FPS values, if you reset more often only the frames + * recorded after the last reset will be taken into consideration. + */ + public static void resetFPSTiming() { + framesProcessed = 0; + processInputTime = 0; + processOutputTime = 0; + } + + /** + * Returns an approximation of the amount of memory that is being used. + * + * Not that this call might affecting timings. + * + * @return approximate # of bytes of memory used + */ + public static long getApproximateUsedMemory() { + System.gc(); + System.runFinalization(); + final long totalMemory = Runtime.getRuntime().totalMemory(); + final long free = Runtime.getRuntime().freeMemory(); + return totalMemory - free; + } +} diff --git a/core/src/main/java/org/piccolo2d/util/PDimension.java b/core/src/main/java/org/piccolo2d/util/PDimension.java new file mode 100644 index 0000000..7a3715e --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PDimension.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; +import java.io.Serializable; + +/** + * PDimension this class should be removed once a concrete Dimension2D + * that supports doubles is added to java. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PDimension extends Dimension2D implements Serializable { + /** + * Allows for future serialization code to understand versioned binary + * formats. + */ + private static final long serialVersionUID = 1L; + + /** The width of the dimension. */ + public double width; + + /** The height of the dimension. */ + public double height; + + /** + * Returns a dimension with no width or height. + */ + public PDimension() { + super(); + } + + /** + * Copies the provided dimension. + * + * @param aDimension dimension to copy + */ + public PDimension(final Dimension2D aDimension) { + this(aDimension.getWidth(), aDimension.getHeight()); + } + + /** + * Creates a dimension with the provided dimensions. + * + * @param aWidth desired width + * @param aHeight desired height + */ + public PDimension(final double aWidth, final double aHeight) { + super(); + width = aWidth; + height = aHeight; + } + + /** + * Creates a dimension that's the size of a rectangel with the points + * provided as opposite corners. + * + * @param p1 first point on rectangle + * @param p2 point diagonally across from p1 + */ + public PDimension(final Point2D p1, final Point2D p2) { + width = p2.getX() - p1.getX(); + height = p2.getY() - p1.getY(); + } + + /** + * Returns the height of the dimension. + * + * @return height height of the dimension + */ + public double getHeight() { + return height; + } + + /** + * Returns the width of the dimension. + * + * @return width width of the dimension + */ + public double getWidth() { + return width; + } + + /** + * Resizes the dimension to have the dimensions provided. + * + * @param aWidth desired width + * @param aHeight desired height + */ + public void setSize(final double aWidth, final double aHeight) { + width = aWidth; + height = aHeight; + } + + /** + * Returns a string representation of this dimension object. + * + * @return string representation of this dimension object. + */ + public String toString() { + final StringBuffer result = new StringBuffer(); + + result.append(super.toString().replaceAll(".*\\.", "")); + result.append('['); + result.append("width="); + result.append(width); + result.append(",height="); + result.append(height); + result.append(']'); + + return result.toString(); + } +} diff --git a/core/src/main/java/org/piccolo2d/util/PNodeFilter.java b/core/src/main/java/org/piccolo2d/util/PNodeFilter.java new file mode 100644 index 0000000..8caed50 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PNodeFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import org.piccolo2d.PNode; + +/** + * PNodeFilter is a interface that filters (accepts or rejects) nodes. + * Its main use is to retrieve all the children of a node the meet some criteria + * by using the method PNode.getAllNodes(collection, filter); + *
+ *
+ * @version 1.0
+ * @author Jesse Grosjean
+ */
+public interface PNodeFilter {
+
+ /**
+ * Return true if the filter should accept the given node.
+ *
+ * @param aNode node under test
+ * @return true if node should be accepted
+ */
+ boolean accept(PNode aNode);
+
+ /**
+ * Return true if the filter should test the children of the given node for
+ * acceptance.
+ *
+ * @param aNode parent being tested
+ * @return true if children should be tested for acceptance
+ */
+ boolean acceptChildrenOf(PNode aNode);
+}
diff --git a/core/src/main/java/org/piccolo2d/util/PObjectOutputStream.java b/core/src/main/java/org/piccolo2d/util/PObjectOutputStream.java
new file mode 100644
index 0000000..240a53f
--- /dev/null
+++ b/core/src/main/java/org/piccolo2d/util/PObjectOutputStream.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org
+ * Copyright (c) 1998-2008, University of Maryland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+ * and the following disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
+ * contributors may be used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.piccolo2d.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+
+/**
+ * PObjectOutputStream is an extension of ObjectOutputStream to handle
+ * optional elements. This is similar to the concept of Java's
+ * "weak references", but applied to object serialization rather than garbage
+ * collection. Here, PObjectOutputStream provides a method,
+ * writeConditionalObject
, which only serializes the specified
+ * object to the stream if there is a strong reference (if it has been written
+ * somewhere else using writeObject()) to that object elsewhere in the stream.
+ *
+ * To discover strong references to objects, PObjectOutputStream uses a
+ * two-phase writing process. First, a "discovery" phase is used to find out
+ * what objects are about to be serialized. This works by effectively
+ * serializing the object graph to /dev/null, recording which objects are
+ * unconditionally written using the standard writeObject method. Then, in the
+ * second "write" phase, ObjectOutputStream actually serializes the data to the
+ * output stream. During this phase, calls to writeConditionalObject() will only
+ * write the specified object if the object was found to be serialized during
+ * the discovery stage. If the object was not recorded during the discovery
+ * stage, a an optional null (the default) is unconditionally written in place
+ * of the object. To skip writting out the null use
+ * writeConditionalObject(object, false)
+ *
+ * By careful implementation of readObject and writeObject methods, streams + * serialized using PObjectOutputStream can be deserialized using the standard + * ObjectInputStream. + *
+ * + * @version 1.0 + * @author Jon Meyer + * @author Jesse Grosjean + */ +public class PObjectOutputStream extends ObjectOutputStream { + + private boolean writingRoot; + private final HashMap unconditionallyWritten; + + /** + * Transform the given object into an array of bytes. + * + * @param object the object to be transformed + * @return array of bytes representing the given object + * @throws IOException when serialization system throws one + */ + public static byte[] toByteArray(final Object object) throws IOException { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final PObjectOutputStream zout = new PObjectOutputStream(out); + zout.writeObjectTree(object); + return out.toByteArray(); + } + + /** + * Constructs a PObjectOutputStream that wraps the provided OutputStream. + * + * @param out underlying outputstream that will receive the serialized + * objects + * + * @throws IOException when underlying subsystem throws one + */ + public PObjectOutputStream(final OutputStream out) throws IOException { + super(out); + unconditionallyWritten = new HashMap(); + } + + /** + * Writes the provided object to the underlying stream like an ordination + * ObjectOutputStream except that it does not record duplicates at all. + * + * @param object object to be serialized + * + * @throws IOException when underlying subsystem throws one + */ + public void writeObjectTree(final Object object) throws IOException { + writingRoot = true; + recordUnconditionallyWritten(object); // record pass + writeObject(object); // write pass + writingRoot = false; + } + + /** + * Writes the given object, but only if it was not in the object tree + * multiple times. + * + * @param object object to write to the stream. + * @throws IOException when underlying subsystem throws one + */ + public void writeConditionalObject(final Object object) throws IOException { + if (!writingRoot) { + throw new RuntimeException( + "writeConditionalObject() may only be called when a root object has been written."); + } + + if (unconditionallyWritten.containsKey(object)) { + writeObject(object); + } + else { + writeObject(null); + } + } + + /** + * Resets the ObjectOutputStream clearing any memory about objects already + * being written while it's at it. + * + * @throws IOException when underlying subsystem throws one + */ + public void reset() throws IOException { + super.reset(); + unconditionallyWritten.clear(); + } + + /** + * Performs a scan of objects that can be serialized once. + * + * @param aRoot Object from which to start the scan + * @throws IOException when serialization fails + */ + protected void recordUnconditionallyWritten(final Object aRoot) throws IOException { + class ZMarkObjectOutputStream extends PObjectOutputStream { + public ZMarkObjectOutputStream() throws IOException { + super(NULL_OUTPUT_STREAM); + enableReplaceObject(true); + } + + public Object replaceObject(final Object object) { + unconditionallyWritten.put(object, Boolean.TRUE); + return object; + } + + public void writeConditionalObject(final Object object) throws IOException { + } + } + new ZMarkObjectOutputStream().writeObject(aRoot); + } + + private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() { + public void close() { + } + + public void flush() { + } + + public void write(final byte[] b) { + } + + public void write(final byte[] b, final int off, final int len) { + } + + public void write(final int b) { + } + }; +} diff --git a/core/src/main/java/org/piccolo2d/util/PPaintContext.java b/core/src/main/java/org/piccolo2d/util/PPaintContext.java new file mode 100644 index 0000000..3e2f012 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PPaintContext.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.AlphaComposite; +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.piccolo2d.PCamera; + + +/** + * PPaintContext is used by piccolo nodes to paint themselves on the + * screen. PPaintContext wraps a Graphics2D to implement painting. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PPaintContext { + /** Used for lowering quality of rendering when requested. */ + public static final int LOW_QUALITY_RENDERING = 0; + + /** Used for improving quality of rendering when requested. */ + public static final int HIGH_QUALITY_RENDERING = 1; + + /** Font context to use while in low quality rendering. */ + public static final FontRenderContext RENDER_QUALITY_LOW_FRC = new FontRenderContext(null, false, true); + + /** Font context to use while in high quality rendering. */ + public static final FontRenderContext RENDER_QUALITY_HIGH_FRC = new FontRenderContext(null, true, true); + + /** Used while calculating scale at which rendering is occurring. */ + private static final double[] PTS = new double[4]; + + /** PaintContext is associated with this graphics context. */ + private final Graphics2D graphics; + + /** Used while computing transparency. */ + protected PStack compositeStack; + + /** Used to optimize clipping region. */ + protected PStack clipStack; + + /** Tracks clipping region in local coordinate system. */ + protected PStack localClipStack; + + /** Stack of cameras through which the node being painted is being viewed. */ + protected PStack cameraStack; + + /** Stack of transforms being applied to the drawing context. */ + protected PStack transformStack; + + /** The current render quality that all rendering should be done in. */ + protected int renderQuality; + + /** + * Creates a PPaintContext associated with the given graphics context. + * + * @param graphics graphics context to associate with this paint context + */ + public PPaintContext(final Graphics2D graphics) { + this.graphics = graphics; + compositeStack = new PStack(); + clipStack = new PStack(); + localClipStack = new PStack(); + cameraStack = new PStack(); + transformStack = new PStack(); + renderQuality = HIGH_QUALITY_RENDERING; + + Shape clip = graphics.getClip(); + if (clip == null) { + clip = new PBounds(-Integer.MAX_VALUE / 2, -Integer.MAX_VALUE / 2, Integer.MAX_VALUE, Integer.MAX_VALUE); + graphics.setClip(clip); + } + + localClipStack.push(clip.getBounds2D()); + } + + /** + * Returns the graphics context associated with this paint context. + * + * @return graphics context associated with this paint context + */ + public Graphics2D getGraphics() { + return graphics; + } + + /** + * Returns the clipping region in the local coordinate system applied by + * graphics. + * + * @return clipping region in the local coordinate system applied by + * graphics + */ + public Rectangle2D getLocalClip() { + return (Rectangle2D) localClipStack.peek(); + } + + /** + * Returns scale of the current graphics context. By calculating how a unit + * segment gets transformed after transforming it by the graphics context's + * transform. + * + * @return scale of the current graphics context's transformation + */ + public double getScale() { + // x1, y1, x2, y2 + PTS[0] = 0; + PTS[1] = 0; + PTS[2] = 1; + PTS[3] = 0; + graphics.getTransform().transform(PTS, 0, PTS, 0, 2); + return Point2D.distance(PTS[0], PTS[1], PTS[2], PTS[3]); + } + + /** + * Pushes the camera onto the camera stack. + * + * @param aCamera camera to push onto the stack + */ + public void pushCamera(final PCamera aCamera) { + cameraStack.push(aCamera); + } + + /** + * Removes the camera at the top of the camera stack. + * + * @since 1.3 + */ + public void popCamera() { + cameraStack.pop(); + } + + /** + * Returns the camera at the top of the camera stack, or null if stack is + * empty. + * + * @return topmost camera on camera stack or null if stack is empty + */ + public PCamera getCamera() { + return (PCamera) cameraStack.peek(); + } + + /** + * Pushes the given clip to the pain context. + * + * @param clip clip to be pushed + */ + public void pushClip(final Shape clip) { + final Shape currentClip = graphics.getClip(); + clipStack.push(currentClip); + graphics.clip(clip); + final Rectangle2D newLocalClip = clip.getBounds2D(); + Rectangle2D.intersect(getLocalClip(), newLocalClip, newLocalClip); + localClipStack.push(newLocalClip); + } + + /** + * Removes the topmost clipping region from the clipping stack. + * + * @param clip not used in this method + */ + public void popClip(final Shape clip) { + final Shape newClip = (Shape) clipStack.pop(); + graphics.setClip(newClip); + localClipStack.pop(); + } + + /** + * Pushes the provided transparency onto the transparency stack if + * necessary. If the transparency is fully opaque, then it does nothing. + * + * @param transparency transparency to be pushed onto the transparency stack + */ + public void pushTransparency(final float transparency) { + if (transparency == 1.0f) { + return; + } + final Composite current = graphics.getComposite(); + float currentAlaph = 1.0f; + compositeStack.push(current); + + if (current instanceof AlphaComposite) { + currentAlaph = ((AlphaComposite) current).getAlpha(); + } + final AlphaComposite newComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, currentAlaph + * transparency); + graphics.setComposite(newComposite); + } + + /** + * Removes the topmost transparency if the given transparency is not opaque + * (1f). + * + * @param transparency transparency to be popped + */ + public void popTransparency(final float transparency) { + if (transparency == 1.0f) { + return; + } + final Composite c = (Composite) compositeStack.pop(); + graphics.setComposite(c); + } + + /** + * Pushed the provided transform onto the transform stack if it is not null. + * + * @param transform will be pushed onto the transform stack if not null + */ + public void pushTransform(final PAffineTransform transform) { + if (transform != null) { + final Rectangle2D newLocalClip = (Rectangle2D) getLocalClip().clone(); + transform.inverseTransform(newLocalClip, newLocalClip); + transformStack.push(graphics.getTransform()); + localClipStack.push(newLocalClip); + graphics.transform(transform); + } + } + + /** + * Pops the topmost Transform from the top of the transform if the passed in + * transform is not null. + * + * @param transform transform that should be at the top of the stack + */ + public void popTransform(final PAffineTransform transform) { + if (transform != null) { + graphics.setTransform((AffineTransform) transformStack.pop()); + localClipStack.pop(); + } + } + + /** + * Return the render quality used by this paint context. + * + * @return the current render quality + */ + public int getRenderQuality() { + return renderQuality; + } + + /** + * Set the rendering hints for this paint context. The render quality is + * most often set by the rendering PCanvas. Use PCanvas.setRenderQuality() + * and PCanvas.setInteractingRenderQuality() to set these values. + * + * @param requestedQuality supports PPaintContext.HIGH_QUALITY_RENDERING or + * PPaintContext.LOW_QUALITY_RENDERING + */ + public void setRenderQuality(final int requestedQuality) { + renderQuality = requestedQuality; + + switch (renderQuality) { + case HIGH_QUALITY_RENDERING: + setRenderQualityToHigh(); + break; + + case LOW_QUALITY_RENDERING: + setRenderQualityToLow(); + break; + + default: + throw new RuntimeException("Quality must be either HIGH_QUALITY_RENDERING or LOW_QUALITY_RENDERING"); + } + } + + private void setRenderQualityToLow() { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); + graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + private void setRenderQualityToHigh() { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } +} diff --git a/core/src/main/java/org/piccolo2d/util/PPickPath.java b/core/src/main/java/org/piccolo2d/util/PPickPath.java new file mode 100644 index 0000000..fcb466c --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PPickPath.java @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.HashMap; + +import javax.swing.event.EventListenerList; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventListener; + + +/** + * PPickPath represents a ordered list of nodes that have been picked. + * The topmost ancestor node is the first node in the list (and should be a + * camera), the bottommost child node is at the end of the list. It is this + * bottom node that is given first chance to handle events, and that any active + * event handlers usually manipulate. + *
+ * Note that because of layers (which can be picked by multiple camera's) the + * ordered list of nodes in a pick path do not all share a parent child + * relationship with the nodes in the list next to them. This means that the + * normal localToGlobal methods don't work when trying to transform geometry up + * and down the pick path, instead you should use the pick paths canvasToLocal + * methods to get the mouse event points into your local coord system. + *
+ * Note that PInputEvent wraps most of the useful PPickPath methods, so often + * you can use a PInputEvent directly instead of having to access its pick path. + *
+ * + * @see org.piccolo2d.event.PInputEvent + * @version 1.0 + * @author Jesse Grosjean + */ +public class PPickPath implements PInputEventListener { + /** Global pick path. */ + public static PPickPath CURRENT_PICK_PATH; + + /** Used when calculating the scale. */ + private static final double[] PTS = new double[4]; + + /** Stack of nodes representing all picked nodes. */ + private PStack nodeStack; + + private final PCamera topCamera; + private PStack transformStack; + private PStack pickBoundsStack; + private PCamera bottomCamera; + private HashMap excludedNodes; + + /** + * Creates a pick pack originating from the provided camera and with the + * given screen pick bounds. + * + * @param camera camera from which the pickpath originates + * @param aScreenPickBounds bounds of pick area + */ + public PPickPath(final PCamera camera, final PBounds aScreenPickBounds) { + super(); + pickBoundsStack = new PStack(); + topCamera = camera; + nodeStack = new PStack(); + transformStack = new PStack(); + pickBoundsStack.push(aScreenPickBounds); + + CURRENT_PICK_PATH = this; + } + + /** + * Returns the bounds of the entire PickPath taken as a whole. + * + * @return bounds of the entire PickPath + */ + public PBounds getPickBounds() { + return (PBounds) pickBoundsStack.peek(); + } + + /** + * Determines if the passed node has been excluded from being a member of + * the pickpath. + * + * @param node node being tested + * @return true if node is acceptable to the path + */ + public boolean acceptsNode(final PNode node) { + return excludedNodes == null || !excludedNodes.containsKey(node); + } + + // **************************************************************** + // Picked Nodes + // **************************************************************** + + /** + * Pushes the provided node to the top of the pick path. + * + * @param node node to be added to the pick path + */ + public void pushNode(final PNode node) { + nodeStack.push(node); + } + + /** + * Removes the topmost node from the node stack. + * + * @param node completely unused in this method, but is passed in so that + * subclasses may be informed of it. + */ + public void popNode(final PNode node) { + nodeStack.pop(); + } + + /** + * Get the bottom node on the pick path node stack. That is the last node to + * be picked. + * + * @return the bottom node on the pick path + */ + public PNode getPickedNode() { + return (PNode) nodeStack.peek(); + } + + // **************************************************************** + // Iterating over picked nodes. + // **************************************************************** + + /** + * Return the next node that will be picked after the current picked node. + * For instance of you have two overlapping children nodes then the topmost + * child will always be picked first, use this method to find the covered + * child. Return the camera when no more visual will be picked. + * + * @return next node to picked after the picked node + */ + public PNode nextPickedNode() { + final PNode picked = getPickedNode(); + + if (picked == topCamera) { + return null; + } + if (excludedNodes == null) { + excludedNodes = new HashMap(); + } + + // exclude current picked node + excludedNodes.put(picked, picked); + + final Object screenPickBounds = pickBoundsStack.get(0); + + // reset path state + pickBoundsStack = new PStack(); + nodeStack = new PStack(); + transformStack = new PStack(); + pickBoundsStack = new PStack(); + + pickBoundsStack.push(screenPickBounds); + + // pick again + topCamera.fullPick(this); + + // make sure top camera is pushed. + if (getNodeStackReference().size() == 0) { + pushNode(topCamera); + pushTransform(topCamera.getTransformReference(false)); + } + + return getPickedNode(); + } + + /** + * Get the top camera on the pick path. This is the camera that originated + * the pick action. + * + * @return the topmost camera of this pick pack + */ + public PCamera getTopCamera() { + return topCamera; + } + + /** + * Get the bottom camera on the pick path. This may be different then the + * top camera if internal cameras are in use. + * + * @return the camera closest to the picked node + */ + public PCamera getBottomCamera() { + if (bottomCamera == null) { + bottomCamera = calculateBottomCamera(); + } + return bottomCamera; + } + + private PCamera calculateBottomCamera() { + for (int i = nodeStack.size() - 1; i >= 0; i--) { + final PNode each = (PNode) nodeStack.get(i); + if (each instanceof PCamera) { + return (PCamera) each; + } + } + return null; + } + + /** + * Returns a reference to the node stack. Be Careful! + * + * @return the node stack + */ + public PStack getNodeStackReference() { + return nodeStack; + } + + // **************************************************************** + // Path Transform + // **************************************************************** + + /** + * Returns the resulting scale of applying the transforms of the entire pick + * path. In essence it gives you the scale at which interaction is + * occurring. + * + * @return scale at which interaction is occurring. + */ + public double getScale() { + // x1, y1, x2, y3 + PTS[0] = 0; + PTS[1] = 0; + PTS[2] = 1; + PTS[3] = 0; + + final int count = transformStack.size(); + for (int i = 0; i < count; i++) { + final PAffineTransform each = ((PTuple) transformStack.get(i)).transform; + if (each != null) { + each.transform(PTS, 0, PTS, 0, 2); + } + } + + return Point2D.distance(PTS[0], PTS[1], PTS[2], PTS[3]); + } + + /** + * Adds the transform to the pick path's transform. This is used when + * determining the context of the current interaction. + * + * @param transform transform to be added to applied to the pickpath. + */ + public void pushTransform(final PAffineTransform transform) { + transformStack.push(new PTuple(getPickedNode(), transform)); + if (transform != null) { + final Rectangle2D newPickBounds = (Rectangle2D) getPickBounds().clone(); + transform.inverseTransform(newPickBounds, newPickBounds); + pickBoundsStack.push(newPickBounds); + } + } + + /** + * Pops the top most transform from the pick path. + * + * @param transform unused in this method + */ + public void popTransform(final PAffineTransform transform) { + transformStack.pop(); + if (transform != null) { + pickBoundsStack.pop(); + } + } + + /** + * Calculates the context at which the given node is being interacted with. + * + * @param nodeOnPath a node currently on the pick path. An exception will be + * thrown if the node cannot be found. + * + * @return Transform at which the given node is being interacted with. + */ + public PAffineTransform getPathTransformTo(final PNode nodeOnPath) { + final PAffineTransform aTransform = new PAffineTransform(); + + final int count = transformStack.size(); + for (int i = 0; i < count; i++) { + final PTuple each = (PTuple) transformStack.get(i); + if (each.transform != null) { + aTransform.concatenate(each.transform); + } + if (nodeOnPath == each.node) { + return aTransform; + } + } + + throw new RuntimeException("Node could not be found on pick path"); + } + + /** + * Process Events - Give each node in the pick path, starting at the bottom + * most one, a chance to handle the event. + * + * @param event event to be processed + * @param eventType the type of event being processed + */ + public void processEvent(final PInputEvent event, final int eventType) { + event.setPath(this); + + for (int i = nodeStack.size() - 1; i >= 0; i--) { + final PNode each = (PNode) nodeStack.get(i); + + final EventListenerList list = each.getListenerList(); + + if (list != null) { + final Object[] listeners = list.getListeners(PInputEventListener.class); + + for (int j = 0; j < listeners.length; j++) { + final PInputEventListener listener = (PInputEventListener) listeners[j]; + listener.processEvent(event, eventType); + if (event.isHandled()) { + return; + } + } + } + } + } + + // **************************************************************** + // Transforming Geometry - Methods to transform geometry through + // this path. + //
+ // Note that this is different that just using the + // PNode.localToGlobal (an other coord system transform methods). + // The PNode coord system transform methods always go directly up + // through their parents. The PPickPath coord system transform + // methods go up through the list of picked nodes instead. And since + // cameras can pick their layers in addition to their children these + // two paths may be different. + // **************************************************************** + + /** + * Convert the given point from the canvas coordinates, down through the + * pick path (and through any camera view transforms applied to the path) to + * the local coordinates of the given node. + * + * @param canvasPoint point to be transformed + * @param nodeOnPath node into which the point is to be transformed + * iteratively through the pick path + * + * @return transformed canvasPoint in local coordinates of the picked node + */ + public Point2D canvasToLocal(final Point2D canvasPoint, final PNode nodeOnPath) { + return getPathTransformTo(nodeOnPath).inverseTransform(canvasPoint, canvasPoint); + } + + /** + * Convert the given dimension from the canvas coordinates, down through the + * pick path (and through any camera view transforms applied to the path) to + * the local coordinates of the given node. + * + * @param canvasDimension dimension to be transformed + * @param nodeOnPath node into which the dimension is to be transformed + * iteratively through the stack + * + * @return transformed canvasDimension in local coordinates of the picked + * node + */ + public Dimension2D canvasToLocal(final Dimension2D canvasDimension, final PNode nodeOnPath) { + return getPathTransformTo(nodeOnPath).inverseTransform(canvasDimension, canvasDimension); + } + + /** + * Convert the given rectangle from the canvas coordinates, down through the + * pick path (and through any camera view transforms applied to the path) to + * the local coordinates of the given node. + * + * @param canvasRectangle rectangle to be transformed + * @param nodeOnPath node into which the rectangle is to be transformed + * iteratively through the stack + * @return transformed canvasRectangle in local coordinates of the picked + * node + */ + public Rectangle2D canvasToLocal(final Rectangle2D canvasRectangle, final PNode nodeOnPath) { + return getPathTransformTo(nodeOnPath).inverseTransform(canvasRectangle, canvasRectangle); + } + + /** + * Used to associated nodes with their transforms on the transform stack. + */ + private static class PTuple { + public PNode node; + public PAffineTransform transform; + + public PTuple(final PNode n, final PAffineTransform t) { + node = n; + transform = t; + } + } +} diff --git a/core/src/main/java/org/piccolo2d/util/PStack.java b/core/src/main/java/org/piccolo2d/util/PStack.java new file mode 100644 index 0000000..700526f --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PStack.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.util.ArrayList; + +/** + * PStack this class should be removed when a non thread safe stack is + * added to the java class libraries. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PStack extends ArrayList { + /** + * Allows for future serialization code to understand versioned binary + * formats. + */ + private static final long serialVersionUID = 1L; + + /** + * Creates an empty stack. + */ + public PStack() { + } + + /** + * Pushes the provided object onto the top of the stack. + * + * @param o object to add to the stack + */ + public void push(final Object o) { + add(o); + } + + /** + * Returns topmost element on the stack, or null if stack is empty. + * + * @return topmost element on the stack, or null if empty + */ + public Object peek() { + final int s = size(); + if (s == 0) { + return null; + } + else { + return get(s - 1); + } + } + + /** + * Removes top element on the stack and returns it. + * + * @return topmost element on stack. + */ + public Object pop() { + return remove(size() - 1); + } +} diff --git a/core/src/main/java/org/piccolo2d/util/PUtil.java b/core/src/main/java/org/piccolo2d/util/PUtil.java new file mode 100644 index 0000000..66f0de5 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/PUtil.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.BasicStroke; +import java.awt.Stroke; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PRoot; + + +/** + * PUtil util methods for the Piccolo framework. + *
+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PUtil { + /** + * PActivities are broken into steps, this is how many milliseconds should + * pass between steps. + */ + public static final long DEFAULT_ACTIVITY_STEP_RATE = 20; + + /** Rate in milliseconds at which the activity timer will get invoked. */ + public static final int ACTIVITY_SCHEDULER_FRAME_DELAY = 10; + + /** An iterator that iterates over an empty collection. */ + public static final Iterator NULL_ITERATOR = Collections.EMPTY_LIST.iterator(); + + /** + * Used when persisting paths to an object stream. Used to mark the end of + * the path. + */ + private static final int PATH_TERMINATOR = -1; + + /** A utility enumeration with no elements. */ + public static final Enumeration NULL_ENUMERATION = new Enumeration() { + public boolean hasMoreElements() { + return false; + } + + public Object nextElement() { + return null; + } + }; + + /** + * Creates the simplest possible scene graph. 1 Camera, 1 Layer, 1 Root + * + * @return a basic scene with 1 camera, layer and root + */ + public static PCamera createBasicScenegraph() { + final PRoot root = new PRoot(); + final PLayer layer = new PLayer(); + final PCamera camera = new PCamera(); + + root.addChild(camera); + root.addChild(layer); + camera.addLayer(layer); + + return camera; + } + + /** + * Serializes the given stroke object to the object output stream provided. + * By default strokes are not serializable. This method solves that problem. + * + * @param stroke stroke to be serialize + * @param out stream to which the stroke is to be serialized + * @throws IOException can occur if exception occurs with underlying output + * stream + */ + public static void writeStroke(final Stroke stroke, final ObjectOutputStream out) throws IOException { + if (stroke instanceof Serializable) { + out.writeBoolean(true); + out.writeBoolean(true); + out.writeObject(stroke); + } + else if (stroke instanceof BasicStroke) { + out.writeBoolean(true); + out.writeBoolean(false); + writeBasicStroke((BasicStroke) stroke, out); + } + else { + out.writeBoolean(false); + } + } + + private static void writeBasicStroke(final BasicStroke basicStroke, final ObjectOutputStream out) + throws IOException { + final float[] dash = basicStroke.getDashArray(); + + if (dash == null) { + out.write(0); + } + else { + out.write(dash.length); + for (int i = 0; i < dash.length; i++) { + out.writeFloat(dash[i]); + } + } + + out.writeFloat(basicStroke.getLineWidth()); + out.writeInt(basicStroke.getEndCap()); + out.writeInt(basicStroke.getLineJoin()); + out.writeFloat(basicStroke.getMiterLimit()); + out.writeFloat(basicStroke.getDashPhase()); + } + + /** + * Reconstitutes a stroke from the provided Object Input Stream. According + * to the scheme found in writeStroke. By default strokes are not + * serializable. + * + * @param in stream from which Stroke is to be read + * @return a stroke object + * @throws IOException occurs if an exception occurs reading from in stream + * @throws ClassNotFoundException should never happen, but can if somehow + * the stroke class is not on the classpath + */ + public static Stroke readStroke(final ObjectInputStream in) throws IOException, ClassNotFoundException { + final boolean wroteStroke = in.readBoolean(); + if (!wroteStroke) { + return null; + } + + final boolean serializedStroke = in.readBoolean(); + if (serializedStroke) { + return (Stroke) in.readObject(); + } + + return readBasicStroke(in); + } + + private static Stroke readBasicStroke(final ObjectInputStream in) throws IOException { + float[] dash = null; + final int dashLength = in.read(); + + if (dashLength != 0) { + dash = new float[dashLength]; + for (int i = 0; i < dashLength; i++) { + dash[i] = in.readFloat(); + } + } + + final float lineWidth = in.readFloat(); + final int endCap = in.readInt(); + final int lineJoin = in.readInt(); + final float miterLimit = in.readFloat(); + final float dashPhase = in.readFloat(); + + return new BasicStroke(lineWidth, endCap, lineJoin, miterLimit, dash, dashPhase); + } + + /** + * Reads a path from the provided inputStream in accordance with the + * serialization policy defined in writePath. + * + * @param in stream from which to read the path. + * @return reconstituted path + * @throws IOException if an unknown path type is read from the stream + * @throws ClassNotFoundException should never happen, but can if somehow + * the classpath is seriously messed up + */ + public static GeneralPath readPath(final ObjectInputStream in) throws IOException, ClassNotFoundException { + final GeneralPath path = new GeneralPath(); + + while (true) { + final int segType = in.readInt(); + + switch (segType) { + case PathIterator.SEG_MOVETO: + path.moveTo(in.readFloat(), in.readFloat()); + break; + + case PathIterator.SEG_LINETO: + path.lineTo(in.readFloat(), in.readFloat()); + break; + + case PathIterator.SEG_QUADTO: + path.quadTo(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); + break; + + case PathIterator.SEG_CUBICTO: + path.curveTo(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in + .readFloat()); + break; + + case PathIterator.SEG_CLOSE: + path.closePath(); + break; + + case PATH_TERMINATOR: + return path; + + default: + throw new IOException("Unknown path type encountered while deserializing path."); + } + } + } + + /** + * Serializes the given path to the provided Object Output Stream. + * + * @param path path to be serialized + * @param out stream to which the path should be serialized + * @throws IOException if unknown path segment type is encountered, or an + * exception occurs writing to the output stream + */ + public static void writePath(final GeneralPath path, final ObjectOutputStream out) throws IOException { + final PathIterator i = path.getPathIterator(null); + final float[] data = new float[6]; + + while (!i.isDone()) { + switch (i.currentSegment(data)) { + case PathIterator.SEG_MOVETO: + out.writeInt(PathIterator.SEG_MOVETO); + out.writeFloat(data[0]); + out.writeFloat(data[1]); + break; + + case PathIterator.SEG_LINETO: + out.writeInt(PathIterator.SEG_LINETO); + out.writeFloat(data[0]); + out.writeFloat(data[1]); + break; + + case PathIterator.SEG_QUADTO: + out.writeInt(PathIterator.SEG_QUADTO); + out.writeFloat(data[0]); + out.writeFloat(data[1]); + out.writeFloat(data[2]); + out.writeFloat(data[3]); + break; + + case PathIterator.SEG_CUBICTO: + out.writeInt(PathIterator.SEG_CUBICTO); + out.writeFloat(data[0]); + out.writeFloat(data[1]); + out.writeFloat(data[2]); + out.writeFloat(data[3]); + out.writeFloat(data[4]); + out.writeFloat(data[5]); + break; + + case PathIterator.SEG_CLOSE: + out.writeInt(PathIterator.SEG_CLOSE); + break; + + default: + throw new IOException("Unknown path type encountered while serializing path."); + } + + i.next(); + } + + out.writeInt(PATH_TERMINATOR); + } +} diff --git a/core/src/main/java/org/piccolo2d/util/package.html b/core/src/main/java/org/piccolo2d/util/package.html new file mode 100644 index 0000000..c6af443 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/util/package.html @@ -0,0 +1,5 @@ +
+This package defines several utility classes that are likely +to be useful for Piccolo applications. These utility classes are +also used within the implementation of Piccolo. + diff --git a/core/src/test/java/edu/umd/cs/piccolo/MockPCamera.java b/core/src/test/java/edu/umd/cs/piccolo/MockPCamera.java deleted file mode 100644 index 98cd5a1..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/MockPCamera.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.util.ArrayList; -import java.util.List; - -import edu.umd.cs.piccolo.util.PBounds; - -/** - * Mock PCamera. - */ -class MockPCamera extends PCamera { - private static final long serialVersionUID = 1L; - private final List notifications = new ArrayList(); - - public void repaintFromLayer(final PBounds bounds, final PLayer layer) { - notifications.add(new Notification("repaintFromLayer", bounds, layer)); - super.repaintFromLayer(bounds, layer); - } - - static class Notification { - private final String type; - private final PBounds bounds; - // this should really be PLayer - private final PNode layer; - - private Notification(final String type, final PBounds bounds, final PNode layer) { - this.bounds = bounds; - this.layer = layer; - this.type = type; - } - - public PNode getLayer() { - return layer; - } - - public PBounds getBounds() { - return bounds; - } - } - - public int getNotificationCount() { - return notifications.size(); - } - - public Notification getNotification(final int i) { - return (Notification) notifications.get(i); - } - -} \ No newline at end of file diff --git a/core/src/test/java/edu/umd/cs/piccolo/MockPInputEventListener.java b/core/src/test/java/edu/umd/cs/piccolo/MockPInputEventListener.java deleted file mode 100644 index 5924045..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/MockPInputEventListener.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.util.ArrayList; -import java.util.List; - -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventListener; - -/** - * Mock PInputEventListener. - */ -public class MockPInputEventListener implements PInputEventListener { - static class Notification { - public PInputEvent event; - public int type; - - public Notification(final PInputEvent event, final int type) { - this.event = event; - this.type = type; - } - } - - private final List notifications = new ArrayList(); - - public void processEvent(final PInputEvent aEvent, final int type) { - notifications.add(new Notification(aEvent, type)); - } - - public int getNotificationCount() { - return notifications.size(); - } - - public Notification getNotification(final int index) { - return (Notification) notifications.get(index); - } - -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/MockPropertyChangeListener.java b/core/src/test/java/edu/umd/cs/piccolo/MockPropertyChangeListener.java deleted file mode 100644 index 47ec517..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/MockPropertyChangeListener.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.List; - -/** - * Mock PropertyChangeListener. - */ -public class MockPropertyChangeListener implements PropertyChangeListener { - private final List changes = new ArrayList(); - - public void propertyChange(final PropertyChangeEvent evt) { - changes.add(evt); - } - - public int getPropertyChangeCount() { - return changes.size(); - } - - public PropertyChangeEvent getPropertyChange(final int index) { - return (PropertyChangeEvent) changes.get(index); - } -} \ No newline at end of file diff --git a/core/src/test/java/edu/umd/cs/piccolo/PCameraTest.java b/core/src/test/java/edu/umd/cs/piccolo/PCameraTest.java deleted file mode 100644 index 2f5a988..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PCameraTest.java +++ /dev/null @@ -1,738 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.GraphicsEnvironment; -import java.awt.geom.AffineTransform; -import java.awt.geom.Dimension2D; -import java.awt.geom.Point2D; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.util.Collection; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.activities.PTransformActivity; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDebug; -import edu.umd.cs.piccolo.util.PNodeFilter; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PPickPath; - -/** - * Unit test for PCamera. - */ -public class PCameraTest extends TestCase { - - private PCamera camera; - - public PCameraTest(final String name) { - super(name); - } - - public void setUp() { - camera = new PCamera(); - PDebug.debugBounds = false; - PDebug.debugFullBounds = false; - } - - public void testClone() throws CloneNotSupportedException { - final PLayer layer1 = new PLayer(); - final PLayer layer2 = new PLayer(); - - final PCamera camera1 = new PCamera(); - camera1.addLayer(layer1); - camera1.addLayer(layer2); - - - final PCamera cameraCopy = (PCamera) camera1.clone(); - //TODO: assertEquals(2, cameraCopy.getLayerCount()); - } - - public void testCameraShouldHaveNullComponentUntilAssigned() { - assertNull(camera.getComponent()); - - final MockPComponent component = new MockPComponent(); - camera.setComponent(component); - - assertNotNull(camera.getComponent()); - assertEquals(component, camera.getComponent()); - } - - public void testLayersReferenceIsNotNullByDefault() { - assertNotNull(camera.getLayersReference()); - } - - public void testCameraHasNoLayersByDefault() { - assertEquals(0, camera.getLayerCount()); - } - - public void testIndexOfLayerReturnsMinusOneWhenLayerNotFound() { - final PLayer orphanLayer = new PLayer(); - assertEquals(-1, camera.indexOfLayer(orphanLayer)); - - camera.addLayer(new PLayer()); - assertEquals(-1, camera.indexOfLayer(orphanLayer)); - } - - public void testRemoveLayerByReferenceWorks() { - final PLayer layer = new PLayer(); - camera.addLayer(layer); - camera.removeLayer(layer); - assertEquals(0, camera.getLayerCount()); - } - - public void testRemoveLayerByReferenceDoesNothingWithStrangeLayerWorks() { - final PLayer strangeLayer = new PLayer(); - camera.removeLayer(strangeLayer); - } - - public void testRemoveLayerRemovesTheCameraFromTheLayer() { - final PLayer layer = new PLayer(); - camera.addLayer(layer); - camera.removeLayer(layer); - assertEquals(0, layer.getCameraCount()); - } - - public void testAddingLayerAddCameraToLayer() { - final PLayer layer = new PLayer(); - camera.addLayer(layer); - assertSame(camera, layer.getCamera(0)); - } - - public void testGetFullUnionOfLayerFullBoundsWorks() { - final PLayer layer1 = new PLayer(); - layer1.setBounds(0, 0, 10, 10); - camera.addLayer(layer1); - - final PLayer layer2 = new PLayer(); - layer2.setBounds(10, 10, 10, 10); - camera.addLayer(layer2); - - final PBounds fullLayerBounds = camera.getUnionOfLayerFullBounds(); - assertEquals(new PBounds(0, 0, 20, 20), fullLayerBounds); - } - - public void testPaintPaintsAllLayers() { - final PCanvas canvas = new PCanvas(); - final PCamera camera = canvas.getCamera(); - - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final Graphics2D g2 = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); - - final PLayer layer1 = canvas.getLayer(); - final PNode blueSquare = new PNode(); - blueSquare.setPaint(Color.BLUE); - blueSquare.setBounds(0, 0, 10, 10); - layer1.addChild(blueSquare); - camera.addLayer(layer1); - - final PLayer layer2 = new PLayer(); - canvas.getLayer().getRoot().addChild(layer2); - layer2.setOffset(10, 10); - final PNode redSquare = new PNode(); - redSquare.setPaint(Color.RED); - redSquare.setBounds(0, 0, 10, 10); - layer2.addChild(redSquare); - camera.addLayer(layer2); - - canvas.setBounds(0, 0, 20, 20); - canvas.paint(g2); - - assertEquals(Color.BLUE.getRGB(), img.getRGB(5, 5)); - assertEquals(Color.RED.getRGB(), img.getRGB(15, 15)); - } - - public void testPickPackWorksInSimpleCases() { - final PLayer layer = new PLayer(); - camera.addChild(layer); - - final PNode node1 = new PNode(); - node1.setBounds(0, 0, 10, 10); - layer.addChild(node1); - - final PNode node2 = new PNode(); - node2.setBounds(0, 0, 10, 10); - node2.setOffset(10, 10); - layer.addChild(node2); - - final PPickPath path1 = camera.pick(5, 5, 1); - assertEquals(node1, path1.getPickedNode()); - - final PPickPath path2 = camera.pick(15, 15, 1); - assertEquals(node2, path2.getPickedNode()); - } - - public void testDefaultViewScaleIsOne() { - assertEquals(1, camera.getViewScale(), 0.0001); - } - - public void testGetViewBoundsTransformsCamerasBounds() { - camera.setBounds(0, 0, 100, 100); - camera.getViewTransformReference().scale(10, 10); - assertEquals(new PBounds(0, 0, 10, 10), camera.getViewBounds()); - } - - public void testScaleViewIsCummulative() { - camera.scaleView(2); - assertEquals(2, camera.getViewScale(), 0.001); - camera.scaleView(2); - assertEquals(4, camera.getViewScale(), 0.001); - } - - public void testSetViewScalePersists() { - camera.setViewScale(2); - assertEquals(2, camera.getViewScale(), 0.001); - camera.setViewScale(2); - assertEquals(2, camera.getViewScale(), 0.001); - } - - public void testTranslateViewIsCummulative() { - camera.translateView(100, 100); - assertEquals(100, camera.getViewTransform().getTranslateX(), 0.001); - camera.translateView(100, 100); - assertEquals(200, camera.getViewTransform().getTranslateX(), 0.001); - } - - public void testViewTransformedFiresChangeEvent() { - final MockPropertyChangeListener mockListener = new MockPropertyChangeListener(); - camera.addPropertyChangeListener(PCamera.PROPERTY_VIEW_TRANSFORM, mockListener); - camera.setViewTransform(AffineTransform.getScaleInstance(2, 2)); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testAnimateViewToCenterBoundsIsImmediateWhenDurationIsZero() { - camera.setViewBounds(new PBounds(0, 0, 10, 10)); - final PBounds targetBounds = new PBounds(-5, -5, 10, 10); - final PActivity activity = camera.animateViewToCenterBounds(targetBounds, true, 0); - assertNull(activity); - - assertEquals(-5, camera.getViewTransform().getTranslateX(), 0.001); - assertEquals(-5, camera.getViewTransform().getTranslateY(), 0.001); - } - - public void testAnimateViewToCenterBoundsCreatesValidActivity() { - camera.setViewBounds(new PBounds(0, 0, 10, 10)); - final PBounds targetBounds = new PBounds(-5, -5, 10, 10); - final PActivity activity = camera.animateViewToCenterBounds(targetBounds, true, 100); - assertNotNull(activity); - - assertEquals(100, activity.getDuration()); - assertFalse(activity.isStepping()); - } - - public void testAnimateViewToPanToBoundsDoesNotAffectScale() { - camera.setViewBounds(new PBounds(0, 0, 10, 10)); - camera.animateViewToPanToBounds(new PBounds(10, 10, 10, 30), 0); - - assertEquals(1, camera.getViewScale(), 0.0001); - } - - public void testAnimateViewToPanToBoundsIsImmediateWhenDurationIsZero() { - camera.setViewBounds(new PBounds(0, 0, 10, 10)); - final PActivity activity = camera.animateViewToPanToBounds(new PBounds(10, 10, 10, 10), 0); - - assertNull(activity); - assertEquals(AffineTransform.getTranslateInstance(-15, -15), camera.getViewTransform()); - } - - public void testAnimateViewToPanToBoundsReturnsAppropriatelyConfiguredActivity() { - camera.setViewBounds(new PBounds(0, 0, 10, 10)); - final PTransformActivity activity = camera.animateViewToPanToBounds(new PBounds(10, 10, 10, 10), 100); - - assertNotNull(activity); - assertEquals(100, activity.getDuration()); - assertFalse(activity.isStepping()); - assertEquals(AffineTransform.getTranslateInstance(-15, -15), new PAffineTransform(activity - .getDestinationTransform())); - } - - public void testPDebugDebugBoundsPaintsBounds() throws IOException { - final PCanvas canvas = new PCanvas(); - - final PNode parent = new PNode(); - final PNode child = new PNode(); - - parent.addChild(child); - parent.setBounds(0, 0, 10, 10); - child.setBounds(20, 0, 10, 10); - canvas.setBounds(0, 0, 100, 100); - canvas.setSize(100, 100); - canvas.getLayer().addChild(parent); - - parent.setPaint(Color.GREEN); - child.setPaint(Color.GREEN); - - PDebug.debugBounds = true; - PDebug.debugFullBounds = false; - - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final Graphics2D graphics = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); - graphics.setPaint(Color.WHITE); - graphics.fillRect(0, 0, 100, 100); - final PPaintContext pc = new PPaintContext(graphics); - canvas.setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); - canvas.getCamera().paint(pc); - - // First Square's Bounds - assertPointColor(Color.RED, img, 0, 0); - assertPointColor(Color.RED, img, 9, 0); - assertPointColor(Color.RED, img, 10, 10); - assertPointColor(Color.RED, img, 0, 10); - - // Second Square's Bounds - assertPointColor(Color.RED, img, 20, 0); - assertPointColor(Color.RED, img, 29, 0); - assertPointColor(Color.RED, img, 29, 10); - assertPointColor(Color.RED, img, 20, 10); - - // Ensure point between the squares on the full bounds is not drawn - assertPointColor(Color.WHITE, img, 15, 10); - } - - private void assertPointColor(final Color expectedColor, final BufferedImage img, final int x, final int y) { - assertEquals(expectedColor.getRGB(), img.getRGB(x, y)); - } - - public void testSetViewOffsetIsNotCummulative() { - camera.setViewOffset(100, 100); - camera.setViewOffset(100, 100); - assertEquals(100, camera.getViewTransform().getTranslateX(), 0.001); - assertEquals(100, camera.getViewTransform().getTranslateY(), 0.001); - - } - - public void testDefaultViewConstraintsIsNone() { - assertEquals(PCamera.VIEW_CONSTRAINT_NONE, camera.getViewConstraint()); - } - - public void testSetViewContraintsPersists() { - camera.setViewConstraint(PCamera.VIEW_CONSTRAINT_ALL); - assertEquals(PCamera.VIEW_CONSTRAINT_ALL, camera.getViewConstraint()); - camera.setViewConstraint(PCamera.VIEW_CONSTRAINT_CENTER); - assertEquals(PCamera.VIEW_CONSTRAINT_CENTER, camera.getViewConstraint()); - camera.setViewConstraint(PCamera.VIEW_CONSTRAINT_NONE); - assertEquals(PCamera.VIEW_CONSTRAINT_NONE, camera.getViewConstraint()); - } - - public void testSetViewConstraintsThrowsIllegalArgumentException() { - try { - camera.setViewConstraint(-1); - } - catch (IllegalArgumentException e) { - // expected - } - } - - public void testTooFewLayersCamera() { - PCamera tooFew = new TooFewLayersCamera(); - MockPLayer layer0 = new MockPLayer(); - MockPLayer layer1 = new MockPLayer(); - tooFew.addLayer(layer0); - tooFew.addLayer(layer1); - assertEquals(layer0, tooFew.getLayer(0)); - assertEquals(layer1, tooFew.getLayer(1)); - assertEquals(layer0, tooFew.getLayersReference().get(0)); - assertEquals(layer1, tooFew.getLayersReference().get(1)); - assertEquals(0, tooFew.indexOfLayer(layer0)); - assertEquals(0, tooFew.indexOfLayer(layer0)); - - // pickCameraView - PPickPath pickPath = new PPickPath(tooFew, new PBounds(0, 0, 400, 400)); - tooFew.pickCameraView(pickPath); - assertTrue(layer0.fullPickCalled()); - assertTrue(layer1.fullPickCalled()); - - // paintCameraView - BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = image.createGraphics(); - PPaintContext paintContext = new PPaintContext(graphics); - tooFew.paintCameraView(paintContext); - assertTrue(layer0.fullPaintCalled()); - assertTrue(layer1.fullPaintCalled()); - - // getUnionOfLayerFullBounds - tooFew.getUnionOfLayerFullBounds(); - assertTrue(layer0.fullBoundsReferenceCalled()); - assertTrue(layer1.fullBoundsReferenceCalled()); - - // paintDebugInfo - PDebug.debugBounds = true; - tooFew.paintDebugInfo(paintContext); - assertTrue(layer0.getAllNodesCalled()); - assertTrue(layer1.getAllNodesCalled()); - PDebug.debugBounds = false; - - graphics.dispose(); - } - - public void testTooManyLayersCamera() { - PCamera tooMany = new TooManyLayersCamera(); - MockPLayer layer0 = new MockPLayer(); - MockPLayer layer1 = new MockPLayer(); - tooMany.addLayer(layer0); - tooMany.addLayer(layer1); - assertEquals(layer0, tooMany.getLayer(0)); - assertEquals(layer1, tooMany.getLayer(1)); - assertEquals(layer0, tooMany.getLayersReference().get(0)); - assertEquals(layer1, tooMany.getLayersReference().get(1)); - assertEquals(0, tooMany.indexOfLayer(layer0)); - assertEquals(0, tooMany.indexOfLayer(layer0)); - - // pickCameraView - PPickPath pickPath = new PPickPath(tooMany, new PBounds(0, 0, 400, 400)); - tooMany.pickCameraView(pickPath); - assertTrue(layer0.fullPickCalled()); - assertTrue(layer1.fullPickCalled()); - - // paintCameraView - BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = image.createGraphics(); - PPaintContext paintContext = new PPaintContext(graphics); - tooMany.paintCameraView(paintContext); - assertTrue(layer0.fullPaintCalled()); - assertTrue(layer1.fullPaintCalled()); - - // getUnionOfLayerFullBounds - tooMany.getUnionOfLayerFullBounds(); - assertTrue(layer0.fullBoundsReferenceCalled()); - assertTrue(layer1.fullBoundsReferenceCalled()); - - // paintDebugInfo - PDebug.debugBounds = true; - tooMany.paintDebugInfo(paintContext); - assertTrue(layer0.getAllNodesCalled()); - assertTrue(layer1.getAllNodesCalled()); - PDebug.debugBounds = false; - - graphics.dispose(); - } - - public void testRepaintFromNullParent() { - camera.setParent(null); - PCanvas canvas = new PCanvas(); - camera.setComponent(canvas); - camera.repaintFrom(new PBounds(0, 0, 1, 1), camera); - } - - public void testRepaintFromNullComponent() { - PNode parent = new PNode(); - camera.setParent(parent); - camera.setComponent(null); - camera.repaintFrom(new PBounds(0, 0, 1, 1), camera); - } - - public void testRepaintFromNullParentNullComponent() { - camera.setParent(null); - camera.setComponent(null); - camera.repaintFrom(new PBounds(0, 0, 1, 1), camera); - } - - public void testRepaintFromLayer() { - PLayer layer = new PLayer(); - camera.addLayer(layer); - camera.repaintFromLayer(new PBounds(0, 0, 1, 1), layer); - } - - public void testRepaintFromLayerNotViewedByCamera() { - PLayer layer = new PLayer(); - // todo: layer is not contained in list of layers viewed by camera, should complain - camera.repaintFromLayer(new PBounds(0, 0, 1, 1), layer); - } - - public void testRemoveLayerAtIndex() { - PLayer layer = new PLayer(); - camera.addLayer(layer); - assertEquals(1, camera.getLayerCount()); - assertEquals(1, camera.getLayersReference().size()); - camera.removeLayer(0); - assertEquals(0, camera.getLayerCount()); - assertEquals(0, camera.getLayersReference().size()); - } - - public void testRemoveLayerAtIndexIndexOutOfBounds() { - PLayer layer = new PLayer(); - camera.addLayer(layer); - try { - camera.removeLayer(2); - fail("removeLayer(2) expected IndexOutOfBoundsException"); - } - catch (IndexOutOfBoundsException e) { - // expected - } - } - - public void testPaintDebugInfoDebugFullBounds() { - PLayer layer = new PLayer(); - camera.addLayer(layer); - PNode child = new PNode(); - child.setBounds(0.0d, 0.0d, 200.0d, 200.0d); - layer.addChild(child); - BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = image.createGraphics(); - PPaintContext paintContext = new PPaintContext(graphics); - PDebug.debugFullBounds = true; - camera.paintDebugInfo(paintContext); - PDebug.debugFullBounds = false; - graphics.dispose(); - } - - public void testPaintDebugInfoDebugFullBoundsNoChildNodes() { - PLayer layer = new PLayer(); - camera.addLayer(layer); - BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics = image.createGraphics(); - PPaintContext paintContext = new PPaintContext(graphics); - PDebug.debugFullBounds = true; - camera.paintDebugInfo(paintContext); - PDebug.debugFullBounds = false; - graphics.dispose(); - } - - public void testPickAfterChildrenNotPicked() { - PPickPath pickPath = new PPickPath(camera, new PBounds(-5, -5, 0, 0)); - assertFalse(camera.pickAfterChildren(pickPath)); - } - - public void testLocalToViewPoint2D() { - Point2D local = new Point2D.Double(0.0d, 0.0d); - camera.localToView(local); - assertEquals(0.0d, local.getX(), 0.1d); - assertEquals(0.0d, local.getY(), 0.1d); - } - - public void testLocalToViewPoint2DTranslateView() { - camera.translateView(10.0d, 20.0d); - Point2D local = new Point2D.Double(0.0d, 0.0d); - camera.localToView(local); - assertEquals(-10.0d, local.getX(), 0.1d); - assertEquals(-20.0d, local.getY(), 0.1d); - } - - public void testLocalToViewPoint2DScaleView() { - camera.scaleView(10.0d); - Point2D local = new Point2D.Double(10.0d, 20.0d); - camera.localToView(local); - assertEquals(1.0d, local.getX(), 0.1d); - assertEquals(2.0d, local.getY(), 0.1d); - } - - public void testLocalToViewDimension2D() { - Dimension2D local = new Dimension(0, 0); - camera.localToView(local); - assertEquals(0.0d, local.getWidth(), 0.1d); - assertEquals(0.0d, local.getHeight(), 0.1d); - } - - public void testLocalToViewDimension2DTranslateView() { - camera.translateView(10.0d, 20.0d); - Dimension2D local = new Dimension(0, 0); - camera.localToView(local); - assertEquals(0.0d, local.getWidth(), 0.1d); - assertEquals(0.0d, local.getHeight(), 0.1d); - } - - public void testLocalToViewDimension2DScaleView() { - camera.scaleView(10.0d); - Dimension2D local = new Dimension(10, 20); - camera.localToView(local); - assertEquals(1.0d, local.getWidth(), 0.1d); - assertEquals(2.0d, local.getHeight(), 0.1d); - } - - public void testViewToLocalPoint2D() { - Point2D view = new Point2D.Double(0.0d, 0.0d); - camera.viewToLocal(view); - assertEquals(0.0d, view.getX(), 0.1d); - assertEquals(0.0d, view.getY(), 0.1d); - } - - public void testViewToLocalPoint2DTranslateView() { - camera.translateView(10.0d, 20.0d); - Point2D view = new Point2D.Double(0.0d, 0.0d); - camera.viewToLocal(view); - assertEquals(10.0d, view.getX(), 0.1d); - assertEquals(20.0d, view.getY(), 0.1d); - } - - public void testViewToLocalPoint2DScaleView() { - camera.scaleView(10.0d); - Point2D view = new Point2D.Double(10.0d, 20.0d); - camera.viewToLocal(view); - assertEquals(100.0d, view.getX(), 0.1d); - assertEquals(200.0d, view.getY(), 0.1d); - } - - public void testViewToLocalDimension2D() { - Dimension2D view = new Dimension(0, 0); - camera.viewToLocal(view); - assertEquals(0.0d, view.getWidth(), 0.1d); - assertEquals(0.0d, view.getHeight(), 0.1d); - } - - public void testViewToLocalDimension2DTranslateView() { - camera.translateView(10.0d, 20.0d); - Dimension2D view = new Dimension(0, 0); - camera.viewToLocal(view); - assertEquals(0.0d, view.getWidth(), 0.1d); - assertEquals(0.0d, view.getHeight(), 0.1d); - } - - public void testViewToLocalDimension2DScaleView() { - camera.scaleView(10.0d); - Dimension2D view = new Dimension(10, 20); - camera.viewToLocal(view); - assertEquals(100.0d, view.getWidth(), 0.1d); - assertEquals(200.0d, view.getHeight(), 0.1d); - } - - public void testPickWithoutIntersectionStillContainsCamera() { - camera.offset(10.0d, 10.0d); - PPickPath pickPath = camera.pick(0.0d, 0.0d, 0.0d); - // todo: don't understand why this should be the case - assertFalse(pickPath.getNodeStackReference().isEmpty()); - assertTrue(pickPath.getNodeStackReference().contains(camera)); - } - - /* - public void testAnimateViewToTransformIdentity() { - PRoot root = new PRoot(); - PLayer layer = new PLayer(); - root.addChild(camera); - root.addChild(layer); - camera.addChild(layer); - - AffineTransform identity = new AffineTransform(); - camera.animateViewToTransform(identity, System.currentTimeMillis()); - // todo: throws NPE at PActivityScheduler.processActivities(PActivityScheduler.java:176) - root.waitForActivities(); - - assertSame(identity, camera.getViewTransformReference()); - } - */ - - - static class MockPComponent implements PComponent { - - public void paintImmediately() { - } - - public void popCursor() { - } - - public void pushCursor(final Cursor cursor) { - } - - public void repaint(final PBounds bounds) { - } - - public void setInteracting(final boolean interacting) { - } - } - - /** - * Mock PLayer. Should consider using mock library in version 2.0. - */ - private static final class MockPLayer extends PLayer { - private static final long serialVersionUID = 1L; - private boolean fullBoundsReferenceCalled = false; - private boolean fullPaintCalled = false; - private boolean getAllNodesCalled = false; - private boolean fullPickCalled = false; - - /** {@inheritDoc} */ - public PBounds getFullBoundsReference() { - fullBoundsReferenceCalled = true; - return super.getFullBoundsReference(); - } - - /** {@inheritDoc} */ - public void fullPaint(final PPaintContext paintContext) { - fullPaintCalled = true; - super.fullPaint(paintContext); - } - - /** {@inheritDoc} */ - public Collection getAllNodes(final PNodeFilter filter, final Collection nodes) { - getAllNodesCalled = true; - return super.getAllNodes(filter, nodes); - } - - /** {@inheritDoc} */ - public boolean fullPick(final PPickPath pickPath) { - fullPickCalled = true; - return super.fullPick(pickPath); - } - - private boolean fullBoundsReferenceCalled() { - return fullBoundsReferenceCalled; - } - - private boolean fullPaintCalled() { - return fullPaintCalled; - } - - private boolean getAllNodesCalled() { - return getAllNodesCalled; - } - - private boolean fullPickCalled() { - return fullPickCalled; - } - } - - /** - * Subclass of PCamera that advertises too few layers. - */ - private static final class TooFewLayersCamera extends PCamera { - private static final long serialVersionUID = 1L; - - /** {@inheritDoc} */ - public int getLayerCount() { - return Math.max(0, super.getLayerCount() - 1); - } - } - - /** - * Subclass of PCamera that advertises too many layers. - */ - private static final class TooManyLayersCamera extends PCamera { - private static final long serialVersionUID = 1L; - - /** {@inheritDoc} */ - public int getLayerCount() { - return super.getLayerCount() + 1; - } - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/PCanvasTest.java b/core/src/test/java/edu/umd/cs/piccolo/PCanvasTest.java deleted file mode 100644 index a4099d0..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PCanvasTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.Cursor; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.event.PInputEventListener; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; - -/** - * Unit test for PCanvas. - */ -public class PCanvasTest extends TestCase { - private PCanvas canvas; - private MockPInputEventListener mockListener; - - public void setUp() { - canvas = new PCanvas(); - mockListener = new MockPInputEventListener(); - } - - public void testDefaultPanHandlerIsNotNull() { - assertNotNull(canvas.getPanEventHandler()); - } - - public void testGetInteractingReturnsFalseByDefault() { - assertFalse(canvas.getInteracting()); - } - - public void testDefaultNumberOfEventListenersIs2() { - final PInputEventListener[] listeners = canvas.getInputEventListeners(); - assertNotNull(listeners); - assertEquals(2, listeners.length); - } - - public void testGetAnimatingReturnsFalseByDefault() { - assertFalse(canvas.getAnimating()); - } - - public void testSetInteractingPersists() { - canvas.setInteracting(true); - assertTrue(canvas.getInteracting()); - } - - public void testDefaultAnimatingRenderQualityIsLow() { - assertEquals(PPaintContext.LOW_QUALITY_RENDERING, canvas.getAnimatingRenderQuality()); - } - - public void testDefaultInteractingRenderQualityIsLow() { - assertEquals(PPaintContext.LOW_QUALITY_RENDERING, canvas.getInteractingRenderQuality()); - } - - public void testDefaultZoomHandlerIsNotNull() { - assertNotNull(canvas.getZoomEventHandler()); - } - - public void testCanvasLayerIsNotNullByDefault() { - assertNotNull(canvas.getLayer()); - } - - public void testCursorStackWorksAsExpected() { - final Cursor moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); - final Cursor handCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); - final Cursor crosshairCursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); - - canvas.pushCursor(moveCursor); - canvas.pushCursor(handCursor); - canvas.pushCursor(crosshairCursor); - - assertEquals(crosshairCursor, canvas.getCursor()); - canvas.popCursor(); - assertEquals(handCursor, canvas.getCursor()); - canvas.popCursor(); - assertEquals(moveCursor, canvas.getCursor()); - } - - public void testPoppingEmptyCursorStackShouldDoNothing() { - try { - canvas.popCursor(); - } - catch (final IndexOutOfBoundsException e) { - fail("Pop cursor shouldn't fail on an empty stack"); - } - assertEquals(Cursor.getDefaultCursor(), canvas.getCursor()); - } - - public void testSettingCanvasBoundsAffectsCameraBounds() { - canvas.setBounds(0, 0, 100, 100); - assertEquals(new PBounds(0, 0, 100, 100), canvas.getCamera().getBounds()); - } - - public void testAddInputEventListenersIsHonoured() { - canvas.addInputEventListener(mockListener); - final PInputEventListener[] listeners = canvas.getInputEventListeners(); - assertNotNull(listeners); - assertEquals(3, listeners.length); // zoom + pan + mockListener - // by default - } - - public void testRemoveInputEventListenersIsHonoured() { - canvas.addInputEventListener(mockListener); - canvas.removeInputEventListener(mockListener); - final PInputEventListener[] listeners = canvas.getInputEventListeners(); - assertNotNull(listeners); - assertEquals(2, listeners.length); // zoom + pan + mockListener - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/PInputManagerTest.java b/core/src/test/java/edu/umd/cs/piccolo/PInputManagerTest.java deleted file mode 100644 index 8b9bc93..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PInputManagerTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.event.FocusEvent; -import java.awt.event.KeyEvent; -import java.awt.geom.Point2D; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPickPath; - -/** - * Unit test for PInputManager. - */ -public class PInputManagerTest extends TestCase { - private PInputManager manager; - private MockPInputEventListener mockListener; - - public void setUp() { - manager = new PInputManager(); - mockListener = new MockPInputEventListener(); - } - - public void testGetKeyboardFocusNullByDefault() { - assertNull(manager.getKeyboardFocus()); - } - - public void testSetKeyboardFocusIsPersisted() { - manager.setKeyboardFocus(mockListener); - assertEquals(mockListener, manager.getKeyboardFocus()); - } - - public void testSetKeyboardFocusDispatchesEventsAboutFocus() { - final MockPInputEventListener oldListener = new MockPInputEventListener(); - manager.setKeyboardFocus(oldListener); - - assertEquals(1, oldListener.getNotificationCount()); - assertEquals(FocusEvent.FOCUS_GAINED, oldListener.getNotification(0).type); - - final MockPInputEventListener newListener = new MockPInputEventListener(); - manager.setKeyboardFocus(newListener); - - assertEquals(1, newListener.getNotificationCount()); - assertEquals(FocusEvent.FOCUS_GAINED, newListener.getNotification(0).type); - assertEquals(2, oldListener.getNotificationCount()); - assertEquals(FocusEvent.FOCUS_LOST, oldListener.getNotification(1).type); - } - - public void testGetMouseFocusNullByDefault() { - assertNull(manager.getMouseFocus()); - } - - public void testSetMouseFocusPersists() { - final PCamera camera = new PCamera(); - final PPickPath path = new PPickPath(camera, new PBounds(0, 0, 10, 10)); - manager.setMouseFocus(path); - assertEquals(path, manager.getMouseFocus()); - } - - public void testGetMouseOverNullByDefault() { - assertNull(manager.getMouseOver()); - } - - public void testSetMouseOverPersists() { - final PCamera camera = new PCamera(); - final PPickPath path = new PPickPath(camera, new PBounds(0, 0, 10, 10)); - manager.setMouseOver(path); - assertEquals(path, manager.getMouseOver()); - } - - public void testGetCurrentCanvasPositionIsOriginByDefault() { - assertEquals(new Point2D.Double(0, 0), manager.getCurrentCanvasPosition()); - } - - public void testGetLastCanvasPositionIsOriginByDefault() { - assertEquals(new Point2D.Double(0, 0), manager.getLastCanvasPosition()); - } - - public void testKeyPressedDispatchesToCurrentFocus() { - manager.setKeyboardFocus(mockListener); - final PInputEvent event = new PInputEvent(manager, null); - manager.keyPressed(event); - assertEquals(2, mockListener.getNotificationCount()); - assertEquals(KeyEvent.KEY_PRESSED, mockListener.getNotification(1).type); - } - - public void testKeyReleasedDispatchesToCurrentFocus() { - manager.setKeyboardFocus(mockListener); - final PInputEvent event = new PInputEvent(manager, null); - manager.keyReleased(event); - assertEquals(2, mockListener.getNotificationCount()); - assertEquals(KeyEvent.KEY_RELEASED, mockListener.getNotification(1).type); - } - - public void testKeyTypedDispatchesToCurrentFocus() { - manager.setKeyboardFocus(mockListener); - final PInputEvent event = new PInputEvent(manager, null); - manager.keyTyped(event); - assertEquals(2, mockListener.getNotificationCount()); - assertEquals(KeyEvent.KEY_TYPED, mockListener.getNotification(1).type); - } - - public void testProcessInputMayBeCalledOnFreshManager() { - manager.processInput(); - } - -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/PLayerTest.java b/core/src/test/java/edu/umd/cs/piccolo/PLayerTest.java deleted file mode 100644 index dd822b1..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PLayerTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.util.Collection; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.util.PBounds; - -/** - * Unit test for PLayer. - */ -public class PLayerTest extends TestCase { - private PLayer layer; - - public void setUp() { - layer = new PLayer(); - } - - public void testLayerHasEmptyCamerasCollectionByDefault() { - final Collection cameras = layer.getCamerasReference(); - assertNotNull(cameras); - assertTrue(cameras.isEmpty()); - assertEquals(0, layer.getCameraCount()); - } - - public void testGetCameraByIndexThrowsIndexOutOfBoundsExceptionWhenOutOfBounds() { - final PCamera camera = new PCamera(); - layer.addCamera(camera); - try { - layer.getCamera(-1); - fail("Exception should have been thrown"); - } - catch (final IndexOutOfBoundsException e) { - // expected; - } - - try { - layer.getCamera(1); - fail("Exception should have been thrown"); - } - catch (final IndexOutOfBoundsException e) { - // expected; - } - } - - public void testGetCameraReturnsCameraAtCorrectIndex() { - final PCamera camera1 = new PCamera(); - final PCamera camera2 = new PCamera(); - final PCamera camera3 = new PCamera(); - - layer.addCamera(camera1); - layer.addCamera(camera2); - layer.addCamera(camera3); - - assertEquals(camera1, layer.getCamera(0)); - assertEquals(camera2, layer.getCamera(1)); - assertEquals(camera3, layer.getCamera(2)); - } - - public void testAddCameraCorrectlyHandlesIndex() { - final PCamera camera1 = new PCamera(); - final PCamera camera2 = new PCamera(); - final PCamera camera3 = new PCamera(); - - layer.addCamera(0, camera1); - layer.addCamera(0, camera2); - layer.addCamera(1, camera3); - - assertEquals(camera2, layer.getCamera(0)); - assertEquals(camera3, layer.getCamera(1)); - assertEquals(camera1, layer.getCamera(2)); - } - - public void testRemovingCameraByReferenceWorksWhenCameraIsFound() { - final PCamera camera = new PCamera(); - layer.addCamera(camera); - layer.removeCamera(camera); - assertEquals(0, layer.getCameraCount()); - } - - public void testRemovingCameraByIndexWorksWhenIndexIsValid() { - final PCamera camera = new PCamera(); - layer.addCamera(camera); - layer.removeCamera(0); - assertEquals(0, layer.getCameraCount()); - } - - public void testRemovingCameraNotAttachedToCameraShouldDoNothing() { - final PCamera strangerCamera = new PCamera(); - layer.removeCamera(strangerCamera); - assertEquals(0, layer.getCameraCount()); - } - - public void testRepaintFromNotifiesCameras() { - final MockPCamera camera = new MockPCamera(); - layer.addCamera(camera); - - final PBounds bounds = new PBounds(0, 0, 100, 100); - layer.repaintFrom(bounds, layer); - - assertEquals(1, camera.getNotificationCount()); - - final MockPCamera.Notification notification = camera.getNotification(0); - assertEquals(layer, notification.getLayer()); - assertEquals(bounds, notification.getBounds()); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/PNodeTest.java b/core/src/test/java/edu/umd/cs/piccolo/PNodeTest.java deleted file mode 100644 index d93f27a..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PNodeTest.java +++ /dev/null @@ -1,1448 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.GraphicsEnvironment; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.ListIterator; - -import javax.swing.text.MutableAttributeSet; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.activities.PColorActivity; -import edu.umd.cs.piccolo.activities.PInterpolatingActivity; -import edu.umd.cs.piccolo.activities.PTransformActivity; -import edu.umd.cs.piccolo.activities.PColorActivity.Target; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PAffineTransformException; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PNodeFilter; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PPickPath; -import edu.umd.cs.piccolo.util.PUtil; - -/** - * Unit test for PNode. - */ -public class PNodeTest extends TestCase { - - private MockPropertyChangeListener mockListener; - private PNode node; - - public PNodeTest(final String name) { - super(name); - } - - public void setUp() { - node = new PNode(); - mockListener = new MockPropertyChangeListener(); - } - - public void testCenterBaseBoundsOnPoint() { - node.setBounds(100, 300, 100, 80); - node.centerBoundsOnPoint(0, 0); - assertEquals(-50, node.getBoundsReference().getX(), 0); - assertEquals(-40, node.getBoundsReference().getY(), 0); - } - - public void testClientProperties() { - final PNode n = new PNode(); - - assertNull(n.getAttribute(null)); - n.addAttribute("a", "b"); - assertEquals(n.getAttribute("a"), "b"); - assertNull(n.getAttribute(null)); - n.addAttribute("a", null); - assertNull(n.getAttribute("a")); - } - - public void testFullScale() { - final PNode aParent = new PNode(); - final PNode aNode = new PNode(); - - aParent.addChild(aNode); - - aParent.scale(2.0); - aNode.scale(0.5); - - assertEquals(1.0, aNode.getGlobalScale(), 0); - - aParent.setScale(1.0); - assertEquals(0.5, aNode.getGlobalScale(), 0); - - aNode.setScale(.75); - assertEquals(0.75, aNode.getGlobalScale(), 0); - } - - public void testReparent() { - final PNode aParent = new PNode(); - final PNode aNode = new PNode(); - - aParent.setOffset(400, 500); - aParent.scale(0.5); - aNode.reparent(aParent); - - assertEquals(0, aNode.getGlobalTranslation().getX(), 0); - assertEquals(0, aNode.getGlobalTranslation().getY(), 0); - assertEquals(2.0, aNode.getScale(), 0); - - aNode.setGlobalScale(0.25); - aNode.setGlobalTranslation(new Point2D.Double(10, 10)); - - assertEquals(10, aNode.getGlobalTranslation().getX(), 0); - assertEquals(10, aNode.getGlobalTranslation().getY(), 0); - assertEquals(0.25, aNode.getGlobalScale(), 0); - } - - public void testFindIntersectingNodes() { - final PNode n = new PNode(); - final PNode c = new PNode(); - - n.addChild(c); - n.setBounds(0, 0, 100, 100); - c.setBounds(0, 0, 100, 100); - c.scale(200); - - ArrayList found = new ArrayList(); - final Rectangle2D rect2d = new Rectangle2D.Double(50, 50, 10, 10); - n.findIntersectingNodes(rect2d, found); - - assertEquals(found.size(), 2); - assertEquals(rect2d.getHeight(), 10, 0); - found = new ArrayList(); - - final PBounds bounds = new PBounds(50, 50, 10, 10); - n.findIntersectingNodes(bounds, found); - - assertEquals(found.size(), 2); - assertEquals(bounds.getHeight(), 10, 0); - } - - public void testRemoveNonexistantListener() { - final PNode n = new PNode(); - n.removeInputEventListener(new PBasicInputEventHandler()); - } - - public void testAddChildHandleDuplicates() { - final PNode parent = new PNode(); - parent.addChild(node); - parent.addChild(new PNode()); - parent.addChild(node); - assertEquals(1, parent.indexOfChild(node)); - } - - public void testAddChildCanSpecifyAnIndexAndDoesntReplace() { - final PNode parent = new PNode(); - parent.addChild(new PNode()); - parent.addChild(0, node); - assertEquals(0, parent.indexOfChild(node)); - assertEquals(2, parent.getChildrenCount()); - } - - public void testAddChildWithIndexMovesChildAround() { - final PNode parent = new PNode(); - - parent.addChild(new PNode()); - parent.addChild(new PNode()); - parent.addChild(node); - - parent.addChild(0, node); - assertEquals(node, parent.getChild(0)); - - parent.addChild(1, node); - assertEquals(node, parent.getChild(1)); - - parent.addChild(2, node); - assertEquals(node, parent.getChild(2)); - } - - 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 testCloneDoesNotCopyEventListeners() { - node.addInputEventListener(new PBasicInputEventHandler() {}); - - final PNode clonedNode = (PNode) node.clone(); - - assertNull(clonedNode.getListenerList()); - } - - public void testCloneClonesChildrenAswell() { - final PNode child = new PNode(); - node.addChild(child); - - final PNode clonedNode = (PNode) node.clone(); - - assertEquals(clonedNode.getChildrenCount(), 1); - assertNotSame(child, clonedNode.getChild(0)); - } - - public void testCloneDoesADeepCopy() { - final PNode child = new PNode(); - node.addChild(child); - - final PNode clonedNode = (PNode) node.clone(); - - assertNotSame(node.getChildrenReference(), clonedNode.getChildrenReference()); - assertNotSame(node.getChild(0), clonedNode.getChild(0)); - - assertNotSame(node.getBoundsReference(), clonedNode.getBoundsReference()); - } - - public void testCloneDoesNotCopyParent() { - final PNode child = new PNode(); - node.addChild(child); - - final PNode clonedChild = (PNode) child.clone(); - - assertNull(clonedChild.getParent()); - } - - public void testLocalToGlobal() { - final PNode aParent = new PNode(); - final PNode aChild = new PNode(); - - aParent.addChild(aChild); - aChild.scale(0.5); - - // bounds - final PBounds bnds = new PBounds(0, 0, 50, 50); - - aChild.localToGlobal(bnds); - assertEquals(0, bnds.x, 0); - assertEquals(0, bnds.y, 0); - assertEquals(25, bnds.width, 0); - assertEquals(25, bnds.height, 0); - - aChild.globalToLocal(bnds); - assertEquals(0, bnds.x, 0); - assertEquals(0, bnds.y, 0); - assertEquals(50, bnds.width, 0); - assertEquals(50, bnds.height, 0); - - aChild.getGlobalToLocalTransform(new PAffineTransform()); - aChild.getLocalToGlobalTransform(new PAffineTransform()).createTransformedShape(aChild.getBounds()); - - // dimensions - final PDimension dim = new PDimension(50, 50); - - aChild.localToGlobal(dim); - assertEquals(25, dim.getHeight(), 0); - assertEquals(25, dim.getWidth(), 0); - - aChild.globalToLocal(dim); - assertEquals(50, dim.getHeight(), 0); - assertEquals(50, dim.getWidth(), 0); - } - - public void testToString() { - final PNode a = new PNode(); - final PNode b = new PNode(); - final PNode c = new PNode(); - final PNode d = new PNode(); - final PNode e = new PNode(); - final PNode f = new PNode(); - - a.translate(100, 100); - a.getFullBoundsReference(); - - a.addChild(b); - b.addChild(c); - c.addChild(d); - d.addChild(e); - e.addChild(f); - - assertNotNull(a.toString()); - } - - public void testRecursiveLayout() { - final PNode layoutNode1 = new PNode() { - /** - * - */ - private static final long serialVersionUID = 1L; - - protected void layoutChildren() { - if (getChildrenCount() > 0) { - getChild(0).setOffset(1, 0); - } - } - }; - - final PNode layoutNode2 = new PNode() { - /** - * - */ - private static final long serialVersionUID = 1L; - - protected void layoutChildren() { - if (getChildrenCount() > 0) { - getChild(0).setOffset(1, 0); - } - } - }; - - layoutNode1.addChild(layoutNode2); - - final PNode n = new PNode(); - n.setBounds(0, 0, 100, 100); - - layoutNode2.addChild(n); - - n.setBounds(10, 10, 100, 100); - - layoutNode1.getFullBoundsReference(); - } - - public void testAnimateToBoundsWithDuration0IsImmediate() { - node.setBounds(0, 0, 100, 100); - - final PActivity activity = node.animateToBounds(50, 50, 150, 150, 0); - assertNull(activity); - - final PBounds resultBounds = node.getBounds(); - assertEquals(50.0, resultBounds.x, 0.001); - assertEquals(50.0, resultBounds.y, 0.001); - assertEquals(150.0, resultBounds.width, 0.001); - assertEquals(150.0, resultBounds.height, 0.001); - } - - public void testAnimateToBoundsHasProperSetup() { - node.setBounds(0, 0, 100, 100); - final PInterpolatingActivity activity = node.animateToBounds(50, 50, 150, 150, 50); - - assertEquals(50, activity.getDuration()); - assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); - assertTrue(activity.getFirstLoop()); - assertFalse(activity.isStepping()); - } - - public void testAnimateTransformToBoundsWithDuration0IsImmediate() { - node.setBounds(0, 0, 100, 100); - final PActivity activity = node.animateTransformToBounds(0, 0, 10, 10, 0); - - assertNull(activity); - - final PAffineTransform transform = node.getTransform(); - assertEquals(0.1, transform.getScale(), 0.0001); - } - - public void testAnimateTransformToBoundsHasProperSetup() { - node.setBounds(0, 0, 100, 100); - final PTransformActivity activity = node.animateTransformToBounds(0, 0, 10, 10, 50); - - assertEquals(50, activity.getDuration()); - assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); - assertTrue(activity.getFirstLoop()); - assertFalse(activity.isStepping()); - - final double[] resultTransform = activity.getDestinationTransform(); - - assertEquals(0.1, resultTransform[0], 0.001); - assertEquals(0, resultTransform[1], 0.001); - assertEquals(0, resultTransform[2], 0.001); - assertEquals(0.1, resultTransform[3], 0.001); - assertEquals(0, resultTransform[4], 0.001); - assertEquals(0, resultTransform[5], 0.001); - } - - public void testAnimateToPositionScaleRotationWithDuration0IsImmediate() { - node.setBounds(0, 0, 100, 100); - final PActivity activity = node.animateToPositionScaleRotation(50, 50, 0.5, Math.PI, 0); - - assertNull(activity); - - final PAffineTransform resultTransform = node.getTransform(); - - final PAffineTransform expected = new PAffineTransform(); - expected.translate(50, 50); - expected.scale(0.5, 0.5); - expected.rotate(Math.PI); - - assertEquals(expected, resultTransform); - } - - public void testAnimateToPositionScaleRotationHasProperSetup() { - node.setBounds(0, 0, 100, 100); - final PTransformActivity activity = node.animateToPositionScaleRotation(50, 50, 0.5, Math.PI, 50); - - assertEquals(50, activity.getDuration()); - assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); - assertTrue(activity.getFirstLoop()); - assertFalse(activity.isStepping()); - - final double[] resultTransform = activity.getDestinationTransform(); - - final PAffineTransform expected = new PAffineTransform(); - expected.translate(50, 50); - expected.scale(0.5, 0.5); - expected.rotate(Math.PI); - - assertEquals(-0.5, resultTransform[0], 0.001); - assertEquals(0, resultTransform[1], 0.001); - assertEquals(0, resultTransform[2], 0.001); - assertEquals(-0.5, resultTransform[3], 0.001); - assertEquals(50.0, resultTransform[4], 0.001); - assertEquals(50.0, resultTransform[5], 0.001); - } - - public void testAnimateToColorWithDuration0IsImmediate() { - node.setPaint(Color.WHITE); - - final PActivity activity = node.animateToColor(Color.BLACK, 0); - - assertNull(activity); - - assertEquals(Color.BLACK, node.getPaint()); - } - - public void testAnimateToColorHasProperSetup() { - node.setPaint(Color.WHITE); - final PInterpolatingActivity activity = node.animateToColor(Color.BLACK, 50); - - assertEquals(50, activity.getDuration()); - assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); - assertTrue(activity.getFirstLoop()); - assertFalse(activity.isStepping()); - // assertEquals(Color.BLACK, activity.getDestinationColor()); - assertEquals("Paint should not change immediately", Color.WHITE, node.getPaint()); - } - - public void testAddActivityAddsActivityToScheduler() { - final PCanvas canvas = new PCanvas(); - node.setPaint(Color.WHITE); - canvas.getLayer().addChild(node); - - final PColorActivity activity = buildTestActivity(); - - node.addActivity(activity); - - assertEquals(1, node.getRoot().getActivityScheduler().getActivitiesReference().size()); - } - - private PColorActivity buildTestActivity() { - final Target testTarget = new PColorActivity.Target() { - - public Color getColor() { - return Color.BLACK; - } - - public void setColor(final Color color) { - - } - }; - - final PColorActivity activity = new PColorActivity(1000, 0, testTarget, Color.BLACK); - return activity; - } - - public void testAnimateToTransparencyWithDuration0IsImmediate() { - node.setPaint(Color.WHITE); - - final PActivity activity = node.animateToTransparency(0.5f, 0); - - assertNull(activity); - - assertEquals(0.5f, node.getTransparency(), 0.0001); - } - - public void testAnimateToTransparencyHasProperSetup() { - final PInterpolatingActivity activity = node.animateToTransparency(0f, 50); - - assertEquals(50, activity.getDuration()); - assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); - assertTrue(activity.getFirstLoop()); - assertFalse(activity.isStepping()); - - assertEquals("Transparency should not change immediately", 1.0f, node.getTransparency(), 0.0001); - } - - public void testGetClientPropertiesShouldReturnSetEvenIfNonePresent() { - final MutableAttributeSet properties = node.getClientProperties(); - assertNotNull(properties); - assertEquals(0, properties.getAttributeCount()); - } - - public void testGetClientPropertiesShouldReturnSameCollectionAlways() { - final MutableAttributeSet properties1 = node.getClientProperties(); - final MutableAttributeSet properties2 = node.getClientProperties(); - assertSame(properties1, properties2); - } - - public void testGetClientPropertyKeysEnumerationShouldReturnEnumarationOnNewNode() { - final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); - assertNotNull(enumeration); - assertFalse(enumeration.hasMoreElements()); - } - - public void testGetClientPropertyKeysEnumerationShouldReturnCorrectEnumWhenPropertiesExist() { - node.addAttribute("Testing", "Hello"); - final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); - assertNotNull(enumeration); - assertTrue(enumeration.hasMoreElements()); - assertEquals("Testing", enumeration.nextElement()); - assertFalse(enumeration.hasMoreElements()); - } - - public void testGetAttributeReturnsNullWhenMissing() { - assertNull(node.getAttribute("Testing")); - } - - public void testGetAttributeReturnsValueWhenPresent() { - node.addAttribute("Testing", "Hello"); - assertEquals("Hello", node.getAttribute("Testing")); - } - - public void testGetAttributeReturnsDefaultWhenProvided() { - assertEquals("Default", node.getAttribute("Missing", "Default")); - } - - public void testGetAttributeReturnsValueIfFoundWhenDefaultProvided() { - node.addAttribute("Found", "Hello"); - assertEquals("Hello", node.getAttribute("Found", "Default")); - } - - public void testGetBooleanAttributeReturnsDefaultWhenProvided() { - assertEquals(false, node.getBooleanAttribute("Missing", false)); - } - - public void testGetBooleanAttributeReturnsValueIfFoundWhenDefaultProvided() { - node.addAttribute("Found", Boolean.TRUE); - assertEquals(true, node.getBooleanAttribute("Found", false)); - } - - public void testGetIntegerAttributeReturnsDefaultWhenProvided() { - assertEquals(10, node.getIntegerAttribute("Missing", 10)); - } - - public void testGetIntegerAttributeReturnsValueIfFoundWhenDefaultProvided() { - node.addAttribute("Found", new Integer(5)); - assertEquals(5, node.getIntegerAttribute("Found", 10)); - } - - public void testGetDoubleAttributeReturnsDefaultWhenProvided() { - assertEquals(10, node.getDoubleAttribute("Missing", 10), 0.001); - } - - public void testGetDoubleAttributeReturnsValueIfFoundWhenDefaultProvided() { - node.addAttribute("Found", new Double(5)); - assertEquals(5, node.getIntegerAttribute("Found", 10), 0.001); - } - - public void testLocalToParentModifiesGivenPoint() { - final PNode parent = new PNode(); - parent.addChild(node); - - node.scale(0.5); - - final Point2D point = new Point2D.Double(5, 6); - node.localToParent(point); - assertTrue(5 != point.getX()); - assertTrue(6 != point.getY()); - } - - public void testLocalToParentDoesWorkWithOrphanChildWhenTransformed() { - node.scale(0.5); - - final Point2D point = new Point2D.Double(5, 6); - node.localToParent(point); - assertTrue(5 != point.getX()); - assertTrue(6 != point.getY()); - } - - public void testLocalToParentDoesNothingWithOrphanChildWhenNotTransformed() { - final Point2D point = new Point2D.Double(5, 6); - node.localToParent(point); - assertEquals(5, point.getX(), 0.0001); - assertEquals(6, point.getY(), 0.0001); - } - - public void testParentToLocalModifiesGivenPoint() { - final PNode parent = new PNode(); - parent.addChild(node); - - node.scale(0.5); - - final Point2D point = new Point2D.Double(5, 6); - node.parentToLocal(point); - assertTrue(5 != point.getX()); - assertTrue(6 != point.getY()); - } - - public void testParentToLocalTransformsOrphanChildWhenTransformed() { - final PNode aChild = new PNode(); - aChild.scale(0.5); - - final Point2D point = new Point2D.Double(5, 6); - aChild.parentToLocal(point); - assertEquals(10, point.getX(), 0.0001); - assertEquals(12, point.getY(), 0.0001); - } - - public void testGlobalToLocalWorksUnTransformedNodes() { - final PNode parent = new PNode(); - parent.addChild(node); - - final Point2D point = new Point2D.Double(10, 11); - node.globalToLocal(point); - assertEquals(10, point.getX(), 0.0001); - assertEquals(11, point.getY(), 0.0001); - } - - public void testRemoveEventListener() { - final PBasicInputEventHandler eventListener = new PBasicInputEventHandler(); - node.addInputEventListener(eventListener); - assertEquals(1, node.getListenerList().getListenerCount()); - node.removeInputEventListener(eventListener); - assertNull(node.getListenerList()); - - } - - public void testAddPropertyChangeListener() { - node.addPropertyChangeListener(mockListener); - node.setBounds(0, 0, 100, 100); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testAddPropertyChangeListenerForPropertyName() { - node.addPropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); - node.setBounds(0, 0, 100, 100); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testRemovePropertyChangeListener() { - node.addPropertyChangeListener(mockListener); - node.removePropertyChangeListener(mockListener); - node.setBounds(0, 0, 100, 100); - assertEquals(0, mockListener.getPropertyChangeCount()); - } - - public void testRemovePropertyChangeListenerForPropertyName() { - node.addPropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); - node.removePropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); - node.setBounds(0, 0, 100, 100); - assertEquals(0, mockListener.getPropertyChangeCount()); - } - - public void testPropertyChangesCascadeToParent() { - final PNode aParent = new PNode(); - aParent.addPropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); - - final PNode aChild = new PNode(); - aChild.setPropertyChangeParentMask(PNode.PROPERTY_CODE_BOUNDS); - aParent.addChild(aChild); - - aChild.setBounds(0, 0, 100, 100); - assertEquals(1, mockListener.getPropertyChangeCount()); - final PropertyChangeEvent propEvent = mockListener.getPropertyChange(0); - assertEquals(PNode.PROPERTY_BOUNDS, propEvent.getPropertyName()); - assertEquals(new PBounds(0, 0, 100, 100), propEvent.getNewValue()); - } - - public void testStartEndResizeBoundsCanBeCalledWithoutResizes() { - node.startResizeBounds(); - node.endResizeBounds(); - } - - public void testSetXModifiesBounds() { - node.setX(10); - assertEquals(10, node.getBounds().getX(), 0.0001); - } - - public void testSetYModifiesBounds() { - node.setY(10); - assertEquals(10, node.getBounds().getY(), 0.0001); - } - - public void testSetHeightModifiesBounds() { - node.setHeight(10); - assertEquals(10, node.getBounds().getHeight(), 0.0001); - } - - public void testSetWidthModifiesBounds() { - node.setWidth(10); - assertEquals(10, node.getBounds().getWidth(), 0.0001); - } - - public void testResetBoundsDoesSo() { - node.setBounds(10, 15, 100, 115); - node.resetBounds(); - - final PBounds zeroBounds = new PBounds(); - assertEquals(zeroBounds, node.getBounds()); - } - - public void testCenterBoundsOnPointWorksAsExpected() { - node.setBounds(0, 0, 100, 100); - node.centerBoundsOnPoint(0, 0); - - final PBounds expected = new PBounds(-50, -50, 100, 100); - assertEquals(expected, node.getBounds()); - } - - public void testCenterFullBoundsOnPointWorksAsExpected() { - final PNode aParent = buildComplexSquareNode(); - - aParent.centerFullBoundsOnPoint(0, 0); - - final PBounds expected = new PBounds(-50, -50, 100, 100); - assertEquals(expected, aParent.getFullBounds()); - } - - private PNode buildComplexSquareNode() { - final PNode aParent = new PNode(); - aParent.setBounds(0, 0, 50, 100); - - final PNode child1 = new PNode(); - child1.setBounds(50, 0, 50, 50); - aParent.addChild(child1); - - final PNode child2 = new PNode(); - child2.setBounds(50, 50, 50, 50); - aParent.addChild(child2); - - return aParent; - } - - public void testGetUnionOfChildrenBoundsAcceptsNull() { - final PNode node = buildComplexSquareNode(); - - final PBounds union = node.getUnionOfChildrenBounds(null); - - assertNotNull(union); - assertEquals(new PBounds(50, 0, 50, 100), union); - } - - public void testGetGlobalFullBoundsIsSameWhenNoTransforms() { - final PNode parent = new PNode(); - final PNode child = new PNode(); - parent.addChild(child); - final PNode grandChild = new PNode(); - child.addChild(grandChild); - child.setBounds(50, 0, 50, 50); - grandChild.setBounds(0, 50, 50, 50); - - final PBounds globalFullBounds = parent.getGlobalFullBounds(); - - assertNotNull(globalFullBounds); - assertEquals(new PBounds(0, 0, 100, 100), globalFullBounds); - } - - public void testChildBoundsStayVolatile() { - node.setChildBoundsVolatile(true); - assertTrue(node.getChildBoundsVolatile()); - } - - public void testRotatingChangesRotation() { - node.rotate(Math.PI); - assertEquals(Math.PI, node.getRotation(), 0.0001); - } - - public void testSetRotationIsNotCummulative() { - node.setRotation(Math.PI); - node.setRotation(Math.PI); - assertEquals(Math.PI, node.getRotation(), 0.0001); - } - - public void testRotateAboutPointDoesNotAffectBounds() { - node.setBounds(25, 25, 50, 50); - node.rotateAboutPoint(Math.PI, 50, 25); // It's top center point - assertEquals(new PBounds(25, 25, 50, 50), node.getBounds()); - } - - public void testRotateAboutPointVersion1AffectsTransformAsItShould() { - node.setBounds(25, 25, 50, 50); - node.rotateAboutPoint(Math.PI, 50, 0); // It's top center point - - final PAffineTransform expectedTransform = new PAffineTransform(); - expectedTransform.translate(100, 0); - expectedTransform.rotate(Math.PI); - - assertEquals(expectedTransform, node.getTransform()); - } - - public void testRotateAboutPointVersion2AffectsTransformAsItShould() { - node.setBounds(25, 25, 50, 50); - node.rotateAboutPoint(Math.PI, new Point2D.Double(50, 0)); // It's top - // center - // point - - final PAffineTransform expectedTransform = new PAffineTransform(); - expectedTransform.translate(100, 0); - expectedTransform.rotate(Math.PI); - - assertEquals(expectedTransform, node.getTransform()); - } - - public void testScaleAboutPointWorksAsExpected() { - node.setBounds(0, 0, 100, 100); - node.scaleAboutPoint(2, new Point2D.Double(50, 50)); - final PAffineTransform expectedTransform = new PAffineTransform(); - expectedTransform.translate(-50, -50); - expectedTransform.scale(2, 2); - - assertEquals(expectedTransform, node.getTransform()); - } - - public void testRotateInPlaneLeavesFullBoundsUntouched() { - node.setBounds(25, 25, 50, 50); - final PBounds boundsBefore = node.getFullBounds(); - - node.rotateInPlace(Math.PI); - assertEquals(boundsBefore, node.getFullBounds()); - } - - public void testSetGlobalScaleTakesParentsScaleIntoAccount() { - final PNode aParent = new PNode(); - aParent.scale(2); - - final PNode aChild = new PNode(); - aParent.addChild(aChild); - - aChild.setGlobalScale(1); - - assertEquals(0.5, aChild.getScale(), 0.0001); - } - - public void testOffsetDoesNotTakeBoundsIntoAccount() { - node.setOffset(10, 20); - node.setBounds(50, 50, 100, 100); - assertEquals(10, node.getXOffset(), 0.001); - assertEquals(20, node.getYOffset(), 0.001); - } - - public void testTransformByIsCummulative() { - node.transformBy(AffineTransform.getScaleInstance(2, 2)); - node.transformBy(AffineTransform.getScaleInstance(2, 2)); - - assertEquals(AffineTransform.getScaleInstance(4, 4), node.getTransform()); - } - - public void testLerp() { - assertEquals(5, PNode.lerp(0.5, 0, 10), 0.001); - assertEquals(0, PNode.lerp(0, 0, 10), 0.001); - assertEquals(10, PNode.lerp(1, 0, 10), 0.001); - } - - public void testAnimateToRelativePositionResultsInProperTransform() { - final PCanvas canvas = new PCanvas(); - final PNode A = new PNode(); - A.setBounds(0, 0, 50, 50); - canvas.getLayer().addChild(A); - final PNode B = new PNode(); - B.setBounds(0, 0, 100, 100); - B.setOffset(100, 100); - canvas.getLayer().addChild(B); - - final Point2D srcPt = new Point2D.Double(1.0, 0.0); - final Point2D destPt = new Point2D.Double(0.0, 0.0); - A.animateToRelativePosition(srcPt, destPt, B.getGlobalBounds(), 0); - - final PAffineTransform expectedTransform = new PAffineTransform(); - expectedTransform.translate(50, 100); - - assertEquals(expectedTransform, A.getTransform()); - } - - public void testGetInverseTransformWorks() { - node.translate(50, 50); - node.rotate(Math.PI); - - final PAffineTransform expectedTransform = new PAffineTransform(); - expectedTransform.rotate(-Math.PI); - expectedTransform.translate(-50, -50); - assertEquals(expectedTransform, node.getInverseTransform()); - } - - public void testGetInverseTransformThrowsExceptionWhenTransformIsNotInvertible() { - node.setTransform(new AffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); - - try { - node.getInverseTransform(); - fail("Exception not thrown"); - } - catch (final PAffineTransformException e) { - // expected - } - } - - public void testSetVisibleIsRespectedOnPaint() { - final int[] paintCounts = new int[1]; - - final PNode node = new PNode() { - /** - * - */ - private static final long serialVersionUID = 1L; - - public void paint(final PPaintContext pc) { - paintCounts[0]++; - } - }; - node.setBounds(0, 0, 100, 100); - node.setVisible(true); - - final PCanvas canvas = buildCanvasContainingNode(node); - - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final Graphics g = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); - - canvas.paintComponent(g); - - assertEquals(1, paintCounts[0]); - - node.setVisible(false); - node.invalidatePaint(); - canvas.paintComponent(g); - assertEquals(1, paintCounts[0]); - - node.setVisible(true); - node.invalidatePaint(); - canvas.paintComponent(g); - assertEquals(2, paintCounts[0]); - } - - public void testSetTransparency1MeansInvisible() { - final PNode node = new PNode(); - node.setBounds(0, 0, 100, 100); - node.setVisible(true); - node.setPaint(Color.RED); - - final PCanvas canvas = buildCanvasContainingNode(node); - - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final Graphics g = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); - - canvas.paintComponent(g); - node.setTransparency(1f); - assertEquals(Color.RED.getRGB(), img.getRGB(10, 10)); - - node.setTransparency(0f); - canvas.paintComponent(g); - assertEquals(Color.WHITE.getRGB(), img.getRGB(10, 10)); - - } - - private PCanvas buildCanvasContainingNode(final PNode node) { - final PCanvas canvas = new PCanvas(); - canvas.setSize(100, 100); - canvas.getLayer().addChild(node); - return canvas; - } - - public void testPaintColourIsRespectedOnPaint() { - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final Graphics g = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); - - node.setPaint(Color.RED); - node.setBounds(0, 0, 100, 100); - - final PCanvas canvas = buildCanvasContainingNode(node); - canvas.paintComponent(g); - - assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); - } - - public void testToImageReturnsValidImage() { - node.setBounds(0, 0, 10, 10); - node.setPaint(Color.RED); - - // Really don't like casting here, but... without changing the - // interface, I don't see a choice - final BufferedImage img = (BufferedImage) node.toImage(); - - assertEquals(10, img.getHeight(null)); - assertEquals(10, img.getWidth(null)); - assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); - assertEquals(Color.RED.getRGB(), img.getRGB(9, 0)); - assertEquals(Color.RED.getRGB(), img.getRGB(0, 9)); - assertEquals(Color.RED.getRGB(), img.getRGB(9, 9)); - } - - public void testToImageUsesFullBoundsWhenConvertingImage() throws IOException { - node.setBounds(0, 0, 50, 50); - PNode child1 = new PNode(); - child1.setBounds(0, 0, 100, 50); - child1.setPaint(Color.RED); - node.addChild(child1); - - PNode child2 = new PNode(); - child2.setBounds(0, 0, 50, 100); - child2.setPaint(Color.BLUE); - node.addChild(child2); - - BufferedImage image = (BufferedImage) node.toImage(); - assertNotNull(image); - assertEquals(100, image.getWidth()); - assertEquals(100, image.getHeight()); - assertEquals(Color.RED.getRGB(), image.getRGB(99, 1)); - - //This line fails if PNode.toImage uses getWidth() rather than getFullBounds().getWidth() - assertEquals(Color.BLUE.getRGB(), image.getRGB(1, 99)); - } - - public void testToImageWillAcceptBackgroundPaint() { - node.setBounds(0, 0, 10, 10); - - final BufferedImage img = (BufferedImage) node.toImage(10, 10, Color.BLUE); - assertEquals(Color.BLUE.getRGB(), img.getRGB(5, 5)); - } - - public void testToImageResultsInDesiredSizeImage() { - node.setBounds(0, 0, 10, 10); - - final BufferedImage img = (BufferedImage) node.toImage(20, 40, null); - assertEquals(40, img.getHeight(null)); - assertEquals(20, img.getWidth(null)); - } - - public void testToImageWithBackgroundColorGivenReturnsValidImage() { - node.setBounds(0, 0, 10, 10); - node.setPaint(Color.RED); - - final BufferedImage img = (BufferedImage) node.toImage(20, 40, Color.BLUE); - assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); - assertEquals(Color.BLUE.getRGB(), img.getRGB(15, 25)); - } - - public void testToImageScalesNodeAsBigAsCanBe() throws IOException { - node.setBounds(0, 0, 10, 10); - node.setPaint(Color.RED); - - final BufferedImage img = (BufferedImage) node.toImage(20, 40, Color.BLUE); - - assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); - assertEquals(Color.RED.getRGB(), img.getRGB(19, 0)); - assertEquals(Color.RED.getRGB(), img.getRGB(0, 19)); - assertEquals(Color.RED.getRGB(), img.getRGB(19, 19)); - assertEquals(Color.BLUE.getRGB(), img.getRGB(0, 20)); - assertEquals(Color.BLUE.getRGB(), img.getRGB(19, 20)); - } - - public void testToImageScalesAccordingToExactFitStrategy() throws IOException { - node.setBounds(0, 0, 10, 10); - node.setPaint(Color.RED); - - final BufferedImage img = (BufferedImage) node.toImage(new BufferedImage(20, 40, BufferedImage.TYPE_INT_RGB), - Color.BLUE, PNode.FILL_STRATEGY_EXACT_FIT); - - assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); - assertEquals(Color.RED.getRGB(), img.getRGB(19, 0)); - assertEquals(Color.RED.getRGB(), img.getRGB(0, 39)); - assertEquals(Color.RED.getRGB(), img.getRGB(19, 39)); - - } - - 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); - node.addChild(blueSquare); - - PNode greenSquare = new PNode(); - greenSquare.setPaint(Color.GREEN); - 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); - - assertEquals(Color.RED.getRGB(), img.getRGB(11, 19)); - 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(19, 20)); - assertEquals(Color.GREEN.getRGB(), img.getRGB(10, 39)); - assertEquals(Color.GREEN.getRGB(), img.getRGB(19, 39)); - } - - public void testGetPickableShouldDefaultToTrue() { - assertTrue(node.getPickable()); - } - - public void testSetPickableFiresPropertyChange() { - node.addPropertyChangeListener(mockListener); - node.setPickable(false); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testChildrenShouldBePickableByDefault() { - assertTrue(node.getChildrenPickable()); - } - - public void testSetChildrenPickableFiresPropertyChange() { - node.addPropertyChangeListener(mockListener); - node.setChildrenPickable(false); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testByDefaultNodesShouldNotPickThemselvesBeforeTheirChildren() { - final PCanvas canvas = new PCanvas(); - final PPickPath pickPath = new PPickPath(canvas.getCamera(), new PBounds(0, 0, 100, 100)); - assertFalse(node.pick(pickPath)); - } - - public void testfullPickReturnsTrueWhenOverlapsWithChildNode() { - final PCanvas canvas = new PCanvas(); - node.setBounds(0, 0, 10, 10); - - final PNode child = new PNode(); - child.setBounds(20, 0, 10, 10); - node.addChild(child); - - final PPickPath pickPath = new PPickPath(canvas.getCamera(), new PBounds(20, 0, 10, 10)); - canvas.getLayer().addChild(node); - assertTrue(node.fullPick(pickPath)); - } - - public void testfullPickReturnsFalseWhenNotOverlappingWithChildNode() { - final PCanvas canvas = new PCanvas(); - node.setBounds(0, 0, 10, 10); - - final PNode child = new PNode(); - child.setBounds(10, 0, 10, 10); - node.addChild(child); - - final PPickPath pickPath = new PPickPath(canvas.getCamera(), new PBounds(20, 0, 10, 10)); - canvas.getLayer().addChild(node); - assertFalse(node.fullPick(pickPath)); - } - - public void testAddChildrenAddsAllChildren() { - final Collection newChildren = new ArrayList(); - newChildren.add(new PNode()); - newChildren.add(new PNode()); - newChildren.add(new PNode()); - - node.addChildren(newChildren); - - assertEquals(3, node.getChildrenCount()); - } - - public void testRemoveChildrenWorks() { - final Collection newChildren = new ArrayList(); - newChildren.add(new PNode()); - newChildren.add(new PNode()); - newChildren.add(new PNode()); - node.addChildren(newChildren); - node.addChild(new PNode()); - - node.removeChildren(newChildren); - assertEquals(1, node.getChildrenCount()); - } - - public void testGetAllNodesUnrollsTheNodeGraph() { - final Collection newChildren = new ArrayList(); - newChildren.add(new PNode()); - newChildren.add(new PNode()); - newChildren.add(new PNode()); - - node.addChildren(newChildren); - - assertEquals(4, node.getAllNodes().size()); - } - - public void testRemoveAllChildrenDoesntCrashWhenNoChidlren() { - node.removeAllChildren(); - - // And now for the case when there once was a child - node.addChild(new PNode()); - node.removeAllChildren(); - node.removeAllChildren(); - } - - public void testRemoveFromParentDoesSo() { - final PNode parent = new PNode(); - parent.addChild(node); - - node.removeFromParent(); - - assertEquals(0, parent.getChildrenCount()); - } - - public void testReplaceWithSwapsParents() { - final PNode parent = new PNode(); - parent.addChild(node); - - final PNode newNode = new PNode(); - node.replaceWith(newNode); - assertNull(node.getParent()); - - assertEquals(parent, newNode.getParent()); - } - - public void testGetChildrenIteratorReturnsIteratorEvenWithNoChildren() { - final ListIterator iterator = node.getChildrenIterator(); - assertNotNull(iterator); - assertFalse(iterator.hasNext()); - } - - public void testGetChildrenIteratorReturnsValidIteratorWhenHasChildren() { - final PNode child = new PNode(); - node.addChild(child); - - final ListIterator iterator = node.getChildrenIterator(); - assertNotNull(iterator); - assertTrue(iterator.hasNext()); - assertEquals(child, iterator.next()); - assertFalse(iterator.hasNext()); - } - - public void testGetAllNodesDoesntIgnoreFilter() { - final PNodeFilter nullFilter = new PNodeFilter() { - - public boolean accept(final PNode aNode) { - return false; - } - - public boolean acceptChildrenOf(final PNode aNode) { - return true; - } - }; - - node.addChild(new PNode()); - node.addChild(new PNode()); - node.addChild(new PNode()); - final Collection nodes = node.getAllNodes(nullFilter, null); - assertNotNull(nodes); - assertTrue(nodes.isEmpty()); - } - - public void testAncestryMethods() { - final PNode child = new PNode(); - node.addChild(child); - - final PNode grandChild = new PNode(); - child.addChild(grandChild); - - final PNode unrelated = new PNode(); - - assertTrue(node.isAncestorOf(child)); - assertTrue(node.isAncestorOf(grandChild)); - assertTrue(child.isDescendentOf(node)); - assertTrue(grandChild.isDescendentOf(node)); - - assertFalse(node.isAncestorOf(unrelated)); - assertFalse(grandChild.isDescendentOf(unrelated)); - } - - public void testMoveToBackMovesNodeToBeFirstChild() { - final PNode parent = new PNode(); - parent.addChild(new PNode()); - parent.addChild(new PNode()); - parent.addChild(node); - node.moveToBack(); - assertEquals(0, parent.indexOfChild(node)); - } - - public void testMoveToFrontMovesNodeToBeLastChild() { - final PNode parent = new PNode(); - parent.addChild(node); - parent.addChild(new PNode()); - parent.addChild(new PNode()); - node.moveToFront(); - assertEquals(2, parent.indexOfChild(node)); - } - - public void testMoveInBackOfMovesNodeToBeforeSibling() { - final PNode parent = new PNode(); - final PNode sibling = new PNode(); - - parent.addChild(node); - parent.addChild(new PNode()); - parent.addChild(new PNode()); - parent.addChild(sibling); - - node.moveInBackOf(sibling); - assertEquals(2, parent.indexOfChild(node)); - } - - public void testMoveInFrontOfMovesNodeToAfterSibling() { - final PNode parent = new PNode(); - final PNode sibling = new PNode(); - - parent.addChild(node); - parent.addChild(new PNode()); - parent.addChild(new PNode()); - parent.addChild(sibling); - - node.moveInFrontOf(sibling); - assertEquals(3, parent.indexOfChild(node)); - } - - public void testMoveInFrontOfDoesNothingIfNotSibling() { - final PNode parent = new PNode(); - final PNode stranger = new PNode(); - - parent.addChild(node); - parent.addChild(new PNode()); - parent.addChild(new PNode()); - - node.moveInFrontOf(stranger); - assertEquals(0, parent.indexOfChild(node)); - } - - public void testMoveInBackOfDoesNothingIfNotSibling() { - final PNode parent = new PNode(); - final PNode stranger = new PNode(); - - parent.addChild(node); - parent.addChild(new PNode()); - parent.addChild(new PNode()); - - node.moveInBackOf(stranger); - assertEquals(0, parent.indexOfChild(node)); - } - - public void testIsDescendentOfRootHandlesOrphans() { - final PNode orphan = new PNode(); - - assertFalse(orphan.isDescendentOfRoot()); - orphan.addChild(node); - assertFalse(node.isDescendentOfRoot()); - } - - public void testIsDescendentOfRootHandlesDescendentsOfRoot() { - final PCanvas canvas = new PCanvas(); - canvas.getLayer().addChild(node); - - assertTrue(node.isDescendentOfRoot()); - } - - public void testGetGlobalRationTakesParentsIntoAccount() { - final PNode parent = new PNode(); - parent.rotate(Math.PI / 4d); - parent.addChild(node); - - node.rotate(Math.PI / 4d); - - assertEquals(Math.PI / 2d, node.getGlobalRotation(), 0.001); - } - - public void testSetGlobalRationTakesParentsIntoAccount() { - final PNode parent = new PNode(); - parent.rotate(Math.PI / 4d); - parent.addChild(node); - - node.setGlobalRotation(Math.PI / 2d); - - assertEquals(Math.PI / 4d, node.getRotation(), 0.001); - } - - public void testSetGlobalRationWorksWhenNoParent() { - node.setGlobalRotation(Math.PI / 2d); - - assertEquals(Math.PI / 2d, node.getRotation(), 0.001); - } - - public void testSetOccludedPersistes() { - node.setOccluded(true); - assertTrue(node.getOccluded()); - } - - public void testHiddenNodesAreNotPickable() { - final PCanvas canvas = new PCanvas(); - canvas.setBounds(0, 0, 400, 400); - canvas.setPreferredSize(new Dimension(400, 400)); - final PNode node1 = new PNode(); - node1.setBounds(0, 0, 100, 100); - node1.setPaint(Color.RED); - canvas.getLayer().addChild(node1); - - final PNode node2 = (PNode) node1.clone(); - node2.setPaint(Color.BLUE); - - final PLayer layer2 = new PLayer(); - layer2.addChild(node2); - layer2.setVisible(false); - canvas.getCamera().addLayer(layer2); - - final PPickPath path = canvas.getCamera().pick(5, 5, 5); - assertSame(node1, path.getPickedNode()); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/POffscreenCanvasTest.java b/core/src/test/java/edu/umd/cs/piccolo/POffscreenCanvasTest.java deleted file mode 100644 index 8528dd2..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/POffscreenCanvasTest.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; - -/** - * Unit test for POffscreenCanvas. - */ -public class POffscreenCanvasTest extends TestCase { - - public void testConstructor() { - final POffscreenCanvas canvas0 = new POffscreenCanvas(100, 100); - assertNotNull(canvas0); - final POffscreenCanvas canvas1 = new POffscreenCanvas(0, 0); - assertNotNull(canvas1); - final POffscreenCanvas canvas2 = new POffscreenCanvas(0, 100); - assertNotNull(canvas2); - final POffscreenCanvas canvas3 = new POffscreenCanvas(100, 0); - assertNotNull(canvas3); - - try { - new POffscreenCanvas(-1, 100); - fail("ctr(-1, 100) expected IllegalArgumentException"); - } - catch (final IllegalArgumentException e) { - // expected - } - try { - new POffscreenCanvas(100, -1); - fail("ctr(100, -1) expected IllegalArgumentException"); - } - catch (final IllegalArgumentException e) { - // expected - } - try { - new POffscreenCanvas(-1, -1); - fail("ctr(-1, -1) expected IllegalArgumentException"); - } - catch (final IllegalArgumentException e) { - // expected - } - } - - public void testCamera() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - assertNotNull(canvas); - final PCamera camera = canvas.getCamera(); - assertNotNull(camera); - assertEquals(canvas, camera.getComponent()); - final PCamera camera1 = new PCamera(); - canvas.setCamera(camera1); - assertEquals(camera1, canvas.getCamera()); - assertEquals(null, camera.getComponent()); - assertEquals(canvas, camera1.getComponent()); - canvas.setCamera(null); - assertEquals(null, camera1.getComponent()); - assertEquals(null, canvas.getCamera()); - } - - public void testRenderEmpty() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); - final Graphics2D graphics = image.createGraphics(); - canvas.render(graphics); - for (int x = 0; x < 100; x++) { - for (int y = 0; y < 200; y++) { - assertEquals(0, image.getRGB(x, y)); - } - } - } - - public void testRenderEmptyOpaqueNullBackgroundColor() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); - final Graphics2D graphics = image.createGraphics(); - canvas.setOpaque(true); - canvas.render(graphics); - for (int x = 0; x < 100; x++) { - for (int y = 0; y < 200; y++) { - assertEquals(0, image.getRGB(x, y)); - } - } - } - - public void testRenderEmptyOpaque() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); - final Graphics2D graphics = image.createGraphics(); - canvas.setOpaque(true); - canvas.setBackground(Color.RED); - canvas.render(graphics); - for (int x = 0; x < 100; x++) { - for (int y = 0; y < 200; y++) { - // red pixel, RGBA is 255, 0, 0, 255 - assertEquals(-65536, image.getRGB(x, y)); - } - } - } - - public void testRenderFull() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - final PPath rect = PPath.createRectangle(0.0f, 0.0f, 200.0f, 300.0f); - rect.setPaint(new Color(255, 0, 0)); - rect.setStroke(null); - rect.offset(-100.0d, -100.0d); - canvas.getCamera().getLayer(0).addChild(rect); - final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); - final Graphics2D graphics = image.createGraphics(); - canvas.render(graphics); - for (int x = 0; x < 100; x++) { - for (int y = 0; y < 200; y++) { - // red pixel, RGBA is 255, 0, 0, 255 - assertEquals(-65536, image.getRGB(x, y)); - } - } - } - - public void testRenderNull() { - try { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - canvas.render(null); - fail("render(null) expected IllegalArgumentException"); - } - catch (final IllegalArgumentException e) { - // expected - } - } - - public void testRenderQuality() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - assertEquals(POffscreenCanvas.DEFAULT_RENDER_QUALITY, canvas.getRenderQuality()); - canvas.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); - assertEquals(PPaintContext.HIGH_QUALITY_RENDERING, canvas.getRenderQuality()); - canvas.setRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); - assertEquals(PPaintContext.LOW_QUALITY_RENDERING, canvas.getRenderQuality()); - - try { - canvas.setRenderQuality(-1); - } - catch (final IllegalArgumentException e) { - // expected - } - } - - public void testRepaint() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - canvas.repaint(null); - canvas.repaint(new PBounds(0.0, 0.0, 50.0, 50.0)); - } - - public void testPaintImmediately() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - canvas.paintImmediately(); - } - - public void testPopCursor() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - canvas.popCursor(); - } - - public void testPushCursor() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - canvas.pushCursor(null); - canvas.pushCursor(Cursor.getDefaultCursor()); - } - - public void testInteracting() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - canvas.setInteracting(true); - canvas.setInteracting(false); - } - - public void testRoot() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - assertNotNull(canvas.getRoot()); - } - - public void testRootIsNullWhenCameraIsNull() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - canvas.setCamera(null); - assertEquals(null, canvas.getRoot()); - } - - public void testOpaque() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - assertFalse(canvas.isOpaque()); - canvas.setOpaque(true); - assertTrue(canvas.isOpaque()); - } - - public void testBackground() { - final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); - assertEquals(null, canvas.getBackground()); - canvas.setBackground(Color.RED); - assertEquals(Color.RED, canvas.getBackground()); - } -} \ No newline at end of file diff --git a/core/src/test/java/edu/umd/cs/piccolo/PRootTest.java b/core/src/test/java/edu/umd/cs/piccolo/PRootTest.java deleted file mode 100644 index 207c8b7..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PRootTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.Timer; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.activities.PActivity; - -/** - * Unit test for PRoot. - */ -public class PRootTest extends TestCase { - private PRoot root; - private MockPropertyChangeListener mockListener; - - public void setUp() { - root = new PRoot(); - mockListener = new MockPropertyChangeListener(); - } - - public void testActivityScheduleIsNotNullByDefault() { - assertNotNull(root.getActivityScheduler()); - } - - public void testGetRootReturnsItself() { - assertSame(root, root.getRoot()); - } - - public void testGetDefaultInputManagerIsNotNullByDefault() { - assertNotNull(root.getDefaultInputManager()); - } - - public void testAddInputSourceFirePropertyChangeEvent() { - root.addPropertyChangeListener(PRoot.PROPERTY_INPUT_SOURCES, mockListener); - - final PRoot.InputSource newSource = new PRoot.InputSource() { - public void processInput() { - - } - }; - root.addInputSource(newSource); - - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testCreateTimerReturnsATimer() { - final Timer timer = root.createTimer(1, new ActionListener() { - public void actionPerformed(final ActionEvent arg0) { - } - }); - assertNotNull(timer); - } - - public void testCreateTimerReturnsATimerWhenDelayIs0() { - final Timer timer = root.createTimer(0, new ActionListener() { - public void actionPerformed(final ActionEvent arg0) { - } - }); - assertNotNull(timer); - } - - public void testRemoveInputSourceDoesNothingIfStranger() { - final PRoot.InputSource strangeSource = new PRoot.InputSource() { - public void processInput() { - - } - }; - - root.removeInputSource(strangeSource); - } - - public void testGlobalTimeIsNotZeroBeforeCallToProcessInputs() { - assertFalse(0 == root.getGlobalTime()); - } - - public void testProcessInputDelegatesToInputSources() { - final MockInputSource newSource = new MockInputSource(); - root.addInputSource(newSource); - root.processInputs(); - assertEquals(1, newSource.getProcessInputCalls()); - } - - public void testProcessInputProcessesActivities() { - final MockPActivity activity = new MockPActivity(100); - root.addActivity(activity); - root.processInputs(); - assertTrue(activity.isActivityStarted()); - - } - - public void testSetFullBoundsInvalidPerists() { - root.setFullBoundsInvalid(true); - assertTrue(root.getFullBoundsInvalid()); - } - - public void testSetChildBoundsInvalidPerists() { - root.setChildBoundsInvalid(true); - assertTrue(root.getChildBoundsInvalid()); - } - - public void testSetPaintInvalidPersists() { - root.setPaintInvalid(true); - assertTrue(root.getPaintInvalid()); - } - - public void testSetChildPaintInvalidPersists() { - root.setChildPaintInvalid(true); - assertTrue(root.getChildPaintInvalid()); - } - - public void testWaitForActivitiesDoesSo() { - final MockPActivity activity = new MockPActivity(1); - root.addActivity(activity); - root.waitForActivities(); - assertTrue(activity.isActivityFished()); - } - - private static final class MockInputSource implements PRoot.InputSource { - private int processInputCalls; - - public int getProcessInputCalls() { - return processInputCalls; - } - - public void processInput() { - processInputCalls++; - } - } - - private static final class MockPActivity extends PActivity { - private boolean activityStarted; - private boolean activityFinished; - - private MockPActivity(final long aDuration) { - super(aDuration); - } - - public boolean isActivityFished() { - return activityFinished; - } - - public boolean isActivityStarted() { - return activityStarted; - } - - protected void activityStarted() { - activityStarted = true; - super.activityStarted(); - } - - protected void activityFinished() { - activityFinished = true; - super.activityFinished(); - } - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/PerformanceLog.java b/core/src/test/java/edu/umd/cs/piccolo/PerformanceLog.java deleted file mode 100644 index 8ecbb01..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PerformanceLog.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.util.ArrayList; -import java.util.Iterator; - -/** - * Performance log. - */ -public class PerformanceLog { - - private final ArrayList log = new ArrayList(); - private long testTime; - - - // heh, something tells me this was copied from Jazz :) - public static class ZLogEntry { - public String name; - public long time; - - public ZLogEntry(final String aName, final long aTime) { - name = aName; - time = aTime; - } - } - - public void startTest() { - Runtime.getRuntime().gc(); - testTime = System.currentTimeMillis(); - } - - public void endTest(final String name) { - testTime = System.currentTimeMillis() - testTime; - addEntry(name, testTime); - System.gc(); - } - - public void addEntry(final String aName, final long aTime) { - log.add(new ZLogEntry(aName, aTime)); - } - - public void clear() { - log.clear(); - } - - public void writeLog() { - - System.out.println(); - System.out.println("Test data for input into spreadsheet:"); - System.out.println(); - - Iterator i = log.iterator(); - while (i.hasNext()) { - final ZLogEntry each = (ZLogEntry) i.next(); - System.out.println(each.time); - } - - System.out.println(); - System.out.println("Labled test results, see above for simple column \n of times for input into spreadsheet:"); - System.out.println(); - - i = log.iterator(); - while (i.hasNext()) { - final ZLogEntry each = (ZLogEntry) i.next(); - System.out.println(each.name + ", " + each.time); - } - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/PerformanceTests.java b/core/src/test/java/edu/umd/cs/piccolo/PerformanceTests.java deleted file mode 100644 index af661de..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PerformanceTests.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.awt.Graphics2D; -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsEnvironment; -import java.awt.Transparency; -import java.awt.geom.AffineTransform; -import java.awt.geom.GeneralPath; -import java.awt.geom.NoninvertibleTransformException; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.Random; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PBounds; - -/** - * Performance tests. - */ -public class PerformanceTests extends TestCase { - - private static PerformanceLog log = new PerformanceLog(); - private static int NUMBER_NODES = 20000; - - public PerformanceTests(final String name) { - super(name); - } - - public void testRunPerformanceTests() { - if (1 == 1) { - return; - } - - // three times to warm up JVM - for (int i = 0; i < 3; i++) { - addNodes(); - copyNodes(); - createNodes(); - createPaths(); - fullIntersectsNodes(); - memorySizeOfNodes(); - // removeNodes(); - translateNodes(); - costOfNoBoundsCache(); - // renderSpeed(); - if (i != 2) { - log.clear(); - } - } - log.writeLog(); - } - - public void createNodes() { - final PNode[] nodes = new PNode[NUMBER_NODES]; - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - } - log.endTest("Create " + NUMBER_NODES + " new nodes"); - } - - public void createPaths() { - final PNode[] nodes = new PNode[NUMBER_NODES]; - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = PPath.createRectangle(0, 0, 100, 80); - } - log.endTest("Create " + NUMBER_NODES + " new rect paths"); - - final Random r = new Random(); - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i].translate(r.nextFloat() * 300, r.nextFloat() * 300); - } - } - - public void addNodes() { - final PNode parent = new PNode(); - final PNode[] nodes = new PNode[NUMBER_NODES]; - - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - } - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - parent.addChild(nodes[i]); - } - log.endTest("Add " + NUMBER_NODES + " nodes to a new parent"); - } - - public void removeNodes() { - final PNode parent = new PNode(); - final PNode[] nodes = new PNode[NUMBER_NODES]; - final ArrayList list = new ArrayList(); - - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - } - - for (int i = 0; i < NUMBER_NODES; i++) { - parent.addChild(nodes[i]); - list.add(nodes[i]); - } - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - parent.removeChild(nodes[i]); - } - log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() front to back"); - - parent.addChildren(list); - - log.startTest(); - for (int i = NUMBER_NODES - 1; i >= 0; i--) { - parent.removeChild(i); - } - log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() back to front by index"); - - log.startTest(); - // for (int i = NUMBER_NODES - 1; i >= 0; i--) { - // parent.removeChild(nodes[i]); - // } - log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() back to front by object, TO_SLOW"); - - parent.addChildren(list); - - log.startTest(); - parent.removeChildren(list); - log.endTest("Remove " + NUMBER_NODES + " nodes using removeChildren()"); - - parent.addChildren(list); - - log.startTest(); - parent.removeAllChildren(); - log.endTest("Remove " + NUMBER_NODES + " nodes using removeAllChildren()"); - } - - public void translateNodes() { - final PNode parent = new PNode(); - final PNode[] nodes = new PNode[NUMBER_NODES]; - final PBounds b = new PBounds(); - final Random r = new Random(); - - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - nodes[i].setBounds(1000 * r.nextFloat(), 1000 * r.nextFloat(), 100, 80); - parent.addChild(nodes[i]); - nodes[i].getFullBoundsReference(); - } - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i].translate(1000 * r.nextFloat(), 1000 * r.nextFloat()); - nodes[i].scale(1000 * r.nextFloat()); - // nodes[i].translateBy(100.01, 100.2); - // nodes[i].scaleBy(0.9); - } - log.endTest("Translate " + NUMBER_NODES + " nodes, not counting repaint or validate layout"); - - log.startTest(); - // parent.validateFullBounds(); now protected. - parent.getFullBoundsReference(); // calls validateFullBounds as a side - // effect. - log.endTest("Validate Layout after translate " + NUMBER_NODES + " nodes"); - - log.startTest(); - parent.validateFullPaint(); - log.endTest("Validate Paint after translate " + NUMBER_NODES + " nodes"); - - log.startTest(); - parent.computeFullBounds(b); - log.endTest("Parent compute bounds of " + NUMBER_NODES + " children nodes"); - } - - public void fullIntersectsNodes() { - final PNode parent = new PNode(); - final PNode[] nodes = new PNode[NUMBER_NODES]; - final PBounds b = new PBounds(0, 50, 100, 20); - - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - parent.addChild(nodes[i]); - } - - // parent.validateFullBounds(); // now protected - parent.getFullBoundsReference(); // calls validateFullBounds as a side - // effect. - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i].fullIntersects(b); - } - log.endTest("Do fullIntersects test for " + NUMBER_NODES + " nodes"); - } - - public void memorySizeOfNodes() { - final PNode[] nodes = new PNode[NUMBER_NODES]; - Runtime.getRuntime().gc(); - final long startTotalMemory = Runtime.getRuntime().totalMemory(); - final long startFree = Runtime.getRuntime().freeMemory(); - long endFree; - long endTotal; - - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - } - - Runtime.getRuntime().gc(); - endFree = Runtime.getRuntime().freeMemory(); - endTotal = Runtime.getRuntime().totalMemory(); - - log.addEntry("Approximate k used by " + NUMBER_NODES + " nodes", - (endTotal - startTotalMemory + startFree - endFree) / 1024); - nodes[0].getPaint(); - } - - public void copyNodes() { - final PNode parent = new PNode(); - final PNode[] nodes = new PNode[NUMBER_NODES]; - - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - parent.addChild(nodes[i]); - } - - log.startTest(); - parent.clone(); - log.endTest("Copy/Serialize " + NUMBER_NODES + " nodes"); - } - - public void costOfNoBoundsCache() { - final PNode[] nodes = new PNode[NUMBER_NODES]; - final PBounds[] bounds = new PBounds[NUMBER_NODES]; - final PBounds pickRect = new PBounds(0, 0, 1, 1); - final Random r = new Random(); - - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i] = new PNode(); - nodes[i].translate(1000 * r.nextFloat(), 1000 * r.nextFloat()); - nodes[i].scale(1000 * r.nextFloat()); - bounds[i] = new PBounds(1000 * r.nextFloat(), 1000 * r.nextFloat(), 100, 80); - } - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - bounds[i].intersects(pickRect); - } - log.endTest("Do intersects test for " + NUMBER_NODES + " bounds"); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - nodes[i].localToParent(bounds[i]); - } - log.endTest("Transform " + NUMBER_NODES + " bounds from local to parent"); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - pickRect.add(bounds[i]); - } - log.endTest("Sum " + NUMBER_NODES + " bounds"); - - final PBounds b = new PBounds(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble()); - log.startTest(); - for (int i = 0; i < NUMBER_NODES * 10; i++) { - b.clone(); - } - log.endTest("Clone " + NUMBER_NODES * 10 + " PBounds"); - - } - - public void renderSpeed() throws NoninvertibleTransformException { - final Random r = new Random(); - final PAffineTransform at = new PAffineTransform(); - at.setScale(r.nextFloat()); - at.translate(r.nextFloat(), r.nextFloat()); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - at.createInverse(); - } - log.endTest("Create inverse transform " + NUMBER_NODES + " times"); - - final int height = 400; - final int width = 400; - - final double scale1 = 0.5; - final double scale2 = 2; - boolean scaleFlip = true; - - final PAffineTransform transorm1 = new PAffineTransform(); - // transorm1.scale(0.5, 0.5); - transorm1.translate(0.5, 10.1); - PAffineTransform transorm2 = null; - - transorm2 = new PAffineTransform(transorm1.createInverse()); - - final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment() - .getDefaultScreenDevice().getDefaultConfiguration(); - final BufferedImage result = graphicsConfiguration.createCompatibleImage(width, height, - Transparency.TRANSLUCENT); - final Graphics2D g2 = result.createGraphics(); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - if (scaleFlip) { - g2.scale(scale2, scale2); - scaleFlip = !scaleFlip; - } - else { - g2.scale(scale1, scale1); - scaleFlip = !scaleFlip; - } - } - log.endTest("Scale graphics context " + NUMBER_NODES + " times"); - - g2.setTransform(new AffineTransform()); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - g2.translate(0.5, 0.5); - } - log.endTest("Translate graphics context " + NUMBER_NODES + " times"); - - g2.setTransform(new AffineTransform()); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - if (scaleFlip) { - g2.transform(transorm1); - scaleFlip = !scaleFlip; - } - else { - g2.transform(transorm2); - scaleFlip = !scaleFlip; - } - } - log.endTest("Transform graphics context " + NUMBER_NODES + " times"); - - final Rectangle2D rect = new Rectangle2D.Double(0, 0, 100, 80); - final GeneralPath path = new GeneralPath(rect); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - g2.fill(rect); - } - log.endTest("Fill " + NUMBER_NODES + " rects"); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - g2.getTransform().getScaleX(); - } - log.endTest("Call g2.getTransform() " + NUMBER_NODES + " times"); - - log.startTest(); - for (int i = 0; i < NUMBER_NODES; i++) { - g2.fill(path); - } - log.endTest("Fill " + NUMBER_NODES + " paths"); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/PiccoloAsserts.java b/core/src/test/java/edu/umd/cs/piccolo/PiccoloAsserts.java deleted file mode 100644 index 16b081b..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/PiccoloAsserts.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package edu.umd.cs.piccolo; - -import java.awt.geom.Dimension2D; - -import junit.framework.Assert; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; - -/** - * This class provides helper methods to help with testing. - * - * It's implemented this way, as opposed to as a subclass, because when we move - * to JUnit4, inheritance is not the preferred way of importing asserts. - */ -public final class PiccoloAsserts { - private PiccoloAsserts() { - // Nothing to do - } - - public static final void assertEquals(final PBounds expected, final PBounds actual, final double errorRate) { - assertEquals("Expected " + expected + " but was " + actual, expected, actual, errorRate); - } - - public static final void assertEquals(final String message, final PBounds expected, final PBounds actual, - final double errorRate) { - Assert.assertEquals(message, expected.getX(), actual.getX(), errorRate); - Assert.assertEquals(message, expected.getY(), actual.getY(), errorRate); - Assert.assertEquals(message, expected.getWidth(), actual.getWidth(), errorRate); - Assert.assertEquals(message, expected.getHeight(), actual.getHeight(), errorRate); - } - - public static void assertEquals(final PDimension expected, final Dimension2D actual, final double errorRate) { - assertEquals("Expected " + expected + " but was " + actual, expected, actual, errorRate); - } - - public static void assertEquals(final String message, final PDimension expected, final Dimension2D actual, - final double errorRate) { - Assert.assertEquals(message, expected.getWidth(), actual.getWidth(), errorRate); - Assert.assertEquals(message, expected.getHeight(), actual.getHeight(), errorRate); - } - - public static void assertEquals(final String[] expected, final String[] actual) { - Assert.assertEquals("arrays are not same size", expected.length, actual.length); - for (int i = 0; i < expected.length; i++) { - Assert.assertEquals(expected[i], expected[i]); - } - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/SerializationTest.java b/core/src/test/java/edu/umd/cs/piccolo/SerializationTest.java deleted file mode 100644 index 5425dea..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/SerializationTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo; - -import java.util.Iterator; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; - -/** - * Unit test for node serialization. Should be removed - * in favor of class-specific serialization unit tests. - */ -public class SerializationTest extends TestCase { - - public SerializationTest(final String name) { - super(name); - } - - public void test() { - PNode l = new PLayer(); - - for (int i = 0; i < 100; i++) { - l.addChild(new PNode()); - l.addChild(new PText("Hello World")); - l.addChild(new PPath()); - } - - l = (PNode) l.clone(); // copy uses serialization internally - assertTrue(l.getChildrenCount() == 300); - - final Iterator i = l.getChildrenIterator(); - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - assertEquals(l, each.getParent()); - } - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/activities/PInterpolatingActivityTest.java b/core/src/test/java/edu/umd/cs/piccolo/activities/PInterpolatingActivityTest.java deleted file mode 100755 index 7153a7b..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/activities/PInterpolatingActivityTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.activities; - -import edu.umd.cs.piccolo.util.PUtil; - -import junit.framework.TestCase; - -/** - * Unit test for PInterpolatingActivity. - */ -public class PInterpolatingActivityTest extends TestCase { - - public void testConstructorLong() { - PInterpolatingActivity activity = new PInterpolatingActivity(1L); - assertNotNull(activity); - assertEquals(1L, activity.getDuration()); - assertEquals(1, activity.getLoopCount()); - assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); - assertEquals(PInterpolatingActivity.SOURCE_TO_DESTINATION, activity.getMode()); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/activities/PTransformActivityTest.java b/core/src/test/java/edu/umd/cs/piccolo/activities/PTransformActivityTest.java deleted file mode 100644 index 86f8558..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/activities/PTransformActivityTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.activities; - -import junit.framework.TestCase; - -/** - * Unit test for PTransformActivity. - */ -public class PTransformActivityTest extends TestCase { - - public PTransformActivityTest(final String name) { - super(name); - } - - public void testToString() { - final PTransformActivity transformActivity = new PTransformActivity(1000, 0, null); - assertNotNull(transformActivity.toString()); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/event/MockPBasicInputEventHandler.java b/core/src/test/java/edu/umd/cs/piccolo/event/MockPBasicInputEventHandler.java deleted file mode 100644 index b4a898d..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/event/MockPBasicInputEventHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.util.ArrayList; - -/** - * Mock PBasicInputEventHandler. - */ -public class MockPBasicInputEventHandler extends PBasicInputEventHandler { - private final ArrayList methodCalls = new ArrayList(); - - public String[] getMethodCalls() { - final String[] result = new String[methodCalls.size()]; - for (int i = 0; i < methodCalls.size(); i++) { - result[i] = (String) methodCalls.get(i); - } - return result; - } - - public void keyboardFocusGained(final PInputEvent event) { - methodCalls.add("keyboardFocusGained"); - super.keyboardFocusGained(event); - } - - public void keyboardFocusLost(final PInputEvent event) { - methodCalls.add("keyboardFocusLost"); - super.keyboardFocusLost(event); - } - - public void keyPressed(final PInputEvent event) { - methodCalls.add("keyPressed"); - super.keyPressed(event); - } - - public void keyReleased(final PInputEvent event) { - methodCalls.add("keyReleased"); - super.keyReleased(event); - } - - public void keyTyped(final PInputEvent event) { - methodCalls.add("keyTyped"); - super.keyTyped(event); - } - - public void mouseClicked(final PInputEvent event) { - methodCalls.add("mouseClicked"); - super.mouseClicked(event); - } - - public void mouseDragged(final PInputEvent event) { - methodCalls.add("mouseDragged"); - super.mouseDragged(event); - } - - public void mouseEntered(final PInputEvent event) { - methodCalls.add("mouseEntered"); - super.mouseEntered(event); - } - - public void mouseExited(final PInputEvent event) { - methodCalls.add("mouseExited"); - super.mouseExited(event); - } - - public void mouseMoved(final PInputEvent event) { - methodCalls.add("mouseMoved"); - super.mouseMoved(event); - } - - public void mousePressed(final PInputEvent event) { - methodCalls.add("mousePressed"); - super.mousePressed(event); - } - - public void mouseReleased(final PInputEvent event) { - methodCalls.add("mouseReleased"); - super.mouseReleased(event); - } - - public void mouseWheelRotated(final PInputEvent event) { - methodCalls.add("mouseReleased"); - super.mouseWheelRotated(event); - } - - public void mouseWheelRotatedByBlock(final PInputEvent event) { - methodCalls.add("mouseWheelRotatedByBlock"); - super.mouseWheelRotatedByBlock(event); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/event/PBasicInputEventListenerTest.java b/core/src/test/java/edu/umd/cs/piccolo/event/PBasicInputEventListenerTest.java deleted file mode 100644 index 25eb68d..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/event/PBasicInputEventListenerTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.event.FocusEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PiccoloAsserts; - -/** - * Unit test for PBasicInputEventListener. - */ -public class PBasicInputEventListenerTest extends TestCase { - private PBasicInputEventHandler listener; - private MockPBasicInputEventHandler mockListener; - - public void setUp() { - listener = new PBasicInputEventHandler(); - } - - public void testSetEventFilterIsPersisted() { - final PInputEventFilter filter = new PInputEventFilter(); - listener.setEventFilter(filter); - assertSame(filter, listener.getEventFilter()); - } - - public void testAcceptsEventDelegatesToFilter() { - final PInputEventFilter filter = new PInputEventFilter(); - listener.setEventFilter(filter); - final PInputEvent event = buildInputEvent(); - assertTrue(listener.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); - filter.rejectAllEventTypes(); - assertFalse(listener.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); - } - - public void testProcessEventDelegatesToSubClassMethodsBasedOnType() { - final PInputEvent event = buildInputEvent(); - - mockListener = new MockPBasicInputEventHandler(); - final int[] eventTypes = new int[] { KeyEvent.KEY_PRESSED, KeyEvent.KEY_RELEASED, KeyEvent.KEY_TYPED, - MouseEvent.MOUSE_RELEASED, MouseEvent.MOUSE_CLICKED, MouseEvent.MOUSE_DRAGGED, - MouseEvent.MOUSE_ENTERED, MouseEvent.MOUSE_EXITED, MouseEvent.MOUSE_MOVED, MouseEvent.MOUSE_PRESSED, - MouseWheelEvent.WHEEL_UNIT_SCROLL, MouseWheelEvent.WHEEL_BLOCK_SCROLL, FocusEvent.FOCUS_GAINED, - FocusEvent.FOCUS_LOST }; - - for (int i = 0; i < eventTypes.length; i++) { - mockListener.processEvent(event, eventTypes[i]); - } - - PiccoloAsserts.assertEquals(new String[] { "keyPressed", "keyReleased", "keyTyped", "mouseReleased", - "mouseClicked", "mouseDragged", "mouseEntered", "mouseExited", "mouseMoved", "mousePressed", - "mouseWheelRotated", "mouseWheelRotatedByBlock", "focusGained", "focusLost" }, mockListener - .getMethodCalls()); - } - - private PInputEvent buildInputEvent() { - final PCanvas canvas = new PCanvas(); - final MouseEvent mouseEvent = new MouseEvent(canvas, 1, System.currentTimeMillis(), 0, 0, 0, 1, false); - final PInputEvent event = new PInputEvent(canvas.getRoot().getDefaultInputManager(), mouseEvent); - return event; - } - -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/event/PDragEventHandlerTest.java b/core/src/test/java/edu/umd/cs/piccolo/event/PDragEventHandlerTest.java deleted file mode 100644 index 4b595cc..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/event/PDragEventHandlerTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import junit.framework.TestCase; - -/** - * Unit test for PDragEventHander. - */ -public class PDragEventHandlerTest extends TestCase { - private PDragEventHandler handler; - - public void setUp() { - handler = new PDragEventHandler(); - } - - public void testMoveToFrontOnPressDefaultToFalse() { - assertFalse(handler.getMoveToFrontOnPress()); - } - - public void testMoveToFrontOnPressPersists() { - handler.setMoveToFrontOnPress(true); - assertTrue(handler.getMoveToFrontOnPress()); - } - -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/event/PInputEventFilterTest.java b/core/src/test/java/edu/umd/cs/piccolo/event/PInputEventFilterTest.java deleted file mode 100644 index 9c6d096..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/event/PInputEventFilterTest.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; - -import javax.swing.JComponent; -import javax.swing.JPanel; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.PInputManager; - -/** - * Unit test for PInputEventFilter. - */ -public class PInputEventFilterTest extends TestCase { - private PInputEventFilter filter; - - public void setUp() { - filter = new PInputEventFilter(); - } - - public void testAcceptsAlreadyHandledEventsFalseByDefault() { - assertFalse(filter.getAcceptsAlreadyHandledEvents()); - } - - public void testDoesNotMarkEventsHandledByDefault() { - assertFalse(filter.getMarksAcceptedEventsAsHandled()); - } - - public void testAcceptsEverythingByDefault() { - assertAcceptsAll(); - } - - public void testMaskDoesNotAffectReportedAccepts() { - filter = new PInputEventFilter(0); - assertAcceptsAll(); - } - - private void assertAcceptsAll() { - assertTrue(filter.getAcceptsFocusEvents()); - assertTrue(filter.getAcceptsKeyPressed()); - assertTrue(filter.getAcceptsKeyReleased()); - assertTrue(filter.getAcceptsKeyTyped()); - assertTrue(filter.getAcceptsMouseClicked()); - assertTrue(filter.getAcceptsMouseDragged()); - assertTrue(filter.getAcceptsMouseEntered()); - assertTrue(filter.getAcceptsMouseExited()); - assertTrue(filter.getAcceptsMouseMoved()); - assertTrue(filter.getAcceptsMousePressed()); - assertTrue(filter.getAcceptsMouseReleased()); - assertTrue(filter.getAcceptsMouseWheelRotated()); - } - - public void testRejectsEverythingAfterCallingRejectAllEventTypes() { - filter.rejectAllEventTypes(); - assertRejectsAll(); - } - - private void assertRejectsAll() { - assertFalse(filter.getAcceptsFocusEvents()); - assertFalse(filter.getAcceptsKeyPressed()); - assertFalse(filter.getAcceptsKeyReleased()); - assertFalse(filter.getAcceptsKeyTyped()); - assertFalse(filter.getAcceptsMouseClicked()); - assertFalse(filter.getAcceptsMouseDragged()); - assertFalse(filter.getAcceptsMouseEntered()); - assertFalse(filter.getAcceptsMouseExited()); - assertFalse(filter.getAcceptsMouseMoved()); - assertFalse(filter.getAcceptsMousePressed()); - assertFalse(filter.getAcceptsMouseReleased()); - assertFalse(filter.getAcceptsMouseWheelRotated()); - } - - public void testSetAcceptsFocusEventsPersists() { - filter.setAcceptsFocusEvents(false); - assertFalse(filter.getAcceptsFocusEvents()); - } - - public void testSetAcceptsKeyPressedPersists() { - filter.setAcceptsKeyPressed(false); - assertFalse(filter.getAcceptsKeyPressed()); - } - - public void testSetAcceptsKeyReleasedPersists() { - filter.setAcceptsKeyReleased(false); - assertFalse(filter.getAcceptsKeyReleased()); - } - - public void testSetAcceptsKeyTypedPersists() { - filter.setAcceptsKeyTyped(false); - assertFalse(filter.getAcceptsKeyTyped()); - } - - public void testSetAcceptsMouseClickedPersists() { - filter.setAcceptsMouseClicked(false); - assertFalse(filter.getAcceptsMouseClicked()); - } - - public void testSetAcceptsMouseEnteredPersists() { - filter.setAcceptsMouseEntered(false); - assertFalse(filter.getAcceptsMouseEntered()); - } - - public void testSetAcceptsMouseExitedPersists() { - filter.setAcceptsMouseExited(false); - assertFalse(filter.getAcceptsMouseExited()); - } - - public void testSetAcceptsMouseMovedPersists() { - filter.setAcceptsMouseMoved(false); - assertFalse(filter.getAcceptsMouseMoved()); - } - - public void testSetAcceptsMouseDraggedPersists() { - filter.setAcceptsMouseDragged(false); - assertFalse(filter.getAcceptsMouseDragged()); - } - - public void testSetAcceptsMouseMovedPressed() { - filter.setAcceptsMousePressed(false); - assertFalse(filter.getAcceptsMousePressed()); - } - - public void testSetAcceptsMouseMovedReleased() { - filter.setAcceptsMouseReleased(false); - assertFalse(filter.getAcceptsMouseReleased()); - } - - public void testSetAcceptsMouseWheelRotated() { - filter.setAcceptsMouseWheelRotated(false); - assertFalse(filter.getAcceptsMouseWheelRotated()); - } - - public void testAcceptsSimpleEvent() { - final PInputEvent event = buildTestEvent(); - assertAcceptsEvent(event); - } - - public void testRejectsAcceptedEventIfAcceptsHandledEventsIsFalse() { - final PInputEvent event = buildTestEvent(); - event.setHandled(true); - filter.setAcceptsAlreadyHandledEvents(false); - assertRejectsEvent(event); - } - - public void testRejectsEventsUnlessModifiersContainAllOfMask() { - PInputEvent event = buildTestEvent(); - filter.setAndMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); - assertRejectsEvent(event); - event = buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); - assertAcceptsEvent(event); - - event = buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK | InputEvent.META_MASK); - assertAcceptsEvent(event); - } - - public void testRejectsEventsUnlessModifiersContainOneOfOrMask() { - final PInputEvent event = buildTestEvent(); - filter.setOrMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); - assertRejectsEvent(event); - assertRejectsEvent(buildTestEvent(InputEvent.META_MASK)); - assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_MASK)); - assertAcceptsEvent(buildTestEvent(InputEvent.ALT_MASK)); - assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); - } - - public void testRejectsEventsUnlessTheyMatchOneOfNotMask() { - final PInputEvent event = buildTestEvent(); - filter.setNotMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); - assertAcceptsEvent(event); - - assertAcceptsEvent(buildTestEvent(InputEvent.META_MASK)); - assertRejectsEvent(buildTestEvent(InputEvent.CTRL_MASK)); - assertRejectsEvent(buildTestEvent(InputEvent.ALT_MASK)); - assertRejectsEvent(buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); - } - - public void testRejectsMouseEventsIfMouseClickFilterSet() { - filter.setAcceptClickCount((short) 1); - assertRejectsEvent(buildTestEvent(0, 0)); - assertAcceptsEvent(buildTestEvent(0, 1)); - assertRejectsEvent(buildTestEvent(0, 2)); - assertRejectsEvent(buildTestEvent(0, 3)); - } - - public void testMarksEventsAsHandledIsHonnored() { - filter.setMarksAcceptedEventsAsHandled(true); - final PInputEvent event = buildTestEvent(); - assertAcceptsEvent(event); - assertTrue(event.isHandled()); - } - - public void testRejectAllClickCountsIsHonoured() { - filter.rejectAllClickCounts(); - assertRejectsEvent(buildTestEvent(0, 0)); - assertRejectsEvent(buildTestEvent(0, 1)); - assertRejectsEvent(buildTestEvent(0, 2)); - assertRejectsEvent(buildTestEvent(0, 3)); - - } - - private void assertRejectsEvent(final PInputEvent event) { - assertFalse(filter.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); - } - - private void assertAcceptsEvent(final PInputEvent event) { - assertTrue(filter.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); - } - - private PInputEvent buildTestEvent() { - return buildTestEvent(InputEvent.BUTTON1_MASK); - } - - private PInputEvent buildTestEvent(final int modifiers) { - return buildTestEvent(modifiers, 0); - } - - private PInputEvent buildTestEvent(final int modifiers, final int clickCount) { - final JComponent component = new JPanel(); - final PInputManager inputManager = new PInputManager(); - - final MouseEvent event = new MouseEvent(component, 1, System.currentTimeMillis(), modifiers, 1, 1, clickCount, - false); - return new PInputEvent(inputManager, event); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/event/PInputEventTest.java b/core/src/test/java/edu/umd/cs/piccolo/event/PInputEventTest.java deleted file mode 100644 index d4db66f..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/event/PInputEventTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import java.awt.Dimension; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPickPath; - -/** - * Unit test for PInputEvent. - */ -public class PInputEventTest extends TestCase { - private PCanvas canvas; - private MouseEvent swingEvent; - private PInputEvent mouseEvent; - - public void setUp() { - canvas = new PCanvas(); - canvas.setPreferredSize(new Dimension(100, 100)); - canvas.setBounds(0, 0, 100, 100); - swingEvent = buildSwingClick(5, 5); - final PCamera camera = canvas.getCamera(); - final PPickPath pickPath = new PPickPath(camera, new PBounds(0, 0, 10, 10)); - mouseEvent = new PInputEvent(canvas.getRoot().getDefaultInputManager(), swingEvent); - mouseEvent.setPath(pickPath); - } - - public void testInputManagerShouldBeSameAsGivenToConstructor() { - assertSame(canvas.getRoot().getDefaultInputManager(), mouseEvent.getInputManager()); - } - - public void testComponentIsComponentPassedToSwingEvent() { - assertEquals(canvas, mouseEvent.getComponent()); - } - - public void testKeyboardAccessorsThrowExceptionsOnMousEvents() { - try { - mouseEvent.getKeyChar(); - } - catch (final IllegalStateException e) { - // expected - } - - try { - mouseEvent.getKeyCode(); - } - catch (final IllegalStateException e) { - // expected - } - - try { - mouseEvent.getKeyLocation(); - } - catch (final IllegalStateException e) { - // expected - } - - try { - mouseEvent.isActionKey(); - } - catch (final IllegalStateException e) { - // expected - } - - } - - public void testCorrectlyIdentifiesPositiveLeftMouseClick() { - assertTrue(mouseEvent.isLeftMouseButton()); - } - - public void testCorrectlyIdentifiesNegativeRightMouseClick() { - assertFalse(mouseEvent.isRightMouseButton()); - } - - public void testCorrectlyIdentifiesNegativeMiddleMouseClick() { - assertFalse(mouseEvent.isMiddleMouseButton()); - } - - public void testEventsAreNotHandledByDefault() { - assertFalse(mouseEvent.isHandled()); - } - - public void testSetHandledPersists() { - mouseEvent.setHandled(true); - assertTrue(mouseEvent.isHandled()); - } - - public void testHandledEventCanBeUnHandled() { - mouseEvent.setHandled(true); - mouseEvent.setHandled(false); - assertFalse(mouseEvent.isHandled()); - } - - public void testReturnsCorrectModifiers() { - assertEquals(InputEvent.BUTTON1_MASK, mouseEvent.getModifiers()); - } - - public void testGetButtonUsesWhatWasPassedToMouseEvent() { - assertEquals(MouseEvent.BUTTON1, mouseEvent.getButton()); - } - - private MouseEvent buildSwingClick(final int x, final int y) { - return new MouseEvent(canvas, 1, System.currentTimeMillis(), InputEvent.BUTTON1_MASK, x, y, 1, false, - MouseEvent.BUTTON1); - } - -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/event/PPanEventHandlerTest.java b/core/src/test/java/edu/umd/cs/piccolo/event/PPanEventHandlerTest.java deleted file mode 100644 index 0e04629..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/event/PPanEventHandlerTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import junit.framework.TestCase; - -/** - * Unit test for PPanEventHander. - */ -public class PPanEventHandlerTest extends TestCase { - private PPanEventHandler handler; - - public void setUp() { - handler = new PPanEventHandler(); - } - - public void testAutoPanIsTrueByDefault() { - assertTrue(handler.getAutopan()); - } - - public void testSetAutoPanPersists() { - handler.setAutopan(true); - assertTrue(handler.getAutopan()); - } - - public void testDefaultMinAutoPanSpeed() { - assertEquals(250, handler.getMinAutoPanSpeed(), 0.0000001); - } - - public void testMinAutoPanSpeedPersists() { - handler.setMinAutopanSpeed(10); - assertEquals(10, handler.getMinAutoPanSpeed(), 0.000001); - } - - public void testMaxDefaultAutoPanSpeed() { - assertEquals(250, handler.getMinAutoPanSpeed(), 0.0000001); - } - - public void testMaxAutoPanSpeedPersists() { - handler.setMaxAutopanSpeed(10); - assertEquals(10, handler.getMaxAutoPanSpeed(), 0.000001); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/event/PZoomEventHandlerTest.java b/core/src/test/java/edu/umd/cs/piccolo/event/PZoomEventHandlerTest.java deleted file mode 100644 index 4ca08a6..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/event/PZoomEventHandlerTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.event; - -import junit.framework.TestCase; - -/** - * Unit test for PZoomEventHandler. - */ -public class PZoomEventHandlerTest extends TestCase { - - public PZoomEventHandlerTest(final String name) { - super(name); - } - - public void testToString() { - final PZoomEventHandler zoomEventHandler = new PZoomEventHandler(); - assertNotNull(zoomEventHandler.toString()); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/nodes/PHtmlViewTest.java b/core/src/test/java/edu/umd/cs/piccolo/nodes/PHtmlViewTest.java deleted file mode 100644 index 02f6a43..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/nodes/PHtmlViewTest.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.nodes; - -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.MockPropertyChangeListener; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.util.PBounds; - -/** - * Unit test for PHtmlView. - */ -public class PHtmlViewTest extends TestCase { - - private static final String LOREM_IPSUM = "30. Lorem ipsum dolor sit amet, consectetur adipiscing elit posuere."; - private MockPropertyChangeListener mockListener; - - public void setUp() { - mockListener = new MockPropertyChangeListener(); - } - - public void testConstructorRetainsHtmlWithCSSStyling() { - PHtmlView html = new PHtmlView("html text
"); - assertEquals("html text
", html.getText()); - } - - public void testConstructorRetainsAllParametersWhenRealHtml() { - PHtmlView html = new PHtmlView("html text
"); - assertEquals("html text
", html.getText()); - } - - public void testConstructorRetainsAllParameters() { - Font font = new Font("Serif", Font.PLAIN, 12); - PHtmlView html = new PHtmlView("not html", font, Color.RED); - assertEquals("not html", html.getText()); - assertEquals(font, html.getFont()); - assertEquals(Color.RED, html.getTextColor()); - } - - public void testConstructorAcceptsRealisticHtml() { - PHtmlView html = new PHtmlView("html text
"); - assertNotNull(html); - assertEquals("html text
", html.getText()); - } - - public void testConstructorAcceptsNonHtmlText() { - PHtmlView html = new PHtmlView("not html"); - assertNotNull(html); - assertEquals("not html", html.getText()); - assertEquals(PHtmlView.DEFAULT_FONT, html.getFont()); - assertEquals(PHtmlView.DEFAULT_TEXT_COLOR, html.getTextColor()); - } - - public void testConstructorAcceptsNullHtml() { - PHtmlView html = new PHtmlView(null); - assertEquals(null, html.getText()); - } - - public void testDefaultConstructorHasExpectedDefaults() { - PHtmlView html = new PHtmlView(); - assertEquals(null, html.getText()); - assertEquals(PHtmlView.DEFAULT_FONT, html.getFont()); - assertEquals(PHtmlView.DEFAULT_TEXT_COLOR, html.getTextColor()); - } - - public void testConstructorAcceptsNullFontAndColor() { - PHtmlView html9 = new PHtmlView("not html", null, null); - assertEquals(null, html9.getFont()); - assertEquals(null, html9.getTextColor()); - } - - public void testConstructorAcceptsNullColor() { - Font font = new Font("Serif", Font.PLAIN, 12); - PHtmlView html = new PHtmlView("not html", font, null); - assertEquals(null, html.getTextColor()); - } - - public void testConstructorAcceptsNullFont() { - PHtmlView html = new PHtmlView("not html", null, Color.RED); - assertEquals(null, html.getFont()); - } - - public void testGetClickedAddressReturnsSingleQuotedAddress() { - PHtmlView html = new PHtmlView("testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("http://www.testing.com", html.getLinkAddressAt(5,5)); - } - - public void testGetClickedAddressReturnsDoubleQuotedAddress() { - PHtmlView html = new PHtmlView("testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("http://www.testing.com", html.getLinkAddressAt(5,5)); - } - - public void testBracketsAreValidInHrefs() { - PHtmlView html = new PHtmlView("testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("a>b", html.getLinkAddressAt(5,5)); - } - - public void testGetClickedAddressReturnsNullWhenInvalid() { - PHtmlView html = new PHtmlView("b'>testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertNull(html.getLinkAddressAt(5,5)); - } - - public void testGetClickedAddressReturnsHrefWhenMissingEndAnchorTag() { - PHtmlView html = new PHtmlView("testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("testing.com", html.getLinkAddressAt(5,5)); - } - - public void testHandlesTricksyTitles() { - PHtmlView html = new PHtmlView("testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("where to go", html.getLinkAddressAt(5,5)); - } - - public void testHandlesHrefWithoutQuotes() { - PHtmlView html = new PHtmlView("testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("testing.com", html.getLinkAddressAt(5,5)); - } - - public void testUnclosedTagsCauseIgnoreOfTag() { - PHtmlView html = new PHtmlView("Missing End TAg "); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("testing.com", html.getLinkAddressAt(5,5)); - } - - public void testUnclosedQuotesCauseIgnoreOfLink() { - PHtmlView html = new PHtmlView("testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertEquals("", html.getLinkAddressAt(5,5)); - } - - public void testReturnsNullWhenClickOutsideLink() { - PHtmlView html = new PHtmlView("0123456789 testing"); - html.setBounds(new PBounds(0, 0, 100, 100)); - assertNull(html.getLinkAddressAt(5,5)); - } - - public void testSetHtmlColorPersists() { - PHtmlView html = new PHtmlView(); - html.setTextColor(Color.RED); - assertEquals(Color.RED, html.getTextColor()); - } - - public void testFontIsNotNullByDefault() { - PHtmlView html = new PHtmlView(); - assertNotNull(html.getFont()); - } - - public void testHtmlColorIsNotNullByDefault() { - PHtmlView html = new PHtmlView(); - assertNotNull(html.getTextColor()); - } - - public void testSetHtmlFiresEventOnChangeOnly() { - PHtmlView html = new PHtmlView(); - html.addPropertyChangeListener(PHtmlView.PROPERTY_TEXT, mockListener); - html.setText("testing"); - assertEquals(1, mockListener.getPropertyChangeCount()); - assertEquals(PHtmlView.PROPERTY_TEXT, mockListener.getPropertyChange(0).getPropertyName()); - html.setText("testing"); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testSetHtmlToNullIsAllowed() { - PHtmlView html = new PHtmlView(); - html.setText(null); - assertNull(html.getText()); - } - - public void testSetFontPerists() { - PHtmlView html = new PHtmlView(); - Font font = Font.getFont("arial"); - html.setFont(font); - assertSame(font, html.getFont()); - } - - public void testPaintFillsBounds() { - PHtmlView html = new PHtmlView(LOREM_IPSUM); - html.setPaint(Color.RED); - - PCanvas canvas = new PCanvas(); - canvas.setBackground(Color.WHITE); - canvas.setBounds(0, 0, 500, 30); - canvas.getLayer().addChild(html); - - BufferedImage image = new BufferedImage(600, 30, BufferedImage.TYPE_INT_RGB); - Graphics2D g2 = image.createGraphics(); - canvas.paint(g2); - - assertEquals(Color.red.getRGB(), image.getRGB(0, 0)); - assertEquals(Color.red.getRGB(), image.getRGB(0, (int)(html.getHeight()-1))); - assertEquals(Color.red.getRGB(), image.getRGB(300, 0)); - } - - public void testClone() { - PHtmlView html = new PHtmlView(LOREM_IPSUM); - html.setTextColor(Color.RED); - PHtmlView clone = (PHtmlView) html.clone(); - assertNotNull(clone); - assertEquals(Color.RED, clone.getTextColor()); - assertEquals(LOREM_IPSUM, clone.getText()); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/nodes/PImageTest.java b/core/src/test/java/edu/umd/cs/piccolo/nodes/PImageTest.java deleted file mode 100644 index 39349df..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/nodes/PImageTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.nodes; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -import javax.imageio.ImageIO; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.util.PPaintContext; - -/** - * Unit test for PImage. - */ -public class PImageTest extends TestCase { - - public void testClone() { - final PImage srcNode = new PImage(new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB)); - final PImage clonedNode = (PImage) srcNode.clone(); - assertNotNull(clonedNode.getImage()); - - assertEquals(srcNode.getImage().getWidth(null), clonedNode.getImage().getWidth(null)); - assertEquals(srcNode.getImage().getHeight(null), clonedNode.getImage().getHeight(null)); - - assertEquals(srcNode.getBounds(), clonedNode.getBounds()); - } - - public void testToString() { - final PImage aNode = new PImage(new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB)); - assertNotNull(aNode.toString()); - } - - public void testToBufferedImageReturnsCopyIfToldTo() { - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final BufferedImage copy = PImage.toBufferedImage(img, true); - assertNotSame(img, copy); - } - - public void testCanBeCreatedFromFile() throws IOException { - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final File imgFile = File.createTempFile("test", ".jpeg"); - ImageIO.write(img, "JPEG", imgFile); - imgFile.deleteOnExit(); - final PImage imageNode = new PImage(imgFile.getAbsolutePath()); - assertNotNull(imageNode.getImage()); - assertEquals(100, imageNode.getImage().getWidth(null)); - assertEquals(100, imageNode.getImage().getHeight(null)); - } - - public void testCanBeCreatedFromUrl() throws IOException { - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final File imgFile = File.createTempFile("test", ".jpeg"); - imgFile.deleteOnExit(); - ImageIO.write(img, "JPEG", imgFile); - - final PImage imageNode = new PImage(imgFile.toURI().toURL()); - assertEquals(100, imageNode.getImage().getWidth(null)); - assertEquals(100, imageNode.getImage().getHeight(null)); - } - - public void testImageCanBeSetFromFile() throws IOException { - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - final File imgFile = File.createTempFile("test", ".jpeg"); - imgFile.deleteOnExit(); - ImageIO.write(img, "JPEG", imgFile); - - final PImage imageNode = new PImage(); - imageNode.setImage(imgFile.getAbsolutePath()); - assertEquals(100, imageNode.getImage().getWidth(null)); - assertEquals(100, imageNode.getImage().getHeight(null)); - } - - public void testPaintAnEmptyImageNodeDoesNothing() { - final PImage imageNode = new PImage(); - - final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - - final PPaintContext paintContext = new PPaintContext(img.createGraphics()); - imageNode.paint(paintContext); - } - -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/nodes/PPathTest.java b/core/src/test/java/edu/umd/cs/piccolo/nodes/PPathTest.java deleted file mode 100644 index 1c95fd9..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/nodes/PPathTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.nodes; - -import java.awt.Color; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.MockPropertyChangeListener; -import edu.umd.cs.piccolo.PiccoloAsserts; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PObjectOutputStream; - -/** - * Unit test for PPath. - */ -public class PPathTest extends TestCase { - - private MockPropertyChangeListener mockListener; - - public void setUp() { - mockListener = new MockPropertyChangeListener(); - } - - public void testStrokeIsNotNullByDefault() { - final PPath path = new PPath(); - assertNotNull(path.getStroke()); - } - - public void testStrokePaintIsBlackByDefault() { - final PPath path = new PPath(); - assertEquals(Color.BLACK, path.getStrokePaint()); - } - - public void testClone() { - PPath p = PPath.createEllipse(0, 0, 100, 100); - PPath cloned = (PPath) p.clone(); - assertEquals(p.getBounds(), cloned.getBounds()); - //assertEquals(p.getPathReference()., cloned.getPathReference()); - } - - public void testSerialization() throws IOException, ClassNotFoundException { - final PPath srcPath = PPath.createEllipse(0, 0, 100, 100); - final PBounds srcBounds = srcPath.getBounds(); - - final File file = File.createTempFile("test", "ser"); - - serializeToFile(srcPath, file); - final PPath resultPath = deserializeFromFile(srcBounds, file); - file.deleteOnExit(); - - assertEquals(resultPath.getBounds(), srcBounds); - } - - private PPath deserializeFromFile(final PBounds b, final File file) throws FileNotFoundException, IOException, - ClassNotFoundException { - PPath path; - final FileInputStream fin = new FileInputStream(file); - final ObjectInputStream in = new ObjectInputStream(fin); - path = (PPath) in.readObject(); - - return path; - } - - private void serializeToFile(final PPath p, final File file) throws FileNotFoundException, IOException { - final FileOutputStream fout = new FileOutputStream(file); - final PObjectOutputStream out = new PObjectOutputStream(fout); - out.writeObjectTree(p); - out.flush(); - out.close(); - } - - public void testCreateRectangleReturnsValidPPath() { - final PPath path = PPath.createRectangle(0, 0, 100, 50); - assertNotNull(path); - - // Seems like rounding is affecting the bounds greatly - PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); - } - - public void testCreateEllipseReturnsValidPPath() { - final PPath path = PPath.createEllipse(0, 0, 100, 50); - assertNotNull(path); - - // Seems like rounding is affecting the bounds greatly - PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); - } - - public void testCreateRoundedRectReturnsValidPPath() { - final PPath path = PPath.createRoundRectangle(0, 0, 100, 50, 10, 10); - assertNotNull(path); - - // Seems like rounding is affecting the bounds greatly - PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); - } - - public void testCreateLineReturnsValidPPath() { - final PPath path = PPath.createLine(0, 0, 100, 0); - assertNotNull(path); - - // Seems like rounding is affecting the bounds greatly - PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 0), path.getBounds(), 2.0d); - } - - public void testCreatePolyLinePoint2DReturnsValidPPath() { - final PPath path = PPath.createPolyline(new Point2D[] { new Point2D.Double(0, 0), new Point2D.Double(100, 50), - new Point2D.Double(100, 0) }); - assertNotNull(path); - - // Seems like rounding is affecting the bounds greatly - PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); - } - - public void testCreatePolyLineFloatsReturnsValidPPath() { - final PPath path = PPath.createPolyline(new float[] { 0, 100, 100 }, new float[] { 0, 50, 0 }); - assertNotNull(path); - - // Seems like rounding is affecting the bounds greatly - PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); - } - - public void testSetStrokePaintPersists() { - final PPath path = new PPath(); - path.setStrokePaint(Color.RED); - assertEquals(Color.RED, path.getStrokePaint()); - } - - public void testSetStrokeFiresPropertyChangeEvent() { - final PPath path = new PPath(); - path.addPropertyChangeListener(PPath.PROPERTY_STROKE_PAINT, mockListener); - path.setStrokePaint(Color.RED); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - - public void testChangingPathFiresPropertyChangeEvent() { - final PPath path = new PPath(); - path.addPropertyChangeListener(PPath.PROPERTY_PATH, mockListener); - path.append(new Rectangle2D.Double(0, 0, 100, 50), true); - assertEquals(1, mockListener.getPropertyChangeCount()); - } - -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/nodes/PTextTest.java b/core/src/test/java/edu/umd/cs/piccolo/nodes/PTextTest.java deleted file mode 100644 index fbf9c9d..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/nodes/PTextTest.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.nodes; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.MockPropertyChangeListener; - -/** - * Unit test for PText. - */ -public class PTextTest extends TestCase { - - private PText textNode; - private MockPropertyChangeListener mockListener; - - public PTextTest(final String name) { - super(name); - } - - public void setUp() { - textNode = new PText(); - mockListener = new MockPropertyChangeListener(); - } - - public void testClone() { - textNode.setTextPaint(Color.BLUE); - textNode.setText("Boo"); - final PText clonedNode = (PText) textNode.clone(); - assertEquals("Boo", clonedNode.getText()); - assertEquals(textNode.getFont(), clonedNode.getFont()); - assertEquals(Color.BLUE, clonedNode.getTextPaint()); - } - - public void testTextIsEmptyByDefault() { - final PText textNode = new PText(); - assertEquals("", textNode.getText()); - } - - public void testTextMayBeAssignedEmptyString() { - textNode.setText(""); - assertEquals("", textNode.getText()); - } - - public void testTextNullGetsInterpretedAsEmptyString() { - textNode.setText(null); - assertEquals("", textNode.getText()); - } - - public void testBoundsGrowWithTextByDefault() { - final PText text123 = new PText("123"); - final double width123 = text123.getBounds().getWidth(); - - final PText text1234 = new PText("1234"); - - final double width1234 = text1234.getBounds().getWidth(); - - assertTrue(width123 < width1234); - } - - public void testBoundsOfEmptyString() { - textNode.setText(""); - assertEquals(0, textNode.getBoundsReference().getWidth(), 0.000001); - textNode.setText(null); - assertEquals(0, textNode.getBoundsReference().getWidth(), 0.000001); - } - - public void testToString() { - textNode.setText("hello world"); - assertNotNull(textNode.toString()); - } - - public void testHorizontalAlignmentIsLeftByDefault() { - assertEquals(Component.LEFT_ALIGNMENT, textNode.getHorizontalAlignment(), 0.000001); - } - - public void testSetHorizontalAlignmentPersists() { - textNode.setHorizontalAlignment(Component.RIGHT_ALIGNMENT); - assertEquals(Component.RIGHT_ALIGNMENT, textNode.getHorizontalAlignment(), 0.000001); - } - - public void testSetHorizontalAlignmentInvalidValues() { - try { - textNode.setHorizontalAlignment(-2.0f); - } - catch (final IllegalArgumentException e) { - // expected - } - try { - textNode.setHorizontalAlignment(2.0f); - } - catch (final IllegalArgumentException e) { - // expected - } - try { - textNode.setHorizontalAlignment(-Float.MAX_VALUE); - } - catch (final IllegalArgumentException e) { - // expected - } - try { - textNode.setHorizontalAlignment(Float.MAX_VALUE); - } - catch (final IllegalArgumentException e) { - // expected - } - try { - textNode.setHorizontalAlignment(-1.00f); - } - catch (final IllegalArgumentException e) { - // expected - } - try { - textNode.setHorizontalAlignment(1.00f); - } - catch (final IllegalArgumentException e) { - // expected - } - } - - public void testTextPaintIsBlackByDefault() { - assertEquals(Color.BLACK, textNode.getTextPaint()); - } - - public void testSetTextPaintPersists() { - textNode.setTextPaint(Color.RED); - assertEquals(Color.RED, textNode.getTextPaint()); - } - - public void testConstrainWidthToTextTrueByDefault() { - assertTrue(textNode.isConstrainWidthToTextWidth()); - } - - public void testConstrainHeightToTextTrueByDefault() { - assertTrue(textNode.isConstrainHeightToTextHeight()); - } - - public void testConstrainWidthPersists() { - textNode.setConstrainWidthToTextWidth(true); - assertTrue(textNode.isConstrainWidthToTextWidth()); - } - - public void testConstrainHeightPersists() { - textNode.setConstrainHeightToTextHeight(true); - assertTrue(textNode.isConstrainHeightToTextHeight()); - } - - public void testDefaultGreekThreshold() { - assertEquals(PText.DEFAULT_GREEK_THRESHOLD, textNode.getGreekThreshold(), 0.000001); - } - - public void testSetGreekThreshold() { - textNode.setGreekThreshold(2); - assertEquals(2, textNode.getGreekThreshold(), 0.000001); - } - - public void testDefaultFont() { - assertEquals(PText.DEFAULT_FONT, textNode.getFont()); - } - - public void testSetFontPersists() { - final Font newFont = new Font("Arial", Font.BOLD, 10); - textNode.setFont(newFont); - assertEquals(newFont, textNode.getFont()); - } - - public void testSetFontFiresPropertyChangedEvent() { - textNode.addPropertyChangeListener(PText.PROPERTY_FONT, mockListener); - final Font newFont = new Font("Arial", Font.BOLD, 10); - textNode.setFont(newFont); - - assertEquals(1, mockListener.getPropertyChangeCount()); - assertEquals(PText.PROPERTY_FONT, mockListener.getPropertyChange(0).getPropertyName()); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/util/PAffineTransformTest.java b/core/src/test/java/edu/umd/cs/piccolo/util/PAffineTransformTest.java deleted file mode 100644 index e2642bc..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/util/PAffineTransformTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.Dimension; -import java.awt.geom.Dimension2D; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.PiccoloAsserts; - -/** - * Unit test for PAffineTransform. - */ -public class PAffineTransformTest extends TestCase { - - private PAffineTransform at; - - public PAffineTransformTest(final String aName) { - super(aName); - } - - public void setUp() { - at = new PAffineTransform(); - } - - public void testRotation() { - at.rotate(Math.toRadians(45)); - assertEquals(at.getRotation(), Math.toRadians(45), 0.000000001); - at.setRotation(Math.toRadians(90)); - assertEquals(at.getRotation(), Math.toRadians(90), 0.000000001); - } - - public void testScale() { - at.scaleAboutPoint(0.45, 0, 1); - assertEquals(at.getScale(), 0.45, 0.000000001); - at.setScale(0.11); - assertEquals(at.getScale(), 0.11, 0.000000001); - } - - public void testTransformRectLeavesEmptyBoundsEmpty() { - final PBounds b1 = new PBounds(); - at.scale(0.5, 0.5); - at.translate(100, 50); - - at.transform(b1, b1); - assertTrue(b1.isEmpty()); - } - - public void testTransformRect() { - final PBounds b1 = new PBounds(0, 0, 100, 80); - final PBounds b2 = new PBounds(100, 100, 100, 80); - - at.scale(0.5, 0.5); - at.translate(100, 50); - - at.transform(b1, b1); - at.transform(b2, b2); - - PiccoloAsserts.assertEquals(new PBounds(50, 25, 50, 40), b1, 0.0001); - PiccoloAsserts.assertEquals(new PBounds(100, 75, 50, 40), b2, 0.0001); - - at.inverseTransform(b1, b1); - at.inverseTransform(b2, b2); - - PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 80), b1, 0.0001); - PiccoloAsserts.assertEquals(new PBounds(100, 100, 100, 80), b2, 0.0001); - } - - public void testThrowsExceptionWhenSetting0Scale() { - try { - at.setScale(0); - fail("Setting 0 scale should throw exception"); - } - catch (final RuntimeException e) { - // expected - } - } - - public void testSetOffsetLeavesRotationUntouched() { - at.setRotation(Math.PI); - at.setOffset(100, 50); - assertEquals(Math.PI, at.getRotation(), 0.001); - } - - public void testTransformDimensionWorks() { - final Dimension d1 = new Dimension(100, 50); - at.setScale(2); - final Dimension d2 = new Dimension(0, 0); - at.transform(d1, d2); - assertEquals(new Dimension(200, 100), d2); - } - - public void testTransformDimensionWorksWithSecondParamNull() { - final Dimension d1 = new Dimension(100, 50); - at.setScale(2); - final Dimension2D d2 = at.transform(d1, null); - assertEquals(new Dimension(200, 100), d2); - } - - public void testLocalToViewDimensionThrowsExceptionWhenTransformIsNonInvertible() { - at.setTransform(new PAffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); - try { - at.inverseTransform(new PDimension(1, 2), null); - fail("Exception not thrown when inverting non-invertible transform"); - } - catch (final PAffineTransformException e) { - // expected - } - } - - public void testLocalToViewPoint2DThrowsExceptionWhenTransformIsNonInvertible() { - at.setTransform(new PAffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); - try { - at.inverseTransform(new Point2D.Double(1, 2), null); - fail("Exception not thrown when inverting non-invertible transform"); - } - catch (final PAffineTransformException e) { - // expected - } - } - - public void testLocalToViewRectangle2DThrowsExceptionWhenTransformIsNonInvertible() { - at.setTransform(new PAffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); - try { - at.inverseTransform(new Rectangle2D.Double(1, 2, 3, 4), null); - fail("Exception not thrown when inverting non-invertible transform"); - } - catch (final PAffineTransformException e) { - // expected - } - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/util/PBoundsTest.java b/core/src/test/java/edu/umd/cs/piccolo/util/PBoundsTest.java deleted file mode 100644 index 5a500e4..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/util/PBoundsTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.geom.Rectangle2D; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.PiccoloAsserts; - -/** - * Unit test for PBounds. - */ -public class PBoundsTest extends TestCase { - public void testDefaultBoundsAreEmpty() { - final PBounds b = new PBounds(); - PiccoloAsserts.assertEquals(new PBounds(0, 0, 0, 0), b, 0.0001); - } - - public void testBoundsCloneConstructorWorks() { - final PBounds b1 = new PBounds(10, 15, 100, 50); - final PBounds b2 = new PBounds(b1); - PiccoloAsserts.assertEquals(b1, b2, 0.00001); - } - - public void testBoundsCanBeConstructedFromRectangle2D() { - final Rectangle2D r = new Rectangle2D.Double(1, 2, 3, 4); - final PBounds b = new PBounds(r); - PiccoloAsserts.assertEquals(new PBounds(1, 2, 3, 4), b, 0.000001); - } - - /* - * public void testBoundsCreatedFromPointAndWidthIsCorrect() { PBounds b = - * new PBounds(new Point2D.Double(), 10, 10); - * PiccoloAsserts.assertEquals(new PBounds(-10, -10, 20, 20), b, 0.000001); - * } - */ - - public void testResetToZeroClearsBounds() { - final PBounds b = new PBounds(1, 2, 3, 4); - b.resetToZero(); - assertTrue(b.isEmpty()); - PiccoloAsserts.assertEquals(new PBounds(), b, 0.0000001); - } - - public void testAdding1PointToEmptyYieldsEmpty() { - final PBounds b = new PBounds(); - b.add(-10, -10); - PiccoloAsserts.assertEquals(new PDimension(0, 0), b.getSize(), 0.00001); - } - - public void testAdding2PointsToEmptyYieldsNotEmpty() { - final PBounds b = new PBounds(); - b.add(-10, -10); - b.add(0, 0); - PiccoloAsserts.assertEquals(new PDimension(10, 10), b.getSize(), 0.00001); - } - - // TODO: This test should pass, but making it do so would break binary compatability - /* - * public void testWhenBoundsHas0HeightFullBoundsIsCorrectlyReturned() { - * final PNode node = new PNode(); final PBounds testBounds = new - * PBounds(10, 10, 10, 0); node.setBounds(testBounds); - * assertEquals(testBounds, node.getFullBounds()); } - */ -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/util/PDebugTest.java b/core/src/test/java/edu/umd/cs/piccolo/util/PDebugTest.java deleted file mode 100644 index e8d9490..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/util/PDebugTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.Color; - -import junit.framework.TestCase; - -/** - * Unit test for PDebug. - */ -public class PDebugTest extends TestCase { - public void setUp() { - PDebug.resetFPSTiming(); - } - - public void testGetDebugColourGeneratesGraysInCycle() { - assertEquals(new Color(100, 100, 100, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(110, 110, 110, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(120, 120, 120, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(130, 130, 130, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(140, 140, 140, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(150, 150, 150, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(160, 160, 160, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(170, 170, 170, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(180, 180, 180, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(190, 190, 190, 150), PDebug.getDebugPaintColor()); - assertEquals(new Color(100, 100, 100, 150), PDebug.getDebugPaintColor()); - } - - public void testUnlessOutputWasProcessedFPSisZero() throws InterruptedException { - assertEquals(0.0, PDebug.getInputFPS(), 0.00001); - PDebug.startProcessingInput(); - Thread.sleep(2); - PDebug.endProcessingInput(); - - assertEquals(0.0, PDebug.getTotalFPS(), 0.0001); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/util/PDimensionTest.java b/core/src/test/java/edu/umd/cs/piccolo/util/PDimensionTest.java deleted file mode 100644 index a825534..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/util/PDimensionTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.awt.Dimension; -import java.awt.geom.Dimension2D; -import java.awt.geom.Point2D; - -import junit.framework.TestCase; - -/** - * Unit test for PDimension. - */ -public class PDimensionTest extends TestCase { - public void testDefaultConstructorResultsInEmptyDimension() { - final PDimension dimension = new PDimension(); - - assertEquals(0, dimension.getWidth(), 0.00001); - assertEquals(0, dimension.getHeight(), 0.00001); - } - - public void testCloningConstructorDoesSo() { - final Dimension2D src = new Dimension(100, 50); - final PDimension copy = new PDimension(src); - - assertEquals(100, copy.getWidth(), 0.00001); - assertEquals(50, copy.getHeight(), 0.00001); - } - - public void testDimensionGetBuiltFromPoints() { - final PDimension dimension = new PDimension(new Point2D.Double(-50, -25), new Point2D.Double(50, 25)); - assertEquals(100, dimension.getWidth(), 0.00001); - assertEquals(50, dimension.getHeight(), 0.00001); - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/util/PObjectOutputStreamTest.java b/core/src/test/java/edu/umd/cs/piccolo/util/PObjectOutputStreamTest.java deleted file mode 100644 index c28b84d..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/util/PObjectOutputStreamTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import junit.framework.TestCase; - -/** - * Unit test for PObjectOutputStream. - */ -public class PObjectOutputStreamTest extends TestCase { - private PObjectOutputStream outStream; - private ByteArrayOutputStream outByteStream; - - public void setUp() throws IOException { - outByteStream = new ByteArrayOutputStream(); - outStream = new PObjectOutputStream(outByteStream); - } - - public void testToByteArrayThrowsExceptionOnNull() throws IOException { - try { - PObjectOutputStream.toByteArray(null); - } - catch (final NullPointerException e) { - // expected - } - } - - public void testToByteArrayOnEmptyStreamWorks() throws IOException { - outStream.flush(); - final byte[] outputBytes = outByteStream.toByteArray(); - assertNotNull(outputBytes); - assertTrue("Header not output", outputBytes.length > 0); - } - - public void testWriteConditionalObjectAcceptsNull() throws IOException { - - } -} diff --git a/core/src/test/java/edu/umd/cs/piccolo/util/PPickPathTest.java b/core/src/test/java/edu/umd/cs/piccolo/util/PPickPathTest.java deleted file mode 100644 index ea3503f..0000000 --- a/core/src/test/java/edu/umd/cs/piccolo/util/PPickPathTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolo.util; - -import junit.framework.TestCase; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; - -/** - * Unit test for PPickPath. - */ -public class PPickPathTest extends TestCase { - - public PPickPathTest(final String name) { - super(name); - } - - public void testPick() { - final PCanvas canvas = new PCanvas(); - final PCamera camera = canvas.getCamera(); - final PLayer layer = canvas.getLayer(); - - camera.setBounds(0, 0, 100, 100); - - final PNode a = PPath.createRectangle(0, 0, 100, 100); - final PNode b = PPath.createRectangle(0, 0, 100, 100); - final PNode c = PPath.createRectangle(0, 0, 100, 100); - - layer.addChild(a); - layer.addChild(b); - layer.addChild(c); - - final PPickPath pickPath = camera.pick(50, 50, 2); - - assertTrue(pickPath.getPickedNode() == c); - assertTrue(pickPath.nextPickedNode() == b); - assertTrue(pickPath.nextPickedNode() == a); - assertTrue(pickPath.nextPickedNode() == camera); - assertTrue(pickPath.nextPickedNode() == null); - assertTrue(pickPath.nextPickedNode() == null); - } -} diff --git a/core/src/test/java/org/piccolo2d/MockPCamera.java b/core/src/test/java/org/piccolo2d/MockPCamera.java new file mode 100644 index 0000000..7f162c9 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/MockPCamera.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.util.ArrayList; +import java.util.List; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; + + +/** + * Mock PCamera. + */ +class MockPCamera extends PCamera { + private static final long serialVersionUID = 1L; + private final List notifications = new ArrayList(); + + public void repaintFromLayer(final PBounds bounds, final PLayer layer) { + notifications.add(new Notification("repaintFromLayer", bounds, layer)); + super.repaintFromLayer(bounds, layer); + } + + static class Notification { + private final String type; + private final PBounds bounds; + // this should really be PLayer + private final PNode layer; + + private Notification(final String type, final PBounds bounds, final PNode layer) { + this.bounds = bounds; + this.layer = layer; + this.type = type; + } + + public PNode getLayer() { + return layer; + } + + public PBounds getBounds() { + return bounds; + } + } + + public int getNotificationCount() { + return notifications.size(); + } + + public Notification getNotification(final int i) { + return (Notification) notifications.get(i); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/piccolo2d/MockPInputEventListener.java b/core/src/test/java/org/piccolo2d/MockPInputEventListener.java new file mode 100644 index 0000000..093f324 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/MockPInputEventListener.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.util.ArrayList; +import java.util.List; + +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventListener; + + +/** + * Mock PInputEventListener. + */ +public class MockPInputEventListener implements PInputEventListener { + static class Notification { + public PInputEvent event; + public int type; + + public Notification(final PInputEvent event, final int type) { + this.event = event; + this.type = type; + } + } + + private final List notifications = new ArrayList(); + + public void processEvent(final PInputEvent aEvent, final int type) { + notifications.add(new Notification(aEvent, type)); + } + + public int getNotificationCount() { + return notifications.size(); + } + + public Notification getNotification(final int index) { + return (Notification) notifications.get(index); + } + +} diff --git a/core/src/test/java/org/piccolo2d/MockPropertyChangeListener.java b/core/src/test/java/org/piccolo2d/MockPropertyChangeListener.java new file mode 100644 index 0000000..886d46b --- /dev/null +++ b/core/src/test/java/org/piccolo2d/MockPropertyChangeListener.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.List; + +/** + * Mock PropertyChangeListener. + */ +public class MockPropertyChangeListener implements PropertyChangeListener { + private final List changes = new ArrayList(); + + public void propertyChange(final PropertyChangeEvent evt) { + changes.add(evt); + } + + public int getPropertyChangeCount() { + return changes.size(); + } + + public PropertyChangeEvent getPropertyChange(final int index) { + return (PropertyChangeEvent) changes.get(index); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/piccolo2d/PCameraTest.java b/core/src/test/java/org/piccolo2d/PCameraTest.java new file mode 100644 index 0000000..286b842 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PCameraTest.java @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Collection; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PComponent; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.activities.PTransformActivity; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDebug; +import org.piccolo2d.util.PNodeFilter; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PPickPath; + +import junit.framework.TestCase; + +/** + * Unit test for PCamera. + */ +public class PCameraTest extends TestCase { + + private PCamera camera; + + public PCameraTest(final String name) { + super(name); + } + + public void setUp() { + camera = new PCamera(); + PDebug.debugBounds = false; + PDebug.debugFullBounds = false; + } + + public void testClone() throws CloneNotSupportedException { + final PLayer layer1 = new PLayer(); + final PLayer layer2 = new PLayer(); + + final PCamera camera1 = new PCamera(); + camera1.addLayer(layer1); + camera1.addLayer(layer2); + + + final PCamera cameraCopy = (PCamera) camera1.clone(); + //TODO: assertEquals(2, cameraCopy.getLayerCount()); + } + + public void testCameraShouldHaveNullComponentUntilAssigned() { + assertNull(camera.getComponent()); + + final MockPComponent component = new MockPComponent(); + camera.setComponent(component); + + assertNotNull(camera.getComponent()); + assertEquals(component, camera.getComponent()); + } + + public void testLayersReferenceIsNotNullByDefault() { + assertNotNull(camera.getLayersReference()); + } + + public void testCameraHasNoLayersByDefault() { + assertEquals(0, camera.getLayerCount()); + } + + public void testIndexOfLayerReturnsMinusOneWhenLayerNotFound() { + final PLayer orphanLayer = new PLayer(); + assertEquals(-1, camera.indexOfLayer(orphanLayer)); + + camera.addLayer(new PLayer()); + assertEquals(-1, camera.indexOfLayer(orphanLayer)); + } + + public void testRemoveLayerByReferenceWorks() { + final PLayer layer = new PLayer(); + camera.addLayer(layer); + camera.removeLayer(layer); + assertEquals(0, camera.getLayerCount()); + } + + public void testRemoveLayerByReferenceDoesNothingWithStrangeLayerWorks() { + final PLayer strangeLayer = new PLayer(); + camera.removeLayer(strangeLayer); + } + + public void testRemoveLayerRemovesTheCameraFromTheLayer() { + final PLayer layer = new PLayer(); + camera.addLayer(layer); + camera.removeLayer(layer); + assertEquals(0, layer.getCameraCount()); + } + + public void testAddingLayerAddCameraToLayer() { + final PLayer layer = new PLayer(); + camera.addLayer(layer); + assertSame(camera, layer.getCamera(0)); + } + + public void testGetFullUnionOfLayerFullBoundsWorks() { + final PLayer layer1 = new PLayer(); + layer1.setBounds(0, 0, 10, 10); + camera.addLayer(layer1); + + final PLayer layer2 = new PLayer(); + layer2.setBounds(10, 10, 10, 10); + camera.addLayer(layer2); + + final PBounds fullLayerBounds = camera.getUnionOfLayerFullBounds(); + assertEquals(new PBounds(0, 0, 20, 20), fullLayerBounds); + } + + public void testPaintPaintsAllLayers() { + final PCanvas canvas = new PCanvas(); + final PCamera camera = canvas.getCamera(); + + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final Graphics2D g2 = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); + + final PLayer layer1 = canvas.getLayer(); + final PNode blueSquare = new PNode(); + blueSquare.setPaint(Color.BLUE); + blueSquare.setBounds(0, 0, 10, 10); + layer1.addChild(blueSquare); + camera.addLayer(layer1); + + final PLayer layer2 = new PLayer(); + canvas.getLayer().getRoot().addChild(layer2); + layer2.setOffset(10, 10); + final PNode redSquare = new PNode(); + redSquare.setPaint(Color.RED); + redSquare.setBounds(0, 0, 10, 10); + layer2.addChild(redSquare); + camera.addLayer(layer2); + + canvas.setBounds(0, 0, 20, 20); + canvas.paint(g2); + + assertEquals(Color.BLUE.getRGB(), img.getRGB(5, 5)); + assertEquals(Color.RED.getRGB(), img.getRGB(15, 15)); + } + + public void testPickPackWorksInSimpleCases() { + final PLayer layer = new PLayer(); + camera.addChild(layer); + + final PNode node1 = new PNode(); + node1.setBounds(0, 0, 10, 10); + layer.addChild(node1); + + final PNode node2 = new PNode(); + node2.setBounds(0, 0, 10, 10); + node2.setOffset(10, 10); + layer.addChild(node2); + + final PPickPath path1 = camera.pick(5, 5, 1); + assertEquals(node1, path1.getPickedNode()); + + final PPickPath path2 = camera.pick(15, 15, 1); + assertEquals(node2, path2.getPickedNode()); + } + + public void testDefaultViewScaleIsOne() { + assertEquals(1, camera.getViewScale(), 0.0001); + } + + public void testGetViewBoundsTransformsCamerasBounds() { + camera.setBounds(0, 0, 100, 100); + camera.getViewTransformReference().scale(10, 10); + assertEquals(new PBounds(0, 0, 10, 10), camera.getViewBounds()); + } + + public void testScaleViewIsCummulative() { + camera.scaleView(2); + assertEquals(2, camera.getViewScale(), 0.001); + camera.scaleView(2); + assertEquals(4, camera.getViewScale(), 0.001); + } + + public void testSetViewScalePersists() { + camera.setViewScale(2); + assertEquals(2, camera.getViewScale(), 0.001); + camera.setViewScale(2); + assertEquals(2, camera.getViewScale(), 0.001); + } + + public void testTranslateViewIsCummulative() { + camera.translateView(100, 100); + assertEquals(100, camera.getViewTransform().getTranslateX(), 0.001); + camera.translateView(100, 100); + assertEquals(200, camera.getViewTransform().getTranslateX(), 0.001); + } + + public void testViewTransformedFiresChangeEvent() { + final MockPropertyChangeListener mockListener = new MockPropertyChangeListener(); + camera.addPropertyChangeListener(PCamera.PROPERTY_VIEW_TRANSFORM, mockListener); + camera.setViewTransform(AffineTransform.getScaleInstance(2, 2)); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testAnimateViewToCenterBoundsIsImmediateWhenDurationIsZero() { + camera.setViewBounds(new PBounds(0, 0, 10, 10)); + final PBounds targetBounds = new PBounds(-5, -5, 10, 10); + final PActivity activity = camera.animateViewToCenterBounds(targetBounds, true, 0); + assertNull(activity); + + assertEquals(-5, camera.getViewTransform().getTranslateX(), 0.001); + assertEquals(-5, camera.getViewTransform().getTranslateY(), 0.001); + } + + public void testAnimateViewToCenterBoundsCreatesValidActivity() { + camera.setViewBounds(new PBounds(0, 0, 10, 10)); + final PBounds targetBounds = new PBounds(-5, -5, 10, 10); + final PActivity activity = camera.animateViewToCenterBounds(targetBounds, true, 100); + assertNotNull(activity); + + assertEquals(100, activity.getDuration()); + assertFalse(activity.isStepping()); + } + + public void testAnimateViewToPanToBoundsDoesNotAffectScale() { + camera.setViewBounds(new PBounds(0, 0, 10, 10)); + camera.animateViewToPanToBounds(new PBounds(10, 10, 10, 30), 0); + + assertEquals(1, camera.getViewScale(), 0.0001); + } + + public void testAnimateViewToPanToBoundsIsImmediateWhenDurationIsZero() { + camera.setViewBounds(new PBounds(0, 0, 10, 10)); + final PActivity activity = camera.animateViewToPanToBounds(new PBounds(10, 10, 10, 10), 0); + + assertNull(activity); + assertEquals(AffineTransform.getTranslateInstance(-15, -15), camera.getViewTransform()); + } + + public void testAnimateViewToPanToBoundsReturnsAppropriatelyConfiguredActivity() { + camera.setViewBounds(new PBounds(0, 0, 10, 10)); + final PTransformActivity activity = camera.animateViewToPanToBounds(new PBounds(10, 10, 10, 10), 100); + + assertNotNull(activity); + assertEquals(100, activity.getDuration()); + assertFalse(activity.isStepping()); + assertEquals(AffineTransform.getTranslateInstance(-15, -15), new PAffineTransform(activity + .getDestinationTransform())); + } + + public void testPDebugDebugBoundsPaintsBounds() throws IOException { + final PCanvas canvas = new PCanvas(); + + final PNode parent = new PNode(); + final PNode child = new PNode(); + + parent.addChild(child); + parent.setBounds(0, 0, 10, 10); + child.setBounds(20, 0, 10, 10); + canvas.setBounds(0, 0, 100, 100); + canvas.setSize(100, 100); + canvas.getLayer().addChild(parent); + + parent.setPaint(Color.GREEN); + child.setPaint(Color.GREEN); + + PDebug.debugBounds = true; + PDebug.debugFullBounds = false; + + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final Graphics2D graphics = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); + graphics.setPaint(Color.WHITE); + graphics.fillRect(0, 0, 100, 100); + final PPaintContext pc = new PPaintContext(graphics); + canvas.setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + canvas.getCamera().paint(pc); + + // First Square's Bounds + assertPointColor(Color.RED, img, 0, 0); + assertPointColor(Color.RED, img, 9, 0); + assertPointColor(Color.RED, img, 10, 10); + assertPointColor(Color.RED, img, 0, 10); + + // Second Square's Bounds + assertPointColor(Color.RED, img, 20, 0); + assertPointColor(Color.RED, img, 29, 0); + assertPointColor(Color.RED, img, 29, 10); + assertPointColor(Color.RED, img, 20, 10); + + // Ensure point between the squares on the full bounds is not drawn + assertPointColor(Color.WHITE, img, 15, 10); + } + + private void assertPointColor(final Color expectedColor, final BufferedImage img, final int x, final int y) { + assertEquals(expectedColor.getRGB(), img.getRGB(x, y)); + } + + public void testSetViewOffsetIsNotCummulative() { + camera.setViewOffset(100, 100); + camera.setViewOffset(100, 100); + assertEquals(100, camera.getViewTransform().getTranslateX(), 0.001); + assertEquals(100, camera.getViewTransform().getTranslateY(), 0.001); + + } + + public void testDefaultViewConstraintsIsNone() { + assertEquals(PCamera.VIEW_CONSTRAINT_NONE, camera.getViewConstraint()); + } + + public void testSetViewContraintsPersists() { + camera.setViewConstraint(PCamera.VIEW_CONSTRAINT_ALL); + assertEquals(PCamera.VIEW_CONSTRAINT_ALL, camera.getViewConstraint()); + camera.setViewConstraint(PCamera.VIEW_CONSTRAINT_CENTER); + assertEquals(PCamera.VIEW_CONSTRAINT_CENTER, camera.getViewConstraint()); + camera.setViewConstraint(PCamera.VIEW_CONSTRAINT_NONE); + assertEquals(PCamera.VIEW_CONSTRAINT_NONE, camera.getViewConstraint()); + } + + public void testSetViewConstraintsThrowsIllegalArgumentException() { + try { + camera.setViewConstraint(-1); + } + catch (IllegalArgumentException e) { + // expected + } + } + + public void testTooFewLayersCamera() { + PCamera tooFew = new TooFewLayersCamera(); + MockPLayer layer0 = new MockPLayer(); + MockPLayer layer1 = new MockPLayer(); + tooFew.addLayer(layer0); + tooFew.addLayer(layer1); + assertEquals(layer0, tooFew.getLayer(0)); + assertEquals(layer1, tooFew.getLayer(1)); + assertEquals(layer0, tooFew.getLayersReference().get(0)); + assertEquals(layer1, tooFew.getLayersReference().get(1)); + assertEquals(0, tooFew.indexOfLayer(layer0)); + assertEquals(0, tooFew.indexOfLayer(layer0)); + + // pickCameraView + PPickPath pickPath = new PPickPath(tooFew, new PBounds(0, 0, 400, 400)); + tooFew.pickCameraView(pickPath); + assertTrue(layer0.fullPickCalled()); + assertTrue(layer1.fullPickCalled()); + + // paintCameraView + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = image.createGraphics(); + PPaintContext paintContext = new PPaintContext(graphics); + tooFew.paintCameraView(paintContext); + assertTrue(layer0.fullPaintCalled()); + assertTrue(layer1.fullPaintCalled()); + + // getUnionOfLayerFullBounds + tooFew.getUnionOfLayerFullBounds(); + assertTrue(layer0.fullBoundsReferenceCalled()); + assertTrue(layer1.fullBoundsReferenceCalled()); + + // paintDebugInfo + PDebug.debugBounds = true; + tooFew.paintDebugInfo(paintContext); + assertTrue(layer0.getAllNodesCalled()); + assertTrue(layer1.getAllNodesCalled()); + PDebug.debugBounds = false; + + graphics.dispose(); + } + + public void testTooManyLayersCamera() { + PCamera tooMany = new TooManyLayersCamera(); + MockPLayer layer0 = new MockPLayer(); + MockPLayer layer1 = new MockPLayer(); + tooMany.addLayer(layer0); + tooMany.addLayer(layer1); + assertEquals(layer0, tooMany.getLayer(0)); + assertEquals(layer1, tooMany.getLayer(1)); + assertEquals(layer0, tooMany.getLayersReference().get(0)); + assertEquals(layer1, tooMany.getLayersReference().get(1)); + assertEquals(0, tooMany.indexOfLayer(layer0)); + assertEquals(0, tooMany.indexOfLayer(layer0)); + + // pickCameraView + PPickPath pickPath = new PPickPath(tooMany, new PBounds(0, 0, 400, 400)); + tooMany.pickCameraView(pickPath); + assertTrue(layer0.fullPickCalled()); + assertTrue(layer1.fullPickCalled()); + + // paintCameraView + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = image.createGraphics(); + PPaintContext paintContext = new PPaintContext(graphics); + tooMany.paintCameraView(paintContext); + assertTrue(layer0.fullPaintCalled()); + assertTrue(layer1.fullPaintCalled()); + + // getUnionOfLayerFullBounds + tooMany.getUnionOfLayerFullBounds(); + assertTrue(layer0.fullBoundsReferenceCalled()); + assertTrue(layer1.fullBoundsReferenceCalled()); + + // paintDebugInfo + PDebug.debugBounds = true; + tooMany.paintDebugInfo(paintContext); + assertTrue(layer0.getAllNodesCalled()); + assertTrue(layer1.getAllNodesCalled()); + PDebug.debugBounds = false; + + graphics.dispose(); + } + + public void testRepaintFromNullParent() { + camera.setParent(null); + PCanvas canvas = new PCanvas(); + camera.setComponent(canvas); + camera.repaintFrom(new PBounds(0, 0, 1, 1), camera); + } + + public void testRepaintFromNullComponent() { + PNode parent = new PNode(); + camera.setParent(parent); + camera.setComponent(null); + camera.repaintFrom(new PBounds(0, 0, 1, 1), camera); + } + + public void testRepaintFromNullParentNullComponent() { + camera.setParent(null); + camera.setComponent(null); + camera.repaintFrom(new PBounds(0, 0, 1, 1), camera); + } + + public void testRepaintFromLayer() { + PLayer layer = new PLayer(); + camera.addLayer(layer); + camera.repaintFromLayer(new PBounds(0, 0, 1, 1), layer); + } + + public void testRepaintFromLayerNotViewedByCamera() { + PLayer layer = new PLayer(); + // todo: layer is not contained in list of layers viewed by camera, should complain + camera.repaintFromLayer(new PBounds(0, 0, 1, 1), layer); + } + + public void testRemoveLayerAtIndex() { + PLayer layer = new PLayer(); + camera.addLayer(layer); + assertEquals(1, camera.getLayerCount()); + assertEquals(1, camera.getLayersReference().size()); + camera.removeLayer(0); + assertEquals(0, camera.getLayerCount()); + assertEquals(0, camera.getLayersReference().size()); + } + + public void testRemoveLayerAtIndexIndexOutOfBounds() { + PLayer layer = new PLayer(); + camera.addLayer(layer); + try { + camera.removeLayer(2); + fail("removeLayer(2) expected IndexOutOfBoundsException"); + } + catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testPaintDebugInfoDebugFullBounds() { + PLayer layer = new PLayer(); + camera.addLayer(layer); + PNode child = new PNode(); + child.setBounds(0.0d, 0.0d, 200.0d, 200.0d); + layer.addChild(child); + BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = image.createGraphics(); + PPaintContext paintContext = new PPaintContext(graphics); + PDebug.debugFullBounds = true; + camera.paintDebugInfo(paintContext); + PDebug.debugFullBounds = false; + graphics.dispose(); + } + + public void testPaintDebugInfoDebugFullBoundsNoChildNodes() { + PLayer layer = new PLayer(); + camera.addLayer(layer); + BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = image.createGraphics(); + PPaintContext paintContext = new PPaintContext(graphics); + PDebug.debugFullBounds = true; + camera.paintDebugInfo(paintContext); + PDebug.debugFullBounds = false; + graphics.dispose(); + } + + public void testPickAfterChildrenNotPicked() { + PPickPath pickPath = new PPickPath(camera, new PBounds(-5, -5, 0, 0)); + assertFalse(camera.pickAfterChildren(pickPath)); + } + + public void testLocalToViewPoint2D() { + Point2D local = new Point2D.Double(0.0d, 0.0d); + camera.localToView(local); + assertEquals(0.0d, local.getX(), 0.1d); + assertEquals(0.0d, local.getY(), 0.1d); + } + + public void testLocalToViewPoint2DTranslateView() { + camera.translateView(10.0d, 20.0d); + Point2D local = new Point2D.Double(0.0d, 0.0d); + camera.localToView(local); + assertEquals(-10.0d, local.getX(), 0.1d); + assertEquals(-20.0d, local.getY(), 0.1d); + } + + public void testLocalToViewPoint2DScaleView() { + camera.scaleView(10.0d); + Point2D local = new Point2D.Double(10.0d, 20.0d); + camera.localToView(local); + assertEquals(1.0d, local.getX(), 0.1d); + assertEquals(2.0d, local.getY(), 0.1d); + } + + public void testLocalToViewDimension2D() { + Dimension2D local = new Dimension(0, 0); + camera.localToView(local); + assertEquals(0.0d, local.getWidth(), 0.1d); + assertEquals(0.0d, local.getHeight(), 0.1d); + } + + public void testLocalToViewDimension2DTranslateView() { + camera.translateView(10.0d, 20.0d); + Dimension2D local = new Dimension(0, 0); + camera.localToView(local); + assertEquals(0.0d, local.getWidth(), 0.1d); + assertEquals(0.0d, local.getHeight(), 0.1d); + } + + public void testLocalToViewDimension2DScaleView() { + camera.scaleView(10.0d); + Dimension2D local = new Dimension(10, 20); + camera.localToView(local); + assertEquals(1.0d, local.getWidth(), 0.1d); + assertEquals(2.0d, local.getHeight(), 0.1d); + } + + public void testViewToLocalPoint2D() { + Point2D view = new Point2D.Double(0.0d, 0.0d); + camera.viewToLocal(view); + assertEquals(0.0d, view.getX(), 0.1d); + assertEquals(0.0d, view.getY(), 0.1d); + } + + public void testViewToLocalPoint2DTranslateView() { + camera.translateView(10.0d, 20.0d); + Point2D view = new Point2D.Double(0.0d, 0.0d); + camera.viewToLocal(view); + assertEquals(10.0d, view.getX(), 0.1d); + assertEquals(20.0d, view.getY(), 0.1d); + } + + public void testViewToLocalPoint2DScaleView() { + camera.scaleView(10.0d); + Point2D view = new Point2D.Double(10.0d, 20.0d); + camera.viewToLocal(view); + assertEquals(100.0d, view.getX(), 0.1d); + assertEquals(200.0d, view.getY(), 0.1d); + } + + public void testViewToLocalDimension2D() { + Dimension2D view = new Dimension(0, 0); + camera.viewToLocal(view); + assertEquals(0.0d, view.getWidth(), 0.1d); + assertEquals(0.0d, view.getHeight(), 0.1d); + } + + public void testViewToLocalDimension2DTranslateView() { + camera.translateView(10.0d, 20.0d); + Dimension2D view = new Dimension(0, 0); + camera.viewToLocal(view); + assertEquals(0.0d, view.getWidth(), 0.1d); + assertEquals(0.0d, view.getHeight(), 0.1d); + } + + public void testViewToLocalDimension2DScaleView() { + camera.scaleView(10.0d); + Dimension2D view = new Dimension(10, 20); + camera.viewToLocal(view); + assertEquals(100.0d, view.getWidth(), 0.1d); + assertEquals(200.0d, view.getHeight(), 0.1d); + } + + public void testPickWithoutIntersectionStillContainsCamera() { + camera.offset(10.0d, 10.0d); + PPickPath pickPath = camera.pick(0.0d, 0.0d, 0.0d); + // todo: don't understand why this should be the case + assertFalse(pickPath.getNodeStackReference().isEmpty()); + assertTrue(pickPath.getNodeStackReference().contains(camera)); + } + + /* + public void testAnimateViewToTransformIdentity() { + PRoot root = new PRoot(); + PLayer layer = new PLayer(); + root.addChild(camera); + root.addChild(layer); + camera.addChild(layer); + + AffineTransform identity = new AffineTransform(); + camera.animateViewToTransform(identity, System.currentTimeMillis()); + // todo: throws NPE at PActivityScheduler.processActivities(PActivityScheduler.java:176) + root.waitForActivities(); + + assertSame(identity, camera.getViewTransformReference()); + } + */ + + + static class MockPComponent implements PComponent { + + public void paintImmediately() { + } + + public void popCursor() { + } + + public void pushCursor(final Cursor cursor) { + } + + public void repaint(final PBounds bounds) { + } + + public void setInteracting(final boolean interacting) { + } + } + + /** + * Mock PLayer. Should consider using mock library in version 2.0. + */ + private static final class MockPLayer extends PLayer { + private static final long serialVersionUID = 1L; + private boolean fullBoundsReferenceCalled = false; + private boolean fullPaintCalled = false; + private boolean getAllNodesCalled = false; + private boolean fullPickCalled = false; + + /** {@inheritDoc} */ + public PBounds getFullBoundsReference() { + fullBoundsReferenceCalled = true; + return super.getFullBoundsReference(); + } + + /** {@inheritDoc} */ + public void fullPaint(final PPaintContext paintContext) { + fullPaintCalled = true; + super.fullPaint(paintContext); + } + + /** {@inheritDoc} */ + public Collection getAllNodes(final PNodeFilter filter, final Collection nodes) { + getAllNodesCalled = true; + return super.getAllNodes(filter, nodes); + } + + /** {@inheritDoc} */ + public boolean fullPick(final PPickPath pickPath) { + fullPickCalled = true; + return super.fullPick(pickPath); + } + + private boolean fullBoundsReferenceCalled() { + return fullBoundsReferenceCalled; + } + + private boolean fullPaintCalled() { + return fullPaintCalled; + } + + private boolean getAllNodesCalled() { + return getAllNodesCalled; + } + + private boolean fullPickCalled() { + return fullPickCalled; + } + } + + /** + * Subclass of PCamera that advertises too few layers. + */ + private static final class TooFewLayersCamera extends PCamera { + private static final long serialVersionUID = 1L; + + /** {@inheritDoc} */ + public int getLayerCount() { + return Math.max(0, super.getLayerCount() - 1); + } + } + + /** + * Subclass of PCamera that advertises too many layers. + */ + private static final class TooManyLayersCamera extends PCamera { + private static final long serialVersionUID = 1L; + + /** {@inheritDoc} */ + public int getLayerCount() { + return super.getLayerCount() + 1; + } + } +} diff --git a/core/src/test/java/org/piccolo2d/PCanvasTest.java b/core/src/test/java/org/piccolo2d/PCanvasTest.java new file mode 100644 index 0000000..9399479 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PCanvasTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.Cursor; + +import org.piccolo2d.PCanvas; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + +import junit.framework.TestCase; + +/** + * Unit test for PCanvas. + */ +public class PCanvasTest extends TestCase { + private PCanvas canvas; + private MockPInputEventListener mockListener; + + public void setUp() { + canvas = new PCanvas(); + mockListener = new MockPInputEventListener(); + } + + public void testDefaultPanHandlerIsNotNull() { + assertNotNull(canvas.getPanEventHandler()); + } + + public void testGetInteractingReturnsFalseByDefault() { + assertFalse(canvas.getInteracting()); + } + + public void testDefaultNumberOfEventListenersIs2() { + final PInputEventListener[] listeners = canvas.getInputEventListeners(); + assertNotNull(listeners); + assertEquals(2, listeners.length); + } + + public void testGetAnimatingReturnsFalseByDefault() { + assertFalse(canvas.getAnimating()); + } + + public void testSetInteractingPersists() { + canvas.setInteracting(true); + assertTrue(canvas.getInteracting()); + } + + public void testDefaultAnimatingRenderQualityIsLow() { + assertEquals(PPaintContext.LOW_QUALITY_RENDERING, canvas.getAnimatingRenderQuality()); + } + + public void testDefaultInteractingRenderQualityIsLow() { + assertEquals(PPaintContext.LOW_QUALITY_RENDERING, canvas.getInteractingRenderQuality()); + } + + public void testDefaultZoomHandlerIsNotNull() { + assertNotNull(canvas.getZoomEventHandler()); + } + + public void testCanvasLayerIsNotNullByDefault() { + assertNotNull(canvas.getLayer()); + } + + public void testCursorStackWorksAsExpected() { + final Cursor moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); + final Cursor handCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + final Cursor crosshairCursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); + + canvas.pushCursor(moveCursor); + canvas.pushCursor(handCursor); + canvas.pushCursor(crosshairCursor); + + assertEquals(crosshairCursor, canvas.getCursor()); + canvas.popCursor(); + assertEquals(handCursor, canvas.getCursor()); + canvas.popCursor(); + assertEquals(moveCursor, canvas.getCursor()); + } + + public void testPoppingEmptyCursorStackShouldDoNothing() { + try { + canvas.popCursor(); + } + catch (final IndexOutOfBoundsException e) { + fail("Pop cursor shouldn't fail on an empty stack"); + } + assertEquals(Cursor.getDefaultCursor(), canvas.getCursor()); + } + + public void testSettingCanvasBoundsAffectsCameraBounds() { + canvas.setBounds(0, 0, 100, 100); + assertEquals(new PBounds(0, 0, 100, 100), canvas.getCamera().getBounds()); + } + + public void testAddInputEventListenersIsHonoured() { + canvas.addInputEventListener(mockListener); + final PInputEventListener[] listeners = canvas.getInputEventListeners(); + assertNotNull(listeners); + assertEquals(3, listeners.length); // zoom + pan + mockListener + // by default + } + + public void testRemoveInputEventListenersIsHonoured() { + canvas.addInputEventListener(mockListener); + canvas.removeInputEventListener(mockListener); + final PInputEventListener[] listeners = canvas.getInputEventListeners(); + assertNotNull(listeners); + assertEquals(2, listeners.length); // zoom + pan + mockListener + } +} diff --git a/core/src/test/java/org/piccolo2d/PInputManagerTest.java b/core/src/test/java/org/piccolo2d/PInputManagerTest.java new file mode 100644 index 0000000..0ea98b2 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PInputManagerTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.Point2D; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PInputManager; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; + +import junit.framework.TestCase; + +/** + * Unit test for PInputManager. + */ +public class PInputManagerTest extends TestCase { + private PInputManager manager; + private MockPInputEventListener mockListener; + + public void setUp() { + manager = new PInputManager(); + mockListener = new MockPInputEventListener(); + } + + public void testGetKeyboardFocusNullByDefault() { + assertNull(manager.getKeyboardFocus()); + } + + public void testSetKeyboardFocusIsPersisted() { + manager.setKeyboardFocus(mockListener); + assertEquals(mockListener, manager.getKeyboardFocus()); + } + + public void testSetKeyboardFocusDispatchesEventsAboutFocus() { + final MockPInputEventListener oldListener = new MockPInputEventListener(); + manager.setKeyboardFocus(oldListener); + + assertEquals(1, oldListener.getNotificationCount()); + assertEquals(FocusEvent.FOCUS_GAINED, oldListener.getNotification(0).type); + + final MockPInputEventListener newListener = new MockPInputEventListener(); + manager.setKeyboardFocus(newListener); + + assertEquals(1, newListener.getNotificationCount()); + assertEquals(FocusEvent.FOCUS_GAINED, newListener.getNotification(0).type); + assertEquals(2, oldListener.getNotificationCount()); + assertEquals(FocusEvent.FOCUS_LOST, oldListener.getNotification(1).type); + } + + public void testGetMouseFocusNullByDefault() { + assertNull(manager.getMouseFocus()); + } + + public void testSetMouseFocusPersists() { + final PCamera camera = new PCamera(); + final PPickPath path = new PPickPath(camera, new PBounds(0, 0, 10, 10)); + manager.setMouseFocus(path); + assertEquals(path, manager.getMouseFocus()); + } + + public void testGetMouseOverNullByDefault() { + assertNull(manager.getMouseOver()); + } + + public void testSetMouseOverPersists() { + final PCamera camera = new PCamera(); + final PPickPath path = new PPickPath(camera, new PBounds(0, 0, 10, 10)); + manager.setMouseOver(path); + assertEquals(path, manager.getMouseOver()); + } + + public void testGetCurrentCanvasPositionIsOriginByDefault() { + assertEquals(new Point2D.Double(0, 0), manager.getCurrentCanvasPosition()); + } + + public void testGetLastCanvasPositionIsOriginByDefault() { + assertEquals(new Point2D.Double(0, 0), manager.getLastCanvasPosition()); + } + + public void testKeyPressedDispatchesToCurrentFocus() { + manager.setKeyboardFocus(mockListener); + final PInputEvent event = new PInputEvent(manager, null); + manager.keyPressed(event); + assertEquals(2, mockListener.getNotificationCount()); + assertEquals(KeyEvent.KEY_PRESSED, mockListener.getNotification(1).type); + } + + public void testKeyReleasedDispatchesToCurrentFocus() { + manager.setKeyboardFocus(mockListener); + final PInputEvent event = new PInputEvent(manager, null); + manager.keyReleased(event); + assertEquals(2, mockListener.getNotificationCount()); + assertEquals(KeyEvent.KEY_RELEASED, mockListener.getNotification(1).type); + } + + public void testKeyTypedDispatchesToCurrentFocus() { + manager.setKeyboardFocus(mockListener); + final PInputEvent event = new PInputEvent(manager, null); + manager.keyTyped(event); + assertEquals(2, mockListener.getNotificationCount()); + assertEquals(KeyEvent.KEY_TYPED, mockListener.getNotification(1).type); + } + + public void testProcessInputMayBeCalledOnFreshManager() { + manager.processInput(); + } + +} diff --git a/core/src/test/java/org/piccolo2d/PLayerTest.java b/core/src/test/java/org/piccolo2d/PLayerTest.java new file mode 100644 index 0000000..e47b009 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PLayerTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.util.Collection; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.util.PBounds; + +import junit.framework.TestCase; + +/** + * Unit test for PLayer. + */ +public class PLayerTest extends TestCase { + private PLayer layer; + + public void setUp() { + layer = new PLayer(); + } + + public void testLayerHasEmptyCamerasCollectionByDefault() { + final Collection cameras = layer.getCamerasReference(); + assertNotNull(cameras); + assertTrue(cameras.isEmpty()); + assertEquals(0, layer.getCameraCount()); + } + + public void testGetCameraByIndexThrowsIndexOutOfBoundsExceptionWhenOutOfBounds() { + final PCamera camera = new PCamera(); + layer.addCamera(camera); + try { + layer.getCamera(-1); + fail("Exception should have been thrown"); + } + catch (final IndexOutOfBoundsException e) { + // expected; + } + + try { + layer.getCamera(1); + fail("Exception should have been thrown"); + } + catch (final IndexOutOfBoundsException e) { + // expected; + } + } + + public void testGetCameraReturnsCameraAtCorrectIndex() { + final PCamera camera1 = new PCamera(); + final PCamera camera2 = new PCamera(); + final PCamera camera3 = new PCamera(); + + layer.addCamera(camera1); + layer.addCamera(camera2); + layer.addCamera(camera3); + + assertEquals(camera1, layer.getCamera(0)); + assertEquals(camera2, layer.getCamera(1)); + assertEquals(camera3, layer.getCamera(2)); + } + + public void testAddCameraCorrectlyHandlesIndex() { + final PCamera camera1 = new PCamera(); + final PCamera camera2 = new PCamera(); + final PCamera camera3 = new PCamera(); + + layer.addCamera(0, camera1); + layer.addCamera(0, camera2); + layer.addCamera(1, camera3); + + assertEquals(camera2, layer.getCamera(0)); + assertEquals(camera3, layer.getCamera(1)); + assertEquals(camera1, layer.getCamera(2)); + } + + public void testRemovingCameraByReferenceWorksWhenCameraIsFound() { + final PCamera camera = new PCamera(); + layer.addCamera(camera); + layer.removeCamera(camera); + assertEquals(0, layer.getCameraCount()); + } + + public void testRemovingCameraByIndexWorksWhenIndexIsValid() { + final PCamera camera = new PCamera(); + layer.addCamera(camera); + layer.removeCamera(0); + assertEquals(0, layer.getCameraCount()); + } + + public void testRemovingCameraNotAttachedToCameraShouldDoNothing() { + final PCamera strangerCamera = new PCamera(); + layer.removeCamera(strangerCamera); + assertEquals(0, layer.getCameraCount()); + } + + public void testRepaintFromNotifiesCameras() { + final MockPCamera camera = new MockPCamera(); + layer.addCamera(camera); + + final PBounds bounds = new PBounds(0, 0, 100, 100); + layer.repaintFrom(bounds, layer); + + assertEquals(1, camera.getNotificationCount()); + + final MockPCamera.Notification notification = camera.getNotification(0); + assertEquals(layer, notification.getLayer()); + assertEquals(bounds, notification.getBounds()); + } +} diff --git a/core/src/test/java/org/piccolo2d/PNodeTest.java b/core/src/test/java/org/piccolo2d/PNodeTest.java new file mode 100644 index 0000000..1a60652 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PNodeTest.java @@ -0,0 +1,1452 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.GraphicsEnvironment; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.ListIterator; + +import javax.swing.text.MutableAttributeSet; + +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.activities.PColorActivity; +import org.piccolo2d.activities.PInterpolatingActivity; +import org.piccolo2d.activities.PTransformActivity; +import org.piccolo2d.activities.PColorActivity.Target; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PAffineTransformException; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PNodeFilter; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PPickPath; +import org.piccolo2d.util.PUtil; + +import junit.framework.TestCase; + +/** + * Unit test for PNode. + */ +public class PNodeTest extends TestCase { + + private MockPropertyChangeListener mockListener; + private PNode node; + + public PNodeTest(final String name) { + super(name); + } + + public void setUp() { + node = new PNode(); + mockListener = new MockPropertyChangeListener(); + } + + public void testCenterBaseBoundsOnPoint() { + node.setBounds(100, 300, 100, 80); + node.centerBoundsOnPoint(0, 0); + assertEquals(-50, node.getBoundsReference().getX(), 0); + assertEquals(-40, node.getBoundsReference().getY(), 0); + } + + public void testClientProperties() { + final PNode n = new PNode(); + + assertNull(n.getAttribute(null)); + n.addAttribute("a", "b"); + assertEquals(n.getAttribute("a"), "b"); + assertNull(n.getAttribute(null)); + n.addAttribute("a", null); + assertNull(n.getAttribute("a")); + } + + public void testFullScale() { + final PNode aParent = new PNode(); + final PNode aNode = new PNode(); + + aParent.addChild(aNode); + + aParent.scale(2.0); + aNode.scale(0.5); + + assertEquals(1.0, aNode.getGlobalScale(), 0); + + aParent.setScale(1.0); + assertEquals(0.5, aNode.getGlobalScale(), 0); + + aNode.setScale(.75); + assertEquals(0.75, aNode.getGlobalScale(), 0); + } + + public void testReparent() { + final PNode aParent = new PNode(); + final PNode aNode = new PNode(); + + aParent.setOffset(400, 500); + aParent.scale(0.5); + aNode.reparent(aParent); + + assertEquals(0, aNode.getGlobalTranslation().getX(), 0); + assertEquals(0, aNode.getGlobalTranslation().getY(), 0); + assertEquals(2.0, aNode.getScale(), 0); + + aNode.setGlobalScale(0.25); + aNode.setGlobalTranslation(new Point2D.Double(10, 10)); + + assertEquals(10, aNode.getGlobalTranslation().getX(), 0); + assertEquals(10, aNode.getGlobalTranslation().getY(), 0); + assertEquals(0.25, aNode.getGlobalScale(), 0); + } + + public void testFindIntersectingNodes() { + final PNode n = new PNode(); + final PNode c = new PNode(); + + n.addChild(c); + n.setBounds(0, 0, 100, 100); + c.setBounds(0, 0, 100, 100); + c.scale(200); + + ArrayList found = new ArrayList(); + final Rectangle2D rect2d = new Rectangle2D.Double(50, 50, 10, 10); + n.findIntersectingNodes(rect2d, found); + + assertEquals(found.size(), 2); + assertEquals(rect2d.getHeight(), 10, 0); + found = new ArrayList(); + + final PBounds bounds = new PBounds(50, 50, 10, 10); + n.findIntersectingNodes(bounds, found); + + assertEquals(found.size(), 2); + assertEquals(bounds.getHeight(), 10, 0); + } + + public void testRemoveNonexistantListener() { + final PNode n = new PNode(); + n.removeInputEventListener(new PBasicInputEventHandler()); + } + + public void testAddChildHandleDuplicates() { + final PNode parent = new PNode(); + parent.addChild(node); + parent.addChild(new PNode()); + parent.addChild(node); + assertEquals(1, parent.indexOfChild(node)); + } + + public void testAddChildCanSpecifyAnIndexAndDoesntReplace() { + final PNode parent = new PNode(); + parent.addChild(new PNode()); + parent.addChild(0, node); + assertEquals(0, parent.indexOfChild(node)); + assertEquals(2, parent.getChildrenCount()); + } + + public void testAddChildWithIndexMovesChildAround() { + final PNode parent = new PNode(); + + parent.addChild(new PNode()); + parent.addChild(new PNode()); + parent.addChild(node); + + parent.addChild(0, node); + assertEquals(node, parent.getChild(0)); + + parent.addChild(1, node); + assertEquals(node, parent.getChild(1)); + + parent.addChild(2, node); + assertEquals(node, parent.getChild(2)); + } + + 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 testCloneDoesNotCopyEventListeners() { + node.addInputEventListener(new PBasicInputEventHandler() {}); + + final PNode clonedNode = (PNode) node.clone(); + + assertNull(clonedNode.getListenerList()); + } + + public void testCloneClonesChildrenAswell() { + final PNode child = new PNode(); + node.addChild(child); + + final PNode clonedNode = (PNode) node.clone(); + + assertEquals(clonedNode.getChildrenCount(), 1); + assertNotSame(child, clonedNode.getChild(0)); + } + + public void testCloneDoesADeepCopy() { + final PNode child = new PNode(); + node.addChild(child); + + final PNode clonedNode = (PNode) node.clone(); + + assertNotSame(node.getChildrenReference(), clonedNode.getChildrenReference()); + assertNotSame(node.getChild(0), clonedNode.getChild(0)); + + assertNotSame(node.getBoundsReference(), clonedNode.getBoundsReference()); + } + + public void testCloneDoesNotCopyParent() { + final PNode child = new PNode(); + node.addChild(child); + + final PNode clonedChild = (PNode) child.clone(); + + assertNull(clonedChild.getParent()); + } + + public void testLocalToGlobal() { + final PNode aParent = new PNode(); + final PNode aChild = new PNode(); + + aParent.addChild(aChild); + aChild.scale(0.5); + + // bounds + final PBounds bnds = new PBounds(0, 0, 50, 50); + + aChild.localToGlobal(bnds); + assertEquals(0, bnds.x, 0); + assertEquals(0, bnds.y, 0); + assertEquals(25, bnds.width, 0); + assertEquals(25, bnds.height, 0); + + aChild.globalToLocal(bnds); + assertEquals(0, bnds.x, 0); + assertEquals(0, bnds.y, 0); + assertEquals(50, bnds.width, 0); + assertEquals(50, bnds.height, 0); + + aChild.getGlobalToLocalTransform(new PAffineTransform()); + aChild.getLocalToGlobalTransform(new PAffineTransform()).createTransformedShape(aChild.getBounds()); + + // dimensions + final PDimension dim = new PDimension(50, 50); + + aChild.localToGlobal(dim); + assertEquals(25, dim.getHeight(), 0); + assertEquals(25, dim.getWidth(), 0); + + aChild.globalToLocal(dim); + assertEquals(50, dim.getHeight(), 0); + assertEquals(50, dim.getWidth(), 0); + } + + public void testToString() { + final PNode a = new PNode(); + final PNode b = new PNode(); + final PNode c = new PNode(); + final PNode d = new PNode(); + final PNode e = new PNode(); + final PNode f = new PNode(); + + a.translate(100, 100); + a.getFullBoundsReference(); + + a.addChild(b); + b.addChild(c); + c.addChild(d); + d.addChild(e); + e.addChild(f); + + assertNotNull(a.toString()); + } + + public void testRecursiveLayout() { + final PNode layoutNode1 = new PNode() { + /** + * + */ + private static final long serialVersionUID = 1L; + + protected void layoutChildren() { + if (getChildrenCount() > 0) { + getChild(0).setOffset(1, 0); + } + } + }; + + final PNode layoutNode2 = new PNode() { + /** + * + */ + private static final long serialVersionUID = 1L; + + protected void layoutChildren() { + if (getChildrenCount() > 0) { + getChild(0).setOffset(1, 0); + } + } + }; + + layoutNode1.addChild(layoutNode2); + + final PNode n = new PNode(); + n.setBounds(0, 0, 100, 100); + + layoutNode2.addChild(n); + + n.setBounds(10, 10, 100, 100); + + layoutNode1.getFullBoundsReference(); + } + + public void testAnimateToBoundsWithDuration0IsImmediate() { + node.setBounds(0, 0, 100, 100); + + final PActivity activity = node.animateToBounds(50, 50, 150, 150, 0); + assertNull(activity); + + final PBounds resultBounds = node.getBounds(); + assertEquals(50.0, resultBounds.x, 0.001); + assertEquals(50.0, resultBounds.y, 0.001); + assertEquals(150.0, resultBounds.width, 0.001); + assertEquals(150.0, resultBounds.height, 0.001); + } + + public void testAnimateToBoundsHasProperSetup() { + node.setBounds(0, 0, 100, 100); + final PInterpolatingActivity activity = node.animateToBounds(50, 50, 150, 150, 50); + + assertEquals(50, activity.getDuration()); + assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); + assertTrue(activity.getFirstLoop()); + assertFalse(activity.isStepping()); + } + + public void testAnimateTransformToBoundsWithDuration0IsImmediate() { + node.setBounds(0, 0, 100, 100); + final PActivity activity = node.animateTransformToBounds(0, 0, 10, 10, 0); + + assertNull(activity); + + final PAffineTransform transform = node.getTransform(); + assertEquals(0.1, transform.getScale(), 0.0001); + } + + public void testAnimateTransformToBoundsHasProperSetup() { + node.setBounds(0, 0, 100, 100); + final PTransformActivity activity = node.animateTransformToBounds(0, 0, 10, 10, 50); + + assertEquals(50, activity.getDuration()); + assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); + assertTrue(activity.getFirstLoop()); + assertFalse(activity.isStepping()); + + final double[] resultTransform = activity.getDestinationTransform(); + + assertEquals(0.1, resultTransform[0], 0.001); + assertEquals(0, resultTransform[1], 0.001); + assertEquals(0, resultTransform[2], 0.001); + assertEquals(0.1, resultTransform[3], 0.001); + assertEquals(0, resultTransform[4], 0.001); + assertEquals(0, resultTransform[5], 0.001); + } + + public void testAnimateToPositionScaleRotationWithDuration0IsImmediate() { + node.setBounds(0, 0, 100, 100); + final PActivity activity = node.animateToPositionScaleRotation(50, 50, 0.5, Math.PI, 0); + + assertNull(activity); + + final PAffineTransform resultTransform = node.getTransform(); + + final PAffineTransform expected = new PAffineTransform(); + expected.translate(50, 50); + expected.scale(0.5, 0.5); + expected.rotate(Math.PI); + + assertEquals(expected, resultTransform); + } + + public void testAnimateToPositionScaleRotationHasProperSetup() { + node.setBounds(0, 0, 100, 100); + final PTransformActivity activity = node.animateToPositionScaleRotation(50, 50, 0.5, Math.PI, 50); + + assertEquals(50, activity.getDuration()); + assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); + assertTrue(activity.getFirstLoop()); + assertFalse(activity.isStepping()); + + final double[] resultTransform = activity.getDestinationTransform(); + + final PAffineTransform expected = new PAffineTransform(); + expected.translate(50, 50); + expected.scale(0.5, 0.5); + expected.rotate(Math.PI); + + assertEquals(-0.5, resultTransform[0], 0.001); + assertEquals(0, resultTransform[1], 0.001); + assertEquals(0, resultTransform[2], 0.001); + assertEquals(-0.5, resultTransform[3], 0.001); + assertEquals(50.0, resultTransform[4], 0.001); + assertEquals(50.0, resultTransform[5], 0.001); + } + + public void testAnimateToColorWithDuration0IsImmediate() { + node.setPaint(Color.WHITE); + + final PActivity activity = node.animateToColor(Color.BLACK, 0); + + assertNull(activity); + + assertEquals(Color.BLACK, node.getPaint()); + } + + public void testAnimateToColorHasProperSetup() { + node.setPaint(Color.WHITE); + final PInterpolatingActivity activity = node.animateToColor(Color.BLACK, 50); + + assertEquals(50, activity.getDuration()); + assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); + assertTrue(activity.getFirstLoop()); + assertFalse(activity.isStepping()); + // assertEquals(Color.BLACK, activity.getDestinationColor()); + assertEquals("Paint should not change immediately", Color.WHITE, node.getPaint()); + } + + public void testAddActivityAddsActivityToScheduler() { + final PCanvas canvas = new PCanvas(); + node.setPaint(Color.WHITE); + canvas.getLayer().addChild(node); + + final PColorActivity activity = buildTestActivity(); + + node.addActivity(activity); + + assertEquals(1, node.getRoot().getActivityScheduler().getActivitiesReference().size()); + } + + private PColorActivity buildTestActivity() { + final Target testTarget = new PColorActivity.Target() { + + public Color getColor() { + return Color.BLACK; + } + + public void setColor(final Color color) { + + } + }; + + final PColorActivity activity = new PColorActivity(1000, 0, testTarget, Color.BLACK); + return activity; + } + + public void testAnimateToTransparencyWithDuration0IsImmediate() { + node.setPaint(Color.WHITE); + + final PActivity activity = node.animateToTransparency(0.5f, 0); + + assertNull(activity); + + assertEquals(0.5f, node.getTransparency(), 0.0001); + } + + public void testAnimateToTransparencyHasProperSetup() { + final PInterpolatingActivity activity = node.animateToTransparency(0f, 50); + + assertEquals(50, activity.getDuration()); + assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); + assertTrue(activity.getFirstLoop()); + assertFalse(activity.isStepping()); + + assertEquals("Transparency should not change immediately", 1.0f, node.getTransparency(), 0.0001); + } + + public void testGetClientPropertiesShouldReturnSetEvenIfNonePresent() { + final MutableAttributeSet properties = node.getClientProperties(); + assertNotNull(properties); + assertEquals(0, properties.getAttributeCount()); + } + + public void testGetClientPropertiesShouldReturnSameCollectionAlways() { + final MutableAttributeSet properties1 = node.getClientProperties(); + final MutableAttributeSet properties2 = node.getClientProperties(); + assertSame(properties1, properties2); + } + + public void testGetClientPropertyKeysEnumerationShouldReturnEnumarationOnNewNode() { + final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); + assertNotNull(enumeration); + assertFalse(enumeration.hasMoreElements()); + } + + public void testGetClientPropertyKeysEnumerationShouldReturnCorrectEnumWhenPropertiesExist() { + node.addAttribute("Testing", "Hello"); + final Enumeration enumeration = node.getClientPropertyKeysEnumeration(); + assertNotNull(enumeration); + assertTrue(enumeration.hasMoreElements()); + assertEquals("Testing", enumeration.nextElement()); + assertFalse(enumeration.hasMoreElements()); + } + + public void testGetAttributeReturnsNullWhenMissing() { + assertNull(node.getAttribute("Testing")); + } + + public void testGetAttributeReturnsValueWhenPresent() { + node.addAttribute("Testing", "Hello"); + assertEquals("Hello", node.getAttribute("Testing")); + } + + public void testGetAttributeReturnsDefaultWhenProvided() { + assertEquals("Default", node.getAttribute("Missing", "Default")); + } + + public void testGetAttributeReturnsValueIfFoundWhenDefaultProvided() { + node.addAttribute("Found", "Hello"); + assertEquals("Hello", node.getAttribute("Found", "Default")); + } + + public void testGetBooleanAttributeReturnsDefaultWhenProvided() { + assertEquals(false, node.getBooleanAttribute("Missing", false)); + } + + public void testGetBooleanAttributeReturnsValueIfFoundWhenDefaultProvided() { + node.addAttribute("Found", Boolean.TRUE); + assertEquals(true, node.getBooleanAttribute("Found", false)); + } + + public void testGetIntegerAttributeReturnsDefaultWhenProvided() { + assertEquals(10, node.getIntegerAttribute("Missing", 10)); + } + + public void testGetIntegerAttributeReturnsValueIfFoundWhenDefaultProvided() { + node.addAttribute("Found", new Integer(5)); + assertEquals(5, node.getIntegerAttribute("Found", 10)); + } + + public void testGetDoubleAttributeReturnsDefaultWhenProvided() { + assertEquals(10, node.getDoubleAttribute("Missing", 10), 0.001); + } + + public void testGetDoubleAttributeReturnsValueIfFoundWhenDefaultProvided() { + node.addAttribute("Found", new Double(5)); + assertEquals(5, node.getIntegerAttribute("Found", 10), 0.001); + } + + public void testLocalToParentModifiesGivenPoint() { + final PNode parent = new PNode(); + parent.addChild(node); + + node.scale(0.5); + + final Point2D point = new Point2D.Double(5, 6); + node.localToParent(point); + assertTrue(5 != point.getX()); + assertTrue(6 != point.getY()); + } + + public void testLocalToParentDoesWorkWithOrphanChildWhenTransformed() { + node.scale(0.5); + + final Point2D point = new Point2D.Double(5, 6); + node.localToParent(point); + assertTrue(5 != point.getX()); + assertTrue(6 != point.getY()); + } + + public void testLocalToParentDoesNothingWithOrphanChildWhenNotTransformed() { + final Point2D point = new Point2D.Double(5, 6); + node.localToParent(point); + assertEquals(5, point.getX(), 0.0001); + assertEquals(6, point.getY(), 0.0001); + } + + public void testParentToLocalModifiesGivenPoint() { + final PNode parent = new PNode(); + parent.addChild(node); + + node.scale(0.5); + + final Point2D point = new Point2D.Double(5, 6); + node.parentToLocal(point); + assertTrue(5 != point.getX()); + assertTrue(6 != point.getY()); + } + + public void testParentToLocalTransformsOrphanChildWhenTransformed() { + final PNode aChild = new PNode(); + aChild.scale(0.5); + + final Point2D point = new Point2D.Double(5, 6); + aChild.parentToLocal(point); + assertEquals(10, point.getX(), 0.0001); + assertEquals(12, point.getY(), 0.0001); + } + + public void testGlobalToLocalWorksUnTransformedNodes() { + final PNode parent = new PNode(); + parent.addChild(node); + + final Point2D point = new Point2D.Double(10, 11); + node.globalToLocal(point); + assertEquals(10, point.getX(), 0.0001); + assertEquals(11, point.getY(), 0.0001); + } + + public void testRemoveEventListener() { + final PBasicInputEventHandler eventListener = new PBasicInputEventHandler(); + node.addInputEventListener(eventListener); + assertEquals(1, node.getListenerList().getListenerCount()); + node.removeInputEventListener(eventListener); + assertNull(node.getListenerList()); + + } + + public void testAddPropertyChangeListener() { + node.addPropertyChangeListener(mockListener); + node.setBounds(0, 0, 100, 100); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testAddPropertyChangeListenerForPropertyName() { + node.addPropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); + node.setBounds(0, 0, 100, 100); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testRemovePropertyChangeListener() { + node.addPropertyChangeListener(mockListener); + node.removePropertyChangeListener(mockListener); + node.setBounds(0, 0, 100, 100); + assertEquals(0, mockListener.getPropertyChangeCount()); + } + + public void testRemovePropertyChangeListenerForPropertyName() { + node.addPropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); + node.removePropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); + node.setBounds(0, 0, 100, 100); + assertEquals(0, mockListener.getPropertyChangeCount()); + } + + public void testPropertyChangesCascadeToParent() { + final PNode aParent = new PNode(); + aParent.addPropertyChangeListener(PNode.PROPERTY_BOUNDS, mockListener); + + final PNode aChild = new PNode(); + aChild.setPropertyChangeParentMask(PNode.PROPERTY_CODE_BOUNDS); + aParent.addChild(aChild); + + aChild.setBounds(0, 0, 100, 100); + assertEquals(1, mockListener.getPropertyChangeCount()); + final PropertyChangeEvent propEvent = mockListener.getPropertyChange(0); + assertEquals(PNode.PROPERTY_BOUNDS, propEvent.getPropertyName()); + assertEquals(new PBounds(0, 0, 100, 100), propEvent.getNewValue()); + } + + public void testStartEndResizeBoundsCanBeCalledWithoutResizes() { + node.startResizeBounds(); + node.endResizeBounds(); + } + + public void testSetXModifiesBounds() { + node.setX(10); + assertEquals(10, node.getBounds().getX(), 0.0001); + } + + public void testSetYModifiesBounds() { + node.setY(10); + assertEquals(10, node.getBounds().getY(), 0.0001); + } + + public void testSetHeightModifiesBounds() { + node.setHeight(10); + assertEquals(10, node.getBounds().getHeight(), 0.0001); + } + + public void testSetWidthModifiesBounds() { + node.setWidth(10); + assertEquals(10, node.getBounds().getWidth(), 0.0001); + } + + public void testResetBoundsDoesSo() { + node.setBounds(10, 15, 100, 115); + node.resetBounds(); + + final PBounds zeroBounds = new PBounds(); + assertEquals(zeroBounds, node.getBounds()); + } + + public void testCenterBoundsOnPointWorksAsExpected() { + node.setBounds(0, 0, 100, 100); + node.centerBoundsOnPoint(0, 0); + + final PBounds expected = new PBounds(-50, -50, 100, 100); + assertEquals(expected, node.getBounds()); + } + + public void testCenterFullBoundsOnPointWorksAsExpected() { + final PNode aParent = buildComplexSquareNode(); + + aParent.centerFullBoundsOnPoint(0, 0); + + final PBounds expected = new PBounds(-50, -50, 100, 100); + assertEquals(expected, aParent.getFullBounds()); + } + + private PNode buildComplexSquareNode() { + final PNode aParent = new PNode(); + aParent.setBounds(0, 0, 50, 100); + + final PNode child1 = new PNode(); + child1.setBounds(50, 0, 50, 50); + aParent.addChild(child1); + + final PNode child2 = new PNode(); + child2.setBounds(50, 50, 50, 50); + aParent.addChild(child2); + + return aParent; + } + + public void testGetUnionOfChildrenBoundsAcceptsNull() { + final PNode node = buildComplexSquareNode(); + + final PBounds union = node.getUnionOfChildrenBounds(null); + + assertNotNull(union); + assertEquals(new PBounds(50, 0, 50, 100), union); + } + + public void testGetGlobalFullBoundsIsSameWhenNoTransforms() { + final PNode parent = new PNode(); + final PNode child = new PNode(); + parent.addChild(child); + final PNode grandChild = new PNode(); + child.addChild(grandChild); + child.setBounds(50, 0, 50, 50); + grandChild.setBounds(0, 50, 50, 50); + + final PBounds globalFullBounds = parent.getGlobalFullBounds(); + + assertNotNull(globalFullBounds); + assertEquals(new PBounds(0, 0, 100, 100), globalFullBounds); + } + + public void testChildBoundsStayVolatile() { + node.setChildBoundsVolatile(true); + assertTrue(node.getChildBoundsVolatile()); + } + + public void testRotatingChangesRotation() { + node.rotate(Math.PI); + assertEquals(Math.PI, node.getRotation(), 0.0001); + } + + public void testSetRotationIsNotCummulative() { + node.setRotation(Math.PI); + node.setRotation(Math.PI); + assertEquals(Math.PI, node.getRotation(), 0.0001); + } + + public void testRotateAboutPointDoesNotAffectBounds() { + node.setBounds(25, 25, 50, 50); + node.rotateAboutPoint(Math.PI, 50, 25); // It's top center point + assertEquals(new PBounds(25, 25, 50, 50), node.getBounds()); + } + + public void testRotateAboutPointVersion1AffectsTransformAsItShould() { + node.setBounds(25, 25, 50, 50); + node.rotateAboutPoint(Math.PI, 50, 0); // It's top center point + + final PAffineTransform expectedTransform = new PAffineTransform(); + expectedTransform.translate(100, 0); + expectedTransform.rotate(Math.PI); + + assertEquals(expectedTransform, node.getTransform()); + } + + public void testRotateAboutPointVersion2AffectsTransformAsItShould() { + node.setBounds(25, 25, 50, 50); + node.rotateAboutPoint(Math.PI, new Point2D.Double(50, 0)); // It's top + // center + // point + + final PAffineTransform expectedTransform = new PAffineTransform(); + expectedTransform.translate(100, 0); + expectedTransform.rotate(Math.PI); + + assertEquals(expectedTransform, node.getTransform()); + } + + public void testScaleAboutPointWorksAsExpected() { + node.setBounds(0, 0, 100, 100); + node.scaleAboutPoint(2, new Point2D.Double(50, 50)); + final PAffineTransform expectedTransform = new PAffineTransform(); + expectedTransform.translate(-50, -50); + expectedTransform.scale(2, 2); + + assertEquals(expectedTransform, node.getTransform()); + } + + public void testRotateInPlaneLeavesFullBoundsUntouched() { + node.setBounds(25, 25, 50, 50); + final PBounds boundsBefore = node.getFullBounds(); + + node.rotateInPlace(Math.PI); + assertEquals(boundsBefore, node.getFullBounds()); + } + + public void testSetGlobalScaleTakesParentsScaleIntoAccount() { + final PNode aParent = new PNode(); + aParent.scale(2); + + final PNode aChild = new PNode(); + aParent.addChild(aChild); + + aChild.setGlobalScale(1); + + assertEquals(0.5, aChild.getScale(), 0.0001); + } + + public void testOffsetDoesNotTakeBoundsIntoAccount() { + node.setOffset(10, 20); + node.setBounds(50, 50, 100, 100); + assertEquals(10, node.getXOffset(), 0.001); + assertEquals(20, node.getYOffset(), 0.001); + } + + public void testTransformByIsCummulative() { + node.transformBy(AffineTransform.getScaleInstance(2, 2)); + node.transformBy(AffineTransform.getScaleInstance(2, 2)); + + assertEquals(AffineTransform.getScaleInstance(4, 4), node.getTransform()); + } + + public void testLerp() { + assertEquals(5, PNode.lerp(0.5, 0, 10), 0.001); + assertEquals(0, PNode.lerp(0, 0, 10), 0.001); + assertEquals(10, PNode.lerp(1, 0, 10), 0.001); + } + + public void testAnimateToRelativePositionResultsInProperTransform() { + final PCanvas canvas = new PCanvas(); + final PNode A = new PNode(); + A.setBounds(0, 0, 50, 50); + canvas.getLayer().addChild(A); + final PNode B = new PNode(); + B.setBounds(0, 0, 100, 100); + B.setOffset(100, 100); + canvas.getLayer().addChild(B); + + final Point2D srcPt = new Point2D.Double(1.0, 0.0); + final Point2D destPt = new Point2D.Double(0.0, 0.0); + A.animateToRelativePosition(srcPt, destPt, B.getGlobalBounds(), 0); + + final PAffineTransform expectedTransform = new PAffineTransform(); + expectedTransform.translate(50, 100); + + assertEquals(expectedTransform, A.getTransform()); + } + + public void testGetInverseTransformWorks() { + node.translate(50, 50); + node.rotate(Math.PI); + + final PAffineTransform expectedTransform = new PAffineTransform(); + expectedTransform.rotate(-Math.PI); + expectedTransform.translate(-50, -50); + assertEquals(expectedTransform, node.getInverseTransform()); + } + + public void testGetInverseTransformThrowsExceptionWhenTransformIsNotInvertible() { + node.setTransform(new AffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); + + try { + node.getInverseTransform(); + fail("Exception not thrown"); + } + catch (final PAffineTransformException e) { + // expected + } + } + + public void testSetVisibleIsRespectedOnPaint() { + final int[] paintCounts = new int[1]; + + final PNode node = new PNode() { + /** + * + */ + private static final long serialVersionUID = 1L; + + public void paint(final PPaintContext pc) { + paintCounts[0]++; + } + }; + node.setBounds(0, 0, 100, 100); + node.setVisible(true); + + final PCanvas canvas = buildCanvasContainingNode(node); + + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final Graphics g = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); + + canvas.paintComponent(g); + + assertEquals(1, paintCounts[0]); + + node.setVisible(false); + node.invalidatePaint(); + canvas.paintComponent(g); + assertEquals(1, paintCounts[0]); + + node.setVisible(true); + node.invalidatePaint(); + canvas.paintComponent(g); + assertEquals(2, paintCounts[0]); + } + + public void testSetTransparency1MeansInvisible() { + final PNode node = new PNode(); + node.setBounds(0, 0, 100, 100); + node.setVisible(true); + node.setPaint(Color.RED); + + final PCanvas canvas = buildCanvasContainingNode(node); + + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final Graphics g = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); + + canvas.paintComponent(g); + node.setTransparency(1f); + assertEquals(Color.RED.getRGB(), img.getRGB(10, 10)); + + node.setTransparency(0f); + canvas.paintComponent(g); + assertEquals(Color.WHITE.getRGB(), img.getRGB(10, 10)); + + } + + private PCanvas buildCanvasContainingNode(final PNode node) { + final PCanvas canvas = new PCanvas(); + canvas.setSize(100, 100); + canvas.getLayer().addChild(node); + return canvas; + } + + public void testPaintColourIsRespectedOnPaint() { + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final Graphics g = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); + + node.setPaint(Color.RED); + node.setBounds(0, 0, 100, 100); + + final PCanvas canvas = buildCanvasContainingNode(node); + canvas.paintComponent(g); + + assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); + } + + public void testToImageReturnsValidImage() { + node.setBounds(0, 0, 10, 10); + node.setPaint(Color.RED); + + // Really don't like casting here, but... without changing the + // interface, I don't see a choice + final BufferedImage img = (BufferedImage) node.toImage(); + + assertEquals(10, img.getHeight(null)); + assertEquals(10, img.getWidth(null)); + assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); + assertEquals(Color.RED.getRGB(), img.getRGB(9, 0)); + assertEquals(Color.RED.getRGB(), img.getRGB(0, 9)); + assertEquals(Color.RED.getRGB(), img.getRGB(9, 9)); + } + + public void testToImageUsesFullBoundsWhenConvertingImage() throws IOException { + node.setBounds(0, 0, 50, 50); + PNode child1 = new PNode(); + child1.setBounds(0, 0, 100, 50); + child1.setPaint(Color.RED); + node.addChild(child1); + + PNode child2 = new PNode(); + child2.setBounds(0, 0, 50, 100); + child2.setPaint(Color.BLUE); + node.addChild(child2); + + BufferedImage image = (BufferedImage) node.toImage(); + assertNotNull(image); + assertEquals(100, image.getWidth()); + assertEquals(100, image.getHeight()); + assertEquals(Color.RED.getRGB(), image.getRGB(99, 1)); + + //This line fails if PNode.toImage uses getWidth() rather than getFullBounds().getWidth() + assertEquals(Color.BLUE.getRGB(), image.getRGB(1, 99)); + } + + public void testToImageWillAcceptBackgroundPaint() { + node.setBounds(0, 0, 10, 10); + + final BufferedImage img = (BufferedImage) node.toImage(10, 10, Color.BLUE); + assertEquals(Color.BLUE.getRGB(), img.getRGB(5, 5)); + } + + public void testToImageResultsInDesiredSizeImage() { + node.setBounds(0, 0, 10, 10); + + final BufferedImage img = (BufferedImage) node.toImage(20, 40, null); + assertEquals(40, img.getHeight(null)); + assertEquals(20, img.getWidth(null)); + } + + public void testToImageWithBackgroundColorGivenReturnsValidImage() { + node.setBounds(0, 0, 10, 10); + node.setPaint(Color.RED); + + final BufferedImage img = (BufferedImage) node.toImage(20, 40, Color.BLUE); + assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); + assertEquals(Color.BLUE.getRGB(), img.getRGB(15, 25)); + } + + public void testToImageScalesNodeAsBigAsCanBe() throws IOException { + node.setBounds(0, 0, 10, 10); + node.setPaint(Color.RED); + + final BufferedImage img = (BufferedImage) node.toImage(20, 40, Color.BLUE); + + assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); + assertEquals(Color.RED.getRGB(), img.getRGB(19, 0)); + assertEquals(Color.RED.getRGB(), img.getRGB(0, 19)); + assertEquals(Color.RED.getRGB(), img.getRGB(19, 19)); + assertEquals(Color.BLUE.getRGB(), img.getRGB(0, 20)); + assertEquals(Color.BLUE.getRGB(), img.getRGB(19, 20)); + } + + public void testToImageScalesAccordingToExactFitStrategy() throws IOException { + node.setBounds(0, 0, 10, 10); + node.setPaint(Color.RED); + + final BufferedImage img = (BufferedImage) node.toImage(new BufferedImage(20, 40, BufferedImage.TYPE_INT_RGB), + Color.BLUE, PNode.FILL_STRATEGY_EXACT_FIT); + + assertEquals(Color.RED.getRGB(), img.getRGB(0, 0)); + assertEquals(Color.RED.getRGB(), img.getRGB(19, 0)); + assertEquals(Color.RED.getRGB(), img.getRGB(0, 39)); + assertEquals(Color.RED.getRGB(), img.getRGB(19, 39)); + + } + + 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); + node.addChild(blueSquare); + + PNode greenSquare = new PNode(); + greenSquare.setPaint(Color.GREEN); + 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); + + assertEquals(Color.RED.getRGB(), img.getRGB(11, 19)); + 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(19, 20)); + assertEquals(Color.GREEN.getRGB(), img.getRGB(10, 39)); + assertEquals(Color.GREEN.getRGB(), img.getRGB(19, 39)); + } + + public void testGetPickableShouldDefaultToTrue() { + assertTrue(node.getPickable()); + } + + public void testSetPickableFiresPropertyChange() { + node.addPropertyChangeListener(mockListener); + node.setPickable(false); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testChildrenShouldBePickableByDefault() { + assertTrue(node.getChildrenPickable()); + } + + public void testSetChildrenPickableFiresPropertyChange() { + node.addPropertyChangeListener(mockListener); + node.setChildrenPickable(false); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testByDefaultNodesShouldNotPickThemselvesBeforeTheirChildren() { + final PCanvas canvas = new PCanvas(); + final PPickPath pickPath = new PPickPath(canvas.getCamera(), new PBounds(0, 0, 100, 100)); + assertFalse(node.pick(pickPath)); + } + + public void testfullPickReturnsTrueWhenOverlapsWithChildNode() { + final PCanvas canvas = new PCanvas(); + node.setBounds(0, 0, 10, 10); + + final PNode child = new PNode(); + child.setBounds(20, 0, 10, 10); + node.addChild(child); + + final PPickPath pickPath = new PPickPath(canvas.getCamera(), new PBounds(20, 0, 10, 10)); + canvas.getLayer().addChild(node); + assertTrue(node.fullPick(pickPath)); + } + + public void testfullPickReturnsFalseWhenNotOverlappingWithChildNode() { + final PCanvas canvas = new PCanvas(); + node.setBounds(0, 0, 10, 10); + + final PNode child = new PNode(); + child.setBounds(10, 0, 10, 10); + node.addChild(child); + + final PPickPath pickPath = new PPickPath(canvas.getCamera(), new PBounds(20, 0, 10, 10)); + canvas.getLayer().addChild(node); + assertFalse(node.fullPick(pickPath)); + } + + public void testAddChildrenAddsAllChildren() { + final Collection newChildren = new ArrayList(); + newChildren.add(new PNode()); + newChildren.add(new PNode()); + newChildren.add(new PNode()); + + node.addChildren(newChildren); + + assertEquals(3, node.getChildrenCount()); + } + + public void testRemoveChildrenWorks() { + final Collection newChildren = new ArrayList(); + newChildren.add(new PNode()); + newChildren.add(new PNode()); + newChildren.add(new PNode()); + node.addChildren(newChildren); + node.addChild(new PNode()); + + node.removeChildren(newChildren); + assertEquals(1, node.getChildrenCount()); + } + + public void testGetAllNodesUnrollsTheNodeGraph() { + final Collection newChildren = new ArrayList(); + newChildren.add(new PNode()); + newChildren.add(new PNode()); + newChildren.add(new PNode()); + + node.addChildren(newChildren); + + assertEquals(4, node.getAllNodes().size()); + } + + public void testRemoveAllChildrenDoesntCrashWhenNoChidlren() { + node.removeAllChildren(); + + // And now for the case when there once was a child + node.addChild(new PNode()); + node.removeAllChildren(); + node.removeAllChildren(); + } + + public void testRemoveFromParentDoesSo() { + final PNode parent = new PNode(); + parent.addChild(node); + + node.removeFromParent(); + + assertEquals(0, parent.getChildrenCount()); + } + + public void testReplaceWithSwapsParents() { + final PNode parent = new PNode(); + parent.addChild(node); + + final PNode newNode = new PNode(); + node.replaceWith(newNode); + assertNull(node.getParent()); + + assertEquals(parent, newNode.getParent()); + } + + public void testGetChildrenIteratorReturnsIteratorEvenWithNoChildren() { + final ListIterator iterator = node.getChildrenIterator(); + assertNotNull(iterator); + assertFalse(iterator.hasNext()); + } + + public void testGetChildrenIteratorReturnsValidIteratorWhenHasChildren() { + final PNode child = new PNode(); + node.addChild(child); + + final ListIterator iterator = node.getChildrenIterator(); + assertNotNull(iterator); + assertTrue(iterator.hasNext()); + assertEquals(child, iterator.next()); + assertFalse(iterator.hasNext()); + } + + public void testGetAllNodesDoesntIgnoreFilter() { + final PNodeFilter nullFilter = new PNodeFilter() { + + public boolean accept(final PNode aNode) { + return false; + } + + public boolean acceptChildrenOf(final PNode aNode) { + return true; + } + }; + + node.addChild(new PNode()); + node.addChild(new PNode()); + node.addChild(new PNode()); + final Collection nodes = node.getAllNodes(nullFilter, null); + assertNotNull(nodes); + assertTrue(nodes.isEmpty()); + } + + public void testAncestryMethods() { + final PNode child = new PNode(); + node.addChild(child); + + final PNode grandChild = new PNode(); + child.addChild(grandChild); + + final PNode unrelated = new PNode(); + + assertTrue(node.isAncestorOf(child)); + assertTrue(node.isAncestorOf(grandChild)); + assertTrue(child.isDescendentOf(node)); + assertTrue(grandChild.isDescendentOf(node)); + + assertFalse(node.isAncestorOf(unrelated)); + assertFalse(grandChild.isDescendentOf(unrelated)); + } + + public void testMoveToBackMovesNodeToBeFirstChild() { + final PNode parent = new PNode(); + parent.addChild(new PNode()); + parent.addChild(new PNode()); + parent.addChild(node); + node.moveToBack(); + assertEquals(0, parent.indexOfChild(node)); + } + + public void testMoveToFrontMovesNodeToBeLastChild() { + final PNode parent = new PNode(); + parent.addChild(node); + parent.addChild(new PNode()); + parent.addChild(new PNode()); + node.moveToFront(); + assertEquals(2, parent.indexOfChild(node)); + } + + public void testMoveInBackOfMovesNodeToBeforeSibling() { + final PNode parent = new PNode(); + final PNode sibling = new PNode(); + + parent.addChild(node); + parent.addChild(new PNode()); + parent.addChild(new PNode()); + parent.addChild(sibling); + + node.moveInBackOf(sibling); + assertEquals(2, parent.indexOfChild(node)); + } + + public void testMoveInFrontOfMovesNodeToAfterSibling() { + final PNode parent = new PNode(); + final PNode sibling = new PNode(); + + parent.addChild(node); + parent.addChild(new PNode()); + parent.addChild(new PNode()); + parent.addChild(sibling); + + node.moveInFrontOf(sibling); + assertEquals(3, parent.indexOfChild(node)); + } + + public void testMoveInFrontOfDoesNothingIfNotSibling() { + final PNode parent = new PNode(); + final PNode stranger = new PNode(); + + parent.addChild(node); + parent.addChild(new PNode()); + parent.addChild(new PNode()); + + node.moveInFrontOf(stranger); + assertEquals(0, parent.indexOfChild(node)); + } + + public void testMoveInBackOfDoesNothingIfNotSibling() { + final PNode parent = new PNode(); + final PNode stranger = new PNode(); + + parent.addChild(node); + parent.addChild(new PNode()); + parent.addChild(new PNode()); + + node.moveInBackOf(stranger); + assertEquals(0, parent.indexOfChild(node)); + } + + public void testIsDescendentOfRootHandlesOrphans() { + final PNode orphan = new PNode(); + + assertFalse(orphan.isDescendentOfRoot()); + orphan.addChild(node); + assertFalse(node.isDescendentOfRoot()); + } + + public void testIsDescendentOfRootHandlesDescendentsOfRoot() { + final PCanvas canvas = new PCanvas(); + canvas.getLayer().addChild(node); + + assertTrue(node.isDescendentOfRoot()); + } + + public void testGetGlobalRationTakesParentsIntoAccount() { + final PNode parent = new PNode(); + parent.rotate(Math.PI / 4d); + parent.addChild(node); + + node.rotate(Math.PI / 4d); + + assertEquals(Math.PI / 2d, node.getGlobalRotation(), 0.001); + } + + public void testSetGlobalRationTakesParentsIntoAccount() { + final PNode parent = new PNode(); + parent.rotate(Math.PI / 4d); + parent.addChild(node); + + node.setGlobalRotation(Math.PI / 2d); + + assertEquals(Math.PI / 4d, node.getRotation(), 0.001); + } + + public void testSetGlobalRationWorksWhenNoParent() { + node.setGlobalRotation(Math.PI / 2d); + + assertEquals(Math.PI / 2d, node.getRotation(), 0.001); + } + + public void testSetOccludedPersistes() { + node.setOccluded(true); + assertTrue(node.getOccluded()); + } + + public void testHiddenNodesAreNotPickable() { + final PCanvas canvas = new PCanvas(); + canvas.setBounds(0, 0, 400, 400); + canvas.setPreferredSize(new Dimension(400, 400)); + final PNode node1 = new PNode(); + node1.setBounds(0, 0, 100, 100); + node1.setPaint(Color.RED); + canvas.getLayer().addChild(node1); + + final PNode node2 = (PNode) node1.clone(); + node2.setPaint(Color.BLUE); + + final PLayer layer2 = new PLayer(); + layer2.addChild(node2); + layer2.setVisible(false); + canvas.getCamera().addLayer(layer2); + + final PPickPath path = canvas.getCamera().pick(5, 5, 5); + assertSame(node1, path.getPickedNode()); + } +} diff --git a/core/src/test/java/org/piccolo2d/POffscreenCanvasTest.java b/core/src/test/java/org/piccolo2d/POffscreenCanvasTest.java new file mode 100644 index 0000000..3c5f2cb --- /dev/null +++ b/core/src/test/java/org/piccolo2d/POffscreenCanvasTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +import org.piccolo2d.PCamera; +import org.piccolo2d.POffscreenCanvas; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + +import junit.framework.TestCase; + +/** + * Unit test for POffscreenCanvas. + */ +public class POffscreenCanvasTest extends TestCase { + + public void testConstructor() { + final POffscreenCanvas canvas0 = new POffscreenCanvas(100, 100); + assertNotNull(canvas0); + final POffscreenCanvas canvas1 = new POffscreenCanvas(0, 0); + assertNotNull(canvas1); + final POffscreenCanvas canvas2 = new POffscreenCanvas(0, 100); + assertNotNull(canvas2); + final POffscreenCanvas canvas3 = new POffscreenCanvas(100, 0); + assertNotNull(canvas3); + + try { + new POffscreenCanvas(-1, 100); + fail("ctr(-1, 100) expected IllegalArgumentException"); + } + catch (final IllegalArgumentException e) { + // expected + } + try { + new POffscreenCanvas(100, -1); + fail("ctr(100, -1) expected IllegalArgumentException"); + } + catch (final IllegalArgumentException e) { + // expected + } + try { + new POffscreenCanvas(-1, -1); + fail("ctr(-1, -1) expected IllegalArgumentException"); + } + catch (final IllegalArgumentException e) { + // expected + } + } + + public void testCamera() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + assertNotNull(canvas); + final PCamera camera = canvas.getCamera(); + assertNotNull(camera); + assertEquals(canvas, camera.getComponent()); + final PCamera camera1 = new PCamera(); + canvas.setCamera(camera1); + assertEquals(camera1, canvas.getCamera()); + assertEquals(null, camera.getComponent()); + assertEquals(canvas, camera1.getComponent()); + canvas.setCamera(null); + assertEquals(null, camera1.getComponent()); + assertEquals(null, canvas.getCamera()); + } + + public void testRenderEmpty() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); + final Graphics2D graphics = image.createGraphics(); + canvas.render(graphics); + for (int x = 0; x < 100; x++) { + for (int y = 0; y < 200; y++) { + assertEquals(0, image.getRGB(x, y)); + } + } + } + + public void testRenderEmptyOpaqueNullBackgroundColor() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); + final Graphics2D graphics = image.createGraphics(); + canvas.setOpaque(true); + canvas.render(graphics); + for (int x = 0; x < 100; x++) { + for (int y = 0; y < 200; y++) { + assertEquals(0, image.getRGB(x, y)); + } + } + } + + public void testRenderEmptyOpaque() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); + final Graphics2D graphics = image.createGraphics(); + canvas.setOpaque(true); + canvas.setBackground(Color.RED); + canvas.render(graphics); + for (int x = 0; x < 100; x++) { + for (int y = 0; y < 200; y++) { + // red pixel, RGBA is 255, 0, 0, 255 + assertEquals(-65536, image.getRGB(x, y)); + } + } + } + + public void testRenderFull() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + final PPath rect = PPath.createRectangle(0.0f, 0.0f, 200.0f, 300.0f); + rect.setPaint(new Color(255, 0, 0)); + rect.setStroke(null); + rect.offset(-100.0d, -100.0d); + canvas.getCamera().getLayer(0).addChild(rect); + final BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); + final Graphics2D graphics = image.createGraphics(); + canvas.render(graphics); + for (int x = 0; x < 100; x++) { + for (int y = 0; y < 200; y++) { + // red pixel, RGBA is 255, 0, 0, 255 + assertEquals(-65536, image.getRGB(x, y)); + } + } + } + + public void testRenderNull() { + try { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + canvas.render(null); + fail("render(null) expected IllegalArgumentException"); + } + catch (final IllegalArgumentException e) { + // expected + } + } + + public void testRenderQuality() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + assertEquals(POffscreenCanvas.DEFAULT_RENDER_QUALITY, canvas.getRenderQuality()); + canvas.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); + assertEquals(PPaintContext.HIGH_QUALITY_RENDERING, canvas.getRenderQuality()); + canvas.setRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); + assertEquals(PPaintContext.LOW_QUALITY_RENDERING, canvas.getRenderQuality()); + + try { + canvas.setRenderQuality(-1); + } + catch (final IllegalArgumentException e) { + // expected + } + } + + public void testRepaint() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + canvas.repaint(null); + canvas.repaint(new PBounds(0.0, 0.0, 50.0, 50.0)); + } + + public void testPaintImmediately() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + canvas.paintImmediately(); + } + + public void testPopCursor() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + canvas.popCursor(); + } + + public void testPushCursor() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + canvas.pushCursor(null); + canvas.pushCursor(Cursor.getDefaultCursor()); + } + + public void testInteracting() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + canvas.setInteracting(true); + canvas.setInteracting(false); + } + + public void testRoot() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + assertNotNull(canvas.getRoot()); + } + + public void testRootIsNullWhenCameraIsNull() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + canvas.setCamera(null); + assertEquals(null, canvas.getRoot()); + } + + public void testOpaque() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + assertFalse(canvas.isOpaque()); + canvas.setOpaque(true); + assertTrue(canvas.isOpaque()); + } + + public void testBackground() { + final POffscreenCanvas canvas = new POffscreenCanvas(100, 200); + assertEquals(null, canvas.getBackground()); + canvas.setBackground(Color.RED); + assertEquals(Color.RED, canvas.getBackground()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/piccolo2d/PRootTest.java b/core/src/test/java/org/piccolo2d/PRootTest.java new file mode 100644 index 0000000..08b0bc2 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PRootTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.Timer; + +import org.piccolo2d.PRoot; +import org.piccolo2d.activities.PActivity; + +import junit.framework.TestCase; + +/** + * Unit test for PRoot. + */ +public class PRootTest extends TestCase { + private PRoot root; + private MockPropertyChangeListener mockListener; + + public void setUp() { + root = new PRoot(); + mockListener = new MockPropertyChangeListener(); + } + + public void testActivityScheduleIsNotNullByDefault() { + assertNotNull(root.getActivityScheduler()); + } + + public void testGetRootReturnsItself() { + assertSame(root, root.getRoot()); + } + + public void testGetDefaultInputManagerIsNotNullByDefault() { + assertNotNull(root.getDefaultInputManager()); + } + + public void testAddInputSourceFirePropertyChangeEvent() { + root.addPropertyChangeListener(PRoot.PROPERTY_INPUT_SOURCES, mockListener); + + final PRoot.InputSource newSource = new PRoot.InputSource() { + public void processInput() { + + } + }; + root.addInputSource(newSource); + + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testCreateTimerReturnsATimer() { + final Timer timer = root.createTimer(1, new ActionListener() { + public void actionPerformed(final ActionEvent arg0) { + } + }); + assertNotNull(timer); + } + + public void testCreateTimerReturnsATimerWhenDelayIs0() { + final Timer timer = root.createTimer(0, new ActionListener() { + public void actionPerformed(final ActionEvent arg0) { + } + }); + assertNotNull(timer); + } + + public void testRemoveInputSourceDoesNothingIfStranger() { + final PRoot.InputSource strangeSource = new PRoot.InputSource() { + public void processInput() { + + } + }; + + root.removeInputSource(strangeSource); + } + + public void testGlobalTimeIsNotZeroBeforeCallToProcessInputs() { + assertFalse(0 == root.getGlobalTime()); + } + + public void testProcessInputDelegatesToInputSources() { + final MockInputSource newSource = new MockInputSource(); + root.addInputSource(newSource); + root.processInputs(); + assertEquals(1, newSource.getProcessInputCalls()); + } + + public void testProcessInputProcessesActivities() { + final MockPActivity activity = new MockPActivity(100); + root.addActivity(activity); + root.processInputs(); + assertTrue(activity.isActivityStarted()); + + } + + public void testSetFullBoundsInvalidPerists() { + root.setFullBoundsInvalid(true); + assertTrue(root.getFullBoundsInvalid()); + } + + public void testSetChildBoundsInvalidPerists() { + root.setChildBoundsInvalid(true); + assertTrue(root.getChildBoundsInvalid()); + } + + public void testSetPaintInvalidPersists() { + root.setPaintInvalid(true); + assertTrue(root.getPaintInvalid()); + } + + public void testSetChildPaintInvalidPersists() { + root.setChildPaintInvalid(true); + assertTrue(root.getChildPaintInvalid()); + } + + public void testWaitForActivitiesDoesSo() { + final MockPActivity activity = new MockPActivity(1); + root.addActivity(activity); + root.waitForActivities(); + assertTrue(activity.isActivityFished()); + } + + private static final class MockInputSource implements PRoot.InputSource { + private int processInputCalls; + + public int getProcessInputCalls() { + return processInputCalls; + } + + public void processInput() { + processInputCalls++; + } + } + + private static final class MockPActivity extends PActivity { + private boolean activityStarted; + private boolean activityFinished; + + private MockPActivity(final long aDuration) { + super(aDuration); + } + + public boolean isActivityFished() { + return activityFinished; + } + + public boolean isActivityStarted() { + return activityStarted; + } + + protected void activityStarted() { + activityStarted = true; + super.activityStarted(); + } + + protected void activityFinished() { + activityFinished = true; + super.activityFinished(); + } + } +} diff --git a/core/src/test/java/org/piccolo2d/PerformanceLog.java b/core/src/test/java/org/piccolo2d/PerformanceLog.java new file mode 100644 index 0000000..12003d2 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PerformanceLog.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * Performance log. + */ +public class PerformanceLog { + + private final ArrayList log = new ArrayList(); + private long testTime; + + + // heh, something tells me this was copied from Jazz :) + public static class ZLogEntry { + public String name; + public long time; + + public ZLogEntry(final String aName, final long aTime) { + name = aName; + time = aTime; + } + } + + public void startTest() { + Runtime.getRuntime().gc(); + testTime = System.currentTimeMillis(); + } + + public void endTest(final String name) { + testTime = System.currentTimeMillis() - testTime; + addEntry(name, testTime); + System.gc(); + } + + public void addEntry(final String aName, final long aTime) { + log.add(new ZLogEntry(aName, aTime)); + } + + public void clear() { + log.clear(); + } + + public void writeLog() { + + System.out.println(); + System.out.println("Test data for input into spreadsheet:"); + System.out.println(); + + Iterator i = log.iterator(); + while (i.hasNext()) { + final ZLogEntry each = (ZLogEntry) i.next(); + System.out.println(each.time); + } + + System.out.println(); + System.out.println("Labled test results, see above for simple column \n of times for input into spreadsheet:"); + System.out.println(); + + i = log.iterator(); + while (i.hasNext()) { + final ZLogEntry each = (ZLogEntry) i.next(); + System.out.println(each.name + ", " + each.time); + } + } +} diff --git a/core/src/test/java/org/piccolo2d/PerformanceTests.java b/core/src/test/java/org/piccolo2d/PerformanceTests.java new file mode 100644 index 0000000..2c6bf36 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PerformanceTests.java @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Transparency; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Random; + +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; + +import junit.framework.TestCase; + +/** + * Performance tests. + */ +public class PerformanceTests extends TestCase { + + private static PerformanceLog log = new PerformanceLog(); + private static int NUMBER_NODES = 20000; + + public PerformanceTests(final String name) { + super(name); + } + + public void testRunPerformanceTests() { + if (1 == 1) { + return; + } + + // three times to warm up JVM + for (int i = 0; i < 3; i++) { + addNodes(); + copyNodes(); + createNodes(); + createPaths(); + fullIntersectsNodes(); + memorySizeOfNodes(); + // removeNodes(); + translateNodes(); + costOfNoBoundsCache(); + // renderSpeed(); + if (i != 2) { + log.clear(); + } + } + log.writeLog(); + } + + public void createNodes() { + final PNode[] nodes = new PNode[NUMBER_NODES]; + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + } + log.endTest("Create " + NUMBER_NODES + " new nodes"); + } + + public void createPaths() { + final PNode[] nodes = new PNode[NUMBER_NODES]; + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = PPath.createRectangle(0, 0, 100, 80); + } + log.endTest("Create " + NUMBER_NODES + " new rect paths"); + + final Random r = new Random(); + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i].translate(r.nextFloat() * 300, r.nextFloat() * 300); + } + } + + public void addNodes() { + final PNode parent = new PNode(); + final PNode[] nodes = new PNode[NUMBER_NODES]; + + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + } + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + parent.addChild(nodes[i]); + } + log.endTest("Add " + NUMBER_NODES + " nodes to a new parent"); + } + + public void removeNodes() { + final PNode parent = new PNode(); + final PNode[] nodes = new PNode[NUMBER_NODES]; + final ArrayList list = new ArrayList(); + + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + } + + for (int i = 0; i < NUMBER_NODES; i++) { + parent.addChild(nodes[i]); + list.add(nodes[i]); + } + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + parent.removeChild(nodes[i]); + } + log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() front to back"); + + parent.addChildren(list); + + log.startTest(); + for (int i = NUMBER_NODES - 1; i >= 0; i--) { + parent.removeChild(i); + } + log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() back to front by index"); + + log.startTest(); + // for (int i = NUMBER_NODES - 1; i >= 0; i--) { + // parent.removeChild(nodes[i]); + // } + log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() back to front by object, TO_SLOW"); + + parent.addChildren(list); + + log.startTest(); + parent.removeChildren(list); + log.endTest("Remove " + NUMBER_NODES + " nodes using removeChildren()"); + + parent.addChildren(list); + + log.startTest(); + parent.removeAllChildren(); + log.endTest("Remove " + NUMBER_NODES + " nodes using removeAllChildren()"); + } + + public void translateNodes() { + final PNode parent = new PNode(); + final PNode[] nodes = new PNode[NUMBER_NODES]; + final PBounds b = new PBounds(); + final Random r = new Random(); + + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + nodes[i].setBounds(1000 * r.nextFloat(), 1000 * r.nextFloat(), 100, 80); + parent.addChild(nodes[i]); + nodes[i].getFullBoundsReference(); + } + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i].translate(1000 * r.nextFloat(), 1000 * r.nextFloat()); + nodes[i].scale(1000 * r.nextFloat()); + // nodes[i].translateBy(100.01, 100.2); + // nodes[i].scaleBy(0.9); + } + log.endTest("Translate " + NUMBER_NODES + " nodes, not counting repaint or validate layout"); + + log.startTest(); + // parent.validateFullBounds(); now protected. + parent.getFullBoundsReference(); // calls validateFullBounds as a side + // effect. + log.endTest("Validate Layout after translate " + NUMBER_NODES + " nodes"); + + log.startTest(); + parent.validateFullPaint(); + log.endTest("Validate Paint after translate " + NUMBER_NODES + " nodes"); + + log.startTest(); + parent.computeFullBounds(b); + log.endTest("Parent compute bounds of " + NUMBER_NODES + " children nodes"); + } + + public void fullIntersectsNodes() { + final PNode parent = new PNode(); + final PNode[] nodes = new PNode[NUMBER_NODES]; + final PBounds b = new PBounds(0, 50, 100, 20); + + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + parent.addChild(nodes[i]); + } + + // parent.validateFullBounds(); // now protected + parent.getFullBoundsReference(); // calls validateFullBounds as a side + // effect. + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i].fullIntersects(b); + } + log.endTest("Do fullIntersects test for " + NUMBER_NODES + " nodes"); + } + + public void memorySizeOfNodes() { + final PNode[] nodes = new PNode[NUMBER_NODES]; + Runtime.getRuntime().gc(); + final long startTotalMemory = Runtime.getRuntime().totalMemory(); + final long startFree = Runtime.getRuntime().freeMemory(); + long endFree; + long endTotal; + + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + } + + Runtime.getRuntime().gc(); + endFree = Runtime.getRuntime().freeMemory(); + endTotal = Runtime.getRuntime().totalMemory(); + + log.addEntry("Approximate k used by " + NUMBER_NODES + " nodes", + (endTotal - startTotalMemory + startFree - endFree) / 1024); + nodes[0].getPaint(); + } + + public void copyNodes() { + final PNode parent = new PNode(); + final PNode[] nodes = new PNode[NUMBER_NODES]; + + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + parent.addChild(nodes[i]); + } + + log.startTest(); + parent.clone(); + log.endTest("Copy/Serialize " + NUMBER_NODES + " nodes"); + } + + public void costOfNoBoundsCache() { + final PNode[] nodes = new PNode[NUMBER_NODES]; + final PBounds[] bounds = new PBounds[NUMBER_NODES]; + final PBounds pickRect = new PBounds(0, 0, 1, 1); + final Random r = new Random(); + + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i] = new PNode(); + nodes[i].translate(1000 * r.nextFloat(), 1000 * r.nextFloat()); + nodes[i].scale(1000 * r.nextFloat()); + bounds[i] = new PBounds(1000 * r.nextFloat(), 1000 * r.nextFloat(), 100, 80); + } + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + bounds[i].intersects(pickRect); + } + log.endTest("Do intersects test for " + NUMBER_NODES + " bounds"); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + nodes[i].localToParent(bounds[i]); + } + log.endTest("Transform " + NUMBER_NODES + " bounds from local to parent"); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + pickRect.add(bounds[i]); + } + log.endTest("Sum " + NUMBER_NODES + " bounds"); + + final PBounds b = new PBounds(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble()); + log.startTest(); + for (int i = 0; i < NUMBER_NODES * 10; i++) { + b.clone(); + } + log.endTest("Clone " + NUMBER_NODES * 10 + " PBounds"); + + } + + public void renderSpeed() throws NoninvertibleTransformException { + final Random r = new Random(); + final PAffineTransform at = new PAffineTransform(); + at.setScale(r.nextFloat()); + at.translate(r.nextFloat(), r.nextFloat()); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + at.createInverse(); + } + log.endTest("Create inverse transform " + NUMBER_NODES + " times"); + + final int height = 400; + final int width = 400; + + final double scale1 = 0.5; + final double scale2 = 2; + boolean scaleFlip = true; + + final PAffineTransform transorm1 = new PAffineTransform(); + // transorm1.scale(0.5, 0.5); + transorm1.translate(0.5, 10.1); + PAffineTransform transorm2 = null; + + transorm2 = new PAffineTransform(transorm1.createInverse()); + + final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + final BufferedImage result = graphicsConfiguration.createCompatibleImage(width, height, + Transparency.TRANSLUCENT); + final Graphics2D g2 = result.createGraphics(); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + if (scaleFlip) { + g2.scale(scale2, scale2); + scaleFlip = !scaleFlip; + } + else { + g2.scale(scale1, scale1); + scaleFlip = !scaleFlip; + } + } + log.endTest("Scale graphics context " + NUMBER_NODES + " times"); + + g2.setTransform(new AffineTransform()); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + g2.translate(0.5, 0.5); + } + log.endTest("Translate graphics context " + NUMBER_NODES + " times"); + + g2.setTransform(new AffineTransform()); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + if (scaleFlip) { + g2.transform(transorm1); + scaleFlip = !scaleFlip; + } + else { + g2.transform(transorm2); + scaleFlip = !scaleFlip; + } + } + log.endTest("Transform graphics context " + NUMBER_NODES + " times"); + + final Rectangle2D rect = new Rectangle2D.Double(0, 0, 100, 80); + final GeneralPath path = new GeneralPath(rect); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + g2.fill(rect); + } + log.endTest("Fill " + NUMBER_NODES + " rects"); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + g2.getTransform().getScaleX(); + } + log.endTest("Call g2.getTransform() " + NUMBER_NODES + " times"); + + log.startTest(); + for (int i = 0; i < NUMBER_NODES; i++) { + g2.fill(path); + } + log.endTest("Fill " + NUMBER_NODES + " paths"); + } +} diff --git a/core/src/test/java/org/piccolo2d/PiccoloAsserts.java b/core/src/test/java/org/piccolo2d/PiccoloAsserts.java new file mode 100644 index 0000000..58638cc --- /dev/null +++ b/core/src/test/java/org/piccolo2d/PiccoloAsserts.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.piccolo2d; + +import java.awt.geom.Dimension2D; + +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + +import junit.framework.Assert; + +/** + * This class provides helper methods to help with testing. + * + * It's implemented this way, as opposed to as a subclass, because when we move + * to JUnit4, inheritance is not the preferred way of importing asserts. + */ +public final class PiccoloAsserts { + private PiccoloAsserts() { + // Nothing to do + } + + public static final void assertEquals(final PBounds expected, final PBounds actual, final double errorRate) { + assertEquals("Expected " + expected + " but was " + actual, expected, actual, errorRate); + } + + public static final void assertEquals(final String message, final PBounds expected, final PBounds actual, + final double errorRate) { + Assert.assertEquals(message, expected.getX(), actual.getX(), errorRate); + Assert.assertEquals(message, expected.getY(), actual.getY(), errorRate); + Assert.assertEquals(message, expected.getWidth(), actual.getWidth(), errorRate); + Assert.assertEquals(message, expected.getHeight(), actual.getHeight(), errorRate); + } + + public static void assertEquals(final PDimension expected, final Dimension2D actual, final double errorRate) { + assertEquals("Expected " + expected + " but was " + actual, expected, actual, errorRate); + } + + public static void assertEquals(final String message, final PDimension expected, final Dimension2D actual, + final double errorRate) { + Assert.assertEquals(message, expected.getWidth(), actual.getWidth(), errorRate); + Assert.assertEquals(message, expected.getHeight(), actual.getHeight(), errorRate); + } + + public static void assertEquals(final String[] expected, final String[] actual) { + Assert.assertEquals("arrays are not same size", expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(expected[i], expected[i]); + } + } +} diff --git a/core/src/test/java/org/piccolo2d/SerializationTest.java b/core/src/test/java/org/piccolo2d/SerializationTest.java new file mode 100644 index 0000000..925ffac --- /dev/null +++ b/core/src/test/java/org/piccolo2d/SerializationTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d; + +import java.util.Iterator; + +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + +import junit.framework.TestCase; + +/** + * Unit test for node serialization. Should be removed + * in favor of class-specific serialization unit tests. + */ +public class SerializationTest extends TestCase { + + public SerializationTest(final String name) { + super(name); + } + + public void test() { + PNode l = new PLayer(); + + for (int i = 0; i < 100; i++) { + l.addChild(new PNode()); + l.addChild(new PText("Hello World")); + l.addChild(new PPath()); + } + + l = (PNode) l.clone(); // copy uses serialization internally + assertTrue(l.getChildrenCount() == 300); + + final Iterator i = l.getChildrenIterator(); + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + assertEquals(l, each.getParent()); + } + } +} diff --git a/core/src/test/java/org/piccolo2d/activities/PInterpolatingActivityTest.java b/core/src/test/java/org/piccolo2d/activities/PInterpolatingActivityTest.java new file mode 100755 index 0000000..7bef79b --- /dev/null +++ b/core/src/test/java/org/piccolo2d/activities/PInterpolatingActivityTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.activities; + +import org.piccolo2d.activities.PInterpolatingActivity; +import org.piccolo2d.util.PUtil; + +import junit.framework.TestCase; + +/** + * Unit test for PInterpolatingActivity. + */ +public class PInterpolatingActivityTest extends TestCase { + + public void testConstructorLong() { + PInterpolatingActivity activity = new PInterpolatingActivity(1L); + assertNotNull(activity); + assertEquals(1L, activity.getDuration()); + assertEquals(1, activity.getLoopCount()); + assertEquals(PUtil.DEFAULT_ACTIVITY_STEP_RATE, activity.getStepRate()); + assertEquals(PInterpolatingActivity.SOURCE_TO_DESTINATION, activity.getMode()); + } +} diff --git a/core/src/test/java/org/piccolo2d/activities/PTransformActivityTest.java b/core/src/test/java/org/piccolo2d/activities/PTransformActivityTest.java new file mode 100644 index 0000000..e1016b4 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/activities/PTransformActivityTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.activities; + +import org.piccolo2d.activities.PTransformActivity; + +import junit.framework.TestCase; + +/** + * Unit test for PTransformActivity. + */ +public class PTransformActivityTest extends TestCase { + + public PTransformActivityTest(final String name) { + super(name); + } + + public void testToString() { + final PTransformActivity transformActivity = new PTransformActivity(1000, 0, null); + assertNotNull(transformActivity.toString()); + } +} diff --git a/core/src/test/java/org/piccolo2d/event/MockPBasicInputEventHandler.java b/core/src/test/java/org/piccolo2d/event/MockPBasicInputEventHandler.java new file mode 100644 index 0000000..a4c19cc --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/MockPBasicInputEventHandler.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.util.ArrayList; + +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; + +/** + * Mock PBasicInputEventHandler. + */ +public class MockPBasicInputEventHandler extends PBasicInputEventHandler { + private final ArrayList methodCalls = new ArrayList(); + + public String[] getMethodCalls() { + final String[] result = new String[methodCalls.size()]; + for (int i = 0; i < methodCalls.size(); i++) { + result[i] = (String) methodCalls.get(i); + } + return result; + } + + public void keyboardFocusGained(final PInputEvent event) { + methodCalls.add("keyboardFocusGained"); + super.keyboardFocusGained(event); + } + + public void keyboardFocusLost(final PInputEvent event) { + methodCalls.add("keyboardFocusLost"); + super.keyboardFocusLost(event); + } + + public void keyPressed(final PInputEvent event) { + methodCalls.add("keyPressed"); + super.keyPressed(event); + } + + public void keyReleased(final PInputEvent event) { + methodCalls.add("keyReleased"); + super.keyReleased(event); + } + + public void keyTyped(final PInputEvent event) { + methodCalls.add("keyTyped"); + super.keyTyped(event); + } + + public void mouseClicked(final PInputEvent event) { + methodCalls.add("mouseClicked"); + super.mouseClicked(event); + } + + public void mouseDragged(final PInputEvent event) { + methodCalls.add("mouseDragged"); + super.mouseDragged(event); + } + + public void mouseEntered(final PInputEvent event) { + methodCalls.add("mouseEntered"); + super.mouseEntered(event); + } + + public void mouseExited(final PInputEvent event) { + methodCalls.add("mouseExited"); + super.mouseExited(event); + } + + public void mouseMoved(final PInputEvent event) { + methodCalls.add("mouseMoved"); + super.mouseMoved(event); + } + + public void mousePressed(final PInputEvent event) { + methodCalls.add("mousePressed"); + super.mousePressed(event); + } + + public void mouseReleased(final PInputEvent event) { + methodCalls.add("mouseReleased"); + super.mouseReleased(event); + } + + public void mouseWheelRotated(final PInputEvent event) { + methodCalls.add("mouseReleased"); + super.mouseWheelRotated(event); + } + + public void mouseWheelRotatedByBlock(final PInputEvent event) { + methodCalls.add("mouseWheelRotatedByBlock"); + super.mouseWheelRotatedByBlock(event); + } +} diff --git a/core/src/test/java/org/piccolo2d/event/PBasicInputEventListenerTest.java b/core/src/test/java/org/piccolo2d/event/PBasicInputEventListenerTest.java new file mode 100644 index 0000000..ed4e8c4 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/PBasicInputEventListenerTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; + +import org.piccolo2d.PCanvas; +import org.piccolo2d.PiccoloAsserts; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; + +import junit.framework.TestCase; + +/** + * Unit test for PBasicInputEventListener. + */ +public class PBasicInputEventListenerTest extends TestCase { + private PBasicInputEventHandler listener; + private MockPBasicInputEventHandler mockListener; + + public void setUp() { + listener = new PBasicInputEventHandler(); + } + + public void testSetEventFilterIsPersisted() { + final PInputEventFilter filter = new PInputEventFilter(); + listener.setEventFilter(filter); + assertSame(filter, listener.getEventFilter()); + } + + public void testAcceptsEventDelegatesToFilter() { + final PInputEventFilter filter = new PInputEventFilter(); + listener.setEventFilter(filter); + final PInputEvent event = buildInputEvent(); + assertTrue(listener.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); + filter.rejectAllEventTypes(); + assertFalse(listener.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); + } + + public void testProcessEventDelegatesToSubClassMethodsBasedOnType() { + final PInputEvent event = buildInputEvent(); + + mockListener = new MockPBasicInputEventHandler(); + final int[] eventTypes = new int[] { KeyEvent.KEY_PRESSED, KeyEvent.KEY_RELEASED, KeyEvent.KEY_TYPED, + MouseEvent.MOUSE_RELEASED, MouseEvent.MOUSE_CLICKED, MouseEvent.MOUSE_DRAGGED, + MouseEvent.MOUSE_ENTERED, MouseEvent.MOUSE_EXITED, MouseEvent.MOUSE_MOVED, MouseEvent.MOUSE_PRESSED, + MouseWheelEvent.WHEEL_UNIT_SCROLL, MouseWheelEvent.WHEEL_BLOCK_SCROLL, FocusEvent.FOCUS_GAINED, + FocusEvent.FOCUS_LOST }; + + for (int i = 0; i < eventTypes.length; i++) { + mockListener.processEvent(event, eventTypes[i]); + } + + PiccoloAsserts.assertEquals(new String[] { "keyPressed", "keyReleased", "keyTyped", "mouseReleased", + "mouseClicked", "mouseDragged", "mouseEntered", "mouseExited", "mouseMoved", "mousePressed", + "mouseWheelRotated", "mouseWheelRotatedByBlock", "focusGained", "focusLost" }, mockListener + .getMethodCalls()); + } + + private PInputEvent buildInputEvent() { + final PCanvas canvas = new PCanvas(); + final MouseEvent mouseEvent = new MouseEvent(canvas, 1, System.currentTimeMillis(), 0, 0, 0, 1, false); + final PInputEvent event = new PInputEvent(canvas.getRoot().getDefaultInputManager(), mouseEvent); + return event; + } + +} diff --git a/core/src/test/java/org/piccolo2d/event/PDragEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PDragEventHandlerTest.java new file mode 100644 index 0000000..ce504fb --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/PDragEventHandlerTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import org.piccolo2d.event.PDragEventHandler; + +import junit.framework.TestCase; + +/** + * Unit test for PDragEventHander. + */ +public class PDragEventHandlerTest extends TestCase { + private PDragEventHandler handler; + + public void setUp() { + handler = new PDragEventHandler(); + } + + public void testMoveToFrontOnPressDefaultToFalse() { + assertFalse(handler.getMoveToFrontOnPress()); + } + + public void testMoveToFrontOnPressPersists() { + handler.setMoveToFrontOnPress(true); + assertTrue(handler.getMoveToFrontOnPress()); + } + +} diff --git a/core/src/test/java/org/piccolo2d/event/PInputEventFilterTest.java b/core/src/test/java/org/piccolo2d/event/PInputEventFilterTest.java new file mode 100644 index 0000000..fe12c0f --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/PInputEventFilterTest.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import org.piccolo2d.PInputManager; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; + +import junit.framework.TestCase; + +/** + * Unit test for PInputEventFilter. + */ +public class PInputEventFilterTest extends TestCase { + private PInputEventFilter filter; + + public void setUp() { + filter = new PInputEventFilter(); + } + + public void testAcceptsAlreadyHandledEventsFalseByDefault() { + assertFalse(filter.getAcceptsAlreadyHandledEvents()); + } + + public void testDoesNotMarkEventsHandledByDefault() { + assertFalse(filter.getMarksAcceptedEventsAsHandled()); + } + + public void testAcceptsEverythingByDefault() { + assertAcceptsAll(); + } + + public void testMaskDoesNotAffectReportedAccepts() { + filter = new PInputEventFilter(0); + assertAcceptsAll(); + } + + private void assertAcceptsAll() { + assertTrue(filter.getAcceptsFocusEvents()); + assertTrue(filter.getAcceptsKeyPressed()); + assertTrue(filter.getAcceptsKeyReleased()); + assertTrue(filter.getAcceptsKeyTyped()); + assertTrue(filter.getAcceptsMouseClicked()); + assertTrue(filter.getAcceptsMouseDragged()); + assertTrue(filter.getAcceptsMouseEntered()); + assertTrue(filter.getAcceptsMouseExited()); + assertTrue(filter.getAcceptsMouseMoved()); + assertTrue(filter.getAcceptsMousePressed()); + assertTrue(filter.getAcceptsMouseReleased()); + assertTrue(filter.getAcceptsMouseWheelRotated()); + } + + public void testRejectsEverythingAfterCallingRejectAllEventTypes() { + filter.rejectAllEventTypes(); + assertRejectsAll(); + } + + private void assertRejectsAll() { + assertFalse(filter.getAcceptsFocusEvents()); + assertFalse(filter.getAcceptsKeyPressed()); + assertFalse(filter.getAcceptsKeyReleased()); + assertFalse(filter.getAcceptsKeyTyped()); + assertFalse(filter.getAcceptsMouseClicked()); + assertFalse(filter.getAcceptsMouseDragged()); + assertFalse(filter.getAcceptsMouseEntered()); + assertFalse(filter.getAcceptsMouseExited()); + assertFalse(filter.getAcceptsMouseMoved()); + assertFalse(filter.getAcceptsMousePressed()); + assertFalse(filter.getAcceptsMouseReleased()); + assertFalse(filter.getAcceptsMouseWheelRotated()); + } + + public void testSetAcceptsFocusEventsPersists() { + filter.setAcceptsFocusEvents(false); + assertFalse(filter.getAcceptsFocusEvents()); + } + + public void testSetAcceptsKeyPressedPersists() { + filter.setAcceptsKeyPressed(false); + assertFalse(filter.getAcceptsKeyPressed()); + } + + public void testSetAcceptsKeyReleasedPersists() { + filter.setAcceptsKeyReleased(false); + assertFalse(filter.getAcceptsKeyReleased()); + } + + public void testSetAcceptsKeyTypedPersists() { + filter.setAcceptsKeyTyped(false); + assertFalse(filter.getAcceptsKeyTyped()); + } + + public void testSetAcceptsMouseClickedPersists() { + filter.setAcceptsMouseClicked(false); + assertFalse(filter.getAcceptsMouseClicked()); + } + + public void testSetAcceptsMouseEnteredPersists() { + filter.setAcceptsMouseEntered(false); + assertFalse(filter.getAcceptsMouseEntered()); + } + + public void testSetAcceptsMouseExitedPersists() { + filter.setAcceptsMouseExited(false); + assertFalse(filter.getAcceptsMouseExited()); + } + + public void testSetAcceptsMouseMovedPersists() { + filter.setAcceptsMouseMoved(false); + assertFalse(filter.getAcceptsMouseMoved()); + } + + public void testSetAcceptsMouseDraggedPersists() { + filter.setAcceptsMouseDragged(false); + assertFalse(filter.getAcceptsMouseDragged()); + } + + public void testSetAcceptsMouseMovedPressed() { + filter.setAcceptsMousePressed(false); + assertFalse(filter.getAcceptsMousePressed()); + } + + public void testSetAcceptsMouseMovedReleased() { + filter.setAcceptsMouseReleased(false); + assertFalse(filter.getAcceptsMouseReleased()); + } + + public void testSetAcceptsMouseWheelRotated() { + filter.setAcceptsMouseWheelRotated(false); + assertFalse(filter.getAcceptsMouseWheelRotated()); + } + + public void testAcceptsSimpleEvent() { + final PInputEvent event = buildTestEvent(); + assertAcceptsEvent(event); + } + + public void testRejectsAcceptedEventIfAcceptsHandledEventsIsFalse() { + final PInputEvent event = buildTestEvent(); + event.setHandled(true); + filter.setAcceptsAlreadyHandledEvents(false); + assertRejectsEvent(event); + } + + public void testRejectsEventsUnlessModifiersContainAllOfMask() { + PInputEvent event = buildTestEvent(); + filter.setAndMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + assertRejectsEvent(event); + event = buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + assertAcceptsEvent(event); + + event = buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK | InputEvent.META_MASK); + assertAcceptsEvent(event); + } + + public void testRejectsEventsUnlessModifiersContainOneOfOrMask() { + final PInputEvent event = buildTestEvent(); + filter.setOrMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + assertRejectsEvent(event); + assertRejectsEvent(buildTestEvent(InputEvent.META_MASK)); + assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_MASK)); + assertAcceptsEvent(buildTestEvent(InputEvent.ALT_MASK)); + assertAcceptsEvent(buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); + } + + public void testRejectsEventsUnlessTheyMatchOneOfNotMask() { + final PInputEvent event = buildTestEvent(); + filter.setNotMask(InputEvent.CTRL_MASK | InputEvent.ALT_MASK); + assertAcceptsEvent(event); + + assertAcceptsEvent(buildTestEvent(InputEvent.META_MASK)); + assertRejectsEvent(buildTestEvent(InputEvent.CTRL_MASK)); + assertRejectsEvent(buildTestEvent(InputEvent.ALT_MASK)); + assertRejectsEvent(buildTestEvent(InputEvent.CTRL_MASK | InputEvent.ALT_MASK)); + } + + public void testRejectsMouseEventsIfMouseClickFilterSet() { + filter.setAcceptClickCount((short) 1); + assertRejectsEvent(buildTestEvent(0, 0)); + assertAcceptsEvent(buildTestEvent(0, 1)); + assertRejectsEvent(buildTestEvent(0, 2)); + assertRejectsEvent(buildTestEvent(0, 3)); + } + + public void testMarksEventsAsHandledIsHonnored() { + filter.setMarksAcceptedEventsAsHandled(true); + final PInputEvent event = buildTestEvent(); + assertAcceptsEvent(event); + assertTrue(event.isHandled()); + } + + public void testRejectAllClickCountsIsHonoured() { + filter.rejectAllClickCounts(); + assertRejectsEvent(buildTestEvent(0, 0)); + assertRejectsEvent(buildTestEvent(0, 1)); + assertRejectsEvent(buildTestEvent(0, 2)); + assertRejectsEvent(buildTestEvent(0, 3)); + + } + + private void assertRejectsEvent(final PInputEvent event) { + assertFalse(filter.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); + } + + private void assertAcceptsEvent(final PInputEvent event) { + assertTrue(filter.acceptsEvent(event, MouseEvent.MOUSE_CLICKED)); + } + + private PInputEvent buildTestEvent() { + return buildTestEvent(InputEvent.BUTTON1_MASK); + } + + private PInputEvent buildTestEvent(final int modifiers) { + return buildTestEvent(modifiers, 0); + } + + private PInputEvent buildTestEvent(final int modifiers, final int clickCount) { + final JComponent component = new JPanel(); + final PInputManager inputManager = new PInputManager(); + + final MouseEvent event = new MouseEvent(component, 1, System.currentTimeMillis(), modifiers, 1, 1, clickCount, + false); + return new PInputEvent(inputManager, event); + } +} diff --git a/core/src/test/java/org/piccolo2d/event/PInputEventTest.java b/core/src/test/java/org/piccolo2d/event/PInputEventTest.java new file mode 100644 index 0000000..1e38455 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/PInputEventTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import java.awt.Dimension; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; + +import junit.framework.TestCase; + +/** + * Unit test for PInputEvent. + */ +public class PInputEventTest extends TestCase { + private PCanvas canvas; + private MouseEvent swingEvent; + private PInputEvent mouseEvent; + + public void setUp() { + canvas = new PCanvas(); + canvas.setPreferredSize(new Dimension(100, 100)); + canvas.setBounds(0, 0, 100, 100); + swingEvent = buildSwingClick(5, 5); + final PCamera camera = canvas.getCamera(); + final PPickPath pickPath = new PPickPath(camera, new PBounds(0, 0, 10, 10)); + mouseEvent = new PInputEvent(canvas.getRoot().getDefaultInputManager(), swingEvent); + mouseEvent.setPath(pickPath); + } + + public void testInputManagerShouldBeSameAsGivenToConstructor() { + assertSame(canvas.getRoot().getDefaultInputManager(), mouseEvent.getInputManager()); + } + + public void testComponentIsComponentPassedToSwingEvent() { + assertEquals(canvas, mouseEvent.getComponent()); + } + + public void testKeyboardAccessorsThrowExceptionsOnMousEvents() { + try { + mouseEvent.getKeyChar(); + } + catch (final IllegalStateException e) { + // expected + } + + try { + mouseEvent.getKeyCode(); + } + catch (final IllegalStateException e) { + // expected + } + + try { + mouseEvent.getKeyLocation(); + } + catch (final IllegalStateException e) { + // expected + } + + try { + mouseEvent.isActionKey(); + } + catch (final IllegalStateException e) { + // expected + } + + } + + public void testCorrectlyIdentifiesPositiveLeftMouseClick() { + assertTrue(mouseEvent.isLeftMouseButton()); + } + + public void testCorrectlyIdentifiesNegativeRightMouseClick() { + assertFalse(mouseEvent.isRightMouseButton()); + } + + public void testCorrectlyIdentifiesNegativeMiddleMouseClick() { + assertFalse(mouseEvent.isMiddleMouseButton()); + } + + public void testEventsAreNotHandledByDefault() { + assertFalse(mouseEvent.isHandled()); + } + + public void testSetHandledPersists() { + mouseEvent.setHandled(true); + assertTrue(mouseEvent.isHandled()); + } + + public void testHandledEventCanBeUnHandled() { + mouseEvent.setHandled(true); + mouseEvent.setHandled(false); + assertFalse(mouseEvent.isHandled()); + } + + public void testReturnsCorrectModifiers() { + assertEquals(InputEvent.BUTTON1_MASK, mouseEvent.getModifiers()); + } + + public void testGetButtonUsesWhatWasPassedToMouseEvent() { + assertEquals(MouseEvent.BUTTON1, mouseEvent.getButton()); + } + + private MouseEvent buildSwingClick(final int x, final int y) { + return new MouseEvent(canvas, 1, System.currentTimeMillis(), InputEvent.BUTTON1_MASK, x, y, 1, false, + MouseEvent.BUTTON1); + } + +} diff --git a/core/src/test/java/org/piccolo2d/event/PPanEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PPanEventHandlerTest.java new file mode 100644 index 0000000..b7a8b90 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/PPanEventHandlerTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import org.piccolo2d.event.PPanEventHandler; + +import junit.framework.TestCase; + +/** + * Unit test for PPanEventHander. + */ +public class PPanEventHandlerTest extends TestCase { + private PPanEventHandler handler; + + public void setUp() { + handler = new PPanEventHandler(); + } + + public void testAutoPanIsTrueByDefault() { + assertTrue(handler.getAutopan()); + } + + public void testSetAutoPanPersists() { + handler.setAutopan(true); + assertTrue(handler.getAutopan()); + } + + public void testDefaultMinAutoPanSpeed() { + assertEquals(250, handler.getMinAutoPanSpeed(), 0.0000001); + } + + public void testMinAutoPanSpeedPersists() { + handler.setMinAutopanSpeed(10); + assertEquals(10, handler.getMinAutoPanSpeed(), 0.000001); + } + + public void testMaxDefaultAutoPanSpeed() { + assertEquals(250, handler.getMinAutoPanSpeed(), 0.0000001); + } + + public void testMaxAutoPanSpeedPersists() { + handler.setMaxAutopanSpeed(10); + assertEquals(10, handler.getMaxAutoPanSpeed(), 0.000001); + } +} diff --git a/core/src/test/java/org/piccolo2d/event/PZoomEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PZoomEventHandlerTest.java new file mode 100644 index 0000000..1d08724 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/PZoomEventHandlerTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.event; + +import org.piccolo2d.event.PZoomEventHandler; + +import junit.framework.TestCase; + +/** + * Unit test for PZoomEventHandler. + */ +public class PZoomEventHandlerTest extends TestCase { + + public PZoomEventHandlerTest(final String name) { + super(name); + } + + public void testToString() { + final PZoomEventHandler zoomEventHandler = new PZoomEventHandler(); + assertNotNull(zoomEventHandler.toString()); + } +} diff --git a/core/src/test/java/org/piccolo2d/nodes/PHtmlViewTest.java b/core/src/test/java/org/piccolo2d/nodes/PHtmlViewTest.java new file mode 100644 index 0000000..2bfcbd3 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/nodes/PHtmlViewTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.nodes; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +import org.piccolo2d.MockPropertyChangeListener; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PHtmlView; +import org.piccolo2d.util.PBounds; + +import junit.framework.TestCase; + +/** + * Unit test for PHtmlView. + */ +public class PHtmlViewTest extends TestCase { + + private static final String LOREM_IPSUM = "30. Lorem ipsum dolor sit amet, consectetur adipiscing elit posuere."; + private MockPropertyChangeListener mockListener; + + public void setUp() { + mockListener = new MockPropertyChangeListener(); + } + + public void testConstructorRetainsHtmlWithCSSStyling() { + PHtmlView html = new PHtmlView("html text
"); + assertEquals("html text
", html.getText()); + } + + public void testConstructorRetainsAllParametersWhenRealHtml() { + PHtmlView html = new PHtmlView("html text
"); + assertEquals("html text
", html.getText()); + } + + public void testConstructorRetainsAllParameters() { + Font font = new Font("Serif", Font.PLAIN, 12); + PHtmlView html = new PHtmlView("not html", font, Color.RED); + assertEquals("not html", html.getText()); + assertEquals(font, html.getFont()); + assertEquals(Color.RED, html.getTextColor()); + } + + public void testConstructorAcceptsRealisticHtml() { + PHtmlView html = new PHtmlView("html text
"); + assertNotNull(html); + assertEquals("html text
", html.getText()); + } + + public void testConstructorAcceptsNonHtmlText() { + PHtmlView html = new PHtmlView("not html"); + assertNotNull(html); + assertEquals("not html", html.getText()); + assertEquals(PHtmlView.DEFAULT_FONT, html.getFont()); + assertEquals(PHtmlView.DEFAULT_TEXT_COLOR, html.getTextColor()); + } + + public void testConstructorAcceptsNullHtml() { + PHtmlView html = new PHtmlView(null); + assertEquals(null, html.getText()); + } + + public void testDefaultConstructorHasExpectedDefaults() { + PHtmlView html = new PHtmlView(); + assertEquals(null, html.getText()); + assertEquals(PHtmlView.DEFAULT_FONT, html.getFont()); + assertEquals(PHtmlView.DEFAULT_TEXT_COLOR, html.getTextColor()); + } + + public void testConstructorAcceptsNullFontAndColor() { + PHtmlView html9 = new PHtmlView("not html", null, null); + assertEquals(null, html9.getFont()); + assertEquals(null, html9.getTextColor()); + } + + public void testConstructorAcceptsNullColor() { + Font font = new Font("Serif", Font.PLAIN, 12); + PHtmlView html = new PHtmlView("not html", font, null); + assertEquals(null, html.getTextColor()); + } + + public void testConstructorAcceptsNullFont() { + PHtmlView html = new PHtmlView("not html", null, Color.RED); + assertEquals(null, html.getFont()); + } + + public void testGetClickedAddressReturnsSingleQuotedAddress() { + PHtmlView html = new PHtmlView("testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("http://www.testing.com", html.getLinkAddressAt(5,5)); + } + + public void testGetClickedAddressReturnsDoubleQuotedAddress() { + PHtmlView html = new PHtmlView("testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("http://www.testing.com", html.getLinkAddressAt(5,5)); + } + + public void testBracketsAreValidInHrefs() { + PHtmlView html = new PHtmlView("testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("a>b", html.getLinkAddressAt(5,5)); + } + + public void testGetClickedAddressReturnsNullWhenInvalid() { + PHtmlView html = new PHtmlView("b'>testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertNull(html.getLinkAddressAt(5,5)); + } + + public void testGetClickedAddressReturnsHrefWhenMissingEndAnchorTag() { + PHtmlView html = new PHtmlView("testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("testing.com", html.getLinkAddressAt(5,5)); + } + + public void testHandlesTricksyTitles() { + PHtmlView html = new PHtmlView("testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("where to go", html.getLinkAddressAt(5,5)); + } + + public void testHandlesHrefWithoutQuotes() { + PHtmlView html = new PHtmlView("testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("testing.com", html.getLinkAddressAt(5,5)); + } + + public void testUnclosedTagsCauseIgnoreOfTag() { + PHtmlView html = new PHtmlView("Missing End TAg "); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("testing.com", html.getLinkAddressAt(5,5)); + } + + public void testUnclosedQuotesCauseIgnoreOfLink() { + PHtmlView html = new PHtmlView("testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertEquals("", html.getLinkAddressAt(5,5)); + } + + public void testReturnsNullWhenClickOutsideLink() { + PHtmlView html = new PHtmlView("0123456789 testing"); + html.setBounds(new PBounds(0, 0, 100, 100)); + assertNull(html.getLinkAddressAt(5,5)); + } + + public void testSetHtmlColorPersists() { + PHtmlView html = new PHtmlView(); + html.setTextColor(Color.RED); + assertEquals(Color.RED, html.getTextColor()); + } + + public void testFontIsNotNullByDefault() { + PHtmlView html = new PHtmlView(); + assertNotNull(html.getFont()); + } + + public void testHtmlColorIsNotNullByDefault() { + PHtmlView html = new PHtmlView(); + assertNotNull(html.getTextColor()); + } + + public void testSetHtmlFiresEventOnChangeOnly() { + PHtmlView html = new PHtmlView(); + html.addPropertyChangeListener(PHtmlView.PROPERTY_TEXT, mockListener); + html.setText("testing"); + assertEquals(1, mockListener.getPropertyChangeCount()); + assertEquals(PHtmlView.PROPERTY_TEXT, mockListener.getPropertyChange(0).getPropertyName()); + html.setText("testing"); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testSetHtmlToNullIsAllowed() { + PHtmlView html = new PHtmlView(); + html.setText(null); + assertNull(html.getText()); + } + + public void testSetFontPerists() { + PHtmlView html = new PHtmlView(); + Font font = Font.getFont("arial"); + html.setFont(font); + assertSame(font, html.getFont()); + } + + public void testPaintFillsBounds() { + PHtmlView html = new PHtmlView(LOREM_IPSUM); + html.setPaint(Color.RED); + + PCanvas canvas = new PCanvas(); + canvas.setBackground(Color.WHITE); + canvas.setBounds(0, 0, 500, 30); + canvas.getLayer().addChild(html); + + BufferedImage image = new BufferedImage(600, 30, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = image.createGraphics(); + canvas.paint(g2); + + assertEquals(Color.red.getRGB(), image.getRGB(0, 0)); + assertEquals(Color.red.getRGB(), image.getRGB(0, (int)(html.getHeight()-1))); + assertEquals(Color.red.getRGB(), image.getRGB(300, 0)); + } + + public void testClone() { + PHtmlView html = new PHtmlView(LOREM_IPSUM); + html.setTextColor(Color.RED); + PHtmlView clone = (PHtmlView) html.clone(); + assertNotNull(clone); + assertEquals(Color.RED, clone.getTextColor()); + assertEquals(LOREM_IPSUM, clone.getText()); + } +} diff --git a/core/src/test/java/org/piccolo2d/nodes/PImageTest.java b/core/src/test/java/org/piccolo2d/nodes/PImageTest.java new file mode 100644 index 0000000..cc15302 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/nodes/PImageTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.nodes; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.util.PPaintContext; + +import junit.framework.TestCase; + +/** + * Unit test for PImage. + */ +public class PImageTest extends TestCase { + + public void testClone() { + final PImage srcNode = new PImage(new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB)); + final PImage clonedNode = (PImage) srcNode.clone(); + assertNotNull(clonedNode.getImage()); + + assertEquals(srcNode.getImage().getWidth(null), clonedNode.getImage().getWidth(null)); + assertEquals(srcNode.getImage().getHeight(null), clonedNode.getImage().getHeight(null)); + + assertEquals(srcNode.getBounds(), clonedNode.getBounds()); + } + + public void testToString() { + final PImage aNode = new PImage(new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB)); + assertNotNull(aNode.toString()); + } + + public void testToBufferedImageReturnsCopyIfToldTo() { + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final BufferedImage copy = PImage.toBufferedImage(img, true); + assertNotSame(img, copy); + } + + public void testCanBeCreatedFromFile() throws IOException { + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final File imgFile = File.createTempFile("test", ".jpeg"); + ImageIO.write(img, "JPEG", imgFile); + imgFile.deleteOnExit(); + final PImage imageNode = new PImage(imgFile.getAbsolutePath()); + assertNotNull(imageNode.getImage()); + assertEquals(100, imageNode.getImage().getWidth(null)); + assertEquals(100, imageNode.getImage().getHeight(null)); + } + + public void testCanBeCreatedFromUrl() throws IOException { + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final File imgFile = File.createTempFile("test", ".jpeg"); + imgFile.deleteOnExit(); + ImageIO.write(img, "JPEG", imgFile); + + final PImage imageNode = new PImage(imgFile.toURI().toURL()); + assertEquals(100, imageNode.getImage().getWidth(null)); + assertEquals(100, imageNode.getImage().getHeight(null)); + } + + public void testImageCanBeSetFromFile() throws IOException { + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + final File imgFile = File.createTempFile("test", ".jpeg"); + imgFile.deleteOnExit(); + ImageIO.write(img, "JPEG", imgFile); + + final PImage imageNode = new PImage(); + imageNode.setImage(imgFile.getAbsolutePath()); + assertEquals(100, imageNode.getImage().getWidth(null)); + assertEquals(100, imageNode.getImage().getHeight(null)); + } + + public void testPaintAnEmptyImageNodeDoesNothing() { + final PImage imageNode = new PImage(); + + final BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + + final PPaintContext paintContext = new PPaintContext(img.createGraphics()); + imageNode.paint(paintContext); + } + +} diff --git a/core/src/test/java/org/piccolo2d/nodes/PPathTest.java b/core/src/test/java/org/piccolo2d/nodes/PPathTest.java new file mode 100644 index 0000000..573615b --- /dev/null +++ b/core/src/test/java/org/piccolo2d/nodes/PPathTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.nodes; + +import java.awt.Color; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +import org.piccolo2d.MockPropertyChangeListener; +import org.piccolo2d.PiccoloAsserts; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PObjectOutputStream; + +import junit.framework.TestCase; + +/** + * Unit test for PPath. + */ +public class PPathTest extends TestCase { + + private MockPropertyChangeListener mockListener; + + public void setUp() { + mockListener = new MockPropertyChangeListener(); + } + + public void testStrokeIsNotNullByDefault() { + final PPath path = new PPath(); + assertNotNull(path.getStroke()); + } + + public void testStrokePaintIsBlackByDefault() { + final PPath path = new PPath(); + assertEquals(Color.BLACK, path.getStrokePaint()); + } + + public void testClone() { + PPath p = PPath.createEllipse(0, 0, 100, 100); + PPath cloned = (PPath) p.clone(); + assertEquals(p.getBounds(), cloned.getBounds()); + //assertEquals(p.getPathReference()., cloned.getPathReference()); + } + + public void testSerialization() throws IOException, ClassNotFoundException { + final PPath srcPath = PPath.createEllipse(0, 0, 100, 100); + final PBounds srcBounds = srcPath.getBounds(); + + final File file = File.createTempFile("test", "ser"); + + serializeToFile(srcPath, file); + final PPath resultPath = deserializeFromFile(srcBounds, file); + file.deleteOnExit(); + + assertEquals(resultPath.getBounds(), srcBounds); + } + + private PPath deserializeFromFile(final PBounds b, final File file) throws FileNotFoundException, IOException, + ClassNotFoundException { + PPath path; + final FileInputStream fin = new FileInputStream(file); + final ObjectInputStream in = new ObjectInputStream(fin); + path = (PPath) in.readObject(); + + return path; + } + + private void serializeToFile(final PPath p, final File file) throws FileNotFoundException, IOException { + final FileOutputStream fout = new FileOutputStream(file); + final PObjectOutputStream out = new PObjectOutputStream(fout); + out.writeObjectTree(p); + out.flush(); + out.close(); + } + + public void testCreateRectangleReturnsValidPPath() { + final PPath path = PPath.createRectangle(0, 0, 100, 50); + assertNotNull(path); + + // Seems like rounding is affecting the bounds greatly + PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); + } + + public void testCreateEllipseReturnsValidPPath() { + final PPath path = PPath.createEllipse(0, 0, 100, 50); + assertNotNull(path); + + // Seems like rounding is affecting the bounds greatly + PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); + } + + public void testCreateRoundedRectReturnsValidPPath() { + final PPath path = PPath.createRoundRectangle(0, 0, 100, 50, 10, 10); + assertNotNull(path); + + // Seems like rounding is affecting the bounds greatly + PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); + } + + public void testCreateLineReturnsValidPPath() { + final PPath path = PPath.createLine(0, 0, 100, 0); + assertNotNull(path); + + // Seems like rounding is affecting the bounds greatly + PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 0), path.getBounds(), 2.0d); + } + + public void testCreatePolyLinePoint2DReturnsValidPPath() { + final PPath path = PPath.createPolyline(new Point2D[] { new Point2D.Double(0, 0), new Point2D.Double(100, 50), + new Point2D.Double(100, 0) }); + assertNotNull(path); + + // Seems like rounding is affecting the bounds greatly + PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); + } + + public void testCreatePolyLineFloatsReturnsValidPPath() { + final PPath path = PPath.createPolyline(new float[] { 0, 100, 100 }, new float[] { 0, 50, 0 }); + assertNotNull(path); + + // Seems like rounding is affecting the bounds greatly + PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 50), path.getBounds(), 2.0d); + } + + public void testSetStrokePaintPersists() { + final PPath path = new PPath(); + path.setStrokePaint(Color.RED); + assertEquals(Color.RED, path.getStrokePaint()); + } + + public void testSetStrokeFiresPropertyChangeEvent() { + final PPath path = new PPath(); + path.addPropertyChangeListener(PPath.PROPERTY_STROKE_PAINT, mockListener); + path.setStrokePaint(Color.RED); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + + public void testChangingPathFiresPropertyChangeEvent() { + final PPath path = new PPath(); + path.addPropertyChangeListener(PPath.PROPERTY_PATH, mockListener); + path.append(new Rectangle2D.Double(0, 0, 100, 50), true); + assertEquals(1, mockListener.getPropertyChangeCount()); + } + +} diff --git a/core/src/test/java/org/piccolo2d/nodes/PTextTest.java b/core/src/test/java/org/piccolo2d/nodes/PTextTest.java new file mode 100644 index 0000000..5a39983 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/nodes/PTextTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.nodes; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; + +import org.piccolo2d.MockPropertyChangeListener; +import org.piccolo2d.nodes.PText; + +import junit.framework.TestCase; + +/** + * Unit test for PText. + */ +public class PTextTest extends TestCase { + + private PText textNode; + private MockPropertyChangeListener mockListener; + + public PTextTest(final String name) { + super(name); + } + + public void setUp() { + textNode = new PText(); + mockListener = new MockPropertyChangeListener(); + } + + public void testClone() { + textNode.setTextPaint(Color.BLUE); + textNode.setText("Boo"); + final PText clonedNode = (PText) textNode.clone(); + assertEquals("Boo", clonedNode.getText()); + assertEquals(textNode.getFont(), clonedNode.getFont()); + assertEquals(Color.BLUE, clonedNode.getTextPaint()); + } + + public void testTextIsEmptyByDefault() { + final PText textNode = new PText(); + assertEquals("", textNode.getText()); + } + + public void testTextMayBeAssignedEmptyString() { + textNode.setText(""); + assertEquals("", textNode.getText()); + } + + public void testTextNullGetsInterpretedAsEmptyString() { + textNode.setText(null); + assertEquals("", textNode.getText()); + } + + public void testBoundsGrowWithTextByDefault() { + final PText text123 = new PText("123"); + final double width123 = text123.getBounds().getWidth(); + + final PText text1234 = new PText("1234"); + + final double width1234 = text1234.getBounds().getWidth(); + + assertTrue(width123 < width1234); + } + + public void testBoundsOfEmptyString() { + textNode.setText(""); + assertEquals(0, textNode.getBoundsReference().getWidth(), 0.000001); + textNode.setText(null); + assertEquals(0, textNode.getBoundsReference().getWidth(), 0.000001); + } + + public void testToString() { + textNode.setText("hello world"); + assertNotNull(textNode.toString()); + } + + public void testHorizontalAlignmentIsLeftByDefault() { + assertEquals(Component.LEFT_ALIGNMENT, textNode.getHorizontalAlignment(), 0.000001); + } + + public void testSetHorizontalAlignmentPersists() { + textNode.setHorizontalAlignment(Component.RIGHT_ALIGNMENT); + assertEquals(Component.RIGHT_ALIGNMENT, textNode.getHorizontalAlignment(), 0.000001); + } + + public void testSetHorizontalAlignmentInvalidValues() { + try { + textNode.setHorizontalAlignment(-2.0f); + } + catch (final IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(2.0f); + } + catch (final IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(-Float.MAX_VALUE); + } + catch (final IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(Float.MAX_VALUE); + } + catch (final IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(-1.00f); + } + catch (final IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(1.00f); + } + catch (final IllegalArgumentException e) { + // expected + } + } + + public void testTextPaintIsBlackByDefault() { + assertEquals(Color.BLACK, textNode.getTextPaint()); + } + + public void testSetTextPaintPersists() { + textNode.setTextPaint(Color.RED); + assertEquals(Color.RED, textNode.getTextPaint()); + } + + public void testConstrainWidthToTextTrueByDefault() { + assertTrue(textNode.isConstrainWidthToTextWidth()); + } + + public void testConstrainHeightToTextTrueByDefault() { + assertTrue(textNode.isConstrainHeightToTextHeight()); + } + + public void testConstrainWidthPersists() { + textNode.setConstrainWidthToTextWidth(true); + assertTrue(textNode.isConstrainWidthToTextWidth()); + } + + public void testConstrainHeightPersists() { + textNode.setConstrainHeightToTextHeight(true); + assertTrue(textNode.isConstrainHeightToTextHeight()); + } + + public void testDefaultGreekThreshold() { + assertEquals(PText.DEFAULT_GREEK_THRESHOLD, textNode.getGreekThreshold(), 0.000001); + } + + public void testSetGreekThreshold() { + textNode.setGreekThreshold(2); + assertEquals(2, textNode.getGreekThreshold(), 0.000001); + } + + public void testDefaultFont() { + assertEquals(PText.DEFAULT_FONT, textNode.getFont()); + } + + public void testSetFontPersists() { + final Font newFont = new Font("Arial", Font.BOLD, 10); + textNode.setFont(newFont); + assertEquals(newFont, textNode.getFont()); + } + + public void testSetFontFiresPropertyChangedEvent() { + textNode.addPropertyChangeListener(PText.PROPERTY_FONT, mockListener); + final Font newFont = new Font("Arial", Font.BOLD, 10); + textNode.setFont(newFont); + + assertEquals(1, mockListener.getPropertyChangeCount()); + assertEquals(PText.PROPERTY_FONT, mockListener.getPropertyChange(0).getPropertyName()); + } +} diff --git a/core/src/test/java/org/piccolo2d/util/PAffineTransformTest.java b/core/src/test/java/org/piccolo2d/util/PAffineTransformTest.java new file mode 100644 index 0000000..f978710 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/util/PAffineTransformTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.Dimension; +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.piccolo2d.PiccoloAsserts; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PAffineTransformException; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + +import junit.framework.TestCase; + +/** + * Unit test for PAffineTransform. + */ +public class PAffineTransformTest extends TestCase { + + private PAffineTransform at; + + public PAffineTransformTest(final String aName) { + super(aName); + } + + public void setUp() { + at = new PAffineTransform(); + } + + public void testRotation() { + at.rotate(Math.toRadians(45)); + assertEquals(at.getRotation(), Math.toRadians(45), 0.000000001); + at.setRotation(Math.toRadians(90)); + assertEquals(at.getRotation(), Math.toRadians(90), 0.000000001); + } + + public void testScale() { + at.scaleAboutPoint(0.45, 0, 1); + assertEquals(at.getScale(), 0.45, 0.000000001); + at.setScale(0.11); + assertEquals(at.getScale(), 0.11, 0.000000001); + } + + public void testTransformRectLeavesEmptyBoundsEmpty() { + final PBounds b1 = new PBounds(); + at.scale(0.5, 0.5); + at.translate(100, 50); + + at.transform(b1, b1); + assertTrue(b1.isEmpty()); + } + + public void testTransformRect() { + final PBounds b1 = new PBounds(0, 0, 100, 80); + final PBounds b2 = new PBounds(100, 100, 100, 80); + + at.scale(0.5, 0.5); + at.translate(100, 50); + + at.transform(b1, b1); + at.transform(b2, b2); + + PiccoloAsserts.assertEquals(new PBounds(50, 25, 50, 40), b1, 0.0001); + PiccoloAsserts.assertEquals(new PBounds(100, 75, 50, 40), b2, 0.0001); + + at.inverseTransform(b1, b1); + at.inverseTransform(b2, b2); + + PiccoloAsserts.assertEquals(new PBounds(0, 0, 100, 80), b1, 0.0001); + PiccoloAsserts.assertEquals(new PBounds(100, 100, 100, 80), b2, 0.0001); + } + + public void testThrowsExceptionWhenSetting0Scale() { + try { + at.setScale(0); + fail("Setting 0 scale should throw exception"); + } + catch (final RuntimeException e) { + // expected + } + } + + public void testSetOffsetLeavesRotationUntouched() { + at.setRotation(Math.PI); + at.setOffset(100, 50); + assertEquals(Math.PI, at.getRotation(), 0.001); + } + + public void testTransformDimensionWorks() { + final Dimension d1 = new Dimension(100, 50); + at.setScale(2); + final Dimension d2 = new Dimension(0, 0); + at.transform(d1, d2); + assertEquals(new Dimension(200, 100), d2); + } + + public void testTransformDimensionWorksWithSecondParamNull() { + final Dimension d1 = new Dimension(100, 50); + at.setScale(2); + final Dimension2D d2 = at.transform(d1, null); + assertEquals(new Dimension(200, 100), d2); + } + + public void testLocalToViewDimensionThrowsExceptionWhenTransformIsNonInvertible() { + at.setTransform(new PAffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); + try { + at.inverseTransform(new PDimension(1, 2), null); + fail("Exception not thrown when inverting non-invertible transform"); + } + catch (final PAffineTransformException e) { + // expected + } + } + + public void testLocalToViewPoint2DThrowsExceptionWhenTransformIsNonInvertible() { + at.setTransform(new PAffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); + try { + at.inverseTransform(new Point2D.Double(1, 2), null); + fail("Exception not thrown when inverting non-invertible transform"); + } + catch (final PAffineTransformException e) { + // expected + } + } + + public void testLocalToViewRectangle2DThrowsExceptionWhenTransformIsNonInvertible() { + at.setTransform(new PAffineTransform(new double[] { 0, 0, 0, 0, 0, 0 })); + try { + at.inverseTransform(new Rectangle2D.Double(1, 2, 3, 4), null); + fail("Exception not thrown when inverting non-invertible transform"); + } + catch (final PAffineTransformException e) { + // expected + } + } +} diff --git a/core/src/test/java/org/piccolo2d/util/PBoundsTest.java b/core/src/test/java/org/piccolo2d/util/PBoundsTest.java new file mode 100644 index 0000000..109246e --- /dev/null +++ b/core/src/test/java/org/piccolo2d/util/PBoundsTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.geom.Rectangle2D; + +import org.piccolo2d.PiccoloAsserts; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + +import junit.framework.TestCase; + +/** + * Unit test for PBounds. + */ +public class PBoundsTest extends TestCase { + public void testDefaultBoundsAreEmpty() { + final PBounds b = new PBounds(); + PiccoloAsserts.assertEquals(new PBounds(0, 0, 0, 0), b, 0.0001); + } + + public void testBoundsCloneConstructorWorks() { + final PBounds b1 = new PBounds(10, 15, 100, 50); + final PBounds b2 = new PBounds(b1); + PiccoloAsserts.assertEquals(b1, b2, 0.00001); + } + + public void testBoundsCanBeConstructedFromRectangle2D() { + final Rectangle2D r = new Rectangle2D.Double(1, 2, 3, 4); + final PBounds b = new PBounds(r); + PiccoloAsserts.assertEquals(new PBounds(1, 2, 3, 4), b, 0.000001); + } + + /* + * public void testBoundsCreatedFromPointAndWidthIsCorrect() { PBounds b = + * new PBounds(new Point2D.Double(), 10, 10); + * PiccoloAsserts.assertEquals(new PBounds(-10, -10, 20, 20), b, 0.000001); + * } + */ + + public void testResetToZeroClearsBounds() { + final PBounds b = new PBounds(1, 2, 3, 4); + b.resetToZero(); + assertTrue(b.isEmpty()); + PiccoloAsserts.assertEquals(new PBounds(), b, 0.0000001); + } + + public void testAdding1PointToEmptyYieldsEmpty() { + final PBounds b = new PBounds(); + b.add(-10, -10); + PiccoloAsserts.assertEquals(new PDimension(0, 0), b.getSize(), 0.00001); + } + + public void testAdding2PointsToEmptyYieldsNotEmpty() { + final PBounds b = new PBounds(); + b.add(-10, -10); + b.add(0, 0); + PiccoloAsserts.assertEquals(new PDimension(10, 10), b.getSize(), 0.00001); + } + + // TODO: This test should pass, but making it do so would break binary compatability + /* + * public void testWhenBoundsHas0HeightFullBoundsIsCorrectlyReturned() { + * final PNode node = new PNode(); final PBounds testBounds = new + * PBounds(10, 10, 10, 0); node.setBounds(testBounds); + * assertEquals(testBounds, node.getFullBounds()); } + */ +} diff --git a/core/src/test/java/org/piccolo2d/util/PDebugTest.java b/core/src/test/java/org/piccolo2d/util/PDebugTest.java new file mode 100644 index 0000000..8cdb45b --- /dev/null +++ b/core/src/test/java/org/piccolo2d/util/PDebugTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.Color; + +import org.piccolo2d.util.PDebug; + +import junit.framework.TestCase; + +/** + * Unit test for PDebug. + */ +public class PDebugTest extends TestCase { + public void setUp() { + PDebug.resetFPSTiming(); + } + + public void testGetDebugColourGeneratesGraysInCycle() { + assertEquals(new Color(100, 100, 100, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(110, 110, 110, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(120, 120, 120, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(130, 130, 130, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(140, 140, 140, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(150, 150, 150, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(160, 160, 160, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(170, 170, 170, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(180, 180, 180, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(190, 190, 190, 150), PDebug.getDebugPaintColor()); + assertEquals(new Color(100, 100, 100, 150), PDebug.getDebugPaintColor()); + } + + public void testUnlessOutputWasProcessedFPSisZero() throws InterruptedException { + assertEquals(0.0, PDebug.getInputFPS(), 0.00001); + PDebug.startProcessingInput(); + Thread.sleep(2); + PDebug.endProcessingInput(); + + assertEquals(0.0, PDebug.getTotalFPS(), 0.0001); + } +} diff --git a/core/src/test/java/org/piccolo2d/util/PDimensionTest.java b/core/src/test/java/org/piccolo2d/util/PDimensionTest.java new file mode 100644 index 0000000..b2579fe --- /dev/null +++ b/core/src/test/java/org/piccolo2d/util/PDimensionTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.awt.Dimension; +import java.awt.geom.Dimension2D; +import java.awt.geom.Point2D; + +import org.piccolo2d.util.PDimension; + +import junit.framework.TestCase; + +/** + * Unit test for PDimension. + */ +public class PDimensionTest extends TestCase { + public void testDefaultConstructorResultsInEmptyDimension() { + final PDimension dimension = new PDimension(); + + assertEquals(0, dimension.getWidth(), 0.00001); + assertEquals(0, dimension.getHeight(), 0.00001); + } + + public void testCloningConstructorDoesSo() { + final Dimension2D src = new Dimension(100, 50); + final PDimension copy = new PDimension(src); + + assertEquals(100, copy.getWidth(), 0.00001); + assertEquals(50, copy.getHeight(), 0.00001); + } + + public void testDimensionGetBuiltFromPoints() { + final PDimension dimension = new PDimension(new Point2D.Double(-50, -25), new Point2D.Double(50, 25)); + assertEquals(100, dimension.getWidth(), 0.00001); + assertEquals(50, dimension.getHeight(), 0.00001); + } +} diff --git a/core/src/test/java/org/piccolo2d/util/PObjectOutputStreamTest.java b/core/src/test/java/org/piccolo2d/util/PObjectOutputStreamTest.java new file mode 100644 index 0000000..3c640e0 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/util/PObjectOutputStreamTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.piccolo2d.util.PObjectOutputStream; + +import junit.framework.TestCase; + +/** + * Unit test for PObjectOutputStream. + */ +public class PObjectOutputStreamTest extends TestCase { + private PObjectOutputStream outStream; + private ByteArrayOutputStream outByteStream; + + public void setUp() throws IOException { + outByteStream = new ByteArrayOutputStream(); + outStream = new PObjectOutputStream(outByteStream); + } + + public void testToByteArrayThrowsExceptionOnNull() throws IOException { + try { + PObjectOutputStream.toByteArray(null); + } + catch (final NullPointerException e) { + // expected + } + } + + public void testToByteArrayOnEmptyStreamWorks() throws IOException { + outStream.flush(); + final byte[] outputBytes = outByteStream.toByteArray(); + assertNotNull(outputBytes); + assertTrue("Header not output", outputBytes.length > 0); + } + + public void testWriteConditionalObjectAcceptsNull() throws IOException { + + } +} diff --git a/core/src/test/java/org/piccolo2d/util/PPickPathTest.java b/core/src/test/java/org/piccolo2d/util/PPickPathTest.java new file mode 100644 index 0000000..01c79da --- /dev/null +++ b/core/src/test/java/org/piccolo2d/util/PPickPathTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.util; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PPickPath; + +import junit.framework.TestCase; + +/** + * Unit test for PPickPath. + */ +public class PPickPathTest extends TestCase { + + public PPickPathTest(final String name) { + super(name); + } + + public void testPick() { + final PCanvas canvas = new PCanvas(); + final PCamera camera = canvas.getCamera(); + final PLayer layer = canvas.getLayer(); + + camera.setBounds(0, 0, 100, 100); + + final PNode a = PPath.createRectangle(0, 0, 100, 100); + final PNode b = PPath.createRectangle(0, 0, 100, 100); + final PNode c = PPath.createRectangle(0, 0, 100, 100); + + layer.addChild(a); + layer.addChild(b); + layer.addChild(c); + + final PPickPath pickPath = camera.pick(50, 50, 2); + + assertTrue(pickPath.getPickedNode() == c); + assertTrue(pickPath.nextPickedNode() == b); + assertTrue(pickPath.nextPickedNode() == a); + assertTrue(pickPath.nextPickedNode() == camera); + assertTrue(pickPath.nextPickedNode() == null); + assertTrue(pickPath.nextPickedNode() == null); + } +} diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java index 0d7d590..e0b661a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java @@ -30,11 +30,12 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java index e395bf0..a68e559 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java @@ -36,12 +36,13 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPaintContext; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PHandle; import edu.umd.cs.piccolox.util.PLocator; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java index 17944b8..8eeefb6 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java @@ -39,19 +39,20 @@ import javax.swing.JDialog; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PImage; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPaintContext; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.nodes.P3DRect; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java index 0bac02f..aee476a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java @@ -30,10 +30,11 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PBoundsHandle; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java index 1c78f10..dc4c5d4 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java @@ -28,10 +28,11 @@ */ package edu.umd.cs.piccolo.examples; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; public class CenterExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java index 87d9337..fd8d5c1 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java @@ -31,12 +31,13 @@ import java.awt.Color; import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java index c8af82e..8fc08b5 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java @@ -30,9 +30,10 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.nodes.PClip; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java index a5575c8..59ddea2 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java @@ -30,11 +30,12 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.nodes.PComposite; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java index 937d41e..b7884b9 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java @@ -33,12 +33,13 @@ import java.util.Iterator; import java.util.Random; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.util.PFixedWidthStroke; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java index 5f05695..e0779ef 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java @@ -32,13 +32,14 @@ import java.awt.event.InputEvent; import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java index 0413b62..c608a43 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java @@ -45,7 +45,8 @@ import javax.swing.WindowConstants; import javax.swing.border.TitledBorder; -import edu.umd.cs.piccolo.util.PDebug; +import org.piccolo2d.util.PDebug; + import edu.umd.cs.piccolox.PFrame; public class ExampleRunner extends JFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java index 62e7db9..7c78051 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java @@ -30,8 +30,9 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.PFrame; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java index 35a55a5..2d361d6 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java @@ -33,12 +33,13 @@ import java.util.ArrayList; import java.util.Random; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java index a757891..876ca65 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java @@ -38,14 +38,15 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PPaintContext; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java index 86916de..7d7425a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java @@ -33,12 +33,13 @@ import java.awt.Paint; import java.util.ArrayList; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.event.PSelectionEventHandler; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java index ad0108e..95e9b48 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java @@ -31,11 +31,12 @@ import java.awt.BasicStroke; import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PDimension; +import org.piccolo2d.PCanvas; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PDimension; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PBoundsHandle; import edu.umd.cs.piccolox.handles.PHandle; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java index a4ee138..8499146 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java @@ -28,8 +28,9 @@ */ package edu.umd.cs.piccolo.examples; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.PFrame; public class HelloWorldExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java index c6df9c9..aeb2fa1 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java @@ -28,11 +28,12 @@ */ package edu.umd.cs.piccolo.examples; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java index 5539985..39d43ee 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java @@ -32,11 +32,12 @@ import javax.swing.JOptionPane; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PHtmlView; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PHtmlView; + import edu.umd.cs.piccolox.PFrame; public class HtmlViewExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java index f46175e..3ad883d 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java @@ -30,11 +30,12 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java index caefaa9..7ba719c 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java @@ -31,9 +31,10 @@ import java.awt.Color; import java.util.Iterator; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PBoundsHandle; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java index 12da6d4..e6cb752 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java @@ -32,16 +32,17 @@ import java.awt.Graphics2D; import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; +import org.piccolo2d.util.PPaintContext; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PBoundsHandle; import edu.umd.cs.piccolox.nodes.PLens; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java index 1f9ea73..304b9a1 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java @@ -32,9 +32,10 @@ import java.awt.Color; import java.util.Random; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.event.PNavigationEventHandler; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java index d2f0f21..8ca65eb 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java @@ -31,9 +31,10 @@ import java.awt.BasicStroke; import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.nodes.PNodeCache; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java index 2b9a888..09562f3 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java @@ -31,12 +31,13 @@ import java.awt.Color; import java.awt.geom.Dimension2D; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java index 7f2834b..4f95cae 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java @@ -34,17 +34,18 @@ import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PImage; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java index a070618..763cc7b 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java @@ -33,10 +33,11 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/OffscreenCanvasExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/OffscreenCanvasExample.java index 5238485..679cb12 100755 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/OffscreenCanvasExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/OffscreenCanvasExample.java @@ -38,10 +38,11 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferStrategy; -import edu.umd.cs.piccolo.POffscreenCanvas; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.POffscreenCanvas; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + /** * Offscreen canvas example. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java index 7000784..26884b9 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java @@ -28,12 +28,12 @@ */ package edu.umd.cs.piccolo.examples; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.nodes.PText; import edu.umd.cs.piccolox.PFrame; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java index ac2707a..2cd6a44 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java @@ -2,7 +2,8 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.nodes.P3DRect; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java index f6aa9b6..7c60737 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java @@ -33,8 +33,9 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.pswing.PSwing; import edu.umd.cs.piccolox.pswing.PSwingCanvas; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java index 1aeb2f6..145e758 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java @@ -32,12 +32,13 @@ import java.awt.Color; import java.util.Random; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java index 1e88a7e..5a12308 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java @@ -31,9 +31,10 @@ import java.awt.BasicStroke; import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PStickyHandleManager; import edu.umd.cs.piccolox.util.PFixedWidthStroke; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java index 5deaf92..e8c8227 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java @@ -30,9 +30,10 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; public class PositionExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java index de0b3ef..6a8814a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java @@ -31,9 +31,10 @@ import java.awt.geom.Arc2D; import java.awt.geom.GeneralPath; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.activities.PPositionPathActivity; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java index 39dbb37..d4b291b 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java @@ -51,11 +51,12 @@ import javax.swing.JToggleButton; import javax.swing.JToolBar; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.swing.PDefaultScrollDirector; import edu.umd.cs.piccolox.swing.PScrollDirector; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java index 8e429f8..c58068c 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java @@ -30,14 +30,15 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.activities.PActivityScheduler; -import edu.umd.cs.piccolo.activities.PColorActivity; -import edu.umd.cs.piccolo.activities.PInterpolatingActivity; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.activities.PActivityScheduler; +import org.piccolo2d.activities.PColorActivity; +import org.piccolo2d.activities.PInterpolatingActivity; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java index d434baf..eb2f27f 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java @@ -42,11 +42,12 @@ import javax.swing.JToggleButton; import javax.swing.JToolBar; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.swing.PDefaultScrollDirector; import edu.umd.cs.piccolox.swing.PScrollDirector; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java index bc1157d..e010262 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java @@ -30,9 +30,10 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.event.PNotification; import edu.umd.cs.piccolox.event.PNotificationCenter; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java index 80fdbf0..16c4afd 100755 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java @@ -34,11 +34,12 @@ import java.awt.image.BufferedImage; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolo.nodes.PImage; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; + import edu.umd.cs.piccolox.PFrame; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java index f4ec1cb..c70e204 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java @@ -32,13 +32,14 @@ import java.awt.event.InputEvent; import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; public class SquiggleExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java index 3c44a0d..12c0812 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java @@ -30,8 +30,9 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PBoundsHandle; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java index b87c635..5516b72 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java @@ -31,11 +31,12 @@ import java.awt.Color; import java.util.Iterator; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PBoundsHandle; import edu.umd.cs.piccolox.handles.PHandle; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java index 6fadcb5..5c82d6f 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java @@ -31,9 +31,10 @@ import java.awt.BasicStroke; import java.awt.Color; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.util.PFixedWidthStroke; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java index c6a32a5..8f39781 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java @@ -51,13 +51,14 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PHtmlView; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PHtmlView; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.pswing.PSwing; import edu.umd.cs.piccolox.pswing.PSwingCanvas; import edu.umd.cs.piccolox.swing.SwingLayoutNode; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java index 474c93a..805a2a0 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java @@ -28,7 +28,8 @@ */ package edu.umd.cs.piccolo.examples; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.event.PStyledTextEventHandler; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java index e49348b..18c3da3 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java @@ -34,10 +34,11 @@ import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PHtmlView; +import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolo.nodes.PHtmlView; -import edu.umd.cs.piccolo.nodes.PText; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.nodes.PStyledText; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java index 4deff63..4778eec 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java @@ -32,12 +32,13 @@ import java.awt.image.BufferedImage; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolo.nodes.PImage; -import edu.umd.cs.piccolo.nodes.PText; + import edu.umd.cs.piccolox.PFrame; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java index 4b0f364..98e8a03 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java @@ -30,13 +30,14 @@ import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java index 1deaedc..2e92bf0 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java @@ -30,12 +30,13 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; import edu.umd.cs.piccolox.handles.PBoundsHandle; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java index 0991afd..1ad883f 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java @@ -28,11 +28,12 @@ */ package edu.umd.cs.piccolo.examples; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/CalendarNode.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/CalendarNode.java index c997463..af13751 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/CalendarNode.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/CalendarNode.java @@ -30,9 +30,10 @@ import java.awt.Font; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; + class CalendarNode extends PNode { static int DEFAULT_NUM_DAYS = 7; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/DayNode.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/DayNode.java index 6052dca..2f02b1d 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/DayNode.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/DayNode.java @@ -32,8 +32,9 @@ import java.awt.Graphics2D; import java.util.ArrayList; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPaintContext; + class DayNode extends PNode { boolean hasWidthFocus; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/TabularFisheye.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/TabularFisheye.java index 8dd638c..0d2645b 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/TabularFisheye.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/fisheye/TabularFisheye.java @@ -32,7 +32,8 @@ import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + public class TabularFisheye extends PCanvas { private CalendarNode calendarNode; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java index 363b8c2..5534735 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java @@ -49,9 +49,10 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PZoomEventHandler; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PZoomEventHandler; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.pswing.PComboBox; import edu.umd.cs.piccolox.pswing.PSwing; import edu.umd.cs.piccolox.pswing.PSwingCanvas; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java index 7609bdc..75d25fa 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java @@ -71,7 +71,8 @@ import javax.swing.event.HyperlinkListener; import javax.swing.table.TableColumn; -import edu.umd.cs.piccolo.PNode; +import org.piccolo2d.PNode; + import edu.umd.cs.piccolox.pswing.PSwing; import edu.umd.cs.piccolox.pswing.PSwingCanvas; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java index 64cb5c1..d4f1ac9 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java @@ -42,8 +42,9 @@ import javax.swing.JRadioButton; import javax.swing.SwingUtilities; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.pswing.PSwing; import edu.umd.cs.piccolox.pswing.PSwingCanvas; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java index ba1f799..5a9662f 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java @@ -40,8 +40,9 @@ import javax.swing.SwingUtilities; import javax.swing.Timer; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.nodes.PText; +import org.piccolo2d.PCanvas; +import org.piccolo2d.nodes.PText; + import edu.umd.cs.piccolox.pswing.PSwing; import edu.umd.cs.piccolox.pswing.PSwingCanvas; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java index d3281de..9284b32 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java @@ -31,16 +31,17 @@ import java.awt.Color; import java.awt.Graphics2D; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PImage; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.nodes.PText; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + import edu.umd.cs.piccolox.PFrame; public class InterfaceFrame extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java index 850b33b..e600964 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java @@ -34,10 +34,11 @@ import java.io.File; import java.util.ArrayList; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PImage; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PImage; + import edu.umd.cs.piccolox.PFrame; public class PiccoloPresentation extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java index 197bb45..7b7558e 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java @@ -30,10 +30,11 @@ import java.awt.Color; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PPath; + import edu.umd.cs.piccolox.PFrame; public class SpecialEffects extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java index 1084ee6..d28a63e 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java @@ -34,14 +34,15 @@ import java.awt.event.KeyEvent; import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PDimension; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PDimension; + import edu.umd.cs.piccolox.PFrame; public class UserInteraction extends PFrame { diff --git a/extras/src/main/java/edu/umd/cs/piccolox/PApplet.java b/extras/src/main/java/edu/umd/cs/piccolox/PApplet.java index 4dccba4..878d0cb 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/PApplet.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/PApplet.java @@ -31,7 +31,8 @@ import javax.swing.JApplet; import javax.swing.SwingUtilities; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + /** * PApplet is meant to be subclassed by applications that just need a 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 762ee5c..a8b8416 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java @@ -45,7 +45,8 @@ import javax.swing.JFrame; import javax.swing.SwingUtilities; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + /** * PFrame is meant to be subclassed by applications that just need a 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 2dde838..9118e94 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 @@ -28,7 +28,7 @@ */ package edu.umd.cs.piccolox.activities; -import edu.umd.cs.piccolo.activities.PInterpolatingActivity; +import org.piccolo2d.activities.PInterpolatingActivity; /** * PPathActivity is the abstract base class for all path activity 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 9799c72..625de09 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 @@ -33,7 +33,8 @@ import java.awt.geom.Point2D; import java.util.ArrayList; -import edu.umd.cs.piccolo.activities.PInterpolatingActivity; +import org.piccolo2d.activities.PInterpolatingActivity; + /** * PPositionPathActivity animates through a sequence of points. 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 630950c..c26b9ba 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 @@ -39,15 +39,16 @@ import java.util.Iterator; import java.util.List; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.activities.PActivity; -import edu.umd.cs.piccolo.activities.PTransformActivity; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.activities.PTransformActivity; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + /** * PNavigationEventHandler implements simple focus based navigation. Uses 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 f645775..523cbdc 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 @@ -42,15 +42,16 @@ import java.util.List; import java.util.Map; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PNodeFilter; +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PNodeFilter; + import edu.umd.cs.piccolox.handles.PBoundsHandle; /** diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PStyledTextEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PStyledTextEventHandler.java index 0571068..1eb4553 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PStyledTextEventHandler.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/event/PStyledTextEventHandler.java @@ -52,12 +52,13 @@ import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; + import edu.umd.cs.piccolox.nodes.PStyledText; /** diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PZoomToEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PZoomToEventHandler.java index badb99c..a197538 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PZoomToEventHandler.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/event/PZoomToEventHandler.java @@ -30,12 +30,13 @@ import java.awt.event.InputEvent; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.util.PBounds; + /** * PZoomToEventHandler is used to zoom the camera view to the node diff --git a/extras/src/main/java/edu/umd/cs/piccolox/handles/PBoundsHandle.java b/extras/src/main/java/edu/umd/cs/piccolox/handles/PBoundsHandle.java index 2fc5c3c..7c789a9 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/handles/PBoundsHandle.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/handles/PBoundsHandle.java @@ -35,13 +35,14 @@ import javax.swing.SwingConstants; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPickPath; + import edu.umd.cs.piccolox.util.PBoundsLocator; /** diff --git a/extras/src/main/java/edu/umd/cs/piccolox/handles/PHandle.java b/extras/src/main/java/edu/umd/cs/piccolox/handles/PHandle.java index ecab2ec..9699c8f 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/handles/PHandle.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/handles/PHandle.java @@ -38,14 +38,15 @@ import java.io.IOException; import java.io.ObjectInputStream; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + import edu.umd.cs.piccolox.util.PLocator; import edu.umd.cs.piccolox.util.PNodeLocator; diff --git a/extras/src/main/java/edu/umd/cs/piccolox/handles/PStickyHandleManager.java b/extras/src/main/java/edu/umd/cs/piccolox/handles/PStickyHandleManager.java index 3597c1e..3e69ca5 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/handles/PStickyHandleManager.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/handles/PStickyHandleManager.java @@ -28,10 +28,10 @@ */ package edu.umd.cs.piccolox.handles; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; /** * This class relays adjustments to its bounds to its target. diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/P3DRect.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/P3DRect.java index ef0e649..d362f74 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/P3DRect.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/P3DRect.java @@ -36,9 +36,10 @@ import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + /** * This is a simple node that draws a "3D" rectangle within the bounds of the diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PCacheCamera.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PCacheCamera.java index a66f80b..9214b6e 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PCacheCamera.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PCacheCamera.java @@ -35,14 +35,15 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.activities.PTransformActivity; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PUtil; +import org.piccolo2d.PCamera; +import org.piccolo2d.PRoot; +import org.piccolo2d.activities.PTransformActivity; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PUtil; + /** * An extension to PCamera that provides a fast image based diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PClip.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PClip.java index 5ea8093..122e840 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PClip.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PClip.java @@ -32,11 +32,12 @@ import java.awt.Paint; import java.awt.geom.Rectangle2D; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PPickPath; + /** * PClip is a simple node that applies a clip before rendering or picking diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PComposite.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PComposite.java index 395018a..dd69838 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PComposite.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PComposite.java @@ -28,8 +28,8 @@ */ package edu.umd.cs.piccolox.nodes; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPickPath; /** * PComposite is a simple node that makes a group of nodes appear to be a diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLens.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLens.java index 795c18c..ce98b9b 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLens.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLens.java @@ -33,11 +33,12 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PDragEventHandler; -import edu.umd.cs.piccolo.nodes.PPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.nodes.PPath; + /** * PLens is a simple default lens implementation for Piccolo2D. See diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLine.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLine.java index bffef8c..1d905a6 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLine.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLine.java @@ -39,11 +39,12 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PUtil; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PUtil; + import edu.umd.cs.piccolox.util.LineShape; /** diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PNodeCache.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PNodeCache.java index 8bf1875..2e5d28a 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PNodeCache.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PNodeCache.java @@ -32,11 +32,12 @@ import java.awt.Image; import java.awt.geom.Dimension2D; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PPickPath; + /** * PNodeCache caches a visual representation of it's children into an diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PShadow.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PShadow.java index 08279af..3658510 100755 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PShadow.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PShadow.java @@ -31,7 +31,8 @@ import java.awt.Image; import java.awt.Paint; -import edu.umd.cs.piccolo.nodes.PImage; +import org.piccolo2d.nodes.PImage; + import edu.umd.cs.piccolox.util.ShadowUtils; diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java index d495995..8906ac9 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java @@ -54,8 +54,9 @@ import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPaintContext; + /** * @author Lance Good diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java index 5011d58..4546051 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java @@ -54,11 +54,12 @@ import javax.swing.JComponent; import javax.swing.RepaintManager; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + /* This message was sent to Sun on August 27, 1999 diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java index 973ff6d..c693194 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java @@ -33,7 +33,8 @@ import javax.swing.JComponent; import javax.swing.RepaintManager; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + /** * The PSwingCanvas is a PCanvas that can display Swing components with diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java index a91c335..c7237ae 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java @@ -8,8 +8,9 @@ import java.awt.event.MouseEvent; import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPickPath; + /** * Interface allowing PSwing events that originated from swing and are destined 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 de11299..7b65f0a 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 @@ -40,13 +40,14 @@ import javax.swing.SwingUtilities; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventListener; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PAffineTransformException; +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PAffineTransformException; + /** * Event handler to send MousePressed, MouseReleased, MouseMoved, MouseClicked, diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java index 60e9cc8..883884d 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java @@ -35,9 +35,10 @@ import java.awt.geom.Point2D; import java.io.Serializable; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PPickPath; + /** * PMouseEvent is an event which indicates that a mouse action occurred diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseMotionEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseMotionEvent.java index 2de73ee..7b12ec0 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseMotionEvent.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseMotionEvent.java @@ -31,7 +31,8 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; -import edu.umd.cs.piccolo.event.PInputEvent; +import org.piccolo2d.event.PInputEvent; + /** * PMouseMotionEvent is an event which indicates that a mouse motion diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java index 8b4bfe5..08a221f 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java @@ -11,9 +11,10 @@ import java.awt.event.MouseWheelListener; import java.awt.geom.Point2D; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PPickPath; + /** * PMouseMotionEvent is an event which indicates that a mouse motion diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java index 0d7048a..4a46a56 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java @@ -35,7 +35,8 @@ import javax.swing.RepaintManager; import javax.swing.SwingUtilities; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.util.PBounds; + /** * This RepaintManager replaces the default Swing implementation, and is used to diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PCacheCanvas.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PCacheCanvas.java index 54f6866..bb39b09 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PCacheCanvas.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/swing/PCacheCanvas.java @@ -28,10 +28,11 @@ */ package edu.umd.cs.piccolox.swing; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PRoot; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PRoot; + import edu.umd.cs.piccolox.nodes.PCacheCamera; /** diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PDefaultScrollDirector.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PDefaultScrollDirector.java index 40e2a71..e4e4e34 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PDefaultScrollDirector.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/swing/PDefaultScrollDirector.java @@ -39,13 +39,14 @@ import javax.swing.ScrollPaneConstants; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; + /** * The default scroll director implementation. This default implementation diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollDirector.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollDirector.java index 3ab6bc9..6429c46 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollDirector.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollDirector.java @@ -32,7 +32,8 @@ import java.awt.Point; import java.awt.geom.Rectangle2D; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + /** * The interface an application can implement to control scrolling in a diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPane.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPane.java index 527a0bc..e23931d 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPane.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPane.java @@ -45,7 +45,8 @@ import javax.swing.SwingConstants; import javax.swing.plaf.ScrollPaneUI; -import edu.umd.cs.piccolo.PCanvas; +import org.piccolo2d.PCanvas; + /** * A simple extension to a standard scroll pane that uses the jazz version of diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPaneLayout.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPaneLayout.java index 8e4bbac..e35c610 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPaneLayout.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPaneLayout.java @@ -37,7 +37,8 @@ import javax.swing.ScrollPaneLayout; import javax.swing.border.Border; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.util.PBounds; + /** * A subclass of ScrollPaneLayout that looks at the Viewport for sizing diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PViewport.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PViewport.java index a9f9766..0b547ce 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PViewport.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/swing/PViewport.java @@ -39,8 +39,9 @@ import javax.swing.JViewport; import javax.swing.ViewportLayout; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.util.PBounds; +import org.piccolo2d.PCanvas; +import org.piccolo2d.util.PBounds; + /** * A subclass of JViewport that talks to the scroll director to negotiate the diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/SwingLayoutNode.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/SwingLayoutNode.java index e376ef9..a32b8c2 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/SwingLayoutNode.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/swing/SwingLayoutNode.java @@ -41,7 +41,8 @@ import javax.swing.JComponent; import javax.swing.JPanel; -import edu.umd.cs.piccolo.PNode; +import org.piccolo2d.PNode; + /** * Uses Swing layout managers to position PNodes. diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PBoundsLocator.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PBoundsLocator.java index a9caf0f..fc9d3f9 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PBoundsLocator.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/util/PBoundsLocator.java @@ -32,7 +32,8 @@ import javax.swing.SwingConstants; -import edu.umd.cs.piccolo.PNode; +import org.piccolo2d.PNode; + /** * PBoundsLocator is a locator that locates points on the bounds of a diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java index 4d1271e..7d3d92c 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java @@ -52,7 +52,7 @@ * internal state of the base stroke. Try PathExample with extreme zoom in and * zoom back to the original scale. The pickable circles disappear. Strange! * - * @see edu.umd.cs.piccolo.nodes.PPath + * @see org.piccolo2d.nodes.PPath * @see BasicStroke * @version 1.0 * @author Jesse Grosjean diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PNodeLocator.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PNodeLocator.java index 1da3006..f4986bb 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PNodeLocator.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/util/PNodeLocator.java @@ -28,7 +28,7 @@ */ package edu.umd.cs.piccolox.util; -import edu.umd.cs.piccolo.PNode; +import org.piccolo2d.PNode; /** * PNodeLocator provides an abstraction for locating points on a node. diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/POcclusionDetection.java b/extras/src/main/java/edu/umd/cs/piccolox/util/POcclusionDetection.java index 1d5a573..94aed73 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/POcclusionDetection.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/util/POcclusionDetection.java @@ -28,9 +28,9 @@ */ package edu.umd.cs.piccolox.util; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; /** * Experimental class for detecting occlusions. diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java index e031018..811f013 100644 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java +++ b/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java @@ -30,11 +30,12 @@ import java.awt.Shape; import java.awt.Stroke; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.util.PPickPath; + /** * - * @see edu.umd.cs.piccolo.nodes.PPath + * @see org.piccolo2d.nodes.PPath * @see Stroke * @version 1.3 * @author Marcus Rohrmoser diff --git a/extras/src/test/java/edu/umd/cs/piccolox/PAppletTest.java b/extras/src/test/java/edu/umd/cs/piccolox/PAppletTest.java index d11c909..7d2ddd6 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/PAppletTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/PAppletTest.java @@ -28,9 +28,10 @@ */ package edu.umd.cs.piccolox; +import org.piccolo2d.PCanvas; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.PCanvas; /** * Unit test for PApplet. diff --git a/extras/src/test/java/edu/umd/cs/piccolox/PFrameTest.java b/extras/src/test/java/edu/umd/cs/piccolox/PFrameTest.java index f23f94e..6494be6 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/PFrameTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/PFrameTest.java @@ -30,9 +30,10 @@ import java.awt.event.KeyListener; +import org.piccolo2d.PCanvas; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.PCanvas; /** * Unit test for PFrame. diff --git a/extras/src/test/java/edu/umd/cs/piccolox/event/PSelectionEventHandlerTest.java b/extras/src/test/java/edu/umd/cs/piccolox/event/PSelectionEventHandlerTest.java index 22e9db0..5abdfa9 100755 --- a/extras/src/test/java/edu/umd/cs/piccolox/event/PSelectionEventHandlerTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/event/PSelectionEventHandlerTest.java @@ -30,11 +30,12 @@ import java.awt.event.KeyEvent; -import edu.umd.cs.piccolo.PCanvas; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PNode; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEvent; + import junit.framework.TestCase; diff --git a/extras/src/test/java/edu/umd/cs/piccolox/handles/PHandleTest.java b/extras/src/test/java/edu/umd/cs/piccolox/handles/PHandleTest.java index 0bc850a..72aafaf 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/handles/PHandleTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/handles/PHandleTest.java @@ -28,9 +28,10 @@ */ package edu.umd.cs.piccolox.handles; +import org.piccolo2d.PNode; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolox.util.PLocator; diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/P3DRectTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/P3DRectTest.java index 9438936..86bfcf1 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/P3DRectTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/nodes/P3DRectTest.java @@ -32,8 +32,9 @@ import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import org.piccolo2d.util.PPaintContext; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.util.PPaintContext; /** * Unit test for P3DRect. diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingMouseEventTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingMouseEventTest.java index 0f2f4e7..f9b2294 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingMouseEventTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingMouseEventTest.java @@ -6,8 +6,9 @@ import javax.swing.JComponent; import javax.swing.JPanel; +import org.piccolo2d.event.PInputEvent; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.event.PInputEvent; public class PSwingMouseEventTest extends TestCase { diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java index 5382a40..8f66bea 100644 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java +++ b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java @@ -40,8 +40,9 @@ import javax.swing.JPanel; import javax.swing.RepaintManager; +import org.piccolo2d.util.PPaintContext; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.util.PPaintContext; public class PSwingTest extends TestCase { public void setUp() { diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java index 5d090ac..133a830 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java @@ -35,13 +35,14 @@ import javax.swing.SwingConstants; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PBasicInputEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPickPath; + import edu.umd.cs.piccolox.util.PBoundsLocator; /** diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java index e134288..47e9941 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java @@ -48,18 +48,18 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; +import org.piccolo2d.PCamera; +import org.piccolo2d.PComponent; +import org.piccolo2d.PLayer; +import org.piccolo2d.PRoot; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.event.PPanEventHandler; +import org.piccolo2d.event.PZoomEventHandler; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDebug; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PStack; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PComponent; -import edu.umd.cs.piccolo.PLayer; -import edu.umd.cs.piccolo.PRoot; -import edu.umd.cs.piccolo.event.PInputEventListener; -import edu.umd.cs.piccolo.event.PPanEventHandler; -import edu.umd.cs.piccolo.event.PZoomEventHandler; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDebug; -import edu.umd.cs.piccolo.util.PPaintContext; -import edu.umd.cs.piccolo.util.PStack; /** * PSWTCanvas is an SWT Composite that can be used to embed diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java index 6cd1a4e..55f4fa8 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java @@ -38,13 +38,14 @@ import java.io.IOException; import java.io.ObjectInputStream; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PDragSequenceEventHandler; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.event.PInputEventFilter; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PDimension; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + import edu.umd.cs.piccolox.util.PLocator; import edu.umd.cs.piccolox.util.PNodeLocator; diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTImage.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTImage.java index 9bd477c..b977eb0 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTImage.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTImage.java @@ -32,11 +32,11 @@ import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PImage; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PImage; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; /** * PSWTImage is a wrapper around a org.eclipse.swt.graphics.Image. diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTPath.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTPath.java index 0dc23b4..27cf470 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTPath.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTPath.java @@ -41,12 +41,13 @@ import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.nodes.PPath; -import edu.umd.cs.piccolo.util.PAffineTransform; -import edu.umd.cs.piccolo.util.PAffineTransformException; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; +import org.piccolo2d.PNode; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PAffineTransformException; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + /** * PSWTPath is a wrapper around a java.awt.geom.GeneralPath, with diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTRoot.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTRoot.java index 67f4d1a..74e0630 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTRoot.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTRoot.java @@ -33,8 +33,8 @@ import javax.swing.Timer; import org.eclipse.swt.widgets.Composite; +import org.piccolo2d.PRoot; -import edu.umd.cs.piccolo.PRoot; /** * PSWTRoot is a subclass of PRoot that is designed to work in the SWT diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java index 4b879e2..74343e0 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java @@ -34,12 +34,12 @@ import java.util.List; import org.eclipse.swt.SWT; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEvent; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPaintContext; import edu.umd.cs.piccolox.event.PSelectionEventHandler; /** diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTStickyHandleManager.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTStickyHandleManager.java index d3ce90a..f8b8861 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTStickyHandleManager.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTStickyHandleManager.java @@ -28,10 +28,10 @@ */ package edu.umd.cs.piccolox.swt; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PBounds; -import edu.umd.cs.piccolo.util.PPickPath; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; /** * A class for managing the position of a sticky handle. diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTText.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTText.java index 39e9e04..3c2bc21 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTText.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTText.java @@ -17,9 +17,9 @@ import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.util.PPaintContext; /** * PSWTText creates a visual component to support text. Multiple lines diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java index 333097c..b149c1c 100644 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java +++ b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java @@ -28,12 +28,13 @@ */ package edu.umd.cs.piccolox.swt; +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEventListener; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.PCamera; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEventListener; import edu.umd.cs.piccolox.util.PBoundsLocator; diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTCanvasTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTCanvasTest.java index ce5e3d4..e27f8ac 100644 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTCanvasTest.java +++ b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTCanvasTest.java @@ -34,10 +34,10 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.event.PPanEventHandler; +import org.piccolo2d.event.PZoomEventHandler; -import edu.umd.cs.piccolo.event.PInputEventListener; -import edu.umd.cs.piccolo.event.PPanEventHandler; -import edu.umd.cs.piccolo.event.PZoomEventHandler; /** * Unit test for PSWTCanvas. diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java index 878f56a..d4882cd 100644 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java +++ b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java @@ -28,11 +28,12 @@ */ package edu.umd.cs.piccolox.swt; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEventListener; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.PNode; -import edu.umd.cs.piccolo.event.PInputEventListener; import edu.umd.cs.piccolox.util.PBoundsLocator; import edu.umd.cs.piccolox.util.PLocator; diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTPathTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTPathTest.java index fce4546..3d60525 100755 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTPathTest.java +++ b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTPathTest.java @@ -30,9 +30,10 @@ import java.awt.geom.Point2D; +import org.piccolo2d.util.PBounds; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.util.PBounds; /** * Unit test for PSWTPath. diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTTextTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTTextTest.java index 1ee45fa..a85374a 100644 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTTextTest.java +++ b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTTextTest.java @@ -33,9 +33,10 @@ import java.awt.geom.Point2D; +import org.piccolo2d.util.PBounds; + import junit.framework.TestCase; -import edu.umd.cs.piccolo.util.PBounds; /** * Unit test for PSWTText.