1 // Copyright 2006, FreeHEP
2 package org.freehep.graphicsio.test;
3
4 /*
5 * Copyright (c) 2000 David Flanagan. All rights reserved.
6 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
7 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
8 * You may study, use, and modify it for any non-commercial purpose.
9 * You may distribute it non-commercially as long as you retain this notice.
10 * For a commercial use license, or to purchase the book (recommended),
11 * visit http://www.davidflanagan.com/javaexamples2.
12 */
13
14 import java.awt.BasicStroke;
15 import java.awt.Color;
16 import java.awt.Font;
17 import java.awt.Graphics;
18 import java.awt.RenderingHints;
19 import java.awt.Shape;
20 import java.awt.Stroke;
21 import java.awt.font.GlyphVector;
22 import java.awt.geom.GeneralPath;
23 import java.awt.geom.PathIterator;
24 import java.util.Random;
25
26 import org.freehep.graphics2d.VectorGraphics;
27
28 /**
29 * @author Mark Donszelmann
30 * @version $Id: TestCustomStrokes.java 12626 2007-06-08 22:23:13Z duns $
31 */
32 public class TestCustomStrokes extends TestingPanel {
33
34 private Random random;
35
36 // These are the various stroke objects we'll demonstrate
37 private Stroke[] strokes = new Stroke[] {
38 new BasicStroke(4.0f), // The standard, predefined stroke
39 new NullStroke(), // A Stroke that does nothing
40 new DoubleStroke(8.0f, 2.0f), // A Stroke that strokes twice
41 new ControlPointsStroke(2.0f), // Shows the vertices & control
42 // points
43 new SloppyStroke(2.0f, 3.0f) // Perturbs the shape before
44 // stroking
45 };
46
47 public TestCustomStrokes(String[] args) throws Exception {
48 super(args);
49 setName("Custom Strokes");
50 random = new Random(123456L);
51 }
52
53 public void paintComponent(Graphics g) {
54
55 VectorGraphics vg = VectorGraphics.create(g);
56
57 // Get a shape to work with. Here we'll use the letter B
58 Font f = new Font("Serif", Font.BOLD, 150);
59 GlyphVector gv = f.createGlyphVector(vg.getFontRenderContext(), "B");
60 Shape shape = gv.getOutline();
61
62 // Set drawing attributes and starting position
63 vg.setColor(Color.black);
64 vg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
65 RenderingHints.VALUE_ANTIALIAS_ON);
66 vg.translate(10, 175);
67
68 // Draw the shape once with each stroke
69 for(int i = 0; i < strokes.length; i++) {
70 vg.setStroke(strokes[i]); // set the stroke
71 vg.draw(shape); // draw the shape
72 vg.translate(100,0); // move to the right
73 }
74
75 vg.translate(-strokes.length*100, 0);
76 vg.translate(10, 100);
77
78 for (int i=0; i < strokes.length; i++) {
79 vg.setStroke(strokes[i]);
80 vg.drawRect(10, 10, 80, 50);
81 vg.translate(100, 0);
82 }
83 }
84
85 /**
86 * This Stroke implementation does nothing. Its createStrokedShape() method
87 * returns an unmodified shape. Thus, drawing a shape with this Stroke is
88 * the same as filling that shape!
89 */
90 class NullStroke implements Stroke {
91 public Shape createStrokedShape(Shape s) { return s; }
92 }
93
94 /**
95 * This Stroke implementation applies a BasicStroke to a shape twice. If you
96 * draw with this Stroke, then instead of outlining the shape, you're
97 * outlining the outline of the shape.
98 */
99 class DoubleStroke implements Stroke {
100 BasicStroke stroke1, stroke2; // the two strokes to use
101 public DoubleStroke(float width1, float width2) {
102 stroke1 = new BasicStroke(width1); // Constructor arguments specify
103 stroke2 = new BasicStroke(width2); // the line widths for the strokes
104 }
105
106 public Shape createStrokedShape(Shape s) {
107 // Use the first stroke to create an outline of the shape
108 Shape outline = stroke1.createStrokedShape(s);
109 // Use the second stroke to create an outline of that outline.
110 // It is this outline of the outline that will be filled in
111 return stroke2.createStrokedShape(outline);
112 }
113 }
114
115 /**
116 * This Stroke implementation strokes the shape using a thin line, and also
117 * displays the end points and Bezier curve control points of all the line
118 * and curve segments that make up the shape. The radius argument to the
119 * constructor specifies the size of the control point markers. Note the use
120 * of PathIterator to break the shape down into its segments, and of
121 * GeneralPath to build up the stroked shape.
122 */
123 class ControlPointsStroke implements Stroke {
124 float radius; // how big the control point markers should be
125 public ControlPointsStroke(float radius) { this.radius = radius; }
126
127 public Shape createStrokedShape(Shape shape) {
128 // Start off by stroking the shape with a thin line. Store the
129 // resulting shape in a GeneralPath object so we can add to it.
130 GeneralPath strokedShape =
131 new GeneralPath(new BasicStroke(1.0f).createStrokedShape(shape));
132
133 // Use a PathIterator object to iterate through each of the line and
134 // curve segments of the shape. For each one, mark the endpoint and
135 // control points (if any) by adding a rectangle to the GeneralPath
136 float[] coords = new float[6];
137 for(PathIterator i=shape.getPathIterator(null); !i.isDone();i.next()) {
138 int type = i.currentSegment(coords);
139
140 switch(type) {
141 case PathIterator.SEG_CUBICTO:
142 markPoint(strokedShape, coords[4], coords[5]); // falls through
143 case PathIterator.SEG_QUADTO:
144 markPoint(strokedShape, coords[2], coords[3]); // falls through
145 case PathIterator.SEG_MOVETO:
146 case PathIterator.SEG_LINETO:
147 markPoint(strokedShape, coords[0], coords[1]); // falls through
148 case PathIterator.SEG_CLOSE:
149 break;
150 }
151 }
152
153 return strokedShape;
154 }
155
156 /** Add a small square centered at (x,y) to the specified path */
157 void markPoint(GeneralPath path, float x, float y) {
158 path.moveTo(x-radius, y-radius); // Begin a new sub-path
159 path.lineTo(x+radius, y-radius); // Add a line segment to it
160 path.lineTo(x+radius, y+radius); // Add a second line segment
161 path.lineTo(x-radius, y+radius); // And a third
162 path.closePath(); // Go back to last moveTo position
163 }
164 }
165
166 /**
167 * This Stroke implementation randomly perturbs the line and curve segments
168 * that make up a Shape, and then strokes that perturbed shape. It uses
169 * PathIterator to loop through the Shape and GeneralPath to build up the
170 * modified shape. Finally, it uses a BasicStroke to stroke the modified
171 * shape. The result is a "sloppy" looking shape.
172 */
173 class SloppyStroke implements Stroke {
174 BasicStroke stroke;
175 float sloppiness;
176
177 public SloppyStroke(float width, float sloppiness) {
178 this.stroke = new BasicStroke(width); // Used to stroke modified shape
179 this.sloppiness = sloppiness; // How sloppy should we be?
180 }
181
182 public Shape createStrokedShape(Shape shape) {
183 GeneralPath newshape = new GeneralPath(); // Start with an empty shape
184
185 // Iterate through the specified shape, perturb its coordinates, and
186 // use them to build up the new shape.
187 float[] coords = new float[6];
188 for(PathIterator i=shape.getPathIterator(null); !i.isDone();i.next()) {
189 int type = i.currentSegment(coords);
190
191 switch(type) {
192 case PathIterator.SEG_MOVETO:
193 perturb(coords, 2);
194 newshape.moveTo(coords[0], coords[1]);
195 break;
196 case PathIterator.SEG_LINETO:
197 perturb(coords, 2);
198 newshape.lineTo(coords[0], coords[1]);
199 break;
200 case PathIterator.SEG_QUADTO:
201 perturb(coords, 4);
202 newshape.quadTo(coords[0], coords[1], coords[2], coords[3]);
203 break;
204 case PathIterator.SEG_CUBICTO:
205 perturb(coords, 6);
206 newshape.curveTo(coords[0], coords[1], coords[2], coords[3],
207 coords[4], coords[5]);
208 break;
209 case PathIterator.SEG_CLOSE:
210 newshape.closePath();
211 break;
212 }
213 }
214
215 // Finally, stroke the perturbed shape and return the result
216 return stroke.createStrokedShape(newshape);
217 }
218
219 // Randomly modify the specified number of coordinates, by an amount
220 // specified by the sloppiness field.
221 void perturb(float[] coords, int numCoords) {
222 for(int i = 0; i < numCoords; i++) {
223 coords[i] += (float)((random.nextDouble()*2-1.0)*sloppiness);
224 }
225 }
226 }
227
228 public static void main(String[] args) throws Exception {
229 new TestCustomStrokes(args).runTest();
230 }
231 }