It's Hanukkah and I'm trying to animate a spinning top (dreidel):
I can get it to spin on its own axis. Here is my code:
import static javafx.scene.paint.Color.*;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Point3D;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class DreidelAnim extends Application {
private double bodyBase = 30;
private double bodyHeight = bodyBase * 3 / 2;
private double baseRadius = bodyBase / 2;
@Override
public void start(Stage stage) throws Exception {
DoubleProperty spinAngle = new SimpleDoubleProperty();
Rotate spin = new Rotate(0, Rotate.Z_AXIS);
spin.angleProperty().bind(spinAngle);
Timeline spinAnim = new Timeline(new KeyFrame(Duration.seconds(2), new KeyValue(spinAngle, 360)));
spinAnim.setCycleCount(Timeline.INDEFINITE);
spinAnim.play();
Group dreidel = createDreidel();
Translate zTrans = new Translate(0, 0, -(bodyHeight/2 + baseRadius));
dreidel.getTransforms().addAll(spin, zTrans);
Scene scene = new Scene(dreidel, 200, 200, true, SceneAntialiasing.BALANCED);
scene.setFill(SKYBLUE);
scene.setCamera(createCamera());
stage.setScene(scene);
stage.show();
}
private Group createDreidel() {
double handleHeight = bodyBase * 3/4;
Cylinder handle = new Cylinder(bodyBase / 6, handleHeight);
handle.setTranslateZ(-(bodyHeight + handleHeight) / 2);
handle.setRotationAxis(Rotate.X_AXIS);
handle.setRotate(90);
handle.setMaterial(new PhongMaterial(RED));
Box body = new Box(bodyBase, bodyBase, bodyHeight);
body.setMaterial(new PhongMaterial(BLUE));
Sphere base = new Sphere(baseRadius);
base.setTranslateZ(bodyHeight / 2);
base.setMaterial(new PhongMaterial(GREEN));
return new Group(handle, body, base);
}
private Camera createCamera() {
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(1000);
int xy = 150;
Translate trans = new Translate(-xy, xy, -120);
Rotate rotXY = new Rotate(70, new Point3D(1, 1, 0));
Rotate rotZ = new Rotate(45, new Point3D(0, 0, 1));
camera.getTransforms().addAll(trans, rotXY, rotZ);
return camera;
}
public static void main(String[] args) {
launch();
}
}
I created a simple model, spinning it around its axis, and translated it so that its tip is on (0, 0, 0)
. Here is the result:
How can I achieve something similar to the picture on the top where it's also spinning around a rotating axis?
The rotation of the axis around which the object spins is called a Precession. The spinning top motion requires 2 rotations:
On the face of it, you'd need 2
Animation
instances. However, both rotations are actually the same. The pivot point for both is(0, 0, 0)
(after thezTrans
) and they are both around the z axis, only one of them is tilted at an angle.Here is the modified code:
Where I added axes representations for viewing convenience. Note that the
getTransforms()
list does not require its objects to be unique (unlikegetChildren()
), which allows us to reuse the same animation. The order of the animations is also important as noted below.The tilting is a simple rotation around the x or y axis.
If we
tilt
and thenspin
,getTransforms().addAll(tilt, spin, zTrans)
, we would get the internal rotation (listed 1 above), only tilted:If we
spin
and thentilt
,getTransforms().addAll(spin, tilt, zTrans)
, we would get the precession (listed 2 above):Combining the 2 as in the complete code would give the desired result: