【问题标题】:JavaFX: Is there a way for Y axis to be positive upwards with origin at bottom-left?JavaFX:有没有办法让 Y 轴向上,原点在左下角?
【发布时间】:2020-02-17 20:00:33
【问题描述】:

JavaFX 坐标系从屏幕顶部绘制 Y 坐标,向下为正。我希望它是向上的,从屏幕底部开始。

需要翻译,需要翻转文本节点。

希望绘制的矩形能够以我们在数学课中“自然”的方式定位。以左下角为原点,向右上角扩展。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class FlippedExampleChart extends Application {

    private LineChart<Number, Number> chart;

    @Override
    public void start(Stage primaryStage) throws Exception {
        final NumberAxis xAxis = new NumberAxis();
        final NumberAxis yAxis = new NumberAxis();

        // Flip the axis
        yAxis.setScaleY(-1);

        // TODO How to translate to bottom of screen.
        // TODO How to flip the text nodes.

        this.chart = new LineChart<Number, Number>(xAxis, yAxis) {
            @Override
            public void layoutPlotChildren() {
                super.layoutPlotChildren();

                double height = yAxis.getDisplayPosition(100);

                Rectangle r = new Rectangle(0, 0, 50, height);
                r.setFill(Color.GREEN);
                getPlotChildren().addAll(r);
            }
        };
        this.chart.setAnimated(false);

        VBox vbox = new VBox(this.chart);

        Scene scene = new Scene(vbox, 400, 200);
        primaryStage.setScene(scene);
        primaryStage.setHeight(600);
        primaryStage.setWidth(400);
        primaryStage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

【问题讨论】:

  • 这不是您所要求的,但yAxis.getDisplayPosition(y) 会将 y 轴刻度上的 y 值转换为坐标系中的值。所以不要对轴应用平移,只需在轴的坐标系中工作并使用该方法来转换变量。
  • @James_D 我熟悉这种方法。由于增加了心算开销,我想更进一步。
  • 我想我假设您希望将矩形绘制在与轴相同的坐标中(即跨越(0,0)到(50,100) 沿常规图表轴)。但也许你真的想在像素坐标中工作(很难看到一个好的用例......)?困难在于使用Rectangles,因为您需要宽度和高度,而不是顶点的坐标。这是pretty straightforward 和其他形状。

标签: java javafx


【解决方案1】:

我假设这里的目的是使用图表轴定义的坐标系绘制一个形状。

最简单的方法可能是变换形状而不是轴。您可以为此创建一个实用方法:

private Transform chartDisplayTransform(NumberAxis xAxis, NumberAxis yAxis) {
    return new Affine(
            xAxis.getScale(), 0, xAxis.getDisplayPosition(0),
            0, yAxis.getScale(), yAxis.getDisplayPosition(0)
    );
}

关于您的代码的另一个注意事项:layoutPlotChildren() 方法不一定会删除节点,因此您最终可能会添加比您发布的代码更多的矩形。

这是使用此方法的代码版本(并确保只添加一次矩形)。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;

public class FlippedExampleChart extends Application {

    private LineChart<Number, Number> chart;

    @Override
    public void start(Stage primaryStage) throws Exception {
        final NumberAxis xAxis = new NumberAxis();
        final NumberAxis yAxis = new NumberAxis();

        // Flip the axis
        // yAxis.setScaleY(-1);

        Rectangle r = new Rectangle(0, 0, 50, 100);
        r.setFill(Color.GREEN);

        this.chart = new LineChart<Number, Number>(xAxis, yAxis) {
            @Override
            public void layoutPlotChildren() {
                super.layoutPlotChildren();
                r.getTransforms().setAll(chartDisplayTransform(xAxis, yAxis));
                // note nodes don't get removed from the plot children, and this method may be
                // called often:
                if (!getPlotChildren().contains(r)) {
                    getPlotChildren().add(r);
                }
            }
        };
        this.chart.setAnimated(false);

        VBox vbox = new VBox(this.chart);

        Scene scene = new Scene(vbox, 400, 200);
        primaryStage.setScene(scene);
        primaryStage.setHeight(600);
        primaryStage.setWidth(400);
        primaryStage.show();
    }

    private Transform chartDisplayTransform(NumberAxis xAxis, NumberAxis yAxis) {
        return new Affine(xAxis.getScale(), 0, xAxis.getDisplayPosition(0), 0, yAxis.getScale(),
                yAxis.getDisplayPosition(0));
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

结果:

如果您有多个节点以这种方式处理,策略是将它们添加到Group,并将转换应用于Group

@Override
public void start(Stage primaryStage) throws Exception {
    final NumberAxis xAxis = new NumberAxis();
    final NumberAxis yAxis = new NumberAxis();

    Group extraNodes = new Group();

    this.chart = new LineChart<Number, Number>(xAxis, yAxis) {
        @Override
        public void layoutPlotChildren() {
            super.layoutPlotChildren();
            Rectangle r1 = new Rectangle(0, 0, 50, 100);
            r1.setFill(Color.GREEN);
            Rectangle r2 = new Rectangle(70, 0, 30, 20);
            r2.setFill(Color.AQUAMARINE);
            extraNodes.getChildren().setAll(r1, r2);
            extraNodes.getTransforms().setAll(chartDisplayTransform(xAxis, yAxis));
            // note nodes don't get removed from the plot children, and this method may be
            // called often:
            if (!getPlotChildren().contains(extraNodes)) {
                getPlotChildren().add(extraNodes);
            }
        }
    };
    this.chart.setAnimated(false);

    VBox vbox = new VBox(this.chart);

    Scene scene = new Scene(vbox, 400, 200);
    primaryStage.setScene(scene);
    primaryStage.setHeight(600);
    primaryStage.setWidth(400);
    primaryStage.show();
}

另见this related question

【讨论】:

  • 有趣的方法。我需要看看使用多种形状可以变得多么复杂。我希望有一种方法可以将它设置在轴上。
  • @BAR 您可以对所有形状应用相同的变换;应该很容易。您并不真的想转换坐标轴,因为(如您所见),这意味着它们将显示在“显示空间”而不是逻辑“图表空间”中。
  • 似乎不能很好地与 ValueAxis::getDisplayPosition 配合使用。更新的问题。
  • 哦,我明白了,矩形现在是图表坐标而不是屏幕坐标。但是现在 ValueAxis::getDisplayPosition 的反面是什么。
  • @BAR 反函数是getValueForDisplay()(这对于处理用户事件很有用;例如,通过鼠标点击获取图表坐标)。
猜你喜欢
  • 2021-03-23
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 2020-12-13
  • 2014-10-15
  • 2020-03-24
  • 2020-08-25
  • 1970-01-01
相关资源
最近更新 更多