| /* BasicStroke.java -- | |
| Copyright (C) 2002, 2003, 2004, 2005, 2006 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. */ | |
| package java.awt; | |
| import gnu.java.awt.java2d.CubicSegment; | |
| import gnu.java.awt.java2d.LineSegment; | |
| import gnu.java.awt.java2d.QuadSegment; | |
| import gnu.java.awt.java2d.Segment; | |
| import java.awt.geom.GeneralPath; | |
| import java.awt.geom.PathIterator; | |
| import java.awt.geom.Point2D; | |
| import java.util.Arrays; | |
| /** | |
| * A general purpose {@link Stroke} implementation that can represent a wide | |
| * variety of line styles for use with subclasses of {@link Graphics2D}. | |
| * <p> | |
| * The line cap and join styles can be set using the options illustrated | |
| * here: | |
| * <p> | |
| * <img src="doc-files/capjoin.png" width="350" height="180" | |
| * alt="Illustration of line cap and join styles" /> | |
| * <p> | |
| * A dash array can be used to specify lines with alternating opaque and | |
| * transparent sections. | |
| */ | |
| public class BasicStroke implements Stroke | |
| { | |
| /** | |
| * Indicates a mitered line join style. See the class overview for an | |
| * illustration. | |
| */ | |
| public static final int JOIN_MITER = 0; | |
| /** | |
| * Indicates a rounded line join style. See the class overview for an | |
| * illustration. | |
| */ | |
| public static final int JOIN_ROUND = 1; | |
| /** | |
| * Indicates a bevelled line join style. See the class overview for an | |
| * illustration. | |
| */ | |
| public static final int JOIN_BEVEL = 2; | |
| /** | |
| * Indicates a flat line cap style. See the class overview for an | |
| * illustration. | |
| */ | |
| public static final int CAP_BUTT = 0; | |
| /** | |
| * Indicates a rounded line cap style. See the class overview for an | |
| * illustration. | |
| */ | |
| public static final int CAP_ROUND = 1; | |
| /** | |
| * Indicates a square line cap style. See the class overview for an | |
| * illustration. | |
| */ | |
| public static final int CAP_SQUARE = 2; | |
| /** The stroke width. */ | |
| private final float width; | |
| /** The line cap style. */ | |
| private final int cap; | |
| /** The line join style. */ | |
| private final int join; | |
| /** The miter limit. */ | |
| private final float limit; | |
| /** The dash array. */ | |
| private final float[] dash; | |
| /** The dash phase. */ | |
| private final float phase; | |
| // The inner and outer paths of the stroke | |
| private Segment start, end; | |
| /** | |
| * Creates a new <code>BasicStroke</code> instance with the given attributes. | |
| * | |
| * @param width the line width (>= 0.0f). | |
| * @param cap the line cap style (one of {@link #CAP_BUTT}, | |
| * {@link #CAP_ROUND} or {@link #CAP_SQUARE}). | |
| * @param join the line join style (one of {@link #JOIN_ROUND}, | |
| * {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}). | |
| * @param miterlimit the limit to trim the miter join. The miterlimit must be | |
| * greater than or equal to 1.0f. | |
| * @param dash The array representing the dashing pattern. There must be at | |
| * least one non-zero entry. | |
| * @param dashPhase is negative and dash is not null. | |
| * | |
| * @throws IllegalArgumentException If one input parameter doesn't meet | |
| * its needs. | |
| */ | |
| public BasicStroke(float width, int cap, int join, float miterlimit, | |
| float[] dash, float dashPhase) | |
| { | |
| if (width < 0.0f ) | |
| throw new IllegalArgumentException("width " + width + " < 0"); | |
| else if (cap < CAP_BUTT || cap > CAP_SQUARE) | |
| throw new IllegalArgumentException("cap " + cap + " out of range [" | |
| + CAP_BUTT + ".." + CAP_SQUARE + "]"); | |
| else if (miterlimit < 1.0f && join == JOIN_MITER) | |
| throw new IllegalArgumentException("miterlimit " + miterlimit | |
| + " < 1.0f while join == JOIN_MITER"); | |
| else if (join < JOIN_MITER || join > JOIN_BEVEL) | |
| throw new IllegalArgumentException("join " + join + " out of range [" | |
| + JOIN_MITER + ".." + JOIN_BEVEL | |
| + "]"); | |
| else if (dashPhase < 0.0f && dash != null) | |
| throw new IllegalArgumentException("dashPhase " + dashPhase | |
| + " < 0.0f while dash != null"); | |
| else if (dash != null) | |
| if (dash.length == 0) | |
| throw new IllegalArgumentException("dash.length is 0"); | |
| else | |
| { | |
| boolean allZero = true; | |
| for ( int i = 0; i < dash.length; ++i) | |
| { | |
| if (dash[i] != 0.0f) | |
| { | |
| allZero = false; | |
| break; | |
| } | |
| } | |
| if (allZero) | |
| throw new IllegalArgumentException("all dashes are 0.0f"); | |
| } | |
| this.width = width; | |
| this.cap = cap; | |
| this.join = join; | |
| limit = miterlimit; | |
| this.dash = dash == null ? null : (float[]) dash.clone(); | |
| phase = dashPhase; | |
| } | |
| /** | |
| * Creates a new <code>BasicStroke</code> instance with the given attributes. | |
| * | |
| * @param width the line width (>= 0.0f). | |
| * @param cap the line cap style (one of {@link #CAP_BUTT}, | |
| * {@link #CAP_ROUND} or {@link #CAP_SQUARE}). | |
| * @param join the line join style (one of {@link #JOIN_ROUND}, | |
| * {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}). | |
| * @param miterlimit the limit to trim the miter join. The miterlimit must be | |
| * greater than or equal to 1.0f. | |
| * | |
| * @throws IllegalArgumentException If one input parameter doesn't meet | |
| * its needs. | |
| */ | |
| public BasicStroke(float width, int cap, int join, float miterlimit) | |
| { | |
| this(width, cap, join, miterlimit, null, 0); | |
| } | |
| /** | |
| * Creates a new <code>BasicStroke</code> instance with the given attributes. | |
| * The miter limit defaults to <code>10.0</code>. | |
| * | |
| * @param width the line width (>= 0.0f). | |
| * @param cap the line cap style (one of {@link #CAP_BUTT}, | |
| * {@link #CAP_ROUND} or {@link #CAP_SQUARE}). | |
| * @param join the line join style (one of {@link #JOIN_ROUND}, | |
| * {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}). | |
| * | |
| * @throws IllegalArgumentException If one input parameter doesn't meet | |
| * its needs. | |
| */ | |
| public BasicStroke(float width, int cap, int join) | |
| { | |
| this(width, cap, join, 10, null, 0); | |
| } | |
| /** | |
| * Creates a new <code>BasicStroke</code> instance with the given line | |
| * width. The default values are: | |
| * <ul> | |
| * <li>line cap style: {@link #CAP_SQUARE};</li> | |
| * <li>line join style: {@link #JOIN_MITER};</li> | |
| * <li>miter limit: <code>10.0f</code>. | |
| * </ul> | |
| * | |
| * @param width the line width (>= 0.0f). | |
| * | |
| * @throws IllegalArgumentException If <code>width</code> is negative. | |
| */ | |
| public BasicStroke(float width) | |
| { | |
| this(width, CAP_SQUARE, JOIN_MITER, 10, null, 0); | |
| } | |
| /** | |
| * Creates a new <code>BasicStroke</code> instance. The default values are: | |
| * <ul> | |
| * <li>line width: <code>1.0f</code>;</li> | |
| * <li>line cap style: {@link #CAP_SQUARE};</li> | |
| * <li>line join style: {@link #JOIN_MITER};</li> | |
| * <li>miter limit: <code>10.0f</code>. | |
| * </ul> | |
| */ | |
| public BasicStroke() | |
| { | |
| this(1, CAP_SQUARE, JOIN_MITER, 10, null, 0); | |
| } | |
| /** | |
| * Creates a shape representing the stroked outline of the given shape. | |
| * THIS METHOD IS NOT YET IMPLEMENTED. | |
| * | |
| * @param s the shape. | |
| */ | |
| public Shape createStrokedShape(Shape s) | |
| { | |
| PathIterator pi = s.getPathIterator(null); | |
| if( dash == null ) | |
| return solidStroke( pi ); | |
| return dashedStroke( pi ); | |
| } | |
| /** | |
| * Returns the line width. | |
| * | |
| * @return The line width. | |
| */ | |
| public float getLineWidth() | |
| { | |
| return width; | |
| } | |
| /** | |
| * Returns a code indicating the line cap style (one of {@link #CAP_BUTT}, | |
| * {@link #CAP_ROUND}, {@link #CAP_SQUARE}). | |
| * | |
| * @return A code indicating the line cap style. | |
| */ | |
| public int getEndCap() | |
| { | |
| return cap; | |
| } | |
| /** | |
| * Returns a code indicating the line join style (one of {@link #JOIN_BEVEL}, | |
| * {@link #JOIN_MITER} or {@link #JOIN_ROUND}). | |
| * | |
| * @return A code indicating the line join style. | |
| */ | |
| public int getLineJoin() | |
| { | |
| return join; | |
| } | |
| /** | |
| * Returns the miter limit. | |
| * | |
| * @return The miter limit. | |
| */ | |
| public float getMiterLimit() | |
| { | |
| return limit; | |
| } | |
| /** | |
| * Returns the dash array, which defines the length of alternate opaque and | |
| * transparent sections in lines drawn with this stroke. If | |
| * <code>null</code>, a continuous line will be drawn. | |
| * | |
| * @return The dash array (possibly <code>null</code>). | |
| */ | |
| public float[] getDashArray() | |
| { | |
| return dash; | |
| } | |
| /** | |
| * Returns the dash phase for the stroke. This is the offset from the start | |
| * of a path at which the pattern defined by {@link #getDashArray()} is | |
| * rendered. | |
| * | |
| * @return The dash phase. | |
| */ | |
| public float getDashPhase() | |
| { | |
| return phase; | |
| } | |
| /** | |
| * Returns the hash code for this object. The hash is calculated by | |
| * xoring the hash, cap, join, limit, dash array and phase values | |
| * (converted to <code>int</code> first with | |
| * <code>Float.floatToIntBits()</code> if the value is a | |
| * <code>float</code>). | |
| * | |
| * @return The hash code. | |
| */ | |
| public int hashCode() | |
| { | |
| int hash = Float.floatToIntBits(width); | |
| hash ^= cap; | |
| hash ^= join; | |
| hash ^= Float.floatToIntBits(limit); | |
| if (dash != null) | |
| for (int i = 0; i < dash.length; i++) | |
| hash ^= Float.floatToIntBits(dash[i]); | |
| hash ^= Float.floatToIntBits(phase); | |
| return hash; | |
| } | |
| /** | |
| * Compares this <code>BasicStroke</code> for equality with an arbitrary | |
| * object. This method returns <code>true</code> if and only if: | |
| * <ul> | |
| * <li><code>o</code> is an instanceof <code>BasicStroke</code>;</li> | |
| * <li>this object has the same width, line cap style, line join style, | |
| * miter limit, dash array and dash phase as <code>o</code>.</li> | |
| * </ul> | |
| * | |
| * @param o the object (<code>null</code> permitted). | |
| * | |
| * @return <code>true</code> if this stroke is equal to <code>o</code> and | |
| * <code>false</code> otherwise. | |
| */ | |
| public boolean equals(Object o) | |
| { | |
| if (! (o instanceof BasicStroke)) | |
| return false; | |
| BasicStroke s = (BasicStroke) o; | |
| return width == s.width && cap == s.cap && join == s.join | |
| && limit == s.limit && Arrays.equals(dash, s.dash) && phase == s.phase; | |
| } | |
| private Shape solidStroke(PathIterator pi) | |
| { | |
| double[] coords = new double[6]; | |
| double x, y, x0, y0; | |
| boolean pathOpen = false; | |
| GeneralPath output = new GeneralPath( ); | |
| Segment[] p; | |
| x = x0 = y = y0 = 0; | |
| while( !pi.isDone() ) | |
| { | |
| switch( pi.currentSegment(coords) ) | |
| { | |
| case PathIterator.SEG_MOVETO: | |
| x0 = x = coords[0]; | |
| y0 = y = coords[1]; | |
| if( pathOpen ) | |
| { | |
| capEnds(); | |
| convertPath(output, start); | |
| start = end = null; | |
| pathOpen = false; | |
| } | |
| break; | |
| case PathIterator.SEG_LINETO: | |
| p = (new LineSegment(x, y, coords[0], coords[1])). | |
| getDisplacedSegments(width/2.0); | |
| if( !pathOpen ) | |
| { | |
| start = p[0]; | |
| end = p[1]; | |
| pathOpen = true; | |
| } | |
| else | |
| addSegments(p); | |
| x = coords[0]; | |
| y = coords[1]; | |
| break; | |
| case PathIterator.SEG_QUADTO: | |
| p = (new QuadSegment(x, y, coords[0], coords[1], coords[2], | |
| coords[3])).getDisplacedSegments(width/2.0); | |
| if( !pathOpen ) | |
| { | |
| start = p[0]; | |
| end = p[1]; | |
| pathOpen = true; | |
| } | |
| else | |
| addSegments(p); | |
| x = coords[2]; | |
| y = coords[3]; | |
| break; | |
| case PathIterator.SEG_CUBICTO: | |
| p = new CubicSegment(x, y, coords[0], coords[1], | |
| coords[2], coords[3], | |
| coords[4], coords[5]).getDisplacedSegments(width/2.0); | |
| if( !pathOpen ) | |
| { | |
| start = p[0]; | |
| end = p[1]; | |
| pathOpen = true; | |
| } | |
| else | |
| addSegments(p); | |
| x = coords[4]; | |
| y = coords[5]; | |
| break; | |
| case PathIterator.SEG_CLOSE: | |
| if (x == x0 && y == y0) | |
| { | |
| joinSegments(new Segment[] { start.first, end.first }); | |
| } | |
| else | |
| { | |
| p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width / 2.0); | |
| addSegments(p); | |
| } | |
| convertPath(output, start); | |
| convertPath(output, end); | |
| start = end = null; | |
| pathOpen = false; | |
| output.setWindingRule(GeneralPath.WIND_EVEN_ODD); | |
| break; | |
| } | |
| pi.next(); | |
| } | |
| if( pathOpen ) | |
| { | |
| capEnds(); | |
| convertPath(output, start); | |
| } | |
| return output; | |
| } | |
| private Shape dashedStroke(PathIterator pi) | |
| { | |
| GeneralPath out = new GeneralPath(); | |
| return out; | |
| } | |
| /** | |
| * Cap the ends of the path (joining the start and end list of segments) | |
| */ | |
| private void capEnds() | |
| { | |
| Segment returnPath = end.last; | |
| end.reverseAll(); // reverse the path. | |
| end = null; | |
| capEnd(start, returnPath); | |
| start.last = returnPath.last; | |
| end = null; | |
| capEnd(start, start); | |
| } | |
| /** | |
| * Append the Segments in s to the GeneralPath p | |
| */ | |
| private void convertPath(GeneralPath p, Segment s) | |
| { | |
| Segment v = s; | |
| p.moveTo((float)s.P1.getX(), (float)s.P1.getY()); | |
| do | |
| { | |
| if(v instanceof LineSegment) | |
| p.lineTo((float)v.P2.getX(), (float)v.P2.getY()); | |
| else if(v instanceof QuadSegment) | |
| p.quadTo((float)((QuadSegment)v).cp.getX(), | |
| (float)((QuadSegment)v).cp.getY(), | |
| (float)v.P2.getX(), | |
| (float)v.P2.getY()); | |
| else if(v instanceof CubicSegment) | |
| p.curveTo((float)((CubicSegment)v).cp1.getX(), | |
| (float)((CubicSegment)v).cp1.getY(), | |
| (float)((CubicSegment)v).cp2.getX(), | |
| (float)((CubicSegment)v).cp2.getY(), | |
| (float)v.P2.getX(), | |
| (float)v.P2.getY()); | |
| v = v.next; | |
| } while(v != s && v != null); | |
| p.closePath(); | |
| } | |
| /** | |
| * Add the segments to start and end (the inner and outer edges of the stroke) | |
| */ | |
| private void addSegments(Segment[] segments) | |
| { | |
| joinSegments(segments); | |
| start.add(segments[0]); | |
| end.add(segments[1]); | |
| } | |
| private void joinSegments(Segment[] segments) | |
| { | |
| double[] p0 = start.last.cp2(); | |
| double[] p1 = new double[]{start.last.P2.getX(), start.last.P2.getY()}; | |
| double[] p2 = new double[]{segments[0].first.P1.getX(), segments[0].first.P1.getY()}; | |
| double[] p3 = segments[0].cp1(); | |
| Point2D p; | |
| p = lineIntersection(p0[0],p0[1],p1[0],p1[1], | |
| p2[0],p2[1],p3[0],p3[1], false); | |
| double det = (p1[0] - p0[0])*(p3[1] - p2[1]) - | |
| (p3[0] - p2[0])*(p1[1] - p0[1]); | |
| if( det > 0 ) | |
| { | |
| // start and segment[0] form the 'inner' part of a join, | |
| // connect the overlapping segments | |
| joinInnerSegments(start, segments[0], p); | |
| joinOuterSegments(end, segments[1], p); | |
| } | |
| else | |
| { | |
| // end and segment[1] form the 'inner' part | |
| joinInnerSegments(end, segments[1], p); | |
| joinOuterSegments(start, segments[0], p); | |
| } | |
| } | |
| /** | |
| * Make a cap between a and b segments, | |
| * where a-->b is the direction of iteration. | |
| */ | |
| private void capEnd(Segment a, Segment b) | |
| { | |
| double[] p0, p1; | |
| double dx, dy, l; | |
| Point2D c1,c2; | |
| switch( cap ) | |
| { | |
| case CAP_BUTT: | |
| a.add(new LineSegment(a.last.P2, b.P1)); | |
| break; | |
| case CAP_SQUARE: | |
| p0 = a.last.cp2(); | |
| p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; | |
| dx = p1[0] - p0[0]; | |
| dy = p1[1] - p0[1]; | |
| l = Math.sqrt(dx * dx + dy * dy); | |
| dx = 0.5*width*dx/l; | |
| dy = 0.5*width*dy/l; | |
| c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); | |
| c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy); | |
| a.add(new LineSegment(a.last.P2, c1)); | |
| a.add(new LineSegment(c1, c2)); | |
| a.add(new LineSegment(c2, b.P1)); | |
| break; | |
| case CAP_ROUND: | |
| p0 = a.last.cp2(); | |
| p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; | |
| dx = p1[0] - p0[0]; | |
| dy = p1[1] - p0[1]; | |
| l = Math.sqrt(dx * dx + dy * dy); | |
| dx = (2.0/3.0)*width*dx/l; | |
| dy = (2.0/3.0)*width*dy/l; | |
| c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); | |
| c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy); | |
| a.add(new CubicSegment(a.last.P2, c1, c2, b.P1)); | |
| break; | |
| } | |
| a.add(b); | |
| } | |
| /** | |
| * Returns the intersection of two lines, or null if there isn't one. | |
| * @param infinite - true if the lines should be regarded as infinite, false | |
| * if the intersection must be within the given segments. | |
| * @return a Point2D or null. | |
| */ | |
| private Point2D lineIntersection(double X1, double Y1, | |
| double X2, double Y2, | |
| double X3, double Y3, | |
| double X4, double Y4, | |
| boolean infinite) | |
| { | |
| double x1 = X1; | |
| double y1 = Y1; | |
| double rx = X2 - x1; | |
| double ry = Y2 - y1; | |
| double x2 = X3; | |
| double y2 = Y3; | |
| double sx = X4 - x2; | |
| double sy = Y4 - y2; | |
| double determinant = sx * ry - sy * rx; | |
| double nom = (sx * (y2 - y1) + sy * (x1 - x2)); | |
| // lines can be considered parallel. | |
| if (Math.abs(determinant) < 1E-6) | |
| return null; | |
| nom = nom / determinant; | |
| // check if lines are within the bounds | |
| if(!infinite && (nom > 1.0 || nom < 0.0)) | |
| return null; | |
| return new Point2D.Double(x1 + nom * rx, y1 + nom * ry); | |
| } | |
| /** | |
| * Join a and b segments, where a-->b is the direction of iteration. | |
| * | |
| * insideP is the inside intersection point of the join, needed for | |
| * calculating miter lengths. | |
| */ | |
| private void joinOuterSegments(Segment a, Segment b, Point2D insideP) | |
| { | |
| double[] p0, p1; | |
| double dx, dy, l; | |
| Point2D c1,c2; | |
| switch( join ) | |
| { | |
| case JOIN_MITER: | |
| p0 = a.last.cp2(); | |
| p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; | |
| double[] p2 = new double[]{b.P1.getX(), b.P1.getY()}; | |
| double[] p3 = b.cp1(); | |
| Point2D p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], true); | |
| if( p == null || insideP == null ) | |
| a.add(new LineSegment(a.last.P2, b.P1)); | |
| else if((p.distance(insideP)/width) < limit) | |
| { | |
| a.add(new LineSegment(a.last.P2, p)); | |
| a.add(new LineSegment(p, b.P1)); | |
| } | |
| else | |
| { | |
| // outside miter limit, do a bevel join. | |
| a.add(new LineSegment(a.last.P2, b.P1)); | |
| } | |
| break; | |
| case JOIN_ROUND: | |
| p0 = a.last.cp2(); | |
| p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; | |
| dx = p1[0] - p0[0]; | |
| dy = p1[1] - p0[1]; | |
| l = Math.sqrt(dx * dx + dy * dy); | |
| dx = 0.5*width*dx/l; | |
| dy = 0.5*width*dy/l; | |
| c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); | |
| p0 = new double[]{b.P1.getX(), b.P1.getY()}; | |
| p1 = b.cp1(); | |
| dx = p0[0] - p1[0]; // backwards direction. | |
| dy = p0[1] - p1[1]; | |
| l = Math.sqrt(dx * dx + dy * dy); | |
| dx = 0.5*width*dx/l; | |
| dy = 0.5*width*dy/l; | |
| c2 = new Point2D.Double(p0[0] + dx, p0[1] + dy); | |
| a.add(new CubicSegment(a.last.P2, c1, c2, b.P1)); | |
| break; | |
| case JOIN_BEVEL: | |
| a.add(new LineSegment(a.last.P2, b.P1)); | |
| break; | |
| } | |
| } | |
| /** | |
| * Join a and b segments, removing any overlap | |
| */ | |
| private void joinInnerSegments(Segment a, Segment b, Point2D p) | |
| { | |
| double[] p0 = a.last.cp2(); | |
| double[] p1 = new double[] { a.last.P2.getX(), a.last.P2.getY() }; | |
| double[] p2 = new double[] { b.P1.getX(), b.P1.getY() }; | |
| double[] p3 = b.cp1(); | |
| if (p == null) | |
| { | |
| // Dodgy. | |
| a.add(new LineSegment(a.last.P2, b.P1)); | |
| p = new Point2D.Double((b.P1.getX() + a.last.P2.getX()) / 2.0, | |
| (b.P1.getY() + a.last.P2.getY()) / 2.0); | |
| } | |
| else | |
| // This assumes segments a and b are single segments, which is | |
| // incorrect - if they are a linked list of segments (ie, passed in | |
| // from a flattening operation), this produces strange results!! | |
| a.last.P2 = b.P1 = p; | |
| } | |
| } |