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

eu.hansolo.medusa.tools.Helper Maven / Gradle / Ivy

There is a newer version: 16.0.0
Show newest version
/*
 * 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.tools;

import eu.hansolo.medusa.Alarm;
import eu.hansolo.medusa.Clock;
import eu.hansolo.medusa.Fonts;
import eu.hansolo.medusa.Gauge;
import eu.hansolo.medusa.Gauge.ScaleDirection;
import eu.hansolo.medusa.Gauge.SkinType;
import eu.hansolo.medusa.Section;
import eu.hansolo.medusa.TickLabelLocation;
import eu.hansolo.medusa.TickLabelOrientation;
import eu.hansolo.medusa.TickMarkType;
import eu.hansolo.medusa.TimeSection;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.ImagePattern;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;

import java.math.BigDecimal;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.ChronoField;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;


/**
 * Created by hansolo on 11.12.15.
 */
public class Helper {
    public static final double MAX_TICK_MARK_LENGTH = 0.125;
    public static final double MAX_TICK_MARK_WIDTH  = 0.02;
    public static final Color  INACTIVE_ALARM_COLOR = Color.rgb(90, 90, 90, 0.5);
    public static final double MIN_FONT_SIZE        = 5;


    public static final  T clamp(final T MIN, final T MAX, final T VALUE) {
        if (VALUE.doubleValue() < MIN.doubleValue()) return MIN;
        if (VALUE.doubleValue() > MAX.doubleValue()) return MAX;
        return VALUE;
    }

    public static final int clamp(final int MIN, final int MAX, final int VALUE) {
        if (VALUE < MIN) return MIN;
        if (VALUE > MAX) return MAX;
        return VALUE;
    }
    public static final long clamp(final long MIN, final long MAX, final long VALUE) {
        if (VALUE < MIN) return MIN;
        if (VALUE > MAX) return MAX;
        return VALUE;
    }
    public static final double clamp(final double MIN, final double MAX, final double VALUE) {
        if (Double.compare(VALUE, MIN) < 0) return MIN;
        if (Double.compare(VALUE, MAX) > 0) return MAX;
        return VALUE;
    }

    public static final double clampMin(final double MIN, final double VALUE) {
        if (VALUE < MIN) return MIN;
        return VALUE;
    }
    public static final double clampMax(final double MAX, final double VALUE) {
        if (VALUE > MAX) return MAX;
        return VALUE;
    }

    public static final double round(final double VALUE, final int PRECISION) {
        final int SCALE = (int) Math.pow(10, PRECISION);
        return (double) Math.round(VALUE * SCALE) / SCALE;
    }

    public static final double roundTo(final double VALUE, final double TARGET) { return TARGET * (Math.round(VALUE / TARGET)); }

    public static final double roundToHalf(final double VALUE) { return Math.round(VALUE * 2) / 2.0; }


    public static final double nearest(final double SMALLER, final double VALUE, final double LARGER) {
        return (VALUE - SMALLER) < (LARGER - VALUE) ? SMALLER : LARGER;
    }

    public static final int roundDoubleToInt(final double VALUE){
        double dAbs = Math.abs(VALUE);
        int    i      = (int) dAbs;
        double result = dAbs - (double) i;
        if (result < 0.5) {
            return VALUE < 0 ? -i : i;
        } else {
            return VALUE < 0 ? -(i + 1) : i + 1;
        }
    }

    public static final double[] calcAutoScale(final double MIN_VALUE, final double MAX_VALUE) {
        double maxNoOfMajorTicks = 10;
        double maxNoOfMinorTicks = 10;
        double niceMinValue;
        double niceMaxValue;
        double niceRange;
        double majorTickSpace;
        double minorTickSpace;
        niceRange      = (calcNiceNumber((MAX_VALUE - MIN_VALUE), false));
        majorTickSpace = calcNiceNumber(niceRange / (maxNoOfMajorTicks - 1), true);
        niceMinValue   = (Math.floor(MIN_VALUE / majorTickSpace) * majorTickSpace);
        niceMaxValue   = (Math.ceil(MAX_VALUE / majorTickSpace) * majorTickSpace);
        minorTickSpace = calcNiceNumber(majorTickSpace / (maxNoOfMinorTicks - 1), true);
        return new double[]{ niceMinValue, niceMaxValue, majorTickSpace, minorTickSpace };
    }

    /**
     * Calculates nice minValue, maxValue and stepSize for given MIN and MAX values
     * @param MIN
     * @param MAX
     * @return array of doubles with [niceMin, niceMax, niceRange, niceStep]
     */
    public static final double[] getNiceScale(final double MIN, final double MAX) {
        return getNiceScale(MIN, MAX, 20);
    }
    /**
     * Calculates nice minValue, maxValue and stepSize for given MIN and MAX values
     * @param MIN
     * @param MAX
     * @param MAX_NO_OF_TICKS
     * @return array of doubles with [niceMin, niceMax, niceRange, niceStep]
     */
    public static final double[] getNiceScale(final double MIN, final double MAX, final int MAX_NO_OF_TICKS) {
        // Minimal increment to avoid round extreme values to be on the edge of the chart
        double minimum = MIN;
        double maximum = MAX;
        double epsilon = (MAX - MIN) / 1e6;
        maximum += epsilon;
        minimum -= epsilon;
        double range = maximum - minimum;

        // Target number of values to be displayed on the Y axis (it may be less)
        int stepCount = MAX_NO_OF_TICKS;
        // First approximation
        double roughStep = range / (stepCount - 1);

        // Set best niceStep for the range
        //double[] goodNormalizedSteps = { 1, 1.5, 2, 2.5, 5, 7.5, 10 }; // keep the 10 at the end
        double[] goodNormalizedSteps = { 1, 2, 5, 10 };

        // Normalize rough niceStep to find the normalized one that fits best
        double stepPower          = Math.pow(10, -Math.floor(Math.log10(Math.abs(roughStep))));
        double normalizedStep     = roughStep * stepPower;
        double goodNormalizedStep = Arrays.stream(goodNormalizedSteps).filter(n -> Double.compare(n, normalizedStep) >= 0).findFirst().getAsDouble();
        double niceStep           = goodNormalizedStep / stepPower;

        // Determine the scale limits based on the chosen niceStep.
        double niceMin = minimum < 0 ? Math.floor(minimum / niceStep) * niceStep : Math.ceil(minimum / niceStep) * niceStep;
        double niceMax = maximum < 0 ? Math.floor(maximum / niceStep) * niceStep : Math.ceil(maximum / niceStep) * niceStep;

        if (MIN % niceStep == 0) { niceMin = MIN; }
        if (MAX % niceStep == 0) { niceMax = MAX; }

        double niceRange = niceMax - niceMin;

        return new double[] { niceMin, niceMax, niceRange, niceStep };
    }

    /**
     * Can be used to implement discrete steps e.g. on a slider.
     * @param MIN_VALUE          The min value of the range
     * @param MAX_VALUE          The max value of the range
     * @param VALUE              The value to snap
     * @param MINOR_TICK_COUNT   The number of ticks between 2 major tick marks
     * @param MAJOR_TICK_UNIT    The distance between 2 major tick marks
     * @return The value snapped to the next tick mark defined by the given parameters
     */
    public static final double snapToTicks(final double MIN_VALUE, final double MAX_VALUE, final double VALUE, final int MINOR_TICK_COUNT, final double MAJOR_TICK_UNIT) {
        double v = VALUE;
        int    minorTickCount = clamp(0, 10, MINOR_TICK_COUNT);
        double majorTickUnit  = Double.compare(MAJOR_TICK_UNIT, 0.0) <= 0 ? 0.25 : MAJOR_TICK_UNIT;
        double tickSpacing;

        if (minorTickCount != 0) {
            tickSpacing = majorTickUnit / (Math.max(minorTickCount, 0) + 1);
        } else {
            tickSpacing = majorTickUnit;
        }

        int    prevTick      = (int) ((v - MIN_VALUE) / tickSpacing);
        double prevTickValue = prevTick * tickSpacing + MIN_VALUE;
        double nextTickValue = (prevTick + 1) * tickSpacing + MIN_VALUE;

        v = nearest(prevTickValue, v, nextTickValue);

        return clamp(MIN_VALUE, MAX_VALUE, v);
    }

    /**
     * Returns a "niceScaling" number approximately equal to the range.
     * Rounds the number if ROUND == true.
     * Takes the ceiling if ROUND = false.
     *
     * @param RANGE the value range (maxValue - minValue)
     * @param ROUND whether to round the result or ceil
     * @return a "niceScaling" number to be used for the value range
     */
    public static final double calcNiceNumber(final double RANGE, final boolean ROUND) {
        double niceFraction;
        double exponent = Math.floor(Math.log10(RANGE));   // exponent of range
        double fraction = RANGE / Math.pow(10, exponent);  // fractional part of range

        if (ROUND) {
            if (fraction < 1.5) {
                niceFraction = 1;
            } else if (fraction < 3) {
                niceFraction = 2;
            } else if (fraction < 7) {
                niceFraction = 5;
            } else {
                niceFraction = 10;
            }
        } else {
            if (fraction <= 1) {
                niceFraction = 1;
            } else if (fraction <= 2) {
                niceFraction = 2;
            } else if (fraction <= 5) {
                niceFraction = 5;
            } else {
                niceFraction = 10;
            }
        }
        return niceFraction * Math.pow(10, exponent);
    }

    public static final Color getColorOfSection(final List
SECTIONS, final double VALUE, final Color DEFAULT_COLOR) { for (Section section : SECTIONS) { if (section.contains(VALUE)) return section.getColor(); } return DEFAULT_COLOR; } public static final void rotateContextForText(final GraphicsContext CTX, final double START_ANGLE, final double ANGLE, final TickLabelOrientation ORIENTATION) { switch (ORIENTATION) { 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; } } public static final void adjustTextSize(final Text TEXT, final double MAX_WIDTH, final double FONT_SIZE) { final String FONT_NAME = TEXT.getFont().getName(); double adjustableFontSize = FONT_SIZE; while (TEXT.getLayoutBounds().getWidth() > MAX_WIDTH && adjustableFontSize > MIN_FONT_SIZE) { adjustableFontSize -= 0.1; TEXT.setFont(new Font(FONT_NAME, adjustableFontSize)); } } public static final void adjustTextSize(final Label TEXT, final double MAX_WIDTH, final double FONT_SIZE) { final String FONT_NAME = TEXT.getFont().getName(); double adjustableFontSize = FONT_SIZE; while (TEXT.getLayoutBounds().getWidth() > MAX_WIDTH && adjustableFontSize > MIN_FONT_SIZE) { adjustableFontSize -= 0.1; TEXT.setFont(new Font(FONT_NAME, adjustableFontSize)); } } public static final DateTimeFormatter getDateFormat(final Locale LOCALE) { if (Locale.US == LOCALE) { return DateTimeFormatter.ofPattern("MM/dd/YYYY"); } else if (Locale.CHINA == LOCALE) { return DateTimeFormatter.ofPattern("YYYY.MM.dd"); } else { return DateTimeFormatter.ofPattern("dd.MM.YYYY"); } } public static final DateTimeFormatter getLocalizedDateFormat(final Locale LOCALE) { return DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(LOCALE); } public static final void enableNode(final Node NODE, final boolean ENABLE) { NODE.setManaged(ENABLE); NODE.setVisible(ENABLE); } public static final String colorToCss(final Color COLOR) { return COLOR.toString().replace("0x", "#"); } public static final ThreadFactory getThreadFactory(final String THREAD_NAME, final boolean IS_DAEMON) { return runnable -> { Thread thread = new Thread(runnable, THREAD_NAME); thread.setDaemon(IS_DAEMON); return thread; }; } public static final void stopTask(ScheduledFuture task) { if (null == task) return; task.cancel(true); task = null; } public static final ImagePattern createCarbonPattern() { final double SIZE = 12; final Canvas CANVAS = new Canvas(SIZE, SIZE); final GraphicsContext CTX = CANVAS.getGraphicsContext2D(); CTX.setFill(new LinearGradient(0, 0, 0, 0.5 * SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(35, 35, 35)), new Stop(1, Color.rgb(23, 23, 23)))); CTX.fillRect(0, 0, SIZE * 0.5, SIZE * 0.5); CTX.setFill(new LinearGradient(0, 0, 0, 0.416666 * SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(38, 38, 38)), new Stop(1, Color.rgb(30, 30, 30)))); CTX.fillRect(SIZE * 0.083333, 0, SIZE * 0.333333, SIZE * 0.416666); CTX.setFill(new LinearGradient(0, 0.5 * SIZE, 0, SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(35, 35, 35)), new Stop(1, Color.rgb(23, 23, 23)))); CTX.fillRect(SIZE * 0.5, SIZE * 0.5, SIZE * 0.5, SIZE * 0.5); CTX.setFill(new LinearGradient(0, 0.5 * SIZE, 0, 0.916666 * SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(38, 38, 38)), new Stop(1, Color.rgb(30, 30, 30)))); CTX.fillRect(SIZE * 0.583333, SIZE * 0.5, SIZE * 0.333333, SIZE * 0.416666); CTX.setFill(new LinearGradient(0, 0, 0, 0.5 * SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(48, 48, 48)), new Stop(1, Color.rgb(40, 40, 40)))); CTX.fillRect(SIZE * 0.5, 0, SIZE * 0.5, SIZE * 0.5); CTX.setFill(new LinearGradient(0, 0.083333 * SIZE, 0, 0.5 * SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(53, 53, 53)), new Stop(1, Color.rgb(45, 45, 45)))); CTX.fillRect(SIZE * 0.583333, SIZE * 0.083333, SIZE * 0.333333, SIZE * 0.416666); CTX.setFill(new LinearGradient(0, 0.5 * SIZE, 0, SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(48, 48, 48)), new Stop(1, Color.rgb(40, 40, 40)))); CTX.fillRect(0, SIZE * 0.5, SIZE * 0.5, SIZE * 0.5); CTX.setFill(new LinearGradient(0, 0.583333 * SIZE, 0, SIZE, false, CycleMethod.NO_CYCLE, new Stop(0, Color.rgb(53, 53, 53)), new Stop(1, Color.rgb(45, 45, 45)))); CTX.fillRect(SIZE * 0.083333, SIZE * 0.583333, SIZE * 0.333333, SIZE * 0.416666); final Image PATTERN_IMAGE = CANVAS.snapshot(new SnapshotParameters(), null); final ImagePattern PATTERN = new ImagePattern(PATTERN_IMAGE, 0, 0, SIZE, SIZE, false); return PATTERN; } public static final void drawTrapezoid(final GraphicsContext CTX, final double PI1X, final double PI1Y, final double PI2X, final double PI2Y, final double PO1X, final double PO1Y, final double PO2X, final double PO2Y) { CTX.beginPath(); CTX.moveTo(PI2X, PI2Y); CTX.lineTo(PI1X, PI1Y); CTX.lineTo(PO1X, PO1Y); CTX.lineTo(PO2X, PO2Y); CTX.closePath(); CTX.fill(); } public static final void drawTriangle(final GraphicsContext CTX, final double PIX, final double PIY, final double PO1X, final double PO1Y, final double PO2X, final double PO2Y) { CTX.beginPath(); CTX.moveTo(PIX, PIY); CTX.lineTo(PO1X, PO1Y); CTX.lineTo(PO2X, PO2Y); CTX.closePath(); CTX.fill(); } public static final void drawDot(final GraphicsContext CTX, final double CENTER_X, final double CENTER_Y, final double SIZE) { CTX.fillOval(CENTER_X, CENTER_Y, SIZE, SIZE); } public static final void drawLine(final GraphicsContext CTX, final double P1X, final double P1Y, final double P2X, final double P2Y) { CTX.strokeLine(P1X, P1Y, P2X, P2Y); } public static final boolean isMonochrome(final Color COLOR) { return Double.compare(COLOR.getRed(), COLOR.getGreen()) == 0 && Double.compare(COLOR.getGreen(), COLOR.getBlue()) == 0; } public static final double colorDistance(final Color COLOR_1, final Color COLOR_2) { final double DELTA_R = (COLOR_2.getRed() - COLOR_1.getRed()); final double DELTA_G = (COLOR_2.getGreen() - COLOR_1.getGreen()); final double DELTA_B = (COLOR_2.getBlue() - COLOR_1.getBlue()); return Math.sqrt(DELTA_R * DELTA_R + DELTA_G * DELTA_G + DELTA_B * DELTA_B); } public static final boolean isBright(final Color COLOR) { return !isDark(COLOR); } public static final boolean isDark(final Color COLOR) { final double DISTANCE_TO_WHITE = colorDistance(COLOR, Color.WHITE); final double DISTANCE_TO_BLACK = colorDistance(COLOR, Color.BLACK); return DISTANCE_TO_BLACK < DISTANCE_TO_WHITE; } public static final Color getTranslucentColorFrom(final Color COLOR, final double FACTOR) { return Color.color(COLOR.getRed(), COLOR.getGreen(), COLOR.getBlue(), Helper.clamp(0.0, 1.0, FACTOR)); } public static final void drawRadialTickMarks(final Gauge GAUGE, final GraphicsContext CTX, final double MIN_VALUE, final double MAX_VALUE, final double START_ANGLE, final double ANGLE_RANGE, final double ANGLE_STEP, final double CENTER_X, final double CENTER_Y, final double SIZE) { double sinValue; double cosValue; double centerX = CENTER_X; double centerY = CENTER_Y; int tickLabelDecimals = GAUGE.getTickLabelDecimals(); String tickLabelFormatString = "%." + tickLabelDecimals + "f"; double minorTickSpace = GAUGE.getMinorTickSpace(); double tmpAngleStep = ANGLE_STEP * minorTickSpace; TickLabelOrientation tickLabelOrientation = GAUGE.getTickLabelOrientation(); TickLabelLocation tickLabelLocation = GAUGE.getTickLabelLocation(); 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(MIN_VALUE); double counter = MIN_VALUE; List
tickMarkSections = GAUGE.getTickMarkSections(); List
tickLabelSections = GAUGE.getTickLabelSections(); Color tickMarkColor = GAUGE.getTickMarkColor(); Color majorTickMarkColor = GAUGE.getMajorTickMarkColor().equals(tickMarkColor) ? tickMarkColor : GAUGE.getMajorTickMarkColor(); Color mediumTickMarkColor = GAUGE.getMediumTickMarkColor().equals(tickMarkColor) ? tickMarkColor : GAUGE.getMediumTickMarkColor(); Color minorTickMarkColor = GAUGE.getMinorTickMarkColor().equals(tickMarkColor) ? tickMarkColor : GAUGE.getMinorTickMarkColor(); double majorTickMarkLengthFactor = GAUGE.getMajorTickMarkLengthFactor(); double majorTickMarkWidthFactor = GAUGE.getMajorTickMarkWidthFactor(); double mediumTickMarkLengthFactor = GAUGE.getMediumTickMarkLengthFactor(); double mediumTickMarkWidthFactor = GAUGE.getMediumTickMarkWidthFactor(); double minorTickMarkLengthFactor = GAUGE.getMinorTickMarkLengthFactor(); double minorTickMarkWidthFactor = GAUGE.getMinorTickMarkWidthFactor(); Color tickLabelColor = GAUGE.getTickLabelColor(); Color zeroColor = GAUGE.getZeroColor(); boolean isNotZero = true; TickMarkType majorTickMarkType = GAUGE.getMajorTickMarkType(); TickMarkType mediumTickMarkType = GAUGE.getMediumTickMarkType(); TickMarkType minorTickMarkType = GAUGE.getMinorTickMarkType(); boolean tickMarkSectionsVisible = GAUGE.getTickMarkSectionsVisible(); boolean tickLabelSectionsVisible = GAUGE.getTickLabelSectionsVisible(); boolean majorTickMarksVisible = GAUGE.getMajorTickMarksVisible(); boolean mediumTickMarksVisible = GAUGE.getMediumTickMarksVisible(); boolean minorTickMarksVisible = GAUGE.getMinorTickMarksVisible(); boolean tickLabelsVisible = GAUGE.getTickLabelsVisible(); boolean onlyFirstAndLastLabelVisible = GAUGE.isOnlyFirstAndLastTickLabelVisible(); boolean customTickLabelsEnabled = GAUGE.getCustomTickLabelsEnabled(); Locale locale = GAUGE.getLocale(); List customTickLabels = customTickLabelsEnabled ? GAUGE.getCustomTickLabels() : null; double textDisplacementFactor = majorTickMarkType == TickMarkType.DOT ? (TickLabelLocation.OUTSIDE == tickLabelLocation ? 0.95 : 1.05) : 1.0; double majorDotSize; double majorHalfDotSize; double mediumDotSize; double mediumHalfDotSize; double minorDotSize; double minorHalfDotSize; double orthTextFactor; if (TickLabelLocation.OUTSIDE == tickLabelLocation) { orthTextFactor = 0.45 * textDisplacementFactor;//TickLabelOrientation.ORTHOGONAL == tickLabelOrientation ? 0.45 * textDisplacementFactor : 0.45 * textDisplacementFactor; majorDotSize = 0.02 * SIZE; majorHalfDotSize = majorDotSize * 0.5; mediumDotSize = 0.01375 * SIZE; mediumHalfDotSize = mediumDotSize * 0.5; minorDotSize = 0.0075 * SIZE; minorHalfDotSize = minorDotSize * 0.5; } else { orthTextFactor = TickLabelOrientation.ORTHOGONAL == tickLabelOrientation ? 0.38 * textDisplacementFactor : 0.37 * textDisplacementFactor; majorDotSize = 0.025 * SIZE; majorHalfDotSize = majorDotSize * 0.5; mediumDotSize = 0.01875 * SIZE; mediumHalfDotSize = mediumDotSize * 0.5; minorDotSize = 0.0125 * SIZE; minorHalfDotSize = minorDotSize * 0.5; }; double customFontSizeFactor = GAUGE.getCustomTickLabelFontSize() / 400; boolean fullRange = (MIN_VALUE < 0 && MAX_VALUE > 0); double tickLabelFontSize = tickLabelDecimals == 0 ? 0.054 * SIZE : 0.051 * SIZE; tickLabelFontSize = GAUGE.getCustomTickLabelsEnabled() ? customFontSizeFactor * SIZE : tickLabelFontSize; double tickMarkFontSize = tickLabelDecimals == 0 ? 0.047 * SIZE: 0.044 * SIZE; double tickLabelOrientationFactor = TickLabelOrientation.HORIZONTAL == tickLabelOrientation ? 0.9 : 1.0; Font tickLabelFont = Fonts.robotoCondensedRegular(tickLabelFontSize * tickLabelOrientationFactor); Font tickMarkFont = Fonts.robotoCondensedRegular(tickMarkFontSize * tickLabelOrientationFactor); Font tickLabelZeroFont = fullRange ? Fonts.robotoCondensedBold(tickLabelFontSize * tickLabelOrientationFactor) : tickLabelFont; Font tickMarkZeroFont = fullRange ? Fonts.robotoCondensedBold(tickMarkFontSize * tickLabelOrientationFactor) : tickMarkFont; // Variables needed for tickmarks double innerPointX; double innerPointY; double innerMediumPointX; double innerMediumPointY; double innerMinorPointX; double innerMinorPointY; double outerPointX; double outerPointY; double outerMediumPointX; double outerMediumPointY; double outerMinorPointX; double outerMinorPointY; double textPointX; double textPointY; double dotCenterX; double dotCenterY; double dotMediumCenterX; double dotMediumCenterY; double dotMinorCenterX; double dotMinorCenterY; double tickLabelTickMarkX; double tickLabelTickMarkY; double trapezoidMajorInnerAngle1; double trapezoidMajorInnerAngle2; double trapezoidMajorOuterAngle1; double trapezoidMajorOuterAngle2; double trapezoidMajorInnerPoint1X; double trapezoidMajorInnerPoint1Y; double trapezoidMajorInnerPoint2X; double trapezoidMajorInnerPoint2Y; double trapezoidMajorOuterPoint1X; double trapezoidMajorOuterPoint1Y; double trapezoidMajorOuterPoint2X; double trapezoidMajorOuterPoint2Y; double trapezoidMediumInnerAngle1; double trapezoidMediumInnerAngle2; double trapezoidMediumOuterAngle1; double trapezoidMediumOuterAngle2; double trapezoidMediumInnerPoint1X; double trapezoidMediumInnerPoint1Y; double trapezoidMediumInnerPoint2X; double trapezoidMediumInnerPoint2Y; double trapezoidMediumOuterPoint1X; double trapezoidMediumOuterPoint1Y; double trapezoidMediumOuterPoint2X; double trapezoidMediumOuterPoint2Y; double trapezoidMinorInnerAngle1; double trapezoidMinorInnerAngle2; double trapezoidMinorOuterAngle1; double trapezoidMinorOuterAngle2; double trapezoidMinorInnerPoint1X; double trapezoidMinorInnerPoint1Y; double trapezoidMinorInnerPoint2X; double trapezoidMinorInnerPoint2Y; double trapezoidMinorOuterPoint1X; double trapezoidMinorOuterPoint1Y; double trapezoidMinorOuterPoint2X; double trapezoidMinorOuterPoint2Y; double triangleMajorInnerPointX; double triangleMajorInnerPointY; double triangleMajorOuterPointX; double triangleMajorOuterPointY; double triangleMediumInnerPointX; double triangleMediumInnerPointY; double triangleMediumOuterPointX; double triangleMediumOuterPointY; double triangleMinorInnerPointX; double triangleMinorInnerPointY; double triangleMinorOuterPointX; double triangleMinorOuterPointY; ScaleDirection scaleDirection = GAUGE.getScaleDirection(); // Draw tickmark ring if (GAUGE.isTickMarkRingVisible()) { SkinType skinType = GAUGE.getSkinType(); double xy = TickLabelLocation.INSIDE == tickLabelLocation ? SIZE * 0.026 : SIZE * 0.14; double wh = TickLabelLocation.INSIDE == tickLabelLocation ? SIZE * 0.948 : SIZE * 0.72; double offset = -90 + START_ANGLE; double horVerOffset = SIZE * 0.055555555; CTX.setLineWidth(SIZE * 0.004); CTX.setLineCap(StrokeLineCap.SQUARE); CTX.save(); CTX.setStroke(tickMarkColor); switch(skinType) { case HORIZONTAL: CTX.strokeArc(xy + horVerOffset, xy, wh, wh, offset, -ANGLE_RANGE, ArcType.OPEN); break; case VERTICAL : CTX.strokeArc(xy, xy + horVerOffset, wh, wh, offset, -ANGLE_RANGE, ArcType.OPEN); break; default : CTX.strokeArc(xy, xy, wh, wh, offset, -ANGLE_RANGE, ArcType.OPEN); break; } CTX.restore(); if (tickMarkSections.size() > 0) { int listSize = tickMarkSections.size(); double sectionStartAngle; for (int i = 0; i < listSize; i++) { Section section = tickMarkSections.get(i); if (Double.compare(section.getStart(), MIN_VALUE) < 0 && Double.compare(section.getStop(), MAX_VALUE) < 0) { sectionStartAngle = 0; } else { sectionStartAngle = ScaleDirection.CLOCKWISE == scaleDirection ? (section.getStart() - MIN_VALUE) * ANGLE_STEP : -(section.getStart() - MIN_VALUE) * ANGLE_STEP; } double sectionAngleExtend; if (Double.compare(section.getStop(), MAX_VALUE) > 0) { sectionAngleExtend = ScaleDirection.CLOCKWISE == scaleDirection ? (MAX_VALUE - section.getStart()) * ANGLE_STEP : -(MAX_VALUE - section.getStart()) * ANGLE_STEP; } else { sectionAngleExtend = ScaleDirection.CLOCKWISE == scaleDirection ? (section.getStop() - section.getStart()) * ANGLE_STEP : -(section.getStop() - section.getStart()) * ANGLE_STEP; } CTX.save(); CTX.setStroke(section.getColor()); switch(skinType) { case HORIZONTAL: CTX.strokeArc(xy + horVerOffset, xy, wh, wh, offset - sectionStartAngle, -sectionAngleExtend, ArcType.OPEN); break; case VERTICAL : CTX.strokeArc(xy, xy + horVerOffset, wh, wh, offset - sectionStartAngle, -sectionAngleExtend, ArcType.OPEN); break; default : CTX.strokeArc(xy, xy, wh, wh, offset - sectionStartAngle, -sectionAngleExtend, ArcType.OPEN); break; } CTX.restore(); } } } // Main loop BigDecimal tmpStepBD = new BigDecimal(tmpAngleStep); tmpStepBD = tmpStepBD.setScale(3, BigDecimal.ROUND_HALF_UP); double tmpStep = tmpStepBD.doubleValue(); double angle = 0; int customTickLabelCounter = 0; for (double i = 0 ; Double.compare(-ANGLE_RANGE - tmpStep, i) <= 0 ; i -= tmpStep) { sinValue = Math.sin(Math.toRadians(angle + START_ANGLE)); cosValue = Math.cos(Math.toRadians(angle + START_ANGLE)); switch(tickLabelLocation) { case OUTSIDE: innerPointX = centerX + SIZE * 0.3585 * sinValue; innerPointY = centerY + SIZE * 0.3585 * cosValue; innerMediumPointX = innerPointX; innerMediumPointY = innerPointY; innerMinorPointX = innerPointX; innerMinorPointY = innerPointY; outerPointX = centerX + SIZE * (0.3585 + MAX_TICK_MARK_LENGTH * majorTickMarkLengthFactor) * sinValue; outerPointY = centerY + SIZE * (0.3585 + MAX_TICK_MARK_LENGTH * majorTickMarkLengthFactor) * cosValue; outerMediumPointX = centerX + SIZE * (0.3585 + MAX_TICK_MARK_LENGTH * mediumTickMarkLengthFactor) * sinValue; outerMediumPointY = centerY + SIZE * (0.3585 + MAX_TICK_MARK_LENGTH * mediumTickMarkLengthFactor) * cosValue; outerMinorPointX = centerX + SIZE * (0.3585 + MAX_TICK_MARK_LENGTH * minorTickMarkLengthFactor) * sinValue; outerMinorPointY = centerY + SIZE * (0.3585 + MAX_TICK_MARK_LENGTH * minorTickMarkLengthFactor) * cosValue; textPointX = centerX + SIZE * orthTextFactor * sinValue; textPointY = centerY + SIZE * orthTextFactor * cosValue; dotCenterX = centerX + SIZE * 0.3685 * sinValue; dotCenterY = centerY + SIZE * 0.3685 * cosValue; dotMediumCenterX = centerX + SIZE * 0.365375 * sinValue; dotMediumCenterY = centerY + SIZE * 0.365375 * cosValue; dotMinorCenterX = centerX + SIZE * 0.36225 * sinValue; dotMinorCenterY = centerY + SIZE * 0.36225 * cosValue; tickLabelTickMarkX = centerX + SIZE * 0.3805 * sinValue; tickLabelTickMarkY = centerY + SIZE * 0.3805 * cosValue; trapezoidMajorInnerAngle1 = Math.toRadians(angle - 1.2 + START_ANGLE); trapezoidMajorInnerAngle2 = Math.toRadians(angle + 1.2 + START_ANGLE); trapezoidMajorOuterAngle1 = Math.toRadians(angle - 0.8 + START_ANGLE); trapezoidMajorOuterAngle2 = Math.toRadians(angle + 0.8 + START_ANGLE); trapezoidMajorInnerPoint1X = centerX + SIZE * 0.3585 * Math.sin(trapezoidMajorInnerAngle1); trapezoidMajorInnerPoint1Y = centerY + SIZE * 0.3585 * Math.cos(trapezoidMajorInnerAngle1); trapezoidMajorInnerPoint2X = centerX + SIZE * 0.3585 * Math.sin(trapezoidMajorInnerAngle2); trapezoidMajorInnerPoint2Y = centerY + SIZE * 0.3585 * Math.cos(trapezoidMajorInnerAngle2); trapezoidMajorOuterPoint1X = centerX + SIZE * 0.4105 * Math.sin(trapezoidMajorOuterAngle1); trapezoidMajorOuterPoint1Y = centerY + SIZE * 0.4105 * Math.cos(trapezoidMajorOuterAngle1); trapezoidMajorOuterPoint2X = centerX + SIZE * 0.4105 * Math.sin(trapezoidMajorOuterAngle2); trapezoidMajorOuterPoint2Y = centerY + SIZE * 0.4105 * Math.cos(trapezoidMajorOuterAngle2); trapezoidMediumInnerAngle1 = Math.toRadians(angle - 1.0 + START_ANGLE); trapezoidMediumInnerAngle2 = Math.toRadians(angle + 1.0 + START_ANGLE); trapezoidMediumOuterAngle1 = Math.toRadians(angle - 0.7 + START_ANGLE); trapezoidMediumOuterAngle2 = Math.toRadians(angle + 0.7 + START_ANGLE); trapezoidMediumInnerPoint1X = centerX + SIZE * 0.3585 * Math.sin(trapezoidMediumInnerAngle1); trapezoidMediumInnerPoint1Y = centerY + SIZE * 0.3585 * Math.cos(trapezoidMediumInnerAngle1); trapezoidMediumInnerPoint2X = centerX + SIZE * 0.3585 * Math.sin(trapezoidMediumInnerAngle2); trapezoidMediumInnerPoint2Y = centerY + SIZE * 0.3585 * Math.cos(trapezoidMediumInnerAngle2); trapezoidMediumOuterPoint1X = centerX + SIZE * 0.3985 * Math.sin(trapezoidMajorOuterAngle1); trapezoidMediumOuterPoint1Y = centerY + SIZE * 0.3985 * Math.cos(trapezoidMediumOuterAngle1); trapezoidMediumOuterPoint2X = centerX + SIZE * 0.3985 * Math.sin(trapezoidMediumOuterAngle2); trapezoidMediumOuterPoint2Y = centerY + SIZE * 0.3985 * Math.cos(trapezoidMediumOuterAngle2); trapezoidMinorInnerAngle1 = Math.toRadians(angle - 0.8 + START_ANGLE); trapezoidMinorInnerAngle2 = Math.toRadians(angle + 0.8 + START_ANGLE); trapezoidMinorOuterAngle1 = Math.toRadians(angle - 0.6 + START_ANGLE); trapezoidMinorOuterAngle2 = Math.toRadians(angle + 0.6 + START_ANGLE); trapezoidMinorInnerPoint1X = centerX + SIZE * 0.3585 * Math.sin(trapezoidMinorInnerAngle1); trapezoidMinorInnerPoint1Y = centerY + SIZE * 0.3585 * Math.cos(trapezoidMinorInnerAngle1); trapezoidMinorInnerPoint2X = centerX + SIZE * 0.3585 * Math.sin(trapezoidMinorInnerAngle2); trapezoidMinorInnerPoint2Y = centerY + SIZE * 0.3585 * Math.cos(trapezoidMinorInnerAngle2); trapezoidMinorOuterPoint1X = centerX + SIZE * 0.3975 * Math.sin(trapezoidMinorOuterAngle1); trapezoidMinorOuterPoint1Y = centerY + SIZE * 0.3975 * Math.cos(trapezoidMinorOuterAngle1); trapezoidMinorOuterPoint2X = centerX + SIZE * 0.3975 * Math.sin(trapezoidMinorOuterAngle2); trapezoidMinorOuterPoint2Y = centerY + SIZE * 0.3975 * Math.cos(trapezoidMinorOuterAngle2); triangleMajorInnerPointX = centerX + SIZE * 0.3585 * sinValue; triangleMajorInnerPointY = centerY + SIZE * 0.3585 * cosValue; triangleMajorOuterPointX = centerX + SIZE * 0.4105 * sinValue; triangleMajorOuterPointY = centerY + SIZE * 0.4105 * cosValue; triangleMediumInnerPointX = triangleMajorInnerPointX; triangleMediumInnerPointY = triangleMajorInnerPointY; triangleMediumOuterPointX = centerX + SIZE * 0.4045 * sinValue; triangleMediumOuterPointY = centerY + SIZE * 0.4045 * cosValue; triangleMinorInnerPointX = triangleMajorInnerPointX; triangleMinorInnerPointY = triangleMajorInnerPointY; triangleMinorOuterPointX = centerX + SIZE * 0.3975 * sinValue; triangleMinorOuterPointY = centerY + SIZE * 0.3975 * cosValue; break; case INSIDE: default: innerPointX = centerX + SIZE * (0.475 - MAX_TICK_MARK_LENGTH * majorTickMarkLengthFactor) * sinValue; innerPointY = centerY + SIZE * (0.475 - MAX_TICK_MARK_LENGTH * majorTickMarkLengthFactor) * cosValue; innerMediumPointX = centerX + SIZE * (0.475 - MAX_TICK_MARK_LENGTH * mediumTickMarkLengthFactor) * sinValue; innerMediumPointY = centerY + SIZE * (0.475 - MAX_TICK_MARK_LENGTH * mediumTickMarkLengthFactor) * cosValue; innerMinorPointX = centerX + SIZE * (0.475 - MAX_TICK_MARK_LENGTH * minorTickMarkLengthFactor) * sinValue; innerMinorPointY = centerY + SIZE * (0.475 - MAX_TICK_MARK_LENGTH * minorTickMarkLengthFactor) * cosValue; outerPointX = centerX + SIZE * 0.475 * sinValue; outerPointY = centerY + SIZE * 0.475 * cosValue; outerMediumPointX = outerPointX; outerMediumPointY = outerPointY; outerMinorPointX = outerPointX; outerMinorPointY = outerPointY; textPointX = centerX + SIZE * orthTextFactor * sinValue; textPointY = centerY + SIZE * orthTextFactor * cosValue; dotCenterX = centerX + SIZE * 0.4625 * sinValue; dotCenterY = centerY + SIZE * 0.4625 * cosValue; dotMediumCenterX = centerX + SIZE * 0.465625 * sinValue; dotMediumCenterY = centerY + SIZE * 0.465625 * cosValue; dotMinorCenterX = centerX + SIZE * 0.46875 * sinValue; dotMinorCenterY = centerY + SIZE * 0.46875 * cosValue; tickLabelTickMarkX = centerX + SIZE * 0.445 * sinValue; tickLabelTickMarkY = centerY + SIZE * 0.445 * cosValue; trapezoidMajorInnerAngle1 = Math.toRadians(angle - 0.8 + START_ANGLE); trapezoidMajorInnerAngle2 = Math.toRadians(angle + 0.8 + START_ANGLE); trapezoidMajorOuterAngle1 = Math.toRadians(angle - 1.2 + START_ANGLE); trapezoidMajorOuterAngle2 = Math.toRadians(angle + 1.2 + START_ANGLE); trapezoidMajorInnerPoint1X = centerX + SIZE * 0.423 * Math.sin(trapezoidMajorInnerAngle1); trapezoidMajorInnerPoint1Y = centerY + SIZE * 0.423 * Math.cos(trapezoidMajorInnerAngle1); trapezoidMajorInnerPoint2X = centerX + SIZE * 0.423 * Math.sin(trapezoidMajorInnerAngle2); trapezoidMajorInnerPoint2Y = centerY + SIZE * 0.423 * Math.cos(trapezoidMajorInnerAngle2); trapezoidMajorOuterPoint1X = centerX + SIZE * 0.475 * Math.sin(trapezoidMajorOuterAngle1); trapezoidMajorOuterPoint1Y = centerY + SIZE * 0.475 * Math.cos(trapezoidMajorOuterAngle1); trapezoidMajorOuterPoint2X = centerX + SIZE * 0.475 * Math.sin(trapezoidMajorOuterAngle2); trapezoidMajorOuterPoint2Y = centerY + SIZE * 0.475 * Math.cos(trapezoidMajorOuterAngle2); trapezoidMediumInnerAngle1 = Math.toRadians(angle - 0.7 + START_ANGLE); trapezoidMediumInnerAngle2 = Math.toRadians(angle + 0.7 + START_ANGLE); trapezoidMediumOuterAngle1 = Math.toRadians(angle - 1.0 + START_ANGLE); trapezoidMediumOuterAngle2 = Math.toRadians(angle + 1.0 + START_ANGLE); trapezoidMediumInnerPoint1X = centerX + SIZE * 0.435 * Math.sin(trapezoidMediumInnerAngle1); trapezoidMediumInnerPoint1Y = centerY + SIZE * 0.435 * Math.cos(trapezoidMediumInnerAngle1); trapezoidMediumInnerPoint2X = centerX + SIZE * 0.435 * Math.sin(trapezoidMediumInnerAngle2); trapezoidMediumInnerPoint2Y = centerY + SIZE * 0.435 * Math.cos(trapezoidMediumInnerAngle2); trapezoidMediumOuterPoint1X = centerX + SIZE * 0.475 * Math.sin(trapezoidMajorOuterAngle1); trapezoidMediumOuterPoint1Y = centerY + SIZE * 0.475 * Math.cos(trapezoidMediumOuterAngle1); trapezoidMediumOuterPoint2X = centerX + SIZE * 0.475 * Math.sin(trapezoidMediumOuterAngle2); trapezoidMediumOuterPoint2Y = centerY + SIZE * 0.475 * Math.cos(trapezoidMediumOuterAngle2); trapezoidMinorInnerAngle1 = Math.toRadians(angle - 0.6 + START_ANGLE); trapezoidMinorInnerAngle2 = Math.toRadians(angle + 0.6 + START_ANGLE); trapezoidMinorOuterAngle1 = Math.toRadians(angle - 0.8 + START_ANGLE); trapezoidMinorOuterAngle2 = Math.toRadians(angle + 0.8 + START_ANGLE); trapezoidMinorInnerPoint1X = centerX + SIZE * 0.440 * Math.sin(trapezoidMinorInnerAngle1); trapezoidMinorInnerPoint1Y = centerY + SIZE * 0.440 * Math.cos(trapezoidMinorInnerAngle1); trapezoidMinorInnerPoint2X = centerX + SIZE * 0.440 * Math.sin(trapezoidMinorInnerAngle2); trapezoidMinorInnerPoint2Y = centerY + SIZE * 0.440 * Math.cos(trapezoidMinorInnerAngle2); trapezoidMinorOuterPoint1X = centerX + SIZE * 0.475 * Math.sin(trapezoidMinorOuterAngle1); trapezoidMinorOuterPoint1Y = centerY + SIZE * 0.475 * Math.cos(trapezoidMinorOuterAngle1); trapezoidMinorOuterPoint2X = centerX + SIZE * 0.475 * Math.sin(trapezoidMinorOuterAngle2); trapezoidMinorOuterPoint2Y = centerY + SIZE * 0.475 * Math.cos(trapezoidMinorOuterAngle2); triangleMajorInnerPointX = centerX + SIZE * 0.423 * sinValue; triangleMajorInnerPointY = centerY + SIZE * 0.423 * cosValue; triangleMajorOuterPointX = centerX + SIZE * 0.475 * sinValue; triangleMajorOuterPointY = centerY + SIZE * 0.475 * cosValue; triangleMediumInnerPointX = centerX + SIZE * 0.43 * sinValue; triangleMediumInnerPointY = centerY + SIZE * 0.43 * cosValue; triangleMediumOuterPointX = triangleMajorOuterPointX; triangleMediumOuterPointY = triangleMajorOuterPointY; triangleMinorInnerPointX = centerX + SIZE * 0.436 * sinValue; triangleMinorInnerPointY = centerY + SIZE * 0.436 * cosValue; triangleMinorOuterPointX = triangleMajorOuterPointX; triangleMinorOuterPointY = triangleMajorOuterPointY; break; } // Set the general tickmark color CTX.setStroke(tickMarkColor); CTX.setFill(tickMarkColor); CTX.setLineCap(StrokeLineCap.BUTT); if (Double.compare(counterBD.remainder(majorTickSpaceBD).doubleValue(), 0.0) == 0) { // Draw major tick mark isNotZero = Double.compare(0.0, counter) != 0; TickMarkType tickMarkType = null; if (majorTickMarksVisible) { tickMarkType = majorTickMarkType; CTX.setFill(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, majorTickMarkColor) : majorTickMarkColor); CTX.setStroke(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, majorTickMarkColor) : majorTickMarkColor); CTX.setLineWidth(SIZE * (TickMarkType.BOX == tickMarkType || TickMarkType.PILL == tickMarkType ? 0.016 : MAX_TICK_MARK_WIDTH * majorTickMarkWidthFactor)); CTX.setLineCap(TickMarkType.PILL == tickMarkType ? StrokeLineCap.ROUND : StrokeLineCap.BUTT); } else if (minorTickMarksVisible) { tickMarkType = minorTickMarkType; CTX.setFill(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, minorTickMarkColor) : minorTickMarkColor); CTX.setStroke(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, minorTickMarkColor) : minorTickMarkColor); CTX.setLineWidth(SIZE * (TickMarkType.BOX == tickMarkType || TickMarkType.PILL == tickMarkType ? 0.007 : MAX_TICK_MARK_WIDTH * minorTickMarkWidthFactor)); CTX.setLineCap(TickMarkType.PILL == tickMarkType ? StrokeLineCap.ROUND : StrokeLineCap.BUTT); } if (fullRange && !isNotZero) { CTX.setFill(zeroColor); CTX.setStroke(zeroColor); } if (null != tickMarkType) { switch (tickMarkType) { case TRAPEZOID: if (majorTickMarksVisible) { Helper.drawTrapezoid(CTX, trapezoidMajorInnerPoint1X, trapezoidMajorInnerPoint1Y, trapezoidMajorInnerPoint2X, trapezoidMajorInnerPoint2Y, trapezoidMajorOuterPoint1X, trapezoidMajorOuterPoint1Y, trapezoidMajorOuterPoint2X, trapezoidMajorOuterPoint2Y); } else if (minorTickMarksVisible) { Helper.drawTrapezoid(CTX, trapezoidMinorInnerPoint1X, trapezoidMinorInnerPoint1Y, trapezoidMinorInnerPoint2X, trapezoidMinorInnerPoint2Y, trapezoidMinorOuterPoint1X, trapezoidMinorOuterPoint1Y, trapezoidMinorOuterPoint2X, trapezoidMinorOuterPoint2Y); } break; case TRIANGLE: if (majorTickMarksVisible) { if (TickLabelLocation.INSIDE == tickLabelLocation) { Helper.drawTriangle(CTX, triangleMajorInnerPointX, triangleMajorInnerPointY, trapezoidMajorOuterPoint1X, trapezoidMajorOuterPoint1Y, trapezoidMajorOuterPoint2X, trapezoidMajorOuterPoint2Y); } else { Helper.drawTriangle(CTX, triangleMajorOuterPointX, triangleMajorOuterPointY, trapezoidMajorInnerPoint1X, trapezoidMajorInnerPoint1Y, trapezoidMajorInnerPoint2X, trapezoidMajorInnerPoint2Y); } } else if (minorTickMarksVisible) { if (TickLabelLocation.INSIDE == tickLabelLocation) { Helper.drawTriangle(CTX, triangleMinorInnerPointX, triangleMinorInnerPointY, trapezoidMinorOuterPoint1X, trapezoidMinorOuterPoint1Y, trapezoidMinorOuterPoint2X, trapezoidMinorOuterPoint2Y); } else { Helper.drawTriangle(CTX, triangleMinorOuterPointX, triangleMinorOuterPointY, trapezoidMinorInnerPoint1X, trapezoidMinorInnerPoint1Y, trapezoidMinorInnerPoint2X, trapezoidMinorInnerPoint2Y); } } break; case DOT: if (majorTickMarksVisible) { Helper.drawDot(CTX, dotCenterX - majorHalfDotSize, dotCenterY - majorHalfDotSize, majorDotSize); } else if (minorTickMarksVisible) { Helper.drawDot(CTX, dotMinorCenterX - minorHalfDotSize, dotMinorCenterY - minorHalfDotSize, minorDotSize); } break; case TICK_LABEL: if (majorTickMarksVisible) { CTX.save(); CTX.translate(tickLabelTickMarkX, tickLabelTickMarkY); Helper.rotateContextForText(CTX, START_ANGLE, angle, tickLabelOrientation); CTX.setFont(isNotZero ? tickMarkFont : tickMarkZeroFont); CTX.setTextAlign(TextAlignment.CENTER); CTX.setTextBaseline(VPos.CENTER); CTX.fillText(String.format(locale, tickLabelFormatString, counter), 0, 0); CTX.restore(); } break; case LINE: default: if (majorTickMarksVisible) { Helper.drawLine(CTX, innerPointX, innerPointY, outerPointX, outerPointY); } else if (minorTickMarksVisible) { if (TickLabelLocation.OUTSIDE == tickLabelLocation) { Helper.drawLine(CTX, innerPointX, innerPointY, outerMinorPointX, outerMinorPointY); } else { Helper.drawLine(CTX, innerMinorPointX, innerMinorPointY, outerPointX, outerPointY); } } break; } } // Draw tick label text if (tickLabelsVisible) { CTX.save(); CTX.translate(textPointX, textPointY); Helper.rotateContextForText(CTX, START_ANGLE, angle, tickLabelOrientation); CTX.setFont(isNotZero ? tickLabelFont : tickLabelZeroFont); CTX.setTextAlign(TextAlignment.CENTER); CTX.setTextBaseline(VPos.CENTER); if (!onlyFirstAndLastLabelVisible) { if (isNotZero) { CTX.setFill(tickLabelSectionsVisible ? Helper.getColorOfSection(tickLabelSections, counter, tickLabelColor) : tickLabelColor); } else { CTX.setFill(tickLabelSectionsVisible ? Helper.getColorOfSection(tickLabelSections, counter, tickLabelColor) : fullRange ? zeroColor : tickLabelColor); } } else { if ((Double.compare(counter, MIN_VALUE) == 0 || Double.compare(counter, MAX_VALUE) == 0)) { if (isNotZero) { CTX.setFill(tickLabelSectionsVisible ? Helper.getColorOfSection(tickLabelSections, counter, tickLabelColor) : tickLabelColor); } else { CTX.setFill(tickLabelSectionsVisible ? Helper.getColorOfSection(tickLabelSections, counter, tickLabelColor) : fullRange ? zeroColor : tickLabelColor); } } else { CTX.setFill(Color.TRANSPARENT); } } if (customTickLabelsEnabled) { if (customTickLabelCounter >= 0) { CTX.fillText(customTickLabels.get(customTickLabelCounter), 0, 0); customTickLabelCounter++; } if (customTickLabelCounter > customTickLabels.size() - 1) customTickLabelCounter = -1; } else { CTX.fillText(String.format(locale, tickLabelFormatString, counter), 0, 0); } CTX.restore(); } } else if (mediumTickMarksVisible && Double.compare(minorTickSpaceBD.remainder(mediumCheck2).doubleValue(), 0.0) != 0.0 && Double.compare(counterBD.remainder(mediumCheck5).doubleValue(), 0.0) == 0.0) { // Draw medium tick mark CTX.setFill(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, mediumTickMarkColor) : mediumTickMarkColor); CTX.setStroke(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, mediumTickMarkColor) : mediumTickMarkColor); switch(mediumTickMarkType) { case TRAPEZOID: Helper.drawTrapezoid(CTX, trapezoidMediumInnerPoint1X, trapezoidMediumInnerPoint1Y, trapezoidMediumInnerPoint2X, trapezoidMediumInnerPoint2Y, trapezoidMediumOuterPoint1X, trapezoidMediumOuterPoint1Y, trapezoidMediumOuterPoint2X, trapezoidMediumOuterPoint2Y); break; case TRIANGLE: if (TickLabelLocation.INSIDE == tickLabelLocation) { Helper.drawTriangle(CTX, triangleMediumInnerPointX, triangleMediumInnerPointY, trapezoidMediumOuterPoint1X, trapezoidMediumOuterPoint1Y, trapezoidMediumOuterPoint2X, trapezoidMediumOuterPoint2Y); } else { Helper.drawTriangle(CTX, triangleMediumOuterPointX, triangleMediumOuterPointY, trapezoidMediumInnerPoint1X, trapezoidMediumInnerPoint1Y, trapezoidMediumInnerPoint2X, trapezoidMediumInnerPoint2Y); } break; case DOT: Helper.drawDot(CTX, dotMediumCenterX - mediumHalfDotSize, dotMediumCenterY - mediumHalfDotSize, mediumDotSize); break; case BOX: CTX.setLineWidth(SIZE * 0.009); if (TickLabelLocation.OUTSIDE == tickLabelLocation) { Helper.drawLine(CTX, innerPointX, innerPointY, outerMediumPointX, outerMediumPointY); } else { Helper.drawLine(CTX, innerMediumPointX, innerMediumPointY, outerPointX, outerPointY); } break; case PILL: CTX.setLineCap(StrokeLineCap.ROUND); CTX.setLineWidth(SIZE * 0.009); if (TickLabelLocation.OUTSIDE == tickLabelLocation) { Helper.drawLine(CTX, innerPointX, innerPointY, outerMediumPointX, outerMediumPointY); } else { Helper.drawLine(CTX, innerMediumPointX, innerMediumPointY, outerPointX, outerPointY); } break; case LINE: default: CTX.setLineWidth(SIZE * MAX_TICK_MARK_WIDTH * mediumTickMarkWidthFactor); if (TickLabelLocation.OUTSIDE == tickLabelLocation) { Helper.drawLine(CTX, innerPointX, innerPointY, outerMediumPointX, outerMediumPointY); } else { Helper.drawLine(CTX, innerMediumPointX, innerMediumPointY, outerPointX, outerPointY); } break; } } else if (minorTickMarksVisible) { boolean drawMinorTicks = false; if (minorTickSpaceBD.stripTrailingZeros().scale() <= 0) { if (Double.compare(counterBD.remainder(minorTickSpaceBD).doubleValue(), 0.0) == 0) { drawMinorTicks = true; } } else { drawMinorTicks = true; } // Draw minor tick mark if (drawMinorTicks && TickMarkType.TICK_LABEL != majorTickMarkType) { CTX.setFill(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, minorTickMarkColor) : minorTickMarkColor); CTX.setStroke(tickMarkSectionsVisible ? Helper.getColorOfSection(tickMarkSections, counter, minorTickMarkColor) : minorTickMarkColor); switch (minorTickMarkType) { case TRAPEZOID: Helper.drawTrapezoid(CTX, trapezoidMinorInnerPoint1X, trapezoidMinorInnerPoint1Y, trapezoidMinorInnerPoint2X, trapezoidMinorInnerPoint2Y, trapezoidMinorOuterPoint1X, trapezoidMinorOuterPoint1Y, trapezoidMinorOuterPoint2X, trapezoidMinorOuterPoint2Y); break; case TRIANGLE: if (TickLabelLocation.INSIDE == tickLabelLocation) { Helper.drawTriangle(CTX, triangleMinorInnerPointX, triangleMinorInnerPointY, trapezoidMinorOuterPoint1X, trapezoidMinorOuterPoint1Y, trapezoidMinorOuterPoint2X, trapezoidMinorOuterPoint2Y); } else { Helper.drawTriangle(CTX, triangleMinorOuterPointX, triangleMinorOuterPointY, trapezoidMinorInnerPoint1X, trapezoidMinorInnerPoint1Y, trapezoidMinorInnerPoint2X, trapezoidMinorInnerPoint2Y); } break; case DOT: Helper.drawDot(CTX, dotMinorCenterX - minorHalfDotSize, dotMinorCenterY - minorHalfDotSize, minorDotSize); break; case BOX: CTX.setLineWidth(SIZE * 0.007); if (TickLabelLocation.OUTSIDE == tickLabelLocation) { Helper.drawLine(CTX, innerPointX, innerPointY, outerMinorPointX, outerMinorPointY); } else { Helper.drawLine(CTX, innerMinorPointX, innerMinorPointY, outerPointX, outerPointY); } break; case PILL: CTX.setLineCap(StrokeLineCap.ROUND); CTX.setLineWidth(SIZE * 0.007); if (TickLabelLocation.OUTSIDE == tickLabelLocation) { Helper.drawLine(CTX, innerPointX, innerPointY, outerMinorPointX, outerMinorPointY); } else { Helper.drawLine(CTX, innerMinorPointX, innerMinorPointY, outerPointX, outerPointY); } break; case LINE: default: CTX.setLineWidth(SIZE * MAX_TICK_MARK_WIDTH * minorTickMarkWidthFactor); if (TickLabelLocation.OUTSIDE == tickLabelLocation) { Helper.drawLine(CTX, innerPointX, innerPointY, outerMinorPointX, outerMinorPointY); } else { Helper.drawLine(CTX, innerMinorPointX, innerMinorPointY, outerPointX, outerPointY); } break; } } } counterBD = counterBD.add(minorTickSpaceBD); counter = counterBD.doubleValue(); if (counter > MAX_VALUE) break; angle = ScaleDirection.CLOCKWISE == scaleDirection ? (angle - tmpAngleStep) : (angle + tmpAngleStep); } } public static final Image createNoiseImage(final double WIDTH, final double HEIGHT, final Color DARK_COLOR, final Color BRIGHT_COLOR, final double ALPHA_VARIATION_IN_PERCENT) { if (Double.compare(WIDTH, 0) <= 0 || Double.compare(HEIGHT, 0) <= 0) return null; int width = (int) WIDTH; int height = (int) HEIGHT; double alphaVariationInPercent = Helper.clamp(0.0, 100.0, ALPHA_VARIATION_IN_PERCENT); final WritableImage IMAGE = new WritableImage(width, height); final PixelWriter PIXEL_WRITER = IMAGE.getPixelWriter(); final Random BW_RND = new Random(); final Random ALPHA_RND = new Random(); final double ALPHA_START = alphaVariationInPercent / 100 / 2; final double ALPHA_VARIATION = alphaVariationInPercent / 100; for (int y = 0 ; y < height ; y++) { for (int x = 0 ; x < width ; x++) { final Color NOISE_COLOR = BW_RND.nextBoolean() ? BRIGHT_COLOR : DARK_COLOR; final double NOISE_ALPHA = Helper.clamp(0.0, 1.0, ALPHA_START + ALPHA_RND.nextDouble() * ALPHA_VARIATION); PIXEL_WRITER.setColor(x, y, Color.color(NOISE_COLOR.getRed(), NOISE_COLOR.getGreen(), NOISE_COLOR.getBlue(), NOISE_ALPHA)); } } return IMAGE; } public static final void drawTimeSections(final Clock CLOCK, final GraphicsContext CTX, final List SECTIONS, final double SIZE, final double XY_INSIDE, final double XY_OUTSIDE, final double WH_INSIDE, final double WH_OUTSIDE, final double LINE_WIDTH) { if (SECTIONS.isEmpty()) return; TickLabelLocation tickLabelLocation = CLOCK.getTickLabelLocation(); ZonedDateTime time = CLOCK.getTime(); boolean isAM = time.get(ChronoField.AMPM_OF_DAY) == 0; double xy = TickLabelLocation.INSIDE == tickLabelLocation ? XY_INSIDE * SIZE : XY_OUTSIDE * SIZE; double wh = TickLabelLocation.INSIDE == tickLabelLocation ? WH_INSIDE * SIZE : WH_OUTSIDE * SIZE; double offset = 90; int listSize = SECTIONS.size(); double angleStep = 360.0 / 60.0; boolean highlightSections = CLOCK.isHighlightSections(); for (int i = 0 ; i < listSize ; i++) { TimeSection section = SECTIONS.get(i); LocalTime start = section.getStart(); LocalTime stop = section.getStop(); boolean isStartAM = start.get(ChronoField.AMPM_OF_DAY) == 0; boolean isStopAM = stop.get(ChronoField.AMPM_OF_DAY) == 0; boolean draw = isAM ? (isStartAM || isStopAM) :(!isStartAM || !isStopAM); if (draw) { double sectionStartAngle = (start.getHour() % 12 * 5.0 + start.getMinute() / 12.0 + start.getSecond() / 300.0) * angleStep + 180; double sectionAngleExtend = ((stop.getHour() - start.getHour()) % 12 * 5.0 + (stop.getMinute() - start.getMinute()) / 12.0 + (stop.getSecond() - start.getSecond()) / 300.0) * angleStep; //TODO: Add an indicator to the section like -1 or similar // check if start was already yesterday if (start.getHour() > stop.getHour()) { sectionAngleExtend = (360.0 - Math.abs(sectionAngleExtend)); } CTX.save(); if (highlightSections) { CTX.setStroke(section.contains(time.toLocalTime()) ? section.getHighlightColor() : section.getColor()); } else { CTX.setStroke(section.getColor()); } CTX.setLineWidth(SIZE * LINE_WIDTH); CTX.setLineCap(StrokeLineCap.BUTT); CTX.strokeArc(xy, xy, wh, wh, -(offset + sectionStartAngle), -sectionAngleExtend, ArcType.OPEN); CTX.restore(); } } } public static final void drawTimeAreas(final Clock CLOCK, final GraphicsContext CTX, final List AREAS, final double SIZE, final double XY_INSIDE, final double XY_OUTSIDE, final double WH_INSIDE, final double WH_OUTSIDE) { if (AREAS.isEmpty()) return; TickLabelLocation tickLabelLocation = CLOCK.getTickLabelLocation(); ZonedDateTime time = CLOCK.getTime(); boolean isAM = time.get(ChronoField.AMPM_OF_DAY) == 0; double xy = TickLabelLocation.OUTSIDE == tickLabelLocation ? XY_OUTSIDE * SIZE : XY_INSIDE * SIZE; double wh = TickLabelLocation.OUTSIDE == tickLabelLocation ? WH_OUTSIDE * SIZE : WH_INSIDE * SIZE; double offset = 90; double angleStep = 360.0 / 60.0; int listSize = AREAS.size(); boolean highlightAreas = CLOCK.isHighlightAreas(); for (int i = 0; i < listSize ; i++) { TimeSection area = AREAS.get(i); LocalTime start = area.getStart(); LocalTime stop = area.getStop(); boolean isStartAM = start.get(ChronoField.AMPM_OF_DAY) == 0; boolean isStopAM = stop.get(ChronoField.AMPM_OF_DAY) == 0; boolean draw = isAM ? (isStartAM || isStopAM) :(!isStartAM || !isStopAM); if (draw) { double areaStartAngle = (start.getHour() % 12 * 5.0 + start.getMinute() / 12.0 + start.getSecond() / 300.0) * angleStep + 180;; double areaAngleExtend = ((stop.getHour() - start.getHour()) % 12 * 5.0 + (stop.getMinute() - start.getMinute()) / 12.0 + (stop.getSecond() - start.getSecond()) / 300.0) * angleStep; //TODO: Add an indicator to the area like -1 or similar // check if start was already yesterday if (start.getHour() > stop.getHour()) { areaAngleExtend = (360.0 - Math.abs(areaAngleExtend)); } CTX.save(); if (highlightAreas) { CTX.setFill(area.contains(time.toLocalTime()) ? area.getHighlightColor() : area.getColor()); } else { CTX.setFill(area.getColor()); } CTX.fillArc(xy, xy, wh, wh, -(offset + areaStartAngle), -areaAngleExtend, ArcType.ROUND); CTX.restore(); } } } public static final void drawAlarms(final Clock CLOCK, final double SIZE, final double ALARM_MARKER_SIZE, final double ALARM_MARKER_RADIUS, final Map ALARM_MAP, final DateTimeFormatter DATE_TIME_FORMATTER, final ZonedDateTime TIME) { if (CLOCK.isAlarmsVisible()) { double alarmSize = ALARM_MARKER_SIZE * SIZE; double center = SIZE * 0.5; double angleStep = 360.0 / 60.0; for (Map.Entry entry : ALARM_MAP.entrySet()) { Alarm alarm = entry.getKey(); ZonedDateTime alarmTime = alarm.getTime(); double alarmAngle = (alarmTime.getMinute() + alarmTime.getSecond() / 60.0) * angleStep + 180; double sinValue = Math.sin(Math.toRadians((-alarmAngle))); double cosValue = Math.cos(Math.toRadians((-alarmAngle))); Color alarmColor = alarm.isArmed() ? alarm.getColor() : INACTIVE_ALARM_COLOR; Circle dot = entry.getValue(); dot.setRadius(alarmSize); dot.setCenterX(center + SIZE * ALARM_MARKER_RADIUS * sinValue); dot.setCenterY(center + SIZE * ALARM_MARKER_RADIUS * cosValue); dot.setFill(alarmColor); dot.setStroke(alarmColor.darker()); dot.setPickOnBounds(false); dot.setOnMousePressed(e -> alarm.fireAlarmMarkerEvent(alarm.ALARM_MARKER_PRESSED_EVENT)); dot.setOnMouseReleased(e -> alarm.fireAlarmMarkerEvent(alarm.ALARM_MARKER_RELEASED_EVENT)); if (alarmTime.getDayOfMonth() == TIME.getDayOfMonth() && alarmTime.getMonthValue() == TIME.getMonthValue() && alarmTime.getYear() == TIME.getYear() && alarmTime.getHour() == TIME.getHour() && alarmTime.getMinute() >= TIME.getMinute()) { dot.setManaged(true); dot.setVisible(true); } else { dot.setManaged(false); dot.setVisible(false); } Tooltip alarmTooltip; if (alarm.getText().isEmpty()) { alarmTooltip = new Tooltip(DATE_TIME_FORMATTER.format(alarm.getTime())); } else { alarmTooltip = new Tooltip(new StringBuilder(alarm.getText()).append("\n").append(DATE_TIME_FORMATTER.format(alarm.getTime())).toString()); } alarmTooltip.setTextAlignment(TextAlignment.CENTER); Tooltip.install(dot, alarmTooltip); } } } public static final String formatNumber(final Gauge GAUGE, final double VALUE) { return formatNumber(GAUGE.getLocale(), GAUGE.getFormatString(), GAUGE.getDecimals(), VALUE); } public static final String formatNumber(final Locale LOCALE, final String FORMAT_STRING, final int DECIMALS, final double VALUE) { double value = VALUE; if (value > 0) { value = Math.floor(value * Math.pow(10, DECIMALS)) / Math.pow(10, DECIMALS); } else if (value < 0) { value = Math.ceil(value * Math.pow(10, DECIMALS)) / Math.pow(10, DECIMALS); } return String.format(LOCALE, FORMAT_STRING, value); } public static final String formatNumber(final Locale LOCALE, final double MIN_VALUE, final double MAX_VALUE, final int DECIMALS, final double VALUE) { StringBuilder sb = new StringBuilder("%.").append(DECIMALS).append("f"); String f = sb.toString(); int minLength = String.format(Locale.US, f, MIN_VALUE).length(); int maxLength = String.format(Locale.US, f, MAX_VALUE).length(); int length = Math.max(minLength, maxLength); StringBuilder formatStringBuilder = new StringBuilder("%").append(length).append(".").append(DECIMALS).append("f"); String formatString = formatStringBuilder.toString(); double value = VALUE; if (value > 0) { value = Math.floor(VALUE * Math.pow(10, DECIMALS)) / Math.pow(10, DECIMALS); } else if (value < 0) { value = Math.ceil(VALUE * Math.pow(10, DECIMALS)) / Math.pow(10, DECIMALS); } return String.format(LOCALE, formatString, value); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy