Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.layout;
027
028import java.util.ArrayList;
029import java.util.Collections;
030import java.util.List;
031import javafx.scene.shape.StrokeLineCap;
032import javafx.scene.shape.StrokeLineJoin;
033import javafx.scene.shape.StrokeType;
034
035/**
036 * Defines the style of the stroke to use on one side of a BorderStroke. There are
037 * several predefined styles, although the properties of these predefined styles may
038 * not match the settings used to ultimately draw them. Or you may create a new
039 * BorderStrokeStyle and define each of the stroke settings manually, similar
040 * to any {@link javafx.scene.shape.Shape}.
041 */
042public final class BorderStrokeStyle {
043    private static final List<Double> DOTTED_LIST = Collections.unmodifiableList(asList(0, 2));
044    private static final List<Double> DASHED_LIST = Collections.unmodifiableList(asList(2, 1.4));
045
046    /**
047     * Indicates that no stroke should be drawn.
048     */
049    public static final BorderStrokeStyle NONE = new BorderStrokeStyle(
050            StrokeType.INSIDE, StrokeLineJoin.MITER, StrokeLineCap.BUTT, 0, 0, null);
051
052    /**
053     * A predefined dotted pattern to be used for stroking
054     */
055    public static final BorderStrokeStyle DOTTED = new BorderStrokeStyle(
056            StrokeType.INSIDE, StrokeLineJoin.MITER, StrokeLineCap.ROUND, 10, 0, DOTTED_LIST);
057
058    /**
059     * A predefined dashed pattern to be used for stroking
060     */
061    public static final BorderStrokeStyle DASHED = new BorderStrokeStyle(
062            StrokeType.INSIDE, StrokeLineJoin.MITER, StrokeLineCap.BUTT, 10, 0, DASHED_LIST);
063
064    /**
065     * A predefined solid line to be used for stroking
066     */
067    public static final BorderStrokeStyle SOLID = new BorderStrokeStyle(
068            StrokeType.INSIDE, StrokeLineJoin.MITER, StrokeLineCap.BUTT, 10, 0, null);
069
070    /**
071     * Defines the direction (inside, outside, or both) that the strokeWidth
072     * is applied to the boundary of the shape.
073     *
074     * @defaultValue CENTERED
075     */
076    private final StrokeType type;
077    public final StrokeType getType() { return type; }
078
079    /**
080     * Defines the decoration applied where path segments meet.
081     * The value must have one of the following values:
082     * {@code StrokeLineJoin.BEVEL}, {@code StrokeLineJoin.MITER},
083     * and {@code StrokeLineJoin.ROUND}.
084     *
085     * @defaultValue MITER
086     */
087    private final StrokeLineJoin lineJoin;
088    public final StrokeLineJoin getLineJoin() { return lineJoin; }
089
090    /**
091     * The end cap style of this {@code Shape} as one of the following
092     * values that define possible end cap styles:
093     * {@code StrokeLineCap.BUTT}, {@code StrokeLineCap.ROUND},
094     * and  {@code StrokeLineCap.SQUARE}.
095     *
096     * @defaultValue SQUARE
097     */
098    private final StrokeLineCap lineCap;
099    public final StrokeLineCap getLineCap() { return lineCap; }
100
101    /**
102     * Defines the limit for the {@code StrokeLineJoin.MITER} line join style.
103     *
104     * @defaultValue 10
105     */
106    private final double miterLimit;
107    public final double getMiterLimit() { return miterLimit; }
108
109    /**
110     * Defines a distance specified in user coordinates that represents
111     * an offset into the dashing pattern. In other words, the dash phase
112     * defines the point in the dashing pattern that will correspond
113     * to the beginning of the stroke.
114     *
115     * @defaultValue 0
116     */
117    private final double dashOffset;
118    public final double getDashOffset() { return dashOffset; }
119
120    /**
121     * Defines the array representing the lengths of the dash segments.
122     * Alternate entries in the array represent the user space lengths
123     * of the opaque and transparent segments of the dashes.
124     * As the pen moves along the outline of the {@code Shape} to be stroked,
125     * the user space distance that the pen travels is accumulated.
126     * The distance value is used to index into the dash array.
127     * The pen is opaque when its current cumulative distance maps
128     * to an even element of the dash array and transparent otherwise.
129     * An empty dashArray indicates a solid line with no spaces.
130     * @defaultValue empty
131     */
132    private final List<Double> dashArray;
133    public final List<Double> getDashArray() { return dashArray; }
134
135    /**
136     * A cached hash code
137     */
138    private final int hash;
139
140    /**
141     * Creates a new BorderStrokeStyle.
142     *
143     * @param type    The type of stroke, whether rendered OUTSIDE, INSIDE, or CENTERED on the
144     *                border line. If null, defaults to CENTERED.
145     * @param lineJoin  The line join. If null, defaults to MITER
146     * @param lineCap   The line cap. If null, defaults to BUTT.
147     * @param miterLimit    The miter limit. 10 is a good default value.
148     * @param dashOffset    The dashOffset. 0 is a good default value.
149     * @param dashArray    The dash array. If null, defaults to an empty list.
150     */
151    public BorderStrokeStyle(StrokeType type, StrokeLineJoin lineJoin,
152                       StrokeLineCap lineCap, double miterLimit,
153                       double dashOffset, List<Double> dashArray) {
154        this.type = (type != null) ?
155                type : StrokeType.CENTERED;
156        this.lineJoin = (lineJoin != null) ?
157                lineJoin : StrokeLineJoin.MITER;
158        this.lineCap = (lineCap != null) ?
159                lineCap : StrokeLineCap.BUTT;
160        this.miterLimit = miterLimit;
161        this.dashOffset = dashOffset;
162
163        if (dashArray == null) {
164            this.dashArray = Collections.emptyList();
165        } else {
166            if (dashArray == DASHED_LIST || dashArray == DOTTED_LIST) {
167                // We want to use the SAME EXACT LIST in the case of DASHED_LIST or DOTTED_LIST
168                // so that code in NGRegion can execute specialized code paths for such cases.
169                this.dashArray = dashArray;
170            } else {
171                // Must not allow the passed in array to inadvertently mutate the
172                // state of this BorderStrokeStyle!
173                List<Double> list = new ArrayList<Double>(dashArray);
174                this.dashArray = Collections.unmodifiableList(list);
175            }
176        }
177
178        // Pre-compute the hash code. NOTE: all variables are prefixed with "this" so that we
179        // do not accidentally compute the hash based on the constructor arguments rather than
180        // based on the fields themselves!
181        int result;
182        long temp;
183        result = this.type.hashCode();
184//        result = 31 * result + (this == NONE ? 0 : 1); // TODO OH NO. NONE hasn't been assigned yet.
185        result = 31 * result + this.lineJoin.hashCode();
186        result = 31 * result + this.lineCap.hashCode();
187        temp = this.miterLimit != +0.0d ? Double.doubleToLongBits(this.miterLimit) : 0L;
188        result = 31 * result + (int) (temp ^ (temp >>> 32));
189        temp = this.dashOffset != +0.0d ? Double.doubleToLongBits(this.dashOffset) : 0L;
190        result = 31 * result + (int) (temp ^ (temp >>> 32));
191        result = 31 * result + this.dashArray.hashCode();
192        hash = result;
193    }
194
195    /**
196     * @inheritDoc
197     */
198    @Override public String toString() {
199        if (this == NONE) {
200            return "BorderStyle.NONE";
201        } else if (this == DASHED) {
202            return "BorderStyle.DASHED";
203        } else if (this == DOTTED) {
204            return "BorderStyle.DOTTED";
205        } else if (this == SOLID) {
206            return "BorderStyle.SOLID";
207        } else {
208            StringBuilder buffer = new StringBuilder();
209            buffer.append("BorderStyle: ");
210            buffer.append(type);
211            buffer.append(", ");
212            buffer.append(lineJoin);
213            buffer.append(", ");
214            buffer.append(lineCap);
215            buffer.append(", ");
216            buffer.append(miterLimit);
217            buffer.append(", ");
218            buffer.append(dashOffset);
219            buffer.append(", [");
220            if (dashArray != null) {
221                buffer.append(dashArray);
222            }
223            buffer.append("]");
224            return buffer.toString();
225        }
226    }
227
228    /**
229     * @inheritDoc
230     */
231    @Override public boolean equals(Object o) {
232        if (this == o) return true;
233        if ((this == NONE && o != NONE) || (o == NONE && this != NONE)) return false;
234        if (o == null || getClass() != o.getClass()) return false;
235        BorderStrokeStyle that = (BorderStrokeStyle) o;
236        if (this.hash != that.hash) return false;
237        if (Double.compare(that.dashOffset, dashOffset) != 0) return false;
238        if (Double.compare(that.miterLimit, miterLimit) != 0) return false;
239        if (!dashArray.equals(that.dashArray)) return false;
240        if (lineCap != that.lineCap) return false;
241        if (lineJoin != that.lineJoin) return false;
242        if (type != that.type) return false;
243
244        return true;
245    }
246
247    /**
248     * @inheritDoc
249     */
250    @Override public int hashCode() {
251        return hash;
252    }
253
254    private static List<Double> asList(double... items) {
255        List<Double> list = new ArrayList<Double>(items.length);
256        for (int i=0; i<items.length; i++) {
257            list.add(items[i]);
258        }
259        return list;
260    }
261}