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 Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 /*
26 
27  * The JVXL file format
28  * --------------------
29  *
30  * as of 3/29/07 this code is COMPLETELY untested. It was hacked out of the
31  * Jmol code, so there is probably more here than is needed.
32  *
33  *
34  *
35  * see http://www.stolaf.edu/academics/chemapps/jmol/docs/misc/JVXL-format.pdf
36  *
37  * The JVXL (Jmol VoXeL) format is a file format specifically designed
38  * to encode an isosurface or planar slice through a set of 3D scalar values
39  * in lieu of a that set. A JVXL file can contain coordinates, and in fact
40  * it must contain at least one coordinate, but additional coordinates are
41  * optional. The file can contain any finite number of encoded surfaces.
42  * However, the compression of 300-500:1 is based on the reduction of the
43  * data to a SINGLE surface.
44  *
45  *
46  * The original Marching Cubes code was written by Miguel Howard in 2005.
47  * The classes Parser, ArrayUtil, and TextFormat are condensed versions
48  * of the classes found in org.jmol.util.
49  *
50  * All code relating to JVXL format is copyrighted 2006/2007 and invented by
51  * Robert M. Hanson,
52  * Professor of Chemistry,
53  * St. Olaf College,
54  * 1520 St. Olaf Ave.
55  * Northfield, MN. 55057.
56  *
57  * Implementations of the JVXL format should reference
58  * "Robert M. Hanson, St. Olaf College" and the opensource Jmol project.
59  *
60  *
61  * implementing marching squares; see
62  * http://www.secam.ex.ac.uk/teaching/ug/studyres/COM3404/COM3404-2006-Lecture15.pdf
63  *
64  * lines through coordinates are identical to CUBE files
65  * after that, we have a line that starts with a negative number to indicate this
66  * is a JVXL file:
67  *
68  * line1:  (int)-nSurfaces  (int)edgeFractionBase (int)edgeFractionRange
69  * (nSurface lines): (float)cutoff (int)nBytesData (int)nBytesFractions
70  *
71  * definition1
72  * edgedata1
73  * fractions1
74  * colordata1
75  * ....
76  * definition2
77  * edgedata2
78  * fractions2
79  * colordata2
80  * ....
81  *
82  * definitions: a line with detail about what sort of compression follows
83  *
84  * edgedata: a list of the count of vertices ouside and inside the cutoff, whatever
85  * that may be, ordered by nested for loops for(x){for(y){for(z)}}}.
86  *
87  * nOutside nInside nOutside nInside...
88  *
89  * fractions: an ascii list of characters representing the fraction of distance each
90  * encountered surface point is along each voxel cube edge found to straddle the
91  * surface. The order written is dictated by the reader algorithm and is not trivial
92  * to describe. Each ascii character is constructed by taking a base character and
93  * adding onto it the fraction times a range. This gives a character that can be
94  * quoted EXCEPT for backslash, which MAY be substituted for by '!'. Jmol uses the
95  * range # - | (35 - 124), reserving ! and } for special meanings.
96  *
97  * colordata: same deal here, but with possibility of "double precision" using two bytes.
98  *
99  *
100  *
101  * THIS READER
102  * -----------
103  *
104  * This is a first attempt at a generic JVXL file reader and writer class.
105  * It is an extraction of Jmol org.jmol.viewer.Isosurface.Java and related pieces.
106  *
107  * The goal of the reader is to be able to read CUBE-like data and
108  * convert that data to JVXL file data.
109  *
110  *
111  */
112 
113 package org.jmol.jvxl.readers;
114 
115 
116 import java.io.BufferedInputStream;
117 import java.io.BufferedReader;
118 import java.util.Map;
119 
120 
121 import org.jmol.atomdata.AtomData;
122 import org.jmol.atomdata.AtomDataServer;
123 import org.jmol.atomdata.RadiusData;
124 import javajs.util.BS;
125 import org.jmol.jvxl.data.JvxlCoder;
126 import org.jmol.jvxl.data.JvxlData;
127 import org.jmol.jvxl.data.VolumeData;
128 import org.jmol.jvxl.data.MeshData;
129 import org.jmol.jvxl.api.MeshDataServer;
130 import org.jmol.jvxl.calc.MarchingSquares;
131 
132 import javajs.api.GenericBinaryDocument;
133 import javajs.util.AU;
134 import javajs.util.Lst;
135 import org.jmol.util.Logger;
136 import org.jmol.viewer.FileManager;
137 import org.jmol.viewer.Viewer;
138 
139 import javajs.util.M4;
140 import javajs.util.Measure;
141 import javajs.util.Rdr;
142 import javajs.util.SB;
143 import javajs.util.OC;
144 import javajs.util.PT;
145 import javajs.util.P3;
146 import javajs.util.P4;
147 import javajs.util.V3;
148 
149 public class SurfaceGenerator {
150 
151   public Parameters params;
152   public JvxlData jvxlData;
153   public MeshData meshData;
154   public VolumeData volumeDataTemp;
155   public MeshDataServer meshDataServer;
156   public AtomDataServer atomDataServer;
157   public MarchingSquares marchingSquares;
158   public String version;
159   public boolean isValid = true;
160   public String fileType;
161   public BS bsVdw;
162 
163   private int colorPtr;
164   private SurfaceReader surfaceReader;
165   private OC out;
166 
SurfaceGenerator(AtomDataServer atomDataServer, MeshDataServer meshDataServer, MeshData meshData, JvxlData jvxlData)167   public SurfaceGenerator(AtomDataServer atomDataServer, MeshDataServer meshDataServer,
168                           MeshData meshData, JvxlData jvxlData) {
169     this.atomDataServer = atomDataServer;
170     this.meshDataServer = meshDataServer;
171     params = new Parameters();
172     this.meshData = (meshData == null ? new MeshData() : meshData);
173     this.jvxlData = (jvxlData == null ? new JvxlData() : jvxlData);
174     volumeDataTemp = new VolumeData();
175     // volumeDataTemp is used for some initial work, but it is sometimes
176     // replaced in surfaceReader by other sources of volume data.
177     // Contact will re-use it.
178     initializeIsosurface();
179   }
180 
setJvxlData(JvxlData jvxlData)181   public void setJvxlData(JvxlData jvxlData) {
182     this.jvxlData = jvxlData;
183     if (jvxlData != null)
184       jvxlData.version = version;
185   }
186 
187   //////////////////////////////////////////////////////////////
188 
189   /**
190    *
191    * @param propertyName
192    * @param value
193    * @param bs
194    * @return TRUE if handled
195    */
196   @SuppressWarnings("unchecked")
setProp(String propertyName, Object value, BS bs)197   public boolean setProp(String propertyName, Object value, BS bs) {
198 
199     if ("debug" == propertyName) {
200       boolean TF = ((Boolean) value).booleanValue();
201       params.logMessages = TF;
202       // logCompression = TF;
203       params.logCube = TF;
204       return true;
205     }
206 
207     if ("init" == propertyName) {
208       initializeIsosurface();
209       if (value instanceof Parameters) {
210         params = (Parameters) value;
211       } else {
212         params.script = (String) value;
213         if (params.script != null && params.script.indexOf(";#") >= 0) {
214           // crude hack for ScriptEvaluator messing up
215           params.script = PT.rep(params.script, ";#", "; #");
216         }
217       }
218       return false; // more to do
219     }
220 
221     if ("silent" == propertyName) {
222       params.isSilent = true;
223       return true;
224     }
225 
226     if ("map" == propertyName) {
227       params.resetForMapping(((Boolean) value).booleanValue());
228       if (surfaceReader != null)
229         surfaceReader.minMax = null;
230       return true;
231     }
232     if ("finalize" == propertyName) {
233       initializeIsosurface();
234       return true;
235     }
236 
237     if ("clear" == propertyName) {
238       if (surfaceReader != null)
239         surfaceReader.discardTempData(true);
240       return false;
241     }
242 
243     if ("fileIndex" == propertyName) {
244       params.fileIndex = ((Integer) value).intValue();
245       if (params.fileIndex < 0)
246         params.fileIndex = 0;// 0 used in efvet reader for "current color"
247       params.readAllData = false;
248       return true;
249     }
250 
251     if ("blockData" == propertyName) {
252       params.blockCubeData = ((Boolean) value).booleanValue();
253       return true;
254     }
255 
256     if ("withinPoints" == propertyName) {
257       params.boundingBox = (P3[]) ((Object[]) value)[1];
258       return true;
259     }
260 
261     if ("boundingBox" == propertyName) {
262       P3[] pts = (P3[]) value;
263       params.boundingBox = new P3[] { P3.newP(pts[0]),
264           P3.newP(pts[pts.length - 1]) };
265       return true;
266     }
267 
268     if ("func" == propertyName) {
269       params.func = value;
270       return true;
271     }
272 
273     if ("intersection" == propertyName) {
274       params.intersection = (BS[]) value;
275       return true;
276     }
277 
278     if ("bsSolvent" == propertyName) {
279       params.bsSolvent = (BS) value;
280       return true;
281     }
282 
283     if ("select" == propertyName) {
284       params.bsSelected = (BS) value;
285       return true;
286     }
287 
288     if ("ignore" == propertyName) {
289       params.bsIgnore = (BS) value;
290       return true;
291     }
292 
293     if ("propertySmoothing" == propertyName) {
294       params.propertySmoothing = ((Boolean) value).booleanValue();
295       return true;
296     }
297 
298     if ("propertyDistanceMax" == propertyName) {
299       params.propertyDistanceMax = ((Float) value).floatValue();
300       return true;
301     }
302 
303     if ("propertySmoothingPower" == propertyName) {
304       params.propertySmoothingPower = ((Integer) value).intValue();
305       return true;
306     }
307 
308     if ("title" == propertyName) {
309       if (value == null) {
310         params.title = null;
311         return true;
312       } else if (AU.isAS(value)) {
313         params.title = (String[]) value;
314         if (Logger.debugging)
315          for (int i = 0; i < params.title.length; i++)
316            if (params.title[i].length() > 0)
317              Logger.info(params.title[i]);
318       }
319       return true;
320     }
321 
322     if ("sigma" == propertyName) {
323       // not all readers will take this, so we assign
324       // cutoff to the value as well.
325       params.cutoff = params.sigma = ((Float) value).floatValue();
326       //params.isPositiveOnly = false;
327       params.cutoffAutomatic = false;
328       return true;
329     }
330 
331     if ("cutoff" == propertyName) {
332       params.cutoff = ((Float) value).floatValue();
333       params.isPositiveOnly = false;
334       params.cutoffAutomatic = false;
335       return true;
336     }
337 
338     if ("parameters" == propertyName) {
339       params.parameters = AU.ensureLengthA((float[]) value, 2);
340       if (params.parameters.length > 0 && params.parameters[0] != 0)
341         params.cutoff = params.parameters[0];
342       return true;
343     }
344 
345     if ("cutoffPositive" == propertyName) {
346       params.cutoff = ((Float) value).floatValue();
347       params.isPositiveOnly = true;
348       params.isCutoffAbsolute = false;
349       return true;
350     }
351 
352     if ("cap" == propertyName || "slab" == propertyName) {
353       if (value != null)
354         params.addSlabInfo((Object[]) value);
355       return true;
356     }
357 
358     if ("scale" == propertyName) {
359       params.scale = ((Float) value).floatValue();
360       return true;
361     }
362 
363     if ("scale3d" == propertyName) {
364       params.scale3d = ((Float) value).floatValue();
365       return true;
366     }
367 
368     if ("angstroms" == propertyName) {
369       params.isAngstroms = true;
370       return true;
371     }
372 
373     if ("resolution" == propertyName) {
374       float resolution = ((Float) value).floatValue();
375       params.resolution = (resolution > 0 ? resolution : Float.MAX_VALUE);
376       return true;
377     }
378 
379     if ("downsample" == propertyName) {
380       int rate = ((Integer) value).intValue();
381       params.downsampleFactor = (rate >= 0 ? rate : 0);
382       return true;
383     }
384 
385     if ("anisotropy" == propertyName) {
386       if ((params.dataType & Parameters.NO_ANISOTROPY) == 0)
387         params.setAnisotropy((P3) value);
388       return true;
389     }
390 
391     if ("eccentricity" == propertyName) {
392       params.setEccentricity((P4) value);
393       return true;
394     }
395 
396     if ("addHydrogens" == propertyName) {
397       params.addHydrogens = ((Boolean) value).booleanValue();
398       return true;
399     }
400 
401     if ("squareData" == propertyName) {
402       params.isSquared = (value == null ? false : ((Boolean) value)
403           .booleanValue());
404       return true;
405     }
406 
407     if ("squareLinear" == propertyName) {
408       params.isSquaredLinear = (value == null ? false : ((Boolean) value)
409           .booleanValue());
410       return true;
411     }
412 
413     if ("gridPoints" == propertyName) {
414       params.iAddGridPoints = true;
415       return true;
416     }
417 
418     if ("atomIndex" == propertyName) {
419       params.atomIndex = ((Integer) value).intValue();
420       return true;
421     }
422 
423     // / color options
424 
425     if ("insideOut" == propertyName) {
426       params.insideOut = true;
427       return true;
428     }
429 
430     if ("sign" == propertyName) {
431         params.isCutoffAbsolute = !params.isPositiveOnly;
432       params.colorBySign = true;
433       colorPtr = 0;
434       return true;
435     }
436 
437     if ("colorRGB" == propertyName) {
438       int rgb = ((Integer) value).intValue();
439       params.colorRgb = params.colorPos = params.colorPosLCAO = rgb;
440       if (colorPtr++ == 0) {
441         params.colorNeg = params.colorNegLCAO = rgb;
442       } else {
443         params.colorRgb = Integer.MAX_VALUE;
444       }
445       return true;
446     }
447 
448     if ("monteCarloCount" == propertyName) {
449       params.psi_monteCarloCount = ((Integer) value).intValue();
450       return true;
451     }
452     if ("rangeAll" == propertyName) {
453       params.rangeAll = true;
454       return true;
455     }
456 
457     if ("rangeSelected" == propertyName) {
458       params.rangeSelected = true;
459       return true;
460     }
461 
462     if ("red" == propertyName) {
463       params.valueMappedToRed = ((Float) value).floatValue();
464       return true;
465     }
466 
467     if ("blue" == propertyName) {
468       params.valueMappedToBlue = ((Float) value).floatValue();
469       if (params.valueMappedToRed > params.valueMappedToBlue) {
470         float f = params.valueMappedToRed;
471         params.valueMappedToRed = params.valueMappedToBlue;
472         params.valueMappedToBlue = f;
473         params.isColorReversed = !params.isColorReversed;
474       }
475       params.rangeDefined = true;
476       params.rangeAll = false;
477       return true;
478     }
479 
480     if ("reverseColor" == propertyName) {
481       params.isColorReversed = true;
482       return true;
483     }
484 
485     if ("setColorScheme" == propertyName) {
486       getSurfaceSets();
487       params.colorBySets = true;
488       mapSurface();
489       return true;
490     }
491 
492     if ("center" == propertyName) {
493       params.center.setT((P3) value);
494       return true;
495     }
496 
497 //    if ("volumeData" == propertyName) {
498 //      params.volumeData = (VolumeData) value;
499 //      return true;
500 //    }
501 
502     if ("origin" == propertyName) {
503       params.origin = (P3) value;
504       return true;
505     }
506 
507     if ("step" == propertyName) {
508       params.steps = (P3) value;
509       return true;
510     }
511 
512     if ("modelInvRotation" == propertyName) {
513       params.modelInvRotation = (M4) value;
514       return true;
515     }
516 
517     if ("point" == propertyName) {
518       params.points = (P3) value;
519       return true;
520     }
521 
522     if ("withinDistance" == propertyName) {
523       params.distance = ((Float) value).floatValue();
524       return true;
525     }
526 
527     if ("withinPoint" == propertyName) {
528       params.point = (P3) value;
529       return true;
530     }
531 
532     if ("progressive" == propertyName) {
533       // an option for JVXL.java
534       params.isXLowToHigh = true;
535       return true;
536     }
537 
538     if ("phase" == propertyName) {
539       String color = (String) value;
540       params.isCutoffAbsolute = true;
541       params.colorBySign = true;
542       params.colorByPhase = true;
543       params.colorPhase = SurfaceReader.getColorPhaseIndex(color);
544       if (params.colorPhase < 0) {
545         Logger.warn(" invalid color phase: " + color);
546         params.colorPhase = 0;
547       }
548       params.colorByPhase = params.colorPhase != 0;
549       if (params.state >= Parameters.STATE_DATA_READ) {
550         params.dataType = params.surfaceType;
551         params.state = Parameters.STATE_DATA_COLORED;
552         params.isBicolorMap = true;
553         surfaceReader.applyColorScale();
554       }
555       return true;
556     }
557 
558     /*
559      * Based on the form of the parameters, returns and encoded radius as
560      * follows:
561      *
562      * script meaning range encoded
563      *
564      * +1.2 offset [0 - 10] x -1.2 offset 0) x 1.2 absolute (0 - 10] x + 10 -30%
565      * 70% (-100 - 0) x + 200 +30% 130% (0 x + 200 80% percent (0 x + 100
566      *
567      * in each case, numbers can be integer or float
568      */
569 
570     if ("radius" == propertyName) {
571       Logger.info("solvent probe radius set to " + value);
572       params.atomRadiusData = (RadiusData) value;
573       return true;
574     }
575 
576     if ("envelopeRadius" == propertyName) {
577       params.envelopeRadius = ((Float) value).floatValue();
578       return true;
579     }
580 
581     if ("cavityRadius" == propertyName) {
582       params.cavityRadius = ((Float) value).floatValue();
583       return true;
584     }
585 
586     if ("cavity" == propertyName) {
587       params.isCavity = true;
588       return true;
589     }
590 
591     if ("doFullMolecular" == propertyName) {
592       params.doFullMolecular = true;
593       return true;
594     }
595 
596     if ("pocket" == propertyName) {
597       params.pocket = (Boolean) value;
598       params.fullyLit = params.pocket.booleanValue();
599       return true;
600     }
601 
602     if ("minset" == propertyName) {
603       params.minSet = ((Integer) value).intValue();
604       return true;
605     }
606 
607     if ("maxset" == propertyName) {
608       params.maxSet = ((Integer) value).intValue();
609       return true;
610     }
611 
612     if ("plane" == propertyName) {
613       params.setPlane((P4) value);
614       return true;
615     }
616 
617     if ("contour" == propertyName) {
618       params.isContoured = true;
619       int n;
620       if (AU.isAF(value)) {
621         // discrete values
622         params.contoursDiscrete = (float[]) value;
623         params.nContours = params.contoursDiscrete.length;
624       } else if (value instanceof P3) {
625         P3 pt = params.contourIncrements = (P3) value;
626         float from = pt.x;
627         float to = pt.y;
628         float step = pt.z;
629         if (step <= 0)
630           step = 1;
631         n = 0;
632         for (float p = from; p <= to + step / 10; p += step, n++) {
633         }
634         params.contoursDiscrete = new float[n];
635         float p = from;
636         for (int i = 0; i < n; i++, p += step) {
637           params.contoursDiscrete[i] = p;
638         }
639         params.nContours = n;
640       } else {
641         n = ((Integer) value).intValue();
642         params.thisContour = 0; // flags as marked for JVXL reader
643         if (n == 0)
644           params.nContours = MarchingSquares.defaultContourCount;
645         else if (n > 0)
646           params.nContours = n;
647         else
648           params.thisContour = -n;
649       }
650       return true;
651     }
652 
653     if ("colorDiscrete" == propertyName) {
654       params.contourColixes = (short[]) value;
655       return true;
656     }
657 
658     if ("colorDensity" == propertyName) {
659       params.colorDensity = true;
660       if (value != null)
661         params.pointSize = ((Float) value).floatValue();
662       return false;
663     }
664     if ("fullPlane" == propertyName) {
665       // fullPlane == true --> params.contourFromZero is false
666       // fullPlane == false --> params.contourFromZero is true
667       // this only relates to projections onto a plane
668       // the default is contourFromZero TRUE
669       // but MEP default is contourFromZero FALSE
670       // the setting is ignored when discrete contours
671       // are specified, because in that case we just
672       // define the triangle color by their centers
673 
674       params.contourFromZero = !((Boolean) value).booleanValue();
675       return true;
676     }
677 
678     if ("mapLattice" == propertyName) {
679       params.mapLattice = (P3) value;
680       return true;
681     }
682 
683     if ("extendGrid" == propertyName) {
684       params.extendGrid = ((Float) value).floatValue();
685       return true;
686     }
687 
688     // / final actions ///
689 
690     if ("property" == propertyName) {
691       params.dataType = Parameters.SURFACE_PROPERTY;
692       params.theProperty = (float[]) value;
693       mapSurface();
694       return true;
695     }
696 
697     // these next four set the reader themselves.
698     if ("sphere" == propertyName) {
699       params.setSphere(((Float) value).floatValue(), false);
700       readerData = Float.valueOf(params.distance);
701       surfaceReader = newReader("IsoShapeReader");
702       generateSurface();
703       return true;
704     }
705 
706     // these next four set the reader themselves.
707     if ("geodesic" == propertyName) {
708       params.setSphere(((Float) value).floatValue(), true);
709       readerData = Float.valueOf(params.distance);
710       surfaceReader = newReader("IsoShapeReader");
711       generateSurface();
712       return true;
713     }
714 
715     if ("ellipsoid" == propertyName) {
716       if (value instanceof P4)
717         params.setEllipsoidP4((P4) value);
718       else if (AU.isAF(value))
719         params.setEllipsoidAF((float[]) value);
720       else
721         return true;
722       readerData = Float.valueOf(params.distance);
723       surfaceReader = newReader("IsoShapeReader");
724       generateSurface();
725       return true;
726     }
727 
728     if ("ellipsoid3" == propertyName) {
729       params.setEllipsoidAF((float[]) value);
730       readerData = Float.valueOf(params.distance);
731       surfaceReader = newReader("IsoShapeReader");
732       generateSurface();
733       return true;
734     }
735 
736     if ("lp" == propertyName) {
737       params.setLp((P4) value);
738       readerData = new float[] { 3, 2, 0, 15, 0 };
739       surfaceReader = newReader("IsoShapeReader");
740       generateSurface();
741       return true;
742     }
743 
744     if ("rad" == propertyName) {
745       params.setRadical((P4) value);
746       readerData = new float[] { 3, 2, 0, 15, 0 };
747       surfaceReader = newReader("IsoShapeReader");
748       generateSurface();
749       return true;
750     }
751 
752     if ("lobe" == propertyName) {
753       params.setLobe((P4) value);
754       readerData = new float[] { 3, 2, 0, 15, 0 };
755       surfaceReader = newReader("IsoShapeReader");
756       generateSurface();
757       return true;
758     }
759 
760     if ("hydrogenOrbital" == propertyName) {
761       if (!params.setAtomicOrbital((float[]) value)) {
762         isValid = false;
763         return true;
764       }
765       readerData = new float[] { params.psi_n, params.psi_l, params.psi_m,
766           params.psi_Znuc, params.psi_monteCarloCount };
767       surfaceReader = newReader("IsoShapeReader");
768       processState();
769       return true;
770     }
771 
772     if ("functionXY" == propertyName) {
773       params.setFunctionXY((Lst<Object>) value);
774       if (params.isContoured)      // xy plane through origin
775         volumeDataTemp.setPlaneParameters(
776             params.thePlane == null ? params.thePlane = P4.new4(0, 0, 1, 0)
777             : params.thePlane);
778       if (((String) params.functionInfo.get(0)).indexOf("_xyz") >= 0)
779         getFunctionZfromXY();
780       processState();
781       return true;
782     }
783 
784     if ("functionXYZ" == propertyName) {
785       params.setFunctionXYZ((Lst<Object>) value);
786       processState();
787       return true;
788     }
789 
790     if ("lcaoType" == propertyName) {
791       params.setLcao((String) value, colorPtr);
792       return true;
793     }
794 
795     if ("lcaoCartoonCenter" == propertyName) {
796       if (++params.state != Parameters.STATE_DATA_READ)
797         return true;
798       if (Float.isNaN(params.center.x))
799         params.center.setT((V3) value);
800       return false;
801     }
802 
803     if ("molecular" == propertyName || "solvent" == propertyName
804         || "sasurface" == propertyName || "nomap" == propertyName) {
805       params.setSolvent(propertyName, ((Float) value).floatValue());
806       if (!params.isSilent)
807         Logger.info(params.calculationType);
808       processState();
809       return true;
810     }
811 
812     if ("moData" == propertyName) {
813       params.moData = (Map<String, Object>) value;
814       return true;
815     }
816 
817     if ("mepCalcType" == propertyName) {
818       params.mep_calcType = ((Integer) value).intValue();
819       return true;
820     }
821 
822     if ("mep" == propertyName) {
823       params.setMep((float[]) value, false); // mep charges
824       processState();
825       return true;
826     }
827 
828     if ("mlp" == propertyName) {
829       params.setMep((float[]) value, true); // mlp charges
830       processState();
831       return true;
832     }
833 
834     if ("nci" == propertyName) {
835       boolean isPromolecular = ((Boolean) value).booleanValue();
836       params.setNci(isPromolecular);
837       if (isPromolecular)
838         processState();
839       return true;
840     }
841 
842     if ("calculationType" == propertyName) {
843       params.calculationType = (String) value;
844       return true;
845     }
846 
847     if ("charges" == propertyName) {
848       params.theProperty = (float[]) value;
849       return true;
850     }
851 
852     if ("randomSeed" == propertyName) {
853       // for any object requiring reproduction in the state
854       // and using random numbers -- AO and MO
855       params.randomSeed = ((Integer) value).intValue();
856       return true;
857     }
858 
859     if ("molecularOrbital" == propertyName) {
860       int iMo = 0;
861       float[] linearCombination = null;
862       if (value instanceof Integer) {
863         iMo = ((Integer) value).intValue();
864       } else {
865         linearCombination = (float[]) value;
866       }
867       params.setMO(iMo, linearCombination);
868       Logger.info(params.calculationType);
869       processState();
870       return true;
871     }
872 
873     if ("fileType" == propertyName) {
874       fileType = (String) value;
875       return true;
876     }
877 
878     if ("fileName" == propertyName) {
879       params.fileName = (String) value;
880       return true;
881     }
882 
883     if ("filesData" == propertyName) {
884       params.filesData = (Object[]) value;
885       return true;
886     }
887 
888     if ("outputChannel" == propertyName) {
889       out = (OC) value;
890       return true;
891     }
892 
893     if ("readFile" == propertyName) {
894       if ((surfaceReader = setFileData((Viewer) atomDataServer, value)) == null) {
895         Logger.error("Could not set the surface data");
896         return true;
897       }
898       surfaceReader.setOutputChannel(out);
899       generateSurface();
900       return true;
901     }
902 
903     if ("mapColor" == propertyName) {
904       if ((surfaceReader = setFileData((Viewer) atomDataServer, value)) == null) {
905         Logger.error("Could not set the mapping data");
906         return true;
907       }
908       surfaceReader.setOutputChannel(out);
909       mapSurface();
910       return true;
911     }
912 
913     if ("getSurfaceSets" == propertyName) {
914       getSurfaceSets();
915       return true;
916     }
917 
918     if ("periodic" == propertyName) {
919       params.isPeriodic = true;
920     }
921 
922     // continue with operations in calling class...
923     return false;
924   }
925 
newReader(String name)926   private SurfaceReader newReader(String name) {
927     SurfaceReader sr = (SurfaceReader) getInterface(name);
928     if (sr != null)
929       sr.init(this);
930     return sr;
931   }
932 
newReaderBr(String name, BufferedReader br)933   private SurfaceReader newReaderBr(String name, BufferedReader br) {
934     SurfaceFileReader sr = (SurfaceFileReader) getInterface(name);
935     if (sr != null)
936       sr.init2(this, br);
937     return sr;
938   }
939 
getInterface(String name)940   private static Object getInterface(String name) {
941     try {
942       Class<?> x = Class.forName("org.jmol.jvxl.readers." + name);
943       return (x == null ? null : x.newInstance());
944     } catch (Exception e) {
945       Logger.error("Interface.java Error creating instance for " + name + ": \n" + e.toString());
946       return null;
947     }
948   }
949 
getSurfaceSets()950   private void getSurfaceSets() {
951     if (meshDataServer == null) {
952       meshData.getSurfaceSet();
953     } else {
954       meshDataServer.fillMeshData(meshData, MeshData.MODE_GET_VERTICES, null);
955       meshData.getSurfaceSet();
956       meshDataServer.fillMeshData(meshData, MeshData.MODE_PUT_SETS, null);
957     }
958   }
959 
processState()960   private void processState() {
961     if (params.state == Parameters.STATE_INITIALIZED && params.thePlane != null)
962       params.state++;
963     if (params.state >= Parameters.STATE_DATA_READ) {
964       mapSurface();
965     } else {
966       generateSurface();
967     }
968   }
969 
setReader()970   private boolean setReader() {
971     readerData = null;
972     if (surfaceReader != null)
973       return !surfaceReader.vertexDataOnly;
974     switch (params.dataType) {
975     case Parameters.SURFACE_NOMAP:
976       surfaceReader = newReader("IsoPlaneReader");
977       break;
978     case Parameters.SURFACE_PROPERTY:
979       surfaceReader = newReader("AtomPropertyMapper");
980       break;
981     case Parameters.SURFACE_MEP:
982     case Parameters.SURFACE_MLP:
983       readerData = (params.dataType == Parameters.SURFACE_MEP ? "Mep" : "Mlp");
984       if (params.state == Parameters.STATE_DATA_COLORED) {
985         surfaceReader = newReader("AtomPropertyMapper");
986       } else {
987         surfaceReader = newReader("Iso" + readerData + "Reader");
988       }
989       break;
990     case Parameters.SURFACE_INTERSECT_FILE:
991       surfaceReader = newReader("IsoIntersectFileReader");
992       break;
993     case Parameters.SURFACE_INTERSECT_ATOM:
994       surfaceReader = newReader("IsoIntersectAtomReader");
995       break;
996     case Parameters.SURFACE_SOLVENT:
997     case Parameters.SURFACE_MOLECULAR:
998     case Parameters.SURFACE_SASURFACE:
999       surfaceReader = newReader("IsoSolventReader");
1000       break;
1001     case Parameters.SURFACE_NCI:
1002     case Parameters.SURFACE_MOLECULARORBITAL:
1003       surfaceReader = newReader("IsoMOReader");
1004       break;
1005     case Parameters.SURFACE_FUNCTIONXY:
1006       surfaceReader = newReader("IsoFxyReader");
1007       break;
1008     case Parameters.SURFACE_FUNCTIONXYZ:
1009       surfaceReader = newReader("IsoFxyzReader");
1010       break;
1011     }
1012     if (Logger.debugging)
1013       Logger.info("Using surface reader " + surfaceReader);
1014     if (params.isSilent && surfaceReader != null)
1015       surfaceReader.isQuiet = true;
1016     return true;
1017   }
1018 
generateSurface()1019   private void generateSurface() {
1020     if (++params.state != Parameters.STATE_DATA_READ)
1021       return;
1022     setReader();
1023     if (params.sbOut == null)
1024       params.sbOut = new SB();
1025     jvxlData.sbOut = params.sbOut;
1026     boolean haveMeshDataServer = (meshDataServer != null);
1027     if (params.colorBySign)
1028       params.isBicolorMap = true;
1029     if (surfaceReader == null) {
1030       Logger.error("surfaceReader is null for " + params.dataType);
1031       return;
1032     }
1033     if (!surfaceReader.createIsosurface(false)) {
1034       Logger.error("Could not create isosurface");
1035       params.cutoff = Float.NaN;
1036       surfaceReader.closeReader();
1037       return;
1038     }
1039 
1040     if (params.probes != null) {
1041       for (int i = params.probeValues.length; --i >= 0;) {
1042         params.probeValues[i] = surfaceReader.getValueAtPoint(params.probes[i], false);
1043       }
1044     }
1045 
1046     if (params.pocket != null && haveMeshDataServer)
1047       surfaceReader.selectPocket(!params.pocket.booleanValue());
1048 
1049     if (params.minSet > 0)
1050       surfaceReader.excludeMinimumSet();
1051 
1052     if (params.maxSet > 0)
1053       surfaceReader.excludeMaximumSet();
1054 
1055     if (params.slabInfo != null)
1056       surfaceReader.slabIsosurface(params.slabInfo);
1057 
1058     if (haveMeshDataServer && meshDataServer.notifySurfaceGenerationCompleted())
1059       surfaceReader.hasColorData = false;
1060 
1061     if (jvxlData.thisSet != null)
1062       getSurfaceSets();
1063     if (jvxlData.jvxlDataIs2dContour) {
1064       surfaceReader.colorIsosurface();
1065       params.state = Parameters.STATE_DATA_COLORED;
1066     }
1067     if (jvxlData.jvxlDataIsColorDensity) {
1068       params.state = Parameters.STATE_DATA_COLORED;
1069     }
1070     if (params.colorBySign || params.isBicolorMap) {
1071       params.state = Parameters.STATE_DATA_COLORED;
1072       surfaceReader.applyColorScale();
1073     }
1074     if (jvxlData.vertexColorMap != null) {
1075       jvxlData.vertexColorMap = null;
1076       surfaceReader.hasColorData = false;
1077     }
1078     surfaceReader.jvxlUpdateInfo();
1079     marchingSquares = surfaceReader.marchingSquares;
1080     surfaceReader.discardTempData(false);
1081     params.mappedDataMin = Float.MAX_VALUE;
1082     surfaceReader.closeReader();
1083     if (params.state != Parameters.STATE_DATA_COLORED &&
1084         (surfaceReader.hasColorData || params.colorDensity)) {
1085       params.state = Parameters.STATE_DATA_COLORED;
1086       colorIsosurface();
1087     }
1088     surfaceReader = null;
1089   }
1090 
mapSurface()1091   private void mapSurface() {
1092     if (params.state == Parameters.STATE_INITIALIZED && params.thePlane != null)
1093       params.state++;
1094     if (++params.state < Parameters.STATE_DATA_COLORED)
1095       return;
1096     if (!setReader())
1097       return;
1098     //if (params.dataType == Parameters.SURFACE_FUNCTIONXY)
1099       //params.thePlane = new Point4f(0, 0, 1, 0);
1100     if (params.isPeriodic)
1101       surfaceReader.volumeData.isPeriodic = true;
1102     if (params.thePlane != null) {
1103       boolean isSquared = params.isSquared;
1104       params.isSquared = false;
1105       params.cutoff = 0;
1106       surfaceReader.volumeData.setMappingPlane(params.thePlane);
1107       surfaceReader.createIsosurface(!params.isPeriodic);//but don't read volume data yet
1108       surfaceReader.volumeData.setMappingPlane(null);
1109       if (meshDataServer != null)
1110         meshDataServer.notifySurfaceGenerationCompleted();
1111       if (params.dataType == Parameters.SURFACE_NOMAP) {
1112         // just a simple plane
1113         surfaceReader.discardTempData(true);
1114         return;
1115       }
1116       params.isSquared = isSquared;
1117       params.mappedDataMin = Float.MAX_VALUE;
1118       surfaceReader.readVolumeData(true);
1119       if (params.mapLattice != null)
1120         surfaceReader.volumeData.isPeriodic = true;
1121     } else if (!params.colorBySets && !params.colorDensity) {
1122       surfaceReader.readAndSetVolumeParameters(true);
1123       params.mappedDataMin = Float.MAX_VALUE;
1124       surfaceReader.readVolumeData(true);
1125     }
1126     colorIsosurface();
1127     surfaceReader.closeReader();
1128     surfaceReader = null;
1129   }
1130 
colorIsosurface()1131   void colorIsosurface() {
1132     surfaceReader.colorIsosurface();
1133     surfaceReader.jvxlUpdateInfo();
1134     surfaceReader.updateTriangles();
1135     surfaceReader.discardTempData(true);
1136     if (meshDataServer != null)
1137       meshDataServer.notifySurfaceMappingCompleted();
1138   }
1139 
1140   /**
1141    * only called from org.openscience.jvxl.Jvxl.main
1142    *
1143    * @param property
1144    * @param index
1145    * @return Object
1146    */
getProperty(String property, int index)1147   public Object getProperty(String property, int index) {
1148     if (property == "jvxlFileData")
1149       return JvxlCoder.jvxlGetFile(jvxlData, null, params.title, "", true,
1150           index, null, null); // for Jvxl.java
1151     if (property == "jvxlFileInfo")
1152       return JvxlCoder.jvxlGetInfo(jvxlData); // for Jvxl.java
1153     return null;
1154   }
1155 
1156   /**
1157    * @param vwr
1158    * @param value
1159    * @return SurfaceReader
1160    */
1161   @SuppressWarnings("unchecked")
setFileData(Viewer vwr, Object value)1162   private SurfaceReader setFileData(Viewer vwr, Object value) {
1163     // comes from "readFile" or "mapColors"
1164     String fileType = this.fileType;
1165     this.fileType = null;
1166     if (value instanceof Map) {
1167       Map<String, Object> map = (Map<String, Object>) value;
1168       if (map.containsKey("__pymolSurfaceData__")) {
1169         readerData = map;
1170         return newReaderBr("PyMOLMeshReader", null);
1171       }
1172       value = map.get("volumeData");
1173     }
1174     if (value instanceof VolumeData) {
1175       // never implemented?
1176       volumeDataTemp = (VolumeData) value;
1177       return newReader("VolumeDataReader");
1178     }
1179     String data = null;
1180     if (value instanceof String) {
1181       // inline string data
1182       data = (String) value;
1183       // this will be OK, because any string will be a simple string,
1184       // not a binary file.
1185       value = Rdr.getBR((String) value);
1186     }
1187     if (value instanceof Object[]) {
1188       // [BufferedFileReader[], float[]] -> [VolumeFileReader[], float[]]
1189       Object[] a = (Object[]) ((Object[]) value)[0];
1190       VolumeFileReader[] b = new VolumeFileReader[a.length];
1191       for (int i = 0; i < a.length; i++)
1192         b[i] = (VolumeFileReader) setFileData(vwr, a[i]);
1193       ((Object[]) value)[0] = b;
1194       readerData = value;
1195       return newReader("IsoIntersectGridReader");
1196     }
1197     BufferedReader br = (BufferedReader) value;
1198     if (fileType == null)
1199       fileType = FileManager.determineSurfaceFileType(br);
1200     if (fileType != null && fileType.startsWith("UPPSALA")) {
1201       //"http://eds.bmc.uu.se/cgi-bin/eds/gen_maps_zip.pl?POST?pdbCode=1blu&mapformat=ccp4&maptype=2fofc&page=generate"
1202       // -- ah, but this does not work, because it is asynchronous!
1203       // -- first a message is sent back that suggests you might have to wait,
1204       //    then a message that says, "Here is your map" is sent.
1205 
1206       String fname = params.fileName;
1207       fname = fname.substring(0, fname.indexOf("/", 10));
1208       fname += PT.getQuotedStringAt(fileType,
1209           fileType.indexOf("A HREF") + 1);
1210       params.fileName = fname;
1211       value = atomDataServer.getBufferedInputStream(fname);
1212       if (value == null) {
1213         Logger.error("Isosurface: could not open file " + fname);
1214         return null;
1215       }
1216       try {
1217         br = Rdr.getBufferedReader((BufferedInputStream) value, null);
1218       } catch (Exception e) {
1219         // TODO
1220       }
1221       fileType = FileManager.determineSurfaceFileType(br);
1222     }
1223     if (fileType == null)
1224       fileType = "UNKNOWN";
1225     Logger.info("data file type was determined to be " + fileType);
1226     if (fileType.equals("Jvxl+"))
1227       return newReaderBr("JvxlReader", br);
1228 
1229     readerData = new Object[] { params.fileName, data };
1230 
1231     if ("MRC DELPHI DSN6".indexOf(fileType.toUpperCase()) >= 0) {
1232       fileType += "Binary";
1233     }
1234     return newReaderBr(fileType + "Reader", br);
1235   }
1236 
1237   private Object readerData;
1238 
getReaderData()1239   Object getReaderData() {
1240     // needed by DelPhiBinary, Dsn6Binary, IsoShape, MrcBinary, Msms, Pmesh, CifDensity
1241     Object o = readerData;
1242     readerData = null;
1243     return o;
1244   }
1245 
initializeIsosurface()1246   void initializeIsosurface() {
1247     params.initialize();
1248     colorPtr = 0;
1249     surfaceReader = null;
1250     marchingSquares = null;
1251     initState();
1252   }
1253 
initState()1254   public void initState() {
1255     params.state = Parameters.STATE_INITIALIZED;
1256     params.dataType = params.surfaceType = Parameters.SURFACE_NONE;
1257   }
1258 
setLcao()1259   public String setLcao() {
1260     params.colorPos = params.colorPosLCAO;
1261     params.colorNeg = params.colorNegLCAO;
1262     return params.lcaoType;
1263 
1264   }
1265 
getFunctionZfromXY()1266   private void getFunctionZfromXY() {
1267     P3 origin = (P3) params.functionInfo.get(1);
1268     int[] counts = new int[3];
1269     int[] nearest = new int[3];
1270     V3[] vectors = new V3[3];
1271     for (int i = 0; i < 3; i++) {
1272       P4 info = (P4) params.functionInfo.get(i + 2);
1273       counts[i] = Math.abs((int) info.x);
1274       vectors[i] = V3.new3(info.y, info.z, info.w);
1275     }
1276     int nx = counts[0];
1277     int ny = counts[1];
1278     P3 pt = new P3();
1279     P3 pta = new P3();
1280     P3 ptb = new P3();
1281     P3 ptc = new P3();
1282 
1283     float[][] data = (float[][]) params.functionInfo.get(5);
1284     float[][] data2 = new float[nx][ny];
1285     float[] d;
1286     //int n = 0;
1287     //for (int i = 0; i < data.length; i++)
1288       //System.out.println("draw pt"+(++n)+" {" + data[i][0] + " " + data[i][1] + " " + data[i][2] + "} color yellow");
1289     for (int i = 0; i < nx; i++)
1290       for (int j = 0; j < ny; j++) {
1291         pt.scaleAdd2(i, vectors[0], origin);
1292         pt.scaleAdd2(j, vectors[1], pt);
1293         float dist = findNearestThreePoints(pt.x, pt.y, data, nearest);
1294         pta.set((d = data[nearest[0]])[0], d[1], d[2]);
1295         if (dist < 0.00001) {
1296           pt.z = d[2];
1297         } else {
1298           ptb.set((d = data[nearest[1]])[0], d[1], d[2]);
1299           ptc.set((d = data[nearest[2]])[0], d[1], d[2]);
1300           pt.z = distanceVerticalToPlane(pt.x, pt.y, pta, ptb, ptc);
1301         }
1302         data2[i][j] = pt.z;
1303         //System.out.println("draw pt"+(++n)+" " + Escape.escape(pt) + " color red");
1304       }
1305     params.functionInfo.set(5, data2);
1306   }
1307 
1308   private final V3 vAB = new V3();
1309   private final V3 vNorm = new V3();
1310   private final P3 ptRef = P3.new3(0, 0, 1e15f);
1311 
distanceVerticalToPlane(float x, float y, P3 pta, P3 ptb, P3 ptc)1312   private float distanceVerticalToPlane(float x, float y, P3 pta,
1313                                               P3 ptb, P3 ptc) {
1314     // ax + by + cz + d = 0
1315 
1316     float d = Measure.getDirectedNormalThroughPoints(pta, ptb, ptc, ptRef, vNorm, vAB);
1317     return (vNorm.x * x + vNorm.y * y + d) / -vNorm.z;
1318   }
1319 
findNearestThreePoints(float x, float y, float[][] xyz, int[] result)1320   private static float findNearestThreePoints(float x, float y, float[][] xyz, int[] result) {
1321     //result should be int[3];
1322     float d, dist1, dist2, dist3;
1323     int i1, i2, i3;
1324     i1 = i2 = i3 = -1;
1325     dist1 = dist2 = dist3 = Float.MAX_VALUE;
1326     for (int i = xyz.length; --i >= 0;) {
1327       d = (d = xyz[i][0] - x) * d + (d = xyz[i][1] - y) * d;
1328       if (d < dist1) {
1329         dist3 = dist2;
1330         dist2 = dist1;
1331         dist1 = d;
1332         i3 = i2;
1333         i2 = i1;
1334         i1 = i;
1335       } else if (d < dist2) {
1336         dist3 = dist2;
1337         dist2 = d;
1338         i3 = i2;
1339         i2 = i;
1340       } else if (d < dist3) {
1341         dist3 = d;
1342         i3 = i;
1343       }
1344     }
1345     //System.out.println("findnearest " + dist1);
1346     result[0] = i1;
1347     result[1] = i2;
1348     result[2] = i3;
1349     return dist1;
1350   }
1351 
addRequiredFile(String fileName)1352   public void addRequiredFile(String fileName) {
1353     if (meshDataServer == null)
1354       return;
1355     meshDataServer.addRequiredFile(fileName);
1356   }
1357 
setRequiredFile(String oldName, String fileName)1358   public void setRequiredFile(String oldName, String fileName) {
1359     if (meshDataServer == null)
1360       return;
1361     meshDataServer.setRequiredFile(oldName, fileName);
1362   }
1363 
log(String msg)1364   void log(String msg) {
1365     if (atomDataServer == null)
1366       System.out.println(msg);
1367     else
1368       atomDataServer.log(msg);
1369   }
1370 
setOutputChannel(GenericBinaryDocument binaryDoc, OC out)1371   void setOutputChannel(GenericBinaryDocument binaryDoc, OC out) {
1372     if (meshDataServer == null)
1373       return;
1374      meshDataServer.setOutputChannel(binaryDoc, out);
1375   }
1376 
fillAtomData(AtomData atomData, int mode)1377   void fillAtomData(AtomData atomData, int mode) {
1378     if ((mode & AtomData.MODE_FILL_RADII) != 0
1379         && atomData.bsSelected != null) {
1380       if (bsVdw == null)
1381         bsVdw = new BS();
1382       bsVdw.or(atomData.bsSelected);
1383     }
1384     atomDataServer.fillAtomData(atomData, mode);
1385   }
1386 
getOriginVaVbVc()1387   public V3[] getOriginVaVbVc() {
1388     return (surfaceReader.volumeData == null ? null : surfaceReader.volumeData.oabc);
1389   }
1390 
1391 }
1392