View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.potential.bonded;
39  
40  import java.awt.Color;
41  import java.util.ArrayList;
42  import java.util.Collections;
43  import java.util.Hashtable;
44  import java.util.List;
45  import org.jogamp.java3d.Appearance;
46  import org.jogamp.java3d.BranchGroup;
47  import org.jogamp.java3d.Canvas3D;
48  import org.jogamp.java3d.ColoringAttributes;
49  import org.jogamp.java3d.Geometry;
50  import org.jogamp.java3d.GeometryArray;
51  import org.jogamp.java3d.LineAttributes;
52  import org.jogamp.java3d.Material;
53  import org.jogamp.java3d.Node;
54  import org.jogamp.java3d.PointAttributes;
55  import org.jogamp.java3d.PolygonAttributes;
56  import org.jogamp.java3d.RenderingAttributes;
57  import org.jogamp.java3d.ShaderAppearance;
58  import org.jogamp.java3d.ShaderProgram;
59  import org.jogamp.java3d.Shape3D;
60  import org.jogamp.java3d.Transform3D;
61  import org.jogamp.java3d.TransformGroup;
62  import org.jogamp.java3d.TransparencyAttributes;
63  import org.jogamp.java3d.utils.geometry.Cone;
64  import org.jogamp.java3d.utils.geometry.Cylinder;
65  import org.jogamp.java3d.utils.geometry.Sphere;
66  import org.jogamp.vecmath.Color3f;
67  import org.jogamp.vecmath.Point2d;
68  import org.jogamp.vecmath.Point3d;
69  import org.jogamp.vecmath.Vector3d;
70  
71  /**
72   * The RendererCache class defines constants related to rendering modes and caches primitives for
73   * the Renderer.
74   *
75   * @author Michael J. Schnieders
76   * @since 1.0
77   */
78  public class RendererCache {
79  
80    /** Constant <code>BLACK</code> */
81    public static final Color3f BLACK = new Color3f(Color.black.getRGBColorComponents(new float[3]));
82    /** Constant <code>viewModelHash</code> */
83    public static final Hashtable<String, ViewModel> viewModelHash = new Hashtable<>();
84    /** Constant <code>colorModelHash</code> */
85    public static final Hashtable<String, ColorModel> colorModelHash = new Hashtable<>();
86    /** Constant <code>ORANGE</code> */
87    static final Color3f ORANGE = new Color3f(Color.orange.getRGBColorComponents(new float[3]));
88    /** Constant <code>RED</code> */
89    static final Color3f RED = new Color3f(Color.red.getRGBColorComponents(new float[3]));
90    /** Constant <code>BLUE</code> */
91    static final Color3f BLUE = new Color3f(Color.blue.getRGBColorComponents(new float[3]));
92    /** Constant <code>GRAY</code> */
93    static final Color3f GRAY = new Color3f(Color.lightGray.getRGBColorComponents(new float[3]));
94    /** Constant <code>YELLOW</code> */
95    static final Color3f YELLOW = new Color3f(Color.yellow.getRGBColorComponents(new float[3]));
96    /** Constant <code>CYAN</code> */
97    static final Color3f CYAN = new Color3f(Color.cyan.getRGBColorComponents(new float[3]));
98    /** Constant <code>GREEN</code> */
99    static final Color3f GREEN = new Color3f(Color.green.getRGBColorComponents(new float[3]));
100   /** Constant <code>WHITE</code> */
101   static final Color3f WHITE = new Color3f(Color.white.getRGBColorComponents(new float[3]));
102   /** Constant <code>PINK</code> */
103   static final Color3f PINK = new Color3f(Color.pink.getRGBColorComponents(new float[3]));
104   /** Constant <code>MAGENTA</code> */
105   static final Color3f MAGENTA = new Color3f(Color.magenta.getRGBColorComponents(new float[3]));
106   /** Constant <code>nullAp</code> */
107   static final Appearance nullAp;
108 
109   private static final Transform3D localToVworld = new Transform3D();
110   private static final Transform3D worldToImagePlate = new Transform3D();
111   private static final Hashtable<Color3f, Material> materials = new Hashtable<>();
112   private static final Geometry[] sphereGeom = new Geometry[11];
113   private static final Geometry[][] cylgeom = new Geometry[3][11];
114   private static final Geometry[][] conegeom = new Geometry[2][4];
115   private static final Hashtable<Color3f, Appearance> pointAppearances = new Hashtable<>();
116   private static final Hashtable<Color3f, Appearance> lineAppearances = new Hashtable<>();
117   private static final Hashtable<Color3f, Appearance> fillAppearances = new Hashtable<>();
118   private static final Color3f[] negCharge = new Color3f[1000];
119   private static final Color3f[] posCharge = new Color3f[1000];
120   /** Constant <code>NULLColor</code> */
121   private static final Color3f NULLColor =
122       new Color3f(Color.darkGray.getRGBColorComponents(new float[3]));
123   /** Constant <code>lineAttributes</code> */
124   private static final LineAttributes lineAttributes = new LineAttributes();
125   /** Constant <code>pointAttributes</code> */
126   private static final PointAttributes pointAttributes = new PointAttributes();
127   /** Constant <code>coloringAttributes</code> */
128   private static final ColoringAttributes coloringAttributes = new ColoringAttributes();
129   /** Constant <code>renderingAttributes</code> */
130   private static final RenderingAttributes renderingAttributes = new RenderingAttributes();
131   /** Constant <code>transparencyAttributes</code> */
132   private static final TransparencyAttributes transparencyAttributes = new TransparencyAttributes();
133   /** Constant <code>fillPolygonAttributes</code> */
134   private static final PolygonAttributes fillPolygonAttributes = new PolygonAttributes();
135   /** Constant <code>pointPolygonAttributes</code> */
136   private static final PolygonAttributes pointPolygonAttributes = new PolygonAttributes();
137   /** Constant <code>linePolygonAttributes</code> */
138   private static final PolygonAttributes linePolygonAttributes = new PolygonAttributes();
139   /** Constant <code>detail=3</code> */
140   public static int detail = 3;
141   /** Constant <code>radius=1.0d</code> */
142   public static double radius = 1.0d;
143   /** Constant <code>bondwidth=3</code> */
144   public static int bondwidth = 3;
145   /** Constant <code>highlightSelections=false</code> */
146   public static boolean highlightSelections = false;
147   /** Constant <code>labelAtoms=false</code> */
148   public static boolean labelAtoms = false;
149   /** Constant <code>labelResidues=false</code> */
150   public static boolean labelResidues = false;
151   /** Constant <code>pickingColor</code> */
152   public static Color3f pickingColor = MAGENTA;
153   /** Constant <code>selectionColor</code> */
154   public static Color3f selectionColor = YELLOW;
155   /** Constant <code>userColor</code> */
156   public static Color3f userColor = WHITE;
157 
158   private static final List<Transform3D> transform3DPool = Collections.synchronizedList(new ArrayList<>());
159   private static final List<BranchGroup> spherePool = Collections.synchronizedList(new ArrayList<>());
160   private static final List<BranchGroup> doubleCylinderPool = Collections.synchronizedList(new ArrayList<>());
161   private static ShaderProgram shaderProgram = null;
162 
163   static {
164     coloringAttributes.setShadeModel(ColoringAttributes.NICEST);
165     coloringAttributes.setColor(new Color3f(0, 0, 0));
166     lineAttributes.setLineAntialiasingEnable(true);
167     lineAttributes.setLinePattern(LineAttributes.PATTERN_SOLID);
168     lineAttributes.setLineWidth(1.0f);
169     pointAttributes.setPointAntialiasingEnable(true);
170     pointAttributes.setPointSize(1.0f);
171     fillPolygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_FILL);
172     fillPolygonAttributes.setCullFace(PolygonAttributes.CULL_BACK);
173     linePolygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_LINE);
174     pointPolygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_POINT);
175     renderingAttributes.setVisible(true);
176     renderingAttributes.setDepthBufferEnable(true);
177     renderingAttributes.setDepthBufferWriteEnable(true);
178     renderingAttributes.setIgnoreVertexColors(true);
179     transparencyAttributes.setTransparencyMode(TransparencyAttributes.NONE);
180 
181     ViewModel[] values = ViewModel.values();
182     for (ViewModel value : values) {
183       viewModelHash.put(value.toString(), value);
184     }
185 
186     ColorModel[] colorModelValues = ColorModel.values();
187     for (ColorModel value : colorModelValues) {
188       colorModelHash.put(value.toString(), value);
189     }
190 
191     nullAp = new Appearance();
192     RenderingAttributes ra = new RenderingAttributes();
193     ra.setVisible(false);
194     nullAp.setRenderingAttributes(ra);
195   }
196 
197   /** Constructor for RendererCache. */
198   public RendererCache() {}
199 
200   /**
201    * appearanceFactory
202    *
203    * @param col a {@link org.jogamp.vecmath.Color3f} object.
204    * @param polygonType a {@link ffx.potential.bonded.RendererCache.ViewModel} object.
205    * @return a {@link org.jogamp.java3d.Appearance} object.
206    */
207   static Appearance appearanceFactory(Color3f col, ViewModel polygonType) {
208     if (col == null) {
209       return null;
210     }
211     Appearance ap;
212     if (polygonType == RendererCache.ViewModel.FILL) {
213       ap = fillAppearances.get(col);
214     } else if (polygonType == RendererCache.ViewModel.POINTS) {
215       ap = pointAppearances.get(col);
216     } else {
217       ap = lineAppearances.get(col);
218     }
219     if (ap == null) {
220       ap = createAppearance(col, polygonType);
221     }
222     return ap;
223   }
224 
225   /**
226    * coneFactory
227    *
228    * @param ap a {@link org.jogamp.java3d.Appearance} object.
229    * @param res a int.
230    * @return a {@link org.jogamp.java3d.Shape3D} object.
231    */
232   protected static Shape3D coneFactory(Appearance ap, int res) {
233     if (res > 3) {
234       res = 3;
235     }
236     Shape3D cone = new Shape3D();
237     cone.setAppearance(ap);
238     cone.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
239     cone.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
240     cone.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
241     cone.setCapability(Shape3D.ALLOW_PICKABLE_WRITE);
242     cone.addGeometry(getConeGeom(0, res));
243     cone.addGeometry(getConeGeom(1, res));
244     return cone;
245   }
246 
247   private static Appearance createAppearance(Color3f col, ViewModel polygonType) {
248     Appearance ap = null;
249     if (shaderProgram != null) {
250       ShaderAppearance sap = new ShaderAppearance();
251       sap.setShaderProgram(shaderProgram);
252       ap = sap;
253     }
254     if (ap == null) {
255       ap = new Appearance();
256     }
257     Material mat = materialFactory(col);
258     ap.setMaterial(mat);
259     ap.setRenderingAttributes(renderingAttributes);
260     ap.setColoringAttributes(coloringAttributes);
261     ap.setLineAttributes(lineAttributes);
262     ap.setPointAttributes(pointAttributes);
263     if (polygonType == RendererCache.ViewModel.FILL) {
264       ap.setPolygonAttributes(fillPolygonAttributes);
265       fillAppearances.put(col, ap);
266     } else if (polygonType == RendererCache.ViewModel.POINTS) {
267       ap.setPolygonAttributes(pointPolygonAttributes);
268       pointAppearances.put(col, ap);
269     } else {
270       ap.setPolygonAttributes(linePolygonAttributes);
271       lineAppearances.put(col, ap);
272     }
273     return ap;
274   }
275 
276   /**
277    * This method creates a Cylinder
278    *
279    * @param ap a {@link org.jogamp.java3d.Appearance} object.
280    * @param res a int.
281    * @return a {@link org.jogamp.java3d.Shape3D} object.
282    */
283   private static Shape3D createCylinder(Appearance ap, int res) {
284     if (res < 0) {
285       res = 0;
286     }
287     if (res > 10) {
288       res = 10;
289     }
290     final Shape3D cyl = new Shape3D();
291     cyl.setAppearance(ap);
292     cyl.addGeometry(getCylinderGeom(0, res));
293     cyl.addGeometry(getCylinderGeom(1, res));
294     cyl.addGeometry(getCylinderGeom(2, res));
295     cyl.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
296     cyl.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
297     cyl.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
298     cyl.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
299     cyl.setCapability(Shape3D.ENABLE_PICK_REPORTING);
300     cyl.setCapability(Shape3D.ALLOW_PICKABLE_WRITE);
301     return cyl;
302   }
303 
304   /** This method creates a single Sphere from the given appearance */
305   private static Shape3D createSphere(Appearance ap, int div) {
306     Shape3D shape3d = new Shape3D();
307     shape3d.setAppearance(ap);
308     shape3d.addGeometry(getSphereGeom(div));
309     shape3d.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
310     shape3d.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
311     shape3d.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
312     shape3d.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
313     shape3d.setCapability(Shape3D.ENABLE_PICK_REPORTING);
314     shape3d.setCapability(Shape3D.ALLOW_PICKABLE_WRITE);
315     return shape3d;
316   }
317 
318   private static TransformGroup createTransformGroup(Transform3D transform3D) {
319     TransformGroup transformGroup;
320     if (transform3D == null) {
321       transformGroup = new TransformGroup();
322     } else {
323       transformGroup = new TransformGroup(transform3D);
324     }
325     transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
326     transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
327     transformGroup.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
328     return transformGroup;
329   }
330 
331   /**
332    * doubleCylinderFactory
333    *
334    * @param a1 a {@link ffx.potential.bonded.Atom} object.
335    * @param a2 a {@link ffx.potential.bonded.Atom} object.
336    * @param div a int.
337    * @return a {@link org.jogamp.java3d.BranchGroup} object.
338    */
339   static BranchGroup doubleCylinderFactory(Atom a1, Atom a2, int div) {
340     BranchGroup branchGroup;
341     if (!doubleCylinderPool.isEmpty()) {
342       branchGroup = doubleCylinderPool.remove(0);
343       if (branchGroup != null) {
344         TransformGroup cy1tg = (TransformGroup) branchGroup.getChild(0);
345         Shape3D cy1 = (Shape3D) cy1tg.getChild(0);
346         cy1.setAppearance(a1.getAtomAppearance());
347         cy1.setUserData(a1);
348         TransformGroup cy2tg = (TransformGroup) branchGroup.getChild(1);
349         Shape3D cy2 = (Shape3D) cy2tg.getChild(0);
350         cy2.setUserData(a2);
351         cy2.setAppearance(a2.getAtomAppearance());
352         return branchGroup;
353       }
354     }
355     branchGroup = new BranchGroup();
356     branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
357     branchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
358     branchGroup.setCapability(BranchGroup.ENABLE_PICK_REPORTING);
359     TransformGroup cy1tg = createTransformGroup(null);
360     Shape3D cy1 = createCylinder(a1.getAtomAppearance(), div);
361     cy1.setUserData(a1);
362     cy1tg.addChild(cy1);
363     branchGroup.addChild(cy1tg);
364     TransformGroup cy2tg = createTransformGroup(null);
365     Shape3D cy2 = createCylinder(a2.getAtomAppearance(), div);
366     cy2.setUserData(a2);
367     cy2tg.addChild(cy2);
368     branchGroup.addChild(cy2tg);
369     branchGroup.compile();
370     return branchGroup;
371   }
372 
373   /**
374    * getColor
375    *
376    * @param a a {@link ffx.potential.bonded.Atom} object.
377    * @param mode a {@link ffx.potential.bonded.RendererCache.ColorModel} object.
378    * @return a {@link org.jogamp.vecmath.Color3f} object.
379    */
380   static Color3f getColor(Atom a, ColorModel mode) {
381     switch (mode) {
382       case CPK:
383         return Atom.AtomColor.get(a.getAtomicNumber());
384       case PICK:
385         return pickingColor;
386       case SELECT:
387         return selectionColor;
388       case PARTIALCHARGE:
389         int index;
390         double charge = a.getCharge();
391         if (charge < 0.0d) {
392           float c = (float) (charge * 1000.0);
393           index = -1 * Math.round(c);
394           if (index > 999) {
395             index = 999;
396           }
397           if (negCharge[index] == null) {
398             float value = index * 0.001f;
399             negCharge[index] = new Color3f(1.0f, 1.0f - value, 1.0f - value);
400           }
401           return negCharge[index];
402         } else if (charge == 0) {
403           return WHITE;
404         } else {
405           float c = (float) (charge * 1000.0);
406           index = Math.round(c);
407           if (index > 999) {
408             index = 999;
409           }
410           if (posCharge[index] == null) {
411             float value = index * 0.001f;
412             posCharge[index] = new Color3f(1.0f - value, 1.0f - value, 1.0f);
413           }
414           return posCharge[index];
415         }
416       default:
417         return NULLColor;
418     }
419   }
420 
421   /**
422    * getConeGeom
423    *
424    * @param num a int.
425    * @param res a int.
426    * @return a {@link org.jogamp.java3d.Geometry} object.
427    */
428   private static Geometry getConeGeom(int num, int res) {
429     if (res > 3) {
430       res = 3;
431     }
432     if (conegeom[num][res] == null) {
433       initConeGeom(res);
434     }
435     return conegeom[num][res];
436   }
437 
438   /**
439    * getCylinderGeom
440    *
441    * @param num a int.
442    * @param res a int.
443    * @return a {@link org.jogamp.java3d.Geometry} object.
444    */
445   static Geometry getCylinderGeom(int num, int res) {
446     if (res < 0) {
447       res = 0;
448     }
449     if (res > 10) {
450       res = 10;
451     }
452     if (cylgeom[num][res] == null) {
453       initCylinderGeom(res);
454     }
455     return cylgeom[num][res];
456   }
457 
458   /**
459    * getPolarGeom
460    *
461    * @param res a int.
462    * @return a {@link org.jogamp.java3d.Geometry} object.
463    */
464   protected static Geometry getPolarGeom(int res) {
465     return getSphereGeom(res);
466   }
467 
468   /**
469    * getScreenCoordinate
470    *
471    * @param canvas a {@link org.jogamp.java3d.Canvas3D} object.
472    * @param node a {@link org.jogamp.java3d.Node} object.
473    * @param point3d a {@link org.jogamp.vecmath.Point3d} object.
474    * @param point a {@link org.jogamp.vecmath.Point2d} object.
475    */
476   static void getScreenCoordinate(
477       Canvas3D canvas, Node node, Point3d point3d, final Point2d point) {
478     if (point == null) {
479       return;
480     }
481     // Get the transform to put the node in the world coordinate system
482     node.getLocalToVworld(localToVworld);
483     // Get the image plate transform
484     canvas.getVworldToImagePlate(worldToImagePlate);
485     // Transform into world coordinates
486     localToVworld.transform(point3d);
487     // Transform into imageplate coordinates
488     worldToImagePlate.transform(point3d);
489     // Final step to the 2D Screen.
490     canvas.getPixelLocationFromImagePlate(point3d, point);
491     /*
492      Now we have the location where the point will be rendered on the
493      screen depending on resize, placement, size, and eye point policies.
494      This should only be called on points that reside within the clipping
495      planes.
496     */
497   }
498 
499   /**
500    * Getter for the field <code>sphereGeom</code>.
501    *
502    * @param res a int.
503    * @return a {@link org.jogamp.java3d.Geometry} object.
504    */
505   static Geometry getSphereGeom(int res) {
506     if (res < 0) {
507       res = 0;
508     }
509     if (res > 10) {
510       res = 10;
511     }
512     if (sphereGeom[res] == null) {
513       initSphereGeom(res);
514     }
515     return sphereGeom[res];
516   }
517 
518   private static void initConeGeom(int res) {
519     Cone cone =
520         new Cone(
521             1.0f,
522             1.0f,
523             Cone.GENERATE_NORMALS | Cone.ENABLE_GEOMETRY_PICKING | Cone.ENABLE_APPEARANCE_MODIFY,
524             (res + 1) * 4,
525             1,
526             nullAp);
527     for (int i = 0; i < 2; i++) {
528       conegeom[i][res] = cone.getShape(i).getGeometry();
529       /*
530        * conegeom[i][res].setCapability(Geometry.ALLOW_INTERSECT);
531        * conegeom[i][res].setCapability(GeometryArray.ALLOW_FORMAT_READ);
532        * conegeom[i][res].setCapability(GeometryArray.ALLOW_COUNT_READ);
533        * conegeom
534        * [i][res].setCapability(GeometryArray.ALLOW_COORDINATE_READ);
535        */
536     }
537   }
538 
539   private static void initCylinderGeom(int res) {
540     Appearance ap = new Appearance();
541     Cylinder cyl =
542         new Cylinder(
543             1.0f,
544             1.0f,
545             Cylinder.GENERATE_NORMALS
546                 | Cylinder.ENABLE_APPEARANCE_MODIFY
547                 | Cylinder.ENABLE_GEOMETRY_PICKING,
548             2 + res,
549             1,
550             ap);
551     for (int i = 0; i < 3; i++) {
552       cylgeom[i][res] = cyl.getShape(i).getGeometry();
553       try {
554         cylgeom[i][res].setCapability(Geometry.ALLOW_INTERSECT);
555         cylgeom[i][res].setCapability(GeometryArray.ALLOW_FORMAT_READ);
556         cylgeom[i][res].setCapability(GeometryArray.ALLOW_COUNT_READ);
557         cylgeom[i][res].setCapability(GeometryArray.ALLOW_COORDINATE_READ);
558       } catch (Exception e) {
559         return;
560       }
561     }
562   }
563 
564   private static void initSphereGeom(int res) {
565     Appearance ap = new Appearance();
566     Sphere sphere;
567     sphere =
568         new Sphere(
569             1.0f,
570             Sphere.GENERATE_NORMALS
571                 | Sphere.ENABLE_APPEARANCE_MODIFY
572                 | Sphere.ENABLE_GEOMETRY_PICKING,
573             4 + 3 * res,
574             ap);
575     sphereGeom[res] = sphere.getShape().getGeometry();
576     // GeometryArray g = (GeometryArray) sphereGeom[res];
577     /*
578      * if (!g.isLive()) { g.setCapability(g.ALLOW_FORMAT_READ);
579      * g.setCapability(g.ALLOW_COUNT_READ);
580      * g.setCapability(g.ALLOW_COORDINATE_READ); }
581      */
582   }
583 
584   /**
585    * materialFactory
586    *
587    * @param col a {@link org.jogamp.vecmath.Color3f} object.
588    * @return a {@link org.jogamp.java3d.Material} object.
589    */
590   static Material materialFactory(Color3f col) {
591     if (col == null) {
592       return null;
593     }
594     Material mat = materials.get(col);
595     if (mat == null) {
596       mat = new Material(col, BLACK, col, WHITE, 75.0f);
597       mat.setLightingEnable(true);
598       materials.put(col, mat);
599     }
600     return mat;
601   }
602 
603   /**
604    * poolDoubleCylinder
605    *
606    * @param branchGroup a {@link org.jogamp.java3d.BranchGroup} object.
607    */
608   static void poolDoubleCylinder(BranchGroup branchGroup) {
609     if (branchGroup != null) {
610       doubleCylinderPool.add(branchGroup);
611     }
612   }
613 
614   /**
615    * poolSphere
616    *
617    * @param tg a {@link org.jogamp.java3d.BranchGroup} object.
618    */
619   static void poolSphere(BranchGroup tg) {
620     if (tg != null) {
621       spherePool.add(tg);
622     }
623   }
624 
625   /**
626    * poolTransform3D
627    *
628    * @param transform3D a {@link org.jogamp.java3d.Transform3D} object.
629    */
630   static void poolTransform3D(Transform3D transform3D) {
631     if (transform3D != null) {
632       transform3DPool.add(transform3D);
633     }
634   }
635 
636   /**
637    * sphereFactory
638    *
639    * @param ap a {@link org.jogamp.java3d.Appearance} object.
640    * @param div a int.
641    * @param transform3D a {@link org.jogamp.java3d.Transform3D} object.
642    * @return a {@link org.jogamp.java3d.BranchGroup} object.
643    */
644   static BranchGroup sphereFactory(Appearance ap, int div, Transform3D transform3D) {
645     BranchGroup branchGroup;
646     if (!spherePool.isEmpty()) {
647       branchGroup = spherePool.remove(0);
648       if (branchGroup != null) {
649         TransformGroup transformGroup = (TransformGroup) branchGroup.getChild(0);
650         transformGroup.setTransform(transform3D);
651         Shape3D sphere = (Shape3D) transformGroup.getChild(0);
652         sphere.setAppearance(ap);
653         return branchGroup;
654       }
655     }
656     branchGroup = new BranchGroup();
657     branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
658     branchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
659     branchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
660     TransformGroup transformGroup = createTransformGroup(transform3D);
661     Shape3D sphere = createSphere(ap, div);
662     transformGroup.addChild(sphere);
663     branchGroup.addChild(transformGroup);
664     branchGroup.compile();
665     return branchGroup;
666   }
667 
668   /**
669    * toAtomColor
670    *
671    * @param s a {@link java.lang.String} object.
672    * @return a {@link org.jogamp.vecmath.Color3f} object.
673    */
674   static Color3f toAtomColor(String s) {
675     String c = s.toLowerCase();
676     if (c.startsWith("h")) {
677       return WHITE;
678     }
679     if (c.startsWith("c")) {
680       return GRAY;
681     }
682     if (c.startsWith("n")) {
683       return BLUE;
684     }
685     if (c.startsWith("o")) {
686       return RED;
687     }
688     if (c.startsWith("p")) {
689       return GREEN;
690     }
691     if (c.startsWith("s")) {
692       return YELLOW;
693     }
694     return NULLColor;
695   }
696 
697   /**
698    * transform3DFactory
699    *
700    * @return a {@link org.jogamp.java3d.Transform3D} object.
701    */
702   static Transform3D transform3DFactory() {
703     Transform3D transform3D;
704     if (!transform3DPool.isEmpty()) {
705       transform3D = transform3DPool.get(0);
706       if (transform3D != null) {
707         return transform3D;
708       }
709     }
710     transform3D = new Transform3D();
711     return transform3D;
712   }
713 
714   /**
715    * transform3DFactory
716    *
717    * @param position a {@link org.jogamp.vecmath.Vector3d} object.
718    * @param scale a double.
719    * @return a {@link org.jogamp.java3d.Transform3D} object.
720    */
721   static Transform3D transform3DFactory(Vector3d position, double scale) {
722     Transform3D transform3D;
723     if (!transform3DPool.isEmpty()) {
724       transform3D = transform3DPool.get(0);
725       if (transform3D != null) {
726         transform3D.setTranslation(position);
727         transform3D.setScale(scale);
728         return transform3D;
729       }
730     }
731     transform3D = new Transform3D();
732     transform3D.setTranslation(position);
733     transform3D.setScale(scale);
734     return transform3D;
735   }
736 
737   public enum ColorModel {
738     CPK,
739     GROUP,
740     RESIDUE,
741     POLYMER,
742     MOLECULE,
743     MONOCHROME,
744     USERCOLOR,
745     PARTIALCHARGE,
746     PICK,
747     SELECT,
748     REVERT,
749     STRUCTURE,
750     APPLYUSERCOLOR
751   }
752 
753   public enum ViewModel {
754     WIREFRAME,
755     BALLANDSTICK,
756     SPACEFILL,
757     RMIN,
758     TUBE,
759     INVISIBLE,
760     RESTRICT,
761     SHOWHYDROGEN,
762     HIDEHYDROGEN,
763     DETAIL,
764     RIBBON,
765     SHOWVRML,
766     HIDEVRML,
767     INDUCEDDIPOLE,
768     FORCE,
769     VELOCITY,
770     ACCELERATION,
771     HIDEVECTORS,
772     UNIT,
773     RELATIVE,
774     ABSOLUTE,
775     POINTS,
776     LINES,
777     FILL,
778     DESTROY
779   }
780 }