/* * Copyright (C) 1997-2005, R3vis Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA, or visit http://www.gnu.org/copyleft/lgpl.html. * * Original Contributor: * Wes Bethel, R3vis Corporation, Marin County, California * Additional Contributor(s): * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: rmps.c,v 1.5 2005/06/15 02:10:40 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.5 $ * $Log: rmps.c,v $ * Revision 1.5 2005/06/15 02:10:40 wes * Added initial support for application-settable defaults. Apps * will use rmSetEnum/rmGetEnum to set or get defaults that are RMenums. * The first round of variables that can be set/get by apps are * RMnode traversal masks assigned to new scene graph nodes by rmNodeNew. * * Revision 1.4 2005/06/12 21:48:44 wes * Minor tweaks to avoid compiler warnings. * * Revision 1.3 2005/06/06 02:04:29 wes * Lots of small additions to clean up compiler warnings. * * Revision 1.2 2005/03/16 16:46:40 wes * More code merge activities. * * Revision 1.1 2005/02/19 16:36:40 wes * Distro merge and consolidation. * */ #include #include "rmprivat.h" #include "rmprivatps.h" /* * * Last Updated: Sat Mar 21 11:41:56 PST 1998 * * Sat Mar 21 11:41:56 PST 1998. add code to support path-preserving * for polylines. dashing patterns will be correctly rendered along * an arbitrary-length polyline so long as all the pieces are contained * within one path. the "path preserve flag" is set in rmLinesConnected * at render time. as feedback tokens are processed, a path index is * incremented on each new polyline (triggered by each new * RM_PATH_PRESERVE_FLAG==1 opcode sent over from rmLinesConnected()). * the hard part is to maintain the notion of the path in the presence * of a possibly broken path, which could potentially happen during * the painters() sort and split operations. therefore, we don't try * to maintain the path except in the following set of strict conditions * (which will always work in 2d, but may have odd side effects in * 3d -- keep in mind this happens only when a dashing pattern is * requested): * line reset token (generated at the start of a polyline) followed * by some arbitrary number of line tokens which have the same * current path index. whenever these conditions are not met (in * the dispatch() routine in this file), the dashing pattern is * effectively reset. * * Sat Nov 22 18:53:36 PST 1997 * 2d ellipses, circles, boxes. * * Sun Oct 5 07:02:16 PDT 1997 * added application callbacks for color conversion. * * Thu Aug 28 11:27:40 MDT 1997 * heartbeat function, early exit from painters, fast vs. slow * vector mode. * * Wed Aug 27 11:09:47 MDT 1997 * graceful error recovery * * Tue Jul 29 18:46:52 PDT 1997 * implement modified painter's sorting. * * Sat Jun 14 19:15:03 PDT 1997 * support for cached-glyph text primitives (RM_TEXTCHARS prim). * * Thu Jun 5 15:33:39 MDT 1997 - added support for image tiling. * added rectclip clipping rectangle - this is dumped on the root * node ONLY. if the root node doesn't have a viewport scene * parameter, there will be no global clipping to the outline * rectangle. * * * * TODO: * * 1. text - does the font need to be inversely scaled so that if the * user asks for 14 point they get 14 point? * * Bugs. * - very small lines don't show up. */ /*#define RM_PS_ZOFFSET 0.0005F */ #define RM_PS_ZOFFSET 0.0 /*#define PRINT_FEEDBACK_BUFFER 1 */ #ifdef PRINT_FEEDBACK_BUFFER static FILE *dfile; #endif /* * when dumping pixel data (ie, image tiles), the maximum number of * pixels that can be stored into a single string variable is the * following: */ /* #define RM_PS_MAX_TILESIZE_PIXELS (65536 / 3) */ /* #define RM_PS_MAX_TILESIZE_PIXELS ((65536 >> 2) / 3) */ #define RM_PS_MAX_TILESIZE_PIXELS ((65536 / 3)) /* * the following few arrays are used in constructing a font name that * is recognizable to ps interpreters. */ /* * ---------------------------------------------------- * @Name rmPSSpecNew @pstart RMpsSpec * rmPSSpecNew (void) @pend @astart (No arguments) @aend @dstart Use this routine to create a new RMpsSpec object, which is used to specify PostScript output options to the RM renderer. Upon success, this routine returns a handle to an RMpsSpec object that has been initialized with default values (see below). Upon failure, NULL is returned. To use the RMpsSpec object to create PostScript output, you may modify PostScript output attributes by using the appropriate routines, then provide the RMpsSpec object as input along with a scene graph and an RMpipe to the RM PostScript rendering utility, rmFramePS(). Upon creation, the new RMpsSpec object contains the following default values: 1. Page size : 612 points wide by 792 points high. These dimensions correspond to an 8.5 by 11 inch US letter document at 72 points/inch. Use the routines rmPSPageSetSize/rmPSPageGetSize to set/get the page dimensions for PS output. 2. Page orientation: RM_PS_PORTAIT. The document uses a default orientation of portrait. Use the routines rmPSPageSetOrientation/rmPSPageGetOrientation to set/get the page orientation. 3. Margin: The minimum margin is 36 points (0.5 inches). Use the routines rmPSPageSetMargin/rmPSPageGetMargin to set/get the margin. 4. PS Format: The default PostScript output format is RM_PS_REGULAR, or regular PostScript output (the alternative would be RM_PS_EPS for Encapsulated PostScript). Use the routines rmPSPageSetOutputFormat/ rmPSPageGetOutputFormat to set/get the PostScript output format. 5. Vector output format: The default vector output form is RM_PS_VECTOR (the alternative is RM_PS_RASTER). Use the routines rmPSPageSetVectorFormat/ rmPSPageGetVectorFormat to set/get the output vector format. 6. Sorting method: The default sorting method used to convert from 3D scenes to ordered 2D PostScript primitives is RM_PS_SORT_FULL. Use the routines rmPSSetSortMethod/rmPSGetSortMethod to set/get the sort attributes. 7. Output filename: The default output filename is "rmPSFile.ps". Use the routines rmPSSetOutputFilename/rmPSGetOutputFilename to set/get the file name where PostScript output will be placed. Note: you could malloc() your own RMpsSpec object, or use one declared off the heap if you so desire. The advantage of using rmPSSpecNew() is that the new RMpsSpec object will be initialized to a set of known default values. @dend * ---------------------------------------------------- */ RMpsSpec *rmPSSpecNew(void) { RMpsSpec *t = (RMpsSpec *)calloc(1, sizeof(RMpsSpec)); if (RM_ASSERT(t, "rmPSSpecNew() error - unable to malloc space for a new RMpsSpec object. \n") == RM_WHACKED) return NULL; rmPSPageSetSize(t, RM_PS_DEFAULT_PAGE_WIDTH_POINTS, RM_PS_DEFAULT_PAGE_HEIGHT_POINTS); rmPSPageSetOrientation(t, RM_PS_DEFAULT_ORIENTATION); rmPSPageSetMargin(t, RM_PS_DEFAULT_MIN_MARGIN); rmPSPageSetOutputFormat(t, RM_PS_REGULAR); /* non-EPS, or regular PS, is the default */ rmPSPageSetVectorFormat(t, RM_PS_VECTOR); /* default is vector, not raster PS output format */ rmPSSetSortMethod(t, RM_PS_SORT_FULL); rmPSSetOutputFilename(t, "rmPSFile.ps"); return t; } /* * ---------------------------------------------------- * @Name rmPSSpecDelete @pstart RMenum rmPSSpecDelete (RMpsSpec *toDelete) @pend @astart RMpsSpec *toDelete - an handle to an RMpsSpec object that will be deleted. @aend @dstart Use this routine to delete an RMpsSpec object created by rmPSSpecNew. Upon success, RM_CHILL is returned and the RMpsSpec object toDelete is deleted. Upon failure, RM_WHACKED is returned and the object toDelete is not deleted. @dend * ---------------------------------------------------- */ RMenum rmPSSpecDelete(RMpsSpec *t) { if (RM_ASSERT(t, "rmPSSpecDelete() warning - the input RMpsSpec pointer is NULL") == RM_WHACKED) return RM_WHACKED; free((void *)t); return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageSetSize @pstart RMenum rmPSPageSetSize (RMpsSpec *toModify, int pagePointsWidth, int pagePointsHeight) @pend @astart RMpsSpec *toModify - an handle to an RMpsSpec object that will be modified. int pagePointsWidth, pagePointsHeight - the width and height in points of the output PostScript document. @aend @dstart This routine will set the output page size dimensions in points of the output PostScript image. Upon success, RM_CHILL is returned and the RMpsSpec object toModify will be updated to contain the new size attributes specified by the caller. Upon failure, RM_WHACKED is returned and the RMpsSpec object toModify is left unmodified. @dend * ---------------------------------------------------- */ RMenum rmPSPageSetSize(RMpsSpec *p, int pagePointsWidth, int pagePointsHeight) { if (RM_ASSERT(p, "rmPSPageSetSize() warning - the input RMpsSpec pointer is NULL") == RM_WHACKED) return RM_WHACKED; p->psWidthPoints = pagePointsWidth; p->psHeightPoints = pagePointsHeight; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageGetSize @pstart RMenum rmPSPageGetSize (const RMpsSpec *toQuery, int *returnPagePointsWidth, int *returnPagePointsHeight) @pend @astart const RMpsSpec *toModify - an handle to an RMpsSpec object that will be queried. int *returnPagePointsWidth, *returnPagePointsHeight - pointers to caller- supplied memory where results will be placed. @aend @dstart Use this routine to query the PostScript page size dimensions. Upon success, RM_CHILL is returned, and the page size width and height (in points) will be copied into caller-supplied memory. Upon failure, RM_WHACKED is returned and the caller-supplied memory will remain unchanged. Note that you may use a value of NULL for either of returnPagePointsWidth or returnPagePointsHeight if you wish to obtain only one, both or none of the page size attributes. @dend * ---------------------------------------------------- */ RMenum rmPSPageGetSize(const RMpsSpec *p, int *pagePointsWidth, int *pagePointsHeight) { if (RM_ASSERT(p, "rmPSPageGetSize() warning - the input RMpsSpec pointer is NULL") == RM_WHACKED) return RM_WHACKED; if (pagePointsWidth != NULL) *pagePointsWidth = p->psWidthPoints; if (pagePointsHeight != NULL) *pagePointsHeight = p->psHeightPoints; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageSetOrientation @pstart RMenum rmPSPageSetOrientation (RMpsSpec *toModify, RMenum orientation) @pend @astart RMpsSpec *toModify - an handle to an RMpsSpec object that will be modified. RMenum orientation - an enumerator value that specifies the orientation for the PostScript output. Must be one of RM_PS_PORTRAIT or RM_PS_LANDSCAPE. @aend @dstart Use this routine to specify the page orientation for PostScript output. When you specify RM_PS_PORTRAIT, the imageable area is oriented such that the width axis of the output PostScript is oriented along the width axis of the output page. When you specify RM_PS_LANDSCAPE, the width axis of the output PostScript is oriented along the height axis of the output page. Upon success, this routine returns RM_CHILL and updates the RMpsSpec object toModify. Upon failure, which can occur of the RMpsSpec object is NULL or if the input orientation enumerator is neither RM_PS_PORTRAIT nor RM_PS_LANDSCAPE, RM_WHACKED is returned and the RMpsSpec object is not modified. @dend * ---------------------------------------------------- */ RMenum rmPSPageSetOrientation(RMpsSpec *p, RMenum orientation) { if (RM_ASSERT(p, "rmPSPageSetOrientation() warning - the input RMpsSpec pointer is NULL") == RM_WHACKED) return RM_WHACKED; if ((orientation != RM_PS_PORTRAIT) && (orientation != RM_PS_LANDSCAPE)) { rmWarning("rmPSPageSetOrientation() warning - the input orientation parameter is neither RM_PS_LANDSCAPE nor RM_PS_PORTRAIT"); return RM_WHACKED; } p->psOrientation = orientation; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageGetOrientation @pstart RMenum rmPSPageGetOrientation (const RMpsSpec *toQuery) @pend @astart const RMpsSpec *toQuery - an handle to an RMpsSpec object that will be queried. @aend @dstart Use this routine to query the page orientation attribute of an RMpsSpec object. Upon failure, RM_WHACKED is returned. Upon success, the page orientation attribute is returned (RM_PS_LANDSCAPE or RM_PS_PORTRAIT). @dend * ---------------------------------------------------- */ RMenum rmPSPageGetOrientation(const RMpsSpec *p) { if (RM_ASSERT(p, "rmPSPageGetOrientation() warning - the input RMpsSpec pointer is NULL") == RM_WHACKED) return RM_WHACKED; return p->psOrientation; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageSetOutputFormat @pstart RMenum rmPSPageSetOutputFormat (RMpsSpec *toModify, RMenum newFormat) @pend @astart RMpsSpec *toModify - an RM object holding PostScript generation options. RMenum newFormat - a format enumerator for the type of PS to output. Must be one of RM_PS_REGULAR or RM_PS_EPS. @aend @dstart There are many parameters that affect how RM produces PostScript output from a scene graph. These parameters are all specified to RM via an RMpsSpec object. See rmPSSpecNew() for information about creating RMpsSpec objects, and see the RM Programming Guide for complete information about all the PostScript output attributes. Use this routine to specify whether the PostScript output file will be "normal" PostScript (RM_PS_REGULAR) or Encapsulated PostScript (RM_PS_EPS). Returns RM_CHILL upon success. RM_WHACKED is returned if the toModify parameter is NULL, or if newFormat is not one of RM_PS_REGULAR or RM_PS_EPS. @dend * ---------------------------------------------------- */ RMenum rmPSPageSetOutputFormat(RMpsSpec *p, RMenum outputFormat) { /* input should be RM_PS_REGULAR or RM_PS_EPS */ if (RM_ASSERT(p,"rmPSPageSetOutputFormat error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; if ((outputFormat != RM_PS_EPS) && (outputFormat != RM_PS_REGULAR)) { rmWarning("rmPSPageSetOutputFormat warning: the outputFormat parameter is neither RM_PS_EPS nor RM_PS_REGULAR"); return RM_WHACKED; } p->psOutputFormat = outputFormat; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageGetOutputFormat @pstart RMenum rmPSPageGetOutputFormat (const RMpsSpec *toQuery) @pend @astart const RMpsSpec *toQuery - an RM object holding PostScript generation options. @aend @dstart Use this routine to obtain the PostScript output format specifier. Upon success, on of RM_PS_REGULAR or RM_PS_EPS will be returned. Upon failure, RM_WHACKED will be returned. @dend * ---------------------------------------------------- */ RMenum rmPSPageGetOutputFormat(const RMpsSpec *p) { if (RM_ASSERT(p,"rmPSPageGetOutputFormat error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; return p->psOutputFormat; } /* * ---------------------------------------------------- * @Name rmPSPageSetVectorFormat @pstart RMenum rmPSPageSetVectorFormat (RMpsSpec *toModify, RMenum newFormat) @pend @astart RMpsSpec *toModify - an RM object holding PostScript generation options. RMenum newFormat - input enumerator that must be either RM_PS_VECTOR or RM_PS_RASTER. @aend @dstart Use this routine to select between vector (RM_PS_VECTOR) or raster (RM_PS_RASTER) PostScript formats. Upon success, RM_CHILL is returned and the new PostScript output will be applied to the RMpsSpec object toModify. Upon failure, RM_WHACKED will be returned, and the RMpsSpec object toModify will not be modified. Failure will occur if the input RMpsSpec object toModify is NULL, or if the input RMenum newFormat is neither RM_PS_VECTOR nor RM_PS_RASTER. @dend * ---------------------------------------------------- */ RMenum rmPSPageSetVectorFormat(RMpsSpec *p, RMenum newFormat) { /* input should be RM_PS_VECTOR or RM_PS_RASTER */ if (RM_ASSERT(p,"rmPSPageSetVectorFormat error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; if ((newFormat != RM_PS_VECTOR) && (newFormat != RM_PS_RASTER)) { rmWarning("rmPSPageSetVectorFormat warning: the newFormat parameter is neither RM_PS_VECTOR nor RM_PS_RASTER. \n"); return RM_WHACKED; } p->psRasterOrVector = newFormat; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageGetVectorFormat @pstart RMenum rmPSPageGetVectorFormat (const RMpsSpec *toQuery) @pend @astart const RMpsSpec *toQuery - an RM object holding PostScript generation options. @aend @dstart Use this routine to obtain the raster vs. vector PostScript output format attribute from an RMpsSpec object. Upon success, one of RM_PS_RASTER or RM_PS_VECTOR will be returned. Upon failure, RM_WHACKED is returned. @dend * ---------------------------------------------------- */ RMenum rmPSPageGetVectorFormat(const RMpsSpec *p) { if (RM_ASSERT(p,"rmPSPageGetVectorFormat error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; return p->psRasterOrVector; } /* * ---------------------------------------------------- * @Name rmPSPageSetMargin @pstart RMenum rmPSPageSetMargin (RMpsSpec *toModify, int minMarginPoints) @pend @astart RMpsSpec *toModify - an handle to an RMpsSpec object that will be modified. int minMarginPoints - an integer value specifing the minimum margin size in units of points (one point = 1/72 of an inch). @aend @dstart Use this routine to set the minimum margin (in points) attribute of the RMpsSpec object toModify. Upon success, RM_CHILL is returned and the RMpsSpec object toModify is updated. Upon failure, RM_WHACKED is returned. The term "minimum margin" means that a single margin value is used to specify minimum margin sizes that will be used when computing the mapping to the printable page. Under normal circumstances, the rendered image is automatically sized to fill the full PS output page. The margin attribute controls how much whitespace remains at the border of the page. For example, if you specify a 0.5inch minimum margin (36 points) on an A4 output page (8.5inches by 11 inches), portrait orientation and your on-screen window is 400 by 300 pixels, the imageable area will fill the page width except for 0.5 inches of margin at each of the left and right sides. The top and bottom margin will be much larger, however, due to the fact that you are mapping the larger imageable area size to the smaller page dimension. @dend * ---------------------------------------------------- */ RMenum rmPSPageSetMargin(RMpsSpec *p, int minMarginPoints) { if (RM_ASSERT(p,"rmPSPageSetMargin error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; p->psMinMargin = minMarginPoints; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSPageGetMargin @pstart RMenum rmPSPageGetMargin (const RMpsSpec *toModify, int *marginPointsReturn) @pend @astart const RMpsSpec *toQuery - an handle to an RMpsSpec object that will be queried. int * marginPointsReturn - a handle to a caller-supplied integer. The margin size attribute of the input RMpsSpec object will be copied into caller-supplied memory. @aend @dstart Use this routine to obtain the minimum margin attribute from an RMpsSpec object. Upon success, RM_CHILL is returned and the margin attribute value is copied into caller-supplied memory. Upon failure, RM_WHACKED is returned and the caller-supplied memory remains unmodified. Note that it is permissible to specify a value of NULL for the caller-supplied memory. @dend * ---------------------------------------------------- */ RMenum rmPSPageGetMargin(const RMpsSpec *p, int *minMarginPointsReturn) { if (RM_ASSERT(p,"rmPSPageGetMargin error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; if (minMarginPointsReturn != NULL) *minMarginPointsReturn = p->psMinMargin; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSSetOutputFilename @pstart RMenum rmPSSetOutputFilename (RMpsSpec *toModify, const char *fileName) @pend @astart RMpsSpec *toModify - an handle to an RMpsSpec object that will be modified. const char *fileName - a character string (input). @aend @dstart Use this routine to set the name of the file to which PostScript output will be placed when you call rmFramePS() to render a scene to PostScript. Upon success, this routine will return RM_CHILL, and the input filename will be copied into the RMpsSpec object toModify. Upon failure, RM_WHACKED is returned and the RMpsSpec object toModify will remain unmodified. @dend * ---------------------------------------------------- */ RMenum rmPSSetOutputFilename(RMpsSpec *p, const char *fName) { if (RM_ASSERT(p,"rmPSSetOutputFilename error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; if ((fName != NULL) && (strlen(fName) > 0)) { if (p->fName != NULL) free((void *)(p->fName)); p->fName = strdup(fName); } return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSGetOutputFilename @pstart const char * rmPSGetOutputFilename (const RMpsSpec *toQuery) @pend @astart const RMpsSpec *toQuery - an handle to an RMpsSpec object to query. @aend @dstart Use this routine to obtain read-only access to the filename from an RMpsSpec object where PostScript output will be written. Upon failure, NULL is returned. Upon success, a read-only character string is returned. @dend * ---------------------------------------------------- */ const char * rmPSGetOutputFilename(const RMpsSpec *p) { if (RM_ASSERT(p,"rmPSGetOutputFilename error - the input RMpsSpec object is NULL") == RM_WHACKED) return NULL; return p->fName; } /* * ---------------------------------------------------- * @Name rmPSSetSortMethod @pstart RMenum rmPSSetSortMethod (RMpsSpec *toModify, RMenum sortMethod) @pend @astart RMpsSpec *toModify - an RM object holding PostScript generation options. RMenum sortMethod - an enumerator that specifies which sorting method will be used when creating the PS file. @aend @dstart There are many parameters that affect how RM produces PostScript output from a scene graph. These parameters are all specified to RM via an RMpsSpec object. See rmPSSpecNew() for information about creating RMpsSpec objects, and see the RM Programming Guide for complete information about all the PostScript output attributes. In generating PostScript output, RM needs to sort primitives in back-to-front order since PS has no notion of a depth buffer. Sorting primitives is an expensive, time-consuming operation. RM provides four methods you can use for generating PostScript files: RM_PS_SORT_FAST - will make a single pass through the primitives and sort them in back-to-front order using the primitive's centroid. This method uses qsort internally, and as such is an O(N logN) operation. If the primitives in the scene don't have much overlap in either x/y space or in depth, then this method should produce satisfactory results. RM_PS_SORT_FULL - this method will perform full geometric sorting of all primitives in the scene using a Binary Space Partition algorithm. The RM_PS_SORT_FULL algorithm is of O (N logN) complexity, and as such will run in comparable run time to RM_PS_SORT_FAST, depending upon scene complexity. In all instances, it has greater memory requirements than RM_PS_SORT_FAST. In some cases, the memory requirements can be substantial. RM_PS_SORT_HYBRID_SCREEN_BSP - this sorting algorithm uses a novel hybrid method of partitioning: it will first subdivide the primitives into screen-space tiles. Primitives that cross tile boundaries are subdivided so that the resulting primitive fragments are entirely contained within a screen space tile. When the number of primitives within that screen tile falls below a certain threshold (a constant in the code) or if the tile area falls below a certain threshold (also a constand in the code), then the primitives contained within that screen are then sorted using RM_PS_SORT_FULL. This approach has the benefit of reducing algorithmic complexity, which in turn results in much faster run times than RM_PS_SORT_FULL as well as reduced memory requirements. Note, however, that RM_PS_SORT_HYBRID_SCREEN_BSP will result in PS rendering artifacts if you specify "fat" line drawing styles or point sizes greater than one pixel. This sorting technique is interesting, but should be viewed as experimental. RM_PS_SORT_HYBRID_DEPTH_BSP - another hybrid approach to reduce run time and memory requirements as compared to a full BSP sort. This algorithm will first subdivide the scene using planes positioned in depth (rather than screen tiles). While this approach is not quite as fast or memory effecient as RM_PS_SORT_HYBRID_SCREEN_BSP, it does not exhibit any of the rendering errors, and its memory and runtime performance are substantially better than RM_PS_SORT_FULL. When you create an RMpsSpec object with rmPSSpecNew(), the default sorting method assigned to the RMpsSpec object is RM_PS_SORT_FULL. Returns RM_CHILL upon success. RM_WHACKED is returned if the toModify parameter is NULL, or if newFormat is not one of RM_PS_REGULAR or RM_PS_EPS. @dend * ---------------------------------------------------- */ RMenum rmPSSetSortMethod(RMpsSpec *p, RMenum sortMethod) { /* inout should be RM_PS_SORT_FULL, RM_PS_SORT_FAST, RM_PS_SORT_HYBRID_SCREEN_BSP, RM_PS_SORT_HYBRID_DEPTH_BSP. */ if (RM_ASSERT(p,"rmPSPageSetSortMethod error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; if ((sortMethod != RM_PS_SORT_FULL) && (sortMethod != RM_PS_SORT_FAST) && (sortMethod != RM_PS_SORT_HYBRID_SCREEN_BSP) && (sortMethod != RM_PS_SORT_HYBRID_DEPTH_BSP)) { rmWarning("rmPSSetSortMethod warning: the sortMethod parameter is not one of RM_PS_SORT_FULL, RM_PS_SORT_FAST, RM_PS_SORT_HYBRID_SCREEN_BSP, RM_PS_SORT_HYBRID_DEPTH_BSP"); return RM_WHACKED; } p->psSortMethod = sortMethod; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPSGetSortMethod @pstart RMenum rmPSGetSortMethod (const RMpsSpec *toQuery) @pend @astart const RMpsSpec *toQuery - an RMpsSpec object that will be queried. @aend @dstart Use this routine to query the sort method specification from an RMpsSpec object. Upon failure, RM_WHACKED is returned. Upon success, the sorting method is returned to the caller (RM_PS_SORT_FULL, RM_PS_SORT_FAST, RM_PS_SORT_HYBRID_SCREEN_BSP, RM_PS_SORT_HYBRID_DEPTH_BSP). @dend * ---------------------------------------------------- */ RMenum rmPSGetSortMethod(const RMpsSpec *p) { if (RM_ASSERT(p,"rmPSPageGetSortMethod error - the input RMpsSpec object is NULL") == RM_WHACKED) return RM_WHACKED; return p->psSortMethod; } /* PRIVATE */ void private_rmPSEpilogue(FILE *f, RMpsSpec *p) { } /* PRIVATE */ void private_rmPSPrintVertex(FILE *f, float xval, float yval) { } /* PRIVATE */ void private_rmPSPrologue(FILE *f, const RMpipe *pipe, RMpsSpec *p) { } /* * test: we're going to restrict the maximum number of columns per * subtile to be, say, 200 pixels. we'll recompute the size of the * subtile to reflect this (previously used whole scanlines). */ #define MAX_COLS 150 /* PRIVATE */ void private_rmPSTileATile(FILE *f, RMimage *s, int xmin, /* viewport dims */ int ymin, int xmax, int ymax) { } /* PRIVATE */ void private_PSBackgroundFunc(const RMnode *r, float vp[4], FILE *f) { } /* PRIVATE */ int comparefunc(const void *aa, const void *bb) { return(1); } /* * ---------------------------------------------------- * @Name rmFramePS @pstart RMenum rmFramePS (RMpipe *drawOn, RMnode *subTree, RMpsSpec *spec) @pend @astart RMpipe *drawOn - the RMpipe environment used to draw the scene. RMnode *subTree - the scene. RMpsSpec *spec - an RMpsSpec containing PS parameter specifications. @aend @dstart rmFramePS is used to render a scene into a PostScript[tm] file. The final rendered frame contents are a function of the (1) relevant parameters specified in the input RMpipe "drawOn" (like display window pixel dimensions, etc.); (2) the scene contents as specified by the scene graph rooted at the RMnode "subTree"; (3) PS-specific parameters and algorithmic controls specified via the RMpsSpec parameter. See rmPSSpecNew() for an introduction to the numerous parameters provided for specifying PS rendering algorithmic control and output options. Upon success, RM_CHILL is returned; upon failure, RM_WHACKED is returned. @dend * ---------------------------------------------------- */ RMenum rmFramePS (RMpipe *drawOn, RMnode *subTree, RMpsSpec *p) { RMenum rstat = RM_WHACKED; return rstat; } /* * ---------------------------------------------------- * @Name rmFramePSHeartbeat @pstart RMenum rmFramePSHeartbeat(RMpipe *drawOn, RMnode *subTree, RMpsSpec *p, int (*heartBeatFunc)(int percentComplete)) @pend @astart RMpipe *drawOn - the RMpipe environment used to draw the scene. RMnode *subTree - the scene. RMpsSpec *spec - an RMpsSpec containing PS parameter specifications. int (*heartBeatFunc)(int percentComplete) - an application supplied callback function. @aend @dstart rmFramePSHeartbeat is identical to function and behavior to rmFramePS with one exception - it will accept as input an application-provided callback (the "heartbeat function"). The callback will be periodically invoked during the PS generation process. When invoked: 1. The application can "interrupt" and prematurely abort the PS generation process by having the heartbeat callback function return a non-zero value to RM. In other words, if the heartbeat application callback returns a value of zero to RM, PS output processing will continue; if the application heartbeat application callback returns a non-zero value, PS output processing will terminate. Control will be returned to the application, and the return status (RM_CHILL or RM_WHACKED) does not indicate whether or not processing was prematurely aborted due to a non-zero return status from the application heartbeat callback. 2. The application will be provided an estimate of "percent complete" via the integer parameter "percentComplete." A value of zero means the algorithm has not yet begun; a value of one hundred (100) means the algorithm has completed. See Note D below for more information about the meaning of this parameter during lengthy sorting operations. Notes: A. During raster postscript output processing, the heartbeat function is invoked at 0, 50, and 100 percent complete. Its return status is ignored, which means that the application cannot interrupt PS output when the RM_PS_RASTER output format is requested. The reasoning is that only lengthy sorting operations should be interruptable via the heartbeat callback. B. During vector postscript output processing, the heartbeat function is invoked at a mixture of "hard coded" percent complete points that correspond to algorithmic milestones, and the return status from the heartbeat application callback is ignored at those points. C. While lengthy sort operations are conducted during vector PS processing, the heartbeat callback is periodically invoked. "Periodically" means approximately once per second. The sorting operation may be interrupted by the application vis-a-via returning a non-zero value from the heartbeat callback function. D. A best-effort algorithm is used to report an accurate percent-complete value via the integer parameter to the heartbeat callback. However, due to the nature of how the PS sorting operation works, the percent complete value is not guaranteed to be monotonic or accurate. We have found it to be both monotonic and reasonably accurate in all cases we have tested. Since the underlying method used to compute "percent complete" takes into account the number of remaining unsorted primitives; this number can increase depending upon the scene complexity and partition planes used during binary space partitioning. @dend * ---------------------------------------------------- */ RMenum rmFramePSHeartbeat(RMpipe *drawOn, RMnode *subTree, RMpsSpec *p, int (*heartBeatFunc)(int percentComplete)) { RMenum rstat=RM_WHACKED; return rstat; } /* EOF */