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.data;
25 
26 
27 
28 import java.util.Map;
29 
30 import javajs.J2SIgnoreImport;
31 import javajs.util.Lst;
32 import javajs.util.P3;
33 import javajs.util.PT;
34 import javajs.util.SB;
35 import javajs.util.T3;
36 import javajs.util.XmlUtil;
37 
38 import org.jmol.api.Interface;
39 import javajs.util.BS;
40 import org.jmol.util.BSUtil;
41 import org.jmol.util.C;
42 import org.jmol.util.Escape;
43 import org.jmol.util.Logger;
44 import org.jmol.viewer.Viewer;
45 
46 
47 @J2SIgnoreImport({ javajs.util.XmlUtil.class })
48 public class JvxlCoder {
49 
50   //TODO -- need to escapeXml for text data
51 
52   final public static String JVXL_VERSION1 = "2.0";
53   final public static String JVXL_VERSION_XML = "2.3";
54 
55 
56   // 1.4 adds -nContours to indicate contourFromZero for MEP data mapped onto planes
57   // 2.0 adds vertex/triangle compression when no grid is present
58   // Jmol 11.7.25 -- recoded so that we do not create voxelData[nx][ny][nz] and instead
59   //                 simply create a BitSet of length nx * ny * nz. This saves memory hugely.
60   // 2.1 adds JvxlXmlReader
61   // 2.2 adds color density Jmol 12.0.15/12.1.13
62   // 2.3 adds discrete colors for vertex-only data (encoding="none")
63 
64   /**
65    * @j2sIgnore
66    *
67    * @param volumeData
68    * @param jvxlData
69    * @param title
70    * @return XML string
71    */
jvxlGetFile(VolumeData volumeData, JvxlData jvxlData, String[] title)72   public static String jvxlGetFile(VolumeData volumeData, JvxlData jvxlData,
73                                    String[] title) {
74     // for the simple writer
75     int[] counts = volumeData.getVoxelCounts();
76     jvxlData.nPointsX = counts[0];
77     jvxlData.nPointsY = counts[1];
78     jvxlData.nPointsZ = counts[2];
79     jvxlData.jvxlVolumeDataXml = volumeData.setVolumetricXml();
80     return jvxlGetFile(jvxlData, null, title, null, true, 1, null, null);
81   }
82 
83   private static boolean haveXMLUtil;
84 
85   /**
86    *
87    * @param jvxlData
88    * @param meshData
89    * @param title
90    * @param msg
91    * @param includeHeader
92    * @param nSurfaces
93    * @param state
94    * @param comment
95    * @return JVXL file XML
96    */
jvxlGetFile(JvxlData jvxlData, MeshData meshData, String[] title, String msg, boolean includeHeader, int nSurfaces, String state, String comment)97   public static String jvxlGetFile(JvxlData jvxlData, MeshData meshData,
98                                    String[] title, String msg,
99                                    boolean includeHeader, int nSurfaces,
100                                    String state, String comment) {
101     checkHaveXMLUtil();
102     SB data = new SB();
103     if ("TRAILERONLY".equals(msg)) {
104       XmlUtil.closeTag(data, "jvxlSurfaceSet");
105       XmlUtil.closeTag(data, "jvxl");
106       return data.toString();
107     }
108 
109     boolean vertexDataOnly = (meshData != null);
110     boolean isHeaderOnly = ("HEADERONLY".equals(msg));
111     if (includeHeader) {
112       XmlUtil.openDocument(data);
113       XmlUtil.openTagAttr(data, "jvxl", new String[] {
114           "version", JVXL_VERSION_XML,
115           "jmolVersion", jvxlData.version,
116           "xmlns", "http://jmol.org/jvxl_schema",
117           "xmlns:cml", "http://www.xml-cml.org/schema" });
118       XmlUtil.appendCdata(data, "jvxlFileTitle", null, jvxlData.jvxlFileTitle == null ? "\n" : "\n" + jvxlData.jvxlFileTitle);
119       if (jvxlData.moleculeXml != null)
120         data.append(jvxlData.moleculeXml);
121       String volumeDataXml = (vertexDataOnly ? null : jvxlData.jvxlVolumeDataXml);
122       if (volumeDataXml == null)
123         volumeDataXml = (new VolumeData()).setVolumetricXml();
124       data.append(volumeDataXml);
125       XmlUtil.openTagAttr(data,"jvxlSurfaceSet",
126           new String[] { "count", "" + (nSurfaces > 0 ? nSurfaces : 1) });
127       if (isHeaderOnly)
128         return data.toString();
129     }
130     SB sb;
131     String type = (vertexDataOnly ? "pmesh"
132         : jvxlData.jvxlPlane == null ? "isosurface" : "plane");
133     // TODO: contours mentioned here? when discrete?
134     if (jvxlData.jvxlColorData != null && jvxlData.jvxlColorData.length() > 0)
135       type = "mapped " + type;
136     XmlUtil.openTagAttr(data, "jvxlSurface", new String[] { "type", type });
137     data.append(jvxlGetInfoData(jvxlData, vertexDataOnly));
138     jvxlAppendCommandState(data, comment, state);
139     if (title != null || msg != null && msg.length() > 0) {
140       sb = new SB();
141       if (msg != null && msg.length() > 0)
142         sb.append(msg).append("\n");
143       if (title != null)
144         for (int i = 0; i < title.length; i++)
145           sb.append(title[i]).appendC('\n');
146       XmlUtil.appendCdata(data, "jvxlSurfaceTitle", null, sb.toString());
147     }
148     sb = new SB();
149 
150     XmlUtil.openTagAttr(sb, "jvxlSurfaceData", (vertexDataOnly || jvxlData.jvxlPlane == null ? null :
151       jvxlData.mapLattice == null ? new String[] { "plane", Escape.eP4(jvxlData.jvxlPlane) }
152       :  new String[] { "plane", Escape.eP4(jvxlData.jvxlPlane),  "maplattice", Escape.eP(jvxlData.mapLattice)  }));
153     if (vertexDataOnly) {
154       appendXmlVertexOnlyData(sb, jvxlData, meshData, true);
155     } else if (jvxlData.jvxlPlane == null) {
156       if (jvxlData.jvxlEdgeData == null)
157         return "";
158       appendXmlEdgeData(sb, jvxlData);
159       appendXmlColorData(sb, jvxlData.jvxlColorData,
160           true, jvxlData.isJvxlPrecisionColor, jvxlData.valueMappedToRed,
161           jvxlData.valueMappedToBlue);
162     } else {
163       appendXmlColorData(sb, jvxlData.jvxlColorData,
164           true, jvxlData.isJvxlPrecisionColor, jvxlData.valueMappedToRed,
165           jvxlData.valueMappedToBlue);
166     }
167     appendEncodedBitSetTag(sb, "jvxlInvalidatedVertexData", jvxlData.jvxlExcluded[1], -1, null);
168     if (jvxlData.excludedVertexCount > 0) {
169       appendEncodedBitSetTag(sb, "jvxlExcludedVertexData", jvxlData.jvxlExcluded[0], jvxlData.excludedVertexCount, null);
170       appendEncodedBitSetTag(sb, "jvxlExcludedPlaneData", jvxlData.jvxlExcluded[2], -1, null);
171     }
172     appendEncodedBitSetTag(sb, "jvxlExcludedTriangleData", jvxlData.jvxlExcluded[3], jvxlData.excludedTriangleCount, null);
173     XmlUtil.closeTag(sb, "jvxlSurfaceData");
174     int len = sb.length();
175     data.appendSB(sb);
176     if (jvxlData.vContours != null && jvxlData.vContours.length > 0) {
177       jvxlEncodeContourData(jvxlData.vContours, data);
178     }
179     if (jvxlData.vertexColorMap != null) {
180       if (jvxlData.baseColor == null)
181         XmlUtil.openTag(data, "jvxlVertexColorData");
182       else
183         XmlUtil.openTagAttr(data, "jvxlVertexColorData", new String[] {"baseColor", jvxlData.baseColor});
184       for (Map.Entry<String, BS> entry : jvxlData.vertexColorMap.entrySet())
185         appendEncodedBitSetTag(data, "jvxlColorMap", entry.getValue(), -1, new Object[] { "color", entry.getKey() });
186       jvxlData.vertexColorMap = null;
187       XmlUtil.closeTag(data, "jvxlVertexColorData");
188     }
189     XmlUtil.closeTag(data, "jvxlSurface");
190     if (includeHeader) {
191       XmlUtil.closeTag(data, "jvxlSurfaceSet");
192       XmlUtil.closeTag(data, "jvxl");
193     }
194     return jvxlSetCompressionRatio(data, jvxlData, len);
195   }
196 
checkHaveXMLUtil()197   private static void checkHaveXMLUtil() {
198     if (!haveXMLUtil) {
199       // creating an instance prevents pre-loading by JavaScript
200       if (Viewer.isJS)
201         Interface.getInterface("javajs.util.XmlUtil", null, "show");
202       haveXMLUtil = true;
203     }
204   }
205 
appendEncodedBitSetTag(SB sb, String name, BS bs, int count, Object[] attribs)206   private static void appendEncodedBitSetTag(SB sb, String name, BS bs, int count, Object[] attribs) {
207     if (count < 0)
208       count = BSUtil.cardinalityOf(bs);
209     if (count == 0)
210       return;
211     SB sb1 = new SB();
212     sb1.append("\n ");
213     jvxlEncodeBitSetBuffer(bs, -1, sb1);
214     XmlUtil.appendTagObj(sb, name, new Object[] {
215         attribs,
216         "bsEncoding", "base90+35",
217         "count", "" + count,
218         "len", "" + bs.length() },
219         jvxlCompressString(sb1.toString(), true));
220   }
221 
jvxlSetCompressionRatio(SB data, JvxlData jvxlData, int len)222   private static String jvxlSetCompressionRatio(SB data,
223                                                 JvxlData jvxlData, int len) {
224     String s = data.toString();
225     int r = (int) (jvxlData.nBytes > 0 ? ((float) jvxlData.nBytes) / len
226         : ((float) (jvxlData.nPointsX
227           * jvxlData.nPointsY * jvxlData.nPointsZ * 13)) / len);
228     return PT.rep(s, "\"not calculated\"", (r > 0 ? "\"" + r +":1\"": "\"?\""));
229   }
230 
appendXmlEdgeData(SB sb, JvxlData jvxlData)231   private static void appendXmlEdgeData(SB sb, JvxlData jvxlData) {
232     XmlUtil.appendTagObj(sb, "jvxlEdgeData", new String[] {
233         "count", "" + (jvxlData.jvxlEdgeData.length() - 1),
234         "encoding", "base90f1",
235         "bsEncoding", "base90+35c",
236         "isXLowToHigh", "" + jvxlData.isXLowToHigh,
237         "data", jvxlCompressString(jvxlData.jvxlEdgeData, true) }, "\n"
238         + jvxlCompressString(jvxlData.jvxlSurfaceData, true));
239   }
240 
jvxlAppendCommandState(SB data, String cmd, String state)241   private static void jvxlAppendCommandState(SB data, String cmd,
242                                              String state) {
243     if (cmd != null)
244       XmlUtil.appendCdata(data, "jvxlIsosurfaceCommand", null,
245           "\n" + (cmd.indexOf("#") < 0 ? cmd : cmd.substring(0, cmd.indexOf("#"))) + "\n");
246     if (state != null) {
247       if (state.indexOf("** XML ** ") >=0) {
248         state = PT.split(state, "** XML **")[1].trim();
249         XmlUtil.appendTag(data, "jvxlIsosurfaceState",  "\n" + state + "\n");
250       } else {
251         XmlUtil.appendCdata(data, "jvxlIsosurfaceState", null, "\n" + state);
252       }
253     }
254   }
255 
appendXmlColorData(SB sb, String data, boolean isEncoded, boolean isPrecisionColor, float value1, float value2)256   private static void appendXmlColorData(SB sb,
257                                          String data,
258                                          boolean isEncoded,
259                                          boolean isPrecisionColor,
260                                          float value1,
261                                          float value2) {
262     int n;
263     if (data == null || (n = data.length() - 1) < 0)
264       return;
265     if (isPrecisionColor)
266       n /= 2;
267     XmlUtil.appendTagObj(sb, "jvxlColorData", new String[] {
268         "count", "" + n,
269         "encoding", (isEncoded ? "base90f" + (isPrecisionColor ? "2" : "1") : "none"),
270         "min", "" + value1,
271         "max", "" + value2,
272         "data", jvxlCompressString(data, true) }, null);
273   }
274 
275 
jvxlGetInfo(JvxlData jvxlData)276   public static String jvxlGetInfo(JvxlData jvxlData) {
277     return jvxlGetInfoData(jvxlData, jvxlData.vertexDataOnly);
278   }
279 
jvxlGetInfoData(JvxlData jvxlData, boolean vertexDataOnly)280   public static String jvxlGetInfoData(JvxlData jvxlData, boolean vertexDataOnly) {
281     if (jvxlData.jvxlSurfaceData == null)
282       return "";
283     checkHaveXMLUtil();
284     Lst<String[]> attribs = new  Lst<String[]>();
285 
286     int nSurfaceInts = jvxlData.nSurfaceInts;// jvxlData.jvxlSurfaceData.length();
287     int bytesUncompressedEdgeData = (vertexDataOnly ? 0
288         : jvxlData.jvxlEdgeData.length() - 1);
289     int nColorData = (jvxlData.jvxlColorData == null ? -1 : (jvxlData.jvxlColorData.length() - 1));
290     addAttrib(attribs, "\n  isModelConnected", "" + jvxlData.isModelConnected);
291     if (!vertexDataOnly) {
292       // informational only:
293       addAttrib(attribs, "\n  cutoff", "" + jvxlData.cutoff);
294       addAttrib(attribs, "\n  isCutoffAbsolute", "" + jvxlData.isCutoffAbsolute);
295       addAttrib(attribs, "\n  pointsPerAngstrom", "" + jvxlData.pointsPerAngstrom);
296       int n = jvxlData.jvxlSurfaceData.length()
297           + bytesUncompressedEdgeData + nColorData + 1;
298       if (n > 0)
299         addAttrib(attribs, "\n  nBytesData", "" + n);
300 
301       //TODO: these should only be for information purposes, but are not:
302       addAttrib(attribs, "\n  isXLowToHigh", "" + jvxlData.isXLowToHigh);
303       if (jvxlData.jvxlPlane == null) {
304         addAttrib(attribs, "\n  nSurfaceInts", "" + nSurfaceInts);
305         addAttrib(attribs, "\n  nBytesUncompressedEdgeData", "" + bytesUncompressedEdgeData);
306       }
307       if (nColorData > 0)
308         addAttrib(attribs, "\n  nBytesUncompressedColorData", "" + nColorData); // TODO: later?
309     }
310     jvxlData.excludedVertexCount = BSUtil.cardinalityOf(jvxlData.jvxlExcluded[0]);
311     jvxlData.excludedTriangleCount = BSUtil.cardinalityOf(jvxlData.jvxlExcluded[3]);
312     if (jvxlData.excludedVertexCount > 0)
313       addAttrib(attribs, "\n  nExcludedVertexes", "" + jvxlData.excludedVertexCount);
314     if (jvxlData.excludedTriangleCount > 0)
315       addAttrib(attribs, "\n  nExcludedTriangles", "" + jvxlData.excludedTriangleCount);
316     int n = BSUtil.cardinalityOf(jvxlData.jvxlExcluded[1]);
317     if (n > 0)
318       addAttrib(attribs, "\n  nInvalidatedVertexes", "" + n);
319     if (jvxlData.slabInfo != null)
320       addAttrib(attribs, "\n  slabInfo", jvxlData.slabInfo);
321     //next is for information only -- will be superceded by "encoding" attribute of jvxlColorData
322     if (jvxlData.isJvxlPrecisionColor)
323       addAttrib(attribs, "\n  precisionColor", "true");
324     if (jvxlData.colorDensity)
325       addAttrib(attribs, "\n  colorDensity", "true");
326     if (!Float.isNaN(jvxlData.pointSize))
327       addAttrib(attribs, "\n  pointSize", "" + jvxlData.pointSize);
328     else if (jvxlData.diameter != 0)
329       addAttrib(attribs, "\n  diameter", "" + jvxlData.diameter);
330     if (!jvxlData.allowVolumeRender)
331       addAttrib(attribs, "\n  allowVolumeRender", "false");
332     if (jvxlData.jvxlPlane == null || vertexDataOnly) {
333       if (jvxlData.fixedLattice != null && !vertexDataOnly)
334         addAttrib(attribs, "\n  fixedLattice", "" + jvxlData.fixedLattice);
335       if (jvxlData.isContoured) {
336         addAttrib(attribs, "\n  contoured", "true");
337         addAttrib(attribs, "\n  colorMapped", "true");
338       } else if (jvxlData.isBicolorMap) {
339         addAttrib(attribs, "\n  bicolorMap", "true");
340         addAttrib(attribs, "\n  colorNegative", C.getHexCode(jvxlData.minColorIndex));
341         addAttrib(attribs, "\n  colorPositive", C.getHexCode(jvxlData.maxColorIndex));
342       } else if (nColorData > 0) {
343         addAttrib(attribs, "\n  colorMapped", "true");
344       }
345       if (jvxlData.vContours != null && jvxlData.vContours.length > 0)
346         addAttrib(attribs, "\n  nContourData", "" + jvxlData.vContours.length);
347     } else {
348       if (jvxlData.mapLattice != null)
349         addAttrib(attribs, "\n  mapLattice", "" + jvxlData.mapLattice);
350       if (jvxlData.scale3d != 0)
351         addAttrib(attribs, "\n  scale3d", "" + jvxlData.scale3d);
352       if (nColorData > 0)
353         addAttrib(attribs, "\n  colorMapped", "true");
354       addAttrib(attribs, "\n  plane", Escape.eP4(jvxlData.jvxlPlane));
355     }
356     if (jvxlData.color != null && jvxlData.color.indexOf("null") < 0)
357       addAttrib(attribs, "\n  color", jvxlData.color);
358     addAttrib(attribs, "\n  translucency", "" + jvxlData.translucency);
359     if (jvxlData.meshColor != null)
360       addAttrib(attribs, "\n  meshColor", jvxlData.meshColor);
361     if (jvxlData.colorScheme != null)
362       addAttrib(attribs, "\n  colorScheme", jvxlData.colorScheme);
363     if (jvxlData.rendering != null)
364       addAttrib(attribs, "\n  rendering", jvxlData.rendering);
365     if (jvxlData.thisSet != null) {
366       String s = subsetString(jvxlData.thisSet);
367       if (s.startsWith("["))
368         addAttrib(attribs, "\n  subset", s);
369       else
370         addAttrib(attribs, "\n  set", s);
371     }
372     if (jvxlData.slabValue != Integer.MIN_VALUE)
373       addAttrib(attribs, "\n  slabValue", "" + jvxlData.slabValue);
374     if (jvxlData.isSlabbable)
375       addAttrib(attribs, "\n  slabbable", "true");
376     if (jvxlData.nVertexColors > 0)
377       addAttrib(attribs, "\n  nVertexColors", "" + jvxlData.nVertexColors);
378 
379     float min = (jvxlData.mappedDataMin == Float.MAX_VALUE ? 0f
380         : jvxlData.mappedDataMin);
381     float blue = (jvxlData.isColorReversed ? jvxlData.valueMappedToRed : jvxlData.valueMappedToBlue);
382     float red = (jvxlData.isColorReversed ? jvxlData.valueMappedToBlue : jvxlData.valueMappedToRed);
383 
384     if (jvxlData.jvxlColorData != null && jvxlData.jvxlColorData.length() > 0 && !jvxlData.isBicolorMap) {
385       addAttrib(attribs, "\n  dataMinimum", "" + min);
386       addAttrib(attribs, "\n  dataMaximum", "" + jvxlData.mappedDataMax);
387       addAttrib(attribs, "\n  valueMappedToRed", "" + red);
388       addAttrib(attribs, "\n  valueMappedToBlue", "" + blue);
389     }
390     if (jvxlData.isContoured) {
391       if (jvxlData.contourValues == null || jvxlData.contourColixes == null) {
392         if (jvxlData.vContours == null)
393           addAttrib(attribs, "\n  nContours", "" + Math.abs(jvxlData.nContours));
394       } else {
395         if (jvxlData.jvxlPlane != null)
396           addAttrib(attribs, "\n  contoured", "true");
397         addAttrib(attribs, "\n  nContours", "" + jvxlData.contourValues.length);
398         addAttrib(attribs, "\n  contourValues", Escape.eAF(jvxlData.contourValuesUsed == null ? jvxlData.contourValues : jvxlData.contourValuesUsed));
399         addAttrib(attribs, "\n  contourColors", jvxlData.contourColors);
400       }
401       if (jvxlData.thisContour > 0)
402         addAttrib(attribs, "\n  thisContour", "" + jvxlData.thisContour);
403     }
404     //TODO: confusing flag insideOut:
405     if (jvxlData.insideOut)
406       addAttrib(attribs, "\n  insideOut", "true");
407 
408     // rest is information only:
409     if (jvxlData.vertexDataOnly)
410       addAttrib(attribs, "\n  note", "vertex/face data only");
411     else if (jvxlData.isXLowToHigh)
412       addAttrib(attribs, "\n  note", "progressive JVXL+ -- X values read from low(0) to high("
413               + (jvxlData.nPointsX - 1) + ")");
414     addAttrib(attribs, "\n  xyzMin", Escape.eP(jvxlData.boundingBox[0]));
415     addAttrib(attribs, "\n  xyzMax", Escape.eP(jvxlData.boundingBox[1]));
416     addAttrib(attribs, "\n  approximateCompressionRatio", "not calculated");
417     addAttrib(attribs, "\n  jmolVersion", jvxlData.version);
418 
419     SB info = new SB();
420     XmlUtil.openTagAttr(info, "jvxlSurfaceInfo", attribs.toArray(new Object[attribs.size()]));
421     XmlUtil.closeTag(info, "jvxlSurfaceInfo");
422     return info.toString();
423   }
424 
subsetString(BS bs)425   private static String subsetString(BS bs) {
426     int n = bs.cardinality();
427     if (n > 1) {
428       String a = "[ ";
429       for (int ia = bs.nextSetBit(0); ia >= 0; ia = bs.nextSetBit(ia))
430         a += (++ia) + " ";
431       return a + "]";
432     }
433     return "" + (bs.nextSetBit(0) + 1);
434   }
435 
addAttrib(Lst<String[]> attribs, String name, String value)436   private static void addAttrib(Lst<String[]> attribs, String name, String value) {
437     attribs.addLast(new String[] { name, value });
438   }
439 
440   public static final int CONTOUR_NPOLYGONS = 0;
441   public static final int CONTOUR_BITSET = 1;
442   public static final int CONTOUR_VALUE = 2;
443   public static final int CONTOUR_COLIX = 3;
444   public static final int CONTOUR_COLOR = 4;
445   public static final int CONTOUR_FDATA = 5;
446   public static final int CONTOUR_POINTS = 6; // must be last
447 
448   /**
449    * contour data are appended to a string buffer in the form of a
450    * <jmolContourData count="[nContours]">
451    *   <jmolContour index="0" value="-0.033" color="[xff0000]" encoding="base90iff1" data="fractional data">triangle bitset data</jmolContour>
452    *   <jmolContour index="1" value=" 0.000" color="[xffff00]" encoding="base90iff1" data="fractional data">triangle bitset data</jmolContour>
453    *   <jmolContour index="2" value=" 0.033" color="[x00ffff]" encoding="base90iff1" data="fractional data">triangle bitset data</jmolContour>
454    *   ...
455    * </jmolContourData>
456    *
457    * One presumes an ordered set of triangles.
458    * The contour intersects these triangles along two edges or at two vertices.
459    * (see IsosurfaceMesh for details)
460    * Each contour is a Vector containing:
461    *   0 Integer number of polygons (length of BitSet)
462    *   1 BitSet of critical triangles
463    *   2 Float value
464    *   3 int[] [colorArgb]
465    *   4 StringXBuilder containing encoded data for each segment:
466    *     char type ('3', '6', '5') indicating which two edges
467    *       of the triangle are connected:
468    *         '3' 0x011 AB-BC
469    *         '5' 0x101 AB-CA
470    *         '6' 0x110 BC-CA
471    *     char fraction along first edge (jvxlFractionToCharacter)
472    *     char fraction along second edge (jvxlFractionToCharacter)
473    *   5- stream of pairs of points for rendering (created
474    *
475    *
476    * @param contours
477    * @param sb
478    */
jvxlEncodeContourData(Lst<Object>[] contours, SB sb)479   private static void jvxlEncodeContourData(Lst<Object>[] contours, SB sb) {
480     XmlUtil.openTagAttr(sb, "jvxlContourData", new String[] { "count", "" + contours.length });
481     for (int i = 0; i < contours.length; i++) {
482       if (contours[i].size() < CONTOUR_POINTS) {
483         continue;
484       }
485       int nPolygons = ((Integer) contours[i].get(CONTOUR_NPOLYGONS)).intValue();
486       SB sb1 = new SB();
487       sb1.append("\n");
488       BS bs = (BS) contours[i].get(CONTOUR_BITSET);
489       jvxlEncodeBitSetBuffer(bs, nPolygons, sb1);
490       XmlUtil.appendTagObj(sb, "jvxlContour", new String[] {
491           "index", "" + i,
492           "value", "" + contours[i].get(CONTOUR_VALUE),
493           "color", Escape.escapeColor(((int[]) contours[i]
494               .get(CONTOUR_COLOR))[0]),
495           "count", "" + bs.length(),
496           "encoding", "base90iff1",
497           "bsEncoding", "base90+35c",
498           "data", jvxlCompressString(contours[i].get(CONTOUR_FDATA).toString(), true) },
499           jvxlCompressString(sb1.toString(), true));
500     }
501     XmlUtil.closeTag(sb, "jvxlContourData");
502   }
503 
504   /**
505    * Interpret fractional data in terms of actual vertex positions and
506    * create the elements of a Vector in Vector[] vContours starting at
507    * the CONTOUR_POINTS position.
508    *
509    * @param v
510    * @param polygonIndexes
511    * @param vertices
512    */
set3dContourVector(Lst<Object> v, int[][] polygonIndexes, T3[] vertices)513   public static void set3dContourVector(Lst<Object> v, int[][] polygonIndexes, T3[] vertices) {
514     // we must add points only after the MarchingCubes process has completed.
515     if (v.size() < CONTOUR_POINTS)
516       return;
517     SB fData = (SB) v.get(CONTOUR_FDATA);
518     BS bs = (BS) v.get(CONTOUR_BITSET);
519     //int nPolygons = ((Integer)v.get(CONTOUR_NPOLYGONS)).intValue();
520     int pt = 0;
521     int nBuf = fData.length();
522     int type = 0;
523     char c1 = ' ';
524     char c2 = ' ';
525     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
526       int[] vertexIndexes = polygonIndexes[i];
527       while (pt < nBuf && !PT.isDigit(c1 = fData.charAt(pt++))) {
528         // skip non-digit data
529       }
530       type = c1 - 48;
531       while (pt < nBuf && PT.isWhitespace(c1 = fData.charAt(pt++))) {
532         // skip whitespace
533       }
534       while (pt < nBuf && PT.isWhitespace(c2 = fData.charAt(pt++))) {
535         // skip whitespace
536       }
537       float f1 = jvxlFractionFromCharacter(c1, defaultEdgeFractionBase, defaultEdgeFractionRange, 0);
538       float f2 = jvxlFractionFromCharacter(c2, defaultEdgeFractionBase, defaultEdgeFractionRange, 0);
539       int i1, i2, i3, i4;
540       /*
541        *     char type ('3', '6', '5') indicating which two edges
542        *       of the triangle are connected:
543        *         '3' 0x011 AB-BC
544        *         '5' 0x101 AB-CA
545        *         '6' 0x110 BC-CA
546        */
547       if ((type & 1) == 0) { //BC-CA
548         i1 = vertexIndexes[1];
549         i2 = i3 = vertexIndexes[2];
550         i4 = vertexIndexes[0];
551       } else { //AB-BC or //AB-CA
552         i1 = vertexIndexes[0];
553         i2 = vertexIndexes[1];
554         if ((type & 2) != 0) {
555           i3 = i2;
556           i4 = vertexIndexes[2];
557         } else {
558           i3 = vertexIndexes[2];
559           i4 = i1;
560         }
561       }
562       v.addLast(getContourPoint(vertices, i1, i2, f1));
563       v.addLast(getContourPoint(vertices, i3, i4, f2));
564     }
565   }
566 
getContourPoint(T3[] vertices, int i, int j, float f)567   private static T3 getContourPoint(T3[] vertices, int i, int j, float f) {
568     P3 pt = new P3();
569     pt.sub2(vertices[j], vertices[i]);
570     pt.scaleAdd2(f, pt, vertices[i]);
571     return pt;
572   }
573 
574   /**
575    * appends an integer (3, 5, or 6) representing two sides of a triangle ABC --
576    * AB/BC(3), AB/CA(5), or BC/CA(6) -- along with two fractions along the edges
577    * for the intersection point base-90-encoded. This version is single precision.
578    *
579    * type     f1     f2
580    *  3       AB     BC
581    *  5       AB     CA
582    *  6       BC     CA
583    *
584    * @param type
585    * @param f1 -- character-encoded fraction
586    * @param f2 -- character-encoded fraction
587    * @param fData
588    */
appendContourTriangleIntersection(int type, float f1, float f2, SB fData)589   public static void appendContourTriangleIntersection(int type, float f1, float f2, SB fData) {
590     fData.appendI(type);
591     fData.appendC(jvxlFractionAsCharacter(f1));
592     fData.appendC(jvxlFractionAsCharacter(f2));
593   }
594 
595   /**
596    *
597    * @param jvxlData
598    * @param vertexValues
599    */
jvxlCreateColorData(JvxlData jvxlData, float[] vertexValues)600   public static void jvxlCreateColorData(JvxlData jvxlData, float[] vertexValues) {
601     if (vertexValues == null) {
602       jvxlData.jvxlColorData = "";
603       return;
604     }
605     boolean writePrecisionColor = jvxlData.isJvxlPrecisionColor;
606     boolean doTruncate = jvxlData.isTruncated;
607     int colorFractionBase = jvxlData.colorFractionBase;
608     int colorFractionRange = jvxlData.colorFractionRange;
609     float valueBlue = jvxlData.valueMappedToBlue;
610     float valueRed = jvxlData.valueMappedToRed;
611     int vertexCount = (jvxlData.saveVertexCount > 0 ? jvxlData.saveVertexCount
612         : jvxlData.vertexCount);
613     if(vertexCount > vertexValues.length)
614       System.out.println("JVXLCODER ERROR");
615     float min = jvxlData.mappedDataMin;
616     float max = jvxlData.mappedDataMax;
617     SB list1 = new SB();
618     SB list2 = new SB();
619     if (vertexValues.length < vertexCount)
620       System.out.println("JVXLCOLOR OHOHO");
621     for (int i = 0; i < vertexCount; i++) {
622       float value = vertexValues[i];
623       if (Float.isNaN(value))
624         value = min;
625       if (doTruncate)
626         value = (value > 0 ? 0.999f : -0.999f);
627       if (writePrecisionColor)
628         jvxlAppendCharacter2(value, min, max, colorFractionBase,
629             colorFractionRange, list1, list2);
630       else
631         list1.appendC(jvxlValueAsCharacter(value, valueRed, valueBlue,
632             colorFractionBase, colorFractionRange));
633     }
634     jvxlData.jvxlColorData = list1.appendSB(list2).appendC('\n').toString();
635   }
636 
637   /* ******************************************************************
638    *
639    * JVXL 2.0 encoding of vertices, triangles, and vertex values:
640    *
641    * <jvxlSurfaceData>
642    *   <jvxlTriangleData data="!]][[_]]Y`WbVa^]]] ... cZ_T^ZdUdTc!^[Dv][Bx-43,+44,]-55,f+43,_W`Z^X`Z ...">
643    *   </jvxlTriangleData>
644    *   <jvxlVertexData min="(15.218472, -28.304049, 34.71112)" max="(97.8228, 54.011948, 109.95208)" data=0HY0HZ0HZ0HY0H[0GZ0IZ0IZ0H[0FZ...">
645    *   </jvxlVertexData>
646    *   <jvxlColorData type="range|discrete" data="015.86++1@<?<D~4 CD2BDDCD*D?BCB?~6 @@.=??CAAC@?~4 A@A?CCD@?@?ABA?>B=<=~4 <====???>>>???,0<<<0/5;:;=><;=<<;,8:;:;=><BAA=?<;+,)*0+/<=<<<<==<~7 =<=<=<====<??<<>>>?=>>??>~5 ?>=>>>===<<<<;<<;;<<<=<<<===><====<==<=~4 <=<9::<<;;;::;;:;;<:;>~4 ;;==<=;=~4 <====>??>==>>>==<=<==<==>=~4 >>>>=>>==?==><~7 ;;;<;<<<;;;;/..0/<;316268<<<;;22:~18 ;:;:<<<=<~9 ;;<;~4 <~6 =<;;<;~4 <<<;<<;<<<;~5 <~16 ;;;;<;:;;:~4 ;:;;;<;;;:;<<<;::;;:;:99;;;:?@>@<<<;==<;::<<;=;<>;:<==>;<;@@>===<<;=AAAA;=~4 ;:=<::><::9::::9:;;;;::::;~4 :;;:;~7 <<;;;<:;::;~5 <;<;~5 ::;9~7 :999;:::;:9~4 ;:;~6 :;;:~4 ;;;:::9;~4 <;<<::999:999:~7 ;;;:::;:<<<:<;<::<;<:::;:~5 9:::99:999::999<<<=<===>~4 ?@?A@A?BABA===>>>==<A>><><>???B>>>BBB?~6 >>BBBB>@AA>~4 ?AB~4 <~5 9~16 :~4 9:~5 9:::9~7 8999:::9:::999:99::9::9~11 ;::::;;99:9::99::9999:9~6 :~5 ;;;988889~5 8889889998:989989~4 8~5 9==>==;<;=>;<====<;>>>=<<<<;;?A?AB<~4 ;=<;ABB<;<~6 ==<=<~4 ;;;;9~7 8989~4 88898~4 989~11 8999989889:::998888::7788789999:8989888898988988988989~31 ;:;<:9888999:;;;;<~6 ;::<<;<:~4 9;;::8887777556878887666787867757775~6 6~4 8789889:99:998999:999899::99:9~23 8898~4 99998~4 989998989767788876776696668766667899777889989999898~4 987778~4 9998989~5 898~4 78668777788767555655688786668998666967888668898~11 66687778~8 78~16 7766686655667688786~14 7766778898899:9:7899995554454~5 5544655456655568~9 9998889899998~4 99888777887~4 8889998766999896859988986666558865886695~4 8888998~5 9887776787~4 878~4 788445444554436666776~6 76~4 7778867788778666886667663~10 433223334444338~14 998~6 99899988898~16 66545888776787865777883333445456343~10 8~10 98~4 989~7 88993~11 878~5 9867766676776~10 333343~7 443~9 76776776633357776554755888878~6 787788877676666765999988878877766668866557563335564554555656577657755756655665664554434564465544467778878~4 7777898798989998889~5 5~5 6~13 776445565756675446766565566446~5 7~5 56665556666565644765588775767686676878~5 78787777886667~5 6~8 78~4 7777887678887869996~7 76~5 7667~6 8~5 78~5 98778887787887~7 ::886828::88988:9::9:88:98888:::88777769768987978889:96777788:::;::;:87789~13 8999777766665~7 6666566555599888898998988:::7787:::99897:889787~4 88887778887998898999-)**,+)*)*()*(+++(***7878889788989998999898777677776777876~4 7665652231~5 4/24656657677<<;<<<<5665656555665~5 4456565~11 6665~4 6655656556665657~17 6655777677554~6 33;4444;<;;4555;;5<;<<;;449;;;<7~7 67776777767~5 6656677657<<<;~4 <<<=<;;<;<~6 =<;5~14 =~16 <===8889999899998~4 99997887877878~7 998888987888798989999;;;9:~6 ;:::989;;;988887788665589886577887778877767~4 6655565789899998787878788788998999:9:~4 988766676~10 565<<<<=~7 <===<<=<====>>=>=:;~4 <<;;;:<::78878777677767~4 8877888787787~5 8~7 7788878878887~5 8777787~4 66788776767667~15 877887988988778978876~6 565~4 66656656;<;;;<<:;:~4 ;;;;<:~5 9:99:9~6 7~10 8~6 7~6 887~10 88878~4 7878866678898~6 6787768668778887777888788878778~21 6~4 5676~4 56878878~8 99888999::9:::9~7 :4~8 54~7 54446458~7 9888898889~14 8998989898~6 9889~4 88999898779889889969899687869969867866556989~7 89888898889~8 8894556568~19 98989~9 4~5 333434~5 55455454445~5 88889988899898988989~7 8~5 99998~8 98~4 988878656787888645~4 675455546778~8 988998884~4 34~17 9~10 889899989~5 89~5 8999989:9::9~7 :9~8 787899989~5 88799989889~4 :9~10 :9:9~4 :9:5598898769~4 688899:566645477767774644;;;;:::;;;;9:::6~4 777::::6~12 ::;:::4456~4 447~5 66664557776669~7 :::9:9~5 :;;:;;:~16 99::9::98:7:999988778988::::9:::99::999:~8 ;777:::8989:~4 98:7:8:;:8;~7 :7778<;<<<;<;~7 <<:<<::;<;;;;<;;<<;~4 <;;<~4 78;;:;:;;9;;:;<;~6 :;:;;:;~11 :::;:;:;~8 :~7 ;;::::9998~4 :::899888:;;::978;:;9999:9;;:<<;::;::::;<:::9<<<:;<<;<;;<;;;;:;;;;:9;;:;;;::;~5 :;;::;~5 ::9;::9:~5 ;::::;;:::;;9:~5 ;:~4 ;~20 :;;::;~6 :::;:;;::;~16 :;;;9::::;:;;;;:;~4 :;;;;:~4 ;~4 :;~6 :~8 ;;;;8988998889:~4 99:9:~7 99:9997789~5 889898899879878899889:;;;:~7 ;:~15 889999:9:9:89779~5 8~4 ::9:::9::9::9:~19 9:999989~5 ::;9~5 ::9::;:9::99:998888;~6 88;;;:9:9:::98;;::9:;9:9999::;;:::;;:~5 ;:99:~4 ;<;;<<:;:<;;<;::::<<<<::<;:::<~4 =:::9<;;<<<;<<<;:;<:;<;~6 ::999989:9:99::9998898:9~17 8887778767668~4 98:~8 ;<~4 ;<<<;<;:;:;;:;;:::99::::98:~7 ;:~5 ;:9;;:;9::9;:;8:9:9::98988899::9888878:~12 9999:~8 7677787676669~24 54~8 5656559:9:9:9:9:::9::;99::9;;;::9999::::;:;~5 :;:::9;::9~8 897798~5 999886667766678::9999898~5 7~4 88:;::99:99:;:5566456654~8 5449~9 ::::;:::;~6 ::;;;888989~7 88899988989989~12 89~7 667767755888776487888855666675644545555664~4 5~5 45~5 ;::;;;;<<<<;<~5 ==<~4 99::;;<<:;;:77998989989898979~4 :~5 9:9:;;::;;;;:<;<66699796999:767668888978998454566655;;;:::;;;;99:9::;;9;;;;8899899998<<<<;;;=<=:;;<<5556555=~8 <=<<====<666656776~6 55664446688998~10 98::9~5 :9:::99:988::;;;;:98889986666788889988669688677789978~5 776778~15 9889~8 ::::88666766766:::;:;;:;65~5 665556~4 56~9 8~18 98988998998899998~8 9999889888988878~5 6666777888688877778~15 998989998889955566555:~5 9999::;;:;:~4 ;;;;<<;;5~4 666565~4 7~8 88877878~15 7~5 87888778~10 7~6 87787766777677788677665567766667755675557~9 87787777566665~8 9::;::::;888899888989878~10 78787878778887778~6 7~10 8~6 7~4 8887888777876667768787666555665~9 787877887787788787~6 875~4 4457888778887~4 878778~4 5~8 4554559~11 8988898888798898~4 9877788889988877789876786::9:~5 7::::9889~4 8:788766999855775~4 9~5 889998998887978889988878~14 756667676577877776677767756666778776785667~4 577675759~13 :~9 99:99::9:9~6 ::::99::999898998889988997998897899889987888879~5 8979899879889~5 8899988989899998988899889~7 78~5 778978~4 76~4 56~8 999897~6 877889:::99978879:99:9999899:99987998889889~5 ::99998:::99:99:~20 7~8 6768787777676776554554664677677766989::::8899::999::99888:::9:~15 9999:99989998~6 :898~4 97887878988898888::9:::99:~16 8~4 :::99998:99:778566786998666:::9:~5 9:99:::98897996678;;;:;::;;:::56545644333343~4 4534:9:9::999:~11 ;;;:;899898999;<;99:99<<<;;:;;<877;<::99:::;:~8 =~4 <<=>====:=~9 <~4 66766559:;<<6;=46789:3334333435633=>~5 ==><;=>~4 ==>>>?=:<;:<;=:;54~12 55455534~12 9:9:;;::9;:::;;<;;<~4 ;;;;>==>==<<:;><~8 :=<<<<===>~7 7~4 87884555764577658799::;9:;988999::9::::668987=><89;;<;::95555657779986887955<==<<89;<;::;97778~5 9?>>???>>?>=>=56556666>>?@A<???@?>???<==<>;~6 <<>>;;;<;;=;?>?>@<;<<;666656~12 566668888:99:999;;:98~4 ;<::9::999::=>=<;;;:8~6 777677878~6 77878776677776777788;:99:;:~6 9:99:8>>>=;<=?>;>~5 ;<;?>?>7~8 666?~9 >?~5 >;;;;<;;?<?<<><<;~4 ?>;;;??@????;;<;;<9~4 889998~6 997878987786:999:999::9998889898889998898:999:9::99::9::99:~5 9:::888:99:::9::88889998899989~7 :9:~4 9:9~7 ::::9:~5 ;;:;;:<;8~5 ::::>><><==?;;:;::::;;<<;7978878889~20 89~6 89999899:99:9:::999:9::99988998889~4 ::999888878888779~5 :99:9~4 ::::9:9998:9:~17 9:::9::;9::;~4 ::88989898~8 9:~4 9:9:~9 99:999::9::::9:;;;:;::;:::;::::;::<;<~4 ;<;<;;;9::99988;;::98<~4 ;;<~4 ::9;9<;;:::99<<<;<<<;;;:;:<<<<;:899;;<<<;:;::<<;;<;;;;::<:::<<;;<<;<;<;;<<;<<<;:99<;:;;;:9:;;;;<;;:;;;;9<9;==>==>~5 =<<=~7 9999===:::<<<;;;9::=<;~4 <::>==>??>?~8 >?>>>>=>?>>?>~6 ==>~6 =>88788878799888>>>>====>>>====>?>>==?:586649568;88967878~8 78889:7:6786777766577:898:=~9 <<=<;;<::::;:<<<<;:;:9;7979::984655796:97877<~4 ====::<<=<<;=<<<<;:::989897778887788867;;:;998:;=~5 ;<~4 ====<<===<~16 =<>~4 ?>>=>?>=<;;;:;<;<=~5 ><>~6 ==>>==>=;<<;:;<<:;=:::?~4 >~6 ?>????>~5 ?>~6 =>~8 =<=<<;=;=><<>~6 =<>><<=>?=<?~5 >>?>?>>>>A@@@?@???>?>@==@>>?@@?>>>>??=<=<<>>====>==><?<=?>>>?<;;<;<<;<<<==<=<==<=<<<=~5 <====<>=~5 <~4 ;<~5 ;;<;~5 :;;;<<=<~5 ;;<~12 ==;<>====>=>=>~8 ??=>>>=~7 ???>>==>==>==????>>>>?====>=>=>>>=~19 <<=<=<<<====<~8 =<==<<<<====>==>=>===<<==>=~4 <=~4 <<=<~4 =<~4 ;<;~7 ::;;:;;<;<;<~5 ===<>=>=><<<<==>>>=>==>~9 ==>====>>=>~5 =>=~14 <=~9 <=~22 >~11 <<<<==>=<<====>====>~7 <~4 ;<<=~4 >>==<<<<=~4 <~5 ==<~12 =<~4 =~4 <<==<=<~12 =~5 ><>===<=<~8 =;;;<<;<;<~16 ===<<<;~8 =~5 ;==;;;<=<<<<=~6 >>>=>===>>=<=<====>~6 ?>>><====<>=~10 <===<~9 =<<<==<=>~12 ?>~4 ===<~5 =~5 <====>>=>=>~4 ;~6 <;;<<<<;;;<<;;<<<DFEDEFFFFGFFE~8 FFE~5 FFFEEFE~6 DDD=~50 <~11 =<=<=~8 <;;;;<<;<;<;~4 <~5 ;;<;;;;<<<;;;==<=<<=<===<=<===<~36 ;<<<===<====<=>>=<<<;<<=>=>>>?>>>>:~7 ;::;:::;<::;::A?@AA@?>A?@@@?@>???@A@?@?C???>>=>=;;:?<<>9=<7:>>>====<===<~14 =DBAC<<B===<:;<<;:?<<?=>8:8<;;<=<9<=<<=<>==<;>=>@@@<<=<~9 ?<>??<?A@?<;>@B9<=?=@>:@B@<<<=<===<~6 ;;<;<;<<<<;9:999::;;<:9;;:=<<<<=>>==>>?<==<=>@>==<=<=<<<====@AA@B@@AAAB@AAAA@<~6 ??>??>?~4 ===<=<<===<~5 ==>>==<<===>>@=<====>?===>==@=>=@>@~4 ?>>?@?@@@?<;<<;;<<=<<<;;<;;=>><<<;<??@>==:<:;;=~4 <=<====>=>===>>>=>=~4 >=>??====??>>?>>===>89<===>~4 :899<>=9989;9;;:>>?A???ABBABB@???>?~11 @A?>?>???>??>~5 ???>~9 ??>?@@????>~7 =>@>~9 ??==>=>=~6 ???@~4 ?AAAA@@AAB@BB@A???====>~5 ?>=>>??@@====@A@???AA??@@@?~4 >???>~4 =>>>=>~4 =~5 <=<==>=<<==>==<<<<==>>???>???>>>>????@~4 ?@@@?@@@AABAB@AA@@?BB@~5 AA@@@?@???AA@~7 A@~6 A@@?@~5 A@?~4 @~6 ?@@@?@@@?@~5 >?~4 @~5 ??@?@??@??>>>===>====>=~12 >=~8 <=<<===>==>==<<>><<=>><>>>==>>>==>>>=>?==?>~8 ?~8 >@???@@=>===>===?@>@@?=>>>>??=>??>==<<=?<~5 ==>????@?>==>???>>>>???>?=>>>@@???@@?>>;9?>>>=:9<>>>>?>>>??>>?><>9::?>?=?=>>>:<<<><>?;7999:8=?<;<?>66;==<==?~5 >A@@A?>@@>@?@@?@@@@???>~4 ?<>>?@CA?@??@@??@@?@@@@?@@>?>>>=>>>>====<<==<<<;<<<;;<;;;<<<;<<;~4 <~5 ;;;::;:9::9~4 ;:>>====<===<<=<<<<?@>?>?>>?@>=?=~6 ?>>?>?===???@?>==?<=~4 <~7 :;:;999:99887789~4 8777899976669988<;<<<:::;;<9998898798768776~4 7:;:;:9888989889~6 8~5 9889888898899988898~8 787~4 66687776776665889:9~5 8999;;::;:~5 9999::;~7 :~4 ;:;:~7 ;<<::::<<<8~5 98~5 98?>?>~6 ====?===?>>>>=>::;;;:9:99:8<<==;:>96=<=~7 <<=~4 >>=>=~4 <==>>><=<~8 =<~8 ==<=<<<=<~4 ==<~4 ==>~5 =~5 <~5 =~15 >=>~5 =>====>==>>>====>~7 =~5 >>>=~4 >>>====;~4 <;;====:;;:;;==<=<<<<;<=<;<=~18 @@@?@?@@?@?<=<<=?===>==>>?=?>>=~4 >=>=>?<?=;<?<>=>==<==>>?>?>??>>>====>=>==>>=~6 >=~7 <<;;;>;;<=<<;=;;===<<=<=~7 >=<=;:7>@?@?<<;<<;<~10 =>==?==?>>?>>>???=99;998~10 :8998;::9;;98<;:=9=:<;8;664687787574325289:<66FFFGFGGGFGF~5 EFFFFEFEF~4 EFCCCEBDGEEFCC@DDBF=~5 <=<<<=<~7 =<~7 ;<<;~6 :;;::<~8 ;;;:9:899;<<:;:<;;;;<;<<<;;<<<;:<<:::;~4 =?>887;<=>;:99::::;:;~10 :;::;~4 79777878E~6 DEE8EEEED;=;9;<<;;<;;67;~5 <<6;<EDDDEDEDEEEED~7 ;;::;::;<;~4 <~7 ;<:;;:;~9 <;~48 <~7 ===<=<~12 =<<<<===;<====<==<<<=<~20 >~8 =>?>=><==>=><<<<=<<<=~4 <<=<>=~4 <~10 >>><<<<==>===><<==<=~5 <<<=<==<~7 =~9 >>>>=>~4 ==>>=>>>=<<===<~4 ===<=<==<=~11 <~5 ===>??>==>>=~10 >=>===<<<===<~6 =<=<<=<<<<>>>=~14 >=~9 <====<====>>?>~4 ?>>??>?>?>>=>>>>=>>>>==>~4 ?>====>>=>~24 ?>???>???>>?>~5 ?>=~4 >=<=<~8 =<=<=<====<<=<<<=<~4 ;=>=>==<==<<=<<<<===<;?=<>=<;;;<=<<==>>=>=>>>><C?C@C@=BABCD@?@@??@???@??@@@@??@???>===><>;<@@@??=>>?~4 =>????<>=??@@?><<<<==<=<==><;:;;<>=~5 >====^__^`_]]]^^]]_^__^^]]]^^^_^~4 babadffcgddfffcdeb^^^```????@?~10 >?~8 !!![!^_^][!]!^!![![[???>?~5 >@??=?=?~4 ><<<>>><;<=<=>>=>=<=>~4 ?>=>~6 kkjkkhhhg!^!_^Y^!Yhfd_`!]gbbeeY[![[!ZYZ!Z[[_!`a`^Y_dfZWWX[kkjjjkhjgjjdjjedkkkk_[a[aXXWWWX[cdicXVXVYYVXrrrsposlkkkkjkkkikilkmknlh_^^]lgmhb`eaoonqlYZ[XVYY[Z[Y[[[[Z]^][[]!^!]YYYXXXYXYYYWXXVWXWXZXSSVRSRRRSRSRRVSVZXV<~7 ;~7 <;<;;<<;<~4 ;<~14 99::9::<<;;<;;<:~6 <<<<;<;~4 <<:9:9:9:;;;:::98899<~22 ;;<;;<<<8887~4 877877;:9999:<<<<;<<<;<;>~5 ?>????9889????:=:>>899788<?>????>?;<<<<>>>>=<<==<<=>>?>>>>=<=<>>;<<;==<=;<====;==ECEC???BB;=<HIJ@EE88:9878:3443333654545444455432553:845545~4 :89955588798554544456<<B?89=56=?=<<<<:<<<<;<~4 ;<~6 ;~5 <;<4636.333387867767741354655623665564044547~6 8777865=>>>??>?=;:;;<<=<=;;;<;EGFHGHELLKKAGENK7679HDI86NONDC567::;::::;::<9:?~8 <GHIGGHIIHGGGHHIEIGH204D364=HIIJI3<H./KFGFFFIGGG--//0;:;:;;;;:::;:~4 ^_^a]cd`alllkleeflkihli;::99:aagg9f``b^]]]][^!::9999fffc]a_a_`zyyzrr{twwzy{|zzkklkklkjkikwqppvtli^^iWWVVX`Wj[[]Z[[[[Z[f`WXvxWWfbZYY![]]]!]!]![~5 Z[[[ZZ]^~9 _[[^^^__^^ZWYXXYYZXYWYWXXYX[:;;;:;;;:;;9:;~4 :;::;;:;;::::=>=:;::<==>;:;>=<=<=;====<>>==>>>??>=??>A~5 @AAAA@~4 AAAA@@A@AAAA@@AA@@A????>?>>>=>>>>?>>>A@@A@@??A===>>A@?@A@@?A?~4 @?>?@???@@??A~5 @@@A@?A@??@@@A~5 @A@@@@???>>>?~6 ;<<;<~5 ;<<>=<=<;>?@??@?@@?@?@@??@@??>??@???>~6 ?>?>>?=~4 <<===>>???>>=>>=?~5 =>===<=<<<>=>=>=<<=>>>==<<=~4 >>=~8 >>==>>>>?>??@@????@?>===<~13 ;<<<;~8 <<<=<=<<<===<<<===<=<=<~16 =<=<=<~5 =~4 <=>=>?>;;<~4 =<<;;;;<<<<;;;<~4 =;~4 <<<;;;;<~9 ==<=<<;=>=?==>>=<<==<==<====>>>=>>=>~4 ??>>??>~7 ====?>>>>??>>>>??@@?~4 @????>=~4 <<=<;~6 <;<<:;;;;<<>;=9<<<?=<<<<;=~14 <=<<=~11 <===<=;===>>>==<=~5 <=>=>====<<<<==<<=<<<===<<<=~6 <=~6 <=>=<<<=;===<=~7 <~12 =<====<==>=~6 <=~5 DA@DC<=?FDCEDHJJH=~11 <==CEEBGIBH<~19 =<=<=~4 <=>BC=~4 ><<===<E<CJKIGHBJ====<=<==<<<===<<<==;;<~11 =<~9 ;~7 <;~12 <;<<<=>====;<>>>?=::9:;;;;:::9<<<9=;;;<~6 =<=<<<<;;<;;<;<;~7 <;~6 <<;~5 <<;;;<;~53 :;~10 <;;;<~6 ;;<;;<;;;;:::<:~4 <~4 ;<<;~5 <<<;~8 :~5 ;<;;;<<<;;;;<~17 =<<;;9::;;:;;;<;;;::9998;:;99889:98888;;:::9<;;;;GGIFJHLLBCLFHI<CA==<===<<<<===<=><=<>>=<=~12 <====<<<=~7 <;<<==;<;:=:::;~4 <<:;:~4 8:9988977===<;<;hggjhrnotvnsiqvfhffeefjyxyhghxyxtyzxy|xtmpxxxefcccdecbcbccddgffbabccabadcccd|zz{~7 zz{{zz{wz|zzz{{zyyyzwywwwsttrrnzxwssssnllmmvqpR~13 QRRQRQRQRRPRQQQRQQrwnvxxruuolsjihivgebaa```gb```cdl```bbb`bi`^_`elfnahae`p^_^^_^skoressnajbga`b_e?>>>??>~5 ?>>=IMIK:;:~7 ;:~4 ;;;9~5 :999:9:~9 ;<~5 ;;;<;;:9:~13 9999888899998899889~4 ;~7 <;;<~4 ;;<<;~10 :~5 ;<<;;;:::;:::;~10 <<<<;;<;<<<<;;:;:9<=<;<~4 =<<<===9:9:889::;;<;<99=;98:<<===<<;<;;;;:<=:<~8 ;;;9~5 :99:;;;;:;99<~5 ;<;<;;<9988977778788865767::8998:89==<=<<=<==<==<==<=<~8 ;;<<<<:;:;;;;<<<;;;<;:::=~11 <===<~12 =<=<<<<=<=<~14 =<=<;<;<;~7 =~6 <<=;;=<<=<==;<<=;=~4 >=>><<===<<=~10 >=>>=~6 >=>>=;<<<=<<<====>>==>??@?>>>??>>?@?>><~5 >==>??>=>>>>?>?>::;~6 <;~10 <~8 ;<;~5 <<;<<<<;;<;~5 :::;::;~13 <;;<;;<==;::;<;::<~6 ;99998899889;;:;:;;<:;:;99;:;;9:;:;:;;<;<<;;;;<<;<;~4 88898899;;<<;~4 <<<<;<;<~5 =<<=<;<<<==<=<<==@>??;==<??<<<<>==>>>;;>>=<=<;<=><9;;:9:;;;;<~4 ;~5 ::::;;:::9::9898::::999;898878989;9:9988::87887878778888::::;:89::::9:<==<<<=<9::999:99::;::=<>>=>>><;>>><~4 :::;;<<;::::;<;;;:~5 ;::<;<=>==<<;<;:==<<;;<<<=~4 ;;<<;<<<;;:<=<==<;>=>>;;;==<==<>~5 ====>==<=~4 <=~4 <<<=<~5 ;=~10 <<=~6 <===>>>==>=~15 >~5 =?>===>==>>>=>>=>===>>>=~10 >:;~9 :;~4 ::;~5 <<<<=<;<<<<;<;<==;;<<<<;;>>>>?>>>><<<>=<=<<<;>>>=<>>:<>>=9::;;::;9=;<<;::;==9:::<;;::=::::>=>~4 8998999899:88787889999:9999:999:~12 ;~5 ::::;;::;:~5 ;<==<<=~5 <;~5 =<<<=~11 <;<<;;;]aa]_a_ba^eeakljlihkllkkkgffeed<<>>>=;<<;~4 <::Y!Z!Z_]ZYX^`db^!!ff[cccbbababcbcb_`bc[[Wdb^[]Z[]`dXW[eaYXVYehgdextsdccccnm{xystuw{wsf[XeXW_Wjk_XZmk7~4 :;::::88::9:999acb_`_`^^a_accba__]bca````a``ab``<=<@@@```_``_^_`]^^==<==5657989<6>:>:=443444756><<=D?>??>>>>???>>>??>~4 ==>=>=>>>===BBADCG====>=>?>>====>~18 EEDFEGGE~19 DDEDEDE>~8 5453552256;5986A:<;<<77767;::;99665569797;::7656<<;<:;=>====:9;9:66:967577888989766555656687677768978<;99;;<:;9::;~9 :787;;99;77;:977;;;<~8 ===<<==;;==>=>=>=>====<=<;==>=>=;;;<~5 ;<<;;<<<;;;<;~4 <<<;;;<;~13 <;;<<;:;9;9:::;<74;;22487;<;<<;<<;<;<~5 ;:;;::;~5 :;;9:::9~7 ::;999:9:;;998;;:99:9999778788789889::99898~10 778777688758877887675656777<~6 ===<=<~10 ===<~5 ;~4 :;::;:;;:9===<<<=~5 <~6 =<<=~7 <=<~5 ;<;;<~4 ==<=<====<<<::<<<;;<=<~6 :<;:9~6 <<<;;;<<;<<<;:;;::;;;;:::99;99888;;::9:<<;~8 <<;~9 :;;9~5 8887~4 677667678898:;;;:;~13 :<:?;:~5 ;9::;;;;<;;;999;9:::877887898978:~5 9::98:966677887777668777A@;<BF8~7 56679887~4 889877998998:::89~4 77;;;9:;97878~5 ;;;;99:999:999987::;>989:;::::9>?@@A9~17 <<<::;<::;<:9~6 =<<<;<<<;::;:;9<;;:9;:;:;;<<>==>><<<>===><QQQQRQQRRQPQQQP~8 ==>>==>=~7 @@@A@A@A@@@AA>>>>?@@?~4 @?>~8 ?=>>>=>?>???@@@?@???>?@DDDCDDDD?@?@CCCADDD@@???AA?@AAACABDDEEEDECAAA@@????C???GGGEFHHHFHEFGFFFGFHFHGHHHGG?~4 @@>~4 ?>??BBDB>??>~8 =>~4 ?===>>>>==>~5 AA??@>>@ABBA@@B?@?@??>A@?A?>>@@???A~5 >@?~4 @?>>@??@@@@?@@??@~5 A??@~4 >>?>~9 ==>@@@@>???>?@>?>>>====>=>=<=<~5 ===>>=>>==>>><<>;>=><<<;;==<=<<===<~5 ====;;;:::;;;::;<<;:=<;::;<;=~4 >=~8 ??@@===>>>@=@@@==?==A~4 BA][]XXZZVVXYVUUWUVVUZ[Z![]^_^^_^_^^_^^ZVTSRSTSSTSSSR<~8 ;;RROOQOKHHLJFRQOQOPOQFGGGFFGGHGFHFFFFHFHLKL8~13 77877878~6 <<;<;<;;;;8~4 <<;;<899::;;:::;:98888998;;<;:998:;?@@@>?>>??>>?>?>>?888?>>>8~6 =>>===9:;8:8~6 ;=:89;98~4 ?==<9;<<;98?@???>><>~4 =~4 <<;<>>>===<8888;;<;88989:::9<9:99:~4 ;:;:<:::;<:8:9::::8~6 >~6 =>>>887888877779899878778:7;99:::9998~4 77676~5 :;::;:::>~8 =~4 <<===<<<<;;<:~4 ;:;;99::9<=;:89889ONNPNQQPOPPQPQPOOIHHJJIPOPPLLLMNLKOHGHLFHEFL:~7 ;~6 :~5 9::9;<~6 ??@?~8 >?~32 >?~10 @@@?==<>;<;;<<<;>=;;;<=>=?E~11 D~4 ==<=<;::<;::;;<=;;;;:;;;<;<~9 =<<<<==<<<;<<<<;~4 <<;<~21 =<<=<;;<;<;;<;<<<<;<~9 ===<====<==<<;;<<;<====<~21 ;;;;<;<<<;~4 <;;<;;;===<<====<<<<==;<<;==<=<~13 ;~19 <~5 ;;;;=;;<~4 ==<<=;===<~8 ===<~4 =<~11 ;;<=;<~4 ====<<====<==<~6 =<<===>=<<=~5 >=~4 >>=>?>==>>??>??=>>>==>==?>@?>@>~11 =>==<<==<????@??>????@@@@>>????@???><~5 ;;<~5 ;;;;<;<<;<;~6 :~7 <<<<;:~10 ;:;<<;:~10 9~5 8~4 9989988:;<;:::HHIHIIIGFHIIIHIGFHGIFFF>?JI?>?JJJGG?>FFFH?>=OPOOOOKMMKJLNONKPPOPILIJ???>>?>????@>=~5 >@==@<A>=<~4 ===>>=>=?>=>><<=<>>>?>>A@A@CC?~4 @@@?@?ABC@>>?A@BACDEAAB~5 ADQQPRRPPOPMKKMNORPRRRIIJIHPQPPONMMLHKHHHHA@@A~4 ?AA=>>>=>@@@B@@@?@?BA@???@@>@@@>@>>===>>>=>>=~10 <<<<=~5 >=>??>@@@=>=>>?>>>?>>@@@@????@>=>??=>???@?~5 @=?==<<<@?>?>?@@<@@>>>??>???>?~7 >A@@@?~4 @??@~8 ?@@@??@?@?@@??>?~6 >?>@~9 ?@?@@?@?~5 >?~7 >~6 ?>?>????>>??>~4 =>>====>??>=~5 ????>~5 ?>>?=>>?~8 >>>??>AAA@?A@AAA@=@@>=><=~4 >?====<===>=>>;<;;<;=<<<=;;;=~14 ;;;<<;<;~6 :~5 ??>?>=@@@>==>=@===?@>>>=>=<<====>~5 @~4 ??>>@@>@>?~7 >>>?>??@@??@????@BB?@?>?>@B@~4 A@??>??@=~6 ;<~5 >>>>=<=<=;;<===<===<:;;;<;9;9;=<;=89:9:9;:;7776667755668568778888=<<;<;::999:9:;9:999;88788;<<;:~4 ;::;:98:~6 7877878:::;;;:::99:885799<:<<<;:;:===<=<=;;<;<<<<;~4 :~4 898~4 ::9:99:::89:9999899<===<<<;;<~5 ;;:<:9:<::9998998~5 98878778877<<<;<;99::;;;;::98~5 96~6 99777766887798978787688878~4 7878998888989:9:;88:9898877768778~6 78876677677767666868~4 9~6 ::99:9;9;;:::;;99:~5 ;:9::::99:;;;@~9 ?@?@A~8 @B~4 ABB?@@ABBAA>A@AAAA=~5 >>A=>AA===@@==><=~5 <<>>?>>=~4 ?@??>====>>?>>>@A????@A?><@??@AAA<BAB@?@AA@?;?<=B<BBBB<<<BABAAA@AAAAB@@>?=><<<<==>?@<<<<@?@<<<<>><~5 ;<<;~4 <~5 =<=;==<;=~4 <;::;:<~13 ?@~4 ?AA@;:;::<<<::<<;:;:~6 99@AA@~6 AA@~5 A@@@@A@@@B~4 ABDCAAABABBCBA@>>AAA@>@AAA<=<@99::999:::9:@~4 A@~5 AA>?>?CBBCABB@A@AABB:;;:::<<<;;:~4 ;;;<;=;<=~8 <>>>=>>===>>>=>>==>>>=>>????>>>>????>=>?<;<<<=;==;<;;<<<<;;;;=;;;:;:~4 ?>?<<<?~7 =>=<=>??<?==>>><>~4 A~7 BAAAA<<;;<<=;=BABA==AAA==BB=~4 AAA@A==BCBB:;;BBBCBB:::;BBBB;<;AA=<B<;=AA@@A@@???>=>A>>>>?>>?=<===>=?B@@@?AA>??@??@???>~7 ====@~5 ??>==@@=>@>>>=~10 >>>=>>=>=~37 >>=>??>~5 ?>?>>??>?>>=~4 >==>>>==>=~6 A~5 @A~6 @????@@?>?>>?=@@A??@@A=~5 >>?===>=>>===>~5 =>>=>~11 ==>==>~5 =>>=~7 >=>>=~15 <=~4 <=<===<<==<==<<<=~7 <=<=~15 <===<~5 =<<<=<<==<<=~8 <=<<<<=<<<<====<==>>=~6 <~8 =<<>>>=~4 <<<<=~5 ;<<;;;<;;;<~15 =<<<<;<;;=~6 <>>===<=~6 >~19 ==>>;~11 <~34 >~7 <=~4 <==<<<>>=<<====><<;==<=<;<=~29 >=~5 <<====>=<=<>=>=<~4 ;;<;<=~21 <=<<<<===<<<=<=<<<=~5 >===>>=~12 >=~7 <<>~22 =~17 <<=~11 <=<<==<====<===<<<<=<<=<====<=<<<<==LMLLLLKIJLLLJK=>=~6 >=~5 >>===IHKJGJHKLLLLM===>~7 KFJKJJHFJJ>=~10 <=<<=<==EFFFFHFHFIHFHE?DCA<===?<<=CDCC<=~6 FGGBD;;;;DGFE;<;;=H;=>>CH<=<~4 FHHIEIG=<==<~4 ==<=<>=~4 <<<<==>~8 ====>==<=~6 <=EE>===E@;<~5 ;~4 <<==<;<<<;;<<<==<=<==<=<==<<=<=<~6 ====<<=<====A?@@@C??=>?>?>=?<<==<=<===>>=~4 <=~4 >>?>>??<<<<=~4 <<=???>>???===<=<EEDEEDDDDEDEDDCD;~7 <<<<;;<;;;;::;;:;;::;;;::;~4 <;=FBFBHHHG=>=>>=<<<=IKID;<=GJJJ?A@CB?>>@?C@?><;<;<<<<====>;=<==>;;;>;~7 ====<=<=~6 <<<=<<<===<<>=~10 >=<====>=<<><~5 ;=<=<=~4 <<<;;;;<;~5 <<;9999:::9::;<<<=<<<;=<<<===<~5 >>=>~4 :::<:;:;:9~11 ;<<=:<;====:<=~9 <<=~6 >>=~5 >>>=::8:999;;;999;:;;<~7 =;;:::;;:<<:~4 ;~4 <~4 :<<<:::;::;<==>==<<>=>;=>>>==<<<<=<=<=<<<<=<~4 =<<<==<=~4 >DFFDEHEKJGKIIJIGILKKKMCKKKLJKKKII?AA><<?B=<=<ACD==<=<=KLKLKMJJ=~11 >=>~4 =>>=>~6 ==>==>==>>==>>>=~4 >~4 =~4 <==<=<=<=~4 <<<<==<=<<=;<;;=~18 >==>=~10 >>=~10 >=~4 <=<==<====<<<;;;;<;=;9~10 ::::;;9999::9;~8 :9::;;:<=999899:;:::;::;<<;=;<<<;:~8 ;~4 ::;9:999:98889:;;;;::;;:;8787788997~4 99::9877888968867776788898899989997798887;<;~7 <;;<<<===<<==<<===<<=<<=~4 <===<<<==<<<===<===>=~8 :<9<;<:<<<<==<;;;;::899:;9998~4 JGGLD>F9;=>?@46<@E>>=F>===>=~4 ::<<;<;<<=>>>>=>>>=<==;=>>=CC;6@8=6DA7:98798;98?~5 @CAJDG@CLD?8?::?55<6@A6675@95553434558C9C;74755566665~7 DQ77:9NNOFM::9:99<?~9 >~4 =~4 ????>???>>=>====HLKJ=~5 AB<<<=<<<=<<====<===<====<==;;<;=:;;;:<<=<==<<=<;<<GEEEGDDED<=<<==>==<=>>==<~5 =<<=~5 <==CF>FCF<=<<=<~8 =<<<>>=<==<<?<<<<=<<<@?B@??@??@?@~4 =~4 <<<==<~6 =<~6 ===<~6 :;;;<~5 ;;<;;<==::::<~4 ;;<~5 ;;<<;~4 <;;<;~7 <<;~10 :;:;;;;:~4 ;:;<;;<;~9 :;~4 A>?;;;;@?;~7 ?>??@<;<;~4 <;~8 <;;;<;;<~4 ;<;<;~10 <;;<;~26 <;~5 <~4 ;~4 <~5 ==<==<<<<=<=<<<==;<~4 =<<<<;;<<;;=~4 <~4 =<~20 ===<====<=~7 <<<;;<<;;==;~4 <<<<===<~4 =;~10 <<<;<~10 =<<<=<==;<;<;;<<;;<;~11 <;:::;<<;;<<<==<;<=:;=<:<;<~4 ;;=<~4 ==<<<=>>=<<=<=<~6 ====<<<<===;;:;<<;;;:;:<:::<;:=>=~8 <=<=~7 <===<==<<<=<<==<>==<<<>>>=~8 >=<<>=>==>>=>=>>>>=~10 >>;<<;<;;==>=~7 >=<<;<=<;~6 >~13 ====<<====<=~12 <=~26 >>?>?>===<===>??>=>>>=~5 >>>>=>==EEEDEDDDC~5 DDCCCDCCDDDDCDCD~7 EBAABBABA~4 BA~12 @?@@A@AA?@AAAA@AA@?@A?@>?>>???@@?A@?@??>?>>?>>>??>~6 b~26 acaabcbbaaabbbabbbbaA~7 @A@A~6 WWWWVWXVWYXXWWXWWTUWUXWXWY@A@@@@AA@AABAAA?A??@A~5 @@@AXXXYXWZYhhgih[]!]eefcdeefcdca`dde`bcT[T^^XZUWZXZ[Y[Xcdd^]_ZYWWZmlkffmmhjeWVWW!!]bb`UUTUWZTTVV[^!]ZZ!Zcc`ccdddffihi[!jllkikkjkic`b_Zj9==:?<<@<?:<::;::<::?>~6 =>~5 <=<<=>=>><<==>>>=<???>==<>~5 ???>>>>???9:99=<====;;<==<:999==<99:;8<=<=;::;=>;:>>>???>=<<><=>?<<=?>;>~10 =>~10 =>====<<=>==<<<=<<<<;<;~6 >====;:;====<<<;~5 <<;:<;===>>=>====>~4 =~6 >====>?~5 >>=???==?~10 =>~5 ?>~10 ??>A??A?>>?>?~5 ==>=>~5 =>>>=;~6 <<;<~5 =>?~4 A@@@?@????@@?@A~4 BA@SSU?BBWSVZPPP?BEKXQPNNONNPQP>ONNNJOMMMJKQQPN~4 B>@@>>>=~4 >C==?PQ>=F>====HDLC?>=>>>>???@B@=@<<<==<<=<<==<===>>====<?>>><~8 >>>>@==??<==???>==<<<=<<====<>>>=????=<<<?>>=~11 <~4 ;<=;===<<;:::;~9 ::;:;<=<=<<<;<>>>>=<==>;<=;<=>?:~6 ;~11 <;<~4 =;~5 =<=<;<;;;<~6 ;:;<~9 ;<~8 ;<~9 :;::;:;;::::;:<<;;:~5 ;;9:;<<===<<<==<<<;;;:::;<;9;:;::=~5 <=~6 :::====>>====chbh_``bcmnmnhlnl]]]]b]^!]]bklhklkglll=~6 >>>==?>???>==bb_fg[^[T]mmgpmlm]mngbebdcfebaaVTVTSYTUX!!Y!W]VV!`[YYTXa^_aVU_ZZZgfefeebRUQURUWWUT^baWUUU]bdcTTY]WVVVbbbaddecdeecdcd@=<A?=<=<=<=>><<<;;:;;<<<;;;:;;;<=<~5 ===<<===<===>>>=~8 ;<~5 =<=<~4 =<~5 =<==<<<<=>=>=<=<==<~6 ;<~16 JJLJHKLLLLKLMMLLKLLKHKNNNPOLI>?>????;<<>>?===;~7 <~14 ===<~16 ;?>??>~5 ???>>>><~4 =~8 <>~4 ?>><==>===<=<<<==>>>?==<=>>=<<?>???<~4 ???>?>?>>A@A@???@~4 ;=<<<==>>?=;>=<?;<;;=;;BA@>=AABBAA<<<A87666989::;:8;<;:;:9:KGIIEFGLGHKNKJ>3HL1BE<6CO47657999:;;5699986897:660116575224<;;:~6 ;;<;~5 :::;:;<~6 ;;;<<;;;<<<;<;;<;;;<<<<;~4 :;::<;~5 <;;;<<;;=<=<<===:;=;:;:::;::=;::>><===>>>==>>>?>??A?>?AAA>?>A>>>?>?AAA;;:;HFEG[!YWZ!XWUYUWVWUYXWWVWUY!XWWWWRRRSSQSRSS^~8 99:~17 ;::;::9:9786658666;596857996<8<7989~4 :97899778876997=~13 :~10 ;:;~13 :;;::99;:::778;8~4 788:;8989~9 89:78~4 586885:5698;6677888766588>>>><<<;==<;><;<=><;=~4 <;<;<=;;<=>>===<<<<=<<<<;<~20 ===<~28 ;~8 <;;;9:9898999::999:~5 9~4 :9:::;<;<<;<<<<;;;<<;;<;<<;<<=<=<<=~5 <<<<=<===<==<<<<=<~8 ;;;:;::;:;:;::;:;;;<<;:;~5 <;<<;<<=<<;;;<~15 ;<<<;;<;<;<;=<<<;;<<<:<:;:<<;<<nopllkjkhikjlmlprrpppqnqoorppmkknnmghjhhjfffgh~4 ileefddeh~4 ei:~9 srsrptmrptqqpowwtejflllhmunlffdhfjsvvrlrkknmnvvlehfdckgfmmdljfjmllkmqpqlmkmussutturihjffigfgffikleffefjkefedccdddcghflpelmkjkngkbbccbcbcbbccbbbbcifdcjffe77888878:9:;97:<;858768949~7 59999859795=~4 <<==<=~8 E~16 DDEDE~11 99:98868:7;;<;<<;~9 <;<=<<;;;;<888799989;~4 ::;~8 :::;:;;;;:;:~4 <;~4 ::;;:;::;;:;:<<<;<<;;<<<;<<<;<<<<=<<==<<<;~9 <<;<;;;<;;=:::9999<;;9;<<<=<<;<<::;9;;;999<==<;;;;=;<;;;<;;<~6 =<<<=<~4 ==<;=;;:;;::9;~4 :<9:=;<;;:;;9;;<<<<====<<<;;=;;===;~10 <<<;;;=<~11 ;<<==<~6 =~7 <~6 ;<<;;<~16 ;;<<<;<<<:;<:<<===<;;;<~4 ;<;~9 <<<=<<<==;<==;<=<=<<=<<==<==<==<=~9 <=~7 <====>><=<<=<=~8 <=>>>=~4 >=~4 >====>===????=<<=>>=><===>=><==<====;==;<==>~5 ?>~9 ?>;<~4 ;;;;<;;;<;;;<<====<=<<<=<~11 =<<<<=<<<;;;;<;;<;;;:;~7 <~6 ;<<==;<<<>==::;;;:==;;;;>>=;=<==<<=;:::;~7 :;~7 <:<;;<;;;<~5 ===<<;;;<~5 ;;;<;<>???@@@?@@@@?AA??>@<~5 >>>>=<<=><<<<=>?=>=><~5 :::;;;::<;~7 <<<<;;;:;;;;<~6 ;::;<;<;;<<<;;;;<<<<;~4 <<<<==<<<;:<;=<<<>>?@>??A@AA@?>??==<<?>??=???<=<=~4 <=<;<~4 =><<<=<<<<====>?>>=~34 >>>>=>>>=>>===>>>==>>>==>=~4 <<==<;==<=~7 ;~4 >~5 ====>>==>=>=>~10 ===>==>>=>~11 ==>~5 =>~7 =>>==>~4 =>==>=>=~8 >~4 =A?A@@@???>?=@A@@?@@>>>?@?>=>>?==>?><>=~4 >=~5 >===<<<=~8 <<===>~8 =>==>~4 ?>>?>~6 =~4 >>>=>??A@@AABAB@@@?>>CABBA@B?BB??B~4 =<==<<<===<=<=<~8 ;<;;;<;;<<<=>=>>>==<><=<<=~7 <=~12 <=~6 A@AAB?B^h]gfBf^_fbXXZ!YXZYYBBkkjj!C?@????efhnnkmkmlnlkllk??>^d]VZ[WVX_eXZ[kjkUYVTTTRQcddddecccddddcd`cacc^^VWaaVTWacca!ccbbU!VVTTUTYYXUWWXbXWecRc`TUVUafj_XVkiffdblXnmoUTTUh`befmXompoek]k<=>=ebcbceecdbbbfabhhhghfhgddA~4 DDFEEDDCCDDDDCCDDDEDE~4 DDhfececfcbdbbchgfd<<<<==<<<==<E~4 FE~11 DEEEDDDE~6 ::;:~4 99989989:979::9:988;<<>;B?;>>:<:;>>=>>>>=>>=~12 ><=>>====<==<;;====<~6 ;;<;<;;;<<<;;;<;~5 @<AAEEADDAAA<C::99:8998:<999889~4 :9;:;;==:9:8:9<<<<=<<:::<<<=:~5 ;:;<;::;;;:99;;<;;9999<:9;:::9:~5 9~4 ;:;<<<:;:;;<;99:999::9:9999:~5 999:;~10 9:9;;;=<<==<~8 =<=<;<<<<;<~4 ;<<<;<<:;;:;::<;~6 <<;;<<;~10 <<;;;;<<;~21 =><;?;~6 >;?;;<<<;<::<<<<;<<<;;;<<;:<~9 ;;;<;;<;~4 <;;;;898;:;;<<<<;:;;9898:::;9;9;;;::;99::;;;::;;9:;<<;;;;:~5 ;:~4 ;::;:::;::;;:::9:~6 99:;:~9 ;;;;<~12 ===<<<=:;9:9::;::;:9;;;999;<;<~8 ===;;;<<<<=<~5 ==<<=<~5 =<~9 =<;<;<<::;<;~5 :;<<<<:;;:9999;::9:;;:~9 ;::;;:~5 ;:~4 9:::;;<;;;::;~7 :;;;:;;:~6 <<<<;~4 <;;<;;;;<~4 ;;;:9;~4 :::99;<<;<<;<;:~5 ;::::>>;;?;:~7 <;~5 :;;;;:;~9 :~9 ;;;;:::;::;:::;~4 ::;;<<<:;~4 :;~15 <;;;;<<<<;<;<;~4 :;:~4 9:9999F<?:;=;<9999:::C=99::BECDB:;~5 <<<;;;::;~5 <<;:<;;;9:::;;;;<~4 ==;<>><<=<;;<~5 ==>====<<=<=<=~5 <<<::;:;;>??@C=~5 ;~4 :;:;;:;;;==<;<;<;~5 <<<<;;<;;<~4 ;;<<;;<~4 D~8 CDEDDEDDDC~4 DCCDDDDCDD67657776676669~13 4633300-..10/31.769955566715157599555342:0812.:;;-,12,;;;;<;;-6,;;9;<=>=>::>><=>;:<>><<<>~6 ==>.0-9~10 :9::9999::99:~5 ;;:::17/73:9~5 <<:<<99;:;;9::9<<<;;;::<~10 =<=>>=>=<==<==<;<;::;<;:<::;:<<9:98;99<98<9;<9<9:9~4 :99:9:9~4 :<<<<=<<====<=~8 <<<=~4 <<=<==<=<<<;;<;;;;<<<;:;:<<;;;<;<::<~4 ;==<<=9::98999:999::;9=~8 99::;:;998:999;<===;:<<;=;::<<===<=~4 <<<<::;:99:99:::99;;;:::98899;:;::9999:;<998~4 9999877988778799977:98979:;;;::::;;;;<:;;=<<;;<<<<=<<==<=<=<=~5 <=<>==;<;;<;<<999::::<:::;:;<<<9999:;:;;::<:<99<:<>?~4 >?=~7 >>>>?=~5 >?A~13 @AA@A@~6 AA@~5 A~5 @AAA@A~7 @AAA?>?>><=?===>><<<>>>====<=<==<~6 ===<;<<;<=>=><>>><<=<<;=<><<>;:;:;:;;;:;=~8 <<==<<=<<<=<==<<<<=<<<==<~12 =~12 <~13 =<<<<=<<====<=~4 <<<<=<<=<<<<==<<<=<=<~10 =<<==>=>===>=>>=<?<=?~4 =~4 <==<~9 =<=~8 ><=>><<==>>=>=>>>?>>==>?>>>====>>===>>=>>=~11 <<<=<<==<<====>>>=>==>~9 ===<===<==>=~10 >==>???>>@?>?>@A@~4 AA@@AAA??@?>>>A>~6 =<>=>>>>==>>=>A~10 BA@A@AABBACCABCC?~5 >~4 ?>>>?>>?>=~6 <===<===>===<<=?>~6 ?>=~5 >=>=~9 >=>~4 =>~4 =>>=~9 <<<<;;;<:77788788:;889889;:9789777=><;<>>=>==>===>><<<;<;<777;:7~6 12:;;85+..98889:87988872/*0..7~5 17~9 337789899978,.))(672655===34<<<9<22<4=2-==>===<6===>=>>>>=~5 ;<<;<<;<<;;<;;:;::;;;:<<=~4 <=99:9<9:;8~5 9999:8;:;;;<:::;<;:676~8 7~12 879888978777787778777789<<<;;<=<;?>?AAB>>?~4 BD>>>?>?==>?=>>==>=?==<<>;<;:;~4 :;~5 :~6 9:9:;;:~14 ;;;:::;;9<;<=<<;<;;;;<;;<;<;;<=<==<=;~7 <~4 =<;;;<;<<;;;<=9~7 88898:987:88988877788798899464767~4 676654778777755644445664556~7 44546~9 767~4 6~8 766676666877787888778787888878787~5 8~4 999899888998:~11 9989:::9:::9:9:9:~6 9:~5 ;;:;;:;::;;:;~6 :::<;<<<;:~9 ;;:;;;:~4 9778~4 :::88::9899:9::9::9:9777789899989766678877877588887547777556676645778~4 77778~9 98888989~5 7787777886566756678668545556666455676776889899998~5 798~5 99::::97~4 8889~4 ::88998888778779898999:;~6 ::;9::99;~7 ::;;;:;:::;~7 A@A~4 @~5 AAA@AA@A~6 @@AA@ABBA@A@@@A@~4 AA@A@@@>~18 BBBCCBBBCBBAAABBBADDDEDB@~4 BB@BAA@BBBB@@@AA@@CC@@@?~6 @~7 ??>~4 =>=>~8 @>~18 ????>>?>~20 ?>????>@~6 >>>?=>==@???>?>=~4 >>=~4 >>===>>==>=~17 @ABBBA@B~32 ?BA@??AB>B~4 >===>>?>?=?~4 >??>??B~9 @?@??@A~4 BA?A@B@???@@ABBBB????@@BBBA?~9 >?>?>~4 ????>==>=<=<=~4 >><~4 ===>~8 ==>>>?>?>>=>===>??>>===>==<=~6 <=?~8 >?AB?@@AA???A????@@?@?@@?@?@@A@@A@@@@????@BB@B???@@??@@?~11 @@?~4 @@?@???@?~11 >@=?~8 @@@@?~13 @@?@@>?~5 >?>?<<<<>===<<<>=>=<=>>>>==<=>~8 ??>??>?@@?~4 >???@????@??@@@@A@~5 ?~4 @????@??@???@????@?>?>?@??>>????>>?~7 >>>=>=>~16 ?>~9 ?>~9 =>>===>~7 =>~7 @?@?~4 >>>?==<<=<<<>==<<<???>>>===<=>>>=><<<<;==<;=<=>;?~6 >~5 ?>~12 =>=>>>>==>>>==>=~4 <~11 >~15 <==><==<~15 =<~19 >>>=<=>>>=~4 <=~5 >>>>=>>=>====<<<====<~14 =<<<==<~5 ===<=<=>~7 =<<==>>=>>==>====<===<~8 ;<<;~7 ?~12 <~14 =<==>>??<<<<>=><~4 =~4 <?>~4 =>>>===>><~9 ;=<;;;<<;;;<=<=<<;?~9 >=~5 >=>>><<<>=<<<==<<=>???>???>?~11 ===;<;<<=<~5 ;<<;;===<~5 ;;;<~7 ;<<<=;;;<<<;;<;<;<=;;;;<;<;;;TQRRSSRRSPS==<<==<<==KNONNOOOQOPSS=~5 >>>==???>?>????>>?>SPRRQRSQNNQR===<>=>=KKLKLKKLKLLMOLQMMKFJGPNQONOHEHKMOLFEEEIEI>=>==>><==<<<==<<<<==<<<=<<;<<<;LKLJLMLNPPNPKPPNOQNIJNKPKMMJMKJO??>~5 ????>>?~5 @???@?@@>=>===>>====POPPNOOOOPOONMLNMMMNM?M?~6 >~5 ?>?>==?=>>>=>===>>=>??>??>=~14 >===>=<<===<=<=<<<<=~6 <=<<<===<~5 =<<=~5 <=<==<<<<=<~5 ;<~4 ;<<==<=<;~4 ONMNOQOOOFGIHJ>=HJ>~9 =~17 >=>=>=>>===>=@??@AGDEHC?@?@>>>>DH>>>FA?NFNGILMMMMKJIIL?>IHLL?MO>KO?>===>=~5 <=~5 <==<===<<<<===<====<==<~9 =<<<<;<~6 >?>??@@??=>?>=@@@?<<<=<~5 ;<<;<<;;:::;<<;;<::<<:9<;<<===;;;<>;:<<;;;;::;::<==;<<=~6 ?~5 :~8 ;;:~16 ;~4 :===>;=~4 >>>??@?@@??@@A@A;=~4 <~6 =<>?<?<<<?=?<=<=<<<=>AB<<B=<<?~6 >BBBB<B@>>BB<<<>><~5 ;@AAAA<<<<@A>>A<<@>@>ABBBAA<~4 =;<=<?@@@<@~7 A~10 @@@A;~10 :;~5 :;=>==DFCF=>=;<<<<;==<<=<===>A;<>;<=A====<;::999;;::;;::;<;<;<=<?LNDQO??FO>PVI>PJG@K@===?>==IPQFUSVVTYJDVVU>~6 @>>?>??@>==AB???=?>>??@?<<;;::;@@><?::::<;:::@>??>?@?@@@@?@@A@ZXQ[QQOMMKJJQMNLMMLKJKHIIJILIMNJMMONOPPPPNKOPMMNLNMNOMMKKHLMNMPPOOMLKJONPONMNMNLNONNMNOPPPPOJMMPOOONGDFMN=<<===GHF>JFE>>=<<<=<<=OOOQONOPPO=<<=<=<<<<=<<<=~5 >=>>===>~6 ==>>>=>>>==>=>>==>>=>>>>==>=<~8 ===<<<<=<<<<;<<;<===<<>==<<<<=<=~5 <=<=<<<====<<;;;;=~5 <<<;=;<>=>=<<=<<;;;;<~10 ;;<~8 ;~5 <~6 ;<;<<<;::9;;;9:9:;;:9;;;<<;;9<<<;<~14 ;~6 :;;;;:::<<<;;;;<:;<;~8 <;<;~6 <~10 ;~7 :;~6 :;~4 <;~4 <~5 ;<<<==<=~5 <==<;;;<<;<~4 ==>>=;<???;~16 ECCFGGF@@@?FFEFG~6 BC@@?@DA>?>=MMMKLORTSRRRRQRSRSPONMPMPOLMLNQPOOQOTORSNNNOSQORQQQRQSPOONOPOPQNOMNMLMLOMKMKLKKK>~4 ?>>>?>>=~5 >=>=>=>=~7 :;:~6 ;;:::>~4 ===<===<;=<<==<====>>>=~23 >~4 <<=<==<===@>?>=@A=<=>??CACACCC=~4 >=>=<=~7 <=<~5 ;<;;<<<:;;<<<;<;=C??A===<D@BD<====>=~4 >>?=<=;<=>==;;<;:;OMQQNPPPQQONNNOOONMQPQOLJKKNM?@?>>>@?===>=???>=?==?~4 >>>>==>==>~5 ==>=<====><=?@=@<<>><<?;;;:;;;;::PNPOPPNOMKKLONRRQNRSRMLLMMLLMNNP???@@????@~4 ??PPQPNOPOMP@???>=>=?@?>>=><=~6 ><~7 ;~7 <<<<;;<<;;@?>??@>@=?@=?>@@@@<~8 ;<<;<;:;:9~5 :;;88:;:;<<<;;;;<;:;~4 :;:;;<<<;<;~15 <<;;;<<;<;;;;<;;;:~7 9:~9 99989:9~4 :::99::::8:::;:99:8<;;;<:;:::;;;;<;;;<<<;;::;;::;::;;;;:;~5 ::;;<;<<;~6 :;~5 <;;<<<;<<<<;;;;<<;~7 <;:;~7 :9;;;:;;;;<=<<<==<=~9 <<=<===<<<<==;<==<~6 ;<;;=<;;=~6 ;;<~18 =<=<<<=~5 ;====<=~4 <;;;<<<<==<;;<<=;;<;<;<~5 =~5 <=<~4 ==<=~6 <~11 ;<~16 ><<<=~4 <=~4 :::=@?<<::@?@>@?@=<:>::@::<;:@<;<;<<;:<=~5 <=<<=<<;<:;<<===<=~5 <;===<~6 ==<<<==<>?==>=<<<=<=~12 <<<;<<;;<<;;;;<~4 ==;;:::=<<<<=~11 <=~9 <=~5 >=~4 >??<=<@?=?=>?=~4 <<=;;=~7 <===;<;====;;;===<:;;;;<<=;==<;;;<<::;:?;;@=~6 >=:~4 <:;===<~5 =<<<<=<<;=<<?>??@@@B?>?==>>=<<><<><~6 :;:<==<~6 =<~7 ====>><<@>????>=<<<==<=<?>><>===><===<<<<><?:9::999;:;98989~7 898998999BBBAAAABABBBA~4 B~6 AAABA@B?@@ABBAABAA@@?~4 BBA?>??>===>~7 @??>>>?@@????>==>!~5 [![!~11 [!!!B~4 ABAB~5 AB~16 A~8 B~11 AAAAB~5 !_]_[^[^[X][Xhiihiihhhg~4 hhfeehfegdehmnfiX^Yab[]XWXXttr!ZXY[YZ[Z!Yxvvxzvyqsuvnhp![[[!![[]Z!_!!c_chkXWWZZX[jo!ZX^`_`^iij[^[e~4 aedfhfhfjlkjkikadf?>>?=>>>>??>?=>~7 ?~10 >?>=>====>>====?=~4 >~11 @~9 ?~10 ====>?>=>><<>>>=<<?>??>>===><<=<?>>?>===>>?~9 >?>??>>>??>>>>?>>?~4 @@???>=>==>>????>???>?>~7 ??>>>>?~9 >>?==?==>=>==>>===>>>=~18 <==<~4 ==?>~4 =>>>>=?>>>>=~14 >=>????>>>>?>=~5 >=~5 <<<<=<=~11 <=<<=<=?==>~6 ==>===>>=>~4 =>=~4 >>=>?>~6 ===>=>~7 =~4 <=~6 <<=~9 >>>=~4 >>=>>==>===>>>=~24 <=~24 >>==>==>=>==>~13 =>>><===<<====>>===<<=<<=~4 >~4 =~5 >??>=>>>=>>==<=<===<===<=>>>>===>=>====<~16 =<=<=~7 <<=<<<<=<=<=<~6 =<<<?~4 >~4 =>><<<<=~4 >><=djbioojc`rmru`^~6 aa_!!ecdffcuuwxwyxwxvvr]lr?=>>=>~4 ?>???==>~4 choio]^!Y`cb^ruvwwwxwyxXZ[][!WWYWXZYY[!`[bedhi!lnlmlllhhYWWY!!XXpgegglkZXabkeaeedffceYXa!]YY>~4 ?>?>?~4 =~7 >>>=>>>>=~6 >===>>>=~7 >>>=~4 >====<===>>>?>??>??>~7 ?>>><;<:89>>>=>=>::9:9;9<<=<=~4 <~6 ====<<====68576:8687787;;85:ED~4 @@BBB@AEECCABD>?A>@A@AA@~5 A@~8 AAA@~5 A@@A~6 @A:;;;:::;:;;:<~10 >===?~7 >=<<;<;;:~9 ;<<<<@???<<=>=<;;<;<===>===<>=~4 ?@?@>>>=~4 >=?@@????AA@===@<===?@@@?~5 @?~5 @~7 >???@??@?>>>??>>>>=>=@>>>>??>?<~11 ;<;<<>=~8 >=>=<====>=>====>>><<<>>=~8 <~4 =~6 >==?>@?===>=<=>@==@@@<<>=>><<>===?<=?=<=<=99:9~18 :9999:~24 ;:::;:::<<=<;;<;;;:;;<:9~4 ;::;:~8 ;;;::::;~7 99::=~4 <;<<=<<<=<<>==<<<;<=;<==:::;<;=<<===;<:<~5 ;<;<<<<==>???=>>=>>>====>====>~7 ====>>><=<>>?>>???>~4 ===>==>~7 99:999:::9<<:<<;<;:::<~12 ::;::;;;<;<~5 ::;;==<==<<<===<<==<>=~6 <~6 .10,54+.--+48731/0::;,0---*-/-+-//./0/0-0/0.32561-,,,,-165?=@@?9=BA,,,,<<=~5 >==@??CAACCAC===>>>D?=?>?@>=>)+*,++*'*'(&('&&#$#*+:~7 ;:~6 9~4 79<;::9;887777;~4 77867:9~4 888879~6 :::9999::9999:::9:~6 999:~12 ;~6 ====<<<=<*))&%-,++'(($$%#&%*##&#,-.---,,===;<<=<=<<<<;;:;<==<<<;=~5 ;;;:;;=<<=~5 >==<=~12 <====;<====<====<=>===>===;:<::::;;<;<<==;=~4 9:8=<<<:;<<;===<;<<=<~5 77778898698;;:;;::9:::9:;;<:<;~5 98999::::<;;::;;:;<;;<;<;<;;;;<;~5 =~13 <<=~5 <===<<<<;===<=~9 >==>=~10 8987889899887*+*777,,+---.-0,-.-,,-,((*'(&*-#(%(+(0178736546678777#1449~4 :;:;;:::30./2460/664567567665676464-455;~6 9999:::999:9~6 :~6 8:::7887788779997878::9~6 ::;;:;~5 ::;::;;9;;;;:;::;~4 ::8:9:98987878:;~5 8778<::=~4 <=<<8779;>B?B<8878896787>;=;9>=?;95Arpnnmpsqrrrnmmfghfdgfihlfkihkijfldea~4 bb`^_``a`a``_a`a`agggebbdbab9899998897~8 rrpstrtwwuuwuoonooppqporquuvvsurssvotswtklhoolggpmmmheeflkoqvwwmhhkfhirsieipddhe_`]]_]_]bc]!!]!``^_]]!]!]!!d]^]c`]^]^]]]!!]eqqgghdfahfa__gjhfaplmmii_]^ghlhfg_afdeh]^^]]!]!]]h]^]!~7 97978887::8;?><<.(,//1-'.CAA??@===::A7@7<;;;==<;<<;<~10 >>>><><=>=<=<==>==>~4 =>~6 =;==;=;=<>::;;<<;:;<;;;<;<;;::;;<<;;<;<<;<<<<::::9:;<:;;;====;;>=>~7 =<<<=;<==<<===<?=<=<<=;<<;=<;;<;;;;<=<;:::;;:;;:;<;:;:;;:~4 >=<<<>>>?>>@?~7 <=?>>=~6 <===A~5 >=>=?@<<<<?=>?><~4 >>>>?>>>>@AA?A@??>>=>~4 ===>===>>=~5 >~8 ?>?>>?>>?>??>?@?@??@??>=>>>===>>?>>>>?>?==>>==>AAA@@=~4 <===>=~9 >=~5 ;<;<;;<;<;<=<===;;;=>>>>?@@@==??>>@>=>=>=~6 >>A@@ABB?>?>?@A>===>>>=>?>;=<~4 ;<==;<;;<<;<;<<=<=<==>=>>>???>?A==>>===>~4 ==>~7 ?>??>?>~4 ??>>>>=>=>>>>?>?>>>>=>>=>==><=<>>>>=>====>==>=>>>==>?==>==>~10 <~4 ===<<;<;~13 ==<;<;<;<~8 =<:;;:::;;<;<<<<;<;~10 <<<<;<<<<:~8 ;;:;;;:;;<:;<88889988987888:~7 9:~6 88:;:;9;<<:9:;;:878::78::87~5 @??@A???AAAA?A;;;<=<====;;@@@A;@@@==>~4 @==<<=;?A>>@?@;<=;>=>=::;~8 <<;<<==;;<=:::;::9:::88::9::;:;::;988:~8 777899978:::9888787789~4 8988?@@A@AA=~7 @ABAAAA@AAA??@BB==>====>=~13 >?>@>??AAA>>==>?~4 BB?B====;:;;:::9:::;:=~12 <~8 ====<~10 ===<<<<>=~7 ;=<<<<=;==<<;==>===;=<<====<><==>><>>=>~4 ==<<>=>><==>=><<>~6 =>>>====>==>>===>>>>==>=><~10 ==<<===<<<====>===>>>=~6 >==>>??=>=?===>==>=><=<==<<==<<<<=<=<==<>>@?@@?@?@@@???>~5 =>=>~13 =>=>>>=>~5 CBB@@>@?@>>?>~11 =~4 ::;9:::===:<<<:;;>=~6 <=<<===;<=<=<====<<====<<==AAAA@caadc_ae]^^`outuqtwpv!]!!``![!ilcllc>~4 yxwxuyyywyywy>>a`XYZ[Y^_]_]a^c[[Ydbdca```acbb^`__Y[W!]fffed]!]!!XZfe][^[![Z[!!!![[[!aZcXXXZffeec`hcYXce`eednqmmfeYYkienoilkYWWYW!!bdqrmlZfj777a_^`_^]]]]^!]![!!!]_!]^``]]]^A~4 BBA]!]~4 ::99;;:89;<;<;<;9::]!!]]!!!!?D?@??:;;::;<9;9:;~5 :;;:>>=>=~7 >>>====>===<===???>>===A>>>>=>=~13 >~7 ==>=<=?><<<=<<<==>??<?==<<?@@=??<=<<==<<=~7 <~4 ;<<<==<<==<<=<<====<~12 =<~10 =<<;<~6 >=<==>===<~11 =<~11 =<~4 ==<=<~4 ===<~5 =<~5 =<=~7 <=<<=<~6 =~6 <===<~5 ;~5 <<;~5 <;;:::;;;::;:;::;:;::;~4 :;:~5 ;;<;<~4 =~8 <===<>=>==>=>><<;<===<=<==<=????>AA@AAA@A??>>??=>>?A:A:A:~8 <=;<;;;:;;A;::B:BBA::::???=<<>?>===<<<<==;<=<====>>>;;:<;;>==<=<;~5 ?@A???@@@ABA??=>=>@@B?>>>>==>?>==>==>>=><<>=>;;<=::;;:~4 ;~9 <<=<;~9 <;<<<;~4 <~4 ;<<;<;~12 ::;~9 <~8 ===::;;;<:<~4 ;<~4 ;;:;:::9;:~4 9::9998:9988;~10 9;;;:;;;;::;;;;:;;;:;;:9::;::8=~5 <~4 =<=<<<===<~5 ===<~6 ;<====<====<<<<=~9 >=<~4 ==<<=~5 <====;<<==<;<~5 ==<==<?@?>>===??==@~5 ?@?;=<~5 ==<<==;<<<==>=>=~4 >===>===>>>=>?=?>~4 ===>>>>====<~4 ===<<;<>>?=~5 ??==?=<<;;;;::899:889:~4 ;<;<;<;:;;;;RRSRSTSQQSSTSSPQQQRQPPRTRBCDBDBDDFCDBDBBD??@DCCC==-1<=CD1484:0<?A?C-BD;===:../0=<~14 =<>==>>=<====<=<==<~6 ==>><@>?@DD<?@B?DBD~4 EEGEGDDED?=DHGFDEEFEGHHHGGGFHHHEH<3<<<72>~4 =>>>>====>~4 ====>~5 ====>><=>~5 <<====1052==>=~7 >~6 @A@@@?@@?A??>A@AAA@>>>>?>>@~4 ??>@???@@@??@~5 ??@>~13 =>>===>~5 ?>>?>==>>>>?>~6 ?>>=~4 >=>=~8 ??>>>==???==>~9 ?>=~4 >>>??>><<=<<<>=>>=~4 <~8 ====>==<====><>>>>==>>=>=><=<~8 ?>?>>=>=>=>~16 ?~9 ===>>>>=>====<<=<>===<<<=<=????>~16 ?>>>D~4 CD>~9 D~8 B@A@A??CD?>?>>>?>>DDCD??A???>??>?~4 >>>>?>???>~11 =>~14 ?~12 >>=>?=?==>>==>>?>>>EDEDDD>~6 ===>>=>>>>====<<<<==<<<<==DCDFEDGFGDFGCEBAC?@@CB@??@??@?>>>???A??<=<===<<===<~5 =<~5 =<<>~7 A~10 B~11 AAB~9 AAA@AAA@?@@@?BA~5 BBBCCBB?>>@?>~10 ??><<==<<;:;::;<;;:;;;:;;:~5 ;=:;<~9 ====<=;<<<<=<<=<~7 ;~20 <~9 ;<~16 =<<<==<~11 ;<~19 ===<<=<<<<=<==<<=<=<<=<=<<==<<<=<~10 =~18 <=<=~4 <===<=<<<==<~11 =>>=;:;==>==>>=~6 >>?>>>???>~4 ?>?>?>=?~5 =>???>>??<<<=>><=<>=><~4 =<=~6 <~4 =<<=;;==>>>>=~4 >>????>?>=>~6 <<=~8 >==>>==?>@~4 A>??>@?>?@BB>>??AA@AB@@A@??>?@@===>=~4 >~4 =~6 <<<<=~6 BBCCBCCB~4 A~6 @ABAA@AAA@A@@@@>~5 ?>>????>????>?>>>??>=><=>?;;;<<=<=>>??;<==<<<<=<~14 ;<;<<<;;<<;;;;<;;<~4 ;~4 ?>><<=>=G~15 C~4 DECBDDBEEDDCDCCCBBBBCBBDCC@@@BB@@?>?>@??=>=?>?>@?@=?<?>?<<??<;<?>>?=:>=>>====>===87;A9;>>;<~6 >>==<===:~5 @:~7 @@A::::<=~25 >>;<=;:~4 ??>??=>=:<??=::=@=;=<;<<;;<~8 ::<=~6 <@?>==<>>>?@?>=~4 ;=;<<<;<;;;<<<??@~8 A@?@~19 =~5 <><==>=@>>><=>~11 =<=>>AAA@><<<<A@ABB;;<<;<?@=@@@<<???=??>=??>??>??@@=>~4 ?>BBB<=;;;;B;@>=>~4 ==>>=~12 ;=<====<:;<;<::<<==>~5 :~5 <;<;;;;<>>>I>><;<==>;;<;;<<=>==><<<:~4 ;99:99:<;<;<;~11 <;<<<;<;;:;;;<<<::<???<<==><<<==><@???@>=<~6 @@<<<<@>>==<>=>==<~25 >~10 <<=<<>=>>>=~5 <=>===<==>~5 ====<<<<=~4 <~5 =<=<===<=<=<=<;~9 =>>==><<=;;;=>;<<??=>=>?=><<<<;=887788878~4 7~4 88777999:99889988989899998999::99:~6 ;;::;::;<<<;~7 <;::::9998878898:9:87778787778<<<<;<;<;;:;;;:;;:;;;:~4 99::::8::99::999::899897899:788:9:9<~7 ;<<<;<:;<~6 ;;;:::9999;:;8:9<<==<===<<<=<<=~4 <==<<<<=<=<<<;;<<===<<<<===;<;=<;;:;;;;:;<::;89::8:::8:::8787:::79:9:99989:9:;:;;;:;;<;;;;<~7 ;<99;;:">
647    *   </jvxlColorData>
648    * </jvxlSurfaceData>
649    *
650    *
651    **********************************************************/
652 
appendXmlVertexOnlyData(SB sb, JvxlData jvxlData, MeshData meshData, boolean escapeXml)653   private static void appendXmlVertexOnlyData(SB sb,
654                                         JvxlData jvxlData, MeshData meshData, boolean escapeXml) {
655     int[] vertexIdNew = new int[meshData.vc];
656     if (appendXmlTriangleData(sb, meshData.pis,
657         meshData.pc, meshData.bsSlabDisplay,
658         vertexIdNew, escapeXml))
659       appendXmlVertexData(sb, jvxlData, vertexIdNew,
660           meshData.vs, meshData.vvs, meshData.vc,
661           meshData.polygonColorData, meshData.pc,
662           meshData.bsSlabDisplay,
663           jvxlData.vertexColors,
664           jvxlData.jvxlColorData.length() > 0, escapeXml);
665   }
666 
667   /**
668    * encode triangle data -- [ia ib ic]  [ia ib ic]  [ia ib ic] ...
669    * algorithm written by Bob Hanson, 11/2008. The principle is that
670    * not all vertices may be represented -- we only need the
671    * used vertices here. Capitalizing on the fact that triangle sets
672    * tend to have common edges and similar numbers for sequential triangles.
673    *
674    * a) Renumbering vertices as they appear in the triangle set
675    *
676    *    [2456 2457 2458] [2456 2459 2458]
677    *
678    *   becomes
679    *
680    *    [   1    2    3] [   1    4    3]
681    *
682    * b) This allows efficient encoding of differences, not absolute numbers.
683    *
684    *        0    1    2     -2    3   -1
685    *
686    * c) Which can then be represented often using a single ASCII character.
687    *    I chose \ to be 0, and replace that with !.
688    *
689    *    ASCII:
690    *    -30       -20       -10         0       +10       +20       +30
691    *    <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
692    *
693    *    So the above sequence would simply be:
694    *
695    *      !]^Z_[
696    *
697    *    When the range falls outside of +/-32, we simply use a number.
698    *    When a positive number follows another number, we add a "+" to it.
699    *
700    *      !]^Z_[-33+250]230-210]]
701    *
702    *    Preliminary trials indicated that on average a triangle
703    *    can be encoded in about 7 bytes, or roughly half the 12 bytes
704    *    necessary for standard binary encoding of integers. The advantage
705    *    here is that we have an ASCII-readable file and no little-/big-endian issue.
706    *
707    * @param sb
708    * @param triangles
709    * @param nData
710    * @param bsSlabDisplay
711    * @param vertexIdNew
712    * @param escapeXml
713    * @return (triangles are present)
714    */
appendXmlTriangleData(SB sb, int[][] triangles, int nData, BS bsSlabDisplay, int[] vertexIdNew, boolean escapeXml)715   private static boolean appendXmlTriangleData(SB sb, int[][] triangles, int nData,
716                                               BS bsSlabDisplay,
717                                               int[] vertexIdNew, boolean escapeXml) {
718     SB list1 = new SB();
719     SB list2 = new SB();
720     int ilast = 1;
721     int p = 0;
722     int inew = 0;
723     boolean addPlus = false;
724     int nTri = 0;
725 
726     // note that the slabbing present becomes irreversible if there is no ghosting.
727     boolean removeSlabbed = (bsSlabDisplay != null);
728 
729     for (int i = 0; i < nData;) {
730       if (triangles[i] == null || (removeSlabbed && !bsSlabDisplay.get(i))) {
731         i++;
732         continue;
733       }
734       int idata = triangles[i][p];
735       if (vertexIdNew[idata] > 0) {
736         idata = vertexIdNew[idata];
737       } else {
738         idata = vertexIdNew[idata] = ++inew;
739       }
740       int diff = idata - ilast;
741       ilast = idata;
742       if (diff == 0) {
743         list1.appendC('!');
744         addPlus = false;
745       } else if (diff > 32) {
746         if (addPlus)
747           list1.appendC('+');
748         list1.appendI(diff);
749         addPlus = true;
750       } else if (diff < -32) {
751         list1.appendI(diff);
752         addPlus = true;
753       } else {
754         list1.appendC((char) ('\\' + diff));
755         addPlus = false;
756       }
757       if (++p % 3 == 0) {
758         list2.appendI(triangles[i][3]);
759         p = 0;
760         i++;
761         nTri++;
762       }
763     }
764     if (list1.length() == 0)
765       return true;
766     XmlUtil.appendTagObj(sb, "jvxlTriangleData", new String[] {
767         "count", "" + nTri,
768         "encoding", "jvxltdiff",
769         "data" , jvxlCompressString(list1.toString(), escapeXml),
770         }, null);
771     XmlUtil.appendTagObj(sb, "jvxlTriangleEdgeData", new String[] { // Jmol 12.1.50
772         "count", "" + nTri,
773         "encoding", "jvxlsc",
774         "data" , jvxlCompressString(list2.toString(), escapeXml) }, null);
775     return true;
776   }
777 
778   /**
779    * encode the vertex data. This must be done AFTER encoding the triangles,
780    * because the triangles redefine the order of vertices.
781    *
782    * Bob Hanson 11/2008
783    *
784    * If another program has created the triangles, we probably do not know the
785    * grid that was used for Marching Cubes, or quite possibly no grid was used.
786    * In that case, we just save the vertex/triangle/value data in a compact
787    * form.
788    *
789    * For the we use an extension of the way edge points are encoded. We simply
790    * identify the minimum and maximum x, y, and z coordinates and then express
791    * the point as a fraction along each of those directions. Thus, the x, y, and
792    * z coordinate are within the interval [0,1].
793    *
794    * We opt for the two-byte double-precision JVXL character compression. This
795    * allows a 1 part in 8100 resolution, which is plenty for these purposes.
796    *
797    * The tag will indicate the minimum and maximum values:
798    *
799    * <jvxlVertexData count="150" min="(15.218472, -28.304049, 34.71112)"
800    * max="(97.8228, 54.011948, 109.95208)" data="...."> </jvxlVertexData>
801    *
802    * The resultant string is really two strings of length nData where the first
803    * string lists the "high" part of the positions, and the second string lists
804    * the "low" part of the positions.
805    *
806    * @param sb
807    * @param jvxlData
808    * @param vertexIdNew
809    * @param vertices
810    * @param vertexValues
811    * @param vertexCount
812    * @param polygonColorData
813    * @param polygonCount
814    * @param bsSlabDisplay
815    * @param vertexColors
816    * @param addColorData
817    * @param escapeXml
818    */
appendXmlVertexData(SB sb, JvxlData jvxlData, int[] vertexIdNew, T3[] vertices, float[] vertexValues, int vertexCount, String polygonColorData, int polygonCount, BS bsSlabDisplay, int[] vertexColors, boolean addColorData, boolean escapeXml)819   private static void appendXmlVertexData(SB sb, JvxlData jvxlData,
820                                           int[] vertexIdNew, T3[] vertices,
821                                           float[] vertexValues,
822                                           int vertexCount,
823                                           String polygonColorData,
824                                           int polygonCount, BS bsSlabDisplay,
825                                           int[] vertexColors,
826                                           boolean addColorData,
827                                           boolean escapeXml) {
828     int colorFractionBase = jvxlData.colorFractionBase;
829     int colorFractionRange = jvxlData.colorFractionRange;
830     T3 p;
831     P3 min = jvxlData.boundingBox[0];
832     P3 max = jvxlData.boundingBox[1];
833     SB list1 = new SB();
834     SB list2 = new SB();
835     int[] vertexIdOld = null;
836     boolean removeSlabbed = (bsSlabDisplay != null);
837     if (polygonCount > 0) {
838       if (removeSlabbed)
839         polygonCount = bsSlabDisplay.cardinality();
840       removeSlabbed = false;
841       vertexIdOld = new int[vertexCount];
842       for (int i = 0; i < vertexCount; i++)
843         if (vertexIdNew[i] > 0) // not all vertices may be in triangle -- that's OK
844           vertexIdOld[vertexIdNew[i] - 1] = i;
845     }
846     int n = 0;
847     for (int i = 0; i < vertexCount; i++)
848       if (!removeSlabbed || bsSlabDisplay.get(i)) {
849         n++;
850         p = vertices[(polygonCount == 0 ? i : vertexIdOld[i])];
851         jvxlAppendCharacter2(p.x, min.x, max.x, colorFractionBase,
852             colorFractionRange, list1, list2);
853         jvxlAppendCharacter2(p.y, min.y, max.y, colorFractionBase,
854             colorFractionRange, list1, list2);
855         jvxlAppendCharacter2(p.z, min.z, max.z, colorFractionBase,
856             colorFractionRange, list1, list2);
857       }
858     list1.appendSB(list2);
859     XmlUtil.appendTagObj(sb, "jvxlVertexData", new String[] { "count", "" + n,
860         "min", Escape.eP(min), "max", Escape.eP(max), "encoding", "base90xyz2",
861         "data", jvxlCompressString(list1.toString(), escapeXml), }, null);
862     if (polygonColorData != null)
863       XmlUtil.appendTagObj(sb, "jvxlPolygonColorData", new String[] {
864           "encoding", "jvxlnc", "count", "" + polygonCount }, "\n"
865           + polygonColorData);
866     if (!addColorData)
867       return;
868 
869     // now add the color data, again as a double-precision value.
870 
871     list1 = new SB();
872     list2 = new SB();
873     if (vertexColors == null) {
874       for (int i = 0; i < vertexCount; i++)
875         if (!removeSlabbed || bsSlabDisplay.get(i)) {
876           float value = vertexValues[polygonCount == 0 ? i : vertexIdOld[i]];
877           jvxlAppendCharacter2(value, jvxlData.mappedDataMin,
878               jvxlData.mappedDataMax, colorFractionBase, colorFractionRange,
879               list1, list2);
880         }
881     } else {
882       int lastColor = 0;
883       list1.appendI(n).append(" ");
884       for (int i = 0; i < vertexCount; i++)
885         if (!removeSlabbed || bsSlabDisplay.get(i)) {
886           int c = vertexColors[polygonCount == 0 ? i : vertexIdOld[i]];
887           if (c == lastColor)
888             c = 0;
889           else
890             lastColor = c;
891           list1.appendI(c);
892           list1.append(" ");
893         }
894     }
895     appendXmlColorData(sb, list1.appendSB(list2).append("\n")
896         .toString(), (vertexColors == null), true, jvxlData.valueMappedToRed,
897         jvxlData.valueMappedToBlue);
898   }
899 
900   ////////// character - fraction encoding and decoding
901 
902   // NEVER change the numbers for these next defaults
903 
904   final public static int defaultEdgeFractionBase = 35; //#$%.......
905   final public static int defaultEdgeFractionRange = 90;
906   final public static int defaultColorFractionBase = 35;
907   final public static int defaultColorFractionRange = 90;
908 
909   /* character-encoding of factions in base 90:
910    *
911    * characters ASC(35) - ASC(124) are used for this encoding with the
912    * exception of ASC(92)'\\', which is encoded as ASC(33)'!'.
913    * ASC(125)'}' is reserved for "NaN".
914    * Double-quote is not in this range, but '<' and '>' are, so this
915    * is only XML-safe when quoted as an attribute.
916    *
917    */
jvxlFractionAsCharacter(float fraction)918   public static char jvxlFractionAsCharacter(float fraction) {
919     return jvxlFractionAsCharacterRange(fraction, defaultEdgeFractionBase, defaultEdgeFractionRange);
920   }
921 
jvxlFractionAsCharacterRange(float fraction, int base, int range)922   public static char jvxlFractionAsCharacterRange(float fraction, int base, int range) {
923     if (fraction > 0.9999f)
924       fraction = 0.9999f;
925     else if (Float.isNaN(fraction))
926       fraction = 1.0001f;
927     int ich = (int) Math.floor(fraction * range + base);
928     if (ich < base)
929       return (char) base;
930     if (ich == 92)
931       return '!'; // \ --> !
932     //if (logCompression)
933     //Logger.info("fac: " + fraction + " --> " + ich + " " + (char) ich);
934     return (char) ich;
935   }
936 
jvxlAppendCharacter2(float value, float min, float max, int base, int range, SB list1, SB list2)937   private static void jvxlAppendCharacter2(float value, float min, float max,
938                                            int base, int range,
939                                            SB list1,
940                                            SB list2) {
941     float fraction = (min == max ? value : (value - min) / (max - min));
942     char ch1 = jvxlFractionAsCharacterRange(fraction, base, range);
943     list1.appendC(ch1);
944     fraction -= jvxlFractionFromCharacter(ch1, base, range, 0);
945     list2.appendC(jvxlFractionAsCharacterRange(fraction * range, base, range));
946   }
947 
jvxlFractionFromCharacter(int ich, int base, int range, float fracOffset)948   public static float jvxlFractionFromCharacter(int ich, int base, int range,
949                                                 float fracOffset) {
950     if (ich == base + range)
951       return Float.NaN;
952     if (ich < base)
953       ich = 92; // ! --> \
954     float fraction = (ich - base + fracOffset) / range;
955     if (fraction < 0f)
956       return 0f;
957     if (fraction > 1f)
958       return 0.999999f;
959     //System.out.println("ffc: " + fraction + " <-- " + ich + " " + (char)
960     // ich);
961     return fraction;
962   }
963 
jvxlFractionFromCharacter2(int ich1, int ich2, int base, int range)964   public static float jvxlFractionFromCharacter2(int ich1, int ich2, int base,
965                                           int range) {
966     float fraction = jvxlFractionFromCharacter(ich1, base, range, 0);
967     float remains = jvxlFractionFromCharacter(ich2, base, range, 0.5f);
968     return fraction + remains / range;
969   }
970 
jvxlValueAsCharacter(float value, float min, float max, int base, int range)971   public static char jvxlValueAsCharacter(float value, float min, float max, int base,
972                                    int range) {
973     float fraction = (min == max ? value : (value - min) / (max - min));
974     return jvxlFractionAsCharacterRange(fraction, base, range);
975   }
976 
jvxlValueFromCharacter2(int ich, int ich2, float min, float max, int base, int range)977   protected static float jvxlValueFromCharacter2(int ich, int ich2, float min,
978                                                  float max, int base, int range) {
979     float fraction = jvxlFractionFromCharacter2(ich, ich2, base, range);
980     return (max == min ? fraction : min + fraction * (max - min));
981   }
982 
983   // /// differential bitset encoding and decoding (Bob Hanson hansonr@stolaf.edu for Jmol)
984 
jvxlEncodeBitSet0(BS bs, int nPoints, SB sb)985   public static int jvxlEncodeBitSet0(BS bs, int nPoints, SB sb) {
986     // nunset nset nunset ...
987     // for repeated numbers:
988     // 3 3 3 3 3 3 3 becomes 3 -6
989     int dataCount = 0;
990     int prevCount = -1;
991     int nPrev = 0;
992     if (nPoints < 0)
993       nPoints = bs.length();
994     int n = 0;
995     boolean isset = false;
996     int lastPoint = nPoints - 1;
997 
998     for (int i = 0; i < nPoints; ++i) {
999       if (isset == bs.get(i)) {
1000         dataCount++;
1001       } else {
1002         if (dataCount == prevCount && i != lastPoint) {
1003           nPrev++;
1004         } else {
1005           if (nPrev > 0) {
1006             sb.appendC(' ').appendI(-nPrev);
1007             nPrev = 0;
1008             n++;
1009           }
1010           sb.appendC(' ').appendI(dataCount);
1011           n++;
1012           prevCount = dataCount;
1013         }
1014         dataCount = 1;
1015         isset = !isset;
1016       }
1017     }
1018     sb.appendC(' ').appendI(dataCount).appendC('\n');
1019     return n;
1020   }
1021 
jvxlEncodeBitSet(BS bs)1022   public static String jvxlEncodeBitSet(BS bs) {
1023     SB sb = new SB();
1024     jvxlEncodeBitSetBuffer(bs, -1, sb);
1025     return sb.toString();
1026   }
1027 
jvxlEncodeBitSetBuffer(BS bs, int nPoints, SB sb)1028   public static int jvxlEncodeBitSetBuffer(BS bs, int nPoints, SB sb) {
1029     //System.out.println("jvxlcoder " + Escape.escape(bs));
1030     int dataCount = 0;
1031     int n = 0;
1032     boolean isset = false;
1033     if (nPoints < 0)
1034       nPoints = bs.length();
1035     if (nPoints == 0)
1036       return 0;
1037     sb.append("-");
1038     for (int i = 0; i < nPoints; ++i) {
1039       if (isset == bs.get(i)) {
1040         dataCount++;
1041       } else {
1042          jvxlAppendEncodedNumber(sb, dataCount, defaultEdgeFractionBase, defaultEdgeFractionRange);
1043         n++;
1044         dataCount = 1;
1045         isset = !isset;
1046       }
1047     }
1048     jvxlAppendEncodedNumber(sb, dataCount, defaultEdgeFractionBase, defaultEdgeFractionRange);
1049     sb.appendC('\n');
1050     return n;
1051   }
1052 
jvxlAppendEncodedNumber(SB sb, int n, int base, int range)1053   public static void jvxlAppendEncodedNumber(SB sb, int n, int base, int range) {
1054     boolean isInRange = (n < range);
1055     if (n == 0)
1056       sb.appendC((char) base);
1057     else if (!isInRange)
1058       sb.appendC((char)(base + range));
1059     while (n > 0) {
1060       int n1 = n / range;
1061       int x = base + n - n1 * range;
1062       if (x == 92)
1063         x = 33;  // \ --> !
1064       sb.appendC((char) x);
1065       n = n1;
1066     }
1067     if (!isInRange)
1068       sb.append(" ");
1069   }
1070 
jvxlDecodeBitSetRange(String data, int base, int range)1071   public static BS jvxlDecodeBitSetRange(String data, int base, int range) {
1072     BS bs = new BS();
1073     int dataCount = 0;
1074     int ptr = 0;
1075     boolean isset = false;
1076     int[] next = new int[1];
1077     while ((dataCount = jvxlParseEncodedInt(data, base, range, next)) != Integer.MIN_VALUE) {
1078       if (isset)
1079         bs.setBits(ptr, ptr + dataCount);
1080       ptr += dataCount;
1081       isset = !isset;
1082     }
1083     return bs;
1084   }
1085 
jvxlParseEncodedInt(String str, int offset, int base, int[] next)1086   public static int jvxlParseEncodedInt(String str, int offset, int base, int[] next) {
1087     boolean digitSeen = false;
1088     int value = 0;
1089     int ich = next[0];
1090     int ichMax = str.length();
1091     if (ich < 0)
1092       return Integer.MIN_VALUE;
1093     while (ich < ichMax && PT.isWhitespace(str.charAt(ich)))
1094       ++ich;
1095     if (ich >= ichMax)
1096       return Integer.MIN_VALUE;
1097     int factor = 1;
1098     boolean isLong = (str.charAt(ich) == (offset + base));
1099     if (isLong)
1100       ich++;
1101     while (ich < ichMax && !PT.isWhitespace(str.charAt(ich))) {
1102       int i = str.charAt(ich);
1103       if (i < offset)
1104         i = 92;   // ! --> \
1105       value += (i - offset) * factor;
1106       digitSeen = true;
1107       ++ich;
1108       if (!isLong)
1109         break;
1110       factor *= base;
1111     }
1112     if (!digitSeen)
1113       value = Integer.MIN_VALUE;
1114     next[0] = ich;
1115     return value;
1116   }
1117 
jvxlDecodeBitSet(String data)1118   public static BS jvxlDecodeBitSet(String data) {
1119     if (data.startsWith("-"))
1120       return jvxlDecodeBitSetRange(jvxlDecompressString(data.substring(1)), defaultEdgeFractionBase, defaultEdgeFractionRange);
1121     // nunset nset nunset ...
1122     BS bs = new BS();
1123     int dataCount = 0;
1124     int lastCount = 0;
1125     int nPrev = 0;
1126     int ptr = 0;
1127     boolean isset = false;
1128     int[] next = new int[1];
1129     while (true) {
1130       dataCount = (nPrev++ < 0 ? dataCount : PT.parseIntNext(data, next));
1131       if (dataCount == Integer.MIN_VALUE)
1132         break;
1133       if (dataCount < 0) {
1134         nPrev = dataCount;
1135         dataCount = lastCount;
1136         continue;
1137       }
1138       if (isset)
1139         bs.setBits(ptr, ptr + dataCount);
1140       ptr += dataCount;
1141       lastCount = dataCount;
1142       isset = !isset;
1143     }
1144     return bs;
1145   }
1146 
1147   /////// string data compression/decompression
1148 
jvxlCompressString(String data, boolean escapeXml)1149   public static String jvxlCompressString(String data, boolean escapeXml) {
1150 
1151 
1152     /* just a simple compression, but allows 2000-6000:1 CUBE:JVXL for planes!
1153      *
1154      *   "X~nnn " means "nnn copies of character X"
1155      *
1156      *   ########## becomes "#~10 "
1157      *
1158      *   ~ is not encoded, as it is ASC(126), outside the range of 33--125.
1159      *
1160      *   for escaping XML, we also do:
1161      *
1162      *   < becomes "~;0 "
1163      *   & becomes "~%0 "
1164      *
1165      *   and repeats of those become:
1166      *
1167      *   "~;nnn "
1168      *   "~%nnn "
1169      *
1170      */
1171     if (data.indexOf("~") >= 0)
1172       return data;
1173     SB dataOut = new SB();
1174     char chLast = '\0';
1175     boolean escaped = false;
1176     boolean lastEscaped = false;
1177     int nLast = 0;
1178     int n = data.length();
1179     for (int i = 0; i <= n; i++) {
1180       char ch = (i == n ? '\0' : data.charAt(i));
1181       switch (ch) {
1182       case '\n':
1183       case '\r':
1184         continue;
1185       case '&':
1186       case '<':
1187         escaped = escapeXml;
1188         break;
1189       default:
1190         escaped = false;
1191       }
1192       if (ch == chLast) {
1193         ++nLast;
1194         ch = '\0';
1195       } else if (nLast > 0 || lastEscaped) {
1196         if (nLast < 4 && !lastEscaped || chLast == ' '
1197             || chLast == '\t') {
1198           while (--nLast >= 0)
1199             dataOut.appendC(chLast);
1200         } else {
1201           if (lastEscaped)
1202             lastEscaped = false;
1203           else
1204             dataOut.appendC('~');
1205           dataOut.appendI(nLast);
1206           dataOut.appendC(' ');
1207         }
1208         nLast = 0;
1209       }
1210       if (ch != '\0') {
1211         if (escaped) {
1212           lastEscaped = true;
1213           escaped = false;
1214           dataOut.appendC('~');
1215           chLast = ch;
1216           --ch;
1217         } else {
1218           chLast = ch;
1219         }
1220         dataOut.appendC(ch);
1221       }
1222     }
1223 
1224     return dataOut.toString();
1225   }
1226 
jvxlDecompressString(String data)1227   public static String jvxlDecompressString(String data) {
1228     if (data.indexOf("~") < 0)
1229       return data;
1230     SB dataOut = new SB();
1231     char chLast = '\0';
1232     int[] next = new int[1];
1233     for (int i = 0; i < data.length(); i++) {
1234       char ch = data.charAt(i);
1235       if (ch == '~') {
1236         next[0] = ++i;
1237         switch (ch = data.charAt(i)) {
1238         case ';':
1239         case '%':
1240           next[0]++;
1241           dataOut.appendC(chLast = ++ch);
1242           //$FALL-THROUGH$
1243         case '1':
1244         case '2':
1245         case '3':
1246         case '4':
1247         case '5':
1248         case '6':
1249         case '7':
1250         case '8':
1251         case '9':
1252           int nChar = PT.parseIntNext(data, next);
1253           for (int c = 0; c < nChar; c++)
1254             dataOut.appendC(chLast);
1255           i = next[0];
1256           continue;
1257         case '~':
1258           --i;
1259           break;
1260         default:
1261           Logger.error("Error uncompressing string " + data.substring(0, i) + "?");
1262         }
1263       }
1264       dataOut.appendC(ch);
1265       chLast = ch;
1266     }
1267     return dataOut.toString();
1268   }
1269 
1270   // VERSION 1 methods -- deprecated but still available through Jmol 11.9.18
1271 
jvxlCreateHeaderWithoutTitleOrAtoms(VolumeData v, SB bs)1272   public static void jvxlCreateHeaderWithoutTitleOrAtoms(VolumeData v, SB bs) {
1273     jvxlCreateHeader(v, bs);
1274   }
1275 
1276   /**
1277    * Creates a two-line header for the XJVXL file. It is no longer necessary
1278    * to create the atom set or generate the vectors here. Please leave the
1279    * commented code for posterity.
1280    *
1281    * @param v
1282    * @param sb
1283    */
jvxlCreateHeader(VolumeData v, SB sb)1284   public static void jvxlCreateHeader(VolumeData v, SB sb) {
1285     // if the StringXBuilder comes in non-empty, it should have two lines
1286     // that do not start with # already present.
1287     v.setVolumetricXml();
1288     if (sb.length() == 0)
1289       sb.append("Line 1\nLine 2\n");
1290     /* no longer necessary
1291     sb.append(nAtoms == Integer.MIN_VALUE ? "+2"
1292         : nAtoms == Integer.MAX_VALUE ? "-2" : "" + (-nAtoms))
1293       .append(' ')
1294       .append(v.volumetricOrigin.x).append(' ')
1295       .append(v.volumetricOrigin.y).append(' ')
1296       .append(v.volumetricOrigin.z).append(" ANGSTROMS\n");
1297     for (int i = 0; i < 3; i++)
1298       sb.append(v.voxelCounts[i]).append(' ')
1299         .append(v.volumetricVectors[i].x).append(' ')
1300         .append(v.volumetricVectors[i].y).append(' ')
1301         .append(v.volumetricVectors[i].z).append('\n');
1302     if (nAtoms != Integer.MAX_VALUE && nAtoms != Integer.MIN_VALUE) {
1303       nAtoms = Math.abs(nAtoms);
1304       for (int i = 0, n = 0; i < nAtoms; i++)
1305         sb.append((n = Math.abs(atomNo[i])) + " " + n + ".0 "
1306             + atomXyz[i].x + " " + atomXyz[i].y + " " + atomXyz[i].z + "\n");
1307       return;
1308     }
1309     Point3f pt = Point3f.new3(v.volumetricOrigin);
1310     sb.append("1 1.0 ").append(pt.x).append(' ').append(pt.y).append(' ')
1311         .append(pt.z).append(" //BOGUS H ATOM ADDED FOR JVXL FORMAT\n");
1312     for (int i = 0; i < 3; i++)
1313       pt.scaleAdd(v.voxelCounts[i] - 1, v.volumetricVectors[i], pt);
1314     sb.append("2 2.0 ").append(pt.x).append(' ').append(pt.y).append(' ')
1315         .append(pt.z).append(" //BOGUS He ATOM ADDED FOR JVXL FORMAT\n");
1316     */
1317   }
1318 
1319   /*
1320   private static String jvxlGetFileVersion1(JvxlData jvxlData,
1321                                             MeshData meshData, String[] title,
1322                                             String msg, boolean includeHeader,
1323                                             int nSurfaces, String state,
1324                                             String comment) {
1325     // pre-XML
1326     if ("TRAILERONLY".equals(msg))
1327       return "";
1328     StringXBuilder data = new StringXBuilder();
1329     if (includeHeader) {
1330       String s = jvxlData.jvxlFileHeader
1331           + (nSurfaces > 0 ? -nSurfaces : -1) +" " + jvxlData.edgeFractionBase + " "
1332           + jvxlData.edgeFractionRange + " " + jvxlData.colorFractionBase + " "
1333           + jvxlData.colorFractionRange + " Jmol voxel format version " +  JVXL_VERSION1 + "\n";
1334       if (s.indexOf("#JVXL") != 0)
1335         data.append("#JVXL").append(jvxlData.isXLowToHigh ? "+" : "").append(
1336             " VERSION ").append(JVXL_VERSION1).append("\n");
1337       data.append(s);
1338     }
1339     if ("HEADERONLY".equals(msg))
1340       return data.toString();
1341     data.append("# ").append(msg).append('\n');
1342     if (title != null)
1343       for (int i = 0; i < title.length; i++)
1344         data.append("# ").append(title[i]).append('\n');
1345     state = (state == null ? "" : " rendering:" + state);
1346     String definitionLine = jvxlGetDefinitionLineVersion1(jvxlData);
1347     data.append(definitionLine).append(state).append('\n');
1348     StringXBuilder sb = new StringXBuilder();
1349     String colorData = (jvxlData.jvxlColorData == null ? "" : jvxlData.jvxlColorData);
1350     // if (jvxlData.vertexDataOnly) {  // see XML version
1351     //  sb.append("<jvxlSurfaceData>\n");
1352     //  jvxlAppendMeshXml(sb, jvxlData, meshData, false);
1353     //  sb.append("</jvxlSurfaceData>\n");
1354     //} else
1355     if (jvxlData.jvxlPlane == null) {
1356       if (jvxlData.jvxlEdgeData == null)
1357         return "";
1358       //no real point in compressing this unless it's a sign-based coloring
1359       sb.append(jvxlData.jvxlSurfaceData);
1360       sb.append(jvxlCompressString(jvxlData.jvxlEdgeData, false)).append('\n').append(
1361           jvxlCompressString(colorData, false)).append('\n');
1362     } else if (colorData != null) {
1363       sb.append(jvxlCompressString(colorData, false)).append('\n');
1364     }
1365     int len = sb.length();
1366     data.append(sb);
1367     if (includeHeader) {
1368       if (msg != null && !jvxlData.vertexDataOnly)
1369         data.append("#-------end of jvxl file data-------\n");
1370       data.append(jvxlGetInfo(jvxlData, false)).append('\n');
1371         jvxlAppendCommandState(data, comment, state, false);
1372       if (includeHeader)
1373         XmlUtil.appendTag(data, "jvxlFileTitle", null, null, jvxlData.jvxlFileTitle, false, true);
1374     }
1375     return jvxlSetCompressionRatio(data, jvxlData, len);
1376   }
1377 
1378   private static String jvxlGetDefinitionLineVersion1(JvxlData jvxlData) {
1379     String definitionLine =
1380     //(jvxlData.vContours == null ? ""  : "#+contourlines\n")+
1381        jvxlData.cutoff + " ";
1382 
1383     //  optional comment line for compatibility with earlier Jmol versions:
1384     //  #+contourlines (no longer used -- see XML version)
1385     //  cutoff       nInts     (+/-)bytesEdgeData (+/-)bytesColorData
1386     //               param1              param2         param3
1387     //                 |                   |              |
1388     //   when          |                   |        >  0 ==> jvxlDataIsColorMapped
1389     //   when          |                   |       == -1 ==> not color mapped
1390     //   when          |                   |        < -1 ==> jvxlDataIsPrecisionColor
1391     //   when        == -1     &&   == -1 ==> noncontoured plane
1392     //   when        == -1     &&   == -2 ==> contourable plane
1393     //   when        < -1*     &&    >  0 ==> contourable functionXY
1394     //   when        > 0       &&    <  0 ==> jvxlDataisBicolorMap
1395 
1396     //  nInts saved as -1 - nInts
1397 
1398     if (jvxlData.jvxlSurfaceData == null)
1399       return "";
1400     int nSurfaceInts = jvxlData.nSurfaceInts;// jvxlData.jvxlSurfaceData.length();
1401     int bytesUncompressedEdgeData = (jvxlData.vertexDataOnly ? 0
1402         : jvxlData.jvxlEdgeData.length() - 1);
1403     int nColorData = (jvxlData.jvxlColorData == null ? -1 : (jvxlData.jvxlColorData.length() - 1));
1404     if (jvxlData.jvxlPlane == null) {
1405       if (jvxlData.isContoured) {
1406         definitionLine += (-1 - nSurfaceInts) + " " + bytesUncompressedEdgeData;
1407       } else if (jvxlData.isBicolorMap) {
1408         definitionLine += (nSurfaceInts) + " " + (-bytesUncompressedEdgeData);
1409       } else {
1410         definitionLine += nSurfaceInts + " " + bytesUncompressedEdgeData;
1411       }
1412       definitionLine += " "
1413           + (jvxlData.isJvxlPrecisionColor && nColorData != -1 ? -nColorData
1414               : nColorData);
1415     } else {
1416       String s = " " + jvxlData.jvxlPlane.x + " " + jvxlData.jvxlPlane.y + " "
1417           + jvxlData.jvxlPlane.z + " " + jvxlData.jvxlPlane.w;
1418       definitionLine += (jvxlData.isContoured ? "-1 -2 " + (-nColorData)
1419           : "-1 -1 " + nColorData)
1420           + s;
1421     }
1422     if (jvxlData.isContoured) {
1423       if (jvxlData.contourValues == null || jvxlData.contourColixes == null) {
1424         definitionLine += " " + jvxlData.nContours;
1425       } else {
1426         definitionLine += " " + Escape.escapeArray(jvxlData.contourValues)
1427             + " \"" + jvxlData.contourColors + "\"";
1428       }
1429     }
1430     // ... mappedDataMin mappedDataMax valueMappedToRed valueMappedToBlue ...
1431     float min = (jvxlData.mappedDataMin == Float.MAX_VALUE ? 0f
1432         : jvxlData.mappedDataMin);
1433     definitionLine += " " + min + " " + jvxlData.mappedDataMax + " "
1434         + jvxlData.valueMappedToRed + " " + jvxlData.valueMappedToBlue;
1435     if (jvxlData.insideOut) {
1436       definitionLine += " insideOut";
1437     }
1438     return definitionLine;
1439   }
1440 
1441   */
1442 
1443 }
1444