Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F91356180
image.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, Nov 10, 07:17
Size
54 KB
Mime Type
text/x-c
Expires
Tue, Nov 12, 07:17 (2 d)
Engine
blob
Format
Raw Data
Handle
22248906
Attached To
rLAMMPS lammps
image.cpp
View Options
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://lammps.sandia.gov, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: Nathan Fabian (Sandia)
------------------------------------------------------------------------- */
#include "mpi.h"
#include "math.h"
#include "ctype.h"
#include "stdlib.h"
#include "string.h"
#include "image.h"
#include "math_extra.h"
#include "random_mars.h"
#include "math_const.h"
#include "error.h"
#include "memory.h"
#ifdef LAMMPS_JPEG
#include "jpeglib.h"
#endif
using namespace LAMMPS_NS;
using namespace MathConst;
#define NCOLORS 140
#define NELEMENTS 109
#define BIG 1.0e20
enum{NUMERIC,MINVALUE,MAXVALUE};
enum{CONTINUOUS,DISCRETE,SEQUENTIAL};
enum{ABSOLUTE,FRACTIONAL};
enum{NO,YES};
/* ---------------------------------------------------------------------- */
Image::Image(LAMMPS *lmp) : Pointers(lmp)
{
MPI_Comm_rank(world,&me);
MPI_Comm_size(world,&nprocs);
// defaults
width = height = 512;
theta = 60.0 * MY_PI/180.0;
phi = 30.0 * MY_PI/180.0;
zoom = 1.0;
persp = 0.0;
shiny = 1.0;
ssao = NO;
// colors
ncolors = 0;
username = NULL;
userrgb = NULL;
boxcolor = color2rgb("yellow");
background[0] = background[1] = background[2] = 0;
// default color map
mlo = MINVALUE;
mhi = MAXVALUE;
mstyle = CONTINUOUS;
mrange = FRACTIONAL;
nentry = 2;
mentry = new MapEntry[nentry];
mentry[0].svalue = 0.0;
mentry[0].color = color2rgb("blue");
mentry[1].svalue = 1.0;
mentry[1].color = color2rgb("red");
// static parameters
FOV = MY_PI/6.0; // 30 degrees
ambientColor[0] = 0.0;
ambientColor[1] = 0.0;
ambientColor[2] = 0.0;
keyLightPhi = -MY_PI4; // -45 degrees
keyLightTheta = MY_PI/6.0; // 30 degrees
keyLightColor[0] = 0.9;
keyLightColor[1] = 0.9;
keyLightColor[2] = 0.9;
fillLightPhi = MY_PI/6.0; // 30 degrees
fillLightTheta = 0;
fillLightColor[0] = 0.45;
fillLightColor[1] = 0.45;
fillLightColor[2] = 0.45;
backLightPhi = MY_PI; // 180 degrees
backLightTheta = MY_PI/12.0; // 15 degrees
backLightColor[0] = 0.9;
backLightColor[1] = 0.9;
backLightColor[2] = 0.9;
// RNG for SSAO depth shading
if (ssao) random = new RanMars(lmp,seed+me);
else random = NULL;
}
/* ---------------------------------------------------------------------- */
Image::~Image()
{
for (int i = 0; i < ncolors; i++) delete [] username[i];
memory->sfree(username);
memory->destroy(userrgb);
delete [] mentry;
memory->destroy(depthBuffer);
memory->destroy(surfaceBuffer);
memory->destroy(imageBuffer);
memory->destroy(depthcopy);
memory->destroy(surfacecopy);
memory->destroy(rgbcopy);
delete random;
}
/* ----------------------------------------------------------------------
allocate image and depth buffers
called after image size is set
------------------------------------------------------------------------- */
void Image::buffers()
{
npixels = width * height;
memory->create(depthBuffer,npixels,"image:depthBuffer");
memory->create(surfaceBuffer,2*npixels,"image:surfaceBuffer");
memory->create(imageBuffer,3*npixels,"image:imageBuffer");
memory->create(depthcopy,npixels,"image:depthcopy");
memory->create(surfacecopy,2*npixels,"image:surfacecopy");
memory->create(rgbcopy,3*npixels,"image:rgbcopy");
}
/* ----------------------------------------------------------------------
reset view parameters
called once if view is STATIC
called before every render if view is DYNAMIC
------------------------------------------------------------------------- */
void Image::view_params(double boxxlo, double boxxhi, double boxylo,
double boxyhi, double boxzlo, double boxzhi)
{
// camDir points at the camera, view direction = -camDir
camDir[0] = sin(theta)*cos(phi);
camDir[1] = sin(theta)*sin(phi);
camDir[2] = cos(theta);
// zdist = camera distance = function of zoom & bounding box
// camPos = camera position = function of camDir and zdist
double delx = 2.0*(boxxhi-boxxlo);
double dely = 2.0*(boxyhi-boxylo);
double delz = 2.0*(boxzhi-boxzlo);
double maxdel = MAX(delx,dely);
maxdel = MAX(maxdel,delz);
zdist = maxdel;
zdist /= tan(FOV);
zdist += 0.5 * (delx*camDir[0] + dely*camDir[1] + delz*camDir[2]);
zdist /= zoom;
camPos[0] = camDir[0] * zdist;
camPos[1] = camDir[1] * zdist;
camPos[2] = camDir[2] * zdist;
// normalize up vector
if (up[0] == 0.0 && up[1] == 0.0 && up[2] == 0.0)
error->all(FLERR,"Invalid image up vector");
MathExtra::norm3(up);
// rotate up if camDir and up point in same direction
// should also check for opposite direction
// NOTE: this is not yet right for preserving view as rotate thru up
if (camDir[0] == up[0] && camDir[1] == up[1] && camDir[2] == up[2]) {
double tmp = up[0];
up[0] = up[1];
up[1] = up[2];
up[2] = tmp;
}
// camUp = camDir x (Up x camDir)
MathExtra::cross3(up,camDir,camRight);
MathExtra::norm3(camRight);
MathExtra::cross3(camDir,camRight,camUp);
if (camUp[0] == 0.0 && camUp[1] == 0.0 && camUp[2] == 0.0)
error->all(FLERR,"Invalid image up vector");
MathExtra::norm3(camUp);
// light directions in terms of -camDir = z
keyLightDir[0] = cos(keyLightTheta) * sin(keyLightPhi);
keyLightDir[1] = sin(keyLightTheta);
keyLightDir[2] = cos(keyLightTheta) * cos(keyLightPhi);
fillLightDir[0] = cos(fillLightTheta) * sin(fillLightPhi);
fillLightDir[1] = sin(fillLightTheta);
fillLightDir[2] = cos(fillLightTheta) * cos(fillLightPhi);
backLightDir[0] = cos(backLightTheta) * sin(backLightPhi);
backLightDir[1] = sin(backLightTheta);
backLightDir[2] = cos(backLightTheta) * cos(backLightPhi);
keyHalfDir[0] = 0 + keyLightDir[0];
keyHalfDir[1] = 0 + keyLightDir[1];
keyHalfDir[2] = 1 + keyLightDir[2];
MathExtra::norm3(keyHalfDir);
// adjust shinyness of the reflection
specularHardness = 16.0 * shiny;
specularIntensity = shiny;
// adjust strength of the SSAO
if (ssao) {
SSAORadius = maxdel * 0.05 * ssaoint;
SSAOSamples = static_cast<int> (8.0 + 32.0*ssaoint);
SSAOJitter = MY_PI / 12;
ambientColor[0] = 0.5;
ambientColor[1] = 0.5;
ambientColor[2] = 0.5;
}
// param for rasterizing spheres
tanPerPixel = -(maxdel / (double) height);
}
/* ----------------------------------------------------------------------
set explicit values for all min/max settings in color map
lo/hi current and lvalue/hvalue settings for lo/hi = MIN/MAX VALUE in entries
if mlo/mhi = MIN/MAX VALUE, compute bounds on just the atoms being visualized
------------------------------------------------------------------------- */
void Image::color_minmax(int n, double *buf, int stride)
{
double two[2],twoall[2];
if (mlo == MINVALUE || mhi == MAXVALUE) {
double lo = BIG;
double hi = -BIG;
int m = 0;
for (int i = 0; i < n; i++) {
lo = MIN(lo,buf[m]);
hi = MAX(hi,buf[m]);
m += stride;
}
two[0] = -lo;
two[1] = hi;
MPI_Allreduce(two,twoall,2,MPI_DOUBLE,MPI_MAX,world);
}
if (mlo == MINVALUE) locurrent = -twoall[0];
else locurrent = mlovalue;
if (mhi == MAXVALUE) hicurrent = twoall[1];
else hicurrent = mhivalue;
if (locurrent > hicurrent) error->all(FLERR,"Invalid image color range");
if (mstyle == CONTINUOUS) {
if (mrange == ABSOLUTE) mentry[0].svalue = locurrent;
else mentry[0].svalue = 0.0;
if (mrange == ABSOLUTE) mentry[nentry-1].svalue = hicurrent;
else mentry[nentry-1].svalue = 1.0;
} else if (mstyle == DISCRETE) {
for (int i = 0; i < nentry; i++) {
if (mentry[i].lo == MINVALUE) {
if (mrange == ABSOLUTE) mentry[i].lvalue = locurrent;
else mentry[i].lvalue = 0.0;
}
if (mentry[i].hi == MAXVALUE) {
if (mrange == ABSOLUTE) mentry[i].hvalue = hicurrent;
else mentry[i].hvalue = 1.0;
}
}
}
}
/* ----------------------------------------------------------------------
initialize image to background color and depth buffer
no need to init surfaceBuffer, since will be based on depth
------------------------------------------------------------------------- */
void Image::clear()
{
int red = background[0];
int green = background[1];
int blue = background[2];
int ix,iy;
for (iy = 0; iy < height; iy ++)
for (ix = 0; ix < width; ix ++) {
imageBuffer[iy * width * 3 + ix * 3 + 0] = red;
imageBuffer[iy * width * 3 + ix * 3 + 1] = green;
imageBuffer[iy * width * 3 + ix * 3 + 2] = blue;
depthBuffer[iy * width + ix] = -1;
}
}
/* ----------------------------------------------------------------------
merge image from each processor into one composite image
done pixel by pixel, respecting depth buffer
hi procs send to lo procs, cascading down logarithmically
------------------------------------------------------------------------- */
void Image::merge()
{
MPI_Request requests[3];
MPI_Status statuses[3];
int nhalf = 1;
while (nhalf < nprocs) nhalf *= 2;
nhalf /= 2;
while (nhalf) {
if (me < nhalf && me+nhalf < nprocs) {
MPI_Irecv(rgbcopy,npixels*3,MPI_BYTE,me+nhalf,0,world,&requests[0]);
MPI_Irecv(depthcopy,npixels,MPI_DOUBLE,me+nhalf,0,world,&requests[1]);
if (ssao)
MPI_Irecv(surfacecopy,npixels*2,MPI_DOUBLE,
me+nhalf,0,world,&requests[2]);
if (ssao) MPI_Waitall(3,requests,statuses);
else MPI_Waitall(2,requests,statuses);
for (int i = 0; i < npixels; i++) {
if (depthBuffer[i] < 0 || (depthcopy[i] >= 0 &&
depthcopy[i] < depthBuffer[i])) {
depthBuffer[i] = depthcopy[i];
imageBuffer[i*3+0] = rgbcopy[i*3+0];
imageBuffer[i*3+1] = rgbcopy[i*3+1];
imageBuffer[i*3+2] = rgbcopy[i*3+2];
if (ssao) {
surfaceBuffer[i*2+0] = surfacecopy[i*2+0];
surfaceBuffer[i*2+1] = surfacecopy[i*2+1];
}
}
}
} else if (me >= nhalf && me < 2*nhalf) {
MPI_Send(imageBuffer,npixels*3,MPI_BYTE,me-nhalf,0,world);
MPI_Send(depthBuffer,npixels,MPI_DOUBLE,me-nhalf,0,world);
if (ssao) MPI_Send(surfaceBuffer,npixels*2,MPI_DOUBLE,me-nhalf,0,world);
}
nhalf /= 2;
}
// extra SSAO enhancement
// bcast full image to all procs
// each works on subset of pixels
// gather result back to proc 0
if (ssao) {
MPI_Bcast(imageBuffer,npixels*3,MPI_BYTE,0,world);
MPI_Bcast(surfaceBuffer,npixels*2,MPI_DOUBLE,0,world);
MPI_Bcast(depthBuffer,npixels,MPI_DOUBLE,0,world);
compute_SSAO();
int pixelPart = height/nprocs * width*3;
MPI_Gather(imageBuffer+me*pixelPart,pixelPart,MPI_BYTE,
rgbcopy,pixelPart,MPI_BYTE,0,world);
writeBuffer = rgbcopy;
} else {
writeBuffer = imageBuffer;
}
}
/* ----------------------------------------------------------------------
draw simulation bounding box as 12 cylinders
------------------------------------------------------------------------- */
void Image::draw_box(double (*corners)[3], double diameter)
{
draw_cylinder(corners[0],corners[1],boxcolor,diameter,3);
draw_cylinder(corners[2],corners[3],boxcolor,diameter,3);
draw_cylinder(corners[0],corners[2],boxcolor,diameter,3);
draw_cylinder(corners[1],corners[3],boxcolor,diameter,3);
draw_cylinder(corners[0],corners[4],boxcolor,diameter,3);
draw_cylinder(corners[1],corners[5],boxcolor,diameter,3);
draw_cylinder(corners[2],corners[6],boxcolor,diameter,3);
draw_cylinder(corners[3],corners[7],boxcolor,diameter,3);
draw_cylinder(corners[4],corners[5],boxcolor,diameter,3);
draw_cylinder(corners[6],corners[7],boxcolor,diameter,3);
draw_cylinder(corners[4],corners[6],boxcolor,diameter,3);
draw_cylinder(corners[5],corners[7],boxcolor,diameter,3);
}
/* ----------------------------------------------------------------------
draw XYZ axes in red/green/blue
axes = 4 end points
------------------------------------------------------------------------- */
void Image::draw_axes(double (*axes)[3], double diameter)
{
draw_cylinder(axes[0],axes[1],color2rgb("red"),diameter,3);
draw_cylinder(axes[0],axes[2],color2rgb("green"),diameter,3);
draw_cylinder(axes[0],axes[3],color2rgb("blue"),diameter,3);
}
/* ----------------------------------------------------------------------
draw sphere at x with surfaceColor and diameter
render pixel by pixel onto image plane with depth buffering
------------------------------------------------------------------------- */
void Image::draw_sphere(double *x, double *surfaceColor, double diameter)
{
int ix,iy;
double projRad;
double xlocal[3],surface[3];
double depth;
xlocal[0] = x[0] - xctr;
xlocal[1] = x[1] - yctr;
xlocal[2] = x[2] - zctr;
double xmap = MathExtra::dot3(camRight,xlocal);
double ymap = MathExtra::dot3(camUp,xlocal);
double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(xlocal,camDir);
double radius = 0.5*diameter;
double radsq = radius*radius;
double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist :
-tanPerPixel / zoom;
double pixelRadiusFull = radius / pixelWidth;
int pixelRadius = static_cast<int> (pixelRadiusFull + 0.5) + 1;
double xf = xmap / pixelWidth;
double yf = ymap / pixelWidth;
int xc = static_cast<int> (xf);
int yc = static_cast<int> (yf);
double width_error = xf - xc;
double height_error = yf - yc;
// shift 0,0 to screen center (vs lower left)
xc += width / 2;
yc += height / 2;
for (iy = yc - pixelRadius; iy <= yc + pixelRadius; iy++) {
for (ix = xc - pixelRadius; ix <= xc + pixelRadius; ix++) {
if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue;
surface[1] = ((iy - yc) - height_error) * pixelWidth;
surface[0] = ((ix - xc) - width_error) * pixelWidth;
projRad = surface[0]*surface[0] + surface[1]*surface[1];
// outside the sphere in the projected image
if (projRad > radsq) continue;
surface[2] = sqrt(radsq - projRad);
depth = dist - surface[2];
surface[0] /= radius;
surface[1] /= radius;
surface[2] /= radius;
draw_pixel (ix, iy, depth, surface, surfaceColor);
}
}
}
/* ----------------------------------------------------------------------
draw axis oriented cube at x with surfaceColor and diameter in size
render pixel by pixel onto image plane with depth buffering
------------------------------------------------------------------------- */
void Image::draw_cube(double *x, double *surfaceColor, double diameter)
{
double xlocal[3],surface[3],normal[3];
double t,tdir[3];
double depth;
xlocal[0] = x[0] - xctr;
xlocal[1] = x[1] - yctr;
xlocal[2] = x[2] - zctr;
double xmap = MathExtra::dot3(camRight,xlocal);
double ymap = MathExtra::dot3(camUp,xlocal);
double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(xlocal,camDir);
double radius = 0.5*diameter;
double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist :
-tanPerPixel / zoom;
double halfWidth = diameter;
double pixelHalfWidthFull = halfWidth / pixelWidth;
int pixelHalfWidth = static_cast<int> (pixelHalfWidthFull + 0.5);
double xf = xmap / pixelWidth;
double yf = ymap / pixelWidth;
int xc = static_cast<int> (xf);
int yc = static_cast<int> (yf);
double width_error = xf - xc;
double height_error = yf - yc;
// shift 0,0 to screen center (vs lower left)
xc += width / 2;
yc += height / 2;
for (int iy = yc - pixelHalfWidth; iy <= yc + pixelHalfWidth; iy ++) {
for (int ix = xc - pixelHalfWidth; ix <= xc + pixelHalfWidth; ix ++) {
if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue;
double sy = ((iy - yc) - height_error) * pixelWidth;
double sx = ((ix - xc) - width_error) * pixelWidth;
surface[0] = camRight[0] * sx + camUp[0] * sy;
surface[1] = camRight[1] * sx + camUp[1] * sy;
surface[2] = camRight[2] * sx + camUp[2] * sy;
// iterate through each of the 6 axis-oriented planes of the box
// only render up to 3 which are facing the camera
// these checks short circuit a dot product, testing for > 0
for (int dim = 0; dim < 3; dim ++) {
if (camDir[dim] > 0) { // positive faces camera
t = (radius - surface[dim]) / camDir[dim];
normal[0] = camRight[dim];
normal[1] = camUp[dim];
normal[2] = camDir[dim];
} else if (camDir[dim] < 0) { // negative faces camera
t = -(radius + surface[dim]) / camDir[dim];
normal[0] = -camRight[dim];
normal[1] = -camUp[dim];
normal[2] = -camDir[dim];
}
if (camDir[dim] != 0) {
tdir[0] = camDir[0] * t;
tdir[1] = camDir[1] * t;
tdir[2] = camDir[2] * t;
bool xin = ((surface[0]+tdir[0]) >= -radius) &&
((surface[0]+tdir[0]) <= radius);
bool yin = ((surface[1]+tdir[1]) >= -radius) &&
((surface[1]+tdir[1]) <= radius);
bool zin = ((surface[2]+tdir[2]) >= -radius) &&
((surface[2]+tdir[2]) <= radius);
switch (dim) {
case 0:
if (yin & zin) {
depth = dist - t;
draw_pixel (ix, iy, depth, normal, surfaceColor);
}
break;
case 1:
if (xin & zin) {
depth = dist - t;
draw_pixel (ix, iy, depth, normal, surfaceColor);
}
break;
case 2:
if (xin & yin) {
depth = dist - t;
draw_pixel (ix, iy, depth, normal, surfaceColor);
}
break;
}
}
}
}
}
}
/* ----------------------------------------------------------------------
draw cylinder from x to y with surfaceColor and diameter
render pixel by pixel onto image plane with depth buffering
if sflag = 0, draw no end spheres
if sflag = 1, draw 1st end sphere
if sflag = 2, draw 2nd end sphere
if sflag = 3, draw both end spheres
------------------------------------------------------------------------- */
void Image::draw_cylinder(double *x, double *y,
double *surfaceColor, double diameter, int sflag)
{
double surface[3], normal[3];
double mid[3],xaxis[3],yaxis[3],zaxis[3];
double camLDir[3], camLRight[3], camLUp[3];
double zmin, zmax;
if (sflag % 2) draw_sphere(x,surfaceColor,diameter);
if (sflag/2) draw_sphere(y,surfaceColor,diameter);
double radius = 0.5*diameter;
double radsq = radius*radius;
zaxis[0] = y[0] - x[0];
zaxis[1] = y[1] - x[1];
zaxis[2] = y[2] - x[2];
double rasterWidth = fabs(MathExtra::dot3(zaxis, camRight)) + diameter;
double rasterHeight = fabs(MathExtra::dot3(zaxis, camUp)) + diameter;
mid[0] = (y[0] + x[0]) * 0.5 - xctr;
mid[1] = (y[1] + x[1]) * 0.5 - yctr;
mid[2] = (y[2] + x[2]) * 0.5 - zctr;
double len = MathExtra::len3(zaxis);
MathExtra::scale3(1.0/len,zaxis);
len *= 0.5;
zmax = len;
zmin = -len;
double xmap = MathExtra::dot3(camRight,mid);
double ymap = MathExtra::dot3(camUp,mid);
double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(mid,camDir);
double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist :
-tanPerPixel / zoom;
double xf = xmap / pixelWidth;
double yf = ymap / pixelWidth;
int xc = static_cast<int> (xf);
int yc = static_cast<int> (yf);
double width_error = xf - xc;
double height_error = yf - yc;
// shift 0,0 to screen center (vs lower left)
xc += width / 2;
yc += height / 2;
double pixelHalfWidthFull = (rasterWidth * 0.5) / pixelWidth;
double pixelHalfHeightFull = (rasterHeight * 0.5) / pixelWidth;
int pixelHalfWidth = static_cast<int> (pixelHalfWidthFull + 0.5);
int pixelHalfHeight = static_cast<int> (pixelHalfHeightFull + 0.5);
if (zaxis[0] == camDir[0] && zaxis[1] == camDir[1] && zaxis[2] == camDir[2])
return;
if (zaxis[0] == -camDir[0] && zaxis[1] == -camDir[1] &&
zaxis[2] == -camDir[2]) return;
MathExtra::cross3(zaxis,camDir,yaxis);
MathExtra::norm3(yaxis);
MathExtra::cross3(yaxis,zaxis,xaxis);
MathExtra::norm3(xaxis);
camLDir[0] = MathExtra::dot3(camDir,xaxis);
camLDir[1] = 0.0;
camLDir[2] = MathExtra::dot3(camDir,zaxis);
camLRight[0] = MathExtra::dot3(camRight,xaxis);
camLRight[1] = MathExtra::dot3(camRight,yaxis);
camLRight[2] = MathExtra::dot3(camRight,zaxis);
MathExtra::norm3(camLRight);
camLUp[0] = MathExtra::dot3(camUp,xaxis);
camLUp[1] = MathExtra::dot3(camUp,yaxis);
camLUp[2] = MathExtra::dot3(camUp,zaxis);
MathExtra::norm3(camLUp);
double a = camLDir[0] * camLDir[0];
for (int iy = yc - pixelHalfHeight; iy <= yc + pixelHalfHeight; iy ++) {
for (int ix = xc - pixelHalfWidth; ix <= xc + pixelHalfWidth; ix ++) {
if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue;
double sy = ((iy - yc) - height_error) * pixelWidth;
double sx = ((ix - xc) - width_error) * pixelWidth;
surface[0] = camLRight[0] * sx + camLUp[0] * sy;
surface[1] = camLRight[1] * sx + camLUp[1] * sy;
surface[2] = camLRight[2] * sx + camLUp[2] * sy;
double b = 2 * camLDir[0] * surface[0];
double c = surface[0] * surface[0] + surface[1] * surface[1] - radsq;
double partial = b*b - 4*a*c;
if (partial < 0) continue;
partial = sqrt (partial);
double t = (-b + partial) / (2*a);
double t2 = (-b - partial) / (2*a);
if (t2 > t) { t = t2; }
surface[0] += t * camLDir[0];
surface[1] += t * camLDir[1];
surface[2] += t * camLDir[2];
if (surface[2] > zmax || surface[2] < zmin) continue;
// convert surface into the surface normal
normal[0] = surface[0] / radius;
normal[1] = surface[1] / radius;
normal[2] = 0.0;
// in camera space
surface[0] = MathExtra::dot3 (normal, camLRight);
surface[1] = MathExtra::dot3 (normal, camLUp);
surface[2] = MathExtra::dot3 (normal, camLDir);
double depth = dist - t;
draw_pixel (ix, iy, depth, surface, surfaceColor);
}
}
}
/* ----------------------------------------------------------------------
draw triangle with 3 corner points x,y,z and surfaceColor
------------------------------------------------------------------------- */
void Image::draw_triangle(double *x, double *y, double *z, double *surfaceColor)
{
double d1[3], d1len, d2[3], d2len, normal[3], invndotd;
double xlocal[3], ylocal[3], zlocal[3];
double center[3], bounds[6];
double surface[3];
double depth;
xlocal[0] = x[0] - xctr;
xlocal[1] = x[1] - yctr;
xlocal[2] = x[2] - zctr;
ylocal[0] = y[0] - xctr;
ylocal[1] = y[1] - yctr;
ylocal[2] = y[2] - zctr;
zlocal[0] = z[0] - xctr;
zlocal[1] = z[1] - yctr;
zlocal[2] = z[2] - zctr;
MathExtra::sub3 (xlocal, ylocal, d1);
d1len = MathExtra::len3 (d1);
MathExtra::scale3 (1.0 / d1len, d1);
MathExtra::sub3 (zlocal, ylocal, d2);
d2len = MathExtra::len3 (d2);
MathExtra::scale3 (1.0 / d2len, d2);
MathExtra::cross3 (d1, d2, normal);
MathExtra::norm3 (normal);
invndotd = 1.0 / MathExtra::dot3(normal, camDir);
// invalid triangle (parallel)
if (invndotd == 0) return;
double r[3],u[3];
center[0] = (xlocal[0] + ylocal[0] + zlocal[0]) / 3;
center[1] = (xlocal[1] + ylocal[1] + zlocal[1]) / 3;
center[2] = (xlocal[2] + ylocal[2] + zlocal[2]) / 3;
r[0] = MathExtra::dot3(camRight,xlocal);
r[1] = MathExtra::dot3(camRight,ylocal);
r[2] = MathExtra::dot3(camRight,zlocal);
u[0] = MathExtra::dot3(camUp,xlocal);
u[1] = MathExtra::dot3(camUp,ylocal);
u[2] = MathExtra::dot3(camUp,zlocal);
double rasterLeft = r[0] - MIN(r[0],MIN(r[1],r[2]));
double rasterRight = MAX(r[0],MAX(r[1],r[2])) - r[0];
double rasterDown = u[0] - MIN(u[0],MIN(u[1],u[2]));
double rasterUp = MAX(u[0],MAX(u[1],u[2])) - u[0];
double xmap = MathExtra::dot3(camRight,xlocal);
double ymap = MathExtra::dot3(camUp,xlocal);
double dist = MathExtra::dot3(camPos,camDir) - MathExtra::dot3(xlocal,camDir);
double pixelWidth = (tanPerPixel > 0) ? tanPerPixel * dist :
-tanPerPixel / zoom;
double xf = xmap / pixelWidth;
double yf = ymap / pixelWidth;
int xc = static_cast<int> (xf);
int yc = static_cast<int> (yf);
double width_error = xf - xc;
double height_error = yf - yc;
// shift 0,0 to screen center (vs lower left)
xc += width / 2;
yc += height / 2;
double pixelLeftFull = rasterLeft / pixelWidth;
double pixelRightFull = rasterRight / pixelWidth;
double pixelDownFull = rasterDown / pixelWidth;
double pixelUpFull = rasterUp / pixelWidth;
int pixelLeft = static_cast<int> (pixelLeftFull + 0.5);
int pixelRight = static_cast<int> (pixelRightFull + 0.5);
int pixelDown = static_cast<int> (pixelDownFull + 0.5);
int pixelUp = static_cast<int> (pixelUpFull + 0.5);
for (int iy = yc - pixelDown; iy <= yc + pixelUp; iy ++) {
for (int ix = xc - pixelLeft; ix <= xc + pixelRight; ix ++) {
if (iy < 0 || iy >= height || ix < 0 || ix >= width) continue;
double sy = ((iy - yc) - height_error) * pixelWidth;
double sx = ((ix - xc) - width_error) * pixelWidth;
surface[0] = camRight[0] * sx + camUp[0] * sy;
surface[1] = camRight[1] * sx + camUp[1] * sy;
surface[2] = camRight[2] * sx + camUp[2] * sy;
double t = -MathExtra::dot3(normal,surface) * invndotd;
// test inside the triangle
double p[3];
p[0] = xlocal[0] + surface[0] + camDir[0] * t;
p[1] = xlocal[1] + surface[1] + camDir[1] * t;
p[2] = xlocal[2] + surface[2] + camDir[2] * t;
double s1[3], s2[3], s3[3];
double c1[3], c2[3];
MathExtra::sub3 (zlocal, xlocal, s1);
MathExtra::sub3 (ylocal, xlocal, s2);
MathExtra::sub3 (p, xlocal, s3);
MathExtra::cross3 (s1, s2, c1);
MathExtra::cross3 (s1, s3, c2);
if (MathExtra::dot3 (c1, c2) <= 0) continue;
MathExtra::sub3 (xlocal, ylocal, s1);
MathExtra::sub3 (zlocal, ylocal, s2);
MathExtra::sub3 (p, ylocal, s3);
MathExtra::cross3 (s1, s2, c1);
MathExtra::cross3 (s1, s3, c2);
if (MathExtra::dot3 (c1, c2) <= 0) continue;
MathExtra::sub3 (ylocal, zlocal, s1);
MathExtra::sub3 (xlocal, zlocal, s2);
MathExtra::sub3 (p, zlocal, s3);
MathExtra::cross3 (s1, s2, c1);
MathExtra::cross3 (s1, s3, c2);
if (MathExtra::dot3 (c1, c2) <= 0) continue;
double cNormal[3];
cNormal[0] = MathExtra::dot3(camRight, normal);
cNormal[1] = MathExtra::dot3(camUp, normal);
cNormal[2] = MathExtra::dot3(camDir, normal);
depth = dist - t;
draw_pixel(ix,iy,depth,cNormal,surfaceColor);
}
}
}
/* ---------------------------------------------------------------------- */
void Image::draw_pixel(int ix, int iy, double depth,
double *surface, double *surfaceColor)
{
double diffuseKey,diffuseFill,diffuseBack,specularKey;
if (depth < 0 || (depthBuffer[ix + iy*width] >= 0 &&
depth >= depthBuffer[ix + iy*width])) return;
depthBuffer[ix + iy*width] = depth;
// store only the tangent relative to the camera normal (0,0,-1)
surfaceBuffer[0 + ix * 2 + iy*width * 2] = surface[1];
surfaceBuffer[1 + ix * 2 + iy*width * 2] = -surface[0];
diffuseKey = saturate(MathExtra::dot3(surface, keyLightDir));
diffuseFill = saturate(MathExtra::dot3(surface, fillLightDir));
diffuseBack = saturate(MathExtra::dot3(surface, backLightDir));
specularKey = pow(saturate(MathExtra::dot3(surface, keyHalfDir)),
specularHardness) * specularIntensity;
double c[3];
c[0] = surfaceColor[0] * ambientColor[0];
c[1] = surfaceColor[1] * ambientColor[1];
c[2] = surfaceColor[2] * ambientColor[2];
c[0] += surfaceColor[0] * keyLightColor[0] * diffuseKey;
c[1] += surfaceColor[1] * keyLightColor[1] * diffuseKey;
c[2] += surfaceColor[2] * keyLightColor[2] * diffuseKey;
c[0] += keyLightColor[0] * specularKey;
c[1] += keyLightColor[1] * specularKey;
c[2] += keyLightColor[2] * specularKey;
c[0] += surfaceColor[0] * fillLightColor[0] * diffuseFill;
c[1] += surfaceColor[1] * fillLightColor[1] * diffuseFill;
c[2] += surfaceColor[2] * fillLightColor[2] * diffuseFill;
c[0] += surfaceColor[0] * backLightColor[0] * diffuseBack;
c[1] += surfaceColor[1] * backLightColor[1] * diffuseBack;
c[2] += surfaceColor[2] * backLightColor[2] * diffuseBack;
c[0] = saturate(c[0]);
c[1] = saturate(c[1]);
c[2] = saturate(c[2]);
imageBuffer[0 + ix*3 + iy*width*3] = static_cast<int>(c[0] * 255.0);
imageBuffer[1 + ix*3 + iy*width*3] = static_cast<int>(c[1] * 255.0);
imageBuffer[2 + ix*3 + iy*width*3] = static_cast<int>(c[2] * 255.0);
}
/* ---------------------------------------------------------------------- */
void Image::compute_SSAO()
{
// used for rasterizing the spheres
double delTheta = 2.0*MY_PI / SSAOSamples;
// typical neighborhood value for shading
double pixelWidth = (tanPerPixel > 0) ? tanPerPixel :
-tanPerPixel / zoom;
int pixelRadius = (int) trunc (SSAORadius / pixelWidth + 0.5);
int x,y,s;
int hPart = height / nprocs;
int index = me * hPart * width;
for (y = me * hPart; y < (me + 1) * hPart; y ++) {
for (x = 0; x < width; x ++, index ++) {
double cdepth = depthBuffer[index];
if (cdepth < 0) { continue; }
double sx = surfaceBuffer[index * 2 + 0];
double sy = surfaceBuffer[index * 2 + 1];
double sin_t = -sqrt(sx*sx + sy*sy);
double theta = random->uniform() * SSAOJitter;
double ao = 0.0;
for (s = 0; s < SSAOSamples; s ++) {
double hx = cos(theta);
double hy = sin(theta);
theta += delTheta;
// multiply by z cross surface tangent
// so that dot (aka cos) works here
double scaled_sin_t = sin_t * (hx*sy + hy*sx);
// Bresenham's line algorithm to march over depthBuffer
int dx = static_cast<int> (hx * pixelRadius);
int dy = static_cast<int> (hy * pixelRadius);
int ex = x + dx;
if (ex < 0) { ex = 0; } if (ex >= width) { ex = width - 1; }
int ey = y + dy;
if (ey < 0) { ey = 0; } if (ey >= height) { ey = height - 1; }
double delta;
int small, large;
double lenIncr;
if (fabs(hx) > fabs(hy)) {
small = (hx > 0) ? 1 : -1;
large = (hy > 0) ? width : -width;
delta = fabs(hy / hx);
} else {
small = (hy > 0) ? width : -width;
large = (hx > 0) ? 1 : -1;
delta = fabs(hx / hy);
}
lenIncr = sqrt (1 + delta * delta) * pixelWidth;
// initialize with one step
// because the center point doesn't need testing
int end = ex + ey * width;
int ind = index + small;
double len = lenIncr;
double err = delta;
if (err >= 1.0) {
ind += large;
err -= 1.0;
}
double minPeak = -1;
double peakLen = 0.0;
int stepsTaken = 1;
while ((small > 0 && ind <= end) || (small < 0 && ind >= end)) {
if (ind < 0 || ind >= (width*height)) {
break;
}
// cdepth - depthBuffer B/C we want it in the negative z direction
if (minPeak < 0 || (depthBuffer[ind] >= 0 &&
depthBuffer[ind] < minPeak)) {
minPeak = depthBuffer[ind];
peakLen = len;
}
ind += small;
len += lenIncr;
err += delta;
if (err >= 1.0) {
ind += large;
err -= 1.0;
}
stepsTaken ++;
}
if (peakLen > 0) {
double h = atan ((cdepth - minPeak) / peakLen);
ao += saturate(sin (h) - scaled_sin_t);
} else {
ao += saturate(-scaled_sin_t);
}
}
ao /= (double)SSAOSamples;
double c[3];
c[0] = (double) (*(unsigned char *) &imageBuffer[index * 3 + 0]);
c[1] = (double) (*(unsigned char *) &imageBuffer[index * 3 + 1]);
c[2] = (double) (*(unsigned char *) &imageBuffer[index * 3 + 2]);
c[0] *= (1.0 - ao);
c[1] *= (1.0 - ao);
c[2] *= (1.0 - ao);
imageBuffer[index * 3 + 0] = (int) c[0];
imageBuffer[index * 3 + 1] = (int) c[1];
imageBuffer[index * 3 + 2] = (int) c[2];
}
}
}
/* ---------------------------------------------------------------------- */
void Image::write_JPG(FILE *fp)
{
#ifdef LAMMPS_JPEG
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo,fp);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 100, 1);
jpeg_start_compress(&cinfo, 1);
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer = (JSAMPROW)
&writeBuffer[(cinfo.image_height - 1 - cinfo.next_scanline) * 3 * width];
jpeg_write_scanlines(&cinfo,&row_pointer,1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
#endif
}
/* ---------------------------------------------------------------------- */
void Image::write_PPM(FILE *fp)
{
fprintf (fp,"P6\n%d %d\n255\n",width,height);
int x,y;
for (y = height-1; y >= 0; y --)
for (x = 0; x < width; x ++)
fprintf(fp,"%c%c%c",
writeBuffer[0 + x*3 + y*width*3],
writeBuffer[1 + x*3 + y*width*3],
writeBuffer[2 + x*3 + y*width*3]);
}
/* ----------------------------------------------------------------------
define a color map
args = lo hi style delta N entry1 entry2 ... entryN as defined by caller
return 1 if any error in args, else return 0
------------------------------------------------------------------------- */
int Image::colormap(int narg, char **arg)
{
if (!islower(arg[0][0])) {
mlo = NUMERIC;
mlovalue = atof(arg[0]);
} else if (strcmp(arg[0],"min") == 0) mlo = MINVALUE;
else return 1;
if (!islower(arg[1][0])) {
mhi = NUMERIC;
mhivalue = atof(arg[1]);
} else if (strcmp(arg[1],"max") == 0) mhi = MAXVALUE;
else return 1;
if (mlo == NUMERIC && mhi == NUMERIC && mlovalue >= mhivalue) return 1;
if (strlen(arg[2]) != 2) return 1;
if (arg[2][0] == 'c') mstyle = CONTINUOUS;
else if (arg[2][0] == 'd') mstyle = DISCRETE;
else if (arg[2][0] == 's') mstyle = SEQUENTIAL;
else return 1;
if (arg[2][1] == 'a') mrange = ABSOLUTE;
else if (arg[2][1] == 'f') mrange = FRACTIONAL;
else return 1;
if (mstyle == SEQUENTIAL) {
mbinsize = atof(arg[3]);
if (mbinsize <= 0.0) return 1;
mbinsizeinv = 1.0/mbinsize;
}
nentry = atoi(arg[4]);
if (nentry < 1) return 1;
mentry = new MapEntry[nentry];
int n = 5;
for (int i = 0; i < nentry; i++) {
if (mstyle == CONTINUOUS) {
if (n+2 > narg) return 1;
if (!islower(arg[n][0])) {
mentry[i].single = NUMERIC;
mentry[i].svalue = atof(arg[n]);
} else if (strcmp(arg[n],"min") == 0) mentry[i].single = MINVALUE;
else if (strcmp(arg[n],"max") == 0) mentry[i].single = MAXVALUE;
else return 1;
mentry[i].color = color2rgb(arg[n+1]);
n += 2;
} else if (mstyle == DISCRETE) {
if (n+3 > narg) return 1;
if (!islower(arg[n][0])) {
mentry[i].lo = NUMERIC;
mentry[i].lvalue = atof(arg[n]);
} else if (strcmp(arg[n],"min") == 0) mentry[i].single = MINVALUE;
else if (strcmp(arg[n],"max") == 0) mentry[i].single = MAXVALUE;
else return 1;
if (!islower(arg[n+1][0])) {
mentry[i].hi = NUMERIC;
mentry[i].hvalue = atof(arg[n+1]);
} else if (strcmp(arg[n+1],"min") == 0) mentry[i].single = MINVALUE;
else if (strcmp(arg[n+1],"max") == 0) mentry[i].single = MAXVALUE;
else return 1;
mentry[i].color = color2rgb(arg[n+2]);
n += 3;
} else if (mstyle == SEQUENTIAL) {
if (n+1 > narg) return 1;
mentry[i].color = color2rgb(arg[n]);
n += 1;
}
if (mentry[i].color == NULL) return 1;
}
if (mstyle == CONTINUOUS) {
if (nentry < 2) return 1;
if (mentry[0].single != MINVALUE || mentry[nentry-1].single != MAXVALUE)
return 1;
for (int i = 2; i < nentry-1; i++)
if (mentry[i].svalue <= mentry[i-1].svalue) return 1;
} else if (mstyle == DISCRETE) {
if (nentry < 1) return 1;
if (mentry[nentry-1].lo != MINVALUE || mentry[nentry-1].hi != MAXVALUE)
return 1;
} else if (mstyle == SEQUENTIAL) {
if (nentry < 1) return 1;
}
return 0;
}
/* ----------------------------------------------------------------------
add a new color to username and userrgb
redefine RGB values in userrgb if name already exists
return 1 if RGB values are invalid, else return 0
------------------------------------------------------------------------- */
int Image::addcolor(char *name, double r, double g, double b)
{
int icolor;
for (icolor = 0; icolor < ncolors; icolor++)
if (strcmp(name,username[icolor]) == 0) break;
if (icolor == ncolors) {
username = (char **)
memory->srealloc(username,(ncolors+1)*sizeof(char *),"image:username");
memory->grow(userrgb,ncolors+1,3,"image:userrgb");
ncolors++;
}
int n = strlen(name) + 1;
username[icolor] = new char[n];
strcpy(username[icolor],name);
if (r < 0.0 || r > 1.0 || g < 0.0 || g > 1.0 || b < 0.0 || b > 1.0)
return 1;
userrgb[icolor][0] = r;
userrgb[icolor][1] = g;
userrgb[icolor][2] = b;
return 0;
}
/* ----------------------------------------------------------------------
convert value into an RGB color via color map
------------------------------------------------------------------------- */
double *Image::value2color(double value)
{
double lo,hi;
value = MAX(value,locurrent);
value = MIN(value,hicurrent);
if (mrange == FRACTIONAL) {
if (locurrent == hicurrent) value = 0.0;
else value = (value-locurrent) / (hicurrent-locurrent);
lo = 0.0;
hi = 1.0;
} else {
lo = locurrent;
hi = hicurrent;
}
if (mstyle == CONTINUOUS) {
for (int i = 0; i < nentry-1; i++)
if (value >= mentry[i].svalue && value <= mentry[i+1].svalue) {
double fraction = (value-mentry[i].svalue) /
(mentry[i+1].svalue-mentry[i].svalue);
interpolate[0] = mentry[i].color[0] +
fraction*(mentry[i+1].color[0]-mentry[i].color[0]);
interpolate[1] = mentry[i].color[1] +
fraction*(mentry[i+1].color[1]-mentry[i].color[1]);
interpolate[2] = mentry[i].color[2] +
fraction*(mentry[i+1].color[2]-mentry[i].color[2]);
return interpolate;
}
} else if (mstyle == DISCRETE) {
for (int i = 0; i < nentry; i++)
if (value >= mentry[i].lvalue && value <= mentry[i].hvalue)
return mentry[i].color;
} else {
int ibin = static_cast<int> ((value-lo) * mbinsizeinv);
return mentry[ibin%nentry].color;
}
return NULL;
}
/* ----------------------------------------------------------------------
search the list of color names for the string color
return a pointer to the 3 floating point RGB values
search user-defined color names first, then the list of NCOLORS names
------------------------------------------------------------------------- */
double *Image::color2rgb(const char *color, int index)
{
static const char *name[NCOLORS] = {
"aliceblue",
"antiquewhite",
"aqua",
"aquamarine",
"azure",
"beige",
"bisque",
"black",
"blanchedalmond",
"blue",
"blueviolet",
"brown",
"burlywood",
"cadetblue",
"chartreuse",
"chocolate",
"coral",
"cornflowerblue",
"cornsilk",
"crimson",
"cyan",
"darkblue",
"darkcyan",
"darkgoldenrod",
"darkgray",
"darkgreen",
"darkkhaki",
"darkmagenta",
"darkolivegreen",
"darkorange",
"darkorchid",
"darkred",
"darksalmon",
"darkseagreen",
"darkslateblue",
"darkslategray",
"darkturquoise",
"darkviolet",
"deeppink",
"deepskyblue",
"dimgray",
"dodgerblue",
"firebrick",
"floralwhite",
"forestgreen",
"fuchsia",
"gainsboro",
"ghostwhite",
"gold",
"goldenrod",
"gray",
"green",
"greenyellow",
"honeydew",
"hotpink",
"indianred",
"indigo",
"ivory",
"khaki",
"lavender",
"lavenderblush",
"lawngreen",
"lemonchiffon",
"lightblue",
"lightcoral",
"lightcyan",
"lightgoldenrodyellow",
"lightgreen",
"lightgrey",
"lightpink",
"lightsalmon",
"lightseagreen",
"lightskyblue",
"lightslategray",
"lightsteelblue",
"lightyellow",
"lime",
"limegreen",
"linen",
"magenta",
"maroon",
"mediumaquamarine",
"mediumblue",
"mediumorchid",
"mediumpurple",
"mediumseagreen",
"mediumslateblue",
"mediumspringgreen",
"mediumturquoise",
"mediumvioletred",
"midnightblue",
"mintcream",
"mistyrose",
"moccasin",
"navajowhite",
"navy",
"oldlace",
"olive",
"olivedrab",
"orange",
"orangered",
"orchid",
"palegoldenrod",
"palegreen",
"paleturquoise",
"palevioletred",
"papayawhip",
"peachpuff",
"peru",
"pink",
"plum",
"powderblue",
"purple",
"red",
"rosybrown",
"royalblue",
"saddlebrown",
"salmon",
"sandybrown",
"seagreen",
"seashell",
"sienna",
"silver",
"skyblue",
"slateblue",
"slategray",
"snow",
"springgreen",
"steelblue",
"tan",
"teal",
"thistle",
"tomato",
"turquoise",
"violet",
"wheat",
"white",
"whitesmoke",
"yellow",
"yellowgreen"
};
static double rgb[NCOLORS][3] = {
{240/255.0, 248/255.0, 255/255.0},
{250/255.0, 235/255.0, 215/255.0},
{0/255.0, 255/255.0, 255/255.0},
{127/255.0, 255/255.0, 212/255.0},
{240/255.0, 255/255.0, 255/255.0},
{245/255.0, 245/255.0, 220/255.0},
{255/255.0, 228/255.0, 196/255.0},
{0/255.0, 0/255.0, 0/255.0},
{255/255.0, 255/255.0, 205/255.0},
{0/255.0, 0/255.0, 255/255.0},
{138/255.0, 43/255.0, 226/255.0},
{165/255.0, 42/255.0, 42/255.0},
{222/255.0, 184/255.0, 135/255.0},
{95/255.0, 158/255.0, 160/255.0},
{127/255.0, 255/255.0, 0/255.0},
{210/255.0, 105/255.0, 30/255.0},
{255/255.0, 127/255.0, 80/255.0},
{100/255.0, 149/255.0, 237/255.0},
{255/255.0, 248/255.0, 220/255.0},
{220/255.0, 20/255.0, 60/255.0},
{0/255.0, 255/255.0, 255/255.0},
{0/255.0, 0/255.0, 139/255.0},
{0/255.0, 139/255.0, 139/255.0},
{184/255.0, 134/255.0, 11/255.0},
{169/255.0, 169/255.0, 169/255.0},
{0/255.0, 100/255.0, 0/255.0},
{189/255.0, 183/255.0, 107/255.0},
{139/255.0, 0/255.0, 139/255.0},
{85/255.0, 107/255.0, 47/255.0},
{255/255.0, 140/255.0, 0/255.0},
{153/255.0, 50/255.0, 204/255.0},
{139/255.0, 0/255.0, 0/255.0},
{233/255.0, 150/255.0, 122/255.0},
{143/255.0, 188/255.0, 143/255.0},
{72/255.0, 61/255.0, 139/255.0},
{47/255.0, 79/255.0, 79/255.0},
{0/255.0, 206/255.0, 209/255.0},
{148/255.0, 0/255.0, 211/255.0},
{255/255.0, 20/255.0, 147/255.0},
{0/255.0, 191/255.0, 255/255.0},
{105/255.0, 105/255.0, 105/255.0},
{30/255.0, 144/255.0, 255/255.0},
{178/255.0, 34/255.0, 34/255.0},
{255/255.0, 250/255.0, 240/255.0},
{34/255.0, 139/255.0, 34/255.0},
{255/255.0, 0/255.0, 255/255.0},
{220/255.0, 220/255.0, 220/255.0},
{248/255.0, 248/255.0, 255/255.0},
{255/255.0, 215/255.0, 0/255.0},
{218/255.0, 165/255.0, 32/255.0},
{128/255.0, 128/255.0, 128/255.0},
{0/255.0, 128/255.0, 0/255.0},
{173/255.0, 255/255.0, 47/255.0},
{240/255.0, 255/255.0, 240/255.0},
{255/255.0, 105/255.0, 180/255.0},
{205/255.0, 92/255.0, 92/255.0},
{75/255.0, 0/255.0, 130/255.0},
{255/255.0, 240/255.0, 240/255.0},
{240/255.0, 230/255.0, 140/255.0},
{230/255.0, 230/255.0, 250/255.0},
{255/255.0, 240/255.0, 245/255.0},
{124/255.0, 252/255.0, 0/255.0},
{255/255.0, 250/255.0, 205/255.0},
{173/255.0, 216/255.0, 230/255.0},
{240/255.0, 128/255.0, 128/255.0},
{224/255.0, 255/255.0, 255/255.0},
{250/255.0, 250/255.0, 210/255.0},
{144/255.0, 238/255.0, 144/255.0},
{211/255.0, 211/255.0, 211/255.0},
{255/255.0, 182/255.0, 193/255.0},
{255/255.0, 160/255.0, 122/255.0},
{32/255.0, 178/255.0, 170/255.0},
{135/255.0, 206/255.0, 250/255.0},
{119/255.0, 136/255.0, 153/255.0},
{176/255.0, 196/255.0, 222/255.0},
{255/255.0, 255/255.0, 224/255.0},
{0/255.0, 255/255.0, 0/255.0},
{50/255.0, 205/255.0, 50/255.0},
{250/255.0, 240/255.0, 230/255.0},
{255/255.0, 0/255.0, 255/255.0},
{128/255.0, 0/255.0, 0/255.0},
{102/255.0, 205/255.0, 170/255.0},
{0/255.0, 0/255.0, 205/255.0},
{186/255.0, 85/255.0, 211/255.0},
{147/255.0, 112/255.0, 219/255.0},
{60/255.0, 179/255.0, 113/255.0},
{123/255.0, 104/255.0, 238/255.0},
{0/255.0, 250/255.0, 154/255.0},
{72/255.0, 209/255.0, 204/255.0},
{199/255.0, 21/255.0, 133/255.0},
{25/255.0, 25/255.0, 112/255.0},
{245/255.0, 255/255.0, 250/255.0},
{255/255.0, 228/255.0, 225/255.0},
{255/255.0, 228/255.0, 181/255.0},
{255/255.0, 222/255.0, 173/255.0},
{0/255.0, 0/255.0, 128/255.0},
{253/255.0, 245/255.0, 230/255.0},
{128/255.0, 128/255.0, 0/255.0},
{107/255.0, 142/255.0, 35/255.0},
{255/255.0, 165/255.0, 0/255.0},
{255/255.0, 69/255.0, 0/255.0},
{218/255.0, 112/255.0, 214/255.0},
{238/255.0, 232/255.0, 170/255.0},
{152/255.0, 251/255.0, 152/255.0},
{175/255.0, 238/255.0, 238/255.0},
{219/255.0, 112/255.0, 147/255.0},
{255/255.0, 239/255.0, 213/255.0},
{255/255.0, 239/255.0, 213/255.0},
{205/255.0, 133/255.0, 63/255.0},
{255/255.0, 192/255.0, 203/255.0},
{221/255.0, 160/255.0, 221/255.0},
{176/255.0, 224/255.0, 230/255.0},
{128/255.0, 0/255.0, 128/255.0},
{255/255.0, 0/255.0, 0/255.0},
{188/255.0, 143/255.0, 143/255.0},
{65/255.0, 105/255.0, 225/255.0},
{139/255.0, 69/255.0, 19/255.0},
{250/255.0, 128/255.0, 114/255.0},
{244/255.0, 164/255.0, 96/255.0},
{46/255.0, 139/255.0, 87/255.0},
{255/255.0, 245/255.0, 238/255.0},
{160/255.0, 82/255.0, 45/255.0},
{192/255.0, 192/255.0, 192/255.0},
{135/255.0, 206/255.0, 235/255.0},
{106/255.0, 90/255.0, 205/255.0},
{112/255.0, 128/255.0, 144/255.0},
{255/255.0, 250/255.0, 250/255.0},
{0/255.0, 255/255.0, 127/255.0},
{70/255.0, 130/255.0, 180/255.0},
{210/255.0, 180/255.0, 140/255.0},
{0/255.0, 128/255.0, 128/255.0},
{216/255.0, 191/255.0, 216/255.0},
{253/255.0, 99/255.0, 71/255.0},
{64/255.0, 224/255.0, 208/255.0},
{238/255.0, 130/255.0, 238/255.0},
{245/255.0, 222/255.0, 179/255.0},
{255/255.0, 255/255.0, 255/255.0},
{245/255.0, 245/255.0, 245/255.0},
{255/255.0, 255/255.0, 0/255.0},
{154/255.0, 205/255.0, 50/255.0}
};
if (index) return rgb[index-1];
for (int i = 0; i < ncolors; i++)
if (strcmp(color,username[i]) == 0) return userrgb[i];
for (int i = 0; i < NCOLORS; i++)
if (strcmp(color,name[i]) == 0) return rgb[i];
return NULL;
}
/* ----------------------------------------------------------------------
return number of default colors
------------------------------------------------------------------------- */
int Image::default_colors()
{
return NCOLORS;
}
/* ----------------------------------------------------------------------
search the list of element names for the string element
return a pointer to the 3 floating point RGB values
this list is used by AtomEye and is taken from its Mendeleyev.c file
------------------------------------------------------------------------- */
double *Image::element2color(char *element)
{
static const char *name[NELEMENTS] = {
"H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne",
"Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca",
"Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
"Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr",
"Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn",
"Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
"Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb",
"Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg",
"Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
"Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm",
"Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt"
};
static double rgb[NELEMENTS][3] = {
{0.8, 0.8, 0.8},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.7, 0.7, 0.7},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.9, 0.4, 0},
{0.35, 0.35, 0.35},
{0.2, 0.2, 0.8},
{0.8, 0.2, 0.2},
{0.7, 0.85, 0.45},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6, 0.6, 0.6},
{0.6, 0.6, 0.7},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6901960784, 0.768627451, 0.8705882353},
{0.1, 0.7, 0.3},
{0.95, 0.9, 0.2},
{0.15, 0.5, 0.1},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.5, 0.5, 0.5},
{0.8, 0.8, 0.7},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0, 0.8, 0},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.5176470588, 0.5764705882, 0.6529411765},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.257254902, 0.2666666667, 0.271372549},
{0.95, 0.7900735294, 0.01385869565},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.9, 0, 1},
{0.6431372549, 0.6666666667, 0.6784313725},
{1, 1, 0.3},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.5, 0.08, 0.12},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.5, 0.1, 0.5},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.8, 0.8, 0},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{1, 0.8431372549, 0},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.9, 0.8, 0},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.8, 0.2, 0.2},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.1, 0.7, 0.3},
{0.1, 0.3, 0.7},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.9, 0.8, 0},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725},
{0.6431372549, 0.6666666667, 0.6784313725}
};
for (int i = 0; i < NELEMENTS; i++)
if (strcmp(element,name[i]) == 0) return rgb[i];
return NULL;
}
/* ----------------------------------------------------------------------
search the list of element names for the string element
return a pointer to the 3 floating point RGB values
this list is used by AtomEye and is taken from its Mendeleyev.c file
------------------------------------------------------------------------- */
double Image::element2diam(char *element)
{
static const char *name[NELEMENTS] = {
"H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne",
"Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca",
"Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
"Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr",
"Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn",
"Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
"Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb",
"Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg",
"Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
"Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm",
"Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt"
};
static double diameter[NELEMENTS] = {
0.35, 1.785, 1.45, 1.05, 0.85, 0.72, 0.65, 0.6, 0.5, 1.5662,
1.8, 1.5, 1.4255, 1.07, 1, 1, 1, 1.8597, 2.2, 1.8,
1.6, 1.4, 1.51995, 1.44225, 1.4, 1.43325, 1.35, 1.35, 1.278, 1.35,
1.3, 1.25, 1.15, 1.15, 1.15, 2.0223, 2.35, 2, 1.8, 1.55,
1.6504, 1.3872, 1.35, 1.3, 1.35, 1.4, 1.6, 1.55, 1.55, 1.45,
1.45, 1.4, 1.4, 2.192, 2.6, 2.15, 1.95, 1.85, 1.85, 1.85,
1.85, 1.85, 1.85, 1.8, 1.75, 1.75, 1.75, 1.75, 1.75, 1.75,
1.75, 1.55, 1.6529, 1.5826, 1.35, 1.3, 1.35, 1.35, 1.35, 1.5,
1.9, 1.8, 1.6, 1.9, 1.6, 1.0, 1.0, 2.15, 1.95, 1.8,
1.8, 1.75, 1.75, 1.75, 1.75, 1.0, 1.0, 1.6, 1.6, 1.0,
1.0, 1.0, 1.0, 1.0, 1.6, 1.0, 1.0, 1.0, 1.0
};
for (int i = 0; i < NELEMENTS; i++)
if (strcmp(element,name[i]) == 0) return diameter[i];
return 0.0;
}
Event Timeline
Log In to Comment