Showing the changes to java/pkg/java/awt/geom (76 of 18352 files). Show all
Unified · Split
Binary file not shown.
| @@ -0,0 +1,3923 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.beans.ConstructorProperties; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * The <code>AffineTransform</code> class represents a 2D affine transform | ||
| 33 | * that performs a linear mapping from 2D coordinates to other 2D | ||
| 34 | * coordinates that preserves the "straightness" and | ||
| 35 | * "parallelness" of lines. Affine transformations can be constructed | ||
| 36 | * using sequences of translations, scales, flips, rotations, and shears. | ||
| 37 | * <p> | ||
| 38 | * Such a coordinate transformation can be represented by a 3 row by | ||
| 39 | * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix | ||
| 40 | * transforms source coordinates {@code (x,y)} into | ||
| 41 | * destination coordinates {@code (x',y')} by considering | ||
| 42 | * them to be a column vector and multiplying the coordinate vector | ||
| 43 | * by the matrix according to the following process: | ||
| 44 | * <pre> | ||
| 45 | * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ] | ||
| 46 | * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ] | ||
| 47 | * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] | ||
| 48 | * </pre> | ||
| 49 | * <p> | ||
| 50 | * <a name="quadrantapproximation"><h4>Handling 90-Degree Rotations</h4></a> | ||
| 51 | * <p> | ||
| 52 | * In some variations of the <code>rotate</code> methods in the | ||
| 53 | * <code>AffineTransform</code> class, a double-precision argument | ||
| 54 | * specifies the angle of rotation in radians. | ||
| 55 | * These methods have special handling for rotations of approximately | ||
| 56 | * 90 degrees (including multiples such as 180, 270, and 360 degrees), | ||
| 57 | * so that the common case of quadrant rotation is handled more | ||
| 58 | * efficiently. | ||
| 59 | * This special handling can cause angles very close to multiples of | ||
| 60 | * 90 degrees to be treated as if they were exact multiples of | ||
| 61 | * 90 degrees. | ||
| 62 | * For small multiples of 90 degrees the range of angles treated | ||
| 63 | * as a quadrant rotation is approximately 0.00000121 degrees wide. | ||
| 64 | * This section explains why such special care is needed and how | ||
| 65 | * it is implemented. | ||
| 66 | * <p> | ||
| 67 | * Since 90 degrees is represented as <code>PI/2</code> in radians, | ||
| 68 | * and since PI is a transcendental (and therefore irrational) number, | ||
| 69 | * it is not possible to exactly represent a multiple of 90 degrees as | ||
| 70 | * an exact double precision value measured in radians. | ||
| 71 | * As a result it is theoretically impossible to describe quadrant | ||
| 72 | * rotations (90, 180, 270 or 360 degrees) using these values. | ||
| 73 | * Double precision floating point values can get very close to | ||
| 74 | * non-zero multiples of <code>PI/2</code> but never close enough | ||
| 75 | * for the sine or cosine to be exactly 0.0, 1.0 or -1.0. | ||
| 76 | * The implementations of <code>Math.sin()</code> and | ||
| 77 | * <code>Math.cos()</code> correspondingly never return 0.0 | ||
| 78 | * for any case other than <code>Math.sin(0.0)</code>. | ||
| 79 | * These same implementations do, however, return exactly 1.0 and | ||
| 80 | * -1.0 for some range of numbers around each multiple of 90 | ||
| 81 | * degrees since the correct answer is so close to 1.0 or -1.0 that | ||
| 82 | * the double precision significand cannot represent the difference | ||
| 83 | * as accurately as it can for numbers that are near 0.0. | ||
| 84 | * <p> | ||
| 85 | * The net result of these issues is that if the | ||
| 86 | * <code>Math.sin()</code> and <code>Math.cos()</code> methods | ||
| 87 | * are used to directly generate the values for the matrix modifications | ||
| 88 | * during these radian-based rotation operations then the resulting | ||
| 89 | * transform is never strictly classifiable as a quadrant rotation | ||
| 90 | * even for a simple case like <code>rotate(Math.PI/2.0)</code>, | ||
| 91 | * due to minor variations in the matrix caused by the non-0.0 values | ||
| 92 | * obtained for the sine and cosine. | ||
| 93 | * If these transforms are not classified as quadrant rotations then | ||
| 94 | * subsequent code which attempts to optimize further operations based | ||
| 95 | * upon the type of the transform will be relegated to its most general | ||
| 96 | * implementation. | ||
| 97 | * <p> | ||
| 98 | * Because quadrant rotations are fairly common, | ||
| 99 | * this class should handle these cases reasonably quickly, both in | ||
| 100 | * applying the rotations to the transform and in applying the resulting | ||
| 101 | * transform to the coordinates. | ||
| 102 | * To facilitate this optimal handling, the methods which take an angle | ||
| 103 | * of rotation measured in radians attempt to detect angles that are | ||
| 104 | * intended to be quadrant rotations and treat them as such. | ||
| 105 | * These methods therefore treat an angle <em>theta</em> as a quadrant | ||
| 106 | * rotation if either <code>Math.sin(<em>theta</em>)</code> or | ||
| 107 | * <code>Math.cos(<em>theta</em>)</code> returns exactly 1.0 or -1.0. | ||
| 108 | * As a rule of thumb, this property holds true for a range of | ||
| 109 | * approximately 0.0000000211 radians (or 0.00000121 degrees) around | ||
| 110 | * small multiples of <code>Math.PI/2.0</code>. | ||
| 111 | * | ||
| 112 | * @author Jim Graham | ||
| 113 | * @since 1.2 | ||
| 114 | */ | ||
| 115 | public class AffineTransform implements Cloneable, java.io.Serializable { | ||
| 116 | |||
| 117 | /* | ||
| 118 | * This constant is only useful for the cached type field. | ||
| 119 | * It indicates that the type has been decached and must be recalculated. | ||
| 120 | */ | ||
| 121 | private static final int TYPE_UNKNOWN = -1; | ||
| 122 | |||
| 123 | /** | ||
| 124 | * This constant indicates that the transform defined by this object | ||
| 125 | * is an identity transform. | ||
| 126 | * An identity transform is one in which the output coordinates are | ||
| 127 | * always the same as the input coordinates. | ||
| 128 | * If this transform is anything other than the identity transform, | ||
| 129 | * the type will either be the constant GENERAL_TRANSFORM or a | ||
| 130 | * combination of the appropriate flag bits for the various coordinate | ||
| 131 | * conversions that this transform performs. | ||
| 132 | * @see #TYPE_TRANSLATION | ||
| 133 | * @see #TYPE_UNIFORM_SCALE | ||
| 134 | * @see #TYPE_GENERAL_SCALE | ||
| 135 | * @see #TYPE_FLIP | ||
| 136 | * @see #TYPE_QUADRANT_ROTATION | ||
| 137 | * @see #TYPE_GENERAL_ROTATION | ||
| 138 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 139 | * @see #getType | ||
| 140 | * @since 1.2 | ||
| 141 | */ | ||
| 142 | public static final int TYPE_IDENTITY = 0; | ||
| 143 | |||
| 144 | /** | ||
| 145 | * This flag bit indicates that the transform defined by this object | ||
| 146 | * performs a translation in addition to the conversions indicated | ||
| 147 | * by other flag bits. | ||
| 148 | * A translation moves the coordinates by a constant amount in x | ||
| 149 | * and y without changing the length or angle of vectors. | ||
| 150 | * @see #TYPE_IDENTITY | ||
| 151 | * @see #TYPE_UNIFORM_SCALE | ||
| 152 | * @see #TYPE_GENERAL_SCALE | ||
| 153 | * @see #TYPE_FLIP | ||
| 154 | * @see #TYPE_QUADRANT_ROTATION | ||
| 155 | * @see #TYPE_GENERAL_ROTATION | ||
| 156 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 157 | * @see #getType | ||
| 158 | * @since 1.2 | ||
| 159 | */ | ||
| 160 | public static final int TYPE_TRANSLATION = 1; | ||
| 161 | |||
| 162 | /** | ||
| 163 | * This flag bit indicates that the transform defined by this object | ||
| 164 | * performs a uniform scale in addition to the conversions indicated | ||
| 165 | * by other flag bits. | ||
| 166 | * A uniform scale multiplies the length of vectors by the same amount | ||
| 167 | * in both the x and y directions without changing the angle between | ||
| 168 | * vectors. | ||
| 169 | * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag. | ||
| 170 | * @see #TYPE_IDENTITY | ||
| 171 | * @see #TYPE_TRANSLATION | ||
| 172 | * @see #TYPE_GENERAL_SCALE | ||
| 173 | * @see #TYPE_FLIP | ||
| 174 | * @see #TYPE_QUADRANT_ROTATION | ||
| 175 | * @see #TYPE_GENERAL_ROTATION | ||
| 176 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 177 | * @see #getType | ||
| 178 | * @since 1.2 | ||
| 179 | */ | ||
| 180 | public static final int TYPE_UNIFORM_SCALE = 2; | ||
| 181 | |||
| 182 | /** | ||
| 183 | * This flag bit indicates that the transform defined by this object | ||
| 184 | * performs a general scale in addition to the conversions indicated | ||
| 185 | * by other flag bits. | ||
| 186 | * A general scale multiplies the length of vectors by different | ||
| 187 | * amounts in the x and y directions without changing the angle | ||
| 188 | * between perpendicular vectors. | ||
| 189 | * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag. | ||
| 190 | * @see #TYPE_IDENTITY | ||
| 191 | * @see #TYPE_TRANSLATION | ||
| 192 | * @see #TYPE_UNIFORM_SCALE | ||
| 193 | * @see #TYPE_FLIP | ||
| 194 | * @see #TYPE_QUADRANT_ROTATION | ||
| 195 | * @see #TYPE_GENERAL_ROTATION | ||
| 196 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 197 | * @see #getType | ||
| 198 | * @since 1.2 | ||
| 199 | */ | ||
| 200 | public static final int TYPE_GENERAL_SCALE = 4; | ||
| 201 | |||
| 202 | /** | ||
| 203 | * This constant is a bit mask for any of the scale flag bits. | ||
| 204 | * @see #TYPE_UNIFORM_SCALE | ||
| 205 | * @see #TYPE_GENERAL_SCALE | ||
| 206 | * @since 1.2 | ||
| 207 | */ | ||
| 208 | public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE | | ||
| 209 | TYPE_GENERAL_SCALE); | ||
| 210 | |||
| 211 | /** | ||
| 212 | * This flag bit indicates that the transform defined by this object | ||
| 213 | * performs a mirror image flip about some axis which changes the | ||
| 214 | * normally right handed coordinate system into a left handed | ||
| 215 | * system in addition to the conversions indicated by other flag bits. | ||
| 216 | * A right handed coordinate system is one where the positive X | ||
| 217 | * axis rotates counterclockwise to overlay the positive Y axis | ||
| 218 | * similar to the direction that the fingers on your right hand | ||
| 219 | * curl when you stare end on at your thumb. | ||
| 220 | * A left handed coordinate system is one where the positive X | ||
| 221 | * axis rotates clockwise to overlay the positive Y axis similar | ||
| 222 | * to the direction that the fingers on your left hand curl. | ||
| 223 | * There is no mathematical way to determine the angle of the | ||
| 224 | * original flipping or mirroring transformation since all angles | ||
| 225 | * of flip are identical given an appropriate adjusting rotation. | ||
| 226 | * @see #TYPE_IDENTITY | ||
| 227 | * @see #TYPE_TRANSLATION | ||
| 228 | * @see #TYPE_UNIFORM_SCALE | ||
| 229 | * @see #TYPE_GENERAL_SCALE | ||
| 230 | * @see #TYPE_QUADRANT_ROTATION | ||
| 231 | * @see #TYPE_GENERAL_ROTATION | ||
| 232 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 233 | * @see #getType | ||
| 234 | * @since 1.2 | ||
| 235 | */ | ||
| 236 | public static final int TYPE_FLIP = 64; | ||
| 237 | /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public | ||
| 238 | * circulation and the flag bits could no longer be conveniently | ||
| 239 | * renumbered without introducing binary incompatibility in outside | ||
| 240 | * code. | ||
| 241 | */ | ||
| 242 | |||
| 243 | /** | ||
| 244 | * This flag bit indicates that the transform defined by this object | ||
| 245 | * performs a quadrant rotation by some multiple of 90 degrees in | ||
| 246 | * addition to the conversions indicated by other flag bits. | ||
| 247 | * A rotation changes the angles of vectors by the same amount | ||
| 248 | * regardless of the original direction of the vector and without | ||
| 249 | * changing the length of the vector. | ||
| 250 | * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag. | ||
| 251 | * @see #TYPE_IDENTITY | ||
| 252 | * @see #TYPE_TRANSLATION | ||
| 253 | * @see #TYPE_UNIFORM_SCALE | ||
| 254 | * @see #TYPE_GENERAL_SCALE | ||
| 255 | * @see #TYPE_FLIP | ||
| 256 | * @see #TYPE_GENERAL_ROTATION | ||
| 257 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 258 | * @see #getType | ||
| 259 | * @since 1.2 | ||
| 260 | */ | ||
| 261 | public static final int TYPE_QUADRANT_ROTATION = 8; | ||
| 262 | |||
| 263 | /** | ||
| 264 | * This flag bit indicates that the transform defined by this object | ||
| 265 | * performs a rotation by an arbitrary angle in addition to the | ||
| 266 | * conversions indicated by other flag bits. | ||
| 267 | * A rotation changes the angles of vectors by the same amount | ||
| 268 | * regardless of the original direction of the vector and without | ||
| 269 | * changing the length of the vector. | ||
| 270 | * This flag bit is mutually exclusive with the | ||
| 271 | * TYPE_QUADRANT_ROTATION flag. | ||
| 272 | * @see #TYPE_IDENTITY | ||
| 273 | * @see #TYPE_TRANSLATION | ||
| 274 | * @see #TYPE_UNIFORM_SCALE | ||
| 275 | * @see #TYPE_GENERAL_SCALE | ||
| 276 | * @see #TYPE_FLIP | ||
| 277 | * @see #TYPE_QUADRANT_ROTATION | ||
| 278 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 279 | * @see #getType | ||
| 280 | * @since 1.2 | ||
| 281 | */ | ||
| 282 | public static final int TYPE_GENERAL_ROTATION = 16; | ||
| 283 | |||
| 284 | /** | ||
| 285 | * This constant is a bit mask for any of the rotation flag bits. | ||
| 286 | * @see #TYPE_QUADRANT_ROTATION | ||
| 287 | * @see #TYPE_GENERAL_ROTATION | ||
| 288 | * @since 1.2 | ||
| 289 | */ | ||
| 290 | public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION | | ||
| 291 | TYPE_GENERAL_ROTATION); | ||
| 292 | |||
| 293 | /** | ||
| 294 | * This constant indicates that the transform defined by this object | ||
| 295 | * performs an arbitrary conversion of the input coordinates. | ||
| 296 | * If this transform can be classified by any of the above constants, | ||
| 297 | * the type will either be the constant TYPE_IDENTITY or a | ||
| 298 | * combination of the appropriate flag bits for the various coordinate | ||
| 299 | * conversions that this transform performs. | ||
| 300 | * @see #TYPE_IDENTITY | ||
| 301 | * @see #TYPE_TRANSLATION | ||
| 302 | * @see #TYPE_UNIFORM_SCALE | ||
| 303 | * @see #TYPE_GENERAL_SCALE | ||
| 304 | * @see #TYPE_FLIP | ||
| 305 | * @see #TYPE_QUADRANT_ROTATION | ||
| 306 | * @see #TYPE_GENERAL_ROTATION | ||
| 307 | * @see #getType | ||
| 308 | * @since 1.2 | ||
| 309 | */ | ||
| 310 | public static final int TYPE_GENERAL_TRANSFORM = 32; | ||
| 311 | |||
| 312 | /** | ||
| 313 | * This constant is used for the internal state variable to indicate | ||
| 314 | * that no calculations need to be performed and that the source | ||
| 315 | * coordinates only need to be copied to their destinations to | ||
| 316 | * complete the transformation equation of this transform. | ||
| 317 | * @see #APPLY_TRANSLATE | ||
| 318 | * @see #APPLY_SCALE | ||
| 319 | * @see #APPLY_SHEAR | ||
| 320 | * @see #state | ||
| 321 | */ | ||
| 322 | static final int APPLY_IDENTITY = 0; | ||
| 323 | |||
| 324 | /** | ||
| 325 | * This constant is used for the internal state variable to indicate | ||
| 326 | * that the translation components of the matrix (m02 and m12) need | ||
| 327 | * to be added to complete the transformation equation of this transform. | ||
| 328 | * @see #APPLY_IDENTITY | ||
| 329 | * @see #APPLY_SCALE | ||
| 330 | * @see #APPLY_SHEAR | ||
| 331 | * @see #state | ||
| 332 | */ | ||
| 333 | static final int APPLY_TRANSLATE = 1; | ||
| 334 | |||
| 335 | /** | ||
| 336 | * This constant is used for the internal state variable to indicate | ||
| 337 | * that the scaling components of the matrix (m00 and m11) need | ||
| 338 | * to be factored in to complete the transformation equation of | ||
| 339 | * this transform. If the APPLY_SHEAR bit is also set then it | ||
| 340 | * indicates that the scaling components are not both 0.0. If the | ||
| 341 | * APPLY_SHEAR bit is not also set then it indicates that the | ||
| 342 | * scaling components are not both 1.0. If neither the APPLY_SHEAR | ||
| 343 | * nor the APPLY_SCALE bits are set then the scaling components | ||
| 344 | * are both 1.0, which means that the x and y components contribute | ||
| 345 | * to the transformed coordinate, but they are not multiplied by | ||
| 346 | * any scaling factor. | ||
| 347 | * @see #APPLY_IDENTITY | ||
| 348 | * @see #APPLY_TRANSLATE | ||
| 349 | * @see #APPLY_SHEAR | ||
| 350 | * @see #state | ||
| 351 | */ | ||
| 352 | static final int APPLY_SCALE = 2; | ||
| 353 | |||
| 354 | /** | ||
| 355 | * This constant is used for the internal state variable to indicate | ||
| 356 | * that the shearing components of the matrix (m01 and m10) need | ||
| 357 | * to be factored in to complete the transformation equation of this | ||
| 358 | * transform. The presence of this bit in the state variable changes | ||
| 359 | * the interpretation of the APPLY_SCALE bit as indicated in its | ||
| 360 | * documentation. | ||
| 361 | * @see #APPLY_IDENTITY | ||
| 362 | * @see #APPLY_TRANSLATE | ||
| 363 | * @see #APPLY_SCALE | ||
| 364 | * @see #state | ||
| 365 | */ | ||
| 366 | static final int APPLY_SHEAR = 4; | ||
| 367 | |||
| 368 | /* | ||
| 369 | * For methods which combine together the state of two separate | ||
| 370 | * transforms and dispatch based upon the combination, these constants | ||
| 371 | * specify how far to shift one of the states so that the two states | ||
| 372 | * are mutually non-interfering and provide constants for testing the | ||
| 373 | * bits of the shifted (HI) state. The methods in this class use | ||
| 374 | * the convention that the state of "this" transform is unshifted and | ||
| 375 | * the state of the "other" or "argument" transform is shifted (HI). | ||
| 376 | */ | ||
| 377 | private static final int HI_SHIFT = 3; | ||
| 378 | private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT; | ||
| 379 | private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT; | ||
| 380 | private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT; | ||
| 381 | private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT; | ||
| 382 | |||
| 383 | /** | ||
| 384 | * The X coordinate scaling element of the 3x3 | ||
| 385 | * affine transformation matrix. | ||
| 386 | * | ||
| 387 | * @serial | ||
| 388 | */ | ||
| 389 | double m00; | ||
| 390 | |||
| 391 | /** | ||
| 392 | * The Y coordinate shearing element of the 3x3 | ||
| 393 | * affine transformation matrix. | ||
| 394 | * | ||
| 395 | * @serial | ||
| 396 | */ | ||
| 397 | double m10; | ||
| 398 | |||
| 399 | /** | ||
| 400 | * The X coordinate shearing element of the 3x3 | ||
| 401 | * affine transformation matrix. | ||
| 402 | * | ||
| 403 | * @serial | ||
| 404 | */ | ||
| 405 | double m01; | ||
| 406 | |||
| 407 | /** | ||
| 408 | * The Y coordinate scaling element of the 3x3 | ||
| 409 | * affine transformation matrix. | ||
| 410 | * | ||
| 411 | * @serial | ||
| 412 | */ | ||
| 413 | double m11; | ||
| 414 | |||
| 415 | /** | ||
| 416 | * The X coordinate of the translation element of the | ||
| 417 | * 3x3 affine transformation matrix. | ||
| 418 | * | ||
| 419 | * @serial | ||
| 420 | */ | ||
| 421 | double m02; | ||
| 422 | |||
| 423 | /** | ||
| 424 | * The Y coordinate of the translation element of the | ||
| 425 | * 3x3 affine transformation matrix. | ||
| 426 | * | ||
| 427 | * @serial | ||
| 428 | */ | ||
| 429 | double m12; | ||
| 430 | |||
| 431 | /** | ||
| 432 | * This field keeps track of which components of the matrix need to | ||
| 433 | * be applied when performing a transformation. | ||
| 434 | * @see #APPLY_IDENTITY | ||
| 435 | * @see #APPLY_TRANSLATE | ||
| 436 | * @see #APPLY_SCALE | ||
| 437 | * @see #APPLY_SHEAR | ||
| 438 | */ | ||
| 439 | transient int state; | ||
| 440 | |||
| 441 | /** | ||
| 442 | * This field caches the current transformation type of the matrix. | ||
| 443 | * @see #TYPE_IDENTITY | ||
| 444 | * @see #TYPE_TRANSLATION | ||
| 445 | * @see #TYPE_UNIFORM_SCALE | ||
| 446 | * @see #TYPE_GENERAL_SCALE | ||
| 447 | * @see #TYPE_FLIP | ||
| 448 | * @see #TYPE_QUADRANT_ROTATION | ||
| 449 | * @see #TYPE_GENERAL_ROTATION | ||
| 450 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 451 | * @see #TYPE_UNKNOWN | ||
| 452 | * @see #getType | ||
| 453 | */ | ||
| 454 | private transient int type; | ||
| 455 | |||
| 456 | private AffineTransform(double m00, double m10, | ||
| 457 | double m01, double m11, | ||
| 458 | double m02, double m12, | ||
| 459 | int state) { | ||
| 460 | this.m00 = m00; | ||
| 461 | this.m10 = m10; | ||
| 462 | this.m01 = m01; | ||
| 463 | this.m11 = m11; | ||
| 464 | this.m02 = m02; | ||
| 465 | this.m12 = m12; | ||
| 466 | this.state = state; | ||
| 467 | this.type = TYPE_UNKNOWN; | ||
| 468 | } | ||
| 469 | |||
| 470 | /** | ||
| 471 | * Constructs a new <code>AffineTransform</code> representing the | ||
| 472 | * Identity transformation. | ||
| 473 | * @since 1.2 | ||
| 474 | */ | ||
| 475 | public AffineTransform() { | ||
| 476 | m00 = m11 = 1.0; | ||
| 477 | // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */ | ||
| 478 | // state = APPLY_IDENTITY; /* Not needed. */ | ||
| 479 | // type = TYPE_IDENTITY; /* Not needed. */ | ||
| 480 | } | ||
| 481 | |||
| 482 | /** | ||
| 483 | * Constructs a new <code>AffineTransform</code> that is a copy of | ||
| 484 | * the specified <code>AffineTransform</code> object. | ||
| 485 | * @param Tx the <code>AffineTransform</code> object to copy | ||
| 486 | * @since 1.2 | ||
| 487 | */ | ||
| 488 | public AffineTransform(AffineTransform Tx) { | ||
| 489 | this.m00 = Tx.m00; | ||
| 490 | this.m10 = Tx.m10; | ||
| 491 | this.m01 = Tx.m01; | ||
| 492 | this.m11 = Tx.m11; | ||
| 493 | this.m02 = Tx.m02; | ||
| 494 | this.m12 = Tx.m12; | ||
| 495 | this.state = Tx.state; | ||
| 496 | this.type = Tx.type; | ||
| 497 | } | ||
| 498 | |||
| 499 | /** | ||
| 500 | * Constructs a new <code>AffineTransform</code> from 6 floating point | ||
| 501 | * values representing the 6 specifiable entries of the 3x3 | ||
| 502 | * transformation matrix. | ||
| 503 | * | ||
| 504 | * @param m00 the X coordinate scaling element of the 3x3 matrix | ||
| 505 | * @param m10 the Y coordinate shearing element of the 3x3 matrix | ||
| 506 | * @param m01 the X coordinate shearing element of the 3x3 matrix | ||
| 507 | * @param m11 the Y coordinate scaling element of the 3x3 matrix | ||
| 508 | * @param m02 the X coordinate translation element of the 3x3 matrix | ||
| 509 | * @param m12 the Y coordinate translation element of the 3x3 matrix | ||
| 510 | * @since 1.2 | ||
| 511 | */ | ||
| 512 | @ConstructorProperties({ "scaleX", "shearY", "shearX", "scaleY", "translateX", "translateY" }) | ||
| 513 | public AffineTransform(float m00, float m10, | ||
| 514 | float m01, float m11, | ||
| 515 | float m02, float m12) { | ||
| 516 | this.m00 = m00; | ||
| 517 | this.m10 = m10; | ||
| 518 | this.m01 = m01; | ||
| 519 | this.m11 = m11; | ||
| 520 | this.m02 = m02; | ||
| 521 | this.m12 = m12; | ||
| 522 | updateState(); | ||
| 523 | } | ||
| 524 | |||
| 525 | /** | ||
| 526 | * Constructs a new <code>AffineTransform</code> from an array of | ||
| 527 | * floating point values representing either the 4 non-translation | ||
| 528 | * enries or the 6 specifiable entries of the 3x3 transformation | ||
| 529 | * matrix. The values are retrieved from the array as | ||
| 530 | * { m00 m10 m01 m11 [m02 m12]}. | ||
| 531 | * @param flatmatrix the float array containing the values to be set | ||
| 532 | * in the new <code>AffineTransform</code> object. The length of the | ||
| 533 | * array is assumed to be at least 4. If the length of the array is | ||
| 534 | * less than 6, only the first 4 values are taken. If the length of | ||
| 535 | * the array is greater than 6, the first 6 values are taken. | ||
| 536 | * @since 1.2 | ||
| 537 | */ | ||
| 538 | public AffineTransform(float[] flatmatrix) { | ||
| 539 | m00 = flatmatrix[0]; | ||
| 540 | m10 = flatmatrix[1]; | ||
| 541 | m01 = flatmatrix[2]; | ||
| 542 | m11 = flatmatrix[3]; | ||
| 543 | if (flatmatrix.length > 5) { | ||
| 544 | m02 = flatmatrix[4]; | ||
| 545 | m12 = flatmatrix[5]; | ||
| 546 | } | ||
| 547 | updateState(); | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * Constructs a new <code>AffineTransform</code> from 6 double | ||
| 552 | * precision values representing the 6 specifiable entries of the 3x3 | ||
| 553 | * transformation matrix. | ||
| 554 | * | ||
| 555 | * @param m00 the X coordinate scaling element of the 3x3 matrix | ||
| 556 | * @param m10 the Y coordinate shearing element of the 3x3 matrix | ||
| 557 | * @param m01 the X coordinate shearing element of the 3x3 matrix | ||
| 558 | * @param m11 the Y coordinate scaling element of the 3x3 matrix | ||
| 559 | * @param m02 the X coordinate translation element of the 3x3 matrix | ||
| 560 | * @param m12 the Y coordinate translation element of the 3x3 matrix | ||
| 561 | * @since 1.2 | ||
| 562 | */ | ||
| 563 | public AffineTransform(double m00, double m10, | ||
| 564 | double m01, double m11, | ||
| 565 | double m02, double m12) { | ||
| 566 | this.m00 = m00; | ||
| 567 | this.m10 = m10; | ||
| 568 | this.m01 = m01; | ||
| 569 | this.m11 = m11; | ||
| 570 | this.m02 = m02; | ||
| 571 | this.m12 = m12; | ||
| 572 | updateState(); | ||
| 573 | } | ||
| 574 | |||
| 575 | /** | ||
| 576 | * Constructs a new <code>AffineTransform</code> from an array of | ||
| 577 | * double precision values representing either the 4 non-translation | ||
| 578 | * entries or the 6 specifiable entries of the 3x3 transformation | ||
| 579 | * matrix. The values are retrieved from the array as | ||
| 580 | * { m00 m10 m01 m11 [m02 m12]}. | ||
| 581 | * @param flatmatrix the double array containing the values to be set | ||
| 582 | * in the new <code>AffineTransform</code> object. The length of the | ||
| 583 | * array is assumed to be at least 4. If the length of the array is | ||
| 584 | * less than 6, only the first 4 values are taken. If the length of | ||
| 585 | * the array is greater than 6, the first 6 values are taken. | ||
| 586 | * @since 1.2 | ||
| 587 | */ | ||
| 588 | public AffineTransform(double[] flatmatrix) { | ||
| 589 | m00 = flatmatrix[0]; | ||
| 590 | m10 = flatmatrix[1]; | ||
| 591 | m01 = flatmatrix[2]; | ||
| 592 | m11 = flatmatrix[3]; | ||
| 593 | if (flatmatrix.length > 5) { | ||
| 594 | m02 = flatmatrix[4]; | ||
| 595 | m12 = flatmatrix[5]; | ||
| 596 | } | ||
| 597 | updateState(); | ||
| 598 | } | ||
| 599 | |||
| 600 | /** | ||
| 601 | * Returns a transform representing a translation transformation. | ||
| 602 | * The matrix representing the returned transform is: | ||
| 603 | * <pre> | ||
| 604 | * [ 1 0 tx ] | ||
| 605 | * [ 0 1 ty ] | ||
| 606 | * [ 0 0 1 ] | ||
| 607 | * </pre> | ||
| 608 | * @param tx the distance by which coordinates are translated in the | ||
| 609 | * X axis direction | ||
| 610 | * @param ty the distance by which coordinates are translated in the | ||
| 611 | * Y axis direction | ||
| 612 | * @return an <code>AffineTransform</code> object that represents a | ||
| 613 | * translation transformation, created with the specified vector. | ||
| 614 | * @since 1.2 | ||
| 615 | */ | ||
| 616 | public static AffineTransform getTranslateInstance(double tx, double ty) { | ||
| 617 | AffineTransform Tx = new AffineTransform(); | ||
| 618 | Tx.setToTranslation(tx, ty); | ||
| 619 | return Tx; | ||
| 620 | } | ||
| 621 | |||
| 622 | /** | ||
| 623 | * Returns a transform representing a rotation transformation. | ||
| 624 | * The matrix representing the returned transform is: | ||
| 625 | * <pre> | ||
| 626 | * [ cos(theta) -sin(theta) 0 ] | ||
| 627 | * [ sin(theta) cos(theta) 0 ] | ||
| 628 | * [ 0 0 1 ] | ||
| 629 | * </pre> | ||
| 630 | * Rotating by a positive angle theta rotates points on the positive | ||
| 631 | * X axis toward the positive Y axis. | ||
| 632 | * Note also the discussion of | ||
| 633 | * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> | ||
| 634 | * above. | ||
| 635 | * @param theta the angle of rotation measured in radians | ||
| 636 | * @return an <code>AffineTransform</code> object that is a rotation | ||
| 637 | * transformation, created with the specified angle of rotation. | ||
| 638 | * @since 1.2 | ||
| 639 | */ | ||
| 640 | public static AffineTransform getRotateInstance(double theta) { | ||
| 641 | AffineTransform Tx = new AffineTransform(); | ||
| 642 | Tx.setToRotation(theta); | ||
| 643 | return Tx; | ||
| 644 | } | ||
| 645 | |||
| 646 | /** | ||
| 647 | * Returns a transform that rotates coordinates around an anchor point. | ||
| 648 | * This operation is equivalent to translating the coordinates so | ||
| 649 | * that the anchor point is at the origin (S1), then rotating them | ||
| 650 | * about the new origin (S2), and finally translating so that the | ||
| 651 | * intermediate origin is restored to the coordinates of the original | ||
| 652 | * anchor point (S3). | ||
| 653 | * <p> | ||
| 654 | * This operation is equivalent to the following sequence of calls: | ||
| 655 | * <pre> | ||
| 656 | * AffineTransform Tx = new AffineTransform(); | ||
| 657 | * Tx.translate(anchorx, anchory); // S3: final translation | ||
| 658 | * Tx.rotate(theta); // S2: rotate around anchor | ||
| 659 | * Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin | ||
| 660 | * </pre> | ||
| 661 | * The matrix representing the returned transform is: | ||
| 662 | * <pre> | ||
| 663 | * [ cos(theta) -sin(theta) x-x*cos+y*sin ] | ||
| 664 | * [ sin(theta) cos(theta) y-x*sin-y*cos ] | ||
| 665 | * [ 0 0 1 ] | ||
| 666 | * </pre> | ||
| 667 | * Rotating by a positive angle theta rotates points on the positive | ||
| 668 | * X axis toward the positive Y axis. | ||
| 669 | * Note also the discussion of | ||
| 670 | * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> | ||
| 671 | * above. | ||
| 672 | * | ||
| 673 | * @param theta the angle of rotation measured in radians | ||
| 674 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 675 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 676 | * @return an <code>AffineTransform</code> object that rotates | ||
| 677 | * coordinates around the specified point by the specified angle of | ||
| 678 | * rotation. | ||
| 679 | * @since 1.2 | ||
| 680 | */ | ||
| 681 | public static AffineTransform getRotateInstance(double theta, | ||
| 682 | double anchorx, | ||
| 683 | double anchory) | ||
| 684 | { | ||
| 685 | AffineTransform Tx = new AffineTransform(); | ||
| 686 | Tx.setToRotation(theta, anchorx, anchory); | ||
| 687 | return Tx; | ||
| 688 | } | ||
| 689 | |||
| 690 | /** | ||
| 691 | * Returns a transform that rotates coordinates according to | ||
| 692 | * a rotation vector. | ||
| 693 | * All coordinates rotate about the origin by the same amount. | ||
| 694 | * The amount of rotation is such that coordinates along the former | ||
| 695 | * positive X axis will subsequently align with the vector pointing | ||
| 696 | * from the origin to the specified vector coordinates. | ||
| 697 | * If both <code>vecx</code> and <code>vecy</code> are 0.0, | ||
| 698 | * an identity transform is returned. | ||
| 699 | * This operation is equivalent to calling: | ||
| 700 | * <pre> | ||
| 701 | * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx)); | ||
| 702 | * </pre> | ||
| 703 | * | ||
| 704 | * @param vecx the X coordinate of the rotation vector | ||
| 705 | * @param vecy the Y coordinate of the rotation vector | ||
| 706 | * @return an <code>AffineTransform</code> object that rotates | ||
| 707 | * coordinates according to the specified rotation vector. | ||
| 708 | * @since 1.6 | ||
| 709 | */ | ||
| 710 | public static AffineTransform getRotateInstance(double vecx, double vecy) { | ||
| 711 | AffineTransform Tx = new AffineTransform(); | ||
| 712 | Tx.setToRotation(vecx, vecy); | ||
| 713 | return Tx; | ||
| 714 | } | ||
| 715 | |||
| 716 | /** | ||
| 717 | * Returns a transform that rotates coordinates around an anchor | ||
| 718 | * point accordinate to a rotation vector. | ||
| 719 | * All coordinates rotate about the specified anchor coordinates | ||
| 720 | * by the same amount. | ||
| 721 | * The amount of rotation is such that coordinates along the former | ||
| 722 | * positive X axis will subsequently align with the vector pointing | ||
| 723 | * from the origin to the specified vector coordinates. | ||
| 724 | * If both <code>vecx</code> and <code>vecy</code> are 0.0, | ||
| 725 | * an identity transform is returned. | ||
| 726 | * This operation is equivalent to calling: | ||
| 727 | * <pre> | ||
| 728 | * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx), | ||
| 729 | * anchorx, anchory); | ||
| 730 | * </pre> | ||
| 731 | * | ||
| 732 | * @param vecx the X coordinate of the rotation vector | ||
| 733 | * @param vecy the Y coordinate of the rotation vector | ||
| 734 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 735 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 736 | * @return an <code>AffineTransform</code> object that rotates | ||
| 737 | * coordinates around the specified point according to the | ||
| 738 | * specified rotation vector. | ||
| 739 | * @since 1.6 | ||
| 740 | */ | ||
| 741 | public static AffineTransform getRotateInstance(double vecx, | ||
| 742 | double vecy, | ||
| 743 | double anchorx, | ||
| 744 | double anchory) | ||
| 745 | { | ||
| 746 | AffineTransform Tx = new AffineTransform(); | ||
| 747 | Tx.setToRotation(vecx, vecy, anchorx, anchory); | ||
| 748 | return Tx; | ||
| 749 | } | ||
| 750 | |||
| 751 | /** | ||
| 752 | * Returns a transform that rotates coordinates by the specified | ||
| 753 | * number of quadrants. | ||
| 754 | * This operation is equivalent to calling: | ||
| 755 | * <pre> | ||
| 756 | * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0); | ||
| 757 | * </pre> | ||
| 758 | * Rotating by a positive number of quadrants rotates points on | ||
| 759 | * the positive X axis toward the positive Y axis. | ||
| 760 | * @param numquadrants the number of 90 degree arcs to rotate by | ||
| 761 | * @return an <code>AffineTransform</code> object that rotates | ||
| 762 | * coordinates by the specified number of quadrants. | ||
| 763 | * @since 1.6 | ||
| 764 | */ | ||
| 765 | public static AffineTransform getQuadrantRotateInstance(int numquadrants) { | ||
| 766 | AffineTransform Tx = new AffineTransform(); | ||
| 767 | Tx.setToQuadrantRotation(numquadrants); | ||
| 768 | return Tx; | ||
| 769 | } | ||
| 770 | |||
| 771 | /** | ||
| 772 | * Returns a transform that rotates coordinates by the specified | ||
| 773 | * number of quadrants around the specified anchor point. | ||
| 774 | * This operation is equivalent to calling: | ||
| 775 | * <pre> | ||
| 776 | * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0, | ||
| 777 | * anchorx, anchory); | ||
| 778 | * </pre> | ||
| 779 | * Rotating by a positive number of quadrants rotates points on | ||
| 780 | * the positive X axis toward the positive Y axis. | ||
| 781 | * | ||
| 782 | * @param numquadrants the number of 90 degree arcs to rotate by | ||
| 783 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 784 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 785 | * @return an <code>AffineTransform</code> object that rotates | ||
| 786 | * coordinates by the specified number of quadrants around the | ||
| 787 | * specified anchor point. | ||
| 788 | * @since 1.6 | ||
| 789 | */ | ||
| 790 | public static AffineTransform getQuadrantRotateInstance(int numquadrants, | ||
| 791 | double anchorx, | ||
| 792 | double anchory) | ||
| 793 | { | ||
| 794 | AffineTransform Tx = new AffineTransform(); | ||
| 795 | Tx.setToQuadrantRotation(numquadrants, anchorx, anchory); | ||
| 796 | return Tx; | ||
| 797 | } | ||
| 798 | |||
| 799 | /** | ||
| 800 | * Returns a transform representing a scaling transformation. | ||
| 801 | * The matrix representing the returned transform is: | ||
| 802 | * <pre> | ||
| 803 | * [ sx 0 0 ] | ||
| 804 | * [ 0 sy 0 ] | ||
| 805 | * [ 0 0 1 ] | ||
| 806 | * </pre> | ||
| 807 | * @param sx the factor by which coordinates are scaled along the | ||
| 808 | * X axis direction | ||
| 809 | * @param sy the factor by which coordinates are scaled along the | ||
| 810 | * Y axis direction | ||
| 811 | * @return an <code>AffineTransform</code> object that scales | ||
| 812 | * coordinates by the specified factors. | ||
| 813 | * @since 1.2 | ||
| 814 | */ | ||
| 815 | public static AffineTransform getScaleInstance(double sx, double sy) { | ||
| 816 | AffineTransform Tx = new AffineTransform(); | ||
| 817 | Tx.setToScale(sx, sy); | ||
| 818 | return Tx; | ||
| 819 | } | ||
| 820 | |||
| 821 | /** | ||
| 822 | * Returns a transform representing a shearing transformation. | ||
| 823 | * The matrix representing the returned transform is: | ||
| 824 | * <pre> | ||
| 825 | * [ 1 shx 0 ] | ||
| 826 | * [ shy 1 0 ] | ||
| 827 | * [ 0 0 1 ] | ||
| 828 | * </pre> | ||
| 829 | * @param shx the multiplier by which coordinates are shifted in the | ||
| 830 | * direction of the positive X axis as a factor of their Y coordinate | ||
| 831 | * @param shy the multiplier by which coordinates are shifted in the | ||
| 832 | * direction of the positive Y axis as a factor of their X coordinate | ||
| 833 | * @return an <code>AffineTransform</code> object that shears | ||
| 834 | * coordinates by the specified multipliers. | ||
| 835 | * @since 1.2 | ||
| 836 | */ | ||
| 837 | public static AffineTransform getShearInstance(double shx, double shy) { | ||
| 838 | AffineTransform Tx = new AffineTransform(); | ||
| 839 | Tx.setToShear(shx, shy); | ||
| 840 | return Tx; | ||
| 841 | } | ||
| 842 | |||
| 843 | /** | ||
| 844 | * Retrieves the flag bits describing the conversion properties of | ||
| 845 | * this transform. | ||
| 846 | * The return value is either one of the constants TYPE_IDENTITY | ||
| 847 | * or TYPE_GENERAL_TRANSFORM, or a combination of the | ||
| 848 | * appriopriate flag bits. | ||
| 849 | * A valid combination of flag bits is an exclusive OR operation | ||
| 850 | * that can combine | ||
| 851 | * the TYPE_TRANSLATION flag bit | ||
| 852 | * in addition to either of the | ||
| 853 | * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits | ||
| 854 | * as well as either of the | ||
| 855 | * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits. | ||
| 856 | * @return the OR combination of any of the indicated flags that | ||
| 857 | * apply to this transform | ||
| 858 | * @see #TYPE_IDENTITY | ||
| 859 | * @see #TYPE_TRANSLATION | ||
| 860 | * @see #TYPE_UNIFORM_SCALE | ||
| 861 | * @see #TYPE_GENERAL_SCALE | ||
| 862 | * @see #TYPE_QUADRANT_ROTATION | ||
| 863 | * @see #TYPE_GENERAL_ROTATION | ||
| 864 | * @see #TYPE_GENERAL_TRANSFORM | ||
| 865 | * @since 1.2 | ||
| 866 | */ | ||
| 867 | public int getType() { | ||
| 868 | if (type == TYPE_UNKNOWN) { | ||
| 869 | calculateType(); | ||
| 870 | } | ||
| 871 | return type; | ||
| 872 | } | ||
| 873 | |||
| 874 | /** | ||
| 875 | * This is the utility function to calculate the flag bits when | ||
| 876 | * they have not been cached. | ||
| 877 | * @see #getType | ||
| 878 | */ | ||
| 879 | private void calculateType() { | ||
| 880 | int ret = TYPE_IDENTITY; | ||
| 881 | boolean sgn0, sgn1; | ||
| 882 | double M0, M1, M2, M3; | ||
| 883 | updateState(); | ||
| 884 | switch (state) { | ||
| 885 | default: | ||
| 886 | stateError(); | ||
| 887 | /* NOTREACHED */ | ||
| 888 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 889 | ret = TYPE_TRANSLATION; | ||
| 890 | /* NOBREAK */ | ||
| 891 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 892 | if ((M0 = m00) * (M2 = m01) + (M3 = m10) * (M1 = m11) != 0) { | ||
| 893 | // Transformed unit vectors are not perpendicular... | ||
| 894 | this.type = TYPE_GENERAL_TRANSFORM; | ||
| 895 | return; | ||
| 896 | } | ||
| 897 | sgn0 = (M0 >= 0.0); | ||
| 898 | sgn1 = (M1 >= 0.0); | ||
| 899 | if (sgn0 == sgn1) { | ||
| 900 | // sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3) | ||
| 901 | // This is the "unflipped" (right-handed) state | ||
| 902 | if (M0 != M1 || M2 != -M3) { | ||
| 903 | ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE); | ||
| 904 | } else if (M0 * M1 - M2 * M3 != 1.0) { | ||
| 905 | ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE); | ||
| 906 | } else { | ||
| 907 | ret |= TYPE_GENERAL_ROTATION; | ||
| 908 | } | ||
| 909 | } else { | ||
| 910 | // sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3) | ||
| 911 | // This is the "flipped" (left-handed) state | ||
| 912 | if (M0 != -M1 || M2 != M3) { | ||
| 913 | ret |= (TYPE_GENERAL_ROTATION | | ||
| 914 | TYPE_FLIP | | ||
| 915 | TYPE_GENERAL_SCALE); | ||
| 916 | } else if (M0 * M1 - M2 * M3 != 1.0) { | ||
| 917 | ret |= (TYPE_GENERAL_ROTATION | | ||
| 918 | TYPE_FLIP | | ||
| 919 | TYPE_UNIFORM_SCALE); | ||
| 920 | } else { | ||
| 921 | ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP); | ||
| 922 | } | ||
| 923 | } | ||
| 924 | break; | ||
| 925 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 926 | ret = TYPE_TRANSLATION; | ||
| 927 | /* NOBREAK */ | ||
| 928 | case (APPLY_SHEAR): | ||
| 929 | sgn0 = ((M0 = m01) >= 0.0); | ||
| 930 | sgn1 = ((M1 = m10) >= 0.0); | ||
| 931 | if (sgn0 != sgn1) { | ||
| 932 | // Different signs - simple 90 degree rotation | ||
| 933 | if (M0 != -M1) { | ||
| 934 | ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); | ||
| 935 | } else if (M0 != 1.0 && M0 != -1.0) { | ||
| 936 | ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); | ||
| 937 | } else { | ||
| 938 | ret |= TYPE_QUADRANT_ROTATION; | ||
| 939 | } | ||
| 940 | } else { | ||
| 941 | // Same signs - 90 degree rotation plus an axis flip too | ||
| 942 | if (M0 == M1) { | ||
| 943 | ret |= (TYPE_QUADRANT_ROTATION | | ||
| 944 | TYPE_FLIP | | ||
| 945 | TYPE_UNIFORM_SCALE); | ||
| 946 | } else { | ||
| 947 | ret |= (TYPE_QUADRANT_ROTATION | | ||
| 948 | TYPE_FLIP | | ||
| 949 | TYPE_GENERAL_SCALE); | ||
| 950 | } | ||
| 951 | } | ||
| 952 | break; | ||
| 953 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 954 | ret = TYPE_TRANSLATION; | ||
| 955 | /* NOBREAK */ | ||
| 956 | case (APPLY_SCALE): | ||
| 957 | sgn0 = ((M0 = m00) >= 0.0); | ||
| 958 | sgn1 = ((M1 = m11) >= 0.0); | ||
| 959 | if (sgn0 == sgn1) { | ||
| 960 | if (sgn0) { | ||
| 961 | // Both scaling factors non-negative - simple scale | ||
| 962 | // Note: APPLY_SCALE implies M0, M1 are not both 1 | ||
| 963 | if (M0 == M1) { | ||
| 964 | ret |= TYPE_UNIFORM_SCALE; | ||
| 965 | } else { | ||
| 966 | ret |= TYPE_GENERAL_SCALE; | ||
| 967 | } | ||
| 968 | } else { | ||
| 969 | // Both scaling factors negative - 180 degree rotation | ||
| 970 | if (M0 != M1) { | ||
| 971 | ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); | ||
| 972 | } else if (M0 != -1.0) { | ||
| 973 | ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); | ||
| 974 | } else { | ||
| 975 | ret |= TYPE_QUADRANT_ROTATION; | ||
| 976 | } | ||
| 977 | } | ||
| 978 | } else { | ||
| 979 | // Scaling factor signs different - flip about some axis | ||
| 980 | if (M0 == -M1) { | ||
| 981 | if (M0 == 1.0 || M0 == -1.0) { | ||
| 982 | ret |= TYPE_FLIP; | ||
| 983 | } else { | ||
| 984 | ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE); | ||
| 985 | } | ||
| 986 | } else { | ||
| 987 | ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE); | ||
| 988 | } | ||
| 989 | } | ||
| 990 | break; | ||
| 991 | case (APPLY_TRANSLATE): | ||
| 992 | ret = TYPE_TRANSLATION; | ||
| 993 | break; | ||
| 994 | case (APPLY_IDENTITY): | ||
| 995 | break; | ||
| 996 | } | ||
| 997 | this.type = ret; | ||
| 998 | } | ||
| 999 | |||
| 1000 | /** | ||
| 1001 | * Returns the determinant of the matrix representation of the transform. | ||
| 1002 | * The determinant is useful both to determine if the transform can | ||
| 1003 | * be inverted and to get a single value representing the | ||
| 1004 | * combined X and Y scaling of the transform. | ||
| 1005 | * <p> | ||
| 1006 | * If the determinant is non-zero, then this transform is | ||
| 1007 | * invertible and the various methods that depend on the inverse | ||
| 1008 | * transform do not need to throw a | ||
| 1009 | * {@link NoninvertibleTransformException}. | ||
| 1010 | * If the determinant is zero then this transform can not be | ||
| 1011 | * inverted since the transform maps all input coordinates onto | ||
| 1012 | * a line or a point. | ||
| 1013 | * If the determinant is near enough to zero then inverse transform | ||
| 1014 | * operations might not carry enough precision to produce meaningful | ||
| 1015 | * results. | ||
| 1016 | * <p> | ||
| 1017 | * If this transform represents a uniform scale, as indicated by | ||
| 1018 | * the <code>getType</code> method then the determinant also | ||
| 1019 | * represents the square of the uniform scale factor by which all of | ||
| 1020 | * the points are expanded from or contracted towards the origin. | ||
| 1021 | * If this transform represents a non-uniform scale or more general | ||
| 1022 | * transform then the determinant is not likely to represent a | ||
| 1023 | * value useful for any purpose other than determining if inverse | ||
| 1024 | * transforms are possible. | ||
| 1025 | * <p> | ||
| 1026 | * Mathematically, the determinant is calculated using the formula: | ||
| 1027 | * <pre> | ||
| 1028 | * | m00 m01 m02 | | ||
| 1029 | * | m10 m11 m12 | = m00 * m11 - m01 * m10 | ||
| 1030 | * | 0 0 1 | | ||
| 1031 | * </pre> | ||
| 1032 | * | ||
| 1033 | * @return the determinant of the matrix used to transform the | ||
| 1034 | * coordinates. | ||
| 1035 | * @see #getType | ||
| 1036 | * @see #createInverse | ||
| 1037 | * @see #inverseTransform | ||
| 1038 | * @see #TYPE_UNIFORM_SCALE | ||
| 1039 | * @since 1.2 | ||
| 1040 | */ | ||
| 1041 | public double getDeterminant() { | ||
| 1042 | switch (state) { | ||
| 1043 | default: | ||
| 1044 | stateError(); | ||
| 1045 | /* NOTREACHED */ | ||
| 1046 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1047 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 1048 | return m00 * m11 - m01 * m10; | ||
| 1049 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 1050 | case (APPLY_SHEAR): | ||
| 1051 | return -(m01 * m10); | ||
| 1052 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1053 | case (APPLY_SCALE): | ||
| 1054 | return m00 * m11; | ||
| 1055 | case (APPLY_TRANSLATE): | ||
| 1056 | case (APPLY_IDENTITY): | ||
| 1057 | return 1.0; | ||
| 1058 | } | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | /** | ||
| 1062 | * Manually recalculates the state of the transform when the matrix | ||
| 1063 | * changes too much to predict the effects on the state. | ||
| 1064 | * The following table specifies what the various settings of the | ||
| 1065 | * state field say about the values of the corresponding matrix | ||
| 1066 | * element fields. | ||
| 1067 | * Note that the rules governing the SCALE fields are slightly | ||
| 1068 | * different depending on whether the SHEAR flag is also set. | ||
| 1069 | * <pre> | ||
| 1070 | * SCALE SHEAR TRANSLATE | ||
| 1071 | * m00/m11 m01/m10 m02/m12 | ||
| 1072 | * | ||
| 1073 | * IDENTITY 1.0 0.0 0.0 | ||
| 1074 | * TRANSLATE (TR) 1.0 0.0 not both 0.0 | ||
| 1075 | * SCALE (SC) not both 1.0 0.0 0.0 | ||
| 1076 | * TR | SC not both 1.0 0.0 not both 0.0 | ||
| 1077 | * SHEAR (SH) 0.0 not both 0.0 0.0 | ||
| 1078 | * TR | SH 0.0 not both 0.0 not both 0.0 | ||
| 1079 | * SC | SH not both 0.0 not both 0.0 0.0 | ||
| 1080 | * TR | SC | SH not both 0.0 not both 0.0 not both 0.0 | ||
| 1081 | * </pre> | ||
| 1082 | */ | ||
| 1083 | void updateState() { | ||
| 1084 | if (m01 == 0.0 && m10 == 0.0) { | ||
| 1085 | if (m00 == 1.0 && m11 == 1.0) { | ||
| 1086 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1087 | state = APPLY_IDENTITY; | ||
| 1088 | type = TYPE_IDENTITY; | ||
| 1089 | } else { | ||
| 1090 | state = APPLY_TRANSLATE; | ||
| 1091 | type = TYPE_TRANSLATION; | ||
| 1092 | } | ||
| 1093 | } else { | ||
| 1094 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1095 | state = APPLY_SCALE; | ||
| 1096 | type = TYPE_UNKNOWN; | ||
| 1097 | } else { | ||
| 1098 | state = (APPLY_SCALE | APPLY_TRANSLATE); | ||
| 1099 | type = TYPE_UNKNOWN; | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | } else { | ||
| 1103 | if (m00 == 0.0 && m11 == 0.0) { | ||
| 1104 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1105 | state = APPLY_SHEAR; | ||
| 1106 | type = TYPE_UNKNOWN; | ||
| 1107 | } else { | ||
| 1108 | state = (APPLY_SHEAR | APPLY_TRANSLATE); | ||
| 1109 | type = TYPE_UNKNOWN; | ||
| 1110 | } | ||
| 1111 | } else { | ||
| 1112 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1113 | state = (APPLY_SHEAR | APPLY_SCALE); | ||
| 1114 | type = TYPE_UNKNOWN; | ||
| 1115 | } else { | ||
| 1116 | state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE); | ||
| 1117 | type = TYPE_UNKNOWN; | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | /* | ||
| 1124 | * Convenience method used internally to throw exceptions when | ||
| 1125 | * a case was forgotten in a switch statement. | ||
| 1126 | */ | ||
| 1127 | private void stateError() { | ||
| 1128 | throw new InternalError("missing case in transform state switch"); | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | /** | ||
| 1132 | * Retrieves the 6 specifiable values in the 3x3 affine transformation | ||
| 1133 | * matrix and places them into an array of double precisions values. | ||
| 1134 | * The values are stored in the array as | ||
| 1135 | * { m00 m10 m01 m11 m02 m12 }. | ||
| 1136 | * An array of 4 doubles can also be specified, in which case only the | ||
| 1137 | * first four elements representing the non-transform | ||
| 1138 | * parts of the array are retrieved and the values are stored into | ||
| 1139 | * the array as { m00 m10 m01 m11 } | ||
| 1140 | * @param flatmatrix the double array used to store the returned | ||
| 1141 | * values. | ||
| 1142 | * @see #getScaleX | ||
| 1143 | * @see #getScaleY | ||
| 1144 | * @see #getShearX | ||
| 1145 | * @see #getShearY | ||
| 1146 | * @see #getTranslateX | ||
| 1147 | * @see #getTranslateY | ||
| 1148 | * @since 1.2 | ||
| 1149 | */ | ||
| 1150 | public void getMatrix(double[] flatmatrix) { | ||
| 1151 | flatmatrix[0] = m00; | ||
| 1152 | flatmatrix[1] = m10; | ||
| 1153 | flatmatrix[2] = m01; | ||
| 1154 | flatmatrix[3] = m11; | ||
| 1155 | if (flatmatrix.length > 5) { | ||
| 1156 | flatmatrix[4] = m02; | ||
| 1157 | flatmatrix[5] = m12; | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | /** | ||
| 1162 | * Returns the X coordinate scaling element (m00) of the 3x3 | ||
| 1163 | * affine transformation matrix. | ||
| 1164 | * @return a double value that is the X coordinate of the scaling | ||
| 1165 | * element of the affine transformation matrix. | ||
| 1166 | * @see #getMatrix | ||
| 1167 | * @since 1.2 | ||
| 1168 | */ | ||
| 1169 | public double getScaleX() { | ||
| 1170 | return m00; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | /** | ||
| 1174 | * Returns the Y coordinate scaling element (m11) of the 3x3 | ||
| 1175 | * affine transformation matrix. | ||
| 1176 | * @return a double value that is the Y coordinate of the scaling | ||
| 1177 | * element of the affine transformation matrix. | ||
| 1178 | * @see #getMatrix | ||
| 1179 | * @since 1.2 | ||
| 1180 | */ | ||
| 1181 | public double getScaleY() { | ||
| 1182 | return m11; | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | /** | ||
| 1186 | * Returns the X coordinate shearing element (m01) of the 3x3 | ||
| 1187 | * affine transformation matrix. | ||
| 1188 | * @return a double value that is the X coordinate of the shearing | ||
| 1189 | * element of the affine transformation matrix. | ||
| 1190 | * @see #getMatrix | ||
| 1191 | * @since 1.2 | ||
| 1192 | */ | ||
| 1193 | public double getShearX() { | ||
| 1194 | return m01; | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /** | ||
| 1198 | * Returns the Y coordinate shearing element (m10) of the 3x3 | ||
| 1199 | * affine transformation matrix. | ||
| 1200 | * @return a double value that is the Y coordinate of the shearing | ||
| 1201 | * element of the affine transformation matrix. | ||
| 1202 | * @see #getMatrix | ||
| 1203 | * @since 1.2 | ||
| 1204 | */ | ||
| 1205 | public double getShearY() { | ||
| 1206 | return m10; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | /** | ||
| 1210 | * Returns the X coordinate of the translation element (m02) of the | ||
| 1211 | * 3x3 affine transformation matrix. | ||
| 1212 | * @return a double value that is the X coordinate of the translation | ||
| 1213 | * element of the affine transformation matrix. | ||
| 1214 | * @see #getMatrix | ||
| 1215 | * @since 1.2 | ||
| 1216 | */ | ||
| 1217 | public double getTranslateX() { | ||
| 1218 | return m02; | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | /** | ||
| 1222 | * Returns the Y coordinate of the translation element (m12) of the | ||
| 1223 | * 3x3 affine transformation matrix. | ||
| 1224 | * @return a double value that is the Y coordinate of the translation | ||
| 1225 | * element of the affine transformation matrix. | ||
| 1226 | * @see #getMatrix | ||
| 1227 | * @since 1.2 | ||
| 1228 | */ | ||
| 1229 | public double getTranslateY() { | ||
| 1230 | return m12; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | /** | ||
| 1234 | * Concatenates this transform with a translation transformation. | ||
| 1235 | * This is equivalent to calling concatenate(T), where T is an | ||
| 1236 | * <code>AffineTransform</code> represented by the following matrix: | ||
| 1237 | * <pre> | ||
| 1238 | * [ 1 0 tx ] | ||
| 1239 | * [ 0 1 ty ] | ||
| 1240 | * [ 0 0 1 ] | ||
| 1241 | * </pre> | ||
| 1242 | * @param tx the distance by which coordinates are translated in the | ||
| 1243 | * X axis direction | ||
| 1244 | * @param ty the distance by which coordinates are translated in the | ||
| 1245 | * Y axis direction | ||
| 1246 | * @since 1.2 | ||
| 1247 | */ | ||
| 1248 | public void translate(double tx, double ty) { | ||
| 1249 | switch (state) { | ||
| 1250 | default: | ||
| 1251 | stateError(); | ||
| 1252 | /* NOTREACHED */ | ||
| 1253 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1254 | m02 = tx * m00 + ty * m01 + m02; | ||
| 1255 | m12 = tx * m10 + ty * m11 + m12; | ||
| 1256 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1257 | state = APPLY_SHEAR | APPLY_SCALE; | ||
| 1258 | if (type != TYPE_UNKNOWN) { | ||
| 1259 | type -= TYPE_TRANSLATION; | ||
| 1260 | } | ||
| 1261 | } | ||
| 1262 | return; | ||
| 1263 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 1264 | m02 = tx * m00 + ty * m01; | ||
| 1265 | m12 = tx * m10 + ty * m11; | ||
| 1266 | if (m02 != 0.0 || m12 != 0.0) { | ||
| 1267 | state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE; | ||
| 1268 | type |= TYPE_TRANSLATION; | ||
| 1269 | } | ||
| 1270 | return; | ||
| 1271 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 1272 | m02 = ty * m01 + m02; | ||
| 1273 | m12 = tx * m10 + m12; | ||
| 1274 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1275 | state = APPLY_SHEAR; | ||
| 1276 | if (type != TYPE_UNKNOWN) { | ||
| 1277 | type -= TYPE_TRANSLATION; | ||
| 1278 | } | ||
| 1279 | } | ||
| 1280 | return; | ||
| 1281 | case (APPLY_SHEAR): | ||
| 1282 | m02 = ty * m01; | ||
| 1283 | m12 = tx * m10; | ||
| 1284 | if (m02 != 0.0 || m12 != 0.0) { | ||
| 1285 | state = APPLY_SHEAR | APPLY_TRANSLATE; | ||
| 1286 | type |= TYPE_TRANSLATION; | ||
| 1287 | } | ||
| 1288 | return; | ||
| 1289 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1290 | m02 = tx * m00 + m02; | ||
| 1291 | m12 = ty * m11 + m12; | ||
| 1292 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1293 | state = APPLY_SCALE; | ||
| 1294 | if (type != TYPE_UNKNOWN) { | ||
| 1295 | type -= TYPE_TRANSLATION; | ||
| 1296 | } | ||
| 1297 | } | ||
| 1298 | return; | ||
| 1299 | case (APPLY_SCALE): | ||
| 1300 | m02 = tx * m00; | ||
| 1301 | m12 = ty * m11; | ||
| 1302 | if (m02 != 0.0 || m12 != 0.0) { | ||
| 1303 | state = APPLY_SCALE | APPLY_TRANSLATE; | ||
| 1304 | type |= TYPE_TRANSLATION; | ||
| 1305 | } | ||
| 1306 | return; | ||
| 1307 | case (APPLY_TRANSLATE): | ||
| 1308 | m02 = tx + m02; | ||
| 1309 | m12 = ty + m12; | ||
| 1310 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1311 | state = APPLY_IDENTITY; | ||
| 1312 | type = TYPE_IDENTITY; | ||
| 1313 | } | ||
| 1314 | return; | ||
| 1315 | case (APPLY_IDENTITY): | ||
| 1316 | m02 = tx; | ||
| 1317 | m12 = ty; | ||
| 1318 | if (tx != 0.0 || ty != 0.0) { | ||
| 1319 | state = APPLY_TRANSLATE; | ||
| 1320 | type = TYPE_TRANSLATION; | ||
| 1321 | } | ||
| 1322 | return; | ||
| 1323 | } | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | // Utility methods to optimize rotate methods. | ||
| 1327 | // These tables translate the flags during predictable quadrant | ||
| 1328 | // rotations where the shear and scale values are swapped and negated. | ||
| 1329 | private static final int rot90conversion[] = { | ||
| 1330 | /* IDENTITY => */ APPLY_SHEAR, | ||
| 1331 | /* TRANSLATE (TR) => */ APPLY_SHEAR | APPLY_TRANSLATE, | ||
| 1332 | /* SCALE (SC) => */ APPLY_SHEAR, | ||
| 1333 | /* SC | TR => */ APPLY_SHEAR | APPLY_TRANSLATE, | ||
| 1334 | /* SHEAR (SH) => */ APPLY_SCALE, | ||
| 1335 | /* SH | TR => */ APPLY_SCALE | APPLY_TRANSLATE, | ||
| 1336 | /* SH | SC => */ APPLY_SHEAR | APPLY_SCALE, | ||
| 1337 | /* SH | SC | TR => */ APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, | ||
| 1338 | }; | ||
| 1339 | private final void rotate90() { | ||
| 1340 | double M0 = m00; | ||
| 1341 | m00 = m01; | ||
| 1342 | m01 = -M0; | ||
| 1343 | M0 = m10; | ||
| 1344 | m10 = m11; | ||
| 1345 | m11 = -M0; | ||
| 1346 | int state = rot90conversion[this.state]; | ||
| 1347 | if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && | ||
| 1348 | m00 == 1.0 && m11 == 1.0) | ||
| 1349 | { | ||
| 1350 | state -= APPLY_SCALE; | ||
| 1351 | } | ||
| 1352 | this.state = state; | ||
| 1353 | type = TYPE_UNKNOWN; | ||
| 1354 | } | ||
| 1355 | private final void rotate180() { | ||
| 1356 | m00 = -m00; | ||
| 1357 | m11 = -m11; | ||
| 1358 | int state = this.state; | ||
| 1359 | if ((state & (APPLY_SHEAR)) != 0) { | ||
| 1360 | // If there was a shear, then this rotation has no | ||
| 1361 | // effect on the state. | ||
| 1362 | m01 = -m01; | ||
| 1363 | m10 = -m10; | ||
| 1364 | } else { | ||
| 1365 | // No shear means the SCALE state may toggle when | ||
| 1366 | // m00 and m11 are negated. | ||
| 1367 | if (m00 == 1.0 && m11 == 1.0) { | ||
| 1368 | this.state = state & ~APPLY_SCALE; | ||
| 1369 | } else { | ||
| 1370 | this.state = state | APPLY_SCALE; | ||
| 1371 | } | ||
| 1372 | } | ||
| 1373 | type = TYPE_UNKNOWN; | ||
| 1374 | } | ||
| 1375 | private final void rotate270() { | ||
| 1376 | double M0 = m00; | ||
| 1377 | m00 = -m01; | ||
| 1378 | m01 = M0; | ||
| 1379 | M0 = m10; | ||
| 1380 | m10 = -m11; | ||
| 1381 | m11 = M0; | ||
| 1382 | int state = rot90conversion[this.state]; | ||
| 1383 | if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && | ||
| 1384 | m00 == 1.0 && m11 == 1.0) | ||
| 1385 | { | ||
| 1386 | state -= APPLY_SCALE; | ||
| 1387 | } | ||
| 1388 | this.state = state; | ||
| 1389 | type = TYPE_UNKNOWN; | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | /** | ||
| 1393 | * Concatenates this transform with a rotation transformation. | ||
| 1394 | * This is equivalent to calling concatenate(R), where R is an | ||
| 1395 | * <code>AffineTransform</code> represented by the following matrix: | ||
| 1396 | * <pre> | ||
| 1397 | * [ cos(theta) -sin(theta) 0 ] | ||
| 1398 | * [ sin(theta) cos(theta) 0 ] | ||
| 1399 | * [ 0 0 1 ] | ||
| 1400 | * </pre> | ||
| 1401 | * Rotating by a positive angle theta rotates points on the positive | ||
| 1402 | * X axis toward the positive Y axis. | ||
| 1403 | * Note also the discussion of | ||
| 1404 | * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> | ||
| 1405 | * above. | ||
| 1406 | * @param theta the angle of rotation measured in radians | ||
| 1407 | * @since 1.2 | ||
| 1408 | */ | ||
| 1409 | public void rotate(double theta) { | ||
| 1410 | double sin = Math.sin(theta); | ||
| 1411 | if (sin == 1.0) { | ||
| 1412 | rotate90(); | ||
| 1413 | } else if (sin == -1.0) { | ||
| 1414 | rotate270(); | ||
| 1415 | } else { | ||
| 1416 | double cos = Math.cos(theta); | ||
| 1417 | if (cos == -1.0) { | ||
| 1418 | rotate180(); | ||
| 1419 | } else if (cos != 1.0) { | ||
| 1420 | double M0, M1; | ||
| 1421 | M0 = m00; | ||
| 1422 | M1 = m01; | ||
| 1423 | m00 = cos * M0 + sin * M1; | ||
| 1424 | m01 = -sin * M0 + cos * M1; | ||
| 1425 | M0 = m10; | ||
| 1426 | M1 = m11; | ||
| 1427 | m10 = cos * M0 + sin * M1; | ||
| 1428 | m11 = -sin * M0 + cos * M1; | ||
| 1429 | updateState(); | ||
| 1430 | } | ||
| 1431 | } | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | /** | ||
| 1435 | * Concatenates this transform with a transform that rotates | ||
| 1436 | * coordinates around an anchor point. | ||
| 1437 | * This operation is equivalent to translating the coordinates so | ||
| 1438 | * that the anchor point is at the origin (S1), then rotating them | ||
| 1439 | * about the new origin (S2), and finally translating so that the | ||
| 1440 | * intermediate origin is restored to the coordinates of the original | ||
| 1441 | * anchor point (S3). | ||
| 1442 | * <p> | ||
| 1443 | * This operation is equivalent to the following sequence of calls: | ||
| 1444 | * <pre> | ||
| 1445 | * translate(anchorx, anchory); // S3: final translation | ||
| 1446 | * rotate(theta); // S2: rotate around anchor | ||
| 1447 | * translate(-anchorx, -anchory); // S1: translate anchor to origin | ||
| 1448 | * </pre> | ||
| 1449 | * Rotating by a positive angle theta rotates points on the positive | ||
| 1450 | * X axis toward the positive Y axis. | ||
| 1451 | * Note also the discussion of | ||
| 1452 | * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> | ||
| 1453 | * above. | ||
| 1454 | * | ||
| 1455 | * @param theta the angle of rotation measured in radians | ||
| 1456 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 1457 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 1458 | * @since 1.2 | ||
| 1459 | */ | ||
| 1460 | public void rotate(double theta, double anchorx, double anchory) { | ||
| 1461 | // REMIND: Simple for now - optimize later | ||
| 1462 | translate(anchorx, anchory); | ||
| 1463 | rotate(theta); | ||
| 1464 | translate(-anchorx, -anchory); | ||
| 1465 | } | ||
| 1466 | |||
| 1467 | /** | ||
| 1468 | * Concatenates this transform with a transform that rotates | ||
| 1469 | * coordinates according to a rotation vector. | ||
| 1470 | * All coordinates rotate about the origin by the same amount. | ||
| 1471 | * The amount of rotation is such that coordinates along the former | ||
| 1472 | * positive X axis will subsequently align with the vector pointing | ||
| 1473 | * from the origin to the specified vector coordinates. | ||
| 1474 | * If both <code>vecx</code> and <code>vecy</code> are 0.0, | ||
| 1475 | * no additional rotation is added to this transform. | ||
| 1476 | * This operation is equivalent to calling: | ||
| 1477 | * <pre> | ||
| 1478 | * rotate(Math.atan2(vecy, vecx)); | ||
| 1479 | * </pre> | ||
| 1480 | * | ||
| 1481 | * @param vecx the X coordinate of the rotation vector | ||
| 1482 | * @param vecy the Y coordinate of the rotation vector | ||
| 1483 | * @since 1.6 | ||
| 1484 | */ | ||
| 1485 | public void rotate(double vecx, double vecy) { | ||
| 1486 | if (vecy == 0.0) { | ||
| 1487 | if (vecx < 0.0) { | ||
| 1488 | rotate180(); | ||
| 1489 | } | ||
| 1490 | // If vecx > 0.0 - no rotation | ||
| 1491 | // If vecx == 0.0 - undefined rotation - treat as no rotation | ||
| 1492 | } else if (vecx == 0.0) { | ||
| 1493 | if (vecy > 0.0) { | ||
| 1494 | rotate90(); | ||
| 1495 | } else { // vecy must be < 0.0 | ||
| 1496 | rotate270(); | ||
| 1497 | } | ||
| 1498 | } else { | ||
| 1499 | double len = Math.sqrt(vecx * vecx + vecy * vecy); | ||
| 1500 | double sin = vecy / len; | ||
| 1501 | double cos = vecx / len; | ||
| 1502 | double M0, M1; | ||
| 1503 | M0 = m00; | ||
| 1504 | M1 = m01; | ||
| 1505 | m00 = cos * M0 + sin * M1; | ||
| 1506 | m01 = -sin * M0 + cos * M1; | ||
| 1507 | M0 = m10; | ||
| 1508 | M1 = m11; | ||
| 1509 | m10 = cos * M0 + sin * M1; | ||
| 1510 | m11 = -sin * M0 + cos * M1; | ||
| 1511 | updateState(); | ||
| 1512 | } | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | /** | ||
| 1516 | * Concatenates this transform with a transform that rotates | ||
| 1517 | * coordinates around an anchor point according to a rotation | ||
| 1518 | * vector. | ||
| 1519 | * All coordinates rotate about the specified anchor coordinates | ||
| 1520 | * by the same amount. | ||
| 1521 | * The amount of rotation is such that coordinates along the former | ||
| 1522 | * positive X axis will subsequently align with the vector pointing | ||
| 1523 | * from the origin to the specified vector coordinates. | ||
| 1524 | * If both <code>vecx</code> and <code>vecy</code> are 0.0, | ||
| 1525 | * the transform is not modified in any way. | ||
| 1526 | * This method is equivalent to calling: | ||
| 1527 | * <pre> | ||
| 1528 | * rotate(Math.atan2(vecy, vecx), anchorx, anchory); | ||
| 1529 | * </pre> | ||
| 1530 | * | ||
| 1531 | * @param vecx the X coordinate of the rotation vector | ||
| 1532 | * @param vecy the Y coordinate of the rotation vector | ||
| 1533 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 1534 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 1535 | * @since 1.6 | ||
| 1536 | */ | ||
| 1537 | public void rotate(double vecx, double vecy, | ||
| 1538 | double anchorx, double anchory) | ||
| 1539 | { | ||
| 1540 | // REMIND: Simple for now - optimize later | ||
| 1541 | translate(anchorx, anchory); | ||
| 1542 | rotate(vecx, vecy); | ||
| 1543 | translate(-anchorx, -anchory); | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | /** | ||
| 1547 | * Concatenates this transform with a transform that rotates | ||
| 1548 | * coordinates by the specified number of quadrants. | ||
| 1549 | * This is equivalent to calling: | ||
| 1550 | * <pre> | ||
| 1551 | * rotate(numquadrants * Math.PI / 2.0); | ||
| 1552 | * </pre> | ||
| 1553 | * Rotating by a positive number of quadrants rotates points on | ||
| 1554 | * the positive X axis toward the positive Y axis. | ||
| 1555 | * @param numquadrants the number of 90 degree arcs to rotate by | ||
| 1556 | * @since 1.6 | ||
| 1557 | */ | ||
| 1558 | public void quadrantRotate(int numquadrants) { | ||
| 1559 | switch (numquadrants & 3) { | ||
| 1560 | case 0: | ||
| 1561 | break; | ||
| 1562 | case 1: | ||
| 1563 | rotate90(); | ||
| 1564 | break; | ||
| 1565 | case 2: | ||
| 1566 | rotate180(); | ||
| 1567 | break; | ||
| 1568 | case 3: | ||
| 1569 | rotate270(); | ||
| 1570 | break; | ||
| 1571 | } | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | /** | ||
| 1575 | * Concatenates this transform with a transform that rotates | ||
| 1576 | * coordinates by the specified number of quadrants around | ||
| 1577 | * the specified anchor point. | ||
| 1578 | * This method is equivalent to calling: | ||
| 1579 | * <pre> | ||
| 1580 | * rotate(numquadrants * Math.PI / 2.0, anchorx, anchory); | ||
| 1581 | * </pre> | ||
| 1582 | * Rotating by a positive number of quadrants rotates points on | ||
| 1583 | * the positive X axis toward the positive Y axis. | ||
| 1584 | * | ||
| 1585 | * @param numquadrants the number of 90 degree arcs to rotate by | ||
| 1586 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 1587 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 1588 | * @since 1.6 | ||
| 1589 | */ | ||
| 1590 | public void quadrantRotate(int numquadrants, | ||
| 1591 | double anchorx, double anchory) | ||
| 1592 | { | ||
| 1593 | switch (numquadrants & 3) { | ||
| 1594 | case 0: | ||
| 1595 | return; | ||
| 1596 | case 1: | ||
| 1597 | m02 += anchorx * (m00 - m01) + anchory * (m01 + m00); | ||
| 1598 | m12 += anchorx * (m10 - m11) + anchory * (m11 + m10); | ||
| 1599 | rotate90(); | ||
| 1600 | break; | ||
| 1601 | case 2: | ||
| 1602 | m02 += anchorx * (m00 + m00) + anchory * (m01 + m01); | ||
| 1603 | m12 += anchorx * (m10 + m10) + anchory * (m11 + m11); | ||
| 1604 | rotate180(); | ||
| 1605 | break; | ||
| 1606 | case 3: | ||
| 1607 | m02 += anchorx * (m00 + m01) + anchory * (m01 - m00); | ||
| 1608 | m12 += anchorx * (m10 + m11) + anchory * (m11 - m10); | ||
| 1609 | rotate270(); | ||
| 1610 | break; | ||
| 1611 | } | ||
| 1612 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 1613 | state &= ~APPLY_TRANSLATE; | ||
| 1614 | } else { | ||
| 1615 | state |= APPLY_TRANSLATE; | ||
| 1616 | } | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | /** | ||
| 1620 | * Concatenates this transform with a scaling transformation. | ||
| 1621 | * This is equivalent to calling concatenate(S), where S is an | ||
| 1622 | * <code>AffineTransform</code> represented by the following matrix: | ||
| 1623 | * <pre> | ||
| 1624 | * [ sx 0 0 ] | ||
| 1625 | * [ 0 sy 0 ] | ||
| 1626 | * [ 0 0 1 ] | ||
| 1627 | * </pre> | ||
| 1628 | * @param sx the factor by which coordinates are scaled along the | ||
| 1629 | * X axis direction | ||
| 1630 | * @param sy the factor by which coordinates are scaled along the | ||
| 1631 | * Y axis direction | ||
| 1632 | * @since 1.2 | ||
| 1633 | */ | ||
| 1634 | public void scale(double sx, double sy) { | ||
| 1635 | int state = this.state; | ||
| 1636 | switch (state) { | ||
| 1637 | default: | ||
| 1638 | stateError(); | ||
| 1639 | /* NOTREACHED */ | ||
| 1640 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1641 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 1642 | m00 *= sx; | ||
| 1643 | m11 *= sy; | ||
| 1644 | /* NOBREAK */ | ||
| 1645 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 1646 | case (APPLY_SHEAR): | ||
| 1647 | m01 *= sy; | ||
| 1648 | m10 *= sx; | ||
| 1649 | if (m01 == 0 && m10 == 0) { | ||
| 1650 | state &= APPLY_TRANSLATE; | ||
| 1651 | if (m00 == 1.0 && m11 == 1.0) { | ||
| 1652 | this.type = (state == APPLY_IDENTITY | ||
| 1653 | ? TYPE_IDENTITY | ||
| 1654 | : TYPE_TRANSLATION); | ||
| 1655 | } else { | ||
| 1656 | state |= APPLY_SCALE; | ||
| 1657 | this.type = TYPE_UNKNOWN; | ||
| 1658 | } | ||
| 1659 | this.state = state; | ||
| 1660 | } | ||
| 1661 | return; | ||
| 1662 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1663 | case (APPLY_SCALE): | ||
| 1664 | m00 *= sx; | ||
| 1665 | m11 *= sy; | ||
| 1666 | if (m00 == 1.0 && m11 == 1.0) { | ||
| 1667 | this.state = (state &= APPLY_TRANSLATE); | ||
| 1668 | this.type = (state == APPLY_IDENTITY | ||
| 1669 | ? TYPE_IDENTITY | ||
| 1670 | : TYPE_TRANSLATION); | ||
| 1671 | } else { | ||
| 1672 | this.type = TYPE_UNKNOWN; | ||
| 1673 | } | ||
| 1674 | return; | ||
| 1675 | case (APPLY_TRANSLATE): | ||
| 1676 | case (APPLY_IDENTITY): | ||
| 1677 | m00 = sx; | ||
| 1678 | m11 = sy; | ||
| 1679 | if (sx != 1.0 || sy != 1.0) { | ||
| 1680 | this.state = state | APPLY_SCALE; | ||
| 1681 | this.type = TYPE_UNKNOWN; | ||
| 1682 | } | ||
| 1683 | return; | ||
| 1684 | } | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | /** | ||
| 1688 | * Concatenates this transform with a shearing transformation. | ||
| 1689 | * This is equivalent to calling concatenate(SH), where SH is an | ||
| 1690 | * <code>AffineTransform</code> represented by the following matrix: | ||
| 1691 | * <pre> | ||
| 1692 | * [ 1 shx 0 ] | ||
| 1693 | * [ shy 1 0 ] | ||
| 1694 | * [ 0 0 1 ] | ||
| 1695 | * </pre> | ||
| 1696 | * @param shx the multiplier by which coordinates are shifted in the | ||
| 1697 | * direction of the positive X axis as a factor of their Y coordinate | ||
| 1698 | * @param shy the multiplier by which coordinates are shifted in the | ||
| 1699 | * direction of the positive Y axis as a factor of their X coordinate | ||
| 1700 | * @since 1.2 | ||
| 1701 | */ | ||
| 1702 | public void shear(double shx, double shy) { | ||
| 1703 | int state = this.state; | ||
| 1704 | switch (state) { | ||
| 1705 | default: | ||
| 1706 | stateError(); | ||
| 1707 | /* NOTREACHED */ | ||
| 1708 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1709 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 1710 | double M0, M1; | ||
| 1711 | M0 = m00; | ||
| 1712 | M1 = m01; | ||
| 1713 | m00 = M0 + M1 * shy; | ||
| 1714 | m01 = M0 * shx + M1; | ||
| 1715 | |||
| 1716 | M0 = m10; | ||
| 1717 | M1 = m11; | ||
| 1718 | m10 = M0 + M1 * shy; | ||
| 1719 | m11 = M0 * shx + M1; | ||
| 1720 | updateState(); | ||
| 1721 | return; | ||
| 1722 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 1723 | case (APPLY_SHEAR): | ||
| 1724 | m00 = m01 * shy; | ||
| 1725 | m11 = m10 * shx; | ||
| 1726 | if (m00 != 0.0 || m11 != 0.0) { | ||
| 1727 | this.state = state | APPLY_SCALE; | ||
| 1728 | } | ||
| 1729 | this.type = TYPE_UNKNOWN; | ||
| 1730 | return; | ||
| 1731 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 1732 | case (APPLY_SCALE): | ||
| 1733 | m01 = m00 * shx; | ||
| 1734 | m10 = m11 * shy; | ||
| 1735 | if (m01 != 0.0 || m10 != 0.0) { | ||
| 1736 | this.state = state | APPLY_SHEAR; | ||
| 1737 | } | ||
| 1738 | this.type = TYPE_UNKNOWN; | ||
| 1739 | return; | ||
| 1740 | case (APPLY_TRANSLATE): | ||
| 1741 | case (APPLY_IDENTITY): | ||
| 1742 | m01 = shx; | ||
| 1743 | m10 = shy; | ||
| 1744 | if (m01 != 0.0 || m10 != 0.0) { | ||
| 1745 | this.state = state | APPLY_SCALE | APPLY_SHEAR; | ||
| 1746 | this.type = TYPE_UNKNOWN; | ||
| 1747 | } | ||
| 1748 | return; | ||
| 1749 | } | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | /** | ||
| 1753 | * Resets this transform to the Identity transform. | ||
| 1754 | * @since 1.2 | ||
| 1755 | */ | ||
| 1756 | public void setToIdentity() { | ||
| 1757 | m00 = m11 = 1.0; | ||
| 1758 | m10 = m01 = m02 = m12 = 0.0; | ||
| 1759 | state = APPLY_IDENTITY; | ||
| 1760 | type = TYPE_IDENTITY; | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | /** | ||
| 1764 | * Sets this transform to a translation transformation. | ||
| 1765 | * The matrix representing this transform becomes: | ||
| 1766 | * <pre> | ||
| 1767 | * [ 1 0 tx ] | ||
| 1768 | * [ 0 1 ty ] | ||
| 1769 | * [ 0 0 1 ] | ||
| 1770 | * </pre> | ||
| 1771 | * @param tx the distance by which coordinates are translated in the | ||
| 1772 | * X axis direction | ||
| 1773 | * @param ty the distance by which coordinates are translated in the | ||
| 1774 | * Y axis direction | ||
| 1775 | * @since 1.2 | ||
| 1776 | */ | ||
| 1777 | public void setToTranslation(double tx, double ty) { | ||
| 1778 | m00 = 1.0; | ||
| 1779 | m10 = 0.0; | ||
| 1780 | m01 = 0.0; | ||
| 1781 | m11 = 1.0; | ||
| 1782 | m02 = tx; | ||
| 1783 | m12 = ty; | ||
| 1784 | if (tx != 0.0 || ty != 0.0) { | ||
| 1785 | state = APPLY_TRANSLATE; | ||
| 1786 | type = TYPE_TRANSLATION; | ||
| 1787 | } else { | ||
| 1788 | state = APPLY_IDENTITY; | ||
| 1789 | type = TYPE_IDENTITY; | ||
| 1790 | } | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | /** | ||
| 1794 | * Sets this transform to a rotation transformation. | ||
| 1795 | * The matrix representing this transform becomes: | ||
| 1796 | * <pre> | ||
| 1797 | * [ cos(theta) -sin(theta) 0 ] | ||
| 1798 | * [ sin(theta) cos(theta) 0 ] | ||
| 1799 | * [ 0 0 1 ] | ||
| 1800 | * </pre> | ||
| 1801 | * Rotating by a positive angle theta rotates points on the positive | ||
| 1802 | * X axis toward the positive Y axis. | ||
| 1803 | * Note also the discussion of | ||
| 1804 | * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> | ||
| 1805 | * above. | ||
| 1806 | * @param theta the angle of rotation measured in radians | ||
| 1807 | * @since 1.2 | ||
| 1808 | */ | ||
| 1809 | public void setToRotation(double theta) { | ||
| 1810 | double sin = Math.sin(theta); | ||
| 1811 | double cos; | ||
| 1812 | if (sin == 1.0 || sin == -1.0) { | ||
| 1813 | cos = 0.0; | ||
| 1814 | state = APPLY_SHEAR; | ||
| 1815 | type = TYPE_QUADRANT_ROTATION; | ||
| 1816 | } else { | ||
| 1817 | cos = Math.cos(theta); | ||
| 1818 | if (cos == -1.0) { | ||
| 1819 | sin = 0.0; | ||
| 1820 | state = APPLY_SCALE; | ||
| 1821 | type = TYPE_QUADRANT_ROTATION; | ||
| 1822 | } else if (cos == 1.0) { | ||
| 1823 | sin = 0.0; | ||
| 1824 | state = APPLY_IDENTITY; | ||
| 1825 | type = TYPE_IDENTITY; | ||
| 1826 | } else { | ||
| 1827 | state = APPLY_SHEAR | APPLY_SCALE; | ||
| 1828 | type = TYPE_GENERAL_ROTATION; | ||
| 1829 | } | ||
| 1830 | } | ||
| 1831 | m00 = cos; | ||
| 1832 | m10 = sin; | ||
| 1833 | m01 = -sin; | ||
| 1834 | m11 = cos; | ||
| 1835 | m02 = 0.0; | ||
| 1836 | m12 = 0.0; | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | /** | ||
| 1840 | * Sets this transform to a translated rotation transformation. | ||
| 1841 | * This operation is equivalent to translating the coordinates so | ||
| 1842 | * that the anchor point is at the origin (S1), then rotating them | ||
| 1843 | * about the new origin (S2), and finally translating so that the | ||
| 1844 | * intermediate origin is restored to the coordinates of the original | ||
| 1845 | * anchor point (S3). | ||
| 1846 | * <p> | ||
| 1847 | * This operation is equivalent to the following sequence of calls: | ||
| 1848 | * <pre> | ||
| 1849 | * setToTranslation(anchorx, anchory); // S3: final translation | ||
| 1850 | * rotate(theta); // S2: rotate around anchor | ||
| 1851 | * translate(-anchorx, -anchory); // S1: translate anchor to origin | ||
| 1852 | * </pre> | ||
| 1853 | * The matrix representing this transform becomes: | ||
| 1854 | * <pre> | ||
| 1855 | * [ cos(theta) -sin(theta) x-x*cos+y*sin ] | ||
| 1856 | * [ sin(theta) cos(theta) y-x*sin-y*cos ] | ||
| 1857 | * [ 0 0 1 ] | ||
| 1858 | * </pre> | ||
| 1859 | * Rotating by a positive angle theta rotates points on the positive | ||
| 1860 | * X axis toward the positive Y axis. | ||
| 1861 | * Note also the discussion of | ||
| 1862 | * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> | ||
| 1863 | * above. | ||
| 1864 | * | ||
| 1865 | * @param theta the angle of rotation measured in radians | ||
| 1866 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 1867 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 1868 | * @since 1.2 | ||
| 1869 | */ | ||
| 1870 | public void setToRotation(double theta, double anchorx, double anchory) { | ||
| 1871 | setToRotation(theta); | ||
| 1872 | double sin = m10; | ||
| 1873 | double oneMinusCos = 1.0 - m00; | ||
| 1874 | m02 = anchorx * oneMinusCos + anchory * sin; | ||
| 1875 | m12 = anchory * oneMinusCos - anchorx * sin; | ||
| 1876 | if (m02 != 0.0 || m12 != 0.0) { | ||
| 1877 | state |= APPLY_TRANSLATE; | ||
| 1878 | type |= TYPE_TRANSLATION; | ||
| 1879 | } | ||
| 1880 | } | ||
| 1881 | |||
| 1882 | /** | ||
| 1883 | * Sets this transform to a rotation transformation that rotates | ||
| 1884 | * coordinates according to a rotation vector. | ||
| 1885 | * All coordinates rotate about the origin by the same amount. | ||
| 1886 | * The amount of rotation is such that coordinates along the former | ||
| 1887 | * positive X axis will subsequently align with the vector pointing | ||
| 1888 | * from the origin to the specified vector coordinates. | ||
| 1889 | * If both <code>vecx</code> and <code>vecy</code> are 0.0, | ||
| 1890 | * the transform is set to an identity transform. | ||
| 1891 | * This operation is equivalent to calling: | ||
| 1892 | * <pre> | ||
| 1893 | * setToRotation(Math.atan2(vecy, vecx)); | ||
| 1894 | * </pre> | ||
| 1895 | * | ||
| 1896 | * @param vecx the X coordinate of the rotation vector | ||
| 1897 | * @param vecy the Y coordinate of the rotation vector | ||
| 1898 | * @since 1.6 | ||
| 1899 | */ | ||
| 1900 | public void setToRotation(double vecx, double vecy) { | ||
| 1901 | double sin, cos; | ||
| 1902 | if (vecy == 0) { | ||
| 1903 | sin = 0.0; | ||
| 1904 | if (vecx < 0.0) { | ||
| 1905 | cos = -1.0; | ||
| 1906 | state = APPLY_SCALE; | ||
| 1907 | type = TYPE_QUADRANT_ROTATION; | ||
| 1908 | } else { | ||
| 1909 | cos = 1.0; | ||
| 1910 | state = APPLY_IDENTITY; | ||
| 1911 | type = TYPE_IDENTITY; | ||
| 1912 | } | ||
| 1913 | } else if (vecx == 0) { | ||
| 1914 | cos = 0.0; | ||
| 1915 | sin = (vecy > 0.0) ? 1.0 : -1.0; | ||
| 1916 | state = APPLY_SHEAR; | ||
| 1917 | type = TYPE_QUADRANT_ROTATION; | ||
| 1918 | } else { | ||
| 1919 | double len = Math.sqrt(vecx * vecx + vecy * vecy); | ||
| 1920 | cos = vecx / len; | ||
| 1921 | sin = vecy / len; | ||
| 1922 | state = APPLY_SHEAR | APPLY_SCALE; | ||
| 1923 | type = TYPE_GENERAL_ROTATION; | ||
| 1924 | } | ||
| 1925 | m00 = cos; | ||
| 1926 | m10 = sin; | ||
| 1927 | m01 = -sin; | ||
| 1928 | m11 = cos; | ||
| 1929 | m02 = 0.0; | ||
| 1930 | m12 = 0.0; | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | /** | ||
| 1934 | * Sets this transform to a rotation transformation that rotates | ||
| 1935 | * coordinates around an anchor point according to a rotation | ||
| 1936 | * vector. | ||
| 1937 | * All coordinates rotate about the specified anchor coordinates | ||
| 1938 | * by the same amount. | ||
| 1939 | * The amount of rotation is such that coordinates along the former | ||
| 1940 | * positive X axis will subsequently align with the vector pointing | ||
| 1941 | * from the origin to the specified vector coordinates. | ||
| 1942 | * If both <code>vecx</code> and <code>vecy</code> are 0.0, | ||
| 1943 | * the transform is set to an identity transform. | ||
| 1944 | * This operation is equivalent to calling: | ||
| 1945 | * <pre> | ||
| 1946 | * setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory); | ||
| 1947 | * </pre> | ||
| 1948 | * | ||
| 1949 | * @param vecx the X coordinate of the rotation vector | ||
| 1950 | * @param vecy the Y coordinate of the rotation vector | ||
| 1951 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 1952 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 1953 | * @since 1.6 | ||
| 1954 | */ | ||
| 1955 | public void setToRotation(double vecx, double vecy, | ||
| 1956 | double anchorx, double anchory) | ||
| 1957 | { | ||
| 1958 | setToRotation(vecx, vecy); | ||
| 1959 | double sin = m10; | ||
| 1960 | double oneMinusCos = 1.0 - m00; | ||
| 1961 | m02 = anchorx * oneMinusCos + anchory * sin; | ||
| 1962 | m12 = anchory * oneMinusCos - anchorx * sin; | ||
| 1963 | if (m02 != 0.0 || m12 != 0.0) { | ||
| 1964 | state |= APPLY_TRANSLATE; | ||
| 1965 | type |= TYPE_TRANSLATION; | ||
| 1966 | } | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | /** | ||
| 1970 | * Sets this transform to a rotation transformation that rotates | ||
| 1971 | * coordinates by the specified number of quadrants. | ||
| 1972 | * This operation is equivalent to calling: | ||
| 1973 | * <pre> | ||
| 1974 | * setToRotation(numquadrants * Math.PI / 2.0); | ||
| 1975 | * </pre> | ||
| 1976 | * Rotating by a positive number of quadrants rotates points on | ||
| 1977 | * the positive X axis toward the positive Y axis. | ||
| 1978 | * @param numquadrants the number of 90 degree arcs to rotate by | ||
| 1979 | * @since 1.6 | ||
| 1980 | */ | ||
| 1981 | public void setToQuadrantRotation(int numquadrants) { | ||
| 1982 | switch (numquadrants & 3) { | ||
| 1983 | case 0: | ||
| 1984 | m00 = 1.0; | ||
| 1985 | m10 = 0.0; | ||
| 1986 | m01 = 0.0; | ||
| 1987 | m11 = 1.0; | ||
| 1988 | m02 = 0.0; | ||
| 1989 | m12 = 0.0; | ||
| 1990 | state = APPLY_IDENTITY; | ||
| 1991 | type = TYPE_IDENTITY; | ||
| 1992 | break; | ||
| 1993 | case 1: | ||
| 1994 | m00 = 0.0; | ||
| 1995 | m10 = 1.0; | ||
| 1996 | m01 = -1.0; | ||
| 1997 | m11 = 0.0; | ||
| 1998 | m02 = 0.0; | ||
| 1999 | m12 = 0.0; | ||
| 2000 | state = APPLY_SHEAR; | ||
| 2001 | type = TYPE_QUADRANT_ROTATION; | ||
| 2002 | break; | ||
| 2003 | case 2: | ||
| 2004 | m00 = -1.0; | ||
| 2005 | m10 = 0.0; | ||
| 2006 | m01 = 0.0; | ||
| 2007 | m11 = -1.0; | ||
| 2008 | m02 = 0.0; | ||
| 2009 | m12 = 0.0; | ||
| 2010 | state = APPLY_SCALE; | ||
| 2011 | type = TYPE_QUADRANT_ROTATION; | ||
| 2012 | break; | ||
| 2013 | case 3: | ||
| 2014 | m00 = 0.0; | ||
| 2015 | m10 = -1.0; | ||
| 2016 | m01 = 1.0; | ||
| 2017 | m11 = 0.0; | ||
| 2018 | m02 = 0.0; | ||
| 2019 | m12 = 0.0; | ||
| 2020 | state = APPLY_SHEAR; | ||
| 2021 | type = TYPE_QUADRANT_ROTATION; | ||
| 2022 | break; | ||
| 2023 | } | ||
| 2024 | } | ||
| 2025 | |||
| 2026 | /** | ||
| 2027 | * Sets this transform to a translated rotation transformation | ||
| 2028 | * that rotates coordinates by the specified number of quadrants | ||
| 2029 | * around the specified anchor point. | ||
| 2030 | * This operation is equivalent to calling: | ||
| 2031 | * <pre> | ||
| 2032 | * setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory); | ||
| 2033 | * </pre> | ||
| 2034 | * Rotating by a positive number of quadrants rotates points on | ||
| 2035 | * the positive X axis toward the positive Y axis. | ||
| 2036 | * | ||
| 2037 | * @param numquadrants the number of 90 degree arcs to rotate by | ||
| 2038 | * @param anchorx the X coordinate of the rotation anchor point | ||
| 2039 | * @param anchory the Y coordinate of the rotation anchor point | ||
| 2040 | * @since 1.6 | ||
| 2041 | */ | ||
| 2042 | public void setToQuadrantRotation(int numquadrants, | ||
| 2043 | double anchorx, double anchory) | ||
| 2044 | { | ||
| 2045 | switch (numquadrants & 3) { | ||
| 2046 | case 0: | ||
| 2047 | m00 = 1.0; | ||
| 2048 | m10 = 0.0; | ||
| 2049 | m01 = 0.0; | ||
| 2050 | m11 = 1.0; | ||
| 2051 | m02 = 0.0; | ||
| 2052 | m12 = 0.0; | ||
| 2053 | state = APPLY_IDENTITY; | ||
| 2054 | type = TYPE_IDENTITY; | ||
| 2055 | break; | ||
| 2056 | case 1: | ||
| 2057 | m00 = 0.0; | ||
| 2058 | m10 = 1.0; | ||
| 2059 | m01 = -1.0; | ||
| 2060 | m11 = 0.0; | ||
| 2061 | m02 = anchorx + anchory; | ||
| 2062 | m12 = anchory - anchorx; | ||
| 2063 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 2064 | state = APPLY_SHEAR; | ||
| 2065 | type = TYPE_QUADRANT_ROTATION; | ||
| 2066 | } else { | ||
| 2067 | state = APPLY_SHEAR | APPLY_TRANSLATE; | ||
| 2068 | type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; | ||
| 2069 | } | ||
| 2070 | break; | ||
| 2071 | case 2: | ||
| 2072 | m00 = -1.0; | ||
| 2073 | m10 = 0.0; | ||
| 2074 | m01 = 0.0; | ||
| 2075 | m11 = -1.0; | ||
| 2076 | m02 = anchorx + anchorx; | ||
| 2077 | m12 = anchory + anchory; | ||
| 2078 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 2079 | state = APPLY_SCALE; | ||
| 2080 | type = TYPE_QUADRANT_ROTATION; | ||
| 2081 | } else { | ||
| 2082 | state = APPLY_SCALE | APPLY_TRANSLATE; | ||
| 2083 | type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; | ||
| 2084 | } | ||
| 2085 | break; | ||
| 2086 | case 3: | ||
| 2087 | m00 = 0.0; | ||
| 2088 | m10 = -1.0; | ||
| 2089 | m01 = 1.0; | ||
| 2090 | m11 = 0.0; | ||
| 2091 | m02 = anchorx - anchory; | ||
| 2092 | m12 = anchory + anchorx; | ||
| 2093 | if (m02 == 0.0 && m12 == 0.0) { | ||
| 2094 | state = APPLY_SHEAR; | ||
| 2095 | type = TYPE_QUADRANT_ROTATION; | ||
| 2096 | } else { | ||
| 2097 | state = APPLY_SHEAR | APPLY_TRANSLATE; | ||
| 2098 | type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; | ||
| 2099 | } | ||
| 2100 | break; | ||
| 2101 | } | ||
| 2102 | } | ||
| 2103 | |||
| 2104 | /** | ||
| 2105 | * Sets this transform to a scaling transformation. | ||
| 2106 | * The matrix representing this transform becomes: | ||
| 2107 | * <pre> | ||
| 2108 | * [ sx 0 0 ] | ||
| 2109 | * [ 0 sy 0 ] | ||
| 2110 | * [ 0 0 1 ] | ||
| 2111 | * </pre> | ||
| 2112 | * @param sx the factor by which coordinates are scaled along the | ||
| 2113 | * X axis direction | ||
| 2114 | * @param sy the factor by which coordinates are scaled along the | ||
| 2115 | * Y axis direction | ||
| 2116 | * @since 1.2 | ||
| 2117 | */ | ||
| 2118 | public void setToScale(double sx, double sy) { | ||
| 2119 | m00 = sx; | ||
| 2120 | m10 = 0.0; | ||
| 2121 | m01 = 0.0; | ||
| 2122 | m11 = sy; | ||
| 2123 | m02 = 0.0; | ||
| 2124 | m12 = 0.0; | ||
| 2125 | if (sx != 1.0 || sy != 1.0) { | ||
| 2126 | state = APPLY_SCALE; | ||
| 2127 | type = TYPE_UNKNOWN; | ||
| 2128 | } else { | ||
| 2129 | state = APPLY_IDENTITY; | ||
| 2130 | type = TYPE_IDENTITY; | ||
| 2131 | } | ||
| 2132 | } | ||
| 2133 | |||
| 2134 | /** | ||
| 2135 | * Sets this transform to a shearing transformation. | ||
| 2136 | * The matrix representing this transform becomes: | ||
| 2137 | * <pre> | ||
| 2138 | * [ 1 shx 0 ] | ||
| 2139 | * [ shy 1 0 ] | ||
| 2140 | * [ 0 0 1 ] | ||
| 2141 | * </pre> | ||
| 2142 | * @param shx the multiplier by which coordinates are shifted in the | ||
| 2143 | * direction of the positive X axis as a factor of their Y coordinate | ||
| 2144 | * @param shy the multiplier by which coordinates are shifted in the | ||
| 2145 | * direction of the positive Y axis as a factor of their X coordinate | ||
| 2146 | * @since 1.2 | ||
| 2147 | */ | ||
| 2148 | public void setToShear(double shx, double shy) { | ||
| 2149 | m00 = 1.0; | ||
| 2150 | m01 = shx; | ||
| 2151 | m10 = shy; | ||
| 2152 | m11 = 1.0; | ||
| 2153 | m02 = 0.0; | ||
| 2154 | m12 = 0.0; | ||
| 2155 | if (shx != 0.0 || shy != 0.0) { | ||
| 2156 | state = (APPLY_SHEAR | APPLY_SCALE); | ||
| 2157 | type = TYPE_UNKNOWN; | ||
| 2158 | } else { | ||
| 2159 | state = APPLY_IDENTITY; | ||
| 2160 | type = TYPE_IDENTITY; | ||
| 2161 | } | ||
| 2162 | } | ||
| 2163 | |||
| 2164 | /** | ||
| 2165 | * Sets this transform to a copy of the transform in the specified | ||
| 2166 | * <code>AffineTransform</code> object. | ||
| 2167 | * @param Tx the <code>AffineTransform</code> object from which to | ||
| 2168 | * copy the transform | ||
| 2169 | * @since 1.2 | ||
| 2170 | */ | ||
| 2171 | public void setTransform(AffineTransform Tx) { | ||
| 2172 | this.m00 = Tx.m00; | ||
| 2173 | this.m10 = Tx.m10; | ||
| 2174 | this.m01 = Tx.m01; | ||
| 2175 | this.m11 = Tx.m11; | ||
| 2176 | this.m02 = Tx.m02; | ||
| 2177 | this.m12 = Tx.m12; | ||
| 2178 | this.state = Tx.state; | ||
| 2179 | this.type = Tx.type; | ||
| 2180 | } | ||
| 2181 | |||
| 2182 | /** | ||
| 2183 | * Sets this transform to the matrix specified by the 6 | ||
| 2184 | * double precision values. | ||
| 2185 | * | ||
| 2186 | * @param m00 the X coordinate scaling element of the 3x3 matrix | ||
| 2187 | * @param m10 the Y coordinate shearing element of the 3x3 matrix | ||
| 2188 | * @param m01 the X coordinate shearing element of the 3x3 matrix | ||
| 2189 | * @param m11 the Y coordinate scaling element of the 3x3 matrix | ||
| 2190 | * @param m02 the X coordinate translation element of the 3x3 matrix | ||
| 2191 | * @param m12 the Y coordinate translation element of the 3x3 matrix | ||
| 2192 | * @since 1.2 | ||
| 2193 | */ | ||
| 2194 | public void setTransform(double m00, double m10, | ||
| 2195 | double m01, double m11, | ||
| 2196 | double m02, double m12) { | ||
| 2197 | this.m00 = m00; | ||
| 2198 | this.m10 = m10; | ||
| 2199 | this.m01 = m01; | ||
| 2200 | this.m11 = m11; | ||
| 2201 | this.m02 = m02; | ||
| 2202 | this.m12 = m12; | ||
| 2203 | updateState(); | ||
| 2204 | } | ||
| 2205 | |||
| 2206 | /** | ||
| 2207 | * Concatenates an <code>AffineTransform</code> <code>Tx</code> to | ||
| 2208 | * this <code>AffineTransform</code> Cx in the most commonly useful | ||
| 2209 | * way to provide a new user space | ||
| 2210 | * that is mapped to the former user space by <code>Tx</code>. | ||
| 2211 | * Cx is updated to perform the combined transformation. | ||
| 2212 | * Transforming a point p by the updated transform Cx' is | ||
| 2213 | * equivalent to first transforming p by <code>Tx</code> and then | ||
| 2214 | * transforming the result by the original transform Cx like this: | ||
| 2215 | * Cx'(p) = Cx(Tx(p)) | ||
| 2216 | * In matrix notation, if this transform Cx is | ||
| 2217 | * represented by the matrix [this] and <code>Tx</code> is represented | ||
| 2218 | * by the matrix [Tx] then this method does the following: | ||
| 2219 | * <pre> | ||
| 2220 | * [this] = [this] x [Tx] | ||
| 2221 | * </pre> | ||
| 2222 | * @param Tx the <code>AffineTransform</code> object to be | ||
| 2223 | * concatenated with this <code>AffineTransform</code> object. | ||
| 2224 | * @see #preConcatenate | ||
| 2225 | * @since 1.2 | ||
| 2226 | */ | ||
| 2227 | public void concatenate(AffineTransform Tx) { | ||
| 2228 | double M0, M1; | ||
| 2229 | double T00, T01, T10, T11; | ||
| 2230 | double T02, T12; | ||
| 2231 | int mystate = state; | ||
| 2232 | int txstate = Tx.state; | ||
| 2233 | switch ((txstate << HI_SHIFT) | mystate) { | ||
| 2234 | |||
| 2235 | /* ---------- Tx == IDENTITY cases ---------- */ | ||
| 2236 | case (HI_IDENTITY | APPLY_IDENTITY): | ||
| 2237 | case (HI_IDENTITY | APPLY_TRANSLATE): | ||
| 2238 | case (HI_IDENTITY | APPLY_SCALE): | ||
| 2239 | case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2240 | case (HI_IDENTITY | APPLY_SHEAR): | ||
| 2241 | case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2242 | case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): | ||
| 2243 | case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2244 | return; | ||
| 2245 | |||
| 2246 | /* ---------- this == IDENTITY cases ---------- */ | ||
| 2247 | case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): | ||
| 2248 | m01 = Tx.m01; | ||
| 2249 | m10 = Tx.m10; | ||
| 2250 | /* NOBREAK */ | ||
| 2251 | case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): | ||
| 2252 | m00 = Tx.m00; | ||
| 2253 | m11 = Tx.m11; | ||
| 2254 | /* NOBREAK */ | ||
| 2255 | case (HI_TRANSLATE | APPLY_IDENTITY): | ||
| 2256 | m02 = Tx.m02; | ||
| 2257 | m12 = Tx.m12; | ||
| 2258 | state = txstate; | ||
| 2259 | type = Tx.type; | ||
| 2260 | return; | ||
| 2261 | case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY): | ||
| 2262 | m01 = Tx.m01; | ||
| 2263 | m10 = Tx.m10; | ||
| 2264 | /* NOBREAK */ | ||
| 2265 | case (HI_SCALE | APPLY_IDENTITY): | ||
| 2266 | m00 = Tx.m00; | ||
| 2267 | m11 = Tx.m11; | ||
| 2268 | state = txstate; | ||
| 2269 | type = Tx.type; | ||
| 2270 | return; | ||
| 2271 | case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY): | ||
| 2272 | m02 = Tx.m02; | ||
| 2273 | m12 = Tx.m12; | ||
| 2274 | /* NOBREAK */ | ||
| 2275 | case (HI_SHEAR | APPLY_IDENTITY): | ||
| 2276 | m01 = Tx.m01; | ||
| 2277 | m10 = Tx.m10; | ||
| 2278 | m00 = m11 = 0.0; | ||
| 2279 | state = txstate; | ||
| 2280 | type = Tx.type; | ||
| 2281 | return; | ||
| 2282 | |||
| 2283 | /* ---------- Tx == TRANSLATE cases ---------- */ | ||
| 2284 | case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2285 | case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): | ||
| 2286 | case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2287 | case (HI_TRANSLATE | APPLY_SHEAR): | ||
| 2288 | case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2289 | case (HI_TRANSLATE | APPLY_SCALE): | ||
| 2290 | case (HI_TRANSLATE | APPLY_TRANSLATE): | ||
| 2291 | translate(Tx.m02, Tx.m12); | ||
| 2292 | return; | ||
| 2293 | |||
| 2294 | /* ---------- Tx == SCALE cases ---------- */ | ||
| 2295 | case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2296 | case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): | ||
| 2297 | case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2298 | case (HI_SCALE | APPLY_SHEAR): | ||
| 2299 | case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2300 | case (HI_SCALE | APPLY_SCALE): | ||
| 2301 | case (HI_SCALE | APPLY_TRANSLATE): | ||
| 2302 | scale(Tx.m00, Tx.m11); | ||
| 2303 | return; | ||
| 2304 | |||
| 2305 | /* ---------- Tx == SHEAR cases ---------- */ | ||
| 2306 | case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2307 | case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): | ||
| 2308 | T01 = Tx.m01; T10 = Tx.m10; | ||
| 2309 | M0 = m00; | ||
| 2310 | m00 = m01 * T10; | ||
| 2311 | m01 = M0 * T01; | ||
| 2312 | M0 = m10; | ||
| 2313 | m10 = m11 * T10; | ||
| 2314 | m11 = M0 * T01; | ||
| 2315 | type = TYPE_UNKNOWN; | ||
| 2316 | return; | ||
| 2317 | case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2318 | case (HI_SHEAR | APPLY_SHEAR): | ||
| 2319 | m00 = m01 * Tx.m10; | ||
| 2320 | m01 = 0.0; | ||
| 2321 | m11 = m10 * Tx.m01; | ||
| 2322 | m10 = 0.0; | ||
| 2323 | state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); | ||
| 2324 | type = TYPE_UNKNOWN; | ||
| 2325 | return; | ||
| 2326 | case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2327 | case (HI_SHEAR | APPLY_SCALE): | ||
| 2328 | m01 = m00 * Tx.m01; | ||
| 2329 | m00 = 0.0; | ||
| 2330 | m10 = m11 * Tx.m10; | ||
| 2331 | m11 = 0.0; | ||
| 2332 | state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); | ||
| 2333 | type = TYPE_UNKNOWN; | ||
| 2334 | return; | ||
| 2335 | case (HI_SHEAR | APPLY_TRANSLATE): | ||
| 2336 | m00 = 0.0; | ||
| 2337 | m01 = Tx.m01; | ||
| 2338 | m10 = Tx.m10; | ||
| 2339 | m11 = 0.0; | ||
| 2340 | state = APPLY_TRANSLATE | APPLY_SHEAR; | ||
| 2341 | type = TYPE_UNKNOWN; | ||
| 2342 | return; | ||
| 2343 | } | ||
| 2344 | // If Tx has more than one attribute, it is not worth optimizing | ||
| 2345 | // all of those cases... | ||
| 2346 | T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02; | ||
| 2347 | T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12; | ||
| 2348 | switch (mystate) { | ||
| 2349 | default: | ||
| 2350 | stateError(); | ||
| 2351 | /* NOTREACHED */ | ||
| 2352 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 2353 | state = mystate | txstate; | ||
| 2354 | /* NOBREAK */ | ||
| 2355 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2356 | M0 = m00; | ||
| 2357 | M1 = m01; | ||
| 2358 | m00 = T00 * M0 + T10 * M1; | ||
| 2359 | m01 = T01 * M0 + T11 * M1; | ||
| 2360 | m02 += T02 * M0 + T12 * M1; | ||
| 2361 | |||
| 2362 | M0 = m10; | ||
| 2363 | M1 = m11; | ||
| 2364 | m10 = T00 * M0 + T10 * M1; | ||
| 2365 | m11 = T01 * M0 + T11 * M1; | ||
| 2366 | m12 += T02 * M0 + T12 * M1; | ||
| 2367 | type = TYPE_UNKNOWN; | ||
| 2368 | return; | ||
| 2369 | |||
| 2370 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2371 | case (APPLY_SHEAR): | ||
| 2372 | M0 = m01; | ||
| 2373 | m00 = T10 * M0; | ||
| 2374 | m01 = T11 * M0; | ||
| 2375 | m02 += T12 * M0; | ||
| 2376 | |||
| 2377 | M0 = m10; | ||
| 2378 | m10 = T00 * M0; | ||
| 2379 | m11 = T01 * M0; | ||
| 2380 | m12 += T02 * M0; | ||
| 2381 | break; | ||
| 2382 | |||
| 2383 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2384 | case (APPLY_SCALE): | ||
| 2385 | M0 = m00; | ||
| 2386 | m00 = T00 * M0; | ||
| 2387 | m01 = T01 * M0; | ||
| 2388 | m02 += T02 * M0; | ||
| 2389 | |||
| 2390 | M0 = m11; | ||
| 2391 | m10 = T10 * M0; | ||
| 2392 | m11 = T11 * M0; | ||
| 2393 | m12 += T12 * M0; | ||
| 2394 | break; | ||
| 2395 | |||
| 2396 | case (APPLY_TRANSLATE): | ||
| 2397 | m00 = T00; | ||
| 2398 | m01 = T01; | ||
| 2399 | m02 += T02; | ||
| 2400 | |||
| 2401 | m10 = T10; | ||
| 2402 | m11 = T11; | ||
| 2403 | m12 += T12; | ||
| 2404 | state = txstate | APPLY_TRANSLATE; | ||
| 2405 | type = TYPE_UNKNOWN; | ||
| 2406 | return; | ||
| 2407 | } | ||
| 2408 | updateState(); | ||
| 2409 | } | ||
| 2410 | |||
| 2411 | /** | ||
| 2412 | * Concatenates an <code>AffineTransform</code> <code>Tx</code> to | ||
| 2413 | * this <code>AffineTransform</code> Cx | ||
| 2414 | * in a less commonly used way such that <code>Tx</code> modifies the | ||
| 2415 | * coordinate transformation relative to the absolute pixel | ||
| 2416 | * space rather than relative to the existing user space. | ||
| 2417 | * Cx is updated to perform the combined transformation. | ||
| 2418 | * Transforming a point p by the updated transform Cx' is | ||
| 2419 | * equivalent to first transforming p by the original transform | ||
| 2420 | * Cx and then transforming the result by | ||
| 2421 | * <code>Tx</code> like this: | ||
| 2422 | * Cx'(p) = Tx(Cx(p)) | ||
| 2423 | * In matrix notation, if this transform Cx | ||
| 2424 | * is represented by the matrix [this] and <code>Tx</code> is | ||
| 2425 | * represented by the matrix [Tx] then this method does the | ||
| 2426 | * following: | ||
| 2427 | * <pre> | ||
| 2428 | * [this] = [Tx] x [this] | ||
| 2429 | * </pre> | ||
| 2430 | * @param Tx the <code>AffineTransform</code> object to be | ||
| 2431 | * concatenated with this <code>AffineTransform</code> object. | ||
| 2432 | * @see #concatenate | ||
| 2433 | * @since 1.2 | ||
| 2434 | */ | ||
| 2435 | public void preConcatenate(AffineTransform Tx) { | ||
| 2436 | double M0, M1; | ||
| 2437 | double T00, T01, T10, T11; | ||
| 2438 | double T02, T12; | ||
| 2439 | int mystate = state; | ||
| 2440 | int txstate = Tx.state; | ||
| 2441 | switch ((txstate << HI_SHIFT) | mystate) { | ||
| 2442 | case (HI_IDENTITY | APPLY_IDENTITY): | ||
| 2443 | case (HI_IDENTITY | APPLY_TRANSLATE): | ||
| 2444 | case (HI_IDENTITY | APPLY_SCALE): | ||
| 2445 | case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2446 | case (HI_IDENTITY | APPLY_SHEAR): | ||
| 2447 | case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2448 | case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): | ||
| 2449 | case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2450 | // Tx is IDENTITY... | ||
| 2451 | return; | ||
| 2452 | |||
| 2453 | case (HI_TRANSLATE | APPLY_IDENTITY): | ||
| 2454 | case (HI_TRANSLATE | APPLY_SCALE): | ||
| 2455 | case (HI_TRANSLATE | APPLY_SHEAR): | ||
| 2456 | case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): | ||
| 2457 | // Tx is TRANSLATE, this has no TRANSLATE | ||
| 2458 | m02 = Tx.m02; | ||
| 2459 | m12 = Tx.m12; | ||
| 2460 | state = mystate | APPLY_TRANSLATE; | ||
| 2461 | type |= TYPE_TRANSLATION; | ||
| 2462 | return; | ||
| 2463 | |||
| 2464 | case (HI_TRANSLATE | APPLY_TRANSLATE): | ||
| 2465 | case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2466 | case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2467 | case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2468 | // Tx is TRANSLATE, this has one too | ||
| 2469 | m02 = m02 + Tx.m02; | ||
| 2470 | m12 = m12 + Tx.m12; | ||
| 2471 | return; | ||
| 2472 | |||
| 2473 | case (HI_SCALE | APPLY_TRANSLATE): | ||
| 2474 | case (HI_SCALE | APPLY_IDENTITY): | ||
| 2475 | // Only these two existing states need a new state | ||
| 2476 | state = mystate | APPLY_SCALE; | ||
| 2477 | /* NOBREAK */ | ||
| 2478 | case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2479 | case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): | ||
| 2480 | case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2481 | case (HI_SCALE | APPLY_SHEAR): | ||
| 2482 | case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2483 | case (HI_SCALE | APPLY_SCALE): | ||
| 2484 | // Tx is SCALE, this is anything | ||
| 2485 | T00 = Tx.m00; | ||
| 2486 | T11 = Tx.m11; | ||
| 2487 | if ((mystate & APPLY_SHEAR) != 0) { | ||
| 2488 | m01 = m01 * T00; | ||
| 2489 | m10 = m10 * T11; | ||
| 2490 | if ((mystate & APPLY_SCALE) != 0) { | ||
| 2491 | m00 = m00 * T00; | ||
| 2492 | m11 = m11 * T11; | ||
| 2493 | } | ||
| 2494 | } else { | ||
| 2495 | m00 = m00 * T00; | ||
| 2496 | m11 = m11 * T11; | ||
| 2497 | } | ||
| 2498 | if ((mystate & APPLY_TRANSLATE) != 0) { | ||
| 2499 | m02 = m02 * T00; | ||
| 2500 | m12 = m12 * T11; | ||
| 2501 | } | ||
| 2502 | type = TYPE_UNKNOWN; | ||
| 2503 | return; | ||
| 2504 | case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2505 | case (HI_SHEAR | APPLY_SHEAR): | ||
| 2506 | mystate = mystate | APPLY_SCALE; | ||
| 2507 | /* NOBREAK */ | ||
| 2508 | case (HI_SHEAR | APPLY_TRANSLATE): | ||
| 2509 | case (HI_SHEAR | APPLY_IDENTITY): | ||
| 2510 | case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2511 | case (HI_SHEAR | APPLY_SCALE): | ||
| 2512 | state = mystate ^ APPLY_SHEAR; | ||
| 2513 | /* NOBREAK */ | ||
| 2514 | case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2515 | case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): | ||
| 2516 | // Tx is SHEAR, this is anything | ||
| 2517 | T01 = Tx.m01; | ||
| 2518 | T10 = Tx.m10; | ||
| 2519 | |||
| 2520 | M0 = m00; | ||
| 2521 | m00 = m10 * T01; | ||
| 2522 | m10 = M0 * T10; | ||
| 2523 | |||
| 2524 | M0 = m01; | ||
| 2525 | m01 = m11 * T01; | ||
| 2526 | m11 = M0 * T10; | ||
| 2527 | |||
| 2528 | M0 = m02; | ||
| 2529 | m02 = m12 * T01; | ||
| 2530 | m12 = M0 * T10; | ||
| 2531 | type = TYPE_UNKNOWN; | ||
| 2532 | return; | ||
| 2533 | } | ||
| 2534 | // If Tx has more than one attribute, it is not worth optimizing | ||
| 2535 | // all of those cases... | ||
| 2536 | T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02; | ||
| 2537 | T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12; | ||
| 2538 | switch (mystate) { | ||
| 2539 | default: | ||
| 2540 | stateError(); | ||
| 2541 | /* NOTREACHED */ | ||
| 2542 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2543 | M0 = m02; | ||
| 2544 | M1 = m12; | ||
| 2545 | T02 += M0 * T00 + M1 * T01; | ||
| 2546 | T12 += M0 * T10 + M1 * T11; | ||
| 2547 | |||
| 2548 | /* NOBREAK */ | ||
| 2549 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 2550 | m02 = T02; | ||
| 2551 | m12 = T12; | ||
| 2552 | |||
| 2553 | M0 = m00; | ||
| 2554 | M1 = m10; | ||
| 2555 | m00 = M0 * T00 + M1 * T01; | ||
| 2556 | m10 = M0 * T10 + M1 * T11; | ||
| 2557 | |||
| 2558 | M0 = m01; | ||
| 2559 | M1 = m11; | ||
| 2560 | m01 = M0 * T00 + M1 * T01; | ||
| 2561 | m11 = M0 * T10 + M1 * T11; | ||
| 2562 | break; | ||
| 2563 | |||
| 2564 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2565 | M0 = m02; | ||
| 2566 | M1 = m12; | ||
| 2567 | T02 += M0 * T00 + M1 * T01; | ||
| 2568 | T12 += M0 * T10 + M1 * T11; | ||
| 2569 | |||
| 2570 | /* NOBREAK */ | ||
| 2571 | case (APPLY_SHEAR): | ||
| 2572 | m02 = T02; | ||
| 2573 | m12 = T12; | ||
| 2574 | |||
| 2575 | M0 = m10; | ||
| 2576 | m00 = M0 * T01; | ||
| 2577 | m10 = M0 * T11; | ||
| 2578 | |||
| 2579 | M0 = m01; | ||
| 2580 | m01 = M0 * T00; | ||
| 2581 | m11 = M0 * T10; | ||
| 2582 | break; | ||
| 2583 | |||
| 2584 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2585 | M0 = m02; | ||
| 2586 | M1 = m12; | ||
| 2587 | T02 += M0 * T00 + M1 * T01; | ||
| 2588 | T12 += M0 * T10 + M1 * T11; | ||
| 2589 | |||
| 2590 | /* NOBREAK */ | ||
| 2591 | case (APPLY_SCALE): | ||
| 2592 | m02 = T02; | ||
| 2593 | m12 = T12; | ||
| 2594 | |||
| 2595 | M0 = m00; | ||
| 2596 | m00 = M0 * T00; | ||
| 2597 | m10 = M0 * T10; | ||
| 2598 | |||
| 2599 | M0 = m11; | ||
| 2600 | m01 = M0 * T01; | ||
| 2601 | m11 = M0 * T11; | ||
| 2602 | break; | ||
| 2603 | |||
| 2604 | case (APPLY_TRANSLATE): | ||
| 2605 | M0 = m02; | ||
| 2606 | M1 = m12; | ||
| 2607 | T02 += M0 * T00 + M1 * T01; | ||
| 2608 | T12 += M0 * T10 + M1 * T11; | ||
| 2609 | |||
| 2610 | /* NOBREAK */ | ||
| 2611 | case (APPLY_IDENTITY): | ||
| 2612 | m02 = T02; | ||
| 2613 | m12 = T12; | ||
| 2614 | |||
| 2615 | m00 = T00; | ||
| 2616 | m10 = T10; | ||
| 2617 | |||
| 2618 | m01 = T01; | ||
| 2619 | m11 = T11; | ||
| 2620 | |||
| 2621 | state = mystate | txstate; | ||
| 2622 | type = TYPE_UNKNOWN; | ||
| 2623 | return; | ||
| 2624 | } | ||
| 2625 | updateState(); | ||
| 2626 | } | ||
| 2627 | |||
| 2628 | /** | ||
| 2629 | * Returns an <code>AffineTransform</code> object representing the | ||
| 2630 | * inverse transformation. | ||
| 2631 | * The inverse transform Tx' of this transform Tx | ||
| 2632 | * maps coordinates transformed by Tx back | ||
| 2633 | * to their original coordinates. | ||
| 2634 | * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). | ||
| 2635 | * <p> | ||
| 2636 | * If this transform maps all coordinates onto a point or a line | ||
| 2637 | * then it will not have an inverse, since coordinates that do | ||
| 2638 | * not lie on the destination point or line will not have an inverse | ||
| 2639 | * mapping. | ||
| 2640 | * The <code>getDeterminant</code> method can be used to determine if this | ||
| 2641 | * transform has no inverse, in which case an exception will be | ||
| 2642 | * thrown if the <code>createInverse</code> method is called. | ||
| 2643 | * @return a new <code>AffineTransform</code> object representing the | ||
| 2644 | * inverse transformation. | ||
| 2645 | * @see #getDeterminant | ||
| 2646 | * @exception NoninvertibleTransformException | ||
| 2647 | * if the matrix cannot be inverted. | ||
| 2648 | * @since 1.2 | ||
| 2649 | */ | ||
| 2650 | public AffineTransform createInverse() | ||
| 2651 | throws NoninvertibleTransformException | ||
| 2652 | { | ||
| 2653 | double det; | ||
| 2654 | switch (state) { | ||
| 2655 | default: | ||
| 2656 | stateError(); | ||
| 2657 | /* NOTREACHED */ | ||
| 2658 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2659 | det = m00 * m11 - m01 * m10; | ||
| 2660 | if (Math.abs(det) <= Double.MIN_VALUE) { | ||
| 2661 | throw new NoninvertibleTransformException("Determinant is "+ | ||
| 2662 | det); | ||
| 2663 | } | ||
| 2664 | return new AffineTransform( m11 / det, -m10 / det, | ||
| 2665 | -m01 / det, m00 / det, | ||
| 2666 | (m01 * m12 - m11 * m02) / det, | ||
| 2667 | (m10 * m02 - m00 * m12) / det, | ||
| 2668 | (APPLY_SHEAR | | ||
| 2669 | APPLY_SCALE | | ||
| 2670 | APPLY_TRANSLATE)); | ||
| 2671 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 2672 | det = m00 * m11 - m01 * m10; | ||
| 2673 | if (Math.abs(det) <= Double.MIN_VALUE) { | ||
| 2674 | throw new NoninvertibleTransformException("Determinant is "+ | ||
| 2675 | det); | ||
| 2676 | } | ||
| 2677 | return new AffineTransform( m11 / det, -m10 / det, | ||
| 2678 | -m01 / det, m00 / det, | ||
| 2679 | 0.0, 0.0, | ||
| 2680 | (APPLY_SHEAR | APPLY_SCALE)); | ||
| 2681 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2682 | if (m01 == 0.0 || m10 == 0.0) { | ||
| 2683 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2684 | } | ||
| 2685 | return new AffineTransform( 0.0, 1.0 / m01, | ||
| 2686 | 1.0 / m10, 0.0, | ||
| 2687 | -m12 / m10, -m02 / m01, | ||
| 2688 | (APPLY_SHEAR | APPLY_TRANSLATE)); | ||
| 2689 | case (APPLY_SHEAR): | ||
| 2690 | if (m01 == 0.0 || m10 == 0.0) { | ||
| 2691 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2692 | } | ||
| 2693 | return new AffineTransform(0.0, 1.0 / m01, | ||
| 2694 | 1.0 / m10, 0.0, | ||
| 2695 | 0.0, 0.0, | ||
| 2696 | (APPLY_SHEAR)); | ||
| 2697 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2698 | if (m00 == 0.0 || m11 == 0.0) { | ||
| 2699 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2700 | } | ||
| 2701 | return new AffineTransform( 1.0 / m00, 0.0, | ||
| 2702 | 0.0, 1.0 / m11, | ||
| 2703 | -m02 / m00, -m12 / m11, | ||
| 2704 | (APPLY_SCALE | APPLY_TRANSLATE)); | ||
| 2705 | case (APPLY_SCALE): | ||
| 2706 | if (m00 == 0.0 || m11 == 0.0) { | ||
| 2707 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2708 | } | ||
| 2709 | return new AffineTransform(1.0 / m00, 0.0, | ||
| 2710 | 0.0, 1.0 / m11, | ||
| 2711 | 0.0, 0.0, | ||
| 2712 | (APPLY_SCALE)); | ||
| 2713 | case (APPLY_TRANSLATE): | ||
| 2714 | return new AffineTransform( 1.0, 0.0, | ||
| 2715 | 0.0, 1.0, | ||
| 2716 | -m02, -m12, | ||
| 2717 | (APPLY_TRANSLATE)); | ||
| 2718 | case (APPLY_IDENTITY): | ||
| 2719 | return new AffineTransform(); | ||
| 2720 | } | ||
| 2721 | |||
| 2722 | /* NOTREACHED */ | ||
| 2723 | } | ||
| 2724 | |||
| 2725 | /** | ||
| 2726 | * Sets this transform to the inverse of itself. | ||
| 2727 | * The inverse transform Tx' of this transform Tx | ||
| 2728 | * maps coordinates transformed by Tx back | ||
| 2729 | * to their original coordinates. | ||
| 2730 | * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). | ||
| 2731 | * <p> | ||
| 2732 | * If this transform maps all coordinates onto a point or a line | ||
| 2733 | * then it will not have an inverse, since coordinates that do | ||
| 2734 | * not lie on the destination point or line will not have an inverse | ||
| 2735 | * mapping. | ||
| 2736 | * The <code>getDeterminant</code> method can be used to determine if this | ||
| 2737 | * transform has no inverse, in which case an exception will be | ||
| 2738 | * thrown if the <code>invert</code> method is called. | ||
| 2739 | * @see #getDeterminant | ||
| 2740 | * @exception NoninvertibleTransformException | ||
| 2741 | * if the matrix cannot be inverted. | ||
| 2742 | * @since 1.6 | ||
| 2743 | */ | ||
| 2744 | public void invert() | ||
| 2745 | throws NoninvertibleTransformException | ||
| 2746 | { | ||
| 2747 | double M00, M01, M02; | ||
| 2748 | double M10, M11, M12; | ||
| 2749 | double det; | ||
| 2750 | switch (state) { | ||
| 2751 | default: | ||
| 2752 | stateError(); | ||
| 2753 | /* NOTREACHED */ | ||
| 2754 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2755 | M00 = m00; M01 = m01; M02 = m02; | ||
| 2756 | M10 = m10; M11 = m11; M12 = m12; | ||
| 2757 | det = M00 * M11 - M01 * M10; | ||
| 2758 | if (Math.abs(det) <= Double.MIN_VALUE) { | ||
| 2759 | throw new NoninvertibleTransformException("Determinant is "+ | ||
| 2760 | det); | ||
| 2761 | } | ||
| 2762 | m00 = M11 / det; | ||
| 2763 | m10 = -M10 / det; | ||
| 2764 | m01 = -M01 / det; | ||
| 2765 | m11 = M00 / det; | ||
| 2766 | m02 = (M01 * M12 - M11 * M02) / det; | ||
| 2767 | m12 = (M10 * M02 - M00 * M12) / det; | ||
| 2768 | break; | ||
| 2769 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 2770 | M00 = m00; M01 = m01; | ||
| 2771 | M10 = m10; M11 = m11; | ||
| 2772 | det = M00 * M11 - M01 * M10; | ||
| 2773 | if (Math.abs(det) <= Double.MIN_VALUE) { | ||
| 2774 | throw new NoninvertibleTransformException("Determinant is "+ | ||
| 2775 | det); | ||
| 2776 | } | ||
| 2777 | m00 = M11 / det; | ||
| 2778 | m10 = -M10 / det; | ||
| 2779 | m01 = -M01 / det; | ||
| 2780 | m11 = M00 / det; | ||
| 2781 | // m02 = 0.0; | ||
| 2782 | // m12 = 0.0; | ||
| 2783 | break; | ||
| 2784 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2785 | M01 = m01; M02 = m02; | ||
| 2786 | M10 = m10; M12 = m12; | ||
| 2787 | if (M01 == 0.0 || M10 == 0.0) { | ||
| 2788 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2789 | } | ||
| 2790 | // m00 = 0.0; | ||
| 2791 | m10 = 1.0 / M01; | ||
| 2792 | m01 = 1.0 / M10; | ||
| 2793 | // m11 = 0.0; | ||
| 2794 | m02 = -M12 / M10; | ||
| 2795 | m12 = -M02 / M01; | ||
| 2796 | break; | ||
| 2797 | case (APPLY_SHEAR): | ||
| 2798 | M01 = m01; | ||
| 2799 | M10 = m10; | ||
| 2800 | if (M01 == 0.0 || M10 == 0.0) { | ||
| 2801 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2802 | } | ||
| 2803 | // m00 = 0.0; | ||
| 2804 | m10 = 1.0 / M01; | ||
| 2805 | m01 = 1.0 / M10; | ||
| 2806 | // m11 = 0.0; | ||
| 2807 | // m02 = 0.0; | ||
| 2808 | // m12 = 0.0; | ||
| 2809 | break; | ||
| 2810 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2811 | M00 = m00; M02 = m02; | ||
| 2812 | M11 = m11; M12 = m12; | ||
| 2813 | if (M00 == 0.0 || M11 == 0.0) { | ||
| 2814 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2815 | } | ||
| 2816 | m00 = 1.0 / M00; | ||
| 2817 | // m10 = 0.0; | ||
| 2818 | // m01 = 0.0; | ||
| 2819 | m11 = 1.0 / M11; | ||
| 2820 | m02 = -M02 / M00; | ||
| 2821 | m12 = -M12 / M11; | ||
| 2822 | break; | ||
| 2823 | case (APPLY_SCALE): | ||
| 2824 | M00 = m00; | ||
| 2825 | M11 = m11; | ||
| 2826 | if (M00 == 0.0 || M11 == 0.0) { | ||
| 2827 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 2828 | } | ||
| 2829 | m00 = 1.0 / M00; | ||
| 2830 | // m10 = 0.0; | ||
| 2831 | // m01 = 0.0; | ||
| 2832 | m11 = 1.0 / M11; | ||
| 2833 | // m02 = 0.0; | ||
| 2834 | // m12 = 0.0; | ||
| 2835 | break; | ||
| 2836 | case (APPLY_TRANSLATE): | ||
| 2837 | // m00 = 1.0; | ||
| 2838 | // m10 = 0.0; | ||
| 2839 | // m01 = 0.0; | ||
| 2840 | // m11 = 1.0; | ||
| 2841 | m02 = -m02; | ||
| 2842 | m12 = -m12; | ||
| 2843 | break; | ||
| 2844 | case (APPLY_IDENTITY): | ||
| 2845 | // m00 = 1.0; | ||
| 2846 | // m10 = 0.0; | ||
| 2847 | // m01 = 0.0; | ||
| 2848 | // m11 = 1.0; | ||
| 2849 | // m02 = 0.0; | ||
| 2850 | // m12 = 0.0; | ||
| 2851 | break; | ||
| 2852 | } | ||
| 2853 | } | ||
| 2854 | |||
| 2855 | /** | ||
| 2856 | * Transforms the specified <code>ptSrc</code> and stores the result | ||
| 2857 | * in <code>ptDst</code>. | ||
| 2858 | * If <code>ptDst</code> is <code>null</code>, a new {@link Point2D} | ||
| 2859 | * object is allocated and then the result of the transformation is | ||
| 2860 | * stored in this object. | ||
| 2861 | * In either case, <code>ptDst</code>, which contains the | ||
| 2862 | * transformed point, is returned for convenience. | ||
| 2863 | * If <code>ptSrc</code> and <code>ptDst</code> are the same | ||
| 2864 | * object, the input point is correctly overwritten with | ||
| 2865 | * the transformed point. | ||
| 2866 | * @param ptSrc the specified <code>Point2D</code> to be transformed | ||
| 2867 | * @param ptDst the specified <code>Point2D</code> that stores the | ||
| 2868 | * result of transforming <code>ptSrc</code> | ||
| 2869 | * @return the <code>ptDst</code> after transforming | ||
| 2870 | * <code>ptSrc</code> and stroring the result in <code>ptDst</code>. | ||
| 2871 | * @since 1.2 | ||
| 2872 | */ | ||
| 2873 | public Point2D transform(Point2D ptSrc, Point2D ptDst) { | ||
| 2874 | if (ptDst == null) { | ||
| 2875 | if (ptSrc instanceof Point2D.Double) { | ||
| 2876 | ptDst = new Point2D.Double(); | ||
| 2877 | } else { | ||
| 2878 | ptDst = new Point2D.Float(); | ||
| 2879 | } | ||
| 2880 | } | ||
| 2881 | // Copy source coords into local variables in case src == dst | ||
| 2882 | double x = ptSrc.getX(); | ||
| 2883 | double y = ptSrc.getY(); | ||
| 2884 | switch (state) { | ||
| 2885 | default: | ||
| 2886 | stateError(); | ||
| 2887 | /* NOTREACHED */ | ||
| 2888 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2889 | ptDst.setLocation(x * m00 + y * m01 + m02, | ||
| 2890 | x * m10 + y * m11 + m12); | ||
| 2891 | return ptDst; | ||
| 2892 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 2893 | ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); | ||
| 2894 | return ptDst; | ||
| 2895 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2896 | ptDst.setLocation(y * m01 + m02, x * m10 + m12); | ||
| 2897 | return ptDst; | ||
| 2898 | case (APPLY_SHEAR): | ||
| 2899 | ptDst.setLocation(y * m01, x * m10); | ||
| 2900 | return ptDst; | ||
| 2901 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2902 | ptDst.setLocation(x * m00 + m02, y * m11 + m12); | ||
| 2903 | return ptDst; | ||
| 2904 | case (APPLY_SCALE): | ||
| 2905 | ptDst.setLocation(x * m00, y * m11); | ||
| 2906 | return ptDst; | ||
| 2907 | case (APPLY_TRANSLATE): | ||
| 2908 | ptDst.setLocation(x + m02, y + m12); | ||
| 2909 | return ptDst; | ||
| 2910 | case (APPLY_IDENTITY): | ||
| 2911 | ptDst.setLocation(x, y); | ||
| 2912 | return ptDst; | ||
| 2913 | } | ||
| 2914 | |||
| 2915 | /* NOTREACHED */ | ||
| 2916 | } | ||
| 2917 | |||
| 2918 | /** | ||
| 2919 | * Transforms an array of point objects by this transform. | ||
| 2920 | * If any element of the <code>ptDst</code> array is | ||
| 2921 | * <code>null</code>, a new <code>Point2D</code> object is allocated | ||
| 2922 | * and stored into that element before storing the results of the | ||
| 2923 | * transformation. | ||
| 2924 | * <p> | ||
| 2925 | * Note that this method does not take any precautions to | ||
| 2926 | * avoid problems caused by storing results into <code>Point2D</code> | ||
| 2927 | * objects that will be used as the source for calculations | ||
| 2928 | * further down the source array. | ||
| 2929 | * This method does guarantee that if a specified <code>Point2D</code> | ||
| 2930 | * object is both the source and destination for the same single point | ||
| 2931 | * transform operation then the results will not be stored until | ||
| 2932 | * the calculations are complete to avoid storing the results on | ||
| 2933 | * top of the operands. | ||
| 2934 | * If, however, the destination <code>Point2D</code> object for one | ||
| 2935 | * operation is the same object as the source <code>Point2D</code> | ||
| 2936 | * object for another operation further down the source array then | ||
| 2937 | * the original coordinates in that point are overwritten before | ||
| 2938 | * they can be converted. | ||
| 2939 | * @param ptSrc the array containing the source point objects | ||
| 2940 | * @param ptDst the array into which the transform point objects are | ||
| 2941 | * returned | ||
| 2942 | * @param srcOff the offset to the first point object to be | ||
| 2943 | * transformed in the source array | ||
| 2944 | * @param dstOff the offset to the location of the first | ||
| 2945 | * transformed point object that is stored in the destination array | ||
| 2946 | * @param numPts the number of point objects to be transformed | ||
| 2947 | * @since 1.2 | ||
| 2948 | */ | ||
| 2949 | public void transform(Point2D[] ptSrc, int srcOff, | ||
| 2950 | Point2D[] ptDst, int dstOff, | ||
| 2951 | int numPts) { | ||
| 2952 | int state = this.state; | ||
| 2953 | while (--numPts >= 0) { | ||
| 2954 | // Copy source coords into local variables in case src == dst | ||
| 2955 | Point2D src = ptSrc[srcOff++]; | ||
| 2956 | double x = src.getX(); | ||
| 2957 | double y = src.getY(); | ||
| 2958 | Point2D dst = ptDst[dstOff++]; | ||
| 2959 | if (dst == null) { | ||
| 2960 | if (src instanceof Point2D.Double) { | ||
| 2961 | dst = new Point2D.Double(); | ||
| 2962 | } else { | ||
| 2963 | dst = new Point2D.Float(); | ||
| 2964 | } | ||
| 2965 | ptDst[dstOff - 1] = dst; | ||
| 2966 | } | ||
| 2967 | switch (state) { | ||
| 2968 | default: | ||
| 2969 | stateError(); | ||
| 2970 | /* NOTREACHED */ | ||
| 2971 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2972 | dst.setLocation(x * m00 + y * m01 + m02, | ||
| 2973 | x * m10 + y * m11 + m12); | ||
| 2974 | break; | ||
| 2975 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 2976 | dst.setLocation(x * m00 + y * m01, x * m10 + y * m11); | ||
| 2977 | break; | ||
| 2978 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 2979 | dst.setLocation(y * m01 + m02, x * m10 + m12); | ||
| 2980 | break; | ||
| 2981 | case (APPLY_SHEAR): | ||
| 2982 | dst.setLocation(y * m01, x * m10); | ||
| 2983 | break; | ||
| 2984 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 2985 | dst.setLocation(x * m00 + m02, y * m11 + m12); | ||
| 2986 | break; | ||
| 2987 | case (APPLY_SCALE): | ||
| 2988 | dst.setLocation(x * m00, y * m11); | ||
| 2989 | break; | ||
| 2990 | case (APPLY_TRANSLATE): | ||
| 2991 | dst.setLocation(x + m02, y + m12); | ||
| 2992 | break; | ||
| 2993 | case (APPLY_IDENTITY): | ||
| 2994 | dst.setLocation(x, y); | ||
| 2995 | break; | ||
| 2996 | } | ||
| 2997 | } | ||
| 2998 | |||
| 2999 | /* NOTREACHED */ | ||
| 3000 | } | ||
| 3001 | |||
| 3002 | /** | ||
| 3003 | * Transforms an array of floating point coordinates by this transform. | ||
| 3004 | * The two coordinate array sections can be exactly the same or | ||
| 3005 | * can be overlapping sections of the same array without affecting the | ||
| 3006 | * validity of the results. | ||
| 3007 | * This method ensures that no source coordinates are overwritten by a | ||
| 3008 | * previous operation before they can be transformed. | ||
| 3009 | * The coordinates are stored in the arrays starting at the specified | ||
| 3010 | * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. | ||
| 3011 | * @param srcPts the array containing the source point coordinates. | ||
| 3012 | * Each point is stored as a pair of x, y coordinates. | ||
| 3013 | * @param dstPts the array into which the transformed point coordinates | ||
| 3014 | * are returned. Each point is stored as a pair of x, y | ||
| 3015 | * coordinates. | ||
| 3016 | * @param srcOff the offset to the first point to be transformed | ||
| 3017 | * in the source array | ||
| 3018 | * @param dstOff the offset to the location of the first | ||
| 3019 | * transformed point that is stored in the destination array | ||
| 3020 | * @param numPts the number of points to be transformed | ||
| 3021 | * @since 1.2 | ||
| 3022 | */ | ||
| 3023 | public void transform(float[] srcPts, int srcOff, | ||
| 3024 | float[] dstPts, int dstOff, | ||
| 3025 | int numPts) { | ||
| 3026 | double M00, M01, M02, M10, M11, M12; // For caching | ||
| 3027 | if (dstPts == srcPts && | ||
| 3028 | dstOff > srcOff && dstOff < srcOff + numPts * 2) | ||
| 3029 | { | ||
| 3030 | // If the arrays overlap partially with the destination higher | ||
| 3031 | // than the source and we transform the coordinates normally | ||
| 3032 | // we would overwrite some of the later source coordinates | ||
| 3033 | // with results of previous transformations. | ||
| 3034 | // To get around this we use arraycopy to copy the points | ||
| 3035 | // to their final destination with correct overwrite | ||
| 3036 | // handling and then transform them in place in the new | ||
| 3037 | // safer location. | ||
| 3038 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); | ||
| 3039 | // srcPts = dstPts; // They are known to be equal. | ||
| 3040 | srcOff = dstOff; | ||
| 3041 | } | ||
| 3042 | switch (state) { | ||
| 3043 | default: | ||
| 3044 | stateError(); | ||
| 3045 | /* NOTREACHED */ | ||
| 3046 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3047 | M00 = m00; M01 = m01; M02 = m02; | ||
| 3048 | M10 = m10; M11 = m11; M12 = m12; | ||
| 3049 | while (--numPts >= 0) { | ||
| 3050 | double x = srcPts[srcOff++]; | ||
| 3051 | double y = srcPts[srcOff++]; | ||
| 3052 | dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); | ||
| 3053 | dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); | ||
| 3054 | } | ||
| 3055 | return; | ||
| 3056 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3057 | M00 = m00; M01 = m01; | ||
| 3058 | M10 = m10; M11 = m11; | ||
| 3059 | while (--numPts >= 0) { | ||
| 3060 | double x = srcPts[srcOff++]; | ||
| 3061 | double y = srcPts[srcOff++]; | ||
| 3062 | dstPts[dstOff++] = (float) (M00 * x + M01 * y); | ||
| 3063 | dstPts[dstOff++] = (float) (M10 * x + M11 * y); | ||
| 3064 | } | ||
| 3065 | return; | ||
| 3066 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3067 | M01 = m01; M02 = m02; | ||
| 3068 | M10 = m10; M12 = m12; | ||
| 3069 | while (--numPts >= 0) { | ||
| 3070 | double x = srcPts[srcOff++]; | ||
| 3071 | dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); | ||
| 3072 | dstPts[dstOff++] = (float) (M10 * x + M12); | ||
| 3073 | } | ||
| 3074 | return; | ||
| 3075 | case (APPLY_SHEAR): | ||
| 3076 | M01 = m01; M10 = m10; | ||
| 3077 | while (--numPts >= 0) { | ||
| 3078 | double x = srcPts[srcOff++]; | ||
| 3079 | dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); | ||
| 3080 | dstPts[dstOff++] = (float) (M10 * x); | ||
| 3081 | } | ||
| 3082 | return; | ||
| 3083 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3084 | M00 = m00; M02 = m02; | ||
| 3085 | M11 = m11; M12 = m12; | ||
| 3086 | while (--numPts >= 0) { | ||
| 3087 | dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); | ||
| 3088 | dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); | ||
| 3089 | } | ||
| 3090 | return; | ||
| 3091 | case (APPLY_SCALE): | ||
| 3092 | M00 = m00; M11 = m11; | ||
| 3093 | while (--numPts >= 0) { | ||
| 3094 | dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); | ||
| 3095 | dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); | ||
| 3096 | } | ||
| 3097 | return; | ||
| 3098 | case (APPLY_TRANSLATE): | ||
| 3099 | M02 = m02; M12 = m12; | ||
| 3100 | while (--numPts >= 0) { | ||
| 3101 | dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); | ||
| 3102 | dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); | ||
| 3103 | } | ||
| 3104 | return; | ||
| 3105 | case (APPLY_IDENTITY): | ||
| 3106 | if (srcPts != dstPts || srcOff != dstOff) { | ||
| 3107 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, | ||
| 3108 | numPts * 2); | ||
| 3109 | } | ||
| 3110 | return; | ||
| 3111 | } | ||
| 3112 | |||
| 3113 | /* NOTREACHED */ | ||
| 3114 | } | ||
| 3115 | |||
| 3116 | /** | ||
| 3117 | * Transforms an array of double precision coordinates by this transform. | ||
| 3118 | * The two coordinate array sections can be exactly the same or | ||
| 3119 | * can be overlapping sections of the same array without affecting the | ||
| 3120 | * validity of the results. | ||
| 3121 | * This method ensures that no source coordinates are | ||
| 3122 | * overwritten by a previous operation before they can be transformed. | ||
| 3123 | * The coordinates are stored in the arrays starting at the indicated | ||
| 3124 | * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. | ||
| 3125 | * @param srcPts the array containing the source point coordinates. | ||
| 3126 | * Each point is stored as a pair of x, y coordinates. | ||
| 3127 | * @param dstPts the array into which the transformed point | ||
| 3128 | * coordinates are returned. Each point is stored as a pair of | ||
| 3129 | * x, y coordinates. | ||
| 3130 | * @param srcOff the offset to the first point to be transformed | ||
| 3131 | * in the source array | ||
| 3132 | * @param dstOff the offset to the location of the first | ||
| 3133 | * transformed point that is stored in the destination array | ||
| 3134 | * @param numPts the number of point objects to be transformed | ||
| 3135 | * @since 1.2 | ||
| 3136 | */ | ||
| 3137 | public void transform(double[] srcPts, int srcOff, | ||
| 3138 | double[] dstPts, int dstOff, | ||
| 3139 | int numPts) { | ||
| 3140 | double M00, M01, M02, M10, M11, M12; // For caching | ||
| 3141 | if (dstPts == srcPts && | ||
| 3142 | dstOff > srcOff && dstOff < srcOff + numPts * 2) | ||
| 3143 | { | ||
| 3144 | // If the arrays overlap partially with the destination higher | ||
| 3145 | // than the source and we transform the coordinates normally | ||
| 3146 | // we would overwrite some of the later source coordinates | ||
| 3147 | // with results of previous transformations. | ||
| 3148 | // To get around this we use arraycopy to copy the points | ||
| 3149 | // to their final destination with correct overwrite | ||
| 3150 | // handling and then transform them in place in the new | ||
| 3151 | // safer location. | ||
| 3152 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); | ||
| 3153 | // srcPts = dstPts; // They are known to be equal. | ||
| 3154 | srcOff = dstOff; | ||
| 3155 | } | ||
| 3156 | switch (state) { | ||
| 3157 | default: | ||
| 3158 | stateError(); | ||
| 3159 | /* NOTREACHED */ | ||
| 3160 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3161 | M00 = m00; M01 = m01; M02 = m02; | ||
| 3162 | M10 = m10; M11 = m11; M12 = m12; | ||
| 3163 | while (--numPts >= 0) { | ||
| 3164 | double x = srcPts[srcOff++]; | ||
| 3165 | double y = srcPts[srcOff++]; | ||
| 3166 | dstPts[dstOff++] = M00 * x + M01 * y + M02; | ||
| 3167 | dstPts[dstOff++] = M10 * x + M11 * y + M12; | ||
| 3168 | } | ||
| 3169 | return; | ||
| 3170 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3171 | M00 = m00; M01 = m01; | ||
| 3172 | M10 = m10; M11 = m11; | ||
| 3173 | while (--numPts >= 0) { | ||
| 3174 | double x = srcPts[srcOff++]; | ||
| 3175 | double y = srcPts[srcOff++]; | ||
| 3176 | dstPts[dstOff++] = M00 * x + M01 * y; | ||
| 3177 | dstPts[dstOff++] = M10 * x + M11 * y; | ||
| 3178 | } | ||
| 3179 | return; | ||
| 3180 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3181 | M01 = m01; M02 = m02; | ||
| 3182 | M10 = m10; M12 = m12; | ||
| 3183 | while (--numPts >= 0) { | ||
| 3184 | double x = srcPts[srcOff++]; | ||
| 3185 | dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; | ||
| 3186 | dstPts[dstOff++] = M10 * x + M12; | ||
| 3187 | } | ||
| 3188 | return; | ||
| 3189 | case (APPLY_SHEAR): | ||
| 3190 | M01 = m01; M10 = m10; | ||
| 3191 | while (--numPts >= 0) { | ||
| 3192 | double x = srcPts[srcOff++]; | ||
| 3193 | dstPts[dstOff++] = M01 * srcPts[srcOff++]; | ||
| 3194 | dstPts[dstOff++] = M10 * x; | ||
| 3195 | } | ||
| 3196 | return; | ||
| 3197 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3198 | M00 = m00; M02 = m02; | ||
| 3199 | M11 = m11; M12 = m12; | ||
| 3200 | while (--numPts >= 0) { | ||
| 3201 | dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; | ||
| 3202 | dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; | ||
| 3203 | } | ||
| 3204 | return; | ||
| 3205 | case (APPLY_SCALE): | ||
| 3206 | M00 = m00; M11 = m11; | ||
| 3207 | while (--numPts >= 0) { | ||
| 3208 | dstPts[dstOff++] = M00 * srcPts[srcOff++]; | ||
| 3209 | dstPts[dstOff++] = M11 * srcPts[srcOff++]; | ||
| 3210 | } | ||
| 3211 | return; | ||
| 3212 | case (APPLY_TRANSLATE): | ||
| 3213 | M02 = m02; M12 = m12; | ||
| 3214 | while (--numPts >= 0) { | ||
| 3215 | dstPts[dstOff++] = srcPts[srcOff++] + M02; | ||
| 3216 | dstPts[dstOff++] = srcPts[srcOff++] + M12; | ||
| 3217 | } | ||
| 3218 | return; | ||
| 3219 | case (APPLY_IDENTITY): | ||
| 3220 | if (srcPts != dstPts || srcOff != dstOff) { | ||
| 3221 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, | ||
| 3222 | numPts * 2); | ||
| 3223 | } | ||
| 3224 | return; | ||
| 3225 | } | ||
| 3226 | |||
| 3227 | /* NOTREACHED */ | ||
| 3228 | } | ||
| 3229 | |||
| 3230 | /** | ||
| 3231 | * Transforms an array of floating point coordinates by this transform | ||
| 3232 | * and stores the results into an array of doubles. | ||
| 3233 | * The coordinates are stored in the arrays starting at the specified | ||
| 3234 | * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. | ||
| 3235 | * @param srcPts the array containing the source point coordinates. | ||
| 3236 | * Each point is stored as a pair of x, y coordinates. | ||
| 3237 | * @param dstPts the array into which the transformed point coordinates | ||
| 3238 | * are returned. Each point is stored as a pair of x, y | ||
| 3239 | * coordinates. | ||
| 3240 | * @param srcOff the offset to the first point to be transformed | ||
| 3241 | * in the source array | ||
| 3242 | * @param dstOff the offset to the location of the first | ||
| 3243 | * transformed point that is stored in the destination array | ||
| 3244 | * @param numPts the number of points to be transformed | ||
| 3245 | * @since 1.2 | ||
| 3246 | */ | ||
| 3247 | public void transform(float[] srcPts, int srcOff, | ||
| 3248 | double[] dstPts, int dstOff, | ||
| 3249 | int numPts) { | ||
| 3250 | double M00, M01, M02, M10, M11, M12; // For caching | ||
| 3251 | switch (state) { | ||
| 3252 | default: | ||
| 3253 | stateError(); | ||
| 3254 | /* NOTREACHED */ | ||
| 3255 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3256 | M00 = m00; M01 = m01; M02 = m02; | ||
| 3257 | M10 = m10; M11 = m11; M12 = m12; | ||
| 3258 | while (--numPts >= 0) { | ||
| 3259 | double x = srcPts[srcOff++]; | ||
| 3260 | double y = srcPts[srcOff++]; | ||
| 3261 | dstPts[dstOff++] = M00 * x + M01 * y + M02; | ||
| 3262 | dstPts[dstOff++] = M10 * x + M11 * y + M12; | ||
| 3263 | } | ||
| 3264 | return; | ||
| 3265 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3266 | M00 = m00; M01 = m01; | ||
| 3267 | M10 = m10; M11 = m11; | ||
| 3268 | while (--numPts >= 0) { | ||
| 3269 | double x = srcPts[srcOff++]; | ||
| 3270 | double y = srcPts[srcOff++]; | ||
| 3271 | dstPts[dstOff++] = M00 * x + M01 * y; | ||
| 3272 | dstPts[dstOff++] = M10 * x + M11 * y; | ||
| 3273 | } | ||
| 3274 | return; | ||
| 3275 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3276 | M01 = m01; M02 = m02; | ||
| 3277 | M10 = m10; M12 = m12; | ||
| 3278 | while (--numPts >= 0) { | ||
| 3279 | double x = srcPts[srcOff++]; | ||
| 3280 | dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; | ||
| 3281 | dstPts[dstOff++] = M10 * x + M12; | ||
| 3282 | } | ||
| 3283 | return; | ||
| 3284 | case (APPLY_SHEAR): | ||
| 3285 | M01 = m01; M10 = m10; | ||
| 3286 | while (--numPts >= 0) { | ||
| 3287 | double x = srcPts[srcOff++]; | ||
| 3288 | dstPts[dstOff++] = M01 * srcPts[srcOff++]; | ||
| 3289 | dstPts[dstOff++] = M10 * x; | ||
| 3290 | } | ||
| 3291 | return; | ||
| 3292 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3293 | M00 = m00; M02 = m02; | ||
| 3294 | M11 = m11; M12 = m12; | ||
| 3295 | while (--numPts >= 0) { | ||
| 3296 | dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; | ||
| 3297 | dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; | ||
| 3298 | } | ||
| 3299 | return; | ||
| 3300 | case (APPLY_SCALE): | ||
| 3301 | M00 = m00; M11 = m11; | ||
| 3302 | while (--numPts >= 0) { | ||
| 3303 | dstPts[dstOff++] = M00 * srcPts[srcOff++]; | ||
| 3304 | dstPts[dstOff++] = M11 * srcPts[srcOff++]; | ||
| 3305 | } | ||
| 3306 | return; | ||
| 3307 | case (APPLY_TRANSLATE): | ||
| 3308 | M02 = m02; M12 = m12; | ||
| 3309 | while (--numPts >= 0) { | ||
| 3310 | dstPts[dstOff++] = srcPts[srcOff++] + M02; | ||
| 3311 | dstPts[dstOff++] = srcPts[srcOff++] + M12; | ||
| 3312 | } | ||
| 3313 | return; | ||
| 3314 | case (APPLY_IDENTITY): | ||
| 3315 | while (--numPts >= 0) { | ||
| 3316 | dstPts[dstOff++] = srcPts[srcOff++]; | ||
| 3317 | dstPts[dstOff++] = srcPts[srcOff++]; | ||
| 3318 | } | ||
| 3319 | return; | ||
| 3320 | } | ||
| 3321 | |||
| 3322 | /* NOTREACHED */ | ||
| 3323 | } | ||
| 3324 | |||
| 3325 | /** | ||
| 3326 | * Transforms an array of double precision coordinates by this transform | ||
| 3327 | * and stores the results into an array of floats. | ||
| 3328 | * The coordinates are stored in the arrays starting at the specified | ||
| 3329 | * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. | ||
| 3330 | * @param srcPts the array containing the source point coordinates. | ||
| 3331 | * Each point is stored as a pair of x, y coordinates. | ||
| 3332 | * @param dstPts the array into which the transformed point | ||
| 3333 | * coordinates are returned. Each point is stored as a pair of | ||
| 3334 | * x, y coordinates. | ||
| 3335 | * @param srcOff the offset to the first point to be transformed | ||
| 3336 | * in the source array | ||
| 3337 | * @param dstOff the offset to the location of the first | ||
| 3338 | * transformed point that is stored in the destination array | ||
| 3339 | * @param numPts the number of point objects to be transformed | ||
| 3340 | * @since 1.2 | ||
| 3341 | */ | ||
| 3342 | public void transform(double[] srcPts, int srcOff, | ||
| 3343 | float[] dstPts, int dstOff, | ||
| 3344 | int numPts) { | ||
| 3345 | double M00, M01, M02, M10, M11, M12; // For caching | ||
| 3346 | switch (state) { | ||
| 3347 | default: | ||
| 3348 | stateError(); | ||
| 3349 | /* NOTREACHED */ | ||
| 3350 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3351 | M00 = m00; M01 = m01; M02 = m02; | ||
| 3352 | M10 = m10; M11 = m11; M12 = m12; | ||
| 3353 | while (--numPts >= 0) { | ||
| 3354 | double x = srcPts[srcOff++]; | ||
| 3355 | double y = srcPts[srcOff++]; | ||
| 3356 | dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); | ||
| 3357 | dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); | ||
| 3358 | } | ||
| 3359 | return; | ||
| 3360 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3361 | M00 = m00; M01 = m01; | ||
| 3362 | M10 = m10; M11 = m11; | ||
| 3363 | while (--numPts >= 0) { | ||
| 3364 | double x = srcPts[srcOff++]; | ||
| 3365 | double y = srcPts[srcOff++]; | ||
| 3366 | dstPts[dstOff++] = (float) (M00 * x + M01 * y); | ||
| 3367 | dstPts[dstOff++] = (float) (M10 * x + M11 * y); | ||
| 3368 | } | ||
| 3369 | return; | ||
| 3370 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3371 | M01 = m01; M02 = m02; | ||
| 3372 | M10 = m10; M12 = m12; | ||
| 3373 | while (--numPts >= 0) { | ||
| 3374 | double x = srcPts[srcOff++]; | ||
| 3375 | dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); | ||
| 3376 | dstPts[dstOff++] = (float) (M10 * x + M12); | ||
| 3377 | } | ||
| 3378 | return; | ||
| 3379 | case (APPLY_SHEAR): | ||
| 3380 | M01 = m01; M10 = m10; | ||
| 3381 | while (--numPts >= 0) { | ||
| 3382 | double x = srcPts[srcOff++]; | ||
| 3383 | dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); | ||
| 3384 | dstPts[dstOff++] = (float) (M10 * x); | ||
| 3385 | } | ||
| 3386 | return; | ||
| 3387 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3388 | M00 = m00; M02 = m02; | ||
| 3389 | M11 = m11; M12 = m12; | ||
| 3390 | while (--numPts >= 0) { | ||
| 3391 | dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); | ||
| 3392 | dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); | ||
| 3393 | } | ||
| 3394 | return; | ||
| 3395 | case (APPLY_SCALE): | ||
| 3396 | M00 = m00; M11 = m11; | ||
| 3397 | while (--numPts >= 0) { | ||
| 3398 | dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); | ||
| 3399 | dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); | ||
| 3400 | } | ||
| 3401 | return; | ||
| 3402 | case (APPLY_TRANSLATE): | ||
| 3403 | M02 = m02; M12 = m12; | ||
| 3404 | while (--numPts >= 0) { | ||
| 3405 | dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); | ||
| 3406 | dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); | ||
| 3407 | } | ||
| 3408 | return; | ||
| 3409 | case (APPLY_IDENTITY): | ||
| 3410 | while (--numPts >= 0) { | ||
| 3411 | dstPts[dstOff++] = (float) (srcPts[srcOff++]); | ||
| 3412 | dstPts[dstOff++] = (float) (srcPts[srcOff++]); | ||
| 3413 | } | ||
| 3414 | return; | ||
| 3415 | } | ||
| 3416 | |||
| 3417 | /* NOTREACHED */ | ||
| 3418 | } | ||
| 3419 | |||
| 3420 | /** | ||
| 3421 | * Inverse transforms the specified <code>ptSrc</code> and stores the | ||
| 3422 | * result in <code>ptDst</code>. | ||
| 3423 | * If <code>ptDst</code> is <code>null</code>, a new | ||
| 3424 | * <code>Point2D</code> object is allocated and then the result of the | ||
| 3425 | * transform is stored in this object. | ||
| 3426 | * In either case, <code>ptDst</code>, which contains the transformed | ||
| 3427 | * point, is returned for convenience. | ||
| 3428 | * If <code>ptSrc</code> and <code>ptDst</code> are the same | ||
| 3429 | * object, the input point is correctly overwritten with the | ||
| 3430 | * transformed point. | ||
| 3431 | * @param ptSrc the point to be inverse transformed | ||
| 3432 | * @param ptDst the resulting transformed point | ||
| 3433 | * @return <code>ptDst</code>, which contains the result of the | ||
| 3434 | * inverse transform. | ||
| 3435 | * @exception NoninvertibleTransformException if the matrix cannot be | ||
| 3436 | * inverted. | ||
| 3437 | * @since 1.2 | ||
| 3438 | */ | ||
| 3439 | public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst) | ||
| 3440 | throws NoninvertibleTransformException | ||
| 3441 | { | ||
| 3442 | if (ptDst == null) { | ||
| 3443 | if (ptSrc instanceof Point2D.Double) { | ||
| 3444 | ptDst = new Point2D.Double(); | ||
| 3445 | } else { | ||
| 3446 | ptDst = new Point2D.Float(); | ||
| 3447 | } | ||
| 3448 | } | ||
| 3449 | // Copy source coords into local variables in case src == dst | ||
| 3450 | double x = ptSrc.getX(); | ||
| 3451 | double y = ptSrc.getY(); | ||
| 3452 | switch (state) { | ||
| 3453 | default: | ||
| 3454 | stateError(); | ||
| 3455 | /* NOTREACHED */ | ||
| 3456 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3457 | x -= m02; | ||
| 3458 | y -= m12; | ||
| 3459 | /* NOBREAK */ | ||
| 3460 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3461 | double det = m00 * m11 - m01 * m10; | ||
| 3462 | if (Math.abs(det) <= Double.MIN_VALUE) { | ||
| 3463 | throw new NoninvertibleTransformException("Determinant is "+ | ||
| 3464 | det); | ||
| 3465 | } | ||
| 3466 | ptDst.setLocation((x * m11 - y * m01) / det, | ||
| 3467 | (y * m00 - x * m10) / det); | ||
| 3468 | return ptDst; | ||
| 3469 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3470 | x -= m02; | ||
| 3471 | y -= m12; | ||
| 3472 | /* NOBREAK */ | ||
| 3473 | case (APPLY_SHEAR): | ||
| 3474 | if (m01 == 0.0 || m10 == 0.0) { | ||
| 3475 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 3476 | } | ||
| 3477 | ptDst.setLocation(y / m10, x / m01); | ||
| 3478 | return ptDst; | ||
| 3479 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3480 | x -= m02; | ||
| 3481 | y -= m12; | ||
| 3482 | /* NOBREAK */ | ||
| 3483 | case (APPLY_SCALE): | ||
| 3484 | if (m00 == 0.0 || m11 == 0.0) { | ||
| 3485 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 3486 | } | ||
| 3487 | ptDst.setLocation(x / m00, y / m11); | ||
| 3488 | return ptDst; | ||
| 3489 | case (APPLY_TRANSLATE): | ||
| 3490 | ptDst.setLocation(x - m02, y - m12); | ||
| 3491 | return ptDst; | ||
| 3492 | case (APPLY_IDENTITY): | ||
| 3493 | ptDst.setLocation(x, y); | ||
| 3494 | return ptDst; | ||
| 3495 | } | ||
| 3496 | |||
| 3497 | /* NOTREACHED */ | ||
| 3498 | } | ||
| 3499 | |||
| 3500 | /** | ||
| 3501 | * Inverse transforms an array of double precision coordinates by | ||
| 3502 | * this transform. | ||
| 3503 | * The two coordinate array sections can be exactly the same or | ||
| 3504 | * can be overlapping sections of the same array without affecting the | ||
| 3505 | * validity of the results. | ||
| 3506 | * This method ensures that no source coordinates are | ||
| 3507 | * overwritten by a previous operation before they can be transformed. | ||
| 3508 | * The coordinates are stored in the arrays starting at the specified | ||
| 3509 | * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. | ||
| 3510 | * @param srcPts the array containing the source point coordinates. | ||
| 3511 | * Each point is stored as a pair of x, y coordinates. | ||
| 3512 | * @param dstPts the array into which the transformed point | ||
| 3513 | * coordinates are returned. Each point is stored as a pair of | ||
| 3514 | * x, y coordinates. | ||
| 3515 | * @param srcOff the offset to the first point to be transformed | ||
| 3516 | * in the source array | ||
| 3517 | * @param dstOff the offset to the location of the first | ||
| 3518 | * transformed point that is stored in the destination array | ||
| 3519 | * @param numPts the number of point objects to be transformed | ||
| 3520 | * @exception NoninvertibleTransformException if the matrix cannot be | ||
| 3521 | * inverted. | ||
| 3522 | * @since 1.2 | ||
| 3523 | */ | ||
| 3524 | public void inverseTransform(double[] srcPts, int srcOff, | ||
| 3525 | double[] dstPts, int dstOff, | ||
| 3526 | int numPts) | ||
| 3527 | throws NoninvertibleTransformException | ||
| 3528 | { | ||
| 3529 | double M00, M01, M02, M10, M11, M12; // For caching | ||
| 3530 | double det; | ||
| 3531 | if (dstPts == srcPts && | ||
| 3532 | dstOff > srcOff && dstOff < srcOff + numPts * 2) | ||
| 3533 | { | ||
| 3534 | // If the arrays overlap partially with the destination higher | ||
| 3535 | // than the source and we transform the coordinates normally | ||
| 3536 | // we would overwrite some of the later source coordinates | ||
| 3537 | // with results of previous transformations. | ||
| 3538 | // To get around this we use arraycopy to copy the points | ||
| 3539 | // to their final destination with correct overwrite | ||
| 3540 | // handling and then transform them in place in the new | ||
| 3541 | // safer location. | ||
| 3542 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); | ||
| 3543 | // srcPts = dstPts; // They are known to be equal. | ||
| 3544 | srcOff = dstOff; | ||
| 3545 | } | ||
| 3546 | switch (state) { | ||
| 3547 | default: | ||
| 3548 | stateError(); | ||
| 3549 | /* NOTREACHED */ | ||
| 3550 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3551 | M00 = m00; M01 = m01; M02 = m02; | ||
| 3552 | M10 = m10; M11 = m11; M12 = m12; | ||
| 3553 | det = M00 * M11 - M01 * M10; | ||
| 3554 | if (Math.abs(det) <= Double.MIN_VALUE) { | ||
| 3555 | throw new NoninvertibleTransformException("Determinant is "+ | ||
| 3556 | det); | ||
| 3557 | } | ||
| 3558 | while (--numPts >= 0) { | ||
| 3559 | double x = srcPts[srcOff++] - M02; | ||
| 3560 | double y = srcPts[srcOff++] - M12; | ||
| 3561 | dstPts[dstOff++] = (x * M11 - y * M01) / det; | ||
| 3562 | dstPts[dstOff++] = (y * M00 - x * M10) / det; | ||
| 3563 | } | ||
| 3564 | return; | ||
| 3565 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3566 | M00 = m00; M01 = m01; | ||
| 3567 | M10 = m10; M11 = m11; | ||
| 3568 | det = M00 * M11 - M01 * M10; | ||
| 3569 | if (Math.abs(det) <= Double.MIN_VALUE) { | ||
| 3570 | throw new NoninvertibleTransformException("Determinant is "+ | ||
| 3571 | det); | ||
| 3572 | } | ||
| 3573 | while (--numPts >= 0) { | ||
| 3574 | double x = srcPts[srcOff++]; | ||
| 3575 | double y = srcPts[srcOff++]; | ||
| 3576 | dstPts[dstOff++] = (x * M11 - y * M01) / det; | ||
| 3577 | dstPts[dstOff++] = (y * M00 - x * M10) / det; | ||
| 3578 | } | ||
| 3579 | return; | ||
| 3580 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3581 | M01 = m01; M02 = m02; | ||
| 3582 | M10 = m10; M12 = m12; | ||
| 3583 | if (M01 == 0.0 || M10 == 0.0) { | ||
| 3584 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 3585 | } | ||
| 3586 | while (--numPts >= 0) { | ||
| 3587 | double x = srcPts[srcOff++] - M02; | ||
| 3588 | dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10; | ||
| 3589 | dstPts[dstOff++] = x / M01; | ||
| 3590 | } | ||
| 3591 | return; | ||
| 3592 | case (APPLY_SHEAR): | ||
| 3593 | M01 = m01; M10 = m10; | ||
| 3594 | if (M01 == 0.0 || M10 == 0.0) { | ||
| 3595 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 3596 | } | ||
| 3597 | while (--numPts >= 0) { | ||
| 3598 | double x = srcPts[srcOff++]; | ||
| 3599 | dstPts[dstOff++] = srcPts[srcOff++] / M10; | ||
| 3600 | dstPts[dstOff++] = x / M01; | ||
| 3601 | } | ||
| 3602 | return; | ||
| 3603 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3604 | M00 = m00; M02 = m02; | ||
| 3605 | M11 = m11; M12 = m12; | ||
| 3606 | if (M00 == 0.0 || M11 == 0.0) { | ||
| 3607 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 3608 | } | ||
| 3609 | while (--numPts >= 0) { | ||
| 3610 | dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00; | ||
| 3611 | dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11; | ||
| 3612 | } | ||
| 3613 | return; | ||
| 3614 | case (APPLY_SCALE): | ||
| 3615 | M00 = m00; M11 = m11; | ||
| 3616 | if (M00 == 0.0 || M11 == 0.0) { | ||
| 3617 | throw new NoninvertibleTransformException("Determinant is 0"); | ||
| 3618 | } | ||
| 3619 | while (--numPts >= 0) { | ||
| 3620 | dstPts[dstOff++] = srcPts[srcOff++] / M00; | ||
| 3621 | dstPts[dstOff++] = srcPts[srcOff++] / M11; | ||
| 3622 | } | ||
| 3623 | return; | ||
| 3624 | case (APPLY_TRANSLATE): | ||
| 3625 | M02 = m02; M12 = m12; | ||
| 3626 | while (--numPts >= 0) { | ||
| 3627 | dstPts[dstOff++] = srcPts[srcOff++] - M02; | ||
| 3628 | dstPts[dstOff++] = srcPts[srcOff++] - M12; | ||
| 3629 | } | ||
| 3630 | return; | ||
| 3631 | case (APPLY_IDENTITY): | ||
| 3632 | if (srcPts != dstPts || srcOff != dstOff) { | ||
| 3633 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, | ||
| 3634 | numPts * 2); | ||
| 3635 | } | ||
| 3636 | return; | ||
| 3637 | } | ||
| 3638 | |||
| 3639 | /* NOTREACHED */ | ||
| 3640 | } | ||
| 3641 | |||
| 3642 | /** | ||
| 3643 | * Transforms the relative distance vector specified by | ||
| 3644 | * <code>ptSrc</code> and stores the result in <code>ptDst</code>. | ||
| 3645 | * A relative distance vector is transformed without applying the | ||
| 3646 | * translation components of the affine transformation matrix | ||
| 3647 | * using the following equations: | ||
| 3648 | * <pre> | ||
| 3649 | * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] | ||
| 3650 | * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] | ||
| 3651 | * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] | ||
| 3652 | * </pre> | ||
| 3653 | * If <code>ptDst</code> is <code>null</code>, a new | ||
| 3654 | * <code>Point2D</code> object is allocated and then the result of the | ||
| 3655 | * transform is stored in this object. | ||
| 3656 | * In either case, <code>ptDst</code>, which contains the | ||
| 3657 | * transformed point, is returned for convenience. | ||
| 3658 | * If <code>ptSrc</code> and <code>ptDst</code> are the same object, | ||
| 3659 | * the input point is correctly overwritten with the transformed | ||
| 3660 | * point. | ||
| 3661 | * @param ptSrc the distance vector to be delta transformed | ||
| 3662 | * @param ptDst the resulting transformed distance vector | ||
| 3663 | * @return <code>ptDst</code>, which contains the result of the | ||
| 3664 | * transformation. | ||
| 3665 | * @since 1.2 | ||
| 3666 | */ | ||
| 3667 | public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) { | ||
| 3668 | if (ptDst == null) { | ||
| 3669 | if (ptSrc instanceof Point2D.Double) { | ||
| 3670 | ptDst = new Point2D.Double(); | ||
| 3671 | } else { | ||
| 3672 | ptDst = new Point2D.Float(); | ||
| 3673 | } | ||
| 3674 | } | ||
| 3675 | // Copy source coords into local variables in case src == dst | ||
| 3676 | double x = ptSrc.getX(); | ||
| 3677 | double y = ptSrc.getY(); | ||
| 3678 | switch (state) { | ||
| 3679 | default: | ||
| 3680 | stateError(); | ||
| 3681 | /* NOTREACHED */ | ||
| 3682 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3683 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3684 | ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); | ||
| 3685 | return ptDst; | ||
| 3686 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3687 | case (APPLY_SHEAR): | ||
| 3688 | ptDst.setLocation(y * m01, x * m10); | ||
| 3689 | return ptDst; | ||
| 3690 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3691 | case (APPLY_SCALE): | ||
| 3692 | ptDst.setLocation(x * m00, y * m11); | ||
| 3693 | return ptDst; | ||
| 3694 | case (APPLY_TRANSLATE): | ||
| 3695 | case (APPLY_IDENTITY): | ||
| 3696 | ptDst.setLocation(x, y); | ||
| 3697 | return ptDst; | ||
| 3698 | } | ||
| 3699 | |||
| 3700 | /* NOTREACHED */ | ||
| 3701 | } | ||
| 3702 | |||
| 3703 | /** | ||
| 3704 | * Transforms an array of relative distance vectors by this | ||
| 3705 | * transform. | ||
| 3706 | * A relative distance vector is transformed without applying the | ||
| 3707 | * translation components of the affine transformation matrix | ||
| 3708 | * using the following equations: | ||
| 3709 | * <pre> | ||
| 3710 | * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] | ||
| 3711 | * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] | ||
| 3712 | * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] | ||
| 3713 | * </pre> | ||
| 3714 | * The two coordinate array sections can be exactly the same or | ||
| 3715 | * can be overlapping sections of the same array without affecting the | ||
| 3716 | * validity of the results. | ||
| 3717 | * This method ensures that no source coordinates are | ||
| 3718 | * overwritten by a previous operation before they can be transformed. | ||
| 3719 | * The coordinates are stored in the arrays starting at the indicated | ||
| 3720 | * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. | ||
| 3721 | * @param srcPts the array containing the source distance vectors. | ||
| 3722 | * Each vector is stored as a pair of relative x, y coordinates. | ||
| 3723 | * @param dstPts the array into which the transformed distance vectors | ||
| 3724 | * are returned. Each vector is stored as a pair of relative | ||
| 3725 | * x, y coordinates. | ||
| 3726 | * @param srcOff the offset to the first vector to be transformed | ||
| 3727 | * in the source array | ||
| 3728 | * @param dstOff the offset to the location of the first | ||
| 3729 | * transformed vector that is stored in the destination array | ||
| 3730 | * @param numPts the number of vector coordinate pairs to be | ||
| 3731 | * transformed | ||
| 3732 | * @since 1.2 | ||
| 3733 | */ | ||
| 3734 | public void deltaTransform(double[] srcPts, int srcOff, | ||
| 3735 | double[] dstPts, int dstOff, | ||
| 3736 | int numPts) { | ||
| 3737 | double M00, M01, M10, M11; // For caching | ||
| 3738 | if (dstPts == srcPts && | ||
| 3739 | dstOff > srcOff && dstOff < srcOff + numPts * 2) | ||
| 3740 | { | ||
| 3741 | // If the arrays overlap partially with the destination higher | ||
| 3742 | // than the source and we transform the coordinates normally | ||
| 3743 | // we would overwrite some of the later source coordinates | ||
| 3744 | // with results of previous transformations. | ||
| 3745 | // To get around this we use arraycopy to copy the points | ||
| 3746 | // to their final destination with correct overwrite | ||
| 3747 | // handling and then transform them in place in the new | ||
| 3748 | // safer location. | ||
| 3749 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); | ||
| 3750 | // srcPts = dstPts; // They are known to be equal. | ||
| 3751 | srcOff = dstOff; | ||
| 3752 | } | ||
| 3753 | switch (state) { | ||
| 3754 | default: | ||
| 3755 | stateError(); | ||
| 3756 | /* NOTREACHED */ | ||
| 3757 | case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3758 | case (APPLY_SHEAR | APPLY_SCALE): | ||
| 3759 | M00 = m00; M01 = m01; | ||
| 3760 | M10 = m10; M11 = m11; | ||
| 3761 | while (--numPts >= 0) { | ||
| 3762 | double x = srcPts[srcOff++]; | ||
| 3763 | double y = srcPts[srcOff++]; | ||
| 3764 | dstPts[dstOff++] = x * M00 + y * M01; | ||
| 3765 | dstPts[dstOff++] = x * M10 + y * M11; | ||
| 3766 | } | ||
| 3767 | return; | ||
| 3768 | case (APPLY_SHEAR | APPLY_TRANSLATE): | ||
| 3769 | case (APPLY_SHEAR): | ||
| 3770 | M01 = m01; M10 = m10; | ||
| 3771 | while (--numPts >= 0) { | ||
| 3772 | double x = srcPts[srcOff++]; | ||
| 3773 | dstPts[dstOff++] = srcPts[srcOff++] * M01; | ||
| 3774 | dstPts[dstOff++] = x * M10; | ||
| 3775 | } | ||
| 3776 | return; | ||
| 3777 | case (APPLY_SCALE | APPLY_TRANSLATE): | ||
| 3778 | case (APPLY_SCALE): | ||
| 3779 | M00 = m00; M11 = m11; | ||
| 3780 | while (--numPts >= 0) { | ||
| 3781 | dstPts[dstOff++] = srcPts[srcOff++] * M00; | ||
| 3782 | dstPts[dstOff++] = srcPts[srcOff++] * M11; | ||
| 3783 | } | ||
| 3784 | return; | ||
| 3785 | case (APPLY_TRANSLATE): | ||
| 3786 | case (APPLY_IDENTITY): | ||
| 3787 | if (srcPts != dstPts || srcOff != dstOff) { | ||
| 3788 | System.arraycopy(srcPts, srcOff, dstPts, dstOff, | ||
| 3789 | numPts * 2); | ||
| 3790 | } | ||
| 3791 | return; | ||
| 3792 | } | ||
| 3793 | |||
| 3794 | /* NOTREACHED */ | ||
| 3795 | } | ||
| 3796 | |||
| 3797 | /** | ||
| 3798 | * Returns a new {@link Shape} object defined by the geometry of the | ||
| 3799 | * specified <code>Shape</code> after it has been transformed by | ||
| 3800 | * this transform. | ||
| 3801 | * @param pSrc the specified <code>Shape</code> object to be | ||
| 3802 | * transformed by this transform. | ||
| 3803 | * @return a new <code>Shape</code> object that defines the geometry | ||
| 3804 | * of the transformed <code>Shape</code>, or null if {@code pSrc} is null. | ||
| 3805 | * @since 1.2 | ||
| 3806 | */ | ||
| 3807 | public Shape createTransformedShape(Shape pSrc) { | ||
| 3808 | if (pSrc == null) { | ||
| 3809 | return null; | ||
| 3810 | } | ||
| 3811 | return new Path2D.Double(pSrc, this); | ||
| 3812 | } | ||
| 3813 | |||
| 3814 | // Round values to sane precision for printing | ||
| 3815 | // Note that Math.sin(Math.PI) has an error of about 10^-16 | ||
| 3816 | private static double _matround(double matval) { | ||
| 3817 | return Math.rint(matval * 1E15) / 1E15; | ||
| 3818 | } | ||
| 3819 | |||
| 3820 | /** | ||
| 3821 | * Returns a <code>String</code> that represents the value of this | ||
| 3822 | * {@link Object}. | ||
| 3823 | * @return a <code>String</code> representing the value of this | ||
| 3824 | * <code>Object</code>. | ||
| 3825 | * @since 1.2 | ||
| 3826 | */ | ||
| 3827 | public String toString() { | ||
| 3828 | return ("AffineTransform[[" | ||
| 3829 | + _matround(m00) + ", " | ||
| 3830 | + _matround(m01) + ", " | ||
| 3831 | + _matround(m02) + "], [" | ||
| 3832 | + _matround(m10) + ", " | ||
| 3833 | + _matround(m11) + ", " | ||
| 3834 | + _matround(m12) + "]]"); | ||
| 3835 | } | ||
| 3836 | |||
| 3837 | /** | ||
| 3838 | * Returns <code>true</code> if this <code>AffineTransform</code> is | ||
| 3839 | * an identity transform. | ||
| 3840 | * @return <code>true</code> if this <code>AffineTransform</code> is | ||
| 3841 | * an identity transform; <code>false</code> otherwise. | ||
| 3842 | * @since 1.2 | ||
| 3843 | */ | ||
| 3844 | public boolean isIdentity() { | ||
| 3845 | return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY)); | ||
| 3846 | } | ||
| 3847 | |||
| 3848 | /** | ||
| 3849 | * Returns a copy of this <code>AffineTransform</code> object. | ||
| 3850 | * @return an <code>Object</code> that is a copy of this | ||
| 3851 | * <code>AffineTransform</code> object. | ||
| 3852 | * @since 1.2 | ||
| 3853 | */ | ||
| 3854 | public Object clone() { | ||
| 3855 | try { | ||
| 3856 | return super.clone(); | ||
| 3857 | } catch (CloneNotSupportedException e) { | ||
| 3858 | // this shouldn't happen, since we are Cloneable | ||
| 3859 | throw new InternalError(); | ||
| 3860 | } | ||
| 3861 | } | ||
| 3862 | |||
| 3863 | /** | ||
| 3864 | * Returns the hashcode for this transform. | ||
| 3865 | * @return a hash code for this transform. | ||
| 3866 | * @since 1.2 | ||
| 3867 | */ | ||
| 3868 | public int hashCode() { | ||
| 3869 | long bits = Double.doubleToLongBits(m00); | ||
| 3870 | bits = bits * 31 + Double.doubleToLongBits(m01); | ||
| 3871 | bits = bits * 31 + Double.doubleToLongBits(m02); | ||
| 3872 | bits = bits * 31 + Double.doubleToLongBits(m10); | ||
| 3873 | bits = bits * 31 + Double.doubleToLongBits(m11); | ||
| 3874 | bits = bits * 31 + Double.doubleToLongBits(m12); | ||
| 3875 | return (((int) bits) ^ ((int) (bits >> 32))); | ||
| 3876 | } | ||
| 3877 | |||
| 3878 | /** | ||
| 3879 | * Returns <code>true</code> if this <code>AffineTransform</code> | ||
| 3880 | * represents the same affine coordinate transform as the specified | ||
| 3881 | * argument. | ||
| 3882 | * @param obj the <code>Object</code> to test for equality with this | ||
| 3883 | * <code>AffineTransform</code> | ||
| 3884 | * @return <code>true</code> if <code>obj</code> equals this | ||
| 3885 | * <code>AffineTransform</code> object; <code>false</code> otherwise. | ||
| 3886 | * @since 1.2 | ||
| 3887 | */ | ||
| 3888 | public boolean equals(Object obj) { | ||
| 3889 | if (!(obj instanceof AffineTransform)) { | ||
| 3890 | return false; | ||
| 3891 | } | ||
| 3892 | |||
| 3893 | AffineTransform a = (AffineTransform)obj; | ||
| 3894 | |||
| 3895 | return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) && | ||
| 3896 | (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12)); | ||
| 3897 | } | ||
| 3898 | |||
| 3899 | /* Serialization support. A readObject method is neccessary because | ||
| 3900 | * the state field is part of the implementation of this particular | ||
| 3901 | * AffineTransform and not part of the public specification. The | ||
| 3902 | * state variable's value needs to be recalculated on the fly by the | ||
| 3903 | * readObject method as it is in the 6-argument matrix constructor. | ||
| 3904 | */ | ||
| 3905 | |||
| 3906 | /* | ||
| 3907 | * JDK 1.2 serialVersionUID | ||
| 3908 | */ | ||
| 3909 | private static final long serialVersionUID = 1330973210523860834L; | ||
| 3910 | |||
| 3911 | private void writeObject(java.io.ObjectOutputStream s) | ||
| 3912 | throws java.lang.ClassNotFoundException, java.io.IOException | ||
| 3913 | { | ||
| 3914 | s.defaultWriteObject(); | ||
| 3915 | } | ||
| 3916 | |||
| 3917 | private void readObject(java.io.ObjectInputStream s) | ||
| 3918 | throws java.lang.ClassNotFoundException, java.io.IOException | ||
| 3919 | { | ||
| 3920 | s.defaultReadObject(); | ||
| 3921 | updateState(); | ||
| 3922 | } | ||
| 3923 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,1474 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.io.Serializable; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * <CODE>Arc2D</CODE> is the abstract superclass for all objects that | ||
| 32 | * store a 2D arc defined by a framing rectangle, | ||
| 33 | * start angle, angular extent (length of the arc), and a closure type | ||
| 34 | * (<CODE>OPEN</CODE>, <CODE>CHORD</CODE>, or <CODE>PIE</CODE>). | ||
| 35 | * <p> | ||
| 36 | * <a name="inscribes"> | ||
| 37 | * The arc is a partial section of a full ellipse which | ||
| 38 | * inscribes the framing rectangle of its parent {@link RectangularShape}. | ||
| 39 | * </a> | ||
| 40 | * <a name="angles"> | ||
| 41 | * The angles are specified relative to the non-square | ||
| 42 | * framing rectangle such that 45 degrees always falls on the line from | ||
| 43 | * the center of the ellipse to the upper right corner of the framing | ||
| 44 | * rectangle. | ||
| 45 | * As a result, if the framing rectangle is noticeably longer along one | ||
| 46 | * axis than the other, the angles to the start and end of the arc segment | ||
| 47 | * will be skewed farther along the longer axis of the frame. | ||
| 48 | * </a> | ||
| 49 | * <p> | ||
| 50 | * The actual storage representation of the coordinates is left to | ||
| 51 | * the subclass. | ||
| 52 | * | ||
| 53 | * @author Jim Graham | ||
| 54 | * @since 1.2 | ||
| 55 | */ | ||
| 56 | public abstract class Arc2D extends RectangularShape { | ||
| 57 | |||
| 58 | /** | ||
| 59 | * The closure type for an open arc with no path segments | ||
| 60 | * connecting the two ends of the arc segment. | ||
| 61 | * @since 1.2 | ||
| 62 | */ | ||
| 63 | public final static int OPEN = 0; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * The closure type for an arc closed by drawing a straight | ||
| 67 | * line segment from the start of the arc segment to the end of the | ||
| 68 | * arc segment. | ||
| 69 | * @since 1.2 | ||
| 70 | */ | ||
| 71 | public final static int CHORD = 1; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * The closure type for an arc closed by drawing straight line | ||
| 75 | * segments from the start of the arc segment to the center | ||
| 76 | * of the full ellipse and from that point to the end of the arc segment. | ||
| 77 | * @since 1.2 | ||
| 78 | */ | ||
| 79 | public final static int PIE = 2; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * This class defines an arc specified in {@code float} precision. | ||
| 83 | * @since 1.2 | ||
| 84 | */ | ||
| 85 | public static class Float extends Arc2D implements Serializable { | ||
| 86 | /** | ||
| 87 | * The X coordinate of the upper-left corner of the framing | ||
| 88 | * rectangle of the arc. | ||
| 89 | * @since 1.2 | ||
| 90 | * @serial | ||
| 91 | */ | ||
| 92 | public float x; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * The Y coordinate of the upper-left corner of the framing | ||
| 96 | * rectangle of the arc. | ||
| 97 | * @since 1.2 | ||
| 98 | * @serial | ||
| 99 | */ | ||
| 100 | public float y; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * The overall width of the full ellipse of which this arc is | ||
| 104 | * a partial section (not considering the | ||
| 105 | * angular extents). | ||
| 106 | * @since 1.2 | ||
| 107 | * @serial | ||
| 108 | */ | ||
| 109 | public float width; | ||
| 110 | |||
| 111 | /** | ||
| 112 | * The overall height of the full ellipse of which this arc is | ||
| 113 | * a partial section (not considering the | ||
| 114 | * angular extents). | ||
| 115 | * @since 1.2 | ||
| 116 | * @serial | ||
| 117 | */ | ||
| 118 | public float height; | ||
| 119 | |||
| 120 | /** | ||
| 121 | * The starting angle of the arc in degrees. | ||
| 122 | * @since 1.2 | ||
| 123 | * @serial | ||
| 124 | */ | ||
| 125 | public float start; | ||
| 126 | |||
| 127 | /** | ||
| 128 | * The angular extent of the arc in degrees. | ||
| 129 | * @since 1.2 | ||
| 130 | * @serial | ||
| 131 | */ | ||
| 132 | public float extent; | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Constructs a new OPEN arc, initialized to location (0, 0), | ||
| 136 | * size (0, 0), angular extents (start = 0, extent = 0). | ||
| 137 | * @since 1.2 | ||
| 138 | */ | ||
| 139 | public Float() { | ||
| 140 | super(OPEN); | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Constructs a new arc, initialized to location (0, 0), | ||
| 145 | * size (0, 0), angular extents (start = 0, extent = 0), and | ||
| 146 | * the specified closure type. | ||
| 147 | * | ||
| 148 | * @param type The closure type for the arc: | ||
| 149 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 150 | * @since 1.2 | ||
| 151 | */ | ||
| 152 | public Float(int type) { | ||
| 153 | super(type); | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Constructs a new arc, initialized to the specified location, | ||
| 158 | * size, angular extents, and closure type. | ||
| 159 | * | ||
| 160 | * @param x The X coordinate of the upper-left corner of | ||
| 161 | * the arc's framing rectangle. | ||
| 162 | * @param y The Y coordinate of the upper-left corner of | ||
| 163 | * the arc's framing rectangle. | ||
| 164 | * @param w The overall width of the full ellipse of which | ||
| 165 | * this arc is a partial section. | ||
| 166 | * @param h The overall height of the full ellipse of which this | ||
| 167 | * arc is a partial section. | ||
| 168 | * @param start The starting angle of the arc in degrees. | ||
| 169 | * @param extent The angular extent of the arc in degrees. | ||
| 170 | * @param type The closure type for the arc: | ||
| 171 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 172 | * @since 1.2 | ||
| 173 | */ | ||
| 174 | public Float(float x, float y, float w, float h, | ||
| 175 | float start, float extent, int type) { | ||
| 176 | super(type); | ||
| 177 | this.x = x; | ||
| 178 | this.y = y; | ||
| 179 | this.width = w; | ||
| 180 | this.height = h; | ||
| 181 | this.start = start; | ||
| 182 | this.extent = extent; | ||
| 183 | } | ||
| 184 | |||
| 185 | /** | ||
| 186 | * Constructs a new arc, initialized to the specified location, | ||
| 187 | * size, angular extents, and closure type. | ||
| 188 | * | ||
| 189 | * @param ellipseBounds The framing rectangle that defines the | ||
| 190 | * outer boundary of the full ellipse of which this arc is a | ||
| 191 | * partial section. | ||
| 192 | * @param start The starting angle of the arc in degrees. | ||
| 193 | * @param extent The angular extent of the arc in degrees. | ||
| 194 | * @param type The closure type for the arc: | ||
| 195 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 196 | * @since 1.2 | ||
| 197 | */ | ||
| 198 | public Float(Rectangle2D ellipseBounds, | ||
| 199 | float start, float extent, int type) { | ||
| 200 | super(type); | ||
| 201 | this.x = (float) ellipseBounds.getX(); | ||
| 202 | this.y = (float) ellipseBounds.getY(); | ||
| 203 | this.width = (float) ellipseBounds.getWidth(); | ||
| 204 | this.height = (float) ellipseBounds.getHeight(); | ||
| 205 | this.start = start; | ||
| 206 | this.extent = extent; | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * {@inheritDoc} | ||
| 211 | * Note that the arc | ||
| 212 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 213 | * the framing rectangle of this {@code RectangularShape}. | ||
| 214 | * | ||
| 215 | * @since 1.2 | ||
| 216 | */ | ||
| 217 | public double getX() { | ||
| 218 | return (double) x; | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * {@inheritDoc} | ||
| 223 | * Note that the arc | ||
| 224 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 225 | * the framing rectangle of this {@code RectangularShape}. | ||
| 226 | * | ||
| 227 | * @since 1.2 | ||
| 228 | */ | ||
| 229 | public double getY() { | ||
| 230 | return (double) y; | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * {@inheritDoc} | ||
| 235 | * Note that the arc | ||
| 236 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 237 | * the framing rectangle of this {@code RectangularShape}. | ||
| 238 | * | ||
| 239 | * @since 1.2 | ||
| 240 | */ | ||
| 241 | public double getWidth() { | ||
| 242 | return (double) width; | ||
| 243 | } | ||
| 244 | |||
| 245 | /** | ||
| 246 | * {@inheritDoc} | ||
| 247 | * Note that the arc | ||
| 248 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 249 | * the framing rectangle of this {@code RectangularShape}. | ||
| 250 | * | ||
| 251 | * @since 1.2 | ||
| 252 | */ | ||
| 253 | public double getHeight() { | ||
| 254 | return (double) height; | ||
| 255 | } | ||
| 256 | |||
| 257 | /** | ||
| 258 | * {@inheritDoc} | ||
| 259 | * @since 1.2 | ||
| 260 | */ | ||
| 261 | public double getAngleStart() { | ||
| 262 | return (double) start; | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * {@inheritDoc} | ||
| 267 | * @since 1.2 | ||
| 268 | */ | ||
| 269 | public double getAngleExtent() { | ||
| 270 | return (double) extent; | ||
| 271 | } | ||
| 272 | |||
| 273 | /** | ||
| 274 | * {@inheritDoc} | ||
| 275 | * @since 1.2 | ||
| 276 | */ | ||
| 277 | public boolean isEmpty() { | ||
| 278 | return (width <= 0.0 || height <= 0.0); | ||
| 279 | } | ||
| 280 | |||
| 281 | /** | ||
| 282 | * {@inheritDoc} | ||
| 283 | * @since 1.2 | ||
| 284 | */ | ||
| 285 | public void setArc(double x, double y, double w, double h, | ||
| 286 | double angSt, double angExt, int closure) { | ||
| 287 | this.setArcType(closure); | ||
| 288 | this.x = (float) x; | ||
| 289 | this.y = (float) y; | ||
| 290 | this.width = (float) w; | ||
| 291 | this.height = (float) h; | ||
| 292 | this.start = (float) angSt; | ||
| 293 | this.extent = (float) angExt; | ||
| 294 | } | ||
| 295 | |||
| 296 | /** | ||
| 297 | * {@inheritDoc} | ||
| 298 | * @since 1.2 | ||
| 299 | */ | ||
| 300 | public void setAngleStart(double angSt) { | ||
| 301 | this.start = (float) angSt; | ||
| 302 | } | ||
| 303 | |||
| 304 | /** | ||
| 305 | * {@inheritDoc} | ||
| 306 | * @since 1.2 | ||
| 307 | */ | ||
| 308 | public void setAngleExtent(double angExt) { | ||
| 309 | this.extent = (float) angExt; | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * {@inheritDoc} | ||
| 314 | * @since 1.2 | ||
| 315 | */ | ||
| 316 | protected Rectangle2D makeBounds(double x, double y, | ||
| 317 | double w, double h) { | ||
| 318 | return new Rectangle2D.Float((float) x, (float) y, | ||
| 319 | (float) w, (float) h); | ||
| 320 | } | ||
| 321 | |||
| 322 | /* | ||
| 323 | * JDK 1.6 serialVersionUID | ||
| 324 | */ | ||
| 325 | private static final long serialVersionUID = 9130893014586380278L; | ||
| 326 | |||
| 327 | /** | ||
| 328 | * Writes the default serializable fields to the | ||
| 329 | * <code>ObjectOutputStream</code> followed by a byte | ||
| 330 | * indicating the arc type of this <code>Arc2D</code> | ||
| 331 | * instance. | ||
| 332 | * | ||
| 333 | * @serialData | ||
| 334 | * <ol> | ||
| 335 | * <li>The default serializable fields. | ||
| 336 | * <li> | ||
| 337 | * followed by a <code>byte</code> indicating the arc type | ||
| 338 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 339 | * </ol> | ||
| 340 | */ | ||
| 341 | private void writeObject(java.io.ObjectOutputStream s) | ||
| 342 | throws java.io.IOException | ||
| 343 | { | ||
| 344 | s.defaultWriteObject(); | ||
| 345 | |||
| 346 | s.writeByte(getArcType()); | ||
| 347 | } | ||
| 348 | |||
| 349 | /** | ||
| 350 | * Reads the default serializable fields from the | ||
| 351 | * <code>ObjectInputStream</code> followed by a byte | ||
| 352 | * indicating the arc type of this <code>Arc2D</code> | ||
| 353 | * instance. | ||
| 354 | * | ||
| 355 | * @serialData | ||
| 356 | * <ol> | ||
| 357 | * <li>The default serializable fields. | ||
| 358 | * <li> | ||
| 359 | * followed by a <code>byte</code> indicating the arc type | ||
| 360 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 361 | * </ol> | ||
| 362 | */ | ||
| 363 | private void readObject(java.io.ObjectInputStream s) | ||
| 364 | throws java.lang.ClassNotFoundException, java.io.IOException | ||
| 365 | { | ||
| 366 | s.defaultReadObject(); | ||
| 367 | |||
| 368 | try { | ||
| 369 | setArcType(s.readByte()); | ||
| 370 | } catch (IllegalArgumentException iae) { | ||
| 371 | throw new java.io.InvalidObjectException(iae.getMessage()); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | /** | ||
| 377 | * This class defines an arc specified in {@code double} precision. | ||
| 378 | * @since 1.2 | ||
| 379 | */ | ||
| 380 | public static class Double extends Arc2D implements Serializable { | ||
| 381 | /** | ||
| 382 | * The X coordinate of the upper-left corner of the framing | ||
| 383 | * rectangle of the arc. | ||
| 384 | * @since 1.2 | ||
| 385 | * @serial | ||
| 386 | */ | ||
| 387 | public double x; | ||
| 388 | |||
| 389 | /** | ||
| 390 | * The Y coordinate of the upper-left corner of the framing | ||
| 391 | * rectangle of the arc. | ||
| 392 | * @since 1.2 | ||
| 393 | * @serial | ||
| 394 | */ | ||
| 395 | public double y; | ||
| 396 | |||
| 397 | /** | ||
| 398 | * The overall width of the full ellipse of which this arc is | ||
| 399 | * a partial section (not considering the angular extents). | ||
| 400 | * @since 1.2 | ||
| 401 | * @serial | ||
| 402 | */ | ||
| 403 | public double width; | ||
| 404 | |||
| 405 | /** | ||
| 406 | * The overall height of the full ellipse of which this arc is | ||
| 407 | * a partial section (not considering the angular extents). | ||
| 408 | * @since 1.2 | ||
| 409 | * @serial | ||
| 410 | */ | ||
| 411 | public double height; | ||
| 412 | |||
| 413 | /** | ||
| 414 | * The starting angle of the arc in degrees. | ||
| 415 | * @since 1.2 | ||
| 416 | * @serial | ||
| 417 | */ | ||
| 418 | public double start; | ||
| 419 | |||
| 420 | /** | ||
| 421 | * The angular extent of the arc in degrees. | ||
| 422 | * @since 1.2 | ||
| 423 | * @serial | ||
| 424 | */ | ||
| 425 | public double extent; | ||
| 426 | |||
| 427 | /** | ||
| 428 | * Constructs a new OPEN arc, initialized to location (0, 0), | ||
| 429 | * size (0, 0), angular extents (start = 0, extent = 0). | ||
| 430 | * @since 1.2 | ||
| 431 | */ | ||
| 432 | public Double() { | ||
| 433 | super(OPEN); | ||
| 434 | } | ||
| 435 | |||
| 436 | /** | ||
| 437 | * Constructs a new arc, initialized to location (0, 0), | ||
| 438 | * size (0, 0), angular extents (start = 0, extent = 0), and | ||
| 439 | * the specified closure type. | ||
| 440 | * | ||
| 441 | * @param type The closure type for the arc: | ||
| 442 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 443 | * @since 1.2 | ||
| 444 | */ | ||
| 445 | public Double(int type) { | ||
| 446 | super(type); | ||
| 447 | } | ||
| 448 | |||
| 449 | /** | ||
| 450 | * Constructs a new arc, initialized to the specified location, | ||
| 451 | * size, angular extents, and closure type. | ||
| 452 | * | ||
| 453 | * @param x The X coordinate of the upper-left corner | ||
| 454 | * of the arc's framing rectangle. | ||
| 455 | * @param y The Y coordinate of the upper-left corner | ||
| 456 | * of the arc's framing rectangle. | ||
| 457 | * @param w The overall width of the full ellipse of which this | ||
| 458 | * arc is a partial section. | ||
| 459 | * @param h The overall height of the full ellipse of which this | ||
| 460 | * arc is a partial section. | ||
| 461 | * @param start The starting angle of the arc in degrees. | ||
| 462 | * @param extent The angular extent of the arc in degrees. | ||
| 463 | * @param type The closure type for the arc: | ||
| 464 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 465 | * @since 1.2 | ||
| 466 | */ | ||
| 467 | public Double(double x, double y, double w, double h, | ||
| 468 | double start, double extent, int type) { | ||
| 469 | super(type); | ||
| 470 | this.x = x; | ||
| 471 | this.y = y; | ||
| 472 | this.width = w; | ||
| 473 | this.height = h; | ||
| 474 | this.start = start; | ||
| 475 | this.extent = extent; | ||
| 476 | } | ||
| 477 | |||
| 478 | /** | ||
| 479 | * Constructs a new arc, initialized to the specified location, | ||
| 480 | * size, angular extents, and closure type. | ||
| 481 | * | ||
| 482 | * @param ellipseBounds The framing rectangle that defines the | ||
| 483 | * outer boundary of the full ellipse of which this arc is a | ||
| 484 | * partial section. | ||
| 485 | * @param start The starting angle of the arc in degrees. | ||
| 486 | * @param extent The angular extent of the arc in degrees. | ||
| 487 | * @param type The closure type for the arc: | ||
| 488 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 489 | * @since 1.2 | ||
| 490 | */ | ||
| 491 | public Double(Rectangle2D ellipseBounds, | ||
| 492 | double start, double extent, int type) { | ||
| 493 | super(type); | ||
| 494 | this.x = ellipseBounds.getX(); | ||
| 495 | this.y = ellipseBounds.getY(); | ||
| 496 | this.width = ellipseBounds.getWidth(); | ||
| 497 | this.height = ellipseBounds.getHeight(); | ||
| 498 | this.start = start; | ||
| 499 | this.extent = extent; | ||
| 500 | } | ||
| 501 | |||
| 502 | /** | ||
| 503 | * {@inheritDoc} | ||
| 504 | * Note that the arc | ||
| 505 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 506 | * the framing rectangle of this {@code RectangularShape}. | ||
| 507 | * | ||
| 508 | * @since 1.2 | ||
| 509 | */ | ||
| 510 | public double getX() { | ||
| 511 | return x; | ||
| 512 | } | ||
| 513 | |||
| 514 | /** | ||
| 515 | * {@inheritDoc} | ||
| 516 | * Note that the arc | ||
| 517 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 518 | * the framing rectangle of this {@code RectangularShape}. | ||
| 519 | * | ||
| 520 | * @since 1.2 | ||
| 521 | */ | ||
| 522 | public double getY() { | ||
| 523 | return y; | ||
| 524 | } | ||
| 525 | |||
| 526 | /** | ||
| 527 | * {@inheritDoc} | ||
| 528 | * Note that the arc | ||
| 529 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 530 | * the framing rectangle of this {@code RectangularShape}. | ||
| 531 | * | ||
| 532 | * @since 1.2 | ||
| 533 | */ | ||
| 534 | public double getWidth() { | ||
| 535 | return width; | ||
| 536 | } | ||
| 537 | |||
| 538 | /** | ||
| 539 | * {@inheritDoc} | ||
| 540 | * Note that the arc | ||
| 541 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 542 | * the framing rectangle of this {@code RectangularShape}. | ||
| 543 | * | ||
| 544 | * @since 1.2 | ||
| 545 | */ | ||
| 546 | public double getHeight() { | ||
| 547 | return height; | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * {@inheritDoc} | ||
| 552 | * @since 1.2 | ||
| 553 | */ | ||
| 554 | public double getAngleStart() { | ||
| 555 | return start; | ||
| 556 | } | ||
| 557 | |||
| 558 | /** | ||
| 559 | * {@inheritDoc} | ||
| 560 | * @since 1.2 | ||
| 561 | */ | ||
| 562 | public double getAngleExtent() { | ||
| 563 | return extent; | ||
| 564 | } | ||
| 565 | |||
| 566 | /** | ||
| 567 | * {@inheritDoc} | ||
| 568 | * @since 1.2 | ||
| 569 | */ | ||
| 570 | public boolean isEmpty() { | ||
| 571 | return (width <= 0.0 || height <= 0.0); | ||
| 572 | } | ||
| 573 | |||
| 574 | /** | ||
| 575 | * {@inheritDoc} | ||
| 576 | * @since 1.2 | ||
| 577 | */ | ||
| 578 | public void setArc(double x, double y, double w, double h, | ||
| 579 | double angSt, double angExt, int closure) { | ||
| 580 | this.setArcType(closure); | ||
| 581 | this.x = x; | ||
| 582 | this.y = y; | ||
| 583 | this.width = w; | ||
| 584 | this.height = h; | ||
| 585 | this.start = angSt; | ||
| 586 | this.extent = angExt; | ||
| 587 | } | ||
| 588 | |||
| 589 | /** | ||
| 590 | * {@inheritDoc} | ||
| 591 | * @since 1.2 | ||
| 592 | */ | ||
| 593 | public void setAngleStart(double angSt) { | ||
| 594 | this.start = angSt; | ||
| 595 | } | ||
| 596 | |||
| 597 | /** | ||
| 598 | * {@inheritDoc} | ||
| 599 | * @since 1.2 | ||
| 600 | */ | ||
| 601 | public void setAngleExtent(double angExt) { | ||
| 602 | this.extent = angExt; | ||
| 603 | } | ||
| 604 | |||
| 605 | /** | ||
| 606 | * {@inheritDoc} | ||
| 607 | * @since 1.2 | ||
| 608 | */ | ||
| 609 | protected Rectangle2D makeBounds(double x, double y, | ||
| 610 | double w, double h) { | ||
| 611 | return new Rectangle2D.Double(x, y, w, h); | ||
| 612 | } | ||
| 613 | |||
| 614 | /* | ||
| 615 | * JDK 1.6 serialVersionUID | ||
| 616 | */ | ||
| 617 | private static final long serialVersionUID = 728264085846882001L; | ||
| 618 | |||
| 619 | /** | ||
| 620 | * Writes the default serializable fields to the | ||
| 621 | * <code>ObjectOutputStream</code> followed by a byte | ||
| 622 | * indicating the arc type of this <code>Arc2D</code> | ||
| 623 | * instance. | ||
| 624 | * | ||
| 625 | * @serialData | ||
| 626 | * <ol> | ||
| 627 | * <li>The default serializable fields. | ||
| 628 | * <li> | ||
| 629 | * followed by a <code>byte</code> indicating the arc type | ||
| 630 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 631 | * </ol> | ||
| 632 | */ | ||
| 633 | private void writeObject(java.io.ObjectOutputStream s) | ||
| 634 | throws java.io.IOException | ||
| 635 | { | ||
| 636 | s.defaultWriteObject(); | ||
| 637 | |||
| 638 | s.writeByte(getArcType()); | ||
| 639 | } | ||
| 640 | |||
| 641 | /** | ||
| 642 | * Reads the default serializable fields from the | ||
| 643 | * <code>ObjectInputStream</code> followed by a byte | ||
| 644 | * indicating the arc type of this <code>Arc2D</code> | ||
| 645 | * instance. | ||
| 646 | * | ||
| 647 | * @serialData | ||
| 648 | * <ol> | ||
| 649 | * <li>The default serializable fields. | ||
| 650 | * <li> | ||
| 651 | * followed by a <code>byte</code> indicating the arc type | ||
| 652 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 653 | * </ol> | ||
| 654 | */ | ||
| 655 | private void readObject(java.io.ObjectInputStream s) | ||
| 656 | throws java.lang.ClassNotFoundException, java.io.IOException | ||
| 657 | { | ||
| 658 | s.defaultReadObject(); | ||
| 659 | |||
| 660 | try { | ||
| 661 | setArcType(s.readByte()); | ||
| 662 | } catch (IllegalArgumentException iae) { | ||
| 663 | throw new java.io.InvalidObjectException(iae.getMessage()); | ||
| 664 | } | ||
| 665 | } | ||
| 666 | } | ||
| 667 | |||
| 668 | private int type; | ||
| 669 | |||
| 670 | /** | ||
| 671 | * This is an abstract class that cannot be instantiated directly. | ||
| 672 | * Type-specific implementation subclasses are available for | ||
| 673 | * instantiation and provide a number of formats for storing | ||
| 674 | * the information necessary to satisfy the various accessor | ||
| 675 | * methods below. | ||
| 676 | * <p> | ||
| 677 | * This constructor creates an object with a default closure | ||
| 678 | * type of {@link #OPEN}. It is provided only to enable | ||
| 679 | * serialization of subclasses. | ||
| 680 | * | ||
| 681 | * @see java.awt.geom.Arc2D.Float | ||
| 682 | * @see java.awt.geom.Arc2D.Double | ||
| 683 | */ | ||
| 684 | protected Arc2D() { | ||
| 685 | this(OPEN); | ||
| 686 | } | ||
| 687 | |||
| 688 | /** | ||
| 689 | * This is an abstract class that cannot be instantiated directly. | ||
| 690 | * Type-specific implementation subclasses are available for | ||
| 691 | * instantiation and provide a number of formats for storing | ||
| 692 | * the information necessary to satisfy the various accessor | ||
| 693 | * methods below. | ||
| 694 | * | ||
| 695 | * @param type The closure type of this arc: | ||
| 696 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 697 | * @see java.awt.geom.Arc2D.Float | ||
| 698 | * @see java.awt.geom.Arc2D.Double | ||
| 699 | * @since 1.2 | ||
| 700 | */ | ||
| 701 | protected Arc2D(int type) { | ||
| 702 | setArcType(type); | ||
| 703 | } | ||
| 704 | |||
| 705 | /** | ||
| 706 | * Returns the starting angle of the arc. | ||
| 707 | * | ||
| 708 | * @return A double value that represents the starting angle | ||
| 709 | * of the arc in degrees. | ||
| 710 | * @see #setAngleStart | ||
| 711 | * @since 1.2 | ||
| 712 | */ | ||
| 713 | public abstract double getAngleStart(); | ||
| 714 | |||
| 715 | /** | ||
| 716 | * Returns the angular extent of the arc. | ||
| 717 | * | ||
| 718 | * @return A double value that represents the angular extent | ||
| 719 | * of the arc in degrees. | ||
| 720 | * @see #setAngleExtent | ||
| 721 | * @since 1.2 | ||
| 722 | */ | ||
| 723 | public abstract double getAngleExtent(); | ||
| 724 | |||
| 725 | /** | ||
| 726 | * Returns the arc closure type of the arc: {@link #OPEN}, | ||
| 727 | * {@link #CHORD}, or {@link #PIE}. | ||
| 728 | * @return One of the integer constant closure types defined | ||
| 729 | * in this class. | ||
| 730 | * @see #setArcType | ||
| 731 | * @since 1.2 | ||
| 732 | */ | ||
| 733 | public int getArcType() { | ||
| 734 | return type; | ||
| 735 | } | ||
| 736 | |||
| 737 | /** | ||
| 738 | * Returns the starting point of the arc. This point is the | ||
| 739 | * intersection of the ray from the center defined by the | ||
| 740 | * starting angle and the elliptical boundary of the arc. | ||
| 741 | * | ||
| 742 | * @return A <CODE>Point2D</CODE> object representing the | ||
| 743 | * x,y coordinates of the starting point of the arc. | ||
| 744 | * @since 1.2 | ||
| 745 | */ | ||
| 746 | public Point2D getStartPoint() { | ||
| 747 | double angle = Math.toRadians(-getAngleStart()); | ||
| 748 | double x = getX() + (Math.cos(angle) * 0.5 + 0.5) * getWidth(); | ||
| 749 | double y = getY() + (Math.sin(angle) * 0.5 + 0.5) * getHeight(); | ||
| 750 | return new Point2D.Double(x, y); | ||
| 751 | } | ||
| 752 | |||
| 753 | /** | ||
| 754 | * Returns the ending point of the arc. This point is the | ||
| 755 | * intersection of the ray from the center defined by the | ||
| 756 | * starting angle plus the angular extent of the arc and the | ||
| 757 | * elliptical boundary of the arc. | ||
| 758 | * | ||
| 759 | * @return A <CODE>Point2D</CODE> object representing the | ||
| 760 | * x,y coordinates of the ending point of the arc. | ||
| 761 | * @since 1.2 | ||
| 762 | */ | ||
| 763 | public Point2D getEndPoint() { | ||
| 764 | double angle = Math.toRadians(-getAngleStart() - getAngleExtent()); | ||
| 765 | double x = getX() + (Math.cos(angle) * 0.5 + 0.5) * getWidth(); | ||
| 766 | double y = getY() + (Math.sin(angle) * 0.5 + 0.5) * getHeight(); | ||
| 767 | return new Point2D.Double(x, y); | ||
| 768 | } | ||
| 769 | |||
| 770 | /** | ||
| 771 | * Sets the location, size, angular extents, and closure type of | ||
| 772 | * this arc to the specified double values. | ||
| 773 | * | ||
| 774 | * @param x The X coordinate of the upper-left corner of the arc. | ||
| 775 | * @param y The Y coordinate of the upper-left corner of the arc. | ||
| 776 | * @param w The overall width of the full ellipse of which | ||
| 777 | * this arc is a partial section. | ||
| 778 | * @param h The overall height of the full ellipse of which | ||
| 779 | * this arc is a partial section. | ||
| 780 | * @param angSt The starting angle of the arc in degrees. | ||
| 781 | * @param angExt The angular extent of the arc in degrees. | ||
| 782 | * @param closure The closure type for the arc: | ||
| 783 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 784 | * @since 1.2 | ||
| 785 | */ | ||
| 786 | public abstract void setArc(double x, double y, double w, double h, | ||
| 787 | double angSt, double angExt, int closure); | ||
| 788 | |||
| 789 | /** | ||
| 790 | * Sets the location, size, angular extents, and closure type of | ||
| 791 | * this arc to the specified values. | ||
| 792 | * | ||
| 793 | * @param loc The <CODE>Point2D</CODE> representing the coordinates of | ||
| 794 | * the upper-left corner of the arc. | ||
| 795 | * @param size The <CODE>Dimension2D</CODE> representing the width | ||
| 796 | * and height of the full ellipse of which this arc is | ||
| 797 | * a partial section. | ||
| 798 | * @param angSt The starting angle of the arc in degrees. | ||
| 799 | * @param angExt The angular extent of the arc in degrees. | ||
| 800 | * @param closure The closure type for the arc: | ||
| 801 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 802 | * @since 1.2 | ||
| 803 | */ | ||
| 804 | public void setArc(Point2D loc, Dimension2D size, | ||
| 805 | double angSt, double angExt, int closure) { | ||
| 806 | setArc(loc.getX(), loc.getY(), size.getWidth(), size.getHeight(), | ||
| 807 | angSt, angExt, closure); | ||
| 808 | } | ||
| 809 | |||
| 810 | /** | ||
| 811 | * Sets the location, size, angular extents, and closure type of | ||
| 812 | * this arc to the specified values. | ||
| 813 | * | ||
| 814 | * @param rect The framing rectangle that defines the | ||
| 815 | * outer boundary of the full ellipse of which this arc is a | ||
| 816 | * partial section. | ||
| 817 | * @param angSt The starting angle of the arc in degrees. | ||
| 818 | * @param angExt The angular extent of the arc in degrees. | ||
| 819 | * @param closure The closure type for the arc: | ||
| 820 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 821 | * @since 1.2 | ||
| 822 | */ | ||
| 823 | public void setArc(Rectangle2D rect, double angSt, double angExt, | ||
| 824 | int closure) { | ||
| 825 | setArc(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(), | ||
| 826 | angSt, angExt, closure); | ||
| 827 | } | ||
| 828 | |||
| 829 | /** | ||
| 830 | * Sets this arc to be the same as the specified arc. | ||
| 831 | * | ||
| 832 | * @param a The <CODE>Arc2D</CODE> to use to set the arc's values. | ||
| 833 | * @since 1.2 | ||
| 834 | */ | ||
| 835 | public void setArc(Arc2D a) { | ||
| 836 | setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), | ||
| 837 | a.getAngleStart(), a.getAngleExtent(), a.type); | ||
| 838 | } | ||
| 839 | |||
| 840 | /** | ||
| 841 | * Sets the position, bounds, angular extents, and closure type of | ||
| 842 | * this arc to the specified values. The arc is defined by a center | ||
| 843 | * point and a radius rather than a framing rectangle for the full ellipse. | ||
| 844 | * | ||
| 845 | * @param x The X coordinate of the center of the arc. | ||
| 846 | * @param y The Y coordinate of the center of the arc. | ||
| 847 | * @param radius The radius of the arc. | ||
| 848 | * @param angSt The starting angle of the arc in degrees. | ||
| 849 | * @param angExt The angular extent of the arc in degrees. | ||
| 850 | * @param closure The closure type for the arc: | ||
| 851 | * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. | ||
| 852 | * @since 1.2 | ||
| 853 | */ | ||
| 854 | public void setArcByCenter(double x, double y, double radius, | ||
| 855 | double angSt, double angExt, int closure) { | ||
| 856 | setArc(x - radius, y - radius, radius * 2.0, radius * 2.0, | ||
| 857 | angSt, angExt, closure); | ||
| 858 | } | ||
| 859 | |||
| 860 | /** | ||
| 861 | * Sets the position, bounds, and angular extents of this arc to the | ||
| 862 | * specified value. The starting angle of the arc is tangent to the | ||
| 863 | * line specified by points (p1, p2), the ending angle is tangent to | ||
| 864 | * the line specified by points (p2, p3), and the arc has the | ||
| 865 | * specified radius. | ||
| 866 | * | ||
| 867 | * @param p1 The first point that defines the arc. The starting | ||
| 868 | * angle of the arc is tangent to the line specified by points (p1, p2). | ||
| 869 | * @param p2 The second point that defines the arc. The starting | ||
| 870 | * angle of the arc is tangent to the line specified by points (p1, p2). | ||
| 871 | * The ending angle of the arc is tangent to the line specified by | ||
| 872 | * points (p2, p3). | ||
| 873 | * @param p3 The third point that defines the arc. The ending angle | ||
| 874 | * of the arc is tangent to the line specified by points (p2, p3). | ||
| 875 | * @param radius The radius of the arc. | ||
| 876 | * @since 1.2 | ||
| 877 | */ | ||
| 878 | public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, | ||
| 879 | double radius) { | ||
| 880 | double ang1 = Math.atan2(p1.getY() - p2.getY(), | ||
| 881 | p1.getX() - p2.getX()); | ||
| 882 | double ang2 = Math.atan2(p3.getY() - p2.getY(), | ||
| 883 | p3.getX() - p2.getX()); | ||
| 884 | double diff = ang2 - ang1; | ||
| 885 | if (diff > Math.PI) { | ||
| 886 | ang2 -= Math.PI * 2.0; | ||
| 887 | } else if (diff < -Math.PI) { | ||
| 888 | ang2 += Math.PI * 2.0; | ||
| 889 | } | ||
| 890 | double bisect = (ang1 + ang2) / 2.0; | ||
| 891 | double theta = Math.abs(ang2 - bisect); | ||
| 892 | double dist = radius / Math.sin(theta); | ||
| 893 | double x = p2.getX() + dist * Math.cos(bisect); | ||
| 894 | double y = p2.getY() + dist * Math.sin(bisect); | ||
| 895 | // REMIND: This needs some work... | ||
| 896 | if (ang1 < ang2) { | ||
| 897 | ang1 -= Math.PI / 2.0; | ||
| 898 | ang2 += Math.PI / 2.0; | ||
| 899 | } else { | ||
| 900 | ang1 += Math.PI / 2.0; | ||
| 901 | ang2 -= Math.PI / 2.0; | ||
| 902 | } | ||
| 903 | ang1 = Math.toDegrees(-ang1); | ||
| 904 | ang2 = Math.toDegrees(-ang2); | ||
| 905 | diff = ang2 - ang1; | ||
| 906 | if (diff < 0) { | ||
| 907 | diff += 360; | ||
| 908 | } else { | ||
| 909 | diff -= 360; | ||
| 910 | } | ||
| 911 | setArcByCenter(x, y, radius, ang1, diff, type); | ||
| 912 | } | ||
| 913 | |||
| 914 | /** | ||
| 915 | * Sets the starting angle of this arc to the specified double | ||
| 916 | * value. | ||
| 917 | * | ||
| 918 | * @param angSt The starting angle of the arc in degrees. | ||
| 919 | * @see #getAngleStart | ||
| 920 | * @since 1.2 | ||
| 921 | */ | ||
| 922 | public abstract void setAngleStart(double angSt); | ||
| 923 | |||
| 924 | /** | ||
| 925 | * Sets the angular extent of this arc to the specified double | ||
| 926 | * value. | ||
| 927 | * | ||
| 928 | * @param angExt The angular extent of the arc in degrees. | ||
| 929 | * @see #getAngleExtent | ||
| 930 | * @since 1.2 | ||
| 931 | */ | ||
| 932 | public abstract void setAngleExtent(double angExt); | ||
| 933 | |||
| 934 | /** | ||
| 935 | * Sets the starting angle of this arc to the angle that the | ||
| 936 | * specified point defines relative to the center of this arc. | ||
| 937 | * The angular extent of the arc will remain the same. | ||
| 938 | * | ||
| 939 | * @param p The <CODE>Point2D</CODE> that defines the starting angle. | ||
| 940 | * @see #getAngleStart | ||
| 941 | * @since 1.2 | ||
| 942 | */ | ||
| 943 | public void setAngleStart(Point2D p) { | ||
| 944 | // Bias the dx and dy by the height and width of the oval. | ||
| 945 | double dx = getHeight() * (p.getX() - getCenterX()); | ||
| 946 | double dy = getWidth() * (p.getY() - getCenterY()); | ||
| 947 | setAngleStart(-Math.toDegrees(Math.atan2(dy, dx))); | ||
| 948 | } | ||
| 949 | |||
| 950 | /** | ||
| 951 | * Sets the starting angle and angular extent of this arc using two | ||
| 952 | * sets of coordinates. The first set of coordinates is used to | ||
| 953 | * determine the angle of the starting point relative to the arc's | ||
| 954 | * center. The second set of coordinates is used to determine the | ||
| 955 | * angle of the end point relative to the arc's center. | ||
| 956 | * The arc will always be non-empty and extend counterclockwise | ||
| 957 | * from the first point around to the second point. | ||
| 958 | * | ||
| 959 | * @param x1 The X coordinate of the arc's starting point. | ||
| 960 | * @param y1 The Y coordinate of the arc's starting point. | ||
| 961 | * @param x2 The X coordinate of the arc's ending point. | ||
| 962 | * @param y2 The Y coordinate of the arc's ending point. | ||
| 963 | * @since 1.2 | ||
| 964 | */ | ||
| 965 | public void setAngles(double x1, double y1, double x2, double y2) { | ||
| 966 | double x = getCenterX(); | ||
| 967 | double y = getCenterY(); | ||
| 968 | double w = getWidth(); | ||
| 969 | double h = getHeight(); | ||
| 970 | // Note: reversing the Y equations negates the angle to adjust | ||
| 971 | // for the upside down coordinate system. | ||
| 972 | // Also we should bias atans by the height and width of the oval. | ||
| 973 | double ang1 = Math.atan2(w * (y - y1), h * (x1 - x)); | ||
| 974 | double ang2 = Math.atan2(w * (y - y2), h * (x2 - x)); | ||
| 975 | ang2 -= ang1; | ||
| 976 | if (ang2 <= 0.0) { | ||
| 977 | ang2 += Math.PI * 2.0; | ||
| 978 | } | ||
| 979 | setAngleStart(Math.toDegrees(ang1)); | ||
| 980 | setAngleExtent(Math.toDegrees(ang2)); | ||
| 981 | } | ||
| 982 | |||
| 983 | /** | ||
| 984 | * Sets the starting angle and angular extent of this arc using | ||
| 985 | * two points. The first point is used to determine the angle of | ||
| 986 | * the starting point relative to the arc's center. | ||
| 987 | * The second point is used to determine the angle of the end point | ||
| 988 | * relative to the arc's center. | ||
| 989 | * The arc will always be non-empty and extend counterclockwise | ||
| 990 | * from the first point around to the second point. | ||
| 991 | * | ||
| 992 | * @param p1 The <CODE>Point2D</CODE> that defines the arc's | ||
| 993 | * starting point. | ||
| 994 | * @param p2 The <CODE>Point2D</CODE> that defines the arc's | ||
| 995 | * ending point. | ||
| 996 | * @since 1.2 | ||
| 997 | */ | ||
| 998 | public void setAngles(Point2D p1, Point2D p2) { | ||
| 999 | setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY()); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | /** | ||
| 1003 | * Sets the closure type of this arc to the specified value: | ||
| 1004 | * <CODE>OPEN</CODE>, <CODE>CHORD</CODE>, or <CODE>PIE</CODE>. | ||
| 1005 | * | ||
| 1006 | * @param type The integer constant that represents the closure | ||
| 1007 | * type of this arc: {@link #OPEN}, {@link #CHORD}, or | ||
| 1008 | * {@link #PIE}. | ||
| 1009 | * | ||
| 1010 | * @throws IllegalArgumentException if <code>type</code> is not | ||
| 1011 | * 0, 1, or 2.+ | ||
| 1012 | * @see #getArcType | ||
| 1013 | * @since 1.2 | ||
| 1014 | */ | ||
| 1015 | public void setArcType(int type) { | ||
| 1016 | if (type < OPEN || type > PIE) { | ||
| 1017 | throw new IllegalArgumentException("invalid type for Arc: "+type); | ||
| 1018 | } | ||
| 1019 | this.type = type; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | /** | ||
| 1023 | * {@inheritDoc} | ||
| 1024 | * Note that the arc | ||
| 1025 | * <a href="Arc2D.html#inscribes">partially inscribes</a> | ||
| 1026 | * the framing rectangle of this {@code RectangularShape}. | ||
| 1027 | * | ||
| 1028 | * @since 1.2 | ||
| 1029 | */ | ||
| 1030 | public void setFrame(double x, double y, double w, double h) { | ||
| 1031 | setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type); | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | /** | ||
| 1035 | * Returns the high-precision framing rectangle of the arc. The framing | ||
| 1036 | * rectangle contains only the part of this <code>Arc2D</code> that is | ||
| 1037 | * in between the starting and ending angles and contains the pie | ||
| 1038 | * wedge, if this <code>Arc2D</code> has a <code>PIE</code> closure type. | ||
| 1039 | * <p> | ||
| 1040 | * This method differs from the | ||
| 1041 | * {@link RectangularShape#getBounds() getBounds} in that the | ||
| 1042 | * <code>getBounds</code> method only returns the bounds of the | ||
| 1043 | * enclosing ellipse of this <code>Arc2D</code> without considering | ||
| 1044 | * the starting and ending angles of this <code>Arc2D</code>. | ||
| 1045 | * | ||
| 1046 | * @return the <CODE>Rectangle2D</CODE> that represents the arc's | ||
| 1047 | * framing rectangle. | ||
| 1048 | * @since 1.2 | ||
| 1049 | */ | ||
| 1050 | public Rectangle2D getBounds2D() { | ||
| 1051 | if (isEmpty()) { | ||
| 1052 | return makeBounds(getX(), getY(), getWidth(), getHeight()); | ||
| 1053 | } | ||
| 1054 | double x1, y1, x2, y2; | ||
| 1055 | if (getArcType() == PIE) { | ||
| 1056 | x1 = y1 = x2 = y2 = 0.0; | ||
| 1057 | } else { | ||
| 1058 | x1 = y1 = 1.0; | ||
| 1059 | x2 = y2 = -1.0; | ||
| 1060 | } | ||
| 1061 | double angle = 0.0; | ||
| 1062 | for (int i = 0; i < 6; i++) { | ||
| 1063 | if (i < 4) { | ||
| 1064 | // 0-3 are the four quadrants | ||
| 1065 | angle += 90.0; | ||
| 1066 | if (!containsAngle(angle)) { | ||
| 1067 | continue; | ||
| 1068 | } | ||
| 1069 | } else if (i == 4) { | ||
| 1070 | // 4 is start angle | ||
| 1071 | angle = getAngleStart(); | ||
| 1072 | } else { | ||
| 1073 | // 5 is end angle | ||
| 1074 | angle += getAngleExtent(); | ||
| 1075 | } | ||
| 1076 | double rads = Math.toRadians(-angle); | ||
| 1077 | double xe = Math.cos(rads); | ||
| 1078 | double ye = Math.sin(rads); | ||
| 1079 | x1 = Math.min(x1, xe); | ||
| 1080 | y1 = Math.min(y1, ye); | ||
| 1081 | x2 = Math.max(x2, xe); | ||
| 1082 | y2 = Math.max(y2, ye); | ||
| 1083 | } | ||
| 1084 | double w = getWidth(); | ||
| 1085 | double h = getHeight(); | ||
| 1086 | x2 = (x2 - x1) * 0.5 * w; | ||
| 1087 | y2 = (y2 - y1) * 0.5 * h; | ||
| 1088 | x1 = getX() + (x1 * 0.5 + 0.5) * w; | ||
| 1089 | y1 = getY() + (y1 * 0.5 + 0.5) * h; | ||
| 1090 | return makeBounds(x1, y1, x2, y2); | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | /** | ||
| 1094 | * Constructs a <code>Rectangle2D</code> of the appropriate precision | ||
| 1095 | * to hold the parameters calculated to be the framing rectangle | ||
| 1096 | * of this arc. | ||
| 1097 | * | ||
| 1098 | * @param x The X coordinate of the upper-left corner of the | ||
| 1099 | * framing rectangle. | ||
| 1100 | * @param y The Y coordinate of the upper-left corner of the | ||
| 1101 | * framing rectangle. | ||
| 1102 | * @param w The width of the framing rectangle. | ||
| 1103 | * @param h The height of the framing rectangle. | ||
| 1104 | * @return a <code>Rectangle2D</code> that is the framing rectangle | ||
| 1105 | * of this arc. | ||
| 1106 | * @since 1.2 | ||
| 1107 | */ | ||
| 1108 | protected abstract Rectangle2D makeBounds(double x, double y, | ||
| 1109 | double w, double h); | ||
| 1110 | |||
| 1111 | /* | ||
| 1112 | * Normalizes the specified angle into the range -180 to 180. | ||
| 1113 | */ | ||
| 1114 | static double normalizeDegrees(double angle) { | ||
| 1115 | if (angle > 180.0) { | ||
| 1116 | if (angle <= (180.0 + 360.0)) { | ||
| 1117 | angle = angle - 360.0; | ||
| 1118 | } else { | ||
| 1119 | angle = Math.IEEEremainder(angle, 360.0); | ||
| 1120 | // IEEEremainder can return -180 here for some input values... | ||
| 1121 | if (angle == -180.0) { | ||
| 1122 | angle = 180.0; | ||
| 1123 | } | ||
| 1124 | } | ||
| 1125 | } else if (angle <= -180.0) { | ||
| 1126 | if (angle > (-180.0 - 360.0)) { | ||
| 1127 | angle = angle + 360.0; | ||
| 1128 | } else { | ||
| 1129 | angle = Math.IEEEremainder(angle, 360.0); | ||
| 1130 | // IEEEremainder can return -180 here for some input values... | ||
| 1131 | if (angle == -180.0) { | ||
| 1132 | angle = 180.0; | ||
| 1133 | } | ||
| 1134 | } | ||
| 1135 | } | ||
| 1136 | return angle; | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | /** | ||
| 1140 | * Determines whether or not the specified angle is within the | ||
| 1141 | * angular extents of the arc. | ||
| 1142 | * | ||
| 1143 | * @param angle The angle to test. | ||
| 1144 | * | ||
| 1145 | * @return <CODE>true</CODE> if the arc contains the angle, | ||
| 1146 | * <CODE>false</CODE> if the arc doesn't contain the angle. | ||
| 1147 | * @since 1.2 | ||
| 1148 | */ | ||
| 1149 | public boolean containsAngle(double angle) { | ||
| 1150 | double angExt = getAngleExtent(); | ||
| 1151 | boolean backwards = (angExt < 0.0); | ||
| 1152 | if (backwards) { | ||
| 1153 | angExt = -angExt; | ||
| 1154 | } | ||
| 1155 | if (angExt >= 360.0) { | ||
| 1156 | return true; | ||
| 1157 | } | ||
| 1158 | angle = normalizeDegrees(angle) - normalizeDegrees(getAngleStart()); | ||
| 1159 | if (backwards) { | ||
| 1160 | angle = -angle; | ||
| 1161 | } | ||
| 1162 | if (angle < 0.0) { | ||
| 1163 | angle += 360.0; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | |||
| 1167 | return (angle >= 0.0) && (angle < angExt); | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | /** | ||
| 1171 | * Determines whether or not the specified point is inside the boundary | ||
| 1172 | * of the arc. | ||
| 1173 | * | ||
| 1174 | * @param x The X coordinate of the point to test. | ||
| 1175 | * @param y The Y coordinate of the point to test. | ||
| 1176 | * | ||
| 1177 | * @return <CODE>true</CODE> if the point lies within the bound of | ||
| 1178 | * the arc, <CODE>false</CODE> if the point lies outside of the | ||
| 1179 | * arc's bounds. | ||
| 1180 | * @since 1.2 | ||
| 1181 | */ | ||
| 1182 | public boolean contains(double x, double y) { | ||
| 1183 | // Normalize the coordinates compared to the ellipse | ||
| 1184 | // having a center at 0,0 and a radius of 0.5. | ||
| 1185 | double ellw = getWidth(); | ||
| 1186 | if (ellw <= 0.0) { | ||
| 1187 | return false; | ||
| 1188 | } | ||
| 1189 | double normx = (x - getX()) / ellw - 0.5; | ||
| 1190 | double ellh = getHeight(); | ||
| 1191 | if (ellh <= 0.0) { | ||
| 1192 | return false; | ||
| 1193 | } | ||
| 1194 | double normy = (y - getY()) / ellh - 0.5; | ||
| 1195 | double distSq = (normx * normx + normy * normy); | ||
| 1196 | if (distSq >= 0.25) { | ||
| 1197 | return false; | ||
| 1198 | } | ||
| 1199 | double angExt = Math.abs(getAngleExtent()); | ||
| 1200 | if (angExt >= 360.0) { | ||
| 1201 | return true; | ||
| 1202 | } | ||
| 1203 | boolean inarc = containsAngle(-Math.toDegrees(Math.atan2(normy, | ||
| 1204 | normx))); | ||
| 1205 | if (type == PIE) { | ||
| 1206 | return inarc; | ||
| 1207 | } | ||
| 1208 | // CHORD and OPEN behave the same way | ||
| 1209 | if (inarc) { | ||
| 1210 | if (angExt >= 180.0) { | ||
| 1211 | return true; | ||
| 1212 | } | ||
| 1213 | // point must be outside the "pie triangle" | ||
| 1214 | } else { | ||
| 1215 | if (angExt <= 180.0) { | ||
| 1216 | return false; | ||
| 1217 | } | ||
| 1218 | // point must be inside the "pie triangle" | ||
| 1219 | } | ||
| 1220 | // The point is inside the pie triangle iff it is on the same | ||
| 1221 | // side of the line connecting the ends of the arc as the center. | ||
| 1222 | double angle = Math.toRadians(-getAngleStart()); | ||
| 1223 | double x1 = Math.cos(angle); | ||
| 1224 | double y1 = Math.sin(angle); | ||
| 1225 | angle += Math.toRadians(-getAngleExtent()); | ||
| 1226 | double x2 = Math.cos(angle); | ||
| 1227 | double y2 = Math.sin(angle); | ||
| 1228 | boolean inside = (Line2D.relativeCCW(x1, y1, x2, y2, 2*normx, 2*normy) * | ||
| 1229 | Line2D.relativeCCW(x1, y1, x2, y2, 0, 0) >= 0); | ||
| 1230 | return inarc ? !inside : inside; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | /** | ||
| 1234 | * Determines whether or not the interior of the arc intersects | ||
| 1235 | * the interior of the specified rectangle. | ||
| 1236 | * | ||
| 1237 | * @param x The X coordinate of the rectangle's upper-left corner. | ||
| 1238 | * @param y The Y coordinate of the rectangle's upper-left corner. | ||
| 1239 | * @param w The width of the rectangle. | ||
| 1240 | * @param h The height of the rectangle. | ||
| 1241 | * | ||
| 1242 | * @return <CODE>true</CODE> if the arc intersects the rectangle, | ||
| 1243 | * <CODE>false</CODE> if the arc doesn't intersect the rectangle. | ||
| 1244 | * @since 1.2 | ||
| 1245 | */ | ||
| 1246 | public boolean intersects(double x, double y, double w, double h) { | ||
| 1247 | |||
| 1248 | double aw = getWidth(); | ||
| 1249 | double ah = getHeight(); | ||
| 1250 | |||
| 1251 | if ( w <= 0 || h <= 0 || aw <= 0 || ah <= 0 ) { | ||
| 1252 | return false; | ||
| 1253 | } | ||
| 1254 | double ext = getAngleExtent(); | ||
| 1255 | if (ext == 0) { | ||
| 1256 | return false; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | double ax = getX(); | ||
| 1260 | double ay = getY(); | ||
| 1261 | double axw = ax + aw; | ||
| 1262 | double ayh = ay + ah; | ||
| 1263 | double xw = x + w; | ||
| 1264 | double yh = y + h; | ||
| 1265 | |||
| 1266 | // check bbox | ||
| 1267 | if (x >= axw || y >= ayh || xw <= ax || yh <= ay) { | ||
| 1268 | return false; | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | // extract necessary data | ||
| 1272 | double axc = getCenterX(); | ||
| 1273 | double ayc = getCenterY(); | ||
| 1274 | Point2D sp = getStartPoint(); | ||
| 1275 | Point2D ep = getEndPoint(); | ||
| 1276 | double sx = sp.getX(); | ||
| 1277 | double sy = sp.getY(); | ||
| 1278 | double ex = ep.getX(); | ||
| 1279 | double ey = ep.getY(); | ||
| 1280 | |||
| 1281 | /* | ||
| 1282 | * Try to catch rectangles that intersect arc in areas | ||
| 1283 | * outside of rectagle with left top corner coordinates | ||
| 1284 | * (min(center x, start point x, end point x), | ||
| 1285 | * min(center y, start point y, end point y)) | ||
| 1286 | * and rigth bottom corner coordinates | ||
| 1287 | * (max(center x, start point x, end point x), | ||
| 1288 | * max(center y, start point y, end point y)). | ||
| 1289 | * So we'll check axis segments outside of rectangle above. | ||
| 1290 | */ | ||
| 1291 | if (ayc >= y && ayc <= yh) { // 0 and 180 | ||
| 1292 | if ((sx < xw && ex < xw && axc < xw && | ||
| 1293 | axw > x && containsAngle(0)) || | ||
| 1294 | (sx > x && ex > x && axc > x && | ||
| 1295 | ax < xw && containsAngle(180))) { | ||
| 1296 | return true; | ||
| 1297 | } | ||
| 1298 | } | ||
| 1299 | if (axc >= x && axc <= xw) { // 90 and 270 | ||
| 1300 | if ((sy > y && ey > y && ayc > y && | ||
| 1301 | ay < yh && containsAngle(90)) || | ||
| 1302 | (sy < yh && ey < yh && ayc < yh && | ||
| 1303 | ayh > y && containsAngle(270))) { | ||
| 1304 | return true; | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | /* | ||
| 1309 | * For PIE we should check intersection with pie slices; | ||
| 1310 | * also we should do the same for arcs with extent is greater | ||
| 1311 | * than 180, because we should cover case of rectangle, which | ||
| 1312 | * situated between center of arc and chord, but does not | ||
| 1313 | * intersect the chord. | ||
| 1314 | */ | ||
| 1315 | Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); | ||
| 1316 | if (type == PIE || Math.abs(ext) > 180) { | ||
| 1317 | // for PIE: try to find intersections with pie slices | ||
| 1318 | if (rect.intersectsLine(axc, ayc, sx, sy) || | ||
| 1319 | rect.intersectsLine(axc, ayc, ex, ey)) { | ||
| 1320 | return true; | ||
| 1321 | } | ||
| 1322 | } else { | ||
| 1323 | // for CHORD and OPEN: try to find intersections with chord | ||
| 1324 | if (rect.intersectsLine(sx, sy, ex, ey)) { | ||
| 1325 | return true; | ||
| 1326 | } | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | // finally check the rectangle corners inside the arc | ||
| 1330 | if (contains(x, y) || contains(x + w, y) || | ||
| 1331 | contains(x, y + h) || contains(x + w, y + h)) { | ||
| 1332 | return true; | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | return false; | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | /** | ||
| 1339 | * Determines whether or not the interior of the arc entirely contains | ||
| 1340 | * the specified rectangle. | ||
| 1341 | * | ||
| 1342 | * @param x The X coordinate of the rectangle's upper-left corner. | ||
| 1343 | * @param y The Y coordinate of the rectangle's upper-left corner. | ||
| 1344 | * @param w The width of the rectangle. | ||
| 1345 | * @param h The height of the rectangle. | ||
| 1346 | * | ||
| 1347 | * @return <CODE>true</CODE> if the arc contains the rectangle, | ||
| 1348 | * <CODE>false</CODE> if the arc doesn't contain the rectangle. | ||
| 1349 | * @since 1.2 | ||
| 1350 | */ | ||
| 1351 | public boolean contains(double x, double y, double w, double h) { | ||
| 1352 | return contains(x, y, w, h, null); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | /** | ||
| 1356 | * Determines whether or not the interior of the arc entirely contains | ||
| 1357 | * the specified rectangle. | ||
| 1358 | * | ||
| 1359 | * @param r The <CODE>Rectangle2D</CODE> to test. | ||
| 1360 | * | ||
| 1361 | * @return <CODE>true</CODE> if the arc contains the rectangle, | ||
| 1362 | * <CODE>false</CODE> if the arc doesn't contain the rectangle. | ||
| 1363 | * @since 1.2 | ||
| 1364 | */ | ||
| 1365 | public boolean contains(Rectangle2D r) { | ||
| 1366 | return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight(), r); | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | private boolean contains(double x, double y, double w, double h, | ||
| 1370 | Rectangle2D origrect) { | ||
| 1371 | if (!(contains(x, y) && | ||
| 1372 | contains(x + w, y) && | ||
| 1373 | contains(x, y + h) && | ||
| 1374 | contains(x + w, y + h))) { | ||
| 1375 | return false; | ||
| 1376 | } | ||
| 1377 | // If the shape is convex then we have done all the testing | ||
| 1378 | // we need. Only PIE arcs can be concave and then only if | ||
| 1379 | // the angular extents are greater than 180 degrees. | ||
| 1380 | if (type != PIE || Math.abs(getAngleExtent()) <= 180.0) { | ||
| 1381 | return true; | ||
| 1382 | } | ||
| 1383 | // For a PIE shape we have an additional test for the case where | ||
| 1384 | // the angular extents are greater than 180 degrees and all four | ||
| 1385 | // rectangular corners are inside the shape but one of the | ||
| 1386 | // rectangle edges spans across the "missing wedge" of the arc. | ||
| 1387 | // We can test for this case by checking if the rectangle intersects | ||
| 1388 | // either of the pie angle segments. | ||
| 1389 | if (origrect == null) { | ||
| 1390 | origrect = new Rectangle2D.Double(x, y, w, h); | ||
| 1391 | } | ||
| 1392 | double halfW = getWidth() / 2.0; | ||
| 1393 | double halfH = getHeight() / 2.0; | ||
| 1394 | double xc = getX() + halfW; | ||
| 1395 | double yc = getY() + halfH; | ||
| 1396 | double angle = Math.toRadians(-getAngleStart()); | ||
| 1397 | double xe = xc + halfW * Math.cos(angle); | ||
| 1398 | double ye = yc + halfH * Math.sin(angle); | ||
| 1399 | if (origrect.intersectsLine(xc, yc, xe, ye)) { | ||
| 1400 | return false; | ||
| 1401 | } | ||
| 1402 | angle += Math.toRadians(-getAngleExtent()); | ||
| 1403 | xe = xc + halfW * Math.cos(angle); | ||
| 1404 | ye = yc + halfH * Math.sin(angle); | ||
| 1405 | return !origrect.intersectsLine(xc, yc, xe, ye); | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | /** | ||
| 1409 | * Returns an iteration object that defines the boundary of the | ||
| 1410 | * arc. | ||
| 1411 | * This iterator is multithread safe. | ||
| 1412 | * <code>Arc2D</code> guarantees that | ||
| 1413 | * modifications to the geometry of the arc | ||
| 1414 | * do not affect any iterations of that geometry that | ||
| 1415 | * are already in process. | ||
| 1416 | * | ||
| 1417 | * @param at an optional <CODE>AffineTransform</CODE> to be applied | ||
| 1418 | * to the coordinates as they are returned in the iteration, or null | ||
| 1419 | * if the untransformed coordinates are desired. | ||
| 1420 | * | ||
| 1421 | * @return A <CODE>PathIterator</CODE> that defines the arc's boundary. | ||
| 1422 | * @since 1.2 | ||
| 1423 | */ | ||
| 1424 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 1425 | return new ArcIterator(this, at); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | /** | ||
| 1429 | * Returns the hashcode for this <code>Arc2D</code>. | ||
| 1430 | * @return the hashcode for this <code>Arc2D</code>. | ||
| 1431 | * @since 1.6 | ||
| 1432 | */ | ||
| 1433 | public int hashCode() { | ||
| 1434 | long bits = java.lang.Double.doubleToLongBits(getX()); | ||
| 1435 | bits += java.lang.Double.doubleToLongBits(getY()) * 37; | ||
| 1436 | bits += java.lang.Double.doubleToLongBits(getWidth()) * 43; | ||
| 1437 | bits += java.lang.Double.doubleToLongBits(getHeight()) * 47; | ||
| 1438 | bits += java.lang.Double.doubleToLongBits(getAngleStart()) * 53; | ||
| 1439 | bits += java.lang.Double.doubleToLongBits(getAngleExtent()) * 59; | ||
| 1440 | bits += getArcType() * 61; | ||
| 1441 | return (((int) bits) ^ ((int) (bits >> 32))); | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | /** | ||
| 1445 | * Determines whether or not the specified <code>Object</code> is | ||
| 1446 | * equal to this <code>Arc2D</code>. The specified | ||
| 1447 | * <code>Object</code> is equal to this <code>Arc2D</code> | ||
| 1448 | * if it is an instance of <code>Arc2D</code> and if its | ||
| 1449 | * location, size, arc extents and type are the same as this | ||
| 1450 | * <code>Arc2D</code>. | ||
| 1451 | * @param obj an <code>Object</code> to be compared with this | ||
| 1452 | * <code>Arc2D</code>. | ||
| 1453 | * @return <code>true</code> if <code>obj</code> is an instance | ||
| 1454 | * of <code>Arc2D</code> and has the same values; | ||
| 1455 | * <code>false</code> otherwise. | ||
| 1456 | * @since 1.6 | ||
| 1457 | */ | ||
| 1458 | public boolean equals(Object obj) { | ||
| 1459 | if (obj == this) { | ||
| 1460 | return true; | ||
| 1461 | } | ||
| 1462 | if (obj instanceof Arc2D) { | ||
| 1463 | Arc2D a2d = (Arc2D) obj; | ||
| 1464 | return ((getX() == a2d.getX()) && | ||
| 1465 | (getY() == a2d.getY()) && | ||
| 1466 | (getWidth() == a2d.getWidth()) && | ||
| 1467 | (getHeight() == a2d.getHeight()) && | ||
| 1468 | (getAngleStart() == a2d.getAngleStart()) && | ||
| 1469 | (getAngleExtent() == a2d.getAngleExtent()) && | ||
| 1470 | (getArcType() == a2d.getArcType())); | ||
| 1471 | } | ||
| 1472 | return false; | ||
| 1473 | } | ||
| 1474 | } | ||
Binary file not shown.
| @@ -0,0 +1,312 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A utility class to iterate over the path segments of an arc | ||
| 32 | * through the PathIterator interface. | ||
| 33 | * | ||
| 34 | * @author Jim Graham | ||
| 35 | */ | ||
| 36 | class ArcIterator implements PathIterator { | ||
| 37 | double x, y, w, h, angStRad, increment, cv; | ||
| 38 | AffineTransform affine; | ||
| 39 | int index; | ||
| 40 | int arcSegs; | ||
| 41 | int lineSegs; | ||
| 42 | |||
| 43 | ArcIterator(Arc2D a, AffineTransform at) { | ||
| 44 | this.w = a.getWidth() / 2; | ||
| 45 | this.h = a.getHeight() / 2; | ||
| 46 | this.x = a.getX() + w; | ||
| 47 | this.y = a.getY() + h; | ||
| 48 | this.angStRad = -Math.toRadians(a.getAngleStart()); | ||
| 49 | this.affine = at; | ||
| 50 | double ext = -a.getAngleExtent(); | ||
| 51 | if (ext >= 360.0 || ext <= -360) { | ||
| 52 | arcSegs = 4; | ||
| 53 | this.increment = Math.PI / 2; | ||
| 54 | // btan(Math.PI / 2); | ||
| 55 | this.cv = 0.5522847498307933; | ||
| 56 | if (ext < 0) { | ||
| 57 | increment = -increment; | ||
| 58 | cv = -cv; | ||
| 59 | } | ||
| 60 | } else { | ||
| 61 | arcSegs = (int) Math.ceil(Math.abs(ext) / 90.0); | ||
| 62 | this.increment = Math.toRadians(ext / arcSegs); | ||
| 63 | this.cv = btan(increment); | ||
| 64 | if (cv == 0) { | ||
| 65 | arcSegs = 0; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | switch (a.getArcType()) { | ||
| 69 | case Arc2D.OPEN: | ||
| 70 | lineSegs = 0; | ||
| 71 | break; | ||
| 72 | case Arc2D.CHORD: | ||
| 73 | lineSegs = 1; | ||
| 74 | break; | ||
| 75 | case Arc2D.PIE: | ||
| 76 | lineSegs = 2; | ||
| 77 | break; | ||
| 78 | } | ||
| 79 | if (w < 0 || h < 0) { | ||
| 80 | arcSegs = lineSegs = -1; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Return the winding rule for determining the insideness of the | ||
| 86 | * path. | ||
| 87 | * @see #WIND_EVEN_ODD | ||
| 88 | * @see #WIND_NON_ZERO | ||
| 89 | */ | ||
| 90 | public int getWindingRule() { | ||
| 91 | return WIND_NON_ZERO; | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Tests if there are more points to read. | ||
| 96 | * @return true if there are more points to read | ||
| 97 | */ | ||
| 98 | public boolean isDone() { | ||
| 99 | return index > arcSegs + lineSegs; | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Moves the iterator to the next segment of the path forwards | ||
| 104 | * along the primary direction of traversal as long as there are | ||
| 105 | * more points in that direction. | ||
| 106 | */ | ||
| 107 | public void next() { | ||
| 108 | index++; | ||
| 109 | } | ||
| 110 | |||
| 111 | /* | ||
| 112 | * btan computes the length (k) of the control segments at | ||
| 113 | * the beginning and end of a cubic bezier that approximates | ||
| 114 | * a segment of an arc with extent less than or equal to | ||
| 115 | * 90 degrees. This length (k) will be used to generate the | ||
| 116 | * 2 bezier control points for such a segment. | ||
| 117 | * | ||
| 118 | * Assumptions: | ||
| 119 | * a) arc is centered on 0,0 with radius of 1.0 | ||
| 120 | * b) arc extent is less than 90 degrees | ||
| 121 | * c) control points should preserve tangent | ||
| 122 | * d) control segments should have equal length | ||
| 123 | * | ||
| 124 | * Initial data: | ||
| 125 | * start angle: ang1 | ||
| 126 | * end angle: ang2 = ang1 + extent | ||
| 127 | * start point: P1 = (x1, y1) = (cos(ang1), sin(ang1)) | ||
| 128 | * end point: P4 = (x4, y4) = (cos(ang2), sin(ang2)) | ||
| 129 | * | ||
| 130 | * Control points: | ||
| 131 | * P2 = (x2, y2) | ||
| 132 | * | x2 = x1 - k * sin(ang1) = cos(ang1) - k * sin(ang1) | ||
| 133 | * | y2 = y1 + k * cos(ang1) = sin(ang1) + k * cos(ang1) | ||
| 134 | * | ||
| 135 | * P3 = (x3, y3) | ||
| 136 | * | x3 = x4 + k * sin(ang2) = cos(ang2) + k * sin(ang2) | ||
| 137 | * | y3 = y4 - k * cos(ang2) = sin(ang2) - k * cos(ang2) | ||
| 138 | * | ||
| 139 | * The formula for this length (k) can be found using the | ||
| 140 | * following derivations: | ||
| 141 | * | ||
| 142 | * Midpoints: | ||
| 143 | * a) bezier (t = 1/2) | ||
| 144 | * bPm = P1 * (1-t)^3 + | ||
| 145 | * 3 * P2 * t * (1-t)^2 + | ||
| 146 | * 3 * P3 * t^2 * (1-t) + | ||
| 147 | * P4 * t^3 = | ||
| 148 | * = (P1 + 3P2 + 3P3 + P4)/8 | ||
| 149 | * | ||
| 150 | * b) arc | ||
| 151 | * aPm = (cos((ang1 + ang2)/2), sin((ang1 + ang2)/2)) | ||
| 152 | * | ||
| 153 | * Let angb = (ang2 - ang1)/2; angb is half of the angle | ||
| 154 | * between ang1 and ang2. | ||
| 155 | * | ||
| 156 | * Solve the equation bPm == aPm | ||
| 157 | * | ||
| 158 | * a) For xm coord: | ||
| 159 | * x1 + 3*x2 + 3*x3 + x4 = 8*cos((ang1 + ang2)/2) | ||
| 160 | * | ||
| 161 | * cos(ang1) + 3*cos(ang1) - 3*k*sin(ang1) + | ||
| 162 | * 3*cos(ang2) + 3*k*sin(ang2) + cos(ang2) = | ||
| 163 | * = 8*cos((ang1 + ang2)/2) | ||
| 164 | * | ||
| 165 | * 4*cos(ang1) + 4*cos(ang2) + 3*k*(sin(ang2) - sin(ang1)) = | ||
| 166 | * = 8*cos((ang1 + ang2)/2) | ||
| 167 | * | ||
| 168 | * 8*cos((ang1 + ang2)/2)*cos((ang2 - ang1)/2) + | ||
| 169 | * 6*k*sin((ang2 - ang1)/2)*cos((ang1 + ang2)/2) = | ||
| 170 | * = 8*cos((ang1 + ang2)/2) | ||
| 171 | * | ||
| 172 | * 4*cos(angb) + 3*k*sin(angb) = 4 | ||
| 173 | * | ||
| 174 | * k = 4 / 3 * (1 - cos(angb)) / sin(angb) | ||
| 175 | * | ||
| 176 | * b) For ym coord we derive the same formula. | ||
| 177 | * | ||
| 178 | * Since this formula can generate "NaN" values for small | ||
| 179 | * angles, we will derive a safer form that does not involve | ||
| 180 | * dividing by very small values: | ||
| 181 | * (1 - cos(angb)) / sin(angb) = | ||
| 182 | * = (1 - cos(angb))*(1 + cos(angb)) / sin(angb)*(1 + cos(angb)) = | ||
| 183 | * = (1 - cos(angb)^2) / sin(angb)*(1 + cos(angb)) = | ||
| 184 | * = sin(angb)^2 / sin(angb)*(1 + cos(angb)) = | ||
| 185 | * = sin(angb) / (1 + cos(angb)) | ||
| 186 | * | ||
| 187 | */ | ||
| 188 | private static double btan(double increment) { | ||
| 189 | increment /= 2.0; | ||
| 190 | return 4.0 / 3.0 * Math.sin(increment) / (1.0 + Math.cos(increment)); | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Returns the coordinates and type of the current path segment in | ||
| 195 | * the iteration. | ||
| 196 | * The return value is the path segment type: | ||
| 197 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 198 | * A float array of length 6 must be passed in and may be used to | ||
| 199 | * store the coordinates of the point(s). | ||
| 200 | * Each point is stored as a pair of float x,y coordinates. | ||
| 201 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 202 | * SEG_QUADTO will return two points, | ||
| 203 | * SEG_CUBICTO will return 3 points | ||
| 204 | * and SEG_CLOSE will not return any points. | ||
| 205 | * @see #SEG_MOVETO | ||
| 206 | * @see #SEG_LINETO | ||
| 207 | * @see #SEG_QUADTO | ||
| 208 | * @see #SEG_CUBICTO | ||
| 209 | * @see #SEG_CLOSE | ||
| 210 | */ | ||
| 211 | public int currentSegment(float[] coords) { | ||
| 212 | if (isDone()) { | ||
| 213 | throw new NoSuchElementException("arc iterator out of bounds"); | ||
| 214 | } | ||
| 215 | double angle = angStRad; | ||
| 216 | if (index == 0) { | ||
| 217 | coords[0] = (float) (x + Math.cos(angle) * w); | ||
| 218 | coords[1] = (float) (y + Math.sin(angle) * h); | ||
| 219 | if (affine != null) { | ||
| 220 | affine.transform(coords, 0, coords, 0, 1); | ||
| 221 | } | ||
| 222 | return SEG_MOVETO; | ||
| 223 | } | ||
| 224 | if (index > arcSegs) { | ||
| 225 | if (index == arcSegs + lineSegs) { | ||
| 226 | return SEG_CLOSE; | ||
| 227 | } | ||
| 228 | coords[0] = (float) x; | ||
| 229 | coords[1] = (float) y; | ||
| 230 | if (affine != null) { | ||
| 231 | affine.transform(coords, 0, coords, 0, 1); | ||
| 232 | } | ||
| 233 | return SEG_LINETO; | ||
| 234 | } | ||
| 235 | angle += increment * (index - 1); | ||
| 236 | double relx = Math.cos(angle); | ||
| 237 | double rely = Math.sin(angle); | ||
| 238 | coords[0] = (float) (x + (relx - cv * rely) * w); | ||
| 239 | coords[1] = (float) (y + (rely + cv * relx) * h); | ||
| 240 | angle += increment; | ||
| 241 | relx = Math.cos(angle); | ||
| 242 | rely = Math.sin(angle); | ||
| 243 | coords[2] = (float) (x + (relx + cv * rely) * w); | ||
| 244 | coords[3] = (float) (y + (rely - cv * relx) * h); | ||
| 245 | coords[4] = (float) (x + relx * w); | ||
| 246 | coords[5] = (float) (y + rely * h); | ||
| 247 | if (affine != null) { | ||
| 248 | affine.transform(coords, 0, coords, 0, 3); | ||
| 249 | } | ||
| 250 | return SEG_CUBICTO; | ||
| 251 | } | ||
| 252 | |||
| 253 | /** | ||
| 254 | * Returns the coordinates and type of the current path segment in | ||
| 255 | * the iteration. | ||
| 256 | * The return value is the path segment type: | ||
| 257 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 258 | * A double array of length 6 must be passed in and may be used to | ||
| 259 | * store the coordinates of the point(s). | ||
| 260 | * Each point is stored as a pair of double x,y coordinates. | ||
| 261 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 262 | * SEG_QUADTO will return two points, | ||
| 263 | * SEG_CUBICTO will return 3 points | ||
| 264 | * and SEG_CLOSE will not return any points. | ||
| 265 | * @see #SEG_MOVETO | ||
| 266 | * @see #SEG_LINETO | ||
| 267 | * @see #SEG_QUADTO | ||
| 268 | * @see #SEG_CUBICTO | ||
| 269 | * @see #SEG_CLOSE | ||
| 270 | */ | ||
| 271 | public int currentSegment(double[] coords) { | ||
| 272 | if (isDone()) { | ||
| 273 | throw new NoSuchElementException("arc iterator out of bounds"); | ||
| 274 | } | ||
| 275 | double angle = angStRad; | ||
| 276 | if (index == 0) { | ||
| 277 | coords[0] = x + Math.cos(angle) * w; | ||
| 278 | coords[1] = y + Math.sin(angle) * h; | ||
| 279 | if (affine != null) { | ||
| 280 | affine.transform(coords, 0, coords, 0, 1); | ||
| 281 | } | ||
| 282 | return SEG_MOVETO; | ||
| 283 | } | ||
| 284 | if (index > arcSegs) { | ||
| 285 | if (index == arcSegs + lineSegs) { | ||
| 286 | return SEG_CLOSE; | ||
| 287 | } | ||
| 288 | coords[0] = x; | ||
| 289 | coords[1] = y; | ||
| 290 | if (affine != null) { | ||
| 291 | affine.transform(coords, 0, coords, 0, 1); | ||
| 292 | } | ||
| 293 | return SEG_LINETO; | ||
| 294 | } | ||
| 295 | angle += increment * (index - 1); | ||
| 296 | double relx = Math.cos(angle); | ||
| 297 | double rely = Math.sin(angle); | ||
| 298 | coords[0] = x + (relx - cv * rely) * w; | ||
| 299 | coords[1] = y + (rely + cv * relx) * h; | ||
| 300 | angle += increment; | ||
| 301 | relx = Math.cos(angle); | ||
| 302 | rely = Math.sin(angle); | ||
| 303 | coords[2] = x + (relx + cv * rely) * w; | ||
| 304 | coords[3] = y + (rely - cv * relx) * h; | ||
| 305 | coords[4] = x + relx * w; | ||
| 306 | coords[5] = y + rely * h; | ||
| 307 | if (affine != null) { | ||
| 308 | affine.transform(coords, 0, coords, 0, 3); | ||
| 309 | } | ||
| 310 | return SEG_CUBICTO; | ||
| 311 | } | ||
| 312 | } | ||
Binary file not shown.
| @@ -0,0 +1,744 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.awt.Rectangle; | ||
| 30 | import java.util.Vector; | ||
| 31 | import java.util.Enumeration; | ||
| 32 | import java.util.NoSuchElementException; | ||
| 33 | import sun.awt.geom.Curve; | ||
| 34 | import sun.awt.geom.Crossings; | ||
| 35 | import sun.awt.geom.AreaOp; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * An <code>Area</code> object stores and manipulates a | ||
| 39 | * resolution-independent description of an enclosed area of | ||
| 40 | * 2-dimensional space. | ||
| 41 | * <code>Area</code> objects can be transformed and can perform | ||
| 42 | * various Constructive Area Geometry (CAG) operations when combined | ||
| 43 | * with other <code>Area</code> objects. | ||
| 44 | * The CAG operations include area | ||
| 45 | * {@link #add addition}, {@link #subtract subtraction}, | ||
| 46 | * {@link #intersect intersection}, and {@link #exclusiveOr exclusive or}. | ||
| 47 | * See the linked method documentation for examples of the various | ||
| 48 | * operations. | ||
| 49 | * <p> | ||
| 50 | * The <code>Area</code> class implements the <code>Shape</code> | ||
| 51 | * interface and provides full support for all of its hit-testing | ||
| 52 | * and path iteration facilities, but an <code>Area</code> is more | ||
| 53 | * specific than a generalized path in a number of ways: | ||
| 54 | * <ul> | ||
| 55 | * <li>Only closed paths and sub-paths are stored. | ||
| 56 | * <code>Area</code> objects constructed from unclosed paths | ||
| 57 | * are implicitly closed during construction as if those paths | ||
| 58 | * had been filled by the <code>Graphics2D.fill</code> method. | ||
| 59 | * <li>The interiors of the individual stored sub-paths are all | ||
| 60 | * non-empty and non-overlapping. Paths are decomposed during | ||
| 61 | * construction into separate component non-overlapping parts, | ||
| 62 | * empty pieces of the path are discarded, and then these | ||
| 63 | * non-empty and non-overlapping properties are maintained | ||
| 64 | * through all subsequent CAG operations. Outlines of different | ||
| 65 | * component sub-paths may touch each other, as long as they | ||
| 66 | * do not cross so that their enclosed areas overlap. | ||
| 67 | * <li>The geometry of the path describing the outline of the | ||
| 68 | * <code>Area</code> resembles the path from which it was | ||
| 69 | * constructed only in that it describes the same enclosed | ||
| 70 | * 2-dimensional area, but may use entirely different types | ||
| 71 | * and ordering of the path segments to do so. | ||
| 72 | * </ul> | ||
| 73 | * Interesting issues which are not always obvious when using | ||
| 74 | * the <code>Area</code> include: | ||
| 75 | * <ul> | ||
| 76 | * <li>Creating an <code>Area</code> from an unclosed (open) | ||
| 77 | * <code>Shape</code> results in a closed outline in the | ||
| 78 | * <code>Area</code> object. | ||
| 79 | * <li>Creating an <code>Area</code> from a <code>Shape</code> | ||
| 80 | * which encloses no area (even when "closed") produces an | ||
| 81 | * empty <code>Area</code>. A common example of this issue | ||
| 82 | * is that producing an <code>Area</code> from a line will | ||
| 83 | * be empty since the line encloses no area. An empty | ||
| 84 | * <code>Area</code> will iterate no geometry in its | ||
| 85 | * <code>PathIterator</code> objects. | ||
| 86 | * <li>A self-intersecting <code>Shape</code> may be split into | ||
| 87 | * two (or more) sub-paths each enclosing one of the | ||
| 88 | * non-intersecting portions of the original path. | ||
| 89 | * <li>An <code>Area</code> may take more path segments to | ||
| 90 | * describe the same geometry even when the original | ||
| 91 | * outline is simple and obvious. The analysis that the | ||
| 92 | * <code>Area</code> class must perform on the path may | ||
| 93 | * not reflect the same concepts of "simple and obvious" | ||
| 94 | * as a human being perceives. | ||
| 95 | * </ul> | ||
| 96 | * | ||
| 97 | * @since 1.2 | ||
| 98 | */ | ||
| 99 | public class Area implements Shape, Cloneable { | ||
| 100 | private static Vector EmptyCurves = new Vector(); | ||
| 101 | |||
| 102 | private Vector curves; | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Default constructor which creates an empty area. | ||
| 106 | * @since 1.2 | ||
| 107 | */ | ||
| 108 | public Area() { | ||
| 109 | curves = EmptyCurves; | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * The <code>Area</code> class creates an area geometry from the | ||
| 114 | * specified {@link Shape} object. The geometry is explicitly | ||
| 115 | * closed, if the <code>Shape</code> is not already closed. The | ||
| 116 | * fill rule (even-odd or winding) specified by the geometry of the | ||
| 117 | * <code>Shape</code> is used to determine the resulting enclosed area. | ||
| 118 | * @param s the <code>Shape</code> from which the area is constructed | ||
| 119 | * @throws NullPointerException if <code>s</code> is null | ||
| 120 | * @since 1.2 | ||
| 121 | */ | ||
| 122 | public Area(Shape s) { | ||
| 123 | if (s instanceof Area) { | ||
| 124 | curves = ((Area) s).curves; | ||
| 125 | } else { | ||
| 126 | curves = pathToCurves(s.getPathIterator(null)); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | private static Vector pathToCurves(PathIterator pi) { | ||
| 131 | Vector curves = new Vector(); | ||
| 132 | int windingRule = pi.getWindingRule(); | ||
| 133 | // coords array is big enough for holding: | ||
| 134 | // coordinates returned from currentSegment (6) | ||
| 135 | // OR | ||
| 136 | // two subdivided quadratic curves (2+4+4=10) | ||
| 137 | // AND | ||
| 138 | // 0-1 horizontal splitting parameters | ||
| 139 | // OR | ||
| 140 | // 2 parametric equation derivative coefficients | ||
| 141 | // OR | ||
| 142 | // three subdivided cubic curves (2+6+6+6=20) | ||
| 143 | // AND | ||
| 144 | // 0-2 horizontal splitting parameters | ||
| 145 | // OR | ||
| 146 | // 3 parametric equation derivative coefficients | ||
| 147 | double coords[] = new double[23]; | ||
| 148 | double movx = 0, movy = 0; | ||
| 149 | double curx = 0, cury = 0; | ||
| 150 | double newx, newy; | ||
| 151 | while (!pi.isDone()) { | ||
| 152 | switch (pi.currentSegment(coords)) { | ||
| 153 | case PathIterator.SEG_MOVETO: | ||
| 154 | Curve.insertLine(curves, curx, cury, movx, movy); | ||
| 155 | curx = movx = coords[0]; | ||
| 156 | cury = movy = coords[1]; | ||
| 157 | Curve.insertMove(curves, movx, movy); | ||
| 158 | break; | ||
| 159 | case PathIterator.SEG_LINETO: | ||
| 160 | newx = coords[0]; | ||
| 161 | newy = coords[1]; | ||
| 162 | Curve.insertLine(curves, curx, cury, newx, newy); | ||
| 163 | curx = newx; | ||
| 164 | cury = newy; | ||
| 165 | break; | ||
| 166 | case PathIterator.SEG_QUADTO: | ||
| 167 | newx = coords[2]; | ||
| 168 | newy = coords[3]; | ||
| 169 | Curve.insertQuad(curves, curx, cury, coords); | ||
| 170 | curx = newx; | ||
| 171 | cury = newy; | ||
| 172 | break; | ||
| 173 | case PathIterator.SEG_CUBICTO: | ||
| 174 | newx = coords[4]; | ||
| 175 | newy = coords[5]; | ||
| 176 | Curve.insertCubic(curves, curx, cury, coords); | ||
| 177 | curx = newx; | ||
| 178 | cury = newy; | ||
| 179 | break; | ||
| 180 | case PathIterator.SEG_CLOSE: | ||
| 181 | Curve.insertLine(curves, curx, cury, movx, movy); | ||
| 182 | curx = movx; | ||
| 183 | cury = movy; | ||
| 184 | break; | ||
| 185 | } | ||
| 186 | pi.next(); | ||
| 187 | } | ||
| 188 | Curve.insertLine(curves, curx, cury, movx, movy); | ||
| 189 | AreaOp operator; | ||
| 190 | if (windingRule == PathIterator.WIND_EVEN_ODD) { | ||
| 191 | operator = new AreaOp.EOWindOp(); | ||
| 192 | } else { | ||
| 193 | operator = new AreaOp.NZWindOp(); | ||
| 194 | } | ||
| 195 | return operator.calculate(curves, EmptyCurves); | ||
| 196 | } | ||
| 197 | |||
| 198 | /** | ||
| 199 | * Adds the shape of the specified <code>Area</code> to the | ||
| 200 | * shape of this <code>Area</code>. | ||
| 201 | * The resulting shape of this <code>Area</code> will include | ||
| 202 | * the union of both shapes, or all areas that were contained | ||
| 203 | * in either this or the specified <code>Area</code>. | ||
| 204 | * <pre> | ||
| 205 | * // Example: | ||
| 206 | * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]); | ||
| 207 | * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]); | ||
| 208 | * a1.add(a2); | ||
| 209 | * | ||
| 210 | * a1(before) + a2 = a1(after) | ||
| 211 | * | ||
| 212 | * ################ ################ ################ | ||
| 213 | * ############## ############## ################ | ||
| 214 | * ############ ############ ################ | ||
| 215 | * ########## ########## ################ | ||
| 216 | * ######## ######## ################ | ||
| 217 | * ###### ###### ###### ###### | ||
| 218 | * #### #### #### #### | ||
| 219 | * ## ## ## ## | ||
| 220 | * </pre> | ||
| 221 | * @param rhs the <code>Area</code> to be added to the | ||
| 222 | * current shape | ||
| 223 | * @throws NullPointerException if <code>rhs</code> is null | ||
| 224 | * @since 1.2 | ||
| 225 | */ | ||
| 226 | public void add(Area rhs) { | ||
| 227 | curves = new AreaOp.AddOp().calculate(this.curves, rhs.curves); | ||
| 228 | invalidateBounds(); | ||
| 229 | } | ||
| 230 | |||
| 231 | /** | ||
| 232 | * Subtracts the shape of the specified <code>Area</code> from the | ||
| 233 | * shape of this <code>Area</code>. | ||
| 234 | * The resulting shape of this <code>Area</code> will include | ||
| 235 | * areas that were contained only in this <code>Area</code> | ||
| 236 | * and not in the specified <code>Area</code>. | ||
| 237 | * <pre> | ||
| 238 | * // Example: | ||
| 239 | * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]); | ||
| 240 | * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]); | ||
| 241 | * a1.subtract(a2); | ||
| 242 | * | ||
| 243 | * a1(before) - a2 = a1(after) | ||
| 244 | * | ||
| 245 | * ################ ################ | ||
| 246 | * ############## ############## ## | ||
| 247 | * ############ ############ #### | ||
| 248 | * ########## ########## ###### | ||
| 249 | * ######## ######## ######## | ||
| 250 | * ###### ###### ###### | ||
| 251 | * #### #### #### | ||
| 252 | * ## ## ## | ||
| 253 | * </pre> | ||
| 254 | * @param rhs the <code>Area</code> to be subtracted from the | ||
| 255 | * current shape | ||
| 256 | * @throws NullPointerException if <code>rhs</code> is null | ||
| 257 | * @since 1.2 | ||
| 258 | */ | ||
| 259 | public void subtract(Area rhs) { | ||
| 260 | curves = new AreaOp.SubOp().calculate(this.curves, rhs.curves); | ||
| 261 | invalidateBounds(); | ||
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * Sets the shape of this <code>Area</code> to the intersection of | ||
| 266 | * its current shape and the shape of the specified <code>Area</code>. | ||
| 267 | * The resulting shape of this <code>Area</code> will include | ||
| 268 | * only areas that were contained in both this <code>Area</code> | ||
| 269 | * and also in the specified <code>Area</code>. | ||
| 270 | * <pre> | ||
| 271 | * // Example: | ||
| 272 | * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]); | ||
| 273 | * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]); | ||
| 274 | * a1.intersect(a2); | ||
| 275 | * | ||
| 276 | * a1(before) intersect a2 = a1(after) | ||
| 277 | * | ||
| 278 | * ################ ################ ################ | ||
| 279 | * ############## ############## ############ | ||
| 280 | * ############ ############ ######## | ||
| 281 | * ########## ########## #### | ||
| 282 | * ######## ######## | ||
| 283 | * ###### ###### | ||
| 284 | * #### #### | ||
| 285 | * ## ## | ||
| 286 | * </pre> | ||
| 287 | * @param rhs the <code>Area</code> to be intersected with this | ||
| 288 | * <code>Area</code> | ||
| 289 | * @throws NullPointerException if <code>rhs</code> is null | ||
| 290 | * @since 1.2 | ||
| 291 | */ | ||
| 292 | public void intersect(Area rhs) { | ||
| 293 | curves = new AreaOp.IntOp().calculate(this.curves, rhs.curves); | ||
| 294 | invalidateBounds(); | ||
| 295 | } | ||
| 296 | |||
| 297 | /** | ||
| 298 | * Sets the shape of this <code>Area</code> to be the combined area | ||
| 299 | * of its current shape and the shape of the specified <code>Area</code>, | ||
| 300 | * minus their intersection. | ||
| 301 | * The resulting shape of this <code>Area</code> will include | ||
| 302 | * only areas that were contained in either this <code>Area</code> | ||
| 303 | * or in the specified <code>Area</code>, but not in both. | ||
| 304 | * <pre> | ||
| 305 | * // Example: | ||
| 306 | * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]); | ||
| 307 | * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]); | ||
| 308 | * a1.exclusiveOr(a2); | ||
| 309 | * | ||
| 310 | * a1(before) xor a2 = a1(after) | ||
| 311 | * | ||
| 312 | * ################ ################ | ||
| 313 | * ############## ############## ## ## | ||
| 314 | * ############ ############ #### #### | ||
| 315 | * ########## ########## ###### ###### | ||
| 316 | * ######## ######## ################ | ||
| 317 | * ###### ###### ###### ###### | ||
| 318 | * #### #### #### #### | ||
| 319 | * ## ## ## ## | ||
| 320 | * </pre> | ||
| 321 | * @param rhs the <code>Area</code> to be exclusive ORed with this | ||
| 322 | * <code>Area</code>. | ||
| 323 | * @throws NullPointerException if <code>rhs</code> is null | ||
| 324 | * @since 1.2 | ||
| 325 | */ | ||
| 326 | public void exclusiveOr(Area rhs) { | ||
| 327 | curves = new AreaOp.XorOp().calculate(this.curves, rhs.curves); | ||
| 328 | invalidateBounds(); | ||
| 329 | } | ||
| 330 | |||
| 331 | /** | ||
| 332 | * Removes all of the geometry from this <code>Area</code> and | ||
| 333 | * restores it to an empty area. | ||
| 334 | * @since 1.2 | ||
| 335 | */ | ||
| 336 | public void reset() { | ||
| 337 | curves = new Vector(); | ||
| 338 | invalidateBounds(); | ||
| 339 | } | ||
| 340 | |||
| 341 | /** | ||
| 342 | * Tests whether this <code>Area</code> object encloses any area. | ||
| 343 | * @return <code>true</code> if this <code>Area</code> object | ||
| 344 | * represents an empty area; <code>false</code> otherwise. | ||
| 345 | * @since 1.2 | ||
| 346 | */ | ||
| 347 | public boolean isEmpty() { | ||
| 348 | return (curves.size() == 0); | ||
| 349 | } | ||
| 350 | |||
| 351 | /** | ||
| 352 | * Tests whether this <code>Area</code> consists entirely of | ||
| 353 | * straight edged polygonal geometry. | ||
| 354 | * @return <code>true</code> if the geometry of this | ||
| 355 | * <code>Area</code> consists entirely of line segments; | ||
| 356 | * <code>false</code> otherwise. | ||
| 357 | * @since 1.2 | ||
| 358 | */ | ||
| 359 | public boolean isPolygonal() { | ||
| 360 | Enumeration enum_ = curves.elements(); | ||
| 361 | while (enum_.hasMoreElements()) { | ||
| 362 | if (((Curve) enum_.nextElement()).getOrder() > 1) { | ||
| 363 | return false; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | return true; | ||
| 367 | } | ||
| 368 | |||
| 369 | /** | ||
| 370 | * Tests whether this <code>Area</code> is rectangular in shape. | ||
| 371 | * @return <code>true</code> if the geometry of this | ||
| 372 | * <code>Area</code> is rectangular in shape; <code>false</code> | ||
| 373 | * otherwise. | ||
| 374 | * @since 1.2 | ||
| 375 | */ | ||
| 376 | public boolean isRectangular() { | ||
| 377 | int size = curves.size(); | ||
| 378 | if (size == 0) { | ||
| 379 | return true; | ||
| 380 | } | ||
| 381 | if (size > 3) { | ||
| 382 | return false; | ||
| 383 | } | ||
| 384 | Curve c1 = (Curve) curves.get(1); | ||
| 385 | Curve c2 = (Curve) curves.get(2); | ||
| 386 | if (c1.getOrder() != 1 || c2.getOrder() != 1) { | ||
| 387 | return false; | ||
| 388 | } | ||
| 389 | if (c1.getXTop() != c1.getXBot() || c2.getXTop() != c2.getXBot()) { | ||
| 390 | return false; | ||
| 391 | } | ||
| 392 | if (c1.getYTop() != c2.getYTop() || c1.getYBot() != c2.getYBot()) { | ||
| 393 | // One might be able to prove that this is impossible... | ||
| 394 | return false; | ||
| 395 | } | ||
| 396 | return true; | ||
| 397 | } | ||
| 398 | |||
| 399 | /** | ||
| 400 | * Tests whether this <code>Area</code> is comprised of a single | ||
| 401 | * closed subpath. This method returns <code>true</code> if the | ||
| 402 | * path contains 0 or 1 subpaths, or <code>false</code> if the path | ||
| 403 | * contains more than 1 subpath. The subpaths are counted by the | ||
| 404 | * number of {@link PathIterator#SEG_MOVETO SEG_MOVETO} segments | ||
| 405 | * that appear in the path. | ||
| 406 | * @return <code>true</code> if the <code>Area</code> is comprised | ||
| 407 | * of a single basic geometry; <code>false</code> otherwise. | ||
| 408 | * @since 1.2 | ||
| 409 | */ | ||
| 410 | public boolean isSingular() { | ||
| 411 | if (curves.size() < 3) { | ||
| 412 | return true; | ||
| 413 | } | ||
| 414 | Enumeration enum_ = curves.elements(); | ||
| 415 | enum_.nextElement(); // First Order0 "moveto" | ||
| 416 | while (enum_.hasMoreElements()) { | ||
| 417 | if (((Curve) enum_.nextElement()).getOrder() == 0) { | ||
| 418 | return false; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | return true; | ||
| 422 | } | ||
| 423 | |||
| 424 | private Rectangle2D cachedBounds; | ||
| 425 | private void invalidateBounds() { | ||
| 426 | cachedBounds = null; | ||
| 427 | } | ||
| 428 | private Rectangle2D getCachedBounds() { | ||
| 429 | if (cachedBounds != null) { | ||
| 430 | return cachedBounds; | ||
| 431 | } | ||
| 432 | Rectangle2D r = new Rectangle2D.Double(); | ||
| 433 | if (curves.size() > 0) { | ||
| 434 | Curve c = (Curve) curves.get(0); | ||
| 435 | // First point is always an order 0 curve (moveto) | ||
| 436 | r.setRect(c.getX0(), c.getY0(), 0, 0); | ||
| 437 | for (int i = 1; i < curves.size(); i++) { | ||
| 438 | ((Curve) curves.get(i)).enlarge(r); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | return (cachedBounds = r); | ||
| 442 | } | ||
| 443 | |||
| 444 | /** | ||
| 445 | * Returns a high precision bounding {@link Rectangle2D} that | ||
| 446 | * completely encloses this <code>Area</code>. | ||
| 447 | * <p> | ||
| 448 | * The Area class will attempt to return the tightest bounding | ||
| 449 | * box possible for the Shape. The bounding box will not be | ||
| 450 | * padded to include the control points of curves in the outline | ||
| 451 | * of the Shape, but should tightly fit the actual geometry of | ||
| 452 | * the outline itself. | ||
| 453 | * @return the bounding <code>Rectangle2D</code> for the | ||
| 454 | * <code>Area</code>. | ||
| 455 | * @since 1.2 | ||
| 456 | */ | ||
| 457 | public Rectangle2D getBounds2D() { | ||
| 458 | return getCachedBounds().getBounds2D(); | ||
| 459 | } | ||
| 460 | |||
| 461 | /** | ||
| 462 | * Returns a bounding {@link Rectangle} that completely encloses | ||
| 463 | * this <code>Area</code>. | ||
| 464 | * <p> | ||
| 465 | * The Area class will attempt to return the tightest bounding | ||
| 466 | * box possible for the Shape. The bounding box will not be | ||
| 467 | * padded to include the control points of curves in the outline | ||
| 468 | * of the Shape, but should tightly fit the actual geometry of | ||
| 469 | * the outline itself. Since the returned object represents | ||
| 470 | * the bounding box with integers, the bounding box can only be | ||
| 471 | * as tight as the nearest integer coordinates that encompass | ||
| 472 | * the geometry of the Shape. | ||
| 473 | * @return the bounding <code>Rectangle</code> for the | ||
| 474 | * <code>Area</code>. | ||
| 475 | * @since 1.2 | ||
| 476 | */ | ||
| 477 | public Rectangle getBounds() { | ||
| 478 | return getCachedBounds().getBounds(); | ||
| 479 | } | ||
| 480 | |||
| 481 | /** | ||
| 482 | * Returns an exact copy of this <code>Area</code> object. | ||
| 483 | * @return Created clone object | ||
| 484 | * @since 1.2 | ||
| 485 | */ | ||
| 486 | public Object clone() { | ||
| 487 | return new Area(this); | ||
| 488 | } | ||
| 489 | |||
| 490 | /** | ||
| 491 | * Tests whether the geometries of the two <code>Area</code> objects | ||
| 492 | * are equal. | ||
| 493 | * This method will return false if the argument is null. | ||
| 494 | * @param other the <code>Area</code> to be compared to this | ||
| 495 | * <code>Area</code> | ||
| 496 | * @return <code>true</code> if the two geometries are equal; | ||
| 497 | * <code>false</code> otherwise. | ||
| 498 | * @since 1.2 | ||
| 499 | */ | ||
| 500 | public boolean equals(Area other) { | ||
| 501 | // REMIND: A *much* simpler operation should be possible... | ||
| 502 | // Should be able to do a curve-wise comparison since all Areas | ||
| 503 | // should evaluate their curves in the same top-down order. | ||
| 504 | if (other == this) { | ||
| 505 | return true; | ||
| 506 | } | ||
| 507 | if (other == null) { | ||
| 508 | return false; | ||
| 509 | } | ||
| 510 | Vector c = new AreaOp.XorOp().calculate(this.curves, other.curves); | ||
| 511 | return c.isEmpty(); | ||
| 512 | } | ||
| 513 | |||
| 514 | /** | ||
| 515 | * Transforms the geometry of this <code>Area</code> using the specified | ||
| 516 | * {@link AffineTransform}. The geometry is transformed in place, which | ||
| 517 | * permanently changes the enclosed area defined by this object. | ||
| 518 | * @param t the transformation used to transform the area | ||
| 519 | * @throws NullPointerException if <code>t</code> is null | ||
| 520 | * @since 1.2 | ||
| 521 | */ | ||
| 522 | public void transform(AffineTransform t) { | ||
| 523 | if (t == null) { | ||
| 524 | throw new NullPointerException("transform must not be null"); | ||
| 525 | } | ||
| 526 | // REMIND: A simpler operation can be performed for some types | ||
| 527 | // of transform. | ||
| 528 | curves = pathToCurves(getPathIterator(t)); | ||
| 529 | invalidateBounds(); | ||
| 530 | } | ||
| 531 | |||
| 532 | /** | ||
| 533 | * Creates a new <code>Area</code> object that contains the same | ||
| 534 | * geometry as this <code>Area</code> transformed by the specified | ||
| 535 | * <code>AffineTransform</code>. This <code>Area</code> object | ||
| 536 | * is unchanged. | ||
| 537 | * @param t the specified <code>AffineTransform</code> used to transform | ||
| 538 | * the new <code>Area</code> | ||
| 539 | * @throws NullPointerException if <code>t</code> is null | ||
| 540 | * @return a new <code>Area</code> object representing the transformed | ||
| 541 | * geometry. | ||
| 542 | * @since 1.2 | ||
| 543 | */ | ||
| 544 | public Area createTransformedArea(AffineTransform t) { | ||
| 545 | Area a = new Area(this); | ||
| 546 | a.transform(t); | ||
| 547 | return a; | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * {@inheritDoc} | ||
| 552 | * @since 1.2 | ||
| 553 | */ | ||
| 554 | public boolean contains(double x, double y) { | ||
| 555 | if (!getCachedBounds().contains(x, y)) { | ||
| 556 | return false; | ||
| 557 | } | ||
| 558 | Enumeration enum_ = curves.elements(); | ||
| 559 | int crossings = 0; | ||
| 560 | while (enum_.hasMoreElements()) { | ||
| 561 | Curve c = (Curve) enum_.nextElement(); | ||
| 562 | crossings += c.crossingsFor(x, y); | ||
| 563 | } | ||
| 564 | return ((crossings & 1) == 1); | ||
| 565 | } | ||
| 566 | |||
| 567 | /** | ||
| 568 | * {@inheritDoc} | ||
| 569 | * @since 1.2 | ||
| 570 | */ | ||
| 571 | public boolean contains(Point2D p) { | ||
| 572 | return contains(p.getX(), p.getY()); | ||
| 573 | } | ||
| 574 | |||
| 575 | /** | ||
| 576 | * {@inheritDoc} | ||
| 577 | * @since 1.2 | ||
| 578 | */ | ||
| 579 | public boolean contains(double x, double y, double w, double h) { | ||
| 580 | if (w < 0 || h < 0) { | ||
| 581 | return false; | ||
| 582 | } | ||
| 583 | if (!getCachedBounds().contains(x, y, w, h)) { | ||
| 584 | return false; | ||
| 585 | } | ||
| 586 | Crossings c = Crossings.findCrossings(curves, x, y, x+w, y+h); | ||
| 587 | return (c != null && c.covers(y, y+h)); | ||
| 588 | } | ||
| 589 | |||
| 590 | /** | ||
| 591 | * {@inheritDoc} | ||
| 592 | * @since 1.2 | ||
| 593 | */ | ||
| 594 | public boolean contains(Rectangle2D r) { | ||
| 595 | return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 596 | } | ||
| 597 | |||
| 598 | /** | ||
| 599 | * {@inheritDoc} | ||
| 600 | * @since 1.2 | ||
| 601 | */ | ||
| 602 | public boolean intersects(double x, double y, double w, double h) { | ||
| 603 | if (w < 0 || h < 0) { | ||
| 604 | return false; | ||
| 605 | } | ||
| 606 | if (!getCachedBounds().intersects(x, y, w, h)) { | ||
| 607 | return false; | ||
| 608 | } | ||
| 609 | Crossings c = Crossings.findCrossings(curves, x, y, x+w, y+h); | ||
| 610 | return (c == null || !c.isEmpty()); | ||
| 611 | } | ||
| 612 | |||
| 613 | /** | ||
| 614 | * {@inheritDoc} | ||
| 615 | * @since 1.2 | ||
| 616 | */ | ||
| 617 | public boolean intersects(Rectangle2D r) { | ||
| 618 | return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 619 | } | ||
| 620 | |||
| 621 | /** | ||
| 622 | * Creates a {@link PathIterator} for the outline of this | ||
| 623 | * <code>Area</code> object. This <code>Area</code> object is unchanged. | ||
| 624 | * @param at an optional <code>AffineTransform</code> to be applied to | ||
| 625 | * the coordinates as they are returned in the iteration, or | ||
| 626 | * <code>null</code> if untransformed coordinates are desired | ||
| 627 | * @return the <code>PathIterator</code> object that returns the | ||
| 628 | * geometry of the outline of this <code>Area</code>, one | ||
| 629 | * segment at a time. | ||
| 630 | * @since 1.2 | ||
| 631 | */ | ||
| 632 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 633 | return new AreaIterator(curves, at); | ||
| 634 | } | ||
| 635 | |||
| 636 | /** | ||
| 637 | * Creates a <code>PathIterator</code> for the flattened outline of | ||
| 638 | * this <code>Area</code> object. Only uncurved path segments | ||
| 639 | * represented by the SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point | ||
| 640 | * types are returned by the iterator. This <code>Area</code> | ||
| 641 | * object is unchanged. | ||
| 642 | * @param at an optional <code>AffineTransform</code> to be | ||
| 643 | * applied to the coordinates as they are returned in the | ||
| 644 | * iteration, or <code>null</code> if untransformed coordinates | ||
| 645 | * are desired | ||
| 646 | * @param flatness the maximum amount that the control points | ||
| 647 | * for a given curve can vary from colinear before a subdivided | ||
| 648 | * curve is replaced by a straight line connecting the end points | ||
| 649 | * @return the <code>PathIterator</code> object that returns the | ||
| 650 | * geometry of the outline of this <code>Area</code>, one segment | ||
| 651 | * at a time. | ||
| 652 | * @since 1.2 | ||
| 653 | */ | ||
| 654 | public PathIterator getPathIterator(AffineTransform at, double flatness) { | ||
| 655 | return new FlatteningPathIterator(getPathIterator(at), flatness); | ||
| 656 | } | ||
| 657 | } | ||
| 658 | |||
| 659 | class AreaIterator implements PathIterator { | ||
| 660 | private AffineTransform transform; | ||
| 661 | private Vector curves; | ||
| 662 | private int index; | ||
| 663 | private Curve prevcurve; | ||
| 664 | private Curve thiscurve; | ||
| 665 | |||
| 666 | public AreaIterator(Vector curves, AffineTransform at) { | ||
| 667 | this.curves = curves; | ||
| 668 | this.transform = at; | ||
| 669 | if (curves.size() >= 1) { | ||
| 670 | thiscurve = (Curve) curves.get(0); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | public int getWindingRule() { | ||
| 675 | // REMIND: Which is better, EVEN_ODD or NON_ZERO? | ||
| 676 | // The paths calculated could be classified either way. | ||
| 677 | //return WIND_EVEN_ODD; | ||
| 678 | return WIND_NON_ZERO; | ||
| 679 | } | ||
| 680 | |||
| 681 | public boolean isDone() { | ||
| 682 | return (prevcurve == null && thiscurve == null); | ||
| 683 | } | ||
| 684 | |||
| 685 | public void next() { | ||
| 686 | if (prevcurve != null) { | ||
| 687 | prevcurve = null; | ||
| 688 | } else { | ||
| 689 | prevcurve = thiscurve; | ||
| 690 | index++; | ||
| 691 | if (index < curves.size()) { | ||
| 692 | thiscurve = (Curve) curves.get(index); | ||
| 693 | if (thiscurve.getOrder() != 0 && | ||
| 694 | prevcurve.getX1() == thiscurve.getX0() && | ||
| 695 | prevcurve.getY1() == thiscurve.getY0()) | ||
| 696 | { | ||
| 697 | prevcurve = null; | ||
| 698 | } | ||
| 699 | } else { | ||
| 700 | thiscurve = null; | ||
| 701 | } | ||
| 702 | } | ||
| 703 | } | ||
| 704 | |||
| 705 | public int currentSegment(float coords[]) { | ||
| 706 | double dcoords[] = new double[6]; | ||
| 707 | int segtype = currentSegment(dcoords); | ||
| 708 | int numpoints = (segtype == SEG_CLOSE ? 0 | ||
| 709 | : (segtype == SEG_QUADTO ? 2 | ||
| 710 | : (segtype == SEG_CUBICTO ? 3 | ||
| 711 | : 1))); | ||
| 712 | for (int i = 0; i < numpoints * 2; i++) { | ||
| 713 | coords[i] = (float) dcoords[i]; | ||
| 714 | } | ||
| 715 | return segtype; | ||
| 716 | } | ||
| 717 | |||
| 718 | public int currentSegment(double coords[]) { | ||
| 719 | int segtype; | ||
| 720 | int numpoints; | ||
| 721 | if (prevcurve != null) { | ||
| 722 | // Need to finish off junction between curves | ||
| 723 | if (thiscurve == null || thiscurve.getOrder() == 0) { | ||
| 724 | return SEG_CLOSE; | ||
| 725 | } | ||
| 726 | coords[0] = thiscurve.getX0(); | ||
| 727 | coords[1] = thiscurve.getY0(); | ||
| 728 | segtype = SEG_LINETO; | ||
| 729 | numpoints = 1; | ||
| 730 | } else if (thiscurve == null) { | ||
| 731 | throw new NoSuchElementException("area iterator out of bounds"); | ||
| 732 | } else { | ||
| 733 | segtype = thiscurve.getSegment(coords); | ||
| 734 | numpoints = thiscurve.getOrder(); | ||
| 735 | if (numpoints == 0) { | ||
| 736 | numpoints = 1; | ||
| 737 | } | ||
| 738 | } | ||
| 739 | if (transform != null) { | ||
| 740 | transform.transform(coords, 0, coords, 0, numpoints); | ||
| 741 | } | ||
| 742 | return segtype; | ||
| 743 | } | ||
| 744 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,1575 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.awt.Rectangle; | ||
| 30 | import java.util.Arrays; | ||
| 31 | import java.io.Serializable; | ||
| 32 | import sun.awt.geom.Curve; | ||
| 33 | |||
| 34 | import static java.lang.Math.abs; | ||
| 35 | import static java.lang.Math.max; | ||
| 36 | import static java.lang.Math.ulp; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * The <code>CubicCurve2D</code> class defines a cubic parametric curve | ||
| 40 | * segment in {@code (x,y)} coordinate space. | ||
| 41 | * <p> | ||
| 42 | * This class is only the abstract superclass for all objects which | ||
| 43 | * store a 2D cubic curve segment. | ||
| 44 | * The actual storage representation of the coordinates is left to | ||
| 45 | * the subclass. | ||
| 46 | * | ||
| 47 | * @author Jim Graham | ||
| 48 | * @since 1.2 | ||
| 49 | */ | ||
| 50 | public abstract class CubicCurve2D implements Shape, Cloneable { | ||
| 51 | |||
| 52 | /** | ||
| 53 | * A cubic parametric curve segment specified with | ||
| 54 | * {@code float} coordinates. | ||
| 55 | * @since 1.2 | ||
| 56 | */ | ||
| 57 | public static class Float extends CubicCurve2D implements Serializable { | ||
| 58 | /** | ||
| 59 | * The X coordinate of the start point | ||
| 60 | * of the cubic curve segment. | ||
| 61 | * @since 1.2 | ||
| 62 | * @serial | ||
| 63 | */ | ||
| 64 | public float x1; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * The Y coordinate of the start point | ||
| 68 | * of the cubic curve segment. | ||
| 69 | * @since 1.2 | ||
| 70 | * @serial | ||
| 71 | */ | ||
| 72 | public float y1; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * The X coordinate of the first control point | ||
| 76 | * of the cubic curve segment. | ||
| 77 | * @since 1.2 | ||
| 78 | * @serial | ||
| 79 | */ | ||
| 80 | public float ctrlx1; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * The Y coordinate of the first control point | ||
| 84 | * of the cubic curve segment. | ||
| 85 | * @since 1.2 | ||
| 86 | * @serial | ||
| 87 | */ | ||
| 88 | public float ctrly1; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * The X coordinate of the second control point | ||
| 92 | * of the cubic curve segment. | ||
| 93 | * @since 1.2 | ||
| 94 | * @serial | ||
| 95 | */ | ||
| 96 | public float ctrlx2; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * The Y coordinate of the second control point | ||
| 100 | * of the cubic curve segment. | ||
| 101 | * @since 1.2 | ||
| 102 | * @serial | ||
| 103 | */ | ||
| 104 | public float ctrly2; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * The X coordinate of the end point | ||
| 108 | * of the cubic curve segment. | ||
| 109 | * @since 1.2 | ||
| 110 | * @serial | ||
| 111 | */ | ||
| 112 | public float x2; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * The Y coordinate of the end point | ||
| 116 | * of the cubic curve segment. | ||
| 117 | * @since 1.2 | ||
| 118 | * @serial | ||
| 119 | */ | ||
| 120 | public float y2; | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Constructs and initializes a CubicCurve with coordinates | ||
| 124 | * (0, 0, 0, 0, 0, 0, 0, 0). | ||
| 125 | * @since 1.2 | ||
| 126 | */ | ||
| 127 | public Float() { | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * Constructs and initializes a {@code CubicCurve2D} from | ||
| 132 | * the specified {@code float} coordinates. | ||
| 133 | * | ||
| 134 | * @param x1 the X coordinate for the start point | ||
| 135 | * of the resulting {@code CubicCurve2D} | ||
| 136 | * @param y1 the Y coordinate for the start point | ||
| 137 | * of the resulting {@code CubicCurve2D} | ||
| 138 | * @param ctrlx1 the X coordinate for the first control point | ||
| 139 | * of the resulting {@code CubicCurve2D} | ||
| 140 | * @param ctrly1 the Y coordinate for the first control point | ||
| 141 | * of the resulting {@code CubicCurve2D} | ||
| 142 | * @param ctrlx2 the X coordinate for the second control point | ||
| 143 | * of the resulting {@code CubicCurve2D} | ||
| 144 | * @param ctrly2 the Y coordinate for the second control point | ||
| 145 | * of the resulting {@code CubicCurve2D} | ||
| 146 | * @param x2 the X coordinate for the end point | ||
| 147 | * of the resulting {@code CubicCurve2D} | ||
| 148 | * @param y2 the Y coordinate for the end point | ||
| 149 | * of the resulting {@code CubicCurve2D} | ||
| 150 | * @since 1.2 | ||
| 151 | */ | ||
| 152 | public Float(float x1, float y1, | ||
| 153 | float ctrlx1, float ctrly1, | ||
| 154 | float ctrlx2, float ctrly2, | ||
| 155 | float x2, float y2) | ||
| 156 | { | ||
| 157 | setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * {@inheritDoc} | ||
| 162 | * @since 1.2 | ||
| 163 | */ | ||
| 164 | public double getX1() { | ||
| 165 | return (double) x1; | ||
| 166 | } | ||
| 167 | |||
| 168 | /** | ||
| 169 | * {@inheritDoc} | ||
| 170 | * @since 1.2 | ||
| 171 | */ | ||
| 172 | public double getY1() { | ||
| 173 | return (double) y1; | ||
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * {@inheritDoc} | ||
| 178 | * @since 1.2 | ||
| 179 | */ | ||
| 180 | public Point2D getP1() { | ||
| 181 | return new Point2D.Float(x1, y1); | ||
| 182 | } | ||
| 183 | |||
| 184 | /** | ||
| 185 | * {@inheritDoc} | ||
| 186 | * @since 1.2 | ||
| 187 | */ | ||
| 188 | public double getCtrlX1() { | ||
| 189 | return (double) ctrlx1; | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * {@inheritDoc} | ||
| 194 | * @since 1.2 | ||
| 195 | */ | ||
| 196 | public double getCtrlY1() { | ||
| 197 | return (double) ctrly1; | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * {@inheritDoc} | ||
| 202 | * @since 1.2 | ||
| 203 | */ | ||
| 204 | public Point2D getCtrlP1() { | ||
| 205 | return new Point2D.Float(ctrlx1, ctrly1); | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * {@inheritDoc} | ||
| 210 | * @since 1.2 | ||
| 211 | */ | ||
| 212 | public double getCtrlX2() { | ||
| 213 | return (double) ctrlx2; | ||
| 214 | } | ||
| 215 | |||
| 216 | /** | ||
| 217 | * {@inheritDoc} | ||
| 218 | * @since 1.2 | ||
| 219 | */ | ||
| 220 | public double getCtrlY2() { | ||
| 221 | return (double) ctrly2; | ||
| 222 | } | ||
| 223 | |||
| 224 | /** | ||
| 225 | * {@inheritDoc} | ||
| 226 | * @since 1.2 | ||
| 227 | */ | ||
| 228 | public Point2D getCtrlP2() { | ||
| 229 | return new Point2D.Float(ctrlx2, ctrly2); | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * {@inheritDoc} | ||
| 234 | * @since 1.2 | ||
| 235 | */ | ||
| 236 | public double getX2() { | ||
| 237 | return (double) x2; | ||
| 238 | } | ||
| 239 | |||
| 240 | /** | ||
| 241 | * {@inheritDoc} | ||
| 242 | * @since 1.2 | ||
| 243 | */ | ||
| 244 | public double getY2() { | ||
| 245 | return (double) y2; | ||
| 246 | } | ||
| 247 | |||
| 248 | /** | ||
| 249 | * {@inheritDoc} | ||
| 250 | * @since 1.2 | ||
| 251 | */ | ||
| 252 | public Point2D getP2() { | ||
| 253 | return new Point2D.Float(x2, y2); | ||
| 254 | } | ||
| 255 | |||
| 256 | /** | ||
| 257 | * {@inheritDoc} | ||
| 258 | * @since 1.2 | ||
| 259 | */ | ||
| 260 | public void setCurve(double x1, double y1, | ||
| 261 | double ctrlx1, double ctrly1, | ||
| 262 | double ctrlx2, double ctrly2, | ||
| 263 | double x2, double y2) | ||
| 264 | { | ||
| 265 | this.x1 = (float) x1; | ||
| 266 | this.y1 = (float) y1; | ||
| 267 | this.ctrlx1 = (float) ctrlx1; | ||
| 268 | this.ctrly1 = (float) ctrly1; | ||
| 269 | this.ctrlx2 = (float) ctrlx2; | ||
| 270 | this.ctrly2 = (float) ctrly2; | ||
| 271 | this.x2 = (float) x2; | ||
| 272 | this.y2 = (float) y2; | ||
| 273 | } | ||
| 274 | |||
| 275 | /** | ||
| 276 | * Sets the location of the end points and control points | ||
| 277 | * of this curve to the specified {@code float} coordinates. | ||
| 278 | * | ||
| 279 | * @param x1 the X coordinate used to set the start point | ||
| 280 | * of this {@code CubicCurve2D} | ||
| 281 | * @param y1 the Y coordinate used to set the start point | ||
| 282 | * of this {@code CubicCurve2D} | ||
| 283 | * @param ctrlx1 the X coordinate used to set the first control point | ||
| 284 | * of this {@code CubicCurve2D} | ||
| 285 | * @param ctrly1 the Y coordinate used to set the first control point | ||
| 286 | * of this {@code CubicCurve2D} | ||
| 287 | * @param ctrlx2 the X coordinate used to set the second control point | ||
| 288 | * of this {@code CubicCurve2D} | ||
| 289 | * @param ctrly2 the Y coordinate used to set the second control point | ||
| 290 | * of this {@code CubicCurve2D} | ||
| 291 | * @param x2 the X coordinate used to set the end point | ||
| 292 | * of this {@code CubicCurve2D} | ||
| 293 | * @param y2 the Y coordinate used to set the end point | ||
| 294 | * of this {@code CubicCurve2D} | ||
| 295 | * @since 1.2 | ||
| 296 | */ | ||
| 297 | public void setCurve(float x1, float y1, | ||
| 298 | float ctrlx1, float ctrly1, | ||
| 299 | float ctrlx2, float ctrly2, | ||
| 300 | float x2, float y2) | ||
| 301 | { | ||
| 302 | this.x1 = x1; | ||
| 303 | this.y1 = y1; | ||
| 304 | this.ctrlx1 = ctrlx1; | ||
| 305 | this.ctrly1 = ctrly1; | ||
| 306 | this.ctrlx2 = ctrlx2; | ||
| 307 | this.ctrly2 = ctrly2; | ||
| 308 | this.x2 = x2; | ||
| 309 | this.y2 = y2; | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * {@inheritDoc} | ||
| 314 | * @since 1.2 | ||
| 315 | */ | ||
| 316 | public Rectangle2D getBounds2D() { | ||
| 317 | float left = Math.min(Math.min(x1, x2), | ||
| 318 | Math.min(ctrlx1, ctrlx2)); | ||
| 319 | float top = Math.min(Math.min(y1, y2), | ||
| 320 | Math.min(ctrly1, ctrly2)); | ||
| 321 | float right = Math.max(Math.max(x1, x2), | ||
| 322 | Math.max(ctrlx1, ctrlx2)); | ||
| 323 | float bottom = Math.max(Math.max(y1, y2), | ||
| 324 | Math.max(ctrly1, ctrly2)); | ||
| 325 | return new Rectangle2D.Float(left, top, | ||
| 326 | right - left, bottom - top); | ||
| 327 | } | ||
| 328 | |||
| 329 | /* | ||
| 330 | * JDK 1.6 serialVersionUID | ||
| 331 | */ | ||
| 332 | private static final long serialVersionUID = -1272015596714244385L; | ||
| 333 | } | ||
| 334 | |||
| 335 | /** | ||
| 336 | * A cubic parametric curve segment specified with | ||
| 337 | * {@code double} coordinates. | ||
| 338 | * @since 1.2 | ||
| 339 | */ | ||
| 340 | public static class Double extends CubicCurve2D implements Serializable { | ||
| 341 | /** | ||
| 342 | * The X coordinate of the start point | ||
| 343 | * of the cubic curve segment. | ||
| 344 | * @since 1.2 | ||
| 345 | * @serial | ||
| 346 | */ | ||
| 347 | public double x1; | ||
| 348 | |||
| 349 | /** | ||
| 350 | * The Y coordinate of the start point | ||
| 351 | * of the cubic curve segment. | ||
| 352 | * @since 1.2 | ||
| 353 | * @serial | ||
| 354 | */ | ||
| 355 | public double y1; | ||
| 356 | |||
| 357 | /** | ||
| 358 | * The X coordinate of the first control point | ||
| 359 | * of the cubic curve segment. | ||
| 360 | * @since 1.2 | ||
| 361 | * @serial | ||
| 362 | */ | ||
| 363 | public double ctrlx1; | ||
| 364 | |||
| 365 | /** | ||
| 366 | * The Y coordinate of the first control point | ||
| 367 | * of the cubic curve segment. | ||
| 368 | * @since 1.2 | ||
| 369 | * @serial | ||
| 370 | */ | ||
| 371 | public double ctrly1; | ||
| 372 | |||
| 373 | /** | ||
| 374 | * The X coordinate of the second control point | ||
| 375 | * of the cubic curve segment. | ||
| 376 | * @since 1.2 | ||
| 377 | * @serial | ||
| 378 | */ | ||
| 379 | public double ctrlx2; | ||
| 380 | |||
| 381 | /** | ||
| 382 | * The Y coordinate of the second control point | ||
| 383 | * of the cubic curve segment. | ||
| 384 | * @since 1.2 | ||
| 385 | * @serial | ||
| 386 | */ | ||
| 387 | public double ctrly2; | ||
| 388 | |||
| 389 | /** | ||
| 390 | * The X coordinate of the end point | ||
| 391 | * of the cubic curve segment. | ||
| 392 | * @since 1.2 | ||
| 393 | * @serial | ||
| 394 | */ | ||
| 395 | public double x2; | ||
| 396 | |||
| 397 | /** | ||
| 398 | * The Y coordinate of the end point | ||
| 399 | * of the cubic curve segment. | ||
| 400 | * @since 1.2 | ||
| 401 | * @serial | ||
| 402 | */ | ||
| 403 | public double y2; | ||
| 404 | |||
| 405 | /** | ||
| 406 | * Constructs and initializes a CubicCurve with coordinates | ||
| 407 | * (0, 0, 0, 0, 0, 0, 0, 0). | ||
| 408 | * @since 1.2 | ||
| 409 | */ | ||
| 410 | public Double() { | ||
| 411 | } | ||
| 412 | |||
| 413 | /** | ||
| 414 | * Constructs and initializes a {@code CubicCurve2D} from | ||
| 415 | * the specified {@code double} coordinates. | ||
| 416 | * | ||
| 417 | * @param x1 the X coordinate for the start point | ||
| 418 | * of the resulting {@code CubicCurve2D} | ||
| 419 | * @param y1 the Y coordinate for the start point | ||
| 420 | * of the resulting {@code CubicCurve2D} | ||
| 421 | * @param ctrlx1 the X coordinate for the first control point | ||
| 422 | * of the resulting {@code CubicCurve2D} | ||
| 423 | * @param ctrly1 the Y coordinate for the first control point | ||
| 424 | * of the resulting {@code CubicCurve2D} | ||
| 425 | * @param ctrlx2 the X coordinate for the second control point | ||
| 426 | * of the resulting {@code CubicCurve2D} | ||
| 427 | * @param ctrly2 the Y coordinate for the second control point | ||
| 428 | * of the resulting {@code CubicCurve2D} | ||
| 429 | * @param x2 the X coordinate for the end point | ||
| 430 | * of the resulting {@code CubicCurve2D} | ||
| 431 | * @param y2 the Y coordinate for the end point | ||
| 432 | * of the resulting {@code CubicCurve2D} | ||
| 433 | * @since 1.2 | ||
| 434 | */ | ||
| 435 | public Double(double x1, double y1, | ||
| 436 | double ctrlx1, double ctrly1, | ||
| 437 | double ctrlx2, double ctrly2, | ||
| 438 | double x2, double y2) | ||
| 439 | { | ||
| 440 | setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); | ||
| 441 | } | ||
| 442 | |||
| 443 | /** | ||
| 444 | * {@inheritDoc} | ||
| 445 | * @since 1.2 | ||
| 446 | */ | ||
| 447 | public double getX1() { | ||
| 448 | return x1; | ||
| 449 | } | ||
| 450 | |||
| 451 | /** | ||
| 452 | * {@inheritDoc} | ||
| 453 | * @since 1.2 | ||
| 454 | */ | ||
| 455 | public double getY1() { | ||
| 456 | return y1; | ||
| 457 | } | ||
| 458 | |||
| 459 | /** | ||
| 460 | * {@inheritDoc} | ||
| 461 | * @since 1.2 | ||
| 462 | */ | ||
| 463 | public Point2D getP1() { | ||
| 464 | return new Point2D.Double(x1, y1); | ||
| 465 | } | ||
| 466 | |||
| 467 | /** | ||
| 468 | * {@inheritDoc} | ||
| 469 | * @since 1.2 | ||
| 470 | */ | ||
| 471 | public double getCtrlX1() { | ||
| 472 | return ctrlx1; | ||
| 473 | } | ||
| 474 | |||
| 475 | /** | ||
| 476 | * {@inheritDoc} | ||
| 477 | * @since 1.2 | ||
| 478 | */ | ||
| 479 | public double getCtrlY1() { | ||
| 480 | return ctrly1; | ||
| 481 | } | ||
| 482 | |||
| 483 | /** | ||
| 484 | * {@inheritDoc} | ||
| 485 | * @since 1.2 | ||
| 486 | */ | ||
| 487 | public Point2D getCtrlP1() { | ||
| 488 | return new Point2D.Double(ctrlx1, ctrly1); | ||
| 489 | } | ||
| 490 | |||
| 491 | /** | ||
| 492 | * {@inheritDoc} | ||
| 493 | * @since 1.2 | ||
| 494 | */ | ||
| 495 | public double getCtrlX2() { | ||
| 496 | return ctrlx2; | ||
| 497 | } | ||
| 498 | |||
| 499 | /** | ||
| 500 | * {@inheritDoc} | ||
| 501 | * @since 1.2 | ||
| 502 | */ | ||
| 503 | public double getCtrlY2() { | ||
| 504 | return ctrly2; | ||
| 505 | } | ||
| 506 | |||
| 507 | /** | ||
| 508 | * {@inheritDoc} | ||
| 509 | * @since 1.2 | ||
| 510 | */ | ||
| 511 | public Point2D getCtrlP2() { | ||
| 512 | return new Point2D.Double(ctrlx2, ctrly2); | ||
| 513 | } | ||
| 514 | |||
| 515 | /** | ||
| 516 | * {@inheritDoc} | ||
| 517 | * @since 1.2 | ||
| 518 | */ | ||
| 519 | public double getX2() { | ||
| 520 | return x2; | ||
| 521 | } | ||
| 522 | |||
| 523 | /** | ||
| 524 | * {@inheritDoc} | ||
| 525 | * @since 1.2 | ||
| 526 | */ | ||
| 527 | public double getY2() { | ||
| 528 | return y2; | ||
| 529 | } | ||
| 530 | |||
| 531 | /** | ||
| 532 | * {@inheritDoc} | ||
| 533 | * @since 1.2 | ||
| 534 | */ | ||
| 535 | public Point2D getP2() { | ||
| 536 | return new Point2D.Double(x2, y2); | ||
| 537 | } | ||
| 538 | |||
| 539 | /** | ||
| 540 | * {@inheritDoc} | ||
| 541 | * @since 1.2 | ||
| 542 | */ | ||
| 543 | public void setCurve(double x1, double y1, | ||
| 544 | double ctrlx1, double ctrly1, | ||
| 545 | double ctrlx2, double ctrly2, | ||
| 546 | double x2, double y2) | ||
| 547 | { | ||
| 548 | this.x1 = x1; | ||
| 549 | this.y1 = y1; | ||
| 550 | this.ctrlx1 = ctrlx1; | ||
| 551 | this.ctrly1 = ctrly1; | ||
| 552 | this.ctrlx2 = ctrlx2; | ||
| 553 | this.ctrly2 = ctrly2; | ||
| 554 | this.x2 = x2; | ||
| 555 | this.y2 = y2; | ||
| 556 | } | ||
| 557 | |||
| 558 | /** | ||
| 559 | * {@inheritDoc} | ||
| 560 | * @since 1.2 | ||
| 561 | */ | ||
| 562 | public Rectangle2D getBounds2D() { | ||
| 563 | double left = Math.min(Math.min(x1, x2), | ||
| 564 | Math.min(ctrlx1, ctrlx2)); | ||
| 565 | double top = Math.min(Math.min(y1, y2), | ||
| 566 | Math.min(ctrly1, ctrly2)); | ||
| 567 | double right = Math.max(Math.max(x1, x2), | ||
| 568 | Math.max(ctrlx1, ctrlx2)); | ||
| 569 | double bottom = Math.max(Math.max(y1, y2), | ||
| 570 | Math.max(ctrly1, ctrly2)); | ||
| 571 | return new Rectangle2D.Double(left, top, | ||
| 572 | right - left, bottom - top); | ||
| 573 | } | ||
| 574 | |||
| 575 | /* | ||
| 576 | * JDK 1.6 serialVersionUID | ||
| 577 | */ | ||
| 578 | private static final long serialVersionUID = -4202960122839707295L; | ||
| 579 | } | ||
| 580 | |||
| 581 | /** | ||
| 582 | * This is an abstract class that cannot be instantiated directly. | ||
| 583 | * Type-specific implementation subclasses are available for | ||
| 584 | * instantiation and provide a number of formats for storing | ||
| 585 | * the information necessary to satisfy the various accessor | ||
| 586 | * methods below. | ||
| 587 | * | ||
| 588 | * @see java.awt.geom.CubicCurve2D.Float | ||
| 589 | * @see java.awt.geom.CubicCurve2D.Double | ||
| 590 | * @since 1.2 | ||
| 591 | */ | ||
| 592 | protected CubicCurve2D() { | ||
| 593 | } | ||
| 594 | |||
| 595 | /** | ||
| 596 | * Returns the X coordinate of the start point in double precision. | ||
| 597 | * @return the X coordinate of the start point of the | ||
| 598 | * {@code CubicCurve2D}. | ||
| 599 | * @since 1.2 | ||
| 600 | */ | ||
| 601 | public abstract double getX1(); | ||
| 602 | |||
| 603 | /** | ||
| 604 | * Returns the Y coordinate of the start point in double precision. | ||
| 605 | * @return the Y coordinate of the start point of the | ||
| 606 | * {@code CubicCurve2D}. | ||
| 607 | * @since 1.2 | ||
| 608 | */ | ||
| 609 | public abstract double getY1(); | ||
| 610 | |||
| 611 | /** | ||
| 612 | * Returns the start point. | ||
| 613 | * @return a {@code Point2D} that is the start point of | ||
| 614 | * the {@code CubicCurve2D}. | ||
| 615 | * @since 1.2 | ||
| 616 | */ | ||
| 617 | public abstract Point2D getP1(); | ||
| 618 | |||
| 619 | /** | ||
| 620 | * Returns the X coordinate of the first control point in double precision. | ||
| 621 | * @return the X coordinate of the first control point of the | ||
| 622 | * {@code CubicCurve2D}. | ||
| 623 | * @since 1.2 | ||
| 624 | */ | ||
| 625 | public abstract double getCtrlX1(); | ||
| 626 | |||
| 627 | /** | ||
| 628 | * Returns the Y coordinate of the first control point in double precision. | ||
| 629 | * @return the Y coordinate of the first control point of the | ||
| 630 | * {@code CubicCurve2D}. | ||
| 631 | * @since 1.2 | ||
| 632 | */ | ||
| 633 | public abstract double getCtrlY1(); | ||
| 634 | |||
| 635 | /** | ||
| 636 | * Returns the first control point. | ||
| 637 | * @return a {@code Point2D} that is the first control point of | ||
| 638 | * the {@code CubicCurve2D}. | ||
| 639 | * @since 1.2 | ||
| 640 | */ | ||
| 641 | public abstract Point2D getCtrlP1(); | ||
| 642 | |||
| 643 | /** | ||
| 644 | * Returns the X coordinate of the second control point | ||
| 645 | * in double precision. | ||
| 646 | * @return the X coordinate of the second control point of the | ||
| 647 | * {@code CubicCurve2D}. | ||
| 648 | * @since 1.2 | ||
| 649 | */ | ||
| 650 | public abstract double getCtrlX2(); | ||
| 651 | |||
| 652 | /** | ||
| 653 | * Returns the Y coordinate of the second control point | ||
| 654 | * in double precision. | ||
| 655 | * @return the Y coordinate of the second control point of the | ||
| 656 | * {@code CubicCurve2D}. | ||
| 657 | * @since 1.2 | ||
| 658 | */ | ||
| 659 | public abstract double getCtrlY2(); | ||
| 660 | |||
| 661 | /** | ||
| 662 | * Returns the second control point. | ||
| 663 | * @return a {@code Point2D} that is the second control point of | ||
| 664 | * the {@code CubicCurve2D}. | ||
| 665 | * @since 1.2 | ||
| 666 | */ | ||
| 667 | public abstract Point2D getCtrlP2(); | ||
| 668 | |||
| 669 | /** | ||
| 670 | * Returns the X coordinate of the end point in double precision. | ||
| 671 | * @return the X coordinate of the end point of the | ||
| 672 | * {@code CubicCurve2D}. | ||
| 673 | * @since 1.2 | ||
| 674 | */ | ||
| 675 | public abstract double getX2(); | ||
| 676 | |||
| 677 | /** | ||
| 678 | * Returns the Y coordinate of the end point in double precision. | ||
| 679 | * @return the Y coordinate of the end point of the | ||
| 680 | * {@code CubicCurve2D}. | ||
| 681 | * @since 1.2 | ||
| 682 | */ | ||
| 683 | public abstract double getY2(); | ||
| 684 | |||
| 685 | /** | ||
| 686 | * Returns the end point. | ||
| 687 | * @return a {@code Point2D} that is the end point of | ||
| 688 | * the {@code CubicCurve2D}. | ||
| 689 | * @since 1.2 | ||
| 690 | */ | ||
| 691 | public abstract Point2D getP2(); | ||
| 692 | |||
| 693 | /** | ||
| 694 | * Sets the location of the end points and control points of this curve | ||
| 695 | * to the specified double coordinates. | ||
| 696 | * | ||
| 697 | * @param x1 the X coordinate used to set the start point | ||
| 698 | * of this {@code CubicCurve2D} | ||
| 699 | * @param y1 the Y coordinate used to set the start point | ||
| 700 | * of this {@code CubicCurve2D} | ||
| 701 | * @param ctrlx1 the X coordinate used to set the first control point | ||
| 702 | * of this {@code CubicCurve2D} | ||
| 703 | * @param ctrly1 the Y coordinate used to set the first control point | ||
| 704 | * of this {@code CubicCurve2D} | ||
| 705 | * @param ctrlx2 the X coordinate used to set the second control point | ||
| 706 | * of this {@code CubicCurve2D} | ||
| 707 | * @param ctrly2 the Y coordinate used to set the second control point | ||
| 708 | * of this {@code CubicCurve2D} | ||
| 709 | * @param x2 the X coordinate used to set the end point | ||
| 710 | * of this {@code CubicCurve2D} | ||
| 711 | * @param y2 the Y coordinate used to set the end point | ||
| 712 | * of this {@code CubicCurve2D} | ||
| 713 | * @since 1.2 | ||
| 714 | */ | ||
| 715 | public abstract void setCurve(double x1, double y1, | ||
| 716 | double ctrlx1, double ctrly1, | ||
| 717 | double ctrlx2, double ctrly2, | ||
| 718 | double x2, double y2); | ||
| 719 | |||
| 720 | /** | ||
| 721 | * Sets the location of the end points and control points of this curve | ||
| 722 | * to the double coordinates at the specified offset in the specified | ||
| 723 | * array. | ||
| 724 | * @param coords a double array containing coordinates | ||
| 725 | * @param offset the index of <code>coords</code> from which to begin | ||
| 726 | * setting the end points and control points of this curve | ||
| 727 | * to the coordinates contained in <code>coords</code> | ||
| 728 | * @since 1.2 | ||
| 729 | */ | ||
| 730 | public void setCurve(double[] coords, int offset) { | ||
| 731 | setCurve(coords[offset + 0], coords[offset + 1], | ||
| 732 | coords[offset + 2], coords[offset + 3], | ||
| 733 | coords[offset + 4], coords[offset + 5], | ||
| 734 | coords[offset + 6], coords[offset + 7]); | ||
| 735 | } | ||
| 736 | |||
| 737 | /** | ||
| 738 | * Sets the location of the end points and control points of this curve | ||
| 739 | * to the specified <code>Point2D</code> coordinates. | ||
| 740 | * @param p1 the first specified <code>Point2D</code> used to set the | ||
| 741 | * start point of this curve | ||
| 742 | * @param cp1 the second specified <code>Point2D</code> used to set the | ||
| 743 | * first control point of this curve | ||
| 744 | * @param cp2 the third specified <code>Point2D</code> used to set the | ||
| 745 | * second control point of this curve | ||
| 746 | * @param p2 the fourth specified <code>Point2D</code> used to set the | ||
| 747 | * end point of this curve | ||
| 748 | * @since 1.2 | ||
| 749 | */ | ||
| 750 | public void setCurve(Point2D p1, Point2D cp1, Point2D cp2, Point2D p2) { | ||
| 751 | setCurve(p1.getX(), p1.getY(), cp1.getX(), cp1.getY(), | ||
| 752 | cp2.getX(), cp2.getY(), p2.getX(), p2.getY()); | ||
| 753 | } | ||
| 754 | |||
| 755 | /** | ||
| 756 | * Sets the location of the end points and control points of this curve | ||
| 757 | * to the coordinates of the <code>Point2D</code> objects at the specified | ||
| 758 | * offset in the specified array. | ||
| 759 | * @param pts an array of <code>Point2D</code> objects | ||
| 760 | * @param offset the index of <code>pts</code> from which to begin setting | ||
| 761 | * the end points and control points of this curve to the | ||
| 762 | * points contained in <code>pts</code> | ||
| 763 | * @since 1.2 | ||
| 764 | */ | ||
| 765 | public void setCurve(Point2D[] pts, int offset) { | ||
| 766 | setCurve(pts[offset + 0].getX(), pts[offset + 0].getY(), | ||
| 767 | pts[offset + 1].getX(), pts[offset + 1].getY(), | ||
| 768 | pts[offset + 2].getX(), pts[offset + 2].getY(), | ||
| 769 | pts[offset + 3].getX(), pts[offset + 3].getY()); | ||
| 770 | } | ||
| 771 | |||
| 772 | /** | ||
| 773 | * Sets the location of the end points and control points of this curve | ||
| 774 | * to the same as those in the specified <code>CubicCurve2D</code>. | ||
| 775 | * @param c the specified <code>CubicCurve2D</code> | ||
| 776 | * @since 1.2 | ||
| 777 | */ | ||
| 778 | public void setCurve(CubicCurve2D c) { | ||
| 779 | setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(), | ||
| 780 | c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2()); | ||
| 781 | } | ||
| 782 | |||
| 783 | /** | ||
| 784 | * Returns the square of the flatness of the cubic curve specified | ||
| 785 | * by the indicated control points. The flatness is the maximum distance | ||
| 786 | * of a control point from the line connecting the end points. | ||
| 787 | * | ||
| 788 | * @param x1 the X coordinate that specifies the start point | ||
| 789 | * of a {@code CubicCurve2D} | ||
| 790 | * @param y1 the Y coordinate that specifies the start point | ||
| 791 | * of a {@code CubicCurve2D} | ||
| 792 | * @param ctrlx1 the X coordinate that specifies the first control point | ||
| 793 | * of a {@code CubicCurve2D} | ||
| 794 | * @param ctrly1 the Y coordinate that specifies the first control point | ||
| 795 | * of a {@code CubicCurve2D} | ||
| 796 | * @param ctrlx2 the X coordinate that specifies the second control point | ||
| 797 | * of a {@code CubicCurve2D} | ||
| 798 | * @param ctrly2 the Y coordinate that specifies the second control point | ||
| 799 | * of a {@code CubicCurve2D} | ||
| 800 | * @param x2 the X coordinate that specifies the end point | ||
| 801 | * of a {@code CubicCurve2D} | ||
| 802 | * @param y2 the Y coordinate that specifies the end point | ||
| 803 | * of a {@code CubicCurve2D} | ||
| 804 | * @return the square of the flatness of the {@code CubicCurve2D} | ||
| 805 | * represented by the specified coordinates. | ||
| 806 | * @since 1.2 | ||
| 807 | */ | ||
| 808 | public static double getFlatnessSq(double x1, double y1, | ||
| 809 | double ctrlx1, double ctrly1, | ||
| 810 | double ctrlx2, double ctrly2, | ||
| 811 | double x2, double y2) { | ||
| 812 | return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx1, ctrly1), | ||
| 813 | Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx2, ctrly2)); | ||
| 814 | |||
| 815 | } | ||
| 816 | |||
| 817 | /** | ||
| 818 | * Returns the flatness of the cubic curve specified | ||
| 819 | * by the indicated control points. The flatness is the maximum distance | ||
| 820 | * of a control point from the line connecting the end points. | ||
| 821 | * | ||
| 822 | * @param x1 the X coordinate that specifies the start point | ||
| 823 | * of a {@code CubicCurve2D} | ||
| 824 | * @param y1 the Y coordinate that specifies the start point | ||
| 825 | * of a {@code CubicCurve2D} | ||
| 826 | * @param ctrlx1 the X coordinate that specifies the first control point | ||
| 827 | * of a {@code CubicCurve2D} | ||
| 828 | * @param ctrly1 the Y coordinate that specifies the first control point | ||
| 829 | * of a {@code CubicCurve2D} | ||
| 830 | * @param ctrlx2 the X coordinate that specifies the second control point | ||
| 831 | * of a {@code CubicCurve2D} | ||
| 832 | * @param ctrly2 the Y coordinate that specifies the second control point | ||
| 833 | * of a {@code CubicCurve2D} | ||
| 834 | * @param x2 the X coordinate that specifies the end point | ||
| 835 | * of a {@code CubicCurve2D} | ||
| 836 | * @param y2 the Y coordinate that specifies the end point | ||
| 837 | * of a {@code CubicCurve2D} | ||
| 838 | * @return the flatness of the {@code CubicCurve2D} | ||
| 839 | * represented by the specified coordinates. | ||
| 840 | * @since 1.2 | ||
| 841 | */ | ||
| 842 | public static double getFlatness(double x1, double y1, | ||
| 843 | double ctrlx1, double ctrly1, | ||
| 844 | double ctrlx2, double ctrly2, | ||
| 845 | double x2, double y2) { | ||
| 846 | return Math.sqrt(getFlatnessSq(x1, y1, ctrlx1, ctrly1, | ||
| 847 | ctrlx2, ctrly2, x2, y2)); | ||
| 848 | } | ||
| 849 | |||
| 850 | /** | ||
| 851 | * Returns the square of the flatness of the cubic curve specified | ||
| 852 | * by the control points stored in the indicated array at the | ||
| 853 | * indicated index. The flatness is the maximum distance | ||
| 854 | * of a control point from the line connecting the end points. | ||
| 855 | * @param coords an array containing coordinates | ||
| 856 | * @param offset the index of <code>coords</code> from which to begin | ||
| 857 | * getting the end points and control points of the curve | ||
| 858 | * @return the square of the flatness of the <code>CubicCurve2D</code> | ||
| 859 | * specified by the coordinates in <code>coords</code> at | ||
| 860 | * the specified offset. | ||
| 861 | * @since 1.2 | ||
| 862 | */ | ||
| 863 | public static double getFlatnessSq(double coords[], int offset) { | ||
| 864 | return getFlatnessSq(coords[offset + 0], coords[offset + 1], | ||
| 865 | coords[offset + 2], coords[offset + 3], | ||
| 866 | coords[offset + 4], coords[offset + 5], | ||
| 867 | coords[offset + 6], coords[offset + 7]); | ||
| 868 | } | ||
| 869 | |||
| 870 | /** | ||
| 871 | * Returns the flatness of the cubic curve specified | ||
| 872 | * by the control points stored in the indicated array at the | ||
| 873 | * indicated index. The flatness is the maximum distance | ||
| 874 | * of a control point from the line connecting the end points. | ||
| 875 | * @param coords an array containing coordinates | ||
| 876 | * @param offset the index of <code>coords</code> from which to begin | ||
| 877 | * getting the end points and control points of the curve | ||
| 878 | * @return the flatness of the <code>CubicCurve2D</code> | ||
| 879 | * specified by the coordinates in <code>coords</code> at | ||
| 880 | * the specified offset. | ||
| 881 | * @since 1.2 | ||
| 882 | */ | ||
| 883 | public static double getFlatness(double coords[], int offset) { | ||
| 884 | return getFlatness(coords[offset + 0], coords[offset + 1], | ||
| 885 | coords[offset + 2], coords[offset + 3], | ||
| 886 | coords[offset + 4], coords[offset + 5], | ||
| 887 | coords[offset + 6], coords[offset + 7]); | ||
| 888 | } | ||
| 889 | |||
| 890 | /** | ||
| 891 | * Returns the square of the flatness of this curve. The flatness is the | ||
| 892 | * maximum distance of a control point from the line connecting the | ||
| 893 | * end points. | ||
| 894 | * @return the square of the flatness of this curve. | ||
| 895 | * @since 1.2 | ||
| 896 | */ | ||
| 897 | public double getFlatnessSq() { | ||
| 898 | return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), | ||
| 899 | getCtrlX2(), getCtrlY2(), getX2(), getY2()); | ||
| 900 | } | ||
| 901 | |||
| 902 | /** | ||
| 903 | * Returns the flatness of this curve. The flatness is the | ||
| 904 | * maximum distance of a control point from the line connecting the | ||
| 905 | * end points. | ||
| 906 | * @return the flatness of this curve. | ||
| 907 | * @since 1.2 | ||
| 908 | */ | ||
| 909 | public double getFlatness() { | ||
| 910 | return getFlatness(getX1(), getY1(), getCtrlX1(), getCtrlY1(), | ||
| 911 | getCtrlX2(), getCtrlY2(), getX2(), getY2()); | ||
| 912 | } | ||
| 913 | |||
| 914 | /** | ||
| 915 | * Subdivides this cubic curve and stores the resulting two | ||
| 916 | * subdivided curves into the left and right curve parameters. | ||
| 917 | * Either or both of the left and right objects may be the same | ||
| 918 | * as this object or null. | ||
| 919 | * @param left the cubic curve object for storing for the left or | ||
| 920 | * first half of the subdivided curve | ||
| 921 | * @param right the cubic curve object for storing for the right or | ||
| 922 | * second half of the subdivided curve | ||
| 923 | * @since 1.2 | ||
| 924 | */ | ||
| 925 | public void subdivide(CubicCurve2D left, CubicCurve2D right) { | ||
| 926 | subdivide(this, left, right); | ||
| 927 | } | ||
| 928 | |||
| 929 | /** | ||
| 930 | * Subdivides the cubic curve specified by the <code>src</code> parameter | ||
| 931 | * and stores the resulting two subdivided curves into the | ||
| 932 | * <code>left</code> and <code>right</code> curve parameters. | ||
| 933 | * Either or both of the <code>left</code> and <code>right</code> objects | ||
| 934 | * may be the same as the <code>src</code> object or <code>null</code>. | ||
| 935 | * @param src the cubic curve to be subdivided | ||
| 936 | * @param left the cubic curve object for storing the left or | ||
| 937 | * first half of the subdivided curve | ||
| 938 | * @param right the cubic curve object for storing the right or | ||
| 939 | * second half of the subdivided curve | ||
| 940 | * @since 1.2 | ||
| 941 | */ | ||
| 942 | public static void subdivide(CubicCurve2D src, | ||
| 943 | CubicCurve2D left, | ||
| 944 | CubicCurve2D right) { | ||
| 945 | double x1 = src.getX1(); | ||
| 946 | double y1 = src.getY1(); | ||
| 947 | double ctrlx1 = src.getCtrlX1(); | ||
| 948 | double ctrly1 = src.getCtrlY1(); | ||
| 949 | double ctrlx2 = src.getCtrlX2(); | ||
| 950 | double ctrly2 = src.getCtrlY2(); | ||
| 951 | double x2 = src.getX2(); | ||
| 952 | double y2 = src.getY2(); | ||
| 953 | double centerx = (ctrlx1 + ctrlx2) / 2.0; | ||
| 954 | double centery = (ctrly1 + ctrly2) / 2.0; | ||
| 955 | ctrlx1 = (x1 + ctrlx1) / 2.0; | ||
| 956 | ctrly1 = (y1 + ctrly1) / 2.0; | ||
| 957 | ctrlx2 = (x2 + ctrlx2) / 2.0; | ||
| 958 | ctrly2 = (y2 + ctrly2) / 2.0; | ||
| 959 | double ctrlx12 = (ctrlx1 + centerx) / 2.0; | ||
| 960 | double ctrly12 = (ctrly1 + centery) / 2.0; | ||
| 961 | double ctrlx21 = (ctrlx2 + centerx) / 2.0; | ||
| 962 | double ctrly21 = (ctrly2 + centery) / 2.0; | ||
| 963 | centerx = (ctrlx12 + ctrlx21) / 2.0; | ||
| 964 | centery = (ctrly12 + ctrly21) / 2.0; | ||
| 965 | if (left != null) { | ||
| 966 | left.setCurve(x1, y1, ctrlx1, ctrly1, | ||
| 967 | ctrlx12, ctrly12, centerx, centery); | ||
| 968 | } | ||
| 969 | if (right != null) { | ||
| 970 | right.setCurve(centerx, centery, ctrlx21, ctrly21, | ||
| 971 | ctrlx2, ctrly2, x2, y2); | ||
| 972 | } | ||
| 973 | } | ||
| 974 | |||
| 975 | /** | ||
| 976 | * Subdivides the cubic curve specified by the coordinates | ||
| 977 | * stored in the <code>src</code> array at indices <code>srcoff</code> | ||
| 978 | * through (<code>srcoff</code> + 7) and stores the | ||
| 979 | * resulting two subdivided curves into the two result arrays at the | ||
| 980 | * corresponding indices. | ||
| 981 | * Either or both of the <code>left</code> and <code>right</code> | ||
| 982 | * arrays may be <code>null</code> or a reference to the same array | ||
| 983 | * as the <code>src</code> array. | ||
| 984 | * Note that the last point in the first subdivided curve is the | ||
| 985 | * same as the first point in the second subdivided curve. Thus, | ||
| 986 | * it is possible to pass the same array for <code>left</code> | ||
| 987 | * and <code>right</code> and to use offsets, such as <code>rightoff</code> | ||
| 988 | * equals (<code>leftoff</code> + 6), in order | ||
| 989 | * to avoid allocating extra storage for this common point. | ||
| 990 | * @param src the array holding the coordinates for the source curve | ||
| 991 | * @param srcoff the offset into the array of the beginning of the | ||
| 992 | * the 6 source coordinates | ||
| 993 | * @param left the array for storing the coordinates for the first | ||
| 994 | * half of the subdivided curve | ||
| 995 | * @param leftoff the offset into the array of the beginning of the | ||
| 996 | * the 6 left coordinates | ||
| 997 | * @param right the array for storing the coordinates for the second | ||
| 998 | * half of the subdivided curve | ||
| 999 | * @param rightoff the offset into the array of the beginning of the | ||
| 1000 | * the 6 right coordinates | ||
| 1001 | * @since 1.2 | ||
| 1002 | */ | ||
| 1003 | public static void subdivide(double src[], int srcoff, | ||
| 1004 | double left[], int leftoff, | ||
| 1005 | double right[], int rightoff) { | ||
| 1006 | double x1 = src[srcoff + 0]; | ||
| 1007 | double y1 = src[srcoff + 1]; | ||
| 1008 | double ctrlx1 = src[srcoff + 2]; | ||
| 1009 | double ctrly1 = src[srcoff + 3]; | ||
| 1010 | double ctrlx2 = src[srcoff + 4]; | ||
| 1011 | double ctrly2 = src[srcoff + 5]; | ||
| 1012 | double x2 = src[srcoff + 6]; | ||
| 1013 | double y2 = src[srcoff + 7]; | ||
| 1014 | if (left != null) { | ||
| 1015 | left[leftoff + 0] = x1; | ||
| 1016 | left[leftoff + 1] = y1; | ||
| 1017 | } | ||
| 1018 | if (right != null) { | ||
| 1019 | right[rightoff + 6] = x2; | ||
| 1020 | right[rightoff + 7] = y2; | ||
| 1021 | } | ||
| 1022 | x1 = (x1 + ctrlx1) / 2.0; | ||
| 1023 | y1 = (y1 + ctrly1) / 2.0; | ||
| 1024 | x2 = (x2 + ctrlx2) / 2.0; | ||
| 1025 | y2 = (y2 + ctrly2) / 2.0; | ||
| 1026 | double centerx = (ctrlx1 + ctrlx2) / 2.0; | ||
| 1027 | double centery = (ctrly1 + ctrly2) / 2.0; | ||
| 1028 | ctrlx1 = (x1 + centerx) / 2.0; | ||
| 1029 | ctrly1 = (y1 + centery) / 2.0; | ||
| 1030 | ctrlx2 = (x2 + centerx) / 2.0; | ||
| 1031 | ctrly2 = (y2 + centery) / 2.0; | ||
| 1032 | centerx = (ctrlx1 + ctrlx2) / 2.0; | ||
| 1033 | centery = (ctrly1 + ctrly2) / 2.0; | ||
| 1034 | if (left != null) { | ||
| 1035 | left[leftoff + 2] = x1; | ||
| 1036 | left[leftoff + 3] = y1; | ||
| 1037 | left[leftoff + 4] = ctrlx1; | ||
| 1038 | left[leftoff + 5] = ctrly1; | ||
| 1039 | left[leftoff + 6] = centerx; | ||
| 1040 | left[leftoff + 7] = centery; | ||
| 1041 | } | ||
| 1042 | if (right != null) { | ||
| 1043 | right[rightoff + 0] = centerx; | ||
| 1044 | right[rightoff + 1] = centery; | ||
| 1045 | right[rightoff + 2] = ctrlx2; | ||
| 1046 | right[rightoff + 3] = ctrly2; | ||
| 1047 | right[rightoff + 4] = x2; | ||
| 1048 | right[rightoff + 5] = y2; | ||
| 1049 | } | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | /** | ||
| 1053 | * Solves the cubic whose coefficients are in the <code>eqn</code> | ||
| 1054 | * array and places the non-complex roots back into the same array, | ||
| 1055 | * returning the number of roots. The solved cubic is represented | ||
| 1056 | * by the equation: | ||
| 1057 | * <pre> | ||
| 1058 | * eqn = {c, b, a, d} | ||
| 1059 | * dx^3 + ax^2 + bx + c = 0 | ||
| 1060 | * </pre> | ||
| 1061 | * A return value of -1 is used to distinguish a constant equation | ||
| 1062 | * that might be always 0 or never 0 from an equation that has no | ||
| 1063 | * zeroes. | ||
| 1064 | * @param eqn an array containing coefficients for a cubic | ||
| 1065 | * @return the number of roots, or -1 if the equation is a constant. | ||
| 1066 | * @since 1.2 | ||
| 1067 | */ | ||
| 1068 | public static int solveCubic(double eqn[]) { | ||
| 1069 | return solveCubic(eqn, eqn); | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | /** | ||
| 1073 | * Solve the cubic whose coefficients are in the <code>eqn</code> | ||
| 1074 | * array and place the non-complex roots into the <code>res</code> | ||
| 1075 | * array, returning the number of roots. | ||
| 1076 | * The cubic solved is represented by the equation: | ||
| 1077 | * eqn = {c, b, a, d} | ||
| 1078 | * dx^3 + ax^2 + bx + c = 0 | ||
| 1079 | * A return value of -1 is used to distinguish a constant equation, | ||
| 1080 | * which may be always 0 or never 0, from an equation which has no | ||
| 1081 | * zeroes. | ||
| 1082 | * @param eqn the specified array of coefficients to use to solve | ||
| 1083 | * the cubic equation | ||
| 1084 | * @param res the array that contains the non-complex roots | ||
| 1085 | * resulting from the solution of the cubic equation | ||
| 1086 | * @return the number of roots, or -1 if the equation is a constant | ||
| 1087 | * @since 1.3 | ||
| 1088 | */ | ||
| 1089 | public static int solveCubic(double eqn[], double res[]) { | ||
| 1090 | // From Graphics Gems: | ||
| 1091 | // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c | ||
| 1092 | final double d = eqn[3]; | ||
| 1093 | if (d == 0) { | ||
| 1094 | return QuadCurve2D.solveQuadratic(eqn, res); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | /* normal form: x^3 + Ax^2 + Bx + C = 0 */ | ||
| 1098 | final double A = eqn[2] / d; | ||
| 1099 | final double B = eqn[1] / d; | ||
| 1100 | final double C = eqn[0] / d; | ||
| 1101 | |||
| 1102 | |||
| 1103 | // substitute x = y - A/3 to eliminate quadratic term: | ||
| 1104 | // x^3 +Px + Q = 0 | ||
| 1105 | // | ||
| 1106 | // Since we actually need P/3 and Q/2 for all of the | ||
| 1107 | // calculations that follow, we will calculate | ||
| 1108 | // p = P/3 | ||
| 1109 | // q = Q/2 | ||
| 1110 | // instead and use those values for simplicity of the code. | ||
| 1111 | double sq_A = A * A; | ||
| 1112 | double p = 1.0/3 * (-1.0/3 * sq_A + B); | ||
| 1113 | double q = 1.0/2 * (2.0/27 * A * sq_A - 1.0/3 * A * B + C); | ||
| 1114 | |||
| 1115 | /* use Cardano's formula */ | ||
| 1116 | |||
| 1117 | double cb_p = p * p * p; | ||
| 1118 | double D = q * q + cb_p; | ||
| 1119 | |||
| 1120 | final double sub = 1.0/3 * A; | ||
| 1121 | |||
| 1122 | int num; | ||
| 1123 | if (D < 0) { /* Casus irreducibilis: three real solutions */ | ||
| 1124 | // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method | ||
| 1125 | double phi = 1.0/3 * Math.acos(-q / Math.sqrt(-cb_p)); | ||
| 1126 | double t = 2 * Math.sqrt(-p); | ||
| 1127 | |||
| 1128 | if (res == eqn) { | ||
| 1129 | eqn = Arrays.copyOf(eqn, 4); | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | res[ 0 ] = ( t * Math.cos(phi)); | ||
| 1133 | res[ 1 ] = (-t * Math.cos(phi + Math.PI / 3)); | ||
| 1134 | res[ 2 ] = (-t * Math.cos(phi - Math.PI / 3)); | ||
| 1135 | num = 3; | ||
| 1136 | |||
| 1137 | for (int i = 0; i < num; ++i) { | ||
| 1138 | res[ i ] -= sub; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | } else { | ||
| 1142 | // Please see the comment in fixRoots marked 'XXX' before changing | ||
| 1143 | // any of the code in this case. | ||
| 1144 | double sqrt_D = Math.sqrt(D); | ||
| 1145 | double u = Math.cbrt(sqrt_D - q); | ||
| 1146 | double v = - Math.cbrt(sqrt_D + q); | ||
| 1147 | double uv = u+v; | ||
| 1148 | |||
| 1149 | num = 1; | ||
| 1150 | |||
| 1151 | double err = 1200000000*ulp(abs(uv) + abs(sub)); | ||
| 1152 | if (iszero(D, err) || within(u, v, err)) { | ||
| 1153 | if (res == eqn) { | ||
| 1154 | eqn = Arrays.copyOf(eqn, 4); | ||
| 1155 | } | ||
| 1156 | res[1] = -(uv / 2) - sub; | ||
| 1157 | num = 2; | ||
| 1158 | } | ||
| 1159 | // this must be done after the potential Arrays.copyOf | ||
| 1160 | res[ 0 ] = uv - sub; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | if (num > 1) { // num == 3 || num == 2 | ||
| 1164 | num = fixRoots(eqn, res, num); | ||
| 1165 | } | ||
| 1166 | if (num > 2 && (res[2] == res[1] || res[2] == res[0])) { | ||
| 1167 | num--; | ||
| 1168 | } | ||
| 1169 | if (num > 1 && res[1] == res[0]) { | ||
| 1170 | res[1] = res[--num]; // Copies res[2] to res[1] if needed | ||
| 1171 | } | ||
| 1172 | return num; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | // preconditions: eqn != res && eqn[3] != 0 && num > 1 | ||
| 1176 | // This method tries to improve the accuracy of the roots of eqn (which | ||
| 1177 | // should be in res). It also might eliminate roots in res if it decideds | ||
| 1178 | // that they're not real roots. It will not check for roots that the | ||
| 1179 | // computation of res might have missed, so this method should only be | ||
| 1180 | // used when the roots in res have been computed using an algorithm | ||
| 1181 | // that never underestimates the number of roots (such as solveCubic above) | ||
| 1182 | private static int fixRoots(double[] eqn, double[] res, int num) { | ||
| 1183 | double[] intervals = {eqn[1], 2*eqn[2], 3*eqn[3]}; | ||
| 1184 | int critCount = QuadCurve2D.solveQuadratic(intervals, intervals); | ||
| 1185 | if (critCount == 2 && intervals[0] == intervals[1]) { | ||
| 1186 | critCount--; | ||
| 1187 | } | ||
| 1188 | if (critCount == 2 && intervals[0] > intervals[1]) { | ||
| 1189 | double tmp = intervals[0]; | ||
| 1190 | intervals[0] = intervals[1]; | ||
| 1191 | intervals[1] = tmp; | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | // below we use critCount to possibly filter out roots that shouldn't | ||
| 1195 | // have been computed. We require that eqn[3] != 0, so eqn is a proper | ||
| 1196 | // cubic, which means that its limits at -/+inf are -/+inf or +/-inf. | ||
| 1197 | // Therefore, if critCount==2, the curve is shaped like a sideways S, | ||
| 1198 | // and it could have 1-3 roots. If critCount==0 it is monotonic, and | ||
| 1199 | // if critCount==1 it is monotonic with a single point where it is | ||
| 1200 | // flat. In the last 2 cases there can only be 1 root. So in cases | ||
| 1201 | // where num > 1 but critCount < 2, we eliminate all roots in res | ||
| 1202 | // except one. | ||
| 1203 | |||
| 1204 | if (num == 3) { | ||
| 1205 | double xe = getRootUpperBound(eqn); | ||
| 1206 | double x0 = -xe; | ||
| 1207 | |||
| 1208 | Arrays.sort(res, 0, num); | ||
| 1209 | if (critCount == 2) { | ||
| 1210 | // this just tries to improve the accuracy of the computed | ||
| 1211 | // roots using Newton's method. | ||
| 1212 | res[0] = refineRootWithHint(eqn, x0, intervals[0], res[0]); | ||
| 1213 | res[1] = refineRootWithHint(eqn, intervals[0], intervals[1], res[1]); | ||
| 1214 | res[2] = refineRootWithHint(eqn, intervals[1], xe, res[2]); | ||
| 1215 | return 3; | ||
| 1216 | } else if (critCount == 1) { | ||
| 1217 | // we only need fx0 and fxe for the sign of the polynomial | ||
| 1218 | // at -inf and +inf respectively, so we don't need to do | ||
| 1219 | // fx0 = solveEqn(eqn, 3, x0); fxe = solveEqn(eqn, 3, xe) | ||
| 1220 | double fxe = eqn[3]; | ||
| 1221 | double fx0 = -fxe; | ||
| 1222 | |||
| 1223 | double x1 = intervals[0]; | ||
| 1224 | double fx1 = solveEqn(eqn, 3, x1); | ||
| 1225 | |||
| 1226 | // if critCount == 1 or critCount == 0, but num == 3 then | ||
| 1227 | // something has gone wrong. This branch and the one below | ||
| 1228 | // would ideally never execute, but if they do we can't know | ||
| 1229 | // which of the computed roots is closest to the real root; | ||
| 1230 | // therefore, we can't use refineRootWithHint. But even if | ||
| 1231 | // we did know, being here most likely means that the | ||
| 1232 | // curve is very flat close to two of the computed roots | ||
| 1233 | // (or maybe even all three). This might make Newton's method | ||
| 1234 | // fail altogether, which would be a pain to detect and fix. | ||
| 1235 | // This is why we use a very stable bisection method. | ||
| 1236 | if (oppositeSigns(fx0, fx1)) { | ||
| 1237 | res[0] = bisectRootWithHint(eqn, x0, x1, res[0]); | ||
| 1238 | } else if (oppositeSigns(fx1, fxe)) { | ||
| 1239 | res[0] = bisectRootWithHint(eqn, x1, xe, res[2]); | ||
| 1240 | } else /* fx1 must be 0 */ { | ||
| 1241 | res[0] = x1; | ||
| 1242 | } | ||
| 1243 | // return 1 | ||
| 1244 | } else if (critCount == 0) { | ||
| 1245 | res[0] = bisectRootWithHint(eqn, x0, xe, res[1]); | ||
| 1246 | // return 1 | ||
| 1247 | } | ||
| 1248 | } else if (num == 2 && critCount == 2) { | ||
| 1249 | // XXX: here we assume that res[0] has better accuracy than res[1]. | ||
| 1250 | // This is true because this method is only used from solveCubic | ||
| 1251 | // which puts in res[0] the root that it would compute anyway even | ||
| 1252 | // if num==1. If this method is ever used from any other method, or | ||
| 1253 | // if the solveCubic implementation changes, this assumption should | ||
| 1254 | // be reevaluated, and the choice of goodRoot might have to become | ||
| 1255 | // goodRoot = (abs(eqn'(res[0])) > abs(eqn'(res[1]))) ? res[0] : res[1] | ||
| 1256 | // where eqn' is the derivative of eqn. | ||
| 1257 | double goodRoot = res[0]; | ||
| 1258 | double badRoot = res[1]; | ||
| 1259 | double x1 = intervals[0]; | ||
| 1260 | double x2 = intervals[1]; | ||
| 1261 | // If a cubic curve really has 2 roots, one of those roots must be | ||
| 1262 | // at a critical point. That can't be goodRoot, so we compute x to | ||
| 1263 | // be the farthest critical point from goodRoot. If there are two | ||
| 1264 | // roots, x must be the second one, so we evaluate eqn at x, and if | ||
| 1265 | // it is zero (or close enough) we put x in res[1] (or badRoot, if | ||
| 1266 | // |solveEqn(eqn, 3, badRoot)| < |solveEqn(eqn, 3, x)| but this | ||
| 1267 | // shouldn't happen often). | ||
| 1268 | double x = abs(x1 - goodRoot) > abs(x2 - goodRoot) ? x1 : x2; | ||
| 1269 | double fx = solveEqn(eqn, 3, x); | ||
| 1270 | |||
| 1271 | if (iszero(fx, 10000000*ulp(x))) { | ||
| 1272 | double badRootVal = solveEqn(eqn, 3, badRoot); | ||
| 1273 | res[1] = abs(badRootVal) < abs(fx) ? badRoot : x; | ||
| 1274 | return 2; | ||
| 1275 | } | ||
| 1276 | } // else there can only be one root - goodRoot, and it is already in res[0] | ||
| 1277 | |||
| 1278 | return 1; | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | // use newton's method. | ||
| 1282 | private static double refineRootWithHint(double[] eqn, double min, double max, double t) { | ||
| 1283 | if (!inInterval(t, min, max)) { | ||
| 1284 | return t; | ||
| 1285 | } | ||
| 1286 | double[] deriv = {eqn[1], 2*eqn[2], 3*eqn[3]}; | ||
| 1287 | double origt = t; | ||
| 1288 | for (int i = 0; i < 3; i++) { | ||
| 1289 | double slope = solveEqn(deriv, 2, t); | ||
| 1290 | double y = solveEqn(eqn, 3, t); | ||
| 1291 | double delta = - (y / slope); | ||
| 1292 | double newt = t + delta; | ||
| 1293 | |||
| 1294 | if (slope == 0 || y == 0 || t == newt) { | ||
| 1295 | break; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | t = newt; | ||
| 1299 | } | ||
| 1300 | if (within(t, origt, 1000*ulp(origt)) && inInterval(t, min, max)) { | ||
| 1301 | return t; | ||
| 1302 | } | ||
| 1303 | return origt; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | private static double bisectRootWithHint(double[] eqn, double x0, double xe, double hint) { | ||
| 1307 | double delta1 = Math.min(abs(hint - x0) / 64, 0.0625); | ||
| 1308 | double delta2 = Math.min(abs(hint - xe) / 64, 0.0625); | ||
| 1309 | double x02 = hint - delta1; | ||
| 1310 | double xe2 = hint + delta2; | ||
| 1311 | double fx02 = solveEqn(eqn, 3, x02); | ||
| 1312 | double fxe2 = solveEqn(eqn, 3, xe2); | ||
| 1313 | while (oppositeSigns(fx02, fxe2)) { | ||
| 1314 | if (x02 >= xe2) { | ||
| 1315 | return x02; | ||
| 1316 | } | ||
| 1317 | x0 = x02; | ||
| 1318 | xe = xe2; | ||
| 1319 | delta1 /= 64; | ||
| 1320 | delta2 /= 64; | ||
| 1321 | x02 = hint - delta1; | ||
| 1322 | xe2 = hint + delta2; | ||
| 1323 | fx02 = solveEqn(eqn, 3, x02); | ||
| 1324 | fxe2 = solveEqn(eqn, 3, xe2); | ||
| 1325 | } | ||
| 1326 | if (fx02 == 0) { | ||
| 1327 | return x02; | ||
| 1328 | } | ||
| 1329 | if (fxe2 == 0) { | ||
| 1330 | return xe2; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | return bisectRoot(eqn, x0, xe); | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | private static double bisectRoot(double[] eqn, double x0, double xe) { | ||
| 1337 | double fx0 = solveEqn(eqn, 3, x0); | ||
| 1338 | double m = x0 + (xe - x0) / 2; | ||
| 1339 | while (m != x0 && m != xe) { | ||
| 1340 | double fm = solveEqn(eqn, 3, m); | ||
| 1341 | if (fm == 0) { | ||
| 1342 | return m; | ||
| 1343 | } | ||
| 1344 | if (oppositeSigns(fx0, fm)) { | ||
| 1345 | xe = m; | ||
| 1346 | } else { | ||
| 1347 | fx0 = fm; | ||
| 1348 | x0 = m; | ||
| 1349 | } | ||
| 1350 | m = x0 + (xe-x0)/2; | ||
| 1351 | } | ||
| 1352 | return m; | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | private static boolean inInterval(double t, double min, double max) { | ||
| 1356 | return min <= t && t <= max; | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | private static boolean within(double x, double y, double err) { | ||
| 1360 | double d = y - x; | ||
| 1361 | return (d <= err && d >= -err); | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | private static boolean iszero(double x, double err) { | ||
| 1365 | return within(x, 0, err); | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | private static boolean oppositeSigns(double x1, double x2) { | ||
| 1369 | return (x1 < 0 && x2 > 0) || (x1 > 0 && x2 < 0); | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | private static double solveEqn(double eqn[], int order, double t) { | ||
| 1373 | double v = eqn[order]; | ||
| 1374 | while (--order >= 0) { | ||
| 1375 | v = v * t + eqn[order]; | ||
| 1376 | } | ||
| 1377 | return v; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | /* | ||
| 1381 | * Computes M+1 where M is an upper bound for all the roots in of eqn. | ||
| 1382 | * See: http://en.wikipedia.org/wiki/Sturm%27s_theorem#Applications. | ||
| 1383 | * The above link doesn't contain a proof, but I [dlila] proved it myself | ||
| 1384 | * so the result is reliable. The proof isn't difficult, but it's a bit | ||
| 1385 | * long to include here. | ||
| 1386 | * Precondition: eqn must represent a cubic polynomial | ||
| 1387 | */ | ||
| 1388 | private static double getRootUpperBound(double[] eqn) { | ||
| 1389 | double d = eqn[3]; | ||
| 1390 | double a = eqn[2]; | ||
| 1391 | double b = eqn[1]; | ||
| 1392 | double c = eqn[0]; | ||
| 1393 | |||
| 1394 | double M = 1 + max(max(abs(a), abs(b)), abs(c)) / abs(d); | ||
| 1395 | M += ulp(M) + 1; | ||
| 1396 | return M; | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | |||
| 1400 | /** | ||
| 1401 | * {@inheritDoc} | ||
| 1402 | * @since 1.2 | ||
| 1403 | */ | ||
| 1404 | public boolean contains(double x, double y) { | ||
| 1405 | if (!(x * 0.0 + y * 0.0 == 0.0)) { | ||
| 1406 | /* Either x or y was infinite or NaN. | ||
| 1407 | * A NaN always produces a negative response to any test | ||
| 1408 | * and Infinity values cannot be "inside" any path so | ||
| 1409 | * they should return false as well. | ||
| 1410 | */ | ||
| 1411 | return false; | ||
| 1412 | } | ||
| 1413 | // We count the "Y" crossings to determine if the point is | ||
| 1414 | // inside the curve bounded by its closing line. | ||
| 1415 | double x1 = getX1(); | ||
| 1416 | double y1 = getY1(); | ||
| 1417 | double x2 = getX2(); | ||
| 1418 | double y2 = getY2(); | ||
| 1419 | int crossings = | ||
| 1420 | (Curve.pointCrossingsForLine(x, y, x1, y1, x2, y2) + | ||
| 1421 | Curve.pointCrossingsForCubic(x, y, | ||
| 1422 | x1, y1, | ||
| 1423 | getCtrlX1(), getCtrlY1(), | ||
| 1424 | getCtrlX2(), getCtrlY2(), | ||
| 1425 | x2, y2, 0)); | ||
| 1426 | return ((crossings & 1) == 1); | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | /** | ||
| 1430 | * {@inheritDoc} | ||
| 1431 | * @since 1.2 | ||
| 1432 | */ | ||
| 1433 | public boolean contains(Point2D p) { | ||
| 1434 | return contains(p.getX(), p.getY()); | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | /** | ||
| 1438 | * {@inheritDoc} | ||
| 1439 | * @since 1.2 | ||
| 1440 | */ | ||
| 1441 | public boolean intersects(double x, double y, double w, double h) { | ||
| 1442 | // Trivially reject non-existant rectangles | ||
| 1443 | if (w <= 0 || h <= 0) { | ||
| 1444 | return false; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | int numCrossings = rectCrossings(x, y, w, h); | ||
| 1448 | // the intended return value is | ||
| 1449 | // numCrossings != 0 || numCrossings == Curve.RECT_INTERSECTS | ||
| 1450 | // but if (numCrossings != 0) numCrossings == INTERSECTS won't matter | ||
| 1451 | // and if !(numCrossings != 0) then numCrossings == 0, so | ||
| 1452 | // numCrossings != RECT_INTERSECT | ||
| 1453 | return numCrossings != 0; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | /** | ||
| 1457 | * {@inheritDoc} | ||
| 1458 | * @since 1.2 | ||
| 1459 | */ | ||
| 1460 | public boolean intersects(Rectangle2D r) { | ||
| 1461 | return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | /** | ||
| 1465 | * {@inheritDoc} | ||
| 1466 | * @since 1.2 | ||
| 1467 | */ | ||
| 1468 | public boolean contains(double x, double y, double w, double h) { | ||
| 1469 | if (w <= 0 || h <= 0) { | ||
| 1470 | return false; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | int numCrossings = rectCrossings(x, y, w, h); | ||
| 1474 | return !(numCrossings == 0 || numCrossings == Curve.RECT_INTERSECTS); | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | private int rectCrossings(double x, double y, double w, double h) { | ||
| 1478 | int crossings = 0; | ||
| 1479 | if (!(getX1() == getX2() && getY1() == getY2())) { | ||
| 1480 | crossings = Curve.rectCrossingsForLine(crossings, | ||
| 1481 | x, y, | ||
| 1482 | x+w, y+h, | ||
| 1483 | getX1(), getY1(), | ||
| 1484 | getX2(), getY2()); | ||
| 1485 | if (crossings == Curve.RECT_INTERSECTS) { | ||
| 1486 | return crossings; | ||
| 1487 | } | ||
| 1488 | } | ||
| 1489 | // we call this with the curve's direction reversed, because we wanted | ||
| 1490 | // to call rectCrossingsForLine first, because it's cheaper. | ||
| 1491 | return Curve.rectCrossingsForCubic(crossings, | ||
| 1492 | x, y, | ||
| 1493 | x+w, y+h, | ||
| 1494 | getX2(), getY2(), | ||
| 1495 | getCtrlX2(), getCtrlY2(), | ||
| 1496 | getCtrlX1(), getCtrlY1(), | ||
| 1497 | getX1(), getY1(), 0); | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | /** | ||
| 1501 | * {@inheritDoc} | ||
| 1502 | * @since 1.2 | ||
| 1503 | */ | ||
| 1504 | public boolean contains(Rectangle2D r) { | ||
| 1505 | return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 1506 | } | ||
| 1507 | |||
| 1508 | /** | ||
| 1509 | * {@inheritDoc} | ||
| 1510 | * @since 1.2 | ||
| 1511 | */ | ||
| 1512 | public Rectangle getBounds() { | ||
| 1513 | return getBounds2D().getBounds(); | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | /** | ||
| 1517 | * Returns an iteration object that defines the boundary of the | ||
| 1518 | * shape. | ||
| 1519 | * The iterator for this class is not multi-threaded safe, | ||
| 1520 | * which means that this <code>CubicCurve2D</code> class does not | ||
| 1521 | * guarantee that modifications to the geometry of this | ||
| 1522 | * <code>CubicCurve2D</code> object do not affect any iterations of | ||
| 1523 | * that geometry that are already in process. | ||
| 1524 | * @param at an optional <code>AffineTransform</code> to be applied to the | ||
| 1525 | * coordinates as they are returned in the iteration, or <code>null</code> | ||
| 1526 | * if untransformed coordinates are desired | ||
| 1527 | * @return the <code>PathIterator</code> object that returns the | ||
| 1528 | * geometry of the outline of this <code>CubicCurve2D</code>, one | ||
| 1529 | * segment at a time. | ||
| 1530 | * @since 1.2 | ||
| 1531 | */ | ||
| 1532 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 1533 | return new CubicIterator(this, at); | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | /** | ||
| 1537 | * Return an iteration object that defines the boundary of the | ||
| 1538 | * flattened shape. | ||
| 1539 | * The iterator for this class is not multi-threaded safe, | ||
| 1540 | * which means that this <code>CubicCurve2D</code> class does not | ||
| 1541 | * guarantee that modifications to the geometry of this | ||
| 1542 | * <code>CubicCurve2D</code> object do not affect any iterations of | ||
| 1543 | * that geometry that are already in process. | ||
| 1544 | * @param at an optional <code>AffineTransform</code> to be applied to the | ||
| 1545 | * coordinates as they are returned in the iteration, or <code>null</code> | ||
| 1546 | * if untransformed coordinates are desired | ||
| 1547 | * @param flatness the maximum amount that the control points | ||
| 1548 | * for a given curve can vary from colinear before a subdivided | ||
| 1549 | * curve is replaced by a straight line connecting the end points | ||
| 1550 | * @return the <code>PathIterator</code> object that returns the | ||
| 1551 | * geometry of the outline of this <code>CubicCurve2D</code>, | ||
| 1552 | * one segment at a time. | ||
| 1553 | * @since 1.2 | ||
| 1554 | */ | ||
| 1555 | public PathIterator getPathIterator(AffineTransform at, double flatness) { | ||
| 1556 | return new FlatteningPathIterator(getPathIterator(at), flatness); | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | /** | ||
| 1560 | * Creates a new object of the same class as this object. | ||
| 1561 | * | ||
| 1562 | * @return a clone of this instance. | ||
| 1563 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 1564 | * @see java.lang.Cloneable | ||
| 1565 | * @since 1.2 | ||
| 1566 | */ | ||
| 1567 | public Object clone() { | ||
| 1568 | try { | ||
| 1569 | return super.clone(); | ||
| 1570 | } catch (CloneNotSupportedException e) { | ||
| 1571 | // this shouldn't happen, since we are Cloneable | ||
| 1572 | throw new InternalError(); | ||
| 1573 | } | ||
| 1574 | } | ||
| 1575 | } | ||
Binary file not shown.
| @@ -0,0 +1,156 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A utility class to iterate over the path segments of a cubic curve | ||
| 32 | * segment through the PathIterator interface. | ||
| 33 | * | ||
| 34 | * @author Jim Graham | ||
| 35 | */ | ||
| 36 | class CubicIterator implements PathIterator { | ||
| 37 | CubicCurve2D cubic; | ||
| 38 | AffineTransform affine; | ||
| 39 | int index; | ||
| 40 | |||
| 41 | CubicIterator(CubicCurve2D q, AffineTransform at) { | ||
| 42 | this.cubic = q; | ||
| 43 | this.affine = at; | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Return the winding rule for determining the insideness of the | ||
| 48 | * path. | ||
| 49 | * @see #WIND_EVEN_ODD | ||
| 50 | * @see #WIND_NON_ZERO | ||
| 51 | */ | ||
| 52 | public int getWindingRule() { | ||
| 53 | return WIND_NON_ZERO; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Tests if there are more points to read. | ||
| 58 | * @return true if there are more points to read | ||
| 59 | */ | ||
| 60 | public boolean isDone() { | ||
| 61 | return (index > 1); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Moves the iterator to the next segment of the path forwards | ||
| 66 | * along the primary direction of traversal as long as there are | ||
| 67 | * more points in that direction. | ||
| 68 | */ | ||
| 69 | public void next() { | ||
| 70 | index++; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Returns the coordinates and type of the current path segment in | ||
| 75 | * the iteration. | ||
| 76 | * The return value is the path segment type: | ||
| 77 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 78 | * A float array of length 6 must be passed in and may be used to | ||
| 79 | * store the coordinates of the point(s). | ||
| 80 | * Each point is stored as a pair of float x,y coordinates. | ||
| 81 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 82 | * SEG_QUADTO will return two points, | ||
| 83 | * SEG_CUBICTO will return 3 points | ||
| 84 | * and SEG_CLOSE will not return any points. | ||
| 85 | * @see #SEG_MOVETO | ||
| 86 | * @see #SEG_LINETO | ||
| 87 | * @see #SEG_QUADTO | ||
| 88 | * @see #SEG_CUBICTO | ||
| 89 | * @see #SEG_CLOSE | ||
| 90 | */ | ||
| 91 | public int currentSegment(float[] coords) { | ||
| 92 | if (isDone()) { | ||
| 93 | throw new NoSuchElementException("cubic iterator iterator out of bounds"); | ||
| 94 | } | ||
| 95 | int type; | ||
| 96 | if (index == 0) { | ||
| 97 | coords[0] = (float) cubic.getX1(); | ||
| 98 | coords[1] = (float) cubic.getY1(); | ||
| 99 | type = SEG_MOVETO; | ||
| 100 | } else { | ||
| 101 | coords[0] = (float) cubic.getCtrlX1(); | ||
| 102 | coords[1] = (float) cubic.getCtrlY1(); | ||
| 103 | coords[2] = (float) cubic.getCtrlX2(); | ||
| 104 | coords[3] = (float) cubic.getCtrlY2(); | ||
| 105 | coords[4] = (float) cubic.getX2(); | ||
| 106 | coords[5] = (float) cubic.getY2(); | ||
| 107 | type = SEG_CUBICTO; | ||
| 108 | } | ||
| 109 | if (affine != null) { | ||
| 110 | affine.transform(coords, 0, coords, 0, index == 0 ? 1 : 3); | ||
| 111 | } | ||
| 112 | return type; | ||
| 113 | } | ||
| 114 | |||
| 115 | /** | ||
| 116 | * Returns the coordinates and type of the current path segment in | ||
| 117 | * the iteration. | ||
| 118 | * The return value is the path segment type: | ||
| 119 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 120 | * A double array of length 6 must be passed in and may be used to | ||
| 121 | * store the coordinates of the point(s). | ||
| 122 | * Each point is stored as a pair of double x,y coordinates. | ||
| 123 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 124 | * SEG_QUADTO will return two points, | ||
| 125 | * SEG_CUBICTO will return 3 points | ||
| 126 | * and SEG_CLOSE will not return any points. | ||
| 127 | * @see #SEG_MOVETO | ||
| 128 | * @see #SEG_LINETO | ||
| 129 | * @see #SEG_QUADTO | ||
| 130 | * @see #SEG_CUBICTO | ||
| 131 | * @see #SEG_CLOSE | ||
| 132 | */ | ||
| 133 | public int currentSegment(double[] coords) { | ||
| 134 | if (isDone()) { | ||
| 135 | throw new NoSuchElementException("cubic iterator iterator out of bounds"); | ||
| 136 | } | ||
| 137 | int type; | ||
| 138 | if (index == 0) { | ||
| 139 | coords[0] = cubic.getX1(); | ||
| 140 | coords[1] = cubic.getY1(); | ||
| 141 | type = SEG_MOVETO; | ||
| 142 | } else { | ||
| 143 | coords[0] = cubic.getCtrlX1(); | ||
| 144 | coords[1] = cubic.getCtrlY1(); | ||
| 145 | coords[2] = cubic.getCtrlX2(); | ||
| 146 | coords[3] = cubic.getCtrlY2(); | ||
| 147 | coords[4] = cubic.getX2(); | ||
| 148 | coords[5] = cubic.getY2(); | ||
| 149 | type = SEG_CUBICTO; | ||
| 150 | } | ||
| 151 | if (affine != null) { | ||
| 152 | affine.transform(coords, 0, coords, 0, index == 0 ? 1 : 3); | ||
| 153 | } | ||
| 154 | return type; | ||
| 155 | } | ||
| 156 | } | ||
Binary file not shown.
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * The <code>Dimension2D</code> class is to encapsulate a width | ||
| 30 | * and a height dimension. | ||
| 31 | * <p> | ||
| 32 | * This class is only the abstract superclass for all objects that | ||
| 33 | * store a 2D dimension. | ||
| 34 | * The actual storage representation of the sizes is left to | ||
| 35 | * the subclass. | ||
| 36 | * | ||
| 37 | * @author Jim Graham | ||
| 38 | * @since 1.2 | ||
| 39 | */ | ||
| 40 | public abstract class Dimension2D implements Cloneable { | ||
| 41 | |||
| 42 | /** | ||
| 43 | * This is an abstract class that cannot be instantiated directly. | ||
| 44 | * Type-specific implementation subclasses are available for | ||
| 45 | * instantiation and provide a number of formats for storing | ||
| 46 | * the information necessary to satisfy the various accessor | ||
| 47 | * methods below. | ||
| 48 | * | ||
| 49 | * @see java.awt.Dimension | ||
| 50 | * @since 1.2 | ||
| 51 | */ | ||
| 52 | protected Dimension2D() { | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Returns the width of this <code>Dimension</code> in double | ||
| 57 | * precision. | ||
| 58 | * @return the width of this <code>Dimension</code>. | ||
| 59 | * @since 1.2 | ||
| 60 | */ | ||
| 61 | public abstract double getWidth(); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Returns the height of this <code>Dimension</code> in double | ||
| 65 | * precision. | ||
| 66 | * @return the height of this <code>Dimension</code>. | ||
| 67 | * @since 1.2 | ||
| 68 | */ | ||
| 69 | public abstract double getHeight(); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Sets the size of this <code>Dimension</code> object to the | ||
| 73 | * specified width and height. | ||
| 74 | * This method is included for completeness, to parallel the | ||
| 75 | * {@link java.awt.Component#getSize getSize} method of | ||
| 76 | * {@link java.awt.Component}. | ||
| 77 | * @param width the new width for the <code>Dimension</code> | ||
| 78 | * object | ||
| 79 | * @param height the new height for the <code>Dimension</code> | ||
| 80 | * object | ||
| 81 | * @since 1.2 | ||
| 82 | */ | ||
| 83 | public abstract void setSize(double width, double height); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Sets the size of this <code>Dimension2D</code> object to | ||
| 87 | * match the specified size. | ||
| 88 | * This method is included for completeness, to parallel the | ||
| 89 | * <code>getSize</code> method of <code>Component</code>. | ||
| 90 | * @param d the new size for the <code>Dimension2D</code> | ||
| 91 | * object | ||
| 92 | * @since 1.2 | ||
| 93 | */ | ||
| 94 | public void setSize(Dimension2D d) { | ||
| 95 | setSize(d.getWidth(), d.getHeight()); | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Creates a new object of the same class as this object. | ||
| 100 | * | ||
| 101 | * @return a clone of this instance. | ||
| 102 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 103 | * @see java.lang.Cloneable | ||
| 104 | * @since 1.2 | ||
| 105 | */ | ||
| 106 | public Object clone() { | ||
| 107 | try { | ||
| 108 | return super.clone(); | ||
| 109 | } catch (CloneNotSupportedException e) { | ||
| 110 | // this shouldn't happen, since we are Cloneable | ||
| 111 | throw new InternalError(); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,464 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.io.Serializable; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * The <code>Ellipse2D</code> class describes an ellipse that is defined | ||
| 32 | * by a framing rectangle. | ||
| 33 | * <p> | ||
| 34 | * This class is only the abstract superclass for all objects which | ||
| 35 | * store a 2D ellipse. | ||
| 36 | * The actual storage representation of the coordinates is left to | ||
| 37 | * the subclass. | ||
| 38 | * | ||
| 39 | * @author Jim Graham | ||
| 40 | * @since 1.2 | ||
| 41 | */ | ||
| 42 | public abstract class Ellipse2D extends RectangularShape { | ||
| 43 | |||
| 44 | /** | ||
| 45 | * The <code>Float</code> class defines an ellipse specified | ||
| 46 | * in <code>float</code> precision. | ||
| 47 | * @since 1.2 | ||
| 48 | */ | ||
| 49 | public static class Float extends Ellipse2D implements Serializable { | ||
| 50 | /** | ||
| 51 | * The X coordinate of the upper-left corner of the | ||
| 52 | * framing rectangle of this {@code Ellipse2D}. | ||
| 53 | * @since 1.2 | ||
| 54 | * @serial | ||
| 55 | */ | ||
| 56 | public float x; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * The Y coordinate of the upper-left corner of the | ||
| 60 | * framing rectangle of this {@code Ellipse2D}. | ||
| 61 | * @since 1.2 | ||
| 62 | * @serial | ||
| 63 | */ | ||
| 64 | public float y; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * The overall width of this <code>Ellipse2D</code>. | ||
| 68 | * @since 1.2 | ||
| 69 | * @serial | ||
| 70 | */ | ||
| 71 | public float width; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * The overall height of this <code>Ellipse2D</code>. | ||
| 75 | * @since 1.2 | ||
| 76 | * @serial | ||
| 77 | */ | ||
| 78 | public float height; | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Constructs a new <code>Ellipse2D</code>, initialized to | ||
| 82 | * location (0, 0) and size (0, 0). | ||
| 83 | * @since 1.2 | ||
| 84 | */ | ||
| 85 | public Float() { | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Constructs and initializes an <code>Ellipse2D</code> from the | ||
| 90 | * specified coordinates. | ||
| 91 | * | ||
| 92 | * @param x the X coordinate of the upper-left corner | ||
| 93 | * of the framing rectangle | ||
| 94 | * @param y the Y coordinate of the upper-left corner | ||
| 95 | * of the framing rectangle | ||
| 96 | * @param w the width of the framing rectangle | ||
| 97 | * @param h the height of the framing rectangle | ||
| 98 | * @since 1.2 | ||
| 99 | */ | ||
| 100 | public Float(float x, float y, float w, float h) { | ||
| 101 | setFrame(x, y, w, h); | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * {@inheritDoc} | ||
| 106 | * @since 1.2 | ||
| 107 | */ | ||
| 108 | public double getX() { | ||
| 109 | return (double) x; | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * {@inheritDoc} | ||
| 114 | * @since 1.2 | ||
| 115 | */ | ||
| 116 | public double getY() { | ||
| 117 | return (double) y; | ||
| 118 | } | ||
| 119 | |||
| 120 | /** | ||
| 121 | * {@inheritDoc} | ||
| 122 | * @since 1.2 | ||
| 123 | */ | ||
| 124 | public double getWidth() { | ||
| 125 | return (double) width; | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * {@inheritDoc} | ||
| 130 | * @since 1.2 | ||
| 131 | */ | ||
| 132 | public double getHeight() { | ||
| 133 | return (double) height; | ||
| 134 | } | ||
| 135 | |||
| 136 | /** | ||
| 137 | * {@inheritDoc} | ||
| 138 | * @since 1.2 | ||
| 139 | */ | ||
| 140 | public boolean isEmpty() { | ||
| 141 | return (width <= 0.0 || height <= 0.0); | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Sets the location and size of the framing rectangle of this | ||
| 146 | * <code>Shape</code> to the specified rectangular values. | ||
| 147 | * | ||
| 148 | * @param x the X coordinate of the upper-left corner of the | ||
| 149 | * specified rectangular shape | ||
| 150 | * @param y the Y coordinate of the upper-left corner of the | ||
| 151 | * specified rectangular shape | ||
| 152 | * @param w the width of the specified rectangular shape | ||
| 153 | * @param h the height of the specified rectangular shape | ||
| 154 | * @since 1.2 | ||
| 155 | */ | ||
| 156 | public void setFrame(float x, float y, float w, float h) { | ||
| 157 | this.x = x; | ||
| 158 | this.y = y; | ||
| 159 | this.width = w; | ||
| 160 | this.height = h; | ||
| 161 | } | ||
| 162 | |||
| 163 | /** | ||
| 164 | * {@inheritDoc} | ||
| 165 | * @since 1.2 | ||
| 166 | */ | ||
| 167 | public void setFrame(double x, double y, double w, double h) { | ||
| 168 | this.x = (float) x; | ||
| 169 | this.y = (float) y; | ||
| 170 | this.width = (float) w; | ||
| 171 | this.height = (float) h; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * {@inheritDoc} | ||
| 176 | * @since 1.2 | ||
| 177 | */ | ||
| 178 | public Rectangle2D getBounds2D() { | ||
| 179 | return new Rectangle2D.Float(x, y, width, height); | ||
| 180 | } | ||
| 181 | |||
| 182 | /* | ||
| 183 | * JDK 1.6 serialVersionUID | ||
| 184 | */ | ||
| 185 | private static final long serialVersionUID = -6633761252372475977L; | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * The <code>Double</code> class defines an ellipse specified | ||
| 190 | * in <code>double</code> precision. | ||
| 191 | * @since 1.2 | ||
| 192 | */ | ||
| 193 | public static class Double extends Ellipse2D implements Serializable { | ||
| 194 | /** | ||
| 195 | * The X coordinate of the upper-left corner of the | ||
| 196 | * framing rectangle of this {@code Ellipse2D}. | ||
| 197 | * @since 1.2 | ||
| 198 | * @serial | ||
| 199 | */ | ||
| 200 | public double x; | ||
| 201 | |||
| 202 | /** | ||
| 203 | * The Y coordinate of the upper-left corner of the | ||
| 204 | * framing rectangle of this {@code Ellipse2D}. | ||
| 205 | * @since 1.2 | ||
| 206 | * @serial | ||
| 207 | */ | ||
| 208 | public double y; | ||
| 209 | |||
| 210 | /** | ||
| 211 | * The overall width of this <code>Ellipse2D</code>. | ||
| 212 | * @since 1.2 | ||
| 213 | * @serial | ||
| 214 | */ | ||
| 215 | public double width; | ||
| 216 | |||
| 217 | /** | ||
| 218 | * The overall height of the <code>Ellipse2D</code>. | ||
| 219 | * @since 1.2 | ||
| 220 | * @serial | ||
| 221 | */ | ||
| 222 | public double height; | ||
| 223 | |||
| 224 | /** | ||
| 225 | * Constructs a new <code>Ellipse2D</code>, initialized to | ||
| 226 | * location (0, 0) and size (0, 0). | ||
| 227 | * @since 1.2 | ||
| 228 | */ | ||
| 229 | public Double() { | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * Constructs and initializes an <code>Ellipse2D</code> from the | ||
| 234 | * specified coordinates. | ||
| 235 | * | ||
| 236 | * @param x the X coordinate of the upper-left corner | ||
| 237 | * of the framing rectangle | ||
| 238 | * @param y the Y coordinate of the upper-left corner | ||
| 239 | * of the framing rectangle | ||
| 240 | * @param w the width of the framing rectangle | ||
| 241 | * @param h the height of the framing rectangle | ||
| 242 | * @since 1.2 | ||
| 243 | */ | ||
| 244 | public Double(double x, double y, double w, double h) { | ||
| 245 | setFrame(x, y, w, h); | ||
| 246 | } | ||
| 247 | |||
| 248 | /** | ||
| 249 | * {@inheritDoc} | ||
| 250 | * @since 1.2 | ||
| 251 | */ | ||
| 252 | public double getX() { | ||
| 253 | return x; | ||
| 254 | } | ||
| 255 | |||
| 256 | /** | ||
| 257 | * {@inheritDoc} | ||
| 258 | * @since 1.2 | ||
| 259 | */ | ||
| 260 | public double getY() { | ||
| 261 | return y; | ||
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * {@inheritDoc} | ||
| 266 | * @since 1.2 | ||
| 267 | */ | ||
| 268 | public double getWidth() { | ||
| 269 | return width; | ||
| 270 | } | ||
| 271 | |||
| 272 | /** | ||
| 273 | * {@inheritDoc} | ||
| 274 | * @since 1.2 | ||
| 275 | */ | ||
| 276 | public double getHeight() { | ||
| 277 | return height; | ||
| 278 | } | ||
| 279 | |||
| 280 | /** | ||
| 281 | * {@inheritDoc} | ||
| 282 | * @since 1.2 | ||
| 283 | */ | ||
| 284 | public boolean isEmpty() { | ||
| 285 | return (width <= 0.0 || height <= 0.0); | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * {@inheritDoc} | ||
| 290 | * @since 1.2 | ||
| 291 | */ | ||
| 292 | public void setFrame(double x, double y, double w, double h) { | ||
| 293 | this.x = x; | ||
| 294 | this.y = y; | ||
| 295 | this.width = w; | ||
| 296 | this.height = h; | ||
| 297 | } | ||
| 298 | |||
| 299 | /** | ||
| 300 | * {@inheritDoc} | ||
| 301 | * @since 1.2 | ||
| 302 | */ | ||
| 303 | public Rectangle2D getBounds2D() { | ||
| 304 | return new Rectangle2D.Double(x, y, width, height); | ||
| 305 | } | ||
| 306 | |||
| 307 | /* | ||
| 308 | * JDK 1.6 serialVersionUID | ||
| 309 | */ | ||
| 310 | private static final long serialVersionUID = 5555464816372320683L; | ||
| 311 | } | ||
| 312 | |||
| 313 | /** | ||
| 314 | * This is an abstract class that cannot be instantiated directly. | ||
| 315 | * Type-specific implementation subclasses are available for | ||
| 316 | * instantiation and provide a number of formats for storing | ||
| 317 | * the information necessary to satisfy the various accessor | ||
| 318 | * methods below. | ||
| 319 | * | ||
| 320 | * @see java.awt.geom.Ellipse2D.Float | ||
| 321 | * @see java.awt.geom.Ellipse2D.Double | ||
| 322 | * @since 1.2 | ||
| 323 | */ | ||
| 324 | protected Ellipse2D() { | ||
| 325 | } | ||
| 326 | |||
| 327 | /** | ||
| 328 | * {@inheritDoc} | ||
| 329 | * @since 1.2 | ||
| 330 | */ | ||
| 331 | public boolean contains(double x, double y) { | ||
| 332 | // Normalize the coordinates compared to the ellipse | ||
| 333 | // having a center at 0,0 and a radius of 0.5. | ||
| 334 | double ellw = getWidth(); | ||
| 335 | if (ellw <= 0.0) { | ||
| 336 | return false; | ||
| 337 | } | ||
| 338 | double normx = (x - getX()) / ellw - 0.5; | ||
| 339 | double ellh = getHeight(); | ||
| 340 | if (ellh <= 0.0) { | ||
| 341 | return false; | ||
| 342 | } | ||
| 343 | double normy = (y - getY()) / ellh - 0.5; | ||
| 344 | return (normx * normx + normy * normy) < 0.25; | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * {@inheritDoc} | ||
| 349 | * @since 1.2 | ||
| 350 | */ | ||
| 351 | public boolean intersects(double x, double y, double w, double h) { | ||
| 352 | if (w <= 0.0 || h <= 0.0) { | ||
| 353 | return false; | ||
| 354 | } | ||
| 355 | // Normalize the rectangular coordinates compared to the ellipse | ||
| 356 | // having a center at 0,0 and a radius of 0.5. | ||
| 357 | double ellw = getWidth(); | ||
| 358 | if (ellw <= 0.0) { | ||
| 359 | return false; | ||
| 360 | } | ||
| 361 | double normx0 = (x - getX()) / ellw - 0.5; | ||
| 362 | double normx1 = normx0 + w / ellw; | ||
| 363 | double ellh = getHeight(); | ||
| 364 | if (ellh <= 0.0) { | ||
| 365 | return false; | ||
| 366 | } | ||
| 367 | double normy0 = (y - getY()) / ellh - 0.5; | ||
| 368 | double normy1 = normy0 + h / ellh; | ||
| 369 | // find nearest x (left edge, right edge, 0.0) | ||
| 370 | // find nearest y (top edge, bottom edge, 0.0) | ||
| 371 | // if nearest x,y is inside circle of radius 0.5, then intersects | ||
| 372 | double nearx, neary; | ||
| 373 | if (normx0 > 0.0) { | ||
| 374 | // center to left of X extents | ||
| 375 | nearx = normx0; | ||
| 376 | } else if (normx1 < 0.0) { | ||
| 377 | // center to right of X extents | ||
| 378 | nearx = normx1; | ||
| 379 | } else { | ||
| 380 | nearx = 0.0; | ||
| 381 | } | ||
| 382 | if (normy0 > 0.0) { | ||
| 383 | // center above Y extents | ||
| 384 | neary = normy0; | ||
| 385 | } else if (normy1 < 0.0) { | ||
| 386 | // center below Y extents | ||
| 387 | neary = normy1; | ||
| 388 | } else { | ||
| 389 | neary = 0.0; | ||
| 390 | } | ||
| 391 | return (nearx * nearx + neary * neary) < 0.25; | ||
| 392 | } | ||
| 393 | |||
| 394 | /** | ||
| 395 | * {@inheritDoc} | ||
| 396 | * @since 1.2 | ||
| 397 | */ | ||
| 398 | public boolean contains(double x, double y, double w, double h) { | ||
| 399 | return (contains(x, y) && | ||
| 400 | contains(x + w, y) && | ||
| 401 | contains(x, y + h) && | ||
| 402 | contains(x + w, y + h)); | ||
| 403 | } | ||
| 404 | |||
| 405 | /** | ||
| 406 | * Returns an iteration object that defines the boundary of this | ||
| 407 | * <code>Ellipse2D</code>. | ||
| 408 | * The iterator for this class is multi-threaded safe, which means | ||
| 409 | * that this <code>Ellipse2D</code> class guarantees that | ||
| 410 | * modifications to the geometry of this <code>Ellipse2D</code> | ||
| 411 | * object do not affect any iterations of that geometry that | ||
| 412 | * are already in process. | ||
| 413 | * @param at an optional <code>AffineTransform</code> to be applied to | ||
| 414 | * the coordinates as they are returned in the iteration, or | ||
| 415 | * <code>null</code> if untransformed coordinates are desired | ||
| 416 | * @return the <code>PathIterator</code> object that returns the | ||
| 417 | * geometry of the outline of this <code>Ellipse2D</code>, | ||
| 418 | * one segment at a time. | ||
| 419 | * @since 1.2 | ||
| 420 | */ | ||
| 421 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 422 | return new EllipseIterator(this, at); | ||
| 423 | } | ||
| 424 | |||
| 425 | /** | ||
| 426 | * Returns the hashcode for this <code>Ellipse2D</code>. | ||
| 427 | * @return the hashcode for this <code>Ellipse2D</code>. | ||
| 428 | * @since 1.6 | ||
| 429 | */ | ||
| 430 | public int hashCode() { | ||
| 431 | long bits = java.lang.Double.doubleToLongBits(getX()); | ||
| 432 | bits += java.lang.Double.doubleToLongBits(getY()) * 37; | ||
| 433 | bits += java.lang.Double.doubleToLongBits(getWidth()) * 43; | ||
| 434 | bits += java.lang.Double.doubleToLongBits(getHeight()) * 47; | ||
| 435 | return (((int) bits) ^ ((int) (bits >> 32))); | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * Determines whether or not the specified <code>Object</code> is | ||
| 440 | * equal to this <code>Ellipse2D</code>. The specified | ||
| 441 | * <code>Object</code> is equal to this <code>Ellipse2D</code> | ||
| 442 | * if it is an instance of <code>Ellipse2D</code> and if its | ||
| 443 | * location and size are the same as this <code>Ellipse2D</code>. | ||
| 444 | * @param obj an <code>Object</code> to be compared with this | ||
| 445 | * <code>Ellipse2D</code>. | ||
| 446 | * @return <code>true</code> if <code>obj</code> is an instance | ||
| 447 | * of <code>Ellipse2D</code> and has the same values; | ||
| 448 | * <code>false</code> otherwise. | ||
| 449 | * @since 1.6 | ||
| 450 | */ | ||
| 451 | public boolean equals(Object obj) { | ||
| 452 | if (obj == this) { | ||
| 453 | return true; | ||
| 454 | } | ||
| 455 | if (obj instanceof Ellipse2D) { | ||
| 456 | Ellipse2D e2d = (Ellipse2D) obj; | ||
| 457 | return ((getX() == e2d.getX()) && | ||
| 458 | (getY() == e2d.getY()) && | ||
| 459 | (getWidth() == e2d.getWidth()) && | ||
| 460 | (getHeight() == e2d.getHeight())); | ||
| 461 | } | ||
| 462 | return false; | ||
| 463 | } | ||
| 464 | } | ||
Binary file not shown.
| @@ -0,0 +1,189 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A utility class to iterate over the path segments of an ellipse | ||
| 32 | * through the PathIterator interface. | ||
| 33 | * | ||
| 34 | * @author Jim Graham | ||
| 35 | */ | ||
| 36 | class EllipseIterator implements PathIterator { | ||
| 37 | double x, y, w, h; | ||
| 38 | AffineTransform affine; | ||
| 39 | int index; | ||
| 40 | |||
| 41 | EllipseIterator(Ellipse2D e, AffineTransform at) { | ||
| 42 | this.x = e.getX(); | ||
| 43 | this.y = e.getY(); | ||
| 44 | this.w = e.getWidth(); | ||
| 45 | this.h = e.getHeight(); | ||
| 46 | this.affine = at; | ||
| 47 | if (w < 0 || h < 0) { | ||
| 48 | index = 6; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Return the winding rule for determining the insideness of the | ||
| 54 | * path. | ||
| 55 | * @see #WIND_EVEN_ODD | ||
| 56 | * @see #WIND_NON_ZERO | ||
| 57 | */ | ||
| 58 | public int getWindingRule() { | ||
| 59 | return WIND_NON_ZERO; | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Tests if there are more points to read. | ||
| 64 | * @return true if there are more points to read | ||
| 65 | */ | ||
| 66 | public boolean isDone() { | ||
| 67 | return index > 5; | ||
| 68 | } | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Moves the iterator to the next segment of the path forwards | ||
| 72 | * along the primary direction of traversal as long as there are | ||
| 73 | * more points in that direction. | ||
| 74 | */ | ||
| 75 | public void next() { | ||
| 76 | index++; | ||
| 77 | } | ||
| 78 | |||
| 79 | // ArcIterator.btan(Math.PI/2) | ||
| 80 | public static final double CtrlVal = 0.5522847498307933; | ||
| 81 | |||
| 82 | /* | ||
| 83 | * ctrlpts contains the control points for a set of 4 cubic | ||
| 84 | * bezier curves that approximate a circle of radius 0.5 | ||
| 85 | * centered at 0.5, 0.5 | ||
| 86 | */ | ||
| 87 | private static final double pcv = 0.5 + CtrlVal * 0.5; | ||
| 88 | private static final double ncv = 0.5 - CtrlVal * 0.5; | ||
| 89 | private static double ctrlpts[][] = { | ||
| 90 | { 1.0, pcv, pcv, 1.0, 0.5, 1.0 }, | ||
| 91 | { ncv, 1.0, 0.0, pcv, 0.0, 0.5 }, | ||
| 92 | { 0.0, ncv, ncv, 0.0, 0.5, 0.0 }, | ||
| 93 | { pcv, 0.0, 1.0, ncv, 1.0, 0.5 } | ||
| 94 | }; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Returns the coordinates and type of the current path segment in | ||
| 98 | * the iteration. | ||
| 99 | * The return value is the path segment type: | ||
| 100 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 101 | * A float array of length 6 must be passed in and may be used to | ||
| 102 | * store the coordinates of the point(s). | ||
| 103 | * Each point is stored as a pair of float x,y coordinates. | ||
| 104 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 105 | * SEG_QUADTO will return two points, | ||
| 106 | * SEG_CUBICTO will return 3 points | ||
| 107 | * and SEG_CLOSE will not return any points. | ||
| 108 | * @see #SEG_MOVETO | ||
| 109 | * @see #SEG_LINETO | ||
| 110 | * @see #SEG_QUADTO | ||
| 111 | * @see #SEG_CUBICTO | ||
| 112 | * @see #SEG_CLOSE | ||
| 113 | */ | ||
| 114 | public int currentSegment(float[] coords) { | ||
| 115 | if (isDone()) { | ||
| 116 | throw new NoSuchElementException("ellipse iterator out of bounds"); | ||
| 117 | } | ||
| 118 | if (index == 5) { | ||
| 119 | return SEG_CLOSE; | ||
| 120 | } | ||
| 121 | if (index == 0) { | ||
| 122 | double ctrls[] = ctrlpts[3]; | ||
| 123 | coords[0] = (float) (x + ctrls[4] * w); | ||
| 124 | coords[1] = (float) (y + ctrls[5] * h); | ||
| 125 | if (affine != null) { | ||
| 126 | affine.transform(coords, 0, coords, 0, 1); | ||
| 127 | } | ||
| 128 | return SEG_MOVETO; | ||
| 129 | } | ||
| 130 | double ctrls[] = ctrlpts[index - 1]; | ||
| 131 | coords[0] = (float) (x + ctrls[0] * w); | ||
| 132 | coords[1] = (float) (y + ctrls[1] * h); | ||
| 133 | coords[2] = (float) (x + ctrls[2] * w); | ||
| 134 | coords[3] = (float) (y + ctrls[3] * h); | ||
| 135 | coords[4] = (float) (x + ctrls[4] * w); | ||
| 136 | coords[5] = (float) (y + ctrls[5] * h); | ||
| 137 | if (affine != null) { | ||
| 138 | affine.transform(coords, 0, coords, 0, 3); | ||
| 139 | } | ||
| 140 | return SEG_CUBICTO; | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Returns the coordinates and type of the current path segment in | ||
| 145 | * the iteration. | ||
| 146 | * The return value is the path segment type: | ||
| 147 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 148 | * A double array of length 6 must be passed in and may be used to | ||
| 149 | * store the coordinates of the point(s). | ||
| 150 | * Each point is stored as a pair of double x,y coordinates. | ||
| 151 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 152 | * SEG_QUADTO will return two points, | ||
| 153 | * SEG_CUBICTO will return 3 points | ||
| 154 | * and SEG_CLOSE will not return any points. | ||
| 155 | * @see #SEG_MOVETO | ||
| 156 | * @see #SEG_LINETO | ||
| 157 | * @see #SEG_QUADTO | ||
| 158 | * @see #SEG_CUBICTO | ||
| 159 | * @see #SEG_CLOSE | ||
| 160 | */ | ||
| 161 | public int currentSegment(double[] coords) { | ||
| 162 | if (isDone()) { | ||
| 163 | throw new NoSuchElementException("ellipse iterator out of bounds"); | ||
| 164 | } | ||
| 165 | if (index == 5) { | ||
| 166 | return SEG_CLOSE; | ||
| 167 | } | ||
| 168 | if (index == 0) { | ||
| 169 | double ctrls[] = ctrlpts[3]; | ||
| 170 | coords[0] = x + ctrls[4] * w; | ||
| 171 | coords[1] = y + ctrls[5] * h; | ||
| 172 | if (affine != null) { | ||
| 173 | affine.transform(coords, 0, coords, 0, 1); | ||
| 174 | } | ||
| 175 | return SEG_MOVETO; | ||
| 176 | } | ||
| 177 | double ctrls[] = ctrlpts[index - 1]; | ||
| 178 | coords[0] = x + ctrls[0] * w; | ||
| 179 | coords[1] = y + ctrls[1] * h; | ||
| 180 | coords[2] = x + ctrls[2] * w; | ||
| 181 | coords[3] = y + ctrls[3] * h; | ||
| 182 | coords[4] = x + ctrls[4] * w; | ||
| 183 | coords[5] = y + ctrls[5] * h; | ||
| 184 | if (affine != null) { | ||
| 185 | affine.transform(coords, 0, coords, 0, 3); | ||
| 186 | } | ||
| 187 | return SEG_CUBICTO; | ||
| 188 | } | ||
| 189 | } | ||
Binary file not shown.
| @@ -0,0 +1,395 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * The <code>FlatteningPathIterator</code> class returns a flattened view of | ||
| 32 | * another {@link PathIterator} object. Other {@link java.awt.Shape Shape} | ||
| 33 | * classes can use this class to provide flattening behavior for their paths | ||
| 34 | * without having to perform the interpolation calculations themselves. | ||
| 35 | * | ||
| 36 | * @author Jim Graham | ||
| 37 | */ | ||
| 38 | public class FlatteningPathIterator implements PathIterator { | ||
| 39 | static final int GROW_SIZE = 24; // Multiple of cubic & quad curve size | ||
| 40 | |||
| 41 | PathIterator src; // The source iterator | ||
| 42 | |||
| 43 | double squareflat; // Square of the flatness parameter | ||
| 44 | // for testing against squared lengths | ||
| 45 | |||
| 46 | int limit; // Maximum number of recursion levels | ||
| 47 | |||
| 48 | double hold[] = new double[14]; // The cache of interpolated coords | ||
| 49 | // Note that this must be long enough | ||
| 50 | // to store a full cubic segment and | ||
| 51 | // a relative cubic segment to avoid | ||
| 52 | // aliasing when copying the coords | ||
| 53 | // of a curve to the end of the array. | ||
| 54 | // This is also serendipitously equal | ||
| 55 | // to the size of a full quad segment | ||
| 56 | // and 2 relative quad segments. | ||
| 57 | |||
| 58 | double curx, cury; // The ending x,y of the last segment | ||
| 59 | |||
| 60 | double movx, movy; // The x,y of the last move segment | ||
| 61 | |||
| 62 | int holdType; // The type of the curve being held | ||
| 63 | // for interpolation | ||
| 64 | |||
| 65 | int holdEnd; // The index of the last curve segment | ||
| 66 | // being held for interpolation | ||
| 67 | |||
| 68 | int holdIndex; // The index of the curve segment | ||
| 69 | // that was last interpolated. This | ||
| 70 | // is the curve segment ready to be | ||
| 71 | // returned in the next call to | ||
| 72 | // currentSegment(). | ||
| 73 | |||
| 74 | int levels[]; // The recursion level at which | ||
| 75 | // each curve being held in storage | ||
| 76 | // was generated. | ||
| 77 | |||
| 78 | int levelIndex; // The index of the entry in the | ||
| 79 | // levels array of the curve segment | ||
| 80 | // at the holdIndex | ||
| 81 | |||
| 82 | boolean done; // True when iteration is done | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Constructs a new <code>FlatteningPathIterator</code> object that | ||
| 86 | * flattens a path as it iterates over it. The iterator does not | ||
| 87 | * subdivide any curve read from the source iterator to more than | ||
| 88 | * 10 levels of subdivision which yields a maximum of 1024 line | ||
| 89 | * segments per curve. | ||
| 90 | * @param src the original unflattened path being iterated over | ||
| 91 | * @param flatness the maximum allowable distance between the | ||
| 92 | * control points and the flattened curve | ||
| 93 | */ | ||
| 94 | public FlatteningPathIterator(PathIterator src, double flatness) { | ||
| 95 | this(src, flatness, 10); | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Constructs a new <code>FlatteningPathIterator</code> object | ||
| 100 | * that flattens a path as it iterates over it. | ||
| 101 | * The <code>limit</code> parameter allows you to control the | ||
| 102 | * maximum number of recursive subdivisions that the iterator | ||
| 103 | * can make before it assumes that the curve is flat enough | ||
| 104 | * without measuring against the <code>flatness</code> parameter. | ||
| 105 | * The flattened iteration therefore never generates more than | ||
| 106 | * a maximum of <code>(2^limit)</code> line segments per curve. | ||
| 107 | * @param src the original unflattened path being iterated over | ||
| 108 | * @param flatness the maximum allowable distance between the | ||
| 109 | * control points and the flattened curve | ||
| 110 | * @param limit the maximum number of recursive subdivisions | ||
| 111 | * allowed for any curved segment | ||
| 112 | * @exception <code>IllegalArgumentException</code> if | ||
| 113 | * <code>flatness</code> or <code>limit</code> | ||
| 114 | * is less than zero | ||
| 115 | */ | ||
| 116 | public FlatteningPathIterator(PathIterator src, double flatness, | ||
| 117 | int limit) { | ||
| 118 | if (flatness < 0.0) { | ||
| 119 | throw new IllegalArgumentException("flatness must be >= 0"); | ||
| 120 | } | ||
| 121 | if (limit < 0) { | ||
| 122 | throw new IllegalArgumentException("limit must be >= 0"); | ||
| 123 | } | ||
| 124 | this.src = src; | ||
| 125 | this.squareflat = flatness * flatness; | ||
| 126 | this.limit = limit; | ||
| 127 | this.levels = new int[limit + 1]; | ||
| 128 | // prime the first path segment | ||
| 129 | next(false); | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Returns the flatness of this iterator. | ||
| 134 | * @return the flatness of this <code>FlatteningPathIterator</code>. | ||
| 135 | */ | ||
| 136 | public double getFlatness() { | ||
| 137 | return Math.sqrt(squareflat); | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Returns the recursion limit of this iterator. | ||
| 142 | * @return the recursion limit of this | ||
| 143 | * <code>FlatteningPathIterator</code>. | ||
| 144 | */ | ||
| 145 | public int getRecursionLimit() { | ||
| 146 | return limit; | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Returns the winding rule for determining the interior of the | ||
| 151 | * path. | ||
| 152 | * @return the winding rule of the original unflattened path being | ||
| 153 | * iterated over. | ||
| 154 | * @see PathIterator#WIND_EVEN_ODD | ||
| 155 | * @see PathIterator#WIND_NON_ZERO | ||
| 156 | */ | ||
| 157 | public int getWindingRule() { | ||
| 158 | return src.getWindingRule(); | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Tests if the iteration is complete. | ||
| 163 | * @return <code>true</code> if all the segments have | ||
| 164 | * been read; <code>false</code> otherwise. | ||
| 165 | */ | ||
| 166 | public boolean isDone() { | ||
| 167 | return done; | ||
| 168 | } | ||
| 169 | |||
| 170 | /* | ||
| 171 | * Ensures that the hold array can hold up to (want) more values. | ||
| 172 | * It is currently holding (hold.length - holdIndex) values. | ||
| 173 | */ | ||
| 174 | void ensureHoldCapacity(int want) { | ||
| 175 | if (holdIndex - want < 0) { | ||
| 176 | int have = hold.length - holdIndex; | ||
| 177 | int newsize = hold.length + GROW_SIZE; | ||
| 178 | double newhold[] = new double[newsize]; | ||
| 179 | System.arraycopy(hold, holdIndex, | ||
| 180 | newhold, holdIndex + GROW_SIZE, | ||
| 181 | have); | ||
| 182 | hold = newhold; | ||
| 183 | holdIndex += GROW_SIZE; | ||
| 184 | holdEnd += GROW_SIZE; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * Moves the iterator to the next segment of the path forwards | ||
| 190 | * along the primary direction of traversal as long as there are | ||
| 191 | * more points in that direction. | ||
| 192 | */ | ||
| 193 | public void next() { | ||
| 194 | next(true); | ||
| 195 | } | ||
| 196 | |||
| 197 | private void next(boolean doNext) { | ||
| 198 | int level; | ||
| 199 | |||
| 200 | if (holdIndex >= holdEnd) { | ||
| 201 | if (doNext) { | ||
| 202 | src.next(); | ||
| 203 | } | ||
| 204 | if (src.isDone()) { | ||
| 205 | done = true; | ||
| 206 | return; | ||
| 207 | } | ||
| 208 | holdType = src.currentSegment(hold); | ||
| 209 | levelIndex = 0; | ||
| 210 | levels[0] = 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | switch (holdType) { | ||
| 214 | case SEG_MOVETO: | ||
| 215 | case SEG_LINETO: | ||
| 216 | curx = hold[0]; | ||
| 217 | cury = hold[1]; | ||
| 218 | if (holdType == SEG_MOVETO) { | ||
| 219 | movx = curx; | ||
| 220 | movy = cury; | ||
| 221 | } | ||
| 222 | holdIndex = 0; | ||
| 223 | holdEnd = 0; | ||
| 224 | break; | ||
| 225 | case SEG_CLOSE: | ||
| 226 | curx = movx; | ||
| 227 | cury = movy; | ||
| 228 | holdIndex = 0; | ||
| 229 | holdEnd = 0; | ||
| 230 | break; | ||
| 231 | case SEG_QUADTO: | ||
| 232 | if (holdIndex >= holdEnd) { | ||
| 233 | // Move the coordinates to the end of the array. | ||
| 234 | holdIndex = hold.length - 6; | ||
| 235 | holdEnd = hold.length - 2; | ||
| 236 | hold[holdIndex + 0] = curx; | ||
| 237 | hold[holdIndex + 1] = cury; | ||
| 238 | hold[holdIndex + 2] = hold[0]; | ||
| 239 | hold[holdIndex + 3] = hold[1]; | ||
| 240 | hold[holdIndex + 4] = curx = hold[2]; | ||
| 241 | hold[holdIndex + 5] = cury = hold[3]; | ||
| 242 | } | ||
| 243 | |||
| 244 | level = levels[levelIndex]; | ||
| 245 | while (level < limit) { | ||
| 246 | if (QuadCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) { | ||
| 247 | break; | ||
| 248 | } | ||
| 249 | |||
| 250 | ensureHoldCapacity(4); | ||
| 251 | QuadCurve2D.subdivide(hold, holdIndex, | ||
| 252 | hold, holdIndex - 4, | ||
| 253 | hold, holdIndex); | ||
| 254 | holdIndex -= 4; | ||
| 255 | |||
| 256 | // Now that we have subdivided, we have constructed | ||
| 257 | // two curves of one depth lower than the original | ||
| 258 | // curve. One of those curves is in the place of | ||
| 259 | // the former curve and one of them is in the next | ||
| 260 | // set of held coordinate slots. We now set both | ||
| 261 | // curves level values to the next higher level. | ||
| 262 | level++; | ||
| 263 | levels[levelIndex] = level; | ||
| 264 | levelIndex++; | ||
| 265 | levels[levelIndex] = level; | ||
| 266 | } | ||
| 267 | |||
| 268 | // This curve segment is flat enough, or it is too deep | ||
| 269 | // in recursion levels to try to flatten any more. The | ||
| 270 | // two coordinates at holdIndex+4 and holdIndex+5 now | ||
| 271 | // contain the endpoint of the curve which can be the | ||
| 272 | // endpoint of an approximating line segment. | ||
| 273 | holdIndex += 4; | ||
| 274 | levelIndex--; | ||
| 275 | break; | ||
| 276 | case SEG_CUBICTO: | ||
| 277 | if (holdIndex >= holdEnd) { | ||
| 278 | // Move the coordinates to the end of the array. | ||
| 279 | holdIndex = hold.length - 8; | ||
| 280 | holdEnd = hold.length - 2; | ||
| 281 | hold[holdIndex + 0] = curx; | ||
| 282 | hold[holdIndex + 1] = cury; | ||
| 283 | hold[holdIndex + 2] = hold[0]; | ||
| 284 | hold[holdIndex + 3] = hold[1]; | ||
| 285 | hold[holdIndex + 4] = hold[2]; | ||
| 286 | hold[holdIndex + 5] = hold[3]; | ||
| 287 | hold[holdIndex + 6] = curx = hold[4]; | ||
| 288 | hold[holdIndex + 7] = cury = hold[5]; | ||
| 289 | } | ||
| 290 | |||
| 291 | level = levels[levelIndex]; | ||
| 292 | while (level < limit) { | ||
| 293 | if (CubicCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) { | ||
| 294 | break; | ||
| 295 | } | ||
| 296 | |||
| 297 | ensureHoldCapacity(6); | ||
| 298 | CubicCurve2D.subdivide(hold, holdIndex, | ||
| 299 | hold, holdIndex - 6, | ||
| 300 | hold, holdIndex); | ||
| 301 | holdIndex -= 6; | ||
| 302 | |||
| 303 | // Now that we have subdivided, we have constructed | ||
| 304 | // two curves of one depth lower than the original | ||
| 305 | // curve. One of those curves is in the place of | ||
| 306 | // the former curve and one of them is in the next | ||
| 307 | // set of held coordinate slots. We now set both | ||
| 308 | // curves level values to the next higher level. | ||
| 309 | level++; | ||
| 310 | levels[levelIndex] = level; | ||
| 311 | levelIndex++; | ||
| 312 | levels[levelIndex] = level; | ||
| 313 | } | ||
| 314 | |||
| 315 | // This curve segment is flat enough, or it is too deep | ||
| 316 | // in recursion levels to try to flatten any more. The | ||
| 317 | // two coordinates at holdIndex+6 and holdIndex+7 now | ||
| 318 | // contain the endpoint of the curve which can be the | ||
| 319 | // endpoint of an approximating line segment. | ||
| 320 | holdIndex += 6; | ||
| 321 | levelIndex--; | ||
| 322 | break; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | /** | ||
| 327 | * Returns the coordinates and type of the current path segment in | ||
| 328 | * the iteration. | ||
| 329 | * The return value is the path segment type: | ||
| 330 | * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. | ||
| 331 | * A float array of length 6 must be passed in and can be used to | ||
| 332 | * store the coordinates of the point(s). | ||
| 333 | * Each point is stored as a pair of float x,y coordinates. | ||
| 334 | * SEG_MOVETO and SEG_LINETO types return one point, | ||
| 335 | * and SEG_CLOSE does not return any points. | ||
| 336 | * @param coords an array that holds the data returned from | ||
| 337 | * this method | ||
| 338 | * @return the path segment type of the current path segment. | ||
| 339 | * @exception <code>NoSuchElementException</code> if there | ||
| 340 | * are no more elements in the flattening path to be | ||
| 341 | * returned. | ||
| 342 | * @see PathIterator#SEG_MOVETO | ||
| 343 | * @see PathIterator#SEG_LINETO | ||
| 344 | * @see PathIterator#SEG_CLOSE | ||
| 345 | */ | ||
| 346 | public int currentSegment(float[] coords) { | ||
| 347 | if (isDone()) { | ||
| 348 | throw new NoSuchElementException("flattening iterator out of bounds"); | ||
| 349 | } | ||
| 350 | int type = holdType; | ||
| 351 | if (type != SEG_CLOSE) { | ||
| 352 | coords[0] = (float) hold[holdIndex + 0]; | ||
| 353 | coords[1] = (float) hold[holdIndex + 1]; | ||
| 354 | if (type != SEG_MOVETO) { | ||
| 355 | type = SEG_LINETO; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | return type; | ||
| 359 | } | ||
| 360 | |||
| 361 | /** | ||
| 362 | * Returns the coordinates and type of the current path segment in | ||
| 363 | * the iteration. | ||
| 364 | * The return value is the path segment type: | ||
| 365 | * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. | ||
| 366 | * A double array of length 6 must be passed in and can be used to | ||
| 367 | * store the coordinates of the point(s). | ||
| 368 | * Each point is stored as a pair of double x,y coordinates. | ||
| 369 | * SEG_MOVETO and SEG_LINETO types return one point, | ||
| 370 | * and SEG_CLOSE does not return any points. | ||
| 371 | * @param coords an array that holds the data returned from | ||
| 372 | * this method | ||
| 373 | * @return the path segment type of the current path segment. | ||
| 374 | * @exception <code>NoSuchElementException</code> if there | ||
| 375 | * are no more elements in the flattening path to be | ||
| 376 | * returned. | ||
| 377 | * @see PathIterator#SEG_MOVETO | ||
| 378 | * @see PathIterator#SEG_LINETO | ||
| 379 | * @see PathIterator#SEG_CLOSE | ||
| 380 | */ | ||
| 381 | public int currentSegment(double[] coords) { | ||
| 382 | if (isDone()) { | ||
| 383 | throw new NoSuchElementException("flattening iterator out of bounds"); | ||
| 384 | } | ||
| 385 | int type = holdType; | ||
| 386 | if (type != SEG_CLOSE) { | ||
| 387 | coords[0] = hold[holdIndex + 0]; | ||
| 388 | coords[1] = hold[holdIndex + 1]; | ||
| 389 | if (type != SEG_MOVETO) { | ||
| 390 | type = SEG_LINETO; | ||
| 391 | } | ||
| 392 | } | ||
| 393 | return type; | ||
| 394 | } | ||
| 395 | } | ||
Binary file not shown.
| @@ -0,0 +1,127 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * The {@code GeneralPath} class represents a geometric path | ||
| 32 | * constructed from straight lines, and quadratic and cubic | ||
| 33 | * (Bézier) curves. It can contain multiple subpaths. | ||
| 34 | * <p> | ||
| 35 | * {@code GeneralPath} is a legacy final class which exactly | ||
| 36 | * implements the behavior of its superclass {@link Path2D.Float}. | ||
| 37 | * Together with {@link Path2D.Double}, the {@link Path2D} classes | ||
| 38 | * provide full implementations of a general geometric path that | ||
| 39 | * support all of the functionality of the {@link Shape} and | ||
| 40 | * {@link PathIterator} interfaces with the ability to explicitly | ||
| 41 | * select different levels of internal coordinate precision. | ||
| 42 | * <p> | ||
| 43 | * Use {@code Path2D.Float} (or this legacy {@code GeneralPath} | ||
| 44 | * subclass) when dealing with data that can be represented | ||
| 45 | * and used with floating point precision. Use {@code Path2D.Double} | ||
| 46 | * for data that requires the accuracy or range of double precision. | ||
| 47 | * | ||
| 48 | * @author Jim Graham | ||
| 49 | * @since 1.2 | ||
| 50 | */ | ||
| 51 | public final class GeneralPath extends Path2D.Float { | ||
| 52 | /** | ||
| 53 | * Constructs a new empty single precision {@code GeneralPath} object | ||
| 54 | * with a default winding rule of {@link #WIND_NON_ZERO}. | ||
| 55 | * | ||
| 56 | * @since 1.2 | ||
| 57 | */ | ||
| 58 | public GeneralPath() { | ||
| 59 | super(WIND_NON_ZERO, INIT_SIZE); | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Constructs a new <code>GeneralPath</code> object with the specified | ||
| 64 | * winding rule to control operations that require the interior of the | ||
| 65 | * path to be defined. | ||
| 66 | * | ||
| 67 | * @param rule the winding rule | ||
| 68 | * @see #WIND_EVEN_ODD | ||
| 69 | * @see #WIND_NON_ZERO | ||
| 70 | * @since 1.2 | ||
| 71 | */ | ||
| 72 | public GeneralPath(int rule) { | ||
| 73 | super(rule, INIT_SIZE); | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Constructs a new <code>GeneralPath</code> object with the specified | ||
| 78 | * winding rule and the specified initial capacity to store path | ||
| 79 | * coordinates. | ||
| 80 | * This number is an initial guess as to how many path segments | ||
| 81 | * will be added to the path, but the storage is expanded as | ||
| 82 | * needed to store whatever path segments are added. | ||
| 83 | * | ||
| 84 | * @param rule the winding rule | ||
| 85 | * @param initialCapacity the estimate for the number of path segments | ||
| 86 | * in the path | ||
| 87 | * @see #WIND_EVEN_ODD | ||
| 88 | * @see #WIND_NON_ZERO | ||
| 89 | * @since 1.2 | ||
| 90 | */ | ||
| 91 | public GeneralPath(int rule, int initialCapacity) { | ||
| 92 | super(rule, initialCapacity); | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Constructs a new <code>GeneralPath</code> object from an arbitrary | ||
| 97 | * {@link Shape} object. | ||
| 98 | * All of the initial geometry and the winding rule for this path are | ||
| 99 | * taken from the specified <code>Shape</code> object. | ||
| 100 | * | ||
| 101 | * @param s the specified <code>Shape</code> object | ||
| 102 | * @since 1.2 | ||
| 103 | */ | ||
| 104 | public GeneralPath(Shape s) { | ||
| 105 | super(s, null); | ||
| 106 | } | ||
| 107 | |||
| 108 | GeneralPath(int windingRule, | ||
| 109 | byte[] pointTypes, | ||
| 110 | int numTypes, | ||
| 111 | float[] pointCoords, | ||
| 112 | int numCoords) | ||
| 113 | { | ||
| 114 | // used to construct from native | ||
| 115 | |||
| 116 | this.windingRule = windingRule; | ||
| 117 | this.pointTypes = pointTypes; | ||
| 118 | this.numTypes = numTypes; | ||
| 119 | this.floatCoords = pointCoords; | ||
| 120 | this.numCoords = numCoords; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* | ||
| 124 | * JDK 1.6 serialVersionUID | ||
| 125 | */ | ||
| 126 | private static final long serialVersionUID = -8327096662768731142L; | ||
| 127 | } | ||
Binary file not shown.
| @@ -0,0 +1,56 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * The <code>IllegalPathStateException</code> represents an | ||
| 30 | * exception that is thrown if an operation is performed on a path | ||
| 31 | * that is in an illegal state with respect to the particular | ||
| 32 | * operation being performed, such as appending a path segment | ||
| 33 | * to a {@link GeneralPath} without an initial moveto. | ||
| 34 | * | ||
| 35 | */ | ||
| 36 | |||
| 37 | public class IllegalPathStateException extends RuntimeException { | ||
| 38 | /** | ||
| 39 | * Constructs an <code>IllegalPathStateException</code> with no | ||
| 40 | * detail message. | ||
| 41 | * | ||
| 42 | * @since 1.2 | ||
| 43 | */ | ||
| 44 | public IllegalPathStateException() { | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Constructs an <code>IllegalPathStateException</code> with the | ||
| 49 | * specified detail message. | ||
| 50 | * @param s the detail message | ||
| 51 | * @since 1.2 | ||
| 52 | */ | ||
| 53 | public IllegalPathStateException(String s) { | ||
| 54 | super (s); | ||
| 55 | } | ||
| 56 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,1128 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.awt.Rectangle; | ||
| 30 | import java.io.Serializable; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * This <code>Line2D</code> represents a line segment in {@code (x,y)} | ||
| 34 | * coordinate space. This class, like all of the Java 2D API, uses a | ||
| 35 | * default coordinate system called <i>user space</i> in which the y-axis | ||
| 36 | * values increase downward and x-axis values increase to the right. For | ||
| 37 | * more information on the user space coordinate system, see the | ||
| 38 | * <a href="http://java.sun.com/j2se/1.3/docs/guide/2d/spec/j2d-intro.fm2.html#61857"> | ||
| 39 | * Coordinate Systems</a> section of the Java 2D Programmer's Guide. | ||
| 40 | * <p> | ||
| 41 | * This class is only the abstract superclass for all objects that | ||
| 42 | * store a 2D line segment. | ||
| 43 | * The actual storage representation of the coordinates is left to | ||
| 44 | * the subclass. | ||
| 45 | * | ||
| 46 | * @author Jim Graham | ||
| 47 | * @since 1.2 | ||
| 48 | */ | ||
| 49 | public abstract class Line2D implements Shape, Cloneable { | ||
| 50 | |||
| 51 | /** | ||
| 52 | * A line segment specified with float coordinates. | ||
| 53 | * @since 1.2 | ||
| 54 | */ | ||
| 55 | public static class Float extends Line2D implements Serializable { | ||
| 56 | /** | ||
| 57 | * The X coordinate of the start point of the line segment. | ||
| 58 | * @since 1.2 | ||
| 59 | * @serial | ||
| 60 | */ | ||
| 61 | public float x1; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * The Y coordinate of the start point of the line segment. | ||
| 65 | * @since 1.2 | ||
| 66 | * @serial | ||
| 67 | */ | ||
| 68 | public float y1; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * The X coordinate of the end point of the line segment. | ||
| 72 | * @since 1.2 | ||
| 73 | * @serial | ||
| 74 | */ | ||
| 75 | public float x2; | ||
| 76 | |||
| 77 | /** | ||
| 78 | * The Y coordinate of the end point of the line segment. | ||
| 79 | * @since 1.2 | ||
| 80 | * @serial | ||
| 81 | */ | ||
| 82 | public float y2; | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Constructs and initializes a Line with coordinates (0, 0) -> (0, 0). | ||
| 86 | * @since 1.2 | ||
| 87 | */ | ||
| 88 | public Float() { | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Constructs and initializes a Line from the specified coordinates. | ||
| 93 | * @param x1 the X coordinate of the start point | ||
| 94 | * @param y1 the Y coordinate of the start point | ||
| 95 | * @param x2 the X coordinate of the end point | ||
| 96 | * @param y2 the Y coordinate of the end point | ||
| 97 | * @since 1.2 | ||
| 98 | */ | ||
| 99 | public Float(float x1, float y1, float x2, float y2) { | ||
| 100 | setLine(x1, y1, x2, y2); | ||
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Constructs and initializes a <code>Line2D</code> from the | ||
| 105 | * specified <code>Point2D</code> objects. | ||
| 106 | * @param p1 the start <code>Point2D</code> of this line segment | ||
| 107 | * @param p2 the end <code>Point2D</code> of this line segment | ||
| 108 | * @since 1.2 | ||
| 109 | */ | ||
| 110 | public Float(Point2D p1, Point2D p2) { | ||
| 111 | setLine(p1, p2); | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * {@inheritDoc} | ||
| 116 | * @since 1.2 | ||
| 117 | */ | ||
| 118 | public double getX1() { | ||
| 119 | return (double) x1; | ||
| 120 | } | ||
| 121 | |||
| 122 | /** | ||
| 123 | * {@inheritDoc} | ||
| 124 | * @since 1.2 | ||
| 125 | */ | ||
| 126 | public double getY1() { | ||
| 127 | return (double) y1; | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * {@inheritDoc} | ||
| 132 | * @since 1.2 | ||
| 133 | */ | ||
| 134 | public Point2D getP1() { | ||
| 135 | return new Point2D.Float(x1, y1); | ||
| 136 | } | ||
| 137 | |||
| 138 | /** | ||
| 139 | * {@inheritDoc} | ||
| 140 | * @since 1.2 | ||
| 141 | */ | ||
| 142 | public double getX2() { | ||
| 143 | return (double) x2; | ||
| 144 | } | ||
| 145 | |||
| 146 | /** | ||
| 147 | * {@inheritDoc} | ||
| 148 | * @since 1.2 | ||
| 149 | */ | ||
| 150 | public double getY2() { | ||
| 151 | return (double) y2; | ||
| 152 | } | ||
| 153 | |||
| 154 | /** | ||
| 155 | * {@inheritDoc} | ||
| 156 | * @since 1.2 | ||
| 157 | */ | ||
| 158 | public Point2D getP2() { | ||
| 159 | return new Point2D.Float(x2, y2); | ||
| 160 | } | ||
| 161 | |||
| 162 | /** | ||
| 163 | * {@inheritDoc} | ||
| 164 | * @since 1.2 | ||
| 165 | */ | ||
| 166 | public void setLine(double x1, double y1, double x2, double y2) { | ||
| 167 | this.x1 = (float) x1; | ||
| 168 | this.y1 = (float) y1; | ||
| 169 | this.x2 = (float) x2; | ||
| 170 | this.y2 = (float) y2; | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Sets the location of the end points of this <code>Line2D</code> | ||
| 175 | * to the specified float coordinates. | ||
| 176 | * @param x1 the X coordinate of the start point | ||
| 177 | * @param y1 the Y coordinate of the start point | ||
| 178 | * @param x2 the X coordinate of the end point | ||
| 179 | * @param y2 the Y coordinate of the end point | ||
| 180 | * @since 1.2 | ||
| 181 | */ | ||
| 182 | public void setLine(float x1, float y1, float x2, float y2) { | ||
| 183 | this.x1 = x1; | ||
| 184 | this.y1 = y1; | ||
| 185 | this.x2 = x2; | ||
| 186 | this.y2 = y2; | ||
| 187 | } | ||
| 188 | |||
| 189 | /** | ||
| 190 | * {@inheritDoc} | ||
| 191 | * @since 1.2 | ||
| 192 | */ | ||
| 193 | public Rectangle2D getBounds2D() { | ||
| 194 | float x, y, w, h; | ||
| 195 | if (x1 < x2) { | ||
| 196 | x = x1; | ||
| 197 | w = x2 - x1; | ||
| 198 | } else { | ||
| 199 | x = x2; | ||
| 200 | w = x1 - x2; | ||
| 201 | } | ||
| 202 | if (y1 < y2) { | ||
| 203 | y = y1; | ||
| 204 | h = y2 - y1; | ||
| 205 | } else { | ||
| 206 | y = y2; | ||
| 207 | h = y1 - y2; | ||
| 208 | } | ||
| 209 | return new Rectangle2D.Float(x, y, w, h); | ||
| 210 | } | ||
| 211 | |||
| 212 | /* | ||
| 213 | * JDK 1.6 serialVersionUID | ||
| 214 | */ | ||
| 215 | private static final long serialVersionUID = 6161772511649436349L; | ||
| 216 | } | ||
| 217 | |||
| 218 | /** | ||
| 219 | * A line segment specified with double coordinates. | ||
| 220 | * @since 1.2 | ||
| 221 | */ | ||
| 222 | public static class Double extends Line2D implements Serializable { | ||
| 223 | /** | ||
| 224 | * The X coordinate of the start point of the line segment. | ||
| 225 | * @since 1.2 | ||
| 226 | * @serial | ||
| 227 | */ | ||
| 228 | public double x1; | ||
| 229 | |||
| 230 | /** | ||
| 231 | * The Y coordinate of the start point of the line segment. | ||
| 232 | * @since 1.2 | ||
| 233 | * @serial | ||
| 234 | */ | ||
| 235 | public double y1; | ||
| 236 | |||
| 237 | /** | ||
| 238 | * The X coordinate of the end point of the line segment. | ||
| 239 | * @since 1.2 | ||
| 240 | * @serial | ||
| 241 | */ | ||
| 242 | public double x2; | ||
| 243 | |||
| 244 | /** | ||
| 245 | * The Y coordinate of the end point of the line segment. | ||
| 246 | * @since 1.2 | ||
| 247 | * @serial | ||
| 248 | */ | ||
| 249 | public double y2; | ||
| 250 | |||
| 251 | /** | ||
| 252 | * Constructs and initializes a Line with coordinates (0, 0) -> (0, 0). | ||
| 253 | * @since 1.2 | ||
| 254 | */ | ||
| 255 | public Double() { | ||
| 256 | } | ||
| 257 | |||
| 258 | /** | ||
| 259 | * Constructs and initializes a <code>Line2D</code> from the | ||
| 260 | * specified coordinates. | ||
| 261 | * @param x1 the X coordinate of the start point | ||
| 262 | * @param y1 the Y coordinate of the start point | ||
| 263 | * @param x2 the X coordinate of the end point | ||
| 264 | * @param y2 the Y coordinate of the end point | ||
| 265 | * @since 1.2 | ||
| 266 | */ | ||
| 267 | public Double(double x1, double y1, double x2, double y2) { | ||
| 268 | setLine(x1, y1, x2, y2); | ||
| 269 | } | ||
| 270 | |||
| 271 | /** | ||
| 272 | * Constructs and initializes a <code>Line2D</code> from the | ||
| 273 | * specified <code>Point2D</code> objects. | ||
| 274 | * @param p1 the start <code>Point2D</code> of this line segment | ||
| 275 | * @param p2 the end <code>Point2D</code> of this line segment | ||
| 276 | * @since 1.2 | ||
| 277 | */ | ||
| 278 | public Double(Point2D p1, Point2D p2) { | ||
| 279 | setLine(p1, p2); | ||
| 280 | } | ||
| 281 | |||
| 282 | /** | ||
| 283 | * {@inheritDoc} | ||
| 284 | * @since 1.2 | ||
| 285 | */ | ||
| 286 | public double getX1() { | ||
| 287 | return x1; | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * {@inheritDoc} | ||
| 292 | * @since 1.2 | ||
| 293 | */ | ||
| 294 | public double getY1() { | ||
| 295 | return y1; | ||
| 296 | } | ||
| 297 | |||
| 298 | /** | ||
| 299 | * {@inheritDoc} | ||
| 300 | * @since 1.2 | ||
| 301 | */ | ||
| 302 | public Point2D getP1() { | ||
| 303 | return new Point2D.Double(x1, y1); | ||
| 304 | } | ||
| 305 | |||
| 306 | /** | ||
| 307 | * {@inheritDoc} | ||
| 308 | * @since 1.2 | ||
| 309 | */ | ||
| 310 | public double getX2() { | ||
| 311 | return x2; | ||
| 312 | } | ||
| 313 | |||
| 314 | /** | ||
| 315 | * {@inheritDoc} | ||
| 316 | * @since 1.2 | ||
| 317 | */ | ||
| 318 | public double getY2() { | ||
| 319 | return y2; | ||
| 320 | } | ||
| 321 | |||
| 322 | /** | ||
| 323 | * {@inheritDoc} | ||
| 324 | * @since 1.2 | ||
| 325 | */ | ||
| 326 | public Point2D getP2() { | ||
| 327 | return new Point2D.Double(x2, y2); | ||
| 328 | } | ||
| 329 | |||
| 330 | /** | ||
| 331 | * {@inheritDoc} | ||
| 332 | * @since 1.2 | ||
| 333 | */ | ||
| 334 | public void setLine(double x1, double y1, double x2, double y2) { | ||
| 335 | this.x1 = x1; | ||
| 336 | this.y1 = y1; | ||
| 337 | this.x2 = x2; | ||
| 338 | this.y2 = y2; | ||
| 339 | } | ||
| 340 | |||
| 341 | /** | ||
| 342 | * {@inheritDoc} | ||
| 343 | * @since 1.2 | ||
| 344 | */ | ||
| 345 | public Rectangle2D getBounds2D() { | ||
| 346 | double x, y, w, h; | ||
| 347 | if (x1 < x2) { | ||
| 348 | x = x1; | ||
| 349 | w = x2 - x1; | ||
| 350 | } else { | ||
| 351 | x = x2; | ||
| 352 | w = x1 - x2; | ||
| 353 | } | ||
| 354 | if (y1 < y2) { | ||
| 355 | y = y1; | ||
| 356 | h = y2 - y1; | ||
| 357 | } else { | ||
| 358 | y = y2; | ||
| 359 | h = y1 - y2; | ||
| 360 | } | ||
| 361 | return new Rectangle2D.Double(x, y, w, h); | ||
| 362 | } | ||
| 363 | |||
| 364 | /* | ||
| 365 | * JDK 1.6 serialVersionUID | ||
| 366 | */ | ||
| 367 | private static final long serialVersionUID = 7979627399746467499L; | ||
| 368 | } | ||
| 369 | |||
| 370 | /** | ||
| 371 | * This is an abstract class that cannot be instantiated directly. | ||
| 372 | * Type-specific implementation subclasses are available for | ||
| 373 | * instantiation and provide a number of formats for storing | ||
| 374 | * the information necessary to satisfy the various accessory | ||
| 375 | * methods below. | ||
| 376 | * | ||
| 377 | * @see java.awt.geom.Line2D.Float | ||
| 378 | * @see java.awt.geom.Line2D.Double | ||
| 379 | * @since 1.2 | ||
| 380 | */ | ||
| 381 | protected Line2D() { | ||
| 382 | } | ||
| 383 | |||
| 384 | /** | ||
| 385 | * Returns the X coordinate of the start point in double precision. | ||
| 386 | * @return the X coordinate of the start point of this | ||
| 387 | * {@code Line2D} object. | ||
| 388 | * @since 1.2 | ||
| 389 | */ | ||
| 390 | public abstract double getX1(); | ||
| 391 | |||
| 392 | /** | ||
| 393 | * Returns the Y coordinate of the start point in double precision. | ||
| 394 | * @return the Y coordinate of the start point of this | ||
| 395 | * {@code Line2D} object. | ||
| 396 | * @since 1.2 | ||
| 397 | */ | ||
| 398 | public abstract double getY1(); | ||
| 399 | |||
| 400 | /** | ||
| 401 | * Returns the start <code>Point2D</code> of this <code>Line2D</code>. | ||
| 402 | * @return the start <code>Point2D</code> of this <code>Line2D</code>. | ||
| 403 | * @since 1.2 | ||
| 404 | */ | ||
| 405 | public abstract Point2D getP1(); | ||
| 406 | |||
| 407 | /** | ||
| 408 | * Returns the X coordinate of the end point in double precision. | ||
| 409 | * @return the X coordinate of the end point of this | ||
| 410 | * {@code Line2D} object. | ||
| 411 | * @since 1.2 | ||
| 412 | */ | ||
| 413 | public abstract double getX2(); | ||
| 414 | |||
| 415 | /** | ||
| 416 | * Returns the Y coordinate of the end point in double precision. | ||
| 417 | * @return the Y coordinate of the end point of this | ||
| 418 | * {@code Line2D} object. | ||
| 419 | * @since 1.2 | ||
| 420 | */ | ||
| 421 | public abstract double getY2(); | ||
| 422 | |||
| 423 | /** | ||
| 424 | * Returns the end <code>Point2D</code> of this <code>Line2D</code>. | ||
| 425 | * @return the end <code>Point2D</code> of this <code>Line2D</code>. | ||
| 426 | * @since 1.2 | ||
| 427 | */ | ||
| 428 | public abstract Point2D getP2(); | ||
| 429 | |||
| 430 | /** | ||
| 431 | * Sets the location of the end points of this <code>Line2D</code> to | ||
| 432 | * the specified double coordinates. | ||
| 433 | * @param x1 the X coordinate of the start point | ||
| 434 | * @param y1 the Y coordinate of the start point | ||
| 435 | * @param x2 the X coordinate of the end point | ||
| 436 | * @param y2 the Y coordinate of the end point | ||
| 437 | * @since 1.2 | ||
| 438 | */ | ||
| 439 | public abstract void setLine(double x1, double y1, double x2, double y2); | ||
| 440 | |||
| 441 | /** | ||
| 442 | * Sets the location of the end points of this <code>Line2D</code> to | ||
| 443 | * the specified <code>Point2D</code> coordinates. | ||
| 444 | * @param p1 the start <code>Point2D</code> of the line segment | ||
| 445 | * @param p2 the end <code>Point2D</code> of the line segment | ||
| 446 | * @since 1.2 | ||
| 447 | */ | ||
| 448 | public void setLine(Point2D p1, Point2D p2) { | ||
| 449 | setLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()); | ||
| 450 | } | ||
| 451 | |||
| 452 | /** | ||
| 453 | * Sets the location of the end points of this <code>Line2D</code> to | ||
| 454 | * the same as those end points of the specified <code>Line2D</code>. | ||
| 455 | * @param l the specified <code>Line2D</code> | ||
| 456 | * @since 1.2 | ||
| 457 | */ | ||
| 458 | public void setLine(Line2D l) { | ||
| 459 | setLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); | ||
| 460 | } | ||
| 461 | |||
| 462 | /** | ||
| 463 | * Returns an indicator of where the specified point | ||
| 464 | * {@code (px,py)} lies with respect to the line segment from | ||
| 465 | * {@code (x1,y1)} to {@code (x2,y2)}. | ||
| 466 | * The return value can be either 1, -1, or 0 and indicates | ||
| 467 | * in which direction the specified line must pivot around its | ||
| 468 | * first end point, {@code (x1,y1)}, in order to point at the | ||
| 469 | * specified point {@code (px,py)}. | ||
| 470 | * <p>A return value of 1 indicates that the line segment must | ||
| 471 | * turn in the direction that takes the positive X axis towards | ||
| 472 | * the negative Y axis. In the default coordinate system used by | ||
| 473 | * Java 2D, this direction is counterclockwise. | ||
| 474 | * <p>A return value of -1 indicates that the line segment must | ||
| 475 | * turn in the direction that takes the positive X axis towards | ||
| 476 | * the positive Y axis. In the default coordinate system, this | ||
| 477 | * direction is clockwise. | ||
| 478 | * <p>A return value of 0 indicates that the point lies | ||
| 479 | * exactly on the line segment. Note that an indicator value | ||
| 480 | * of 0 is rare and not useful for determining colinearity | ||
| 481 | * because of floating point rounding issues. | ||
| 482 | * <p>If the point is colinear with the line segment, but | ||
| 483 | * not between the end points, then the value will be -1 if the point | ||
| 484 | * lies "beyond {@code (x1,y1)}" or 1 if the point lies | ||
| 485 | * "beyond {@code (x2,y2)}". | ||
| 486 | * | ||
| 487 | * @param x1 the X coordinate of the start point of the | ||
| 488 | * specified line segment | ||
| 489 | * @param y1 the Y coordinate of the start point of the | ||
| 490 | * specified line segment | ||
| 491 | * @param x2 the X coordinate of the end point of the | ||
| 492 | * specified line segment | ||
| 493 | * @param y2 the Y coordinate of the end point of the | ||
| 494 | * specified line segment | ||
| 495 | * @param px the X coordinate of the specified point to be | ||
| 496 | * compared with the specified line segment | ||
| 497 | * @param py the Y coordinate of the specified point to be | ||
| 498 | * compared with the specified line segment | ||
| 499 | * @return an integer that indicates the position of the third specified | ||
| 500 | * coordinates with respect to the line segment formed | ||
| 501 | * by the first two specified coordinates. | ||
| 502 | * @since 1.2 | ||
| 503 | */ | ||
| 504 | public static int relativeCCW(double x1, double y1, | ||
| 505 | double x2, double y2, | ||
| 506 | double px, double py) | ||
| 507 | { | ||
| 508 | x2 -= x1; | ||
| 509 | y2 -= y1; | ||
| 510 | px -= x1; | ||
| 511 | py -= y1; | ||
| 512 | double ccw = px * y2 - py * x2; | ||
| 513 | if (ccw == 0.0) { | ||
| 514 | // The point is colinear, classify based on which side of | ||
| 515 | // the segment the point falls on. We can calculate a | ||
| 516 | // relative value using the projection of px,py onto the | ||
| 517 | // segment - a negative value indicates the point projects | ||
| 518 | // outside of the segment in the direction of the particular | ||
| 519 | // endpoint used as the origin for the projection. | ||
| 520 | ccw = px * x2 + py * y2; | ||
| 521 | if (ccw > 0.0) { | ||
| 522 | // Reverse the projection to be relative to the original x2,y2 | ||
| 523 | // x2 and y2 are simply negated. | ||
| 524 | // px and py need to have (x2 - x1) or (y2 - y1) subtracted | ||
| 525 | // from them (based on the original values) | ||
| 526 | // Since we really want to get a positive answer when the | ||
| 527 | // point is "beyond (x2,y2)", then we want to calculate | ||
| 528 | // the inverse anyway - thus we leave x2 & y2 negated. | ||
| 529 | px -= x2; | ||
| 530 | py -= y2; | ||
| 531 | ccw = px * x2 + py * y2; | ||
| 532 | if (ccw < 0.0) { | ||
| 533 | ccw = 0.0; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | } | ||
| 537 | return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0); | ||
| 538 | } | ||
| 539 | |||
| 540 | /** | ||
| 541 | * Returns an indicator of where the specified point | ||
| 542 | * {@code (px,py)} lies with respect to this line segment. | ||
| 543 | * See the method comments of | ||
| 544 | * {@link #relativeCCW(double, double, double, double, double, double)} | ||
| 545 | * to interpret the return value. | ||
| 546 | * @param px the X coordinate of the specified point | ||
| 547 | * to be compared with this <code>Line2D</code> | ||
| 548 | * @param py the Y coordinate of the specified point | ||
| 549 | * to be compared with this <code>Line2D</code> | ||
| 550 | * @return an integer that indicates the position of the specified | ||
| 551 | * coordinates with respect to this <code>Line2D</code> | ||
| 552 | * @see #relativeCCW(double, double, double, double, double, double) | ||
| 553 | * @since 1.2 | ||
| 554 | */ | ||
| 555 | public int relativeCCW(double px, double py) { | ||
| 556 | return relativeCCW(getX1(), getY1(), getX2(), getY2(), px, py); | ||
| 557 | } | ||
| 558 | |||
| 559 | /** | ||
| 560 | * Returns an indicator of where the specified <code>Point2D</code> | ||
| 561 | * lies with respect to this line segment. | ||
| 562 | * See the method comments of | ||
| 563 | * {@link #relativeCCW(double, double, double, double, double, double)} | ||
| 564 | * to interpret the return value. | ||
| 565 | * @param p the specified <code>Point2D</code> to be compared | ||
| 566 | * with this <code>Line2D</code> | ||
| 567 | * @return an integer that indicates the position of the specified | ||
| 568 | * <code>Point2D</code> with respect to this <code>Line2D</code> | ||
| 569 | * @see #relativeCCW(double, double, double, double, double, double) | ||
| 570 | * @since 1.2 | ||
| 571 | */ | ||
| 572 | public int relativeCCW(Point2D p) { | ||
| 573 | return relativeCCW(getX1(), getY1(), getX2(), getY2(), | ||
| 574 | p.getX(), p.getY()); | ||
| 575 | } | ||
| 576 | |||
| 577 | /** | ||
| 578 | * Tests if the line segment from {@code (x1,y1)} to | ||
| 579 | * {@code (x2,y2)} intersects the line segment from {@code (x3,y3)} | ||
| 580 | * to {@code (x4,y4)}. | ||
| 581 | * | ||
| 582 | * @param x1 the X coordinate of the start point of the first | ||
| 583 | * specified line segment | ||
| 584 | * @param y1 the Y coordinate of the start point of the first | ||
| 585 | * specified line segment | ||
| 586 | * @param x2 the X coordinate of the end point of the first | ||
| 587 | * specified line segment | ||
| 588 | * @param y2 the Y coordinate of the end point of the first | ||
| 589 | * specified line segment | ||
| 590 | * @param x3 the X coordinate of the start point of the second | ||
| 591 | * specified line segment | ||
| 592 | * @param y3 the Y coordinate of the start point of the second | ||
| 593 | * specified line segment | ||
| 594 | * @param x4 the X coordinate of the end point of the second | ||
| 595 | * specified line segment | ||
| 596 | * @param y4 the Y coordinate of the end point of the second | ||
| 597 | * specified line segment | ||
| 598 | * @return <code>true</code> if the first specified line segment | ||
| 599 | * and the second specified line segment intersect | ||
| 600 | * each other; <code>false</code> otherwise. | ||
| 601 | * @since 1.2 | ||
| 602 | */ | ||
| 603 | public static boolean linesIntersect(double x1, double y1, | ||
| 604 | double x2, double y2, | ||
| 605 | double x3, double y3, | ||
| 606 | double x4, double y4) | ||
| 607 | { | ||
| 608 | return ((relativeCCW(x1, y1, x2, y2, x3, y3) * | ||
| 609 | relativeCCW(x1, y1, x2, y2, x4, y4) <= 0) | ||
| 610 | && (relativeCCW(x3, y3, x4, y4, x1, y1) * | ||
| 611 | relativeCCW(x3, y3, x4, y4, x2, y2) <= 0)); | ||
| 612 | } | ||
| 613 | |||
| 614 | /** | ||
| 615 | * Tests if the line segment from {@code (x1,y1)} to | ||
| 616 | * {@code (x2,y2)} intersects this line segment. | ||
| 617 | * | ||
| 618 | * @param x1 the X coordinate of the start point of the | ||
| 619 | * specified line segment | ||
| 620 | * @param y1 the Y coordinate of the start point of the | ||
| 621 | * specified line segment | ||
| 622 | * @param x2 the X coordinate of the end point of the | ||
| 623 | * specified line segment | ||
| 624 | * @param y2 the Y coordinate of the end point of the | ||
| 625 | * specified line segment | ||
| 626 | * @return <true> if this line segment and the specified line segment | ||
| 627 | * intersect each other; <code>false</code> otherwise. | ||
| 628 | * @since 1.2 | ||
| 629 | */ | ||
| 630 | public boolean intersectsLine(double x1, double y1, double x2, double y2) { | ||
| 631 | return linesIntersect(x1, y1, x2, y2, | ||
| 632 | getX1(), getY1(), getX2(), getY2()); | ||
| 633 | } | ||
| 634 | |||
| 635 | /** | ||
| 636 | * Tests if the specified line segment intersects this line segment. | ||
| 637 | * @param l the specified <code>Line2D</code> | ||
| 638 | * @return <code>true</code> if this line segment and the specified line | ||
| 639 | * segment intersect each other; | ||
| 640 | * <code>false</code> otherwise. | ||
| 641 | * @since 1.2 | ||
| 642 | */ | ||
| 643 | public boolean intersectsLine(Line2D l) { | ||
| 644 | return linesIntersect(l.getX1(), l.getY1(), l.getX2(), l.getY2(), | ||
| 645 | getX1(), getY1(), getX2(), getY2()); | ||
| 646 | } | ||
| 647 | |||
| 648 | /** | ||
| 649 | * Returns the square of the distance from a point to a line segment. | ||
| 650 | * The distance measured is the distance between the specified | ||
| 651 | * point and the closest point between the specified end points. | ||
| 652 | * If the specified point intersects the line segment in between the | ||
| 653 | * end points, this method returns 0.0. | ||
| 654 | * | ||
| 655 | * @param x1 the X coordinate of the start point of the | ||
| 656 | * specified line segment | ||
| 657 | * @param y1 the Y coordinate of the start point of the | ||
| 658 | * specified line segment | ||
| 659 | * @param x2 the X coordinate of the end point of the | ||
| 660 | * specified line segment | ||
| 661 | * @param y2 the Y coordinate of the end point of the | ||
| 662 | * specified line segment | ||
| 663 | * @param px the X coordinate of the specified point being | ||
| 664 | * measured against the specified line segment | ||
| 665 | * @param py the Y coordinate of the specified point being | ||
| 666 | * measured against the specified line segment | ||
| 667 | * @return a double value that is the square of the distance from the | ||
| 668 | * specified point to the specified line segment. | ||
| 669 | * @see #ptLineDistSq(double, double, double, double, double, double) | ||
| 670 | * @since 1.2 | ||
| 671 | */ | ||
| 672 | public static double ptSegDistSq(double x1, double y1, | ||
| 673 | double x2, double y2, | ||
| 674 | double px, double py) | ||
| 675 | { | ||
| 676 | // Adjust vectors relative to x1,y1 | ||
| 677 | // x2,y2 becomes relative vector from x1,y1 to end of segment | ||
| 678 | x2 -= x1; | ||
| 679 | y2 -= y1; | ||
| 680 | // px,py becomes relative vector from x1,y1 to test point | ||
| 681 | px -= x1; | ||
| 682 | py -= y1; | ||
| 683 | double dotprod = px * x2 + py * y2; | ||
| 684 | double projlenSq; | ||
| 685 | if (dotprod <= 0.0) { | ||
| 686 | // px,py is on the side of x1,y1 away from x2,y2 | ||
| 687 | // distance to segment is length of px,py vector | ||
| 688 | // "length of its (clipped) projection" is now 0.0 | ||
| 689 | projlenSq = 0.0; | ||
| 690 | } else { | ||
| 691 | // switch to backwards vectors relative to x2,y2 | ||
| 692 | // x2,y2 are already the negative of x1,y1=>x2,y2 | ||
| 693 | // to get px,py to be the negative of px,py=>x2,y2 | ||
| 694 | // the dot product of two negated vectors is the same | ||
| 695 | // as the dot product of the two normal vectors | ||
| 696 | px = x2 - px; | ||
| 697 | py = y2 - py; | ||
| 698 | dotprod = px * x2 + py * y2; | ||
| 699 | if (dotprod <= 0.0) { | ||
| 700 | // px,py is on the side of x2,y2 away from x1,y1 | ||
| 701 | // distance to segment is length of (backwards) px,py vector | ||
| 702 | // "length of its (clipped) projection" is now 0.0 | ||
| 703 | projlenSq = 0.0; | ||
| 704 | } else { | ||
| 705 | // px,py is between x1,y1 and x2,y2 | ||
| 706 | // dotprod is the length of the px,py vector | ||
| 707 | // projected on the x2,y2=>x1,y1 vector times the | ||
| 708 | // length of the x2,y2=>x1,y1 vector | ||
| 709 | projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2); | ||
| 710 | } | ||
| 711 | } | ||
| 712 | // Distance to line is now the length of the relative point | ||
| 713 | // vector minus the length of its projection onto the line | ||
| 714 | // (which is zero if the projection falls outside the range | ||
| 715 | // of the line segment). | ||
| 716 | double lenSq = px * px + py * py - projlenSq; | ||
| 717 | if (lenSq < 0) { | ||
| 718 | lenSq = 0; | ||
| 719 | } | ||
| 720 | return lenSq; | ||
| 721 | } | ||
| 722 | |||
| 723 | /** | ||
| 724 | * Returns the distance from a point to a line segment. | ||
| 725 | * The distance measured is the distance between the specified | ||
| 726 | * point and the closest point between the specified end points. | ||
| 727 | * If the specified point intersects the line segment in between the | ||
| 728 | * end points, this method returns 0.0. | ||
| 729 | * | ||
| 730 | * @param x1 the X coordinate of the start point of the | ||
| 731 | * specified line segment | ||
| 732 | * @param y1 the Y coordinate of the start point of the | ||
| 733 | * specified line segment | ||
| 734 | * @param x2 the X coordinate of the end point of the | ||
| 735 | * specified line segment | ||
| 736 | * @param y2 the Y coordinate of the end point of the | ||
| 737 | * specified line segment | ||
| 738 | * @param px the X coordinate of the specified point being | ||
| 739 | * measured against the specified line segment | ||
| 740 | * @param py the Y coordinate of the specified point being | ||
| 741 | * measured against the specified line segment | ||
| 742 | * @return a double value that is the distance from the specified point | ||
| 743 | * to the specified line segment. | ||
| 744 | * @see #ptLineDist(double, double, double, double, double, double) | ||
| 745 | * @since 1.2 | ||
| 746 | */ | ||
| 747 | public static double ptSegDist(double x1, double y1, | ||
| 748 | double x2, double y2, | ||
| 749 | double px, double py) | ||
| 750 | { | ||
| 751 | return Math.sqrt(ptSegDistSq(x1, y1, x2, y2, px, py)); | ||
| 752 | } | ||
| 753 | |||
| 754 | /** | ||
| 755 | * Returns the square of the distance from a point to this line segment. | ||
| 756 | * The distance measured is the distance between the specified | ||
| 757 | * point and the closest point between the current line's end points. | ||
| 758 | * If the specified point intersects the line segment in between the | ||
| 759 | * end points, this method returns 0.0. | ||
| 760 | * | ||
| 761 | * @param px the X coordinate of the specified point being | ||
| 762 | * measured against this line segment | ||
| 763 | * @param py the Y coordinate of the specified point being | ||
| 764 | * measured against this line segment | ||
| 765 | * @return a double value that is the square of the distance from the | ||
| 766 | * specified point to the current line segment. | ||
| 767 | * @see #ptLineDistSq(double, double) | ||
| 768 | * @since 1.2 | ||
| 769 | */ | ||
| 770 | public double ptSegDistSq(double px, double py) { | ||
| 771 | return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), px, py); | ||
| 772 | } | ||
| 773 | |||
| 774 | /** | ||
| 775 | * Returns the square of the distance from a <code>Point2D</code> to | ||
| 776 | * this line segment. | ||
| 777 | * The distance measured is the distance between the specified | ||
| 778 | * point and the closest point between the current line's end points. | ||
| 779 | * If the specified point intersects the line segment in between the | ||
| 780 | * end points, this method returns 0.0. | ||
| 781 | * @param pt the specified <code>Point2D</code> being measured against | ||
| 782 | * this line segment. | ||
| 783 | * @return a double value that is the square of the distance from the | ||
| 784 | * specified <code>Point2D</code> to the current | ||
| 785 | * line segment. | ||
| 786 | * @see #ptLineDistSq(Point2D) | ||
| 787 | * @since 1.2 | ||
| 788 | */ | ||
| 789 | public double ptSegDistSq(Point2D pt) { | ||
| 790 | return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), | ||
| 791 | pt.getX(), pt.getY()); | ||
| 792 | } | ||
| 793 | |||
| 794 | /** | ||
| 795 | * Returns the distance from a point to this line segment. | ||
| 796 | * The distance measured is the distance between the specified | ||
| 797 | * point and the closest point between the current line's end points. | ||
| 798 | * If the specified point intersects the line segment in between the | ||
| 799 | * end points, this method returns 0.0. | ||
| 800 | * | ||
| 801 | * @param px the X coordinate of the specified point being | ||
| 802 | * measured against this line segment | ||
| 803 | * @param py the Y coordinate of the specified point being | ||
| 804 | * measured against this line segment | ||
| 805 | * @return a double value that is the distance from the specified | ||
| 806 | * point to the current line segment. | ||
| 807 | * @see #ptLineDist(double, double) | ||
| 808 | * @since 1.2 | ||
| 809 | */ | ||
| 810 | public double ptSegDist(double px, double py) { | ||
| 811 | return ptSegDist(getX1(), getY1(), getX2(), getY2(), px, py); | ||
| 812 | } | ||
| 813 | |||
| 814 | /** | ||
| 815 | * Returns the distance from a <code>Point2D</code> to this line | ||
| 816 | * segment. | ||
| 817 | * The distance measured is the distance between the specified | ||
| 818 | * point and the closest point between the current line's end points. | ||
| 819 | * If the specified point intersects the line segment in between the | ||
| 820 | * end points, this method returns 0.0. | ||
| 821 | * @param pt the specified <code>Point2D</code> being measured | ||
| 822 | * against this line segment | ||
| 823 | * @return a double value that is the distance from the specified | ||
| 824 | * <code>Point2D</code> to the current line | ||
| 825 | * segment. | ||
| 826 | * @see #ptLineDist(Point2D) | ||
| 827 | * @since 1.2 | ||
| 828 | */ | ||
| 829 | public double ptSegDist(Point2D pt) { | ||
| 830 | return ptSegDist(getX1(), getY1(), getX2(), getY2(), | ||
| 831 | pt.getX(), pt.getY()); | ||
| 832 | } | ||
| 833 | |||
| 834 | /** | ||
| 835 | * Returns the square of the distance from a point to a line. | ||
| 836 | * The distance measured is the distance between the specified | ||
| 837 | * point and the closest point on the infinitely-extended line | ||
| 838 | * defined by the specified coordinates. If the specified point | ||
| 839 | * intersects the line, this method returns 0.0. | ||
| 840 | * | ||
| 841 | * @param x1 the X coordinate of the start point of the specified line | ||
| 842 | * @param y1 the Y coordinate of the start point of the specified line | ||
| 843 | * @param x2 the X coordinate of the end point of the specified line | ||
| 844 | * @param y2 the Y coordinate of the end point of the specified line | ||
| 845 | * @param px the X coordinate of the specified point being | ||
| 846 | * measured against the specified line | ||
| 847 | * @param py the Y coordinate of the specified point being | ||
| 848 | * measured against the specified line | ||
| 849 | * @return a double value that is the square of the distance from the | ||
| 850 | * specified point to the specified line. | ||
| 851 | * @see #ptSegDistSq(double, double, double, double, double, double) | ||
| 852 | * @since 1.2 | ||
| 853 | */ | ||
| 854 | public static double ptLineDistSq(double x1, double y1, | ||
| 855 | double x2, double y2, | ||
| 856 | double px, double py) | ||
| 857 | { | ||
| 858 | // Adjust vectors relative to x1,y1 | ||
| 859 | // x2,y2 becomes relative vector from x1,y1 to end of segment | ||
| 860 | x2 -= x1; | ||
| 861 | y2 -= y1; | ||
| 862 | // px,py becomes relative vector from x1,y1 to test point | ||
| 863 | px -= x1; | ||
| 864 | py -= y1; | ||
| 865 | double dotprod = px * x2 + py * y2; | ||
| 866 | // dotprod is the length of the px,py vector | ||
| 867 | // projected on the x1,y1=>x2,y2 vector times the | ||
| 868 | // length of the x1,y1=>x2,y2 vector | ||
| 869 | double projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2); | ||
| 870 | // Distance to line is now the length of the relative point | ||
| 871 | // vector minus the length of its projection onto the line | ||
| 872 | double lenSq = px * px + py * py - projlenSq; | ||
| 873 | if (lenSq < 0) { | ||
| 874 | lenSq = 0; | ||
| 875 | } | ||
| 876 | return lenSq; | ||
| 877 | } | ||
| 878 | |||
| 879 | /** | ||
| 880 | * Returns the distance from a point to a line. | ||
| 881 | * The distance measured is the distance between the specified | ||
| 882 | * point and the closest point on the infinitely-extended line | ||
| 883 | * defined by the specified coordinates. If the specified point | ||
| 884 | * intersects the line, this method returns 0.0. | ||
| 885 | * | ||
| 886 | * @param x1 the X coordinate of the start point of the specified line | ||
| 887 | * @param y1 the Y coordinate of the start point of the specified line | ||
| 888 | * @param x2 the X coordinate of the end point of the specified line | ||
| 889 | * @param y2 the Y coordinate of the end point of the specified line | ||
| 890 | * @param px the X coordinate of the specified point being | ||
| 891 | * measured against the specified line | ||
| 892 | * @param py the Y coordinate of the specified point being | ||
| 893 | * measured against the specified line | ||
| 894 | * @return a double value that is the distance from the specified | ||
| 895 | * point to the specified line. | ||
| 896 | * @see #ptSegDist(double, double, double, double, double, double) | ||
| 897 | * @since 1.2 | ||
| 898 | */ | ||
| 899 | public static double ptLineDist(double x1, double y1, | ||
| 900 | double x2, double y2, | ||
| 901 | double px, double py) | ||
| 902 | { | ||
| 903 | return Math.sqrt(ptLineDistSq(x1, y1, x2, y2, px, py)); | ||
| 904 | } | ||
| 905 | |||
| 906 | /** | ||
| 907 | * Returns the square of the distance from a point to this line. | ||
| 908 | * The distance measured is the distance between the specified | ||
| 909 | * point and the closest point on the infinitely-extended line | ||
| 910 | * defined by this <code>Line2D</code>. If the specified point | ||
| 911 | * intersects the line, this method returns 0.0. | ||
| 912 | * | ||
| 913 | * @param px the X coordinate of the specified point being | ||
| 914 | * measured against this line | ||
| 915 | * @param py the Y coordinate of the specified point being | ||
| 916 | * measured against this line | ||
| 917 | * @return a double value that is the square of the distance from a | ||
| 918 | * specified point to the current line. | ||
| 919 | * @see #ptSegDistSq(double, double) | ||
| 920 | * @since 1.2 | ||
| 921 | */ | ||
| 922 | public double ptLineDistSq(double px, double py) { | ||
| 923 | return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), px, py); | ||
| 924 | } | ||
| 925 | |||
| 926 | /** | ||
| 927 | * Returns the square of the distance from a specified | ||
| 928 | * <code>Point2D</code> to this line. | ||
| 929 | * The distance measured is the distance between the specified | ||
| 930 | * point and the closest point on the infinitely-extended line | ||
| 931 | * defined by this <code>Line2D</code>. If the specified point | ||
| 932 | * intersects the line, this method returns 0.0. | ||
| 933 | * @param pt the specified <code>Point2D</code> being measured | ||
| 934 | * against this line | ||
| 935 | * @return a double value that is the square of the distance from a | ||
| 936 | * specified <code>Point2D</code> to the current | ||
| 937 | * line. | ||
| 938 | * @see #ptSegDistSq(Point2D) | ||
| 939 | * @since 1.2 | ||
| 940 | */ | ||
| 941 | public double ptLineDistSq(Point2D pt) { | ||
| 942 | return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), | ||
| 943 | pt.getX(), pt.getY()); | ||
| 944 | } | ||
| 945 | |||
| 946 | /** | ||
| 947 | * Returns the distance from a point to this line. | ||
| 948 | * The distance measured is the distance between the specified | ||
| 949 | * point and the closest point on the infinitely-extended line | ||
| 950 | * defined by this <code>Line2D</code>. If the specified point | ||
| 951 | * intersects the line, this method returns 0.0. | ||
| 952 | * | ||
| 953 | * @param px the X coordinate of the specified point being | ||
| 954 | * measured against this line | ||
| 955 | * @param py the Y coordinate of the specified point being | ||
| 956 | * measured against this line | ||
| 957 | * @return a double value that is the distance from a specified point | ||
| 958 | * to the current line. | ||
| 959 | * @see #ptSegDist(double, double) | ||
| 960 | * @since 1.2 | ||
| 961 | */ | ||
| 962 | public double ptLineDist(double px, double py) { | ||
| 963 | return ptLineDist(getX1(), getY1(), getX2(), getY2(), px, py); | ||
| 964 | } | ||
| 965 | |||
| 966 | /** | ||
| 967 | * Returns the distance from a <code>Point2D</code> to this line. | ||
| 968 | * The distance measured is the distance between the specified | ||
| 969 | * point and the closest point on the infinitely-extended line | ||
| 970 | * defined by this <code>Line2D</code>. If the specified point | ||
| 971 | * intersects the line, this method returns 0.0. | ||
| 972 | * @param pt the specified <code>Point2D</code> being measured | ||
| 973 | * @return a double value that is the distance from a specified | ||
| 974 | * <code>Point2D</code> to the current line. | ||
| 975 | * @see #ptSegDist(Point2D) | ||
| 976 | * @since 1.2 | ||
| 977 | */ | ||
| 978 | public double ptLineDist(Point2D pt) { | ||
| 979 | return ptLineDist(getX1(), getY1(), getX2(), getY2(), | ||
| 980 | pt.getX(), pt.getY()); | ||
| 981 | } | ||
| 982 | |||
| 983 | /** | ||
| 984 | * Tests if a specified coordinate is inside the boundary of this | ||
| 985 | * <code>Line2D</code>. This method is required to implement the | ||
| 986 | * {@link Shape} interface, but in the case of <code>Line2D</code> | ||
| 987 | * objects it always returns <code>false</code> since a line contains | ||
| 988 | * no area. | ||
| 989 | * @param x the X coordinate of the specified point to be tested | ||
| 990 | * @param y the Y coordinate of the specified point to be tested | ||
| 991 | * @return <code>false</code> because a <code>Line2D</code> contains | ||
| 992 | * no area. | ||
| 993 | * @since 1.2 | ||
| 994 | */ | ||
| 995 | public boolean contains(double x, double y) { | ||
| 996 | return false; | ||
| 997 | } | ||
| 998 | |||
| 999 | /** | ||
| 1000 | * Tests if a given <code>Point2D</code> is inside the boundary of | ||
| 1001 | * this <code>Line2D</code>. | ||
| 1002 | * This method is required to implement the {@link Shape} interface, | ||
| 1003 | * but in the case of <code>Line2D</code> objects it always returns | ||
| 1004 | * <code>false</code> since a line contains no area. | ||
| 1005 | * @param p the specified <code>Point2D</code> to be tested | ||
| 1006 | * @return <code>false</code> because a <code>Line2D</code> contains | ||
| 1007 | * no area. | ||
| 1008 | * @since 1.2 | ||
| 1009 | */ | ||
| 1010 | public boolean contains(Point2D p) { | ||
| 1011 | return false; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | /** | ||
| 1015 | * {@inheritDoc} | ||
| 1016 | * @since 1.2 | ||
| 1017 | */ | ||
| 1018 | public boolean intersects(double x, double y, double w, double h) { | ||
| 1019 | return intersects(new Rectangle2D.Double(x, y, w, h)); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | /** | ||
| 1023 | * {@inheritDoc} | ||
| 1024 | * @since 1.2 | ||
| 1025 | */ | ||
| 1026 | public boolean intersects(Rectangle2D r) { | ||
| 1027 | return r.intersectsLine(getX1(), getY1(), getX2(), getY2()); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | /** | ||
| 1031 | * Tests if the interior of this <code>Line2D</code> entirely contains | ||
| 1032 | * the specified set of rectangular coordinates. | ||
| 1033 | * This method is required to implement the <code>Shape</code> interface, | ||
| 1034 | * but in the case of <code>Line2D</code> objects it always returns | ||
| 1035 | * false since a line contains no area. | ||
| 1036 | * @param x the X coordinate of the upper-left corner of the | ||
| 1037 | * specified rectangular area | ||
| 1038 | * @param y the Y coordinate of the upper-left corner of the | ||
| 1039 | * specified rectangular area | ||
| 1040 | * @param w the width of the specified rectangular area | ||
| 1041 | * @param h the height of the specified rectangular area | ||
| 1042 | * @return <code>false</code> because a <code>Line2D</code> contains | ||
| 1043 | * no area. | ||
| 1044 | * @since 1.2 | ||
| 1045 | */ | ||
| 1046 | public boolean contains(double x, double y, double w, double h) { | ||
| 1047 | return false; | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | /** | ||
| 1051 | * Tests if the interior of this <code>Line2D</code> entirely contains | ||
| 1052 | * the specified <code>Rectangle2D</code>. | ||
| 1053 | * This method is required to implement the <code>Shape</code> interface, | ||
| 1054 | * but in the case of <code>Line2D</code> objects it always returns | ||
| 1055 | * <code>false</code> since a line contains no area. | ||
| 1056 | * @param r the specified <code>Rectangle2D</code> to be tested | ||
| 1057 | * @return <code>false</code> because a <code>Line2D</code> contains | ||
| 1058 | * no area. | ||
| 1059 | * @since 1.2 | ||
| 1060 | */ | ||
| 1061 | public boolean contains(Rectangle2D r) { | ||
| 1062 | return false; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | /** | ||
| 1066 | * {@inheritDoc} | ||
| 1067 | * @since 1.2 | ||
| 1068 | */ | ||
| 1069 | public Rectangle getBounds() { | ||
| 1070 | return getBounds2D().getBounds(); | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | /** | ||
| 1074 | * Returns an iteration object that defines the boundary of this | ||
| 1075 | * <code>Line2D</code>. | ||
| 1076 | * The iterator for this class is not multi-threaded safe, | ||
| 1077 | * which means that this <code>Line2D</code> class does not | ||
| 1078 | * guarantee that modifications to the geometry of this | ||
| 1079 | * <code>Line2D</code> object do not affect any iterations of that | ||
| 1080 | * geometry that are already in process. | ||
| 1081 | * @param at the specified {@link AffineTransform} | ||
| 1082 | * @return a {@link PathIterator} that defines the boundary of this | ||
| 1083 | * <code>Line2D</code>. | ||
| 1084 | * @since 1.2 | ||
| 1085 | */ | ||
| 1086 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 1087 | return new LineIterator(this, at); | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | /** | ||
| 1091 | * Returns an iteration object that defines the boundary of this | ||
| 1092 | * flattened <code>Line2D</code>. | ||
| 1093 | * The iterator for this class is not multi-threaded safe, | ||
| 1094 | * which means that this <code>Line2D</code> class does not | ||
| 1095 | * guarantee that modifications to the geometry of this | ||
| 1096 | * <code>Line2D</code> object do not affect any iterations of that | ||
| 1097 | * geometry that are already in process. | ||
| 1098 | * @param at the specified <code>AffineTransform</code> | ||
| 1099 | * @param flatness the maximum amount that the control points for a | ||
| 1100 | * given curve can vary from colinear before a subdivided | ||
| 1101 | * curve is replaced by a straight line connecting the | ||
| 1102 | * end points. Since a <code>Line2D</code> object is | ||
| 1103 | * always flat, this parameter is ignored. | ||
| 1104 | * @return a <code>PathIterator</code> that defines the boundary of the | ||
| 1105 | * flattened <code>Line2D</code> | ||
| 1106 | * @since 1.2 | ||
| 1107 | */ | ||
| 1108 | public PathIterator getPathIterator(AffineTransform at, double flatness) { | ||
| 1109 | return new LineIterator(this, at); | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | /** | ||
| 1113 | * Creates a new object of the same class as this object. | ||
| 1114 | * | ||
| 1115 | * @return a clone of this instance. | ||
| 1116 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 1117 | * @see java.lang.Cloneable | ||
| 1118 | * @since 1.2 | ||
| 1119 | */ | ||
| 1120 | public Object clone() { | ||
| 1121 | try { | ||
| 1122 | return super.clone(); | ||
| 1123 | } catch (CloneNotSupportedException e) { | ||
| 1124 | // this shouldn't happen, since we are Cloneable | ||
| 1125 | throw new InternalError(); | ||
| 1126 | } | ||
| 1127 | } | ||
| 1128 | } | ||
Binary file not shown.
| @@ -0,0 +1,148 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A utility class to iterate over the path segments of a line segment | ||
| 32 | * through the PathIterator interface. | ||
| 33 | * | ||
| 34 | * @author Jim Graham | ||
| 35 | */ | ||
| 36 | class LineIterator implements PathIterator { | ||
| 37 | Line2D line; | ||
| 38 | AffineTransform affine; | ||
| 39 | int index; | ||
| 40 | |||
| 41 | LineIterator(Line2D l, AffineTransform at) { | ||
| 42 | this.line = l; | ||
| 43 | this.affine = at; | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Return the winding rule for determining the insideness of the | ||
| 48 | * path. | ||
| 49 | * @see #WIND_EVEN_ODD | ||
| 50 | * @see #WIND_NON_ZERO | ||
| 51 | */ | ||
| 52 | public int getWindingRule() { | ||
| 53 | return WIND_NON_ZERO; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Tests if there are more points to read. | ||
| 58 | * @return true if there are more points to read | ||
| 59 | */ | ||
| 60 | public boolean isDone() { | ||
| 61 | return (index > 1); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Moves the iterator to the next segment of the path forwards | ||
| 66 | * along the primary direction of traversal as long as there are | ||
| 67 | * more points in that direction. | ||
| 68 | */ | ||
| 69 | public void next() { | ||
| 70 | index++; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Returns the coordinates and type of the current path segment in | ||
| 75 | * the iteration. | ||
| 76 | * The return value is the path segment type: | ||
| 77 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 78 | * A float array of length 6 must be passed in and may be used to | ||
| 79 | * store the coordinates of the point(s). | ||
| 80 | * Each point is stored as a pair of float x,y coordinates. | ||
| 81 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 82 | * SEG_QUADTO will return two points, | ||
| 83 | * SEG_CUBICTO will return 3 points | ||
| 84 | * and SEG_CLOSE will not return any points. | ||
| 85 | * @see #SEG_MOVETO | ||
| 86 | * @see #SEG_LINETO | ||
| 87 | * @see #SEG_QUADTO | ||
| 88 | * @see #SEG_CUBICTO | ||
| 89 | * @see #SEG_CLOSE | ||
| 90 | */ | ||
| 91 | public int currentSegment(float[] coords) { | ||
| 92 | if (isDone()) { | ||
| 93 | throw new NoSuchElementException("line iterator out of bounds"); | ||
| 94 | } | ||
| 95 | int type; | ||
| 96 | if (index == 0) { | ||
| 97 | coords[0] = (float) line.getX1(); | ||
| 98 | coords[1] = (float) line.getY1(); | ||
| 99 | type = SEG_MOVETO; | ||
| 100 | } else { | ||
| 101 | coords[0] = (float) line.getX2(); | ||
| 102 | coords[1] = (float) line.getY2(); | ||
| 103 | type = SEG_LINETO; | ||
| 104 | } | ||
| 105 | if (affine != null) { | ||
| 106 | affine.transform(coords, 0, coords, 0, 1); | ||
| 107 | } | ||
| 108 | return type; | ||
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Returns the coordinates and type of the current path segment in | ||
| 113 | * the iteration. | ||
| 114 | * The return value is the path segment type: | ||
| 115 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 116 | * A double array of length 6 must be passed in and may be used to | ||
| 117 | * store the coordinates of the point(s). | ||
| 118 | * Each point is stored as a pair of double x,y coordinates. | ||
| 119 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 120 | * SEG_QUADTO will return two points, | ||
| 121 | * SEG_CUBICTO will return 3 points | ||
| 122 | * and SEG_CLOSE will not return any points. | ||
| 123 | * @see #SEG_MOVETO | ||
| 124 | * @see #SEG_LINETO | ||
| 125 | * @see #SEG_QUADTO | ||
| 126 | * @see #SEG_CUBICTO | ||
| 127 | * @see #SEG_CLOSE | ||
| 128 | */ | ||
| 129 | public int currentSegment(double[] coords) { | ||
| 130 | if (isDone()) { | ||
| 131 | throw new NoSuchElementException("line iterator out of bounds"); | ||
| 132 | } | ||
| 133 | int type; | ||
| 134 | if (index == 0) { | ||
| 135 | coords[0] = line.getX1(); | ||
| 136 | coords[1] = line.getY1(); | ||
| 137 | type = SEG_MOVETO; | ||
| 138 | } else { | ||
| 139 | coords[0] = line.getX2(); | ||
| 140 | coords[1] = line.getY2(); | ||
| 141 | type = SEG_LINETO; | ||
| 142 | } | ||
| 143 | if (affine != null) { | ||
| 144 | affine.transform(coords, 0, coords, 0, 1); | ||
| 145 | } | ||
| 146 | return type; | ||
| 147 | } | ||
| 148 | } | ||
Binary file not shown.
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * The <code>NoninvertibleTransformException</code> class represents | ||
| 30 | * an exception that is thrown if an operation is performed requiring | ||
| 31 | * the inverse of an {@link AffineTransform} object but the | ||
| 32 | * <code>AffineTransform</code> is in a non-invertible state. | ||
| 33 | */ | ||
| 34 | |||
| 35 | public class NoninvertibleTransformException extends java.lang.Exception { | ||
| 36 | /** | ||
| 37 | * Constructs an instance of | ||
| 38 | * <code>NoninvertibleTransformException</code> | ||
| 39 | * with the specified detail message. | ||
| 40 | * @param s the detail message | ||
| 41 | * @since 1.2 | ||
| 42 | */ | ||
| 43 | public NoninvertibleTransformException(String s) { | ||
| 44 | super (s); | ||
| 45 | } | ||
| 46 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,2595 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.awt.Rectangle; | ||
| 30 | import sun.awt.geom.Curve; | ||
| 31 | import java.io.Serializable; | ||
| 32 | import java.io.StreamCorruptedException; | ||
| 33 | import java.util.Arrays; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * The {@code Path2D} class provides a simple, yet flexible | ||
| 37 | * shape which represents an arbitrary geometric path. | ||
| 38 | * It can fully represent any path which can be iterated by the | ||
| 39 | * {@link PathIterator} interface including all of its segment | ||
| 40 | * types and winding rules and it implements all of the | ||
| 41 | * basic hit testing methods of the {@link Shape} interface. | ||
| 42 | * <p> | ||
| 43 | * Use {@link Path2D.Float} when dealing with data that can be represented | ||
| 44 | * and used with floating point precision. Use {@link Path2D.Double} | ||
| 45 | * for data that requires the accuracy or range of double precision. | ||
| 46 | * <p> | ||
| 47 | * {@code Path2D} provides exactly those facilities required for | ||
| 48 | * basic construction and management of a geometric path and | ||
| 49 | * implementation of the above interfaces with little added | ||
| 50 | * interpretation. | ||
| 51 | * If it is useful to manipulate the interiors of closed | ||
| 52 | * geometric shapes beyond simple hit testing then the | ||
| 53 | * {@link Area} class provides additional capabilities | ||
| 54 | * specifically targeted at closed figures. | ||
| 55 | * While both classes nominally implement the {@code Shape} | ||
| 56 | * interface, they differ in purpose and together they provide | ||
| 57 | * two useful views of a geometric shape where {@code Path2D} | ||
| 58 | * deals primarily with a trajectory formed by path segments | ||
| 59 | * and {@code Area} deals more with interpretation and manipulation | ||
| 60 | * of enclosed regions of 2D geometric space. | ||
| 61 | * <p> | ||
| 62 | * The {@link PathIterator} interface has more detailed descriptions | ||
| 63 | * of the types of segments that make up a path and the winding rules | ||
| 64 | * that control how to determine which regions are inside or outside | ||
| 65 | * the path. | ||
| 66 | * | ||
| 67 | * @author Jim Graham | ||
| 68 | * @since 1.6 | ||
| 69 | */ | ||
| 70 | public abstract class Path2D implements Shape, Cloneable { | ||
| 71 | /** | ||
| 72 | * An even-odd winding rule for determining the interior of | ||
| 73 | * a path. | ||
| 74 | * | ||
| 75 | * @see PathIterator#WIND_EVEN_ODD | ||
| 76 | * @since 1.6 | ||
| 77 | */ | ||
| 78 | public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; | ||
| 79 | |||
| 80 | /** | ||
| 81 | * A non-zero winding rule for determining the interior of a | ||
| 82 | * path. | ||
| 83 | * | ||
| 84 | * @see PathIterator#WIND_NON_ZERO | ||
| 85 | * @since 1.6 | ||
| 86 | */ | ||
| 87 | public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; | ||
| 88 | |||
| 89 | // For code simplicity, copy these constants to our namespace | ||
| 90 | // and cast them to byte constants for easy storage. | ||
| 91 | private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO; | ||
| 92 | private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO; | ||
| 93 | private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO; | ||
| 94 | private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO; | ||
| 95 | private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE; | ||
| 96 | |||
| 97 | transient byte[] pointTypes; | ||
| 98 | transient int numTypes; | ||
| 99 | transient int numCoords; | ||
| 100 | transient int windingRule; | ||
| 101 | |||
| 102 | static final int INIT_SIZE = 20; | ||
| 103 | static final int EXPAND_MAX = 500; | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Constructs a new empty {@code Path2D} object. | ||
| 107 | * It is assumed that the package sibling subclass that is | ||
| 108 | * defaulting to this constructor will fill in all values. | ||
| 109 | * | ||
| 110 | * @since 1.6 | ||
| 111 | */ | ||
| 112 | /* private protected */ | ||
| 113 | Path2D() { | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Constructs a new {@code Path2D} object from the given | ||
| 118 | * specified initial values. | ||
| 119 | * This method is only intended for internal use and should | ||
| 120 | * not be made public if the other constructors for this class | ||
| 121 | * are ever exposed. | ||
| 122 | * | ||
| 123 | * @param rule the winding rule | ||
| 124 | * @param initialTypes the size to make the initial array to | ||
| 125 | * store the path segment types | ||
| 126 | * @since 1.6 | ||
| 127 | */ | ||
| 128 | /* private protected */ | ||
| 129 | Path2D(int rule, int initialTypes) { | ||
| 130 | setWindingRule(rule); | ||
| 131 | this.pointTypes = new byte[initialTypes]; | ||
| 132 | } | ||
| 133 | |||
| 134 | abstract float[] cloneCoordsFloat(AffineTransform at); | ||
| 135 | abstract double[] cloneCoordsDouble(AffineTransform at); | ||
| 136 | abstract void append(float x, float y); | ||
| 137 | abstract void append(double x, double y); | ||
| 138 | abstract Point2D getPoint(int coordindex); | ||
| 139 | abstract void needRoom(boolean needMove, int newCoords); | ||
| 140 | abstract int pointCrossings(double px, double py); | ||
| 141 | abstract int rectCrossings(double rxmin, double rymin, | ||
| 142 | double rxmax, double rymax); | ||
| 143 | |||
| 144 | /** | ||
| 145 | * The {@code Float} class defines a geometric path with | ||
| 146 | * coordinates stored in single precision floating point. | ||
| 147 | * | ||
| 148 | * @since 1.6 | ||
| 149 | */ | ||
| 150 | public static class Float extends Path2D implements Serializable { | ||
| 151 | transient float floatCoords[]; | ||
| 152 | |||
| 153 | /** | ||
| 154 | * Constructs a new empty single precision {@code Path2D} object | ||
| 155 | * with a default winding rule of {@link #WIND_NON_ZERO}. | ||
| 156 | * | ||
| 157 | * @since 1.6 | ||
| 158 | */ | ||
| 159 | public Float() { | ||
| 160 | this(WIND_NON_ZERO, INIT_SIZE); | ||
| 161 | } | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Constructs a new empty single precision {@code Path2D} object | ||
| 165 | * with the specified winding rule to control operations that | ||
| 166 | * require the interior of the path to be defined. | ||
| 167 | * | ||
| 168 | * @param rule the winding rule | ||
| 169 | * @see #WIND_EVEN_ODD | ||
| 170 | * @see #WIND_NON_ZERO | ||
| 171 | * @since 1.6 | ||
| 172 | */ | ||
| 173 | public Float(int rule) { | ||
| 174 | this(rule, INIT_SIZE); | ||
| 175 | } | ||
| 176 | |||
| 177 | /** | ||
| 178 | * Constructs a new empty single precision {@code Path2D} object | ||
| 179 | * with the specified winding rule and the specified initial | ||
| 180 | * capacity to store path segments. | ||
| 181 | * This number is an initial guess as to how many path segments | ||
| 182 | * will be added to the path, but the storage is expanded as | ||
| 183 | * needed to store whatever path segments are added. | ||
| 184 | * | ||
| 185 | * @param rule the winding rule | ||
| 186 | * @param initialCapacity the estimate for the number of path segments | ||
| 187 | * in the path | ||
| 188 | * @see #WIND_EVEN_ODD | ||
| 189 | * @see #WIND_NON_ZERO | ||
| 190 | * @since 1.6 | ||
| 191 | */ | ||
| 192 | public Float(int rule, int initialCapacity) { | ||
| 193 | super(rule, initialCapacity); | ||
| 194 | floatCoords = new float[initialCapacity * 2]; | ||
| 195 | } | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Constructs a new single precision {@code Path2D} object | ||
| 199 | * from an arbitrary {@link Shape} object. | ||
| 200 | * All of the initial geometry and the winding rule for this path are | ||
| 201 | * taken from the specified {@code Shape} object. | ||
| 202 | * | ||
| 203 | * @param s the specified {@code Shape} object | ||
| 204 | * @since 1.6 | ||
| 205 | */ | ||
| 206 | public Float(Shape s) { | ||
| 207 | this(s, null); | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Constructs a new single precision {@code Path2D} object | ||
| 212 | * from an arbitrary {@link Shape} object, transformed by an | ||
| 213 | * {@link AffineTransform} object. | ||
| 214 | * All of the initial geometry and the winding rule for this path are | ||
| 215 | * taken from the specified {@code Shape} object and transformed | ||
| 216 | * by the specified {@code AffineTransform} object. | ||
| 217 | * | ||
| 218 | * @param s the specified {@code Shape} object | ||
| 219 | * @param at the specified {@code AffineTransform} object | ||
| 220 | * @since 1.6 | ||
| 221 | */ | ||
| 222 | public Float(Shape s, AffineTransform at) { | ||
| 223 | if (s instanceof Path2D) { | ||
| 224 | Path2D p2d = (Path2D) s; | ||
| 225 | setWindingRule(p2d.windingRule); | ||
| 226 | this.numTypes = p2d.numTypes; | ||
| 227 | this.pointTypes = Arrays.copyOf(p2d.pointTypes, | ||
| 228 | p2d.pointTypes.length); | ||
| 229 | this.numCoords = p2d.numCoords; | ||
| 230 | this.floatCoords = p2d.cloneCoordsFloat(at); | ||
| 231 | } else { | ||
| 232 | PathIterator pi = s.getPathIterator(at); | ||
| 233 | setWindingRule(pi.getWindingRule()); | ||
| 234 | this.pointTypes = new byte[INIT_SIZE]; | ||
| 235 | this.floatCoords = new float[INIT_SIZE * 2]; | ||
| 236 | append(pi, false); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | float[] cloneCoordsFloat(AffineTransform at) { | ||
| 241 | float ret[]; | ||
| 242 | if (at == null) { | ||
| 243 | ret = Arrays.copyOf(this.floatCoords, this.floatCoords.length); | ||
| 244 | } else { | ||
| 245 | ret = new float[floatCoords.length]; | ||
| 246 | at.transform(floatCoords, 0, ret, 0, numCoords / 2); | ||
| 247 | } | ||
| 248 | return ret; | ||
| 249 | } | ||
| 250 | |||
| 251 | double[] cloneCoordsDouble(AffineTransform at) { | ||
| 252 | double ret[] = new double[floatCoords.length]; | ||
| 253 | if (at == null) { | ||
| 254 | for (int i = 0; i < numCoords; i++) { | ||
| 255 | ret[i] = floatCoords[i]; | ||
| 256 | } | ||
| 257 | } else { | ||
| 258 | at.transform(floatCoords, 0, ret, 0, numCoords / 2); | ||
| 259 | } | ||
| 260 | return ret; | ||
| 261 | } | ||
| 262 | |||
| 263 | void append(float x, float y) { | ||
| 264 | floatCoords[numCoords++] = x; | ||
| 265 | floatCoords[numCoords++] = y; | ||
| 266 | } | ||
| 267 | |||
| 268 | void append(double x, double y) { | ||
| 269 | floatCoords[numCoords++] = (float) x; | ||
| 270 | floatCoords[numCoords++] = (float) y; | ||
| 271 | } | ||
| 272 | |||
| 273 | Point2D getPoint(int coordindex) { | ||
| 274 | return new Point2D.Float(floatCoords[coordindex], | ||
| 275 | floatCoords[coordindex+1]); | ||
| 276 | } | ||
| 277 | |||
| 278 | void needRoom(boolean needMove, int newCoords) { | ||
| 279 | if (needMove && numTypes == 0) { | ||
| 280 | throw new IllegalPathStateException("missing initial moveto "+ | ||
| 281 | "in path definition"); | ||
| 282 | } | ||
| 283 | int size = pointTypes.length; | ||
| 284 | if (numTypes >= size) { | ||
| 285 | int grow = size; | ||
| 286 | if (grow > EXPAND_MAX) { | ||
| 287 | grow = EXPAND_MAX; | ||
| 288 | } | ||
| 289 | pointTypes = Arrays.copyOf(pointTypes, size+grow); | ||
| 290 | } | ||
| 291 | size = floatCoords.length; | ||
| 292 | if (numCoords + newCoords > size) { | ||
| 293 | int grow = size; | ||
| 294 | if (grow > EXPAND_MAX * 2) { | ||
| 295 | grow = EXPAND_MAX * 2; | ||
| 296 | } | ||
| 297 | if (grow < newCoords) { | ||
| 298 | grow = newCoords; | ||
| 299 | } | ||
| 300 | floatCoords = Arrays.copyOf(floatCoords, size+grow); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | /** | ||
| 305 | * {@inheritDoc} | ||
| 306 | * @since 1.6 | ||
| 307 | */ | ||
| 308 | public final synchronized void moveTo(double x, double y) { | ||
| 309 | if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) { | ||
| 310 | floatCoords[numCoords-2] = (float) x; | ||
| 311 | floatCoords[numCoords-1] = (float) y; | ||
| 312 | } else { | ||
| 313 | needRoom(false, 2); | ||
| 314 | pointTypes[numTypes++] = SEG_MOVETO; | ||
| 315 | floatCoords[numCoords++] = (float) x; | ||
| 316 | floatCoords[numCoords++] = (float) y; | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | /** | ||
| 321 | * Adds a point to the path by moving to the specified | ||
| 322 | * coordinates specified in float precision. | ||
| 323 | * <p> | ||
| 324 | * This method provides a single precision variant of | ||
| 325 | * the double precision {@code moveTo()} method on the | ||
| 326 | * base {@code Path2D} class. | ||
| 327 | * | ||
| 328 | * @param x the specified X coordinate | ||
| 329 | * @param y the specified Y coordinate | ||
| 330 | * @see Path2D#moveTo | ||
| 331 | * @since 1.6 | ||
| 332 | */ | ||
| 333 | public final synchronized void moveTo(float x, float y) { | ||
| 334 | if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) { | ||
| 335 | floatCoords[numCoords-2] = x; | ||
| 336 | floatCoords[numCoords-1] = y; | ||
| 337 | } else { | ||
| 338 | needRoom(false, 2); | ||
| 339 | pointTypes[numTypes++] = SEG_MOVETO; | ||
| 340 | floatCoords[numCoords++] = x; | ||
| 341 | floatCoords[numCoords++] = y; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | /** | ||
| 346 | * {@inheritDoc} | ||
| 347 | * @since 1.6 | ||
| 348 | */ | ||
| 349 | public final synchronized void lineTo(double x, double y) { | ||
| 350 | needRoom(true, 2); | ||
| 351 | pointTypes[numTypes++] = SEG_LINETO; | ||
| 352 | floatCoords[numCoords++] = (float) x; | ||
| 353 | floatCoords[numCoords++] = (float) y; | ||
| 354 | } | ||
| 355 | |||
| 356 | /** | ||
| 357 | * Adds a point to the path by drawing a straight line from the | ||
| 358 | * current coordinates to the new specified coordinates | ||
| 359 | * specified in float precision. | ||
| 360 | * <p> | ||
| 361 | * This method provides a single precision variant of | ||
| 362 | * the double precision {@code lineTo()} method on the | ||
| 363 | * base {@code Path2D} class. | ||
| 364 | * | ||
| 365 | * @param x the specified X coordinate | ||
| 366 | * @param y the specified Y coordinate | ||
| 367 | * @see Path2D#lineTo | ||
| 368 | * @since 1.6 | ||
| 369 | */ | ||
| 370 | public final synchronized void lineTo(float x, float y) { | ||
| 371 | needRoom(true, 2); | ||
| 372 | pointTypes[numTypes++] = SEG_LINETO; | ||
| 373 | floatCoords[numCoords++] = x; | ||
| 374 | floatCoords[numCoords++] = y; | ||
| 375 | } | ||
| 376 | |||
| 377 | /** | ||
| 378 | * {@inheritDoc} | ||
| 379 | * @since 1.6 | ||
| 380 | */ | ||
| 381 | public final synchronized void quadTo(double x1, double y1, | ||
| 382 | double x2, double y2) | ||
| 383 | { | ||
| 384 | needRoom(true, 4); | ||
| 385 | pointTypes[numTypes++] = SEG_QUADTO; | ||
| 386 | floatCoords[numCoords++] = (float) x1; | ||
| 387 | floatCoords[numCoords++] = (float) y1; | ||
| 388 | floatCoords[numCoords++] = (float) x2; | ||
| 389 | floatCoords[numCoords++] = (float) y2; | ||
| 390 | } | ||
| 391 | |||
| 392 | /** | ||
| 393 | * Adds a curved segment, defined by two new points, to the path by | ||
| 394 | * drawing a Quadratic curve that intersects both the current | ||
| 395 | * coordinates and the specified coordinates {@code (x2,y2)}, | ||
| 396 | * using the specified point {@code (x1,y1)} as a quadratic | ||
| 397 | * parametric control point. | ||
| 398 | * All coordinates are specified in float precision. | ||
| 399 | * <p> | ||
| 400 | * This method provides a single precision variant of | ||
| 401 | * the double precision {@code quadTo()} method on the | ||
| 402 | * base {@code Path2D} class. | ||
| 403 | * | ||
| 404 | * @param x1 the X coordinate of the quadratic control point | ||
| 405 | * @param y1 the Y coordinate of the quadratic control point | ||
| 406 | * @param x2 the X coordinate of the final end point | ||
| 407 | * @param y2 the Y coordinate of the final end point | ||
| 408 | * @see Path2D#quadTo | ||
| 409 | * @since 1.6 | ||
| 410 | */ | ||
| 411 | public final synchronized void quadTo(float x1, float y1, | ||
| 412 | float x2, float y2) | ||
| 413 | { | ||
| 414 | needRoom(true, 4); | ||
| 415 | pointTypes[numTypes++] = SEG_QUADTO; | ||
| 416 | floatCoords[numCoords++] = x1; | ||
| 417 | floatCoords[numCoords++] = y1; | ||
| 418 | floatCoords[numCoords++] = x2; | ||
| 419 | floatCoords[numCoords++] = y2; | ||
| 420 | } | ||
| 421 | |||
| 422 | /** | ||
| 423 | * {@inheritDoc} | ||
| 424 | * @since 1.6 | ||
| 425 | */ | ||
| 426 | public final synchronized void curveTo(double x1, double y1, | ||
| 427 | double x2, double y2, | ||
| 428 | double x3, double y3) | ||
| 429 | { | ||
| 430 | needRoom(true, 6); | ||
| 431 | pointTypes[numTypes++] = SEG_CUBICTO; | ||
| 432 | floatCoords[numCoords++] = (float) x1; | ||
| 433 | floatCoords[numCoords++] = (float) y1; | ||
| 434 | floatCoords[numCoords++] = (float) x2; | ||
| 435 | floatCoords[numCoords++] = (float) y2; | ||
| 436 | floatCoords[numCoords++] = (float) x3; | ||
| 437 | floatCoords[numCoords++] = (float) y3; | ||
| 438 | } | ||
| 439 | |||
| 440 | /** | ||
| 441 | * Adds a curved segment, defined by three new points, to the path by | ||
| 442 | * drawing a Bézier curve that intersects both the current | ||
| 443 | * coordinates and the specified coordinates {@code (x3,y3)}, | ||
| 444 | * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as | ||
| 445 | * Bézier control points. | ||
| 446 | * All coordinates are specified in float precision. | ||
| 447 | * <p> | ||
| 448 | * This method provides a single precision variant of | ||
| 449 | * the double precision {@code curveTo()} method on the | ||
| 450 | * base {@code Path2D} class. | ||
| 451 | * | ||
| 452 | * @param x1 the X coordinate of the first Bézier control point | ||
| 453 | * @param y1 the Y coordinate of the first Bézier control point | ||
| 454 | * @param x2 the X coordinate of the second Bézier control point | ||
| 455 | * @param y2 the Y coordinate of the second Bézier control point | ||
| 456 | * @param x3 the X coordinate of the final end point | ||
| 457 | * @param y3 the Y coordinate of the final end point | ||
| 458 | * @see Path2D#curveTo | ||
| 459 | * @since 1.6 | ||
| 460 | */ | ||
| 461 | public final synchronized void curveTo(float x1, float y1, | ||
| 462 | float x2, float y2, | ||
| 463 | float x3, float y3) | ||
| 464 | { | ||
| 465 | needRoom(true, 6); | ||
| 466 | pointTypes[numTypes++] = SEG_CUBICTO; | ||
| 467 | floatCoords[numCoords++] = x1; | ||
| 468 | floatCoords[numCoords++] = y1; | ||
| 469 | floatCoords[numCoords++] = x2; | ||
| 470 | floatCoords[numCoords++] = y2; | ||
| 471 | floatCoords[numCoords++] = x3; | ||
| 472 | floatCoords[numCoords++] = y3; | ||
| 473 | } | ||
| 474 | |||
| 475 | int pointCrossings(double px, double py) { | ||
| 476 | double movx, movy, curx, cury, endx, endy; | ||
| 477 | float coords[] = floatCoords; | ||
| 478 | curx = movx = coords[0]; | ||
| 479 | cury = movy = coords[1]; | ||
| 480 | int crossings = 0; | ||
| 481 | int ci = 2; | ||
| 482 | for (int i = 1; i < numTypes; i++) { | ||
| 483 | switch (pointTypes[i]) { | ||
| 484 | case PathIterator.SEG_MOVETO: | ||
| 485 | if (cury != movy) { | ||
| 486 | crossings += | ||
| 487 | Curve.pointCrossingsForLine(px, py, | ||
| 488 | curx, cury, | ||
| 489 | movx, movy); | ||
| 490 | } | ||
| 491 | movx = curx = coords[ci++]; | ||
| 492 | movy = cury = coords[ci++]; | ||
| 493 | break; | ||
| 494 | case PathIterator.SEG_LINETO: | ||
| 495 | crossings += | ||
| 496 | Curve.pointCrossingsForLine(px, py, | ||
| 497 | curx, cury, | ||
| 498 | endx = coords[ci++], | ||
| 499 | endy = coords[ci++]); | ||
| 500 | curx = endx; | ||
| 501 | cury = endy; | ||
| 502 | break; | ||
| 503 | case PathIterator.SEG_QUADTO: | ||
| 504 | crossings += | ||
| 505 | Curve.pointCrossingsForQuad(px, py, | ||
| 506 | curx, cury, | ||
| 507 | coords[ci++], | ||
| 508 | coords[ci++], | ||
| 509 | endx = coords[ci++], | ||
| 510 | endy = coords[ci++], | ||
| 511 | 0); | ||
| 512 | curx = endx; | ||
| 513 | cury = endy; | ||
| 514 | break; | ||
| 515 | case PathIterator.SEG_CUBICTO: | ||
| 516 | crossings += | ||
| 517 | Curve.pointCrossingsForCubic(px, py, | ||
| 518 | curx, cury, | ||
| 519 | coords[ci++], | ||
| 520 | coords[ci++], | ||
| 521 | coords[ci++], | ||
| 522 | coords[ci++], | ||
| 523 | endx = coords[ci++], | ||
| 524 | endy = coords[ci++], | ||
| 525 | 0); | ||
| 526 | curx = endx; | ||
| 527 | cury = endy; | ||
| 528 | break; | ||
| 529 | case PathIterator.SEG_CLOSE: | ||
| 530 | if (cury != movy) { | ||
| 531 | crossings += | ||
| 532 | Curve.pointCrossingsForLine(px, py, | ||
| 533 | curx, cury, | ||
| 534 | movx, movy); | ||
| 535 | } | ||
| 536 | curx = movx; | ||
| 537 | cury = movy; | ||
| 538 | break; | ||
| 539 | } | ||
| 540 | } | ||
| 541 | if (cury != movy) { | ||
| 542 | crossings += | ||
| 543 | Curve.pointCrossingsForLine(px, py, | ||
| 544 | curx, cury, | ||
| 545 | movx, movy); | ||
| 546 | } | ||
| 547 | return crossings; | ||
| 548 | } | ||
| 549 | |||
| 550 | int rectCrossings(double rxmin, double rymin, | ||
| 551 | double rxmax, double rymax) | ||
| 552 | { | ||
| 553 | float coords[] = floatCoords; | ||
| 554 | double curx, cury, movx, movy, endx, endy; | ||
| 555 | curx = movx = coords[0]; | ||
| 556 | cury = movy = coords[1]; | ||
| 557 | int crossings = 0; | ||
| 558 | int ci = 2; | ||
| 559 | for (int i = 1; | ||
| 560 | crossings != Curve.RECT_INTERSECTS && i < numTypes; | ||
| 561 | i++) | ||
| 562 | { | ||
| 563 | switch (pointTypes[i]) { | ||
| 564 | case PathIterator.SEG_MOVETO: | ||
| 565 | if (curx != movx || cury != movy) { | ||
| 566 | crossings = | ||
| 567 | Curve.rectCrossingsForLine(crossings, | ||
| 568 | rxmin, rymin, | ||
| 569 | rxmax, rymax, | ||
| 570 | curx, cury, | ||
| 571 | movx, movy); | ||
| 572 | } | ||
| 573 | // Count should always be a multiple of 2 here. | ||
| 574 | // assert((crossings & 1) != 0); | ||
| 575 | movx = curx = coords[ci++]; | ||
| 576 | movy = cury = coords[ci++]; | ||
| 577 | break; | ||
| 578 | case PathIterator.SEG_LINETO: | ||
| 579 | crossings = | ||
| 580 | Curve.rectCrossingsForLine(crossings, | ||
| 581 | rxmin, rymin, | ||
| 582 | rxmax, rymax, | ||
| 583 | curx, cury, | ||
| 584 | endx = coords[ci++], | ||
| 585 | endy = coords[ci++]); | ||
| 586 | curx = endx; | ||
| 587 | cury = endy; | ||
| 588 | break; | ||
| 589 | case PathIterator.SEG_QUADTO: | ||
| 590 | crossings = | ||
| 591 | Curve.rectCrossingsForQuad(crossings, | ||
| 592 | rxmin, rymin, | ||
| 593 | rxmax, rymax, | ||
| 594 | curx, cury, | ||
| 595 | coords[ci++], | ||
| 596 | coords[ci++], | ||
| 597 | endx = coords[ci++], | ||
| 598 | endy = coords[ci++], | ||
| 599 | 0); | ||
| 600 | curx = endx; | ||
| 601 | cury = endy; | ||
| 602 | break; | ||
| 603 | case PathIterator.SEG_CUBICTO: | ||
| 604 | crossings = | ||
| 605 | Curve.rectCrossingsForCubic(crossings, | ||
| 606 | rxmin, rymin, | ||
| 607 | rxmax, rymax, | ||
| 608 | curx, cury, | ||
| 609 | coords[ci++], | ||
| 610 | coords[ci++], | ||
| 611 | coords[ci++], | ||
| 612 | coords[ci++], | ||
| 613 | endx = coords[ci++], | ||
| 614 | endy = coords[ci++], | ||
| 615 | 0); | ||
| 616 | curx = endx; | ||
| 617 | cury = endy; | ||
| 618 | break; | ||
| 619 | case PathIterator.SEG_CLOSE: | ||
| 620 | if (curx != movx || cury != movy) { | ||
| 621 | crossings = | ||
| 622 | Curve.rectCrossingsForLine(crossings, | ||
| 623 | rxmin, rymin, | ||
| 624 | rxmax, rymax, | ||
| 625 | curx, cury, | ||
| 626 | movx, movy); | ||
| 627 | } | ||
| 628 | curx = movx; | ||
| 629 | cury = movy; | ||
| 630 | // Count should always be a multiple of 2 here. | ||
| 631 | // assert((crossings & 1) != 0); | ||
| 632 | break; | ||
| 633 | } | ||
| 634 | } | ||
| 635 | if (crossings != Curve.RECT_INTERSECTS && | ||
| 636 | (curx != movx || cury != movy)) | ||
| 637 | { | ||
| 638 | crossings = | ||
| 639 | Curve.rectCrossingsForLine(crossings, | ||
| 640 | rxmin, rymin, | ||
| 641 | rxmax, rymax, | ||
| 642 | curx, cury, | ||
| 643 | movx, movy); | ||
| 644 | } | ||
| 645 | // Count should always be a multiple of 2 here. | ||
| 646 | // assert((crossings & 1) != 0); | ||
| 647 | return crossings; | ||
| 648 | } | ||
| 649 | |||
| 650 | /** | ||
| 651 | * {@inheritDoc} | ||
| 652 | * @since 1.6 | ||
| 653 | */ | ||
| 654 | public final void append(PathIterator pi, boolean connect) { | ||
| 655 | float coords[] = new float[6]; | ||
| 656 | while (!pi.isDone()) { | ||
| 657 | switch (pi.currentSegment(coords)) { | ||
| 658 | case SEG_MOVETO: | ||
| 659 | if (!connect || numTypes < 1 || numCoords < 1) { | ||
| 660 | moveTo(coords[0], coords[1]); | ||
| 661 | break; | ||
| 662 | } | ||
| 663 | if (pointTypes[numTypes - 1] != SEG_CLOSE && | ||
| 664 | floatCoords[numCoords-2] == coords[0] && | ||
| 665 | floatCoords[numCoords-1] == coords[1]) | ||
| 666 | { | ||
| 667 | // Collapse out initial moveto/lineto | ||
| 668 | break; | ||
| 669 | } | ||
| 670 | // NO BREAK; | ||
| 671 | case SEG_LINETO: | ||
| 672 | lineTo(coords[0], coords[1]); | ||
| 673 | break; | ||
| 674 | case SEG_QUADTO: | ||
| 675 | quadTo(coords[0], coords[1], | ||
| 676 | coords[2], coords[3]); | ||
| 677 | break; | ||
| 678 | case SEG_CUBICTO: | ||
| 679 | curveTo(coords[0], coords[1], | ||
| 680 | coords[2], coords[3], | ||
| 681 | coords[4], coords[5]); | ||
| 682 | break; | ||
| 683 | case SEG_CLOSE: | ||
| 684 | closePath(); | ||
| 685 | break; | ||
| 686 | } | ||
| 687 | pi.next(); | ||
| 688 | connect = false; | ||
| 689 | } | ||
| 690 | } | ||
| 691 | |||
| 692 | /** | ||
| 693 | * {@inheritDoc} | ||
| 694 | * @since 1.6 | ||
| 695 | */ | ||
| 696 | public final void transform(AffineTransform at) { | ||
| 697 | at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2); | ||
| 698 | } | ||
| 699 | |||
| 700 | /** | ||
| 701 | * {@inheritDoc} | ||
| 702 | * @since 1.6 | ||
| 703 | */ | ||
| 704 | public final synchronized Rectangle2D getBounds2D() { | ||
| 705 | float x1, y1, x2, y2; | ||
| 706 | int i = numCoords; | ||
| 707 | if (i > 0) { | ||
| 708 | y1 = y2 = floatCoords[--i]; | ||
| 709 | x1 = x2 = floatCoords[--i]; | ||
| 710 | while (i > 0) { | ||
| 711 | float y = floatCoords[--i]; | ||
| 712 | float x = floatCoords[--i]; | ||
| 713 | if (x < x1) x1 = x; | ||
| 714 | if (y < y1) y1 = y; | ||
| 715 | if (x > x2) x2 = x; | ||
| 716 | if (y > y2) y2 = y; | ||
| 717 | } | ||
| 718 | } else { | ||
| 719 | x1 = y1 = x2 = y2 = 0.0f; | ||
| 720 | } | ||
| 721 | return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1); | ||
| 722 | } | ||
| 723 | |||
| 724 | /** | ||
| 725 | * {@inheritDoc} | ||
| 726 | * <p> | ||
| 727 | * The iterator for this class is not multi-threaded safe, | ||
| 728 | * which means that the {@code Path2D} class does not | ||
| 729 | * guarantee that modifications to the geometry of this | ||
| 730 | * {@code Path2D} object do not affect any iterations of | ||
| 731 | * that geometry that are already in process. | ||
| 732 | * | ||
| 733 | * @since 1.6 | ||
| 734 | */ | ||
| 735 | public final PathIterator getPathIterator(AffineTransform at) { | ||
| 736 | if (at == null) { | ||
| 737 | return new CopyIterator(this); | ||
| 738 | } else { | ||
| 739 | return new TxIterator(this, at); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | |||
| 743 | /** | ||
| 744 | * Creates a new object of the same class as this object. | ||
| 745 | * | ||
| 746 | * @return a clone of this instance. | ||
| 747 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 748 | * @see java.lang.Cloneable | ||
| 749 | * @since 1.6 | ||
| 750 | */ | ||
| 751 | public final Object clone() { | ||
| 752 | // Note: It would be nice to have this return Path2D | ||
| 753 | // but one of our subclasses (GeneralPath) needs to | ||
| 754 | // offer "public Object clone()" for backwards | ||
| 755 | // compatibility so we cannot restrict it further. | ||
| 756 | // REMIND: Can we do both somehow? | ||
| 757 | if (this instanceof GeneralPath) { | ||
| 758 | return new GeneralPath(this); | ||
| 759 | } else { | ||
| 760 | return new Path2D.Float(this); | ||
| 761 | } | ||
| 762 | } | ||
| 763 | |||
| 764 | /* | ||
| 765 | * JDK 1.6 serialVersionUID | ||
| 766 | */ | ||
| 767 | private static final long serialVersionUID = 6990832515060788886L; | ||
| 768 | |||
| 769 | /** | ||
| 770 | * Writes the default serializable fields to the | ||
| 771 | * {@code ObjectOutputStream} followed by an explicit | ||
| 772 | * serialization of the path segments stored in this | ||
| 773 | * path. | ||
| 774 | * | ||
| 775 | * @serialData | ||
| 776 | * <a name="Path2DSerialData"><!-- --></a> | ||
| 777 | * <ol> | ||
| 778 | * <li>The default serializable fields. | ||
| 779 | * There are no default serializable fields as of 1.6. | ||
| 780 | * <li>followed by | ||
| 781 | * a byte indicating the storage type of the original object | ||
| 782 | * as a hint (SERIAL_STORAGE_FLT_ARRAY) | ||
| 783 | * <li>followed by | ||
| 784 | * an integer indicating the number of path segments to follow (NP) | ||
| 785 | * or -1 to indicate an unknown number of path segments follows | ||
| 786 | * <li>followed by | ||
| 787 | * an integer indicating the total number of coordinates to follow (NC) | ||
| 788 | * or -1 to indicate an unknown number of coordinates follows | ||
| 789 | * (NC should always be even since coordinates always appear in pairs | ||
| 790 | * representing an x,y pair) | ||
| 791 | * <li>followed by | ||
| 792 | * a byte indicating the winding rule | ||
| 793 | * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or | ||
| 794 | * {@link #WIND_NON_ZERO WIND_NON_ZERO}) | ||
| 795 | * <li>followed by | ||
| 796 | * NP (or unlimited if NP < 0) sets of values consisting of | ||
| 797 | * a single byte indicating a path segment type | ||
| 798 | * followed by one or more pairs of float or double | ||
| 799 | * values representing the coordinates of the path segment | ||
| 800 | * <li>followed by | ||
| 801 | * a byte indicating the end of the path (SERIAL_PATH_END). | ||
| 802 | * </ol> | ||
| 803 | * <p> | ||
| 804 | * The following byte value constants are used in the serialized form | ||
| 805 | * of {@code Path2D} objects: | ||
| 806 | * <table> | ||
| 807 | * <tr> | ||
| 808 | * <th>Constant Name</th> | ||
| 809 | * <th>Byte Value</th> | ||
| 810 | * <th>Followed by</th> | ||
| 811 | * <th>Description</th> | ||
| 812 | * </tr> | ||
| 813 | * <tr> | ||
| 814 | * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td> | ||
| 815 | * <td>0x30</td> | ||
| 816 | * <td></td> | ||
| 817 | * <td>A hint that the original {@code Path2D} object stored | ||
| 818 | * the coordinates in a Java array of floats.</td> | ||
| 819 | * </tr> | ||
| 820 | * <tr> | ||
| 821 | * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td> | ||
| 822 | * <td>0x31</td> | ||
| 823 | * <td></td> | ||
| 824 | * <td>A hint that the original {@code Path2D} object stored | ||
| 825 | * the coordinates in a Java array of doubles.</td> | ||
| 826 | * </tr> | ||
| 827 | * <tr> | ||
| 828 | * <td>{@code SERIAL_SEG_FLT_MOVETO}</td> | ||
| 829 | * <td>0x40</td> | ||
| 830 | * <td>2 floats</td> | ||
| 831 | * <td>A {@link #moveTo moveTo} path segment follows.</td> | ||
| 832 | * </tr> | ||
| 833 | * <tr> | ||
| 834 | * <td>{@code SERIAL_SEG_FLT_LINETO}</td> | ||
| 835 | * <td>0x41</td> | ||
| 836 | * <td>2 floats</td> | ||
| 837 | * <td>A {@link #lineTo lineTo} path segment follows.</td> | ||
| 838 | * </tr> | ||
| 839 | * <tr> | ||
| 840 | * <td>{@code SERIAL_SEG_FLT_QUADTO}</td> | ||
| 841 | * <td>0x42</td> | ||
| 842 | * <td>4 floats</td> | ||
| 843 | * <td>A {@link #quadTo quadTo} path segment follows.</td> | ||
| 844 | * </tr> | ||
| 845 | * <tr> | ||
| 846 | * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td> | ||
| 847 | * <td>0x43</td> | ||
| 848 | * <td>6 floats</td> | ||
| 849 | * <td>A {@link #curveTo curveTo} path segment follows.</td> | ||
| 850 | * </tr> | ||
| 851 | * <tr> | ||
| 852 | * <td>{@code SERIAL_SEG_DBL_MOVETO}</td> | ||
| 853 | * <td>0x50</td> | ||
| 854 | * <td>2 doubles</td> | ||
| 855 | * <td>A {@link #moveTo moveTo} path segment follows.</td> | ||
| 856 | * </tr> | ||
| 857 | * <tr> | ||
| 858 | * <td>{@code SERIAL_SEG_DBL_LINETO}</td> | ||
| 859 | * <td>0x51</td> | ||
| 860 | * <td>2 doubles</td> | ||
| 861 | * <td>A {@link #lineTo lineTo} path segment follows.</td> | ||
| 862 | * </tr> | ||
| 863 | * <tr> | ||
| 864 | * <td>{@code SERIAL_SEG_DBL_QUADTO}</td> | ||
| 865 | * <td>0x52</td> | ||
| 866 | * <td>4 doubles</td> | ||
| 867 | * <td>A {@link #curveTo curveTo} path segment follows.</td> | ||
| 868 | * </tr> | ||
| 869 | * <tr> | ||
| 870 | * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td> | ||
| 871 | * <td>0x53</td> | ||
| 872 | * <td>6 doubles</td> | ||
| 873 | * <td>A {@link #curveTo curveTo} path segment follows.</td> | ||
| 874 | * </tr> | ||
| 875 | * <tr> | ||
| 876 | * <td>{@code SERIAL_SEG_CLOSE}</td> | ||
| 877 | * <td>0x60</td> | ||
| 878 | * <td></td> | ||
| 879 | * <td>A {@link #closePath closePath} path segment.</td> | ||
| 880 | * </tr> | ||
| 881 | * <tr> | ||
| 882 | * <td>{@code SERIAL_PATH_END}</td> | ||
| 883 | * <td>0x61</td> | ||
| 884 | * <td></td> | ||
| 885 | * <td>There are no more path segments following.</td> | ||
| 886 | * </table> | ||
| 887 | * | ||
| 888 | * @since 1.6 | ||
| 889 | */ | ||
| 890 | private void writeObject(java.io.ObjectOutputStream s) | ||
| 891 | throws java.io.IOException | ||
| 892 | { | ||
| 893 | super.writeObject(s, false); | ||
| 894 | } | ||
| 895 | |||
| 896 | /** | ||
| 897 | * Reads the default serializable fields from the | ||
| 898 | * {@code ObjectInputStream} followed by an explicit | ||
| 899 | * serialization of the path segments stored in this | ||
| 900 | * path. | ||
| 901 | * <p> | ||
| 902 | * There are no default serializable fields as of 1.6. | ||
| 903 | * <p> | ||
| 904 | * The serial data for this object is described in the | ||
| 905 | * writeObject method. | ||
| 906 | * | ||
| 907 | * @since 1.6 | ||
| 908 | */ | ||
| 909 | private void readObject(java.io.ObjectInputStream s) | ||
| 910 | throws java.lang.ClassNotFoundException, java.io.IOException | ||
| 911 | { | ||
| 912 | super.readObject(s, false); | ||
| 913 | } | ||
| 914 | |||
| 915 | static class CopyIterator extends Path2D.Iterator { | ||
| 916 | float floatCoords[]; | ||
| 917 | |||
| 918 | CopyIterator(Path2D.Float p2df) { | ||
| 919 | super(p2df); | ||
| 920 | this.floatCoords = p2df.floatCoords; | ||
| 921 | } | ||
| 922 | |||
| 923 | public int currentSegment(float[] coords) { | ||
| 924 | int type = path.pointTypes[typeIdx]; | ||
| 925 | int numCoords = curvecoords[type]; | ||
| 926 | if (numCoords > 0) { | ||
| 927 | System.arraycopy(floatCoords, pointIdx, | ||
| 928 | coords, 0, numCoords); | ||
| 929 | } | ||
| 930 | return type; | ||
| 931 | } | ||
| 932 | |||
| 933 | public int currentSegment(double[] coords) { | ||
| 934 | int type = path.pointTypes[typeIdx]; | ||
| 935 | int numCoords = curvecoords[type]; | ||
| 936 | if (numCoords > 0) { | ||
| 937 | for (int i = 0; i < numCoords; i++) { | ||
| 938 | coords[i] = floatCoords[pointIdx + i]; | ||
| 939 | } | ||
| 940 | } | ||
| 941 | return type; | ||
| 942 | } | ||
| 943 | } | ||
| 944 | |||
| 945 | static class TxIterator extends Path2D.Iterator { | ||
| 946 | float floatCoords[]; | ||
| 947 | AffineTransform affine; | ||
| 948 | |||
| 949 | TxIterator(Path2D.Float p2df, AffineTransform at) { | ||
| 950 | super(p2df); | ||
| 951 | this.floatCoords = p2df.floatCoords; | ||
| 952 | this.affine = at; | ||
| 953 | } | ||
| 954 | |||
| 955 | public int currentSegment(float[] coords) { | ||
| 956 | int type = path.pointTypes[typeIdx]; | ||
| 957 | int numCoords = curvecoords[type]; | ||
| 958 | if (numCoords > 0) { | ||
| 959 | affine.transform(floatCoords, pointIdx, | ||
| 960 | coords, 0, numCoords / 2); | ||
| 961 | } | ||
| 962 | return type; | ||
| 963 | } | ||
| 964 | |||
| 965 | public int currentSegment(double[] coords) { | ||
| 966 | int type = path.pointTypes[typeIdx]; | ||
| 967 | int numCoords = curvecoords[type]; | ||
| 968 | if (numCoords > 0) { | ||
| 969 | affine.transform(floatCoords, pointIdx, | ||
| 970 | coords, 0, numCoords / 2); | ||
| 971 | } | ||
| 972 | return type; | ||
| 973 | } | ||
| 974 | } | ||
| 975 | |||
| 976 | } | ||
| 977 | |||
| 978 | /** | ||
| 979 | * The {@code Double} class defines a geometric path with | ||
| 980 | * coordinates stored in double precision floating point. | ||
| 981 | * | ||
| 982 | * @since 1.6 | ||
| 983 | */ | ||
| 984 | public static class Double extends Path2D implements Serializable { | ||
| 985 | transient double doubleCoords[]; | ||
| 986 | |||
| 987 | /** | ||
| 988 | * Constructs a new empty double precision {@code Path2D} object | ||
| 989 | * with a default winding rule of {@link #WIND_NON_ZERO}. | ||
| 990 | * | ||
| 991 | * @since 1.6 | ||
| 992 | */ | ||
| 993 | public Double() { | ||
| 994 | this(WIND_NON_ZERO, INIT_SIZE); | ||
| 995 | } | ||
| 996 | |||
| 997 | /** | ||
| 998 | * Constructs a new empty double precision {@code Path2D} object | ||
| 999 | * with the specified winding rule to control operations that | ||
| 1000 | * require the interior of the path to be defined. | ||
| 1001 | * | ||
| 1002 | * @param rule the winding rule | ||
| 1003 | * @see #WIND_EVEN_ODD | ||
| 1004 | * @see #WIND_NON_ZERO | ||
| 1005 | * @since 1.6 | ||
| 1006 | */ | ||
| 1007 | public Double(int rule) { | ||
| 1008 | this(rule, INIT_SIZE); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | /** | ||
| 1012 | * Constructs a new empty double precision {@code Path2D} object | ||
| 1013 | * with the specified winding rule and the specified initial | ||
| 1014 | * capacity to store path segments. | ||
| 1015 | * This number is an initial guess as to how many path segments | ||
| 1016 | * are in the path, but the storage is expanded as needed to store | ||
| 1017 | * whatever path segments are added to this path. | ||
| 1018 | * | ||
| 1019 | * @param rule the winding rule | ||
| 1020 | * @param initialCapacity the estimate for the number of path segments | ||
| 1021 | * in the path | ||
| 1022 | * @see #WIND_EVEN_ODD | ||
| 1023 | * @see #WIND_NON_ZERO | ||
| 1024 | * @since 1.6 | ||
| 1025 | */ | ||
| 1026 | public Double(int rule, int initialCapacity) { | ||
| 1027 | super(rule, initialCapacity); | ||
| 1028 | doubleCoords = new double[initialCapacity * 2]; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | /** | ||
| 1032 | * Constructs a new double precision {@code Path2D} object | ||
| 1033 | * from an arbitrary {@link Shape} object. | ||
| 1034 | * All of the initial geometry and the winding rule for this path are | ||
| 1035 | * taken from the specified {@code Shape} object. | ||
| 1036 | * | ||
| 1037 | * @param s the specified {@code Shape} object | ||
| 1038 | * @since 1.6 | ||
| 1039 | */ | ||
| 1040 | public Double(Shape s) { | ||
| 1041 | this(s, null); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | /** | ||
| 1045 | * Constructs a new double precision {@code Path2D} object | ||
| 1046 | * from an arbitrary {@link Shape} object, transformed by an | ||
| 1047 | * {@link AffineTransform} object. | ||
| 1048 | * All of the initial geometry and the winding rule for this path are | ||
| 1049 | * taken from the specified {@code Shape} object and transformed | ||
| 1050 | * by the specified {@code AffineTransform} object. | ||
| 1051 | * | ||
| 1052 | * @param s the specified {@code Shape} object | ||
| 1053 | * @param at the specified {@code AffineTransform} object | ||
| 1054 | * @since 1.6 | ||
| 1055 | */ | ||
| 1056 | public Double(Shape s, AffineTransform at) { | ||
| 1057 | if (s instanceof Path2D) { | ||
| 1058 | Path2D p2d = (Path2D) s; | ||
| 1059 | setWindingRule(p2d.windingRule); | ||
| 1060 | this.numTypes = p2d.numTypes; | ||
| 1061 | this.pointTypes = Arrays.copyOf(p2d.pointTypes, | ||
| 1062 | p2d.pointTypes.length); | ||
| 1063 | this.numCoords = p2d.numCoords; | ||
| 1064 | this.doubleCoords = p2d.cloneCoordsDouble(at); | ||
| 1065 | } else { | ||
| 1066 | PathIterator pi = s.getPathIterator(at); | ||
| 1067 | setWindingRule(pi.getWindingRule()); | ||
| 1068 | this.pointTypes = new byte[INIT_SIZE]; | ||
| 1069 | this.doubleCoords = new double[INIT_SIZE * 2]; | ||
| 1070 | append(pi, false); | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | float[] cloneCoordsFloat(AffineTransform at) { | ||
| 1075 | float ret[] = new float[doubleCoords.length]; | ||
| 1076 | if (at == null) { | ||
| 1077 | for (int i = 0; i < numCoords; i++) { | ||
| 1078 | ret[i] = (float) doubleCoords[i]; | ||
| 1079 | } | ||
| 1080 | } else { | ||
| 1081 | at.transform(doubleCoords, 0, ret, 0, numCoords / 2); | ||
| 1082 | } | ||
| 1083 | return ret; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | double[] cloneCoordsDouble(AffineTransform at) { | ||
| 1087 | double ret[]; | ||
| 1088 | if (at == null) { | ||
| 1089 | ret = Arrays.copyOf(this.doubleCoords, | ||
| 1090 | this.doubleCoords.length); | ||
| 1091 | } else { | ||
| 1092 | ret = new double[doubleCoords.length]; | ||
| 1093 | at.transform(doubleCoords, 0, ret, 0, numCoords / 2); | ||
| 1094 | } | ||
| 1095 | return ret; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | void append(float x, float y) { | ||
| 1099 | doubleCoords[numCoords++] = x; | ||
| 1100 | doubleCoords[numCoords++] = y; | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | void append(double x, double y) { | ||
| 1104 | doubleCoords[numCoords++] = x; | ||
| 1105 | doubleCoords[numCoords++] = y; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | Point2D getPoint(int coordindex) { | ||
| 1109 | return new Point2D.Double(doubleCoords[coordindex], | ||
| 1110 | doubleCoords[coordindex+1]); | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | void needRoom(boolean needMove, int newCoords) { | ||
| 1114 | if (needMove && numTypes == 0) { | ||
| 1115 | throw new IllegalPathStateException("missing initial moveto "+ | ||
| 1116 | "in path definition"); | ||
| 1117 | } | ||
| 1118 | int size = pointTypes.length; | ||
| 1119 | if (numTypes >= size) { | ||
| 1120 | int grow = size; | ||
| 1121 | if (grow > EXPAND_MAX) { | ||
| 1122 | grow = EXPAND_MAX; | ||
| 1123 | } | ||
| 1124 | pointTypes = Arrays.copyOf(pointTypes, size+grow); | ||
| 1125 | } | ||
| 1126 | size = doubleCoords.length; | ||
| 1127 | if (numCoords + newCoords > size) { | ||
| 1128 | int grow = size; | ||
| 1129 | if (grow > EXPAND_MAX * 2) { | ||
| 1130 | grow = EXPAND_MAX * 2; | ||
| 1131 | } | ||
| 1132 | if (grow < newCoords) { | ||
| 1133 | grow = newCoords; | ||
| 1134 | } | ||
| 1135 | doubleCoords = Arrays.copyOf(doubleCoords, size+grow); | ||
| 1136 | } | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | /** | ||
| 1140 | * {@inheritDoc} | ||
| 1141 | * @since 1.6 | ||
| 1142 | */ | ||
| 1143 | public final synchronized void moveTo(double x, double y) { | ||
| 1144 | if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) { | ||
| 1145 | doubleCoords[numCoords-2] = x; | ||
| 1146 | doubleCoords[numCoords-1] = y; | ||
| 1147 | } else { | ||
| 1148 | needRoom(false, 2); | ||
| 1149 | pointTypes[numTypes++] = SEG_MOVETO; | ||
| 1150 | doubleCoords[numCoords++] = x; | ||
| 1151 | doubleCoords[numCoords++] = y; | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | /** | ||
| 1156 | * {@inheritDoc} | ||
| 1157 | * @since 1.6 | ||
| 1158 | */ | ||
| 1159 | public final synchronized void lineTo(double x, double y) { | ||
| 1160 | needRoom(true, 2); | ||
| 1161 | pointTypes[numTypes++] = SEG_LINETO; | ||
| 1162 | doubleCoords[numCoords++] = x; | ||
| 1163 | doubleCoords[numCoords++] = y; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /** | ||
| 1167 | * {@inheritDoc} | ||
| 1168 | * @since 1.6 | ||
| 1169 | */ | ||
| 1170 | public final synchronized void quadTo(double x1, double y1, | ||
| 1171 | double x2, double y2) | ||
| 1172 | { | ||
| 1173 | needRoom(true, 4); | ||
| 1174 | pointTypes[numTypes++] = SEG_QUADTO; | ||
| 1175 | doubleCoords[numCoords++] = x1; | ||
| 1176 | doubleCoords[numCoords++] = y1; | ||
| 1177 | doubleCoords[numCoords++] = x2; | ||
| 1178 | doubleCoords[numCoords++] = y2; | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | /** | ||
| 1182 | * {@inheritDoc} | ||
| 1183 | * @since 1.6 | ||
| 1184 | */ | ||
| 1185 | public final synchronized void curveTo(double x1, double y1, | ||
| 1186 | double x2, double y2, | ||
| 1187 | double x3, double y3) | ||
| 1188 | { | ||
| 1189 | needRoom(true, 6); | ||
| 1190 | pointTypes[numTypes++] = SEG_CUBICTO; | ||
| 1191 | doubleCoords[numCoords++] = x1; | ||
| 1192 | doubleCoords[numCoords++] = y1; | ||
| 1193 | doubleCoords[numCoords++] = x2; | ||
| 1194 | doubleCoords[numCoords++] = y2; | ||
| 1195 | doubleCoords[numCoords++] = x3; | ||
| 1196 | doubleCoords[numCoords++] = y3; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | int pointCrossings(double px, double py) { | ||
| 1200 | double movx, movy, curx, cury, endx, endy; | ||
| 1201 | double coords[] = doubleCoords; | ||
| 1202 | curx = movx = coords[0]; | ||
| 1203 | cury = movy = coords[1]; | ||
| 1204 | int crossings = 0; | ||
| 1205 | int ci = 2; | ||
| 1206 | for (int i = 1; i < numTypes; i++) { | ||
| 1207 | switch (pointTypes[i]) { | ||
| 1208 | case PathIterator.SEG_MOVETO: | ||
| 1209 | if (cury != movy) { | ||
| 1210 | crossings += | ||
| 1211 | Curve.pointCrossingsForLine(px, py, | ||
| 1212 | curx, cury, | ||
| 1213 | movx, movy); | ||
| 1214 | } | ||
| 1215 | movx = curx = coords[ci++]; | ||
| 1216 | movy = cury = coords[ci++]; | ||
| 1217 | break; | ||
| 1218 | case PathIterator.SEG_LINETO: | ||
| 1219 | crossings += | ||
| 1220 | Curve.pointCrossingsForLine(px, py, | ||
| 1221 | curx, cury, | ||
| 1222 | endx = coords[ci++], | ||
| 1223 | endy = coords[ci++]); | ||
| 1224 | curx = endx; | ||
| 1225 | cury = endy; | ||
| 1226 | break; | ||
| 1227 | case PathIterator.SEG_QUADTO: | ||
| 1228 | crossings += | ||
| 1229 | Curve.pointCrossingsForQuad(px, py, | ||
| 1230 | curx, cury, | ||
| 1231 | coords[ci++], | ||
| 1232 | coords[ci++], | ||
| 1233 | endx = coords[ci++], | ||
| 1234 | endy = coords[ci++], | ||
| 1235 | 0); | ||
| 1236 | curx = endx; | ||
| 1237 | cury = endy; | ||
| 1238 | break; | ||
| 1239 | case PathIterator.SEG_CUBICTO: | ||
| 1240 | crossings += | ||
| 1241 | Curve.pointCrossingsForCubic(px, py, | ||
| 1242 | curx, cury, | ||
| 1243 | coords[ci++], | ||
| 1244 | coords[ci++], | ||
| 1245 | coords[ci++], | ||
| 1246 | coords[ci++], | ||
| 1247 | endx = coords[ci++], | ||
| 1248 | endy = coords[ci++], | ||
| 1249 | 0); | ||
| 1250 | curx = endx; | ||
| 1251 | cury = endy; | ||
| 1252 | break; | ||
| 1253 | case PathIterator.SEG_CLOSE: | ||
| 1254 | if (cury != movy) { | ||
| 1255 | crossings += | ||
| 1256 | Curve.pointCrossingsForLine(px, py, | ||
| 1257 | curx, cury, | ||
| 1258 | movx, movy); | ||
| 1259 | } | ||
| 1260 | curx = movx; | ||
| 1261 | cury = movy; | ||
| 1262 | break; | ||
| 1263 | } | ||
| 1264 | } | ||
| 1265 | if (cury != movy) { | ||
| 1266 | crossings += | ||
| 1267 | Curve.pointCrossingsForLine(px, py, | ||
| 1268 | curx, cury, | ||
| 1269 | movx, movy); | ||
| 1270 | } | ||
| 1271 | return crossings; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | int rectCrossings(double rxmin, double rymin, | ||
| 1275 | double rxmax, double rymax) | ||
| 1276 | { | ||
| 1277 | double coords[] = doubleCoords; | ||
| 1278 | double curx, cury, movx, movy, endx, endy; | ||
| 1279 | curx = movx = coords[0]; | ||
| 1280 | cury = movy = coords[1]; | ||
| 1281 | int crossings = 0; | ||
| 1282 | int ci = 2; | ||
| 1283 | for (int i = 1; | ||
| 1284 | crossings != Curve.RECT_INTERSECTS && i < numTypes; | ||
| 1285 | i++) | ||
| 1286 | { | ||
| 1287 | switch (pointTypes[i]) { | ||
| 1288 | case PathIterator.SEG_MOVETO: | ||
| 1289 | if (curx != movx || cury != movy) { | ||
| 1290 | crossings = | ||
| 1291 | Curve.rectCrossingsForLine(crossings, | ||
| 1292 | rxmin, rymin, | ||
| 1293 | rxmax, rymax, | ||
| 1294 | curx, cury, | ||
| 1295 | movx, movy); | ||
| 1296 | } | ||
| 1297 | // Count should always be a multiple of 2 here. | ||
| 1298 | // assert((crossings & 1) != 0); | ||
| 1299 | movx = curx = coords[ci++]; | ||
| 1300 | movy = cury = coords[ci++]; | ||
| 1301 | break; | ||
| 1302 | case PathIterator.SEG_LINETO: | ||
| 1303 | endx = coords[ci++]; | ||
| 1304 | endy = coords[ci++]; | ||
| 1305 | crossings = | ||
| 1306 | Curve.rectCrossingsForLine(crossings, | ||
| 1307 | rxmin, rymin, | ||
| 1308 | rxmax, rymax, | ||
| 1309 | curx, cury, | ||
| 1310 | endx, endy); | ||
| 1311 | curx = endx; | ||
| 1312 | cury = endy; | ||
| 1313 | break; | ||
| 1314 | case PathIterator.SEG_QUADTO: | ||
| 1315 | crossings = | ||
| 1316 | Curve.rectCrossingsForQuad(crossings, | ||
| 1317 | rxmin, rymin, | ||
| 1318 | rxmax, rymax, | ||
| 1319 | curx, cury, | ||
| 1320 | coords[ci++], | ||
| 1321 | coords[ci++], | ||
| 1322 | endx = coords[ci++], | ||
| 1323 | endy = coords[ci++], | ||
| 1324 | 0); | ||
| 1325 | curx = endx; | ||
| 1326 | cury = endy; | ||
| 1327 | break; | ||
| 1328 | case PathIterator.SEG_CUBICTO: | ||
| 1329 | crossings = | ||
| 1330 | Curve.rectCrossingsForCubic(crossings, | ||
| 1331 | rxmin, rymin, | ||
| 1332 | rxmax, rymax, | ||
| 1333 | curx, cury, | ||
| 1334 | coords[ci++], | ||
| 1335 | coords[ci++], | ||
| 1336 | coords[ci++], | ||
| 1337 | coords[ci++], | ||
| 1338 | endx = coords[ci++], | ||
| 1339 | endy = coords[ci++], | ||
| 1340 | 0); | ||
| 1341 | curx = endx; | ||
| 1342 | cury = endy; | ||
| 1343 | break; | ||
| 1344 | case PathIterator.SEG_CLOSE: | ||
| 1345 | if (curx != movx || cury != movy) { | ||
| 1346 | crossings = | ||
| 1347 | Curve.rectCrossingsForLine(crossings, | ||
| 1348 | rxmin, rymin, | ||
| 1349 | rxmax, rymax, | ||
| 1350 | curx, cury, | ||
| 1351 | movx, movy); | ||
| 1352 | } | ||
| 1353 | curx = movx; | ||
| 1354 | cury = movy; | ||
| 1355 | // Count should always be a multiple of 2 here. | ||
| 1356 | // assert((crossings & 1) != 0); | ||
| 1357 | break; | ||
| 1358 | } | ||
| 1359 | } | ||
| 1360 | if (crossings != Curve.RECT_INTERSECTS && | ||
| 1361 | (curx != movx || cury != movy)) | ||
| 1362 | { | ||
| 1363 | crossings = | ||
| 1364 | Curve.rectCrossingsForLine(crossings, | ||
| 1365 | rxmin, rymin, | ||
| 1366 | rxmax, rymax, | ||
| 1367 | curx, cury, | ||
| 1368 | movx, movy); | ||
| 1369 | } | ||
| 1370 | // Count should always be a multiple of 2 here. | ||
| 1371 | // assert((crossings & 1) != 0); | ||
| 1372 | return crossings; | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | /** | ||
| 1376 | * {@inheritDoc} | ||
| 1377 | * @since 1.6 | ||
| 1378 | */ | ||
| 1379 | public final void append(PathIterator pi, boolean connect) { | ||
| 1380 | double coords[] = new double[6]; | ||
| 1381 | while (!pi.isDone()) { | ||
| 1382 | switch (pi.currentSegment(coords)) { | ||
| 1383 | case SEG_MOVETO: | ||
| 1384 | if (!connect || numTypes < 1 || numCoords < 1) { | ||
| 1385 | moveTo(coords[0], coords[1]); | ||
| 1386 | break; | ||
| 1387 | } | ||
| 1388 | if (pointTypes[numTypes - 1] != SEG_CLOSE && | ||
| 1389 | doubleCoords[numCoords-2] == coords[0] && | ||
| 1390 | doubleCoords[numCoords-1] == coords[1]) | ||
| 1391 | { | ||
| 1392 | // Collapse out initial moveto/lineto | ||
| 1393 | break; | ||
| 1394 | } | ||
| 1395 | // NO BREAK; | ||
| 1396 | case SEG_LINETO: | ||
| 1397 | lineTo(coords[0], coords[1]); | ||
| 1398 | break; | ||
| 1399 | case SEG_QUADTO: | ||
| 1400 | quadTo(coords[0], coords[1], | ||
| 1401 | coords[2], coords[3]); | ||
| 1402 | break; | ||
| 1403 | case SEG_CUBICTO: | ||
| 1404 | curveTo(coords[0], coords[1], | ||
| 1405 | coords[2], coords[3], | ||
| 1406 | coords[4], coords[5]); | ||
| 1407 | break; | ||
| 1408 | case SEG_CLOSE: | ||
| 1409 | closePath(); | ||
| 1410 | break; | ||
| 1411 | } | ||
| 1412 | pi.next(); | ||
| 1413 | connect = false; | ||
| 1414 | } | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | /** | ||
| 1418 | * {@inheritDoc} | ||
| 1419 | * @since 1.6 | ||
| 1420 | */ | ||
| 1421 | public final void transform(AffineTransform at) { | ||
| 1422 | at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2); | ||
| 1423 | } | ||
| 1424 | |||
| 1425 | /** | ||
| 1426 | * {@inheritDoc} | ||
| 1427 | * @since 1.6 | ||
| 1428 | */ | ||
| 1429 | public final synchronized Rectangle2D getBounds2D() { | ||
| 1430 | double x1, y1, x2, y2; | ||
| 1431 | int i = numCoords; | ||
| 1432 | if (i > 0) { | ||
| 1433 | y1 = y2 = doubleCoords[--i]; | ||
| 1434 | x1 = x2 = doubleCoords[--i]; | ||
| 1435 | while (i > 0) { | ||
| 1436 | double y = doubleCoords[--i]; | ||
| 1437 | double x = doubleCoords[--i]; | ||
| 1438 | if (x < x1) x1 = x; | ||
| 1439 | if (y < y1) y1 = y; | ||
| 1440 | if (x > x2) x2 = x; | ||
| 1441 | if (y > y2) y2 = y; | ||
| 1442 | } | ||
| 1443 | } else { | ||
| 1444 | x1 = y1 = x2 = y2 = 0.0; | ||
| 1445 | } | ||
| 1446 | return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1); | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | /** | ||
| 1450 | * {@inheritDoc} | ||
| 1451 | * <p> | ||
| 1452 | * The iterator for this class is not multi-threaded safe, | ||
| 1453 | * which means that the {@code Path2D} class does not | ||
| 1454 | * guarantee that modifications to the geometry of this | ||
| 1455 | * {@code Path2D} object do not affect any iterations of | ||
| 1456 | * that geometry that are already in process. | ||
| 1457 | * | ||
| 1458 | * @param at an {@code AffineTransform} | ||
| 1459 | * @return a new {@code PathIterator} that iterates along the boundary | ||
| 1460 | * of this {@code Shape} and provides access to the geometry | ||
| 1461 | * of this {@code Shape}'s outline | ||
| 1462 | * @since 1.6 | ||
| 1463 | */ | ||
| 1464 | public final PathIterator getPathIterator(AffineTransform at) { | ||
| 1465 | if (at == null) { | ||
| 1466 | return new CopyIterator(this); | ||
| 1467 | } else { | ||
| 1468 | return new TxIterator(this, at); | ||
| 1469 | } | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | /** | ||
| 1473 | * Creates a new object of the same class as this object. | ||
| 1474 | * | ||
| 1475 | * @return a clone of this instance. | ||
| 1476 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 1477 | * @see java.lang.Cloneable | ||
| 1478 | * @since 1.6 | ||
| 1479 | */ | ||
| 1480 | public final Object clone() { | ||
| 1481 | // Note: It would be nice to have this return Path2D | ||
| 1482 | // but one of our subclasses (GeneralPath) needs to | ||
| 1483 | // offer "public Object clone()" for backwards | ||
| 1484 | // compatibility so we cannot restrict it further. | ||
| 1485 | // REMIND: Can we do both somehow? | ||
| 1486 | return new Path2D.Double(this); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | /* | ||
| 1490 | * JDK 1.6 serialVersionUID | ||
| 1491 | */ | ||
| 1492 | private static final long serialVersionUID = 1826762518450014216L; | ||
| 1493 | |||
| 1494 | /** | ||
| 1495 | * Writes the default serializable fields to the | ||
| 1496 | * {@code ObjectOutputStream} followed by an explicit | ||
| 1497 | * serialization of the path segments stored in this | ||
| 1498 | * path. | ||
| 1499 | * | ||
| 1500 | * @serialData | ||
| 1501 | * <a name="Path2DSerialData"><!-- --></a> | ||
| 1502 | * <ol> | ||
| 1503 | * <li>The default serializable fields. | ||
| 1504 | * There are no default serializable fields as of 1.6. | ||
| 1505 | * <li>followed by | ||
| 1506 | * a byte indicating the storage type of the original object | ||
| 1507 | * as a hint (SERIAL_STORAGE_DBL_ARRAY) | ||
| 1508 | * <li>followed by | ||
| 1509 | * an integer indicating the number of path segments to follow (NP) | ||
| 1510 | * or -1 to indicate an unknown number of path segments follows | ||
| 1511 | * <li>followed by | ||
| 1512 | * an integer indicating the total number of coordinates to follow (NC) | ||
| 1513 | * or -1 to indicate an unknown number of coordinates follows | ||
| 1514 | * (NC should always be even since coordinates always appear in pairs | ||
| 1515 | * representing an x,y pair) | ||
| 1516 | * <li>followed by | ||
| 1517 | * a byte indicating the winding rule | ||
| 1518 | * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or | ||
| 1519 | * {@link #WIND_NON_ZERO WIND_NON_ZERO}) | ||
| 1520 | * <li>followed by | ||
| 1521 | * NP (or unlimited if NP < 0) sets of values consisting of | ||
| 1522 | * a single byte indicating a path segment type | ||
| 1523 | * followed by one or more pairs of float or double | ||
| 1524 | * values representing the coordinates of the path segment | ||
| 1525 | * <li>followed by | ||
| 1526 | * a byte indicating the end of the path (SERIAL_PATH_END). | ||
| 1527 | * </ol> | ||
| 1528 | * <p> | ||
| 1529 | * The following byte value constants are used in the serialized form | ||
| 1530 | * of {@code Path2D} objects: | ||
| 1531 | * <table> | ||
| 1532 | * <tr> | ||
| 1533 | * <th>Constant Name</th> | ||
| 1534 | * <th>Byte Value</th> | ||
| 1535 | * <th>Followed by</th> | ||
| 1536 | * <th>Description</th> | ||
| 1537 | * </tr> | ||
| 1538 | * <tr> | ||
| 1539 | * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td> | ||
| 1540 | * <td>0x30</td> | ||
| 1541 | * <td></td> | ||
| 1542 | * <td>A hint that the original {@code Path2D} object stored | ||
| 1543 | * the coordinates in a Java array of floats.</td> | ||
| 1544 | * </tr> | ||
| 1545 | * <tr> | ||
| 1546 | * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td> | ||
| 1547 | * <td>0x31</td> | ||
| 1548 | * <td></td> | ||
| 1549 | * <td>A hint that the original {@code Path2D} object stored | ||
| 1550 | * the coordinates in a Java array of doubles.</td> | ||
| 1551 | * </tr> | ||
| 1552 | * <tr> | ||
| 1553 | * <td>{@code SERIAL_SEG_FLT_MOVETO}</td> | ||
| 1554 | * <td>0x40</td> | ||
| 1555 | * <td>2 floats</td> | ||
| 1556 | * <td>A {@link #moveTo moveTo} path segment follows.</td> | ||
| 1557 | * </tr> | ||
| 1558 | * <tr> | ||
| 1559 | * <td>{@code SERIAL_SEG_FLT_LINETO}</td> | ||
| 1560 | * <td>0x41</td> | ||
| 1561 | * <td>2 floats</td> | ||
| 1562 | * <td>A {@link #lineTo lineTo} path segment follows.</td> | ||
| 1563 | * </tr> | ||
| 1564 | * <tr> | ||
| 1565 | * <td>{@code SERIAL_SEG_FLT_QUADTO}</td> | ||
| 1566 | * <td>0x42</td> | ||
| 1567 | * <td>4 floats</td> | ||
| 1568 | * <td>A {@link #quadTo quadTo} path segment follows.</td> | ||
| 1569 | * </tr> | ||
| 1570 | * <tr> | ||
| 1571 | * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td> | ||
| 1572 | * <td>0x43</td> | ||
| 1573 | * <td>6 floats</td> | ||
| 1574 | * <td>A {@link #curveTo curveTo} path segment follows.</td> | ||
| 1575 | * </tr> | ||
| 1576 | * <tr> | ||
| 1577 | * <td>{@code SERIAL_SEG_DBL_MOVETO}</td> | ||
| 1578 | * <td>0x50</td> | ||
| 1579 | * <td>2 doubles</td> | ||
| 1580 | * <td>A {@link #moveTo moveTo} path segment follows.</td> | ||
| 1581 | * </tr> | ||
| 1582 | * <tr> | ||
| 1583 | * <td>{@code SERIAL_SEG_DBL_LINETO}</td> | ||
| 1584 | * <td>0x51</td> | ||
| 1585 | * <td>2 doubles</td> | ||
| 1586 | * <td>A {@link #lineTo lineTo} path segment follows.</td> | ||
| 1587 | * </tr> | ||
| 1588 | * <tr> | ||
| 1589 | * <td>{@code SERIAL_SEG_DBL_QUADTO}</td> | ||
| 1590 | * <td>0x52</td> | ||
| 1591 | * <td>4 doubles</td> | ||
| 1592 | * <td>A {@link #curveTo curveTo} path segment follows.</td> | ||
| 1593 | * </tr> | ||
| 1594 | * <tr> | ||
| 1595 | * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td> | ||
| 1596 | * <td>0x53</td> | ||
| 1597 | * <td>6 doubles</td> | ||
| 1598 | * <td>A {@link #curveTo curveTo} path segment follows.</td> | ||
| 1599 | * </tr> | ||
| 1600 | * <tr> | ||
| 1601 | * <td>{@code SERIAL_SEG_CLOSE}</td> | ||
| 1602 | * <td>0x60</td> | ||
| 1603 | * <td></td> | ||
| 1604 | * <td>A {@link #closePath closePath} path segment.</td> | ||
| 1605 | * </tr> | ||
| 1606 | * <tr> | ||
| 1607 | * <td>{@code SERIAL_PATH_END}</td> | ||
| 1608 | * <td>0x61</td> | ||
| 1609 | * <td></td> | ||
| 1610 | * <td>There are no more path segments following.</td> | ||
| 1611 | * </table> | ||
| 1612 | * | ||
| 1613 | * @since 1.6 | ||
| 1614 | */ | ||
| 1615 | private void writeObject(java.io.ObjectOutputStream s) | ||
| 1616 | throws java.io.IOException | ||
| 1617 | { | ||
| 1618 | super.writeObject(s, true); | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | /** | ||
| 1622 | * Reads the default serializable fields from the | ||
| 1623 | * {@code ObjectInputStream} followed by an explicit | ||
| 1624 | * serialization of the path segments stored in this | ||
| 1625 | * path. | ||
| 1626 | * <p> | ||
| 1627 | * There are no default serializable fields as of 1.6. | ||
| 1628 | * <p> | ||
| 1629 | * The serial data for this object is described in the | ||
| 1630 | * writeObject method. | ||
| 1631 | * | ||
| 1632 | * @since 1.6 | ||
| 1633 | */ | ||
| 1634 | private void readObject(java.io.ObjectInputStream s) | ||
| 1635 | throws java.lang.ClassNotFoundException, java.io.IOException | ||
| 1636 | { | ||
| 1637 | super.readObject(s, true); | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | static class CopyIterator extends Path2D.Iterator { | ||
| 1641 | double doubleCoords[]; | ||
| 1642 | |||
| 1643 | CopyIterator(Path2D.Double p2dd) { | ||
| 1644 | super(p2dd); | ||
| 1645 | this.doubleCoords = p2dd.doubleCoords; | ||
| 1646 | } | ||
| 1647 | |||
| 1648 | public int currentSegment(float[] coords) { | ||
| 1649 | int type = path.pointTypes[typeIdx]; | ||
| 1650 | int numCoords = curvecoords[type]; | ||
| 1651 | if (numCoords > 0) { | ||
| 1652 | for (int i = 0; i < numCoords; i++) { | ||
| 1653 | coords[i] = (float) doubleCoords[pointIdx + i]; | ||
| 1654 | } | ||
| 1655 | } | ||
| 1656 | return type; | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | public int currentSegment(double[] coords) { | ||
| 1660 | int type = path.pointTypes[typeIdx]; | ||
| 1661 | int numCoords = curvecoords[type]; | ||
| 1662 | if (numCoords > 0) { | ||
| 1663 | System.arraycopy(doubleCoords, pointIdx, | ||
| 1664 | coords, 0, numCoords); | ||
| 1665 | } | ||
| 1666 | return type; | ||
| 1667 | } | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | static class TxIterator extends Path2D.Iterator { | ||
| 1671 | double doubleCoords[]; | ||
| 1672 | AffineTransform affine; | ||
| 1673 | |||
| 1674 | TxIterator(Path2D.Double p2dd, AffineTransform at) { | ||
| 1675 | super(p2dd); | ||
| 1676 | this.doubleCoords = p2dd.doubleCoords; | ||
| 1677 | this.affine = at; | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | public int currentSegment(float[] coords) { | ||
| 1681 | int type = path.pointTypes[typeIdx]; | ||
| 1682 | int numCoords = curvecoords[type]; | ||
| 1683 | if (numCoords > 0) { | ||
| 1684 | affine.transform(doubleCoords, pointIdx, | ||
| 1685 | coords, 0, numCoords / 2); | ||
| 1686 | } | ||
| 1687 | return type; | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | public int currentSegment(double[] coords) { | ||
| 1691 | int type = path.pointTypes[typeIdx]; | ||
| 1692 | int numCoords = curvecoords[type]; | ||
| 1693 | if (numCoords > 0) { | ||
| 1694 | affine.transform(doubleCoords, pointIdx, | ||
| 1695 | coords, 0, numCoords / 2); | ||
| 1696 | } | ||
| 1697 | return type; | ||
| 1698 | } | ||
| 1699 | } | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | /** | ||
| 1703 | * Adds a point to the path by moving to the specified | ||
| 1704 | * coordinates specified in double precision. | ||
| 1705 | * | ||
| 1706 | * @param x the specified X coordinate | ||
| 1707 | * @param y the specified Y coordinate | ||
| 1708 | * @since 1.6 | ||
| 1709 | */ | ||
| 1710 | public abstract void moveTo(double x, double y); | ||
| 1711 | |||
| 1712 | /** | ||
| 1713 | * Adds a point to the path by drawing a straight line from the | ||
| 1714 | * current coordinates to the new specified coordinates | ||
| 1715 | * specified in double precision. | ||
| 1716 | * | ||
| 1717 | * @param x the specified X coordinate | ||
| 1718 | * @param y the specified Y coordinate | ||
| 1719 | * @since 1.6 | ||
| 1720 | */ | ||
| 1721 | public abstract void lineTo(double x, double y); | ||
| 1722 | |||
| 1723 | /** | ||
| 1724 | * Adds a curved segment, defined by two new points, to the path by | ||
| 1725 | * drawing a Quadratic curve that intersects both the current | ||
| 1726 | * coordinates and the specified coordinates {@code (x2,y2)}, | ||
| 1727 | * using the specified point {@code (x1,y1)} as a quadratic | ||
| 1728 | * parametric control point. | ||
| 1729 | * All coordinates are specified in double precision. | ||
| 1730 | * | ||
| 1731 | * @param x1 the X coordinate of the quadratic control point | ||
| 1732 | * @param y1 the Y coordinate of the quadratic control point | ||
| 1733 | * @param x2 the X coordinate of the final end point | ||
| 1734 | * @param y2 the Y coordinate of the final end point | ||
| 1735 | * @since 1.6 | ||
| 1736 | */ | ||
| 1737 | public abstract void quadTo(double x1, double y1, | ||
| 1738 | double x2, double y2); | ||
| 1739 | |||
| 1740 | /** | ||
| 1741 | * Adds a curved segment, defined by three new points, to the path by | ||
| 1742 | * drawing a Bézier curve that intersects both the current | ||
| 1743 | * coordinates and the specified coordinates {@code (x3,y3)}, | ||
| 1744 | * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as | ||
| 1745 | * Bézier control points. | ||
| 1746 | * All coordinates are specified in double precision. | ||
| 1747 | * | ||
| 1748 | * @param x1 the X coordinate of the first Bézier control point | ||
| 1749 | * @param y1 the Y coordinate of the first Bézier control point | ||
| 1750 | * @param x2 the X coordinate of the second Bézier control point | ||
| 1751 | * @param y2 the Y coordinate of the second Bézier control point | ||
| 1752 | * @param x3 the X coordinate of the final end point | ||
| 1753 | * @param y3 the Y coordinate of the final end point | ||
| 1754 | * @since 1.6 | ||
| 1755 | */ | ||
| 1756 | public abstract void curveTo(double x1, double y1, | ||
| 1757 | double x2, double y2, | ||
| 1758 | double x3, double y3); | ||
| 1759 | |||
| 1760 | /** | ||
| 1761 | * Closes the current subpath by drawing a straight line back to | ||
| 1762 | * the coordinates of the last {@code moveTo}. If the path is already | ||
| 1763 | * closed then this method has no effect. | ||
| 1764 | * | ||
| 1765 | * @since 1.6 | ||
| 1766 | */ | ||
| 1767 | public final synchronized void closePath() { | ||
| 1768 | if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) { | ||
| 1769 | needRoom(true, 0); | ||
| 1770 | pointTypes[numTypes++] = SEG_CLOSE; | ||
| 1771 | } | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | /** | ||
| 1775 | * Appends the geometry of the specified {@code Shape} object to the | ||
| 1776 | * path, possibly connecting the new geometry to the existing path | ||
| 1777 | * segments with a line segment. | ||
| 1778 | * If the {@code connect} parameter is {@code true} and the | ||
| 1779 | * path is not empty then any initial {@code moveTo} in the | ||
| 1780 | * geometry of the appended {@code Shape} | ||
| 1781 | * is turned into a {@code lineTo} segment. | ||
| 1782 | * If the destination coordinates of such a connecting {@code lineTo} | ||
| 1783 | * segment match the ending coordinates of a currently open | ||
| 1784 | * subpath then the segment is omitted as superfluous. | ||
| 1785 | * The winding rule of the specified {@code Shape} is ignored | ||
| 1786 | * and the appended geometry is governed by the winding | ||
| 1787 | * rule specified for this path. | ||
| 1788 | * | ||
| 1789 | * @param s the {@code Shape} whose geometry is appended | ||
| 1790 | * to this path | ||
| 1791 | * @param connect a boolean to control whether or not to turn an initial | ||
| 1792 | * {@code moveTo} segment into a {@code lineTo} segment | ||
| 1793 | * to connect the new geometry to the existing path | ||
| 1794 | * @since 1.6 | ||
| 1795 | */ | ||
| 1796 | public final void append(Shape s, boolean connect) { | ||
| 1797 | append(s.getPathIterator(null), connect); | ||
| 1798 | } | ||
| 1799 | |||
| 1800 | /** | ||
| 1801 | * Appends the geometry of the specified | ||
| 1802 | * {@link PathIterator} object | ||
| 1803 | * to the path, possibly connecting the new geometry to the existing | ||
| 1804 | * path segments with a line segment. | ||
| 1805 | * If the {@code connect} parameter is {@code true} and the | ||
| 1806 | * path is not empty then any initial {@code moveTo} in the | ||
| 1807 | * geometry of the appended {@code Shape} is turned into a | ||
| 1808 | * {@code lineTo} segment. | ||
| 1809 | * If the destination coordinates of such a connecting {@code lineTo} | ||
| 1810 | * segment match the ending coordinates of a currently open | ||
| 1811 | * subpath then the segment is omitted as superfluous. | ||
| 1812 | * The winding rule of the specified {@code Shape} is ignored | ||
| 1813 | * and the appended geometry is governed by the winding | ||
| 1814 | * rule specified for this path. | ||
| 1815 | * | ||
| 1816 | * @param pi the {@code PathIterator} whose geometry is appended to | ||
| 1817 | * this path | ||
| 1818 | * @param connect a boolean to control whether or not to turn an initial | ||
| 1819 | * {@code moveTo} segment into a {@code lineTo} segment | ||
| 1820 | * to connect the new geometry to the existing path | ||
| 1821 | * @since 1.6 | ||
| 1822 | */ | ||
| 1823 | public abstract void append(PathIterator pi, boolean connect); | ||
| 1824 | |||
| 1825 | /** | ||
| 1826 | * Returns the fill style winding rule. | ||
| 1827 | * | ||
| 1828 | * @return an integer representing the current winding rule. | ||
| 1829 | * @see #WIND_EVEN_ODD | ||
| 1830 | * @see #WIND_NON_ZERO | ||
| 1831 | * @see #setWindingRule | ||
| 1832 | * @since 1.6 | ||
| 1833 | */ | ||
| 1834 | public final synchronized int getWindingRule() { | ||
| 1835 | return windingRule; | ||
| 1836 | } | ||
| 1837 | |||
| 1838 | /** | ||
| 1839 | * Sets the winding rule for this path to the specified value. | ||
| 1840 | * | ||
| 1841 | * @param rule an integer representing the specified | ||
| 1842 | * winding rule | ||
| 1843 | * @exception IllegalArgumentException if | ||
| 1844 | * {@code rule} is not either | ||
| 1845 | * {@link #WIND_EVEN_ODD} or | ||
| 1846 | * {@link #WIND_NON_ZERO} | ||
| 1847 | * @see #getWindingRule | ||
| 1848 | * @since 1.6 | ||
| 1849 | */ | ||
| 1850 | public final void setWindingRule(int rule) { | ||
| 1851 | if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) { | ||
| 1852 | throw new IllegalArgumentException("winding rule must be "+ | ||
| 1853 | "WIND_EVEN_ODD or "+ | ||
| 1854 | "WIND_NON_ZERO"); | ||
| 1855 | } | ||
| 1856 | windingRule = rule; | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | /** | ||
| 1860 | * Returns the coordinates most recently added to the end of the path | ||
| 1861 | * as a {@link Point2D} object. | ||
| 1862 | * | ||
| 1863 | * @return a {@code Point2D} object containing the ending coordinates of | ||
| 1864 | * the path or {@code null} if there are no points in the path. | ||
| 1865 | * @since 1.6 | ||
| 1866 | */ | ||
| 1867 | public final synchronized Point2D getCurrentPoint() { | ||
| 1868 | int index = numCoords; | ||
| 1869 | if (numTypes < 1 || index < 1) { | ||
| 1870 | return null; | ||
| 1871 | } | ||
| 1872 | if (pointTypes[numTypes - 1] == SEG_CLOSE) { | ||
| 1873 | loop: | ||
| 1874 | for (int i = numTypes - 2; i > 0; i--) { | ||
| 1875 | switch (pointTypes[i]) { | ||
| 1876 | case SEG_MOVETO: | ||
| 1877 | break loop; | ||
| 1878 | case SEG_LINETO: | ||
| 1879 | index -= 2; | ||
| 1880 | break; | ||
| 1881 | case SEG_QUADTO: | ||
| 1882 | index -= 4; | ||
| 1883 | break; | ||
| 1884 | case SEG_CUBICTO: | ||
| 1885 | index -= 6; | ||
| 1886 | break; | ||
| 1887 | case SEG_CLOSE: | ||
| 1888 | break; | ||
| 1889 | } | ||
| 1890 | } | ||
| 1891 | } | ||
| 1892 | return getPoint(index - 2); | ||
| 1893 | } | ||
| 1894 | |||
| 1895 | /** | ||
| 1896 | * Resets the path to empty. The append position is set back to the | ||
| 1897 | * beginning of the path and all coordinates and point types are | ||
| 1898 | * forgotten. | ||
| 1899 | * | ||
| 1900 | * @since 1.6 | ||
| 1901 | */ | ||
| 1902 | public final synchronized void reset() { | ||
| 1903 | numTypes = numCoords = 0; | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | /** | ||
| 1907 | * Transforms the geometry of this path using the specified | ||
| 1908 | * {@link AffineTransform}. | ||
| 1909 | * The geometry is transformed in place, which permanently changes the | ||
| 1910 | * boundary defined by this object. | ||
| 1911 | * | ||
| 1912 | * @param at the {@code AffineTransform} used to transform the area | ||
| 1913 | * @since 1.6 | ||
| 1914 | */ | ||
| 1915 | public abstract void transform(AffineTransform at); | ||
| 1916 | |||
| 1917 | /** | ||
| 1918 | * Returns a new {@code Shape} representing a transformed version | ||
| 1919 | * of this {@code Path2D}. | ||
| 1920 | * Note that the exact type and coordinate precision of the return | ||
| 1921 | * value is not specified for this method. | ||
| 1922 | * The method will return a Shape that contains no less precision | ||
| 1923 | * for the transformed geometry than this {@code Path2D} currently | ||
| 1924 | * maintains, but it may contain no more precision either. | ||
| 1925 | * If the tradeoff of precision vs. storage size in the result is | ||
| 1926 | * important then the convenience constructors in the | ||
| 1927 | * {@link Path2D.Float#Path2D.Float(Shape, AffineTransform) Path2D.Float} | ||
| 1928 | * and | ||
| 1929 | * {@link Path2D.Double#Path2D.Double(Shape, AffineTransform) Path2D.Double} | ||
| 1930 | * subclasses should be used to make the choice explicit. | ||
| 1931 | * | ||
| 1932 | * @param at the {@code AffineTransform} used to transform a | ||
| 1933 | * new {@code Shape}. | ||
| 1934 | * @return a new {@code Shape}, transformed with the specified | ||
| 1935 | * {@code AffineTransform}. | ||
| 1936 | * @since 1.6 | ||
| 1937 | */ | ||
| 1938 | public final synchronized Shape createTransformedShape(AffineTransform at) { | ||
| 1939 | Path2D p2d = (Path2D) clone(); | ||
| 1940 | if (at != null) { | ||
| 1941 | p2d.transform(at); | ||
| 1942 | } | ||
| 1943 | return p2d; | ||
| 1944 | } | ||
| 1945 | |||
| 1946 | /** | ||
| 1947 | * {@inheritDoc} | ||
| 1948 | * @since 1.6 | ||
| 1949 | */ | ||
| 1950 | public final Rectangle getBounds() { | ||
| 1951 | return getBounds2D().getBounds(); | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | /** | ||
| 1955 | * Tests if the specified coordinates are inside the closed | ||
| 1956 | * boundary of the specified {@link PathIterator}. | ||
| 1957 | * <p> | ||
| 1958 | * This method provides a basic facility for implementors of | ||
| 1959 | * the {@link Shape} interface to implement support for the | ||
| 1960 | * {@link Shape#contains(double, double)} method. | ||
| 1961 | * | ||
| 1962 | * @param pi the specified {@code PathIterator} | ||
| 1963 | * @param x the specified X coordinate | ||
| 1964 | * @param y the specified Y coordinate | ||
| 1965 | * @return {@code true} if the specified coordinates are inside the | ||
| 1966 | * specified {@code PathIterator}; {@code false} otherwise | ||
| 1967 | * @since 1.6 | ||
| 1968 | */ | ||
| 1969 | public static boolean contains(PathIterator pi, double x, double y) { | ||
| 1970 | if (x * 0.0 + y * 0.0 == 0.0) { | ||
| 1971 | /* N * 0.0 is 0.0 only if N is finite. | ||
| 1972 | * Here we know that both x and y are finite. | ||
| 1973 | */ | ||
| 1974 | int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1); | ||
| 1975 | int cross = Curve.pointCrossingsForPath(pi, x, y); | ||
| 1976 | return ((cross & mask) != 0); | ||
| 1977 | } else { | ||
| 1978 | /* Either x or y was infinite or NaN. | ||
| 1979 | * A NaN always produces a negative response to any test | ||
| 1980 | * and Infinity values cannot be "inside" any path so | ||
| 1981 | * they should return false as well. | ||
| 1982 | */ | ||
| 1983 | return false; | ||
| 1984 | } | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | /** | ||
| 1988 | * Tests if the specified {@link Point2D} is inside the closed | ||
| 1989 | * boundary of the specified {@link PathIterator}. | ||
| 1990 | * <p> | ||
| 1991 | * This method provides a basic facility for implementors of | ||
| 1992 | * the {@link Shape} interface to implement support for the | ||
| 1993 | * {@link Shape#contains(Point2D)} method. | ||
| 1994 | * | ||
| 1995 | * @param pi the specified {@code PathIterator} | ||
| 1996 | * @param p the specified {@code Point2D} | ||
| 1997 | * @return {@code true} if the specified coordinates are inside the | ||
| 1998 | * specified {@code PathIterator}; {@code false} otherwise | ||
| 1999 | * @since 1.6 | ||
| 2000 | */ | ||
| 2001 | public static boolean contains(PathIterator pi, Point2D p) { | ||
| 2002 | return contains(pi, p.getX(), p.getY()); | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | /** | ||
| 2006 | * {@inheritDoc} | ||
| 2007 | * @since 1.6 | ||
| 2008 | */ | ||
| 2009 | public final boolean contains(double x, double y) { | ||
| 2010 | if (x * 0.0 + y * 0.0 == 0.0) { | ||
| 2011 | /* N * 0.0 is 0.0 only if N is finite. | ||
| 2012 | * Here we know that both x and y are finite. | ||
| 2013 | */ | ||
| 2014 | if (numTypes < 2) { | ||
| 2015 | return false; | ||
| 2016 | } | ||
| 2017 | int mask = (windingRule == WIND_NON_ZERO ? -1 : 1); | ||
| 2018 | return ((pointCrossings(x, y) & mask) != 0); | ||
| 2019 | } else { | ||
| 2020 | /* Either x or y was infinite or NaN. | ||
| 2021 | * A NaN always produces a negative response to any test | ||
| 2022 | * and Infinity values cannot be "inside" any path so | ||
| 2023 | * they should return false as well. | ||
| 2024 | */ | ||
| 2025 | return false; | ||
| 2026 | } | ||
| 2027 | } | ||
| 2028 | |||
| 2029 | /** | ||
| 2030 | * {@inheritDoc} | ||
| 2031 | * @since 1.6 | ||
| 2032 | */ | ||
| 2033 | public final boolean contains(Point2D p) { | ||
| 2034 | return contains(p.getX(), p.getY()); | ||
| 2035 | } | ||
| 2036 | |||
| 2037 | /** | ||
| 2038 | * Tests if the specified rectangular area is entirely inside the | ||
| 2039 | * closed boundary of the specified {@link PathIterator}. | ||
| 2040 | * <p> | ||
| 2041 | * This method provides a basic facility for implementors of | ||
| 2042 | * the {@link Shape} interface to implement support for the | ||
| 2043 | * {@link Shape#contains(double, double, double, double)} method. | ||
| 2044 | * <p> | ||
| 2045 | * This method object may conservatively return false in | ||
| 2046 | * cases where the specified rectangular area intersects a | ||
| 2047 | * segment of the path, but that segment does not represent a | ||
| 2048 | * boundary between the interior and exterior of the path. | ||
| 2049 | * Such segments could lie entirely within the interior of the | ||
| 2050 | * path if they are part of a path with a {@link #WIND_NON_ZERO} | ||
| 2051 | * winding rule or if the segments are retraced in the reverse | ||
| 2052 | * direction such that the two sets of segments cancel each | ||
| 2053 | * other out without any exterior area falling between them. | ||
| 2054 | * To determine whether segments represent true boundaries of | ||
| 2055 | * the interior of the path would require extensive calculations | ||
| 2056 | * involving all of the segments of the path and the winding | ||
| 2057 | * rule and are thus beyond the scope of this implementation. | ||
| 2058 | * | ||
| 2059 | * @param pi the specified {@code PathIterator} | ||
| 2060 | * @param x the specified X coordinate | ||
| 2061 | * @param y the specified Y coordinate | ||
| 2062 | * @param w the width of the specified rectangular area | ||
| 2063 | * @param h the height of the specified rectangular area | ||
| 2064 | * @return {@code true} if the specified {@code PathIterator} contains | ||
| 2065 | * the specified rectangluar area; {@code false} otherwise. | ||
| 2066 | * @since 1.6 | ||
| 2067 | */ | ||
| 2068 | public static boolean contains(PathIterator pi, | ||
| 2069 | double x, double y, double w, double h) | ||
| 2070 | { | ||
| 2071 | if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { | ||
| 2072 | /* [xy]+[wh] is NaN if any of those values are NaN, | ||
| 2073 | * or if adding the two together would produce NaN | ||
| 2074 | * by virtue of adding opposing Infinte values. | ||
| 2075 | * Since we need to add them below, their sum must | ||
| 2076 | * not be NaN. | ||
| 2077 | * We return false because NaN always produces a | ||
| 2078 | * negative response to tests | ||
| 2079 | */ | ||
| 2080 | return false; | ||
| 2081 | } | ||
| 2082 | if (w <= 0 || h <= 0) { | ||
| 2083 | return false; | ||
| 2084 | } | ||
| 2085 | int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2); | ||
| 2086 | int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h); | ||
| 2087 | return (crossings != Curve.RECT_INTERSECTS && | ||
| 2088 | (crossings & mask) != 0); | ||
| 2089 | } | ||
| 2090 | |||
| 2091 | /** | ||
| 2092 | * Tests if the specified {@link Rectangle2D} is entirely inside the | ||
| 2093 | * closed boundary of the specified {@link PathIterator}. | ||
| 2094 | * <p> | ||
| 2095 | * This method provides a basic facility for implementors of | ||
| 2096 | * the {@link Shape} interface to implement support for the | ||
| 2097 | * {@link Shape#contains(Rectangle2D)} method. | ||
| 2098 | * <p> | ||
| 2099 | * This method object may conservatively return false in | ||
| 2100 | * cases where the specified rectangular area intersects a | ||
| 2101 | * segment of the path, but that segment does not represent a | ||
| 2102 | * boundary between the interior and exterior of the path. | ||
| 2103 | * Such segments could lie entirely within the interior of the | ||
| 2104 | * path if they are part of a path with a {@link #WIND_NON_ZERO} | ||
| 2105 | * winding rule or if the segments are retraced in the reverse | ||
| 2106 | * direction such that the two sets of segments cancel each | ||
| 2107 | * other out without any exterior area falling between them. | ||
| 2108 | * To determine whether segments represent true boundaries of | ||
| 2109 | * the interior of the path would require extensive calculations | ||
| 2110 | * involving all of the segments of the path and the winding | ||
| 2111 | * rule and are thus beyond the scope of this implementation. | ||
| 2112 | * | ||
| 2113 | * @param pi the specified {@code PathIterator} | ||
| 2114 | * @param r a specified {@code Rectangle2D} | ||
| 2115 | * @return {@code true} if the specified {@code PathIterator} contains | ||
| 2116 | * the specified {@code Rectangle2D}; {@code false} otherwise. | ||
| 2117 | * @since 1.6 | ||
| 2118 | */ | ||
| 2119 | public static boolean contains(PathIterator pi, Rectangle2D r) { | ||
| 2120 | return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 2121 | } | ||
| 2122 | |||
| 2123 | /** | ||
| 2124 | * {@inheritDoc} | ||
| 2125 | * <p> | ||
| 2126 | * This method object may conservatively return false in | ||
| 2127 | * cases where the specified rectangular area intersects a | ||
| 2128 | * segment of the path, but that segment does not represent a | ||
| 2129 | * boundary between the interior and exterior of the path. | ||
| 2130 | * Such segments could lie entirely within the interior of the | ||
| 2131 | * path if they are part of a path with a {@link #WIND_NON_ZERO} | ||
| 2132 | * winding rule or if the segments are retraced in the reverse | ||
| 2133 | * direction such that the two sets of segments cancel each | ||
| 2134 | * other out without any exterior area falling between them. | ||
| 2135 | * To determine whether segments represent true boundaries of | ||
| 2136 | * the interior of the path would require extensive calculations | ||
| 2137 | * involving all of the segments of the path and the winding | ||
| 2138 | * rule and are thus beyond the scope of this implementation. | ||
| 2139 | * | ||
| 2140 | * @since 1.6 | ||
| 2141 | */ | ||
| 2142 | public final boolean contains(double x, double y, double w, double h) { | ||
| 2143 | if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { | ||
| 2144 | /* [xy]+[wh] is NaN if any of those values are NaN, | ||
| 2145 | * or if adding the two together would produce NaN | ||
| 2146 | * by virtue of adding opposing Infinte values. | ||
| 2147 | * Since we need to add them below, their sum must | ||
| 2148 | * not be NaN. | ||
| 2149 | * We return false because NaN always produces a | ||
| 2150 | * negative response to tests | ||
| 2151 | */ | ||
| 2152 | return false; | ||
| 2153 | } | ||
| 2154 | if (w <= 0 || h <= 0) { | ||
| 2155 | return false; | ||
| 2156 | } | ||
| 2157 | int mask = (windingRule == WIND_NON_ZERO ? -1 : 2); | ||
| 2158 | int crossings = rectCrossings(x, y, x+w, y+h); | ||
| 2159 | return (crossings != Curve.RECT_INTERSECTS && | ||
| 2160 | (crossings & mask) != 0); | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | /** | ||
| 2164 | * {@inheritDoc} | ||
| 2165 | * <p> | ||
| 2166 | * This method object may conservatively return false in | ||
| 2167 | * cases where the specified rectangular area intersects a | ||
| 2168 | * segment of the path, but that segment does not represent a | ||
| 2169 | * boundary between the interior and exterior of the path. | ||
| 2170 | * Such segments could lie entirely within the interior of the | ||
| 2171 | * path if they are part of a path with a {@link #WIND_NON_ZERO} | ||
| 2172 | * winding rule or if the segments are retraced in the reverse | ||
| 2173 | * direction such that the two sets of segments cancel each | ||
| 2174 | * other out without any exterior area falling between them. | ||
| 2175 | * To determine whether segments represent true boundaries of | ||
| 2176 | * the interior of the path would require extensive calculations | ||
| 2177 | * involving all of the segments of the path and the winding | ||
| 2178 | * rule and are thus beyond the scope of this implementation. | ||
| 2179 | * | ||
| 2180 | * @since 1.6 | ||
| 2181 | */ | ||
| 2182 | public final boolean contains(Rectangle2D r) { | ||
| 2183 | return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 2184 | } | ||
| 2185 | |||
| 2186 | /** | ||
| 2187 | * Tests if the interior of the specified {@link PathIterator} | ||
| 2188 | * intersects the interior of a specified set of rectangular | ||
| 2189 | * coordinates. | ||
| 2190 | * <p> | ||
| 2191 | * This method provides a basic facility for implementors of | ||
| 2192 | * the {@link Shape} interface to implement support for the | ||
| 2193 | * {@link Shape#intersects(double, double, double, double)} method. | ||
| 2194 | * <p> | ||
| 2195 | * This method object may conservatively return true in | ||
| 2196 | * cases where the specified rectangular area intersects a | ||
| 2197 | * segment of the path, but that segment does not represent a | ||
| 2198 | * boundary between the interior and exterior of the path. | ||
| 2199 | * Such a case may occur if some set of segments of the | ||
| 2200 | * path are retraced in the reverse direction such that the | ||
| 2201 | * two sets of segments cancel each other out without any | ||
| 2202 | * interior area between them. | ||
| 2203 | * To determine whether segments represent true boundaries of | ||
| 2204 | * the interior of the path would require extensive calculations | ||
| 2205 | * involving all of the segments of the path and the winding | ||
| 2206 | * rule and are thus beyond the scope of this implementation. | ||
| 2207 | * | ||
| 2208 | * @param pi the specified {@code PathIterator} | ||
| 2209 | * @param x the specified X coordinate | ||
| 2210 | * @param y the specified Y coordinate | ||
| 2211 | * @param w the width of the specified rectangular coordinates | ||
| 2212 | * @param h the height of the specified rectangular coordinates | ||
| 2213 | * @return {@code true} if the specified {@code PathIterator} and | ||
| 2214 | * the interior of the specified set of rectangular | ||
| 2215 | * coordinates intersect each other; {@code false} otherwise. | ||
| 2216 | * @since 1.6 | ||
| 2217 | */ | ||
| 2218 | public static boolean intersects(PathIterator pi, | ||
| 2219 | double x, double y, double w, double h) | ||
| 2220 | { | ||
| 2221 | if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { | ||
| 2222 | /* [xy]+[wh] is NaN if any of those values are NaN, | ||
| 2223 | * or if adding the two together would produce NaN | ||
| 2224 | * by virtue of adding opposing Infinte values. | ||
| 2225 | * Since we need to add them below, their sum must | ||
| 2226 | * not be NaN. | ||
| 2227 | * We return false because NaN always produces a | ||
| 2228 | * negative response to tests | ||
| 2229 | */ | ||
| 2230 | return false; | ||
| 2231 | } | ||
| 2232 | if (w <= 0 || h <= 0) { | ||
| 2233 | return false; | ||
| 2234 | } | ||
| 2235 | int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2); | ||
| 2236 | int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h); | ||
| 2237 | return (crossings == Curve.RECT_INTERSECTS || | ||
| 2238 | (crossings & mask) != 0); | ||
| 2239 | } | ||
| 2240 | |||
| 2241 | /** | ||
| 2242 | * Tests if the interior of the specified {@link PathIterator} | ||
| 2243 | * intersects the interior of a specified {@link Rectangle2D}. | ||
| 2244 | * <p> | ||
| 2245 | * This method provides a basic facility for implementors of | ||
| 2246 | * the {@link Shape} interface to implement support for the | ||
| 2247 | * {@link Shape#intersects(Rectangle2D)} method. | ||
| 2248 | * <p> | ||
| 2249 | * This method object may conservatively return true in | ||
| 2250 | * cases where the specified rectangular area intersects a | ||
| 2251 | * segment of the path, but that segment does not represent a | ||
| 2252 | * boundary between the interior and exterior of the path. | ||
| 2253 | * Such a case may occur if some set of segments of the | ||
| 2254 | * path are retraced in the reverse direction such that the | ||
| 2255 | * two sets of segments cancel each other out without any | ||
| 2256 | * interior area between them. | ||
| 2257 | * To determine whether segments represent true boundaries of | ||
| 2258 | * the interior of the path would require extensive calculations | ||
| 2259 | * involving all of the segments of the path and the winding | ||
| 2260 | * rule and are thus beyond the scope of this implementation. | ||
| 2261 | * | ||
| 2262 | * @param pi the specified {@code PathIterator} | ||
| 2263 | * @param r the specified {@code Rectangle2D} | ||
| 2264 | * @return {@code true} if the specified {@code PathIterator} and | ||
| 2265 | * the interior of the specified {@code Rectangle2D} | ||
| 2266 | * intersect each other; {@code false} otherwise. | ||
| 2267 | * @since 1.6 | ||
| 2268 | */ | ||
| 2269 | public static boolean intersects(PathIterator pi, Rectangle2D r) { | ||
| 2270 | return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 2271 | } | ||
| 2272 | |||
| 2273 | /** | ||
| 2274 | * {@inheritDoc} | ||
| 2275 | * <p> | ||
| 2276 | * This method object may conservatively return true in | ||
| 2277 | * cases where the specified rectangular area intersects a | ||
| 2278 | * segment of the path, but that segment does not represent a | ||
| 2279 | * boundary between the interior and exterior of the path. | ||
| 2280 | * Such a case may occur if some set of segments of the | ||
| 2281 | * path are retraced in the reverse direction such that the | ||
| 2282 | * two sets of segments cancel each other out without any | ||
| 2283 | * interior area between them. | ||
| 2284 | * To determine whether segments represent true boundaries of | ||
| 2285 | * the interior of the path would require extensive calculations | ||
| 2286 | * involving all of the segments of the path and the winding | ||
| 2287 | * rule and are thus beyond the scope of this implementation. | ||
| 2288 | * | ||
| 2289 | * @since 1.6 | ||
| 2290 | */ | ||
| 2291 | public final boolean intersects(double x, double y, double w, double h) { | ||
| 2292 | if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { | ||
| 2293 | /* [xy]+[wh] is NaN if any of those values are NaN, | ||
| 2294 | * or if adding the two together would produce NaN | ||
| 2295 | * by virtue of adding opposing Infinte values. | ||
| 2296 | * Since we need to add them below, their sum must | ||
| 2297 | * not be NaN. | ||
| 2298 | * We return false because NaN always produces a | ||
| 2299 | * negative response to tests | ||
| 2300 | */ | ||
| 2301 | return false; | ||
| 2302 | } | ||
| 2303 | if (w <= 0 || h <= 0) { | ||
| 2304 | return false; | ||
| 2305 | } | ||
| 2306 | int mask = (windingRule == WIND_NON_ZERO ? -1 : 2); | ||
| 2307 | int crossings = rectCrossings(x, y, x+w, y+h); | ||
| 2308 | return (crossings == Curve.RECT_INTERSECTS || | ||
| 2309 | (crossings & mask) != 0); | ||
| 2310 | } | ||
| 2311 | |||
| 2312 | /** | ||
| 2313 | * {@inheritDoc} | ||
| 2314 | * <p> | ||
| 2315 | * This method object may conservatively return true in | ||
| 2316 | * cases where the specified rectangular area intersects a | ||
| 2317 | * segment of the path, but that segment does not represent a | ||
| 2318 | * boundary between the interior and exterior of the path. | ||
| 2319 | * Such a case may occur if some set of segments of the | ||
| 2320 | * path are retraced in the reverse direction such that the | ||
| 2321 | * two sets of segments cancel each other out without any | ||
| 2322 | * interior area between them. | ||
| 2323 | * To determine whether segments represent true boundaries of | ||
| 2324 | * the interior of the path would require extensive calculations | ||
| 2325 | * involving all of the segments of the path and the winding | ||
| 2326 | * rule and are thus beyond the scope of this implementation. | ||
| 2327 | * | ||
| 2328 | * @since 1.6 | ||
| 2329 | */ | ||
| 2330 | public final boolean intersects(Rectangle2D r) { | ||
| 2331 | return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 2332 | } | ||
| 2333 | |||
| 2334 | /** | ||
| 2335 | * {@inheritDoc} | ||
| 2336 | * <p> | ||
| 2337 | * The iterator for this class is not multi-threaded safe, | ||
| 2338 | * which means that this {@code Path2D} class does not | ||
| 2339 | * guarantee that modifications to the geometry of this | ||
| 2340 | * {@code Path2D} object do not affect any iterations of | ||
| 2341 | * that geometry that are already in process. | ||
| 2342 | * | ||
| 2343 | * @since 1.6 | ||
| 2344 | */ | ||
| 2345 | public final PathIterator getPathIterator(AffineTransform at, | ||
| 2346 | double flatness) | ||
| 2347 | { | ||
| 2348 | return new FlatteningPathIterator(getPathIterator(at), flatness); | ||
| 2349 | } | ||
| 2350 | |||
| 2351 | /** | ||
| 2352 | * Creates a new object of the same class as this object. | ||
| 2353 | * | ||
| 2354 | * @return a clone of this instance. | ||
| 2355 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 2356 | * @see java.lang.Cloneable | ||
| 2357 | * @since 1.6 | ||
| 2358 | */ | ||
| 2359 | public abstract Object clone(); | ||
| 2360 | // Note: It would be nice to have this return Path2D | ||
| 2361 | // but one of our subclasses (GeneralPath) needs to | ||
| 2362 | // offer "public Object clone()" for backwards | ||
| 2363 | // compatibility so we cannot restrict it further. | ||
| 2364 | // REMIND: Can we do both somehow? | ||
| 2365 | |||
| 2366 | /* | ||
| 2367 | * Support fields and methods for serializing the subclasses. | ||
| 2368 | */ | ||
| 2369 | private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30; | ||
| 2370 | private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31; | ||
| 2371 | |||
| 2372 | private static final byte SERIAL_SEG_FLT_MOVETO = 0x40; | ||
| 2373 | private static final byte SERIAL_SEG_FLT_LINETO = 0x41; | ||
| 2374 | private static final byte SERIAL_SEG_FLT_QUADTO = 0x42; | ||
| 2375 | private static final byte SERIAL_SEG_FLT_CUBICTO = 0x43; | ||
| 2376 | |||
| 2377 | private static final byte SERIAL_SEG_DBL_MOVETO = 0x50; | ||
| 2378 | private static final byte SERIAL_SEG_DBL_LINETO = 0x51; | ||
| 2379 | private static final byte SERIAL_SEG_DBL_QUADTO = 0x52; | ||
| 2380 | private static final byte SERIAL_SEG_DBL_CUBICTO = 0x53; | ||
| 2381 | |||
| 2382 | private static final byte SERIAL_SEG_CLOSE = 0x60; | ||
| 2383 | private static final byte SERIAL_PATH_END = 0x61; | ||
| 2384 | |||
| 2385 | final void writeObject(java.io.ObjectOutputStream s, boolean isdbl) | ||
| 2386 | throws java.io.IOException | ||
| 2387 | { | ||
| 2388 | s.defaultWriteObject(); | ||
| 2389 | |||
| 2390 | float fCoords[]; | ||
| 2391 | double dCoords[]; | ||
| 2392 | |||
| 2393 | if (isdbl) { | ||
| 2394 | dCoords = ((Path2D.Double) this).doubleCoords; | ||
| 2395 | fCoords = null; | ||
| 2396 | } else { | ||
| 2397 | fCoords = ((Path2D.Float) this).floatCoords; | ||
| 2398 | dCoords = null; | ||
| 2399 | } | ||
| 2400 | |||
| 2401 | int numTypes = this.numTypes; | ||
| 2402 | |||
| 2403 | s.writeByte(isdbl | ||
| 2404 | ? SERIAL_STORAGE_DBL_ARRAY | ||
| 2405 | : SERIAL_STORAGE_FLT_ARRAY); | ||
| 2406 | s.writeInt(numTypes); | ||
| 2407 | s.writeInt(numCoords); | ||
| 2408 | s.writeByte((byte) windingRule); | ||
| 2409 | |||
| 2410 | int cindex = 0; | ||
| 2411 | for (int i = 0; i < numTypes; i++) { | ||
| 2412 | int npoints; | ||
| 2413 | byte serialtype; | ||
| 2414 | switch (pointTypes[i]) { | ||
| 2415 | case SEG_MOVETO: | ||
| 2416 | npoints = 1; | ||
| 2417 | serialtype = (isdbl | ||
| 2418 | ? SERIAL_SEG_DBL_MOVETO | ||
| 2419 | : SERIAL_SEG_FLT_MOVETO); | ||
| 2420 | break; | ||
| 2421 | case SEG_LINETO: | ||
| 2422 | npoints = 1; | ||
| 2423 | serialtype = (isdbl | ||
| 2424 | ? SERIAL_SEG_DBL_LINETO | ||
| 2425 | : SERIAL_SEG_FLT_LINETO); | ||
| 2426 | break; | ||
| 2427 | case SEG_QUADTO: | ||
| 2428 | npoints = 2; | ||
| 2429 | serialtype = (isdbl | ||
| 2430 | ? SERIAL_SEG_DBL_QUADTO | ||
| 2431 | : SERIAL_SEG_FLT_QUADTO); | ||
| 2432 | break; | ||
| 2433 | case SEG_CUBICTO: | ||
| 2434 | npoints = 3; | ||
| 2435 | serialtype = (isdbl | ||
| 2436 | ? SERIAL_SEG_DBL_CUBICTO | ||
| 2437 | : SERIAL_SEG_FLT_CUBICTO); | ||
| 2438 | break; | ||
| 2439 | case SEG_CLOSE: | ||
| 2440 | npoints = 0; | ||
| 2441 | serialtype = SERIAL_SEG_CLOSE; | ||
| 2442 | break; | ||
| 2443 | |||
| 2444 | default: | ||
| 2445 | // Should never happen | ||
| 2446 | throw new InternalError("unrecognized path type"); | ||
| 2447 | } | ||
| 2448 | s.writeByte(serialtype); | ||
| 2449 | while (--npoints >= 0) { | ||
| 2450 | if (isdbl) { | ||
| 2451 | s.writeDouble(dCoords[cindex++]); | ||
| 2452 | s.writeDouble(dCoords[cindex++]); | ||
| 2453 | } else { | ||
| 2454 | s.writeFloat(fCoords[cindex++]); | ||
| 2455 | s.writeFloat(fCoords[cindex++]); | ||
| 2456 | } | ||
| 2457 | } | ||
| 2458 | } | ||
| 2459 | s.writeByte((byte) SERIAL_PATH_END); | ||
| 2460 | } | ||
| 2461 | |||
| 2462 | final void readObject(java.io.ObjectInputStream s, boolean storedbl) | ||
| 2463 | throws java.lang.ClassNotFoundException, java.io.IOException | ||
| 2464 | { | ||
| 2465 | s.defaultReadObject(); | ||
| 2466 | |||
| 2467 | // The subclass calls this method with the storage type that | ||
| 2468 | // they want us to use (storedbl) so we ignore the storage | ||
| 2469 | // method hint from the stream. | ||
| 2470 | s.readByte(); | ||
| 2471 | int nT = s.readInt(); | ||
| 2472 | int nC = s.readInt(); | ||
| 2473 | try { | ||
| 2474 | setWindingRule(s.readByte()); | ||
| 2475 | } catch (IllegalArgumentException iae) { | ||
| 2476 | throw new java.io.InvalidObjectException(iae.getMessage()); | ||
| 2477 | } | ||
| 2478 | |||
| 2479 | pointTypes = new byte[(nT < 0) ? INIT_SIZE : nT]; | ||
| 2480 | if (nC < 0) { | ||
| 2481 | nC = INIT_SIZE * 2; | ||
| 2482 | } | ||
| 2483 | if (storedbl) { | ||
| 2484 | ((Path2D.Double) this).doubleCoords = new double[nC]; | ||
| 2485 | } else { | ||
| 2486 | ((Path2D.Float) this).floatCoords = new float[nC]; | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | PATHDONE: | ||
| 2490 | for (int i = 0; nT < 0 || i < nT; i++) { | ||
| 2491 | boolean isdbl; | ||
| 2492 | int npoints; | ||
| 2493 | byte segtype; | ||
| 2494 | |||
| 2495 | byte serialtype = s.readByte(); | ||
| 2496 | switch (serialtype) { | ||
| 2497 | case SERIAL_SEG_FLT_MOVETO: | ||
| 2498 | isdbl = false; | ||
| 2499 | npoints = 1; | ||
| 2500 | segtype = SEG_MOVETO; | ||
| 2501 | break; | ||
| 2502 | case SERIAL_SEG_FLT_LINETO: | ||
| 2503 | isdbl = false; | ||
| 2504 | npoints = 1; | ||
| 2505 | segtype = SEG_LINETO; | ||
| 2506 | break; | ||
| 2507 | case SERIAL_SEG_FLT_QUADTO: | ||
| 2508 | isdbl = false; | ||
| 2509 | npoints = 2; | ||
| 2510 | segtype = SEG_QUADTO; | ||
| 2511 | break; | ||
| 2512 | case SERIAL_SEG_FLT_CUBICTO: | ||
| 2513 | isdbl = false; | ||
| 2514 | npoints = 3; | ||
| 2515 | segtype = SEG_CUBICTO; | ||
| 2516 | break; | ||
| 2517 | |||
| 2518 | case SERIAL_SEG_DBL_MOVETO: | ||
| 2519 | isdbl = true; | ||
| 2520 | npoints = 1; | ||
| 2521 | segtype = SEG_MOVETO; | ||
| 2522 | break; | ||
| 2523 | case SERIAL_SEG_DBL_LINETO: | ||
| 2524 | isdbl = true; | ||
| 2525 | npoints = 1; | ||
| 2526 | segtype = SEG_LINETO; | ||
| 2527 | break; | ||
| 2528 | case SERIAL_SEG_DBL_QUADTO: | ||
| 2529 | isdbl = true; | ||
| 2530 | npoints = 2; | ||
| 2531 | segtype = SEG_QUADTO; | ||
| 2532 | break; | ||
| 2533 | case SERIAL_SEG_DBL_CUBICTO: | ||
| 2534 | isdbl = true; | ||
| 2535 | npoints = 3; | ||
| 2536 | segtype = SEG_CUBICTO; | ||
| 2537 | break; | ||
| 2538 | |||
| 2539 | case SERIAL_SEG_CLOSE: | ||
| 2540 | isdbl = false; | ||
| 2541 | npoints = 0; | ||
| 2542 | segtype = SEG_CLOSE; | ||
| 2543 | break; | ||
| 2544 | |||
| 2545 | case SERIAL_PATH_END: | ||
| 2546 | if (nT < 0) { | ||
| 2547 | break PATHDONE; | ||
| 2548 | } | ||
| 2549 | throw new StreamCorruptedException("unexpected PATH_END"); | ||
| 2550 | |||
| 2551 | default: | ||
| 2552 | throw new StreamCorruptedException("unrecognized path type"); | ||
| 2553 | } | ||
| 2554 | needRoom(segtype != SEG_MOVETO, npoints * 2); | ||
| 2555 | if (isdbl) { | ||
| 2556 | while (--npoints >= 0) { | ||
| 2557 | append(s.readDouble(), s.readDouble()); | ||
| 2558 | } | ||
| 2559 | } else { | ||
| 2560 | while (--npoints >= 0) { | ||
| 2561 | append(s.readFloat(), s.readFloat()); | ||
| 2562 | } | ||
| 2563 | } | ||
| 2564 | pointTypes[numTypes++] = segtype; | ||
| 2565 | } | ||
| 2566 | if (nT >= 0 && s.readByte() != SERIAL_PATH_END) { | ||
| 2567 | throw new StreamCorruptedException("missing PATH_END"); | ||
| 2568 | } | ||
| 2569 | } | ||
| 2570 | |||
| 2571 | static abstract class Iterator implements PathIterator { | ||
| 2572 | int typeIdx; | ||
| 2573 | int pointIdx; | ||
| 2574 | Path2D path; | ||
| 2575 | |||
| 2576 | static final int curvecoords[] = {2, 2, 4, 6, 0}; | ||
| 2577 | |||
| 2578 | Iterator(Path2D path) { | ||
| 2579 | this.path = path; | ||
| 2580 | } | ||
| 2581 | |||
| 2582 | public int getWindingRule() { | ||
| 2583 | return path.getWindingRule(); | ||
| 2584 | } | ||
| 2585 | |||
| 2586 | public boolean isDone() { | ||
| 2587 | return (typeIdx >= path.numTypes); | ||
| 2588 | } | ||
| 2589 | |||
| 2590 | public void next() { | ||
| 2591 | int type = path.pointTypes[typeIdx++]; | ||
| 2592 | pointIdx += curvecoords[type]; | ||
| 2593 | } | ||
| 2594 | } | ||
| 2595 | } | ||
Binary file not shown.
| @@ -0,0 +1,215 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2000, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * The <code>PathIterator</code> interface provides the mechanism | ||
| 30 | * for objects that implement the {@link java.awt.Shape Shape} | ||
| 31 | * interface to return the geometry of their boundary by allowing | ||
| 32 | * a caller to retrieve the path of that boundary a segment at a | ||
| 33 | * time. This interface allows these objects to retrieve the path of | ||
| 34 | * their boundary a segment at a time by using 1st through 3rd order | ||
| 35 | * Bézier curves, which are lines and quadratic or cubic | ||
| 36 | * Bézier splines. | ||
| 37 | * <p> | ||
| 38 | * Multiple subpaths can be expressed by using a "MOVETO" segment to | ||
| 39 | * create a discontinuity in the geometry to move from the end of | ||
| 40 | * one subpath to the beginning of the next. | ||
| 41 | * <p> | ||
| 42 | * Each subpath can be closed manually by ending the last segment in | ||
| 43 | * the subpath on the same coordinate as the beginning "MOVETO" segment | ||
| 44 | * for that subpath or by using a "CLOSE" segment to append a line | ||
| 45 | * segment from the last point back to the first. | ||
| 46 | * Be aware that manually closing an outline as opposed to using a | ||
| 47 | * "CLOSE" segment to close the path might result in different line | ||
| 48 | * style decorations being used at the end points of the subpath. | ||
| 49 | * For example, the {@link java.awt.BasicStroke BasicStroke} object | ||
| 50 | * uses a line "JOIN" decoration to connect the first and last points | ||
| 51 | * if a "CLOSE" segment is encountered, whereas simply ending the path | ||
| 52 | * on the same coordinate as the beginning coordinate results in line | ||
| 53 | * "CAP" decorations being used at the ends. | ||
| 54 | * | ||
| 55 | * @see java.awt.Shape | ||
| 56 | * @see java.awt.BasicStroke | ||
| 57 | * | ||
| 58 | * @author Jim Graham | ||
| 59 | */ | ||
| 60 | public interface PathIterator { | ||
| 61 | /** | ||
| 62 | * The winding rule constant for specifying an even-odd rule | ||
| 63 | * for determining the interior of a path. | ||
| 64 | * The even-odd rule specifies that a point lies inside the | ||
| 65 | * path if a ray drawn in any direction from that point to | ||
| 66 | * infinity is crossed by path segments an odd number of times. | ||
| 67 | */ | ||
| 68 | public static final int WIND_EVEN_ODD = 0; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * The winding rule constant for specifying a non-zero rule | ||
| 72 | * for determining the interior of a path. | ||
| 73 | * The non-zero rule specifies that a point lies inside the | ||
| 74 | * path if a ray drawn in any direction from that point to | ||
| 75 | * infinity is crossed by path segments a different number | ||
| 76 | * of times in the counter-clockwise direction than the | ||
| 77 | * clockwise direction. | ||
| 78 | */ | ||
| 79 | public static final int WIND_NON_ZERO = 1; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * The segment type constant for a point that specifies the | ||
| 83 | * starting location for a new subpath. | ||
| 84 | */ | ||
| 85 | public static final int SEG_MOVETO = 0; | ||
| 86 | |||
| 87 | /** | ||
| 88 | * The segment type constant for a point that specifies the | ||
| 89 | * end point of a line to be drawn from the most recently | ||
| 90 | * specified point. | ||
| 91 | */ | ||
| 92 | public static final int SEG_LINETO = 1; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * The segment type constant for the pair of points that specify | ||
| 96 | * a quadratic parametric curve to be drawn from the most recently | ||
| 97 | * specified point. | ||
| 98 | * The curve is interpolated by solving the parametric control | ||
| 99 | * equation in the range <code>(t=[0..1])</code> using | ||
| 100 | * the most recently specified (current) point (CP), | ||
| 101 | * the first control point (P1), | ||
| 102 | * and the final interpolated control point (P2). | ||
| 103 | * The parametric control equation for this curve is: | ||
| 104 | * <pre> | ||
| 105 | * P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2 | ||
| 106 | * 0 <= t <= 1 | ||
| 107 | * | ||
| 108 | * B(n,m) = mth coefficient of nth degree Bernstein polynomial | ||
| 109 | * = C(n,m) * t^(m) * (1 - t)^(n-m) | ||
| 110 | * C(n,m) = Combinations of n things, taken m at a time | ||
| 111 | * = n! / (m! * (n-m)!) | ||
| 112 | * </pre> | ||
| 113 | */ | ||
| 114 | public static final int SEG_QUADTO = 2; | ||
| 115 | |||
| 116 | /** | ||
| 117 | * The segment type constant for the set of 3 points that specify | ||
| 118 | * a cubic parametric curve to be drawn from the most recently | ||
| 119 | * specified point. | ||
| 120 | * The curve is interpolated by solving the parametric control | ||
| 121 | * equation in the range <code>(t=[0..1])</code> using | ||
| 122 | * the most recently specified (current) point (CP), | ||
| 123 | * the first control point (P1), | ||
| 124 | * the second control point (P2), | ||
| 125 | * and the final interpolated control point (P3). | ||
| 126 | * The parametric control equation for this curve is: | ||
| 127 | * <pre> | ||
| 128 | * P(t) = B(3,0)*CP + B(3,1)*P1 + B(3,2)*P2 + B(3,3)*P3 | ||
| 129 | * 0 <= t <= 1 | ||
| 130 | * | ||
| 131 | * B(n,m) = mth coefficient of nth degree Bernstein polynomial | ||
| 132 | * = C(n,m) * t^(m) * (1 - t)^(n-m) | ||
| 133 | * C(n,m) = Combinations of n things, taken m at a time | ||
| 134 | * = n! / (m! * (n-m)!) | ||
| 135 | * </pre> | ||
| 136 | * This form of curve is commonly known as a Bézier curve. | ||
| 137 | */ | ||
| 138 | public static final int SEG_CUBICTO = 3; | ||
| 139 | |||
| 140 | /** | ||
| 141 | * The segment type constant that specifies that | ||
| 142 | * the preceding subpath should be closed by appending a line segment | ||
| 143 | * back to the point corresponding to the most recent SEG_MOVETO. | ||
| 144 | */ | ||
| 145 | public static final int SEG_CLOSE = 4; | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Returns the winding rule for determining the interior of the | ||
| 149 | * path. | ||
| 150 | * @return the winding rule. | ||
| 151 | * @see #WIND_EVEN_ODD | ||
| 152 | * @see #WIND_NON_ZERO | ||
| 153 | */ | ||
| 154 | public int getWindingRule(); | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Tests if the iteration is complete. | ||
| 158 | * @return <code>true</code> if all the segments have | ||
| 159 | * been read; <code>false</code> otherwise. | ||
| 160 | */ | ||
| 161 | public boolean isDone(); | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Moves the iterator to the next segment of the path forwards | ||
| 165 | * along the primary direction of traversal as long as there are | ||
| 166 | * more points in that direction. | ||
| 167 | */ | ||
| 168 | public void next(); | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Returns the coordinates and type of the current path segment in | ||
| 172 | * the iteration. | ||
| 173 | * The return value is the path-segment type: | ||
| 174 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 175 | * A float array of length 6 must be passed in and can be used to | ||
| 176 | * store the coordinates of the point(s). | ||
| 177 | * Each point is stored as a pair of float x,y coordinates. | ||
| 178 | * SEG_MOVETO and SEG_LINETO types returns one point, | ||
| 179 | * SEG_QUADTO returns two points, | ||
| 180 | * SEG_CUBICTO returns 3 points | ||
| 181 | * and SEG_CLOSE does not return any points. | ||
| 182 | * @param coords an array that holds the data returned from | ||
| 183 | * this method | ||
| 184 | * @return the path-segment type of the current path segment. | ||
| 185 | * @see #SEG_MOVETO | ||
| 186 | * @see #SEG_LINETO | ||
| 187 | * @see #SEG_QUADTO | ||
| 188 | * @see #SEG_CUBICTO | ||
| 189 | * @see #SEG_CLOSE | ||
| 190 | */ | ||
| 191 | public int currentSegment(float[] coords); | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Returns the coordinates and type of the current path segment in | ||
| 195 | * the iteration. | ||
| 196 | * The return value is the path-segment type: | ||
| 197 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 198 | * A double array of length 6 must be passed in and can be used to | ||
| 199 | * store the coordinates of the point(s). | ||
| 200 | * Each point is stored as a pair of double x,y coordinates. | ||
| 201 | * SEG_MOVETO and SEG_LINETO types returns one point, | ||
| 202 | * SEG_QUADTO returns two points, | ||
| 203 | * SEG_CUBICTO returns 3 points | ||
| 204 | * and SEG_CLOSE does not return any points. | ||
| 205 | * @param coords an array that holds the data returned from | ||
| 206 | * this method | ||
| 207 | * @return the path-segment type of the current path segment. | ||
| 208 | * @see #SEG_MOVETO | ||
| 209 | * @see #SEG_LINETO | ||
| 210 | * @see #SEG_QUADTO | ||
| 211 | * @see #SEG_CUBICTO | ||
| 212 | * @see #SEG_CLOSE | ||
| 213 | */ | ||
| 214 | public int currentSegment(double[] coords); | ||
| 215 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,428 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.io.Serializable; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * The <code>Point2D</code> class defines a point representing a location | ||
| 32 | * in {@code (x,y)} coordinate space. | ||
| 33 | * <p> | ||
| 34 | * This class is only the abstract superclass for all objects that | ||
| 35 | * store a 2D coordinate. | ||
| 36 | * The actual storage representation of the coordinates is left to | ||
| 37 | * the subclass. | ||
| 38 | * | ||
| 39 | * @author Jim Graham | ||
| 40 | * @since 1.2 | ||
| 41 | */ | ||
| 42 | public abstract class Point2D implements Cloneable { | ||
| 43 | |||
| 44 | /** | ||
| 45 | * The <code>Float</code> class defines a point specified in float | ||
| 46 | * precision. | ||
| 47 | * @since 1.2 | ||
| 48 | */ | ||
| 49 | public static class Float extends Point2D implements Serializable { | ||
| 50 | /** | ||
| 51 | * The X coordinate of this <code>Point2D</code>. | ||
| 52 | * @since 1.2 | ||
| 53 | * @serial | ||
| 54 | */ | ||
| 55 | public float x; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * The Y coordinate of this <code>Point2D</code>. | ||
| 59 | * @since 1.2 | ||
| 60 | * @serial | ||
| 61 | */ | ||
| 62 | public float y; | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Constructs and initializes a <code>Point2D</code> with | ||
| 66 | * coordinates (0, 0). | ||
| 67 | * @since 1.2 | ||
| 68 | */ | ||
| 69 | public Float() { | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Constructs and initializes a <code>Point2D</code> with | ||
| 74 | * the specified coordinates. | ||
| 75 | * | ||
| 76 | * @param x the X coordinate of the newly | ||
| 77 | * constructed <code>Point2D</code> | ||
| 78 | * @param y the Y coordinate of the newly | ||
| 79 | * constructed <code>Point2D</code> | ||
| 80 | * @since 1.2 | ||
| 81 | */ | ||
| 82 | public Float(float x, float y) { | ||
| 83 | this.x = x; | ||
| 84 | this.y = y; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * {@inheritDoc} | ||
| 89 | * @since 1.2 | ||
| 90 | */ | ||
| 91 | public double getX() { | ||
| 92 | return (double) x; | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * {@inheritDoc} | ||
| 97 | * @since 1.2 | ||
| 98 | */ | ||
| 99 | public double getY() { | ||
| 100 | return (double) y; | ||
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * {@inheritDoc} | ||
| 105 | * @since 1.2 | ||
| 106 | */ | ||
| 107 | public void setLocation(double x, double y) { | ||
| 108 | this.x = (float) x; | ||
| 109 | this.y = (float) y; | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Sets the location of this <code>Point2D</code> to the | ||
| 114 | * specified <code>float</code> coordinates. | ||
| 115 | * | ||
| 116 | * @param x the new X coordinate of this {@code Point2D} | ||
| 117 | * @param y the new Y coordinate of this {@code Point2D} | ||
| 118 | * @since 1.2 | ||
| 119 | */ | ||
| 120 | public void setLocation(float x, float y) { | ||
| 121 | this.x = x; | ||
| 122 | this.y = y; | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Returns a <code>String</code> that represents the value | ||
| 127 | * of this <code>Point2D</code>. | ||
| 128 | * @return a string representation of this <code>Point2D</code>. | ||
| 129 | * @since 1.2 | ||
| 130 | */ | ||
| 131 | public String toString() { | ||
| 132 | return "Point2D.Float["+x+", "+y+"]"; | ||
| 133 | } | ||
| 134 | |||
| 135 | /* | ||
| 136 | * JDK 1.6 serialVersionUID | ||
| 137 | */ | ||
| 138 | private static final long serialVersionUID = -2870572449815403710L; | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * The <code>Double</code> class defines a point specified in | ||
| 143 | * <code>double</code> precision. | ||
| 144 | * @since 1.2 | ||
| 145 | */ | ||
| 146 | public static class Double extends Point2D implements Serializable { | ||
| 147 | /** | ||
| 148 | * The X coordinate of this <code>Point2D</code>. | ||
| 149 | * @since 1.2 | ||
| 150 | * @serial | ||
| 151 | */ | ||
| 152 | public double x; | ||
| 153 | |||
| 154 | /** | ||
| 155 | * The Y coordinate of this <code>Point2D</code>. | ||
| 156 | * @since 1.2 | ||
| 157 | * @serial | ||
| 158 | */ | ||
| 159 | public double y; | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Constructs and initializes a <code>Point2D</code> with | ||
| 163 | * coordinates (0, 0). | ||
| 164 | * @since 1.2 | ||
| 165 | */ | ||
| 166 | public Double() { | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * Constructs and initializes a <code>Point2D</code> with the | ||
| 171 | * specified coordinates. | ||
| 172 | * | ||
| 173 | * @param x the X coordinate of the newly | ||
| 174 | * constructed <code>Point2D</code> | ||
| 175 | * @param y the Y coordinate of the newly | ||
| 176 | * constructed <code>Point2D</code> | ||
| 177 | * @since 1.2 | ||
| 178 | */ | ||
| 179 | public Double(double x, double y) { | ||
| 180 | this.x = x; | ||
| 181 | this.y = y; | ||
| 182 | } | ||
| 183 | |||
| 184 | /** | ||
| 185 | * {@inheritDoc} | ||
| 186 | * @since 1.2 | ||
| 187 | */ | ||
| 188 | public double getX() { | ||
| 189 | return x; | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * {@inheritDoc} | ||
| 194 | * @since 1.2 | ||
| 195 | */ | ||
| 196 | public double getY() { | ||
| 197 | return y; | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * {@inheritDoc} | ||
| 202 | * @since 1.2 | ||
| 203 | */ | ||
| 204 | public void setLocation(double x, double y) { | ||
| 205 | this.x = x; | ||
| 206 | this.y = y; | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * Returns a <code>String</code> that represents the value | ||
| 211 | * of this <code>Point2D</code>. | ||
| 212 | * @return a string representation of this <code>Point2D</code>. | ||
| 213 | * @since 1.2 | ||
| 214 | */ | ||
| 215 | public String toString() { | ||
| 216 | return "Point2D.Double["+x+", "+y+"]"; | ||
| 217 | } | ||
| 218 | |||
| 219 | /* | ||
| 220 | * JDK 1.6 serialVersionUID | ||
| 221 | */ | ||
| 222 | private static final long serialVersionUID = 6150783262733311327L; | ||
| 223 | } | ||
| 224 | |||
| 225 | /** | ||
| 226 | * This is an abstract class that cannot be instantiated directly. | ||
| 227 | * Type-specific implementation subclasses are available for | ||
| 228 | * instantiation and provide a number of formats for storing | ||
| 229 | * the information necessary to satisfy the various accessor | ||
| 230 | * methods below. | ||
| 231 | * | ||
| 232 | * @see java.awt.geom.Point2D.Float | ||
| 233 | * @see java.awt.geom.Point2D.Double | ||
| 234 | * @see java.awt.Point | ||
| 235 | * @since 1.2 | ||
| 236 | */ | ||
| 237 | protected Point2D() { | ||
| 238 | } | ||
| 239 | |||
| 240 | /** | ||
| 241 | * Returns the X coordinate of this <code>Point2D</code> in | ||
| 242 | * <code>double</code> precision. | ||
| 243 | * @return the X coordinate of this <code>Point2D</code>. | ||
| 244 | * @since 1.2 | ||
| 245 | */ | ||
| 246 | public abstract double getX(); | ||
| 247 | |||
| 248 | /** | ||
| 249 | * Returns the Y coordinate of this <code>Point2D</code> in | ||
| 250 | * <code>double</code> precision. | ||
| 251 | * @return the Y coordinate of this <code>Point2D</code>. | ||
| 252 | * @since 1.2 | ||
| 253 | */ | ||
| 254 | public abstract double getY(); | ||
| 255 | |||
| 256 | /** | ||
| 257 | * Sets the location of this <code>Point2D</code> to the | ||
| 258 | * specified <code>double</code> coordinates. | ||
| 259 | * | ||
| 260 | * @param x the new X coordinate of this {@code Point2D} | ||
| 261 | * @param y the new Y coordinate of this {@code Point2D} | ||
| 262 | * @since 1.2 | ||
| 263 | */ | ||
| 264 | public abstract void setLocation(double x, double y); | ||
| 265 | |||
| 266 | /** | ||
| 267 | * Sets the location of this <code>Point2D</code> to the same | ||
| 268 | * coordinates as the specified <code>Point2D</code> object. | ||
| 269 | * @param p the specified <code>Point2D</code> to which to set | ||
| 270 | * this <code>Point2D</code> | ||
| 271 | * @since 1.2 | ||
| 272 | */ | ||
| 273 | public void setLocation(Point2D p) { | ||
| 274 | setLocation(p.getX(), p.getY()); | ||
| 275 | } | ||
| 276 | |||
| 277 | /** | ||
| 278 | * Returns the square of the distance between two points. | ||
| 279 | * | ||
| 280 | * @param x1 the X coordinate of the first specified point | ||
| 281 | * @param y1 the Y coordinate of the first specified point | ||
| 282 | * @param x2 the X coordinate of the second specified point | ||
| 283 | * @param y2 the Y coordinate of the second specified point | ||
| 284 | * @return the square of the distance between the two | ||
| 285 | * sets of specified coordinates. | ||
| 286 | * @since 1.2 | ||
| 287 | */ | ||
| 288 | public static double distanceSq(double x1, double y1, | ||
| 289 | double x2, double y2) | ||
| 290 | { | ||
| 291 | x1 -= x2; | ||
| 292 | y1 -= y2; | ||
| 293 | return (x1 * x1 + y1 * y1); | ||
| 294 | } | ||
| 295 | |||
| 296 | /** | ||
| 297 | * Returns the distance between two points. | ||
| 298 | * | ||
| 299 | * @param x1 the X coordinate of the first specified point | ||
| 300 | * @param y1 the Y coordinate of the first specified point | ||
| 301 | * @param x2 the X coordinate of the second specified point | ||
| 302 | * @param y2 the Y coordinate of the second specified point | ||
| 303 | * @return the distance between the two sets of specified | ||
| 304 | * coordinates. | ||
| 305 | * @since 1.2 | ||
| 306 | */ | ||
| 307 | public static double distance(double x1, double y1, | ||
| 308 | double x2, double y2) | ||
| 309 | { | ||
| 310 | x1 -= x2; | ||
| 311 | y1 -= y2; | ||
| 312 | return Math.sqrt(x1 * x1 + y1 * y1); | ||
| 313 | } | ||
| 314 | |||
| 315 | /** | ||
| 316 | * Returns the square of the distance from this | ||
| 317 | * <code>Point2D</code> to a specified point. | ||
| 318 | * | ||
| 319 | * @param px the X coordinate of the specified point to be measured | ||
| 320 | * against this <code>Point2D</code> | ||
| 321 | * @param py the Y coordinate of the specified point to be measured | ||
| 322 | * against this <code>Point2D</code> | ||
| 323 | * @return the square of the distance between this | ||
| 324 | * <code>Point2D</code> and the specified point. | ||
| 325 | * @since 1.2 | ||
| 326 | */ | ||
| 327 | public double distanceSq(double px, double py) { | ||
| 328 | px -= getX(); | ||
| 329 | py -= getY(); | ||
| 330 | return (px * px + py * py); | ||
| 331 | } | ||
| 332 | |||
| 333 | /** | ||
| 334 | * Returns the square of the distance from this | ||
| 335 | * <code>Point2D</code> to a specified <code>Point2D</code>. | ||
| 336 | * | ||
| 337 | * @param pt the specified point to be measured | ||
| 338 | * against this <code>Point2D</code> | ||
| 339 | * @return the square of the distance between this | ||
| 340 | * <code>Point2D</code> to a specified <code>Point2D</code>. | ||
| 341 | * @since 1.2 | ||
| 342 | */ | ||
| 343 | public double distanceSq(Point2D pt) { | ||
| 344 | double px = pt.getX() - this.getX(); | ||
| 345 | double py = pt.getY() - this.getY(); | ||
| 346 | return (px * px + py * py); | ||
| 347 | } | ||
| 348 | |||
| 349 | /** | ||
| 350 | * Returns the distance from this <code>Point2D</code> to | ||
| 351 | * a specified point. | ||
| 352 | * | ||
| 353 | * @param px the X coordinate of the specified point to be measured | ||
| 354 | * against this <code>Point2D</code> | ||
| 355 | * @param py the Y coordinate of the specified point to be measured | ||
| 356 | * against this <code>Point2D</code> | ||
| 357 | * @return the distance between this <code>Point2D</code> | ||
| 358 | * and a specified point. | ||
| 359 | * @since 1.2 | ||
| 360 | */ | ||
| 361 | public double distance(double px, double py) { | ||
| 362 | px -= getX(); | ||
| 363 | py -= getY(); | ||
| 364 | return Math.sqrt(px * px + py * py); | ||
| 365 | } | ||
| 366 | |||
| 367 | /** | ||
| 368 | * Returns the distance from this <code>Point2D</code> to a | ||
| 369 | * specified <code>Point2D</code>. | ||
| 370 | * | ||
| 371 | * @param pt the specified point to be measured | ||
| 372 | * against this <code>Point2D</code> | ||
| 373 | * @return the distance between this <code>Point2D</code> and | ||
| 374 | * the specified <code>Point2D</code>. | ||
| 375 | * @since 1.2 | ||
| 376 | */ | ||
| 377 | public double distance(Point2D pt) { | ||
| 378 | double px = pt.getX() - this.getX(); | ||
| 379 | double py = pt.getY() - this.getY(); | ||
| 380 | return Math.sqrt(px * px + py * py); | ||
| 381 | } | ||
| 382 | |||
| 383 | /** | ||
| 384 | * Creates a new object of the same class and with the | ||
| 385 | * same contents as this object. | ||
| 386 | * @return a clone of this instance. | ||
| 387 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 388 | * @see java.lang.Cloneable | ||
| 389 | * @since 1.2 | ||
| 390 | */ | ||
| 391 | public Object clone() { | ||
| 392 | try { | ||
| 393 | return super.clone(); | ||
| 394 | } catch (CloneNotSupportedException e) { | ||
| 395 | // this shouldn't happen, since we are Cloneable | ||
| 396 | throw new InternalError(); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | /** | ||
| 401 | * Returns the hashcode for this <code>Point2D</code>. | ||
| 402 | * @return a hash code for this <code>Point2D</code>. | ||
| 403 | */ | ||
| 404 | public int hashCode() { | ||
| 405 | long bits = java.lang.Double.doubleToLongBits(getX()); | ||
| 406 | bits ^= java.lang.Double.doubleToLongBits(getY()) * 31; | ||
| 407 | return (((int) bits) ^ ((int) (bits >> 32))); | ||
| 408 | } | ||
| 409 | |||
| 410 | /** | ||
| 411 | * Determines whether or not two points are equal. Two instances of | ||
| 412 | * <code>Point2D</code> are equal if the values of their | ||
| 413 | * <code>x</code> and <code>y</code> member fields, representing | ||
| 414 | * their position in the coordinate space, are the same. | ||
| 415 | * @param obj an object to be compared with this <code>Point2D</code> | ||
| 416 | * @return <code>true</code> if the object to be compared is | ||
| 417 | * an instance of <code>Point2D</code> and has | ||
| 418 | * the same values; <code>false</code> otherwise. | ||
| 419 | * @since 1.2 | ||
| 420 | */ | ||
| 421 | public boolean equals(Object obj) { | ||
| 422 | if (obj instanceof Point2D) { | ||
| 423 | Point2D p2d = (Point2D) obj; | ||
| 424 | return (getX() == p2d.getX()) && (getY() == p2d.getY()); | ||
| 425 | } | ||
| 426 | return super.equals(obj); | ||
| 427 | } | ||
| 428 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,1401 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.awt.Rectangle; | ||
| 30 | import java.io.Serializable; | ||
| 31 | import sun.awt.geom.Curve; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * The <code>QuadCurve2D</code> class defines a quadratic parametric curve | ||
| 35 | * segment in {@code (x,y)} coordinate space. | ||
| 36 | * <p> | ||
| 37 | * This class is only the abstract superclass for all objects that | ||
| 38 | * store a 2D quadratic curve segment. | ||
| 39 | * The actual storage representation of the coordinates is left to | ||
| 40 | * the subclass. | ||
| 41 | * | ||
| 42 | * @author Jim Graham | ||
| 43 | * @since 1.2 | ||
| 44 | */ | ||
| 45 | public abstract class QuadCurve2D implements Shape, Cloneable { | ||
| 46 | |||
| 47 | /** | ||
| 48 | * A quadratic parametric curve segment specified with | ||
| 49 | * {@code float} coordinates. | ||
| 50 | * | ||
| 51 | * @since 1.2 | ||
| 52 | */ | ||
| 53 | public static class Float extends QuadCurve2D implements Serializable { | ||
| 54 | /** | ||
| 55 | * The X coordinate of the start point of the quadratic curve | ||
| 56 | * segment. | ||
| 57 | * @since 1.2 | ||
| 58 | * @serial | ||
| 59 | */ | ||
| 60 | public float x1; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * The Y coordinate of the start point of the quadratic curve | ||
| 64 | * segment. | ||
| 65 | * @since 1.2 | ||
| 66 | * @serial | ||
| 67 | */ | ||
| 68 | public float y1; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * The X coordinate of the control point of the quadratic curve | ||
| 72 | * segment. | ||
| 73 | * @since 1.2 | ||
| 74 | * @serial | ||
| 75 | */ | ||
| 76 | public float ctrlx; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * The Y coordinate of the control point of the quadratic curve | ||
| 80 | * segment. | ||
| 81 | * @since 1.2 | ||
| 82 | * @serial | ||
| 83 | */ | ||
| 84 | public float ctrly; | ||
| 85 | |||
| 86 | /** | ||
| 87 | * The X coordinate of the end point of the quadratic curve | ||
| 88 | * segment. | ||
| 89 | * @since 1.2 | ||
| 90 | * @serial | ||
| 91 | */ | ||
| 92 | public float x2; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * The Y coordinate of the end point of the quadratic curve | ||
| 96 | * segment. | ||
| 97 | * @since 1.2 | ||
| 98 | * @serial | ||
| 99 | */ | ||
| 100 | public float y2; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Constructs and initializes a <code>QuadCurve2D</code> with | ||
| 104 | * coordinates (0, 0, 0, 0, 0, 0). | ||
| 105 | * @since 1.2 | ||
| 106 | */ | ||
| 107 | public Float() { | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Constructs and initializes a <code>QuadCurve2D</code> from the | ||
| 112 | * specified {@code float} coordinates. | ||
| 113 | * | ||
| 114 | * @param x1 the X coordinate of the start point | ||
| 115 | * @param y1 the Y coordinate of the start point | ||
| 116 | * @param ctrlx the X coordinate of the control point | ||
| 117 | * @param ctrly the Y coordinate of the control point | ||
| 118 | * @param x2 the X coordinate of the end point | ||
| 119 | * @param y2 the Y coordinate of the end point | ||
| 120 | * @since 1.2 | ||
| 121 | */ | ||
| 122 | public Float(float x1, float y1, | ||
| 123 | float ctrlx, float ctrly, | ||
| 124 | float x2, float y2) | ||
| 125 | { | ||
| 126 | setCurve(x1, y1, ctrlx, ctrly, x2, y2); | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * {@inheritDoc} | ||
| 131 | * @since 1.2 | ||
| 132 | */ | ||
| 133 | public double getX1() { | ||
| 134 | return (double) x1; | ||
| 135 | } | ||
| 136 | |||
| 137 | /** | ||
| 138 | * {@inheritDoc} | ||
| 139 | * @since 1.2 | ||
| 140 | */ | ||
| 141 | public double getY1() { | ||
| 142 | return (double) y1; | ||
| 143 | } | ||
| 144 | |||
| 145 | /** | ||
| 146 | * {@inheritDoc} | ||
| 147 | * @since 1.2 | ||
| 148 | */ | ||
| 149 | public Point2D getP1() { | ||
| 150 | return new Point2D.Float(x1, y1); | ||
| 151 | } | ||
| 152 | |||
| 153 | /** | ||
| 154 | * {@inheritDoc} | ||
| 155 | * @since 1.2 | ||
| 156 | */ | ||
| 157 | public double getCtrlX() { | ||
| 158 | return (double) ctrlx; | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * {@inheritDoc} | ||
| 163 | * @since 1.2 | ||
| 164 | */ | ||
| 165 | public double getCtrlY() { | ||
| 166 | return (double) ctrly; | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * {@inheritDoc} | ||
| 171 | * @since 1.2 | ||
| 172 | */ | ||
| 173 | public Point2D getCtrlPt() { | ||
| 174 | return new Point2D.Float(ctrlx, ctrly); | ||
| 175 | } | ||
| 176 | |||
| 177 | /** | ||
| 178 | * {@inheritDoc} | ||
| 179 | * @since 1.2 | ||
| 180 | */ | ||
| 181 | public double getX2() { | ||
| 182 | return (double) x2; | ||
| 183 | } | ||
| 184 | |||
| 185 | /** | ||
| 186 | * {@inheritDoc} | ||
| 187 | * @since 1.2 | ||
| 188 | */ | ||
| 189 | public double getY2() { | ||
| 190 | return (double) y2; | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * {@inheritDoc} | ||
| 195 | * @since 1.2 | ||
| 196 | */ | ||
| 197 | public Point2D getP2() { | ||
| 198 | return new Point2D.Float(x2, y2); | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * {@inheritDoc} | ||
| 203 | * @since 1.2 | ||
| 204 | */ | ||
| 205 | public void setCurve(double x1, double y1, | ||
| 206 | double ctrlx, double ctrly, | ||
| 207 | double x2, double y2) | ||
| 208 | { | ||
| 209 | this.x1 = (float) x1; | ||
| 210 | this.y1 = (float) y1; | ||
| 211 | this.ctrlx = (float) ctrlx; | ||
| 212 | this.ctrly = (float) ctrly; | ||
| 213 | this.x2 = (float) x2; | ||
| 214 | this.y2 = (float) y2; | ||
| 215 | } | ||
| 216 | |||
| 217 | /** | ||
| 218 | * Sets the location of the end points and control point of this curve | ||
| 219 | * to the specified {@code float} coordinates. | ||
| 220 | * | ||
| 221 | * @param x1 the X coordinate of the start point | ||
| 222 | * @param y1 the Y coordinate of the start point | ||
| 223 | * @param ctrlx the X coordinate of the control point | ||
| 224 | * @param ctrly the Y coordinate of the control point | ||
| 225 | * @param x2 the X coordinate of the end point | ||
| 226 | * @param y2 the Y coordinate of the end point | ||
| 227 | * @since 1.2 | ||
| 228 | */ | ||
| 229 | public void setCurve(float x1, float y1, | ||
| 230 | float ctrlx, float ctrly, | ||
| 231 | float x2, float y2) | ||
| 232 | { | ||
| 233 | this.x1 = x1; | ||
| 234 | this.y1 = y1; | ||
| 235 | this.ctrlx = ctrlx; | ||
| 236 | this.ctrly = ctrly; | ||
| 237 | this.x2 = x2; | ||
| 238 | this.y2 = y2; | ||
| 239 | } | ||
| 240 | |||
| 241 | /** | ||
| 242 | * {@inheritDoc} | ||
| 243 | * @since 1.2 | ||
| 244 | */ | ||
| 245 | public Rectangle2D getBounds2D() { | ||
| 246 | float left = Math.min(Math.min(x1, x2), ctrlx); | ||
| 247 | float top = Math.min(Math.min(y1, y2), ctrly); | ||
| 248 | float right = Math.max(Math.max(x1, x2), ctrlx); | ||
| 249 | float bottom = Math.max(Math.max(y1, y2), ctrly); | ||
| 250 | return new Rectangle2D.Float(left, top, | ||
| 251 | right - left, bottom - top); | ||
| 252 | } | ||
| 253 | |||
| 254 | /* | ||
| 255 | * JDK 1.6 serialVersionUID | ||
| 256 | */ | ||
| 257 | private static final long serialVersionUID = -8511188402130719609L; | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * A quadratic parametric curve segment specified with | ||
| 262 | * {@code double} coordinates. | ||
| 263 | * | ||
| 264 | * @since 1.2 | ||
| 265 | */ | ||
| 266 | public static class Double extends QuadCurve2D implements Serializable { | ||
| 267 | /** | ||
| 268 | * The X coordinate of the start point of the quadratic curve | ||
| 269 | * segment. | ||
| 270 | * @since 1.2 | ||
| 271 | * @serial | ||
| 272 | */ | ||
| 273 | public double x1; | ||
| 274 | |||
| 275 | /** | ||
| 276 | * The Y coordinate of the start point of the quadratic curve | ||
| 277 | * segment. | ||
| 278 | * @since 1.2 | ||
| 279 | * @serial | ||
| 280 | */ | ||
| 281 | public double y1; | ||
| 282 | |||
| 283 | /** | ||
| 284 | * The X coordinate of the control point of the quadratic curve | ||
| 285 | * segment. | ||
| 286 | * @since 1.2 | ||
| 287 | * @serial | ||
| 288 | */ | ||
| 289 | public double ctrlx; | ||
| 290 | |||
| 291 | /** | ||
| 292 | * The Y coordinate of the control point of the quadratic curve | ||
| 293 | * segment. | ||
| 294 | * @since 1.2 | ||
| 295 | * @serial | ||
| 296 | */ | ||
| 297 | public double ctrly; | ||
| 298 | |||
| 299 | /** | ||
| 300 | * The X coordinate of the end point of the quadratic curve | ||
| 301 | * segment. | ||
| 302 | * @since 1.2 | ||
| 303 | * @serial | ||
| 304 | */ | ||
| 305 | public double x2; | ||
| 306 | |||
| 307 | /** | ||
| 308 | * The Y coordinate of the end point of the quadratic curve | ||
| 309 | * segment. | ||
| 310 | * @since 1.2 | ||
| 311 | * @serial | ||
| 312 | */ | ||
| 313 | public double y2; | ||
| 314 | |||
| 315 | /** | ||
| 316 | * Constructs and initializes a <code>QuadCurve2D</code> with | ||
| 317 | * coordinates (0, 0, 0, 0, 0, 0). | ||
| 318 | * @since 1.2 | ||
| 319 | */ | ||
| 320 | public Double() { | ||
| 321 | } | ||
| 322 | |||
| 323 | /** | ||
| 324 | * Constructs and initializes a <code>QuadCurve2D</code> from the | ||
| 325 | * specified {@code double} coordinates. | ||
| 326 | * | ||
| 327 | * @param x1 the X coordinate of the start point | ||
| 328 | * @param y1 the Y coordinate of the start point | ||
| 329 | * @param ctrlx the X coordinate of the control point | ||
| 330 | * @param ctrly the Y coordinate of the control point | ||
| 331 | * @param x2 the X coordinate of the end point | ||
| 332 | * @param y2 the Y coordinate of the end point | ||
| 333 | * @since 1.2 | ||
| 334 | */ | ||
| 335 | public Double(double x1, double y1, | ||
| 336 | double ctrlx, double ctrly, | ||
| 337 | double x2, double y2) | ||
| 338 | { | ||
| 339 | setCurve(x1, y1, ctrlx, ctrly, x2, y2); | ||
| 340 | } | ||
| 341 | |||
| 342 | /** | ||
| 343 | * {@inheritDoc} | ||
| 344 | * @since 1.2 | ||
| 345 | */ | ||
| 346 | public double getX1() { | ||
| 347 | return x1; | ||
| 348 | } | ||
| 349 | |||
| 350 | /** | ||
| 351 | * {@inheritDoc} | ||
| 352 | * @since 1.2 | ||
| 353 | */ | ||
| 354 | public double getY1() { | ||
| 355 | return y1; | ||
| 356 | } | ||
| 357 | |||
| 358 | /** | ||
| 359 | * {@inheritDoc} | ||
| 360 | * @since 1.2 | ||
| 361 | */ | ||
| 362 | public Point2D getP1() { | ||
| 363 | return new Point2D.Double(x1, y1); | ||
| 364 | } | ||
| 365 | |||
| 366 | /** | ||
| 367 | * {@inheritDoc} | ||
| 368 | * @since 1.2 | ||
| 369 | */ | ||
| 370 | public double getCtrlX() { | ||
| 371 | return ctrlx; | ||
| 372 | } | ||
| 373 | |||
| 374 | /** | ||
| 375 | * {@inheritDoc} | ||
| 376 | * @since 1.2 | ||
| 377 | */ | ||
| 378 | public double getCtrlY() { | ||
| 379 | return ctrly; | ||
| 380 | } | ||
| 381 | |||
| 382 | /** | ||
| 383 | * {@inheritDoc} | ||
| 384 | * @since 1.2 | ||
| 385 | */ | ||
| 386 | public Point2D getCtrlPt() { | ||
| 387 | return new Point2D.Double(ctrlx, ctrly); | ||
| 388 | } | ||
| 389 | |||
| 390 | /** | ||
| 391 | * {@inheritDoc} | ||
| 392 | * @since 1.2 | ||
| 393 | */ | ||
| 394 | public double getX2() { | ||
| 395 | return x2; | ||
| 396 | } | ||
| 397 | |||
| 398 | /** | ||
| 399 | * {@inheritDoc} | ||
| 400 | * @since 1.2 | ||
| 401 | */ | ||
| 402 | public double getY2() { | ||
| 403 | return y2; | ||
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * {@inheritDoc} | ||
| 408 | * @since 1.2 | ||
| 409 | */ | ||
| 410 | public Point2D getP2() { | ||
| 411 | return new Point2D.Double(x2, y2); | ||
| 412 | } | ||
| 413 | |||
| 414 | /** | ||
| 415 | * {@inheritDoc} | ||
| 416 | * @since 1.2 | ||
| 417 | */ | ||
| 418 | public void setCurve(double x1, double y1, | ||
| 419 | double ctrlx, double ctrly, | ||
| 420 | double x2, double y2) | ||
| 421 | { | ||
| 422 | this.x1 = x1; | ||
| 423 | this.y1 = y1; | ||
| 424 | this.ctrlx = ctrlx; | ||
| 425 | this.ctrly = ctrly; | ||
| 426 | this.x2 = x2; | ||
| 427 | this.y2 = y2; | ||
| 428 | } | ||
| 429 | |||
| 430 | /** | ||
| 431 | * {@inheritDoc} | ||
| 432 | * @since 1.2 | ||
| 433 | */ | ||
| 434 | public Rectangle2D getBounds2D() { | ||
| 435 | double left = Math.min(Math.min(x1, x2), ctrlx); | ||
| 436 | double top = Math.min(Math.min(y1, y2), ctrly); | ||
| 437 | double right = Math.max(Math.max(x1, x2), ctrlx); | ||
| 438 | double bottom = Math.max(Math.max(y1, y2), ctrly); | ||
| 439 | return new Rectangle2D.Double(left, top, | ||
| 440 | right - left, bottom - top); | ||
| 441 | } | ||
| 442 | |||
| 443 | /* | ||
| 444 | * JDK 1.6 serialVersionUID | ||
| 445 | */ | ||
| 446 | private static final long serialVersionUID = 4217149928428559721L; | ||
| 447 | } | ||
| 448 | |||
| 449 | /** | ||
| 450 | * This is an abstract class that cannot be instantiated directly. | ||
| 451 | * Type-specific implementation subclasses are available for | ||
| 452 | * instantiation and provide a number of formats for storing | ||
| 453 | * the information necessary to satisfy the various accessor | ||
| 454 | * methods below. | ||
| 455 | * | ||
| 456 | * @see java.awt.geom.QuadCurve2D.Float | ||
| 457 | * @see java.awt.geom.QuadCurve2D.Double | ||
| 458 | * @since 1.2 | ||
| 459 | */ | ||
| 460 | protected QuadCurve2D() { | ||
| 461 | } | ||
| 462 | |||
| 463 | /** | ||
| 464 | * Returns the X coordinate of the start point in | ||
| 465 | * <code>double</code> in precision. | ||
| 466 | * @return the X coordinate of the start point. | ||
| 467 | * @since 1.2 | ||
| 468 | */ | ||
| 469 | public abstract double getX1(); | ||
| 470 | |||
| 471 | /** | ||
| 472 | * Returns the Y coordinate of the start point in | ||
| 473 | * <code>double</code> precision. | ||
| 474 | * @return the Y coordinate of the start point. | ||
| 475 | * @since 1.2 | ||
| 476 | */ | ||
| 477 | public abstract double getY1(); | ||
| 478 | |||
| 479 | /** | ||
| 480 | * Returns the start point. | ||
| 481 | * @return a <code>Point2D</code> that is the start point of this | ||
| 482 | * <code>QuadCurve2D</code>. | ||
| 483 | * @since 1.2 | ||
| 484 | */ | ||
| 485 | public abstract Point2D getP1(); | ||
| 486 | |||
| 487 | /** | ||
| 488 | * Returns the X coordinate of the control point in | ||
| 489 | * <code>double</code> precision. | ||
| 490 | * @return X coordinate the control point | ||
| 491 | * @since 1.2 | ||
| 492 | */ | ||
| 493 | public abstract double getCtrlX(); | ||
| 494 | |||
| 495 | /** | ||
| 496 | * Returns the Y coordinate of the control point in | ||
| 497 | * <code>double</code> precision. | ||
| 498 | * @return the Y coordinate of the control point. | ||
| 499 | * @since 1.2 | ||
| 500 | */ | ||
| 501 | public abstract double getCtrlY(); | ||
| 502 | |||
| 503 | /** | ||
| 504 | * Returns the control point. | ||
| 505 | * @return a <code>Point2D</code> that is the control point of this | ||
| 506 | * <code>Point2D</code>. | ||
| 507 | * @since 1.2 | ||
| 508 | */ | ||
| 509 | public abstract Point2D getCtrlPt(); | ||
| 510 | |||
| 511 | /** | ||
| 512 | * Returns the X coordinate of the end point in | ||
| 513 | * <code>double</code> precision. | ||
| 514 | * @return the x coordiante of the end point. | ||
| 515 | * @since 1.2 | ||
| 516 | */ | ||
| 517 | public abstract double getX2(); | ||
| 518 | |||
| 519 | /** | ||
| 520 | * Returns the Y coordinate of the end point in | ||
| 521 | * <code>double</code> precision. | ||
| 522 | * @return the Y coordinate of the end point. | ||
| 523 | * @since 1.2 | ||
| 524 | */ | ||
| 525 | public abstract double getY2(); | ||
| 526 | |||
| 527 | /** | ||
| 528 | * Returns the end point. | ||
| 529 | * @return a <code>Point</code> object that is the end point | ||
| 530 | * of this <code>Point2D</code>. | ||
| 531 | * @since 1.2 | ||
| 532 | */ | ||
| 533 | public abstract Point2D getP2(); | ||
| 534 | |||
| 535 | /** | ||
| 536 | * Sets the location of the end points and control point of this curve | ||
| 537 | * to the specified <code>double</code> coordinates. | ||
| 538 | * | ||
| 539 | * @param x1 the X coordinate of the start point | ||
| 540 | * @param y1 the Y coordinate of the start point | ||
| 541 | * @param ctrlx the X coordinate of the control point | ||
| 542 | * @param ctrly the Y coordinate of the control point | ||
| 543 | * @param x2 the X coordinate of the end point | ||
| 544 | * @param y2 the Y coordinate of the end point | ||
| 545 | * @since 1.2 | ||
| 546 | */ | ||
| 547 | public abstract void setCurve(double x1, double y1, | ||
| 548 | double ctrlx, double ctrly, | ||
| 549 | double x2, double y2); | ||
| 550 | |||
| 551 | /** | ||
| 552 | * Sets the location of the end points and control points of this | ||
| 553 | * <code>QuadCurve2D</code> to the <code>double</code> coordinates at | ||
| 554 | * the specified offset in the specified array. | ||
| 555 | * @param coords the array containing coordinate values | ||
| 556 | * @param offset the index into the array from which to start | ||
| 557 | * getting the coordinate values and assigning them to this | ||
| 558 | * <code>QuadCurve2D</code> | ||
| 559 | * @since 1.2 | ||
| 560 | */ | ||
| 561 | public void setCurve(double[] coords, int offset) { | ||
| 562 | setCurve(coords[offset + 0], coords[offset + 1], | ||
| 563 | coords[offset + 2], coords[offset + 3], | ||
| 564 | coords[offset + 4], coords[offset + 5]); | ||
| 565 | } | ||
| 566 | |||
| 567 | /** | ||
| 568 | * Sets the location of the end points and control point of this | ||
| 569 | * <code>QuadCurve2D</code> to the specified <code>Point2D</code> | ||
| 570 | * coordinates. | ||
| 571 | * @param p1 the start point | ||
| 572 | * @param cp the control point | ||
| 573 | * @param p2 the end point | ||
| 574 | * @since 1.2 | ||
| 575 | */ | ||
| 576 | public void setCurve(Point2D p1, Point2D cp, Point2D p2) { | ||
| 577 | setCurve(p1.getX(), p1.getY(), | ||
| 578 | cp.getX(), cp.getY(), | ||
| 579 | p2.getX(), p2.getY()); | ||
| 580 | } | ||
| 581 | |||
| 582 | /** | ||
| 583 | * Sets the location of the end points and control points of this | ||
| 584 | * <code>QuadCurve2D</code> to the coordinates of the | ||
| 585 | * <code>Point2D</code> objects at the specified offset in | ||
| 586 | * the specified array. | ||
| 587 | * @param pts an array containing <code>Point2D</code> that define | ||
| 588 | * coordinate values | ||
| 589 | * @param offset the index into <code>pts</code> from which to start | ||
| 590 | * getting the coordinate values and assigning them to this | ||
| 591 | * <code>QuadCurve2D</code> | ||
| 592 | * @since 1.2 | ||
| 593 | */ | ||
| 594 | public void setCurve(Point2D[] pts, int offset) { | ||
| 595 | setCurve(pts[offset + 0].getX(), pts[offset + 0].getY(), | ||
| 596 | pts[offset + 1].getX(), pts[offset + 1].getY(), | ||
| 597 | pts[offset + 2].getX(), pts[offset + 2].getY()); | ||
| 598 | } | ||
| 599 | |||
| 600 | /** | ||
| 601 | * Sets the location of the end points and control point of this | ||
| 602 | * <code>QuadCurve2D</code> to the same as those in the specified | ||
| 603 | * <code>QuadCurve2D</code>. | ||
| 604 | * @param c the specified <code>QuadCurve2D</code> | ||
| 605 | * @since 1.2 | ||
| 606 | */ | ||
| 607 | public void setCurve(QuadCurve2D c) { | ||
| 608 | setCurve(c.getX1(), c.getY1(), | ||
| 609 | c.getCtrlX(), c.getCtrlY(), | ||
| 610 | c.getX2(), c.getY2()); | ||
| 611 | } | ||
| 612 | |||
| 613 | /** | ||
| 614 | * Returns the square of the flatness, or maximum distance of a | ||
| 615 | * control point from the line connecting the end points, of the | ||
| 616 | * quadratic curve specified by the indicated control points. | ||
| 617 | * | ||
| 618 | * @param x1 the X coordinate of the start point | ||
| 619 | * @param y1 the Y coordinate of the start point | ||
| 620 | * @param ctrlx the X coordinate of the control point | ||
| 621 | * @param ctrly the Y coordinate of the control point | ||
| 622 | * @param x2 the X coordinate of the end point | ||
| 623 | * @param y2 the Y coordinate of the end point | ||
| 624 | * @return the square of the flatness of the quadratic curve | ||
| 625 | * defined by the specified coordinates. | ||
| 626 | * @since 1.2 | ||
| 627 | */ | ||
| 628 | public static double getFlatnessSq(double x1, double y1, | ||
| 629 | double ctrlx, double ctrly, | ||
| 630 | double x2, double y2) { | ||
| 631 | return Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx, ctrly); | ||
| 632 | } | ||
| 633 | |||
| 634 | /** | ||
| 635 | * Returns the flatness, or maximum distance of a | ||
| 636 | * control point from the line connecting the end points, of the | ||
| 637 | * quadratic curve specified by the indicated control points. | ||
| 638 | * | ||
| 639 | * @param x1 the X coordinate of the start point | ||
| 640 | * @param y1 the Y coordinate of the start point | ||
| 641 | * @param ctrlx the X coordinate of the control point | ||
| 642 | * @param ctrly the Y coordinate of the control point | ||
| 643 | * @param x2 the X coordinate of the end point | ||
| 644 | * @param y2 the Y coordinate of the end point | ||
| 645 | * @return the flatness of the quadratic curve defined by the | ||
| 646 | * specified coordinates. | ||
| 647 | * @since 1.2 | ||
| 648 | */ | ||
| 649 | public static double getFlatness(double x1, double y1, | ||
| 650 | double ctrlx, double ctrly, | ||
| 651 | double x2, double y2) { | ||
| 652 | return Line2D.ptSegDist(x1, y1, x2, y2, ctrlx, ctrly); | ||
| 653 | } | ||
| 654 | |||
| 655 | /** | ||
| 656 | * Returns the square of the flatness, or maximum distance of a | ||
| 657 | * control point from the line connecting the end points, of the | ||
| 658 | * quadratic curve specified by the control points stored in the | ||
| 659 | * indicated array at the indicated index. | ||
| 660 | * @param coords an array containing coordinate values | ||
| 661 | * @param offset the index into <code>coords</code> from which to | ||
| 662 | * to start getting the values from the array | ||
| 663 | * @return the flatness of the quadratic curve that is defined by the | ||
| 664 | * values in the specified array at the specified index. | ||
| 665 | * @since 1.2 | ||
| 666 | */ | ||
| 667 | public static double getFlatnessSq(double coords[], int offset) { | ||
| 668 | return Line2D.ptSegDistSq(coords[offset + 0], coords[offset + 1], | ||
| 669 | coords[offset + 4], coords[offset + 5], | ||
| 670 | coords[offset + 2], coords[offset + 3]); | ||
| 671 | } | ||
| 672 | |||
| 673 | /** | ||
| 674 | * Returns the flatness, or maximum distance of a | ||
| 675 | * control point from the line connecting the end points, of the | ||
| 676 | * quadratic curve specified by the control points stored in the | ||
| 677 | * indicated array at the indicated index. | ||
| 678 | * @param coords an array containing coordinate values | ||
| 679 | * @param offset the index into <code>coords</code> from which to | ||
| 680 | * start getting the coordinate values | ||
| 681 | * @return the flatness of a quadratic curve defined by the | ||
| 682 | * specified array at the specified offset. | ||
| 683 | * @since 1.2 | ||
| 684 | */ | ||
| 685 | public static double getFlatness(double coords[], int offset) { | ||
| 686 | return Line2D.ptSegDist(coords[offset + 0], coords[offset + 1], | ||
| 687 | coords[offset + 4], coords[offset + 5], | ||
| 688 | coords[offset + 2], coords[offset + 3]); | ||
| 689 | } | ||
| 690 | |||
| 691 | /** | ||
| 692 | * Returns the square of the flatness, or maximum distance of a | ||
| 693 | * control point from the line connecting the end points, of this | ||
| 694 | * <code>QuadCurve2D</code>. | ||
| 695 | * @return the square of the flatness of this | ||
| 696 | * <code>QuadCurve2D</code>. | ||
| 697 | * @since 1.2 | ||
| 698 | */ | ||
| 699 | public double getFlatnessSq() { | ||
| 700 | return Line2D.ptSegDistSq(getX1(), getY1(), | ||
| 701 | getX2(), getY2(), | ||
| 702 | getCtrlX(), getCtrlY()); | ||
| 703 | } | ||
| 704 | |||
| 705 | /** | ||
| 706 | * Returns the flatness, or maximum distance of a | ||
| 707 | * control point from the line connecting the end points, of this | ||
| 708 | * <code>QuadCurve2D</code>. | ||
| 709 | * @return the flatness of this <code>QuadCurve2D</code>. | ||
| 710 | * @since 1.2 | ||
| 711 | */ | ||
| 712 | public double getFlatness() { | ||
| 713 | return Line2D.ptSegDist(getX1(), getY1(), | ||
| 714 | getX2(), getY2(), | ||
| 715 | getCtrlX(), getCtrlY()); | ||
| 716 | } | ||
| 717 | |||
| 718 | /** | ||
| 719 | * Subdivides this <code>QuadCurve2D</code> and stores the resulting | ||
| 720 | * two subdivided curves into the <code>left</code> and | ||
| 721 | * <code>right</code> curve parameters. | ||
| 722 | * Either or both of the <code>left</code> and <code>right</code> | ||
| 723 | * objects can be the same as this <code>QuadCurve2D</code> or | ||
| 724 | * <code>null</code>. | ||
| 725 | * @param left the <code>QuadCurve2D</code> object for storing the | ||
| 726 | * left or first half of the subdivided curve | ||
| 727 | * @param right the <code>QuadCurve2D</code> object for storing the | ||
| 728 | * right or second half of the subdivided curve | ||
| 729 | * @since 1.2 | ||
| 730 | */ | ||
| 731 | public void subdivide(QuadCurve2D left, QuadCurve2D right) { | ||
| 732 | subdivide(this, left, right); | ||
| 733 | } | ||
| 734 | |||
| 735 | /** | ||
| 736 | * Subdivides the quadratic curve specified by the <code>src</code> | ||
| 737 | * parameter and stores the resulting two subdivided curves into the | ||
| 738 | * <code>left</code> and <code>right</code> curve parameters. | ||
| 739 | * Either or both of the <code>left</code> and <code>right</code> | ||
| 740 | * objects can be the same as the <code>src</code> object or | ||
| 741 | * <code>null</code>. | ||
| 742 | * @param src the quadratic curve to be subdivided | ||
| 743 | * @param left the <code>QuadCurve2D</code> object for storing the | ||
| 744 | * left or first half of the subdivided curve | ||
| 745 | * @param right the <code>QuadCurve2D</code> object for storing the | ||
| 746 | * right or second half of the subdivided curve | ||
| 747 | * @since 1.2 | ||
| 748 | */ | ||
| 749 | public static void subdivide(QuadCurve2D src, | ||
| 750 | QuadCurve2D left, | ||
| 751 | QuadCurve2D right) { | ||
| 752 | double x1 = src.getX1(); | ||
| 753 | double y1 = src.getY1(); | ||
| 754 | double ctrlx = src.getCtrlX(); | ||
| 755 | double ctrly = src.getCtrlY(); | ||
| 756 | double x2 = src.getX2(); | ||
| 757 | double y2 = src.getY2(); | ||
| 758 | double ctrlx1 = (x1 + ctrlx) / 2.0; | ||
| 759 | double ctrly1 = (y1 + ctrly) / 2.0; | ||
| 760 | double ctrlx2 = (x2 + ctrlx) / 2.0; | ||
| 761 | double ctrly2 = (y2 + ctrly) / 2.0; | ||
| 762 | ctrlx = (ctrlx1 + ctrlx2) / 2.0; | ||
| 763 | ctrly = (ctrly1 + ctrly2) / 2.0; | ||
| 764 | if (left != null) { | ||
| 765 | left.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx, ctrly); | ||
| 766 | } | ||
| 767 | if (right != null) { | ||
| 768 | right.setCurve(ctrlx, ctrly, ctrlx2, ctrly2, x2, y2); | ||
| 769 | } | ||
| 770 | } | ||
| 771 | |||
| 772 | /** | ||
| 773 | * Subdivides the quadratic curve specified by the coordinates | ||
| 774 | * stored in the <code>src</code> array at indices | ||
| 775 | * <code>srcoff</code> through <code>srcoff</code> + 5 | ||
| 776 | * and stores the resulting two subdivided curves into the two | ||
| 777 | * result arrays at the corresponding indices. | ||
| 778 | * Either or both of the <code>left</code> and <code>right</code> | ||
| 779 | * arrays can be <code>null</code> or a reference to the same array | ||
| 780 | * and offset as the <code>src</code> array. | ||
| 781 | * Note that the last point in the first subdivided curve is the | ||
| 782 | * same as the first point in the second subdivided curve. Thus, | ||
| 783 | * it is possible to pass the same array for <code>left</code> and | ||
| 784 | * <code>right</code> and to use offsets such that | ||
| 785 | * <code>rightoff</code> equals <code>leftoff</code> + 4 in order | ||
| 786 | * to avoid allocating extra storage for this common point. | ||
| 787 | * @param src the array holding the coordinates for the source curve | ||
| 788 | * @param srcoff the offset into the array of the beginning of the | ||
| 789 | * the 6 source coordinates | ||
| 790 | * @param left the array for storing the coordinates for the first | ||
| 791 | * half of the subdivided curve | ||
| 792 | * @param leftoff the offset into the array of the beginning of the | ||
| 793 | * the 6 left coordinates | ||
| 794 | * @param right the array for storing the coordinates for the second | ||
| 795 | * half of the subdivided curve | ||
| 796 | * @param rightoff the offset into the array of the beginning of the | ||
| 797 | * the 6 right coordinates | ||
| 798 | * @since 1.2 | ||
| 799 | */ | ||
| 800 | public static void subdivide(double src[], int srcoff, | ||
| 801 | double left[], int leftoff, | ||
| 802 | double right[], int rightoff) { | ||
| 803 | double x1 = src[srcoff + 0]; | ||
| 804 | double y1 = src[srcoff + 1]; | ||
| 805 | double ctrlx = src[srcoff + 2]; | ||
| 806 | double ctrly = src[srcoff + 3]; | ||
| 807 | double x2 = src[srcoff + 4]; | ||
| 808 | double y2 = src[srcoff + 5]; | ||
| 809 | if (left != null) { | ||
| 810 | left[leftoff + 0] = x1; | ||
| 811 | left[leftoff + 1] = y1; | ||
| 812 | } | ||
| 813 | if (right != null) { | ||
| 814 | right[rightoff + 4] = x2; | ||
| 815 | right[rightoff + 5] = y2; | ||
| 816 | } | ||
| 817 | x1 = (x1 + ctrlx) / 2.0; | ||
| 818 | y1 = (y1 + ctrly) / 2.0; | ||
| 819 | x2 = (x2 + ctrlx) / 2.0; | ||
| 820 | y2 = (y2 + ctrly) / 2.0; | ||
| 821 | ctrlx = (x1 + x2) / 2.0; | ||
| 822 | ctrly = (y1 + y2) / 2.0; | ||
| 823 | if (left != null) { | ||
| 824 | left[leftoff + 2] = x1; | ||
| 825 | left[leftoff + 3] = y1; | ||
| 826 | left[leftoff + 4] = ctrlx; | ||
| 827 | left[leftoff + 5] = ctrly; | ||
| 828 | } | ||
| 829 | if (right != null) { | ||
| 830 | right[rightoff + 0] = ctrlx; | ||
| 831 | right[rightoff + 1] = ctrly; | ||
| 832 | right[rightoff + 2] = x2; | ||
| 833 | right[rightoff + 3] = y2; | ||
| 834 | } | ||
| 835 | } | ||
| 836 | |||
| 837 | /** | ||
| 838 | * Solves the quadratic whose coefficients are in the <code>eqn</code> | ||
| 839 | * array and places the non-complex roots back into the same array, | ||
| 840 | * returning the number of roots. The quadratic solved is represented | ||
| 841 | * by the equation: | ||
| 842 | * <pre> | ||
| 843 | * eqn = {C, B, A}; | ||
| 844 | * ax^2 + bx + c = 0 | ||
| 845 | * </pre> | ||
| 846 | * A return value of <code>-1</code> is used to distinguish a constant | ||
| 847 | * equation, which might be always 0 or never 0, from an equation that | ||
| 848 | * has no zeroes. | ||
| 849 | * @param eqn the array that contains the quadratic coefficients | ||
| 850 | * @return the number of roots, or <code>-1</code> if the equation is | ||
| 851 | * a constant | ||
| 852 | * @since 1.2 | ||
| 853 | */ | ||
| 854 | public static int solveQuadratic(double eqn[]) { | ||
| 855 | return solveQuadratic(eqn, eqn); | ||
| 856 | } | ||
| 857 | |||
| 858 | /** | ||
| 859 | * Solves the quadratic whose coefficients are in the <code>eqn</code> | ||
| 860 | * array and places the non-complex roots into the <code>res</code> | ||
| 861 | * array, returning the number of roots. | ||
| 862 | * The quadratic solved is represented by the equation: | ||
| 863 | * <pre> | ||
| 864 | * eqn = {C, B, A}; | ||
| 865 | * ax^2 + bx + c = 0 | ||
| 866 | * </pre> | ||
| 867 | * A return value of <code>-1</code> is used to distinguish a constant | ||
| 868 | * equation, which might be always 0 or never 0, from an equation that | ||
| 869 | * has no zeroes. | ||
| 870 | * @param eqn the specified array of coefficients to use to solve | ||
| 871 | * the quadratic equation | ||
| 872 | * @param res the array that contains the non-complex roots | ||
| 873 | * resulting from the solution of the quadratic equation | ||
| 874 | * @return the number of roots, or <code>-1</code> if the equation is | ||
| 875 | * a constant. | ||
| 876 | * @since 1.3 | ||
| 877 | */ | ||
| 878 | public static int solveQuadratic(double eqn[], double res[]) { | ||
| 879 | double a = eqn[2]; | ||
| 880 | double b = eqn[1]; | ||
| 881 | double c = eqn[0]; | ||
| 882 | int roots = 0; | ||
| 883 | if (a == 0.0) { | ||
| 884 | // The quadratic parabola has degenerated to a line. | ||
| 885 | if (b == 0.0) { | ||
| 886 | // The line has degenerated to a constant. | ||
| 887 | return -1; | ||
| 888 | } | ||
| 889 | res[roots++] = -c / b; | ||
| 890 | } else { | ||
| 891 | // From Numerical Recipes, 5.6, Quadratic and Cubic Equations | ||
| 892 | double d = b * b - 4.0 * a * c; | ||
| 893 | if (d < 0.0) { | ||
| 894 | // If d < 0.0, then there are no roots | ||
| 895 | return 0; | ||
| 896 | } | ||
| 897 | d = Math.sqrt(d); | ||
| 898 | // For accuracy, calculate one root using: | ||
| 899 | // (-b +/- d) / 2a | ||
| 900 | // and the other using: | ||
| 901 | // 2c / (-b +/- d) | ||
| 902 | // Choose the sign of the +/- so that b+d gets larger in magnitude | ||
| 903 | if (b < 0.0) { | ||
| 904 | d = -d; | ||
| 905 | } | ||
| 906 | double q = (b + d) / -2.0; | ||
| 907 | // We already tested a for being 0 above | ||
| 908 | res[roots++] = q / a; | ||
| 909 | if (q != 0.0) { | ||
| 910 | res[roots++] = c / q; | ||
| 911 | } | ||
| 912 | } | ||
| 913 | return roots; | ||
| 914 | } | ||
| 915 | |||
| 916 | /** | ||
| 917 | * {@inheritDoc} | ||
| 918 | * @since 1.2 | ||
| 919 | */ | ||
| 920 | public boolean contains(double x, double y) { | ||
| 921 | |||
| 922 | double x1 = getX1(); | ||
| 923 | double y1 = getY1(); | ||
| 924 | double xc = getCtrlX(); | ||
| 925 | double yc = getCtrlY(); | ||
| 926 | double x2 = getX2(); | ||
| 927 | double y2 = getY2(); | ||
| 928 | |||
| 929 | /* | ||
| 930 | * We have a convex shape bounded by quad curve Pc(t) | ||
| 931 | * and ine Pl(t). | ||
| 932 | * | ||
| 933 | * P1 = (x1, y1) - start point of curve | ||
| 934 | * P2 = (x2, y2) - end point of curve | ||
| 935 | * Pc = (xc, yc) - control point | ||
| 936 | * | ||
| 937 | * Pq(t) = P1*(1 - t)^2 + 2*Pc*t*(1 - t) + P2*t^2 = | ||
| 938 | * = (P1 - 2*Pc + P2)*t^2 + 2*(Pc - P1)*t + P1 | ||
| 939 | * Pl(t) = P1*(1 - t) + P2*t | ||
| 940 | * t = [0:1] | ||
| 941 | * | ||
| 942 | * P = (x, y) - point of interest | ||
| 943 | * | ||
| 944 | * Let's look at second derivative of quad curve equation: | ||
| 945 | * | ||
| 946 | * Pq''(t) = 2 * (P1 - 2 * Pc + P2) = Pq'' | ||
| 947 | * It's constant vector. | ||
| 948 | * | ||
| 949 | * Let's draw a line through P to be parallel to this | ||
| 950 | * vector and find the intersection of the quad curve | ||
| 951 | * and the line. | ||
| 952 | * | ||
| 953 | * Pq(t) is point of intersection if system of equations | ||
| 954 | * below has the solution. | ||
| 955 | * | ||
| 956 | * L(s) = P + Pq''*s == Pq(t) | ||
| 957 | * Pq''*s + (P - Pq(t)) == 0 | ||
| 958 | * | ||
| 959 | * | xq''*s + (x - xq(t)) == 0 | ||
| 960 | * | yq''*s + (y - yq(t)) == 0 | ||
| 961 | * | ||
| 962 | * This system has the solution if rank of its matrix equals to 1. | ||
| 963 | * That is, determinant of the matrix should be zero. | ||
| 964 | * | ||
| 965 | * (y - yq(t))*xq'' == (x - xq(t))*yq'' | ||
| 966 | * | ||
| 967 | * Let's solve this equation with 't' variable. | ||
| 968 | * Also let kx = x1 - 2*xc + x2 | ||
| 969 | * ky = y1 - 2*yc + y2 | ||
| 970 | * | ||
| 971 | * t0q = (1/2)*((x - x1)*ky - (y - y1)*kx) / | ||
| 972 | * ((xc - x1)*ky - (yc - y1)*kx) | ||
| 973 | * | ||
| 974 | * Let's do the same for our line Pl(t): | ||
| 975 | * | ||
| 976 | * t0l = ((x - x1)*ky - (y - y1)*kx) / | ||
| 977 | * ((x2 - x1)*ky - (y2 - y1)*kx) | ||
| 978 | * | ||
| 979 | * It's easy to check that t0q == t0l. This fact means | ||
| 980 | * we can compute t0 only one time. | ||
| 981 | * | ||
| 982 | * In case t0 < 0 or t0 > 1, we have an intersections outside | ||
| 983 | * of shape bounds. So, P is definitely out of shape. | ||
| 984 | * | ||
| 985 | * In case t0 is inside [0:1], we should calculate Pq(t0) | ||
| 986 | * and Pl(t0). We have three points for now, and all of them | ||
| 987 | * lie on one line. So, we just need to detect, is our point | ||
| 988 | * of interest between points of intersections or not. | ||
| 989 | * | ||
| 990 | * If the denominator in the t0q and t0l equations is | ||
| 991 | * zero, then the points must be collinear and so the | ||
| 992 | * curve is degenerate and encloses no area. Thus the | ||
| 993 | * result is false. | ||
| 994 | */ | ||
| 995 | double kx = x1 - 2 * xc + x2; | ||
| 996 | double ky = y1 - 2 * yc + y2; | ||
| 997 | double dx = x - x1; | ||
| 998 | double dy = y - y1; | ||
| 999 | double dxl = x2 - x1; | ||
| 1000 | double dyl = y2 - y1; | ||
| 1001 | |||
| 1002 | double t0 = (dx * ky - dy * kx) / (dxl * ky - dyl * kx); | ||
| 1003 | if (t0 < 0 || t0 > 1 || t0 != t0) { | ||
| 1004 | return false; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | double xb = kx * t0 * t0 + 2 * (xc - x1) * t0 + x1; | ||
| 1008 | double yb = ky * t0 * t0 + 2 * (yc - y1) * t0 + y1; | ||
| 1009 | double xl = dxl * t0 + x1; | ||
| 1010 | double yl = dyl * t0 + y1; | ||
| 1011 | |||
| 1012 | return (x >= xb && x < xl) || | ||
| 1013 | (x >= xl && x < xb) || | ||
| 1014 | (y >= yb && y < yl) || | ||
| 1015 | (y >= yl && y < yb); | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | /** | ||
| 1019 | * {@inheritDoc} | ||
| 1020 | * @since 1.2 | ||
| 1021 | */ | ||
| 1022 | public boolean contains(Point2D p) { | ||
| 1023 | return contains(p.getX(), p.getY()); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | /** | ||
| 1027 | * Fill an array with the coefficients of the parametric equation | ||
| 1028 | * in t, ready for solving against val with solveQuadratic. | ||
| 1029 | * We currently have: | ||
| 1030 | * val = Py(t) = C1*(1-t)^2 + 2*CP*t*(1-t) + C2*t^2 | ||
| 1031 | * = C1 - 2*C1*t + C1*t^2 + 2*CP*t - 2*CP*t^2 + C2*t^2 | ||
| 1032 | * = C1 + (2*CP - 2*C1)*t + (C1 - 2*CP + C2)*t^2 | ||
| 1033 | * 0 = (C1 - val) + (2*CP - 2*C1)*t + (C1 - 2*CP + C2)*t^2 | ||
| 1034 | * 0 = C + Bt + At^2 | ||
| 1035 | * C = C1 - val | ||
| 1036 | * B = 2*CP - 2*C1 | ||
| 1037 | * A = C1 - 2*CP + C2 | ||
| 1038 | */ | ||
| 1039 | private static void fillEqn(double eqn[], double val, | ||
| 1040 | double c1, double cp, double c2) { | ||
| 1041 | eqn[0] = c1 - val; | ||
| 1042 | eqn[1] = cp + cp - c1 - c1; | ||
| 1043 | eqn[2] = c1 - cp - cp + c2; | ||
| 1044 | return; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | /** | ||
| 1048 | * Evaluate the t values in the first num slots of the vals[] array | ||
| 1049 | * and place the evaluated values back into the same array. Only | ||
| 1050 | * evaluate t values that are within the range <0, 1>, including | ||
| 1051 | * the 0 and 1 ends of the range iff the include0 or include1 | ||
| 1052 | * booleans are true. If an "inflection" equation is handed in, | ||
| 1053 | * then any points which represent a point of inflection for that | ||
| 1054 | * quadratic equation are also ignored. | ||
| 1055 | */ | ||
| 1056 | private static int evalQuadratic(double vals[], int num, | ||
| 1057 | boolean include0, | ||
| 1058 | boolean include1, | ||
| 1059 | double inflect[], | ||
| 1060 | double c1, double ctrl, double c2) { | ||
| 1061 | int j = 0; | ||
| 1062 | for (int i = 0; i < num; i++) { | ||
| 1063 | double t = vals[i]; | ||
| 1064 | if ((include0 ? t >= 0 : t > 0) && | ||
| 1065 | (include1 ? t <= 1 : t < 1) && | ||
| 1066 | (inflect == null || | ||
| 1067 | inflect[1] + 2*inflect[2]*t != 0)) | ||
| 1068 | { | ||
| 1069 | double u = 1 - t; | ||
| 1070 | vals[j++] = c1*u*u + 2*ctrl*t*u + c2*t*t; | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | return j; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | private static final int BELOW = -2; | ||
| 1077 | private static final int LOWEDGE = -1; | ||
| 1078 | private static final int INSIDE = 0; | ||
| 1079 | private static final int HIGHEDGE = 1; | ||
| 1080 | private static final int ABOVE = 2; | ||
| 1081 | |||
| 1082 | /** | ||
| 1083 | * Determine where coord lies with respect to the range from | ||
| 1084 | * low to high. It is assumed that low <= high. The return | ||
| 1085 | * value is one of the 5 values BELOW, LOWEDGE, INSIDE, HIGHEDGE, | ||
| 1086 | * or ABOVE. | ||
| 1087 | */ | ||
| 1088 | private static int getTag(double coord, double low, double high) { | ||
| 1089 | if (coord <= low) { | ||
| 1090 | return (coord < low ? BELOW : LOWEDGE); | ||
| 1091 | } | ||
| 1092 | if (coord >= high) { | ||
| 1093 | return (coord > high ? ABOVE : HIGHEDGE); | ||
| 1094 | } | ||
| 1095 | return INSIDE; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | /** | ||
| 1099 | * Determine if the pttag represents a coordinate that is already | ||
| 1100 | * in its test range, or is on the border with either of the two | ||
| 1101 | * opttags representing another coordinate that is "towards the | ||
| 1102 | * inside" of that test range. In other words, are either of the | ||
| 1103 | * two "opt" points "drawing the pt inward"? | ||
| 1104 | */ | ||
| 1105 | private static boolean inwards(int pttag, int opt1tag, int opt2tag) { | ||
| 1106 | switch (pttag) { | ||
| 1107 | case BELOW: | ||
| 1108 | case ABOVE: | ||
| 1109 | default: | ||
| 1110 | return false; | ||
| 1111 | case LOWEDGE: | ||
| 1112 | return (opt1tag >= INSIDE || opt2tag >= INSIDE); | ||
| 1113 | case INSIDE: | ||
| 1114 | return true; | ||
| 1115 | case HIGHEDGE: | ||
| 1116 | return (opt1tag <= INSIDE || opt2tag <= INSIDE); | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | /** | ||
| 1121 | * {@inheritDoc} | ||
| 1122 | * @since 1.2 | ||
| 1123 | */ | ||
| 1124 | public boolean intersects(double x, double y, double w, double h) { | ||
| 1125 | // Trivially reject non-existant rectangles | ||
| 1126 | if (w <= 0 || h <= 0) { | ||
| 1127 | return false; | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | // Trivially accept if either endpoint is inside the rectangle | ||
| 1131 | // (not on its border since it may end there and not go inside) | ||
| 1132 | // Record where they lie with respect to the rectangle. | ||
| 1133 | // -1 => left, 0 => inside, 1 => right | ||
| 1134 | double x1 = getX1(); | ||
| 1135 | double y1 = getY1(); | ||
| 1136 | int x1tag = getTag(x1, x, x+w); | ||
| 1137 | int y1tag = getTag(y1, y, y+h); | ||
| 1138 | if (x1tag == INSIDE && y1tag == INSIDE) { | ||
| 1139 | return true; | ||
| 1140 | } | ||
| 1141 | double x2 = getX2(); | ||
| 1142 | double y2 = getY2(); | ||
| 1143 | int x2tag = getTag(x2, x, x+w); | ||
| 1144 | int y2tag = getTag(y2, y, y+h); | ||
| 1145 | if (x2tag == INSIDE && y2tag == INSIDE) { | ||
| 1146 | return true; | ||
| 1147 | } | ||
| 1148 | double ctrlx = getCtrlX(); | ||
| 1149 | double ctrly = getCtrlY(); | ||
| 1150 | int ctrlxtag = getTag(ctrlx, x, x+w); | ||
| 1151 | int ctrlytag = getTag(ctrly, y, y+h); | ||
| 1152 | |||
| 1153 | // Trivially reject if all points are entirely to one side of | ||
| 1154 | // the rectangle. | ||
| 1155 | if (x1tag < INSIDE && x2tag < INSIDE && ctrlxtag < INSIDE) { | ||
| 1156 | return false; // All points left | ||
| 1157 | } | ||
| 1158 | if (y1tag < INSIDE && y2tag < INSIDE && ctrlytag < INSIDE) { | ||
| 1159 | return false; // All points above | ||
| 1160 | } | ||
| 1161 | if (x1tag > INSIDE && x2tag > INSIDE && ctrlxtag > INSIDE) { | ||
| 1162 | return false; // All points right | ||
| 1163 | } | ||
| 1164 | if (y1tag > INSIDE && y2tag > INSIDE && ctrlytag > INSIDE) { | ||
| 1165 | return false; // All points below | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | // Test for endpoints on the edge where either the segment | ||
| 1169 | // or the curve is headed "inwards" from them | ||
| 1170 | // Note: These tests are a superset of the fast endpoint tests | ||
| 1171 | // above and thus repeat those tests, but take more time | ||
| 1172 | // and cover more cases | ||
| 1173 | if (inwards(x1tag, x2tag, ctrlxtag) && | ||
| 1174 | inwards(y1tag, y2tag, ctrlytag)) | ||
| 1175 | { | ||
| 1176 | // First endpoint on border with either edge moving inside | ||
| 1177 | return true; | ||
| 1178 | } | ||
| 1179 | if (inwards(x2tag, x1tag, ctrlxtag) && | ||
| 1180 | inwards(y2tag, y1tag, ctrlytag)) | ||
| 1181 | { | ||
| 1182 | // Second endpoint on border with either edge moving inside | ||
| 1183 | return true; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | // Trivially accept if endpoints span directly across the rectangle | ||
| 1187 | boolean xoverlap = (x1tag * x2tag <= 0); | ||
| 1188 | boolean yoverlap = (y1tag * y2tag <= 0); | ||
| 1189 | if (x1tag == INSIDE && x2tag == INSIDE && yoverlap) { | ||
| 1190 | return true; | ||
| 1191 | } | ||
| 1192 | if (y1tag == INSIDE && y2tag == INSIDE && xoverlap) { | ||
| 1193 | return true; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | // We now know that both endpoints are outside the rectangle | ||
| 1197 | // but the 3 points are not all on one side of the rectangle. | ||
| 1198 | // Therefore the curve cannot be contained inside the rectangle, | ||
| 1199 | // but the rectangle might be contained inside the curve, or | ||
| 1200 | // the curve might intersect the boundary of the rectangle. | ||
| 1201 | |||
| 1202 | double[] eqn = new double[3]; | ||
| 1203 | double[] res = new double[3]; | ||
| 1204 | if (!yoverlap) { | ||
| 1205 | // Both Y coordinates for the closing segment are above or | ||
| 1206 | // below the rectangle which means that we can only intersect | ||
| 1207 | // if the curve crosses the top (or bottom) of the rectangle | ||
| 1208 | // in more than one place and if those crossing locations | ||
| 1209 | // span the horizontal range of the rectangle. | ||
| 1210 | fillEqn(eqn, (y1tag < INSIDE ? y : y+h), y1, ctrly, y2); | ||
| 1211 | return (solveQuadratic(eqn, res) == 2 && | ||
| 1212 | evalQuadratic(res, 2, true, true, null, | ||
| 1213 | x1, ctrlx, x2) == 2 && | ||
| 1214 | getTag(res[0], x, x+w) * getTag(res[1], x, x+w) <= 0); | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | // Y ranges overlap. Now we examine the X ranges | ||
| 1218 | if (!xoverlap) { | ||
| 1219 | // Both X coordinates for the closing segment are left of | ||
| 1220 | // or right of the rectangle which means that we can only | ||
| 1221 | // intersect if the curve crosses the left (or right) edge | ||
| 1222 | // of the rectangle in more than one place and if those | ||
| 1223 | // crossing locations span the vertical range of the rectangle. | ||
| 1224 | fillEqn(eqn, (x1tag < INSIDE ? x : x+w), x1, ctrlx, x2); | ||
| 1225 | return (solveQuadratic(eqn, res) == 2 && | ||
| 1226 | evalQuadratic(res, 2, true, true, null, | ||
| 1227 | y1, ctrly, y2) == 2 && | ||
| 1228 | getTag(res[0], y, y+h) * getTag(res[1], y, y+h) <= 0); | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | // The X and Y ranges of the endpoints overlap the X and Y | ||
| 1232 | // ranges of the rectangle, now find out how the endpoint | ||
| 1233 | // line segment intersects the Y range of the rectangle | ||
| 1234 | double dx = x2 - x1; | ||
| 1235 | double dy = y2 - y1; | ||
| 1236 | double k = y2 * x1 - x2 * y1; | ||
| 1237 | int c1tag, c2tag; | ||
| 1238 | if (y1tag == INSIDE) { | ||
| 1239 | c1tag = x1tag; | ||
| 1240 | } else { | ||
| 1241 | c1tag = getTag((k + dx * (y1tag < INSIDE ? y : y+h)) / dy, x, x+w); | ||
| 1242 | } | ||
| 1243 | if (y2tag == INSIDE) { | ||
| 1244 | c2tag = x2tag; | ||
| 1245 | } else { | ||
| 1246 | c2tag = getTag((k + dx * (y2tag < INSIDE ? y : y+h)) / dy, x, x+w); | ||
| 1247 | } | ||
| 1248 | // If the part of the line segment that intersects the Y range | ||
| 1249 | // of the rectangle crosses it horizontally - trivially accept | ||
| 1250 | if (c1tag * c2tag <= 0) { | ||
| 1251 | return true; | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | // Now we know that both the X and Y ranges intersect and that | ||
| 1255 | // the endpoint line segment does not directly cross the rectangle. | ||
| 1256 | // | ||
| 1257 | // We can almost treat this case like one of the cases above | ||
| 1258 | // where both endpoints are to one side, except that we will | ||
| 1259 | // only get one intersection of the curve with the vertical | ||
| 1260 | // side of the rectangle. This is because the endpoint segment | ||
| 1261 | // accounts for the other intersection. | ||
| 1262 | // | ||
| 1263 | // (Remember there is overlap in both the X and Y ranges which | ||
| 1264 | // means that the segment must cross at least one vertical edge | ||
| 1265 | // of the rectangle - in particular, the "near vertical side" - | ||
| 1266 | // leaving only one intersection for the curve.) | ||
| 1267 | // | ||
| 1268 | // Now we calculate the y tags of the two intersections on the | ||
| 1269 | // "near vertical side" of the rectangle. We will have one with | ||
| 1270 | // the endpoint segment, and one with the curve. If those two | ||
| 1271 | // vertical intersections overlap the Y range of the rectangle, | ||
| 1272 | // we have an intersection. Otherwise, we don't. | ||
| 1273 | |||
| 1274 | // c1tag = vertical intersection class of the endpoint segment | ||
| 1275 | // | ||
| 1276 | // Choose the y tag of the endpoint that was not on the same | ||
| 1277 | // side of the rectangle as the subsegment calculated above. | ||
| 1278 | // Note that we can "steal" the existing Y tag of that endpoint | ||
| 1279 | // since it will be provably the same as the vertical intersection. | ||
| 1280 | c1tag = ((c1tag * x1tag <= 0) ? y1tag : y2tag); | ||
| 1281 | |||
| 1282 | // c2tag = vertical intersection class of the curve | ||
| 1283 | // | ||
| 1284 | // We have to calculate this one the straightforward way. | ||
| 1285 | // Note that the c2tag can still tell us which vertical edge | ||
| 1286 | // to test against. | ||
| 1287 | fillEqn(eqn, (c2tag < INSIDE ? x : x+w), x1, ctrlx, x2); | ||
| 1288 | int num = solveQuadratic(eqn, res); | ||
| 1289 | |||
| 1290 | // Note: We should be able to assert(num == 2); since the | ||
| 1291 | // X range "crosses" (not touches) the vertical boundary, | ||
| 1292 | // but we pass num to evalQuadratic for completeness. | ||
| 1293 | evalQuadratic(res, num, true, true, null, y1, ctrly, y2); | ||
| 1294 | |||
| 1295 | // Note: We can assert(num evals == 1); since one of the | ||
| 1296 | // 2 crossings will be out of the [0,1] range. | ||
| 1297 | c2tag = getTag(res[0], y, y+h); | ||
| 1298 | |||
| 1299 | // Finally, we have an intersection if the two crossings | ||
| 1300 | // overlap the Y range of the rectangle. | ||
| 1301 | return (c1tag * c2tag <= 0); | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | /** | ||
| 1305 | * {@inheritDoc} | ||
| 1306 | * @since 1.2 | ||
| 1307 | */ | ||
| 1308 | public boolean intersects(Rectangle2D r) { | ||
| 1309 | return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | /** | ||
| 1313 | * {@inheritDoc} | ||
| 1314 | * @since 1.2 | ||
| 1315 | */ | ||
| 1316 | public boolean contains(double x, double y, double w, double h) { | ||
| 1317 | if (w <= 0 || h <= 0) { | ||
| 1318 | return false; | ||
| 1319 | } | ||
| 1320 | // Assertion: Quadratic curves closed by connecting their | ||
| 1321 | // endpoints are always convex. | ||
| 1322 | return (contains(x, y) && | ||
| 1323 | contains(x + w, y) && | ||
| 1324 | contains(x + w, y + h) && | ||
| 1325 | contains(x, y + h)); | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | /** | ||
| 1329 | * {@inheritDoc} | ||
| 1330 | * @since 1.2 | ||
| 1331 | */ | ||
| 1332 | public boolean contains(Rectangle2D r) { | ||
| 1333 | return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | /** | ||
| 1337 | * {@inheritDoc} | ||
| 1338 | * @since 1.2 | ||
| 1339 | */ | ||
| 1340 | public Rectangle getBounds() { | ||
| 1341 | return getBounds2D().getBounds(); | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | /** | ||
| 1345 | * Returns an iteration object that defines the boundary of the | ||
| 1346 | * shape of this <code>QuadCurve2D</code>. | ||
| 1347 | * The iterator for this class is not multi-threaded safe, | ||
| 1348 | * which means that this <code>QuadCurve2D</code> class does not | ||
| 1349 | * guarantee that modifications to the geometry of this | ||
| 1350 | * <code>QuadCurve2D</code> object do not affect any iterations of | ||
| 1351 | * that geometry that are already in process. | ||
| 1352 | * @param at an optional {@link AffineTransform} to apply to the | ||
| 1353 | * shape boundary | ||
| 1354 | * @return a {@link PathIterator} object that defines the boundary | ||
| 1355 | * of the shape. | ||
| 1356 | * @since 1.2 | ||
| 1357 | */ | ||
| 1358 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 1359 | return new QuadIterator(this, at); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | /** | ||
| 1363 | * Returns an iteration object that defines the boundary of the | ||
| 1364 | * flattened shape of this <code>QuadCurve2D</code>. | ||
| 1365 | * The iterator for this class is not multi-threaded safe, | ||
| 1366 | * which means that this <code>QuadCurve2D</code> class does not | ||
| 1367 | * guarantee that modifications to the geometry of this | ||
| 1368 | * <code>QuadCurve2D</code> object do not affect any iterations of | ||
| 1369 | * that geometry that are already in process. | ||
| 1370 | * @param at an optional <code>AffineTransform</code> to apply | ||
| 1371 | * to the boundary of the shape | ||
| 1372 | * @param flatness the maximum distance that the control points for a | ||
| 1373 | * subdivided curve can be with respect to a line connecting | ||
| 1374 | * the end points of this curve before this curve is | ||
| 1375 | * replaced by a straight line connecting the end points. | ||
| 1376 | * @return a <code>PathIterator</code> object that defines the | ||
| 1377 | * flattened boundary of the shape. | ||
| 1378 | * @since 1.2 | ||
| 1379 | */ | ||
| 1380 | public PathIterator getPathIterator(AffineTransform at, double flatness) { | ||
| 1381 | return new FlatteningPathIterator(getPathIterator(at), flatness); | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | /** | ||
| 1385 | * Creates a new object of the same class and with the same contents | ||
| 1386 | * as this object. | ||
| 1387 | * | ||
| 1388 | * @return a clone of this instance. | ||
| 1389 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 1390 | * @see java.lang.Cloneable | ||
| 1391 | * @since 1.2 | ||
| 1392 | */ | ||
| 1393 | public Object clone() { | ||
| 1394 | try { | ||
| 1395 | return super.clone(); | ||
| 1396 | } catch (CloneNotSupportedException e) { | ||
| 1397 | // this shouldn't happen, since we are Cloneable | ||
| 1398 | throw new InternalError(); | ||
| 1399 | } | ||
| 1400 | } | ||
| 1401 | } | ||
Binary file not shown.
| @@ -0,0 +1,152 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A utility class to iterate over the path segments of a quadratic curve | ||
| 32 | * segment through the PathIterator interface. | ||
| 33 | * | ||
| 34 | * @author Jim Graham | ||
| 35 | */ | ||
| 36 | class QuadIterator implements PathIterator { | ||
| 37 | QuadCurve2D quad; | ||
| 38 | AffineTransform affine; | ||
| 39 | int index; | ||
| 40 | |||
| 41 | QuadIterator(QuadCurve2D q, AffineTransform at) { | ||
| 42 | this.quad = q; | ||
| 43 | this.affine = at; | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Return the winding rule for determining the insideness of the | ||
| 48 | * path. | ||
| 49 | * @see #WIND_EVEN_ODD | ||
| 50 | * @see #WIND_NON_ZERO | ||
| 51 | */ | ||
| 52 | public int getWindingRule() { | ||
| 53 | return WIND_NON_ZERO; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Tests if there are more points to read. | ||
| 58 | * @return true if there are more points to read | ||
| 59 | */ | ||
| 60 | public boolean isDone() { | ||
| 61 | return (index > 1); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Moves the iterator to the next segment of the path forwards | ||
| 66 | * along the primary direction of traversal as long as there are | ||
| 67 | * more points in that direction. | ||
| 68 | */ | ||
| 69 | public void next() { | ||
| 70 | index++; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Returns the coordinates and type of the current path segment in | ||
| 75 | * the iteration. | ||
| 76 | * The return value is the path segment type: | ||
| 77 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 78 | * A float array of length 6 must be passed in and may be used to | ||
| 79 | * store the coordinates of the point(s). | ||
| 80 | * Each point is stored as a pair of float x,y coordinates. | ||
| 81 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 82 | * SEG_QUADTO will return two points, | ||
| 83 | * SEG_CUBICTO will return 3 points | ||
| 84 | * and SEG_CLOSE will not return any points. | ||
| 85 | * @see #SEG_MOVETO | ||
| 86 | * @see #SEG_LINETO | ||
| 87 | * @see #SEG_QUADTO | ||
| 88 | * @see #SEG_CUBICTO | ||
| 89 | * @see #SEG_CLOSE | ||
| 90 | */ | ||
| 91 | public int currentSegment(float[] coords) { | ||
| 92 | if (isDone()) { | ||
| 93 | throw new NoSuchElementException("quad iterator iterator out of bounds"); | ||
| 94 | } | ||
| 95 | int type; | ||
| 96 | if (index == 0) { | ||
| 97 | coords[0] = (float) quad.getX1(); | ||
| 98 | coords[1] = (float) quad.getY1(); | ||
| 99 | type = SEG_MOVETO; | ||
| 100 | } else { | ||
| 101 | coords[0] = (float) quad.getCtrlX(); | ||
| 102 | coords[1] = (float) quad.getCtrlY(); | ||
| 103 | coords[2] = (float) quad.getX2(); | ||
| 104 | coords[3] = (float) quad.getY2(); | ||
| 105 | type = SEG_QUADTO; | ||
| 106 | } | ||
| 107 | if (affine != null) { | ||
| 108 | affine.transform(coords, 0, coords, 0, index == 0 ? 1 : 2); | ||
| 109 | } | ||
| 110 | return type; | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Returns the coordinates and type of the current path segment in | ||
| 115 | * the iteration. | ||
| 116 | * The return value is the path segment type: | ||
| 117 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 118 | * A double array of length 6 must be passed in and may be used to | ||
| 119 | * store the coordinates of the point(s). | ||
| 120 | * Each point is stored as a pair of double x,y coordinates. | ||
| 121 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 122 | * SEG_QUADTO will return two points, | ||
| 123 | * SEG_CUBICTO will return 3 points | ||
| 124 | * and SEG_CLOSE will not return any points. | ||
| 125 | * @see #SEG_MOVETO | ||
| 126 | * @see #SEG_LINETO | ||
| 127 | * @see #SEG_QUADTO | ||
| 128 | * @see #SEG_CUBICTO | ||
| 129 | * @see #SEG_CLOSE | ||
| 130 | */ | ||
| 131 | public int currentSegment(double[] coords) { | ||
| 132 | if (isDone()) { | ||
| 133 | throw new NoSuchElementException("quad iterator iterator out of bounds"); | ||
| 134 | } | ||
| 135 | int type; | ||
| 136 | if (index == 0) { | ||
| 137 | coords[0] = quad.getX1(); | ||
| 138 | coords[1] = quad.getY1(); | ||
| 139 | type = SEG_MOVETO; | ||
| 140 | } else { | ||
| 141 | coords[0] = quad.getCtrlX(); | ||
| 142 | coords[1] = quad.getCtrlY(); | ||
| 143 | coords[2] = quad.getX2(); | ||
| 144 | coords[3] = quad.getY2(); | ||
| 145 | type = SEG_QUADTO; | ||
| 146 | } | ||
| 147 | if (affine != null) { | ||
| 148 | affine.transform(coords, 0, coords, 0, index == 0 ? 1 : 2); | ||
| 149 | } | ||
| 150 | return type; | ||
| 151 | } | ||
| 152 | } | ||
Binary file not shown.
| @@ -0,0 +1,156 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A utility class to iterate over the path segments of a rectangle | ||
| 32 | * through the PathIterator interface. | ||
| 33 | * | ||
| 34 | * @author Jim Graham | ||
| 35 | */ | ||
| 36 | class RectIterator implements PathIterator { | ||
| 37 | double x, y, w, h; | ||
| 38 | AffineTransform affine; | ||
| 39 | int index; | ||
| 40 | |||
| 41 | RectIterator(Rectangle2D r, AffineTransform at) { | ||
| 42 | this.x = r.getX(); | ||
| 43 | this.y = r.getY(); | ||
| 44 | this.w = r.getWidth(); | ||
| 45 | this.h = r.getHeight(); | ||
| 46 | this.affine = at; | ||
| 47 | if (w < 0 || h < 0) { | ||
| 48 | index = 6; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Return the winding rule for determining the insideness of the | ||
| 54 | * path. | ||
| 55 | * @see #WIND_EVEN_ODD | ||
| 56 | * @see #WIND_NON_ZERO | ||
| 57 | */ | ||
| 58 | public int getWindingRule() { | ||
| 59 | return WIND_NON_ZERO; | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Tests if there are more points to read. | ||
| 64 | * @return true if there are more points to read | ||
| 65 | */ | ||
| 66 | public boolean isDone() { | ||
| 67 | return index > 5; | ||
| 68 | } | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Moves the iterator to the next segment of the path forwards | ||
| 72 | * along the primary direction of traversal as long as there are | ||
| 73 | * more points in that direction. | ||
| 74 | */ | ||
| 75 | public void next() { | ||
| 76 | index++; | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Returns the coordinates and type of the current path segment in | ||
| 81 | * the iteration. | ||
| 82 | * The return value is the path segment type: | ||
| 83 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 84 | * A float array of length 6 must be passed in and may be used to | ||
| 85 | * store the coordinates of the point(s). | ||
| 86 | * Each point is stored as a pair of float x,y coordinates. | ||
| 87 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 88 | * SEG_QUADTO will return two points, | ||
| 89 | * SEG_CUBICTO will return 3 points | ||
| 90 | * and SEG_CLOSE will not return any points. | ||
| 91 | * @see #SEG_MOVETO | ||
| 92 | * @see #SEG_LINETO | ||
| 93 | * @see #SEG_QUADTO | ||
| 94 | * @see #SEG_CUBICTO | ||
| 95 | * @see #SEG_CLOSE | ||
| 96 | */ | ||
| 97 | public int currentSegment(float[] coords) { | ||
| 98 | if (isDone()) { | ||
| 99 | throw new NoSuchElementException("rect iterator out of bounds"); | ||
| 100 | } | ||
| 101 | if (index == 5) { | ||
| 102 | return SEG_CLOSE; | ||
| 103 | } | ||
| 104 | coords[0] = (float) x; | ||
| 105 | coords[1] = (float) y; | ||
| 106 | if (index == 1 || index == 2) { | ||
| 107 | coords[0] += (float) w; | ||
| 108 | } | ||
| 109 | if (index == 2 || index == 3) { | ||
| 110 | coords[1] += (float) h; | ||
| 111 | } | ||
| 112 | if (affine != null) { | ||
| 113 | affine.transform(coords, 0, coords, 0, 1); | ||
| 114 | } | ||
| 115 | return (index == 0 ? SEG_MOVETO : SEG_LINETO); | ||
| 116 | } | ||
| 117 | |||
| 118 | /** | ||
| 119 | * Returns the coordinates and type of the current path segment in | ||
| 120 | * the iteration. | ||
| 121 | * The return value is the path segment type: | ||
| 122 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 123 | * A double array of length 6 must be passed in and may be used to | ||
| 124 | * store the coordinates of the point(s). | ||
| 125 | * Each point is stored as a pair of double x,y coordinates. | ||
| 126 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 127 | * SEG_QUADTO will return two points, | ||
| 128 | * SEG_CUBICTO will return 3 points | ||
| 129 | * and SEG_CLOSE will not return any points. | ||
| 130 | * @see #SEG_MOVETO | ||
| 131 | * @see #SEG_LINETO | ||
| 132 | * @see #SEG_QUADTO | ||
| 133 | * @see #SEG_CUBICTO | ||
| 134 | * @see #SEG_CLOSE | ||
| 135 | */ | ||
| 136 | public int currentSegment(double[] coords) { | ||
| 137 | if (isDone()) { | ||
| 138 | throw new NoSuchElementException("rect iterator out of bounds"); | ||
| 139 | } | ||
| 140 | if (index == 5) { | ||
| 141 | return SEG_CLOSE; | ||
| 142 | } | ||
| 143 | coords[0] = x; | ||
| 144 | coords[1] = y; | ||
| 145 | if (index == 1 || index == 2) { | ||
| 146 | coords[0] += w; | ||
| 147 | } | ||
| 148 | if (index == 2 || index == 3) { | ||
| 149 | coords[1] += h; | ||
| 150 | } | ||
| 151 | if (affine != null) { | ||
| 152 | affine.transform(coords, 0, coords, 0, 1); | ||
| 153 | } | ||
| 154 | return (index == 0 ? SEG_MOVETO : SEG_LINETO); | ||
| 155 | } | ||
| 156 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,930 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.io.Serializable; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * The <code>Rectangle2D</code> class describes a rectangle | ||
| 32 | * defined by a location {@code (x,y)} and dimension | ||
| 33 | * {@code (w x h)}. | ||
| 34 | * <p> | ||
| 35 | * This class is only the abstract superclass for all objects that | ||
| 36 | * store a 2D rectangle. | ||
| 37 | * The actual storage representation of the coordinates is left to | ||
| 38 | * the subclass. | ||
| 39 | * | ||
| 40 | * @author Jim Graham | ||
| 41 | * @since 1.2 | ||
| 42 | */ | ||
| 43 | public abstract class Rectangle2D extends RectangularShape { | ||
| 44 | /** | ||
| 45 | * The bitmask that indicates that a point lies to the left of | ||
| 46 | * this <code>Rectangle2D</code>. | ||
| 47 | * @since 1.2 | ||
| 48 | */ | ||
| 49 | public static final int OUT_LEFT = 1; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * The bitmask that indicates that a point lies above | ||
| 53 | * this <code>Rectangle2D</code>. | ||
| 54 | * @since 1.2 | ||
| 55 | */ | ||
| 56 | public static final int OUT_TOP = 2; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * The bitmask that indicates that a point lies to the right of | ||
| 60 | * this <code>Rectangle2D</code>. | ||
| 61 | * @since 1.2 | ||
| 62 | */ | ||
| 63 | public static final int OUT_RIGHT = 4; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * The bitmask that indicates that a point lies below | ||
| 67 | * this <code>Rectangle2D</code>. | ||
| 68 | * @since 1.2 | ||
| 69 | */ | ||
| 70 | public static final int OUT_BOTTOM = 8; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * The <code>Float</code> class defines a rectangle specified in float | ||
| 74 | * coordinates. | ||
| 75 | * @since 1.2 | ||
| 76 | */ | ||
| 77 | public static class Float extends Rectangle2D implements Serializable { | ||
| 78 | /** | ||
| 79 | * The X coordinate of this <code>Rectangle2D</code>. | ||
| 80 | * @since 1.2 | ||
| 81 | * @serial | ||
| 82 | */ | ||
| 83 | public float x; | ||
| 84 | |||
| 85 | /** | ||
| 86 | * The Y coordinate of this <code>Rectangle2D</code>. | ||
| 87 | * @since 1.2 | ||
| 88 | * @serial | ||
| 89 | */ | ||
| 90 | public float y; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * The width of this <code>Rectangle2D</code>. | ||
| 94 | * @since 1.2 | ||
| 95 | * @serial | ||
| 96 | */ | ||
| 97 | public float width; | ||
| 98 | |||
| 99 | /** | ||
| 100 | * The height of this <code>Rectangle2D</code>. | ||
| 101 | * @since 1.2 | ||
| 102 | * @serial | ||
| 103 | */ | ||
| 104 | public float height; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Constructs a new <code>Rectangle2D</code>, initialized to | ||
| 108 | * location (0.0, 0.0) and size (0.0, 0.0). | ||
| 109 | * @since 1.2 | ||
| 110 | */ | ||
| 111 | public Float() { | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Constructs and initializes a <code>Rectangle2D</code> | ||
| 116 | * from the specified <code>float</code> coordinates. | ||
| 117 | * | ||
| 118 | * @param x the X coordinate of the upper-left corner | ||
| 119 | * of the newly constructed <code>Rectangle2D</code> | ||
| 120 | * @param y the Y coordinate of the upper-left corner | ||
| 121 | * of the newly constructed <code>Rectangle2D</code> | ||
| 122 | * @param w the width of the newly constructed | ||
| 123 | * <code>Rectangle2D</code> | ||
| 124 | * @param h the height of the newly constructed | ||
| 125 | * <code>Rectangle2D</code> | ||
| 126 | * @since 1.2 | ||
| 127 | */ | ||
| 128 | public Float(float x, float y, float w, float h) { | ||
| 129 | setRect(x, y, w, h); | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * {@inheritDoc} | ||
| 134 | * @since 1.2 | ||
| 135 | */ | ||
| 136 | public double getX() { | ||
| 137 | return (double) x; | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * {@inheritDoc} | ||
| 142 | * @since 1.2 | ||
| 143 | */ | ||
| 144 | public double getY() { | ||
| 145 | return (double) y; | ||
| 146 | } | ||
| 147 | |||
| 148 | /** | ||
| 149 | * {@inheritDoc} | ||
| 150 | * @since 1.2 | ||
| 151 | */ | ||
| 152 | public double getWidth() { | ||
| 153 | return (double) width; | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * {@inheritDoc} | ||
| 158 | * @since 1.2 | ||
| 159 | */ | ||
| 160 | public double getHeight() { | ||
| 161 | return (double) height; | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * {@inheritDoc} | ||
| 166 | * @since 1.2 | ||
| 167 | */ | ||
| 168 | public boolean isEmpty() { | ||
| 169 | return (width <= 0.0f) || (height <= 0.0f); | ||
| 170 | } | ||
| 171 | |||
| 172 | /** | ||
| 173 | * Sets the location and size of this <code>Rectangle2D</code> | ||
| 174 | * to the specified <code>float</code> values. | ||
| 175 | * | ||
| 176 | * @param x the X coordinate of the upper-left corner | ||
| 177 | * of this <code>Rectangle2D</code> | ||
| 178 | * @param y the Y coordinate of the upper-left corner | ||
| 179 | * of this <code>Rectangle2D</code> | ||
| 180 | * @param w the width of this <code>Rectangle2D</code> | ||
| 181 | * @param h the height of this <code>Rectangle2D</code> | ||
| 182 | * @since 1.2 | ||
| 183 | */ | ||
| 184 | public void setRect(float x, float y, float w, float h) { | ||
| 185 | this.x = x; | ||
| 186 | this.y = y; | ||
| 187 | this.width = w; | ||
| 188 | this.height = h; | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * {@inheritDoc} | ||
| 193 | * @since 1.2 | ||
| 194 | */ | ||
| 195 | public void setRect(double x, double y, double w, double h) { | ||
| 196 | this.x = (float) x; | ||
| 197 | this.y = (float) y; | ||
| 198 | this.width = (float) w; | ||
| 199 | this.height = (float) h; | ||
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * {@inheritDoc} | ||
| 204 | * @since 1.2 | ||
| 205 | */ | ||
| 206 | public void setRect(Rectangle2D r) { | ||
| 207 | this.x = (float) r.getX(); | ||
| 208 | this.y = (float) r.getY(); | ||
| 209 | this.width = (float) r.getWidth(); | ||
| 210 | this.height = (float) r.getHeight(); | ||
| 211 | } | ||
| 212 | |||
| 213 | /** | ||
| 214 | * {@inheritDoc} | ||
| 215 | * @since 1.2 | ||
| 216 | */ | ||
| 217 | public int outcode(double x, double y) { | ||
| 218 | /* | ||
| 219 | * Note on casts to double below. If the arithmetic of | ||
| 220 | * x+w or y+h is done in float, then some bits may be | ||
| 221 | * lost if the binary exponents of x/y and w/h are not | ||
| 222 | * similar. By converting to double before the addition | ||
| 223 | * we force the addition to be carried out in double to | ||
| 224 | * avoid rounding error in the comparison. | ||
| 225 | * | ||
| 226 | * See bug 4320890 for problems that this inaccuracy causes. | ||
| 227 | */ | ||
| 228 | int out = 0; | ||
| 229 | if (this.width <= 0) { | ||
| 230 | out |= OUT_LEFT | OUT_RIGHT; | ||
| 231 | } else if (x < this.x) { | ||
| 232 | out |= OUT_LEFT; | ||
| 233 | } else if (x > this.x + (double) this.width) { | ||
| 234 | out |= OUT_RIGHT; | ||
| 235 | } | ||
| 236 | if (this.height <= 0) { | ||
| 237 | out |= OUT_TOP | OUT_BOTTOM; | ||
| 238 | } else if (y < this.y) { | ||
| 239 | out |= OUT_TOP; | ||
| 240 | } else if (y > this.y + (double) this.height) { | ||
| 241 | out |= OUT_BOTTOM; | ||
| 242 | } | ||
| 243 | return out; | ||
| 244 | } | ||
| 245 | |||
| 246 | /** | ||
| 247 | * {@inheritDoc} | ||
| 248 | * @since 1.2 | ||
| 249 | */ | ||
| 250 | public Rectangle2D getBounds2D() { | ||
| 251 | return new Float(x, y, width, height); | ||
| 252 | } | ||
| 253 | |||
| 254 | /** | ||
| 255 | * {@inheritDoc} | ||
| 256 | * @since 1.2 | ||
| 257 | */ | ||
| 258 | public Rectangle2D createIntersection(Rectangle2D r) { | ||
| 259 | Rectangle2D dest; | ||
| 260 | if (r instanceof Float) { | ||
| 261 | dest = new Rectangle2D.Float(); | ||
| 262 | } else { | ||
| 263 | dest = new Rectangle2D.Double(); | ||
| 264 | } | ||
| 265 | Rectangle2D.intersect(this, r, dest); | ||
| 266 | return dest; | ||
| 267 | } | ||
| 268 | |||
| 269 | /** | ||
| 270 | * {@inheritDoc} | ||
| 271 | * @since 1.2 | ||
| 272 | */ | ||
| 273 | public Rectangle2D createUnion(Rectangle2D r) { | ||
| 274 | Rectangle2D dest; | ||
| 275 | if (r instanceof Float) { | ||
| 276 | dest = new Rectangle2D.Float(); | ||
| 277 | } else { | ||
| 278 | dest = new Rectangle2D.Double(); | ||
| 279 | } | ||
| 280 | Rectangle2D.union(this, r, dest); | ||
| 281 | return dest; | ||
| 282 | } | ||
| 283 | |||
| 284 | /** | ||
| 285 | * Returns the <code>String</code> representation of this | ||
| 286 | * <code>Rectangle2D</code>. | ||
| 287 | * @return a <code>String</code> representing this | ||
| 288 | * <code>Rectangle2D</code>. | ||
| 289 | * @since 1.2 | ||
| 290 | */ | ||
| 291 | public String toString() { | ||
| 292 | return getClass().getName() | ||
| 293 | + "[x=" + x + | ||
| 294 | ",y=" + y + | ||
| 295 | ",w=" + width + | ||
| 296 | ",h=" + height + "]"; | ||
| 297 | } | ||
| 298 | |||
| 299 | /* | ||
| 300 | * JDK 1.6 serialVersionUID | ||
| 301 | */ | ||
| 302 | private static final long serialVersionUID = 3798716824173675777L; | ||
| 303 | } | ||
| 304 | |||
| 305 | /** | ||
| 306 | * The <code>Double</code> class defines a rectangle specified in | ||
| 307 | * double coordinates. | ||
| 308 | * @since 1.2 | ||
| 309 | */ | ||
| 310 | public static class Double extends Rectangle2D implements Serializable { | ||
| 311 | /** | ||
| 312 | * The X coordinate of this <code>Rectangle2D</code>. | ||
| 313 | * @since 1.2 | ||
| 314 | * @serial | ||
| 315 | */ | ||
| 316 | public double x; | ||
| 317 | |||
| 318 | /** | ||
| 319 | * The Y coordinate of this <code>Rectangle2D</code>. | ||
| 320 | * @since 1.2 | ||
| 321 | * @serial | ||
| 322 | */ | ||
| 323 | public double y; | ||
| 324 | |||
| 325 | /** | ||
| 326 | * The width of this <code>Rectangle2D</code>. | ||
| 327 | * @since 1.2 | ||
| 328 | * @serial | ||
| 329 | */ | ||
| 330 | public double width; | ||
| 331 | |||
| 332 | /** | ||
| 333 | * The height of this <code>Rectangle2D</code>. | ||
| 334 | * @since 1.2 | ||
| 335 | * @serial | ||
| 336 | */ | ||
| 337 | public double height; | ||
| 338 | |||
| 339 | /** | ||
| 340 | * Constructs a new <code>Rectangle2D</code>, initialized to | ||
| 341 | * location (0, 0) and size (0, 0). | ||
| 342 | * @since 1.2 | ||
| 343 | */ | ||
| 344 | public Double() { | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * Constructs and initializes a <code>Rectangle2D</code> | ||
| 349 | * from the specified <code>double</code> coordinates. | ||
| 350 | * | ||
| 351 | * @param x the X coordinate of the upper-left corner | ||
| 352 | * of the newly constructed <code>Rectangle2D</code> | ||
| 353 | * @param y the Y coordinate of the upper-left corner | ||
| 354 | * of the newly constructed <code>Rectangle2D</code> | ||
| 355 | * @param w the width of the newly constructed | ||
| 356 | * <code>Rectangle2D</code> | ||
| 357 | * @param h the height of the newly constructed | ||
| 358 | * <code>Rectangle2D</code> | ||
| 359 | * @since 1.2 | ||
| 360 | */ | ||
| 361 | public Double(double x, double y, double w, double h) { | ||
| 362 | setRect(x, y, w, h); | ||
| 363 | } | ||
| 364 | |||
| 365 | /** | ||
| 366 | * {@inheritDoc} | ||
| 367 | * @since 1.2 | ||
| 368 | */ | ||
| 369 | public double getX() { | ||
| 370 | return x; | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * {@inheritDoc} | ||
| 375 | * @since 1.2 | ||
| 376 | */ | ||
| 377 | public double getY() { | ||
| 378 | return y; | ||
| 379 | } | ||
| 380 | |||
| 381 | /** | ||
| 382 | * {@inheritDoc} | ||
| 383 | * @since 1.2 | ||
| 384 | */ | ||
| 385 | public double getWidth() { | ||
| 386 | return width; | ||
| 387 | } | ||
| 388 | |||
| 389 | /** | ||
| 390 | * {@inheritDoc} | ||
| 391 | * @since 1.2 | ||
| 392 | */ | ||
| 393 | public double getHeight() { | ||
| 394 | return height; | ||
| 395 | } | ||
| 396 | |||
| 397 | /** | ||
| 398 | * {@inheritDoc} | ||
| 399 | * @since 1.2 | ||
| 400 | */ | ||
| 401 | public boolean isEmpty() { | ||
| 402 | return (width <= 0.0) || (height <= 0.0); | ||
| 403 | } | ||
| 404 | |||
| 405 | /** | ||
| 406 | * {@inheritDoc} | ||
| 407 | * @since 1.2 | ||
| 408 | */ | ||
| 409 | public void setRect(double x, double y, double w, double h) { | ||
| 410 | this.x = x; | ||
| 411 | this.y = y; | ||
| 412 | this.width = w; | ||
| 413 | this.height = h; | ||
| 414 | } | ||
| 415 | |||
| 416 | /** | ||
| 417 | * {@inheritDoc} | ||
| 418 | * @since 1.2 | ||
| 419 | */ | ||
| 420 | public void setRect(Rectangle2D r) { | ||
| 421 | this.x = r.getX(); | ||
| 422 | this.y = r.getY(); | ||
| 423 | this.width = r.getWidth(); | ||
| 424 | this.height = r.getHeight(); | ||
| 425 | } | ||
| 426 | |||
| 427 | /** | ||
| 428 | * {@inheritDoc} | ||
| 429 | * @since 1.2 | ||
| 430 | */ | ||
| 431 | public int outcode(double x, double y) { | ||
| 432 | int out = 0; | ||
| 433 | if (this.width <= 0) { | ||
| 434 | out |= OUT_LEFT | OUT_RIGHT; | ||
| 435 | } else if (x < this.x) { | ||
| 436 | out |= OUT_LEFT; | ||
| 437 | } else if (x > this.x + this.width) { | ||
| 438 | out |= OUT_RIGHT; | ||
| 439 | } | ||
| 440 | if (this.height <= 0) { | ||
| 441 | out |= OUT_TOP | OUT_BOTTOM; | ||
| 442 | } else if (y < this.y) { | ||
| 443 | out |= OUT_TOP; | ||
| 444 | } else if (y > this.y + this.height) { | ||
| 445 | out |= OUT_BOTTOM; | ||
| 446 | } | ||
| 447 | return out; | ||
| 448 | } | ||
| 449 | |||
| 450 | /** | ||
| 451 | * {@inheritDoc} | ||
| 452 | * @since 1.2 | ||
| 453 | */ | ||
| 454 | public Rectangle2D getBounds2D() { | ||
| 455 | return new Double(x, y, width, height); | ||
| 456 | } | ||
| 457 | |||
| 458 | /** | ||
| 459 | * {@inheritDoc} | ||
| 460 | * @since 1.2 | ||
| 461 | */ | ||
| 462 | public Rectangle2D createIntersection(Rectangle2D r) { | ||
| 463 | Rectangle2D dest = new Rectangle2D.Double(); | ||
| 464 | Rectangle2D.intersect(this, r, dest); | ||
| 465 | return dest; | ||
| 466 | } | ||
| 467 | |||
| 468 | /** | ||
| 469 | * {@inheritDoc} | ||
| 470 | * @since 1.2 | ||
| 471 | */ | ||
| 472 | public Rectangle2D createUnion(Rectangle2D r) { | ||
| 473 | Rectangle2D dest = new Rectangle2D.Double(); | ||
| 474 | Rectangle2D.union(this, r, dest); | ||
| 475 | return dest; | ||
| 476 | } | ||
| 477 | |||
| 478 | /** | ||
| 479 | * Returns the <code>String</code> representation of this | ||
| 480 | * <code>Rectangle2D</code>. | ||
| 481 | * @return a <code>String</code> representing this | ||
| 482 | * <code>Rectangle2D</code>. | ||
| 483 | * @since 1.2 | ||
| 484 | */ | ||
| 485 | public String toString() { | ||
| 486 | return getClass().getName() | ||
| 487 | + "[x=" + x + | ||
| 488 | ",y=" + y + | ||
| 489 | ",w=" + width + | ||
| 490 | ",h=" + height + "]"; | ||
| 491 | } | ||
| 492 | |||
| 493 | /* | ||
| 494 | * JDK 1.6 serialVersionUID | ||
| 495 | */ | ||
| 496 | private static final long serialVersionUID = 7771313791441850493L; | ||
| 497 | } | ||
| 498 | |||
| 499 | /** | ||
| 500 | * This is an abstract class that cannot be instantiated directly. | ||
| 501 | * Type-specific implementation subclasses are available for | ||
| 502 | * instantiation and provide a number of formats for storing | ||
| 503 | * the information necessary to satisfy the various accessor | ||
| 504 | * methods below. | ||
| 505 | * | ||
| 506 | * @see java.awt.geom.Rectangle2D.Float | ||
| 507 | * @see java.awt.geom.Rectangle2D.Double | ||
| 508 | * @see java.awt.Rectangle | ||
| 509 | * @since 1.2 | ||
| 510 | */ | ||
| 511 | protected Rectangle2D() { | ||
| 512 | } | ||
| 513 | |||
| 514 | /** | ||
| 515 | * Sets the location and size of this <code>Rectangle2D</code> | ||
| 516 | * to the specified <code>double</code> values. | ||
| 517 | * | ||
| 518 | * @param x the X coordinate of the upper-left corner | ||
| 519 | * of this <code>Rectangle2D</code> | ||
| 520 | * @param y the Y coordinate of the upper-left corner | ||
| 521 | * of this <code>Rectangle2D</code> | ||
| 522 | * @param w the width of this <code>Rectangle2D</code> | ||
| 523 | * @param h the height of this <code>Rectangle2D</code> | ||
| 524 | * @since 1.2 | ||
| 525 | */ | ||
| 526 | public abstract void setRect(double x, double y, double w, double h); | ||
| 527 | |||
| 528 | /** | ||
| 529 | * Sets this <code>Rectangle2D</code> to be the same as the specified | ||
| 530 | * <code>Rectangle2D</code>. | ||
| 531 | * @param r the specified <code>Rectangle2D</code> | ||
| 532 | * @since 1.2 | ||
| 533 | */ | ||
| 534 | public void setRect(Rectangle2D r) { | ||
| 535 | setRect(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 536 | } | ||
| 537 | |||
| 538 | /** | ||
| 539 | * Tests if the specified line segment intersects the interior of this | ||
| 540 | * <code>Rectangle2D</code>. | ||
| 541 | * | ||
| 542 | * @param x1 the X coordinate of the start point of the specified | ||
| 543 | * line segment | ||
| 544 | * @param y1 the Y coordinate of the start point of the specified | ||
| 545 | * line segment | ||
| 546 | * @param x2 the X coordinate of the end point of the specified | ||
| 547 | * line segment | ||
| 548 | * @param y2 the Y coordinate of the end point of the specified | ||
| 549 | * line segment | ||
| 550 | * @return <code>true</code> if the specified line segment intersects | ||
| 551 | * the interior of this <code>Rectangle2D</code>; <code>false</code> | ||
| 552 | * otherwise. | ||
| 553 | * @since 1.2 | ||
| 554 | */ | ||
| 555 | public boolean intersectsLine(double x1, double y1, double x2, double y2) { | ||
| 556 | int out1, out2; | ||
| 557 | if ((out2 = outcode(x2, y2)) == 0) { | ||
| 558 | return true; | ||
| 559 | } | ||
| 560 | while ((out1 = outcode(x1, y1)) != 0) { | ||
| 561 | if ((out1 & out2) != 0) { | ||
| 562 | return false; | ||
| 563 | } | ||
| 564 | if ((out1 & (OUT_LEFT | OUT_RIGHT)) != 0) { | ||
| 565 | double x = getX(); | ||
| 566 | if ((out1 & OUT_RIGHT) != 0) { | ||
| 567 | x += getWidth(); | ||
| 568 | } | ||
| 569 | y1 = y1 + (x - x1) * (y2 - y1) / (x2 - x1); | ||
| 570 | x1 = x; | ||
| 571 | } else { | ||
| 572 | double y = getY(); | ||
| 573 | if ((out1 & OUT_BOTTOM) != 0) { | ||
| 574 | y += getHeight(); | ||
| 575 | } | ||
| 576 | x1 = x1 + (y - y1) * (x2 - x1) / (y2 - y1); | ||
| 577 | y1 = y; | ||
| 578 | } | ||
| 579 | } | ||
| 580 | return true; | ||
| 581 | } | ||
| 582 | |||
| 583 | /** | ||
| 584 | * Tests if the specified line segment intersects the interior of this | ||
| 585 | * <code>Rectangle2D</code>. | ||
| 586 | * @param l the specified {@link Line2D} to test for intersection | ||
| 587 | * with the interior of this <code>Rectangle2D</code> | ||
| 588 | * @return <code>true</code> if the specified <code>Line2D</code> | ||
| 589 | * intersects the interior of this <code>Rectangle2D</code>; | ||
| 590 | * <code>false</code> otherwise. | ||
| 591 | * @since 1.2 | ||
| 592 | */ | ||
| 593 | public boolean intersectsLine(Line2D l) { | ||
| 594 | return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); | ||
| 595 | } | ||
| 596 | |||
| 597 | /** | ||
| 598 | * Determines where the specified coordinates lie with respect | ||
| 599 | * to this <code>Rectangle2D</code>. | ||
| 600 | * This method computes a binary OR of the appropriate mask values | ||
| 601 | * indicating, for each side of this <code>Rectangle2D</code>, | ||
| 602 | * whether or not the specified coordinates are on the same side | ||
| 603 | * of the edge as the rest of this <code>Rectangle2D</code>. | ||
| 604 | * @param x the specified X coordinate | ||
| 605 | * @param y the specified Y coordinate | ||
| 606 | * @return the logical OR of all appropriate out codes. | ||
| 607 | * @see #OUT_LEFT | ||
| 608 | * @see #OUT_TOP | ||
| 609 | * @see #OUT_RIGHT | ||
| 610 | * @see #OUT_BOTTOM | ||
| 611 | * @since 1.2 | ||
| 612 | */ | ||
| 613 | public abstract int outcode(double x, double y); | ||
| 614 | |||
| 615 | /** | ||
| 616 | * Determines where the specified {@link Point2D} lies with | ||
| 617 | * respect to this <code>Rectangle2D</code>. | ||
| 618 | * This method computes a binary OR of the appropriate mask values | ||
| 619 | * indicating, for each side of this <code>Rectangle2D</code>, | ||
| 620 | * whether or not the specified <code>Point2D</code> is on the same | ||
| 621 | * side of the edge as the rest of this <code>Rectangle2D</code>. | ||
| 622 | * @param p the specified <code>Point2D</code> | ||
| 623 | * @return the logical OR of all appropriate out codes. | ||
| 624 | * @see #OUT_LEFT | ||
| 625 | * @see #OUT_TOP | ||
| 626 | * @see #OUT_RIGHT | ||
| 627 | * @see #OUT_BOTTOM | ||
| 628 | * @since 1.2 | ||
| 629 | */ | ||
| 630 | public int outcode(Point2D p) { | ||
| 631 | return outcode(p.getX(), p.getY()); | ||
| 632 | } | ||
| 633 | |||
| 634 | /** | ||
| 635 | * Sets the location and size of the outer bounds of this | ||
| 636 | * <code>Rectangle2D</code> to the specified rectangular values. | ||
| 637 | * | ||
| 638 | * @param x the X coordinate of the upper-left corner | ||
| 639 | * of this <code>Rectangle2D</code> | ||
| 640 | * @param y the Y coordinate of the upper-left corner | ||
| 641 | * of this <code>Rectangle2D</code> | ||
| 642 | * @param w the width of this <code>Rectangle2D</code> | ||
| 643 | * @param h the height of this <code>Rectangle2D</code> | ||
| 644 | * @since 1.2 | ||
| 645 | */ | ||
| 646 | public void setFrame(double x, double y, double w, double h) { | ||
| 647 | setRect(x, y, w, h); | ||
| 648 | } | ||
| 649 | |||
| 650 | /** | ||
| 651 | * {@inheritDoc} | ||
| 652 | * @since 1.2 | ||
| 653 | */ | ||
| 654 | public Rectangle2D getBounds2D() { | ||
| 655 | return (Rectangle2D) clone(); | ||
| 656 | } | ||
| 657 | |||
| 658 | /** | ||
| 659 | * {@inheritDoc} | ||
| 660 | * @since 1.2 | ||
| 661 | */ | ||
| 662 | public boolean contains(double x, double y) { | ||
| 663 | double x0 = getX(); | ||
| 664 | double y0 = getY(); | ||
| 665 | return (x >= x0 && | ||
| 666 | y >= y0 && | ||
| 667 | x < x0 + getWidth() && | ||
| 668 | y < y0 + getHeight()); | ||
| 669 | } | ||
| 670 | |||
| 671 | /** | ||
| 672 | * {@inheritDoc} | ||
| 673 | * @since 1.2 | ||
| 674 | */ | ||
| 675 | public boolean intersects(double x, double y, double w, double h) { | ||
| 676 | if (isEmpty() || w <= 0 || h <= 0) { | ||
| 677 | return false; | ||
| 678 | } | ||
| 679 | double x0 = getX(); | ||
| 680 | double y0 = getY(); | ||
| 681 | return (x + w > x0 && | ||
| 682 | y + h > y0 && | ||
| 683 | x < x0 + getWidth() && | ||
| 684 | y < y0 + getHeight()); | ||
| 685 | } | ||
| 686 | |||
| 687 | /** | ||
| 688 | * {@inheritDoc} | ||
| 689 | * @since 1.2 | ||
| 690 | */ | ||
| 691 | public boolean contains(double x, double y, double w, double h) { | ||
| 692 | if (isEmpty() || w <= 0 || h <= 0) { | ||
| 693 | return false; | ||
| 694 | } | ||
| 695 | double x0 = getX(); | ||
| 696 | double y0 = getY(); | ||
| 697 | return (x >= x0 && | ||
| 698 | y >= y0 && | ||
| 699 | (x + w) <= x0 + getWidth() && | ||
| 700 | (y + h) <= y0 + getHeight()); | ||
| 701 | } | ||
| 702 | |||
| 703 | /** | ||
| 704 | * Returns a new <code>Rectangle2D</code> object representing the | ||
| 705 | * intersection of this <code>Rectangle2D</code> with the specified | ||
| 706 | * <code>Rectangle2D</code>. | ||
| 707 | * @param r the <code>Rectangle2D</code> to be intersected with | ||
| 708 | * this <code>Rectangle2D</code> | ||
| 709 | * @return the largest <code>Rectangle2D</code> contained in both | ||
| 710 | * the specified <code>Rectangle2D</code> and in this | ||
| 711 | * <code>Rectangle2D</code>. | ||
| 712 | * @since 1.2 | ||
| 713 | */ | ||
| 714 | public abstract Rectangle2D createIntersection(Rectangle2D r); | ||
| 715 | |||
| 716 | /** | ||
| 717 | * Intersects the pair of specified source <code>Rectangle2D</code> | ||
| 718 | * objects and puts the result into the specified destination | ||
| 719 | * <code>Rectangle2D</code> object. One of the source rectangles | ||
| 720 | * can also be the destination to avoid creating a third Rectangle2D | ||
| 721 | * object, but in this case the original points of this source | ||
| 722 | * rectangle will be overwritten by this method. | ||
| 723 | * @param src1 the first of a pair of <code>Rectangle2D</code> | ||
| 724 | * objects to be intersected with each other | ||
| 725 | * @param src2 the second of a pair of <code>Rectangle2D</code> | ||
| 726 | * objects to be intersected with each other | ||
| 727 | * @param dest the <code>Rectangle2D</code> that holds the | ||
| 728 | * results of the intersection of <code>src1</code> and | ||
| 729 | * <code>src2</code> | ||
| 730 | * @since 1.2 | ||
| 731 | */ | ||
| 732 | public static void intersect(Rectangle2D src1, | ||
| 733 | Rectangle2D src2, | ||
| 734 | Rectangle2D dest) { | ||
| 735 | double x1 = Math.max(src1.getMinX(), src2.getMinX()); | ||
| 736 | double y1 = Math.max(src1.getMinY(), src2.getMinY()); | ||
| 737 | double x2 = Math.min(src1.getMaxX(), src2.getMaxX()); | ||
| 738 | double y2 = Math.min(src1.getMaxY(), src2.getMaxY()); | ||
| 739 | dest.setFrame(x1, y1, x2-x1, y2-y1); | ||
| 740 | } | ||
| 741 | |||
| 742 | /** | ||
| 743 | * Returns a new <code>Rectangle2D</code> object representing the | ||
| 744 | * union of this <code>Rectangle2D</code> with the specified | ||
| 745 | * <code>Rectangle2D</code>. | ||
| 746 | * @param r the <code>Rectangle2D</code> to be combined with | ||
| 747 | * this <code>Rectangle2D</code> | ||
| 748 | * @return the smallest <code>Rectangle2D</code> containing both | ||
| 749 | * the specified <code>Rectangle2D</code> and this | ||
| 750 | * <code>Rectangle2D</code>. | ||
| 751 | * @since 1.2 | ||
| 752 | */ | ||
| 753 | public abstract Rectangle2D createUnion(Rectangle2D r); | ||
| 754 | |||
| 755 | /** | ||
| 756 | * Unions the pair of source <code>Rectangle2D</code> objects | ||
| 757 | * and puts the result into the specified destination | ||
| 758 | * <code>Rectangle2D</code> object. One of the source rectangles | ||
| 759 | * can also be the destination to avoid creating a third Rectangle2D | ||
| 760 | * object, but in this case the original points of this source | ||
| 761 | * rectangle will be overwritten by this method. | ||
| 762 | * @param src1 the first of a pair of <code>Rectangle2D</code> | ||
| 763 | * objects to be combined with each other | ||
| 764 | * @param src2 the second of a pair of <code>Rectangle2D</code> | ||
| 765 | * objects to be combined with each other | ||
| 766 | * @param dest the <code>Rectangle2D</code> that holds the | ||
| 767 | * results of the union of <code>src1</code> and | ||
| 768 | * <code>src2</code> | ||
| 769 | * @since 1.2 | ||
| 770 | */ | ||
| 771 | public static void union(Rectangle2D src1, | ||
| 772 | Rectangle2D src2, | ||
| 773 | Rectangle2D dest) { | ||
| 774 | double x1 = Math.min(src1.getMinX(), src2.getMinX()); | ||
| 775 | double y1 = Math.min(src1.getMinY(), src2.getMinY()); | ||
| 776 | double x2 = Math.max(src1.getMaxX(), src2.getMaxX()); | ||
| 777 | double y2 = Math.max(src1.getMaxY(), src2.getMaxY()); | ||
| 778 | dest.setFrameFromDiagonal(x1, y1, x2, y2); | ||
| 779 | } | ||
| 780 | |||
| 781 | /** | ||
| 782 | * Adds a point, specified by the double precision arguments | ||
| 783 | * <code>newx</code> and <code>newy</code>, to this | ||
| 784 | * <code>Rectangle2D</code>. The resulting <code>Rectangle2D</code> | ||
| 785 | * is the smallest <code>Rectangle2D</code> that | ||
| 786 | * contains both the original <code>Rectangle2D</code> and the | ||
| 787 | * specified point. | ||
| 788 | * <p> | ||
| 789 | * After adding a point, a call to <code>contains</code> with the | ||
| 790 | * added point as an argument does not necessarily return | ||
| 791 | * <code>true</code>. The <code>contains</code> method does not | ||
| 792 | * return <code>true</code> for points on the right or bottom | ||
| 793 | * edges of a rectangle. Therefore, if the added point falls on | ||
| 794 | * the left or bottom edge of the enlarged rectangle, | ||
| 795 | * <code>contains</code> returns <code>false</code> for that point. | ||
| 796 | * @param newx the X coordinate of the new point | ||
| 797 | * @param newy the Y coordinate of the new point | ||
| 798 | * @since 1.2 | ||
| 799 | */ | ||
| 800 | public void add(double newx, double newy) { | ||
| 801 | double x1 = Math.min(getMinX(), newx); | ||
| 802 | double x2 = Math.max(getMaxX(), newx); | ||
| 803 | double y1 = Math.min(getMinY(), newy); | ||
| 804 | double y2 = Math.max(getMaxY(), newy); | ||
| 805 | setRect(x1, y1, x2 - x1, y2 - y1); | ||
| 806 | } | ||
| 807 | |||
| 808 | /** | ||
| 809 | * Adds the <code>Point2D</code> object <code>pt</code> to this | ||
| 810 | * <code>Rectangle2D</code>. | ||
| 811 | * The resulting <code>Rectangle2D</code> is the smallest | ||
| 812 | * <code>Rectangle2D</code> that contains both the original | ||
| 813 | * <code>Rectangle2D</code> and the specified <code>Point2D</code>. | ||
| 814 | * <p> | ||
| 815 | * After adding a point, a call to <code>contains</code> with the | ||
| 816 | * added point as an argument does not necessarily return | ||
| 817 | * <code>true</code>. The <code>contains</code> | ||
| 818 | * method does not return <code>true</code> for points on the right | ||
| 819 | * or bottom edges of a rectangle. Therefore, if the added point falls | ||
| 820 | * on the left or bottom edge of the enlarged rectangle, | ||
| 821 | * <code>contains</code> returns <code>false</code> for that point. | ||
| 822 | * @param pt the new <code>Point2D</code> to add to this | ||
| 823 | * <code>Rectangle2D</code>. | ||
| 824 | * @since 1.2 | ||
| 825 | */ | ||
| 826 | public void add(Point2D pt) { | ||
| 827 | add(pt.getX(), pt.getY()); | ||
| 828 | } | ||
| 829 | |||
| 830 | /** | ||
| 831 | * Adds a <code>Rectangle2D</code> object to this | ||
| 832 | * <code>Rectangle2D</code>. The resulting <code>Rectangle2D</code> | ||
| 833 | * is the union of the two <code>Rectangle2D</code> objects. | ||
| 834 | * @param r the <code>Rectangle2D</code> to add to this | ||
| 835 | * <code>Rectangle2D</code>. | ||
| 836 | * @since 1.2 | ||
| 837 | */ | ||
| 838 | public void add(Rectangle2D r) { | ||
| 839 | double x1 = Math.min(getMinX(), r.getMinX()); | ||
| 840 | double x2 = Math.max(getMaxX(), r.getMaxX()); | ||
| 841 | double y1 = Math.min(getMinY(), r.getMinY()); | ||
| 842 | double y2 = Math.max(getMaxY(), r.getMaxY()); | ||
| 843 | setRect(x1, y1, x2 - x1, y2 - y1); | ||
| 844 | } | ||
| 845 | |||
| 846 | /** | ||
| 847 | * Returns an iteration object that defines the boundary of this | ||
| 848 | * <code>Rectangle2D</code>. | ||
| 849 | * The iterator for this class is multi-threaded safe, which means | ||
| 850 | * that this <code>Rectangle2D</code> class guarantees that | ||
| 851 | * modifications to the geometry of this <code>Rectangle2D</code> | ||
| 852 | * object do not affect any iterations of that geometry that | ||
| 853 | * are already in process. | ||
| 854 | * @param at an optional <code>AffineTransform</code> to be applied to | ||
| 855 | * the coordinates as they are returned in the iteration, or | ||
| 856 | * <code>null</code> if untransformed coordinates are desired | ||
| 857 | * @return the <code>PathIterator</code> object that returns the | ||
| 858 | * geometry of the outline of this | ||
| 859 | * <code>Rectangle2D</code>, one segment at a time. | ||
| 860 | * @since 1.2 | ||
| 861 | */ | ||
| 862 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 863 | return new RectIterator(this, at); | ||
| 864 | } | ||
| 865 | |||
| 866 | /** | ||
| 867 | * Returns an iteration object that defines the boundary of the | ||
| 868 | * flattened <code>Rectangle2D</code>. Since rectangles are already | ||
| 869 | * flat, the <code>flatness</code> parameter is ignored. | ||
| 870 | * The iterator for this class is multi-threaded safe, which means | ||
| 871 | * that this <code>Rectangle2D</code> class guarantees that | ||
| 872 | * modifications to the geometry of this <code>Rectangle2D</code> | ||
| 873 | * object do not affect any iterations of that geometry that | ||
| 874 | * are already in process. | ||
| 875 | * @param at an optional <code>AffineTransform</code> to be applied to | ||
| 876 | * the coordinates as they are returned in the iteration, or | ||
| 877 | * <code>null</code> if untransformed coordinates are desired | ||
| 878 | * @param flatness the maximum distance that the line segments used to | ||
| 879 | * approximate the curved segments are allowed to deviate from any | ||
| 880 | * point on the original curve. Since rectangles are already flat, | ||
| 881 | * the <code>flatness</code> parameter is ignored. | ||
| 882 | * @return the <code>PathIterator</code> object that returns the | ||
| 883 | * geometry of the outline of this | ||
| 884 | * <code>Rectangle2D</code>, one segment at a time. | ||
| 885 | * @since 1.2 | ||
| 886 | */ | ||
| 887 | public PathIterator getPathIterator(AffineTransform at, double flatness) { | ||
| 888 | return new RectIterator(this, at); | ||
| 889 | } | ||
| 890 | |||
| 891 | /** | ||
| 892 | * Returns the hashcode for this <code>Rectangle2D</code>. | ||
| 893 | * @return the hashcode for this <code>Rectangle2D</code>. | ||
| 894 | * @since 1.2 | ||
| 895 | */ | ||
| 896 | public int hashCode() { | ||
| 897 | long bits = java.lang.Double.doubleToLongBits(getX()); | ||
| 898 | bits += java.lang.Double.doubleToLongBits(getY()) * 37; | ||
| 899 | bits += java.lang.Double.doubleToLongBits(getWidth()) * 43; | ||
| 900 | bits += java.lang.Double.doubleToLongBits(getHeight()) * 47; | ||
| 901 | return (((int) bits) ^ ((int) (bits >> 32))); | ||
| 902 | } | ||
| 903 | |||
| 904 | /** | ||
| 905 | * Determines whether or not the specified <code>Object</code> is | ||
| 906 | * equal to this <code>Rectangle2D</code>. The specified | ||
| 907 | * <code>Object</code> is equal to this <code>Rectangle2D</code> | ||
| 908 | * if it is an instance of <code>Rectangle2D</code> and if its | ||
| 909 | * location and size are the same as this <code>Rectangle2D</code>. | ||
| 910 | * @param obj an <code>Object</code> to be compared with this | ||
| 911 | * <code>Rectangle2D</code>. | ||
| 912 | * @return <code>true</code> if <code>obj</code> is an instance | ||
| 913 | * of <code>Rectangle2D</code> and has | ||
| 914 | * the same values; <code>false</code> otherwise. | ||
| 915 | * @since 1.2 | ||
| 916 | */ | ||
| 917 | public boolean equals(Object obj) { | ||
| 918 | if (obj == this) { | ||
| 919 | return true; | ||
| 920 | } | ||
| 921 | if (obj instanceof Rectangle2D) { | ||
| 922 | Rectangle2D r2d = (Rectangle2D) obj; | ||
| 923 | return ((getX() == r2d.getX()) && | ||
| 924 | (getY() == r2d.getY()) && | ||
| 925 | (getWidth() == r2d.getWidth()) && | ||
| 926 | (getHeight() == r2d.getHeight())); | ||
| 927 | } | ||
| 928 | return false; | ||
| 929 | } | ||
| 930 | } | ||
Binary file not shown.
| @@ -0,0 +1,397 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.awt.Rectangle; | ||
| 30 | import java.beans.Transient; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * <code>RectangularShape</code> is the base class for a number of | ||
| 34 | * {@link Shape} objects whose geometry is defined by a rectangular frame. | ||
| 35 | * This class does not directly specify any specific geometry by | ||
| 36 | * itself, but merely provides manipulation methods inherited by | ||
| 37 | * a whole category of <code>Shape</code> objects. | ||
| 38 | * The manipulation methods provided by this class can be used to | ||
| 39 | * query and modify the rectangular frame, which provides a reference | ||
| 40 | * for the subclasses to define their geometry. | ||
| 41 | * | ||
| 42 | * @author Jim Graham | ||
| 43 | * @since 1.2 | ||
| 44 | */ | ||
| 45 | public abstract class RectangularShape implements Shape, Cloneable { | ||
| 46 | |||
| 47 | /** | ||
| 48 | * This is an abstract class that cannot be instantiated directly. | ||
| 49 | * | ||
| 50 | * @see Arc2D | ||
| 51 | * @see Ellipse2D | ||
| 52 | * @see Rectangle2D | ||
| 53 | * @see RoundRectangle2D | ||
| 54 | * @since 1.2 | ||
| 55 | */ | ||
| 56 | protected RectangularShape() { | ||
| 57 | } | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Returns the X coordinate of the upper-left corner of | ||
| 61 | * the framing rectangle in <code>double</code> precision. | ||
| 62 | * @return the X coordinate of the upper-left corner of | ||
| 63 | * the framing rectangle. | ||
| 64 | * @since 1.2 | ||
| 65 | */ | ||
| 66 | public abstract double getX(); | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Returns the Y coordinate of the upper-left corner of | ||
| 70 | * the framing rectangle in <code>double</code> precision. | ||
| 71 | * @return the Y coordinate of the upper-left corner of | ||
| 72 | * the framing rectangle. | ||
| 73 | * @since 1.2 | ||
| 74 | */ | ||
| 75 | public abstract double getY(); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Returns the width of the framing rectangle in | ||
| 79 | * <code>double</code> precision. | ||
| 80 | * @return the width of the framing rectangle. | ||
| 81 | * @since 1.2 | ||
| 82 | */ | ||
| 83 | public abstract double getWidth(); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Returns the height of the framing rectangle | ||
| 87 | * in <code>double</code> precision. | ||
| 88 | * @return the height of the framing rectangle. | ||
| 89 | * @since 1.2 | ||
| 90 | */ | ||
| 91 | public abstract double getHeight(); | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Returns the smallest X coordinate of the framing | ||
| 95 | * rectangle of the <code>Shape</code> in <code>double</code> | ||
| 96 | * precision. | ||
| 97 | * @return the smallest X coordinate of the framing | ||
| 98 | * rectangle of the <code>Shape</code>. | ||
| 99 | * @since 1.2 | ||
| 100 | */ | ||
| 101 | public double getMinX() { | ||
| 102 | return getX(); | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Returns the smallest Y coordinate of the framing | ||
| 107 | * rectangle of the <code>Shape</code> in <code>double</code> | ||
| 108 | * precision. | ||
| 109 | * @return the smallest Y coordinate of the framing | ||
| 110 | * rectangle of the <code>Shape</code>. | ||
| 111 | * @since 1.2 | ||
| 112 | */ | ||
| 113 | public double getMinY() { | ||
| 114 | return getY(); | ||
| 115 | } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Returns the largest X coordinate of the framing | ||
| 119 | * rectangle of the <code>Shape</code> in <code>double</code> | ||
| 120 | * precision. | ||
| 121 | * @return the largest X coordinate of the framing | ||
| 122 | * rectangle of the <code>Shape</code>. | ||
| 123 | * @since 1.2 | ||
| 124 | */ | ||
| 125 | public double getMaxX() { | ||
| 126 | return getX() + getWidth(); | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Returns the largest Y coordinate of the framing | ||
| 131 | * rectangle of the <code>Shape</code> in <code>double</code> | ||
| 132 | * precision. | ||
| 133 | * @return the largest Y coordinate of the framing | ||
| 134 | * rectangle of the <code>Shape</code>. | ||
| 135 | * @since 1.2 | ||
| 136 | */ | ||
| 137 | public double getMaxY() { | ||
| 138 | return getY() + getHeight(); | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Returns the X coordinate of the center of the framing | ||
| 143 | * rectangle of the <code>Shape</code> in <code>double</code> | ||
| 144 | * precision. | ||
| 145 | * @return the X coordinate of the center of the framing rectangle | ||
| 146 | * of the <code>Shape</code>. | ||
| 147 | * @since 1.2 | ||
| 148 | */ | ||
| 149 | public double getCenterX() { | ||
| 150 | return getX() + getWidth() / 2.0; | ||
| 151 | } | ||
| 152 | |||
| 153 | /** | ||
| 154 | * Returns the Y coordinate of the center of the framing | ||
| 155 | * rectangle of the <code>Shape</code> in <code>double</code> | ||
| 156 | * precision. | ||
| 157 | * @return the Y coordinate of the center of the framing rectangle | ||
| 158 | * of the <code>Shape</code>. | ||
| 159 | * @since 1.2 | ||
| 160 | */ | ||
| 161 | public double getCenterY() { | ||
| 162 | return getY() + getHeight() / 2.0; | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * Returns the framing {@link Rectangle2D} | ||
| 167 | * that defines the overall shape of this object. | ||
| 168 | * @return a <code>Rectangle2D</code>, specified in | ||
| 169 | * <code>double</code> coordinates. | ||
| 170 | * @see #setFrame(double, double, double, double) | ||
| 171 | * @see #setFrame(Point2D, Dimension2D) | ||
| 172 | * @see #setFrame(Rectangle2D) | ||
| 173 | * @since 1.2 | ||
| 174 | */ | ||
| 175 | @Transient | ||
| 176 | public Rectangle2D getFrame() { | ||
| 177 | return new Rectangle2D.Double(getX(), getY(), getWidth(), getHeight()); | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * Determines whether the <code>RectangularShape</code> is empty. | ||
| 182 | * When the <code>RectangularShape</code> is empty, it encloses no | ||
| 183 | * area. | ||
| 184 | * @return <code>true</code> if the <code>RectangularShape</code> is empty; | ||
| 185 | * <code>false</code> otherwise. | ||
| 186 | * @since 1.2 | ||
| 187 | */ | ||
| 188 | public abstract boolean isEmpty(); | ||
| 189 | |||
| 190 | /** | ||
| 191 | * Sets the location and size of the framing rectangle of this | ||
| 192 | * <code>Shape</code> to the specified rectangular values. | ||
| 193 | * | ||
| 194 | * @param x the X coordinate of the upper-left corner of the | ||
| 195 | * specified rectangular shape | ||
| 196 | * @param y the Y coordinate of the upper-left corner of the | ||
| 197 | * specified rectangular shape | ||
| 198 | * @param w the width of the specified rectangular shape | ||
| 199 | * @param h the height of the specified rectangular shape | ||
| 200 | * @see #getFrame | ||
| 201 | * @since 1.2 | ||
| 202 | */ | ||
| 203 | public abstract void setFrame(double x, double y, double w, double h); | ||
| 204 | |||
| 205 | /** | ||
| 206 | * Sets the location and size of the framing rectangle of this | ||
| 207 | * <code>Shape</code> to the specified {@link Point2D} and | ||
| 208 | * {@link Dimension2D}, respectively. The framing rectangle is used | ||
| 209 | * by the subclasses of <code>RectangularShape</code> to define | ||
| 210 | * their geometry. | ||
| 211 | * @param loc the specified <code>Point2D</code> | ||
| 212 | * @param size the specified <code>Dimension2D</code> | ||
| 213 | * @see #getFrame | ||
| 214 | * @since 1.2 | ||
| 215 | */ | ||
| 216 | public void setFrame(Point2D loc, Dimension2D size) { | ||
| 217 | setFrame(loc.getX(), loc.getY(), size.getWidth(), size.getHeight()); | ||
| 218 | } | ||
| 219 | |||
| 220 | /** | ||
| 221 | * Sets the framing rectangle of this <code>Shape</code> to | ||
| 222 | * be the specified <code>Rectangle2D</code>. The framing rectangle is | ||
| 223 | * used by the subclasses of <code>RectangularShape</code> to define | ||
| 224 | * their geometry. | ||
| 225 | * @param r the specified <code>Rectangle2D</code> | ||
| 226 | * @see #getFrame | ||
| 227 | * @since 1.2 | ||
| 228 | */ | ||
| 229 | public void setFrame(Rectangle2D r) { | ||
| 230 | setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * Sets the diagonal of the framing rectangle of this <code>Shape</code> | ||
| 235 | * based on the two specified coordinates. The framing rectangle is | ||
| 236 | * used by the subclasses of <code>RectangularShape</code> to define | ||
| 237 | * their geometry. | ||
| 238 | * | ||
| 239 | * @param x1 the X coordinate of the start point of the specified diagonal | ||
| 240 | * @param y1 the Y coordinate of the start point of the specified diagonal | ||
| 241 | * @param x2 the X coordinate of the end point of the specified diagonal | ||
| 242 | * @param y2 the Y coordinate of the end point of the specified diagonal | ||
| 243 | * @since 1.2 | ||
| 244 | */ | ||
| 245 | public void setFrameFromDiagonal(double x1, double y1, | ||
| 246 | double x2, double y2) { | ||
| 247 | if (x2 < x1) { | ||
| 248 | double t = x1; | ||
| 249 | x1 = x2; | ||
| 250 | x2 = t; | ||
| 251 | } | ||
| 252 | if (y2 < y1) { | ||
| 253 | double t = y1; | ||
| 254 | y1 = y2; | ||
| 255 | y2 = t; | ||
| 256 | } | ||
| 257 | setFrame(x1, y1, x2 - x1, y2 - y1); | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * Sets the diagonal of the framing rectangle of this <code>Shape</code> | ||
| 262 | * based on two specified <code>Point2D</code> objects. The framing | ||
| 263 | * rectangle is used by the subclasses of <code>RectangularShape</code> | ||
| 264 | * to define their geometry. | ||
| 265 | * | ||
| 266 | * @param p1 the start <code>Point2D</code> of the specified diagonal | ||
| 267 | * @param p2 the end <code>Point2D</code> of the specified diagonal | ||
| 268 | * @since 1.2 | ||
| 269 | */ | ||
| 270 | public void setFrameFromDiagonal(Point2D p1, Point2D p2) { | ||
| 271 | setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY()); | ||
| 272 | } | ||
| 273 | |||
| 274 | /** | ||
| 275 | * Sets the framing rectangle of this <code>Shape</code> | ||
| 276 | * based on the specified center point coordinates and corner point | ||
| 277 | * coordinates. The framing rectangle is used by the subclasses of | ||
| 278 | * <code>RectangularShape</code> to define their geometry. | ||
| 279 | * | ||
| 280 | * @param centerX the X coordinate of the specified center point | ||
| 281 | * @param centerY the Y coordinate of the specified center point | ||
| 282 | * @param cornerX the X coordinate of the specified corner point | ||
| 283 | * @param cornerY the Y coordinate of the specified corner point | ||
| 284 | * @since 1.2 | ||
| 285 | */ | ||
| 286 | public void setFrameFromCenter(double centerX, double centerY, | ||
| 287 | double cornerX, double cornerY) { | ||
| 288 | double halfW = Math.abs(cornerX - centerX); | ||
| 289 | double halfH = Math.abs(cornerY - centerY); | ||
| 290 | setFrame(centerX - halfW, centerY - halfH, halfW * 2.0, halfH * 2.0); | ||
| 291 | } | ||
| 292 | |||
| 293 | /** | ||
| 294 | * Sets the framing rectangle of this <code>Shape</code> based on a | ||
| 295 | * specified center <code>Point2D</code> and corner | ||
| 296 | * <code>Point2D</code>. The framing rectangle is used by the subclasses | ||
| 297 | * of <code>RectangularShape</code> to define their geometry. | ||
| 298 | * @param center the specified center <code>Point2D</code> | ||
| 299 | * @param corner the specified corner <code>Point2D</code> | ||
| 300 | * @since 1.2 | ||
| 301 | */ | ||
| 302 | public void setFrameFromCenter(Point2D center, Point2D corner) { | ||
| 303 | setFrameFromCenter(center.getX(), center.getY(), | ||
| 304 | corner.getX(), corner.getY()); | ||
| 305 | } | ||
| 306 | |||
| 307 | /** | ||
| 308 | * {@inheritDoc} | ||
| 309 | * @since 1.2 | ||
| 310 | */ | ||
| 311 | public boolean contains(Point2D p) { | ||
| 312 | return contains(p.getX(), p.getY()); | ||
| 313 | } | ||
| 314 | |||
| 315 | /** | ||
| 316 | * {@inheritDoc} | ||
| 317 | * @since 1.2 | ||
| 318 | */ | ||
| 319 | public boolean intersects(Rectangle2D r) { | ||
| 320 | return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 321 | } | ||
| 322 | |||
| 323 | /** | ||
| 324 | * {@inheritDoc} | ||
| 325 | * @since 1.2 | ||
| 326 | */ | ||
| 327 | public boolean contains(Rectangle2D r) { | ||
| 328 | return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||
| 329 | } | ||
| 330 | |||
| 331 | /** | ||
| 332 | * {@inheritDoc} | ||
| 333 | * @since 1.2 | ||
| 334 | */ | ||
| 335 | public Rectangle getBounds() { | ||
| 336 | double width = getWidth(); | ||
| 337 | double height = getHeight(); | ||
| 338 | if (width < 0 || height < 0) { | ||
| 339 | return new Rectangle(); | ||
| 340 | } | ||
| 341 | double x = getX(); | ||
| 342 | double y = getY(); | ||
| 343 | double x1 = Math.floor(x); | ||
| 344 | double y1 = Math.floor(y); | ||
| 345 | double x2 = Math.ceil(x + width); | ||
| 346 | double y2 = Math.ceil(y + height); | ||
| 347 | return new Rectangle((int) x1, (int) y1, | ||
| 348 | (int) (x2 - x1), (int) (y2 - y1)); | ||
| 349 | } | ||
| 350 | |||
| 351 | /** | ||
| 352 | * Returns an iterator object that iterates along the | ||
| 353 | * <code>Shape</code> object's boundary and provides access to a | ||
| 354 | * flattened view of the outline of the <code>Shape</code> | ||
| 355 | * object's geometry. | ||
| 356 | * <p> | ||
| 357 | * Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point types will | ||
| 358 | * be returned by the iterator. | ||
| 359 | * <p> | ||
| 360 | * The amount of subdivision of the curved segments is controlled | ||
| 361 | * by the <code>flatness</code> parameter, which specifies the | ||
| 362 | * maximum distance that any point on the unflattened transformed | ||
| 363 | * curve can deviate from the returned flattened path segments. | ||
| 364 | * An optional {@link AffineTransform} can | ||
| 365 | * be specified so that the coordinates returned in the iteration are | ||
| 366 | * transformed accordingly. | ||
| 367 | * @param at an optional <code>AffineTransform</code> to be applied to the | ||
| 368 | * coordinates as they are returned in the iteration, | ||
| 369 | * or <code>null</code> if untransformed coordinates are desired. | ||
| 370 | * @param flatness the maximum distance that the line segments used to | ||
| 371 | * approximate the curved segments are allowed to deviate | ||
| 372 | * from any point on the original curve | ||
| 373 | * @return a <code>PathIterator</code> object that provides access to | ||
| 374 | * the <code>Shape</code> object's flattened geometry. | ||
| 375 | * @since 1.2 | ||
| 376 | */ | ||
| 377 | public PathIterator getPathIterator(AffineTransform at, double flatness) { | ||
| 378 | return new FlatteningPathIterator(getPathIterator(at), flatness); | ||
| 379 | } | ||
| 380 | |||
| 381 | /** | ||
| 382 | * Creates a new object of the same class and with the same | ||
| 383 | * contents as this object. | ||
| 384 | * @return a clone of this instance. | ||
| 385 | * @exception OutOfMemoryError if there is not enough memory. | ||
| 386 | * @see java.lang.Cloneable | ||
| 387 | * @since 1.2 | ||
| 388 | */ | ||
| 389 | public Object clone() { | ||
| 390 | try { | ||
| 391 | return super.clone(); | ||
| 392 | } catch (CloneNotSupportedException e) { | ||
| 393 | // this shouldn't happen, since we are Cloneable | ||
| 394 | throw new InternalError(); | ||
| 395 | } | ||
| 396 | } | ||
| 397 | } | ||
Binary file not shown.
| @@ -0,0 +1,189 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A utility class to iterate over the path segments of an rounded rectangle | ||
| 32 | * through the PathIterator interface. | ||
| 33 | * | ||
| 34 | * @author Jim Graham | ||
| 35 | */ | ||
| 36 | class RoundRectIterator implements PathIterator { | ||
| 37 | double x, y, w, h, aw, ah; | ||
| 38 | AffineTransform affine; | ||
| 39 | int index; | ||
| 40 | |||
| 41 | RoundRectIterator(RoundRectangle2D rr, AffineTransform at) { | ||
| 42 | this.x = rr.getX(); | ||
| 43 | this.y = rr.getY(); | ||
| 44 | this.w = rr.getWidth(); | ||
| 45 | this.h = rr.getHeight(); | ||
| 46 | this.aw = Math.min(w, Math.abs(rr.getArcWidth())); | ||
| 47 | this.ah = Math.min(h, Math.abs(rr.getArcHeight())); | ||
| 48 | this.affine = at; | ||
| 49 | if (aw < 0 || ah < 0) { | ||
| 50 | // Don't draw anything... | ||
| 51 | index = ctrlpts.length; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Return the winding rule for determining the insideness of the | ||
| 57 | * path. | ||
| 58 | * @see #WIND_EVEN_ODD | ||
| 59 | * @see #WIND_NON_ZERO | ||
| 60 | */ | ||
| 61 | public int getWindingRule() { | ||
| 62 | return WIND_NON_ZERO; | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Tests if there are more points to read. | ||
| 67 | * @return true if there are more points to read | ||
| 68 | */ | ||
| 69 | public boolean isDone() { | ||
| 70 | return index >= ctrlpts.length; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Moves the iterator to the next segment of the path forwards | ||
| 75 | * along the primary direction of traversal as long as there are | ||
| 76 | * more points in that direction. | ||
| 77 | */ | ||
| 78 | public void next() { | ||
| 79 | index++; | ||
| 80 | } | ||
| 81 | |||
| 82 | private static final double angle = Math.PI / 4.0; | ||
| 83 | private static final double a = 1.0 - Math.cos(angle); | ||
| 84 | private static final double b = Math.tan(angle); | ||
| 85 | private static final double c = Math.sqrt(1.0 + b * b) - 1 + a; | ||
| 86 | private static final double cv = 4.0 / 3.0 * a * b / c; | ||
| 87 | private static final double acv = (1.0 - cv) / 2.0; | ||
| 88 | |||
| 89 | // For each array: | ||
| 90 | // 4 values for each point {v0, v1, v2, v3}: | ||
| 91 | // point = (x + v0 * w + v1 * arcWidth, | ||
| 92 | // y + v2 * h + v3 * arcHeight); | ||
| 93 | private static double ctrlpts[][] = { | ||
| 94 | { 0.0, 0.0, 0.0, 0.5 }, | ||
| 95 | { 0.0, 0.0, 1.0, -0.5 }, | ||
| 96 | { 0.0, 0.0, 1.0, -acv, | ||
| 97 | 0.0, acv, 1.0, 0.0, | ||
| 98 | 0.0, 0.5, 1.0, 0.0 }, | ||
| 99 | { 1.0, -0.5, 1.0, 0.0 }, | ||
| 100 | { 1.0, -acv, 1.0, 0.0, | ||
| 101 | 1.0, 0.0, 1.0, -acv, | ||
| 102 | 1.0, 0.0, 1.0, -0.5 }, | ||
| 103 | { 1.0, 0.0, 0.0, 0.5 }, | ||
| 104 | { 1.0, 0.0, 0.0, acv, | ||
| 105 | 1.0, -acv, 0.0, 0.0, | ||
| 106 | 1.0, -0.5, 0.0, 0.0 }, | ||
| 107 | { 0.0, 0.5, 0.0, 0.0 }, | ||
| 108 | { 0.0, acv, 0.0, 0.0, | ||
| 109 | 0.0, 0.0, 0.0, acv, | ||
| 110 | 0.0, 0.0, 0.0, 0.5 }, | ||
| 111 | {}, | ||
| 112 | }; | ||
| 113 | private static int types[] = { | ||
| 114 | SEG_MOVETO, | ||
| 115 | SEG_LINETO, SEG_CUBICTO, | ||
| 116 | SEG_LINETO, SEG_CUBICTO, | ||
| 117 | SEG_LINETO, SEG_CUBICTO, | ||
| 118 | SEG_LINETO, SEG_CUBICTO, | ||
| 119 | SEG_CLOSE, | ||
| 120 | }; | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Returns the coordinates and type of the current path segment in | ||
| 124 | * the iteration. | ||
| 125 | * The return value is the path segment type: | ||
| 126 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 127 | * A float array of length 6 must be passed in and may be used to | ||
| 128 | * store the coordinates of the point(s). | ||
| 129 | * Each point is stored as a pair of float x,y coordinates. | ||
| 130 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 131 | * SEG_QUADTO will return two points, | ||
| 132 | * SEG_CUBICTO will return 3 points | ||
| 133 | * and SEG_CLOSE will not return any points. | ||
| 134 | * @see #SEG_MOVETO | ||
| 135 | * @see #SEG_LINETO | ||
| 136 | * @see #SEG_QUADTO | ||
| 137 | * @see #SEG_CUBICTO | ||
| 138 | * @see #SEG_CLOSE | ||
| 139 | */ | ||
| 140 | public int currentSegment(float[] coords) { | ||
| 141 | if (isDone()) { | ||
| 142 | throw new NoSuchElementException("roundrect iterator out of bounds"); | ||
| 143 | } | ||
| 144 | double ctrls[] = ctrlpts[index]; | ||
| 145 | int nc = 0; | ||
| 146 | for (int i = 0; i < ctrls.length; i += 4) { | ||
| 147 | coords[nc++] = (float) (x + ctrls[i + 0] * w + ctrls[i + 1] * aw); | ||
| 148 | coords[nc++] = (float) (y + ctrls[i + 2] * h + ctrls[i + 3] * ah); | ||
| 149 | } | ||
| 150 | if (affine != null) { | ||
| 151 | affine.transform(coords, 0, coords, 0, nc / 2); | ||
| 152 | } | ||
| 153 | return types[index]; | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Returns the coordinates and type of the current path segment in | ||
| 158 | * the iteration. | ||
| 159 | * The return value is the path segment type: | ||
| 160 | * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. | ||
| 161 | * A double array of length 6 must be passed in and may be used to | ||
| 162 | * store the coordinates of the point(s). | ||
| 163 | * Each point is stored as a pair of double x,y coordinates. | ||
| 164 | * SEG_MOVETO and SEG_LINETO types will return one point, | ||
| 165 | * SEG_QUADTO will return two points, | ||
| 166 | * SEG_CUBICTO will return 3 points | ||
| 167 | * and SEG_CLOSE will not return any points. | ||
| 168 | * @see #SEG_MOVETO | ||
| 169 | * @see #SEG_LINETO | ||
| 170 | * @see #SEG_QUADTO | ||
| 171 | * @see #SEG_CUBICTO | ||
| 172 | * @see #SEG_CLOSE | ||
| 173 | */ | ||
| 174 | public int currentSegment(double[] coords) { | ||
| 175 | if (isDone()) { | ||
| 176 | throw new NoSuchElementException("roundrect iterator out of bounds"); | ||
| 177 | } | ||
| 178 | double ctrls[] = ctrlpts[index]; | ||
| 179 | int nc = 0; | ||
| 180 | for (int i = 0; i < ctrls.length; i += 4) { | ||
| 181 | coords[nc++] = (x + ctrls[i + 0] * w + ctrls[i + 1] * aw); | ||
| 182 | coords[nc++] = (y + ctrls[i + 2] * h + ctrls[i + 3] * ah); | ||
| 183 | } | ||
| 184 | if (affine != null) { | ||
| 185 | affine.transform(coords, 0, coords, 0, nc / 2); | ||
| 186 | } | ||
| 187 | return types[index]; | ||
| 188 | } | ||
| 189 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,681 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.awt.geom; | ||
| 27 | |||
| 28 | import java.io.Serializable; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * The <code>RoundRectangle2D</code> class defines a rectangle with | ||
| 32 | * rounded corners defined by a location {@code (x,y)}, a | ||
| 33 | * dimension {@code (w x h)}, and the width and height of an arc | ||
| 34 | * with which to round the corners. | ||
| 35 | * <p> | ||
| 36 | * This class is the abstract superclass for all objects that | ||
| 37 | * store a 2D rounded rectangle. | ||
| 38 | * The actual storage representation of the coordinates is left to | ||
| 39 | * the subclass. | ||
| 40 | * | ||
| 41 | * @author Jim Graham | ||
| 42 | * @since 1.2 | ||
| 43 | */ | ||
| 44 | public abstract class RoundRectangle2D extends RectangularShape { | ||
| 45 | |||
| 46 | /** | ||
| 47 | * The <code>Float</code> class defines a rectangle with rounded | ||
| 48 | * corners all specified in <code>float</code> coordinates. | ||
| 49 | * @since 1.2 | ||
| 50 | */ | ||
| 51 | public static class Float extends RoundRectangle2D | ||
| 52 | implements Serializable | ||
| 53 | { | ||
| 54 | /** | ||
| 55 | * The X coordinate of this <code>RoundRectangle2D</code>. | ||
| 56 | * @since 1.2 | ||
| 57 | * @serial | ||
| 58 | */ | ||
| 59 | public float x; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * The Y coordinate of this <code>RoundRectangle2D</code>. | ||
| 63 | * @since 1.2 | ||
| 64 | * @serial | ||
| 65 | */ | ||
| 66 | public float y; | ||
| 67 | |||
| 68 | /** | ||
| 69 | * The width of this <code>RoundRectangle2D</code>. | ||
| 70 | * @since 1.2 | ||
| 71 | * @serial | ||
| 72 | */ | ||
| 73 | public float width; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * The height of this <code>RoundRectangle2D</code>. | ||
| 77 | * @since 1.2 | ||
| 78 | * @serial | ||
| 79 | */ | ||
| 80 | public float height; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * The width of the arc that rounds off the corners. | ||
| 84 | * @since 1.2 | ||
| 85 | * @serial | ||
| 86 | */ | ||
| 87 | public float arcwidth; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * The height of the arc that rounds off the corners. | ||
| 91 | * @since 1.2 | ||
| 92 | * @serial | ||
| 93 | */ | ||
| 94 | public float archeight; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Constructs a new <code>RoundRectangle2D</code>, initialized to | ||
| 98 | * location (0.0, 0.0), size (0.0, 0.0), and corner arcs | ||
| 99 | * of radius 0.0. | ||
| 100 | * @since 1.2 | ||
| 101 | */ | ||
| 102 | public Float() { | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Constructs and initializes a <code>RoundRectangle2D</code> | ||
| 107 | * from the specified <code>float</code> coordinates. | ||
| 108 | * | ||
| 109 | * @param x the X coordinate of the newly | ||
| 110 | * constructed <code>RoundRectangle2D</code> | ||
| 111 | * @param y the Y coordinate of the newly | ||
| 112 | * constructed <code>RoundRectangle2D</code> | ||
| 113 | * @param w the width to which to set the newly | ||
| 114 | * constructed <code>RoundRectangle2D</code> | ||
| 115 | * @param h the height to which to set the newly | ||
| 116 | * constructed <code>RoundRectangle2D</code> | ||
| 117 | * @param arcw the width of the arc to use to round off the | ||
| 118 | * corners of the newly constructed | ||
| 119 | * <code>RoundRectangle2D</code> | ||
| 120 | * @param arch the height of the arc to use to round off the | ||
| 121 | * corners of the newly constructed | ||
| 122 | * <code>RoundRectangle2D</code> | ||
| 123 | * @since 1.2 | ||
| 124 | */ | ||
| 125 | public Float(float x, float y, float w, float h, | ||
| 126 | float arcw, float arch) | ||
| 127 | { | ||
| 128 | setRoundRect(x, y, w, h, arcw, arch); | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * {@inheritDoc} | ||
| 133 | * @since 1.2 | ||
| 134 | */ | ||
| 135 | public double getX() { | ||
| 136 | return (double) x; | ||
| 137 | } | ||
| 138 | |||
| 139 | /** | ||
| 140 | * {@inheritDoc} | ||
| 141 | * @since 1.2 | ||
| 142 | */ | ||
| 143 | public double getY() { | ||
| 144 | return (double) y; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * {@inheritDoc} | ||
| 149 | * @since 1.2 | ||
| 150 | */ | ||
| 151 | public double getWidth() { | ||
| 152 | return (double) width; | ||
| 153 | } | ||
| 154 | |||
| 155 | /** | ||
| 156 | * {@inheritDoc} | ||
| 157 | * @since 1.2 | ||
| 158 | */ | ||
| 159 | public double getHeight() { | ||
| 160 | return (double) height; | ||
| 161 | } | ||
| 162 | |||
| 163 | /** | ||
| 164 | * {@inheritDoc} | ||
| 165 | * @since 1.2 | ||
| 166 | */ | ||
| 167 | public double getArcWidth() { | ||
| 168 | return (double) arcwidth; | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 172 | * {@inheritDoc} | ||
| 173 | * @since 1.2 | ||
| 174 | */ | ||
| 175 | public double getArcHeight() { | ||
| 176 | return (double) archeight; | ||
| 177 | } | ||
| 178 | |||
| 179 | /** | ||
| 180 | * {@inheritDoc} | ||
| 181 | * @since 1.2 | ||
| 182 | */ | ||
| 183 | public boolean isEmpty() { | ||
| 184 | return (width <= 0.0f) || (height <= 0.0f); | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * Sets the location, size, and corner radii of this | ||
| 189 | * <code>RoundRectangle2D</code> to the specified | ||
| 190 | * <code>float</code> values. | ||
| 191 | * | ||
| 192 | * @param x the X coordinate to which to set the | ||
| 193 | * location of this <code>RoundRectangle2D</code> | ||
| 194 | * @param y the Y coordinate to which to set the | ||
| 195 | * location of this <code>RoundRectangle2D</code> | ||
| 196 | * @param w the width to which to set this | ||
| 197 | * <code>RoundRectangle2D</code> | ||
| 198 | * @param h the height to which to set this | ||
| 199 | * <code>RoundRectangle2D</code> | ||
| 200 | * @param arcw the width to which to set the arc of this | ||
| 201 | * <code>RoundRectangle2D</code> | ||
| 202 | * @param arch the height to which to set the arc of this | ||
| 203 | * <code>RoundRectangle2D</code> | ||
| 204 | * @since 1.2 | ||
| 205 | */ | ||
| 206 | public void setRoundRect(float x, float y, float w, float h, | ||
| 207 | float arcw, float arch) | ||
| 208 | { | ||
| 209 | this.x = x; | ||
| 210 | this.y = y; | ||
| 211 | this.width = w; | ||
| 212 | this.height = h; | ||
| 213 | this.arcwidth = arcw; | ||
| 214 | this.archeight = arch; | ||
| 215 | } | ||
| 216 | |||
| 217 | /** | ||
| 218 | * {@inheritDoc} | ||
| 219 | * @since 1.2 | ||
| 220 | */ | ||
| 221 | public void setRoundRect(double x, double y, double w, double h, | ||
| 222 | double arcw, double arch) | ||
| 223 | { | ||
| 224 | this.x = (float) x; | ||
| 225 | this.y = (float) y; | ||
| 226 | this.width = (float) w; | ||
| 227 | this.height = (float) h; | ||
| 228 | this.arcwidth = (float) arcw; | ||
| 229 | this.archeight = (float) arch; | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * {@inheritDoc} | ||
| 234 | * @since 1.2 | ||
| 235 | */ | ||
| 236 | public void setRoundRect(RoundRectangle2D rr) { | ||
| 237 | this.x = (float) rr.getX(); | ||
| 238 | this.y = (float) rr.getY(); | ||
| 239 | this.width = (float) rr.getWidth(); | ||
| 240 | this.height = (float) rr.getHeight(); | ||
| 241 | this.arcwidth = (float) rr.getArcWidth(); | ||
| 242 | this.archeight = (float) rr.getArcHeight(); | ||
| 243 | } | ||
| 244 | |||
| 245 | /** | ||
| 246 | * {@inheritDoc} | ||
| 247 | * @since 1.2 | ||
| 248 | */ | ||
| 249 | public Rectangle2D getBounds2D() { | ||
| 250 | return new Rectangle2D.Float(x, y, width, height); | ||
| 251 | } | ||
| 252 | |||
| 253 | /* | ||
| 254 | * JDK 1.6 serialVersionUID | ||
| 255 | */ | ||
| 256 | private static final long serialVersionUID = -3423150618393866922L; | ||
| 257 | } | ||
| 258 | |||
| 259 | /** | ||
| 260 | * The <code>Double</code> class defines a rectangle with rounded | ||
| 261 | * corners all specified in <code>double</code> coordinates. | ||
| 262 | * @since 1.2 | ||
| 263 | */ | ||
| 264 | public static class Double extends RoundRectangle2D | ||
| 265 | implements Serializable | ||
| 266 | { | ||
| 267 | /** | ||
| 268 | * The X coordinate of this <code>RoundRectangle2D</code>. | ||
| 269 | * @since 1.2 | ||
| 270 | * @serial | ||
| 271 | */ | ||
| 272 | public double x; | ||
| 273 | |||
| 274 | /** | ||
| 275 | * The Y coordinate of this <code>RoundRectangle2D</code>. | ||
| 276 | * @since 1.2 | ||
| 277 | * @serial | ||
| 278 | */ | ||
| 279 | public double y; | ||
| 280 | |||
| 281 | /** | ||
| 282 | * The width of this <code>RoundRectangle2D</code>. | ||
| 283 | * @since 1.2 | ||
| 284 | * @serial | ||
| 285 | */ | ||
| 286 | public double width; | ||
| 287 | |||
| 288 | /** | ||
| 289 | * The height of this <code>RoundRectangle2D</code>. | ||
| 290 | * @since 1.2 | ||
| 291 | * @serial | ||
| 292 | */ | ||
| 293 | public double height; | ||
| 294 | |||
| 295 | /** | ||
| 296 | * The width of the arc that rounds off the corners. | ||
| 297 | * @since 1.2 | ||
| 298 | * @serial | ||
| 299 | */ | ||
| 300 | public double arcwidth; | ||
| 301 | |||
| 302 | /** | ||
| 303 | * The height of the arc that rounds off the corners. | ||
| 304 | * @since 1.2 | ||
| 305 | * @serial | ||
| 306 | */ | ||
| 307 | public double archeight; | ||
| 308 | |||
| 309 | /** | ||
| 310 | * Constructs a new <code>RoundRectangle2D</code>, initialized to | ||
| 311 | * location (0.0, 0.0), size (0.0, 0.0), and corner arcs | ||
| 312 | * of radius 0.0. | ||
| 313 | * @since 1.2 | ||
| 314 | */ | ||
| 315 | public Double() { | ||
| 316 | } | ||
| 317 | |||
| 318 | /** | ||
| 319 | * Constructs and initializes a <code>RoundRectangle2D</code> | ||
| 320 | * from the specified <code>double</code> coordinates. | ||
| 321 | * | ||
| 322 | * @param x the X coordinate of the newly | ||
| 323 | * constructed <code>RoundRectangle2D</code> | ||
| 324 | * @param y the Y coordinate of the newly | ||
| 325 | * constructed <code>RoundRectangle2D</code> | ||
| 326 | * @param w the width to which to set the newly | ||
| 327 | * constructed <code>RoundRectangle2D</code> | ||
| 328 | * @param h the height to which to set the newly | ||
| 329 | * constructed <code>RoundRectangle2D</code> | ||
| 330 | * @param arcw the width of the arc to use to round off the | ||
| 331 | * corners of the newly constructed | ||
| 332 | * <code>RoundRectangle2D</code> | ||
| 333 | * @param arch the height of the arc to use to round off the | ||
| 334 | * corners of the newly constructed | ||
| 335 | * <code>RoundRectangle2D</code> | ||
| 336 | * @since 1.2 | ||
| 337 | */ | ||
| 338 | public Double(double x, double y, double w, double h, | ||
| 339 | double arcw, double arch) | ||
| 340 | { | ||
| 341 | setRoundRect(x, y, w, h, arcw, arch); | ||
| 342 | } | ||
| 343 | |||
| 344 | /** | ||
| 345 | * {@inheritDoc} | ||
| 346 | * @since 1.2 | ||
| 347 | */ | ||
| 348 | public double getX() { | ||
| 349 | return x; | ||
| 350 | } | ||
| 351 | |||
| 352 | /** | ||
| 353 | * {@inheritDoc} | ||
| 354 | * @since 1.2 | ||
| 355 | */ | ||
| 356 | public double getY() { | ||
| 357 | return y; | ||
| 358 | } | ||
| 359 | |||
| 360 | /** | ||
| 361 | * {@inheritDoc} | ||
| 362 | * @since 1.2 | ||
| 363 | */ | ||
| 364 | public double getWidth() { | ||
| 365 | return width; | ||
| 366 | } | ||
| 367 | |||
| 368 | /** | ||
| 369 | * {@inheritDoc} | ||
| 370 | * @since 1.2 | ||
| 371 | */ | ||
| 372 | public double getHeight() { | ||
| 373 | return height; | ||
| 374 | } | ||
| 375 | |||
| 376 | /** | ||
| 377 | * {@inheritDoc} | ||
| 378 | * @since 1.2 | ||
| 379 | */ | ||
| 380 | public double getArcWidth() { | ||
| 381 | return arcwidth; | ||
| 382 | } | ||
| 383 | |||
| 384 | /** | ||
| 385 | * {@inheritDoc} | ||
| 386 | * @since 1.2 | ||
| 387 | */ | ||
| 388 | public double getArcHeight() { | ||
| 389 | return archeight; | ||
| 390 | } | ||
| 391 | |||
| 392 | /** | ||
| 393 | * {@inheritDoc} | ||
| 394 | * @since 1.2 | ||
| 395 | */ | ||
| 396 | public boolean isEmpty() { | ||
| 397 | return (width <= 0.0f) || (height <= 0.0f); | ||
| 398 | } | ||
| 399 | |||
| 400 | /** | ||
| 401 | * {@inheritDoc} | ||
| 402 | * @since 1.2 | ||
| 403 | */ | ||
| 404 | public void setRoundRect(double x, double y, double w, double h, | ||
| 405 | double arcw, double arch) | ||
| 406 | { | ||
| 407 | this.x = x; | ||
| 408 | this.y = y; | ||
| 409 | this.width = w; | ||
| 410 | this.height = h; | ||
| 411 | this.arcwidth = arcw; | ||
| 412 | this.archeight = arch; | ||
| 413 | } | ||
| 414 | |||
| 415 | /** | ||
| 416 | * {@inheritDoc} | ||
| 417 | * @since 1.2 | ||
| 418 | */ | ||
| 419 | public void setRoundRect(RoundRectangle2D rr) { | ||
| 420 | this.x = rr.getX(); | ||
| 421 | this.y = rr.getY(); | ||
| 422 | this.width = rr.getWidth(); | ||
| 423 | this.height = rr.getHeight(); | ||
| 424 | this.arcwidth = rr.getArcWidth(); | ||
| 425 | this.archeight = rr.getArcHeight(); | ||
| 426 | } | ||
| 427 | |||
| 428 | /** | ||
| 429 | * {@inheritDoc} | ||
| 430 | * @since 1.2 | ||
| 431 | */ | ||
| 432 | public Rectangle2D getBounds2D() { | ||
| 433 | return new Rectangle2D.Double(x, y, width, height); | ||
| 434 | } | ||
| 435 | |||
| 436 | /* | ||
| 437 | * JDK 1.6 serialVersionUID | ||
| 438 | */ | ||
| 439 | private static final long serialVersionUID = 1048939333485206117L; | ||
| 440 | } | ||
| 441 | |||
| 442 | /** | ||
| 443 | * This is an abstract class that cannot be instantiated directly. | ||
| 444 | * Type-specific implementation subclasses are available for | ||
| 445 | * instantiation and provide a number of formats for storing | ||
| 446 | * the information necessary to satisfy the various accessor | ||
| 447 | * methods below. | ||
| 448 | * | ||
| 449 | * @see java.awt.geom.RoundRectangle2D.Float | ||
| 450 | * @see java.awt.geom.RoundRectangle2D.Double | ||
| 451 | * @since 1.2 | ||
| 452 | */ | ||
| 453 | protected RoundRectangle2D() { | ||
| 454 | } | ||
| 455 | |||
| 456 | /** | ||
| 457 | * Gets the width of the arc that rounds off the corners. | ||
| 458 | * @return the width of the arc that rounds off the corners | ||
| 459 | * of this <code>RoundRectangle2D</code>. | ||
| 460 | * @since 1.2 | ||
| 461 | */ | ||
| 462 | public abstract double getArcWidth(); | ||
| 463 | |||
| 464 | /** | ||
| 465 | * Gets the height of the arc that rounds off the corners. | ||
| 466 | * @return the height of the arc that rounds off the corners | ||
| 467 | * of this <code>RoundRectangle2D</code>. | ||
| 468 | * @since 1.2 | ||
| 469 | */ | ||
| 470 | public abstract double getArcHeight(); | ||
| 471 | |||
| 472 | /** | ||
| 473 | * Sets the location, size, and corner radii of this | ||
| 474 | * <code>RoundRectangle2D</code> to the specified | ||
| 475 | * <code>double</code> values. | ||
| 476 | * | ||
| 477 | * @param x the X coordinate to which to set the | ||
| 478 | * location of this <code>RoundRectangle2D</code> | ||
| 479 | * @param y the Y coordinate to which to set the | ||
| 480 | * location of this <code>RoundRectangle2D</code> | ||
| 481 | * @param w the width to which to set this | ||
| 482 | * <code>RoundRectangle2D</code> | ||
| 483 | * @param h the height to which to set this | ||
| 484 | * <code>RoundRectangle2D</code> | ||
| 485 | * @param arcWidth the width to which to set the arc of this | ||
| 486 | * <code>RoundRectangle2D</code> | ||
| 487 | * @param arcHeight the height to which to set the arc of this | ||
| 488 | * <code>RoundRectangle2D</code> | ||
| 489 | * @since 1.2 | ||
| 490 | */ | ||
| 491 | public abstract void setRoundRect(double x, double y, double w, double h, | ||
| 492 | double arcWidth, double arcHeight); | ||
| 493 | |||
| 494 | /** | ||
| 495 | * Sets this <code>RoundRectangle2D</code> to be the same as the | ||
| 496 | * specified <code>RoundRectangle2D</code>. | ||
| 497 | * @param rr the specified <code>RoundRectangle2D</code> | ||
| 498 | * @since 1.2 | ||
| 499 | */ | ||
| 500 | public void setRoundRect(RoundRectangle2D rr) { | ||
| 501 | setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), | ||
| 502 | rr.getArcWidth(), rr.getArcHeight()); | ||
| 503 | } | ||
| 504 | |||
| 505 | /** | ||
| 506 | * {@inheritDoc} | ||
| 507 | * @since 1.2 | ||
| 508 | */ | ||
| 509 | public void setFrame(double x, double y, double w, double h) { | ||
| 510 | setRoundRect(x, y, w, h, getArcWidth(), getArcHeight()); | ||
| 511 | } | ||
| 512 | |||
| 513 | /** | ||
| 514 | * {@inheritDoc} | ||
| 515 | * @since 1.2 | ||
| 516 | */ | ||
| 517 | public boolean contains(double x, double y) { | ||
| 518 | if (isEmpty()) { | ||
| 519 | return false; | ||
| 520 | } | ||
| 521 | double rrx0 = getX(); | ||
| 522 | double rry0 = getY(); | ||
| 523 | double rrx1 = rrx0 + getWidth(); | ||
| 524 | double rry1 = rry0 + getHeight(); | ||
| 525 | // Check for trivial rejection - point is outside bounding rectangle | ||
| 526 | if (x < rrx0 || y < rry0 || x >= rrx1 || y >= rry1) { | ||
| 527 | return false; | ||
| 528 | } | ||
| 529 | double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0; | ||
| 530 | double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0; | ||
| 531 | // Check which corner point is in and do circular containment | ||
| 532 | // test - otherwise simple acceptance | ||
| 533 | if (x >= (rrx0 += aw) && x < (rrx0 = rrx1 - aw)) { | ||
| 534 | return true; | ||
| 535 | } | ||
| 536 | if (y >= (rry0 += ah) && y < (rry0 = rry1 - ah)) { | ||
| 537 | return true; | ||
| 538 | } | ||
| 539 | x = (x - rrx0) / aw; | ||
| 540 | y = (y - rry0) / ah; | ||
| 541 | return (x * x + y * y <= 1.0); | ||
| 542 | } | ||
| 543 | |||
| 544 | private int classify(double coord, double left, double right, | ||
| 545 | double arcsize) | ||
| 546 | { | ||
| 547 | if (coord < left) { | ||
| 548 | return 0; | ||
| 549 | } else if (coord < left + arcsize) { | ||
| 550 | return 1; | ||
| 551 | } else if (coord < right - arcsize) { | ||
| 552 | return 2; | ||
| 553 | } else if (coord < right) { | ||
| 554 | return 3; | ||
| 555 | } else { | ||
| 556 | return 4; | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | /** | ||
| 561 | * {@inheritDoc} | ||
| 562 | * @since 1.2 | ||
| 563 | */ | ||
| 564 | public boolean intersects(double x, double y, double w, double h) { | ||
| 565 | if (isEmpty() || w <= 0 || h <= 0) { | ||
| 566 | return false; | ||
| 567 | } | ||
| 568 | double rrx0 = getX(); | ||
| 569 | double rry0 = getY(); | ||
| 570 | double rrx1 = rrx0 + getWidth(); | ||
| 571 | double rry1 = rry0 + getHeight(); | ||
| 572 | // Check for trivial rejection - bounding rectangles do not intersect | ||
| 573 | if (x + w <= rrx0 || x >= rrx1 || y + h <= rry0 || y >= rry1) { | ||
| 574 | return false; | ||
| 575 | } | ||
| 576 | double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0; | ||
| 577 | double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0; | ||
| 578 | int x0class = classify(x, rrx0, rrx1, aw); | ||
| 579 | int x1class = classify(x + w, rrx0, rrx1, aw); | ||
| 580 | int y0class = classify(y, rry0, rry1, ah); | ||
| 581 | int y1class = classify(y + h, rry0, rry1, ah); | ||
| 582 | // Trivially accept if any point is inside inner rectangle | ||
| 583 | if (x0class == 2 || x1class == 2 || y0class == 2 || y1class == 2) { | ||
| 584 | return true; | ||
| 585 | } | ||
| 586 | // Trivially accept if either edge spans inner rectangle | ||
| 587 | if ((x0class < 2 && x1class > 2) || (y0class < 2 && y1class > 2)) { | ||
| 588 | return true; | ||
| 589 | } | ||
| 590 | // Since neither edge spans the center, then one of the corners | ||
| 591 | // must be in one of the rounded edges. We detect this case if | ||
| 592 | // a [xy]0class is 3 or a [xy]1class is 1. One of those two cases | ||
| 593 | // must be true for each direction. | ||
| 594 | // We now find a "nearest point" to test for being inside a rounded | ||
| 595 | // corner. | ||
| 596 | x = (x1class == 1) ? (x = x + w - (rrx0 + aw)) : (x = x - (rrx1 - aw)); | ||
| 597 | y = (y1class == 1) ? (y = y + h - (rry0 + ah)) : (y = y - (rry1 - ah)); | ||
| 598 | x = x / aw; | ||
| 599 | y = y / ah; | ||
| 600 | return (x * x + y * y <= 1.0); | ||
| 601 | } | ||
| 602 | |||
| 603 | /** | ||
| 604 | * {@inheritDoc} | ||
| 605 | * @since 1.2 | ||
| 606 | */ | ||
| 607 | public boolean contains(double x, double y, double w, double h) { | ||
| 608 | if (isEmpty() || w <= 0 || h <= 0) { | ||
| 609 | return false; | ||
| 610 | } | ||
| 611 | return (contains(x, y) && | ||
| 612 | contains(x + w, y) && | ||
| 613 | contains(x, y + h) && | ||
| 614 | contains(x + w, y + h)); | ||
| 615 | } | ||
| 616 | |||
| 617 | /** | ||
| 618 | * Returns an iteration object that defines the boundary of this | ||
| 619 | * <code>RoundRectangle2D</code>. | ||
| 620 | * The iterator for this class is multi-threaded safe, which means | ||
| 621 | * that this <code>RoundRectangle2D</code> class guarantees that | ||
| 622 | * modifications to the geometry of this <code>RoundRectangle2D</code> | ||
| 623 | * object do not affect any iterations of that geometry that | ||
| 624 | * are already in process. | ||
| 625 | * @param at an optional <code>AffineTransform</code> to be applied to | ||
| 626 | * the coordinates as they are returned in the iteration, or | ||
| 627 | * <code>null</code> if untransformed coordinates are desired | ||
| 628 | * @return the <code>PathIterator</code> object that returns the | ||
| 629 | * geometry of the outline of this | ||
| 630 | * <code>RoundRectangle2D</code>, one segment at a time. | ||
| 631 | * @since 1.2 | ||
| 632 | */ | ||
| 633 | public PathIterator getPathIterator(AffineTransform at) { | ||
| 634 | return new RoundRectIterator(this, at); | ||
| 635 | } | ||
| 636 | |||
| 637 | /** | ||
| 638 | * Returns the hashcode for this <code>RoundRectangle2D</code>. | ||
| 639 | * @return the hashcode for this <code>RoundRectangle2D</code>. | ||
| 640 | * @since 1.6 | ||
| 641 | */ | ||
| 642 | public int hashCode() { | ||
| 643 | long bits = java.lang.Double.doubleToLongBits(getX()); | ||
| 644 | bits += java.lang.Double.doubleToLongBits(getY()) * 37; | ||
| 645 | bits += java.lang.Double.doubleToLongBits(getWidth()) * 43; | ||
| 646 | bits += java.lang.Double.doubleToLongBits(getHeight()) * 47; | ||
| 647 | bits += java.lang.Double.doubleToLongBits(getArcWidth()) * 53; | ||
| 648 | bits += java.lang.Double.doubleToLongBits(getArcHeight()) * 59; | ||
| 649 | return (((int) bits) ^ ((int) (bits >> 32))); | ||
| 650 | } | ||
| 651 | |||
| 652 | /** | ||
| 653 | * Determines whether or not the specified <code>Object</code> is | ||
| 654 | * equal to this <code>RoundRectangle2D</code>. The specified | ||
| 655 | * <code>Object</code> is equal to this <code>RoundRectangle2D</code> | ||
| 656 | * if it is an instance of <code>RoundRectangle2D</code> and if its | ||
| 657 | * location, size, and corner arc dimensions are the same as this | ||
| 658 | * <code>RoundRectangle2D</code>. | ||
| 659 | * @param obj an <code>Object</code> to be compared with this | ||
| 660 | * <code>RoundRectangle2D</code>. | ||
| 661 | * @return <code>true</code> if <code>obj</code> is an instance | ||
| 662 | * of <code>RoundRectangle2D</code> and has the same values; | ||
| 663 | * <code>false</code> otherwise. | ||
| 664 | * @since 1.6 | ||
| 665 | */ | ||
| 666 | public boolean equals(Object obj) { | ||
| 667 | if (obj == this) { | ||
| 668 | return true; | ||
| 669 | } | ||
| 670 | if (obj instanceof RoundRectangle2D) { | ||
| 671 | RoundRectangle2D rr2d = (RoundRectangle2D) obj; | ||
| 672 | return ((getX() == rr2d.getX()) && | ||
| 673 | (getY() == rr2d.getY()) && | ||
| 674 | (getWidth() == rr2d.getWidth()) && | ||
| 675 | (getHeight() == rr2d.getHeight()) && | ||
| 676 | (getArcWidth() == rr2d.getArcWidth()) && | ||
| 677 | (getArcHeight() == rr2d.getArcHeight())); | ||
| 678 | } | ||
| 679 | return false; | ||
| 680 | } | ||
| 681 | } | ||
| @@ -0,0 +1,32 @@ | |||
| 1 | <../../../../../mkconfig | ||
| 2 | |||
| 3 | TARG=\ | ||
| 4 | AffineTransform.dis\ | ||
| 5 | Arc2D.dis\ | ||
| 6 | ArcIterator.dis\ | ||
| 7 | Area.dis\ | ||
| 8 | CubicCurve2D.dis\ | ||
| 9 | CubicIterator.dis\ | ||
| 10 | Dimension2D.dis\ | ||
| 11 | Ellipse2D.dis\ | ||
| 12 | EllipseIterator.dis\ | ||
| 13 | FlatteningPathIterator.dis\ | ||
| 14 | GeneralPath.dis\ | ||
| 15 | IllegalPathStateException.dis\ | ||
| 16 | Line2D.dis\ | ||
| 17 | LineIterator.dis\ | ||
| 18 | NoninvertibleTransformException.dis\ | ||
| 19 | Path2D.dis\ | ||
| 20 | PathIterator.dis\ | ||
| 21 | Point2D.dis\ | ||
| 22 | QuadCurve2D.dis\ | ||
| 23 | QuadIterator.dis\ | ||
| 24 | Rectangle2D.dis\ | ||
| 25 | RectangularShape.dis\ | ||
| 26 | RectIterator.dis\ | ||
| 27 | RoundRectangle2D.dis\ | ||
| 28 | RoundRectIterator.dis\ | ||
| 29 | |||
| 30 | DISBIN=$ROOT/java/dis/java/java/java/awt/geom | ||
| 31 | |||
| 32 | <$ROOT/mkfiles/mkjava | ||
| @@ -0,0 +1,62 @@ | |||
| 1 | <!-- | ||
| 2 | Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | |||
| 5 | This code is free software; you can redistribute it and/or modify it | ||
| 6 | under the terms of the GNU General Public License version 2 only, as | ||
| 7 | published by the Free Software Foundation. Oracle designates this | ||
| 8 | particular file as subject to the "Classpath" exception as provided | ||
| 9 | by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | |||
| 11 | This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | accompanied this code). | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License version | ||
| 18 | 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | |||
| 21 | Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | or visit www.oracle.com if you need additional information or have any | ||
| 23 | questions. | ||
| 24 | --> | ||
| 25 | |||
| 26 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> | ||
| 27 | <html> | ||
| 28 | <body bgcolor="white"> | ||
| 29 | |||
| 30 | Provides the Java 2D classes for defining and performing operations | ||
| 31 | on objects related to two-dimensional geometry. Some important features | ||
| 32 | of the package include: | ||
| 33 | <ul> | ||
| 34 | <li>classes for manipulating geometry, such as AffineTransform and | ||
| 35 | the PathIterator interface which is implemented by all Shape objects. | ||
| 36 | |||
| 37 | <li>classes that implement the Shape interface, such as | ||
| 38 | CubicCurve2D, Ellipse2D, Line2D, Rectangle2D, and GeneralShape. | ||
| 39 | |||
| 40 | <li>the Area class which provides mechanisms for add (union), subtract, | ||
| 41 | intersect, and exclusiveOR operations on other Shape objects. | ||
| 42 | </ul> | ||
| 43 | |||
| 44 | <!-- | ||
| 45 | <h2>Package Specification</h2> | ||
| 46 | |||
| 47 | ##### FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT ##### | ||
| 48 | <ul> | ||
| 49 | <li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a> | ||
| 50 | </ul> | ||
| 51 | |||
| 52 | <h2>Related Documentation</h2> | ||
| 53 | |||
| 54 | For overviews, tutorials, examples, guides, and tool documentation, please see: | ||
| 55 | <ul> | ||
| 56 | <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a> | ||
| 57 | </ul> | ||
| 58 | --> | ||
| 59 | |||
| 60 | @since 1.2 | ||
| 61 | </body> | ||
| 62 | </html> | ||