1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.svg;
\r
14 import java.awt.BasicStroke;
\r
15 import java.awt.Graphics2D;
\r
17 //import org.apache.batik.util.CSSConstants;
\r
20 * @author Tuukka Lehtonen
\r
22 public class StrokeDesc {
\r
24 public static final String DEFAULT_MEASUREMENT_UNIT = "";
\r
27 * This empty array stands for no dashing.
\r
29 public static final double[] NO_DASHES_DASH_ARRAY = {};
\r
31 // public static final String[] joinConv = { "miter", "round", "bevel" };
\r
32 // public static final String[] capConv = { "butt", "round", "square" };
\r
35 * Joins path segments by extending their outside edges until they meet.
\r
37 public final static Integer JOIN_MITER = BasicStroke.JOIN_MITER;
\r
40 * Joins path segments by rounding off the corner at a radius of half the
\r
43 public final static Integer JOIN_ROUND = BasicStroke.JOIN_ROUND;
\r
46 * Joins path segments by connecting the outer corners of their wide
\r
47 * outlines with a straight segment.
\r
49 public final static Integer JOIN_BEVEL = BasicStroke.JOIN_BEVEL;
\r
52 * Ends unclosed subpaths and dash segments with no added decoration.
\r
54 public final static Integer CAP_BUTT = BasicStroke.CAP_BUTT;
\r
57 * Ends unclosed subpaths and dash segments with a round decoration that has
\r
58 * a radius equal to half of the width of the pen.
\r
60 public final static Integer CAP_ROUND = BasicStroke.CAP_ROUND;
\r
63 * Ends unclosed subpaths and dash segments with a square projection that
\r
64 * extends beyond the end of the segment to a distance equal to half of the
\r
67 public final static Integer CAP_SQUARE = BasicStroke.CAP_SQUARE;
\r
70 private String paint;
\r
72 private double opacity;
\r
74 private double width;
\r
76 private LineJoin join;
\r
78 private LineCap cap;
\r
80 private double miterLimit;
\r
83 * An empty array (length == 0) stands for no dashing.
\r
85 private double[] dash;
\r
87 private double dashOffset;
\r
90 * One of valid measurement units in SVG, such as "in", "mm", "cm", etc.
\r
92 private String unitSuffix;
\r
94 public StrokeDesc() {
\r
95 this(StyleConstants.INHERIT, 1.0, 1.0, LineJoin.bevel, LineCap.butt, 10.0, null, 0.0);
\r
98 public StrokeDesc(String paint) {
\r
99 this(paint, 1.0, 1.0, LineJoin.bevel, LineCap.butt, 10.0, null, 0.0);
\r
102 public StrokeDesc(String paint, double opacity, double width) {
\r
103 this(paint, opacity, width, LineJoin.bevel, LineCap.butt, 10.0, null, 0.0);
\r
106 public StrokeDesc(String paint, double opacity, double width, LineJoin join, LineCap cap) {
\r
107 this(paint, opacity, width, join, cap, 10.0, null, 0.0);
\r
110 public StrokeDesc(String paint, double opacity, double width, LineJoin join, LineCap cap, double miterLimit, double[] dashArray, double dashOffset) {
\r
112 setOpacity(opacity);
\r
113 setLineWidth(width);
\r
116 setMiterLimit(miterLimit);
\r
118 if (dashArray == null || dashArray.length == 0)
\r
119 dashArray = NO_DASHES_DASH_ARRAY;
\r
120 this.dash = dashArray;
\r
121 this.dashOffset = dashOffset;
\r
123 this.unitSuffix = DEFAULT_MEASUREMENT_UNIT;
\r
126 public void setUnitSuffix(String unitSuffix) {
\r
127 if (!SVGUnits.isValidUnit(unitSuffix))
\r
128 throw new IllegalArgumentException("invalid unit suffix: " + unitSuffix);
\r
129 this.unitSuffix = unitSuffix;
\r
132 public String getUnitSuffix() {
\r
136 public String getPaint() {
\r
140 public void setPaint(String paint) {
\r
141 this.paint = paint;
\r
144 public double getOpacity() {
\r
148 public void setOpacity(double opacity) {
\r
149 this.opacity = opacity;
\r
153 * Returns the line width. Line width is represented in user space, which is
\r
154 * the default-coordinate system used by Java 2D. See the
\r
155 * <code>Graphics2D</code> class comments for more information on the user
\r
156 * space coordinate system.
\r
158 * @return the line width of this <code>BasicStroke</code>.
\r
161 public double getLineWidth() {
\r
165 public String getLineWidthWithUnit() {
\r
166 return String.valueOf(width) + unitSuffix;
\r
169 public void setLineWidth(double width) {
\r
170 this.width = width;
\r
174 * Returns the end cap style.
\r
176 * @return the end cap style of this <code>BasicStroke</code> as one of
\r
177 * the static <code>int</code> values that define possible end cap
\r
180 public LineCap getEndCap() {
\r
184 public void setEndCap(LineCap cap) {
\r
189 * Returns the line join style.
\r
191 * @return the line join style of the <code>BasicStroke</code> as one of
\r
192 * the static <code>int</code> values that define possible line
\r
195 public LineJoin getLineJoin() {
\r
199 public void setLineJoin(LineJoin join) {
\r
204 * Returns the limit of miter joins.
\r
206 * @return the limit of miter joins of the <code>BasicStroke</code>.
\r
208 public double getMiterLimit() {
\r
212 public void setMiterLimit(double miterLimit) {
\r
213 this.miterLimit = miterLimit;
\r
217 * Returns the array representing the lengths of the dash segments.
\r
218 * Alternate entries in the array represent the user space lengths of the
\r
219 * opaque and transparent segments of the dashes. As the pen moves along the
\r
220 * outline of the <code>Shape</code> to be stroked, the user space
\r
221 * distance that the pen travels is accumulated. The distance value is used
\r
222 * to index into the dash array. The pen is opaque when its current
\r
223 * cumulative distance maps to an even element of the dash array and
\r
224 * transparent otherwise.
\r
226 * @return the dash array.
\r
228 public double[] getDashArray() {
\r
229 if (dash == NO_DASHES_DASH_ARRAY)
\r
231 return (double[]) dash.clone();
\r
234 public void setDashArray(double[] dash) {
\r
235 if (dash == null || dash.length == 0)
\r
236 dash = NO_DASHES_DASH_ARRAY;
\r
241 * Returns the current dash phase. The dash phase is a distance specified in
\r
242 * user coordinates that represents an offset into the dashing pattern. In
\r
243 * other words, the dash phase defines the point in the dashing pattern that
\r
244 * will correspond to the beginning of the stroke.
\r
246 * @return the dash phase as a <code>double</code> value.
\r
248 public double getDashOffset() {
\r
252 public void setDashOffset(double dashOffset) {
\r
253 this.dashOffset = dashOffset;
\r
257 * Returns the hashcode for this stroke.
\r
259 * @return a hash code for this stroke.
\r
261 public int hashCode() {
\r
262 int hash = (int) Double.doubleToLongBits(width);
\r
263 hash = hash * 31 + join.ordinal();
\r
264 hash = hash * 31 + cap.ordinal();
\r
265 hash = hash * 31 + (int) Double.doubleToLongBits(miterLimit);
\r
266 if (dash != null) {
\r
267 hash = hash * 31 + (int) Double.doubleToLongBits(dashOffset);
\r
268 for (int i = 0; i < dash.length; i++) {
\r
269 hash = hash * 31 + (int) Double.doubleToLongBits(dash[i]);
\r
276 * Returns true if this BasicStroke represents the same stroking operation
\r
277 * as the given argument.
\r
280 * Tests if a specified object is equal to this <code>Stroke</code> by
\r
281 * first testing if it is a <code>BasicStroke</code> and then comparing
\r
282 * its width, join, cap, miter limit, dash, and dash phase attributes with
\r
283 * those of this <code>Stroke</code>.
\r
285 * @param obj the specified object to compare to this <code>Stroke</code>
\r
286 * @return <code>true</code> if the width, join, cap, miter limit, dash,
\r
287 * and dash phase are the same for both objects; <code>false</code>
\r
290 public boolean equals(Object obj) {
\r
291 if (!(obj instanceof StrokeDesc)) {
\r
295 StrokeDesc bs = (StrokeDesc) obj;
\r
296 if (width != bs.width) {
\r
300 if (join != bs.join) {
\r
304 if (cap != bs.cap) {
\r
308 if (miterLimit != bs.miterLimit) {
\r
312 if (dash != null) {
\r
313 if (dashOffset != bs.dashOffset) {
\r
317 if (!java.util.Arrays.equals(dash, bs.dash)) {
\r
320 } else if (bs.dash != null) {
\r
327 // public String toStyleString() {
\r
328 // StringBuilder s = new StringBuilder();
\r
330 // s.append(CSSConstants.CSS_STROKE_PROPERTY);
\r
332 // s.append(paint);
\r
333 // if (!paint.equals(CSSConstants.CSS_NONE_VALUE)) {
\r
335 // s.append(CSSConstants.CSS_STROKE_OPACITY_PROPERTY);
\r
337 // s.append(opacity);
\r
339 // s.append(CSSConstants.CSS_STROKE_WIDTH_PROPERTY);
\r
341 // s.append(width);
\r
342 // s.append(unitSuffix);
\r
343 // if (dash.length > 0) {
\r
345 // s.append(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY);
\r
347 // appendDashArrayString(s);
\r
349 // s.append(CSSConstants.CSS_STROKE_DASHOFFSET_PROPERTY);
\r
351 // s.append(dashOffset);
\r
354 // s.append(CSSConstants.CSS_STROKE_LINECAP_PROPERTY);
\r
356 // s.append(cap.toString());
\r
358 // s.append(CSSConstants.CSS_STROKE_LINEJOIN_PROPERTY);
\r
360 // s.append(join.toString());
\r
361 // if (LineJoin.miter.equals(join)) {
\r
363 // s.append(CSSConstants.CSS_STROKE_MITERLIMIT_PROPERTY);
\r
365 // s.append(miterLimit);
\r
370 // return s.toString();
\r
373 public void appendDashArrayString(StringBuilder s) {
\r
374 if (dash.length > 0) {
\r
376 for (int i = 1; i < dash.length; ++i) {
\r
383 public String dashArrayToString() {
\r
385 if (dash.length > 0) {
\r
387 for (int i = 1; i < dash.length; ++i) {
\r
388 s += ',' + dash[i];
\r