sandbox/Antoonvh/sketch.h
Sketching functions for Bwatch
Sketching utilities consist of a few functions and definitions.
#include "bwatch-iterators.h"
Sketch vof facets
sketch_vof()
mimics bview’s draw_vof()
a
little
struct _sketch_vof {
scalar f;
bool precomp; //precompute normals
;
material mat};
struct _sketch_vof svlist[N_SKETCH]; // Arguments to sketch vof
trace
double get_pixel_sketch_vof (struct _sketch_vof inp, ray r, double DG, unsigned char * px) {
double distance = HUGE;
, a;
coord nif (inp.precomp) {
foreach_precomp_ray_facet_intersection(r, inp.f, DG) {
= _distg;
distance = _n;
n = _a[0];
a }
}
else {
foreach_ray_facet_intersection(r, inp.f) {
= _distg;
distance = _n;
n = _a[0];
a }
}
if (distance < HUGE && distance < DG)
if (get_color_material (r, a, n, inp.mat, px))
return distance;
return HUGE;
}
// User function:
bool sketch_vof (struct _sketch_vof inp) {
[functot] = &get_pixel_sketch_vof;
sketch_listif (!inp.mat.dull && !inp.mat.ind && !any_col (inp.mat.col) &&
!inp.mat.s.i && !inp.mat.v.x.i && !(inp.mat.R >= 1.))
.mat.col[0] = inp.mat.col[1] = inp.mat.col[2] = 250;
inpif (!inp.mat.map)
.mat.map = jet;
inpif (!inp.mat.min && !inp.mat.max) {
.mat.min = -1;
inp.mat.max = 1;
inp}
if (!inp.mat.SPexp && !inp.mat.SPR) {
.mat.SPexp = 40;
inp.mat.SPR = 0.1;
inp}
[functot] = inp;
svlist++;
functot= list_add (shading, inp.f);
shading return true;
}
A Slice
quadriangles
may mimic bview’s squares()
a
bit.
struct _quadriangles {
scalar s;
double alpha;
;
coord n;
material mat};
struct _quadriangles qlist[N_SKETCH]; // Arguments to quadriangles
trace
double get_pixel_quadriangles (struct _quadriangles inp, ray r, double DG, unsigned char * px) {
;
coord adouble d = ray_plane_intersect (r, inp.n, inp.alpha, &a);
if (d <= 0 || d >= DG)
return HUGE;
if (locate (a.x, a.y, a.z).level < 0) // Only slices in the domain
return HUGE;
(&inp.n);
normalize if (get_color_material (r, a, inp.n, inp.mat, px))
return d;
return HUGE;
}
// User interface
void quadriangles (struct _quadriangles inp) {
[functot] = &get_pixel_quadriangles;
sketch_listif (!any_comp (inp.n))
.n.z = 1;
inpif (!inp.mat.dull && !inp.mat.ind && !any_col (inp.mat.col) &&
!inp.mat.s.i && !inp.mat.v.y.i && !(inp.mat.R >= 1)) {
if (inp.s.i)
.mat.s = inp.s;
inpelse
.mat.col[0] = 250, inp.mat.col[1] = 150, inp.mat.col[2] = 50;
inp}
if (!inp.mat.map)
.mat.map = jet;
inpif (!inp.mat.min && !inp.mat.max) {
.mat.min = -L0;
inp.mat.max = L0;
inp}
[functot] = inp;
qlist++;
functot}
The grid
lattice()
may mimic bview’s cells()
a
bit.
struct _lattice {
double alpha;
;
coord ndouble width;
;
material matbool HQ; // High quality, some background object required
};
struct _lattice llist[N_SKETCH]; // Arguments to lattice
double get_pixel_lattice (struct _lattice inp, ray r, double DG, unsigned char * px) {
;
coord adouble d = ray_plane_intersect (r, inp.n, inp.alpha, &a);
if (d < 0 || d > 1e4*L0 || d > DG)
return HUGE;
double val = HUGE;
foreach_point (a.x, a.y, a.z, serial) {
= {x, y, z};
coord cc //grid-alligned slices only
foreach_dimension() {
if (inp.n.z) {
int sx = sign (a.x - cc.x);
int sy = sign (a.y - cc.y);
double dx = cc.x + sx*Delta/2 - a.x;
double dy = cc.y + sy*Delta/2 - a.y;
double dist = min (fabs(dx), fabs(dy));
if (dist < (inp.HQ ? 3 : 1)*inp.width && d > Delta*.1) {
(&inp.n);
normalize if (inp.HQ) {
double b = 0, c = 0;
foreach_dimension() {
+= sq(cam.O.x - a.x);
b += sq(cam.O.x - cam.poi.x);
c }
= sqrt(b);
b = sqrt(c);
c double ps = cam.fov*b/(cam.nx*c);
= {0,0,0}, rp = {0, 0, 0};
coord np .z = 1.;
np.z = r.dir.z;
rpif(dist == fabs(dx))
.y = r.dir.y;
rp
else.x = r.dir.x;
rp(&rp);
normalize double cc = fabs(dot(rp, np));
double ss = cc > 0. ? ps/cc : ps;
//printf ("%g %g %g\n", ss, ps, cc);
double temp = dist + ss/4. < inp.width ? 1 :
+ ss/4. < 2*inp.width ?
dist 1 - (dist + ss/4. - inp.width)/(inp.width) : 0;
+= fabs(dist - ss/4.) < inp.width ? 1 :
temp (dist - ss/4.) < 2*inp.width ?
fabs1 - (fabs(dist - ss/4.) - inp.width)/(inp.width) : 0;
= (2. - temp)/2.;
temp .mat.T = max(min(temp, 1), 0);
inpforeach_dimension()
.x -= 0.04*Delta*r.dir.x;
a}
if (get_color_material (r, a, inp.n, inp.mat, px))
= d - Delta/20.;
val }
}
}
}
return val;
}
// User interface
void lattice (struct _lattice inp) {
[functot] = &get_pixel_lattice;
sketch_listif (!inp.mat.dull && !inp.mat.ind && !any_col (inp.mat.col) &&
!inp.mat.s.i && !inp.mat.v.x.i && !(inp.mat.R >= 1))
for (int i = 0; i < 3; i++)
.mat.col[i] = 1;
inpif (!any_comp(inp.n))
.n.z = 1.;
inpif (!inp.width)
.width = cam.fov/(40*N);
inpif (inp.HQ)
.width /= 1.5;
inp[functot] = inp;
llist++;
functot}
A disk
A round planar disk
struct _disk {
double R;
;
coord P;
coord n;
material mat};
struct _disk dlist[N_SKETCH]; // Arguments to disk
double get_pixel_disk (struct _disk inp, ray r, double DG, unsigned char * px) {
;
coord adouble alpha = 0;
foreach_dimension()
+= inp.n.x*inp.P.x;
alpha double d = ray_plane_intersect (r, inp.n, alpha, &a);
if (d >= DG || d < 0.0001) //No further computations
return HUGE;
double D = 0;
foreach_dimension()
+= sq(a.x - inp.P.x);
D if (D > sq(inp.R))
return HUGE;
(&inp.n);
normalize (&r.dir);
normalize if (get_color_material (r, a, inp.n, inp.mat, px))
return d;
return HUGE;
}
bool disk (struct _disk inp) {
[functot] = &get_pixel_disk;
sketch_listif (!inp.R)
.R = L0/2.;
inpif (!any_comp (inp.n))
.n.z = 1;
inpif (!inp.mat.dull || !inp.mat.ind || !any_col (inp.mat.col) ||
!inp.mat.s.i || !inp.mat.v.x.i || !(inp.mat.R >= 1.))
.mat.R = 1;
inp[functot] = inp;
dlist++;
functotreturn true;
}
Sketch a sphere
struct _sphere {
double R; //Radius
; //centre
coord Cstruct Material mat;
};
struct _sphere slist[N_SKETCH]; // Arguments to sphere
trace
double get_pixel_sphere (struct _sphere inp, ray r, double DG, unsigned char * px) {
, n;
coord adouble d = ray_sphere_intersect (r, inp.C, inp.R, &a, &n);
if (d > 0 && d < DG) { // possibly in sight
if (get_color_material (r, a, n, inp.mat, px))
return d;
}
return HUGE;
}
// User interface
void sphere (struct _sphere inp) {
[functot] = &get_pixel_sphere;
sketch_listif (!inp.R)
.R = 0.5;
inpif (!inp.mat.dull && !inp.mat.ind && !any_col (inp.mat.col) &&
!inp.mat.s.i && !inp.mat.v.y.i && !(inp.mat.R >= 1.))
.mat.col[0]= 200, inp.mat.col[1] = 100, inp.mat.col[2] = 150;
inpif (inp.mat.dull && !any_col(inp.mat.col)) {
.mat.col[0] = 50, inp.mat.col[1] = 100, inp.mat.col[2] = 150;
inp.mat.col2[0] = 50, inp.mat.col2[1] = 150, inp.mat.col2[2] = 100;
inp.mat.n1 = (coord){0.1, 0.9, 0};
inp}
if (!inp.mat.SPexp && !inp.mat.SPR) {
.mat.SPexp = 40;
inp.mat.SPR = 0.1;
inp}
if (!inp.mat.map)
.mat.map = jet;
inpif (!inp.mat.min && !inp.mat.max) {
.mat.min = -L0;
inp.mat.max = L0;
inp}
[functot] = inp;
slist++;
functot}
Equiplane
An (Curved) Plane where a scalar field s
has the value
val
. It can also be used to sketch a smooth vof-interface
reconstruction.
struct _equiplane {
scalar s;
double val;
bool insideout;
bool vof;
;
material mat} _equiplane;
struct _equiplane eqlist[N_SKETCH];
trace
double get_pixel_equiplane (struct _equiplane inp, ray r, double DG, unsigned char * px) {
scalar s = inp.s;
// Draw the distance equiplane for vof
if (inp.vof) {
= s.vofd;
s if (inp.insideout) //Invert
.insideout = false;
inp
else.insideout = true;
inp}
scalar posi = s.possible;
vector v = s.normals;
double d = HUGE;
, a;
coord nforeach_possible_ray_equi_intersection(r, s, posi, DG) {
if (_dist < d) {
double vals[2];
for (int i = 0; i < 2; i++)
[i] = interpolate(s, _a[i].x, _a[i].y, _a[i].z);
valsif ((vals[0] - inp.val) * (vals[1] - inp.val) < 0.) {
double w = fabs(vals[0] - inp.val)/fabs((vals[0] - inp.val) - (vals[1] - inp.val));
if (w < 0 || w > 1)
continue;
double mrdir = 0;
(&r.dir);
normalize foreach_dimension() {
.x = w*_a[1].x + (1. - w)*_a[0].x;
aif (fabs(r.dir.x) > mrdir) {
= fabs(r.dir.x);
mrdir = (a.x - r.O.x)/r.dir.x;
d }
}
.x = interpolate (v.x, a.x, a.y, a.z);
n.y = interpolate (v.y, a.x, a.y, a.z);
n.z = interpolate (v.z, a.x, a.y, a.z);
n}
}
}
if (any_comp(n) && d < DG && d > 0) {
if (inp.insideout) {
foreach_dimension()
.x *= -1;
n}
(&n);
normalize if (get_color_material (r, a, n, inp.mat, px))
return d;
}
return HUGE;
}
void equiplane (struct _equiplane inp) {
[functot] = &get_pixel_equiplane;
sketch_listif (!inp.mat.dull && !inp.mat.ind && !any_col (inp.mat.col) &&
!inp.mat.s.i && !inp.mat.v.x.i && !(inp.mat.R >= 1.))
.mat.col[0]= 100, inp.mat.col[1] = 200, inp.mat.col[2] = 150;
inpif (!inp.mat.SPexp && !inp.mat.SPR) {
.mat.SPexp = 40;
inp.mat.SPR = 0.1;
inp}
if (!inp.mat.map)
.mat.map = jet;
inpif (!inp.mat.min && !inp.mat.max) {
.mat.min = -L0;
inp.mat.max = L0;
inp}
[functot] = inp;
eqlist++;
functot}
Image in the scene
Place an image in the scene
#define PIXEL_INDEX (((ix + inp.res*jy)*3))
struct _image {
char * fname;
int res;
unsigned char * imgrgb; //[res*res*3];
;
coord ndouble alpha;
;
coord upunsigned char greenscreen[3]; // Transparent color code (non black green screen)
;
material mat};
struct _image ilist[N_SKETCH];
double get_pixel_image (struct _image inp, ray r, double DG, unsigned char * px) {
;
coord adouble d = ray_plane_intersect (r, inp.n, inp.alpha, &a);
if (d <= 0 || d >= DG)
return HUGE;
if (locate (a.x, a.y, a.z).level < 0) // Only slices in the domain
return HUGE;
(&inp.n);
normalize {;}
//Only grid aligned projections
= {X0, Y0, Z0};
coord Ori foreach_dimension() {
if (inp.n.z) {
int ix = max(min(inp.res*(a.x - Ori.x)/L0, inp.res - 1), 0);
int jy = max(min(inp.res*(Ori.y + L0 - a.y)/L0, inp.res - 1), 0);
for (int i = 0; i < 3; i++)
.mat.col[i] = max(inp.imgrgb[PIXEL_INDEX + i], 1);
inp}
}
if (any_col (inp.greenscreen)) {
int count = 0;
for (int i = 0; i < 3; i++)
if (inp.greenscreen[i] == inp.mat.col[i])
++;
countif (count == 3) //its the green-screen val
return HUGE;
}
if (get_color_material (r, a, inp.n, inp.mat, px))
return d;
return HUGE;
}
bool image (struct _image inp) {
if (!any_comp(inp.n))
.n.z = 1;
inpif (!inp.alpha)
.alpha = 0;
inpif (!inp.res)
.res = (1 << grid->maxdepth);
inpif (inp.fname && inp.imgrgb == NULL) {
.imgrgb = malloc (3*sizeof(unsigned char)*sq(inp.res));
inpchar cmd[999];
sprintf (cmd, "convert -quiet %s -interpolate Nearest -filter point -resize %dx%d^ \
-gravity center -extent %dx%d -depth 8 2not8exist5.ppm",
.fname, inp.res, inp.res, inp.res, inp.res);
inpassert(!system (cmd));
FILE * fpi = fopen ("2not8exist5.ppm", "rb");
char line_one[3]; //three to hold "P6?"
int height, width, max_color;
fscanf(fpi, "%s\n%d %d\n%d\n", line_one, &height, &width, &max_color);
//printf ("%s %d %d %d\n", line_one, height, width, max_color);
(inp.imgrgb, 3*sizeof(unsigned char), sq(inp.res), fpi);
fread fclose (fpi);
} else
assert (0);
[functot] = inp;
ilist[functot] = &get_pixel_image;
sketch_list++;
functotreturn true;
}
Display text
It is possible to add text using convert
and
image
struct _sketch_text {
char * str;
int fs; // Font size (compare against .res)
unsigned char tc[3]; // text color
char * pos; // Imagemagick Position (center, north, south west etc..)
int res; // Text image resolution
char * ops; // Options to pass to convert
;
coord ndouble alpha;
;
coord up;
material mat};
bool sketch_text (struct _sketch_text inp) {
char fname[99] = "3not9exist6.ppm";
char cmd[999];
if (!inp.fs)
.fs = 100;
inpif (!inp.res)
.res = 1000;
inpunsigned char tc[3] = {0,0,0}; //black
char d_ops[2] = " ";
if (!inp.ops)
.ops = d_ops;
inpif (any_col(inp.tc)) {
[0] = inp.tc[0];
tc[1] = inp.tc[1];
tc[2] = inp.tc[2];
tc}
unsigned char gs[3]; //Set Green screen color
for (int i = 0; i < 3; i++) {
int tci = tc[i];
[i] = tci - 2 <= 0 ? tc[i] + 2 : tc[i] - 2;
gs}
char tmp[99] = "northwest";
if (!inp.pos)
.pos = tmp;
inpsprintf (cmd, "convert -quiet %s -size %dx%d -background \"rgb(%d, %d, %d)\"\
-fill \"rgb(%d,%d,%d)\" -pointsize %d -gravity %s caption:'%s' -depth 8 %s",
.ops, inp.res, inp.res, gs[0], gs[1], gs[2], tc[0], tc[1], tc[2],
inp.fs, inp.pos, inp.str, fname);
inp//printf ("%s \n", cmd);
system (cmd);
struct _image inpn;
.fname = fname;
inpn.greenscreen[0] = gs[0];
inpn.greenscreen[1] = gs[1];
inpn.greenscreen[2] = gs[2];
inpn.res = inp.res;
inpn.n = inp.n;
inpn.alpha = inp.alpha;
inpn.up = inp.up;
inpn.mat = inp.mat;
inpn.imgrgb = NULL;
inpnreturn image (inpn);
}
A volumetric object
It is possible to sketch volumetric objects as a smoke concentration
field. (0 ~ mval < s[]
)
#include "radix.h" //A sorting algorithm
bool sketch_a_volume = false;
struct _volume {
scalar s;
double sc; // 1/e scale for s times length
double mval; // Minimal value (positive and close to zero)
unsigned char col[3]; // prescribed Color
bool cols; // Color by scalar value (color map)
vector colorv; // Color map 3D vector direction xyz -> rgb
; // color Map (default is cool_warm)
Colormap mapdouble min; // min cbar
double max; // max cbar
double shading; // shading effect for default lights
};
struct _volume vollist; //max 1
double get_attenuation (coord a, struct _volume inp) {
= {.O = a, .dir = lights[1].dir};
ray r foreach_dimension()
.dir.x *= -1;
r(&r.dir);
normalize scalar s = inp.s;
double sl = 0;
foreach_ray_cell_intersection_volume (r, HUGE, s.possible) {
= {0, 0, 0};
coord mean double len = 0;
foreach_dimension() {
.x = (_a[0].x + _a[1].x)/2.;
mean+= sq(_a[0].x - _a[1].x);
len }
double val = interpolate (inp.s, mean.x, mean.y, mean.z);
if (val > inp.mval) {
= sqrt(len);
len if (len > 0)
+= fabs(val)*len;
sl }
}
return sl;
}
trace
void mod_pixel_volume (struct _volume inp, ray r, double DG, unsigned char * px) {
scalar s = inp.s;
int np = 50, incf = 2; // Array size, increase factor;
double * dvl = NULL;
int elm = 0;
if (inp.cols) {
= 3 + (inp.shading > 0) + 3*(inp.colorv.x.i > 0);
elm = malloc (elm*sizeof(double)*np); //Distance, values and length;
dvl }
int celld = 0; // Number if cells
double sl = 0; //integral of s times length;
foreach_ray_cell_intersection_volume(r, DG, s.possible) {
= {0, 0, 0};
coord mean double len = 0;
foreach_dimension() {
.x = (_a[0].x + _a[1].x)/2.;
mean+= sq(_a[0].x - _a[1].x);
len }
double val = interpolate (inp.s, mean.x, mean.y, mean.z);
if (val > inp.mval) {
= sqrt(len);
len if (len > 0)
+= fabs(val)*len;
sl if (inp.cols) { // Record data
++;
celldif (celld >= np) { // increase array size
*= incf;
np = realloc (dvl, elm*sizeof(double)*np);
dvl }
double dirm = 0;
foreach_dimension() { //distance
if (fabs(r.dir.x) > dirm)
[elm*(celld - 1)] = (mean.x - r.O.x)/r.dir.x;
dvl}
assert (dvl[elm*(celld - 1)] > 0);
[elm*(celld - 1) + 1] = val;
dvl[elm*(celld - 1) + 2] = len;
dvlif (inp.shading)
[elm*(celld - 1) + 3] = get_attenuation (mean, inp);
dvlif (inp.colorv.x.i) {
double xp = mean.x, yp = mean.y, zp = mean.z;
int dim = 0;
foreach_dimension()
[elm*(celld - 1) + (inp.shading ? 4: 3) + dim++] = interpolate (inp.colorv.x, xp, yp, zp);
dvl}
}
}
}
if (inp.cols && sl > 0) { // Compute effective absorbed color
int ind[celld], dista[celld];
for (int it = 0; it < celld; it++)
[it] = (int)(1 << (depth() + 1))*(dvl[elm*it]/L0); // distance in integer units
distaradixsort (celld, dista, ind);
double col[3] = {0, 0, 0};
double cmap[NCMAP][3];
.map (cmap);
inpdouble sl2 = 0;
double TW = 0;
for (int it = 0; it < celld; it++) {
unsigned char coll[3];
if (inp.colorv.x.i) {
int mx = 0;
for (int i = 0; i < 3; i++) {
[i] = 255*min(1, fabs(dvl[elm*ind[it] + (inp.shading ? 4: 3) + i])/inp.max);
collif (coll[i] > mx)
= coll[i];
mx }
// rescale
if (inp.colorv.x.i == 0)
for (int i = 0; i < 3; i++)
[i] = mx > 0 ? min (255, (255*coll[i])/mx) : 1;
coll} else
(coll, cmap, dvl[elm*ind[it] + 1], inp.min, inp.max);
colormap_pigmentation double sle = dvl[elm*ind[it] + 2]*fabs(dvl[elm*ind[it] + 1]);
double Weight = exp(-sl2/inp.sc) - exp(-(sl2 + sle)/inp.sc);
double fac = 1;
if (inp.shading) {
double I = lights[0].I;
+= lights[1].I*exp(-dvl[elm*ind[it] + 3]/(smoke.att));
I = min (max(cam.f(I) - cam.f(cam.min),0)/cam.f(cam.max/cam.min), 1);
fac }
+= Weight;
TW for (int i = 0; i < 3; i++)
[i] += (double)coll[i]*Weight*fac;
col+= sle;
sl2 }
if (TW > 0) {
for (int i = 0; i < 3; i++)
.col[i] = (unsigned char)(col[i]/TW);
inp}
}
if (sl > 0) {
double W = exp(-sl/inp.sc);
[0] = (1 - W)*inp.col[0] + W*px[0];
px[1] = (1 - W)*inp.col[1] + W*px[1];
px[2] = (1 - W)*inp.col[2] + W*px[2];
px}
free (dvl); dvl = NULL;
}
bool volume (struct _volume inp) {
if (!inp.sc)
.sc = 1.;
inpif (!any_col(inp.col)) {
.col[0] = 255;
inp.col[1] = 255;
inp.col[2] = 255;
inp}
if (!inp.max)
.max = 1;
inpif (!inp.mval)
.mval = 0;
inpif (!inp.min)
.min = inp.mval;
inpif (inp.shading) {
scalar s = inp.s;
= s;
smoke .att = inp.sc/inp.shading;
s}
if (!inp.map)
.map = cool_warm;
inp= inp;
vollist = true;
sketch_a_volume return sketch_a_volume;
}
Triangles
Draw a list of triangles, stored as a nodata
terminated
list of vertex triplets (e.g. as read by inport_stl
).
struct _triangles {
* c;
coord [2]; //Bounding box (automatically computed)
coord bb;
material mat};
struct _triangles tlist[N_SKETCH];
trace
double get_pixel_triangles (struct _triangles inp, ray r, double DG, unsigned char * px) {
* p = inp.c;
coord double dist = DG;
, n;
coord abool any = false;
if (ray_box (r, inp.bb) < dist) {
while (p->x != nodata) {
double d = ray_triangle (r, p);
if (d < dist) {
= d;
dist = true;
any foreach_dimension()
.x = r.O.x + r.dir.x*d;
a, a2;
coord a1foreach_dimension() {
.x = p[1].x - p[0].x;
a1.x = p[2].x - p[0].x;
a2}
= cross (a1, a2);
n (&n);
normalize }
+= dimension; //3?
p }
if (any)
if (get_color_material (r, a, n, inp.mat, px))
return dist;
}
return HUGE;
}
struct _triangles get_bb (struct _triangles in) {
struct _triangles inp = in;
foreach_dimension() {
.bb[0].x = HUGE;
inp.bb[1].x = -HUGE;
inp}
while (in.c->x < nodata) {
foreach_dimension() {
if (in.c->x < inp.bb[0].x)
.bb[0].x = in.c->x;
inpif (in.c->x > inp.bb[1].x)
.bb[1].x = in.c->x;
inp}
.c++;
in}
return inp;
}
bool triangles (struct _triangles inp) {
[functot] = &get_pixel_triangles;
sketch_listif (!inp.mat.dull && !inp.mat.ind && !any_col (inp.mat.col) &&
!inp.mat.s.i && !inp.mat.v.x.i && !(inp.mat.R >= 1.))
.mat.col[0]= 200, inp.mat.col[1] = 150, inp.mat.col[2] = 100;
inpif (!inp.mat.SPexp && !inp.mat.SPR) {
.mat.SPexp = 40;
inp.mat.SPR = 0.1;
inp}
if (!inp.mat.map)
.mat.map = jet;
inpif (!inp.mat.min && !inp.mat.max) {
.mat.min = -L0;
inp.mat.max = L0;
inp}
= get_bb (inp);
inp [functot] = inp;
tlist++;
functotreturn true;
}
## A plain sheet for sketching…
plain()
mimics bview’s clear()
a bit.
void plain (void) {
for (int func = 0; func < functot; func++)
if (sketch_list[func] == &get_pixel_image) {
free (ilist[func].imgrgb);
[func].imgrgb = NULL;
ilist}
= 0;
functot free (shading);
= NULL;
shading = false;
sketch_a_volume }
The ray-caster function
The implementation of a helper function that gets the
rgb
values of a ray by cycling though all sketch function
calls…
double get_color_ray (ray r, unsigned char * px) {
double Distg = HUGE;
for (int func = 0; func < functot; func++) {
double dist = HUGE;
unsigned char pt[3];
if (sketch_list[func] == &get_pixel_quadriangles)
= sketch_list[func](qlist[func], r, Distg, pt);
dist else if (sketch_list[func] == &get_pixel_sketch_vof)
= sketch_list[func](svlist[func], r, Distg, pt);
dist else if (sketch_list[func] == &get_pixel_disk)
= sketch_list[func](dlist[func], r, Distg, pt);
dist else if (sketch_list[func] == &get_pixel_lattice)
= sketch_list[func](llist[func], r, Distg, pt);
dist else if (sketch_list[func] == &get_pixel_sphere)
= sketch_list[func](slist[func], r, Distg, pt);
dist else if (sketch_list[func] == &get_pixel_equiplane)
= sketch_list[func](eqlist[func], r, Distg, pt);
dist else if (sketch_list[func] == &get_pixel_image)
= sketch_list[func](ilist[func], r, Distg, pt);
dist else if (sketch_list[func] == &get_pixel_triangles)
= sketch_list[func](tlist[func], r, Distg, pt);
dist if (dist < Distg && dist > 0) {
= dist;
Distg for (int i = 0; i < 3; i++)
[i] = pt[i];
px}
}
if (sketch_a_volume) // Modify this pixel color
mod_pixel_volume (vollist, r, Distg, px);
return Distg;
}
static inline double point_value (scalar s, Point point) {
return s[];
}
Watch the sketch process
You may watch the sketching process live in a (minimalistic)
X11 window. You should have the libX11
library installed and
compile using something like:
-O2 -DWATCH_ALONG bwatch.c -lm -lX11
$ qcc ./a.out $
Note that it may slow the rendering, especially with openmp.
#if WATCH_ALONG
#include <X11/Xlib.h>
;
GC gc_2= (Window)NULL;
Window win *dsp;
Display int screen;
;
Colormap cmap
void XSetup (void) {
if (win == (Window)NULL) {
#if _OPENMP
();
XInitThreads#pragma omp barrier
#endif
= XOpenDisplay(NULL);
dsp = DefaultScreen(dsp);
screen = XCreateWindow(dsp, DefaultRootWindow(dsp),
win 0, 0, cam.nx, cam.ny,
0, 0, 0, 0, 0, 0);
;
XGCValues gcvalues_2.function = GXcopy;
gcvalues_2.plane_mask = AllPlanes;
gcvalues_2.foreground = 0x00FF00;
gcvalues_2.background = 0xFFFFFF;
gcvalues_2= XCreateGC(dsp, win,
gc_2 |GCPlaneMask|GCForeground|GCBackground,
GCFunction&gcvalues_2);
(dsp, win, "Bwatch: Watch Along\0");
XStoreName (dsp, win);
XMapWindow= XDefaultColormap(dsp, screen);
cmap}
}
void XSketchPx (int x, int y, unsigned char px[3]) {
(dsp);
XLockDisplay ;
XColor pX11.red = 256*px[0];
pX11.green = 256*px[1];;
pX11.blue = 256*px[2];;
pX11if (XAllocColor(dsp,cmap,&pX11)==0)
assert (0);
(dsp,gc_2,pX11.pixel);
XSetForeground(dsp, win, gc_2, x, cam.ny - y );
XDrawPointif (tid() == 0)
(dsp);
XFlush (dsp);
XUnlockDisplay }
void XStop (void) {
(dsp, win);
XDestroyWindow(dsp);
XCloseDisplay= (Window)NULL;
win }
event stop_X11 (t = end) {
XStop();
}
#endif
Preparation
Before rendering, it maybe usefull to compute some helper fields to store data for rendering or help guide optimized grid iterators.
void preparation_steps (void){
#if WATCH_ALONG
XSetup();
#endif
for (int func = 0; func < functot; func++) {
//Gradients and possibles for isosurfaces,
// maybe compute the nearby distances to vof fields.
if (sketch_list[func] == &get_pixel_equiplane) {
scalar s = eqlist[func].s;
if (eqlist[func].vof) {
char dname[99];
sprintf (dname, "dfor%s%d", s.name, func);
scalar d = new_scalar (dname);
.vofd = d;
sfind_nearby_distances (s);
= d; // we will draw the isosurface of d not s
s }
char vname[99], pname[99];
sprintf (vname, "vfor%s%d", s.name, func);
sprintf (pname, "pfor%s%d", s.name, func);
vector v = new_vector (vname);
scalar p = new_scalar (pname);
.normals = v;
s.possible = p;
sfind_possible (s, eqlist[func].val);
({s}, {v});
gradients foreach() {
= {0};
coord n foreach_dimension()
.x = v.x[];
nif (any_comp(n)) {
(&n);
normalize foreach_dimension()
.x[] = n.x;
v}
}
boundary ((scalar*){v});
}
// Precompute normals and plane alphas for vof facets
else if (sketch_list[func] == &get_pixel_sketch_vof) {
if (svlist[func].precomp) {
scalar f = svlist[func].f;
char vname[99], aname[99];
sprintf (vname, "vfor%s%d", f.name, func);
sprintf (aname, "afor%s%d", f.name, func);
vector v = new_vector (vname);
scalar alpha = new_scalar (aname);
.normals = v;
f.possible = alpha; //alpha
fforeach() {
[] = nodata;
alphaforeach_dimension()
.x[] = nodata;
vif (interfacial2 (f, point)) {
= mycs (point, f); //pointing outwards.
coord n double l = vec_length(n);
[] = plane_alpha (point_value(f, point), n)/l;
alpha(&n);
normalize foreach_dimension()
.x[] = n.x;
v}
}
boundary ({alpha, v});
}
}
}
if (sketch_a_volume) { //Pre compute cells with volume
scalar s = vollist.s;
char pname [99];
sprintf (pname, "p_for_%s_volume", s.name);
scalar p = new_scalar (pname);
.possible = p;
sfind_possible_vol (s, vollist.mval);
}
}
void aftercare (void) {
for (int func = 0; func < functot; func++) {
if (sketch_list[func] == &get_pixel_equiplane) {
scalar s = eqlist[func].s;
if (eqlist[func].vof)
= s.vofd;
s vector v = s.normals;
scalar posi = s.possible;
delete ({posi, v});
if (eqlist[func].vof)
delete ({s});
}
else if (sketch_list[func] == &get_pixel_sketch_vof)
if (svlist[func].precomp) {
scalar s = svlist[func].f;
vector v = s.normals;
scalar alpha = s.possible;
delete ((scalar*){alpha, v});
}
}
if (sketch_a_volume) {
.i = 0;
smokescalar s = vollist.s;
= s.possible;
s delete ({s});
}
}
Store
Write a .ppm
to the FILE * fp
.
// Default background
#define BGR (0)
#define BGG (25.*(ii + jj)/cam.nx + 120)
#define BGB (50.*jj/cam.nx + 120)
trace
bool store (FILE * fp) {
if (lights[0].type == 0) // No lights
();
default_lightsfprintf (fp, "P6\n%d %d\n%d\n", cam.nx, cam.ny, 255); // .ppm header
preparation_steps();
foreach_ray() {
unsigned char px[3] = {BGR, BGG, BGB};
get_color_ray (_r, px);
(px, 1, 3, fp);
fwrite #if WATCH_ALONG
XSketchPx (ii, jj, px);
#endif
}
fflush (fp);
aftercare();
return true;
}
#if _OPENMP
trace
bool store_OPENMP (FILE * fp) {
if (lights[0].type == 0) // No lights
();
default_lightsfprintf (fp, "P6\n%d %d\n%d\n", cam.nx, cam.ny, 255); // .ppm header
preparation_steps();
#pragma omp parallel shared(fp)
{
assert (!(cam.nx*cam.ny % npe()));
unsigned char px[3];
foreach_ray() {
int jjj = cam.ny - jj;
if ((jjj*cam.nx + ii) % npe() == tid()) {
[0]= BGR, px[1] = BGG, px[2] = BGB;
pxget_color_ray (_r, px);
#if WATCH_ALONG
XSketchPx (ii, jj, px);
#endif
}
if ((jjj*cam.nx + ii) % npe() == (npe() - 1))
for (int i = 0; i < npe(); i++) {
if (tid() == i) {
(px, 1, 3, fp);
fwrite
}
#pragma omp barrier
}
}
}
fflush (fp);
aftercare();
return true;
}
// Overload calls to store
#define store store_OPENMP
#endif //_OPENMP
Store adaptive
With raycaster
(or this one) installed, we can adaptively sample
the scene (lossy compresion). It can be effecient to generate high
resolution movies.
# if 0
#include <sys/wait.h>
#define ParentRead read_pipe[0]
#define ParentWrite write_pipe[1]
#define ChildRead write_pipe[0]
#define ChildWrite read_pipe[1]
#define BGRa (0)
#define BGGa (25.*(b[0] + b[1]) + 120)
#define BGBa (50.*b[1] + 120)
struct _sa {
FILE * fp;
int ml; // max level of output image (cam.nx/ny are ignored)
double tol; // tolerance
};
trace
bool store_adaptive (struct _sa inp) {
double tol = 25;
int ml = 10;
if (inp.tol)
= inp.tol;
tol if (inp.ml)
= inp.ml;
ml if (lights[0].type == 0) // No lights
();
default_lightspreparation_steps();
// Organize pipe and fork
int read_pipe[2];
int write_pipe[2];
(read_pipe);
pipe (write_pipe);
pipe ;
pid_t p= fork();
p if (p == 0) { //child calls `raycaster`
(ParentRead);
close (ParentWrite);
close if (system("which ./raycaster > /dev/null 2>&1")) {
fprintf (stderr, "Error: Adaptive raycaster is not installed\n");
assert(0);
} else {
(ChildRead, STDIN_FILENO);
dup2 (ChildWrite, STDOUT_FILENO);
dup2 (fileno(inp.fp), STDERR_FILENO);
dup2 char m1[9], m2[15];
sprintf (m1, "%d", ml);
sprintf (m2, "%g", tol);
("raycaster", "raycaster", m1, m2, NULL);
execl }
} else { // Parent computes color codes
(ChildRead);
close (ChildWrite);
close int a = sq(1 << (ml - 2)) + 1;
int al = a;
double * pos = malloc (2*al*sizeof(double));
unsigned char * pxs = malloc (3*al*sizeof(unsigned char));
while (a) {
(ParentRead, &a, sizeof(int));
read if (a > al) {
= 2*a;
al = realloc (pos, 2*al*sizeof(double));
pos = realloc (pxs, 3*al*sizeof(unsigned char));
pxs }
for (int i = 0; i < a; i++)
(ParentRead, &pos[2*i], 2*sizeof(double));
read , proj, vert, hori;
coord crforeach_dimension()
.x = cam.poi.x - cam.O.x;
cr(&cr);
normalize foreach_dimension() {
.x = cam.up.x*cr.x;
proj.x = cam.up.x - proj.x;
vert}
= cross (cr, vert);
hori (&vert);
normalize (&hori);
normalize #if _OPENMP
#pragma omp parallel
#endif
for (int i = tid(); i < a; i += npe()) {
;
coord apointdouble b[2];
[0] = pos[2*(i)];
b[1] = pos[2*(i) + 1];
bforeach_dimension()
.x = (cam.poi.x
apoint+ cam.fov*(b[0] - 0.5)*hori.x
+ cam.fov*(b[1] - 0.5)*vert.x);
// New ray
;
ray _r.depth = 0;
_r.O = cam.O;
_rforeach_dimension()
.dir.x = apoint.x - cam.O.x;
_r(&_r.dir);
normalize // Get and write color
[3*i] = BGRa;
pxs[3*i + 1] = BGGa;
pxs[3*i + 2] = BGBa;
pxsget_color_ray (_r, &pxs[3*i]);
}
(ParentWrite, &pxs[0], 3*a*sizeof(unsigned char));
write }
(NULL); //wait until the child is done writing the image
wait free (pos); pos = NULL;
free (pxs); pxs = NULL;
}
aftercare();
return true;
}
#endif