1 /* 2 * Copyright (c) 2010, 2016, 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 javafx.scene.shape; 27 28 import com.sun.javafx.geom.BaseBounds; 29 import com.sun.javafx.geom.Ellipse2D; 30 import com.sun.javafx.geom.transform.BaseTransform; 31 import com.sun.javafx.scene.DirtyBits; 32 import com.sun.javafx.scene.NodeHelper; 33 import com.sun.javafx.scene.shape.EllipseHelper; 34 import com.sun.javafx.scene.shape.ShapeHelper; 35 import com.sun.javafx.sg.prism.NGEllipse; 36 import com.sun.javafx.sg.prism.NGNode; 37 import com.sun.javafx.sg.prism.NGShape; 38 import javafx.beans.property.DoubleProperty; 39 import javafx.beans.property.DoublePropertyBase; 40 import javafx.scene.Node; 41 import javafx.scene.paint.Paint; 42 43 44 /** 45 * The {@code Ellipse} class creates a new ellipse 46 * with the specified size and location in pixels 47 * 48 <PRE> 49 import javafx.scene.shape.*; 50 51 Ellipse ellipse = new Ellipse(); { 52 ellipse.setCenterX(50.0f); 53 ellipse.setCenterY(50.0f); 54 ellipse.setRadiusX(50.0f); 55 ellipse.setRadiusY(25.0f); 56 </PRE> 57 * @since JavaFX 2.0 58 */ 59 public class Ellipse extends Shape { 60 static { 61 EllipseHelper.setEllipseAccessor(new EllipseHelper.EllipseAccessor() { 62 @Override 63 public NGNode doCreatePeer(Node node) { 64 return ((Ellipse) node).doCreatePeer(); 65 } 66 67 @Override 68 public void doUpdatePeer(Node node) { 69 ((Ellipse) node).doUpdatePeer(); 70 } 71 72 @Override 73 public com.sun.javafx.geom.Shape doConfigShape(Shape shape) { 74 return ((Ellipse) shape).doConfigShape(); 75 } 76 }); 77 } 78 79 private final Ellipse2D shape = new Ellipse2D(); 80 81 private static final int NON_RECTILINEAR_TYPE_MASK = ~( 82 BaseTransform.TYPE_TRANSLATION | 83 BaseTransform.TYPE_QUADRANT_ROTATION | 84 BaseTransform.TYPE_MASK_SCALE | 85 BaseTransform.TYPE_FLIP); 86 87 { 88 // To initialize the class helper at the begining each constructor of this class 89 EllipseHelper.initHelper(this); 90 } 91 92 /** 93 * Creates an empty instance of Ellipse. 94 */ 95 public Ellipse() { 96 } 97 98 /** 99 * Creates an instance of Ellipse of the given size. 100 * @param radiusX the horizontal radius of the ellipse in pixels 101 * @param radiusY the vertical radius of the ellipse in pixels 102 */ 103 public Ellipse(double radiusX, double radiusY) { 104 setRadiusX(radiusX); 105 setRadiusY(radiusY); 106 } 107 108 /** 109 * Creates an instance of Ellipse of the given position and size. 110 * @param centerX the horizontal position of the center of the ellipse in pixels 111 * @param centerY the vertical position of the center of the ellipse in pixels 112 * @param radiusX the horizontal radius of the ellipse in pixels 113 * @param radiusY the vertical radius of the ellipse in pixels 114 */ 115 public Ellipse(double centerX, double centerY, double radiusX, double radiusY) { 116 this(radiusX, radiusY); 117 setCenterX(centerX); 118 setCenterY(centerY); 119 } 120 121 /** 122 * Defines the horizontal position of the center of the ellipse in pixels. 123 * 124 * @defaultValue 0.0 125 */ 126 private DoubleProperty centerX; 127 128 public final void setCenterX(double value) { 129 if (centerX != null || value != 0.0) { 130 centerXProperty().set(value); 131 } 132 } 133 134 public final double getCenterX() { 135 return centerX == null ? 0.0 : centerX.get(); 136 } 137 138 public final DoubleProperty centerXProperty() { 139 if (centerX == null) { 140 centerX = new DoublePropertyBase() { 141 142 @Override 143 public void invalidated() { 144 NodeHelper.markDirty(Ellipse.this, DirtyBits.NODE_GEOMETRY); 145 impl_geomChanged(); 146 } 147 148 @Override 149 public Object getBean() { 150 return Ellipse.this; 151 } 152 153 @Override 154 public String getName() { 155 return "centerX"; 156 } 157 }; 158 } 159 return centerX; 160 } 161 162 /** 163 * Defines the vertical position of the center of the ellipse in pixels. 164 * 165 * @defaultValue 0.0 166 */ 167 private DoubleProperty centerY; 168 169 public final void setCenterY(double value) { 170 if (centerY != null || value != 0.0) { 171 centerYProperty().set(value); 172 } 173 } 174 175 public final double getCenterY() { 176 return centerY == null ? 0.0 : centerY.get(); 177 } 178 179 public final DoubleProperty centerYProperty() { 180 if (centerY == null) { 181 centerY = new DoublePropertyBase() { 182 183 @Override 184 public void invalidated() { 185 NodeHelper.markDirty(Ellipse.this, DirtyBits.NODE_GEOMETRY); 186 impl_geomChanged(); 187 } 188 189 @Override 190 public Object getBean() { 191 return Ellipse.this; 192 } 193 194 @Override 195 public String getName() { 196 return "centerY"; 197 } 198 }; 199 } 200 return centerY; 201 } 202 203 /** 204 * Defines the width of the ellipse in pixels. 205 * 206 * @defaultValue 0.0 207 */ 208 private final DoubleProperty radiusX = new DoublePropertyBase() { 209 210 @Override 211 public void invalidated() { 212 NodeHelper.markDirty(Ellipse.this, DirtyBits.NODE_GEOMETRY); 213 impl_geomChanged(); 214 } 215 216 @Override 217 public Object getBean() { 218 return Ellipse.this; 219 } 220 221 @Override 222 public String getName() { 223 return "radiusX"; 224 } 225 }; 226 227 public final void setRadiusX(double value) { 228 radiusX.set(value); 229 } 230 231 public final double getRadiusX() { 232 return radiusX.get(); 233 } 234 235 public final DoubleProperty radiusXProperty() { 236 return radiusX; 237 } 238 239 /** 240 * Defines the height of the ellipse in pixels. 241 * 242 * @defaultValue 0.0 243 */ 244 private final DoubleProperty radiusY = new DoublePropertyBase() { 245 246 @Override 247 public void invalidated() { 248 NodeHelper.markDirty(Ellipse.this, DirtyBits.NODE_GEOMETRY); 249 impl_geomChanged(); 250 } 251 252 @Override 253 public Object getBean() { 254 return Ellipse.this; 255 } 256 257 @Override 258 public String getName() { 259 return "radiusY"; 260 } 261 }; 262 263 public final void setRadiusY(double value) { 264 radiusY.set(value); 265 } 266 267 public final double getRadiusY() { 268 return radiusY.get(); 269 } 270 271 public final DoubleProperty radiusYProperty() { 272 return radiusY; 273 } 274 275 /* 276 * Note: This method MUST only be called via its accessor method. 277 */ 278 private NGNode doCreatePeer() { 279 return new NGEllipse(); 280 } 281 282 /** 283 */ 284 @Override StrokeLineJoin convertLineJoin(StrokeLineJoin t) { 285 // The MITER join style can produce anomalous results for very thin or 286 // very wide ellipses when the bezier curves that approximate the arcs 287 // become so distorted that they shoot out MITER-like extensions. This 288 // effect complicates matters because it makes the ellipses very non-round, 289 // and also because it means we might have to pad the bounds to account 290 // for this rare and unpredictable circumstance. 291 // To avoid the problem, we set the Join style to BEVEL for any 292 // ellipse. The BEVEL join style is more predictable for 293 // anomalous angles and is the simplest join style to compute in 294 // the stroking code. 295 return StrokeLineJoin.BEVEL; 296 } 297 298 /** 299 * @treatAsPrivate implementation detail 300 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 301 */ 302 @Deprecated 303 @Override 304 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 305 // if there is no fill or stroke, then there are no bounds. The bounds 306 // must be marked empty in this case to distinguish it from 0,0,0,0 307 // which would actually contribute to the bounds of a group. 308 if (getMode() == NGShape.Mode.EMPTY) { 309 return bounds.makeEmpty(); 310 } 311 if ((tx.getType() & NON_RECTILINEAR_TYPE_MASK) != 0) { 312 return computeShapeBounds(bounds, tx, ShapeHelper.configShape(this)); 313 } 314 315 // compute the x, y, width and height of the ellipse 316 final double x = getCenterX() - getRadiusX(); 317 final double y = getCenterY() - getRadiusY(); 318 final double width = 2.0f * getRadiusX(); 319 final double height = 2.0f * getRadiusY(); 320 double upad; 321 double dpad; 322 if (getMode() == NGShape.Mode.FILL || getStrokeType() == StrokeType.INSIDE) { 323 upad = dpad = 0.0f; 324 } else { 325 upad = getStrokeWidth(); 326 if (getStrokeType() == StrokeType.CENTERED) { 327 upad /= 2.0f; 328 } 329 dpad = 0.0f; 330 } 331 return computeBounds(bounds, tx, upad, dpad, x, y, width, height); 332 } 333 334 /* 335 * Note: This method MUST only be called via its accessor method. 336 */ 337 private Ellipse2D doConfigShape() { 338 shape.setFrame( 339 (float)(getCenterX() - getRadiusX()), // x 340 (float)(getCenterY() - getRadiusY()), // y 341 (float)(getRadiusX() * 2.0), // w 342 (float)(getRadiusY() * 2.0)); // h 343 return shape; 344 } 345 346 /* 347 * Note: This method MUST only be called via its accessor method. 348 */ 349 private void doUpdatePeer() { 350 if (NodeHelper.isDirty(this, DirtyBits.NODE_GEOMETRY)) { 351 NGEllipse peer = NodeHelper.getPeer(this); 352 peer.updateEllipse((float)getCenterX(), 353 (float)getCenterY(), 354 (float)getRadiusX(), 355 (float)getRadiusY()); 356 } 357 } 358 359 /** 360 * Returns a string representation of this {@code Ellipse} object. 361 * @return a string representation of this {@code Ellipse} object. 362 */ 363 @Override 364 public String toString() { 365 final StringBuilder sb = new StringBuilder("Ellipse["); 366 367 String id = getId(); 368 if (id != null) { 369 sb.append("id=").append(id).append(", "); 370 } 371 372 sb.append("centerX=").append(getCenterX()); 373 sb.append(", centerY=").append(getCenterY()); 374 sb.append(", radiusX=").append(getRadiusX()); 375 sb.append(", radiusY=").append(getRadiusY()); 376 377 sb.append(", fill=").append(getFill()); 378 379 Paint stroke = getStroke(); 380 if (stroke != null) { 381 sb.append(", stroke=").append(stroke); 382 sb.append(", strokeWidth=").append(getStrokeWidth()); 383 } 384 385 return sb.append("]").toString(); 386 } 387 }