content: /* ===========================================================
   * JFreeChart : a free chart library for the Java(tm) platform
   * ===========================================================
   *
   * (C) Copyright 2000-2012, by Object Refinery Limited and Contributors.
   *
   * Project Info:  http://www.jfree.org/jfreechart/index.html
   *
   * This library is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation; either version 2.1 of the License, or
   * (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
   * License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
   * USA.
   *
   * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   * Other names may be trademarks of their respective owners.]
   *
   * --------------------
   * RectangleInsets.java
   * --------------------
   * (C) Copyright 2004-2012, by Object Refinery Limited.
   *
   * Original Author:  David Gilbert (for Object Refinery Limited);
   * Contributor(s):   -;
   *
   * $Id: RectangleInsets.java,v 1.15 2007/03/16 14:29:45 mungady Exp $
   *
   * Changes:
   * --------
   * 11-Feb-2004 : Version 1 (DG);
   * 14-Jun-2004 : Implemented Serializable (DG);
   * 02-Feb-2005 : Added new methods and renamed some existing methods (DG);
   * 22-Feb-2005 : Added a new constructor for convenience (DG);
   * 19-Apr-2005 : Changed order of parameters in constructors to match
   *               java.awt.Insets (DG);
   * 16-Mar-2007 : Added default constructor (DG);
   * 16-Jun-2012 : Moved from JCommon to JFreeChart (DG);
   *
   */
  
  package org.jfree.chart.ui;
  
  import java.awt.geom.Rectangle2D;
  import java.io.Serializable;
  
  import org.jfree.chart.util.UnitType;
  
  /**
   * Represents the insets for a rectangle, specified in absolute or relative
   * terms. This class is immutable.
   */
  public class RectangleInsets implements Serializable {
  
      /** For serialization. */
      private static final long serialVersionUID = 1902273207559319996L;
  
      /**
       * A useful constant representing zero insets.
       */
      public static final RectangleInsets ZERO_INSETS = new RectangleInsets(
          UnitType.ABSOLUTE, 0.0, 0.0, 0.0, 0.0);
  
      /** Absolute or relative units. */
      private UnitType unitType;
  
      /** The top insets. */
      private double top;
  
      /** The left insets. */
      private double left;
  
      /** The bottom insets. */
      private double bottom;
  
      /** The right insets. */
      private double right;
  
      /**
       * Creates a new instance with all insets initialised to <code>1.0</code>.
       *
       * @since 1.0.9
       */
      public RectangleInsets() {
          this(1.0, 1.0, 1.0, 1.0);
      }
  
      /**
       * Creates a new instance with the specified insets (as 'absolute' units).
       *
       * @param top  the top insets.
       * @param left  the left insets.
       * @param bottom  the bottom insets.
       * @param right  the right insets.
       */
      public RectangleInsets(final double top, final double left,
                             final double bottom, final double right) {
          this(UnitType.ABSOLUTE, top, left, bottom, right);
      }
  
      /**
       * Creates a new instance.
       *
       * @param unitType  absolute or relative units (<code>null</code> not
       *                  permitted).
       * @param top  the top insets.
       * @param left  the left insets.
       * @param bottom  the bottom insets.
       * @param right  the right insets.
       */
      public RectangleInsets(final UnitType unitType,
                             final double top, final double left,
                             final double bottom, final double right) {
          if (unitType == null) {
              throw new IllegalArgumentException("Null 'unitType' argument.");
          }
          this.unitType = unitType;
          this.top = top;
          this.bottom = bottom;
          this.left = left;
          this.right = right;
      }
  
      /**
       * Returns the unit type (absolute or relative).  This specifies whether
       * the insets are measured as Java2D units or percentages.
       *
       * @return The unit type (never <code>null</code>).
       */
      public UnitType getUnitType() {
          return this.unitType;
      }
  
      /**
       * Returns the top insets.
       *
       * @return The top insets.
       */
      public double getTop() {
          return this.top;
      }
  
      /**
       * Returns the bottom insets.
       *
       * @return The bottom insets.
       */
      public double getBottom() {
          return this.bottom;
      }
  
      /**
       * Returns the left insets.
       *
       * @return The left insets.
       */
      public double getLeft() {
          return this.left;
      }
  
      /**
       * Returns the right insets.
       *
       * @return The right insets.
       */
      public double getRight() {
          return this.right;
      }
  
      /**
       * Tests this instance for equality with an arbitrary object.
       *
       * @param obj  the object (<code>null</code> permitted).
       *
       * @return A boolean.
       */
      @Override
      public boolean equals(final Object obj) {
          if (obj == this) {
              return true;
          }
          if (!(obj instanceof RectangleInsets)) {
                  return false;
          }
          final RectangleInsets that = (RectangleInsets) obj;
          if (that.unitType != this.unitType) {
              return false;
          }
          if (this.left != that.left) {
              return false;
          }
          if (this.right != that.right) {
              return false;
          }
          if (this.top != that.top) {
              return false;
          }
          if (this.bottom != that.bottom) {
              return false;
          }
          return true;
      }
  
      /**
       * Returns a hash code for the object.
       *
       * @return A hash code.
       */
      @Override
      public int hashCode() {
          int result;
          long temp;
          result = (this.unitType != null ? this.unitType.hashCode() : 0);
          temp = this.top != +0.0d ? Double.doubleToLongBits(this.top) : 0L;
          result = 29 * result + (int) (temp ^ (temp >>> 32));
          temp = this.bottom != +0.0d ? Double.doubleToLongBits(this.bottom) : 0L;
          result = 29 * result + (int) (temp ^ (temp >>> 32));
          temp = this.left != +0.0d ? Double.doubleToLongBits(this.left) : 0L;
          result = 29 * result + (int) (temp ^ (temp >>> 32));
          temp = this.right != +0.0d ? Double.doubleToLongBits(this.right) : 0L;
          result = 29 * result + (int) (temp ^ (temp >>> 32));
          return result;
      }
  
      /**
       * Returns a textual representation of this instance, useful for debugging
       * purposes.
       *
       * @return A string representing this instance.
       */
      @Override
      public String toString() {
          return "RectangleInsets[t=" + this.top + ",l=" + this.left
                  + ",b=" + this.bottom + ",r=" + this.right + "]";
      }
  
      /**
       * Creates an adjusted rectangle using the supplied rectangle, the insets
       * specified by this instance, and the horizontal and vertical
       * adjustment types.
       *
       * @param base  the base rectangle (<code>null</code> not permitted).
       * @param horizontal  the horizontal adjustment type (<code>null</code> not
       *                    permitted).
       * @param vertical  the vertical adjustment type (<code>null</code> not
       *                  permitted).
       *
       * @return The inset rectangle.
       */
      public Rectangle2D createAdjustedRectangle(final Rectangle2D base,
                                            final LengthAdjustmentType horizontal,
                                                    final LengthAdjustmentType vertical) {
          if (base == null) {
              throw new IllegalArgumentException("Null 'base' argument.");
          }
          double x = base.getX();
          double y = base.getY();
          double w = base.getWidth();
          double h = base.getHeight();
          if (horizontal == LengthAdjustmentType.EXPAND) {
              final double leftOutset = calculateLeftOutset(w);
              x = x - leftOutset;
              w = w + leftOutset + calculateRightOutset(w);
          }
          else if (horizontal == LengthAdjustmentType.CONTRACT) {
              final double leftMargin = calculateLeftInset(w);
              x = x + leftMargin;
              w = w - leftMargin - calculateRightInset(w);
          }
          if (vertical == LengthAdjustmentType.EXPAND) {
              final double topMargin = calculateTopOutset(h);
              y = y - topMargin;
              h = h + topMargin + calculateBottomOutset(h);
          }
          else if (vertical == LengthAdjustmentType.CONTRACT) {
              final double topMargin = calculateTopInset(h);
              y = y + topMargin;
              h = h - topMargin - calculateBottomInset(h);
          }
          return new Rectangle2D.Double(x, y, w, h);
      }
  
      /**
       * Creates an 'inset' rectangle.
       *
       * @param base  the base rectangle (<code>null</code> not permitted).
       *
       * @return The inset rectangle.
       */
      public Rectangle2D createInsetRectangle(final Rectangle2D base) {
          return createInsetRectangle(base, true, true);
      }
  
      /**
       * Creates an 'inset' rectangle.
       *
       * @param base  the base rectangle (<code>null</code> not permitted).
       * @param horizontal  apply horizontal insets?
       * @param vertical  apply vertical insets?
       *
       * @return The inset rectangle.
       */
      public Rectangle2D createInsetRectangle(final Rectangle2D base,
                                              final boolean horizontal,
                                              final boolean vertical) {
          if (base == null) {
              throw new IllegalArgumentException("Null 'base' argument.");
          }
          double topMargin = 0.0;
          double bottomMargin = 0.0;
          if (vertical) {
              topMargin = calculateTopInset(base.getHeight());
              bottomMargin = calculateBottomInset(base.getHeight());
          }
          double leftMargin = 0.0;
          double rightMargin = 0.0;
          if (horizontal) {
              leftMargin = calculateLeftInset(base.getWidth());
              rightMargin = calculateRightInset(base.getWidth());
          }
          return new Rectangle2D.Double(
              base.getX() + leftMargin,
              base.getY() + topMargin,
              base.getWidth() - leftMargin - rightMargin,
              base.getHeight() - topMargin - bottomMargin
          );
      }
  
      /**
       * Creates an outset rectangle.
       *
       * @param base  the base rectangle (<code>null</code> not permitted).
       *
       * @return An outset rectangle.
       */
      public Rectangle2D createOutsetRectangle(final Rectangle2D base) {
          return createOutsetRectangle(base, true, true);
      }
  
      /**
       * Creates an outset rectangle.
       *
       * @param base  the base rectangle (<code>null</code> not permitted).
       * @param horizontal  apply horizontal insets?
       * @param vertical  apply vertical insets?
       *
       * @return An outset rectangle.
       */
      public Rectangle2D createOutsetRectangle(final Rectangle2D base,
                                               final boolean horizontal,
                                               final boolean vertical) {
          if (base == null) {
              throw new IllegalArgumentException("Null 'base' argument.");
          }
          double topMargin = 0.0;
          double bottomMargin = 0.0;
          if (vertical) {
              topMargin = calculateTopOutset(base.getHeight());
              bottomMargin = calculateBottomOutset(base.getHeight());
          }
          double leftMargin = 0.0;
          double rightMargin = 0.0;
          if (horizontal) {
              leftMargin = calculateLeftOutset(base.getWidth());
              rightMargin = calculateRightOutset(base.getWidth());
          }
          return new Rectangle2D.Double(
              base.getX() - leftMargin,
              base.getY() - topMargin,
              base.getWidth() + leftMargin + rightMargin,
              base.getHeight() + topMargin + bottomMargin
          );
      }
  
      /**
       * Returns the top margin.
       *
       * @param height  the height of the base rectangle.
       *
       * @return The top margin (in Java2D units).
       */
      public double calculateTopInset(final double height) {
          double result = this.top;
          if (this.unitType == UnitType.RELATIVE) {
              result = (this.top * height);
          }
          return result;
      }
  
      /**
       * Returns the top margin.
       *
       * @param height  the height of the base rectangle.
       *
       * @return The top margin (in Java2D units).
       */
      public double calculateTopOutset(final double height) {
          double result = this.top;
          if (this.unitType == UnitType.RELATIVE) {
              result = (height / (1 - this.top - this.bottom)) * this.top;
          }
          return result;
      }
  
      /**
       * Returns the bottom margin.
       *
       * @param height  the height of the base rectangle.
       *
       * @return The bottom margin (in Java2D units).
       */
      public double calculateBottomInset(final double height) {
          double result = this.bottom;
          if (this.unitType == UnitType.RELATIVE) {
              result = (this.bottom * height);
          }
          return result;
      }
  
      /**
       * Returns the bottom margin.
       *
       * @param height  the height of the base rectangle.
       *
       * @return The bottom margin (in Java2D units).
       */
      public double calculateBottomOutset(final double height) {
          double result = this.bottom;
          if (this.unitType == UnitType.RELATIVE) {
              result = (height / (1 - this.top - this.bottom)) * this.bottom;
          }
          return result;
      }
  
      /**
       * Returns the left margin.
       *
       * @param width  the width of the base rectangle.
       *
       * @return The left margin (in Java2D units).
       */
      public double calculateLeftInset(final double width) {
          double result = this.left;
          if (this.unitType == UnitType.RELATIVE) {
              result = (this.left * width);
          }
          return result;
      }
  
      /**
       * Returns the left margin.
       *
       * @param width  the width of the base rectangle.
       *
       * @return The left margin (in Java2D units).
       */
      public double calculateLeftOutset(final double width) {
          double result = this.left;
          if (this.unitType == UnitType.RELATIVE) {
              result = (width / (1 - this.left - this.right)) * this.left;
          }
          return result;
      }
  
      /**
       * Returns the right margin.
       *
       * @param width  the width of the base rectangle.
       *
       * @return The right margin (in Java2D units).
       */
      public double calculateRightInset(final double width) {
          double result = this.right;
          if (this.unitType == UnitType.RELATIVE) {
              result = (this.right * width);
          }
          return result;
      }
  
      /**
       * Returns the right margin.
       *
       * @param width  the width of the base rectangle.
       *
       * @return The right margin (in Java2D units).
       */
      public double calculateRightOutset(final double width) {
          double result = this.right;
          if (this.unitType == UnitType.RELATIVE) {
              result = (width / (1 - this.left - this.right)) * this.right;
          }
          return result;
      }
  
      /**
       * Trims the given width to allow for the insets.
       *
       * @param width  the width.
       *
       * @return The trimmed width.
       */
      public double trimWidth(final double width) {
          return width - calculateLeftInset(width) - calculateRightInset(width);
      }
  
      /**
       * Extends the given width to allow for the insets.
       *
       * @param width  the width.
       *
       * @return The extended width.
       */
      public double extendWidth(final double width) {
          return width + calculateLeftOutset(width) + calculateRightOutset(width);
      }
  
      /**
       * Trims the given height to allow for the insets.
       *
       * @param height  the height.
       *
       * @return The trimmed height.
       */
      public double trimHeight(final double height) {
          return height
                 - calculateTopInset(height) - calculateBottomInset(height);
      }
  
      /**
       * Extends the given height to allow for the insets.
       *
       * @param height  the height.
       *
       * @return The extended height.
       */
      public double extendHeight(final double height) {
          return height
                 + calculateTopOutset(height) + calculateBottomOutset(height);
      }
  
      /**
       * Shrinks the given rectangle by the amount of these insets.
       *
       * @param area  the area (<code>null</code> not permitted).
       */
      public void trim(final Rectangle2D area) {
          final double w = area.getWidth();
          final double h = area.getHeight();
          final double l = calculateLeftInset(w);
          final double r = calculateRightInset(w);
          final double t = calculateTopInset(h);
          final double b = calculateBottomInset(h);
          area.setRect(area.getX() + l, area.getY() + t, w - l - r, h - t - b);
      }
  
  }
  