Copyright Derek O'Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.
Stroking is the process of drawing a shape's outline.
Java provides a BasicStroke, which allows you to adjust a line's thickness, end shape and whether it is dashed or not. A BasicStroke object holds information about the line width, join style, end-cap style, and dash style.
BasicStroke(float width, // width of the line
int cap, // end of line segment CAP_BUTT, CAP_ROUND or CAP_SQUARE
int join, // between line segments JOIN_BEVEL, JOIN_MITER or JOIN_ROUND
float miterlimit, //
float[] dash // an array showing the size of dashes and gaps
float dash_phase) // an index into dash, stateing start of sequence
There are various other, simplier, BasicStroke constructors.
BasicStrokeDemo Example: (Run Applet)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Stroke_BasicStroke_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel implements ActionListener
{
private final float THICK_LINE = 30.0f;
private final float THIN_LINE = 10.0f;
private final float dash[] =
{
30.0f, 50.0f
}; // interval for dashed lines
private int cap = BasicStroke.CAP_SQUARE;
private int join = BasicStroke.JOIN_ROUND;
private float lineThickness = THICK_LINE;
private boolean isDashed = false; // dashed or solid line
private final JMenuItem capButt = new JMenuItem("CAP_BUTT");
private final JMenuItem capRound = new JMenuItem("CAP_ROUND");
private final JMenuItem capSquare = new JMenuItem("CAP_SQUARE");
private final JMenuItem joinMiter = new JMenuItem("JOIN_MITER");
private final JMenuItem joinBevel = new JMenuItem("JOIN_BEVEL");
private final JMenuItem joinRound = new JMenuItem("JOIN_ROUND");
private final JCheckBoxMenuItem dashed = new JCheckBoxMenuItem("Dashed", this.isDashed);
private final JCheckBoxMenuItem thickLine = new JCheckBoxMenuItem("Thick Lines", true);
public View()
{
super();
final JMenuBar menuBar = new JMenuBar();
final JMenu capMenu = new JMenu("CAP");
final JMenu joinMenu = new JMenu("JOIN");
final JMenu otherMenu = new JMenu("Other");
setJMenuBar(menuBar);
menuBar.add(capMenu);
menuBar.add(joinMenu);
menuBar.add(otherMenu);
capMenu.add(capButt);
capMenu.add(capRound);
capMenu.add(capSquare);
joinMenu.add(this.joinMiter);
joinMenu.add(this.joinBevel);
joinMenu.add(this.joinRound);
otherMenu.add(this.dashed);
otherMenu.add(this.thickLine);
this.capButt.addActionListener(this);
this.capRound.addActionListener(this);
this.capSquare.addActionListener(this);
this.joinBevel.addActionListener(this);
this.joinMiter.addActionListener(this);
this.joinRound.addActionListener(this);
this.dashed.addActionListener(this);
this.thickLine.addActionListener(this);
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
BasicStroke basicStroke = null;
if (this.isDashed)
{
basicStroke = new BasicStroke(this.lineThickness, this.cap, this.join, 1.0f, dash, 0.0f);
}
else
{
basicStroke = new BasicStroke(this.lineThickness, this.cap, this.join);
}
g2.setStroke(basicStroke);
g2.drawLine(30, 30, 150, 30);
g2.drawLine(150, 30, 30, 150);
g2.drawRect(200, 30, 250, 100);
}
@Override
public void actionPerformed(ActionEvent e)
{
final Object source = e.getSource();
if (source == this.capButt)
{
this.cap = BasicStroke.CAP_BUTT;
}
else if (source == this.capRound)
{
this.cap = BasicStroke.CAP_ROUND;
}
else if (source == this.capSquare)
{
this.cap = BasicStroke.CAP_SQUARE;
}
else if (source == this.joinBevel)
{
this.join = BasicStroke.JOIN_BEVEL;
}
else if (source == this.joinMiter)
{
this.join = BasicStroke.JOIN_MITER;
}
else if (source == this.joinRound)
{
this.join = BasicStroke.JOIN_ROUND;
}
else if (source == this.thickLine)
{
if (this.thickLine.isSelected())
{
this.lineThickness = THICK_LINE;
}
else
{
this.lineThickness = THIN_LINE;
}
}
else if (source == this.dashed)
{
this.isDashed = this.dashed.isSelected();
}
this.repaint();
}
}
}
Textures can be placed onto strokes, as shown in the example below.
Stroke_Texture_Demo Example: (Run Applet)
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
public class Stroke_Texture_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Image image = new ImageIcon(getClass().getClassLoader().getResource("images/smiley.jpg")).getImage();
final Graphics2D g2 = (Graphics2D) g;
// texture
final int textureWidth = 30;
final int textureHeight = 30;
final BufferedImage textureImg = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_RGB);
final Graphics2D textureG = textureImg.createGraphics();
textureG.drawImage(image, 0, 0, textureWidth, textureHeight, this);
final Rectangle rectangle = new Rectangle(0, 0, textureWidth, textureHeight);
final TexturePaint texturePaint = new TexturePaint(textureImg, rectangle);
g2.setPaint(texturePaint);
// stroke
final BasicStroke stroke = new BasicStroke(15f);
g2.setStroke(stroke);
g2.drawArc(30, 50, 250, 100, 180, -360);
}
}
}
Gradients can be placed onto strokes, as shown in the example below.
Stroke_Gradient_Demo Example: (Run Applet)
import java.awt.*;
import javax.swing.*;
public class Stroke_Gradient_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
// gradient
final GradientPaint gradient = new GradientPaint(0, 0, Color.yellow, getWidth(), getHeight(), Color.blue);
g2.setPaint(gradient);
// stroke
final BasicStroke stroke = new BasicStroke(15f);
g2.setStroke(stroke);
g2.drawArc(30, 50, 250, 100, 180, -360);
}
}
}
Java provides a Stroke interface. This comes with one method:
public Shape createStrokedShape(Shape shape)
This method returns a Shape that can be used as a stroke.
Stroke_Composite_Demo Example: (Run Applet)
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Stroke_Composite_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
final CompositeStroke compositeStroke = new CompositeStroke(new BasicStroke(15f), new BasicStroke(5.0f));
g2.setStroke(compositeStroke);
g2.drawArc(30, 50, 250, 100, 180, -180);
g2.drawArc(30, 90, 250, 100, 180, 180);
}
}
public class CompositeStroke implements Stroke
{
private Stroke mainStroke;
private Stroke minusStroke;
public CompositeStroke(Stroke mainStroke, Stroke minusStroke)
{
this.mainStroke = mainStroke;
this.minusStroke = minusStroke;
}
@Override
public Shape createStrokedShape(Shape shape)
{
final Area mainArea = new Area(this.mainStroke.createStrokedShape(shape));
final Area minusArea = new Area(this.minusStroke.createStrokedShape(shape));
mainArea.subtract(minusArea);
return mainArea;
}
}
}
Stroke_Pattern_Demo Example: (Run Applet)
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Stroke_Pattern_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
// set up the shapeStroke
final Rectangle rectangle = new Rectangle(0, 0, 20, 20);
final Ellipse2D ellipse = new Ellipse2D.Float(0, 0, 20, 50);
final Shape shape[] = new Shape[]
{
rectangle, ellipse
};
final PatternStroke shapeStroke = new PatternStroke(shape, 15.0f);
g2.setStroke(shapeStroke);
g2.drawArc(30, 50, 250, 100, 180, -180);
g2.drawArc(30, 90, 250, 100, 180, 180);
}
}
public class PatternStroke implements Stroke
{
private float resolution; // the distance between shapes
private boolean repeat = true;
private final Shape shapes[];
private final AffineTransform affineTransform = new AffineTransform();
public PatternStroke(Shape shapes[], float resolution)
{
this.resolution = resolution; // the distance between shapes
this.shapes = new Shape[shapes.length];
for (int i = 0; i < this.shapes.length; i++)
{
final Rectangle2D bounds = shapes[i].getBounds2D();
this.affineTransform.setToTranslation(-bounds.getCenterX(), -bounds.getCenterY());
this.shapes[i] = this.affineTransform.createTransformedShape(shapes[i]);
}
}
@Override
public Shape createStrokedShape(Shape shape)
{
final GeneralPath result = new GeneralPath();
final PathIterator pathIterator = new FlatteningPathIterator(shape.getPathIterator(null), 1.0);
float points[] = new float[2];
float moveX = 0;
float moveY = 0;
float curX = 0;
float curY = 0;
float nextX = 0;
float nextY = 0;
float distanceToNext = 0;
final int length = this.shapes.length;
int segmentType = 0;
int currentPos = 0;
while ((currentPos < length) && (!pathIterator.isDone()))
{
segmentType = pathIterator.currentSegment(points);
switch (segmentType)
{
case PathIterator.SEG_MOVETO: // starting location for a new subpath
{
moveX = nextX = points[0];
moveY = nextY = points[1];
result.moveTo(moveX, moveY);
distanceToNext = 0;
break;
}
case PathIterator.SEG_LINETO: // reached end of a subpath
{
curX = points[0];
curY = points[1];
// each subpath needs to be closed, so do not break here
// instead fall into SEG_CLOSE code below
}
case PathIterator.SEG_CLOSE: // close off the subpath
{
points[0] = moveX;
points[1] = moveY;
final float dx = curX - nextX;
final float dy = curY - nextY;
final float distance = (float) (Math.sqrt((dx * dx) + (dy * dy)));
if (distance >= distanceToNext)
{
final float ratio = 1.0f / distance;
final float angle = (float) (Math.atan2(dy, dx));
while ((currentPos < length) && (distance >= distanceToNext))
{
final float x = nextX + distanceToNext * dx * ratio;
final float y = nextY + distanceToNext * dy * ratio;
this.affineTransform.setToTranslation(x, y);
this.affineTransform.rotate(angle);
result.append(this.affineTransform.createTransformedShape(this.shapes[currentPos]), false);
distanceToNext += this.resolution;
currentPos++;
if (this.repeat)
{
currentPos %= length;
}
}
}
distanceToNext -= distance;
nextX = curX;
nextY = curY;
break;
}
}
pathIterator.next();
}
return result;
}
}
}
Stroke_CompositeWithPattern_Demo Example: (Run Applet)
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Stroke_CompositeWithPattern_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
final Rectangle rectangle = new Rectangle(0, 0, 15, 15);
final Ellipse2D ellipse = new Ellipse2D.Float(0, 0, 15, 15);
final Shape shape[] = new Shape[]
{
rectangle, ellipse
};
final PatternStroke shapeStroke = new PatternStroke(shape, 20.0f);
final CompositeStroke compositeStroke = new CompositeStroke(new BasicStroke(25f), shapeStroke);
g2.setStroke(compositeStroke);
g2.drawArc(30, 50, 250, 100, 180, -180);
g2.drawArc(30, 90, 250, 100, 180, 180);
}
}
public class CompositeStroke implements Stroke
{
private Stroke mainStroke;
private Stroke minusStroke;
public CompositeStroke(Stroke mainStroke, Stroke minusStroke)
{
this.mainStroke = mainStroke;
this.minusStroke = minusStroke;
}
public Shape createStrokedShape(Shape shape)
{
final Area mainArea = new Area(this.mainStroke.createStrokedShape(shape));
final Area minusArea = new Area(this.minusStroke.createStrokedShape(shape));
mainArea.subtract(minusArea);
return mainArea;
}
}
public class PatternStroke implements Stroke
{
private float resolution; // the distance between shapes
private boolean repeat = true;
private final Shape shapes[];
private final AffineTransform affineTransform = new AffineTransform();
public PatternStroke(Shape shapes[], float resolution)
{
this.resolution = resolution; // the distance between shapes
this.shapes = new Shape[shapes.length];
for (int i = 0; i < this.shapes.length; i++)
{
final Rectangle2D bounds = shapes[i].getBounds2D();
this.affineTransform.setToTranslation(-bounds.getCenterX(), -bounds.getCenterY());
this.shapes[i] = this.affineTransform.createTransformedShape(shapes[i]);
}
}
public Shape createStrokedShape(Shape shape)
{
final GeneralPath result = new GeneralPath();
final PathIterator pathIterator = new FlatteningPathIterator(shape.getPathIterator(null), 1.0);
float points[] = new float[2];
float moveX = 0;
float moveY = 0;
float curX = 0;
float curY = 0;
float nextX = 0;
float nextY = 0;
float distanceToNext = 0;
int length = this.shapes.length;
int segmentType = 0;
int currentPos = 0;
while ((currentPos < length) && (!pathIterator.isDone()))
{
segmentType = pathIterator.currentSegment(points);
switch (segmentType)
{
case PathIterator.SEG_MOVETO: // starting location for a new subpath
{
moveX = nextX = points[0];
moveY = nextY = points[1];
result.moveTo(moveX, moveY);
distanceToNext = 0;
break;
}
case PathIterator.SEG_LINETO: // reached end of a subpath
{
curX = points[0];
curY = points[1];
// each subpath needs to be closed, so do not break here
// instead fall into SEG_CLOSE code below
}
case PathIterator.SEG_CLOSE: // close off the subpath
{
points[0] = moveX;
points[1] = moveY;
final float dx = curX - nextX;
final float dy = curY - nextY;
final float distance = (float) (Math.sqrt((dx * dx) + (dy * dy)));
if (distance >= distanceToNext)
{
final float ratio = 1.0f / distance;
final float angle = (float) (Math.atan2(dy, dx));
while ((currentPos < length) && (distance >= distanceToNext))
{
final float x = nextX + distanceToNext * dx * ratio;
final float y = nextY + distanceToNext * dy * ratio;
this.affineTransform.setToTranslation(x, y);
this.affineTransform.rotate(angle);
result.append(this.affineTransform.createTransformedShape(this.shapes[currentPos]), false);
distanceToNext += this.resolution;
currentPos++;
if (this.repeat)
{
currentPos %= length;
}
}
}
distanceToNext -= distance;
nextX = curX;
nextY = curY;
break;
}
}
pathIterator.next();
}
return result;
}
}
}
Stroke_Rough_Demo Example: (Run Applet)
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Stroke_Rough_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new RoughStroke(20, 10, 2));
g2.drawArc(30, 50, 250, 100, 180, -180);
g2.drawArc(30, 90, 250, 100, 180, 180);
}
}
public class RoughStroke implements Stroke
{
private int strokeWidth;
private float resolution;
private float amplitude;
public RoughStroke(int strokeSize, float resolution, float amplitude)
{
this.strokeWidth = strokeSize;
this.resolution = resolution;
this.amplitude = amplitude;
}
public Shape createStrokedShape(Shape shape)
{
shape = new BasicStroke(this.strokeWidth).createStrokedShape(shape);
final GeneralPath result = new GeneralPath();
final PathIterator pathIterator = new FlatteningPathIterator(shape.getPathIterator(null), 1.0);
float points[] = new float[2];
float moveX = 0;
float moveY = 0;
float curX = 0;
float curY = 0;
float nextX = 0;
float nextY = 0;
float distanceToNext = 0;
int segmentType = 0;
while (!pathIterator.isDone())
{
segmentType = pathIterator.currentSegment(points);
switch (segmentType)
{
case PathIterator.SEG_MOVETO: // starting location for a new subpath
{
moveX = nextX = randomize(points[0]);
moveY = nextY = randomize(points[1]);
result.moveTo(moveX, moveY);
distanceToNext = 0;
break;
}
case PathIterator.SEG_LINETO:
{
curX = randomize(points[0]);
curY = randomize(points[1]);
// each subpath needs to be closed, so do not break here
// instead fall into SEG_CLOSE code below
}
case PathIterator.SEG_CLOSE: //
{
points[0] = moveX;
points[1] = moveY;
final float dx = curX - nextX;
final float dy = curY - nextY;
final float distance = (float) (Math.sqrt(dx * dx + dy * dy));
if (distance >= distanceToNext)
{
final float ratio = 1.0f / distance;
while (distance >= distanceToNext)
{
final float x = nextX + distanceToNext * dx * ratio;
final float y = nextY + distanceToNext * dy * ratio;
result.lineTo(randomize(x), randomize(y));
distanceToNext += this.resolution;
}
}
distanceToNext -= distance;
nextX = curX;
nextY = curY;
break;
}
}
pathIterator.next();
}
return result;
}
private float randomize(float x)
{
return x + (float) (Math.random() * this.amplitude * 2 - 1);
}
}
}
Stroke_String_Demo Example: (Run Applet)
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
public class Stroke_String_Demo extends JApplet
{
@Override
public void init()
{
this.setContentPane(new View());
}
public class View extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new StringStroke("Dundalk I.T.", new Font("Times New Roman", Font.BOLD + Font.ITALIC, 50), StringStroke.ALIGN_CENTER));
g2.drawArc(30, 50, 250, 100, 180, -180);
g2.setStroke(new StringStroke("DkIT", new Font("Times New Roman", Font.BOLD, 30), StringStroke.REPEAT));
g2.drawArc(30, 90, 250, 100, 180, 180);
}
}
public class StringStroke implements Stroke
{
public final static int ALIGN_LEFT = 0;
public final static int ALIGN_RIGHT = 1;
public final static int ALIGN_CENTER = 2;
public final static int REPEAT = 3;
public final static int STRETCH = 4;
private int layout = 0;
private String text;
private Font font;
public StringStroke(String text, Font font, int layout)
{
this.text = text;
this.font = font;
this.layout = layout;
if (this.layout == StringStroke.REPEAT)
{
this.text += " "; // place gap between repeating text
}
else if ((this.layout == StringStroke.ALIGN_CENTER))
{
this.text = " " + this.text + " ";
}
else if ((this.layout == StringStroke.ALIGN_RIGHT))
{
this.text = " " + this.text;
}
}
public Shape createStrokedShape(Shape shape)
{
final FontRenderContext fontRenderContext = new FontRenderContext(null, true, true);
final GlyphVector glyphVector = this.font.createGlyphVector(fontRenderContext, this.text);
final GeneralPath result = new GeneralPath();
int length = glyphVector.getNumGlyphs();
if (length == 0) // string length == 0
{
return result;
}
final PathIterator pathIterator = new FlatteningPathIterator(shape.getPathIterator(null), 1.0);
final AffineTransform affineTransform = new AffineTransform();
float points[] = new float[2];
float moveX = 0;
float moveY = 0;
float curX = 0;
float curY = 0;
float nextX = 0;
float nextY = 0;
float distanceToNext = 0;
int segmentType = 0;
int curCharacterPos = 0;
// calculate the length of the path
final float pathLength = measurePathLength(shape);
final double stringLength = glyphVector.getLogicalBounds().getWidth();
float factor;
if (this.layout == StringStroke.STRETCH)
{
factor = pathLength / (float) stringLength;
}
else
{
factor = 1.0f;
}
float offset = 0;
if (this.layout == StringStroke.ALIGN_CENTER)
{
offset = (pathLength - (float) stringLength) / 2.0f;
}
else if (this.layout == StringStroke.ALIGN_RIGHT)
{
offset = (pathLength - (float) stringLength);
}
float nextAdvance = 0;
while ((curCharacterPos < length) && (!pathIterator.isDone()))
{
segmentType = pathIterator.currentSegment(points);
switch (segmentType)
{
case PathIterator.SEG_MOVETO: // starting location for a new subpath
{
curX = points[0];
curY = points[1];
moveX = curX;
moveY = curY;
result.moveTo(moveX, moveY);
nextAdvance = glyphVector.getGlyphMetrics(curCharacterPos).getAdvance() * 0.5f;
distanceToNext = nextAdvance;
break;
}
case PathIterator.SEG_LINETO: // reached end of a subpath
{
nextX = points[0];
nextY = points[1];
// each subpath needs to be closed, so do not break here
// instead fall into SEG_CLOSE code below
}
case PathIterator.SEG_CLOSE: // close off the subpath
{
points[0] = moveX;
points[1] = moveY;
final float dx = nextX - curX;
final float dy = nextY - curY;
final float distance = (float) Math.sqrt(dx * dx + dy * dy);
if (distance >= distanceToNext)
{
final float ratio = 1.0f / distance;
final float angle = (float) Math.atan2(dy, dx);
while ((curCharacterPos < length) && (distance >= distanceToNext))
{
final Shape glyph = glyphVector.getGlyphOutline(curCharacterPos);
final Point2D p = glyphVector.getGlyphPosition(curCharacterPos);
final float px = (float) p.getX();
final float py = (float) p.getY();
final float x = curX + distanceToNext * dx * ratio;
final float y = curY + distanceToNext * dy * ratio;
float advance = nextAdvance;
if ((curCharacterPos < length - 1))
{
nextAdvance = glyphVector.getGlyphMetrics(curCharacterPos + 1).getAdvance() * 0.5f;
}
else
{
nextAdvance = 0.0f;
}
// allow for offset to CENTER or RIGHT
if (curCharacterPos == 0)
{
advance += offset;
}
affineTransform.setToTranslation(x, y);
affineTransform.rotate(angle);
affineTransform.translate(-px - advance, -py);
result.append(affineTransform.createTransformedShape(glyph), false);
distanceToNext += (advance + nextAdvance) * factor;
curCharacterPos++;
if ((this.layout == StringStroke.REPEAT) && (curCharacterPos == length))
{
curCharacterPos = 0;
nextAdvance = glyphVector.getGlyphMetrics(curCharacterPos).getAdvance() * 0.5f;
}
}
}
distanceToNext -= distance;
curX = nextX;
curY = nextY;
break;
}
}
pathIterator.next();
}
return result;
}
public float measurePathLength(Shape shape)
{
final PathIterator pathIterator = new FlatteningPathIterator(shape.getPathIterator(null), 1.0);
float points[] = new float[2];
float moveX = 0;
float moveY = 0;
float nextX = 0;
float nextY = 0;
float curX = 0;
float curY = 0;
float total = 0;
int segmentType = 0;
while (!pathIterator.isDone())
{
segmentType = pathIterator.currentSegment(points);
switch (segmentType)
{
case PathIterator.SEG_MOVETO: // starting location for a new subpath
{
moveX = nextX = points[0];
moveY = nextY = points[1];
break;
}
case PathIterator.SEG_LINETO: // reached end of a subpath
{
curX = points[0];
curY = points[1];
// each subpath needs to be closed, so do not break here
// instead fall into SEG_CLOSE code below
}
case PathIterator.SEG_CLOSE: // close off the subpath
{
points[0] = moveX;
points[1] = moveY;
final float dx = curX - nextX;
final float dy = curY - nextY;
total += (float) Math.sqrt((dx * dx) + (dy * dy));
nextX = curX;
nextY = curY;
break;
}
}
pathIterator.next();
}
return total;
}
}
}
Copyright Derek O' Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.