2
* routines to render a fractal landscape as an image
9
char artist_Id[] = "$Id: artist.c,v 1.40 1997/12/03 17:21:10 spb Exp spb $";
17
int base=0; /* parity flag for mirror routine */
19
extern Parm fold_param;
29
double shadow_register;
38
float viewpos; /* position of viewpoint */
39
float viewheight; /* height of viewpoint */
41
float vstrength; /* strength of vertical light source */
42
float lstrength; /* strength of vertical light source */
46
Height *shadow; /* height of the shadows */
47
Height *a_strip, *b_strip; /* the two most recent strips */
51
/* {{{ void set_clut(int max_col, Gun *red, Gun *green, Gun *blue)*/
53
* setup the colour lookup table
55
void set_clut (max_col,red,green,blue)
66
* float rb[N_BANDS] = { 0.167,0.200,0.333,0.450,0.600,1.000 };
67
* float gb[N_BANDS] = { 0.667,0.667,0.500,0.500,0.600,1.000 };
68
* float bb[N_BANDS] = { 0.500,0.450,0.333,0.200,0.000,1.000 };
75
/* band base colours as RGB fractions */
76
rb[0] = 0.450; rb[1] = 0.600; rb[2] = 1.000;
77
gb[0] = 0.500; gb[1] = 0.600; gb[2] = 1.000;
78
bb[0] = 0.333; bb[1] = 0.000; bb[2] = 1.000;
86
red[WHITE] = COL_RANGE;
87
green[WHITE] = COL_RANGE;
88
blue[WHITE] = COL_RANGE;
91
red[SKY] = 0.404*COL_RANGE;
92
green[SKY] = 0.588*COL_RANGE;
93
blue[SKY] = COL_RANGE;
97
green[SEA_LIT] = 0.500*COL_RANGE;
98
blue[SEA_LIT] = 0.700*COL_RANGE;
102
green[SEA_UNLIT] = ((g.ambient+(g.vfract/(1.0+g.vfract)))*0.500)*COL_RANGE;
103
blue[SEA_UNLIT] = ((g.ambient+(g.vfract/(1.0+g.vfract)))*0.700)*COL_RANGE;
106
if( MIN_COL > max_col )
108
fprintf(stderr,"set_clut: less than the minimum number of colours available\n");
111
/* max_col can over-rule band_size */
112
while( (BAND_BASE +g.band_size*N_BANDS) > max_col )
117
for( band=0 ; band<N_BANDS; band++)
119
for(shade=0 ; shade < g.band_size ; shade++)
121
if( (BAND_BASE + (band*g.band_size) + shade) >= max_col )
123
fprintf(stderr,"INTERNAL ERROR, overflowed clut\n");
128
bot = g.ambient * top;
129
intensity = bot + ((shade * (top - bot))/(g.band_size-1));
130
tmp = COL_RANGE * intensity;
133
fprintf(stderr,"set_clut: internal error: invalid code %d\n",tmp);
136
if( tmp > COL_RANGE )
140
red[BAND_BASE + (band*g.band_size) + shade] = tmp;
144
bot = g.ambient * top;
145
intensity = bot + ((shade * (top - bot))/(g.band_size-1));
146
tmp = COL_RANGE * intensity;
149
fprintf(stderr,"set_clut: internal error: invalid code %d\n",tmp);
152
if( tmp > COL_RANGE )
156
green[BAND_BASE + (band*g.band_size) + shade] = tmp;
160
bot = g.ambient * top;
161
intensity = bot + ((shade * (top - bot))/(g.band_size-1));
162
tmp = COL_RANGE * intensity;
165
fprintf(stderr,"set_clut: internal error: invalid code %d\n",tmp);
168
if( tmp > COL_RANGE )
172
blue[BAND_BASE + (band*g.band_size) + shade] = tmp;
178
/* {{{ Height *extract(Strip *s) */
180
* extract the table of heights from the Strip struct
181
* and discard the rest of the struct.
191
for(i=0 ; i<g.width; i++ )
193
p[i] = shift + (vscale * p[i]);
198
/* {{{ void init_artist_variables() */
200
* initialise the variables for the artist routines.
202
void init_artist_variables()
205
int pwidth; /* longest lengthscale for update */
207
g.width= (1 << g.levels)+1;
208
pwidth= (1 << (g.levels - g.stop))+1;
210
/* make the fractal SIDE wide, this makes it easy to predict the
211
* average height returned by calcalt. If we have stop != 0 then
212
* make the largest update length = SIDE
214
cos_phi = cos( g.phi );
215
sin_phi = sin( g.phi );
216
tan_phi = tan( g.phi );
218
x_fact = cos_phi* cos(g.alpha);
219
y_fact = cos_phi* sin(g.alpha);
220
vscale = g.stretch * pwidth; /* have approx same height as fractal width
221
* this makes each pixel SIDE=1.0 wide.
225
delta_shadow = tan_phi /cos(g.alpha);
226
shadow_slip = tan(g.alpha);
227
/* guess the average height of the fractal */
228
varience = pow( SIDE ,(2.0 * fold_param.fdim));
229
varience = vscale * varience ;
230
shift = g.base_shift * varience;
231
varience = varience + shift;
234
/* set the position of the view point */
235
viewheight = g.altitude * g.width;
236
viewpos = - g.distance * g.width;
238
/* set viewing angle and focal length (vertical-magnification)
239
* try mapping the bottom of the fractal to the bottom of the
240
* screen. Try to get points in the middle of the fractal
244
dd = (g.width / 2.0) - viewpos;
245
focal = sqrt( (dd*dd) + (dh*dh) );
247
tan_vangle = (double) ((double)(viewheight-g.sealevel)/(double) - viewpos);
248
vangle = atan ( tan_vangle );
249
vangle -= atan( (double) (g.graph_height/2) / focal );
251
/* we are making some horrible approximations to avoid trig funtions */
252
tan_vangle = (double) ((double)(viewheight-sealevel)/(double) - viewpos);
253
tan_vangle = tan_vangle - ( (double) (height/2) / focal );
256
top=make_fold(NULL, &fold_param, g.levels,g.stop,(SIDE / pwidth));
258
/* use first set of heights to set shadow value */
259
shadow = extract(next_strip(top));
260
a_strip = extract( next_strip(top) );
261
b_strip = extract( next_strip(top) );
263
/* initialise the light strengths */
264
vstrength = g.vfract * g.contrast /( 1.0 + g.vfract );
265
lstrength = g.contrast /( 1.0 + g.vfract );
269
g.pos=g.graph_width-1;
273
/* {{{ Col get_col(Height p, Height p_minus_x, Height p_minus_y, Height shadow) */
275
* calculate the colour of a point.
277
Col get_col (p,p_minus_x,p_minus_y,shadow)
283
Height delta_x, delta_y;
284
Height delta_x_sqr, delta_y_sqr;
291
/* {{{ if underwater*/
292
if ( p < g.sealevel )
294
if( shadow > g.sealevel )
303
* We have three light sources, one slanting in from the left
304
* one directly from above and an ambient light.
305
* For the directional sources illumination is proportional to the
306
* cosine between the normal to the surface and the light.
308
* The surface contains two vectors
312
* The normal therefore is parallel to
313
* ( -delta_x, -delta_y, 1)/sqrt( 1 + delta_x^2 + delta_y^2)
315
* For light parallel to ( cos_phi, 0, -sin_phi) the cosine is
316
* (cos_phi*delta_x + sin_phi)/sqrt( 1 + delta_x^2 + delta_y^2)
318
* For light parallel to ( cos_phi*cos_alpha, cos_phi*sin_alpha, -sin_phi)
320
* (cos_phi*cos_alpha*delta_x + cos_phi*sin_alpha*delta_y+ sin_phi)/sqrt( 1 + delta_x^2 + delta_y^2)
322
* For vertical light the cosine is
323
* 1 / sqrt( 1 + delta_x^2 + delta_y^2)
326
delta_x = p - p_minus_x;
327
delta_y = p - p_minus_y;
328
delta_x_sqr = delta_x * delta_x;
329
delta_y_sqr = delta_y * delta_y;
330
hypot_sqr = delta_x_sqr + delta_y_sqr;
331
norm = sqrt( 1.0 + hypot_sqr );
333
/* {{{ calculate effective height */
334
effective = (p + (varience * g.contour *
335
(1.0/ ( 1.0 + hypot_sqr))));
337
/* {{{ calculate colour band. */
338
band = ( effective / varience) * N_BANDS;
343
if( band > (N_BANDS - 1))
347
result = (BAND_BASE + (band * g.band_size));
350
/* {{{ calculate the illumination stength*/
352
* add in a contribution for the vertical light. The normalisation factor
361
* add in contribution from the main light source
363
/* dshade += ((double) lstrength * ((delta_x * cos_phi) + sin_phi));*/
364
dshade += ((double) lstrength *
365
((delta_x * x_fact) + (delta_y * y_fact) + sin_phi));
367
/* divide by the normalisation factor (the same for both light sources) */
370
/* {{{ calculate shading */
371
/* dshade should be in the range 0.0 -> 1.0
372
* if the light intensities add to 1.0
373
* now convert to an integer
375
shade = dshade * (double) g.band_size;
376
if( shade > (g.band_size-1))
378
shade = (g.band_size-1);
380
/* {{{ if shade is negative then point is really in deep shadow */
388
if( (result >= g.n_col) || (result < 0) )
390
fprintf(stderr,"INTERNAL ERROR colour out of range %d max %d\n",result,g.n_col);
396
/* {{{ Col *makemap(Height *a, Height *b, Height *shadow) */
397
Col *makemap (a,b,shadow)
405
/* This routine returns a plan view of the surface */
406
res = (Col *) malloc(g.width * sizeof(Col) );
409
fprintf(stderr,"malloc failed for colour strip\n");
413
for(i=1 ; i<g.width ; i++)
415
res[i] = get_col(b[i],a[i],b[i-1],shadow[i]);
421
/* {{{ Col *camera(Height *a, Height *b, Height *shadow) */
422
Col *camera(a,b,shadow)
427
int i, j, coord, last;
430
/* this routine returns a perspective view of the surface */
431
res = (Col *) malloc( g.graph_height * sizeof(Col) );
434
fprintf(stderr,"malloc failed for picture strip\n");
438
* optimised painters algorithm
440
* scan from front to back, we can avoid calculating the
441
* colour if the point is not visable.
443
for( i=0, last=0 ; (i < g.width)&&(last < g.graph_height) ; i++ )
445
if( a[i] < g.sealevel )
449
coord = 1 + project( i, a[i] );
452
/* get the colour of this point, the front strip should be black */
457
col = get_col(b[i],a[i],b[i-1],shadow[i]);
459
if( coord > g.graph_height )
461
coord = g.graph_height;
463
for(;last<coord;last++)
469
for(;last<g.graph_height;last++)
476
/* {{{ Col *mirror(Height *a, Height *b, Height *shadow)*/
477
Col *mirror(a,b,shadow)
484
int i,j, top, bottom, coord;
485
int last_top, last_bottom;
487
/* this routine returns a perspective view of the surface
488
* with reflections in the water
491
res = (Col *) malloc( g.graph_height * sizeof(Col) );
494
fprintf(stderr,"malloc failed for picture strip\n");
498
last_top=g.graph_height-1;
501
* many of the optimisation in the camera routine are
502
* hard to implement in this case so we revert to the
503
* simple painters algorithm modified to produce reflections
504
* scan from back to front drawing strips between the
505
* projected position of height and -height.
506
* for water stipple the colour so the reflection is still visable
508
map=makemap(a,b,shadow);
509
pivot=2.0*g.sealevel;
510
for(i=g.width-1;i>0;i--)
512
if(map[i] < BAND_BASE)
514
/* {{{ stipple water values*/
515
for(j=last_bottom;j<=last_top;j++)
520
/* invalidate strip so last stip does not exist */
521
last_bottom=g.graph_height;
523
/* fill in water values */
524
coord=1+project(i,g.sealevel);
527
/* do not print on every other point
528
* if the current value is a land value
530
if( (j+base)%2 || (res[j]<BAND_BASE) )
535
/* skip any adjacent bits of water with the same colour */
536
while(map[i]==last_col)
540
i++; /* the end of the for loop will decrement as well */
543
/* {{{ draw land values*/
544
top = project(i,a[i]);
545
bottom=project(i,pivot-a[i]);
546
if(last_col == map[i])
552
if( bottom < last_bottom)
559
for(j=top+1;j<=last_top;j++)
564
if(bottom > last_bottom)
566
for(j=last_bottom;j<bottom;j++)
578
/* {{{ draw in front face*/
579
for(j=last_bottom;j<=last_top;j++)
583
if( a[0] < g.sealevel )
585
coord=1+project(0,g.sealevel);
587
coord=1+project(0,a[0]);
599
/* {{{ int project( int x , Height y ) */
601
* project a point onto the screen position
611
theta = atan( (double) ((viewheight - y)/( x - viewpos)) );
612
theta = theta - vangle;
613
pos = (g.graph_height/2) - (focal * tan( theta));
617
/* nast approx to avoid trig functions */
618
tan_theta = (viewheight -y)/(x-viewpos) - tan_vangle;
619
pos = (height/2) - (focal * tan_theta);
621
if( pos > (g.graph_height-1))
623
pos = g.graph_height-1;
632
/* {{{ void finish_artist() */
634
* Tidy up and free everything.
644
/* {{{ void init_parameters() */
646
void init_parameters()
653
g.band_size=BAND_SIZE;
660
g.phi=(40.0 * PI)/180.0;
672
fold_param.rg1=FALSE;
673
fold_param.rg2=FALSE;
675
fold_param.cross=TRUE;
676
fold_param.force_front=TRUE;
677
fold_param.force_back=FALSE;
678
fold_param.forceval=-1.0;
679
fold_param.fdim = 0.65;
681
fold_param.midmix=0.0;
686
/* {{{ Col *next_col(int paint, int reflec) */
688
Col *next_col (paint, reflec)
695
/* {{{ update strips */
700
res = mirror( a_strip,b_strip,shadow);
702
res = camera( a_strip,b_strip,shadow);
705
res = makemap(a_strip,b_strip,shadow);
709
b_strip = extract( next_strip(top) );
712
/* {{{ update the shadows*/
714
/* shadow_slip is the Y component of the light vector.
715
* The shadows can only step an integer number of points in the Y
716
* direction so we maintain shadow_register as the deviation between
717
* where the shadows are and where they should be. When the magnitude of
718
* this gets larger then 1 the shadows are slipped by the required number of
720
* This will not work for very oblique angles so the horizontal angle
721
* of illumination should be constrained.
723
shadow_register += shadow_slip;
724
if( shadow_register >= 1.0 )
726
/* {{{ negative offset*/
728
while( shadow_register >= 1.0 )
730
shadow_register -= 1.0;
733
for(i=g.width-1 ; i>=offset ; i--)
735
shadow[i] = shadow[i-offset]-delta_shadow;
736
if( shadow[i] < b_strip[i] )
738
shadow[i] = b_strip[i];
740
/* {{{ stop shadow at sea level */
742
if( shadow[i] < g.sealevel )
744
shadow[i] = g.sealevel;
751
for(i=0;i<offset;i++)
753
shadow[i] = b_strip[i];
754
/* {{{ stop shadow at sea level*/
755
if( shadow[i] < g.sealevel )
757
shadow[i] = g.sealevel;
763
}else if( shadow_register <= -1.0 ){
764
/* {{{ positive offset*/
765
while( shadow_register <= -1.0 )
767
shadow_register += 1.0;
770
for(i=0 ; i<g.width-offset ; i++)
772
shadow[i] = shadow[i+offset]-delta_shadow;
773
if( shadow[i] < b_strip[i] )
775
shadow[i] = b_strip[i];
777
/* {{{ stop shadow at sea level */
778
if( shadow[i] < g.sealevel )
780
shadow[i] = g.sealevel;
786
shadow[i] = b_strip[i];
787
/* {{{ stop shadow at sea level*/
788
if( shadow[i] < g.sealevel )
790
shadow[i] = g.sealevel;
797
for(i=0 ; i<g.width ; i++)
799
shadow[i] -= delta_shadow;
800
if( shadow[i] < b_strip[i] )
802
shadow[i] = b_strip[i];
804
/* {{{ stop shadow at sea level */
805
if( shadow[i] < g.sealevel )
807
shadow[i] = g.sealevel;
820
/* {{{ void plot_column(g)*/
828
/* blank if we are doing the full window */
831
blank_region(0,0,g->graph_width,g->graph_height);
832
flush_region(0,0,g->graph_width,g->graph_height);
835
if( g->pos == g->graph_width-1){
836
blank_region(0,0,g->graph_width,g->graph_height);
837
flush_region(0,0,g->graph_width,g->graph_height);
841
scroll_screen(g->scroll);
844
l = next_col(1-g->map,g->reflec);
847
if( g->graph_height > g->width ){
850
mapwid=g->graph_height;
852
for( j=0 ;j<(g->graph_height-mapwid); j++)
854
plot_pixel(g->pos,((g->graph_height-1)-j),BLACK);
856
for(j=0; j<mapwid ; j++)
858
plot_pixel(g->pos,((mapwid-1)-j),l[j]);
861
for(j=0 ; j<g->graph_height ; j++)
863
/* we assume that the scroll routine fills the
864
* new region with a SKY value. This allows us to
865
* use a testured sky for B/W displays
869
plot_pixel(g->pos,((g->graph_height-1)-j),l[j]);
874
flush_region(g->pos,0,1,g->graph_height);
876
/* now update pos ready for next time */
879
if(g->pos >= g->graph_width)
882
if( g->pos < 0 || g->pos > g->graph_width-1 )
886
g->scroll = g->repeat;
893
if( g->pos < 0 || g->pos > (g->graph_width-1) ){
894
g->pos=g->graph_width-1;
896
g->scroll = g->repeat;