Initial revision
From-SVN: r102074
@@ -0,0 +1,118 @@
|
||||
/* Dimension2D.java -- abstraction of a dimension
|
||||
Copyright (C) 1999, 2000, 2002 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
/**
|
||||
* This stores a dimension in 2-dimensional space - a width (along the x-axis)
|
||||
* and height (along the y-axis). The storage is left to subclasses.
|
||||
*
|
||||
* @author Per Bothner (bothner@cygnus.com)
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public abstract class Dimension2D implements Cloneable
|
||||
{
|
||||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
protected Dimension2D()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of this dimension. A negative result, while legal, is
|
||||
* undefined in meaning.
|
||||
*
|
||||
* @return the width
|
||||
*/
|
||||
public abstract double getWidth();
|
||||
|
||||
/**
|
||||
* Get the height of this dimension. A negative result, while legal, is
|
||||
* undefined in meaning.
|
||||
*
|
||||
* @return the height
|
||||
*/
|
||||
public abstract double getHeight();
|
||||
|
||||
/**
|
||||
* Set the size of this dimension to the requested values. Loss of precision
|
||||
* may occur.
|
||||
*
|
||||
* @param w the new width
|
||||
* @param h the new height
|
||||
*/
|
||||
public abstract void setSize(double w, double h);
|
||||
|
||||
/**
|
||||
* Set the size of this dimension to the requested value. Loss of precision
|
||||
* may occur.
|
||||
*
|
||||
* @param d the dimension containing the new values
|
||||
*
|
||||
* @throws NullPointerException if d is null
|
||||
*/
|
||||
public void setSize(Dimension2D d)
|
||||
{
|
||||
setSize(d.getWidth(), d.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new dimension of the same run-time type with the same contents
|
||||
* as this one.
|
||||
*
|
||||
* @return the clone
|
||||
*
|
||||
* @exception OutOfMemoryError If there is not enough memory available.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public Object clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
return super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e)
|
||||
{
|
||||
throw (Error) new InternalError().initCause(e); // Impossible
|
||||
}
|
||||
}
|
||||
} // class Dimension2D
|
||||
@@ -0,0 +1,413 @@
|
||||
/* Ellipse2D.java -- represents an ellipse in 2-D space
|
||||
Copyright (C) 2000, 2002, 2004 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
|
||||
/**
|
||||
* Ellipse2D is the shape of an ellipse.
|
||||
* <BR>
|
||||
* <img src="doc-files/Ellipse-1.png" width="347" height="221"
|
||||
* alt="A drawing of an ellipse" /><BR>
|
||||
* The ellipse is defined by it's bounding box (shown in red),
|
||||
* and is defined by the implicit curve:<BR>
|
||||
* <blockquote>(<i>x</i>/<i>a</i>)<sup>2</sup> +
|
||||
* (<i>y</i>/<i>b</i>)<sup>2</sup> = 1<BR><BR></blockquote>
|
||||
*
|
||||
* @author Tom Tromey (tromey@cygnus.com)
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public abstract class Ellipse2D extends RectangularShape
|
||||
{
|
||||
/**
|
||||
* Ellipse2D is defined as abstract.
|
||||
* Implementing classes are Ellipse2D.Float and Ellipse2D.Double.
|
||||
*/
|
||||
protected Ellipse2D()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a point is contained within the ellipse. <P>
|
||||
* @param x - x coordinate of the point.
|
||||
* @param y - y coordinate of the point.
|
||||
* @return true if the point is within the ellipse, false otherwise.
|
||||
*/
|
||||
public boolean contains(double x, double y)
|
||||
{
|
||||
double rx = getWidth() / 2;
|
||||
double ry = getHeight() / 2;
|
||||
double tx = (x - (getX() + rx)) / rx;
|
||||
double ty = (y - (getY() + ry)) / ry;
|
||||
return tx * tx + ty * ty < 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a rectangle is completely contained within the
|
||||
* ellipse. <P>
|
||||
* @param x - x coordinate of the upper-left corner of the rectangle
|
||||
* @param y - y coordinate of the upper-left corner of the rectangle
|
||||
* @param w - width of the rectangle
|
||||
* @param h - height of the rectangle
|
||||
* @return true if the rectangle is completely contained, false otherwise.
|
||||
*/
|
||||
public boolean contains(double x, double y, double w, double h)
|
||||
{
|
||||
double x2 = x + w;
|
||||
double y2 = y + h;
|
||||
return (contains(x, y) && contains(x, y2) && contains(x2, y)
|
||||
&& contains(x2, y2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PathIterator object corresponding to the ellipse.<P>
|
||||
*
|
||||
* Note: An ellipse cannot be represented exactly in PathIterator
|
||||
* segments, the outline is thefore approximated with cubic
|
||||
* Bezier segments.
|
||||
*
|
||||
* @param at an optional transform.
|
||||
* @return A path iterator.
|
||||
*/
|
||||
public PathIterator getPathIterator(AffineTransform at)
|
||||
{
|
||||
// An ellipse is just a complete arc.
|
||||
return new Arc2D.ArcIterator(this, at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a rectangle intersects any part of the ellipse.<P>
|
||||
* @param x - x coordinate of the upper-left corner of the rectangle
|
||||
* @param y - y coordinate of the upper-left corner of the rectangle
|
||||
* @param w - width of the rectangle
|
||||
* @param h - height of the rectangle
|
||||
* @return true if the rectangle intersects the ellipse, false otherwise.
|
||||
*/
|
||||
public boolean intersects(double x, double y, double w, double h)
|
||||
{
|
||||
Rectangle2D r = new Rectangle2D.Double(x, y, w, h);
|
||||
if (! r.intersects(getX(), getY(), getWidth(), getHeight()))
|
||||
return false;
|
||||
|
||||
if (contains(x, y) || contains(x, y + h) || contains(x + w, y)
|
||||
|| contains(x + w, y + h))
|
||||
return true;
|
||||
|
||||
Line2D l1 = new Line2D.Double(getX(), getY() + (getHeight() / 2),
|
||||
getX() + getWidth(),
|
||||
getY() + (getHeight() / 2));
|
||||
Line2D l2 = new Line2D.Double(getX() + (getWidth() / 2), getY(),
|
||||
getX() + (getWidth() / 2),
|
||||
getY() + getHeight());
|
||||
|
||||
if (l1.intersects(r) || l2.intersects(r))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link Ellipse2D} that stores its coordinates using <code>double</code>
|
||||
* primitives.
|
||||
*/
|
||||
public static class Double extends Ellipse2D
|
||||
{
|
||||
/**
|
||||
* The height of the ellipse.
|
||||
*/
|
||||
public double height;
|
||||
|
||||
/**
|
||||
* The width of the ellipse.
|
||||
*/
|
||||
public double width;
|
||||
|
||||
/**
|
||||
* The upper-left x coordinate of the bounding-box
|
||||
*/
|
||||
public double x;
|
||||
|
||||
/**
|
||||
* The upper-left y coordinate of the bounding-box
|
||||
*/
|
||||
public double y;
|
||||
|
||||
/**
|
||||
* Creates a new Ellipse2D with an upper-left coordinate of (0,0)
|
||||
* and a zero size.
|
||||
*/
|
||||
public Double()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Ellipse2D within a given rectangle
|
||||
* using double-precision coordinates.<P>
|
||||
* @param x - x coordinate of the upper-left of the bounding rectangle
|
||||
* @param y - y coordinate of the upper-left of the bounding rectangle
|
||||
* @param w - width of the ellipse
|
||||
* @param h - height of the ellipse
|
||||
*/
|
||||
public Double(double x, double y, double w, double h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
height = h;
|
||||
width = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding-box of the ellipse.
|
||||
* @return The bounding box.
|
||||
*/
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
return new Rectangle2D.Double(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the ellipse.
|
||||
* @return The height of the ellipse.
|
||||
*/
|
||||
public double getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the ellipse.
|
||||
* @return The width of the ellipse.
|
||||
*/
|
||||
public double getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns x coordinate of the upper-left corner of
|
||||
* the ellipse's bounding-box.
|
||||
* @return The x coordinate.
|
||||
*/
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns y coordinate of the upper-left corner of
|
||||
* the ellipse's bounding-box.
|
||||
* @return The y coordinate.
|
||||
*/
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the ellipse encloses no area, and
|
||||
* <code>false</code> otherwise.
|
||||
*
|
||||
* @return A boolean.
|
||||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return height <= 0 || width <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the geometry of the ellipse's bounding box.<P>
|
||||
*
|
||||
* @param x - x coordinate of the upper-left of the bounding rectangle
|
||||
* @param y - y coordinate of the upper-left of the bounding rectangle
|
||||
* @param w - width of the ellipse
|
||||
* @param h - height of the ellipse
|
||||
*/
|
||||
public void setFrame(double x, double y, double w, double h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
height = h;
|
||||
width = w;
|
||||
}
|
||||
} // class Double
|
||||
|
||||
/**
|
||||
* An {@link Ellipse2D} that stores its coordinates using <code>float</code>
|
||||
* primitives.
|
||||
*/
|
||||
public static class Float extends Ellipse2D
|
||||
{
|
||||
/**
|
||||
* The height of the ellipse.
|
||||
*/
|
||||
public float height;
|
||||
|
||||
/**
|
||||
* The width of the ellipse.
|
||||
*/
|
||||
public float width;
|
||||
|
||||
/**
|
||||
* The upper-left x coordinate of the bounding-box
|
||||
*/
|
||||
public float x;
|
||||
|
||||
/**
|
||||
* The upper-left y coordinate of the bounding-box
|
||||
*/
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* Creates a new Ellipse2D with an upper-left coordinate of (0,0)
|
||||
* and a zero size.
|
||||
*/
|
||||
public Float()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Ellipse2D within a given rectangle
|
||||
* using floating-point precision.<P>
|
||||
* @param x - x coordinate of the upper-left of the bounding rectangle
|
||||
* @param y - y coordinate of the upper-left of the bounding rectangle
|
||||
* @param w - width of the ellipse
|
||||
* @param h - height of the ellipse
|
||||
*
|
||||
*/
|
||||
public Float(float x, float y, float w, float h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.height = h;
|
||||
this.width = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding-box of the ellipse.
|
||||
* @return The bounding box.
|
||||
*/
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
return new Rectangle2D.Float(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the ellipse.
|
||||
* @return The height of the ellipse.
|
||||
*/
|
||||
public double getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the ellipse.
|
||||
* @return The width of the ellipse.
|
||||
*/
|
||||
public double getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns x coordinate of the upper-left corner of
|
||||
* the ellipse's bounding-box.
|
||||
* @return The x coordinate.
|
||||
*/
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns y coordinate of the upper-left corner of
|
||||
* the ellipse's bounding-box.
|
||||
* @return The y coordinate.
|
||||
*/
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the ellipse encloses no area, and
|
||||
* <code>false</code> otherwise.
|
||||
*
|
||||
* @return A boolean.
|
||||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return height <= 0 || width <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the geometry of the ellipse's bounding box.<P>
|
||||
*
|
||||
* @param x - x coordinate of the upper-left of the bounding rectangle
|
||||
* @param y - y coordinate of the upper-left of the bounding rectangle
|
||||
* @param w - width of the ellipse
|
||||
* @param h - height of the ellipse
|
||||
*/
|
||||
public void setFrame(float x, float y, float w, float h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
height = h;
|
||||
width = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the geometry of the ellipse's bounding box.
|
||||
*
|
||||
* Note: This leads to a loss of precision.<P>
|
||||
*
|
||||
* @param x - x coordinate of the upper-left of the bounding rectangle
|
||||
* @param y - y coordinate of the upper-left of the bounding rectangle
|
||||
* @param w - width of the ellipse
|
||||
* @param h - height of the ellipse
|
||||
*/
|
||||
public void setFrame(double x, double y, double w, double h)
|
||||
{
|
||||
this.x = (float) x;
|
||||
this.y = (float) y;
|
||||
height = (float) h;
|
||||
width = (float) w;
|
||||
}
|
||||
} // class Float
|
||||
} // class Ellipse2D
|
||||
@@ -0,0 +1,579 @@
|
||||
/* FlatteningPathIterator.java -- Approximates curves by straight lines
|
||||
Copyright (C) 2003 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
|
||||
/**
|
||||
* A PathIterator for approximating curved path segments by sequences
|
||||
* of straight lines. Instances of this class will only return
|
||||
* segments of type {@link PathIterator#SEG_MOVETO}, {@link
|
||||
* PathIterator#SEG_LINETO}, and {@link PathIterator#SEG_CLOSE}.
|
||||
*
|
||||
* <p>The accuracy of the approximation is determined by two
|
||||
* parameters:
|
||||
*
|
||||
* <ul><li>The <i>flatness</i> is a threshold value for deciding when
|
||||
* a curved segment is consided flat enough for being approximated by
|
||||
* a single straight line. Flatness is defined as the maximal distance
|
||||
* of a curve control point to the straight line that connects the
|
||||
* curve start and end. A lower flatness threshold means a closer
|
||||
* approximation. See {@link QuadCurve2D#getFlatness()} and {@link
|
||||
* CubicCurve2D#getFlatness()} for drawings which illustrate the
|
||||
* meaning of flatness.</li>
|
||||
*
|
||||
* <li>The <i>recursion limit</i> imposes an upper bound for how often
|
||||
* a curved segment gets subdivided. A limit of <i>n</i> means that
|
||||
* for each individual quadratic and cubic Bézier spline
|
||||
* segment, at most 2<sup><small><i>n</i></small></sup> {@link
|
||||
* PathIterator#SEG_LINETO} segments will be created.</li></ul>
|
||||
*
|
||||
* <p><b>Memory Efficiency:</b> The memory consumption grows linearly
|
||||
* with the recursion limit. Neither the <i>flatness</i> parameter nor
|
||||
* the number of segments in the flattened path will affect the memory
|
||||
* consumption.
|
||||
*
|
||||
* <p><b>Thread Safety:</b> Multiple threads can safely work on
|
||||
* separate instances of this class. However, multiple threads should
|
||||
* not concurrently access the same instance, as no synchronization is
|
||||
* performed.
|
||||
*
|
||||
* @see <a href="doc-files/FlatteningPathIterator-1.html"
|
||||
* >Implementation Note</a>
|
||||
*
|
||||
* @author Sascha Brawer (brawer@dandelis.ch)
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public class FlatteningPathIterator
|
||||
implements PathIterator
|
||||
{
|
||||
/**
|
||||
* The PathIterator whose curved segments are being approximated.
|
||||
*/
|
||||
private final PathIterator srcIter;
|
||||
|
||||
|
||||
/**
|
||||
* The square of the flatness threshold value, which determines when
|
||||
* a curve segment is considered flat enough that no further
|
||||
* subdivision is needed.
|
||||
*
|
||||
* <p>Calculating flatness actually produces the squared flatness
|
||||
* value. To avoid the relatively expensive calculation of a square
|
||||
* root for each curve segment, we perform all flatness comparisons
|
||||
* on squared values.
|
||||
*
|
||||
* @see QuadCurve2D#getFlatnessSq()
|
||||
* @see CubicCurve2D#getFlatnessSq()
|
||||
*/
|
||||
private final double flatnessSq;
|
||||
|
||||
|
||||
/**
|
||||
* The maximal number of subdivions that are performed to
|
||||
* approximate a quadratic or cubic curve segment.
|
||||
*/
|
||||
private final int recursionLimit;
|
||||
|
||||
|
||||
/**
|
||||
* A stack for holding the coordinates of subdivided segments.
|
||||
*
|
||||
* @see <a href="doc-files/FlatteningPathIterator-1.html"
|
||||
* >Implementation Note</a>
|
||||
*/
|
||||
private double[] stack;
|
||||
|
||||
|
||||
/**
|
||||
* The current stack size.
|
||||
*
|
||||
* @see <a href="doc-files/FlatteningPathIterator-1.html"
|
||||
* >Implementation Note</a>
|
||||
*/
|
||||
private int stackSize;
|
||||
|
||||
|
||||
/**
|
||||
* The number of recursions that were performed to arrive at
|
||||
* a segment on the stack.
|
||||
*
|
||||
* @see <a href="doc-files/FlatteningPathIterator-1.html"
|
||||
* >Implementation Note</a>
|
||||
*/
|
||||
private int[] recLevel;
|
||||
|
||||
|
||||
|
||||
private final double[] scratch = new double[6];
|
||||
|
||||
|
||||
/**
|
||||
* The segment type of the last segment that was returned by
|
||||
* the source iterator.
|
||||
*/
|
||||
private int srcSegType;
|
||||
|
||||
|
||||
/**
|
||||
* The current <i>x</i> position of the source iterator.
|
||||
*/
|
||||
private double srcPosX;
|
||||
|
||||
|
||||
/**
|
||||
* The current <i>y</i> position of the source iterator.
|
||||
*/
|
||||
private double srcPosY;
|
||||
|
||||
|
||||
/**
|
||||
* A flag that indicates when this path iterator has finished its
|
||||
* iteration over path segments.
|
||||
*/
|
||||
private boolean done;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new PathIterator for approximating an input
|
||||
* PathIterator with straight lines. The approximation works by
|
||||
* recursive subdivisons, until the specified flatness threshold is
|
||||
* not exceeded.
|
||||
*
|
||||
* <p>There will not be more than 10 nested recursion steps, which
|
||||
* means that a single <code>SEG_QUADTO</code> or
|
||||
* <code>SEG_CUBICTO</code> segment is approximated by at most
|
||||
* 2<sup><small>10</small></sup> = 1024 straight lines.
|
||||
*/
|
||||
public FlatteningPathIterator(PathIterator src, double flatness)
|
||||
{
|
||||
this(src, flatness, 10);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new PathIterator for approximating an input
|
||||
* PathIterator with straight lines. The approximation works by
|
||||
* recursive subdivisons, until the specified flatness threshold is
|
||||
* not exceeded. Additionally, the number of recursions is also
|
||||
* bound by the specified recursion limit.
|
||||
*/
|
||||
public FlatteningPathIterator(PathIterator src, double flatness,
|
||||
int limit)
|
||||
{
|
||||
if (flatness < 0 || limit < 0)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
srcIter = src;
|
||||
flatnessSq = flatness * flatness;
|
||||
recursionLimit = limit;
|
||||
fetchSegment();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the maximally acceptable flatness.
|
||||
*
|
||||
* @see QuadCurve2D#getFlatness()
|
||||
* @see CubicCurve2D#getFlatness()
|
||||
*/
|
||||
public double getFlatness()
|
||||
{
|
||||
return Math.sqrt(flatnessSq);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the maximum number of recursive curve subdivisions.
|
||||
*/
|
||||
public int getRecursionLimit()
|
||||
{
|
||||
return recursionLimit;
|
||||
}
|
||||
|
||||
|
||||
// Documentation will be copied from PathIterator.
|
||||
public int getWindingRule()
|
||||
{
|
||||
return srcIter.getWindingRule();
|
||||
}
|
||||
|
||||
|
||||
// Documentation will be copied from PathIterator.
|
||||
public boolean isDone()
|
||||
{
|
||||
return done;
|
||||
}
|
||||
|
||||
|
||||
// Documentation will be copied from PathIterator.
|
||||
public void next()
|
||||
{
|
||||
if (stackSize > 0)
|
||||
{
|
||||
--stackSize;
|
||||
if (stackSize > 0)
|
||||
{
|
||||
switch (srcSegType)
|
||||
{
|
||||
case PathIterator.SEG_QUADTO:
|
||||
subdivideQuadratic();
|
||||
return;
|
||||
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
subdivideCubic();
|
||||
return;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcIter.next();
|
||||
fetchSegment();
|
||||
}
|
||||
|
||||
|
||||
// Documentation will be copied from PathIterator.
|
||||
public int currentSegment(double[] coords)
|
||||
{
|
||||
if (done)
|
||||
throw new NoSuchElementException();
|
||||
|
||||
switch (srcSegType)
|
||||
{
|
||||
case PathIterator.SEG_CLOSE:
|
||||
return srcSegType;
|
||||
|
||||
case PathIterator.SEG_MOVETO:
|
||||
case PathIterator.SEG_LINETO:
|
||||
coords[0] = srcPosX;
|
||||
coords[1] = srcPosY;
|
||||
return srcSegType;
|
||||
|
||||
case PathIterator.SEG_QUADTO:
|
||||
if (stackSize == 0)
|
||||
{
|
||||
coords[0] = srcPosX;
|
||||
coords[1] = srcPosY;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sp = stack.length - 4 * stackSize;
|
||||
coords[0] = stack[sp + 2];
|
||||
coords[1] = stack[sp + 3];
|
||||
}
|
||||
return PathIterator.SEG_LINETO;
|
||||
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
if (stackSize == 0)
|
||||
{
|
||||
coords[0] = srcPosX;
|
||||
coords[1] = srcPosY;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sp = stack.length - 6 * stackSize;
|
||||
coords[0] = stack[sp + 4];
|
||||
coords[1] = stack[sp + 5];
|
||||
}
|
||||
return PathIterator.SEG_LINETO;
|
||||
}
|
||||
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
|
||||
// Documentation will be copied from PathIterator.
|
||||
public int currentSegment(float[] coords)
|
||||
{
|
||||
if (done)
|
||||
throw new NoSuchElementException();
|
||||
|
||||
switch (srcSegType)
|
||||
{
|
||||
case PathIterator.SEG_CLOSE:
|
||||
return srcSegType;
|
||||
|
||||
case PathIterator.SEG_MOVETO:
|
||||
case PathIterator.SEG_LINETO:
|
||||
coords[0] = (float) srcPosX;
|
||||
coords[1] = (float) srcPosY;
|
||||
return srcSegType;
|
||||
|
||||
case PathIterator.SEG_QUADTO:
|
||||
if (stackSize == 0)
|
||||
{
|
||||
coords[0] = (float) srcPosX;
|
||||
coords[1] = (float) srcPosY;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sp = stack.length - 4 * stackSize;
|
||||
coords[0] = (float) stack[sp + 2];
|
||||
coords[1] = (float) stack[sp + 3];
|
||||
}
|
||||
return PathIterator.SEG_LINETO;
|
||||
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
if (stackSize == 0)
|
||||
{
|
||||
coords[0] = (float) srcPosX;
|
||||
coords[1] = (float) srcPosY;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sp = stack.length - 6 * stackSize;
|
||||
coords[0] = (float) stack[sp + 4];
|
||||
coords[1] = (float) stack[sp + 5];
|
||||
}
|
||||
return PathIterator.SEG_LINETO;
|
||||
}
|
||||
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches the next segment from the source iterator.
|
||||
*/
|
||||
private void fetchSegment()
|
||||
{
|
||||
int sp;
|
||||
|
||||
if (srcIter.isDone())
|
||||
{
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
srcSegType = srcIter.currentSegment(scratch);
|
||||
|
||||
switch (srcSegType)
|
||||
{
|
||||
case PathIterator.SEG_CLOSE:
|
||||
return;
|
||||
|
||||
case PathIterator.SEG_MOVETO:
|
||||
case PathIterator.SEG_LINETO:
|
||||
srcPosX = scratch[0];
|
||||
srcPosY = scratch[1];
|
||||
return;
|
||||
|
||||
case PathIterator.SEG_QUADTO:
|
||||
if (recursionLimit == 0)
|
||||
{
|
||||
srcPosX = scratch[2];
|
||||
srcPosY = scratch[3];
|
||||
stackSize = 0;
|
||||
return;
|
||||
}
|
||||
sp = 4 * recursionLimit;
|
||||
stackSize = 1;
|
||||
if (stack == null)
|
||||
{
|
||||
stack = new double[sp + /* 4 + 2 */ 6];
|
||||
recLevel = new int[recursionLimit + 1];
|
||||
}
|
||||
recLevel[0] = 0;
|
||||
stack[sp] = srcPosX; // P1.x
|
||||
stack[sp + 1] = srcPosY; // P1.y
|
||||
stack[sp + 2] = scratch[0]; // C.x
|
||||
stack[sp + 3] = scratch[1]; // C.y
|
||||
srcPosX = stack[sp + 4] = scratch[2]; // P2.x
|
||||
srcPosY = stack[sp + 5] = scratch[3]; // P2.y
|
||||
subdivideQuadratic();
|
||||
break;
|
||||
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
if (recursionLimit == 0)
|
||||
{
|
||||
srcPosX = scratch[4];
|
||||
srcPosY = scratch[5];
|
||||
stackSize = 0;
|
||||
return;
|
||||
}
|
||||
sp = 6 * recursionLimit;
|
||||
stackSize = 1;
|
||||
if ((stack == null) || (stack.length < sp + 8))
|
||||
{
|
||||
stack = new double[sp + /* 6 + 2 */ 8];
|
||||
recLevel = new int[recursionLimit + 1];
|
||||
}
|
||||
recLevel[0] = 0;
|
||||
stack[sp] = srcPosX; // P1.x
|
||||
stack[sp + 1] = srcPosY; // P1.y
|
||||
stack[sp + 2] = scratch[0]; // C1.x
|
||||
stack[sp + 3] = scratch[1]; // C1.y
|
||||
stack[sp + 4] = scratch[2]; // C2.x
|
||||
stack[sp + 5] = scratch[3]; // C2.y
|
||||
srcPosX = stack[sp + 6] = scratch[4]; // P2.x
|
||||
srcPosY = stack[sp + 7] = scratch[5]; // P2.y
|
||||
subdivideCubic();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Repeatedly subdivides the quadratic curve segment that is on top
|
||||
* of the stack. The iteration terminates when the recursion limit
|
||||
* has been reached, or when the resulting segment is flat enough.
|
||||
*/
|
||||
private void subdivideQuadratic()
|
||||
{
|
||||
int sp;
|
||||
int level;
|
||||
|
||||
sp = stack.length - 4 * stackSize - 2;
|
||||
level = recLevel[stackSize - 1];
|
||||
while ((level < recursionLimit)
|
||||
&& (QuadCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
|
||||
{
|
||||
recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
|
||||
QuadCurve2D.subdivide(stack, sp, stack, sp - 4, stack, sp);
|
||||
++stackSize;
|
||||
sp -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Repeatedly subdivides the cubic curve segment that is on top
|
||||
* of the stack. The iteration terminates when the recursion limit
|
||||
* has been reached, or when the resulting segment is flat enough.
|
||||
*/
|
||||
private void subdivideCubic()
|
||||
{
|
||||
int sp;
|
||||
int level;
|
||||
|
||||
sp = stack.length - 6 * stackSize - 2;
|
||||
level = recLevel[stackSize - 1];
|
||||
while ((level < recursionLimit)
|
||||
&& (CubicCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
|
||||
{
|
||||
recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
|
||||
|
||||
CubicCurve2D.subdivide(stack, sp, stack, sp - 6, stack, sp);
|
||||
++stackSize;
|
||||
sp -= 6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* These routines were useful for debugging. Since they would
|
||||
* just bloat the implementation, they are commented out.
|
||||
*
|
||||
*
|
||||
|
||||
private static String segToString(int segType, double[] d, int offset)
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (segType)
|
||||
{
|
||||
case PathIterator.SEG_CLOSE:
|
||||
return "SEG_CLOSE";
|
||||
|
||||
case PathIterator.SEG_MOVETO:
|
||||
return "SEG_MOVETO (" + d[offset] + ", " + d[offset + 1] + ")";
|
||||
|
||||
case PathIterator.SEG_LINETO:
|
||||
return "SEG_LINETO (" + d[offset] + ", " + d[offset + 1] + ")";
|
||||
|
||||
case PathIterator.SEG_QUADTO:
|
||||
return "SEG_QUADTO (" + d[offset] + ", " + d[offset + 1]
|
||||
+ ") (" + d[offset + 2] + ", " + d[offset + 3] + ")";
|
||||
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
return "SEG_CUBICTO (" + d[offset] + ", " + d[offset + 1]
|
||||
+ ") (" + d[offset + 2] + ", " + d[offset + 3]
|
||||
+ ") (" + d[offset + 4] + ", " + d[offset + 5] + ")";
|
||||
}
|
||||
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
|
||||
private void dumpQuadraticStack(String msg)
|
||||
{
|
||||
int sp = stack.length - 4 * stackSize - 2;
|
||||
int i = 0;
|
||||
System.err.print(" " + msg + ":");
|
||||
while (sp < stack.length)
|
||||
{
|
||||
System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
|
||||
if (i < recLevel.length)
|
||||
System.out.print("/" + recLevel[i++]);
|
||||
if (sp + 3 < stack.length)
|
||||
System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
|
||||
sp += 4;
|
||||
}
|
||||
System.err.println();
|
||||
}
|
||||
|
||||
|
||||
private void dumpCubicStack(String msg)
|
||||
{
|
||||
int sp = stack.length - 6 * stackSize - 2;
|
||||
int i = 0;
|
||||
System.err.print(" " + msg + ":");
|
||||
while (sp < stack.length)
|
||||
{
|
||||
System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
|
||||
if (i < recLevel.length)
|
||||
System.out.print("/" + recLevel[i++]);
|
||||
if (sp + 3 < stack.length)
|
||||
{
|
||||
System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
|
||||
System.err.print(" [" + stack[sp+4] + ", " + stack[sp+5] + "]");
|
||||
}
|
||||
sp += 6;
|
||||
}
|
||||
System.err.println();
|
||||
}
|
||||
|
||||
*
|
||||
*
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,958 @@
|
||||
/* GeneralPath.java -- represents a shape built from subpaths
|
||||
Copyright (C) 2002, 2003, 2004 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
|
||||
|
||||
/**
|
||||
* A general geometric path, consisting of any number of subpaths
|
||||
* constructed out of straight lines and cubic or quadratic Bezier
|
||||
* curves.
|
||||
*
|
||||
* <p>The inside of the curve is defined for drawing purposes by a winding
|
||||
* rule. Either the WIND_EVEN_ODD or WIND_NON_ZERO winding rule can be chosen.
|
||||
*
|
||||
* <p><img src="doc-files/GeneralPath-1.png" width="300" height="210"
|
||||
* alt="A drawing of a GeneralPath" />
|
||||
* <p>The EVEN_ODD winding rule defines a point as inside a path if:
|
||||
* A ray from the point towards infinity in an arbitrary direction
|
||||
* intersects the path an odd number of times. Points <b>A</b> and
|
||||
* <b>C</b> in the image are considered to be outside the path.
|
||||
* (both intersect twice)
|
||||
* Point <b>B</b> intersects once, and is inside.
|
||||
*
|
||||
* <p>The NON_ZERO winding rule defines a point as inside a path if:
|
||||
* The path intersects the ray in an equal number of opposite directions.
|
||||
* Point <b>A</b> in the image is outside (one intersection in the
|
||||
* ’up’
|
||||
* direction, one in the ’down’ direction) Point <b>B</b> in
|
||||
* the image is inside (one intersection ’down’)
|
||||
* Point <b>C</b> in the image is outside (two intersections
|
||||
* ’down’)
|
||||
*
|
||||
* @see Line2D
|
||||
* @see CubicCurve2D
|
||||
* @see QuadCurve2D
|
||||
*
|
||||
* @author Sascha Brawer (brawer@dandelis.ch)
|
||||
* @author Sven de Marothy (sven@physto.se)
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public final class GeneralPath implements Shape, Cloneable
|
||||
{
|
||||
public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
|
||||
public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
|
||||
|
||||
/** Initial size if not specified. */
|
||||
private static final int INIT_SIZE = 10;
|
||||
|
||||
/** A big number, but not so big it can't survive a few float operations */
|
||||
private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0;
|
||||
|
||||
/** The winding rule.
|
||||
* This is package-private to avoid an accessor method.
|
||||
*/
|
||||
int rule;
|
||||
|
||||
/**
|
||||
* The path type in points. Note that xpoints[index] and ypoints[index] maps
|
||||
* to types[index]; the control points of quad and cubic paths map as
|
||||
* well but are ignored.
|
||||
* This is package-private to avoid an accessor method.
|
||||
*/
|
||||
byte[] types;
|
||||
|
||||
/**
|
||||
* The list of all points seen. Since you can only append floats, it makes
|
||||
* sense for these to be float[]. I have no idea why Sun didn't choose to
|
||||
* allow a general path of double precision points.
|
||||
* Note: Storing x and y coords seperately makes for a slower transforms,
|
||||
* But it speeds up and simplifies box-intersection checking a lot.
|
||||
* These are package-private to avoid accessor methods.
|
||||
*/
|
||||
float[] xpoints;
|
||||
float[] ypoints;
|
||||
|
||||
/** The index of the most recent moveto point, or null. */
|
||||
private int subpath = -1;
|
||||
|
||||
/** The next available index into points.
|
||||
* This is package-private to avoid an accessor method.
|
||||
*/
|
||||
int index;
|
||||
|
||||
/**
|
||||
* Constructs a GeneralPath with the default (NON_ZERO)
|
||||
* winding rule and initial capacity (20).
|
||||
*/
|
||||
public GeneralPath()
|
||||
{
|
||||
this(WIND_NON_ZERO, INIT_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GeneralPath with a specific winding rule
|
||||
* and the default initial capacity (20).
|
||||
* @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD)
|
||||
*/
|
||||
public GeneralPath(int rule)
|
||||
{
|
||||
this(rule, INIT_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GeneralPath with a specific winding rule
|
||||
* and the initial capacity. The initial capacity should be
|
||||
* the approximate number of path segments to be used.
|
||||
* @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD)
|
||||
* @param capacity the inital capacity, in path segments
|
||||
*/
|
||||
public GeneralPath(int rule, int capacity)
|
||||
{
|
||||
if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
|
||||
throw new IllegalArgumentException();
|
||||
this.rule = rule;
|
||||
if (capacity < INIT_SIZE)
|
||||
capacity = INIT_SIZE;
|
||||
types = new byte[capacity];
|
||||
xpoints = new float[capacity];
|
||||
ypoints = new float[capacity];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GeneralPath from an arbitrary shape object.
|
||||
* The Shapes PathIterator path and winding rule will be used.
|
||||
* @param s the shape
|
||||
*/
|
||||
public GeneralPath(Shape s)
|
||||
{
|
||||
types = new byte[INIT_SIZE];
|
||||
xpoints = new float[INIT_SIZE];
|
||||
ypoints = new float[INIT_SIZE];
|
||||
PathIterator pi = s.getPathIterator(null);
|
||||
setWindingRule(pi.getWindingRule());
|
||||
append(pi, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new point to a path.
|
||||
*/
|
||||
public void moveTo(float x, float y)
|
||||
{
|
||||
subpath = index;
|
||||
ensureSize(index + 1);
|
||||
types[index] = PathIterator.SEG_MOVETO;
|
||||
xpoints[index] = x;
|
||||
ypoints[index++] = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a straight line to the current path.
|
||||
* @param x x coordinate of the line endpoint.
|
||||
* @param y y coordinate of the line endpoint.
|
||||
*/
|
||||
public void lineTo(float x, float y)
|
||||
{
|
||||
ensureSize(index + 1);
|
||||
types[index] = PathIterator.SEG_LINETO;
|
||||
xpoints[index] = x;
|
||||
ypoints[index++] = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a quadratic Bezier curve to the current path.
|
||||
* @param x1 x coordinate of the control point
|
||||
* @param y1 y coordinate of the control point
|
||||
* @param x2 x coordinate of the curve endpoint.
|
||||
* @param y2 y coordinate of the curve endpoint.
|
||||
*/
|
||||
public void quadTo(float x1, float y1, float x2, float y2)
|
||||
{
|
||||
ensureSize(index + 2);
|
||||
types[index] = PathIterator.SEG_QUADTO;
|
||||
xpoints[index] = x1;
|
||||
ypoints[index++] = y1;
|
||||
xpoints[index] = x2;
|
||||
ypoints[index++] = y2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a cubic Bezier curve to the current path.
|
||||
* @param x1 x coordinate of the first control point
|
||||
* @param y1 y coordinate of the first control point
|
||||
* @param x2 x coordinate of the second control point
|
||||
* @param y2 y coordinate of the second control point
|
||||
* @param x3 x coordinate of the curve endpoint.
|
||||
* @param y3 y coordinate of the curve endpoint.
|
||||
*/
|
||||
public void curveTo(float x1, float y1, float x2, float y2, float x3,
|
||||
float y3)
|
||||
{
|
||||
ensureSize(index + 3);
|
||||
types[index] = PathIterator.SEG_CUBICTO;
|
||||
xpoints[index] = x1;
|
||||
ypoints[index++] = y1;
|
||||
xpoints[index] = x2;
|
||||
ypoints[index++] = y2;
|
||||
xpoints[index] = x3;
|
||||
ypoints[index++] = y3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current subpath by drawing a line
|
||||
* back to the point of the last moveTo.
|
||||
*/
|
||||
public void closePath()
|
||||
{
|
||||
ensureSize(index + 1);
|
||||
types[index] = PathIterator.SEG_CLOSE;
|
||||
xpoints[index] = xpoints[subpath];
|
||||
ypoints[index++] = ypoints[subpath];
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the segments of a Shape to the path. If <code>connect</code> is
|
||||
* true, the new path segments are connected to the existing one with a line.
|
||||
* The winding rule of the Shape is ignored.
|
||||
*/
|
||||
public void append(Shape s, boolean connect)
|
||||
{
|
||||
append(s.getPathIterator(null), connect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the segments of a PathIterator to this GeneralPath.
|
||||
* Optionally, the initial {@link PathIterator#SEG_MOVETO} segment
|
||||
* of the appended path is changed into a {@link
|
||||
* PathIterator#SEG_LINETO} segment.
|
||||
*
|
||||
* @param iter the PathIterator specifying which segments shall be
|
||||
* appended.
|
||||
*
|
||||
* @param connect <code>true</code> for substituting the initial
|
||||
* {@link PathIterator#SEG_MOVETO} segment by a {@link
|
||||
* PathIterator#SEG_LINETO}, or <code>false</code> for not
|
||||
* performing any substitution. If this GeneralPath is currently
|
||||
* empty, <code>connect</code> is assumed to be <code>false</code>,
|
||||
* thus leaving the initial {@link PathIterator#SEG_MOVETO}
|
||||
* unchanged.
|
||||
*/
|
||||
public void append(PathIterator iter, boolean connect)
|
||||
{
|
||||
// A bad implementation of this method had caused Classpath bug #6076.
|
||||
float[] f = new float[6];
|
||||
while (! iter.isDone())
|
||||
{
|
||||
switch (iter.currentSegment(f))
|
||||
{
|
||||
case PathIterator.SEG_MOVETO:
|
||||
if (! connect || (index == 0))
|
||||
{
|
||||
moveTo(f[0], f[1]);
|
||||
break;
|
||||
}
|
||||
if ((index >= 1) && (types[index - 1] == PathIterator.SEG_CLOSE)
|
||||
&& (f[0] == xpoints[index - 1])
|
||||
&& (f[1] == ypoints[index - 1]))
|
||||
break;
|
||||
|
||||
// Fall through.
|
||||
case PathIterator.SEG_LINETO:
|
||||
lineTo(f[0], f[1]);
|
||||
break;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
quadTo(f[0], f[1], f[2], f[3]);
|
||||
break;
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
curveTo(f[0], f[1], f[2], f[3], f[4], f[5]);
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE:
|
||||
closePath();
|
||||
break;
|
||||
}
|
||||
|
||||
connect = false;
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path’s current winding rule.
|
||||
*/
|
||||
public int getWindingRule()
|
||||
{
|
||||
return rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path’s winding rule, which controls which areas are
|
||||
* considered ’inside’ or ’outside’ the path
|
||||
* on drawing. Valid rules are WIND_EVEN_ODD for an even-odd winding rule,
|
||||
* or WIND_NON_ZERO for a non-zero winding rule.
|
||||
*/
|
||||
public void setWindingRule(int rule)
|
||||
{
|
||||
if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
|
||||
throw new IllegalArgumentException();
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current appending point of the path.
|
||||
*/
|
||||
public Point2D getCurrentPoint()
|
||||
{
|
||||
if (subpath < 0)
|
||||
return null;
|
||||
return new Point2D.Float(xpoints[index - 1], ypoints[index - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the path. All points and segments are destroyed.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
subpath = -1;
|
||||
index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a transform to the path.
|
||||
*/
|
||||
public void transform(AffineTransform xform)
|
||||
{
|
||||
double nx;
|
||||
double ny;
|
||||
double[] m = new double[6];
|
||||
xform.getMatrix(m);
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
nx = m[0] * xpoints[i] + m[2] * ypoints[i] + m[4];
|
||||
ny = m[1] * xpoints[i] + m[3] * ypoints[i] + m[5];
|
||||
xpoints[i] = (float) nx;
|
||||
ypoints[i] = (float) ny;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transformed version of the path.
|
||||
* @param xform the transform to apply
|
||||
* @return a new transformed GeneralPath
|
||||
*/
|
||||
public Shape createTransformedShape(AffineTransform xform)
|
||||
{
|
||||
GeneralPath p = new GeneralPath(this);
|
||||
p.transform(xform);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path’s bounding box.
|
||||
*/
|
||||
public Rectangle getBounds()
|
||||
{
|
||||
return getBounds2D().getBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path’s bounding box, in <code>float</code> precision
|
||||
*/
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
x1 = x2 = xpoints[0];
|
||||
y1 = y2 = ypoints[0];
|
||||
}
|
||||
else
|
||||
x1 = x2 = y1 = y2 = 0.0f;
|
||||
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
x1 = Math.min(xpoints[i], x1);
|
||||
y1 = Math.min(ypoints[i], y1);
|
||||
x2 = Math.max(xpoints[i], x2);
|
||||
y2 = Math.max(ypoints[i], y2);
|
||||
}
|
||||
return (new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates if a point is within the GeneralPath,
|
||||
* The NON_ZERO winding rule is used, regardless of the
|
||||
* set winding rule.
|
||||
* @param x x coordinate of the point to evaluate
|
||||
* @param y y coordinate of the point to evaluate
|
||||
* @return true if the point is within the path, false otherwise
|
||||
*/
|
||||
public boolean contains(double x, double y)
|
||||
{
|
||||
return (getWindingNumber(x, y) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates if a Point2D is within the GeneralPath,
|
||||
* The NON_ZERO winding rule is used, regardless of the
|
||||
* set winding rule.
|
||||
* @param p The Point2D to evaluate
|
||||
* @return true if the point is within the path, false otherwise
|
||||
*/
|
||||
public boolean contains(Point2D p)
|
||||
{
|
||||
return contains(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates if a rectangle is completely contained within the path.
|
||||
* This method will return false in the cases when the box
|
||||
* intersects an inner segment of the path.
|
||||
* (i.e.: The method is accurate for the EVEN_ODD winding rule)
|
||||
*/
|
||||
public boolean contains(double x, double y, double w, double h)
|
||||
{
|
||||
if (! getBounds2D().intersects(x, y, w, h))
|
||||
return false;
|
||||
|
||||
/* Does any edge intersect? */
|
||||
if (getAxisIntersections(x, y, false, w) != 0 /* top */
|
||||
|| getAxisIntersections(x, y + h, false, w) != 0 /* bottom */
|
||||
|| getAxisIntersections(x + w, y, true, h) != 0 /* right */
|
||||
|| getAxisIntersections(x, y, true, h) != 0) /* left */
|
||||
return false;
|
||||
|
||||
/* No intersections, is any point inside? */
|
||||
if (getWindingNumber(x, y) != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates if a rectangle is completely contained within the path.
|
||||
* This method will return false in the cases when the box
|
||||
* intersects an inner segment of the path.
|
||||
* (i.e.: The method is accurate for the EVEN_ODD winding rule)
|
||||
* @param r the rectangle
|
||||
* @return <code>true</code> if the rectangle is completely contained
|
||||
* within the path, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean contains(Rectangle2D r)
|
||||
{
|
||||
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates if a rectangle intersects the path.
|
||||
* @param x x coordinate of the rectangle
|
||||
* @param y y coordinate of the rectangle
|
||||
* @param w width of the rectangle
|
||||
* @param h height of the rectangle
|
||||
* @return <code>true</code> if the rectangle intersects the path,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public boolean intersects(double x, double y, double w, double h)
|
||||
{
|
||||
/* Does any edge intersect? */
|
||||
if (getAxisIntersections(x, y, false, w) != 0 /* top */
|
||||
|| getAxisIntersections(x, y + h, false, w) != 0 /* bottom */
|
||||
|| getAxisIntersections(x + w, y, true, h) != 0 /* right */
|
||||
|| getAxisIntersections(x, y, true, h) != 0) /* left */
|
||||
return true;
|
||||
|
||||
/* No intersections, is any point inside? */
|
||||
if (getWindingNumber(x, y) != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates if a Rectangle2D intersects the path.
|
||||
* @param r The rectangle
|
||||
* @return <code>true</code> if the rectangle intersects the path,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public boolean intersects(Rectangle2D r)
|
||||
{
|
||||
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* A PathIterator that iterates over the segments of a GeneralPath.
|
||||
*
|
||||
* @author Sascha Brawer (brawer@dandelis.ch)
|
||||
*/
|
||||
private static class GeneralPathIterator implements PathIterator
|
||||
{
|
||||
/**
|
||||
* The number of coordinate values for each segment type.
|
||||
*/
|
||||
private static final int[] NUM_COORDS = {
|
||||
/* 0: SEG_MOVETO */ 1,
|
||||
/* 1: SEG_LINETO */ 1,
|
||||
/* 2: SEG_QUADTO */ 2,
|
||||
/* 3: SEG_CUBICTO */ 3,
|
||||
/* 4: SEG_CLOSE */ 0};
|
||||
|
||||
/**
|
||||
* The GeneralPath whose segments are being iterated.
|
||||
* This is package-private to avoid an accessor method.
|
||||
*/
|
||||
final GeneralPath path;
|
||||
|
||||
/**
|
||||
* The affine transformation used to transform coordinates.
|
||||
*/
|
||||
private final AffineTransform transform;
|
||||
|
||||
/**
|
||||
* The current position of the iterator.
|
||||
*/
|
||||
private int pos;
|
||||
|
||||
/**
|
||||
* Constructs a new iterator for enumerating the segments of a
|
||||
* GeneralPath.
|
||||
*
|
||||
* @param at an affine transformation for projecting the returned
|
||||
* points, or <code>null</code> to return the original points
|
||||
* without any mapping.
|
||||
*/
|
||||
GeneralPathIterator(GeneralPath path, AffineTransform transform)
|
||||
{
|
||||
this.path = path;
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current winding rule of the GeneralPath.
|
||||
*/
|
||||
public int getWindingRule()
|
||||
{
|
||||
return path.rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the iterator has reached the last segment in
|
||||
* the path.
|
||||
*/
|
||||
public boolean isDone()
|
||||
{
|
||||
return pos >= path.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the iterator position by one segment.
|
||||
*/
|
||||
public void next()
|
||||
{
|
||||
int seg;
|
||||
|
||||
/*
|
||||
* Increment pos by the number of coordinate pairs.
|
||||
*/
|
||||
seg = path.types[pos];
|
||||
if (seg == SEG_CLOSE)
|
||||
pos++;
|
||||
else
|
||||
pos += NUM_COORDS[seg];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current segment in float coordinates.
|
||||
*/
|
||||
public int currentSegment(float[] coords)
|
||||
{
|
||||
int seg;
|
||||
int numCoords;
|
||||
|
||||
seg = path.types[pos];
|
||||
numCoords = NUM_COORDS[seg];
|
||||
if (numCoords > 0)
|
||||
{
|
||||
for (int i = 0; i < numCoords; i++)
|
||||
{
|
||||
coords[i << 1] = path.xpoints[pos + i];
|
||||
coords[(i << 1) + 1] = path.ypoints[pos + i];
|
||||
}
|
||||
|
||||
if (transform != null)
|
||||
transform.transform( /* src */
|
||||
coords, /* srcOffset */
|
||||
0, /* dest */ coords, /* destOffset */
|
||||
0, /* numPoints */ numCoords);
|
||||
}
|
||||
return seg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current segment in double coordinates.
|
||||
*/
|
||||
public int currentSegment(double[] coords)
|
||||
{
|
||||
int seg;
|
||||
int numCoords;
|
||||
|
||||
seg = path.types[pos];
|
||||
numCoords = NUM_COORDS[seg];
|
||||
if (numCoords > 0)
|
||||
{
|
||||
for (int i = 0; i < numCoords; i++)
|
||||
{
|
||||
coords[i << 1] = (double) path.xpoints[pos + i];
|
||||
coords[(i << 1) + 1] = (double) path.ypoints[pos + i];
|
||||
}
|
||||
if (transform != null)
|
||||
transform.transform( /* src */
|
||||
coords, /* srcOffset */
|
||||
0, /* dest */ coords, /* destOffset */
|
||||
0, /* numPoints */ numCoords);
|
||||
}
|
||||
return seg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PathIterator for iterating along the segments of the path.
|
||||
*
|
||||
* @param at an affine transformation for projecting the returned
|
||||
* points, or <code>null</code> to let the created iterator return
|
||||
* the original points without any mapping.
|
||||
*/
|
||||
public PathIterator getPathIterator(AffineTransform at)
|
||||
{
|
||||
return new GeneralPathIterator(this, at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FlatteningPathIterator for the path
|
||||
*/
|
||||
public PathIterator getPathIterator(AffineTransform at, double flatness)
|
||||
{
|
||||
return new FlatteningPathIterator(getPathIterator(at), flatness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new shape of the same run-time type with the same contents
|
||||
* as this one.
|
||||
*
|
||||
* @return the clone
|
||||
*
|
||||
* @exception OutOfMemoryError If there is not enough memory available.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public Object clone()
|
||||
{
|
||||
// This class is final; no need to use super.clone().
|
||||
return new GeneralPath(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method - ensure the size of the data arrays,
|
||||
* otherwise, reallocate new ones twice the size
|
||||
*/
|
||||
private void ensureSize(int size)
|
||||
{
|
||||
if (subpath < 0)
|
||||
throw new IllegalPathStateException("need initial moveto");
|
||||
if (size <= xpoints.length)
|
||||
return;
|
||||
byte[] b = new byte[types.length << 1];
|
||||
System.arraycopy(types, 0, b, 0, index);
|
||||
types = b;
|
||||
float[] f = new float[xpoints.length << 1];
|
||||
System.arraycopy(xpoints, 0, f, 0, index);
|
||||
xpoints = f;
|
||||
f = new float[ypoints.length << 1];
|
||||
System.arraycopy(ypoints, 0, f, 0, index);
|
||||
ypoints = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method - Get the total number of intersections from (x,y) along
|
||||
* a given axis, within a given distance.
|
||||
*/
|
||||
private int getAxisIntersections(double x, double y, boolean useYaxis,
|
||||
double distance)
|
||||
{
|
||||
return (evaluateCrossings(x, y, false, useYaxis, distance));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method - returns the winding number of a point.
|
||||
*/
|
||||
private int getWindingNumber(double x, double y)
|
||||
{
|
||||
/* Evaluate the crossings from x,y to infinity on the y axis (arbitrary
|
||||
choice). Note that we don't actually use Double.INFINITY, since that's
|
||||
slower, and may cause problems. */
|
||||
return (evaluateCrossings(x, y, true, true, BIG_VALUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method - evaluates the number of intersections on an axis from
|
||||
* the point (x,y) to the point (x,y+distance) or (x+distance,y).
|
||||
* @param x x coordinate.
|
||||
* @param y y coordinate.
|
||||
* @param neg True if opposite-directed intersections should cancel,
|
||||
* false to sum all intersections.
|
||||
* @param useYaxis Use the Y axis, false uses the X axis.
|
||||
* @param distance Interval from (x,y) on the selected axis to find
|
||||
* intersections.
|
||||
*/
|
||||
private int evaluateCrossings(double x, double y, boolean neg,
|
||||
boolean useYaxis, double distance)
|
||||
{
|
||||
float cx = 0.0f;
|
||||
float cy = 0.0f;
|
||||
float firstx = 0.0f;
|
||||
float firsty = 0.0f;
|
||||
|
||||
int negative = (neg) ? -1 : 1;
|
||||
double x0;
|
||||
double x1;
|
||||
double x2;
|
||||
double x3;
|
||||
double y0;
|
||||
double y1;
|
||||
double y2;
|
||||
double y3;
|
||||
double[] r = new double[4];
|
||||
int nRoots;
|
||||
double epsilon = 0.0;
|
||||
int pos = 0;
|
||||
int windingNumber = 0;
|
||||
boolean pathStarted = false;
|
||||
|
||||
if (index == 0)
|
||||
return (0);
|
||||
if (useYaxis)
|
||||
{
|
||||
float[] swap1;
|
||||
swap1 = ypoints;
|
||||
ypoints = xpoints;
|
||||
xpoints = swap1;
|
||||
double swap2;
|
||||
swap2 = y;
|
||||
y = x;
|
||||
x = swap2;
|
||||
}
|
||||
|
||||
/* Get a value which is hopefully small but not insignificant relative
|
||||
the path. */
|
||||
epsilon = ypoints[0] * 1E-7;
|
||||
|
||||
if(epsilon == 0)
|
||||
epsilon = 1E-7;
|
||||
|
||||
pos = 0;
|
||||
while (pos < index)
|
||||
{
|
||||
switch (types[pos])
|
||||
{
|
||||
case PathIterator.SEG_MOVETO:
|
||||
if (pathStarted) // close old path
|
||||
{
|
||||
x0 = cx;
|
||||
y0 = cy;
|
||||
x1 = firstx;
|
||||
y1 = firsty;
|
||||
|
||||
if (y0 == 0.0)
|
||||
y0 -= epsilon;
|
||||
if (y1 == 0.0)
|
||||
y1 -= epsilon;
|
||||
if (Line2D.linesIntersect(x0, y0, x1, y1,
|
||||
epsilon, 0.0, distance, 0.0))
|
||||
windingNumber += (y1 < y0) ? 1 : negative;
|
||||
|
||||
cx = firstx;
|
||||
cy = firsty;
|
||||
}
|
||||
cx = firstx = xpoints[pos] - (float) x;
|
||||
cy = firsty = ypoints[pos++] - (float) y;
|
||||
pathStarted = true;
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE:
|
||||
x0 = cx;
|
||||
y0 = cy;
|
||||
x1 = firstx;
|
||||
y1 = firsty;
|
||||
|
||||
if (y0 == 0.0)
|
||||
y0 -= epsilon;
|
||||
if (y1 == 0.0)
|
||||
y1 -= epsilon;
|
||||
if (Line2D.linesIntersect(x0, y0, x1, y1,
|
||||
epsilon, 0.0, distance, 0.0))
|
||||
windingNumber += (y1 < y0) ? 1 : negative;
|
||||
|
||||
cx = firstx;
|
||||
cy = firsty;
|
||||
pos++;
|
||||
pathStarted = false;
|
||||
break;
|
||||
case PathIterator.SEG_LINETO:
|
||||
x0 = cx;
|
||||
y0 = cy;
|
||||
x1 = xpoints[pos] - (float) x;
|
||||
y1 = ypoints[pos++] - (float) y;
|
||||
|
||||
if (y0 == 0.0)
|
||||
y0 -= epsilon;
|
||||
if (y1 == 0.0)
|
||||
y1 -= epsilon;
|
||||
if (Line2D.linesIntersect(x0, y0, x1, y1,
|
||||
epsilon, 0.0, distance, 0.0))
|
||||
windingNumber += (y1 < y0) ? 1 : negative;
|
||||
|
||||
cx = xpoints[pos - 1] - (float) x;
|
||||
cy = ypoints[pos - 1] - (float) y;
|
||||
break;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
x0 = cx;
|
||||
y0 = cy;
|
||||
x1 = xpoints[pos] - x;
|
||||
y1 = ypoints[pos++] - y;
|
||||
x2 = xpoints[pos] - x;
|
||||
y2 = ypoints[pos++] - y;
|
||||
|
||||
/* check if curve may intersect X+ axis. */
|
||||
if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0)
|
||||
&& (y0 * y1 <= 0 || y1 * y2 <= 0))
|
||||
{
|
||||
if (y0 == 0.0)
|
||||
y0 -= epsilon;
|
||||
if (y2 == 0.0)
|
||||
y2 -= epsilon;
|
||||
|
||||
r[0] = y0;
|
||||
r[1] = 2 * (y1 - y0);
|
||||
r[2] = (y2 - 2 * y1 + y0);
|
||||
|
||||
/* degenerate roots (=tangent points) do not
|
||||
contribute to the winding number. */
|
||||
if ((nRoots = QuadCurve2D.solveQuadratic(r)) == 2)
|
||||
for (int i = 0; i < nRoots; i++)
|
||||
{
|
||||
float t = (float) r[i];
|
||||
if (t > 0.0f && t < 1.0f)
|
||||
{
|
||||
double crossing = t * t * (x2 - 2 * x1 + x0)
|
||||
+ 2 * t * (x1 - x0) + x0;
|
||||
if (crossing >= 0.0 && crossing <= distance)
|
||||
windingNumber += (2 * t * (y2 - 2 * y1 + y0)
|
||||
+ 2 * (y1 - y0) < 0) ? 1 : negative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx = xpoints[pos - 1] - (float) x;
|
||||
cy = ypoints[pos - 1] - (float) y;
|
||||
break;
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
x0 = cx;
|
||||
y0 = cy;
|
||||
x1 = xpoints[pos] - x;
|
||||
y1 = ypoints[pos++] - y;
|
||||
x2 = xpoints[pos] - x;
|
||||
y2 = ypoints[pos++] - y;
|
||||
x3 = xpoints[pos] - x;
|
||||
y3 = ypoints[pos++] - y;
|
||||
|
||||
/* check if curve may intersect X+ axis. */
|
||||
if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0)
|
||||
&& (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0))
|
||||
{
|
||||
if (y0 == 0.0)
|
||||
y0 -= epsilon;
|
||||
if (y3 == 0.0)
|
||||
y3 -= epsilon;
|
||||
|
||||
r[0] = y0;
|
||||
r[1] = 3 * (y1 - y0);
|
||||
r[2] = 3 * (y2 + y0 - 2 * y1);
|
||||
r[3] = y3 - 3 * y2 + 3 * y1 - y0;
|
||||
|
||||
if ((nRoots = CubicCurve2D.solveCubic(r)) != 0)
|
||||
for (int i = 0; i < nRoots; i++)
|
||||
{
|
||||
float t = (float) r[i];
|
||||
if (t > 0.0 && t < 1.0)
|
||||
{
|
||||
double crossing = -(t * t * t) * (x0 - 3 * x1
|
||||
+ 3 * x2 - x3)
|
||||
+ 3 * t * t * (x0 - 2 * x1 + x2)
|
||||
+ 3 * t * (x1 - x0) + x0;
|
||||
if (crossing >= 0 && crossing <= distance)
|
||||
windingNumber += (3 * t * t * (y3 + 3 * y1
|
||||
- 3 * y2 - y0)
|
||||
+ 6 * t * (y0 - 2 * y1 + y2)
|
||||
+ 3 * (y1 - y0) < 0) ? 1 : negative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx = xpoints[pos - 1] - (float) x;
|
||||
cy = ypoints[pos - 1] - (float) y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// swap coordinates back
|
||||
if (useYaxis)
|
||||
{
|
||||
float[] swap;
|
||||
swap = ypoints;
|
||||
ypoints = xpoints;
|
||||
xpoints = swap;
|
||||
}
|
||||
return (windingNumber);
|
||||
}
|
||||
} // class GeneralPath
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/* IllegalPathStateException.java -- an operation was in an illegal path state
|
||||
Copyright (C) 2000, 2002 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
/**
|
||||
* Thrown when an operation on a path is in an illegal state, such as appending
|
||||
* a segment to a <code>GeneralPath</code> without an initial moveto.
|
||||
*
|
||||
* @author Tom Tromey (tromey@cygnus.com)
|
||||
* @see GeneralPath
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public class IllegalPathStateException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Compatible with JDK 1.2+.
|
||||
*/
|
||||
private static final long serialVersionUID = -5158084205220481094L;
|
||||
|
||||
/**
|
||||
* Create an exception with no message.
|
||||
*/
|
||||
public IllegalPathStateException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exception with a message.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
public IllegalPathStateException(String msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* NoninvertibleTransformException.java -- a transform can't be inverted
|
||||
Copyright (C) 2000, 2002 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
/**
|
||||
* Thrown if an operation requires an inverse of an
|
||||
* <code>AffineTransform</code>, but the transform is in a non-invertible
|
||||
* state.
|
||||
*
|
||||
* @author Tom Tromey (tromey@cygnus.com)
|
||||
* @see AffineTransform
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public class NoninvertibleTransformException extends Exception
|
||||
{
|
||||
/**
|
||||
* Compatible with JDK 1.2+.
|
||||
*/
|
||||
private static final long serialVersionUID = 6137225240503990466L;
|
||||
|
||||
/**
|
||||
* Create an exception with a message.
|
||||
*
|
||||
* @param s the message
|
||||
*/
|
||||
public NoninvertibleTransformException(String s)
|
||||
{
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/* PathIterator.java -- describes a shape by iterating over its vertices
|
||||
Copyright (C) 2000, 2002, 2003 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
/**
|
||||
* This interface provides a directed path over the boundary of a shape. The
|
||||
* path can contain 1st through 3rd order Bezier curves (lines, and quadratic
|
||||
* and cubic splines). A shape can have multiple disjoint paths via the
|
||||
* MOVETO directive, and can close a circular path back to the previos
|
||||
* MOVETO via the CLOSE directive.
|
||||
*
|
||||
* @author Tom Tromey (tromey@cygnus.com)
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @see java.awt.Shape
|
||||
* @see java.awt.Stroke
|
||||
* @see FlatteningPathIterator
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public interface PathIterator
|
||||
{
|
||||
/**
|
||||
* The even-odd winding mode: a point is internal to the shape if a ray
|
||||
* from the point to infinity (in any direction) crosses an odd number of
|
||||
* segments.
|
||||
*/
|
||||
int WIND_EVEN_ODD = 0;
|
||||
|
||||
/**
|
||||
* The non-zero winding mode: a point is internal to the shape if a ray
|
||||
* from the point to infinity (in any direction) crosses a different number
|
||||
* of segments headed clockwise than those headed counterclockwise.
|
||||
*/
|
||||
int WIND_NON_ZERO = 1;
|
||||
|
||||
/**
|
||||
* Starts a new subpath. There is no segment from the previous vertex.
|
||||
*/
|
||||
int SEG_MOVETO = 0;
|
||||
|
||||
/**
|
||||
* The current segment is a line.
|
||||
*/
|
||||
int SEG_LINETO = 1;
|
||||
|
||||
/**
|
||||
* The current segment is a quadratic parametric curve. It is interpolated
|
||||
* as t varies from 0 to 1 over the current point (CP), first control point
|
||||
* (P1), and final interpolated control point (P2):
|
||||
* <pre>
|
||||
* P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2
|
||||
* 0 <= t <= 1
|
||||
* B(n,m) = mth coefficient of nth degree Bernstein polynomial
|
||||
* = C(n,m) * t^(m) * (1 - t)^(n-m)
|
||||
* C(n,m) = Combinations of n things, taken m at a time
|
||||
* = n! / (m! * (n-m)!)
|
||||
* </pre>
|
||||
*/
|
||||
int SEG_QUADTO = 2;
|
||||
|
||||
/**
|
||||
* The current segment is a cubic parametric curve (more commonly known as
|
||||
* a Bezier curve). It is interpolated as t varies from 0 to 1 over the
|
||||
* current point (CP), first control point (P1), the second control point
|
||||
* (P2), and final interpolated control point (P3):
|
||||
* <pre>
|
||||
* P(t) = B(3,0)*CP + B(3,1)*P1 + B(3,2)*P2 + B(3,3)*P3
|
||||
* 0 <= t <= 1
|
||||
* B(n,m) = mth coefficient of nth degree Bernstein polynomial
|
||||
* = C(n,m) * t^(m) * (1 - t)^(n-m)
|
||||
* C(n,m) = Combinations of n things, taken m at a time
|
||||
* = n! / (m! * (n-m)!)
|
||||
* </pre>
|
||||
*/
|
||||
int SEG_CUBICTO = 3;
|
||||
|
||||
/**
|
||||
* The current segment closes a loop by an implicit line to the previous
|
||||
* SEG_MOVETO coordinate.
|
||||
*/
|
||||
int SEG_CLOSE = 4;
|
||||
|
||||
/**
|
||||
* Returns the winding rule to determine which points are inside this path.
|
||||
*
|
||||
* @return the winding rule
|
||||
* @see #WIND_EVEN_ODD
|
||||
* @see #WIND_NON_ZERO
|
||||
*/
|
||||
int getWindingRule();
|
||||
|
||||
/**
|
||||
* Tests if the iterator is exhausted. If this returns true, currentSegment
|
||||
* and next may throw a NoSuchElementException (although this is not
|
||||
* required).
|
||||
*
|
||||
* @return true if the iteration is complete
|
||||
*/
|
||||
boolean isDone();
|
||||
|
||||
/**
|
||||
* Advance to the next segment in the iteration. It is not specified what
|
||||
* this does if called when isDone() returns true.
|
||||
*
|
||||
* @throws java.util.NoSuchElementException optional when isDone() is true
|
||||
*/
|
||||
void next();
|
||||
|
||||
/**
|
||||
* Returns the coordinates of the next point(s), as well as the type of
|
||||
* line segment. The input array must be at least a float[6], to accomodate
|
||||
* up to three (x,y) point pairs (although if you know the iterator is
|
||||
* flat, you can probably get by with a float[2]). If the returned type is
|
||||
* SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if
|
||||
* the returned type is SEG_QUADTO, the first two points are modified; if
|
||||
* the returned type is SEG_CUBICTO, all three points are modified; and if
|
||||
* the returned type is SEG_CLOSE, the array is untouched.
|
||||
*
|
||||
* @param coords the array to place the point coordinates in
|
||||
* @return the segment type
|
||||
* @throws NullPointerException if coords is null
|
||||
* @throws ArrayIndexOutOfBoundsException if coords is too small
|
||||
* @throws java.util.NoSuchElementException optional when isDone() is true
|
||||
* @see #SEG_MOVETO
|
||||
* @see #SEG_LINETO
|
||||
* @see #SEG_QUADTO
|
||||
* @see #SEG_CUBICTO
|
||||
* @see #SEG_CLOSE
|
||||
*/
|
||||
int currentSegment(float[] coords);
|
||||
|
||||
/**
|
||||
* Returns the coordinates of the next point(s), as well as the type of
|
||||
* line segment. The input array must be at least a double[6], to accomodate
|
||||
* up to three (x,y) point pairs (although if you know the iterator is
|
||||
* flat, you can probably get by with a double[2]). If the returned type is
|
||||
* SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if
|
||||
* the returned type is SEG_QUADTO, the first two points are modified; if
|
||||
* the returned type is SEG_CUBICTO, all three points are modified; and if
|
||||
* the returned type is SEG_CLOSE, the array is untouched.
|
||||
*
|
||||
* @param coords the array to place the point coordinates in
|
||||
* @return the segment type
|
||||
* @throws NullPointerException if coords is null
|
||||
* @throws ArrayIndexOutOfBoundsException if coords is too small
|
||||
* @throws java.util.NoSuchElementException optional when isDone() is true
|
||||
* @see #SEG_MOVETO
|
||||
* @see #SEG_LINETO
|
||||
* @see #SEG_QUADTO
|
||||
* @see #SEG_CUBICTO
|
||||
* @see #SEG_CLOSE
|
||||
*/
|
||||
int currentSegment(double[] coords);
|
||||
} // interface PathIterator
|
||||
@@ -0,0 +1,396 @@
|
||||
/* Point2D.java -- generic point in 2-D space
|
||||
Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
/**
|
||||
* This class implements a generic point in 2D Cartesian space. The storage
|
||||
* representation is left up to the subclass. Point includes two useful
|
||||
* nested classes, for float and double storage respectively.
|
||||
*
|
||||
* @author Per Bothner (bothner@cygnus.com)
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public abstract class Point2D implements Cloneable
|
||||
{
|
||||
/**
|
||||
* The default constructor.
|
||||
*
|
||||
* @see java.awt.Point
|
||||
* @see Point2D.Float
|
||||
* @see Point2D.Double
|
||||
*/
|
||||
protected Point2D()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the X coordinate, in double precision.
|
||||
*
|
||||
* @return the x coordinate
|
||||
*/
|
||||
public abstract double getX();
|
||||
|
||||
/**
|
||||
* Get the Y coordinate, in double precision.
|
||||
*
|
||||
* @return the y coordinate
|
||||
*/
|
||||
public abstract double getY();
|
||||
|
||||
/**
|
||||
* Set the location of this point to the new coordinates. There may be a
|
||||
* loss of precision.
|
||||
*
|
||||
* @param x the new x coordinate
|
||||
* @param y the new y coordinate
|
||||
*/
|
||||
public abstract void setLocation(double x, double y);
|
||||
|
||||
/**
|
||||
* Set the location of this point to the new coordinates. There may be a
|
||||
* loss of precision.
|
||||
*
|
||||
* @param p the point to copy
|
||||
* @throws NullPointerException if p is null
|
||||
*/
|
||||
public void setLocation(Point2D p)
|
||||
{
|
||||
setLocation(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the square of the distance between two points.
|
||||
*
|
||||
* @param x1 the x coordinate of point 1
|
||||
* @param y1 the y coordinate of point 1
|
||||
* @param x2 the x coordinate of point 2
|
||||
* @param y2 the y coordinate of point 2
|
||||
* @return (x2 - x1)^2 + (y2 - y1)^2
|
||||
*/
|
||||
public static double distanceSq(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
x2 -= x1;
|
||||
y2 -= y1;
|
||||
return x2 * x2 + y2 * y2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the distance between two points.
|
||||
*
|
||||
* @param x1 the x coordinate of point 1
|
||||
* @param y1 the y coordinate of point 1
|
||||
* @param x2 the x coordinate of point 2
|
||||
* @param y2 the y coordinate of point 2
|
||||
* @return the distance from (x1,y1) to (x2,y2)
|
||||
*/
|
||||
public static double distance(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
return Math.sqrt(distanceSq(x1, y1, x2, y2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the square of the distance from this point to the given one.
|
||||
*
|
||||
* @param x the x coordinate of the other point
|
||||
* @param y the y coordinate of the other point
|
||||
* @return the square of the distance
|
||||
*/
|
||||
public double distanceSq(double x, double y)
|
||||
{
|
||||
return distanceSq(getX(), x, getY(), y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the square of the distance from this point to the given one.
|
||||
*
|
||||
* @param p the other point
|
||||
* @return the square of the distance
|
||||
* @throws NullPointerException if p is null
|
||||
*/
|
||||
public double distanceSq(Point2D p)
|
||||
{
|
||||
return distanceSq(getX(), p.getX(), getY(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the distance from this point to the given one.
|
||||
*
|
||||
* @param x the x coordinate of the other point
|
||||
* @param y the y coordinate of the other point
|
||||
* @return the distance
|
||||
*/
|
||||
public double distance(double x, double y)
|
||||
{
|
||||
return distance(getX(), x, getY(), y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the distance from this point to the given one.
|
||||
*
|
||||
* @param p the other point
|
||||
* @return the distance
|
||||
* @throws NullPointerException if p is null
|
||||
*/
|
||||
public double distance(Point2D p)
|
||||
{
|
||||
return distance(getX(), p.getX(), getY(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new point of the same run-time type with the same contents as
|
||||
* this one.
|
||||
*
|
||||
* @return the clone
|
||||
*/
|
||||
public Object clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
return super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e)
|
||||
{
|
||||
throw (Error) new InternalError().initCause(e); // Impossible
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hashcode for this point. The formula is not documented, but
|
||||
* appears to be the same as:
|
||||
* <pre>
|
||||
* long l = Double.doubleToLongBits(getY());
|
||||
* l = l * 31 ^ Double.doubleToLongBits(getX());
|
||||
* return (int) ((l >> 32) ^ l);
|
||||
* </pre>
|
||||
*
|
||||
* @return the hashcode
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
// Talk about a fun time reverse engineering this one!
|
||||
long l = java.lang.Double.doubleToLongBits(getY());
|
||||
l = l * 31 ^ java.lang.Double.doubleToLongBits(getX());
|
||||
return (int) ((l >> 32) ^ l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two points for equality. This returns true if they have the
|
||||
* same coordinates.
|
||||
*
|
||||
* @param o the point to compare
|
||||
* @return true if it is equal
|
||||
*/
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (! (o instanceof Point2D))
|
||||
return false;
|
||||
Point2D p = (Point2D) o;
|
||||
return getX() == p.getX() && getY() == p.getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a point in <code>double</code> precision.
|
||||
*
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public static class Double extends Point2D
|
||||
{
|
||||
/** The X coordinate. */
|
||||
public double x;
|
||||
|
||||
/** The Y coordinate. */
|
||||
public double y;
|
||||
|
||||
/**
|
||||
* Create a new point at (0,0).
|
||||
*/
|
||||
public Double()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new point at (x,y).
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
*/
|
||||
public Double(double x, double y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the x coordinate.
|
||||
*
|
||||
* @return the x coordinate
|
||||
*/
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the y coordinate.
|
||||
*
|
||||
* @return the y coordinate
|
||||
*/
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of this point.
|
||||
*
|
||||
* @param x the new x coordinate
|
||||
* @param y the new y coordinate
|
||||
*/
|
||||
public void setLocation(double x, double y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this object. The format is:
|
||||
* <code>"Point2D.Double[" + x + ", " + y + ']'</code>.
|
||||
*
|
||||
* @return a string representation of this object
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return "Point2D.Double[" + x + ", " + y + ']';
|
||||
}
|
||||
} // class Double
|
||||
|
||||
/**
|
||||
* This class defines a point in <code>float</code> precision.
|
||||
*
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public static class Float extends Point2D
|
||||
{
|
||||
/** The X coordinate. */
|
||||
public float x;
|
||||
|
||||
/** The Y coordinate. */
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* Create a new point at (0,0).
|
||||
*/
|
||||
public Float()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new point at (x,y).
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
*/
|
||||
public Float(float x, float y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the x coordinate.
|
||||
*
|
||||
* @return the x coordinate
|
||||
*/
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the y coordinate.
|
||||
*
|
||||
* @return the y coordinate
|
||||
*/
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of this point.
|
||||
*
|
||||
* @param x the new x coordinate
|
||||
* @param y the new y coordinate
|
||||
*/
|
||||
public void setLocation(double x, double y)
|
||||
{
|
||||
this.x = (float) x;
|
||||
this.y = (float) y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of this point.
|
||||
*
|
||||
* @param x the new x coordinate
|
||||
* @param y the new y coordinate
|
||||
*/
|
||||
public void setLocation(float x, float y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this object. The format is:
|
||||
* <code>"Point2D.Float[" + x + ", " + y + ']'</code>.
|
||||
*
|
||||
* @return a string representation of this object
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return "Point2D.Float[" + x + ", " + y + ']';
|
||||
}
|
||||
} // class Float
|
||||
} // class Point2D
|
||||
@@ -0,0 +1,992 @@
|
||||
/* Rectangle2D.java -- generic rectangles in 2-D space
|
||||
Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This class describes a rectangle by a point (x,y) and dimension (w x h).
|
||||
* The actual storage is left up to subclasses.
|
||||
*
|
||||
* <p>It is valid for a rectangle to have negative width or height; but it
|
||||
* is considered to have no area or internal points. Therefore, the behavior
|
||||
* in methods like <code>contains</code> or <code>intersects</code> is
|
||||
* undefined unless the rectangle has positive width and height.
|
||||
*
|
||||
* @author Tom Tromey (tromey@cygnus.com)
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public abstract class Rectangle2D extends RectangularShape
|
||||
{
|
||||
/**
|
||||
* The point lies left of the rectangle (p.x < r.x).
|
||||
*
|
||||
* @see #outcode(double, double)
|
||||
*/
|
||||
public static final int OUT_LEFT = 1;
|
||||
|
||||
/**
|
||||
* The point lies above the rectangle (p.y < r.y).
|
||||
*
|
||||
* @see #outcode(double, double)
|
||||
*/
|
||||
public static final int OUT_TOP = 2;
|
||||
|
||||
/**
|
||||
* The point lies right of the rectangle (p.x > r.maxX).
|
||||
*
|
||||
* @see #outcode(double, double)
|
||||
*/
|
||||
public static final int OUT_RIGHT = 4;
|
||||
|
||||
/**
|
||||
* The point lies below of the rectangle (p.y > r.maxY).
|
||||
*
|
||||
* @see #outcode(double, double)
|
||||
*/
|
||||
public static final int OUT_BOTTOM = 8;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
protected Rectangle2D()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bounding box of this rectangle.
|
||||
*
|
||||
* @param x the new X coordinate
|
||||
* @param y the new Y coordinate
|
||||
* @param w the new width
|
||||
* @param h the new height
|
||||
*/
|
||||
public abstract void setRect(double x, double y, double w, double h);
|
||||
|
||||
/**
|
||||
* Set the bounding box of this rectangle from the given one.
|
||||
*
|
||||
* @param r rectangle to copy
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public void setRect(Rectangle2D r)
|
||||
{
|
||||
setRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the specified line intersects the interior of this rectangle.
|
||||
*
|
||||
* @param x1 the first x coordinate of line segment
|
||||
* @param y1 the first y coordinate of line segment
|
||||
* @param x2 the second x coordinate of line segment
|
||||
* @param y2 the second y coordinate of line segment
|
||||
* @return true if the line intersects the rectangle
|
||||
*/
|
||||
public boolean intersectsLine(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
double x = getX();
|
||||
double y = getY();
|
||||
double w = getWidth();
|
||||
double h = getHeight();
|
||||
if (w <= 0 || h <= 0)
|
||||
return false;
|
||||
|
||||
if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y + h)
|
||||
return true;
|
||||
if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y + h)
|
||||
return true;
|
||||
|
||||
double x3 = x + w;
|
||||
double y3 = y + h;
|
||||
|
||||
return (Line2D.linesIntersect(x1, y1, x2, y2, x, y, x, y3)
|
||||
|| Line2D.linesIntersect(x1, y1, x2, y2, x, y3, x3, y3)
|
||||
|| Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x3, y)
|
||||
|| Line2D.linesIntersect(x1, y1, x2, y2, x3, y, x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the specified line intersects the interior of this rectangle.
|
||||
*
|
||||
* @param l the line segment
|
||||
* @return true if the line intersects the rectangle
|
||||
* @throws NullPointerException if l is null
|
||||
*/
|
||||
public boolean intersectsLine(Line2D l)
|
||||
{
|
||||
return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine where the point lies with respect to this rectangle. The
|
||||
* result will be the binary OR of the appropriate bit masks.
|
||||
*
|
||||
* @param x the x coordinate to check
|
||||
* @param y the y coordinate to check
|
||||
* @return the binary OR of the result
|
||||
* @see #OUT_LEFT
|
||||
* @see #OUT_TOP
|
||||
* @see #OUT_RIGHT
|
||||
* @see #OUT_BOTTOM
|
||||
*/
|
||||
public abstract int outcode(double x, double y);
|
||||
|
||||
/**
|
||||
* Determine where the point lies with respect to this rectangle. The
|
||||
* result will be the binary OR of the appropriate bit masks.
|
||||
*
|
||||
* @param p the point to check
|
||||
* @return the binary OR of the result
|
||||
* @throws NullPointerException if p is null
|
||||
* @see #OUT_LEFT
|
||||
* @see #OUT_TOP
|
||||
* @see #OUT_RIGHT
|
||||
* @see #OUT_BOTTOM
|
||||
*/
|
||||
public int outcode(Point2D p)
|
||||
{
|
||||
return outcode(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bounding box of this rectangle.
|
||||
*
|
||||
* @param x the new X coordinate
|
||||
* @param y the new Y coordinate
|
||||
* @param w the new width
|
||||
* @param h the new height
|
||||
*/
|
||||
public void setFrame(double x, double y, double w, double h)
|
||||
{
|
||||
setRect(x, y, w, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of this rectangle. A pretty useless method, as this
|
||||
* is already a rectangle.
|
||||
*
|
||||
* @return a copy of this rectangle
|
||||
*/
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
return (Rectangle2D) clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the given point is contained in the rectangle.
|
||||
*
|
||||
* @param x the x coordinate of the point
|
||||
* @param y the y coordinate of the point
|
||||
* @return true if (x,y) is in the rectangle
|
||||
*/
|
||||
public boolean contains(double x, double y)
|
||||
{
|
||||
double mx = getX();
|
||||
double my = getY();
|
||||
double w = getWidth();
|
||||
double h = getHeight();
|
||||
return w > 0 && h > 0 && x >= mx && x < mx + w && y >= my && y < my + h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given rectangle intersects this one. In other words, test if
|
||||
* the two rectangles share at least one internal point.
|
||||
*
|
||||
* @param x the x coordinate of the other rectangle
|
||||
* @param y the y coordinate of the other rectangle
|
||||
* @param w the width of the other rectangle
|
||||
* @param h the height of the other rectangle
|
||||
* @return true if the rectangles intersect
|
||||
*/
|
||||
public boolean intersects(double x, double y, double w, double h)
|
||||
{
|
||||
double mx = getX();
|
||||
double my = getY();
|
||||
double mw = getWidth();
|
||||
double mh = getHeight();
|
||||
return w > 0 && h > 0 && mw > 0 && mh > 0
|
||||
&& x < mx + mw && x + w > mx && y < my + mh && y + h > my;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this rectangle contains the given one. In other words, test if
|
||||
* this rectangle contains all points in the given one.
|
||||
*
|
||||
* @param x the x coordinate of the other rectangle
|
||||
* @param y the y coordinate of the other rectangle
|
||||
* @param w the width of the other rectangle
|
||||
* @param h the height of the other rectangle
|
||||
* @return true if this rectangle contains the other
|
||||
*/
|
||||
public boolean contains(double x, double y, double w, double h)
|
||||
{
|
||||
double mx = getX();
|
||||
double my = getY();
|
||||
double mw = getWidth();
|
||||
double mh = getHeight();
|
||||
return w > 0 && h > 0 && mw > 0 && mh > 0
|
||||
&& x >= mx && x + w <= mx + mw && y >= my && y + h <= my + mh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new rectangle which is the intersection of this and the given
|
||||
* one. The result will be empty if there is no intersection.
|
||||
*
|
||||
* @param r the rectangle to be intersected
|
||||
* @return the intersection
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public abstract Rectangle2D createIntersection(Rectangle2D r);
|
||||
|
||||
/**
|
||||
* Intersects a pair of rectangles, and places the result in the
|
||||
* destination; this can be used to avoid object creation. This method
|
||||
* even works when the destination is also a source, although you stand
|
||||
* to lose the original data.
|
||||
*
|
||||
* @param src1 the first source
|
||||
* @param src2 the second source
|
||||
* @param dest the destination for the intersection
|
||||
* @throws NullPointerException if any rectangle is null
|
||||
*/
|
||||
public static void intersect(Rectangle2D src1, Rectangle2D src2,
|
||||
Rectangle2D dest)
|
||||
{
|
||||
double x = Math.max(src1.getX(), src2.getX());
|
||||
double y = Math.max(src1.getY(), src2.getY());
|
||||
double maxx = Math.min(src1.getMaxX(), src2.getMaxX());
|
||||
double maxy = Math.min(src1.getMaxY(), src2.getMaxY());
|
||||
dest.setRect(x, y, maxx - x, maxy - y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new rectangle which is the union of this and the given one.
|
||||
*
|
||||
* @param r the rectangle to be merged
|
||||
* @return the union
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public abstract Rectangle2D createUnion(Rectangle2D r);
|
||||
|
||||
/**
|
||||
* Joins a pair of rectangles, and places the result in the destination;
|
||||
* this can be used to avoid object creation. This method even works when
|
||||
* the destination is also a source, although you stand to lose the
|
||||
* original data.
|
||||
*
|
||||
* @param src1 the first source
|
||||
* @param src2 the second source
|
||||
* @param dest the destination for the union
|
||||
* @throws NullPointerException if any rectangle is null
|
||||
*/
|
||||
public static void union(Rectangle2D src1, Rectangle2D src2,
|
||||
Rectangle2D dest)
|
||||
{
|
||||
double x = Math.min(src1.getX(), src2.getX());
|
||||
double y = Math.min(src1.getY(), src2.getY());
|
||||
double maxx = Math.max(src1.getMaxX(), src2.getMaxX());
|
||||
double maxy = Math.max(src1.getMaxY(), src2.getMaxY());
|
||||
dest.setRect(x, y, maxx - x, maxy - y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies this rectangle so that it represents the smallest rectangle
|
||||
* that contains both the existing rectangle and the specified point.
|
||||
* However, if the point falls on one of the two borders which are not
|
||||
* inside the rectangle, a subsequent call to <code>contains</code> may
|
||||
* return false.
|
||||
*
|
||||
* @param newx the X coordinate of the point to add to this rectangle
|
||||
* @param newy the Y coordinate of the point to add to this rectangle
|
||||
*/
|
||||
public void add(double newx, double newy)
|
||||
{
|
||||
double minx = Math.min(getX(), newx);
|
||||
double maxx = Math.max(getMaxX(), newx);
|
||||
double miny = Math.min(getY(), newy);
|
||||
double maxy = Math.max(getMaxY(), newy);
|
||||
setRect(minx, miny, maxx - minx, maxy - miny);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies this rectangle so that it represents the smallest rectangle
|
||||
* that contains both the existing rectangle and the specified point.
|
||||
* However, if the point falls on one of the two borders which are not
|
||||
* inside the rectangle, a subsequent call to <code>contains</code> may
|
||||
* return false.
|
||||
*
|
||||
* @param p the point to add to this rectangle
|
||||
* @throws NullPointerException if p is null
|
||||
*/
|
||||
public void add(Point2D p)
|
||||
{
|
||||
add(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies this rectangle so that it represents the smallest rectangle
|
||||
* that contains both the existing rectangle and the specified rectangle.
|
||||
*
|
||||
* @param r the rectangle to add to this rectangle
|
||||
* @throws NullPointerException if r is null
|
||||
* @see #union(Rectangle2D, Rectangle2D, Rectangle2D)
|
||||
*/
|
||||
public void add(Rectangle2D r)
|
||||
{
|
||||
union(this, r, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator along the shape boundary. If the optional transform
|
||||
* is provided, the iterator is transformed accordingly. Each call returns
|
||||
* a new object, independent from others in use. This iterator is thread
|
||||
* safe; modifications to the rectangle do not affect the results of this
|
||||
* path instance.
|
||||
*
|
||||
* @param at an optional transform to apply to the iterator
|
||||
* @return a new iterator over the boundary
|
||||
* @since 1.2
|
||||
*/
|
||||
public PathIterator getPathIterator(final AffineTransform at)
|
||||
{
|
||||
final double minx = getX();
|
||||
final double miny = getY();
|
||||
final double maxx = minx + getWidth();
|
||||
final double maxy = miny + getHeight();
|
||||
return new PathIterator()
|
||||
{
|
||||
/** Current coordinate. */
|
||||
private int current = (maxx <= minx && maxy <= miny) ? 6 : 0;
|
||||
|
||||
public int getWindingRule()
|
||||
{
|
||||
// A test program showed that Sun J2SE 1.3.1 and 1.4.1_01
|
||||
// return WIND_NON_ZERO paths. While this does not really
|
||||
// make any difference for rectangles (because they are not
|
||||
// self-intersecting), it seems appropriate to behave
|
||||
// identically.
|
||||
|
||||
return WIND_NON_ZERO;
|
||||
}
|
||||
|
||||
public boolean isDone()
|
||||
{
|
||||
return current > 5;
|
||||
}
|
||||
|
||||
public void next()
|
||||
{
|
||||
current++;
|
||||
}
|
||||
|
||||
public int currentSegment(float[] coords)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
case 1:
|
||||
coords[0] = (float) maxx;
|
||||
coords[1] = (float) miny;
|
||||
break;
|
||||
case 2:
|
||||
coords[0] = (float) maxx;
|
||||
coords[1] = (float) maxy;
|
||||
break;
|
||||
case 3:
|
||||
coords[0] = (float) minx;
|
||||
coords[1] = (float) maxy;
|
||||
break;
|
||||
case 0:
|
||||
case 4:
|
||||
coords[0] = (float) minx;
|
||||
coords[1] = (float) miny;
|
||||
break;
|
||||
case 5:
|
||||
return SEG_CLOSE;
|
||||
default:
|
||||
throw new NoSuchElementException("rect iterator out of bounds");
|
||||
}
|
||||
if (at != null)
|
||||
at.transform(coords, 0, coords, 0, 1);
|
||||
return current == 0 ? SEG_MOVETO : SEG_LINETO;
|
||||
}
|
||||
|
||||
public int currentSegment(double[] coords)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
case 1:
|
||||
coords[0] = maxx;
|
||||
coords[1] = miny;
|
||||
break;
|
||||
case 2:
|
||||
coords[0] = maxx;
|
||||
coords[1] = maxy;
|
||||
break;
|
||||
case 3:
|
||||
coords[0] = minx;
|
||||
coords[1] = maxy;
|
||||
break;
|
||||
case 0:
|
||||
case 4:
|
||||
coords[0] = minx;
|
||||
coords[1] = miny;
|
||||
break;
|
||||
case 5:
|
||||
return SEG_CLOSE;
|
||||
default:
|
||||
throw new NoSuchElementException("rect iterator out of bounds");
|
||||
}
|
||||
if (at != null)
|
||||
at.transform(coords, 0, coords, 0, 1);
|
||||
return current == 0 ? SEG_MOVETO : SEG_LINETO;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator along the shape boundary. If the optional transform
|
||||
* is provided, the iterator is transformed accordingly. Each call returns
|
||||
* a new object, independent from others in use. This iterator is thread
|
||||
* safe; modifications to the rectangle do not affect the results of this
|
||||
* path instance. As the rectangle is already flat, the flatness parameter
|
||||
* is ignored.
|
||||
*
|
||||
* @param at an optional transform to apply to the iterator
|
||||
* @param flatness the maximum distance for deviation from the real boundary
|
||||
* @return a new iterator over the boundary
|
||||
* @since 1.2
|
||||
*/
|
||||
public PathIterator getPathIterator(AffineTransform at, double flatness)
|
||||
{
|
||||
return getPathIterator(at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hashcode for this rectangle. The formula is not documented, but
|
||||
* appears to be the same as:
|
||||
* <pre>
|
||||
* long l = Double.doubleToLongBits(getX())
|
||||
* + 37 * Double.doubleToLongBits(getY())
|
||||
* + 43 * Double.doubleToLongBits(getWidth())
|
||||
* + 47 * Double.doubleToLongBits(getHeight());
|
||||
* return (int) ((l >> 32) ^ l);
|
||||
* </pre>
|
||||
*
|
||||
* @return the hashcode
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
// Talk about a fun time reverse engineering this one!
|
||||
long l = java.lang.Double.doubleToLongBits(getX())
|
||||
+ 37 * java.lang.Double.doubleToLongBits(getY())
|
||||
+ 43 * java.lang.Double.doubleToLongBits(getWidth())
|
||||
+ 47 * java.lang.Double.doubleToLongBits(getHeight());
|
||||
return (int) ((l >> 32) ^ l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests this rectangle for equality against the specified object. This
|
||||
* will be true if an only if the specified object is an instance of
|
||||
* Rectangle2D with the same coordinates and dimensions.
|
||||
*
|
||||
* @param obj the object to test against for equality
|
||||
* @return true if the specified object is equal to this one
|
||||
*/
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (! (obj instanceof Rectangle2D))
|
||||
return false;
|
||||
Rectangle2D r = (Rectangle2D) obj;
|
||||
return r.getX() == getX() && r.getY() == getY()
|
||||
&& r.getWidth() == getWidth() && r.getHeight() == getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a rectangle in <code>double</code> precision.
|
||||
*
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public static class Double extends Rectangle2D
|
||||
{
|
||||
/** The x coordinate of the lower left corner. */
|
||||
public double x;
|
||||
|
||||
/** The y coordinate of the lower left corner. */
|
||||
public double y;
|
||||
|
||||
/** The width of the rectangle. */
|
||||
public double width;
|
||||
|
||||
/** The height of the rectangle. */
|
||||
public double height;
|
||||
|
||||
/**
|
||||
* Create a rectangle at (0,0) with width 0 and height 0.
|
||||
*/
|
||||
public Double()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rectangle with the given values.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param w the width
|
||||
* @param h the height
|
||||
*/
|
||||
public Double(double x, double y, double w, double h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the X coordinate.
|
||||
*
|
||||
* @return the value of x
|
||||
*/
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Y coordinate.
|
||||
*
|
||||
* @return the value of y
|
||||
*/
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the width.
|
||||
*
|
||||
* @return the value of width
|
||||
*/
|
||||
public double getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the height.
|
||||
*
|
||||
* @return the value of height
|
||||
*/
|
||||
public double getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the rectangle is empty.
|
||||
*
|
||||
* @return true if width or height is not positive
|
||||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return width <= 0 || height <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents of this rectangle to those specified.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param w the width
|
||||
* @param h the height
|
||||
*/
|
||||
public void setRect(double x, double y, double w, double h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents of this rectangle to those specified.
|
||||
*
|
||||
* @param r the rectangle to copy
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public void setRect(Rectangle2D r)
|
||||
{
|
||||
x = r.getX();
|
||||
y = r.getY();
|
||||
width = r.getWidth();
|
||||
height = r.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine where the point lies with respect to this rectangle. The
|
||||
* result will be the binary OR of the appropriate bit masks.
|
||||
*
|
||||
* @param x the x coordinate to check
|
||||
* @param y the y coordinate to check
|
||||
* @return the binary OR of the result
|
||||
* @see #OUT_LEFT
|
||||
* @see #OUT_TOP
|
||||
* @see #OUT_RIGHT
|
||||
* @see #OUT_BOTTOM
|
||||
* @since 1.2
|
||||
*/
|
||||
public int outcode(double x, double y)
|
||||
{
|
||||
int result = 0;
|
||||
if (width <= 0)
|
||||
result |= OUT_LEFT | OUT_RIGHT;
|
||||
else if (x < this.x)
|
||||
result |= OUT_LEFT;
|
||||
else if (x > this.x + width)
|
||||
result |= OUT_RIGHT;
|
||||
if (height <= 0)
|
||||
result |= OUT_BOTTOM | OUT_TOP;
|
||||
else if (y < this.y) // Remember that +y heads top-to-bottom.
|
||||
result |= OUT_TOP;
|
||||
else if (y > this.y + height)
|
||||
result |= OUT_BOTTOM;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of this rectangle. A pretty useless method, as this
|
||||
* is already a rectangle.
|
||||
*
|
||||
* @return a copy of this rectangle
|
||||
*/
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
return new Double(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new rectangle which is the intersection of this and the given
|
||||
* one. The result will be empty if there is no intersection.
|
||||
*
|
||||
* @param r the rectangle to be intersected
|
||||
* @return the intersection
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public Rectangle2D createIntersection(Rectangle2D r)
|
||||
{
|
||||
Double res = new Double();
|
||||
intersect(this, r, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new rectangle which is the union of this and the given one.
|
||||
*
|
||||
* @param r the rectangle to be merged
|
||||
* @return the union
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public Rectangle2D createUnion(Rectangle2D r)
|
||||
{
|
||||
Double res = new Double();
|
||||
union(this, r, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this rectangle. This is in the form
|
||||
* <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
|
||||
* + ",h=" + height + ']'</code>.
|
||||
*
|
||||
* @return a string representation of this rectangle
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
|
||||
+ ",h=" + height + ']';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a rectangle in <code>float</code> precision.
|
||||
*
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public static class Float extends Rectangle2D
|
||||
{
|
||||
/** The x coordinate of the lower left corner. */
|
||||
public float x;
|
||||
|
||||
/** The y coordinate of the lower left corner. */
|
||||
public float y;
|
||||
|
||||
/** The width of the rectangle. */
|
||||
public float width;
|
||||
|
||||
/** The height of the rectangle. */
|
||||
public float height;
|
||||
|
||||
/**
|
||||
* Create a rectangle at (0,0) with width 0 and height 0.
|
||||
*/
|
||||
public Float()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rectangle with the given values.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param w the width
|
||||
* @param h the height
|
||||
*/
|
||||
public Float(float x, float y, float w, float h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rectangle with the given values.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param w the width
|
||||
* @param h the height
|
||||
*/
|
||||
Float(double x, double y, double w, double h)
|
||||
{
|
||||
this.x = (float) x;
|
||||
this.y = (float) y;
|
||||
width = (float) w;
|
||||
height = (float) h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the X coordinate.
|
||||
*
|
||||
* @return the value of x
|
||||
*/
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Y coordinate.
|
||||
*
|
||||
* @return the value of y
|
||||
*/
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the width.
|
||||
*
|
||||
* @return the value of width
|
||||
*/
|
||||
public double getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the height.
|
||||
*
|
||||
* @return the value of height
|
||||
*/
|
||||
public double getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the rectangle is empty.
|
||||
*
|
||||
* @return true if width or height is not positive
|
||||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return width <= 0 || height <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents of this rectangle to those specified.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param w the width
|
||||
* @param h the height
|
||||
*/
|
||||
public void setRect(float x, float y, float w, float h)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents of this rectangle to those specified.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param w the width
|
||||
* @param h the height
|
||||
*/
|
||||
public void setRect(double x, double y, double w, double h)
|
||||
{
|
||||
this.x = (float) x;
|
||||
this.y = (float) y;
|
||||
width = (float) w;
|
||||
height = (float) h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents of this rectangle to those specified.
|
||||
*
|
||||
* @param r the rectangle to copy
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public void setRect(Rectangle2D r)
|
||||
{
|
||||
x = (float) r.getX();
|
||||
y = (float) r.getY();
|
||||
width = (float) r.getWidth();
|
||||
height = (float) r.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine where the point lies with respect to this rectangle. The
|
||||
* result will be the binary OR of the appropriate bit masks.
|
||||
*
|
||||
* @param x the x coordinate to check
|
||||
* @param y the y coordinate to check
|
||||
* @return the binary OR of the result
|
||||
* @see #OUT_LEFT
|
||||
* @see #OUT_TOP
|
||||
* @see #OUT_RIGHT
|
||||
* @see #OUT_BOTTOM
|
||||
* @since 1.2
|
||||
*/
|
||||
public int outcode(double x, double y)
|
||||
{
|
||||
int result = 0;
|
||||
if (width <= 0)
|
||||
result |= OUT_LEFT | OUT_RIGHT;
|
||||
else if (x < this.x)
|
||||
result |= OUT_LEFT;
|
||||
else if (x > this.x + width)
|
||||
result |= OUT_RIGHT;
|
||||
if (height <= 0)
|
||||
result |= OUT_BOTTOM | OUT_TOP;
|
||||
else if (y < this.y) // Remember that +y heads top-to-bottom.
|
||||
result |= OUT_TOP;
|
||||
else if (y > this.y + height)
|
||||
result |= OUT_BOTTOM;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of this rectangle. A pretty useless method, as this
|
||||
* is already a rectangle.
|
||||
*
|
||||
* @return a copy of this rectangle
|
||||
*/
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
return new Float(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new rectangle which is the intersection of this and the given
|
||||
* one. The result will be empty if there is no intersection.
|
||||
*
|
||||
* @param r the rectangle to be intersected
|
||||
* @return the intersection
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public Rectangle2D createIntersection(Rectangle2D r)
|
||||
{
|
||||
Float res = new Float();
|
||||
intersect(this, r, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new rectangle which is the union of this and the given one.
|
||||
*
|
||||
* @param r the rectangle to be merged
|
||||
* @return the union
|
||||
* @throws NullPointerException if r is null
|
||||
*/
|
||||
public Rectangle2D createUnion(Rectangle2D r)
|
||||
{
|
||||
Float res = new Float();
|
||||
union(this, r, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this rectangle. This is in the form
|
||||
* <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
|
||||
* + ",h=" + height + ']'</code>.
|
||||
*
|
||||
* @return a string representation of this rectangle
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
|
||||
+ ",h=" + height + ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
/* RectangularShape.java -- a rectangular frame for several generic shapes
|
||||
Copyright (C) 2000, 2002 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
|
||||
/**
|
||||
* This class provides a generic framework, and several helper methods, for
|
||||
* subclasses which represent geometric objects inside a rectangular frame.
|
||||
* This does not specify any geometry except for the bounding box.
|
||||
*
|
||||
* @author Tom Tromey (tromey@cygnus.com)
|
||||
* @author Eric Blake (ebb9@email.byu.edu)
|
||||
* @since 1.2
|
||||
* @see Arc2D
|
||||
* @see Ellipse2D
|
||||
* @see Rectangle2D
|
||||
* @see RoundRectangle2D
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public abstract class RectangularShape implements Shape, Cloneable
|
||||
{
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
protected RectangularShape()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x coordinate of the upper-left corner of the framing rectangle.
|
||||
*
|
||||
* @return the x coordinate
|
||||
*/
|
||||
public abstract double getX();
|
||||
|
||||
/**
|
||||
* Get the y coordinate of the upper-left corner of the framing rectangle.
|
||||
*
|
||||
* @return the y coordinate
|
||||
*/
|
||||
public abstract double getY();
|
||||
|
||||
/**
|
||||
* Get the width of the framing rectangle.
|
||||
*
|
||||
* @return the width
|
||||
*/
|
||||
public abstract double getWidth();
|
||||
|
||||
/**
|
||||
* Get the height of the framing rectangle.
|
||||
*
|
||||
* @return the height
|
||||
*/
|
||||
public abstract double getHeight();
|
||||
|
||||
/**
|
||||
* Get the minimum x coordinate in the frame. This is misnamed, or else
|
||||
* Sun has a bug, because the implementation returns getX() even when
|
||||
* getWidth() is negative.
|
||||
*
|
||||
* @return the minimum x coordinate
|
||||
*/
|
||||
public double getMinX()
|
||||
{
|
||||
return getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum y coordinate in the frame. This is misnamed, or else
|
||||
* Sun has a bug, because the implementation returns getY() even when
|
||||
* getHeight() is negative.
|
||||
*
|
||||
* @return the minimum y coordinate
|
||||
*/
|
||||
public double getMinY()
|
||||
{
|
||||
return getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum x coordinate in the frame. This is misnamed, or else
|
||||
* Sun has a bug, because the implementation returns getX()+getWidth() even
|
||||
* when getWidth() is negative.
|
||||
*
|
||||
* @return the maximum x coordinate
|
||||
*/
|
||||
public double getMaxX()
|
||||
{
|
||||
return getX() + getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum y coordinate in the frame. This is misnamed, or else
|
||||
* Sun has a bug, because the implementation returns getY()+getHeight() even
|
||||
* when getHeight() is negative.
|
||||
*
|
||||
* @return the maximum y coordinate
|
||||
*/
|
||||
public double getMaxY()
|
||||
{
|
||||
return getY() + getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the x coordinate of the center point of the framing rectangle.
|
||||
*
|
||||
* @return the central x coordinate
|
||||
*/
|
||||
public double getCenterX()
|
||||
{
|
||||
return getX() + getWidth() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the y coordinate of the center point of the framing rectangle.
|
||||
*
|
||||
* @return the central y coordinate
|
||||
*/
|
||||
public double getCenterY()
|
||||
{
|
||||
return getY() + getHeight() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the frame around this object. Note that this may be a looser
|
||||
* bounding box than getBounds2D.
|
||||
*
|
||||
* @return the frame, in double precision
|
||||
* @see #setFrame(double, double, double, double)
|
||||
*/
|
||||
public Rectangle2D getFrame()
|
||||
{
|
||||
return new Rectangle2D.Double(getX(), getY(), getWidth(), getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the shape is empty, meaning that no points are inside it.
|
||||
*
|
||||
* @return true if the shape is empty
|
||||
*/
|
||||
public abstract boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Set the framing rectangle of this shape to the given coordinate and size.
|
||||
*
|
||||
* @param x the new x coordinate
|
||||
* @param y the new y coordinate
|
||||
* @param w the new width
|
||||
* @param h the new height
|
||||
* @see #getFrame()
|
||||
*/
|
||||
public abstract void setFrame(double x, double y, double w, double h);
|
||||
|
||||
/**
|
||||
* Set the framing rectangle of this shape to the given coordinate and size.
|
||||
*
|
||||
* @param p the new point
|
||||
* @param d the new dimension
|
||||
* @throws NullPointerException if p or d is null
|
||||
* @see #getFrame()
|
||||
*/
|
||||
public void setFrame(Point2D p, Dimension2D d)
|
||||
{
|
||||
setFrame(p.getX(), p.getY(), d.getWidth(), d.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the framing rectangle of this shape to the given rectangle.
|
||||
*
|
||||
* @param r the new framing rectangle
|
||||
* @throws NullPointerException if r is null
|
||||
* @see #getFrame()
|
||||
*/
|
||||
public void setFrame(Rectangle2D r)
|
||||
{
|
||||
setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the framing rectangle of this shape using two points on a diagonal.
|
||||
* The area will be positive.
|
||||
*
|
||||
* @param x1 the first x coordinate
|
||||
* @param y1 the first y coordinate
|
||||
* @param x2 the second x coordinate
|
||||
* @param y2 the second y coordinate
|
||||
*/
|
||||
public void setFrameFromDiagonal(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
if (x1 > x2)
|
||||
{
|
||||
double t = x2;
|
||||
x2 = x1;
|
||||
x1 = t;
|
||||
}
|
||||
if (y1 > y2)
|
||||
{
|
||||
double t = y2;
|
||||
y2 = y1;
|
||||
y1 = t;
|
||||
}
|
||||
setFrame(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the framing rectangle of this shape using two points on a diagonal.
|
||||
* The area will be positive.
|
||||
*
|
||||
* @param p1 the first point
|
||||
* @param p2 the second point
|
||||
* @throws NullPointerException if either point is null
|
||||
*/
|
||||
public void setFrameFromDiagonal(Point2D p1, Point2D p2)
|
||||
{
|
||||
setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the framing rectangle of this shape using the center of the frame,
|
||||
* and one of the four corners. The area will be positive.
|
||||
*
|
||||
* @param centerX the x coordinate at the center
|
||||
* @param centerY the y coordinate at the center
|
||||
* @param cornerX the x coordinate at a corner
|
||||
* @param cornerY the y coordinate at a corner
|
||||
*/
|
||||
public void setFrameFromCenter(double centerX, double centerY,
|
||||
double cornerX, double cornerY)
|
||||
{
|
||||
double halfw = Math.abs(cornerX - centerX);
|
||||
double halfh = Math.abs(cornerY - centerY);
|
||||
setFrame(centerX - halfw, centerY - halfh, halfw + halfw, halfh + halfh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the framing rectangle of this shape using the center of the frame,
|
||||
* and one of the four corners. The area will be positive.
|
||||
*
|
||||
* @param center the center point
|
||||
* @param corner a corner point
|
||||
* @throws NullPointerException if either point is null
|
||||
*/
|
||||
public void setFrameFromCenter(Point2D center, Point2D corner)
|
||||
{
|
||||
setFrameFromCenter(center.getX(), center.getY(),
|
||||
corner.getX(), corner.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a point is inside the boundary of the shape.
|
||||
*
|
||||
* @param p the point to test
|
||||
* @return true if the point is inside the shape
|
||||
* @throws NullPointerException if p is null
|
||||
* @see #contains(double, double)
|
||||
*/
|
||||
public boolean contains(Point2D p)
|
||||
{
|
||||
return contains(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a rectangle and this shape share common internal points.
|
||||
*
|
||||
* @param r the rectangle to test
|
||||
* @return true if the rectangle intersects this shpae
|
||||
* @throws NullPointerException if r is null
|
||||
* @see #intersects(double, double, double, double)
|
||||
*/
|
||||
public boolean intersects(Rectangle2D r)
|
||||
{
|
||||
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the shape completely contains the given rectangle.
|
||||
*
|
||||
* @param r the rectangle to test
|
||||
* @return true if r is contained in this shape
|
||||
* @throws NullPointerException if r is null
|
||||
* @see #contains(double, double, double, double)
|
||||
*/
|
||||
public boolean contains(Rectangle2D r)
|
||||
{
|
||||
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bounding box for this shape, in integer format. Notice that you
|
||||
* may get a tighter bound with getBounds2D. If the frame is empty, the
|
||||
* box is the default empty box at the origin.
|
||||
*
|
||||
* @return a bounding box
|
||||
*/
|
||||
public Rectangle getBounds()
|
||||
{
|
||||
if (isEmpty())
|
||||
return new Rectangle();
|
||||
double x = getX();
|
||||
double y = getY();
|
||||
double maxx = Math.ceil(x + getWidth());
|
||||
double maxy = Math.ceil(y + getHeight());
|
||||
x = Math.floor(x);
|
||||
y = Math.floor(y);
|
||||
return new Rectangle((int) x, (int) y, (int) (maxx - x), (int) (maxy - y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator along the shape boundary. If the optional transform
|
||||
* is provided, the iterator is transformed accordingly. The path is
|
||||
* flattened until all segments differ from the curve by at most the value
|
||||
* of the flatness parameter, within the limits of the default interpolation
|
||||
* recursion limit of 1024 segments between actual points. Each call
|
||||
* returns a new object, independent from others in use. The result is
|
||||
* threadsafe if and only if the iterator returned by
|
||||
* {@link #getPathIterator(AffineTransform)} is as well.
|
||||
*
|
||||
* @param at an optional transform to apply to the iterator
|
||||
* @param flatness the desired flatness
|
||||
* @return a new iterator over the boundary
|
||||
* @throws IllegalArgumentException if flatness is invalid
|
||||
* @since 1.2
|
||||
*/
|
||||
public PathIterator getPathIterator(AffineTransform at, double flatness)
|
||||
{
|
||||
return new FlatteningPathIterator(getPathIterator(at), flatness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new shape of the same run-time type with the same contents as
|
||||
* this one.
|
||||
*
|
||||
* @return the clone
|
||||
*/
|
||||
public Object clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
return super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e)
|
||||
{
|
||||
throw (Error) new InternalError().initCause(e); // Impossible
|
||||
}
|
||||
}
|
||||
} // class RectangularShape
|
||||
@@ -0,0 +1,533 @@
|
||||
/* RoundRectangle2D.java -- represents a rectangle with rounded corners
|
||||
Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
|
||||
/** This class implements a rectangle with rounded corners.
|
||||
* @author Tom Tromey (tromey@cygnus.com)
|
||||
* @date December 3, 2000
|
||||
*/
|
||||
public abstract class RoundRectangle2D extends RectangularShape
|
||||
{
|
||||
/** Return the arc height of this round rectangle. */
|
||||
public abstract double getArcHeight();
|
||||
|
||||
/** Return the arc width of this round rectangle. */
|
||||
public abstract double getArcWidth();
|
||||
|
||||
/** Set the values of this round rectangle
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
* @param w The width
|
||||
* @param h The height
|
||||
* @param arcWidth The arc width
|
||||
* @param arcHeight The arc height
|
||||
*/
|
||||
public abstract void setRoundRect(double x, double y, double w, double h,
|
||||
double arcWidth, double arcHeight);
|
||||
|
||||
/** Create a RoundRectangle2D. This is protected because this class
|
||||
* is abstract and cannot be instantiated.
|
||||
*/
|
||||
protected RoundRectangle2D()
|
||||
{
|
||||
}
|
||||
|
||||
/** Return true if this object contains the specified point.
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
*/
|
||||
public boolean contains(double x, double y)
|
||||
{
|
||||
double mx = getX();
|
||||
double mw = getWidth();
|
||||
if (x < mx || x >= mx + mw)
|
||||
return false;
|
||||
double my = getY();
|
||||
double mh = getHeight();
|
||||
if (y < my || y >= my + mh)
|
||||
return false;
|
||||
|
||||
// Now check to see if the point is in range of an arc.
|
||||
double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y));
|
||||
double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x));
|
||||
|
||||
// The arc dimensions are that of the corresponding ellipse
|
||||
// thus a 90 degree segment is half of that.
|
||||
double aw = getArcWidth() / 2.0;
|
||||
double ah = getArcHeight() / 2.0;
|
||||
if (dx > aw || dy > ah)
|
||||
return true;
|
||||
|
||||
// At this point DX represents the distance from the nearest edge
|
||||
// of the rectangle. But we want to transform it to represent the
|
||||
// scaled distance from the center of the ellipse that forms the
|
||||
// arc. Hence this code:
|
||||
dy = (ah - dy) / ah;
|
||||
dx = (aw - dx) / aw;
|
||||
|
||||
return dx * dx + dy * dy <= 1.0;
|
||||
}
|
||||
|
||||
/** Return true if this object contains the specified rectangle
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
* @param w The width
|
||||
* @param h The height
|
||||
*/
|
||||
public boolean contains(double x, double y, double w, double h)
|
||||
{
|
||||
// We have to check all four points here (for ordinary rectangles
|
||||
// we can just check opposing corners).
|
||||
return (contains(x, y) && contains(x, y + h) && contains(x + w, y + h)
|
||||
&& contains(x + w, y));
|
||||
}
|
||||
|
||||
/** Return a new path iterator which iterates over this rectangle.
|
||||
* @param at An affine transform to apply to the object
|
||||
*/
|
||||
public PathIterator getPathIterator(final AffineTransform at)
|
||||
{
|
||||
final double minx = getX();
|
||||
final double miny = getY();
|
||||
final double maxx = minx + getWidth();
|
||||
final double maxy = miny + getHeight();
|
||||
final double arcwidth = getArcWidth();
|
||||
final double archeight = getArcHeight();
|
||||
return new PathIterator()
|
||||
{
|
||||
/** We iterate counterclockwise around the rectangle, starting in the
|
||||
* upper right. This variable tracks our current point, which
|
||||
* can be on either side of a given corner. */
|
||||
private int current = 0;
|
||||
|
||||
/** Child path iterator, used for corners. */
|
||||
private PathIterator corner;
|
||||
|
||||
/** This is used when rendering the corners. We re-use the arc
|
||||
* for each corner. */
|
||||
private Arc2D arc = new Arc2D.Double();
|
||||
|
||||
/** Temporary array used by getPoint. */
|
||||
private double[] temp = new double[2];
|
||||
|
||||
public int getWindingRule()
|
||||
{
|
||||
return WIND_NON_ZERO;
|
||||
}
|
||||
|
||||
public boolean isDone()
|
||||
{
|
||||
return current > 9;
|
||||
}
|
||||
|
||||
private void getPoint(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case 0:
|
||||
case 8:
|
||||
temp[0] = maxx;
|
||||
temp[1] = miny + archeight;
|
||||
break;
|
||||
case 7:
|
||||
temp[0] = maxx;
|
||||
temp[1] = maxy - archeight;
|
||||
break;
|
||||
case 6:
|
||||
temp[0] = maxx - arcwidth;
|
||||
temp[1] = maxy;
|
||||
break;
|
||||
case 5:
|
||||
temp[0] = minx + arcwidth;
|
||||
temp[1] = maxy;
|
||||
break;
|
||||
case 4:
|
||||
temp[0] = minx;
|
||||
temp[1] = maxy - archeight;
|
||||
break;
|
||||
case 3:
|
||||
temp[0] = minx;
|
||||
temp[1] = miny + archeight;
|
||||
break;
|
||||
case 2:
|
||||
temp[0] = minx + arcwidth;
|
||||
temp[1] = miny;
|
||||
break;
|
||||
case 1:
|
||||
temp[0] = maxx - arcwidth;
|
||||
temp[1] = miny;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void next()
|
||||
{
|
||||
if (current >= 8)
|
||||
++current;
|
||||
else if (corner != null)
|
||||
{
|
||||
// We're iterating through the corner. Work on the child
|
||||
// iterator; if it finishes, reset and move to the next
|
||||
// point along the rectangle.
|
||||
corner.next();
|
||||
if (corner.isDone())
|
||||
{
|
||||
corner = null;
|
||||
++current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make an arc between this point on the rectangle and
|
||||
// the next one, and then iterate over this arc.
|
||||
getPoint(current);
|
||||
double x1 = temp[0];
|
||||
double y1 = temp[1];
|
||||
getPoint(current + 1);
|
||||
Rectangle2D.Double r = new Rectangle2D.Double(Math.min(x1,
|
||||
temp[0]),
|
||||
Math.min(y1,
|
||||
temp[1]),
|
||||
Math.abs(x1
|
||||
- temp[0]),
|
||||
Math.abs(y1
|
||||
- temp[1]));
|
||||
arc.setArc(r, (current >> 1) * 90.0, 90.0, Arc2D.OPEN);
|
||||
corner = arc.getPathIterator(at);
|
||||
}
|
||||
}
|
||||
|
||||
public int currentSegment(float[] coords)
|
||||
{
|
||||
if (corner != null)
|
||||
{
|
||||
int r = corner.currentSegment(coords);
|
||||
if (r == SEG_MOVETO)
|
||||
r = SEG_LINETO;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (current < 9)
|
||||
{
|
||||
getPoint(current);
|
||||
coords[0] = (float) temp[0];
|
||||
coords[1] = (float) temp[1];
|
||||
}
|
||||
else if (current == 9)
|
||||
return SEG_CLOSE;
|
||||
else
|
||||
throw new NoSuchElementException("rect iterator out of bounds");
|
||||
|
||||
if (at != null)
|
||||
at.transform(coords, 0, coords, 0, 1);
|
||||
return current == 0 ? SEG_MOVETO : SEG_LINETO;
|
||||
}
|
||||
|
||||
public int currentSegment(double[] coords)
|
||||
{
|
||||
if (corner != null)
|
||||
{
|
||||
int r = corner.currentSegment(coords);
|
||||
if (r == SEG_MOVETO)
|
||||
r = SEG_LINETO;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (current < 9)
|
||||
{
|
||||
getPoint(current);
|
||||
coords[0] = temp[0];
|
||||
coords[1] = temp[1];
|
||||
}
|
||||
else if (current == 9)
|
||||
return SEG_CLOSE;
|
||||
else
|
||||
throw new NoSuchElementException("rect iterator out of bounds");
|
||||
|
||||
if (at != null)
|
||||
at.transform(coords, 0, coords, 0, 1);
|
||||
return current == 0 ? SEG_MOVETO : SEG_LINETO;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Return true if the given rectangle intersects this shape.
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
* @param w The width
|
||||
* @param h The height
|
||||
*/
|
||||
public boolean intersects(double x, double y, double w, double h)
|
||||
{
|
||||
// Check if any corner is within the rectangle
|
||||
return (contains(x, y) || contains(x, y + h) || contains(x + w, y + h)
|
||||
|| contains(x + w, y));
|
||||
}
|
||||
|
||||
/** Set the boundary of this round rectangle.
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
* @param w The width
|
||||
* @param h The height
|
||||
*/
|
||||
public void setFrame(double x, double y, double w, double h)
|
||||
{
|
||||
// This is a bit lame.
|
||||
setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
|
||||
}
|
||||
|
||||
/** Set the values of this round rectangle to be the same as those
|
||||
* of the argument.
|
||||
* @param rr The round rectangle to copy
|
||||
*/
|
||||
public void setRoundRect(RoundRectangle2D rr)
|
||||
{
|
||||
setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
|
||||
rr.getArcWidth(), rr.getArcHeight());
|
||||
}
|
||||
|
||||
/** A subclass of RoundRectangle which keeps its parameters as
|
||||
* doubles. */
|
||||
public static class Double extends RoundRectangle2D
|
||||
{
|
||||
/** The height of the corner arc. */
|
||||
public double archeight;
|
||||
|
||||
/** The width of the corner arc. */
|
||||
public double arcwidth;
|
||||
|
||||
/** The x coordinate of this object. */
|
||||
public double x;
|
||||
|
||||
/** The y coordinate of this object. */
|
||||
public double y;
|
||||
|
||||
/** The width of this object. */
|
||||
public double width;
|
||||
|
||||
/** The height of this object. */
|
||||
public double height;
|
||||
|
||||
/** Construct a new instance, with all parameters set to 0. */
|
||||
public Double()
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a new instance with the given arguments.
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
* @param w The width
|
||||
* @param h The height
|
||||
* @param arcWidth The arc width
|
||||
* @param arcHeight The arc height
|
||||
*/
|
||||
public Double(double x, double y, double w, double h, double arcWidth,
|
||||
double arcHeight)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
this.arcwidth = arcWidth;
|
||||
this.archeight = arcHeight;
|
||||
}
|
||||
|
||||
public double getArcHeight()
|
||||
{
|
||||
return archeight;
|
||||
}
|
||||
|
||||
public double getArcWidth()
|
||||
{
|
||||
return arcwidth;
|
||||
}
|
||||
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
return new Rectangle2D.Double(x, y, width, height);
|
||||
}
|
||||
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public double getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
public double getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return width <= 0 || height <= 0;
|
||||
}
|
||||
|
||||
public void setRoundRect(double x, double y, double w, double h,
|
||||
double arcWidth, double arcHeight)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
this.arcwidth = arcWidth;
|
||||
this.archeight = arcHeight;
|
||||
}
|
||||
} // class Double
|
||||
|
||||
/** A subclass of RoundRectangle which keeps its parameters as
|
||||
* floats. */
|
||||
public static class Float extends RoundRectangle2D
|
||||
{
|
||||
/** The height of the corner arc. */
|
||||
public float archeight;
|
||||
|
||||
/** The width of the corner arc. */
|
||||
public float arcwidth;
|
||||
|
||||
/** The x coordinate of this object. */
|
||||
public float x;
|
||||
|
||||
/** The y coordinate of this object. */
|
||||
public float y;
|
||||
|
||||
/** The width of this object. */
|
||||
public float width;
|
||||
|
||||
/** The height of this object. */
|
||||
public float height;
|
||||
|
||||
/** Construct a new instance, with all parameters set to 0. */
|
||||
public Float()
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a new instance with the given arguments.
|
||||
* @param x The x coordinate
|
||||
* @param y The y coordinate
|
||||
* @param w The width
|
||||
* @param h The height
|
||||
* @param arcWidth The arc width
|
||||
* @param arcHeight The arc height
|
||||
*/
|
||||
public Float(float x, float y, float w, float h, float arcWidth,
|
||||
float arcHeight)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
this.arcwidth = arcWidth;
|
||||
this.archeight = arcHeight;
|
||||
}
|
||||
|
||||
public double getArcHeight()
|
||||
{
|
||||
return archeight;
|
||||
}
|
||||
|
||||
public double getArcWidth()
|
||||
{
|
||||
return arcwidth;
|
||||
}
|
||||
|
||||
public Rectangle2D getBounds2D()
|
||||
{
|
||||
return new Rectangle2D.Float(x, y, width, height);
|
||||
}
|
||||
|
||||
public double getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public double getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
public double getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return width <= 0 || height <= 0;
|
||||
}
|
||||
|
||||
public void setRoundRect(float x, float y, float w, float h,
|
||||
float arcWidth, float arcHeight)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
this.arcwidth = arcWidth;
|
||||
this.archeight = arcHeight;
|
||||
}
|
||||
|
||||
public void setRoundRect(double x, double y, double w, double h,
|
||||
double arcWidth, double arcHeight)
|
||||
{
|
||||
this.x = (float) x;
|
||||
this.y = (float) y;
|
||||
this.width = (float) w;
|
||||
this.height = (float) h;
|
||||
this.arcwidth = (float) arcWidth;
|
||||
this.archeight = (float) arcHeight;
|
||||
}
|
||||
} // class Float
|
||||
} // class RoundRectangle2D
|
||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,481 @@
|
||||
<?xml version="1.0" encoding="US-ASCII"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>The GNU Implementation of java.awt.geom.FlatteningPathIterator</title>
|
||||
<meta name="author" content="Sascha Brawer" />
|
||||
<style type="text/css"><!--
|
||||
td { white-space: nowrap; }
|
||||
li { margin: 2mm 0; }
|
||||
--></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>The GNU Implementation of FlatteningPathIterator</h1>
|
||||
|
||||
<p><i><a href="http://www.dandelis.ch/people/brawer/">Sascha
|
||||
Brawer</a>, November 2003</i></p>
|
||||
|
||||
<p>This document describes the GNU implementation of the class
|
||||
<code>java.awt.geom.FlatteningPathIterator</code>. It does
|
||||
<em>not</em> describe how a programmer should use this class; please
|
||||
refer to the generated API documentation for this purpose. Instead, it
|
||||
is intended for maintenance programmers who want to understand the
|
||||
implementation, for example because they want to extend the class or
|
||||
fix a bug.</p>
|
||||
|
||||
|
||||
<h2>Data Structures</h2>
|
||||
|
||||
<p>The algorithm uses a stack. Its allocation is delayed to the time
|
||||
when the source path iterator actually returns the first curved
|
||||
segment (either <code>SEG_QUADTO</code> or <code>SEG_CUBICTO</code>).
|
||||
If the input path does not contain any curved segments, the value of
|
||||
the <code>stack</code> variable stays <code>null</code>. In this quite
|
||||
common case, the memory consumption is minimal.</p>
|
||||
|
||||
<dl><dt><code>stack</code></dt><dd>The variable <code>stack</code> is
|
||||
a <code>double</code> array that holds the start, control and end
|
||||
points of individual sub-segments.</dd>
|
||||
|
||||
<dt><code>recLevel</code></dt><dd>The variable <code>recLevel</code>
|
||||
holds how many recursive sub-divisions were needed to calculate a
|
||||
segment. The original curve has recursion level 0. For each
|
||||
sub-division, the corresponding recursion level is increased by
|
||||
one.</dd>
|
||||
|
||||
<dt><code>stackSize</code></dt><dd>Finally, the variable
|
||||
<code>stackSize</code> indicates how many sub-segments are stored on
|
||||
the stack.</dd></dl>
|
||||
|
||||
<h2>Algorithm</h2>
|
||||
|
||||
<p>The implementation separately processes each segment that the
|
||||
base iterator returns.</p>
|
||||
|
||||
<p>In the case of <code>SEG_CLOSE</code>,
|
||||
<code>SEG_MOVETO</code> and <code>SEG_LINETO</code> segments, the
|
||||
implementation simply hands the segment to the consumer, without actually
|
||||
doing anything.</p>
|
||||
|
||||
<p>Any <code>SEG_QUADTO</code> and <code>SEG_CUBICTO</code> segments
|
||||
need to be flattened. Flattening is performed with a fixed-sized
|
||||
stack, holding the coordinates of subdivided segments. When the base
|
||||
iterator returns a <code>SEG_QUADTO</code> and
|
||||
<code>SEG_CUBICTO</code> segments, it is recursively flattened as
|
||||
follows:</p>
|
||||
|
||||
<ol><li>Intialization: Allocate memory for the stack (unless a
|
||||
sufficiently large stack has been allocated previously). Push the
|
||||
original quadratic or cubic curve onto the stack. Mark that segment as
|
||||
having a <code>recLevel</code> of zero.</li>
|
||||
|
||||
<li>If the stack is empty, flattening the segment is complete,
|
||||
and the next segment is fetched from the base iterator.</li>
|
||||
|
||||
<li>If the stack is not empty, pop a curve segment from the
|
||||
stack.
|
||||
|
||||
<ul><li>If its <code>recLevel</code> exceeds the recursion limit,
|
||||
hand the current segment to the consumer.</li>
|
||||
|
||||
<li>Calculate the squared flatness of the segment. If it smaller
|
||||
than <code>flatnessSq</code>, hand the current segment to the
|
||||
consumer.</li>
|
||||
|
||||
<li>Otherwise, split the segment in two halves. Push the right
|
||||
half onto the stack. Then, push the left half onto the stack.
|
||||
Continue with step two.</li></ul></li>
|
||||
</ol>
|
||||
|
||||
<p>The implementation is slightly complicated by the fact that
|
||||
consumers <em>pull</em> the flattened segments from the
|
||||
<code>FlatteningPathIterator</code>. This means that we actually
|
||||
cannot “hand the curent segment over to the consumer.”
|
||||
But the algorithm is easier to understand if one assumes a
|
||||
<em>push</em> paradigm.</p>
|
||||
|
||||
|
||||
<h2>Example</h2>
|
||||
|
||||
<p>The following example shows how a
|
||||
<code>FlatteningPathIterator</code> processes a
|
||||
<code>SEG_QUADTO</code> segment. It is (arbitrarily) assumed that the
|
||||
recursion limit was set to 2.</p>
|
||||
|
||||
<blockquote>
|
||||
<table border="1" cellspacing="0" cellpadding="8">
|
||||
<tr align="center" valign="baseline">
|
||||
<th></th><th>A</th><th>B</th><th>C</th>
|
||||
<th>D</th><th>E</th><th>F</th><th>G</th><th>H</th>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[0]</code></th>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td><i>S<sub>ll</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[1]</code></th>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td><i>S<sub>ll</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[2]</code></th>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td><i>C<sub>ll</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[3]</code></th>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td><i>C<sub>ll</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[4]</code></th>
|
||||
<td>—</td>
|
||||
<td><i>S<sub>l</sub>.x</i></td>
|
||||
<td><i>E<sub>ll</sub>.x</i>
|
||||
= <i>S<sub>lr</sub>.x</i></td>
|
||||
<td><i>S<sub>lr</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
<td><i>S<sub>rl</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[5]</code></th>
|
||||
<td>—</td>
|
||||
<td><i>S<sub>l</sub>.y</i></td>
|
||||
<td><i>E<sub>ll</sub>.x</i>
|
||||
= <i>S<sub>lr</sub>.y</i></td>
|
||||
<td><i>S<sub>lr</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
<td><i>S<sub>rl</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[6]</code></th>
|
||||
<td>—</td>
|
||||
<td><i>C<sub>l</sub>.x</i></td>
|
||||
<td><i>C<sub>lr</sub>.x</i></td>
|
||||
<td><i>C<sub>lr</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
<td><i>C<sub>rl</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[7]</code></th>
|
||||
<td>—</td>
|
||||
<td><i>C<sub>l</sub>.y</i></td>
|
||||
<td><i>C<sub>lr</sub>.y</i></td>
|
||||
<td><i>C<sub>lr</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
<td><i>C<sub>rl</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[8]</code></th>
|
||||
<td><i>S.x</i></td>
|
||||
<td><i>E<sub>l</sub>.x</i>
|
||||
= <i>S<sub>r</sub>.x</i></td>
|
||||
<td><i>E<sub>lr</sub>.x</i>
|
||||
= <i>S<sub>r</sub>.x</i></td>
|
||||
<td><i>E<sub>lr</sub>.x</i>
|
||||
= <i>S<sub>r</sub>.x</i></td>
|
||||
<td><i>S<sub>r</sub>.x</i></td>
|
||||
<td><i>E<sub>rl</sub>.x</i>
|
||||
= <i>S<sub>rr</sub>.x</i></td>
|
||||
<td><i>S<sub>rr</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[9]</code></th>
|
||||
<td><i>S.y</i></td>
|
||||
<td><i>E<sub>l</sub>.y</i>
|
||||
= <i>S<sub>r</sub>.y</i></td>
|
||||
<td><i>E<sub>lr</sub>.y</i>
|
||||
= <i>S<sub>r</sub>.y</i></td>
|
||||
<td><i>E<sub>lr</sub>.y</i>
|
||||
= <i>S<sub>r</sub>.y</i></td>
|
||||
<td><i>S<sub>r</sub>.y</i></td>
|
||||
<td><i>E<sub>rl</sub>.y</i>
|
||||
= <i>S<sub>rr</sub>.y</i></td>
|
||||
<td><i>S<sub>rr</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[10]</code></th>
|
||||
<td><i>C.x</i></td>
|
||||
<td><i>C<sub>r</sub>.x</i></td>
|
||||
<td><i>C<sub>r</sub>.x</i></td>
|
||||
<td><i>C<sub>r</sub>.x</i></td>
|
||||
<td><i>C<sub>r</sub>.x</i></td>
|
||||
<td><i>C<sub>rr</sub>.x</i></td>
|
||||
<td><i>C<sub>rr</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[11]</code></th>
|
||||
<td><i>C.y</i></td>
|
||||
<td><i>C<sub>r</sub>.y</i></td>
|
||||
<td><i>C<sub>r</sub>.y</i></td>
|
||||
<td><i>C<sub>r</sub>.y</i></td>
|
||||
<td><i>C<sub>r</sub>.y</i></td>
|
||||
<td><i>C<sub>rr</sub>.y</i></td>
|
||||
<td><i>C<sub>rr</sub>.y</i></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[12]</code></th>
|
||||
<td><i>E.x</i></td>
|
||||
<td><i>E<sub>r</sub>.x</i></td>
|
||||
<td><i>E<sub>r</sub>.x</i></td>
|
||||
<td><i>E<sub>r</sub>.x</i></td>
|
||||
<td><i>E<sub>r</sub>.x</i></td>
|
||||
<td><i>E<sub>rr</sub>.x</i></td>
|
||||
<td><i>E<sub>rr</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stack[13]</code></th>
|
||||
<td><i>E.y</i></td>
|
||||
<td><i>E<sub>r</sub>.y</i></td>
|
||||
<td><i>E<sub>r</sub>.y</i></td>
|
||||
<td><i>E<sub>r</sub>.y</i></td>
|
||||
<td><i>E<sub>r</sub>.y</i></td>
|
||||
<td><i>E<sub>rr</sub>.y</i></td>
|
||||
<td><i>E<sub>rr</sub>.x</i></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>stackSize</code></th>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>2</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>1</td>
|
||||
<td>0</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>recLevel[2]</code></th>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>2</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>recLevel[1]</code></th>
|
||||
<td>—</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>2</td>
|
||||
<td>—</td>
|
||||
<td>2</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr align="center" valign="baseline">
|
||||
<th><code>recLevel[0]</code></th>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>2</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
|
||||
<ol>
|
||||
|
||||
<li>The data structures are initialized as follows.
|
||||
|
||||
<ul><li>The segment’s end point <i>E</i>, control point
|
||||
<i>C</i>, and start point <i>S</i> are pushed onto the stack.</li>
|
||||
|
||||
<li>Currently, the curve in the stack would be approximated by one
|
||||
single straight line segment (<i>S</i> – <i>E</i>).
|
||||
Therefore, <code>stackSize</code> is set to 1.</li>
|
||||
|
||||
<li>This single straight line segment is approximating the original
|
||||
curve, which can be seen as the result of zero recursive
|
||||
splits. Therefore, <code>recLevel[0]</code> is set to
|
||||
zero.</li></ul>
|
||||
|
||||
Column A shows the state after the initialization step.</li>
|
||||
|
||||
<li>The algorithm proceeds by taking the topmost curve segment
|
||||
(<i>S</i> – <i>C</i> – <i>E</i>) from the stack.
|
||||
|
||||
<ul><li>The recursion level of this segment (stored in
|
||||
<code>recLevel[0]</code>) is zero, which is smaller than
|
||||
the limit 2.</li>
|
||||
|
||||
<li>The method <code>java.awt.geom.QuadCurve2D.getFlatnessSq</code>
|
||||
is called to calculate the squared flatness.</li>
|
||||
|
||||
<li>For the sake of argument, we assume that the squared flatness is
|
||||
exceeding the threshold stored in <code>flatnessSq</code>. Thus, the
|
||||
curve segment <i>S</i> – <i>C</i> – <i>E</i> gets
|
||||
subdivided into a left and a right half, namely
|
||||
<i>S<sub>l</sub></i> – <i>C<sub>l</sub></i> –
|
||||
<i>E<sub>l</sub></i> and <i>S<sub>r</sub></i> –
|
||||
<i>C<sub>r</sub></i> – <i>E<sub>r</sub></i>. Both halves are
|
||||
pushed onto the stack, so the left half is now on top.
|
||||
|
||||
<br /> <br />The left half starts at the same point
|
||||
as the original curve, so <i>S<sub>l</sub></i> has the same
|
||||
coordinates as <i>S</i>. Similarly, the end point of the right
|
||||
half and of the original curve are identical
|
||||
(<i>E<sub>r</sub></i> = <i>E</i>). More interestingly, the left
|
||||
half ends where the right half starts. Because
|
||||
<i>E<sub>l</sub></i> = <i>S<sub>r</sub></i>, their coordinates need
|
||||
to be stored only once, which amounts to saving 16 bytes (two
|
||||
<code>double</code> values) for each iteration.</li></ul>
|
||||
|
||||
Column B shows the state after the first iteration.</li>
|
||||
|
||||
<li>Again, the topmost curve segment (<i>S<sub>l</sub></i>
|
||||
– <i>C<sub>l</sub></i> – <i>E<sub>l</sub></i>) is
|
||||
taken from the stack.
|
||||
|
||||
<ul><li>The recursion level of this segment (stored in
|
||||
<code>recLevel[1]</code>) is 1, which is smaller than
|
||||
the limit 2.</li>
|
||||
|
||||
<li>The method <code>java.awt.geom.QuadCurve2D.getFlatnessSq</code>
|
||||
is called to calculate the squared flatness.</li>
|
||||
|
||||
<li>Assuming that the segment is still not considered
|
||||
flat enough, it gets subdivided into a left
|
||||
(<i>S<sub>ll</sub></i> – <i>C<sub>ll</sub></i> –
|
||||
<i>E<sub>ll</sub></i>) and a right (<i>S<sub>lr</sub></i>
|
||||
– <i>C<sub>lr</sub></i> – <i>E<sub>lr</sub></i>)
|
||||
half.</li></ul>
|
||||
|
||||
Column C shows the state after the second iteration.</li>
|
||||
|
||||
<li>The topmost curve segment (<i>S<sub>ll</sub></i> –
|
||||
<i>C<sub>ll</sub></i> – <i>E<sub>ll</sub></i>) is popped from
|
||||
the stack.
|
||||
|
||||
<ul><li>The recursion level of this segment (stored in
|
||||
<code>recLevel[2]</code>) is 2, which is <em>not</em> smaller than
|
||||
the limit 2. Therefore, a <code>SEG_LINETO</code> (from
|
||||
<i>S<sub>ll</sub></i> to <i>E<sub>ll</sub></i>) is passed to the
|
||||
consumer.</li></ul>
|
||||
|
||||
The new state is shown in column D.</li>
|
||||
|
||||
|
||||
<li>The topmost curve segment (<i>S<sub>lr</sub></i> –
|
||||
<i>C<sub>lr</sub></i> – <i>E<sub>lr</sub></i>) is popped from
|
||||
the stack.
|
||||
|
||||
<ul><li>The recursion level of this segment (stored in
|
||||
<code>recLevel[1]</code>) is 2, which is <em>not</em> smaller than
|
||||
the limit 2. Therefore, a <code>SEG_LINETO</code> (from
|
||||
<i>S<sub>lr</sub></i> to <i>E<sub>lr</sub></i>) is passed to the
|
||||
consumer.</li></ul>
|
||||
|
||||
The new state is shown in column E.</li>
|
||||
|
||||
<li>The algorithm proceeds by taking the topmost curve segment
|
||||
(<i>S<sub>r</sub></i> – <i>C<sub>r</sub></i> –
|
||||
<i>E<sub>r</sub></i>) from the stack.
|
||||
|
||||
<ul><li>The recursion level of this segment (stored in
|
||||
<code>recLevel[0]</code>) is 1, which is smaller than
|
||||
the limit 2.</li>
|
||||
|
||||
<li>The method <code>java.awt.geom.QuadCurve2D.getFlatnessSq</code>
|
||||
is called to calculate the squared flatness.</li>
|
||||
|
||||
<li>For the sake of argument, we again assume that the squared
|
||||
flatness is exceeding the threshold stored in
|
||||
<code>flatnessSq</code>. Thus, the curve segment
|
||||
(<i>S<sub>r</sub></i> – <i>C<sub>r</sub></i> –
|
||||
<i>E<sub>r</sub></i>) is subdivided into a left and a right half,
|
||||
namely
|
||||
<i>S<sub>rl</sub></i> – <i>C<sub>rl</sub></i> –
|
||||
<i>E<sub>rl</sub></i> and <i>S<sub>rr</sub></i> –
|
||||
<i>C<sub>rr</sub></i> – <i>E<sub>rr</sub></i>. Both halves
|
||||
are pushed onto the stack.</li></ul>
|
||||
|
||||
The new state is shown in column F.</li>
|
||||
|
||||
<li>The topmost curve segment (<i>S<sub>rl</sub></i> –
|
||||
<i>C<sub>rl</sub></i> – <i>E<sub>rl</sub></i>) is popped from
|
||||
the stack.
|
||||
|
||||
<ul><li>The recursion level of this segment (stored in
|
||||
<code>recLevel[2]</code>) is 2, which is <em>not</em> smaller than
|
||||
the limit 2. Therefore, a <code>SEG_LINETO</code> (from
|
||||
<i>S<sub>rl</sub></i> to <i>E<sub>rl</sub></i>) is passed to the
|
||||
consumer.</li></ul>
|
||||
|
||||
The new state is shown in column G.</li>
|
||||
|
||||
<li>The topmost curve segment (<i>S<sub>rr</sub></i> –
|
||||
<i>C<sub>rr</sub></i> – <i>E<sub>rr</sub></i>) is popped from
|
||||
the stack.
|
||||
|
||||
<ul><li>The recursion level of this segment (stored in
|
||||
<code>recLevel[2]</code>) is 2, which is <em>not</em> smaller than
|
||||
the limit 2. Therefore, a <code>SEG_LINETO</code> (from
|
||||
<i>S<sub>rr</sub></i> to <i>E<sub>rr</sub></i>) is passed to the
|
||||
consumer.</li></ul>
|
||||
|
||||
The new state is shown in column H.</li>
|
||||
|
||||
<li>The stack is now empty. The FlatteningPathIterator will fetch the
|
||||
next segment from the base iterator, and process it.</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p>In order to split the most recently pushed segment, the
|
||||
<code>subdivideQuadratic()</code> method passes <code>stack</code>
|
||||
directly to
|
||||
<code>QuadCurve2D.subdivide(double[],int,double[],int,double[],int)</code>.
|
||||
Because the stack grows towards the beginning of the array, no data
|
||||
needs to be copied around: <code>subdivide</code> will directly store
|
||||
the result into the stack, which will have the contents shown to the
|
||||
right.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<!-- package.html - describes classes in java.awt.geom package.
|
||||
Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. -->
|
||||
|
||||
<html>
|
||||
<head><title>GNU Classpath - java.awt.geom</title></head>
|
||||
|
||||
<body>
|
||||
<p>Classes to represent 2D objects and different path transformations.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||