1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2017-05-29 21:30:21 -0500 (Mon, 29 May 2017) $ 4 * $Revision: 21625 $ 5 6 * 7 * Copyright (C) 2002-2005 The Jmol Development Team 8 * 9 * Contact: jmol-developers@lists.sf.net 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 24 */ 25 26 package org.jmol.render; 27 28 import org.jmol.c.PAL; 29 import javajs.util.BS; 30 import org.jmol.modelset.Atom; 31 import org.jmol.modelset.Bond; 32 import org.jmol.script.T; 33 import org.jmol.util.C; 34 import org.jmol.util.GData; 35 import org.jmol.util.Edge; 36 37 import javajs.util.A4; 38 import javajs.util.M3; 39 import javajs.util.P3; 40 import javajs.util.V3; 41 import org.jmol.viewer.JC; 42 43 public class SticksRenderer extends FontLineShapeRenderer { 44 45 private boolean showMultipleBonds; 46 private float multipleBondSpacing; 47 private float multipleBondRadiusFactor; 48 private boolean bondsPerp; 49 private boolean useBananas; 50 private byte modeMultipleBond; 51 private boolean isCartesian; 52 //boolean showHydrogens; 53 private byte endcaps; 54 55 private boolean ssbondsBackbone; 56 private boolean hbondsBackbone; 57 private boolean bondsBackbone; 58 private boolean hbondsSolid; 59 60 private Atom a, b; 61 private Bond bond; 62 private int xA, yA, zA; 63 private int xB, yB, zB; 64 private int dx, dy; 65 private int mag2d; 66 private int bondOrder; 67 private boolean wireframeOnly; 68 private boolean isAntialiased; 69 private boolean slabbing; 70 private boolean slabByAtom; 71 72 private final V3 x = new V3(); 73 private final V3 y = new V3(); 74 private final V3 z = new V3(); 75 private final P3 p1 = new P3(); 76 private final P3 p2 = new P3(); 77 private final BS bsForPass2 = BS.newN(64); 78 private boolean isPass2; 79 private double rTheta; 80 81 @Override render()82 protected boolean render() { 83 Bond[] bonds = ms.bo; 84 if (bonds == null) 85 return false; 86 isPass2 = vwr.gdata.isPass2; 87 if (!isPass2) 88 bsForPass2.clearAll(); 89 slabbing = tm.slabEnabled; 90 slabByAtom = vwr.getBoolean(T.slabbyatom); 91 endcaps = GData.ENDCAPS_SPHERICAL; 92 dashDots = (vwr.getBoolean(T.partialdots) ? sixdots : dashes); 93 isCartesian = (exportType == GData.EXPORT_CARTESIAN); 94 getMultipleBondSettings(false); 95 wireframeOnly = !vwr.checkMotionRendering(T.bonds); 96 ssbondsBackbone = vwr.getBoolean(T.ssbondsbackbone); 97 hbondsBackbone = vwr.getBoolean(T.hbondsbackbone); 98 bondsBackbone = hbondsBackbone | ssbondsBackbone; 99 hbondsSolid = vwr.getBoolean(T.hbondssolid); 100 isAntialiased = g3d.isAntialiased(); 101 boolean needTranslucent = false; 102 if (isPass2) { 103 if (!isExport) 104 for (int i = bsForPass2.nextSetBit(0); i >= 0; i = bsForPass2 105 .nextSetBit(i + 1)) { 106 bond = bonds[i]; 107 renderBond(); 108 } 109 } else { 110 for (int i = ms.bondCount; --i >= 0;) { 111 bond = bonds[i]; 112 if ((bond.shapeVisibilityFlags & myVisibilityFlag) != 0 && renderBond()) { 113 needTranslucent = true; 114 bsForPass2.set(i); 115 } 116 } 117 } 118 return needTranslucent; 119 } 120 getMultipleBondSettings(boolean isPymol)121 private void getMultipleBondSettings(boolean isPymol) { 122 useBananas = (vwr.getBoolean(T.multiplebondbananas) && !isPymol); 123 // negative spacing is relative, depending upon atom-atom distance; 124 // positive spacing is absolute, for fixed in-plane (radiusFactor > 0) or perp-plane (radiusFactor < 0) 125 multipleBondSpacing = (isPymol ? 0.15f : vwr 126 .getFloat(T.multiplebondspacing)); 127 // negative radius factor indicates perpendicular fixed double bond 128 multipleBondRadiusFactor = (isPymol ? 0.4f : vwr 129 .getFloat(T.multiplebondradiusfactor)); 130 bondsPerp = (useBananas || multipleBondSpacing > 0 131 && multipleBondRadiusFactor < 0); 132 if (useBananas) 133 multipleBondSpacing = (multipleBondSpacing < 0 ? -multipleBondSpacing * 0.4f 134 : multipleBondSpacing); 135 multipleBondRadiusFactor = Math.abs(multipleBondRadiusFactor); 136 if (multipleBondSpacing == 0 && isCartesian) 137 multipleBondSpacing = 0.2f; 138 modeMultipleBond = vwr.g.modeMultipleBond; 139 showMultipleBonds = (multipleBondSpacing != 0 140 && modeMultipleBond != JC.MULTIBOND_NEVER && vwr 141 .getBoolean(T.showmultiplebonds)); 142 } 143 renderBond()144 private boolean renderBond() { 145 Atom atomA0, atomB0; 146 147 a = atomA0 = bond.atom1; 148 b = atomB0 = bond.atom2; 149 150 int order = bond.order & ~Edge.BOND_NEW; 151 if (bondsBackbone) { 152 if (ssbondsBackbone && (order & Edge.BOND_SULFUR_MASK) != 0) { 153 // for ssbonds, always render the sidechain, 154 // then render the backbone version 155 /* 156 mth 2004 04 26 157 No, we are not going to do this any more 158 render(bond, atomA, atomB); 159 */ 160 161 a = a.group.getLeadAtomOr(a); 162 b = b.group.getLeadAtomOr(b); 163 } else if (hbondsBackbone && Edge.isOrderH(order)) { 164 a = a.group.getLeadAtomOr(a); 165 b = b.group.getLeadAtomOr(b); 166 } 167 } 168 if (!isPass2 169 && (!a.isVisible(Atom.ATOM_INFRAME_NOTHIDDEN) 170 || !b.isVisible(Atom.ATOM_INFRAME_NOTHIDDEN) 171 || !g3d.isInDisplayRange(a.sX, a.sY) || !g3d.isInDisplayRange(b.sX, 172 b.sY))) 173 return false; 174 175 if (slabbing) { 176 boolean ba = vwr.gdata.isClippedZ(a.sZ); 177 if (ba && vwr.gdata.isClippedZ(b.sZ) || slabByAtom 178 && (ba || vwr.gdata.isClippedZ(b.sZ))) 179 return false; 180 } 181 zA = a.sZ; 182 zB = b.sZ; 183 if (zA == 1 || zB == 1) 184 return false; 185 colixA = atomA0.colixAtom; 186 colixB = atomB0.colixAtom; 187 if (((colix = bond.colix) & C.OPAQUE_MASK) == C.USE_PALETTE) { 188 colix = (short) (colix & ~C.OPAQUE_MASK); 189 colixA = C.getColixInherited( 190 (short) (colix | vwr.cm.getColixAtomPalette(atomA0, PAL.CPK.id)), 191 colixA); 192 colixB = C.getColixInherited( 193 (short) (colix | vwr.cm.getColixAtomPalette(atomB0, PAL.CPK.id)), 194 colixB); 195 } else { 196 colixA = C.getColixInherited(colix, colixA); 197 colixB = C.getColixInherited(colix, colixB); 198 } 199 boolean needTranslucent = false; 200 if (!isExport && !isPass2) { 201 boolean doA = !C.renderPass2(colixA); 202 boolean doB = !C.renderPass2(colixB); 203 if (!doA || !doB) { 204 if (!doA && !doB && !needTranslucent) { 205 g3d.setC(!doA ? colixA : colixB); 206 return true; 207 } 208 needTranslucent = true; 209 } 210 } 211 212 // set the rendered bond order 213 214 bondOrder = order & ~Edge.BOND_NEW; 215 if ((bondOrder & Edge.BOND_PARTIAL_MASK) == 0) { 216 if ((bondOrder & Edge.BOND_SULFUR_MASK) != 0) 217 bondOrder &= ~Edge.BOND_SULFUR_MASK; 218 if ((bondOrder & Edge.BOND_COVALENT_MASK) != 0) { 219 if (!showMultipleBonds 220 || (modeMultipleBond == JC.MULTIBOND_NOTSMALL && mad > JC.madMultipleBondSmallMaximum) 221 || (bondOrder & Edge.BOND_PYMOL_MULT) == Edge.BOND_RENDER_SINGLE) { 222 bondOrder = 1; 223 } 224 } 225 } 226 227 // set the mask 228 229 int mask = 0; 230 switch (bondOrder) { 231 case Edge.BOND_STEREO_NEAR: 232 case Edge.BOND_STEREO_FAR: 233 bondOrder = 1; 234 //$FALL-THROUGH$ 235 case 1: 236 case 2: 237 case 3: 238 case 4: 239 case 5: 240 case 6: 241 break; 242 case Edge.BOND_ORDER_UNSPECIFIED: 243 case Edge.BOND_AROMATIC_SINGLE: 244 bondOrder = 1; 245 mask = (order == Edge.BOND_AROMATIC_SINGLE ? 0 : 1); 246 break; 247 case Edge.BOND_AROMATIC: 248 case Edge.BOND_AROMATIC_DOUBLE: 249 bondOrder = 2; 250 mask = (order == Edge.BOND_AROMATIC ? getAromaticDottedBondMask() : 0); 251 break; 252 default: 253 if ((bondOrder & Edge.BOND_PARTIAL_MASK) != 0) { 254 bondOrder = Edge.getPartialBondOrder(order); 255 mask = Edge.getPartialBondDotted(order); 256 } else if (Edge.isOrderH(bondOrder)) { 257 bondOrder = 1; 258 if (!hbondsSolid) 259 mask = -1; 260 } else if (bondOrder == Edge.BOND_STRUT) { 261 bondOrder = 1; 262 } else if ((bondOrder & Edge.BOND_PYMOL_MULT) == Edge.BOND_PYMOL_MULT) { 263 getMultipleBondSettings(true); 264 bondOrder &= 3; 265 mask = -2; 266 } 267 } 268 269 // set the diameter 270 271 xA = a.sX; 272 yA = a.sY; 273 xB = b.sX; 274 yB = b.sY; 275 276 mad = bond.mad; 277 if (multipleBondRadiusFactor > 0 && bondOrder > 1) 278 mad *= multipleBondRadiusFactor; 279 dx = xB - xA; 280 dy = yB - yA; 281 width = (int) vwr.tm.scaleToScreen((zA + zB) / 2, mad); 282 if (wireframeOnly && width > 0) 283 width = 1; 284 if (!isCartesian) { 285 asLineOnly = (width <= 1); 286 if (asLineOnly && (isAntialiased)) { 287 width = 3; 288 asLineOnly = false; 289 } 290 } 291 292 // draw the bond 293 294 switch (mask) { 295 case -2: 296 drawBond(0); 297 getMultipleBondSettings(false); 298 break; 299 case -1: 300 drawDashed(xA, yA, zA, xB, yB, zB, hDashes); 301 break; 302 default: 303 switch (bondOrder) { 304 case 4: { 305 bondOrder = 2; 306 float f = multipleBondRadiusFactor; 307 if (f == 0 && width > 1) 308 width = (int) (width * 0.5); 309 float m = multipleBondSpacing; 310 if (m < 0) 311 multipleBondSpacing = 0.30f; 312 drawBond(mask); 313 bondsPerp = !bondsPerp; 314 bondOrder = 2; 315 drawBond(mask >> 2); 316 bondsPerp = !bondsPerp; 317 multipleBondSpacing = m; 318 } 319 break; 320 case 5: { 321 bondOrder = 3; 322 float f = multipleBondRadiusFactor; 323 if (f == 0 && width > 1) 324 width = (int) (width * 0.5); 325 float m = multipleBondSpacing; 326 if (m < 0) 327 multipleBondSpacing = 0.20f; 328 drawBond(mask); 329 bondsPerp = !bondsPerp; 330 bondOrder = 2; 331 multipleBondSpacing *= 1.5f; 332 drawBond(mask >> 3); 333 bondsPerp = !bondsPerp; 334 multipleBondSpacing = m; 335 } 336 break; 337 case 6: { 338 bondOrder = 4; 339 float f = multipleBondRadiusFactor; 340 if (f == 0 && width > 1) 341 width = (int) (width * 0.5); 342 float m = multipleBondSpacing; 343 if (m < 0) 344 multipleBondSpacing = 0.15f; 345 drawBond(mask); 346 bondsPerp = !bondsPerp; 347 bondOrder = 2; 348 multipleBondSpacing *= 1.5f; 349 drawBond(mask >> 4); 350 bondsPerp = !bondsPerp; 351 multipleBondSpacing = m; 352 } 353 break; 354 default: 355 drawBond(mask); 356 } 357 break; 358 } 359 return needTranslucent; 360 } 361 drawBond(int dottedMask)362 private void drawBond(int dottedMask) { 363 boolean isDashed = (dottedMask & 1) != 0; 364 if (isCartesian && bondOrder == 1 && !isDashed) { 365 // bypass screen rendering and just use the atoms themselves 366 g3d.drawBond(a, b, colixA, colixB, endcaps, mad, -1); 367 return; 368 } 369 boolean isEndOn = (dx == 0 && dy == 0); 370 if (isEndOn && asLineOnly && !isCartesian) 371 return; 372 boolean doFixedSpacing = (bondOrder > 1 && multipleBondSpacing > 0); 373 boolean isPiBonded = doFixedSpacing 374 && (vwr.getHybridizationAndAxes(a.i, z, x, "pz") != null || vwr 375 .getHybridizationAndAxes(b.i, z, x, "pz") != null) 376 && !Float.isNaN(x.x); 377 if (isEndOn && !doFixedSpacing) { 378 // end-on view 379 int space = width / 8 + 3; 380 int step = width + space; 381 int y = yA - (bondOrder - 1) * step / 2; 382 do { 383 fillCylinder(colixA, colixB, endcaps, width, xA, y, zA, xB, y, zB); 384 y += step; 385 } while (--bondOrder > 0); 386 return; 387 } 388 if (bondOrder == 1) { 389 if (isDashed) 390 drawDashed(xA, yA, zA, xB, yB, zB, dashDots); 391 else 392 fillCylinder(colixA, colixB, endcaps, width, xA, yA, zA, xB, yB, zB); 393 return; 394 } 395 if (doFixedSpacing) { 396 if (!isPiBonded) // obscure point 397 z.setT(P3.getUnlikely()); 398 x.sub2(b, a); 399 y.cross(x, z); 400 y.normalize(); 401 if (Float.isNaN(y.x)) { 402 // in case x and z are parallel (O=C=O) 403 z.setT(P3.getUnlikely()); 404 y.cross(x, z); 405 y.cross(y, x); 406 y.normalize(); 407 } 408 if (bondsPerp) 409 y.cross(y, x); 410 y.scale(multipleBondSpacing); 411 x.setT(y); 412 x.scale((bondOrder - 1) / 2f); 413 if (useBananas) { 414 drawBanana(a, b, x, 0); 415 switch (bondOrder) { 416 case 4: 417 drawBanana(a, b, x, 90); 418 drawBanana(a, b, x, -90); 419 //$FALL-THROUGH$ 420 case 2: 421 default: 422 drawBanana(a, b, x, 180); 423 break; 424 case 3: 425 drawBanana(a, b, x, 120); 426 drawBanana(a, b, x, -120); 427 break; 428 } 429 return; 430 } 431 p1.sub2(a, x); 432 p2.sub2(b, x); 433 434 while (true) { 435 if (isCartesian) { 436 // bypass screen rendering and just use the atoms themselves 437 g3d.drawBond(p1, p2, colixA, colixB, endcaps, mad, -2); 438 } else { 439 tm.transformPtScr(p1, s1); 440 tm.transformPtScr(p2, s2); 441 if (isDashed) 442 drawDashed(s1.x, s1.y, s1.z, s2.x, s2.y, s2.z, dashDots); 443 else 444 fillCylinder(colixA, colixB, endcaps, width, s1.x, s1.y, s1.z, 445 s2.x, s2.y, s2.z); 446 } 447 dottedMask >>= 1; 448 isDashed = (dottedMask & 1) != 0; 449 if (--bondOrder <= 0) 450 break; 451 p1.add(y); 452 p2.add(y); 453 stepAxisCoordinates(); 454 } 455 return; 456 } 457 int dxB = dx * dx; 458 int dyB = dy * dy; 459 mag2d = (int) Math.round(Math.sqrt(dxB + dyB)); 460 resetAxisCoordinates(); 461 if (isCartesian && bondOrder == 3) { 462 fillCylinder(colixA, colixB, endcaps, width, xAxis1, yAxis1, zA, xAxis2, 463 yAxis2, zB); 464 stepAxisCoordinates(); 465 x.sub2(b, a); 466 x.scale(0.05f); 467 p1.sub2(a, x); 468 p2.add2(b, x); 469 g3d.drawBond(p1, p2, colixA, colixB, endcaps, mad, -2); 470 stepAxisCoordinates(); 471 fillCylinder(colixA, colixB, endcaps, width, xAxis1, yAxis1, zA, xAxis2, 472 yAxis2, zB); 473 return; 474 } 475 while (true) { 476 if ((dottedMask & 1) != 0) 477 drawDashed(xAxis1, yAxis1, zA, xAxis2, yAxis2, zB, dashDots); 478 else 479 fillCylinder(colixA, colixB, endcaps, width, xAxis1, yAxis1, zA, 480 xAxis2, yAxis2, zB); 481 dottedMask >>= 1; 482 if (--bondOrder <= 0) 483 break; 484 stepAxisCoordinates(); 485 } 486 } 487 488 private int xAxis1, yAxis1, xAxis2, yAxis2, dxStep, dyStep; 489 resetAxisCoordinates()490 private void resetAxisCoordinates() { 491 int space = mag2d >> 3; 492 if (multipleBondSpacing != -1 && multipleBondSpacing < 0) 493 space *= -multipleBondSpacing; 494 int step = width + space; 495 dxStep = step * dy / mag2d; 496 dyStep = step * -dx / mag2d; 497 xAxis1 = xA; 498 yAxis1 = yA; 499 xAxis2 = xB; 500 yAxis2 = yB; 501 int f = (bondOrder - 1); 502 xAxis1 -= dxStep * f / 2; 503 yAxis1 -= dyStep * f / 2; 504 xAxis2 -= dxStep * f / 2; 505 yAxis2 -= dyStep * f / 2; 506 } 507 stepAxisCoordinates()508 private void stepAxisCoordinates() { 509 xAxis1 += dxStep; 510 yAxis1 += dyStep; 511 xAxis2 += dxStep; 512 yAxis2 += dyStep; 513 } 514 getAromaticDottedBondMask()515 private int getAromaticDottedBondMask() { 516 Atom atomC = b.findAromaticNeighbor(a.i); 517 if (atomC == null) 518 return 1; 519 int dxAC = atomC.sX - xA; 520 int dyAC = atomC.sY - yA; 521 return ((dx * dyAC - dy * dxAC) < 0 ? 2 : 1); 522 } 523 524 private M3 rot; 525 private A4 a4; 526 drawBanana(Atom a, Atom b, V3 x, int deg)527 private void drawBanana(Atom a, Atom b, V3 x, int deg) { 528 g3d.addRenderer(T.hermitelevel); 529 vectorT.sub2(b, a); 530 if (rot == null) { 531 rot = new M3(); 532 a4 = new A4(); 533 } 534 a4.setVA(vectorT, (float) (deg * Math.PI / 180)); 535 rot.setAA(a4); 536 pointT.setT(a); 537 pointT3.setT(b); 538 pointT2.ave(a, b); 539 rot.rotate2(x, vectorT); 540 pointT2.add(vectorT); 541 tm.transformPtScrT3(a, pointT); 542 tm.transformPtScrT3(pointT2, pointT2); 543 tm.transformPtScrT3(b, pointT3); 544 int w = Math.max(width, 1); 545 g3d.setC(colixA); 546 g3d.fillHermite(5, w, w, w, pointT, pointT, pointT2, pointT3); 547 g3d.setC(colixB); 548 g3d.fillHermite(5, w, w, w, pointT, pointT2, pointT3, pointT3); 549 } 550 551 } 552