1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2006-03-05 12:22:08 -0600 (Sun, 05 Mar 2006) $ 4 * $Revision: 4545 $ 5 * 6 * Copyright (C) 2002-2005 The Jmol Development Team 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.scriptext; 26 27 import java.io.BufferedInputStream; 28 import java.util.Date; 29 import java.util.HashMap; 30 import java.util.Hashtable; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Random; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 37 import org.jmol.api.Interface; 38 import org.jmol.api.JmolDataManager; 39 import org.jmol.api.JmolNMRInterface; 40 import org.jmol.api.JmolPatternMatcher; 41 import org.jmol.api.SymmetryInterface; 42 import org.jmol.atomdata.RadiusData; 43 import org.jmol.atomdata.RadiusData.EnumType; 44 import org.jmol.bspt.PointIterator; 45 import org.jmol.c.VDW; 46 import org.jmol.i18n.GT; 47 import org.jmol.modelset.Atom; 48 import org.jmol.modelset.Bond; 49 import org.jmol.modelset.BondSet; 50 import org.jmol.modelset.Measurement; 51 import org.jmol.modelset.ModelSet; 52 import org.jmol.script.SV; 53 import org.jmol.script.ScriptEval; 54 import org.jmol.script.ScriptException; 55 import org.jmol.script.ScriptMathProcessor; 56 import org.jmol.script.ScriptParam; 57 import org.jmol.script.T; 58 import org.jmol.util.BSUtil; 59 import org.jmol.util.ColorEncoder; 60 import org.jmol.util.Edge; 61 import org.jmol.util.Escape; 62 import org.jmol.util.JmolMolecule; 63 import org.jmol.util.Logger; 64 import org.jmol.util.Parser; 65 import org.jmol.util.Point3fi; 66 import org.jmol.util.SimpleUnitCell; 67 import org.jmol.util.Tensor; 68 import org.jmol.viewer.FileManager; 69 import org.jmol.viewer.JC; 70 import org.jmol.viewer.Viewer; 71 72 import javajs.util.AU; 73 import javajs.util.BS; 74 import javajs.util.CU; 75 import javajs.util.Lst; 76 import javajs.util.M3; 77 import javajs.util.M4; 78 import javajs.util.Measure; 79 import javajs.util.OC; 80 import javajs.util.P3; 81 import javajs.util.P4; 82 import javajs.util.PT; 83 import javajs.util.Quat; 84 import javajs.util.Rdr; 85 import javajs.util.SB; 86 import javajs.util.T3; 87 import javajs.util.V3; 88 89 public class MathExt { 90 91 private Viewer vwr; 92 private ScriptEval e; 93 MathExt()94 public MathExt() { 95 // used by Reflection 96 } 97 init(Object se)98 public MathExt init(Object se) { 99 e = (ScriptEval) se; 100 vwr = e.vwr; 101 return this; 102 } 103 104 ///////////// ScriptMathProcessor extensions /////////// 105 private static long t0 = System.currentTimeMillis(); 106 evaluate(ScriptMathProcessor mp, T op, SV[] args, int tok)107 public boolean evaluate(ScriptMathProcessor mp, T op, SV[] args, int tok) 108 throws ScriptException { 109 switch (tok) { 110 case T.now: 111 return (args.length >= 1 && args[0].tok == T.string 112 ? mp.addXStr((args.length == 1 ? new Date().toString() 113 : vwr.apiPlatform.getDateFormat(SV.sValue(args[1]))) + "\t" 114 + SV.sValue(args[0]).trim()) 115 : mp.addXInt(((int) (System.currentTimeMillis() - t0)) 116 - (args.length == 0 ? 0 : args[0].asInt()))); 117 case T.abs: 118 return (args.length == 1 && args[0].tok == T.integer 119 ? mp.addXInt(args[0].intValue) 120 : mp.addXFloat(Math.abs(args[0].asFloat()))); 121 case T.acos: 122 case T.cos: 123 case T.sin: 124 case T.sqrt: 125 return (args.length == 1 && evaluateMath(mp, args, tok)); 126 case T.add: 127 case T.div: 128 case T.mul: 129 case T.mul3: 130 case T.sub: 131 case T.push: 132 case T.pop: 133 return evaluateList(mp, op.intValue, args); 134 case T.leftsquare: 135 if (args.length == 0) 136 mp.wasX = false; 137 //$FALL-THROUGH$ 138 case T.array: 139 return evaluateArray(mp, args, 140 tok == T.array && op.tok == T.propselector); 141 case T.axisangle: 142 case T.quaternion: 143 return evaluateQuaternion(mp, args, tok); 144 case T.bin: 145 return evaluateBin(mp, args); 146 case T.cache: 147 return evaluateCache(mp, args); 148 case T.col: 149 case T.row: 150 return evaluateRowCol(mp, args, tok); 151 case T.color: 152 return evaluateColor(mp, args); 153 case T.compare: 154 return evaluateCompare(mp, args); 155 case T.bondcount: 156 case T.connected: 157 case T.polyhedra: 158 return evaluateConnected(mp, args, tok, op.intValue); 159 case T.unitcell: 160 return evaluateUnitCell(mp, args, op.tok == T.propselector); 161 case T.contact: 162 return evaluateContact(mp, args); 163 case T.data: 164 return evaluateData(mp, args); 165 case T.dot: 166 case T.cross: 167 return evaluateDotDist(mp, args, tok, op.intValue); 168 case T.distance: 169 if (op.tok == T.propselector) 170 return evaluateDotDist(mp, args, tok, op.intValue); 171 //$FALL-THROUGH$ 172 case T.angle: 173 case T.measure: 174 return evaluateMeasure(mp, args, op.tok); 175 case T.file: 176 case T.load: 177 return evaluateLoad(mp, args, tok == T.file); 178 case T.find: 179 return evaluateFind(mp, args); 180 case T.inchi: 181 return evaluateInChI(mp, args); 182 case T.format: 183 case T.label: 184 return evaluateFormat(mp, op.intValue, args, tok == T.label); 185 case T.function: 186 return evaluateUserFunction(mp, (String) op.value, args, op.intValue, 187 op.tok == T.propselector); 188 case T.__: 189 case T.pivot: 190 case T.select: 191 case T.getproperty: 192 return evaluateGetProperty(mp, args, tok, op.tok == T.propselector); 193 case T.helix: 194 return evaluateHelix(mp, args); 195 case T.hkl: 196 case T.plane: 197 case T.intersection: 198 return evaluatePlane(mp, args, tok); 199 case T.eval: 200 case T.javascript: 201 case T.script: 202 case T.show: 203 return evaluateScript(mp, args, tok); 204 case T.join: 205 case T.split: 206 case T.trim: 207 return evaluateString(mp, op.intValue, args); 208 case T.point: 209 return evaluatePoint(mp, args); 210 case T.pointgroup: 211 return evaluatePointGroup(mp, args); 212 case T.prompt: 213 return evaluatePrompt(mp, args); 214 case T.random: 215 return evaluateRandom(mp, args); 216 case T.in: 217 return evaluateIn(mp, args); 218 case T.modulation: 219 return evaluateModulation(mp, args); 220 case T.replace: 221 return evaluateReplace(mp, args); 222 case T.search: 223 case T.smiles: 224 case T.substructure: 225 return evaluateSubstructure(mp, args, tok, op.tok == T.propselector); 226 case T.sort: 227 case T.count: 228 return evaluateSort(mp, args, tok); 229 case T.spacegroup: 230 return evaluateSpacegroup(mp, args); 231 case T.symop: 232 return evaluateSymop(mp, args, op.tok == T.propselector); 233 // case Token.volume: 234 // return evaluateVolume(args); 235 case T.tensor: 236 return evaluateTensor(mp, args); 237 case T.within: 238 return evaluateWithin(mp, args); 239 case T.write: 240 return evaluateWrite(mp, args); 241 } 242 return false; 243 } 244 evaluateSpacegroup(ScriptMathProcessor mp, SV[] args)245 private boolean evaluateSpacegroup(ScriptMathProcessor mp, SV[] args) { 246 // spacegroup(); 247 // spacegroup(3); 248 // spacegroup("x,y,z,-x,-y,-z"); 249 // spacegroup("x,y,z,-x,-y,-z", [unitcellParams]); 250 251 float[] unitCell = null; 252 switch (args.length) { 253 case 0: 254 return mp.addXObj(vwr.getSymTemp().getSpaceGroupInfo(vwr.ms, null, 255 vwr.am.cmi, true, null)); 256 case 2: 257 unitCell = SV.flistValue(args[1], 0); 258 if (unitCell.length < 6) 259 unitCell = null; 260 //$FALL-THROUGH$ 261 case 1: 262 return mp.addXObj(vwr.getSymTemp().getSpaceGroupInfo(vwr.ms, 263 "" + args[0].asString(), Integer.MIN_VALUE, true, unitCell)); 264 default: 265 return false; 266 } 267 } 268 269 @SuppressWarnings("unchecked") evaluatePointGroup(ScriptMathProcessor mp, SV[] args)270 private boolean evaluatePointGroup(ScriptMathProcessor mp, SV[] args) { 271 // pointGroup(points) 272 // pointGroup(points, center) 273 // pointGroup(points, center, distanceTolerance (def. 0.2 A), linearTolerance (def. 8 deg.) 274 // center can be non-point to ignore, such as 0 or "" 275 T3[] pts = null; 276 P3 center = null; 277 float distanceTolerance = Float.NaN; 278 float linearTolerance = Float.NaN; 279 BS bsAtoms; 280 switch (args.length) { 281 case 4: 282 linearTolerance = args[3].asFloat(); 283 //$FALL-THROUGH$ 284 case 3: 285 distanceTolerance = args[2].asFloat(); 286 //$FALL-THROUGH$ 287 case 2: 288 switch (args[1].tok) { 289 case T.point3f: 290 center = SV.ptValue(args[1]); 291 break; 292 case T.bitset: 293 // pointgroup {vertices} {center} 294 bsAtoms = SV.getBitSet(args[1], false); 295 int iatom = bsAtoms.nextSetBit(0); 296 if (iatom < 0 || iatom >= vwr.ms.ac || bsAtoms.cardinality() != 1) 297 return false; 298 if (SV.sValue(args[0]).equalsIgnoreCase("spaceGroup")) { 299 // pointgroup("spaceGroup", @1) 300 Lst<P3> lst = vwr.ms.generateCrystalClass(iatom, 301 P3.new3(Float.NaN, Float.NaN, Float.NaN)); 302 pts = new T3[lst.size()]; 303 for (int i = pts.length; --i >= 0;) 304 pts[i] = lst.get(i); 305 center = new P3(); 306 if (args.length == 2) 307 distanceTolerance = 0; // will set tolerances especially tight 308 } else { 309 center = vwr.ms.at[iatom]; 310 } 311 } 312 if (pts != null) 313 break; 314 //$FALL-THROUGH$ 315 case 1: 316 switch (args[0].tok) { 317 case T.varray: 318 Lst<SV> points = args[0].getList(); 319 pts = new T3[points.size()]; 320 for (int i = pts.length; --i >= 0;) 321 pts[i] = SV.ptValue(points.get(i)); 322 break; 323 case T.bitset: 324 bsAtoms = SV.getBitSet(args[0], false); 325 Lst<P3> atoms = vwr.ms.getAtomPointVector(bsAtoms); 326 pts = new T3[atoms.size()]; 327 for (int i = pts.length; --i >= 0;) 328 pts[i] = atoms.get(i); 329 break; 330 default: 331 return false; 332 } 333 break; 334 case 0: 335 return mp.addXObj(vwr.ms.getPointGroupInfo(null)); 336 default: 337 return false; 338 } 339 SymmetryInterface pointGroup = vwr.getSymTemp().setPointGroup(null, center, 340 pts, null, false, 341 Float.isNaN(distanceTolerance) 342 ? vwr.getFloat(T.pointgroupdistancetolerance) 343 : distanceTolerance, 344 Float.isNaN(linearTolerance) ? vwr.getFloat(T.pointgrouplineartolerance) 345 : linearTolerance, 346 true); 347 return mp.addXMap((Map<String, ?>) pointGroup.getPointGroupInfo(-1, null, 348 true, null, 0, 1)); 349 } 350 evaluateUnitCell(ScriptMathProcessor mp, SV[] args, boolean isSelector)351 private boolean evaluateUnitCell(ScriptMathProcessor mp, SV[] args, 352 boolean isSelector) 353 throws ScriptException { 354 // optional last parameter: scale 355 // unitcell("-a,-b,c;0,0,0.50482") (polar groups can have irrational translations along z) 356 // unitcell(uc) 357 // unitcell(uc, "reciprocal") 358 // unitcell(origin, [va, vb, vc]) 359 // unitcell(origin, pta, ptb, ptc) 360 361 // next can be without {1.1}, but then assume "all atoms" 362 // {1.1}.unitcell() 363 // {1.1}.unitcell(ucconv, "primitive","BCC"|"FCC") 364 // {1.1}.unitcell(ucprim, "conventional","BCC"|"FCC") 365 366 BS x1 = (isSelector ? SV.getBitSet(mp.getX(), true) : null); 367 int iatom = ((x1 == null ? vwr.getAllAtoms() : x1).nextSetBit(0)); 368 int lastParam = args.length - 1; 369 float scale = 1; 370 switch (lastParam < 0 ? T.nada : args[lastParam].tok) { 371 case T.integer: 372 case T.decimal: 373 scale = args[lastParam].asFloat(); 374 lastParam--; 375 break; 376 } 377 int tok0 = (lastParam < 0 ? T.nada : args[0].tok); 378 T3[] ucnew = null; 379 Lst<SV> uc = null; 380 switch (tok0) { 381 case T.varray: 382 uc = args[0].getList(); 383 break; 384 case T.string: 385 String s = args[0].asString(); 386 if (s.indexOf("a=") == 0) { 387 ucnew = new P3[4]; 388 for (int i = 0; i < 4; i++) 389 ucnew[i] = new P3(); 390 SimpleUnitCell.setOabc(s, null, ucnew); 391 } else if (s.indexOf(",") >= 0) { 392 return mp.addXObj(vwr.getV0abc(-1, s)); 393 } 394 break; 395 } 396 SymmetryInterface u = null; 397 boolean haveUC = (uc != null); 398 if (ucnew == null && haveUC && uc.size() < 4) 399 return false; 400 int ptParam = (haveUC ? 1 : 0); 401 if (ucnew == null && !haveUC && tok0 != T.point3f) { 402 // unitcell() or {1.1}.unitcell 403 u = (iatom < 0 ? null : vwr.ms.getUnitCell(vwr.ms.at[iatom].mi)); 404 ucnew = (u == null 405 ? new P3[] { P3.new3(0, 0, 0), P3.new3(1, 0, 0), P3.new3(0, 1, 0), 406 P3.new3(0, 0, 1) } 407 : u.getUnitCellVectors()); 408 } 409 if (ucnew == null) { 410 ucnew = new P3[4]; 411 if (haveUC) { 412 switch (uc.size()) { 413 case 3: 414 // [va. vb. vc] 415 ucnew[0] = new P3(); 416 for (int i = 0; i < 3; i++) 417 ucnew[i + 1] = P3.newP(SV.ptValue(uc.get(i))); 418 break; 419 case 4: 420 for (int i = 0; i < 4; i++) 421 ucnew[i] = P3.newP(SV.ptValue(uc.get(i))); 422 break; 423 case 6: 424 // unitcell([a b c alpha beta gamma]) 425 float[] params = new float[6]; 426 for (int i = 0; i < 6; i++) 427 params[i] = uc.get(i).asFloat(); 428 SimpleUnitCell.setOabc(null, params, ucnew); 429 break; 430 default: 431 return false; 432 } 433 } else { 434 ucnew[0] = SV.ptValue(args[0]); 435 switch (lastParam) { 436 case 3: 437 // unitcell(origin, pa, pb, pc) 438 for (int i = 1; i < 4; i++) 439 (ucnew[i] = P3.newP(SV.ptValue(args[i]))).sub(ucnew[0]); 440 break; 441 case 1: 442 // unitcell(origin, [va, vb, vc]) 443 Lst<SV> l = args[1].getList(); 444 if (l != null && l.size() == 3) { 445 for (int i = 0; i < 3; i++) 446 ucnew[i + 1] = SV.ptValue(l.get(i)); 447 break; 448 } 449 //$FALL-THROUGH$ 450 default: 451 return false; 452 } 453 } 454 } 455 456 String op = (ptParam <= lastParam ? args[ptParam].asString() : null); 457 boolean toPrimitive = "primitive".equalsIgnoreCase(op); 458 if (toPrimitive || "conventional".equalsIgnoreCase(op)) { 459 String stype = (++ptParam > lastParam ? "" 460 : args[ptParam].asString().toUpperCase()); 461 if (stype.equals("BCC")) 462 stype = "I"; 463 else if (stype.length() == 0) 464 stype = (String) vwr.getSymmetryInfo(iatom, null, 0, null, null, 465 null, T.lattice, null, 0, -1, 0); 466 if (stype == null || stype.length() == 0) 467 return false; 468 if (u == null) 469 u = vwr.getSymTemp(); 470 M3 m3 = (M3) vwr.getModelForAtomIndex(iatom).auxiliaryInfo 471 .get("primitiveToCrystal"); 472 if (!u.toFromPrimitive(toPrimitive, stype.charAt(0), ucnew, m3)) 473 return false; 474 } else if ("reciprocal".equalsIgnoreCase(op)) { 475 ucnew = SimpleUnitCell.getReciprocal(ucnew, null, scale); 476 scale = 1; 477 } 478 if (scale != 1) 479 for (int i = 1; i < 4; i++) 480 ucnew[i].scale(scale); 481 return mp.addXObj(ucnew); 482 } 483 484 @SuppressWarnings("unchecked") evaluateArray(ScriptMathProcessor mp, SV[] args, boolean isSelector)485 private boolean evaluateArray(ScriptMathProcessor mp, SV[] args, 486 boolean isSelector) 487 throws ScriptException { 488 if (isSelector) { 489 SV x1 = mp.getX(); 490 switch (args.length == 1 ? x1.tok : T.nada) { 491 case T.hash: 492 // map of maps to lst of maps 493 Lst<SV> lst = new Lst<SV>(); 494 String id = args[0].asString(); 495 Map<String, SV> map = x1.getMap(); 496 String[] keys = x1.getKeys(false); 497 // values must all be maps 498 for (int i = 0, n = keys.length; i < n; i++) 499 if (map.get(keys[i]).getMap() == null) 500 return false; 501 for (int i = 0, n = keys.length; i < n; i++) { 502 SV m = map.get(keys[i]); 503 Map<String, SV> m1 = m.getMap(); 504 Map<String, SV> m2 = (Map<String, SV>) SV.deepCopy(m1, true, false); 505 m2.put(id, SV.newS(keys[i])); 506 lst.addLast(SV.newV(T.hash, m2)); 507 } 508 return mp.addXList(lst); 509 case T.varray: 510 Map<String, SV> map1 = new Hashtable<String, SV>(); 511 Lst<SV> lst1 = x1.getList(); 512 String id1 = args[0].asString(); 513 // values must all be maps 514 for (int i = 0, n = lst1.size(); i < n; i++) { 515 Map<String, SV> m0 = lst1.get(i).getMap(); 516 if (m0 == null || m0.get(id1) == null) 517 return false; 518 } 519 for (int i = 0, n = lst1.size(); i < n; i++) { 520 SV m = lst1.get(i); 521 Map<String, SV> m1 = (Map<String, SV>) SV.deepCopy(m.getMap(), true, 522 false); 523 SV mid = m1.remove(id1); 524 map1.put(mid.asString(), SV.newV(T.hash, m1)); 525 } 526 return mp.addXObj(map1); 527 } 528 return false; 529 } 530 SV[] a = new SV[args.length]; 531 for (int i = a.length; --i >= 0;) 532 a[i] = SV.newT(args[i]); 533 return mp.addXAV(a); 534 } 535 evaluateBin(ScriptMathProcessor mp, SV[] args)536 private boolean evaluateBin(ScriptMathProcessor mp, SV[] args) 537 throws ScriptException { 538 int n = args.length; 539 if (n < 3 || n > 5) 540 return false; 541 SV x1 = mp.getX(); 542 boolean isListf = (x1.tok == T.listf); 543 if (!isListf && x1.tok != T.varray) 544 return mp.addX(x1); 545 float f0 = SV.fValue(args[0]); 546 float f1 = SV.fValue(args[1]); 547 float df = SV.fValue(args[2]); 548 boolean addBins = (n >= 4 && args[n - 1].tok == T.on); 549 String key = ((n == 5 || n == 4 && !addBins) && args[3].tok != T.off 550 ? SV.sValue(args[3]) 551 : null); 552 float[] data; 553 Map<String, SV>[] maps = null; 554 if (isListf) { 555 data = (float[]) x1.value; 556 } else { 557 Lst<SV> list = x1.getList(); 558 data = new float[list.size()]; 559 if (key != null) 560 maps = AU.createArrayOfHashtable(list.size()); 561 try { 562 for (int i = list.size(); --i >= 0;) 563 data[i] = SV.fValue(key == null ? list.get(i) 564 : (maps[i] = list.get(i).getMap()).get(key)); 565 } catch (Exception e) { 566 return false; 567 } 568 } 569 int nbins = Math.max((int) Math.floor((f1 - f0) / df + 0.01f), 1); 570 int[] array = new int[nbins]; 571 int nPoints = data.length; 572 for (int i = 0; i < nPoints; i++) { 573 float v = data[i]; 574 int bin = (int) Math.floor((v - f0) / df); 575 if (bin < 0 || bin >= nbins) 576 continue; 577 array[bin]++; 578 if (key != null) { 579 Map<String, SV> map = maps[i]; 580 if (map == null) 581 continue; 582 map.put("_bin", SV.newI(bin)); 583 float v1 = f0 + df * bin; 584 float v2 = v1 + df; 585 map.put("_binMin", SV.newF(bin == 0 ? -Float.MAX_VALUE : v1)); 586 map.put("_binMax", SV.newF(bin == nbins - 1 ? Float.MAX_VALUE : v2)); 587 } 588 } 589 if (addBins) { 590 Lst<float[]> lst = new Lst<float[]>(); 591 for (int i = 0; i < nbins; i++) 592 lst.addLast(new float[] { f0 + df * i, array[i] }); 593 return mp.addXList(lst); 594 } 595 return mp.addXAI(array); 596 } 597 evaluateCache(ScriptMathProcessor mp, SV[] args)598 private boolean evaluateCache(ScriptMathProcessor mp, SV[] args) { 599 if (args.length > 0) 600 return false; 601 return mp.addXMap(vwr.fm.cacheList()); 602 } 603 evaluateColor(ScriptMathProcessor mp, SV[] args)604 private boolean evaluateColor(ScriptMathProcessor mp, SV[] args) { 605 // color("toHSL", {r g b}) # r g b in 0 to 255 scale 606 // color("toRGB", "colorName or hex code") # r g b in 0 to 255 scale 607 // color("toRGB", {h s l}) # h s l in 360, 100, 100 scale 608 // color("rwb") # "" for most recently used scheme for coloring by property 609 // color("rwb", min, max) # min/max default to most recent property mapping 610 // color("rwb", min, max, value) # returns color 611 // color("$isosurfaceId") # info for a given isosurface 612 // color("$isosurfaceId", value) # color for a given mapped isosurface value 613 // color(ptColor1, ptColor2, n, asHSL) 614 615 String colorScheme = (args.length > 0 ? SV.sValue(args[0]) : ""); 616 boolean isIsosurface = colorScheme.startsWith("$"); 617 if (args.length == 2 && colorScheme.equalsIgnoreCase("TOHSL")) 618 return mp.addXPt( 619 CU.rgbToHSL(P3.newP(args[1].tok == T.point3f ? SV.ptValue(args[1]) 620 : CU.colorPtFromString(args[1].asString())), true)); 621 if (args.length == 2 && colorScheme.equalsIgnoreCase("TORGB")) { 622 P3 pt = P3.newP(args[1].tok == T.point3f ? SV.ptValue(args[1]) 623 : CU.colorPtFromString(args[1].asString())); 624 return mp.addXPt(args[1].tok == T.point3f ? CU.hslToRGB(pt) : pt); 625 } 626 if (args.length == 4 && (args[3].tok == T.on || args[3].tok == T.off)) { 627 P3 pt1 = P3.newP(args[0].tok == T.point3f ? SV.ptValue(args[0]) 628 : CU.colorPtFromString(args[0].asString())); 629 P3 pt2 = P3.newP(args[1].tok == T.point3f ? SV.ptValue(args[1]) 630 : CU.colorPtFromString(args[1].asString())); 631 boolean usingHSL = (args[3].tok == T.on); 632 if (usingHSL) { 633 pt1 = CU.rgbToHSL(pt1, false); 634 pt2 = CU.rgbToHSL(pt2, false); 635 } 636 637 SB sb = new SB(); 638 V3 vd = V3.newVsub(pt2, pt1); 639 int n = args[2].asInt(); 640 if (n < 2) 641 n = 20; 642 vd.scale(1f / (n - 1)); 643 for (int i = 0; i < n; i++) { 644 sb.append(Escape 645 .escapeColor(CU.colorPtToFFRGB(usingHSL ? CU.hslToRGB(pt1) : pt1))); 646 pt1.add(vd); 647 } 648 return mp.addXStr(sb.toString()); 649 } 650 651 ColorEncoder ce = (isIsosurface ? null 652 : vwr.cm.getColorEncoder(colorScheme)); 653 if (!isIsosurface && ce == null) 654 return mp.addXStr(""); 655 float lo = (args.length > 1 ? SV.fValue(args[1]) : Float.MAX_VALUE); 656 float hi = (args.length > 2 ? SV.fValue(args[2]) : Float.MAX_VALUE); 657 float value = (args.length > 3 ? SV.fValue(args[3]) : Float.MAX_VALUE); 658 boolean getValue = (value != Float.MAX_VALUE 659 || lo != Float.MAX_VALUE && hi == Float.MAX_VALUE); 660 boolean haveRange = (hi != Float.MAX_VALUE); 661 if (!haveRange && colorScheme.length() == 0) { 662 value = lo; 663 float[] range = vwr.getCurrentColorRange(); 664 lo = range[0]; 665 hi = range[1]; 666 } 667 if (isIsosurface) { 668 // isosurface color scheme 669 String id = colorScheme.substring(1); 670 Object[] data = new Object[] { id, null }; 671 if (!vwr.shm.getShapePropertyData(JC.SHAPE_ISOSURFACE, "colorEncoder", 672 data)) 673 return mp.addXStr(""); 674 ce = (ColorEncoder) data[1]; 675 } else { 676 ce.setRange(lo, hi, lo > hi); 677 } 678 Map<String, Object> key = ce.getColorKey(); 679 if (getValue) 680 return mp.addXPt(CU.colorPtFromInt( 681 ce.getArgb(hi == Float.MAX_VALUE ? lo : value), null)); 682 return mp.addX(SV.getVariableMap(key)); 683 } 684 evaluateCompare(ScriptMathProcessor mp, SV[] args)685 private boolean evaluateCompare(ScriptMathProcessor mp, SV[] args) 686 throws ScriptException { 687 // compare([{bitset} or {positions}],[{bitset} or {positions}] [,"stddev"]) 688 // compare({bitset},{bitset}[,"SMARTS"|"SMILES"],smilesString [,"stddev"]) 689 // returns matrix4f for rotation/translation or stddev 690 // compare({bitset},{bitset},"ISOMER") 12.1.5 691 // compare({bitset},{bitset},smartsString, "BONDS") 13.1.17 692 // compare({bitset},{bitset},"SMILES", "BONDS") 13.3.9 693 // compare({bitest},{bitset},"MAP","smilesString") 694 // compare({bitset},{bitset},"MAP") 695 // compare(@1,@2,"SMILES", "polyhedra"[,"stddev"]) 696 // compare(@1,@2,"SMARTS", "polyhedra"[,"stddev"]) 697 698 if (args.length < 2 || args.length > 5) 699 return false; 700 float stddev; 701 String sOpt = SV.sValue(args[args.length - 1]); 702 boolean isStdDev = sOpt.equalsIgnoreCase("stddev"); 703 boolean isIsomer = sOpt.equalsIgnoreCase("ISOMER"); 704 boolean isBonds = sOpt.equalsIgnoreCase("BONDS"); 705 boolean isPoints = (args[0].tok == T.varray && args[1].tok == T.varray); 706 boolean isSmiles = (!isPoints && !isIsomer 707 && args.length > (isStdDev ? 3 : 2)); 708 BS bs1 = (args[0].tok == T.bitset ? (BS) args[0].value : null); 709 BS bs2 = (args[1].tok == T.bitset ? (BS) args[1].value : null); 710 String smiles1 = (bs1 == null ? SV.sValue(args[0]) : ""); 711 String smiles2 = (bs2 == null ? SV.sValue(args[1]) : ""); 712 stddev = Float.NaN; 713 try { 714 if (isBonds) { 715 if (args.length != 4) 716 return false; 717 // A, B, ........................BONDS 718 // A, B, SMILES,...................BONDS 719 smiles1 = SV.sValue(args[2]); 720 isSmiles = smiles1.equalsIgnoreCase("SMILES"); 721 try { 722 if (isSmiles) 723 smiles1 = vwr.getSmiles(bs1); 724 } catch (Exception ex) { 725 e.evalError(ex.getMessage(), null); 726 } 727 float[] data = e.getSmilesExt().getFlexFitList(bs1, bs2, smiles1, 728 !isSmiles); 729 return (data == null ? mp.addXStr("") : mp.addXAF(data)); 730 } 731 if (isIsomer) { 732 if (args.length != 3) 733 return false; 734 735 // A, B, ISOMER 736 737 if (bs1 == null && bs2 == null) 738 return mp.addXStr(vwr.getSmilesMatcher() 739 .getRelationship(smiles1, smiles2).toUpperCase()); 740 String mf1 = (bs1 == null 741 ? vwr.getSmilesMatcher().getMolecularFormula(smiles1, false) 742 : JmolMolecule.getMolecularFormulaAtoms(vwr.ms.at, bs1, null, 743 false)); 744 String mf2 = (bs2 == null 745 ? vwr.getSmilesMatcher().getMolecularFormula(smiles2, false) 746 : JmolMolecule.getMolecularFormulaAtoms(vwr.ms.at, bs2, null, 747 false)); 748 if (!mf1.equals(mf2)) 749 return mp.addXStr("NONE"); 750 if (bs1 != null) 751 smiles1 = (String) e.getSmilesExt().getSmilesMatches("/strict///", 752 null, bs1, null, JC.SMILES_TYPE_SMILES, true, false); 753 boolean check; 754 if (bs2 == null) { 755 // note: find smiles1 IN smiles2 here 756 check = (vwr.getSmilesMatcher().areEqual(smiles2, smiles1) > 0); 757 } else { 758 smiles2 = (String) e.getSmilesExt().getSmilesMatches("/strict///", 759 null, bs2, null, JC.SMILES_TYPE_SMILES, true, false); 760 check = (((BS) e.getSmilesExt().getSmilesMatches( 761 "/strict///" + smiles1, null, bs2, null, JC.SMILES_TYPE_SMILES, 762 true, false)).nextSetBit(0) >= 0); 763 } 764 if (!check) { 765 // MF matched, but didn't match SMILES 766 String s = smiles1 + smiles2; 767 if (s.indexOf("/") >= 0 || s.indexOf("\\") >= 0 768 || s.indexOf("@") >= 0) { 769 if (smiles1.indexOf("@") >= 0 770 && (bs2 != null || smiles2.indexOf("@") >= 0) 771 && smiles1.indexOf("@SP") < 0) { 772 // reverse chirality centers to see if we have an enantiomer 773 // but only if not square planar 774 int pt = smiles1.toLowerCase().indexOf("invertstereo"); 775 smiles1 = (pt >= 0 776 ? "/strict/" + smiles1.substring(0, pt) 777 + smiles1.substring(pt + 12) 778 : "/invertstereo strict/" + smiles1);//vwr.getSmilesMatcher().reverseChirality(smiles1); 779 780 if (bs2 == null) { 781 check = (vwr.getSmilesMatcher().areEqual(smiles1, smiles2) > 0); 782 } else { 783 check = (((BS) e.getSmilesExt().getSmilesMatches(smiles1, null, 784 bs2, null, JC.SMILES_TYPE_SMILES, true, false)) 785 .nextSetBit(0) >= 0); 786 } 787 if (check) 788 return mp.addXStr("ENANTIOMERS"); 789 } 790 // remove all stereochemistry from SMILES string 791 if (bs2 == null) { 792 check = (vwr.getSmilesMatcher().areEqual("/nostereo/" + smiles2, 793 smiles1) > 0); 794 } else { 795 Object ret = e.getSmilesExt().getSmilesMatches( 796 "/nostereo/" + smiles1, null, bs2, null, 797 JC.SMILES_TYPE_SMILES, true, false); 798 check = (((BS) ret).nextSetBit(0) >= 0); 799 } 800 if (check) 801 return mp.addXStr("DIASTEREOMERS"); 802 } 803 // MF matches, but not enantiomers or diastereomers 804 return mp.addXStr("CONSTITUTIONAL ISOMERS"); 805 } 806 //identical or conformational 807 if (bs1 == null || bs2 == null) 808 return mp.addXStr("IDENTICAL"); 809 stddev = e.getSmilesExt().getSmilesCorrelation(bs1, bs2, smiles1, null, 810 null, null, null, false, null, null, false, JC.SMILES_TYPE_SMILES); 811 return mp.addXStr(stddev < 0.2f ? "IDENTICAL" 812 : "IDENTICAL or CONFORMATIONAL ISOMERS (RMSD=" + stddev + ")"); 813 } 814 M4 m = new M4(); 815 Lst<P3> ptsA = null, ptsB = null; 816 if (isSmiles) { 817 // A, B, MAP 818 // A, B, MAP, [pattern "H" "allH" "bestH"], ["stddev"] 819 // A, B, SMILES, [pattern "H" "allH" "bestH"], ["stddev"] 820 // A, B, SMARTS, pattern, ["stddev"] 821 // A, B, SMILES, "polyhedron", [pattern "all" "best"], ["stddev"] 822 // A, B, SMARTS, "polyhedron", pattern, ["all" "best"], ["stddev"] 823 if (bs1 == null || bs2 == null) 824 return false; 825 sOpt = SV.sValue(args[2]); 826 boolean isMap = sOpt.equalsIgnoreCase("MAP"); 827 isSmiles = sOpt.equalsIgnoreCase("SMILES"); 828 boolean isSearch = (isMap || sOpt.equalsIgnoreCase("SMARTS")); 829 if (isSmiles || isSearch) 830 sOpt = (args.length > (isStdDev ? 4 : 3) ? SV.sValue(args[3]) : null); 831 832 // sOpts = "H", "allH", "bestH", "polyhedra", pattern, or null 833 boolean hMaps = (("H".equalsIgnoreCase(sOpt) 834 || "allH".equalsIgnoreCase(sOpt) 835 || "bestH".equalsIgnoreCase(sOpt))); 836 837 boolean isPolyhedron = !hMaps && ("polyhedra".equalsIgnoreCase(sOpt) 838 || "polyhedron".equalsIgnoreCase(sOpt)); 839 if (isPolyhedron) { 840 stddev = e.getSmilesExt().mapPolyhedra(bs1.nextSetBit(0), 841 bs2.nextSetBit(0), isSmiles, m); 842 } else { 843 ptsA = new Lst<P3>(); 844 ptsB = new Lst<P3>(); 845 boolean allMaps = (("all".equalsIgnoreCase(sOpt) 846 || "allH".equalsIgnoreCase(sOpt))); 847 boolean bestMap = (("best".equalsIgnoreCase(sOpt) 848 || "bestH".equalsIgnoreCase(sOpt))); 849 if ("stddev".equals(sOpt)) 850 sOpt = null; 851 String pattern = sOpt; 852 if (sOpt == null || hMaps || allMaps || bestMap) { 853 // with explicitH we set to find only the first match. 854 if (!isMap && !isSmiles || hMaps && isPolyhedron) 855 return false; 856 pattern = "/noaromatic" + (allMaps || bestMap ? "/" : " nostereo/") 857 + e.getSmilesExt().getSmilesMatches((hMaps ? "H" : ""), null, 858 bs1, null, JC.SMILES_TYPE_SMILES, true, false); 859 } else { 860 allMaps = true; 861 } 862 stddev = e.getSmilesExt().getSmilesCorrelation(bs1, bs2, pattern, 863 ptsA, ptsB, m, null, isMap, null, null, bestMap, 864 (isSmiles ? JC.SMILES_TYPE_SMILES : JC.SMILES_TYPE_SMARTS) 865 | (!allMaps && !bestMap ? JC.SMILES_FIRST_MATCH_ONLY : 0)); 866 if (isMap) { 867 int nAtoms = ptsA.size(); 868 if (nAtoms == 0) 869 return mp.addXStr(""); 870 int nMatch = ptsB.size() / nAtoms; 871 Lst<int[][]> ret = new Lst<int[][]>(); 872 for (int i = 0, pt = 0; i < nMatch; i++) { 873 int[][] a = AU.newInt2(nAtoms); 874 ret.addLast(a); 875 for (int j = 0; j < nAtoms; j++, pt++) 876 a[j] = new int[] { ((Atom) ptsA.get(j)).i, 877 ((Atom) ptsB.get(pt)).i }; 878 } 879 return (allMaps ? mp.addXList(ret) 880 : ret.size() > 0 ? mp.addXAII(ret.get(0)) : mp.addXStr("")); 881 } 882 } 883 } 884 if (isPoints) { 885 // A, B 886 // A, B, stddev 887 // A, B, int[] map 888 // A, B, int[] map, stddev 889 ptsA = e.getPointVector(args[0], 3); 890 ptsB = e.getPointVector(args[1], 3); 891 Lst<SV> a = (args.length >= 3 ? args[2].getList() : null); 892 int na = args.length; 893 if (a != null) { 894 na--; 895 int n = a.size(); 896 if (n != ptsA.size() || n != ptsB.size()) 897 return false; 898 Lst<P3> list = new Lst<P3>(); 899 for (int i = 0; i < n; i++) 900 list.addLast(ptsA.get(a.get(i).intValue)); 901 ptsA = list; 902 } 903 switch (na) { 904 case 2: 905 break; 906 case 3: 907 if (isStdDev) 908 break; 909 //$FALL-THROUGH$ 910 default: 911 return false; 912 } 913 if (ptsA != null && ptsB != null && ptsA.size() == ptsB.size()) { 914 Interface.getInterface("javajs.util.Eigen", vwr, "script"); 915 stddev = Measure.getTransformMatrix4(ptsA, ptsB, m, null); 916 } 917 } 918 // now have m and stddev 919 return (isStdDev || Float.isNaN(stddev) ? mp.addXFloat(stddev) 920 : mp.addXM4(m.round(1e-7f))); 921 } catch (Exception ex) { 922 e.evalError(ex.getMessage() == null ? ex.toString() : ex.getMessage(), 923 null); 924 return false; 925 } 926 } 927 evaluateConnected(ScriptMathProcessor mp, SV[] args, int tok, int intValue)928 private boolean evaluateConnected(ScriptMathProcessor mp, SV[] args, int tok, 929 int intValue) 930 throws ScriptException { 931 /* 932 * Several options here: 933 * 934 * .bondCount({_Au}) 935 * 936 * connected(1, 3, "single", {carbon}) 937 * 938 * means "atoms connected to carbon by from 1 to 3 single bonds" 939 * and returns an atom set. 940 * 941 * connected(1.0, 1.5, "single", {carbon}, {oxygen}) 942 * 943 * means "single bonds from 1.0 to 1.5 Angstroms between carbon and oxygen" 944 * and returns a bond bitset. 945 * 946 * connected({*}.bonds, "DOUBLE") 947 * 948 * means just that and returns a bond set 949 * 950 * 951 */ 952 953 if (args.length > 5) 954 return false; 955 float min = Integer.MIN_VALUE, max = Integer.MAX_VALUE; 956 float fmin = 0, fmax = Float.MAX_VALUE; 957 958 int order = Edge.BOND_ORDER_ANY; 959 BS atoms1 = null; 960 BS atoms2 = null; 961 boolean haveDecimal = false; 962 boolean isBonds = false; 963 switch (tok) { 964 case T.polyhedra: 965 // polyhedra() 966 // polyhedra(n) 967 // polyhedra(smilesString) 968 // {xx}.polyhedra() 969 // {xx}.polyhedra(n) 970 // {xx}.polyhedra(smilesString) 971 int nv = Integer.MIN_VALUE; 972 String smiles = null; 973 if (args.length > 0) { 974 switch (args[0].tok) { 975 case T.integer: 976 nv = args[0].intValue; 977 break; 978 case T.string: 979 smiles = SV.sValue(args[0]); 980 break; 981 } 982 } 983 if (intValue == T.polyhedra) 984 atoms1 = SV.getBitSet(mp.getX(), true); 985 Object[] data = new Object[] { Integer.valueOf(nv), smiles, atoms1 }; 986 if (!vwr.shm.getShapePropertyData(JC.SHAPE_POLYHEDRA, "getCenters", data)) 987 data[1] = null; 988 return mp.addXBs(data[1] == null ? new BS() : (BS) data[1]); 989 case T.bondcount: 990 // {atoms1}.bondCount({atoms2}) 991 SV x1 = mp.getX(); 992 if (x1.tok != T.bitset || args.length != 1 || args[0].tok != T.bitset) 993 return false; 994 atoms1 = (BS) x1.value; 995 atoms2 = (BS) args[0].value; 996 Lst<Integer> list = new Lst<Integer>(); 997 Atom[] atoms = vwr.ms.at; 998 for (int i = atoms1.nextSetBit(0); i >= 0; i = atoms1.nextSetBit(i + 1)) { 999 int n = 0; 1000 Bond[] b = atoms[i].bonds; 1001 for (int j = b.length; --j >= 0;) 1002 if (atoms2.get(b[j].getOtherAtom(atoms[i]).i)) 1003 n++; 1004 list.addLast(Integer.valueOf(n)); 1005 } 1006 return mp.addXList(list); 1007 } 1008 1009 // connected( 1010 for (int i = 0; i < args.length; i++) { 1011 SV var = args[i]; 1012 switch (var.tok) { 1013 case T.bitset: 1014 isBonds = (var.value instanceof BondSet); 1015 if (isBonds && atoms1 != null) 1016 return false; 1017 if (atoms1 == null) 1018 atoms1 = (BS) var.value; 1019 else if (atoms2 == null) 1020 atoms2 = (BS) var.value; 1021 else 1022 return false; 1023 break; 1024 case T.string: 1025 String type = SV.sValue(var); 1026 if (type.equalsIgnoreCase("hbond")) 1027 order = Edge.BOND_HYDROGEN_MASK; 1028 else 1029 order = ScriptParam.getBondOrderFromString(type); 1030 if (order == Edge.BOND_ORDER_NULL) 1031 return false; 1032 break; 1033 case T.decimal: 1034 haveDecimal = true; 1035 //$FALL-THROUGH$ 1036 default: 1037 int n = var.asInt(); 1038 float f = var.asFloat(); 1039 if (max != Integer.MAX_VALUE) 1040 return false; 1041 1042 if (min == Integer.MIN_VALUE) { 1043 min = Math.max(n, 0); 1044 fmin = f; 1045 } else { 1046 max = n; 1047 fmax = f; 1048 } 1049 } 1050 } 1051 if (min == Integer.MIN_VALUE) { 1052 min = 1; 1053 max = 100; 1054 fmin = JC.DEFAULT_MIN_CONNECT_DISTANCE; 1055 fmax = JC.DEFAULT_MAX_CONNECT_DISTANCE; 1056 } else if (max == Integer.MAX_VALUE) { 1057 max = min; 1058 fmax = fmin; 1059 fmin = JC.DEFAULT_MIN_CONNECT_DISTANCE; 1060 } 1061 if (atoms1 == null) 1062 atoms1 = vwr.getAllAtoms(); 1063 if (haveDecimal && atoms2 == null) 1064 atoms2 = atoms1; 1065 if (atoms2 != null) { 1066 BS bsBonds = new BS(); 1067 vwr.makeConnections(fmin, fmax, order, T.identify, atoms1, atoms2, 1068 bsBonds, isBonds, false, 0); 1069 return mp.addX(SV.newV(T.bitset, BondSet.newBS(bsBonds, 1070 vwr.ms.getAtomIndices(vwr.ms.getAtoms(T.bonds, bsBonds))))); 1071 } 1072 return mp.addXBs(vwr.ms.getAtomsConnected(min, max, order, atoms1)); 1073 } 1074 evaluateContact(ScriptMathProcessor mp, SV[] args)1075 private boolean evaluateContact(ScriptMathProcessor mp, SV[] args) { 1076 if (args.length < 1 || args.length > 3) 1077 return false; 1078 int i = 0; 1079 float distance = 100; 1080 int tok = args[0].tok; 1081 switch (tok) { 1082 case T.decimal: 1083 case T.integer: 1084 distance = SV.fValue(args[i++]); 1085 break; 1086 case T.bitset: 1087 break; 1088 default: 1089 return false; 1090 } 1091 if (i == args.length || !(args[i].value instanceof BS)) 1092 return false; 1093 BS bsA = BSUtil.copy((BS) args[i++].value); 1094 BS bsB = (i < args.length ? BSUtil.copy((BS) args[i].value) : null); 1095 RadiusData rd = new RadiusData(null, 1096 (distance > 10 ? distance / 100 : distance), 1097 (distance > 10 ? EnumType.FACTOR : EnumType.OFFSET), VDW.AUTO); 1098 bsB = setContactBitSets(bsA, bsB, true, Float.NaN, rd, false); 1099 bsB.or(bsA); 1100 return mp.addXBs(bsB); 1101 } 1102 evaluateData(ScriptMathProcessor mp, SV[] args)1103 private boolean evaluateData(ScriptMathProcessor mp, SV[] args) { 1104 1105 // x = data("somedataname") # the data 1106 // x = data("data2d_xxxx") # 2D data (x,y paired values) 1107 // x = data("data2d_xxxx", iSelected) # selected row of 2D data, with <=0 1108 // meaning "relative to the last row" 1109 // x = data("property_x", "property_y") # array mp.addition of two property 1110 // sets 1111 // x = data({atomno < 10},"xyz") # (or "pdb" or "mol") coordinate data in 1112 // xyz, pdb, or mol format 1113 // x = data(someData,ptrFieldOrColumn,nBytes,firstLine) # extraction of a 1114 // column of data based on a field (nBytes = 0) or column range (nBytes > 1115 // 0) 1116 String selected = (args.length == 0 ? "" : SV.sValue(args[0])); 1117 String type = ""; 1118 switch (args.length) { 1119 case 0: 1120 case 1: 1121 break; 1122 case 2: 1123 case 3: 1124 if (args[0].tok == T.bitset) 1125 return mp.addXStr(vwr.getModelFileData(selected, SV.sValue(args[1]), 1126 args.length == 3 && SV.bValue(args[2]))); 1127 break; 1128 case 4: 1129 int iField = args[1].asInt(); 1130 int nBytes = args[2].asInt(); 1131 int firstLine = args[3].asInt(); 1132 float[] f = Parser.parseFloatArrayFromMatchAndField(SV.sValue(args[0]), 1133 null, 0, 0, null, iField, nBytes, null, firstLine); 1134 return mp.addXStr(Escape.escapeFloatA(f, false)); 1135 default: 1136 return false; 1137 } 1138 if (selected.indexOf("data2d_") == 0) { 1139 // tab, newline separated data 1140 float[][] f1 = (float[][]) vwr.getDataObj(selected, null, 1141 JmolDataManager.DATA_TYPE_AFF); 1142 if (f1 == null) 1143 return mp.addXStr(""); 1144 if (args.length == 2 && args[1].tok == T.integer) { 1145 int pt = args[1].intValue; 1146 if (pt < 0) 1147 pt += f1.length; 1148 if (pt >= 0 && pt < f1.length) 1149 return mp.addXStr(Escape.escapeFloatA(f1[pt], false)); 1150 return mp.addXStr(""); 1151 } 1152 return mp.addXStr(Escape.escapeFloatAA(f1, false)); 1153 } 1154 1155 // parallel mp.addition of float property data sets 1156 1157 if (selected.indexOf("property_") == 0) { 1158 float[] f1 = (float[]) vwr.getDataObj(selected, null, 1159 JmolDataManager.DATA_TYPE_AF); 1160 if (f1 == null) 1161 return mp.addXStr(""); 1162 float[] f2 = (type.indexOf("property_") == 0 1163 ? (float[]) vwr.getDataObj(selected, null, 1164 JmolDataManager.DATA_TYPE_AF) 1165 : null); 1166 if (f2 != null) { 1167 f1 = AU.arrayCopyF(f1, -1); 1168 for (int i = Math.min(f1.length, f2.length); --i >= 0;) 1169 f1[i] += f2[i]; 1170 } 1171 return mp.addXStr(Escape.escapeFloatA(f1, false)); 1172 } 1173 1174 // some other data type -- just return it 1175 1176 //if (args.length == 1) { 1177 Object[] data = (Object[]) vwr.getDataObj(selected, null, 1178 JmolDataManager.DATA_TYPE_UNKNOWN); 1179 return mp.addXStr(data == null ? "" : "" + data[1]); 1180 // } 1181 } 1182 1183 /** 1184 * x = y.distance({atoms}) the average distance from elements of y to the 1185 * CENTER of {atoms} 1186 * 1187 * x = {atomset1}.distance.min({atomset2}, asAtomSet) If asAtomSet is true, 1188 * returns the closest atom in atomset1 to any atom of atomset2; if false or 1189 * omitted, returns an array listing the distance of each atom in atomset1 to 1190 * the closest atom in atomset2. This array can be used to assign properties 1191 * to atomset1: {1.1}.property_d = {1.1}.distance.min({2.1}); color {1.1} 1192 * property_d. 1193 * 1194 * x = {atomset1}.distance.min({point}, asAtomSet) If asAtomSet is true, 1195 * returns the atom in atomset1 closest to the specified point;if false or 1196 * omitted, returns the closest distance to the specified point from any atom 1197 * in atomset1. 1198 * 1199 * x = {atomset1}.distance.min({atomset2}).min returns the shortest distance 1200 * from any atom in atomset1 to any atom in atomset2. 1201 * 1202 * x = {atomset1}.distance.max({atomset2}, asAtomSet) If asAtomSet is true, 1203 * returns the furthest atom in atomset1 to any atom of atomset2; if false or 1204 * omitted, returns an array listing the distance of each atom in atomset1 to 1205 * the furthest atom in atomset2. 1206 * 1207 * x = {atomset1}.distance.max({point}, asAtomSet) If asAtomSet is true, 1208 * returns the atom in atomset1 furthest from the specified point;if false or 1209 * omitted, returns the furthest distance to the specified point from any atom 1210 * in atomset1. 1211 * 1212 * x = {atomset1}.distance.max({atomset2}).max returns the furthest distance 1213 * from any atom in atomset1 to any atom in atomset2. 1214 * 1215 * x = {atomset1}.distance.all({atomset2}) returns an array or array of arrays 1216 * of values 1217 * 1218 * @param mp 1219 * @param args 1220 * @param tok 1221 * @param op 1222 * optional .min .max for distance 1223 * @return true if successful 1224 * @throws ScriptException 1225 */ 1226 evaluateDotDist(ScriptMathProcessor mp, SV[] args, int tok, int op)1227 private boolean evaluateDotDist(ScriptMathProcessor mp, SV[] args, int tok, 1228 int op) 1229 throws ScriptException { 1230 boolean isDist = (tok == T.distance); 1231 SV x1, x2, x3 = null; 1232 switch (args.length) { 1233 case 2: 1234 if (op == Integer.MAX_VALUE) { 1235 x1 = args[0]; 1236 x2 = args[1]; 1237 break; 1238 } 1239 x3 = args[1]; 1240 //$FALL-THROUGH$ 1241 case 1: 1242 x1 = mp.getX(); 1243 x2 = args[0]; 1244 break; 1245 default: 1246 return false; 1247 } 1248 1249 float f = Float.NaN; 1250 try { 1251 if (tok == T.cross) { 1252 P3 a = P3.newP(mp.ptValue(x1, null)); 1253 a.cross(a, mp.ptValue(x2, null)); 1254 return mp.addXPt(a); 1255 } 1256 1257 P3 pt2 = (x2.tok == T.varray ? null : mp.ptValue(x2, null)); 1258 P4 plane2 = ScriptMathProcessor.planeValue(x2); 1259 if (isDist) { 1260 int minMax = (op == Integer.MIN_VALUE ? 0 : op & T.minmaxmask); 1261 boolean isMinMax = (minMax == T.min || minMax == T.max); 1262 boolean isAll = minMax == T.minmaxmask; 1263 switch (x1.tok) { 1264 case T.varray: 1265 case T.bitset: 1266 boolean isAtomSet1 = (x1.tok == T.bitset); 1267 boolean isAtomSet2 = (x2.tok == T.bitset); 1268 boolean isPoint2 = (x2.tok == T.point3f); 1269 BS bs1 = (isAtomSet1 ? (BS) x1.value : null); 1270 BS bs2 = (isAtomSet2 ? (BS) x2.value : null); 1271 Lst<SV> list1 = (isAtomSet1 ? null : x1.getList()); 1272 Lst<SV> list2 = (isAtomSet2 ? null : x2.getList()); 1273 boolean returnAtom = (isMinMax && x3 != null && x3.asBoolean()); 1274 switch (x2.tok) { 1275 case T.bitset: 1276 case T.varray: 1277 //$FALL-THROUGH$ 1278 case T.point3f: 1279 Atom[] atoms = vwr.ms.at; 1280 if (returnAtom) { 1281 float dMinMax = Float.NaN; 1282 int iMinMax = Integer.MAX_VALUE; 1283 if (isAtomSet1) { 1284 for (int i = bs1.nextSetBit(0); i >= 0; i = bs1 1285 .nextSetBit(i + 1)) { 1286 float d = (isPoint2 ? atoms[i].distanceSquared(pt2) 1287 : ((Float) e.getBitsetProperty(bs2, list2, op, atoms[i], 1288 plane2, x1.value, null, false, x1.index, false)) 1289 .floatValue()); 1290 if (minMax == T.min ? d >= dMinMax : d <= dMinMax) 1291 continue; 1292 dMinMax = d; 1293 iMinMax = i; 1294 } 1295 return mp.addXBs(iMinMax == Integer.MAX_VALUE ? new BS() 1296 : BSUtil.newAndSetBit(iMinMax)); 1297 } 1298 // list of points 1299 for (int i = list1.size(); --i >= 0;) { 1300 P3 pt = SV.ptValue(list1.get(i)); 1301 float d = (isPoint2 ? pt.distanceSquared(pt2) 1302 : ((Float) e.getBitsetProperty(bs2, list2, op, pt, plane2, 1303 x1.value, null, false, Integer.MAX_VALUE, false)) 1304 .floatValue()); 1305 if (minMax == T.min ? d >= dMinMax : d <= dMinMax) 1306 continue; 1307 dMinMax = d; 1308 iMinMax = i; 1309 } 1310 return mp.addXInt(iMinMax); 1311 } 1312 if (isAll) { 1313 if (bs2 == null) { 1314 float[] data = new float[bs1.cardinality()]; 1315 for (int p = 0, i = bs1.nextSetBit(0); i >= 0; i = bs1 1316 .nextSetBit(i + 1), p++) 1317 data[p] = atoms[i].distance(pt2); 1318 return mp.addXAF(data); 1319 } 1320 float[][] data2 = new float[bs1.cardinality()][bs2.cardinality()]; 1321 for (int p = 0, i = bs1.nextSetBit(0); i >= 0; i = bs1 1322 .nextSetBit(i + 1), p++) 1323 for (int q = 0, j = bs2.nextSetBit(0); j >= 0; j = bs2 1324 .nextSetBit(j + 1), q++) 1325 data2[p][q] = atoms[i].distance(atoms[j]); 1326 return mp.addXAFF(data2); 1327 } 1328 if (isMinMax) { 1329 float[] data = new float[isAtomSet1 ? bs1.cardinality() 1330 : list1.size()]; 1331 if (isAtomSet1) { 1332 for (int i = bs1.nextSetBit(0), p = 0; i >= 0; i = bs1 1333 .nextSetBit(i + 1)) 1334 data[p++] = ((Float) e.getBitsetProperty(bs2, list2, op, 1335 atoms[i], plane2, x1.value, null, false, x1.index, false)) 1336 .floatValue(); 1337 return mp.addXAF(data); 1338 } 1339 // list of points 1340 for (int i = data.length; --i >= 0;) 1341 data[i] = ((Float) e.getBitsetProperty(bs2, list2, op, 1342 SV.ptValue(list1.get(i)), plane2, null, null, false, 1343 Integer.MAX_VALUE, false)).floatValue(); 1344 return mp.addXAF(data); 1345 } 1346 return mp.addXObj(e.getBitsetProperty(bs1, list1, op, pt2, plane2, 1347 x1.value, null, false, x1.index, false)); 1348 } 1349 } 1350 } 1351 P3 pt1 = mp.ptValue(x1, null); 1352 P4 plane1 = ScriptMathProcessor.planeValue(x1); 1353 if (isDist) { 1354 if (plane2 != null && x3 != null) 1355 f = Measure.directedDistanceToPlane(pt1, plane2, SV.ptValue(x3)); 1356 else 1357 f = (plane1 == null 1358 ? (plane2 == null ? pt2.distance(pt1) 1359 : Measure.distanceToPlane(plane2, pt1)) 1360 : Measure.distanceToPlane(plane1, pt2)); 1361 } else { 1362 if (plane1 != null && plane2 != null) { 1363 // q1.dot(q2) assume quaternions 1364 f = plane1.x * plane2.x + plane1.y * plane2.y + plane1.z * plane2.z 1365 + plane1.w * plane2.w; 1366 // plane.dot(point) = 1367 } else { 1368 if (plane1 != null) 1369 pt1 = P3.new3(plane1.x, plane1.y, plane1.z); 1370 // point.dot(plane) 1371 else if (plane2 != null) 1372 pt2 = P3.new3(plane2.x, plane2.y, plane2.z); 1373 f = pt1.dot(pt2); 1374 } 1375 } 1376 } catch (Exception e) { 1377 } 1378 return mp.addXFloat(f); 1379 } 1380 evaluateHelix(ScriptMathProcessor mp, SV[] args)1381 private boolean evaluateHelix(ScriptMathProcessor mp, SV[] args) 1382 throws ScriptException { 1383 if (args.length < 1 || args.length > 5) 1384 return false; 1385 // helix({resno=3}) 1386 // helix({resno=3},"point|axis|radius|angle|draw|measure|array") 1387 // helix(resno,"point|axis|radius|angle|draw|measure|array") 1388 // helix(pt1, pt2, dq, "point|axis|radius|angle|draw|measure|array|") 1389 // helix(pt1, pt2, dq, "draw","someID") 1390 // helix(pt1, pt2, dq) 1391 int pt = (args.length > 2 ? 3 : 1); 1392 String type = (pt >= args.length ? "array" : SV.sValue(args[pt])); 1393 int tok = T.getTokFromName(type); 1394 if (args.length > 2) { 1395 // helix(pt1, pt2, dq ...) 1396 P3 pta = mp.ptValue(args[0], null); 1397 P3 ptb = mp.ptValue(args[1], null); 1398 if (tok == T.nada || args[2].tok != T.point4f || pta == null 1399 || ptb == null) 1400 return false; 1401 Quat dq = Quat.newP4((P4) args[2].value); 1402 T3[] data = Measure.computeHelicalAxis(pta, ptb, dq); 1403 //new T3[] { pt_a_prime, n, r, P3.new3(theta, pitch, residuesPerTurn) }; 1404 return (data == null ? false 1405 : mp.addXObj(Escape.escapeHelical(type, tok, pta, ptb, data))); 1406 } 1407 BS bs = (args[0].value instanceof BS ? (BS) args[0].value 1408 : vwr.ms.getAtoms(T.resno, new Integer(args[0].asInt()))); 1409 switch (tok) { 1410 case T.point: 1411 case T.axis: 1412 case T.radius: 1413 return mp.addXObj(getHelixData(bs, tok)); 1414 case T.angle: 1415 return mp.addXFloat(((Float) getHelixData(bs, T.angle)).floatValue()); 1416 case T.draw: 1417 case T.measure: 1418 return mp.addXObj(getHelixData(bs, tok)); 1419 case T.array: 1420 String[] data = (String[]) getHelixData(bs, T.list); 1421 if (data == null) 1422 return false; 1423 return mp.addXAS(data); 1424 } 1425 return false; 1426 } 1427 getHelixData(BS bs, int tokType)1428 private Object getHelixData(BS bs, int tokType) { 1429 int iAtom = bs.nextSetBit(0); 1430 return (iAtom < 0 ? "null" 1431 : vwr.ms.at[iAtom].group.getHelixData(tokType, vwr.getQuaternionFrame(), 1432 vwr.getInt(T.helixstep))); 1433 } 1434 evaluateInChI(ScriptMathProcessor mp, SV[] args)1435 private boolean evaluateInChI(ScriptMathProcessor mp, SV[] args) 1436 throws ScriptException { 1437 // {*}.inchi(options) 1438 // InChI.inchi("key") 1439 // smiles.inchi(options) // including "key" 1440 // molFIleData.inchi(options) // including "key" 1441 SV x1 = mp.getX(); 1442 String flags = (args.length > 0 ? SV.sValue(args[0]) : ""); 1443 BS atoms = SV.getBitSet(x1, true); 1444 String molData = null; 1445 if (atoms == null) { 1446 molData = SV.sValue(x1); 1447 } 1448 return mp.addXStr(vwr.getInchi(atoms, molData, flags)); 1449 } 1450 evaluateFind(ScriptMathProcessor mp, SV[] args)1451 private boolean evaluateFind(ScriptMathProcessor mp, SV[] args) 1452 throws ScriptException { 1453 1454 // {1.1}.find("2.1","map", labelFormat) 1455 // {*}.find("inchi",inchi-options) 1456 // {*}.find("crystalClass") 1457 // {*}.find("CF",true|false) 1458 // {*}.find("MF") 1459 // {*}.find("MF", "C2H4") 1460 // {*}.find("SEQUENCE") 1461 // {*}.find("SEQ") 1462 // {*}.find("SEQ", true) 1463 // {*}.find("SEQUENCE", true) 1464 // {*}.find("SEQUENCE", "H") 1465 // "AVA".find("SEQUENCE") 1466 // "a sequence".find("sequence", "i") // case insensitive 1467 // "a sequence".find("sequence", true) // case insensitive 1468 // "a sequence".find("sequence", false) // case sensitive 1469 // {*}.find("SMARTS", "CCCC") 1470 // "CCCC".find("SMARTS", "CC") 1471 // "CCCC".find("SMILES", "MF") 1472 // {2.1}.find("CCCC",{1.1}) // find pattern "CCCC" in {2.1} with conformation given by {1.1} 1473 // {*}.find("ccCCN","BONDS") 1474 // {*}.find("SMILES","H") 1475 // {*}.find("chemical",type) 1476 // {1.1}.find("SMILES", {2.1}) 1477 // {1.1}.find("SMARTS", {2.1}) 1478 // smiles1.find("SMILES", smiles2) 1479 // smiles1.find("SMILES", smiles2) 1480 1481 1482 SV x1 = mp.getX(); 1483 boolean isList = (x1.tok == T.varray); 1484 boolean isAtoms = (x1.tok == T.bitset); 1485 boolean isEmpty = (args.length == 0); 1486 int tok0 = (args.length == 0 ? T.nada : args[0].tok); 1487 String sFind = (isEmpty || tok0 != T.string ? "" : SV.sValue(args[0])); 1488 boolean isOff = (args.length > 1 && args[1].tok == T.off); 1489 int tokLast = (tok0 == T.nada ? T.nada : args[args.length - 1].tok); 1490 SV argLast = (tokLast == T.nada ? SV.vF : args[args.length - 1]); 1491 boolean isON = !isList && (tokLast == T.on); 1492 String flags = (args.length > 1 && args[1].tok != T.on 1493 && args[1].tok != T.off && args[1].tok != T.bitset ? SV.sValue(args[1]) 1494 : ""); 1495 boolean isSequence = !isList && !isOff 1496 && sFind.equalsIgnoreCase("SEQUENCE"); 1497 boolean isSeq = !isList && !isOff && sFind.equalsIgnoreCase("SEQ"); 1498 if (sFind.toUpperCase().startsWith("SMILES/")) { 1499 if (!sFind.endsWith("/")) 1500 sFind += "/"; 1501 String s = sFind.substring(6) + "//"; 1502 if (JC.isSmilesCanonical(s)) { 1503 flags = "SMILES"; 1504 sFind = "CHEMICAL"; 1505 } else { 1506 sFind = "SMILES"; 1507 flags = s + flags; 1508 } 1509 } else if (sFind.toUpperCase().startsWith("SMARTS/")) { 1510 if (!sFind.endsWith("/")) 1511 sFind += "/"; 1512 flags = sFind.substring(6) + (flags.length() == 0 ? "//" : flags); 1513 sFind = "SMARTS"; 1514 } 1515 1516 boolean isSmiles = !isList && sFind.equalsIgnoreCase("SMILES"); 1517 boolean isSMARTS = !isList && sFind.equalsIgnoreCase("SMARTS"); 1518 boolean isChemical = !isList && sFind.equalsIgnoreCase("CHEMICAL"); 1519 boolean isMF = !isList && sFind.equalsIgnoreCase("MF"); 1520 boolean isCF = !isList && sFind.equalsIgnoreCase("CELLFORMULA"); 1521 boolean isInchi = isAtoms && !isList && sFind.equalsIgnoreCase("INCHI"); 1522 boolean isInchiKey = isAtoms && !isList 1523 && sFind.equalsIgnoreCase("INCHIKEY"); 1524 boolean isStructureMap = (!isSmiles && !isSMARTS && tok0 == T.bitset 1525 && flags.toLowerCase().indexOf("map") >= 0); 1526 try { 1527 if (isInchi || isInchiKey) { 1528 if (isInchiKey) 1529 flags += " key"; 1530 return mp.addXStr(vwr.getInchi(SV.getBitSet(x1, true), null, flags)); 1531 } 1532 if (isChemical) { 1533 BS bsAtoms = (isAtoms ? (BS) x1.value : null); 1534 String data = (bsAtoms == null ? SV.sValue(x1) 1535 : vwr.getOpenSmiles(bsAtoms)); 1536 data = (data.length() == 0 ? "" 1537 : vwr.getChemicalInfo(data, flags.toLowerCase(), bsAtoms)).trim(); 1538 if (data.startsWith("InChI")) 1539 data = PT.rep(PT.rep(data, "InChI=", ""), "InChIKey=", ""); 1540 return mp.addXStr(data); 1541 } 1542 if (isSmiles || isSMARTS || isAtoms) { 1543 int iPt = (isStructureMap ? 0 : isSmiles || isSMARTS ? 2 : 1); 1544 BS bs2 = (iPt < args.length && args[iPt].tok == T.bitset 1545 ? (BS) args[iPt++].value 1546 : null); 1547 boolean asBonds = ("bonds".equalsIgnoreCase(SV.sValue(argLast))); 1548 boolean isAll = (asBonds || isON); 1549 Object ret = null; 1550 switch (x1.tok) { 1551 case T.string: 1552 String smiles = SV.sValue(x1); 1553 if (bs2 != null || isSmiles && args.length == 1) 1554 return false; 1555 if (flags.equalsIgnoreCase("mf")) { 1556 ret = vwr.getSmilesMatcher().getMolecularFormula(smiles, isSMARTS); 1557 } else { 1558 String pattern = flags; 1559 // "SMARTS",flags,asMap, allMappings 1560 boolean allMappings = true; 1561 boolean asMap = false; 1562 switch (args.length) { 1563 case 4: 1564 allMappings = SV.bValue(args[3]); 1565 //$FALL-THROUGH$ 1566 case 3: 1567 asMap = SV.bValue(args[2]); 1568 break; 1569 } 1570 boolean isChirality = pattern.equals("chirality"); 1571 boolean justOne = (!asMap 1572 && (!allMappings || !isSMARTS && !isChirality)); 1573 try { 1574 ret = e.getSmilesExt().getSmilesMatches(pattern, smiles, null, 1575 null, 1576 isSMARTS ? JC.SMILES_TYPE_SMARTS : JC.SMILES_TYPE_SMILES, 1577 !asMap, !allMappings); 1578 } catch (Exception ex) { 1579 System.out.println(ex.getMessage()); 1580 return mp.addXInt(-1); 1581 } 1582 int len = (isChirality ? 1 1583 : AU.isAI(ret) ? ((int[]) ret).length : ((int[][]) ret).length); 1584 if (len == 0 1585 && vwr.getSmilesMatcher().getLastException() != "MF_FAILED" 1586 && smiles.toLowerCase().indexOf("noaromatic") < 0 1587 && smiles.toLowerCase().indexOf("strict") < 0) { 1588 // problem arising from Jmol interpreting one string as aromatic 1589 // and the other not, perhaps because of one using [N] as in NCI caffeine 1590 // and one not, as from PubChem. 1591 // There is no loss in doing this search again, except for 1592 ret = e.getSmilesExt().getSmilesMatches(pattern, smiles, null, 1593 null, 1594 JC.SMILES_NO_AROMATIC | (isSMARTS ? JC.SMILES_TYPE_SMARTS 1595 : JC.SMILES_TYPE_SMILES), 1596 !asMap, !allMappings); 1597 } 1598 if (justOne) { 1599 return mp.addXInt(!allMappings && len > 0 ? 1 : len); 1600 } 1601 } 1602 break; 1603 case T.bitset: 1604 BS bs = (BS) x1.value; 1605 if (sFind.equalsIgnoreCase("crystalClass")) { 1606 // {*}.find("crystalClass") 1607 // {*}.find("crystalClass", pt) 1608 int n = bs.nextSetBit(0); 1609 BS bsNew = null; 1610 if (args.length != 2) { 1611 bsNew = new BS(); 1612 bsNew.set(n); 1613 } 1614 return mp.addXList(vwr.ms.generateCrystalClass(n, 1615 (bsNew != null ? vwr.ms.getAtomSetCenter(bsNew) 1616 : argLast.tok == T.bitset 1617 ? vwr.ms.getAtomSetCenter((BS) argLast.value) 1618 : SV.ptValue(argLast)))); 1619 } 1620 if (isMF && flags.length() != 0) { 1621 return mp.addXBs(JmolMolecule.getBitSetForMF(vwr.ms.at, bs, flags)); 1622 } 1623 if (isMF || isCF) { 1624 return mp.addXStr(JmolMolecule.getMolecularFormulaAtoms(vwr.ms.at, 1625 bs, (isMF ? null : vwr.ms.getCellWeights(bs)), isON)); 1626 } 1627 if (isSequence || isSeq) { 1628 boolean isHH = (argLast.asString().equalsIgnoreCase("H")); 1629 isAll |= isHH; 1630 return mp.addXStr(vwr.getSmilesOpt(bs, -1, -1, (isAll 1631 ? JC.SMILES_GEN_BIO_ALLOW_UNMATCHED_RINGS 1632 | JC.SMILES_GEN_BIO_COV_CROSSLINK 1633 | (isHH ? JC.SMILES_GEN_BIO_HH_CROSSLINK : 0) 1634 : 0) 1635 | (isSeq ? JC.SMILES_GEN_BIO_NOCOMMENTS : JC.SMILES_GEN_BIO), 1636 null)); 1637 } 1638 if (isStructureMap) { 1639 int[] map = null, map1 = null, map2 = null; 1640 Object[][] mapNames = null; 1641 String key = (args.length == 3 ? SV.sValue(argLast) : null); 1642 char itype = (key == null || key.equals("%i") 1643 || key.equals("number") ? 'i' 1644 : key.equals("%a") || key.equals("name") ? 'a' 1645 : key.equals("%D") || key.equals("index") ? 'D' : '?'); 1646 if (key == null) 1647 key = "number"; 1648 String err = null; 1649 flags = flags.replace("map", "").trim(); 1650 sFind = vwr.getSmilesOpt(bs, 0, 0, 0, flags); 1651 if (bs.cardinality() != bs2.cardinality()) { 1652 err = "atom sets are not the same size"; 1653 } else { 1654 try { 1655 int iflags = (JC.SMILES_TYPE_SMILES | JC.SMILES_MAP_UNIQUE 1656 | JC.SMILES_FIRST_MATCH_ONLY); 1657 if (flags.length() > 0) 1658 sFind = "/" + flags + "/" + sFind; 1659 map1 = vwr.getSmilesMatcher().getCorrelationMaps(sFind, 1660 vwr.ms.at, vwr.ms.ac, bs, iflags)[0]; 1661 int[][] m2 = vwr.getSmilesMatcher().getCorrelationMaps(sFind, 1662 vwr.ms.at, vwr.ms.ac, bs2, iflags); 1663 if (m2.length > 0) { 1664 map = new int[bs.length()]; 1665 for (int i = map.length; --i >= 0;) 1666 map[i] = -1; 1667 map2 = m2[0]; 1668 for (int i = map1.length; --i >= 0;) 1669 map[map1[i]] = map2[i]; 1670 mapNames = new Object[map1.length][2]; 1671 BS bsAll = BS.copy(bs); 1672 bsAll.or(bs2); 1673 String[] names = (itype == '?' ? new String[bsAll.length()] 1674 : null); 1675 if (names != null) 1676 names = (String[]) e.getCmdExt().getBitsetIdentFull(bsAll, 1677 key, null, false, Integer.MAX_VALUE, false, names); 1678 Atom[] at = vwr.ms.at; 1679 for (int pt = 0, i = bs.nextSetBit(0); i >= 0; i = bs 1680 .nextSetBit(i + 1)) { 1681 int j = map[i]; 1682 if (j == -1) 1683 continue; 1684 Object[] a; 1685 switch (itype) { 1686 case 'a': 1687 a = new String[] { at[i].getAtomName(), 1688 at[j].getAtomName() }; 1689 break; 1690 case 'i': 1691 a = new Integer[] { 1692 Integer.valueOf(at[i].getAtomNumber()), 1693 Integer.valueOf(at[j].getAtomNumber()) }; 1694 break; 1695 case 'D': 1696 a = new Integer[] { Integer.valueOf(i), 1697 Integer.valueOf(j) }; 1698 break; 1699 default: 1700 a = new String[] { names[i], names[j] }; 1701 break; 1702 } 1703 mapNames[pt++] = a; 1704 } 1705 } 1706 } catch (Exception ee) { 1707 err = ee.getMessage(); 1708 } 1709 } 1710 Map<String, Object> m = new HashMap<String, Object>(); 1711 m.put("BS1", bs); 1712 m.put("BS2", bs2); 1713 m.put("SMILES", sFind); 1714 if (err == null) { 1715 m.put("SMILEStoBS1", map1); 1716 m.put("SMILEStoBS2", map2); 1717 m.put("BS1toBS2", map); 1718 m.put("MAP1to2", mapNames); 1719 m.put("key", key); 1720 } else { 1721 m.put("error", err); 1722 } 1723 return mp.addXMap(m); 1724 } 1725 if (isSmiles || isSMARTS) { 1726 sFind = (args.length > 1 && args[1].tok == T.bitset 1727 ? vwr.getSmilesOpt((BS) args[1].value, 0, 0, 0, flags) 1728 : flags); 1729 } 1730 flags = flags.toUpperCase(); 1731 BS bsMatch3D = bs2; 1732 if (asBonds) { 1733 // this will return a single match 1734 int[][] map = vwr.getSmilesMatcher().getCorrelationMaps(sFind, 1735 vwr.ms.at, vwr.ms.ac, bs, 1736 (isSmiles ? JC.SMILES_TYPE_SMILES : JC.SMILES_TYPE_SMARTS) 1737 | JC.SMILES_FIRST_MATCH_ONLY); 1738 ret = (map.length > 0 ? vwr.ms.getDihedralMap(map[0]) : new int[0]); 1739 } else if (flags.equalsIgnoreCase("map")) { 1740 // we add NO_AROMATIC because that is not important for structure-SMILES matching 1741 int[][] map = vwr.getSmilesMatcher().getCorrelationMaps(sFind, 1742 vwr.ms.at, vwr.ms.ac, bs, 1743 (isSmiles ? JC.SMILES_TYPE_SMILES : JC.SMILES_TYPE_SMARTS) 1744 | JC.SMILES_MAP_UNIQUE | JC.SMILES_NO_AROMATIC); 1745 ret = map; 1746 } else { 1747 // SMILES or SMARTS only 1748 int smilesFlags = (isSmiles ? 1749 1750 (flags.indexOf("OPEN") >= 0 ? JC.SMILES_TYPE_OPENSMILES 1751 : JC.SMILES_TYPE_SMILES) 1752 : JC.SMILES_TYPE_SMARTS) 1753 | (isON && sFind.length() == 0 ? JC.SMILES_GEN_BIO_COV_CROSSLINK 1754 | JC.SMILES_GEN_BIO_COMMENT : 0); 1755 if (flags.indexOf("/MOLECULE/") >= 0) { 1756 // all molecules 1757 JmolMolecule[] mols = vwr.ms.getMolecules(); 1758 Lst<BS> molList = new Lst<BS>(); 1759 for (int i = 0; i < mols.length; i++) { 1760 if (mols[i].atomList.intersects(bs)) { 1761 BS bsRet = (BS) e.getSmilesExt().getSmilesMatches(sFind, null, 1762 mols[i].atomList, bsMatch3D, smilesFlags, !isON, false); 1763 if (!bsRet.isEmpty()) 1764 molList.addLast(bsRet); 1765 } 1766 } 1767 ret = molList; 1768 } else { 1769 ret = e.getSmilesExt().getSmilesMatches(sFind, null, bs, 1770 bsMatch3D, smilesFlags, !isON, false); 1771 } 1772 } 1773 break; 1774 } 1775 if (ret == null) 1776 e.invArg(); 1777 return mp.addXObj(ret); 1778 } 1779 } catch (Exception ex) { 1780 e.evalError(ex.getMessage(), null); 1781 } 1782 BS bs = new BS(); 1783 Lst<SV> svlist = (isList ? x1.getList() : null); 1784 if (isList && tok0 != T.string && tok0 != T.nada) { 1785 SV v = args[0]; 1786 for (int i = 0, n = svlist.size(); i < n; i++) { 1787 if (SV.areEqual(svlist.get(i), v)) 1788 bs.set(i); 1789 } 1790 int[] ret = new int[bs.cardinality()]; 1791 for (int pt = 0, i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) 1792 ret[pt++] = i + 1; 1793 return mp.addXAI(ret); 1794 } 1795 1796 boolean isReverse = (flags.indexOf("v") >= 0); 1797 boolean isCaseInsensitive = (flags.indexOf("i") >= 0) || isOff; 1798 boolean asMatch = (flags.indexOf("m") >= 0); 1799 boolean checkEmpty = (sFind.length() == 0); 1800 boolean isPattern = (!checkEmpty && args.length == 2); 1801 if (isList || isPattern) { 1802 JmolPatternMatcher pm = (isPattern ? getPatternMatcher() : null); 1803 Pattern pattern = null; 1804 if (isPattern) { 1805 try { 1806 pattern = pm.compile(sFind, isCaseInsensitive); 1807 } catch (Exception ex) { 1808 e.evalError(ex.toString(), null); 1809 } 1810 } 1811 String[] list = (checkEmpty ? null : SV.strListValue(x1)); 1812 int nlist = (checkEmpty ? svlist.size() : list.length); 1813 if (Logger.debugging) 1814 Logger.debug("finding " + sFind); 1815 int n = 0; 1816 Matcher matcher = null; 1817 Lst<String> v = (asMatch ? new Lst<String>() : null); 1818 String what = ""; 1819 for (int i = 0; i < nlist; i++) { 1820 boolean isMatch; 1821 if (checkEmpty) { 1822 SV o = svlist.get(i); 1823 switch (o.tok) { 1824 case T.hash: 1825 isMatch = (o.getMap().isEmpty() != isEmpty); 1826 break; 1827 case T.varray: 1828 isMatch = ((o.getList().size() == 0) != isEmpty); 1829 break; 1830 case T.string: 1831 isMatch = ((o.asString().length() == 0) != isEmpty); 1832 break; 1833 default: 1834 isMatch = true; 1835 } 1836 } else if (isPattern) { 1837 what = list[i]; 1838 matcher = pattern.matcher(what); 1839 isMatch = matcher.find(); 1840 } else { 1841 isMatch = (SV.sValue(svlist.get(i)).indexOf(sFind) >= 0); 1842 } 1843 if (asMatch && isMatch || !asMatch && isMatch == !isReverse) { 1844 n++; 1845 bs.set(i); 1846 if (asMatch) 1847 v.addLast( 1848 isReverse 1849 ? what.substring(0, matcher.start()) 1850 + what.substring(matcher.end()) 1851 : matcher.group()); 1852 } 1853 } 1854 if (!isList) { 1855 return (asMatch ? mp.addXStr(v.size() == 1 ? (String) v.get(0) : "") 1856 : isReverse ? mp.addXBool(n == 1) 1857 : asMatch ? mp.addXStr(n == 0 ? "" : matcher.group()) 1858 : mp.addXInt(n == 0 ? 0 : matcher.start() + 1)); 1859 } 1860 // removed in 14.2/3.14 -- not documented and not expected if (n == 1) 1861 // return mp.addXStr(asMatch ? (String) v.get(0) : list[ipt]); 1862 1863 if (asMatch) { 1864 String[] listNew = new String[n]; 1865 if (n > 0) 1866 for (int i = list.length; --i >= 0;) 1867 if (bs.get(i)) { 1868 --n; 1869 listNew[n] = (asMatch ? (String) v.get(n) : list[i]); 1870 } 1871 return mp.addXAS(listNew); 1872 } 1873 Lst<SV> l = new Lst<SV>(); 1874 for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) 1875 l.addLast(svlist.get(i)); 1876 return mp.addXList(l); 1877 } 1878 if (isSequence) { 1879 return mp.addXStr(vwr.getJBR().toStdAmino3(SV.sValue(x1))); 1880 } 1881 return mp.addXInt(SV.sValue(x1).indexOf(sFind) + 1); 1882 } 1883 1884 /** 1885 * _ by itself, not as a function, is shorthand for 1886 * getProperty("auxiliaryInfo") 1887 * 1888 * $ print _.keys 1889 * 1890 * boundbox group3Counts group3Lists modelLoadNote models properties 1891 * someModelsHaveFractionalCoordinates someModelsHaveSymmetry 1892 * someModelsHaveUnitcells symmetryRange 1893 * 1894 * 1895 * _m by itself, not as a function, is shorthand for 1896 * getProperty("auxiliaryInfo.models")[_currentFrame] 1897 * 1898 * $ print format("json",_m.unitCellParams) 1899 * 1900 * [ 0.0,0.0,0.0,0.0,0.0,0.0,0.0,-2.1660376,-2.1660376,0.0,-2.1660376, 1901 * 2.1660376,-4.10273,0.0,0.0,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN ] 1902 * 1903 * 1904 * {atomset}._ by itself delivers a subset array of auxiliaryInfo.models for 1905 * all models in {atomset} 1906 * 1907 * $ print {*}._..1..aflowInfo 1908 * 1909 * (first model's aflowInfo) 1910 * 1911 * 1912 * _(key) prepends "auxiliaryInfo.models", delivering a modelCount-length 1913 * array of information 1914 * 1915 * $ print _("aflowInfo[SELECT auid WHERE H__eV___VASP_ < 0]") 1916 * 1917 * 1918 * {atomset}._(key) selects for model Auxiliary info related to models of the 1919 * specified atoms 1920 * 1921 * {atomset}.getProperty(key) defaults to atomInfo, but also allows key to 1922 * start with "bondInfo" 1923 * 1924 * Examples: 1925 * 1926 * print _("aflowInfo[select sg where volume_cell > 70]") 1927 * 1928 * print {model>10}._("aflowInfo[select sg where volume_cell > 70]") 1929 * 1930 * @param mp 1931 * @param args 1932 * @param tok0 1933 * @param isAtomProperty 1934 * @return true if no syntax problems 1935 * @throws ScriptException 1936 */ evaluateGetProperty(ScriptMathProcessor mp, SV[] args, int tok0, boolean isAtomProperty)1937 private boolean evaluateGetProperty(ScriptMathProcessor mp, SV[] args, 1938 int tok0, boolean isAtomProperty) 1939 throws ScriptException { 1940 int nargs = args.length; 1941 boolean isSelect = (isAtomProperty && tok0 == T.select); 1942 boolean isPivot = (isAtomProperty && tok0 == T.pivot && nargs > 0); 1943 boolean isAuxiliary = (tok0 == T.__); 1944 int pt = 0; 1945 int tok = (nargs == 0 ? T.nada : args[0].tok); 1946 if (nargs == 2 && (tok == T.varray || tok == T.hash || tok == T.context)) { 1947 return mp.addXObj( 1948 vwr.extractProperty(args[0].value, args[1].value.toString(), -1)); 1949 } 1950 BS bsSelect = (isAtomProperty && nargs == 1 && args[0].tok == T.bitset 1951 ? (BS) args[0].value 1952 : null); 1953 String pname = (bsSelect == null && nargs > 0 ? SV.sValue(args[pt++]) : ""); 1954 String propertyName = pname; 1955 String lc = propertyName.toLowerCase(); 1956 if (!isSelect && lc.indexOf("[select ") < 0) 1957 propertyName = lc; 1958 boolean isJSON = false; 1959 if (propertyName.equals("json") && nargs > pt) { 1960 isJSON = true; 1961 propertyName = SV.sValue(args[pt++]); 1962 } 1963 SV x = null; 1964 if (isAtomProperty) { // also includes $isosurface1.getProperty 1965 x = mp.getX(); 1966 switch (x.tok) { 1967 case T.bitset: 1968 break; 1969 case T.string: 1970 String name = (String) x.value; 1971 Object[] data = new Object[3]; 1972 int shapeID; 1973 if (name.startsWith("$")) { 1974 // "$P4".getProperty.... 1975 name = name.substring(1); 1976 shapeID = vwr.shm.getShapeIdFromObjectName(name); 1977 if (shapeID >= 0) { 1978 data[0] = name; 1979 vwr.shm.getShapePropertyData(shapeID, "index", data); 1980 if (data[1] != null && !pname.equals("index")) { 1981 int index = ((Integer) data[1]).intValue(); 1982 data[1] = vwr.shm.getShapePropertyIndex(shapeID, pname.intern(), 1983 index); 1984 } 1985 } 1986 } else { 1987 shapeID = JC.shapeTokenIndex(T.getTokFromName(name)); 1988 if (shapeID >= 0) { 1989 // "isosurface".getProperty... 1990 data[0] = pname; 1991 data[1] = Integer.valueOf(-1); 1992 vwr.shm.getShapePropertyData(shapeID, pname.intern(), data); 1993 } 1994 } 1995 return (data[1] == null ? mp.addXStr("") : mp.addXObj(data[1])); 1996 case T.varray: 1997 if (isPivot) { 1998 Lst<SV> lstx = x.getList(); 1999 // array of hash pivot("key") 2000 Map<String, SV> map = new Hashtable<String, SV>(); 2001 String sep = (nargs > 1 ? SV.sValue(args[nargs - 1]) : null); 2002 if (sep != null) 2003 nargs--; 2004 String[] keys = new String[nargs]; 2005 for (int i = 0; i < nargs; i++) 2006 keys[i] = SV.sValue(args[i]); 2007 for (int i = 0, n = lstx.size(); i < n; i++) { 2008 SV sv = lstx.get(i); 2009 if (sv.tok != T.hash) 2010 continue; 2011 Map<String, SV> mapi = sv.getMap(); 2012 String key = ""; 2013 for (int j = 0; j < nargs; j++) { 2014 SV obj = mapi.get(keys[j]); 2015 key += (j == 0 ? "" : sep) + SV.sValue(obj); 2016 } 2017 SV vlist = map.get(key); 2018 if (vlist == null) 2019 map.put(key, vlist = SV.newV(T.varray, new Lst<SV>())); 2020 vlist.getList().addLast(sv); 2021 } 2022 return mp.addXMap(map); 2023 } 2024 if (bsSelect != null) { 2025 Lst<SV> l0 = x.getList(); 2026 Lst<SV> lst = new Lst<SV>(); 2027 for (int i = bsSelect.nextSetBit(0); i >= 0; i = bsSelect 2028 .nextSetBit(i + 1)) 2029 lst.addLast(l0.get(i)); 2030 return mp.addXList(lst); 2031 } 2032 //$FALL-THROUGH$ 2033 default: 2034 if (isSelect) 2035 propertyName = "[SELECT " + propertyName + "]"; 2036 return mp.addXObj(vwr.extractProperty(x, propertyName, -1)); 2037 } 2038 if (!lc.startsWith("bondinfo") && !lc.startsWith("atominfo") 2039 && !lc.startsWith("modelkitinfo")) 2040 propertyName = "atomInfo." + propertyName; 2041 } 2042 Object propertyValue = ""; 2043 if (propertyName.equalsIgnoreCase("fileContents") && nargs >= 2) { 2044 String s = SV.sValue(args[1]); 2045 for (int i = 2; i < nargs; i++) 2046 s += "|" + SV.sValue(args[i]); 2047 propertyValue = s; 2048 pt = nargs; 2049 } else if (nargs > pt) { 2050 switch (args[pt].tok) { 2051 case T.bitset: 2052 propertyValue = args[pt++].value; 2053 if (propertyName.equalsIgnoreCase("bondInfo") && nargs > pt 2054 && args[pt].tok == T.bitset) 2055 propertyValue = new BS[] { (BS) propertyValue, (BS) args[pt].value }; 2056 break; 2057 case T.hash: 2058 case T.string: 2059 if (vwr.checkPropertyParameter(propertyName)) 2060 propertyValue = args[pt++].value; 2061 break; 2062 } 2063 } 2064 if (isAtomProperty) { 2065 BS bs = (BS) x.value; 2066 int iAtom = bs.nextSetBit(0); 2067 if (iAtom < 0) 2068 return mp.addXStr(""); 2069 propertyValue = bs; 2070 } 2071 if (isAuxiliary && !isAtomProperty) 2072 propertyName = "auxiliaryInfo.models." + propertyName; 2073 propertyName = PT.rep(propertyName, ".[", "["); 2074 Object property = vwr.getProperty(null, propertyName, propertyValue); 2075 if (pt < nargs) 2076 property = vwr.extractProperty(property, args, pt); 2077 return mp.addXObj(isJSON ? SV.safeJSON("value", property) 2078 : SV.isVariableType(property) ? property 2079 : Escape.toReadable(propertyName, property)); 2080 } 2081 evaluateFormat(ScriptMathProcessor mp, int intValue, SV[] args, boolean isLabel)2082 private boolean evaluateFormat(ScriptMathProcessor mp, int intValue, 2083 SV[] args, boolean isLabel) 2084 throws ScriptException { 2085 // NOT {xxx}.label 2086 // {xxx}.label("....") 2087 // {xxx}.yyy.format("...") 2088 // (value).format("...") 2089 // format("....",a,b,c...) 2090 // format("....",[a1, a2, a3, a3....]) 2091 // format("base64", x) 2092 // format("JSON", x) 2093 // format("byteArray", x) 2094 // format("array", x) 2095 SV x1 = (args.length < 2 || intValue == T.format ? mp.getX() : null); 2096 String format = (args.length == 0 ? "%U" 2097 : args[0].tok == T.varray ? null : SV.sValue(args[0])); 2098 if (!isLabel && args.length > 0 && x1 != null && x1.tok != T.bitset 2099 && format != null) { 2100 // x1.format(["energy", "pointGroup"]); 2101 // x1.format("%5.3f %5s", ["energy", "pointGroup"]) 2102 // but not x1.format() or {*}.format(....) 2103 if (args.length == 2) { 2104 Lst<SV> listIn = x1.getList(); 2105 Lst<SV> formatList = args[1].getList(); 2106 if (listIn == null || formatList == null) 2107 return false; 2108 x1 = SV.getVariableList(getSublist(listIn, formatList)); 2109 } 2110 args = new SV[] { args[0], x1 }; 2111 x1 = null; 2112 } 2113 if (x1 == null) { 2114 int pt = (isLabel ? -1 : SV.getFormatType(format)); 2115 if (pt >= 0 && args.length != 2) 2116 return false; 2117 if (pt >= 0 || args.length < 2 || args[1].tok != T.varray) { 2118 Object o = SV.format(args, pt); 2119 return (format.equalsIgnoreCase("json") ? mp.addXStr((String) o) 2120 : mp.addXObj(o)); 2121 } 2122 // fill an array with applied formats 2123 Lst<SV> a = args[1].getList(); 2124 SV[] args2 = new SV[] { args[0], null }; 2125 String[] sa = new String[a.size()]; 2126 for (int i = sa.length; --i >= 0;) { 2127 args2[1] = a.get(i); 2128 sa[i] = SV.format(args2, pt).toString(); 2129 } 2130 return mp.addXAS(sa); 2131 } 2132 if (x1.tok == T.varray && format == null) { 2133 Lst<SV> listIn = x1.getList(); 2134 Lst<SV> formatList = args[0].getList(); 2135 Lst<SV> listOut = getSublist(listIn, formatList); 2136 return mp.addXList(listOut); 2137 } 2138 2139 BS bs = (x1.tok == T.bitset ? (BS) x1.value : null); 2140 boolean asArray = T.tokAttr(intValue, T.minmaxmask); // "all" 2141 return mp.addXObj(format == null ? "" 2142 : bs == null ? SV.sprintf(PT.formatCheck(format), x1) 2143 : e.getCmdExt().getBitsetIdent(bs, format, x1.value, true, x1.index, 2144 asArray)); 2145 2146 } 2147 2148 /** 2149 * [ {...},{...}... ] ==> [[...],[...]] 2150 * 2151 * @param listIn 2152 * @param formatList 2153 * @return sublist 2154 */ getSublist(Lst<SV> listIn, Lst<SV> formatList)2155 private Lst<SV> getSublist(Lst<SV> listIn, Lst<SV> formatList) { 2156 Lst<SV> listOut = new Lst<SV>(); 2157 Map<String, SV> map; 2158 SV v; 2159 Lst<SV> list; 2160 for (int i = 0, n = listIn.size(); i < n; i++) { 2161 SV element = listIn.get(i); 2162 switch (element.tok) { 2163 case T.hash: 2164 map = element.getMap(); 2165 list = new Lst<SV>(); 2166 for (int j = 0, n1 = formatList.size(); j < n1; j++) { 2167 v = map.get(SV.sValue(formatList.get(j))); 2168 list.addLast(v == null ? SV.newS("") : v); 2169 } 2170 listOut.addLast(SV.getVariableList(list)); 2171 break; 2172 case T.varray: 2173 map = new Hashtable<String, SV>(); 2174 list = element.getList(); 2175 for (int j = 0, n1 = Math.min(list.size(), 2176 formatList.size()); j < n1; j++) { 2177 map.put(SV.sValue(formatList.get(j)), list.get(j)); 2178 } 2179 listOut.addLast(SV.getVariable(map)); 2180 } 2181 } 2182 return listOut; 2183 } 2184 evaluateList(ScriptMathProcessor mp, int tok, SV[] args)2185 private boolean evaluateList(ScriptMathProcessor mp, int tok, SV[] args) 2186 throws ScriptException { 2187 // array.add(x) 2188 // array.add(sep, x) 2189 // array.sub(x) 2190 // array.mul(x) 2191 // array.mul3(x) 2192 // array.div(x) 2193 // array.push() 2194 // array.pop() 2195 2196 // array.join() 2197 // array.join(sep) 2198 // array.join(sep,true) 2199 // array.join("",true) (CSV) 2200 2201 // array.split() 2202 // array.split(sep) 2203 // array.split("\t",true) 2204 // array.split("",true) (CSV) 2205 2206 int len = args.length; 2207 SV x1 = mp.getX(); 2208 boolean isArray1 = (x1.tok == T.varray); 2209 SV x2; 2210 switch (tok) { 2211 case T.push: 2212 return (len == 2 && mp.addX(x1.pushPop(args[0], args[1])) 2213 || len == 1 && mp.addX(x1.pushPop(null, args[0]))); 2214 case T.pop: 2215 return (len == 1 && mp.addX(x1.pushPop(args[0], null)) 2216 || len == 0 && mp.addX(x1.pushPop(null, null))); 2217 case T.add: 2218 if (len != 1 && len != 2) 2219 return false; 2220 break; 2221 case T.split: 2222 case T.join: 2223 break; 2224 default: 2225 if (len != 1) 2226 return false; 2227 } 2228 String[] sList1 = null, sList2 = null, sList3 = null; 2229 2230 if (len == 2) { 2231 // special add, join, split 2232 String tab = SV.sValue(args[0]); 2233 x2 = args[1]; 2234 if (tok == T.add) { 2235 // [...].add("\t", [...]) 2236 sList1 = (isArray1 ? SV.strListValue(x1) 2237 : PT.split(SV.sValue(x1), "\n")); 2238 sList2 = (x2.tok == T.varray ? SV.strListValue(x2) 2239 : PT.split(SV.sValue(x2), "\n")); 2240 sList3 = new String[len = Math.max(sList1.length, sList2.length)]; 2241 for (int i = 0; i < len; i++) 2242 sList3[i] = (i >= sList1.length ? "" : sList1[i]) + tab 2243 + (i >= sList2.length ? "" : sList2[i]); 2244 return mp.addXAS(sList3); 2245 } 2246 if (x2.tok != T.on) 2247 return false; // second parameter must be "true" for now. 2248 Lst<SV> l = x1.getList(); 2249 boolean isCSV = (tab.length() == 0); 2250 if (isCSV) 2251 tab = ","; 2252 if (tok == T.join) { 2253 // [...][...].join(sep, true) [2D-array line join] 2254 // [...][...].join("", true) [CSV join] 2255 SV[] s2 = new SV[l.size()]; 2256 for (int i = l.size(); --i >= 0;) { 2257 Lst<SV> a = l.get(i).getList(); 2258 if (a == null) 2259 s2[i] = l.get(i); 2260 else { 2261 SB sb = new SB(); 2262 for (int j = 0, n = a.size(); j < n; j++) { 2263 if (j > 0) 2264 sb.append(tab); 2265 SV sv = a.get(j); 2266 sb.append(isCSV && sv.tok == T.string 2267 ? "\"" + PT.rep((String) sv.value, "\"", "\"\"") + "\"" 2268 : "" + sv.asString()); 2269 } 2270 s2[i] = SV.newS(sb.toString()); 2271 } 2272 } 2273 return mp.addXAV(s2); 2274 } 2275 // [...].split(sep, true) [split individual elements as strings] 2276 // [...].split("", true) [CSV split] 2277 Lst<SV> sa = new Lst<SV>(); 2278 if (isCSV) 2279 tab = "\0"; 2280 int[] next = new int[2]; 2281 for (int i = 0, nl = l.size(); i < nl; i++) { 2282 String line = l.get(i).asString(); 2283 if (isCSV) { 2284 next[1] = 0; 2285 next[0] = 0; 2286 int last = 0; 2287 while (true) { 2288 String s = PT.getCSVString(line, next); 2289 if (s == null) { 2290 if (next[1] == -1) { 2291 // unmatched -- continue with next line if present 2292 // or just close quotes gracefully 2293 line += (++i < nl ? "\n" + l.get(i).asString() : "\""); 2294 next[1] = last; 2295 continue; 2296 } 2297 line = line.substring(0, last) 2298 + line.substring(last).replace(',', '\0'); 2299 break; 2300 } 2301 line = line.substring(0, last) 2302 + line.substring(last, next[0]).replace(',', '\0') + s 2303 + line.substring(next[1]); 2304 next[1] = last = next[0] + s.length(); 2305 } 2306 } 2307 String[] linaa = line.split(tab); 2308 Lst<SV> la = new Lst<SV>(); 2309 for (int j = 0, n = linaa.length; j < n; j++) { 2310 String s = linaa[j]; 2311 if (s.indexOf(".") < 0) 2312 try { 2313 la.addLast(SV.newI(Integer.parseInt(s))); 2314 continue; 2315 } catch (Exception e) { 2316 } 2317 else 2318 try { 2319 la.addLast(SV.getVariable(Float.valueOf(Float.parseFloat(s)))); 2320 continue; 2321 } catch (Exception ee) { 2322 } 2323 la.addLast(SV.newS(s)); 2324 } 2325 sa.addLast(SV.getVariableList(la)); 2326 } 2327 return mp.addXObj(SV.getVariableList(sa)); 2328 } 2329 x2 = (len == 0 ? SV.newV(T.all, "all") : args[0]); 2330 boolean isAll = (x2.tok == T.all); 2331 if (!isArray1 && x1.tok != T.string) 2332 return mp.binaryOp(opTokenFor(tok), x1, x2); 2333 boolean isScalar1 = SV.isScalar(x1); 2334 boolean isScalar2 = SV.isScalar(x2); 2335 2336 float[] list1 = null; 2337 float[] list2 = null; 2338 Lst<SV> alist1 = x1.getList(); 2339 Lst<SV> alist2 = x2.getList(); 2340 2341 if (isArray1) { 2342 len = alist1.size(); 2343 } else if (isScalar1) { 2344 len = Integer.MAX_VALUE; 2345 } else { 2346 sList1 = (PT.split(SV.sValue(x1), "\n")); 2347 list1 = new float[len = sList1.length]; 2348 PT.parseFloatArrayData(sList1, list1); 2349 } 2350 if (isAll && tok != T.join) { 2351 float sum = 0f; 2352 if (isArray1) { 2353 for (int i = len; --i >= 0;) 2354 sum += SV.fValue(alist1.get(i)); 2355 } else if (!isScalar1) { 2356 for (int i = len; --i >= 0;) 2357 sum += list1[i]; 2358 } 2359 return mp.addXFloat(sum); 2360 } 2361 if (tok == T.join && x2.tok == T.string) { 2362 SB sb = new SB(); 2363 if (isScalar1) { 2364 sb.append(SV.sValue(x1)); 2365 } else { 2366 String s = (isAll ? "" : x2.value.toString()); 2367 for (int i = 0; i < len; i++) 2368 sb.append(i > 0 ? s : "").append(SV.sValue(alist1.get(i))); 2369 } 2370 return mp.addXStr(sb.toString()); 2371 } 2372 2373 SV scalar = null; 2374 if (isScalar2) { 2375 scalar = x2; 2376 } else if (x2.tok == T.varray) { 2377 len = Math.min(len, alist2.size()); 2378 } else { 2379 sList2 = PT.split(SV.sValue(x2), "\n"); 2380 list2 = new float[sList2.length]; 2381 PT.parseFloatArrayData(sList2, list2); 2382 len = Math.min(len, list2.length); 2383 } 2384 2385 T token = opTokenFor(tok); 2386 2387 if (isArray1 && isAll) { 2388 Lst<SV> llist = new Lst<SV>(); 2389 return mp.addXList(addAllLists(x1.getList(), llist)); 2390 } 2391 SV a = (isScalar1 ? x1 : null); 2392 SV b; 2393 boolean justVal = (len == Integer.MAX_VALUE); 2394 if (justVal) 2395 len = 1; 2396 SV[] olist = new SV[len]; 2397 for (int i = 0; i < len; i++) { 2398 if (isScalar2) 2399 b = scalar; 2400 else if (x2.tok == T.varray) 2401 b = alist2.get(i); 2402 else if (Float.isNaN(list2[i])) 2403 b = SV.getVariable(SV.unescapePointOrBitsetAsVariable(sList2[i])); 2404 else 2405 b = SV.newF(list2[i]); 2406 if (!isScalar1) { 2407 if (isArray1) 2408 a = alist1.get(i); 2409 else if (Float.isNaN(list1[i])) 2410 a = SV.getVariable(SV.unescapePointOrBitsetAsVariable(sList1[i])); 2411 else 2412 a = SV.newF(list1[i]); 2413 } 2414 if (tok == T.join) { 2415 if (a.tok != T.varray) { 2416 Lst<SV> l = new Lst<SV>(); 2417 l.addLast(a); 2418 a = SV.getVariableList(l); 2419 } 2420 } 2421 if (!mp.binaryOp(token, a, b)) 2422 return false; 2423 olist[i] = mp.getX(); 2424 } 2425 return (justVal ? mp.addXObj(olist[0]) : mp.addXAV(olist)); 2426 } 2427 addAllLists(Lst<SV> list, Lst<SV> l)2428 private Lst<SV> addAllLists(Lst<SV> list, Lst<SV> l) { 2429 int n = list.size(); 2430 for (int i = 0; i < n; i++) { 2431 SV v = list.get(i); 2432 if (v.tok == T.varray) 2433 addAllLists(v.getList(), l); 2434 else 2435 l.addLast(v); 2436 } 2437 return l; 2438 } 2439 evaluateLoad(ScriptMathProcessor mp, SV[] args, boolean isFile)2440 private boolean evaluateLoad(ScriptMathProcessor mp, SV[] args, 2441 boolean isFile) 2442 throws ScriptException { 2443 // file("myfile.xyz") 2444 // load("myfile.png",true) 2445 // load("myfile.txt",1000) 2446 // load("myfile.xyz",0,true) 2447 // load("myfile.json","JSON") 2448 // load("myfile.json","JSON", true) 2449 2450 if (args.length < 1 || args.length > 3) 2451 return false; 2452 String file = FileManager.fixDOSName(SV.sValue(args[0])); 2453 boolean asBytes = (args.length > 1 && args[1].tok == T.on); 2454 boolean async = (vwr.async 2455 || args.length > 2 && args[args.length - 1].tok == T.on); 2456 int nBytesMax = (args.length > 1 && args[1].tok == T.integer 2457 ? args[1].asInt() 2458 : -1); 2459 boolean asJSON = (args.length > 1 2460 && args[1].asString().equalsIgnoreCase("JSON")); 2461 if (asBytes) 2462 return mp.addXMap(vwr.fm.getFileAsMap(file, null)); 2463 boolean isQues = file.startsWith("?"); 2464 if (Viewer.isJS && (isQues || async)) { 2465 if (isFile && isQues) 2466 return mp.addXStr(""); 2467 file = e.loadFileAsync("load()_", file, mp.oPt, true); 2468 // A ScriptInterrupt will be thrown, and an asynchronous 2469 // file load will initiate, which will return to the script 2470 // at this command when the load operation has completed. 2471 // Note that we need to have just a simple command here. 2472 // The evaluation will be repeated up to this point, so for example, 2473 // x = (i++) + load("?") would increment i twice. 2474 } 2475 String str = isFile ? vwr.fm.getFilePath(file, false, false) 2476 : vwr.getFileAsString4(file, nBytesMax, false, false, true, "script"); 2477 try { 2478 return (asJSON ? mp.addXObj(vwr.parseJSON(str)) : mp.addXStr(str)); 2479 } catch (Exception e) { 2480 return false; 2481 } 2482 } 2483 evaluateMath(ScriptMathProcessor mp, SV[] args, int tok)2484 private boolean evaluateMath(ScriptMathProcessor mp, SV[] args, int tok) { 2485 double x = SV.fValue(args[0]); 2486 switch (tok) { 2487 case T.sqrt: 2488 x = Math.sqrt(x); 2489 break; 2490 case T.sin: 2491 x = Math.sin(x * Math.PI / 180); 2492 break; 2493 case T.cos: 2494 x = Math.cos(x * Math.PI / 180); 2495 break; 2496 case T.acos: 2497 x = Math.acos(x) * 180 / Math.PI; 2498 break; 2499 } 2500 return mp.addXFloat((float) x); 2501 } 2502 2503 // private boolean evaluateVolume(ScriptVariable[] args) throws ScriptException { 2504 // ScriptVariable x1 = mp.getX(); 2505 // if (x1.tok != Token.bitset) 2506 // return false; 2507 // String type = (args.length == 0 ? null : ScriptVariable.sValue(args[0])); 2508 // return mp.addX(vwr.getVolume((BitSet) x1.value, type)); 2509 // } 2510 evaluateMeasure(ScriptMathProcessor mp, SV[] args, int tok)2511 private boolean evaluateMeasure(ScriptMathProcessor mp, SV[] args, int tok) 2512 throws ScriptException { 2513 int nPoints = 0; 2514 switch (tok) { 2515 case T.measure: 2516 // note: min/max are always in Angstroms 2517 // note: order is not important (other than min/max) 2518 // measure({a},{b},{c},{d}, min, max, property, format, units) 2519 // measure({a},{b},{c}, min, max, property, format, units) 2520 // measure({a},{b}, min, max, property, format, units) 2521 // measure({a},{b},{c},{d}, min, max, property, format, units) 2522 // measure({a} {b} "minArray") -- returns array of minimum distance values 2523 String property = null; 2524 Lst<Object> points = new Lst<Object>(); 2525 float[] rangeMinMax = new float[] { Float.MAX_VALUE, Float.MAX_VALUE }; 2526 String strFormat = null; 2527 String units = null; 2528 boolean isAllConnected = false; 2529 boolean isNotConnected = false; 2530 int rPt = 0; 2531 boolean isNull = false; 2532 RadiusData rd = null; 2533 int nBitSets = 0; 2534 float vdw = Float.MAX_VALUE; 2535 boolean asMinArray = false; 2536 boolean asFloatArray = false; 2537 for (int i = 0; i < args.length; i++) { 2538 switch (args[i].tok) { 2539 case T.bitset: 2540 BS bs = (BS) args[i].value; 2541 if (bs.length() == 0) 2542 isNull = true; 2543 points.addLast(bs); 2544 nPoints++; 2545 nBitSets++; 2546 break; 2547 case T.point3f: 2548 Point3fi v = new Point3fi(); 2549 v.setT((P3) args[i].value); 2550 points.addLast(v); 2551 nPoints++; 2552 break; 2553 case T.integer: 2554 case T.decimal: 2555 rangeMinMax[rPt++ % 2] = SV.fValue(args[i]); 2556 break; 2557 2558 case T.string: 2559 String s = SV.sValue(args[i]); 2560 if (s.startsWith("property_")) { 2561 property = s; 2562 break; 2563 } 2564 if (s.equalsIgnoreCase("vdw") || s.equalsIgnoreCase("vanderwaals")) 2565 vdw = (i + 1 < args.length && args[i + 1].tok == T.integer 2566 ? args[++i].asInt() 2567 : 100) / 100f; 2568 else if (s.equalsIgnoreCase("notConnected")) 2569 isNotConnected = true; 2570 else if (s.equalsIgnoreCase("connected")) 2571 isAllConnected = true; 2572 else if (s.equalsIgnoreCase("minArray")) 2573 asMinArray = (nBitSets >= 1); 2574 else if (s.equalsIgnoreCase("asArray") || s.length() == 0) 2575 asFloatArray = (nBitSets >= 1); 2576 else if (Measurement.isUnits(s)) 2577 units = s.toLowerCase(); 2578 else 2579 strFormat = nPoints + ":" + s; 2580 break; 2581 default: 2582 return false; 2583 } 2584 } 2585 if (nPoints < 2 || nPoints > 4 || rPt > 2 2586 || isNotConnected && isAllConnected) 2587 return false; 2588 if (isNull) 2589 return mp.addXStr(""); 2590 if (vdw != Float.MAX_VALUE && (nBitSets != 2 || nPoints != 2)) 2591 return mp.addXStr(""); 2592 rd = (vdw == Float.MAX_VALUE ? new RadiusData(rangeMinMax, 0, null, null) 2593 : new RadiusData(null, vdw, EnumType.FACTOR, VDW.AUTO)); 2594 Object obj = (vwr.newMeasurementData(null, points)) 2595 .set(0, null, rd, property, strFormat, units, null, isAllConnected, 2596 isNotConnected, null, true, 0, (short) 0, null, Float.NaN) 2597 .getMeasurements(asFloatArray, asMinArray); 2598 return mp.addXObj(obj); 2599 case T.angle: 2600 if ((nPoints = args.length) != 3 && nPoints != 4) 2601 return false; 2602 break; 2603 default: // distance 2604 if ((nPoints = args.length) != 2) 2605 return false; 2606 } 2607 P3[] pts = new P3[nPoints]; 2608 for (int i = 0; i < nPoints; i++) { 2609 if ((pts[i] = mp.ptValue(args[i], null)) == null) 2610 return false; 2611 } 2612 switch (nPoints) { 2613 case 2: 2614 return mp.addXFloat(pts[0].distance(pts[1])); 2615 case 3: 2616 return mp 2617 .addXFloat(Measure.computeAngleABC(pts[0], pts[1], pts[2], true)); 2618 case 4: 2619 return mp.addXFloat( 2620 Measure.computeTorsion(pts[0], pts[1], pts[2], pts[3], true)); 2621 } 2622 return false; 2623 } 2624 evaluateModulation(ScriptMathProcessor mp, SV[] args)2625 private boolean evaluateModulation(ScriptMathProcessor mp, SV[] args) 2626 throws ScriptException { 2627 String type = ""; 2628 float t = Float.NaN; 2629 P3 t456 = null; 2630 switch (args.length) { 2631 case 0: 2632 break; 2633 case 1: 2634 switch (args[0].tok) { 2635 case T.point3f: 2636 t456 = (P3) args[0].value; 2637 break; 2638 case T.string: 2639 type = args[0].asString(); 2640 break; 2641 default: 2642 t = SV.fValue(args[0]); 2643 } 2644 break; 2645 case 2: 2646 type = SV.sValue(args[0]); 2647 t = SV.fValue(args[1]); 2648 break; 2649 default: 2650 return false; 2651 } 2652 if (t456 == null && t < 1e6) 2653 t456 = P3.new3(t, t, t); 2654 SV x = mp.getX(); 2655 BS bs = (x.tok == T.bitset ? (BS) x.value : new BS()); 2656 return mp.addXList(vwr.ms.getModulationList(bs, 2657 (type + "D").toUpperCase().charAt(0), t456)); 2658 } 2659 2660 /** 2661 * plane() or intersection() 2662 * 2663 * @param mp 2664 * @param args 2665 * @param tok 2666 * @return true 2667 * @throws ScriptException 2668 */ evaluatePlane(ScriptMathProcessor mp, SV[] args, int tok)2669 private boolean evaluatePlane(ScriptMathProcessor mp, SV[] args, int tok) 2670 throws ScriptException { 2671 if (tok == T.hkl && args.length != 3 || tok == T.intersection 2672 && args.length != 2 && args.length != 3 && args.length != 4 2673 || args.length == 0 || args.length > 4) 2674 return false; 2675 P3 pt1, pt2, pt3; 2676 P4 plane = ScriptMathProcessor.planeValue(args[0]); 2677 V3 norm, vTemp; 2678 switch (args.length) { 2679 case 1: 2680 if (args[0].tok == T.bitset) { 2681 BS bs = (BS) args[0].value; 2682 if (bs.cardinality() == 3) { 2683 Lst<P3> pts = vwr.ms.getAtomPointVector(bs); 2684 return mp.addXPt4(Measure.getPlaneThroughPoints(pts.get(0), 2685 pts.get(1), pts.get(2), new V3(), new V3(), new P4())); 2686 } 2687 } 2688 return (plane != null && mp.addXPt4(plane)); 2689 case 2: 2690 if (tok == T.intersection) { 2691 // intersection(plane, plane) 2692 // intersection(point, plane) 2693 P4 plane1 = ScriptMathProcessor.planeValue(args[1]); 2694 if (plane1 == null) 2695 return false; 2696 pt3 = new P3(); 2697 norm = new V3(); 2698 vTemp = new V3(); 2699 2700 if (plane != null) { 2701 Lst<Object> list = Measure.getIntersectionPP(plane, plane1); 2702 if (list == null) 2703 return mp.addXStr(""); 2704 return mp.addXList(list); 2705 } 2706 pt2 = mp.ptValue(args[0], null); 2707 if (pt2 == null) 2708 return mp.addXStr(""); 2709 return mp.addXPt( 2710 Measure.getIntersection(pt2, null, plane1, pt3, norm, vTemp)); 2711 } 2712 //$FALL-THROUGH$ 2713 case 3: 2714 case 4: 2715 switch (tok) { 2716 case T.hkl: 2717 // hkl(i,j,k) 2718 return mp.addXPt4(e.getHklPlane(P3.new3(SV.fValue(args[0]), 2719 SV.fValue(args[1]), SV.fValue(args[2])), Float.NaN, false)); 2720 case T.intersection: 2721 pt1 = mp.ptValue(args[0], null); 2722 pt2 = mp.ptValue(args[1], null); 2723 if (pt1 == null || pt2 == null) 2724 return mp.addXStr(""); 2725 V3 vLine = V3.newV(pt2); 2726 vLine.normalize(); 2727 P4 plane2 = ScriptMathProcessor.planeValue(args[2]); 2728 if (plane2 != null) { 2729 // intersection(ptLine, vLine, plane) 2730 pt3 = new P3(); 2731 norm = new V3(); 2732 vTemp = new V3(); 2733 pt1 = Measure.getIntersection(pt1, vLine, plane2, pt3, norm, vTemp); 2734 if (pt1 == null) 2735 return mp.addXStr(""); 2736 return mp.addXPt(pt1); 2737 } 2738 pt3 = mp.ptValue(args[2], null); 2739 if (pt3 == null) 2740 return mp.addXStr(""); 2741 // intersection(ptLine, vLine, ptCenter, radius) 2742 // intersection(ptLine, vLine, pt2); 2743 // IE intersection of plane perp to line through pt2 2744 V3 v = new V3(); 2745 pt3 = P3.newP(pt3); 2746 if (args.length == 3) { 2747 // intersection(ptLine, vLine, pt2); 2748 // IE intersection of plane perp to line through pt2 2749 Measure.projectOntoAxis(pt3, pt1, vLine, v); 2750 return mp.addXPt(pt3); 2751 } 2752 // intersection(ptLine, vLine, ptCenter, radius) 2753 // IE intersection of a line with a sphere -- return list of 0, 1, or 2 points 2754 float r = SV.fValue(args[3]); 2755 P3 ptCenter = P3.newP(pt3); 2756 Measure.projectOntoAxis(pt3, pt1, vLine, v); 2757 float d = ptCenter.distance(pt3); 2758 Lst<P3> l = new Lst<P3>(); 2759 if (d == r) { 2760 l.addLast(pt3); 2761 } else if (d < r) { 2762 d = (float) Math.sqrt(r * r - d * d); 2763 v.scaleAdd2(d, vLine, pt3); 2764 l.addLast(P3.newP(v)); 2765 v.scaleAdd2(-d, vLine, pt3); 2766 l.addLast(P3.newP(v)); 2767 } 2768 return mp.addXList(l); 2769 } 2770 switch (args[0].tok) { 2771 case T.integer: 2772 case T.decimal: 2773 if (args.length == 3) { 2774 // plane(r theta phi) 2775 float r = SV.fValue(args[0]); 2776 float theta = SV.fValue(args[1]); // longitude, azimuthal, in xy plane 2777 float phi = SV.fValue(args[2]); // 90 - latitude, polar, from z 2778 // rotate {0 0 r} about y axis need to stay in the x-z plane 2779 norm = V3.new3(0, 0, 1); 2780 pt2 = P3.new3(0, 1, 0); 2781 Quat q = Quat.newVA(pt2, phi); 2782 q.getMatrix().rotate(norm); 2783 // rotate that vector around z 2784 pt2.set(0, 0, 1); 2785 q = Quat.newVA(pt2, theta); 2786 q.getMatrix().rotate(norm); 2787 pt2.setT(norm); 2788 pt2.scale(r); 2789 plane = new P4(); 2790 Measure.getPlaneThroughPoint(pt2, norm, plane); 2791 return mp.addXPt4(plane); 2792 } 2793 break; 2794 case T.bitset: 2795 case T.point3f: 2796 pt1 = mp.ptValue(args[0], null); 2797 pt2 = mp.ptValue(args[1], null); 2798 if (pt2 == null) 2799 return false; 2800 pt3 = (args.length > 2 2801 && (args[2].tok == T.bitset || args[2].tok == T.point3f) 2802 ? mp.ptValue(args[2], null) 2803 : null); 2804 norm = V3.newV(pt2); 2805 if (pt3 == null) { 2806 plane = new P4(); 2807 if (args.length == 2 || args[2].tok != T.integer 2808 && args[2].tok != T.decimal && !args[2].asBoolean()) { 2809 // plane(<point1>,<point2>) or 2810 // plane(<point1>,<point2>,false) 2811 pt3 = P3.newP(pt1); 2812 pt3.add(pt2); 2813 pt3.scale(0.5f); 2814 norm.sub(pt1); 2815 norm.normalize(); 2816 } else if (args[2].tok == T.on) { 2817 // plane(<point1>,<vLine>,true) 2818 pt3 = pt1; 2819 } else { 2820 // plane(<point1>,<point2>,f) 2821 norm.sub(pt1); 2822 pt3 = new P3(); 2823 pt3.scaleAdd2(args[2].asFloat(), norm, pt1); 2824 } 2825 Measure.getPlaneThroughPoint(pt3, norm, plane); 2826 return mp.addXPt4(plane); 2827 } 2828 // plane(<point1>,<point2>,<point3>) 2829 // plane(<point1>,<point2>,<point3>,<pointref>) 2830 V3 vAB = new V3(); 2831 P3 ptref = (args.length == 4 ? mp.ptValue(args[3], null) : null); 2832 float nd = Measure.getDirectedNormalThroughPoints(pt1, pt2, pt3, ptref, 2833 norm, vAB); 2834 return mp.addXPt4(P4.new4(norm.x, norm.y, norm.z, nd)); 2835 } 2836 } 2837 if (args.length != 4) 2838 return false; 2839 float x = SV.fValue(args[0]); 2840 float y = SV.fValue(args[1]); 2841 float z = SV.fValue(args[2]); 2842 float w = SV.fValue(args[3]); 2843 return mp.addXPt4(P4.new4(x, y, z, w)); 2844 } 2845 evaluatePoint(ScriptMathProcessor mp, SV[] args)2846 private boolean evaluatePoint(ScriptMathProcessor mp, SV[] args) { 2847 // point(1.3) // rounds toward 0 2848 // point(pt, true) // to screen coord 2849 // point(pt, false) // from screen coord 2850 // point(x, y, z) 2851 // point(x, y, z, w) 2852 // point(["{1,2,3", "{2,3,4}"]) 2853 2854 switch (args.length) { 2855 default: 2856 return false; 2857 case 1: 2858 if (args[0].tok == T.decimal || args[0].tok == T.integer) 2859 return mp.addXInt(args[0].asInt()); 2860 String s = null; 2861 if (args[0].tok == T.varray) { 2862 Lst<SV> list = args[0].getList(); 2863 int len = list.size(); 2864 if (len == 0) { 2865 return false; 2866 } 2867 switch (list.get(0).tok) { 2868 case T.integer: 2869 case T.decimal: 2870 break; 2871 case T.string: 2872 s = (String) list.get(0).value; 2873 if (!s.startsWith("{") || Escape.uP(s) instanceof String) { 2874 s = null; 2875 break; 2876 } 2877 Lst<SV> a = new Lst<SV>(); 2878 for (int i = 0; i < len; i++) { 2879 a.addLast(SV.getVariable(Escape.uP(SV.sValue(list.get(i))))); 2880 } 2881 return mp.addXList(a); 2882 } 2883 s = "{" + SV.sValue(args[0]) + "}"; 2884 } 2885 if (s == null) 2886 s = SV.sValue(args[0]); 2887 Object pt = Escape.uP(s); 2888 return (pt instanceof P3 ? mp.addXPt((P3) pt) : mp.addXStr("" + pt)); 2889 case 2: 2890 P3 pt3; 2891 switch (args[1].tok) { 2892 case T.off: 2893 case T.on: 2894 // to/from screen coordinates 2895 switch (args[0].tok) { 2896 case T.point3f: 2897 pt3 = P3.newP((T3) args[0].value); 2898 break; 2899 case T.bitset: 2900 pt3 = vwr.ms.getAtomSetCenter((BS) args[0].value); 2901 break; 2902 default: 2903 return false; 2904 } 2905 if (args[1].tok == T.on) { 2906 // this is TO screen coordinates, 0 at bottom left 2907 vwr.tm.transformPt3f(pt3, pt3); 2908 pt3.y = vwr.tm.height - pt3.y; 2909 if (vwr.antialiased) 2910 pt3.scale(0.5f); 2911 } else { 2912 // this is FROM screen coordinates 2913 if (vwr.antialiased) 2914 pt3.scale(2f); 2915 pt3.y = vwr.tm.height - pt3.y; 2916 vwr.tm.unTransformPoint(pt3, pt3); 2917 } 2918 break; 2919 case T.point3f: 2920 // unitcell transform 2921 Lst<SV> sv = args[0].getList(); 2922 if (sv == null || sv.size() != 4) 2923 return false; 2924 P3 pt1 = SV.ptValue(args[1]); 2925 pt3 = P3.newP(SV.ptValue(sv.get(0))); 2926 pt3.scaleAdd2(pt1.x, SV.ptValue(sv.get(1)), pt3); 2927 pt3.scaleAdd2(pt1.y, SV.ptValue(sv.get(2)), pt3); 2928 pt3.scaleAdd2(pt1.z, SV.ptValue(sv.get(3)), pt3); 2929 break; 2930 default: 2931 return false; 2932 } 2933 return mp.addXPt(pt3); 2934 case 3: 2935 return mp.addXPt( 2936 P3.new3(args[0].asFloat(), args[1].asFloat(), args[2].asFloat())); 2937 case 4: 2938 return mp.addXPt4(P4.new4(args[0].asFloat(), args[1].asFloat(), 2939 args[2].asFloat(), args[3].asFloat())); 2940 } 2941 } 2942 evaluatePrompt(ScriptMathProcessor mp, SV[] args)2943 private boolean evaluatePrompt(ScriptMathProcessor mp, SV[] args) { 2944 //x = prompt("testing") 2945 //x = prompt("testing","defaultInput") 2946 //x = prompt("testing","yes|no|cancel", true) 2947 //x = prompt("testing",["button1", "button2", "button3"]) 2948 2949 if (args.length != 1 && args.length != 2 && args.length != 3) 2950 return false; 2951 String label = SV.sValue(args[0]); 2952 String[] buttonArray = (args.length > 1 && args[1].tok == T.varray 2953 ? SV.strListValue(args[1]) 2954 : null); 2955 boolean asButtons = (buttonArray != null || args.length == 1 2956 || args.length == 3 && args[2].asBoolean()); 2957 String input = (buttonArray != null ? null 2958 : args.length >= 2 ? SV.sValue(args[1]) : "OK"); 2959 String s = "" + vwr.prompt(label, input, buttonArray, asButtons); 2960 return (asButtons && buttonArray != null 2961 ? mp.addXInt(Integer.parseInt(s) + 1) 2962 : mp.addXStr(s)); 2963 } 2964 evaluateQuaternion(ScriptMathProcessor mp, SV[] args, int tok)2965 private boolean evaluateQuaternion(ScriptMathProcessor mp, SV[] args, int tok) 2966 throws ScriptException { 2967 P3 pt0 = null; 2968 // quaternion([quaternion array]) // mean 2969 // quaternion([quaternion array1], [quaternion array2], "relative") // 2970 // difference array 2971 // quaternion(matrix) 2972 // quaternion({atom1}) // quaternion (1st if array) 2973 // quaternion({atomSet}, nMax) // nMax quaternions, by group; 0 for all 2974 // quaternion({atom1}, {atom2}) // difference 2975 // quaternion({atomSet1}, {atomset2}, nMax) // difference array, by group; 0 for all 2976 // quaternion(vector, theta) 2977 // quaternion(q0, q1, q2, q3) 2978 // quaternion("{x, y, z, w"}) 2979 // quaternion("best") 2980 // quaternion(center, X, XY) 2981 // quaternion(mcol1, mcol2) 2982 // quaternion(q, "id", center) // draw code 2983 // axisangle(vector, theta) 2984 // axisangle(x, y, z, theta) 2985 // axisangle("{x, y, z, theta"}) 2986 int nArgs = args.length; 2987 int nMax = Integer.MAX_VALUE; 2988 boolean isRelative = false; 2989 if (tok == T.quaternion) { 2990 if (nArgs > 1 && args[nArgs - 1].tok == T.string 2991 && ((String) args[nArgs - 1].value).equalsIgnoreCase("relative")) { 2992 nArgs--; 2993 isRelative = true; 2994 } 2995 if (nArgs > 1 && args[nArgs - 1].tok == T.integer 2996 && args[0].tok == T.bitset) { 2997 nMax = args[nArgs - 1].asInt(); 2998 if (nMax <= 0) 2999 nMax = Integer.MAX_VALUE - 1; 3000 nArgs--; 3001 } 3002 } 3003 3004 switch (nArgs) { 3005 case 0: 3006 case 1: 3007 case 4: 3008 break; 3009 case 2: 3010 if (tok == T.quaternion) { 3011 if (args[0].tok == T.varray 3012 && (args[1].tok == T.varray || args[1].tok == T.on)) 3013 break; 3014 if (args[0].tok == T.bitset 3015 && (args[1].tok == T.integer || args[1].tok == T.bitset)) 3016 break; 3017 } 3018 if ((pt0 = mp.ptValue(args[0], null)) == null 3019 || tok != T.quaternion && args[1].tok == T.point3f) 3020 return false; 3021 break; 3022 case 3: 3023 if (tok != T.quaternion) 3024 return false; 3025 if (args[0].tok == T.point4f) { 3026 if (args[2].tok != T.point3f && args[2].tok != T.bitset) 3027 return false; 3028 break; 3029 } 3030 for (int i = 0; i < 3; i++) 3031 if (args[i].tok != T.point3f && args[i].tok != T.bitset) 3032 return false; 3033 break; 3034 default: 3035 return false; 3036 } 3037 Quat q = null; 3038 Quat[] qs = null; 3039 P4 p4 = null; 3040 switch (nArgs) { 3041 case 0: 3042 return mp.addXPt4(vwr.tm.getRotationQ().toPoint4f()); 3043 case 1: 3044 default: 3045 if (tok == T.quaternion && args[0].tok == T.varray) { 3046 Quat[] data1 = e.getQuaternionArray(args[0].getList(), T.list); 3047 Object mean = Quat.sphereMean(data1, null, 0.0001f); 3048 q = (mean instanceof Quat ? (Quat) mean : null); 3049 break; 3050 } else if (tok == T.quaternion && args[0].tok == T.bitset) { 3051 qs = vwr.getAtomGroupQuaternions((BS) args[0].value, nMax); 3052 } else if (args[0].tok == T.matrix3f) { 3053 q = Quat.newM((M3) args[0].value); 3054 } else if (args[0].tok == T.point4f) { 3055 p4 = (P4) args[0].value; 3056 } else { 3057 String s = SV.sValue(args[0]); 3058 Object v = Escape.uP(s.equalsIgnoreCase("best") 3059 ? vwr.getOrientationText(T.best, "best", null).toString() 3060 : s); 3061 if (!(v instanceof P4)) 3062 return false; 3063 p4 = (P4) v; 3064 } 3065 if (tok == T.axisangle) 3066 q = Quat.newVA(P3.new3(p4.x, p4.y, p4.z), p4.w); 3067 break; 3068 case 2: 3069 if (tok == T.quaternion) { 3070 if (args[0].tok == T.varray && args[1].tok == T.varray) { 3071 Quat[] data1 = e.getQuaternionArray(args[0].getList(), T.list); 3072 Quat[] data2 = e.getQuaternionArray(args[1].getList(), T.list); 3073 qs = Quat.div(data2, data1, nMax, isRelative); 3074 break; 3075 } 3076 if (args[0].tok == T.varray && args[1].tok == T.on) { 3077 Quat[] data1 = e.getQuaternionArray(args[0].getList(), T.list); 3078 float[] stddev = new float[1]; 3079 Quat.sphereMean(data1, stddev, 0.0001f); 3080 return mp.addXFloat(stddev[0]); 3081 } 3082 if (args[0].tok == T.bitset && args[1].tok == T.bitset) { 3083 Quat[] data1 = vwr.getAtomGroupQuaternions((BS) args[0].value, 3084 Integer.MAX_VALUE); 3085 Quat[] data2 = vwr.getAtomGroupQuaternions((BS) args[1].value, 3086 Integer.MAX_VALUE); 3087 qs = Quat.div(data2, data1, nMax, isRelative); 3088 break; 3089 } 3090 } 3091 P3 pt1 = mp.ptValue(args[1], null); 3092 p4 = ScriptMathProcessor.planeValue(args[0]); 3093 if (pt1 != null) 3094 q = Quat.getQuaternionFrame(P3.new3(0, 0, 0), pt0, pt1); 3095 else 3096 q = Quat.newVA(pt0, SV.fValue(args[1])); 3097 break; 3098 case 3: 3099 if (args[0].tok == T.point4f) { 3100 P3 pt = (args[2].tok == T.point3f ? (P3) args[2].value 3101 : vwr.ms.getAtomSetCenter((BS) args[2].value)); 3102 return mp.addXStr(Escape.drawQuat(Quat.newP4((P4) args[0].value), "q", 3103 SV.sValue(args[1]), pt, 1f)); 3104 } 3105 P3[] pts = new P3[3]; 3106 for (int i = 0; i < 3; i++) 3107 pts[i] = (args[i].tok == T.point3f ? (P3) args[i].value 3108 : vwr.ms.getAtomSetCenter((BS) args[i].value)); 3109 q = Quat.getQuaternionFrame(pts[0], pts[1], pts[2]); 3110 break; 3111 case 4: 3112 if (tok == T.quaternion) 3113 p4 = P4.new4(SV.fValue(args[1]), SV.fValue(args[2]), SV.fValue(args[3]), 3114 SV.fValue(args[0])); 3115 else 3116 q = Quat.newVA( 3117 P3.new3(SV.fValue(args[0]), SV.fValue(args[1]), SV.fValue(args[2])), 3118 SV.fValue(args[3])); 3119 break; 3120 } 3121 if (qs != null) { 3122 if (nMax != Integer.MAX_VALUE) { 3123 Lst<P4> list = new Lst<P4>(); 3124 for (int i = 0; i < qs.length; i++) 3125 list.addLast(qs[i].toPoint4f()); 3126 return mp.addXList(list); 3127 } 3128 q = (qs.length > 0 ? qs[0] : null); 3129 } 3130 return mp.addXPt4((q == null ? Quat.newP4(p4) : q).toPoint4f()); 3131 } 3132 3133 private Random rand; 3134 evaluateRandom(ScriptMathProcessor mp, SV[] args)3135 private boolean evaluateRandom(ScriptMathProcessor mp, SV[] args) { 3136 if (args.length > 3) 3137 return false; 3138 if (rand == null) 3139 rand = new Random(); 3140 float lower = 0, upper = 1; 3141 switch (args.length) { 3142 case 3: 3143 rand.setSeed((int) SV.fValue(args[2])); 3144 //$FALL-THROUGH$ 3145 case 2: 3146 upper = SV.fValue(args[1]); 3147 //$FALL-THROUGH$ 3148 case 1: 3149 lower = SV.fValue(args[0]); 3150 //$FALL-THROUGH$ 3151 case 0: 3152 break; 3153 default: 3154 return false; 3155 } 3156 return mp.addXFloat((rand.nextFloat() * (upper - lower)) + lower); 3157 } 3158 evaluateRowCol(ScriptMathProcessor mp, SV[] args, int tok)3159 private boolean evaluateRowCol(ScriptMathProcessor mp, SV[] args, int tok) 3160 throws ScriptException { 3161 if (args.length != 1) 3162 return false; 3163 int n = args[0].asInt() - 1; 3164 SV x1 = mp.getX(); 3165 float[] f; 3166 switch (x1.tok) { 3167 case T.matrix3f: 3168 if (n < 0 || n > 2) 3169 return false; 3170 M3 m = (M3) x1.value; 3171 switch (tok) { 3172 case T.row: 3173 f = new float[3]; 3174 m.getRow(n, f); 3175 return mp.addXAF(f); 3176 case T.col: 3177 default: 3178 f = new float[3]; 3179 m.getColumn(n, f); 3180 return mp.addXAF(f); 3181 } 3182 case T.matrix4f: 3183 if (n < 0 || n > 2) 3184 return false; 3185 M4 m4 = (M4) x1.value; 3186 switch (tok) { 3187 case T.row: 3188 f = new float[4]; 3189 m4.getRow(n, f); 3190 return mp.addXAF(f); 3191 case T.col: 3192 default: 3193 f = new float[4]; 3194 m4.getColumn(n, f); 3195 return mp.addXAF(f); 3196 } 3197 case T.varray: 3198 // column of a[][] 3199 Lst<SV> l1 = x1.getList(); 3200 Lst<SV> l2 = new Lst<SV>(); 3201 for (int i = 0, len = l1.size(); i < len; i++) { 3202 Lst<SV> l3 = l1.get(i).getList(); 3203 if (l3 == null) 3204 return mp.addXStr(""); 3205 l2.addLast(n < l3.size() ? l3.get(n) : SV.newS("")); 3206 } 3207 return mp.addXList(l2); 3208 } 3209 return false; 3210 3211 } 3212 3213 private boolean evaluateIn(ScriptMathProcessor mp, SV[] args) 3214 throws ScriptException { 3215 SV x1 = mp.getX(); 3216 switch (args.length) { 3217 case 1: 3218 Lst<SV> lst = args[0].getList(); 3219 if (lst != null) 3220 for (int i = 0, n = lst.size(); i < n; i++) 3221 if (SV.areEqual(x1, lst.get(i))) 3222 return mp.addXInt(i + 1); 3223 break; 3224 default: 3225 for (int i = 0; i < args.length; i++) 3226 if (SV.areEqual(x1, args[i])) 3227 return mp.addXInt(i + 1); 3228 break; 3229 } 3230 return mp.addXInt(0); 3231 } 3232 3233 private boolean evaluateReplace(ScriptMathProcessor mp, SV[] args) 3234 throws ScriptException { 3235 boolean isAll = false; 3236 String sFind, sReplace; 3237 switch (args.length) { 3238 case 0: 3239 isAll = true; 3240 sFind = sReplace = null; 3241 break; 3242 case 3: 3243 isAll = SV.bValue(args[2]); 3244 //$FALL-THROUGH$ 3245 case 2: 3246 sFind = SV.sValue(args[0]); 3247 sReplace = SV.sValue(args[1]); 3248 break; 3249 default: 3250 return false; 3251 } 3252 SV x = mp.getX(); 3253 if (x.tok == T.varray) { 3254 String[] list = SV.strListValue(x); 3255 String[] l = new String[list.length]; 3256 for (int i = list.length; --i >= 0;) 3257 l[i] = (sFind == null ? PT.clean(list[i]) 3258 : isAll ? PT.replaceAllCharacters(list[i], sFind, sReplace) 3259 : PT.rep(list[i], sFind, sReplace)); 3260 return mp.addXAS(l); 3261 } 3262 String s = SV.sValue(x); 3263 return mp.addXStr(sFind == null ? PT.clean(s) 3264 : isAll ? PT.replaceAllCharacters(s, sFind, sReplace) 3265 : PT.rep(s, sFind, sReplace)); 3266 } 3267 3268 private boolean evaluateScript(ScriptMathProcessor mp, SV[] args, int tok) 3269 throws ScriptException { 3270 // eval(cmd) 3271 // eval("JSON",json) 3272 // javascript(cmd) 3273 // script(cmd) 3274 // script(cmd, syncTarget) 3275 // show(showCmd) 3276 if ((tok == T.show || tok == T.javascript) && args.length != 1 3277 || args.length == 0) 3278 return false; 3279 String s = SV.sValue(args[0]); 3280 SB sb = new SB(); 3281 switch (tok) { 3282 case T.eval: 3283 return (args.length == 2 3284 ? s.equalsIgnoreCase("JSON") 3285 && mp.addXObj(vwr.parseJSON(SV.sValue(args[1]))) 3286 : mp.addXObj(vwr.evaluateExpressionAsVariable(s))); 3287 case T.script: 3288 String appID = (args.length == 2 ? SV.sValue(args[1]) : "."); 3289 // options include * > . or an appletID with or without "jmolApplet" 3290 if (!appID.equals(".")) 3291 sb.append(vwr.jsEval(appID + "\1" + s)); 3292 if (appID.equals(".") || appID.equals("*")) 3293 e.runScriptBuffer(s, sb, true); 3294 break; 3295 case T.show: 3296 e.runScriptBuffer("show " + s, sb, true); 3297 break; 3298 case T.javascript: 3299 return mp.addX(vwr.jsEvalSV(s)); 3300 } 3301 s = sb.toString(); 3302 float f; 3303 return (Float.isNaN(f = PT.parseFloatStrict(s)) ? mp.addXStr(s) 3304 : s.indexOf(".") >= 0 ? mp.addXFloat(f) : mp.addXInt(PT.parseInt(s))); 3305 } 3306 3307 /** 3308 * sort() or sort(n) or count() or count("xxxx") 3309 * 3310 * @param mp 3311 * @param args 3312 * @param tok 3313 * @return true if no error 3314 * @throws ScriptException 3315 */ 3316 private boolean evaluateSort(ScriptMathProcessor mp, SV[] args, int tok) 3317 throws ScriptException { 3318 if (args.length > 1) 3319 return false; 3320 if (tok == T.sort) { 3321 if (args.length == 1 && args[0].tok == T.string) { 3322 return mp.addX(mp.getX().sortMapArray(args[0].asString())); 3323 } 3324 int n = (args.length == 0 ? 0 : args[0].asInt()); 3325 return mp.addX(mp.getX().sortOrReverse(n)); 3326 } 3327 SV x = mp.getX(); 3328 SV match = (args.length == 0 ? null : args[0]); 3329 if (x.tok == T.string) { 3330 int n = 0; 3331 String s = SV.sValue(x); 3332 if (match == null) 3333 return mp.addXInt(0); 3334 String m = SV.sValue(match); 3335 for (int i = 0; i < s.length(); i++) { 3336 int pt = s.indexOf(m, i); 3337 if (pt < 0) 3338 break; 3339 n++; 3340 i = pt; 3341 } 3342 return mp.addXInt(n); 3343 } 3344 Lst<SV> counts = new Lst<SV>(); 3345 SV last = null; 3346 SV count = null; 3347 Lst<SV> xList = SV.getVariable(x.value).sortOrReverse(0).getList(); 3348 if (xList == null) 3349 return (match == null ? mp.addXStr("") : mp.addXInt(0)); 3350 for (int i = 0, nLast = xList.size(); i <= nLast; i++) { 3351 SV a = (i == nLast ? null : xList.get(i)); 3352 if (match != null && a != null && !SV.areEqual(a, match)) 3353 continue; 3354 if (SV.areEqual(a, last)) { 3355 count.intValue++; 3356 continue; 3357 } else if (last != null) { 3358 Lst<SV> y = new Lst<SV>(); 3359 y.addLast(last); 3360 y.addLast(count); 3361 counts.addLast(SV.getVariableList(y)); 3362 } 3363 count = SV.newI(1); 3364 last = a; 3365 } 3366 if (match == null) 3367 return mp.addX(SV.getVariableList(counts)); 3368 if (counts.isEmpty()) 3369 return mp.addXInt(0); 3370 return mp.addX(counts.get(0).getList().get(1)); 3371 } 3372 evaluateString(ScriptMathProcessor mp, int tok, SV[] args)3373 private boolean evaluateString(ScriptMathProcessor mp, int tok, SV[] args) 3374 throws ScriptException { 3375 SV x = mp.getX(); 3376 String sArg = (args.length > 0 ? SV.sValue(args[0]) 3377 : tok == T.trim ? "" : "\n"); 3378 switch (args.length) { 3379 case 0: 3380 break; 3381 case 1: 3382 if (args[0].tok == T.on) { 3383 return mp.addX(SV.getVariable(PT.getTokens(x.asString()))); 3384 } 3385 break; 3386 case 2: 3387 if (x.tok == T.varray) 3388 break; 3389 if (tok == T.split) { 3390 x = SV.getVariable(PT.split( 3391 PT.rep((String) x.value, "\n\r", "\n").replace('\r', '\n'), "\n")); 3392 break; 3393 } 3394 //$FALL-THROUGH$ 3395 default: 3396 return false; 3397 } 3398 3399 if (x.tok == T.varray && tok != T.trim 3400 && (tok != T.split || args.length == 2)) { 3401 mp.addX(x); 3402 return evaluateList(mp, tok, args); 3403 } 3404 String s = (tok == T.split && x.tok == T.bitset 3405 || tok == T.trim && x.tok == T.varray ? null : SV.sValue(x)); 3406 switch (tok) { 3407 case T.split: 3408 if (x.tok == T.bitset) { 3409 BS bsSelected = (BS) x.value; 3410 int modelCount = vwr.ms.mc; 3411 Lst<SV> lst = new Lst<SV>(); 3412 for (int i = 0; i < modelCount; i++) { 3413 BS bs = vwr.getModelUndeletedAtomsBitSet(i); 3414 bs.and(bsSelected); 3415 lst.addLast(SV.getVariable(bs)); 3416 } 3417 return mp.addXList(lst); 3418 } 3419 return mp.addXAS(PT.split(s, sArg)); 3420 case T.join: 3421 if (s.length() > 0 && s.charAt(s.length() - 1) == '\n') 3422 s = s.substring(0, s.length() - 1); 3423 return mp.addXStr(PT.rep(s, "\n", sArg)); 3424 case T.trim: 3425 if (s != null) 3426 return mp.addXStr(PT.trim(s, sArg)); 3427 String[] list = SV.strListValue(x); 3428 for (int i = list.length; --i >= 0;) 3429 list[i] = PT.trim(list[i], sArg); 3430 return mp.addXAS(list); 3431 } 3432 return mp.addXStr(""); 3433 } 3434 evaluateSubstructure(ScriptMathProcessor mp, SV[] args, int tok, boolean isSelector)3435 private boolean evaluateSubstructure(ScriptMathProcessor mp, SV[] args, 3436 int tok, boolean isSelector) 3437 throws ScriptException { 3438 // select substucture(....) legacy - was same as smiles(), now search() 3439 // select smiles(...) 3440 // select search(...) now same as substructure 3441 // print {*}.search(...) 3442 if (args.length == 0 || isSelector && args.length > 1) 3443 return false; 3444 BS bs = new BS(); 3445 String pattern = SV.sValue(args[0]); 3446 if (pattern.length() > 0) 3447 try { 3448 BS bsSelected = (isSelector ? (BS) mp.getX().value 3449 : args.length == 2 && args[1].tok == T.bitset ? (BS) args[1].value 3450 : vwr.getModelUndeletedAtomsBitSet(-1)); 3451 bs = vwr.getSmilesMatcher().getSubstructureSet(pattern, vwr.ms.at, 3452 vwr.ms.ac, bsSelected, 3453 (tok == T.smiles ? JC.SMILES_TYPE_SMILES : JC.SMILES_TYPE_SMARTS)); 3454 } catch (Exception ex) { 3455 e.evalError(ex.getMessage(), null); 3456 } 3457 return mp.addXBs(bs); 3458 } 3459 3460 @SuppressWarnings("unchecked") evaluateSymop(ScriptMathProcessor mp, SV[] args, boolean haveBitSet)3461 private boolean evaluateSymop(ScriptMathProcessor mp, SV[] args, 3462 boolean haveBitSet) 3463 throws ScriptException { 3464 3465 // In the following, "op" can be the operator number in a space group 3466 // or a string representation of the operator, such as "x,-y,z" 3467 3468 // x = y.symop(op,atomOrPoint) 3469 // Returns the point that is the result of the transformation of atomOrPoint 3470 // via a crystallographic symmetry operation. The atom set y selects the unit 3471 // cell and spacegroup to be used. If only one model is present, this can simply be all. 3472 // Otherwise, it could be any atom or group of atoms from any model, for example 3473 // {*/1.2} or {atomno=1}. The first parameter, op, is a symmetry operation. 3474 // This can be the 1-based index of a symmetry operation in a file (use show spacegroup to get this listing) 3475 // or a specific Jones-Faithful expression in quotes such as "x,1/2-y,z". 3476 3477 // x = y.symop(op,"label") 3478 // This form of the .symop() function returns a set of draw commands that describe 3479 // the symmetry operation in terms of rotation axes, inversion centers, planes, and 3480 // translational vectors. The draw objects will all have IDs starting with whatever 3481 // is given for the label. 3482 3483 // x = y.symop(op) 3484 // Returns the 4x4 matrix associated with this operator. 3485 3486 // x = y.symop(n,"time") 3487 // Returns the time reversal of a symmetry operator in a magnetic space group 3488 3489 // x = y.symop(atomOrPoint, atomOrPoint) 3490 3491 // x = y.symop(n, [h,k,l]) 3492 // adds a lattice translation to the symmetry operation 3493 3494 // x = y.symop(n,...,"cif2") 3495 // return a <symop> <translation> as nn [a b c] suitable for CIF2 inclusion 3496 3497 3498 SV x1 = (haveBitSet ? mp.getX() : null); 3499 if (x1 != null && x1.tok != T.bitset) 3500 return false; 3501 BS bsAtoms = (x1 == null ? null : (BS) x1.value); 3502 if (bsAtoms == null && vwr.ms.mc == 1) 3503 bsAtoms = vwr.getModelUndeletedAtomsBitSet(0); 3504 int narg = args.length; 3505 if (narg == 0) { 3506 if (bsAtoms.isEmpty()) 3507 return false; 3508 String[] ops = PT.split(PT.trim((String) vwr 3509 .getSymTemp().getSpaceGroupInfo(vwr.ms, null, 3510 vwr.ms.at[bsAtoms.nextSetBit(0)].mi, false, null) 3511 .get("symmetryInfo"), "\n"), "\n"); 3512 Lst<String[]> lst = new Lst<String[]>(); 3513 for (int i = 0, n = ops.length; i < n; i++) 3514 lst.addLast(PT.split(ops[i], "\t")); 3515 return mp.addXList(lst); 3516 } 3517 String xyz = null; 3518 int iOp = Integer.MIN_VALUE; 3519 int apt = 0; 3520 switch (args[0].tok) { 3521 case T.string: 3522 xyz = SV.sValue(args[0]); 3523 apt++; 3524 break; 3525 case T.matrix4f: 3526 xyz = args[0].escape(); 3527 apt++; 3528 break; 3529 case T.integer: 3530 iOp = args[0].asInt(); 3531 apt++; 3532 break; 3533 } 3534 if (bsAtoms == null) { 3535 if (apt < narg && args[apt].tok == T.bitset) 3536 (bsAtoms = new BS()).or((BS) args[apt].value); 3537 if (apt + 1 < narg && args[apt + 1].tok == T.bitset) 3538 (bsAtoms == null ? (bsAtoms = new BS()) : bsAtoms) 3539 .or((BS) args[apt + 1].value); 3540 } 3541 // allow for [ h k l ] lattice translation 3542 P3 trans = null; 3543 if (narg > apt && args[apt].tok == T.varray) { 3544 List<SV> a = args[apt++].getList(); 3545 if (a.size() != 3) 3546 return false; 3547 trans = P3.new3(SV.fValue(a.get(0)), SV.fValue(a.get(1)), 3548 SV.fValue(a.get(2))); 3549 } else if (narg > apt && args[apt].tok == T.integer) { 3550 SimpleUnitCell.ijkToPoint3f(SV.iValue(args[apt++]), trans = new P3(), 0, 3551 0); 3552 } 3553 P3 pt1 = null, pt2 = null; 3554 if ((pt1 = (narg > apt ? mp.ptValue(args[apt], bsAtoms) : null)) != null) 3555 apt++; 3556 if ((pt2 = (narg > apt ? mp.ptValue(args[apt], bsAtoms) : null)) != null) 3557 apt++; 3558 int nth = (pt2 != null && args.length > apt && iOp == Integer.MIN_VALUE 3559 && args[apt].tok == T.integer ? args[apt].intValue : -1); 3560 if (nth >= 0) // 0 here means "give me all of them" 3561 apt++; 3562 if (iOp == Integer.MIN_VALUE) 3563 iOp = 0; 3564 Map<String, ?> map = null; 3565 if (xyz != null) { 3566 if (apt == narg) { 3567 map = vwr.ms.getPointGroupInfo(null); 3568 } else if (args[apt].tok == T.hash) { 3569 map = args[apt].getMap(); 3570 } 3571 } 3572 if (map != null) { 3573 M3 m; 3574 // pointgroup. 3575 int pt = xyz.indexOf('.'); 3576 int p1 = xyz.indexOf('^'); 3577 if (p1 > 0) { 3578 nth = PT.parseInt(xyz.substring(p1 + 1)); 3579 } else { 3580 p1 = xyz.length(); 3581 nth = 1; 3582 } 3583 if (pt > 0 && p1 > pt + 1) { 3584 iOp = PT.parseInt(xyz.substring(pt + 1, p1)); 3585 if (iOp < 1) 3586 iOp = 1; 3587 p1 = pt; 3588 } else { 3589 iOp = 1; 3590 } 3591 xyz = xyz.substring(0, p1); 3592 Object o = map.get(xyz + "_m"); 3593 if (o == null) { 3594 o = map.get(xyz); 3595 return (o == null ? mp.addXStr("") : mp.addXObj(o)); 3596 } 3597 P3 centerPt; 3598 try { 3599 if (o instanceof SV) { 3600 centerPt = (P3) ((SV) map.get("center")).value; 3601 SV obj = (SV) o; 3602 if (obj.tok == T.matrix3f) { 3603 m = (M3) obj.value; 3604 } else if (obj.tok == T.varray) { 3605 m = (M3) obj.getList().get(iOp - 1).value; 3606 } else { 3607 return false; 3608 } 3609 } else { 3610 centerPt = (P3) map.get("center"); 3611 if (o instanceof M3) { 3612 m = (M3) o; 3613 } else { 3614 m = ((Lst<M3>) o).get(iOp - 1); 3615 } 3616 } 3617 M3 m0 = m; 3618 m = M3.newM3(m); 3619 if (nth > 1) { 3620 for (int i = 1; i < nth; i++) { 3621 m.mul(m0); 3622 } 3623 } 3624 if (pt1 == null) 3625 return mp.addXObj(m); 3626 pt1 = P3.newP(pt1); 3627 pt1.sub(centerPt); 3628 m.rotate(pt1); 3629 pt1.add(centerPt); 3630 return mp.addXPt(pt1); 3631 } catch (Exception e) { 3632 return false; 3633 } 3634 } 3635 String desc = (narg == apt 3636 ? (pt2 != null ? "all" : pt1 != null ? "point" : "matrix") 3637 : SV.sValue(args[apt++]).toLowerCase()); 3638 3639 return (bsAtoms != null && !bsAtoms.isEmpty() && apt == args.length 3640 && mp.addXObj(vwr.getSymmetryInfo(bsAtoms.nextSetBit(0), xyz, iOp, 3641 trans, pt1, pt2, T.array, desc, 0, nth, 0))); 3642 } 3643 evaluateTensor(ScriptMathProcessor mp, SV[] args)3644 private boolean evaluateTensor(ScriptMathProcessor mp, SV[] args) 3645 throws ScriptException { 3646 // {*}.tensor() 3647 // {*}.tensor("isc") // only within this atom set 3648 // {atomindex=1}.tensor("isc") // all to this atom 3649 // {*}.tensor("efg","eigenvalues") 3650 // tensor(t,what) 3651 boolean isTensor = (args.length == 2 && args[1].tok == T.tensor); 3652 SV x = (isTensor ? null : mp.getX()); 3653 if (args.length > 2 || !isTensor && x.tok != T.bitset) 3654 return false; 3655 BS bs = (BS) x.value; 3656 String tensorType = (isTensor || args.length == 0 ? null 3657 : SV.sValue(args[0]).toLowerCase()); 3658 JmolNMRInterface calc = vwr.getNMRCalculation(); 3659 if ("unique".equals(tensorType)) 3660 return mp.addXBs(calc.getUniqueTensorSet(bs)); 3661 String infoType = (args.length < 2 ? null 3662 : SV.sValue(args[1]).toLowerCase()); 3663 if (isTensor) { 3664 return mp.addXObj(((Tensor) args[0].value).getInfo(infoType)); 3665 } 3666 return mp.addXList(calc.getTensorInfo(tensorType, infoType, bs)); 3667 } 3668 evaluateUserFunction(ScriptMathProcessor mp, String name, SV[] args, int tok, boolean isSelector)3669 private boolean evaluateUserFunction(ScriptMathProcessor mp, String name, 3670 SV[] args, int tok, boolean isSelector) 3671 throws ScriptException { 3672 SV x1 = null; 3673 if (isSelector) { 3674 x1 = mp.getX(); 3675 switch (x1.tok) { 3676 case T.bitset: 3677 break; 3678 case T.hash: 3679 // really xx.yyy where yyy is a function; 3680 if (args.length > 0) 3681 return false; 3682 x1 = x1.getMap().get(name); 3683 return (x1 == null ? mp.addXStr("") : mp.addX(x1)); 3684 default: 3685 return false; 3686 } 3687 } 3688 name = name.toLowerCase(); 3689 mp.wasX = false; 3690 Lst<SV> params = new Lst<SV>(); 3691 for (int i = 0; i < args.length; i++) { 3692 params.addLast(args[i]); 3693 } 3694 if (isSelector) { 3695 return mp 3696 .addXObj(e.getBitsetProperty((BS) x1.value, null, tok, null, null, 3697 x1.value, new Object[] { name, params }, false, x1.index, false)); 3698 } 3699 SV var = e.getUserFunctionResult(name, params, null); 3700 return (var == null ? false : mp.addX(var)); 3701 } 3702 evaluateWithin(ScriptMathProcessor mp, SV[] args)3703 private boolean evaluateWithin(ScriptMathProcessor mp, SV[] args) 3704 throws ScriptException { 3705 int len = args.length; 3706 if (len < 1 || len > 5) 3707 return false; 3708 if (len == 1 && args[0].tok == T.bitset) 3709 return mp.addX(args[0]); 3710 float distance = 0; 3711 Object withinSpec = args[0].value; 3712 String withinStr = "" + withinSpec; 3713 int tok = args[0].tok; 3714 if (tok == T.string) 3715 tok = T.getTokFromName(withinStr); 3716 ModelSet ms = vwr.ms; 3717 boolean isVdw = false; 3718 boolean isWithinModelSet = false; 3719 boolean isWithinGroup = false; 3720 boolean isDistance = false; 3721 RadiusData rd = null; 3722 switch (tok) { 3723 case T.vanderwaals: 3724 isVdw = true; 3725 withinSpec = null; 3726 //$FALL-THROUGH$ 3727 case T.decimal: 3728 case T.integer: 3729 isDistance = true; 3730 if (len < 2 3731 || len == 3 && args[1].tok == T.varray && args[2].tok != T.varray) 3732 return false; 3733 distance = (isVdw ? 100 : SV.fValue(args[0])); 3734 switch (tok = args[1].tok) { 3735 case T.on: 3736 case T.off: 3737 isWithinModelSet = args[1].asBoolean(); 3738 if (len > 2 && SV.sValue(args[2]).equalsIgnoreCase("unitcell")) 3739 tok = T.unitcell; 3740 len = 0; 3741 break; 3742 case T.string: 3743 String s = SV.sValue(args[1]); 3744 if (s.startsWith("$")) 3745 return mp.addXBs(getAtomsNearSurface(distance, s.substring(1))); 3746 if (s.equalsIgnoreCase("group")) { 3747 isWithinGroup = true; 3748 tok = T.group; 3749 } else if (s.equalsIgnoreCase("vanderwaals") 3750 || s.equalsIgnoreCase("vdw")) { 3751 withinSpec = null; 3752 isVdw = true; 3753 tok = T.vanderwaals; 3754 } else if (s.equalsIgnoreCase("unitcell")) { 3755 tok = T.unitcell; 3756 } else { 3757 return false; 3758 } 3759 break; 3760 } 3761 break; 3762 case T.varray: 3763 if (len == 1) { 3764 withinSpec = args[0].asString(); // units ids8 3765 tok = T.nada; 3766 } 3767 break; 3768 case T.branch: 3769 return (len == 3 && args[1].value instanceof BS 3770 && args[2].value instanceof BS 3771 && mp.addXBs(vwr.getBranchBitSet(((BS) args[2].value).nextSetBit(0), 3772 ((BS) args[1].value).nextSetBit(0), true))); 3773 case T.smiles: 3774 case T.substructure: // same as "SMILES" 3775 case T.search: 3776 // within("smiles", "...", {bitset}) 3777 // within("smiles", "...", {bitset}) 3778 BS bsSelected = null; 3779 boolean isOK = true; 3780 switch (len) { 3781 case 2: 3782 break; 3783 case 3: 3784 isOK = (args[2].tok == T.bitset); 3785 if (isOK) 3786 bsSelected = (BS) args[2].value; 3787 break; 3788 default: 3789 isOK = false; 3790 } 3791 if (!isOK) 3792 e.invArg(); 3793 return mp.addXObj(e.getSmilesExt().getSmilesMatches(SV.sValue(args[1]), 3794 null, bsSelected, null, 3795 tok == T.search ? JC.SMILES_TYPE_SMARTS : JC.SMILES_TYPE_SMILES, 3796 mp.asBitSet, false)); 3797 } 3798 3799 if (withinSpec instanceof String) { 3800 if (tok == T.nada) { 3801 tok = T.spec_seqcode; 3802 if (len > 2) 3803 return false; 3804 len = 2; 3805 } 3806 } else if (!isDistance) { 3807 return false; 3808 } 3809 3810 switch (len) { 3811 case 1: 3812 // within (sheet) 3813 // within (helix) 3814 // within (boundbox) 3815 switch (tok) { 3816 case T.helix: 3817 case T.sheet: 3818 case T.boundbox: 3819 return mp.addXBs(ms.getAtoms(tok, null)); 3820 case T.basepair: 3821 return mp.addXBs(ms.getAtoms(tok, "")); 3822 case T.spec_seqcode: 3823 return mp.addXBs(ms.getAtoms(T.sequence, withinStr)); 3824 } 3825 return false; 3826 case 2: 3827 // within (atomName, "XX,YY,ZZZ") 3828 switch (tok) { 3829 case T.spec_seqcode: 3830 tok = T.sequence; 3831 break; 3832 case T.identifier: 3833 case T.atomname: 3834 case T.atomtype: 3835 case T.basepair: 3836 case T.sequence: 3837 case T.dssr: 3838 case T.rna3d: 3839 case T.domains: 3840 case T.validation: 3841 return mp.addXBs(vwr.ms.getAtoms(tok, SV.sValue(args[1]))); 3842 case T.cell: 3843 case T.centroid: 3844 return mp.addXBs(vwr.ms.getAtoms(tok, SV.ptValue(args[1]))); 3845 } 3846 break; 3847 case 3: 3848 switch (tok) { 3849 case T.on: 3850 case T.off: 3851 case T.group: 3852 case T.vanderwaals: 3853 case T.unitcell: 3854 case T.plane: 3855 case T.hkl: 3856 case T.coord: 3857 case T.point3f: 3858 case T.varray: 3859 break; 3860 case T.sequence: 3861 // within ("sequence", "CII", *.ca) 3862 withinStr = SV.sValue(args[2]); 3863 break; 3864 default: 3865 return false; 3866 } 3867 // within (distance, group, {atom collection}) 3868 // within (distance, true|false, {atom collection}) 3869 // within (distance, plane|hkl, [plane definition] ) 3870 // within (distance, coord, [point or atom center] ) 3871 break; 3872 } 3873 P4 plane = null; 3874 P3 pt = null; 3875 Lst<SV> pts1 = null; 3876 int last = args.length - 1; 3877 switch (args[last].tok) { 3878 case T.point4f: 3879 plane = (P4) args[last].value; 3880 break; 3881 case T.point3f: 3882 pt = (P3) args[last].value; 3883 if (SV.sValue(args[1]).equalsIgnoreCase("hkl")) 3884 plane = e.getHklPlane(pt, Float.NaN, false); 3885 break; 3886 case T.varray: 3887 pts1 = (last == 2 && args[1].tok == T.varray ? args[1].getList() : null); 3888 pt = (last == 2 ? SV.ptValue(args[1]) 3889 : last == 1 ? P3.new3(Float.NaN, 0, 0) : null); 3890 break; 3891 } 3892 if (plane != null) 3893 return mp.addXBs(ms.getAtomsNearPlane(distance, plane)); 3894 BS bs = (args[last].tok == T.bitset ? (BS) args[last].value : null); 3895 if (last > 0 && pt == null && pts1 == null && bs == null) 3896 return false; 3897 // if we have anything, it must have a point or an array or a plane or a bitset from here on out 3898 if (tok == T.unitcell) { 3899 boolean asMap = isWithinModelSet; 3900 return ((bs != null || pt != null) && mp 3901 .addXObj(vwr.ms.getUnitCellPointsWithin(distance, bs, pt, asMap))); 3902 } 3903 if (pt != null || pts1 != null) { 3904 if (args[last].tok == T.varray) { 3905 // within(dist, pt, [pt1, pt2, pt3...]) 3906 // within(dist, [pt1, pt2, pt3...]) 3907 Lst<SV> sv = args[last].getList(); 3908 P3[] ap3 = new P3[sv.size()]; 3909 for (int i = ap3.length; --i >= 0;) 3910 ap3[i] = SV.ptValue(sv.get(i)); 3911 P3[] ap31 = null; 3912 if (pts1 != null) { 3913 ap31 = new P3[pts1.size()]; 3914 for (int i = ap31.length; --i >= 0;) 3915 ap31[i] = SV.ptValue(pts1.get(i)); 3916 } 3917 Object[] ret = new Object[1]; 3918 switch (PointIterator.withinDistPoints(distance, pt, ap3, ap31, ret)) { 3919 case T.point: 3920 return mp.addXPt((P3) ret[0]); 3921 case T.list: 3922 return mp.addXList((Lst<?>) ret[0]); 3923 case T.array: // 3924 return mp.addXAI((int[]) ret[0]); 3925 case T.string: // "" return 3926 return mp.addXStr((String) ret[0]); 3927 default: 3928 return false; // error return 3929 } 3930 } 3931 return mp.addXBs(vwr.getAtomsNearPt(distance, pt)); 3932 } 3933 3934 if (tok == T.sequence) 3935 return mp.addXBs(vwr.ms.getSequenceBits(withinStr, bs, new BS())); 3936 if (bs == null) 3937 bs = new BS(); 3938 if (!isDistance) { 3939 try { 3940 return mp.addXBs(vwr.ms.getAtoms(tok, bs)); 3941 } catch (Exception e) { 3942 return false; 3943 } 3944 } 3945 if (isWithinGroup) 3946 return mp.addXBs(vwr.getGroupsWithin((int) distance, bs)); 3947 if (isVdw) { 3948 rd = new RadiusData(null, (distance > 10 ? distance / 100 : distance), 3949 (distance > 10 ? EnumType.FACTOR : EnumType.OFFSET), VDW.AUTO); 3950 if (distance < 0) 3951 distance = 0; // not used, but this prevents a diversion 3952 } 3953 return mp.addXBs( 3954 vwr.ms.getAtomsWithinRadius(distance, bs, isWithinModelSet, rd)); 3955 } 3956 evaluateWrite(ScriptMathProcessor mp, SV[] args)3957 private boolean evaluateWrite(ScriptMathProcessor mp, SV[] args) 3958 throws ScriptException { 3959 switch (args.length) { 3960 case 0: 3961 return false; 3962 case 1: 3963 String type = args[0].asString().toUpperCase(); 3964 if (type.equals("PNGJ")) { 3965 return mp.addXMap(vwr.fm.getFileAsMap(null, "PNGJ")); 3966 } 3967 if (PT.isOneOf(type, ";ZIP;ZIPALL;JMOL;")) { 3968 Map<String, Object> params = new Hashtable<String, Object>(); 3969 OC oc = new OC(); 3970 params.put("outputChannel", oc); 3971 vwr.createZip(null, type, params); 3972 BufferedInputStream bis = Rdr.getBIS(oc.toByteArray()); 3973 params = new Hashtable<String, Object>(); 3974 vwr.readFileAsMap(bis, params, null); 3975 return mp.addXMap(params); 3976 } 3977 break; 3978 } 3979 return mp.addXStr(e.getCmdExt().dispatch(T.write, true, args)); 3980 } 3981 3982 ///////// private methods used by evaluateXXXXX //////// 3983 getAtomsNearSurface(float distance, String surfaceId)3984 private BS getAtomsNearSurface(float distance, String surfaceId) { 3985 Object[] data = new Object[] { surfaceId, null, null }; 3986 if (e.getShapePropertyData(JC.SHAPE_ISOSURFACE, "getVertices", data)) 3987 return getAtomsNearPts(distance, (T3[]) data[1], (BS) data[2]); 3988 data[1] = Integer.valueOf(0); 3989 data[2] = Integer.valueOf(-1); 3990 if (e.getShapePropertyData(JC.SHAPE_DRAW, "getCenter", data)) 3991 return vwr.getAtomsNearPt(distance, (P3) data[2]); 3992 data[1] = Float.valueOf(distance); 3993 if (e.getShapePropertyData(JC.SHAPE_POLYHEDRA, "getAtomsWithin", data)) 3994 return (BS) data[2]; 3995 return new BS(); 3996 } 3997 getAtomsNearPts(float distance, T3[] points, BS bsInclude)3998 private BS getAtomsNearPts(float distance, T3[] points, BS bsInclude) { 3999 BS bsResult = new BS(); 4000 if (points.length == 0 || bsInclude != null && bsInclude.isEmpty()) 4001 return bsResult; 4002 if (bsInclude == null) 4003 bsInclude = BSUtil.setAll(points.length); 4004 Atom[] at = vwr.ms.at; 4005 for (int i = vwr.ms.ac; --i >= 0;) { 4006 Atom atom = at[i]; 4007 for (int j = bsInclude.nextSetBit(0); j >= 0; j = bsInclude 4008 .nextSetBit(j + 1)) 4009 if (atom.distance(points[j]) < distance) { 4010 bsResult.set(i); 4011 break; 4012 } 4013 } 4014 return bsResult; 4015 } 4016 4017 @SuppressWarnings("unchecked") getMinMax(Object floatOrSVArray, int tok)4018 public Object getMinMax(Object floatOrSVArray, int tok) { 4019 float[] data = null; 4020 Lst<SV> sv = null; 4021 int ndata = 0; 4022 Map<String, Integer> htPivot = null; 4023 while (true) { 4024 if (AU.isAF(floatOrSVArray)) { 4025 if (tok == T.pivot) 4026 return "NaN"; 4027 data = (float[]) floatOrSVArray; 4028 ndata = data.length; 4029 if (ndata == 0) 4030 break; 4031 } else if (floatOrSVArray instanceof Lst<?>) { 4032 sv = (Lst<SV>) floatOrSVArray; 4033 ndata = sv.size(); 4034 if (ndata == 0) { 4035 if (tok != T.pivot) 4036 break; 4037 } else { 4038 if (tok != T.pivot) { 4039 SV sv0 = sv.get(0); 4040 if (sv0.tok == T.point3f) 4041 return getMinMaxPoint(sv, tok); 4042 if (sv0.tok == T.string && ((String) sv0.value).startsWith("{")) { 4043 Object pt = SV.ptValue(sv0); 4044 if (pt instanceof P3) 4045 return getMinMaxPoint(sv, tok); 4046 if (pt instanceof P4) 4047 return getMinMaxQuaternion(sv, tok); 4048 break; 4049 } 4050 } 4051 } 4052 } else { 4053 break; 4054 } 4055 double sum; 4056 int minMax; 4057 boolean isMin = false; 4058 switch (tok) { 4059 case T.pivot: 4060 htPivot = new Hashtable<String, Integer>(); 4061 sum = minMax = 0; 4062 break; 4063 case T.min: 4064 isMin = true; 4065 sum = Float.MAX_VALUE; 4066 minMax = Integer.MAX_VALUE; 4067 break; 4068 case T.max: 4069 sum = -Float.MAX_VALUE; 4070 minMax = -Integer.MAX_VALUE; 4071 break; 4072 default: 4073 sum = minMax = 0; 4074 } 4075 double sum2 = 0; 4076 int n = 0; 4077 boolean isInt = true; 4078 boolean isPivot = (tok == T.pivot); 4079 for (int i = ndata; --i >= 0;) { 4080 SV svi = (sv == null ? SV.vF : sv.get(i)); 4081 float v = (isPivot ? 1 : data == null ? SV.fValue(svi) : data[i]); 4082 if (Float.isNaN(v)) 4083 continue; 4084 n++; 4085 switch (tok) { 4086 case T.sum2: 4087 case T.stddev: 4088 sum2 += ((double) v) * v; 4089 //$FALL-THROUGH$ 4090 case T.sum: 4091 case T.average: 4092 sum += v; 4093 break; 4094 case T.pivot: 4095 isInt &= (svi.tok == T.integer); 4096 String key = svi.asString(); 4097 Integer ii = htPivot.get(key); 4098 htPivot.put(key, 4099 (ii == null ? new Integer(1) : new Integer(ii.intValue() + 1))); 4100 break; 4101 case T.min: 4102 case T.max: 4103 isInt &= (svi.tok == T.integer); 4104 if (isMin == (v < sum)) { 4105 sum = v; 4106 if (isInt) 4107 minMax = svi.intValue; 4108 } 4109 break; 4110 } 4111 } 4112 if (tok == T.pivot) { 4113 return htPivot; 4114 } 4115 if (n == 0) 4116 break; 4117 switch (tok) { 4118 case T.average: 4119 sum /= n; 4120 break; 4121 case T.stddev: 4122 if (n == 1) 4123 break; 4124 sum = Math.sqrt((sum2 - sum * sum / n) / (n - 1)); 4125 break; 4126 case T.min: 4127 case T.max: 4128 if (isInt) 4129 return Integer.valueOf(minMax); 4130 break; 4131 case T.sum: 4132 break; 4133 case T.sum2: 4134 sum = sum2; 4135 break; 4136 } 4137 return Float.valueOf((float) sum); 4138 } 4139 return "NaN"; 4140 } 4141 4142 /** 4143 * calculates the statistical value for x, y, and z independently 4144 * 4145 * @param pointOrSVArray 4146 * @param tok 4147 * @return Point3f or "NaN" 4148 */ 4149 @SuppressWarnings("unchecked") getMinMaxPoint(Object pointOrSVArray, int tok)4150 private Object getMinMaxPoint(Object pointOrSVArray, int tok) { 4151 P3[] data = null; 4152 Lst<SV> sv = null; 4153 int ndata = 0; 4154 if (pointOrSVArray instanceof Quat[]) { 4155 data = (P3[]) pointOrSVArray; 4156 ndata = data.length; 4157 } else if (pointOrSVArray instanceof Lst<?>) { 4158 sv = (Lst<SV>) pointOrSVArray; 4159 ndata = sv.size(); 4160 } 4161 if (sv == null && data == null) 4162 return "NaN"; 4163 P3 result = new P3(); 4164 float[] fdata = new float[ndata]; 4165 for (int xyz = 0; xyz < 3; xyz++) { 4166 for (int i = 0; i < ndata; i++) { 4167 P3 pt = (data == null ? SV.ptValue(sv.get(i)) : data[i]); 4168 if (pt == null) 4169 return "NaN"; 4170 switch (xyz) { 4171 case 0: 4172 fdata[i] = pt.x; 4173 break; 4174 case 1: 4175 fdata[i] = pt.y; 4176 break; 4177 case 2: 4178 fdata[i] = pt.z; 4179 break; 4180 } 4181 } 4182 Object f = getMinMax(fdata, tok); 4183 if (!(f instanceof Number)) 4184 return "NaN"; 4185 float value = ((Number) f).floatValue(); 4186 switch (xyz) { 4187 case 0: 4188 result.x = value; 4189 break; 4190 case 1: 4191 result.y = value; 4192 break; 4193 case 2: 4194 result.z = value; 4195 break; 4196 } 4197 } 4198 return result; 4199 } 4200 getMinMaxQuaternion(Lst<SV> svData, int tok)4201 private Object getMinMaxQuaternion(Lst<SV> svData, int tok) { 4202 Quat[] data; 4203 switch (tok) { 4204 case T.min: 4205 case T.max: 4206 case T.sum: 4207 case T.sum2: 4208 return "NaN"; 4209 } 4210 4211 // only stddev and average 4212 4213 while (true) { 4214 data = e.getQuaternionArray(svData, T.list); 4215 if (data == null) 4216 break; 4217 float[] retStddev = new float[1]; 4218 Quat result = Quat.sphereMean(data, retStddev, 0.0001f); 4219 switch (tok) { 4220 case T.average: 4221 return result; 4222 case T.stddev: 4223 return Float.valueOf(retStddev[0]); 4224 } 4225 break; 4226 } 4227 return "NaN"; 4228 } 4229 4230 private JmolPatternMatcher pm; 4231 getPatternMatcher()4232 private JmolPatternMatcher getPatternMatcher() { 4233 return (pm == null 4234 ? pm = (JmolPatternMatcher) Interface.getUtil("PatternMatcher", e.vwr, 4235 "script") 4236 : pm); 4237 } 4238 opTokenFor(int tok)4239 private T opTokenFor(int tok) { 4240 switch (tok) { 4241 case T.add: 4242 case T.join: 4243 return T.tokenPlus; 4244 case T.sub: 4245 return T.tokenMinus; 4246 case T.mul: 4247 return T.tokenTimes; 4248 case T.mul3: 4249 return T.tokenMul3; 4250 case T.div: 4251 return T.tokenDivide; 4252 } 4253 return null; 4254 } 4255 setContactBitSets(BS bsA, BS bsB, boolean localOnly, float distance, RadiusData rd, boolean warnMultiModel)4256 public BS setContactBitSets(BS bsA, BS bsB, boolean localOnly, float distance, 4257 RadiusData rd, boolean warnMultiModel) { 4258 boolean withinAllModels; 4259 BS bs; 4260 if (bsB == null) { 4261 // default is within just one model when {B} is missing 4262 bsB = BSUtil.setAll(vwr.ms.ac); 4263 BSUtil.andNot(bsB, vwr.slm.bsDeleted); 4264 bsB.andNot(bsA); 4265 withinAllModels = false; 4266 } else { 4267 // two atom sets specified; within ALL MODELS here 4268 bs = BSUtil.copy(bsA); 4269 bs.or(bsB); 4270 int nModels = vwr.ms.getModelBS(bs, false).cardinality(); 4271 withinAllModels = (nModels > 1); 4272 if (warnMultiModel && nModels > 1 && !e.tQuiet) 4273 e.showString( 4274 GT.$("Note: More than one model is involved in this contact!")); 4275 } 4276 // B always within some possibly extended VDW of A or just A itself 4277 if (!bsA.equals(bsB)) { 4278 boolean setBfirst = (!localOnly || bsA.cardinality() < bsB.cardinality()); 4279 if (setBfirst) { 4280 bs = vwr.ms.getAtomsWithinRadius(distance, bsA, withinAllModels, 4281 (Float.isNaN(distance) ? rd : null)); 4282 bsB.and(bs); 4283 } 4284 if (localOnly) { 4285 // we can just get the near atoms for A as well. 4286 bs = vwr.ms.getAtomsWithinRadius(distance, bsB, withinAllModels, 4287 (Float.isNaN(distance) ? rd : null)); 4288 bsA.and(bs); 4289 if (!setBfirst) { 4290 bs = vwr.ms.getAtomsWithinRadius(distance, bsA, withinAllModels, 4291 (Float.isNaN(distance) ? rd : null)); 4292 bsB.and(bs); 4293 } 4294 // If the two sets are not the same, 4295 // we AND them and see if that is A. 4296 // If so, then the smaller set is 4297 // removed from the larger set. 4298 bs = BSUtil.copy(bsB); 4299 bs.and(bsA); 4300 if (bs.equals(bsA)) 4301 bsB.andNot(bsA); 4302 else if (bs.equals(bsB)) 4303 bsA.andNot(bsB); 4304 } 4305 } 4306 return bsB; 4307 } 4308 } 4309