package uk.ac.ed.epcc.mountain; import java.awt.*; import java.lang.Math; public class Artist{ final double SIDE=1.0; final int BLACK=0; final int WHITE=1; final int SEA_LIT=2; final int SEA_UNLIT=3; final int SKY=4; final int BAND_BASE=5; final int BAND_SIZE=80; final int N_BANDS=3; final int DEF_COL=(BAND_BASE + (N_BANDS * BAND_SIZE)); final int MIN_COL=(BAND_BASE + (N_BANDS * 2)); private final boolean debug=false; private boolean initialised=false; private int base=0; Mountain m; private double shadow[]; private double a_strip[], b_strip[]; int graph_height=768; /* height of display */ int graph_width=1024 ; /* width of display */ Graphics g; Color[] clut; int n_col=DEF_COL; int band_size=BAND_SIZE; public int width; /* width of terrain strip */ public float ambient=0.3F; /* level of ambient light */ public double contrast=1.0; /* contrast, * increases or decreases effect of cosine rule */ public double contour=0.3; public double vfract=0.6; /* relative strength of vertical light relative * to the main light source */ public double altitude=2.5; public double distance=4.0; public double phi=(40.0 * Math.PI )/180.0;/* angle of the light (vertical plane)*/ public double alpha=0.0; /* angle of the light (horizontal plane) * must have -pi/4 < alpha < pi/4 */ public double base_shift=0.5; /* offset from calcalt to artist coordinates */ public double sealevel=0.0; public double stretch=0.6; /* vertical stretch */ public boolean draw_map=false; public boolean reflec=true; public int repeat=20; int pos=0; public int scroll=0; private double shift; private double varience; private double delta_shadow; private double shadow_slip; private double shadow_register; private double cos_phi; private double sin_phi; private double tan_phi; private double x_fact; private double y_fact; private double vangle; private double vscale; private double tan_vangle; private double viewpos; /* position of viewpoint */ private double viewheight; /* height of viewpoint */ private double focal; private double vstrength; /* strength of vertical light source */ private double lstrength; /* strength of vertical light source */ public Artist(int width,int height, Graphics gg, Mountain mm){ m=mm; g=gg; graph_width=width; graph_height=height; if( debug ) System.out.println("Artist is go"); } /* {{{ set_clut*/ private void set_clut(){ int band, shade; float red,green,blue,top,bot; float rb[] = { 0.450F, 0.600F, 1.000F }; float gb[] = { 0.500F, 0.600F, 1.000F }; float bb[] = { 0.333F, 0.000F, 1.000F }; if( debug ) System.out.println("set_clut called"); clut = new Color[n_col]; clut[BLACK] = Color.black; clut[WHITE] = Color.white; clut[SKY] = new Color(0.404F,0.588F,1.000F); clut[SEA_LIT] = new Color(0.000F,0.500F,0.700F); clut[SEA_UNLIT] = new Color(0.000F, (float) ((ambient+(vfract/(1.0+vfract)))*0.500), (float) ((ambient+(vfract/(1.0+vfract)))*0.700)); /* max_col can over-rule band_size */ while( (BAND_BASE +band_size*N_BANDS) > n_col ) { band_size--; } for( band=0 ; band 1.0 ) red = 1.0F; /* }}} */ /* {{{ set green */ top = gb[band]; bot = ambient * top; green = bot + ((shade * (top - bot))/(band_size-1)); if( green < 0 ) green = 0.0F; if( green > 1.0 ) green = 1.0F; /* }}} */ /* {{{ set blue */ top = bb[band]; bot = ambient * top; blue = bot + ((shade * (top - bot))/(band_size-1)); if( blue < 0 ) blue = 0.0F; if( blue > 1.0 ) blue = 1.0F; /* }}} */ clut[BAND_BASE + (band*band_size) + shade]=new Color(red,green,blue); } } } /* }}} */ /* {{{ extract*/ private double[] extract(){ double[] res; int i; if( debug ) System.out.println("extract called"); res = m.next_strip(); for(i=0;i= 0 ){ pos=0; }else{ pos=graph_width-1; } /* use first set of heights to set shadow value */ shadow = extract(); a_strip = extract(); b_strip = extract(); initialised=true; } /* }}} */ /* {{{ get_col*/ private int get_col(double p, double p_minus_x, double p_minus_y, double shadow){ double delta_x, delta_y; double delta_x_sqr, delta_y_sqr; double hypot_sqr; double norm, dshade; double effective; int index; int band, shade; /* {{{ if underwater*/ if ( p < sealevel ) { if( shadow > sealevel ) { return( SEA_UNLIT ); }else{ return( SEA_LIT ); } } /* }}} */ /* * We have three light sources, one slanting in from the left * one directly from above and an ambient light. * For the directional sources illumination is proportional to the * cosine between the normal to the surface and the light. * * The surface contains two vectors * ( 1, 0, delta_x ) * ( 0, 1, delta_y ) * * The normal therefore is parallel to * ( -delta_x, -delta_y, 1)/sqrt( 1 + delta_x^2 + delta_y^2) * * For light parallel to ( cos_phi, 0, -sin_phi) the cosine is * (cos_phi*delta_x + sin_phi)/sqrt( 1 + delta_x^2 + delta_y^2) * * For light parallel to ( cos_phi*cos_alpha, cos_phi*sin_alpha, -sin_phi) * the cosine is * (cos_phi*cos_alpha*delta_x + cos_phi*sin_alpha*delta_y+ sin_phi)/sqrt( 1 + delta_x^2 + delta_y^2) * * For vertical light the cosine is * 1 / sqrt( 1 + delta_x^2 + delta_y^2) */ delta_x = p - p_minus_x; delta_y = p - p_minus_y; delta_x_sqr = delta_x * delta_x; delta_y_sqr = delta_y * delta_y; hypot_sqr = delta_x_sqr + delta_y_sqr; norm = Math.sqrt( 1.0 + hypot_sqr ); /* {{{ calculate effective height */ effective = (p + (varience * contour * (1.0/ ( 1.0 + hypot_sqr)))); /* }}} */ /* {{{ calculate colour band. */ band = (int )(( effective / varience) * (double)N_BANDS); if ( band < 0 ) { band = 0; } if( band > (N_BANDS - 1)) { band = (N_BANDS -1); } index = (BAND_BASE + (band * band_size)); /* }}} */ /* {{{ calculate the illumination stength*/ /* * add in a contribution for the vertical light. The normalisation factor * is applied later * */ dshade = vstrength; if( p >= shadow ) { /* * add in contribution from the main light source */ /* dshade += ((double) lstrength * ((delta_x * cos_phi) + sin_phi));*/ dshade += ((double) lstrength * ((delta_x * x_fact) + (delta_y * y_fact) + sin_phi)); } /* divide by the normalisation factor (the same for both light sources) */ dshade /= norm; /* }}} */ /* {{{ calculate shading */ /* dshade should be in the range 0.0 -> 1.0 * if the light intensities add to 1.0 * now convert to an integer */ shade = (int)(dshade * (double) band_size); if( shade > (band_size-1)) { shade = (band_size-1); } /* {{{ if shade is negative then point is really in deep shadow */ if( shade < 0 ) { shade = 0; } /* }}} */ /* }}} */ index += shade; return(index); } /* }}} */ /* {{{ makemap*/ synchronized int[] makemap(){ int[] res= new int[width]; int i; if( debug ) System.out.println("makemap called"); res[0] = BLACK; for(i=1 ; i last ) { /* get the colour of this point, the front strip should be black */ if( i==0 ) { col = BLACK; }else{ col = get_col(b_strip[i],a_strip[i],b_strip[i-1],shadow[i]); } if( coord > graph_height ) { coord = graph_height; } for(;last0;i--) { if(map[i] < BAND_BASE) { /* {{{ stipple water values*/ for(j=last_bottom;j<=last_top;j++) { res[j]=last_col; } last_col=map[i]; /* invalidate strip so last stip does not exist */ last_bottom=graph_height; last_top= -1; /* fill in water values */ coord=1+project(i,sealevel); for(j=0;j last_top) { last_top=top; } if( bottom < last_bottom) { last_bottom=bottom; } }else{ if(top < last_top) { for(j=top+1;j<=last_top;j++) { res[j]=last_col; } } if(bottom > last_bottom) { for(j=last_bottom;j (graph_height-1)) { pos = graph_height-1; } else if( pos < 0 ) { pos = 0; } return( pos ); } /* }}} */ /* {{{ next_col*/ private int []next_col () { int res[]; int i,offset=0; /* {{{ update strips */ if(! draw_map) { if(reflec) { res = mirror(); }else{ res = camera(); } }else{ res = makemap(); } a_strip=b_strip; b_strip = extract(); /* }}} */ /* {{{ update the shadows*/ /* shadow_slip is the Y component of the light vector. * The shadows can only step an integer number of points in the Y * direction so we maintain shadow_register as the deviation between * where the shadows are and where they should be. When the magnitude of * this gets larger then 1 the shadows are slipped by the required number of * points. * This will not work for very oblique angles so the horizontal angle * of illumination should be constrained. */ shadow_register += shadow_slip; if( shadow_register >= 1.0 ) { /* {{{ negative offset*/ while( shadow_register >= 1.0 ) { shadow_register -= 1.0; offset++; } for(i=width-1 ; i>=offset ; i--) { shadow[i] = shadow[i-offset]-delta_shadow; if( shadow[i] < b_strip[i] ) { shadow[i] = b_strip[i]; } /* {{{ stop shadow at sea level */ if( shadow[i] < sealevel ) { shadow[i] = sealevel; } /* }}} */ } for(i=0;i 0 ){ g.copyArea(dist ,0,graph_width-dist,graph_height,-dist,0); blank_region(graph_width-dist,0,graph_width,graph_height); }else{ g.copyArea(0 ,0,graph_width-dist,graph_height,-dist,0); blank_region(0,0,-dist,graph_height); } } /* }}} */ /* {{{ plot_pixel */ private void plot_pixel(int x, int y, int col){ if( clut[col] == null ){ System.out.println("Bad color"); System.exit(1); } // System.out.println(x); // System.out.println(y); // System.out.println(col); g.setColor(clut[col]); //g.setColor(clut[y%n_col]); g.drawLine(x,y,x,y); } /* }}} */ /* {{{ plot_column*/ synchronized public void plot_column() { int l[]; int j; int mapwid; if( ! initialised ){ init_artist_variables(); } if( debug ) System.out.println("plot_column called"); /* blank if we are doing the full window */ if( repeat >= 0){ if(pos == 0){ blank_region(0,0,graph_width,graph_height); } }else{ if( pos == graph_width-1){ blank_region(0,0,graph_width,graph_height); } } if( scroll != 0 ){ scroll_screen(scroll); } l = next_col(); if( draw_map ) { if( graph_height > width ){ mapwid=width; }else{ mapwid=graph_height; } for( j=0 ;j<(graph_height-mapwid); j++) { plot_pixel(pos,((graph_height-1)-j),BLACK); } for(j=0; j=0 ){ pos++; if(pos >= graph_width) { pos -= repeat; if( pos < 0 || pos > graph_width-1 ) { pos=0; }else{ scroll = repeat; } } }else{ pos--; if( pos < 0 ){ pos -= repeat; if( pos < 0 || pos > (graph_width-1) ){ pos=graph_width-1; }else{ scroll = repeat; } } } } /* }}} */ }