/*
* @(#)StarsTransition2D.java
*
* $Date: 2014-06-06 20:04:49 +0200 (P, 06 jún. 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 com.bric.geom.RectangularTransform;
import com.bric.geom.ShapeBounds;
import com.bric.geom.ShapeStringUtils;
import com.bric.geom.ShapeUtils;
import com.bric.geom.TransformUtils;
import net.jafama.FastMath;
import java.awt.Dimension;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Random;
import java.util.Vector;
/** In this transition the current frame splits apart into
* shrinking stars that spin off towards a distant point. Here are playback samples:
* <p><table summary="Sample Animations of StarsTransition2D" cellspacing="50" border="0"><tr>
* <td align="center">
* <img src="https://javagraphics.java.net/resources/transition/StarsTransition2D/StarsLeft.gif" alt="Stars Left">
* <p>Stars Left
* </td>
* <td align="center">
* <img src="https://javagraphics.java.net/resources/transition/StarsTransition2D/StarsRight.gif" alt="Stars Right">
* <p>Stars Right
* </td>
* </tr></table>
*
*/
public class StarsTransition2D extends AbstractClippedTransition2D {
/** This public static method is used by the
* {@link com.bric.image.transition.Transition2DDemoHelper}
* class to create sample animations of this transition.
* @return the transitions that should be used to demonstrate this
* transition.
*/
public static Transition[] getDemoTransitions() {
return new Transition[] {
new StarsTransition2D(LEFT),
new StarsTransition2D(RIGHT)
};
}
static GeneralPath[] star = new GeneralPath[] {
createStar(1.5f),
createStar(1.6f),
createStar(1.7f),
createStar(1.8f),
createStar(1.9f),
createStar(2f),
createStar(2.1f),
createStar(2.2f),
createStar(2.3f)
};
/** Creates a 5-pointed star.
* There are 2 distances involved. The distance from the center to middle edge
* is 1, and the distance from the center to the tips of the star is r2.
*
* @param r2
* @return
*/
private static GeneralPath createStar(float r2) {
GeneralPath p = new GeneralPath();
double angle = 0;
double k = Math.PI*2/10;
p.moveTo( (float)(FastMath.cos(angle)) , (float)(FastMath.sin(angle)) );
for(int a = 0; a<5; a++) {
p.lineTo( (float)(r2*FastMath.cos(angle+k)), (float)(r2*FastMath.sin(angle+k)) );
angle+= Math.PI*2.0/5.0;
p.lineTo( (float)(FastMath.cos(angle)), (float)(FastMath.sin(angle)) );
}
p.closePath();
return p;
}
int type = RIGHT;
/** Create a new stars transition that moves to the right.
*
*/
public StarsTransition2D() {
this(RIGHT);
}
/** Create a new stars transition.
*
* @param type must be LEFT or RIGHT.
*/
public StarsTransition2D(int type) {
if(!(type==LEFT || type==RIGHT)) {
throw new IllegalArgumentException("This transition must use type RIGHT or LEFT");
}
this.type = type;
}
@Override
public String toString() {
if(type==RIGHT) {
return "Stars Right";
} else {
return "Stars Left";
}
}
protected void fit(GeneralPath p,float length,float centerX, float centerY, GeneralPath path, Dimension size, float progress) {
Rectangle2D r = p.getBounds2D();
AffineTransform t = new AffineTransform();
t.translate(-r.getX()-r.getWidth()/2,-r.getY()-r.getHeight()/2);
t.rotate((1-progress)*1);
double scaleProgress = Math.pow(progress,3)*.75f;
t.scale(length/r.getWidth()*(.02+1.8*scaleProgress),length/r.getWidth()*(.02+1.8*scaleProgress));
p.transform(t);
if(progress>1) progress = 1;
if(progress<0) progress = 0;
Point2D endPoint = ShapeUtils.getPoint(path, 1);
Point2D startPoint = ShapeUtils.getPoint(path, progress);
Rectangle2D pathBounds = ShapeBounds.getBounds(path);
AffineTransform pathTransform = RectangularTransform.create(
pathBounds,
new Rectangle2D.Float(0,0,size.width+100,size.height)
);
pathTransform.transform(endPoint, endPoint);
pathTransform.transform(startPoint, startPoint);
r = p.getBounds();
t.setToTranslation( -r.getCenterX()+centerX-endPoint.getX()+startPoint.getX(),
-r.getCenterY()+centerY-endPoint.getY()+startPoint.getY());
p.transform(t);
}
GeneralPath[] paths = new GeneralPath[] {
ShapeStringUtils.createGeneralPath("m 82.604 6.405 c 81.496 6.405 58.748 5.967 57.234 5.937 c 48.657 5.767 39.783 5.605 30.4 11.819 c 19.367 19.125 9.915 39.783 23.713 50.988 c 35.754 60.766 50.748 54.184 53.807 47.734 c 56.105 42.887 49.464 38.223 45.159 38.223"),
ShapeStringUtils.createGeneralPath("m 130.936 47.089 c 130.636 46.6 113.679 45.149 103.386 45.364 c 94.251 45.555 88.013 49.832 82.977 54.875 c 75.353 62.51 70.458 72.743 73.292 82.281 c 76.126 91.818 93.239 89.414 93.239 89.414 c 93.239 89.414 101.796 85.728 100.734 78.276 c 99.683 70.903 96.561 71.393 93.124 71.393 c 84.366 71.393 85.661 83.327 94.277 78.651"),
ShapeStringUtils.createGeneralPath("m 124.379 23.124 c 124.044 24.216 107.26 20.206 97.997 21.225 c 88.734 22.245 74.072 27.614 64.329 38.119 c 54.586 48.624 52.078 53.184 52.683 61.27 c 53.288 69.356 71.622 78.91 77.935 66.901")
};
@Override
public Shape[] getShapes(float progress, Dimension size) {
progress = 1-progress;
GeneralPath star1 = new GeneralPath(star[8]);
GeneralPath star2 = new GeneralPath(star[5]);
GeneralPath star3 = new GeneralPath(star[8]);
GeneralPath star4 = new GeneralPath(star[5]);
GeneralPath star5 = new GeneralPath(star[7]);
GeneralPath star6 = new GeneralPath(star[5]);
GeneralPath star7 = new GeneralPath(star[8]);
GeneralPath star8 = new GeneralPath(star[6]);
Random random = new Random(2);
star1.transform(AffineTransform.getRotateInstance(random.nextDouble()));
star2.transform(AffineTransform.getRotateInstance(random.nextDouble()));
star3.transform(AffineTransform.getRotateInstance(random.nextDouble()));
star4.transform(AffineTransform.getRotateInstance(random.nextDouble()));
star5.transform(AffineTransform.getRotateInstance(random.nextDouble()));
star6.transform(AffineTransform.getRotateInstance(random.nextDouble()));
star7.transform(AffineTransform.getRotateInstance(random.nextDouble()));
star8.transform(AffineTransform.getRotateInstance(random.nextDouble()));
float big = (Math.min(size.width,size.height))*.7f;
float base1 = (float)(Math.pow(progress,2.2)*.5f+0f/8f*.3f);
float base2 = (float)(Math.pow(progress,2.2)*.5f+1f/8f*.3f);
float base3 = (float)(Math.pow(progress,2.2)*.5f+2f/8f*.3f);
float base4 = (float)(Math.pow(progress,2.2)*.5f+3f/8f*.3f);
float base5 = (float)(Math.pow(progress,2.2)*.5f+4f/8f*.3f);
float base6 = (float)(Math.pow(progress,2.2)*.5f+5f/8f*.3f);
float base7 = (float)(Math.pow(progress,2.2)*.5f+6f/8f*.3f);
float base8 = (float)(Math.pow(progress,2.2)*.5f+7f/8f*.3f);
float progress1 = (progress-base1)/(1-base1);
float progress2 = (progress-base2)/(1-base2);
float progress3 = (progress-base3)/(1-base3);
float progress4 = (progress-base4)/(1-base4);
float progress5 = (progress-base5)/(1-base5);
float progress6 = (progress-base6)/(1-base6);
float progress7 = (progress-base7)/(1-base7);
float progress8 = (progress-base8)/(1-base8);
Vector<GeneralPath> v = new Vector<GeneralPath>();
if(progress1>0) {
fit(star1,big, size.width*2f/3f, size.height*3f/4f, paths[0], size, progress1*2);
v.add(star1);
}
if(progress2>0) {
fit(star2,big, size.width*7f/8f, size.height*1f/5f, paths[1], size, progress2*2);
v.add(star2);
}
if(progress3>0) {
fit(star3,big, size.width*1f/6f, size.height*2.2f/5f, paths[2], size, progress3*2);
v.add(star3);
}
if(progress4>0) {
fit(star4,big, size.width*3.1f/6f, size.height*1.2f/5f, paths[0], size, progress4*2);
v.add(star4);
}
if(progress5>0) {
fit(star5,big, size.width*1.9f/6f, size.height*4.2f/5f, paths[1], size, progress5*2);
v.add(star5);
}
if(progress6>0) {
fit(star6,big, size.width*13f/15f, size.height*4.3f/5f, paths[2], size, progress6*2);
v.add(star6);
}
if(progress7>0) {
fit(star7,big, size.width*2f/5f, size.height*2.4f/5f, paths[0], size, progress7*2);
v.add(star7);
}
if(progress8>0) {
fit(star8,big, size.width*3f/6f, size.height*2f/5f, paths[2], size, progress8*2);
v.add(star8);
}
Shape[] shapes = v.toArray(new Shape[v.size()]);
if(type==LEFT) {
AffineTransform flipHorizontal = TransformUtils.createAffineTransform(0,0,
0,size.height,
size.width,0,
size.width,0,
size.width,size.height,
0,0);
for(int a = 0; a<shapes.length; a++) {
if(shapes[a] instanceof GeneralPath) {
((GeneralPath)shapes[a]).transform(flipHorizontal);
} else {
shapes[a] = flipHorizontal.createTransformedShape(shapes[a]);
}
}
}
return shapes;
}
@Override
public float getStrokeWidth(float progress,Dimension size) {
return 2+7*(1-progress);
}
@Override
public Transition2DInstruction[] getInstructions(float progress, Dimension size) {
//This transition was written, um, backwards. So the simplest solution is to
//meddle with the resulting frames...
Transition2DInstruction[] instr = super.getInstructions(progress, size);
for(int a = 0; a<instr.length; a++) {
if(instr[a] instanceof ImageInstruction) {
ImageInstruction i = (ImageInstruction)instr[a];
i.isFirstFrame = !i.isFirstFrame;
}
}
return instr;
}
}