Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Zener cards

21 views
Skip to first unread message

Mark T. B. Carroll

unread,
Jan 9, 2011, 8:59:26 PM1/9/11
to
I was avoiding real work today so I wrote some code to produce Zener
cards. I added a new triangle one because it's easier to fit six cards
nicely than five. (-: Anyhow, I figure I may as well share the code in
case anybody's interested. With ps2pdf it produces a PDF under 3kB which
is quite pleasing. I wonder if I write in a conventional or weird way.

/in { 72 mul } def

/pagewidth 8.5 in def
/pageheight 11 in def

<< /PageSize [ pagewidth pageheight ] >> setpagedevice

/xmargin 0.5 in def
/ymargin 0.5 in def
/imargin 0.5 in def

/across 2 def
/down 3 def

/cardwidth pagewidth xmargin 2 mul sub imargin across 1 sub mul sub across div def
/cardheight pageheight ymargin 2 mul sub imargin down 1 sub mul sub down div def

cardheight cardwidth gt
{
/symbolsize cardwidth 1.5 div def
/xoffset cardwidth symbolsize sub 2 div def
/yoffset cardheight cardwidth sub 2 div xoffset add def
}
{
/symbolsize cardheight 1.5 div def
/yoffset cardheight symbolsize sub 2 div def
/xoffset cardwidth cardheight sub 2 div yoffset add def
}
ifelse

/halfsymbol symbolsize 2 div def

/circle
{
4 dict begin

/r exch def
/y exch def
/x exch def

/o 2 sqrt 1 sub r mul 4 3 div mul def

x r sub y moveto
x r sub y o add x o sub y r add x y r add curveto
x o add y r add x r add y o add x r add y curveto
x r add y o sub x o add y r sub x y r sub curveto
x o sub y r sub x r sub y o sub x r sub y curveto
closepath

end
} def

/circlecard {
halfsymbol dup dup circle
stroke
} def

/crosscard {
halfsymbol 0 moveto
0 symbolsize rlineto
0 halfsymbol moveto
symbolsize 0 rlineto
stroke
} def

/squarecard {
1 dict begin

/scale 0.1 def

scale symbolsize mul dup
1 scale 2 mul sub symbolsize mul dup
rectstroke

end
} def

/starcard {
3 dict begin

/points 5 def
/shortsize halfsymbol 72 cos mul 36 cos div def
/halfangle 180 5 div def

halfsymbol dup translate
0 halfsymbol moveto
1 1 points
{
pop

halfangle rotate
0 shortsize lineto
halfangle rotate
0 halfsymbol lineto
}
for
closepath
stroke

end
} def

/trianglecard {
halfsymbol dup translate
0 1 30 sin sub halfsymbol mul neg 2 div translate
0 halfsymbol moveto
120 rotate
0 halfsymbol lineto
120 rotate
0 halfsymbol lineto
closepath
stroke
} def

/wavycard {
8 dict begin

/lines 4 def
/wiggles 5 def
/wiggleheight symbolsize wiggles 2 mul div def
/linegap symbolsize wiggleheight sub lines 1 sub div def
/wigglewidth symbolsize wiggles div def
/y 0 def

1 1 lines
{
pop

/x 0 def

x y moveto

1 1 wiggles
{
/midx x wigglewidth 2 div add def

1 and 0 ne
{
midx y
midx y wiggleheight add
x wigglewidth add y wiggleheight add curveto
}
{
midx y wiggleheight add
midx y
x wigglewidth add y curveto
}
ifelse

/x x wigglewidth add def
}
for

stroke

/y y linegap add def
}
for

end
} def

/cardnumber 0 def

/cards [ /circlecard /crosscard /squarecard /starcard /trianglecard /wavycard ] def

1 setlinecap
1 setlinejoin

1 1 down
{
/yn exch def

/cornery ymargin yn 1 sub cardheight imargin add mul add def

1 1 across
{
/xn exch def
/cornerx xmargin xn 1 sub cardwidth imargin add mul add def

gsave

cornerx cornery translate
0 0 cardwidth cardheight rectstroke

currentlinewidth
0.1 in setlinewidth
xoffset yoffset translate

cards cardnumber get cvx exec

setlinewidth

grestore

/cardnumber cardnumber 1 add def
}
for
}
for

showpage

luser- -droog

unread,
Jan 10, 2011, 1:26:12 AM1/10/11
to
On Jan 9, 7:59 pm, "Mark T. B. Carroll" <m...@bcs.org> wrote:
> I was avoiding real work today so I wrote some code to produce Zener
> cards. I added a new triangle one because it's easier to fit six cards
> nicely than five. (-: Anyhow, I figure I may as well share the code in
> case anybody's interested. With ps2pdf it produces a PDF under 3kB which
> is quite pleasing. I wonder if I write in a conventional or weird way.

Looks good!

If you put a %! at the beginning, it's slightly easier to see where
the
code begins.

> /in { 72 mul } def
>
> /pagewidth 8.5 in def
> /pageheight 11 in def
>
> << /PageSize [ pagewidth pageheight ] >> setpagedevice
>
> /xmargin 0.5 in def
> /ymargin 0.5 in def
> /imargin 0.5 in def

I usually use shorter names, just to save typing.
It used to be the case that short names are better
so the program can execute faster; but if you're
converting to pdf, that's largely irrelevant.

> /across 2 def
> /down 3 def
>
> /cardwidth pagewidth xmargin 2 mul sub imargin across 1 sub mul sub across div def
> /cardheight pageheight ymargin 2 mul sub imargin down 1 sub mul sub down div def
>
> cardheight cardwidth gt
> {
>     /symbolsize cardwidth 1.5 div def
>     /xoffset cardwidth symbolsize sub 2 div def
>     /yoffset cardheight cardwidth sub 2 div xoffset add def}
>
> {
>     /symbolsize cardheight 1.5 div def
>     /yoffset cardheight symbolsize sub 2 div def
>     /xoffset cardwidth cardheight sub 2 div yoffset add def}
>
> ifelse

There's a lot of duplication in these two procs.
It might be easier to use ifelse to define two new
names.

Or maybe not. I just tried to rewrite this part like this:

cardheight cardwidth 2 copy gt { exch } if
/smaller exch def
/bigger exch def
/symbolsize smaller 1.5 div def
/xoffset smaller symbolsize sub 2 div def
/yoffset bigger smaller sub 2 div xoffset add def

And then I noticed that the offsets are flopped.
So, your way is probably better here, but bear in
mind that stack manipulation can sometimes simplify
things.

>
> /halfsymbol symbolsize 2 div def
>
> /circle
> {
>     4 dict begin
>
>     /r exch def
>     /y exch def
>     /x exch def
>
>     /o 2 sqrt 1 sub r mul 4 3 div mul def
>
>                                     x r sub y       moveto
>     x r sub y o add x o sub y r add x       y r add curveto
>     x o add y r add x r add y o add x r add y       curveto
>     x r add y o sub x o add y r sub x       y r sub curveto
>     x o sub y r sub x r sub y o sub x r sub y       curveto
>     closepath
>
>   end
>
> } def

I'd probably use arc for a circle, but it's fun to see
how you calculate the control points.

I'm probably too used to C programming.
It looks funny to me to have a for loop start on 1.
I'd probably do

0 1 down across mul 1 sub

and then separate the rows with

loopindex across mod 0 eq { move_to_next_row } if

That way your loopindex matches the array index.

> {
>     /yn exch def
>
>     /cornery ymargin yn 1 sub cardheight imargin add mul add def
>
>     1 1 across
>     {
>         /xn exch def
>         /cornerx xmargin xn 1 sub cardwidth imargin add mul add def
>
>         gsave
>
>         cornerx cornery translate
>         0 0 cardwidth cardheight rectstroke
>
>         currentlinewidth
>         0.1 in setlinewidth
>         xoffset yoffset translate
>
>         cards cardnumber get cvx exec
>
>         setlinewidth
>
>         grestore
>
>         /cardnumber cardnumber 1 add def

Again, from too much C, I like to make a little
helper proc:
/++ { dup load 1 add store } def

If you use more dictionaries, store is often
better for changing a value where it already
exists.

>     }
>     for}

It looks like most of your braces line up
with the opening brace or the beginning of
text on the line with the opening brace.
But a scattered few sit on the end of the line.
Not a big deal, but it looks a little weird.

>
> for
>
> showpage

Mark T. B. Carroll

unread,
Jan 11, 2011, 8:55:29 PM1/11/11
to
luser- -droog <mij...@yahoo.com> writes:
(snip)

> And then I noticed that the offsets are flopped.
> So, your way is probably better here, but bear in
> mind that stack manipulation can sometimes simplify
> things.

That's true. Though with 'roll' I always have to test it out, I usually
get it wrong first try! exch is okay though. (-:

>> /circle
(snip)


> I'd probably use arc for a circle, but it's fun to see
> how you calculate the control points.

Huh, good point, I wonder why I didn't use arc too. (I pulled that
procedure from an old project.) Given that a pathforall after an arc
gives us curveto anyway, I wonder if what I did is pretty much what
interpreters do internally with arc.


>> 1 1 down
>
> I'm probably too used to C programming.
> It looks funny to me to have a for loop start on 1.
> I'd probably do
>
> 0 1 down across mul 1 sub
>
> and then separate the rows with
>
> loopindex across mod 0 eq { move_to_next_row } if
>
> That way your loopindex matches the array index.

Oh, yes, that's a good idea.

> Again, from too much C, I like to make a little
> helper proc:
> /++ { dup load 1 add store } def

Ohh, that's a nice trick, I'd not seen it before.

>>     }
>>     for}
>
> It looks like most of your braces line up
> with the opening brace or the beginning of
> text on the line with the opening brace.
> But a scattered few sit on the end of the line.
> Not a big deal, but it looks a little weird.

Hmmm, it doesn't look like that in my original here or in the Google
archive; perhaps your newsreader helpfully reformatted a little?

Thanks very much -- I appreciate you taking the time to form and share
your thoughts.

Mark

luser- -droog

unread,
Jan 11, 2011, 10:39:09 PM1/11/11
to
On Jan 11, 7:55 pm, "Mark T. B. Carroll" <m...@bcs.org> wrote:

> luser- -droog <mijo...@yahoo.com> writes:
>
> (snip)
>
> > And then I noticed that the offsets are flopped.
> > So, your way is probably better here, but bear in
> > mind that stack manipulation can sometimes simplify
> > things.
>
> That's true. Though with 'roll' I always have to test it out, I usually
> get it wrong first try! exch is okay though. (-:

I had difficulty with roll, too, for a very long time.
I remember it now this way:
n j roll
positive j to roll away
negative to get it back

luser- -droog

unread,
Jan 12, 2011, 3:47:00 AM1/12/11
to

Perhaps a better way to think of it is a physical stack
(of books, say) so the top of stack is literally "on top".
Then a positive roll goes up:
for j number of times
pick up n books
put the top one on the bottom (shifting the substack "up")
put them back down
And a negative roll goes down:
for j number of times
pick up n books
put the bottom one on top (shifting the substack "down")
put them back down

But I usually picture the stack sideways, the way the objects
would look in a file as a sequence of literals. So I think of
the positive roll as stashing the top j things behind the nth
thing; and the negative roll as snagging j things starting
with the nth thing. Give and Take. Jesus says giving is better
so it's the positive thing to do.

HTH

luser- -droog

unread,
Jan 12, 2011, 4:26:15 AM1/12/11
to
On Jan 12, 2:47 am, luser- -droog <mijo...@yahoo.com> wrote:
> On Jan 11, 9:39 pm, luser- -droog <mijo...@yahoo.com> wrote:
>
>
>
> > On Jan 11, 7:55 pm, "Mark T. B. Carroll" <m...@bcs.org> wrote:
>
> > > luser- -droog <mijo...@yahoo.com> writes:
>
> > > (snip)
>
> > > > And then I noticed that the offsets are flopped.
> > > > So, your way is probably better here, but bear in
> > > > mind that stack manipulation can sometimes simplify
> > > > things.
>
> > > That's true. Though with 'roll' I always have to test it out, I usually
> > > get it wrong first try! exch is okay though. (-:
>
> > I had difficulty with roll, too, for a very long time.
> > I remember it now this way:
> > n j roll
> > positive j to roll away
> > negative to get it back
>
> Perhaps a better way to think of it is a physical stack
> (of books, say) so the top of stack is literally "on top".
[snip]

> But I usually picture the stack sideways, the way the objects
> would look in a file as a sequence of literals.
[snip]

Still another way is to picture it sideways,
and laying a sticky wheel on top (a lint-roller, maybe)

(a) (b) (c) (d) (e) 5 3 roll

_______
/ \
| 3 |
| / | \ |
\_______/
(a) (b) (c) (d) (e)

Then a positive roll goes counterclockwise
just like arc and rotate.

_______ (e)
/ / \
| 3 --| (d)
| \ |
\_______/ (c)
(a) (b)


(e)__(d)__(c)
/\ | /\
| 3 |
| |
\_______/
(a) (b)


(c)_______
/\ \
(d) |-- 3 |
|/ |
\_______/
(e) (a) (b)


_______
/ \
| 3 |
| / | \ |
\_______/
(c) (d) (e) (a) (b)


And a negative roll goes clockwise
like arcn and a negative rotation.

_______
/ \
| 3 |
| / | \ |
\_______/
(a) (b) (c) (d) (e)

(a)_______
/\ \
(b) |-- 3 |
|/ |
\_______/
(c) (d) (e)


(c)__(b)__(a)
/\ | /\
| 3 |
| |
\_______/
(d) (e)


_______ (c)
/ / \
| 3 --| (b)
| \ |
\_______/ (a)
(d) (e)

_______
/ \
| 3 |
| / | \ |
\_______/
(d) (e) (a) (b) (c)

0 new messages