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
028/**
029 * Defines the radii of each of the four corners of a BorderStroke. The
030 * CornerRadii class is immutable and therefore can be reused on multiple
031 * BorderStrokes. This class defines 8 different values, corresponding
032 * to the horizontal and vertical components of 4 quarter ellipses, which
033 * in turn define the curvature of the corners of the BorderStroke.
034 *
035 * TODO insert picture
036 */
037public class CornerRadii {
038    /**
039     * A CornerRadii which is entirely empty, indicating squared corners.
040     * This is the default value for a BorderStroke's radii.
041     */
042    public static final CornerRadii EMPTY = new CornerRadii(
043            0, 0, 0, 0, 0, 0, 0, 0,
044            false, false, false, false, false, false, false, false
045    );
046
047    /**
048     * The length of the horizontal radii of the top-left corner.
049     */
050    public final double getTopLeftHorizontalRadius() { return topLeftHorizontalRadius; }
051    private double topLeftHorizontalRadius;
052
053    /**
054     * The length of the vertical radii of the top-left corner.
055     */
056    public final double getTopLeftVerticalRadius() { return topLeftVerticalRadius; }
057    private double topLeftVerticalRadius;
058
059    /**
060     * The length of the vertical radii of the top-right corner.
061     */
062    public final double getTopRightVerticalRadius() { return topRightVerticalRadius; }
063    private double topRightVerticalRadius;
064
065    /**
066     * The length of the horizontal radii of the top-right corner.
067     */
068    public final double getTopRightHorizontalRadius() { return topRightHorizontalRadius; }
069    private double topRightHorizontalRadius;
070
071    /**
072     * The length of the horizontal radii of the bottom-right corner.
073     */
074    public final double getBottomRightHorizontalRadius() { return bottomRightHorizontalRadius; }
075    private double bottomRightHorizontalRadius;
076
077    /**
078     * The length of the vertical radii of the bottom-right corner.
079     */
080    public final double getBottomRightVerticalRadius() { return bottomRightVerticalRadius; }
081    private double bottomRightVerticalRadius;
082
083    /**
084     * The length of the vertical radii of the bottom-left corner.
085     */
086    public final double getBottomLeftVerticalRadius() { return bottomLeftVerticalRadius; }
087    private double bottomLeftVerticalRadius;
088
089    /**
090     * The length of the horizontal radii of the bottom-left corner.
091     */
092    public final double getBottomLeftHorizontalRadius() { return bottomLeftHorizontalRadius; }
093    private double bottomLeftHorizontalRadius;
094
095    /**
096     * indicates whether {@code topLeftHorizontalRadius} is interpreted as a value or a percentage.
097     */
098    private final boolean topLeftHorizontalRadiusAsPercentage;
099    public final boolean isTopLeftHorizontalRadiusAsPercentage() { return topLeftHorizontalRadiusAsPercentage; }
100
101    /**
102     * indicates whether {@code topLeftVerticalRadius} is interpreted as a value or a percentage.
103     */
104    private final boolean topLeftVerticalRadiusAsPercentage;
105    public final boolean isTopLeftVerticalRadiusAsPercentage() { return topLeftVerticalRadiusAsPercentage; }
106
107    /**
108     * indicates whether {@code topRightVerticalRadius} is interpreted as a value or a percentage.
109     */
110    private final boolean topRightVerticalRadiusAsPercentage;
111    public final boolean isTopRightVerticalRadiusAsPercentage() { return topRightVerticalRadiusAsPercentage; }
112
113    /**
114     * indicates whether {@code topRightHorizontalRadius} is interpreted as a value or a percentage.
115     */
116    private final boolean topRightHorizontalRadiusAsPercentage;
117    public final boolean isTopRightHorizontalRadiusAsPercentage() { return topRightHorizontalRadiusAsPercentage; }
118
119    /**
120     * indicates whether {@code bottomRightHorizontalRadius} is interpreted as a value or a percentage.
121     */
122    private final boolean bottomRightHorizontalRadiusAsPercentage;
123    public final boolean isBottomRightHorizontalRadiusAsPercentage() { return bottomRightHorizontalRadiusAsPercentage; }
124
125    /**
126     * indicates whether {@code bottomRightVerticalRadius} is interpreted as a value or a percentage.
127     */
128    private final boolean bottomRightVerticalRadiusAsPercentage;
129    public final boolean isBottomRightVerticalRadiusAsPercentage() { return bottomRightVerticalRadiusAsPercentage; }
130
131    /**
132     * indicates whether {@code bottomLeftVerticalRadius} is interpreted as a value or a percentage.
133     */
134    private final boolean bottomLeftVerticalRadiusAsPercentage;
135    public final boolean isBottomLeftVerticalRadiusAsPercentage() { return bottomLeftVerticalRadiusAsPercentage; }
136
137    /**
138     * indicates whether {@code bottomLeftHorizontalRadius} is interpreted as a value or a percentage.
139     */
140    private final boolean bottomLeftHorizontalRadiusAsPercentage;
141    public final boolean isBottomLeftHorizontalRadiusAsPercentage() { return bottomLeftHorizontalRadiusAsPercentage; }
142
143    final boolean hasPercentBasedRadii;
144
145    /**
146     * Indicates whether each corner radius is exactly the same, and each are either uniformly percentage-based
147     * or not.
148     */
149    final boolean uniform;
150    public final boolean isUniform() { return uniform; }
151
152    /**
153     * The cached hash code.
154     */
155    private final int hash;
156
157    /**
158     * Create a new CornerRadii with a single uniform radii value for all components of all
159     * corners. This constructor will create the CornerRadii such that none of the values are
160     * percentages.
161     *
162     * @param radius    The radii for each corner. Negative values are not allowed.
163     */
164    public CornerRadii(double radius) {
165        // As per the CSS Spec 5.1
166        if (radius < 0) {
167            throw new IllegalArgumentException("The radii value may not be < 0");
168        }
169        this.topLeftHorizontalRadius = this.topLeftVerticalRadius =
170                this.topRightVerticalRadius = this.topRightHorizontalRadius =
171                this.bottomRightHorizontalRadius = this.bottomRightVerticalRadius =
172                this.bottomLeftVerticalRadius = this.bottomLeftHorizontalRadius = radius;
173
174        this.topLeftHorizontalRadiusAsPercentage = this.topLeftVerticalRadiusAsPercentage =
175                this.topRightVerticalRadiusAsPercentage = this.topRightHorizontalRadiusAsPercentage =
176                this.bottomRightHorizontalRadiusAsPercentage = this.bottomRightVerticalRadiusAsPercentage =
177                this.bottomLeftVerticalRadiusAsPercentage = this.bottomLeftHorizontalRadiusAsPercentage = false;
178
179        hasPercentBasedRadii = false;
180        uniform = true;
181        this.hash = preComputeHash();
182    }
183
184    /**
185     * Create a new CornerRadii with the given radii for each corner. The value is
186     * interpreted either as being a percentage or not based on the {@code asPercent}
187     * argument.
188     *
189     * @param radius       The radii for each corner. Negative values are not allowed.
190     * @param asPercent    Whether the radii should be interpreted as a percentage.
191     */
192    public CornerRadii(double radius, boolean asPercent) {
193        if (radius < 0) {
194            throw new IllegalArgumentException("The radii value may not be < 0");
195        }
196        this.topLeftHorizontalRadius = this.topLeftVerticalRadius =
197                this.topRightVerticalRadius = this.topRightHorizontalRadius =
198                this.bottomRightHorizontalRadius = this.bottomRightVerticalRadius =
199                this.bottomLeftVerticalRadius = this.bottomLeftHorizontalRadius = radius;
200
201        this.topLeftHorizontalRadiusAsPercentage = this.topLeftVerticalRadiusAsPercentage =
202                this.topRightVerticalRadiusAsPercentage = this.topRightHorizontalRadiusAsPercentage =
203                this.bottomRightHorizontalRadiusAsPercentage = this.bottomRightVerticalRadiusAsPercentage =
204                this.bottomLeftVerticalRadiusAsPercentage = this.bottomLeftHorizontalRadiusAsPercentage = asPercent;
205
206        uniform = true;
207        hasPercentBasedRadii = asPercent;
208        this.hash = preComputeHash();
209    }
210
211    /**
212     * Create a new CornerRadii with uniform yet independent radii for each corner. That is, each corner
213     * can be specified independently, but the horizontal and vertical components of each corner is uniform.
214     *
215     * @param topLeft        The radii of the top-left corner. Negative numbers are not allowed.
216     * @param topRight       The radii of the top-right corner. Negative numbers are not allowed.
217     * @param bottomRight    The radii of the bottom-right corner. Negative numbers are not allowed.
218     * @param bottomLeft     The radii of the bottom-left corner. Negative numbers are not allowed.
219     * @param asPercent      Whether all four radii should be considered as values or percentages
220     */
221    public CornerRadii(double topLeft, double topRight, double bottomRight, double bottomLeft, boolean asPercent) {
222        if (topLeft < 0 || topRight < 0 || bottomRight < 0 || bottomLeft < 0) {
223            throw new IllegalArgumentException("No radii value may be < 0");
224        }
225
226        this.topLeftHorizontalRadius = this.topLeftVerticalRadius = topLeft;
227        this.topRightVerticalRadius = this.topRightHorizontalRadius = topRight;
228        this.bottomRightHorizontalRadius = this.bottomRightVerticalRadius = bottomRight;
229        this.bottomLeftVerticalRadius = this.bottomLeftHorizontalRadius = bottomLeft;
230        this.topLeftHorizontalRadiusAsPercentage = this.topLeftVerticalRadiusAsPercentage =
231                this.topRightVerticalRadiusAsPercentage = this.topRightHorizontalRadiusAsPercentage =
232                this.bottomRightHorizontalRadiusAsPercentage = this.bottomRightVerticalRadiusAsPercentage =
233                this.bottomLeftVerticalRadiusAsPercentage = this.bottomLeftHorizontalRadiusAsPercentage = asPercent;
234
235        uniform = topLeft == topRight && topLeft == bottomLeft && topLeft == bottomRight;
236        hasPercentBasedRadii = asPercent;
237        this.hash = preComputeHash();
238    }
239
240    /**
241     * Creates a new CornerRadii, allowing for specification of each component of each corner
242     * radii and whether each component should be treated as a value or percentage.
243     *
244     * @param topLeftHorizontalRadius
245     * @param topLeftVerticalRadius
246     * @param topRightVerticalRadius
247     * @param topRightHorizontalRadius
248     * @param bottomRightHorizontalRadius
249     * @param bottomRightVerticalRadius
250     * @param bottomLeftVerticalRadius
251     * @param bottomLeftHorizontalRadius
252     * @param topLeftHorizontalRadiusAsPercent
253     * @param topLeftVerticalRadiusAsPercent
254     * @param topRightVerticalRadiusAsPercent
255     * @param topRightHorizontalRadiusAsPercent
256     * @param bottomRightHorizontalRadiusAsPercent
257     * @param bottomRightVerticalRadiusAsPercent
258     * @param bottomLeftVerticalRadiusAsPercent
259     * @param bottomLeftHorizontalRadiusAsPercent
260     */
261    public CornerRadii(
262            double topLeftHorizontalRadius, double topLeftVerticalRadius, double topRightVerticalRadius, double topRightHorizontalRadius,
263            double bottomRightHorizontalRadius, double bottomRightVerticalRadius, double bottomLeftVerticalRadius, double bottomLeftHorizontalRadius,
264            boolean topLeftHorizontalRadiusAsPercent, boolean topLeftVerticalRadiusAsPercent, boolean topRightVerticalRadiusAsPercent,
265            boolean topRightHorizontalRadiusAsPercent, boolean bottomRightHorizontalRadiusAsPercent, boolean bottomRightVerticalRadiusAsPercent,
266            boolean bottomLeftVerticalRadiusAsPercent, boolean bottomLeftHorizontalRadiusAsPercent)
267    {
268        if (topLeftHorizontalRadius < 0 || topLeftVerticalRadius < 0 ||
269                topRightVerticalRadius < 0 || topRightHorizontalRadius < 0 ||
270                bottomRightHorizontalRadius < 0 || bottomRightVerticalRadius < 0 ||
271                bottomLeftVerticalRadius < 0 || bottomLeftHorizontalRadius < 0) {
272            throw new IllegalArgumentException("No radii value may be < 0");
273        }
274        this.topLeftHorizontalRadius = topLeftHorizontalRadius;
275        this.topLeftVerticalRadius = topLeftVerticalRadius;
276        this.topRightVerticalRadius = topRightVerticalRadius;
277        this.topRightHorizontalRadius = topRightHorizontalRadius;
278        this.bottomRightHorizontalRadius = bottomRightHorizontalRadius;
279        this.bottomRightVerticalRadius = bottomRightVerticalRadius;
280        this.bottomLeftVerticalRadius = bottomLeftVerticalRadius;
281        this.bottomLeftHorizontalRadius = bottomLeftHorizontalRadius;
282        this.topLeftHorizontalRadiusAsPercentage = topLeftHorizontalRadiusAsPercent;
283        this.topLeftVerticalRadiusAsPercentage = topLeftVerticalRadiusAsPercent;
284        this.topRightVerticalRadiusAsPercentage = topRightVerticalRadiusAsPercent;
285        this.topRightHorizontalRadiusAsPercentage = topRightHorizontalRadiusAsPercent;
286        this.bottomRightHorizontalRadiusAsPercentage = bottomRightHorizontalRadiusAsPercent;
287        this.bottomRightVerticalRadiusAsPercentage = bottomRightVerticalRadiusAsPercent;
288        this.bottomLeftVerticalRadiusAsPercentage = bottomLeftVerticalRadiusAsPercent;
289        this.bottomLeftHorizontalRadiusAsPercentage = bottomLeftHorizontalRadiusAsPercent;
290        this.hash = preComputeHash();
291        hasPercentBasedRadii = topLeftHorizontalRadiusAsPercent || topLeftVerticalRadiusAsPercent ||
292                topRightVerticalRadiusAsPercent || topRightHorizontalRadiusAsPercent ||
293                bottomRightHorizontalRadiusAsPercent || bottomRightVerticalRadiusAsPercent ||
294                bottomLeftVerticalRadiusAsPercent || bottomLeftHorizontalRadiusAsPercent;
295        uniform = topLeftHorizontalRadius == topRightHorizontalRadius &&
296                topLeftVerticalRadius == topRightVerticalRadius &&
297                topLeftHorizontalRadius == bottomRightHorizontalRadius &&
298                topLeftVerticalRadius == bottomRightVerticalRadius &&
299                topLeftHorizontalRadius == bottomLeftHorizontalRadius &&
300                topLeftVerticalRadius == bottomLeftVerticalRadius &&
301                topLeftHorizontalRadiusAsPercent == topRightHorizontalRadiusAsPercent &&
302                topLeftVerticalRadiusAsPercent == topRightVerticalRadiusAsPercent &&
303                topLeftHorizontalRadiusAsPercent == bottomRightHorizontalRadiusAsPercent &&
304                topLeftVerticalRadiusAsPercent == bottomRightVerticalRadiusAsPercent &&
305                topLeftHorizontalRadiusAsPercent == bottomLeftHorizontalRadiusAsPercent &&
306                topLeftVerticalRadiusAsPercent == bottomLeftVerticalRadiusAsPercent;
307    }
308
309    private int preComputeHash() {
310        int result;
311        long temp;
312        temp = topLeftHorizontalRadius != +0.0d ? Double.doubleToLongBits(topLeftHorizontalRadius) : 0L;
313        result = (int) (temp ^ (temp >>> 32));
314        temp = topLeftVerticalRadius != +0.0d ? Double.doubleToLongBits(topLeftVerticalRadius) : 0L;
315        result = 31 * result + (int) (temp ^ (temp >>> 32));
316        temp = topRightVerticalRadius != +0.0d ? Double.doubleToLongBits(topRightVerticalRadius) : 0L;
317        result = 31 * result + (int) (temp ^ (temp >>> 32));
318        temp = topRightHorizontalRadius != +0.0d ? Double.doubleToLongBits(topRightHorizontalRadius) : 0L;
319        result = 31 * result + (int) (temp ^ (temp >>> 32));
320        temp = bottomRightHorizontalRadius != +0.0d ? Double.doubleToLongBits(bottomRightHorizontalRadius) : 0L;
321        result = 31 * result + (int) (temp ^ (temp >>> 32));
322        temp = bottomRightVerticalRadius != +0.0d ? Double.doubleToLongBits(bottomRightVerticalRadius) : 0L;
323        result = 31 * result + (int) (temp ^ (temp >>> 32));
324        temp = bottomLeftVerticalRadius != +0.0d ? Double.doubleToLongBits(bottomLeftVerticalRadius) : 0L;
325        result = 31 * result + (int) (temp ^ (temp >>> 32));
326        temp = bottomLeftHorizontalRadius != +0.0d ? Double.doubleToLongBits(bottomLeftHorizontalRadius) : 0L;
327        result = 31 * result + (int) (temp ^ (temp >>> 32));
328        result = 31 * result + (topLeftHorizontalRadiusAsPercentage ? 1 : 0);
329        result = 31 * result + (topLeftVerticalRadiusAsPercentage ? 1 : 0);
330        result = 31 * result + (topRightVerticalRadiusAsPercentage ? 1 : 0);
331        result = 31 * result + (topRightHorizontalRadiusAsPercentage ? 1 : 0);
332        result = 31 * result + (bottomRightHorizontalRadiusAsPercentage ? 1 : 0);
333        result = 31 * result + (bottomRightVerticalRadiusAsPercentage ? 1 : 0);
334        result = 31 * result + (bottomLeftVerticalRadiusAsPercentage ? 1 : 0);
335        result = 31 * result + (bottomLeftHorizontalRadiusAsPercentage ? 1 : 0);
336        result = 31 * result + result;
337        return result;
338    }
339
340    /**
341     * @inheritDoc
342     */
343    @Override public boolean equals(Object o) {
344        if (this == o) return true;
345        if (o == null || getClass() != o.getClass()) return false;
346        CornerRadii that = (CornerRadii) o;
347        if (this.hash != that.hash) return false;
348
349        if (Double.compare(that.bottomLeftHorizontalRadius, bottomLeftHorizontalRadius) != 0) return false;
350        if (bottomLeftHorizontalRadiusAsPercentage != that.bottomLeftHorizontalRadiusAsPercentage) return false;
351        if (Double.compare(that.bottomLeftVerticalRadius, bottomLeftVerticalRadius) != 0) return false;
352        if (bottomLeftVerticalRadiusAsPercentage != that.bottomLeftVerticalRadiusAsPercentage) return false;
353        if (Double.compare(that.bottomRightVerticalRadius, bottomRightVerticalRadius) != 0) return false;
354        if (bottomRightVerticalRadiusAsPercentage != that.bottomRightVerticalRadiusAsPercentage) return false;
355        if (Double.compare(that.bottomRightHorizontalRadius, bottomRightHorizontalRadius) != 0) return false;
356        if (bottomRightHorizontalRadiusAsPercentage != that.bottomRightHorizontalRadiusAsPercentage) return false;
357        if (Double.compare(that.topLeftVerticalRadius, topLeftVerticalRadius) != 0) return false;
358        if (topLeftVerticalRadiusAsPercentage != that.topLeftVerticalRadiusAsPercentage) return false;
359        if (Double.compare(that.topLeftHorizontalRadius, topLeftHorizontalRadius) != 0) return false;
360        if (topLeftHorizontalRadiusAsPercentage != that.topLeftHorizontalRadiusAsPercentage) return false;
361        if (Double.compare(that.topRightHorizontalRadius, topRightHorizontalRadius) != 0) return false;
362        if (topRightHorizontalRadiusAsPercentage != that.topRightHorizontalRadiusAsPercentage) return false;
363        if (Double.compare(that.topRightVerticalRadius, topRightVerticalRadius) != 0) return false;
364        if (topRightVerticalRadiusAsPercentage != that.topRightVerticalRadiusAsPercentage) return false;
365
366        return true;
367    }
368
369    /**
370     * @inheritDoc
371     */
372    @Override public int hashCode() {
373        return hash;
374    }
375
376    @Override public String toString() {
377        if (isUniform()) {
378            return "CornerRadii [uniform radius = " + topLeftHorizontalRadius + "]";
379        }
380
381        return "CornerRadii [" +
382                (topLeftHorizontalRadius == topLeftVerticalRadius ?
383                    "topLeft=" + topLeftHorizontalRadius :
384                    "topLeftHorizontalRadius=" + topLeftHorizontalRadius +
385                    ", topLeftVerticalRadius=" + topLeftVerticalRadius) +
386                (topRightHorizontalRadius == topRightVerticalRadius ?
387                    ", topRight=" + topRightHorizontalRadius :
388                    ", topRightVerticalRadius=" + topRightVerticalRadius +
389                    ", topRightHorizontalRadius=" + topRightHorizontalRadius) +
390                (bottomRightHorizontalRadius == bottomRightVerticalRadius ?
391                    ", bottomRight=" + bottomRightHorizontalRadius :
392                    ", bottomRightHorizontalRadius=" + bottomRightHorizontalRadius +
393                    ", bottomRightVerticalRadius=" + bottomRightVerticalRadius) +
394                (bottomLeftHorizontalRadius == bottomLeftVerticalRadius ?
395                    ", bottomLeft=" + bottomLeftHorizontalRadius :
396                    ", bottomLeftVerticalRadius=" + bottomLeftVerticalRadius +
397                    ", bottomLeftHorizontalRadius=" + bottomLeftHorizontalRadius) +
398//                ", topLeftHorizontalRadiusAsPercentage=" + topLeftHorizontalRadiusAsPercentage +
399//                ", topLeftVerticalRadiusAsPercentage=" + topLeftVerticalRadiusAsPercentage +
400//                ", topRightVerticalRadiusAsPercentage=" + topRightVerticalRadiusAsPercentage +
401//                ", topRightHorizontalRadiusAsPercentage=" + topRightHorizontalRadiusAsPercentage +
402//                ", bottomRightHorizontalRadiusAsPercentage=" + bottomRightHorizontalRadiusAsPercentage +
403//                ", bottomRightVerticalRadiusAsPercentage=" + bottomRightVerticalRadiusAsPercentage +
404//                ", bottomLeftVerticalRadiusAsPercentage=" + bottomLeftVerticalRadiusAsPercentage +
405//                ", bottomLeftHorizontalRadiusAsPercentage=" + bottomLeftHorizontalRadiusAsPercentage +
406//                ", hasPercentBasedRadii=" + hasPercentBasedRadii +
407//                ", uniform=" + uniform +
408                ']';
409    }
410}