1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2007-04-25 11:08:02 -0500 (Wed, 25 Apr 2007) $
4  * $Revision: 7492 $
5  *
6  * Copyright (C) 2005 Miguel, Jmol Development
7  *
8  * Contact: jmol-developers@lists.sf.net,jmol-developers@lists.sourceforge.net
9  * Contact: hansonr@stolaf.edu
10  *
11  *  This library is free software; you can redistribute it and/or
12  *  modify it under the terms of the GNU Lesser General Public
13  *  License as published by the Free Software Foundation; either
14  *  version 2.1 of the License, or (at your option) any later version.
15  *
16  *  This library is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *  Lesser General Public License for more details.
20  *
21  *  You should have received a copy of the GNU Lesser General Public
22  *  License along with this library; if not, write to the Free Software
23  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 /*
27  * miguel 2005 07 17
28  *
29  *  System and method for the display of surface structures
30  *  contained within the interior region of a solid body
31  * United States Patent Number 4,710,876
32  * Granted: Dec 1, 1987
33  * Inventors:  Cline; Harvey E. (Schenectady, NY);
34  *             Lorensen; William E. (Ballston Lake, NY)
35  * Assignee: General Electric Company (Schenectady, NY)
36  * Appl. No.: 741390
37  * Filed: June 5, 1985
38  *
39  *
40  * Patents issuing prior to June 8, 1995 can last up to 17
41  * years from the date of issuance.
42  *
43  * Dec 1 1987 + 17 yrs = Dec 1 2004
44  */
45 
46 /*
47  * Bob Hanson May 22, 2006
48  *
49  * implementing marching squares; see
50  * http://www.secam.ex.ac.uk/teaching/ug/studyres/COM3404/COM3404-2006-Lecture15.pdf
51  *
52  * inventing "Jmol Voxel File" format, *.jvxl
53  *
54  * see http://www.stolaf.edu/academics/chemapps/jmol/docs/misc/JVXL-format.pdf
55  *
56  * lines through coordinates are identical to CUBE files
57  * after that, we have a line that starts with a negative number to indicate this
58  * is a JVXL file:
59  *
60  * line1:  (int)-nSurfaces  (int)edgeFractionBase (int)edgeFractionRange
61  * (nSurface lines): (float)cutoff (int)nBytesData (int)nBytesFractions
62  *
63  * definition1
64  * edgedata1
65  * fractions1
66  * colordata1
67  * ....
68  * definition2
69  * edgedata2
70  * fractions2
71  * colordata2
72  * ....
73  *
74  * definitions: a line with detail about what sort of compression follows
75  *
76  * edgedata: a list of the count of vertices ouside and inside the cutoff, whatever
77  * that may be, ordered by nested for loops for(x){for(y){for(z)}}}.
78  *
79  * nOutside nInside nOutside nInside...
80  *
81  * fractions: an ascii list of characters represting the fraction of distance each
82  * encountered surface point is along each voxel cube edge found to straddle the
83  * surface. The order written is dictated by the reader algorithm and is not trivial
84  * to describe. Each ascii character is constructed by taking a base character and
85  * adding onto it the fraction times a range. This gives a character that can be
86  * quoted EXCEPT for backslash, which MAY be substituted for by '!'. Jmol uses the
87  * range # - | (35 - 124), reserving ! and } for special meanings.
88  *
89  * colordata: same deal here, but with possibility of "double precision" using two bytes.
90  *
91  */
92 
93 package org.jmol.shapesurface;
94 
95 import java.io.BufferedInputStream;
96 import java.io.BufferedReader;
97 import java.io.IOException;
98 import java.util.Hashtable;
99 import java.util.Map;
100 
101 import javajs.api.GenericBinaryDocument;
102 import javajs.util.A4;
103 import javajs.util.AU;
104 import javajs.util.CU;
105 import javajs.util.Lst;
106 import javajs.util.M3;
107 import javajs.util.M4;
108 import javajs.util.OC;
109 import javajs.util.P3;
110 import javajs.util.P3i;
111 import javajs.util.P4;
112 import javajs.util.PT;
113 import javajs.util.Quat;
114 import javajs.util.Rdr;
115 import javajs.util.SB;
116 import javajs.util.T3;
117 import javajs.util.V3;
118 
119 import javajs.util.BS;
120 import org.jmol.jvxl.api.MeshDataServer;
121 import org.jmol.jvxl.data.JvxlCoder;
122 import org.jmol.jvxl.data.JvxlData;
123 import org.jmol.jvxl.data.MeshData;
124 import org.jmol.jvxl.readers.Parameters;
125 import org.jmol.jvxl.readers.SurfaceGenerator;
126 import org.jmol.script.T;
127 import org.jmol.shape.Mesh;
128 import org.jmol.shape.MeshCollection;
129 import org.jmol.util.C;
130 import org.jmol.util.ColorEncoder;
131 import org.jmol.util.Escape;
132 import org.jmol.util.Logger;
133 import org.jmol.util.MeshSurface;
134 import org.jmol.util.TempArray;
135 import org.jmol.viewer.ActionManager;
136 import org.jmol.viewer.JC;
137 import org.jmol.viewer.Viewer;
138 
139 
140 public class Isosurface extends MeshCollection implements MeshDataServer {
141 
142   protected IsosurfaceMesh[] isomeshes = new IsosurfaceMesh[4];
143   protected IsosurfaceMesh thisMesh;
144 
145   @Override
allocMesh(String thisID, Mesh m)146   public void allocMesh(String thisID, Mesh m) {
147     int index = meshCount++;
148     meshes = isomeshes = (IsosurfaceMesh[]) AU.ensureLength(isomeshes,
149         meshCount * 2);
150     currentMesh = thisMesh = isomeshes[index] = (m == null ? new IsosurfaceMesh(vwr,
151         thisID, colix, index) : (IsosurfaceMesh) m);
152     currentMesh.index = index;
153     if (sg != null)
154       sg.setJvxlData(jvxlData = thisMesh.jvxlData);
155   }
156 
157   @Override
initShape()158   public void initShape() {
159     super.initShape();
160     myType = "isosurface";
161     newSg();
162   }
163 
newSg()164   protected void newSg() {
165     sg = new SurfaceGenerator(vwr, this, null, jvxlData = new JvxlData());
166     sg.params.showTiming = vwr.getBoolean(T.showtiming);
167     sg.version = "Jmol " + Viewer.getJmolVersion();
168   }
169 
clearSg()170   protected void clearSg() {
171     sg = null; // not Molecular Orbitals
172   }
173   //private boolean logMessages;
174   private String actualID;
175   protected boolean iHaveBitSets;
176   private boolean explicitContours;
177   private int atomIndex;
178   private int moNumber;
179   private float[] moLinearCombination;
180   private int colorType;
181   private short defaultColix;
182   private short meshColix;
183   private P3 center;
184   private float scale3d;
185   private boolean isPhaseColored;
186   private boolean isColorExplicit;
187   private String scriptAppendix = "";
188 
189   protected SurfaceGenerator sg;
190 
191   private float withinDistance2;
192   private boolean isWithinNot;
193   private Lst<P3> withinPoints;
194   private float[] cutoffRange;
195 
196   //private boolean allowContourLines;
197   boolean allowMesh = true;
198 
199   @Override
setProperty(String propertyName, Object value, BS bs)200   public void setProperty(String propertyName, Object value, BS bs) {
201     setPropI(propertyName, value, bs);
202   }
203 
204   @SuppressWarnings("unchecked")
setPropI(String propertyName, Object value, BS bs)205   protected void setPropI(String propertyName, Object value, BS bs) {
206 
207     //System.out.println("isosurface testing " + propertyName + " " + value + (propertyName == "token" ? " " + T.nameOf(((Integer)value).intValue()) : ""));
208 
209     //isosurface-only (no calculation required; no calc parameters to set)
210 
211     //    if ("navigate" == propertyName) {
212     //      navigate(((Integer) value).intValue());
213     //      return;
214     //    }
215     if ("cache" == propertyName) {
216       if (currentMesh == null)
217         return;
218       String id = currentMesh.thisID;
219       int imodel = currentMesh.modelIndex;
220       vwr.cachePut("cache://isosurface_" + id,
221           ((String) getPropI("jvxlDataXml", -1)).getBytes());
222       deleteMeshI(currentMesh.index);
223       setPropI("init", null, null);
224       setPropI("thisID", id, null);
225       setPropI("modelIndex", Integer.valueOf(imodel), null);
226       setPropI("fileName", "cache://isosurface_" + id, null);
227       setPropI("readFile", null, null);
228       setPropI("finalize",
229           "isosurface ID " + PT.esc(id)
230               + (imodel >= 0 ? " modelIndex " + imodel : "") + " /*file*/"
231               + PT.esc("cache://isosurface_" + id),
232           null);
233       setPropI("clear", null, null);
234       return;
235     }
236     if ("delete" == propertyName) {
237       setPropertySuper(propertyName, value, bs);
238       if (!explicitID)
239         nLCAO = nUnnamed = 0;
240       currentMesh = thisMesh = null;
241       return;
242     }
243 
244     if ("remapInherited" == propertyName) {
245       for (int i = meshCount; --i >= 0;) {
246         if (isomeshes[i] != null
247             && "#inherit;".equals(isomeshes[i].colorCommand))
248           isomeshes[i].remapColors(vwr, null, Float.NaN);
249       }
250       return;
251     }
252 
253     if ("remapColor" == propertyName) {
254       if (thisMesh != null)
255         thisMesh.remapColors(vwr, (ColorEncoder) value, translucentLevel);
256       return;
257     }
258 
259     if ("thisID" == propertyName) {
260       if (actualID != null)
261         value = actualID;
262       setPropertySuper("thisID", value, null);
263       return;
264     }
265 
266     if ("params" == propertyName) {
267       if (thisMesh != null) {
268         ensureMeshSource();
269         thisMesh.checkAllocColixes();
270         Object[] data = (Object[]) value;
271         short[] colixes = (short[]) data[0];
272         int[] atomMap = null;
273         //float[] atrans = (float[]) data[1];
274         if (colixes != null) {
275           for (int i = 0; i < colixes.length; i++) {
276             short colix = colixes[i];
277             float f = 0;//(atrans == null ? 0 : atrans[pt]);
278             if (f > 0.01f)
279               colix = C.getColixTranslucent3(colix, true, f);
280             colixes[i] = colix;
281           }
282           atomMap = new int[bs.length()];
283           for (int pt = 0, i = bs.nextSetBit(0); i >= 0; i = bs
284               .nextSetBit(i + 1), pt++)
285             atomMap[i] = pt;
286         }
287         thisMesh.setVertexColixesForAtoms(vwr, colixes, atomMap, bs);
288         thisMesh.setVertexColorMap();
289       }
290       return;
291     }
292     if ("atomcolor" == propertyName) {
293       // color $id red ({0:30 ....})  (atoms)
294       // color $id red [{0:30 ....}]  (vertices)
295       if (thisMesh != null) {
296         ensureMeshSource();
297         thisMesh.colorVertices(C.getColixO(value), bs, true);
298       }
299       return;
300     }
301 
302     if ("pointSize" == propertyName) {
303       if (thisMesh != null) {
304         thisMesh.volumeRenderPointSize = ((Float) value).floatValue();
305       }
306       return;
307     }
308 
309     if ("vertexcolor" == propertyName) {
310       if (thisMesh != null) {
311         thisMesh.colorVertices(C.getColixO(value), bs, false);
312       }
313       return;
314     }
315 
316     if ("colorPhase" == propertyName) {
317       // from color isosurface phase color1 color2  Jmol 12.3.5
318       Object[] colors = (Object[]) value;
319       short colix0 = C.getColix(((Integer) colors[0]).intValue());
320       short colix1 = C.getColix(((Integer) colors[1]).intValue());
321       String id = (thisMesh != null ? thisMesh.thisID
322           : PT.isWild(previousMeshID) ? previousMeshID : null);
323       Lst<Mesh> list = getMeshList(id, false);
324       for (int i = list.size(); --i >= 0;)
325         setColorPhase((IsosurfaceMesh) list.get(i), colix0, colix1);
326       return;
327     }
328     if ("color" == propertyName) {
329       String color = C.getHexCode(C.getColixO(value));
330       if (thisMesh != null) {
331         setIsoMeshColor(thisMesh, color);
332       } else {
333         Lst<Mesh> list = getMeshList(
334             PT.isWild(previousMeshID) ? previousMeshID : null, false);
335         for (int i = list.size(); --i >= 0;)
336           setIsoMeshColor((IsosurfaceMesh) list.get(i), color);
337       }
338       setPropertySuper(propertyName, value, bs);
339       return;
340     }
341 
342     if ("nocontour" == propertyName) {
343       // recontouring
344       if (thisMesh != null) {
345         thisMesh.deleteContours();
346       }
347       return;
348     }
349     if ("fixed" == propertyName) {
350       isFixed = ((Boolean) value).booleanValue();
351       setMeshI();
352       return;
353     }
354 
355     if ("newObject" == propertyName) {
356       if (thisMesh != null)
357         thisMesh.clearType(thisMesh.meshType, false);
358       return;
359     }
360 
361     if ("moveIsosurface" == propertyName) {
362       if (thisMesh != null && !thisMesh.isModelConnected) {
363         thisMesh.updateCoordinates((M4) value, null);
364         thisMesh.altVertices = null;
365       }
366       return;
367     }
368 
369     if ("refreshTrajectories" == propertyName) {
370       int m = ((Integer) ((Object[]) value)[0]).intValue();
371       for (int i = meshCount; --i >= 0;)
372         if (meshes[i].modelIndex == m
373             && (meshes[i].connectedAtoms != null || meshes[i].isModelConnected))
374           ((IsosurfaceMesh) meshes[i]).updateCoordinates(
375               (M4) ((Object[]) value)[2], (BS) ((Object[]) value)[1]);
376       return;
377     }
378 
379     if ("modelIndex" == propertyName) {
380       if (!iHaveModelIndex) {
381         modelIndex = ((Integer) value).intValue();
382         isFixed = (modelIndex < 0);
383         sg.params.modelIndex = Math.abs(modelIndex);
384       }
385       return;
386     }
387 
388     if ("lcaoCartoon" == propertyName || "lonePair" == propertyName
389         || "radical" == propertyName) {
390       // z x center rotationAxis (only one of x, y, or z is nonzero; in radians)
391       V3[] info = (V3[]) value;
392       if (!explicitID) {
393         setPropertySuper("thisID", null, null);
394       }
395       // center (info[2]) is set in SurfaceGenerator
396       if (!sg.setProp("lcaoCartoonCenter", info[2], null))
397         drawLcaoCartoon(info[0], info[1], info[3],
398             ("lonePair" == propertyName ? 2
399                 : "radical" == propertyName ? 1 : 0));
400       return;
401     }
402 
403     if ("select" == propertyName) {
404       if (iHaveBitSets)
405         return;
406     }
407 
408     if ("ignore" == propertyName) {
409       if (iHaveBitSets)
410         return;
411     }
412 
413     if ("meshcolor" == propertyName) {
414       int rgb = ((Integer) value).intValue();
415       meshColix = C.getColix(rgb);
416       if (thisMesh != null)
417         thisMesh.meshColix = meshColix;
418       return;
419     }
420 
421     if ("offset" == propertyName) {
422       P3 offset = P3.newP((P3) value);
423       if (offset.equals(JC.center))
424         offset = null;
425       if (thisMesh != null) {
426         thisMesh.rotateTranslate(null, offset, true);
427         thisMesh.altVertices = null;
428       }
429       return;
430     }
431 
432     if ("rotate" == propertyName) {
433       P4 pt4 = (P4) value;
434       if (thisMesh != null) {
435         thisMesh.rotateTranslate(Quat.newP4(pt4), null, true);
436         thisMesh.altVertices = null;
437       }
438       return;
439     }
440 
441     if ("bsDisplay" == propertyName) {
442       bsDisplay = (BS) value;
443       return;
444     }
445     if ("displayWithin" == propertyName) {
446       Object[] o = (Object[]) value;
447       displayWithinDistance2 = ((Float) o[0]).floatValue();
448       isDisplayWithinNot = (displayWithinDistance2 < 0);
449       displayWithinDistance2 *= displayWithinDistance2;
450       displayWithinPoints = (Lst<P3>) o[3];
451       if (displayWithinPoints.size() == 0)
452         displayWithinPoints = vwr.ms.getAtomPointVector((BS) o[2]);
453       return;
454     }
455 
456     if ("finalize" == propertyName) {
457       if (thisMesh != null) {
458         String cmd = (String) value;
459         if (cmd != null && !cmd.startsWith("; isosurface map")) {
460           thisMesh.setDiscreteColixes(sg.params.contoursDiscrete,
461               sg.params.contourColixes);
462           setJvxlInfo();
463         }
464         setScriptInfo(cmd);
465       }
466       clearSg();
467       return;
468     }
469 
470     if ("connections" == propertyName) {
471       if (currentMesh != null) {
472         connections = (int[]) value;
473         if (connections[0] >= 0 && connections[0] < vwr.ms.ac)
474           currentMesh.connectedAtoms = connections;
475         else
476           connections = currentMesh.connectedAtoms = null;
477       }
478       return;
479     }
480 
481     if ("cutoffRange" == propertyName) {
482       cutoffRange = (float[]) value;
483       return;
484     }
485 
486     if ("fixLattice" == propertyName) {
487       if (thisMesh != null)
488         thisMesh.fixLattice();
489       return;
490     }
491 
492     // Isosurface / SurfaceGenerator both interested
493 
494     if ("slab" == propertyName) {
495       if (value instanceof Integer) {
496         if (thisMesh != null)
497           thisMesh.jvxlData.slabValue = ((Integer) value).intValue();
498         return;
499       }
500       if (thisMesh != null) {
501         Object[] slabInfo = (Object[]) value;
502         int tok = ((Integer) slabInfo[0]).intValue();
503         switch (tok) {
504         case T.mesh:
505           Object[] data = (Object[]) slabInfo[1];
506           Mesh m = getMesh((String) data[1]);
507           if (m == null)
508             return;
509           data[1] = m;
510           break;
511         }
512         slabPolygons(slabInfo);
513         return;
514       }
515     }
516 
517     if ("cap" == propertyName) {
518       if (thisMesh != null && thisMesh.pc != 0) {
519         thisMesh.getMeshSlicer().slabPolygons((Object[]) value, true);
520         thisMesh.initialize(thisMesh.lighting, null, null);
521         return;
522       }
523     }
524     if ("map" == propertyName) {
525       if (sg != null)
526         sg.params.isMapped = true;
527       setProperty("squareData", Boolean.FALSE, null);
528       if (thisMesh == null || thisMesh.vc == 0)
529         return;
530     }
531 
532     if ("probes" == propertyName) {
533       if (sg != null) {
534         sg.params.probes = (P3[]) value;
535         sg.params.probeValues = new float[sg.params.probes.length];
536       }
537       return;
538     }
539 
540     if ("deleteVdw" == propertyName) {
541       for (int i = meshCount; --i >= 0;)
542         if (isomeshes[i].bsVdw != null
543             && (bs == null || bs.intersects(isomeshes[i].bsVdw)))
544           deleteMeshI(i);
545       currentMesh = thisMesh = null;
546       return;
547     }
548     if ("mapColor" == propertyName || "readFile" == propertyName) {
549       if (value == null) {
550         // ScriptEvaluator has passed the filename to us as the value of the
551         // "fileName" property. We retrieve that from the surfaceGenerator
552         // and open a BufferedReader for it. Or not. But that would be
553         // unlikely since we have just checked it in ScriptEvaluator
554 
555         if (sg.params.filesData == null) {
556           value = getFileReader(sg.params.fileName);
557         } else {
558           value = sg.params.filesData;
559           String[] a = (String[]) sg.params.filesData[0];
560           Object[] b = new Object[a.length];
561           for (int i = b.length; --i >= 0 && value != null;)
562             if ((b[i] = getFileReader(a[i])) == null)
563               value = null;
564           if (value != null)
565             sg.params.filesData[0] = b;
566         }
567         if (value == null)
568           return;
569       }
570     } else if ("atomIndex" == propertyName) {
571       atomIndex = ((Integer) value).intValue();
572       if (thisMesh != null)
573         thisMesh.atomIndex = atomIndex;
574     } else if ("center" == propertyName) {
575       center.setT((P3) value);
576     } else if ("colorRGB" == propertyName) {
577       int rgb = ((Integer) value).intValue();
578       if (rgb == T.symop) {
579         colorType = rgb;
580       } else {
581         colorType = 0;
582         defaultColix = C.getColix(rgb);
583       }
584     } else if ("contour" == propertyName) {
585       explicitContours = true;
586     } else if ("functionXY" == propertyName) {
587       //allowContourLines = false;
588       if (sg.params.state == Parameters.STATE_DATA_READ)
589         setScriptInfo(null); // for script DATA1
590     } else if ("init" == propertyName) {
591       newSg();
592     } else if ("getSurfaceSets" == propertyName) {
593       if (thisMesh != null) {
594         BS bsSets;
595         if (value instanceof BS) {
596           bsSets = ((BS) value);
597           if (bsSets.cardinality() == 0)
598             bsSets = null;
599         } else {
600           bsSets = new BS();
601           int[] a = (int[]) value;
602           for (int i = a.length; --i >= 0;) {
603             if (a[i] > 0)
604               bsSets.set(a[i] - 1);
605           }
606         }
607         thisMesh.jvxlData.thisSet = bsSets;
608         thisMesh.calculatedVolume = null;
609         thisMesh.calculatedArea = null;
610       }
611     } else if ("localName" == propertyName) {
612       value = vwr.getOutputChannel((String) value, null);
613       propertyName = "outputChannel";
614     } else if ("molecularOrbital" == propertyName) {
615       isFixed = false;
616       setMeshI();
617       if (value instanceof Integer) {
618         moNumber = ((Integer) value).intValue();
619         moLinearCombination = null;
620       } else {
621         moLinearCombination = (float[]) value;
622         moNumber = 0;
623       }
624       if (!isColorExplicit)
625         isPhaseColored = true;
626       if (sg == null || !sg.params.isMapped) {
627         M4 mat4 = ms.am[currentMesh.modelIndex].mat4;
628         if (mat4 != null) {
629           M4 minv = M4.newM4(mat4);
630           minv.invert();
631           setPropI("modelInvRotation", minv, null);
632         }
633       }
634     } else if ("phase" == propertyName) {
635       isPhaseColored = true;
636     } else if ("plane" == propertyName) {
637       //allowContourLines = false;
638     } else if ("pocket" == propertyName) {
639       // Boolean pocket = (Boolean) value;
640       // lighting = (pocket.booleanValue() ? JmolConstants.FULLYLIT
641       //     : JmolConstants.FRONTLIT);
642     } else if ("scale3d" == propertyName) {
643       scale3d = ((Float) value).floatValue();
644       if (thisMesh != null) {
645         thisMesh.scale3d = thisMesh.jvxlData.scale3d = scale3d;
646         thisMesh.altVertices = null;
647       }
648     } else if ("title" == propertyName) {
649       if (value instanceof String && "-".equals(value))
650         value = null;
651       setPropertySuper(propertyName, value, bs);
652       value = title;
653     } else if ("withinPoints" == propertyName) {
654       Object[] o = (Object[]) value;
655       withinDistance2 = ((Float) o[0]).floatValue();
656       isWithinNot = (withinDistance2 < 0);
657       withinDistance2 *= withinDistance2;
658       withinPoints = (Lst<P3>) o[3];
659       if (withinPoints.size() == 0)
660         withinPoints = vwr.ms.getAtomPointVector((BS) o[2]);
661     } else if (("nci" == propertyName || "orbital" == propertyName)
662         && sg != null) {
663       sg.params.testFlags = (vwr.getBoolean(T.testflag2) ? 2 : 0);
664     }
665 
666     // surface Export3D only (return TRUE) or shared (return FALSE)
667 
668     if (sg != null && sg.setProp(propertyName, value, bs)) {
669       if (sg.isValid) {
670         if ("molecularOrbital" == propertyName) {
671           currentMesh.isModelConnected = true;
672           currentMesh.mat4 = ms.am[currentMesh.modelIndex].mat4;
673         }
674         return;
675       }
676       propertyName = "delete";
677     }
678 
679     // ///////////// isosurface LAST, shared
680 
681     if ("init" == propertyName) {
682       explicitID = false;
683       scriptAppendix = "";
684       String script = (value instanceof String ? (String) value : null);
685       int pt = (script == null ? -1 : script.indexOf("# ID="));
686       actualID = (pt >= 0 ? PT.getQuotedStringAt(script, pt) : null);
687       setPropertySuper("thisID", MeshCollection.PREVIOUS_MESH_ID, null);
688       if (script != null && !(iHaveBitSets = getScriptBitSets(script, null)))
689         sg.setProp("select", bs, null);
690       initializeIsosurface();
691       sg.params.modelIndex = (isFixed ? -1 : modelIndex);
692       return;
693     }
694 
695     if ("clear" == propertyName) {
696       discardTempData(true);
697       return;
698     }
699 
700     if ("colorDensity" == propertyName) {
701       if (value != null && currentMesh != null)
702         currentMesh.volumeRenderPointSize = ((Float) value).floatValue();
703       return;
704     }
705     /*
706      * if ("background" == propertyName) { boolean doHide = !((Boolean)
707      * value).booleanValue(); if (thisMesh != null) thisMesh.hideBackground =
708      * doHide; else { for (int i = meshCount; --i >= 0;)
709      * meshes[i].hideBackground = doHide; } return; }
710      */
711 
712     if (propertyName == "deleteModelAtoms") {
713       int modelIndex = ((int[]) ((Object[]) value)[2])[0];
714       int firstAtomDeleted = ((int[]) ((Object[]) value)[2])[1];
715       int nAtomsDeleted = ((int[]) ((Object[]) value)[2])[2];
716       for (int i = meshCount; --i >= 0;) {
717         Mesh m = meshes[i];
718         if (m == null)
719           continue;
720         if (m.connectedAtoms != null) {
721           int iAtom = m.connectedAtoms[0];
722           if (iAtom >= firstAtomDeleted + nAtomsDeleted)
723             m.connectedAtoms[0] = iAtom - nAtomsDeleted;
724           else if (iAtom >= firstAtomDeleted)
725             m.connectedAtoms = null;
726         }
727         m.connectedAtoms = null; // just no way to
728         if (m.modelIndex == modelIndex) {
729           meshCount--;
730           if (m == currentMesh)
731             currentMesh = thisMesh = null;
732           meshes = isomeshes = (IsosurfaceMesh[]) AU.deleteElements(meshes, i,
733               1);
734         } else if (m.modelIndex > modelIndex) {
735           m.modelIndex--;
736           if (m.atomIndex >= firstAtomDeleted)
737             m.atomIndex -= nAtomsDeleted;
738         }
739       }
740       return;
741     }
742 
743     // processing by meshCollection:
744     setPropertySuper(propertyName, value, bs);
745   }
746 
getFileReader(String fileName)747   private Object getFileReader(String fileName) {
748     Object value = vwr.fm.getBufferedReaderOrErrorMessageFromName(
749         fileName, null, true, true);
750     if (value instanceof String) {
751       Logger.error("Isosurface: could not open file " + fileName
752           + " -- " + value);
753       return null;
754     }
755     if (!(value instanceof BufferedReader))
756       try {
757         value = Rdr.getBufferedReader((BufferedInputStream) value,
758             "ISO-8859-1");
759       } catch (IOException e) {
760         // ignore
761       }
762     return value;
763   }
764 
setIsoMeshColor(IsosurfaceMesh m, String color)765   private void setIsoMeshColor(IsosurfaceMesh m, String color) {
766     // thisMesh.vertexColixes = null;
767     m.jvxlData.baseColor = color;
768     m.isColorSolid = true;
769     m.pcs = null;
770     m.colorsExplicit = false;
771     m.colorEncoder = null;
772     m.vertexColorMap = null;
773   }
774 
setColorPhase(IsosurfaceMesh m, short colix0, short colix1)775   private void setColorPhase(IsosurfaceMesh m, short colix0,
776                              short colix1) {
777     m.colorPhased = true;
778     m.colix = m.jvxlData.minColorIndex = colix0;
779     m.jvxlData.maxColorIndex = colix1;
780     m.jvxlData.isBicolorMap = true;
781     m.jvxlData.colorDensity = false;
782     m.isColorSolid = false;
783     m.remapColors(vwr, null, translucentLevel);
784   }
785 
ensureMeshSource()786   private void ensureMeshSource() {
787     boolean haveColors = (thisMesh.vertexSource != null);
788     if (haveColors)
789       for (int i = thisMesh.vc; --i >= 0;)
790         if (thisMesh.vertexSource[i] < 0) {
791           haveColors = false;
792           break;
793         }
794     if (!haveColors) {
795       int[] source = thisMesh.vertexSource;
796       short[] vertexColixes = thisMesh.vcs;
797       short colix = (thisMesh.isColorSolid ? thisMesh.colix : 0);
798       setProperty("init", null, null);
799       setProperty("map", Boolean.FALSE, null);
800       setProperty("property", new float[vwr.ms.ac], null);
801       if (colix != 0) {
802         thisMesh.colorCommand = "color isosurface "
803             + C.getHexCode(colix);
804         setProperty("color", Integer.valueOf(C.getArgb(colix)), null);
805       }
806       if (source != null) {
807         for (int i = thisMesh.vc; --i >= 0;)
808           if (source[i] < 0)
809             source[i] = thisMesh.vertexSource[i];
810         thisMesh.vertexSource = source;
811         thisMesh.vcs = vertexColixes;
812       }
813     }
814   }
815 
slabPolygons(Object[] slabInfo)816   protected void slabPolygons(Object[] slabInfo) {
817     thisMesh.calculatedVolume = null;
818     thisMesh.calculatedArea = null;
819     thisMesh.getMeshSlicer().slabPolygons(slabInfo, false);
820     thisMesh.reinitializeLightingAndColor(vwr);
821   }
822 
setPropertySuper(String propertyName, Object value, BS bs)823   private void setPropertySuper(String propertyName, Object value, BS bs) {
824     if (propertyName == "thisID" && currentMesh != null
825         && currentMesh.thisID != null && currentMesh.thisID.equals(value)) {
826       checkExplicit((String) value);
827       return;
828     }
829     currentMesh = thisMesh;
830     setPropMC(propertyName, value, bs);
831     thisMesh = (IsosurfaceMesh) currentMesh;
832     jvxlData = (thisMesh == null ? null : thisMesh.jvxlData);
833     if (sg != null)
834       sg.setJvxlData(jvxlData);
835   }
836 
837 
838   @SuppressWarnings("unchecked")
839   @Override
getPropertyData(String property, Object[] data)840   public boolean getPropertyData(String property, Object[] data) {
841     IsosurfaceMesh m;
842     if (property == "keys") {
843       Lst<String> keys = (data[1] instanceof Lst<?> ? (Lst<String>) data[1] : new Lst<String>());
844       data[1] = keys;
845       keys.addLast("info");
846       keys.addLast("data");
847       keys.addLast("atoms");
848       // will continue on to super
849     }
850     if (property == "colorEncoder") {
851       m = (IsosurfaceMesh) getMesh((String) data[0]);
852       return (m != null && (data[1] = m.colorEncoder) != null);
853     }
854     if (property == "intersectPlane") {
855       m = (IsosurfaceMesh) getMesh((String) data[0]);
856       if (m == null || data.length < 4)
857         return false;
858       data[3] = Integer.valueOf(m.modelIndex);
859       m.getMeshSlicer().getIntersection(0, (P4) data[1], null, (Lst<P3[]>) data[2], null, null, null, false, false, T.plane, false);
860       return true;
861     }
862     if (property == "getBoundingBox") {
863       String id = (String) data[0];
864       m = (IsosurfaceMesh) getMesh(id);
865       if (m == null || m.vs == null)
866         return false;
867       data[2] = m.jvxlData.boundingBox;
868       if (m.mat4 != null) {
869         P3[] d = new P3[2];
870         d[0] = P3.newP(m.jvxlData.boundingBox[0]);
871         d[1] = P3.newP(m.jvxlData.boundingBox[1]);
872         V3 v = new V3();
873         m.mat4.getTranslation(v);
874         d[0].add(v);
875         d[1].add(v);
876         data[2] = d;
877       }
878       return true;
879     }
880     if (property == "unitCell") {
881       m = (IsosurfaceMesh) getMesh((String) data[0]);
882       return (m != null && (data[1] = m.getUnitCell()) != null);
883     }
884     if (property == "getCenter") {
885       int index = ((Integer)data[1]).intValue();
886       if (index == Integer.MIN_VALUE) {
887         String id = (String) data[0];
888         m = (IsosurfaceMesh) getMesh(id);
889         if (m == null || m.vs == null)
890           return false;
891         P3 p = P3.newP(m.jvxlData.boundingBox[0]);
892         p.add(m.jvxlData.boundingBox[1]);
893         p.scale(0.5f);
894         if (m.mat4 != null) {
895           V3 v = new V3();
896           m.mat4.getTranslation(v);
897           p.add(v);
898         }
899         data[2] = p;
900         return true;
901       }
902       // continue to super
903     }
904 
905     return getPropDataMC(property, data);
906   }
907 
908   @Override
getProperty(String property, int index)909   public Object getProperty(String property, int index) {
910     return getPropI(property, index);
911   }
912 
getPropI(String property, int index)913   protected Object getPropI(String property, int index) {
914     IsosurfaceMesh m = thisMesh;
915     if (index >= 0 && (index >= meshCount || (m = isomeshes[index]) == null))
916       return null;
917     Object ret = getPropMC(property, index);
918     if (ret != null)
919       return ret;
920     if (property == "message") {
921       String s = "";
922       if (!jvxlData.isValid)
923         return "invalid! (no atoms selected?)";
924       if (!Float.isNaN(jvxlData.integration))
925         s += "integration " + jvxlData.integration;
926       if (shapeID == JC.SHAPE_ISOSURFACE || shapeID == JC.SHAPE_MO  || shapeID == JC.SHAPE_NBO)
927         s += " with cutoff=" + jvxlData.cutoff;
928       if (shapeID == JC.SHAPE_MO || shapeID == JC.SHAPE_NBO)
929         return s;
930       if (jvxlData.dataMin != Float.MAX_VALUE)
931         s += " min=" + jvxlData.dataMin + " max=" + jvxlData.dataMax;
932 
933       s += "; " + JC.shapeClassBases[shapeID].toLowerCase() + " count: "
934           + getPropMC("count", index);
935       return s + getPropI("dataRangeStr", index) + jvxlData.msg;
936     }
937     if (property == "dataRange")
938       return getDataRange(m);
939     if (property == "dataRangeStr") {
940       float[] dataRange = getDataRange(m);
941       return (dataRange != null && dataRange[0] != Float.MAX_VALUE
942           && dataRange[0] != dataRange[1] ? "\nisosurface"
943           + " full data range " + dataRange[0] + " to " + dataRange[1]
944           + " with color scheme spanning " + dataRange[2] + " to " + dataRange[3]
945           : "");
946     }
947     if (property == "moNumber")
948       return Integer.valueOf(moNumber);
949     if (property == "moLinearCombination")
950       return moLinearCombination;
951     if (property == "nSets") {
952       int n = (m == null ? Integer.MIN_VALUE : m.nSets);
953       if (n == 0) {
954         calculateVolumeOrArea(m, true);
955         n = m.nSets;
956       }
957       return Integer.valueOf(n == Integer.MIN_VALUE ? 0 : Math.abs(m.nSets));
958     }
959     if (property == "area") // could be Float or double[]
960       return (m == null ? Float.valueOf(Float.NaN) : calculateVolumeOrArea(m, true));
961     if (property == "volume") // could be Float or double[]
962       return (m == null ? Float.valueOf(Float.NaN) : calculateVolumeOrArea(m, false));
963     if (m == null)
964       return null;//"no current isosurface";
965     if (property == "output") {
966       return (m.jvxlData.sbOut == null && m.jvxlData.jvxlFileTitle == null ? null :
967         m.jvxlData.jvxlFileTitle + "\n" + (m.jvxlData.sbOut == null ? "" : m.jvxlData.sbOut.toString()));
968     }
969     if (property == "cutoff")
970       return Float.valueOf(jvxlData.cutoff);
971     if (property == "minMaxInfo")
972       return new float[] { jvxlData.dataMin, jvxlData.dataMax };
973     if (property == "plane")
974       return jvxlData.jvxlPlane;
975     if (property == "contours")
976       return m.getContours();
977     if (property == "pmesh" || property == "pmeshbin")
978       return m.getPmeshData(property == "pmeshbin");
979     if (property == "jvxlDataXml" || property == "jvxlMeshXml") {
980       MeshData meshData = null;
981       jvxlData.slabInfo = null;
982       if (property == "jvxlMeshXml" || jvxlData.vertexDataOnly || m.bsSlabDisplay != null && m.bsSlabGhost == null) {
983         meshData = new MeshData();
984         fillMeshData(meshData, MeshData.MODE_GET_VERTICES, m);
985         meshData.polygonColorData = getPolygonColorData(meshData.pc, meshData.pcs, (meshData.colorsExplicit ? meshData.pis : null), meshData.bsSlabDisplay);
986       } else if (m.bsSlabGhost != null) {
987         jvxlData.slabInfo = m.slabOptions.toString();
988       }
989       SB sb = new SB();
990       getMeshCommand(sb, m.index);
991       m.setJvxlColorMap(true);
992       return JvxlCoder.jvxlGetFile(jvxlData, meshData, title, "", true, 1, sb.toString(), null);
993     }
994     if (property == "jvxlFileInfo") {
995       return JvxlCoder.jvxlGetInfo(jvxlData);
996     }
997     if (property == "command") {
998       SB sb = new SB();
999       Lst<Mesh> list = getMeshList((index < 0 ? previousMeshID : m.thisID), false);
1000       for (int i = list.size(); --i >= 0;)
1001          getMeshCommand(sb, i);
1002       return sb.toString();
1003     }
1004     if (property == "atoms") {
1005       return m.surfaceAtoms;
1006     }
1007     if (property == "colorEncoder")
1008       return m.colorEncoder;
1009     if (property == "values" || property == "value") {
1010       return m.probeValues;
1011     }
1012 
1013     return null;
1014   }
1015 
getDataRange(IsosurfaceMesh mesh)1016   private float[] getDataRange(IsosurfaceMesh mesh) {
1017     return (mesh == null ? null : mesh.getDataRange());
1018   }
1019 
calculateVolumeOrArea(IsosurfaceMesh mesh, boolean isArea)1020   private Object calculateVolumeOrArea(IsosurfaceMesh mesh, boolean isArea) {
1021     if (isArea) {
1022       if (mesh.calculatedArea != null)
1023         return mesh.calculatedArea;
1024     } else {
1025       if (mesh.calculatedVolume != null)
1026         return mesh.calculatedVolume;
1027     }
1028     MeshData meshData = new MeshData();
1029     fillMeshData(meshData, MeshData.MODE_GET_VERTICES, mesh);
1030     meshData.nSets = mesh.nSets;
1031     meshData.vertexSets = mesh.vertexSets;
1032     if (!isArea && mesh.jvxlData.colorDensity) {
1033       float f = mesh.jvxlData.voxelVolume;
1034       f *= (mesh.bsSlabDisplay == null ? mesh.vc : mesh.bsSlabDisplay.cardinality());
1035       return  mesh.calculatedVolume = Float.valueOf(f);
1036     }
1037     Object ret = MeshData.calculateVolumeOrArea(meshData, mesh.jvxlData.thisSet, isArea, false);
1038     if (mesh.nSets <= 0)
1039       mesh.nSets = -meshData.nSets;
1040     if (isArea)
1041       mesh.calculatedArea = ret;
1042     else
1043       mesh.calculatedVolume = ret;
1044     return ret;
1045   }
1046 
getPolygonColorData(int ccount, short[] colixes, int[][] polygons, BS bsSlabDisplay)1047   public static String getPolygonColorData(int ccount, short[] colixes, int[][] polygons, BS bsSlabDisplay) {
1048     boolean isExplicit = (polygons != null);
1049     if (colixes == null && polygons == null)
1050       return null;
1051     SB list1 = new SB();
1052     int count = 0;
1053     short colix = 0;
1054     int color = 0, colorNext = 0;
1055     boolean done = false;
1056     for (int i = 0; i < ccount || (done = true) == true; i++) {
1057       if (!done && bsSlabDisplay != null && !bsSlabDisplay.get(i))
1058         continue;
1059       if (done || (isExplicit ? (colorNext = polygons[i][MeshSurface.P_EXPLICIT_COLOR]) != color : colixes[i] != colix)) {
1060         if (count != 0)
1061           list1.append(" ").appendI(count).append(" ").appendI(
1062               (isExplicit ? color : colix == 0 ? 0 : C.getArgb(colix)));
1063         if (done)
1064           break;
1065         if (isExplicit)
1066           color = colorNext;
1067         else
1068           colix = colixes[i];
1069         count = 1;
1070       } else {
1071         count++;
1072       }
1073     }
1074     list1.append("\n");
1075     return list1.toString();
1076   }
1077 
1078   @Override
getShapeState()1079   public String getShapeState() {
1080     clean();
1081     SB sb = new SB();
1082     sb.append("\n");
1083     for (int i = 0; i < meshCount; i++)
1084       getMeshCommand(sb, i);
1085     return sb.toString();
1086   }
1087 
getMeshCommand(SB sb, int i)1088   private void getMeshCommand(SB sb, int i) {
1089     IsosurfaceMesh imesh = (IsosurfaceMesh) meshes[i];
1090     if (imesh == null || imesh.scriptCommand == null)
1091       return;
1092     String cmd = imesh.scriptCommand;
1093     int modelCount = vwr.ms.mc;
1094     if (modelCount > 1)
1095       appendCmd(sb, "frame " + vwr.getModelNumberDotted(imesh.modelIndex));
1096     cmd = PT.rep(cmd, ";; isosurface map"," map");
1097     cmd = PT.rep(cmd, "; isosurface map", " map");
1098     if (cmd.endsWith(" map")) // isosurface map colorscheme "rwb" bug
1099       cmd = cmd.substring(0, cmd.length() - 4);
1100     cmd = cmd.replace('\t', ' ');
1101     cmd = PT.rep(cmd, ";#", "; #");
1102     int pt = cmd.indexOf("; #");
1103     if (pt >= 0)
1104       cmd = cmd.substring(0, pt);
1105     if (imesh.connectedAtoms != null)
1106       cmd += " connect " + Escape.eAI(imesh.connectedAtoms);
1107     cmd = PT.trim(cmd, ";");
1108     if (imesh.linkedMesh != null)
1109       cmd += " LINK"; // for lcaoCartoon state
1110     if (myType == "lcaoCartoon" && imesh.atomIndex >= 0)
1111       cmd += " ATOMINDEX " + imesh.atomIndex;
1112     appendCmd(sb, cmd);
1113     String id = myType + " ID " + PT.esc(imesh.thisID);
1114     if (imesh.jvxlData.thisSet != null && imesh.jvxlData.thisSet.cardinality() > 0) {
1115       appendCmd(sb, id + (imesh.jvxlData.thisSet.cardinality() == 1 ? " set " + (imesh.jvxlData.thisSet.nextSetBit(0)+1)
1116       : " subset " + imesh.jvxlData.thisSet));
1117     }
1118     if (imesh.mat4 != null && !imesh.isModelConnected)
1119       appendCmd(sb, id + " move " + Escape.matrixToScript(imesh.mat4));
1120     if (imesh.scale3d != 0)
1121       appendCmd(sb, id + " scale3d " + imesh.scale3d);
1122     if (imesh.jvxlData.slabValue != Integer.MIN_VALUE)
1123       appendCmd(sb, id + " slab " + imesh.jvxlData.slabValue);
1124     if (imesh.slabOptions != null)
1125       appendCmd(sb, imesh.slabOptions.toString());
1126     if (cmd.charAt(0) != '#') {
1127       if (allowMesh)
1128         appendCmd(sb, imesh.getState(myType));
1129       if (!imesh.isColorSolid && imesh.colorType == 0 && C.isColixTranslucent(imesh.colix))
1130         appendCmd(sb, "color " + myType + " " + getTranslucentLabel(imesh.colix));
1131       if (imesh.colorCommand != null && imesh.colorType == 0 && !imesh.colorCommand.equals("#inherit;")) {
1132         appendCmd(sb, imesh.colorCommand);
1133       }
1134       boolean colorArrayed = (imesh.isColorSolid && imesh.pcs != null);
1135       if (imesh.isColorSolid && imesh.colorType == 0 && !imesh.colorsExplicit && !colorArrayed) {
1136         appendCmd(sb, getColorCommandUnk(myType, imesh.colix, translucentAllowed));
1137       } else if (imesh.jvxlData.isBicolorMap && imesh.colorPhased) {
1138         appendCmd(sb, "color isosurface phase "
1139             + encodeColor(imesh.jvxlData.minColorIndex) + " "
1140             + encodeColor(imesh.jvxlData.maxColorIndex));
1141       }
1142       if (imesh.vertexColorMap != null)
1143         for (Map.Entry<String, BS> entry : imesh.vertexColorMap.entrySet()) {
1144           BS bs = entry.getValue();
1145           if (!bs.isEmpty())
1146             appendCmd(sb, "color " + myType + " " + Escape.eBS(bs)
1147                 + " " + entry.getKey());
1148         }
1149     }
1150   }
1151 
1152 
1153   private String script;
1154 
getScriptBitSets(String script, BS[] bsCmd)1155   private boolean getScriptBitSets(String script, BS[] bsCmd) {
1156     this.script = script;
1157     int i;
1158     iHaveModelIndex = false;
1159     modelIndex = -1;
1160     if (script != null && (i = script.indexOf("MODEL({")) >= 0) {
1161       int j = script.indexOf("})", i);
1162       if (j > 0) {
1163         BS bs = BS.unescape(script.substring(i + 3, j + 1));
1164         modelIndex = (bs == null ? -1 : bs.nextSetBit(0));
1165         iHaveModelIndex = (modelIndex >= 0);
1166       }
1167     }
1168     if (script == null)
1169       return false;
1170     getCapSlabInfo(script);
1171     i = script.indexOf("# ({");
1172     if (i < 0)
1173       return false;
1174     int j = script.indexOf("})", i);
1175     if (j < 0)
1176       return false;
1177     BS bs = BS.unescape(script.substring(i + 2, j + 2));
1178     if (bsCmd == null)
1179       sg.setProp("select", bs, null);
1180     else
1181       bsCmd[0] = bs;
1182     if ((i = script.indexOf("({", j)) < 0)
1183       return true;
1184     j = script.indexOf("})", i);
1185     if (j < 0)
1186       return false;
1187       bs = BS.unescape(script.substring(i + 1, j + 1));
1188       if (bsCmd == null)
1189         sg.setProp("ignore", bs, null);
1190       else
1191         bsCmd[1] = bs;
1192     if ((i = script.indexOf("/({", j)) == j + 2) {
1193       if ((j = script.indexOf("})", i)) < 0)
1194         return false;
1195       bs = BS.unescape(script.substring(i + 3, j + 1));
1196       if (bsCmd == null)
1197         vwr.ms.setTrajectoryBs(bs);
1198       else
1199         bsCmd[2] = bs;
1200     }
1201     return true;
1202   }
1203 
getCapSlabInfo(String script)1204   protected void getCapSlabInfo(String script) {
1205     int i = script.indexOf("# SLAB=");
1206     if (i >= 0)
1207       sg.setProp("slab", getCapSlabObject(PT.getQuotedStringAt(script, i), false), null);
1208     i = script.indexOf("# CAP=");
1209     if (i >= 0)
1210       sg.setProp("slab", getCapSlabObject(PT.getQuotedStringAt(script, i), true), null);
1211   }
1212 
1213   /**
1214    * legacy -- for some scripts with early isosurface slabbing
1215    *
1216    * @param s
1217    * @param isCap
1218    * @return slabInfo object
1219    */
getCapSlabObject(String s, boolean isCap)1220   private Object[] getCapSlabObject(String s, boolean isCap) {
1221     try {
1222       if (s.indexOf("array") == 0) {
1223         String[] pts = PT.split(s.substring(6, s.length() - 1), ",");
1224         return TempArray.getSlabObjectType(T.boundbox,
1225             new P3[] { (P3) Escape.uP(pts[0]), (P3) Escape.uP(pts[1]),
1226                 (P3) Escape.uP(pts[2]), (P3) Escape.uP(pts[3]) }, isCap, null);
1227       }
1228       Object plane = Escape.uP(s);
1229       if (plane instanceof P4)
1230         return TempArray.getSlabObjectType(T.plane, plane, isCap, null);
1231     } catch (Exception e) {
1232       //
1233     }
1234     return null;
1235   }
1236 
1237 
1238   private boolean iHaveModelIndex;
1239 
initializeIsosurface()1240   private void initializeIsosurface() {
1241     //System.out.println("isosurface initializing " + thisMesh);
1242     if (!iHaveModelIndex)
1243       modelIndex = vwr.am.cmi;
1244     atomIndex = -1;
1245     //allowContourLines = true; //but not for f(x,y) or plane, which use mesh
1246     bsDisplay = null;
1247     center = P3.new3(Float.NaN, 0, 0);
1248     colix = C.ORANGE;
1249     connections = null;
1250     cutoffRange = null;
1251     colorType = defaultColix = meshColix = 0;
1252     displayWithinPoints = null;
1253     explicitContours = false;
1254     isFixed = (modelIndex < 0);
1255     isPhaseColored = isColorExplicit = false;
1256     linkedMesh = null;
1257     if (modelIndex < 0)
1258       modelIndex = 0;
1259     // but note that modelIndex = -1
1260     // is critical for surfaceGenerator. Setting this equal to
1261     // 0 indicates only surfaces for model 0.
1262     scale3d = 0;
1263     title = null;
1264     translucentLevel = 0;
1265     withinPoints = null;
1266     initState();
1267   }
1268 
initState()1269   private void initState() {
1270     associateNormals = true;
1271     sg.initState();
1272     //TODO   need to pass assocCutoff to sg
1273   }
1274 
setMeshI()1275   private void setMeshI() {
1276     thisMesh.visible = true;
1277     if ((thisMesh.atomIndex = atomIndex) >= 0)
1278       thisMesh.modelIndex = vwr.ms.at[atomIndex].mi;
1279     else if (isFixed)
1280       thisMesh.modelIndex = -1;
1281     else if (modelIndex >= 0)
1282       thisMesh.modelIndex = modelIndex;
1283     else
1284       thisMesh.modelIndex = vwr.am.cmi;
1285     thisMesh.scriptCommand = script;
1286     thisMesh.ptCenter.setT(center);
1287     thisMesh.scale3d = (thisMesh.jvxlData.jvxlPlane == null ? 0 : scale3d);
1288 //    if (thisMesh.bsSlabDisplay != null)
1289 //      thisMesh.jvxlData.vertexDataOnly = true;
1290 //      thisMesh.bsSlabDisplay = thisMesh.jvxlData.bsSlabDisplay;
1291   }
1292 
1293   /*
1294    void checkFlags() {
1295    if (vwr.getTestFlag2())
1296    associateNormals = false;
1297    if (!logMessages)
1298    return;
1299    Logger.info("Isosurface using testflag2: no associative grouping = "
1300    + !associateNormals);
1301    Logger.info("IsosurfaceRenderer using testflag4: show vertex normals = "
1302    + vwr.getTestFlag4());
1303    Logger
1304    .info("For grid points, use: isosurface delete myiso gridpoints \"\"");
1305    }
1306    */
1307 
discardTempData(boolean discardAll)1308   protected void discardTempData(boolean discardAll) {
1309     if (!discardAll)
1310       return;
1311     title = null;
1312     if (thisMesh == null)
1313       return;
1314     thisMesh.surfaceSet = null;
1315   }
1316 
1317   ////////////////////////////////////////////////////////////////
1318   // default color stuff (deprecated in 11.2)
1319   ////////////////////////////////////////////////////////////////
1320 
getDefaultColix()1321   private short getDefaultColix() {
1322     if (defaultColix != 0)
1323       return defaultColix;
1324     if (!sg.jvxlData.wasCubic)
1325       return colix; // orange
1326     int argb = (sg.params.cutoff >= 0 ? JC.argbsIsosurfacePositive
1327         : JC.argbsIsosurfaceNegative);
1328     return C.getColix(argb);
1329   }
1330 
1331   ///////////////////////////////////////////////////
1332   ////  LCAO Cartoons  are sets of lobes ////
1333 
1334   private int nLCAO = 0;
1335 
drawLcaoCartoon(V3 z, V3 x, V3 rotAxis, int nElectrons)1336   private void drawLcaoCartoon(V3 z, V3 x, V3 rotAxis, int nElectrons) {
1337     String lcaoCartoon = sg.setLcao();
1338     //really rotRadians is just one of these -- x, y, or z -- not all
1339     float rotRadians = rotAxis.x + rotAxis.y + rotAxis.z;
1340     defaultColix = C.getColix(sg.params.colorPos);
1341     short colixNeg = C.getColix(sg.params.colorNeg);
1342     V3 y = new V3();
1343     boolean isReverse = (lcaoCartoon.length() > 0 && lcaoCartoon.charAt(0) == '-');
1344     if (isReverse)
1345       lcaoCartoon = lcaoCartoon.substring(1);
1346     int sense = (isReverse ? -1 : 1);
1347     y.cross(z, x);
1348     if (rotRadians != 0) {
1349       A4 a = new A4();
1350       if (rotAxis.x != 0)
1351         a.setVA(x, rotRadians);
1352       else if (rotAxis.y != 0)
1353         a.setVA(y, rotRadians);
1354       else
1355         a.setVA(z, rotRadians);
1356       M3 m = new M3().setAA(a);
1357       m.rotate(x);
1358       m.rotate(y);
1359       m.rotate(z);
1360     }
1361     if (thisMesh == null && nLCAO == 0)
1362       nLCAO = meshCount;
1363     String id = (thisMesh == null ? (nElectrons > 0 ? "lp" : "lcao") + (++nLCAO) + "_" + lcaoCartoon
1364         : thisMesh.thisID);
1365     if (thisMesh == null)
1366       allocMesh(id, null);
1367     if (lcaoCartoon.equals("px")) {
1368       thisMesh.thisID += "a";
1369       Mesh meshA = thisMesh;
1370       createLcaoLobe(x, sense, nElectrons);
1371       if (nElectrons > 0)
1372         return;
1373       setProperty("thisID", id + "b", null);
1374       createLcaoLobe(x, -sense, nElectrons);
1375       thisMesh.colix = colixNeg;
1376       linkedMesh = thisMesh.linkedMesh = meshA;
1377       return;
1378     }
1379     if (lcaoCartoon.equals("py")) {
1380       thisMesh.thisID += "a";
1381       Mesh meshA = thisMesh;
1382       createLcaoLobe(y, sense, nElectrons);
1383       if (nElectrons > 0)
1384         return;
1385       setProperty("thisID", id + "b", null);
1386       createLcaoLobe(y, -sense, nElectrons);
1387       thisMesh.colix = colixNeg;
1388       linkedMesh = thisMesh.linkedMesh = meshA;
1389       return;
1390     }
1391     if (lcaoCartoon.equals("pz")) {
1392       thisMesh.thisID += "a";
1393       Mesh meshA = thisMesh;
1394       createLcaoLobe(z, sense, nElectrons);
1395       if (nElectrons > 0)
1396         return;
1397       setProperty("thisID", id + "b", null);
1398       createLcaoLobe(z, -sense, nElectrons);
1399       thisMesh.colix = colixNeg;
1400       linkedMesh = thisMesh.linkedMesh = meshA;
1401       return;
1402     }
1403     if (lcaoCartoon.equals("pza")
1404         || lcaoCartoon.indexOf("sp") == 0
1405         || lcaoCartoon.indexOf("d") == 0
1406         || lcaoCartoon.indexOf("lp") == 0) {
1407       createLcaoLobe(z, sense, nElectrons);
1408       return;
1409     }
1410     if (lcaoCartoon.equals("pzb")) {
1411       createLcaoLobe(z, -sense, nElectrons);
1412       return;
1413     }
1414     if (lcaoCartoon.equals("pxa")) {
1415       createLcaoLobe(x, sense, nElectrons);
1416       return;
1417     }
1418     if (lcaoCartoon.equals("pxb")) {
1419       createLcaoLobe(x, -sense, nElectrons);
1420       return;
1421     }
1422     if (lcaoCartoon.equals("pya")) {
1423       createLcaoLobe(y, sense, nElectrons);
1424       return;
1425     }
1426     if (lcaoCartoon.equals("pyb")) {
1427       createLcaoLobe(y, -sense, nElectrons);
1428       return;
1429     }
1430     if (lcaoCartoon.equals("spacefill") || lcaoCartoon.equals("cpk")) {
1431       createLcaoLobe(null, 2 * vwr.ms.at[atomIndex].getRadius(), nElectrons);
1432       return;
1433     }
1434 
1435     // assume s
1436     createLcaoLobe(null, 1, nElectrons);
1437     return;
1438   }
1439 
1440   private P4 lcaoDir = new P4();
1441 
createLcaoLobe(V3 lobeAxis, float factor, int nElectrons)1442   private void createLcaoLobe(V3 lobeAxis, float factor, int nElectrons) {
1443     initState();
1444     if (Logger.debugging) {
1445       Logger.debug("creating isosurface ID " + thisMesh.thisID);
1446     }
1447     if (lobeAxis == null) {
1448       setProperty("sphere", Float.valueOf(factor / 2f), null);
1449     } else {
1450       lcaoDir.x = lobeAxis.x * factor;
1451       lcaoDir.y = lobeAxis.y * factor;
1452       lcaoDir.z = lobeAxis.z * factor;
1453       lcaoDir.w = 0.7f;
1454       setProperty(nElectrons == 2 ? "lp" : nElectrons == 1 ? "rad" : "lobe",
1455           lcaoDir, null);
1456     }
1457     thisMesh.colix = defaultColix;
1458     setScriptInfo(null);
1459   }
1460 
1461   /////////////// meshDataServer interface /////////////////
1462 
1463   @Override
invalidateTriangles()1464   public void invalidateTriangles() {
1465     thisMesh.invalidatePolygons();
1466   }
1467 
1468   @Override
setOutputChannel(GenericBinaryDocument binaryDoc, OC out)1469   public void setOutputChannel(GenericBinaryDocument binaryDoc, OC out) {
1470     binaryDoc.setOutputChannel(out);
1471   }
1472 
1473   @Override
fillMeshData(MeshData meshData, int mode, IsosurfaceMesh mesh)1474   public void fillMeshData(MeshData meshData, int mode, IsosurfaceMesh mesh) {
1475     if (meshData == null) {
1476       if (thisMesh == null)
1477         allocMesh(null, null);
1478       if (!thisMesh.isMerged)
1479         thisMesh.clearType(myType, sg.params.iAddGridPoints);
1480       thisMesh.connectedAtoms = connections;
1481       thisMesh.colix = getDefaultColix();
1482       thisMesh.colorType = colorType;
1483       thisMesh.meshColix = meshColix;
1484       if (isPhaseColored || thisMesh.jvxlData.isBicolorMap)
1485         thisMesh.isColorSolid = false;
1486       return;
1487     }
1488     if (mesh == null)
1489       mesh = thisMesh;
1490     if (mesh == null)
1491       return;
1492     //System.out.println("isosurface _get " + mode + " " + MeshData.MODE_GET_VERTICES + " " + MeshData.MODE_PUT_VERTICES + " vc=" + mesh.vertexCount + " pc=" + mesh.polygonCount + " " + mesh +" "
1493       //  + (mesh.bsSlabDisplay == null ? "" :
1494         //" bscard=" + mesh.bsSlabDisplay.cardinality() +
1495         //" " + mesh.bsSlabDisplay.hashCode() + "  " + mesh.bsSlabDisplay));
1496     switch (mode) {
1497     case MeshData.MODE_GET_VERTICES:
1498       meshData.mergeVertexCount0 = mesh.mergeVertexCount0;
1499       meshData.vs = mesh.vs;
1500       meshData.vertexSource = mesh.vertexSource;
1501       meshData.vvs = mesh.vvs;
1502       meshData.vc = mesh.vc;
1503       meshData.vertexIncrement = mesh.vertexIncrement;
1504       meshData.pc = mesh.pc;
1505       meshData.pis = mesh.pis;
1506       meshData.pcs = mesh.pcs;
1507       meshData.bsSlabDisplay = mesh.bsSlabDisplay;
1508       meshData.bsSlabGhost = mesh.bsSlabGhost;
1509       meshData.slabColix = mesh.slabColix;
1510       meshData.slabMeshType = mesh.slabMeshType;
1511       meshData.polygonCount0 = mesh.polygonCount0;
1512       meshData.vertexCount0 = mesh.vertexCount0;
1513       meshData.slabOptions = mesh.slabOptions;
1514       meshData.colorsExplicit = mesh.colorsExplicit;
1515       return;
1516     case MeshData.MODE_GET_COLOR_INDEXES:
1517       if (mesh.vcs == null
1518           || mesh.vc > mesh.vcs.length)
1519         mesh.vcs = new short[mesh.vc];
1520       meshData.vcs = mesh.vcs;
1521       //meshData.polygonIndexes = null;
1522       return;
1523     case MeshData.MODE_PUT_SETS:
1524       mesh.surfaceSet = meshData.surfaceSet;
1525       mesh.vertexSets = meshData.vertexSets;
1526       mesh.nSets = meshData.nSets;
1527       return;
1528     case MeshData.MODE_PUT_VERTICES:
1529       mesh.vs = meshData.vs;
1530       mesh.vvs = meshData.vvs;
1531       mesh.vc = meshData.vc;
1532       mesh.vertexIncrement = meshData.vertexIncrement;
1533       mesh.vertexSource = meshData.vertexSource;
1534       mesh.pc = meshData.pc;
1535       mesh.pis = meshData.pis;
1536       mesh.pcs = meshData.pcs;
1537       mesh.bsSlabDisplay = meshData.bsSlabDisplay;
1538       mesh.bsSlabGhost = meshData.bsSlabGhost;
1539       mesh.slabColix = meshData.slabColix;
1540       mesh.slabMeshType = meshData.slabMeshType;
1541       mesh.polygonCount0 = meshData.polygonCount0;
1542       mesh.vertexCount0 = meshData.vertexCount0;
1543       mesh.mergeVertexCount0 = meshData.mergeVertexCount0;
1544       mesh.slabOptions = meshData.slabOptions;
1545       mesh.colorsExplicit = meshData.colorsExplicit;
1546       return;
1547     }
1548   }
1549 
1550   @Override
notifySurfaceGenerationCompleted()1551   public boolean notifySurfaceGenerationCompleted() {
1552     setMeshI();
1553     setBsVdw();
1554     thisMesh.surfaceAtoms = sg.params.bsSelected;
1555     thisMesh.insideOut = sg.params.isInsideOut();
1556     thisMesh.isModelConnected = sg.params.isModelConnected;
1557     thisMesh.vertexSource = sg.params.vertexSource;
1558     thisMesh.oabc = sg.getOriginVaVbVc();
1559     thisMesh.calculatedArea = null;
1560     thisMesh.calculatedVolume = null;
1561     thisMesh.probeValues = sg.params.probeValues;
1562     // from JVXL file:
1563     if (!thisMesh.isMerged) {
1564       thisMesh.initialize(sg.params.isFullyLit() ? T.fullylit
1565         : T.frontlit, null, sg.params.thePlane);
1566       if (jvxlData.fixedLattice != null) {
1567         thisMesh.lattice = jvxlData.fixedLattice;
1568         thisMesh.fixLattice();
1569       }
1570       return thisMesh.setColorsFromJvxlData(sg.params.colorRgb);
1571     }
1572     if (!sg.params.allowVolumeRender)
1573       thisMesh.jvxlData.allowVolumeRender = false;
1574     thisMesh.setColorsFromJvxlData(sg.params.colorRgb);
1575     if (thisMesh.jvxlData.slabInfo != null)
1576       vwr.runScriptCautiously("isosurface " + thisMesh.jvxlData.slabInfo);
1577 
1578     if (sg.params.psi_monteCarloCount > 0)
1579       thisMesh.diameter = -1; // use set DOTSCALE
1580     return false;
1581 
1582   }
1583 
1584   @Override
notifySurfaceMappingCompleted()1585   public void notifySurfaceMappingCompleted() {
1586     if (!thisMesh.isMerged)
1587       thisMesh.initialize(sg.params.isFullyLit() ? T.fullylit : T.frontlit, null,
1588           sg.params.thePlane);
1589     setBsVdw();
1590     thisMesh.isColorSolid = false;
1591     thisMesh.colorDensity = jvxlData.colorDensity;
1592     thisMesh.volumeRenderPointSize = jvxlData.pointSize;
1593     thisMesh.colorEncoder = sg.params.colorEncoder;
1594     thisMesh.getContours();
1595     if (thisMesh.jvxlData.nContours != 0 && thisMesh.jvxlData.nContours != -1)
1596       explicitContours = true;
1597     if (explicitContours && thisMesh.jvxlData.jvxlPlane != null)
1598       thisMesh.havePlanarContours = true;
1599     setPropertySuper("token",
1600         Integer.valueOf(explicitContours ? T.nofill : T.fill), null);
1601     setPropertySuper("token",
1602         Integer.valueOf(explicitContours ? T.contourlines : T.nocontourlines),
1603         null);
1604     if (!thisMesh.isMerged)
1605       thisMesh.setJvxlDataRendering();
1606     if (sg.params.slabInfo != null) {
1607       thisMesh.slabPolygonsList(sg.params.slabInfo, false);
1608       thisMesh.reinitializeLightingAndColor(vwr);
1609     }
1610     // may not be the final color scheme, though.
1611     thisMesh.setColorCommand();
1612   }
1613 
setBsVdw()1614   private void setBsVdw() {
1615     if (sg.bsVdw == null)
1616       return;
1617     if (thisMesh.bsVdw == null)
1618       thisMesh.bsVdw = new BS();
1619     thisMesh.bsVdw.or(sg.bsVdw);
1620   }
1621 
1622   @Override
calculateGeodesicSurface(BS bsSelected, float envelopeRadius)1623   public P3[] calculateGeodesicSurface(BS bsSelected,
1624                                             float envelopeRadius) {
1625     return vwr.calculateSurface(bsSelected, envelopeRadius);
1626   }
1627 
1628   /////////////  VertexDataServer interface methods ////////////////
1629 
1630   @Override
getSurfacePointIndexAndFraction(float cutoff, boolean isCutoffAbsolute, int x, int y, int z, P3i offset, int vA, int vB, float valueA, float valueB, T3 pointA, V3 edgeVector, boolean isContourType, float[] fReturn)1631   public int getSurfacePointIndexAndFraction(float cutoff, boolean isCutoffAbsolute,
1632                                   int x, int y, int z, P3i offset, int vA,
1633                                   int vB, float valueA, float valueB,
1634                                   T3 pointA, V3 edgeVector,
1635                                   boolean isContourType, float[] fReturn) {
1636     return 0;
1637   }
1638 
1639   private boolean associateNormals;
1640   private String oldFileName;
1641   private String newFileName;
1642 
1643   @Override
addVertexCopy(T3 vertexXYZ, float value, int assocVertex, boolean asCopy)1644   public int addVertexCopy(T3 vertexXYZ, float value, int assocVertex, boolean asCopy) {
1645     if (cutoffRange != null && (value < cutoffRange[0] || value > cutoffRange[1]))
1646       return -1;
1647     return (withinPoints != null && !Mesh.checkWithin(vertexXYZ, withinPoints, withinDistance2, isWithinNot) ? -1
1648         : thisMesh.addVertexCopy(vertexXYZ, value, assocVertex,
1649         associateNormals, asCopy));
1650   }
1651 
1652   @Override
addTriangleCheck(int iA, int iB, int iC, int check, int iContour, boolean isAbsolute, int color)1653   public int addTriangleCheck(int iA, int iB, int iC, int check,
1654                               int iContour, boolean isAbsolute, int color) {
1655    return (iA < 0 || iB < 0 || iC < 0
1656        || isAbsolute && !MeshData.checkCutoff(iA, iB, iC, thisMesh.vvs)
1657        ? -1 : thisMesh.addTriangleCheck(iA, iB, iC, check, iContour, color));
1658   }
1659 
setScriptInfo(String strCommand)1660   protected void setScriptInfo(String strCommand) {
1661     // also from lcaoCartoon
1662     String script = (strCommand == null ? sg.params.script : strCommand);
1663     int pt = (script == null ? -1 : script.indexOf("; isosurface map"));
1664     if (pt == 0) {
1665       // remapping surface
1666       if (thisMesh.scriptCommand == null)
1667         return;
1668       pt = thisMesh.scriptCommand.indexOf("; isosurface map");
1669       if (pt >= 0)
1670         thisMesh.scriptCommand = thisMesh.scriptCommand.substring(0, pt);
1671       thisMesh.scriptCommand += script;
1672       return;
1673     }
1674     thisMesh.title = sg.params.title;
1675     thisMesh.dataType = sg.params.dataType;
1676     thisMesh.scale3d = sg.params.scale3d;
1677     if (script != null) {
1678       if (oldFileName != null) {
1679         script = script.replace(oldFileName, newFileName);
1680       }
1681       if (script.charAt(0) == ' ') {
1682         script = myType + " ID " + PT.esc(thisMesh.thisID) + script;
1683         pt = script.indexOf("; isosurface map");
1684       }
1685     }
1686     if (pt > 0 && scriptAppendix.length() > 0)
1687       thisMesh.scriptCommand = script.substring(0, pt) + scriptAppendix + script.substring(pt);
1688     else
1689       thisMesh.scriptCommand = script + scriptAppendix;
1690     if (!explicitID && script != null && (pt = script.indexOf("# ID=")) >= 0)
1691       thisMesh.thisID = PT.getQuotedStringAt(script, pt);
1692   }
1693 
1694   @Override
addRequiredFile(String fileName)1695   public void addRequiredFile(String fileName) {
1696     fileName = " # /*file*/\"" + fileName + "\"";
1697     if (scriptAppendix.indexOf(fileName) < 0)
1698     scriptAppendix += fileName;
1699   }
1700 
1701   @Override
setRequiredFile(String oldName, String fileName)1702   public void setRequiredFile(String oldName, String fileName) {
1703     oldFileName = oldName;
1704     newFileName = fileName;
1705   }
1706 
setJvxlInfo()1707   private void setJvxlInfo() {
1708     if (sg.jvxlData != jvxlData || sg.jvxlData != thisMesh.jvxlData)
1709       jvxlData = thisMesh.jvxlData = sg.jvxlData;
1710   }
1711 
1712   @Override
getShapeDetail()1713   public Object getShapeDetail() {
1714     Lst<Map<String, Object>> V = new  Lst<Map<String, Object>>();
1715     for (int i = 0; i < meshCount; i++) {
1716       Map<String, Object> info = new Hashtable<String, Object>();
1717       IsosurfaceMesh mesh = isomeshes[i];
1718       if (mesh == null || mesh.vs == null
1719           || mesh.vc == 0 && mesh.pc == 0)
1720         continue;
1721       addMeshInfo(mesh, info);
1722       V.addLast(info);
1723     }
1724     return V;
1725   }
1726 
addMeshInfo(IsosurfaceMesh mesh, Map<String, Object> info)1727   protected void addMeshInfo(IsosurfaceMesh mesh, Map<String, Object> info) {
1728     info.put("ID", (mesh.thisID == null ? "<noid>" : mesh.thisID));
1729     info.put("visible", Boolean.valueOf(mesh.visible));
1730     info.put("vertexCount", Integer.valueOf(mesh.vc));
1731     if (mesh.calculatedVolume != null)
1732       info.put("volume", mesh.calculatedVolume);
1733     if (mesh.calculatedArea != null)
1734       info.put("area", mesh.calculatedArea);
1735     if (!Float.isNaN(mesh.ptCenter.x))
1736       info.put("center", mesh.ptCenter);
1737     if (mesh.mat4 != null)
1738       info.put("mat4", mesh.mat4);
1739     if (mesh.scale3d != 0)
1740       info.put("scale3d", Float.valueOf(mesh.scale3d));
1741     info.put("xyzMin", mesh.jvxlData.boundingBox[0]);
1742     info.put("xyzMax", mesh.jvxlData.boundingBox[1]);
1743     String s = JvxlCoder.jvxlGetInfo(mesh.jvxlData);
1744     if (s != null)
1745       info.put("jvxlInfo", s.replace('\n', ' '));
1746     info.put("modelIndex", Integer.valueOf(mesh.modelIndex));
1747     info.put("color", CU.colorPtFromInt(C
1748         .getArgb(mesh.colix), null));
1749     if (mesh.colorEncoder != null)
1750       info.put("colorKey", mesh.colorEncoder.getColorKey());
1751     if (mesh.title != null)
1752       info.put("title", mesh.title);
1753     if (mesh.jvxlData.contourValues != null
1754         || mesh.jvxlData.contourValuesUsed != null)
1755       info.put("contours", mesh.getContourList(vwr));
1756   }
1757 
1758   @Override
getPlane(int x)1759   public float[] getPlane(int x) {
1760     // only for surface readers
1761     return null;
1762   }
1763 
1764   @Override
getValue(int x, int y, int z, int ptyz)1765   public float getValue(int x, int y, int z, int ptyz) {
1766     return 0;
1767   }
1768 
1769   @Override
checkObjectHovered(int x, int y, BS bsVisible)1770   public boolean checkObjectHovered(int x, int y, BS bsVisible) {
1771     if (keyXy != null && x >= keyXy[0] && y >= keyXy[1] && x < keyXy[2] && y < keyXy[3]) {
1772       hoverKey(x, y);
1773       return true;
1774     }
1775     if (!vwr.getDrawHover())
1776       return false;
1777     String s = findValue(x, y, false, bsVisible);
1778     if (s == null)
1779       return false;
1780     if (vwr.gdata.antialiasEnabled) {
1781       //because hover rendering is done in FIRST pass only
1782       x <<= 1;
1783       y <<= 1;
1784     }
1785     vwr.hoverOnPt(x, y, s, pickedMesh.thisID, pickedPt);
1786     return true;
1787   }
1788 
hoverKey(int x, int y)1789   private void hoverKey(int x, int y) {
1790     try {
1791       String s;
1792       float f = 1 - 1.0f * (y - keyXy[1]) / (keyXy[3] - keyXy[1]);
1793       if (thisMesh.showContourLines) {
1794         Lst<Object>[] vContours = thisMesh.getContours();
1795         if (vContours == null) {
1796           if (thisMesh.jvxlData.contourValues == null)
1797             return;
1798           int i = (int) Math.floor(f * thisMesh.jvxlData.contourValues.length);
1799           if (i < 0 || i > thisMesh.jvxlData.contourValues.length)
1800             return;
1801           s = "" + thisMesh.jvxlData.contourValues[i];
1802         } else {
1803           int i = (int) Math.floor(f * vContours.length);
1804           if (i < 0 || i > vContours.length)
1805             return;
1806           s = ""
1807               + ((Float) vContours[i].get(JvxlCoder.CONTOUR_VALUE))
1808                   .floatValue();
1809         }
1810       } else {
1811         float g = thisMesh.colorEncoder.quantize(f, true);
1812         f = thisMesh.colorEncoder.quantize(f, false);
1813         s = "" + g + " - " + f;
1814       }
1815       if (vwr.gdata.isAntialiased()) {
1816         x <<= 1;
1817         y <<= 1;
1818       }
1819       vwr.hoverOnPt(x, y, s, null, null);
1820     } catch (Exception e) {
1821       // never mind!
1822     }
1823   }
1824   private final static int MAX_OBJECT_CLICK_DISTANCE_SQUARED = 10 * 10;
1825   private final P3i ptXY = new P3i();
1826   public int[] keyXy;
1827 
1828   @Override
checkObjectClicked(int x, int y, int action, BS bsVisible, boolean drawPicking)1829   public Map<String, Object> checkObjectClicked(int x, int y, int action, BS bsVisible, boolean drawPicking) {
1830     if (!drawPicking)// || vwr.getNavigationMode() && vwr.getNavigateSurface()))
1831        return null;
1832     if (!vwr.isBound(action, ActionManager.ACTION_pickIsosurface))
1833       return null;
1834     int dmin2 = MAX_OBJECT_CLICK_DISTANCE_SQUARED;
1835     if (vwr.gdata.isAntialiased()) {
1836       x <<= 1;
1837       y <<= 1;
1838       dmin2 <<= 1;
1839     }
1840     int imesh = -1;
1841     int jmaxz = -1;
1842     int jminz = -1;
1843     int maxz = Integer.MIN_VALUE;
1844     int minz = Integer.MAX_VALUE;
1845     boolean pickFront = true;
1846     for (int i = 0; i < meshCount; i++) {
1847       IsosurfaceMesh m = isomeshes[i];
1848       if (!isPickable(m, bsVisible))
1849         continue;
1850       T3[] centers = (pickFront ? m.vs : m.getCenters());
1851       if (centers == null)
1852         continue;
1853       for (int j = centers.length; --j >= 0; ) {
1854           T3 v = centers[j];
1855           if (v == null)
1856             continue;
1857           int d2 = coordinateInRange(x, y, v, dmin2, ptXY);
1858           if (d2 >= 0) {
1859             if (ptXY.z < minz) {
1860               if (pickFront)
1861                 imesh = i;
1862               minz = ptXY.z;
1863               jminz = j;
1864             }
1865             if (ptXY.z > maxz) {
1866               if (!pickFront)
1867                 imesh = i;
1868               maxz = ptXY.z;
1869               jmaxz = j;
1870             }
1871           }
1872       }
1873     }
1874     if (imesh < 0)
1875       return null;
1876     pickedMesh = isomeshes[imesh];
1877     setPropertySuper("thisID", pickedMesh.thisID, null);
1878     int iFace = pickedVertex = (pickFront ? jminz : jmaxz);
1879     P3 ptRet = new P3();
1880     ptRet.setT((pickFront ? pickedMesh.vs[pickedVertex] : ((IsosurfaceMesh)pickedMesh).centers[iFace]));
1881     pickedModel = (short) pickedMesh.modelIndex;
1882     Map<String, Object> map = getPickedPoint(ptRet, pickedModel);
1883 //    if (pickFront) {
1884       setStatusPicked(-4, ptRet, map);
1885 //    } else {
1886 //      Vector3f vNorm = new Vector3f();
1887 //      ((IsosurfaceMesh)pickedMesh).getFacePlane(iFace, vNorm);
1888 //      // get normal to surface
1889 //      vNorm.scale(-1);
1890 //     // setHeading(ptRet, vNorm, 2);
1891 //    }
1892     return map;
1893   }
1894 
isPickable(IsosurfaceMesh m, BS bsVisible)1895   private boolean isPickable(IsosurfaceMesh m, BS bsVisible) {
1896     return m.visibilityFlags != 0 && (m.modelIndex < 0
1897         || bsVisible.get(m.modelIndex)) && !C
1898         .isColixTranslucent(m.colix);
1899   }
1900 
1901   //  private void navigate(int dz) {
1902   //    if (thisMesh == null)
1903   //      return;
1904   //    Point3f navPt = Point3f.newP(vwr.getNavigationOffset());
1905   //    Point3f toPt = new Point3f();
1906   //    vwr.unTransformPoint(navPt, toPt);
1907   //    navPt.z += dz;
1908   //    vwr.unTransformPoint(navPt, toPt);
1909   //    Point3f ptRet = new Point3f();
1910   //    Vector3f vNorm = new Vector3f();
1911   //    if (!getClosestNormal(thisMesh, toPt, ptRet, vNorm))
1912   //      return;
1913   //    Point3f pt2 = Point3f.newP(ptRet);
1914   //    pt2.add(vNorm);
1915   //    Point3f pt2s = new Point3f();
1916   //    vwr.tm.transformPt3f(pt2, pt2s);
1917   //    if (pt2s.y > navPt.y)
1918   //      vNorm.scale(-1);
1919   //    setHeading(ptRet, vNorm, 0);
1920   //  }
1921 
1922   //  private void setHeading(Point3f pt, Vector3f vNorm, int nSeconds) {
1923   //    // general trick here is to save the original orientation,
1924   //    // then do all the changes and save the new orientation.
1925   //    // Then just do a timed restore.
1926   //
1927   //    Orientation o1 = vwr.getOrientation();
1928   //
1929   //    // move to point
1930   //    vwr.navigatePt(pt);
1931   //
1932   //    Point3f toPts = new Point3f();
1933   //
1934   //    // get screen point along normal
1935   //    Point3f toPt = Point3f.newP(vNorm);
1936   //    //vwr.script("draw test2 vector " + Escape.escape(pt) + " " + Escape.escape(toPt));
1937   //    toPt.add(pt);
1938   //    vwr.tm.transformPt3f(toPt, toPts);
1939   //
1940   //    // subtract the navigation point to get a relative point
1941   //    // that we can project into the xy plane by setting z = 0
1942   //    Point3f navPt = Point3f.newP(vwr.getNavigationOffset());
1943   //    toPts.sub(navPt);
1944   //    toPts.z = 0;
1945   //
1946   //    // set the directed angle and rotate normal into yz plane,
1947   //    // less 20 degrees for the normal upward sloping view
1948   //    float angle = Measure.computeTorsion(JmolConstants.axisNY,
1949   //        JmolConstants.center, JmolConstants.axisZ, toPts, true);
1950   //    vwr.navigateAxis(JmolConstants.axisZ, angle);
1951   //    toPt.setT(vNorm);
1952   //    toPt.add(pt);
1953   //    vwr.tm.transformPt3f(toPt, toPts);
1954   //    toPts.sub(navPt);
1955   //    angle = Measure.computeTorsion(JmolConstants.axisNY,
1956   //        JmolConstants.center, JmolConstants.axisX, toPts, true);
1957   //    vwr.navigateAxis(JmolConstants.axisX, 20 - angle);
1958   //
1959   //    // save this orientation, restore the first, and then
1960   //    // use TransformManager.moveto to smoothly transition to it
1961   //    // a script is necessary here because otherwise the application
1962   //    // would hang.
1963   //
1964   //    navPt = Point3f.newP(vwr.getNavigationOffset());
1965   //    if (nSeconds <= 0)
1966   //      return;
1967   //    vwr.saveOrientation("_navsurf");
1968   //    o1.restore(0, true);
1969   //    vwr.script("restore orientation _navsurf " + nSeconds);
1970   //  }
1971 
1972   //  private boolean getClosestNormal(IsosurfaceMesh m, Point3f toPt, Point3f ptRet, Vector3f normalRet) {
1973   //    Point3f[] centers = m.getCenters();
1974   //    float d;
1975   //    float dmin = Float.MAX_VALUE;
1976   //    int imin = -1;
1977   //    for (int i = centers.length; --i >= 0; ) {
1978   //      if ((d = centers[i].distance(toPt)) >= dmin)
1979   //        continue;
1980   //      dmin = d;
1981   //      imin = i;
1982   //    }
1983   //    if (imin < 0)
1984   //      return false;
1985   //    getClosestPoint(m, imin, toPt, ptRet, normalRet);
1986   //    return true;
1987   //  }
1988 
1989   //  private void getClosestPoint(IsosurfaceMesh m, int imin, Point3f toPt, Point3f ptRet,
1990   //                               Vector3f normalRet) {
1991   //    Point4f plane = m.getFacePlane(imin, normalRet);
1992   //    float dist = Measure.distanceToPlane(plane, toPt);
1993   //    normalRet.scale(-dist);
1994   //    ptRet.setT(toPt);
1995   //    ptRet.add(normalRet);
1996   //    dist = Measure.distanceToPlane(plane, ptRet);
1997   //    if (m.centers[imin].distance(toPt) < ptRet.distance(toPt))
1998   //      ptRet.setT(m.centers[imin]);
1999   //  }
2000 
2001   /**
2002    *
2003    * @param x
2004    * @param y
2005    * @param isPicking
2006    *        IGNORED
2007    * @param bsVisible
2008    * @return value found
2009    */
findValue(int x, int y, boolean isPicking, BS bsVisible)2010   private String findValue(int x, int y, boolean isPicking, BS bsVisible) {
2011     int dmin2 = MAX_OBJECT_CLICK_DISTANCE_SQUARED;
2012     if (vwr.gdata.isAntialiased()) {
2013       x <<= 1;
2014       y <<= 1;
2015       dmin2 <<= 1;
2016     }
2017     int pickedVertex = -1;
2018     Lst<Object> pickedContour = null;
2019     IsosurfaceMesh m = null;
2020     for (int i = 0; i < meshCount; i++) {
2021       m = isomeshes[i];
2022       if (!isPickable(m, bsVisible))
2023         continue;
2024       Lst<Object>[] vs = m.jvxlData.vContours;
2025       int ilast = (m.firstRealVertex < 0 ? 0 : m.firstRealVertex);
2026       int pickedJ = 0;
2027       if (vs != null && vs.length > 0) {
2028         for (int j = 0; j < vs.length; j++) {
2029           Lst<Object> vc = vs[j];
2030           int n = vc.size() - 1;
2031           for (int k = JvxlCoder.CONTOUR_POINTS; k < n; k++) {
2032             T3 v = (T3) vc.get(k);
2033             int d2 = coordinateInRange(x, y, v, dmin2, ptXY);
2034             if (d2 >= 0) {
2035               dmin2 = d2;
2036               pickedContour = vc;
2037               pickedJ = j;
2038               pickedMesh = m;
2039               pickedPt = v;
2040             }
2041           }
2042         }
2043         if (pickedContour != null)
2044           return pickedContour.get(JvxlCoder.CONTOUR_VALUE).toString()
2045               + (Logger.debugging ? " " + pickedJ : "");
2046       } else if (m.jvxlData.jvxlPlane != null && m.vvs != null) {
2047         T3[] vertices = (m.mat4 == null && m.scale3d == 0 ? m.vs : m
2048             .getOffsetVertices(m.jvxlData.jvxlPlane));
2049         for (int k = m.vc; --k >= ilast;) {
2050           T3 v = vertices[k];
2051           int d2 = coordinateInRange(x, y, v, dmin2, ptXY);
2052           if (d2 >= 0) {
2053             dmin2 = d2;
2054             pickedVertex = k;
2055             pickedMesh = m;
2056             pickedPt = v;
2057           }
2058         }
2059         if (pickedVertex != -1)
2060           break;
2061       } else if (m.vvs != null) {
2062         if (m.bsSlabDisplay != null) {
2063           for (int k = m.bsSlabDisplay.nextSetBit(0); k >= 0; k = m.bsSlabDisplay
2064               .nextSetBit(k + 1)) {
2065             int[] p = m.pis[k];
2066             if (p != null)
2067               for (int l = 0; l < 3; l++) {
2068                 T3 v = m.vs[p[l]];
2069                 int d2 = coordinateInRange(x, y, v, dmin2, ptXY);
2070                 if (d2 >= 0) {
2071                   dmin2 = d2;
2072                   pickedVertex = p[l];
2073                   pickedMesh = m;
2074                   pickedPt = v;
2075                 }
2076               }
2077           }
2078         } else {
2079           for (int k = m.vc; --k >= ilast;) {
2080             T3 v = m.vs[k];
2081             int d2 = coordinateInRange(x, y, v, dmin2, ptXY);
2082             if (d2 >= 0) {
2083               dmin2 = d2;
2084               pickedVertex = k;
2085               pickedMesh = m;
2086               pickedPt = v;
2087             }
2088           }
2089         }
2090         if (pickedVertex != -1)
2091           break;
2092       }
2093     }
2094     return (pickedVertex == -1 ? null : (Logger.debugging ? "$" + m.thisID
2095         + "[" + (pickedVertex + 1) + "] " + m.vs[pickedVertex] + ": "
2096         : m.thisID + ": ")
2097         + m.vvs[pickedVertex]);
2098   }
2099 
getCmd(int index)2100   public String getCmd(int index){
2101     SB sb = new SB().append("\n");
2102 //    result = this.isomeshes[index].scriptCommand;
2103     getMeshCommand(sb, index);
2104     return (sb.toString());
2105   }
2106 
2107   @Override
getValues(Mesh mesh)2108   protected Object getValues(Mesh mesh) {
2109     return (mesh == null ? null : ((IsosurfaceMesh) mesh).getValidValues(null));
2110   }
2111 
2112 
2113   @Override
getVertices(Mesh mesh)2114   protected Object getVertices(Mesh mesh) {
2115     return (mesh == null ? null : ((IsosurfaceMesh) mesh).getValidVertices(null));
2116   }
2117 
2118 }
2119