ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.41
Committed: Fri Apr 18 23:59:03 2025 UTC (6 weeks, 4 days ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.40: +22 -5 lines
Log Message:
refactor(rmtxop,rcomb,pvsum): Removed BSDF library dependency where unneeded

File Contents

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