/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.svg; import java.awt.BasicStroke; import java.awt.Graphics2D; //import org.apache.batik.util.CSSConstants; /** * @author Tuukka Lehtonen */ public class StrokeDesc { public static final String DEFAULT_MEASUREMENT_UNIT = ""; /** * This empty array stands for no dashing. */ public static final double[] NO_DASHES_DASH_ARRAY = {}; // public static final String[] joinConv = { "miter", "round", "bevel" }; // public static final String[] capConv = { "butt", "round", "square" }; /** * Joins path segments by extending their outside edges until they meet. */ public final static Integer JOIN_MITER = BasicStroke.JOIN_MITER; /** * Joins path segments by rounding off the corner at a radius of half the * line width. */ public final static Integer JOIN_ROUND = BasicStroke.JOIN_ROUND; /** * Joins path segments by connecting the outer corners of their wide * outlines with a straight segment. */ public final static Integer JOIN_BEVEL = BasicStroke.JOIN_BEVEL; /** * Ends unclosed subpaths and dash segments with no added decoration. */ public final static Integer CAP_BUTT = BasicStroke.CAP_BUTT; /** * Ends unclosed subpaths and dash segments with a round decoration that has * a radius equal to half of the width of the pen. */ public final static Integer CAP_ROUND = BasicStroke.CAP_ROUND; /** * Ends unclosed subpaths and dash segments with a square projection that * extends beyond the end of the segment to a distance equal to half of the * line width. */ public final static Integer CAP_SQUARE = BasicStroke.CAP_SQUARE; private String paint; private double opacity; private double width; private LineJoin join; private LineCap cap; private double miterLimit; /** * An empty array (length == 0) stands for no dashing. */ private double[] dash; private double dashOffset; /** * One of valid measurement units in SVG, such as "in", "mm", "cm", etc. */ private String unitSuffix; public StrokeDesc() { this(StyleConstants.INHERIT, 1.0, 1.0, LineJoin.bevel, LineCap.butt, 10.0, null, 0.0); } public StrokeDesc(String paint) { this(paint, 1.0, 1.0, LineJoin.bevel, LineCap.butt, 10.0, null, 0.0); } public StrokeDesc(String paint, double opacity, double width) { this(paint, opacity, width, LineJoin.bevel, LineCap.butt, 10.0, null, 0.0); } public StrokeDesc(String paint, double opacity, double width, LineJoin join, LineCap cap) { this(paint, opacity, width, join, cap, 10.0, null, 0.0); } public StrokeDesc(String paint, double opacity, double width, LineJoin join, LineCap cap, double miterLimit, double[] dashArray, double dashOffset) { setPaint(paint); setOpacity(opacity); setLineWidth(width); setLineJoin(join); setEndCap(cap); setMiterLimit(miterLimit); if (dashArray == null || dashArray.length == 0) dashArray = NO_DASHES_DASH_ARRAY; this.dash = dashArray; this.dashOffset = dashOffset; this.unitSuffix = DEFAULT_MEASUREMENT_UNIT; } public void setUnitSuffix(String unitSuffix) { if (!SVGUnits.isValidUnit(unitSuffix)) throw new IllegalArgumentException("invalid unit suffix: " + unitSuffix); this.unitSuffix = unitSuffix; } public String getUnitSuffix() { return unitSuffix; } public String getPaint() { return paint; } public void setPaint(String paint) { this.paint = paint; } public double getOpacity() { return opacity; } public void setOpacity(double opacity) { this.opacity = opacity; } /** * Returns the line width. Line width is represented in user space, which is * the default-coordinate system used by Java 2D. See the * Graphics2D class comments for more information on the user * space coordinate system. * * @return the line width of this BasicStroke. * @see Graphics2D */ public double getLineWidth() { return width; } public String getLineWidthWithUnit() { return String.valueOf(width) + unitSuffix; } public void setLineWidth(double width) { this.width = width; } /** * Returns the end cap style. * * @return the end cap style of this BasicStroke as one of * the static int values that define possible end cap * styles. */ public LineCap getEndCap() { return cap; } public void setEndCap(LineCap cap) { this.cap = cap; } /** * Returns the line join style. * * @return the line join style of the BasicStroke as one of * the static int values that define possible line * join styles. */ public LineJoin getLineJoin() { return join; } public void setLineJoin(LineJoin join) { this.join = join; } /** * Returns the limit of miter joins. * * @return the limit of miter joins of the BasicStroke. */ public double getMiterLimit() { return miterLimit; } public void setMiterLimit(double miterLimit) { this.miterLimit = miterLimit; } /** * Returns the array representing the lengths of the dash segments. * Alternate entries in the array represent the user space lengths of the * opaque and transparent segments of the dashes. As the pen moves along the * outline of the Shape to be stroked, the user space * distance that the pen travels is accumulated. The distance value is used * to index into the dash array. The pen is opaque when its current * cumulative distance maps to an even element of the dash array and * transparent otherwise. * * @return the dash array. */ public double[] getDashArray() { if (dash == NO_DASHES_DASH_ARRAY) return dash; return (double[]) dash.clone(); } public void setDashArray(double[] dash) { if (dash == null || dash.length == 0) dash = NO_DASHES_DASH_ARRAY; this.dash = dash; } /** * Returns the current dash phase. The dash phase is a distance specified in * user coordinates that represents an offset into the dashing pattern. In * other words, the dash phase defines the point in the dashing pattern that * will correspond to the beginning of the stroke. * * @return the dash phase as a double value. */ public double getDashOffset() { return dashOffset; } public void setDashOffset(double dashOffset) { this.dashOffset = dashOffset; } /** * Returns the hashcode for this stroke. * * @return a hash code for this stroke. */ public int hashCode() { int hash = (int) Double.doubleToLongBits(width); hash = hash * 31 + join.ordinal(); hash = hash * 31 + cap.ordinal(); hash = hash * 31 + (int) Double.doubleToLongBits(miterLimit); if (dash != null) { hash = hash * 31 + (int) Double.doubleToLongBits(dashOffset); for (int i = 0; i < dash.length; i++) { hash = hash * 31 + (int) Double.doubleToLongBits(dash[i]); } } return hash; } /** * Returns true if this BasicStroke represents the same stroking operation * as the given argument. * *

* Tests if a specified object is equal to this Stroke by * first testing if it is a BasicStroke and then comparing * its width, join, cap, miter limit, dash, and dash phase attributes with * those of this Stroke. * * @param obj the specified object to compare to this Stroke * @return true if the width, join, cap, miter limit, dash, * and dash phase are the same for both objects; false * otherwise. */ public boolean equals(Object obj) { if (!(obj instanceof StrokeDesc)) { return false; } StrokeDesc bs = (StrokeDesc) obj; if (width != bs.width) { return false; } if (join != bs.join) { return false; } if (cap != bs.cap) { return false; } if (miterLimit != bs.miterLimit) { return false; } if (dash != null) { if (dashOffset != bs.dashOffset) { return false; } if (!java.util.Arrays.equals(dash, bs.dash)) { return false; } } else if (bs.dash != null) { return false; } return true; } // public String toStyleString() { // StringBuilder s = new StringBuilder(); // // s.append(CSSConstants.CSS_STROKE_PROPERTY); // s.append(':'); // s.append(paint); // if (!paint.equals(CSSConstants.CSS_NONE_VALUE)) { // s.append(';'); // s.append(CSSConstants.CSS_STROKE_OPACITY_PROPERTY); // s.append(':'); // s.append(opacity); // s.append(';'); // s.append(CSSConstants.CSS_STROKE_WIDTH_PROPERTY); // s.append(':'); // s.append(width); // s.append(unitSuffix); // if (dash.length > 0) { // s.append(';'); // s.append(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY); // s.append(':'); // appendDashArrayString(s); // s.append(';'); // s.append(CSSConstants.CSS_STROKE_DASHOFFSET_PROPERTY); // s.append(':'); // s.append(dashOffset); // } // s.append(';'); // s.append(CSSConstants.CSS_STROKE_LINECAP_PROPERTY); // s.append(':'); // s.append(cap.toString()); // s.append(';'); // s.append(CSSConstants.CSS_STROKE_LINEJOIN_PROPERTY); // s.append(':'); // s.append(join.toString()); // if (LineJoin.miter.equals(join)) { // s.append(';'); // s.append(CSSConstants.CSS_STROKE_MITERLIMIT_PROPERTY); // s.append(':'); // s.append(miterLimit); // } // } // s.append(';'); // // return s.toString(); // } public void appendDashArrayString(StringBuilder s) { if (dash.length > 0) { s.append(dash[0]); for (int i = 1; i < dash.length; ++i) { s.append(','); s.append(dash[i]); } } } public String dashArrayToString() { String s = ""; if (dash.length > 0) { s += dash[0]; for (int i = 1; i < dash.length; ++i) { s += ',' + dash[i]; } } return s; } }