#!/usr/bin/env python
###################################################
# dotgenerator.py
#
# Copyright (C) 2002 Simon Budig.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.


import random, sys
from math import pi, sqrt, floor, sin, cos


###################################################
# Bildobjekte (laden)

class Image:
   def get_pixel (self, x, y):
      if x < 0 or y < 0 or x >= self.width or y >= self.height:
         return 0
      return self.pixels [y * self.rowstride + x]

   def subsample (self, x, y):
      x0 = int (floor (x))
      y0 = int (floor (y))
      dx = x - x0
      dy = y - y0
      return ( (1-dx)*(1-dy)*self.get_pixel (  x0,   y0) +
                  dx *(1-dy)*self.get_pixel (x0+1,   y0) +
               (1-dx)*   dy *self.get_pixel (  x0, y0+1) +
                  dx *   dy *self.get_pixel (x0+1, y0+1) )


class PGMImage(Image):
   def __init__ (self, filename):
      f = open (filename).readlines ()
      data = (' '.join ([i for i in f if i.strip()[0] != '#'])).split ()

      if data[0] != 'P2':
         raise "Not an Ascii-PGM-Image"

      self.width = int (data[1])
      self.height = int (data[2])
      self.rowstride = self.width
      depth = float (data[3])
      self.pixels = [1 - int(i)/depth for i in data[4:] ]


class GradientImage(Image):
   def __init__ (self, width, height):
      self.width = width
      self.height = height
      self.rowstride = self.width
      self.pixels = [1 - float(i)/(width-1) for i in range (width) ] * height




###################################################
# Bilder rausschreiben

class ImageWriter:
   def __init__ (self, filename = None):
      self.x0 = 20
      self.y0 = 822
      self.width = 555
      self.height = -802

      if filename:
         self.file = open ("filename", "w")
      else:
         self.file = sys.stdout


class PSWriter (ImageWriter):
   def header (self):
      self.file.write ("%!PS-Adobe-3.0\n"
                       "\n"
                       "0 setgray\n")

   def footer (self):
      self.file.write ("showpage\n")

   def rect (self, xc, yc, rx, ry, angle=45):
      angle = angle / 180.0 * pi
      self.file.write ("newpath\n"
                       "%f %f moveto\n"
                       "%f %f rlineto\n"
                       "%f %f rlineto\n"
                       "%f %f rlineto\n"
                       "closepath\n"
                       "fill\n" % (xc - rx * cos(angle) + ry * sin(angle),
                                   yc - rx * sin(angle) - ry * cos(angle),
                                   2 * rx * cos(angle),
                                   2 * rx * sin (angle),
                                   2 * -ry * sin(angle),
                                   2 * ry * cos (angle),
                                   - 2 * rx * cos(angle),
                                   - 2 * rx * sin (angle)))

   def ellipse (self, xc, yc, rx, ry, angle=45):
      self.file.write ("gsave\n"
                       "%f %f translate\n"
                       "%f rotate\n"
                       "%f %f scale\n"
                       "newpath\n"
                       "0 0 1 0 360 arc\n"
                       "closepath\n"
                       "fill\n"
                       "grestore\n" % (xc, yc, angle, rx, ry))


class SKWriter (ImageWriter):
   def header (self):
      self.file.write ("##Sketch 1 2\n"
                       "document()\n"
                       "layout('A4',0)\n"
                       "layer('Layer 1',1,1,0,0,(0,0,0))\n"
                       "guidelayer('Guide Lines',1,0,0,1,(0,0,1))\n"
                       "grid((0,0,20,20),0,(0,0,1),'Grid')\n"
                       "layer('Rasterlayer',1,1,0,0,(0,0,0))\n"
                       "G()\n" )

   def footer (self):
      self.file.write ("G_()\n")

   def rect (self, xc, yc, rx, ry, angle=45):
      angle = angle / 180.0 * pi
      self.file.write ("fp((0,0,0))\n"
                       "le()\n"
                       "lw(1)\n"
                       "r(%f,%f,%f,%f,%f,%f)\n" % (2 * rx * cos(angle),
                                                   2 * rx * sin (angle),
                                                   2 * -ry * sin(angle),
                                                   2 * ry * cos (angle),
                                                   xc - rx * cos(angle) + ry * sin(angle),
                                                   yc - rx * sin(angle) - ry * cos(angle)))

   def ellipse (self, xc, yc, rx, ry, angle=45):
      angle = angle / 180.0 * pi
      self.file.write ("fp((0,0,0))\n"
                       "le()\n"
                       "lw(1)\n"
                       "e(%f,%f,%f,%f,%f,%f)\n" % (rx * cos(angle),
                                                   rx * sin (angle),
                                                   -ry * sin(angle),
                                                   ry * cos (angle),
                                                   xc, yc))





###################################################
# verschiedene Punktanordnungen

class Pattern:
   def __init__ (self, image, writer, pointfunc, dotscale=1):
      self.image = image
      self.writer = writer
      self.pointfunc = pointfunc
      self.dotscale = dotscale
      self.points = []

      self.xratio = float (writer.width) / image.width
      self.yratio = float (writer.height) / image.height
      self.ratio = min (abs (self.xratio), abs (self.yratio))
      self.xratio = self.xratio / abs (self.xratio) * self.ratio
      self.yratio = self.yratio / abs (self.yratio) * self.ratio
      
      self.offx = writer.x0 + (writer.width  - image.width  * self.xratio) / 2.0
      self.offy = writer.y0 + (writer.height - image.height * self.yratio) / 2.0


   def make (self, width=100, x0=0, y0=0):
      if not self.points:
         self.getpoints ()
      
      self.writer.header ()
      for point in self.points:
         transpoint = ( self.offx + point[0] * self.xratio,
                        self.offy + point[1] * self.yratio,
                        point[2] )

         brightness = self.image.subsample (point[0], point[1])

         if brightness >= 0.02:
            self.pointfunc (transpoint[0], transpoint[1], transpoint[2],
                            brightness,
                            self.ratio / 2.0 * self.dotscale, self.writer)
      self.writer.footer ()


class RectGrid (Pattern):
   def getpoints (self):
      self.points = []
      
      for y in range (self.image.height):
         for x in range (self.image.width):
            self.points.append ((x, y, 0))
 
   
class HexGrid (Pattern):
   def getpoints (self):
      self.points = []
      y = 0
      count = 0
      while round (y, 1) <= self.image.height:
         x = (count % 2) * 0.5
         while round (x, 1) <= self.image.width:
            self.points.append ((x, y, 0))
            x += 1
         y += sqrt (0.75)
         count += 1
 

class CenterCircles (Pattern):
   def getpoints (self):
      self.points = []
      x = float (self.image.width) / 2
      y = float (self.image.height) / 2
      self.points.append ((x, y, 0))
      for r in range (1, 1 + int (sqrt (x*x + y*y))):
         for i in range (r*6):
            self.points.append ((x + r * sin (float (i) / (r * 6) * 2 * pi),
                                 y + r * cos (float (i) / (r * 6) * 2 * pi),
                                 float (i) / (r * 6) * 360))
 


###################################################
# verschiedene Punktformen

def round_dots (x, y, angle, brightness, radius, writer):
   writer.ellipse (x, y,
                   sqrt (brightness) * radius,
                   sqrt (brightness) * radius,
                   0)


def squares (x, y, angle, brightness, radius, writer):
   writer.rect    (x, y,
                   sqrt (brightness) * radius,
                   sqrt (brightness) * radius,
                   angle)


def rotating_squares (x, y, angle, brightness, radius, writer):
   writer.rect    (x, y,
                   brightness * radius * sqrt (2),
                   brightness * radius * sqrt (2),
                   angle + brightness * 90)


def random_ellipses (x, y, angle, brightness, radius, writer):
   writer.ellipse (x, y,
                   2 * radius - brightness**2 * radius * sqrt(2),
                   0.05 * radius + brightness**2 * radius * sqrt(2),
                   random.random () * 180)





########################################
# Hauptprogramm

if __name__ == '__main__':
   writer = PSWriter()
   image = PGMImage ("flower.pgm")
   # image = GradientImage (50,50)

   p = CenterCircles (image, writer, squares, 1.08)
   # p = HexGrid (image, writer, round_dots, 1 / cos (30.0 / 180 * pi))
   # p = RectGrid (image, writer, round_dots, sqrt (2))

   p.make ()