ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.40
Committed: Fri Apr 4 22:47:56 2025 UTC (4 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.39: +3 -3 lines
Log Message:
fix(rmtxop,rcomb): Minor fixes to auto-typing, preferring native output

File Contents

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