JavaFX中的嵌套路径转换
我试图让我的节点沿着一个圆的路径移动,同时让这个圆沿着一个矩形的路径移动。可能吗 这就是我到目前为止所做的:JavaFX中的嵌套路径转换,java,javafx,path-traversal,Java,Javafx,Path Traversal,我试图让我的节点沿着一个圆的路径移动,同时让这个圆沿着一个矩形的路径移动。可能吗 这就是我到目前为止所做的: void move(GamePane aThis) { double speed = 10; Rectangle rectangle = new Rectangle(100, 200, 100, 500); Circle circle = new Circle(50); circle.setFill(Color.WHITE); circle.s
void move(GamePane aThis)
{
double speed = 10;
Rectangle rectangle = new Rectangle(100, 200, 100, 500);
Circle circle = new Circle(50);
circle.setFill(Color.WHITE);
circle.setStroke(Color.BLACK);
circle.setStrokeWidth(3);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.millis(1000));
pt.setPath(circle);
pt.setNode(this);
pt.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pt.setCycleCount(Timeline.INDEFINITE);
pt.setAutoReverse(false);
pt.play();
PathTransition pt2 = new PathTransition();
pt2.setDuration(Duration.millis(1000));
pt2.setPath(rectangle);
pt2.setNode(circle);
pt2.setOrientation
(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pt2.setCycleCount(Timeline.INDEFINITE);
pt2.setAutoReverse(false);
pt2.play();
}
理论上,应该可以将一个过渡嵌套在另一个过渡之上 但存在一个问题:转换应用于转换属性,而节点布局未修改。这意味着在您的情况下,圆将遵循矩形定义的路径,但节点将在圆的初始位置上继续旋转 所以我们需要找到一种方法来随时更新圆的位置,这样节点就可以在这个位置上旋转 基于此,一种可能的方法是使用两个
AnimationTimer
s,以及一种在任何时刻插值路径并相应更新位置的方法
第一步是将原始路径转换为仅使用线性元素的路径:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import javafx.geometry.Point2D;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.shape.QuadCurveTo;
/**
*
* @author jpereda
*/
public class LinearPath {
private final Path originalPath;
public LinearPath(Path path){
this.originalPath=path;
}
public Path generateLinePath(){
/*
Generate a list of points interpolating the original path
*/
originalPath.getElements().forEach(this::getPoints);
/*
Create a path only with MoveTo,LineTo
*/
Path path = new Path(new MoveTo(list.get(0).getX(),list.get(0).getY()));
list.stream().skip(1).forEach(p->path.getElements().add(new LineTo(p.getX(),p.getY())));
path.getElements().add(new ClosePath());
return path;
}
private Point2D p0;
private List<Point2D> list;
private final int POINTS_CURVE=5;
private void getPoints(PathElement elem){
if(elem instanceof MoveTo){
list=new ArrayList<>();
p0=new Point2D(((MoveTo)elem).getX(),((MoveTo)elem).getY());
list.add(p0);
} else if(elem instanceof LineTo){
list.add(new Point2D(((LineTo)elem).getX(),((LineTo)elem).getY()));
} else if(elem instanceof CubicCurveTo){
Point2D ini = (list.size()>0?list.get(list.size()-1):p0);
IntStream.rangeClosed(1, POINTS_CURVE).forEach(i->list.add(evalCubicBezier((CubicCurveTo)elem, ini, ((double)i)/POINTS_CURVE)));
} else if(elem instanceof QuadCurveTo){
Point2D ini = (list.size()>0?list.get(list.size()-1):p0);
IntStream.rangeClosed(1, POINTS_CURVE).forEach(i->list.add(evalQuadBezier((QuadCurveTo)elem, ini, ((double)i)/POINTS_CURVE)));
} else if(elem instanceof ClosePath){
list.add(p0);
}
}
private Point2D evalCubicBezier(CubicCurveTo c, Point2D ini, double t){
Point2D p=new Point2D(Math.pow(1-t,3)*ini.getX()+
3*t*Math.pow(1-t,2)*c.getControlX1()+
3*(1-t)*t*t*c.getControlX2()+
Math.pow(t, 3)*c.getX(),
Math.pow(1-t,3)*ini.getY()+
3*t*Math.pow(1-t, 2)*c.getControlY1()+
3*(1-t)*t*t*c.getControlY2()+
Math.pow(t, 3)*c.getY());
return p;
}
private Point2D evalQuadBezier(QuadCurveTo c, Point2D ini, double t){
Point2D p=new Point2D(Math.pow(1-t,2)*ini.getX()+
2*(1-t)*t*c.getControlX()+
Math.pow(t, 2)*c.getX(),
Math.pow(1-t,2)*ini.getY()+
2*(1-t)*t*c.getControlY()+
Math.pow(t, 2)*c.getY());
return p;
}
}
基本上,一旦你有了一条线性路径,它就会为每一行生成一个段
。现在,通过这些线段的列表,您可以调用interpolate
方法来计算节点在0和1之间任何分数处的位置和旋转,并且在第二次过渡的情况下,相应地更新形状的位置
最后,您可以在应用程序中创建两个AnimationTimer
s:
@Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Polygon poly = new Polygon( 0, 0, 30, 15, 0, 30);
poly.setFill(Color.YELLOW);
poly.setStroke(Color.RED);
root.getChildren().add(poly);
Rectangle rectangle = new Rectangle(200, 100, 100, 400);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStroke(Color.BLUE);
Circle circle = new Circle(50);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.RED);
circle.setStrokeWidth(3);
root.getChildren().add(rectangle);
root.getChildren().add(circle);
PathInterpolator in1=new PathInterpolator(rectangle, circle);
PathInterpolator in2=new PathInterpolator(circle, poly);
AnimationTimer timer1 = new AnimationTimer() {
@Override
public void handle(long now) {
double millis=(now/1_000_000)%10000;
in1.interpolate(millis/10000);
}
};
AnimationTimer timer2 = new AnimationTimer() {
@Override
public void handle(long now) {
double millis=(now/1_000_000)%2000;
// Interpolate over the translated circle
in2.interpolate(millis/2000,
circle.getTranslateX(),
circle.getTranslateY());
}
};
timer2.start();
timer1.start();
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
请注意,可以对动画应用不同的速度
这张照片拍摄了这个动画的两个瞬间
@Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Polygon poly = new Polygon( 0, 0, 30, 15, 0, 30);
poly.setFill(Color.YELLOW);
poly.setStroke(Color.RED);
root.getChildren().add(poly);
Rectangle rectangle = new Rectangle(200, 100, 100, 400);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStroke(Color.BLUE);
Circle circle = new Circle(50);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.RED);
circle.setStrokeWidth(3);
root.getChildren().add(rectangle);
root.getChildren().add(circle);
PathInterpolator in1=new PathInterpolator(rectangle, circle);
PathInterpolator in2=new PathInterpolator(circle, poly);
AnimationTimer timer1 = new AnimationTimer() {
@Override
public void handle(long now) {
double millis=(now/1_000_000)%10000;
in1.interpolate(millis/10000);
}
};
AnimationTimer timer2 = new AnimationTimer() {
@Override
public void handle(long now) {
double millis=(now/1_000_000)%2000;
// Interpolate over the translated circle
in2.interpolate(millis/2000,
circle.getTranslateX(),
circle.getTranslateY());
}
};
timer2.start();
timer1.start();
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}