1 /*
   2  * Copyright (c) 2010, 2015, 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.effect;
  27 
  28 import javafx.beans.property.BooleanProperty;
  29 import javafx.beans.property.DoubleProperty;
  30 import javafx.beans.property.DoublePropertyBase;
  31 import javafx.beans.property.ObjectProperty;
  32 import javafx.beans.property.ObjectPropertyBase;
  33 import javafx.beans.property.SimpleBooleanProperty;
  34 import javafx.scene.paint.Color;
  35 
  36 import com.sun.javafx.util.Utils;
  37 import com.sun.javafx.tk.Toolkit;
  38 
  39 /**
  40  * The abstract base class for all light implementations.
  41  * @since JavaFX 2.0
  42  */
  43 public abstract class Light {
  44 
  45     /**
  46      * Creates a new Light.
  47      */
  48     protected Light() {
  49         impl_markDirty();
  50     }
  51 
  52     abstract com.sun.scenario.effect.light.Light impl_createImpl();
  53     private com.sun.scenario.effect.light.Light peer;
  54 
  55     com.sun.scenario.effect.light.Light impl_getImpl() {
  56         if (peer == null) {
  57             peer = impl_createImpl();
  58         }
  59         return peer;
  60     }
  61     /**
  62      * The color of the light source.
  63      * <pre>
  64      *       Min: n/a
  65      *       Max: n/a
  66      *   Default: Color.WHITE
  67      *  Identity: n/a
  68      * </pre>
  69      * @defaultValue WHITE
  70      */
  71     private ObjectProperty<Color> color;
  72 
  73     public final void setColor(Color value) {
  74         colorProperty().set(value);
  75     }
  76 
  77     public final Color getColor() {
  78         return color == null ? Color.WHITE : color.get();
  79     }
  80 
  81     public final ObjectProperty<Color> colorProperty() {
  82         if (color == null) {
  83             color = new ObjectPropertyBase<Color>(Color.WHITE) {
  84 
  85                 @Override
  86                 public void invalidated() {
  87                     impl_markDirty();
  88                 }
  89 
  90                 @Override
  91                 public Object getBean() {
  92                     return Light.this;
  93                 }
  94 
  95                 @Override
  96                 public String getName() {
  97                     return "color";
  98                 }
  99             };
 100         }
 101         return color;
 102     }
 103 
 104     void impl_sync() {
 105         if (impl_isEffectDirty()) {
 106             impl_update();
 107             impl_clearDirty();
 108         }
 109     }
 110 
 111    private Color getColorInternal() {
 112         Color c = getColor();
 113         return c == null ? Color.WHITE : c;
 114     }
 115 
 116     void impl_update() {
 117         impl_getImpl().setColor(Toolkit.getToolkit().toColor4f(getColorInternal()));
 118     }
 119 
 120     private BooleanProperty effectDirty;
 121 
 122     private void setEffectDirty(boolean value) {
 123         effectDirtyProperty().set(value);
 124     }
 125 
 126     final BooleanProperty effectDirtyProperty() {
 127         if (effectDirty == null) {
 128             effectDirty = new SimpleBooleanProperty(this, "effectDirty");
 129         }
 130         return effectDirty;
 131     }
 132 
 133     boolean impl_isEffectDirty() {
 134         return effectDirty == null ? false : effectDirty.get();
 135     }
 136 
 137     final void impl_markDirty() {
 138         setEffectDirty(true);
 139     }
 140 
 141     final void impl_clearDirty() {
 142         setEffectDirty(false);
 143     }
 144 
 145     /**
 146      * Represents a distant light source.
 147      *
 148      * <p>
 149      * Example:
 150      * <pre><code>
 151      * Light.Distant light = new Light.Distant();
 152      * light.setAzimuth(45.0);
 153      * light.setElevation(30.0);
 154      *
 155      * Lighting lighting = new Lighting();
 156      * lighting.setLight(light);
 157      * lighting.setSurfaceScale(5.0);
 158      *
 159      * Text text = new Text();
 160      * text.setText("Distant");
 161      * text.setFill(Color.STEELBLUE);
 162      * text.setFont(Font.font("null", FontWeight.BOLD, 80));
 163      * text.setX(10.0f);
 164      * text.setY(10.0f);
 165      * text.setTextOrigin(VPos.TOP);
 166      * text.setEffect(lighting);
 167      *
 168      * Rectangle rect = new Rectangle(300,150);
 169      * rect.setFill(Color.ALICEBLUE);
 170      * rect.setEffect(lighting);
 171      * </pre></code>
 172      *
 173      * <p> The code above produces the following: </p> 
 174      * <p> <img src="doc-files/lightdistant.png"/> </p>
 175      * @since JavaFX 2.0
 176      */
 177     public static class Distant extends Light {
 178        /**
 179         * Creates a new instance of Distant light with default parameters.
 180         */
 181         public Distant() {}
 182 
 183        /**
 184         * Creates a new instance of Distant light with the specified azimuth,
 185         * elevation, and color.
 186         * @param azimuth the azimuth of the light
 187         * @param elevation the elevation of the light
 188         * @param color the color of the light
 189         * @since JavaFX 2.1
 190         */
 191         public Distant(double azimuth, double elevation, Color color) {
 192            setAzimuth(azimuth);
 193            setElevation(elevation);
 194            setColor(color);
 195         }
 196     
 197         @Override
 198         com.sun.scenario.effect.light.DistantLight impl_createImpl() {
 199             return new com.sun.scenario.effect.light.DistantLight();
 200         }
 201         /**
 202          * The azimuth of the light.  The azimuth is the direction angle
 203          * for the light source on the XY plane, in degrees.
 204          * <pre>
 205          *       Min:  n/a
 206          *       Max:  n/a
 207          *   Default: 45.0
 208          *  Identity:  n/a
 209          * </pre>
 210          * @defaultValue 45.0
 211          */
 212         private DoubleProperty azimuth;
 213 
 214         public final void setAzimuth(double value) {
 215             azimuthProperty().set(value);
 216         }
 217 
 218         public final double getAzimuth() {
 219             return azimuth == null ? 45 : azimuth.get();
 220         }
 221 
 222         public final DoubleProperty azimuthProperty() {
 223             if (azimuth == null) {
 224                 azimuth = new DoublePropertyBase(45) {
 225 
 226                     @Override
 227                     public void invalidated() {
 228                         impl_markDirty();
 229                     }
 230 
 231                     @Override
 232                     public Object getBean() {
 233                         return Distant.this;
 234                     }
 235 
 236                     @Override
 237                     public String getName() {
 238                         return "azimuth";
 239                     }
 240                 };
 241             }
 242             return azimuth;
 243         }
 244         /**
 245          * The elevation of the light.  The elevation is the
 246          * direction angle for the light source on the YZ plane, in degrees.
 247          * <pre>
 248          *       Min:  n/a
 249          *       Max:  n/a
 250          *   Default: 45.0
 251          *  Identity:  n/a
 252          * </pre>
 253          * @defaultValue 45.0
 254          */
 255         private DoubleProperty elevation;
 256 
 257         public final void setElevation(double value) {
 258             elevationProperty().set(value);
 259         }
 260 
 261         public final double getElevation() {
 262             return elevation == null ? 45 : elevation.get();
 263         }
 264 
 265         public final DoubleProperty elevationProperty() {
 266             if (elevation == null) {
 267                 elevation = new DoublePropertyBase(45) {
 268 
 269                     @Override
 270                     public void invalidated() {
 271                         impl_markDirty();
 272                     }
 273 
 274                     @Override
 275                     public Object getBean() {
 276                         return Distant.this;
 277                     }
 278 
 279                     @Override
 280                     public String getName() {
 281                         return "elevation";
 282                     }
 283                 };
 284             }
 285             return elevation;
 286         }
 287 
 288         @Override
 289         void impl_update() {
 290             super.impl_update();
 291             com.sun.scenario.effect.light.DistantLight peer =
 292                     (com.sun.scenario.effect.light.DistantLight) impl_getImpl();
 293             peer.setAzimuth((float) getAzimuth());
 294             peer.setElevation((float) getElevation());
 295         }
 296     }
 297 
 298     /**
 299      * Represents a light source at a given position in 3D space.
 300      *
 301      * <p>
 302      * Example:
 303      * <pre><code>
 304      * Light.Point light = new Light.Point();
 305      * light.setX(100);
 306      * light.setY(100);
 307      * light.setZ(50);
 308      *
 309      * Lighting lighting = new Lighting();
 310      * lighting.setLight(light);
 311      * lighting.setSurfaceScale(5.0);
 312      *
 313      * Text text = new Text();
 314      * text.setText("Point");
 315      * text.setFill(Color.STEELBLUE);
 316      * text.setFont(Font.font(null, FontWeight.BOLD, 80));
 317      * text.setX(10.0);
 318      * text.setY(10.0);
 319      * text.setTextOrigin(VPos.TOP);
 320      *
 321      * Rectangle rect = new Rectangle(250, 150);
 322      * rect.setFill(Color.ALICEBLUE);
 323      * rect.setEffect(lighting);
 324      * text.setEffect(lighting);
 325      * </pre></code>
 326      *
 327      * <p> The code above produces the following: </p> 
 328      * <p> <img src="doc-files/lightpoint.png"/> </p>
 329      * @since JavaFX 2.0
 330      */
 331     public static class Point extends Light {
 332        /**
 333         * Creates a new instance of Point light with default parameters.
 334         */
 335         public Point() {}
 336 
 337        /**
 338         * Creates a new instance of Point light with the specified x, y, x, and
 339         * color.
 340         * @param x the x coordinate of the light position
 341         * @param y the y coordinate of the light position
 342         * @param z the z coordinate of the light position
 343         * @param color the color of the light
 344         * @since JavaFX 2.1
 345         */
 346         public Point(double x, double y, double z, Color color) {
 347            setX(x);
 348            setY(y);
 349            setZ(z);
 350            setColor(color);
 351         }
 352 
 353         /**
 354          * @treatAsPrivate implementation detail
 355          * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 356          */
 357         @Deprecated
 358         @Override
 359         com.sun.scenario.effect.light.PointLight impl_createImpl() {
 360             return new com.sun.scenario.effect.light.PointLight();
 361         }
 362         /**
 363          * The x coordinate of the light position.
 364          * <pre>
 365          *       Min: n/a
 366          *       Max: n/a
 367          *   Default: 0.0
 368          *  Identity: n/a
 369          * </pre>
 370          * @defaultValue 0.0
 371          */
 372         private DoubleProperty x;
 373 
 374         public final void setX(double value) {
 375             xProperty().set(value);
 376         }
 377 
 378         public final double getX() {
 379             return x == null ? 0 : x.get();
 380         }
 381 
 382         public final DoubleProperty xProperty() {
 383             if (x == null) {
 384                 x = new DoublePropertyBase() {
 385 
 386                     @Override
 387                     public void invalidated() {
 388                         impl_markDirty();
 389                     }
 390 
 391                     @Override
 392                     public Object getBean() {
 393                         return Point.this;
 394                     }
 395 
 396                     @Override
 397                     public String getName() {
 398                         return "x";
 399                     }
 400                 };
 401             }
 402             return x;
 403         }
 404         /**
 405          * The y coordinate of the light position.
 406          * <pre>
 407          *       Min: n/a
 408          *       Max: n/a
 409          *   Default: 0.0
 410          *  Identity: n/a
 411          * </pre>
 412          * @defaultValue 0.0
 413          */
 414         private DoubleProperty y;
 415 
 416         public final void setY(double value) {
 417             yProperty().set(value);
 418         }
 419 
 420         public final double getY() {
 421             return y == null ? 0 : y.get();
 422         }
 423 
 424         public final DoubleProperty yProperty() {
 425             if (y == null) {
 426                 y = new DoublePropertyBase() {
 427 
 428                     @Override
 429                     public void invalidated() {
 430                         impl_markDirty();
 431                     }
 432 
 433                     @Override
 434                     public Object getBean() {
 435                         return Point.this;
 436                     }
 437 
 438                     @Override
 439                     public String getName() {
 440                         return "y";
 441                     }
 442                 };
 443             }
 444             return y;
 445         }
 446         /**
 447          * The z coordinate of the light position.
 448          * <pre>
 449          *       Min: n/a
 450          *       Max: n/a
 451          *   Default: 0.0
 452          *  Identity: n/a
 453          * </pre>
 454          * @defaultValue 0.0
 455          */
 456         private DoubleProperty z;
 457 
 458         public final void setZ(double value) {
 459             zProperty().set(value);
 460         }
 461 
 462         public final double getZ() {
 463             return z == null ? 0 : z.get();
 464         }
 465 
 466         public final DoubleProperty zProperty() {
 467             if (z == null) {
 468                 z = new DoublePropertyBase() {
 469 
 470                     @Override
 471                     public void invalidated() {
 472                         impl_markDirty();
 473                     }
 474 
 475                     @Override
 476                     public Object getBean() {
 477                         return Point.this;
 478                     }
 479 
 480                     @Override
 481                     public String getName() {
 482                         return "z";
 483                     }
 484                 };
 485             }
 486             return z;
 487         }
 488 
 489         @Override
 490         void impl_update() {
 491             super.impl_update();
 492             com.sun.scenario.effect.light.PointLight peer =
 493                     (com.sun.scenario.effect.light.PointLight) impl_getImpl();
 494             peer.setX((float) getX());
 495             peer.setY((float) getY());
 496             peer.setZ((float) getZ());
 497         }
 498     }
 499 
 500     /**
 501      * Represents a spot light source at a given position in 3D space, with
 502      * configurable direction and focus.
 503      *
 504      * <p>
 505      * Example:
 506      * <pre><code>        
 507      * Light.Spot light = new Light.Spot();
 508      * light.setX(150);
 509      * light.setY(100);
 510      * light.setZ(80);
 511      * light.setPointsAtX(0);
 512      * light.setPointsAtY(0);
 513      * light.setPointsAtZ(-50);
 514      * light.setSpecularExponent(2);
 515      *
 516      * Lighting lighting = new Lighting();
 517      * lighting.setLight(light);
 518      * lighting.setSurfaceScale(5.0);
 519      *
 520      * Text text = new Text();
 521      * text.setText("Spot");
 522      * text.setFill(Color.STEELBLUE);
 523      * text.setFont(Font.font(null, FontWeight.BOLD, 80));
 524      * text.setX(10.0);
 525      * text.setY(10.0);
 526      * text.setTextOrigin(VPos.TOP);
 527      * text.setEffect(lighting);
 528      *
 529      * Rectangle rect = new Rectangle(200, 150);
 530      * rect.setFill(Color.ALICEBLUE);
 531      * rect.setEffect(lighting);
 532      * </pre></code>
 533      *
 534      * <p> The code above produces the following: </p> 
 535      * <p> <img src="doc-files/lightspot.png"/> </p>
 536      *
 537      * @since JavaFX 2.0
 538      */
 539     public static class Spot extends Light.Point {
 540        /**
 541         * Creates a new instance of Spot light with default parameters.
 542         */
 543         public Spot() {}
 544 
 545        /**
 546         * Creates a new instance of Spot light with the specified x, y, z,
 547         * specularExponent, and color.
 548         * @param x the x coordinate of the light position
 549         * @param y the y coordinate of the light position
 550         * @param z the z coordinate of the light position
 551         * @param specularExponent the specular exponent, which controls the 
 552         * focus of the light source
 553         * @param color the color of the light
 554         * @since JavaFX 2.1
 555         */
 556         public Spot(double x, double y, double z, double specularExponent, Color color) {
 557            setX(x);
 558            setY(y);
 559            setZ(z);
 560            setSpecularExponent(specularExponent);
 561            setColor(color);
 562         }
 563 
 564         /**
 565          * @treatAsPrivate implementation detail
 566          * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 567          */
 568         @Deprecated
 569         @Override
 570         com.sun.scenario.effect.light.SpotLight impl_createImpl() {
 571             return new com.sun.scenario.effect.light.SpotLight();
 572         }
 573         /**
 574          * The x coordinate of the direction vector for this light.
 575          * <pre>
 576          *       Min: n/a
 577          *       Max: n/a
 578          *   Default: 0.0
 579          *  Identity: n/a
 580          * </pre>
 581          * @defaultValue 0.0
 582          */
 583         private DoubleProperty pointsAtX;
 584 
 585         public final void setPointsAtX(double value) {
 586             pointsAtXProperty().set(value);
 587         }
 588 
 589         public final double getPointsAtX() {
 590             return pointsAtX == null ? 0 : pointsAtX.get();
 591         }
 592 
 593         public final DoubleProperty pointsAtXProperty() {
 594             if (pointsAtX == null) {
 595                 pointsAtX = new DoublePropertyBase() {
 596 
 597                     @Override
 598                     public void invalidated() {
 599                         impl_markDirty();
 600                     }
 601 
 602                     @Override
 603                     public Object getBean() {
 604                         return Spot.this;
 605                     }
 606 
 607                     @Override
 608                     public String getName() {
 609                         return "pointsAtX";
 610                     }
 611                 };
 612             }
 613             return pointsAtX;
 614         }
 615         /**
 616          * The y coordinate of the direction vector for this light.
 617          * <pre>
 618          *       Min: n/a
 619          *       Max: n/a
 620          *   Default: 0.0
 621          *  Identity: n/a
 622          * </pre>
 623          * @defaultValue 0.0
 624          */
 625         private DoubleProperty pointsAtY;
 626 
 627         public final void setPointsAtY(double value) {
 628             pointsAtYProperty().set(value);
 629         }
 630 
 631         public final double getPointsAtY() {
 632             return pointsAtY == null ? 0 : pointsAtY.get();
 633         }
 634 
 635         public final DoubleProperty pointsAtYProperty() {
 636             if (pointsAtY == null) {
 637                 pointsAtY = new DoublePropertyBase() {
 638 
 639                     @Override
 640                     public void invalidated() {
 641                         impl_markDirty();
 642                     }
 643 
 644                     @Override
 645                     public Object getBean() {
 646                         return Spot.this;
 647                     }
 648 
 649                     @Override
 650                     public String getName() {
 651                         return "pointsAtY";
 652                     }
 653                 };
 654             }
 655             return pointsAtY;
 656         }
 657         /**
 658          * The z coordinate of the direction vector for this light.
 659          * <pre>
 660          *       Min: n/a
 661          *       Max: n/a
 662          *   Default: 0.0
 663          *  Identity: n/a
 664          * </pre>
 665          * @defaultValue 0.0
 666          */
 667         private DoubleProperty pointsAtZ;
 668 
 669         public final void setPointsAtZ(double value) {
 670             pointsAtZProperty().set(value);
 671         }
 672 
 673         public final double getPointsAtZ() {
 674             return pointsAtZ == null ? 0 : pointsAtZ.get();
 675         }
 676 
 677         public final DoubleProperty pointsAtZProperty() {
 678             if (pointsAtZ == null) {
 679                 pointsAtZ = new DoublePropertyBase() {
 680 
 681                     @Override
 682                     public void invalidated() {
 683                         impl_markDirty();
 684                     }
 685 
 686                     @Override
 687                     public Object getBean() {
 688                         return Spot.this;
 689                     }
 690 
 691                     @Override
 692                     public String getName() {
 693                         return "pointsAtZ";
 694                     }
 695                 };
 696             }
 697             return pointsAtZ;
 698         }
 699         /**
 700          * The specular exponent, which controls the focus of this
 701          * light source.
 702          * <pre>
 703          *       Min: 0.0
 704          *       Max: 4.0
 705          *   Default: 1.0
 706          *  Identity: 1.0
 707          * </pre>
 708          * @defaultValue 1.0
 709          */
 710         private DoubleProperty specularExponent;
 711 
 712         public final void setSpecularExponent(double value) {
 713             specularExponentProperty().set(value);
 714         }
 715 
 716         public final double getSpecularExponent() {
 717             return specularExponent == null ? 1 : specularExponent.get();
 718         }
 719 
 720         public final DoubleProperty specularExponentProperty() {
 721             if (specularExponent == null) {
 722                 specularExponent = new DoublePropertyBase(1) {
 723 
 724                     @Override
 725                     public void invalidated() {
 726                         impl_markDirty();
 727                     }
 728 
 729                     @Override
 730                     public Object getBean() {
 731                         return Spot.this;
 732                     }
 733 
 734                     @Override
 735                     public String getName() {
 736                         return "specularExponent";
 737                     }
 738                 };
 739             }
 740             return specularExponent;
 741         }
 742 
 743         @Override
 744         void impl_update() {
 745             super.impl_update();
 746             com.sun.scenario.effect.light.SpotLight peer =
 747                     (com.sun.scenario.effect.light.SpotLight) impl_getImpl();
 748             peer.setPointsAtX((float) getPointsAtX());
 749             peer.setPointsAtY((float) getPointsAtY());
 750             peer.setPointsAtZ((float) getPointsAtZ());
 751             peer.setSpecularExponent((float) Utils.clamp(0, getSpecularExponent(), 4));
 752         }
 753     }
 754 }