
org.jmol.g3d.Graphics3D Maven / Gradle / Ivy
/* $RCSfile$
* * $Author: hansonr $
* $Date: 2016-10-10 05:59:27 +0200 (Mon, 10 Oct 2016) $
* $Revision: 21263 $
*
* Copyright (C) 2003-2006 Miguel, Jmol Development, www.jmol.org
*
* Contact: [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jmol.g3d;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import org.jmol.api.Interface;
import org.jmol.api.JmolRendererInterface;
import org.jmol.c.STER;
import org.jmol.modelset.Atom;
import org.jmol.script.T;
import org.jmol.util.C;
import org.jmol.util.GData;
import org.jmol.util.MeshSurface;
import org.jmol.util.Normix;
import javajs.api.GenericPlatform;
import javajs.awt.Font;
import javajs.util.AU;
import javajs.util.M3;
import javajs.util.M4;
import javajs.util.P3;
import javajs.util.P3i;
import javajs.util.T3;
import org.jmol.util.Rgb16;
import org.jmol.util.Shader;
import javajs.util.V3;
import org.jmol.viewer.Viewer;
/**
* Provides high-level graphics primitives for 3D visualization for the software
* renderers. These methods should not have to be used with WebGL or OpenGL or
* other hardware accelerators.
*
* This module is linked to via reflection from org.jmol.viewer.Viewer
*
* Bob Hanson 9/2/2012
*
* Note added 4/2015 BH:
*
* Well, it turns out that the calculation of the intermediate pixel z value
* in all methods involving rasterization of lines is incorrect and has been
* incorrect since Jmol's inception. I noticed long ago that large triangles such as
* produced in DRAW could incorrectly overlay/underlay other objects, but I could
* never determine why. It turns out that the assumption that z-value is linear
* across a line when perspectiveDepth is TRUE is simply incorrect.
*
* Basically, the function z(x) is non-linear. Treating it as simply a
* linear function results in oddities where lines and planes
* -- particularly created using DRAW and large distances -- appear
* to be where they are not.
*
* Through Jmol 13.3.13 we had the standard linear relationship:
*
* z = (x - xa) / (xb - xa) * (zb - za) + za
*
* I worked it out, and, amazingly, it should be
*
* z = (xb - xa) * za * zb / ((xb - x) * zb + (x - xa) * za)
*
* Note that it is still true that when x = xb, z = zb
* and when x = xa, z = za, as required.
*
* This equation can be rearranged to
*
* z = a / (b - x)
*
* where
*
* a = (xb - xa) * za * (zb / (zb - za))
*
* and
*
* b = (xb * zb - xa * za) / (zb - za)
*
* These values must be floats, not integers, to work properly, because
* these are extrapolations from long distances in some cases. So there is
* considerable overhead there. It will take some experimentation to figure this
* out.
*
* The practical implications are for line, cylinder, and triangle drawing.
* First-pass corrections are for axes and DRAW objects. They tend to be the
* larger objects that result in the issue.
*
* Also affected is POV-Ray output, because right now POV-Ray is created using
* perspective on and plotted as though it were orthographic, but although that
* works in x and y, it does not work in z!
*
*
*
* A pure software implementation of a 3D graphics engine. No hardware required.
* Depending upon what you are rendering ... some people say it is pretty
* fast.
*
* @author Miguel, [email protected]
*
* with additions by Bob Hanson [email protected]
*
* The above is an understatement to say the least.
*
* This is a two-pass rendering system. In the first pass, all opaque
* objects are rendered. In the second pass, all translucent objects are
* rendered.
*
* If there are no translucent objects, then that is found in the first
* pass as follows:
*
* The renderers first try to set the color index of the object to be
* rendered using setColix(short colix), and that method returns false
* if we are in the wrong pass for that type of object.
*
* In addition, setColix records in the boolean haveTranslucentObjects
* whether a translucent object was seen in the first pass.
*
* The second pass is skipped if this flag is not set. This saves
* immensely on rendering time when there are no translucent objects.
*
* THUS, IT IS CRITICAL THAT ALL RENDERING OPTIONS CHECK THE COLIX USING
* g3d.setColix(short colix) PRIOR TO RENDERING.
*
* Translucency is rendered only approximately. We can't maintain a full
* buffer of all translucent objects. Instead, we "cheat" by maintaining
* one translucent z buffer. When a translucent pixel is to be written,
* its z position is checked and...
*
* ...if it is behind or at the z position of any pixel, it is ignored
* ...if it is in front of a translucent pixel, it is added to the
* translucent buffer ...if it is between an opaque and translucent
* pixel, the translucent pixel is turned opaque, and the new pixel is
* added to the translucent buffer
*
* This guarantees accurate translucency when there are no more than two
* translucent pixels between the user and an opaque pixel. It's a
* fudge, for sure. But it is pretty good, and certainly fine for
* "draft" work.
*
* Users needing more accurate translucencty are encouraged to use the
* POV-Ray export facility for production-level work.
*
* Antialiasing is accomplished as full scene antialiasing. This means
* that the width and height are doubled (both here and in
* TransformManager), the scene is rendered, and then each set of four
* pixels is averaged (roughly) as the final pixel in the width*height
* buffer.
*
* Antialiasing options allow for antialiasing of all objects:
*
* antialiasDisplay = true antialiasTranslucent = true
*
* or just the opaque ones:
*
* antialiasDisplay = true antialiasTranslucent = false
*
* or not at all:
*
* antialiasDisplay = false
*
* The difference will be speed and memory. Adding translucent objects
* doubles the buffer requirement, and adding antialiasing quadruples
* the buffer requirement.
*
* So we have:
*
* Memory requirements are significant, in multiples of (width) *
* (height) 32-bit integers:
*
* antialias OFF ON/opaque only ON/all objects
*
* no translucent 1p + 1z = 2 4p + 4z = 8 4p + 4z = 8 objects
*
* with translucent 2p + 2z = 4 5p + 5z = 10 8p + 8z = 16 objects
*
* Note that no antialising at all is required for POV-Ray output.
* POV-Ray will do antialiasing on its own.
*
* In principle we could save a bit in the case of antialiasing of just
* opaque objects and reuse the p and z buffers for the translucent
* buffer, but this hasn't been implemented because the savings isn't
* that great, and if you are going to the trouble of having
* antialiasing, you probably what it all.
*
*
*/
final public class Graphics3D extends GData implements JmolRendererInterface {
Platform3D platform;
LineRenderer line3d;
private SphereRenderer sphere3d;
private CylinderRenderer cylinder3d;
// loaded only if needed
private G3DRenderer triangle3d;
private G3DRenderer circle3d;
private G3DRenderer hermite3d;
private boolean isFullSceneAntialiasingEnabled;
private boolean antialias2;
private TextString[] strings = null;
private int stringCount;
@Override
public boolean isWebGL() {
return false;
}
@Override
public void clear() {
stringCount = 0;
strings = null;
TextRenderer.clearFontCache();
}
@Override
public void destroy() {
releaseBuffers();
platform = null;
pixel = pixel0 = pixelShaded = null;
pixelT0 = null;
pixelScreened = null;
graphicsForMetrics = null;
}
private byte[] anaglyphChannelBytes;
private boolean twoPass = false;
private boolean haveTranslucentObjects;
protected int[] pbuf;
protected int[] pbufT;
protected int[] zbuf;
protected int[] zbufT;
protected int translucencyMask;
private boolean renderLow;
private int[] shadesCurrent;
private int anaglyphLength;
Pixelator pixel;
Pixelator pixel0;
private PixelatorT pixelT0;
private PixelatorScreened pixelScreened;
private PixelatorShaded pixelShaded;
protected int zMargin;
private int[] aobuf;
void setZMargin(int dz) {
zMargin = dz;
}
public Graphics3D() {
for (int i = normixCount; --i >= 0;)
transformedVectors[i] = new V3();
}
@Override
public void initialize(Viewer vwr, GenericPlatform apiPlatform) {
this.vwr = vwr;
this.apiPlatform = apiPlatform;
platform = new Platform3D(apiPlatform);
pixel = pixel0 = new Pixelator(this);
graphicsForMetrics = platform.getGraphicsForMetrics();
line3d = new LineRenderer(this);
sphere3d = new SphereRenderer(this);
cylinder3d = new CylinderRenderer(this);
}
/**
* allows core JavaScript loading to not involve these classes
*
* @param tok
*
*/
@Override
public void addRenderer(int tok) {
switch (tok) {
case T.circle:
if (circle3d == null)
circle3d = getRenderer("Circle");
break;
case T.hermitelevel:
if (hermite3d == null)
hermite3d = getRenderer("Hermite");
//$FALL-THROUGH$
case T.triangles:
if (triangle3d == null) {
triangle3d = getRenderer("Triangle");
((PrecisionRenderer) triangle3d).isOrthographic = !vwr.tm.perspectiveDepth;
}
break;
}
}
private G3DRenderer getRenderer(String type) {
G3DRenderer r = ((G3DRenderer) Interface.getOption("g3d." + type
+ "Renderer", vwr, "render"));
if (r == null)
throw new NullPointerException("Interface");
r.set(this, this);
return r;
}
@Override
public void setWindowParameters(int width, int height, boolean antialias) {
setWinParams(width, height, antialias);
if (currentlyRendering)
endRendering();
}
@Override
public boolean checkTranslucent(boolean isAlphaTranslucent) {
if (isAlphaTranslucent)
haveTranslucentObjects = true;
return (!twoPass || twoPass && (isPass2 == isAlphaTranslucent));
}
@Override
public void beginRendering(M3 rotationMatrix, boolean translucentMode,
boolean isImageWrite, boolean renderLow) {
if (currentlyRendering)
endRendering();
this.renderLow = renderLow;
if (windowWidth != newWindowWidth || windowHeight != newWindowHeight
|| newAntialiasing != isFullSceneAntialiasingEnabled) {
windowWidth = newWindowWidth;
windowHeight = newWindowHeight;
isFullSceneAntialiasingEnabled = newAntialiasing;
releaseBuffers();
}
setRotationMatrix(rotationMatrix);
((PrecisionRenderer) line3d).isOrthographic = !vwr.tm.perspectiveDepth;
if (triangle3d != null)
((PrecisionRenderer) triangle3d).isOrthographic = !vwr.tm.perspectiveDepth;
antialiasEnabled = antialiasThisFrame = newAntialiasing;
currentlyRendering = true;
if (strings != null)
for (int i = Math.min(strings.length, stringCount); --i >= 0;)
strings[i] = null;
stringCount = 0;
twoPass = true; //only for testing -- set false to disallow second pass
isPass2 = false;
pass2Flag01 = 0;
colixCurrent = 0;
haveTranslucentObjects = wasScreened = false;
pixel = pixel0;
pixel.bgcolor = bgcolor;
translucentCoverOnly = !translucentMode;
if (pbuf == null) {
platform.allocateBuffers(windowWidth, windowHeight, antialiasThisFrame,
isImageWrite);
pbuf = platform.pBuffer;
zbuf = platform.zBuffer;
aobuf = null;
pixel0.setBuf();
if (pixelT0 != null)
pixelT0.setBuf();
if (pixelShaded != null)
pixelShaded.setBuf();
}
setWidthHeight(antialiasThisFrame);
if (pixelScreened != null)
pixelScreened.width = width;
platform.clearBuffer();
if (backgroundImage != null)
plotImage(Integer.MIN_VALUE, 0, Integer.MIN_VALUE, backgroundImage, null,
(short) 0, 0, 0);
textY = 0;
}
@Override
public void setBackgroundTransparent(boolean TF) {
if (platform != null)
platform.setBackgroundTransparent(TF);
}
private void releaseBuffers() {
pbuf = null;
zbuf = null;
pbufT = null;
zbufT = null;
aobuf = null;
platform.releaseBuffers();
line3d.clearLineCache();
}
@Override
public boolean setPass2(boolean antialiasTranslucent) {
if (!haveTranslucentObjects || !currentlyRendering)
return false;
isPass2 = true;
pass2Flag01 = 1;
colixCurrent = 0;
if (pbufT == null || antialias2 != antialiasTranslucent) {
platform.allocateTBuffers(antialiasTranslucent);
pbufT = platform.pBufferT;
zbufT = platform.zBufferT;
}
antialias2 = antialiasTranslucent;
if (antialiasThisFrame && !antialias2)
downsampleFullSceneAntialiasing(true);
platform.clearTBuffer();
if (pixelT0 == null)
pixelT0 = new PixelatorT(this);
if (pixel.p0 == null)
pixel = pixelT0;
else
pixel.p0 = pixelT0;
return true;
}
@Override
public void endRendering() {
if (!currentlyRendering)
return;
if (pbuf != null) {
if (isPass2 && pbufT != null)
for (int offset = pbufT.length; --offset >= 0;)
pbuf[offset] = mergeBufferPixel(pbuf[offset], pbufT[offset], bgcolor);
if (pixel == pixelShaded && pixelShaded.zShadePower == 0)
pixelShaded.showZBuffer();
// if (ambientOcclusion != 0) {
// if (aobuf == null)
// aobuf = new int[pbuf.length];
// else
// for (int offset = pbuf.length; --offset >= 0;)
// aobuf[offset] = 0;
// shader
// .occludePixels(pbuf, zbuf, aobuf, width, height, ambientOcclusion);
// }
if (antialiasThisFrame)
downsampleFullSceneAntialiasing(false);
}
platform.setBackgroundColor(bgcolor);
platform.notifyEndOfRendering();
currentlyRendering = isPass2 = false;
}
public static int mergeBufferPixel(int argbA, int argbB,
int bgcolor) {
if (argbB == 0 || argbA == argbB)
return argbA;
if (argbA == 0)
argbA = bgcolor;
int rbA = (argbA & 0x00FF00FF);
int gA = (argbA & 0x0000FF00);
int rbB = (argbB & 0x00FF00FF);
int gB = (argbB & 0x0000FF00);
int logAlpha = (argbB >> 24) & 0xF;
//just for now:
//0 or 1=100% opacity, 2=87.5%, 3=75%, 4=50%, 5=50%, 6 = 25%, 7 = 12.5% opacity.
switch (logAlpha) {
// 0.0 to 1.0 ==> MORE translucent
// 1/8 1/4 3/8 1/2 5/8 3/4 7/8
// t 32 64 96 128 160 192 224
// t >> 5 1 2 3 4 5 6 7
case 0: // 8:0
rbA = rbB;
gA = gB;
break;
case 1: // 7:1
rbA = (((rbB << 2) + (rbB << 1) + rbB + rbA) >> 3) & 0x00FF00FF;
gA = (((gB << 2) + +(gB << 1) + gB + gA) >> 3) & 0x0000FF00;
break;
case 2: // 3:1
rbA = (((rbB << 1) + rbB + rbA) >> 2) & 0x00FF00FF;
gA = (((gB << 1) + gB + gA) >> 2) & 0x0000FF00;
break;
case 3: // 5:3
rbA = (((rbB << 2) + rbB + (rbA << 1) + rbA) >> 3) & 0x00FF00FF;
gA = (((gB << 2) + gB + (gA << 1) + gA) >> 3) & 0x0000FF00;
break;
case 4: // 1:1
rbA = ((rbA + rbB) >> 1) & 0x00FF00FF;
gA = ((gA + gB) >> 1) & 0x0000FF00;
break;
case 5: // 3:5
rbA = (((rbB << 1) + rbB + (rbA << 2) + rbA) >> 3) & 0x00FF00FF;
gA = (((gB << 1) + gB + (gA << 2) + gA) >> 3) & 0x0000FF00;
break;
case 6: // 1:3
rbA = (((rbA << 1) + rbA + rbB) >> 2) & 0x00FF00FF;
gA = (((gA << 1) + gA + gB) >> 2) & 0x0000FF00;
break;
case 7: // 1:7
rbA = (((rbA << 2) + (rbA << 1) + rbA + rbB) >> 3) & 0x00FF00FF;
gA = (((gA << 2) + (gA << 1) + gA + gB) >> 3) & 0x0000FF00;
break;
}
return 0xFF000000 | rbA | gA;
}
@Override
public Object getScreenImage(boolean isImageWrite) {
/**
* @j2sNative var obj = this.platform.bufferedImage; if (isImageWrite) {
* this.releaseBuffers(); } return obj;
*
*/
{
return platform.bufferedImage;
}
}
@Override
public void applyAnaglygh(STER stereoMode, int[] stereoColors) {
switch (stereoMode) {
case REDCYAN:
for (int i = anaglyphLength; --i >= 0;) {
int blue = anaglyphChannelBytes[i] & 0x000000FF;
int cyan = (blue << 8) | blue;
pbuf[i] = pbuf[i] & 0xFFFF0000 | cyan;
}
break;
case CUSTOM:
//best if complementary, but they do not have to be
int color1 = stereoColors[0];
int color2 = stereoColors[1] & 0x00FFFFFF;
for (int i = anaglyphLength; --i >= 0;) {
int a = anaglyphChannelBytes[i] & 0x000000FF;
a = (a | ((a | (a << 8)) << 8)) & color2;
pbuf[i] = (pbuf[i] & color1) | a;
}
break;
case REDBLUE:
for (int i = anaglyphLength; --i >= 0;) {
int blue = anaglyphChannelBytes[i] & 0x000000FF;
pbuf[i] = (pbuf[i] & 0xFFFF0000) | blue;
}
break;
case REDGREEN:
for (int i = anaglyphLength; --i >= 0;) {
int green = (anaglyphChannelBytes[i] & 0x000000FF) << 8;
pbuf[i] = (pbuf[i] & 0xFFFF0000) | green;
}
break;
case DTI:
case DOUBLE:
case NONE:
break;
}
}
@Override
public void snapshotAnaglyphChannelBytes() {
if (currentlyRendering)
throw new NullPointerException();
anaglyphLength = windowWidth * windowHeight;
if (anaglyphChannelBytes == null
|| anaglyphChannelBytes.length != anaglyphLength)
anaglyphChannelBytes = new byte[anaglyphLength];
for (int i = anaglyphLength; --i >= 0;)
anaglyphChannelBytes[i] = (byte) pbuf[i];
}
@Override
public void releaseScreenImage() {
platform.clearScreenBufferThreaded();
}
@Override
public boolean haveTranslucentObjects() {
return haveTranslucentObjects;
}
@Override
public void setSlabAndZShade(int slabValue, int depthValue, int zSlab,
int zDepth, int zShadePower) {
setSlab(slabValue);
setDepth(depthValue);
if (zSlab < zDepth) {
if (pixelShaded == null)
pixelShaded = new PixelatorShaded(this);
pixel = pixelShaded.set(zSlab, zDepth, zShadePower);
} else {
pixel = pixel0;
}
}
private void downsampleFullSceneAntialiasing(boolean downsampleZBuffer) {
// now is the time we have to put in the correct background color
// this was a bug in 11.6.0-11.6.2.
// we must downsample the Z Buffer if there are translucent
// objects left to draw and antialiasTranslucent is set false
// in that case we must fudge the background color, because
// otherwise a match of the background color with an object
// will put it in the back -- the "blue tie on a blue screen"
// television effect. We want to avoid that. Here we can do that
// because the colors will be blurred anyway.
int bgcheck = bgcolor;
if (downsampleZBuffer)
bgcheck += ((bgcheck & 0xFF) == 0xFF ? -1 : 1);
downsample2d(pbuf, windowWidth, windowHeight, bgcheck);
if (downsampleZBuffer) {
downsample2dZ(pbuf, zbuf, windowWidth, windowHeight, bgcheck);
antialiasThisFrame = false;
setWidthHeight(false);
}
}
public static void downsample2d(int[] pbuf, int width, int height, int bgcheck) {
int width4 = width << 1;
if (bgcheck != 0) {
bgcheck &= 0xFFFFFF;
for (int i = pbuf.length; --i >= 0;)
if (pbuf[i] == 0)
pbuf[i] = bgcheck;
}
int bg0 = ((bgcheck >> 2) & 0x3F3F3F3F) << 2;
bg0 += (bg0 & 0xC0C0C0C0) >> 6;
int offset1 = 0;
int offset4 = 0;
for (int i = height; --i >= 0; offset4 += width4)
for (int j = width; --j >= 0; ++offset1) {
/* more precise, but of no benefit:
int a = pbuf[offset4];
int b = pbuf[offset4++ + width4];
int c = pbuf[offset4];
int d = pbuf[offset4++ + width4];
int argb = ((((a & 0x0f0f0f) + (b & 0x0f0f0f)
+ (c & 0x0f0f0f) + (d & 0x0f0f0f)) >> 2) & 0x0f0f0f)
+ ( ((a & 0xF0F0F0) + (b & 0xF0F0F0)
+ (c & 0xF0F0F0) + (d & 0xF0F0F0)
) >> 2);
*/
int argb = ((pbuf[offset4] >> 2) & 0x3F3F3F3F)
+ ((pbuf[offset4++ + width4] >> 2) & 0x3F3F3F3F)
+ ((pbuf[offset4] >> 2) & 0x3F3F3F3F)
+ ((pbuf[offset4++ + width4] >> 2) & 0x3F3F3F3F);
argb += (argb & 0xC0C0C0C0) >> 6;
if (argb == bg0)
argb = bgcheck;
/**
* I don't know why this is necessary.
*
* @j2sNative
*
* pbuf[offset1] = argb & 0x00FFFFFF | 0xFF000000;
*/
{
pbuf[offset1] = argb & 0x00FFFFFF;
}
}
}
private static void downsample2dZ(int[] pbuf, int[] zbuf, int width,
int height, int bgcheck) {
int width4 = width << 1;
//we will add the alpha mask later
int offset1 = 0, offset4 = 0;
for (int i = height; --i >= 0; offset4 += width4)
for (int j = width; --j >= 0; ++offset1, ++offset4) {
int z = Math.min(zbuf[offset4], zbuf[offset4 + width4]);
z = Math.min(z, zbuf[++offset4]);
z = Math.min(z, zbuf[offset4 + width4]);
if (z != Integer.MAX_VALUE)
z >>= 1;
zbuf[offset1] = (pbuf[offset1] == bgcheck ? Integer.MAX_VALUE : z);
}
}
public boolean hasContent() {
return platform.hasContent();
}
int currentShadeIndex;
private int lastRawColor;
int translucencyLog;
private boolean wasScreened;
/**
* sets current color from colix color index
*
* @param colix
* the color index
* @return true or false if this is the right pass
*/
@Override
public boolean setC(short colix) {
boolean isLast = C.isColixLastAvailable(colix);
if (!isLast && colix == colixCurrent && currentShadeIndex == -1)
return true;
int mask = colix & C.TRANSLUCENT_MASK;
if (mask == C.TRANSPARENT)
return false;
if (renderLow)
mask = 0;
boolean isTranslucent = (mask != 0);
boolean isScreened = (isTranslucent && mask == C.TRANSLUCENT_SCREENED);
setScreened(isScreened);
if (!checkTranslucent(isTranslucent && !isScreened))
return false;
if (isPass2) {
translucencyMask = (mask << C.ALPHA_SHIFT) | 0xFFFFFF;
translucencyLog = mask >> C.TRANSLUCENT_SHIFT;
} else {
translucencyLog = 0;
}
colixCurrent = colix;
if (isLast) {
if (argbCurrent != lastRawColor) {
if (argbCurrent == 0)
argbCurrent = 0xFFFFFFFF;
lastRawColor = argbCurrent;
shader.setLastColix(argbCurrent, inGreyscaleMode);
}
}
shadesCurrent = getShades(colix);
currentShadeIndex = -1;
setColor(getColorArgbOrGray(colix));
return true;
}
Pixelator setScreened(boolean isScreened) {
if (wasScreened != isScreened) {
wasScreened = isScreened;
if (isScreened) {
if (pixelScreened == null)
pixelScreened = new PixelatorScreened(this, pixel0);
if (pixel.p0 == null)
pixel = pixelScreened;
else
pixel.p0 = pixelScreened;
} else if (pixel.p0 == null || pixel == pixelScreened) {
pixel = (isPass2 ? pixelT0 : pixel0);
} else {
pixel.p0 = (isPass2 ? pixelT0 : pixel0);
}
}
return pixel;
}
@Override
public void drawFilledCircle(short colixRing, short colixFill, int diameter,
int x, int y, int z) {
// Halos, Draw handles
if (isClippedZ(z))
return;
int r = (diameter + 1) / 2;
boolean isClipped = x < r || x + r >= width || y < r || y + r >= height;
if (isClipped && isClippedXY(diameter, x, y))
return;
if (colixRing != 0 && setC(colixRing)) {
if (isClipped) {
if (!isClippedXY(diameter, x, y))
((CircleRenderer) circle3d).plotCircleCenteredClipped(x, y, z,
diameter);
} else {
((CircleRenderer) circle3d).plotCircleCenteredUnclipped(x, y, z,
diameter);
}
}
if (colixFill != 0 && setC(colixFill)) {
if (isClipped)
((CircleRenderer) circle3d).plotFilledCircleCenteredClipped(x, y, z,
diameter);
else
((CircleRenderer) circle3d).plotFilledCircleCenteredUnclipped(x, y, z,
diameter);
}
}
@Override
public void volumeRender4(int diameter, int x, int y, int z) {
if (diameter == 1) {
plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel);
return;
}
if (isClippedZ(z))
return;
int r = (diameter + 1) / 2;
boolean isClipped = x < r || x + r >= width || y < r || y + r >= height;
if (isClipped && isClippedXY(diameter, x, y))
return;
if (isClipped)
((CircleRenderer) circle3d).plotFilledCircleCenteredClipped(x, y, z,
diameter);
else
((CircleRenderer) circle3d).plotFilledCircleCenteredUnclipped(x, y, z,
diameter);
}
/**
* fills a solid sphere
*
* @param diameter
* pixel count
* @param x
* center x
* @param y
* center y
* @param z
* center z
*/
@Override
public void fillSphereXYZ(int diameter, int x, int y, int z) {
switch (diameter) {
case 1:
plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel);
return;
case 0:
return;
}
if (diameter <= (antialiasThisFrame ? SphereRenderer.maxSphereDiameter2
: SphereRenderer.maxSphereDiameter))
sphere3d.render(shadesCurrent, diameter, x, y, z, null, null, null, -1,
null);
}
private int saveAmbient, saveDiffuse;
@Override
public void volumeRender(boolean TF) {
if (TF) {
saveAmbient = getAmbientPercent();
saveDiffuse = getDiffusePercent();
setAmbientPercent(100);
setDiffusePercent(0);
addRenderer(T.circle);
} else {
setAmbientPercent(saveAmbient);
setDiffusePercent(saveDiffuse);
}
}
/**
* fills a solid sphere
*
* @param diameter
* pixel count
* @param center
* javax.vecmath.Point3i defining the center
*/
@Override
public void fillSphereI(int diameter, P3i center) {
fillSphereXYZ(diameter, center.x, center.y, center.z);
}
/**
* fills a solid sphere
*
* @param diameter
* pixel count
* @param center
* a javax.vecmath.Point3f ... floats are casted to ints
*/
@Override
public void fillSphereBits(int diameter, P3 center) {
// from hermite ribbon
fillSphereXYZ(diameter, Math.round(center.x), Math.round(center.y),
Math.round(center.z));
}
@Override
public void fillEllipsoid(P3 center, P3[] points, int x, int y, int z,
int diameter, M3 mToEllipsoidal, double[] coef,
M4 mDeriv, int selectedOctant, P3[] octantPoints) {
switch (diameter) {
case 1:
plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel);
return;
case 0:
return;
}
if (diameter <= (antialiasThisFrame ? SphereRenderer.maxSphereDiameter2
: SphereRenderer.maxSphereDiameter))
sphere3d.render(shadesCurrent, diameter, x, y, z, mToEllipsoidal, coef,
mDeriv, selectedOctant, octantPoints);
}
/**
* draws a rectangle
*
* @param x
* upper left x
* @param y
* upper left y
* @param z
* upper left z
* @param zSlab
* z for slab check (for set labelsFront)
* @param rWidth
* pixel count
* @param rHeight
* pixel count
*/
@Override
public void drawRect(int x, int y, int z, int zSlab, int rWidth, int rHeight) {
// labels (and rubberband, not implemented) and navigation cursor
if (zSlab != 0 && isClippedZ(zSlab))
return;
int w = rWidth - 1;
int h = rHeight - 1;
int xRight = x + w;
int yBottom = y + h;
if (y >= 0 && y < height)
drawHLine(x, y, z, w);
if (yBottom >= 0 && yBottom < height)
drawHLine(x, yBottom, z, w);
if (x >= 0 && x < width)
drawVLine(x, y, z, h);
if (xRight >= 0 && xRight < width)
drawVLine(xRight, y, z, h);
}
private void drawHLine(int x, int y, int z, int w) {
// hover, labels only
if (w < 0) {
x += w;
w = -w;
}
if (x < 0) {
w += x;
x = 0;
}
if (x + w >= width)
w = width - 1 - x;
Pixelator p = pixel;
int c = argbCurrent;
int offset = x + width * y;
for (int i = 0; i <= w; i++) {
if (z < zbuf[offset])
p.addPixel(offset, z, c);
offset++;
}
}
private void drawVLine(int x, int y, int z, int h) {
// hover, labels only
if (h < 0) {
y += h;
h = -h;
}
if (y < 0) {
h += y;
y = 0;
}
if (y + h >= height) {
h = height - 1 - y;
}
int offset = x + width * y;
Pixelator p = pixel;
int c = argbCurrent;
for (int i = 0; i <= h; i++) {
if (z < zbuf[offset])
p.addPixel(offset, z, c);
offset += width;
}
}
/**
* fills background rectangle for label
*
*
* @param x
* upper left x
* @param y
* upper left y
* @param z
* upper left z
* @param zSlab
* z value for slabbing
* @param widthFill
* pixel count
* @param heightFill
* pixel count
*/
@Override
public void fillTextRect(int x, int y, int z, int zSlab, int widthFill,
int heightFill) {
// hover and labels only -- slab at atom or front -- simple Z/window clip
if (isClippedZ(zSlab))
return;
int w = width;
if (x < 0) {
widthFill += x;
if (widthFill <= 0)
return;
x = 0;
}
if (x + widthFill > w) {
widthFill = w - x;
if (widthFill <= 0)
return;
}
if (y < 0) {
heightFill += y;
if (heightFill <= 0)
return;
y = 0;
}
if (y + heightFill > height)
heightFill = height - y;
int c = argbCurrent;
int[] zb = zbuf;
Pixelator p = pixel;
while (--heightFill >= 0)
plotPixelsUnclippedCount(c, widthFill, x, y++, z, w, zb, p);
}
/**
* draws the specified string in the current font. no line wrapping -- axis,
* labels, measures
*
* @param str
* the String
* @param font3d
* the Font3D
* @param xBaseline
* baseline x
* @param yBaseline
* baseline y
* @param z
* baseline z
* @param zSlab
* z for slab calculation
* @param bgColix
*/
@Override
public void drawString(String str, Font font3d, int xBaseline, int yBaseline,
int z, int zSlab, short bgColix) {
//axis, labels, measures, echo
currentShadeIndex = 0;
if (str == null)
return;
if (isClippedZ(zSlab))
return;
drawStringNoSlab(str, font3d, xBaseline, yBaseline, z, bgColix);
}
/**
* draws the specified string in the current font. no line wrapping -- echo,
* frank, hover, molecularOrbital, uccage
*
* @param str
* the String
* @param font3d
* the Font3D
* @param xBaseline
* baseline x
* @param yBaseline
* baseline y
* @param z
* baseline z
* @param bgColix
*/
@Override
public void drawStringNoSlab(String str, Font font3d, int xBaseline,
int yBaseline, int z, short bgColix) {
// echo, frank, hover, molecularOrbital, uccage
if (str == null)
return;
if (strings == null)
strings = new TextString[10];
if (stringCount == strings.length)
strings = (TextString[]) AU.doubleLength(strings);
TextString t = new TextString();
t.setText(str, font3d == null ? currentFont : (currentFont = font3d),
argbCurrent, C.isColixTranslucent(bgColix) ? // shift colix translucency mask into integer alpha position
(getColorArgbOrGray(bgColix) & 0xFFFFFF)
| ((bgColix & C.TRANSLUCENT_MASK) << C.ALPHA_SHIFT)
: 0, xBaseline, yBaseline, z);
strings[stringCount++] = t;
}
public static Comparator sort;
@Override
public void renderAllStrings(Object jmolRenderer) {
if (strings == null)
return;
if (stringCount >= 2) {
if (sort == null)
sort = new TextString();
Arrays.sort(strings, sort);
}
for (int i = 0; i < stringCount; i++) {
TextString ts = strings[i];
plotText(ts.x, ts.y, ts.z, ts.argb, ts.bgargb, ts.text, ts.font,
(JmolRendererInterface) jmolRenderer);
}
strings = null;
stringCount = 0;
}
@Override
public void plotText(int x, int y, int z, int argb, int bgargb, String text,
Font font3d, JmolRendererInterface jmolRenderer) {
TextRenderer.plot(x, y, z, argb, bgargb, text, font3d, this, jmolRenderer,
antialiasThisFrame);
}
@Override
public void drawImage(Object objImage, int x, int y, int z, int zSlab,
short bgcolix, int width, int height) {
// overridden in Export
if (objImage != null && width > 0 && height > 0 && !isClippedZ(zSlab))
plotImage(x, y, z, objImage, null, bgcolix, width, height);
}
@Override
public void plotImage(int x, int y, int z, Object image,
JmolRendererInterface jmolRenderer, short bgcolix,
int imageWidth, int imageHeight) {
// overridden in __Exporter
setC(bgcolix);
if (!isPass2)
translucencyMask = -1;
if (bgcolix == 0)
argbCurrent = 0;
boolean isBackground = (x == Integer.MIN_VALUE);
int bg = (isBackground ? bgcolor : argbCurrent);
if (isBackground) {
x = 0;
z = Integer.MAX_VALUE - 1;
imageWidth = width;
imageHeight = height;
}
if (x + imageWidth <= 0 || x >= width || y + imageHeight <= 0
|| y >= height)
return;
Object g;
/**
* @j2sNative
*
* g = null;
*
*/
{
g = platform.getGraphicsForTextOrImage(imageWidth, imageHeight);
}
int[] buffer = apiPlatform.drawImageToBuffer(g,
platform.offscreenImage, image, imageWidth, imageHeight,
isBackground ? bg : 0);
if (buffer == null)
return;
int[] zb = zbuf;
int w = width;
Pixelator p = pixel;
int h = height;
int t = translucencyLog;
if (jmolRenderer == null
&& (x >= 0 && x + imageWidth <= w && y >= 0 && y + imageHeight <= h)) {
// unclipped
for (int i = 0, offset = 0, pbufOffset = y * w + x; i < imageHeight; i++, pbufOffset += (w - imageWidth)) {
for (int j = 0; j < imageWidth; j++,offset++,pbufOffset++) {
if (z < zb[pbufOffset]) {
int b = buffer[offset];
if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000))
p.addPixel(pbufOffset, z, b);
}
}
}
} else {
if (jmolRenderer == null)
jmolRenderer = this;
// clipped in some way -- let G3D or Exporter handle it
for (int i = 0, offset = 0; i < imageHeight; i++)
for (int j = 0; j < imageWidth; j++) {
int b = buffer[offset++];
if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000))
jmolRenderer.plotImagePixel(b, x + j, y + i, z, (byte) 8, bg, w,
h, zb, p, t);
}
}
}
@Override
public void setFontFid(byte fid) {
currentFont = Font.getFont3D(fid);
}
@Override
public void setFont(Font font3d) {
currentFont = font3d;
}
/*
private void setRectClip(int x, int y, int width, int height) {
// not implemented
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x + width > windowWidth)
width = windowWidth - x;
if (y + height > windowHeight)
height = windowHeight - y;
clipX = x;
clipY = y;
clipWidth = width;
clipHeight = height;
if (antialiasThisFrame) {
clipX *= 2;
clipY *= 2;
clipWidth *= 2;
clipHeight *= 2;
}
}
*/
//mostly public drawing methods -- add "public" if you need to
/* ***************************************************************
* points
* ***************************************************************/
@Override
public void drawPixel(int x, int y, int z) {
// measures - render angle
plotPixelClippedArgb(argbCurrent, x, y, z, width, zbuf, pixel);
}
@Override
public void drawPoints(int count, int[] coordinates, int scale) {
// for dots only
if (scale > 1) {
float s2 = scale * scale * 0.8f;
for (int i = -scale; i < scale; i++) {
for (int j = -scale; j < scale; j++) {
if (i * i + j * j > s2)
continue;
plotPoints(count, coordinates, i, j);
plotPoints(count, coordinates, i, j);
}
}
} else {
plotPoints(count, coordinates, 0, 0);
}
}
/* ***************************************************************
* lines and cylinders
* ***************************************************************/
@Override
public void drawDashedLineBits(int run, int rise, P3 pointA, P3 pointB) {
// measures only
setScreeni(pointA, sA);
setScreeni(pointB, sB);
line3d.plotLineBits(argbCurrent, argbCurrent, sA, sB, run, rise, true);
}
private void setScreeni(P3 pt, P3i p) {
p.x = Math.round(pt.x);
p.y = Math.round(pt.y);
p.z = Math.round(pt.z);
}
@Override
public void drawLineXYZ(int x1, int y1, int z1, int x2, int y2, int z2) {
// stars, text
line3d.plotLineOld(argbCurrent, argbCurrent, x1, y1, z1, x2, y2, z2);
}
@Override
public void drawLine(short colixA, short colixB, int x1, int y1, int z1,
int x2, int y2, int z2) {
// backbone and sticks
if (!setC(colixA))
colixA = 0;
int argbA = argbCurrent;
if (!setC(colixB))
colixB = 0;
if (colixA != 0 || colixB != 0)
line3d.plotLineOld(argbA, argbCurrent, x1, y1, z1, x2, y2, z2);
}
@Override
public void drawLineBits(short colixA, short colixB, P3 pointA, P3 pointB) {
// drawQuadBits, drawTriangleBits
if (!setC(colixA))
colixA = 0;
int argbA = argbCurrent;
if (!setC(colixB))
colixB = 0;
if (colixA != 0 || colixB != 0) {
setScreeni(pointA, sA);
setScreeni(pointB, sB);
line3d.plotLineBits(argbA, argbCurrent, sA, sB, 0, 0, false);
}
}
@Override
public void drawLineAB(P3 pointA, P3 pointB) {
// draw quadrilateral and hermite
setScreeni(pointA, sA);
setScreeni(pointB, sB);
line3d.plotLineBits(argbCurrent, argbCurrent, sA, sB, 0, 0, false);
}
@Override
public void fillCylinderXYZ(short colixA, short colixB, byte endcaps,
int diameter, int xA, int yA, int zA, int xB,
int yB, int zB) {
//Backbone, Mps, Sticks
if (diameter > ht3)
return;
int screen = 0;
currentShadeIndex = 0;
if (!setC(colixB))
colixB = 0;
if (wasScreened)
screen = 2;
if (!setC(colixA))
colixA = 0;
if (wasScreened)
screen += 1;
if (colixA == 0 && colixB == 0)
return;
cylinder3d.renderOld(colixA, colixB, screen, endcaps, diameter, xA, yA, zA,
xB, yB, zB);
}
// @Override
// public void fillCylinderScreen(byte endcaps, int diameter, int xA, int yA,
// int zA, int xB, int yB, int zB) {
// //measures, vectors, polyhedra
// if (diameter <= ht3)
// cylinder3d.renderOld(colixCurrent, colixCurrent, 0, endcaps, diameter, xA, yA,
// zA, xB, yB, zB);
// }
//
@Override
public void fillCylinderScreen3I(byte endcaps, int diameter, P3 screenA,
P3 screenB, P3 pt0f, P3 pt1f, float radius) {
//nucleic cartoon, draw arrowhead
// this needs to be old style, not exact for performance in JavaScript.
if (diameter <= ht3)
cylinder3d.renderOld(colixCurrent, colixCurrent, 0, endcaps, diameter,
(int) screenA.x, (int) screenA.y, (int) screenA.z, (int) screenB.x, (int) screenB.y, (int) screenB.z);
}
@Override
public void fillCylinder(byte endcaps, int diameter, P3i screenA, P3i screenB) {
// mesh(low-precision)
if (diameter <= ht3)
cylinder3d.renderOld(colixCurrent, colixCurrent, 0, endcaps, diameter,
screenA.x, screenA.y, screenA.z, screenB.x, screenB.y, screenB.z);
}
@Override
public void fillCylinderBits(byte endcaps, int diameter, P3 screenA,
P3 screenB) {
// dipole cross, cartoonRockets, draw line
if (diameter <= ht3 && screenA.z != 1 && screenB.z != 1) {
setScreeni(screenA, sA);
setScreeni(screenB, sB);
cylinder3d.renderBits(colixCurrent, colixCurrent, 0, endcaps, diameter, sA, sB);
}
}
@Override
public void fillCylinderBits2(short colixA, short colixB, byte endcaps,
int diameter, P3 screenA, P3 screenB) {
//Backbone, Mps, Sticks
if (diameter > ht3)
return;
int screen = 0;
currentShadeIndex = 0;
if (!setC(colixB))
colixB = 0;
if (wasScreened)
screen = 2;
if (!setC(colixA))
colixA = 0;
if (wasScreened)
screen += 1;
if (colixA == 0 && colixB == 0)
return;
setScreeni(screenA, sA);
setScreeni(screenB, sB);
cylinder3d.renderBits(colixA, colixB, screen, endcaps, diameter, sA, sB);
}
@Override
public void fillConeScreen3f(byte endcap, int screenDiameter, P3 screenBase,
P3 screenTip, boolean isBarb) {
// cartoons, rockets
if (screenDiameter <= ht3)
cylinder3d.renderConeOld(colixCurrent, endcap, screenDiameter, screenBase.x,
screenBase.y, screenBase.z, screenTip.x, screenTip.y, screenTip.z,
true, isBarb);
}
@Override
public void drawHermite4(int tension, P3 s0, P3 s1, P3 s2, P3 s3) {
// bioShapeRenderer
((HermiteRenderer) hermite3d).renderHermiteRope(false, tension, 0, 0, 0,
s0, s1, s2, s3);
}
@Override
public void 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) {
if (colixBack == 0) {
((HermiteRenderer) hermite3d).renderHermiteRibbon(fill, border, tension,
s0, s1, s2, s3, s4, s5, s6, s7, aspectRatio, 0);
return;
}
((HermiteRenderer) hermite3d).renderHermiteRibbon(fill, border, tension,
s0, s1, s2, s3, s4, s5, s6, s7, aspectRatio, 1);
short colix = colixCurrent;
setC(colixBack);
((HermiteRenderer) hermite3d).renderHermiteRibbon(fill, border, tension,
s0, s1, s2, s3, s4, s5, s6, s7, aspectRatio, -1);
setC(colix);
}
@Override
public void fillHermite(int tension, int diameterBeg, int diameterMid,
int diameterEnd, P3 s0, P3 s1, P3 s2, P3 s3) {
((HermiteRenderer) hermite3d).renderHermiteRope(true, tension, diameterBeg,
diameterMid, diameterEnd, s0, s1, s2, s3);
}
@Override
public void drawTriangle3C(P3i screenA, short colixA, P3i screenB,
short colixB, P3i screenC, short colixC, int check) {
// primary method for mapped Mesh
if ((check & 1) == 1)
drawLine(colixA, colixB, screenA.x, screenA.y, screenA.z, screenB.x,
screenB.y, screenB.z);
if ((check & 2) == 2)
drawLine(colixB, colixC, screenB.x, screenB.y, screenB.z, screenC.x,
screenC.y, screenC.z);
if ((check & 4) == 4)
drawLine(colixA, colixC, screenA.x, screenA.y, screenA.z, screenC.x,
screenC.y, screenC.z);
}
@Override
public void fillTriangleTwoSided(short normix, P3 screenA, P3 screenB,
P3 screenC) {
// polyhedra
setColorNoisy(getShadeIndex(normix));
fillTriangleP3f(screenA, screenB, screenC, false);
}
private P3i sA = new P3i(), sB = new P3i(), sC = new P3i();
private void fillTriangleP3f(P3 screenA, P3 screenB, P3 screenC, boolean useGouraud) {
setScreeni(screenA, sA);
setScreeni(screenB, sB);
setScreeni(screenC, sC);
((TriangleRenderer) triangle3d).fillTriangle(sA, sB, sC, useGouraud);
}
@Override
public void fillTriangle3f(P3 screenA, P3 screenB, P3 screenC,
boolean isSolid) {
// rocket box, cartoon ribbon
int i = getShadeIndexP3(screenA, screenB, screenC, isSolid);
if (i < 0)
return;
if (isSolid)
setColorNoisy(i);
else
setColor(shadesCurrent[i]);
fillTriangleP3f(screenA, screenB, screenC, false);
}
@Override
public void fillTriangle3i(P3 screenA, P3 screenB, P3 screenC, T3 ptA,
T3 ptB, T3 ptC, boolean doShade) {
// cartoon DNA plates; preset color
if (doShade) {
V3 v = vectorAB;
v.set(screenB.x - screenA.x, screenB.y - screenA.y, screenB.z - screenA.z);
int shadeIndex;
if (screenC == null) {
shadeIndex = shader.getShadeIndex(-v.x, -v.y, v.z);
} else {
vectorAC.set(screenC.x - screenA.x, screenC.y - screenA.y, screenC.z
- screenA.z);
v.cross(v, vectorAC);
shadeIndex = v.z >= 0 ? shader.getShadeIndex(-v.x, -v.y, v.z) : shader
.getShadeIndex(v.x, v.y, -v.z);
}
if (shadeIndex > Shader.SHADE_INDEX_NOISY_LIMIT)
shadeIndex = Shader.SHADE_INDEX_NOISY_LIMIT;
setColorNoisy(shadeIndex);
}
fillTriangleP3f(screenA, screenB, screenC, false);
}
@Override
public void fillTriangle3CN(P3i screenA, short colixA, short normixA,
P3i screenB, short colixB, short normixB,
P3i screenC, short colixC, short normixC) {
((TriangleRenderer) triangle3d).fillTriangle(screenA, screenB, screenC,
checkGouraud(colixA, colixB, colixC, normixA, normixB, normixC));
}
@Override
public void fillTriangle3CNBits(P3 screenA, short colixA, short normixA, P3 screenB,
short colixB, short normixB, P3 screenC, short colixC,
short normixC, boolean twoSided) {
// mesh, isosurface
fillTriangleP3f(screenA, screenB, screenC,
checkGouraud(colixA, colixB, colixC, normixA, normixB, normixC));
}
private boolean checkGouraud(short colixA, short colixB, short colixC,
short normixA, short normixB, short normixC) {
if (!isPass2 && normixA == normixB && normixA == normixC
&& colixA == colixB && colixA == colixC) {
int shadeIndex = getShadeIndex(normixA);
if (colixA != colixCurrent || currentShadeIndex != shadeIndex) {
currentShadeIndex = -1;
setC(colixA);
setColorNoisy(shadeIndex);
}
return false;
}
setTriangleTranslucency(colixA, colixB, colixC);
((TriangleRenderer) triangle3d).setGouraud(
getShades(colixA)[getShadeIndex(normixA)],
getShades(colixB)[getShadeIndex(normixB)],
getShades(colixC)[getShadeIndex(normixC)]);
return true;
}
private static byte nullShadeIndex = 50;
public int getShadeIndex(short normix) {
// from Graphics3D.fillTriangle
return (normix == ~Normix.NORMIX_NULL || normix == Normix.NORMIX_NULL ? nullShadeIndex
: normix < 0 ? shadeIndexes2Sided[~normix] : shadeIndexes[normix]);
}
private void setTriangleTranslucency(short colixA, short colixB, short colixC) {
if (isPass2) {
int maskA = colixA & C.TRANSLUCENT_MASK;
int maskB = colixB & C.TRANSLUCENT_MASK;
int maskC = colixC & C.TRANSLUCENT_MASK;
maskA &= ~C.TRANSPARENT;
maskB &= ~C.TRANSPARENT;
maskC &= ~C.TRANSPARENT;
int mask = roundInt((maskA + maskB + maskC) / 3) & C.TRANSLUCENT_MASK;
translucencyMask = (mask << C.ALPHA_SHIFT) | 0xFFFFFF;
}
}
/* ***************************************************************
* quadrilaterals
* ***************************************************************/
@Override
public void fillQuadrilateral(P3 screenA, P3 screenB, P3 screenC, P3 screenD, boolean isSolid) {
// cartoon ribbon, rocket boxes
int i = getShadeIndexP3(screenA, screenB, screenC, isSolid);
if (i < 0)
return;
setColorNoisy(i);
fillTriangleP3f(screenA, screenB, screenC, false);
// don't do the following; it creates a moire pattern in cartoons
// setColorNoisy(i);
fillTriangleP3f(screenA, screenC, screenD, false);
}
@Override
public void drawSurface(MeshSurface meshSurface, short colix) {
// Export3D only
}
@Override
public void plotPixelClippedP3i(P3i screen) {
// hermite only; export checks for clipping; overridden in Export3D
plotPixelClippedArgb(argbCurrent, screen.x, screen.y, screen.z, width,
zbuf, pixel);
}
void plotPixelClippedArgb(int argb, int x, int y, int z, int width,
int[] zbuf, Pixelator p) {
// cylinder3d plotRaster
if (isClipped3(x, y, z))
return;
int offset = y * width + x;
if (z < zbuf[offset])
p.addPixel(offset, z, argb);
}
void plotPixelUnclipped(int argb, int x, int y, int z, int width, int[] zbuf,
Pixelator p) {
// circle (halo)
int offset = y * width + x;
if (z < zbuf[offset])
p.addPixel(offset, z, argb);
}
@Override
public void plotImagePixel(int argb, int x, int y, int z, byte shade,
int bgargb, int width, int height, int[] zbuf,
Object p, int transpLog) {
// drawString via text3d.plotClipped; overridden in Export
if (x < 0 || x >= width || y < 0 || y >= height)
return;
((Pixelator)p).addImagePixel(shade, transpLog, y * width + x, z, argb, bgargb);
}
void plotPixelsClippedRaster(int count, int x, int y, int zAtLeft,
int zPastRight, Rgb16 rgb16Left, Rgb16 rgb16Right) {
// cylinder3d.renderFlatEndcap, triangle3d.fillRaster
int depth, slab;
if (count <= 0 || y < 0 || y >= height || x >= width
|| (zAtLeft < (slab = this.slab) && zPastRight < slab)
|| (zAtLeft > (depth = this.depth) && zPastRight > depth))
return;
int[] zb = zbuf;
int seed = (x << 16) + (y << 1) ^ 0x33333333;
// scale the z coordinates;
int zScaled = (zAtLeft << 10) + (1 << 9);
int dz = zPastRight - zAtLeft;
int roundFactor = count / 2;
int zIncrementScaled = roundInt(((dz << 10) + (dz >= 0 ? roundFactor
: -roundFactor)) / count);
if (x < 0) {
x = -x;
zScaled += zIncrementScaled * x;
count -= x;
if (count <= 0)
return;
x = 0;
}
if (count + x > width)
count = width - x;
int offsetPbuf = y * width + x;
Pixelator p = pixel;
if (rgb16Left == null) {
int adn = argbNoisyDn;
int aup = argbNoisyUp;
int ac = argbCurrent;
while (--count >= 0) {
int z = zScaled >> 10;
if (z >= slab && z <= depth && z < zb[offsetPbuf]) {
seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF;
int bits = (seed >> 16) & 0x07;
p.addPixel(offsetPbuf, z, bits == 0 ? adn : (bits == 1 ? aup : ac));
}
++offsetPbuf;
zScaled += zIncrementScaled;
}
} else {
int rScaled = rgb16Left.r << 8;
int rIncrement = ((rgb16Right.r - rgb16Left.r) << 8) / count;
int gScaled = rgb16Left.g;
int gIncrement = (rgb16Right.g - gScaled) / count;
int bScaled = rgb16Left.b;
int bIncrement = (rgb16Right.b - bScaled) / count;
while (--count >= 0) {
int z = zScaled >> 10;
if (z >= slab && z <= depth && z < zb[offsetPbuf])
p.addPixel(offsetPbuf, z, 0xFF000000 | (rScaled & 0xFF0000)
| (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF));
++offsetPbuf;
zScaled += zIncrementScaled;
rScaled += rIncrement;
gScaled += gIncrement;
bScaled += bIncrement;
}
}
}
void plotPixelsUnclippedRaster(int count, int x, int y, int zAtLeft,
int zPastRight, Rgb16 rgb16Left,
Rgb16 rgb16Right) {
// for isosurface Triangle3D.fillRaster
if (count <= 0)
return;
int seed = ((x << 16) + (y << 1) ^ 0x33333333) & 0x7FFFFFFF;
// scale the z coordinates;
int zScaled = (zAtLeft << 10) + (1 << 9);
int dz = zPastRight - zAtLeft;
int roundFactor = count / 2;
int zIncrementScaled = roundInt(((dz << 10) + (dz >= 0 ? roundFactor
: -roundFactor)) / count);
int offsetPbuf = y * width + x;
int[] zb = zbuf;
Pixelator p = pixel;
if (rgb16Left == null) {
int adn = argbNoisyDn;
int aup = argbNoisyUp;
int ac = argbCurrent;
while (--count >= 0) {
int z = zScaled >> 10;
if (z < zb[offsetPbuf]) {
seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF;
int bits = (seed >> 16) & 0x07;
p.addPixel(offsetPbuf, z, bits == 0 ? adn : (bits == 1 ? aup : ac));
}
++offsetPbuf;
zScaled += zIncrementScaled;
}
} else {
int rScaled = rgb16Left.r << 8;
int rIncrement = roundInt(((rgb16Right.r - rgb16Left.r) << 8) / count);
int gScaled = rgb16Left.g;
int gIncrement = roundInt((rgb16Right.g - gScaled) / count);
int bScaled = rgb16Left.b;
int bIncrement = roundInt((rgb16Right.b - bScaled) / count);
while (--count >= 0) {
int z = zScaled >> 10;
if (z < zb[offsetPbuf])
p.addPixel(offsetPbuf, z, 0xFF000000 | (rScaled & 0xFF0000)
| (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF));
++offsetPbuf;
zScaled += zIncrementScaled;
rScaled += rIncrement;
gScaled += gIncrement;
bScaled += bIncrement;
}
}
}
void plotPixelsClippedRasterBits(int count, int x, int y, int zAtLeft,
int zPastRight, Rgb16 rgb16Left, Rgb16 rgb16Right, float a, float b) {
// cylinder3d.renderFlatEndcap, triangle3d.fillRaster
int depth, slab;
if (count <= 0 || y < 0 || y >= height || x >= width
|| (zAtLeft < (slab = this.slab) && zPastRight < slab)
|| (zAtLeft > (depth = this.depth) && zPastRight > depth))
return;
int[] zb = zbuf;
int seed = (x << 16) + (y << 1) ^ 0x33333333;
if (x < 0) {
x = -x;
count -= x;
if (count <= 0)
return;
x = 0;
}
if (count + x > width)
count = width - x;
int offsetPbuf = y * width + x;
Pixelator p = pixel;
if (rgb16Left == null) {
int adn = argbNoisyDn;
int aup = argbNoisyUp;
int ac = argbCurrent;
while (--count >= 0) {
int zCurrent = line3d.getZCurrent(a, b, x++);
if (zCurrent >= slab && zCurrent <= depth && zCurrent < zb[offsetPbuf]) {
seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF;
int bits = (seed >> 16) & 0x07;
p.addPixel(offsetPbuf, zCurrent, bits <2 ? adn : bits < 6 ? aup : ac);
}
++offsetPbuf;
}
} else {
int rScaled = rgb16Left.r << 8;
int rIncrement = ((rgb16Right.r - rgb16Left.r) << 8) / count;
int gScaled = rgb16Left.g;
int gIncrement = (rgb16Right.g - gScaled) / count;
int bScaled = rgb16Left.b;
int bIncrement = (rgb16Right.b - bScaled) / count;
while (--count >= 0) {
int zCurrent = line3d.getZCurrent(a, b, x++);
if (zCurrent >= slab && zCurrent <= depth && zCurrent < zb[offsetPbuf])
p.addPixel(offsetPbuf, zCurrent, 0xFF000000 | (rScaled & 0xFF0000)
| (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF));
++offsetPbuf;
rScaled += rIncrement;
gScaled += gIncrement;
bScaled += bIncrement;
}
}
}
void plotPixelsUnclippedRasterBits(int count, int x, int y,
Rgb16 rgb16Left,
Rgb16 rgb16Right, float a, float b) {
// for isosurface Triangle3D.fillRaster
if (count <= 0)
return;
int seed = ((x << 16) + (y << 1) ^ 0x33333333) & 0x7FFFFFFF;
// scale the z coordinates;
int offsetPbuf = y * width + x;
int[] zb = zbuf;
Pixelator p = pixel;
if (rgb16Left == null) {
int adn = argbNoisyDn;
int aup = argbNoisyUp;
int ac = argbCurrent;
while (--count >= 0) {
int zCurrent = line3d.getZCurrent(a, b, x++);
if (zCurrent < zb[offsetPbuf]) {
seed = ((seed << 16) + (seed << 1) + seed) & 0x7FFFFFFF;
int bits = (seed >> 16) & 0x07;
int c =(bits == 0 ? adn : bits == 1 ? aup : ac);
//System.out.println(bits + " " + adn + " " + aup + " " + ac + " " + Integer.toHexString(c));
p.addPixel(offsetPbuf, zCurrent, c);
}
++offsetPbuf;
}
} else {
int rScaled = rgb16Left.r << 8;
int rIncrement = roundInt(((rgb16Right.r - rgb16Left.r) << 8) / count);
int gScaled = rgb16Left.g;
int gIncrement = roundInt((rgb16Right.g - gScaled) / count);
int bScaled = rgb16Left.b;
int bIncrement = roundInt((rgb16Right.b - bScaled) / count);
while (--count >= 0) {
int zCurrent = line3d.getZCurrent(a, b, x++);
if (zCurrent < zb[offsetPbuf])
p.addPixel(offsetPbuf, zCurrent, 0xFF000000 | (rScaled & 0xFF0000)
| (gScaled & 0xFF00) | ((bScaled >> 8) & 0xFF));
++offsetPbuf;
rScaled += rIncrement;
gScaled += gIncrement;
bScaled += bIncrement;
}
}
}
void plotPixelsUnclippedCount(int c, int count, int x, int y, int z,
int width, int[] zbuf, Pixelator p) {
// for Cirle3D.plot8Filled and fillRect
int offsetPbuf = y * width + x;
while (--count >= 0) {
if (z < zbuf[offsetPbuf])
p.addPixel(offsetPbuf, z, c);
++offsetPbuf;
}
}
private void plotPoints(int count, int[] coordinates, int xOffset, int yOffset) {
Pixelator p = pixel;
int c = argbCurrent;
int[] zb = zbuf;
int w = width;
boolean antialias = antialiasThisFrame;
for (int i = count * 3; i > 0;) {
int z = coordinates[--i];
int y = coordinates[--i] + yOffset;
int x = coordinates[--i] + xOffset;
if (isClipped3(x, y, z))
continue;
int offset = y * w + x++;
if (z < zb[offset])
p.addPixel(offset, z, c);
if (antialias) {
offset = y * w + x;
if (!isClipped3(x, y, z) && z < zb[offset])
p.addPixel(offset, z, c);
offset = (++y) * w + x;
if (!isClipped3(x, y, z) && z < zb[offset])
p.addPixel(offset, z, c);
offset = y * w + (--x);
if (!isClipped3(x, y, z) && z < zb[offset])
p.addPixel(offset, z, c);
}
}
}
private final V3 vectorAB = new V3();
private final V3 vectorAC = new V3();
private final V3 vectorNormal = new V3();
void setColorNoisy(int shadeIndex) {
currentShadeIndex = shadeIndex;
argbCurrent = shadesCurrent[shadeIndex];
argbNoisyUp = shadesCurrent[shadeIndex < Shader.SHADE_INDEX_LAST ? shadeIndex + 1
: Shader.SHADE_INDEX_LAST];
argbNoisyDn = shadesCurrent[shadeIndex > 0 ? shadeIndex - 1 : 0];
}
private int getShadeIndexP3(P3 screenA, P3 screenB, P3 screenC, boolean isSolid) {
// for fillTriangle and fillQuad.
vectorAB.sub2(screenB, screenA);
vectorAC.sub2(screenC, screenA);
V3 v = vectorNormal;
v.cross(vectorAB, vectorAC);
int i = (v.z < 0 ? shader
.getShadeIndex(v.x, v.y, -v.z) : isSolid ? -1 : shader.getShadeIndex(-v.x, -v.y, v.z));
return i;
}
//////////////////////////////////////////////////////////
@Override
public void renderBackground(JmolRendererInterface jmolRenderer) {
if (backgroundImage != null)
plotImage(Integer.MIN_VALUE, 0, Integer.MIN_VALUE, backgroundImage,
jmolRenderer, (short) 0, 0, 0);
}
@Override
public void drawAtom(Atom atom, float radius) {
// radius is used for Cartesian Export only
fillSphereXYZ(atom.sD, atom.sX, atom.sY, atom.sZ);
}
// implemented only for Export3D:
@Override
public int getExportType() {
return EXPORT_NOT;
}
@Override
public String getExportName() {
return null;
}
public boolean canDoTriangles() {
return true;
}
public boolean isCartesianExport() {
return false;
}
@Override
public JmolRendererInterface initializeExporter(Viewer vwr,
double privateKey, GData g3d,
Map params) {
return null;
}
@Override
public String finalizeOutput() {
return null;
}
@Override
public void drawBond(P3 atomA, P3 atomB, short colixA, short colixB,
byte endcaps, short mad, int bondOrder) {
}
@Override
public boolean drawEllipse(P3 ptAtom, P3 ptX, P3 ptY, boolean fillArc,
boolean wireframeOnly) {
return false;
}
public double getPrivateKey() {
// exporter only
return 0;
}
@Override
public void clearFontCache() {
TextRenderer.clearFontCache();
}
// Normix/Shading related methods
// only these three instance variables depend upon current orientation:
private final byte[] shadeIndexes = new byte[normixCount];
private final byte[] shadeIndexes2Sided = new byte[normixCount];
public int pass2Flag01;
public void setRotationMatrix(M3 rotationMatrix) {
V3[] vertexVectors = Normix.getVertexVectors();
for (int i = normixCount; --i >= 0;) {
V3 tv = transformedVectors[i];
rotationMatrix.rotate2(vertexVectors[i], tv);
shadeIndexes[i] = shader.getShadeB(tv.x, -tv.y, tv.z);
shadeIndexes2Sided[i] = (tv.z >= 0 ? shadeIndexes[i] : shader.getShadeB(
-tv.x, tv.y, -tv.z));
}
}
/////////// special rendering ///////////
/**
* @param minMax
* @param screenWidth
* @param screenHeight
* @param navOffset
* @param navDepth
*/
@Override
public void renderCrossHairs(int[] minMax, int screenWidth, int screenHeight,
P3 navOffset, float navDepth) {
// this is the square and crosshairs for the navigator
boolean antialiased = isAntialiased();
setC(navDepth < 0 ? C.RED : navDepth > 100 ? C.GREEN : C.GOLD);
int x = Math.max(Math.min(width, Math.round(navOffset.x)), 0);
int y = Math.max(Math.min(height, Math.round(navOffset.y)), 0);
int z = Math.round(navOffset.z) + 1;
// TODO: fix for antialiasDisplay
int off = (antialiased ? 8 : 4);
int h = (antialiased ? 20 : 10);
int w = (antialiased ? 2 : 1);
drawRect(x - off, y, z, 0, h, w);
drawRect(x, y - off, z, 0, w, h);
drawRect(x - off, y - off, z, 0, h, h);
off = h;
h = h >> 1;
setC(minMax[1] < navOffset.x ? C.YELLOW : C.GREEN);
drawRect(x - off, y, z, 0, h, w);
setC(minMax[0] > navOffset.x ? C.YELLOW : C.GREEN);
drawRect(x + h, y, z, 0, h, w);
setC(minMax[3] < navOffset.y ? C.YELLOW : C.GREEN);
drawRect(x, y - off, z, 0, w, h);
setC(minMax[2] > navOffset.y ? C.YELLOW : C.GREEN);
drawRect(x, y + h, z, 0, w, h);
}
@Override
public boolean initializeOutput(Viewer vwr, double privateKey,
Map params) {
// N/A
return false;
}
}