[comp.lang.postscript] Drawing lots of triangles

eric@batcomputer.tn.cornell.edu (Eric Fielding) (01/18/90)

I was glad to see all of the helpful suggestions about how to draw boxes fast,
but I have a different problem that seems to me much harder to optimize under
PS.  I have a picture that consists almost entirely of 180,000 filled triangles.
(This is the output from a 3D rendering program.)  Here is a sample of one
of the files:

%!
%%BoundingBox: 0 0 612 792
%%EndComments
initgraphics
8.5 72 mul 0 translate 90 rotate 72 1000 div dup scale
2.0 setmiterlimit 60 45 {add .5 mul} setscreen
/NP {newpath} def /SD {setdash} def /SG {99 div setgray} def /MT {moveto} def
/LT {lineto} def /CP {closepath} def /GS {gsave} def /FL {fill} def
/GR {grestore} def /SK {stroke} def /LW {setlinewidth} def
/PLP {1024 1 true [1024 0 0 1 0 0] {<8080808080808080>} imagemask} def
/WLP {1024 1 true [1024 0 0 1 0 0] {<8888888888888888>} imagemask} def
erasepage
[... other graphics stuff deleted, triangles follow]
 NP 8761 6576 MT
8781 6584 LT
8782 6574 LT
8761 6576 LT
CP GS 99 SG FL GR
NP 8761 6576 MT
8760 6587 LT
8781 6584 LT
8761 6576 LT
CP GS 99 SG FL GR
NP 8740 6581 MT
8760 6587 LT
8761 6576 LT
8740 6581 LT
CP GS 99 SG FL GR
... about 180,000 times

Is there anything obviously inefficient about the way this is being printed? I
see from the 'boxes' discussion that I could redefine the NP, MT, LT, etc. to
be one letter, but that is a small savings.  Any other suggestions?  (I don't
have the source the graphics package, but I could get it, or use post-processing
under either Ultrix or VMS.)

				++Eric Fielding
eric@geology.tn.cornell.edu
eric@crnlthry.bitnet

batcheldern@hannah.enet.dec.com (01/18/90)

> I was glad to see all of the helpful suggestions about how to draw
boxes fast,
> but I have a different problem that seems to me much harder to optimize under
> PS.  I have a picture that consists almost entirely of 180,000 filled
triangles.
> (This is the output from a 3D rendering program.)  Here is a sample of one
> of the files:
> 
> %!
> %%BoundingBox: 0 0 612 792
> %%EndComments
> initgraphics
> 8.5 72 mul 0 translate 90 rotate 72 1000 div dup scale
> 2.0 setmiterlimit 60 45 {add .5 mul} setscreen
> /NP {newpath} def /SD {setdash} def /SG {99 div setgray} def /MT {moveto} def
> /LT {lineto} def /CP {closepath} def /GS {gsave} def /FL {fill} def
> /GR {grestore} def /SK {stroke} def /LW {setlinewidth} def
> erasepage
> [... other graphics stuff deleted, triangles follow]
>  NP 8761 6576 MT
> 8781 6584 LT
> 8782 6574 LT
> 8761 6576 LT
> CP GS 99 SG FL GR
> ... about 180,000 times
> 
> Is there anything obviously inefficient about the way this is being
printed? I
> see from the 'boxes' discussion that I could redefine the NP, MT, LT, etc. to
> be one letter, but that is a small savings.  Any other suggestions?

Yes, define your shortened commands as single letters, but more
importantly, change the form of the definitions. Your definition involve
executing a procedure, and looking up the name in the procedure, and
then executing the operator. Better to use definitions of the form:

	/l /lineto load def

so that l is defined to be the operator, rather than a procedure which
contains a name which has the operator as a value. Also, you only need
two lineto's to makea triangle, since the closepath will add the last
line segment for you.

Also, rather than do a gsave/grestore and then a newpath, simply fill
the triangle. This would require getting the source to the package, but
you're going to have to do that to get any real savings anyway. Better
yet, do your own PostScript conversion, and have all the flexibility you want.

I don't know what range of triangle shapes you have, but if there are
less than 257 of them, then you can create a font with all of your
different shapes, and use show to put them on the page. This will
compact your file tremendously, and will put the power of the font cache
to work for you.

As an aside, for those of us out here who are curious, could you give us
a short description of what this thing you're drawing is? (I'd like to
know about the boxes one too!)

> 				++Eric Fielding
> eric@geology.tn.cornell.edu
> eric@crnlthry.bitnet
> 

Ned Batchelder, Digital Equipment Corp., BatchelderN@Hannah.enet.DEC.com

In article <9561@batcomputer.tn.cornell.edu>,
eric@batcomputer.tn.cornell.edu (Eric Fielding) writes:

> I was glad to see all of the helpful suggestions about how to draw
boxes fast,
> but I have a different problem that seems to me much harder to optimize under
> PS.  I have a picture that consists almost entirely of 180,000 filled
triangles.
> (This is the output from a 3D rendering program.)  Here is a sample of one
> of the files:
> 
> %!
> %%BoundingBox: 0 0 612 792
> %%EndComments
> initgraphics
> 8.5 72 mul 0 translate 90 rotate 72 1000 div dup scale
> 2.0 setmiterlimit 60 45 {add .5 mul} setscreen
> /NP {newpath} def /SD {setdash} def /SG {99 div setgray} def /MT {moveto} def
> /LT {lineto} def /CP {closepath} def /GS {gsave} def /FL {fill} def
> /GR {grestore} def /SK {stroke} def /LW {setlinewidth} def
> /PLP {1024 1 true [1024 0 0 1 0 0] {<8080808080808080>} imagemask} def
> /WLP {1024 1 true [1024 0 0 1 0 0] {<8888888888888888>} imagemask} def
> erasepage
> [... other graphics stuff deleted, triangles follow]
>  NP 8761 6576 MT
> 8781 6584 LT
> 8782 6574 LT
> 8761 6576 LT
> CP GS 99 SG FL GR
> NP 8761 6576 MT
> 8760 6587 LT
> 8781 6584 LT
> 8761 6576 LT
> CP GS 99 SG FL GR
> NP 8740 6581 MT
> 8760 6587 LT
> 8761 6576 LT
> 8740 6581 LT
> CP GS 99 SG FL GR
> ... about 180,000 times
> 
> Is there anything obviously inefficient about the way this is being
printed? I
> see from the 'boxes' discussion that I could redefine the NP, MT, LT, etc. to
> be one letter, but that is a small savings.  Any other suggestions?  (I don't
> have the source the graphics package, but I could get it, or use
post-processing
> under either Ultrix or VMS.)
> 

Yes, define your shortened commands as single letters, but more
importantly, change the form of the definitions. Your definition involve
executing a procedure, and looking up the name in the procedure, and
then executing the operator. Better to use definitions of the form:

	/l /lineto load def

so that l is defined to be the operator, rather than a procedure which
contains a name which has the operator as a value. Also, you only need
two lineto's to makea triangle, since the closepath will add the last
line segment for you.

Also, rather than do a gsave/grestore and then a newpath, simply fill
the triangle. This would require getting the source to the package, but
you're going to have to do that to get any real savings anyway. Better
yet, do your own PostScript conversion, and have all the flexibility you want.

I don't know what range of triangle shapes you have, but if there are
less than 257 of them, then you can create a font with all of your
different shapes, and use show to put them on the page. This will
compact your file tremendously, and will put the power of the font cache
to work for you.

As an aside, for those of us out here who are curious, could you give us
a short description of what this thing you're drawing is? (I'd like to
know about the boxes one too!)

Ned Batchelder, Digital Equipment Corp., BatchelderN@Hannah.enet.DEC.com

eric@batcomputer.tn.cornell.edu (Eric Fielding) (01/19/90)

In a recent article BatchelderN@hannah.enet.dec.com wrote in response to my
query: 

>> I have a picture that consists almost entirely of 180,000 filled
>>triangles.  (This is the output from a 3D rendering program.)

Thanks for your suggestion to use 'load' instead of 'def'.  I am going see if
I can try that.

>As an aside, for those of us out here who are curious, could you give us
>a short description of what this thing you're drawing is? (I'd like to
>know about the boxes one too!)
>Ned Batchelder, Digital Equipment Corp., BatchelderN@Hannah.enet.DEC.com

The triangles are rendered topography with a light on the surface.  The topo
data is actually a grid of 1200x1200 elevations, but I subsample it to 
300x300 (x 2 triangles per grid cell) before rendering it to make feasible.  If
I was a graphics whiz, I can see that it would be much more efficient to use
a ray-tracing approach, since at the (dithering) resolution of the LaserWriter,
there are fewer pixels than triangles.  A bitmap of the output would be much
faster to print, but I am using a graphics package that does not have that
option.

				++Eric Fielding

woody@rpp386.cactus.org (Woodrow Baker) (01/19/90)

> %!
> %%BoundingBox: 0 0 612 792
> %%EndComments
> initgraphics
> 8.5 72 mul 0 translate 90 rotate 72 1000 div dup scale
> 2.0 setmiterlimit 60 45 {add .5 mul} setscreen
> /NP {newpath} def /SD {setdash} def /SG {99 div setgray} def /MT {moveto} def
> /LT {lineto} def /CP {closepath} def /GS {gsave} def /FL {fill} def
> /GR {grestore} def /SK {stroke} def /LW {setlinewidth} def
> /PLP {1024 1 true [1024 0 0 1 0 0] {<8080808080808080>} imagemask} def
> /WLP {1024 1 true [1024 0 0 1 0 0] {<8888888888888888>} imagemask} def
> erasepage
> [... other graphics stuff deleted, triangles follow]
>  NP 8761 6576 MT
> 8781 6584 LT
> 8782 6574 LT
> 8761 6576 LT
Well, this last line probably could be replaced with a closepath operator
certainly, the 2 character abrevieations should be reduced to single character
abreviations, and it would be better to create a routine called triangle
that would take the coordinate pairs, and do a triangle

so that
8782 6574 8781 6584 8761 6576 triangle

/triangle
	{
	moveto lineto lineto closepath
	}
of course you would use t  or   tr instead of triangle.
	or send it 4 pairs and use
	moveto lineto lineto lineto

	or mt lt lt lt....

or
/triangle
	{
	NP MT LT LT LT CP GS 99 SG FL GR
	}
 
> CP GS 99 SG FL GR
> NP 8761 6576 MT
> 8760 6587 LT
> 8781 6584 LT
> 8761 6576 LT
> CP GS 99 SG FL GR
> NP 8740 6581 MT
> 8760 6587 LT
> 8761 6576 LT
> 8740 6581 LT
> CP GS 99 SG FL GR
> ... about 180,000 times
> 
> Is there anything obviously inefficient about the way this is being printed? I
> see from the 'boxes' discussion that I could redefine the NP, MT, LT, etc. to
> be one letter, but that is a small savings.  Any other suggestions?  (I don't
> have the source the graphics package, but I could get it, or use post-processing
> under either Ultrix or VMS.)
> 
> 				++Eric Fielding
> eric@geology.tn.cornell.edu
> eric@crnlthry.bitnet

Cheers
Woody
There are probably some other things one could do as well.  It seems that
there are lots of coordinate repititions that might be telescoped to gether.
It might also be possible to write the range of coordinates out to a large
array, and then index the array inorder to get the cordinates. Each
one of these, however when taken together, would probably exceed permissible
array space.
 

capslock@wet.UUCP (Allen Crider) (01/19/90)

>%!
>%%BoundingBox: 0 0 612 792
>%%EndComments
%initgraphics--no need for initgraphics
>8.5 72 mul 0 translate 90 rotate 72 1000 div dup scale
>2.0 setmiterlimit
    	            %60 45 {add .5 mul} setscreen--no need to do a setscreen
					--who wants a 60 line screen if printing
					--to a Lino?
%/NP {newpath} def--how bout /NP{}def?
> /SD {setdash} def 
>/SG {99 div setgray} def /MT {moveto} def--use bind def
>/LT {lineto} def /CP {closepath} def /GS {gsave} def /FL {fill} def
>/GR {grestore} def /SK {stroke} def /LW {setlinewidth} def
>/PLP {1024 1 true [1024 0 0 1 0 0] {<8080808080808080>} imagemask} def
>/WLP {1024 1 true [1024 0 0 1 0 0] {<8888888888888888>} imagemask} def
%erasepage--what's on the page to erase?
>[... other graphics stuff deleted, triangles follow]
> NP 8761 6576 MT %the newpath before every triangle is a waste
>8781 6584 LT
>8782 6574 LT
>8761 6576 LT
>CP GS 99 SG FL GR %three lines make a triangle:why the closepath?
			%These triangles aren't being stroked, there isn't
			%any need to do a gsave, grestore
			%how about:
			/FL {99 div setgray fill} bind def
			/SFL{gsave 99 div setgray fill grestore stroke} bind def
we would get:
8761 6576 MT
8781 6584 LT
8782 6574 LT
8761 6576 LT
99 FL

I'm sure others could improve on this, I'm just a typographer.

thomson@hub.toronto.edu (Brian Thomson) (01/20/90)

In article <9561@batcomputer.tn.cornell.edu> eric@geology.tn.cornell.edu writes:
>/NP {newpath} def /SD {setdash} def /SG {99 div setgray} def /MT {moveto} def
>/LT {lineto} def /CP {closepath} def /GS {gsave} def /FL {fill} def
>/GR {grestore} def /SK {stroke} def /LW {setlinewidth} def
>[... other graphics stuff deleted, triangles follow]
> NP 8761 6576 MT
>8781 6584 LT
>8782 6574 LT
>8761 6576 LT
>CP GS 99 SG FL GR
>... about 180,000 times

I don't think anyone has yet suggested another way to lower the
transmission costs: the triangles are all small, so use relative motion 
when drawing them.  Combined with other suggestions, we get

 NP 8761 6576 MT
 8781 6584 LT
 8782 6574 LT
 8761 6576 LT
 CP GS 99 SG FL GR

turning into

/SG { 99 div setgray } bind def
/T { newpath moveto rlineto rlineto closepath fill } bind def
...
99 SG
1 -10 20 8 8761 6576 T
etc.
-- 
		    Brian Thomson,	    CSRI Univ. of Toronto
		    utcsri!uthub!thomson, thomson@hub.toronto.edu

rsilverman@eagle.wesleyan.edu (01/21/90)

In article <9561@batcomputer.tn.cornell.edu>, eric@batcomputer.tn.cornell.edu (Eric Fielding) writes:
asks about improving the PostScript output of a program.

Eric,

Based on the sample you gave, I think there are a number of things you could
do:

1)  Remove the initgraphics and erasepage from the top of the code.  They will 
prevent your description from being easily operated on as a unit for inclusion 
in another page.

2)  Instead of sequences like "/SK {stroke} def", use "/SK /stroke load def". 
This avoids the overhead of both the procedure call and name lookup in the
former.

3)  A typical sequence painting a triangle is

>  NP 8761 6576 MT
> 8781 6584 LT
> 8782 6574 LT
> 8761 6576 LT
> CP GS 99 SG FL GR
> NP 8761 6576 MT   %...beginning of next triangle

This is inefficient in a number of ways.  The third lineto instruction is
unnecessary; closepath does it for you.  The filling operation is surrounded by
a gsave/grestore pair, for no apparent reason, as the path is destroyed by
newpath immediately afterward.  Get rid of the GS/GR, and then you can remove
the NP as well; the fill operator clears the current path for you.  Thus:

8761 6576 MT
8781 6584 LT
8782 6574 LT
CP 99 SG FL

is all you need for each triangle.  Note that if the triangles were being
stroked as well as filled, then the gsave/grestore would be appropriate.

Another thing you might consider: if the picture contains many instances of
exactly the same triangle in the same size and orientation, in black or white,
the you could create a font containing the frequently used triangles as
characters, and take advantage of the font cache for a tremendous speedup. 
Hope this helps,

Richard Silverman