1 /* $RCSfiodelle$allrueFFFF
2  * $Author: egonw $
3  * $Date: 2005-11-10 09:52:44 -0600 (Thu, 10 Nov 2005) $
4  * $Revision: 4255 $
5  *
6  * Copyright (C) 2003-2005  Miguel, Jmol Development, www.jmol.org
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 package org.jmol.symmetry;
26 
27 import java.util.Map;
28 
29 import org.jmol.api.AtomIndexIterator;
30 import org.jmol.api.GenericPlatform;
31 import org.jmol.api.Interface;
32 import org.jmol.api.SymmetryInterface;
33 import org.jmol.bspt.Bspt;
34 import org.jmol.bspt.CubeIterator;
35 import org.jmol.modelset.Atom;
36 import org.jmol.modelset.ModelSet;
37 import org.jmol.script.T;
38 import org.jmol.util.Escape;
39 import org.jmol.util.JmolMolecule;
40 import org.jmol.util.Logger;
41 import org.jmol.util.SimpleUnitCell;
42 import org.jmol.util.Tensor;
43 import org.jmol.viewer.Viewer;
44 
45 import javajs.util.BS;
46 import javajs.util.Lst;
47 import javajs.util.M3;
48 import javajs.util.M4;
49 import javajs.util.Matrix;
50 import javajs.util.P3;
51 import javajs.util.Quat;
52 import javajs.util.SB;
53 import javajs.util.T3;
54 import javajs.util.V3;
55 
56 public class Symmetry implements SymmetryInterface {
57   // NOTE: THIS CLASS IS VERY IMPORTANT.
58   // IN ORDER TO MODULARIZE IT, IT IS REFERENCED USING
59   // xxxx = Interface.getSymmetry();
60 
61   /* Symmetry is a wrapper class that allows access to the package-local
62    * classes PointGroup, SpaceGroup, SymmetryInfo, and UnitCell.
63    *
64    * When symmetry is detected in ANY model being loaded, a SymmetryInterface
65    * is established for ALL models.
66    *
67    * The SpaceGroup information could be saved with each model, but because this
68    * depends closely on what atoms have been selected, and since tracking that with atom
69    * deletion is a bit complicated, instead we just use local instances of that class.
70    *
71    * The three PointGroup methods here could be their own interface; they are just here
72    * for convenience.
73    *
74    * The file readers use SpaceGroup and UnitCell methods
75    *
76    * The modelSet and modelLoader classes use UnitCell and SymmetryInfo
77    *
78    */
79   private PointGroup pointGroup;
80   SpaceGroup spaceGroup;
81   private SymmetryInfo symmetryInfo;
82   private UnitCell unitCell;
83   private boolean isBio;
84 
85   @Override
isBio()86   public boolean isBio() {
87     return isBio;
88   }
89 
Symmetry()90   public Symmetry() {
91     // instantiated ONLY using
92     // symmetry = Interface.getSymmetry();
93     // DO NOT use symmetry = new Symmetry();
94     // as that will invalidate the Jar file modularization
95   }
96 
97   @Override
setPointGroup(SymmetryInterface siLast, T3 center, T3[] atomset, BS bsAtoms, boolean haveVibration, float distanceTolerance, float linearTolerance, boolean localEnvOnly)98   public SymmetryInterface setPointGroup(SymmetryInterface siLast,
99                                          T3 center, T3[] atomset,
100                                          BS bsAtoms,
101                                          boolean haveVibration,
102                                          float distanceTolerance,
103                                          float linearTolerance, boolean localEnvOnly) {
104     pointGroup = PointGroup.getPointGroup(siLast == null ? null
105         : ((Symmetry) siLast).pointGroup, center, atomset, bsAtoms,
106         haveVibration, distanceTolerance, linearTolerance, localEnvOnly);
107     return this;
108   }
109 
110   @Override
getPointGroupName()111   public String getPointGroupName() {
112     return pointGroup.getName();
113   }
114 
115   @Override
getPointGroupInfo(int modelIndex, String drawID, boolean asInfo, String type, int index, float scale)116   public Object getPointGroupInfo(int modelIndex, String drawID,
117                                   boolean asInfo, String type, int index,
118                                   float scale) {
119     if (drawID == null && !asInfo && pointGroup.textInfo != null)
120       return pointGroup.textInfo;
121     else if (drawID == null && pointGroup.isDrawType(type, index, scale))
122       return pointGroup.drawInfo;
123     else if (asInfo && pointGroup.info != null)
124       return pointGroup.info;
125     return pointGroup.getInfo(modelIndex, drawID, asInfo, type, index, scale);
126   }
127 
128   // SpaceGroup methods
129 
130   @Override
setSpaceGroup(boolean doNormalize)131   public void setSpaceGroup(boolean doNormalize) {
132     if (spaceGroup == null)
133       spaceGroup = SpaceGroup.getNull(true, doNormalize, false);
134   }
135 
136   @Override
addSpaceGroupOperation(String xyz, int opId)137   public int addSpaceGroupOperation(String xyz, int opId) {
138     return spaceGroup.addSymmetry(xyz, opId, false);
139   }
140 
141   @Override
addBioMoleculeOperation(M4 mat, boolean isReverse)142   public int addBioMoleculeOperation(M4 mat, boolean isReverse) {
143     isBio = spaceGroup.isBio = true;
144     return spaceGroup.addSymmetry((isReverse ? "!" : "") + "[[bio" + mat, 0,
145         false);
146   }
147 
148   @Override
setLattice(int latt)149   public void setLattice(int latt) {
150     spaceGroup.setLatticeParam(latt);
151   }
152 
153   @Override
getSpaceGroup()154   public Object getSpaceGroup() {
155     return spaceGroup;
156   }
157 
158   @Override
setSpaceGroupFrom(SymmetryInterface symmetry)159   public void setSpaceGroupFrom(SymmetryInterface symmetry) {
160     spaceGroup = (SpaceGroup) symmetry.getSpaceGroup();
161   }
162 
163   /**
164    *
165    * @param desiredSpaceGroupIndex
166    * @param name
167    * @param data a Lst<SymmetryOperation> or Lst<M4>
168    * @param modDim in [3+d] modulation dimension
169    * @return true if a known space group
170    */
171   @Override
createSpaceGroup(int desiredSpaceGroupIndex, String name, Object data, int modDim)172   public boolean createSpaceGroup(int desiredSpaceGroupIndex, String name,
173                                   Object data, int modDim) {
174     spaceGroup = SpaceGroup.createSpaceGroup(desiredSpaceGroupIndex, name,
175         data, modDim);
176     if (spaceGroup != null && Logger.debugging)
177       Logger.debug("using generated space group " + spaceGroup.dumpInfo());
178     return spaceGroup != null;
179   }
180 
181   @Override
getSpaceGroupInfoObj(String name, SymmetryInterface cellInfo, boolean isFull)182   public Object getSpaceGroupInfoObj(String name, SymmetryInterface cellInfo, boolean isFull) {
183     return SpaceGroup.getInfo(spaceGroup, name, cellInfo, isFull);
184   }
185 
186   @Override
getLatticeDesignation()187   public Object getLatticeDesignation() {
188     return spaceGroup.getLatticeDesignation();
189   }
190 
191   @Override
setFinalOperations(String name, P3[] atoms, int iAtomFirst, int noSymmetryCount, boolean doNormalize, String filterSymop)192   public void setFinalOperations(String name, P3[] atoms, int iAtomFirst,
193                                  int noSymmetryCount, boolean doNormalize,
194                                  String filterSymop) {
195     if (name != null && (name.startsWith("bio") || name.indexOf(" *(") >= 0)) // filter SYMOP
196       spaceGroup.name = name;
197     if (filterSymop != null) {
198       Lst<SymmetryOperation> lst = new Lst<SymmetryOperation>();
199       lst.addLast(spaceGroup.operations[0]);
200       for (int i = 1; i < spaceGroup.operationCount; i++)
201         if (filterSymop.contains(" " + (i + 1) + " "))
202           lst.addLast(spaceGroup.operations[i]);
203       spaceGroup = SpaceGroup.createSpaceGroup(-1,
204           name + " *(" + filterSymop.trim() + ")", lst, -1);
205     }
206     spaceGroup.setFinalOperations(atoms, iAtomFirst, noSymmetryCount,
207         doNormalize);
208   }
209 
210   @Override
getSpaceGroupOperation(int i)211   public M4 getSpaceGroupOperation(int i) {
212     return (spaceGroup == null || spaceGroup.operations == null // bio
213         || i >= spaceGroup.operations.length ? null
214         : spaceGroup.finalOperations == null ? spaceGroup.operations[i]
215             : spaceGroup.finalOperations[i]);
216   }
217 
218   @Override
getSpaceGroupOperationRaw(int i)219   public M4 getSpaceGroupOperationRaw(int i) {
220     return spaceGroup.getRawOperation(i);
221   }
222 
223   @Override
getSpaceGroupXyz(int i, boolean doNormalize)224   public String getSpaceGroupXyz(int i, boolean doNormalize) {
225     return spaceGroup.getXyz(i, doNormalize);
226   }
227 
228   @Override
newSpaceGroupPoint(int i, P3 atom1, P3 atom2, int transX, int transY, int transZ, M4 o)229   public void newSpaceGroupPoint(int i, P3 atom1, P3 atom2, int transX,
230                                  int transY, int transZ, M4 o) {
231     if (o == null && spaceGroup.finalOperations == null) {
232       SymmetryOperation op = spaceGroup.operations[i];
233       // temporary spacegroups don't have to have finalOperations
234       if (!op.isFinalized)
235         op.doFinalize();
236       SymmetryOperation.newPoint(op, atom1, atom2, transX, transY, transZ);
237       return;
238     }
239     SymmetryOperation.newPoint((o == null ? spaceGroup.finalOperations[i] : o), atom1, atom2, transX, transY, transZ);
240   }
241 
242   @Override
rotateAxes(int iop, V3[] axes, P3 ptTemp, M3 mTemp)243   public V3[] rotateAxes(int iop, V3[] axes, P3 ptTemp, M3 mTemp) {
244     return (iop == 0 ? axes : spaceGroup.finalOperations[iop].rotateAxes(axes,
245         unitCell, ptTemp, mTemp));
246   }
247 
248   @Override
getSpaceGroupOperationCode(int iOp)249   public String getSpaceGroupOperationCode(int iOp) {
250     return spaceGroup.operations[iOp].subsystemCode;
251   }
252 
253   @Override
setTimeReversal(int op, int val)254   public void setTimeReversal(int op, int val) {
255     spaceGroup.operations[op].setTimeReversal(val);
256   }
257 
258   @Override
getSpinOp(int op)259   public float getSpinOp(int op) {
260     return spaceGroup.operations[op].getMagneticOp();
261   }
262 
263   @Override
addLatticeVectors(Lst<float[]> lattvecs)264   public boolean addLatticeVectors(Lst<float[]> lattvecs) {
265     return spaceGroup.addLatticeVectors(lattvecs);
266   }
267 
268   @Override
getLatticeOp()269   public int getLatticeOp() {
270     return spaceGroup.latticeOp;
271   }
272 
273   @Override
getOperationRsVs(int iop)274   public Matrix getOperationRsVs(int iop) {
275     return (spaceGroup.finalOperations == null ? spaceGroup.operations
276         : spaceGroup.finalOperations)[iop].rsvs;
277   }
278 
279   @Override
getSiteMultiplicity(P3 pt)280   public int getSiteMultiplicity(P3 pt) {
281     return spaceGroup.getSiteMultiplicity(pt, unitCell);
282   }
283 
284   @Override
285   /**
286    * @param rot is a full (3+d)x(3+d) array of epsilons
287    * @param trans is a (3+d)x(1) array of translations
288    * @return Jones-Faithful representation
289    */
addOp(String code, Matrix rs, Matrix vs, Matrix sigma)290   public String addOp(String code, Matrix rs, Matrix vs, Matrix sigma) {
291     spaceGroup.isSSG = true;
292     String s = SymmetryOperation.getXYZFromRsVs(rs, vs, false);
293     int i = spaceGroup.addSymmetry(s, -1, true);
294     spaceGroup.operations[i].setSigma(code, sigma);
295     return s;
296   }
297 
298   @Override
getMatrixFromString(String xyz, float[] rotTransMatrix, boolean allowScaling, int modDim)299   public String getMatrixFromString(String xyz, float[] rotTransMatrix,
300                                     boolean allowScaling, int modDim) {
301     return SymmetryOperation.getMatrixFromString(null, xyz, rotTransMatrix,
302         allowScaling);
303   }
304 
305   /// symmetryInfo ////
306 
307   // symmetryInfo is (inefficiently) passed to Jmol from the adapter
308   // in lieu of saving the actual unit cell read in the reader. Not perfect.
309   // The idea was to be able to create the unit cell from "scratch" independent
310   // of the reader.
311 
312   @Override
getSpaceGroupName()313   public String getSpaceGroupName() {
314     return (symmetryInfo != null ? symmetryInfo.sgName
315         : spaceGroup != null ? spaceGroup.getName() : unitCell != null
316             && unitCell.name.length() > 0 ? "cell=" + unitCell.name : "");
317   }
318 
319   @Override
setSpaceGroupName(String name)320   public void setSpaceGroupName(String name) {
321     if (spaceGroup != null)
322       spaceGroup.setName(name);
323   }
324 
325   @Override
getSpaceGroupOperationCount()326   public int getSpaceGroupOperationCount() {
327     return (symmetryInfo != null ? symmetryInfo.symmetryOperations.length
328         : spaceGroup != null && spaceGroup.finalOperations != null ? spaceGroup.finalOperations.length
329             : 0);
330   }
331 
332   @Override
getLatticeType()333   public String getLatticeType() {
334     return (symmetryInfo != null ? symmetryInfo.latticeType
335         : spaceGroup == null ? "P"
336             : spaceGroup.latticeType);
337   }
338 
339   @Override
setLatticeType(String type)340   public void setLatticeType(String type) {
341     if (spaceGroup != null)
342       spaceGroup.latticeType = type;
343   }
344 
345   @Override
getIntTableNumber()346   public String getIntTableNumber() {
347     return (symmetryInfo != null ? symmetryInfo.intlTableNo
348         : spaceGroup == null ? null
349             : spaceGroup.intlTableNumber);
350   }
351 
352   @Override
getCoordinatesAreFractional()353   public boolean getCoordinatesAreFractional() {
354     return symmetryInfo == null || symmetryInfo.coordinatesAreFractional;
355   }
356 
357   @Override
getCellRange()358   public int[] getCellRange() {
359     return symmetryInfo == null ? null : symmetryInfo.cellRange;
360   }
361 
362   @Override
getSymmetryInfoStr()363   public String getSymmetryInfoStr() {
364     return (symmetryInfo == null ? "" : symmetryInfo.infoStr);
365   }
366 
367   @Override
getSymmetryOperations()368   public M4[] getSymmetryOperations() {
369     if (symmetryInfo != null)
370       return symmetryInfo.symmetryOperations;
371     if (spaceGroup == null)
372       spaceGroup = SpaceGroup.getNull(true, false, true);
373     return spaceGroup.finalOperations;
374   }
375 
376   @Override
isSimple()377   public boolean isSimple() {
378     return (symmetryInfo == null || symmetryInfo.symmetryOperations == null);
379   }
380 
381   /**
382    * Set space group and unit cell from the auxiliary info generated by the
383    * model adapter.
384    *
385    */
386   @SuppressWarnings("unchecked")
387   @Override
setSymmetryInfo(int modelIndex, Map<String, Object> modelAuxiliaryInfo, float[] unitCellParams)388   public SymmetryInterface setSymmetryInfo(int modelIndex,
389                                            Map<String, Object> modelAuxiliaryInfo,
390                                            float[] unitCellParams) {
391     symmetryInfo = new SymmetryInfo();
392     float[] params = symmetryInfo.setSymmetryInfo(modelAuxiliaryInfo,
393         unitCellParams);
394     if (params != null) {
395       setUnitCell(params, modelAuxiliaryInfo.containsKey("jmolData"));
396       unitCell.moreInfo = (Lst<String>) modelAuxiliaryInfo
397           .get("moreUnitCellInfo");
398       modelAuxiliaryInfo.put("infoUnitCell", getUnitCellAsArray(false));
399       setOffsetPt((T3) modelAuxiliaryInfo.get("unitCellOffset"));
400       M3 matUnitCellOrientation = (M3) modelAuxiliaryInfo
401           .get("matUnitCellOrientation");
402       if (matUnitCellOrientation != null)
403         initializeOrientation(matUnitCellOrientation);
404       if (Logger.debugging)
405         Logger.debug("symmetryInfos[" + modelIndex + "]:\n"
406             + unitCell.dumpInfo(true));
407     }
408     return this;
409   }
410 
411   // UnitCell methods
412 
413   @Override
haveUnitCell()414   public boolean haveUnitCell() {
415     return (unitCell != null);
416   }
417 
418   @Override
checkUnitCell(SymmetryInterface uc, P3 cell, P3 ptTemp, boolean isAbsolute)419   public boolean checkUnitCell(SymmetryInterface uc, P3 cell, P3 ptTemp,
420                                boolean isAbsolute) {
421     uc.toFractional(ptTemp, isAbsolute);
422     // {1 1 1} here is the original cell
423     return (ptTemp.x >= cell.x - 1f - SimpleUnitCell.SLOP && ptTemp.x <= cell.x + SimpleUnitCell.SLOP
424         && ptTemp.y >= cell.y - 1f - SimpleUnitCell.SLOP && ptTemp.y <= cell.y + SimpleUnitCell.SLOP
425         && ptTemp.z >= cell.z - 1f - SimpleUnitCell.SLOP && ptTemp.z <= cell.z + SimpleUnitCell.SLOP);
426   }
427 
428   @Override
setUnitCell(float[] unitCellParams, boolean setRelative)429   public void setUnitCell(float[] unitCellParams, boolean setRelative) {
430     unitCell = UnitCell.fromParams(unitCellParams, setRelative);
431   }
432 
433   @Override
unitCellEquals(SymmetryInterface uc2)434   public boolean unitCellEquals(SymmetryInterface uc2) {
435     return ((Symmetry) (uc2)).unitCell.isSameAs(unitCell);
436   }
437 
438   @Override
getUnitCellState()439   public String getUnitCellState() {
440     return (unitCell == null ? "" : unitCell.getState());
441   }
442 
443   @Override
getMoreInfo()444   public Lst<String> getMoreInfo() {
445     return unitCell.moreInfo;
446   }
447 
getUnitsymmetryInfo()448   public String getUnitsymmetryInfo() {
449     // not used in Jmol?
450     return unitCell.dumpInfo(false);
451   }
452 
453   @Override
initializeOrientation(M3 mat)454   public void initializeOrientation(M3 mat) {
455     unitCell.initOrientation(mat);
456   }
457 
458   @Override
unitize(T3 ptFrac)459   public void unitize(T3 ptFrac) {
460     unitCell.unitize(ptFrac);
461   }
462 
463   @Override
toUnitCell(T3 pt, T3 offset)464   public void toUnitCell(T3 pt, T3 offset) {
465     unitCell.toUnitCell(pt, offset);
466   }
467 
468   @Override
toSupercell(P3 fpt)469   public P3 toSupercell(P3 fpt) {
470     return unitCell.toSupercell(fpt);
471   }
472 
473   @Override
toFractional(T3 pt, boolean ignoreOffset)474   public void toFractional(T3 pt, boolean ignoreOffset) {
475     if (!isBio)
476       unitCell.toFractional(pt, ignoreOffset);
477   }
478 
479   @Override
toFractionalM(M4 m)480   public void toFractionalM(M4 m) {
481     if (!isBio)
482       unitCell.toFractionalM(m);
483   }
484 
485   @Override
toCartesian(T3 fpt, boolean ignoreOffset)486   public void toCartesian(T3 fpt, boolean ignoreOffset) {
487     if (!isBio)
488       unitCell.toCartesian(fpt, ignoreOffset);
489   }
490 
491   @Override
getUnitCellParams()492   public float[] getUnitCellParams() {
493     return unitCell.getUnitCellParams();
494   }
495 
496   @Override
getUnitCellAsArray(boolean vectorsOnly)497   public float[] getUnitCellAsArray(boolean vectorsOnly) {
498     return unitCell.getUnitCellAsArray(vectorsOnly);
499   }
500 
501   @Override
getTensor(Viewer vwr, float[] parBorU)502   public Tensor getTensor(Viewer vwr, float[] parBorU) {
503     if (parBorU == null)
504       return null;
505     if (unitCell == null)
506       unitCell = UnitCell.fromParams(new float[] { 1, 1, 1, 90, 90, 90 }, true);
507     return unitCell.getTensor(vwr, parBorU);
508   }
509 
510   @Override
getUnitCellVerticesNoOffset()511   public P3[] getUnitCellVerticesNoOffset() {
512     return unitCell.getVertices();
513   }
514 
515   @Override
getCartesianOffset()516   public P3 getCartesianOffset() {
517     return unitCell.getCartesianOffset();
518   }
519 
520   @Override
getFractionalOffset()521   public P3 getFractionalOffset() {
522     return unitCell.getFractionalOffset();
523   }
524 
525   @Override
setOffsetPt(T3 pt)526   public void setOffsetPt(T3 pt) {
527     unitCell.setOffset(pt);
528   }
529 
530   @Override
setOffset(int nnn)531   public void setOffset(int nnn) {
532     P3 pt = new P3();
533     SimpleUnitCell.ijkToPoint3f(nnn, pt, 0, 0);
534     unitCell.setOffset(pt);
535   }
536 
537   @Override
getUnitCellMultiplier()538   public T3 getUnitCellMultiplier() {
539     return unitCell.getUnitCellMultiplier();
540   }
541 
542   @Override
getCanonicalCopy(float scale, boolean withOffset)543   public P3[] getCanonicalCopy(float scale, boolean withOffset) {
544     return unitCell.getCanonicalCopy(scale, withOffset);
545   }
546 
547   @Override
getUnitCellInfoType(int infoType)548   public float getUnitCellInfoType(int infoType) {
549     return unitCell.getInfo(infoType);
550   }
551 
552   @Override
getUnitCellInfo()553   public String getUnitCellInfo() {
554     return unitCell.dumpInfo(false);
555   }
556 
557   @Override
isSlab()558   public boolean isSlab() {
559     return unitCell.isSlab();
560   }
561 
562   @Override
isPolymer()563   public boolean isPolymer() {
564     return unitCell.isPolymer();
565   }
566 
567   @Override
checkDistance(P3 f1, P3 f2, float distance, float dx, int iRange, int jRange, int kRange, P3 ptOffset)568   public boolean checkDistance(P3 f1, P3 f2, float distance, float dx,
569                                int iRange, int jRange, int kRange, P3 ptOffset) {
570     return unitCell.checkDistance(f1, f2, distance, dx, iRange, jRange, kRange,
571         ptOffset);
572   }
573 
574   @Override
getUnitCellVectors()575   public P3[] getUnitCellVectors() {
576     return unitCell.getUnitCellVectors();
577   }
578 
579   /**
580    * @param oabc  [ptorigin, va, vb, vc]
581    * @param setRelative a flag only set true for IsosurfaceMesh
582    * @param name
583    * @return this SymmetryInterface
584    */
585   @Override
getUnitCell(T3[] oabc, boolean setRelative, String name)586   public SymmetryInterface getUnitCell(T3[] oabc, boolean setRelative,
587                                        String name) {
588     if (oabc == null)
589       return null;
590     unitCell = UnitCell.fromOABC(oabc, setRelative);
591     if (name != null)
592       unitCell.name = name;
593     return this;
594   }
595 
596   @Override
isSupercell()597   public boolean isSupercell() {
598     return unitCell.isSupercell();
599   }
600 
601   @Override
notInCentroid(ModelSet modelSet, BS bsAtoms, int[] minmax)602   public BS notInCentroid(ModelSet modelSet, BS bsAtoms, int[] minmax) {
603     try {
604       BS bsDelete = new BS();
605       int iAtom0 = bsAtoms.nextSetBit(0);
606       JmolMolecule[] molecules = modelSet.getMolecules();
607       int moleculeCount = molecules.length;
608       Atom[] atoms = modelSet.at;
609       boolean isOneMolecule = (molecules[moleculeCount - 1].firstAtomIndex == modelSet.am[atoms[iAtom0].mi].firstAtomIndex);
610       P3 center = new P3();
611       boolean centroidPacked = (minmax[6] == 1);
612       nextMol: for (int i = moleculeCount; --i >= 0
613           && bsAtoms.get(molecules[i].firstAtomIndex);) {
614         BS bs = molecules[i].atomList;
615         center.set(0, 0, 0);
616         int n = 0;
617         for (int j = bs.nextSetBit(0); j >= 0; j = bs.nextSetBit(j + 1)) {
618           if (isOneMolecule || centroidPacked) {
619             center.setT(atoms[j]);
620             if (isNotCentroid(center, 1, minmax, centroidPacked)) {
621               if (isOneMolecule)
622                 bsDelete.set(j);
623             } else if (!isOneMolecule) {
624               continue nextMol;
625             }
626           } else {
627             center.add(atoms[j]);
628             n++;
629           }
630         }
631         if (centroidPacked || n > 0 && isNotCentroid(center, n, minmax, false))
632           bsDelete.or(bs);
633       }
634       return bsDelete;
635     } catch (Exception e) {
636       return null;
637     }
638   }
639 
isNotCentroid(P3 center, int n, int[] minmax, boolean centroidPacked)640   private boolean isNotCentroid(P3 center, int n, int[] minmax,
641                                 boolean centroidPacked) {
642     center.scale(1f / n);
643     toFractional(center, false);
644     // we have to disallow just a tiny slice of atoms due to rounding errors
645     // so  -0.000001 is OK, but 0.999991 is not.
646     if (centroidPacked)
647       return (center.x + 0.000005f <= minmax[0]
648           || center.x - 0.000005f > minmax[3]
649           || center.y + 0.000005f <= minmax[1]
650           || center.y - 0.000005f > minmax[4]
651           || center.z + 0.000005f <= minmax[2] || center.z - 0.000005f > minmax[5]);
652 
653     return (center.x + 0.000005f <= minmax[0]
654         || center.x + 0.00005f > minmax[3] || center.y + 0.000005f <= minmax[1]
655         || center.y + 0.00005f > minmax[4] || center.z + 0.000005f <= minmax[2] || center.z + 0.00005f > minmax[5]);
656   }
657 
658   // info
659 
660   private SymmetryDesc desc;
661 
getDesc(ModelSet modelSet)662   private SymmetryDesc getDesc(ModelSet modelSet) {
663     return (desc == null ? (desc = ((SymmetryDesc) Interface.getInterface(
664         "org.jmol.symmetry.SymmetryDesc", modelSet.vwr, "eval"))) : desc).set(modelSet);
665   }
666 
667   @Override
getSymmetryInfoAtom(ModelSet modelSet, int iatom, String xyz, int op, P3 translation, P3 pt, P3 pt2, String id, int type, float scaleFactor, int nth, int options)668   public Object getSymmetryInfoAtom(ModelSet modelSet, int iatom, String xyz,
669                                     int op, P3 translation, P3 pt, P3 pt2, String id, int type, float scaleFactor, int nth, int options) {
670     return getDesc(modelSet).getSymopInfo(iatom, xyz, op, translation, pt,
671         pt2, id, type, scaleFactor, nth, options);
672   }
673 
674   @Override
getSpaceGroupInfo(ModelSet modelSet, String sgName, int modelIndex, boolean isFull, float[] cellParams)675   public Map<String, Object> getSpaceGroupInfo(ModelSet modelSet, String sgName, int modelIndex, boolean isFull, float[] cellParams) {
676     boolean isForModel = (sgName == null);
677     if (sgName == null) {
678       Map<String, Object> info = modelSet.getModelAuxiliaryInfo(modelSet.vwr.am.cmi);
679       if (info != null)
680         sgName = (String) info.get("spaceGroup");
681     }
682     SymmetryInterface cellInfo = null;
683     if (cellParams != null) {
684       cellInfo = new Symmetry();
685       cellInfo.setUnitCell(cellParams, false);
686     }
687     return getDesc(modelSet).getSpaceGroupInfo(this, modelIndex, sgName, 0, null, null,
688         null, 0, -1, isFull, isForModel, 0, cellInfo);
689   }
690 
691 
692   @Override
fcoord(T3 p)693   public String fcoord(T3 p) {
694     return SymmetryOperation.fcoord(p);
695   }
696 
697   @Override
getV0abc(Object def)698   public T3[] getV0abc(Object def) {
699     return (unitCell == null ? null : unitCell.getV0abc(def));
700   }
701 
702   @Override
getQuaternionRotation(String abc)703   public Quat getQuaternionRotation(String abc) {
704     return (unitCell == null ? null : unitCell.getQuaternionRotation(abc));
705   }
706 
707   @Override
getFractionalOrigin()708   public T3 getFractionalOrigin() {
709     return unitCell.getFractionalOrigin();
710   }
711 
712   @Override
getState(SB commands)713   public boolean getState(SB commands) {
714     T3 pt = getFractionalOffset();
715     boolean loadUC = false;
716     if (pt != null && (pt.x != 0 || pt.y != 0 || pt.z != 0)) {
717       commands.append("; set unitcell ").append(Escape.eP(pt));
718       loadUC = true;
719     }
720     pt = getUnitCellMultiplier();
721     if (pt != null) {
722       commands.append("; set unitcell ").append(SimpleUnitCell.escapeMultiplier(pt));
723       loadUC = true;
724     }
725     return loadUC;
726   }
727 
728   @Override
getIterator(Viewer vwr, Atom atom, Atom[] atoms, BS bsAtoms, float radius)729   public AtomIndexIterator getIterator(Viewer vwr, Atom atom, Atom[] atoms,
730                                        BS bsAtoms, float radius) {
731     return ((UnitCellIterator) Interface.getInterface("org.jmol.symmetry.UnitCellIterator", vwr, "script"))
732         .set(this, atom, atoms, bsAtoms, radius);
733   }
734 
735   @Override
toFromPrimitive(boolean toPrimitive, char type, T3[] oabc, M3 primitiveToCrystal)736   public boolean toFromPrimitive(boolean toPrimitive, char type, T3[] oabc, M3 primitiveToCrystal) {
737     if (unitCell == null)
738       unitCell = UnitCell.fromOABC(oabc, false);
739     return unitCell.toFromPrimitive(toPrimitive, type, oabc, primitiveToCrystal);
740   }
741 
742   @Override
generateCrystalClass(P3 pt0)743   public Lst<P3> generateCrystalClass(P3 pt0) {
744      M4[] ops = getSymmetryOperations();
745     Lst<P3> lst = new Lst<P3>();
746     boolean isRandom = (pt0 == null);
747     float rand1=0,rand2=0,rand3=0;
748     if (isRandom) {
749       rand1 = (float) Math.E;
750       rand2 = (float) Math.PI;
751       rand3 = (float) Math.log10(2000);
752       pt0 = P3.new3(rand1 + 1, rand2 + 2, rand3 + 3);
753     } else {
754       pt0 = P3.newP(pt0);
755     }
756     if (ops == null || unitCell == null) {
757       lst.addLast(pt0);
758     } else {
759       unitCell.toFractional(pt0, true); // ignoreOffset
760       P3 pt1 = null;
761       P3 pt2 = null;
762       P3 pt3 = null;
763       if (isRandom) {
764         pt1 = P3.new3(rand2 + 4, rand3 + 5, rand1 + 6);
765         unitCell.toFractional(pt1, true); // ignoreOffset
766         pt2 = P3.new3(rand3 + 7, rand1 + 8, rand2 + 9);
767         unitCell.toFractional(pt2, true); // ignoreOffset
768       }
769       Bspt bspt = new Bspt(3, 0);
770       CubeIterator iter = bspt.allocateCubeIterator();
771       P3 pt = new P3();
772       out: for (int i = ops.length; --i >= 0;) {
773         ops[i].rotate2(pt0, pt);
774         iter.initialize(pt, 0.001f, false);
775         if (iter.hasMoreElements())
776           continue out;
777         P3 ptNew = P3.newP(pt);
778         lst.addLast(ptNew);
779         bspt.addTuple(ptNew);
780         if (isRandom) {
781           if (pt2 != null) {
782             pt3 = new P3();
783             ops[i].rotate2(pt2, pt3);
784             lst.addLast(pt3);
785           }
786           if (pt1 != null) {
787             // pt2 is necessary to distinguish between Cs, Ci, and C1
788             pt3 = new P3();
789             ops[i].rotate2(pt1, pt3);
790             lst.addLast(pt3);
791           }
792         }
793       }
794       for (int j = lst.size(); --j >= 0;)
795         unitCell.toCartesian(lst.get(j), true); // ignoreOffset
796     }
797     return lst;
798   }
799 
800   @Override
calculateCIPChiralityForAtoms(Viewer vwr, BS bsAtoms)801   public void calculateCIPChiralityForAtoms(Viewer vwr, BS bsAtoms) {
802     vwr.setCursor(GenericPlatform.CURSOR_WAIT);
803     CIPChirality cip = getCIPChirality(vwr);
804     String dataClass = (vwr.getBoolean(T.testflag1) ? "CIPData" : "CIPDataTracker");
805     CIPData data = ((CIPData) Interface.getInterface("org.jmol.symmetry." + dataClass, vwr, "script")).set(vwr, bsAtoms);
806     data.setRule6Full(vwr.getBoolean(T.ciprule6full));
807     cip.getChiralityForAtoms(data);
808     vwr.setCursor(GenericPlatform.CURSOR_DEFAULT);
809   }
810 
811   @Override
calculateCIPChiralityForSmiles(Viewer vwr, String smiles)812   public String[] calculateCIPChiralityForSmiles(Viewer vwr, String smiles) throws Exception {
813     vwr.setCursor(GenericPlatform.CURSOR_WAIT);
814     CIPChirality cip = getCIPChirality(vwr);
815     CIPDataSmiles data = ((CIPDataSmiles) Interface.getInterface("org.jmol.symmetry.CIPDataSmiles", vwr, "script")).setAtomsForSmiles(vwr, smiles);
816     cip.getChiralityForAtoms(data);
817     vwr.setCursor(GenericPlatform.CURSOR_DEFAULT);
818        return data.getSmilesChiralityArray();
819   }
820 
821   CIPChirality cip;
822 
getCIPChirality(Viewer vwr)823   private CIPChirality getCIPChirality(Viewer vwr) {
824     return (cip == null ? (cip = ((CIPChirality) Interface.getInterface("org.jmol.symmetry.CIPChirality", vwr, "script"))) : cip);
825   }
826 
827 
828   /**
829    * return a conventional lattice from a primitive
830    *
831    * @param latticeType
832    *        "A" "B" "C" "R" etc.
833    * @return [origin va vb vc]
834    */
835   @Override
getConventionalUnitCell(String latticeType, M3 primitiveToCrystal)836   public T3[] getConventionalUnitCell(String latticeType,
837                                       M3 primitiveToCrystal) {
838     return (unitCell == null || latticeType == null ? null
839         : unitCell.getConventionalUnitCell(latticeType, primitiveToCrystal));
840   }
841 
842   @Override
getUnitCellInfoMap()843   public Map<String, Object> getUnitCellInfoMap() {
844     return (unitCell == null ? null : unitCell.getInfo());
845   }
846 
847   @Override
setUnitCell(Symmetry uc)848   public void setUnitCell(Symmetry uc) {
849     unitCell = UnitCell.cloneUnitCell(uc.unitCell);
850   }
851 
852 }
853