001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, 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 029package org.jfree.chart.ui; 030 031import java.awt.geom.Rectangle2D; 032import java.io.Serializable; 033import org.jfree.chart.util.UnitType; 034 035/** 036 * Represents the insets for a rectangle, specified in absolute or relative 037 * terms. This class is immutable. 038 */ 039public class RectangleInsets implements Serializable { 040 041 /** For serialization. */ 042 private static final long serialVersionUID = 1902273207559319996L; 043 044 /** 045 * A useful constant representing zero insets. 046 */ 047 public static final RectangleInsets ZERO_INSETS = new RectangleInsets( 048 UnitType.ABSOLUTE, 0.0, 0.0, 0.0, 0.0); 049 050 /** Absolute or relative units. */ 051 private UnitType unitType; 052 053 /** The top insets. */ 054 private double top; 055 056 /** The left insets. */ 057 private double left; 058 059 /** The bottom insets. */ 060 private double bottom; 061 062 /** The right insets. */ 063 private double right; 064 065 /** 066 * Creates a new instance with all insets initialised to {@code 1.0}. 067 */ 068 public RectangleInsets() { 069 this(1.0, 1.0, 1.0, 1.0); 070 } 071 072 /** 073 * Creates a new instance with the specified insets (as 'absolute' units). 074 * 075 * @param top the top insets. 076 * @param left the left insets. 077 * @param bottom the bottom insets. 078 * @param right the right insets. 079 */ 080 public RectangleInsets(double top, double left, double bottom, 081 double right) { 082 this(UnitType.ABSOLUTE, top, left, bottom, right); 083 } 084 085 /** 086 * Creates a new instance. 087 * 088 * @param unitType absolute or relative units ({@code null} not 089 * permitted). 090 * @param top the top insets. 091 * @param left the left insets. 092 * @param bottom the bottom insets. 093 * @param right the right insets. 094 */ 095 public RectangleInsets(UnitType unitType, double top, double left, 096 double bottom, double right) { 097 if (unitType == null) { 098 throw new IllegalArgumentException("Null 'unitType' argument."); 099 } 100 this.unitType = unitType; 101 this.top = top; 102 this.bottom = bottom; 103 this.left = left; 104 this.right = right; 105 } 106 107 /** 108 * Returns the unit type (absolute or relative). This specifies whether 109 * the insets are measured as Java2D units or percentages. 110 * 111 * @return The unit type (never {@code null}). 112 */ 113 public UnitType getUnitType() { 114 return this.unitType; 115 } 116 117 /** 118 * Returns the top insets. 119 * 120 * @return The top insets. 121 */ 122 public double getTop() { 123 return this.top; 124 } 125 126 /** 127 * Returns the bottom insets. 128 * 129 * @return The bottom insets. 130 */ 131 public double getBottom() { 132 return this.bottom; 133 } 134 135 /** 136 * Returns the left insets. 137 * 138 * @return The left insets. 139 */ 140 public double getLeft() { 141 return this.left; 142 } 143 144 /** 145 * Returns the right insets. 146 * 147 * @return The right insets. 148 */ 149 public double getRight() { 150 return this.right; 151 } 152 153 /** 154 * Tests this instance for equality with an arbitrary object. 155 * 156 * @param obj the object ({@code null} permitted). 157 * 158 * @return A boolean. 159 */ 160 @Override 161 public boolean equals(Object obj) { 162 if (obj == this) { 163 return true; 164 } 165 if (!(obj instanceof RectangleInsets)) { 166 return false; 167 } 168 final RectangleInsets that = (RectangleInsets) obj; 169 if (that.unitType != this.unitType) { 170 return false; 171 } 172 if (this.left != that.left) { 173 return false; 174 } 175 if (this.right != that.right) { 176 return false; 177 } 178 if (this.top != that.top) { 179 return false; 180 } 181 if (this.bottom != that.bottom) { 182 return false; 183 } 184 return true; 185 } 186 187 /** 188 * Returns a hash code for the object. 189 * 190 * @return A hash code. 191 */ 192 @Override 193 public int hashCode() { 194 int result; 195 long temp; 196 result = (this.unitType != null ? this.unitType.hashCode() : 0); 197 temp = this.top != +0.0d ? Double.doubleToLongBits(this.top) : 0L; 198 result = 29 * result + (int) (temp ^ (temp >>> 32)); 199 temp = this.bottom != +0.0d ? Double.doubleToLongBits(this.bottom) : 0L; 200 result = 29 * result + (int) (temp ^ (temp >>> 32)); 201 temp = this.left != +0.0d ? Double.doubleToLongBits(this.left) : 0L; 202 result = 29 * result + (int) (temp ^ (temp >>> 32)); 203 temp = this.right != +0.0d ? Double.doubleToLongBits(this.right) : 0L; 204 result = 29 * result + (int) (temp ^ (temp >>> 32)); 205 return result; 206 } 207 208 /** 209 * Returns a textual representation of this instance, useful for debugging 210 * purposes. 211 * 212 * @return A string representing this instance. 213 */ 214 @Override 215 public String toString() { 216 return "RectangleInsets[t=" + this.top + ",l=" + this.left 217 + ",b=" + this.bottom + ",r=" + this.right + "]"; 218 } 219 220 /** 221 * Creates an adjusted rectangle using the supplied rectangle, the insets 222 * specified by this instance, and the horizontal and vertical 223 * adjustment types. 224 * 225 * @param base the base rectangle ({@code null} not permitted). 226 * @param horizontal the horizontal adjustment type ({@code null} not 227 * permitted). 228 * @param vertical the vertical adjustment type ({@code null} not 229 * permitted). 230 * 231 * @return The inset rectangle. 232 */ 233 public Rectangle2D createAdjustedRectangle(Rectangle2D base, 234 LengthAdjustmentType horizontal, LengthAdjustmentType vertical) { 235 if (base == null) { 236 throw new IllegalArgumentException("Null 'base' argument."); 237 } 238 double x = base.getX(); 239 double y = base.getY(); 240 double w = base.getWidth(); 241 double h = base.getHeight(); 242 if (horizontal == LengthAdjustmentType.EXPAND) { 243 final double leftOutset = calculateLeftOutset(w); 244 x = x - leftOutset; 245 w = w + leftOutset + calculateRightOutset(w); 246 } 247 else if (horizontal == LengthAdjustmentType.CONTRACT) { 248 final double leftMargin = calculateLeftInset(w); 249 x = x + leftMargin; 250 w = w - leftMargin - calculateRightInset(w); 251 } 252 if (vertical == LengthAdjustmentType.EXPAND) { 253 final double topMargin = calculateTopOutset(h); 254 y = y - topMargin; 255 h = h + topMargin + calculateBottomOutset(h); 256 } 257 else if (vertical == LengthAdjustmentType.CONTRACT) { 258 final double topMargin = calculateTopInset(h); 259 y = y + topMargin; 260 h = h - topMargin - calculateBottomInset(h); 261 } 262 return new Rectangle2D.Double(x, y, w, h); 263 } 264 265 /** 266 * Creates an 'inset' rectangle. 267 * 268 * @param base the base rectangle ({@code null} not permitted). 269 * 270 * @return The inset rectangle. 271 */ 272 public Rectangle2D createInsetRectangle(Rectangle2D base) { 273 return createInsetRectangle(base, true, true); 274 } 275 276 /** 277 * Creates an 'inset' rectangle. 278 * 279 * @param base the base rectangle ({@code null} not permitted). 280 * @param horizontal apply horizontal insets? 281 * @param vertical apply vertical insets? 282 * 283 * @return The inset rectangle. 284 */ 285 public Rectangle2D createInsetRectangle(Rectangle2D base, 286 boolean horizontal, boolean vertical) { 287 if (base == null) { 288 throw new IllegalArgumentException("Null 'base' argument."); 289 } 290 double topMargin = 0.0; 291 double bottomMargin = 0.0; 292 if (vertical) { 293 topMargin = calculateTopInset(base.getHeight()); 294 bottomMargin = calculateBottomInset(base.getHeight()); 295 } 296 double leftMargin = 0.0; 297 double rightMargin = 0.0; 298 if (horizontal) { 299 leftMargin = calculateLeftInset(base.getWidth()); 300 rightMargin = calculateRightInset(base.getWidth()); 301 } 302 return new Rectangle2D.Double(base.getX() + leftMargin, 303 base.getY() + topMargin, 304 base.getWidth() - leftMargin - rightMargin, 305 base.getHeight() - topMargin - bottomMargin); 306 } 307 308 /** 309 * Creates an outset rectangle. 310 * 311 * @param base the base rectangle ({@code null} not permitted). 312 * 313 * @return An outset rectangle. 314 */ 315 public Rectangle2D createOutsetRectangle(Rectangle2D base) { 316 return createOutsetRectangle(base, true, true); 317 } 318 319 /** 320 * Creates an outset rectangle. 321 * 322 * @param base the base rectangle ({@code null} not permitted). 323 * @param horizontal apply horizontal insets? 324 * @param vertical apply vertical insets? 325 * 326 * @return An outset rectangle. 327 */ 328 public Rectangle2D createOutsetRectangle(Rectangle2D base, 329 boolean horizontal, boolean vertical) { 330 if (base == null) { 331 throw new IllegalArgumentException("Null 'base' argument."); 332 } 333 double topMargin = 0.0; 334 double bottomMargin = 0.0; 335 if (vertical) { 336 topMargin = calculateTopOutset(base.getHeight()); 337 bottomMargin = calculateBottomOutset(base.getHeight()); 338 } 339 double leftMargin = 0.0; 340 double rightMargin = 0.0; 341 if (horizontal) { 342 leftMargin = calculateLeftOutset(base.getWidth()); 343 rightMargin = calculateRightOutset(base.getWidth()); 344 } 345 return new Rectangle2D.Double(base.getX() - leftMargin, 346 base.getY() - topMargin, 347 base.getWidth() + leftMargin + rightMargin, 348 base.getHeight() + topMargin + bottomMargin); 349 } 350 351 /** 352 * Returns the top margin. 353 * 354 * @param height the height of the base rectangle. 355 * 356 * @return The top margin (in Java2D units). 357 */ 358 public double calculateTopInset(double height) { 359 double result = this.top; 360 if (this.unitType == UnitType.RELATIVE) { 361 result = (this.top * height); 362 } 363 return result; 364 } 365 366 /** 367 * Returns the top margin. 368 * 369 * @param height the height of the base rectangle. 370 * 371 * @return The top margin (in Java2D units). 372 */ 373 public double calculateTopOutset(double height) { 374 double result = this.top; 375 if (this.unitType == UnitType.RELATIVE) { 376 result = (height / (1 - this.top - this.bottom)) * this.top; 377 } 378 return result; 379 } 380 381 /** 382 * Returns the bottom margin. 383 * 384 * @param height the height of the base rectangle. 385 * 386 * @return The bottom margin (in Java2D units). 387 */ 388 public double calculateBottomInset(double height) { 389 double result = this.bottom; 390 if (this.unitType == UnitType.RELATIVE) { 391 result = (this.bottom * height); 392 } 393 return result; 394 } 395 396 /** 397 * Returns the bottom margin. 398 * 399 * @param height the height of the base rectangle. 400 * 401 * @return The bottom margin (in Java2D units). 402 */ 403 public double calculateBottomOutset(double height) { 404 double result = this.bottom; 405 if (this.unitType == UnitType.RELATIVE) { 406 result = (height / (1 - this.top - this.bottom)) * this.bottom; 407 } 408 return result; 409 } 410 411 /** 412 * Returns the left margin. 413 * 414 * @param width the width of the base rectangle. 415 * 416 * @return The left margin (in Java2D units). 417 */ 418 public double calculateLeftInset(double width) { 419 double result = this.left; 420 if (this.unitType == UnitType.RELATIVE) { 421 result = (this.left * width); 422 } 423 return result; 424 } 425 426 /** 427 * Returns the left margin. 428 * 429 * @param width the width of the base rectangle. 430 * 431 * @return The left margin (in Java2D units). 432 */ 433 public double calculateLeftOutset(double width) { 434 double result = this.left; 435 if (this.unitType == UnitType.RELATIVE) { 436 result = (width / (1 - this.left - this.right)) * this.left; 437 } 438 return result; 439 } 440 441 /** 442 * Returns the right margin. 443 * 444 * @param width the width of the base rectangle. 445 * 446 * @return The right margin (in Java2D units). 447 */ 448 public double calculateRightInset(double width) { 449 double result = this.right; 450 if (this.unitType == UnitType.RELATIVE) { 451 result = (this.right * width); 452 } 453 return result; 454 } 455 456 /** 457 * Returns the right margin. 458 * 459 * @param width the width of the base rectangle. 460 * 461 * @return The right margin (in Java2D units). 462 */ 463 public double calculateRightOutset(double width) { 464 double result = this.right; 465 if (this.unitType == UnitType.RELATIVE) { 466 result = (width / (1 - this.left - this.right)) * this.right; 467 } 468 return result; 469 } 470 471 /** 472 * Trims the given width to allow for the insets. 473 * 474 * @param width the width. 475 * 476 * @return The trimmed width. 477 */ 478 public double trimWidth(double width) { 479 return width - calculateLeftInset(width) - calculateRightInset(width); 480 } 481 482 /** 483 * Extends the given width to allow for the insets. 484 * 485 * @param width the width. 486 * 487 * @return The extended width. 488 */ 489 public double extendWidth(double width) { 490 return width + calculateLeftOutset(width) + calculateRightOutset(width); 491 } 492 493 /** 494 * Trims the given height to allow for the insets. 495 * 496 * @param height the height. 497 * 498 * @return The trimmed height. 499 */ 500 public double trimHeight(double height) { 501 return height - calculateTopInset(height) 502 - calculateBottomInset(height); 503 } 504 505 /** 506 * Extends the given height to allow for the insets. 507 * 508 * @param height the height. 509 * 510 * @return The extended height. 511 */ 512 public double extendHeight(double height) { 513 return height + calculateTopOutset(height) 514 + calculateBottomOutset(height); 515 } 516 517 /** 518 * Shrinks the given rectangle by the amount of these insets. 519 * 520 * @param area the area ({@code null} not permitted). 521 */ 522 public void trim(Rectangle2D area) { 523 double w = area.getWidth(); 524 double h = area.getHeight(); 525 double l = calculateLeftInset(w); 526 double r = calculateRightInset(w); 527 double t = calculateTopInset(h); 528 double b = calculateBottomInset(h); 529 area.setRect(area.getX() + l, area.getY() + t, w - l - r, h - t - b); 530 } 531 532} 533