LineChart FX - 删除实线

LineChart FX - Delete solid line

我对图表 LineChart JavaFX 有一个奇怪的问题。

我有这张图: 在 X 轴上形成 "jump" 的点(如我得分的两个红点所示),因此 JavaFX 在这两点之间绘制了一条线。如何删除每个 "jump"?

之间的那条线

我post代码:

public class ControllerIndividua {

    public static void plotIndividuaFull(String path, Stage stage, String name) {
        final NumberAxis xAxisIntensity = new NumberAxis(); //Dichiarazione asseX
        final NumberAxis yAxisIntensity = new NumberAxis();//Dichiarazione asseY

        DetectionS1.countS1();

        //Dichiarazione del tipo di grafico
        final LineChart<Number, Number> lineChartIntensity = new LineChart<Number, Number>(xAxisIntensity,yAxisIntensity);

        ArrayList<Double> extractedData; //Lista dei valori dello dell' intensità
        ArrayList<Double> extractedTime; //Lista del tempo
        ArrayList<Double> extractedS1; //Lista del tempo
        ArrayList<Double> extractedS1Time; //Lista del tempo

        //Gestione e settaggio del grafico
        lineChartIntensity.getData().clear();

        try {
            //Popolamento delle liste
            extractedTime = IntensityExtractor.pointsTime();
            extractedData = IntensityExtractor.pointsIntensity();
            extractedS1 = DetectionS1.S1pitch();
            extractedS1Time = DetectionS1.pointsS1Time();
            XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
            XYChart.Series<Number, Number> seriesS1 = new XYChart.Series<Number, Number>(); //Creazione seconda serie
            series.setName("Intensità di:\t" + name.toUpperCase());

            for (int j = 0; j < extractedS1.size(); j++) {
                seriesS1.getData().add(new XYChart.Data<Number, Number>(extractedS1Time.get(j), extractedS1.get(j)));
                lineChartIntensity.getStyleClass().add("CSSintensity");
            }

            //Creazione finestra e stampa del grafico
            Scene scene = new Scene(lineChartIntensity, 1000, 600);
            lineChartIntensity.getData().addAll(series,seriesS1);
            scene.getStylesheets().add("application/application.css");
            stage.setScene(scene);
            stage.show();
        } catch (java.lang.Exception e) {
            e.printStackTrace();
        }
    }
}

有人也知道我该怎么做吗?

提前谢谢大家。

我查过了。不可能只更改此行的一部分。因为这是一条很长的 Path,您不能更改 Path 的一个元素。

我认为唯一的方法是将每个 "jump" 添加到不同的系列中。

这是一个有公认答案的老问题,但我遇到了它并且很好奇。我想知道是否可以在 LineChart 中放置一个缺口(至少无需创建自定义图表实现)。原来是有的。该解决方案有点老套和脆弱。它涉及获取 Path by using XYChart.Series.getNode() and manipulating the list of PathElements。下面的代码给出了一个例子:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());
        chart.getXAxis().setLabel("X");
        chart.getYAxis().setLabel("Y");
        chart.setLegendVisible(false);
        chart.getData().add(new XYChart.Series<>());

        for (int x = 0; x <= 10; x++) {
            chart.getData().get(0).getData().add(new XYChart.Data<>(x, Math.pow(x, 2)));
        }

        /*
         * Had to wrap the call in a Platform.runLater otherwise the Path was
         * redrawn after the modifications are made.
         */
        primaryStage.setOnShown(we -> Platform.runLater(() -> {
            Path path = (Path) chart.getData().get(0).getNode();
            LineTo lineTo = (LineTo) path.getElements().get(8);
            path.getElements().set(8, new MoveTo(lineTo.getX(), lineTo.getY()));
        }));

        primaryStage.setScene(new Scene(new StackPane(chart), 500, 300));
        primaryStage.setTitle("LineChart Gap");
        primaryStage.show();
    }

}

此代码产生以下结果:

这是可能的,因为 PathElementObservableList 似乎是 MoveTo followed by a bunch of LineTo。我只是选择了一个 LineTo 并将其替换为 MoveTo 到相同的坐标。然而,我还没有完全弄清楚 LineTo 的哪个索引与 XYChart.Data 匹配,并随机选择了 8 作为示例。

这个解决方案有几个问题。第一个也是显而易见的是,这依赖于 LineChart 的内部实现。第二个是真正脆弱性的来源。对数据的任何更改,无论是轴的值范围、图表的宽度或高度,还是几乎 任何导致图表重绘的东西 都会导致重新计算 Path并重新绘制。这意味着如果您使用此解决方案,则每次 图表重新绘制时都必须重新应用修改

我制作了一个 GapLineChart,当 Slaw 看到 Double.NaN 时,它会自动设置一个 MoveTo,就像 Slaw 指向它一样。你可以在这里找到它 https://gist.github.com/sirolf2009/ae8a7897b57dcf902b4ed747b05641f9。查看第一条评论以获取示例

我需要调整 LineChart 以绘制线段,因此每对数据点创建一个线段。通过扩展 LineChart 并覆盖 layoutPlotChildren 函数非常简单,尽管我不得不删除一个动画 y-scaling 变量,因为它在 super 中是 private class,但我没有为图表制作动画,所以没问题。无论如何,这里是 Kotlin 版本(您可以轻松适应 Java,或者只需复制 Java LineChart layoutPlotChildren 代码并适应)。很容易适应您的情况,只需在最后更改 for 循环的内容即可。

import javafx.scene.chart.Axis
import javafx.scene.chart.LineChart
import javafx.scene.shape.LineTo
import javafx.scene.shape.MoveTo
import javafx.scene.shape.Path
import java.util.*
import kotlin.collections.ArrayList

class GapLineChart<X, Y>(xAxis: Axis<X>?, yAxis: Axis<Y>?) : LineChart<X, Y>(xAxis, yAxis) {

    public override fun layoutPlotChildren() {

        val constructedPath = ArrayList<LineTo>(data.size)

        for (seriesIndex in 0 until data.size) {

            val series = data[seriesIndex]

            if (series.node is Path) {

                val seriesLine = (series.node as Path).elements
                val it = getDisplayedDataIterator(series)

                seriesLine.clear()
                constructedPath.clear()

                var parity = true

                while (it.hasNext()) {

                    val item = it.next()
                    val x = xAxis.getDisplayPosition(item.xValue)
                    val y = yAxis.getDisplayPosition(yAxis.toRealValue(yAxis.toNumericValue(item.yValue)))

                    if (java.lang.Double.isNaN(x) || java.lang.Double.isNaN(y)) {
                        continue
                    }

                    if(parity)
                        constructedPath.add(LineTo(x, y))

                    val symbol = item.node

                    if (symbol != null) {

                        val w = symbol.prefWidth(-1.0)
                        val h = symbol.prefHeight(-1.0)

                        symbol.resizeRelocate(x - w / 2, y - h / 2, w, h)
                    }
                }
                when (axisSortingPolicy) {

                    SortingPolicy.X_AXIS -> constructedPath.sortWith(Comparator { e1, e2 -> java.lang.Double.compare(e1.x, e2.x) })
                    SortingPolicy.Y_AXIS -> constructedPath.sortWith(Comparator { e1, e2 -> java.lang.Double.compare(e1.y, e2.y) })
                }

                if (!constructedPath.isEmpty()) {

                    for (i in 0 until constructedPath.size-1 step 2) {

                        seriesLine.add(MoveTo(constructedPath[i].x, constructedPath[i].y))
                        seriesLine.add(LineTo(constructedPath[i+1].x, constructedPath[i+1].y))
                    }
                }
            }
        }
    }
}