ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.36
Committed: Fri Mar 28 00:06:36 2025 UTC (2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.35: +3 -1 lines
Log Message:
refactor: Made MAXCOMP redefinable and common between rmatrix tools

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.36 static const char RCSid[] = "$Id: rmtxop.c,v 2.35 2024/11/08 17:52:26 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * General component matrix operations.
6     */
7    
8 greg 2.11 #include <errno.h>
9 greg 2.1 #include "rtio.h"
10     #include "rmatrix.h"
11 greg 2.10 #include "platform.h"
12 greg 2.1
13 greg 2.36 #ifndef MAXCOMP
14 greg 2.22 #define MAXCOMP MAXCSAMP /* #components we support */
15 greg 2.36 #endif
16 greg 2.1
17 greg 2.22 /* Unary matrix operation(s) */
18 greg 2.1 typedef struct {
19 greg 2.27 double cmat[MAXCOMP*MAXCOMP]; /* component transformation */
20 greg 2.1 double sca[MAXCOMP]; /* scalar coefficients */
21 greg 2.27 const char *csym; /* symbolic coefs or file */
22     short clen; /* number of coefficients */
23 greg 2.13 short nsf; /* number of scalars */
24 greg 2.27 short transpose; /* do transpose? */
25 greg 2.13 } RUNARYOP;
26    
27 greg 2.22 /* Matrix input source and requested operation(s) */
28 greg 2.13 typedef struct {
29     const char *inspec; /* input specification */
30 greg 2.18 RMPref rmp; /* matrix preference */
31 greg 2.13 RUNARYOP preop; /* unary operation(s) */
32     RMATRIX *mtx; /* original matrix if loaded */
33     int binop; /* binary op with next (or 0) */
34     } ROPMAT;
35 greg 2.1
36     int verbose = 0; /* verbose reporting? */
37    
38 greg 2.13 /* Load matrix */
39 greg 2.35 int
40 greg 2.13 loadmatrix(ROPMAT *rop)
41 greg 2.1 {
42 greg 2.20 if (rop->mtx != NULL) /* already loaded? */
43 greg 2.13 return(0);
44    
45 greg 2.18 rop->mtx = rmx_load(rop->inspec, rop->rmp);
46 greg 2.23
47     return(!rop->mtx ? -1 : 1);
48 greg 2.1 }
49    
50 greg 2.35 extern int checksymbolic(ROPMAT *rop);
51 greg 2.27
52     /* Check/set transform based on a reference input file */
53 greg 2.35 int
54 greg 2.27 checkreffile(ROPMAT *rop)
55     {
56     static const char *curRF = NULL;
57     static RMATRIX refm;
58     const int nc = rop->mtx->ncomp;
59     int i;
60    
61     if (!curRF || strcmp(rop->preop.csym, curRF)) {
62     FILE *fp = fopen(rop->preop.csym, "rb");
63     if (!rmx_load_header(&refm, fp)) {
64     fprintf(stderr, "%s: cannot read info header\n",
65     rop->preop.csym);
66     curRF = NULL;
67     if (fp) fclose(fp);
68     return(-1);
69     }
70     fclose(fp);
71     curRF = rop->preop.csym;
72     }
73 greg 2.30 if (refm.ncomp == 3) {
74 greg 2.27 rop->preop.csym = (refm.dtype == DTxyze) ? "XYZ" : "RGB";
75     return(checksymbolic(rop));
76     }
77     if (refm.ncomp == 2) {
78     fprintf(stderr, "%s: cannot convert to 2 components\n",
79     curRF);
80     return(-1);
81     }
82     if (refm.ncomp == 1) {
83     rop->preop.csym = "Y"; /* XXX big assumption */
84     return(checksymbolic(rop));
85     }
86     if (refm.ncomp == nc &&
87     !memcmp(refm.wlpart, rop->mtx->wlpart, sizeof(refm.wlpart)))
88     return(0); /* nothing to do */
89    
90     if ((nc <= 3) | (nc > MAXCSAMP) | (refm.ncomp > MAXCSAMP)) {
91     fprintf(stderr, "%s: cannot resample from %d to %d components\n",
92     curRF, nc, refm.ncomp);
93     return(-1);
94     }
95     rop->preop.clen = refm.ncomp * nc; /* compute spec to ref */
96    
97     for (i = 0; i < nc; i++) {
98     SCOLOR scstim, scresp;
99     int j;
100     memset(scstim, 0, sizeof(COLORV)*nc);
101     scstim[i] = 1.f;
102     convertscolor(scresp, refm.ncomp, refm.wlpart[0], refm.wlpart[3],
103     scstim, nc, rop->mtx->wlpart[0], rop->mtx->wlpart[3]);
104     for (j = refm.ncomp; j-- > 0; )
105     rop->preop.cmat[j*nc + i] = scresp[j];
106     }
107 greg 2.29 memcpy(rop->mtx->wlpart, refm.wlpart, sizeof(rop->mtx->wlpart));
108 greg 2.27 return(0);
109     }
110    
111 greg 2.22 /* Compute conversion row from spectrum to one channel of RGB */
112 greg 2.35 void
113 greg 2.22 rgbrow(ROPMAT *rop, int r, int p)
114     {
115     const int nc = rop->mtx->ncomp;
116     const float * wlp = rop->mtx->wlpart;
117     int i;
118    
119     for (i = nc; i--; ) {
120     int nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc;
121     int nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc;
122     COLOR crgb;
123     spec_rgb(crgb, nmStart, nmEnd);
124     rop->preop.cmat[r*nc+i] = crgb[p];
125     }
126     }
127    
128     /* Compute conversion row from spectrum to one channel of XYZ */
129 greg 2.35 void
130 greg 2.22 xyzrow(ROPMAT *rop, int r, int p)
131     {
132     const int nc = rop->mtx->ncomp;
133     const float * wlp = rop->mtx->wlpart;
134     int i;
135    
136     for (i = nc; i--; ) {
137     int nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc;
138     int nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc;
139     COLOR cxyz;
140     spec_cie(cxyz, nmStart, nmEnd);
141     rop->preop.cmat[r*nc+i] = cxyz[p];
142     }
143     }
144    
145     /* Use the spectral sensitivity function to compute matrix coefficients */
146 greg 2.35 void
147 greg 2.34 sensrow(ROPMAT *rop, int r, double (*sf)(const SCOLOR sc, int ncs, const float wlpt[4]))
148 greg 2.22 {
149     const int nc = rop->mtx->ncomp;
150     int i;
151    
152     for (i = nc; i--; ) {
153     SCOLOR sclr;
154 greg 2.27 memset(sclr, 0, sizeof(COLORV)*nc);
155 greg 2.24 sclr[i] = 1.f;
156 greg 2.22 rop->preop.cmat[r*nc+i] = (*sf)(sclr, nc, rop->mtx->wlpart);
157     }
158     }
159    
160     /* Check/set symbolic transform */
161 greg 2.35 int
162 greg 2.22 checksymbolic(ROPMAT *rop)
163     {
164     const int nc = rop->mtx->ncomp;
165     const int dt = rop->mtx->dtype;
166 greg 2.31 double cf = 1;
167 greg 2.22 int i, j;
168 greg 2.27 /* check suffix => reference file */
169     if (strchr(rop->preop.csym, '.') > rop->preop.csym)
170     return(checkreffile(rop));
171 greg 2.22
172     if (nc < 3) {
173     fprintf(stderr, "%s: -c '%s' requires at least 3 components\n",
174     rop->inspec, rop->preop.csym);
175     return(-1);
176     }
177     rop->preop.clen = strlen(rop->preop.csym) * nc;
178     if (rop->preop.clen > MAXCOMP*MAXCOMP) {
179     fprintf(stderr, "%s: -c '%s' results in too many components\n",
180     rop->inspec, rop->preop.csym);
181     return(-1);
182     }
183     for (j = 0; rop->preop.csym[j]; j++) {
184     int comp = 0;
185     switch (rop->preop.csym[j]) {
186     case 'B':
187 greg 2.31 case 'b':
188 greg 2.22 ++comp;
189     /* fall through */
190     case 'G':
191 greg 2.31 case 'g':
192 greg 2.22 ++comp;
193     /* fall through */
194     case 'R':
195 greg 2.31 case 'r':
196     if (rop->preop.csym[j] <= 'Z')
197     cf = 1./WHTEFFICACY;
198 greg 2.22 if (dt == DTxyze) {
199     for (i = 3; i--; )
200 greg 2.31 rop->preop.cmat[j*nc+i] = cf*xyz2rgbmat[comp][i];
201 greg 2.22 } else if (nc == 3)
202     rop->preop.cmat[j*nc+comp] = 1.;
203     else
204     rgbrow(rop, j, comp);
205     break;
206     case 'Z':
207 greg 2.31 case 'z':
208 greg 2.22 ++comp;
209     /* fall through */
210     case 'Y':
211 greg 2.31 case 'y':
212 greg 2.22 ++comp;
213     /* fall through */
214     case 'X':
215 greg 2.31 case 'x':
216     if ((rop->preop.csym[j] <= 'Z') & (dt != DTxyze))
217     cf = WHTEFFICACY;
218 greg 2.22 if (dt == DTxyze) {
219     rop->preop.cmat[j*nc+comp] = 1.;
220     } else if (nc == 3) {
221     for (i = 3; i--; )
222     rop->preop.cmat[j*nc+i] =
223     rgb2xyzmat[comp][i];
224     } else if (comp == CIEY)
225     sensrow(rop, j, scolor2photopic);
226     else
227     xyzrow(rop, j, comp);
228    
229 greg 2.31 for (i = nc*(cf != 1); i--; )
230     rop->preop.cmat[j*nc+i] *= cf;
231 greg 2.22 break;
232 greg 2.24 case 'S': /* scotopic (il)luminance */
233 greg 2.31 cf = WHTSCOTOPIC;
234     /* fall through */
235     case 's':
236 greg 2.22 sensrow(rop, j, scolor2scotopic);
237 greg 2.31 for (i = nc*(cf != 1); i--; )
238     rop->preop.cmat[j*nc+i] *= cf;
239 greg 2.22 break;
240 greg 2.24 case 'M': /* melanopic (il)luminance */
241 greg 2.31 cf = WHTMELANOPIC;
242     /* fall through */
243     case 'm':
244 greg 2.22 sensrow(rop, j, scolor2melanopic);
245 greg 2.31 for (i = nc*(cf != 1); i--; )
246     rop->preop.cmat[j*nc+i] *= cf;
247 greg 2.22 break;
248 greg 2.24 case 'A': /* average component */
249 greg 2.31 case 'a':
250 greg 2.24 for (i = nc; i--; )
251 greg 2.25 rop->preop.cmat[j*nc+i] = 1./(double)nc;
252 greg 2.24 break;
253 greg 2.22 default:
254     fprintf(stderr, "%s: -c '%c' unsupported\n",
255     rop->inspec, rop->preop.csym[j]);
256     return(-1);
257     }
258     }
259     /* return recommended output type */
260 greg 2.32 if (!strcasecmp(rop->preop.csym, "XYZ")) {
261 greg 2.22 if (dt <= DTspec)
262     return(DTxyze);
263 greg 2.32 } else if (!strcasecmp(rop->preop.csym, "RGB")) {
264 greg 2.22 if (dt <= DTspec)
265     return(DTrgbe);
266 greg 2.30 } else if (dt == DTspec)
267 greg 2.22 return(DTfloat); /* probably not actual spectrum */
268     return(0);
269     }
270    
271 greg 2.13 /* Get matrix and perform unary operations */
272 greg 2.35 RMATRIX *
273 greg 2.13 loadop(ROPMAT *rop)
274 greg 2.1 {
275 greg 2.22 int outtype = 0;
276 greg 2.13 RMATRIX *mres;
277 greg 2.22 int i, j;
278 greg 2.1
279 greg 2.13 if (loadmatrix(rop) < 0) /* make sure we're loaded */
280 greg 2.1 return(NULL);
281 greg 2.13
282 greg 2.27 if (rop->preop.csym && /* symbolic transform? */
283 greg 2.22 (outtype = checksymbolic(rop)) < 0)
284     goto failure;
285     if (rop->preop.clen > 0) { /* apply component transform? */
286     if (rop->preop.clen % rop->mtx->ncomp) {
287     fprintf(stderr, "%s: -c must have N x %d coefficients\n",
288     rop->inspec, rop->mtx->ncomp);
289     goto failure;
290     }
291     if (rop->preop.nsf > 0) { /* scale transform, first */
292     if (rop->preop.nsf == 1) {
293     for (i = rop->preop.clen; i--; )
294     rop->preop.cmat[i] *= rop->preop.sca[0];
295 greg 2.28 } else if (rop->preop.nsf*rop->mtx->ncomp != rop->preop.clen) {
296 greg 2.22 fprintf(stderr, "%s: -s must have one or %d factors\n",
297 greg 2.28 rop->inspec,
298     rop->preop.clen/rop->mtx->ncomp);
299 greg 2.22 goto failure;
300     } else {
301 greg 2.28 for (i = rop->preop.nsf; i--; )
302     for (j = rop->mtx->ncomp; j--; )
303     rop->preop.cmat[i*rop->mtx->ncomp+j]
304     *= rop->preop.sca[i];
305 greg 2.22 }
306     }
307     mres = rmx_transform(rop->mtx, rop->preop.clen/rop->mtx->ncomp,
308     rop->preop.cmat);
309     if (mres == NULL) {
310     fprintf(stderr, "%s: matrix transform failed\n",
311     rop->inspec);
312 greg 2.13 goto failure;
313 greg 2.1 }
314 greg 2.22 if (verbose)
315     fprintf(stderr, "%s: applied %d x %d transform%s\n",
316     rop->inspec, mres->ncomp,
317     rop->mtx->ncomp,
318     rop->preop.nsf ? " (* scalar)" : "");
319 greg 2.26 rop->preop.nsf = 0; /* now folded in */
320 greg 2.22 if ((mres->ncomp > 3) & (mres->dtype <= DTspec))
321     outtype = DTfloat; /* probably not actual spectrum */
322     rmx_free(rop->mtx);
323     rop->mtx = mres;
324     }
325     if (rop->preop.nsf > 0) { /* apply scalar(s)? */
326 greg 2.13 if (rop->preop.nsf == 1) {
327     for (i = rop->mtx->ncomp; --i; )
328     rop->preop.sca[i] = rop->preop.sca[0];
329     } else if (rop->preop.nsf != rop->mtx->ncomp) {
330 greg 2.1 fprintf(stderr, "%s: -s must have one or %d factors\n",
331 greg 2.13 rop->inspec, rop->mtx->ncomp);
332     goto failure;
333 greg 2.1 }
334 greg 2.13 if (!rmx_scale(rop->mtx, rop->preop.sca)) {
335     fputs(rop->inspec, stderr);
336 greg 2.1 fputs(": scalar operation failed\n", stderr);
337 greg 2.13 goto failure;
338 greg 2.1 }
339     if (verbose) {
340 greg 2.13 fputs(rop->inspec, stderr);
341 greg 2.1 fputs(": applied scalar (", stderr);
342 greg 2.13 for (i = 0; i < rop->preop.nsf; i++)
343     fprintf(stderr, " %f", rop->preop.sca[i]);
344 greg 2.1 fputs(" )\n", stderr);
345     }
346     }
347 greg 2.22 if (rop->preop.transpose) { /* transpose matrix? */
348 greg 2.13 mres = rmx_transpose(rop->mtx);
349     if (mres == NULL) {
350     fputs(rop->inspec, stderr);
351 greg 2.12 fputs(": transpose failed\n", stderr);
352 greg 2.13 goto failure;
353 greg 2.12 }
354     if (verbose) {
355 greg 2.13 fputs(rop->inspec, stderr);
356 greg 2.12 fputs(": transposed rows and columns\n", stderr);
357     }
358 greg 2.13 rmx_free(rop->mtx);
359     rop->mtx = mres;
360     }
361     mres = rop->mtx;
362     rop->mtx = NULL;
363 greg 2.22 if (outtype)
364     mres->dtype = outtype;
365 greg 2.13 return(mres);
366     failure:
367     rmx_free(rop->mtx);
368     return(rop->mtx = NULL);
369     }
370    
371     /* Execute binary operation, free matrix arguments and return new result */
372 greg 2.35 RMATRIX *
373 greg 2.13 binaryop(const char *inspec, RMATRIX *mleft, int op, RMATRIX *mright)
374     {
375     RMATRIX *mres = NULL;
376     int i;
377    
378     if ((mleft == NULL) | (mright == NULL))
379     return(NULL);
380     switch (op) {
381     case '.': /* concatenate */
382 greg 2.16 if (mleft->ncomp != mright->ncomp) {
383     fputs(inspec, stderr);
384     fputs(": # components do not match\n", stderr);
385     } else if (mleft->ncols != mright->nrows) {
386     fputs(inspec, stderr);
387     fputs(": mismatched dimensions\n",
388     stderr);
389     } else
390     mres = rmx_multiply(mleft, mright);
391 greg 2.13 rmx_free(mleft);
392 greg 2.12 rmx_free(mright);
393 greg 2.1 if (mres == NULL) {
394 greg 2.13 fputs(inspec, stderr);
395 greg 2.16 fputs(": concatenation failed\n", stderr);
396 greg 2.1 return(NULL);
397     }
398     if (verbose) {
399 greg 2.13 fputs(inspec, stderr);
400 greg 2.1 fputs(": concatenated matrix\n", stderr);
401     }
402 greg 2.13 break;
403     case '+':
404     if (!rmx_sum(mleft, mright, NULL)) {
405     fputs(inspec, stderr);
406 greg 2.1 fputs(": matrix sum failed\n", stderr);
407 greg 2.13 rmx_free(mleft);
408 greg 2.1 rmx_free(mright);
409     return(NULL);
410     }
411     if (verbose) {
412 greg 2.13 fputs(inspec, stderr);
413 greg 2.1 fputs(": added in matrix\n", stderr);
414     }
415     rmx_free(mright);
416 greg 2.13 mres = mleft;
417     break;
418     case '*':
419     case '/': {
420     const char * tnam = (op == '/') ?
421 greg 2.11 "division" : "multiplication";
422     errno = 0;
423 greg 2.13 if (!rmx_elemult(mleft, mright, (op == '/'))) {
424 greg 2.11 fprintf(stderr, "%s: element-wise %s failed\n",
425 greg 2.13 inspec, tnam);
426     rmx_free(mleft);
427 greg 2.11 rmx_free(mright);
428     return(NULL);
429     }
430     if (errno)
431     fprintf(stderr,
432     "%s: warning - error during element-wise %s\n",
433 greg 2.13 inspec, tnam);
434 greg 2.11 else if (verbose)
435 greg 2.13 fprintf(stderr, "%s: element-wise %s\n", inspec, tnam);
436 greg 2.11 rmx_free(mright);
437 greg 2.13 mres = mleft;
438     } break;
439     default:
440     fprintf(stderr, "%s: unknown operation '%c'\n", inspec, op);
441     rmx_free(mleft);
442 greg 2.1 rmx_free(mright);
443     return(NULL);
444     }
445 greg 2.13 return(mres);
446     }
447    
448     /* Perform matrix operations from left to right */
449 greg 2.35 RMATRIX *
450 greg 2.13 op_left2right(ROPMAT *mop)
451     {
452     RMATRIX *mleft = loadop(mop);
453    
454     while (mop->binop) {
455     if (mleft == NULL)
456     break;
457     mleft = binaryop(mop[1].inspec,
458     mleft, mop->binop, loadop(mop+1));
459     mop++;
460     }
461 greg 2.1 return(mleft);
462     }
463    
464 greg 2.13 /* Perform matrix operations from right to left */
465 greg 2.35 RMATRIX *
466 greg 2.13 op_right2left(ROPMAT *mop)
467     {
468     RMATRIX *mright;
469     int rpos = 0;
470     /* find end of list */
471     while (mop[rpos].binop)
472 greg 2.14 if (mop[rpos++].binop != '.') {
473     fputs(
474     "Right-to-left evaluation only for matrix multiplication!\n",
475     stderr);
476     return(NULL);
477     }
478 greg 2.13 mright = loadop(mop+rpos);
479     while (rpos-- > 0) {
480     if (mright == NULL)
481     break;
482 greg 2.20 mright = binaryop(mop[rpos+1].inspec,
483 greg 2.13 loadop(mop+rpos), mop[rpos].binop, mright);
484     }
485     return(mright);
486     }
487    
488     #define t_nrows(mop) ((mop)->preop.transpose ? (mop)->mtx->ncols \
489     : (mop)->mtx->nrows)
490     #define t_ncols(mop) ((mop)->preop.transpose ? (mop)->mtx->nrows \
491     : (mop)->mtx->ncols)
492    
493     /* Should we prefer concatenating from rightmost matrix towards left? */
494 greg 2.35 int
495 greg 2.13 prefer_right2left(ROPMAT *mop)
496     {
497     int mri = 0;
498    
499     while (mop[mri].binop) /* find rightmost matrix */
500     if (mop[mri++].binop != '.')
501     return(0); /* pre-empt reversal for other ops */
502    
503     if (mri <= 1)
504     return(0); /* won't matter */
505    
506     if (loadmatrix(mop+mri) < 0) /* load rightmost cat */
507     return(1); /* fail will bail in a moment */
508    
509     if (t_ncols(mop+mri) == 1)
510     return(1); /* definitely better R->L */
511    
512     if (t_ncols(mop+mri) >= t_nrows(mop+mri))
513     return(0); /* ...probably worse */
514    
515     if (loadmatrix(mop) < 0) /* load leftmost */
516     return(0); /* fail will bail in a moment */
517    
518     return(t_ncols(mop+mri) < t_nrows(mop));
519     }
520    
521 greg 2.35 int
522 greg 2.1 get_factors(double da[], int n, char *av[])
523     {
524     int ac;
525    
526     for (ac = 0; ac < n && isflt(av[ac]); ac++)
527     da[ac] = atof(av[ac]);
528     return(ac);
529     }
530    
531 greg 2.35 ROPMAT *
532 greg 2.26 resize_moparr(ROPMAT *mop, int n2alloc)
533 greg 2.13 {
534     int nmats = 0;
535 greg 2.26 int i;
536 greg 2.13
537     while (mop[nmats++].binop)
538     ;
539 greg 2.35 for (i = nmats; i >= n2alloc; i--)
540 greg 2.26 rmx_free(mop[i].mtx);
541 greg 2.13 mop = (ROPMAT *)realloc(mop, n2alloc*sizeof(ROPMAT));
542     if (mop == NULL) {
543 greg 2.26 fputs("Out of memory in resize_moparr()\n", stderr);
544 greg 2.13 exit(1);
545     }
546     if (n2alloc > nmats)
547     memset(mop+nmats, 0, (n2alloc-nmats)*sizeof(ROPMAT));
548     return(mop);
549     }
550    
551 greg 2.1 /* Load one or more matrices and operate on them, sending results to stdout */
552     int
553     main(int argc, char *argv[])
554     {
555 greg 2.27 int outfmt = DTfromHeader;
556     const char *defCsym = NULL;
557     int nall = 2;
558     ROPMAT *mop = (ROPMAT *)calloc(nall, sizeof(ROPMAT));
559     int nmats = 0;
560     RMATRIX *mres = NULL;
561     int stdin_used = 0;
562     int i;
563 greg 2.1 /* get options and arguments */
564 greg 2.13 for (i = 1; i < argc; i++) {
565 greg 2.11 if (argv[i][0] && !argv[i][1] &&
566 greg 2.13 strchr(".+*/", argv[i][0]) != NULL) {
567 greg 2.15 if (!nmats || mop[nmats-1].binop) {
568 greg 2.14 fprintf(stderr,
569 greg 2.15 "%s: missing matrix argument before '%c' operation\n",
570 greg 2.14 argv[0], argv[i][0]);
571 greg 2.13 return(1);
572     }
573 greg 2.15 mop[nmats-1].binop = argv[i][0];
574 greg 2.1 } else if (argv[i][0] != '-' || !argv[i][1]) {
575 greg 2.13 if (argv[i][0] == '-') {
576     if (stdin_used++) {
577     fprintf(stderr,
578     "%s: standard input used for more than one matrix\n",
579     argv[0]);
580     return(1);
581     }
582     mop[nmats].inspec = stdin_name;
583     } else
584     mop[nmats].inspec = argv[i];
585 greg 2.27 if (!mop[nmats].preop.csym)
586     mop[nmats].preop.csym = defCsym;
587 greg 2.13 if (nmats > 0 && !mop[nmats-1].binop)
588     mop[nmats-1].binop = '.';
589     nmats++;
590 greg 2.1 } else {
591     int n = argc-1 - i;
592     switch (argv[i][1]) { /* get option */
593     case 'v':
594 greg 2.14 verbose++;
595 greg 2.1 break;
596     case 'f':
597     switch (argv[i][2]) {
598     case 'd':
599     outfmt = DTdouble;
600     break;
601     case 'f':
602     outfmt = DTfloat;
603     break;
604     case 'a':
605     outfmt = DTascii;
606     break;
607     case 'c':
608     outfmt = DTrgbe;
609     break;
610     default:
611     goto userr;
612     }
613     break;
614     case 't':
615 greg 2.13 mop[nmats].preop.transpose = 1;
616 greg 2.1 break;
617     case 's':
618     if (n > MAXCOMP) n = MAXCOMP;
619 greg 2.13 i += mop[nmats].preop.nsf =
620     get_factors(mop[nmats].preop.sca,
621     n, argv+i+1);
622 greg 2.22 if (mop[nmats].preop.nsf <= 0) {
623     fprintf(stderr, "%s: -s missing arguments\n",
624     argv[0]);
625     goto userr;
626     }
627 greg 2.1 break;
628 greg 2.27 case 'C':
629     if (!n || isflt(argv[i+1]))
630     goto userr;
631     defCsym = mop[nmats].preop.csym = argv[++i];
632     mop[nmats].preop.clen = 0;
633     break;
634 greg 2.1 case 'c':
635 greg 2.27 if (n && !isflt(argv[i+1])) {
636     mop[nmats].preop.csym = argv[++i];
637 greg 2.22 mop[nmats].preop.clen = 0;
638     break;
639     }
640 greg 2.1 if (n > MAXCOMP*MAXCOMP) n = MAXCOMP*MAXCOMP;
641 greg 2.13 i += mop[nmats].preop.clen =
642     get_factors(mop[nmats].preop.cmat,
643     n, argv+i+1);
644 greg 2.22 if (mop[nmats].preop.clen <= 0) {
645     fprintf(stderr, "%s: -c missing arguments\n",
646     argv[0]);
647     goto userr;
648     }
649 greg 2.27 mop[nmats].preop.csym = NULL;
650 greg 2.1 break;
651 greg 2.18 case 'r':
652     if (argv[i][2] == 'f')
653     mop[nmats].rmp = RMPreflF;
654     else if (argv[i][2] == 'b')
655     mop[nmats].rmp = RMPreflB;
656     else
657     goto userr;
658     break;
659 greg 2.1 default:
660     fprintf(stderr, "%s: unknown operation '%s'\n",
661     argv[0], argv[i]);
662     goto userr;
663     }
664     }
665 greg 2.14 if (nmats >= nall)
666 greg 2.26 mop = resize_moparr(mop, nall += 2);
667 greg 2.13 }
668     if (mop[0].inspec == NULL) /* nothing to do? */
669 greg 2.1 goto userr;
670 greg 2.15 if (mop[nmats-1].binop) {
671     fprintf(stderr,
672     "%s: missing matrix argument after '%c' operation\n",
673     argv[0], mop[nmats-1].binop);
674     return(1);
675     }
676 greg 2.13 /* favor quicker concatenation */
677 greg 2.14 mop[nmats].mtx = prefer_right2left(mop) ? op_right2left(mop)
678     : op_left2right(mop);
679     if (mop[nmats].mtx == NULL)
680     return(1);
681     /* apply trailing unary operations */
682     mop[nmats].inspec = "trailing_ops";
683     mres = loadop(mop+nmats);
684     if (mres == NULL)
685 greg 2.13 return(1);
686 greg 2.22 if (outfmt == DTfromHeader) /* check data type */
687 greg 2.6 outfmt = mres->dtype;
688 greg 2.22 if (outfmt == DTrgbe) {
689     if (mres->ncomp > 3)
690     outfmt = DTspec;
691     else if (mres->dtype == DTxyze)
692     outfmt = DTxyze;
693     }
694 greg 2.26 newheader("RADIANCE", stdout); /* write result to stdout */
695 greg 2.1 printargs(argc, argv, stdout);
696 greg 2.23 return(rmx_write(mres, outfmt, stdout) ? 0 : 1);
697 greg 2.1 userr:
698     fprintf(stderr,
699 greg 2.26 "Usage: %s [-v][-f{adfc}][-t][-s sf .. | -c ce ..][-rf|-rb] m1 [.+*/] .. > mres\n",
700 greg 2.1 argv[0]);
701     return(1);
702     }