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

geodesic flowers

44 views
Skip to first unread message

luser- -droog

unread,
Sep 25, 2011, 4:15:27 AM9/25/11
to
I've been bewitched by this stackoverflow question:
http://stackoverflow.com/questions/7478666/how-can-i-programatically-generate-venn-diagram-images-with-labels-on-top-of-the/7507624#7507624

And I'd like to share the program I've cooked up to try to draw these
things. This program explores the construction of Venn diagrams and
related apparatus to locate the centers of the delimited regions so
labels can be suitably placed.

There's a lot to play with here. In fact, I can't stop playing with
it.
I'n many places, moving the comment % from one line to the next will
enable or disable a block of code, thus generating the described page
or not. The venoptions array allows you to select varied combinations
of apparatus. There a block to draw 20 small diagrams on a single
page, and another to draw 1 large diagram per page for a selection of
varying numbers of cells.

Have fun! Don't get too hypnotized.

%!

%cp:xy rad circ -
/circ {
currentpoint newpath
2 copy 5 -1 roll 0 360 arc stroke
moveto
} def

%rad n poly [[x0 y0]..[xn-1 yn-1]]
%construct a pointlist for n-poly with radius rad
/poly {
1 dict begin exch /prad exch def
[ exch
0 exch 360 exch div 359.5 {
[ exch
dup cos prad mul exch
sin prad mul
]
} for
]
end
} def

%[[x0 y0]..[xn-1..yn-1]] draw -
%draw polygon from pointlist
/draw {
1 dict begin gsave
currentpoint translate
/do { moveto /do { lineto } def } def
{ aload pop do } forall
closepath stroke
grestore end
} def

/rotw { 180 n div rotate } def

%[[x0 y0]..[xn-1..yn-1]] rotdraw -
%draw rotated polygon from pointlist
/rotdraw {
1 dict begin gsave
currentpoint translate
180 rotate
/do { moveto /do { lineto } def } def
{ aload pop do } forall
closepath stroke
grestore end
} def

%[list] drawperp -
%draw perpendiculars from origin through points
/drawperp {
gsave
currentpoint translate
{ 0 0 moveto aload pop lineto } forall
stroke
grestore
} def

%[list] drawrotperp -
%draw rotated perpendiculars from origin through points
/drawrotperp {
gsave
currentpoint translate
180 rotate
{ 0 0 moveto aload pop lineto } forall
stroke
grestore
} def

%[[x0 y0]..[xn-1..yn-1]] crop -
%crop polygon to pointlist
/crop {
2 dict begin
/state [ currentpoint matrix currentmatrix ] def
currentpoint translate
/do { moveto /do { lineto } def } def
{ aload pop do } forall
closepath clip
state aload pop setmatrix moveto
end
} def

%[list] rad subcirc -
/subcirc {
1 dict begin /crad exch def gsave
currentpoint translate
{ aload pop moveto crad circ } forall
grestore end
} def

%[list] rad n subpolydraw -
/subpolydraw {
2 dict begin /spn exch def /sprad exch def gsave
currentpoint translate
{ aload pop moveto sprad spn poly draw } forall
grestore end
} def

%[list] rad n rotsubpolydraw -
/rotsubpolydraw {
2 dict begin /spn exch def /sprad exch def gsave
currentpoint translate
180 rotate
{ aload pop moveto sprad spn poly draw } forall
grestore end
} def

%[list] rad n rotsubpolyrotdraw -
/rotsubpolyrotdraw {
2 dict begin /spn exch def /sprad exch def gsave
currentpoint translate
180 rotate
{ aload pop moveto sprad spn poly rotdraw } forall
grestore end
} def

%[list] rad n subpolyrotdraw -
/subpolyrotdraw {
2 dict begin /spn exch def /sprad exch def gsave
currentpoint translate
{ aload pop moveto sprad spn poly rotdraw } forall
grestore end
} def

%[x0 y0] [x1 y1] pyth-dist radius
/pyth-dist {
aload pop 3 -1 roll aload pop % x1 y1 x0 y0
exch % x1 y1 y0 x0
3 1 roll sub dup mul % x1 x0 dy^2
3 1 roll sub dup mul % dy^2 dx^2
add sqrt
} def

%cp:xy rad n ven2 -
%make the circles intersect the opposite point of def poly
/ven2 {
3 dict begin /n exch def /vrad exch def
vrad n poly
dup 0 get exch
dup length 2 idiv get
pyth-dist /crad exch def
vrad crad n ven
end
} def

%cp:xy rad n ven3 -
%make the circles intersect the adjacent point of def poly
% this one quickly degenerates to something NOT a Venn diagram
/ven3 {
3 dict begin /n exch def /vrad exch def
n 2 lt { /crad vrad 2 mul def
}{
vrad n poly
dup 0 get exch
1 get exch
pyth-dist /crad exch def
} ifelse
vrad crad n ven
end
} def

%cp:xy rad n ven4 -
%make the circles intersect 2 points away on def poly
% this one degenerates at n= 11
/ven4 {
3 dict begin /n exch def /vrad exch def
n 2 le { /crad vrad 2 mul def
}{
vrad n poly
dup 0 get exch
2 get exch
pyth-dist /crad exch def
} ifelse
vrad crad n ven
end
} def

%ven4, but clipped to the def poly
%for n>6, this is really pretty
/ven4c {
2 copy poly crop
ven4
} def

/venprocs [
{vrad n poly crad subcirc } %the Venn circles
{vrad n poly crad n subpolydraw } %polys on the poly
{vrad n poly crad n subpolyrotdraw } %rotated polys on the poly
{vrad n poly crad n rotsubpolydraw } %polys on the rotated poly
{vrad n poly crad n rotsubpolyrotdraw } %rotated polys on the rot poly

{vrad circ } %defining circle
{vrad n poly draw } %defining polygon
{vrad n poly drawperp } %draw perpendiculars to def poly
{vrad crad add n poly drawperp } %draw perpendiculars to diagram edges
{vrad crad add n poly drawrotperp } %draw rotated perps to diagram
edges

{vrad crad 2 div add n poly draw } %Rv+Rc/2 centered poly
{crad n poly draw } %cell-radius centered poly
{vrad crad .5 mul add n poly draw } %enlarged def poly
{vrad crad .25 mul sub n poly draw } %reduced def poly
{vrad 2 1 n div sub mul n poly draw } %weightedly enlarged def poly

{gsave 180 rotate vrad n poly draw grestore } %rotated defining
polygon
%rotated enlarged def poly
{gsave 180 rotate vrad crad .5 mul add n poly draw grestore }
%rot weightedly enlarged def poly
{gsave 180 rotate vrad 2 1 n div sub mul n poly draw grestore }
%rotated defining polygon
{gsave rotw vrad n poly draw grestore }
%rotated enlarged def poly
{gsave rotw vrad crad .5 mul add n poly draw grestore }
%rot weightedly enlarged def poly
{gsave rotw vrad 2 1 n div sub mul n poly draw grestore }
] def

/venoptions [
0 1 3 6 8 9 %lots of polys and perps
%0 % The normal Venn diagram
%0 6 8 9 15 16 17 %def poly, perps, rotated def polys
%0 12 14 15 %enlarged, weighted, rotated
%0 1 venprocs length 1 sub {} for % Draw Everything
] def

%cp:xy vrad crad n ven -
/ven {
3 dict begin /n exch def /crad exch def /vrad exch def {
venoptions {
venprocs exch get exec
} forall
exit } loop end
} def

%draw diagrams for n= 2..16
{
/in{72 mul}def
/n 2 def
4 {
1.7 in 2 in 10 in {
1.3 in exch moveto
20 n ven2
/n n 1 add store
} for
2 in 0 translate
} repeat
showpage
}
%pop
exec

[
%6
2 1 10 {} for
12 2 20 {} for
25 5 40 {} for
60 20 120 {} for
150 50 300 {} for
] {
/n exch def
(n = )print n =
300 400 moveto
%100 n ven4c
%100 140 n ven
100 n ven2
%100 n ven3
%100 n ven4
showpage
}
forall
%pop pop


[ %doodles

{
288 288 moveto
144 circ
144 2 poly draw
144 3 poly draw
144 4 poly draw
144 5 poly draw
144 6 poly draw
showpage
}

{
288 380 moveto
144 circ
144 3 poly draw
144 3 poly 144 subcirc
144 3 poly 144 3 subpolydraw
showpage
}

{
288 360 moveto
144 circ
%144 3 poly draw
144 3 poly 144 subcirc
216 3 poly draw
showpage
}

{
300 400 moveto
100 circ
100 3 poly draw
100 3 poly 120 subcirc
160 3 poly draw
100 3 poly 120 3 subpolydraw
showpage
}

{
300 400 moveto
100 circ
100 4 poly draw
100 4 poly 120 subcirc
100 4 poly 120 4 subpolydraw
}

]
pop
%{exec} forall

luser- -droog

unread,
Sep 25, 2011, 4:30:26 AM9/25/11
to
On Sep 25, 3:15 am, luser- -droog <mijo...@yahoo.com> wrote:
> I've been bewitched by this stackoverflow question:http://stackoverflow.com/questions/7478666/how-can-i-programatically-...

>
> And I'd like to share the program I've cooked up to try to draw these
> things. This program explores the construction of Venn diagrams and
> related apparatus to locate the centers of the delimited regions so
> labels can be suitably placed.
>
> There's a lot to play with here. In fact, I can't stop playing with
> it.
> I'n many places, moving the comment % from one line to the next will
> enable or disable a block of code, thus generating the described page
> or not. The venoptions array allows you to select varied combinations
> of apparatus. There a block to draw 20 small diagrams on a single
> page, and another to draw 1 large diagram per page for a selection of
> varying numbers of cells.
>
> Have fun! Don't get too hypnotized.
>

Those darned broken lines again!
Here it is a little skinnier.

%!

{vrad n poly crad n rotsubpolyrotdraw } %rot polys on the rot poly

{vrad circ } %defining circle
{vrad n poly draw } %defining polygon
{vrad n poly drawperp } %draw perpendiculars to def poly

{vrad crad add n poly drawperp } %draw perps to diagram edges
{vrad crad add n poly drawrotperp } %draw rotated perps to edges

{vrad crad 2 div add n poly draw } %Rv+Rc/2 centered poly
{crad n poly draw } %cell-radius centered poly
{vrad crad .5 mul add n poly draw } %enlarged def poly
{vrad crad .25 mul sub n poly draw } %reduced def poly
{vrad 2 1 n div sub mul n poly draw } %weightedly enlarged def poly

%rotated defining polygon


{gsave 180 rotate vrad n poly draw grestore }

Eli the Bearded

unread,
Sep 26, 2011, 8:31:30 PM9/26/11
to
In comp.lang.postscript, luser- -droog <mij...@yahoo.com> wrote:
> > Have fun! Don't get too hypnotized.
> Those darned broken lines again!
> Here it is a little skinnier.

Fun, indeed. But what is causing ghostview to sometimes pop up an
informational window with a bunch of numbers?

Elijah
------
did not examine the code that closely

luser- -droog

unread,
Sep 27, 2011, 12:33:05 AM9/27/11
to
On Sep 26, 7:31 pm, Eli the Bearded <*...@eli.users.panix.com> wrote:
> In comp.lang.postscript, luser- -droog  <mijo...@yahoo.com> wrote:
>
> > > Have fun! Don't get too hypnotized.
> > Those darned broken lines again!
> > Here it is a little skinnier.
>
> Fun, indeed. But what is causing ghostview to sometimes pop up an
> informational window with a bunch of numbers?
>
> Elijah
> ------
> did not examine the code that closely

That'll be this part. I usually run it from within vi using the
command ":!gs %". The numbers are the value of n for the diagram
just drawn.

[
%6
2 1 10 {} for
12 2 20 {} for
25 5 40 {} for
60 20 120 {} for
150 50 300 {} for
] {
/n exch def
(n = )print n = % comment-out this line for no numbers
300 400 moveto
%100 n ven4c
%100 140 n ven
100 n ven2
%100 n ven3
%100 n ven4
showpage
}

forall
%pop pop

Sadly, I learned that for n>3, these are not Venn diagrams.
They are Euler diagrams.

Eli the Bearded

unread,
Sep 27, 2011, 1:12:23 AM9/27/11
to
In comp.lang.postscript, luser- -droog <mij...@yahoo.com> wrote:
> Sadly, I learned that for n>3, these are not Venn diagrams.
> They are Euler diagrams.

"Euler diagrams" sounds so dirty.
"Oil 'er diagrams? I 'ardly know 'er.'

Elijah
------
say venn you want the puns to end

luser- -droog

unread,
Oct 2, 2011, 1:52:02 PM10/2/11
to
I'm trying to redesign and simply the Euler diagram code I posted
earlier,
and I need help debugging it! This test attempts to draw the circles
as
parts of a single path so it can be eofill-ed for that cool
checkerboard
pattern.

But I'm getting a ghost polygon on the right-hand side!
This is a minimal example that illustrates the problem.
It doesn't happen with the polygon approximation (move comment).
And if you add closepath after arc, it *almost* goes away, but
it leaves some spooky hairline radii from the initial point.

%!

%rad n poly [[x1 y1]..[xN yN]]
/poly {
[ 3 1 roll %[ r n
0 exch 360 exch div 359.9 { %[ .. r a
[ exch dup cos 3 index mul %[ .. r [ a rc
exch sin 3 index mul ] exch %[ .. [r*cos(a) r*sin(a)] r
} for
pop ]
} def
%5 4 poly == quit
%!!gsnd -q % => [[5.0 0.0] [0.0 5.0] [-5.0 0.0] [0.0 -5.0]]

%[plist] draw -
/draw {
dup 1 1 index length 1 sub getinterval exch 0 get %rest first
aload pop moveto
{ aload pop lineto } forall
closepath
} def

%[plist] {proc} oeachpoint -
%for each point, translate and call proc WITHOUT gsave/grestore
/oeachpoint {
%{ matrix currentmatrix exch aload pop translate
% DUMMY6 exec setmatrix }
%dup length array copy cvx dup
% 6 4 -1 roll put
{ aload pop translate DUMMY3 exec matrix setmatrix }
dup length array copy cvx dup 3 4 -1 roll put
dup 5 matrix currentmatrix put
forall
} def

%[x0 y0] [x1 y1] dist radius
/dist {
aload pop 3 -1 roll aload pop % x1 y1 x0 y0
exch % x1 y1 y0 x0
3 1 roll sub dup mul % x1 x0 dy^2
3 1 roll sub dup mul % dy^2 dx^2
add sqrt
} def


%rad n euler -
/euler { 4 dict begin /n exch def /rad exch def
%/crad rad n poly dup 0 get exch dup length 2 idiv get dist def
/crad rad def
/p rad n poly def
%rad circ stroke %def circ
%p draw stroke %def poly
p { 0 0 crad 0 360 arc } oeachpoint eofill %filled euler circles
%p { crad n poly draw } oeachpoint eofill %euler poly checker
end } def

300 400 translate
100 9 euler
showpage

tlvp

unread,
Oct 2, 2011, 2:24:34 PM10/2/11
to
Every time I've managed to figure out why eofill or fill didn't work
for me the way I had had in mind when invoking it, it turned out that
what I thought I had set up as one closed path I had in fact set up
as something *other* than one closed path -- most often as *two* paths,
consecutive ones, with the second beginning where the first one ended.

PS/gs will close each first, and then (eo)fill the resulting closed paths.

But it can be tricky to find one's mistake, as it's hard to notice
in what invocation one has inadvertently "lifted the virtual cursor"
from the page, only to "put it down again" at the same point.

HTH. All the best; and cheers, -- tlvp
--
Avant de repondre, jeter la poubelle, SVP.

luser- -droog

unread,
Oct 3, 2011, 12:08:47 AM10/3/11
to
On Oct 2, 1:24 pm, tlvp <mPiOsUcB.EtLlL...@att.net> wrote:
> On Sun, 2 Oct 2011 10:52:02 -0700 (PDT), luser- -droog wrote:
> > I'm trying to redesign and simply the Euler diagram code I posted
> > earlier,
> > and I need help debugging it! This test attempts to draw the circles
> > as
> > parts of a single path so it can be eofill-ed for that cool
> > checkerboard
> > pattern.
>
> > But I'm getting a ghost polygon on the right-hand side!
> > This is a minimal example that illustrates the problem.
> > It doesn't happen with the polygon approximation (move comment).
> > And if you add closepath after arc, it *almost* goes away, but
> > it leaves some spooky hairline radii from the initial point.
>

[snip buggy code]

>
> Every time I've managed to figure out why eofill or fill didn't work
> for me the way I had had in mind when invoking it, it turned out that
> what I thought I had set up as one closed path I had in fact set up
> as something *other* than one closed path -- most often as *two* paths,
> consecutive ones, with the second beginning where the first one ended.
>
> PS/gs will close each first, and then (eo)fill the resulting closed paths.
>
> But it can be tricky to find one's mistake, as it's hard to notice
> in what invocation one has inadvertently "lifted the virtual cursor"
> from the page, only to "put it down again" at the same point.
>
> HTH. All the best; and cheers, -- tlvp


I found the problem. It was the /poly procedure. The primary
procedure
upon which everything else is built. It's just too early in the
program
to use Cartesian coordinates. Drawing circles around circles with
circles
is a task for which radial coordinates are more appropriate.

So, back to the drawing board... :)

Thanks to all telepathic readers!

luser- -droog

unread,
Oct 3, 2011, 2:52:02 AM10/3/11
to
Total Rethink. I packed all the power into one superprocedure.
Oh, and I found a much nicer way to dynamically generate procedures.
Nicer, even, than I ever thought possible.

This program generates 24 images which are just awesome.

And it's so much shorter than the other ones!


%!

%rad n {proc} atpoly -
%call proc having rotated+scaled
%so (1,0) is each vertex of rad-radius n-polygon
/atpoly { 5 dict begin /p exch def /n exch def /r exch def
/m matrix currentmatrix def
r r scale
/s matrix currentmatrix def
0 360 n div 359.9 ({
//s setmatrix
rotate
//p exec
}) token pop exch pop for
m setmatrix
end } def

/procs [
%0 defining circle
{ 0 0 rad -180 180 arc }

%1 venn-euler circs
{ rad n { rad2 0 rad2 -180 180 arc } atpoly }

%2 defining polygon
{ rad 0 moveto rad n { 1 0 lineto } atpoly closepath }

%3 venn-euler polygon approximation
{ rad n { 1 0 translate rad2 0 moveto rad2 n { 1 0 lineto } atpoly
closepath } atpoly }

] def

%rad n [proc-indices] venn-euler -
/venn-euler { 3 dict begin 3 1 roll /n exch def /rad exch def
/rad2 1 def
{
procs exch get exec
} forall
} def

3 1 12 { /n exch def
300 400 translate
100 n [ 2 3 ] venn-euler stroke % def poly & poly approx
180 n div rotate
100 n [ 2 ] venn-euler stroke % rotated def poly
showpage
} for %clear

3 1 12 { /n exch def
300 400 translate
100 n [ 1 ] venn-euler eofill % circ checkerboard
.7 .1 .2 setrgbcolor
100 n [ 3 ] venn-euler stroke % scarlet poly approx
showpage
} for

%eof

Peter Billam

unread,
Oct 3, 2011, 4:29:39 AM10/3/11
to
On 2011-10-03, luser- -droog <mij...@yahoo.com> wrote:
> Total Rethink. I packed all the power into one superprocedure.
> Oh, and I found a much nicer way to dynamically generate procedures.
> Nicer, even, than I ever thought possible.
> This program generates 24 images which are just awesome.
> And it's so much shorter than the other ones!
... ps snipped, with regret ...

That's beautiful, well done :-)

Regards, Peter

--
Peter Billam www.pjb.com.au www.pjb.com.au/comp/contact.html

tlvp

unread,
Oct 3, 2011, 1:17:41 PM10/3/11
to
On 03 Oct 2011 08:29:39 GMT, Peter Billam wrote:

> ... That's beautiful, well done :-)

I sincerely echo that sentiment :-) . Cheers, -- tlvp

luser- -droog

unread,
Oct 4, 2011, 3:44:54 AM10/4/11
to
I'll spare you the part about having to scan my comments for
mismatched braces. Oh. um. moving on...

This should be the Final version. I hope it has been sufficient
distraction so I can get back to other stalled projects. :)

DO NOT PRINT THIS FILE until you read the comments. You need to
set the upper limit of the loop to something reasonable.
The comments tell you where to make the change.


%!
/comment{{currentfile token pop/endcomment eq{exit}if}loop}def
comment
This program (I call it "ve6.ps") prints a series of fancy
Venn(n=2,3)-Euler(n>3) diagrams, four to a page,
for increasing values of n.

It uses a few "tricks" I've just discovered(?) which harness
a great deal of power from the postscript interpreter.

Dynamically constructed procedure bodies.
The clumsy way to do this is to count tokens in your procedure
with your finger on the screen to get the offset of the patch.
Then you do 'dup offset patch put' before you 'def' or whatever.

eg, a static local dictionary:
/p { 0 begin /y exch def /x exch def x dup mul y add end }
dup 0 2 dict put def

The new, fancy way is to abuse the scanner. Specifically,
you put your template in a string with your patch points
as immediately evaluated names. Then define the patch names
in the dictstack, and use 'token pop exch pop'. 'token' does
the patching for you!

eg. a nestable control-structure procedure
see /atpoly , below

Unnamed procedures.
There were way too many names in previous versions of this
program, mostly for slight variations of the same thing.
The shiningly bad example is /rotsubpolyrotdraw . To combat
this, I made a vector of procedures which are accessed by
index-number. These can be composed by mentioning more than
one index in the [proc-indices] argument to /venn-euler .

TODO
Find the formula for /rad2 so opposite polygons intersect nicely.
With the old version, I was generating a list of points,
so it was easy to snag the "middle" point, apply the
pythagorean theorem, and run with it. But now rad2 needs to
be a multiplier of rad, not an "absolute" value; and there's no
list of points! So I've got to go do some trigonometry...
endcomment

%rad n {proc} atpoly -
%call proc having rotated+scaled
%so (1,0) is each vertex of rad-radius n-polygon
/atpoly { 5 dict begin /p exch def /n exch def /r exch def
/m matrix currentmatrix def
r r scale
/s matrix currentmatrix def
0 360 n div 359.9 ({
//s setmatrix
rotate
//p exec
}) token pop exch pop for
m setmatrix
end } def

/procs [
%0 defining circle
{ 0 0 rad -180 180 arc closepath }

%1 venn-euler circs
{ rad n { 1 0 rad2 -180 180 arc closepath } atpoly }

%2 defining polygon
{ rad 0 moveto rad n { 1 0 lineto } atpoly closepath }

%3 venn-euler polygon approximation
{ rad n { 1 0 translate rad2 0 moveto rad2 n { 1 0 lineto } atpoly
closepath } atpoly }

%4 radii
{ 0 0 moveto rad n { 1 0 lineto 0 0 lineto } atpoly closepath }

%5 v-e radii
{ rad n { 1 0 translate 0 0 moveto rad2 n { 1 0 lineto 0 0 lineto }
atpoly closepath } atpoly }

] def

%rad n [proc-indices] venn-euler -
/venn-euler { 3 dict begin 3 1 roll /n exch def /rad exch def
/rad2 1 def
{
procs exch get exec
} forall
} def

comment
Following is the page description itself. /venn-euler merely
describes the path, allowing the calling code to decide whether
to stroke or fill or clip or what-have-you.

This script was modified from a version which prints large
images one-to-a-page. This behavior may be resurrected by
commenting-out the gsave/grestore lines and uncommenting
the adjacent lines.

/bank is a little trick to "skip to the good part" when
building-up a sequence of variations. As each version is
completed and a new one begun, change the 'for' to 'bank'.
This deactivates the loop. Then, for presentation, delete
'clear %', making 'bank' a synonym for 'for'. This activates
all the loops. mnemonic: "bank is 'for' or nothing".

For printing, you may wish to change the loop control line
from 'ini 1 infin ...' to 'ini 1 fin ...'.
Otherwise it could take a long time and waste lots of paper.

Also, try enabling and disabling (%commenting) the "big circle"
in each diagram: it inverts black/white portions of the
even-odd fill.

endcomment

.5 .5 scale %four to a page: two chops

/ini 1 def
/fin 15 def
/infin 100 def

/bank /clear %for
load def

/Palatino-Roman 12 selectfont

ini 1 infin { /n exch def %(n = )print n =

gsave 20 699 translate

(Black defining polygon and polygon approximation) 50 700 moveto
show
(Red rotated defining polygon) 50 686 moveto show
( n=)show n( )cvs show newpath
300 400 translate
0 0 200 180 n div dup 360 add arc closepath % big circle
100 n [ 2 3 ] venn-euler stroke % def poly & poly approx
180 n div rotate
.8 .1 .2 setrgbcolor % scarlet
9 n div setlinewidth
100 n [ 2 ] venn-euler stroke % rotated def poly

%showpage } bank ini 1 fin { /n exch def %(n = )print n =
grestore gsave 652 699 translate

(Black circular checkerboard with circle outline) 50 700 moveto
show
(Red polygon approximation) 50 686 moveto show
( n=)show n( )cvs show newpath
300 400 translate
0 0 200 180 n div dup 360 add arc closepath % big circle
100 n [ 1 ] venn-euler eofill % circ checkerboard
.8 .1 .2 setrgbcolor % scarlet
9 n div setlinewidth
100 n [ 3 ] venn-euler stroke % poly approx

%showpage } bank ini 1 fin { /n exch def %(n = )print n =
grestore gsave 652 20 translate

(Black between circles and polygon approximations) 50 700 moveto
show
(Red defining circle and radii on def poly and all polys) 50 686
moveto show
( n=)show n( )cvs show newpath
300 400 translate
%0 0 200 180 n div dup 360 add arc closepath % big circle
100 n [ 0 2 ] venn-euler
100 n [ 1 3 ] venn-euler eofill % between the circ and poly
.8 .1 .2 setrgbcolor % scarlet
9 n div setlinewidth %small lines don't obscure small shapes
100 n [ 0 ] venn-euler stroke % def circ
100 n [ 4 5 ] venn-euler stroke % radii & v-e radii

%showpage } bank ini 1 infin { /n exch def %(n = )print n =
grestore gsave 20 20 translate

(Black polygon checkerboard) 50 700 moveto show
(Red def circle, euler circles, radii; poly, polys, radii) 50 686
moveto show
( n=)show n( )cvs show newpath
300 400 translate
%0 0 200 180 n div dup 360 add arc closepath % big circle
100 n [ 3 ] venn-euler eofill % eo polys
.8 .1 .2 setrgbcolor % scarlet
9 n div setlinewidth
100 n [ 0 2 4 ] venn-euler stroke
100 n [ 1 3 5 ] venn-euler stroke

%showpage
grestore gsave showpage grestore

} for

%eof M Joshua Ryan mij...@yahoo.com Tue Oct 4 02:25:39 CDT 2011

luser- -droog

unread,
Oct 5, 2011, 2:17:56 AM10/5/11
to
Why do I always post in such haste?!

The program I last posted, as written, attempts to draw diagrams
for n=1..100. But after 15 or so, they just get so slow that it's
not fun to watch anymore. So I never tested it past 20 or so.

Later I decided to make a pdf so I could zoom in on some larger
ones. ghostscript crashed on number 46. So I thought: oh, that's
got to be quite a lot of lines. I changed n to 45 and gs was
happy to make a pdf. But the pdf takes about the same amount
of time to render as ghostscript; but with the advantage of
skipping ahead and going backward.

That wasn't so terrible because I warned everyone to change the
number anyway.

So I just remembered that I, too, have a postscript interpreter.
xpost2 exits with a fatal error on page 3. A little debugging
and it turns out to be a dictstackoverflow. Hmmm. That's the
same error that ghostscript ran into. So I pull up ve6.ps again
and sure enough ...

On Oct 4, 2:44 am, luser- -droog <mijo...@yahoo.com> wrote:

> %rad n [proc-indices]  venn-euler  -
> /venn-euler { 3 dict begin 3 1 roll /n exch def /rad exch def
>     /rad2 1 def
>     {
>         procs exch get exec
>     } forall
>
> } def

... should be:

end } def


Then you can take it to 100 (and beyond?).
But it's just a blur. :(

42(1)12:48 AM:~ 0> ps2pdf ve6.ps
43(1)12:54 AM:~ 0> ll ve6.pdf
-rw-r--r-- 1 olpc olpc 14435153 Oct 5 00:54 ve6.pdf

So it takes about 6 minutes to generate a 14Meg pdf
from this 6k ps file on my olpc.

luser- -droog

unread,
Oct 6, 2011, 3:51:37 AM10/6/11
to
I've fixed a great many things. /atpoly removes the dictionary
before executing the loop; thus making it a truly transparent
control structure. /rad2 has a nice short formula (discovered in
a round-about way: the horrors are preserved in a %comment). And
the ghost lines are gone (Thanks, Ken!).



%!
/comment{{currentfile token pop/endcomment eq{exit}if}loop}def


comment Intro
This program (I call it "ve6.ps") prints a series of fancy
Venn(n=2,3)-Euler(n>3) diagrams, four to a page,
for increasing values of n.

It uses a few "tricks" I've just discovered(?) which harness
a great deal of power from the postscript interpreter.
endcomment


comment Dynamically constructed procedure bodies.
The clumsy way to do this is to count tokens in your procedure
with your finger on the screen to get the offset of the patch.
Then you do 'dup offset patch put' before you 'def' or whatever.

eg, a static local dictionary:
/p { 0 begin /y exch def /x exch def x dup mul y add end }
dup 0 2 dict put def

The new, fancy way is to abuse the scanner. Specifically,
you put your template in a string with your patch points
as immediately evaluated names. Then define the patch names
in the dictstack, and use 'token pop exch pop'. 'token' does
the patching for you!

eg. a nestable control-structure procedure: endcomment

%rad n {proc} atpoly -
%call proc having rotated+scaled
%so (1,0) is each vertex of rad-radius n-polygon
/atpoly {
4 dict begin /p exch def /n exch def % rad
/m matrix currentmatrix def
dup scale
/s matrix currentmatrix def
0 360 n div 359.9 %0 dAng maxAng %{}for
({
{
//s setmatrix
rotate
//p exec
} for
//m setmatrix
}) token pop exch pop %instantiate code template
end exec % run loop without dictionary
} def

% A few helper functions

%[device-points] devcur [ctm-points]
%devcur converts a list of device space points
% into a list of user space points
/devcur {
dup {
aload 3 1 roll
itransform
3 -1 roll astore pop
} forall
} def

%x0 y0 x1 y1 dist num
%dist applies the pythagorean theorem to two points
/dist {
exch % x1 y1 y0 x0
3 1 roll sub dup mul % x1 x0 dy^2
3 1 roll sub dup mul % dy^2 dx^2
add sqrt
} def

comment Unnamed procedures.
There were way too many names in previous versions of this
program, mostly for slight variations of the same thing.
The shiningly bad example is /rotsubpolyrotdraw . To combat
this, I made a vector of procedures which are accessed by
index-number. These can be composed by mentioning more than
one index in the [proc-indices] argument to /venn-euler .
endcomment

/procs [
%0 defining circle
{ rad neg 0 moveto 0 0 rad -180 180 arc closepath }

%1 venn-euler circs
{ rad n { rad2 neg 0 moveto 1 0 rad2 -180 180 arc closepath } atpoly }

%2 defining polygon
{ rad 0 moveto rad n { 1 0 lineto } atpoly closepath }

%3 venn-euler polygon approximation
{ rad n { 1 0 translate rad2 0 moveto rad2 n { 1 0 lineto } atpoly
closepath } atpoly }

%4 radii
{ rad 0 moveto rad n { 1 0 lineto 0 0 lineto } atpoly closepath }

%5 v-e radii
{ rad n { 1 0 translate 0 0 moveto rad2 n { 1 0 lineto 0 0 lineto }
atpoly closepath } atpoly }

%6 rotated defining polygon
{ 180 n div rotate rad 0 moveto rad n { 1 0 lineto } atpoly
closepath }

%7 rotated polygon approximation
{ rad n { 1 0 translate 180 n div rotate rad2 0 moveto
rad2 n { 1 0 lineto } atpoly closepath } atpoly }

%list polygon points
{[rad n{[1 0 transform]}atpoly]devcur}

] def

%rad n [proc-indices] venn-euler -
/venn-euler { 3 dict begin 3 1 roll /n exch def /rad exch def
/rad2 where { pop }{ %/rad2 1 def rad n [6] venn-euler
%dup 0 get exch n 2 idiv get dist rad div
n 2 idiv 360 n div mul dup cos exch sin 1 0 dist
/rad2 exch def
} ifelse
{
procs exch get exec
} forall
end } def

comment Radius of the Subcircles
Find the formula for /rad2 so opposite polygons intersect nicely.
With the old version, I was generating a list of points,
so it was easy to snag the "middle" point, apply the
pythagorean theorem, and run with it. But now rad2 needs to
be a multiplier of rad, not an "absolute" value; and there's no
list of points! So I've got to go do some trigonometry...

Update. Forget the trig.
I added a proc to venn-euler which returns a list of polygon
points (the old method revived). Then I have venn-euler check
if rad2 is defined, and if not, define it as 1 (to prevent
infinite recursion), call itself to get a point list, get the
distance, set rad2, and continue. You can thus override the
calculation by providing a value for rad2 in an outer scope.

Update. Trig wins.
Since it operates within a coordinate system where /rad 1 def ,
/rad2 is dist( (cos(trunc(n/2)*360/n), sin(trunc(n/2)*360/n)), (1,0) )
endcomment

% Override Subcircle Radius
%/rad2 .5 def % little circles
/rad2 2 def % big circles

%shrink is called in each diagram
%after translation to the center
/shrink
{.8 .8 scale} def % if rad2 > rad, you may need to shrink
%{} def % if not, don't.



comment
Following is the page description itself. /venn-euler merely
describes the path, allowing the calling code to decide whether
to stroke or fill or clip or what-have-you.

This script was modified from a version which prints large
images one-to-a-page. This behavior may be resurrected by
commenting-out the gsave/grestore lines and uncommenting
the adjacent lines.

/bank is a little trick to "skip to the good part" when
building-up a sequence of variations. As each version is
completed and a new one begun, change the 'for' to 'bank'.
This deactivates the loop. Then, for presentation, delete
'clear %', making 'bank' a synonym for 'for'. This activates
all the loops. mnemonic: "bank is 'for' or nothing".

For printing, you may wish to change the loop control line
from 'ini 1 infin ...' to 'ini 1 fin ...'.
Otherwise it could take a long time and waste lots of paper.

Also, try enabling and disabling (%commenting) the "big circle"
in each diagram: it inverts black/white portions of the
even-odd fill.

endcomment

.5 .5 scale %four to a page: two chops

/bank /clear %for
load def

/Palatino-Roman 12 selectfont

/ini 1 def
/fin 15 def
/infin 100 def

%Loop Control
ini 1 infin {
/sav save def
/n exch def (n = )print n =

gsave 20 699 translate

(Black defining polygon and rotated polygon approximation) 50 700
moveto show
(Red rotated defining polygon and unrotated approximation) 50 686
moveto show
( n=)show n( )cvs show newpath
300 400 translate
shrink
%0 0 200 180 n div dup 360 add arc closepath % big circle
5 setlinewidth
100 n [ 2 ] venn-euler stroke % def poly & poly approx
100 n [ 7 ] venn-euler stroke % rotated poly approx
%180 n div rotate
.8 .1 .2 setrgbcolor % scarlet
%9 n div setlinewidth
2 setlinewidth
100 n [ 3 6 ] venn-euler stroke % poly approx & rotated poly

%showpage } bank ini 1 fin { /n exch def %(n = )print n =
grestore gsave 652 699 translate

(Black circular checkerboard with circle outline) 50 700 moveto
show
(Red polygon approximation) 50 686 moveto show
( n=)show n( )cvs show newpath
300 400 translate
shrink
0 0 200 180 n div dup 360 add arc closepath % big circle
100 n [ 1 ] venn-euler eofill % circ checkerboard
.8 .1 .2 setrgbcolor % scarlet
9 n div setlinewidth
100 n [ 3 ] venn-euler stroke % poly approx

%showpage } bank ini 1 fin { /n exch def %(n = )print n =
grestore gsave 652 20 translate

(Black between circles and polygon approximations) 50 700 moveto
show
(Red defining circle and radii on def poly and all polys) 50 686
moveto show
( n=)show n( )cvs show newpath
300 400 translate
shrink
%0 0 200 180 n div dup 360 add arc closepath % big circle
100 n [ 0 2 ] venn-euler
100 n [ 1 3 ] venn-euler eofill % between the circ and poly
.8 .1 .2 setrgbcolor % scarlet
9 n div setlinewidth%small lines don't obscure small shapes
100 n [ 0 ] venn-euler stroke % def circ
100 n [ 4 5 ] venn-euler stroke % radii & v-e radii

%showpage } bank ini 1 infin { /n exch def %(n = )print n =
grestore gsave 20 20 translate

(Black polygon checkerboard) 50 700 moveto show
(Red circles, radii; def poly, euler polys, radii) 50 686 moveto
show
( n=)show n( )cvs show newpath
300 400 translate
shrink
%0 0 200 180 n div dup 360 add arc closepath % big circle
100 n [ 3 ] venn-euler eofill % eo polys
.8 .1 .2 setrgbcolor % scarlet
9 n div setlinewidth
100 n [ 0 2 4 ] venn-euler stroke
100 n [ 1 3 5 ] venn-euler stroke
%100 n [ 6 7 ] venn-euler stroke % rotated poly approx

%showpage
grestore gsave showpage grestore

sav restore
} for

%eof M Joshua Ryan mij...@yahoo.com Thu Oct 6 02:44:20 CDT 2011

tlvp

unread,
Oct 6, 2011, 7:14:52 PM10/6/11
to
On Thu, 6 Oct 2011 00:51:37 -0700 (PDT), luser- -droog commented:

> ... But now rad2 needs to
> be a multiplier of rad, not an "absolute" value; and there's no
> list of points! So I've got to go do some trigonometry...
>
> Update. Forget the trig.
> I added a proc to venn-euler which returns a list of polygon
> points (the old method revived). Then I have venn-euler check
> if rad2 is defined, and if not, define it as 1 (to prevent
> infinite recursion), call itself to get a point list, get the
> distance, set rad2, and continue. You can thus override the
> calculation by providing a value for rad2 in an outer scope.
>
> Update. Trig wins.
> Since it operates within a coordinate system where /rad 1 def ,
> /rad2 is dist( (cos(trunc(n/2)*360/n), sin(trunc(n/2)*360/n)), (1,0) )
> endcomment

(Applause :-) !) Trig wins because PS is, among other things, an
absolutely superlative RPN scientific calculator, far superior even
to the best college-level or engineering RPN scientific calculator
in the HP or Sharp or TI or Casio calculator spectrum.

Cheers, -- tlvp

luser- -droog

unread,
Oct 10, 2011, 3:02:57 AM10/10/11
to
I've discovered to my delight that xpost can draw these images
considerably faster than ghostscript (8.62) even with "

-2 vmreclaim %disable all garbage collection

". But when I try to use my new subcircle radius calculation,
Cairo spazzes out with ... oh, it isn't doing it now. Weird.
Well I was getting an Error Matrix Not Invertible or something
to that effect. But the program isn't using itransform or even
currentpoint. Oh well, I guess it's an outstanding bug.

But back to the good part. Speed. Raw, blinding speed!
If you've got xpost, you can see for yourself. After n=11,
ghostscript (in *my* installation, which is not at all current)
shows a noticeable delay after drawing 2 of the 4 diagrams,
and on subsequent pages, it just get more and more excruciating
(partly because the images get less interesting, too).

But thanks to xpost's horrible user interface, most of the
drawing time elapses while I'm moving the pointer back and
forth between the output window and the input window. :)

I don't dare test xpost/Cairo's pdf output of this file.
In my brief experience Cairo/pdf files are hugely enormous
for no apparent reason (perhaps I should look into upgrading
my software?! :) )

Well, thanks for listening, we'll talk more soon.

luser- -droog

unread,
Oct 13, 2011, 8:33:15 PM10/13/11
to
On Oct 10, 2:02 am, luser- -droog <mijo...@yahoo.com> wrote:
> I've discovered to my delight that xpost can draw these images
> considerably faster than ghostscript (8.62) even with "
>
>   -2 vmreclaim %disable all garbage collection
>
> ". But when I try to use my new subcircle radius calculation,
> Cairo spazzes out with ... oh, it isn't doing it now. Weird.
> Well I was getting an Error Matrix Not Invertible or something
> to that effect. But the program isn't using itransform or even
> currentpoint. Oh well, I guess it's an outstanding bug.

I solved this problem! It was in the calculation for the
"subcircle radius" for n=1. The radius is defined as the
distance between opposite points on the defining polygon,
but for n=1, there's only one point; so the distance between
it and itself is Zero. Then we try to scale by that amount.
kablooey! But ghostscript never complained; it willingly
placed all user-points on the same device point while
that matrix was in effect.

> But back to the good part. Speed. Raw, blinding speed!
> If you've got xpost, you can see for yourself. After n=11,
> ghostscript (in *my* installation, which is not at all current)
> shows a noticeable delay after drawing 2 of the 4 diagrams,
> and on subsequent pages, it just get more and more excruciating
> (partly because the images get less interesting, too).
>
> But thanks to xpost's horrible user interface, most of the
> drawing time elapses while I'm moving the pointer back and
> forth between the output window and the input window. :)

I solved this problem, too. I borrowed a few lines from
ghostscript:

{
XWMHints wm_hints;
wm_hints.flags = InputHint;
wm_hints.input = False;
XSetWMHints(st->dis, st->win, &wm_hints);
}

But xpost is doing some crazy stuff with the complicated
even-odd fills. ghostscript takes a long time with these,
but there are no detectable cracks in the symmetry.
I'll choose a really gnarly picture and post it.
I suspect it has to do with floating-point round-off.

Also, the ve6.ps code has gotten really long and complicated
again, so I'm going to print it out and retype it before posting
(this inevitably simplifies the code, because I get bored typing
anything unduly repetitive).

luser- -droog

unread,
Oct 27, 2011, 4:35:30 AM10/27/11
to
On Oct 13, 7:33 pm, luser- -droog <mijo...@yahoo.com> wrote:
> But xpost is doing some crazy stuff with the complicated
> even-odd fills. ghostscript takes a long time with these,
> but there are no detectable cracks in the symmetry.
> I'll choose a really gnarly picture and post it.
> I suspect it has to do with floating-point round-off.
>
> Also, the ve6.ps code has gotten really long and complicated
> again, so I'm going to print it out and retype it before posting
> (this inevitably simplifies the code, because I get bored typing
> anything unduly repetitive).


Well, I never did retype it. But I added a bunch of comments
to the whole mess, and ve6a.ps is available at
code.google.com/p/xpost

There's also a gradient sampler inspired by
http://stackoverflow.com/questions/7500185/postscript-drawing-a-gradient/

0 new messages