001/**
002 * J3DRenderingPrefs -- A class that contains Java3D rendering preferences for this
003 * application.
004 *
005 * Copyright (C) 2013-2023, Joseph A. Huwaldt. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or modify it under the terms
008 * of the GNU Lesser General Public License as published by the Free Software Foundation;
009 * either version 2.1 of the License, or (at your option) any later version.
010 *
011 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License along with
016 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
017 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html
018 */
019package geomss.j3d;
020
021import jahuwaldt.js.param.Parameter;
022import java.awt.Color;
023import java.util.ResourceBundle;
024import javax.measure.quantity.Length;
025import javax.measure.unit.SI;
026import org.jogamp.java3d.*;
027import org.jogamp.vecmath.Color3f;
028import jahuwaldt.j3d.JColor4f;
029import javolution.lang.Immutable;
030
031/**
032 * A class that contains a set of Java3D rendering preferences for this application.
033 *
034 * <p> Modified by: Joseph A. Huwaldt </p>
035 *
036 * @author Joseph A. Huwaldt, Date: December 21, 2013
037 * @version June 4, 2023
038 */
039public class J3DRenderingPrefs implements Immutable {
040
041    /**
042     * The resource bundle for this class.
043     */
044    protected static final ResourceBundle RB = J3DGeomGroup.RB;
045
046    //  The default color used when drawing points.
047    private static final Color DEFAULT_POINT_COLOR = new Color(255, 0, 0, 255);   //  Default = Red
048//  Default = Red
049
050    //  The default color used when drawing lines.
051    private static final Color DEFAULT_LINE_COLOR = new Color(0, 255, 0, 255);
052    //  The color to use when drawing points.
053     // Default = Green
054    private JColor4f _pointColor;
055
056    //  The color to use when drawing lines and curves.
057    private JColor4f _lineColor = new JColor4f(DEFAULT_LINE_COLOR);
058
059    //  The size that points are rendered in pixels.
060    private int _pointSize = 2;
061
062    //  The line width used when rendering lines and curves.
063    private int _lineWidth = 1;
064
065    //  The appearance used when rendering surfaces and point-arrays.
066    private static final Appearance DEFAULT_APPEARANCE = new Appearance();
067
068    static {
069        Material material = new Material();
070        material.setDiffuseColor(new Color3f(0.5F, 0.5F, 0.5F));
071        material.setShininess(64.5F);   //      Set shininess to 50% of range rather than 49.6063%.
072        material.setLightingEnable(true);
073        PolygonAttributes polyAttrib = new PolygonAttributes();
074        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
075        LineAttributes lineAttrib = new LineAttributes();
076        lineAttrib.setLineWidth(1);
077        DEFAULT_APPEARANCE.setPolygonAttributes(polyAttrib);
078        DEFAULT_APPEARANCE.setLineAttributes(lineAttrib);
079        DEFAULT_APPEARANCE.setMaterial(material);
080    }
081    private Appearance _srfAppearance = DEFAULT_APPEARANCE;
082
083    //  The drawing tolerance used for rendering parametric objects.
084    private Parameter<Length> _drawTol = Parameter.valueOf(0.005, SI.METER);
085
086    /**
087     * Construct a default set of rendering preferences.
088     */
089    public J3DRenderingPrefs() {this._pointColor = new JColor4f(DEFAULT_POINT_COLOR);
090 }
091
092    /**
093     * @return the default Color used to render points.
094     */
095    public static Color getDefaultPointColor() {
096        return DEFAULT_POINT_COLOR;
097    }
098
099    /**
100     * @return the default Color used to render lines.
101     */
102    public static Color getDefaultLineColor() {
103        return DEFAULT_LINE_COLOR;
104    }
105
106    /**
107     * @return the Java3D Color4f used to render points.
108     */
109    JColor4f getPointColorJ3D() {
110        return new JColor4f(_pointColor);
111    }
112
113    /**
114     * @return the Color used to render points.
115     */
116    public Color getPointColor() {
117        return _pointColor.get();
118    }
119
120    /**
121     * Return a set of rendering preferences that are identical to this one, but with the
122     * point color changed to the specified color.
123     *
124     * @param color The new color to use for rendering points.
125     * @return A copy of this set of rendering preferences with the point color changed.
126     */
127    public J3DRenderingPrefs changePointColor(Color color) {
128        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
129        copyPrefs(prefs);
130
131        JColor4f color4f = new JColor4f(color);
132
133        prefs._pointColor = color4f;
134        return prefs;
135    }
136
137    /**
138     * @return the size in pixels used for rendering points.
139     */
140    public int getPointSize() {
141        return _pointSize;
142    }
143
144    /**
145     * Return a set of rendering preferences that are identical to this one, but with the
146     * point size changed to the specified value.
147     *
148     * @param pixels The new size, in pixels, to use for rendering points.
149     * @return A copy of this set of rendering preferences with the point size changed.
150     */
151    public J3DRenderingPrefs changePointSize(int pixels) {
152        if (pixels < 1)
153            throw new IllegalArgumentException(RB.getString("pointSizeToSmall"));
154        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
155        copyPrefs(prefs);
156        prefs._pointSize = pixels;
157        return prefs;
158    }
159
160    /**
161     * @return the Java3D Color4f used to render lines and curves.
162     */
163    JColor4f getLineColorJ3D() {
164        return new JColor4f(_lineColor);
165    }
166
167    /**
168     * @return the Color used to render lines and curves.
169     */
170    public Color getLineColor() {
171        return _lineColor.get();
172    }
173
174    /**
175     * Return a set of rendering preferences that are identical to this one, but with the
176     * line or curve color changed to the specified color.
177     *
178     * @param color The color to use for rendering lines and curves.
179     * @return A copy of this set of rendering preferences with the line color changed.
180     */
181    public J3DRenderingPrefs changeLineColor(Color color) {
182        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
183        copyPrefs(prefs);
184
185        JColor4f color4f = new JColor4f(color);
186
187        prefs._lineColor = color4f;
188        return prefs;
189    }
190
191    /**
192     * @return the width in pixels used for rendering lines and curves.
193     */
194    public int getLineWidth() {
195        return _lineWidth;
196    }
197
198    /**
199     * Return a set of rendering preferences that are identical to this one, but with the
200     * line width changed to the specified value.
201     *
202     * @param pixels The width, in pixels, to use when rendering lines and curves.
203     * @return A copy of this set of rendering preferences with the line width changed.
204     */
205    public J3DRenderingPrefs changeLineWidth(int pixels) {
206        if (pixels < 1)
207            throw new IllegalArgumentException(RB.getString("pointSizeToSmall"));
208        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
209        copyPrefs(prefs);
210        prefs._lineWidth = pixels;
211        return prefs;
212    }
213
214    /**
215     * @return the tolerance used when rendering parametric objects.
216     */
217    public Parameter<Length> getDrawTolerance() {
218        return _drawTol;
219    }
220
221    /**
222     * Return a set of rendering preferences that are identical to this one, but with the
223     * draw tolerance changed to the specified value. This tolerance is used when
224     * determining how to subdivide parametric objects for rendering. If the input value
225     * is <code>null</code> or equal to <code>0</code>, it will be silently ignored.
226     *
227     * @param tol The geometric tolerance to use when rendering parametric objects. May
228     *            not be null.
229     * @return A copy of this set of rendering preferences with the draw tolerance
230     *         changed.
231     */
232    public J3DRenderingPrefs changeDrawTolerance(Parameter<Length> tol) {
233        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
234        copyPrefs(prefs);
235        if (tol.getValue() < 0)
236            tol = tol.opposite();
237        prefs._drawTol = tol;
238        return prefs;
239    }
240
241    /**
242     * @return the Java 3D Appearance used when rendering surfaces.
243     */
244    public Appearance getSurfaceAppearance() {
245        return (Appearance)_srfAppearance.cloneNodeComponent(true);
246    }
247
248    /**
249     * Get the color (of the specified type) used to render surfaces and point-arrays.
250     *
251     * @param colorType The aspect or type of the surface color that is being set. If
252     *                  AMBIENT_AND_DIFFUSE is passed in, then only the ambient color is
253     *                  returned!
254     * @return The color used for the specified type of surface color. Alpha is ignored.
255     * @see #getSurfaceAlpha
256     */
257    public Color getSurfaceColor(SurfaceColorType colorType) {
258
259        //      Retrieve the appearance and material.
260        Appearance appearance = _srfAppearance;
261        Material material = appearance.getMaterial();
262
263        //      Get the specified color.
264        Color3f color3f = new Color3f();
265        switch (colorType) {
266            case AMBIENT:
267            case AMBIENT_AND_DIFFUSE:
268                material.getAmbientColor(color3f);
269                break;
270            case DIFFUSE:
271                material.getDiffuseColor(color3f);
272                break;
273            case EMISSIVE:
274                material.getEmissiveColor(color3f);
275                break;
276            case SPECULAR:
277                material.getSpecularColor(color3f);
278                break;
279        }
280
281        //      Return as an AWT color object.
282        return new Color(color3f.getX(), color3f.getY(), color3f.getZ(), 1F);
283    }
284
285    /**
286     * Return a set of rendering preferences that are identical to this one, but with the
287     * specified type of color used to render surfaces and point-arrays changed.
288     *
289     * @param colorType The aspect or type of the surface color that is being set.
290     * @param color     The color to use for the specified type of surface color. The
291     *                  alpha, if present, is ignored.
292     * @return A copy of this set of rendering preferences with the surface color changed.
293     * @see #changeSurfaceAlpha
294     */
295    public J3DRenderingPrefs changeSurfaceColor(SurfaceColorType colorType, Color color) {
296        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
297        copyPrefs(prefs);
298
299        //  Copy the surface appearance data.
300        prefs._srfAppearance = (Appearance)prefs._srfAppearance.cloneNodeComponent(true);
301
302        float red = color.getRed() / 255F;
303        float green = color.getGreen() / 255F;
304        float blue = color.getBlue() / 255F;
305
306        //      Retrieve the appearance and material.
307        Appearance appearance = prefs._srfAppearance;
308        Material material = appearance.getMaterial();
309
310        //      Set the material color.
311        switch (colorType) {
312            case AMBIENT:
313                material.setAmbientColor(red, green, blue);
314                break;
315            case DIFFUSE:
316                material.setDiffuseColor(red, green, blue);
317                break;
318            case AMBIENT_AND_DIFFUSE:
319                material.setAmbientColor(red, green, blue);
320                material.setDiffuseColor(red, green, blue);
321                break;
322            case EMISSIVE:
323                material.setEmissiveColor(red, green, blue);
324                break;
325            case SPECULAR:
326                material.setSpecularColor(red, green, blue);
327                break;
328        }
329
330        return prefs;
331    }
332
333    /**
334     * Get the alpha or transparency used when rendering surfaces or point-arrays.
335     *
336     * @return The alpha value used (0.0=completely transparent, 1.0=completely opaque).
337     */
338    public float getSurfaceAlpha() {
339
340        //      Get the transparency attributes (if there are any).
341        TransparencyAttributes transp = _srfAppearance.getTransparencyAttributes();
342        float alpha = 1;
343        if (transp != null)
344            alpha -= transp.getTransparency();
345
346        return alpha;
347    }
348
349    /**
350     * Return a set of rendering preferences that are identical to this one, but with the
351     * alpha or transparency used to render surfaces and point-arrays changed.
352     *
353     * @param alpha The alpha value to use (0.0=completely transparent, 1.0=completely
354     *              opaque).
355     * @return A copy of this set of rendering preferences with the surface alpha changed.
356     */
357    public J3DRenderingPrefs changeSurfaceAlpha(float alpha) {
358        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
359        copyPrefs(prefs);
360
361        //  Copy the surface appearance data.
362        prefs._srfAppearance = (Appearance)prefs._srfAppearance.cloneNodeComponent(true);
363
364        //      Retrieve the appearance.
365        Appearance appearance = prefs._srfAppearance;
366
367        //      Get the transparency attributes (if there are any).
368        TransparencyAttributes transp = appearance.getTransparencyAttributes();
369        if (transp != null)
370            transp.setTransparency(1F - alpha);
371        else {
372            transp = new TransparencyAttributes(TransparencyAttributes.NICEST, 1F - alpha);
373            appearance.setTransparencyAttributes(transp);
374        }
375        if (alpha == 1)
376            transp.setTransparencyMode(TransparencyAttributes.NONE);
377        else
378            transp.setTransparencyMode(TransparencyAttributes.NICEST);
379
380        return prefs;
381    }
382
383    /**
384     * Get the shininess used when rendering surfaces and point-arrays.
385     *
386     * @return The shininess to use in the range [0.0, 1.0] where 0.0 is not shiny and 1.0
387     *         is very shiny.
388     */
389    public float getSurfaceShininess() {
390
391        //      Retrieve the appearance and material.
392        Material material = _srfAppearance.getMaterial();
393
394        //      Get the shininess value.
395        float shininess = material.getShininess();
396
397        //      Convert the shininess from Java3D to GeomSS.
398        shininess = (shininess - 1F) / 127F;
399        return shininess;
400    }
401
402    /**
403     * Return a set of rendering preferences that are identical to this one, but with the
404     * shininess used when rendering surfaces and point-arrays changed.
405     *
406     * @param shininess The shininess to use in the range [0.0, 1.0] where 0.0 is not
407     *                  shiny and 1.0 is very shiny. Values outside this range are
408     *                  clamped.
409     * @return A copy of this set of rendering preferences with the surface shininess
410     *         changed.
411     */
412    public J3DRenderingPrefs changeSurfaceShininess(float shininess) {
413        J3DRenderingPrefs prefs = new J3DRenderingPrefs();
414        copyPrefs(prefs);
415
416        //  Copy the surface appearance data.
417        prefs._srfAppearance = (Appearance)prefs._srfAppearance.cloneNodeComponent(true);
418
419        //      Convert shininess value from GeomSS to Java3D values.
420        shininess = 127F * shininess + 1F;
421
422        //      Retrieve a copy of the appearance and material.
423        Material material = prefs._srfAppearance.getMaterial();
424
425        //      Set the shininess.
426        material.setShininess(shininess);
427
428        return prefs;
429    }
430
431    /**
432     * Copies over the existing set of J3D rendering preferences from this object to the
433     * target one.
434     */
435    private void copyPrefs(J3DRenderingPrefs target) {
436        target._pointColor = _pointColor;
437        target._pointSize = _pointSize;
438        target._lineColor = _lineColor;
439        target._lineWidth = _lineWidth;
440        target._srfAppearance = _srfAppearance;
441        target._drawTol = _drawTol;
442    }
443
444    /**
445     * Compares this J3DRenderingPrefs against the specified object for strict equality.
446     *
447     * @param obj the object to compare with.
448     * @return <code>true</code> if this set of preferences is identical to that one;
449     *         <code>false</code> otherwise.
450     */
451    @Override
452    public boolean equals(Object obj) {
453        if (this == obj)
454            return true;
455        if ((obj == null) || (obj.getClass() != this.getClass()))
456            return false;
457
458        J3DRenderingPrefs that = (J3DRenderingPrefs)obj;
459        if (!this._pointColor.equals(that._pointColor))
460            return false;
461        if (this._pointSize != that._pointSize)
462            return false;
463        if (!this._lineColor.equals(that._lineColor))
464            return false;
465        if (this._lineWidth != that._lineWidth)
466            return false;
467        if (!this._drawTol.equals(that._drawTol))
468            return false;
469        if (this.getSurfaceAlpha() != that.getSurfaceAlpha())
470            return false;
471        if (this.getSurfaceShininess() != that.getSurfaceShininess())
472            return false;
473        if (!this.getSurfaceColor(SurfaceColorType.AMBIENT).equals(that.getSurfaceColor(SurfaceColorType.AMBIENT)))
474            return false;
475        if (!this.getSurfaceColor(SurfaceColorType.DIFFUSE).equals(that.getSurfaceColor(SurfaceColorType.DIFFUSE)))
476            return false;
477        if (!this.getSurfaceColor(SurfaceColorType.EMISSIVE).equals(that.getSurfaceColor(SurfaceColorType.EMISSIVE)))
478            return false;
479
480        return this.getSurfaceColor(SurfaceColorType.SPECULAR).equals(that.getSurfaceColor(SurfaceColorType.SPECULAR));
481    }
482
483    /**
484     * Returns the hash code for this set of preferences.
485     *
486     * @return the hash code value.
487     */
488    @Override
489    public int hashCode() {
490        int hash = 7;
491
492        hash = hash * 31 + _pointColor.hashCode();
493        hash = hash * 31 + _pointSize;
494        hash = hash * 31 + _lineColor.hashCode();
495        hash = hash * 31 + _lineWidth;
496        hash = hash * 31 + _srfAppearance.hashCode();
497        hash = hash * 31 + _drawTol.hashCode();
498
499        return hash;
500    }
501
502}