/******************************************************************************* * Copyright (c) 2010-2015 Henshin developers. All rights reserved. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * TU Berlin, University of Luxembourg, SES S.A. *******************************************************************************/ /** * */ package de.tub.tfs.muvitor.animation; import static java.lang.StrictMath.PI; import static java.lang.StrictMath.hypot; import static java.lang.StrictMath.pow; import static java.lang.StrictMath.random; import static java.lang.StrictMath.sin; import static java.lang.StrictMath.sqrt; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; /** * This class provides some basic {@link AnimationPathModifier}s ready to use * with {@link AnimatingCommand}s: * * <ul> * <li> {@link #getStandardModifier()}: just linear interpolation over progress, * no y-amplitude. * <li> {@link #getCircularModifier()}: alter the path to be a half circle from * start to end. * <li> {@link #getEllipticModifier(int)}: alter the path to be a ellipse from * start to end with a given axis length in px. * <li> {@link #getParabolicModifier(int)} : alter the path to be a parabolic * curve from start to end with a given maximum amplitude length in px. * <li> {@link #getRandomModifier(int)} : alter the path to have random * y-amplitudes. * <li> {@link #getSineModifier(int, float)} : alter the path to a sine curve * with given frequency and amplitude. * </ul> * * <p> * Of course you may implement your own modifier via * {@link #getLocation(Rectangle, Rectangle, float)} using the helper method * {@link #getOrthogonalShifted(Point, Dimension, double)} to rotate the * relative coordinate system. Sorry, I can't explain this better now. * </p> * * TODO Proceed steadily on the paths themselves instead of proceeding steadily * along the shift vector. So the elements would not "accelerate" when reaching * the end of a circle. This would need to alter the x coordinate according to * the progress and the shape of the path as well. * * @author "Tony Modica" * */ abstract public class AnimationPathModifier { private final static AnimationPathModifier circularModifier = new AnimationPathModifier() { @Override public Point getLocation(final Rectangle initialBounds, final Rectangle endingBounds, final double progress) { final Point initialLocation = initialBounds.getLocation(); final Dimension shift = endingBounds.getLocation().getDifference(initialLocation); final double axis = hypot(shift.width, shift.height); final double delta = sqrt(progress - pow(progress, 2)) * axis; shift.scale(progress); return getOrthogonalShifted(initialLocation.translate(shift), shift, delta); } }; private final static AnimationPathModifier linearModifier = new AnimationPathModifier() { @Override public Point getLocation(final Rectangle initialBounds, final Rectangle endingBounds, final double progress) { final Point initialLocation = initialBounds.getLocation(); final Dimension shift = endingBounds.getLocation().getDifference(initialLocation) .scale(progress); return initialLocation.translate(shift); } }; final static public AnimationPathModifier getCircularModifier() { return circularModifier; } final static public AnimationPathModifier getEllipticModifier(final int axis) { return new AnimationPathModifier() { @Override public Point getLocation(final Rectangle initialBounds, final Rectangle endingBounds, final double progress) { final Point initialLocation = initialBounds.getLocation(); final double delta = sqrt(progress - pow(progress, 2)) * 2 * axis; final Dimension shift = endingBounds.getLocation().getDifference(initialLocation) .scale(progress); return getOrthogonalShifted(initialLocation.translate(shift), shift, delta); } }; } final static public AnimationPathModifier getParabolicModifier(final int amp) { return new AnimationPathModifier() { @Override public Point getLocation(final Rectangle initialBounds, final Rectangle endingBounds, final double progress) { final Point initialLocation = initialBounds.getLocation(); final double delta = amp * (1 - pow((2 * progress - 1), 2)); final Dimension shift = endingBounds.getLocation().getDifference(initialLocation) .scale(progress); return getOrthogonalShifted(initialLocation.translate(shift), shift, delta); } }; } final static public AnimationPathModifier getRandomModifier(final int max) { return new AnimationPathModifier() { @Override public Point getLocation(final Rectangle initialBounds, final Rectangle endingBounds, final double progress) { final Point initialLocation = initialBounds.getLocation(); final double delta = (random() - 0.5) * max; final Dimension shift = endingBounds.getLocation().getDifference(initialLocation) .scale(progress); return getOrthogonalShifted(initialLocation.translate(shift), shift, delta); } }; } final static public AnimationPathModifier getSineModifier(final int amp, final float periods) { return new AnimationPathModifier() { // a whole period is 360� or 2*PI final private double cachedFactor = periods * 2 * PI; @Override public Point getLocation(final Rectangle initialBounds, final Rectangle endingBounds, final double progress) { final Point initialLocation = initialBounds.getLocation(); final double delta = amp * sin(progress * cachedFactor); final Dimension shift = endingBounds.getLocation().getDifference(initialLocation) .scale(progress); return getOrthogonalShifted(initialLocation.translate(shift), shift, delta); } }; } final static public AnimationPathModifier getStandardModifier() { return linearModifier; // return getRandomModifier(100); // return getParabolicModifier(100); // return getSineModifier(50, 3); // return getEllipticModifier(100); // return getCircularModifier(); } final protected static Point getOrthogonalShifted(final Point location, final Dimension direction, final double length) { final double factor = length / hypot(direction.width, direction.height); direction.transpose().scale(-factor, factor); return location.translate(direction); } abstract public Point getLocation(final Rectangle intialBounds, final Rectangle endingBounds, double progress); }