java - 在javafx中创建连续样条曲线

标签 java javafx javafx-8 spline

我正在尝试实现类似 this image 的样条曲线将绿色控件放在每个节点的侧面,完成后使两端相互连接。

现在我发现的唯一方法是实现带有关节点的折线,在获得三个关节后计算catmull曲线的点,然后连接这些点并使用折线绘制它们,并在移动任何关节点后更新catmull点。但我发现这个过程计算量很大,所以有什么方法可以快速模拟链接中的样条线并使用绿色控件吗?

提前致谢。

private List<double[]> x = new ArrayList<double[]>();
private static ArrayList<Double> vx = new ArrayList<>();
public static boolean strtdrag;
public static double difx,dify;
static int ind;
@Override
public void start(Stage stage) {
    Group group = new Group();
    Rectangle rec=new Rectangle(0,0,400,400);
    rec.setFill(Color.BISQUE);
    Polyline polyline = new Polyline();
    polyline.setSmooth(true);
    group.getChildren().addAll(rec,polyline);
    group.setOnMouseClicked(event->{
        if (event.getButton()==MouseButton.SECONDARY) {
        Circle df = joint(event.getX(),event.getY(),polyline);
        group.getChildren().add(df);
        double[]z= {event.getX(),event.getY()};
        polyline.getPoints().addAll(new Double[] {event.getX(),event.getY()});
        x.add(z);
        vx.add(event.getX());
        vx.add(event.getY());
        }
    });
    // create scene which can be dragged and zoomed
    Scene scene = new Scene(group, 500,500);
    stage.setScene(scene);
    stage.show();
}
private static Circle joint(double x ,double y,Polyline polyline ) {
    Circle df = new Circle(x,y,5);
    df.setFill(Color.RED);
    df.setOnMousePressed(event->{
        strtdrag=true;
        for(int i = 0; i < vx.size(); i+=2) {
            if (vx.get(i)==df.getCenterX()) {
                ind=i;
                break;
            }
        }
        difx=event.getX();
        dify=event.getY();
    });

    df.setOnMouseDragged(event->{
        if(strtdrag) {
            double diffx=event.getX()-difx;
            double diffy=event.getY()-dify;
            difx=event.getX();
            dify=event.getY();
            df.setCenterX(df.getCenterX()+diffx);
            df.setCenterY(df.getCenterY()+diffy);
            vx.set(ind, df.getCenterX());
            vx.set(ind+1, df.getCenterY());
            polyline.getPoints().remove(0, vx.size());
            polyline.getPoints().addAll(vx);
        }   
    });
    df.setOnMouseReleased(event ->{
        strtdrag=false;
    });
    return df;
}

最佳答案

我建议您使用CubicCurve代表立方体的类 Bézier parametric curve分段并有控制点。

我编写了一个示例,允许您创建如下内容:

enter image description here

这是代码:

import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;

public class SplineCurve extends Application {

    private List<Anchor> points = new ArrayList<>();

    @Override
    public void start(Stage stage) {
        Group group = new Group();
        Scene scene = new Scene(group, 500,500, Color.BISQUE);
        scene.setOnMouseClicked(event -> {
            if (event.getButton() == MouseButton.SECONDARY) {
                double x = event.getX(), y = event.getY();
                if (!points.isEmpty()) {
                    Anchor start = points.get(points.size() - 1);
                    CubicCurve curve = createCurve(start, x, y, group);
                    Anchor end = new Anchor(Color.TOMATO, curve.endXProperty(), curve.endYProperty(), 5);
                    group.getChildren().add(end);
                    points.add(end);
                } else {
                    Anchor anchor = new Anchor(Color.TOMATO, x, y, 5);
                    anchor.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
                        if (e.getButton() == MouseButton.SECONDARY) {
                            Anchor start = points.get(points.size() - 1);
                            CubicCurve curve = createCurve(start, anchor.getCenterX(), anchor.getCenterY(), group);
                            curve.endXProperty().bind(anchor.centerXProperty());
                            curve.endYProperty().bind(anchor.centerYProperty());
                            points.clear();
                            e.consume();
                        }
                    });
                    group.getChildren().add(anchor);
                    points.add(anchor);
                }
            }
        });
        stage.setScene(scene);
        stage.show();
    }

    private CubicCurve createCurve(Anchor from, double x2, double y2, Group group) {
        double x1 = from.getCenterX(), y1 = from.getCenterY();
        double distance = Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
        CubicCurve curve = new CubicCurve();
        curve.setStartX(x1);
        curve.setStartY(y1);
        curve.setControlX1(x1 + 20 * (x2 - x1) / distance);
        curve.setControlY1(y1 + 20 * (y2 - y1) / distance);
        curve.setControlX2(x2 - 20 * (x2 - x1) / distance);
        curve.setControlY2(y2 - 20 * (y2 - y1) / distance);
        curve.setEndX(x2);
        curve.setEndY(y2);
        curve.setStroke(Color.BLUEVIOLET);
        curve.setStrokeWidth(4);
        curve.setStrokeLineCap(StrokeLineCap.ROUND);
        curve.setFill(Color.TRANSPARENT);
        curve.startXProperty().bind(from.centerXProperty());
        curve.startYProperty().bind(from.centerYProperty());
        Line controlLine1 = new ControlLine(curve.controlX1Property(), curve.controlY1Property(), curve.startXProperty(), curve.startYProperty());
        Line controlLine2 = new ControlLine(curve.controlX2Property(), curve.controlY2Property(), curve.endXProperty(), curve.endYProperty());
        Anchor control1 = new Anchor(Color.FORESTGREEN, curve.controlX1Property(), curve.controlY1Property(), 3);
        Anchor control2 = new Anchor(Color.FORESTGREEN, curve.controlX2Property(), curve.controlY2Property(), 3);
        group.getChildren().addAll(curve, control1, control2, controlLine1, controlLine2);
        return curve;
    }

    class ControlLine extends Line {
        ControlLine(DoubleProperty startX, DoubleProperty startY, DoubleProperty endX, DoubleProperty endY) {
            startXProperty().bind(startX);
            startYProperty().bind(startY);
            endXProperty().bind(endX);
            endYProperty().bind(endY);
            setStrokeWidth(2);
            setStroke(Color.FORESTGREEN.deriveColor(0, 1, 1, 0.5));
        }
    }

    // a draggable anchor displayed around a point.
    class Anchor extends Circle {
        Anchor(Color color, DoubleProperty x, DoubleProperty y, double radius) {
            super(x.get(), y.get(), radius);
            setFill(color.deriveColor(1, 1, 1, 0.5));
            setStroke(color);
            setStrokeWidth(2);
            setStrokeType(StrokeType.OUTSIDE);
            x.bind(centerXProperty());
            y.bind(centerYProperty());
            enableDrag();
        }
        Anchor(Color color, double x, double y, double radius) {
            super(x, y, radius);
            setFill(color.deriveColor(1, 1, 1, 0.5));
            setStroke(color);
            setStrokeWidth(2);
            setStrokeType(StrokeType.OUTSIDE);
            enableDrag();
        }
        // make a node movable by dragging it around with the mouse.
        private void enableDrag() {
            final Delta dragDelta = new Delta();
            setOnMousePressed(mouseEvent -> {
                // record a delta distance for the drag and drop operation.
                dragDelta.x = getCenterX() - mouseEvent.getX();
                dragDelta.y = getCenterY() - mouseEvent.getY();
                getScene().setCursor(Cursor.MOVE);
            });
            setOnMouseReleased(mouseEvent -> getScene().setCursor(Cursor.HAND));
            setOnMouseDragged(mouseEvent -> {
                double newX = mouseEvent.getX() + dragDelta.x;
                if (newX > 0 && newX < getScene().getWidth()) {
                    setCenterX(newX);
                }
                double newY = mouseEvent.getY() + dragDelta.y;
                if (newY > 0 && newY < getScene().getHeight()) {
                    setCenterY(newY);
                }
            });
            setOnMouseEntered(mouseEvent -> {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    getScene().setCursor(Cursor.HAND);
                }
            });
            setOnMouseExited(mouseEvent -> {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    getScene().setCursor(Cursor.DEFAULT);
                }
            });
        }
        // records relative x and y co-ordinates.
        private class Delta { double x, y; }
    }
}

关于java - 在javafx中创建连续样条曲线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48956473/

相关文章:

java - 使用泛型参数的泛型类

java - 尝试在调用 Platform.exit() 后打开 JavaFX 阶段

javafx - 如何从复选框中完全删除标签?

java.lang.IllegalStateException : Not on the main thread 错误

java - 使用 Java 从 Adob​​e 的 SiteCatalyst 请求多个元素

java - gradle:从ivy存储库下载特定文件

JavaFX:VBox 中的标签重叠

Javafx SceneBuilder 不可共享或 derby 未在 .exe 中连接

java - 如何使辅助 anchor 板扩展其尺寸以适合主 anchor 板?

css - 如何在 JavaFX 中嵌入字体变体