1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2006-12-03 14:51:57 -0600 (Sun, 03 Dec 2006) $ 4 * $Revision: 6372 $ 5 * 6 * Copyright (C) 2005 Miguel, The Jmol Development Team 7 * 8 * Contact: jmol-developers@lists.sf.net, 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.shapesurface; 26 27 import javajs.util.AU; 28 import javajs.util.Lst; 29 import javajs.util.SB; 30 31 import java.util.Hashtable; 32 33 import java.util.Map; 34 35 36 import org.jmol.api.Interface; 37 import org.jmol.api.SymmetryInterface; 38 import org.jmol.util.C; 39 import org.jmol.util.ColorEncoder; 40 import org.jmol.util.GData; 41 import org.jmol.util.Logger; 42 import org.jmol.util.MeshSurface; 43 import org.jmol.util.SimpleUnitCell; 44 45 import javajs.util.CU; 46 import javajs.util.M4; 47 import javajs.util.P3; 48 import javajs.util.P3i; 49 import javajs.util.PT; 50 import javajs.util.T3; 51 import javajs.util.V3; 52 import org.jmol.viewer.Viewer; 53 import javajs.util.BS; 54 import org.jmol.jvxl.data.JvxlCoder; 55 import org.jmol.jvxl.data.JvxlData; 56 import org.jmol.jvxl.data.MeshData; 57 58 import org.jmol.jvxl.calc.MarchingSquares; 59 import org.jmol.modelset.Atom; 60 import org.jmol.script.T; 61 import org.jmol.shape.Mesh; 62 63 public class IsosurfaceMesh extends Mesh { 64 public JvxlData jvxlData; 65 public int vertexIncrement = 1; 66 public int firstRealVertex = -1; 67 public int dataType; 68 public boolean hasGridPoints; 69 Object calculatedArea; 70 Object calculatedVolume; 71 Object info; 72 73 @Override getResolution()74 public float getResolution() { 75 return 1/jvxlData.pointsPerAngstrom; 76 } 77 78 /** 79 * @param vwr 80 * @XXXXj2sIgnoreSuperConstructor 81 * 82 * @param thisID 83 * @param colix 84 * @param index 85 */ IsosurfaceMesh(Viewer vwr, String thisID, short colix, int index)86 IsosurfaceMesh(Viewer vwr, String thisID, short colix, int index) { 87 mesh1(vwr, thisID, colix, index); 88 jvxlData = new JvxlData(); 89 checkByteCount = 2; 90 jvxlData.version = Viewer.getJmolVersion(); 91 } 92 clearType(String meshType, boolean iAddGridPoints)93 void clearType(String meshType, boolean iAddGridPoints) { 94 clear(meshType); 95 jvxlData.clear(); 96 assocGridPointMap = null; 97 assocGridPointNormals = null; 98 bsVdw = null; 99 calculatedVolume = null; 100 calculatedArea = null; 101 centers = null; 102 colorEncoder = null; 103 colorPhased = false; 104 colorsExplicit = false; 105 firstRealVertex = -1; 106 hasGridPoints = iAddGridPoints; 107 isColorSolid = true; 108 mergeAssociatedNormalCount = 0; 109 nSets = 0; 110 pcs = null; 111 showPoints = iAddGridPoints; 112 surfaceSet = null; 113 vcs = null; 114 vertexColorMap = null; 115 vertexIncrement = 1; 116 vertexSets = null; 117 vvs = null; 118 } 119 allocVertexColixes()120 void allocVertexColixes() { 121 if (vcs == null) { 122 vcs = new short[vc]; 123 for (int i = vc; --i >= 0;) 124 vcs[i] = colix; 125 } 126 isColorSolid = false; 127 } 128 129 private Map<Integer, Integer> assocGridPointMap; 130 private Map<Integer, V3> assocGridPointNormals; 131 addVertexCopy(T3 vertex, float value, int assocVertex, boolean associateNormals, boolean asCopy)132 int addVertexCopy(T3 vertex, float value, int assocVertex, 133 boolean associateNormals, boolean asCopy) { 134 int vPt = addVCVal(vertex, value, asCopy); 135 switch (assocVertex) { 136 case MarchingSquares.CONTOUR_POINT: 137 if (firstRealVertex < 0) 138 firstRealVertex = vPt; 139 break; 140 case MarchingSquares.VERTEX_POINT: 141 hasGridPoints = true; 142 break; 143 case MarchingSquares.EDGE_POINT: 144 vertexIncrement = 3; 145 break; 146 default: 147 if (firstRealVertex < 0) 148 firstRealVertex = vPt; 149 if (associateNormals) { 150 if (assocGridPointMap == null) 151 assocGridPointMap = new Hashtable<Integer, Integer>(); 152 assocGridPointMap.put(Integer.valueOf(vPt), Integer.valueOf(assocVertex + mergeAssociatedNormalCount)); 153 } 154 } 155 return vPt; 156 } 157 158 @Override setTranslucent(boolean isTranslucent, float iLevel)159 public void setTranslucent(boolean isTranslucent, float iLevel) { 160 colix = C.getColixTranslucent3(colix, isTranslucent, iLevel); 161 if (vcs != null) 162 for (int i = vc; --i >= 0;) 163 vcs[i] = C.getColixTranslucent3(vcs[i], 164 isTranslucent, iLevel); 165 } 166 167 private int mergeAssociatedNormalCount; setMerged(boolean TF)168 public void setMerged(boolean TF) { 169 isMerged = TF; 170 mergePolygonCount0 = (TF ? pc : 0); 171 mergeVertexCount0 = (TF ? vc: 0); 172 if (TF) { 173 mergeAssociatedNormalCount += jvxlData.nPointsX * jvxlData.nPointsY * jvxlData.nPointsZ; 174 assocGridPointNormals = null; 175 } 176 } 177 178 179 @Override sumVertexNormals(T3[] vertices, V3[] vectorSums)180 protected void sumVertexNormals(T3[] vertices, V3[] vectorSums) { 181 sumVertexNormals2(this, vertices, vectorSums); 182 /* 183 * OK, so if there is an associated grid point (because the 184 * point is so close to one), we now declare that associated 185 * point to be used for the vecetorSum instead of a new, 186 * independent one for the point itself. 187 * 188 * Bob Hanson, 05/2006 189 * 190 * having 2-sided normixes is INCOMPATIBLE with this when not a plane 191 * 192 */ 193 if (assocGridPointMap != null && vectorSums.length > 0 && !isMerged) { 194 if (assocGridPointNormals == null) 195 assocGridPointNormals = new Hashtable<Integer, V3>(); 196 for (Map.Entry<Integer, Integer> entry : assocGridPointMap.entrySet()) { 197 // keys are indices into vertices[] 198 // values are unique identifiers for a grid point 199 Integer gridPoint = entry.getValue(); 200 if (!assocGridPointNormals.containsKey(gridPoint)) 201 assocGridPointNormals.put(gridPoint, V3.new3(0, 0, 0)); 202 assocGridPointNormals.get(gridPoint).add(vectorSums[entry.getKey().intValue()]); 203 } 204 for (Map.Entry<Integer, Integer> entry : assocGridPointMap.entrySet()) 205 vectorSums[entry.getKey().intValue()] = assocGridPointNormals.get(entry.getValue()); 206 } 207 } 208 209 P3[] centers; 210 getCenters()211 P3[] getCenters() { 212 if (centers != null) 213 return centers; 214 centers = new P3[pc]; 215 for (int i = 0; i < pc; i++) { 216 int[] p = pis[i]; 217 if (p == null) 218 continue; 219 P3 pt = centers[i] = P3.newP(vs[p[0]]); 220 pt.add(vs[p[1]]); 221 pt.add(vs[p[2]]); 222 pt.scale(1 / 3f); 223 } 224 return centers; 225 } 226 227 // P4 getFacePlane(int i, V3 vNorm) { 228 // return Measure.getPlaneThroughPoints(vs[pis[i][0]], 229 // vs[pis[i][1]], vs[pis[i][2]], vNorm, 230 // vAB, new P4()); 231 // } 232 233 /** 234 * create a set of contour data. 235 * 236 * Each contour is a Vector containing: 0 Integer number of polygons (length 237 * of BitSet) 1 BitSet of critical triangles 2 Float value 3 int[] [colorArgb] 238 * 4 StringXBuilder containing encoded data for each segment: char type ('3', 239 * '6', '5') indicating which two edges of the triangle are connected: '3' 240 * 0x011 AB-BC '5' 0x101 AB-CA '6' 0x110 BC-CA char fraction along first edge 241 * (jvxlFractionToCharacter) char fraction along second edge 242 * (jvxlFractionToCharacter) 5- stream of pairs of points for rendering 243 * 244 * @return contour vector set 245 */ 246 @SuppressWarnings("unchecked") 247 public getContours()248 Lst<Object>[] getContours() { 249 int n = jvxlData.nContours; 250 if (n == 0 || pis == null) 251 return null; 252 havePlanarContours = (jvxlData.jvxlPlane != null); 253 if (havePlanarContours) 254 return null; // not necessary; 255 if (n < 0) 256 n = -1 - n; 257 Lst<Object>[] vContours = jvxlData.vContours; 258 if (vContours != null) { 259 for (int i = 0; i < n; i++) { 260 if (vContours[i].size() > JvxlCoder.CONTOUR_POINTS) 261 return jvxlData.vContours; 262 JvxlCoder.set3dContourVector(vContours[i], pis, vs); 263 } 264 //dumpData(); 265 return jvxlData.vContours; 266 } 267 //dumpData(); 268 vContours = new Lst[n]; 269 for (int i = 0; i < n; i++) { 270 vContours[i] = new Lst<Object>(); 271 } 272 if (jvxlData.contourValuesUsed == null) { 273 float dv = (jvxlData.valueMappedToBlue - jvxlData.valueMappedToRed) 274 / (n + 1); 275 // n + 1 because we want n lines between n + 1 slices 276 for (int i = 0; i < n; i++) { 277 float value = jvxlData.valueMappedToRed + (i + 1) * dv; 278 get3dContour(this, vContours[i], value, jvxlData.contourColixes[i]); 279 } 280 Logger.info(n + " contour lines; separation = " + dv); 281 } else { 282 for (int i = 0; i < n; i++) { 283 float value = jvxlData.contourValuesUsed[i]; 284 get3dContour(this, vContours[i], value, jvxlData.contourColixes[i]); 285 } 286 } 287 jvxlData.contourColixes = new short[n]; 288 jvxlData.contourValues = new float[n]; 289 for (int i = 0; i < n; i++) { 290 jvxlData.contourValues[i] = ((Float) vContours[i].get(2)).floatValue(); 291 jvxlData.contourColixes[i] = ((short[]) vContours[i].get(3))[0]; 292 } 293 return jvxlData.vContours = vContours; 294 } 295 getPmeshData(boolean isBinary)296 public Object getPmeshData(boolean isBinary) { 297 PMeshWriter mw = (PMeshWriter) Interface.getInterface("org.jmol.shapesurface.PMeshWriter", vwr, "script"); 298 return mw.write(this, isBinary); 299 } 300 get3dContour(IsosurfaceMesh m, Lst<Object> v, float value, short colix)301 private static void get3dContour(IsosurfaceMesh m, Lst<Object> v, float value, short colix) { 302 BS bsContour = BS.newN(m.pc); 303 SB fData = new SB(); 304 int color = C.getArgb(colix); 305 setContourVector(v, m.pc, bsContour, value, colix, color, fData); 306 for (int i = 0; i < m.pc; i++) 307 if (m.setABC(i) != null) 308 addContourPoints(v, bsContour, i, fData, m.vs, m.vvs, m.iA, 309 m.iB, m.iC, value); 310 } 311 setContourVector(Lst<Object> v, int nPolygons, BS bsContour, float value, short colix, int color, SB fData)312 public static void setContourVector(Lst<Object> v, int nPolygons, 313 BS bsContour, float value, 314 short colix, int color, SB fData) { 315 v.add(JvxlCoder.CONTOUR_NPOLYGONS, Integer.valueOf(nPolygons)); 316 v.add(JvxlCoder.CONTOUR_BITSET, bsContour); 317 v.add(JvxlCoder.CONTOUR_VALUE, Float.valueOf(value)); 318 v.add(JvxlCoder.CONTOUR_COLIX, new short[] { colix }); 319 v.add(JvxlCoder.CONTOUR_COLOR, new int[] { color }); 320 v.add(JvxlCoder.CONTOUR_FDATA, fData); 321 } 322 addContourPoints(Lst<Object> v, BS bsContour, int i, SB fData, T3[] vertices, float[] vertexValues, int iA, int iB, int iC, float value)323 public static void addContourPoints(Lst<Object> v, BS bsContour, int i, 324 SB fData, T3[] vertices, 325 float[] vertexValues, int iA, int iB, 326 int iC, float value) { 327 P3 pt1 = null; 328 P3 pt2 = null; 329 int type = 0; 330 // check AB 331 float f1 = checkPt(vertexValues, iA, iB, value); 332 if (!Float.isNaN(f1)) { 333 pt1 = getContourPoint(vertices, iA, iB, f1); 334 type |= 1; 335 } 336 // check BC only if v not found only at B already in testing AB 337 float f2 = (f1 == 1 ? Float.NaN : checkPt(vertexValues, iB, iC, value)); 338 if (!Float.isNaN(f2)) { 339 pt2 = getContourPoint(vertices, iB, iC, f2); 340 if (type == 0) { 341 pt1 = pt2; 342 f1 = f2; 343 } 344 type |= 2; 345 } 346 // only check CA under certain circumstances 347 switch (type) { 348 case 0: 349 return; // not in AB or BC, so ignore 350 case 1: 351 if (f1 == 0) 352 return; //because at A and not along BC, so only at A 353 //$FALL-THROUGH$ 354 case 2: 355 // check CA only if v not found only at C already in testing BC 356 f2 = (f2 == 1 ? Float.NaN : checkPt(vertexValues, iC, iA, value)); 357 if (!Float.isNaN(f2)) { 358 pt2 = getContourPoint(vertices, iC, iA, f2); 359 type |= 4; 360 } 361 break; 362 } 363 // only types AB-BC, AB-CA, or BC-CA are valid intersections 364 switch (type) { 365 case 3: 366 case 5: 367 case 6: 368 break; 369 default: 370 return; 371 } 372 bsContour.set(i); 373 JvxlCoder.appendContourTriangleIntersection(type, f1, f2, fData); 374 v.addLast(pt1); 375 v.addLast(pt2); 376 } 377 378 /** 379 * two values -- v1, and v2, which need not be ordered v1 < v2. v == v1 --> 0 380 * v == v2 --> 1 v1 < v < v2 --> f in (0,1) v2 < v < v1 --> f in (0,1) i.e. 381 * (v1 < v) == (v < v2) 382 * 383 * We check AB, then (usually) BC, then (sometimes) CA. 384 * 385 * What if two end points are identical values? So, for example, if v = 1.0 386 * and: 387 * 388 * A 1.0 0.5 1.0 1.0 / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ C-----B 1.0--0.5 389 * 1.0--1.0 0.5--1.0 1.0---1.0 case I case II case III case IV 390 * 391 * case I: AB[0] and BC[1], type == 3 --> CA not tested. case II: AB[1] and 392 * CA[0]; f1 == 1.0 --> BC not tested. case III: AB[0] and BC[0], type == 3 393 * --> CA not tested. case IV: AB[0] and BC[0], type == 3 --> CA not tested. 394 * 395 * what if v = 0.5? 396 * 397 * case I: AB[1]; BC not tested --> type == 1, invalid. case II: AB[0]; type 398 * == 1, f1 == 0.0 --> CA not tested. case III: BC[1]; f2 == 1.0 --> CA not 399 * tested. 400 * 401 * @param vertexValues 402 * @param i 403 * @param j 404 * @param v 405 * @return fraction along the edge or NaN 406 */ checkPt(float[] vertexValues, int i, int j, float v)407 private static float checkPt(float[] vertexValues, int i, int j, float v) { 408 float v1, v2; 409 return (v == (v1 = vertexValues[i]) ? 0 : v == (v2 = vertexValues[j]) ? 1 410 : (v1 < v) == (v < v2) ? (v - v1) / (v2 - v1) : Float.NaN); 411 } 412 getContourPoint(T3[] vertices, int i, int j, float f)413 private static P3 getContourPoint(T3[] vertices, int i, int j, 414 float f) { 415 P3 pt = new P3(); 416 pt.sub2(vertices[j], vertices[i]); 417 pt.scaleAdd2(f, pt, vertices[i]); 418 return pt; 419 } 420 421 float[] contourValues; 422 short[] contourColixes; 423 public ColorEncoder colorEncoder; 424 425 BS bsVdw; 426 public boolean colorPhased; 427 public float[] probeValues; 428 setDiscreteColixes(float[] values, short[] colixes)429 public void setDiscreteColixes(float[] values, short[] colixes) { 430 if (values != null) 431 jvxlData.contourValues = values; 432 if (values == null || values.length == 0) 433 values = jvxlData.contourValues = jvxlData.contourValuesUsed; 434 if (colixes == null && jvxlData.contourColixes != null) { 435 colixes = jvxlData.contourColixes; 436 } else { 437 jvxlData.contourColixes = colixes; 438 jvxlData.contourColors = C.getHexCodes(colixes); 439 } 440 if (vs == null || vvs == null || values == null) 441 return; 442 int n = values.length; 443 float vMax = values[n - 1]; 444 colorCommand = null; 445 boolean haveColixes = (colixes != null && colixes.length > 0); 446 isColorSolid = (haveColixes && jvxlData.jvxlPlane != null); 447 if (jvxlData.vContours != null) { 448 if (haveColixes) 449 for (int i = 0; i < jvxlData.vContours.length; i++) { 450 short colix = colixes[i % colixes.length]; 451 ((short[]) jvxlData.vContours[i].get(JvxlCoder.CONTOUR_COLIX))[0] = colix; 452 ((int[]) jvxlData.vContours[i].get(JvxlCoder.CONTOUR_COLOR))[0] = C 453 .getArgb(colix); 454 } 455 return; 456 } 457 short defaultColix = 0; 458 pcs = new short[pc]; 459 colorsExplicit = false; 460 for (int i = 0; i < pc; i++) { 461 int[] p = pis[i]; 462 if (p == null) 463 continue; 464 pcs[i] = defaultColix; 465 float v = (vvs[p[0]] + vvs[p[1]] + vvs[p[2]]) / 3; 466 for (int j = n; --j >= 0;) { 467 if (v >= values[j] && v < vMax) { 468 pcs[i] = (haveColixes ? colixes[j % colixes.length] : 0); 469 break; 470 } 471 } 472 } 473 } 474 475 /** 476 * 477 * @param vwr 478 * @return a Hashtable containing "values" and "colors" 479 * 480 */ getContourList(Viewer vwr)481 Map<String, Object> getContourList(Viewer vwr) { 482 Map<String, Object> ht = new Hashtable<String, Object>(); 483 ht.put("values", 484 (jvxlData.contourValuesUsed == null ? jvxlData.contourValues 485 : jvxlData.contourValuesUsed)); 486 Lst<P3> colors = new Lst<P3>(); 487 if (jvxlData.contourColixes != null) { 488 // set in SurfaceReader.colorData() 489 for (int i = 0; i < jvxlData.contourColixes.length; i++) { 490 colors.addLast(CU.colorPtFromInt(C 491 .getArgb(jvxlData.contourColixes[i]), null)); 492 } 493 ht.put("colors", colors); 494 } 495 return ht; 496 } 497 deleteContours()498 void deleteContours() { 499 jvxlData.contourValuesUsed = null; 500 jvxlData.contourValues = null; 501 jvxlData.contourColixes = null; 502 jvxlData.vContours = null; 503 } 504 setVertexColorMap()505 void setVertexColorMap() { 506 vertexColorMap = new Hashtable<String, BS>(); 507 short lastColix = -999; 508 BS bs = null; 509 for (int i = vc; --i >= 0;) { 510 short c = vcs[i]; 511 if (c != lastColix) { 512 String color = C.getHexCode(lastColix = c); 513 bs = vertexColorMap.get(color); 514 if (bs == null) 515 vertexColorMap.put(color, bs = new BS()); 516 } 517 bs.set(i); 518 } 519 } 520 setVertexColixesForAtoms(Viewer vwr, short[] colixes, int[] atomMap, BS bs)521 void setVertexColixesForAtoms(Viewer vwr, short[] colixes, int[] atomMap, 522 BS bs) { 523 jvxlData.vertexDataOnly = true; 524 jvxlData.vertexColors = new int[vc]; 525 jvxlData.nVertexColors = vc; 526 Atom[] atoms = vwr.ms.at; 527 GData gdata = vwr.gdata; 528 for (int i = mergeVertexCount0; i < vc; i++) { 529 int iAtom = vertexSource[i]; 530 if (iAtom < 0 || !bs.get(iAtom)) 531 continue; 532 jvxlData.vertexColors[i] = gdata.getColorArgbOrGray(vcs[i] = C 533 .copyColixTranslucency(colix, atoms[iAtom].colixAtom)); 534 535 short colix = (colixes == null ? C.INHERIT_ALL : colixes[atomMap[iAtom]]); 536 if (colix == C.INHERIT_ALL) 537 colix = atoms[iAtom].colixAtom; 538 vcs[i] = C.copyColixTranslucency(this.colix, colix); 539 } 540 } 541 542 /** 543 * color a specific set of vertices a specific color 544 * 545 * @param colix 546 * @param bs 547 * @param isAtoms 548 */ colorVertices(short colix, BS bs, boolean isAtoms)549 void colorVertices(short colix, BS bs, boolean isAtoms) { 550 if (vertexSource == null) 551 return; 552 colix = C.copyColixTranslucency(this.colix, colix); 553 BS bsVertices = (isAtoms ? new BS() : bs); 554 checkAllocColixes(); 555 // TODO: color translucency? 556 if (isAtoms) 557 for (int i = 0; i < vc; i++) { 558 int pt = vertexSource[i]; 559 if (pt >= 0 && bs.get(pt)) { 560 vcs[i] = colix; 561 if (bsVertices != null) 562 bsVertices.set(i); 563 } 564 } 565 else 566 for (int i = 0; i < vc; i++) 567 if (bsVertices.get(i)) 568 vcs[i] = colix; 569 570 if (!isAtoms) { 571 // JVXL file color maps do not have to be saved here. 572 // They are just kept in jvxlData 573 return; 574 } 575 String color = C.getHexCode(colix); 576 if (vertexColorMap == null) 577 vertexColorMap = new Hashtable<String, BS>(); 578 addColorToMap(vertexColorMap, color, bs); 579 } 580 checkAllocColixes()581 void checkAllocColixes() { 582 if (vcs == null || vertexColorMap == null && isColorSolid) 583 allocVertexColixes(); 584 isColorSolid = false; 585 } 586 587 /** 588 * adds a set of specifically-colored vertices to the map, 589 * ensuring that no vertex is in two maps. 590 * 591 * @param colorMap 592 * @param color 593 * @param bs 594 */ addColorToMap(Map<String, BS> colorMap, String color, BS bs)595 private static void addColorToMap(Map<String, BS> colorMap, String color, 596 BS bs) { 597 BS bsMap = null; 598 for (Map.Entry<String, BS> entry : colorMap.entrySet()) 599 if (entry.getKey() == color) { 600 bsMap = entry.getValue(); 601 bsMap.or(bs); 602 } else { 603 entry.getValue().andNot(bs); 604 } 605 if (bsMap == null) 606 colorMap.put(color, bs); 607 } 608 609 /** 610 * set up the jvxlData fields needed for either just the 611 * header (isAll = false) or the full file (isAll = true) 612 * 613 * @param isAll 614 */ setJvxlColorMap(boolean isAll)615 void setJvxlColorMap(boolean isAll) { 616 jvxlData.diameter = diameter; 617 jvxlData.color = C.getHexCode(colix); 618 jvxlData.meshColor = (meshColix == 0 ? null : C.getHexCode(meshColix)); 619 jvxlData.translucency = ((colix & C.TRANSLUCENT_SCREENED) == C.TRANSLUCENT_SCREENED ? -1 : C.getColixTranslucencyFractional(colix)); 620 jvxlData.rendering = getRendering().substring(1); 621 jvxlData.colorScheme = (colorEncoder == null ? null : colorEncoder 622 .getColorScheme()); 623 if (jvxlData.vertexColors == null) 624 jvxlData.nVertexColors = (vertexColorMap == null ? 0 : vertexColorMap 625 .size()); 626 if (vertexColorMap == null || vertexSource == null || !isAll) 627 return; 628 if (jvxlData.vertexColorMap == null) 629 jvxlData.vertexColorMap = new Hashtable<String, BS>(); 630 for (Map.Entry<String, BS> entry : vertexColorMap.entrySet()) { 631 BS bsMap = entry.getValue(); 632 if (bsMap.isEmpty()) 633 continue; 634 String color = entry.getKey(); 635 BS bs = new BS(); 636 for (int i = 0; i < vc; i++) 637 if (bsMap.get(vertexSource[i])) 638 bs.set(i); 639 addColorToMap(jvxlData.vertexColorMap, color, bs); 640 } 641 jvxlData.nVertexColors = jvxlData.vertexColorMap.size(); 642 if (jvxlData.vertexColorMap.size() == 0) 643 jvxlData.vertexColorMap = null; 644 } 645 646 /** 647 * just sets the color command for this isosurface. 648 */ setColorCommand()649 void setColorCommand() { 650 if (colorEncoder == null || (colorCommand = colorEncoder.getColorScheme()) == null) 651 return; 652 if (colorCommand.equals("inherit")) { 653 colorCommand = "#inherit;"; 654 return; 655 } 656 colorCommand = "color $" + PT.esc(thisID) + PT.esc(colorCommand) + " range " 657 + (jvxlData.isColorReversed ? jvxlData.valueMappedToBlue + " " 658 + jvxlData.valueMappedToRed : jvxlData.valueMappedToRed + " " 659 + jvxlData.valueMappedToBlue); 660 } 661 662 663 /** 664 * from Isosurface.notifySurfaceGenerationCompleted() 665 * 666 * starting with Jmol 12.1.50, JVXL files contain color, translucency, color 667 * scheme information, and vertex color mappings (as from COLOR ISOSURFACE 668 * {hydrophobic} WHITE), returning these settings when the JVXL file is 669 * opened. 670 * 671 * @param colorRgb 672 * @return true if still need color handling 673 */ setColorsFromJvxlData(int colorRgb)674 boolean setColorsFromJvxlData(int colorRgb) { 675 diameter = jvxlData.diameter; 676 if (colorRgb == -1) { 677 } else if (colorRgb != Integer.MIN_VALUE && colorRgb != Integer.MAX_VALUE) { 678 // max value set when second color option given in isosurface command 679 colix = C.getColix(colorRgb); 680 } else if (jvxlData.color != null) { 681 colix = C.getColixS(jvxlData.color); 682 } 683 if (colix == 0) 684 colix = C.ORANGE; 685 colix = C.getColixTranslucent3(colix, jvxlData.translucency != 0, 686 jvxlData.translucency); 687 float translucencyLevel = (jvxlData.translucency == 0 ? Float.NaN 688 : jvxlData.translucency); 689 if (jvxlData.meshColor != null) 690 meshColix = C.getColixS(jvxlData.meshColor); 691 setJvxlDataRendering(); 692 693 isColorSolid = !jvxlData.isBicolorMap && jvxlData.vertexColors == null 694 && jvxlData.vertexColorMap == null; 695 if (colorEncoder == null) 696 return false; 697 // bicolor map will be taken care of with params.isBicolorMap 698 if (jvxlData.vertexColorMap == null) { 699 if (jvxlData.colorScheme != null) { 700 String colorScheme = jvxlData.colorScheme; 701 boolean isTranslucent = colorScheme.startsWith("translucent "); 702 if (isTranslucent) { 703 colorScheme = colorScheme.substring(12); 704 translucencyLevel = Float.NaN; 705 } 706 colorEncoder.setColorScheme(colorScheme, isTranslucent); 707 remapColors(null, null, translucencyLevel); 708 } 709 } else { 710 if (jvxlData.baseColor != null) { 711 for (int i = vc; --i >= 0;) 712 vcs[i] = colix; 713 } 714 for (Map.Entry<String, BS> entry : jvxlData.vertexColorMap.entrySet()) { 715 BS bsMap = entry.getValue(); 716 short colix = C.copyColixTranslucency(this.colix, 717 C.getColixS(entry.getKey())); 718 for (int i = bsMap.nextSetBit(0); i >= 0; i = bsMap.nextSetBit(i + 1)) 719 vcs[i] = colix; 720 } 721 } 722 return true; 723 } 724 setJvxlDataRendering()725 void setJvxlDataRendering() { 726 if (jvxlData.rendering != null) { 727 String[] tokens = PT.getTokens(jvxlData.rendering); 728 for (int i = 0; i < tokens.length; i++) 729 setTokenProperty(T.getTokFromName(tokens[i]), true); 730 } 731 } 732 733 /** 734 * remaps colors based on a new color scheme or translucency level 735 * @param vwr 736 * 737 * @param ce 738 * @param translucentLevel 739 */ remapColors(Viewer vwr, ColorEncoder ce, float translucentLevel)740 void remapColors(Viewer vwr, ColorEncoder ce, float translucentLevel) { 741 if (ce == null) 742 ce = colorEncoder; 743 if (ce == null) 744 ce = colorEncoder = new ColorEncoder(null, vwr); 745 colorEncoder = ce; 746 setColorCommand(); 747 if (Float.isNaN(translucentLevel)) { 748 translucentLevel = C.getColixTranslucencyLevel(colix); 749 } else { 750 colix = C.getColixTranslucent3(colix, true, translucentLevel); 751 } 752 float min = ce.lo; 753 float max = ce.hi; 754 boolean inherit = (vertexSource != null && ce.currentPalette == ColorEncoder.INHERIT); 755 vertexColorMap = null; 756 pcs = null; 757 colorsExplicit = false; 758 jvxlData.baseColor = null; 759 jvxlData.vertexCount = vc; 760 if (vvs == null || jvxlData.vertexCount == 0) 761 return; 762 if (vcs == null || vcs.length != vc) 763 allocVertexColixes(); 764 if (inherit) { 765 jvxlData.vertexDataOnly = true; 766 jvxlData.vertexColors = new int[vc]; 767 jvxlData.nVertexColors = vc; 768 Atom[] atoms = vwr.ms.at; 769 GData gdata = vwr.gdata; 770 for (int i = mergeVertexCount0; i < vc; i++) { 771 int pt = vertexSource[i]; 772 if (pt >= 0 && pt < atoms.length) 773 jvxlData.vertexColors[i] = gdata.getColorArgbOrGray(vcs[i] = C.copyColixTranslucency(colix, 774 atoms[pt].colixAtom)); 775 } 776 return; 777 } 778 jvxlData.vertexColors = null; 779 jvxlData.vertexColorMap = null; 780 if (jvxlData.isBicolorMap) { 781 for (int i = mergeVertexCount0; i < vc; i++) 782 vcs[i] = C.copyColixTranslucency(colix, 783 vvs[i] < 0 ? jvxlData.minColorIndex 784 : jvxlData.maxColorIndex); 785 return; 786 } 787 jvxlData.isColorReversed = ce.isReversed; 788 if (max != Float.MAX_VALUE) { 789 jvxlData.valueMappedToRed = min; 790 jvxlData.valueMappedToBlue = max; 791 } 792 ce.setRange(jvxlData.valueMappedToRed, jvxlData.valueMappedToBlue, 793 jvxlData.isColorReversed); 794 // colix must be translucent if the scheme is translucent 795 // but may be translucent if the scheme is not translucent 796 boolean isTranslucent = C.isColixTranslucent(colix); 797 if (ce.isTranslucent) { 798 if (!isTranslucent) 799 colix = C.getColixTranslucent3(colix, true, 0.5f); 800 // still, if the scheme is translucent, we don't want to color the vertices translucent 801 isTranslucent = false; 802 } 803 vcs = AU.ensureLengthShort(vcs, vc); // when reading contour plane may be remapped 804 for (int i = vc; --i >= mergeVertexCount0;) 805 vcs[i] = ce.getColorIndex(vvs[i]); 806 setTranslucent(isTranslucent, translucentLevel); 807 colorEncoder = ce; 808 Lst<Object>[] contours = getContours(); 809 if (contours != null) { 810 for (int i = contours.length; --i >= 0;) { 811 float value = ((Float) contours[i].get(JvxlCoder.CONTOUR_VALUE)) 812 .floatValue(); 813 short[] colix = ((short[]) contours[i].get(JvxlCoder.CONTOUR_COLIX)); 814 colix[0] = ce.getColorIndex(value); 815 int[] color = ((int[]) contours[i].get(JvxlCoder.CONTOUR_COLOR)); 816 color[0] = C.getArgb(colix[0]); 817 } 818 } 819 if (contourValues != null) { 820 contourColixes = new short[contourValues.length]; 821 for (int i = 0; i < contourValues.length; i++) 822 contourColixes[i] = ce.getColorIndex(contourValues[i]); 823 setDiscreteColixes(null, null); 824 } 825 jvxlData.isJvxlPrecisionColor = true; 826 JvxlCoder.jvxlCreateColorData(jvxlData, vvs); 827 setColorCommand(); 828 isColorSolid = false; 829 } 830 reinitializeLightingAndColor(Viewer vwr)831 public void reinitializeLightingAndColor(Viewer vwr) { 832 initialize(lighting, null, null); 833 if (colorEncoder != null || jvxlData.isBicolorMap) { 834 vcs = null; 835 remapColors(vwr, null, Float.NaN); 836 } 837 } 838 839 @Override getBoundingBox()840 public P3[] getBoundingBox() { 841 return jvxlData.boundingBox; 842 } 843 844 @Override setBoundingBox(P3[] pts)845 public void setBoundingBox(P3[] pts) { 846 jvxlData.boundingBox = pts; 847 } 848 849 //private void dumpData() { 850 //for (int i =0;i<10;i++) { 851 // System.out.println("P["+i+"]="+polygonIndexes[i][0]+" "+polygonIndexes[i][1]+" "+polygonIndexes[i][2]+" "+ polygonIndexes[i][3]+" "+vertices[i]); 852 //} 853 //} 854 merge(MeshData m)855 protected void merge(MeshData m) { 856 int nV = vc + (m == null ? 0 : m.vc); 857 if (pis == null) 858 pis = new int[0][0]; 859 if (m != null && m.pis == null) 860 m.pis = new int[0][0]; 861 int nP = (bsSlabDisplay == null || pc == 0 ? pc : bsSlabDisplay 862 .cardinality()) 863 + (m == null || m.pc == 0 ? 0 : m.bsSlabDisplay == null ? m.pc 864 : m.bsSlabDisplay.cardinality()); 865 if (vs == null) 866 vs = new P3[0]; 867 vs = (T3[]) AU.ensureLength(vs, nV); 868 vvs = AU.ensureLengthA(vvs, nV); 869 boolean haveSources = (vertexSource != null && (m == null || m.vertexSource != null)); 870 vertexSource = AU.ensureLengthI(vertexSource, nV); 871 int[][] newPolygons = AU.newInt2(nP); 872 // note -- no attempt here to merge vertices 873 int ipt = mergePolygons(this, 0, 0, newPolygons); 874 if (m != null) { 875 ipt = mergePolygons(m, ipt, vc, newPolygons); 876 for (int i = 0; i < m.vc; i++, vc++) { 877 vs[vc] = m.vs[i]; 878 vvs[vc] = m.vvs[i]; 879 if (haveSources) 880 vertexSource[vc] = m.vertexSource[i]; 881 } 882 } 883 pc = polygonCount0 = nP; 884 vc = vertexCount0 = nV; 885 if (nP > 0) 886 resetSlab(); 887 pis = newPolygons; 888 } 889 mergePolygons(MeshSurface m, int ipt, int vertexCount, int[][] newPolygons)890 private static int mergePolygons(MeshSurface m, int ipt, int vertexCount, int[][] newPolygons) { 891 int[] p; 892 for (int i = 0; i < m.pc; i++) { 893 if ((p = m.pis[i]) == null || m.bsSlabDisplay != null && !m.bsSlabDisplay.get(i)) 894 continue; 895 newPolygons[ipt++] = m.pis[i]; 896 if (vertexCount > 0) 897 for (int j = 0; j < 3; j++) 898 p[j] += vertexCount; 899 } 900 //System.out.println("isosurfaceMesh mergePolygons " + m.polygonCount + " " + m.polygonIndexes.length); 901 return ipt; 902 } 903 904 @Override getUnitCell()905 public SymmetryInterface getUnitCell() { 906 return (unitCell != null 907 || (unitCell = vwr.ms.am[modelIndex].biosymmetry) != null 908 || (unitCell = vwr.ms.getUnitCell(modelIndex)) != null 909 || oabc != null && (unitCell = Interface.getSymmetry(vwr, "symmetry").getUnitCell( 910 oabc, true, null)) != null ? unitCell : null); 911 } 912 fixLattice()913 void fixLattice() { 914 if (getUnitCell() == null) 915 return; 916 P3i minXYZ = new P3i(); 917 P3i maxXYZ = P3i.new3((int) lattice.x, (int) lattice.y, (int) lattice.z); 918 jvxlData.fixedLattice = lattice; 919 lattice = null; 920 SimpleUnitCell.setMinMaxLatticeParameters((int) unitCell.getUnitCellInfoType(SimpleUnitCell.INFO_DIMENSIONS), minXYZ, maxXYZ, 0); 921 int nCells = (maxXYZ.x - minXYZ.x) * (maxXYZ.y - minXYZ.y) 922 * (maxXYZ.z - minXYZ.z); 923 P3 latticeOffset = new P3(); 924 int vc0 = vc; 925 int vcNew = nCells * vc; 926 vs = AU.arrayCopyPt(vs, vcNew); 927 vvs = (vvs == null ? null : AU.ensureLengthA(vvs, vcNew)); 928 int pc0 = pc; 929 int pcNew = nCells * pc; 930 pis = AU.arrayCopyII(pis, pcNew); 931 int off = 0; 932 normixes = AU.arrayCopyShort(normixes, vcNew); 933 for (int tx = minXYZ.x; tx < maxXYZ.x; tx++) 934 for (int ty = minXYZ.y; ty < maxXYZ.y; ty++) 935 for (int tz = minXYZ.z; tz < maxXYZ.z; tz++) { 936 if (tx == 0 && ty == 0 && tz == 0) 937 continue; 938 latticeOffset.set(tx, ty, tz); 939 unitCell.toCartesian(latticeOffset, false); 940 for (int i = 0; i < vc0; i++) { 941 normixes[vc] = normixes[i]; 942 P3 v = P3.newP(vs[i]); 943 v.add(latticeOffset); 944 addVCVal(v, vvs[i], false); 945 } 946 off += vc0; 947 for (int i = 0; i < pc0; i++) { 948 int[] p = AU.arrayCopyI(pis[i], -1); 949 p[0] += off; 950 p[1] += off; 951 p[2] += off; 952 addPolygon(p, null); 953 } 954 } 955 P3 xyzMin = new P3(); 956 P3 xyzMax = new P3(); 957 setBox(xyzMin, xyzMax); 958 jvxlData.boundingBox = new P3[] { xyzMin, xyzMax }; 959 960 } 961 962 @Override getMinDistance2ForVertexGrouping()963 protected float getMinDistance2ForVertexGrouping() { 964 if (jvxlData.boundingBox != null && jvxlData.boundingBox[0] != null) { 965 float d2 = jvxlData.boundingBox[1] 966 .distanceSquared(jvxlData.boundingBox[0]); 967 if (d2 < 5) 968 return 1e-10f; 969 } 970 return 1e-8f; 971 } 972 973 @Override getVisibleVertexBitSet()974 public BS getVisibleVertexBitSet() { 975 BS bs = getVisibleVBS(); 976 if (jvxlData.thisSet != null) 977 for (int i = 0; i < vc; i++) 978 if (!jvxlData.thisSet.get(vertexSets[i])) 979 bs.clear(i); 980 return bs; 981 } 982 983 /** 984 * 985 * bs will be null if this is a set from the new isosurface MOVE [mat4] 986 * command 987 * 988 * @param m 989 * @param bs 990 */ updateCoordinates(M4 m, BS bs)991 public void updateCoordinates(M4 m, BS bs) { 992 boolean doUpdate = (bs == null || isModelConnected); 993 if (!doUpdate) 994 for (int i = 0; i < connectedAtoms.length; i++) 995 if (connectedAtoms[i] >= 0 && bs.get(connectedAtoms[i])) { 996 doUpdate = true; 997 break; 998 } 999 if (!doUpdate) 1000 return; 1001 if (isModelConnected) { 1002 mat4 = vwr.ms.am[modelIndex].mat4; 1003 } else { 1004 if (mat4 == null) 1005 mat4 = M4.newM4(null); 1006 mat4.mul2(m, mat4); 1007 } 1008 recalcAltVertices = true; 1009 } 1010 getDataMinMax()1011 float[] getDataMinMax() { 1012 float min = Float.MAX_VALUE; 1013 float max = Float.MIN_VALUE; 1014 for (int i = vvs.length; --i >= 0;) { 1015 float v = vvs[i]; 1016 if (v < min) 1017 min = v; 1018 if (v > max) 1019 max = v; 1020 } 1021 return new float[] { min, max }; 1022 } 1023 getDataRange()1024 float[] getDataRange() { 1025 return (jvxlData.jvxlPlane != null 1026 && colorEncoder == null ? null : new float[] { 1027 jvxlData.mappedDataMin, 1028 jvxlData.mappedDataMax, 1029 (jvxlData.isColorReversed ? jvxlData.valueMappedToBlue 1030 : jvxlData.valueMappedToRed), 1031 (jvxlData.isColorReversed ? jvxlData.valueMappedToRed 1032 : jvxlData.valueMappedToBlue) }); 1033 } 1034 1035 @Override getInfo(boolean isAll)1036 public Object getInfo(boolean isAll) { 1037 @SuppressWarnings("unchecked") 1038 Hashtable<String, Object> info = (Hashtable<String, Object>) super.getInfo( 1039 isAll); 1040 if (isAll) { 1041 BS bs = new BS(); 1042 T3[] valid = getValidVertices(bs); 1043 if (valid != null) { 1044 info.put("allVertices", info.get("vertices")); 1045 info.put("vertices", valid); 1046 Object values = info.get("vertexValues"); 1047 if (values != null) { 1048 float[] v = getValidValues(bs); 1049 info.put("allValues", values); 1050 info.put("vertexValues", v); 1051 } 1052 } 1053 } 1054 return info; 1055 } 1056 getValidValues(BS bs)1057 public float[] getValidValues(BS bs) { 1058 if (bs == null) 1059 getInvalidBS(bs = new BS()); 1060 int n = vc - bs.cardinality(); 1061 float[] v = new float[n]; 1062 for (int pt = 0, i = bs.nextClearBit(0); i >= 0 && i < vc; i = bs.nextClearBit(i + 1)) 1063 v[pt++] = vvs[i]; 1064 return v; 1065 } 1066 getValidVertices(BS bs)1067 public T3[] getValidVertices(BS bs) { 1068 boolean allowNull = (bs != null); 1069 if (bs == null) 1070 bs = new BS(); 1071 getInvalidBS(bs); 1072 int n = vc - bs.cardinality(); 1073 if (n == vc && allowNull) { 1074 return null; 1075 } 1076 T3[] pa = new P3[n]; 1077 for (int pt = 0, i = bs.nextClearBit(0); i >= 0 1078 && pt < n; i = bs.nextClearBit(i + 1)) { 1079 pa[pt++] = vs[i]; 1080 } 1081 return pa; 1082 } 1083 getInvalidBS(BS bs)1084 private void getInvalidBS(BS bs) { 1085 BS[] excluded = jvxlData.jvxlExcluded; 1086 BS invalid = excluded[1]; 1087 BS thisSet = jvxlData.thisSet; 1088 if (invalid != null) 1089 bs.or(invalid); 1090 if (thisSet != null) { 1091 for (int i = vc; --i >= 0;) { 1092 if (!thisSet.get(vertexSets[i])) 1093 bs.set(i); 1094 } 1095 } 1096 } 1097 } 1098