//******************************************************************************
//
// File:    Group.java
// Package: edu.rit.draw.item
// Unit:    Class edu.rit.draw.item.Group
//
// This Java source file is copyright (C) 2011 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ 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 General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************

package edu.rit.draw.item;

import edu.rit.draw.Drawing;

import java.awt.Graphics2D;

import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import java.util.ArrayList;

/**
 * Class Group provides a {@linkplain DrawingItem} that consists of a group of
 * other {@linkplain DrawingItem}s. To add drawing items to a group, call the
 * group's append() and prepend() methods.
 * 

* The group can be scaled as a unit by calling the xScale() and * yScale() methods. The group can be sheared as a unit by calling the * xShear() and yShear() methods. The group can be rotated as * a unit by calling the rotationAngle() method; the group is always * rotated around its center point. The order in which these transforms are * applied is first scaling, then shearing, then rotation. *

* The group's "bounding box" is the smallest rectangle that encloses all the * contained drawing items' bounding boxes, after applying the scale, * shear, and rotation transforms if any. The nw(), n(), * ne(), w(), c(), e(), sw(), * s(), and se() methods refer to points on the group's * bounding box. The setter versions of these methods translate the group so the * designated point on the group's bounding box coincides with the point * specified as the argument. The getter versions of these methods return the * designated point on the group's bounding box. *

* The group's "original bounding box" is the smallest rectangle that encloses * all the contained drawing items' bounding boxes, before applying the * scale, shear, and rotation transforms if any. The orig_nw(), * orig_n(), orig_ne(), orig_w(), orig_c(), * orig_e(), orig_sw(), orig_s(), and * orig_se() methods refer to points on the group's original bounding * box. The setter versions of these methods translate the group so the * designated point on the group's original bounding box coincides with the * point specified as the argument. The getter versions of these methods return * the designated point on the group's original bounding box. *

* The transform() method takes a point, applies the group's scaling, * shearing, rotation, and translation transforms to the point, and returns the * transformed point. You can use this method, for example, to find out where a * point defined before applying transforms to the group would end up after * applying transforms to the group. * * @author Alan Kaminsky * @version 29-Apr-2011 */ public class Group extends DrawingItem implements Externalizable { // Exported constants. /** * The normal X scale factor (1). */ public static final double NORMAL_X_SCALE = 1.0; /** * The normal Y scale factor (1). */ public static final double NORMAL_Y_SCALE = 1.0; /** * The normal X shear factor (0). */ public static final double NORMAL_X_SHEAR = 0.0; /** * The normal Y shear factor (0). */ public static final double NORMAL_Y_SHEAR = 0.0; /** * The normal rotation angle (0). */ public static final double NORMAL_ROTATION_ANGLE = 0.0; // Hidden data members. private static final long serialVersionUID = -2445041343028527776L; // Default attributes. static double theDefaultXScale = NORMAL_X_SCALE; static double theDefaultYScale = NORMAL_Y_SCALE; static double theDefaultXShear = NORMAL_X_SHEAR; static double theDefaultYShear = NORMAL_Y_SHEAR; static double theDefaultRotationAngle = NORMAL_ROTATION_ANGLE; // List of drawing items in this group. ArrayList myItemList; // Coordinates of most recently specified corner. double x; double y; // Factors for going from specified corner to northwest corner. double xFactor; double yFactor; // True if corner was specified with respect to original (untransformed) // bounding box. False if corner was specified with respect to (transformed) // bounding box. boolean cornerIsOriginal; // Transform parameters. double myXScale = theDefaultXScale; double myYScale = theDefaultYScale; double myXShear = theDefaultXShear; double myYShear = theDefaultYShear; double myRotationAngle = theDefaultRotationAngle; // Transform. If myTransform is null, the transform must be recomputed. AffineTransform myTransform; // Northwest point and size of original bounding box. If myOriginalNw is // null, these objects must be recomputed. Point myOriginalNw; Size myOriginalSize; // Northwest point and size of transformed bounding box. If myTransformedNw // is null, these objects must be recomputed. Point myTransformedNw; Size myTransformedSize; // Exported constructors. /** * Construct a new empty group. The group's northwest corner is located at * (0,0). */ public Group() { super(); this.myItemList = new ArrayList(); } /** * Construct a new group that contains the same drawing items, has the same * transforms, and is at the same location, as the given group. * * @param theGroup Group. * * @exception NullPointerException * (unchecked exception) Thrown if theGroup is null. */ public Group (Group theGroup) { super (theGroup); this.myItemList = new ArrayList (theGroup.myItemList); this.x = theGroup.x; this.y = theGroup.y; this.xFactor = theGroup.xFactor; this.yFactor = theGroup.yFactor; this.cornerIsOriginal = theGroup.cornerIsOriginal; this.myXScale = theGroup.myXScale; this.myYScale = theGroup.myYScale; this.myXShear = theGroup.myXShear; this.myYShear = theGroup.myYShear; this.myRotationAngle = theGroup.myRotationAngle; } // Exported operations. /** * Returns the default X scale factor. * * @return Default X scale factor. */ public static double defaultXScale() { return theDefaultXScale; } /** * Set the default X scale factor. Before calling this method the first * time, the default X scale factor is 1. * * @param theXScale Default X scale factor. */ public static void defaultXScale (double theXScale) { theDefaultXScale = theXScale; } /** * Returns the default Y scale factor. * * @return Default Y scale factor. */ public static double defaultYScale() { return theDefaultYScale; } /** * Set the default Y scale factor. Before calling this method the first * time, the default Y scale factor is 1. * * @param theYScale Default Y scale factor. */ public static void defaultYScale (double theYScale) { theDefaultYScale = theYScale; } /** * Returns the default X shear factor. * * @return Default X shear factor. */ public static double defaultXShear() { return theDefaultXShear; } /** * Set the default X shear factor. Before calling this method the first * time, the default X shear factor is 0. * * @param theXShear Default X shear factor. */ public static void defaultXShear (double theXShear) { theDefaultXShear = theXShear; } /** * Returns the default Y shear factor. * * @return Default Y shear factor. */ public static double defaultYShear() { return theDefaultYShear; } /** * Set the default Y shear factor. Before calling this method the first * time, the default Y shear factor is 0. * * @param theYShear Default Y shear factor. */ public static void defaultYShear (double theYShear) { theDefaultYShear = theYShear; } /** * Returns the default rotation angle. * * @return Default rotation angle. */ public static double defaultRotationAngle() { return theDefaultRotationAngle; } /** * Set the default rotation angle. Before calling this method the first * time, the default rotation angle is 0. * * @param theAngle Default rotation angle. */ public static void defaultRotationAngle (double theAngle) { theDefaultRotationAngle = theAngle; } /** * Returns this group's X scale factor. * * @return X scale factor. */ public double xScale() { return myXScale; } /** * Set this group's X scale factor. * * @param theXScale X scale factor. * * @return This group. */ public Group xScale (double theXScale) { myXScale = theXScale; myTransform = null; myTransformedNw = null; return this; } /** * Returns this group's Y scale factor. * * @return Y scale factor. */ public double yScale() { return myYScale; } /** * Set this group's Y scale factor. * * @param theYScale Y scale factor. * * @return This group. */ public Group yScale (double theYScale) { myYScale = theYScale; myTransform = null; myTransformedNw = null; return this; } /** * Returns this group's X shear factor. * * @return X shear factor. */ public double xShear() { return myXShear; } /** * Set this group's X shear factor. * * @param theXShear X shear factor. * * @return This group. */ public Group xShear (double theXShear) { myXShear = theXShear; myTransform = null; myTransformedNw = null; return this; } /** * Returns this group's Y shear factor. * * @return Y shear factor. */ public double yShear() { return myYShear; } /** * Set this group's Y shear factor. * * @param theYShear Y shear factor. * * @return This group. */ public Group yShear (double theYShear) { myYShear = theYShear; myTransform = null; myTransformedNw = null; return this; } /** * Returns this group's rotation angle. * * @return Rotation angle. */ public double rotationAngle() { return myRotationAngle; } /** * Set this group's rotation angle. * * @param theAngle Rotation angle. * * @return This group. */ public Group rotationAngle (double theAngle) { myRotationAngle = theAngle; myTransform = null; myTransformedNw = null; return this; } /** * Clear this group. All drawing items in this group are removed. * * @return This group. */ public Group clear() { myItemList.clear(); myOriginalNw = null; myTransformedNw = null; return this; } /** * Add the given drawing item to the end of this group's list of drawing * items. * * @param theItem Drawing item. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if theItem is null. */ public Group append (DrawingItem theItem) { if (theItem == null) throw new NullPointerException(); myItemList.add (theItem); myOriginalNw = null; myTransformedNw = null; return this; } /** * Add the given drawing item to the beginning of this group's list of * drawing items. * * @param theItem Drawing item. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if theItem is null. */ public Group prepend (DrawingItem theItem) { if (theItem == null) throw new NullPointerException(); myItemList.add (0, theItem); myOriginalNw = null; myTransformedNw = null; return this; } /** * Returns the size of this group's bounding box. * * @return Size. */ public Size size() { computeTransformedBoundingBox(); return myTransformedSize; } /** * Returns the width of this group's bounding box. * * @return Width. */ public double width() { computeTransformedBoundingBox(); return myTransformedSize.width(); } /** * Returns the height of this group's bounding box. * * @return Height. */ public double height() { computeTransformedBoundingBox(); return myTransformedSize.height(); } /** * Returns the northwest corner point of this group's bounding box. * * @return Northwest corner point. */ public Point nw() { computeTransformedBoundingBox(); return myTransformedNw; } /** * Set the northwest corner point of this group's bounding box. * * @param x X coordinate of northwest corner point. * @param y Y coordinate of northwest corner point. * * @return This group. */ public Group nw (double x, double y) { doNw (x, y, false); return this; } /** * Set the northwest corner point of this group's bounding box. * * @param thePoint Northwest corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group nw (Point thePoint) { doNw (thePoint.x, thePoint.y, false); return this; } /** * Set the north middle point of this group's bounding box. * * @param x X coordinate of north middle point. * @param y Y coordinate of north middle point. * * @return This group. */ public Group n (double x, double y) { doN (x, y, false); return this; } /** * Set the north middle point of this group's bounding box. * * @param thePoint North middle point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group n (Point thePoint) { doN (thePoint.x, thePoint.y, false); return this; } /** * Set the northeast corner point of this group's bounding box. * * @param x X coordinate of northeast corner point. * @param y Y coordinate of northeast corner point. * * @return This group. */ public Group ne (double x, double y) { doNe (x, y, false); return this; } /** * Set the northeast corner point of this group's bounding box. * * @param thePoint Northeast corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group ne (Point thePoint) { doNe (thePoint.x, thePoint.y, false); return this; } /** * Set the west middle point of this group's bounding box. * * @param x X coordinate of west middle point. * @param y Y coordinate of west middle point. * * @return This group. */ public Group w (double x, double y) { doW (x, y, false); return this; } /** * Set the west middle point of this group's bounding box. * * @param thePoint West middle point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group w (Point thePoint) { doW (thePoint.x, thePoint.y, false); return this; } /** * Set the center point of this group's bounding box. * * @param x X coordinate of center point. * @param y Y coordinate of center point. * * @return This group. */ public Group c (double x, double y) { doC (x, y, false); return this; } /** * Set the center point of this group's bounding box. * * @param thePoint Center point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group c (Point thePoint) { doC (thePoint.x, thePoint.y, false); return this; } /** * Set the east middle point of this group's bounding box. * * @param x X coordinate of east middle point. * @param y Y coordinate of east middle point. * * @return This group. */ public Group e (double x, double y) { doE (x, y, false); return this; } /** * Set the east middle point of this group's bounding box. * * @param thePoint East middle point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group e (Point thePoint) { doE (thePoint.x, thePoint.y, false); return this; } /** * Set the southwest corner point of this group's bounding box. * * @param x X coordinate of southwest corner point. * @param y Y coordinate of southwest corner point. * * @return This group. */ public Group sw (double x, double y) { doSw (x, y, false); return this; } /** * Set the southwest corner point of this group's bounding box. * * @param thePoint Southwest corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group sw (Point thePoint) { doSw (thePoint.x, thePoint.y, false); return this; } /** * Set the south middle point of this group's bounding box. * * @param x X coordinate of south middle point. * @param y Y coordinate of south middle point. * * @return This group. */ public Group s (double x, double y) { doS (x, y, false); return this; } /** * Set the south middle point of this group's bounding box. * * @param thePoint South middle point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group s (Point thePoint) { doS (thePoint.x, thePoint.y, false); return this; } /** * Set the southeast corner point of this group's bounding box. * * @param x X coordinate of southeast corner point. * @param y Y coordinate of southeast corner point. * * @return This group. */ public Group se (double x, double y) { doSe (x, y, false); return this; } /** * Set the southeast corner point of this group's bounding box. * * @param thePoint Southeast corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group se (Point thePoint) { doSe (thePoint.x, thePoint.y, false); return this; } /** * Returns the northwest corner point of this group's original bounding box, * after applying the group's transformations if any. * * @return Northwest corner point. */ public Point orig_nw() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x, myOriginalNw.y); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the northwest corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param x X coordinate of northwest corner point. * @param y Y coordinate of northwest corner point. * * @return This group. */ public Group orig_nw (double x, double y) { doNw (x, y, true); return this; } /** * Set the northwest corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param thePoint Northwest corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_nw (Point thePoint) { doNw (thePoint.x, thePoint.y, true); return this; } /** * Returns the north center point of this group's original bounding box, * after applying the group's transformations if any. * * @return North center point. */ public Point orig_n() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x + 0.5*myOriginalSize.width, myOriginalNw.y); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the north center point of this group's original bounding box, after * applying the group's transformations if any. * * @param x X coordinate of north center point. * @param y Y coordinate of north center point. * * @return This group. */ public Group orig_n (double x, double y) { doN (x, y, true); return this; } /** * Set the north center point of this group's original bounding box, after * applying the group's transformations if any. * * @param thePoint North center point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_n (Point thePoint) { doN (thePoint.x, thePoint.y, true); return this; } /** * Returns the northeast corner point of this group's original bounding box, * after applying the group's transformations if any. * * @return Northeast corner point. */ public Point orig_ne() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x + myOriginalSize.width(), myOriginalNw.y); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the northeast corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param x X coordinate of northeast corner point. * @param y Y coordinate of northeast corner point. * * @return This group. */ public Group orig_ne (double x, double y) { doNe (x, y, true); return this; } /** * Set the northeast corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param thePoint Northeast corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_ne (Point thePoint) { doNe (thePoint.x, thePoint.y, true); return this; } /** * Returns the west center point of this group's original bounding box, * after applying the group's transformations if any. * * @return West center point. */ public Point orig_w() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x, myOriginalNw.y + 0.5*myOriginalSize.height()); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the west center point of this group's original bounding box, after * applying the group's transformations if any. * * @param x X coordinate of west center point. * @param y Y coordinate of west center point. * * @return This group. */ public Group orig_w (double x, double y) { doW (x, y, true); return this; } /** * Set the west center point of this group's original bounding box, after * applying the group's transformations if any. * * @param thePoint West center point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_w (Point thePoint) { doW (thePoint.x, thePoint.y, true); return this; } /** * Returns the center point of this group's original bounding box, after * applying the group's transformations if any. * * @return Center point. */ public Point orig_c() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x + 0.5*myOriginalSize.width(), myOriginalNw.y + 0.5*myOriginalSize.height()); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the center point of this group's original bounding box, after * applying the group's transformations if any. * * @param x X coordinate of center point. * @param y Y coordinate of center point. * * @return This group. */ public Group orig_c (double x, double y) { doC (x, y, true); return this; } /** * Set the center point of this group's original bounding box, after * applying the group's transformations if any. * * @param thePoint Center point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_c (Point thePoint) { doC (thePoint.x, thePoint.y, true); return this; } /** * Returns the east center point of this group's original bounding box, * after applying the group's transformations if any. * * @return East center point. */ public Point orig_e() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x + myOriginalSize.width(), myOriginalNw.y + 0.5*myOriginalSize.height()); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the east center point of this group's original bounding box, after * applying the group's transformations if any. * * @param x X coordinate of east center point. * @param y Y coordinate of east center point. * * @return This group. */ public Group orig_e (double x, double y) { doE (x, y, true); return this; } /** * Set the east center point of this group's original bounding box, after * applying the group's transformations if any. * * @param thePoint East center point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_e (Point thePoint) { doE (thePoint.x, thePoint.y, true); return this; } /** * Returns the southwest corner point of this group's original bounding box, * after applying the group's transformations if any. * * @return Southwest corner point. */ public Point orig_sw() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x, myOriginalNw.y + myOriginalSize.height()); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the southwest corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param x X coordinate of southwest corner point. * @param y Y coordinate of southwest corner point. * * @return This group. */ public Group orig_sw (double x, double y) { doSw (x, y, true); return this; } /** * Set the southwest corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param thePoint Southwest corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_sw (Point thePoint) { doSw (thePoint.x, thePoint.y, true); return this; } /** * Returns the south center point of this group's original bounding box, * after applying the group's transformations if any. * * @return South center point. */ public Point orig_s() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x + 0.5*myOriginalSize.width(), myOriginalNw.y + myOriginalSize.height()); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the south center point of this group's original bounding box, after * applying the group's transformations if any. * * @param x X coordinate of south center point. * @param y Y coordinate of south center point. * * @return This group. */ public Group orig_s (double x, double y) { doS (x, y, true); return this; } /** * Set the south center point of this group's original bounding box, after * applying the group's transformations if any. * * @param thePoint South center point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_s (Point thePoint) { doS (thePoint.x, thePoint.y, true); return this; } /** * Returns the southeast corner point of this group's original bounding box, * after applying the group's transformations if any. * * @return Southeast corner point. */ public Point orig_se() { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (myOriginalNw.x + myOriginalSize.width(), myOriginalNw.y + myOriginalSize.height()); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } /** * Set the southeast corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param x X coordinate of southeast corner point. * @param y Y coordinate of southeast corner point. * * @return This group. */ public Group orig_se (double x, double y) { doSe (x, y, true); return this; } /** * Set the southeast corner point of this group's original bounding box, * after applying the group's transformations if any. * * @param thePoint Southeast corner point. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if thePoint is null. */ public Group orig_se (Point thePoint) { doSe (thePoint.x, thePoint.y, true); return this; } /** * Returns the northwest corner point of this group's original bounding box, * without applying the group's transformations if any. * * @return Northwest corner point. */ public Point content_nw() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x, myOriginalNw.y); } /** * Returns the north center point of this group's original bounding box, * without applying the group's transformations if any. * * @return North center point. */ public Point content_n() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x + 0.5*myOriginalSize.width, myOriginalNw.y); } /** * Returns the northeast corner point of this group's original bounding box, * without applying the group's transformations if any. * * @return Northeast corner point. */ public Point content_ne() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x + myOriginalSize.width, myOriginalNw.y); } /** * Returns the west center point of this group's original bounding box, * without applying the group's transformations if any. * * @return West center point. */ public Point content_w() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x, myOriginalNw.y + 0.5*myOriginalSize.height); } /** * Returns the center point of this group's original bounding box, without * applying the group's transformations if any. * * @return Center point. */ public Point content_c() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x + 0.5*myOriginalSize.width, myOriginalNw.y + 0.5*myOriginalSize.height); } /** * Returns the east center point of this group's original bounding box, * without applying the group's transformations if any. * * @return East center point. */ public Point content_e() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x + myOriginalSize.width, myOriginalNw.y + 0.5*myOriginalSize.height); } /** * Returns the southwest corner point of this group's original bounding box, * without applying the group's transformations if any. * * @return Southwest corner point. */ public Point content_sw() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x, myOriginalNw.y + myOriginalSize.height); } /** * Returns the south center point of this group's original bounding box, * without applying the group's transformations if any. * * @return South center point. */ public Point content_s() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x + 0.5*myOriginalSize.width, myOriginalNw.y + myOriginalSize.height); } /** * Returns the southeast corner point of this group's original bounding box, * without applying the group's transformations if any. * * @return Southeast corner point. */ public Point content_se() { computeOriginalBoundingBox(); return new Point (myOriginalNw.x + myOriginalSize.width, myOriginalNw.y + myOriginalSize.height); } /** * Add this group to the end of the default drawing's sequence of drawing * items. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if there is no default drawing. * * @see Drawing#defaultDrawing() */ public Group add() { doAdd (Drawing.defaultDrawing()); return this; } /** * Add this group to the end of the given drawing's sequence of drawing * items. * * @param theDrawing Drawing. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if theDrawing is null. */ public Group add (Drawing theDrawing) { doAdd (theDrawing); return this; } /** * Add this group to the beginning of the default drawing's sequence of * drawing items. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if there is no default drawing. * * @see Drawing#defaultDrawing() */ public Group addFirst() { doAddFirst (Drawing.defaultDrawing()); return this; } /** * Add this group to the beginning of the given drawing's sequence of * drawing items. * * @param theDrawing Drawing. * * @return This group. * * @exception NullPointerException * (unchecked exception) Thrown if theDrawing is null. */ public Group addFirst (Drawing theDrawing) { doAddFirst (theDrawing); return this; } /** * Write this group to the given object output stream. * * @param out Object output stream. * * @exception IOException * Thrown if an I/O error occurred. */ public void writeExternal (ObjectOutput out) throws IOException { super.writeExternal (out); int n = myItemList.size(); out.writeInt (n); for (DrawingItem item : myItemList) { out.writeObject (item); } out.writeDouble (x); out.writeDouble (y); out.writeDouble (xFactor); out.writeDouble (yFactor); out.writeBoolean (cornerIsOriginal); out.writeDouble (myXScale); out.writeDouble (myYScale); out.writeDouble (myXShear); out.writeDouble (myYShear); out.writeDouble (myRotationAngle); } /** * Read this group from the given object input stream. * * @param in Object input stream. * * @exception IOException * Thrown if an I/O error occurred. * @exception ClassNotFoundException * Thrown if any class needed to deserialize this group cannot be found. */ public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal (in); myItemList.clear(); int n = in.readInt(); for (int i = 0; i < n; ++ i) { myItemList.add ((DrawingItem) in.readObject()); } x = in.readDouble(); y = in.readDouble(); xFactor = in.readDouble(); yFactor = in.readDouble(); cornerIsOriginal = in.readBoolean(); myXScale = in.readDouble(); myYScale = in.readDouble(); myXShear = in.readDouble(); myYShear = in.readDouble(); myRotationAngle = in.readDouble(); myTransform = null; myOriginalNw = null; myTransformedNw = null; } /** * Draw this drawing item in the given graphics context. This method is * allowed to change the graphics context's paint, stroke, and transform, * and it doesn't have to change them back. * * @param g2d 2-D graphics context. */ public void draw (Graphics2D g2d) { super.draw (g2d); // Apply the scaling, shearing, rotation, and translation transforms. computeTransformedBoundingBox(); AffineTransform oldTransform = g2d.getTransform(); g2d.transform (myTransform); // Draw each item. for (DrawingItem item : myItemList) { item.draw (g2d); } // Restore transform. This is necessary to draw nested groups. g2d.setTransform (oldTransform); } /** * Transform the given point by applying this group's scaling, shearing, * rotation, and translation transforms. * * @param thePoint Point before applying transforms. * * @return Point after applying transforms. */ public Point transform (Point thePoint) { computeTransformedBoundingBox(); Point2D.Double p2d = new Point2D.Double (thePoint.x, thePoint.y); myTransform.transform (p2d, p2d); return new Point (p2d.x, p2d.y); } // Hidden operations. /** * Compute the original bounding box of this group. Store the results in the * myOriginalNw and myOriginalSize fields. */ private void computeOriginalBoundingBox() { if (myOriginalNw == null) { Point p; double minX = Double.MAX_VALUE; double minY = Double.MAX_VALUE; double maxX = Double.MIN_VALUE; double maxY = Double.MIN_VALUE; for (DrawingItem item : myItemList) { p = item.nw(); minX = Math.min (minX, p.x); minY = Math.min (minY, p.y); p = item.se(); maxX = Math.max (maxX, p.x); maxY = Math.max (maxY, p.y); } myOriginalNw = new Point (minX, minY); myOriginalSize = new Size (maxX - minX, maxY - minY); } } /** * Compute the transformed bounding box of this group. Store the results in * the myTransformedNw and myTransformedSize fields. */ private void computeTransformedBoundingBox() { if (myTransformedNw == null) { Point p; Point2D.Double p2d = new Point2D.Double(); AffineTransform t = new AffineTransform(); AffineTransform t2 = new AffineTransform(); // Compute original bounding box, to find its center. computeOriginalBoundingBox(); // Set up transform with just scale and shear. t.shear (/*shx*/ myXShear, /*shy*/ myYShear); t.scale (/*sx*/ myXScale, /*sy*/ myYScale); // Scale and shear the original bounding box's center. p2d.x = myOriginalNw.x() + myOriginalSize.width()/2; p2d.y = myOriginalNw.y() + myOriginalSize.height()/2; t.transform (p2d, p2d); // Prepend rotation to transform. t2.setToRotation (/*theta*/ myRotationAngle, /*x */ p2d.x, /*y */ p2d.y); t.preConcatenate (t2); // Compute bounding box with just scale, shear, and rotation. double minX = Double.MAX_VALUE; double minY = Double.MAX_VALUE; double maxX = Double.MIN_VALUE; double maxY = Double.MIN_VALUE; for (DrawingItem item : myItemList) { p = item.nw(); p2d.x = p.x; p2d.y = p.y; t.transform (p2d, p2d); minX = Math.min (minX, p2d.x); minY = Math.min (minY, p2d.y); maxX = Math.max (maxX, p2d.x); maxY = Math.max (maxY, p2d.y); p = item.ne(); p2d.x = p.x; p2d.y = p.y; t.transform (p2d, p2d); minX = Math.min (minX, p2d.x); minY = Math.min (minY, p2d.y); maxX = Math.max (maxX, p2d.x); maxY = Math.max (maxY, p2d.y); p = item.sw(); p2d.x = p.x; p2d.y = p.y; t.transform (p2d, p2d); minX = Math.min (minX, p2d.x); minY = Math.min (minY, p2d.y); maxX = Math.max (maxX, p2d.x); maxY = Math.max (maxY, p2d.y); p = item.se(); p2d.x = p.x; p2d.y = p.y; t.transform (p2d, p2d); minX = Math.min (minX, p2d.x); minY = Math.min (minY, p2d.y); maxX = Math.max (maxX, p2d.x); maxY = Math.max (maxY, p2d.y); } // Record transformed bounding box's size. myTransformedSize = new Size (maxX - minX, maxY - minY); // Corner was specified with respect to original bounding box. if (cornerIsOriginal) { // Compute original corner point based on (xFactor,yFactor). p2d.x = myOriginalNw.x - xFactor*myOriginalSize.width(); p2d.y = myOriginalNw.y - yFactor*myOriginalSize.height(); // Transform it by scale, shear, and rotation. t.transform (p2d, p2d); // Translate it to the point (x,y). t2.setToTranslation (/*tx*/ x - p2d.x, /*ty*/ y - p2d.y); t.preConcatenate (t2); // Compute transformed NW point. myTransformedNw = new Point (minX + x - p2d.x, minY + y - p2d.y); } // Corner was specified with respect to transformed bounding box. else { // Compute transformed NW point based on (x,y) and // (xFactor,yFactor). myTransformedNw = new Point (x + xFactor*myTransformedSize.width(), y + yFactor*myTransformedSize.height()); // Translate the point (minX,minY) to the point myTransformedNw. t2.setToTranslation (/*tx*/ myTransformedNw.x - minX, /*ty*/ myTransformedNw.y - minY); t.preConcatenate (t2); } // Save final transformation. myTransform = t; } } /** * Set the northwest corner point of this group's bounding * box. * * @param x X coordinate of northwest corner point. * @param y Y coordinate of northwest corner point. * @param orig True if original corner, false if transformed corner. */ void doNw (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = 0.0; this.yFactor = 0.0; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the north middle point of this group's bounding box. * * @param x X coordinate of north middle point. * @param y Y coordinate of north middle point. * @param orig True if original corner, false if transformed corner. */ void doN (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = -0.5; this.yFactor = 0.0; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the northeast corner point of this group's bounding box. * * @param x X coordinate of northeast corner point. * @param y Y coordinate of northeast corner point. * @param orig True if original corner, false if transformed corner. */ void doNe (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = -1.0; this.yFactor = 0.0; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the west middle point of this group's bounding box. * * @param x X coordinate of west middle point. * @param y Y coordinate of west middle point. * @param orig True if original corner, false if transformed corner. */ void doW (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = 0.0; this.yFactor = -0.5; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the center point of this group's bounding box. * * @param x X coordinate of center point. * @param y Y coordinate of center point. * @param orig True if original corner, false if transformed corner. */ void doC (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = -0.5; this.yFactor = -0.5; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the east middle point of this group's bounding box. * * @param x X coordinate of east middle point. * @param y Y coordinate of east middle point. * @param orig True if original corner, false if transformed corner. */ void doE (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = -1.0; this.yFactor = -0.5; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the southwest corner point of this group's bounding box. * * @param x X coordinate of southwest corner point. * @param y Y coordinate of southwest corner point. * @param orig True if original corner, false if transformed corner. */ void doSw (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = 0.0; this.yFactor = -1.0; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the south middle point of this group's bounding box. * * @param x X coordinate of south middle point. * @param y Y coordinate of south middle point. * @param orig True if original corner, false if transformed corner. */ void doS (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = -0.5; this.yFactor = -1.0; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } /** * Set the southeast corner point of this group's bounding box. * * @param x X coordinate of southeast corner point. * @param y Y coordinate of southeast corner point. * @param orig True if original corner, false if transformed corner. */ void doSe (double x, double y, boolean orig) { this.x = x; this.y = y; this.xFactor = -1.0; this.yFactor = -1.0; this.cornerIsOriginal = orig; this.myOriginalNw = null; this.myTransformedNw = null; } }

Ads help maintain this website.