From bff01674ea7700eb15391eb7be7bd8d591112bdd Mon Sep 17 00:00:00 2001
From: Tim Molter <tim@knowm.org>
Date: Tue, 6 Oct 2015 12:09:33 +0200
Subject: [PATCH] made legend rendering code much more efficient, avoid
 repeated calculations

---
 .../xeiam/xchart/internal/chartpart/Axis.java |  16 +--
 .../chartpart/AxisTickDateCalculator.java     |   2 -
 .../internal/chartpart/ChartPainter.java      |   3 +
 .../xchart/internal/chartpart/Legend.java     | 100 +++++++++---------
 4 files changed, 54 insertions(+), 67 deletions(-)

diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
index 0a2b32d3..a9401fb4 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
@@ -92,7 +92,7 @@ public class Axis implements ChartPart {
   /**
    * Reset the default min and max values in preparation for calculating the actual min and max
    */
-      void resetMinMax() {
+  protected void resetMinMax() {
 
     min = Double.MAX_VALUE;
     max = -Double.MAX_VALUE;
@@ -152,11 +152,6 @@ public class Axis implements ChartPart {
       double xOffset = getChartPainter().getStyleManager().getChartPadding();
       double yOffset = getChartPainter().getChartTitle().getSizeHint();
 
-      double chartLegendWidth = 0;
-      if (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE) {
-        chartLegendWidth = getChartPainter().getChartLegend().getSizeHint(g)[0];
-      }
-
       /////////////////////////
       int i = 1; // just twice through is all it takes
       double width = 60; // arbitrary, final width depends on Axis tick labels
@@ -170,7 +165,7 @@ public class Axis implements ChartPart {
 
         - width // y-axis approx. width
 
-        - chartLegendWidth
+        - (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE ? getChartPainter().getChartLegend().getLegendBoxWidth() : 0)
 
         - 2 * getChartPainter().getStyleManager().getChartPadding()
 
@@ -221,18 +216,13 @@ public class Axis implements ChartPart {
           + getChartPainter().getStyleManager().getChartPadding();
       double yOffset = axisPair.getYAxis().getBounds().getY() + axisPair.getYAxis().getBounds().getHeight() + getChartPainter().getStyleManager().getPlotPadding();
 
-      double chartLegendWidth = 0;
-      if (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE) {
-        chartLegendWidth = getChartPainter().getChartLegend().getSizeHint(g)[0];
-      }
-
       double width =
 
       getChartPainter().getWidth()
 
       - axisPair.getYAxis().getBounds().getWidth() // y-axis was already painted
 
-      - chartLegendWidth
+      - (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE ? getChartPainter().getChartLegend().getLegendBoxWidth() : 0)
 
       - 2 * getChartPainter().getStyleManager().getChartPadding()
 
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java
index 5b77b80a..9093a850 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java
@@ -111,7 +111,6 @@ public class AxisTickDateCalculator extends AxisTickCalculator {
 
     super(axisDirection, workingSpace, minValue, maxValue, styleManager);
 
-    System.out.println("AxisTickDateCalculator Constructor");
     calculate();
   }
 
@@ -192,7 +191,6 @@ public class AxisTickDateCalculator extends AxisTickCalculator {
 
       SimpleDateFormat simpleDateformat = new SimpleDateFormat(datePattern, styleManager.getLocale());
       simpleDateformat.setTimeZone(styleManager.getTimezone());
-      // simpleDateformat.applyPattern(datePattern);
 
       // return simpleDateformat.format(value);
 
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java
index a19fb2f7..2e7e7732 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java
@@ -107,6 +107,9 @@ public class ChartPainter {
     Shape rect = new Rectangle2D.Double(0, 0, width, height);
     g.fill(rect);
 
+    // now that we added all the series, we can calculate the legend size
+    chartLegend.determineLegendBoxSize();
+
     axisPair.paint(g);
     plot.paint(g);
     chartTitle.paint(g);
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java
index 3fadc766..8f3a2d16 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java
@@ -27,7 +27,6 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 
 import com.xeiam.xchart.Series;
-import com.xeiam.xchart.StyleManager;
 import com.xeiam.xchart.StyleManager.ChartType;
 
 /**
@@ -39,6 +38,9 @@ public class Legend implements ChartPart {
   private static final int BOX_SIZE = 20;
   private static final int MULTI_LINE_SPACE = 3;
 
+  private double legendBoxWidth = 0.0;
+  private double legendBoxHeight = 0.0;
+
   /**
    * parent
    */
@@ -60,22 +62,15 @@ public class Legend implements ChartPart {
   }
 
   /**
-   * get the width of the chart legend
-   *
-   * @param g
-   * @return
+   * determine the width and height of the chart legend
    */
-  protected double[] getSizeHint(Graphics2D g) {
+  public void determineLegendBoxSize() {
 
     if (!chartPainter.getStyleManager().isLegendVisible()) {
-      return new double[] { 0, 0 };
-    }
-    if (chartPainter.getPlot().getBounds().getWidth() < 30) {
-      return new double[] { 0, 0 };
+      return;
     }
 
-    StyleManager styleManager = getChartPainter().getStyleManager();
-    boolean isBar = styleManager.getChartType() == ChartType.Bar;
+    boolean isBar = getChartPainter().getStyleManager().getChartType() == ChartType.Bar;
 
     // determine legend text content max width
     double legendTextContentMaxWidth = 0;
@@ -85,7 +80,7 @@ public class Legend implements ChartPart {
 
     for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
 
-      Map<String, Rectangle2D> seriesBounds = getSeriesTextBounds(series, g);
+      Map<String, Rectangle2D> seriesBounds = getSeriesTextBounds(series);
 
       double blockHeight = 0;
       for (Map.Entry<String, Rectangle2D> entry : seriesBounds.entrySet()) {
@@ -96,27 +91,30 @@ public class Legend implements ChartPart {
       blockHeight -= MULTI_LINE_SPACE;
       blockHeight = Math.max(blockHeight, isBar ? BOX_SIZE : getChartPainter().getStyleManager().getMarkerSize());
 
-      legendContentHeight += blockHeight + styleManager.getLegendPadding();
+      legendContentHeight += blockHeight + getChartPainter().getStyleManager().getLegendPadding();
     }
 
     // determine legend content width
     double legendContentWidth = 0;
     if (!isBar) {
-      legendContentWidth = styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding() + legendTextContentMaxWidth;
+      legendContentWidth = getChartPainter().getStyleManager().getLegendSeriesLineLength() + getChartPainter().getStyleManager().getLegendPadding() + legendTextContentMaxWidth;
     }
     else {
-      legendContentWidth = BOX_SIZE + styleManager.getLegendPadding() + legendTextContentMaxWidth;
+      legendContentWidth = BOX_SIZE + getChartPainter().getStyleManager().getLegendPadding() + legendTextContentMaxWidth;
     }
 
     // Legend Box
-    double legendBoxWidth = legendContentWidth + 2 * styleManager.getLegendPadding();
-    double legendBoxHeight = legendContentHeight + 1 * styleManager.getLegendPadding();
-    return new double[] { legendBoxWidth, legendBoxHeight };
+    legendBoxWidth = legendContentWidth + 2 * getChartPainter().getStyleManager().getLegendPadding();
+    legendBoxHeight = legendContentHeight + 1 * getChartPainter().getStyleManager().getLegendPadding();
   }
 
   @Override
   public void paint(Graphics2D g) {
 
+    if (!getChartPainter().getStyleManager().isLegendVisible()) {
+      return;
+    }
+
     // if the area to draw a chart on is so small, don't even bother
     if (chartPainter.getPlot().getBounds().getWidth() < 30) {
       return;
@@ -125,23 +123,12 @@ public class Legend implements ChartPart {
     bounds = new Rectangle2D.Double();
     // g.setFont(chartPainter.getStyleManager().getLegendFont());
 
-    StyleManager styleManager = getChartPainter().getStyleManager();
-
-    if (!styleManager.isLegendVisible()) {
-      return;
-    }
-
-    final double[] sizeHint = getSizeHint(g);
-
-    double legendBoxWidth = sizeHint[0];
-    double legendBoxHeight = sizeHint[1];
-
     // legend draw position
     double xOffset = 0;
     double yOffset = 0;
-    switch (styleManager.getLegendPosition()) {
+    switch (getChartPainter().getStyleManager().getLegendPosition()) {
     case OutsideE:
-      xOffset = chartPainter.getWidth() - legendBoxWidth - styleManager.getChartPadding();
+      xOffset = chartPainter.getWidth() - legendBoxWidth - getChartPainter().getStyleManager().getChartPadding();
       yOffset = chartPainter.getPlot().getBounds().getY() + (chartPainter.getPlot().getBounds().getHeight() - legendBoxHeight) / 2.0;
       break;
     case InsideNW:
@@ -172,18 +159,18 @@ public class Legend implements ChartPart {
     g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, new float[] { 3.0f, 0.0f }, 0.0f));
 
     Shape rect = new Rectangle2D.Double(xOffset + 1, yOffset + 1, legendBoxWidth - 2, legendBoxHeight - 2);
-    g.setColor(styleManager.getLegendBackgroundColor());
+    g.setColor(getChartPainter().getStyleManager().getLegendBackgroundColor());
     g.fill(rect);
-    g.setColor(styleManager.getLegendBorderColor());
+    g.setColor(getChartPainter().getStyleManager().getLegendBorderColor());
     g.draw(rect);
 
     // Draw legend content inside legend box
-    double startx = xOffset + styleManager.getLegendPadding();
-    double starty = yOffset + styleManager.getLegendPadding();
+    double startx = xOffset + getChartPainter().getStyleManager().getLegendPadding();
+    double starty = yOffset + getChartPainter().getStyleManager().getLegendPadding();
 
     for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
 
-      Map<String, Rectangle2D> seriesTextBounds = getSeriesTextBounds(series, g);
+      Map<String, Rectangle2D> seriesTextBounds = getSeriesTextBounds(series);
 
       float blockHeight = 0;
       double legendTextContentMaxWidth = 0;
@@ -193,15 +180,15 @@ public class Legend implements ChartPart {
       }
       blockHeight -= MULTI_LINE_SPACE;
 
-      blockHeight = Math.max(blockHeight, styleManager.getChartType() == ChartType.Bar ? BOX_SIZE : getChartPainter().getStyleManager().getMarkerSize());
+      blockHeight = Math.max(blockHeight, getChartPainter().getStyleManager().getChartType() == ChartType.Bar ? BOX_SIZE : getChartPainter().getStyleManager().getMarkerSize());
 
-      if (styleManager.getChartType() != ChartType.Bar) {
+      if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
 
         // paint line
-        if (styleManager.getChartType() != ChartType.Scatter && series.getStroke() != null) {
+        if (getChartPainter().getStyleManager().getChartType() != ChartType.Scatter && series.getStroke() != null) {
           g.setColor(series.getStrokeColor());
           g.setStroke(series.getStroke());
-          Shape line = new Line2D.Double(startx, starty + blockHeight / 2.0, startx + styleManager.getLegendSeriesLineLength(), starty + blockHeight / 2.0);
+          Shape line = new Line2D.Double(startx, starty + blockHeight / 2.0, startx + getChartPainter().getStyleManager().getLegendSeriesLineLength(), starty + blockHeight / 2.0);
           g.draw(line);
         }
 
@@ -214,7 +201,7 @@ public class Legend implements ChartPart {
         // paint marker
         if (series.getMarker() != null) {
           g.setColor(series.getMarkerColor());
-          series.getMarker().paint(g, startx + styleManager.getLegendSeriesLineLength() / 2.0, starty + blockHeight / 2.0, getChartPainter().getStyleManager().getMarkerSize());
+          series.getMarker().paint(g, startx + getChartPainter().getStyleManager().getLegendSeriesLineLength() / 2.0, starty + blockHeight / 2.0, getChartPainter().getStyleManager().getMarkerSize());
 
         }
       }
@@ -237,16 +224,16 @@ public class Legend implements ChartPart {
 
       double multiLineOffset = 0.0;
 
-      if (styleManager.getChartType() != ChartType.Bar) {
+      if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
 
-        double x = startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding();
+        double x = startx + getChartPainter().getStyleManager().getLegendSeriesLineLength() + getChartPainter().getStyleManager().getLegendPadding();
         for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) {
 
           double height = entry.getValue().getHeight();
           double centerOffsetY = (Math.max(getChartPainter().getStyleManager().getMarkerSize(), height) - height) / 2.0;
 
           FontRenderContext frc = g.getFontRenderContext();
-          TextLayout tl = new TextLayout(entry.getKey(), styleManager.getLegendFont(), frc);
+          TextLayout tl = new TextLayout(entry.getKey(), getChartPainter().getStyleManager().getLegendFont(), frc);
           Shape shape = tl.getOutline(null);
           AffineTransform orig = g.getTransform();
           AffineTransform at = new AffineTransform();
@@ -263,18 +250,18 @@ public class Legend implements ChartPart {
           multiLineOffset += height + MULTI_LINE_SPACE;
         }
 
-        starty += blockHeight + styleManager.getLegendPadding();
+        starty += blockHeight + getChartPainter().getStyleManager().getLegendPadding();
       }
       else {
 
-        final double x = startx + BOX_SIZE + styleManager.getLegendPadding();
+        final double x = startx + BOX_SIZE + getChartPainter().getStyleManager().getLegendPadding();
         for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) {
 
           double height = entry.getValue().getHeight();
           double centerOffsetY = (Math.max(BOX_SIZE, height) - height) / 2.0;
 
           FontRenderContext frc = g.getFontRenderContext();
-          TextLayout tl = new TextLayout(entry.getKey(), styleManager.getLegendFont(), frc);
+          TextLayout tl = new TextLayout(entry.getKey(), getChartPainter().getStyleManager().getLegendFont(), frc);
           Shape shape = tl.getOutline(null);
           AffineTransform orig = g.getTransform();
           AffineTransform at = new AffineTransform();
@@ -290,7 +277,7 @@ public class Legend implements ChartPart {
           multiLineOffset += height + MULTI_LINE_SPACE;
 
         }
-        starty += blockHeight + styleManager.getLegendPadding();
+        starty += blockHeight + getChartPainter().getStyleManager().getLegendPadding();
       }
 
     }
@@ -303,7 +290,7 @@ public class Legend implements ChartPart {
 
   }
 
-  private Map<String, Rectangle2D> getSeriesTextBounds(Series series, Graphics2D g) {
+  private Map<String, Rectangle2D> getSeriesTextBounds(Series series) {
 
     // FontMetrics fontMetrics = g.getFontMetrics(getChartPainter().getStyleManager().getLegendFont());
     // float fontDescent = fontMetrics.getDescent();
@@ -311,8 +298,7 @@ public class Legend implements ChartPart {
     String lines[] = series.getName().split("\\n");
     Map<String, Rectangle2D> seriesTextBounds = new LinkedHashMap<String, Rectangle2D>(lines.length);
     for (String line : lines) {
-      FontRenderContext frc = g.getFontRenderContext();
-      TextLayout tl = new TextLayout(line, getChartPainter().getStyleManager().getLegendFont(), frc);
+      TextLayout tl = new TextLayout(line, getChartPainter().getStyleManager().getLegendFont(), new FontRenderContext(null, true, false));
       Shape shape = tl.getOutline(null);
       Rectangle2D bounds = shape.getBounds2D();
       // System.out.println(tl.getAscent());
@@ -325,6 +311,16 @@ public class Legend implements ChartPart {
     return seriesTextBounds;
   }
 
+  public double getLegendBoxWidth() {
+
+    return legendBoxWidth;
+  }
+
+  public double getLegendBoxHeight() {
+
+    return legendBoxHeight;
+  }
+
   @Override
   public Rectangle2D getBounds() {
 
-- 
GitLab