M

#### Matthew Moss

allow inline Postscript as method calls (so not as postfix). The

output is dumped to standard output to be redirected to a .ps file,

which can be easily viewed on Mac OS X (and almost as easily on other

platforms using ghostscript).

require 'matrix'

# Class helpers

class Array

def tail

self[1..-1]

end

def mod_fetch i

self[i % size]

end

def each_pair

each_with_index do |a, i|

yield a, self[i+1] if i+1 < size

end

end

end

class Vector

def x

self[0]

end

def y

self[1]

end

def len

Math.sqrt(inner_product(self))

end

def rot90

Vector[-y, x]

end

def rot45 # cheap rotate by 45 degrees

Vector[x - y, x + y]

end

def to_s

"#{x} #{y}"

end

end

# Postscript class (what a hack!)

class PS

def initialize(&block)

@cmds =3D []

instance_eval(&block) if block

end

def push(*args, &block)

@cmds << args.join(' ')

@cmds << instance_eval(&block) if block

end

def to_s

@cmds.join("\n")

end

def page(&block)

instance_eval(&block)

push 'showpage'

end

def path(&block)

push 'newpath'

instance_eval(&block)

end

def gsave(&block)

push 'gsave'

instance_eval(&block)

push 'grestore'

end

def method_missing(name, *args)

push *args + [name]

end

end

# Constants and helper funcs for building image data

Basis =3D [Vector[1, 0], Vector[0, -1], Vector[-1, 0], Vector[0, 1]]

Shade =3D [0.3, 0.5, 0.7]

def fibo(n)

a, b =3D 1, 1

n.times { a, b =3D b, a + b }

a

end

def spiral(n)

if n.zero?

Vector[0, 0]

else

i =3D n - 1

spiral(i) + Basis.mod_fetch(i) * fibo(i)

end

end

# Build list of spiral coordinates

steps =3D (ARGV[0] || 11).to_i

coords =3D (0..steps).map { |i| spiral(i).rot45 }

# Calculate page/content dimensions, scale and center

inch =3D 72

margin =3D 0.5 * inch

pagew =3D 8.5 * inch

pageh =3D 11 * inch

contw =3D pagew - 2 * margin

conth =3D pageh - 2 * margin

xmin =3D coords.min { |a, b| a.x <=3D> b.x }.x

xmax =3D coords.max { |a, b| a.x <=3D> b.x }.x

ymin =3D coords.min { |a, b| a.y <=3D> b.y }.y

ymax =3D coords.max { |a, b| a.y <=3D> b.y }.y

scale =3D [contw / (xmax - xmin), conth / (ymax - ymin)].min

cx =3D (pagew - (xmax - xmin.abs) * scale) / 2

cy =3D (pageh - (ymax - ymin.abs) * scale) / 2

# Scale coords to fill page

coords.map! { |v| v * scale }

# Build Postscript image

doc =3D PS.new do

def box a, b

l, r =3D [a.x, b.x].min, [a.x, b.x].max

b, t =3D [a.y, b.y].min, [a.y, b.y].max

moveto l, b

lineto r, b

lineto r, t

lineto l, t

closepath

end

page do

translate cx, cy

i =3D 0

coords.each_pair do |a, b|

path do

box a, b

gsave do

setgray Shade.mod_fetch(i +=3D 1)

fill

end

stroke

end

end

setrgbcolor 0.8, 0.4, 0

path do

moveto coords.first

angle =3D 180

coords.each_pair do |a, b|

d =3D (a + b) * 0.5

d +=3D (a - d).rot90

arcn d, (d - a).len, angle, (angle -=3D 90)

end

stroke

end

end

end

puts doc