[comp.lang.postscript] Postscript idiot needed help

bevis@EE.ECN.PURDUE.EDU (Jeff Bevis) (02/04/90)

In article <17807@rpp386.cactus.org>, woody@rpp386.cactus.org (Woodrow Baker) writes:
>In article <9001290016.AA23487@en.ecn.purdue.edu>, bevis@EE.ECN.PURDUE.EDU (Jeff Bevis) writes:
>> I need to create postscript files for use in generating PC board artwork.
>> Unfortunately, I don't know postscript (yet), and the local libraries' books
>> are out... Soo... I hope some kind soul out there might help me.  I need
>
>Don Lancaster, 602-428-4073 has a rather complete schematic drawing package
>already coded up in Postscript.  I think the price is fairly reasonable, and
>can save you an imense amount of work.
>I have never been sucessful getting 'r' to work from within readnews
>Cheers
>Woody

Thanks to you, and everyone else who replied... I have seen Lancaster's stuff
and it seems OK.  However, I decided to go with the brute-force method of
getting things done.  I bought the famed "red and blue" books and started
writing my own layout software for the Amiga computer.  I've already got the
program up and running thanks to the Amiga's graphic-oriented programming
environment...  The postscript stuff looks to be pretty do-able, but I
haven't yet gotten the knack of passing arguments to procedures.  Example:
I want to creat a 'donut' pad on the pcb image;  I call the 'donut'
procedure by placing the x,y of the donut on the stack, followed by the
outer radius (black filled circle) and then the inner radius (white filled
circle, concentric with first)....

	x y rout rin donut

You see, I need to know the (x,y) coord twice;  first for the large black
circle, and second for the inner white one (the hole). 

How should I construct the donut procedure to avoid having to save all the
arguments in variables?  Is that my only hope?  Here's more code:

/black {0 setgray} def
/white {1 setgray} def

/donut
 { /rad1 exch def	% puke-O 
   /rad2 exch def	% save all the arguments
   /yval exch def	% in variables
   /xval exch def	

   black xval yval rad2 0 360 arc fill	% draw black circle
   white xval yval rad1 0 360 arc fill	% then white one on top
 } def

First of all, is this legitimate?  Will it work?  (haven't tried yet...)
How would the guru's out there optimize/improve it? 

Thanks again.  With help, I may actually be learning this. :-)


-------------------------------------------------------------------------------
Jeff Bevis		     Purdue Univeristy School of Electrical Engineering
bevis@en.ecn.purdue.edu	  	   	       Give me Amiga or nothing at all. 

woody@rpp386.cactus.org (Woodrow Baker) (02/05/90)

In article <9002040147.AA16999@en.ecn.purdue.edu>, bevis@EE.ECN.PURDUE.EDU (Jeff Bevis) writes:
> arguments in variables?  Is that my only hope?  Here's more code:
> 
> /black {0 setgray} def
> /white {1 setgray} def
> 
> /donut
>  { /rad1 exch def	% puke-O 
>    /rad2 exch def	% save all the arguments
>    /yval exch def	% in variables
>    /xval exch def	
> 
>    black xval yval rad2 0 360 arc fill	% draw black circle
>    white xval yval rad1 0 360 arc fill	% then white one on top
>  } def
> 
My personal bias on this, is that the above is the way to do it.  I
nearly always like to put things in variables.  There is another philosophy
that says, leave them on the stack, but to me it is more difficult to debug
that way.  You could do it several ways, for example, you could rotat
the stack until x is on the top, dup it, rotate it until y is on the top
dup it, then rotate the top elements of the stack to get
x y rad2 x y rad1  then do
0 360 arc...
I assume that you have defined black and white to set the appropriate
gray shades as needed...
I don't have my red book handy, so am not going to take the time to write
the stack rotations and duplications, but you can work that out.  As
always, it is a trade-off between time, codespace and variable space usage.
It might be more eff. codewise to save into variables, but slower execution
wise.
Cheers
Woody

p.s.  You should also get the GREEN book.  It has a LOT of things to say
about this and other topics.  It really is a philosophy book about
programming.  Glenn did an excellent job on it.  However, I don't
recommend following his brace style, as it is a bit hard to read. Line
up your opening and closing braces verticaly, like you would in 'C' (or should)
and like the BEGIN/END in Pascal.  It is much easier to follow

stuff{
asdfasdfasdff
asdfasdfsadf
} def
^^^^^^^^^^^^^^^^^^NONO.

stuff
	{
	asdfasdfasfd
	asdfasdfffdadsf
	} def
^^^^^^^^^^^^^^^^^^^Much easier to see.  You are started on the right format.

pihlaja@cs.Helsinki.FI (Markku Pihlaja) (02/08/90)

Jeff (bevis@EE.ECN.PURDUE.EDU) in article 2630:
  > How should I construct the donut procedure to avoid having to save all the
  > arguments in variables?  Is that my only hope?


Woody (woody@rpp386.cactus.org) in article 2643:
  >                                               There is another philosophy
  >that says, leave them on the stack, but to me it is more difficult to debug
  >that way.
    ...
  >I don't have my red book handy, so am not going to take the time to write
  >the stack rotations and duplications, but you can work that out. 

I've got mine handy, and since you definitely get more time-efficient code by
not using variables, I'll work it out here.

        /donut {        % Stack when calling:  x y rin rout
                0 setgray
                3 index              % x y rin rout x
                3 index              % x y rin rout x y
                3 -1 roll            % x y rin x y rout
                0 360 arc fill       % x y rin
                1 setgray            % x y rin
                0 360 arc fill       % -
                0 setgray
        } bind def

        % Sorry, Woody, but I prefer the Glenn-style indentations

There are two points to look at here.

A lot of time is `wasted' when you have to load a value from a dictionary
(that's what happens when the interpreter encounters a token like `rad1').
Having 3 stack operations instead of 12 dictionary operations is no doubt
more effective.

Note also the `bind def' at the end. From the Red Book: `For each element
of proc that is an executable name, bind looks up the name in the context
of the current dictionary stack (as if by load). If the name is found and
its value is an operator object, bind replaces the name by the operator
in proc.'  I.e. instead of executing `setgray' or `index' or `roll', which
means first loading the actual operator object from systemdict or wherever
it's defined and only after that executing it,  bind replaces the names
by the operators already when defining the procedure. And if I've got it
right, in the example this means as much as 10 dictionary operations less
than without bind. Correct me if I'm wrong.

I agree with Woody, using variables usually does make the code more
readable. But if Jeff happens to need dillions of donuts drawn on one
page, then it is definitely better to use only stack manipulations instead
of variables.

                                                      Markku

======================================            =======================
Markku Pihlaja                                     Please tell me if my
University of Helsinki, Finland                   English is not perfect.
pihlaja@cs.helsinki.fi                              I'm a perfectionist.
======================================            =======================

batcheldern@hannah.enet.dec.com (Ned Batchelder) (02/10/90)

I recommend reading the Green Book, not to be indoctrinated into one
particular method of writing procedures, but to get exposed to other
ideas on the matter.

Here's another way to do it:

	% rin rout x y `donut' --

	/donut {
		gsave
		translate
		0 setgray
		0 0 3 -1 roll 0 360 arc fill
		1 setgray
		0 0 3 -1 roll 0 360 arc fill
		grestore
	} bind def

Here, we use the x and y to translate the origin so that the pad is
centered at 0,0. This reduces the amount of stack manipulation. I can't
say whether it is more efficient this way, but I wouldn't be surprised.
Notice that I also rearranged the arguments to reduce stack manipulations.

If you look on page 75 of the Red Book (at least my edition), there is a
figure explaining the non-zero winding rule that gives an idea:

	% rin rout x y `donut' --

	/donut {
		gsave
		translate
		0 0 3 -1 roll 0 360 arc
		0 0 3 -1 roll 0 360 arcn
		fill
		grestore
	} bind def

This avoids all of the setgray's, and makes a real donut, in that things
will show through the "hole". It also does only one "fill", which will
will help on the speed.

A point about "local" variables: They're not local. If your program gets
more complex, with procedures invoking other procedures, and each saves
arguments in variables, you may find yourself having to worry about name
clashes. All of the variables get stored in the same dictionary (unless
you use a separate dict for each proc, another headache). This is also a
huge hassle if you want to write recursive procedures, as you then need
a separate dictionary for each invocation of the same procedure.

And finally, if you use variables in your procedure, and bind the
procedure, you can get into a lot of trouble when being included into
other PostScript. Another topic for a longer posting.

I recommend that you learn how to write procedures so that they need a
minimum of stack manipulation, and then avoid the variables. Your code
will be shorter and faster.

Ned Batchelder, Digital Equipment Corp., BatchelderN@Hannah.enet.DEC.com
Disclaimer: all of the code in this posting was typed off the top of my head,
but the ideas are valid.

woody@rpp386.cactus.org (Woodrow Baker) (02/11/90)

In article <8260@shlump.nac.dec.com>, batcheldern@hannah.enet.dec.com (Ned Batchelder) writes:
> I recommend reading the Green Book, not to be indoctrinated into one
> particular method of writing procedures, but to get exposed to other
> ideas on the matter.


code deleted> 
> Notice that I also rearranged the arguments to reduce stack manipulations.
>
This is a very good general technique for reducing time and complexity.

> If you look on page 75 of the Red Book (at least my edition), there is a
> figure explaining the non-zero winding rule that gives an idea:
> 
> 	% rin rout x y `donut' --
> 
> 	/donut 
	{
> 		gsave
> 		translate
> 		0 0 3 -1 roll 0 360 arc
> 		0 0 3 -1 roll 0 360 arcn
> 		fill
> 		grestore
> 	} bind def
> 
> This avoids all of the setgray's, and makes a real donut, in that things
> will show through the "hole". It also does only one "fill", which will
> will help on the speed.

 
> A point about "local" variables: They're not local. If your program gets
> more complex, with procedures invoking other procedures, and each saves
> arguments in variables, you may find yourself having to worry about name
> clashes. All of the variables get stored in the same dictionary (unless
> you use a separate dict for each proc, another headache). This is also a
> huge hassle if you want to write recursive procedures, as you then need
> a separate dictionary for each invocation of the same procedure.


Boy, isn't that the truth!  One of the reasons that I avoid recursion like
the plague.  Recursion also total obscures what is going on.  Even in 'C'.
You might, if you are going to try recursion, try creating dynamic dictionaries
by creating a new dictionary name and copying the old one into it before
executing the recurse.  I'm not even sure that would work, but you can
do it, as far as I know.  I've never tried it.  I don't think I want to.
I would be inclined to simulate my own stack.....
t
E
> And finally, if you use variables in your procedure, and bind the
> procedure, you can get into a lot of trouble when being included into
> other PostScript. Another topic for a longer posting.
> 
> I recommend that you learn how to write procedures so that they need a
> minimum of stack manipulation, and then avoid the variables. Your code
> will be shorter and faster.

Verily, Verily, hear ye!
It might be less maintainable, but definitly shorter and faster.

m
Cheers> 
Woody

rodgers@csc.wcc.govt.nz (02/12/90)

I'm surprised no-one suggested a fat line doughnut.  How about this
(totally untried) procedure:

% stack when calling: x y rin rout
/donut { %def
  gsave
    1 index	       % x y rin rout rin
    sub                % x y rin thickness
    dup setlinewidth
    2 div add          % x y rcentre
    0 360              % x y rcentre 0 360
    3 -1 roll          % x y 0 360 rcentre
    arc stroke
  grestore
} bind def

Unfortunately I don't have my red book handy so I'm not sure if this
is even close to right but I'm sure someone will delight in telling me
if it's not.  Has anyone got enough time and enthusiasm to muck
about with usertime to see how all these donuts compare?  Woody????  I
would really be interested to know how fills compare with strokes in a
case like this.  My experience with fills has been that they are
really slow.
--
Mark Rodgers                                      Computer Services Section
rodgers@csc.wcc.govt.nz                             Wellington City Council
Telephone (04) 733-130               P.O. Box 2199, Wellington, New Zealand