ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.23
Committed: Tue Nov 28 16:36:50 2023 UTC (4 months, 4 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.22: +5 -13 lines
Log Message:
fix(rmtxop): Eliminated redundant error messages

File Contents

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