Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

ppm_draw.c

Go to the documentation of this file.
00001 
00007 #include <stdio.h>
00008 #include <stdlib.h>
00009 #include <ctype.h>
00010 #include <string.h>
00011 #include <math.h>
00012 #include <vector>
00013 using std::vector;
00014 
00015 #include "ipc.h"
00016 #include "cmdparam.h"
00017 
00018 #ifndef NO_INPUTS
00019 #include "inputs.h"  /* Only used for coloring retina pixels by obj angle */
00020 #endif
00021 
00022 #include "analyze.h"
00023 #include "globals.h"
00024 #include "lissom.h"
00025 
00026 #include "ppm_draw.h"
00027 
00028 
00029 /******************************************************************************/
00030 /* Defines and typedefs                                                       */
00031 /******************************************************************************/
00032 
00033 /* For XWindows compatibility, RGB_value should be 'short' and 
00034    HIGHEST_COLOR_LEVEL should be 65535, but values of 'char' and
00035    255 respectively produce the same result here and use much less memory.
00036 */
00037 
00039 typedef unsigned char RGB_value; 
00040 
00042 #define HIGHEST_COLOR_LEVEL 255
00043 
00045 #define BadPixel  ((char)(1<<5))
00046 
00047 /* RGB color value; borrowed from XWindows */
00048 typedef struct 
00049 {
00050   RGB_value red;
00051   RGB_value green;
00052   RGB_value blue;
00053   char flags; 
00054 } XColor;
00055 
00057 #define NOHUE -1.0
00058 
00059 
00060 /* Possible values for cortex plot hue, etc.  (ppm_*_subplot)
00061 
00062    For activity plots, luminance (aka value) encodes activity level, but 
00063    the hue can encode other information if needed 
00064 */
00065 
00066 #define PPM_RYW             0    
00067 #define PPM_GRAYSCALE       1    
00068 #define PPM_ORIENTATION     2    
00069 #define PPM_OCULARDOMINANCE 3    
00070 #define PPM_SINGLEHUE       4    
00071 #define PPM_NONE            5    
00072 #define PPM_SELECTIVITY     6    
00073 #define PPM_MAX_SUBPLOT     6    /*  Maximum number defined above */
00074 
00075 
00076 /* Possible values for ppm_weight_scale_type */
00077 #define PPM_WTSCALE_FIXED        0
00078 #define PPM_WTSCALE_INHIB        1
00079 #define PPM_WTSCALE_COUNT        2
00080 #define PPM_WTSCALE_ACTIVE       3
00081 #define PPM_WTSCALE_PEAKRELATIVE 4
00082 #define PPM_WTSCALE_RELATIVE     5
00083 #define PPM_MAX_WTSCALE          5
00084 
00085 
00095 typedef int (*ppm_inside_test_ptr)
00096 (
00097  int i, int j,
00098  int int1, int int2, double float1, double float2,
00099  void *otherparams
00100 );
00101 
00102 
00103 
00104 /* Crop to the given (inclusive) range */
00105 #ifndef CROP
00106 #define CROP(lower,upper,x) (MAX((lower),MIN((upper),(x))));
00107 #endif
00108 
00109 
00110 
00111 /******************************************************************************/
00112 /* Parameters                                                                 */
00113 /******************************************************************************/
00114 
00115 
00116 double ppm_master_scale = 1.0;
00117 double ppm_retina_scale = Uninitialized;
00118 double ppm_cortex_scale = Uninitialized;
00119 
00120 int    ppm_border = MAX(2,(int)(NMAX*0.03));
00121 
00122 double ppm_bg_R = Uninitialized;
00123 double ppm_bg_G = Uninitialized;
00124 double ppm_bg_B = Uninitialized;
00125 
00126 double ppm_luminance_multiplier        =   1;
00127 double ppm_lateral_multiplier          = 700;
00128 double ppm_afferent_multiplier         = 100;
00129 double ppm_selectivity_multiplier      =   1;
00130 double ppm_line_selectivity_multiplier =  50;
00131 double ppm_activity_luminance_offset   =   0;
00132 
00133 int    ppm_weight_scale_type           = PPM_WTSCALE_PEAKRELATIVE;
00134 int    ppm_weight_scale_init_count     = True;
00135 int    ppm_weight_scale_max_count      = True;
00136 
00137 int    ppm_neuron_skip_aff = 1;
00138 
00139 int    ppm_paper_based_colors = False;   
00140 int    ppm_paper_based_retina = Uninitialized;
00141 int    ppm_outline_weights    = True;
00142 int    ppm_outline_retina     = True;
00143 int    ppm_always_mark_neuron = False;
00144 int    ppm_retina_edge_buffer_suppress=False;
00145 
00146 int    ppm_activity_subplot          = PPM_RYW;
00147 int    ppm_retina_subplot            = PPM_RYW;
00148 int    ppm_lateral_subplot           = PPM_RYW;
00149 int    ppm_afferent_subplot          = PPM_RYW;
00150 int    ppm_orientation_subplot       = PPM_NONE;
00151 int    ppm_ocular_dominance_subplot  = PPM_RYW;
00152 int    ppm_line_orientation_subplot  = PPM_SELECTIVITY;
00153 
00154 double ppm_subplot_hue               = NOHUE;
00155 
00156 int    ppm_line_plots          = False;
00157 int    ppm_shaded_plots        = True;
00158 int    ppm_always_draw_point   = True;
00159 int    ppm_neuron_skip_line    = 1; /* Could implement similar quantity _color for color plots */
00160 int    ppm_neuron_ioffset_line = 0;
00161 int    ppm_neuron_joffset_line = 0;
00162 int    ppm_interior_border     = 1;
00163 int    ppm_interior_outline    = False;
00164 
00165 double ppm_line_R  = Uninitialized;
00166 double ppm_line_G  = Uninitialized;
00167 double ppm_line_B  = Uninitialized;
00168 
00169 int    ppm_start_i = Uninitialized; 
00170 int    ppm_start_j = Uninitialized; 
00171 int    ppm_end_i   = Uninitialized; 
00172 int    ppm_end_j   = Uninitialized; 
00173 
00174 int    ppm_start_x = Uninitialized; 
00175 int    ppm_start_y = Uninitialized; 
00176 int    ppm_end_x   = Uninitialized; 
00177 int    ppm_end_y   = Uninitialized; 
00178 
00179 int    ppm_ui      = Uninitialized;
00180 int    ppm_uj      = Uninitialized;
00181 
00182 double ppm_weight_threshold = DEATH_FLAG;
00183 int    ppm_outline_width    = 1;
00184 double ppm_reference_angle  = 0;
00185 
00186 double ppm_value_range      = 0.6;
00187 double ppm_value_offset     = 0.1;
00188 
00189 int    ppm_minimal_outline  = False;
00190 
00191 double ppm_histo_percentage_range      = 25;
00192 double ppm_histo_scale_factor          = 0.25;
00193 int    ppm_histograms                  = True;
00194 
00195 char   ppm_image_viewer[MAXFILENAMELENGTH]="xv -expand 4 -raw";
00196 char   ppm_remote_viewer[MAXFILENAMELENGTH]="anytoascii";
00197 int&   ppm_spawn_viewer=spawn_viewer;
00198 int    ppm_spawn_independent_viewers=True; 
00199 
00200 int    ppm_color_keys=Uninitialized;
00201 int    ppm_color_key_num=13;
00202 
00203 int    ppm_combine_alternate_eyes = False;
00204 
00205 /******************************************************************************/
00206 /* Global variables                                                           */
00207 /******************************************************************************/
00208 
00209 XColor colors[HIGHEST_COLOR_LEVEL+1];
00210 XColor image[IMG_SIZE_X][IMG_SIZE_Y]; 
00212 XColor ppm_background_color;
00213 XColor ppm_foreground_color;
00214 
00215 int ncolors=252;
00216 int ppm_height = 0;                   
00217 
00218 /* Constant */
00219 const XColor Black = {0,0,0,0};
00220 const XColor White = {HIGHEST_COLOR_LEVEL,HIGHEST_COLOR_LEVEL,HIGHEST_COLOR_LEVEL,0};
00221 const XColor Blue  = {0,0,HIGHEST_COLOR_LEVEL,0};
00222 
00223 
00224 
00225 
00226 /******************************************************************************/
00227 /* Prototypes for private functions                                           */
00228 /******************************************************************************/
00229 
00230 void   BhamXDrawLine_PointsAlongX(int xA, int yA, int xB, int yB, int direction, XColor color, int thickness);
00231 void   BhamXDrawLine_PointsAlongY(int xA, int yA, int xB, int yB, int direction, XColor color, int thickness);
00232 int    canExtendWidthTo(int width);
00233 void   compute_weight_scale( int ui, int uj, int max_coord, int circular_wts,
00234                              int radius, int ar_width,
00235                              void* weights, int weights_are_lat,
00236                              double* factor, double* offset );
00237 void   create_colormap_RGB( XColor colormap[HIGHEST_COLOR_LEVEL+1], int numcolors );
00238 int    draw_box_outline( XColor color, int xl, int xh, int yl, int yh, int width );
00239 int    draw_box_outline_on_cortex( double centeri, double centerj, double radius, int left_offset);
00240 int    draw_box_outline_on_retina( double centeri, double centerj, double radius, int left_offset);
00241 void   draw_histogram_bin( double value, int j, int reset_values, int draw_zero, int left_offset);
00242 void   draw_horizontal_line( XColor color, int l_i, int h_i, int j );
00243 int    draw_outline_on_cortex( int ui, int uj, int radius, int left_offset, ppm_inside_test_ptr testfn);
00244 int    draw_outline_on_retina( int ui, int uj, int radius, int left_offset, ppm_inside_test_ptr testfn);
00245 void   draw_rectangle ( XColor color, int upper_left_x, int upper_left_y, int x_pixels,     int y_pixels );
00246 void   draw_retina_unit( XColor color, int x, int y, int reset_values, int left_offset);
00247 void   draw_cortex_unit( XColor color, int i, int j, int reset_values, int left_offset);
00248 void   draw_vertical_line(  XColor color, int i, int l_j, int h_j );
00249 void   hsv2rgb( double h, double s, double v, XColor *color );
00250 int    inhWeightAbove( int i, int j, int ui,      int uj,      double radius, double threshold, void *dummyv );
00251 int    isAlwaysInside( int i, int j, int int1,    int int2,    double float1, double float2,    void *dummyv );
00252 int    isInsideCircle( int i, int j, int centeri, int centerj, double radius, double dummyf,    void *dummyv );
00253 void   ppm_clear_image(int bg_R, int bg_G, int bg_B);
00254 void   ppm_draw_line(int x_1, int y_1, int x_2, int y_2, XColor color, int thickness);
00255 void   ppm_draw_line_at_angle(int cx, int cy, double angle, double length, double width, XColor color);
00256 int    ppm_draw_od_pref(double od_pref_array[NMAX][NMAX], int left_offset);
00257 int    ppm_draw_or_pref_line(int or_preferences[NMAX][NMAX], double or_selectivities[NMAX][NMAX], int left_offset);
00258 void   ppm_draw_pixel(int xi, int yi, XColor color);
00259 XColor ppm_get_drawing_color(int lowi, int highi, int lowj, int highj, XColor set_color);
00260 int    ppm_init_cortex_plot(int left_offset);
00261 int    ppm_init_retina_plot(int left_offset);
00262 int    ppm_init_histogram_plot(int symmetric, int left_offset);
00263 XColor ppm_lookup_color( int i, int j, double scaled_value, int subplot, int paper_based=Uninitialized);
00264 int    ppm_scaled_cortex_height_offset(void);
00265 int    ppm_scaled_cortex_plot_height( void );
00266 int    ppm_scaled_cortex_plot_width( void );
00267 int    ppm_scaled_cortex_unit_pixels( void );
00268 int    ppm_scaled_histogram_bin_pixels( int num_bins );
00269 int    ppm_scaled_histogram_height_offset(int num_bins );
00270 int    ppm_scaled_histogram_plot_height(int num_bins );
00271 int    ppm_scaled_histogram_plot_width( int num_bins, double scale );
00272 int    ppm_scaled_retina_height_offset(void);
00273 int    ppm_scaled_retina_plot_height( void );
00274 int    ppm_scaled_retina_plot_width( void );
00275 int    ppm_scaled_retina_unit_pixels( void );
00276 void   ppm_write_header( FILE *file, const char *comments, int width, int height, int max_val );
00277 void   rgb2hsv(XColor c, double *h, double *s, double *v );
00278 int    ppm_write_pixel( FILE *file, XColor pixel, int max_val );
00279 template<class T>
00280 int    ppm_write_image_to_file( T imagedata, const char* filename, const char *comments, int width, int height, int max_val );
00281 
00282 int    draw_outline(XColor color,
00283                     int lowi, int highi, int lowj, int highj, int starti, int startj,
00284                     int left_offset, int height_offset, int npixels_perunit, int outline_width,
00285                     ppm_inside_test_ptr testfn, int int1, int int2, double float1, double
00286                     float2, void *otherparams );
00287 
00288 
00289 
00290 /******************************************************************************/
00291 /* Initialization hook                                                        */
00292 /******************************************************************************/
00293 
00294 void  ppm_init_hook( void )
00295 {
00296   /* Constant values */
00297   CONST_I(PPM_RYW,            PPM_RYW,            False,
00298           "Subplot type where the primary plot is encoded using a special colormap.\n"
00299           "When using this with ppm_paper_based_colors==False, inactive units\n"
00300           "are colored black, moderately active units are red, strongly activated\n"
00301           "units are colored yellow, and maximally activated units are colored\n"
00302           "white.  Such a color scheme emphasizes peaks, particularly on video\n"
00303           "terminals.  The colors are reversed when ppm_paper_based_colors is\n"
00304           "true, which isn't quite as effective on paper; in that case PPM_Grayscale\n"
00305           "is a better choice.");
00306 
00307   CONST_I(PPM_Grayscale,      PPM_GRAYSCALE,      False,
00308           "Subplot type where monochrome luminance encodes value (e.g. activity).\n"
00309           "White represents maximum unless ppm_paper_based_colors is true.");
00310   
00311   CONST_I(PPM_Orientation,    PPM_ORIENTATION,    False,
00312           "Subplot type where the primary plot is represented by saturation or luminance\n"
00313           "(depending on whether ppm_paper_based_colors is True or False, respectively),\n"
00314           "while the hue encodes orientation preference.\n\n"
00315           
00316           "For retinal plots, each input pixel is colored by the nominal orientation of\n"
00317           "the object with the highest intensity value at that pixel.  This value is\n"
00318           "not guaranteed to match the actual orientation judgment of a human observer,\n"
00319           "since e.g. a composite object will return its own orientation, even though\n"
00320           "the local orientation of a subobject may differ.");
00321   
00322   CONST_I(PPM_OcularDominance,PPM_OCULARDOMINANCE,False,
00323           "Subplot type where the primary plot is represented by saturation or luminance\n"
00324           "(depending on whether ppm_paper_based_colors is True or False, respectively),\n"
00325           "while the hue encodes ocular dominance.");
00326 
00327   CONST_I(PPM_SingleHue,      PPM_SINGLEHUE,      False,
00328           "Subplot type where the entire plot uses a hue specified by ppm_subplot_hue.");
00329   
00330   CONST_I(PPM_None,           PPM_NONE,           False,
00331           "Subplot type where, depending upon the type of plot, the primary plot is\n"
00332           "represented by hue or luminance at full strength.");
00333   
00334   CONST_I(PPM_Selectivity,    PPM_SELECTIVITY,    False,
00335           "Used for plots consisting of a primary dimension (e.g. orientation or\n"
00336           "ocular dominance), a selectivity associated with the primary\n"
00337           "dimension, and an optional level (e.g. activity or weight strength;\n"
00338           "assumed 1.0 if none).  The primary dimension is represented by hue,\n"
00339           "while the other two dimensions are represented using luminance and\n"
00340           "saturation.  The level effectively acts as a mask for a plot with the\n"
00341           "selectivity as saturation (ppm_paper_based_colors=False) or luminance\n"
00342           "(ppm_paper_based_colors=True).  For ppm_paper_based_colors=False, the\n"
00343           "mask is implemented simply by using the level as the luminance.  If\n"
00344           "ppm_paper_based_colors=True, the mask is implemented by using the\n"
00345           "level as the saturation and Max(1-scaled_level,selectivity) as the\n"
00346           "luminance.  Both methods should be equivalent; they differ only\n"
00347           "because the color-expressing properties of luminance and saturation\n"
00348           "are opposites.");
00349 
00350   CONST_I(PPM_WtScale_Fixed,  PPM_WTSCALE_FIXED,  False,
00351           "Scale the weights for all neurons by a constant multiplicative factor\n"
00352           "(ppm_afferent_multiplier or ppm_lateral_multiplier), regardless of their\n"
00353           "values or number of connections defined or active.  This makes it simple to\n"
00354           "compare absolute weight values between different plots.  However, it\n"
00355           "obscures differences in weight levels between adjacent units except at\n"
00356           "a single set of parameter values for which the multiplier has been\n"
00357           "chosen to be optimum.");
00358 
00359   CONST_I(PPM_WtScale_Inhib,PPM_WTSCALE_INHIB,False,
00360           "Scale all weights by the number of inhibitory connections.\n"
00361           "This makes obvious the differences in weight strength between excitatory\n"
00362           "and inhibitory connections.");
00363 
00364   CONST_I(PPM_WtScale_Count,PPM_WTSCALE_COUNT,False,
00365           "Scale the weights for a given neuron by the number of connections defined.\n"
00366           "Depending upon the values of  ppm_weight_scale_init_count and\n"
00367           "ppm_weight_scale_max_count, this can make obvious the differences in weight\n"
00368           "value between weights to adjacent neurons or between different neurons or\n"
00369           "between the same neurons over time.");
00370 
00371   CONST_I(PPM_WtScale_Active, PPM_WTSCALE_ACTIVE, False,
00372           "Scale the weights for a given neuron by the number of active (non-dead)\n"
00373           "connections for that neuron.  This makes obvious the differences in\n"
00374           "weight levels between adjacent units, even when many units have been\n"
00375           "pruned, but obscures the increase in weight strength which pruning\n"
00376           "brings about.");
00377 
00378   CONST_I(PPM_WtScale_PeakRelative,PPM_WTSCALE_PEAKRELATIVE,  False,
00379           "Scale the weights for a given neuron so that the actual maximum value\n"
00380           "present is assigned the maximum value representable in a plot, while\n"
00381           "the minimum is set at zero.  This makes local differences in value\n"
00382           "obvious without obscuring the distinction between zero and nonzero values.");
00383 
00384   CONST_I(PPM_WtScale_Relative,PPM_WTSCALE_RELATIVE,  False,
00385           "Scale the weights for a given neuron so that the actual maximum and minimum\n"
00386           "values present are assigned the minimum and maximum values representable in\n"
00387           "a plot.  This always makes obvious the differences between weight values to\n"
00388           "adjacent units, which means that it is easy to use, but it also makes it\n"
00389           "impossible to compare absolute or relative weight values between different\n"
00390           "plots.");
00391 
00392   
00393   
00394   PARAM_I(PARAM_INT,   ppm_activity_subplot,0,PPM_MAX_SUBPLOT,
00395           "Secondary plot (using e.g. the hue value) for activity plots.\n\n"
00396           "Possible settings (starting with PPM_):\n"
00397           "  RYW,Grayscale,OcularDominance,Orientation,Selectivity,SingleHue,None");
00398 
00399   PARAM_I(PARAM_DOUBLE,ppm_activity_luminance_offset,0,1,
00400           "Amount to add to the computed value for activity plots using luminance.\n"
00401           "Normally this is zero, but if set to e.g. 0.25 in combination with a\n"
00402           "ppm_activity_subplot of e.g. orientation, the orientation map can be made\n"
00403           "out dimly even in areas where there is no activity.  This can help relate\n"
00404           "the activity bubbles to their location on the map.");
00405   
00406   PARAM_I(PARAM_BOOL,  ppm_always_mark_neuron,False,True,
00407           "If true, always draw an outline around the neuron that is being plotted\n"
00408           "in e.g. a weight plot, and also draw an outline around the RF center of\n"
00409           "that neuron on the retina.  Ordinarily the neuron is marked only when\n"
00410           "ppm_outline_weights is true and only in the inhibitory plots, since the\n"
00411           "center is usually clear on the smaller areas of the afferent and lateral\n"
00412           "excitatory plots.");
00413   
00414   PARAM_L(PARAM_DOUBLE,ppm_master_scale,Uninitialized,
00415           "Ordinarily, this parameter will be set to 1.0, which means to automatically\n"
00416           "compute the minimum plot sizes that will show the full cortex and retina,\n"
00417           "regardless of the settings of ppm_cortex_scale, ppm_retina_scale,\n"
00418           "ppm_start_x,y,i,j, etc.  If higher-resolution plots are desired, it can be\n"
00419           "set to a larger value, e.g. 2.0 to make all plots twice the normal size.\n"
00420           "Note that larger plots are not strictly multiples of the smaller ones,\n"
00421           "since the higher the resolution, the finer a line can be used for details\n"
00422           "such as outlines.  To handle the scaling yourself, set this parameter to\n"
00423           "Uninitialized and set the aforementioned parameters as you desire.  Note\n"
00424           "that like the other scaling parameters, it is of type double but\n"
00425           "currently only integer multiples are actually used.");
00426   
00427   PARAM_L(PARAM_DOUBLE,ppm_afferent_multiplier,0,
00428           "Amount by which to multiply afferent weight value to get luminance value for\n"
00429           "plots.");
00430   
00431   PARAM_I(PARAM_INT,   ppm_afferent_subplot,0,PPM_MAX_SUBPLOT,
00432           "Secondary plot (using e.g. the hue value) for afferent weight plots.\n\n"
00433           "Possible settings (starting with PPM_):\n"
00434           "  RYW,Grayscale,OcularDominance,Orientation,Selectivity,SingleHue,None");
00435   
00436   PARAM_I(PARAM_BOOL,  ppm_always_draw_point,False,True,
00437           "Whether ppm_draw_line_at_angle() always draws at least one point, e.g. when \n"
00438           "plotting orientation using lines.");
00439   
00440   PARAM_I(PARAM_DOUBLE,ppm_bg_B,Uninitialized,1,
00441           "Blue component of color of background and border areas; default is white or\n"
00442           "black depending on ppm_paper_based_colors.");
00443   
00444   PARAM_I(PARAM_DOUBLE,ppm_bg_G,Uninitialized,1,
00445           "Green component of color of background and border areas; default is white\n"
00446           "or black depending on ppm_paper_based_colors.");
00447   
00448   PARAM_I(PARAM_DOUBLE,ppm_bg_R,Uninitialized,1,
00449           "Red component of color of background and border areas; default is white or\n"
00450           "black depending on ppm_paper_based_colors.");
00451   
00452   PARAM_L(PARAM_INT,   ppm_border,0,
00453           "Number of pixels between subsequent plots in the same image; defaults to 3%%\n"
00454           "of the image size, subject to a minimum of two.");
00455   
00456   PARAM_I(PARAM_INT,  ppm_color_key_num,0,or_num_angles,
00457           "The number of oriented bars to use in a color key when ppm_color_keys is true.");
00458   
00459   PARAM_I(PARAM_BOOL,  ppm_color_keys,Uninitialized,True,
00460           "Whether or not to draw a color key after a color plot.  By default will draw\n"
00461           "one if color-coded orientation was used.");
00462   
00463   PARAM_I(PARAM_BOOL,  ppm_combine_alternate_eyes,False,True,
00464           "If there is more than one eye (and an even number total), plot them by twos\n"
00465           "where the second half of each pair is subtracted from the first.  This is\n"
00466           "useful for e.g. ON and OFF cell layers created using Blur_DoGGoD.  This\n"
00467           "option will need more work to be very useful except with a very limited\n"
00468           "range of absolute unnormalized weights, since right now weights are\n"
00469           "normalized independently between the two subtracted values (which has\n"
00470           "poor results.)");
00471   
00472   PARAM_L(PARAM_DOUBLE,ppm_cortex_scale,Uninitialized,
00473           "Scaling factor when plotting cortex.  Defaults to autoscale to match retina.\n"
00474           "At the moment only integer values are used, to ensure that plots always devote\n"
00475           "the same number of pixels to each neuron (to prevent confusion).  As a result\n"
00476           "plots will not be shown if the scale is less than 1.  This restriction may be\n"
00477           "relaxed in the future.");
00478   
00479   PARAM_I(PARAM_INT,   ppm_end_i,Uninitialized,NMAX,
00480           "By default, the entire cortex is plotted, but when ppm_master_scale is\n"
00481           "Uninitialized, this parameter is one higher than the last pixel plotted\n"
00482           "in the X direction.");
00483   
00484   PARAM_I(PARAM_INT,   ppm_end_j,Uninitialized,NMAX,
00485           "By default, the entire cortex is plotted, but when ppm_master_scale is\n"
00486           "Uninitialized, this parameter is one higher than the last pixel plotted\n"
00487           "in the Y direction.");
00488   
00489   PARAM_I(PARAM_INT,   ppm_end_x,Uninitialized,RNMAX,
00490           "By default, the entire retina is plotted, but when ppm_master_scale is\n"
00491           "Uninitialized, this parameter is one higher than the last pixel plotted\n"
00492           "in the X direction.");
00493   
00494   PARAM_I(PARAM_INT,   ppm_end_y,Uninitialized,RNMAX,
00495           "By default, the entire retina is plotted, but when ppm_master_scale is\n"
00496           "Uninitialized, this parameter is one higher than the last pixel plotted\n"
00497           "in the Y direction.");
00498   
00499   PARAM_L(PARAM_DOUBLE,ppm_histo_scale_factor,0,
00500           "Ratio applied to the size of a cortex plot to get the width of an accompanying\n"
00501           "histogram.");
00502   
00503   PARAM_I(PARAM_DOUBLE,ppm_histo_percentage_range,0,100,
00504           "Range of an activity histogram, peak to peak, as a percentage.");
00505   
00506   PARAM_I(PARAM_BOOL,  ppm_histograms,False,True,
00507           "Whether to include activity histograms with plots, if orientation data is\n"
00508           "available.  Doesn't currently support OD histograms, but could do so easily.");
00509   
00510   PARAM_L(PARAM_INT,   ppm_interior_border,0,
00511           "Number of pixels to skip between internal image features, e.g. orientation\n"
00512           "lines.");
00513   
00514   PARAM_I(PARAM_BOOL,  ppm_interior_outline,False,True,
00515           "Whether or not to outline smaller maps which appear inside of larger ones,\n"
00516           "e.g. for plot_afferent_weight_map");
00517   
00518   PARAM_I(PARAM_STRING,ppm_image_viewer,0,MAXFILENAMELENGTH,
00519           "External program to spawn when an image is created, ppm_spawn_viewer is\n"
00520           "true, and running_remotely is false.");
00521   
00522   PARAM_L(PARAM_DOUBLE,ppm_lateral_multiplier,0,
00523           "Amount by which to multiply lateral weight value to get luminance value for\n"
00524           "plots.");
00525   
00526   PARAM_I(PARAM_INT,   ppm_lateral_subplot,0,PPM_MAX_SUBPLOT,
00527           "Secondary plot (using e.g. the hue value) for lateral weight plots.\n\n"
00528           "Possible settings (starting with PPM_):\n"
00529           "  RYW,Grayscale,OcularDominance,Orientation,Selectivity,SingleHue,None");
00530   
00531   PARAM_I(PARAM_DOUBLE,ppm_line_B,Uninitialized,1,
00532           "Blue component of color of lines to be drawn on an image;\n"
00533           "default is dynamically chosen to have good contrast with the area of the\n"
00534           "image being overwritten.");
00535   
00536   PARAM_I(PARAM_DOUBLE,ppm_line_G,Uninitialized,1,
00537           "Green component of color of lines to be drawn on an image;\n"
00538           "default is dynamically chosen to have good contrast with the area of the\n"
00539           "image being overwritten.");
00540   
00541   PARAM_I(PARAM_DOUBLE,ppm_line_R,Uninitialized,1,
00542           "Red component of color of lines to be drawn on an image; default is dynamically\n"
00543           "chosen to have good contrast with the area of the image being overwritten.");
00544   
00545   PARAM_I(PARAM_INT,   ppm_line_orientation_subplot,0,PPM_MAX_SUBPLOT,
00546           "Secondary plot (using the line length of orientation lines) for orientation\n"
00547           "map plots.\n\n"
00548           "Possible settings (starting with PPM_): Selectivity,None");
00549   
00550   PARAM_I(PARAM_BOOL,  ppm_line_plots,False,True,
00551           "Whether to draw line plots (e.g. for orientation or weights) as opposed to \n"
00552           "(or in addition to) shaded plots.");
00553   
00554   PARAM_L(PARAM_DOUBLE,ppm_line_selectivity_multiplier,0,
00555           "Amount by which to multiply orientation selectivity to get line length for\n"
00556           "plots.");
00557   
00558   PARAM_L(PARAM_DOUBLE,ppm_luminance_multiplier,0,
00559           "Amount by which to multiply activity to get luminance value for plots.");
00560   
00561   PARAM_I(PARAM_BOOL,  ppm_minimal_outline,False,True,
00562           "Whether to outline the active lateral weights only, or all of those\n"
00563           "currently allowed.  If true, outlines only those units with weights greater\n"
00564           "than ppm_weight_threshold.");
00565   
00566   PARAM_L(PARAM_INT,   ppm_neuron_ioffset_line,0,
00567           "Horizontal offset for skipping when ppm_neuron_skip_line is not 1.  This \n"
00568           "determines where the cycles start.  It is usually zero but can be set \n"
00569           "to some other value X such that (0 < X < ppm_neuron_skip_line) to ensure\n"
00570           "that a particular neuron is actually drawn.");
00571   
00572   PARAM_L(PARAM_INT,   ppm_neuron_joffset_line,0,
00573           "Vertical offset for skipping when ppm_neuron_skip_line is not 1.  This \n"
00574           "determines where the cycles start.  It is usually zero but can be set \n"
00575           "to some other value X such that (0 < X < ppm_neuron_skip_line) to ensure\n"
00576           "that a particular neuron is actually drawn.");
00577   
00578   PARAM_L(PARAM_INT,   ppm_neuron_skip_line,1,
00579           "Draw every nth neuron only, for line plots.");
00580   
00581   PARAM_L(PARAM_INT,   ppm_neuron_skip_aff,1,
00582           "Draw every nth neuron only, for afferent weight map plots.");
00583   
00584   PARAM_I(PARAM_INT,   ppm_ocular_dominance_subplot,0,PPM_MAX_SUBPLOT,
00585           "Type of plot for ocular dominance map plots.\n\n"
00586           "Possible settings (starting with PPM_):\n"
00587           "  RYW,Grayscale,Selectivity,SingleHue,None");
00588   
00589   PARAM_I(PARAM_INT,   ppm_orientation_subplot,0,PPM_MAX_SUBPLOT,
00590           "Secondary plot (using e.g. the luminance value) for orientation map plots.\n\n"
00591           "Possible settings (starting with PPM_): Grayscale,Selectivity,None\n"
00592           "See the documentation for those options for more details.");
00593   
00594   PARAM_I(PARAM_BOOL,  ppm_outline_retina,False,True,
00595           "Whether to draw a line around the active area of the retina, i.e. the area\n"
00596           "not included in the retina_edge_buffer region, when retina_edge_buffer is\n"
00597           "nonzero.  See also ppm_retina_edge_buffer_suppress.");
00598   
00599   PARAM_I(PARAM_BOOL,  ppm_outline_weights,False,True,
00600           "Whether to draw a line around the active weight area plotted on maps of the\n"
00601           "cortex.");
00602   
00603   PARAM_L(PARAM_INT,   ppm_outline_width,0,
00604           "Number of pixels to use for intra-picture outlines.");
00605   
00606   PARAM_I(PARAM_BOOL,  ppm_paper_based_colors,False,True,
00607           "Usually, the colors are suitable for video, with black backgrounds and \n"
00608           "luminance encoding the weight or activation level.  For paper plots, this\n"
00609           "parameter can be set to 1 so that white backgrounds are used and saturation\n"
00610           "will encode the level.");
00611   
00612   PARAM_I(PARAM_BOOL,  ppm_paper_based_retina,Uninitialized,True,
00613           "If set to Boolean value other than Uninitialized, whether to consider\n"
00614           "ppm_paper_based_colors true for the retina regardless of its global value.");
00615   
00616   PARAM_I(PARAM_DOUBLE,ppm_reference_angle,0,180,
00617           "When setting luminance for monochrome orientation plots, this will be the\n"
00618           "angle (in degrees) at which luminance is zero.");
00619   
00620   PARAM_I(PARAM_STRING,ppm_remote_viewer,0,MAXFILENAMELENGTH,
00621           "External program to spawn when an image is created, ppm_spawn_viewer is\n"
00622           "true, and running_remotely is true.");
00623   
00624   PARAM_L(PARAM_DOUBLE,ppm_retina_scale,Uninitialized,
00625           "Scaling factor when plotting retina.  Defaults to autoscale to match cortex.\n"
00626           "At the moment only integer values are used, to ensure that plots always devote\n"
00627           "the same number of pixels to each retinal unit (to prevent confusion).  As a\n"
00628           "result plots will not be shown if the scale is less than 1.  This restriction\n"
00629           "may be relaxed in the future.");
00630   
00631   PARAM_I(PARAM_INT,   ppm_retina_subplot,0,PPM_MAX_SUBPLOT,
00632           "Secondary plot (using e.g. the hue value) for retinal activation plots.\n\n"
00633           "Possible settings (starting with PPM_):\n"
00634           "  RYW,Grayscale,Orientation,SingleHue,None");
00635   
00636   PARAM_I(PARAM_BOOL,  ppm_retina_edge_buffer_suppress,False,True,
00637           "If true, will only plot the active area of the retina, i.e. the area\n"
00638           "not included in the retina_edge_buffer region.  See also ppm_outline_retina.");
00639   
00640   PARAM_L(PARAM_DOUBLE,ppm_selectivity_multiplier,0,
00641           "Amount by which to multiply orientation selectivity to get luminance value\n"
00642           "for plots.");
00643   
00644   PARAM_I(PARAM_BOOL,  ppm_shaded_plots,False,True,
00645           "Whether to draw shaded plots, including color and mono plots (e.g. for \n"
00646           "orientation). (Not necessarily exclusive of ppm_line_plots.)");
00647   
00648   PARAM_I(PARAM_BOOL,  ppm_spawn_viewer,Uninitialized,True,
00649           "Alias for spawn_viewer; whether to spawn an external program (specified by\n"
00650           "ppm_image_viewer) to view an image when it is created.");
00651   
00652   PARAM_I(PARAM_BOOL,  ppm_spawn_independent_viewers,False,True,
00653           "When ppm_spawn_viewer is true, whether to spawn for every image, or just\n"
00654           "the first one.");
00655   
00656   PARAM_I(PARAM_INT,   ppm_start_i,Uninitialized,NMAX,
00657           "By default, the entire cortex is plotted, but when ppm_master_scale is\n"
00658           "Uninitialized, this parameter sets the first pixel plotted in the X direction.");
00659   
00660   PARAM_I(PARAM_INT,   ppm_start_j,Uninitialized,NMAX,
00661           "By default, the entire cortex is plotted, but when ppm_master_scale is\n"
00662           "Uninitialized, this parameter sets the first pixel plotted in the Y direction.");
00663   
00664   PARAM_I(PARAM_INT,   ppm_start_x,Uninitialized,RNMAX,
00665           "By default, the entire retina is plotted, but when ppm_master_scale is\n"
00666           "Uninitialized, this parameter sets the first pixel plotted in the X direction.");
00667   
00668   PARAM_I(PARAM_INT,   ppm_start_y,Uninitialized,RNMAX,
00669           "By default, the entire retina is plotted, but when ppm_master_scale is\n"
00670           "Uninitialized, this parameter sets the first pixel plotted in the Y direction.");
00671   
00672   PARAM_I(PARAM_DOUBLE,ppm_subplot_hue,Uninitialized,1,
00673           "The hue (in terms of Hue, Saturation, and Value) to use when a subplot is\n"
00674           "set to PPM_SingleHue.");
00675   
00676   PARAM_I(PARAM_INT,   ppm_ui,Uninitialized,NMAX,
00677           "Denotes x-coordinate of unit to graph as a secondary plot, e.g. as lateral\n"
00678           "weights on top of another plot.");
00679   
00680   PARAM_I(PARAM_INT,   ppm_uj,Uninitialized,NMAX,
00681           "Denotes y-coordinate of unit to graph as a secondary plot, e.g. as lateral\n"
00682           "weights on top of another plot.");
00683   
00684   PARAM_I(PARAM_DOUBLE,ppm_value_offset,0,1,
00685           "Determines the offset of the range of values used for HSV coordinates.\n"
00686           "Not always used; check to make sure for a particular plot.");
00687   
00688   PARAM_I(PARAM_DOUBLE,ppm_value_range,0,1,
00689           "Determines the range of Values used for HSV coordinates.  Not always used;\n"
00690           "check to make sure for a particular plot.");
00691   
00692   PARAM_N(PARAM_DOUBLE,ppm_weight_threshold,
00693           "Strength of weight to consider active for weight plots.");
00694 
00695   PARAM_I(PARAM_BOOL,  ppm_weight_scale_init_count,False,True,
00696           "When plotting weights on a scale relative to the number of connections\n"
00697           "(ppm_weight_scale_type == Count), whether to base the scale on\n"
00698           "the number of connections defined at the start of the simulation, as\n"
00699           "opposed to the number currently defined.  If set to True, makes obvious\n"
00700           "the increase in weight strength when a radius is decreased, but obscures\n"
00701           "the differences between the connections of a given unit to adjacent units.\n"
00702           "Only important for a connection type whose radius changes over time.");
00703   
00704   PARAM_I(PARAM_BOOL,  ppm_weight_scale_max_count,False,True,
00705           "When plotting weights on a scale relative to the number of connections\n"
00706           "(ppm_weight_scale_type == Count or Inhib), whether to base the scale on\n"
00707           "the maximum number of connections of the given type possible for any\n"
00708           "neuron, or else to use the number possible for this particular neuron.\n"
00709           "If set to True, makes obvious any differences in weight values from a\n"
00710           "given unit to adjacent units, while obscuring the different absolute\n"
00711           "weight values between units in the center and those near the border.\n"
00712           "Only important for units near the border.");
00713   
00714   PARAM_I(PARAM_INT,   ppm_weight_scale_type,0,PPM_MAX_WTSCALE,
00715           "Type of scaling to use for weight plots.  Possible settings (starting with\n"
00716           "PPM_WtScale_):\n\n"
00717           
00718           "  Fixed\n"
00719           "  Inhib\n"
00720           "  Count\n"
00721           "  Active\n"
00722           "  PeakRelative\n"
00723           "  Relative\n\n"
00724           
00725           "The first type uses a fixed scale for all lateral weight plots (determined\n"
00726           "by ppm_lateral multiplier), and another fixed scale for all afferent weight\n"
00727           "plots (determined by ppm_afferent_multiplier).  This makes it simple to\n"
00728           "compare different plots, since they will be on the same scale, but often\n"
00729           "individual plots will fail to show detail in the differences between\n"
00730           "adjacent units' weight values.  The last type has the opposite virtues and\n"
00731           "faults: each individual plot shows the maximum possible detail, but the\n"
00732           "absolute values of the weights are impossible to determine.  Those types in\n"
00733           "between reflect a different setting of this tradeoff between highlighting\n"
00734           "differences in a single plot, versus being able to compare plots and\n"
00735           "determine absolute weight levels.\n\n"
00736           
00737           "When using any of these types besides PPM_WtScale_Fixed, be sure to keep in\n"
00738           "mind that the absolute weight values depend upon the number of connections of\n"
00739           "that type active for that neuron, which in turn depends upon the connection\n"
00740           "radius, whether connections are square or circular, how close the neuron is\n"
00741           "to the border, and the connection death level.  See the parameters\n"
00742           "ppm_weight_scale_init_count and ppm_weight_scale_max_count to change\n"
00743           "how the scaling depends on the connection counts.\n\n"
00744 
00745           "The effective values of cortical weights also depend upon the cortical weight\n"
00746           "scaling factors gamma_exc and gamma_inh; none of the weight plots currently\n"
00747           "take these parameters' values into account.");
00748 }
00749 
00750 
00751 /******************************************************************************/
00752 /* Public initialization routines                                             */
00753 /******************************************************************************/
00754 
00759 void ppm_bitmap_initialize(void)
00760 {
00761   /* Set defaults for border areas -- white for video, black for paper */
00762   const double bg_default = (ppm_paper_based_colors? 0.0 : 1.0);
00763   const double bg_R = (ppm_bg_R>=0 ? ppm_bg_R : bg_default);
00764   const double bg_G = (ppm_bg_G>=0 ? ppm_bg_G : bg_default);
00765   const double bg_B = (ppm_bg_B>=0 ? ppm_bg_B : bg_default);
00766   
00767   /* Define structured color for actual use */
00768   ppm_background_color.red   = (int)(bg_R*HIGHEST_COLOR_LEVEL);
00769   ppm_background_color.green = (int)(bg_G*HIGHEST_COLOR_LEVEL);
00770   ppm_background_color.blue  = (int)(bg_B*HIGHEST_COLOR_LEVEL);
00771 
00772   /*
00773     Use explicitly set foreground color, if any; otherwise 
00774     set it opposite background.  It will usually be chosen later as
00775     appropriate for the background at a particular location, so it
00776     is marked as a BadPixel.
00777   */
00778   const double fg_default = (ppm_paper_based_colors? 1.0 : 0.0);
00779   const double fg_R = (ppm_line_R>=0 ? ppm_line_R : fg_default);
00780   const double fg_G = (ppm_line_G>=0 ? ppm_line_G : fg_default);
00781   const double fg_B = (ppm_line_B>=0 ? ppm_line_B : fg_default);
00782 
00783   /* Define structured color for actual use */  
00784   ppm_foreground_color.red   = (int)(fg_R*HIGHEST_COLOR_LEVEL);
00785   ppm_foreground_color.green = (int)(fg_G*HIGHEST_COLOR_LEVEL);
00786   ppm_foreground_color.blue  = (int)(fg_B*HIGHEST_COLOR_LEVEL);
00787   ppm_foreground_color.flags = 
00788     (((ppm_line_R < 0) || (ppm_line_G < 0) || (ppm_line_B < 0)) ?
00789      BadPixel : 0);
00790 
00791 
00792   /* Set image to background color */
00793   ppm_clear_image(ppm_background_color.red,ppm_background_color.green,ppm_background_color.blue);
00794 
00795   /* Set default extent of cortex plot to be the entire cortex */
00796   if (ppm_master_scale != Uninitialized || ppm_start_i == Uninitialized) ppm_start_i = 0;
00797   if (ppm_master_scale != Uninitialized || ppm_start_j == Uninitialized) ppm_start_j = 0;
00798   if (ppm_master_scale != Uninitialized || ppm_end_i   == Uninitialized) ppm_end_i   = N;
00799   if (ppm_master_scale != Uninitialized || ppm_end_j   == Uninitialized) ppm_end_j   = N;
00800 
00801   /* Set default extent of retina plot to be the entire retina, usually*/
00802   {
00803     int buffer = (ppm_retina_edge_buffer_suppress ? retina_edge_buffer : 0); 
00804     if (ppm_master_scale != Uninitialized || ppm_start_x == Uninitialized) ppm_start_x =  0+buffer;
00805     if (ppm_master_scale != Uninitialized || ppm_start_y == Uninitialized) ppm_start_y =  0+buffer;
00806     if (ppm_master_scale != Uninitialized || ppm_end_x   == Uninitialized) ppm_end_x   = RN-buffer;
00807     if (ppm_master_scale != Uninitialized || ppm_end_y   == Uninitialized) ppm_end_y   = RN-buffer;
00808   }
00809 
00810   /* Load initial colors into colormap */
00811   create_colormap_RGB(colors, ncolors); 
00812 }
00813 
00814 
00815 
00817 int ppm_presentation_height(void)
00818 {
00819   const double masterscale  = (ppm_master_scale != Uninitialized ? ppm_master_scale : 1.0);
00820   const int active_N        = ppm_end_j-ppm_start_j;
00821   
00822   const int active_RN       = ppm_end_y-ppm_start_y;
00823   const int mapping_start_y = MAX(retina_edge_buffer,    ppm_start_y);
00824   const int mapping_end_y   = MIN(RN-retina_edge_buffer, ppm_end_y);
00825   const int mapping_height  = mapping_end_y - mapping_start_y;
00826 
00827   int height,cortex_height,retina_height;
00828 
00829   /* If no scale has been specified, auto-scale everything to fit and match */
00830   if (ppm_master_scale != Uninitialized || ppm_cortex_scale == Uninitialized)
00831     ppm_cortex_scale = (active_N>mapping_height ? masterscale : masterscale*mapping_height/(1.0*active_N ));
00832   cortex_height=((int)ppm_cortex_scale)*active_N;
00833   
00834   if (ppm_master_scale != Uninitialized || ppm_retina_scale == Uninitialized)
00835     ppm_retina_scale = (active_N<mapping_height ? masterscale :  masterscale*active_N/(1.0*mapping_height));
00836   retina_height=((int)ppm_retina_scale)*active_RN;
00837   
00838   /* Set height to maximum needed, after scaling and quantization */
00839   height = ppm_border + MAX(cortex_height,retina_height) + ppm_border;
00840 
00841   if (height > IMG_SIZE_Y) {
00842     ipc_notify(IPC_ALL,IPC_ERROR,"ppm_presentation_height: Height %d is too large for image of %d; aborting", 
00843               height, (int)IMG_SIZE_Y);
00844     return 0;
00845   }
00846   else {
00847     ppm_height = height;
00848     return height; 
00849   }
00850 }
00851 
00852 
00853 
00854 /******************************************************************************/
00855 /* Public routines to draw a single plot                                      */
00856 /*                                                                            */
00857 /* Each of these routines draws into the current bitmap starting at the       */
00858 /* given offset from the left, returning the number of pixels advanced        */
00859 /* horizontally.                                                              */
00860 /******************************************************************************/
00861 
00862 
00863 LOff ppm_draw_activity(double activity[NMAX][NMAX], int left_offset)
00864 {
00865   const int width = ppm_init_cortex_plot(left_offset);
00866   int i,j;
00867   double max_value=-1e12; /* Arbitrary */
00868   double min_value=+1e12; /* Arbitrary */
00869   
00870   if (width)
00871     for(  i=ppm_start_i; i<ppm_end_i; i++)
00872       for(j=ppm_start_j; j<ppm_end_j; j++) {
00873         const double val   = activity[i][j];
00874         const double value = ppm_activity_luminance_offset + val;
00875 
00876         if (val > max_value) max_value = val;
00877         if (val < min_value) min_value = val;
00878 
00879         draw_cortex_unit(ppm_lookup_color(i,j,ppm_luminance_multiplier*
00880                                           value,ppm_activity_subplot),
00881                            i,j,False,left_offset);
00882       }
00883 
00884   ipc_notify(IPC_ALL,IPC_VERBOSE,"ppm_draw_activity: Values ranged from %g to %g",
00885              min_value,max_value);
00886   
00887   return width;
00888 }
00889 
00890 
00891 
00892 LOff ppm_draw_od_pref(double od_pref_array[NMAX][NMAX], int left_offset)
00893 {
00894   const int width = ppm_init_cortex_plot(left_offset);
00895   int i,j;
00896   
00897   if (width)
00898     for( i=ppm_start_i; i < ppm_end_i; i++)
00899       for(j= ppm_start_j; j < ppm_end_j; j++)
00900         draw_cortex_unit(ppm_lookup_color(i,j,od_pref_array[i][j],
00901                                           ppm_ocular_dominance_subplot),
00902                            i,j,False,left_offset);
00903   return width;
00904 }
00905 
00906 
00907 
00908 LOff ppm_draw_or_pref(int or_preferences[NMAX][NMAX], 
00909                      double or_selectivities[NMAX][NMAX], 
00910                      bool selectivity_only, int left_offset)
00911 {
00912   const int width = ppm_init_cortex_plot(left_offset);
00913   int i,j;
00914   int badpixels = 0;
00915 
00916   if (width)
00917     for( i=ppm_start_i; i < ppm_end_i; i++)
00918       for(j= ppm_start_j; j < ppm_end_j; j++){
00919         /* No default value for hue, but colors default to full saturation and luminance */
00920         double hue           = NOHUE; 
00921         double saturation    = 1.0;
00922         double value         = 1.0;
00923         int    orp           = or_preferences[i][j];
00924         double scaled_select = CROP(0.0,1.0,or_selectivities[i][j]*ppm_selectivity_multiplier);
00925           
00926         XColor color;
00927         
00928         /* special case to account for -0.0001 being converted to -1 by floor() */
00929         if (orp == -1) orp = 0;
00930 
00931         /* If requested, plot selectivity only, as grayscale */
00932         if (selectivity_only) {
00933           saturation = 0.0;
00934           value      = (ppm_paper_based_colors ? scaled_select: 1.0-scaled_select);
00935           if (value > 1.0 || value < 0.0)
00936             cout << value << "\t";
00937         }
00938         else {
00939           
00940           /* Hue encodes orientation preference, if any */
00941           if ( (orp<0)  || (OR_PREF_TO_DEGREES(orp)>180.0) ) {
00942             badpixels++;
00943             hue = NOHUE;
00944           }
00945           else hue = OR_PREF_TO_DEGREES(orp)/180.0;
00946           
00947           switch (ppm_orientation_subplot) {
00948           case PPM_SELECTIVITY:
00949             if (ppm_paper_based_colors) value      = scaled_select; 
00950             else                        saturation = scaled_select;
00951             
00952             break;
00953           case PPM_GRAYSCALE: {
00954             /* Orientation as value -- no primary plot (i.e., monochrome) */
00955             float ord = OR_PREF_TO_DEGREES(orp);
00956             /* offset to determine black angle */
00957             float or_radians = CONSTRAIN_ANGLE(DEGREES_TO_RADIANS(ord-ppm_reference_angle));  
00958             saturation = 0.0; 
00959             
00960             if (or_radians<M_PI/2) value =     (    or_radians )/(M_PI/2);
00961             else                 value = fabs( M_PI-or_radians )/(M_PI/2);  
00962             
00963             /* Make everything somber */
00964             value = CROP(0,1.0,ppm_value_range*value+ppm_value_offset);
00965 
00966             /* Paper and video are photo negatives of each other */
00967             if (ppm_paper_based_colors) value = 1.0-value;
00968           
00969             break;
00970           }
00971           
00972           case PPM_NONE:
00973             break;
00974             
00975           default:
00976             ipc_notify(IPC_ONE,IPC_WARNING,"Unsupported ppm_orientation_subplot: %d",
00977                        ppm_orientation_subplot);
00978           }
00979         }
00980         
00981         hsv2rgb(hue, saturation, value, &color);
00982 
00983         draw_cortex_unit(color,i,j,False,left_offset);
00984       }
00985 
00986 
00987   if (badpixels > 0)
00988     ipc_notify(IPC_ALL,IPC_WARNING,"ppm_draw_or_pref: There were %d bad pixels",badpixels);
00989 
00990   return width;
00991 }
00992 
00993 
00994 
00996 LOff ppm_draw_or_pref_line(int or_preferences[NMAX][NMAX], 
00997                           double or_selectivities[NMAX][NMAX], 
00998                           int left_offset)
00999 {
01000   const int scaled_width    = ppm_scaled_cortex_plot_width();
01001   const int height_offset   = ppm_scaled_cortex_height_offset();
01002   const int npixels_perunit = ppm_scaled_cortex_unit_pixels();
01003 
01004   int i,j; 
01005   int badpixels = 0;
01006 
01007   if (!canExtendWidthTo(left_offset + scaled_width))
01008     return 0;
01009 
01010   /* Draw the or_pref */
01011   for( i=ppm_start_i; i < ppm_end_i; i++){
01012     const int square_left_offset   = left_offset + (i-ppm_start_i)*npixels_perunit;
01013 
01014     for(j= ppm_start_j; j < ppm_end_j; j++){
01015       const int square_height_offset = height_offset + (j-ppm_start_j)*npixels_perunit;
01016       double length;
01017 
01018       /* Draw line for this neuron, if needed */
01019       if ( ppm_line_plots                      &&
01020            ( ((i+ppm_neuron_ioffset_line) % ppm_neuron_skip_line) == 0)  &&
01021            ( ((j+ppm_neuron_joffset_line) % ppm_neuron_skip_line) == 0) ) {
01022         
01023         int orp = or_preferences[i][j];
01024 
01025         length = ppm_neuron_skip_line*npixels_perunit-ppm_interior_border;
01026 
01027         /* special case to account for -0.0001 being converted to -1 by floor()
01028            in old versions of t3d_od.c 
01029            */
01030         if (orp == -1) orp = 0;
01031         
01032         switch (ppm_line_orientation_subplot) {
01033         case PPM_SELECTIVITY:  
01034           length *= or_selectivities[i][j]*ppm_line_selectivity_multiplier;
01035           break;
01036         case PPM_NONE:  
01037           break;
01038         default:
01039           ipc_notify(IPC_ONE,IPC_WARNING,"Unsupported ppm_line_orientation_subplot: %d",
01040                      ppm_line_orientation_subplot);
01041         }
01042         
01043         ppm_draw_line_at_angle(square_left_offset   + ppm_neuron_skip_line*npixels_perunit/2,
01044                                square_height_offset + ppm_neuron_skip_line*npixels_perunit/2, 
01045                                DEGREES_TO_RADIANS(OR_PREF_TO_DEGREES(orp)),
01046                                length, ppm_outline_width,
01047                                ppm_foreground_color);
01048       }
01049     }
01050   }
01051 
01052   if (badpixels > 0)
01053     ipc_notify(IPC_ALL,IPC_WARNING,"ppm_draw_or_pref_line: There were %d bad pixels",badpixels);
01054   
01055   return scaled_width;
01056 }
01057 
01058 
01059 
01060 LOff ppm_draw_lateral_weights(int ui, int uj, l_weight *weights, int radius,
01061                               int ar_width, int left_offset,
01062                               int isInhWts, int mark_neuron)
01063 {
01064   const int lowi             = MAX(ui-radius,  0);
01065   const int highi            = MIN(ui+radius,N-1);
01066   const int lowj             = MAX(uj-radius,  0);
01067   const int highj            = MIN(uj+radius,N-1);
01068   
01069   const int width = ppm_init_cortex_plot(left_offset);
01070 
01071   double scale_factor;
01072   double scale_offset;
01073   int i,j;
01074   
01075   double max_value=-1e12; /* Arbitrary */
01076   double min_value=+1e12; /* Arbitrary */
01077 
01078   if (!width)
01079     return width;
01080 
01081   compute_weight_scale(ui,uj,N,circular_lat_wts,
01082                        radius,ar_width,weights,True,
01083                        &scale_factor,&scale_offset);
01084   
01085   for( i=ppm_start_i; i < ppm_end_i; i++)
01086     for(j= ppm_start_j; j < ppm_end_j; j++){
01087       const double none  = (i<lowi || i>highi || j<lowj || j>highj);
01088       const double val   = (none? 0 : weights[LAT_INDEX(ui,uj,i,j,radius,ar_width)]);
01089       const double level = MAX(0,val);
01090       const double value = (level < ppm_weight_threshold ? 0 :
01091                             (level+scale_offset)*scale_factor);
01092       
01093       if (!none && val > max_value) max_value = val;
01094       if (!none && val < min_value) min_value = val;
01095       
01096       draw_cortex_unit(ppm_lookup_color(i,j,value,
01097                                         ppm_lateral_subplot),
01098                        i,j,False,left_offset);
01099     }
01100   
01101   if (ppm_outline_weights) {
01102     if (isInhWts && ppm_minimal_outline)
01103       draw_outline_on_cortex(     ui, uj, radius,     left_offset, inhWeightAbove);
01104     else if (circular_lat_wts) 
01105       draw_outline_on_cortex(     ui, uj, radius,     left_offset, isInsideCircle);
01106     else
01107       draw_box_outline_on_cortex( ui+0.5, uj+0.5, radius+0.5, left_offset);
01108   }
01109   
01110   if (mark_neuron) /* Outline neuron */    
01111     draw_box_outline_on_cortex(   ui+0.5, uj+0.5,        0.5, left_offset);
01112 
01113   ipc_notify(IPC_ALL,IPC_VERBOSE,"ppm_lateral_weights: Values ranged from %g to %g",
01114              min_value,max_value);
01115 
01116   return width;
01117 }
01118 
01119 
01120 
01126 LOff ppm_draw_retina(int eye, double input_v[RNMAX*RNMAX], int left_offset)
01127 {
01128   const int width = ppm_init_retina_plot(left_offset);
01129   int i,j;
01130   (void)eye;
01131   double max_value=-1e12; /* Arbitrary */
01132   double min_value=+1e12; /* Arbitrary */
01133   
01134   if (width)
01135     for( i=ppm_start_x; i < ppm_end_x; i++)
01136       for(j= ppm_start_y; j < ppm_end_y; j++) {
01137         const double val   = input_v[INP_INDEX(0,i,j)];
01138         const double value = MAX(0,val);
01139         XColor color;
01140 
01141         if (val > max_value) max_value = val;
01142         if (val < min_value) min_value = val;
01143         
01144         if (ppm_retina_subplot == PPM_ORIENTATION) {
01145 #ifdef NO_INPUTS
01146           double angle = 0;
01147 #else
01148           double angle = inputs->angle_of_object_at_location(i,j,eye);
01149 #endif
01150           if (ppm_paper_based_colors)
01151             hsv2rgb(angle/M_PI, value, 1.0, &color);
01152           else
01153             hsv2rgb(angle/M_PI, 1.0, value, &color);
01154         }
01155         else
01156           color = ppm_lookup_color(i*N/RN,j*N/RN, /* arbitrary */
01157                                    value,ppm_retina_subplot,ppm_paper_based_retina);
01158         
01159         draw_retina_unit(color,i,j,False,left_offset);
01160       }
01161   
01162   /* Draw outline around active retinal area if desired */
01163   if (width && ppm_outline_retina && !ppm_retina_edge_buffer_suppress && retina_edge_buffer > 0)
01164     draw_box_outline_on_retina( RN/2.0, RN/2.0, (RN-2.0*retina_edge_buffer)/2.0, left_offset);
01165 
01166   ipc_notify(IPC_ALL,IPC_VERBOSE,"ppm_draw_retina: Values ranged from %g to %g",
01167              min_value,max_value);
01168 
01169   
01170   return width; 
01171 }
01172 
01173 
01174 
01175 LOff ppm_draw_afferent_weights(int center_x, int center_y, 
01176                                a_weight weights[WTMAX][WTMAX], 
01177                                int ui, int uj, int left_offset)
01178 {
01179   const int lowi  = MAX(center_x-rf_radius,0   );
01180   const int highi = MIN(center_x+rf_radius,RN-1);
01181   const int lowj  = MAX(center_y-rf_radius,0   );
01182   const int highj = MIN(center_y+rf_radius,RN-1);
01183   
01184   const int width = ppm_init_retina_plot(left_offset);
01185 
01186   double scale_factor;
01187   double scale_offset;
01188   int i,j;
01189   
01190   double max_value=-1e12; /* Arbitrary */
01191   double min_value=+1e12; /* Arbitrary */
01192 
01193   if (!width)
01194     return width;
01195   
01196   compute_weight_scale(center_x,center_y,RN,circular_aff_wts,
01197                        rf_radius,rf_radius*2+1,weights,False,
01198                        &scale_factor, &scale_offset);
01199   
01200   for( i=ppm_start_x; i < ppm_end_x; i++)
01201     for(j= ppm_start_y; j < ppm_end_y; j++) {
01202       const double none  = (i<lowi || i>highi || j<lowj || j>highj);
01203       const double val   = (none ? 0 : weights[i-lowi][j-lowj]);
01204       const double level = MAX(0,val);
01205       const double value = (level < ppm_weight_threshold ? 0 :
01206                             (level+scale_offset)*scale_factor);
01207       
01208       if (!none && val > max_value) max_value = val;
01209       if (!none && val < min_value) min_value = val;
01210       
01211       draw_retina_unit(ppm_lookup_color(ui,uj,value,ppm_afferent_subplot),
01212                        i,j,False,left_offset);
01213     }
01214   
01215   if (ppm_outline_weights)
01216     if (circular_aff_wts) 
01217       draw_outline_on_retina(cortex_map[ui][uj].centerx, cortex_map[ui][uj].centery,
01218                              rf_radius, left_offset, isInsideCircle);
01219     else
01220       draw_box_outline_on_retina(cortex_map[ui][uj].centerx+0.5, cortex_map[ui][uj].centery+0.5,
01221                                  rf_radius+0.5, left_offset);
01222   
01223   /* Draw outline around active retinal area if desired */
01224   if (width && ppm_outline_retina && !ppm_retina_edge_buffer_suppress && retina_edge_buffer > 0)
01225     draw_box_outline_on_retina( RN/2.0, RN/2.0, (RN-2.0*retina_edge_buffer)/2.0, left_offset);
01226   
01227   if (ppm_always_mark_neuron) /* Outline (centerx,centery) */
01228     draw_box_outline_on_retina( cortex_map[ui][uj].centerx+0.5, cortex_map[ui][uj].centery+0.5, 
01229                                 0.5, left_offset);
01230 
01231   ipc_notify(IPC_ALL,IPC_VERBOSE,"ppm_afferent_weights: Values ranged from %g to %g",
01232              min_value,max_value);
01233 
01234   return width; 
01235 }
01236 
01237 
01238 
01244 LOff ppm_draw_angle_histogram(  OrientationHistogram histo, int symmetric, int left_offset)
01245 {
01246   const int width    = ppm_init_histogram_plot(symmetric,left_offset);
01247   const double bound = ppm_histo_percentage_range*(symmetric? 0.5 : 1);
01248   double max_value=0;
01249   double min_value=0;
01250   int j;
01251   
01252   if (width)
01253     for( j=0; j < or_num_angles; j++) {
01254       const double value = ( (histo.total_level==0) ? 0 : histo.levels[j]/histo.total_level);
01255       draw_histogram_bin(value, j, False, symmetric, left_offset);
01256       if (value > max_value) max_value = value;
01257       if (value < min_value) min_value = value;
01258     }
01259   
01260   if (max_value*100.0  > bound)
01261     ipc_notify(IPC_ALL,IPC_CAUTION,"ppm_draw_angle_histogram: cropped maximum level of %g%% at %g%%",
01262                max_value*100.0,bound);
01263   
01264   if (symmetric && min_value*100.0 < -bound)
01265     ipc_notify(IPC_ALL,IPC_CAUTION,"ppm_draw_angle_histogram: cropped minimum level of %g%% at %g%%",
01266                min_value*100.0,-bound);
01267   
01268   return width;
01269 }
01270 
01271 
01272 
01273 LOff ppm_draw_angle_histogram_difference( OrientationHistogram poshisto,
01274                                           OrientationHistogram neghisto,
01275                                           int left_offset)
01276 {
01277   int j;
01278   OrientationHistogram histo;
01279   
01280   /* Subtract the given histograms */
01281   histo.total_level = 0;
01282   for( j=0; j < or_num_angles; j++) {
01283     histo.levels[j]    = poshisto.levels[j] - neghisto.levels[j];
01284     histo.total_level += poshisto.levels[j] + neghisto.levels[j];
01285   }
01286   
01287   return ppm_draw_angle_histogram( histo, True, left_offset);
01288 }
01289 
01290 
01291 
01292 LOff ppm_draw_or_color_key(int num_examples, int key_length, int vertical, int left_offset)
01293 {
01294   const int    bar_spacing = key_length/(num_examples-1);
01295   const double bar_length  = MAX(2,0.9*bar_spacing); /* Empirical */
01296   const double bar_thick   = MAX(1,bar_length/4.6);  /* Empirical */
01297                 
01298   const int    width       = (vertical? bar_spacing               : key_length+bar_spacing);
01299   const int    center      = (vertical? left_offset+width/2       : ppm_border+ppm_height/2);
01300   const int    offset      = (vertical? (ppm_height-key_length)/2 : left_offset+bar_spacing/2);
01301 
01302   int i;
01303 
01304   if (!canExtendWidthTo(width))
01305     return 0;
01306   
01307   for(i=0; i<num_examples; i++) {
01308     /* Reverse plotting order to match historical usage */
01309     const int iprime = (num_examples-1-i); 
01310     XColor color;
01311     hsv2rgb(iprime*1.0/(num_examples-1), 1.0, 1.0, &color);
01312     
01313     ppm_draw_line_at_angle(vertical? center : offset+i*bar_spacing,
01314                            vertical? offset+i*bar_spacing : center,
01315                            (iprime*M_PI)/(num_examples-1),
01316                            bar_length, bar_thick, color);
01317   }
01318   
01319   return width;
01320 }
01321 
01322 
01323 
01324 /******************************************************************************/
01325 /* Public routines to draw combined groups of related plots                   */
01326 /*                                                                            */
01327 /* Each of these routines draws into the current bitmap starting at the       */
01328 /* left edge, draws a series of plots from left to right (some possibly       */
01329 /* overlaying one another), and returns the final width.                      */ 
01330 /******************************************************************************/
01331 
01332 
01335 LOff ppm_draw_presentation(double input_v[RNMAX*RNMAX*MAX_NUM_EYES],
01336                            double init[NMAX][NMAX],
01337                            double settled[NMAX][NMAX])
01338 {
01339   int left_offset = ppm_border;
01340   OrientationHistogram poshisto;
01341   OrientationHistogram neghisto;
01342 
01343   left_offset   += ppm_draw_retina(0,               input_v, left_offset) + ppm_border;
01344 
01345   left_offset   += ppm_draw_activity(                  init, left_offset) + ppm_border;
01346   if (ppm_histograms && or_dumped != Uninitialized &&
01347       !compute_orientation_distribution(ppm_start_i, ppm_end_i-1,
01348                                         ppm_start_j, ppm_end_j-1,
01349                                         &neghisto, init,combined_eyes))
01350     left_offset += ppm_draw_angle_histogram( neghisto, False, left_offset) + ppm_border;
01351   
01352   left_offset   += ppm_draw_activity(               settled, left_offset) + ppm_border;
01353   if (ppm_histograms && or_dumped != Uninitialized &&
01354       !compute_orientation_distribution(ppm_start_i, ppm_end_i-1,
01355                                         ppm_start_j, ppm_end_j-1,
01356                                         &poshisto, settled,combined_eyes))
01357     left_offset += ppm_draw_angle_histogram( poshisto, False, left_offset) + ppm_border;
01358 
01359   /* Plot difference between initial and settled histograms (not usually very informative) */
01360   /* if (ppm_histograms && (or_dumped != Uninitialized)) 
01361        left_offset += ppm_draw_angle_histogram_difference(  poshisto, neghisto, left_offset) + ppm_border; */
01362 
01363   {
01364     int eye;
01365     for (eye=1; eye<num_eyes; eye++)
01366       left_offset += ppm_draw_retina(eye,&input_v[INP_INDEX(eye,0,0)], left_offset) + ppm_border;
01367   }
01368 
01369     
01370   if ((ppm_color_keys==Uninitialized && or_dumped != Uninitialized &&
01371        (ppm_histograms ||
01372         ppm_retina_subplot==PPM_ORIENTATION ||
01373         ppm_activity_subplot==PPM_ORIENTATION)) ||
01374       ppm_color_keys == True)
01375     left_offset += ppm_draw_or_color_key(ppm_color_key_num, ppm_height-4*ppm_border, True, left_offset)
01376       + ppm_border;
01377   
01378   return left_offset;
01379 }
01380 
01381 
01382 
01384 LOff ppm_draw_orientations(int or_preferences[EYE_ARRAY_SIZE][NMAX][NMAX],
01385                            double or_selectivities[EYE_ARRAY_SIZE][NMAX][NMAX],
01386                            bool selectivity_only)
01387 {
01388   int left_offset = ppm_border;
01389   int offset=0;
01390   int eye;
01391 
01392   for (eye=0; eye<=combined_eyes; eye++) {
01393   
01394     /* All these overlay each other, so left_offset is not incremented until the end */
01395     if (ppm_shaded_plots)
01396       offset = ppm_draw_or_pref(     or_preferences[eye], or_selectivities[eye], selectivity_only, left_offset) + ppm_border;
01397     
01398     if (ppm_line_plots)
01399       offset = ppm_draw_or_pref_line(or_preferences[eye], or_selectivities[eye], left_offset) + ppm_border;
01400     
01401     /* Draw outline around active weights of one neuron, if defined */
01402     if ((ppm_ui != Uninitialized) && (ppm_uj != Uninitialized)) {
01403 
01404       if (ppm_outline_weights) 
01405         draw_box_outline_on_cortex( ppm_ui+0.5, ppm_uj+0.5, 0.5, left_offset);
01406       
01407       draw_outline_on_cortex(ppm_ui,ppm_uj,inh_rad,left_offset,inhWeightAbove);
01408       
01409       /* Display the OR pref of this neuron for reference */
01410       ipc_notify(IPC_ALL,IPC_VERBOSE,"ppm_draw: Neuron (%d,%d) had an orientation preference of %f degrees and selectivity of %f",
01411                  ppm_ui,ppm_uj,
01412                  (double)OR_PREF_TO_DEGREES(or_preferences[eye][ppm_ui][ppm_uj]),
01413                  (double)or_selectivities[eye][ppm_ui][ppm_uj]);
01414     }
01415 
01416     left_offset += offset;
01417     
01418     if (ppm_histograms && !selectivity_only && (or_dumped != Uninitialized)) {
01419       OrientationHistogram histo;
01420       if (!compute_orientation_distribution(ppm_start_i, ppm_end_i-1,
01421                                             ppm_start_j, ppm_end_j-1,
01422                                             &histo, NULL,eye))
01423         left_offset += ppm_draw_angle_histogram(histo, False, left_offset) + ppm_border;
01424     }
01425   }
01426   
01427   if ((!selectivity_only && ppm_color_keys==Uninitialized && ppm_shaded_plots) ||
01428       (ppm_color_keys!=Uninitialized && ppm_color_keys))
01429     left_offset += ppm_draw_or_color_key(ppm_color_key_num, ppm_height-4*ppm_border, True, left_offset)
01430       + ppm_border;
01431   
01432   return left_offset;
01433 }
01434 
01435 
01436 
01438 LOff ppm_draw_ocular_dominance(double od_pref_array[NMAX][NMAX])
01439 {
01440   int left_offset = ppm_border;
01441   int offset=0;
01442   
01443   offset = ppm_draw_od_pref(od_pref_array, left_offset) + ppm_border;
01444   
01445   /* Draw outline around active weights of one neuron, if defined */
01446   if ((ppm_ui != Uninitialized) && (ppm_uj != Uninitialized)) {
01447     
01448     if (ppm_outline_weights) 
01449       draw_box_outline_on_cortex( ppm_ui+0.5, ppm_uj+0.5, 0.5, left_offset);
01450     
01451     draw_outline_on_cortex(ppm_ui,ppm_uj, inh_rad, left_offset, inhWeightAbove);
01452     
01453     /* Display the OD pref of this neuron for reference */
01454     ipc_notify(IPC_ALL,IPC_VERBOSE,
01455                "ppm_draw: Neuron (%d,%d) had an ocular dominance preference of %f",
01456                ppm_ui,ppm_uj,(double)(od_pref_array[ppm_ui][ppm_uj]));
01457   }
01458   
01459   left_offset += offset;
01460   
01461   return left_offset;
01462 }
01463 
01464 
01465 
01471 LOff ppm_draw_weights(int ui, int uj )
01472 {
01473   int left_offset = ppm_border;
01474   int eye;
01475   OrientationHistogram histo;
01476 
01477 
01478   left_offset += ppm_border +
01479     ppm_draw_afferent_weights(cortex_map[ui][uj].centerx, 
01480                               cortex_map[ui][uj].centery, 
01481                               cortex_map[ui][uj].weights[0], 
01482                               ui, uj, left_offset);
01483   
01484 
01485   left_offset += ppm_border +
01486     ppm_draw_lateral_weights(ui,uj, cortex_map[ui][uj].lat_exc_wts, 
01487                              exc_rad,exc_array_width,left_offset,
01488                              False,ppm_always_mark_neuron);
01489 
01490   if (ppm_histograms && or_dumped != Uninitialized &&
01491       !compute_weight_distribution(&histo,ui,uj, cortex_map[ui][uj].lat_exc_wts,
01492                                    exc_rad, exc_array_width, combined_eyes))
01493     left_offset += ppm_draw_angle_histogram(       histo, False, left_offset) + ppm_border;
01494   
01495 
01496   left_offset   += ppm_border +
01497     ppm_draw_lateral_weights(ui,uj, cortex_map[ui][uj].lat_inh_wts, 
01498                              inh_rad, inh_array_width, left_offset,
01499                              True,True);
01500 
01501   if (ppm_histograms && or_dumped != Uninitialized &&
01502       !compute_weight_distribution(&histo, ui,uj, cortex_map[ui][uj].lat_inh_wts,
01503                                    inh_rad, inh_array_width, combined_eyes))
01504     left_offset += ppm_draw_angle_histogram(histo, False, left_offset) + ppm_border;
01505   
01506 
01507   for (eye=1; eye<num_eyes; eye++)
01508     left_offset   += ppm_border +
01509       ppm_draw_afferent_weights(cortex_map[ui][uj].centerx, 
01510                                 cortex_map[ui][uj].centery, 
01511                                 cortex_map[ui][uj].weights[eye], 
01512                                 ui, uj, left_offset);
01513 
01514   if ((ppm_color_keys==Uninitialized && or_dumped != Uninitialized &&
01515        (ppm_histograms ||
01516         ppm_afferent_subplot==PPM_ORIENTATION ||
01517         ppm_lateral_subplot==PPM_ORIENTATION)) ||
01518       ppm_color_keys == True)
01519     left_offset += ppm_draw_or_color_key(ppm_color_key_num, ppm_height-4*ppm_border, True, left_offset)
01520       + ppm_border;
01521   
01522   return left_offset;
01523 }
01524 
01525 
01526 
01533 void ppm_draw_afferent_weight_map(const char* filename, int eye, int baseline_eye)
01534 {
01535   const int cursorincrement = rf_radius*2+1+ppm_interior_border;
01536   const int dx              = (ppm_end_i-ppm_start_i);
01537   const int dy              = (ppm_end_j-ppm_start_j);
01538   const int image_width     = ppm_border+cursorincrement*(1+(dx-1)/ppm_neuron_skip_aff)+ppm_border;
01539   const int image_height    = ppm_border+cursorincrement*(1+(dy-1)/ppm_neuron_skip_aff)+ppm_border;
01540   XColor interior           = ppm_interior_outline ? ppm_background_color : ppm_foreground_color;
01541   interior.flags = 0; /* Clear BadPixel, if any */
01542   
01543   vector< vector<XColor> > image_data(image_width,vector<XColor>(image_height,ppm_background_color));
01544   
01545   int cursorx = ppm_border;
01546   int cursory = ppm_border;
01547   for (int uj=ppm_start_j; uj<ppm_end_j; uj += ppm_neuron_skip_aff) {
01548     for (int ui=ppm_start_i; ui<ppm_end_i; ui += ppm_neuron_skip_aff) {
01549       const int center_x = cortex_map[ui][uj].centerx;
01550       const int center_y = cortex_map[ui][uj].centery;
01551       const int lowx     = MAX(center_x-rf_radius,0   );
01552       const int highx    = MIN(center_x+rf_radius,RN-1);
01553       const int lowy     = MAX(center_y-rf_radius,0   );
01554       const int highy    = MIN(center_y+rf_radius,RN-1);
01555         
01556       double scale_factor;
01557       double scale_offset;
01558       compute_weight_scale(center_x,center_y,RN,circular_aff_wts,
01559                            rf_radius,rf_radius*2+1,
01560                            cortex_map[ui][uj].weights[eye],
01561                            False, &scale_factor, &scale_offset);
01562       //cout << scale_factor << " " << scale_offset << endl;
01563       
01564       for(int x=center_x-rf_radius; x<=center_x+rf_radius+ppm_interior_border; x++)
01565         for(int y=center_y-rf_radius; y<=center_y+rf_radius+ppm_interior_border; y++) {
01566           const double level = ((x<lowx || x>highx || y<lowy || y>highy) ? 0 
01567                                 : MAX(0,cortex_map[ui][uj].weights[eye][x-lowx][y-lowy]));
01568           const double diff  = ((baseline_eye==Uninitialized ||
01569                                  (x<lowx || x>highx || y<lowy || y>highy)) ? level
01570                                 : level - MAX(0,cortex_map[ui][uj].weights[baseline_eye][x-lowx][y-lowy]));
01571           const double value = (diff < ppm_weight_threshold ? 0 :
01572                                 (diff+scale_offset)*scale_factor);
01573           const XColor color = ((x>center_x+rf_radius || y>center_y+rf_radius) ? interior 
01574                                 : ppm_lookup_color(ui,uj,value,ppm_afferent_subplot));
01575           image_data[x-center_x+rf_radius+cursorx][y-center_y+rf_radius+cursory] = color;
01576         }
01577       cursorx += cursorincrement;
01578     }
01579     cursorx  = ppm_border;
01580     cursory += cursorincrement;
01581   }
01582 
01583   ppm_write_image_to_file( image_data,filename,filename,image_width,image_height,255 );
01584 }
01585 
01586 
01587 
01588 /******************************************************************************/
01589 /* Private routines                                                           */
01590 /******************************************************************************/
01591 
01592 
01594 void compute_weight_scale( int ui, int uj, int max_coord, int circular_wts,
01595                            int radius, int ar_width,
01596                            void* weights, int weights_are_lat,
01597                            double* factor, double* offset )
01598 {
01599   const int type = ppm_weight_scale_type;
01600   
01601   /* Defaults */
01602   *factor=1.0;
01603   *offset=0.0;
01604         
01605   /* Simplest case */
01606   if (type == PPM_WTSCALE_FIXED)
01607     *factor=ppm_afferent_multiplier;
01608   
01609   /* Compute scaling from max value, min value, number of connections, and/or num active */
01610   else {
01611     const int array_width       = (weights_are_lat && type == PPM_WTSCALE_INHIB ?
01612                                    inh_array_width : ar_width);
01613     const int rad               = (ppm_weight_scale_init_count ? (array_width-1)/2 : radius);
01614     const double radius_sq      = (rad+circular_radius_trim)*(rad+circular_radius_trim);
01615     
01616     const int lowi              = (ppm_weight_scale_max_count  ? ui-rad : MAX(ui-rad,        0));
01617     const int highi             = (ppm_weight_scale_max_count  ? ui+rad : MIN(ui+rad,max_coord));
01618     const int lowj              = (ppm_weight_scale_max_count  ? uj-rad : MAX(uj-rad,        0));
01619     const int highj             = (ppm_weight_scale_max_count  ? uj+rad : MIN(uj+rad,max_coord));
01620     
01621     const int scale_with_values = (type == PPM_WTSCALE_ACTIVE      ||
01622                                    type == PPM_WTSCALE_RELATIVE    ||
01623                                    type == PPM_WTSCALE_PEAKRELATIVE  );
01624       
01625     double max   = 0.0; /* Init to minimum possible active value */
01626     double min   = 1.0; /* Init to maximum possible value (with normalization) */
01627     int    count =   0;
01628     int    active=   0;
01629     double value = 0.0;
01630     int i,j;
01631 
01632     typedef a_weight a_weights[WTMAX]; /* To allow casting */
01633 
01634     for( i=lowi; i <= highi; i++)
01635       for(j=lowj; j <= highj; j++) {
01636         if (scale_with_values) {
01637           if (weights_are_lat)
01638             value = ((l_weight*)weights)[LAT_INDEX(ui,uj,i,j,rad,ar_width)];
01639           else
01640             value = ((a_weights*)weights)[i-lowi][j-lowj];
01641 
01642           if (value > ppm_weight_threshold) {
01643             active++;
01644             if (value > max) max = value;
01645             if (value < min) min = value;
01646           }
01647         }
01648         else if (circular_wts)
01649           count += (WITHIN_RADIUS(i-ui,j-uj,radius_sq)? 1 : 0);
01650         else
01651           count++;
01652       }
01653     
01654     if      (type == PPM_WTSCALE_RELATIVE) {
01655       *factor= ((max-min)>0 ? 1.0/(max-min) : 1.0);
01656       *offset= -min;
01657     }
01658     
01659     else if (type == PPM_WTSCALE_PEAKRELATIVE)
01660       *factor = (max>0 ? 1.0/max : 1.0);
01661     
01662     else if (type == PPM_WTSCALE_ACTIVE)
01663       *factor = 1.0*active;
01664     
01665     else 
01666       *factor = 1.0*count;
01667   }
01668 }
01669 
01670 
01671 
01672 int ppm_scaled_cortex_unit_pixels( void ) { return (int)ppm_cortex_scale; }
01673 int ppm_scaled_cortex_plot_height( void ) { return ppm_scaled_cortex_unit_pixels()*(ppm_end_j-ppm_start_j); }
01674 int ppm_scaled_cortex_plot_width(  void ) { return ppm_scaled_cortex_unit_pixels()*(ppm_end_i-ppm_start_i); }
01675 int ppm_scaled_cortex_height_offset(void) { return (ppm_height - ppm_scaled_cortex_plot_height())/2; }
01676 
01677 int ppm_scaled_retina_unit_pixels( void ) { return (int)ppm_retina_scale; }
01678 int ppm_scaled_retina_plot_height( void ) { return ppm_scaled_retina_unit_pixels()*(ppm_end_y-ppm_start_y); }
01679 int ppm_scaled_retina_plot_width(  void ) { return ppm_scaled_retina_unit_pixels()*(ppm_end_x-ppm_start_x); }
01680 int ppm_scaled_retina_height_offset(void) { return (ppm_height - ppm_scaled_retina_plot_height())/2; }
01681 
01682 
01683 int ppm_scaled_histogram_bin_pixels( int num_bins )
01684 {  return (int)(ppm_scaled_cortex_plot_height()/num_bins);  }
01685 
01686 int ppm_scaled_histogram_plot_height(int num_bins )
01687 {  return ppm_scaled_histogram_bin_pixels(num_bins)*num_bins;  }
01688 
01689 int ppm_scaled_histogram_plot_width( int num_bins, double scale )
01690 {  return (int)(ppm_scaled_histogram_plot_height(num_bins)*scale);  }
01691 
01692 int ppm_scaled_histogram_height_offset(int num_bins )
01693 {  return (ppm_height - ppm_scaled_histogram_plot_height(num_bins))/2;  }
01694 
01695 
01696 
01702 XColor ppm_get_drawing_color(int lowi, int highi, int lowj, int highj, XColor set_color) 
01703 {
01704   /* Compute color if one isn't set already */
01705   if ((set_color.flags & BadPixel) != 0) {
01706     
01707     double value, dummy;
01708     double total = 0;
01709     double average;
01710     int midi=lowi+(highi-lowi)/2;
01711     int midj=lowj+(highj-lowj)/2;
01712       
01713     rgb2hsv(image[lowi ][midj ], &dummy, &dummy, &value); total += value;
01714     rgb2hsv(image[midi ][lowj ], &dummy, &dummy, &value); total += value;
01715     rgb2hsv(image[midi ][highj], &dummy, &dummy, &value); total += value;
01716     rgb2hsv(image[highi][midj ], &dummy, &dummy, &value); total += value;
01717     average=total/4;
01718     
01719     /* If luminance is clearly to one end or the other, choose a color with
01720        opposite luminance */
01721     
01722     if      (average >= 0.75) /* bright background */
01723       return Black;
01724     
01725     else if (average <  0.25) /* dark background   */
01726       return White;
01727     
01728     else                      /* medium background */
01729       return Blue;
01730   }
01731   
01732   return set_color;
01733 }
01734 
01735 
01736 
01737 void ppm_clear_image(int bg_R, int bg_G, int bg_B) /* set to background */
01738 {
01739   int i, j;
01740 
01741   for (i=0; i<IMG_SIZE_X; i++)
01742     for (j=0; j<IMG_SIZE_Y; j++){
01743       image[i][j].red   = bg_R;
01744       image[i][j].green = bg_G;
01745       image[i][j].blue  = bg_B;
01746     }
01747 }
01748 
01749 
01750 
01755 void create_colormap_RGB( XColor colormap[HIGHEST_COLOR_LEVEL+1], int numcolors )  
01756 {
01757  /* Store all the pixel values in the colormap in contiguous locations  */
01758  const int    rangeby3   = (int)(numcolors/3);
01759  const double multiplier = HIGHEST_COLOR_LEVEL/(double)rangeby3;
01760 
01761  int i;
01762 
01763  for(i= 0; i< numcolors; i++ ) {
01764    colormap[i].green = 0;
01765    colormap[i].blue  = 0;  
01766    if ( i < rangeby3 )
01767      colormap[i].red = (int)floor((double)i * multiplier);
01768    else{
01769      colormap[i].red = HIGHEST_COLOR_LEVEL;
01770      if ( i < 2*rangeby3)
01771        colormap[i].green = (int)floor((double)(i-rangeby3) * multiplier);
01772      else{
01773        colormap[i].green = HIGHEST_COLOR_LEVEL;
01774        colormap[i].blue  = (int)floor((double)(i-2*rangeby3)*multiplier);
01775      }
01776    }
01777  }
01778 }
01779 
01780 
01781 
01788 void hsv2rgb(
01789              double     h,  double     s,  double     v, 
01790              XColor *c 
01791              )
01792 {
01793   int    j;
01794   double rd, gd, bd;
01795   double f, p, q, T;
01796   double hue;
01797 
01798   /* Correct for flipped representation */
01799 #ifndef FOLEY_STANDARD_COLORS
01800   h=1-h;
01801 #endif
01802   
01803   /* convert HSV back to RGB */
01804   if (h==NOHUE || s==0.0) { rd = v;  gd = v;  bd = v; }
01805   else {
01806     /* Ensure bounds are met */
01807     if (s>1.0) s = 1.0;    if (s<0.0) s = 0.0;
01808     if (v>1.0) v = 1.0;    if (v<0.0) v = 0.0;
01809     /* hue ranges [0,6) */
01810     if (h >= 1.0 || h < 0.0) hue = 0.0;
01811     else        hue = h * 6.0;
01812     
01813     j = (int) floor(hue);
01814     if (j<0) j=0;          /* either h or floor seem to go neg on some sys */
01815     f = hue - j;
01816     p = v * (1-s);
01817     q = v * (1 - (s*f));
01818     T = v * (1 - (s*(1 - f)));
01819 
01820     switch (j) {
01821     case 0:  rd = v;  gd = T;  bd = p;  break;
01822     case 1:  rd = q;  gd = v;  bd = p;  break;
01823     case 2:  rd = p;  gd = v;  bd = T;  break;
01824     case 3:  rd = p;  gd = q;  bd = v;  break;
01825     case 4:  rd = T;  gd = p;  bd = v;  break;
01826     case 5:  rd = v;  gd = p;  bd = q;  break;
01827     default: rd = v;  gd = T;  bd = p;  break;  /* never happen */
01828     }
01829   }
01830 
01831   c->red   = (RGB_value)floor((rd * HIGHEST_COLOR_LEVEL) + 0.5);
01832   c->green = (RGB_value)floor((gd * HIGHEST_COLOR_LEVEL) + 0.5);
01833   c->blue  = (RGB_value)floor((bd * HIGHEST_COLOR_LEVEL) + 0.5);
01834   c->flags = 0;
01835 }
01836 
01837 
01838 /* Convert a color specified in RGB to HSV
01839   HSV values are specified in the range [0,1].
01840   From _Computer_Graphics_, 2nd Ed., Foley et al, 1990,
01841   except Hue modified to lie in [0,1) instead of [0,360). */
01842 void rgb2hsv(XColor c,
01843              double *h,  
01844              double *s,  
01845              double *v
01846              )
01847 {
01848   double r = c.red   / (1.0*(HIGHEST_COLOR_LEVEL+1));
01849   double g = c.green / (1.0*(HIGHEST_COLOR_LEVEL+1));
01850   double b = c.blue  / (1.0*(HIGHEST_COLOR_LEVEL+1));
01851 
01852 
01853   double max = MAX(MAX(r,g),b);
01854   double min = MIN(MIN(r,g),b);
01855 
01856   /* Value */
01857   *v = max;
01858 
01859   /* Saturation */
01860   *s = (max == 0.0) ? 0.0 : ((max-min)/max);
01861   
01862   /* Hue */
01863   if (*s == 0.0)
01864     *h = NOHUE;
01865   else {
01866     double delt = max - min;
01867 
01868     if (r == max)
01869       *h =       (g - b)/delt; /* Between yellow and magenta */
01870 
01871     else if (g == max)
01872       *h = 2.0 + (b - r)/delt; /* Between cyan and yellow    */
01873 
01874     else if (b  == max)
01875       *h = 4.0 + (r - g)/delt; /* Between magenta and cyan   */
01876 
01877     /* Convert to degrees (disabled) */
01878     /* *h *= 60; if (*h < 0.0) *h += 360; */
01879 
01880     /* Scale to 1.0 */
01881     *h *= (60.0/360.0); if (*h < 0.0) *h += 1.0; 
01882   }
01883 }
01884 
01885 
01888 void ppm_write_header( FILE *file, const char *comments, int width, int height, int max_val )
01889 {
01890   fprintf(file, "P6\n");                            /* file type: P6=binary, P3=ASCII */
01891   fprintf(file, "#%s\n", comments);                 /* Comments, if any               */
01892   fprintf(file, "%d %d\n", width, height);  /* picture dimensions             */
01893   fprintf(file, "%d\n", max_val);               /* maximum R,G, or B value        */
01894 }
01895 
01896 
01897 
01898 int ppm_write_pixel( FILE *file, XColor pixel, int max_val )
01899 {
01900   char rgb_value[3];
01901   rgb_value[0]=(char)((max_val/(double)(HIGHEST_COLOR_LEVEL+1))*(double)pixel.red);
01902   rgb_value[1]=(char)((max_val/(double)(HIGHEST_COLOR_LEVEL+1))*(double)pixel.green);
01903   rgb_value[2]=(char)((max_val/(double)(HIGHEST_COLOR_LEVEL+1))*(double)pixel.blue);
01904   
01905   if (3 != fwrite(&rgb_value, sizeof(rgb_value[0]), 3, file)) {
01906     ipc_notify(IPC_ALL,IPC_ERROR,"Cannot write to pixmap file");
01907     return -1;
01908   }
01909 
01910   return 0;
01911 }
01912 
01913 
01914 
01916 int ppm_write_to_file( const char* filename, const char *comments, int width, int height, int max_val )
01917 {  return ppm_write_image_to_file( image, filename, comments, width, height, max_val );  }
01918 
01919 
01920 
01922 template<class T>
01923 int ppm_write_image_to_file( T imagedata, const char* filename, const char *comments, int width, int height, int max_val )
01924 {
01925   int i, j;
01926   FILE *file;
01927 
01928   if ((file=fopen(filename,"w+"))==NULL){
01929     ipc_notify(IPC_ALL,IPC_ERROR,"Could not open output file %s", filename);
01930     return -1;
01931   }
01932       
01933   ppm_write_header( file, comments, width, height, max_val);
01934 
01935   for (j=0; j<height; j++)
01936     for (i=0; i<width; i++)
01937       if (ppm_write_pixel(file, imagedata[i][j], max_val))
01938         return -2;
01939   fclose(file);
01940   
01941   if ((ppm_spawn_viewer==Uninitialized && interactive)
01942       || (ppm_spawn_viewer!=Uninitialized && ppm_spawn_viewer) ) {
01943     static int spawned_already=False;
01944     if (!spawned_already || ppm_spawn_independent_viewers) {
01945       char cmdtxt[MAXFILENAMELENGTH*2];
01946       snprintf(cmdtxt,MAXFILENAMELENGTH*2,"%s %s %s",
01947                (running_remotely? ppm_remote_viewer : ppm_image_viewer),
01948                filename,
01949                (running_remotely? "" : "&"));
01950       system(cmdtxt);
01951       spawned_already=True;
01952     }
01953   }
01954   
01955   return 0;
01956 }
01957 
01958 
01959 
01961 XColor ppm_lookup_color( int i, int j, double scaled_level, int subplot, int paper_based)
01962 {
01963   XColor color;
01964   const int assume_paper = paper_based==Uninitialized ? ppm_paper_based_colors : paper_based;
01965 
01966   scaled_level = CROP(0.0,1.0,scaled_level);
01967 
01968   /* Determine color for this pixel */
01969   if (assume_paper) 
01970     
01971     switch(subplot) {
01972       
01973       /* Joseph's red, yellow, and white colormap */
01974     case PPM_RYW: color = colors[(int)( (1-scaled_level) * (double)(ncolors-1) )]; break;
01975           
01976       /* No hue value -- luminance encodes activity level */
01977     case PPM_GRAYSCALE: hsv2rgb(NOHUE, 0.0, 1-scaled_level, &color); break;
01978       
01979       /* Hue encodes orientation preference, if available */
01980     case PPM_ORIENTATION:
01981       
01982       if (or_dumped != Uninitialized) {
01983         double hue   = OR_PREF_TO_DEGREES(or_pref[combined_eyes][i][j])/180;
01984         double value = 1.0;
01985         double scaled_select = CROP(0.0,1.0,or_select[combined_eyes][i][j]*ppm_selectivity_multiplier);
01986           
01987         switch (ppm_orientation_subplot) {
01988         case PPM_SELECTIVITY:
01989           value = MAX(1-scaled_level, scaled_select);
01990           break;
01991         case PPM_NONE:  
01992           break;
01993         }
01994         hsv2rgb(hue,scaled_level,value, &color);
01995       }
01996       else /* No hue value since no or_pref -- luminance encodes activity level */
01997         hsv2rgb(NOHUE, 0.0, 1-scaled_level, &color);
01998       break;
01999 
02000       /* Hue encodes ocular dominance (unverified; prob. need to find good color range)*/
02001     case PPM_OCULARDOMINANCE: hsv2rgb(od_pref[i][j]/6, scaled_level, 1.0, &color); break;
02002       
02003     case PPM_SINGLEHUE: hsv2rgb(ppm_subplot_hue, scaled_level, 1.0, &color); break;      
02004 
02005     default:
02006       ipc_notify(IPC_ONE,IPC_WARNING,"Unsupported subplot: %d",subplot);
02007     }
02008   
02009   /* Video colors */
02010   else 
02011     switch(subplot) {
02012       
02013       /* Joseph's red, yellow, and white colormap */
02014     case PPM_RYW: color = colors[(int)( scaled_level * (double)(ncolors-1) )]; break;
02015       
02016       /* No hue value -- luminance encodes activity level */
02017     case PPM_GRAYSCALE: hsv2rgb(NOHUE, 0.0,   scaled_level, &color); break;
02018       
02019       /* Hue encodes orientation preference, if available */
02020     case PPM_ORIENTATION:       
02021       
02022       if (or_dumped != Uninitialized) {
02023         double hue   = OR_PREF_TO_DEGREES(or_pref[combined_eyes][i][j])/180;
02024         double saturation = 1.0;
02025         double scaled_select = CROP(0.0,1.0,or_select[combined_eyes][i][j]*ppm_selectivity_multiplier);
02026         
02027         switch (ppm_orientation_subplot) {
02028         case PPM_SELECTIVITY:  
02029           saturation = scaled_select;
02030           break;
02031         case PPM_NONE:  
02032           break;
02033         }
02034         hsv2rgb(hue,saturation,scaled_level, &color);
02035       }
02036 
02037       else           hsv2rgb(NOHUE, 0.0, scaled_level, &color);
02038       break;
02039 
02040       /* Hue encodes ocular dominance (unverified; prob. need to find good color range)*/
02041     case PPM_OCULARDOMINANCE: hsv2rgb(od_pref[i][j]/6, 1.0, scaled_level, &color); break;
02042       
02043     case PPM_SINGLEHUE: hsv2rgb(ppm_subplot_hue, 1.0, scaled_level, &color); break;
02044       
02045     default:
02046       ipc_notify(IPC_ONE,IPC_WARNING,"Unsupported subplot: %d",subplot);
02047     }
02048 
02049   return color;
02050 }
02051 
02052 
02053 
02055 int canExtendWidthTo(int width)
02056 {
02057   if (width > IMG_SIZE_X) {
02058     ipc_notify(IPC_ALL,IPC_ERROR,"ppm_draw: Width %d is too large for image of %d; aborting", 
02059               width, (int)IMG_SIZE_X);
02060     return 0;
02061   }
02062   else return 1;
02063 }
02064 
02065 
02066 
02068 int ppm_init_retina_plot(int left_offset)
02069 {
02070   const int width = ppm_scaled_retina_plot_width();
02071   
02072   if (!canExtendWidthTo(left_offset + width))
02073     return 0;
02074 
02075   draw_retina_unit(Black,0,0,True,left_offset); /* Reset */
02076 
02077   return width;
02078 }
02079 
02080 
02081 
02083 int ppm_init_cortex_plot(int left_offset)
02084 {
02085   const int width = ppm_scaled_cortex_plot_width();
02086   
02087   if (!canExtendWidthTo(left_offset + width))
02088     return 0;
02089 
02090   draw_cortex_unit(Black,0,0,True,left_offset); /* Reset */
02091 
02092   return width;
02093 }
02094 
02095 
02096 
02098 int ppm_init_histogram_plot(int symmetric, int left_offset)
02099 {
02100   const int width = ppm_scaled_histogram_plot_width(or_num_angles,ppm_histo_scale_factor);
02101   
02102   if (!canExtendWidthTo(left_offset + width))
02103     return 0;
02104 
02105   draw_histogram_bin((symmetric? -1 : 0),0,True,False,left_offset); /* Reset */
02106 
02107   return width;
02108 }
02109 
02110 
02111 
02112 /********************************************************************************/
02113 /* Drawing primitives */
02114 /*********************************************************************************/
02115 
02116 
02117 void draw_rectangle( XColor color, 
02118                      int upper_left_x, int upper_left_y, 
02119                      int x_pixels,     int y_pixels )
02120 {
02121   int x,y;
02122 
02123   for(x=upper_left_x; x < upper_left_x+x_pixels; x++)
02124     for (y=upper_left_y; y <upper_left_y+y_pixels; y++)
02125       ppm_draw_pixel(x,y,color);
02126 }
02127 
02128 
02129 
02130 void draw_horizontal_line( XColor color, int l_i, int h_i, int j )
02131 {
02132   int i;
02133   for( i=l_i; i <= h_i; i++)   
02134     ppm_draw_pixel(i,j,color);
02135 }
02136 
02137 
02138 void draw_vertical_line( XColor color, int i, int l_j, int h_j )
02139 {
02140   int j;
02141   for( j=l_j; j <= h_j; j++)
02142     ppm_draw_pixel(i,j,color);
02143 }
02144 
02145 
02146 
02151 int draw_box_outline( XColor color, int xl, int xh, int yl, int yh, int width )
02152 {
02153   int p;
02154   
02155   for (p = 0; p < width ; p++) {
02156     draw_vertical_line(  color, xl-p,       yl-p, yh+p);    /* Left */
02157     draw_vertical_line(  color,       xh+p, yl-p, yh+p);    /* Right */
02158     draw_horizontal_line(color, xl-p, xh+p, yl-p);          /* Top */
02159     draw_horizontal_line(color, xl-p, xh+p,        yh+p);   /* Bottom */
02160   }
02161   
02162   return 0;
02163 }
02164 
02165 
02166 
02172 int draw_box_outline_on_retina( double centeri, double centerj, double radius, int left_offset)
02173 {
02174   const int npixels_perunit = ppm_scaled_retina_unit_pixels();
02175   const int height_offset   = ppm_scaled_retina_height_offset();
02176   
02177   const double lowi         = MAX(centeri-radius,ppm_start_x);
02178   const double highi        = MIN(centeri+radius,ppm_end_x  );
02179   const double lowj         = MAX(centerj-radius,ppm_start_y);
02180   const double highj        = MIN(centerj+radius,ppm_end_y  );
02181 
02182   const int xl              = left_offset   + (int)((lowi -ppm_start_x) * npixels_perunit) -1;
02183   const int xh              = left_offset   + (int)((highi-ppm_start_x) * npixels_perunit)   ;
02184   const int yl              = height_offset + (int)((lowj -ppm_start_y) * npixels_perunit) -1;
02185   const int yh              = height_offset + (int)((highj-ppm_start_y) * npixels_perunit)   ;
02186 
02187   /* Make color contrast with what's inside the box */
02188   XColor color;
02189   color = ppm_get_drawing_color(xl+1, xh-1, yl+1, yh-1, ppm_foreground_color );
02190 
02191   draw_box_outline(color,xl,xh,yl,yh,ppm_outline_width);
02192   
02193   return ppm_scaled_retina_plot_width();
02194 }
02195 
02196 
02197 
02203 int draw_box_outline_on_cortex( double centeri, double centerj, double radius, int left_offset)
02204 {
02205   const int npixels_perunit = ppm_scaled_cortex_unit_pixels();
02206   const int height_offset   = ppm_scaled_cortex_height_offset();
02207 
02208   const double lowi         = MAX(centeri-radius,ppm_start_i);
02209   const double highi        = MIN(centeri+radius,ppm_end_i);
02210   const double lowj         = MAX(centerj-radius,ppm_start_j);
02211   const double highj        = MIN(centerj+radius,ppm_end_j);
02212 
02213   const int xl              = left_offset   + (int)((lowi -ppm_start_i) * npixels_perunit) -1;
02214   const int xh              = left_offset   + (int)((highi-ppm_start_i) * npixels_perunit)   ;
02215   const int yl              = height_offset + (int)((lowj -ppm_start_j) * npixels_perunit) -1;
02216   const int yh              = height_offset + (int)((highj-ppm_start_j) * npixels_perunit)   ;
02217 
02218   /* Make color contrast with what's inside the box */
02219   XColor color;
02220   color = ppm_get_drawing_color(xl+1, xh-1, yl+1, yh-1, ppm_foreground_color );
02221 
02222   draw_box_outline(color,xl,xh,yl,yh,ppm_outline_width);
02223   
02224   return ppm_scaled_cortex_plot_width();
02225 }
02226 
02227 
02228 
02229 void draw_retina_unit( XColor color, int x, int y, int reset_values, int left_offset)
02230 {
02231   static int npixels_perunit=0,height_offset=0;
02232   
02233   if (reset_values) {
02234     npixels_perunit = ppm_scaled_retina_unit_pixels();
02235     height_offset   = ppm_scaled_retina_height_offset();
02236   }
02237   else {
02238     const int square_left_offset   = left_offset   + (x-ppm_start_x)*npixels_perunit;
02239     const int square_height_offset = height_offset + (y-ppm_start_y)*npixels_perunit;
02240     
02241     draw_rectangle (color, square_left_offset, square_height_offset, 
02242                     npixels_perunit, npixels_perunit);
02243   }
02244 }
02245 
02246 
02247 
02248 void draw_cortex_unit( XColor color, int i, int j, int reset_values, int left_offset)
02249 {
02250   static int npixels_perunit=0,height_offset=0;
02251   
02252   if (reset_values) {
02253     npixels_perunit = ppm_scaled_cortex_unit_pixels();
02254     height_offset   = ppm_scaled_cortex_height_offset();
02255   }
02256   else {
02257     const int square_left_offset   = left_offset   + (i-ppm_start_i)*npixels_perunit;
02258     const int square_height_offset = height_offset + (j-ppm_start_j)*npixels_perunit;
02259     
02260     draw_rectangle (color, square_left_offset, square_height_offset, 
02261                     npixels_perunit, npixels_perunit);
02262   }
02263 }
02264 
02265 
02266 
02267 void draw_histogram_bin( double value, int j, int reset_values, int draw_zero, int left_offset)
02268 {
02269   static int npixels_perunit=0,scaled_height=0,scaled_width=0,zero_pixel=0,bin_width=0,height_offset=0;
02270   static double val_scale=0;
02271   static XColor bg,fg,color;
02272   
02273   if (reset_values) {
02274     npixels_perunit = ppm_scaled_histogram_bin_pixels(or_num_angles);
02275     scaled_height   = ppm_scaled_histogram_plot_height(or_num_angles);
02276     scaled_width    = ppm_scaled_histogram_plot_width(or_num_angles,ppm_histo_scale_factor); 
02277     height_offset   = ppm_scaled_histogram_height_offset(or_num_angles);
02278     zero_pixel      = (value < 0 ? left_offset+scaled_width/2 : left_offset);
02279     bin_width       = (value < 0 ? scaled_width/2+1           : scaled_width);
02280     val_scale       = scaled_width*100/ppm_histo_percentage_range;
02281     bg              = (ppm_paper_based_colors ? White : Black);
02282     fg              = (ppm_paper_based_colors ? Black : White);
02283 
02284     if (scaled_height < or_num_angles)
02285       ipc_notify(IPC_ONE,IPC_WARNING,"Cortex plot height is insufficient to show angle histogram (%d < %d)",
02286                  scaled_height,or_num_angles);
02287   }
02288   else {
02289     /* Correct for flipped representation when picking plot location */
02290     const int square_height_offset = height_offset   + (or_num_angles-1-j)*npixels_perunit;
02291 
02292     /* Clear background first */
02293     draw_rectangle (bg, left_offset, square_height_offset, 
02294                     scaled_width,    npixels_perunit);
02295 
02296     hsv2rgb(OR_PREF_TO_DEGREES(j)/180, 1.0, 1.0, &color);
02297 
02298     /*
02299       Draw bin
02300 
02301       May allow up to one pixel of overdrawing, just to be sure
02302       not to miss one the other way
02303     */
02304     if (value >= 0)
02305       draw_rectangle(color, zero_pixel,
02306                      square_height_offset, 
02307                      MIN((int)(value*val_scale),bin_width),
02308                      npixels_perunit);
02309     else
02310       draw_rectangle(color, zero_pixel-MIN((int)(-value*val_scale),bin_width),
02311                      square_height_offset, 
02312                      MIN((int)(-value*val_scale),bin_width),
02313                      npixels_perunit);
02314 
02315     /* Draw point for zero */
02316     if (draw_zero)
02317       draw_rectangle (fg, zero_pixel, square_height_offset, 
02318                       1, npixels_perunit);
02319 
02320   }
02321 }
02322 
02323 
02324 
02331 int isInsideCircle( int i, int j, int centeri, int centerj, 
02332                     double radius, double dummyf, void *dummyv)
02333 {
02334   (void)dummyf; (void)dummyv; /* ignored */
02335 
02336   return WITHIN_RADIUS(i-centeri,j-centerj,(radius+circular_radius_trim)*(radius+circular_radius_trim));
02337 }
02338 
02339 
02340 
02347 int inhWeightAbove( int i, int j, int ui, int uj, 
02348                     double radius, double threshold, void *dummyv)
02349 {  
02350   const int idx = LAT_INDEX(ui,uj,i,j,inh_rad,inh_array_width);
02351   (void)radius; /* ignored */
02352   (void)dummyv; /* ignored */
02353   
02354   /* Treat out-of-bounds as just inactive */
02355   if ( (i<0) || (i>N-1) || (j<0) || (j>N-1) ||     
02356        (idx < 0) || (idx >= lat_inh_dimension) )
02357     return 0;
02358   else
02359     return (cortex_map[ui][uj].lat_inh_wts[idx] > threshold );  
02360 }
02361 
02362 
02363 
02365 int isAlwaysInside( int i,  int j,  int centeri, int centerj, 
02366                     double radius,  double dummyf,  void *dummyv)
02367 {
02368   /* Ignores all parameters */
02369   (void)i; (void)j; (void)centeri; (void)centerj; (void)radius;
02370   (void)dummyf; (void)dummyv; 
02371 
02372   return 1;
02373 }
02374 
02375 
02376 
02381 int draw_outline_on_retina( int ui, int uj, int radius, int left_offset, ppm_inside_test_ptr testfn)
02382 {
02383   const int lowi          = MAX(ui-radius,ppm_start_x);
02384   const int highi         = MIN(ui+radius,ppm_end_x-1);
02385   const int lowj          = MAX(uj-radius,ppm_start_y);
02386   const int highj         = MIN(uj+radius,ppm_end_y-1);
02387 
02388   draw_outline(ppm_foreground_color,
02389                lowi,highi, lowj,highj, ppm_start_x, ppm_start_y,
02390                left_offset,
02391                ppm_scaled_retina_height_offset(),
02392                ppm_scaled_retina_unit_pixels(),
02393                ppm_outline_width,
02394                testfn, ui, uj, radius, 0, NULL); 
02395 
02396   return ppm_scaled_retina_plot_width();
02397 }
02398 
02399 
02400 
02405 int draw_outline_on_cortex( int ui, int uj, int radius, int left_offset, ppm_inside_test_ptr testfn)
02406 {
02407   const int lowi          = MAX(ui-radius,ppm_start_i);
02408   const int highi         = MIN(ui+radius,ppm_end_i-1);
02409   const int lowj          = MAX(uj-radius,ppm_start_j);
02410   const int highj         = MIN(uj+radius,ppm_end_j-1);
02411 
02412   draw_outline(ppm_foreground_color,
02413                lowi,highi, lowj,highj, ppm_start_i, ppm_start_j,
02414                left_offset,
02415                ppm_scaled_cortex_height_offset(),
02416                ppm_scaled_cortex_unit_pixels(),
02417                ppm_outline_width,
02418                testfn, ui, uj, radius, ppm_weight_threshold, NULL); 
02419 
02420   return ppm_scaled_cortex_plot_width();
02421 }
02422 
02423 
02424 
02425 int draw_outline( 
02426  XColor color,
02427 
02428  int lowi, int highi, int lowj, int highj, int starti, int startj,
02429  int left_offset, int height_offset, int npixels_perunit,  int outline_width,
02430 
02431  ppm_inside_test_ptr testfn,
02432  int int1, int int2, double float1, double float2, void *otherparams 
02433 )
02434 {
02435   int i,j; 
02436 
02437   color = ppm_get_drawing_color(
02438                                 left_offset   +  ( lowi-starti)    * npixels_perunit - 1,
02439                                 left_offset   + ((highi-starti)+1) * npixels_perunit    ,
02440                                 height_offset +  ( lowj-startj)    * npixels_perunit - 1,
02441                                 height_offset + ((highj-startj)+1) * npixels_perunit    ,
02442                                 color
02443                                 );
02444 
02445   for (i=lowi; i <=highi; i++) 
02446     for (j=lowj; j<=highj; j++) {
02447       
02448       const int xl = left_offset   +  (i-starti)    * npixels_perunit - 1;
02449       const int xh = left_offset   + ((i-starti)+1) * npixels_perunit    ;
02450       const int yl = height_offset +  (j-startj)    * npixels_perunit - 1;
02451       const int yh = height_offset + ((j-startj)+1) * npixels_perunit    ;
02452       
02453       /* If unit is inside, check each of its four sides to see if they need outlining */
02454       if ( (testfn)( i, j,   int1, int2, float1, float2, otherparams) ) {
02455         /* Whether each of the surrounding units is inside
02456            Checks against lowi and highi so that units outside of the box boundaries are 
02457            always considered false.
02458          */
02459         const int left_in   = ((i-1<lowi) ?0:(testfn)(i-1, j, int1, int2, float1, float2, otherparams));
02460         const int right_in  = ((i+1>highi)?0:(testfn)(i+1, j, int1, int2, float1, float2, otherparams));
02461         const int top_in    = ((j-1<lowj) ?0:(testfn)(i, j-1, int1, int2, float1, float2, otherparams));
02462         const int bottom_in = ((j+1>highj)?0:(testfn)(i, j+1, int1, int2, float1, float2, otherparams));
02463 
02464         int p;
02465 
02466         for (p = 0; p < outline_width ; p++) {
02467           /* The logic here is convoluted but it seems to work... */
02468           if (!left_in)
02469             draw_vertical_line(  color, xl-p,             yl+(top_in?1:-p), yh+(bottom_in?-1:p));
02470           if (!right_in)
02471             draw_vertical_line(  color,          xh+p,    yl+(top_in?1:-p), yh+(bottom_in?-1:p));
02472           if (!top_in)
02473             draw_horizontal_line(color, xl+1-(left_in?0:p), xh-1+(right_in?0:p),  yl-p);
02474           if (!bottom_in)
02475             draw_horizontal_line(color, xl+1-(left_in?0:p), xh-1+(right_in?0:p),  yh+p);
02476         }
02477       }
02478     }
02479 
02480   return 0;
02481 }
02482 
02483 
02484 
02485 /******************************************************************************/
02486 /******************** Line drawing routines ************************************/
02487 /******************************************************************************/
02488 
02490 void ppm_draw_pixel(int xi, int yi, XColor color)     
02491 {                          
02492   if ( (yi < 0) || (yi >= IMG_SIZE_Y) || 
02493        (xi < 0) || (xi >= IMG_SIZE_X)    )
02494     return;
02495   
02496   image[xi][yi] = color;
02497 } 
02498 
02499 
02500 
02510 void BhamXDrawLine_PointsAlongX(int xA, int yA, int xB, int yB, int direction, XColor color, int thickness)
02511 {
02512   const int dx    = xB - xA;
02513   const int dy    = yB - yA;
02514   const int incE  = (dy<<1);           /* Q       */
02515   const int incSE = incE   - (dx<<1);  /* Q + R   */
02516 
02517   int d = incE   -  dx;      /* Q + R/2 */
02518   int x, y=yA;
02519   int i;
02520   
02521   ppm_draw_pixel(xA,yA,color); /* set starting point */
02522   for (x = xA; x < xB; x++) {
02523     if (d <= 0 ) {/* choose E */
02524       d += incE;
02525     }
02526     else {       /* choose SE */
02527       d += incSE;
02528       y += direction;
02529     }
02530     for (i=0; i<thickness; i++)
02531       ppm_draw_pixel(x, y+(direction>0? i : -i),color);
02532   }
02533 }
02534 
02535 
02536 
02537 void BhamXDrawLine_PointsAlongY(int xA, int yA, int xB, int yB, int direction, XColor color, int thickness)
02538 {
02539   const int dx    = xB - xA;
02540   const int dy    = yB - yA;
02541   const int incE  = (dx<<1);           /* Q       */
02542   const int incSE = incE   - (dy<<1);  /* Q + R   */
02543 
02544   int d = incE   -  dy;      /* Q + R/2 */
02545   int y, x=xA, yp=yA;
02546   int i;
02547   
02548   ppm_draw_pixel(xA,yA,color); /* set starting point */
02549   for (y = yA; y < yB; y++) {
02550     yp += direction;
02551     if (d <= 0 ) {/* choose E */
02552       d += incE;
02553     }
02554     else {       /* choose SE */
02555       d += incSE;
02556       x += 1;
02557     }
02558     for (i=0; i<thickness; i++)
02559       ppm_draw_pixel(x+ (direction>0? i : -i),yp,color);
02560   }
02561 }
02562 
02563 
02568 void ppm_draw_line(int x_1, int y_1, int x_2, int y_2, XColor color, int thickness)
02569 {
02570   int dx,dy;        /* distances in x and y directions */
02571   int xA,xB,yA,yB;  /* canonical form of coordinates   */
02572   int y, direction;
02573   
02574   /* Swap endpoints if line wasn't specifed from left to right */
02575   dx = x_2 - x_1;
02576   if (dx >= 0)  { xA = x_1; yA = y_1;   xB = x_2; yB = y_2; }
02577   else          { xA = x_2; yA = y_2;   xB = x_1; yB = y_1; }
02578   
02579   
02580   /* Flip right endpoint across horizontal axis if it is above it 
02581      (leaves lower right quadrant only) 
02582   */
02583   dy = yB - yA;
02584   if (dy > 0)   { direction =  1;  y =         yB; }
02585   else          { direction = -1;  y = (yA<<1)-yB; }
02586     
02587 
02588   /* Iterate along the axis which changes the most */
02589   if (abs(dy) > abs(dx))  BhamXDrawLine_PointsAlongY(xA, yA, xB, y,  direction, color, thickness);
02590   else                    BhamXDrawLine_PointsAlongX(xA, yA, xB, y,  direction, color, thickness);
02591 }
02592 
02593 
02594 
02598 void ppm_draw_line_at_angle(int cx, int cy, double angle, double length, double width, XColor color)
02599 {
02600   angle=M_PI-angle; /* Correct for flipped representation */
02601   
02602   /* Only draws lines shorter than one pixel if demanded to */
02603   if (ppm_always_draw_point || (length > 1.0)) {
02604     const int xoffset = (int)((length/2)*cos(angle));
02605     const int yoffset = (int)((length/2)*sin(angle));
02606     
02607     color = ppm_get_drawing_color(cx+xoffset, cy+yoffset, cx-xoffset, cy-yoffset, color);
02608 
02609     ppm_draw_line( cx+xoffset, cy+yoffset, cx-xoffset, cy-yoffset, color, 1);
02610     if (width>1) {
02611       /* This is a hack to get a filled rectangle without having to handle polygons */
02612       double delt;
02613       for (delt=0; delt<width/2; delt+=0.5) {
02614         const int deltax=(int)(delt*cos(M_PI/2-angle));
02615         const int deltay=(int)(delt*sin(M_PI/2-angle));
02616         ppm_draw_line( cx+xoffset-deltax, cy+yoffset+deltay, cx-xoffset-deltax, cy-yoffset+deltay, color, 2);
02617         ppm_draw_line( cx+xoffset+deltax, cy+yoffset-deltay, cx-xoffset+deltax, cy-yoffset-deltay, color, 2);
02618       }
02619     }
02620   }
02621 }

Generated at Mon Aug 21 00:30:55 2000 for RF-LISSOM by doxygen1.2.1 written by Dimitri van Heesch, © 1997-2000