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