Contents of file 'epsimg/epsimg.w':
1 % File: epsimg.w [CWEB source code]
2 % Last change: February 21, 2004
3 % Author: Fredrik Jonsson
4 % Description: CWEB source code for the EPSIMG program. Given a matrix of
5 % numerical values stored in a regular ASCII text file, the
6 % EPSIMG program creates a grey-scale Encapsulated PostScript
7 % image of the matrix using its elements as specification of
8 % the brightness of the corresponding pixels in the image.
9 % For information on the CWEB programming language, see
10 % http://www.literateprogramming.com.
11 % Compilation: Compile this program by using the enclosed Makefile, or use
12 % the blocks of the Makefile as listed in section five of the
13 % documentation (file epsimg.ps or epsimg.pdf). The C source
14 % code (as generated from this CWEB code) conforms to the ANSI
15 % standard for the C programming language (which is equivalent
16 % to the ISO C89 standard for C).
17 %
18 % Copyright (C) 2004, Fredrik Jonsson
19 %
20 \def\version{1.6}
21 \def\lastrevdate{February 21, 2004}
22 \input epsf
23 \font\eightcmr=cmr8
24 \font\tensc=cmcsc10
25 \font\eightcmssq=cmssq8
26 \font\eightcmssqi=cmssqi8
27 \font\twentycmcsc=cmcsc10 at 20 truept
28 \def\epsimg{{\eightcmr EPSIMG\spacefactor1000}}
29 \def\CEE{{\eightcmr C\spacefactor1000}} % The C programming language
30 \def\CWEB{{\eightcmr CWEB\spacefactor1000}} % The CWEB programming language
31 \def\MATLAB{{\eightcmr MATLAB\spacefactor1000}} % The MATLAB ditto
32 \def\endalg{\vrule height 1.4ex width .6ex depth .4ex} % Rectangular bullet
33 %
34 % Define a handy macro for listing the steps of an algorithm.
35 %
36 \newdimen\aitemindent \aitemindent=26pt
37 \newdimen\aitemleftskip \aitemleftskip=36pt
38 \def\aitem[#1]{\smallbreak\noindent\hbox to 10pt{}%
39 \hbox to\aitemindent{\bf #1\hfill}%
40 \hangindent\aitemleftskip\ignorespaces}
41 %
42 % Define a handy macro for the list of program revisions.
43 %
44 \newdimen\citemindent \citemindent=80pt
45 \newdimen\citemleftskip \citemleftskip=90pt
46 \def\citem[#1]{\smallbreak\noindent\hbox to 10pt{}%
47 \hbox to\citemindent{\bf #1\hfill}%
48 \hangindent\citemleftskip\ignorespaces}
49 %
50 % Define a handy macro for listing of operator descriptions.
51 %
52 \newdimen\oitemindent \oitemindent=66pt
53 \newdimen\oitemleftskip \oitemleftskip=76pt
54 \def\oitem[#1]{\smallbreak\noindent\hbox to 10pt{}%
55 \hbox to\oitemindent{\bf #1\hfill}%
56 \hangindent\oitemleftskip\ignorespaces}
57
58 \datethis
59
60 @*Introduction.
61 \vskip 120pt
62 \centerline{\twentycmcsc Epsimg}
63 \vskip 20pt
64 \centerline{Creates grey-scale Encapsulated PostScript images of matrices
65 of numerical data}
66 \vskip 2pt
67 \centerline{(Version \version\ of \lastrevdate)}
68 \vskip 10pt
69 \centerline{Written by Fredrik Jonsson}
70 \vskip 80pt
71 \noindent
72 Given a matrix of floating-point numbers stored in a regular ASCII text file,
73 this \CWEB\footnote{${}^\dagger$}{For information on the \CWEB\ programming
74 language by Donald E.~Knuth, as well as samples of \CWEB\ programs, see
75 {\tt http://www-cs-faculty.stanford.edu/\~\ \kern -5pt knuth/cweb.html}.
76 For general information on literate programming, see
77 {\tt http://www.literateprogramming.com}.} program creates a grey-scale
78 Encapsulated PostScript (EPS) image of the matrix using its elements as
79 specification of the brightness of the corresponding pixels in the image.
80
81 I do by no means claim to have written a program that generates fully optimized
82 Encapsulated PostScript. The output images are in many cases large, and can
83 in many cases be considerably reduced in size, in particular for binary or
84 few-level grayscale images, for which run-length encoding easily can be
85 applied. (In run-length encoding a long row of
86 identical pixels is parametrized as a loop, without the need of individual
87 specification of each pixel.) However, for my purposes it works fine, since
88 I often only is concerned with the evaluation of gray-scale images, generated
89 by mathematical means and often with no a priori specification of the number
90 of intensity levels.
91
92 Of course, there are other ways of generating Encapsulated PostScript images
93 of sampled of simulated data, as for example using the {\tt image()} function
94 of \MATLAB. An advantage with using a stand-alone program, however, is that
95 it is easily incorporated in scripts for batch processing. In addition, the
96 \epsimg\ program is provided free of charge.
97 \bigskip
98 \noindent Copyright \copyright\ Fredrik Jonsson, 2004.
99 All rights reserved.
100 \vfill
101
102 @*Revision history of the program.
103 \medskip
104
105 \citem[2004-01-26]{[v.1.0]} {\tt <jonsson@@uni-wuppertal.de>}\hfill\break
106 First properly working version of the \epsimg\ program. I have now for a
107 longer time had in my mind that it would be useful to write a stand-alone
108 program that is capable of generating Encapsulated PostScript images of
109 data matrices, in similar to the {\tt image()} built-in function of \MATLAB.
110 In particular, I have lately encountered some problems involving the optical
111 analysis of diffraction patterns, and in order to visualize my generated data
112 (without having to use \MATLAB\ every time) I this evening started the coding
113 in \CWEB.
114
115 \citem[2004-01-27]{[v.1.1]} {\tt <jonsson@@uni-wuppertal.de>}\hfill\break
116 Continued with cleaning up the code and adding some features, such as the
117 possibility of letting the program add a frame outlining the bounding box
118 of the Encapsulated PostScript image, and a scaling of the $x$- or $y$-axis
119 to leave the aspect ratio of the square pixels invariant even for non-square
120 input matrices. Also changed the precision of the coordinates and gray scale
121 specifications in order to get really smooth images. However, there still seem
122 to remain some bug that causes the program to refuse to accept data files
123 containing lines with trailing blank spaces and additional line feeds.
124 [Coding finished at 00:45, 2004-01-28]
125
126 \citem[2004-01-28]{[v.1.2]} {\tt <jonsson@@uni-wuppertal.de>}\hfill\break
127 This morning fixed the remaining bug from yesterday, and wrote a basic example
128 ({\tt example1}) as a block in the {\tt Makefile}, using AWK to generate a
129 simple interference pattern that is visualized with the help of the \epsimg\
130 program. Also wrote blocks that provide a proper rescaling of the image
131 width or height whenever either the width or height is larger than their
132 respective maximum values. Wrote an example ({\tt example2}) in the
133 {\tt Makefile} that illustrates this automatic rescaling of the image.
134
135 \citem[2004-01-30]{[v.1.3]} {\tt <jonsson@@uni-wuppertal.de>}\hfill\break
136 This evening (time is now 01:55 Saturday morning) I started to sketch on
137 a partitioning scheme for the reduction of data neccessary to save to disk.
138 For many of my diffraction images, there are large areas that are of equal
139 shade, and since they have considerable extent in the $x$- as well as
140 $y$-direction, a run-length encoding (of the type used in the ancient program
141 for generation of fractals that I wrote together with Tommy Ekola in 1996)
142 of the Encapsulated PostScript will not fix the problem to any greater extent.
143 Therefore, I started formulating a recursive scheme for the partitioning of
144 data into smaller and smaller sub-blocks of the user-supplied matrix, which
145 I for the sake of simplicity so far have assumed to be square, of size
146 $[2^M\times 2^M]$ for some integer $M$.
147 Wrote a MetaPost figure {\tt matfig.mp} that illustrates the partition scheme.
148
149 \citem[2004-02-07]{[v.1.4]} {\tt <jonsson@@uni-wuppertal.de>}\hfill\break
150 [Athens, Greece] Noticed that when viewed using Ghostview, the figures could
151 not be zoomed properly. This was corrected by letting the program explicitly
152 state {\tt \%\%!PS-Adobe-2.0 EPSF-1.2}, and by also explicitly stating the
153 number of pages (that is to say, one) of the figure in the Encapsulated
154 PostScript preamble, using {\tt \%\%Pages: 1}.
155
156 \citem[2004-02-20]{[v.1.5]} {\tt <jonsson@@uni-wuppertal.de>}\hfill\break
157 [\"Ostergarn, Gotland] Added the command-line options
158 {\tt --commmented\_postscript} and {\tt --uncommmented\_postscript},
159 explicitly forcing the program either to include comments on PostScript
160 routines directly into the generated code (default),
161 or forcing the program to suppress these comments (giving a slightly reduced
162 size on disk).
163
164 \citem[2004-02-21]{[v.1.6]} {\tt <jonsson@@uni-wuppertal.de>}\hfill\break
165 [\"Ostergarn, Gotland] Wrote the final blocks of a major revision of the
166 program, concerning the algorithm for generation of individual pixels.
167 While the program previously explicitly stated the pixel boundaries as paths,
168 I have now replaced this by a PostScript routine {\tt drawpixel} that takes
169 a pixel bounding box given by the lower left and upper right corners
170 $(\langle|llx|\rangle,\langle|lly|\rangle)$ and
171 $(\langle|urx|\rangle,\langle|ury|\rangle)$ and draws and fills the pixel
172 with a specified gray value.
173 The syntax for this PostScript routine is (in the PostScript language)
174 simply {\tt drawpixel} $\langle|llx|\rangle$ $\langle|lly|\rangle$
175 $\langle|urx|\rangle$ $\langle|ury|\rangle$ $\langle|w|\rangle$, where
176 $\langle|w|\rangle\in[0,1]$ is the whiteness of the actual pixel.
177 I did, however, keep the possibility of generating the previous, more extensive
178 form of PostScript, and in order to be able to switch the program into
179 either mode, the options {\tt --compactified\_pixelcode} and
180 {\tt --extensive\_pixelcode} were added as parts of the startup syntax.
181 When applied to the previously written example with a $64\times64$-sized
182 matrix of real numbers, the size of the generated was radically reduced
183 from 590.3 kB to 152.4 kB, hence corresponding to a reduction by 74\%!
184 However, there still remain to optimize the code, especially to write
185 PostScript routines that takes the image matrix and automatically loops
186 over the indices, instead of the current approach, where the bounding box
187 of each individual pixel still is specified in the code. (The image generated
188 by the {\tt image()} routine of \MATLAB\ is still considerably smaller in
189 size than what the now optimized algorithm provides; so far the generated
190 PostScript takes approximately 37 byte per pixel, which is far too much
191 even for ``educational purpose''.)
192 What remains now is to also include the more clever partitioning of the
193 image for cases with many adjacent pixels of identical gray value.
194 \medskip
195 \centerline{\epsfxsize=40.0mm\epsfbox{examples/example1-compact.eps}}
196 \medskip\nobreak
197 \centerline{Figure R1. The example $64\times64$ image used in evaluating
198 size reduction 2004-02-21.}
199
200 @*Compiling the source code. The program is written in \CWEB, generating
201 ANSI-C conforming source code and documentation as \TeX-source, and is to
202 be compiled using the enclosed Makefile, leaving an executable
203 file {\tt epsimg}\footnote{$\dagger$}{On platforms running Windows NT,
204 Windows 2000, or any other operating system by Microsoft, the executable
205 file will instead automatically be called {\tt epsimg.exe}.} and a
206 PostScript file {\tt epsimg.ps} (the document you currently are reading),
207 which contains the full documentation of the program:
208 \bigskip
209 {\obeyspaces\obeylines\tt
210 ~ \#
211 ~ \# Makefile designed for use with ctangle, cweave, gcc, and plain TeX.
212 ~ \#
213 ~ \# Copyright (C) 2004, Fredrik Jonsson <jonsson@@uni-wuppertal.de>
214 ~ \#
215 ~ CTANGLE = ctangle
216 ~ CC = gcc
217 ~ CCOPTS = -O2 -Wall -ansi -pedantic \# follow ISO C89 (ANSI) strictly
218 ~ LNOPTS = -lm
219 ~ CWEAVE = cweave
220 ~ TEX = tex
221 ~ DVIPS = dvips
222 ~ DVIPSOPT = -ta4 -D1200
223 ~ ~
224 ~ all: epsimg.exe epsimg.ps
225 ~ ~
226 ~ epsimg.exe: epsimg.o \# generate the executable file
227 ~ \$(CC) \$(CCOPTS) -o epsimg epsimg.o \$(LNOPTS)
228 ~ ~
229 ~ epsimg.o: epsimg.c \# generate the object file
230 ~ \$(CC) \$(CCOPTS) -c epsimg.c
231 ~ ~
232 ~ epsimg.c: epsimg.w \# generate C code from the CWEB source
233 ~ \$(CTANGLE) epsimg
234 ~ ~
235 ~ epsimg.ps: epsimg.dvi \# generate the PostScript documentation
236 ~ \$(DVIPS) \$(DVIPSOPT) epsimg.dvi -o epsimg.ps
237 ~ ~
238 ~ epsimg.dvi: epsimg.tex \# generate the device-independent documentation
239 ~ \$(TEX) epsimg.tex
240 ~ ~
241 ~ epsimg.tex: epsimg.w \# generate plain TeX code from the CWEB source
242 ~ \$(CWEAVE) epsimg
243 ~ ~
244 ~ clean:
245 ~ ~ -rm -Rf *.c *.o *.exe *.aux *.log *.toc *.idx *.scn *.tex *.dvi}
246 \bigskip
247
248 @*Running the program. The program is entirely controlled by the command
249 line options supplied when invoking the program, and the syntax is simply:
250 \medskip
251 {\obeyspaces\obeylines\tt
252 ~ epsimg -i <infile> -o <outfile>
253 }
254 \medskip
255 \noindent
256 where {\tt <infile>} is a regular text file containing the matrix of numerical
257 data, and {\tt <outfile>} is the name of the Encapsulated PostScript image
258 that is to be generated.
259 Instead of {\tt -i} and {\tt -o}, the switches can equivalently be specified
260 in their longer forms {\tt --inputfile} and {\tt --outputfile}, respectively.
261
262 Several options may additionally be specified; to see a listing of all
263 available options, simply invoke \epsimg\ with the help switch {\tt -h}
264 (or, equivalently, {\tt --help} in a longer form), as
265 \medskip
266 {\obeyspaces\obeylines\tt
267 ~ epsimg -h
268 }
269 \medskip
270 \noindent
271
272
273 @*Compressing the size of the generated Encapsulated Postscript.
274 In a general sense, the input to the \epsimg\ program is just an arbitrary
275 matrix of numbers, with no a priori assumption on their individual values
276 or their ordering.
277 In many images, however, there are large areas of equal colour (or brightness,
278 if we stick to the fact that the \epsimg\ program primarily is designed for
279 the visualization of gray scale images), and instead of sequentially writing
280 a large list of identical squares, of the same shading but slightly displaced
281 with respect to one another, one may start thinking that there must be a more
282 efficient method of saving the image to file.
283
284 One possibility is to check the structure sequentially in the order the squares
285 are written, and in case many boxes of the same shade appear, say in the
286 row direction of the supplied matrix, a rectangle with this shade and with
287 a length corresponding to the number of equal squares is to be drawn instead.
288 This, however, may be an inefficient method as well, since any directionality
289 in the image in the column direction of the matrix will be left unnoticed.
290 In addition, if there are large fields of equal shade, a lot of neighbouring
291 rows should be possible to further reduce, for example by instead drawing
292 general rectangles which no longer need to be of the same height as the
293 basic pixels.
294
295 The question therefore arises: Would it not be possible to make a relatively
296 simple divide-and-conquer description of the matrix, partitioning the matrix
297 into rectangular building blocks of equal shade?
298 In Fig.~1, on possibility of a partitioning scheme for the reduction of the
299 data needed to save to disk is illustrated.
300 \bigskip
301 \centerline{\epsfxsize=107.55mm\epsfbox{matfig/matfig.1}}\medskip\nobreak
302 \centerline{Figure 1. A possible partitioning scheme for square matrices
303 of size $[2^M\times 2^M]$.}
304
305
306 @*The main program. Here follows the general outline of the main program.
307
308 For the flags that are internally used, for the settings of desired program
309 actions, the significance of the flags are:
310 |COMPACTIFIED_PIXELCODE| If set to a positive nonzero integer value,
311 this flag causes the program to generate a
312 compactified PostScript code for the definitions of the individual boxes
313 of the image, i.e. the individual pixels.
314
315 In order to have one single and generic output stream, the |OUTSTREAM|
316 definition provides an easy solution to switching the output from file
317 to terminal output, depending on which options that are detected at the
318 command line during startup of the program.
319
320 @c
321 #include <math.h>
322 #include <stdio.h>
323 #include <stddef.h>
324 #include <stdlib.h>
325 #include <string.h>
326 #include <time.h> /* to get automatically generated timestamp in EPS header */
327 #include <ctype.h> /* to access |isalnum()| */
328
329 #define VERSION_NUMBER "1.6"
330
331 #define A4_PAGE_WIDTH (594) /* A4 page width in pt (1/72 in) */
332 #define A4_PAGE_HEIGHT (841) /* A4 page height in pt (1/72 in) */
333 #define MAXIMUM_IMAGE_WIDTH (A4_PAGE_WIDTH-144) /* 1.0 inch default margin */
334 #define MAXIMUM_IMAGE_HEIGHT (A4_PAGE_HEIGHT-144) /* 1.0 inch default margin */
335 #define DEFAULT_IMAGE_WIDTH (0.8*MAXIMUM_IMAGE_WIDTH)
336 #define DEFAULT_IMAGE_XCENTER (A4_PAGE_WIDTH/2)
337 #define DEFAULT_IMAGE_YCENTER (A4_PAGE_HEIGHT/2)
338 #define DEFAULT_LINETHICKNESS 1 /* default line thickness in pt (1/72 in) */
339
340 #define OUTSTREAM (outfile_specified?fpout:stdout)
341 #define SUCCESS 0 /* Return code for successful program termination */
342 #define FAILURE 1 /* Return code for program termination caused by failure */
343
344 #define COMPACTIFIED_PIXELCODE 1
345 #define EXTENSIVE_PIXELCODE 2
346
347 @<Global variables@>@;
348 @<Subroutines@>@;
349
350 int main(int argc, char *argv[])
351 {
352 @<Local variables@>@;
353 @<Parse command line@>@;
354 @<Open files@>@;
355 @<Load text file into image matrix@>@;
356 @<Normalize image matrix@>@;
357 @<Initialize parameters of Encapsulated PostScript image@>@;
358 @<Write preamble of Encapsulated PostScript image@>@;
359 @<Write body of Encapsulated PostScript image@>@;
360 @<Write closing of Encapsulated PostScript image@>@;
361 @<Deallocate image matrix@>@;
362 @<Close files@>@;
363 return(SUCCESS);
364 }
365
366 @*Declaration of global variables. The only global variables allowed in
367 my programs are |optarg|, which is the string of characters that specified
368 the call from the command line, and |progname|, which simply is the string
369 containing the name of the program, as it was invoked from the command line.
370
371 @<Glob...@>=
372 extern char *optarg; /* command line string */
373 char *progname; /* name of the program as invoked from command line */
374
375 @*Declarations of subroutines used by the program.
376
377 @<Subroutines@>=
378 @<Display help message@>@;
379 @<Routine for allocation of double vectors@>@;
380 @<Routine for allocation of double matrices@>@;
381 @<Routine for deallocation of double vectors@>@;
382 @<Routine for deallocation of double matrices@>@;
383 @<Routine for loading matrix data from text file@>@;
384 @<Routine for unloading matrix data previously loaded from text file@>@;
385
386 @ Routine for displaying a help message at the screen.
387
388 @<Display help message@>=
389 void showsomehelp(void) {
390 fprintf(stderr,"Usage: %s -i infile [options] [-o outfile]\n",progname);
391 fprintf(stderr,"Options:\n");
392 fprintf(stderr,
393 " -i, --inputfile <str> Specifies the file where to find the intensity\n"
394 " response for the actual property.\n");
395 fprintf(stderr,
396 " -o, --outputfile <str> Specifies the file where to save the trans-\n"
397 " mitted optical pulse shape. Whenever this\n");
398 fprintf(stderr,
399 " option is not present at the command line,\n"
400 " the generated time series will be written\n"
401 " to standard terminal output instead, in\n"
402 " which case any set verbose mode will be turned\n"
403 " off (see -v option).\n");
404 fprintf(stderr,
405 " -s, --sequential Toggle sequential mode. Default: off.\n"
406 " When generating the Encapsulated PostScript,\n"
407 " in sequential mode, the data\n");
408 fprintf(stderr,
409 " is scanned column/row-wise, with an individual\n"
410 " pixel written for each data point of the input\n"
411 " matrix. In this mode the program will ignore\n"
412 " any possibilities of reducing the data through\n"
413 " a more efficient partitioning of the input\n"
414 " matrix.\n");
415 fprintf(stderr,
416 " -v, --verbose Toggle verbose mode. If no output filename was\n"
417 " specified at the command line, verbose mode\n"
418 " will automatically be turned off, in order for\n"
419 " output messages not to interfere with the\n"
420 " generated Encapsulated PostScript code.\n"
421 " Default: off\n");
422 fprintf(stderr,
423 " -h, --help Display this help message and exit clean\n");
424 fprintf(stderr,
425 "Copyright (C) 2004 Fredrik Jonsson <jonsson@@uni-wuppertal.de>\n");
426 }
427
428 @ The |dvector()| routine allocate a real-valued vector of double precision,
429 with vector index ranging from |nl| to |nh|.
430
431 @<Routine for allocation of double vectors@>=
432 double *dvector(long nl, long nh) {
433 double *v;
434 v=(double *)malloc((size_t) ((nh-nl+2)*sizeof(double)));
435 if (!v) {
436 fprintf(stderr,"Error: Allocation failure in dvector()\n");
437 exit(FAILURE);
438 }
439 return v-nl+1;
440 }
441
442 @ The |dmatrix()| routine allocate a real-valued matrix of double precision,
443 with row index ranging from |nrl| to |nrh|, and column index ranging from
444 |ncl| to |nch|.
445
446 @<Routine for allocation of double matrices@>=
447 double **dmatrix(long nrl, long nrh, long ncl, long nch) {
448 long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
449 double **m;
450 m=(double **) malloc((size_t)((nrow+1)*sizeof(double*)));
451 if (!m) {
452 fprintf(stderr,"%s: Allocation failure 1 in dmatrix() routine!\n",
453 progname);
454 exit(FAILURE);
455 }
456 m += 1;
457 m -= nrl;
458 m[nrl]=(double *) malloc((size_t)((nrow*ncol+1)*sizeof(double)));
459 if (!m[nrl]) {
460 fprintf(stderr,"%s: Allocation failure 2 in dmatrix() routine!\n",
461 progname);
462 exit(FAILURE);
463 }
464 m[nrl] += 1;
465 m[nrl] -= ncl;
466 for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
467 return m;
468 }
469
470 @ The |free_dvector()| routine release the memory occupied by the
471 real-valued vector |v[nl..nh]|.
472
473 @<Routine for deallocation of double vectors@>=
474 void free_dvector(double *v, long nl, long nh) {
475 free((char*) (v+nl-1));
476 }
477
478 @ The |free_dmatrix()| routine release the memory occupied by the
479 real-valued matrix |m[nrl..nrh][ncl..nch]|.
480
481 @<Routine for deallocation of double matrices@>=
482 void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch) {
483 free((char*) (m[nrl]+ncl-1));
484 free((char*) (m+nrl-1));
485 }
486
487 @ The |load_matrix()| routine takes as input a character string
488 |inputfilename|, specifying a regular text file of ASCII data stored
489 as a matrix, and returns a pointer |m| to a matrix of double precision,
490 containing the numerical values as appearing if the text file.
491 The routine also automatically scans the input matrix size, and returns the
492 number of rows in |nr| and the number of columns in |nc|.
493 The number of columns is determined as the number of elements in the first
494 row of data of the supplied text file. All subsequent rows are assumed to
495 contain exactly the same number of elements; if this is not the case, an
496 error message will be displayed on standard terminal output.
497 The minimum and maximum elements found in the matrix are returned in the
498 variables |min| and |max|, respectively.
499
500 Example of usage:
501 \medskip
502 {\obeyspaces\obeylines\tt
503 ~ double **imagematrix,min,max;
504 ~ long int nr,nc;
505 ~ imagematrix=load\_matrix("image.dat",\&nr,\&nc,\&min,\&max);
506 ~ fprintf(stdout,"Detected \%ld rows and \%ld columns of data.\\n",nr,nc);
507 ~ fprintf(stdout,"Minimum element: \%f\\n",min);
508 ~ fprintf(stdout,"Maximum element: \%f\\n",max);
509 }
510 \bigskip
511
512 @<Routine for loading matrix data from text file@>=
513 char validchar(char ch) {
514 return(isalnum(ch)||(ch=='+')||(ch=='-')||(ch=='.'));
515 }
516
517 double **load_matrix(char inputfilename[],long *nr,long *nc,
518 double *min,double *max) {
519 FILE *fpin=NULL;
520 char tmpch;
521 long j,k,nrt,nct;
522 double tmpd,**m,tmin,tmax;
523 if ((fpin=fopen(inputfilename,"r")) == NULL) {
524 fprintf(stderr,"%s: Could not open file %s for reading!\n",
525 progname,inputfilename);
526 exit(FAILURE);
527 }
528
529 fseek(fpin,0L,SEEK_SET);
530 fscanf(fpin,"%lf",&tmpd);
531 tmin=tmpd; /* initialize memory for minimum element */
532 tmax=tmpd; /* initialize memory for maximum element */
533
534 fseek(fpin,0L,SEEK_SET);
535 nct=0; /* initialize column counter */
536 while ((tmpch=getc(fpin))!='\n') { /* determine column size |nc| */
537 ungetc(tmpch,fpin);
538 while ((tmpch=getc(fpin))==' '); /* get rid of any leading blanks */
539 ungetc(tmpch,fpin);
540 while (validchar(tmpch=getc(fpin))); /* scan pass field for valid char */
541 ungetc(tmpch,fpin);
542 nct++;
543 while ((tmpch=getc(fpin))==' '); /* get read of any trailing blanks */
544 ungetc(tmpch,fpin);
545 }
546
547 fseek(fpin,0L,SEEK_SET);
548 nrt=0; /* initialize row counter */
549 while ((tmpch=getc(fpin))!=EOF) { /* determine row size |nr| */
550 ungetc(tmpch,fpin);
551 for (k=1;k<=nct;k++) fscanf(fpin,"%lf",&tmpd);
552 nrt++;
553 tmpch=getc(fpin);
554 while ((tmpch==' ')||(tmpch=='\n')) tmpch=getc(fpin);
555 if (tmpch!=EOF) ungetc(tmpch,fpin);
556 }
557 m=dmatrix(1,nrt,1,nct);
558
559 fseek(fpin,0L,SEEK_SET);
560 for (j=1;j<=nrt;j++) { /* for all rows, ... */
561 for (k=1;k<=nct;k++) { /* and for all columns, ... */
562 fscanf(fpin,"%lf",&tmpd);
563 m[j][k]=tmpd;
564 if (tmpd<tmin)
565 tmin=tmpd;
566 else if (tmpd>tmax)
567 tmax=tmpd;
568 }
569 }
570 fclose(fpin);
571 *nr=nrt;
572 *nc=nct;
573 *min=tmin;
574 *max=tmax;
575 return m;
576 }
577
578 @ The |unload_matrix()| routine simply releases memory previously allocated
579 by the |load_matrix()| routine.
580
581 @<Routine for unloading matrix data previously loaded from text file@>=
582 void unload_matrix(double **m,long nr,long nc) {
583 free_dmatrix(m,1,nr,1,nc); /* yes, it is this simple... */
584 }
585
586 @*Declaration of local variables of the |main| program.
587
588 @<Local variables@>=
589 double **imagematrix,min,max,dx,dy,llx,lly,urx,ury;
590 double imagewidth,imageheight,imagexcenter,imageycenter;
591 double linethickness=DEFAULT_LINETHICKNESS;
592 time_t now=time(NULL);
593 long int j,k,nr,nc;
594 int no_arg,bbllx,bblly,bburx,bbury;
595 FILE *fpout=NULL;
596 char inputfilename[256]="",outputfilename[256]="";
597 short verbose=0,write_floatform=0,write_frame=1;
598 short infile_specified=0, outfile_specified=0, parse_data_sequentially=1;
599 short write_title=0;
600 short pixel_generation_mode=COMPACTIFIED_PIXELCODE;
601 short comments_in_postscript=1;
602
603 @*Parsing command line options. All input parameters are passed to the
604 program through command line options and arguments to the program.
605 The syntax of command line options is listed
606 whenever the program is invoked without any options, or if the {\tt --help}
607 option is specified at startup.
608
609 @<Parse command line@>=
610 {
611 progname=argv[0];
612 no_arg=argc;
613 while(--argc) {
614 if(!strcmp(argv[no_arg-argc],"-o") ||
615 !strcmp(argv[no_arg-argc],"--outputfile")) {
616 --argc;
617 strcpy(outputfilename,argv[no_arg-argc]);
618 outfile_specified=1;
619 } else if(!strcmp(argv[no_arg-argc],"-i") ||
620 !strcmp(argv[no_arg-argc],"--inputfile")) {
621 --argc;
622 strcpy(inputfilename,argv[no_arg-argc]);
623 infile_specified=1;
624 } else if ((!strcmp(argv[no_arg-argc],"-f"))||
625 (!strcmp(argv[no_arg-argc],"--floatform"))) {
626 write_floatform=(write_floatform?0:1);
627 if (verbose) fprintf(stdout,"%s: Using floating number output.\n",
628 progname);
629 } else if ((!strcmp(argv[no_arg-argc],"-r"))||
630 (!strcmp(argv[no_arg-argc],"--writeframe"))) {
631 write_frame=(write_frame?0:1);
632 } else if (!strcmp(argv[no_arg-argc],"--commmented_postscript")) {
633 comments_in_postscript=1;
634 } else if (!strcmp(argv[no_arg-argc],"--uncommmented_postscript")) {
635 comments_in_postscript=0;
636 } else if (!strcmp(argv[no_arg-argc],"--compactified_pixelcode")) {
637 pixel_generation_mode=COMPACTIFIED_PIXELCODE;
638 } else if (!strcmp(argv[no_arg-argc],"--extensive_pixelcode")) {
639 pixel_generation_mode=EXTENSIVE_PIXELCODE;
640 } else if(!strcmp(argv[no_arg-argc],"-v") ||
641 !strcmp(argv[no_arg-argc],"--verbose")) {
642 verbose=(verbose?0:1);
643 } else if(!strcmp(argv[no_arg-argc],"-s") ||
644 !strcmp(argv[no_arg-argc],"--sequential")) {
645 parse_data_sequentially=(parse_data_sequentially?0:1);
646 } else {
647 fprintf(stderr,"%s: Unknown option '%s'.\n",
648 progname,argv[no_arg-argc]);
649 exit(FAILURE);
650 }
651 }
652 if (!outfile_specified) verbose=0; /* terminal output EPS should be clean */
653 }
654
655 @*Opening and closing files for data output.
656
657 @ Open files for reading and writing.
658
659 @<Open files@>=
660 {
661 if (outfile_specified) {
662 if ((fpout=fopen(outputfilename,"w")) == NULL) {
663 fprintf(stderr,"%s: Could not open file %s for writing!\n",
664 progname,outputfilename);
665 exit(FAILURE);
666 }
667 fseek(fpout,0L,SEEK_SET);
668 } else {
669 if (verbose)
670 fprintf(stdout,"%s: No output file specified. (Writing to stdout).\n",
671 progname);
672 }
673 }
674
675 @ Loading the text file into memory. In this first step, the specified input
676 text file is opened, and is loaded into memory allocated by the |dmatrix|
677 routine. The memory area is accessed via the pointer |**imagematrix|, which
678 is the basic variable used later on by the blocks that write the Encapsulated
679 PostScript image to file. After the data is loaded, the input file is closed.
680
681 @<Load text file into image matrix@>=
682 {
683 if (infile_specified) {
684 if (verbose) fprintf(stderr,"%s: Loading data from file %s.\n",
685 progname,inputfilename);
686 imagematrix=load_matrix(inputfilename,&nr,&nc,&min,&max);
687 if (verbose) {
688 fprintf(stdout,
689 "%s: Detected %ld rows and %ld columns of data in file '%s'.\n",
690 progname,nr,nc,inputfilename);
691 fprintf(stdout,
692 "%s: Maximum element in '%s': %f\n",progname,inputfilename,max);
693 fprintf(stdout,
694 "%s: Minimum element in '%s': %f\n",progname,inputfilename,min);
695 }
696 } else {
697 fprintf(stderr,"%s: Error: Specify an input filename.\n",progname);
698 showsomehelp();
699 exit(FAILURE);
700 }
701 }
702
703 @ Normalize the image matrix. In order to write a properly scaled Encapsulated
704 PostScript image to file, the loaded data need to be normalized, so that the
705 elements arenumerical values between 0 and 1.
706
707 @<Normalize image matrix@>=
708 {
709 if (verbose) fprintf(stdout,"%s: Normalizing image matrix.\n",progname);
710 for (j=1;j<=nr;j++) { /* for all rows, ... */
711 for (k=1;k<=nc;k++) { /* and for all columns, ... */
712 imagematrix[j][k]=imagematrix[j][k]-min;
713 imagematrix[j][k]=imagematrix[j][k]/(max-min);
714 }
715 }
716 }
717
718 @ Initialize the parameters to be used for the Encapsulated PostScript image.
719 The parameters to be set prior to the calculation of positioning of the
720 individual pixels of the image are the corner coordinates for the bounding box.
721 The $x$-height and $y$-width of the image are generally scaled such that
722 the aspect ratio of the image is left invariant under scaling of any of the
723 coordinate axes.
724
725 By default, the program will use the width as reference for scaling the height
726 of the image, to give an aspect ratio (height/width) that leaves the individual
727 pixels as squares. If, however, the program finds that the calculated image
728 height exceed the maximum allowed, then the height will be fixed to its maximum
729 value, instead scaling the width of the image (to still give an equal aspect
730 ratio).
731
732 The values here used for the maximum extents of the picture are based on that
733 for an A4 paper, the limiting bounding box is between the lower left corner
734 at~$(0,0)$ pt and upper right corner at~$(594,841)$ pt.
735
736 \bigskip
737 \centerline{\epsfxsize=114.58mm\epsfbox{pagelayt/pagelayt.1}}\medskip\nobreak
738 \centerline{Figure 2. The page layout and definitions as used for the
739 initialization of the Encapsulated PostScript image.}
740
741
742 @<Initialize parameters of Encapsulated PostScript image@>=
743 {
744 imagewidth=((double)(DEFAULT_IMAGE_WIDTH));
745 imageheight=(((double)nr)/((double)nc))*((double)(DEFAULT_IMAGE_WIDTH));
746 imagexcenter=DEFAULT_IMAGE_XCENTER;
747 imageycenter=DEFAULT_IMAGE_YCENTER;
748 if (imageheight>MAXIMUM_IMAGE_HEIGHT) {
749 if (verbose) {
750 fprintf(stdout,"%s: Warning. I found that the height of ",progname);
751 fprintf(stdout,"the image exceeds its maximum\n");
752 fprintf(stdout,"%s: value of %d pt.\n",
753 progname,((int)MAXIMUM_IMAGE_HEIGHT));
754 fprintf(stdout,"%s: Will now instead scale the width of the image.\n",
755 progname);
756 }
757 imageheight=MAXIMUM_IMAGE_HEIGHT;
758 imagewidth=(((double)nc)/((double)nr))*imageheight;
759 } else {
760 if (verbose) {
761 fprintf(stdout,"%s: Image height automatically scaled to ",progname);
762 fprintf(stdout,"width (to give equal aspect ratio).\n");
763 }
764 }
765 bbllx=imagexcenter-imagewidth/2.0;
766 bblly=imageycenter-imageheight/2.0;
767 bburx=imagexcenter+imagewidth/2.0;
768 bbury=imageycenter+imageheight/2.0;
769 }
770
771 @ Write the leading blocks of Encapsulated PostScript code.
772 If the flag |pixel_generation_mode| is set to |COMPACTIFIED PIXELCODE|, then
773 an additional routine for the generation of the individual pixels will be
774 added just after the comments in the preamble; otherwise, the generated code
775 will be self-contained in the sense that the individual pixels are defined
776 as free-standing drawing statements in the code.
777 Notice that the type of output stream (terminal output or file pointer,
778 depending on the options present at the command line at startup of the
779 program) is determined by the current definition provided by |OUTSTREAM|.
780 Notice that the string returned by the |ctime()| routine ends with a linefeeed.
781
782 The blocks dealing with the definition of the PostScript routine for a
783 more ``compactified'' output code clearly deserves some more detailed
784 description.
785 The syntax for drawing an individual pixel, determined by the bounding box
786 given by its lower left and upper right corners $(x_{\rm ll},y_{\rm ll})$
787 and $(x_{\rm ur},y_{\rm ur})$ is\par\medskip
788 {\narrower\narrower{\tt /drawpixel} $\langle|llx|\rangle$ $\langle|lly|\rangle$
789 $\langle|urx|\rangle$ $\langle|ury|\rangle$ $\langle|w|\rangle$,\par}
790 \medskip\noindent
791 where $|llx|=x_{\rm ll}$, $|lly|=y_{\rm ll}$,
792 $|urx|=x_{\rm ur}$, and $|ury|=y_{\rm ur}$.
793 This definition is illustrated in Fig.~3 below.
794 In this description of the syntax, $|w|\in[0,1]$ is the whiteness value of
795 the pixel, with 0 corresponding to black, and 1 corresponding to white.
796 \bigskip
797 \centerline{\epsfxsize=45.0mm\epsfbox{pixbb/pixbb.1}}\medskip\nobreak
798 \centerline{Figure 3. Illustration of the definition of a pixel in terms
799 of its lower left and upper right corners.}
800 \bigskip
801 In the PostScript routine {\tt /drawpixel}, the following commands of the
802 PostScript languange are used for manipulation of the stack:
803 \medskip
804 \oitem[{\tt dup}]{Duplicates the bottom element in the stack, and then pushes
805 it into the stack. This operation is similar to the ENTER as used in reverse
806 polish notation employed in, for example, Hewlett--Packard calculators.}
807
808 \oitem[{\tt exch}]{Interchanges the two bottom-most elements in the stack.
809 This operation is identical to SWAP.}
810
811 \oitem[$\langle m\rangle$ $\langle n\rangle$ {\tt roll}]{Rolls down the~$m$
812 bottom-most elements of the stack $n$ times, that is to say, applying cyclic
813 permutation $n$ times on the first $m$ elements.
814 In analogy with the ROLLD operation of reverse polish notation of
815 Hewlett--Packard calculators, this is identical to executing the operation
816 ``$m$ ROLLD'' exactly $n$ times.
817 Notice that $\langle m\rangle$ $\langle m\rangle$ {\tt roll} always just gives
818 the identity operation on the stack for arbitrary $m$ (of course provided
819 that~$m$ is not greater than the number of elements that currently are present
820 in the stack).
821
822 @<Write preamble of Encapsulated PostScript image@>=
823 {
824 fprintf(OUTSTREAM,"%%!PS-Adobe-2.0 EPSF-1.2\n");
825 fprintf(OUTSTREAM,"%%%%BoundingBox: %d %d %d %d\n",bbllx,bblly,bburx,bbury);
826 fprintf(OUTSTREAM,"%%%%Creator: epsimg %s",VERSION_NUMBER);
827 fprintf(OUTSTREAM," Copyright (C) 2004 Fredrik Jonsson\n");
828 if (outfile_specified)
829 fprintf(OUTSTREAM,"%%%%Title: %s\n",outputfilename);
830 else
831 fprintf(OUTSTREAM,"%%%%Title: (image written to stdout)\n");
832 fprintf(OUTSTREAM,"%%%%CreationDate: %s",ctime(&now));
833 fprintf(OUTSTREAM,"%%%%Pages: 1\n");
834 fprintf(OUTSTREAM,"%%%%EndProlog\n");
835 fprintf(OUTSTREAM,"%%%%Pages: 1\n");
836 fprintf(OUTSTREAM,"%%%%Page: 1 1\n");
837 if (pixel_generation_mode==COMPACTIFIED_PIXELCODE) {
838 if (comments_in_postscript) {
839 fprintf(OUTSTREAM,"%%\n");
840 fprintf(OUTSTREAM,"%% Routine for duplicating the bottom-most pair");
841 fprintf(OUTSTREAM," of elements in the stack.");
842 fprintf(OUTSTREAM,"%%\n");
843 }
844 fprintf(OUTSTREAM,"/dupc {dup 3 2 roll dup 4 1 roll exch} bind def");
845 if (comments_in_postscript) {
846 fprintf(OUTSTREAM,"%%\n");
847 fprintf(OUTSTREAM,"%% Routine for calculating the lower right corner");
848 fprintf(OUTSTREAM," coordinates of the pixel.\n");
849 fprintf(OUTSTREAM,"%% The syntax is simply '<llx> <lly> <urx> <ury> ");
850 fprintf(OUTSTREAM," lrc', where (<llx>,<lly>)\n");
851 fprintf(OUTSTREAM,"%% and (<urx>,<ury>) are the");
852 fprintf(OUTSTREAM," lower left and upper right corner coordinates\n");
853 fprintf(OUTSTREAM,"%% of the pixel. The resulting (<lrx>,<lry>) pair");
854 fprintf(OUTSTREAM," are after the calculation\n%% pushed onto the");
855 fprintf(OUTSTREAM," stack, preserving the previously present stack");
856 fprintf(OUTSTREAM," at above\n%% levels.\n");
857 fprintf(OUTSTREAM,"%%\n");
858 }
859 fprintf(OUTSTREAM,"/lrc {4 1 roll dup 5 2 roll dup 5 -1 roll exch");
860 fprintf(OUTSTREAM," 4 2 roll 6 2 roll} bind def\n");
861 fprintf(OUTSTREAM,"/ulc {4 3 roll dup 5 2 roll dup 6 -1 roll exch}");
862 fprintf(OUTSTREAM," bind def\n");
863 if (comments_in_postscript) {
864 fprintf(OUTSTREAM,"%%\n");
865 fprintf(OUTSTREAM,"%% Routine for drawing individual pixels\n");
866 fprintf(OUTSTREAM,"%%\n");
867 }
868 fprintf(OUTSTREAM,"/pixelstack {lrc 6 2 roll ulc 4 2 roll 8 4 roll");
869 fprintf(OUTSTREAM," dupc 10 2 roll} bind def\n");
870 fprintf(OUTSTREAM,"/drawpixel {setgray pixelstack newpath moveto lineto\n");
871 fprintf(OUTSTREAM," lineto lineto lineto closepath fill} bind def\n");
872
873
874 if (comments_in_postscript) {
875 fprintf(OUTSTREAM,"%%\n");
876 fprintf(OUTSTREAM,"%% The dp routine is short-hand for drawpixel\n");
877 fprintf(OUTSTREAM,"%%\n");
878 }
879 fprintf(OUTSTREAM,"/dp {drawpixel} bind def\n");
880
881
882 }
883 }
884
885 @ Write the body of Encapsulated PostScript code.
886
887 @<Write body of Encapsulated PostScript image@>=
888 {
889 if (parse_data_sequentially) {
890 @<Write body of sequential Encapsulated PostScript image@>@;
891 } else {
892 @<Write body of partitioned Encapsulated PostScript image@>@;
893 }
894 }
895
896 @ Write sequential body of Encapsulated PostScript code.
897 The {\tt moveto} sets the current starting point of each pixel.
898 The path of the boundary of each pixel is traversed in counter-clockwise
899 direction, starting in the lower left corner of each pixel.
900 Here |(llx,lly)| give the $(x,y)$-coordinates of the lower left corner of
901 the pixel, while |(urx,ury)| give the $(x,y)$-coordinates of the upper right
902 corner.
903
904 @<Write body of sequential Encapsulated PostScript image@>=
905 {
906 dx=((double)(bburx-bbllx))/((double)nc);
907 dy=((double)(bbury-bblly))/((double)nr);
908 for (j=1;j<=nr;j++) {
909 lly=((double)bblly)+((double)(j-1))*dy;
910 ury=lly+dy*(1.0+8.0e-2);;
911 for (k=1;k<=nc;k++) {
912 llx=bbllx+((double)(k-1)*dx);
913 urx=llx+dx*(1.0+8.0e-2);
914 if (pixel_generation_mode==COMPACTIFIED_PIXELCODE) {
915 fprintf(OUTSTREAM,"%1.2f %1.2f %1.2f %1.2f %1.3f dp\n",
916 llx,lly,urx,ury,imagematrix[j][k]);
917 } else {
918 fprintf(OUTSTREAM,"%1.3f setgray\n",imagematrix[j][k]);
919 fprintf(OUTSTREAM,"newpath %1.2f %1.2f moveto\n",llx,lly);
920 fprintf(OUTSTREAM," %1.2f %1.2f lineto",urx,lly);
921 fprintf(OUTSTREAM," %1.2f %1.2f lineto\n",urx,ury);
922 fprintf(OUTSTREAM," %1.2f %1.2f lineto",llx,ury);
923 fprintf(OUTSTREAM," %1.2f %1.2f lineto closepath fill\n",llx,lly);
924 }
925 }
926 }
927 if (0==1) { /* for debugging purposes only */
928 for (j=1;j<=nr;j++) {
929 for (k=1;k<=nc;k++) {
930 fprintf(stdout,"%2.4f ",imagematrix[j][k]);
931 }
932 fprintf(stdout,"\n");
933 }
934 }
935 }
936
937 @ Write partitioned body of Encapsulated PostScript code.
938
939 @<Write body of partitioned Encapsulated PostScript image@>=
940 {
941 fprintf(stdout,
942 "Not yet finished with non-sequential partitioning of data\n");
943 exit(-1);
944 }
945
946 @ Write the blocks ending the Encapsulated PostScript code.
947
948 @<Write closing of Encapsulated PostScript image@>=
949 {
950 if (write_frame) { /* write frame corresponding to bounding box */
951 fprintf(OUTSTREAM,"0 setgray 0 %1.2f dtransform truncate ",linethickness);
952 fprintf(OUTSTREAM,"idtransform setlinewidth pop\n");
953 fprintf(OUTSTREAM," [] 0 setdash 1 setlinejoin 10 setmiterlimit\n");
954 fprintf(OUTSTREAM,"newpath %d %d moveto\n",bbllx,bblly);
955 fprintf(OUTSTREAM," %d %d lineto",bburx,bblly);
956 fprintf(OUTSTREAM," %d %d lineto\n",bburx,bbury);
957 fprintf(OUTSTREAM," %d %d lineto",bbllx,bbury);
958 fprintf(OUTSTREAM," %d %d lineto closepath stroke\n",bbllx,bblly);
959 }
960 if (write_title) {
961 fprintf(stderr,"Still to be finished!!\n");
962 exit(-1);
963 fprintf(OUTSTREAM,"%%IncludeResource: font Helvetica\n");
964 fprintf(OUTSTREAM,"/Helvetica /WindowsLatin1Encoding 120 FMSR\n");
965
966 fprintf(OUTSTREAM,"2345 2372 moveto\n");
967 fprintf(OUTSTREAM,"(Intensity distribution in observation plane) s\n");
968
969 fprintf(OUTSTREAM,"504 2372 moveto -90 rotate\n");
970 fprintf(OUTSTREAM,"(y [) s\n");
971 fprintf(OUTSTREAM,"90 rotate\n");
972
973 fprintf(OUTSTREAM,"%%IncludeResource: font Symbol\n");
974 fprintf(OUTSTREAM,"/Symbol /WindowsLatin1Encoding 120 FMSR\n");
975
976 fprintf(OUTSTREAM,"504 2540 moveto -90 rotate\n");
977 fprintf(OUTSTREAM,"(m) s\n");
978 fprintf(OUTSTREAM,"90 rotate\n");
979 fprintf(OUTSTREAM,"504 2372 moveto -90 rotate\n");
980 fprintf(OUTSTREAM,"(]) s\n");
981 /* AND SO ON, IN THIS STYLE .... */
982
983 }
984 fprintf(OUTSTREAM,"showpage\n");
985 fprintf(OUTSTREAM,"%%%%EOF\n");
986 }
987
988
989 @ Deallocate memory occupied by the image matrix.
990
991 @<Deallocate image matrix@>=
992 {
993 unload_matrix(imagematrix,nr,nc);
994 }
995
996
997 @ Close all open files.
998
999 @<Close files@>=
1000 {
1001 fclose(fpout);
1002 }
1003
1004 @*Index.
1005
Generated by ::viewsrc::