1

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?

tokyo
  • 328
  • 4
  • 12
  • I have some ideas but would need to test them... can you [edit] the question to include a complete example (a [MCVE] that can be copied, compiled, and run) that does the "blink" that can be used as a starting point? – James_D Jun 06 '17 at 21:57
  • Thank you for your time! I edited post. Any ideas? You don't have to write code for everything, just, maybe, point me in the right direction. – tokyo Jun 07 '17 at 04:06
  • Did you try animating the color of the [`Light`](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/effect/Light.html)? – fabian Jun 07 '17 at 04:25
  • I did not! I will try immediately! – tokyo Jun 07 '17 at 04:38
  • It is possible to do by animating color. Is there anything more convenient? I have to dynamically make it change and do a lot of other things so using RGB would be... But ok, if I don't find any other way this will do it! – tokyo Jun 07 '17 at 10:11

1 Answers1

3

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));
     }
 };

image

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);
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045