Initial revision

From-SVN: r102074
This commit is contained in:
Tom Tromey
2005-07-16 00:30:23 +00:00
parent 6f4434b39b
commit f911ba985a
4557 changed files with 1000262 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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&#xe9;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
* &#x2019;up&#x2019;
* direction, one in the &#x2019;down&#x2019; direction) Point <b>B</b> in
* the image is inside (one intersection &#x2019;down&#x2019;)
* Point <b>C</b> in the image is outside (two intersections
* &#x2019;down&#x2019;)
*
* @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&#x2019;s current winding rule.
*/
public int getWindingRule()
{
return rule;
}
/**
* Sets the path&#x2019;s winding rule, which controls which areas are
* considered &#x2019;inside&#x2019; or &#x2019;outside&#x2019; 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&#x2019;s bounding box.
*/
public Rectangle getBounds()
{
return getBounds2D().getBounds();
}
/**
* Returns the path&#x2019;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);
}
}
File diff suppressed because it is too large Load Diff
@@ -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 &lt;= t &lt;= 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 &lt;= t &lt;= 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
File diff suppressed because it is too large Load Diff
@@ -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 &lt; r.x).
*
* @see #outcode(double, double)
*/
public static final int OUT_LEFT = 1;
/**
* The point lies above the rectangle (p.y &lt; r.y).
*
* @see #outcode(double, double)
*/
public static final int OUT_TOP = 2;
/**
* The point lies right of the rectangle (p.x &gt; r.maxX).
*
* @see #outcode(double, double)
*/
public static final int OUT_RIGHT = 4;
/**
* The point lies below of the rectangle (p.y &gt; 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 &gt;&gt; 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
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

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 &#x201c;hand the curent segment over to the consumer.&#x201d;
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>&#x2014;</td>
<td>&#x2014;</td>
<td><i>S<sub>ll</sub>.x</i></td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>stack[1]</code></th>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td><i>S<sub>ll</sub>.y</i></td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>stack[2]</code></th>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td><i>C<sub>ll</sub>.x</i></td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>stack[3]</code></th>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td><i>C<sub>ll</sub>.y</i></td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>stack[4]</code></th>
<td>&#x2014;</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>&#x2014;</td>
<td><i>S<sub>rl</sub>.x</i></td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>stack[5]</code></th>
<td>&#x2014;</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>&#x2014;</td>
<td><i>S<sub>rl</sub>.y</i></td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>stack[6]</code></th>
<td>&#x2014;</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>&#x2014;</td>
<td><i>C<sub>rl</sub>.x</i></td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>stack[7]</code></th>
<td>&#x2014;</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>&#x2014;</td>
<td><i>C<sub>rl</sub>.y</i></td>
<td>&#x2014;</td>
<td>&#x2014;</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>&#x2014;</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>&#x2014;</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>&#x2014;</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>&#x2014;</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>&#x2014;</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>&#x2014;</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>&#x2014;</td>
<td>&#x2014;</td>
<td>2</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
<td>&#x2014;</td>
</tr>
<tr align="center" valign="baseline">
<th><code>recLevel[1]</code></th>
<td>&#x2014;</td>
<td>1</td>
<td>2</td>
<td>2</td>
<td>&#x2014;</td>
<td>2</td>
<td>&#x2014;</td>
<td>&#x2014;</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>&#x2014;</td>
</tr>
</table>
</blockquote>
<ol>
<li>The data structures are initialized as follows.
<ul><li>The segment&#x2019;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> &#x2013; <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> &#x2013; <i>C</i> &#x2013; <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> &#x2013; <i>C</i> &#x2013; <i>E</i> gets
subdivided into a left and a right half, namely
<i>S<sub>l</sub></i> &#x2013; <i>C<sub>l</sub></i> &#x2013;
<i>E<sub>l</sub></i> and <i>S<sub>r</sub></i> &#x2013;
<i>C<sub>r</sub></i> &#x2013; <i>E<sub>r</sub></i>. Both halves are
pushed onto the stack, so the left half is now on top.
<br />&nbsp;<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>
&#x2013; <i>C<sub>l</sub></i> &#x2013; <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> &#x2013; <i>C<sub>ll</sub></i> &#x2013;
<i>E<sub>ll</sub></i>) and a right (<i>S<sub>lr</sub></i>
&#x2013; <i>C<sub>lr</sub></i> &#x2013; <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> &#x2013;
<i>C<sub>ll</sub></i> &#x2013; <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> &#x2013;
<i>C<sub>lr</sub></i> &#x2013; <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> &#x2013; <i>C<sub>r</sub></i> &#x2013;
<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> &#x2013; <i>C<sub>r</sub></i> &#x2013;
<i>E<sub>r</sub></i>) is subdivided into a left and a right half,
namely
<i>S<sub>rl</sub></i> &#x2013; <i>C<sub>rl</sub></i> &#x2013;
<i>E<sub>rl</sub></i> and <i>S<sub>rr</sub></i> &#x2013;
<i>C<sub>rr</sub></i> &#x2013; <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> &#x2013;
<i>C<sub>rl</sub></i> &#x2013; <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> &#x2013;
<i>C<sub>rr</sub></i> &#x2013; <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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

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>