<commit_before>/*
 * Copyright 2014 CIRDLES.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cirdles.topsoil.chart;

import static java.lang.Math.*;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.shape.Rectangle;
import javafx.scene.chart.XYChart;
import org.cirdles.topsoil.Tools;

/**
 * <p>
 * A <code>XYChart</code>, supporting to be moved by drag and drop, in which only numbers are accepted.</p>
 * <p>
 * In addition to supporting drag and drop, it also support zoom via right clicking and scrolling.
 *
 * @author John
 * @see XYChart
 */
public abstract class NumberChart extends XYChart<Number, Number> {

    public final NumberAxis xAxis;
    public final NumberAxis yAxis;
    private final Rectangle dragSelect;

    private final BooleanProperty lockToQ1 = new SimpleBooleanProperty(true);

    public NumberChart() {
        super(new NumberAxis(), new NumberAxis());
        setData(FXCollections.<Series<Number, Number>>observableArrayList());

        xAxis = (NumberAxis) getXAxis();
        xAxis.setForceZeroInRange(false);
        xAxis.setTickLabelFormatter(Tools.DYNAMIC_STRING_CONVERTER);

        yAxis = (NumberAxis) getYAxis();
        yAxis.setForceZeroInRange(false);
        yAxis.setTickLabelFormatter(Tools.DYNAMIC_STRING_CONVERTER);

        dragSelect = new Rectangle();
        dragSelect.getStyleClass().add("drag-select");
        dragSelect.setVisible(false);
        getChartChildren().add(dragSelect);

        final DoubleProperty mousePressedX = new SimpleDoubleProperty();
        final DoubleProperty mousePressedY = new SimpleDoubleProperty();
        final DoubleProperty mouseDraggedX = new SimpleDoubleProperty();
        final DoubleProperty mouseDraggedY = new SimpleDoubleProperty();

        setOnKeyReleased((KeyEvent keyEvent) -> {
            System.out.println("Hello!");
        });

        setOnKeyTyped((KeyEvent keyEvent) -> {
            System.out.printf("'%s'\n", keyEvent.getCharacter());
            if (keyEvent.getCharacter().equals(" ")) {
                xAxis.setAutoRanging(true);
                yAxis.setAutoRanging(true);

                updateAxisRange();
            }
        });

        setOnMouseDragged((MouseEvent mouseEvent) -> {
            if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
                shiftPlotWindowConstraint((mouseDraggedX.get() - mouseEvent.getX()) / xAxis.getWidth() * getXRange(),
                                          -(mouseDraggedY.get() - mouseEvent.getY()) / yAxis.getHeight() * getYRange());

                mouseDraggedX.set(mouseEvent.getX());
                mouseDraggedY.set(mouseEvent.getY());
            } else if (mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
                double mouseX = mouseEvent.getX();
                double mouseY = mouseEvent.getY();

                double minX = min(mouseX, mousePressedX.get());
                double maxX = max(mouseX, mousePressedX.get());
                double minY = min(mouseY, mousePressedY.get());
                double maxY = max(mouseY, mousePressedY.get());

                dragSelect.setX(minX);
                dragSelect.setY(minY);
                dragSelect.setWidth(maxX - minX);
                dragSelect.setHeight(maxY - minY);

                dragSelect.setVisible(true);
            }
        });

        setOnMousePressed((MouseEvent mouseEvent) -> {
            mousePressedX.set(mouseEvent.getX());
            mousePressedY.set(mouseEvent.getY());

            if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
                ((Node) mouseEvent.getSource()).setCursor(Cursor.HAND);

                mouseDraggedX.set(mouseEvent.getX());
                mouseDraggedY.set(mouseEvent.getY());
            }
        });

        setOnMouseReleased((MouseEvent mouseEvent) -> {
            if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
                ((Node) mouseEvent.getSource()).setCursor(Cursor.DEFAULT);
            } else if (mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
                dragSelect.setVisible(false);

                double minXValue = xAxis.getValueForDisplay(dragSelect.getX() - xAxis.getLayoutX()).doubleValue();
                double maxXValue = xAxis.getValueForDisplay(
                        dragSelect.getX() + dragSelect.getWidth() - xAxis.getLayoutX()).doubleValue();
                double minYValue = yAxis.getValueForDisplay(
                        dragSelect.getY() + dragSelect.getHeight() - yAxis.getLayoutY()).doubleValue();
                double maxYValue = yAxis.getValueForDisplay(dragSelect.getY() - yAxis.getLayoutY()).doubleValue();

                setPlotWindow(minXValue, maxXValue, minYValue, maxYValue);
            }
        });

        setOnScroll((ScrollEvent scrollEvent) -> {
            double zoomX = xAxis.getValueForDisplay(scrollEvent.getX() - xAxis.getLayoutX()).doubleValue();
            double zoomY = yAxis.getValueForDisplay(scrollEvent.getY() - yAxis.getLayoutY()).doubleValue();

            shiftPlotWindowFree(-zoomX, -zoomY);
            scalePlotWindow(1 - scrollEvent.getDeltaY() / 400);
            shiftPlotWindowFree(zoomX, zoomY);
<commit_msg>The drag window created when right clicking is has to have a minimum size to be effective Fixes #49<commit_after>/*
 * Copyright 2014 CIRDLES.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cirdles.topsoil.chart;

import static java.lang.Math.*;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.shape.Rectangle;
import javafx.scene.chart.XYChart;
import org.cirdles.topsoil.Tools;

/**
 * <p>
 * A <code>XYChart</code>, supporting to be moved by drag and drop, in which only numbers are accepted.</p>
 * <p>
 * In addition to supporting drag and drop, it also support zoom via right clicking and scrolling.
 *
 * @author John
 * @see XYChart
 */
public abstract class NumberChart extends XYChart<Number, Number> {

    public final NumberAxis xAxis;
    public final NumberAxis yAxis;
    private final Rectangle dragSelect;

    private static final int MINSIDE_DRAGWINDOW = 3;

    private final BooleanProperty lockToQ1 = new SimpleBooleanProperty(true);

    public NumberChart() {
        super(new NumberAxis(), new NumberAxis());
        setData(FXCollections.<Series<Number, Number>>observableArrayList());

        xAxis = (NumberAxis) getXAxis();
        xAxis.setForceZeroInRange(false);
        xAxis.setTickLabelFormatter(Tools.DYNAMIC_STRING_CONVERTER);

        yAxis = (NumberAxis) getYAxis();
        yAxis.setForceZeroInRange(false);
        yAxis.setTickLabelFormatter(Tools.DYNAMIC_STRING_CONVERTER);

        dragSelect = new Rectangle();
        dragSelect.getStyleClass().add("drag-select");
        dragSelect.setVisible(false);
        getChartChildren().add(dragSelect);

        final DoubleProperty mousePressedX = new SimpleDoubleProperty();
        final DoubleProperty mousePressedY = new SimpleDoubleProperty();
        final DoubleProperty mouseDraggedX = new SimpleDoubleProperty();
        final DoubleProperty mouseDraggedY = new SimpleDoubleProperty();

        setOnKeyReleased((KeyEvent keyEvent) -> {
            System.out.println("Hello!");
        });

        setOnKeyTyped((KeyEvent keyEvent) -> {
            System.out.printf("'%s'\n", keyEvent.getCharacter());
            if (keyEvent.getCharacter().equals(" ")) {
                xAxis.setAutoRanging(true);
                yAxis.setAutoRanging(true);

                updateAxisRange();
            }
        });

        setOnMouseDragged((MouseEvent mouseEvent) -> {
            if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
                shiftPlotWindowConstraint((mouseDraggedX.get() - mouseEvent.getX()) / xAxis.getWidth() * getXRange(),
                                          -(mouseDraggedY.get() - mouseEvent.getY()) / yAxis.getHeight() * getYRange());

                mouseDraggedX.set(mouseEvent.getX());
                mouseDraggedY.set(mouseEvent.getY());
            } else if (mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
                double mouseX = mouseEvent.getX();
                double mouseY = mouseEvent.getY();

                double minX = min(mouseX, mousePressedX.get());
                double maxX = max(mouseX, mousePressedX.get());
                double minY = min(mouseY, mousePressedY.get());
                double maxY = max(mouseY, mousePressedY.get());

                dragSelect.setX(minX);
                dragSelect.setY(minY);
                dragSelect.setWidth(maxX - minX);
                dragSelect.setHeight(maxY - minY);

                dragSelect.setVisible(true);
            }
        });

        setOnMousePressed((MouseEvent mouseEvent) -> {
            mousePressedX.set(mouseEvent.getX());
            mousePressedY.set(mouseEvent.getY());

            if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
                ((Node) mouseEvent.getSource()).setCursor(Cursor.HAND);

                mouseDraggedX.set(mouseEvent.getX());
                mouseDraggedY.set(mouseEvent.getY());
            }
        });

        setOnMouseReleased((MouseEvent mouseEvent) -> {
            if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
                ((Node) mouseEvent.getSource()).setCursor(Cursor.DEFAULT);
            } else if (mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
                dragSelect.setVisible(false);

                double minXValue_ondisplay = dragSelect.getX() - xAxis.getLayoutX();
                double maxXValue_ondisplay = dragSelect.getX() + dragSelect.getWidth() - xAxis.getLayoutX();
                double minYValue_ondisplay = dragSelect.getY() + dragSelect.getHeight() - yAxis.getLayoutY();
                double maxYValue_ondisplay = dragSelect.getY() - yAxis.getLayoutY();

                double minXValue = xAxis.getValueForDisplay(minXValue_ondisplay).doubleValue();
                double maxXValue = xAxis.getValueForDisplay(maxXValue_ondisplay).doubleValue();
                double minYValue = yAxis.getValueForDisplay(minYValue_ondisplay).doubleValue();
                double maxYValue = yAxis.getValueForDisplay(maxYValue_ondisplay).doubleValue();

                if (abs(maxXValue_ondisplay - minXValue_ondisplay) > MINSIDE_DRAGWINDOW && abs(maxYValue_ondisplay - minYValue_ondisplay) > MINSIDE_DRAGWINDOW) {
                    setPlotWindow(minXValue, maxXValue, minYValue, maxYValue);
                }
                
                dragSelect.setX(0);
                dragSelect.setY(0);
                dragSelect.setWidth(0);
                dragSelect.setHeight(0);
            }
        });

        setOnScroll((ScrollEvent scrollEvent) -> {
            double zoomX = xAxis.getValueForDisplay(scrollEvent.getX() - xAxis.getLayoutX()).doubleValue();
            double zoomY = yAxis.getValueForDisplay(scrollEvent.getY() - yAxis.getLayoutY()).doubleValue();

            shiftPlotWindowFree(-zoomX, -zoomY);
            scalePlotWindow(1 - scrollEvent.getDeltaY() / 400);
            shiftPlotWindowFree(zoomX, zoomY);

{'fraction_non_alphanumeric': 0.10860796103204201, 'fraction_numerical': 0.002511606667174062, 'mean_word_length': 2.7087214225232854, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4261115', 'n_tokens_mistral': 3682, 'n_tokens_neox': 3525, 'n_words': 735}