1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2006-12-03 14:51:57 -0600 (Sun, 03 Dec 2006) $
4  * $Revision: 6372 $
5  *
6  * Copyright (C) 2005  Miguel, The Jmol Development Team
7  *
8  * Contact: jmol-developers@lists.sf.net, jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 package org.jmol.shapesurface;
26 
27 import javajs.util.AU;
28 import javajs.util.Lst;
29 import javajs.util.SB;
30 
31 import java.util.Hashtable;
32 
33 import java.util.Map;
34 
35 
36 import org.jmol.api.Interface;
37 import org.jmol.api.SymmetryInterface;
38 import org.jmol.util.C;
39 import org.jmol.util.ColorEncoder;
40 import org.jmol.util.GData;
41 import org.jmol.util.Logger;
42 import org.jmol.util.MeshSurface;
43 import org.jmol.util.SimpleUnitCell;
44 
45 import javajs.util.CU;
46 import javajs.util.M4;
47 import javajs.util.P3;
48 import javajs.util.P3i;
49 import javajs.util.PT;
50 import javajs.util.T3;
51 import javajs.util.V3;
52 import org.jmol.viewer.Viewer;
53 import javajs.util.BS;
54 import org.jmol.jvxl.data.JvxlCoder;
55 import org.jmol.jvxl.data.JvxlData;
56 import org.jmol.jvxl.data.MeshData;
57 
58 import org.jmol.jvxl.calc.MarchingSquares;
59 import org.jmol.modelset.Atom;
60 import org.jmol.script.T;
61 import org.jmol.shape.Mesh;
62 
63 public class IsosurfaceMesh extends Mesh {
64   public JvxlData jvxlData;
65   public int vertexIncrement = 1;
66   public int firstRealVertex = -1;
67   public int dataType;
68   public boolean hasGridPoints;
69   Object calculatedArea;
70   Object calculatedVolume;
71   Object info;
72 
73   @Override
getResolution()74   public float getResolution() {
75     return 1/jvxlData.pointsPerAngstrom;
76   }
77 
78   /**
79    * @param vwr
80    * @XXXXj2sIgnoreSuperConstructor
81    *
82    * @param thisID
83    * @param colix
84    * @param index
85    */
IsosurfaceMesh(Viewer vwr, String thisID, short colix, int index)86   IsosurfaceMesh(Viewer vwr, String thisID, short colix, int index) {
87     mesh1(vwr, thisID, colix, index);
88     jvxlData = new JvxlData();
89     checkByteCount = 2;
90     jvxlData.version = Viewer.getJmolVersion();
91   }
92 
clearType(String meshType, boolean iAddGridPoints)93   void clearType(String meshType, boolean iAddGridPoints) {
94     clear(meshType);
95     jvxlData.clear();
96     assocGridPointMap = null;
97     assocGridPointNormals = null;
98     bsVdw = null;
99     calculatedVolume = null;
100     calculatedArea = null;
101     centers = null;
102     colorEncoder = null;
103     colorPhased = false;
104     colorsExplicit = false;
105     firstRealVertex = -1;
106     hasGridPoints = iAddGridPoints;
107     isColorSolid = true;
108     mergeAssociatedNormalCount = 0;
109     nSets = 0;
110     pcs = null;
111     showPoints = iAddGridPoints;
112     surfaceSet = null;
113     vcs = null;
114     vertexColorMap = null;
115     vertexIncrement = 1;
116     vertexSets = null;
117     vvs = null;
118   }
119 
allocVertexColixes()120   void allocVertexColixes() {
121     if (vcs == null) {
122       vcs = new short[vc];
123       for (int i = vc; --i >= 0;)
124         vcs[i] = colix;
125     }
126     isColorSolid = false;
127   }
128 
129   private Map<Integer, Integer> assocGridPointMap;
130   private Map<Integer, V3> assocGridPointNormals;
131 
addVertexCopy(T3 vertex, float value, int assocVertex, boolean associateNormals, boolean asCopy)132   int addVertexCopy(T3 vertex, float value, int assocVertex,
133                     boolean associateNormals, boolean asCopy) {
134     int vPt = addVCVal(vertex, value, asCopy);
135     switch (assocVertex) {
136     case MarchingSquares.CONTOUR_POINT:
137       if (firstRealVertex < 0)
138         firstRealVertex = vPt;
139       break;
140     case MarchingSquares.VERTEX_POINT:
141       hasGridPoints = true;
142       break;
143     case MarchingSquares.EDGE_POINT:
144       vertexIncrement = 3;
145       break;
146     default:
147       if (firstRealVertex < 0)
148         firstRealVertex = vPt;
149       if (associateNormals) {
150         if (assocGridPointMap == null)
151           assocGridPointMap = new Hashtable<Integer, Integer>();
152         assocGridPointMap.put(Integer.valueOf(vPt), Integer.valueOf(assocVertex + mergeAssociatedNormalCount));
153       }
154     }
155     return vPt;
156   }
157 
158   @Override
setTranslucent(boolean isTranslucent, float iLevel)159   public void setTranslucent(boolean isTranslucent, float iLevel) {
160     colix = C.getColixTranslucent3(colix, isTranslucent, iLevel);
161     if (vcs != null)
162       for (int i = vc; --i >= 0;)
163         vcs[i] = C.getColixTranslucent3(vcs[i],
164             isTranslucent, iLevel);
165   }
166 
167   private int mergeAssociatedNormalCount;
setMerged(boolean TF)168   public void setMerged(boolean TF) {
169     isMerged = TF;
170     mergePolygonCount0 = (TF ? pc : 0);
171     mergeVertexCount0 = (TF ? vc: 0);
172     if (TF) {
173       mergeAssociatedNormalCount += jvxlData.nPointsX * jvxlData.nPointsY * jvxlData.nPointsZ;
174       assocGridPointNormals = null;
175     }
176   }
177 
178 
179   @Override
sumVertexNormals(T3[] vertices, V3[] vectorSums)180   protected void sumVertexNormals(T3[] vertices, V3[] vectorSums) {
181     sumVertexNormals2(this, vertices, vectorSums);
182     /*
183      * OK, so if there is an associated grid point (because the
184      * point is so close to one), we now declare that associated
185      * point to be used for the   vecetorSum instead of a new,
186      * independent one for the point itself.
187      *
188      *  Bob Hanson, 05/2006
189      *
190      *  having 2-sided normixes is INCOMPATIBLE with this when not a plane
191      *
192      */
193     if (assocGridPointMap != null && vectorSums.length > 0 && !isMerged) {
194       if (assocGridPointNormals == null)
195         assocGridPointNormals = new Hashtable<Integer, V3>();
196       for (Map.Entry<Integer, Integer> entry : assocGridPointMap.entrySet()) {
197         // keys are indices into vertices[]
198         // values are unique identifiers for a grid point
199         Integer gridPoint = entry.getValue();
200         if (!assocGridPointNormals.containsKey(gridPoint))
201           assocGridPointNormals.put(gridPoint, V3.new3(0, 0, 0));
202         assocGridPointNormals.get(gridPoint).add(vectorSums[entry.getKey().intValue()]);
203       }
204       for (Map.Entry<Integer, Integer> entry : assocGridPointMap.entrySet())
205         vectorSums[entry.getKey().intValue()] = assocGridPointNormals.get(entry.getValue());
206     }
207   }
208 
209   P3[] centers;
210 
getCenters()211   P3[] getCenters() {
212     if (centers != null)
213       return centers;
214     centers = new P3[pc];
215     for (int i = 0; i < pc; i++) {
216       int[] p = pis[i];
217       if (p == null)
218         continue;
219       P3 pt = centers[i] = P3.newP(vs[p[0]]);
220       pt.add(vs[p[1]]);
221       pt.add(vs[p[2]]);
222       pt.scale(1 / 3f);
223     }
224     return centers;
225   }
226 
227 //  P4 getFacePlane(int i, V3 vNorm) {
228 //    return Measure.getPlaneThroughPoints(vs[pis[i][0]],
229 //        vs[pis[i][1]], vs[pis[i][2]], vNorm,
230 //        vAB, new P4());
231 //  }
232 
233   /**
234    * create a set of contour data.
235    *
236    * Each contour is a Vector containing: 0 Integer number of polygons (length
237    * of BitSet) 1 BitSet of critical triangles 2 Float value 3 int[] [colorArgb]
238    * 4 StringXBuilder containing encoded data for each segment: char type ('3',
239    * '6', '5') indicating which two edges of the triangle are connected: '3'
240    * 0x011 AB-BC '5' 0x101 AB-CA '6' 0x110 BC-CA char fraction along first edge
241    * (jvxlFractionToCharacter) char fraction along second edge
242    * (jvxlFractionToCharacter) 5- stream of pairs of points for rendering
243    *
244    * @return contour vector set
245    */
246   @SuppressWarnings("unchecked")
247   public
getContours()248   Lst<Object>[] getContours() {
249     int n = jvxlData.nContours;
250     if (n == 0 || pis == null)
251       return null;
252     havePlanarContours = (jvxlData.jvxlPlane != null);
253     if (havePlanarContours)
254       return null; // not necessary;
255     if (n < 0)
256       n = -1 - n;
257     Lst<Object>[] vContours = jvxlData.vContours;
258     if (vContours != null) {
259       for (int i = 0; i < n; i++) {
260         if (vContours[i].size() > JvxlCoder.CONTOUR_POINTS)
261           return jvxlData.vContours;
262         JvxlCoder.set3dContourVector(vContours[i], pis, vs);
263       }
264       //dumpData();
265       return jvxlData.vContours;
266     }
267     //dumpData();
268     vContours = new Lst[n];
269     for (int i = 0; i < n; i++) {
270       vContours[i] = new  Lst<Object>();
271     }
272     if (jvxlData.contourValuesUsed == null) {
273       float dv = (jvxlData.valueMappedToBlue - jvxlData.valueMappedToRed)
274           / (n + 1);
275       // n + 1 because we want n lines between n + 1 slices
276       for (int i = 0; i < n; i++) {
277         float value = jvxlData.valueMappedToRed + (i + 1) * dv;
278         get3dContour(this, vContours[i], value, jvxlData.contourColixes[i]);
279       }
280       Logger.info(n + " contour lines; separation = " + dv);
281     } else {
282       for (int i = 0; i < n; i++) {
283         float value = jvxlData.contourValuesUsed[i];
284         get3dContour(this, vContours[i], value, jvxlData.contourColixes[i]);
285       }
286     }
287     jvxlData.contourColixes = new short[n];
288     jvxlData.contourValues = new float[n];
289     for (int i = 0; i < n; i++) {
290       jvxlData.contourValues[i] = ((Float) vContours[i].get(2)).floatValue();
291       jvxlData.contourColixes[i] = ((short[]) vContours[i].get(3))[0];
292     }
293     return jvxlData.vContours = vContours;
294   }
295 
getPmeshData(boolean isBinary)296   public Object getPmeshData(boolean isBinary) {
297     PMeshWriter mw = (PMeshWriter) Interface.getInterface("org.jmol.shapesurface.PMeshWriter", vwr, "script");
298     return mw.write(this, isBinary);
299   }
300 
get3dContour(IsosurfaceMesh m, Lst<Object> v, float value, short colix)301   private static void get3dContour(IsosurfaceMesh m, Lst<Object> v, float value, short colix) {
302     BS bsContour = BS.newN(m.pc);
303     SB fData = new SB();
304     int color = C.getArgb(colix);
305     setContourVector(v, m.pc, bsContour, value, colix, color, fData);
306     for (int i = 0; i < m.pc; i++)
307       if (m.setABC(i) != null)
308         addContourPoints(v, bsContour, i, fData, m.vs, m.vvs, m.iA,
309             m.iB, m.iC, value);
310   }
311 
setContourVector(Lst<Object> v, int nPolygons, BS bsContour, float value, short colix, int color, SB fData)312   public static void setContourVector(Lst<Object> v, int nPolygons,
313                                       BS bsContour, float value,
314                                       short colix, int color, SB fData) {
315     v.add(JvxlCoder.CONTOUR_NPOLYGONS, Integer.valueOf(nPolygons));
316     v.add(JvxlCoder.CONTOUR_BITSET, bsContour);
317     v.add(JvxlCoder.CONTOUR_VALUE, Float.valueOf(value));
318     v.add(JvxlCoder.CONTOUR_COLIX, new short[] { colix });
319     v.add(JvxlCoder.CONTOUR_COLOR, new int[] { color });
320     v.add(JvxlCoder.CONTOUR_FDATA, fData);
321   }
322 
addContourPoints(Lst<Object> v, BS bsContour, int i, SB fData, T3[] vertices, float[] vertexValues, int iA, int iB, int iC, float value)323   public static void addContourPoints(Lst<Object> v, BS bsContour, int i,
324                                       SB fData, T3[] vertices,
325                                       float[] vertexValues, int iA, int iB,
326                                       int iC, float value) {
327     P3 pt1 = null;
328     P3 pt2 = null;
329     int type = 0;
330     // check AB
331     float f1 = checkPt(vertexValues, iA, iB, value);
332     if (!Float.isNaN(f1)) {
333       pt1 = getContourPoint(vertices, iA, iB, f1);
334       type |= 1;
335     }
336     // check BC only if v not found only at B already in testing AB
337     float f2 = (f1 == 1 ? Float.NaN : checkPt(vertexValues, iB, iC, value));
338     if (!Float.isNaN(f2)) {
339       pt2 = getContourPoint(vertices, iB, iC, f2);
340       if (type == 0) {
341         pt1 = pt2;
342         f1 = f2;
343       }
344       type |= 2;
345     }
346     // only check CA under certain circumstances
347     switch (type) {
348     case 0:
349       return; // not in AB or BC, so ignore
350     case 1:
351       if (f1 == 0)
352         return; //because at A and not along BC, so only at A
353       //$FALL-THROUGH$
354     case 2:
355       // check CA only if v not found only at C already in testing BC
356       f2 = (f2 == 1 ? Float.NaN : checkPt(vertexValues, iC, iA, value));
357       if (!Float.isNaN(f2)) {
358         pt2 = getContourPoint(vertices, iC, iA, f2);
359         type |= 4;
360       }
361       break;
362     }
363     // only types AB-BC, AB-CA, or BC-CA are valid intersections
364     switch (type) {
365     case 3:
366     case 5:
367     case 6:
368       break;
369     default:
370       return;
371     }
372     bsContour.set(i);
373     JvxlCoder.appendContourTriangleIntersection(type, f1, f2, fData);
374     v.addLast(pt1);
375     v.addLast(pt2);
376   }
377 
378   /**
379    * two values -- v1, and v2, which need not be ordered v1 < v2. v == v1 --> 0
380    * v == v2 --> 1 v1 < v < v2 --> f in (0,1) v2 < v < v1 --> f in (0,1) i.e.
381    * (v1 < v) == (v < v2)
382    *
383    * We check AB, then (usually) BC, then (sometimes) CA.
384    *
385    * What if two end points are identical values? So, for example, if v = 1.0
386    * and:
387    *
388    * A 1.0 0.5 1.0 1.0 / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ C-----B 1.0--0.5
389    * 1.0--1.0 0.5--1.0 1.0---1.0 case I case II case III case IV
390    *
391    * case I: AB[0] and BC[1], type == 3 --> CA not tested. case II: AB[1] and
392    * CA[0]; f1 == 1.0 --> BC not tested. case III: AB[0] and BC[0], type == 3
393    * --> CA not tested. case IV: AB[0] and BC[0], type == 3 --> CA not tested.
394    *
395    * what if v = 0.5?
396    *
397    * case I: AB[1]; BC not tested --> type == 1, invalid. case II: AB[0]; type
398    * == 1, f1 == 0.0 --> CA not tested. case III: BC[1]; f2 == 1.0 --> CA not
399    * tested.
400    *
401    * @param vertexValues
402    * @param i
403    * @param j
404    * @param v
405    * @return fraction along the edge or NaN
406    */
checkPt(float[] vertexValues, int i, int j, float v)407   private static float checkPt(float[] vertexValues, int i, int j, float v) {
408     float v1, v2;
409     return (v == (v1 = vertexValues[i]) ? 0 : v == (v2 = vertexValues[j]) ? 1
410         : (v1 < v) == (v < v2) ? (v - v1) / (v2 - v1) : Float.NaN);
411   }
412 
getContourPoint(T3[] vertices, int i, int j, float f)413   private static P3 getContourPoint(T3[] vertices, int i, int j,
414                                          float f) {
415     P3 pt = new P3();
416     pt.sub2(vertices[j], vertices[i]);
417     pt.scaleAdd2(f, pt, vertices[i]);
418     return pt;
419   }
420 
421   float[] contourValues;
422   short[] contourColixes;
423   public ColorEncoder colorEncoder;
424 
425   BS bsVdw;
426   public boolean colorPhased;
427   public float[] probeValues;
428 
setDiscreteColixes(float[] values, short[] colixes)429   public void setDiscreteColixes(float[] values, short[] colixes) {
430     if (values != null)
431       jvxlData.contourValues = values;
432     if (values == null || values.length == 0)
433       values = jvxlData.contourValues = jvxlData.contourValuesUsed;
434     if (colixes == null && jvxlData.contourColixes != null) {
435       colixes = jvxlData.contourColixes;
436     } else {
437       jvxlData.contourColixes = colixes;
438       jvxlData.contourColors = C.getHexCodes(colixes);
439     }
440     if (vs == null || vvs == null || values == null)
441       return;
442     int n = values.length;
443     float vMax = values[n - 1];
444     colorCommand = null;
445     boolean haveColixes = (colixes != null && colixes.length > 0);
446     isColorSolid = (haveColixes && jvxlData.jvxlPlane != null);
447     if (jvxlData.vContours != null) {
448       if (haveColixes)
449         for (int i = 0; i < jvxlData.vContours.length; i++) {
450           short colix = colixes[i % colixes.length];
451           ((short[]) jvxlData.vContours[i].get(JvxlCoder.CONTOUR_COLIX))[0] = colix;
452           ((int[]) jvxlData.vContours[i].get(JvxlCoder.CONTOUR_COLOR))[0] = C
453               .getArgb(colix);
454         }
455       return;
456     }
457     short defaultColix = 0;
458     pcs = new short[pc];
459     colorsExplicit = false;
460     for (int i = 0; i < pc; i++) {
461       int[] p = pis[i];
462       if (p == null)
463         continue;
464       pcs[i] = defaultColix;
465       float v = (vvs[p[0]] + vvs[p[1]] + vvs[p[2]]) / 3;
466       for (int j = n; --j >= 0;) {
467         if (v >= values[j] && v < vMax) {
468           pcs[i] = (haveColixes ? colixes[j % colixes.length] : 0);
469           break;
470         }
471       }
472     }
473   }
474 
475   /**
476    *
477    * @param vwr
478    * @return a Hashtable containing "values" and "colors"
479    *
480    */
getContourList(Viewer vwr)481   Map<String, Object> getContourList(Viewer vwr) {
482     Map<String, Object> ht = new Hashtable<String, Object>();
483     ht.put("values",
484         (jvxlData.contourValuesUsed == null ? jvxlData.contourValues
485             : jvxlData.contourValuesUsed));
486     Lst<P3> colors = new  Lst<P3>();
487     if (jvxlData.contourColixes != null) {
488       // set in SurfaceReader.colorData()
489       for (int i = 0; i < jvxlData.contourColixes.length; i++) {
490         colors.addLast(CU.colorPtFromInt(C
491             .getArgb(jvxlData.contourColixes[i]), null));
492       }
493       ht.put("colors", colors);
494     }
495     return ht;
496   }
497 
deleteContours()498   void deleteContours() {
499     jvxlData.contourValuesUsed = null;
500     jvxlData.contourValues = null;
501     jvxlData.contourColixes = null;
502     jvxlData.vContours = null;
503   }
504 
setVertexColorMap()505   void setVertexColorMap() {
506     vertexColorMap = new Hashtable<String, BS>();
507     short lastColix = -999;
508     BS bs = null;
509     for (int i = vc; --i >= 0;) {
510       short c = vcs[i];
511       if (c != lastColix) {
512         String color = C.getHexCode(lastColix = c);
513         bs = vertexColorMap.get(color);
514         if (bs == null)
515           vertexColorMap.put(color, bs = new BS());
516       }
517       bs.set(i);
518     }
519   }
520 
setVertexColixesForAtoms(Viewer vwr, short[] colixes, int[] atomMap, BS bs)521   void setVertexColixesForAtoms(Viewer vwr, short[] colixes, int[] atomMap,
522                                 BS bs) {
523     jvxlData.vertexDataOnly = true;
524     jvxlData.vertexColors = new int[vc];
525     jvxlData.nVertexColors = vc;
526     Atom[] atoms = vwr.ms.at;
527     GData gdata = vwr.gdata;
528     for (int i = mergeVertexCount0; i < vc; i++) {
529       int iAtom = vertexSource[i];
530       if (iAtom < 0 || !bs.get(iAtom))
531         continue;
532       jvxlData.vertexColors[i] = gdata.getColorArgbOrGray(vcs[i] = C
533           .copyColixTranslucency(colix, atoms[iAtom].colixAtom));
534 
535       short colix = (colixes == null ? C.INHERIT_ALL : colixes[atomMap[iAtom]]);
536       if (colix == C.INHERIT_ALL)
537         colix = atoms[iAtom].colixAtom;
538       vcs[i] = C.copyColixTranslucency(this.colix, colix);
539     }
540   }
541 
542   /**
543    * color a specific set of vertices a specific color
544    *
545    * @param colix
546    * @param bs
547    * @param isAtoms
548    */
colorVertices(short colix, BS bs, boolean isAtoms)549   void colorVertices(short colix, BS bs, boolean isAtoms) {
550     if (vertexSource == null)
551       return;
552     colix = C.copyColixTranslucency(this.colix, colix);
553     BS bsVertices = (isAtoms ? new BS() : bs);
554     checkAllocColixes();
555     // TODO: color translucency?
556     if (isAtoms)
557       for (int i = 0; i < vc; i++) {
558         int pt = vertexSource[i];
559         if (pt >= 0 && bs.get(pt)) {
560           vcs[i] = colix;
561           if (bsVertices != null)
562             bsVertices.set(i);
563         }
564       }
565     else
566       for (int i = 0; i < vc; i++)
567         if (bsVertices.get(i))
568           vcs[i] = colix;
569 
570     if (!isAtoms) {
571       // JVXL file color maps do not have to be saved here.
572       // They are just kept in jvxlData
573       return;
574     }
575     String color = C.getHexCode(colix);
576     if (vertexColorMap == null)
577       vertexColorMap = new Hashtable<String, BS>();
578     addColorToMap(vertexColorMap, color, bs);
579   }
580 
checkAllocColixes()581   void checkAllocColixes() {
582     if (vcs == null || vertexColorMap == null && isColorSolid)
583       allocVertexColixes();
584     isColorSolid = false;
585   }
586 
587   /**
588    * adds a set of specifically-colored vertices to the map,
589    * ensuring that no vertex is in two maps.
590    *
591    * @param colorMap
592    * @param color
593    * @param bs
594    */
addColorToMap(Map<String, BS> colorMap, String color, BS bs)595   private static void addColorToMap(Map<String, BS> colorMap, String color,
596                                     BS bs) {
597     BS bsMap = null;
598     for (Map.Entry<String, BS> entry : colorMap.entrySet())
599       if (entry.getKey() == color) {
600         bsMap = entry.getValue();
601         bsMap.or(bs);
602       } else {
603         entry.getValue().andNot(bs);
604       }
605     if (bsMap == null)
606       colorMap.put(color, bs);
607   }
608 
609   /**
610    * set up the jvxlData fields needed for either just the
611    * header (isAll = false) or the full file (isAll = true)
612    *
613    * @param isAll
614    */
setJvxlColorMap(boolean isAll)615   void setJvxlColorMap(boolean isAll) {
616     jvxlData.diameter = diameter;
617     jvxlData.color = C.getHexCode(colix);
618     jvxlData.meshColor = (meshColix == 0 ? null : C.getHexCode(meshColix));
619     jvxlData.translucency = ((colix & C.TRANSLUCENT_SCREENED) == C.TRANSLUCENT_SCREENED ? -1 : C.getColixTranslucencyFractional(colix));
620     jvxlData.rendering = getRendering().substring(1);
621     jvxlData.colorScheme = (colorEncoder == null ? null : colorEncoder
622         .getColorScheme());
623     if (jvxlData.vertexColors == null)
624       jvxlData.nVertexColors = (vertexColorMap == null ? 0 : vertexColorMap
625           .size());
626     if (vertexColorMap == null || vertexSource == null || !isAll)
627       return;
628     if (jvxlData.vertexColorMap == null)
629       jvxlData.vertexColorMap = new Hashtable<String, BS>();
630     for (Map.Entry<String, BS> entry : vertexColorMap.entrySet()) {
631       BS bsMap = entry.getValue();
632       if (bsMap.isEmpty())
633         continue;
634       String color = entry.getKey();
635       BS bs = new BS();
636       for (int i = 0; i < vc; i++)
637         if (bsMap.get(vertexSource[i]))
638           bs.set(i);
639       addColorToMap(jvxlData.vertexColorMap, color, bs);
640     }
641     jvxlData.nVertexColors = jvxlData.vertexColorMap.size();
642     if (jvxlData.vertexColorMap.size() == 0)
643       jvxlData.vertexColorMap = null;
644   }
645 
646   /**
647    *  just sets the color command for this isosurface.
648    */
setColorCommand()649   void setColorCommand() {
650     if (colorEncoder == null || (colorCommand = colorEncoder.getColorScheme()) == null)
651       return;
652     if (colorCommand.equals("inherit")) {
653       colorCommand = "#inherit;";
654       return;
655     }
656     colorCommand = "color $"  + PT.esc(thisID)  + PT.esc(colorCommand) + " range "
657         + (jvxlData.isColorReversed ? jvxlData.valueMappedToBlue + " "
658             + jvxlData.valueMappedToRed : jvxlData.valueMappedToRed + " "
659             + jvxlData.valueMappedToBlue);
660   }
661 
662 
663   /**
664    * from Isosurface.notifySurfaceGenerationCompleted()
665    *
666    * starting with Jmol 12.1.50, JVXL files contain color, translucency, color
667    * scheme information, and vertex color mappings (as from COLOR ISOSURFACE
668    * {hydrophobic} WHITE), returning these settings when the JVXL file is
669    * opened.
670    *
671    * @param colorRgb
672    * @return true if still need color handling
673    */
setColorsFromJvxlData(int colorRgb)674   boolean setColorsFromJvxlData(int colorRgb) {
675     diameter = jvxlData.diameter;
676     if (colorRgb == -1) {
677     } else if (colorRgb != Integer.MIN_VALUE && colorRgb != Integer.MAX_VALUE) {
678       // max value set when second color option given in isosurface command
679       colix = C.getColix(colorRgb);
680     } else if (jvxlData.color != null) {
681       colix = C.getColixS(jvxlData.color);
682     }
683     if (colix == 0)
684       colix = C.ORANGE;
685     colix = C.getColixTranslucent3(colix, jvxlData.translucency != 0,
686         jvxlData.translucency);
687     float translucencyLevel = (jvxlData.translucency == 0 ? Float.NaN
688         : jvxlData.translucency);
689     if (jvxlData.meshColor != null)
690       meshColix = C.getColixS(jvxlData.meshColor);
691     setJvxlDataRendering();
692 
693     isColorSolid = !jvxlData.isBicolorMap && jvxlData.vertexColors == null
694         && jvxlData.vertexColorMap == null;
695     if (colorEncoder == null)
696       return false;
697     // bicolor map will be taken care of with params.isBicolorMap
698     if (jvxlData.vertexColorMap == null) {
699       if (jvxlData.colorScheme != null) {
700         String colorScheme = jvxlData.colorScheme;
701         boolean isTranslucent = colorScheme.startsWith("translucent ");
702         if (isTranslucent) {
703           colorScheme = colorScheme.substring(12);
704           translucencyLevel = Float.NaN;
705         }
706         colorEncoder.setColorScheme(colorScheme, isTranslucent);
707         remapColors(null, null, translucencyLevel);
708       }
709     } else {
710       if (jvxlData.baseColor != null) {
711         for (int i = vc; --i >= 0;)
712           vcs[i] = colix;
713       }
714       for (Map.Entry<String, BS> entry : jvxlData.vertexColorMap.entrySet()) {
715         BS bsMap = entry.getValue();
716         short colix = C.copyColixTranslucency(this.colix,
717             C.getColixS(entry.getKey()));
718         for (int i = bsMap.nextSetBit(0); i >= 0; i = bsMap.nextSetBit(i + 1))
719           vcs[i] = colix;
720       }
721     }
722     return true;
723   }
724 
setJvxlDataRendering()725   void setJvxlDataRendering() {
726     if (jvxlData.rendering != null) {
727       String[] tokens = PT.getTokens(jvxlData.rendering);
728       for (int i = 0; i < tokens.length; i++)
729         setTokenProperty(T.getTokFromName(tokens[i]), true);
730     }
731   }
732 
733   /**
734    * remaps colors based on a new color scheme or translucency level
735    * @param vwr
736    *
737    * @param ce
738    * @param translucentLevel
739    */
remapColors(Viewer vwr, ColorEncoder ce, float translucentLevel)740   void remapColors(Viewer vwr, ColorEncoder ce, float translucentLevel) {
741     if (ce == null)
742       ce = colorEncoder;
743     if (ce == null)
744       ce = colorEncoder = new ColorEncoder(null, vwr);
745     colorEncoder = ce;
746     setColorCommand();
747     if (Float.isNaN(translucentLevel)) {
748       translucentLevel = C.getColixTranslucencyLevel(colix);
749     } else {
750       colix = C.getColixTranslucent3(colix, true, translucentLevel);
751     }
752     float min = ce.lo;
753     float max = ce.hi;
754     boolean inherit = (vertexSource != null && ce.currentPalette == ColorEncoder.INHERIT);
755     vertexColorMap = null;
756     pcs = null;
757     colorsExplicit = false;
758     jvxlData.baseColor = null;
759     jvxlData.vertexCount = vc;
760     if (vvs == null || jvxlData.vertexCount == 0)
761       return;
762     if (vcs == null || vcs.length != vc)
763       allocVertexColixes();
764     if (inherit) {
765       jvxlData.vertexDataOnly = true;
766       jvxlData.vertexColors = new int[vc];
767       jvxlData.nVertexColors = vc;
768       Atom[] atoms = vwr.ms.at;
769       GData gdata = vwr.gdata;
770       for (int i = mergeVertexCount0; i < vc; i++) {
771         int pt = vertexSource[i];
772         if (pt >= 0 && pt < atoms.length)
773           jvxlData.vertexColors[i] = gdata.getColorArgbOrGray(vcs[i] = C.copyColixTranslucency(colix,
774             atoms[pt].colixAtom));
775       }
776       return;
777     }
778     jvxlData.vertexColors = null;
779     jvxlData.vertexColorMap = null;
780     if (jvxlData.isBicolorMap) {
781       for (int i = mergeVertexCount0; i < vc; i++)
782         vcs[i] = C.copyColixTranslucency(colix,
783             vvs[i] < 0 ? jvxlData.minColorIndex
784                 : jvxlData.maxColorIndex);
785       return;
786     }
787     jvxlData.isColorReversed = ce.isReversed;
788     if (max != Float.MAX_VALUE) {
789       jvxlData.valueMappedToRed = min;
790       jvxlData.valueMappedToBlue = max;
791     }
792     ce.setRange(jvxlData.valueMappedToRed, jvxlData.valueMappedToBlue,
793         jvxlData.isColorReversed);
794     // colix must be translucent if the scheme is translucent
795     // but may be translucent if the scheme is not translucent
796     boolean isTranslucent = C.isColixTranslucent(colix);
797     if (ce.isTranslucent) {
798       if (!isTranslucent)
799         colix = C.getColixTranslucent3(colix, true, 0.5f);
800       // still, if the scheme is translucent, we don't want to color the vertices translucent
801       isTranslucent = false;
802     }
803     vcs = AU.ensureLengthShort(vcs, vc); // when reading contour plane may be remapped
804     for (int i = vc; --i >= mergeVertexCount0;)
805       vcs[i] = ce.getColorIndex(vvs[i]);
806     setTranslucent(isTranslucent, translucentLevel);
807     colorEncoder = ce;
808     Lst<Object>[] contours = getContours();
809     if (contours != null) {
810       for (int i = contours.length; --i >= 0;) {
811         float value = ((Float) contours[i].get(JvxlCoder.CONTOUR_VALUE))
812             .floatValue();
813         short[] colix = ((short[]) contours[i].get(JvxlCoder.CONTOUR_COLIX));
814         colix[0] = ce.getColorIndex(value);
815         int[] color = ((int[]) contours[i].get(JvxlCoder.CONTOUR_COLOR));
816         color[0] = C.getArgb(colix[0]);
817       }
818     }
819     if (contourValues != null) {
820       contourColixes = new short[contourValues.length];
821       for (int i = 0; i < contourValues.length; i++)
822         contourColixes[i] = ce.getColorIndex(contourValues[i]);
823       setDiscreteColixes(null, null);
824     }
825     jvxlData.isJvxlPrecisionColor = true;
826     JvxlCoder.jvxlCreateColorData(jvxlData, vvs);
827     setColorCommand();
828     isColorSolid = false;
829   }
830 
reinitializeLightingAndColor(Viewer vwr)831   public void reinitializeLightingAndColor(Viewer vwr) {
832     initialize(lighting, null, null);
833     if (colorEncoder != null || jvxlData.isBicolorMap) {
834       vcs = null;
835       remapColors(vwr, null, Float.NaN);
836     }
837   }
838 
839   @Override
getBoundingBox()840   public P3[] getBoundingBox() {
841     return jvxlData.boundingBox;
842   }
843 
844   @Override
setBoundingBox(P3[] pts)845   public void setBoundingBox(P3[] pts) {
846     jvxlData.boundingBox = pts;
847   }
848 
849   //private void dumpData() {
850   //for (int i =0;i<10;i++) {
851   //  System.out.println("P["+i+"]="+polygonIndexes[i][0]+" "+polygonIndexes[i][1]+" "+polygonIndexes[i][2]+" "+ polygonIndexes[i][3]+" "+vertices[i]);
852   //}
853   //}
854 
merge(MeshData m)855   protected void merge(MeshData m) {
856     int nV = vc + (m == null ? 0 : m.vc);
857     if (pis == null)
858       pis = new int[0][0];
859     if (m != null && m.pis == null)
860       m.pis = new int[0][0];
861     int nP = (bsSlabDisplay == null || pc == 0 ? pc : bsSlabDisplay
862         .cardinality())
863         + (m == null || m.pc == 0 ? 0 : m.bsSlabDisplay == null ? m.pc
864             : m.bsSlabDisplay.cardinality());
865     if (vs == null)
866       vs = new P3[0];
867     vs = (T3[]) AU.ensureLength(vs, nV);
868     vvs = AU.ensureLengthA(vvs, nV);
869     boolean haveSources = (vertexSource != null && (m == null || m.vertexSource != null));
870     vertexSource = AU.ensureLengthI(vertexSource, nV);
871     int[][] newPolygons = AU.newInt2(nP);
872     // note -- no attempt here to merge vertices
873     int ipt = mergePolygons(this, 0, 0, newPolygons);
874     if (m != null) {
875       ipt = mergePolygons(m, ipt, vc, newPolygons);
876       for (int i = 0; i < m.vc; i++, vc++) {
877         vs[vc] = m.vs[i];
878         vvs[vc] = m.vvs[i];
879         if (haveSources)
880           vertexSource[vc] = m.vertexSource[i];
881       }
882     }
883     pc = polygonCount0 = nP;
884     vc = vertexCount0 = nV;
885     if (nP > 0)
886       resetSlab();
887     pis = newPolygons;
888   }
889 
mergePolygons(MeshSurface m, int ipt, int vertexCount, int[][] newPolygons)890   private static int mergePolygons(MeshSurface m, int ipt, int vertexCount, int[][] newPolygons) {
891     int[] p;
892     for (int i = 0; i < m.pc; i++) {
893       if ((p = m.pis[i]) == null || m.bsSlabDisplay != null && !m.bsSlabDisplay.get(i))
894         continue;
895       newPolygons[ipt++] = m.pis[i];
896       if (vertexCount > 0)
897         for (int j = 0; j < 3; j++)
898           p[j] += vertexCount;
899     }
900     //System.out.println("isosurfaceMesh mergePolygons " + m.polygonCount + " " + m.polygonIndexes.length);
901     return ipt;
902   }
903 
904   @Override
getUnitCell()905   public SymmetryInterface getUnitCell() {
906     return (unitCell != null
907         || (unitCell = vwr.ms.am[modelIndex].biosymmetry) != null
908         || (unitCell = vwr.ms.getUnitCell(modelIndex)) != null
909         || oabc != null && (unitCell = Interface.getSymmetry(vwr, "symmetry").getUnitCell(
910             oabc, true, null)) != null ? unitCell : null);
911   }
912 
fixLattice()913   void fixLattice() {
914     if (getUnitCell() == null)
915       return;
916     P3i minXYZ = new P3i();
917     P3i maxXYZ = P3i.new3((int) lattice.x, (int) lattice.y, (int) lattice.z);
918     jvxlData.fixedLattice = lattice;
919     lattice = null;
920     SimpleUnitCell.setMinMaxLatticeParameters((int) unitCell.getUnitCellInfoType(SimpleUnitCell.INFO_DIMENSIONS), minXYZ, maxXYZ, 0);
921     int nCells = (maxXYZ.x - minXYZ.x) * (maxXYZ.y - minXYZ.y)
922         * (maxXYZ.z - minXYZ.z);
923     P3 latticeOffset = new P3();
924     int vc0 = vc;
925     int vcNew = nCells * vc;
926     vs = AU.arrayCopyPt(vs, vcNew);
927     vvs = (vvs == null ? null : AU.ensureLengthA(vvs, vcNew));
928     int pc0 = pc;
929     int pcNew = nCells * pc;
930     pis = AU.arrayCopyII(pis, pcNew);
931     int off = 0;
932     normixes = AU.arrayCopyShort(normixes, vcNew);
933     for (int tx = minXYZ.x; tx < maxXYZ.x; tx++)
934       for (int ty = minXYZ.y; ty < maxXYZ.y; ty++)
935         for (int tz = minXYZ.z; tz < maxXYZ.z; tz++) {
936           if (tx == 0 && ty == 0 && tz == 0)
937             continue;
938           latticeOffset.set(tx, ty, tz);
939           unitCell.toCartesian(latticeOffset, false);
940           for (int i = 0; i < vc0; i++) {
941             normixes[vc] = normixes[i];
942             P3 v = P3.newP(vs[i]);
943             v.add(latticeOffset);
944             addVCVal(v, vvs[i], false);
945           }
946           off += vc0;
947           for (int i = 0; i < pc0; i++) {
948             int[] p = AU.arrayCopyI(pis[i], -1);
949             p[0] += off;
950             p[1] += off;
951             p[2] += off;
952             addPolygon(p, null);
953           }
954         }
955     P3 xyzMin = new P3();
956     P3 xyzMax = new P3();
957     setBox(xyzMin, xyzMax);
958     jvxlData.boundingBox = new P3[] { xyzMin, xyzMax };
959 
960   }
961 
962   @Override
getMinDistance2ForVertexGrouping()963   protected float getMinDistance2ForVertexGrouping() {
964     if (jvxlData.boundingBox != null && jvxlData.boundingBox[0] != null) {
965       float d2 = jvxlData.boundingBox[1]
966           .distanceSquared(jvxlData.boundingBox[0]);
967       if (d2 < 5)
968         return 1e-10f;
969     }
970     return 1e-8f;
971   }
972 
973   @Override
getVisibleVertexBitSet()974   public BS getVisibleVertexBitSet() {
975     BS bs = getVisibleVBS();
976     if (jvxlData.thisSet != null)
977       for (int i = 0; i < vc; i++)
978         if (!jvxlData.thisSet.get(vertexSets[i]))
979           bs.clear(i);
980    return bs;
981   }
982 
983   /**
984    *
985    * bs will be null if this is a set from the new isosurface MOVE [mat4]
986    * command
987    *
988    * @param m
989    * @param bs
990    */
updateCoordinates(M4 m, BS bs)991   public void updateCoordinates(M4 m, BS bs) {
992     boolean doUpdate = (bs == null || isModelConnected);
993     if (!doUpdate)
994       for (int i = 0; i < connectedAtoms.length; i++)
995         if (connectedAtoms[i] >= 0 && bs.get(connectedAtoms[i])) {
996           doUpdate = true;
997           break;
998         }
999     if (!doUpdate)
1000       return;
1001     if (isModelConnected) {
1002       mat4 = vwr.ms.am[modelIndex].mat4;
1003     } else {
1004       if (mat4 == null)
1005         mat4 = M4.newM4(null);
1006       mat4.mul2(m, mat4);
1007     }
1008     recalcAltVertices = true;
1009   }
1010 
getDataMinMax()1011   float[] getDataMinMax() {
1012     float min = Float.MAX_VALUE;
1013     float max = Float.MIN_VALUE;
1014     for (int i = vvs.length; --i >= 0;) {
1015       float v = vvs[i];
1016       if (v < min)
1017         min = v;
1018       if (v > max)
1019         max = v;
1020     }
1021     return new float[] { min, max };
1022   }
1023 
getDataRange()1024   float[] getDataRange() {
1025     return (jvxlData.jvxlPlane != null
1026         && colorEncoder == null ? null : new float[] {
1027         jvxlData.mappedDataMin,
1028         jvxlData.mappedDataMax,
1029         (jvxlData.isColorReversed ? jvxlData.valueMappedToBlue
1030             : jvxlData.valueMappedToRed),
1031         (jvxlData.isColorReversed ? jvxlData.valueMappedToRed
1032             : jvxlData.valueMappedToBlue) });
1033   }
1034 
1035   @Override
getInfo(boolean isAll)1036   public Object getInfo(boolean isAll) {
1037     @SuppressWarnings("unchecked")
1038     Hashtable<String, Object> info = (Hashtable<String, Object>) super.getInfo(
1039         isAll);
1040     if (isAll) {
1041       BS bs = new BS();
1042       T3[] valid = getValidVertices(bs);
1043       if (valid != null) {
1044         info.put("allVertices", info.get("vertices"));
1045         info.put("vertices", valid);
1046         Object values = info.get("vertexValues");
1047         if (values != null) {
1048           float[] v = getValidValues(bs);
1049           info.put("allValues",  values);
1050           info.put("vertexValues", v);
1051         }
1052       }
1053     }
1054     return info;
1055   }
1056 
getValidValues(BS bs)1057   public float[] getValidValues(BS bs) {
1058     if (bs == null)
1059       getInvalidBS(bs = new BS());
1060     int n = vc - bs.cardinality();
1061     float[] v = new float[n];
1062     for (int pt = 0, i = bs.nextClearBit(0); i >= 0 && i < vc; i = bs.nextClearBit(i + 1))
1063       v[pt++] = vvs[i];
1064     return v;
1065   }
1066 
getValidVertices(BS bs)1067   public T3[] getValidVertices(BS bs) {
1068     boolean allowNull = (bs != null);
1069     if (bs == null)
1070       bs = new BS();
1071     getInvalidBS(bs);
1072     int n = vc - bs.cardinality();
1073     if (n == vc && allowNull) {
1074       return null;
1075     }
1076     T3[] pa = new P3[n];
1077     for (int pt = 0, i = bs.nextClearBit(0); i >= 0
1078         && pt < n; i = bs.nextClearBit(i + 1)) {
1079       pa[pt++] = vs[i];
1080     }
1081     return pa;
1082   }
1083 
getInvalidBS(BS bs)1084   private void getInvalidBS(BS bs) {
1085     BS[] excluded = jvxlData.jvxlExcluded;
1086     BS invalid = excluded[1];
1087     BS thisSet = jvxlData.thisSet;
1088     if (invalid != null)
1089       bs.or(invalid);
1090     if (thisSet != null) {
1091       for (int i = vc; --i >= 0;) {
1092         if (!thisSet.get(vertexSets[i]))
1093           bs.set(i);
1094       }
1095     }
1096   }
1097 }
1098