< prev index next >

modules/controls/src/main/java/javafx/scene/chart/Axis.java

Print this page
rev 9400 : 8139841: Axis class does not render ticks marks when tick labels are invisible


 628         }
 629     }
 630 
 631     /**
 632      * Called during layout if the tickmarks have been updated, allowing subclasses to do anything they need to
 633      * in reaction.
 634      */
 635     protected void tickMarksUpdated(){}
 636 
 637     /**
 638      * Invoked during the layout pass to layout this axis and all its content.
 639      */
 640     @Override protected void layoutChildren() {
 641         final double width = getWidth();
 642         final double height = getHeight();
 643         final double tickMarkLength = (isTickMarkVisible() && getTickLength() > 0) ? getTickLength() : 0;
 644         final boolean isFirstPass = oldLength == 0;
 645         // auto range if it is not valid
 646         final Side side = getEffectiveSide();
 647         final double length = (side.isVertical()) ? height : width;
 648         int tickIndex = 0;
 649         boolean rangeInvalid = !isRangeValid();
 650         boolean lengthDiffers = oldLength != length;
 651         if (lengthDiffers || rangeInvalid) {
 652             // get range
 653             Object range;
 654             if(isAutoRanging()) {
 655                 // auto range
 656                 range = autoRange(length);
 657                 // set current range to new range
 658                 setRange(range, getAnimated() && !isFirstPass && impl_isTreeVisible() && rangeInvalid);
 659             } else {
 660                 range = getRange();
 661             }
 662             // calculate new tick marks
 663             List<T> newTickValues = calculateTickValues(length, range);
 664 
 665             // remove everything
 666             Iterator<TickMark<T>> tickMarkIterator = tickMarks.iterator();
 667             while (tickMarkIterator.hasNext()) {
 668                 TickMark<T> tick = tickMarkIterator.next();


 703             // call tick marks updated to inform subclasses that we have updated tick marks
 704             tickMarksUpdated();
 705             // mark all done
 706             oldLength = length;
 707             rangeValid = true;
 708         }
 709 
 710         if (lengthDiffers || rangeInvalid || measureInvalid || tickLabelsVisibleInvalid) {
 711             measureInvalid = false;
 712             tickLabelsVisibleInvalid = false;
 713             // RT-12272 : tick labels overlapping
 714             labelsToSkip.clear();
 715             double prevEnd = -Double.MAX_VALUE;
 716             double lastStart = Double.MAX_VALUE;
 717             switch (side) {
 718                 case LEFT:
 719                 case RIGHT:
 720                     int stop = 0;
 721                     for (; stop < tickMarks.size(); ++stop) {
 722                         TickMark<T> m = tickMarks.get(stop);

 723                         if (m.isTextVisible()) {
 724                             double tickHeight = measureTickMarkSize(m.getValue(), getRange()).getHeight();
 725                             lastStart = updateAndGetDisplayPosition(m) - tickHeight / 2;
 726                             break;
 727                         } else {
 728                             labelsToSkip.set(stop);
 729                         }
 730                     }
 731 
 732                     for (int i = tickMarks.size() - 1; i > stop; i--) {
 733                         TickMark<T> m = tickMarks.get(i);

 734                         if (!m.isTextVisible()) {
 735                             labelsToSkip.set(i);
 736                             continue;
 737                         }
 738                         double tickHeight = measureTickMarkSize(m.getValue(), getRange()).getHeight();
 739                         double tickStart = updateAndGetDisplayPosition(m) - tickHeight / 2;
 740                         if (tickStart <= prevEnd || tickStart + tickHeight > lastStart) {
 741                             labelsToSkip.set(i);
 742                         } else {
 743                             prevEnd = tickStart + tickHeight;
 744                         }
 745                     }
 746                     break;
 747                 case BOTTOM:
 748                 case TOP:
 749                     stop = tickMarks.size() - 1;
 750                     for (; stop >= 0; --stop) {
 751                         TickMark<T> m = tickMarks.get(stop);

 752                         if (m.isTextVisible()) {
 753                             double tickWidth = measureTickMarkSize(m.getValue(), getRange()).getWidth();
 754                             lastStart = updateAndGetDisplayPosition(m) - tickWidth / 2;
 755                             break;
 756                         } else {
 757                             labelsToSkip.set(stop);
 758                         }
 759                     }
 760 
 761                     for (int i = 0; i < stop; ++i) {
 762                         TickMark<T> m = tickMarks.get(i);

 763                         if (!m.isTextVisible()) {
 764                             labelsToSkip.set(i);
 765                             continue;
 766                         }
 767                         double tickWidth = measureTickMarkSize(m.getValue(), getRange()).getWidth();
 768                         double tickStart = updateAndGetDisplayPosition(m) - tickWidth / 2;
 769                         if (tickStart <= prevEnd || tickStart + tickWidth > lastStart) {
 770                             labelsToSkip.set(i);
 771                         } else {
 772                             prevEnd = tickStart + tickWidth;
 773                         }
 774                     }
 775                     break;
 776             }
 777         }
 778 
 779         // clear tick mark path elements as we will recreate
 780         tickMarkPath.getElements().clear();
 781         // do layout of axis label, tick mark lines and text
 782         double effectiveLabelRotation = getEffectiveTickLabelRotation();
 783         if (Side.LEFT.equals(side)) {
 784             // offset path to make strokes snap to pixel
 785             tickMarkPath.setLayoutX(-0.5);
 786             tickMarkPath.setLayoutY(0.5);
 787             if (getLabel() != null) {
 788                 axisLabel.getTransforms().setAll(new Translate(0, height), new Rotate(-90, 0, 0));
 789                 axisLabel.setLayoutX(0);
 790                 axisLabel.setLayoutY(0);
 791                 //noinspection SuspiciousNameCombination
 792                 axisLabel.resize(height, Math.ceil(axisLabel.prefHeight(width)));
 793             }
 794             tickIndex = 0;
 795             for (int i = 0; i < tickMarks.size(); i++) {
 796                 TickMark<T> tick = tickMarks.get(i);
 797                 positionTextNode(tick.textNode, width - getTickLabelGap() - tickMarkLength,
 798                         tick.getPosition(), effectiveLabelRotation, side);
 799 
 800                 // check if position is inside bounds
 801                 if (tick.getPosition() >= 0 && tick.getPosition() <= Math.ceil(length)) {
 802                     if (isTickLabelsVisible()) {
 803                         tick.textNode.setVisible(!labelsToSkip.get(i));
 804                         tickIndex++;
 805                     }
 806                     // add tick mark line
 807                     tickMarkPath.getElements().addAll(
 808                             new MoveTo(width - tickMarkLength, tick.getPosition()),
 809                             new LineTo(width, tick.getPosition())
 810                     );
 811                 } else {
 812                     tick.textNode.setVisible(false);
 813                 }
 814             }
 815         } else if (Side.RIGHT.equals(side)) {
 816             // offset path to make strokes snap to pixel
 817             tickMarkPath.setLayoutX(0.5);
 818             tickMarkPath.setLayoutY(0.5);
 819             tickIndex = 0;
 820             for (int i = 0; i < tickMarks.size(); i++) {
 821                 TickMark<T> tick = tickMarks.get(i);
 822                 positionTextNode(tick.textNode, getTickLabelGap() + tickMarkLength,
 823                         tick.getPosition(), effectiveLabelRotation, side);
 824                 // check if position is inside bounds
 825                 if (tick.getPosition() >= 0 && tick.getPosition() <= Math.ceil(length)) {
 826                     if (isTickLabelsVisible()) {
 827                         tick.textNode.setVisible(!labelsToSkip.get(i));
 828                         tickIndex++;
 829                     }
 830                     // add tick mark line
 831                     tickMarkPath.getElements().addAll(
 832                             new MoveTo(0, tick.getPosition()),
 833                             new LineTo(tickMarkLength, tick.getPosition())
 834                     );
 835                 } else {
 836                     tick.textNode.setVisible(false);
 837                 }
 838             }
 839             if (getLabel() != null) {
 840                 final double axisLabelWidth = Math.ceil(axisLabel.prefHeight(width));
 841                 axisLabel.getTransforms().setAll(new Translate(0, height), new Rotate(-90, 0, 0));
 842                 axisLabel.setLayoutX(width-axisLabelWidth);
 843                 axisLabel.setLayoutY(0);
 844                 //noinspection SuspiciousNameCombination
 845                 axisLabel.resize(height, axisLabelWidth);
 846             }








 847         } else if (Side.TOP.equals(side)) {
 848             // offset path to make strokes snap to pixel
 849             tickMarkPath.setLayoutX(0.5);
 850             tickMarkPath.setLayoutY(-0.5);
 851             if (getLabel() != null) {
 852                 axisLabel.getTransforms().clear();
 853                 axisLabel.setLayoutX(0);
 854                 axisLabel.setLayoutY(0);
 855                 axisLabel.resize(width, Math.ceil(axisLabel.prefHeight(width)));
 856             }
 857             tickIndex = 0;
 858             for (int i = 0; i < tickMarks.size(); i++) {
 859                 TickMark<T> tick = tickMarks.get(i);
 860                 positionTextNode(tick.textNode, tick.getPosition(), height - tickMarkLength - getTickLabelGap(),
 861                         effectiveLabelRotation, side);
 862                 // check if position is inside bounds
 863                 if (tick.getPosition() >= 0 && tick.getPosition() <= Math.ceil(length)) {
 864                     if (isTickLabelsVisible()) {
 865                         tick.textNode.setVisible(!labelsToSkip.get(i));
 866                         tickIndex++;
 867                     }
 868                     // add tick mark line
 869                     tickMarkPath.getElements().addAll(
 870                             new MoveTo(tick.getPosition(), height),
 871                             new LineTo(tick.getPosition(), height - tickMarkLength)
 872                     );
 873                 } else {
 874                     tick.textNode.setVisible(false);
 875                 }
 876             }
 877         } else {
 878             // BOTTOM
 879             // offset path to make strokes snap to pixel
 880             tickMarkPath.setLayoutX(0.5);
 881             tickMarkPath.setLayoutY(0.5);
 882             tickIndex = 0;
 883             for (int i = 0; i < tickMarks.size(); i++) {
 884                 TickMark<T> tick = tickMarks.get(i);
 885                 final double xPos = Math.round(getDisplayPosition(tick.getValue()));
 886 //                System.out.println("tick pos at : "+tickIndex+" = "+xPos);
 887                 positionTextNode(tick.textNode, xPos, tickMarkLength + getTickLabelGap(),
 888                         effectiveLabelRotation, side);
 889                 // check if position is inside bounds
 890                 if (xPos >= 0 && xPos <= Math.ceil(length)) {
 891                     if (isTickLabelsVisible()) {
 892                         tick.textNode.setVisible(!labelsToSkip.get(i));
 893                         tickIndex++;
 894                     }
 895                     // add tick mark line
 896                     tickMarkPath.getElements().addAll(
 897                             new MoveTo(xPos, 0),
 898                             new LineTo(xPos, tickMarkLength)
 899                     );
 900                 } else {
 901                     tick.textNode.setVisible(false);
 902                 }
 903             }
 904             if (getLabel() != null) {
 905                 axisLabel.getTransforms().clear();
 906                 final double labelHeight = Math.ceil(axisLabel.prefHeight(width));
 907                 axisLabel.setLayoutX(0);
 908                 axisLabel.setLayoutY(height-labelHeight);
 909                 axisLabel.resize(width, labelHeight);
 910             }








 911         }
 912     }
 913 
 914     private double updateAndGetDisplayPosition(TickMark<T> m) {
 915         double displayPosition = getDisplayPosition(m.getValue());
 916         m.setPosition(displayPosition);
 917         return displayPosition;
 918     }
 919 
 920     /**
 921      * Positions a text node to one side of the given point, it X height is vertically centered on point if LEFT or
 922      * RIGHT and its centered horizontally if TOP ot BOTTOM.
 923      *
 924      * @param node The text node to position
 925      * @param posX The x position, to place text next to
 926      * @param posY The y position, to place text next to
 927      * @param angle The text rotation
 928      * @param side The side to place text next to position x,y at
 929      */
 930     private void positionTextNode(Text node, double posX, double posY, double angle, Side side) {
 931         node.setLayoutX(0);
 932         node.setLayoutY(0);
 933         node.setRotate(angle);
 934         final Bounds bounds = node.getBoundsInParent();
 935         if (Side.LEFT.equals(side)) {
 936             node.setLayoutX(posX-bounds.getWidth()-bounds.getMinX());
 937             node.setLayoutY(posY - (bounds.getHeight() / 2d) - bounds.getMinY());
 938         } else if (Side.RIGHT.equals(side)) {
 939             node.setLayoutX(posX-bounds.getMinX());
 940             node.setLayoutY(posY-(bounds.getHeight()/2d)-bounds.getMinY());
 941         } else if (Side.TOP.equals(side)) {
 942             node.setLayoutX(posX-(bounds.getWidth()/2d)-bounds.getMinX());
 943             node.setLayoutY(posY-bounds.getHeight()-bounds.getMinY());
 944         } else {
 945             node.setLayoutX(posX-(bounds.getWidth()/2d)-bounds.getMinX());
 946             node.setLayoutY(posY-bounds.getMinY());
 947         }
 948     }
 949 




















 950     /**
 951      * Get the string label name for a tick mark with the given value
 952      *
 953      * @param value The value to format into a tick label string
 954      * @return A formatted string for the given value
 955      */
 956     protected abstract String getTickMarkLabel(T value);
 957 
 958     /**
 959      * Measure the size of the label for given tick mark value. This uses the font that is set for the tick marks
 960      *
 961      *
 962      * @param labelText     tick mark label text
 963      * @param rotation  The text rotation
 964      * @return size of tick mark label for given value
 965      */
 966     protected final Dimension2D measureTickMarkLabelSize(String labelText, double rotation) {
 967         measure.setRotate(rotation);
 968         measure.setText(labelText);
 969         Bounds bounds = measure.getBoundsInParent();




 628         }
 629     }
 630 
 631     /**
 632      * Called during layout if the tickmarks have been updated, allowing subclasses to do anything they need to
 633      * in reaction.
 634      */
 635     protected void tickMarksUpdated(){}
 636 
 637     /**
 638      * Invoked during the layout pass to layout this axis and all its content.
 639      */
 640     @Override protected void layoutChildren() {
 641         final double width = getWidth();
 642         final double height = getHeight();
 643         final double tickMarkLength = (isTickMarkVisible() && getTickLength() > 0) ? getTickLength() : 0;
 644         final boolean isFirstPass = oldLength == 0;
 645         // auto range if it is not valid
 646         final Side side = getEffectiveSide();
 647         final double length = (side.isVertical()) ? height : width;

 648         boolean rangeInvalid = !isRangeValid();
 649         boolean lengthDiffers = oldLength != length;
 650         if (lengthDiffers || rangeInvalid) {
 651             // get range
 652             Object range;
 653             if(isAutoRanging()) {
 654                 // auto range
 655                 range = autoRange(length);
 656                 // set current range to new range
 657                 setRange(range, getAnimated() && !isFirstPass && impl_isTreeVisible() && rangeInvalid);
 658             } else {
 659                 range = getRange();
 660             }
 661             // calculate new tick marks
 662             List<T> newTickValues = calculateTickValues(length, range);
 663 
 664             // remove everything
 665             Iterator<TickMark<T>> tickMarkIterator = tickMarks.iterator();
 666             while (tickMarkIterator.hasNext()) {
 667                 TickMark<T> tick = tickMarkIterator.next();


 702             // call tick marks updated to inform subclasses that we have updated tick marks
 703             tickMarksUpdated();
 704             // mark all done
 705             oldLength = length;
 706             rangeValid = true;
 707         }
 708 
 709         if (lengthDiffers || rangeInvalid || measureInvalid || tickLabelsVisibleInvalid) {
 710             measureInvalid = false;
 711             tickLabelsVisibleInvalid = false;
 712             // RT-12272 : tick labels overlapping
 713             labelsToSkip.clear();
 714             double prevEnd = -Double.MAX_VALUE;
 715             double lastStart = Double.MAX_VALUE;
 716             switch (side) {
 717                 case LEFT:
 718                 case RIGHT:
 719                     int stop = 0;
 720                     for (; stop < tickMarks.size(); ++stop) {
 721                         TickMark<T> m = tickMarks.get(stop);
 722                         double tickPosition = updateAndGetDisplayPosition(m);
 723                         if (m.isTextVisible()) {
 724                             double tickHeight = measureTickMarkSize(m.getValue(), getRange()).getHeight();
 725                             lastStart = tickPosition - tickHeight / 2;
 726                             break;
 727                         } else {
 728                             labelsToSkip.set(stop);
 729                         }
 730                     }
 731 
 732                     for (int i = tickMarks.size() - 1; i > stop; i--) {
 733                         TickMark<T> m = tickMarks.get(i);
 734                         double tickPosition = updateAndGetDisplayPosition(m);
 735                         if (!m.isTextVisible()) {
 736                             labelsToSkip.set(i);
 737                             continue;
 738                         }
 739                         double tickHeight = measureTickMarkSize(m.getValue(), getRange()).getHeight();
 740                         double tickStart = tickPosition - tickHeight / 2;
 741                         if (tickStart <= prevEnd || tickStart + tickHeight > lastStart) {
 742                             labelsToSkip.set(i);
 743                         } else {
 744                             prevEnd = tickStart + tickHeight;
 745                         }
 746                     }
 747                     break;
 748                 case BOTTOM:
 749                 case TOP:
 750                     stop = tickMarks.size() - 1;
 751                     for (; stop >= 0; --stop) {
 752                         TickMark<T> m = tickMarks.get(stop);
 753                         double tickPosition = updateAndGetDisplayPosition(m);
 754                         if (m.isTextVisible()) {
 755                             double tickWidth = measureTickMarkSize(m.getValue(), getRange()).getWidth();
 756                             lastStart = tickPosition - tickWidth / 2;
 757                             break;
 758                         } else {
 759                             labelsToSkip.set(stop);
 760                         }
 761                     }
 762 
 763                     for (int i = 0; i < stop; ++i) {
 764                         TickMark<T> m = tickMarks.get(i);
 765                         double tickPosition = updateAndGetDisplayPosition(m);
 766                         if (!m.isTextVisible()) {
 767                             labelsToSkip.set(i);
 768                             continue;
 769                         }
 770                         double tickWidth = measureTickMarkSize(m.getValue(), getRange()).getWidth();
 771                         double tickStart = tickPosition - tickWidth / 2;
 772                         if (tickStart <= prevEnd || tickStart + tickWidth > lastStart) {
 773                             labelsToSkip.set(i);
 774                         } else {
 775                             prevEnd = tickStart + tickWidth;
 776                         }
 777                     }
 778                     break;
 779             }
 780         }
 781 
 782         // clear tick mark path elements as we will recreate
 783         tickMarkPath.getElements().clear();
 784         // do layout of axis label, tick mark lines and text
 785         double effectiveLabelRotation = getEffectiveTickLabelRotation();
 786         if (Side.LEFT.equals(side)) {
 787             // offset path to make strokes snap to pixel
 788             tickMarkPath.setLayoutX(-0.5);
 789             tickMarkPath.setLayoutY(0.5);
 790             if (getLabel() != null) {
 791                 axisLabel.getTransforms().setAll(new Translate(0, height), new Rotate(-90, 0, 0));
 792                 axisLabel.setLayoutX(0);
 793                 axisLabel.setLayoutY(0);
 794                 //noinspection SuspiciousNameCombination
 795                 axisLabel.resize(height, Math.ceil(axisLabel.prefHeight(width)));
 796             }

 797             for (int i = 0; i < tickMarks.size(); i++) {
 798                 TickMark<T> tick = tickMarks.get(i);
 799                 positionTextNode(tick.textNode, width - getTickLabelGap() - tickMarkLength,
 800                         tick.getPosition(), effectiveLabelRotation, side);
 801                 updateTickMark(tick, i, length,
 802                         width - tickMarkLength, tick.getPosition(),
 803                         width, tick.getPosition());












 804             }
 805         } else if (Side.RIGHT.equals(side)) {
 806             // offset path to make strokes snap to pixel
 807             tickMarkPath.setLayoutX(0.5);
 808             tickMarkPath.setLayoutY(0.5);




















 809             if (getLabel() != null) {
 810                 final double axisLabelWidth = Math.ceil(axisLabel.prefHeight(width));
 811                 axisLabel.getTransforms().setAll(new Translate(0, height), new Rotate(-90, 0, 0));
 812                 axisLabel.setLayoutX(width-axisLabelWidth);
 813                 axisLabel.setLayoutY(0);
 814                 //noinspection SuspiciousNameCombination
 815                 axisLabel.resize(height, axisLabelWidth);
 816             }
 817             for (int i = 0; i < tickMarks.size(); i++) {
 818                 TickMark<T> tick = tickMarks.get(i);
 819                 positionTextNode(tick.textNode, getTickLabelGap() + tickMarkLength,
 820                         tick.getPosition(), effectiveLabelRotation, side);
 821                 updateTickMark(tick, i, length,
 822                         0, tick.getPosition(),
 823                         tickMarkLength, tick.getPosition());
 824             }
 825         } else if (Side.TOP.equals(side)) {
 826             // offset path to make strokes snap to pixel
 827             tickMarkPath.setLayoutX(0.5);
 828             tickMarkPath.setLayoutY(-0.5);
 829             if (getLabel() != null) {
 830                 axisLabel.getTransforms().clear();
 831                 axisLabel.setLayoutX(0);
 832                 axisLabel.setLayoutY(0);
 833                 axisLabel.resize(width, Math.ceil(axisLabel.prefHeight(width)));
 834             }

 835             for (int i = 0; i < tickMarks.size(); i++) {
 836                 TickMark<T> tick = tickMarks.get(i);
 837                 positionTextNode(tick.textNode, tick.getPosition(), height - tickMarkLength - getTickLabelGap(),
 838                         effectiveLabelRotation, side);
 839                 updateTickMark(tick, i, length,
 840                         tick.getPosition(), height,
 841                         tick.getPosition(), height - tickMarkLength);











 842             }
 843         } else {
 844             // BOTTOM
 845             // offset path to make strokes snap to pixel
 846             tickMarkPath.setLayoutX(0.5);
 847             tickMarkPath.setLayoutY(0.5);






















 848             if (getLabel() != null) {
 849                 axisLabel.getTransforms().clear();
 850                 final double labelHeight = Math.ceil(axisLabel.prefHeight(width));
 851                 axisLabel.setLayoutX(0);
 852                 axisLabel.setLayoutY(height - labelHeight);
 853                 axisLabel.resize(width, labelHeight);
 854             }
 855             for (int i = 0; i < tickMarks.size(); i++) {
 856                 TickMark<T> tick = tickMarks.get(i);
 857                 positionTextNode(tick.textNode, tick.getPosition(), tickMarkLength + getTickLabelGap(),
 858                         effectiveLabelRotation, side);
 859                 updateTickMark(tick, i, length,
 860                         tick.getPosition(), 0,
 861                         tick.getPosition(), tickMarkLength);
 862             }
 863         }
 864     }
 865 
 866     private double updateAndGetDisplayPosition(TickMark<T> m) {
 867         double displayPosition = getDisplayPosition(m.getValue());
 868         m.setPosition(displayPosition);
 869         return displayPosition;
 870     }
 871 
 872     /**
 873      * Positions a text node to one side of the given point, it X height is vertically centered on point if LEFT or
 874      * RIGHT and its centered horizontally if TOP ot BOTTOM.
 875      *
 876      * @param node The text node to position
 877      * @param posX The x position, to place text next to
 878      * @param posY The y position, to place text next to
 879      * @param angle The text rotation
 880      * @param side The side to place text next to position x,y at
 881      */
 882     private void positionTextNode(Text node, double posX, double posY, double angle, Side side) {
 883         node.setLayoutX(0);
 884         node.setLayoutY(0);
 885         node.setRotate(angle);
 886         final Bounds bounds = node.getBoundsInParent();
 887         if (Side.LEFT.equals(side)) {
 888             node.setLayoutX(posX-bounds.getWidth()-bounds.getMinX());
 889             node.setLayoutY(posY - (bounds.getHeight() / 2d) - bounds.getMinY());
 890         } else if (Side.RIGHT.equals(side)) {
 891             node.setLayoutX(posX-bounds.getMinX());
 892             node.setLayoutY(posY-(bounds.getHeight()/2d)-bounds.getMinY());
 893         } else if (Side.TOP.equals(side)) {
 894             node.setLayoutX(posX-(bounds.getWidth()/2d)-bounds.getMinX());
 895             node.setLayoutY(posY-bounds.getHeight()-bounds.getMinY());
 896         } else {
 897             node.setLayoutX(posX-(bounds.getWidth()/2d)-bounds.getMinX());
 898             node.setLayoutY(posY-bounds.getMinY());
 899         }
 900     }
 901 
 902     /**
 903      * Updates visibility of the text node and adds the tick mark to the path
 904      */
 905     private void updateTickMark(TickMark<T> tick, int index, double length,
 906             double startX, double startY, double endX, double endY)
 907     {
 908         // check if position is inside bounds
 909         if (tick.getPosition() >= 0 && tick.getPosition() <= Math.ceil(length)) {
 910             if (isTickLabelsVisible()) {
 911                 tick.textNode.setVisible(!labelsToSkip.get(index));
 912             }
 913             // add tick mark line
 914             tickMarkPath.getElements().addAll(
 915                     new MoveTo(startX, startY),
 916                     new LineTo(endX, endY)
 917             );
 918         } else {
 919             tick.textNode.setVisible(false);
 920         }
 921     }
 922     /**
 923      * Get the string label name for a tick mark with the given value
 924      *
 925      * @param value The value to format into a tick label string
 926      * @return A formatted string for the given value
 927      */
 928     protected abstract String getTickMarkLabel(T value);
 929 
 930     /**
 931      * Measure the size of the label for given tick mark value. This uses the font that is set for the tick marks
 932      *
 933      *
 934      * @param labelText     tick mark label text
 935      * @param rotation  The text rotation
 936      * @return size of tick mark label for given value
 937      */
 938     protected final Dimension2D measureTickMarkLabelSize(String labelText, double rotation) {
 939         measure.setRotate(rotation);
 940         measure.setText(labelText);
 941         Bounds bounds = measure.getBoundsInParent();


< prev index next >