All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pepstock.charba.client.impl.charts.BaseMeterController Maven / Gradle / Ivy

There is a newer version: 6.5-gwt
Show newest version
/**
    Copyright 2017 Andrea "Stock" Stocchero

    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.pepstock.charba.client.impl.charts;

import java.util.List;

import org.pepstock.charba.client.ChartNode;
import org.pepstock.charba.client.Controller;
import org.pepstock.charba.client.Defaults;
import org.pepstock.charba.client.IsChart;
import org.pepstock.charba.client.controllers.AbstractController;
import org.pepstock.charba.client.controllers.ControllerContext;
import org.pepstock.charba.client.controllers.ControllerType;
import org.pepstock.charba.client.data.Dataset;
import org.pepstock.charba.client.dom.elements.Context2dItem;
import org.pepstock.charba.client.dom.elements.TextMetricsItem;
import org.pepstock.charba.client.dom.enums.TextAlign;
import org.pepstock.charba.client.dom.enums.TextBaseline;
import org.pepstock.charba.client.enums.FontStyle;
import org.pepstock.charba.client.items.ChartAreaNode;
import org.pepstock.charba.client.utils.Utilities;

/**
 * Controller implementation to create charts like meter of gauges, extending doughnut chart.
 * 
 * @author Andrea "Stock" Stocchero
 */
final class BaseMeterController extends AbstractController {

	// default font increment
	private static final int FONT_SIZE_INCREMENT = 4;
	// minimum font size
	private static final int MINIMUM_FONT_SIZE = 12;
	// default padding
	private static final double PADDING = 4D;
	// max percentage
	private static final double MAX_PERCENTAGE = 100D;
	// SQRT of 2 to calculate the sqare inside the doughnut
	private static final double SQRT_2 = Math.sqrt(2);
	// controller type
	private final ControllerType type;

	/**
	 * Creates the controller using the type passed as argument.
	 * 
	 * @param type controller type, could be meter or gauge.
	 */
	BaseMeterController(ControllerType type) {
		this.type = type;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.pepstock.charba.client.Controller#getType()
	 */
	@Override
	public ControllerType getType() {
		return type;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.pepstock.charba.client.controllers.AbstractController#initialize(org.pepstock.charba.client.controllers. ControllerContext, org.pepstock.charba.client.IsChart, int)
	 */
	@Override
	public void initialize(ControllerContext context, IsChart chart, int datasetIndex) {
		// checks if arguments are consistent
		if (Controller.isConsistent(this, context, chart)) {
			// gets the dataset at index
			Dataset dataset = chart.getData().getDatasets().get(datasetIndex);
			// checks if is a meter dataset (or gauge)
			if (dataset instanceof MeterDataset) {
				// casts to meter dataset
				MeterDataset meterDataset = (MeterDataset) dataset;
				// meter or gauge charts must have only 1 dataset
				// checks if there is more than 1
				if (datasetIndex > 0) {
					// if more than 1
					// forces hidden dataset
					meterDataset.hide();
				}
				// invokes the initialization
				super.initialize(context, chart, datasetIndex);
			} else {
				// if not meter dataset
				// exception
				throw new IllegalArgumentException("Dataset at index " + datasetIndex + " is not a MeterDataset");
			}
		} else {
			// if here, arguments are not consistent
			// exception
			throw new IllegalArgumentException("Initialize method arguments are not consistent");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.pepstock.charba.client.controllers.AbstractController#draw(org.pepstock.charba.client.controllers .Context, org.pepstock.charba.client.IsChart, double)
	 */
	@Override
	public void draw(ControllerContext context, IsChart chart, double ease) {
		// checks if arguments are consistent
		if (Controller.isConsistent(this, context, chart)) {
			// draw the doughnut chart
			super.draw(context, chart, ease);
			// gets the list of datasets
			List datasets = chart.getData().getDatasets();
			// checks if not empty
			if (!datasets.isEmpty()) {
				// gets chart item
				ChartNode item = context.getNode();
				// gets dataset index 0
				MeterDataset dataset = (MeterDataset) datasets.get(0);
				// checks if meter chart
				if (chart instanceof MeterChart) {
					MeterChart meterChart = (MeterChart) chart;
					MeterOptions options = meterChart.getOptions();
					// let's draw the value inside the doughnut
					execute(chart, item, dataset, options, ease);
				} else if (chart instanceof GaugeChart) {
					// checks if meter chart
					GaugeChart gaugeChart = (GaugeChart) chart;
					GaugeOptions options = gaugeChart.getOptions();
					// let's draw the value inside the doughnut
					execute(chart, item, dataset, options, ease);
				}
			}
		} else {
			// if here, arguments are not consistent
			// exception
			throw new IllegalArgumentException("Draw method arguments are not consistent");
		}
	}

	/**
	 * Draws the value inside the inner radius of doughnut.
	 * 
	 * @param chart chart instance
	 * @param item chart item with CHART.JS properties needed to calculate the area where to drawn the value
	 * @param dataset the dataset instance
	 * @param options the chart options
	 * @param ease easing of drawing (between 0 and 1) for animation
	 */
	private void execute(IsChart chart, ChartNode item, MeterDataset dataset, MeterOptions options, double ease) {
		// calculate the side of the square where to draw the value
		final int sideOfSquare = (int) ((item.getInnerRadius() * 2) / SQRT_2);
		// gets canvas context 2d
		Context2dItem ctx = chart.getCanvas().getContext2d();
		// gets the chart area of CHART.JS
		ChartAreaNode area = item.getChartArea();
		// calculate the center point of the square
		final int centerX = (area.getRight() - area.getLeft()) / 2 + area.getLeft();
		final int centerY = (area.getBottom() - area.getTop()) / 2 + area.getTop();
		// gets max value
		final double maxValue = MeterDisplay.PERCENTAGE.equals(options.getDisplay()) || MeterDisplay.PERCENTAGE_AND_LABEL.equals(options.getDisplay()) ? MAX_PERCENTAGE : dataset.getMax();
		// gets value
		final double valueToCalculate = MeterDisplay.PERCENTAGE.equals(options.getDisplay()) || MeterDisplay.PERCENTAGE_AND_LABEL.equals(options.getDisplay()) ? dataset.getValue() / dataset.getMax() : dataset.getValue();
		// here is calculating the value to showed
		// based on easing of drawing
		final double value = options.isAnimatedDisplay() ? valueToCalculate * ease : valueToCalculate;
		// gets max value into string to check font size
		final String maxValueToShow = getFormattedValue(chart, options, maxValue, 1D);
		// value to show with format required
		final String valueToShow = getFormattedValue(chart, options, value, ease);
		// gets font style
		final FontStyle style = options.getFontStyle() == null ? FontStyle.NORMAL : options.getFontStyle();
		// gets font family
		final String fontFamily = options.getFontFamily() == null ? Defaults.get().getGlobal().getDefaultFontFamily() : options.getFontFamily();
		// gets font color
		final String fontColor = options.getDisplayFontColor() == null ? MeterOptions.DEFAULT_DISPLAY_COLOR.toRGBA() : options.getDisplayFontColor().toRGBA();
		// gets the label
		final String label = dataset.getLabel();
		// saves context
		ctx.save();
		// calculates the font size
		int fontSize = calculateFontSize(ctx, sideOfSquare, maxValueToShow, style, fontFamily);
		// sets color to canvas
		ctx.setFillColor(fontColor);
		// sets alignment
		ctx.setTextAlign(TextAlign.CENTER);
		// checks if it must draw also the label
		if ((MeterDisplay.VALUE_AND_LABEL.equals(options.getDisplay()) || MeterDisplay.PERCENTAGE_AND_LABEL.equals(options.getDisplay())) && label != null) {
			// sets font
			ctx.setFont(Utilities.toCSSFontProperty(style, fontSize, fontFamily));
			// sets alignment from center point
			ctx.setTextBaseline(TextBaseline.BOTTOM);
			// draws text
			ctx.fillText(valueToShow, centerX, centerY - PADDING);
			// re-calculates the font size for label
			fontSize = calculateFontSize(ctx, sideOfSquare, label, style, fontFamily);
			ctx.setFont(Utilities.toCSSFontProperty(style, fontSize, fontFamily));
			// sets alignment from center point
			ctx.setTextBaseline(TextBaseline.TOP);
			// draws text
			ctx.fillText(dataset.getLabel(), centerX, centerY + PADDING);
		} else {
			// if here it must draw ONLY the value
			// sets font
			ctx.setFont(Utilities.toCSSFontProperty(style, fontSize, fontFamily));
			// sets alignment from center point
			ctx.setTextBaseline(TextBaseline.MIDDLE);
			// draws text
			ctx.fillText(valueToShow, centerX, centerY);
		}
		// restores context
		ctx.restore();
	}

	/**
	 * Calculates the font size based on available space into square into doughnut inner radius.
	 * 
	 * @param ctx canvas context
	 * @param sideOfSquare side of square
	 * @param value value to display
	 * @param style font style
	 * @param fontFamily font family
	 * @return the font size to use
	 */
	private int calculateFontSize(Context2dItem ctx, int sideOfSquare, String value, FontStyle style, String fontFamily) {
		// half of side of square
		int fontSize = sideOfSquare / 2;
		boolean check = true;
		// loop to calculate the size
		while (check) {
			// sets font
			ctx.setFont(Utilities.toCSSFontProperty(style, fontSize, fontFamily));
			// gets metrics
			TextMetricsItem metrics = ctx.measureText(value);
			// if the width is inside of side (and padding) or
			// is the minimum size of font
			// exit
			if ((metrics.getWidth() + PADDING * 2D) < sideOfSquare || fontSize <= MINIMUM_FONT_SIZE) {
				check = false;
			} else {
				// decrements the font size
				fontSize = fontSize - FONT_SIZE_INCREMENT;
			}
		}
		// returns font size
		return fontSize;
	}

	/**
	 * Returns a formatted value of the chart applying the precision or invoking the value callback.
	 * 
	 * @param chart chart instance
	 * @param options chart options instance
	 * @param value value of the chart
	 * @param easing the current animation status
	 * @return a formatted value of the chart
	 */
	private String getFormattedValue(IsChart chart, MeterOptions options, double value, double easing) {
		// checks if options has got a callback
		if (options.getValueCallback() != null) {
			// invokes callback
			String result = options.getValueCallback().onFormat(chart, value, easing);
			// checks if result is consistent
			if (result != null) {
				// return this value
				return result;
			}
		}
		// if here, it sues the precision set into options
		return Utilities.applyPrecision(value, options.getPrecision());
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy