eu.hansolo.medusa.skins.PlainAmpSkin Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Medusa Show documentation
Show all versions of Medusa Show documentation
Medusa is a JavaFX 8 library containing gauges and clocks
/*
* Copyright (c) 2019 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.LcdDesign;
import eu.hansolo.medusa.Marker;
import eu.hansolo.medusa.Section;
import eu.hansolo.medusa.TickLabelOrientation;
import eu.hansolo.medusa.tools.Helper;
import javafx.beans.InvalidationListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.CacheHint;
import javafx.scene.Group;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.InnerShadow;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Paint;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
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.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.transform.Rotate;
import java.math.BigDecimal;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static eu.hansolo.medusa.tools.Helper.enableNode;
public class PlainAmpSkin extends GaugeSkinBase {
protected static final double PREFERRED_WIDTH = 300;
protected static final double PREFERRED_HEIGHT = 150;
protected static final double MINIMUM_WIDTH = 30;
protected static final double MINIMUM_HEIGHT = 15;
protected static final double MAXIMUM_WIDTH = 1024;
protected static final double MAXIMUM_HEIGHT = 858;
private static final double ASPECT_RATIO = 0.5;
private static final double START_ANGLE = 225;
private static final double ANGLE_RANGE = 90;
private Map markerMap = new ConcurrentHashMap<>();
private double oldValue;
private double width;
private double height;
private Pane pane;
private Canvas ticksAndSectionsCanvas;
private GraphicsContext ticksAndSections;
private Pane markerPane;
private Path threshold;
private Path average;
private double ledSize;
private InnerShadow ledOnShadow;
private InnerShadow ledOffShadow;
private LinearGradient frameGradient;
private LinearGradient ledOnGradient;
private LinearGradient ledOffGradient;
private RadialGradient highlightGradient;
private Canvas ledCanvas;
private GraphicsContext led;
private Path needle;
private MoveTo needleMoveTo1;
private CubicCurveTo needleCubicCurveTo2;
private CubicCurveTo needleCubicCurveTo3;
private CubicCurveTo needleCubicCurveTo4;
private LineTo needleLineTo5;
private CubicCurveTo needleCubicCurveTo6;
private ClosePath needleClosePath7;
private Rotate needleRotate;
private Group shadowGroup;
private DropShadow dropShadow;
private Text unitText;
private Rectangle lcd;
private Label lcdText;
private double angleStep;
private Tooltip thresholdTooltip;
private String formatString;
private Locale locale;
private ListChangeListener sectionListener;
private InvalidationListener currentValueListener;
private InvalidationListener needleRotateListener;
private ListChangeListener markerListener;
// ******************** Constructors **************************************
public PlainAmpSkin(Gauge gauge) {
super(gauge);
if (gauge.isAutoScale()) gauge.calcAutoScale();
angleStep = gauge.getAngleRange() / gauge.getRange();
oldValue = gauge.getValue();
formatString = new StringBuilder("%.").append(Integer.toString(gauge.getDecimals())).append("f").toString();
locale = gauge.getLocale();
sectionListener = c -> redraw();
currentValueListener = o -> rotateNeedle();
needleRotateListener = o -> handleEvents("ANGLE");
markerListener = c -> {
updateMarkers();
redraw();
};
updateMarkers();
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);
}
}
ticksAndSectionsCanvas = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
ticksAndSections = ticksAndSectionsCanvas.getGraphicsContext2D();
ledCanvas = new Canvas();
led = ledCanvas.getGraphicsContext2D();
thresholdTooltip = new Tooltip("Threshold\n(" + String.format(locale, formatString, gauge.getThreshold()) + ")");
thresholdTooltip.setTextAlignment(TextAlignment.CENTER);
threshold = new Path();
Helper.enableNode(threshold, gauge.isThresholdVisible());
Tooltip.install(threshold, thresholdTooltip);
average = new Path();
Helper.enableNode(average, gauge.isAverageVisible());
markerPane = new Pane();
needleRotate = new Rotate(180 - START_ANGLE);
needleRotate.setAngle(needleRotate.getAngle() + (gauge.getValue() - oldValue - gauge.getMinValue()) * angleStep);
needleMoveTo1 = new MoveTo();
needleCubicCurveTo2 = new CubicCurveTo();
needleCubicCurveTo3 = new CubicCurveTo();
needleCubicCurveTo4 = new CubicCurveTo();
needleLineTo5 = new LineTo();
needleCubicCurveTo6 = new CubicCurveTo();
needleClosePath7 = new ClosePath();
needle = new Path(needleMoveTo1, needleCubicCurveTo2, needleCubicCurveTo3, needleCubicCurveTo4, needleLineTo5, needleCubicCurveTo6, needleClosePath7);
needle.setFillRule(FillRule.EVEN_ODD);
needle.getTransforms().setAll(needleRotate);
dropShadow = new DropShadow();
dropShadow.setColor(Color.rgb(0, 0, 0, 0.25));
dropShadow.setBlurType(BlurType.TWO_PASS_BOX);
dropShadow.setRadius(0.015 * PREFERRED_WIDTH);
dropShadow.setOffsetY(0.015 * PREFERRED_WIDTH);
shadowGroup = new Group(needle);
shadowGroup.setEffect(gauge.isShadowsEnabled() ? dropShadow : null);
unitText = new Text(gauge.getUnit());
unitText.setMouseTransparent(true);
unitText.setTextOrigin(VPos.CENTER);
lcd = new Rectangle(0.3 * PREFERRED_WIDTH, 0.1 * PREFERRED_HEIGHT);
lcd.setArcWidth(0.0125 * PREFERRED_HEIGHT);
lcd.setArcHeight(0.0125 * PREFERRED_HEIGHT);
lcd.relocate((PREFERRED_WIDTH - lcd.getWidth()) * 0.5, 0.66 * PREFERRED_HEIGHT);
Helper.enableNode(lcd, gauge.isLcdVisible() && gauge.isValueVisible());
lcdText = new Label(String.format(locale, "%." + gauge.getDecimals() + "f", gauge.getValue()));
lcdText.setAlignment(Pos.CENTER_RIGHT);
lcdText.setVisible(gauge.isValueVisible());
// Set initial value
angleStep = ANGLE_RANGE / gauge.getRange();
double targetAngle = 180 - START_ANGLE + (gauge.getValue() - gauge.getMinValue()) * angleStep;
targetAngle = clamp(180 - START_ANGLE, 180 - START_ANGLE + ANGLE_RANGE, targetAngle);
needleRotate.setAngle(targetAngle);
// Add all nodes
pane = new Pane();
pane.getChildren().setAll(ticksAndSectionsCanvas,
markerPane,
ledCanvas,
unitText,
lcd,
lcdText,
shadowGroup);
getChildren().setAll(pane);
}
@Override protected void registerListeners() {
super.registerListeners();
gauge.getMarkers().addListener(markerListener);
gauge.getSections().addListener(sectionListener);
gauge.currentValueProperty().addListener(currentValueListener);
needleRotate.angleProperty().addListener(needleRotateListener);
}
// ******************** Methods *******************************************
protected void handleEvents(final String EVENT_TYPE) {
super.handleEvents(EVENT_TYPE);
if ("FINISHED".equals(EVENT_TYPE)) {
if ( gauge.isHighlightSections() ) {
redraw();
}
} else if ("ANGLE".equals(EVENT_TYPE)) {
double currentValue = (needleRotate.getAngle() + START_ANGLE - 180) / angleStep + gauge.getMinValue();
lcdText.setText((String.format(locale, formatString, currentValue)));
if (gauge.isLcdVisible()) {
lcdText.setAlignment(Pos.CENTER_RIGHT);
lcdText.setTranslateX((width - lcdText.getPrefWidth()) * 0.5);
} else {
lcdText.setAlignment(Pos.CENTER);
lcdText.setTranslateX((width - lcdText.getLayoutBounds().getWidth()) * 0.5);
}
} else if ("VISIBILITY".equals(EVENT_TYPE)) {
enableNode(ledCanvas, gauge.isLedVisible());
enableNode(unitText, !gauge.getUnit().isEmpty());
enableNode(lcd,gauge.isLcdVisible());
enableNode(lcdText,gauge.isValueVisible());
enableNode(threshold, gauge.isThresholdVisible());
enableNode(average, gauge.isAverageVisible());
boolean markersVisible = gauge.getMarkersVisible();
for (Shape shape : markerMap.values()) { Helper.enableNode(shape, markersVisible); }
resize();
redraw();
} else if ("LED".equals(EVENT_TYPE)) {
if (gauge.isLedVisible()) { drawLed(led); }
} else if ("LCD".equals(EVENT_TYPE)) {
if (gauge.isLcdVisible()) redraw();
} else if ("RECALC".equals(EVENT_TYPE)) {
angleStep = gauge.getAngleStep();
if (gauge.getValue() < gauge.getMinValue()) {
oldValue = gauge.getMinValue();
}
if (gauge.getValue() > gauge.getMaxValue()) {
oldValue = gauge.getMaxValue();
}
redraw();
rotateNeedle();
}
}
// ******************** Private Methods ***********************************
private void rotateNeedle() {
angleStep = ANGLE_RANGE / (gauge.getRange());
double targetAngle = 180 - START_ANGLE + (gauge.getCurrentValue() - gauge.getMinValue()) * angleStep;
targetAngle = clamp(180 - START_ANGLE, 180 - START_ANGLE + ANGLE_RANGE, targetAngle);
needleRotate.setAngle(targetAngle);
if (gauge.isAverageVisible()) drawAverage();
}
private void drawMarkers() {
markerPane.getChildren().setAll(markerMap.values());
markerPane.getChildren().addAll(average, threshold);
double minValue = gauge.getMinValue();
double markerSize = 0.0125 * width;
double pathHalf = markerSize * 0.3;
double scaledWidth = width * 1.106;
double centerX = width * 0.5;
double centerY = height * 1.4;
if (gauge.getMarkersVisible()) {
for (Map.Entry entry : markerMap.entrySet()) {
Marker marker = entry.getKey();
Shape shape = entry.getValue();
double valueAngle = START_ANGLE - (marker.getValue() - minValue) * angleStep;
double sinValue = Math.sin(Math.toRadians(valueAngle));
double cosValue = Math.cos(Math.toRadians(valueAngle));
switch (marker.getMarkerType()) {
case TRIANGLE:
Path triangle = (Path) shape;
triangle.getElements().clear();
triangle.getElements().add(new MoveTo(centerX + scaledWidth * 0.38 * sinValue, centerY + scaledWidth * 0.38 * cosValue));
sinValue = Math.sin(Math.toRadians(valueAngle - pathHalf));
cosValue = Math.cos(Math.toRadians(valueAngle - pathHalf));
triangle.getElements().add(new LineTo(centerX + scaledWidth * 0.4075 * sinValue, centerY + scaledWidth * 0.4075 * cosValue));
sinValue = Math.sin(Math.toRadians(valueAngle + pathHalf));
cosValue = Math.cos(Math.toRadians(valueAngle + pathHalf));
triangle.getElements().add(new LineTo(centerX + scaledWidth * 0.4075 * sinValue, centerY + scaledWidth * 0.4075 * cosValue));
triangle.getElements().add(new ClosePath());
break;
case DOT:
Circle dot = (Circle) shape;
dot.setRadius(markerSize);
dot.setCenterX(centerX + scaledWidth * 0.3945 * sinValue);
dot.setCenterY(centerY + scaledWidth * 0.3945 * cosValue);
break;
case STANDARD:
default:
Path standard = (Path) shape;
standard.getElements().clear();
standard.getElements().add(new MoveTo(centerX + scaledWidth * 0.38 * sinValue, centerY + scaledWidth * 0.38 * cosValue));
sinValue = Math.sin(Math.toRadians(valueAngle - pathHalf));
cosValue = Math.cos(Math.toRadians(valueAngle - pathHalf));
standard.getElements().add(new LineTo(centerX + scaledWidth * 0.4075 * sinValue, centerY + scaledWidth * 0.4075 * cosValue));
standard.getElements().add(new LineTo(centerX + scaledWidth * 0.43 * sinValue, centerY + scaledWidth * 0.43 * cosValue));
sinValue = Math.sin(Math.toRadians(valueAngle + pathHalf));
cosValue = Math.cos(Math.toRadians(valueAngle + pathHalf));
standard.getElements().add(new LineTo(centerX + scaledWidth * 0.43 * sinValue, centerY + scaledWidth * 0.43 * cosValue));
standard.getElements().add(new LineTo(centerX + scaledWidth * 0.4075 * sinValue, centerY + scaledWidth * 0.4075 * cosValue));
standard.getElements().add(new ClosePath());
break;
}
Color markerColor = marker.getColor();
shape.setFill(markerColor);
shape.setStroke(markerColor.darker());
shape.setPickOnBounds(false);
Tooltip markerTooltip;
if (marker.getText().isEmpty()) {
markerTooltip = new Tooltip(Double.toString(marker.getValue()));
} else {
markerTooltip = new Tooltip(new StringBuilder(marker.getText()).append("\n(").append(Double.toString(marker.getValue())).append(")").toString());
}
markerTooltip.setTextAlignment(TextAlignment.CENTER);
Tooltip.install(shape, markerTooltip);
shape.setOnMousePressed(e -> marker.fireMarkerEvent(marker.MARKER_PRESSED_EVENT));
shape.setOnMouseReleased(e -> marker.fireMarkerEvent(marker.MARKER_RELEASED_EVENT));
}
}
if (gauge.isThresholdVisible()) {
// Draw threshold
threshold.getElements().clear();
double thresholdAngle = START_ANGLE - (gauge.getThreshold() - minValue) * angleStep;
double thresholdSize = Helper.clamp(2.0, 2.5, 0.01 * scaledWidth);
double sinValue = Math.sin(Math.toRadians(thresholdAngle));
double cosValue = Math.cos(Math.toRadians(thresholdAngle));
threshold.getElements().add(new MoveTo(centerX + scaledWidth * 0.38 * sinValue, centerY + scaledWidth * 0.38 * cosValue));
sinValue = Math.sin(Math.toRadians(thresholdAngle - thresholdSize));
cosValue = Math.cos(Math.toRadians(thresholdAngle - thresholdSize));
threshold.getElements().add(new LineTo(centerX + scaledWidth * 0.35 * sinValue, centerY + scaledWidth * 0.35 * cosValue));
sinValue = Math.sin(Math.toRadians(thresholdAngle + thresholdSize));
cosValue = Math.cos(Math.toRadians(thresholdAngle + thresholdSize));
threshold.getElements().add(new LineTo(centerX + scaledWidth * 0.35 * sinValue, centerY + scaledWidth * 0.35 * cosValue));
threshold.getElements().add(new ClosePath());
threshold.setFill(gauge.getThresholdColor());
threshold.setStroke(gauge.getTickMarkColor());
}
}
private void drawAverage() {
double scaledWidth = width * 1.106;
double centerX = width * 0.5;
double centerY = height * 1.4;
double minValue = gauge.getMinValue();
// Draw average
average.getElements().clear();
double averageAngle = START_ANGLE - (gauge.getAverage() - minValue) * angleStep;
double averageSize = Helper.clamp(2.0, 2.5, 0.01 * scaledWidth);
double sinValue = Math.sin(Math.toRadians(averageAngle));
double cosValue = Math.cos(Math.toRadians(averageAngle));
average.getElements().add(new MoveTo(centerX + scaledWidth * 0.38 * sinValue, centerY + scaledWidth * 0.38 * cosValue));
sinValue = Math.sin(Math.toRadians(averageAngle - averageSize));
cosValue = Math.cos(Math.toRadians(averageAngle - averageSize));
average.getElements().add(new LineTo(centerX + scaledWidth * 0.35 * sinValue, centerY + scaledWidth * 0.35 * cosValue));
sinValue = Math.sin(Math.toRadians(averageAngle + averageSize));
cosValue = Math.cos(Math.toRadians(averageAngle + averageSize));
average.getElements().add(new LineTo(centerX + scaledWidth * 0.35 * sinValue, centerY + scaledWidth * 0.35 * cosValue));
average.getElements().add(new ClosePath());
average.setFill(gauge.getAverageColor());
average.setStroke(gauge.getTickMarkColor());
}
private void updateMarkers() {
markerMap.clear();
for (Marker marker : gauge.getMarkers()) {
switch(marker.getMarkerType()) {
case TRIANGLE: markerMap.put(marker, new Path()); break;
case DOT : markerMap.put(marker, new Circle()); break;
case STANDARD:
default: markerMap.put(marker, new Path()); break;
}
}
}
private void drawTickMarks(final GraphicsContext CTX) {
double sinValue;
double cosValue;
double orthText = TickLabelOrientation.ORTHOGONAL == gauge.getTickLabelOrientation() ? 0.61 : 0.62;
Point2D center = new Point2D(width * 0.5, height * 1.4);
double minorTickSpace = gauge.getMinorTickSpace();
double minValue = gauge.getMinValue();
//double maxValue = gauge.getMaxValue();
double tmpAngleStep = angleStep * minorTickSpace;
int decimals = gauge.getTickLabelDecimals();
BigDecimal minorTickSpaceBD = BigDecimal.valueOf(minorTickSpace);
BigDecimal majorTickSpaceBD = BigDecimal.valueOf(gauge.getMajorTickSpace());
BigDecimal mediumCheck2 = BigDecimal.valueOf(2 * minorTickSpace);
BigDecimal mediumCheck5 = BigDecimal.valueOf(5 * minorTickSpace);
BigDecimal counterBD = BigDecimal.valueOf(minValue);
double counter = minValue;
Font tickLabelFont = Fonts.robotoCondensedRegular((decimals == 0 ? 0.07 : 0.068) * height);
CTX.setFont(tickLabelFont);
for (double angle = 0 ; Double.compare(-ANGLE_RANGE - tmpAngleStep, angle) < 0 ; angle -= tmpAngleStep) {
sinValue = Math.sin(Math.toRadians(angle + START_ANGLE));
cosValue = Math.cos(Math.toRadians(angle + START_ANGLE));
Point2D innerPoint = new Point2D(center.getX() + width * 0.51987097 * sinValue, center.getY() + width * 0.51987097 * cosValue);
Point2D outerMinorPoint = new Point2D(center.getX() + width * 0.55387097 * sinValue, center.getY() + width * 0.55387097 * cosValue);
Point2D outerMediumPoint = new Point2D(center.getX() + width * 0.56387097 * sinValue, center.getY() + width * 0.56387097 * cosValue);
Point2D outerPoint = new Point2D(center.getX() + width * 0.58387097 * sinValue, center.getY() + width * 0.58387097 * cosValue);
Point2D textPoint = new Point2D(center.getX() + width * orthText * sinValue, center.getY() + width * orthText * cosValue);
CTX.setStroke(gauge.getTickMarkColor());
if (Double.compare(counterBD.remainder(majorTickSpaceBD).doubleValue(), 0.0) == 0) {
// Draw major tickmark
CTX.setLineWidth(height * 0.0055);
CTX.strokeLine(innerPoint.getX(), innerPoint.getY(), outerPoint.getX(), outerPoint.getY());
// Draw text
CTX.save();
CTX.translate(textPoint.getX(), textPoint.getY());
switch(gauge.getTickLabelOrientation()) {
case ORTHOGONAL:
if ((360 - START_ANGLE - angle) % 360 > 90 && (360 - START_ANGLE - angle) % 360 < 270) {
CTX.rotate((180 - START_ANGLE - angle) % 360);
} else {
CTX.rotate((360 - START_ANGLE - angle) % 360);
}
break;
case TANGENT:
if ((360 - START_ANGLE - angle - 90) % 360 > 90 && (360 - START_ANGLE - angle - 90) % 360 < 270) {
CTX.rotate((90 - START_ANGLE - angle) % 360);
} else {
CTX.rotate((270 - START_ANGLE - angle) % 360);
}
break;
case HORIZONTAL:
default:
break;
}
CTX.setTextAlign(TextAlignment.CENTER);
CTX.setTextBaseline(VPos.CENTER);
CTX.setFill(gauge.getTickLabelColor());
CTX.fillText(String.format(locale, "%." + decimals + "f", counter), 0, 0);
CTX.restore();
} else if (gauge.getMediumTickMarksVisible() &&
Double.compare(minorTickSpaceBD.remainder(mediumCheck2).doubleValue(), 0.0) != 0.0 &&
Double.compare(counterBD.remainder(mediumCheck5).doubleValue(), 0.0) == 0.0) {
CTX.setLineWidth(height * 0.0035);
CTX.strokeLine(innerPoint.getX(), innerPoint.getY(), outerMediumPoint.getX(), outerMediumPoint.getY());
} else if (gauge.getMinorTickMarksVisible() && Double.compare(counterBD.remainder(minorTickSpaceBD).doubleValue(), 0.0) == 0) {
CTX.setLineWidth(height * 0.00225);
CTX.strokeLine(innerPoint.getX(), innerPoint.getY(), outerMinorPoint.getX(), outerMinorPoint.getY());
}
counterBD = counterBD.add(minorTickSpaceBD);
counter = counterBD.doubleValue();
}
}
private void drawSections(final GraphicsContext CTX) {
final double x = -width * 0.03;
final double y = height * 0.345;
final double w = width * 1.06;
final double h = height * 2.085;
final double MIN_VALUE = gauge.getMinValue();
final double MAX_VALUE = gauge.getMaxValue();
final double OFFSET = 90 - START_ANGLE;
final ObservableList sections = gauge.getSections();
final boolean highlightSections = gauge.isHighlightSections();
double value = gauge.getCurrentValue();
int listSize = sections.size();
for (int i = 0 ; i < listSize ; i++) {
final Section SECTION = sections.get(i);
final double SECTION_START_ANGLE;
if (Double.compare(SECTION.getStart(), MAX_VALUE) <= 0 && Double.compare(SECTION.getStop(), MIN_VALUE) >= 0) {
if (SECTION.getStart() < MIN_VALUE && SECTION.getStop() < MAX_VALUE) {
SECTION_START_ANGLE = 0;
} else {
SECTION_START_ANGLE = (SECTION.getStart() - MIN_VALUE) * angleStep;
}
final double SECTION_ANGLE_EXTEND;
if (SECTION.getStop() > MAX_VALUE) {
SECTION_ANGLE_EXTEND = (MAX_VALUE - SECTION.getStart()) * angleStep;
} else {
SECTION_ANGLE_EXTEND = (SECTION.getStop() - SECTION.getStart()) * angleStep;
}
CTX.save();
if (highlightSections) {
CTX.setStroke(SECTION.contains(value) ? SECTION.getHighlightColor() : SECTION.getColor());
} else {
CTX.setStroke(SECTION.getColor());
}
CTX.setLineWidth(height * 0.0415);
CTX.setLineCap(StrokeLineCap.BUTT);
CTX.strokeArc(x, y, w, h, -(OFFSET + SECTION_START_ANGLE), -SECTION_ANGLE_EXTEND, ArcType.OPEN);
CTX.restore();
}
}
}
private void drawLed(final GraphicsContext CTX) {
CTX.clearRect(0, 0, ledSize, ledSize);
CTX.setFill(frameGradient);
CTX.fillOval(0, 0, ledSize, ledSize);
CTX.save();
if (gauge.isLedOn()) {
CTX.setEffect(ledOnShadow);
CTX.setFill(ledOnGradient);
} else {
CTX.setEffect(ledOffShadow);
CTX.setFill(ledOffGradient);
}
CTX.fillOval(0.14 * ledSize, 0.14 * ledSize, 0.72 * ledSize, 0.72 * ledSize);
CTX.restore();
CTX.setFill(highlightGradient);
CTX.fillOval(0.21 * ledSize, 0.21 * ledSize, 0.58 * ledSize, 0.58 * ledSize);
}
private double clamp(final double MIN_VALUE, final double MAX_VALUE, final double VALUE) {
if (VALUE < MIN_VALUE) return MIN_VALUE;
if (VALUE > MAX_VALUE) return MAX_VALUE;
return VALUE;
}
private void resizeStaticText() {
double maxWidth = width * 0.3;
double fontSize = height * 0.15;
unitText.setFont(Fonts.robotoMedium(fontSize));
unitText.setText(gauge.getUnit());
if ( unitText.getLayoutBounds().getWidth() > maxWidth ) {
Helper.adjustTextSize(unitText, maxWidth, fontSize);
}
unitText.setTranslateX((width - unitText.getLayoutBounds().getWidth()) * 0.5);
unitText.setTranslateY(height * 0.55);
}
private void resizeText() {
resizeStaticText();
if (gauge.isLcdVisible()) {
lcdText.setPadding(new Insets(0, 0.005 * width, 0, 0.005 * width));
switch(gauge.getLcdFont()) {
case LCD:
lcdText.setFont(Fonts.digital(0.216 * height));
lcdText.setTranslateY(0.715 * height);
break;
case DIGITAL:
lcdText.setFont(Fonts.digitalReadout(0.210 * height));
lcdText.setTranslateY(0.7 * height);
break;
case DIGITAL_BOLD:
lcdText.setFont(Fonts.digitalReadoutBold(0.210 * height));
lcdText.setTranslateY(0.695 * height);
break;
case ELEKTRA:
lcdText.setFont(Fonts.elektra(0.2232 * height));
lcdText.setTranslateY(0.685 * height);
break;
case STANDARD:
default:
lcdText.setFont(Fonts.robotoMedium(0.18 * height));
lcdText.setTranslateY(0.675 * height);
break;
}
lcdText.setAlignment(Pos.CENTER_RIGHT);
lcdText.setPrefSize(0.3 * width, 0.014 * height);
lcdText.setTranslateX((width - lcdText.getPrefWidth()) * 0.5);
} else {
lcdText.setAlignment(Pos.CENTER);
lcdText.setFont(Fonts.robotoMedium(height * 0.1));
lcdText.setPrefSize(0.3 * width, 0.014 * height);
lcdText.setTranslateY(0.59 * height);
lcdText.setTranslateX((width - lcdText.getLayoutBounds().getWidth()) * 0.5);
}
}
@Override public void dispose() {
gauge.getMarkers().removeListener(markerListener);
gauge.getSections().removeListener(sectionListener);
gauge.currentValueProperty().removeListener(currentValueListener);
needleRotate.angleProperty().removeListener(needleRotateListener);
super.dispose();
}
@Override protected void resize() {
width = gauge.getWidth() - gauge.getInsets().getLeft() - gauge.getInsets().getRight();
height = gauge.getHeight() - gauge.getInsets().getTop() - gauge.getInsets().getBottom();
if (ASPECT_RATIO * width > height) {
width = 1 / (ASPECT_RATIO / height);
} else if (1 / (ASPECT_RATIO / height) > width) {
height = ASPECT_RATIO * width;
}
if (width > 0 && height > 0) {
pane.setMaxSize(width, height);
pane.relocate((gauge.getWidth() - width) * 0.5, (gauge.getHeight() - height) * 0.5);
dropShadow.setRadius(0.01 * height);
dropShadow.setOffsetY(0.01 * height);
ticksAndSectionsCanvas.setWidth(width);
ticksAndSectionsCanvas.setHeight(height);
markerPane.setPrefSize(width, width);
lcd.setWidth(0.3 * width);
lcd.setHeight(0.2 * height);
lcd.setArcWidth(0.0125 * height);
lcd.setArcHeight(0.0125 * height);
lcd.relocate((width - lcd.getWidth()) * 0.5, 0.7 * height);
ledSize = 0.1 * height;
final Color LED_COLOR = gauge.getLedColor();
frameGradient = new LinearGradient(0.14 * ledSize, 0.14 * ledSize,
0.84 * ledSize, 0.84 * ledSize,
false, CycleMethod.NO_CYCLE,
new Stop(0.0, Color.rgb(20, 20, 20, 0.65)),
new Stop(0.15, Color.rgb(20, 20, 20, 0.65)),
new Stop(0.26, Color.rgb(41, 41, 41, 0.65)),
new Stop(0.26, Color.rgb(41, 41, 41, 0.64)),
new Stop(0.85, Color.rgb(200, 200, 200, 0.41)),
new Stop(1.0, Color.rgb(200, 200, 200, 0.35)));
ledOnGradient = new LinearGradient(0.25 * ledSize, 0.25 * ledSize,
0.74 * ledSize, 0.74 * ledSize,
false, CycleMethod.NO_CYCLE,
new Stop(0.0, LED_COLOR.deriveColor(0.0, 1.0, 1.2, 1.0)),
new Stop(0.49, LED_COLOR.deriveColor(0.0, 1.0, 0.5, 1.0)),
new Stop(1.0, LED_COLOR));
ledOffGradient = new LinearGradient(0.25 * ledSize, 0.25 * ledSize,
0.74 * ledSize, 0.74 * ledSize,
false, CycleMethod.NO_CYCLE,
new Stop(0.0, LED_COLOR.deriveColor(0.0, 1.0, 0.20, 1.0)),
new Stop(0.49, LED_COLOR.deriveColor(0.0, 1.0, 0.13, 1.0)),
new Stop(1.0, LED_COLOR.deriveColor(0.0, 1.0, 0.2, 1.0)));
highlightGradient = new RadialGradient(0, 0,
0.3 * ledSize, 0.3 * ledSize,
0.29 * ledSize,
false, CycleMethod.NO_CYCLE,
new Stop(0.0, Color.WHITE),
new Stop(1.0, Color.TRANSPARENT));
ledCanvas.setWidth(ledSize);
ledCanvas.setHeight(ledSize);
ledCanvas.relocate(0.05 * width, 0.10 * height);
ledOffShadow = new InnerShadow(BlurType.TWO_PASS_BOX, Color.rgb(0, 0, 0, 0.65), 0.07 * ledSize, 0, 0, 0);
ledOnShadow = new InnerShadow(BlurType.TWO_PASS_BOX, Color.rgb(0, 0, 0, 0.65), 0.07 * ledSize, 0, 0, 0);
ledOnShadow.setInput(new DropShadow(BlurType.TWO_PASS_BOX, gauge.getLedColor(), 0.36 * ledSize, 0, 0, 0));
double needleWidth = height * 0.02;
double needleHeight = height * 1.175;
needle.setCache(false);
needleMoveTo1.setX(0.25 * needleWidth); needleMoveTo1.setY(0.025423728813559324 * needleHeight);
needleCubicCurveTo2.setControlX1(0.25 * needleWidth); needleCubicCurveTo2.setControlY1(0.00847457627118644 * needleHeight);
needleCubicCurveTo2.setControlX2(0.375 * needleWidth); needleCubicCurveTo2.setControlY2(0);
needleCubicCurveTo2.setX(0.5 * needleWidth); needleCubicCurveTo2.setY(0);
needleCubicCurveTo3.setControlX1(0.625 * needleWidth); needleCubicCurveTo3.setControlY1(0);
needleCubicCurveTo3.setControlX2(0.75 * needleWidth); needleCubicCurveTo3.setControlY2(0.00847457627118644 * needleHeight);
needleCubicCurveTo3.setX(0.75 * needleWidth); needleCubicCurveTo3.setY(0.025423728813559324 * needleHeight);
needleCubicCurveTo4.setControlX1(0.75 * needleWidth); needleCubicCurveTo4.setControlY1(0.025423728813559324 * needleHeight);
needleCubicCurveTo4.setControlX2(needleWidth); needleCubicCurveTo4.setControlY2(needleHeight);
needleCubicCurveTo4.setX(needleWidth); needleCubicCurveTo4.setY(needleHeight);
needleLineTo5.setX(0); needleLineTo5.setY(needleHeight);
needleCubicCurveTo6.setControlX1(0); needleCubicCurveTo6.setControlY1(needleHeight);
needleCubicCurveTo6.setControlX2(0.25 * needleWidth); needleCubicCurveTo6.setControlY2(0.025423728813559324 * needleHeight);
needleCubicCurveTo6.setX(0.25 * needleWidth); needleCubicCurveTo6.setY(0.025423728813559324 * needleHeight);
needle.setCache(true);
needle.setCacheHint(CacheHint.ROTATE);
LinearGradient needleGradient = new LinearGradient(needle.getLayoutBounds().getMinX(), 0,
needle.getLayoutBounds().getMaxX(), 0,
false, CycleMethod.NO_CYCLE,
new Stop(0.0, gauge.getNeedleColor()),
new Stop(0.5, gauge.getNeedleColor()),
new Stop(0.5, gauge.getNeedleColor().brighter().brighter()),
new Stop(1.0, gauge.getNeedleColor().brighter().brighter()));
needle.setFill(needleGradient);
needle.setStrokeWidth(0);
needle.setStroke(Color.TRANSPARENT);
needle.relocate((width - needle.getLayoutBounds().getWidth()) * 0.5, height * 1.4 - needle.getLayoutBounds().getHeight());
needleRotate.setPivotX(needle.getLayoutBounds().getWidth() * 0.5);
needleRotate.setPivotY(needle.getLayoutBounds().getHeight());
shadowGroup.setClip(new Rectangle(width, height));
resizeText();
}
}
@Override protected void redraw() {
locale = gauge.getLocale();
formatString = new StringBuilder("%.").append(Integer.toString(gauge.getDecimals())).append("f").toString();
Color backgroundColor = gauge.getBackgroundPaint() instanceof Color ? (Color) gauge.getBackgroundPaint() : Color.WHITE;
ticksAndSectionsCanvas.setCache(false);
ticksAndSections.clearRect(0, 0, width, height);
ticksAndSections.setFill(backgroundColor);
ticksAndSections.fillRect(0, 0, width, height);
if (gauge.getSectionsVisible()) drawSections(ticksAndSections);
drawTickMarks(ticksAndSections);
ticksAndSectionsCanvas.setCache(true);
ticksAndSectionsCanvas.setCacheHint(CacheHint.QUALITY);
unitText.setFill(gauge.getUnitColor());
unitText.setText(gauge.getUnit());
if (gauge.isLcdVisible()) {
LcdDesign lcdDesign = gauge.getLcdDesign();
Color[] lcdColors = lcdDesign.getColors();
LinearGradient lcdGradient = new LinearGradient(0, 1, 0, lcd.getHeight() - 1,
false, CycleMethod.NO_CYCLE,
new Stop(0, lcdColors[0]),
new Stop(0.03, lcdColors[1]),
new Stop(0.5, lcdColors[2]),
new Stop(0.5, lcdColors[3]),
new Stop(1.0, lcdColors[4]));
Paint lcdFramePaint;
if (lcdDesign.name().startsWith("FLAT")) {
lcdFramePaint = Color.WHITE;
} else {
lcdFramePaint = new LinearGradient(0, 0, 0, lcd.getHeight(),
false, CycleMethod.NO_CYCLE,
new Stop(0.0, Color.rgb(26, 26, 26)),
new Stop(0.01, Color.rgb(77, 77, 77)),
new Stop(0.99, Color.rgb(77, 77, 77)),
new Stop(1.0, Color.rgb(221, 221, 221)));
}
lcd.setFill(lcdGradient);
lcd.setStroke(lcdFramePaint);
lcdText.setTextFill(lcdColors[5]);
}
if (gauge.isLedVisible()) drawLed(led);
shadowGroup.setEffect(gauge.isShadowsEnabled() ? dropShadow : null);
// Markers
drawMarkers();
thresholdTooltip.setText("Threshold\n(" + String.format(locale, formatString, gauge.getThreshold()) + ")");
}
}