1 #include "mrilib.h"
2 #include "coxplot.h"
3 
4 #undef DEBUG
5 
6 /*--------------------------------------------------------------------------
7   Routines to render a memplot into an MRI_rgb image.
8   The assumption is that the plot is over the "object" region [0,1]x[0,1],
9   which is rendered into the actual image [0..nx-1]x[0..ny-1].
10   The actual rendering is done by functions in mri_drawing.c,
11   which were lifted from the PPM library, with some light edits.
12 ----------------------------------------------------------------------------*/
13 
14 /*--------------------------------------------------------------------------*/
15 /*! Set a sub-box within a window into which the next RGB plot should
16    be scaled.  (0,0,0,0) args means use the whole window.  After
17    each drawing (memplot_to_RGB_sef), will be reset to the whole window
18    anyway.
19 ----------------------------------------------------------------------------*/
20 
21 static int box_xbot=0 , box_xtop=0 ,
22            box_ybot=0 , box_ytop=0  ;
23 
24 static int do_thick=0 ;
memplot_to_mri_set_dothick(int dt)25 void memplot_to_mri_set_dothick( int dt ){ do_thick = dt ; }
26 static int do_freee=0 ;
memplot_to_mri_set_dofreee(int df)27 void memplot_to_mri_set_dofreee( int df ){ do_freee = df ; }
28 
set_memplot_RGB_box(int xbot,int ybot,int xtop,int ytop)29 void set_memplot_RGB_box( int xbot, int ybot, int xtop, int ytop )
30 {
31    if( xbot < xtop && ybot < ytop ){
32      box_xbot = xbot ; box_ybot = ybot ;
33      box_xtop = xtop ; box_ytop = ytop ;
34    } else {
35      box_xbot = box_ybot = box_xtop = box_ytop = 0 ;
36    }
37 }
38 
39 /*--------------------------------------------------------------------------*/
40 /*! Actually do the rendering of a memplot into an existing RGB image.
41   - That is, we are drawing on top of whatever is there already.
42   - Plotting will start with line #start and go to #end-1.
43   - If end <= start, will do from #start to the last one in the plot.
44   - To do all lines, set start=end=0.
45   - "freee" controls whether the aspect ratio will be free to vary (!= 0),
46     or will be fixed (==0).
47   - 18 Sep 2001: adapted from X11 routines in coxplot/plot_x11.c
48   - 23 Mar 2002: actually tested for the first time
49 ----------------------------------------------------------------------------*/
50 
memplot_to_RGB_sef(MRI_IMAGE * im,MEM_plotdata * mp,int start,int end,int freee)51 void memplot_to_RGB_sef( MRI_IMAGE *im , MEM_plotdata *mp ,
52                          int start , int end , int freee    )
53 {
54    byte rrr=0,ggg=0,bbb=0 ;
55    int ii , nline , same ;
56    float old_thick , old_color , new_color , new_thick , sthick=0.0f ;
57    float scal,xscal,yscal , xoff,yoff ;
58    int x1,y1 , x2,y2 ;
59    int x1_old=-666,y1_old=-666 , x2_old=-666,y2_old=-666 ; float sthick_old=-666.f ;
60    int skip ;
61 
62 ENTRY("memplot_to_RGB_sef") ;
63 
64    /*--- check for madness ---*/
65 
66    if( im == NULL || im->kind != MRI_rgb || mp == NULL ) EXRETURN ;
67 
68    if( start < 0 ) start = 0 ;
69 
70    nline = MEMPLOT_NLINE(mp) ;
71    if( nline < 1 || start >= nline ) EXRETURN ;
72 
73    if( end <= start || end > nline ) end = nline ;
74 
75    /*--- compute scaling from memplot objective
76          coordinates to RGB window coordinates  ---*/
77 
78    if( box_xbot >= box_xtop || box_ybot >= box_ytop ){
79 
80       xscal = im->nx / mp->aspect ; /* aspect = x-axis objective size */
81       yscal = im->ny / 1.0f ;       /* 1.0    = y-axis objective size */
82       xoff  = yoff = 0.499f ;
83 
84    } else {  /* scale to a given sub-box in the window */
85 
86       xscal = box_xtop - box_xbot ;
87       yscal = box_ytop - box_ybot ;
88       xoff  = box_xbot + 0.499f   ;
89       yoff  = box_ybot + 0.499f   ;
90    }
91 
92    if( !freee && !do_freee ){              /* no aspect freedom ==> */
93       if( yscal < xscal ) xscal = yscal ;  /* use smaller scaling   */
94       else                yscal = xscal ;
95    }
96    scal = sqrt(fabs(xscal*yscal)) ;
97 
98    old_color = -1.0f ;            /* these don't occur naturally */
99    old_thick = -THCODE_INVALID ;
100 
101    /*--- loop over lines, scale and plot ---*/
102 
103    mri_draw_opacity( 1.0f ) ;
104 
105 #ifdef DEBUG
106 INFO_message("memplot_to_RGB_sef from %d to %d",start,end) ;
107 #endif
108 
109    for( ii=start ; ii < end ; ii++ ){
110 
111       skip = 0 ;
112 
113       /* check if need to change color or thickness of line */
114 
115       new_color = MEMPLOT_COL(mp,ii) ;
116       if( new_color != old_color ){
117          float rr=COL_TO_RRR(new_color) ,
118                gg=COL_TO_GGG(new_color) , bb=COL_TO_BBB(new_color) ;
119 
120          rrr = ZO_TO_TFS(rr) ; ggg = ZO_TO_TFS(gg) ; bbb = ZO_TO_TFS(bb) ;
121          old_color = new_color ;
122 #ifdef DEBUG
123 ININFO_message("Changing color to %f->%d %f->%d %f->%d\n",rr,(int)rrr,gg,(int)ggg,bb,(int)bbb) ;
124 #endif
125       }
126 
127       new_thick = MEMPLOT_TH(mp,ii) ;
128       if( new_thick < 0.0 ){               /* special negative thickness codes */
129          int thc = (int)(-new_thick) ;     /* mean special drawing actions */
130          switch( thc ){
131             case THCODE_FRECT:
132             case THCODE_RECT:{        /* rectangle */
133                int xb,yb , xt,yt ;
134                int w,h ;
135                x1 = rint( xoff + xscal * MEMPLOT_X1(mp,ii)         ) ;
136                x2 = rint( xoff + xscal * MEMPLOT_X2(mp,ii)         ) ;
137                y1 = rint( yoff + yscal * (1.0 - MEMPLOT_Y1(mp,ii)) ) ;
138                y2 = rint( yoff + yscal * (1.0 - MEMPLOT_Y2(mp,ii)) ) ;
139                if( x1 < x2 ){ xb=x1; xt=x2; } else { xb=x2; xt=x1; }
140                if( y1 < y2 ){ yb=y1; yt=y2; } else { yb=y2; yt=y1; }
141                w = xt-xb+1 ; h = yt-yb+1 ;
142 #ifdef DEBUG
143 ININFO_message("drawfilledrectangle") ;
144 #endif
145                mri_drawfilledrectangle( im , xb,yb , w,h , rrr,ggg,bbb ) ;
146                skip = 1 ;
147             }
148             break ;
149 
150             case THCODE_BALL:
151             case THCODE_CIRC:{
152                int xcor,ycor , xcen,ycen , rad ; float xrad,yrad ;
153                unsigned int ww, hh ;
154                xcen = rint(xoff + xscal * MEMPLOT_X1(mp,ii)         );
155                ycen = rint(yoff + yscal * (1.0 - MEMPLOT_Y1(mp,ii)) );
156                xrad = xscal * MEMPLOT_X2(mp,ii) ;
157                yrad = yscal * MEMPLOT_X2(mp,ii) ; rad = rintf(sqrtf(xrad*yrad)) ;
158 #ifdef DEBUG
159 ININFO_message("drawcircle") ;
160 #endif
161                mri_drawcircle( im , xcen,ycen , rad, rrr,ggg,bbb , (thc==THCODE_BALL) ) ;
162                skip = 1 ;
163             }
164             break ;
165 
166             case THCODE_OPAC:{        /* opacity [22 Jul 2004] */
167 #ifdef DEBUG
168 ININFO_message("set opacity %f",MEMPLOT_X1(mp,ii)) ;
169 #endif
170                mri_draw_opacity( MEMPLOT_X1(mp,ii) ) ;
171                skip = 1 ;
172             }
173             break ;
174          }
175 
176       } else if( new_thick != old_thick ){ /* normal case: change line thickness */
177 
178          old_thick = new_thick ;  /* thickness not used at this time */
179          sthick = new_thick * scal ; /* sthick = MIN(sthick,9.0f) ; */
180 #ifdef DEBUG
181 ININFO_message("set thick %f",sthick) ;
182 #endif
183 
184       }
185 
186       /* scale line endpoint coords to ints (also see zzphph.f) */
187 
188       if( !skip ){
189         float a1 = MEMPLOT_X1(mp,ii) ;
190         float a2 = MEMPLOT_X2(mp,ii) ;
191         float b1 = (1.0f - MEMPLOT_Y1(mp,ii)) ;
192         float b2 = (1.0f - MEMPLOT_Y2(mp,ii)) ;
193 
194         /* scale from objective coords to pixels */
195         x1 = (int)( xoff + xscal * a1 ) ; x2 = (int)( xoff + xscal * a2 ) ;
196         y1 = (int)( yoff + yscal * b1 ) ; y2 = (int)( yoff + yscal * b2 ) ;
197 
198         /* draw it */
199 #ifdef DEBUG
200 ININFO_message("drawline x1=%d y1=%d  x2=%d y2=%d rrr=%d ggg=%d bbb=%d",x1,y1,x2,y2,(int)rrr,(int)ggg,(int)bbb) ;
201 #endif
202         mri_drawline( im , x1,y1 , x2,y2 , rrr,ggg,bbb ) ;
203 
204         /* the following code "cheats" to draw thick lines,
205            by drawing multiple 1-pixel lines around the central line */
206 
207         if( do_thick && sthick >= 1.0f && (x1 != x2 || y1 != y2) ){  /* 06 Dec 2007 */
208           float da=a2-a1 , db=b2-b1 , dl=new_thick/sqrtf(da*da+db*db) ;
209           float c1,c2 , d1,d2 ;
210           int jj , ss=(int)(4.44f*sthick+0.444f) ; /* ss = num parallel lines */
211 
212           /* spacing for the parallel lines (in objective space) */
213 
214           dl /= (2*ss) ; da *= dl ; db *= dl ; ss = MAX(ss,2) ;
215 #if 1
216           /* this code draws filled circles at the endpoints, to
217              minimize possible gaps where thick lines meet at a sharp corner */
218 
219           if( sthick >= 2.0f && sthick == sthick_old ){  /* 01 May 2012 */
220             int rad = (int)(0.505f*sthick+0.444f) ;
221             if( x1 == x2_old && y1 == y2_old ){
222               mri_drawcircle( im , x1,y1 , rad, rrr,ggg,bbb , 1 ) ;
223             }
224             else if( x2 == x1_old && y2 == y1_old ){
225               mri_drawcircle( im , x2,y2 , rad, rrr,ggg,bbb , 1 ) ;
226             }
227           }
228           x1_old = x1; x2_old = x2; y1_old = y1; y2_old = y2; sthick_old = sthick;
229 #endif
230           for( jj=-ss ; jj <= ss ; jj++ ){  /* multiple parallel line segments: */
231             if( jj == 0 ) continue ;        /* cheap, but it works, so there!! */
232             c1 = a1 + jj*db ; c2 = a2 + jj*db ;  /* offsets in x direction */
233             d1 = b1 - jj*da ; d2 = b2 - jj*da ;  /* offsets in y direction */
234             /* scale from objective coords to pixels */
235             x1 = (int)( xoff + xscal * c1 ) ; x2 = (int)( xoff + xscal * c2 ) ;
236             y1 = (int)( yoff + yscal * d1 ) ; y2 = (int)( yoff + yscal * d2 ) ;
237             mri_drawline( im , x1,y1 , x2,y2 , rrr,ggg,bbb ) ;
238           }
239         }
240       }
241    }
242 
243 #ifdef DEBUG
244 ININFO_message("----- EXIT -----") ;
245 #endif
246    set_memplot_RGB_box(0,0,0,0) ; /* clear box */
247    EXRETURN ;
248 }
249 
250 /*-----------------------------------------------------------------------*/
251 # undef  BOr
252 # undef  BOg
253 # undef  BOb
254 # define BOr(i,j) bout[3*((i)+(j)*nxout)+0]
255 # define BOg(i,j) bout[3*((i)+(j)*nxout)+1]
256 # define BOb(i,j) bout[3*((i)+(j)*nxout)+2]
257 # undef  BIr
258 # undef  BIg
259 # undef  BIb
260 # define BIr(i,j) ((unsigned int)bin[3*((i)+(j)*nxin)+0])
261 # define BIg(i,j) ((unsigned int)bin[3*((i)+(j)*nxin)+1])
262 # define BIb(i,j) ((unsigned int)bin[3*((i)+(j)*nxin)+2])
263 
264 /* this func is used to scale down RGB image size
265    by factor of 2, which in turn is used to anti-alias the line drawing;
266    the algorithm used is simple averaging over each output pixel's 2x2 region */
267 
mri_downsize_by2(MRI_IMAGE * imin)268 MRI_IMAGE * mri_downsize_by2( MRI_IMAGE *imin )
269 {
270    MRI_IMAGE *imout ; int nxin,nyin , nxout,nyout , ii,jj,i2,j2 ;
271    byte *bin , *bout ; unsigned int val ;
272 
273    if( imin == NULL || imin->kind != MRI_rgb ) return NULL ;
274 
275    nxin = imin->nx ; nyin  = imin->ny ;
276    nxout = nxin /2 ; nyout = nyin / 2 ;
277 
278    imout = mri_new( nxout , nyout , MRI_rgb ) ;
279    bout  = MRI_RGB_PTR(imout) ;
280    bin   = MRI_RGB_PTR(imin) ;
281 
282    for( jj=0 ; jj < nyout ; jj++ ){  /* loop over output pixels */
283      j2 = 2*jj ;                     /* input pixel index = double output index */
284      for( ii=0 ; ii < nxout ; ii++ ){
285        i2 = 2*ii ;
286        val = BIr(i2,j2)+BIr(i2+1,j2)+BIr(i2,j2+1)+BIr(i2+1,j2+1)+1; BOr(ii,jj) = (byte)(val >> 2);
287        val = BIg(i2,j2)+BIg(i2+1,j2)+BIg(i2,j2+1)+BIg(i2+1,j2+1)+1; BOg(ii,jj) = (byte)(val >> 2);
288        val = BIb(i2,j2)+BIb(i2+1,j2)+BIb(i2,j2+1)+BIb(i2+1,j2+1)+1; BOb(ii,jj) = (byte)(val >> 2);
289      }
290    }
291 
292    return imout ;
293 }
294 
295 # undef  BOr
296 # undef  BOg
297 # undef  BOb
298 # undef  BIr
299 # undef  BIg
300 # undef  BIb
301 
302 /*-----------------------------------------------------------------------*/
303 
304 #undef  IMSIZ_DEF
305 #define IMSIZ_DEF 1024
306 #undef  IMSIZ_MAX
307 #define IMSIZ_MAX 8192
308 
memplot_to_mri(MEM_plotdata * mp)309 static MRI_IMAGE * memplot_to_mri( MEM_plotdata *mp )  /* 05 Dec 2007 */
310 {
311    MRI_IMAGE *im ; int nx , ny , imsiz ;
312    byte *imp ;
313    int did_dup=0 ;
314 
315    if( mp == NULL || MEMPLOT_NLINE(mp) < 1 ) return NULL ;
316 
317    /* set image dimensions */
318 
319    imsiz = (int)AFNI_numenv("AFNI_1DPLOT_IMSIZE") ;  /* might be 0 */
320         if( imsiz <       128 ) imsiz = IMSIZ_DEF ;
321    else if( imsiz > IMSIZ_MAX ) imsiz = IMSIZ_MAX ;
322 
323    if( mp->aspect > 1.0f ){
324      nx = imsiz ; ny = nx / mp->aspect ;
325    } else {
326      nx = imsiz * mp->aspect ; ny = imsiz ;
327    }
328 
329    /* for smaller output images, make image twice the size,
330       render lines into it, then scale down == anti-aliasing == looks better */
331 
332    if( imsiz <= 2048 ){ nx *=2 ; ny *=2 ; did_dup = 1 ; }
333 
334    im = mri_new( nx , ny , MRI_rgb ) ;            /* full of 0s = blacked out */
335    imp = MRI_RGB_PTR(im) ; memset( imp , 255 , 3*nx*ny ) ; /* white-ize image */
336    set_memplot_RGB_box(0,0,0,0) ;
337    do_thick = 1 ;   /* allow thick lines */
338    memplot_to_RGB_sef( im , mp , 0 , 0 , 0 ) ;         /* the actual drawing! */
339    do_thick = 0 ;
340 
341    if( did_dup ){                          /* scale image down to output size */
342      MRI_IMAGE *qim = mri_downsize_by2(im) ; mri_free(im) ; im = qim ;
343    }
344 
345    return im ;
346 }
347 
348 /*-----------------------------------------------------------------------*/
349 /* Functions to write memplot to image files */
350 
memplot_to_jpg(char * fname,MEM_plotdata * mp)351 void memplot_to_jpg( char *fname , MEM_plotdata *mp )  /* 05 Dec 2007 */
352 {
353    MRI_IMAGE *im ;
354 
355    if( fname == NULL || *fname == '\0' ) return ;
356 
357    im = memplot_to_mri( mp ) ; if( im == NULL ) return ;
358    mri_write_jpg(fname,im) ; mri_free(im) ;
359    return ;
360 }
361 
362 /*-----------------------------------------------------------------------*/
363 
memplot_to_png(char * fname,MEM_plotdata * mp)364 void memplot_to_png( char *fname , MEM_plotdata *mp )  /* 05 Dec 2007 */
365 {
366    MRI_IMAGE *im ;
367 
368    if( fname == NULL || *fname == '\0' ) return ;
369 
370    im = memplot_to_mri( mp ) ; if( im == NULL ) return ;
371    mri_write_png(fname,im) ; mri_free(im) ;
372    return ;
373 }
374 
375 /*-----------------------------------------------------------------------*/
376 
memplot_to_pnm(char * fname,MEM_plotdata * mp)377 void memplot_to_pnm( char *fname , MEM_plotdata *mp )  /* 06 Jan 2015 */
378 {
379    MRI_IMAGE *im ;
380 
381    if( fname == NULL || *fname == '\0' ) return ;
382 
383    im = memplot_to_mri( mp ) ; if( im == NULL ) return ;
384    mri_write_pnm(fname,im) ; mri_free(im) ;
385    return ;
386 }
387 
388 /*-----------------------------------------------------------------------*/
389 /* Find the min/max coordinates used in a plot structure
390    (abstract coords in range 0..1, not yet scaled to actual plotting).
391    Error return has out.a >= out.b and/or out.c >= out.d -- 19 Aug 2021
392 *//*---------------------------------------------------------------------*/
393 
memplot_bbox(MEM_plotdata * mp)394 float_quad memplot_bbox( MEM_plotdata *mp )
395 {
396    float_quad out = {0.0f,0.0f,0.0f,0.f} ;
397    float xbot,xtop , ybot,ytop ;
398    float x1,y1,x2,y2 , new_thick ;
399    int ii , nline ;
400 
401    if( mp == NULL ) return out ;
402 
403    nline = MEMPLOT_NLINE(mp) ; if( nline < 1 ) return out ;
404 
405 /* swap to make them ordered */
406 #define SORD(a,b)  if( a > b ){ float t=a; a=b; b=t; } else {}
407 
408    xbot = ybot =  666.0f ;
409    ytop = xtop = -666.0f ;
410 
411    for( ii=0 ; ii < nline ; ii++ ){
412 
413       new_thick = MEMPLOT_TH(mp,ii) ;      /* thickness of line segment */
414 
415       if( new_thick < 0.0f ){              /* special negative thickness codes */
416          int thc = (int)(-new_thick) ;     /* mean special drawing actions */
417          switch( thc ){
418             case THCODE_FRECT:
419             case THCODE_RECT:{        /* rectangle */
420                x1 = MEMPLOT_X1(mp,ii) ; x2 = MEMPLOT_X2(mp,ii) ; SORD(x1,x2) ;
421                y1 = MEMPLOT_Y1(mp,ii) ; y2 = MEMPLOT_Y2(mp,ii) ; SORD(y1,y2) ;
422                xbot = MIN(xbot,x1) ; xtop = MAX(xtop,x2) ;
423                ybot = MIN(ybot,y1) ; ytop = MAX(ytop,y2) ;
424             }
425             break ;
426 
427             case THCODE_BALL:
428             case THCODE_CIRC:{
429                int xcen,ycen , rad ; float xrad,yrad ;
430                unsigned int ww, hh ;
431                xcen = MEMPLOT_X1(mp,ii) ;
432                ycen = MEMPLOT_Y1(mp,ii) ;
433                rad  = fabsf(MEMPLOT_X2(mp,ii)) ;
434                x1   = xcen - rad ; x2 = xcen + rad ;
435                y1   = ycen - rad ; y2 = ycen + rad ;
436                xbot = MIN(xbot,x1) ; xtop = MAX(xtop,x2) ;
437                ybot = MIN(ybot,y1) ; ytop = MAX(ytop,y2) ;
438             }
439             break ;
440 
441             /* other special codes don't actually cause drawing */
442          }
443 
444       } else { /* normal line segment */
445 
446         x1 = MEMPLOT_X1(mp,ii) ; x2 = MEMPLOT_X2(mp,ii) ; SORD(x1,x2) ;
447         y1 = MEMPLOT_Y1(mp,ii) ; y2 = MEMPLOT_Y2(mp,ii) ; SORD(y1,y2) ;
448         xbot = MIN(xbot,x1) ; xtop = MAX(xtop,x2) ;
449         ybot = MIN(ybot,y1) ; ytop = MAX(ytop,y2) ;
450 
451       }
452    }
453 
454    out.a = xbot ; out.b = xtop ;
455    out.c = ybot ; out.d = ytop ;
456    return out ;
457 }
458