ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.28
Committed: Sun Dec 3 02:28:33 2023 UTC (4 months, 3 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.27: +8 -7 lines
Log Message:
fix(rmtxop): Corrected scalar post-multiplying component transform

File Contents

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