GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/limn/renderLimn.c Lines: 0 187 0.0 %
Date: 2017-05-26 Branches: 0 120 0.0 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
25
#include "limn.h"
26
27
int
28
limnObjectRender(limnObject *obj, limnCamera *cam, limnWindow *win) {
29
  static const char me[]="limnObjectRender";
30
  int E;
31
32
  E = 0;
33
  if (!E) E |= limnCameraUpdate(cam);
34
  /*
35
  fprintf(stderr, "%s: true up = %g %g %g\n", me,
36
          -cam->V[0], -cam->V[1], -cam->V[2]);
37
  fprintf(stderr, "%s: true right = %g %g %g\n", me,
38
          cam->U[0], cam->U[1], cam->U[2]);
39
  */
40
  if (!E) E |= limnObjectWorldHomog(obj);
41
  if (!E) E |= limnObjectFaceNormals(obj, limnSpaceWorld);
42
  if (!E) E |= limnObjectSpaceTransform(obj, cam, win, limnSpaceView);
43
  if (!E) E |= limnObjectSpaceTransform(obj, cam, win, limnSpaceScreen);
44
  if (!E) E |= limnObjectFaceNormals(obj, limnSpaceScreen);
45
  if (!E) E |= limnObjectSpaceTransform(obj, cam, win, limnSpaceDevice);
46
  if (E) {
47
    biffAddf(LIMN, "%s: trouble", me);
48
    return 1;
49
  }
50
  return 0;
51
}
52
53
void
54
_limnPSPreamble(limnObject *obj, limnCamera *cam, limnWindow *win) {
55
56
  AIR_UNUSED(obj);
57
  AIR_UNUSED(cam);
58
  fprintf(win->file, "%%!PS-Adobe-2.0 EPSF-2.0\n");
59
  fprintf(win->file, "%%%%Creator: limn\n");
60
  fprintf(win->file, "%%%%Pages: 1\n");
61
  fprintf(win->file, "%%%%BoundingBox: %d %d %d %d\n",
62
          (int)(win->bbox[0]),
63
          (int)(win->bbox[1]),
64
          (int)(win->bbox[2]),
65
          (int)(win->bbox[3]));
66
  fprintf(win->file, "%%%%EndComments\n");
67
  fprintf(win->file, "%%%%EndProlog\n");
68
  fprintf(win->file, "%%%%Page: 1 1\n");
69
  fprintf(win->file, "gsave\n");
70
  fprintf(win->file, "%g %g moveto\n", win->bbox[0], win->bbox[1]);
71
  fprintf(win->file, "%g %g lineto\n", win->bbox[2], win->bbox[1]);
72
  fprintf(win->file, "%g %g lineto\n", win->bbox[2], win->bbox[3]);
73
  fprintf(win->file, "%g %g lineto\n", win->bbox[0], win->bbox[3]);
74
  fprintf(win->file, "closepath\n");
75
  if (!win->ps.noBackground) {
76
    fprintf(win->file, "gsave %g %g %g setrgbcolor fill grestore\n",
77
            win->ps.bg[0], win->ps.bg[1], win->ps.bg[2]);
78
  }
79
  fprintf(win->file, "clip\n");
80
  fprintf(win->file, "gsave newpath\n");
81
  fprintf(win->file, "1 setlinejoin\n");
82
  fprintf(win->file, "1 setlinecap\n");
83
  fprintf(win->file, "/M {moveto} bind def\n");
84
  fprintf(win->file, "/L {lineto} bind def\n");
85
  fprintf(win->file, "/W {setlinewidth} bind def\n");
86
  fprintf(win->file, "/F {fill} bind def\n");
87
  fprintf(win->file, "/S {stroke} bind def\n");
88
  fprintf(win->file, "/CP {closepath} bind def\n");
89
  fprintf(win->file, "/RGB {setrgbcolor} bind def\n");
90
  fprintf(win->file, "/Gr {setgray} bind def\n");
91
  fprintf(win->file, "\n");
92
}
93
94
void
95
_limnPSEpilogue(limnObject *obj, limnCamera *cam, limnWindow *win) {
96
97
  AIR_UNUSED(obj);
98
  AIR_UNUSED(cam);
99
  fprintf(win->file, "grestore\n");
100
  fprintf(win->file, "grestore\n");
101
  if (win->ps.showpage) {
102
    fprintf(win->file, "showpage\n");
103
  }
104
  fprintf(win->file, "%%%%Trailer\n");
105
}
106
107
void
108
_limnPSDrawFace(limnObject *obj, limnFace *face,
109
                limnCamera *cam, Nrrd *nmap, limnWindow *win) {
110
  /* static const char me[]="_limnPSDrawFace"; */
111
  unsigned int vii;
112
  limnVertex *vert;
113
  limnLook *look;
114
  int qn;
115
  float *map, R, G, B;
116
117
  AIR_UNUSED(cam);
118
  look = obj->look + face->lookIdx;
119
  for (vii=0; vii<face->sideNum; vii++) {
120
    vert = obj->vert + face->vertIdx[vii];
121
    fprintf(win->file, "%g %g %s\n",
122
            vert->coord[0], vert->coord[1], vii ? "L" : "M");
123
  }
124
  R = look->kads[0]*look->rgba[0];
125
  G = look->kads[0]*look->rgba[1];
126
  B = look->kads[0]*look->rgba[2];
127
  if (nmap) {
128
    qn = limnVtoQN_f[limnQN16octa](face->worldNormal);
129
    map = (float *)nmap->data;
130
    R += look->kads[1]*look->rgba[0]*map[0 + 3*qn];
131
    G += look->kads[1]*look->rgba[1]*map[1 + 3*qn];
132
    B += look->kads[1]*look->rgba[2]*map[2 + 3*qn];
133
  } else {
134
    R += look->kads[1]*look->rgba[0];
135
    G += look->kads[1]*look->rgba[1];
136
    B += look->kads[1]*look->rgba[2];
137
  }
138
  /* HEY: not evaluating phong specular for now */
139
  R = AIR_CLAMP(0, R, 1);
140
  G = AIR_CLAMP(0, G, 1);
141
  B = AIR_CLAMP(0, B, 1);
142
  if (0 && R == G && G == B) {
143
    /* As of Sat Mar 1 23:06:14 CST 2014 some version of ghostscript
144
       and/or imagemagick will assign (when rasterizing) different
145
       colors for RGB color (g,g,g) and graylevel g, which caused
146
       strange appearance bugs that were hard to track down. Even if
147
       there's a way of preventing this from happening with the right
148
       incantation in the EPS header, for now it is simpler to forego
149
       the small economy implemented here */
150
    fprintf(win->file, "CP %g Gr F\n", R);
151
  }
152
  else {
153
    fprintf(win->file, "CP %g %g %g RGB F\n", R, G, B);
154
  }
155
}
156
157
void
158
_limnPSDrawEdge(limnObject *obj, limnEdge *edge,
159
                limnCamera *cam, limnWindow *win) {
160
  limnVertex *vert0, *vert1;
161
  float R, G, B;
162
163
  AIR_UNUSED(cam);
164
  if (win->ps.lineWidth[edge->type]) {
165
    vert0 = obj->vert + edge->vertIdx[0];
166
    vert1 = obj->vert + edge->vertIdx[1];
167
    fprintf(win->file, "%g %g M ", vert0->coord[0], vert0->coord[1]);
168
    fprintf(win->file, "%g %g L ", vert1->coord[0], vert1->coord[1]);
169
    fprintf(win->file, "%g W ", win->ps.lineWidth[edge->type]);
170
    R = win->ps.edgeColor[0];
171
    G = win->ps.edgeColor[1];
172
    B = win->ps.edgeColor[2];
173
    if (R == G && G == B) {
174
      fprintf(win->file, "%g Gr S\n", R);
175
    } else {
176
      fprintf(win->file, "%g %g %g RGB S\n", R, G, B);
177
    }
178
  }
179
}
180
181
/*
182
******** limnObjectPSDraw
183
**
184
** draws a "rendered" limn object to postscript.
185
** limnObjectRender MUST be called first.
186
**
187
** The current (feeble) justification for using an environment map is
188
** that its an expressive way of shading things based on surface
189
** normal, in a context where, if flat shading is all you have,
190
** correct specular lighting is not possible
191
*/
192
int
193
limnObjectPSDraw(limnObject *obj, limnCamera *cam,
194
                 Nrrd *nmap, limnWindow *win) {
195
  static const char me[]="limnObjectPSDraw";
196
  int inside;
197
  float angle;
198
  limnFace *face, *face0, *face1; unsigned int fii;
199
  limnEdge *edge; unsigned int eii;
200
  limnPart *part; unsigned int partIdx;
201
  limnVertex *vert; unsigned int vii;
202
203
  if (limnSpaceDevice != obj->vertSpace) {
204
    biffAddf(LIMN, "%s: object's verts in %s (not %s) space", me,
205
             airEnumStr(limnSpace, obj->vertSpace),
206
             airEnumStr(limnSpace, limnSpaceDevice));
207
    return 1;
208
  }
209
  if (nmap) {
210
    if (limnEnvMapCheck(nmap)) {
211
      biffAddf(LIMN, "%s: trouble", me);
212
      return 1;
213
    }
214
  }
215
216
  limnObjectDepthSortParts(obj);
217
218
  _limnPSPreamble(obj, cam, win);
219
220
  for (partIdx=0; partIdx<obj->partNum; partIdx++) {
221
    part = obj->part[partIdx];
222
223
    /* only draw the parts that are inside the field of view */
224
    inside = 0;
225
    for (vii=0; vii<part->vertIdxNum; vii++) {
226
      vert = obj->vert + part->vertIdx[vii];
227
      inside |= (AIR_IN_CL(win->bbox[0], vert->coord[0], win->bbox[2]) &&
228
                 AIR_IN_CL(win->bbox[1], vert->coord[1], win->bbox[3]));
229
      if (inside) {
230
        /* at least vertex is in, we know we can't skip this part */
231
        break;
232
      }
233
    }
234
    if (!inside) {
235
      /* none of the vertices were in, we can skip this part */
236
      continue;
237
    }
238
239
    /* draw the part */
240
    if (1 == part->edgeIdxNum) {
241
      /* this part is just one lone edge */
242
      /* HEY: this is a mess */
243
      /*
244
      e = &(obj->e[r->eBase]);
245
      widthTmp = win->ps.lineWidth[e->type];
246
      fprintf(win->file, "%g setgray\n", 1 - win->ps.bg[0]);
247
      win->ps.edgeWidth[e->type] = 8;
248
      _limnPSDrawEdge(obj, r, e, cam, win);
249
      fprintf(win->file, "%g %g %g RGB\n",
250
              r->rgba[0], r->rgba[1], r->rgba[2]);
251
      win->ps.edgeWidth[e->visible] = 4;
252
      _limnPSDrawEdge(obj, r, e, cam, win);
253
      win->ps.edgeWidth[e->visible] = widthTmp;
254
      */
255
    } else {
256
      /* this part is either a lone face or a solid:
257
         draw the front-facing, shaded faces */
258
      for (fii=0; fii<part->faceIdxNum; fii++) {
259
        face = obj->face + part->faceIdx[fii];
260
        /* The consequence of having a left-handed frame is that world-space
261
           CC-wise vertex traversal becomes C-wise screen-space traversal, so
262
           all the normals are backwards of what we want */
263
        face->visible = (cam->rightHanded
264
                         ? face->screenNormal[2] < 0
265
                         : face->screenNormal[2] > 0);
266
        if (face->sideNum == part->vertIdxNum && !face->visible) {
267
          /* lone faces are always visible */
268
          face->visible = AIR_TRUE;
269
          ELL_3V_SCALE(face->worldNormal, -1, face->worldNormal);
270
        }
271
        if (!win->ps.wireFrame && face->visible) {
272
          _limnPSDrawFace(obj, face, cam, nmap, win);
273
        }
274
      }
275
276
      /* draw ALL edges */
277
      for (eii=0; eii<part->edgeIdxNum; eii++) {
278
        /* hack to change contour of particular object/glyph
279
        if (24 == partIdx) {
280
          win->ps.lineWidth[limnEdgeTypeContour] = 1.2;
281
        } else {
282
          win->ps.lineWidth[limnEdgeTypeContour] = 0.4;
283
        }
284
        */
285
        edge = obj->edge + part->edgeIdx[eii];
286
        face0 = obj->face + edge->faceIdx[0];
287
        face1 = (-1 == edge->faceIdx[1]
288
                 ? NULL
289
                 : obj->face + edge->faceIdx[1]);
290
        if (!face1) {
291
          edge->type = limnEdgeTypeBorder;
292
        } else {
293
          angle = AIR_CAST(float,
294
                           180/AIR_PI*acos(ELL_3V_DOT(face0->worldNormal,
295
                                                      face1->worldNormal)));
296
          if (face0->visible && face1->visible) {
297
            edge->type = (angle > win->ps.creaseAngle
298
                          ? limnEdgeTypeFrontCrease
299
                          : limnEdgeTypeFrontFacet);
300
          } else if (face0->visible ^ face1->visible) {
301
            edge->type = limnEdgeTypeContour;
302
          } else {
303
            edge->type = (angle > win->ps.creaseAngle
304
                          ? limnEdgeTypeBackCrease
305
                          : limnEdgeTypeBackFacet);
306
          }
307
        }
308
        _limnPSDrawEdge(obj, edge, cam, win);
309
      }
310
    }
311
  }
312
313
  _limnPSEpilogue(obj, cam, win);
314
315
  return 0;
316
}
317
318
/*
319
******** limnObjectPSDrawConcave
320
**
321
** new version of the above, which works per-face instead of per-part,
322
** thus better handling self-occlusions, but at the cost of not getting
323
** contours near oblique faces correct...
324
*/
325
int
326
limnObjectPSDrawConcave(limnObject *obj, limnCamera *cam,
327
                     Nrrd *nmap, limnWindow *win) {
328
  static const char me[]="limnObjectPSDrawConcave";
329
  float angle;
330
  limnPart *part;
331
  limnFace *face, *face0, *face1; unsigned int faceIdx;
332
  limnEdge *edge; unsigned int edgeIdx, eii;
333
334
  if (limnSpaceDevice != obj->vertSpace) {
335
    biffAddf(LIMN, "%s: object's verts in %s (not %s) space", me,
336
             airEnumStr(limnSpace, obj->vertSpace),
337
             airEnumStr(limnSpace, limnSpaceDevice));
338
    return 1;
339
  }
340
  if (nmap) {
341
    if (limnEnvMapCheck(nmap)) {
342
      biffAddf(LIMN, "%s: trouble", me);
343
      return 1;
344
    }
345
  }
346
347
  limnObjectDepthSortFaces(obj);
348
349
  _limnPSPreamble(obj, cam, win);
350
351
  /* set every face's visibility */
352
  for (faceIdx=0; faceIdx<obj->faceNum; faceIdx++) {
353
    face = obj->face + faceIdx;
354
    part = obj->part[face->partIdx];
355
    face->visible = (cam->rightHanded
356
                     ? face->screenNormal[2] < 0
357
                     : face->screenNormal[2] > 0);
358
    if (face->sideNum == part->vertIdxNum && !face->visible) {
359
      /* lone faces are always visible */
360
      face->visible = AIR_TRUE;
361
      ELL_3V_SCALE(face->worldNormal, -1, face->worldNormal);
362
    }
363
  }
364
365
  /* categorize all edges by traversing edge array, and looking
366
     at each of their two faces */
367
  for (edgeIdx=0; edgeIdx<obj->edgeNum; edgeIdx++) {
368
    edge = obj->edge + edgeIdx;
369
    part = obj->part[edge->partIdx];
370
    face0 = obj->face + edge->faceIdx[0];
371
    face1 = (-1 == edge->faceIdx[1]
372
             ? NULL
373
             : obj->face + edge->faceIdx[1]);
374
    if (!face1) {
375
      edge->type = limnEdgeTypeBorder;
376
    } else {
377
      angle = AIR_CAST(float, 180/AIR_PI*acos(ELL_3V_DOT(face0->worldNormal,
378
                                                         face1->worldNormal)));
379
      if (face0->visible && face1->visible) {
380
        edge->type = (angle > win->ps.creaseAngle
381
                      ? limnEdgeTypeFrontCrease
382
                      : limnEdgeTypeFrontFacet);
383
      } else if (face0->visible ^ face1->visible) {
384
        edge->type = limnEdgeTypeContour;
385
      } else {
386
        edge->type = (angle > win->ps.creaseAngle
387
                      ? limnEdgeTypeBackCrease
388
                      : limnEdgeTypeBackFacet);
389
      }
390
    }
391
  }
392
393
  /* draw front-faces and their edges
394
     (contours, front crease, front non-crease) */
395
  for (faceIdx=0; faceIdx<obj->faceNum; faceIdx++) {
396
    face = obj->faceSort[faceIdx];
397
    part = obj->part[face->partIdx];
398
    if (!face->visible) {
399
      continue;
400
    }
401
    if (!win->ps.wireFrame) {
402
      _limnPSDrawFace(obj, face, cam, nmap, win);
403
    }
404
    /* draw those edges around the face that won't be seen again by
405
       future faces in the depth-first traversal */
406
    for (eii=0; eii<face->sideNum; eii++) {
407
      edge = obj->edge + face->edgeIdx[eii];
408
      if (limnEdgeTypeContour == edge->type) {
409
        _limnPSDrawEdge(obj, edge, cam, win);
410
      } else if (limnEdgeTypeFrontCrease == edge->type
411
                 || limnEdgeTypeFrontFacet == edge->type) {
412
        if (edge->once) {
413
          /* its been seen once already, okay to draw */
414
          _limnPSDrawEdge(obj, edge, cam, win);
415
          edge->once = AIR_FALSE;
416
        } else {
417
          /* we're the first to see it, and we're not the last, don't draw */
418
          edge->once = AIR_TRUE;
419
        }
420
      }
421
    }
422
  }
423
424
  _limnPSEpilogue(obj, cam, win);
425
426
  return 0;
427
}