英文:
Rotate an object about a pivot in javaFX
问题
I'm trying to create a Rubik's cube in javaFX and I am struggling with the rotating of the faces. Up until now I have just been adding the 9 cubies in a face into a group and rotating that 90 degrees to get the full face to rotate in the correct way however this method does not seem to work for multiple rotations.
Instead I want to rotate each cubie about a pivot point at the center of each face. To do this I tried to use the Rotate classes built-in pivot parameter but it seems to have no effect on the rotation. Here is a simplified version of the function I'm using to rotate the front Face where the 2D array frontFace contains all the cubies in the front face from top left to bottom right:
public void rotate() {
Rotate r = new Rotate(45, 0, 0, -100, Rotate.Z_AXIS);
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
frontFace[x][y].getCube().getTransforms().add(r);
}
}
}
This produces the following result (I have rotated it 45 degrees so it is easier to see):
Whereas I would like it to rotate like this:
This is my first project using JavaFX, and any help would be greatly appreciated! The full project so far can be found here:
EDIT: I have been asked to include a Minimal, Reproducible Example so I have tried to condense the issue down into one class that hopefully isn't too confusing, here is the code:
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.AmbientLight;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class Example extends Application {
public static final float X_RED = 0.5f / 7f;
public static final float X_GREEN = 1.5f / 7f;
public static final float X_BLUE = 2.5f / 7f;
public static final float X_YELLOW = 3.5f / 7f;
public static final float X_ORANGE = 4.5f / 7f;
public static final float X_WHITE = 5.5f / 7f;
public static final float X_GRAY = 6.5f / 7f;
public static int red = 0;
public static int green = 1;
public static int blue = 2;
public static int yellow = 3;
public static int orange = 4;
public static int white = 5;
public static int grey = 6;
public static int WIDTH = 1400;
public static int HEIGHT = 800;
private static final SmartGroup MainCubeGroup = new SmartGroup();
private static PhongMaterial mat = new PhongMaterial();
MeshView[][] frontFace = new MeshView[3][3];
@Override
public void start(Stage primaryStage) {
ViewCamera camera = new ViewCamera(MainCubeGroup);
mat.setDiffuseMap(new Image(getClass().getResourceAsStream("pallete.png")));
MainCubeGroup.getChildren().addAll(new AmbientLight(Color.WHITE));
Scene scene = new Scene(MainCubeGroup, WIDTH, HEIGHT, true);
//creates just the frontFace of the cube
frontFace[0][0] = createCube(new Point3D(-100, -100, -100), new int[]{blue, grey, white, grey, red, grey});
frontFace[0][1] = createCube(new Point3D(0, -100, -100), new int[]{blue, grey, white, grey, grey, grey});
frontFace[0][2] = createCube(new Point3D(100, -100, -100), new int[] {blue, green, white, grey, grey, grey});
frontFace[1][0] = createCube(new Point3D(-100, 0, -100), new int[]{blue, grey, grey, grey, red, grey});
frontFace[1][1] = createCube(new Point3D(0, 0, -100), new int[]{blue, grey, grey, grey, grey, grey});
frontFace[1][2] = createCube(new Point3D(100, 0, -100), new int[]{blue, green, grey, grey, grey, grey});
frontFace[2][0] = createCube(new Point3D(-100, 100, -100), new int[]{blue, grey, grey, grey, red, yellow});
frontFace[2][1] = createCube(new Point3D(0, 100, -100), new int[]{blue, grey, grey, grey, grey, yellow});
frontFace[2][2] = createCube(new Point3D(100, 100, -100), new int[]{blue, green, grey, grey, grey, yellow});
rotate(45);
scene.setFill(Color.BLANCHEDALMOND);
scene.setCamera(camera);
primaryStage.setTitle("DDD");
primaryStage.setScene(scene);
MainCubeGroup.translateXProperty().set(WIDTH / 2);
MainCubeGroup.translateYProperty().set(HEIGHT / 2);
primaryStage.show();
camera.initMouseControl(scene, primaryStage);
}
public static void main(String[] args) {
launch(args);
}
public void rotate(int angle) {
Rotate r = new Rotate(angle, 0, 0, -100, Rotate.Z_AXIS);
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
frontFace[x][y].getTransforms().add(r);
}
}
}
public static MeshView createCube(Point3D position, int[] face){
int sideLength = 90;
TriangleMesh meshCube = new TriangleMesh();
meshCube.getTexCoords().addAll(
X_RED, 0.5f,
X_GREEN, 0.5f,
X_BLUE, 0.5f,
X_YELLOW, 0.5f,
X_ORANGE, 0.5f,
X_WHITE, 0.5f,
X_GRAY, 0.5f
);
meshCube.getPoints().addAll(
sideLength/2, sideLength/2, sideLength/2, //point0
sideLength/2, -sideLength/2, sideLength/2, //point1
sideLength/2, sideLength/2, -sideLength/2, //point2
<details>
<summary>英文:</summary>
I'm trying to create a Rubik's cube in javaFX and I am struggling with the rotating of the faces. Up until now I have just been adding the 9 cubies in a face into a group and rotating that 90 degrees to get the full face to rotate in the correct way however this method does not seem to work for multiple rotations.
Instead I want to rotate each cubie about a pivot point at the centre of each face. To do this I tried to use the Rotate classes built in pivot parameter but it seems to have no affect on the rotation. Here is a simplified version of the function I'm using to rotate the front Face where the 2D array frontFace contains all the cubies in the front face from top left to bottom right:
public void rotate() {
Rotate r = new Rotate(45, 0, 0, -100, Rotate.Z_AXIS);
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
frontFace[x][y].getCube().getTransforms().add(r);
}
}
}
This produces the following result (I have rotated it 45 degrees so it is easier to see):
[Current Rotation][1]
Whereas I would like it to rotate like this:
[Ideal rotation][2]
This is my first project using javaFX and any help would be greatly appreciated! The full project so far can be found here:
https://gofile.io/d/KidwKh
EDIT:
I have been asked to include a Minimal, Reproducible Example so I have tried to condense the issue down into one class that hopefully isn't to confusing, here is the code:
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.AmbientLight;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class Example extends Application {
public static final float X_RED = 0.5f / 7f;
public static final float X_GREEN = 1.5f / 7f;
public static final float X_BLUE = 2.5f / 7f;
public static final float X_YELLOW = 3.5f / 7f;
public static final float X_ORANGE = 4.5f / 7f;
public static final float X_WHITE = 5.5f / 7f;
public static final float X_GRAY = 6.5f / 7f;
public static int red = 0;
public static int green = 1;
public static int blue = 2;
public static int yellow = 3;
public static int orange = 4;
public static int white = 5;
public static int grey = 6;
public static int WIDTH = 1400;
public static int HEIGHT = 800;
private static final SmartGroup MainCubeGroup = new SmartGroup();
private static PhongMaterial mat = new PhongMaterial();
MeshView[][] frontFace = new MeshView[3][3];
@Override
public void start(Stage primaryStage) {
ViewCamera camera = new ViewCamera(MainCubeGroup);
mat.setDiffuseMap(new Image(getClass().getResourceAsStream("pallete.png")));
MainCubeGroup.getChildren().addAll(new AmbientLight(Color.WHITE));
Scene scene = new Scene(MainCubeGroup, WIDTH, HEIGHT, true);
//creates just frontFace of cube
frontFace[0][0] = createCube(new Point3D(-100, -100, -100), new int[]{blue, grey, white, grey, red, grey});
frontFace[0][1] = createCube(new Point3D(0, -100, -100), new int[]{blue, grey, white, grey, grey, grey});
frontFace[0][2] = createCube(new Point3D(100, -100, -100), new int[] {blue, green, white, grey, grey, grey});
frontFace[1][0] = createCube(new Point3D(-100, 0, -100), new int[]{blue, grey, grey, grey, red, grey});
frontFace[1][1] = createCube(new Point3D(0, 0, -100), new int[]{blue, grey, grey, grey, grey, grey});
frontFace[1][2] = createCube(new Point3D(100, 0, -100), new int[]{blue, green, grey, grey, grey, grey});
frontFace[2][0] = createCube(new Point3D(-100, 100, -100), new int[]{blue, grey, grey, grey, red, yellow});
frontFace[2][1] = createCube(new Point3D(0, 100, -100), new int[]{blue, grey, grey, grey, grey, yellow});
frontFace[2][2] = createCube(new Point3D(100, 100, -100), new int[]{blue, green, grey, grey, grey, yellow});
rotate(45);
scene.setFill(Color.BLANCHEDALMOND);
scene.setCamera(camera);
primaryStage.setTitle("DDD");
primaryStage.setScene(scene);
MainCubeGroup.translateXProperty().set(WIDTH / 2);
MainCubeGroup.translateYProperty().set(HEIGHT / 2);
primaryStage.show();
camera.initMouseControl(scene, primaryStage);
}
public static void main(String[] args) {launch(args);}
public void rotate(int angle) {
Rotate r = new Rotate(angle, 0, 0, -100, Rotate.Z_AXIS);
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
frontFace[x][y].getTransforms().add(r);
}
}
}
public static MeshView createCube(Point3D position, int[] face){
int sideLength = 90;
TriangleMesh meshCube = new TriangleMesh();
meshCube.getTexCoords().addAll(
X_RED, 0.5f,
X_GREEN, 0.5f,
X_BLUE, 0.5f,
X_YELLOW, 0.5f,
X_ORANGE, 0.5f,
X_WHITE, 0.5f,
X_GRAY, 0.5f
);
meshCube.getPoints().addAll(
sideLength/2, sideLength/2, sideLength/2, //point0
sideLength/2, -sideLength/2, sideLength/2, //point1
sideLength/2, sideLength/2, -sideLength/2, //point2
sideLength/2, -sideLength/2, -sideLength/2, //point3
-sideLength/2, sideLength/2, sideLength/2, //point4
-sideLength/2, -sideLength/2, sideLength/2, //point5
-sideLength/2, sideLength/2, -sideLength/2, //point6
-sideLength/2, -sideLength/2, -sideLength/2 //point7
);
//textures
meshCube.getFaces().addAll(
2,face[0],3,face[0],6,face[0], // F
3,face[0],7,face[0],6,face[0],
0,face[1],1,face[1],2,face[1], // R
2,face[1],1,face[1],3,face[1],
1,face[2],5,face[2],3,face[2], // U
5,face[2],7,face[2],3,face[2],
0,face[3],4,face[3],1,face[3], // B
4,face[3],5,face[3],1,face[3],
4,face[4],6,face[4],5,face[4], // L
6,face[4],7,face[4],5,face[4],
0,face[5],2,face[5],4,face[5], // D
2,face[5],6,face[5],4,face[5]
);
MeshView cube = new MeshView(meshCube);
cube.setDrawMode(DrawMode.FILL);
cube.setMaterial(mat);
cube.setTranslateX(position.getX());
cube.setTranslateY(position.getY());
cube.setTranslateZ(position.getZ());
MainCubeGroup.getChildren().add(cube);
return cube;
}
}
Here is the image, pallete.png which is used in the code if you would like the colours although I don't think it's relevant to the issue:
[pallete.png][3]
Thanks.
[1]: https://i.stack.imgur.com/9nqRn.png
[2]: https://i.stack.imgur.com/IqcQK.png
[3]: https://i.stack.imgur.com/b6i7s.png
</details>
# 答案1
**得分**: 0
将Rotate对象`r`附加到frontFace节点时,每个这样的节点都会以该节点自己的坐标系解释Rotate对象的坐标。每个立方体在自己的坐标系中都定义了它的中心点为(0, 0, -100),因此围绕该枢轴点旋转会使立方体围绕自己的中心旋转。
您想要的是所有frontFace节点都围绕同一个坐标系中的单个枢轴点旋转——它们父级Group的坐标系。
然后,您可以重复使用[parentToLocal](https://openjfx.io/javadoc/14/javafx.graphics/javafx/scene/Node.html#parentToLocal(javafx.geometry.Point3D))方法将该枢轴点转换为每个frontFace的坐标系,并基于该“本地”枢轴点应用一个新的Rotate实例。
```java
public void rotate(int angle) {
// 这些坐标位于父级Group的坐标系中。
Point3D pivot = new Point3D(0, 0, -100);
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
Node cube = frontFace[x][y];
Point3D localPivot = cube.parentToLocal(pivot);
Rotate r = new Rotate(angle,
localPivot.getX(), localPivot.getY(), localPivot.getZ(),
Rotate.Z_AXIS);
cube.getTransforms().add(r);
}
}
}
英文:
When you append the Rotate object r
to a frontFace Node, each such Node interprets the coordinates of the Rotate object in that Node’s own coordinate system. Each cube has defined its center as (0, 0, -100) in its own coordinate system, so rotating around that pivot point rotates the cube around its own center.
What you want is for all of the frontFace Nodes to be rotated about a single pivot point in the same coordinate system—their parent Group’s coordinate system.
You can then repeatedly convert that pivot point to the coordinate system of each frontFace using the parentToLocal method, and apply a new Rotate instance based on that “local” pivot point.
public void rotate(int angle) {
// These coordinates are in the parent Group's coordinate system.
Point3D pivot = new Point3D(0, 0, -100);
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
Node cube = frontFace[x][y];
Point3D localPivot = cube.parentToLocal(pivot);
Rotate r = new Rotate(angle,
localPivot.getX(), localPivot.getY(), localPivot.getZ(),
Rotate.Z_AXIS);
cube.getTransforms().add(r);
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论