package trident.swing;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.*;
import org.pushingpixels.trident.Timeline;
import org.pushingpixels.trident.TimelineScenario;
import org.pushingpixels.trident.Timeline.RepeatBehavior;
import org.pushingpixels.trident.ease.Spline;
import org.pushingpixels.trident.swing.SwingRepaintTimeline;
public class CropCircles {
public static class CropCircle {
private float xc;
private float yc;
private float radius;
private float initialRadius;
private float opacity;
public CropCircle(float xc, float yc, float radius) {
this.xc = xc;
this.yc = yc;
this.radius = 0.0f;
this.initialRadius = radius;
this.opacity = 0.0f;
}
public void paint(Graphics g) {
if (this.opacity == 0.0f)
return;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.SrcOver.derive(this.opacity));
g2d.setColor(new Color(241, 237, 190));
g2d.fill(new Ellipse2D.Float(this.xc - this.radius, this.yc
- this.radius, 2 * this.radius, 2 * this.radius));
g2d.dispose();
}
public float getInitialRadius() {
return initialRadius;
}
public void setOpacity(float opacity) {
this.opacity = opacity;
}
public void setRadius(float radius) {
this.radius = radius;
}
}
private static class CropArc {
private float xc;
private float yc;
private float radius;
private java.util.List<CropCircle> cs;
public CropArc(float x1, float y1, float x2, float y2, float x3,
float y3) {
float ma = (y2 - y1) / (x2 - x1);
float mb = (y3 - y2) / (x3 - x2);
this.xc = (ma * mb * (y1 - y3) + mb * (x1 + x2) - ma * (x2 + x3))
/ (2 * (mb - ma));
if (ma == 0.0) {
this.yc = (y2 + y3) / 2 - (this.xc - (x2 + x3) / 2) / mb;
} else {
this.yc = (y1 + y2) / 2 - (this.xc - (x1 + x2) / 2) / ma;
}
this.radius = (float) Math.sqrt((x1 - this.xc) * (x1 - this.xc)
+ (y1 - this.yc) * (y1 - this.yc));
}
public float getCenterX() {
return this.xc;
}
public float getCenterY() {
return this.yc;
}
public void populate(float startAngle, float startRadius, float factor) {
this.cs = new ArrayList<CropCircle>();
float currRad = startRadius;
float currAngle = startAngle;
while (currRad > 2) {
double b = Math.abs(Math.acos(1.0 - currRad * currRad
/ (2 * this.radius * this.radius)));
currRad *= factor;
double g = Math.abs(Math.acos(1.0 - currRad * currRad
/ (2 * this.radius * this.radius)));
currAngle -= (b + g);
this.cs.add(new CropCircle(this.xc + this.radius
* (float) Math.cos(currAngle), this.yc - this.radius
* (float) Math.sin(currAngle), currRad));
}
}
public void paint(Graphics g) {
// Graphics2D g2d = (Graphics2D) g.create();
// g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
// RenderingHints.VALUE_ANTIALIAS_ON);
// g2d.setColor(Color.black);
// g2d.draw(new Ellipse2D.Double(this.xc - this.radius, this.yc
// - this.radius, 2 * this.radius, 2 * this.radius));
if (this.cs != null) {
for (CropCircle c : this.cs) {
c.paint(g);
}
}
// g2d.dispose();
}
public java.util.List<CropCircle> getCircles() {
return Collections.unmodifiableList(this.cs);
}
}
public static class CropPanel extends JPanel {
CropArc arc1;
CropArc arc2;
CropArc arc3;
CropCircle center;
CropCircle center1;
CropCircle center2;
CropCircle center3;
float rotation;
public CropPanel() {
int xc = 300;
int yc = 200;
int rad = 80;
float x1 = xc;
float y1 = yc - rad * (float) Math.sin(-Math.PI / 2);
float x2 = xc + rad * (float) Math.cos(5 * Math.PI / 6);
float y2 = yc - rad * (float) Math.sin(5 * Math.PI / 6);
float x3 = xc + rad * (float) Math.cos(Math.PI / 6);
float y3 = yc - rad * (float) Math.sin(Math.PI / 6);
this.arc1 = new CropArc(xc, yc, x2, y2, x3, y3);
this.arc2 = new CropArc(xc, yc, x2, y2, x1, y1);
this.arc3 = new CropArc(xc, yc, x3, y3, x1, y1);
this.rotation = 0.0f;
int startRadius = 46;
float factor = 0.79f;
this.arc1.populate((float) (-Math.PI / 2), startRadius, factor);
this.arc2.populate((float) (Math.PI / 6), startRadius, factor);
this.arc3.populate((float) (5 * Math.PI / 6), startRadius, factor);
this.center = new CropCircle(xc, yc, startRadius - 2);
int smallRadius = 15;
this.center1 = new CropCircle(this.arc1.getCenterX(), this.arc1
.getCenterY(), smallRadius);
this.center1.setRadius(smallRadius);
this.center2 = new CropCircle(this.arc2.getCenterX(), this.arc2
.getCenterY(), smallRadius);
this.center2.setRadius(smallRadius);
this.center3 = new CropCircle(this.arc3.getCenterX(), this.arc3
.getCenterY(), smallRadius);
this.center3.setRadius(smallRadius);
Timeline pulseCenters = new Timeline();
pulseCenters.addPropertyToInterpolate(Timeline.<Float> property(
"opacity").on(this.center1).from(0.0f).to(1.0f));
pulseCenters.addPropertyToInterpolate(Timeline.<Float> property(
"opacity").on(this.center2).from(0.0f).to(1.0f));
pulseCenters.addPropertyToInterpolate(Timeline.<Float> property(
"opacity").on(this.center3).from(0.0f).to(1.0f));
pulseCenters.setDuration(750);
pulseCenters.setEase(new Spline(0.9f));
pulseCenters.playLoop(RepeatBehavior.REVERSE);
Timeline rotationTimeline = new Timeline(this);
rotationTimeline.addPropertyToInterpolate("rotation", 0.0f,
(float) (2 * Math.PI));
rotationTimeline.setDuration(10000);
rotationTimeline.playLoop(RepeatBehavior.LOOP);
Timeline repaintTimeline = new SwingRepaintTimeline(this);
repaintTimeline.playLoop(RepeatBehavior.LOOP);
getScenario().playLoop();
}
private TimelineScenario getScenario() {
final TimelineScenario.RendezvousSequence showScenario = new TimelineScenario.RendezvousSequence();
java.util.List<CropCircle> circles1 = this.arc1.getCircles();
java.util.List<CropCircle> circles2 = this.arc2.getCircles();
java.util.List<CropCircle> circles3 = this.arc3.getCircles();
int count = circles1.size();
for (int i = count - 1; i >= 0; i--) {
int duration = 200 + (count - i) * 70;
CropCircle c1 = circles1.get(i);
Timeline t1 = new Timeline(c1);
t1.addPropertyToInterpolate("opacity", 0.0f, 1.0f);
t1.addPropertyToInterpolate("radius", 0.0f, c1
.getInitialRadius());
t1.setDuration(duration);
t1.setEase(new Spline(0.9f));
CropCircle c2 = circles2.get(i);
Timeline t2 = new Timeline(c2);
t2.addPropertyToInterpolate("opacity", 0.0f, 1.0f);
t2.addPropertyToInterpolate("radius", 0.0f, c2
.getInitialRadius());
t2.setDuration(duration);
t2.setEase(new Spline(0.9f));
CropCircle c3 = circles3.get(i);
Timeline t3 = new Timeline(c3);
t3.addPropertyToInterpolate("opacity", 0.0f, 1.0f);
t3.addPropertyToInterpolate("radius", 0.0f, c3
.getInitialRadius());
t3.setDuration(duration);
t3.setEase(new Spline(0.9f));
showScenario.addScenarioActor(t1);
showScenario.addScenarioActor(t2);
showScenario.addScenarioActor(t3);
showScenario.rendezvous();
}
Timeline tCenter = new Timeline(this.center);
tCenter.addPropertyToInterpolate("opacity", 0.0f, 1.0f);
tCenter.addPropertyToInterpolate("radius", 0.0f, this.center
.getInitialRadius());
tCenter.setDuration(800);
showScenario.addScenarioActor(tCenter);
showScenario.rendezvous();
// fade out all circles after a small delay
Timeline fadeOut = new Timeline(this);
fadeOut.addPropertyToInterpolate("opacity", 1.0f, 0.0f);
fadeOut.setInitialDelay(500);
showScenario.addScenarioActor(fadeOut);
// showScenario.addCallback(new TimelineScenarioCallback() {
// @Override
// public void onTimelineScenarioDone() {
// getScenario().play();
// }
// });
return showScenario;
}
public void setOpacity(float opacity) {
for (CropCircle cc : arc1.getCircles())
cc.setOpacity(opacity);
for (CropCircle cc : arc2.getCircles())
cc.setOpacity(opacity);
for (CropCircle cc : arc3.getCircles())
cc.setOpacity(opacity);
center.setOpacity(opacity);
}
public void setRotation(float rotation) {
this.rotation = rotation;
}
@Override
protected void paintComponent(Graphics g) {
g.setColor(new Color(74, 86, 40));
g.fillRect(0, 0, getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(300, 200);
g2d.rotate(this.rotation);
g2d.translate(-300, -200);
this.arc1.paint(g2d);
this.arc2.paint(g2d);
this.arc3.paint(g2d);
this.center.paint(g2d);
this.center1.paint(g2d);
this.center2.paint(g2d);
this.center3.paint(g2d);
g2d.dispose();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame fr = new JFrame();
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CropPanel panel = new CropPanel();
panel.setPreferredSize(new Dimension(600, 400));
fr.add(panel);
fr.pack();
fr.setLocationRelativeTo(null);
fr.setVisible(true);
}
});
}
}