<commit_before>package tugraz.ivis.parcoord.chart;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;

import java.util.ArrayList;

import org.controlsfx.control.RangeSlider;

// TODO: implement basic graph here
// TODO: this is basically only a bit of "playing around" for now
public class ParallelCoordinatesChart extends HighDimensionalChart {
    private ArrayList<Object>[] data;// => list of columns
    private ArrayList<String> axisLabels;
    private ArrayList<ParallelCoordinatesAxis> axes = new ArrayList<ParallelCoordinatesAxis>();
    private double top;
    private double left;
    private boolean showLabels = true;

    /**
     * Property holding the height of the chartContent which is updated with each layoutChartChildren call.
     * Represents inner values (without padding, titleLabel, etc.)
     */
    private DoubleProperty innerHeightProperty = new SimpleDoubleProperty();
    /**
     * Property holding the width of the chartContent which is updated with each layoutChartChildren call.
     * Represents inner values (without padding, etc.)
     */
    private DoubleProperty innerWidthProperty = new SimpleDoubleProperty();

    /**
     * Default constructor needed for FXML
     */
    public ParallelCoordinatesChart() {
    }

    public void updateData(ArrayList<Object>[] data, ArrayList<String> axisLabels) {
        setData(data, axisLabels);
        bindData();
    }

    public void setData(ArrayList<Object>[] data, ArrayList<String> axisLabels) {
        this.data = data;
        this.axisLabels = axisLabels;
        System.out.println("imported records: " + data[0].size() + " with columns:" + data.length);
    }

    /**
     * Binds the set data to the graph
     */
    public void bindData() {
        //drawBorder();

        bindAxes();
        bindRecords();
    }

    public void forceRedraw() {
        getChartChildren().clear();
        bindData();
    }

    // TODO: not needed for now
    private void drawBorder() {
        Path path = new Path();

        MoveTo moveTo = new MoveTo();
        moveTo.setX(0);
        moveTo.setY(0);
        path.getElements().add(moveTo);

        LineTo lineTo = new LineTo();
        lineTo.setX(innerWidthProperty.doubleValue());
        lineTo.setY(0);
        path.getElements().add(lineTo);

        lineTo = new LineTo();
        lineTo.setX(innerWidthProperty.doubleValue());
        lineTo.setY(innerHeightProperty.doubleValue());
        path.getElements().add(lineTo);

        lineTo = new LineTo();
        lineTo.setX(0);
        lineTo.setY(innerHeightProperty.doubleValue());
        path.getElements().add(lineTo);

        lineTo = new LineTo();
        lineTo.setX(0);
        lineTo.setY(0);
        path.getElements().add(lineTo);

        getChartChildren().add(path);
    }

    private void bindAxes() {
        int numAxes = data.length - 1; // TODO: remove -1 later, for now because of Categories in datamodel

        // configurable
        double spaceBetweenTicks = 50;
        double labelMinWidth = 500;
        double labelYOffset = 50;

        for (int iAxis = 0; iAxis < numAxes; iAxis++) {

            String label = null;
            if (axisLabels.size() - 1 >= iAxis) {
                label = axisLabels.get(iAxis);
            }

            DoubleBinding trueAxisSeparation = getAxisSeparationBinding().multiply(iAxis + 1);

            // TODO get real bounds
            double upperBound = 10.0;
            double lowerBound = 0.0;
            double delta = Math.abs(upperBound - lowerBound);
            
            // axis
            NumberAxis numberAxis = new NumberAxis(null, lowerBound, upperBound, 1.0);
            numberAxis.setSide(Side.LEFT);
            numberAxis.setMinorTickVisible(false);
            numberAxis.setAnimated(false);
            numberAxis.translateXProperty().bind(trueAxisSeparation);
            numberAxis.tickUnitProperty().bind(innerHeightProperty.divide(innerHeightProperty).divide(innerHeightProperty).multiply(spaceBetweenTicks).multiply(delta));

            // label
            Label labelNode = new Label(label);
            labelNode.setMinWidth(labelMinWidth);
            labelNode.setAlignment(Pos.CENTER);
            HBox box = new HBox(labelNode);
            box.translateXProperty().bind(trueAxisSeparation.subtract(labelMinWidth / 2));
            box.translateYProperty().bind(innerHeightProperty.subtract(labelYOffset));
            
            // filters
            RangeSlider vSlider = new RangeSlider(lowerBound, upperBound, lowerBound, upperBound);
            vSlider.setOrientation(Orientation.VERTICAL);
            vSlider.setShowTickLabels(false);
            vSlider.setShowTickMarks(false);
            vSlider.translateXProperty().bind(trueAxisSeparation);
            
        	getChartChildren().add(numberAxis);
        	getChartChildren().add(box);
        	getChartChildren().add(vSlider);
        	
        	// have to style after adding it (CSS wouldn't be accessible otherwise)
            vSlider.applyCss();
            vSlider.lookup(".range-slider .track").setStyle("-fx-opacity: 0;");
            vSlider.lookup(".range-slider .range-bar").setStyle("-fx-opacity: 0.15;");
        	<commit_msg>Added reordering of elements to push axis filters to the front<commit_after>package tugraz.ivis.parcoord.chart;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;

import java.util.ArrayList;

import org.controlsfx.control.RangeSlider;

// TODO: implement basic graph here
// TODO: this is basically only a bit of "playing around" for now
public class ParallelCoordinatesChart extends HighDimensionalChart {
    private ArrayList<Object>[] data;// => list of columns
    private ArrayList<String> axisLabels;
    private ArrayList<ParallelCoordinatesAxis> axes = new ArrayList<ParallelCoordinatesAxis>();
    private double top;
    private double left;
    private boolean showLabels = true;

    /**
     * Property holding the height of the chartContent which is updated with each layoutChartChildren call.
     * Represents inner values (without padding, titleLabel, etc.)
     */
    private DoubleProperty innerHeightProperty = new SimpleDoubleProperty();
    /**
     * Property holding the width of the chartContent which is updated with each layoutChartChildren call.
     * Represents inner values (without padding, etc.)
     */
    private DoubleProperty innerWidthProperty = new SimpleDoubleProperty();

    /**
     * Default constructor needed for FXML
     */
    public ParallelCoordinatesChart() {
    }

    public void updateData(ArrayList<Object>[] data, ArrayList<String> axisLabels) {
        setData(data, axisLabels);
        bindData();
    }

    public void setData(ArrayList<Object>[] data, ArrayList<String> axisLabels) {
        this.data = data;
        this.axisLabels = axisLabels;
        System.out.println("imported records: " + data[0].size() + " with columns:" + data.length);
    }

    /**
     * Binds the set data to the graph
     */
    public void bindData() {
        //drawBorder();

        bindAxes();
        bindRecords();
        reorder();
    }

    public void forceRedraw() {
        getChartChildren().clear();
        bindData();
    }

    // TODO: not needed for now
    private void drawBorder() {
        Path path = new Path();

        MoveTo moveTo = new MoveTo();
        moveTo.setX(0);
        moveTo.setY(0);
        path.getElements().add(moveTo);

        LineTo lineTo = new LineTo();
        lineTo.setX(innerWidthProperty.doubleValue());
        lineTo.setY(0);
        path.getElements().add(lineTo);

        lineTo = new LineTo();
        lineTo.setX(innerWidthProperty.doubleValue());
        lineTo.setY(innerHeightProperty.doubleValue());
        path.getElements().add(lineTo);

        lineTo = new LineTo();
        lineTo.setX(0);
        lineTo.setY(innerHeightProperty.doubleValue());
        path.getElements().add(lineTo);

        lineTo = new LineTo();
        lineTo.setX(0);
        lineTo.setY(0);
        path.getElements().add(lineTo);

        getChartChildren().add(path);
    }
    
    /**
     * Reorders elements in the z dimensions to push certain elements to the front.
     */
    private void reorder() {
    	for(ParallelCoordinatesAxis axis : axes) {
    		axis.getFilterSlider().toFront();
    	}
    }

    /**
     * Creates and binds axes, axes labels and filters.
     */
    private void bindAxes() {
        int numAxes = data.length - 1; // TODO: remove -1 later, for now because of Categories in datamodel

        // configurable
        double spaceBetweenTicks = 50;
        double labelMinWidth = 500;
        double labelYOffset = 50;

        for (int iAxis = 0; iAxis < numAxes; iAxis++) {

            String label = null;
            if (axisLabels.size() - 1 >= iAxis) {
                label = axisLabels.get(iAxis);
            }

            DoubleBinding trueAxisSeparation = getAxisSeparationBinding().multiply(iAxis + 1);

            // TODO get real bounds
            double upperBound = 10.0;
            double lowerBound = 0.0;
            double delta = Math.abs(upperBound - lowerBound);
            
            // axis
            NumberAxis numberAxis = new NumberAxis(null, lowerBound, upperBound, 1.0);
            numberAxis.setSide(Side.LEFT);
            numberAxis.setMinorTickVisible(false);
            numberAxis.setAnimated(false);
            numberAxis.translateXProperty().bind(trueAxisSeparation);
            numberAxis.tickUnitProperty().bind(innerHeightProperty.divide(innerHeightProperty).divide(innerHeightProperty).multiply(spaceBetweenTicks).multiply(delta));

            // label
            Label labelNode = new Label(label);
            labelNode.setMinWidth(labelMinWidth);
            labelNode.setAlignment(Pos.CENTER);
            HBox box = new HBox(labelNode);
            box.translateXProperty().bind(trueAxisSeparation.subtract(labelMinWidth / 2));
            box.translateYProperty().bind(innerHeightProperty.subtract(labelYOffset));
            
            // filters
            RangeSlider vSlider = new RangeSlider(lowerBound, upperBound, lowerBound, upperBound);
            vSlider.setOrientation(Orientation.VERTICAL);
            vSlider.setShowTickLabels(false);
            vSlider.setShowTickMarks(false);
            vSlider.translateXProperty().bind(trueAxisSeparation);
            
            // add to chart
        	getChartChildren().add(numberAxis);
        	getChartChildren().add(box);
        	getChartChildren().add(vSlider);
        	
        	// have to style after adding it (CSS wouldn't be accessible otherwise)
            vSlider.applyCss();
            vSlider.lookup(".range-slider .track").setStyle("-fx-opacity: 0;");
            vSlider.lookup(".range-slider .range-bar").setStyle("-fx-opacity: 0.15;");
        	
{'fraction_non_alphanumeric': 0.10593116914815719, 'fraction_numerical': 0.0050443413880074854, 'mean_word_length': 2.6736401673640167, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3631437', 'n_tokens_mistral': 3415, 'n_tokens_neox': 3337, 'n_words': 766}