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