【发布时间】:2015-08-13 20:59:19
【问题描述】:
问题
我想沿着路径移动一个对象。 PathTransition 在 Duration 方面起作用,但我需要在 AnimationTimer 中使用沿路径的移动。
问题
有人知道通过 AnimationTimer 沿给定路径移动节点的方法吗?
或者,如果有人对沿硬路点在锐边处平滑节点的旋转有更好的想法,那也足够了。
代码
我需要它来沿着陡峭的路径移动物体,但旋转应该有平滑的转弯。下面的代码沿着航路点绘制路径(黑色)。
我认为这样做的一种方法是缩短路径段(红色),而不是硬 LineTo 制作 CubicCurveTo(黄色)。
PathTransition 可以方便地沿路径移动节点,并在边缘正确旋转,但不幸的是它仅在 Duration 基础上起作用。
import java.util.ArrayList;
import java.util.List;
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* Cut a given path.
* Black = original
* Red = cut off
* Yellow = smoothed using bezier curve
*/
public class Main extends Application {
/**
* Pixels that are cut off from start and end of the paths in order to shorten them and make the path smoother.
*/
private double SMOOTHNESS = 30;
@Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Scene scene = new Scene(root,1600,900);
primaryStage.setScene(scene);
primaryStage.show();
// get waypoints for path
List<Point2D> waypoints = getWayPoints();
// draw a path with sharp edges
// --------------------------------------------
Path sharpPath = createSharpPath( waypoints);
sharpPath.setStroke(Color.BLACK);
sharpPath.setStrokeWidth(8);
sharpPath.setStrokeType(StrokeType.CENTERED);
root.getChildren().add( sharpPath);
// draw a path with shortened edges
// --------------------------------------------
Path shortenedPath = createShortenedPath(waypoints, SMOOTHNESS);
shortenedPath.setStroke(Color.RED);
shortenedPath.setStrokeWidth(5);
shortenedPath.setStrokeType(StrokeType.CENTERED);
root.getChildren().add( shortenedPath);
// draw a path with smooth edges
// --------------------------------------------
Path smoothPath = createSmoothPath(waypoints, SMOOTHNESS);
smoothPath.setStroke(Color.YELLOW);
smoothPath.setStrokeWidth(2);
smoothPath.setStrokeType(StrokeType.CENTERED);
root.getChildren().add( smoothPath);
// move arrow on path
// --------------------------------------------
ImageView arrow = createArrow(30,30);
root.getChildren().add( arrow);
PathTransition pt = new PathTransition( Duration.millis(10000), smoothPath);
pt.setNode(arrow);
pt.setAutoReverse(true);
pt.setCycleCount( Transition.INDEFINITE);
pt.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
pt.play();
}
/**
* Create a path from the waypoints
* @param waypoints
* @return
*/
private Path createSharpPath( List<Point2D> waypoints) {
Path path = new Path();
for( Point2D point: waypoints) {
if( path.getElements().isEmpty()) {
path.getElements().add(new MoveTo( point.getX(), point.getY()));
}
else {
path.getElements().add(new LineTo( point.getX(), point.getY()));
}
}
return path;
}
/**
* Create a path from the waypoints, shorten the path and create a line segment between segments
* @param smoothness Pixels that are cut of from start and end.
* @return
*/
private Path createShortenedPath( List<Point2D> waypoints, double smoothness) {
Path path = new Path();
// waypoints to path
Point2D prev = null;
double x;
double y;
for( int i=0; i < waypoints.size(); i++) {
Point2D curr = waypoints.get( i);
if( i == 0) {
path.getElements().add(new MoveTo( curr.getX(), curr.getY()));
x = curr.getX();
y = curr.getY();
}
else {
// shorten previous path
double distanceX = curr.getX() - prev.getX();
double distanceY = curr.getY() - prev.getY();
double rad = Math.atan2(distanceY, distanceX);
double distance = Math.sqrt( distanceX * distanceX + distanceY * distanceY);
// cut off the paths except the last one
if( i != waypoints.size() - 1) {
distance -= smoothness;
}
x = prev.getX() + distance * Math.cos(rad);
y = prev.getY() + distance * Math.sin(rad);
path.getElements().add(new LineTo( x, y));
// shorten current path
if( i + 1 < waypoints.size()) {
Point2D next = waypoints.get( i+1);
distanceX = next.getX() - curr.getX();
distanceY = next.getY() - curr.getY();
distance = smoothness;
rad = Math.atan2(distanceY, distanceX);
x = curr.getX() + distance * Math.cos(rad);
y = curr.getY() + distance * Math.sin(rad);
path.getElements().add(new LineTo( x, y));
}
}
prev = curr;
}
return path;
}
/**
* Create a path from the waypoints, shorten the path and create a smoothing cubic curve segment between segments
* @param smoothness Pixels that are cut of from start and end.
* @return
*/
private Path createSmoothPath( List<Point2D> waypoints, double smoothness) {
Path smoothPath = new Path();
smoothPath.setStroke(Color.YELLOW);
smoothPath.setStrokeWidth(2);
smoothPath.setStrokeType(StrokeType.CENTERED);
// waypoints to path
Point2D ctrl1;
Point2D ctrl2;
Point2D prev = null;
double x;
double y;
for( int i=0; i < waypoints.size(); i++) {
Point2D curr = waypoints.get( i);
if( i == 0) {
smoothPath.getElements().add(new MoveTo( curr.getX(), curr.getY()));
x = curr.getX();
y = curr.getY();
}
else {
// shorten previous path
double distanceX = curr.getX() - prev.getX();
double distanceY = curr.getY() - prev.getY();
double rad = Math.atan2(distanceY, distanceX);
double distance = Math.sqrt( distanceX * distanceX + distanceY * distanceY);
// cut off the paths except the last one
if( i != waypoints.size() - 1) {
distance -= smoothness;
}
// System.out.println( "Segment " + i + ", angle: " + Math.toDegrees( rad) + ", distance: " + distance);
x = prev.getX() + distance * Math.cos(rad);
y = prev.getY() + distance * Math.sin(rad);
smoothPath.getElements().add(new LineTo( x, y));
// shorten current path and add a smoothing segment to it
if( i + 1 < waypoints.size()) {
Point2D next = waypoints.get( i+1);
distanceX = next.getX() - curr.getX();
distanceY = next.getY() - curr.getY();
distance = smoothness;
rad = Math.atan2(distanceY, distanceX);
x = curr.getX() + distance * Math.cos(rad);
y = curr.getY() + distance * Math.sin(rad);
ctrl1 = curr;
ctrl2 = curr;
smoothPath.getElements().add(new CubicCurveTo(ctrl1.getX(), ctrl1.getY(), ctrl2.getX(), ctrl2.getY(), x, y));
}
}
prev = curr;
}
return smoothPath;
}
/**
* Waypoints for the path
* @return
*/
public List<Point2D> getWayPoints() {
List<Point2D> path = new ArrayList<>();
// rectangle
// path.add(new Point2D( 100, 100));
// path.add(new Point2D( 400, 100));
// path.add(new Point2D( 400, 400));
// path.add(new Point2D( 100, 400));
// path.add(new Point2D( 100, 100));
// rectangle with peak on right
path.add(new Point2D( 100, 100));
path.add(new Point2D( 400, 100));
path.add(new Point2D( 450, 250));
path.add(new Point2D( 400, 400));
path.add(new Point2D( 100, 400));
path.add(new Point2D( 100, 100));
return path;
}
/**
* Create an arrow as ImageView
* @param width
* @param height
* @return
*/
private ImageView createArrow( double width, double height) {
WritableImage wi;
Polygon arrow = new Polygon( 0, 0, width, height / 2, 0, height); // left/right lines of the arrow
SnapshotParameters parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
wi = new WritableImage( (int) width, (int) height);
arrow.snapshot(parameters, wi);
return new ImageView( wi);
}
public static void main(String[] args) {
launch(args);
}
}
非常感谢您的帮助!
【问题讨论】: