private static Paint createTransparentCheckeredPaint(Color color, int checkerSize) {
    int s = checkerSize;
    BufferedImage bufferedImage = new BufferedImage(2 * s, 2 * s, BufferedImage.TYPE_INT_ARGB);

    Graphics2D g2 = bufferedImage.createGraphics();
    g2.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING, // Anti-alias!
        RenderingHints.VALUE_ANTIALIAS_ON);

    Color c1 = DataStructureUtils.setColorAlpha(color, (int) (color.getAlpha() * .8));
    Color c2 = DataStructureUtils.setColorAlpha(color, (int) (color.getAlpha() * .2));
    g2.setStroke(new BasicStroke(0));
    g2.setPaint(c2);
    g2.setColor(c2);
    g2.fillRect(0, 0, s, s);
    g2.fillRect(s, s, s, s);
    g2.setPaint(c1);
    g2.setColor(c1);
    g2.fillRect(0, s, s, s);
    g2.fillRect(s, 0, s, s);

    // paint with the texturing brush
    Rectangle2D rect = new Rectangle2D.Double(0, 0, 2 * s, 2 * s);
    return new TexturePaint(bufferedImage, rect);
  }
  private LegendItem createFlankedShapeLegendItem(
      String label,
      double minValue,
      double maxValue,
      Shape itemShape,
      Paint shapeFillPaint,
      boolean shapeOutlineVisible,
      DateFormat dateFormat) {
    // configure legend item
    String description = "";
    String toolTipText = "";
    String urlText = "";
    boolean shapeVisible = true;
    boolean shapeFilled = true;
    Paint outlinePaint = Color.BLACK;
    Stroke outlineStroke = DEFAULT_OUTLINE_STROKE;
    boolean lineVisible = false;
    Shape line = new Line2D.Float();
    Stroke lineStroke = new BasicStroke(); // basic stroke is fine here, since continuous legend
    // item does not show a line
    Paint linePaint = Color.BLACK;

    // create legend item
    FlankedShapeLegendItem legendItem =
        new FlankedShapeLegendItem(
            label,
            description,
            toolTipText,
            urlText,
            shapeVisible,
            itemShape,
            shapeFilled,
            shapeFillPaint,
            shapeOutlineVisible,
            outlinePaint,
            outlineStroke,
            lineVisible,
            line,
            lineStroke,
            linePaint);

    if (dateFormat != null) {
      legendItem.setLeftShapeLabel(dateFormat.format(new Date((long) minValue)));
      legendItem.setRightShapeLabel(dateFormat.format(new Date((long) maxValue)));
    } else {
      // set intelligently rounded strings as labels
      int powerOf10 = DataStructureUtils.getOptimalPrecision(minValue, maxValue);
      legendItem.setLeftShapeLabel(DataStructureUtils.getRoundedString(minValue, powerOf10 - 1));
      legendItem.setRightShapeLabel(DataStructureUtils.getRoundedString(maxValue, powerOf10 - 1));
    }

    return legendItem;
  }
 @Override
 public Paint getItemOutlinePaint(int seriesIdx, int valueIdx) {
   if (getFormatDelegate().isItemSelected(seriesIdx, valueIdx)) {
     return super.getItemOutlinePaint(seriesIdx, valueIdx);
   } else {
     return DataStructureUtils.setColorAlpha(Color.LIGHT_GRAY, 20);
   }
 }
  private static void configureXYDifferenceRenderer(
      FormattedXYDifferenceRenderer renderer,
      ValueSource valueSource,
      PlotConfiguration plotConfiguration) {
    renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
    SeriesFormat seriesFormat = valueSource.getSeriesFormat();
    DimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfig colorDimensionConfig =
        plotConfiguration.getDimensionConfig(PlotDimension.COLOR);
    DimensionConfig shapeDimensionConfig =
        plotConfiguration.getDimensionConfig(PlotDimension.SHAPE);

    int seriesCount = 1; // valueSource.getSeriesDataForAllGroupCells().groupCellCount();

    // Loop all series and set series format.
    // Format based on dimension configs will be set later on in initFormatDelegate().
    for (int seriesIdx = 0; seriesIdx < seriesCount; ++seriesIdx) {
      // configure linestyle
      if (seriesFormat.getLineStyle() == LineStyle.NONE) {
      } else {
        renderer.setSeriesStroke(seriesIdx, seriesFormat.getStroke(), false);
      }

      // configure series shape if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, shapeDimensionConfig)) {
        if (seriesFormat.getItemShape() != ItemShape.NONE) {
          renderer.setSeriesShape(seriesIdx, seriesFormat.getItemShape().getShape());
        } else {
        }
      }

      // configure series color if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, colorDimensionConfig)) {
        Color itemColor = seriesFormat.getItemColor();
        Color halfTransparentPaint =
            DataStructureUtils.setColorAlpha(itemColor, itemColor.getAlpha() / 2);

        renderer.setSeriesPaint(0, halfTransparentPaint);
        renderer.setSeriesFillPaint(0, halfTransparentPaint);
        renderer.setPositivePaint(halfTransparentPaint);
        renderer.setNegativePaint(
            new Color(
                255 - itemColor.getRed(),
                255 - itemColor.getGreen(),
                255 - itemColor.getBlue(),
                itemColor.getAlpha() / 2));
      }
      renderer.setSeriesOutlinePaint(seriesIdx, PlotConfiguration.DEFAULT_SERIES_OUTLINE_PAINT);
    }
  }
  /**
   * @param valueGroups
   * @param valuesAreDates
   *     <p>TODO use param valuesAreDates
   */
  protected void applyAdaptiveVisualRounding(List<ValueRange> valueGroups, boolean valuesAreDates) {
    if (valuesAreDates) {
      DateFormat dateFormat = getDateFormat();
      for (ValueRange range : valueGroups) {
        NumericalValueRange numericalValueRange = (NumericalValueRange) range;
        numericalValueRange.setDateFormat(dateFormat);
      }
    } else {
      // values are not dates

      // first pass
      NumericalValueRange previous = null;
      NumericalValueRange current = null;
      NumericalValueRange next = null;
      for (ValueRange valueGroup : valueGroups) {
        next = (NumericalValueRange) valueGroup;
        if (previous != null) {
          int precisionLower =
              Math.min(
                  DataStructureUtils.getOptimalPrecision(
                      current.getLowerBound(), current.getUpperBound()),
                  DataStructureUtils.getOptimalPrecision(
                      previous.getLowerBound(), current.getLowerBound()));
          int precisionUpper =
              Math.min(
                  DataStructureUtils.getOptimalPrecision(
                      current.getLowerBound(), current.getUpperBound()),
                  DataStructureUtils.getOptimalPrecision(
                      current.getUpperBound(), next.getUpperBound()));
          if (precisionUpper >= Integer.MAX_VALUE) {
            precisionUpper = precisionLower;
          }
          current.setVisualPrecision(precisionLower, precisionUpper);
        } else if (current != null) {
          int precisionLower =
              DataStructureUtils.getOptimalPrecision(
                  current.getLowerBound(), current.getUpperBound());
          int precisionUpper =
              Math.min(
                  DataStructureUtils.getOptimalPrecision(
                      current.getLowerBound(), current.getUpperBound()),
                  DataStructureUtils.getOptimalPrecision(
                      current.getUpperBound(), next.getUpperBound()));
          if (precisionUpper >= Integer.MAX_VALUE) {
            precisionUpper = precisionLower;
          }
          current.setVisualPrecision(precisionLower, precisionUpper);
        }
        previous = current;
        current = next;
      }
      if (previous != null) {
        // even if eclipse states that this code is dead, it is not! (eclipse bug)
        int precisionLower =
            Math.min(
                DataStructureUtils.getOptimalPrecision(
                    current.getLowerBound(), current.getUpperBound()),
                DataStructureUtils.getOptimalPrecision(
                    previous.getLowerBound(), current.getLowerBound()));
        int precisionUpper =
            DataStructureUtils.getOptimalPrecision(
                current.getLowerBound(), current.getUpperBound());
        if (precisionUpper >= Integer.MAX_VALUE) {
          precisionUpper = precisionLower;
        }
        current.setVisualPrecision(precisionLower, precisionUpper);
      } else if (current != null) {
        int precision =
            DataStructureUtils.getOptimalPrecision(
                current.getLowerBound(), current.getUpperBound());
        current.setVisualPrecision(precision, precision);
      }

      //		// second pass
      //		current = null;
      //		next = null;
      //		for (ValueRange valueGroup : valueGroups ) {
      //			next = (NumericalValueRange)valueGroup;
      //			if (current != null) {
      //				int currentPrecision = current.getUpperPrecision();
      //				int nextPrecision = next.getLowerPrecision();
      //				int precision = Math.min(nextPrecision, currentPrecision);
      //				current.setUpperPrecision(precision);
      //				next.setLowerPrecision(precision);
      //			}
      //			current = next;
      //		}
    }
  }