Package eu.hansolo.enzo.charts.skin

Source Code of eu.hansolo.enzo.charts.skin.SimpleLineChartSkin

/*
* Copyright (c) 2013 by Gerrit Grunwald
*
* 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 eu.hansolo.enzo.charts.skin;

import eu.hansolo.enzo.charts.SimpleLineChart;
import eu.hansolo.enzo.common.Section;
import javafx.collections.ListChangeListener;
import javafx.geometry.VPos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Skin;
import javafx.scene.control.SkinBase;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.TextAlignment;


/**
* Created by
* User: hansolo
* Date: 19.08.13
* Time: 15:44
*/
public class SimpleLineChartSkin extends SkinBase<SimpleLineChart> implements Skin<SimpleLineChart> {
    private static final double      PREFERRED_WIDTH  = 200;
    private static final double      PREFERRED_HEIGHT = 100;
    private static final double      MINIMUM_WIDTH    = 50;
    private static final double      MINIMUM_HEIGHT   = 50;
    private static final double      MAXIMUM_WIDTH    = 1024;
    private static final double      MAXIMUM_HEIGHT   = 1024;
    private boolean                  keepAspect;
    private double                   aspectRatio;
    private double                   size;
    private double                   width;
    private double                   height;
    private double                   widthFactor;
    private double                   heightFactor;
    private double                   sectionMinimum;
    private double                   sectionMaximum;
    private Pane                     pane;
    private Canvas                   canvasBkg;
    private GraphicsContext          ctxBkg;
    private Canvas                   canvasFg;
    private GraphicsContext          ctxFg;


    // ******************** Constructors **************************************
    public SimpleLineChartSkin(SimpleLineChart chart) {
        super(chart);
        keepAspect  = true;
        aspectRatio = PREFERRED_HEIGHT / PREFERRED_WIDTH;
        init();
        initGraphics();
        registerListeners();
    }


    // ******************** Initialization ************************************
    private void init() {
        if (Double.compare(getSkinnable().getPrefWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getPrefHeight(), 0.0) <= 0 ||
            Double.compare(getSkinnable().getWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getHeight(), 0.0) <= 0) {
            if (getSkinnable().getPrefWidth() > 0 && getSkinnable().getPrefHeight() > 0) {
                getSkinnable().setPrefSize(getSkinnable().getPrefWidth(), getSkinnable().getPrefHeight());
            } else {
                getSkinnable().setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT);
            }
        }

        if (Double.compare(getSkinnable().getMinWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getMinHeight(), 0.0) <= 0) {
            getSkinnable().setMinSize(MINIMUM_WIDTH, MINIMUM_HEIGHT);
        }

        if (Double.compare(getSkinnable().getMaxWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getMaxHeight(), 0.0) <= 0) {
            getSkinnable().setMaxSize(MAXIMUM_WIDTH, MAXIMUM_HEIGHT);
        }
    }

    private void initGraphics() {
        Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/opensans-semibold.ttf"), (0.06 * PREFERRED_HEIGHT)); // "OpenSans"

        canvasBkg = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxBkg    = canvasBkg.getGraphicsContext2D();

        canvasFg  = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxFg     = canvasFg.getGraphicsContext2D();

        pane      = new Pane();
        pane.getChildren().setAll(canvasBkg, canvasFg);

        getChildren().setAll(pane);
        resize();
        drawBackground();
        drawForeground();
    }

    private void registerListeners() {
        getSkinnable().widthProperty().addListener(observable -> handleControlPropertyChanged("RESIZE"));
        getSkinnable().heightProperty().addListener(observable -> handleControlPropertyChanged("RESIZE"));
        getSkinnable().sectionRangeVisibleProperty().addListener(observable -> handleControlPropertyChanged("REDRAW_BACKGROUND"));
        getSkinnable().unitProperty().addListener(observable -> handleControlPropertyChanged("REDRAW_BACKGROUND"));
        getSkinnable().getSections().addListener((ListChangeListener<Section>) change -> handleControlPropertyChanged("RESIZE"));
        getSkinnable().getSeries().getData().addListener((ListChangeListener) change -> handleControlPropertyChanged("REDRAW_FOREGROUND"));
        getSkinnable().fromProperty().addListener(observable -> handleControlPropertyChanged("REDRAW_FOREGROUND"));
        getSkinnable().toProperty().addListener(observable -> handleControlPropertyChanged("REDRAW_FOREGROUND"));
        getSkinnable().titleVisibleProperty().addListener(observable -> handleControlPropertyChanged("REDRAW_FOREGROUND"));
    }


    // ******************** Methods *******************************************
    protected void handleControlPropertyChanged(final String PROPERTY) {
        if ("RESIZE".equals(PROPERTY)) {
            resize();
            drawBackground();
            drawForeground();
        } else if ("REDRAW".equals(PROPERTY)) {
            drawBackground();
            drawForeground();
        } else if ("REDRAW_FOREGROUND".equals(PROPERTY)) {
            drawForeground();
        } else if ("REDRAW_BACKGROUND".equals(PROPERTY)) {
            drawBackground();
        }
    }

    @Override protected double computeMinWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMinWidth(Math.max(MINIMUM_HEIGHT, HEIGHT - TOP_INSET - BOTTOM_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }
    @Override protected double computeMinHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMinHeight(Math.max(MINIMUM_WIDTH, WIDTH - LEFT_INSET - RIGHT_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }

    @Override protected double computeMaxWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMaxWidth(Math.min(MAXIMUM_HEIGHT, HEIGHT - TOP_INSET - BOTTOM_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }
    @Override protected double computeMaxHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        return super.computeMaxHeight(Math.min(MAXIMUM_WIDTH, WIDTH - LEFT_INSET - RIGHT_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }

    @Override protected double computePrefWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        double prefHeight = PREFERRED_HEIGHT;
        if (HEIGHT != -1) {
            prefHeight = Math.max(0, HEIGHT - TOP_INSET - BOTTOM_INSET);
        }
        return super.computePrefWidth(prefHeight, TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }
    @Override protected double computePrefHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) {
        double prefWidth = PREFERRED_WIDTH;
        if (WIDTH != -1) {
            prefWidth = Math.max(0, WIDTH - LEFT_INSET - RIGHT_INSET);
        }
        return super.computePrefHeight(prefWidth, TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET);
    }


    // ******************** Private Methods ***********************************
    private void drawForeground() {
        ctxFg.clearRect(0, 0, width, height);
        ctxFg.setStroke(getSkinnable().getSeriesStroke());
        ctxFg.setLineCap(StrokeLineCap.ROUND);
        ctxFg.setLineJoin(StrokeLineJoin.ROUND);
        ctxFg.setLineWidth(0.025 * height);

        ctxFg.save();
        ctxFg.translate(0, sectionMinimum * heightFactor);

        widthFactor = width / (getSkinnable().getSeries().getData().size());
        int noOfDataPoints = getSkinnable().getSeries().getData().size();
        if (noOfDataPoints > 2) {
            for (int i = 0 ; i < noOfDataPoints - 1 ; i++) {
                XYChart.Data p1 = (XYChart.Data) getSkinnable().getSeries().getData().get(i);
                XYChart.Data p2 = (XYChart.Data) getSkinnable().getSeries().getData().get(i + 1);
                ctxFg.strokeLine(widthFactor / 2 + i * widthFactor, height - (Double) p1.getYValue() * heightFactor, widthFactor / 2 + (i + 1) * widthFactor, height - (Double) p2.getYValue() * heightFactor);
                drawBullet(ctxFg, widthFactor / 2 + i * widthFactor, height - (Double) p1.getYValue() * heightFactor, getSkinnable().getBulletFill());
            }
            drawBullet(ctxFg, widthFactor / 2 + (noOfDataPoints - 1) * widthFactor, height - (Double) (getSkinnable().getSeries().getData().get(noOfDataPoints - 1)).getYValue() * heightFactor, getSkinnable().getBulletFill());
        }
        ctxFg.save();
        ctxFg.applyEffect(new DropShadow(0.025 * height, 0, 0.025 * height, Color.rgb(0, 0, 0, 0.65)));
        ctxFg.restore();

        // draw from and to text
        ctxFg.setFill(Color.WHITE);
        ctxFg.setFont(Font.font("Open Sans", height * 0.1));
        ctxFg.setTextBaseline(VPos.BOTTOM);
        ctxFg.setTextAlign(TextAlignment.LEFT);
        ctxFg.fillText(getSkinnable().getFrom(), 2, height - 2);
        ctxFg.setTextAlign(TextAlignment.RIGHT);
        ctxFg.fillText(getSkinnable().getTo(), width - 2, height -2);

        // draw title text
        if (getSkinnable().isTitleVisible()) {
            ctxFg.setTextBaseline(VPos.TOP);
            ctxFg.setTextAlign(TextAlignment.CENTER);
            ctxFg.fillText(getSkinnable().getSeries().getName(), width * 0.5, 2);
        }

        ctxFg.restore();
    }

    private void drawBackground() {
        ctxBkg.clearRect(0, 0, width, height);
        sectionMinimum = Double.MAX_VALUE;
        sectionMaximum = Double.MIN_VALUE;
        double lowestSection  = Double.MAX_VALUE;
        for (Section section : getSkinnable().getSections()) {
            sectionMinimum = Math.min(sectionMinimum, section.getStart());
            sectionMaximum = Math.max(sectionMaximum, section.getStop());
            lowestSection  = Math.min(lowestSection, Math.abs(section.getStop() - section.getStart()));
        }
        ctxBkg.setStroke(Color.BLACK);
        ctxBkg.strokeRect(0, 0, width, height);

        heightFactor = height / (sectionMaximum - sectionMinimum);

        ctxBkg.save();
        ctxBkg.translate(0, sectionMinimum * heightFactor);

        ctxBkg.setFont(Font.font("Open Sans", FontWeight.NORMAL, (lowestSection * 0.8 * heightFactor)));
        for (int i = 0 ; i < getSkinnable().getSections().size() ; i++) {
            final Section SECTION = getSkinnable().getSections().get(i);
            ctxBkg.save();
            switch(i) {
                case 0: ctxBkg.setFill(getSkinnable().getSectionFill0()); break;
                case 1: ctxBkg.setFill(getSkinnable().getSectionFill1()); break;
                case 2: ctxBkg.setFill(getSkinnable().getSectionFill2()); break;
                case 3: ctxBkg.setFill(getSkinnable().getSectionFill3()); break;
                case 4: ctxBkg.setFill(getSkinnable().getSectionFill4()); break;
                case 5: ctxBkg.setFill(getSkinnable().getSectionFill5()); break;
                case 6: ctxBkg.setFill(getSkinnable().getSectionFill6()); break;
                case 7: ctxBkg.setFill(getSkinnable().getSectionFill7()); break;
                case 8: ctxBkg.setFill(getSkinnable().getSectionFill8()); break;
                case 9: ctxBkg.setFill(getSkinnable().getSectionFill9()); break;
            }
            ctxBkg.fillRect(0, height - SECTION.getStop() * heightFactor, width, Math.abs(SECTION.getStop() - SECTION.getStart()) * heightFactor);
            ctxBkg.restore();
        }
        if (getSkinnable().isSectionRangeVisible()) {
            for (int i = 0 ; i < getSkinnable().getSections().size() - 1 ; i++) {
                final Section SECTION = getSkinnable().getSections().get(i);
                ctxBkg.setFill(getSkinnable().getSeriesStroke());
                ctxBkg.setTextBaseline(VPos.CENTER);
                ctxBkg.fillText(SECTION.getStop() + getSkinnable().getUnit(), 0.02 * height, height - SECTION.getStop() * heightFactor);
            }
        }
        ctxBkg.restore();
    }

    private void drawBullet(final GraphicsContext CTX, final double X, final double Y, final Paint COLOR) {
        double iconSize = 0.04 * size;
        CTX.save();
        CTX.setLineWidth(0.0125 * height);
        CTX.setStroke(getSkinnable().getSeriesStroke());
        CTX.setFill(COLOR);
        CTX.strokeOval(X - iconSize * 0.5, Y - iconSize * 0.5, iconSize, iconSize);
        CTX.fillOval(X - iconSize * 0.5, Y - iconSize * 0.5, iconSize, iconSize);
        CTX.restore();
    }

    private void resize() {
        size   = getSkinnable().getWidth() < getSkinnable().getHeight() ? getSkinnable().getWidth() : getSkinnable().getHeight();
        width  = getSkinnable().getWidth();
        height = getSkinnable().getHeight();

        if (keepAspect) {
            if (aspectRatio * width > height) {
                width = 1 / (aspectRatio / height);
            } else if (1 / (aspectRatio / height) > width) {
                height = aspectRatio * width;
            }
        }

        if (width > 0 && height > 0) {
            canvasBkg.setWidth(width);
            canvasBkg.setHeight(height);
            canvasFg.setWidth(width);
            canvasFg.setHeight(height);
            drawBackground();
            drawForeground();
        }
    }
}
TOP

Related Classes of eu.hansolo.enzo.charts.skin.SimpleLineChartSkin

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.