1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2007-10-14 12:33:20 -0500 (Sun, 14 Oct 2007) $ 4 * $Revision: 8408 $ 5 6 * 7 * Copyright (C) 2003-2005 The Jmol Development Team 8 * 9 * Contact: jmol-developers@lists.sf.net 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 24 */ 25 26 package org.jmol.modelset; 27 28 import java.util.Arrays; 29 import java.util.Comparator; 30 import java.util.Hashtable; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 34 import org.jmol.api.Interface; 35 import org.jmol.api.JmolDataManager; 36 import org.jmol.api.JmolEnvCalc; 37 import org.jmol.api.JmolModulationSet; 38 import org.jmol.api.SymmetryInterface; 39 import org.jmol.atomdata.AtomData; 40 import org.jmol.atomdata.RadiusData; 41 import org.jmol.atomdata.RadiusData.EnumType; 42 import org.jmol.bspt.Bspf; 43 import org.jmol.c.PAL; 44 import org.jmol.c.VDW; 45 import org.jmol.modelsetbio.BioModelSet; 46 import org.jmol.script.T; 47 import org.jmol.util.BSUtil; 48 import org.jmol.util.Edge; 49 import org.jmol.util.Elements; 50 import org.jmol.util.GData; 51 import org.jmol.util.Logger; 52 import org.jmol.util.Parser; 53 import org.jmol.util.Tensor; 54 import org.jmol.util.Vibration; 55 import org.jmol.viewer.JC; 56 import org.jmol.viewer.Viewer; 57 58 import javajs.util.A4; 59 import javajs.util.AU; 60 import javajs.util.BS; 61 import javajs.util.Lst; 62 import javajs.util.M3; 63 import javajs.util.Measure; 64 import javajs.util.P3; 65 import javajs.util.P4; 66 import javajs.util.PT; 67 import javajs.util.Quat; 68 import javajs.util.T3; 69 import javajs.util.V3; 70 71 abstract public class AtomCollection { 72 73 private final static float almost180 = (float) Math.PI * 0.95f; 74 private final static float sqrt3_2 = (float) (Math.sqrt(3) / 2); 75 private final static V3 vRef = V3.new3(3.14159f, 2.71828f, 1.41421f); 76 77 public Viewer vwr; 78 protected GData g3d; 79 /** 80 * If any model in the collection is a BioModel, then 81 * it is also indicated here as a "bioModelset", meaning 82 * 83 */ 84 public BioModelSet bioModelset; 85 86 public Atom[] at; 87 getAtom(int iatom)88 public Atom getAtom(int iatom) { 89 return (iatom >= 0 && iatom < at.length ? at[iatom] : null); 90 } 91 92 public int ac; 93 94 public Trajectory trajectory; 95 96 protected SymmetryInterface pointGroup; 97 98 //////////////////// 99 100 private LabelToken labeler; 101 102 // the maximum BondingRadius seen in this set of atoms 103 // used in autobonding 104 protected float maxBondingRadius = PT.FLOAT_MIN_SAFE; 105 private float maxVanderwaalsRadius = PT.FLOAT_MIN_SAFE; 106 107 private boolean hasBfactorRange; 108 private int bfactor100Lo; 109 private int bfactor100Hi; 110 111 private boolean haveBSVisible, haveBSClickable; 112 113 private BS bsSurface; 114 private int nSurfaceAtoms; 115 private int surfaceDistanceMax; 116 117 // protected P3 averageAtomPoint; 118 119 protected boolean haveChirality; 120 121 /** 122 * Binary Space Partitioning Forest 123 */ 124 protected Bspf bspf = null; 125 126 protected boolean preserveState = true; 127 public boolean canSkipLoad = true; 128 129 public boolean haveStraightness; 130 131 private BS bsHidden; 132 public BS bsVisible, bsClickable, bsModulated; 133 134 //////////////////////////////////////////////////////////////// 135 // these may or may not be allocated 136 // depending upon the AtomSetCollection characteristics 137 // 138 // used by Atom: 139 // 140 141 public Object[][] atomTensorList; // specifically now for {*}.adpmin {*}.adpmax 142 public Map<String, Lst<Object>> atomTensors; 143 144 protected int[] surfaceDistance100s; 145 146 ///// SETTABLE atomic properties /////////////// 147 // 148 // The following references must be made if a new property is added to this list: 149 // 150 // 1) Add a new static value to the following TAINT_XXXX list. 151 // 2) Add its T.map name to the list in setupAC(). 152 // 3) Follow all references to both atomType (for a string), formalCharge (for an int), or partialCharge (for a float) 153 // These will include a reference in setAPa() and setAtomData() here and 154 // to getAtomicPropertyStateBuffer() in viewer.StateCreator 155 // 4) add the settable attribute to the static int XXXX definition in script.T 156 // 5) check that {*}.xxxx = ....; write state test.spt, and script test.spt all work properly 157 158 public BS[] tainted; 159 public static String[] userSettableValues; 160 setupAC()161 protected void setupAC() { 162 bsHidden = new BS(); 163 bsVisible = new BS(); 164 bsClickable = new BS(); 165 // this allows the Google Closure compiler to skip all the TAINTED defs in Clazz.defineStatics 166 if (userSettableValues == null) 167 userSettableValues = ("atomName atomType coord element formalCharge hydrophobicity " + 168 "ionic occupancy partialCharge temperature valence vanderWaals vibrationVector " + 169 "atomNo seqID resNo chain").split(" "); 170 } 171 172 final public static int TAINT_ATOMNAME = 0; 173 final public static int TAINT_ATOMTYPE = 1; 174 final public static int TAINT_COORD = 2; 175 final public static int TAINT_ELEMENT = 3; 176 final public static int TAINT_FORMALCHARGE = 4; 177 final public static int TAINT_HYDROPHOBICITY = 5; 178 final public static int TAINT_BONDINGRADIUS = 6; 179 final public static int TAINT_OCCUPANCY = 7; 180 final public static int TAINT_PARTIALCHARGE = 8; 181 final public static int TAINT_TEMPERATURE = 9; 182 final public static int TAINT_VALENCE = 10; 183 final public static int TAINT_VANDERWAALS = 11; 184 final public static int TAINT_VIBRATION = 12; 185 final public static int TAINT_ATOMNO = 13; 186 final public static int TAINT_SEQID = 14; 187 final public static int TAINT_RESNO = 15; 188 final public static int TAINT_CHAIN = 16; 189 final public static int TAINT_MAX = 17; // 1 more than last number, above 190 191 192 String[] atomNames; 193 String[] atomTypes; 194 // String[] chainIDs; not necessary, as there is a place for this already in atom.group.chain 195 int[] atomSerials; 196 int[] atomResnos; 197 int[] atomSeqIDs; 198 float[] dssrData; 199 public Vibration[] vibrations; 200 public float[] occupancies; 201 short[] bfactor100s; 202 float[] partialCharges; 203 float[] bondingRadii; 204 float[] hydrophobicities; 205 public BS bsPartialCharges; 206 207 releaseModelSetAC()208 protected void releaseModelSetAC() { 209 at = null; 210 vwr = null; 211 g3d = null; 212 bspf = null; 213 surfaceDistance100s = null; 214 bsSurface = null; 215 tainted = null; 216 217 atomNames = null; 218 atomTypes = null; 219 atomResnos = null; 220 dssrData = null; 221 atomSerials = null; 222 atomSeqIDs = null; 223 vibrations = null; 224 occupancies = null; 225 bfactor100s = null; 226 resetPartialCharges(); 227 bondingRadii = null; 228 atomTensors = null; 229 } 230 mergeAtomArrays(AtomCollection mergeModelSet)231 protected void mergeAtomArrays(AtomCollection mergeModelSet) { 232 tainted = mergeModelSet.tainted; 233 atomNames = mergeModelSet.atomNames; 234 atomTypes = mergeModelSet.atomTypes; 235 atomResnos = mergeModelSet.atomResnos; 236 dssrData = mergeModelSet.dssrData; 237 atomSerials = mergeModelSet.atomSerials; 238 atomSeqIDs = mergeModelSet.atomSeqIDs; 239 vibrations = mergeModelSet.vibrations; 240 occupancies = mergeModelSet.occupancies; 241 bfactor100s = mergeModelSet.bfactor100s; 242 bondingRadii = mergeModelSet.bondingRadii; 243 partialCharges = mergeModelSet.partialCharges; 244 bsPartialCharges = mergeModelSet.bsPartialCharges; 245 atomTensors = mergeModelSet.atomTensors; 246 atomTensorList = mergeModelSet.atomTensorList; 247 bsModulated = mergeModelSet.bsModulated; 248 haveStraightness = false; 249 surfaceDistance100s = null; 250 } 251 getAtomPointVector(BS bs)252 public Lst<P3> getAtomPointVector(BS bs) { 253 Lst<P3> v = new Lst<P3>(); 254 int n = ac; 255 if (bs != null) { 256 for (int i = bs.nextSetBit(0); i >= 0 && i < n; i = bs.nextSetBit(i+1)) { 257 v.addLast(at[i]); 258 } 259 } 260 return v; 261 } 262 modelSetHasVibrationVectors()263 public boolean modelSetHasVibrationVectors(){ 264 return (vibrations != null); 265 } 266 getAtomTypes()267 public String[] getAtomTypes() { 268 return atomTypes; 269 } 270 getPartialCharges()271 public float[] getPartialCharges() { 272 return partialCharges; 273 } 274 getBondingRadii()275 public float[] getBondingRadii() { 276 return bondingRadii; 277 } 278 getBFactors()279 public short[] getBFactors() { 280 return bfactor100s; 281 } 282 getHydrophobicity()283 public float[] getHydrophobicity() { 284 return hydrophobicities; 285 } 286 287 setBsHidden(BS bs)288 public void setBsHidden(BS bs) { //from selection manager 289 bsHidden = bs; 290 } 291 isAtomHidden(int iAtom)292 public boolean isAtomHidden(int iAtom) { 293 return bsHidden.get(iAtom); 294 } 295 296 //////////// atoms ////////////// 297 getLabeler()298 public LabelToken getLabeler() { 299 // prevents JavaScript from requiring LabelToken upon core load 300 // will be abbreviated to JM., so don't use getOption here. 301 return (labeler == null ? labeler = (LabelToken) Interface.getInterface("org.jmol.modelset.LabelToken", vwr, "ms") : labeler); 302 } 303 getAtomInfo(int i, String format, P3 ptTemp)304 public String getAtomInfo(int i, String format, P3 ptTemp) { 305 return (format == null ? at[i].getInfo() 306 : getLabeler().formatLabel(vwr, at[i], format, ptTemp)); 307 } 308 getElementName(int i)309 public String getElementName(int i) { 310 return Elements.elementNameFromNumber(at[i] 311 .getAtomicAndIsotopeNumber()); 312 } 313 getQuaternion(int i, char qtype)314 public Quat getQuaternion(int i, char qtype) { 315 return (i < 0 ? null : at[i].group.getQuaternion(qtype)); 316 } 317 getFirstAtomIndexFromAtomNumber(int atomNumber, BS bsVisibleFrames)318 public int getFirstAtomIndexFromAtomNumber(int atomNumber, BS bsVisibleFrames) { 319 //definitely want FIRST (model) not last here 320 for (int i = 0; i < ac; i++) { 321 Atom atom = at[i]; 322 if (atom != null && atom.getAtomNumber() == atomNumber && bsVisibleFrames.get(atom.mi)) 323 return i; 324 } 325 return -1; 326 } 327 setFormalCharges(BS bs, int formalCharge)328 public void setFormalCharges(BS bs, int formalCharge) { 329 if (bs != null) { 330 resetPartialCharges(); 331 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { 332 at[i].setFormalCharge(formalCharge); 333 taintAtom(i, TAINT_FORMALCHARGE); 334 } 335 } 336 } 337 getAtomicCharges()338 public float[] getAtomicCharges() { 339 float[] charges = new float[ac]; 340 for (int i = ac; --i >= 0; ) 341 charges[i] = (at[i] == null ? 0 : at[i].getElementNumber()); 342 return charges; 343 } 344 getRadiusVdwJmol(Atom atom)345 protected float getRadiusVdwJmol(Atom atom) { 346 return Elements.getVanderwaalsMar(atom.getElementNumber(), 347 VDW.JMOL) / 1000f; 348 } 349 getMaxVanderwaalsRadius()350 public float getMaxVanderwaalsRadius() { 351 //Dots 352 if (maxVanderwaalsRadius == PT.FLOAT_MIN_SAFE) 353 findMaxRadii(); 354 return maxVanderwaalsRadius; 355 } 356 findMaxRadii()357 protected void findMaxRadii() { 358 float r; 359 for (int i = ac; --i >= 0;) { 360 Atom atom = at[i]; 361 if (atom == null) 362 continue; 363 if ((r = atom.getBondingRadius()) > maxBondingRadius) 364 maxBondingRadius = r; 365 if ((r = atom.getVanderwaalsRadiusFloat(vwr, VDW.AUTO)) > maxVanderwaalsRadius) 366 maxVanderwaalsRadius = r; 367 } 368 } 369 clearBfactorRange()370 public void clearBfactorRange() { 371 hasBfactorRange = false; 372 } 373 calcBfactorRange(BS bs)374 private void calcBfactorRange(BS bs) { 375 if (hasBfactorRange) 376 return; 377 bfactor100Lo = Integer.MAX_VALUE; 378 bfactor100Hi = Integer.MIN_VALUE; 379 if (bs == null) { 380 for (int i = 0; i < ac; i++) 381 setBf(i); 382 } else { 383 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) 384 setBf(i); 385 } 386 hasBfactorRange = true; 387 } 388 setBf(int i)389 private void setBf(int i) { 390 if (at[i] == null) 391 return; 392 int bf = at[i].getBfactor100(); 393 if (bf < bfactor100Lo) 394 bfactor100Lo = bf; 395 else if (bf > bfactor100Hi) 396 bfactor100Hi = bf; 397 } 398 getBfactor100Lo()399 public int getBfactor100Lo() { 400 //ColorManager 401 if (!hasBfactorRange) { 402 if (vwr.g.rangeSelected) { 403 calcBfactorRange(vwr.bsA()); 404 } else { 405 calcBfactorRange(null); 406 } 407 } 408 return bfactor100Lo; 409 } 410 getBfactor100Hi()411 public int getBfactor100Hi() { 412 //ColorManager 413 getBfactor100Lo(); 414 return bfactor100Hi; 415 } 416 getSurfaceDistanceMax()417 public int getSurfaceDistanceMax() { 418 //ColorManager, Eval 419 if (surfaceDistance100s == null) 420 calcSurfaceDistances(); 421 return surfaceDistanceMax; 422 } 423 calculateVolume(BS bs, VDW vType)424 public float calculateVolume(BS bs, VDW vType) { 425 // Eval 426 float volume = 0; 427 if (bs != null) 428 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) 429 volume += at[i].getVolume(vwr, vType); 430 return volume; 431 } 432 getSurfaceDistance100(int atomIndex)433 int getSurfaceDistance100(int atomIndex) { 434 //atom 435 if (nSurfaceAtoms == 0) 436 return -1; 437 if (surfaceDistance100s == null) 438 calcSurfaceDistances(); 439 return surfaceDistance100s[atomIndex]; 440 } 441 calcSurfaceDistances()442 private void calcSurfaceDistances() { 443 calculateSurface(null, -1); 444 } 445 calculateSurface(BS bsSelected, float envelopeRadius)446 public P3[] calculateSurface(BS bsSelected, float envelopeRadius) { 447 if (envelopeRadius < 0) 448 envelopeRadius = JC.ENC_CALC_MAX_DIST; 449 450 JmolEnvCalc ec = ((JmolEnvCalc) Interface.getOption("geodesic.EnvelopeCalculation", vwr, "ms")) 451 .set(vwr, ac, null); 452 ec.calculate(new RadiusData(null, envelopeRadius, EnumType.ABSOLUTE, null), 453 Float.MAX_VALUE, 454 bsSelected, BSUtil.copyInvert(bsSelected, ac), 455 false, false, false, true); 456 P3[] points = ec.getPoints(); 457 surfaceDistanceMax = 0; 458 bsSurface = ec.getBsSurfaceClone(); 459 surfaceDistance100s = new int[ac]; 460 nSurfaceAtoms = BSUtil.cardinalityOf(bsSurface); 461 if (nSurfaceAtoms == 0 || points == null || points.length == 0) 462 return points; 463 float radiusAdjust = (envelopeRadius == Float.MAX_VALUE ? 0 : envelopeRadius); 464 for (int i = 0; i < ac; i++) { 465 //surfaceDistance100s[i] = Integer.MIN_VALUE; 466 if (bsSurface.get(i) || at[i] == null) { 467 surfaceDistance100s[i] = 0; 468 } else { 469 float dMin = Float.MAX_VALUE; 470 Atom atom = at[i]; 471 for (int j = points.length; --j >= 0;) { 472 dMin = Math.min(Math.abs(points[j].distance(atom) - radiusAdjust), dMin); 473 } 474 int d = surfaceDistance100s[i] = (int) Math.floor(dMin * 100); 475 surfaceDistanceMax = Math.max(surfaceDistanceMax, d); 476 } 477 } 478 return points; 479 } 480 481 @SuppressWarnings("unchecked") setAtomCoord2(BS bs, int tokType, Object xyzValues)482 protected void setAtomCoord2(BS bs, int tokType, Object xyzValues) { 483 P3 xyz = null; 484 P3[] values = null; 485 Lst<P3> v = null; 486 int type = 0; 487 int nValues = 1; 488 if (xyzValues instanceof P3) { 489 xyz = (P3) xyzValues; 490 } else if (xyzValues instanceof Lst<?>) { 491 v = (Lst<P3>) xyzValues; 492 if ((nValues = v.size()) == 0) 493 return; 494 type = 1; 495 } else if (AU.isAP(xyzValues)) { 496 values = (P3[]) xyzValues; 497 if ((nValues = values.length) == 0) 498 return; 499 type = 2; 500 } else { 501 return; 502 } 503 int n = 0; 504 if (bs != null) 505 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { 506 switch (type) { 507 case 1: 508 if (n >= nValues) 509 return; 510 xyz = v.get(n++); 511 break; 512 case 2: 513 if (n >= nValues) 514 return; 515 xyz = values[n++]; 516 break; 517 } 518 if (xyz != null) 519 switch (tokType) { 520 case T.xyz: 521 setAtomCoord(i, xyz.x, xyz.y, xyz.z); 522 break; 523 case T.fracxyz: 524 at[i].setFractionalCoordTo(xyz, true); 525 taintAtom(i, TAINT_COORD); 526 break; 527 case T.fuxyz: 528 at[i].setFractionalCoordTo(xyz, false); 529 taintAtom(i, TAINT_COORD); 530 break; 531 case T.vibxyz: 532 setAtomVibrationVector(i, xyz); 533 break; 534 } 535 } 536 } 537 setAtomVibrationVector(int atomIndex, T3 vib)538 private void setAtomVibrationVector(int atomIndex, T3 vib) { 539 setVibrationVector(atomIndex, vib); 540 taintAtom(atomIndex, TAINT_VIBRATION); 541 } 542 setAtomCoord(int atomIndex, float x, float y, float z)543 public void setAtomCoord(int atomIndex, float x, float y, float z) { 544 if (atomIndex < 0 || atomIndex >= ac) 545 return; 546 Atom a = at[atomIndex]; 547 a.set(x, y, z); 548 fixTrajectory(a); 549 taintAtom(atomIndex, TAINT_COORD); 550 } 551 fixTrajectory(Atom a)552 private void fixTrajectory(Atom a) { 553 if (((ModelSet) this).isTrajectory(a.mi)) 554 trajectory.fixAtom(a); 555 } 556 setAtomCoordRelative(int atomIndex, float x, float y, float z)557 public void setAtomCoordRelative(int atomIndex, float x, float y, float z) { 558 if (atomIndex < 0 || atomIndex >= ac) 559 return; 560 Atom a = at[atomIndex]; 561 a.add3(x, y, z); 562 fixTrajectory(a); 563 taintAtom(atomIndex, TAINT_COORD); 564 } 565 setAtomsCoordRelative(BS bs, float x, float y, float z)566 protected void setAtomsCoordRelative(BS bs, float x, float y, 567 float z) { 568 if (bs != null) 569 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) 570 setAtomCoordRelative(i, x, y, z); 571 } 572 setAPa(BS bs, int tok, int iValue, float fValue, String sValue, float[] values, String[] list)573 protected void setAPa(BS bs, int tok, int iValue, float fValue, 574 String sValue, float[] values, String[] list) { 575 int n = 0; 576 577 if (values != null && values.length == 0 || bs == null) 578 return; 579 boolean isAll = (values != null && values.length == ac 580 || list != null && list.length == ac); 581 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { 582 if (isAll) 583 n = i; 584 if (values != null) { 585 if (n >= values.length) 586 return; 587 fValue = values[n++]; 588 if (Float.isNaN(fValue)) 589 continue; 590 iValue = (int) fValue; 591 } else if (list != null) { 592 if (n >= list.length) 593 return; 594 sValue = list[n++]; 595 } 596 Atom atom = at[i]; 597 float f; 598 switch (tok) { 599 case T.atomname: 600 setAtomName(i, sValue, true); 601 break; 602 case T.atomtype: 603 setAtomType(i, sValue); 604 break; 605 case T.chain: 606 setChainID(i, sValue); 607 break; 608 case T.atomno: 609 setAtomNumber(i, iValue, true); 610 break; 611 case T.seqid: 612 setAtomSeqID(i, iValue); 613 break; 614 case T.atomx: 615 case T.x: 616 setAtomCoord(i, fValue, atom.y, atom.z); 617 break; 618 case T.atomy: 619 case T.y: 620 setAtomCoord(i, atom.x, fValue, atom.z); 621 break; 622 case T.atomz: 623 case T.z: 624 setAtomCoord(i, atom.x, atom.y, fValue); 625 break; 626 case T.vibx: 627 case T.viby: 628 case T.vibz: 629 setVibrationVector2(i, tok, fValue); 630 break; 631 case T.fracx: 632 case T.fracy: 633 case T.fracz: 634 atom.setFractionalCoord(tok, fValue, true); 635 taintAtom(i, TAINT_COORD); 636 break; 637 case T.fux: 638 case T.fuy: 639 case T.fuz: 640 atom.setFractionalCoord(tok, fValue, false); 641 taintAtom(i, TAINT_COORD); 642 break; 643 case T.elemno: 644 case T.element: 645 setElement(atom, iValue, true); 646 break; 647 case T.formalcharge: 648 resetPartialCharges(); 649 atom.setFormalCharge(iValue); 650 taintAtom(i, TAINT_FORMALCHARGE); 651 break; 652 case T.hydrophobicity: 653 setHydrophobicity(i, fValue); 654 break; 655 case T.occupancy: 656 // a legacy thing 657 f = (fValue < 2 && fValue >= 0.01f ? 100 * fValue : fValue); 658 setOccupancy(i, f, true); 659 break; 660 case T.partialcharge: 661 setPartialCharge(i, fValue, true); 662 break; 663 case T.bondingradius: 664 setBondingRadius(i, fValue); 665 break; 666 case T.temperature: 667 setBFactor(i, fValue, true); 668 break; 669 case T.resno: 670 setAtomResno(i, iValue); 671 break; 672 case T.label: 673 case T.format: 674 vwr.shm.setAtomLabel(sValue, i); 675 break; 676 case T.radius: 677 case T.spacefill: 678 f = fValue; 679 if (f < 0) 680 f = 0; 681 else if (f > Atom.RADIUS_MAX) 682 f = Atom.RADIUS_GLOBAL; 683 atom.madAtom = ((short) (f * 2000)); 684 break; 685 case T.selected: 686 vwr.slm.setSelectedAtom(atom.i, (fValue != 0)); 687 break; 688 case T.valence: 689 atom.setValence(iValue); 690 taintAtom(i, TAINT_VALENCE); 691 break; 692 case T.vanderwaals: 693 if (atom.setRadius(fValue)) 694 taintAtom(i, TAINT_VANDERWAALS); 695 else 696 untaint(i, TAINT_VANDERWAALS); 697 break; 698 default: 699 Logger.error("unsettable atom property: " + T.nameOf(tok)); 700 return; 701 } 702 } 703 switch (tok) { 704 case T.selected: 705 vwr.slm.setSelectedAtom(-1, false); 706 break; 707 case T.radius: 708 case T.spacefill: 709 vwr.setShapeSize(JC.SHAPE_BALLS, Integer.MAX_VALUE, bs); 710 } 711 } 712 713 /** 714 * also handles modulation info 715 * 716 * @param atomIndex 717 * @param c 718 * @return value or NaN 719 */ getVibCoord(int atomIndex, char c)720 public float getVibCoord(int atomIndex, char c) { 721 JmolModulationSet ms = null; 722 Vibration v = null; 723 switch (c) { 724 case 'x': 725 case 'y': 726 case 'z': 727 v = getVibration(atomIndex, false); 728 break; 729 default: 730 ms = getModulation(atomIndex); 731 if (ms != null) { 732 v = ms.getVibration(false); 733 if (v == null) 734 v = (Vibration) ms; 735 } 736 } 737 if (v == null && ms == null) 738 return Float.NaN; 739 switch (c) { 740 case 'x': 741 case 'X': 742 return v.x; 743 case 'y': 744 case 'Y': 745 return v.y; 746 case 'z': 747 case 'Z': 748 return v.z; 749 case 'O': 750 return ((Float) ms.getModulation('O', null)).floatValue(); 751 case '1': 752 case '2': 753 case '3': 754 T3 t = (T3) ms.getModulation('T', null); 755 float x = (c == '1' ? t.x : c == '2' ? t.y : t.z); 756 return (float) (x - Math.floor(x)); 757 default: 758 return Float.NaN; 759 } 760 } 761 getVibration(int atomIndex, boolean forceNew)762 public Vibration getVibration(int atomIndex, boolean forceNew) { 763 Vibration v = (vibrations == null ? null : (Vibration) vibrations[atomIndex]); 764 return (v instanceof JmolModulationSet ? ((JmolModulationSet) v).getVibration(forceNew) 765 : v == null && forceNew ? new Vibration() : v); 766 } 767 getModulation(int iAtom)768 public JmolModulationSet getModulation(int iAtom) { 769 Vibration v = (vibrations == null ? null : (Vibration) vibrations[iAtom]); 770 return (JmolModulationSet) (v != null && v.modDim > 0 ? v : null); 771 } 772 setVibrationVector(int atomIndex, T3 vib)773 protected void setVibrationVector(int atomIndex, T3 vib) { 774 if (Float.isNaN(vib.x) || Float.isNaN(vib.y) || Float.isNaN(vib.z)) 775 return; 776 if (vibrations == null || vibrations.length < atomIndex) 777 vibrations = new Vibration[at.length]; 778 if (vib instanceof Vibration) { 779 vibrations[atomIndex] = (Vibration) vib; 780 } else { 781 if (vibrations[atomIndex] == null) 782 vibrations[atomIndex] = new Vibration(); 783 vibrations[atomIndex].setXYZ(vib); 784 } 785 at[atomIndex].setVibrationVector(); 786 } 787 setVibrationVector2(int atomIndex, int tok, float fValue)788 private void setVibrationVector2(int atomIndex, int tok, float fValue) { 789 Vibration v = getVibration(atomIndex, true); 790 if (v == null) 791 return; 792 switch (tok) { 793 case T.vibx: 794 v.x = fValue; 795 break; 796 case T.viby: 797 v.y = fValue; 798 break; 799 case T.vibz: 800 v.z = fValue; 801 break; 802 } 803 setAtomVibrationVector(atomIndex, v); 804 } 805 setAtomName(int atomIndex, String name, boolean doTaint)806 public void setAtomName(int atomIndex, String name, boolean doTaint) { 807 if (doTaint && name.equals(at[atomIndex].getAtomName())) 808 return; 809 // can't set the name of a bioatom 810 byte id = (((ModelSet) this).am[at[atomIndex].mi].isBioModel ? vwr.getJBR() 811 .lookupSpecialAtomID(name) : 0); 812 at[atomIndex].atomID = id; 813 if (id <= 0) { 814 if (atomNames == null) 815 atomNames = new String[at.length]; 816 atomNames[atomIndex] = name; 817 } 818 if (doTaint) 819 taintAtom(atomIndex, TAINT_ATOMNAME); 820 } 821 setAtomType(int atomIndex, String type)822 private void setAtomType(int atomIndex, String type) { 823 if (type.equals(at[atomIndex].getAtomType())) 824 return; 825 if (atomTypes == null) 826 atomTypes = new String[at.length]; 827 atomTypes[atomIndex] = type; 828 return; 829 } 830 setChainID(int atomIndex, String id)831 private void setChainID(int atomIndex, String id) { 832 if (id.equals(at[atomIndex].getChainIDStr())) 833 return; 834 int intid = at[atomIndex].getChainID(); 835 BS bs = getChainBits(intid); 836 Chain c = at[atomIndex].group.chain; 837 c.chainID = vwr.getChainID(id, true); 838 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) 839 taintAtom(i, TAINT_CHAIN); 840 } 841 setAtomNumber(int atomIndex, int atomno, boolean doTaint)842 public void setAtomNumber(int atomIndex, int atomno, boolean doTaint) { 843 if (doTaint && atomno == at[atomIndex].getAtomNumber()) 844 return; 845 if (atomSerials == null) 846 atomSerials = new int[at.length]; 847 atomSerials[atomIndex] = atomno; 848 if (doTaint) 849 taintAtom(atomIndex, TAINT_ATOMNO); 850 } 851 setElement(Atom atom, int atomicNumber, boolean doTaint)852 public void setElement(Atom atom, int atomicNumber, boolean doTaint) { 853 if (doTaint && atom.getElementNumber() == atomicNumber) 854 return; 855 atom.setAtomicAndIsotopeNumber(atomicNumber); 856 atom.paletteID = PAL.CPK.id; 857 atom.colixAtom = vwr.cm.getColixAtomPalette(atom, 858 PAL.CPK.id); 859 resetPartialCharges(); 860 if (doTaint) 861 taintAtom(atom.i, TAINT_ELEMENT); 862 } 863 resetPartialCharges()864 private void resetPartialCharges() { 865 partialCharges = null; 866 bsPartialCharges = null; 867 } 868 setAtomResno(int atomIndex, int resno)869 private void setAtomResno(int atomIndex, int resno) { 870 if (resno == at[atomIndex].getResno()) 871 return; 872 at[atomIndex].group.setResno(resno); 873 if (atomResnos == null) 874 atomResnos = new int[at.length]; 875 atomResnos[atomIndex] = resno; 876 taintAtom(atomIndex, TAINT_RESNO); 877 } 878 setAtomSeqID(int atomIndex, int seqID)879 private void setAtomSeqID(int atomIndex, int seqID) { 880 if (seqID == at[atomIndex].getSeqID()) 881 return; 882 if (atomSeqIDs == null) 883 atomSeqIDs = new int[at.length]; 884 atomSeqIDs[atomIndex] = seqID; 885 taintAtom(atomIndex, TAINT_SEQID); 886 } 887 setOccupancy(int atomIndex, float occupancy, boolean doTaint)888 protected void setOccupancy(int atomIndex, float occupancy, boolean doTaint) { 889 if (doTaint && occupancy == at[atomIndex].getOccupancy100()) 890 return; 891 if (occupancies == null) { 892 if (occupancy == 100) 893 return; // 100 is the default; 894 occupancies = new float[at.length]; 895 for (int i = at.length; --i >= 0;) 896 occupancies[i] = 100; 897 } 898 occupancies[atomIndex] = occupancy; 899 if (doTaint) 900 taintAtom(atomIndex, TAINT_OCCUPANCY); 901 } 902 setPartialCharge(int atomIndex, float partialCharge, boolean doTaint)903 protected void setPartialCharge(int atomIndex, float partialCharge, boolean doTaint) { 904 if (Float.isNaN(partialCharge)) 905 return; 906 if (partialCharges == null) { 907 bsPartialCharges = new BS(); 908 if (partialCharge == 0) 909 return; // no need to store a 0. 910 partialCharges = new float[at.length]; 911 } 912 bsPartialCharges.set(atomIndex); 913 partialCharges[atomIndex] = partialCharge; 914 if (doTaint) 915 taintAtom(atomIndex, TAINT_PARTIALCHARGE); 916 } 917 setBondingRadius(int atomIndex, float radius)918 protected void setBondingRadius(int atomIndex, float radius) { 919 if (Float.isNaN(radius) || radius == at[atomIndex].getBondingRadius()) 920 return; 921 if (bondingRadii == null) { 922 bondingRadii = new float[at.length]; 923 } else if (bondingRadii.length < at.length) { 924 bondingRadii = (float[]) AU.ensureLength(bondingRadii, at.length); 925 } 926 bondingRadii[atomIndex] = radius; 927 taintAtom(atomIndex, TAINT_BONDINGRADIUS); 928 } 929 setBFactor(int atomIndex, float bfactor, boolean doTaint)930 protected void setBFactor(int atomIndex, float bfactor, boolean doTaint) { 931 if (Float.isNaN(bfactor) || doTaint && bfactor == at[atomIndex].getBfactor100()) 932 return; 933 if (bfactor100s == null) { 934 if (bfactor == 0) // there's no need to store a 0. 935 return; 936 bfactor100s = new short[at.length]; 937 } 938 bfactor100s[atomIndex] = (short) ((bfactor < -327.68f ? -327.68f 939 : bfactor > 327.67 ? 327.67 : bfactor) * 100 + (bfactor < 0 ? -0.5 : 0.5)); 940 if (doTaint) 941 taintAtom(atomIndex, TAINT_TEMPERATURE); 942 } 943 setHydrophobicity(int atomIndex, float value)944 private void setHydrophobicity(int atomIndex, float value) { 945 if (Float.isNaN(value) || value == at[atomIndex].getHydrophobicity()) 946 return; 947 if (hydrophobicities == null) { 948 hydrophobicities = new float[at.length]; 949 for (int i = 0; i < at.length; i++) 950 hydrophobicities[i] = Elements.getHydrophobicity(at[i].group.groupID); 951 } 952 hydrophobicities[atomIndex] = value; 953 taintAtom(atomIndex, TAINT_HYDROPHOBICITY); 954 } 955 956 // loading data 957 setAtomData(int type, String name, String dataString, boolean isDefault)958 public void setAtomData(int type, String name, String dataString, 959 boolean isDefault) { 960 float[] fData = null; 961 BS bs = null; 962 switch(type) { 963 case TAINT_COORD: 964 loadCoordinates(dataString, false, !isDefault); 965 return; 966 case TAINT_VIBRATION: 967 loadCoordinates(dataString, true, true); 968 return; 969 case TAINT_MAX: 970 fData = new float[ac]; 971 bs = BS.newN(ac); 972 break; 973 } 974 int[] lines = Parser.markLines(dataString, ';'); 975 int n = 0; 976 try { 977 int nData = PT.parseInt(dataString.substring(0, lines[0] - 1)); 978 for (int i = 1; i <= nData; i++) { 979 String[] tokens = PT.getTokens(PT.parseTrimmed(dataString.substring( 980 lines[i], lines[i + 1] - 1))); 981 int atomIndex = PT.parseInt(tokens[0]) - 1; 982 if (atomIndex < 0 || atomIndex >= ac) 983 continue; 984 Atom atom = at[atomIndex]; 985 n++; 986 int pt = tokens.length - 1; 987 float x = PT.parseFloat(tokens[pt]); 988 switch (type) { 989 case TAINT_MAX: 990 fData[atomIndex] = x; 991 bs.set(atomIndex); 992 continue; 993 case TAINT_ATOMNAME: 994 setAtomName(atomIndex, tokens[pt], true); 995 break; 996 case TAINT_ATOMNO: 997 setAtomNumber(atomIndex, (int) x, true); 998 break; 999 case TAINT_RESNO: 1000 setAtomResno(atomIndex, (int) x); 1001 break; 1002 case TAINT_SEQID: 1003 setAtomSeqID(atomIndex, (int) x); 1004 break; 1005 case TAINT_ATOMTYPE: 1006 setAtomType(atomIndex, tokens[pt]); 1007 break; 1008 case TAINT_CHAIN: 1009 setChainID(atomIndex, tokens[pt]); 1010 break; 1011 case TAINT_ELEMENT: 1012 atom.setAtomicAndIsotopeNumber((int)x); 1013 atom.paletteID = PAL.CPK.id; 1014 atom.colixAtom = vwr.cm.getColixAtomPalette(atom, PAL.CPK.id); 1015 break; 1016 case TAINT_FORMALCHARGE: 1017 atom.setFormalCharge((int)x); 1018 break; 1019 case TAINT_HYDROPHOBICITY: 1020 setHydrophobicity(atomIndex, x); 1021 break; 1022 case TAINT_BONDINGRADIUS: 1023 setBondingRadius(atomIndex, x); 1024 break; 1025 case TAINT_PARTIALCHARGE: 1026 setPartialCharge(atomIndex, x, true); 1027 break; 1028 case TAINT_TEMPERATURE: 1029 setBFactor(atomIndex, x, true); 1030 break; 1031 case TAINT_VALENCE: 1032 atom.setValence((int)x); 1033 break; 1034 case TAINT_VANDERWAALS: 1035 atom.setRadius(x); 1036 break; 1037 } 1038 taintAtom(atomIndex, type); 1039 } 1040 if (type == TAINT_MAX && n > 0) 1041 vwr.setData(name, new Object[] {name, fData, bs, Integer.valueOf(JmolDataManager.DATA_TYPE_AF)}, 0, 0, 0, 0, 0); 1042 1043 } catch (Exception e) { 1044 Logger.error("AtomCollection.loadData error: " + e); 1045 } 1046 } 1047 loadCoordinates(String data, boolean isVibrationVectors, boolean doTaint)1048 private void loadCoordinates(String data, boolean isVibrationVectors, boolean doTaint) { 1049 int[] lines = Parser.markLines(data, ';'); 1050 V3 v = (isVibrationVectors ? new V3() : null); 1051 try { 1052 int nData = PT.parseInt(data.substring(0, lines[0] - 1)); 1053 for (int i = 1; i <= nData; i++) { 1054 String[] tokens = PT.getTokens(PT.parseTrimmed(data.substring( 1055 lines[i], lines[i + 1]))); 1056 int atomIndex = PT.parseInt(tokens[0]) - 1; 1057 // JavaScript will not be exact, and we will be checking for the exact value 1058 // Modulation setting modScale 1059 float x = (tokens[3].equalsIgnoreCase("1.4E-45") ? 1.4e-45f : PT.parseFloat(tokens[3])); 1060 float y = (tokens[4].equalsIgnoreCase("1.4E-45") ? 1.4e-45f : PT.parseFloat(tokens[4])); 1061 float z = PT.parseFloat(tokens[5]); 1062 if (isVibrationVectors) { 1063 v.set(x, y, z); 1064 setAtomVibrationVector(atomIndex, v); 1065 } else { 1066 setAtomCoord(atomIndex, x, y, z); 1067 if (!doTaint) 1068 untaint(atomIndex, TAINT_COORD); 1069 } 1070 } 1071 } catch (Exception e) { 1072 Logger.error("Frame.loadCoordinate error: " + e); 1073 } 1074 } 1075 validateBspf(boolean isValid)1076 public void validateBspf(boolean isValid) { 1077 if (bspf != null) 1078 bspf.isValid = isValid; 1079 //averageAtomPoint = null; 1080 } 1081 validateBspfForModel(int modelIndex, boolean isValid)1082 void validateBspfForModel(int modelIndex, boolean isValid) { 1083 if (bspf != null) 1084 bspf.validateModel(modelIndex, isValid); 1085 } 1086 1087 // state tainting 1088 setPreserveState(boolean TF)1089 public void setPreserveState(boolean TF) { 1090 preserveState = TF; 1091 } 1092 //// atom coordinate and property changing ////////// 1093 getUserSettableType(String dataType)1094 public static int getUserSettableType(String dataType) { 1095 boolean isExplicit = (dataType.indexOf("property_") == 0); 1096 String check = (isExplicit ? dataType.substring(9) : dataType); 1097 for (int i = 0; i < TAINT_MAX; i++) 1098 if (userSettableValues[i].equalsIgnoreCase(check)) 1099 return i; 1100 return (isExplicit ? TAINT_MAX : -1); 1101 } 1102 getTaintedAtoms(int type)1103 public BS getTaintedAtoms(int type) { 1104 return tainted == null ? null : tainted[type]; 1105 } 1106 taintAtoms(BS bsAtoms, int type)1107 public void taintAtoms(BS bsAtoms, int type) { 1108 canSkipLoad = false; 1109 if (!preserveState) 1110 return; 1111 for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1)) 1112 taintAtom(i, type); 1113 } 1114 taintAtom(int atomIndex, int type)1115 public void taintAtom(int atomIndex, int type) { 1116 if (preserveState) { 1117 if (tainted == null) 1118 tainted = new BS[TAINT_MAX]; 1119 if (tainted[type] == null) 1120 tainted[type] = BS.newN(ac); 1121 tainted[type].set(atomIndex); 1122 } 1123 if (type == TAINT_COORD) 1124 taintModelCoord(atomIndex); 1125 } 1126 taintModelCoord(int atomIndex)1127 private void taintModelCoord(int atomIndex) { 1128 Model m = ((ModelSet) this).am[at[atomIndex].mi]; 1129 validateBspfForModel(m.trajectoryBaseIndex, false); 1130 if (m.isBioModel) 1131 m.resetDSSR(true); 1132 pointGroup = null; 1133 } 1134 untaint(int atomIndex, int type)1135 private void untaint(int atomIndex, int type) { 1136 if (!preserveState) 1137 return; 1138 if (tainted == null || tainted[type] == null) 1139 return; 1140 tainted[type].clear(atomIndex); 1141 } 1142 setTaintedAtoms(BS bs, int type)1143 public void setTaintedAtoms(BS bs, int type) { 1144 if (preserveState) { 1145 if (bs == null) { 1146 if (tainted == null) 1147 return; 1148 tainted[type] = null; 1149 return; 1150 } 1151 if (tainted == null) 1152 tainted = new BS[TAINT_MAX]; 1153 if (tainted[type] == null) 1154 tainted[type] = BS.newN(ac); 1155 BSUtil.copy2(bs, tainted[type]); 1156 } 1157 if (type == TAINT_COORD) { 1158 int i = bs.nextSetBit(0); 1159 if (i >= 0) 1160 taintModelCoord(i); 1161 } 1162 1163 } 1164 unTaintAtoms(BS bs, int type)1165 public void unTaintAtoms(BS bs, int type) { 1166 if (tainted == null || tainted[type] == null) 1167 return; 1168 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) 1169 tainted[type].clear(i); 1170 if (tainted[type].nextSetBit(0) < 0) 1171 tainted[type] = null; 1172 } 1173 1174 /////////////////////////////////////////// 1175 1176 /* 1177 * generalized; not just balls 1178 * 1179 * This algorithm assumes that atoms are circles at the z-depth 1180 * of their center point. Therefore, it probably has some flaws 1181 * around the edges when dealing with intersecting spheres that 1182 * are at approximately the same z-depth. 1183 * But it is much easier to deal with than trying to actually 1184 * calculate which atom was clicked 1185 * 1186 * A more general algorithm of recording which object drew 1187 * which pixel would be very expensive and not worth the trouble 1188 */ findNearest2(int x, int y, Atom[] closest, BS bsNot, int min)1189 protected void findNearest2(int x, int y, Atom[] closest, BS bsNot, int min) { 1190 Atom champion = null; 1191 Atom contender; 1192 for (int i = ac; --i >= 0;) { 1193 if (bsNot != null && bsNot.get(i) || (contender = at[i]) == null) 1194 continue; 1195 if (contender.isClickable() 1196 && isCursorOnTopOf(contender, x, y, min, 1197 champion)) 1198 champion = contender; 1199 } 1200 closest[0] = champion; 1201 } 1202 1203 /** 1204 * used by Frame and AminoMonomer and NucleicMonomer -- does NOT check for clickability 1205 * @param contender 1206 * @param x 1207 * @param y 1208 * @param radius 1209 * @param champion 1210 * @return true if user is pointing to this atom 1211 */ isCursorOnTopOf(Atom contender, int x, int y, int radius, Atom champion)1212 boolean isCursorOnTopOf(Atom contender, int x, int y, int radius, 1213 Atom champion) { 1214 return contender.sZ > 1 && !g3d.isClippedZ(contender.sZ) 1215 && g3d.isInDisplayRange(contender.sX, contender.sY) 1216 && contender.isCursorOnTopOf(x, y, radius, champion); 1217 } 1218 fillADa(AtomData atomData, int mode)1219 protected void fillADa(AtomData atomData, int mode) { 1220 atomData.xyz = atomData.atoms = at; 1221 atomData.ac = ac; 1222 atomData.atomicNumber = new int[ac]; 1223 boolean includeRadii = ((mode & AtomData.MODE_FILL_RADII) != 0); 1224 if (includeRadii) 1225 atomData.atomRadius = new float[ac]; 1226 boolean isMultiModel = ((mode & AtomData.MODE_FILL_MULTIMODEL) != 0); 1227 for (int i = 0; i < ac; i++) { 1228 Atom atom = at[i]; 1229 if (atom == null || atom.isDeleted() || !isMultiModel && atomData.modelIndex >= 0 1230 && atom.mi != atomData.firstModelIndex) { 1231 if (atomData.bsIgnored == null) 1232 atomData.bsIgnored = new BS(); 1233 atomData.bsIgnored.set(i); 1234 continue; 1235 } 1236 atomData.atomicNumber[i] = atom.getElementNumber(); 1237 atomData.lastModelIndex = atom.mi; 1238 if (includeRadii) 1239 atomData.atomRadius[i] = getWorkingRadius(atom, atomData); 1240 } 1241 } 1242 1243 ////// hybridization /////////// 1244 1245 @SuppressWarnings("incomplete-switch") getWorkingRadius(Atom atom, AtomData atomData)1246 private float getWorkingRadius(Atom atom, AtomData atomData) { 1247 float r = 0; 1248 RadiusData rd = atomData.radiusData; 1249 switch (rd.factorType) { 1250 case ABSOLUTE: 1251 r = rd.value; 1252 break; 1253 case FACTOR: 1254 case OFFSET: 1255 switch (rd.vdwType) { 1256 case BONDING: 1257 r = atom.getBondingRadius(); 1258 break; 1259 case ADPMAX: 1260 r = atom.getADPMinMax(true); 1261 break; 1262 case ADPMIN: 1263 r = atom.getADPMinMax(false); 1264 break; 1265 default: 1266 r = atom.getVanderwaalsRadiusFloat(vwr, 1267 atomData.radiusData.vdwType); 1268 } 1269 if (rd.factorType == EnumType.FACTOR) 1270 r *= rd.value; 1271 else 1272 r += rd.value; 1273 } 1274 return r + rd.valueExtended; 1275 } 1276 1277 public final static int CALC_H_DOALL = 0x100; 1278 public final static int CALC_H_JUSTC = 0x200; 1279 public final static int CALC_H_HAVEH = 0x400; 1280 public final static int CALC_H_QUICK = Viewer.MIN_QUICK; 1281 public static final int CALC_H_IGNORE_H = 0x800; 1282 public static final int CALC_H_ALLOW_H = 0x1000; 1283 1284 1285 /** 1286 * get a list of potential H atom positions based on 1287 * elemental valence and formal charge 1288 * 1289 * @param bs 1290 * @param nTotal 1291 * @param vConnect 1292 * @param flags [CALC_H_DOALL | CALC_H_JUSTC | CALC_H_HAVEH | CALC_H_QUICK 1293 * @return array of arrays of points added to specific atoms 1294 */ calculateHydrogens(BS bs, int[] nTotal, Lst<Atom> vConnect, int flags)1295 public P3[][] calculateHydrogens(BS bs, int[] nTotal, 1296 Lst<Atom> vConnect, int flags) { 1297 boolean doAll = ((flags & CALC_H_DOALL) == CALC_H_DOALL); 1298 boolean justCarbon = ((flags & CALC_H_JUSTC) == CALC_H_JUSTC); 1299 boolean isQuick = ((flags & CALC_H_QUICK) == CALC_H_QUICK); 1300 boolean ignoreH = ((flags & CALC_H_IGNORE_H) == CALC_H_IGNORE_H); 1301 boolean allowH = ((flags & CALC_H_ALLOW_H) == CALC_H_ALLOW_H); 1302 V3 z = new V3(); 1303 V3 x = new V3(); 1304 P3[][] hAtoms = new P3[ac][]; 1305 BS bsDeleted = vwr.slm.bsDeleted; 1306 P3 pt; 1307 int nH = 0; 1308 1309 // just not doing aldehydes here -- all A-X-B bent == sp3 for now 1310 if (bs != null) 1311 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { 1312 if (bsDeleted != null && bsDeleted.get(i)) 1313 continue; 1314 Atom atom = at[i]; 1315 int atomicNumber = atom.getElementNumber(); 1316 if (justCarbon && atomicNumber != 6) 1317 continue; 1318 float dHX = (atomicNumber <= 6 ? 1.1f // B, C 1319 : atomicNumber <= 10 ? 1.0f // N, O 1320 : 1.3f); // S 1321 switch (atomicNumber) { 1322 case 7: 1323 case 8: 1324 dHX = 1.0f; 1325 break; 1326 case 6: 1327 } 1328 int n = (doAll || ignoreH || allowH ? atom.getCovalentHydrogenCount() : 0); 1329 if (doAll && n > 0 || ignoreH && n == 0) 1330 continue; 1331 int nMissing = getMissingHydrogenCount(atom, false); 1332 if (doAll && nMissing == 0) 1333 continue; 1334 if (!ignoreH) 1335 n = nMissing; 1336 1337 // TODO - NPE while working with model kit after deleting and adding and dragging atoms. 1338 1339 int targetValence = aaRet[0]; 1340 int hybridization = aaRet[2]; 1341 int nBonds = aaRet[3] - (ignoreH ? n : 0); 1342 if (nBonds == 0 && atom.isHetero()) 1343 continue; // no adding to water 1344 hAtoms[i] = new P3[n]; 1345 int hPt = 0; 1346 if (nBonds == 0) { 1347 switch (n) { 1348 case 4: 1349 // methane 1350 z.set(0.635f, 0.635f, 0.635f); 1351 pt = P3.newP(z); 1352 pt.add(atom); 1353 hAtoms[i][hPt++] = pt; 1354 if (vConnect != null) 1355 vConnect.addLast(atom); 1356 //$FALL-THROUGH$ 1357 case 3: 1358 // nitrogen 1359 z.set(-0.635f, -0.635f, 0.635f); 1360 pt = P3.newP(z); 1361 pt.add(atom); 1362 hAtoms[i][hPt++] = pt; 1363 if (vConnect != null) 1364 vConnect.addLast(atom); 1365 //$FALL-THROUGH$ 1366 case 2: 1367 // oxygen 1368 z.set(-0.635f, 0.635f, -0.635f); 1369 pt = P3.newP(z); 1370 pt.add(atom); 1371 hAtoms[i][hPt++] = pt; 1372 if (vConnect != null) 1373 vConnect.addLast(atom); 1374 //$FALL-THROUGH$ 1375 case 1: 1376 z.set(0.635f, -0.635f, -0.635f); 1377 pt = P3.newP(z); 1378 pt.add(atom); 1379 hAtoms[i][hPt++] = pt; 1380 if (vConnect != null) 1381 vConnect.addLast(atom); 1382 } 1383 } else { 1384 switch (n) { 1385 default: 1386 break; 1387 case 3: // three bonds needed RC 1388 getHybridizationAndAxes(i, atomicNumber, z, x, "sp3b", false, true, isQuick); 1389 pt = new P3(); 1390 pt.scaleAdd2(dHX, z, atom); 1391 hAtoms[i][hPt++] = pt; 1392 if (vConnect != null) 1393 vConnect.addLast(atom); 1394 getHybridizationAndAxes(i, atomicNumber, z, x, "sp3c", false, true, isQuick); 1395 pt = new P3(); 1396 pt.scaleAdd2(dHX, z, atom); 1397 hAtoms[i][hPt++] = pt; 1398 if (vConnect != null) 1399 vConnect.addLast(atom); 1400 getHybridizationAndAxes(i, atomicNumber, z, x, "sp3d", false, true, isQuick); 1401 pt = new P3(); 1402 pt.scaleAdd2(dHX, z, atom); 1403 hAtoms[i][hPt++] = pt; 1404 if (vConnect != null) 1405 vConnect.addLast(atom); 1406 break; 1407 case 2: 1408 // 2 bonds needed R2C or R-N or R2C=C or O 1409 // or RC=C or C=C 1410 boolean isEne = (hybridization == 2 || atomicNumber == 5 || nBonds == 1 1411 && targetValence == 4 1412 || atomicNumber == 7 && isAdjacentSp2(atom)); 1413 getHybridizationAndAxes(i, atomicNumber, z, x, (isEne ? "sp2b" 1414 : targetValence == 3 ? "sp3c" : "lpa"), false, true, isQuick); 1415 pt = P3.newP(z); 1416 pt.scaleAdd2(dHX, z, atom); 1417 hAtoms[i][hPt++] = pt; 1418 if (vConnect != null) 1419 vConnect.addLast(atom); 1420 getHybridizationAndAxes(i, atomicNumber, z, x, (isEne ? "sp2c" 1421 : targetValence == 3 ? "sp3d" : "lpb"), false, true, isQuick); 1422 pt = P3.newP(z); 1423 pt.scaleAdd2(dHX, z, atom); 1424 hAtoms[i][hPt++] = pt; 1425 if (vConnect != null) 1426 vConnect.addLast(atom); 1427 break; 1428 case 1: 1429 // one bond needed R2B, R3C, R-N-R, R-O R=C-R R=N R-3-C 1430 // nbonds ......... 2 .. 3 .. 2 ... 1 ... 2 .. 1 .. 1 1431 // nval ........... 2 .. 3 .. 2 ... 1 ... 3 .. 2 .. 3 1432 // targetValence .. 3 .. 4 .. 3 ... 2 ... 4 .. 3 .. 4 1433 // tV - nbonds .. 1 1 1 1 2 2 3 1434 // ................ sp2c sp3d sp3d sp3b sp2c sp2b sp 1435 switch (targetValence - nBonds) { 1436 case 1: 1437 // sp3 or Boron sp2 or N sp2 1438 if (atomicNumber == 8 && atom == atom.group.getCarbonylOxygenAtom()) { 1439 hAtoms[i] = null; 1440 continue; 1441 } 1442 if (getHybridizationAndAxes(i, atomicNumber, z, x, 1443 (hybridization == 2 || atomicNumber == 5 1444 || atomicNumber == 6 && aaRet[1] == 1 1445 || atomicNumber == 7 1446 && (atom.group.getNitrogenAtom() == atom & atom.getFormalCharge() == 0 || isAdjacentSp2(atom)) 1447 ? "sp2c" 1448 : "sp3d"), true, false, isQuick) != null) { 1449 pt = P3.newP(z); 1450 pt.scaleAdd2(dHX, z, atom); 1451 hAtoms[i][hPt++] = pt; 1452 if (vConnect != null) 1453 vConnect.addLast(atom); 1454 } else { 1455 hAtoms[i] = new P3[0]; 1456 } 1457 break; 1458 case 2: 1459 // sp2 1460 getHybridizationAndAxes(i, atomicNumber, z, x, (targetValence == 4 ? "sp2c" 1461 : "sp2b"), false, false, isQuick); 1462 pt = P3.newP(z); 1463 pt.scaleAdd2(dHX, z, atom); 1464 hAtoms[i][hPt++] = pt; 1465 if (vConnect != null) 1466 vConnect.addLast(atom); 1467 break; 1468 case 3: 1469 // sp 1470 getHybridizationAndAxes(i, atomicNumber, z, x, "spb", false, true, isQuick); 1471 pt = P3.newP(z); 1472 pt.scaleAdd2(dHX, z, atom); 1473 hAtoms[i][hPt++] = pt; 1474 if (vConnect != null) 1475 vConnect.addLast(atom); 1476 break; 1477 } 1478 } 1479 } 1480 nH += hPt; 1481 } 1482 nTotal[0] = nH; 1483 return hAtoms; 1484 } 1485 isAdjacentSp2(Atom atom)1486 private boolean isAdjacentSp2(Atom atom) { 1487 Bond[] bonds = atom.bonds; 1488 for (int i = 0; i < bonds.length; i++) { 1489 Bond[] b2 = bonds[i].getOtherAtom(atom).bonds; 1490 for (int j = 0; j < b2.length; j++) 1491 switch (b2[j].order) { 1492 case Edge.BOND_AROMATIC: 1493 case Edge.BOND_AROMATIC_DOUBLE: 1494 case Edge.BOND_COVALENT_DOUBLE: 1495 case Edge.BOND_COVALENT_TRIPLE: 1496 return true; 1497 } 1498 } 1499 return false; 1500 } 1501 1502 int[] aaRet; 1503 getMissingHydrogenCount(Atom atom, boolean allowNegative)1504 public int getMissingHydrogenCount(Atom atom, boolean allowNegative) { 1505 int targetCount = atom.getTargetValence(); 1506 if (targetCount < 0) 1507 return 0; 1508 int charge = atom.getFormalCharge(); 1509 int valence = atom.getValence(); 1510 Model model = ((ModelSet) this).am[atom.mi]; 1511 String s = (model.isBioModel && !model.isPdbWithMultipleBonds ? atom.group.getGroup3() : null); 1512 if (aaRet == null) 1513 aaRet = new int[5]; 1514 aaRet[0] = targetCount; 1515 aaRet[1] = charge; 1516 aaRet[2] = 0; // hybridization 1517 aaRet[3] = atom.getCovalentBondCount(); 1518 aaRet[4] = (s == null ? 0 : valence); 1519 if (s != null && charge == 0) { 1520 if (bioModelset.getAminoAcidValenceAndCharge(s, atom.getAtomName(), 1521 aaRet)) { 1522 targetCount = aaRet[0]; 1523 charge = aaRet[1]; 1524 } 1525 } 1526 if (charge != 0) { 1527 targetCount += (targetCount == 4 ? -Math.abs(charge) : charge); 1528 aaRet[0] = targetCount; 1529 } 1530 int n = targetCount - valence; 1531 return (n < 0 && !allowNegative ? 0 : n); 1532 } 1533 fixFormalCharges(BS bs)1534 public int fixFormalCharges(BS bs) { 1535 int n = 0; 1536 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { 1537 Atom a = at[i]; 1538 int nH = getMissingHydrogenCount(a, true); 1539 if (nH != 0) { 1540 1541 int c0 = a.getFormalCharge(); 1542 int c = c0 - nH; 1543 a.setFormalCharge(c); 1544 taintAtom(i, TAINT_FORMALCHARGE); 1545 if (Logger.debugging) 1546 Logger.debug("atom " + a + " formal charge " + c0 + " -> " + c); 1547 n++; 1548 } 1549 } 1550 return n; 1551 } 1552 getHybridizationAndAxes(int atomIndex, int atomicNumber, V3 z, V3 x, String lcaoTypeRaw, boolean hybridizationCompatible, boolean doAlignZ, boolean isQuick)1553 public String getHybridizationAndAxes(int atomIndex, int atomicNumber, V3 z, V3 x, 1554 String lcaoTypeRaw, 1555 boolean hybridizationCompatible, 1556 boolean doAlignZ, boolean isQuick) { 1557 1558 String lcaoType = (lcaoTypeRaw.length() > 0 && lcaoTypeRaw.charAt(0) == '-' ? lcaoTypeRaw 1559 .substring(1) 1560 : lcaoTypeRaw); 1561 1562 if (lcaoTypeRaw.indexOf("d") >= 0 && !lcaoTypeRaw.endsWith("sp3d")) 1563 return getHybridizationAndAxesD(atomIndex, z, x, lcaoType); 1564 1565 Atom atom = at[atomIndex]; 1566 if (atomicNumber == 0) 1567 atomicNumber = atom.getElementNumber(); 1568 Atom[] attached = getAttached(atom, 4, hybridizationCompatible, isQuick); 1569 int nAttached = attached.length; 1570 int pt = lcaoType.charAt(lcaoType.length() - 1) - 'a'; 1571 if (pt < 0 || pt > 6) 1572 pt = 0; 1573 z.set(0, 0, 0); 1574 x.set(0, 0, 0); 1575 V3[] v = new V3[4]; 1576 for (int i = 0; i < nAttached; i++) { 1577 Atom a = attached[i]; 1578 if (a == null) { 1579 nAttached = i; 1580 break; 1581 } 1582 v[i] = V3.newVsub(atom, a); 1583 v[i].normalize(); 1584 z.add(v[i]); 1585 } 1586 if (nAttached > 0) 1587 x.setT(v[0]); 1588 boolean isPlanar = false; 1589 V3 vTemp = new V3(); 1590 if (nAttached >= 3) { 1591 if (x.angle(v[1]) < almost180) 1592 vTemp.cross(x, v[1]); 1593 else 1594 vTemp.cross(x, v[2]); 1595 vTemp.normalize(); 1596 V3 vTemp2 = new V3(); 1597 if (v[1].angle(v[2]) < almost180) 1598 vTemp2.cross(v[1], v[2]); 1599 else 1600 vTemp2.cross(x, v[2]); 1601 vTemp2.normalize(); 1602 isPlanar = (Math.abs(vTemp2.dot(vTemp)) >= 0.95f); 1603 } 1604 1605 boolean isSp3 = (lcaoType.indexOf("sp3") == 0); 1606 boolean isSp2 = (!isSp3 && lcaoType.indexOf("sp2") == 0); 1607 boolean isSp = (!isSp3 && !isSp2 && lcaoType.indexOf("sp") == 0); 1608 boolean isP = (lcaoType.indexOf("p") == 0); 1609 boolean isLp = (lcaoType.indexOf("lp") == 0); 1610 1611 String hybridization = null; 1612 if (hybridizationCompatible) { 1613 if (nAttached == 0) 1614 return null; 1615 if (isSp3) { 1616 if (pt > 3 || nAttached > 4) 1617 return null; 1618 } else if (isSp2) { 1619 if (pt > 2 || nAttached > 3) 1620 return null; 1621 } else if (isSp) { 1622 if (pt > 1 || nAttached > 2) 1623 return null; 1624 } 1625 switch (nAttached) { 1626 case 1: 1627 if (atomicNumber == 1 && !isSp3) 1628 return null; 1629 if (isSp3) { 1630 hybridization = "sp3"; 1631 break; 1632 } 1633 switch (attached[0].getCovalentBondCount()) { 1634 case 1: 1635 if (attached[0].getValence() != 2) { 1636 // C-t-C 1637 hybridization = "sp"; 1638 break; 1639 } 1640 // C=C, no other atoms 1641 //$FALL-THROUGH$ 1642 case 2: 1643 hybridization = (isSp ? "sp" : "sp2"); 1644 break; 1645 case 3: 1646 // special case, for example R2C=O oxygen 1647 if (!isSp2 && !isP) 1648 return null; 1649 hybridization = "sp2"; 1650 break; 1651 } 1652 break; 1653 case 2: 1654 if (z.length() < 0.1f) { 1655 // linear A--X--B 1656 if (lcaoType.indexOf("2") >= 0 || lcaoType.indexOf("3") >= 0) 1657 return null; 1658 hybridization = "sp"; 1659 break; 1660 } 1661 // bent A--X--B 1662 hybridization = (isSp3 ? "sp3" : "sp2"); 1663 if (lcaoType.indexOf("sp") == 0) { // align z as sp2 orbital 1664 break; 1665 } 1666 if (isLp) { // align z as lone pair 1667 hybridization = "lp"; // any is OK 1668 break; 1669 } 1670 hybridization = lcaoType; 1671 break; 1672 default: 1673 // 3 or 4 bonds 1674 if (isPlanar && !isQuick) { 1675 hybridization = "sp2"; 1676 } else { 1677 if (isPlanar) 1678 z.setT(vTemp); 1679 if (isLp && nAttached == 3) { 1680 hybridization = "lp"; 1681 break; 1682 } 1683 hybridization = "sp3"; 1684 } 1685 } 1686 if (hybridization == null) 1687 return null; 1688 if (lcaoType.indexOf("p") == 0) { 1689 if (hybridization == "sp3") 1690 return null; 1691 } else if (lcaoType.indexOf(hybridization) < 0) { 1692 return null; 1693 } 1694 } 1695 1696 if (pt < nAttached && !lcaoType.startsWith("p") 1697 && !lcaoType.startsWith("l")) { 1698 z.sub2(attached[pt], atom); 1699 z.normalize(); 1700 return hybridization; 1701 } 1702 1703 switch (nAttached) { 1704 case 0: 1705 if (lcaoType.equals("sp3c") || lcaoType.equals("sp2d") 1706 || lcaoType.equals("lpa")) { 1707 z.set(-0.5f, -0.7f, 1); 1708 x.set(1, 0, 0); 1709 } else if (lcaoType.equals("sp3b") || lcaoType.equals("lpb")) { 1710 z.set(0.5f, -0.7f, -1f); 1711 x.set(1, 0, 0); 1712 } else if (lcaoType.equals("sp3a")) { 1713 z.set(0, 1, 0); 1714 x.set(1, 0, 0); 1715 } else { 1716 z.set(0, 0, 1); 1717 x.set(1, 0, 0); 1718 } 1719 break; 1720 case 1: 1721 // X-C 1722 vTemp.setT(vRef); 1723 x.cross(vTemp, z); 1724 if (isSp3) { 1725 // align z as sp3 orbital 1726 // with reference to atoms connected to connecting atom. 1727 // vRef is a pseudo-random vector 1728 // z is along the bond 1729 for (int i = attached[0].getBondCount(); --i >= 0;) { 1730 if (attached[0].bonds[i].isCovalent() 1731 && attached[0].getBondedAtomIndex(i) != atom.i) { 1732 x.sub2(attached[0], attached[0].bonds[i].getOtherAtom(attached[0])); 1733 x.cross(z, x); 1734 if (x.length() == 0) 1735 continue; 1736 x.cross(x, z); 1737 break; 1738 } 1739 } 1740 x.normalize(); 1741 if (Float.isNaN(x.x)) { 1742 x.setT(vRef); 1743 x.cross(x, z); 1744 } 1745 // x is perp to bond 1746 vTemp.cross(z, x); 1747 vTemp.normalize(); 1748 // y1 is perp to bond and x 1749 z.normalize(); 1750 x.scaleAdd2(2.828f, x, z); // 2*sqrt(2) 1751 if (pt != 3) { 1752 x.normalize(); 1753 // PI*2/3 1754 new M3().setAA(A4.new4(z.x, z.y, z.z, 1755 (pt == 2 ? 1 : -1) * 2.09439507f)).rotate(x); 1756 } 1757 z.setT(x); 1758 x.cross(vTemp, z); 1759 break; 1760 } 1761 // not "sp3" -- sp2 or lone pair 1762 vTemp.cross(x, z); //x and vTemp are now perpendicular to z 1763 switch (attached[0].getCovalentBondCount()) { 1764 case 1: 1765 if (attached[0].getValence() != 2) { 1766 // C-t-C 1767 break; 1768 } 1769 // C=C, no other atoms 1770 //$FALL-THROUGH$ 1771 case 2: 1772 // R-C=C* or C=C=C* 1773 // get third atom 1774 boolean isCumulated = false; 1775 Atom a0 = attached[0]; 1776 x.setT(z); 1777 vTemp.setT(vRef); 1778 while (a0 != null && a0.getCovalentBondCount() == 2) { 1779 Bond[] bonds = a0.bonds; 1780 Atom a = null; 1781 isCumulated = !isCumulated; 1782 for (int i = 0; i < bonds.length; i++) 1783 if (bonds[i].isCovalent()) { 1784 a = bonds[i].getOtherAtom(a0); 1785 if (a != atom) { 1786 vTemp.sub2(a, a0); 1787 break; 1788 } 1789 } 1790 vTemp.cross(vTemp, x); 1791 if (vTemp.length() > 0.1f || a.getCovalentBondCount() != 2) 1792 break; 1793 atom = a0; 1794 a0 = a; 1795 } 1796 if (isSp) { 1797 } else if (vTemp.length() > 0.1f) { 1798 z.cross(vTemp, x); 1799 // C=C or RC=C 1800 z.normalize(); 1801 if (pt == 1) 1802 z.scale(-1); 1803 z.scale(sqrt3_2); 1804 z.scaleAdd2(0.5f, x, z); 1805 if (isP) { 1806 vTemp.cross(z, x); 1807 z.setT(vTemp); 1808 vTemp.setT(x); 1809 } 1810 x.cross(vTemp, z); 1811 } else { 1812 z.setT(x); 1813 x.cross(vRef, x); 1814 } 1815 break; 1816 case 3: 1817 // special case, for example R2C=O oxygen 1818 getHybridizationAndAxes(attached[0].i, 0, x, vTemp, "pz", false, 1819 doAlignZ, isQuick); 1820 vTemp.setT(x); 1821 if (isSp2) { // align z as sp2 orbital 1822 x.cross(x, z); 1823 if (pt == 1) 1824 x.scale(-1); 1825 x.scale(sqrt3_2); 1826 z.scaleAdd2(0.5f, z, x); 1827 } else if (isSp) { 1828 1829 } else { 1830 vTemp.setT(z); 1831 z.setT(x); 1832 } 1833 x.cross(vTemp, z); 1834 break; 1835 } 1836 break; 1837 case 2: 1838 // two attached atoms -- check for linearity 1839 if (z.length() < 0.1f) { 1840 // linear A--X--B 1841 if (!lcaoType.equals("pz")) { 1842 Atom a = attached[0]; 1843 boolean ok = (a.getCovalentBondCount() == 3); 1844 if (!ok) 1845 ok = ((a = attached[1]).getCovalentBondCount() == 3); 1846 if (ok) { 1847 // special case, for example R2C=C=CR2 central carbon 1848 getHybridizationAndAxes(a.i, 0, x, z, "pz", false, doAlignZ, isQuick); 1849 if (lcaoType.equals("px")) 1850 x.scale(-1); 1851 z.setT(v[0]); 1852 break; 1853 } 1854 // O-C*-O 1855 vTemp.setT(vRef); 1856 z.cross(vTemp, x); 1857 vTemp.cross(z, x); 1858 } 1859 z.setT(x); 1860 x.cross(vTemp, z); 1861 break; 1862 } 1863 // bent A--X--B 1864 vTemp.cross(z, x); 1865 if (isSp2) { // align z as sp2 orbital 1866 x.cross(z, vTemp); 1867 break; 1868 } 1869 if (isSp3 || isLp) { // align z as lone pair 1870 vTemp.normalize(); 1871 z.normalize(); 1872 if (!lcaoType.equals("lp")) { 1873 if (pt == 0 || pt == 2) 1874 z.scaleAdd2(-1.2f, vTemp, z); 1875 else 1876 z.scaleAdd2(1.2f, vTemp, z); 1877 } 1878 x.cross(z, vTemp); 1879 break; 1880 } 1881 // align z as p orbital 1882 x.cross(z, vTemp); 1883 z.setT(vTemp); 1884 if (z.z < 0) { 1885 z.scale(-1); 1886 x.scale(-1); 1887 } 1888 break; 1889 default: 1890 // 3 bonds, sp3 or sp2 and lp/p 1891 if (isSp3) 1892 break; 1893 if (!isPlanar) { 1894 // not aligned -- really sp3 1895 x.cross(z, x); 1896 break; 1897 } 1898 // align z as p orbital 1899 z.setT(vTemp); 1900 if (z.z < 0 && doAlignZ) { 1901 z.scale(-1); 1902 x.scale(-1); 1903 } 1904 } 1905 1906 x.normalize(); 1907 z.normalize(); 1908 1909 return hybridization; 1910 } 1911 1912 /** 1913 * dsp3 (trigonal bipyramidal, see-saw, T-shaped) 1914 * or d2sp3 (square planar, square pyramidal, octahedral) 1915 * 1916 * @param atomIndex 1917 * @param z 1918 * @param x 1919 * @param lcaoType 1920 * @return valid hybridization or null 1921 */ getHybridizationAndAxesD(int atomIndex, V3 z, V3 x, String lcaoType)1922 private String getHybridizationAndAxesD(int atomIndex, V3 z, V3 x, 1923 String lcaoType) { 1924 // note -- d2sp3, not sp3d2; dsp3, not sp3d 1925 if (lcaoType.startsWith("sp3d2")) 1926 lcaoType = "d2sp3" 1927 + (lcaoType.length() == 5 ? "a" : lcaoType.substring(5)); 1928 if (lcaoType.startsWith("sp3d")) 1929 lcaoType = "dsp3" 1930 + (lcaoType.length() == 4 ? "a" : lcaoType.substring(4)); 1931 if (lcaoType.equals("d2sp3") || lcaoType.equals("dsp3")) 1932 lcaoType += "a"; 1933 boolean isTrigonal = lcaoType.startsWith("dsp3"); 1934 int pt = lcaoType.charAt(lcaoType.length() - 1) - 'a'; 1935 if (z != null && (!isTrigonal && (pt > 5 || !lcaoType.startsWith("d2sp3")) 1936 || isTrigonal && pt > 4)) 1937 return null; 1938 1939 // pt: a 0 b 1 c 2 d 3 e 4 f 5 1940 1941 Atom atom = at[atomIndex]; 1942 Atom[] attached = getAttached(atom, 6, true, false); 1943 if (attached == null) 1944 return (z == null ? null : "?"); 1945 int nAttached = attached.length; 1946 if (nAttached < 3 && z != null) 1947 return null; 1948 boolean isLP = (pt >= nAttached); 1949 1950 // determine geometry 1951 1952 int nAngles = nAttached * (nAttached - 1) / 2; 1953 int[][] angles = AU.newInt2(nAngles); 1954 1955 // all attached angles must be around 180, 120, or 90 degrees 1956 1957 int[] ntypes = new int[3]; 1958 int[][] typePtrs = new int[3][nAngles]; 1959 1960 int n = 0; 1961 int _90 = 0; 1962 int _120 = 1; 1963 int _180 = 2; 1964 int n120_atom0 = 0; 1965 for (int i = 0; i < nAttached - 1; i++) 1966 for (int j = i + 1; j < nAttached; j++) { 1967 float angle = Measure 1968 .computeAngleABC(attached[i], atom, attached[j], true); 1969 // cutoffs determined empirically and meant to be generous 1970 int itype = (angle < 105 ? _90 : angle >= 150 ? _180 : _120); 1971 typePtrs[itype][ntypes[itype]] = n; 1972 ntypes[itype]++; 1973 angles[n++] = new int[] { i, j }; 1974 if (i == 0 && itype == _120) 1975 n120_atom0++; 1976 } 1977 // categorization is done simply by listing 1978 // the number of 90, 120, and 180 angles. 1979 n = ntypes[_90] * 100 + ntypes[_120] * 10 + ntypes[_180]; 1980 if (z == null) { 1981 // just return geometry 1982 switch (n) { 1983 default: 1984 return ""; 1985 case 0: 1986 return "";// just ignore atoms with only one bond? (atom.getElementNumber() == 1 ? "s" : ""); 1987 case 1: 1988 return "linear"; 1989 case 100: 1990 case 10: 1991 return "bent"; 1992 case 111: 1993 case 201: 1994 return "T-shaped";// -- AX3E or AX3E2 or AX3E3 1995 case 30: 1996 case 120: 1997 case 210: 1998 case 300: 1999 if (Math.abs(Measure.computeTorsion(attached[0], atom, attached[1], attached[2], true)) > 162) 2000 return "trigonal planar";// -- AX3 2001 return "trigonal pyramidal";// -- AX3E 2002 case 330: 2003 // may just have a rather distorted tetrahedron, as in "$phosphorus pentoxide" 2004 // in that case, each atom will have 1 or 3 120o angles, not 0 or 2, as in trigonal pyramid 2005 return (n120_atom0 % 2 == 1 ? "tetrahedral" : "uncapped trigonal pyramid");// -- AX4 or AX4E 2006 case 60: 2007 case 150: 2008 case 240: 2009 return "tetrahedral";// -- AX4 2010 case 402: 2011 return "square planar";// -- AX4E2 2012 case 411: 2013 case 501: 2014 return "see-saw";// -- AX4E 2015 case 631: 2016 return "trigonal bipyramidal";// -- AX5 2017 case 802: 2018 return "uncapped square pyramid";// -- AX5E 2019 case 1203: 2020 return "octahedral";// -- AX6 2021 } 2022 } 2023 2024 switch (n) { 2025 default: 2026 return null; 2027 // 111 is also possible, but quite odd 2028 case 201: 2029 // 201 T-shaped -- could be either 2030 break; 2031 case 210: 2032 case 330: 2033 case 411: 2034 case 631: 2035 // 210 no name (90-90-120) 2036 // 411 see-saw 2037 // 330 trigonal pyramid 2038 // 631 trigonal bipyramidal 2039 if (!isTrigonal) 2040 return null; 2041 break; 2042 case 300: 2043 case 402: 2044 case 501: 2045 case 802: 2046 case 1203: 2047 // 300 no name (90-90-90) 2048 // 402 square planar 2049 // 501 no name (see-saw like, but with 90o angle) 2050 // 802 square pyramidal 2051 // 1203 octahedral 2052 if (isTrigonal) 2053 return null; 2054 break; 2055 } 2056 // if subType a-f is pointing to an attached atom, use it 2057 // otherwise, we need to find the position 2058 if (isLP) { 2059 int[] a; 2060 BS bs; 2061 if (isTrigonal) { 2062 switch (ntypes[_120]) { 2063 case 0: 2064 // T-shaped 2065 z.sub2(attached[angles[typePtrs[_90][0]][0]], atom); 2066 x.sub2(attached[angles[typePtrs[_90][0]][1]], atom); 2067 z.cross(z, x); 2068 z.normalize(); 2069 if (pt == 4) 2070 z.scale(-1); 2071 bs = findNotAttached(nAttached, angles, typePtrs[_180], ntypes[_180]); 2072 int i = bs.nextSetBit(0); 2073 x.sub2(attached[i], atom); 2074 x.normalize(); 2075 x.scale(0.5f); 2076 z.scaleAdd2(sqrt3_2, z, x); 2077 pt = -1; 2078 break; 2079 case 1: 2080 // see-saw 2081 if (pt == 4) { 2082 a = angles[typePtrs[_120][0]]; 2083 z.add2(attached[a[0]], attached[a[1]]); 2084 z.scaleAdd2(-2, atom, z); 2085 pt = -1; 2086 } else { 2087 bs = findNotAttached(nAttached, angles, typePtrs[_120], ntypes[_120]); 2088 pt = bs.nextSetBit(0); 2089 } 2090 break; 2091 default: 2092 // unobserved nor-apical trigonal bipyramid 2093 // or highly distorted trigonal pyramid (PH3) 2094 bs = findNotAttached(nAttached, angles, typePtrs[_120], ntypes[_120]); 2095 pt = bs.nextSetBit(0); 2096 } 2097 } else { 2098 boolean isPlanar = false; 2099 if (nAttached == 4) { 2100 switch (ntypes[_180]) { 2101 case 1: 2102 // unobserved cis-nor-octahedron 2103 bs = findNotAttached(nAttached, angles, typePtrs[_180], 2104 ntypes[_180]); 2105 int i = bs.nextSetBit(0); 2106 if (pt == 4) 2107 pt = i; 2108 else 2109 pt = bs.nextSetBit(i + 1); 2110 break; 2111 default: 2112 // square planar 2113 isPlanar = true; 2114 } 2115 } else { 2116 // square pyramidal 2117 bs = findNotAttached(nAttached, angles, typePtrs[_180], ntypes[_180]); 2118 int i = bs.nextSetBit(0); 2119 for (int j = nAttached; j < pt && i >= 0; j++) 2120 i = bs.nextSetBit(i + 1); 2121 if (i == -1) 2122 isPlanar = true; 2123 else 2124 pt = i; 2125 } 2126 if (isPlanar) { 2127 // square planar or T-shaped 2128 z.sub2(attached[angles[typePtrs[_90][0]][0]], atom); 2129 x.sub2(attached[angles[typePtrs[_90][0]][1]], atom); 2130 z.cross(z, x); 2131 if (pt == 4) 2132 z.scale(-1); 2133 pt = -1; 2134 } 2135 } 2136 } 2137 if (pt >= 0) 2138 z.sub2(attached[pt], atom); 2139 if (isLP) 2140 z.scale(-1); 2141 z.normalize(); 2142 return (isTrigonal ? "dsp3" : "d2sp3"); 2143 } 2144 getAttached(Atom atom, int nMax, boolean doSort, boolean isQuick)2145 private Atom[] getAttached(Atom atom, int nMax, boolean doSort, boolean isQuick) { 2146 int nAttached = atom.getCovalentBondCount(); 2147 if (nAttached > nMax) 2148 return null; 2149 Atom[] attached = new Atom[nAttached]; 2150 if (nAttached > 0) { 2151 Bond[] bonds = atom.bonds; 2152 int n = 0; 2153 for (int i = 0; i < bonds.length; i++) 2154 if (bonds[i].isCovalent()) { 2155 Atom a = bonds[i].getOtherAtom(atom); 2156 if (!isQuick || a.getAtomicAndIsotopeNumber() != 1) 2157 attached[n++] = a; 2158 } 2159 if (doSort && !isQuick) 2160 Arrays.sort(attached, new AtomSorter()); 2161 } 2162 return attached; 2163 } 2164 findNotAttached(int nAttached, int[][] angles, int[] ptrs, int nPtrs)2165 private BS findNotAttached(int nAttached, int[][] angles, int[] ptrs, int nPtrs) { 2166 BS bs = BS.newN(nAttached); 2167 bs.setBits(0, nAttached); 2168 for (int i = 0; i < nAttached; i++) 2169 for (int j = 0; j < nPtrs; j++) { 2170 int[] a = angles[ptrs[j]]; 2171 if (a[0] == i || a[1] == i) 2172 bs.clear(i); 2173 } 2174 return bs; 2175 } 2176 2177 protected class AtomSorter implements Comparator<Atom>{ 2178 @Override compare(Atom a1, Atom a2)2179 public int compare(Atom a1, Atom a2) { 2180 return (a1.i > a2.i ? 1 : a1.i < a2.i ? -1 : 0); 2181 } 2182 } 2183 2184 /* 2185 * ****************************************************** 2186 * 2187 * These next methods are used by Eval to select for specific atom sets. They 2188 * all return a BitSet 2189 * 2190 * ****************************************************** 2191 */ 2192 2193 /** 2194 * general unqualified lookup of atom set type 2195 * 2196 * @param tokType 2197 * @param specInfo 2198 * @param bs 2199 * - to be filled 2200 * @return BitSet; or null if we mess up the type 2201 */ getAtomBitsMDa(int tokType, Object specInfo, BS bs)2202 public BS getAtomBitsMDa(int tokType, Object specInfo, BS bs) { 2203 int iSpec = (specInfo instanceof Integer ? ((Integer) specInfo).intValue() 2204 : 0); 2205 2206 switch (tokType) { 2207 case T.atomname: 2208 case T.atomtype: 2209 // within(atomname 2210 // within(atomtype 2211 boolean isType = (tokType == T.atomtype); 2212 String names = "," + specInfo + ","; 2213 for (int i = ac; --i >= 0;) { 2214 if (at[i] == null) 2215 continue; 2216 String s = (isType ? at[i].getAtomType() : at[i].getAtomName()); 2217 if (names.indexOf("," + s + ",") >= 0) 2218 bs.set(i); 2219 } 2220 return bs; 2221 case T.atomno: 2222 for (int i = ac; --i >= 0;) { 2223 if (at[i] != null && at[i].getAtomNumber() == iSpec) 2224 bs.set(i); 2225 } 2226 return bs; 2227 case T.bonded: 2228 for (int i = ac; --i >= 0;) { 2229 if (at[i] != null && at[i].getCovalentBondCount() > 0) 2230 bs.set(i); 2231 } 2232 return bs; 2233 case T.carbohydrate: 2234 case T.dna: 2235 case T.helix: // WITHIN -- not ends 2236 case T.nucleic: 2237 case T.protein: 2238 case T.purine: 2239 case T.pyrimidine: 2240 case T.rna: 2241 case T.sheet: // WITHIN -- not ends 2242 return (((ModelSet)this).haveBioModels ? ((ModelSet)this).bioModelset.getAtomBitsBS(tokType, null, bs) : bs); 2243 case T.hydrogen: 2244 iSpec = 1; 2245 //$FALL-THROUGH$ 2246 case T.elemno: 2247 for (int i = ac; --i >= 0;) { 2248 if (at[i] != null && at[i].getElementNumber() == iSpec) 2249 bs.set(i); 2250 } 2251 return bs; 2252 case T.hetero: 2253 for (int i = ac; --i >= 0;) { 2254 if (at[i] != null && at[i].isHetero()) 2255 bs.set(i); 2256 } 2257 return bs; 2258 case T.identifier: 2259 return getIdentifierOrNull((String) specInfo); 2260 case T.leadatom: 2261 for (int i = ac; --i >= 0;) { 2262 if (at[i] != null && at[i].isLeadAtom()) 2263 bs.set(i); 2264 } 2265 return bs; 2266 case T.polymer: 2267 case T.structure: 2268 return (((ModelSet)this).haveBioModels ? ((ModelSet)this).bioModelset.getAtomBitsBS(tokType, (BS) specInfo, bs) 2269 : bs); 2270 case T.resno: 2271 // could be PDB type file with UNK 2272 for (int i = ac; --i >= 0;) { 2273 if (at[i] != null && at[i].getResno() == iSpec) 2274 bs.set(i); 2275 } 2276 return bs; 2277 case T.solvent: 2278 // fast search for water 2279 2280 // this is faster by a factor of 2 Jmol 14.1.12 -BH 2281 2282 //"@water _g>=" + GROUPID_WATER + " & _g<" + GROUPID_SOLVENT_MIN 2283 //+ ", oxygen & connected(2) & connected(2, hydrogen), 2284 //(hydrogen) & connected(oxygen & connected(2) & connected(2, hydrogen))", 2285 2286 int[] hs = new int[2]; 2287 Atom a; 2288 for (int i = ac; --i >= 0;) { 2289 if (at[i] == null) 2290 continue; 2291 int g = at[i].group.groupID; 2292 if (g >= JC.GROUPID_WATER && g < JC.GROUPID_SOLVENT_MIN) { 2293 bs.set(i); 2294 } else if ((a = at[i]).getElementNumber() == 8 2295 && a.getCovalentBondCount() == 2) { 2296 Bond[] bonds = a.bonds; 2297 int n = 0; 2298 Atom b; 2299 for (int j = bonds.length; --j >= 0 && n < 3;) 2300 if (bonds[j].isCovalent() 2301 && (b = bonds[j].getOtherAtom(a)).getElementNumber() == 1) 2302 hs[n++ % 2] = b.i; 2303 if (n == 2) { 2304 bs.set(hs[1]); 2305 bs.set(hs[0]); 2306 bs.set(i); 2307 } 2308 } 2309 } 2310 return bs; 2311 case T.spec_alternate: 2312 // could be PDB type file with UNK or a modulated CIF file subsystem 2313 String spec = (String) specInfo; 2314 for (int i = ac; --i >= 0;) 2315 if (at[i] != null && isAltLoc(at[i].altloc, spec)) 2316 bs.set(i); 2317 return bs; 2318 case T.spec_atom: 2319 String atomSpec = ((String) specInfo).toUpperCase(); 2320 if (atomSpec.indexOf("\\?") >= 0) 2321 atomSpec = PT.rep(atomSpec, "\\?", "\1"); 2322 // / here xx*yy is NOT changed to "xx??????????yy" 2323 boolean allowStar = atomSpec.startsWith("?*"); 2324 if (allowStar) 2325 atomSpec = atomSpec.substring(1); 2326 for (int i = ac; --i >= 0;) 2327 if (at[i] != null && isAtomNameMatch(at[i], atomSpec, allowStar, allowStar)) 2328 bs.set(i); 2329 return bs; 2330 case T.spec_chain: 2331 return BSUtil.copy(getChainBits(iSpec)); 2332 case T.spec_name_pattern: 2333 return getSpecName((String) specInfo); 2334 case T.spec_resid: 2335 // could be PDB type file with UNK 2336 for (int i = ac; --i >= 0;) 2337 if (at[i] != null && at[i].group.groupID == iSpec) 2338 bs.set(i); 2339 return bs; 2340 case T.spec_seqcode: 2341 return BSUtil.copy(getSeqcodeBits(iSpec, true)); 2342 case T.inscode: 2343 for (int i = ac; --i >= 0;) 2344 if (at[i] != null && at[i].group.getInsCode() == iSpec) 2345 bs.set(i); 2346 return bs; 2347 case T.symop: 2348 for (int i = ac; --i >= 0;) 2349 if (at[i] != null && at[i].getSymOp() == iSpec) 2350 bs.set(i); 2351 return bs; 2352 } 2353 2354 BS bsTemp; 2355 BS bsInfo = (BS) specInfo; 2356 int i0 = bsInfo.nextSetBit(0); 2357 if (i0 < 0) 2358 return bs; 2359 switch (tokType) { 2360 case T.model: 2361 // within(model 2362 bsTemp = BSUtil.copy(bsInfo); 2363 for (int i = i0; i >= 0; i = bsTemp.nextSetBit(i + 1)) { 2364 bs.or(((ModelSet) this).am[at[i].mi].bsAtoms); 2365 bsTemp.andNot(bs); 2366 } 2367 return bs; 2368 case T.chain: 2369 // within(chain 2370 bsTemp = BSUtil.copy(bsInfo); 2371 for (int i = i0; i >= 0; i = bsTemp.nextSetBit(i + 1)) { 2372 at[i].group.chain.setAtomBits(bs); 2373 bsTemp.andNot(bs); 2374 } 2375 return bs; 2376 case T.element: 2377 // within(element 2378 bsTemp = new BS(); 2379 for (int i = i0; i >= 0; i = bsInfo.nextSetBit(i + 1)) 2380 bsTemp.set(at[i].getElementNumber()); 2381 for (int i = ac; --i >= 0;) 2382 if (at[i] != null && bsTemp.get(at[i].getElementNumber())) 2383 bs.set(i); 2384 return bs; 2385 case T.group: 2386 // within(group 2387 bsTemp = BSUtil.copy(bsInfo); 2388 for (int i = i0; i >= 0; i = bsTemp.nextSetBit(i + 1)) { 2389 at[i].group.setAtomBits(bs); 2390 bsTemp.andNot(bs); 2391 } 2392 return bs; 2393 case T.site: 2394 // within(site 2395 bsTemp = new BS(); 2396 for (int i = i0; i >= 0; i = bsInfo.nextSetBit(i + 1)) 2397 bsTemp.set(at[i].atomSite); 2398 for (int i = ac; --i >= 0;) 2399 if (at[i] != null && bsTemp.get(at[i].atomSite)) 2400 bs.set(i); 2401 return bs; 2402 } 2403 Logger.error("MISSING getAtomBits entry for " + T.nameOf(tokType)); 2404 return bs; 2405 } 2406 getChainBits(int chainID)2407 public BS getChainBits(int chainID) { 2408 boolean caseSensitive = vwr.getBoolean(T.chaincasesensitive); 2409 if (caseSensitive) { 2410 // check for script compiler run before setting chain ids 2411 if (chainID >= 97 && chainID <= 122) 2412 chainID += 159; 2413 } else { 2414 if (chainID >= 0 && chainID < 300) 2415 chainID = chainToUpper(chainID); 2416 } 2417 BS bs = new BS(); 2418 BS bsDone = BS.newN(ac); 2419 int id; 2420 for (int i = bsDone.nextClearBit(0); i < ac; i = bsDone.nextClearBit(i + 1)) { 2421 if (at[i] == null) 2422 continue; 2423 Chain chain = at[i].group.chain; 2424 if (chainID == (id = chain.chainID) || !caseSensitive 2425 && id >= 0 && id < 300 && chainID == chainToUpper(id)) { 2426 chain.setAtomBits(bs); 2427 bsDone.or(bs); 2428 } else { 2429 chain.setAtomBits(bsDone); 2430 } 2431 } 2432 return bs; 2433 } 2434 chainToUpper(int chainID)2435 public int chainToUpper(int chainID) { 2436 return (chainID >= 97 && chainID <= 122 ? chainID - 32 2437 : chainID >= 256 && chainID < 300 ? chainID - 191 2438 // must be single-character lower-case 256=="a" 2439 // to lower case is to 65, so subtract (256-65) 2440 : chainID); 2441 } 2442 isAltLoc(char altloc, String strPattern)2443 private boolean isAltLoc(char altloc, String strPattern) { 2444 if (strPattern == null) 2445 return (altloc == '\0'); 2446 if (strPattern.length() != 1) 2447 return false; 2448 char ch = strPattern.charAt(0); 2449 return (ch == '*' || ch == '?' && altloc != '\0' || altloc == ch); 2450 } 2451 getSeqcodeBits(int seqcode, boolean returnEmpty)2452 public BS getSeqcodeBits(int seqcode, boolean returnEmpty) { 2453 BS bs = new BS(); 2454 int seqNum = Group.getSeqNumberFor(seqcode); 2455 boolean haveSeqNumber = (seqNum != Integer.MAX_VALUE); 2456 boolean isEmpty = true; 2457 char insCode = Group.getInsertionCodeChar(seqcode); 2458 switch (insCode) { 2459 case '?': 2460 for (int i = ac; --i >= 0;) { 2461 if (at[i] == null) 2462 continue; 2463 int atomSeqcode = at[i].group.seqcode; 2464 if ((!haveSeqNumber 2465 || seqNum == Group.getSeqNumberFor(atomSeqcode)) 2466 && Group.getInsertionCodeFor(atomSeqcode) != 0) { 2467 bs.set(i); 2468 isEmpty = false; 2469 } 2470 } 2471 break; 2472 default: 2473 for (int i = ac; --i >= 0;) { 2474 if (at[i] == null) 2475 continue; 2476 int atomSeqcode = at[i].group.seqcode; 2477 if (seqcode == atomSeqcode || 2478 !haveSeqNumber && seqcode == Group.getInsertionCodeFor(atomSeqcode) 2479 || insCode == '*' && seqNum == Group.getSeqNumberFor(atomSeqcode)) { 2480 bs.set(i); 2481 isEmpty = false; 2482 } 2483 } 2484 } 2485 return (!isEmpty || returnEmpty ? bs : null); 2486 } 2487 2488 2489 /** 2490 * overhauled by RMH Nov 1, 2006. 2491 * 2492 * @param identifier 2493 * @return null or bs 2494 */ getIdentifierOrNull(String identifier)2495 private BS getIdentifierOrNull(String identifier) { 2496 //a primitive lookup scheme when [ ] are not used 2497 //nam 2498 //na? 2499 //nam45 2500 //nam45C 2501 //nam45^ 2502 //nam45^A 2503 //nam45^AC -- note, no colon here -- if present, handled separately 2504 //nam4? does NOT match anything for PDB files, but might for others 2505 //atom specifiers: 2506 //H? 2507 //H32 2508 //H3? 2509 2510 //in the case of a ?, we take the whole thing 2511 // * can be used here, but not with ? 2512 // Jmol 14.1.14: allows initial ?* to be same as * 2513 // and therefore can be used in atom expression 2514 2515 //first check with * option OFF 2516 BS bs = getSpecNameOrNull(identifier, false); 2517 2518 if (identifier.indexOf("\\?") >= 0) 2519 identifier = PT.rep(identifier, "\\?","\1"); 2520 return (bs != null || identifier.indexOf("?") > 0 ? bs 2521 : identifier.indexOf("*") > 0 ? getSpecNameOrNull(identifier, true) 2522 : ((ModelSet)this).haveBioModels ? ((ModelSet)this).bioModelset.getIdentifierOrNull(identifier) 2523 : null); 2524 } 2525 getSpecName(String name)2526 private BS getSpecName(String name) { 2527 // * can be used here with ? 2528 BS bs = getSpecNameOrNull(name, false); 2529 if (bs != null) 2530 return bs; 2531 if (name.indexOf("*") > 0) 2532 bs = getSpecNameOrNull(name, true); 2533 return (bs == null ? new BS() : bs); 2534 } 2535 getSpecNameOrNull(String name, boolean checkStar)2536 public BS getSpecNameOrNull(String name, boolean checkStar) { 2537 /// here xx*yy is changed to "xx??????????yy" when coming from getSpecName 2538 /// but not necessarily when coming from getIdentifierOrNull 2539 BS bs = null; 2540 name = name.toUpperCase(); 2541 if (name.indexOf("\\?") >= 0) 2542 name = PT.rep(name, "\\?","\1"); 2543 boolean allowInitialStar = name.startsWith("?*"); 2544 if (allowInitialStar) 2545 name = name.substring(1); 2546 2547 for (int i = ac; --i >= 0;) { 2548 if (at[i] == null) 2549 continue; 2550 String g3 = at[i].getGroup3(true); 2551 if (g3 != null && g3.length() > 0) { 2552 if (PT.isMatch(g3, name, checkStar, true)) { 2553 if (bs == null) 2554 bs = BS.newN(i + 1); 2555 bs.set(i); 2556 while (--i >= 0 && at[i].getGroup3(true).equals(g3)) 2557 bs.set(i); 2558 i++; 2559 } 2560 } else if (isAtomNameMatch(at[i], name, checkStar, allowInitialStar)) { 2561 if (bs == null) 2562 bs = BS.newN(i + 1); 2563 bs.set(i); 2564 } 2565 } 2566 return bs; 2567 } 2568 isAtomNameMatch(Atom atom, String strPattern, boolean checkStar, boolean allowInitialStar)2569 private boolean isAtomNameMatch(Atom atom, String strPattern, boolean checkStar, boolean allowInitialStar) { 2570 // called from getAtomBitsMa (spec_atom) and getSpecNameOrNull 2571 /// here xx*yy is changed to "xx??????????yy" when coming from getSpecName 2572 /// but not necessarily when coming from getIdentifierOrNull 2573 /// and NOT when coming from getAtomBits with Token.spec_atom 2574 /// because it is presumed that some names can include "*" 2575 return PT.isMatch(atom.getAtomName().toUpperCase(), strPattern, 2576 checkStar, allowInitialStar); 2577 } 2578 getAtomIndices(BS bs)2579 public int[] getAtomIndices(BS bs) { 2580 int n = 0; 2581 int[] indices = new int[ac]; 2582 for (int j = bs.nextSetBit(0); j >= 0 && j < ac; j = bs.nextSetBit(j + 1)) 2583 indices[j] = ++n; 2584 return indices; 2585 } 2586 getAtomsNearPlane(float distance, P4 plane)2587 public BS getAtomsNearPlane(float distance, P4 plane) { 2588 BS bsResult = new BS(); 2589 for (int i = ac; --i >= 0;) { 2590 Atom atom = at[i]; 2591 if (atom == null) 2592 continue; 2593 float d = Measure.distanceToPlane(plane, atom); 2594 if (distance > 0 && d >= -0.1 && d <= distance || distance < 0 2595 && d <= 0.1 && d >= distance || distance == 0 && Math.abs(d) < 0.01) 2596 bsResult.set(atom.i); 2597 } 2598 return bsResult; 2599 } 2600 clearVisibleSets()2601 public void clearVisibleSets() { 2602 haveBSVisible = false; 2603 haveBSClickable = false; 2604 } 2605 getAtomsInFrame(BS bsAtoms)2606 public void getAtomsInFrame(BS bsAtoms) { 2607 clearVisibleSets(); 2608 bsAtoms.clearAll(); 2609 for (int i = ac; --i >= 0;) 2610 if (at[i] != null && at[i].isVisible(Atom.ATOM_INFRAME)) 2611 bsAtoms.set(i); 2612 } 2613 getVisibleSet(boolean forceNew)2614 public BS getVisibleSet(boolean forceNew) { 2615 if (forceNew) { 2616 vwr.setModelVisibility(); 2617 vwr.shm.finalizeAtoms(null, true); 2618 } else if (haveBSVisible) { 2619 return bsVisible; 2620 } 2621 bsVisible.clearAll(); 2622 for (int i = ac; --i >= 0;) 2623 if (at[i] != null && at[i].checkVisible()) 2624 bsVisible.set(i); 2625 if (vwr.shm.bsSlabbedInternal != null) 2626 bsVisible.andNot(vwr.shm.bsSlabbedInternal); 2627 haveBSVisible = true; 2628 return bsVisible; 2629 } 2630 getClickableSet(boolean forceNew)2631 public BS getClickableSet(boolean forceNew) { 2632 if (forceNew) 2633 vwr.setModelVisibility(); 2634 else if (haveBSClickable) 2635 return bsClickable; 2636 bsClickable.clearAll(); 2637 for (int i = ac; --i >= 0;) 2638 if (at[i] != null && at[i].isClickable()) 2639 bsClickable.set(i); 2640 haveBSClickable = true; 2641 return bsClickable; 2642 } 2643 isModulated(int i)2644 public boolean isModulated(int i) { 2645 return bsModulated != null && bsModulated.get(i); 2646 } 2647 deleteModelAtoms(int firstAtomIndex, int nAtoms, BS bsAtoms)2648 protected void deleteModelAtoms(int firstAtomIndex, int nAtoms, BS bsAtoms) { 2649 // all atoms in the model are being deleted here 2650 at = (Atom[]) AU.deleteElements(at, firstAtomIndex, nAtoms); 2651 ac = at.length; 2652 for (int j = firstAtomIndex; j < ac; j++) { 2653 at[j].i = j; 2654 at[j].mi--; 2655 } 2656 // fix modulation and tensors 2657 if (bsModulated != null) 2658 BSUtil.deleteBits(bsModulated, bsAtoms); 2659 2660 deleteAtomTensors(bsAtoms); 2661 atomNames = (String[]) AU.deleteElements(atomNames, firstAtomIndex, 2662 nAtoms); 2663 atomTypes = (String[]) AU.deleteElements(atomTypes, firstAtomIndex, 2664 nAtoms); 2665 atomResnos = (int[]) AU.deleteElements(atomResnos, firstAtomIndex, 2666 nAtoms); 2667 atomSerials = (int[]) AU.deleteElements(atomSerials, firstAtomIndex, 2668 nAtoms); 2669 atomSeqIDs = (int[]) AU.deleteElements(atomSeqIDs, firstAtomIndex, 2670 nAtoms); 2671 dssrData = (float[]) AU.deleteElements(dssrData, firstAtomIndex, 2672 nAtoms); 2673 bfactor100s = (short[]) AU.deleteElements(bfactor100s, 2674 firstAtomIndex, nAtoms); 2675 hasBfactorRange = false; 2676 occupancies = (float[]) AU.deleteElements(occupancies, 2677 firstAtomIndex, nAtoms); 2678 resetPartialCharges(); 2679 atomTensorList = (Object[][]) AU.deleteElements(atomTensorList, 2680 firstAtomIndex, nAtoms); 2681 vibrations = (Vibration[]) AU.deleteElements(vibrations, 2682 firstAtomIndex, nAtoms); 2683 nSurfaceAtoms = 0; 2684 bsSurface = null; 2685 surfaceDistance100s = null; 2686 if (tainted != null) 2687 for (int i = 0; i < TAINT_MAX; i++) 2688 BSUtil.deleteBits(tainted[i], bsAtoms); 2689 // what about data? 2690 } 2691 getAtomIdentityInfo(int i, Map<String, Object> info, P3 ptTemp)2692 public void getAtomIdentityInfo(int i, Map<String, Object> info, P3 ptTemp) { 2693 info.put("_ipt", Integer.valueOf(i)); 2694 info.put("atomIndex", Integer.valueOf(i)); 2695 info.put("atomno", Integer.valueOf(at[i].getAtomNumber())); 2696 info.put("info", getAtomInfo(i, null, ptTemp)); 2697 info.put("sym", at[i].getElementSymbol()); 2698 } 2699 getAtomTensorList(int i)2700 public Object[] getAtomTensorList(int i) { 2701 return (i < 0 || atomTensorList == null || i >= atomTensorList.length ? null 2702 : atomTensorList[i]); 2703 } 2704 2705 // clean out deleted model atom tensors (ellipsoids) deleteAtomTensors(BS bsAtoms)2706 private void deleteAtomTensors(BS bsAtoms) { 2707 if (atomTensors == null) 2708 return; 2709 Lst<String> toDelete = new Lst<String>(); 2710 for (String key: atomTensors.keySet()) { 2711 Lst<Object> list = atomTensors.get(key); 2712 for (int i = list.size(); --i >= 0;) { 2713 Tensor t = (Tensor) list.get(i); 2714 if (bsAtoms.get(t.atomIndex1) || t.atomIndex2 >= 0 && bsAtoms.get(t.atomIndex2)) 2715 list.removeItemAt(i); 2716 } 2717 if (list.size() == 0) 2718 toDelete.addLast(key); 2719 } 2720 for (int i = toDelete.size(); --i >= 0;) 2721 atomTensors.remove(toDelete.get(i)); 2722 } 2723 2724 2725 private int atomCapacity; setCapacity(int nAtoms)2726 void setCapacity(int nAtoms) { 2727 atomCapacity += nAtoms; 2728 } 2729 setAtomTensors(int atomIndex, Lst<Object> list)2730 public void setAtomTensors(int atomIndex, Lst<Object> list) { 2731 if (list == null || list.size() == 0) 2732 return; 2733 if (atomTensors == null) 2734 atomTensors = new Hashtable<String, Lst<Object>>(); 2735 if (atomTensorList == null) 2736 atomTensorList = new Object[at.length][]; 2737 atomTensorList = (Object[][]) AU.ensureLength(atomTensorList, at.length); 2738 atomTensorList[atomIndex] = getTensorList(list); 2739 for (int i = list.size(); --i >= 0;) { 2740 Tensor t = (Tensor) list.get(i); 2741 t.atomIndex1 = atomIndex; 2742 t.atomIndex2 = -1; 2743 t.modelIndex = at[atomIndex].mi; 2744 addTensor(t, t.type); 2745 if (t.altType != null) 2746 addTensor(t, t.altType); 2747 } 2748 } 2749 addTensor(Tensor t, String type)2750 public void addTensor(Tensor t, String type) { 2751 type = type.toLowerCase(); 2752 Lst<Object> tensors = atomTensors.get(type); 2753 if (tensors == null) { 2754 atomTensors.put(type, tensors = new Lst<Object>()); 2755 tensors.ensureCapacity(atomCapacity); 2756 } 2757 tensors.addLast(t); 2758 } 2759 getTensorList(Lst<Object> list)2760 private static Object[] getTensorList(Lst<Object> list) { 2761 int pt = -1; 2762 boolean haveTLS = false; 2763 int n = list.size(); 2764 for (int i = n; --i >= 0;) { 2765 Tensor t = (Tensor) list.get(i); 2766 if (t.forThermalEllipsoid) 2767 pt = i; 2768 else if (t.iType == Tensor.TYPE_TLS_U) 2769 haveTLS = true; 2770 } 2771 Object[] a = new Object[(pt >= 0 || !haveTLS ? 0 : 1) + n]; 2772 if (pt >= 0) { 2773 a[0] = list.get(pt); 2774 if (list.size() == 1) 2775 return a; 2776 } 2777 // back-fills list for TLS: 2778 // 0 = temp, 1 = TLS-R, 2 = TLS-U 2779 if (haveTLS) { 2780 pt = 0; 2781 for (int i = n; --i >= 0;) { 2782 Tensor t = (Tensor) list.get(i); 2783 if (t.forThermalEllipsoid) 2784 continue; 2785 a[++pt] = t; 2786 } 2787 } else { 2788 for (int i = 0; i < n; i++) 2789 a[i] = list.get(i); 2790 } 2791 return a; 2792 } 2793 getAtomTensor(int i, String type)2794 public Tensor getAtomTensor(int i, String type) { 2795 Object[] tensors = getAtomTensorList(i); 2796 if (tensors != null && type != null) { 2797 type = type.toLowerCase(); 2798 for (int j = 0; j < tensors.length; j++) { 2799 Tensor t = (Tensor) tensors[j]; 2800 if (t != null && (type.equals(t.type) || type.equals(t.altType))) 2801 return t; 2802 } 2803 } 2804 return null; 2805 } 2806 getAllAtomTensors(String type)2807 public Lst<Object> getAllAtomTensors(String type) { 2808 if (atomTensors == null) 2809 return null; 2810 if (type != null) 2811 return atomTensors.get(type.toLowerCase()); 2812 Lst<Object> list = new Lst<Object>(); 2813 for (Entry<String, Lst<Object>> e : atomTensors.entrySet()) 2814 list.addAll(e.getValue()); 2815 return list; 2816 } 2817 2818 /** 2819 * Scales vibrations and associated vectors such that the 2820 * maximum length is the given value 2821 * 2822 * @param max 2823 */ scaleVectorsToMax(float max)2824 public void scaleVectorsToMax(float max) { 2825 if (vibrations == null) 2826 return; 2827 float m = 0; 2828 BS bsVib = BS.newN(ac); 2829 for (int i = vibrations.length; --i >= 0;) { 2830 Vibration v = getVibration(i, false); 2831 if (v != null && (v.modDim == Vibration.TYPE_VIBRATION 2832 || v.modDim == Vibration.TYPE_SPIN)) { 2833 m = Math.max(m, v.length()); 2834 bsVib.set(i); 2835 } 2836 } 2837 if (m == max || m == 0) 2838 return; 2839 m = max / m; 2840 boolean ok = false; 2841 for (int i = bsVib.nextSetBit(0); i >= 0; i = bsVib.nextSetBit(i + 1)) { 2842 Vibration v = getVibration(i, false); 2843 JmolModulationSet mod = getModulation(i); 2844 if (mod == null) { 2845 if (m == 0) 2846 return; // get out of here! 2847 v.scale(m); 2848 } else { 2849 mod.scaleVibration(m); 2850 } 2851 if (!ok) { 2852 taintAtom(i, TAINT_VIBRATION); 2853 ok = true; 2854 } 2855 } 2856 tainted[TAINT_VIBRATION].or(bsVib); 2857 //{*}.vxyz = {*}.vxyz.all.mul(3.0/{*}.vxyz.all.max) 2858 } 2859 getAtomsFromAtomNumberInFrame(int atomNumber)2860 public BS getAtomsFromAtomNumberInFrame(int atomNumber) { 2861 BS bs = vwr.getFrameAtoms(); 2862 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) 2863 if (at[i].getAtomNumber() != atomNumber) 2864 bs.clear(i); 2865 return bs; 2866 } 2867 2868 generateCrystalClass(int atomIndex, P3 pt)2869 public Lst<P3> generateCrystalClass(int atomIndex, P3 pt) { 2870 SymmetryInterface sym = (atomIndex < 0 || atomIndex >= ac ? null 2871 : at[atomIndex].getUnitCell()); 2872 boolean isRandom = (pt != null && Float.isNaN(pt.x)); 2873 return (sym == null ? new Lst<P3>() : sym 2874 .generateCrystalClass(isRandom ? null : pt != null ? pt : at[atomIndex])); 2875 2876 } 2877 2878 2879 } 2880 2881