1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2007-03-30 11:40:16 -0500 (Fri, 30 Mar 2007) $
4  * $Revision: 7273 $
5  *
6  * Copyright (C) 2007 Miguel, Bob, Jmol Development
7  *
8  * Contact: hansonr@stolaf.edu
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package org.jmol.jvxl.readers;
25 
26 import java.util.Date;
27 
28 import javajs.util.AU;
29 import javajs.util.M4;
30 import javajs.util.P3;
31 import javajs.util.P3i;
32 import javajs.util.SB;
33 import javajs.util.T3;
34 import javajs.util.V3;
35 
36 import org.jmol.atomdata.AtomData;
37 import org.jmol.atomdata.RadiusData;
38 import org.jmol.atomdata.RadiusData.EnumType;
39 import org.jmol.c.VDW;
40 import javajs.util.BS;
41 import org.jmol.jvxl.data.JvxlCoder;
42 import org.jmol.jvxl.data.MeshData;
43 import org.jmol.util.BSUtil;
44 import org.jmol.util.ContactPair;
45 import org.jmol.util.Logger;
46 
47 abstract class AtomDataReader extends VolumeDataReader {
48 
49   protected float maxDistance;
50   protected ContactPair contactPair;
51 
AtomDataReader()52   AtomDataReader() {
53   }
54 
initADR(SurfaceGenerator sg)55   protected void initADR(SurfaceGenerator sg) {
56     initVDR(sg);
57     precalculateVoxelData = true;
58   }
59 
60   protected String fileName;
61   protected String fileDotModel;
62   protected int modelIndex;
63 
64   protected AtomData atomData = new AtomData();
65 
66   protected P3[] atomXyzTruncated;
67   protected float[] atomRadius;
68   protected float[] atomProp;
69   protected int[] atomNo;
70   protected int[] atomIndex;
71   protected int[] myIndex;
72   protected int ac;
73   protected int myAtomCount;
74   protected int nearbyAtomCount;
75   protected int firstNearbyAtom;
76   protected BS bsMySelected = new BS();
77   protected BS bsMyIgnored = new BS();
78   protected BS bsNearby;
79 
80   protected boolean doAddHydrogens;
81   protected boolean havePlane;
82   protected boolean doUseIterator;
83   protected float theProperty;
84   protected boolean haveOneProperty;
85   private float minPtsPerAng;
86 
87   /**
88    * @param isMapData
89    */
90   @Override
setup(boolean isMapData)91   protected void setup(boolean isMapData) {
92     setup2();
93   }
94 
setup2()95   protected void setup2() {
96     //CANNOT BE IN HERE IF atomDataServer is not valid
97     contactPair = params.contactPair;
98     doAddHydrogens = (sg.atomDataServer != null && params.addHydrogens); //Jvxl cannot do this on its own
99     modelIndex = params.modelIndex;
100     if (params.bsIgnore != null)
101       bsMyIgnored = params.bsIgnore;
102     if (params.volumeData != null) {
103       setVolumeDataV(params.volumeData);
104       setBBox(volumeData.volumetricOrigin, 0);
105       ptV.setT(volumeData.volumetricOrigin);
106       for (int i = 0; i < 3; i++)
107         ptV.scaleAdd2(volumeData.voxelCounts[i] - 1,
108             volumeData.volumetricVectors[i], ptV);
109       setBBox(ptV, 0);
110     }
111     havePlane = (params.thePlane != null);
112     if (havePlane)
113       volumeData.setPlaneParameters(params.thePlane);
114   }
115 
markPlaneVoxels(P3 p, float r)116   protected void markPlaneVoxels(P3 p, float r) {
117     for (int i = 0, pt = thisX * yzCount, pt1 = pt + yzCount; pt < pt1; pt++, i++) {
118       volumeData.getPoint(pt, ptV);
119       thisPlane[i] = ptV.distance(p) - r;
120     }
121   }
122 
setVolumeForPlane()123   protected void setVolumeForPlane() {
124     if (useOriginStepsPoints) {
125       xyzMin = P3.newP(params.origin);
126       xyzMax = P3.newP(params.origin);
127       xyzMax.add3((params.points.x - 1) * params.steps.x, (params.points.y - 1)
128           * params.steps.y, (params.points.z - 1) * params.steps.z);
129     } else if (params.boundingBox == null) {
130       getAtoms(params.bsSelected, false, true, false, false, false, false,
131           params.mep_marginAngstroms, params.modelInvRotation);
132       if (xyzMin == null) {
133         xyzMin = P3.new3(-10, -10, -10);
134         xyzMax = P3.new3(10, 10, 10);
135       }
136     } else {
137       xyzMin = P3.newP(params.boundingBox[0]);
138       xyzMax = P3.newP(params.boundingBox[1]);
139     }
140     setRanges(params.plane_ptsPerAngstrom, params.plane_gridMax, 0);
141   }
142 
143   /**
144    *
145    * @param bsSelected
146    * @param doAddHydrogens
147    * @param getRadii
148    * @param getMolecules
149    * @param getAllModels
150    * @param addNearbyAtoms
151    * @param getAtomMinMax
152    * @param marginAtoms
153    * @param modelInvRotation
154    */
getAtoms(BS bsSelected, boolean doAddHydrogens, boolean getRadii, boolean getMolecules, boolean getAllModels, boolean addNearbyAtoms, boolean getAtomMinMax, float marginAtoms, M4 modelInvRotation)155   protected void getAtoms(BS bsSelected, boolean doAddHydrogens,
156                           boolean getRadii, boolean getMolecules,
157                           boolean getAllModels, boolean addNearbyAtoms,
158                           boolean getAtomMinMax, float marginAtoms, M4 modelInvRotation) {
159     if (addNearbyAtoms)
160       getRadii = true;
161     // set atomRadiusData to 100% if it has not been set already
162     // if it hasn't already been set.
163     if (getRadii) {
164       if (params.atomRadiusData == null)
165         params.atomRadiusData = new RadiusData(null, 1, EnumType.FACTOR,
166             VDW.AUTO);
167       atomData.radiusData = params.atomRadiusData;
168       atomData.radiusData.valueExtended = params.solventExtendedAtomRadius;
169       if (doAddHydrogens)
170         atomData.radiusData.vdwType = VDW.NOJMOL;
171     }
172     atomData.modelIndex = modelIndex; // -1 here means fill ALL atoms; any other
173     // means "this model only"
174     atomData.bsSelected = bsSelected;
175     atomData.bsIgnored = bsMyIgnored;
176     sg.fillAtomData(atomData, AtomData.MODE_FILL_COORDS
177         | (getAllModels ? AtomData.MODE_FILL_MULTIMODEL : 0)
178         | (getMolecules ? AtomData.MODE_FILL_MOLECULES : 0)
179         | (getRadii ? AtomData.MODE_FILL_RADII : 0));
180     if (doUseIterator)
181       atomData.bsSelected = null;
182     ac = atomData.ac;
183     modelIndex = atomData.firstModelIndex;
184     if (modelInvRotation != null)
185       atomData.transformXYZ(modelInvRotation, bsSelected);
186     boolean needRadius = false;
187     for (int i = 0; i < ac; i++) {
188       if ((bsSelected == null || bsSelected.get(i)) && (!bsMyIgnored.get(i))) {
189         if (havePlane
190             && Math.abs(volumeData.distancePointToPlane(atomData.xyz[i])) > 2 * (atomData.atomRadius[i] = getWorkingRadius(
191                 i, marginAtoms)))
192           continue;
193         bsMySelected.set(i);
194         needRadius = !havePlane;
195       }
196       if (getRadii && (addNearbyAtoms || needRadius))
197         atomData.atomRadius[i] = getWorkingRadius(i, marginAtoms);
198     }
199 
200     float rH = (getRadii && doAddHydrogens ? getWorkingRadius(-1, marginAtoms)
201         : 0);
202     myAtomCount = BSUtil.cardinalityOf(bsMySelected);
203     BS atomSet = BSUtil.copy(bsMySelected);
204     int nH = 0;
205     atomProp = null;
206     theProperty = Float.MAX_VALUE;
207     haveOneProperty = false;
208     float[] props = params.theProperty;
209     if (myAtomCount > 0) {
210       P3[] hAtoms = null;
211       if (doAddHydrogens) {
212         atomData.bsSelected = atomSet;
213         sg.atomDataServer.fillAtomData(atomData,
214             AtomData.MODE_GET_ATTACHED_HYDROGENS);
215         hAtoms = new P3[nH = atomData.hydrogenAtomCount];
216         for (int i = 0; i < atomData.hAtoms.length; i++)
217           if (atomData.hAtoms[i] != null)
218             for (int j = atomData.hAtoms[i].length; --j >= 0;)
219               hAtoms[--nH] = atomData.hAtoms[i][j];
220         nH = hAtoms.length;
221         Logger.info(nH + " attached hydrogens added");
222       }
223       int n = nH + myAtomCount;
224       if (getRadii)
225         atomRadius = new float[n];
226       atomXyzTruncated = new P3[n];
227       if (params.theProperty != null)
228         atomProp = new float[n];
229       atomNo = new int[n];
230       atomIndex = new int[n];
231       myIndex = new int[ac];
232 
233       for (int i = 0; i < nH; i++) {
234         if (getRadii)
235           atomRadius[i] = rH;
236         atomXyzTruncated[i] = hAtoms[i];
237         atomNo[i] = -1;
238         if (atomProp != null)
239           addAtomProp(i, Float.NaN);
240         // if (params.logMessages)
241         // Logger.debug("draw {" + hAtoms[i].x + " " + hAtoms[i].y + " "
242         // + hAtoms[i].z + "};");
243       }
244       myAtomCount = nH;
245       for (int i = atomSet.nextSetBit(0); i >= 0; i = atomSet.nextSetBit(i + 1)) {
246         if (atomProp != null)
247           addAtomProp(myAtomCount,
248               (props != null && i < props.length ? props[i] : Float.NaN));
249         atomXyzTruncated[myAtomCount] = atomData.xyz[i];
250         atomNo[myAtomCount] = atomData.atomicNumber[i];
251         atomIndex[myAtomCount] = i;
252         myIndex[i] = myAtomCount;
253         if (getRadii)
254           atomRadius[myAtomCount] = atomData.atomRadius[i];
255         myAtomCount++;
256       }
257     }
258     firstNearbyAtom = myAtomCount;
259     if (!isQuiet)
260       Logger.info(myAtomCount + " atoms will be used in the surface calculation");
261 
262     if (myAtomCount == 0) {
263       setBBox(P3.new3(10, 10, 10), 0);
264       setBBox(P3.new3(-10, -10, -10), 0);
265     }
266     for (int i = 0; i < myAtomCount; i++)
267       setBBox(atomXyzTruncated[i], getRadii ? atomRadius[i] + 0.5f : 0);
268     if (!Float.isNaN(params.scale)) {
269       V3 v = V3.newVsub(xyzMax, xyzMin);
270       v.scale(0.5f);
271       xyzMin.add(v);
272       v.scale(params.scale);
273       xyzMax.add2(xyzMin, v);
274       xyzMin.sub(v);
275     }
276 
277     // fragment idea
278 
279     if (!addNearbyAtoms || myAtomCount == 0)
280       return;
281     P3 pt = new P3();
282 
283     bsNearby = new BS();
284     for (int i = 0; i < ac; i++) {
285       if (atomSet.get(i) || bsMyIgnored.get(i))
286         continue;
287       float rA = atomData.atomRadius[i];
288       if (params.thePlane != null
289           && Math.abs(volumeData.distancePointToPlane(atomData.xyz[i])) > 2 * rA)
290         continue;
291       if (params.theProperty != null)
292         rA += maxDistance;
293       pt = atomData.xyz[i];
294       if (pt.x + rA > xyzMin.x && pt.x - rA < xyzMax.x && pt.y + rA > xyzMin.y
295           && pt.y - rA < xyzMax.y && pt.z + rA > xyzMin.z
296           && pt.z - rA < xyzMax.z) {
297         bsNearby.set(i);
298         nearbyAtomCount++;
299       }
300     }
301     int nAtoms = myAtomCount;
302     if (nearbyAtomCount != 0) {
303       nAtoms += nearbyAtomCount;
304       atomRadius = AU.arrayCopyF(atomRadius, nAtoms);
305       atomXyzTruncated = (P3[]) AU.arrayCopyObject(atomXyzTruncated, nAtoms);
306       if (atomIndex != null)
307         atomIndex = AU.arrayCopyI(atomIndex, nAtoms);
308 
309       if (props != null)
310         atomProp = AU.arrayCopyF(atomProp, nAtoms);
311       for (int i = bsNearby.nextSetBit(0); i >= 0; i = bsNearby
312           .nextSetBit(i + 1)) {
313         if (props != null)
314           addAtomProp(myAtomCount, props[i]);
315         myIndex[i] = myAtomCount;
316         atomIndex[myAtomCount] = i;
317         atomXyzTruncated[myAtomCount] = atomData.xyz[i];
318         atomRadius[myAtomCount++] = atomData.atomRadius[i];
319       }
320     }
321 
322     if (getRadii)
323       setRadii();
324 
325     haveOneProperty = (!Float.isNaN(theProperty));
326   }
327 
328   /**
329    * solvent radius
330    */
331   protected float sr;
332   /**
333    * atom radius + solvent radius
334    */
335   protected float[] rs;
336   /**
337    * square of (atom radius + solvent radius)
338    */
339   protected float[] rs2;
340   /**
341    * maximun (atom radius + solvent radius)
342    */
343   protected float maxRS;
344 
setRadii()345   protected void setRadii() {
346     if (rs != null)
347       return;
348     maxRS = 0;
349     rs = new float[myAtomCount];
350     rs2 = new float[myAtomCount];
351     for (int i = 0; i < myAtomCount; i++) {
352       float r = rs[i] = atomRadius[i] + sr;
353       if (r > maxRS)
354         maxRS = r;
355       rs2[i] = rs[i] * rs[i];
356     }
357   }
358 
addAtomProp(int i, float f)359   private void addAtomProp(int i, float f) {
360     atomProp[i] = f;
361     if (!Float.isNaN(theProperty))
362       if (f != theProperty)
363         theProperty = (theProperty == Float.MAX_VALUE ? f : Float.NaN);
364   }
365 
getWorkingRadius(int i, float marginAtoms)366   private float getWorkingRadius(int i, float marginAtoms) {
367     float r = (i < 0 ? atomData.hAtomRadius : atomData.atomRadius[i]);
368     return (Float.isNaN(marginAtoms) ? Math.max(r, 0.1f) : r + marginAtoms);
369   }
370 
setHeader(String calcType, String line2)371   protected void setHeader(String calcType, String line2) {
372     jvxlFileHeaderBuffer = new SB();
373     if (atomData.programInfo != null)
374       jvxlFileHeaderBuffer.append("#created by ").append(atomData.programInfo)
375           .append(" on ").append("" + new Date()).append("\n");
376     jvxlFileHeaderBuffer.append(calcType).append("\n").append(line2)
377         .append("\n");
378   }
379 
setRanges(float ptsPerAngstrom, int maxGrid, float minPtsPerAng)380   protected void setRanges(float ptsPerAngstrom, int maxGrid, float minPtsPerAng) {
381     if (xyzMin == null)
382       return;
383     this.ptsPerAngstrom = ptsPerAngstrom;
384     this.maxGrid = maxGrid;
385     this.minPtsPerAng = minPtsPerAng;
386     setVolumeData();
387     JvxlCoder.jvxlCreateHeader(volumeData, jvxlFileHeaderBuffer);
388   }
389 
390   @Override
setVolumeData()391   protected void setVolumeData() {
392     setVolumeDataADR();
393   }
394 
setVolumeDataADR()395   protected void setVolumeDataADR() {
396     if (!setVolumeDataParams()) {
397       setVoxelRange(0, xyzMin.x, xyzMax.x, ptsPerAngstrom, maxGrid,
398           minPtsPerAng);
399       setVoxelRange(1, xyzMin.y, xyzMax.y, ptsPerAngstrom, maxGrid,
400           minPtsPerAng);
401       setVoxelRange(2, xyzMin.z, xyzMax.z, ptsPerAngstrom, maxGrid,
402           minPtsPerAng);
403     }
404   }
405 
setVertexSource()406   protected void setVertexSource() {
407     if (meshDataServer != null)
408       meshDataServer.fillMeshData(meshData, MeshData.MODE_GET_VERTICES, null);
409     if (params.vertexSource != null) {
410       params.vertexSource = AU.arrayCopyI(params.vertexSource,
411           meshData.vc);
412       for (int i = 0; i < meshData.vc; i++)
413         params.vertexSource[i] = Math.abs(params.vertexSource[i]) - 1;
414     }
415   }
416 
resetPlane(float value)417   protected void resetPlane(float value) {
418     for (int i = 0; i < yzCount; i++)
419       thisPlane[i] = value;
420   }
421 
resetVoxelData(float value)422   protected void resetVoxelData(float value) {
423     for (int x = 0; x < nPointsX; ++x)
424       for (int y = 0; y < nPointsY; ++y)
425         for (int z = 0; z < nPointsZ; ++z)
426           voxelData[x][y][z] = value;
427   }
428 
429   protected float[] thisPlane;
430   protected BS thisAtomSet;
431   protected int thisX;
432 
getVoxel(int i, int j, int k, int ipt)433   private float getVoxel(int i, int j, int k, int ipt) {
434     return (isProgressive ? thisPlane[ipt % yzCount] : voxelData[i][j][k]);
435   }
436 
unsetVoxelData()437   protected void unsetVoxelData() {
438     unsetVoxelData2();
439   }
440 
unsetVoxelData2()441   protected void unsetVoxelData2() {
442     if (isProgressive)
443       for (int i = 0; i < yzCount; i++) {
444         if (thisPlane[i] == Float.MAX_VALUE)
445           thisPlane[i] = Float.NaN;
446       }
447     else
448       for (int x = 0; x < nPointsX; ++x)
449         for (int y = 0; y < nPointsY; ++y)
450           for (int z = 0; z < nPointsZ; ++z)
451             if (voxelData[x][y][z] == Float.MAX_VALUE)
452               voxelData[x][y][z] = Float.NaN;
453   }
454 
455   protected float margin;
456   protected float vl0, vl1, vl2;
457 
setGridLimitsForAtom(P3 ptA, float rA, P3i pt0, P3i pt1)458   protected void setGridLimitsForAtom(P3 ptA, float rA, P3i pt0, P3i pt1) {
459     rA += margin; // to span corner-to-corner possibility
460     volumeData.xyzToVoxelPt(ptA.x, ptA.y, ptA.z, pt0);
461     int x = (int) Math.floor(rA / volumeData.volumetricVectorLengths[0]);
462     int y = (int) Math.floor(rA / volumeData.volumetricVectorLengths[1]);
463     int z = (int) Math.floor(rA / volumeData.volumetricVectorLengths[2]);
464     pt1.set(pt0.x + x, pt0.y + y, pt0.z + z);
465     pt0.set(pt0.x - x, pt0.y - y, pt0.z - z);
466     pt0.x = Math.max(pt0.x - 1, 0);
467     pt0.y = Math.max(pt0.y - 1, 0);
468     pt0.z = Math.max(pt0.z - 1, 0);
469     pt1.x = Math.min(pt1.x + 1, nPointsX);
470     pt1.y = Math.min(pt1.y + 1, nPointsY);
471     pt1.z = Math.min(pt1.z + 1, nPointsZ);
472   }
473 
474   // for isoSolventReader and isoIntersectReader
475 
476   protected BS bsSurfaceVoxels;
477   protected BS validSpheres, noFaceSpheres;
478   protected int[] voxelSource;
479 
getAtomMinMax(BS bs, BS[] bsAtomMinMax)480   protected void getAtomMinMax(BS bs, BS[] bsAtomMinMax) {
481     for (int i = 0; i < nPointsX; i++)
482       bsAtomMinMax[i] = new BS();
483     for (int iAtom = myAtomCount; --iAtom >= 0;) {
484       if (bs != null && !bs.get(iAtom))
485         continue;
486       setGridLimitsForAtom(atomXyzTruncated[iAtom], atomRadius[iAtom], pt0, pt1);
487       for (int i = pt0.x; i < pt1.x; i++)
488         bsAtomMinMax[i].set(iAtom);
489       //System.out.println("for atom " + iAtom + " " + ptA + " " + min + " " + max);
490     }
491   }
492 
493   protected final P3 ptY0 = new P3();
494   protected final P3 ptZ0 = new P3();
495   protected final P3i pt0 = new P3i();
496   protected final P3i pt1 = new P3i();
497   protected final P3 ptV = new P3();
498 
markSphereVoxels(float r0, float distance)499   protected void markSphereVoxels(float r0, float distance) {
500     boolean isWithin = (distance != Float.MAX_VALUE && point != null);
501     T3 v0 = volumetricVectors[0];
502     T3 v1 = volumetricVectors[1];
503     T3 v2 = volumetricVectors[2];
504     for (int iAtom = thisAtomSet.nextSetBit(0); iAtom >= 0; iAtom = thisAtomSet
505         .nextSetBit(iAtom + 1)) {
506       if (!havePlane && validSpheres != null && !validSpheres.get(iAtom))
507         continue;
508       boolean isSurface = (noFaceSpheres != null && noFaceSpheres.get(iAtom));
509       boolean isNearby = (iAtom >= firstNearbyAtom);
510       P3 ptA = atomXyzTruncated[iAtom];
511       float rA = atomRadius[iAtom];
512       if (isWithin && ptA.distance(point) > distance + rA + 0.5)
513         continue;
514       float rA0 = rA + r0;
515       setGridLimitsForAtom(ptA, rA0, pt0, pt1);
516       //pt1.y = nPointsY;
517       //pt1.z = nPointsZ;
518       if (isProgressive) {
519         pt0.x = thisX;
520         pt1.x = thisX + 1;
521       }
522       volumeData.voxelPtToXYZ(pt0.x, pt0.y, pt0.z, ptV);
523       for (int i = pt0.x; i < pt1.x; i++, ptV.add2(v0, ptY0)) {
524         ptY0.setT(ptV);
525         for (int j = pt0.y; j < pt1.y; j++, ptV.add2(v1, ptZ0)) {
526           ptZ0.setT(ptV);
527           for (int k = pt0.z; k < pt1.z; k++, ptV.add(v2)) {
528             float value = ptV.distance(ptA) - rA;
529             int ipt = volumeData.getPointIndex(i, j, k);
530             if ((r0 == 0 || value <= rA0) && value < getVoxel(i, j, k, ipt)) {
531               if (isNearby || isWithin && ptV.distance(point) > distance)
532                 value = Float.NaN;
533               setVoxel(i, j, k, ipt, value);
534               if (!Float.isNaN(value)) {
535                 if (voxelSource != null)
536                   voxelSource[ipt] = iAtom + 1;
537                 if (value < 0 && isSurface)
538                   bsSurfaceVoxels.set(ipt);
539               }
540             }
541           }
542         }
543       }
544     }
545   }
546 
setVoxel(int i, int j, int k, int ipt, float value)547   protected void setVoxel(int i, int j, int k, int ipt, float value) {
548     if (isProgressive)
549       thisPlane[ipt % yzCount] = value;
550     else
551       voxelData[i][j][k] = value;
552   }
553 
554 }
555