Reputation: 1205
I am doing some forms in Postscript and there are times I need to save the current point into a variable, so later I can conveniently put the x y values on the stack for a moveto. It is not that straightforward. I am looking for a procedure that stores both the x and y into a single variable.
Suppose you are at 72 720. I want a variable, say CP, to be able to store both. Of course, if you wanted to store
/cp {72 720} def
and your would create a procedure that when invoked would leave the stack containing 72 720. However, what if you want to store an arbitrary location during execution? The following code would not work.
/cp {currentpoint} def
72 720 moveto
cp
100 100 moveto
cp
That code just invokes currentpoint each time, leaving 72 720 100 100 on the stack.
How do you create a procedure that captures the current point into a variable that will put two values on the stack determined at run time?
Upvotes: 1
Views: 705
Reputation: 19504
While I like (and upvoted) both of the other answers here, I've got a slight variation of the same idea which illustrates some fun little corners, IMO.
You can define the same array twice, making one reference literal and the other one executable. It's marginally more elegant to reuse the same array rather than discarding and allocating new ones.
/cpa 2 array def % literal array for storing
/cpx cpa cvx def % executable version of same array for spilling on the stack
currentpoint cpa astore pop % save currentpoint
cpx moveto % return to currentpoint
Now, this won't work if you've changed the current transformation matrix (CTM) using any of scaling, rotation, translation. For a more general solution, you need to transform
to device coordinates (which are stable with respect to user space), and then itransform
to use. This leads to the following.
/cpa 2 array def % literal array for storing
/cpx cpa cvx def % executable version of same array
/cps { currentpoint transform cpa astore pop } def % save currentpoint
/cpr { cpx itransform moveto } def % return to currentpoint
This will return you to the same device location even if the CTM has changed.
Upvotes: 1
Reputation: 1047
You could do something like this. If you plan to be defining your own functions, you should start by defining your own dictionary and begin it to put it on the top of the dictionary stack
Define a 2 element array cpa
inside the dictionary to hold the x,y
define /cp
to store the currentpoint into the array.
define /CP
to restore the currentpoint from the array
/mydict 100 dict def
mydict begin
/cpa {2 array} def
/cp {/cpa currentpoint cpa astore def} def
/CP { cpa aload pop moveto } def
%test out the functions
123 456 moveto %move to a point
cp %save that point
986 654 moveto %move to a different point
CP %restore the saved point
(Currentpoint ) == currentpoint exch == == %print out the current
You might also want to read up on gsave/grestore and save/restore. These maybe what you are really after.
Upvotes: 1
Reputation: 1205
The following will create a procedure at run time that will put two values on the stack when called:
72 720 moveto
/cp [ currentpoint ] cvx def
cp
100 100 moveto
cp
That will leave 72 720 72 720 on the stack. What happens is that the definition of cp first fills an array with two values. It is made executable, so that when cp is called the two stored values are pushed onto the stack every time is is called, regardless of changes to the current point location.
The utility of this, of course, is not to just get to an explicit point, but rather to capture a point during execution. If the code fragment was
72 720 moveto
(Begin with, end with) show
/cp [ currentpoint ] cvx def
% intervening code ...
cp moveto
(, whatever!) show
then the utility of this is more apparent.
Note that for this to work, a current point needs to exist. In the question section, the procedure {currentpoint} can be made, because execution is deferred. However, if currentpoint is called without a current point it will result in a postscript error. A short postscript program follows to explore this.
%!
/fontsize 14 def
/lineheight 16 def
/Times-Roman findfont fontsize scalefont setfont
/newline
{72 currentpoint exch pop lineheight sub
dup 72 lt {showpage pop 720} if
moveto
} def
/cp2 { currentpoint } def
72 720 moveto
/cp1 [ currentpoint ] cvx def
cp1
cp2
(Test line) show
cp1
cp2
144 500 moveto
cp1
cp2
/cp1 [ currentpoint ] cvx def
cp1
cp2
(Test line) stringwidth
newline
(-top of stack-) show
newline
count {30 string cvs show newline} repeat
(-bottom of stack-) show newline
showpage
I searched a number of references on the internet to figure this out, but didn't see anything. I had resorted to storing the x and y values in separate variables, but the inelegance of that approach got me to come up with this approach. If anyone knows this to be a topic addressed under some keyword please let us know.
Upvotes: 2