001: /*
002: * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.awt;
027:
028: /**
029: * The <code>BasicStroke</code> class defines a basic set of rendering
030: * attributes for the outlines of graphics primitives, which are rendered
031: * with a {@link Graphics2D} object that has its Stroke attribute set to
032: * this <code>BasicStroke</code>.
033: * The rendering attributes defined by <code>BasicStroke</code> describe
034: * the shape of the mark made by a pen drawn along the outline of a
035: * {@link Shape} and the decorations applied at the ends and joins of
036: * path segments of the <code>Shape</code>.
037: * These rendering attributes include:
038: * <dl compact>
039: * <dt><i>width</i>
040: * <dd>The pen width, measured perpendicularly to the pen trajectory.
041: * <dt><i>end caps</i>
042: * <dd>The decoration applied to the ends of unclosed subpaths and
043: * dash segments. Subpaths that start and end on the same point are
044: * still considered unclosed if they do not have a CLOSE segment.
045: * See {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}
046: * for more information on the CLOSE segment.
047: * The three different decorations are: {@link #CAP_BUTT},
048: * {@link #CAP_ROUND}, and {@link #CAP_SQUARE}.
049: * <dt><i>line joins</i>
050: * <dd>The decoration applied at the intersection of two path segments
051: * and at the intersection of the endpoints of a subpath that is closed
052: * using {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}.
053: * The three different decorations are: {@link #JOIN_BEVEL},
054: * {@link #JOIN_MITER}, and {@link #JOIN_ROUND}.
055: * <dt><i>miter limit</i>
056: * <dd>The limit to trim a line join that has a JOIN_MITER decoration.
057: * A line join is trimmed when the ratio of miter length to stroke
058: * width is greater than the miterlimit value. The miter length is
059: * the diagonal length of the miter, which is the distance between
060: * the inside corner and the outside corner of the intersection.
061: * The smaller the angle formed by two line segments, the longer
062: * the miter length and the sharper the angle of intersection. The
063: * default miterlimit value of 10.0f causes all angles less than
064: * 11 degrees to be trimmed. Trimming miters converts
065: * the decoration of the line join to bevel.
066: * <dt><i>dash attributes</i>
067: * <dd>The definition of how to make a dash pattern by alternating
068: * between opaque and transparent sections.
069: * </dl>
070: * All attributes that specify measurements and distances controlling
071: * the shape of the returned outline are measured in the same
072: * coordinate system as the original unstroked <code>Shape</code>
073: * argument. When a <code>Graphics2D</code> object uses a
074: * <code>Stroke</code> object to redefine a path during the execution
075: * of one of its <code>draw</code> methods, the geometry is supplied
076: * in its original form before the <code>Graphics2D</code> transform
077: * attribute is applied. Therefore, attributes such as the pen width
078: * are interpreted in the user space coordinate system of the
079: * <code>Graphics2D</code> object and are subject to the scaling and
080: * shearing effects of the user-space-to-device-space transform in that
081: * particular <code>Graphics2D</code>.
082: * For example, the width of a rendered shape's outline is determined
083: * not only by the width attribute of this <code>BasicStroke</code>,
084: * but also by the transform attribute of the
085: * <code>Graphics2D</code> object. Consider this code:
086: * <blockquote><tt>
087: * // sets the Graphics2D object's Tranform attribute
088: * g2d.scale(10, 10);
089: * // sets the Graphics2D object's Stroke attribute
090: * g2d.setStroke(new BasicStroke(1.5f));
091: * </tt></blockquote>
092: * Assuming there are no other scaling transforms added to the
093: * <code>Graphics2D</code> object, the resulting line
094: * will be approximately 15 pixels wide.
095: * As the example code demonstrates, a floating-point line
096: * offers better precision, especially when large transforms are
097: * used with a <code>Graphics2D</code> object.
098: * When a line is diagonal, the exact width depends on how the
099: * rendering pipeline chooses which pixels to fill as it traces the
100: * theoretical widened outline. The choice of which pixels to turn
101: * on is affected by the antialiasing attribute because the
102: * antialiasing rendering pipeline can choose to color
103: * partially-covered pixels.
104: * <p>
105: * For more information on the user space coordinate system and the
106: * rendering process, see the <code>Graphics2D</code> class comments.
107: * @see Graphics2D
108: * @version 1.50, 06/08/07
109: * @author Jim Graham
110: */
111: public class BasicStroke implements Stroke {
112:
113: /**
114: * Joins path segments by extending their outside edges until
115: * they meet.
116: */
117: public final static int JOIN_MITER = 0;
118:
119: /**
120: * Joins path segments by rounding off the corner at a radius
121: * of half the line width.
122: */
123: public final static int JOIN_ROUND = 1;
124:
125: /**
126: * Joins path segments by connecting the outer corners of their
127: * wide outlines with a straight segment.
128: */
129: public final static int JOIN_BEVEL = 2;
130:
131: /**
132: * Ends unclosed subpaths and dash segments with no added
133: * decoration.
134: */
135: public final static int CAP_BUTT = 0;
136:
137: /**
138: * Ends unclosed subpaths and dash segments with a round
139: * decoration that has a radius equal to half of the width
140: * of the pen.
141: */
142: public final static int CAP_ROUND = 1;
143:
144: /**
145: * Ends unclosed subpaths and dash segments with a square
146: * projection that extends beyond the end of the segment
147: * to a distance equal to half of the line width.
148: */
149: public final static int CAP_SQUARE = 2;
150:
151: float width;
152:
153: int join;
154: int cap;
155: float miterlimit;
156:
157: float dash[];
158: float dash_phase;
159:
160: /**
161: * Constructs a new <code>BasicStroke</code> with the specified
162: * attributes.
163: * @param width the width of this <code>BasicStroke</code>. The
164: * width must be greater than or equal to 0.0f. If width is
165: * set to 0.0f, the stroke is rendered as the thinnest
166: * possible line for the target device and the antialias
167: * hint setting.
168: * @param cap the decoration of the ends of a <code>BasicStroke</code>
169: * @param join the decoration applied where path segments meet
170: * @param miterlimit the limit to trim the miter join. The miterlimit
171: * must be greater than or equal to 1.0f.
172: * @param dash the array representing the dashing pattern
173: * @param dash_phase the offset to start the dashing pattern
174: * @throws IllegalArgumentException if <code>width</code> is negative
175: * @throws IllegalArgumentException if <code>cap</code> is not either
176: * CAP_BUTT, CAP_ROUND or CAP_SQUARE
177: * @throws IllegalArgumentException if <code>miterlimit</code> is less
178: * than 1 and <code>join</code> is JOIN_MITER
179: * @throws IllegalArgumentException if <code>join</code> is not
180: * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
181: * @throws IllegalArgumentException if <code>dash_phase</code>
182: * is negative and <code>dash</code> is not <code>null</code>
183: * @throws IllegalArgumentException if the length of
184: * <code>dash</code> is zero
185: * @throws IllegalArgumentException if dash lengths are all zero.
186: */
187: public BasicStroke(float width, int cap, int join,
188: float miterlimit, float dash[], float dash_phase) {
189: if (width < 0.0f) {
190: throw new IllegalArgumentException("negative width");
191: }
192: if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
193: throw new IllegalArgumentException("illegal end cap value");
194: }
195: if (join == JOIN_MITER) {
196: if (miterlimit < 1.0f) {
197: throw new IllegalArgumentException("miter limit < 1");
198: }
199: } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
200: throw new IllegalArgumentException(
201: "illegal line join value");
202: }
203: if (dash != null) {
204: if (dash_phase < 0.0f) {
205: throw new IllegalArgumentException(
206: "negative dash phase");
207: }
208: boolean allzero = true;
209: for (int i = 0; i < dash.length; i++) {
210: float d = dash[i];
211: if (d > 0.0) {
212: allzero = false;
213: } else if (d < 0.0) {
214: throw new IllegalArgumentException(
215: "negative dash length");
216: }
217: }
218: if (allzero) {
219: throw new IllegalArgumentException(
220: "dash lengths all zero");
221: }
222: }
223: this .width = width;
224: this .cap = cap;
225: this .join = join;
226: this .miterlimit = miterlimit;
227: if (dash != null) {
228: this .dash = (float[]) dash.clone();
229: }
230: this .dash_phase = dash_phase;
231: }
232:
233: /**
234: * Constructs a solid <code>BasicStroke</code> with the specified
235: * attributes.
236: * @param width the width of the <code>BasicStroke</code>
237: * @param cap the decoration of the ends of a <code>BasicStroke</code>
238: * @param join the decoration applied where path segments meet
239: * @param miterlimit the limit to trim the miter join
240: * @throws IllegalArgumentException if <code>width</code> is negative
241: * @throws IllegalArgumentException if <code>cap</code> is not either
242: * CAP_BUTT, CAP_ROUND or CAP_SQUARE
243: * @throws IllegalArgumentException if <code>miterlimit</code> is less
244: * than 1 and <code>join</code> is JOIN_MITER
245: * @throws IllegalArgumentException if <code>join</code> is not
246: * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
247: */
248: public BasicStroke(float width, int cap, int join, float miterlimit) {
249: this (width, cap, join, miterlimit, null, 0.0f);
250: }
251:
252: /**
253: * Constructs a solid <code>BasicStroke</code> with the specified
254: * attributes. The <code>miterlimit</code> parameter is
255: * unnecessary in cases where the default is allowable or the
256: * line joins are not specified as JOIN_MITER.
257: * @param width the width of the <code>BasicStroke</code>
258: * @param cap the decoration of the ends of a <code>BasicStroke</code>
259: * @param join the decoration applied where path segments meet
260: * @throws IllegalArgumentException if <code>width</code> is negative
261: * @throws IllegalArgumentException if <code>cap</code> is not either
262: * CAP_BUTT, CAP_ROUND or CAP_SQUARE
263: * @throws IllegalArgumentException if <code>join</code> is not
264: * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
265: */
266: public BasicStroke(float width, int cap, int join) {
267: this (width, cap, join, 10.0f, null, 0.0f);
268: }
269:
270: /**
271: * Constructs a solid <code>BasicStroke</code> with the specified
272: * line width and with default values for the cap and join
273: * styles.
274: * @param width the width of the <code>BasicStroke</code>
275: * @throws IllegalArgumentException if <code>width</code> is negative
276: */
277: public BasicStroke(float width) {
278: this (width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
279: }
280:
281: /**
282: * Constructs a new <code>BasicStroke</code> with defaults for all
283: * attributes.
284: * The default attributes are a solid line of width 1.0, CAP_SQUARE,
285: * JOIN_MITER, a miter limit of 10.0.
286: */
287: public BasicStroke() {
288: this (1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
289: }
290:
291: /**
292: * Returns a <code>Shape</code> whose interior defines the
293: * stroked outline of a specified <code>Shape</code>.
294: * @param s the <code>Shape</code> boundary be stroked
295: * @return the <code>Shape</code> of the stroked outline.
296: */
297: public Shape createStrokedShape(Shape s) {
298: sun.java2d.pipe.RenderingEngine re = sun.java2d.pipe.RenderingEngine
299: .getInstance();
300: return re.createStrokedShape(s, width, cap, join, miterlimit,
301: dash, dash_phase);
302: }
303:
304: /**
305: * Returns the line width. Line width is represented in user space,
306: * which is the default-coordinate space used by Java 2D. See the
307: * <code>Graphics2D</code> class comments for more information on
308: * the user space coordinate system.
309: * @return the line width of this <code>BasicStroke</code>.
310: * @see Graphics2D
311: */
312: public float getLineWidth() {
313: return width;
314: }
315:
316: /**
317: * Returns the end cap style.
318: * @return the end cap style of this <code>BasicStroke</code> as one
319: * of the static <code>int</code> values that define possible end cap
320: * styles.
321: */
322: public int getEndCap() {
323: return cap;
324: }
325:
326: /**
327: * Returns the line join style.
328: * @return the line join style of the <code>BasicStroke</code> as one
329: * of the static <code>int</code> values that define possible line
330: * join styles.
331: */
332: public int getLineJoin() {
333: return join;
334: }
335:
336: /**
337: * Returns the limit of miter joins.
338: * @return the limit of miter joins of the <code>BasicStroke</code>.
339: */
340: public float getMiterLimit() {
341: return miterlimit;
342: }
343:
344: /**
345: * Returns the array representing the lengths of the dash segments.
346: * Alternate entries in the array represent the user space lengths
347: * of the opaque and transparent segments of the dashes.
348: * As the pen moves along the outline of the <code>Shape</code>
349: * to be stroked, the user space
350: * distance that the pen travels is accumulated. The distance
351: * value is used to index into the dash array.
352: * The pen is opaque when its current cumulative distance maps
353: * to an even element of the dash array and transparent otherwise.
354: * @return the dash array.
355: */
356: public float[] getDashArray() {
357: if (dash == null) {
358: return null;
359: }
360:
361: return (float[]) dash.clone();
362: }
363:
364: /**
365: * Returns the current dash phase.
366: * The dash phase is a distance specified in user coordinates that
367: * represents an offset into the dashing pattern. In other words, the dash
368: * phase defines the point in the dashing pattern that will correspond to
369: * the beginning of the stroke.
370: * @return the dash phase as a <code>float</code> value.
371: */
372: public float getDashPhase() {
373: return dash_phase;
374: }
375:
376: /**
377: * Returns the hashcode for this stroke.
378: * @return a hash code for this stroke.
379: */
380: public int hashCode() {
381: int hash = Float.floatToIntBits(width);
382: hash = hash * 31 + join;
383: hash = hash * 31 + cap;
384: hash = hash * 31 + Float.floatToIntBits(miterlimit);
385: if (dash != null) {
386: hash = hash * 31 + Float.floatToIntBits(dash_phase);
387: for (int i = 0; i < dash.length; i++) {
388: hash = hash * 31 + Float.floatToIntBits(dash[i]);
389: }
390: }
391: return hash;
392: }
393:
394: /**
395: * Returns true if this BasicStroke represents the same
396: * stroking operation as the given argument.
397: */
398: /**
399: * Tests if a specified object is equal to this <code>BasicStroke</code>
400: * by first testing if it is a <code>BasicStroke</code> and then comparing
401: * its width, join, cap, miter limit, dash, and dash phase attributes with
402: * those of this <code>BasicStroke</code>.
403: * @param obj the specified object to compare to this
404: * <code>BasicStroke</code>
405: * @return <code>true</code> if the width, join, cap, miter limit, dash, and
406: * dash phase are the same for both objects;
407: * <code>false</code> otherwise.
408: */
409: public boolean equals(Object obj) {
410: if (!(obj instanceof BasicStroke)) {
411: return false;
412: }
413:
414: BasicStroke bs = (BasicStroke) obj;
415: if (width != bs.width) {
416: return false;
417: }
418:
419: if (join != bs.join) {
420: return false;
421: }
422:
423: if (cap != bs.cap) {
424: return false;
425: }
426:
427: if (miterlimit != bs.miterlimit) {
428: return false;
429: }
430:
431: if (dash != null) {
432: if (dash_phase != bs.dash_phase) {
433: return false;
434: }
435:
436: if (!java.util.Arrays.equals(dash, bs.dash)) {
437: return false;
438: }
439: } else if (bs.dash != null) {
440: return false;
441: }
442:
443: return true;
444: }
445: }
|