1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2007-03-30 11:40:16 -0500 (Fri, 30 Mar 2007) $
4  * $Revision: 7273 $
5  *
6  * Copyright (C) 2007 Miguel, Bob, Jmol Development
7  *
8  * Contact: hansonr@stolaf.edu
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 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 package org.jmol.jvxl.readers;
25 
26 
27 import java.io.BufferedReader;
28 
29 import javajs.util.AU;
30 import javajs.util.CU;
31 import javajs.util.Lst;
32 import javajs.util.PT;
33 import javajs.util.SB;
34 import javajs.util.T3;
35 
36 import java.util.Hashtable;
37 
38 
39 
40 import javajs.util.BS;
41 import org.jmol.jvxl.data.JvxlCoder;
42 import org.jmol.jvxl.data.JvxlData;
43 import org.jmol.jvxl.data.MeshData;
44 import org.jmol.shapesurface.IsosurfaceMesh;
45 import org.jmol.util.C;
46 import org.jmol.util.ColorEncoder;
47 import org.jmol.util.Escape;
48 import org.jmol.util.Logger;
49 
50 import javajs.util.P3;
51 import javajs.util.P4;
52 import javajs.util.V3;
53 
54 public class JvxlXmlReader extends VolumeFileReader {
55 
56   protected String JVXL_VERSION = "2.3";
57   // 2.3 adds encoding "none"
58   // 2.2 adds full support for return of rendering information,
59   //     retrieval of triangle edge data when edges have been modified by slabbing.
60 
61   protected int surfaceDataCount;
62   protected int edgeDataCount;
63   protected int colorDataCount;
64   private int excludedTriangleCount;
65   private int excludedVertexCount;
66   private int invalidatedVertexCount;
67   protected boolean haveContourData;
68 
69   private XmlReader xr;
70 
71   protected boolean isXmlFile= true;
72 
JvxlXmlReader()73   JvxlXmlReader(){}
74 
75   @Override
init2(SurfaceGenerator sg, BufferedReader br)76   void init2(SurfaceGenerator sg, BufferedReader br) {
77     init2JXR(sg, br);
78   }
79 
init2JXR(SurfaceGenerator sg, BufferedReader br)80   void init2JXR(SurfaceGenerator sg, BufferedReader br) {
81     init2VFR(sg, br);
82     jvxlData.wasJvxl = isJvxl = true;
83     isXLowToHigh = canDownsample = false;
84     xr = new XmlReader(br);
85   }
86 
87   protected boolean thisInside;
88 
89   /////////////reading the format///////////
90 
91   @Override
readVolumeData(boolean isMapData)92   protected boolean readVolumeData(boolean isMapData) {
93     if (!readVolumeDataVFR(isMapData))
94       return false;
95     strFractionTemp = jvxlEdgeDataRead;
96     fractionPtr = 0;
97     return true;
98   }
99 
100   @Override
gotoAndReadVoxelData(boolean isMapData)101   protected boolean gotoAndReadVoxelData(boolean isMapData) {
102     initializeVolumetricData();
103     if (nPointsX < 0 || nPointsY < 0 || nPointsZ < 0)
104       return true;
105     try {
106       gotoData(params.fileIndex - 1, nPointsX * nPointsY * nPointsZ);
107       if (vertexDataOnly)
108         return true;
109       volumeData.setMappingPlane(params.thePlane);
110       readSurfaceData(isMapData);
111       volumeData.setMappingPlane(null);
112 
113       if (edgeDataCount > 0)
114         jvxlEdgeDataRead = jvxlReadFractionData("edge", edgeDataCount);
115       params.bsExcluded = jvxlData.jvxlExcluded = new BS[4];
116       hasColorData = (colorDataCount > 0); // for nonXML version of JVXL
117       if (hasColorData)
118         jvxlColorDataRead = jvxlReadFractionData("color", colorDataCount);
119       if (excludedVertexCount > 0) {
120         jvxlData.jvxlExcluded[0] = JvxlCoder.jvxlDecodeBitSet(xr.getXmlData(
121             "jvxlExcludedVertexData", null, false, false));
122         if (xr.isNext("jvxlExcludedPlaneData"))
123           jvxlData.jvxlExcluded[2] = JvxlCoder.jvxlDecodeBitSet(xr.getXmlData(
124               "jvxlExcludedPlaneData", null, false, false));
125       }
126       if (excludedTriangleCount > 0)
127         jvxlData.jvxlExcluded[3] = JvxlCoder.jvxlDecodeBitSet(xr.getXmlData(
128             "jvxlExcludedTriangleData", null, false, false));
129       if (invalidatedVertexCount > 0)
130         jvxlData.jvxlExcluded[1] = JvxlCoder.jvxlDecodeBitSet(xr.getXmlData(
131             "jvxlInvalidatedVertexData", null, false, false));
132       if (haveContourData)
133         jvxlDecodeContourData(jvxlData, xr.getXmlData("jvxlContourData", null,
134             false, false));
135       if (jvxlDataIsColorMapped && jvxlData.nVertexColors > 0) {
136         jvxlData.vertexColorMap = new Hashtable<String, BS>();
137         String vdata = xr.getXmlData("jvxlVertexColorData", null, true, false);
138         String baseColor = XmlReader.getXmlAttrib(vdata, "baseColor");
139         jvxlData.baseColor = (baseColor.length() > 0 ? baseColor : null);
140         for (int i = 0; i < jvxlData.nVertexColors; i++) {
141           String s = xr.getXmlData("jvxlColorMap", vdata, true, false);
142           String color = XmlReader.getXmlAttrib(s, "color");
143           BS bs = JvxlCoder.jvxlDecodeBitSet(xr.getXmlData("jvxlColorMap",
144               s, false, false));
145           jvxlData.vertexColorMap.put(color, bs);
146         }
147       }
148 
149     } catch (Exception e) {
150       Logger.error(e.toString());
151       return false;
152     }
153     return true;
154   }
155 
156   String tempDataXml;
157 
158   @Override
readParameters()159   protected void readParameters() throws Exception {
160     String s = xr.getXmlDataLF("jvxlFileTitle", null, false, false, true);
161     jvxlFileHeaderBuffer = SB.newS(s == null ? "" : s);
162     xr.toTag("jvxlVolumeData");
163     String data = tempDataXml = xr.getXmlData("jvxlVolumeData", null, true, false);
164     volumetricOrigin.setT(xr.getXmlPoint(data, "origin"));
165    isAngstroms = true;
166    readVector(0);
167    readVector(1);
168    readVector(2);
169    line = xr.toTag("jvxlSurfaceSet");
170    nSurfaces = parseIntStr(XmlReader.getXmlAttrib(line, "count"));
171    Logger.info("jvxl file surfaces: " + nSurfaces);
172    Logger.info("using default edge fraction base and range");
173    Logger.info("using default color fraction base and range");
174    cJvxlEdgeNaN = (char) (edgeFractionBase + edgeFractionRange);
175   }
176 
readVector(int voxelVectorIndex)177   protected void readVector(int voxelVectorIndex) throws Exception {
178     String data = xr.getXmlData("jvxlVolumeVector", tempDataXml, true, true);
179     tempDataXml = tempDataXml.substring(tempDataXml.indexOf(data) + data.length());
180     int n = parseIntStr(XmlReader.getXmlAttrib(data, "count"));
181     if (n == Integer.MIN_VALUE)
182       vertexDataOnly = true;
183     voxelCounts[voxelVectorIndex] = (n < 0 ? 0 : n);
184     volumetricVectors[voxelVectorIndex].setT(xr.getXmlPoint(data, "vector"));
185     if (isAnisotropic)
186       setVectorAnisotropy(volumetricVectors[voxelVectorIndex]);
187   }
188 
189   @Override
gotoData(int n, int nPoints)190   protected void gotoData(int n, int nPoints) throws Exception {
191     if (n > 0)
192       Logger.info("skipping " + n + " data sets, " + nPoints + " points each");
193     vertexDataOnly = jvxlData.vertexDataOnly = (nPoints == 0);
194     for (int i = 0; i < n; i++) {
195       jvxlSkipData(nPoints, true);
196     }
197     xr.toTag("jvxlSurface");
198     jvxlReadSurfaceInfo();
199   }
200 
jvxlSkipData(@uppressWarningsR) int nPoints, @SuppressWarnings(R) boolean doSkipColorData)201   protected void jvxlSkipData(@SuppressWarnings("unused") int nPoints,
202                               @SuppressWarnings("unused") boolean doSkipColorData)
203       throws Exception {
204     rd();
205     xr.skipTag("jvxlSurface");
206   }
207 
jvxlReadSurfaceInfo()208   protected void jvxlReadSurfaceInfo() throws Exception {
209     String s;
210     String data = xr.getXmlData("jvxlSurfaceInfo", null, true, true);
211     isXLowToHigh = XmlReader.getXmlAttrib(data, "isXLowToHigh").equals("true");
212     jvxlCutoff = parseFloatStr(XmlReader.getXmlAttrib(data, "cutoff"));
213     if (!Float.isNaN(jvxlCutoff))
214       Logger.info("JVXL read: cutoff " + jvxlCutoff);
215     int nContourData = parseIntStr(XmlReader.getXmlAttrib(data, "nContourData"));
216     haveContourData = (nContourData > 0);
217     params.isContoured = jvxlData.isModelConnected = XmlReader.getXmlAttrib(data, "contoured").equals("true");
218     params.isModelConnected = XmlReader.getXmlAttrib(data, "isModelConnected").equals("true");
219     if (params.isContoured) {
220       int nContoursRead = parseIntStr(XmlReader.getXmlAttrib(data, "nContours"));
221       if (nContoursRead <= 0) {
222         nContoursRead = 0;
223       } else {
224         if (params.thisContour < 0)
225           params.thisContour = parseIntStr(XmlReader.getXmlAttrib(data, "thisContour"));
226         s = XmlReader.getXmlAttrib(data, "contourValues");
227         if (s.length() > 0) {
228           s = s.replace('[',' ').replace(']',' ');
229           jvxlData.contourValues = params.contoursDiscrete = parseFloatArrayStr(s);
230           Logger.info("JVXL read: contourValues " + Escape.eAF(jvxlData.contourValues));
231         }
232         s = XmlReader.getXmlAttrib(data, "contourColors");
233         if (s.length() > 0) {
234           jvxlData.contourColixes = params.contourColixes = C.getColixArray(s);
235           jvxlData.contourColors = C.getHexCodes(jvxlData.contourColixes);
236           Logger.info("JVXL read: contourColixes " +
237               C.getHexCodes(jvxlData.contourColixes));        }
238         params.contourFromZero = XmlReader.getXmlAttrib(data, "contourFromZero").equals("true");
239       }
240       params.nContours = (haveContourData ? nContourData : nContoursRead);
241       //TODO ? params.contourFromZero = false; // MEP data to complete the plane
242     }
243 
244     jvxlData.nVertexColors = parseIntStr(XmlReader.getXmlAttrib(data, "nVertexColors"));
245     params.isBicolorMap = XmlReader.getXmlAttrib(data, "bicolorMap").equals("true");
246     if (params.isBicolorMap) {
247       // TODO -- not quite right, because
248       s = XmlReader.getXmlAttrib(data, "colorPositive");
249       if (s.length() > 0 && params.colorRgb == Integer.MIN_VALUE
250           && params.colorPos == Parameters.defaultColorPositive)
251         params.colorPos = CU.getArgbFromString(s);
252       s = XmlReader.getXmlAttrib(data, "colorNegative");
253       if (s.length() > 0 && params.colorRgb == Integer.MIN_VALUE
254           && params.colorNeg == Parameters.defaultColorNegative)
255         params.colorNeg = CU.getArgbFromString(s);
256     }
257     if (params.isBicolorMap || params.colorBySign)
258       jvxlCutoff = 0;
259     jvxlDataIsColorMapped =
260       ((params.colorRgb == Integer.MIN_VALUE || params.colorRgb == Integer.MAX_VALUE)
261     && (params.isBicolorMap || XmlReader.getXmlAttrib(data, "colorMapped").equals("true")));
262     //isJvxlPrecisionColor is for information only -- will be superceded by encoding attribute of jvxlColorData
263     jvxlData.isJvxlPrecisionColor = XmlReader.getXmlAttrib(data, "precisionColor").equals("true");
264     jvxlData.jvxlDataIsColorDensity = params.colorDensity = (params.colorRgb == Integer.MIN_VALUE && XmlReader.getXmlAttrib(data, "colorDensity").equals("true"));
265     if (jvxlData.jvxlDataIsColorDensity && Float.isNaN(params.pointSize)) {
266       s = XmlReader.getXmlAttrib(data, "pointSize");
267       if (s.length() > 0)
268         jvxlData.pointSize = params.pointSize = parseFloatStr(s);
269     }
270     s = XmlReader.getXmlAttrib(data, "allowVolumeRender");
271       jvxlData.allowVolumeRender = params.allowVolumeRender = (s.length() == 0 || s.equalsIgnoreCase("true"));
272     s = XmlReader.getXmlAttrib(data, "plane");
273     if (s.indexOf("{") >= 0) {
274       params.thePlane = null;
275       params.mapLattice = null;
276       try {
277         params.thePlane = (P4) Escape.uP(s);
278         s = XmlReader.getXmlAttrib(data, "maplattice");
279         Logger.info("JVXL read: plane " + params.thePlane);
280         if (s.indexOf("{") >= 0) {
281           params.mapLattice = (P3) Escape.uP(s);
282           Logger.info("JVXL read: mapLattice " + params.mapLattice);
283         }
284         if (params.scale3d == 0)
285           params.scale3d = parseFloatStr(XmlReader.getXmlAttrib(data, "scale3d"));
286         if (Float.isNaN(params.scale3d))
287           params.scale3d = 0;
288       } catch (Exception e) {
289         if (params.thePlane == null) {
290           Logger
291               .error("JVXL Error reading plane definition -- setting to 0 0 1 0  (z=0)");
292           params.thePlane = P4.new4(0, 0, 1, 0);
293         } else {
294           Logger
295           .error("JVXL Error reading mapLattice definition -- ignored");
296         }
297       }
298       surfaceDataCount = 0;
299       edgeDataCount = 0;
300     } else {
301       params.thePlane = null;
302       surfaceDataCount = parseIntStr(XmlReader.getXmlAttrib(data, "nSurfaceInts"));
303       edgeDataCount = parseIntStr(XmlReader.getXmlAttrib(data, "nBytesUncompressedEdgeData"));
304       s = XmlReader.getXmlAttrib(data, "fixedLattice");
305       if (s.indexOf("{") >= 0)
306         jvxlData.fixedLattice = (P3) Escape.uP(s);
307 
308     }
309     excludedVertexCount = parseIntStr(XmlReader.getXmlAttrib(data, "nExcludedVertexes"));
310     excludedTriangleCount = parseIntStr(XmlReader.getXmlAttrib(data, "nExcludedTriangles"));
311     invalidatedVertexCount = parseIntStr(XmlReader.getXmlAttrib(data, "nInvalidatedVertexes"));
312     s = XmlReader.getXmlAttrib(data, "slabInfo");
313     if (s.length() > 0)
314       jvxlData.slabInfo = s;
315     colorDataCount = Math.max(0, parseIntStr(XmlReader.getXmlAttrib(data, "nBytesUncompressedColorData")));
316     jvxlDataIs2dContour = (params.thePlane != null && jvxlDataIsColorMapped);
317 
318     // new Jmol 12.1.50
319     jvxlData.color = XmlReader.getXmlAttrib(data, "color");
320     if (jvxlData.color.length() == 0 || jvxlData.color.indexOf("null") >= 0)
321       jvxlData.color = "orange";
322     jvxlData.translucency = parseFloatStr(XmlReader.getXmlAttrib(data, "translucency"));
323     if (Float.isNaN(jvxlData.translucency))
324       jvxlData.translucency = 0;
325     s = XmlReader.getXmlAttrib(data, "meshColor");
326     if (s.length() > 0)
327       jvxlData.meshColor = s;
328     s = XmlReader.getXmlAttrib(data, "rendering");
329     if (s.length() > 0)
330       jvxlData.rendering = s;
331     jvxlData.colorScheme = XmlReader.getXmlAttrib(data, "colorScheme");
332     if (jvxlData.colorScheme.length() == 0)
333       jvxlData.colorScheme = (jvxlDataIsColorMapped ? "roygb": null); // allow for legacy default
334     if (jvxlData.thisSet == null) {
335       int n = parseIntStr(XmlReader.getXmlAttrib(data, "set"));
336       if (n > 0) {
337         jvxlData.thisSet = new BS();
338         jvxlData.thisSet.set(n - 1);
339       }
340       String a = XmlReader.getXmlAttrib(data,  "subset");
341       if (a != null && a.length() > 2) {
342         String[] sets = a.replace('[', ' ').replace(']', ' ').trim().split(" ");
343         if (sets.length > 0) {
344           jvxlData.thisSet = new BS();
345           for (int i = sets.length; --i >= 0;) {
346             jvxlData.thisSet.set(PT.parseInt(sets[i]) - 1);
347           }
348 
349         }
350       }
351     }
352     jvxlData.slabValue = parseIntStr(XmlReader.getXmlAttrib(data, "slabValue"));
353     jvxlData.isSlabbable = (XmlReader.getXmlAttrib(data, "slabbable").equalsIgnoreCase("true"));
354     jvxlData.diameter = parseIntStr(XmlReader.getXmlAttrib(data, "diameter"));
355     if (jvxlData.diameter == Integer.MIN_VALUE)
356       jvxlData.diameter = 0;
357 
358     if (jvxlDataIs2dContour)
359       params.isContoured = true;
360 
361     if (params.colorBySign)
362       params.isBicolorMap = true;
363     boolean insideOut = XmlReader.getXmlAttrib(data, "insideOut").equals("true");
364     float dataMin = Float.NaN;
365     float dataMax = Float.NaN;
366     float red = Float.NaN;
367     float blue = Float.NaN;
368     if (jvxlDataIsColorMapped) {
369       dataMin = parseFloatStr(XmlReader.getXmlAttrib(data, "dataMinimum"));
370       dataMax = parseFloatStr(XmlReader.getXmlAttrib(data, "dataMaximum"));
371       red = parseFloatStr(XmlReader.getXmlAttrib(data, "valueMappedToRed"));
372       blue = parseFloatStr(XmlReader.getXmlAttrib(data, "valueMappedToBlue"));
373       if (Float.isNaN(dataMin)) {
374         dataMin = red = -1f;
375         dataMax = blue = 1f;
376       }
377     }
378     jvxlSetColorRanges(dataMin, dataMax, red, blue, insideOut);
379   }
380 
jvxlSetColorRanges(float dataMin, float dataMax, float red, float blue, boolean insideOut)381   protected void jvxlSetColorRanges(float dataMin, float dataMax, float red,
382                                     float blue, boolean insideOut) {
383     if (jvxlDataIsColorMapped) {
384       if (!Float.isNaN(dataMin) && !Float.isNaN(dataMax)) {
385         if (dataMax == 0 && dataMin == 0) {
386           //set standard -1/1; bit of a hack
387           dataMin = -1;
388           dataMax = 1;
389         }
390         params.mappedDataMin = dataMin;
391         params.mappedDataMax = dataMax;
392         Logger.info("JVXL read: data_min/max " + params.mappedDataMin + "/"
393             + params.mappedDataMax);
394       }
395       if (!params.rangeDefined)
396         if (!Float.isNaN(red) && !Float.isNaN(blue)) {
397           if (red == 0 && blue == 0) {
398             //set standard -1/1; bit of a hack
399             red = -1;
400             blue = 1;
401           }
402           params.valueMappedToRed = Math.min(red, blue);
403           params.valueMappedToBlue = Math.max(red, blue);
404           params.isColorReversed = (red > blue);
405           params.rangeDefined = true;
406         } else {
407           params.valueMappedToRed = 0f;
408           params.valueMappedToBlue = 1f;
409           params.rangeDefined = true;
410         }
411       Logger.info("JVXL read: color red/blue: " + params.valueMappedToRed + "/"
412           + params.valueMappedToBlue);
413     }
414     jvxlData.valueMappedToRed = params.valueMappedToRed;
415     jvxlData.valueMappedToBlue = params.valueMappedToBlue;
416     jvxlData.mappedDataMin = params.mappedDataMin;
417     jvxlData.mappedDataMax = params.mappedDataMax;
418     jvxlData.isColorReversed = params.isColorReversed;
419     if (params.insideOut)
420       insideOut = !insideOut;
421     params.insideOut = jvxlData.insideOut = insideOut;
422   }
423 
424   @Override
readSurfaceData(boolean isMapDataIgnored)425   protected void readSurfaceData(boolean isMapDataIgnored) throws Exception {
426     thisInside = !params.isContoured;
427     if (readSurfaceDataXML())
428       return;
429     tempDataXml = xr.getXmlData("jvxlEdgeData", null, true, false);
430     bsVoxelBitSet = JvxlCoder.jvxlDecodeBitSet(xr.getXmlData("jvxlEdgeData",
431         tempDataXml, false, false));
432     // if (thisInside)
433     // bsVoxelBitSet = BitSetUtil.copyInvert(bsVoxelBitSet,
434     // bsVoxelBitSet.size());
435     readSurfaceDataJXR();
436   }
437 
readSurfaceDataXML()438   protected boolean readSurfaceDataXML() throws Exception {
439     if (vertexDataOnly) {
440       getEncodedVertexData();
441       return true;
442     }
443     if (params.thePlane != null) {
444       volumeData.setDataDistanceToPlane(params.thePlane);
445       setVolumeDataV(volumeData);
446       params.cutoff = 0f;
447       jvxlData.setSurfaceInfo(params.thePlane, params.mapLattice, 0, "");
448       jvxlData.scale3d = params.scale3d;
449       return true;
450     }
451     return false;
452   }
453 
readSurfaceDataJXR()454   protected void readSurfaceDataJXR() throws Exception {
455     readSurfaceDataVFR(false);
456     volumeData.setMappingPlane(null);
457   }
458 
459   /**
460    * "edge" data includes two parts -- a compressed bitset indicating exactly which edges,
461    * in order or processing by Jmol, are crossed by the surface, and a set of fractions
462    * indicating how far along that edge (good to 1 part in 8100) that surface crosses that edge.
463    * We are just reading he fractions here.
464    *
465    * "color" data comprises the corresponding sequence of data mapping values, again stored to
466    * a precision of 1 part in 8100, relative to a range of values.
467    *
468    * @param type
469    * @param nPoints
470    * @return data
471    */
jvxlReadFractionData(String type, int nPoints)472   protected String jvxlReadFractionData(String type,
473                                  int nPoints) {
474     String str;
475     try {
476       if (type.equals("edge")) {
477         str = JvxlCoder.jvxlDecompressString(XmlReader.getXmlAttrib(tempDataXml, "data"));
478       } else {
479         String data = xr.getXmlData("jvxlColorData", null, true, false);
480         jvxlData.isJvxlPrecisionColor = getEncoding(data).endsWith("2");
481         str = JvxlCoder.jvxlDecompressString(XmlReader.getXmlAttrib(data, "data"));
482       }
483     } catch (Exception e) {
484       Logger.error("Error reading " + type + " data " + e);
485       throw new NullPointerException();
486     }
487     return str;
488   }
489 
490   protected BS bsVoxelBitSet;
491 
492   @Override
getVoxelBitSet(int nPoints)493   protected BS getVoxelBitSet(int nPoints) throws Exception {
494     if (bsVoxelBitSet != null)
495       return bsVoxelBitSet;
496     BS bs = new BS();
497     int bsVoxelPtr = 0;
498     if (surfaceDataCount <= 0)
499       return bs; //unnecessary -- probably a plane or color density
500     int nThisValue = 0;
501     while (bsVoxelPtr < nPoints) {
502       nThisValue = parseInt();
503       if (nThisValue == Integer.MIN_VALUE) {
504         rd();
505         // note -- does not allow for empty lines;
506         // must be a continuous block of numbers.
507         if (line == null || (nThisValue = parseIntStr(line)) == Integer.MIN_VALUE) {
508           if (!endOfData)
509             Logger.error("end of file in JvxlReader?" + " line=" + line);
510           endOfData = true;
511           nThisValue = 10000;
512           //throw new NullPointerException();
513         }
514       }
515       thisInside = !thisInside;
516       ++jvxlNSurfaceInts;
517       if (thisInside)
518         bs.setBits(bsVoxelPtr, bsVoxelPtr + nThisValue);
519       bsVoxelPtr += nThisValue;
520     }
521     return bs;
522   }
523 
524   @Override
getSurfacePointAndFraction(float cutoff, boolean isCutoffAbsolute, float valueA, float valueB, T3 pointA, V3 edgeVector, int x, int y, int z, int vA, int vB, float[] fReturn, T3 ptReturn)525   protected float getSurfacePointAndFraction(float cutoff,
526                                              boolean isCutoffAbsolute,
527                                              float valueA, float valueB,
528                                              T3 pointA,
529                                              V3 edgeVector,
530                                              int x, int y, int z, int vA, int vB, float[] fReturn, T3 ptReturn) {
531     if (edgeDataCount <= 0)
532       return getSPFv(cutoff, isCutoffAbsolute, valueA,
533           valueB, pointA, edgeVector, x, y, z, vA, vB, fReturn, ptReturn);
534     ptReturn.scaleAdd2(fReturn[0] = jvxlGetNextFraction(edgeFractionBase,
535         edgeFractionRange, 0.5f), edgeVector, pointA);
536     if (Float.isNaN(valueMin))
537       setValueMinMax();
538     return (valueCount == 0 || includeValueNaN && Float.isNaN(fReturn[0])
539         ? fReturn[0] : getNextValue());
540   }
541 
getNextValue()542   private float getNextValue() {
543     float fraction = Float.NaN;
544     while (colorPtr < valueCount && Float.isNaN(fraction)) {
545       if (jvxlData.isJvxlPrecisionColor) {
546         // this COULD be an option for mapped surfaces;
547         // necessary for planes; used for vertex/triangle 2.0 style
548         // precision is used for FULL-data range encoding, allowing full
549         // treatment of JVXL files as though they were CUBE files.
550         // the two parts of the "double-character-precision" value
551         // are in separate lines, separated by n characters.
552         fraction = JvxlCoder.jvxlFractionFromCharacter2(jvxlColorDataRead
553             .charAt(colorPtr), jvxlColorDataRead.charAt((colorPtr++)
554             + valueCount), colorFractionBase, colorFractionRange);
555       } else {
556         // my original encoding scheme
557         // low precision only allows for mapping relative to the defined color range
558         fraction = JvxlCoder.jvxlFractionFromCharacter(jvxlColorDataRead
559             .charAt(colorPtr++), colorFractionBase, colorFractionRange, 0.5f);
560       }
561       break;
562     }
563     return valueMin + fraction * valueRange;
564   }
565 
setValueMinMax()566   private void setValueMinMax() {
567     valueCount = jvxlColorDataRead.length();
568     if (jvxlData.isJvxlPrecisionColor)
569       valueCount /= 2;
570     includeValueNaN = (valueCount != jvxlEdgeDataRead.length());
571     valueMin = (!jvxlData.isJvxlPrecisionColor ? params.valueMappedToRed
572         : params.mappedDataMin == Float.MAX_VALUE ? defaultMappedDataMin
573             : params.mappedDataMin);
574     valueRange = (!jvxlData.isJvxlPrecisionColor ? params.valueMappedToBlue
575         : params.mappedDataMin == Float.MAX_VALUE ? defaultMappedDataMax
576             : params.mappedDataMax)
577         - valueMin;
578     haveReadColorData = true;
579   }
580 
581   private boolean includeValueNaN = true;
582   private int valueCount;
583   private float valueMin = Float.NaN;
584   private float valueRange = Float.NaN;
585   private int fractionPtr;
586   private int colorPtr;
587   private String strFractionTemp = "";
588 
jvxlGetNextFraction(int base, int range, float fracOffset)589   private float jvxlGetNextFraction(int base, int range, float fracOffset) {
590     if (fractionPtr >= strFractionTemp.length()) {
591       if (!endOfData)
592         Logger.error("end of file reading compressed fraction data");
593       endOfData = true;
594       strFractionTemp = "" + (char) base;
595       fractionPtr = 0;
596     }
597     return JvxlCoder.jvxlFractionFromCharacter(strFractionTemp.charAt(fractionPtr++),
598         base, range, fracOffset);
599   }
600 
601   boolean haveReadColorData;
602 
603   private String jvxlColorEncodingRead;
604 
605   @Override
readColorData()606   protected String readColorData() {
607     if (!jvxlDataIsColorMapped)
608       return "";
609     // overloads SurfaceReader
610     // standard jvxl file read for color
611 
612     int vertexCount = jvxlData.vertexCount = meshData.vc;
613     // the problem is that the new way to read data in Marching Cubes
614     // is to ignore all points that are NaN. But then we also have to
615     // remove those points from the color string.
616 
617     short[] colixes = meshData.vcs;
618     float[] vertexValues = meshData.vvs;
619     /*
620      * haveReadColorData?
621      = (isJvxl ? jvxlColorDataRead : "");
622     if (isJvxl && strValueTemp.length() == 0) {
623       Logger
624           .error("You cannot use JVXL data to map onto OTHER data, because it only contains the data for one surface. Use ISOSURFACE \"file.jvxl\" not ISOSURFACE .... MAP \"file.jvxl\".");
625       return "";
626     }
627     */
628 
629     if ("none".equals(jvxlColorEncodingRead)) {
630       jvxlData.vertexColors = new int[vertexCount];
631       int[] nextc = new int[1];
632       int n = PT.parseIntNext(jvxlColorDataRead, nextc);
633       n = Math.min(n, vertexCount);
634       String[] tokens = PT.getTokens(jvxlColorDataRead.substring(nextc[0]));
635       boolean haveTranslucent = false;
636       float trans = jvxlData.translucency;
637       int lastColor = 0;
638       for (int i = 0; i < n; i++)
639         // colix will be one of 8 shades of translucent if A in ARGB is not FF.
640         try{
641           int c = getColor(tokens[i]);
642           if (c == 0)
643             c = lastColor;
644           else
645             lastColor = c;
646           colixes[i] = C.getColixTranslucent(jvxlData.vertexColors[i] = c);
647           if (C.isColixTranslucent(colixes[i]))
648             haveTranslucent = true;
649           else if (trans != 0)
650             colixes[i] = C.getColixTranslucent3(colixes[i], true, trans);
651         } catch (Exception e) {
652           Logger.info("JvxlXmlReader: Cannot interpret color code: " + tokens[i]);
653           // ignore this color if parsing error
654         }
655       if (haveTranslucent && trans == 0){
656         // set to show in pass2
657         jvxlData.translucency = 0.5f;
658       }
659       return "-";
660     }
661     if (params.colorEncoder == null)
662       params.colorEncoder = new ColorEncoder(null, null);
663     params.colorEncoder.setColorScheme(null, false);
664     params.colorEncoder.setRange(params.valueMappedToRed,
665         params.valueMappedToBlue, params.isColorReversed);
666     Logger.info("JVXL reading color data mapped min/max: "
667         + params.mappedDataMin + "/" + params.mappedDataMax + " for "
668         + vertexCount + " vertices." + " using encoding keys "
669         + colorFractionBase + " " + colorFractionRange);
670     Logger.info("mapping red-->blue for " + params.valueMappedToRed + " to "
671         + params.valueMappedToBlue + " colorPrecision:"
672         + jvxlData.isJvxlPrecisionColor);
673     boolean getValues = (Float.isNaN(valueMin));
674     if (getValues)
675       setValueMinMax();
676     float contourPlaneMinimumValue = Float.MAX_VALUE;
677     float contourPlaneMaximumValue = -Float.MAX_VALUE;
678     if (colixes == null || colixes.length < vertexCount)
679       meshData.vcs = colixes = new short[vertexCount];
680     //hasColorData = true;
681     short colixNeg = 0, colixPos = 0;
682     if (params.colorBySign) {
683       colixPos = C.getColix(params.isColorReversed ? params.colorNeg
684           : params.colorPos);
685       colixNeg = C.getColix(params.isColorReversed ? params.colorPos
686           : params.colorNeg);
687     }
688     int vertexIncrement = meshData.vertexIncrement;
689     // here's the problem: we are assuming here that vertexCount == nPointsRead
690     boolean needContourMinMax = (params.mappedDataMin == Float.MAX_VALUE);
691     for (int i = 0; i < vertexCount; i += vertexIncrement) {
692       float value;
693       if (getValues)
694         value = vertexValues[i] = getNextValue();
695       else
696         value = vertexValues[i];
697       if (needContourMinMax) {
698         if (value < contourPlaneMinimumValue)
699           contourPlaneMinimumValue = value;
700         if (value > contourPlaneMaximumValue)
701           contourPlaneMaximumValue = value;
702       }
703     }
704     if (needContourMinMax) {
705       params.mappedDataMin = contourPlaneMinimumValue;
706       params.mappedDataMax = contourPlaneMaximumValue;
707     }
708     if (jvxlData.colorScheme != null) {
709       boolean setContourValue = (marchingSquares != null && params.isContoured);
710       for (int i = 0; i < vertexCount; i += vertexIncrement) {
711         float value = vertexValues[i];
712         //note: these are just default colorings
713         //orbital color had a bug through 11.2.6/11.3.6
714         if (setContourValue) {
715           marchingSquares.setContourData(i, value);
716           continue;
717         }
718         short colix = (!params.colorBySign ? params.colorEncoder
719             .getColorIndex(value) : (params.isColorReversed ? value > 0
720             : value <= 0) ? colixNeg : colixPos);
721         colixes[i] = C.getColixTranslucent3(colix, true,
722             jvxlData.translucency);
723       }
724     }
725     return jvxlColorDataRead + "\n";
726   }
727 
getColor(String c)728   private static int getColor(String c) {
729     int n = 0;
730     try {
731       switch (c.charAt(0)) {
732       case '[':
733         n = CU.getArgbFromString(c);
734         break;
735       case '0': //0x
736         n = PT.parseIntRadix(c.substring(2), 16);
737         break;
738       default:
739         n = PT.parseIntRadix(c, 10);
740       }
741       //if (n < 0x1000000)
742         //n |= 0xFF000000;
743     } catch (Exception e) {
744     }
745     return n;
746   }
747 
748 
749   /**
750    * retrieve Jvxl 2.0 format vertex/triangle/edge/color data found
751    * within <jvxlSurfaceData> element
752    *
753    * @throws Exception
754    */
getEncodedVertexData()755   protected void getEncodedVertexData() throws Exception {
756     // get vertices
757     String sdata = xr.getXmlData("jvxlSurfaceData", null, true, false);
758     jvxlDecodeVertexData(xr.getXmlData("jvxlVertexData", sdata, true, false), false);
759     // get triangles
760     String tData = xr.getXmlData("jvxlTriangleData", sdata, true, false);
761     String edgeData = xr.getXmlData("jvxlTriangleEdgeData", sdata, true, false);
762     // note: polygon color data is always between tags, not an attribute, and not compressed:
763     String polygonColorData = xr.getXmlData("jvxlPolygonColorData", sdata, false, false);
764     jvxlDecodeTriangleData(tData, edgeData, polygonColorData);
765     // get vertex value data or vertex color data:
766     String cData = xr.getXmlData("jvxlColorData", sdata, true, false);
767     jvxlColorEncodingRead = getEncoding(cData);
768     jvxlData.isJvxlPrecisionColor = jvxlColorEncodingRead.endsWith("2");
769     cData = getData(cData, "jvxlColorData");
770     jvxlColorDataRead = (jvxlColorEncodingRead.equals("none") ? cData : JvxlCoder.jvxlDecompressString(cData));
771     jvxlDataIsColorMapped = ((params.colorRgb == Integer.MIN_VALUE || params.colorRgb == Integer.MAX_VALUE) && jvxlColorDataRead.length() > 0);
772     // get contours
773     if (haveContourData)
774       jvxlDecodeContourData(jvxlData, xr.getXmlData("jvxlContourData", null, false, false));
775   }
776 
getData(String sdata, String name)777   private String getData(String sdata, String name) throws Exception {
778     String data = XmlReader.getXmlAttrib(sdata, "data");
779     if (data.length() == 0)
780       data = xr.getXmlData(name, sdata, false, false);
781     return data;
782   }
783 
getEncoding(String data)784   private static String getEncoding(String data) {
785     // original JVXL does not include "encoding"
786     if (XmlReader.getXmlAttrib(data, "len").length() > 0)
787       return "";
788     String s = XmlReader.getXmlAttrib(data, "encoding");
789     return (s.length() == 0 ? "none" : s);
790   }
791 
792   /**
793    * decode vertex data found within <jvxlVertexData> element as created by
794    * jvxlEncodeVertexData (see above)
795    *
796    * @param data
797    *        tag and contents
798    * @param asArray
799    *        or just addVertexCopy
800    * @return Point3f[] if desired
801    * @throws Exception
802    *
803    */
jvxlDecodeVertexData(String data, boolean asArray)804   public P3[] jvxlDecodeVertexData(String data, boolean asArray)
805       throws Exception {
806     int vertexCount = parseIntStr(XmlReader.getXmlAttrib(data, "count"));
807     if (!asArray)
808       Logger.info("Reading " + vertexCount + " vertices");
809     int ptCount = vertexCount * 3;
810     P3[] vertices = (asArray ? new P3[vertexCount] : null);
811     float fraction;
812     String vData = XmlReader.getXmlAttrib(data, "data");
813     String encoding = getEncoding(data);
814     if ("none".equals(encoding)) {
815       if (vData.length() == 0)
816         vData = xr.getXmlData("jvxlVertexData", data, false, false);
817       float[] fdata = PT.parseFloatArray(vData);
818       // first point is count -- ignored.
819       if (fdata[0] != vertexCount * 3)
820         Logger.info("JvxlXmlReader: vertexData count=" + ((int)fdata[0]) + "; expected " + (vertexCount * 3));
821       for (int i = 0, pt = 1; i < vertexCount; i++) {
822         P3 p = P3.new3(fdata[pt++], fdata[pt++], fdata[pt++]);
823         if (asArray)
824           vertices[i] = p;
825         else
826           addVertexCopy(p, 0, i, false);
827       }
828     } else {
829       P3 min = xr.getXmlPoint(data, "min");
830       P3 range = xr.getXmlPoint(data, "max");
831       range.sub(min);
832       int colorFractionBase = jvxlData.colorFractionBase;
833       int colorFractionRange = jvxlData.colorFractionRange;
834       String s = JvxlCoder.jvxlDecompressString(vData);
835       if (s.length() == 0)
836         s = xr.getXmlData("jvxlVertexData", data, false, false);
837       for (int i = 0, pt = -1; i < vertexCount; i++) {
838         P3 p = new P3();
839         fraction = JvxlCoder.jvxlFractionFromCharacter2(s.charAt(++pt), s
840             .charAt(pt + ptCount), colorFractionBase, colorFractionRange);
841         p.x = min.x + fraction * range.x;
842         fraction = JvxlCoder.jvxlFractionFromCharacter2(s.charAt(++pt), s
843             .charAt(pt + ptCount), colorFractionBase, colorFractionRange);
844         p.y = min.y + fraction * range.y;
845         fraction = JvxlCoder.jvxlFractionFromCharacter2(s.charAt(++pt), s
846             .charAt(pt + ptCount), colorFractionBase, colorFractionRange);
847         p.z = min.z + fraction * range.z;
848         if (asArray)
849           vertices[i] = p;
850         else
851           addVertexCopy(p, 0, i, false);
852       }
853     }
854     return vertices;
855   }
856 
857   /**
858    * decode triangle data found within <jvxlTriangleData> element as created
859    * with jvxlEncodeTriangleData (see above)
860    *
861    * @param tdata
862    *        tag and contents
863    * @param edgeData
864    * @param colorData
865    * @throws Exception
866    */
jvxlDecodeTriangleData(String tdata, String edgeData, String colorData)867   void jvxlDecodeTriangleData(String tdata, String edgeData, String colorData)
868       throws Exception {
869     int nTriangles = parseIntStr(XmlReader.getXmlAttrib(tdata, "count"));
870     if (nTriangles < 0)
871       return;
872     int[] nextc = new int[1];
873     int nColors = (colorData == null ? -1 : 1);
874     int color = 0;
875     Logger.info("Reading " + nTriangles + " triangles");
876     String encoding = getEncoding(tdata);
877     tdata = getData(tdata, "jvxlTriangleData");
878     String edata = getData(edgeData, "jvxlTriangleEdgeData");
879     int[] vertex = new int[3];
880     int[] nextp = new int[1];
881     int[] nexte = null;
882     int edgeMask = 7;
883     boolean haveEdgeInfo;
884     boolean haveEncoding = !"none".equals(encoding);
885     if (haveEncoding) {
886       tdata = JvxlCoder.jvxlDecompressString(tdata);
887       edata = JvxlCoder.jvxlDecompressString(edata).trim();
888       haveEdgeInfo = (edata.length() == nTriangles);
889     } else {
890       int n = PT.parseIntNext(tdata, nextp);
891       haveEdgeInfo = (edata.length() > 0);
892       if (haveEdgeInfo) {
893         nexte = new int[1];
894         PT.parseIntNext(edata, nexte); // throw away count
895       } else if (n > 0) {
896         Logger.info("JvxlXmlReader: jvxlTriangleEdgeData count=" + n
897             + "; expected " + nTriangles);
898       }
899     }
900     for (int i = 0, v = 0, p = 0, pt = -1; i < nTriangles;) {
901       if (haveEncoding) {
902         char ch = tdata.charAt(++pt);
903         int diff;
904         switch (ch) {
905         case '!':
906           diff = 0;
907           break;
908         case '+':
909         case '.':
910         case ' ':
911         case '\n':
912         case '\r':
913         case '\t':
914         case ',':
915           continue;
916         case '-':
917         case '0':
918         case '1':
919         case '2':
920         case '3':
921         case '4':
922         case '5':
923         case '6':
924         case '7':
925         case '8':
926         case '9':
927           nextp[0] = pt;
928           diff = PT.parseIntNext(tdata, nextp);
929           pt = nextp[0] - 1;
930           break;
931         default:
932           diff = ch - 92; // '\' character
933         }
934         v += diff;
935       } else {
936         v = PT.parseIntNext(tdata, nextp) - 1;
937       }
938       vertex[p] = v;
939       if (++p == 3) {
940         p = 0;
941         if (haveEdgeInfo) {
942           edgeMask = (nexte == null ? edata.charAt(i) - '0' : javajs.util.PT
943               .parseIntNext(edata, nexte));
944           if (edgeMask < 0 || edgeMask > 7)
945             edgeMask = 7;
946         }
947         if (--nColors == 0) {
948           nColors = (PT.parseIntNext(colorData, nextc));
949           int c = PT.parseIntNext(colorData, nextc);
950           if (c == Integer.MIN_VALUE)
951             nColors = 0;
952           else
953             color = c | 0xFF000000;
954         }
955         addTriangleCheck(vertex[0], vertex[1], vertex[2], edgeMask, color, false,
956             color);
957         i++;
958       }
959     }
960   }
961 
jvxlDecodeContourData(JvxlData jvxlData, String data)962   protected void jvxlDecodeContourData(JvxlData jvxlData, String data)
963       throws Exception {
964     Lst<Lst<Object>> vs = new  Lst<Lst<Object>>();
965     SB values = new SB();
966     SB colors = new SB();
967     int pt = -1;
968     jvxlData.vContours = null;
969     if (data == null)
970       return;
971     while ((pt = data.indexOf("<jvxlContour", pt + 1)) >= 0) {
972       Lst<Object> v = new  Lst<Object>();
973       String s = xr.getXmlData("jvxlContour", data.substring(pt), true, false);
974       float value = parseFloatStr(XmlReader.getXmlAttrib(s, "value"));
975       values.append(" ").appendF(value);
976       int color = getColor(XmlReader.getXmlAttrib(s, "color"));
977       short colix = C.getColix(color);
978       colors.append(" ").append(Escape.escapeColor(color));
979       String fData = JvxlCoder.jvxlDecompressString(XmlReader.getXmlAttrib(s,
980           "data"));
981       BS bs = JvxlCoder.jvxlDecodeBitSet(xr.getXmlData("jvxlContour", s,
982           false, false));
983       int n = bs.length();
984       IsosurfaceMesh.setContourVector(v, n, bs, value, colix, color,
985           SB.newS(fData));
986       vs.addLast(v);
987     }
988     int n = vs.size();
989     if (n > 0) {
990       jvxlData.vContours = AU.createArrayOfArrayList(n);
991       // 3D contour values and colors
992       jvxlData.contourColixes = params.contourColixes = new short[n];
993       jvxlData.contourValues = params.contoursDiscrete = new float[n];
994       for (int i = 0; i < n; i++) {
995         jvxlData.vContours[i] = vs.get(i);
996         jvxlData.contourValues[i] = ((Float) jvxlData.vContours[i].get(2))
997             .floatValue();
998         jvxlData.contourColixes[i] = ((short[]) jvxlData.vContours[i].get(3))[0];
999       }
1000       jvxlData.contourColors = C.getHexCodes(jvxlData.contourColixes);
1001       Logger.info("JVXL read: " + n + " discrete contours");
1002       Logger.info("JVXL read: contour values: " + values);
1003       Logger.info("JVXL read: contour colors: " + colors);
1004     }
1005   }
1006 
1007   @Override
postProcessVertices()1008   protected void postProcessVertices() {
1009     BS bsInvalid = params.bsExcluded[1];
1010     if (bsInvalid != null) {
1011       if (meshDataServer != null)
1012         meshDataServer.fillMeshData(meshData, MeshData.MODE_GET_VERTICES, null);
1013       meshData.invalidateVertices(bsInvalid);
1014       if (meshDataServer != null) {
1015         meshDataServer.fillMeshData(meshData, MeshData.MODE_PUT_VERTICES, null);
1016         meshData = new MeshData();
1017       }
1018       updateTriangles();
1019     }
1020   }
1021 
1022 }
1023