Petra said:
yes, that is really easy if you want simple strokes, maybe with
different widths, or a dash-pattern. But, if you want a stroke with a
texture on it (that is not easily tileable) or sth. like a wiggly line
that's not that easy. Or to be exact, I don't know how to do it.
Draw a GeneralPath between the two endpoints, using a custom
PathIterator. The PathIterator describes the 'wiggly' aspect of the
line. The texture will do what it is supposed to do.
The key to making this simple is to understand how AffineTransform
works. By using an AffineTransform, you can treat every line as though
it starts at 0,0 and is horizontal. This greatly simplifies the math
needed to create the 'wiggles'.
The quickly thrown together example below draws a wiggly line between
two endpoints selected by mouse click. The line is filled via some
TexturePaint code copied from the Java Tutorial.
The heart of this example is the WigglePathIterator class. An instance
is created using two line end points, an amplitude and a period. The
line passed in is normalized via the AffineTransform, which allows the
next() method to compute segments as though the line being drawn is
horizontal with an end point at 0,0.
The 'y' coordinates repeat the following pattern:
1. Move from center up to 1/2 amplitude
2. Move down from 1/2 amplitude to center
3. Move down from center to -1/2 amplitude
4. Move up from -1/2 amplitude to center.
The 'x' coordinate simply indexes by 1/4 period for each segment.
When the x coordinate reaches the line length calculated in the
constructor, the isDone() method returns true.
The magic happens in the currentSegment() methods. The coordinate array
is first populated with the next position. Then the AffineTransform
(created in the constructor) is used to transform the coordinates in the
array back to the coordinate system of the original line.
Jim S.
===============================================
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;
public class WiggleTest {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new WigglePanel(10,50), BorderLayout.CENTER);
f.setSize(800,600);
f.setVisible(true);
}
private static class WigglePanel extends JPanel {
private Point lineStart;
private Point lineEnd;
private Point currentPoint;
private double amplitude;
private double period;
WigglePanel(double amplitude, double period) {
this.amplitude = amplitude;
this.period = period;
initConnections();
}
private void initConnections() {
MouseInputAdapter mia = new MouseInputAdapter() {
public void mouseClicked(MouseEvent me) {
if (lineEnd != null || lineStart == null) {
lineStart = me.getPoint();
lineEnd = null;
} else {
lineEnd = me.getPoint();
}
repaint();
}
public void mouseMoved(MouseEvent me) {
currentPoint = me.getPoint();
repaint();
}
};
addMouseListener(mia);
addMouseMotionListener(mia);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
/* Begin code copied from Java Tutorial */
BufferedImage bi = new BufferedImage(5, 5,
BufferedImage.TYPE_INT_RGB);
Graphics2D big = bi.createGraphics();
big.setColor(Color.blue);
big.fillRect(0, 0, 5, 5);
big.setColor(Color.lightGray);
big.fillOval(0, 0, 5, 5);
Rectangle r = new Rectangle(0,0,5,5);
g2d.setPaint(new TexturePaint(bi, r));
/* End code copied from Java Tutorial */
g2d.setStroke(new BasicStroke(5.0f));
if (lineStart != null && lineEnd != null) {
GeneralPath gp = new GeneralPath();
gp.moveTo((float)lineStart.getX(),
(float)lineStart.getY());
gp.append(new WigglePathIterator(
lineStart, lineEnd, amplitude, period), false);
g2d.draw(gp);
} else if (lineStart != null && currentPoint != null) {
g2d.draw(new Line2D.Double(lineStart, currentPoint));
}
}
}
private static class WigglePathIterator implements PathIterator {
double x0, x1, y0, y1;
double amplitude, period;
boolean rising;
Point2D currentSeg;
AffineTransform at;
double length = 0;
WigglePathIterator(
Point start,
Point end,
double amplitude,
double period) {
x0 = start.getX();
x1 = end.getX();
y0 = start.getY();
y1 = end.getY();
this.amplitude = amplitude;
this.period = period;
currentSeg = new Point2D.Double(0,0);
length = Math.sqrt(Math.pow(x1-x0,2) + Math.pow(y1-y0,2));
double angle = Math.atan((y1-y0)/(x1-x0));
//normalize the angle value for
//the (-x,-y) and (-x,+y) quadrants
if (x1 < x0) {
if (y1 > y0) {
angle = Math.PI + angle;
} else {
angle = angle - Math.PI;
}
}
at = AffineTransform.getTranslateInstance(x0,y0);
at.rotate(angle);
}
public int getWindingRule() {
return PathIterator.WIND_NON_ZERO;
}
public boolean isDone() {
return currentSeg.getX() > length;
}
public void next() {
Point2D oldSeg = currentSeg;
double nextX = 0;
double nextY = 0;
if (oldSeg.getY() != 0) {
nextY = 0;
} else if (rising) {
nextY = amplitude / 2;
rising = false;
} else {
nextY = -(amplitude / 2);
rising = true;
}
nextX = oldSeg.getX() + period / 4;
currentSeg.setLocation(nextX, nextY);
}
public int currentSegment(float[] coords) {
coords[0] = (float)currentSeg.getX();
coords[1] = (float)currentSeg.getY();
at.transform(coords,0,coords,0,1);
return SEG_LINETO;
}
public int currentSegment(double[] coords) {
coords[0] = currentSeg.getX();
coords[1] = currentSeg.getY();
at.transform(coords,0,coords,0,1);
return SEG_LINETO;
}
}
}
===============================================