| File: | src/limn/renderLimn.c |
| Location: | line 397, column 5 |
| Description: | Value stored to 'part' is never read |
| 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(LIMNlimnBiffKey, "%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)(void)(obj); |
| 57 | AIR_UNUSED(cam)(void)(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)(void)(obj); |
| 98 | AIR_UNUSED(cam)(void)(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)(void)(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)((R) < (0) ? (0) : ((R) > (1) ? (1) : (R))); |
| 140 | G = AIR_CLAMP(0, G, 1)((G) < (0) ? (0) : ((G) > (1) ? (1) : (G))); |
| 141 | B = AIR_CLAMP(0, B, 1)((B) < (0) ? (0) : ((B) > (1) ? (1) : (B))); |
| 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)(void)(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(LIMNlimnBiffKey, "%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(LIMNlimnBiffKey, "%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])((win->bbox[0]) <= (vert->coord[0]) && (vert ->coord[0]) <= (win->bbox[2])) && |
| 228 | AIR_IN_CL(win->bbox[1], vert->coord[1], win->bbox[3])((win->bbox[1]) <= (vert->coord[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_TRUE1; |
| 269 | ELL_3V_SCALE(face->worldNormal, -1, face->worldNormal)((face->worldNormal)[0] = (-1)*(face->worldNormal)[0], ( face->worldNormal)[1] = (-1)*(face->worldNormal)[1], (face ->worldNormal)[2] = (-1)*(face->worldNormal)[2]); |
| 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((void*)0) |
| 289 | : obj->face + edge->faceIdx[1]); |
| 290 | if (!face1) { |
| 291 | edge->type = limnEdgeTypeBorder; |
| 292 | } else { |
| 293 | angle = AIR_CAST(float,((float)(180/3.14159265358979323846*acos(((face0->worldNormal )[0]*(face1->worldNormal)[0] + (face0->worldNormal)[1]* (face1->worldNormal)[1] + (face0->worldNormal)[2]*(face1 ->worldNormal)[2])))) |
| 294 | 180/AIR_PI*acos(ELL_3V_DOT(face0->worldNormal,((float)(180/3.14159265358979323846*acos(((face0->worldNormal )[0]*(face1->worldNormal)[0] + (face0->worldNormal)[1]* (face1->worldNormal)[1] + (face0->worldNormal)[2]*(face1 ->worldNormal)[2])))) |
| 295 | face1->worldNormal)))((float)(180/3.14159265358979323846*acos(((face0->worldNormal )[0]*(face1->worldNormal)[0] + (face0->worldNormal)[1]* (face1->worldNormal)[1] + (face0->worldNormal)[2]*(face1 ->worldNormal)[2])))); |
| 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(LIMNlimnBiffKey, "%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(LIMNlimnBiffKey, "%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_TRUE1; |
| 361 | ELL_3V_SCALE(face->worldNormal, -1, face->worldNormal)((face->worldNormal)[0] = (-1)*(face->worldNormal)[0], ( face->worldNormal)[1] = (-1)*(face->worldNormal)[1], (face ->worldNormal)[2] = (-1)*(face->worldNormal)[2]); |
| 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((void*)0) |
| 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,((float)(180/3.14159265358979323846*acos(((face0->worldNormal )[0]*(face1->worldNormal)[0] + (face0->worldNormal)[1]* (face1->worldNormal)[1] + (face0->worldNormal)[2]*(face1 ->worldNormal)[2])))) |
| 378 | face1->worldNormal)))((float)(180/3.14159265358979323846*acos(((face0->worldNormal )[0]*(face1->worldNormal)[0] + (face0->worldNormal)[1]* (face1->worldNormal)[1] + (face0->worldNormal)[2]*(face1 ->worldNormal)[2])))); |
| 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]; |
Value stored to 'part' is never read | |
| 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_FALSE0; |
| 416 | } else { |
| 417 | /* we're the first to see it, and we're not the last, don't draw */ |
| 418 | edge->once = AIR_TRUE1; |
| 419 | } |
| 420 | } |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | _limnPSEpilogue(obj, cam, win); |
| 425 | |
| 426 | return 0; |
| 427 | } |