diff --git a/core/src/main/java/org/piccolo2d/event/PMouseWheelZoomEventHandler.java b/core/src/main/java/org/piccolo2d/event/PMouseWheelZoomEventHandler.java new file mode 100644 index 0000000..16b0ea9 --- /dev/null +++ b/core/src/main/java/org/piccolo2d/event/PMouseWheelZoomEventHandler.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2008-2012, 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.Rectangle; +import java.awt.geom.Point2D; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; + +/** + * Zoom event handler that scales the camera view transform in response to mouse wheel events. + * + * @since 2.0 + */ +public final class PMouseWheelZoomEventHandler extends PBasicInputEventHandler { + /** Default scale factor, 0.1d. */ + static final double DEFAULT_SCALE_FACTOR = 0.1d; + + /** Scale factor. */ + private double scaleFactor = DEFAULT_SCALE_FACTOR; + + /** Zoom mode. */ + private ZoomMode zoomMode = ZoomMode.ZOOM_ABOUT_CANVAS_CENTER; + + + /** + * Create a new mouse wheel zoom event handler. + */ + public PMouseWheelZoomEventHandler() { + super(); + PInputEventFilter eventFilter = new PInputEventFilter(); + eventFilter.rejectAllEventTypes(); + eventFilter.setAcceptsMouseWheelRotated(true); + setEventFilter(eventFilter); + } + + + /** + * Return the scale factor for this mouse wheel zoom event handler. Defaults to DEFAULT_SCALE_FACTOR. + * + * @see #DEFAULT_SCALE_FACTOR + * @return the scale factor for this mouse wheel zoom event handler + */ + public double getScaleFactor() { + return scaleFactor; + } + + /** + * Set the scale factor for this mouse wheel zoom event handler to scaleFactor. + * + * @param scaleFactor scale factor for this mouse wheel zoom event handler + */ + public void setScaleFactor(final double scaleFactor) { + this.scaleFactor = scaleFactor; + } + + /** + * Switch to zoom about mouse mode. + * + * @see ZoomMode#ZOOM_ABOUT_MOUSE + */ + public void zoomAboutMouse() { + zoomMode = ZoomMode.ZOOM_ABOUT_MOUSE; + } + + /** + * Switch to zoom about canvas center mode. + * + * @see ZoomMode#ZOOM_ABOUT_CANVAS_CENTER + */ + public void zoomAboutCanvasCenter() { + zoomMode = ZoomMode.ZOOM_ABOUT_CANVAS_CENTER; + } + + /** + * Switch to zoom about view center mode. + * + * @see ZoomMode#ZOOM_ABOUT_VIEW_CENTER + */ + public void zoomAboutViewCenter() { + zoomMode = ZoomMode.ZOOM_ABOUT_VIEW_CENTER; + } + + /** + * Return the zoom mode for this mouse wheel zoom event handler. Defaults to + * ZoomMode.ZOOM_ABOUT_CANVAS_CENTER. + * + * @return the zoom mode for this mouse wheel zoom event handler + */ + ZoomMode getZoomMode() { + return zoomMode; + } + + /** {@inheritDoc} */ + public void mouseWheelRotated(final PInputEvent event) { + PCamera camera = event.getCamera(); + double scale = 1.0d + event.getWheelRotation() * scaleFactor; + Point2D viewAboutPoint = getViewAboutPoint(event); + camera.scaleViewAboutPoint(scale, viewAboutPoint.getX(), viewAboutPoint.getY()); + } + + /** + * Return the view about point for the specified event according to the current zoom mode. + * + * @param event input event + * @return the view about point for the specified event according to the current zoom mode + */ + private Point2D getViewAboutPoint(final PInputEvent event) { + switch (zoomMode) { + case ZOOM_ABOUT_MOUSE: + return event.getPosition(); + case ZOOM_ABOUT_CANVAS_CENTER: + Rectangle canvasBounds = ((PCanvas) event.getComponent()).getBounds(); + Point2D canvasCenter = new Point2D.Double(canvasBounds.getCenterX(), canvasBounds.getCenterY()); + event.getPath().canvasToLocal(canvasCenter, event.getCamera()); + return event.getCamera().localToView(canvasCenter); + case ZOOM_ABOUT_VIEW_CENTER: + return event.getCamera().getBoundsReference().getCenter2D(); + } + throw new IllegalArgumentException("illegal zoom mode " + zoomMode); + } + + /** + * Zoom mode. + */ + enum ZoomMode { + /** + * Zoom about mouse mode. + */ + ZOOM_ABOUT_MOUSE, + + /** + * Zoom about canvas center mode. + */ + ZOOM_ABOUT_CANVAS_CENTER, + + /** + * Zoom about view center mode. + */ + ZOOM_ABOUT_VIEW_CENTER; + } +} diff --git a/core/src/test/java/org/piccolo2d/event/PMouseWheelZoomEventHandlerTest.java b/core/src/test/java/org/piccolo2d/event/PMouseWheelZoomEventHandlerTest.java new file mode 100644 index 0000000..eab0eb0 --- /dev/null +++ b/core/src/test/java/org/piccolo2d/event/PMouseWheelZoomEventHandlerTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2008-2012, 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 static org.piccolo2d.event.PMouseWheelZoomEventHandler.DEFAULT_SCALE_FACTOR; +import static org.piccolo2d.event.PMouseWheelZoomEventHandler.ZoomMode.ZOOM_ABOUT_MOUSE; +import static org.piccolo2d.event.PMouseWheelZoomEventHandler.ZoomMode.ZOOM_ABOUT_CANVAS_CENTER; +import static org.piccolo2d.event.PMouseWheelZoomEventHandler.ZoomMode.ZOOM_ABOUT_VIEW_CENTER; + +import junit.framework.TestCase; + +/** + * Unit test for PMouseWheelZoomEventHandler. + */ +public final class PMouseWheelZoomEventHandlerTest extends TestCase { + private static final double TOLERANCE = 0.1d; + + public void testConstructor() { + assertNotNull(new PMouseWheelZoomEventHandler()); + } + + public void testScaleFactor() { + PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler(); + assertEquals(DEFAULT_SCALE_FACTOR, zoomHandler.getScaleFactor(), TOLERANCE); + + zoomHandler.setScaleFactor(42.0d); + assertEquals(42.0d, zoomHandler.getScaleFactor(), TOLERANCE); + } + + public void testDefaultZoomMode() { + PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler(); + assertSame(ZOOM_ABOUT_CANVAS_CENTER, zoomHandler.getZoomMode()); + } + + public void testZoomAboutMouse() { + PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler(); + zoomHandler.zoomAboutMouse(); + assertSame(ZOOM_ABOUT_MOUSE, zoomHandler.getZoomMode()); + } + + public void testZoomAboutCanvasCenter() { + PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler(); + zoomHandler.zoomAboutCanvasCenter(); + assertSame(ZOOM_ABOUT_CANVAS_CENTER, zoomHandler.getZoomMode()); + } + + public void testZoomAboutViewCenter() { + PMouseWheelZoomEventHandler zoomHandler = new PMouseWheelZoomEventHandler(); + zoomHandler.zoomAboutViewCenter(); + assertSame(ZOOM_ABOUT_VIEW_CENTER, zoomHandler.getZoomMode()); + } +} diff --git a/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java b/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java index 8bc46b0..adaf147 100644 --- a/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java +++ b/examples/src/main/java/org/piccolo2d/examples/ExampleRunner.java @@ -76,13 +76,13 @@ ClipExample.class, CompositeExample.class, DynamicExample.class, EventHandlerExample.class, FullScreenNodeExample.class, GraphEditorExample.class, GridExample.class, GroupExample.class, HandleExample.class, HelloWorldExample.class, HierarchyZoomExample.class, HtmlViewExample.class, - KeyEventFocusExample.class, LayoutExample.class, LensExample.class, NavigationExample.class, - NodeCacheExample.class, NodeEventExample.class, NodeExample.class, NodeLinkExample.class, - P3DRectExample.class, PanToExample.class, PathExample.class, PositionExample.class, - PositionPathActivityExample.class, PulseExample.class, ScrollingExample.class, SelectionExample.class, - ShadowExample.class, SquiggleExample.class, StickyExample.class, StickyHandleLayerExample.class, - StrokeExample.class, TextExample.class, TooltipExample.class, TwoCanvasExample.class, - WaitForActivitiesExample.class }); + KeyEventFocusExample.class, LayoutExample.class, LensExample.class, MouseWheelZoomExample.class, + NavigationExample.class, NodeCacheExample.class, NodeEventExample.class, NodeExample.class, + NodeLinkExample.class, P3DRectExample.class, PanToExample.class, PathExample.class, + PositionExample.class, PositionPathActivityExample.class, PulseExample.class, ScrollingExample.class, + SelectionExample.class, ShadowExample.class, SquiggleExample.class, StickyExample.class, + StickyHandleLayerExample.class, StrokeExample.class, TextExample.class, TooltipExample.class, + TwoCanvasExample.class, WaitForActivitiesExample.class }); } /** diff --git a/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java b/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java new file mode 100644 index 0000000..1cfb8e9 --- /dev/null +++ b/examples/src/main/java/org/piccolo2d/examples/MouseWheelZoomExample.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2008-2012, 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.examples; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Paint; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Random; + +import javax.swing.ButtonGroup; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; + +import org.piccolo2d.PCanvas; +import org.piccolo2d.event.PMouseWheelZoomEventHandler; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.nodes.PText; + +/** + * Mouse wheel zoom example. + * + * @since 2.0 + */ +public final class MouseWheelZoomExample extends PFrame { + /** Default serial version UID. */ + private static final long serialVersionUID = 1L; + + /** Number of text and rect nodes. */ + private static final int N = 100; + + /** Text paint. */ + private static final Paint TEXT_PAINT = new Color(20, 20, 20, 200); + + /** Source of randomness. */ + private final Random random; + + + /** + * Create a new mouse wheel zoom example. + */ + public MouseWheelZoomExample() { + this(null); + } + + /** + * Create a new mouse wheel zoom example with the specified canvas. + * + * @param canvas canvas for this mouse wheel zoom example + */ + public MouseWheelZoomExample(final PCanvas canvas) { + super("MouseWheelZoomExample", false, canvas); + random = new Random(); + } + + + /** {@inheritDoc} */ + public void initialize() { + for (int i = 0; i < N; i++) { + createTextNode(i); + } + for (int i = 0; i < N; i++) { + createRectNode(i); + } + + // uninstall default zoom event handler + getCanvas().removeInputEventListener(getCanvas().getZoomEventHandler()); + + // install mouse wheel zoom event handler + final PMouseWheelZoomEventHandler mouseWheelZoomEventHandler = new PMouseWheelZoomEventHandler(); + getCanvas().addInputEventListener(mouseWheelZoomEventHandler); + + // create a toolbar + final JToolBar toolBar = new JToolBar(); + final JToggleButton zoomAboutMouse = new JToggleButton("Zoom about mouse"); + final JToggleButton zoomAboutCanvasCenter = new JToggleButton("Zoom about canvas center"); + final JToggleButton zoomAboutViewCenter = new JToggleButton("Zoom about view center"); + final ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(zoomAboutMouse); + buttonGroup.add(zoomAboutCanvasCenter); + buttonGroup.add(zoomAboutViewCenter); + toolBar.add(zoomAboutMouse); + toolBar.add(zoomAboutCanvasCenter); + toolBar.add(zoomAboutViewCenter); + toolBar.setFloatable(false); + + zoomAboutMouse.addActionListener(new ActionListener() { + /** {@inheritDoc} */ + public void actionPerformed(final ActionEvent event) { + mouseWheelZoomEventHandler.zoomAboutMouse(); + } + }); + zoomAboutCanvasCenter.addActionListener(new ActionListener() { + /** {@inheritDoc} */ + public void actionPerformed(final ActionEvent event) { + mouseWheelZoomEventHandler.zoomAboutCanvasCenter(); + } + }); + zoomAboutViewCenter.addActionListener(new ActionListener() { + /** {@inheritDoc} */ + public void actionPerformed(final ActionEvent event) { + mouseWheelZoomEventHandler.zoomAboutViewCenter(); + } + }); + + final JPanel contentPane = new JPanel(); + contentPane.setLayout(new BorderLayout()); + contentPane.add("North", toolBar); + contentPane.add("Center", getCanvas()); + setContentPane(contentPane); + validate(); + } + + private void createTextNode(final int i) { + PText text = new PText("Label " + i); + text.setTextPaint(TEXT_PAINT); + text.setOffset(random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0, + random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0); + getCanvas().getLayer().addChild(text); + } + + private void createRectNode(final int i) { + PPath path = PPath.createRectangle(0.0f, 0.0f, 50.0f * i/N + 10.0f, 50.0f * i/N + 10.0f); + int r = 200 * i/N + 45; + path.setPaint(new Color(r, 0, 0, 200)); + path.setStrokePaint(new Color(r - 10, 0, 0, 200)); + path.setOffset(random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0, + random.nextDouble() * 1000.0d - random.nextDouble() * 1000.0); + getCanvas().getLayer().addChild(path); + } + + + /** + * Main. + * + * @param args command line arguments, ignored + */ + public static void main(final String[] args) { + new MouseWheelZoomExample(); + } +} \ No newline at end of file