RubikFX, Lite Version
-----------------------------------------------------------------
魔方,精简版
后面我们将加入更多的特性,但是现在我们创建一个JavaFX应用,使用BorderPane组件,在Pane控件中添加内容,包含按钮的执行旋转功能的工具条。
public class TestRubikFX extends Application {
private final BorderPane pane=new BorderPane();
private Rubik rubik;
@Override
public void start(Stage stage) {
rubik=new Rubik();
// create toolbars
//创建工具条,在上下左右四个方向创建4个工具条
ToolBar tbTop=new ToolBar(new Button("U"),new Button("Ui"),new Button("F"),
new Button("Fi"),new Separator(),new Button("Y"),
new Button("Yi"),new Button("Z"),new Button("Zi"));
pane.setTop(tbTop);
ToolBar tbBottom=new ToolBar(new Button("B"),new Button("Bi"),new Button("D"),
new Button("Di"),new Button("E"),new Button("Ei"));
pane.setBottom(tbBottom);
ToolBar tbRight=new ToolBar(new Button("R"),new Button("Ri"),new Separator(),
new Button("X"),new Button("Xi"));
//工具条自身方向的设定
tbRight.setOrientation(Orientation.VERTICAL);
pane.setRight(tbRight);
ToolBar tbLeft=new ToolBar(new Button("L"),new Button("Li"),new Button("M"),
new Button("Mi"),new Button("S"),new Button("Si"));
tbLeft.setOrientation(Orientation.VERTICAL);
//工具条自身方向的设定
pane.setLeft(tbLeft);
pane.setCenter(rubik.getSubScene());
pane.getChildren().stream()
.filter(n->(n instanceof ToolBar))
.forEach(tb->{
((ToolBar)tb).getItems().stream()
.filter(n->(n instanceof Button))
.forEach(n->((Button)n).setOnAction(e->rubik.rotateFace(((Button)n).getText())));
});
rubik.isOnRotation().addListener((ov,b,b1)->{
pane.getChildren().stream()
.filter(n->(n instanceof ToolBar))
.forEach(tb->tb.setDisable(b1));
});
final Scene scene = new Scene(pane, 880, 680, true);
scene.setFill(Color.ALICEBLUE);
stage.setTitle("Rubik's Cube - JavaFX3D");
stage.setScene(scene);
stage.show();
}
}
下图就是我们完成后的样子。
图四
如果你想对这个应用进行深入的研究,你能够在我的GitHub上找到源代码。
网址如下:https://github.com/jperedadnr/LiteRubikFX
提示你,首先需要添加3DViewer的jar包。
如何运作?举个例子, “F”旋转,我们在rot中应用
// rotate cube indexes
//旋转立方体顺序索引
rot.turn(btRot);
// get new indexes in terms of blocks numbers from original order
//依据原始顺序的Block编号得到新的顺序索引
reorder=rot.getCube();
使用rot.printCube()方法,我们可以得到旋转前(order)和旋转后(reorder)小立方体的编号
order: 50 51 52 49 54 53 59 48 46 || 58 55 60 57 62 61 47 56 63 || 67 64 69 66 71 70 68 65 72
reorder: 59 49 50 48 54 51 46 53 52 || 58 55 60 57 62 61 47 56 63 || 67 64 69 66 71 70 68 65 72
通过比对列表和获取差异项,我们可以知道哪些小立方体必须旋转,当然我们需要添加中间立方体的编号(54),它在列表中保持不变,但是它也应该被旋转。所以我们为9个立方体创建列表层。
// select cubies to rotate: those in reorder different from order.
//选择被旋转的立方体,所有那些新序号与老序号不同的
AtomicInteger index = new AtomicInteger();
layer=order.stream()
.filter(o->!Objects.equals(o, reorder.get(index.getAndIncrement())))
.collect(Collectors.toList());
// add central cubie
//加入中间立方体
layer.add(0,reorder.get(Utils.getCenter(btRot)));
// set rotation axis
//设置旋转轴
axis=Utils.getAxis(btRot);
Utils是一个管理旋转类型数值的类,举个例子。
public static Point3D getAxis(String face){
Point3D p=new Point3D(0,0,0);
switch(face.substring(0,1)){
case "F":
case "S": p=new Point3D(0,0,1);
break;
}
return p;
}
public static int getCenter(String face){
int c=0;
switch(face.substring(0,1)){
case "F": c=4; break;
}
return c;
}
当我们获得了小立方体和旋转轴,现在需要考虑旋转监听器如何工作了。在时间轴中,(预定义)一次EASE_BOTH插值角度增加从0到90度,所以角度增量在开始时比较小,中间变大,结束时又变小。下面是一种可行的增量变化表:
0.125º-3º-4.6º-2.2º-2.48º-...-2.43º-4.78º-2.4º-2.4º-0.55º。对于在angNew中的每个值,监听者rotMap对于小立方体的每一层都会进行一个小的旋转。在HashMap表中,隶属于小立方体的meshviews面,将进行一个相对于前一个Affine矩阵的旋预旋转。
/* Listener to perform an animated face rotation */
rotMap=(ov,angOld,angNew)->{
mapMeshes.forEach((k,v)->{
layer.stream().filter(l->k.contains(l.toString()))
.findFirst().ifPresent(l->{
Affine a=new Affine(v.getTransforms().get(0));
a.prepend(new Rotate(angNew.doubleValue()-angOld.doubleValue(),axis));
v.getTransforms().setAll(a);
});
});
};
在600毫秒内,我们将对40个meshview面进行30到40次小旋转。最后,在旋转完成后,我们只需在最后的小立方体列表中更新顺序,如此我们又可以进行下一次旋转。
魔方—完整版
添加更多的特性
现在我们取得了一个很基础的基于JavaFX应用的作品,现在可以添加更多地特性,例如图形箭头和旋转预览,在旋转执行前显示旋转方向。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved