Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
eu.hansolo.medusa.skins.SimpleSkin Maven / Gradle / Ivy
/*
* Copyright (c) 2015 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.medusa.skins;
import eu.hansolo.medusa.Fonts;
import eu.hansolo.medusa.Gauge;
import eu.hansolo.medusa.Section;
import eu.hansolo.medusa.tools.Helper;
import javafx.beans.InvalidationListener;
import javafx.collections.ListChangeListener;
import javafx.geometry.Point2D;
import javafx.geometry.VPos;
import javafx.scene.CacheHint;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.layout.Pane;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.FillRule;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.transform.Rotate;
import java.util.List;
import java.util.Locale;
import static eu.hansolo.medusa.tools.Helper.enableNode;
import static eu.hansolo.medusa.tools.Helper.formatNumber;
/**
* Created by hansolo on 20.12.15.
*/
public class SimpleSkin extends GaugeSkinBase {
private double START_ANGLE = 315;
private double ANGLE_RANGE = 270;
private double size;
private Pane pane;
private Canvas sectionsCanvas;
private GraphicsContext sectionsCtx;
private Path needle;
private MoveTo needleMoveTo1;
private CubicCurveTo needleCubicCurveTo2;
private CubicCurveTo needleCubicCurveTo3;
private CubicCurveTo needleCubicCurveTo4;
private LineTo needleLineTo5;
private LineTo needleLineTo6;
private CubicCurveTo needleCubicCurveTo7;
private ClosePath needleClosePath8;
private Rotate needleRotate;
private Text valueText;
private Text titleText;
private Text subTitleText;
private double angleStep;
private Locale locale;
private List sections;
private boolean highlightSections;
private double minValue;
private double maxValue;
private ListChangeListener sectionListener;
private InvalidationListener currentValueListener;
// ******************** Constructors **************************************
public SimpleSkin(Gauge gauge) {
super(gauge);
if (gauge.isAutoScale()) gauge.calcAutoScale();
angleStep = ANGLE_RANGE / (gauge.getMaxValue() - gauge.getMinValue());
locale = gauge.getLocale();
sections = gauge.getSections();
highlightSections = gauge.isHighlightSections();
minValue = gauge.getMinValue();
maxValue = gauge.getMaxValue();
sectionListener = c -> handleEvents("RESIZE");
currentValueListener = o -> rotateNeedle(gauge.getCurrentValue());
initGraphics();
registerListeners();
}
// ******************** Initialization ************************************
private void initGraphics() {
// Set initial size
if (Double.compare(gauge.getPrefWidth(), 0.0) <= 0 || Double.compare(gauge.getPrefHeight(), 0.0) <= 0 ||
Double.compare(gauge.getWidth(), 0.0) <= 0 || Double.compare(gauge.getHeight(), 0.0) <= 0) {
if (gauge.getPrefWidth() > 0 && gauge.getPrefHeight() > 0) {
gauge.setPrefSize(gauge.getPrefWidth(), gauge.getPrefHeight());
} else {
gauge.setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT);
}
}
sectionsCanvas = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
sectionsCtx = sectionsCanvas.getGraphicsContext2D();
needleRotate = new Rotate(180 - START_ANGLE);
angleStep = ANGLE_RANGE / (gauge.getRange());
double targetAngle = 180 - START_ANGLE + (gauge.getValue() - gauge.getMinValue()) * angleStep;
needleRotate.setAngle(Helper.clamp(180 - START_ANGLE, 180 - START_ANGLE + ANGLE_RANGE, targetAngle));
needleMoveTo1 = new MoveTo();
needleCubicCurveTo2 = new CubicCurveTo();
needleCubicCurveTo3 = new CubicCurveTo();
needleCubicCurveTo4 = new CubicCurveTo();
needleLineTo5 = new LineTo();
needleLineTo6 = new LineTo();
needleCubicCurveTo7 = new CubicCurveTo();
needleClosePath8 = new ClosePath();
needle = new Path(needleMoveTo1, needleCubicCurveTo2, needleCubicCurveTo3, needleCubicCurveTo4, needleLineTo5, needleLineTo6, needleCubicCurveTo7, needleClosePath8);
needle.setFillRule(FillRule.EVEN_ODD);
needle.getTransforms().setAll(needleRotate);
needle.setFill(gauge.getNeedleColor());
needle.setStroke(gauge.getBorderPaint());
needle.setStrokeLineCap(StrokeLineCap.ROUND);
needle.setStrokeLineJoin(StrokeLineJoin.BEVEL);
valueText = new Text(formatNumber(gauge.getFormatString(), gauge.getDecimals(), gauge.getMinValue()) + gauge.getUnit());
valueText.setMouseTransparent(true);
valueText.setTextOrigin(VPos.CENTER);
valueText.setFill(gauge.getValueColor());
enableNode(valueText, gauge.isValueVisible());
titleText = new Text(gauge.getTitle());
titleText.setTextOrigin(VPos.CENTER);
titleText.setFill(gauge.getTitleColor());
enableNode(titleText, !gauge.getTitle().isEmpty());
subTitleText = new Text(gauge.getSubTitle());
subTitleText.setTextOrigin(VPos.CENTER);
subTitleText.setFill(gauge.getSubTitleColor());
enableNode(subTitleText, !gauge.getSubTitle().isEmpty());
// Add all nodes
pane = new Pane(sectionsCanvas, needle, valueText, titleText, subTitleText);
getChildren().setAll(pane);
}
@Override protected void registerListeners() {
super.registerListeners();
gauge.getSections().addListener(sectionListener);
gauge.currentValueProperty().addListener(currentValueListener);
}
// ******************** Methods *******************************************
@Override protected void handleEvents(final String EVENT_TYPE) {
super.handleEvents(EVENT_TYPE);
if ("FINISHED".equals(EVENT_TYPE)) {
if (gauge.getCheckSectionsForValue()) {
double currentValue = gauge.getCurrentValue();
int listSize = sections.size();
for (int i = 0 ; i < listSize ; i++) { sections.get(i).checkForValue(currentValue); }
}
// Highlight Sections if enabled
if (highlightSections) {
drawSections();
}
} else if ("RECALC".equals(EVENT_TYPE)) {
minValue = gauge.getMinValue();
maxValue = gauge.getMaxValue();
angleStep = ANGLE_RANGE / gauge.getRange();
needleRotate.setAngle((180 - START_ANGLE) + (gauge.getValue() - gauge.getMinValue()) * angleStep);
resize();
rotateNeedle(gauge.getCurrentValue());
} else if ("SECTION".equals(EVENT_TYPE)) {
sections = gauge.getSections();
highlightSections = gauge.isHighlightSections();
resize();
redraw();
} else if ("VISIBILITY".equals(EVENT_TYPE)) {
enableNode(valueText, gauge.isValueVisible());
enableNode(titleText, !gauge.getTitle().isEmpty());
enableNode(subTitleText, !gauge.getSubTitle().isEmpty());
}
}
@Override public void dispose() {
gauge.getSections().removeListener(sectionListener);
gauge.currentValueProperty().removeListener(currentValueListener);
super.dispose();
}
// ******************** Private Methods ***********************************
private void rotateNeedle(final double VALUE) {
double targetAngle = 180 - START_ANGLE + (VALUE - gauge.getMinValue()) * angleStep;
needleRotate.setAngle(Helper.clamp(180 - START_ANGLE, 180 - START_ANGLE + ANGLE_RANGE, targetAngle));
valueText.setText(formatNumber(gauge.getFormatString(), gauge.getDecimals(), VALUE) + gauge.getUnit());
valueText.setTranslateX((size - valueText.getLayoutBounds().getWidth()) * 0.5);
if (valueText.getLayoutBounds().getWidth() > 0.395 * size) { resizeText(); }
}
// ******************** Drawing *******************************************
private void drawSections() {
sectionsCtx.clearRect(0, 0, size, size);
double value = gauge.getCurrentValue();
boolean sectionTextVisible = gauge.isSectionTextVisible();
boolean sectionIconVisible = gauge.getSectionIconsVisible();
double offset = START_ANGLE - 90;
int listSize = sections.size();
double xy = size * 0.015;
double wh = size - (size * 0.03);
double sectionXY = size * 0.1375;
double sectionWH = size - (size * 0.275);
angleStep = ANGLE_RANGE / (gauge.getRange());
double sinValue;
double cosValue;
sectionsCtx.setLineWidth(size * 0.27);
sectionsCtx.setLineCap(StrokeLineCap.BUTT);
for (int i = 0 ; i < listSize ; i++) {
Section section = sections.get(i);
final double SECTION_START_ANGLE;
if (section.getStart() > maxValue || section.getStop() < minValue) continue;
if (section.getStart() < minValue && section.getStop() < maxValue) {
SECTION_START_ANGLE = 0;
} else {
SECTION_START_ANGLE = (section.getStart() - minValue) * angleStep;
}
final double SECTION_ANGLE_EXTEND;
if (Double.compare(section.getStop(), maxValue) > 0) {
SECTION_ANGLE_EXTEND = (maxValue - section.getStart()) * angleStep;
} else if (Double.compare(section.getStart(), minValue) < 0) {
SECTION_ANGLE_EXTEND = (section.getStop() - minValue) * angleStep;
} else {
SECTION_ANGLE_EXTEND = (section.getStop() - section.getStart()) * angleStep;
}
sectionsCtx.save();
if (highlightSections) {
sectionsCtx.setStroke(section.contains(value) ? section.getHighlightColor() : section.getColor());
} else {
sectionsCtx.setStroke(section.getColor());
}
sectionsCtx.strokeArc(sectionXY, sectionXY, sectionWH, sectionWH, (offset - SECTION_START_ANGLE), -SECTION_ANGLE_EXTEND, ArcType.OPEN);
// Draw Section Text
if (sectionTextVisible) {
sinValue = -Math.sin(Math.toRadians(offset - 90 - SECTION_START_ANGLE - SECTION_ANGLE_EXTEND * 0.5));
cosValue = -Math.cos(Math.toRadians(offset - 90 - SECTION_START_ANGLE - SECTION_ANGLE_EXTEND * 0.5));
Point2D textPoint = new Point2D(size * 0.5 + size * 0.365 * sinValue, size * 0.5 + size * 0.365 * cosValue);
sectionsCtx.setFont(Fonts.robotoMedium(0.08 * size));
sectionsCtx.setTextAlign(TextAlignment.CENTER);
sectionsCtx.setTextBaseline(VPos.CENTER);
sectionsCtx.setFill(section.getTextColor());
sectionsCtx.fillText(section.getText(), textPoint.getX(), textPoint.getY(), 0.2 * size);
} else if (size > 0 && sectionIconVisible) {
// Draw Section Icon
Image icon = section.getImage();
if (null != icon) {
sinValue = -Math.sin(Math.toRadians(offset - 90 - SECTION_START_ANGLE - SECTION_ANGLE_EXTEND * 0.5));
cosValue = -Math.cos(Math.toRadians(offset - 90 - SECTION_START_ANGLE - SECTION_ANGLE_EXTEND * 0.5));
Point2D iconPoint = new Point2D(size * 0.5 + size * 0.365 * sinValue, size * 0.5 + size * 0.365 * cosValue);
sectionsCtx.drawImage(icon, iconPoint.getX() - size * 0.06, iconPoint.getY() - size * 0.06, size * 0.12, size * 0.12);
}
}
sectionsCtx.restore();
}
// Draw white border around area
sectionsCtx.setStroke(gauge.getBorderPaint());
sectionsCtx.setLineWidth(size * 0.025);
sectionsCtx.strokeArc(xy, xy, wh, wh, offset + 90, ANGLE_RANGE, ArcType.ROUND);
if (gauge.getTickLabelsVisible()) {
sectionsCtx.setFont(Fonts.robotoRegular(size * 0.1));
sectionsCtx.setFill(gauge.getTickLabelColor());
sectionsCtx.setTextBaseline(VPos.TOP);
sectionsCtx.setTextAlign(TextAlignment.LEFT);
sectionsCtx.fillText(String.format(locale, "%." + gauge.getTickLabelDecimals() + "f", gauge.getMinValue()), size * 0.15075377, size * 0.86180905, size * 0.3);
sectionsCtx.setTextAlign(TextAlignment.RIGHT);
sectionsCtx.fillText(String.format(locale, "%." + gauge.getTickLabelDecimals() + "f", gauge.getMaxValue()), size * 0.84924623, size * 0.86180905, size * 0.3);
}
}
private void resizeText() {
double fontSize = size * 0.25;
double maxWidth = 0.35 * size;
valueText.setFont(Fonts.robotoMedium(fontSize));
if (valueText.getLayoutBounds().getWidth() > maxWidth) { Helper.adjustTextSize(valueText, maxWidth, fontSize); }
valueText.setTranslateX((size - valueText.getLayoutBounds().getWidth()) * 0.5);
valueText.setTranslateY(size * (titleText.getText().isEmpty() ? 0.5 : 0.46));
fontSize = size * 0.075;
maxWidth = size * 0.3;
titleText.setFont(Fonts.robotoMedium(fontSize));
if (titleText.getLayoutBounds().getWidth() > maxWidth) { Helper.adjustTextSize(titleText, maxWidth, fontSize); }
titleText.setTranslateX((size - titleText.getLayoutBounds().getWidth()) * 0.5);
titleText.setTranslateY(size * 0.5 + valueText.getFont().getSize() * 0.45);
maxWidth = size * 0.45;
subTitleText.setFont(Fonts.robotoMedium(fontSize));
if (subTitleText.getLayoutBounds().getWidth() > maxWidth) { Helper.adjustTextSize(subTitleText, maxWidth, fontSize); }
subTitleText.setTranslateX((size - subTitleText.getLayoutBounds().getWidth()) * 0.5);
subTitleText.setTranslateY(size * 0.8);
}
@Override protected void resize() {
double width = gauge.getWidth() - gauge.getInsets().getLeft() - gauge.getInsets().getRight();
double height = gauge.getHeight() - gauge.getInsets().getTop() - gauge.getInsets().getBottom();
size = width < height ? width : height;
if (size > 0) {
pane.setMaxSize(size, size);
pane.relocate((gauge.getWidth() - size) * 0.5, (gauge.getHeight() - size) * 0.5);
sectionsCanvas.setCache(false);
sectionsCanvas.setWidth(size);
sectionsCanvas.setHeight(size);
drawSections();
sectionsCanvas.setCache(true);
sectionsCanvas.setCacheHint(CacheHint.QUALITY);
needle.setCache(false);
needleMoveTo1.setX(0.275 * size); needleMoveTo1.setY(0.5 * size);
needleCubicCurveTo2.setControlX1(0.275 * size); needleCubicCurveTo2.setControlY1(0.62426575 * size);
needleCubicCurveTo2.setControlX2(0.37573425 * size); needleCubicCurveTo2.setControlY2(0.725 * size);
needleCubicCurveTo2.setX(0.5 * size); needleCubicCurveTo2.setY(0.725 * size);
needleCubicCurveTo3.setControlX1(0.62426575 * size); needleCubicCurveTo3.setControlY1(0.725 * size);
needleCubicCurveTo3.setControlX2(0.725 * size); needleCubicCurveTo3.setControlY2(0.62426575 * size);
needleCubicCurveTo3.setX(0.725 * size); needleCubicCurveTo3.setY(0.5 * size);
needleCubicCurveTo4.setControlX1(0.725 * size); needleCubicCurveTo4.setControlY1(0.3891265 * size);
needleCubicCurveTo4.setControlX2(0.6448105 * size); needleCubicCurveTo4.setControlY2(0.296985 * size);
needleCubicCurveTo4.setX(0.5392625 * size); needleCubicCurveTo4.setY(0.2784125 * size);
needleLineTo5.setX(0.5 * size); needleLineTo5.setY(0.004 * size);
needleLineTo6.setX(0.4607375 * size); needleLineTo6.setY(0.2784125 * size);
needleCubicCurveTo7.setControlX1(0.3551895 * size); needleCubicCurveTo7.setControlY1(0.296985 * size);
needleCubicCurveTo7.setControlX2(0.275 * size); needleCubicCurveTo7.setControlY2(0.3891265 * size);
needleCubicCurveTo7.setX(0.275 * size); needleCubicCurveTo7.setY(0.5 * size);
needle.setCache(true);
needle.setCacheHint(CacheHint.ROTATE);
needle.setStrokeWidth(size * 0.025);
needle.relocate(needle.getLayoutBounds().getMinX(), needle.getLayoutBounds().getMinY());
needleRotate.setPivotX(size * 0.5);
needleRotate.setPivotY(size * 0.5);
double currentValue = (needleRotate.getAngle() + START_ANGLE - 180) / angleStep + gauge.getMinValue();
valueText.setText(formatNumber(gauge.getFormatString(), gauge.getDecimals(), currentValue) + gauge.getUnit());
valueText.setVisible(gauge.isValueVisible());
titleText.setText(gauge.getTitle());
resizeText();
}
}
@Override protected void redraw() {
enableNode(sectionsCanvas, gauge.getSectionsVisible());
needle.setFill(gauge.getNeedleColor());
needle.setStroke(gauge.getNeedleBorderColor());
titleText.setFill(gauge.getTitleColor());
subTitleText.setFill(gauge.getSubTitleColor());
valueText.setFill(gauge.getValueColor());
locale = gauge.getLocale();
titleText.setText(gauge.getTitle());
resizeText();
}
}