1 /* $RCSfile$ 2 * * $Author: hansonr $ 3 * $Date: 2019-11-17 22:49:25 -0600 (Sun, 17 Nov 2019) $ 4 * $Revision: 22002 $ 5 * 6 * Copyright (C) 2003-2006 Miguel, Jmol Development, www.jmol.org 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 package org.jmol.g3d; 25 26 import java.util.Arrays; 27 import java.util.Comparator; 28 import java.util.Map; 29 30 import org.jmol.api.GenericPlatform; 31 import org.jmol.api.Interface; 32 import org.jmol.api.JmolRendererInterface; 33 import org.jmol.c.STER; 34 import org.jmol.modelset.Atom; 35 import org.jmol.script.T; 36 import org.jmol.util.C; 37 import org.jmol.util.Font; 38 import org.jmol.util.GData; 39 import org.jmol.util.MeshSurface; 40 import org.jmol.util.Normix; 41 42 import javajs.util.AU; 43 import javajs.util.M3; 44 import javajs.util.M4; 45 import javajs.util.P3; 46 import javajs.util.P3i; 47 import javajs.util.T3; 48 49 import org.jmol.util.Rgb16; 50 import org.jmol.util.Shader; 51 import javajs.util.V3; 52 import org.jmol.viewer.Viewer; 53 54 /** 55 * Provides high-level graphics primitives for 3D visualization for the software 56 * renderers. These methods should not have to be used with WebGL or OpenGL or 57 * other hardware accelerators. 58 * 59 * This module is linked to via reflection from org.jmol.viewer.Viewer 60 * 61 * Bob Hanson 9/2/2012 62 * 63 * Note added 4/2015 BH: 64 * 65 * Well, it turns out that the calculation of the intermediate pixel z value 66 * in all methods involving rasterization of lines is incorrect and has been 67 * incorrect since Jmol's inception. I noticed long ago that large triangles such as 68 * produced in DRAW could incorrectly overlay/underlay other objects, but I could 69 * never determine why. It turns out that the assumption that z-value is linear 70 * across a line when perspectiveDepth is TRUE is simply incorrect. 71 * 72 * Basically, the function z(x) is non-linear. Treating it as simply a 73 * linear function results in oddities where lines and planes 74 * -- particularly created using DRAW and large distances -- appear 75 * to be where they are not. 76 * 77 * Through Jmol 13.3.13 we had the standard linear relationship: 78 * 79 * z = (x - xa) / (xb - xa) * (zb - za) + za 80 * 81 * I worked it out, and, amazingly, it should be 82 * 83 * z = (xb - xa) * za * zb / ((xb - x) * zb + (x - xa) * za) 84 * 85 * Note that it is still true that when x = xb, z = zb 86 * and when x = xa, z = za, as required. 87 * 88 * This equation can be rearranged to 89 * 90 * z = a / (b - x) 91 * 92 * where 93 * 94 * a = (xb - xa) * za * (zb / (zb - za)) 95 * 96 * and 97 * 98 * b = (xb * zb - xa * za) / (zb - za) 99 * 100 * These values must be floats, not integers, to work properly, because 101 * these are extrapolations from long distances in some cases. So there is 102 * considerable overhead there. It will take some experimentation to figure this 103 * out. 104 * 105 * The practical implications are for line, cylinder, and triangle drawing. 106 * First-pass corrections are for axes and DRAW objects. They tend to be the 107 * larger objects that result in the issue. 108 * 109 * Also affected is POV-Ray output, because right now POV-Ray is created using 110 * perspective on and plotted as though it were orthographic, but although that 111 * works in x and y, it does not work in z! 112 * 113 * 114 * <p> 115 * A pure software implementation of a 3D graphics engine. No hardware required. 116 * Depending upon what you are rendering ... some people say it is <i>pretty 117 * fast</i>. 118 * 119 * @author Miguel, miguel@jmol.org 120 * 121 * with additions by Bob Hanson hansonr@stolaf.edu 122 * 123 * The above is an understatement to say the least. 124 * 125 * This is a two-pass rendering system. In the first pass, all opaque 126 * objects are rendered. In the second pass, all translucent objects are 127 * rendered. 128 * 129 * If there are no translucent objects, then that is found in the first 130 * pass as follows: 131 * 132 * The renderers first try to set the color index of the object to be 133 * rendered using setColix(short colix), and that method returns false 134 * if we are in the wrong pass for that type of object. 135 * 136 * In addition, setColix records in the boolean haveTranslucentObjects 137 * whether a translucent object was seen in the first pass. 138 * 139 * The second pass is skipped if this flag is not set. This saves 140 * immensely on rendering time when there are no translucent objects. 141 * 142 * THUS, IT IS CRITICAL THAT ALL RENDERING OPTIONS CHECK THE COLIX USING 143 * g3d.setColix(short colix) PRIOR TO RENDERING. 144 * 145 * Translucency is rendered only approximately. We can't maintain a full 146 * buffer of all translucent objects. Instead, we "cheat" by maintaining 147 * one translucent z buffer. When a translucent pixel is to be written, 148 * its z position is checked and... 149 * 150 * ...if it is behind or at the z position of any pixel, it is ignored 151 * ...if it is in front of a translucent pixel, it is added to the 152 * translucent buffer ...if it is between an opaque and translucent 153 * pixel, the translucent pixel is turned opaque, and the new pixel is 154 * added to the translucent buffer 155 * 156 * This guarantees accurate translucency when there are no more than two 157 * translucent pixels between the user and an opaque pixel. It's a 158 * fudge, for sure. But it is pretty good, and certainly fine for 159 * "draft" work. 160 * 161 * Users needing more accurate translucencty are encouraged to use the 162 * POV-Ray export facility for production-level work. 163 * 164 * Antialiasing is accomplished as full scene antialiasing. This means 165 * that the width and height are doubled (both here and in 166 * TransformManager), the scene is rendered, and then each set of four 167 * pixels is averaged (roughly) as the final pixel in the width*height 168 * buffer. 169 * 170 * Antialiasing options allow for antialiasing of all objects: 171 * 172 * antialiasDisplay = true antialiasTranslucent = true 173 * 174 * or just the opaque ones: 175 * 176 * antialiasDisplay = true antialiasTranslucent = false 177 * 178 * or not at all: 179 * 180 * antialiasDisplay = false 181 * 182 * The difference will be speed and memory. Adding translucent objects 183 * doubles the buffer requirement, and adding antialiasing quadruples 184 * the buffer requirement. 185 * 186 * So we have: 187 * 188 * Memory requirements are significant, in multiples of (width) * 189 * (height) 32-bit integers: 190 * 191 * antialias OFF ON/opaque only ON/all objects 192 * 193 * no translucent 1p + 1z = 2 4p + 4z = 8 4p + 4z = 8 objects 194 * 195 * with translucent 2p + 2z = 4 5p + 5z = 10 8p + 8z = 16 objects 196 * 197 * Note that no antialising at all is required for POV-Ray output. 198 * POV-Ray will do antialiasing on its own. 199 * 200 * In principle we could save a bit in the case of antialiasing of just 201 * opaque objects and reuse the p and z buffers for the translucent 202 * buffer, but this hasn't been implemented because the savings isn't 203 * that great, and if you are going to the trouble of having 204 * antialiasing, you probably what it all. 205 * 206 * 207 */ 208 209 final public class Graphics3D extends GData implements JmolRendererInterface { 210 211 Platform3D platform; 212 LineRenderer line3d; 213 private SphereRenderer sphere3d; 214 private CylinderRenderer cylinder3d; 215 216 // loaded only if needed 217 private G3DRenderer triangle3d; 218 private G3DRenderer circle3d; 219 private G3DRenderer hermite3d; 220 221 private boolean isFullSceneAntialiasingEnabled; 222 private boolean antialias2; 223 224 private TextString[] strings = null; 225 private int stringCount; 226 227 @Override isWebGL()228 public boolean isWebGL() { 229 return false; 230 } 231 232 @Override clear()233 public void clear() { 234 stringCount = 0; 235 strings = null; 236 TextRenderer.clearFontCache(); 237 } 238 239 @Override destroy()240 public void destroy() { 241 releaseBuffers(); 242 platform = null; 243 pixel = pixel0 = pixelShaded = null; 244 pixelT0 = null; 245 pixelScreened = null; 246 graphicsForMetrics = null; 247 } 248 249 private byte[] anaglyphChannelBytes; 250 251 private boolean twoPass = false; 252 253 private boolean haveTranslucentObjects; 254 protected int[] pbuf; 255 protected int[] pbufT; 256 protected int[] zbuf; 257 protected int[] zbufT; 258 protected int translucencyMask; 259 private boolean renderLow; 260 261 private int[] shadesCurrent; 262 private int anaglyphLength; 263 264 Pixelator pixel; 265 Pixelator pixel0; 266 private PixelatorT pixelT0; 267 private PixelatorScreened pixelScreened; 268 private PixelatorShaded pixelShaded; 269 270 protected int zMargin; 271 private int[] aobuf; 272 setZMargin(int dz)273 void setZMargin(int dz) { 274 zMargin = dz; 275 } 276 Graphics3D()277 public Graphics3D() { 278 for (int i = normixCount; --i >= 0;) 279 transformedVectors[i] = new V3(); 280 } 281 282 @Override initialize(Viewer vwr, GenericPlatform apiPlatform)283 public void initialize(Viewer vwr, GenericPlatform apiPlatform) { 284 this.vwr = vwr; 285 this.apiPlatform = apiPlatform; 286 platform = new Platform3D(apiPlatform); 287 pixel = pixel0 = new Pixelator(this); 288 graphicsForMetrics = platform.getGraphicsForMetrics(); 289 290 line3d = new LineRenderer(this); 291 sphere3d = new SphereRenderer(this); 292 cylinder3d = new CylinderRenderer(this); 293 } 294 295 /** 296 * allows core JavaScript loading to not involve these classes 297 * 298 * @param tok 299 * 300 */ 301 @Override addRenderer(int tok)302 public void addRenderer(int tok) { 303 switch (tok) { 304 case T.circle: 305 if (circle3d == null) 306 circle3d = getRenderer("Circle"); 307 break; 308 case T.hermitelevel: 309 if (hermite3d == null) 310 hermite3d = getRenderer("Hermite"); 311 //$FALL-THROUGH$ 312 case T.triangles: 313 if (triangle3d == null) { 314 triangle3d = getRenderer("Triangle"); 315 ((PrecisionRenderer) triangle3d).isOrthographic = !vwr.tm.perspectiveDepth; 316 } 317 break; 318 } 319 } 320 getRenderer(String type)321 private G3DRenderer getRenderer(String type) { 322 G3DRenderer r = ((G3DRenderer) Interface.getOption("g3d." + type 323 + "Renderer", vwr, "render")); 324 if (r == null) 325 throw new NullPointerException("Interface"); 326 r.set(this, this); 327 return r; 328 } 329 330 @Override setWindowParameters(int width, int height, boolean antialias)331 public void setWindowParameters(int width, int height, boolean antialias) { 332 setWinParams(width, height, antialias); 333 if (currentlyRendering) 334 endRendering(); 335 } 336 337 @Override checkTranslucent(boolean isAlphaTranslucent)338 public boolean checkTranslucent(boolean isAlphaTranslucent) { 339 if (isAlphaTranslucent) 340 haveTranslucentObjects = true; 341 return (!twoPass || twoPass && (isPass2 == isAlphaTranslucent)); 342 } 343 344 @Override beginRendering(M3 rotationMatrix, boolean translucentMode, boolean isImageWrite, boolean renderLow)345 public void beginRendering(M3 rotationMatrix, boolean translucentMode, 346 boolean isImageWrite, boolean renderLow) { 347 if (currentlyRendering) 348 endRendering(); 349 this.renderLow = renderLow; 350 if (windowWidth != newWindowWidth || windowHeight != newWindowHeight 351 || newAntialiasing != isFullSceneAntialiasingEnabled) { 352 windowWidth = newWindowWidth; 353 windowHeight = newWindowHeight; 354 isFullSceneAntialiasingEnabled = newAntialiasing; 355 releaseBuffers(); 356 } 357 setRotationMatrix(rotationMatrix); 358 ((PrecisionRenderer) line3d).isOrthographic = !vwr.tm.perspectiveDepth; 359 if (triangle3d != null) 360 ((PrecisionRenderer) triangle3d).isOrthographic = !vwr.tm.perspectiveDepth; 361 antialiasEnabled = antialiasThisFrame = newAntialiasing; 362 currentlyRendering = true; 363 if (strings != null) 364 for (int i = Math.min(strings.length, stringCount); --i >= 0;) 365 strings[i] = null; 366 stringCount = 0; 367 twoPass = true; //only for testing -- set false to disallow second pass 368 isPass2 = false; 369 pass2Flag01 = 0; 370 colixCurrent = 0; 371 haveTranslucentObjects = wasScreened = false; 372 pixel = pixel0; 373 pixel.bgcolor = bgcolor; 374 translucentCoverOnly = !translucentMode; 375 if (pbuf == null) { 376 platform.allocateBuffers(windowWidth, windowHeight, antialiasThisFrame, 377 isImageWrite); 378 pbuf = platform.pBuffer; 379 zbuf = platform.zBuffer; 380 aobuf = null; 381 pixel0.setBuf(); 382 if (pixelT0 != null) 383 pixelT0.setBuf(); 384 if (pixelShaded != null) 385 pixelShaded.setBuf(); 386 } 387 setWidthHeight(antialiasThisFrame); 388 if (pixelScreened != null) 389 pixelScreened.width = width; 390 platform.clearBuffer(); 391 if (backgroundImage != null) 392 plotImage(Integer.MIN_VALUE, 0, Integer.MIN_VALUE, backgroundImage, null, 393 (short) 0, 0, 0); 394 textY = 0; 395 } 396 397 @Override setBackgroundTransparent(boolean TF)398 public void setBackgroundTransparent(boolean TF) { 399 if (platform != null) 400 platform.setBackgroundTransparent(TF); 401 } 402 releaseBuffers()403 private void releaseBuffers() { 404 pbuf = null; 405 zbuf = null; 406 pbufT = null; 407 zbufT = null; 408 aobuf = null; 409 platform.releaseBuffers(); 410 line3d.clearLineCache(); 411 } 412 413 @Override setPass2(boolean antialiasTranslucent)414 public boolean setPass2(boolean antialiasTranslucent) { 415 if (!haveTranslucentObjects || !currentlyRendering) 416 return false; 417 isPass2 = true; 418 pass2Flag01 = 1; 419 colixCurrent = 0; 420 if (pbufT == null || antialias2 != antialiasTranslucent) { 421 platform.allocateTBuffers(antialiasTranslucent); 422 pbufT = platform.pBufferT; 423 zbufT = platform.zBufferT; 424 } 425 antialias2 = antialiasTranslucent; 426 if (antialiasThisFrame && !antialias2) 427 downsampleFullSceneAntialiasing(true); 428 platform.clearTBuffer(); 429 if (pixelT0 == null) 430 pixelT0 = new PixelatorT(this); 431 if (pixel.p0 == null) 432 pixel = pixelT0; 433 else 434 pixel.p0 = pixelT0; 435 return true; 436 } 437 438 @Override endRendering()439 public void endRendering() { 440 if (!currentlyRendering) 441 return; 442 if (pbuf != null) { 443 if (isPass2 && pbufT != null) 444 for (int offset = pbufT.length; --offset >= 0;) 445 pbuf[offset] = mergeBufferPixel(pbuf[offset], pbufT[offset], bgcolor); 446 447 if (pixel == pixelShaded && pixelShaded.zShadePower == 0) 448 pixelShaded.showZBuffer(); 449 450 451 // if (ambientOcclusion != 0) { 452 // if (aobuf == null) 453 // aobuf = new int[pbuf.length]; 454 // else 455 // for (int offset = pbuf.length; --offset >= 0;) 456 // aobuf[offset] = 0; 457 // shader 458 // .occludePixels(pbuf, zbuf, aobuf, width, height, ambientOcclusion); 459 // } 460 if (antialiasThisFrame) 461 downsampleFullSceneAntialiasing(false); 462 } 463 platform.setBackgroundColor(bgcolor); 464 platform.notifyEndOfRendering(); 465 currentlyRendering = isPass2 = false; 466 } 467 mergeBufferPixel(int argbA, int argbB, int bgcolor)468 public static int mergeBufferPixel(int argbA, int argbB, 469 int bgcolor) { 470 if (argbB == 0 || argbA == argbB) 471 return argbA; 472 if (argbA == 0) 473 argbA = bgcolor; 474 int rbA = (argbA & 0x00FF00FF); 475 int gA = (argbA & 0x0000FF00); 476 int rbB = (argbB & 0x00FF00FF); 477 int gB = (argbB & 0x0000FF00); 478 int logAlpha = (argbB >> 24) & 0xF; 479 //just for now: 480 //0 or 1=100% opacity, 2=87.5%, 3=75%, 4=50%, 5=50%, 6 = 25%, 7 = 12.5% opacity. 481 switch (logAlpha) { 482 // 0.0 to 1.0 ==> MORE translucent 483 // 1/8 1/4 3/8 1/2 5/8 3/4 7/8 484 // t 32 64 96 128 160 192 224 485 // t >> 5 1 2 3 4 5 6 7 486 487 case 0: // 8:0 488 rbA = rbB; 489 gA = gB; 490 break; 491 case 1: // 7:1 492 rbA = (((rbB << 2) + (rbB << 1) + rbB + rbA) >> 3) & 0x00FF00FF; 493 gA = (((gB << 2) + +(gB << 1) + gB + gA) >> 3) & 0x0000FF00; 494 break; 495 case 2: // 3:1 496 rbA = (((rbB << 1) + rbB + rbA) >> 2) & 0x00FF00FF; 497 gA = (((gB << 1) + gB + gA) >> 2) & 0x0000FF00; 498 break; 499 case 3: // 5:3 500 rbA = (((rbB << 2) + rbB + (rbA << 1) + rbA) >> 3) & 0x00FF00FF; 501 gA = (((gB << 2) + gB + (gA << 1) + gA) >> 3) & 0x0000FF00; 502 break; 503 case 4: // 1:1 504 rbA = ((rbA + rbB) >> 1) & 0x00FF00FF; 505 gA = ((gA + gB) >> 1) & 0x0000FF00; 506 break; 507 case 5: // 3:5 508 rbA = (((rbB << 1) + rbB + (rbA << 2) + rbA) >> 3) & 0x00FF00FF; 509 gA = (((gB << 1) + gB + (gA << 2) + gA) >> 3) & 0x0000FF00; 510 break; 511 case 6: // 1:3 512 rbA = (((rbA << 1) + rbA + rbB) >> 2) & 0x00FF00FF; 513 gA = (((gA << 1) + gA + gB) >> 2) & 0x0000FF00; 514 break; 515 case 7: // 1:7 516 rbA = (((rbA << 2) + (rbA << 1) + rbA + rbB) >> 3) & 0x00FF00FF; 517 gA = (((gA << 2) + (gA << 1) + gA + gB) >> 3) & 0x0000FF00; 518 break; 519 } 520 return 0xFF000000 | rbA | gA; 521 } 522 523 @Override getScreenImage(boolean isImageWrite)524 public Object getScreenImage(boolean isImageWrite) { 525 /** 526 * @j2sNative var obj = this.platform.bufferedImage; if (isImageWrite) { 527 * this.releaseBuffers(); } return obj; 528 * 529 */ 530 { 531 return platform.bufferedImage; 532 } 533 } 534 535 @Override applyAnaglygh(STER stereoMode, int[] stereoColors)536 public void applyAnaglygh(STER stereoMode, int[] stereoColors) { 537 switch (stereoMode) { 538 case REDCYAN: 539 for (int i = anaglyphLength; --i >= 0;) { 540 int blue = anaglyphChannelBytes[i] & 0x000000FF; 541 int cyan = (blue << 8) | blue; 542 pbuf[i] = pbuf[i] & 0xFFFF0000 | cyan; 543 } 544 break; 545 case CUSTOM: 546 //best if complementary, but they do not have to be 547 int color1 = stereoColors[0]; 548 int color2 = stereoColors[1] & 0x00FFFFFF; 549 for (int i = anaglyphLength; --i >= 0;) { 550 int a = anaglyphChannelBytes[i] & 0x000000FF; 551 a = (a | ((a | (a << 8)) << 8)) & color2; 552 pbuf[i] = (pbuf[i] & color1) | a; 553 } 554 break; 555 case REDBLUE: 556 for (int i = anaglyphLength; --i >= 0;) { 557 int blue = anaglyphChannelBytes[i] & 0x000000FF; 558 pbuf[i] = (pbuf[i] & 0xFFFF0000) | blue; 559 } 560 break; 561 case REDGREEN: 562 for (int i = anaglyphLength; --i >= 0;) { 563 int green = (anaglyphChannelBytes[i] & 0x000000FF) << 8; 564 pbuf[i] = (pbuf[i] & 0xFFFF0000) | green; 565 } 566 break; 567 case DTI: 568 case DOUBLE: 569 case NONE: 570 break; 571 } 572 } 573 574 @Override snapshotAnaglyphChannelBytes()575 public void snapshotAnaglyphChannelBytes() { 576 if (currentlyRendering) 577 throw new NullPointerException(); 578 anaglyphLength = windowWidth * windowHeight; 579 if (anaglyphChannelBytes == null 580 || anaglyphChannelBytes.length != anaglyphLength) 581 anaglyphChannelBytes = new byte[anaglyphLength]; 582 for (int i = anaglyphLength; --i >= 0;) 583 anaglyphChannelBytes[i] = (byte) pbuf[i]; 584 } 585 586 @Override releaseScreenImage()587 public void releaseScreenImage() { 588 platform.clearScreenBufferThreaded(); 589 } 590 591 @Override haveTranslucentObjects()592 public boolean haveTranslucentObjects() { 593 return haveTranslucentObjects; 594 } 595 596 @Override setSlabAndZShade(int slabValue, int depthValue, int zSlab, int zDepth, int zShadePower)597 public void setSlabAndZShade(int slabValue, int depthValue, int zSlab, 598 int zDepth, int zShadePower) { 599 setSlab(slabValue); 600 setDepth(depthValue); 601 if (zSlab < zDepth) { 602 if (pixelShaded == null) 603 pixelShaded = new PixelatorShaded(this); 604 pixel = pixelShaded.set(zSlab, zDepth, zShadePower); 605 } else { 606 pixel = pixel0; 607 } 608 } 609 downsampleFullSceneAntialiasing(boolean downsampleZBuffer)610 private void downsampleFullSceneAntialiasing(boolean downsampleZBuffer) { 611 // now is the time we have to put in the correct background color 612 // this was a bug in 11.6.0-11.6.2. 613 614 // we must downsample the Z Buffer if there are translucent 615 // objects left to draw and antialiasTranslucent is set false 616 // in that case we must fudge the background color, because 617 // otherwise a match of the background color with an object 618 // will put it in the back -- the "blue tie on a blue screen" 619 // television effect. We want to avoid that. Here we can do that 620 // because the colors will be blurred anyway. 621 622 int bgcheck = bgcolor; 623 if (downsampleZBuffer) 624 bgcheck += ((bgcheck & 0xFF) == 0xFF ? -1 : 1); 625 downsample2d(pbuf, windowWidth, windowHeight, bgcheck); 626 if (downsampleZBuffer) { 627 downsample2dZ(pbuf, zbuf, windowWidth, windowHeight, bgcheck); 628 antialiasThisFrame = false; 629 setWidthHeight(false); 630 } 631 } 632 downsample2d(int[] pbuf, int width, int height, int bgcheck)633 public static void downsample2d(int[] pbuf, int width, int height, int bgcheck) { 634 int width4 = width << 1; 635 if (bgcheck != 0) { 636 bgcheck &= 0xFFFFFF; 637 for (int i = pbuf.length; --i >= 0;) 638 if (pbuf[i] == 0) 639 pbuf[i] = bgcheck; 640 } 641 int bg0 = ((bgcheck >> 2) & 0x3F3F3F3F) << 2; 642 bg0 += (bg0 & 0xC0C0C0C0) >> 6; 643 644 int offset1 = 0; 645 int offset4 = 0; 646 for (int i = height; --i >= 0; offset4 += width4) 647 for (int j = width; --j >= 0; ++offset1) { 648 649 /* more precise, but of no benefit: 650 651 int a = pbuf[offset4]; 652 int b = pbuf[offset4++ + width4]; 653 int c = pbuf[offset4]; 654 int d = pbuf[offset4++ + width4]; 655 int argb = ((((a & 0x0f0f0f) + (b & 0x0f0f0f) 656 + (c & 0x0f0f0f) + (d & 0x0f0f0f)) >> 2) & 0x0f0f0f) 657 + ( ((a & 0xF0F0F0) + (b & 0xF0F0F0) 658 + (c & 0xF0F0F0) + (d & 0xF0F0F0) 659 ) >> 2); 660 */ 661 662 int argb = ((pbuf[offset4] >> 2) & 0x3F3F3F3F) 663 + ((pbuf[offset4++ + width4] >> 2) & 0x3F3F3F3F) 664 + ((pbuf[offset4] >> 2) & 0x3F3F3F3F) 665 + ((pbuf[offset4++ + width4] >> 2) & 0x3F3F3F3F); 666 argb += (argb & 0xC0C0C0C0) >> 6; 667 if (argb == bg0) 668 argb = bgcheck; 669 670 /** 671 * I don't know why this is necessary. 672 * 673 * @j2sNative 674 * 675 * pbuf[offset1] = argb & 0x00FFFFFF | 0xFF000000; 676 */ 677 { 678 pbuf[offset1] = argb & 0x00FFFFFF; 679 } 680 } 681 } 682 downsample2dZ(int[] pbuf, int[] zbuf, int width, int height, int bgcheck)683 private static void downsample2dZ(int[] pbuf, int[] zbuf, int width, 684 int height, int bgcheck) { 685 int width4 = width << 1; 686 //we will add the alpha mask later 687 int offset1 = 0, offset4 = 0; 688 for (int i = height; --i >= 0; offset4 += width4) 689 for (int j = width; --j >= 0; ++offset1, ++offset4) { 690 int z = Math.min(zbuf[offset4], zbuf[offset4 + width4]); 691 z = Math.min(z, zbuf[++offset4]); 692 z = Math.min(z, zbuf[offset4 + width4]); 693 if (z != Integer.MAX_VALUE) 694 z >>= 1; 695 zbuf[offset1] = (pbuf[offset1] == bgcheck ? Integer.MAX_VALUE : z); 696 } 697 } 698 hasContent()699 public boolean hasContent() { 700 return platform.hasContent(); 701 } 702 703 int currentShadeIndex; 704 private int lastRawColor; 705 int translucencyLog; 706 private boolean wasScreened; 707 708 /** 709 * sets current color from colix color index 710 * 711 * @param colix 712 * the color index 713 * @return true or false if this is the right pass 714 */ 715 @Override setC(short colix)716 public boolean setC(short colix) { 717 boolean isLast = C.isColixLastAvailable(colix); 718 if (!isLast && colix == colixCurrent && currentShadeIndex == -1) 719 return true; 720 int mask = colix & C.TRANSLUCENT_MASK; 721 if (mask == C.TRANSPARENT) 722 return false; 723 if (renderLow) 724 mask = 0; 725 boolean isTranslucent = (mask != 0); 726 boolean isScreened = (isTranslucent && mask == C.TRANSLUCENT_SCREENED); 727 setScreened(isScreened); 728 if (!checkTranslucent(isTranslucent && !isScreened)) 729 return false; 730 if (isPass2) { 731 translucencyMask = (mask << C.ALPHA_SHIFT) | 0xFFFFFF; 732 translucencyLog = mask >> C.TRANSLUCENT_SHIFT; 733 } else { 734 translucencyLog = 0; 735 } 736 colixCurrent = colix; 737 if (isLast) { 738 if (argbCurrent != lastRawColor) { 739 if (argbCurrent == 0) 740 argbCurrent = 0xFFFFFFFF; 741 lastRawColor = argbCurrent; 742 shader.setLastColix(argbCurrent, inGreyscaleMode); 743 } 744 } 745 shadesCurrent = getShades(colix); 746 currentShadeIndex = -1; 747 setColor(getColorArgbOrGray(colix)); 748 return true; 749 } 750 setScreened(boolean isScreened)751 Pixelator setScreened(boolean isScreened) { 752 if (wasScreened != isScreened) { 753 wasScreened = isScreened; 754 if (isScreened) { 755 if (pixelScreened == null) 756 pixelScreened = new PixelatorScreened(this, pixel0); 757 if (pixel.p0 == null) 758 pixel = pixelScreened; 759 else 760 pixel.p0 = pixelScreened; 761 } else if (pixel.p0 == null || pixel == pixelScreened) { 762 pixel = (isPass2 ? pixelT0 : pixel0); 763 } else { 764 pixel.p0 = (isPass2 ? pixelT0 : pixel0); 765 } 766 } 767 return pixel; 768 } 769 770 @Override drawFilledCircle(short colixRing, short colixFill, int diameter, int x, int y, int z)771 public void drawFilledCircle(short colixRing, short colixFill, int diameter, 772 int x, int y, int z) { 773 // Halos, Draw handles 774 if (isClippedZ(z)) 775 return; 776 int r = (diameter + 1) / 2; 777 boolean isClipped = x < r || x + r >= width || y < r || y + r >= height; 778 if (isClipped && isClippedXY(diameter, x, y)) 779 return; 780 if (colixRing != 0 && setC(colixRing)) { 781 if (isClipped) { 782 if (!isClippedXY(diameter, x, y)) 783 ((CircleRenderer) circle3d).plotCircleCenteredClipped(x, y, z, 784 diameter); 785 } else { 786 ((CircleRenderer) circle3d).plotCircleCenteredUnclipped(x, y, z, 787 diameter); 788 } 789 } 790 if (colixFill != 0 && setC(colixFill)) { 791 if (isClipped) 792 ((CircleRenderer) circle3d).plotFilledCircleCenteredClipped(x, y, z, 793 diameter); 794 else 795 ((CircleRenderer) circle3d).plotFilledCircleCenteredUnclipped(x, y, z, 796 diameter); 797 } 798 } 799 800 @Override volumeRender4(int diameter, int x, int y, int z)801 public void volumeRender4(int diameter, int x, int y, int z) { 802 if (diameter == 1) { 803 plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel); 804 return; 805 } 806 if (isClippedZ(z)) 807 return; 808 int r = (diameter + 1) / 2; 809 boolean isClipped = x < r || x + r >= width || y < r || y + r >= height; 810 if (isClipped && isClippedXY(diameter, x, y)) 811 return; 812 if (isClipped) 813 ((CircleRenderer) circle3d).plotFilledCircleCenteredClipped(x, y, z, 814 diameter); 815 else 816 ((CircleRenderer) circle3d).plotFilledCircleCenteredUnclipped(x, y, z, 817 diameter); 818 } 819 820 /** 821 * fills a solid sphere 822 * 823 * @param diameter 824 * pixel count 825 * @param x 826 * center x 827 * @param y 828 * center y 829 * @param z 830 * center z 831 */ 832 @Override fillSphereXYZ(int diameter, int x, int y, int z)833 public void fillSphereXYZ(int diameter, int x, int y, int z) { 834 switch (diameter) { 835 case 1: 836 plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel); 837 return; 838 case 0: 839 return; 840 } 841 if (diameter <= (antialiasThisFrame ? SphereRenderer.maxSphereDiameter2 842 : SphereRenderer.maxSphereDiameter)) 843 sphere3d.render(shadesCurrent, diameter, x, y, z, null, null, null, -1, 844 null); 845 } 846 847 private int saveAmbient, saveDiffuse; 848 849 @Override volumeRender(boolean TF)850 public void volumeRender(boolean TF) { 851 if (TF) { 852 saveAmbient = getAmbientPercent(); 853 saveDiffuse = getDiffusePercent(); 854 setAmbientPercent(100); 855 setDiffusePercent(0); 856 addRenderer(T.circle); 857 } else { 858 setAmbientPercent(saveAmbient); 859 setDiffusePercent(saveDiffuse); 860 } 861 } 862 863 /** 864 * fills a solid sphere 865 * 866 * @param diameter 867 * pixel count 868 * @param center 869 * javax.vecmath.Point3i defining the center 870 */ 871 872 @Override fillSphereI(int diameter, P3i center)873 public void fillSphereI(int diameter, P3i center) { 874 fillSphereXYZ(diameter, center.x, center.y, center.z); 875 } 876 877 /** 878 * fills a solid sphere 879 * 880 * @param diameter 881 * pixel count 882 * @param center 883 * a javax.vecmath.Point3f ... floats are casted to ints 884 */ 885 @Override fillSphereBits(int diameter, P3 center)886 public void fillSphereBits(int diameter, P3 center) { 887 // from hermite ribbon 888 fillSphereXYZ(diameter, Math.round(center.x), Math.round(center.y), 889 Math.round(center.z)); 890 } 891 892 @Override fillEllipsoid(P3 center, P3[] points, int x, int y, int z, int diameter, M3 mToEllipsoidal, double[] coef, M4 mDeriv, int selectedOctant, P3[] octantPoints)893 public void fillEllipsoid(P3 center, P3[] points, int x, int y, int z, 894 int diameter, M3 mToEllipsoidal, double[] coef, 895 M4 mDeriv, int selectedOctant, P3[] octantPoints) { 896 switch (diameter) { 897 case 1: 898 plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel); 899 return; 900 case 0: 901 return; 902 } 903 if (diameter <= (antialiasThisFrame ? SphereRenderer.maxSphereDiameter2 904 : SphereRenderer.maxSphereDiameter)) 905 sphere3d.render(shadesCurrent, diameter, x, y, z, mToEllipsoidal, coef, 906 mDeriv, selectedOctant, octantPoints); 907 } 908 909 /** 910 * draws a rectangle 911 * 912 * @param x 913 * upper left x 914 * @param y 915 * upper left y 916 * @param z 917 * upper left z 918 * @param zSlab 919 * z for slab check (for set labelsFront) 920 * @param rWidth 921 * pixel count 922 * @param rHeight 923 * pixel count 924 */ 925 @Override drawRect(int x, int y, int z, int zSlab, int rWidth, int rHeight)926 public void drawRect(int x, int y, int z, int zSlab, int rWidth, int rHeight) { 927 // labels (and rubberband, not implemented) and navigation cursor 928 if (zSlab != 0 && isClippedZ(zSlab)) 929 return; 930 int w = rWidth - 1; 931 int h = rHeight - 1; 932 int xRight = x + w; 933 int yBottom = y + h; 934 if (y >= 0 && y < height) 935 drawHLine(x, y, z, w); 936 if (yBottom >= 0 && yBottom < height) 937 drawHLine(x, yBottom, z, w); 938 if (x >= 0 && x < width) 939 drawVLine(x, y, z, h); 940 if (xRight >= 0 && xRight < width) 941 drawVLine(xRight, y, z, h); 942 } 943 drawHLine(int x, int y, int z, int w)944 private void drawHLine(int x, int y, int z, int w) { 945 // hover, labels only 946 if (w < 0) { 947 x += w; 948 w = -w; 949 } 950 if (x < 0) { 951 w += x; 952 x = 0; 953 } 954 if (x + w >= width) 955 w = width - 1 - x; 956 Pixelator p = pixel; 957 int c = argbCurrent; 958 int offset = x + width * y; 959 for (int i = 0; i <= w; i++) { 960 if (z < zbuf[offset]) 961 p.addPixel(offset, z, c); 962 offset++; 963 } 964 } 965 drawVLine(int x, int y, int z, int h)966 private void drawVLine(int x, int y, int z, int h) { 967 // hover, labels only 968 if (h < 0) { 969 y += h; 970 h = -h; 971 } 972 if (y < 0) { 973 h += y; 974 y = 0; 975 } 976 if (y + h >= height) { 977 h = height - 1 - y; 978 } 979 int offset = x + width * y; 980 Pixelator p = pixel; 981 int c = argbCurrent; 982 for (int i = 0; i <= h; i++) { 983 if (z < zbuf[offset]) 984 p.addPixel(offset, z, c); 985 offset += width; 986 } 987 } 988 989 /** 990 * fills background rectangle for label 991 * <p> 992 * 993 * @param x 994 * upper left x 995 * @param y 996 * upper left y 997 * @param z 998 * upper left z 999 * @param zSlab 1000 * z value for slabbing 1001 * @param widthFill 1002 * pixel count 1003 * @param heightFill 1004 * pixel count 1005 */ 1006 @Override fillTextRect(int x, int y, int z, int zSlab, int widthFill, int heightFill)1007 public void fillTextRect(int x, int y, int z, int zSlab, int widthFill, 1008 int heightFill) { 1009 // hover and labels only -- slab at atom or front -- simple Z/window clip 1010 if (isClippedZ(zSlab)) 1011 return; 1012 int w = width; 1013 if (x < 0) { 1014 widthFill += x; 1015 if (widthFill <= 0) 1016 return; 1017 x = 0; 1018 } 1019 if (x + widthFill > w) { 1020 widthFill = w - x; 1021 if (widthFill <= 0) 1022 return; 1023 } 1024 if (y < 0) { 1025 heightFill += y; 1026 if (heightFill <= 0) 1027 return; 1028 y = 0; 1029 } 1030 if (y + heightFill > height) 1031 heightFill = height - y; 1032 int c = argbCurrent; 1033 int[] zb = zbuf; 1034 Pixelator p = pixel; 1035 while (--heightFill >= 0) 1036 plotPixelsUnclippedCount(c, widthFill, x, y++, z, w, zb, p); 1037 } 1038 1039 /** 1040 * draws the specified string in the current font. no line wrapping -- axis, 1041 * labels, measures 1042 * 1043 * @param str 1044 * the String 1045 * @param font3d 1046 * the Font3D 1047 * @param xBaseline 1048 * baseline x 1049 * @param yBaseline 1050 * baseline y 1051 * @param z 1052 * baseline z 1053 * @param zSlab 1054 * z for slab calculation 1055 * @param bgColix 1056 */ 1057 1058 @Override drawString(String str, Font font3d, int xBaseline, int yBaseline, int z, int zSlab, short bgColix)1059 public void drawString(String str, Font font3d, int xBaseline, int yBaseline, 1060 int z, int zSlab, short bgColix) { 1061 //axis, labels, measures, echo 1062 currentShadeIndex = 0; 1063 if (str == null) 1064 return; 1065 if (isClippedZ(zSlab)) 1066 return; 1067 drawStringNoSlab(str, font3d, xBaseline, yBaseline, z, bgColix); 1068 } 1069 1070 /** 1071 * draws the specified string in the current font. no line wrapping -- echo, 1072 * frank, hover, molecularOrbital, uccage 1073 * 1074 * @param str 1075 * the String 1076 * @param font3d 1077 * the Font3D 1078 * @param xBaseline 1079 * baseline x 1080 * @param yBaseline 1081 * baseline y 1082 * @param z 1083 * baseline z 1084 * @param bgColix 1085 */ 1086 1087 @Override drawStringNoSlab(String str, Font font3d, int xBaseline, int yBaseline, int z, short bgColix)1088 public void drawStringNoSlab(String str, Font font3d, int xBaseline, 1089 int yBaseline, int z, short bgColix) { 1090 // echo, frank, hover, molecularOrbital, uccage 1091 if (str == null) 1092 return; 1093 if (strings == null) 1094 strings = new TextString[10]; 1095 if (stringCount == strings.length) 1096 strings = (TextString[]) AU.doubleLength(strings); 1097 TextString t = new TextString(); 1098 t.setText(str, font3d == null ? currentFont : (currentFont = font3d), 1099 argbCurrent, C.isColixTranslucent(bgColix) ? // shift colix translucency mask into integer alpha position 1100 (getColorArgbOrGray(bgColix) & 0xFFFFFF) 1101 | ((bgColix & C.TRANSLUCENT_MASK) << C.ALPHA_SHIFT) 1102 : 0, xBaseline, yBaseline, z); 1103 strings[stringCount++] = t; 1104 1105 } 1106 1107 public static Comparator<TextString> sort; 1108 1109 @Override renderAllStrings(Object jmolRenderer)1110 public void renderAllStrings(Object jmolRenderer) { 1111 if (strings == null) 1112 return; 1113 if (stringCount >= 2) { 1114 if (sort == null) 1115 sort = new TextString(); 1116 Arrays.sort(strings, sort); 1117 } 1118 for (int i = 0; i < stringCount; i++) { 1119 TextString ts = strings[i]; 1120 plotText(ts.x, ts.y, ts.z, ts.argb, ts.bgargb, ts.text, ts.font, 1121 (JmolRendererInterface) jmolRenderer); 1122 } 1123 strings = null; 1124 stringCount = 0; 1125 } 1126 1127 @Override plotText(int x, int y, int z, int argb, int bgargb, String text, Font font3d, JmolRendererInterface jmolRenderer)1128 public void plotText(int x, int y, int z, int argb, int bgargb, String text, 1129 Font font3d, JmolRendererInterface jmolRenderer) { 1130 TextRenderer.plot(x, y, z, argb, bgargb, text, font3d, this, jmolRenderer, 1131 antialiasThisFrame); 1132 } 1133 1134 @Override drawImage(Object objImage, int x, int y, int z, int zSlab, short bgcolix, int width, int height)1135 public void drawImage(Object objImage, int x, int y, int z, int zSlab, 1136 short bgcolix, int width, int height) { 1137 // overridden in Export 1138 if (objImage != null && width > 0 && height > 0 && !isClippedZ(zSlab)) 1139 plotImage(x, y, z, objImage, null, bgcolix, width, height); 1140 } 1141 1142 @Override plotImage(int x, int y, int z, Object image, JmolRendererInterface jmolRenderer, short bgcolix, int imageWidth, int imageHeight)1143 public void plotImage(int x, int y, int z, Object image, 1144 JmolRendererInterface jmolRenderer, short bgcolix, 1145 int imageWidth, int imageHeight) { 1146 // overridden in __Exporter 1147 setC(bgcolix); 1148 if (!isPass2) 1149 translucencyMask = -1; 1150 if (bgcolix == 0) 1151 argbCurrent = 0; 1152 boolean isBackground = (x == Integer.MIN_VALUE); 1153 int bg = (isBackground ? bgcolor : argbCurrent); 1154 if (isBackground) { 1155 x = 0; 1156 z = Integer.MAX_VALUE - 1; 1157 imageWidth = width; 1158 imageHeight = height; 1159 } 1160 if (x + imageWidth <= 0 || x >= width || y + imageHeight <= 0 1161 || y >= height) 1162 return; 1163 Object g; 1164 /** 1165 * @j2sNative 1166 * 1167 * g = null; 1168 * 1169 */ 1170 { 1171 g = platform.getGraphicsForTextOrImage(imageWidth, imageHeight); 1172 } 1173 int[] buffer = apiPlatform.drawImageToBuffer(g, 1174 platform.offscreenImage, image, imageWidth, imageHeight, 1175 isBackground ? bg : 0); 1176 if (buffer == null) 1177 return; 1178 int[] zb = zbuf; 1179 int w = width; 1180 Pixelator p = pixel; 1181 int h = height; 1182 int t = translucencyLog; 1183 if (jmolRenderer == null 1184 && (x >= 0 && x + imageWidth <= w && y >= 0 && y + imageHeight <= h)) { 1185 // unclipped 1186 for (int i = 0, offset = 0, pbufOffset = y * w + x; i < imageHeight; i++, pbufOffset += (w - imageWidth)) { 1187 for (int j = 0; j < imageWidth; j++,offset++,pbufOffset++) { 1188 if (z < zb[pbufOffset]) { 1189 int b = buffer[offset]; 1190 if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) 1191 p.addPixel(pbufOffset, z, b); 1192 } 1193 } 1194 } 1195 } else { 1196 if (jmolRenderer == null) 1197 jmolRenderer = this; 1198 // clipped in some way -- let G3D or Exporter handle it 1199 for (int i = 0, offset = 0; i < imageHeight; i++) 1200 for (int j = 0; j < imageWidth; j++) { 1201 int b = buffer[offset++]; 1202 if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) 1203 jmolRenderer.plotImagePixel(b, x + j, y + i, z, (byte) 8, bg, w, 1204 h, zb, p, t); 1205 } 1206 } 1207 } 1208 1209 @Override setFontFid(byte fid)1210 public void setFontFid(byte fid) { 1211 currentFont = Font.getFont3D(fid); 1212 } 1213 1214 @Override setFont(Font font3d)1215 public void setFont(Font font3d) { 1216 currentFont = font3d; 1217 } 1218 1219 /* 1220 private void setRectClip(int x, int y, int width, int height) { 1221 // not implemented 1222 if (x < 0) 1223 x = 0; 1224 if (y < 0) 1225 y = 0; 1226 if (x + width > windowWidth) 1227 width = windowWidth - x; 1228 if (y + height > windowHeight) 1229 height = windowHeight - y; 1230 clipX = x; 1231 clipY = y; 1232 clipWidth = width; 1233 clipHeight = height; 1234 if (antialiasThisFrame) { 1235 clipX *= 2; 1236 clipY *= 2; 1237 clipWidth *= 2; 1238 clipHeight *= 2; 1239 } 1240 } 1241 */ 1242 1243 //mostly public drawing methods -- add "public" if you need to 1244 1245 /* *************************************************************** 1246 * points 1247 * ***************************************************************/ 1248 1249 @Override drawPixel(int x, int y, int z)1250 public void drawPixel(int x, int y, int z) { 1251 // measures - render angle 1252 plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel); 1253 } 1254 1255 @Override drawPoints(int count, int[] coordinates, int scale)1256 public void drawPoints(int count, int[] coordinates, int scale) { 1257 // for dots only 1258 if (scale > 1) { 1259 float s2 = scale * scale * 0.8f; 1260 for (int i = -scale; i < scale; i++) { 1261 for (int j = -scale; j < scale; j++) { 1262 if (i * i + j * j > s2) 1263 continue; 1264 plotPoints(count, coordinates, i, j); 1265 plotPoints(count, coordinates, i, j); 1266 } 1267 } 1268 } else { 1269 plotPoints(count, coordinates, 0, 0); 1270 } 1271 } 1272 1273 /* *************************************************************** 1274 * lines and cylinders 1275 * ***************************************************************/ 1276 1277 @Override drawDashedLineBits(int run, int rise, P3 pointA, P3 pointB)1278 public void drawDashedLineBits(int run, int rise, P3 pointA, P3 pointB) { 1279 if (isAntialiased()) { 1280 run += run; 1281 rise += rise; 1282 } 1283 setScreeni(pointA, sA); 1284 setScreeni(pointB, sB); 1285 line3d.plotLineBits(argbCurrent, argbCurrent, sA, sB, run, rise, true); 1286 if (isAntialiased()) { 1287 if (Math.abs(pointA.x - pointB.x) < Math.abs(pointA.y - pointB.y)) { 1288 sA.x += 1; 1289 sB.x += 1; 1290 line3d.plotLineBits(argbCurrent, argbCurrent, sA, sB, run, rise, true); 1291 } else { 1292 sA.y += 1; 1293 sB.y += 1; 1294 line3d.plotLineBits(argbCurrent, argbCurrent, sA, sB, run, rise, true); 1295 } 1296 } 1297 } 1298 setScreeni(P3 pt, P3i p)1299 private void setScreeni(P3 pt, P3i p) { 1300 p.x = Math.round(pt.x); 1301 p.y = Math.round(pt.y); 1302 p.z = Math.round(pt.z); 1303 } 1304 1305 @Override drawLineXYZ(int x1, int y1, int z1, int x2, int y2, int z2)1306 public void drawLineXYZ(int x1, int y1, int z1, int x2, int y2, int z2) { 1307 // stars, text 1308 line3d.plotLineOld(argbCurrent, argbCurrent, x1, y1, z1, x2, y2, z2); 1309 } 1310 1311 @Override drawLine(short colixA, short colixB, int x1, int y1, int z1, int x2, int y2, int z2)1312 public void drawLine(short colixA, short colixB, int x1, int y1, int z1, 1313 int x2, int y2, int z2) { 1314 // backbone and sticks 1315 if (!setC(colixA)) 1316 colixA = 0; 1317 int argbA = argbCurrent; 1318 if (!setC(colixB)) 1319 colixB = 0; 1320 if (colixA != 0 || colixB != 0) 1321 line3d.plotLineOld(argbA, argbCurrent, x1, y1, z1, x2, y2, z2); 1322 } 1323 1324 @Override drawLineBits(short colixA, short colixB, P3 pointA, P3 pointB)1325 public void drawLineBits(short colixA, short colixB, P3 pointA, P3 pointB) { 1326 // drawQuadBits, drawTriangleBits 1327 if (!setC(colixA)) 1328 colixA = 0; 1329 int argbA = argbCurrent; 1330 if (!setC(colixB)) 1331 colixB = 0; 1332 if (colixA != 0 || colixB != 0) { 1333 setScreeni(pointA, sA); 1334 setScreeni(pointB, sB); 1335 line3d.plotLineBits(argbA, argbCurrent, sA, sB, 0, 0, false); 1336 } 1337 } 1338 1339 @Override drawLineAB(P3 pointA, P3 pointB)1340 public void drawLineAB(P3 pointA, P3 pointB) { 1341 // draw quadrilateral and hermite 1342 setScreeni(pointA, sA); 1343 setScreeni(pointB, sB); 1344 line3d.plotLineBits(argbCurrent, argbCurrent, sA, sB, 0, 0, false); 1345 } 1346 1347 @Override fillCylinderXYZ(short colixA, short colixB, byte endcaps, int diameter, int xA, int yA, int zA, int xB, int yB, int zB)1348 public void fillCylinderXYZ(short colixA, short colixB, byte endcaps, 1349 int diameter, int xA, int yA, int zA, int xB, 1350 int yB, int zB) { 1351 //Backbone, Mps, Sticks 1352 if (diameter > ht3) 1353 return; 1354 int screen = 0; 1355 currentShadeIndex = 0; 1356 if (!setC(colixB)) 1357 colixB = 0; 1358 if (wasScreened) 1359 screen = 2; 1360 if (!setC(colixA)) 1361 colixA = 0; 1362 if (wasScreened) 1363 screen += 1; 1364 if (colixA == 0 && colixB == 0) 1365 return; 1366 cylinder3d.renderOld(colixA, colixB, screen, endcaps, diameter, xA, yA, zA, 1367 xB, yB, zB); 1368 } 1369 1370 // @Override 1371 // public void fillCylinderScreen(byte endcaps, int diameter, int xA, int yA, 1372 // int zA, int xB, int yB, int zB) { 1373 // //measures, vectors, polyhedra 1374 // if (diameter <= ht3) 1375 // cylinder3d.renderOld(colixCurrent, colixCurrent, 0, endcaps, diameter, xA, yA, 1376 // zA, xB, yB, zB); 1377 // } 1378 // 1379 @Override fillCylinderScreen3I(byte endcaps, int diameter, P3 screenA, P3 screenB, P3 pt0f, P3 pt1f, float radius)1380 public void fillCylinderScreen3I(byte endcaps, int diameter, P3 screenA, 1381 P3 screenB, P3 pt0f, P3 pt1f, float radius) { 1382 //nucleic cartoon, draw arrowhead 1383 // this needs to be old style, not exact for performance in JavaScript. 1384 if (diameter <= ht3) 1385 cylinder3d.renderOld(colixCurrent, colixCurrent, 0, endcaps, diameter, 1386 (int) screenA.x, (int) screenA.y, (int) screenA.z, (int) screenB.x, (int) screenB.y, (int) screenB.z); 1387 } 1388 1389 @Override fillCylinder(byte endcaps, int diameter, P3i screenA, P3i screenB)1390 public void fillCylinder(byte endcaps, int diameter, P3i screenA, P3i screenB) { 1391 // mesh(low-precision) 1392 if (diameter <= ht3) 1393 cylinder3d.renderOld(colixCurrent, colixCurrent, 0, endcaps, diameter, 1394 screenA.x, screenA.y, screenA.z, screenB.x, screenB.y, screenB.z); 1395 } 1396 1397 @Override fillCylinderBits(byte endcaps, int diameter, P3 screenA, P3 screenB)1398 public void fillCylinderBits(byte endcaps, int diameter, P3 screenA, 1399 P3 screenB) { 1400 // dipole cross, cartoonRockets, draw line 1401 if (diameter <= ht3 && screenA.z != 1 && screenB.z != 1) { 1402 if (diameter == 0 || diameter == 1) { 1403 setScreeni(screenA, sA); 1404 setScreeni(screenB, sB); 1405 line3d.plotLineBits(getColorArgbOrGray(colixCurrent), 1406 getColorArgbOrGray(colixCurrent), sA, sB, 0, 0, false); 1407 return; 1408 } 1409 // in the case of DRAW, we need precision cylinders, because some are 1410 // really very flat disks with almost no difference between screenA and screenB. 1411 cylinder3d.renderBitsFloat(colixCurrent, colixCurrent, 0, endcaps, 1412 diameter, screenA, screenB); 1413 } 1414 } 1415 1416 @Override fillCylinderBits2(short colixA, short colixB, byte endcaps, int diameter, P3 screenA, P3 screenB)1417 public void fillCylinderBits2(short colixA, short colixB, byte endcaps, 1418 int diameter, P3 screenA, P3 screenB) { 1419 //Backbone, Mps, Sticks 1420 if (diameter > ht3) 1421 return; 1422 int screen = 0; 1423 currentShadeIndex = 0; 1424 if (!setC(colixB)) 1425 colixB = 0; 1426 if (wasScreened) 1427 screen = 2; 1428 if (!setC(colixA)) 1429 colixA = 0; 1430 if (wasScreened) 1431 screen += 1; 1432 if (colixA == 0 && colixB == 0) 1433 return; 1434 setScreeni(screenA, sA); 1435 setScreeni(screenB, sB); 1436 cylinder3d.renderBits(colixA, colixB, screen, endcaps, diameter, sA, sB); 1437 } 1438 1439 @Override fillConeScreen3f(byte endcap, int screenDiameter, P3 screenBase, P3 screenTip, boolean isBarb)1440 public void fillConeScreen3f(byte endcap, int screenDiameter, P3 screenBase, 1441 P3 screenTip, boolean isBarb) { 1442 // cartoons, rockets 1443 if (screenDiameter <= ht3) 1444 cylinder3d.renderConeOld(colixCurrent, endcap, screenDiameter, screenBase.x, 1445 screenBase.y, screenBase.z, screenTip.x, screenTip.y, screenTip.z, 1446 true, isBarb); 1447 } 1448 1449 @Override drawHermite4(int tension, P3 s0, P3 s1, P3 s2, P3 s3)1450 public void drawHermite4(int tension, P3 s0, P3 s1, P3 s2, P3 s3) { 1451 // bioShapeRenderer 1452 ((HermiteRenderer) hermite3d).renderHermiteRope(false, tension, 0, 0, 0, 1453 s0, s1, s2, s3); 1454 } 1455 1456 @Override drawHermite7(boolean fill, boolean border, int tension, P3 s0, P3 s1, P3 s2, P3 s3, P3 s4, P3 s5, P3 s6, P3 s7, int aspectRatio, short colixBack)1457 public void drawHermite7(boolean fill, boolean border, int tension, P3 s0, 1458 P3 s1, P3 s2, P3 s3, P3 s4, P3 s5, P3 s6, 1459 P3 s7, int aspectRatio, short colixBack) { 1460 if (colixBack == 0) { 1461 ((HermiteRenderer) hermite3d).renderHermiteRibbon(fill, border, tension, 1462 s0, s1, s2, s3, s4, s5, s6, s7, aspectRatio, 0); 1463 return; 1464 } 1465 ((HermiteRenderer) hermite3d).renderHermiteRibbon(fill, border, tension, 1466 s0, s1, s2, s3, s4, s5, s6, s7, aspectRatio, 1); 1467 short colix = colixCurrent; 1468 setC(colixBack); 1469 ((HermiteRenderer) hermite3d).renderHermiteRibbon(fill, border, tension, 1470 s0, s1, s2, s3, s4, s5, s6, s7, aspectRatio, -1); 1471 setC(colix); 1472 } 1473 1474 @Override fillHermite(int tension, int diameterBeg, int diameterMid, int diameterEnd, P3 s0, P3 s1, P3 s2, P3 s3)1475 public void fillHermite(int tension, int diameterBeg, int diameterMid, 1476 int diameterEnd, P3 s0, P3 s1, P3 s2, P3 s3) { 1477 ((HermiteRenderer) hermite3d).renderHermiteRope(true, tension, diameterBeg, 1478 diameterMid, diameterEnd, s0, s1, s2, s3); 1479 } 1480 1481 @Override drawTriangle3C(P3i screenA, short colixA, P3i screenB, short colixB, P3i screenC, short colixC, int check)1482 public void drawTriangle3C(P3i screenA, short colixA, P3i screenB, 1483 short colixB, P3i screenC, short colixC, int check) { 1484 // primary method for mapped Mesh 1485 if ((check & 1) == 1) 1486 drawLine(colixA, colixB, screenA.x, screenA.y, screenA.z, screenB.x, 1487 screenB.y, screenB.z); 1488 if ((check & 2) == 2) 1489 drawLine(colixB, colixC, screenB.x, screenB.y, screenB.z, screenC.x, 1490 screenC.y, screenC.z); 1491 if ((check & 4) == 4) 1492 drawLine(colixA, colixC, screenA.x, screenA.y, screenA.z, screenC.x, 1493 screenC.y, screenC.z); 1494 } 1495 1496 @Override fillTriangleTwoSided(short normix, P3 screenA, P3 screenB, P3 screenC)1497 public void fillTriangleTwoSided(short normix, P3 screenA, P3 screenB, 1498 P3 screenC) { 1499 // polyhedra 1500 setColorNoisy(getShadeIndex(normix)); 1501 fillTriangleP3f(screenA, screenB, screenC, false); 1502 } 1503 1504 private P3i sA = new P3i(), sB = new P3i(), sC = new P3i(); 1505 fillTriangleP3f(P3 screenA, P3 screenB, P3 screenC, boolean useGouraud)1506 private void fillTriangleP3f(P3 screenA, P3 screenB, P3 screenC, boolean useGouraud) { 1507 setScreeni(screenA, sA); 1508 setScreeni(screenB, sB); 1509 setScreeni(screenC, sC); 1510 ((TriangleRenderer) triangle3d).fillTriangle(sA, sB, sC, useGouraud); 1511 } 1512 1513 @Override fillTriangle3f(P3 screenA, P3 screenB, P3 screenC, boolean isSolid)1514 public void fillTriangle3f(P3 screenA, P3 screenB, P3 screenC, 1515 boolean isSolid) { 1516 // rocket box, cartoon ribbon 1517 int i = getShadeIndexP3(screenA, screenB, screenC, isSolid); 1518 if (i < 0) 1519 return; 1520 if (isSolid) 1521 setColorNoisy(i); 1522 else 1523 setColor(shadesCurrent[i]); 1524 fillTriangleP3f(screenA, screenB, screenC, false); 1525 } 1526 1527 @Override fillTriangle3i(P3 screenA, P3 screenB, P3 screenC, T3 ptA, T3 ptB, T3 ptC, boolean doShade)1528 public void fillTriangle3i(P3 screenA, P3 screenB, P3 screenC, T3 ptA, 1529 T3 ptB, T3 ptC, boolean doShade) { 1530 // cartoon DNA plates; preset color 1531 if (doShade) { 1532 V3 v = vectorAB; 1533 v.set(screenB.x - screenA.x, screenB.y - screenA.y, screenB.z - screenA.z); 1534 int shadeIndex; 1535 if (screenC == null) { 1536 shadeIndex = shader.getShadeIndex(-v.x, -v.y, v.z); 1537 } else { 1538 vectorAC.set(screenC.x - screenA.x, screenC.y - screenA.y, screenC.z 1539 - screenA.z); 1540 v.cross(v, vectorAC); 1541 shadeIndex = v.z >= 0 ? shader.getShadeIndex(-v.x, -v.y, v.z) : shader 1542 .getShadeIndex(v.x, v.y, -v.z); 1543 } 1544 if (shadeIndex > Shader.SHADE_INDEX_NOISY_LIMIT) 1545 shadeIndex = Shader.SHADE_INDEX_NOISY_LIMIT; 1546 setColorNoisy(shadeIndex); 1547 } 1548 fillTriangleP3f(screenA, screenB, screenC, false); 1549 } 1550 1551 @Override fillTriangle3CN(P3i screenA, short colixA, short normixA, P3i screenB, short colixB, short normixB, P3i screenC, short colixC, short normixC)1552 public void fillTriangle3CN(P3i screenA, short colixA, short normixA, 1553 P3i screenB, short colixB, short normixB, 1554 P3i screenC, short colixC, short normixC) { 1555 ((TriangleRenderer) triangle3d).fillTriangle(screenA, screenB, screenC, 1556 checkGouraud(colixA, colixB, colixC, normixA, normixB, normixC)); 1557 } 1558 1559 @Override fillTriangle3CNBits(P3 screenA, short colixA, short normixA, P3 screenB, short colixB, short normixB, P3 screenC, short colixC, short normixC, boolean twoSided)1560 public void fillTriangle3CNBits(P3 screenA, short colixA, short normixA, P3 screenB, 1561 short colixB, short normixB, P3 screenC, short colixC, 1562 short normixC, boolean twoSided) { 1563 // mesh, isosurface 1564 fillTriangleP3f(screenA, screenB, screenC, 1565 checkGouraud(colixA, colixB, colixC, normixA, normixB, normixC)); 1566 } 1567 checkGouraud(short colixA, short colixB, short colixC, short normixA, short normixB, short normixC)1568 private boolean checkGouraud(short colixA, short colixB, short colixC, 1569 short normixA, short normixB, short normixC) { 1570 if (!isPass2 && normixA == normixB && normixA == normixC 1571 && colixA == colixB && colixA == colixC) { 1572 int shadeIndex = getShadeIndex(normixA); 1573 if (colixA != colixCurrent || currentShadeIndex != shadeIndex) { 1574 currentShadeIndex = -1; 1575 setC(colixA); 1576 setColorNoisy(shadeIndex); 1577 } 1578 return false; 1579 } 1580 setTriangleTranslucency(colixA, colixB, colixC); 1581 ((TriangleRenderer) triangle3d).setGouraud( 1582 getShades(colixA)[getShadeIndex(normixA)], 1583 getShades(colixB)[getShadeIndex(normixB)], 1584 getShades(colixC)[getShadeIndex(normixC)]); 1585 return true; 1586 1587 } 1588 1589 private static byte nullShadeIndex = 50; 1590 getShadeIndex(short normix)1591 public int getShadeIndex(short normix) { 1592 // from Graphics3D.fillTriangle 1593 return (normix == ~Normix.NORMIX_NULL || normix == Normix.NORMIX_NULL ? nullShadeIndex 1594 : normix < 0 ? shadeIndexes2Sided[~normix] : shadeIndexes[normix]); 1595 } setTriangleTranslucency(short colixA, short colixB, short colixC)1596 private void setTriangleTranslucency(short colixA, short colixB, short colixC) { 1597 if (isPass2) { 1598 int maskA = colixA & C.TRANSLUCENT_MASK; 1599 int maskB = colixB & C.TRANSLUCENT_MASK; 1600 int maskC = colixC & C.TRANSLUCENT_MASK; 1601 maskA &= ~C.TRANSPARENT; 1602 maskB &= ~C.TRANSPARENT; 1603 maskC &= ~C.TRANSPARENT; 1604 int mask = roundInt((maskA + maskB + maskC) / 3) & C.TRANSLUCENT_MASK; 1605 translucencyMask = (mask << C.ALPHA_SHIFT) | 0xFFFFFF; 1606 } 1607 } 1608 1609 /* *************************************************************** 1610 * quadrilaterals 1611 * ***************************************************************/ 1612 1613 @Override fillQuadrilateral(P3 screenA, P3 screenB, P3 screenC, P3 screenD, boolean isSolid)1614 public void fillQuadrilateral(P3 screenA, P3 screenB, P3 screenC, P3 screenD, boolean isSolid) { 1615 // cartoon ribbon, rocket boxes 1616 int i = getShadeIndexP3(screenA, screenB, screenC, isSolid); 1617 if (i < 0) 1618 return; 1619 setColorNoisy(i); 1620 fillTriangleP3f(screenA, screenB, screenC, false); 1621 // don't do the following; it creates a moire pattern in cartoons 1622 // setColorNoisy(i); 1623 fillTriangleP3f(screenA, screenC, screenD, false); 1624 } 1625 1626 @Override drawSurface(MeshSurface meshSurface, short colix)1627 public void drawSurface(MeshSurface meshSurface, short colix) { 1628 // Export3D only 1629 } 1630 1631 @Override plotPixelClippedP3i(P3i screen)1632 public void plotPixelClippedP3i(P3i screen) { 1633 // hermite only; export checks for clipping; overridden in Export3D 1634 plotPixelClippedArgb(argbCurrent, screen.x, screen.y, screen.z, width, 1635 zbuf, pixel); 1636 } 1637 plotPixelClippedArgb(int argb, int x, int y, int z, int width, int[] zbuf, Pixelator p)1638 void plotPixelClippedArgb(int argb, int x, int y, int z, int width, 1639 int[] zbuf, Pixelator p) { 1640 // cylinder3d plotRaster 1641 if (isClipped3(x, y, z)) 1642 return; 1643 int offset = y * width + x; 1644 if (z < zbuf[offset]) 1645 p.addPixel(offset, z, argb); 1646 } 1647 plotPixelUnclipped(int argb, int x, int y, int z, int width, int[] zbuf, Pixelator p)1648 void plotPixelUnclipped(int argb, int x, int y, int z, int width, int[] zbuf, 1649 Pixelator p) { 1650 // circle (halo) 1651 int offset = y * width + x; 1652 if (z < zbuf[offset]) 1653 p.addPixel(offset, z, argb); 1654 } 1655 1656 @Override plotImagePixel(int argb, int x, int y, int z, byte shade, int bgargb, int width, int height, int[] zbuf, Object p, int transpLog)1657 public void plotImagePixel(int argb, int x, int y, int z, byte shade, 1658 int bgargb, int width, int height, int[] zbuf, 1659 Object p, int transpLog) { 1660 // drawString via text3d.plotClipped; overridden in Export 1661 if (x < 0 || x >= width || y < 0 || y >= height) 1662 return; 1663 ((Pixelator)p).addImagePixel(shade, transpLog, y * width + x, z, argb, bgargb); 1664 } 1665 plotPixelsClippedRaster(int count, int x, int y, int zAtLeft, int zPastRight, Rgb16 rgb16Left, Rgb16 rgb16Right)1666 void plotPixelsClippedRaster(int count, int x, int y, int zAtLeft, 1667 int zPastRight, Rgb16 rgb16Left, Rgb16 rgb16Right) { 1668 // cylinder3d.renderFlatEndcap, triangle3d.fillRaster 1669 int depth, slab; 1670 if (count <= 0 || y < 0 || y >= height || x >= width 1671 || (zAtLeft < (slab = this.slab) && zPastRight < slab) 1672 || (zAtLeft > (depth = this.depth) && zPastRight > depth)) 1673 return; 1674 int[] zb = zbuf; 1675 int seed = (x << 16) + (y << 1) ^ 0x33333333; 1676 // scale the z coordinates; 1677 int zScaled = (zAtLeft << 10) + (1 << 9); 1678 int dz = zPastRight - zAtLeft; 1679 int roundFactor = count / 2; 1680 int zIncrementScaled = roundInt(((dz << 10) + (dz >= 0 ? roundFactor 1681 : -roundFactor)) / count); 1682 if (x < 0) { 1683 x = -x; 1684 zScaled += zIncrementScaled * x; 1685 count -= x; 1686 if (count <= 0) 1687 return; 1688 x = 0; 1689 } 1690 if (count + x > width) 1691 count = width - x; 1692 int offsetPbuf = y * width + x; 1693 Pixelator p = pixel; 1694 if (rgb16Left == null) { 1695 int adn = argbNoisyDn; 1696 int aup = argbNoisyUp; 1697 int ac = argbCurrent; 1698 while (--count >= 0) { 1699 int z = zScaled >> 10; 1700 if (z >= slab && z <= depth && z < zb[offsetPbuf]) { 1701 seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF; 1702 int bits = (seed >> 16) & 0x07; 1703 p.addPixel(offsetPbuf, z, bits == 0 ? adn : (bits == 1 ? aup : ac)); 1704 } 1705 ++offsetPbuf; 1706 zScaled += zIncrementScaled; 1707 } 1708 } else { 1709 int rScaled = rgb16Left.r << 8; 1710 int rIncrement = ((rgb16Right.r - rgb16Left.r) << 8) / count; 1711 int gScaled = rgb16Left.g; 1712 int gIncrement = (rgb16Right.g - gScaled) / count; 1713 int bScaled = rgb16Left.b; 1714 int bIncrement = (rgb16Right.b - bScaled) / count; 1715 while (--count >= 0) { 1716 int z = zScaled >> 10; 1717 if (z >= slab && z <= depth && z < zb[offsetPbuf]) 1718 p.addPixel(offsetPbuf, z, 0xFF000000 | (rScaled & 0xFF0000) 1719 | (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF)); 1720 ++offsetPbuf; 1721 zScaled += zIncrementScaled; 1722 rScaled += rIncrement; 1723 gScaled += gIncrement; 1724 bScaled += bIncrement; 1725 } 1726 } 1727 } 1728 plotPixelsUnclippedRaster(int count, int x, int y, int zAtLeft, int zPastRight, Rgb16 rgb16Left, Rgb16 rgb16Right)1729 void plotPixelsUnclippedRaster(int count, int x, int y, int zAtLeft, 1730 int zPastRight, Rgb16 rgb16Left, 1731 Rgb16 rgb16Right) { 1732 // for isosurface Triangle3D.fillRaster 1733 if (count <= 0) 1734 return; 1735 int seed = ((x << 16) + (y << 1) ^ 0x33333333) & 0x7FFFFFFF; 1736 // scale the z coordinates; 1737 int zScaled = (zAtLeft << 10) + (1 << 9); 1738 int dz = zPastRight - zAtLeft; 1739 int roundFactor = count / 2; 1740 int zIncrementScaled = roundInt(((dz << 10) + (dz >= 0 ? roundFactor 1741 : -roundFactor)) / count); 1742 int offsetPbuf = y * width + x; 1743 int[] zb = zbuf; 1744 Pixelator p = pixel; 1745 if (rgb16Left == null) { 1746 int adn = argbNoisyDn; 1747 int aup = argbNoisyUp; 1748 int ac = argbCurrent; 1749 while (--count >= 0) { 1750 int z = zScaled >> 10; 1751 if (z < zb[offsetPbuf]) { 1752 seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF; 1753 int bits = (seed >> 16) & 0x07; 1754 p.addPixel(offsetPbuf, z, bits == 0 ? adn : (bits == 1 ? aup : ac)); 1755 } 1756 ++offsetPbuf; 1757 zScaled += zIncrementScaled; 1758 } 1759 } else { 1760 int rScaled = rgb16Left.r << 8; 1761 int rIncrement = roundInt(((rgb16Right.r - rgb16Left.r) << 8) / count); 1762 int gScaled = rgb16Left.g; 1763 int gIncrement = roundInt((rgb16Right.g - gScaled) / count); 1764 int bScaled = rgb16Left.b; 1765 int bIncrement = roundInt((rgb16Right.b - bScaled) / count); 1766 while (--count >= 0) { 1767 int z = zScaled >> 10; 1768 if (z < zb[offsetPbuf]) 1769 p.addPixel(offsetPbuf, z, 0xFF000000 | (rScaled & 0xFF0000) 1770 | (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF)); 1771 ++offsetPbuf; 1772 zScaled += zIncrementScaled; 1773 rScaled += rIncrement; 1774 gScaled += gIncrement; 1775 bScaled += bIncrement; 1776 } 1777 } 1778 } 1779 1780 plotPixelsClippedRasterBits(int count, int x, int y, int zAtLeft, int zPastRight, Rgb16 rgb16Left, Rgb16 rgb16Right, float a, float b)1781 void plotPixelsClippedRasterBits(int count, int x, int y, int zAtLeft, 1782 int zPastRight, Rgb16 rgb16Left, Rgb16 rgb16Right, float a, float b) { 1783 // cylinder3d.renderFlatEndcap, triangle3d.fillRaster 1784 int depth, slab; 1785 1786 if (count <= 0 || y < 0 || y >= height || x >= width 1787 || (zAtLeft < (slab = this.slab) && zPastRight < slab) 1788 || (zAtLeft > (depth = this.depth) && zPastRight > depth)) 1789 return; 1790 int[] zb = zbuf; 1791 int seed = (x << 16) + (y << 1) ^ 0x33333333; 1792 if (x < 0) { 1793 x = -x; 1794 count -= x; 1795 if (count <= 0) 1796 return; 1797 x = 0; 1798 } 1799 if (count + x > width) 1800 count = width - x; 1801 int offsetPbuf = y * width + x; 1802 Pixelator p = pixel; 1803 if (rgb16Left == null) { 1804 int adn = argbNoisyDn; 1805 int aup = argbNoisyUp; 1806 int ac = argbCurrent; 1807 1808 while (--count >= 0) { 1809 int zCurrent = line3d.getZCurrent(a, b, x++); 1810 if (zCurrent >= slab && zCurrent <= depth && zCurrent < zb[offsetPbuf]) { 1811 seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF; 1812 int bits = (seed >> 16) & 0x07; 1813 1814 p.addPixel(offsetPbuf, zCurrent, bits <2 ? adn : bits < 6 ? aup : ac); 1815 } 1816 ++offsetPbuf; 1817 } 1818 } else { 1819 int rScaled = rgb16Left.r << 8; 1820 int rIncrement = ((rgb16Right.r - rgb16Left.r) << 8) / count; 1821 int gScaled = rgb16Left.g; 1822 int gIncrement = (rgb16Right.g - gScaled) / count; 1823 int bScaled = rgb16Left.b; 1824 int bIncrement = (rgb16Right.b - bScaled) / count; 1825 while (--count >= 0) { 1826 int zCurrent = line3d.getZCurrent(a, b, x++); 1827 if (zCurrent >= slab && zCurrent <= depth && zCurrent < zb[offsetPbuf]) 1828 p.addPixel(offsetPbuf, zCurrent, 0xFF000000 | (rScaled & 0xFF0000) 1829 | (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF)); 1830 ++offsetPbuf; 1831 rScaled += rIncrement; 1832 gScaled += gIncrement; 1833 bScaled += bIncrement; 1834 } 1835 } 1836 } 1837 plotPixelsUnclippedRasterBits(int count, int x, int y, Rgb16 rgb16Left, Rgb16 rgb16Right, float a, float b)1838 void plotPixelsUnclippedRasterBits(int count, int x, int y, 1839 Rgb16 rgb16Left, 1840 Rgb16 rgb16Right, float a, float b) { 1841 // for isosurface Triangle3D.fillRaster 1842 1843 if (count <= 0) 1844 return; 1845 int seed = ((x << 16) + (y << 1) ^ 0x33333333) & 0x7FFFFFFF; 1846 // scale the z coordinates; 1847 int offsetPbuf = y * width + x; 1848 int[] zb = zbuf; 1849 Pixelator p = pixel; 1850 if (rgb16Left == null) { 1851 int adn = argbNoisyDn; 1852 int aup = argbNoisyUp; 1853 int ac = argbCurrent; 1854 while (--count >= 0) { 1855 int zCurrent = line3d.getZCurrent(a, b, x++); 1856 if (zCurrent < zb[offsetPbuf]) { 1857 seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF; 1858 int bits = (seed >> 16) & 0x07; 1859 int c =(bits == 0 ? adn : bits == 1 ? aup : ac); 1860 //System.out.println(bits + " " + adn + " " + aup + " " + ac + " " + Integer.toHexString(c)); 1861 p.addPixel(offsetPbuf, zCurrent, c); 1862 } 1863 ++offsetPbuf; 1864 } 1865 } else { 1866 int rScaled = rgb16Left.r << 8; 1867 int rIncrement = roundInt(((rgb16Right.r - rgb16Left.r) << 8) / count); 1868 int gScaled = rgb16Left.g; 1869 int gIncrement = roundInt((rgb16Right.g - gScaled) / count); 1870 int bScaled = rgb16Left.b; 1871 int bIncrement = roundInt((rgb16Right.b - bScaled) / count); 1872 while (--count >= 0) { 1873 int zCurrent = line3d.getZCurrent(a, b, x++); 1874 if (zCurrent < zb[offsetPbuf]) 1875 p.addPixel(offsetPbuf, zCurrent, 0xFF000000 | (rScaled & 0xFF0000) 1876 | (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF)); 1877 ++offsetPbuf; 1878 rScaled += rIncrement; 1879 gScaled += gIncrement; 1880 bScaled += bIncrement; 1881 } 1882 } 1883 } 1884 1885 1886 plotPixelsUnclippedCount(int c, int count, int x, int y, int z, int width, int[] zbuf, Pixelator p)1887 void plotPixelsUnclippedCount(int c, int count, int x, int y, int z, 1888 int width, int[] zbuf, Pixelator p) { 1889 1890 // for Cirle3D.plot8Filled and fillRect 1891 1892 int offsetPbuf = y * width + x; 1893 while (--count >= 0) { 1894 if (z < zbuf[offsetPbuf]) 1895 p.addPixel(offsetPbuf, z, c); 1896 ++offsetPbuf; 1897 } 1898 } 1899 plotPoints(int count, int[] coordinates, int xOffset, int yOffset)1900 private void plotPoints(int count, int[] coordinates, int xOffset, int yOffset) { 1901 Pixelator p = pixel; 1902 int c = argbCurrent; 1903 int[] zb = zbuf; 1904 int w = width; 1905 boolean antialias = antialiasThisFrame; 1906 for (int i = count * 3; i > 0;) { 1907 int z = coordinates[--i]; 1908 int y = coordinates[--i] + yOffset; 1909 int x = coordinates[--i] + xOffset; 1910 if (isClipped3(x, y, z)) 1911 continue; 1912 int offset = y * w + x++; 1913 if (z < zb[offset]) 1914 p.addPixel(offset, z, c); 1915 if (antialias) { 1916 offset = y * w + x; 1917 if (!isClipped3(x, y, z) && z < zb[offset]) 1918 p.addPixel(offset, z, c); 1919 offset = (++y) * w + x; 1920 if (!isClipped3(x, y, z) && z < zb[offset]) 1921 p.addPixel(offset, z, c); 1922 offset = y * w + (--x); 1923 if (!isClipped3(x, y, z) && z < zb[offset]) 1924 p.addPixel(offset, z, c); 1925 } 1926 1927 } 1928 } 1929 1930 private final V3 vectorAB = new V3(); 1931 private final V3 vectorAC = new V3(); 1932 private final V3 vectorNormal = new V3(); 1933 setColorNoisy(int shadeIndex)1934 void setColorNoisy(int shadeIndex) { 1935 currentShadeIndex = shadeIndex; 1936 argbCurrent = shadesCurrent[shadeIndex]; 1937 argbNoisyUp = shadesCurrent[shadeIndex < Shader.SHADE_INDEX_LAST ? shadeIndex + 1 1938 : Shader.SHADE_INDEX_LAST]; 1939 argbNoisyDn = shadesCurrent[shadeIndex > 0 ? shadeIndex - 1 : 0]; 1940 } 1941 getShadeIndexP3(P3 screenA, P3 screenB, P3 screenC, boolean isSolid)1942 private int getShadeIndexP3(P3 screenA, P3 screenB, P3 screenC, boolean isSolid) { 1943 // for fillTriangle and fillQuad. 1944 vectorAB.sub2(screenB, screenA); 1945 vectorAC.sub2(screenC, screenA); 1946 V3 v = vectorNormal; 1947 v.cross(vectorAB, vectorAC); 1948 int i = (v.z < 0 ? shader 1949 .getShadeIndex(v.x, v.y, -v.z) : isSolid ? -1 : shader.getShadeIndex(-v.x, -v.y, v.z)); 1950 1951 return i; 1952 } 1953 1954 ////////////////////////////////////////////////////////// 1955 1956 @Override renderBackground(JmolRendererInterface jmolRenderer)1957 public void renderBackground(JmolRendererInterface jmolRenderer) { 1958 if (backgroundImage != null) 1959 plotImage(Integer.MIN_VALUE, 0, Integer.MIN_VALUE, backgroundImage, 1960 jmolRenderer, (short) 0, 0, 0); 1961 } 1962 1963 @Override drawAtom(Atom atom, float radius)1964 public void drawAtom(Atom atom, float radius) { 1965 // radius is used for Cartesian Export only 1966 fillSphereXYZ(atom.sD, atom.sX, atom.sY, atom.sZ); 1967 } 1968 1969 // implemented only for Export3D: 1970 1971 @Override getExportType()1972 public int getExportType() { 1973 return EXPORT_NOT; 1974 } 1975 1976 @Override getExportName()1977 public String getExportName() { 1978 return null; 1979 } 1980 canDoTriangles()1981 public boolean canDoTriangles() { 1982 return true; 1983 } 1984 isCartesianExport()1985 public boolean isCartesianExport() { 1986 return false; 1987 } 1988 1989 @Override initializeExporter(Viewer vwr, double privateKey, GData g3d, Map<String, Object> params)1990 public JmolRendererInterface initializeExporter(Viewer vwr, 1991 double privateKey, GData g3d, 1992 Map<String, Object> params) { 1993 return null; 1994 } 1995 1996 @Override finalizeOutput()1997 public String finalizeOutput() { 1998 return null; 1999 } 2000 2001 @Override drawBond(P3 atomA, P3 atomB, short colixA, short colixB, byte endcaps, short mad, int bondOrder)2002 public void drawBond(P3 atomA, P3 atomB, short colixA, short colixB, 2003 byte endcaps, short mad, int bondOrder) { 2004 } 2005 2006 @Override drawEllipse(P3 ptAtom, P3 ptX, P3 ptY, boolean fillArc, boolean wireframeOnly)2007 public boolean drawEllipse(P3 ptAtom, P3 ptX, P3 ptY, boolean fillArc, 2008 boolean wireframeOnly) { 2009 return false; 2010 } 2011 getPrivateKey()2012 public double getPrivateKey() { 2013 // exporter only 2014 return 0; 2015 } 2016 2017 @Override clearFontCache()2018 public void clearFontCache() { 2019 TextRenderer.clearFontCache(); 2020 } 2021 2022 // Normix/Shading related methods 2023 2024 // only these three instance variables depend upon current orientation: 2025 2026 private final byte[] shadeIndexes = new byte[normixCount]; 2027 private final byte[] shadeIndexes2Sided = new byte[normixCount]; 2028 public int pass2Flag01; 2029 setRotationMatrix(M3 rotationMatrix)2030 public void setRotationMatrix(M3 rotationMatrix) { 2031 V3[] vertexVectors = Normix.getVertexVectors(); 2032 for (int i = normixCount; --i >= 0;) { 2033 V3 tv = transformedVectors[i]; 2034 rotationMatrix.rotate2(vertexVectors[i], tv); 2035 shadeIndexes[i] = shader.getShadeB(tv.x, -tv.y, tv.z); 2036 shadeIndexes2Sided[i] = (tv.z >= 0 ? shadeIndexes[i] : shader.getShadeB( 2037 -tv.x, tv.y, -tv.z)); 2038 } 2039 } 2040 2041 2042 /////////// special rendering /////////// 2043 2044 /** 2045 * @param minMax 2046 * @param screenWidth 2047 * @param screenHeight 2048 * @param navOffset 2049 * @param navDepth 2050 */ 2051 @Override renderCrossHairs(int[] minMax, int screenWidth, int screenHeight, P3 navOffset, float navDepth)2052 public void renderCrossHairs(int[] minMax, int screenWidth, int screenHeight, 2053 P3 navOffset, float navDepth) { 2054 // this is the square and crosshairs for the navigator 2055 boolean antialiased = isAntialiased(); 2056 setC(navDepth < 0 ? C.RED : navDepth > 100 ? C.GREEN : C.GOLD); 2057 int x = Math.max(Math.min(width, Math.round(navOffset.x)), 0); 2058 int y = Math.max(Math.min(height, Math.round(navOffset.y)), 0); 2059 int z = Math.round(navOffset.z) + 1; 2060 // TODO: fix for antialiasDisplay 2061 int off = (antialiased ? 8 : 4); 2062 int h = (antialiased ? 20 : 10); 2063 int w = (antialiased ? 2 : 1); 2064 drawRect(x - off, y, z, 0, h, w); 2065 drawRect(x, y - off, z, 0, w, h); 2066 drawRect(x - off, y - off, z, 0, h, h); 2067 off = h; 2068 h = h >> 1; 2069 setC(minMax[1] < navOffset.x ? C.YELLOW : C.GREEN); 2070 drawRect(x - off, y, z, 0, h, w); 2071 setC(minMax[0] > navOffset.x ? C.YELLOW : C.GREEN); 2072 drawRect(x + h, y, z, 0, h, w); 2073 setC(minMax[3] < navOffset.y ? C.YELLOW : C.GREEN); 2074 drawRect(x, y - off, z, 0, w, h); 2075 setC(minMax[2] > navOffset.y ? C.YELLOW : C.GREEN); 2076 drawRect(x, y + h, z, 0, w, h); 2077 } 2078 2079 @Override initializeOutput(Viewer vwr, double privateKey, Map<String, Object> params)2080 public boolean initializeOutput(Viewer vwr, double privateKey, 2081 Map<String, Object> params) { 2082 // N/A 2083 return false; 2084 } 2085 2086 } 2087