Need to make a sphere in JavaFX "blink" over time, like fade in and fade out. Is it possible? Or at least change shades of color. I managed to make it blink by changing SelfIlluminationMap property of the PhongMaterial I am using, with this code
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.Stage;
import javafx.util.Duration;
public class JavaFXApplication15 extends Application {
Image im = new Image("bump.jpg");
PhongMaterial ph = new PhongMaterial(Color.GREEN);
int nums = 0;
UpdateTimer timer;
private class UpdateTimer extends AnimationTimer {
int counter = 0;
@Override
public void handle(long now) {
if (counter == 20) {
update();
counter = 0;
}
counter++;
}
}
private Parent createContent() throws Exception {
timer = new UpdateTimer();
ph = new PhongMaterial(Color.YELLOW, null, null, null, im);
Box box = new Box(5, 5, 5);
box.setMaterial(ph);
// Create and position camera
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.getTransforms().addAll(
new Rotate(-20, Rotate.X_AXIS),
new Translate(0, 0, -50)
);
// Build the Scene Graph
Group root = new Group();
root.getChildren().add(camera);
root.getChildren().add(box);
// Use a SubScene
SubScene subScene = new SubScene(
root,
300, 300,
true,
SceneAntialiasing.BALANCED
);
subScene.setFill(Color.ALICEBLUE);
subScene.setCamera(camera);
Group group = new Group();
group.getChildren().add(subScene);
return group;
}
void update() {
if (ph.getSelfIlluminationMap() != null) {
ph.setSelfIlluminationMap(null);
} else {
ph.setSelfIlluminationMap(im);
}
}
@Override
public void start(Stage stage) throws Exception {
stage.setResizable(false);
Scene scene = new Scene(createContent());
stage.setScene(scene);
stage.show();
timer.start();
}
public static void main(String[] args) {
launch(args);
}
}
but is it possible to make it fade in and out? With some kind of transition possibly?
Just to see the effect, I tried animating the specularColor
of a sphere's PhongMaterial
. Starting from this example, I followed the approach shown here to get a color lookup table of equally spaced brightness values of a given hue.
private final Queue<Color> clut = new LinkedList<>();
The implementation of handle()
simply cycles through the table.
@Override
public void handle(long now) {
redMaterial.setSpecularColor(clut.peek());
clut.add(clut.remove());
}
If the result is appealing, a more flexible approach might accrue from using a concrete subclass of Transition
.
private final Animation animation = new Transition() {
{
setCycleDuration(Duration.millis(1000));
setAutoReverse(true);
setCycleCount(INDEFINITE);
}
@Override
protected void interpolate(double d) {
redMaterial.setSpecularColor(Color.hsb(LITE.getHue(), 1, d));
redMaterial.setDiffuseColor(Color.hsb(DARK.getHue(), 1, d / 2));
}
};
import java.util.LinkedList;
import java.util.Queue;
import javafx.animation.AnimationTimer;
import javafx.animation.Animation;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* @see https://stackoverflow.com/a/44447913/230513
* @see https://stackoverflow.com/a/37755149/230513
* @see https://stackoverflow.com/a/37743539/230513
* @see https://stackoverflow.com/a/37370840/230513
*/
public class TriadBox extends Application {
private static final double SIZE = 300;
private final Content content = Content.create(SIZE);
private double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;
private static final class Content {
private static final double WIDTH = 3;
private static final Color LITE = Color.RED;
private static final Color DARK = Color.RED.darker().darker();
private final Xform group = new Xform();
private final Group cube = new Group();
private final Group axes = new Group();
private final Box xAxis;
private final Box yAxis;
private final Box zAxis;
private final Box box;
private final Sphere sphere;
private final PhongMaterial redMaterial = new PhongMaterial();
private final UpdateTimer timer = new UpdateTimer();
private class UpdateTimer extends AnimationTimer {
private static final double N = 32d;
private final Queue<Color> clut = new LinkedList<>();
public UpdateTimer() {
for (int i = 0; i < N; i++) {
clut.add(Color.hsb(LITE.getHue(), 1, 1 - (i / N)));
}
for (int i = 0; i < N; i++) {
clut.add(Color.hsb(LITE.getHue(), 1, i / N));
}
}
@Override
public void handle(long now) {
redMaterial.setSpecularColor(clut.peek());
clut.add(clut.remove());
}
}
private final Animation animation = new Transition() {
{
setCycleDuration(Duration.millis(1000));
setAutoReverse(true);
setCycleCount(INDEFINITE);
}
@Override
protected void interpolate(double d) {
redMaterial.setSpecularColor(Color.hsb(LITE.getHue(), 1, d));
redMaterial.setDiffuseColor(Color.hsb(DARK.getHue(), 1, d / 2));
}
};
private static Content create(double size) {
Content c = new Content(size);
c.cube.getChildren().addAll(c.box, c.sphere);
c.axes.getChildren().addAll(c.xAxis, c.yAxis, c.zAxis);
c.group.getChildren().addAll(c.cube, c.axes);
return c;
}
private Content(double size) {
double edge = 3 * size / 4;
xAxis = createBox(edge, WIDTH, WIDTH, edge);
yAxis = createBox(WIDTH, edge / 2, WIDTH, edge);
zAxis = createBox(WIDTH, WIDTH, edge / 4, edge);
box = new Box(edge, edge / 2, edge / 4);
box.setDrawMode(DrawMode.LINE);
sphere = new Sphere(8);
redMaterial.setDiffuseColor(DARK);
redMaterial.setSpecularColor(LITE);
sphere.setMaterial(redMaterial);
sphere.setTranslateX(edge / 2);
sphere.setTranslateY(-edge / 4);
sphere.setTranslateZ(-edge / 8);
}
private Box createBox(double w, double h, double d, double edge) {
Box b = new Box(w, h, d);
b.setMaterial(new PhongMaterial(Color.AQUA));
b.setTranslateX(-edge / 2 + w / 2);
b.setTranslateY(edge / 4 - h / 2);
b.setTranslateZ(edge / 8 - d / 2);
return b;
}
}
private static class Xform extends Group {
private final Point3D px = new Point3D(1.0, 0.0, 0.0);
private final Point3D py = new Point3D(0.0, 1.0, 0.0);
private Rotate r;
private Transform t = new Rotate();
public void rx(double angle) {
r = new Rotate(angle, px);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
public void ry(double angle) {
r = new Rotate(angle, py);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
public void rz(double angle) {
r = new Rotate(angle);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("JavaFX 3D");
Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
primaryStage.setScene(scene);
scene.setFill(Color.BLACK);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(SIZE * 6);
camera.setTranslateZ(-2 * SIZE);
scene.setCamera(camera);
scene.setOnMousePressed((MouseEvent e) -> {
mousePosX = e.getSceneX();
mousePosY = e.getSceneY();
mouseOldX = e.getSceneX();
mouseOldY = e.getSceneY();
});
scene.setOnMouseDragged((MouseEvent e) -> {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = e.getSceneX();
mousePosY = e.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
if (e.isShiftDown()) {
content.group.rz(-mouseDeltaX * 180.0 / scene.getWidth());
} else if (e.isPrimaryButtonDown()) {
content.group.rx(+mouseDeltaY * 180.0 / scene.getHeight());
content.group.ry(-mouseDeltaX * 180.0 / scene.getWidth());
} else if (e.isSecondaryButtonDown()) {
camera.setTranslateX(camera.getTranslateX() - mouseDeltaX * 0.1);
camera.setTranslateY(camera.getTranslateY() - mouseDeltaY * 0.1);
}
});
scene.setOnScroll((final ScrollEvent e) -> {
camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
});
primaryStage.show();
//content.timer.start();
content.animation.play();
}
public static void main(String[] args) {
launch(args);
}
}