/*
* @(#)AbstractPlanarTransition2D.java
*
* $Date: 2014-03-14 07:36:22 +0100 (P, 14 márc. 2014) $
*
* Copyright (c) 2011 by Jeremy Wood.
* All rights reserved.
*
* The copyright of this software is owned by Jeremy Wood.
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* Jeremy Wood. For details see accompanying license terms.
*
* This software is probably, but not necessarily, discussed here:
* https://javagraphics.java.net/
*
* That site should also contain the most recent official version
* of this software. (See the SVN repository for more details.)
*/
package com.bric.image.transition;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.media.jai.PerspectiveTransform;
import com.bric.geom.RectangularTransform;
import com.bric.geom.TransformUtils;
/** Here 2 frames slide around on an imaginary table top
* extending into the distance. This creates a 3D-type
* effect where frames move in and out of the users field of view.
* <P>(The best way to understand exactly what this does is to
* see it in action.)
* <P>More technically: this creates a PerspectiveTransform
* and projects two dots into the plane stretching into the distance, and
* then this positions the two frames based on those dots.
*
*/
public abstract class AbstractPlanarTransition2D extends Transition2D {
Color background;
public AbstractPlanarTransition2D() {
this(Color.black);
}
public AbstractPlanarTransition2D(Color background) {
this.background = background;
}
@Override
public Transition2DInstruction[] getInstructions(float progress,Dimension size) {
PerspectiveTransform transform;
double upperY, lowerY;
upperY = size.height*7/10;
lowerY = size.height;
double x = size.width*5/20;
transform = PerspectiveTransform.getQuadToQuad(
0, 0,
1, 0,
0, 1,
1, 1,
x, upperY,
size.width-x, upperY,
0, lowerY,
size.width, lowerY);
Point2D p = new Point2D.Double(0,.5);
transform.transform(p,p);
Point2D pA = getFrameALocation(progress);
Point2D pB = getFrameBLocation(progress);
transform.transform(pA, pA);
transform.transform(pB, pB);
double height, ratio, width;
Rectangle2D r1 = new Rectangle2D.Double();
Rectangle2D r2 = new Rectangle2D.Double();
height = lowerY-(lowerY-pA.getY())*2;
ratio = height/lowerY;
width = size.getWidth()*ratio;
r1.setFrame(
pA.getX()-width/2, pA.getY()-height, width, height );
height = lowerY-(lowerY-pB.getY())*2;
ratio = height/lowerY;
width = size.getWidth()*ratio;
r2.setFrame(
pB.getX()-width/2, pB.getY()-height, width, height );
Rectangle big = new Rectangle(0,0,size.width,size.height);
AffineTransform transform1 = RectangularTransform.create(big, r1);
AffineTransform transform2 = RectangularTransform.create(big, r2);
float opacity1 = getFrameAOpacity(progress);
float opacity2 = getFrameBOpacity(progress);
ImageInstruction i1A = new ImageInstruction(true,transform1,null);
ShapeInstruction i1B = new ShapeInstruction(r1,getShade(1-opacity1));
ImageInstruction i2A = new ImageInstruction(false,transform2,null);
ShapeInstruction i2B = new ShapeInstruction(r2,getShade(1-opacity2));
AffineTransform transform1z = TransformUtils.createAffineTransform(0,0,
big.getWidth(),0,
0,big.getHeight(),
r1.getX(),r1.getY()+r1.getHeight()*2,
r1.getX()+r1.getWidth(),r1.getY()+r1.getHeight()*2,
r1.getX(),r1.getY()+r1.getHeight()+1);
AffineTransform transform2z = TransformUtils.createAffineTransform(0,0,
big.getWidth(),0,
0,big.getHeight(),
r2.getX(),r2.getY()+r2.getHeight()*2,
r2.getX()+r2.getWidth(),r2.getY()+r2.getHeight()*2,
r2.getX(),r2.getY()+r2.getHeight()+1);
Rectangle2D shadow1Rect = new Rectangle2D.Double(
r1.getX(), r1.getY()+r1.getHeight()+1,
r1.getWidth(), r1.getHeight()
);
Rectangle2D shadow2Rect = new Rectangle2D.Double(
r2.getX(), r2.getY()+r2.getHeight()+1,
r2.getWidth(), r2.getHeight()
);
ImageInstruction i1ShadowA = new ImageInstruction(true,transform1z,null);
ShapeInstruction i1ShadowB = new ShapeInstruction(shadow1Rect,getShade(1-opacity1*.3f));
ImageInstruction i2ShadowA = new ImageInstruction(false,transform2z,null);
ShapeInstruction i2ShadowB = new ShapeInstruction(shadow2Rect,getShade(1-opacity2*.3f));
ShapeInstruction backgroundRect = new ShapeInstruction(new Rectangle(0,0,size.width,size.height),
background, null, 0
);
if(r1.getHeight()>r2.getHeight()) {
return new Transition2DInstruction[] {
backgroundRect,
i2A, i2B,
i2ShadowA, i2ShadowB,
i1A, i1B,
i1ShadowA, i1ShadowB };
} else {
return new Transition2DInstruction[] {
backgroundRect,
i1A, i1B,
i1ShadowA, i1ShadowB,
i2A, i2B,
i2ShadowA, i2ShadowB };
}
}
private Color getShade(float opacity) {
return new Color(background.getRed(), background.getGreen(), background.getBlue(), (int)(255*opacity));
}
/** This should be a dot within the rectangle (0,0,1,1).
* Imagine the rectangle is a diagram of a stage
* (you know, a theatrical stage, facing an audience).
* When the point is (.5,1), this frame is exactly
* centered in the user's field of view.
* At (.5,0) this frame is centered, but small -- as if
* in the distance. So the y-coordinate is used to
* represent depth, and the x-coordinate is used to
* represent horizontal movement.
*/
public abstract Point2D getFrameALocation(float p);
/** This should be a dot within the rectangle (0,0,1,1).
* Imagine the rectangle is a diagram of a stage
* (you know, a theatrical stage, facing an audience).
* When the point is (.5,1), this frame is exactly
* centered in the user's field of view.
* At (.5,0) this frame is centered, but small -- as if
* in the distance. So the y-coordinate is used to
* represent depth, and the x-coordinate is used to
* represent horizontal movement.
*/
public abstract Point2D getFrameBLocation(float p);
/** The opacity of the first frame */
public abstract float getFrameAOpacity(float p);
/** The opacity of the second frame */
public abstract float getFrameBOpacity(float p);
}