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.BulletChartSkin 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.Marker;
import eu.hansolo.medusa.Section;
import eu.hansolo.medusa.tools.Helper;
import javafx.beans.InvalidationListener;
import javafx.collections.ListChangeListener;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.VPos;
import javafx.scene.CacheHint;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import static eu.hansolo.medusa.tools.Helper.formatNumber;
/**
* Created by hansolo on 25.12.15.
*/
public class BulletChartSkin extends GaugeSkinBase {
private double preferredWidth = 400;
private double preferredHeight = 64;
private Pane pane;
private double width;
private double height;
private double aspectRatio;
private Orientation orientation;
private Canvas tickMarkCanvas;
private GraphicsContext tickMarksCtx;
private Canvas sectionsCanvas;
private GraphicsContext sectionsCtx;
private Text titleText;
private Text unitText;
private Rectangle barRect;
private Rectangle thresholdRect;
private double stepSize;
private Tooltip barTooltip;
private Tooltip thresholdTooltip;
private ListChangeListener sectionListener;
private ListChangeListener markerListener;
private InvalidationListener currentValueListener;
private InvalidationListener paneSizeListener;
// ******************** Constructors **************************************
public BulletChartSkin(Gauge gauge) {
super(gauge);
if (gauge.isAutoScale()) gauge.calcAutoScale();
orientation = gauge.getOrientation();
barTooltip = new Tooltip();
thresholdTooltip = new Tooltip();
sectionListener = c -> redraw();
markerListener = c -> redraw();
currentValueListener = o -> updateBar();
paneSizeListener = o -> handleEvents("RESIZE");
if (Orientation.VERTICAL == orientation) {
preferredWidth = 64;
preferredHeight = 400;
} else {
preferredWidth = 400;
preferredHeight = 64;
}
gauge.setPrefSize(preferredWidth, preferredHeight);
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(preferredWidth, preferredHeight);
}
}
orientation = gauge.getOrientation();
aspectRatio = preferredHeight / preferredWidth;
titleText = new Text(gauge.getTitle());
titleText.setTextOrigin(VPos.CENTER);
Helper.enableNode(titleText, !gauge.getTitle().isEmpty());
unitText = new Text(gauge.getUnit());
unitText.setTextOrigin(VPos.CENTER);
Helper.enableNode(unitText, !gauge.getUnit().isEmpty());
tickMarkCanvas = new Canvas(0.79699248 * preferredWidth, 0.08333333 * preferredHeight);
tickMarksCtx = tickMarkCanvas.getGraphicsContext2D();
sectionsCanvas = new Canvas(0.79699248 * preferredWidth, 0.5 * preferredHeight);
sectionsCtx = sectionsCanvas.getGraphicsContext2D();
barRect = new Rectangle();
Tooltip.install(barRect, barTooltip);
thresholdRect = new Rectangle();
Tooltip.install(thresholdRect, thresholdTooltip);
pane = new Pane(titleText,
unitText,
tickMarkCanvas,
sectionsCanvas,
barRect,
thresholdRect);
pane.setBorder(new Border(new BorderStroke(gauge.getBorderPaint(), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(gauge.getBorderWidth()))));
pane.setBackground(new Background(new BackgroundFill(gauge.getBackgroundPaint(), CornerRadii.EMPTY, Insets.EMPTY)));
getChildren().setAll(pane);
}
@Override protected void registerListeners() {
super.registerListeners();
gauge.getSections().addListener(sectionListener);
gauge.getTickMarkSections().addListener(sectionListener);
gauge.getTickLabelSections().addListener(sectionListener);
gauge.getMarkers().addListener(markerListener);
gauge.currentValueProperty().addListener(currentValueListener);
pane.widthProperty().addListener(paneSizeListener);
pane.heightProperty().addListener(paneSizeListener);
}
// ******************** Methods *******************************************
@Override protected void handleEvents(final String EVENT_TYPE) {
if ("RESIZE".equals(EVENT_TYPE)) {
resize();
redraw();
}else if ("REDRAW".equals(EVENT_TYPE)) {
redraw();
} else if ("VISIBILITY".equals(EVENT_TYPE)) {
Helper.enableNode(titleText, !gauge.getTitle().isEmpty());
Helper.enableNode(unitText, !gauge.getUnit().isEmpty());
redraw();
} else if ("RESIZE".equals(EVENT_TYPE)) {
resize();
} else if ("RECALC".equals(EVENT_TYPE)) {
if (Orientation.VERTICAL == orientation) {
width = height / aspectRatio;
stepSize = (0.79699248 * height) / gauge.getRange();
} else {
height = width / aspectRatio;
stepSize = (0.79699248 * width) / gauge.getRange();
}
resize();
redraw();
updateBar();
} else if ("FINISHED".equals(EVENT_TYPE)) {
barTooltip.setText(formatNumber(gauge.getFormatString(), gauge.getDecimals(), gauge.getValue()));
}
}
@Override public void dispose() {
gauge.getSections().removeListener(sectionListener);
gauge.getTickMarkSections().removeListener(sectionListener);
gauge.getTickLabelSections().removeListener(sectionListener);
gauge.getMarkers().removeListener(markerListener);
gauge.currentValueProperty().removeListener(currentValueListener);
pane.widthProperty().removeListener(paneSizeListener);
pane.heightProperty().removeListener(paneSizeListener);
super.dispose();
}
// ******************** Private Methods ***********************************
private void drawTickMarks(final GraphicsContext CTX) {
tickMarkCanvas.setCache(false);
CTX.clearRect(0, 0, tickMarkCanvas.getWidth(), tickMarkCanvas.getHeight());
CTX.setFill(gauge.getMajorTickMarkColor());
List tickMarkSections = gauge.getTickMarkSections();
List tickLabelSections = gauge.getTickLabelSections();
Color majorTickMarkColor = gauge.getTickMarkColor();
Color tickLabelColor = gauge.getTickLabelColor();
boolean smallRange = Double.compare(gauge.getRange(), 10.0) <= 0;
double minValue = gauge.getMinValue();
double maxValue = gauge.getMaxValue();
double tmpStepSize = smallRange ? stepSize / 10 : stepSize;
Font tickLabelFont = Fonts.robotoRegular(0.15 * (Orientation.VERTICAL == orientation ? width : height));
boolean tickMarkSectionsVisible = gauge.getTickMarkSectionsVisible();
boolean tickLabelSectionsVisible = gauge.getTickLabelSectionsVisible();
double offsetX = 0.18345865 * width;
double offsetY = 0.1 * height;
double innerPointX = 0;
double innerPointY = 0;
double outerPointX = 0.07 * width;
double outerPointY = 0.08 * height;
double textPointX = 0.55 * tickMarkCanvas.getWidth();
double textPointY = 0.7 * tickMarkCanvas.getHeight();
BigDecimal minorTickSpaceBD = BigDecimal.valueOf(gauge.getMinorTickSpace());
BigDecimal majorTickSpaceBD = BigDecimal.valueOf(gauge.getMajorTickSpace());
BigDecimal counterBD = BigDecimal.valueOf(gauge.getMinValue());
double counter = minValue;
double range = gauge.getRange();
for (double i = 0 ; Double.compare(i, range) <= 0 ; i++) {
if (Orientation.VERTICAL == orientation) {
innerPointY = counter * tmpStepSize + offsetY;
outerPointY = innerPointY;
textPointY = innerPointY;
} else {
innerPointX = counter * tmpStepSize + offsetX;
outerPointX = innerPointX;
textPointX = innerPointX;
}
// Set the general tickmark color
CTX.setStroke(gauge.getTickMarkColor());
if (Double.compare(counterBD.remainder(majorTickSpaceBD).doubleValue(), 0.0) == 0) {
// Draw major tick mark
if (gauge.getMajorTickMarksVisible()) {
CTX.setFill(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, majorTickMarkColor) : majorTickMarkColor);
CTX.setStroke(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, majorTickMarkColor) : majorTickMarkColor);
CTX.setLineWidth(1);
CTX.strokeLine(innerPointX, innerPointY, outerPointX, outerPointY);
}
// Draw tick label text
if (gauge.getTickLabelsVisible()) {
CTX.save();
CTX.translate(textPointX, textPointY);
CTX.setFont(tickLabelFont);
CTX.setTextAlign(TextAlignment.CENTER);
CTX.setTextBaseline(VPos.CENTER);
CTX.setFill(tickLabelSectionsVisible ? Helper.getColorOfSection(tickLabelSections, counter, tickLabelColor) : tickLabelColor);
if (Orientation.VERTICAL == orientation) {
CTX.fillText(Integer.toString((int) (maxValue - counter)), 0, 0);
} else {
CTX.fillText(Integer.toString((int) counter), 0, 0);
}
CTX.restore();
}
}
counterBD = counterBD.add(minorTickSpaceBD);
counter = counterBD.doubleValue();
if (counter > maxValue) break;
}
tickMarkCanvas.setCache(true);
tickMarkCanvas.setCacheHint(CacheHint.QUALITY);
}
private void drawSections(final GraphicsContext CTX) {
sectionsCanvas.setCache(false);
CTX.clearRect(0, 0, sectionsCanvas.getWidth(), sectionsCanvas.getHeight());
CTX.setFill(gauge.getBackgroundPaint());
if (Orientation.VERTICAL == orientation) {
CTX.fillRect(0, 0, 0.5 * width, 0.89 * height);
} else {
CTX.fillRect(0, 0, 0.79699248 * width, 0.5 * height);
}
double minValue = gauge.getMinValue();
double maxValue = gauge.getMaxValue();
int listSize = gauge.getSections().size();
for (int i = 0 ; i < listSize ; i++) {
final Section SECTION = gauge.getSections().get(i);
final double SECTION_START;
if (Double.compare(SECTION.getStart(), maxValue) <= 0 && Double.compare(SECTION.getStop(), minValue) >= 0) {
if (Double.compare(SECTION.getStart(), minValue) < 0 && Double.compare(SECTION.getStop(), maxValue) < 0) {
SECTION_START = minValue * stepSize;
} else {
SECTION_START = (SECTION.getStart() - minValue) * stepSize;
}
final double SECTION_SIZE;
if (Double.compare(SECTION.getStop(), maxValue) > 0) {
SECTION_SIZE = (maxValue - SECTION.getStart()) * stepSize;
} else {
SECTION_SIZE = (SECTION.getStop() - SECTION.getStart()) * stepSize;
}
CTX.save();
CTX.setFill(SECTION.getColor());
if (Orientation.VERTICAL == orientation) {
CTX.fillRect(0.0, 0.89 * height - SECTION_START - SECTION_SIZE, 0.5 * width, SECTION_SIZE);
} else {
CTX.fillRect(SECTION_START, 0.0, SECTION_SIZE, 0.5 * height);
}
CTX.restore();
}
}
sectionsCanvas.setCache(true);
sectionsCanvas.setCacheHint(CacheHint.QUALITY);
}
private void updateBar() {
double currentValue = gauge.getCurrentValue();
if (Orientation.VERTICAL == orientation) {
barRect.setY(height - 0.06 * width - currentValue * stepSize);
barRect.setHeight(currentValue * stepSize);
thresholdRect.setY(height - gauge.getThreshold() * stepSize - 0.08625 * width);
} else {
barRect.setWidth(currentValue * stepSize);
thresholdRect.setX(gauge.getThreshold() * stepSize - 0.03125 * height + 0.1835 * width);
}
}
@Override protected void resize() {
width = gauge.getWidth() - gauge.getInsets().getLeft() - gauge.getInsets().getRight();
height = gauge.getHeight() - gauge.getInsets().getTop() - gauge.getInsets().getBottom();
double currentValue = gauge.getCurrentValue();
orientation = gauge.getOrientation();
if (Orientation.VERTICAL == orientation) {
width = height / aspectRatio;
stepSize = (0.89 * height) / gauge.getRange();
pane.setMaxSize(width, height);
pane.relocate((gauge.getWidth() - width) * 0.5, (gauge.getHeight() - height) * 0.5);
width = pane.getLayoutBounds().getWidth();
height = pane.getLayoutBounds().getHeight();
tickMarkCanvas.setWidth(0.39 * width);
tickMarkCanvas.setHeight(height);
tickMarkCanvas.relocate(0.578125 * width, 0);
sectionsCanvas.setWidth(0.5 * width);
sectionsCanvas.setHeight(0.89 * height);
sectionsCanvas.relocate(0.078125 * width, 0.1 * height);
barRect.setWidth(0.16666667 * width);
barRect.setHeight(currentValue * stepSize);
barRect.setX(0.078125 * width + (0.5 * width - barRect.getWidth()) * 0.5);
barRect.setY(height - currentValue * stepSize);
thresholdRect.setX(0.16145833 * width);
thresholdRect.setY(height - gauge.getThreshold() * stepSize - 0.03125 * width);
thresholdRect.setWidth(0.33333333 * width);
thresholdRect.setHeight(0.0625 * width);
double maxTextWidth = width;
titleText.setFont(Fonts.robotoRegular(0.24 * width));
if (titleText.getLayoutBounds().getWidth() > maxTextWidth) { Helper.adjustTextSize(titleText, maxTextWidth, 0.24 * width); }
titleText.relocate((width - titleText.getLayoutBounds().getWidth()) * 0.5, 0.03 * width);
unitText.setFont(Fonts.robotoRegular(0.15 * width));
if (unitText.getLayoutBounds().getWidth() > maxTextWidth) { Helper.adjustTextSize(unitText, maxTextWidth, 0.15 * width); }
unitText.relocate((width - unitText.getLayoutBounds().getWidth()) * 0.5, 0.35 * width);
} else {
height = width * aspectRatio;
stepSize = (0.79699248 * width) / gauge.getRange();
pane.setMaxSize(width, height);
pane.relocate((gauge.getWidth() - width) * 0.5, (gauge.getHeight() - height) * 0.5);
width = pane.getLayoutBounds().getWidth();
height = pane.getLayoutBounds().getHeight();
tickMarkCanvas.setWidth(width);
tickMarkCanvas.setHeight(0.29166667 * height);
tickMarkCanvas.relocate(0, 0.60416667 * height);
sectionsCanvas.setWidth(0.79699248 * width);
sectionsCanvas.setHeight(0.5 * height);
sectionsCanvas.relocate(0.18345865 * width, 0.10416667 * height);
barRect.setWidth(currentValue * stepSize);
barRect.setHeight(0.16666667 * height);
barRect.setX(0.18345865 * width);
barRect.setY(0.10416667 * height + (0.5 * height - barRect.getHeight()) * 0.5);
thresholdRect.setX(gauge.getThreshold() * stepSize - 0.03125 * height + 0.20300752 * width);
thresholdRect.setY(0.1875 * height);
thresholdRect.setWidth(0.0625 * height);
thresholdRect.setHeight(0.33333333 * height);
double maxTextWidth = 0.20300752 * width;
titleText.setFont(Fonts.robotoMedium(0.24 * height));
if (titleText.getLayoutBounds().getWidth() > maxTextWidth) { Helper.adjustTextSize(titleText, maxTextWidth, 0.24 * width); }
titleText.relocate(0.17593985 * width - (titleText.getLayoutBounds().getWidth()), 0.075 * height);
unitText.setFont(Fonts.robotoRegular(0.15 * height));
if (unitText.getLayoutBounds().getWidth() > maxTextWidth) { Helper.adjustTextSize(unitText, maxTextWidth, 0.15 * width); }
unitText.relocate(0.17593985 * width - (unitText.getLayoutBounds().getWidth()), 0.4 * height);
}
redraw();
}
@Override protected void redraw() {
pane.setBorder(new Border(new BorderStroke(gauge.getBorderPaint(), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(gauge.getBorderWidth()))));
pane.setBackground(new Background(new BackgroundFill(gauge.getBackgroundPaint(), CornerRadii.EMPTY, Insets.EMPTY)));
drawTickMarks(tickMarksCtx);
drawSections(sectionsCtx);
thresholdRect.setFill(gauge.getThresholdColor());
thresholdTooltip.setText(formatNumber(gauge.getFormatString(), gauge.getDecimals(), gauge.getThreshold()));
barRect.setFill(gauge.getBarColor());
titleText.setFill(gauge.getTitleColor());
unitText.setFill(gauge.getUnitColor());
updateBar();
}
}