001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------- 028 * MarkerAxisBand.java 029 * ------------------- 030 * (C) Copyright 2000-2022, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 03-Sep-2002 : Updated Javadoc comments (DG); 038 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 039 * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG); 040 * 26-Mar-2003 : Implemented Serializable (DG); 041 * 13-May-2003 : Renamed HorizontalMarkerAxisBand --> MarkerAxisBand (DG); 042 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 043 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 044 * 07-Apr-2004 : Changed text bounds calculation (DG); 045 * 046 */ 047 048package org.jfree.chart.axis; 049 050import org.jfree.chart.api.RectangleEdge; 051import org.jfree.chart.plot.IntervalMarker; 052import org.jfree.chart.text.TextUtils; 053 054import java.awt.*; 055import java.awt.font.LineMetrics; 056import java.awt.geom.Rectangle2D; 057import java.io.Serializable; 058import java.util.List; 059import java.util.Objects; 060 061/** 062 * A band that can be added to a number axis to display regions. 063 */ 064public class MarkerAxisBand implements Serializable { 065 066 /** For serialization. */ 067 private static final long serialVersionUID = -1729482413886398919L; 068 069 /** The axis that the band belongs to. */ 070 private NumberAxis axis; 071 072 /** The top outer gap. */ 073 private double topOuterGap; 074 075 /** The top inner gap. */ 076 private double topInnerGap; 077 078 /** The bottom outer gap. */ 079 private double bottomOuterGap; 080 081 /** The bottom inner gap. */ 082 private double bottomInnerGap; 083 084 /** The font. */ 085 private Font font; 086 087 /** Storage for the markers. */ 088 private List markers; 089 090 /** 091 * Constructs a new axis band. 092 * 093 * @param axis the owner. 094 * @param topOuterGap the top outer gap. 095 * @param topInnerGap the top inner gap. 096 * @param bottomOuterGap the bottom outer gap. 097 * @param bottomInnerGap the bottom inner gap. 098 * @param font the font. 099 */ 100 public MarkerAxisBand(NumberAxis axis, 101 double topOuterGap, double topInnerGap, 102 double bottomOuterGap, double bottomInnerGap, 103 Font font) { 104 this.axis = axis; 105 this.topOuterGap = topOuterGap; 106 this.topInnerGap = topInnerGap; 107 this.bottomOuterGap = bottomOuterGap; 108 this.bottomInnerGap = bottomInnerGap; 109 this.font = font; 110 this.markers = new java.util.ArrayList(); 111 } 112 113 /** 114 * Adds a marker to the band. 115 * 116 * @param marker the marker. 117 */ 118 public void addMarker(IntervalMarker marker) { 119 this.markers.add(marker); 120 } 121 122 /** 123 * Returns the height of the band. 124 * 125 * @param g2 the graphics device. 126 * 127 * @return The height of the band. 128 */ 129 public double getHeight(Graphics2D g2) { 130 131 double result = 0.0; 132 if (this.markers.size() > 0) { 133 LineMetrics metrics = this.font.getLineMetrics( 134 "123g", g2.getFontRenderContext() 135 ); 136 result = this.topOuterGap + this.topInnerGap + metrics.getHeight() 137 + this.bottomInnerGap + this.bottomOuterGap; 138 } 139 return result; 140 141 } 142 143 /** 144 * A utility method that draws a string inside a rectangle. 145 * 146 * @param g2 the graphics device. 147 * @param bounds the rectangle. 148 * @param font the font. 149 * @param text the text. 150 */ 151 private void drawStringInRect(Graphics2D g2, Rectangle2D bounds, Font font, 152 String text) { 153 154 g2.setFont(font); 155 FontMetrics fm = g2.getFontMetrics(font); 156 Rectangle2D r = TextUtils.getTextBounds(text, g2, fm); 157 double x = bounds.getX(); 158 if (r.getWidth() < bounds.getWidth()) { 159 x = x + (bounds.getWidth() - r.getWidth()) / 2; 160 } 161 LineMetrics metrics = font.getLineMetrics( 162 text, g2.getFontRenderContext() 163 ); 164 g2.drawString( 165 text, (float) x, (float) (bounds.getMaxY() 166 - this.bottomInnerGap - metrics.getDescent()) 167 ); 168 } 169 170 /** 171 * Draws the band. 172 * 173 * @param g2 the graphics device. 174 * @param plotArea the plot area. 175 * @param dataArea the data area. 176 * @param x the x-coordinate. 177 * @param y the y-coordinate. 178 */ 179 public void draw(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, 180 double x, double y) { 181 182 double h = getHeight(g2); 183 for (Object o : this.markers) { 184 IntervalMarker marker = (IntervalMarker) o; 185 double start = Math.max( 186 marker.getStartValue(), this.axis.getRange().getLowerBound() 187 ); 188 double end = Math.min( 189 marker.getEndValue(), this.axis.getRange().getUpperBound() 190 ); 191 double s = this.axis.valueToJava2D( 192 start, dataArea, RectangleEdge.BOTTOM 193 ); 194 double e = this.axis.valueToJava2D( 195 end, dataArea, RectangleEdge.BOTTOM 196 ); 197 Rectangle2D r = new Rectangle2D.Double( 198 s, y + this.topOuterGap, e - s, 199 h - this.topOuterGap - this.bottomOuterGap 200 ); 201 202 Composite originalComposite = g2.getComposite(); 203 g2.setComposite(AlphaComposite.getInstance( 204 AlphaComposite.SRC_OVER, marker.getAlpha()) 205 ); 206 g2.setPaint(marker.getPaint()); 207 g2.fill(r); 208 g2.setPaint(marker.getOutlinePaint()); 209 g2.draw(r); 210 g2.setComposite(originalComposite); 211 212 g2.setPaint(Color.BLACK); 213 drawStringInRect(g2, r, this.font, marker.getLabel()); 214 } 215 216 } 217 218 /** 219 * Tests this axis for equality with another object. Note that the axis 220 * that the band belongs to is ignored in the test. 221 * 222 * @param obj the object ({@code null} permitted). 223 * 224 * @return {@code true} or {@code false}. 225 */ 226 @Override 227 public boolean equals(Object obj) { 228 if (obj == this) { 229 return true; 230 } 231 if (!(obj instanceof MarkerAxisBand)) { 232 return false; 233 } 234 MarkerAxisBand that = (MarkerAxisBand) obj; 235 if (this.topOuterGap != that.topOuterGap) { 236 return false; 237 } 238 if (this.topInnerGap != that.topInnerGap) { 239 return false; 240 } 241 if (this.bottomInnerGap != that.bottomInnerGap) { 242 return false; 243 } 244 if (this.bottomOuterGap != that.bottomOuterGap) { 245 return false; 246 } 247 if (!Objects.equals(this.font, that.font)) { 248 return false; 249 } 250 if (!Objects.equals(this.markers, that.markers)) { 251 return false; 252 } 253 return true; 254 } 255 256 /** 257 * Returns a hash code for the object. 258 * 259 * @return A hash code. 260 */ 261 @Override 262 public int hashCode() { 263 int result = 37; 264 result = 19 * result + this.font.hashCode(); 265 result = 19 * result + this.markers.hashCode(); 266 return result; 267 } 268 269}