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