Есть ли способ отключить 2 точки подряд в JavaFX LineChart?

У меня есть четыре серии на LineChart. Каждая серия состоит из некоторого количества графиков, разбитых по времени. По умолчанию LineChart связывает эти графики. Это выглядит некрасиво и не имеет смысла в контексте, поэтому я хочу разделить их, но сохранить цвет и легенду. Другими словами, я хочу удалить связь между двумя конкретными точками. Есть ли способ сделать это, не прибегая к добавлению новых рядов на диаграмму (графики логически связаны, а добавление новых рядов может сбить пользователя с толку и загромождать диаграмму)?

for(int j = 0; j < 4; j++) {
    XYChart.Series<Float, Float> series = new XYChart.Series<>();
    series.setName("Канал " + (j + 1));
    fillWithData(series);
    chart.getData().add(series);
}

введите описание изображения здесь

1 ответ

Решение

Да, ты можешь

Как указано в https://docs.oracle.com/javase/8/javafx/api/javafx/scene/chart/XYChart.Series.html узел serie используется для хранения строки. узел - это путь, который вы можете изменить.

посмотрите на это маленькое приложение:

    package application;

import java.util.Arrays;
import java.util.List;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.shape.Circle;
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) {

            NumberAxis xAxis = new NumberAxis();
            NumberAxis yAxis = new NumberAxis();
            MyLineChart chart = new MyLineChart(xAxis, yAxis, Arrays.asList(5));


            XYChart.Series<Number, Number> serie = new Series<>();

            serie.getData().add(new Data<Number, Number>(0, 5));
            serie.getData().add(new Data<Number, Number>(1, 5));
            serie.getData().add(new Data<Number, Number>(2, 5));
            serie.getData().add(new Data<Number, Number>(3, 5));
            serie.getData().add(new Data<Number, Number>(4, 5));
            serie.getData().add(new Data<Number, Number>(5, -5));
            serie.getData().add(new Data<Number, Number>(6, -5));
            serie.getData().add(new Data<Number, Number>(7, -5));

            chart.getData().add(serie);

            Path p = (Path) serie.getNode();        

            Scene scene = new Scene(chart,400,400);
            primaryStage.setScene(scene);
            primaryStage.show();



    }

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

    private static class MyLineChart extends LineChart<Number,Number>{


        private List<Integer> breakpointIndex;

        public MyLineChart(Axis<Number> xAxis, Axis<Number> yAxis, List<Integer> breakpointIndex) {
            super(xAxis, yAxis);
            this.breakpointIndex = breakpointIndex;
        }

        @Override
        protected void layoutPlotChildren() {
            super.layoutPlotChildren();

            Path p = (Path) getData().get(0).getNode();

            breakpointIndex.forEach(i ->{

                Data<Number, Number> discontinuousPoint = getData().get(0).getData().get(i+1);
                p.getElements().add(i+1, new MoveTo(getXAxis().getDisplayPosition( discontinuousPoint.getXValue()), getYAxis().getDisplayPosition(discontinuousPoint.getYValue())));

            });

            System.out.println("\nnew Path :");
            p.getElements().forEach(e -> System.out.println("p : " + e));


            //getPlotChildren().add(new Circle(50));


        }

    }
}

Вы должны переопределить метод layoutplotchildren, который отвечает за рисование диаграммы (интересно, если возможно, увидеть код метода источника).

и тогда вы можете нарисовать, что вы хотите здесь.

В моем небольшом приложении я просто изменяю уже созданный узел, но вы можете опустить супер-вызов и нарисовать то, что вы хотите.

Посмотрите на документ, у вас есть метод для доступа к элементам chartelement, таким как getPlotChildren(), getchartchildren().

и вы можете получить доступ к серии, добавленной в таблицу с помощью getdata()

Видимо в JavaFX 11 внутренняя логика для заполнения базовых Elements список в Path изменился Теперь, если я увеличу масштаб графика (другими словами, изменю нижние / верхние границы на оси), он будет заполнен только видимыми элементами, поэтому индексы для Data а также PathElement больше не совпадают (как и размеры соответствующих списков). Я полагаю, это какая-то оптимизация.

Я был вынужден переписать код следующим хакерским способом:

@Override
protected void layoutPlotChildren() {
    super.layoutPlotChildren();
    for(Series<Number, Number> data : getData()) {
        ObservableList<PathElement> elements = ((Path)data.getNode()).getElements();
        for(int i = 0; i < elements.size() - 1; i++) {
            PathElement oldElement = elements.get(i);
            PathElement newElement = elements.get(i + 1);
            if (oldElement instanceof LineTo && newElement instanceof LineTo) {
                double oldDisplayX = ((LineTo) oldElement).getX();
                double newDisplayX = ((LineTo) newElement).getX();
                if(getXAxis().getValueForDisplay(newDisplayX).doubleValue() -
                       getXAxis().getValueForDisplay(oldDisplayX).doubleValue() > 10)
                    elements.set(i, new MoveTo(newDisplayX, ((LineTo) newElement).getY()));
            }
        }
    }
}

Теперь (для меня) нет необходимости вести отдельный список индексов точек останова, и нет необходимости добавлять их вручную, поскольку я вставляю точку останова только для одного условия: пробел между значениями X больше 10. Я игнорирую Data список полностью и пройти Elements список, преобразовывая координаты дисплея в значения X, где это необходимо. Очевидно, что для большого количества элементов это приведет к дополнительным издержкам, так как это вызывается каждый раз, когда изменяется размер окна или когда добавляется новый элемент, но для моего примера использования диаграмма состоит из 4 серий по 6000-15000 точек и для этого размера. расчет занимает ~1 мс, что незначительно. Я также мог бы, вероятно, удалить один из вызовов getValueForDisplay, сначала посчитав разницу в пикселях, не уверен.

Другие вопросы по тегам