Initial import
[PoPiPaint.git] / tools / polar_grid.py
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2014  Antonio Ospite <ao2@ao2.it>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 import sys
18 import cairo
19 from math import *
20
21
22 def cartesian2polar(x, y, offset_angle=0):
23     """return the polar coordinates relative to a circle
24     centered in (0,0) going counterclockwise.
25
26     returned angle is in degrees
27     """
28     r = sqrt(x*x + y*y)
29     theta = atan2(float(y), float(x)) + offset_angle
30
31     # report theta in the [0,360) range
32     theta = degrees(theta)
33     if theta < 0:
34         theta = 360 + theta
35
36     return r, theta
37
38
39 def polar2cartesian(r, theta, offset_angle=0):
40     """
41     theta expected in degrees
42     """
43     theta = radians(theta) - offset_angle
44     x = r * cos(theta)
45     y = r * sin(theta)
46
47     return x, y
48
49
50 class Diagram(object):
51
52     def __init__(self, filename, width, height):
53         self.filename = filename
54         self.width = width
55         self.height = height
56
57         self.surface = cairo.SVGSurface(filename + '.svg', width, height,
58                                         units="px")
59         cr = self.cr = cairo.Context(self.surface)
60
61         cr.set_line_width(1)
62
63         # draw background
64         cr.rectangle(0, 0, width, height)
65         cr.set_source_rgb(1, 1, 1)
66         cr.fill()
67
68         # center the origin
69         cr.translate(width/2., height/2.)
70
71     def save(self):
72         self.surface.write_to_png(self.filename + '.png')
73         self.show()
74
75     def show(self):
76         import Image
77         import StringIO
78         f = StringIO.StringIO()
79         self.surface.write_to_png(f)
80         f.seek(0)
81         im = Image.open(f)
82         im.show()
83
84     def draw_line(self, x1, y1, x2, y2, stroke_color=[0, 0, 0, 1]):
85         cr = self.cr
86
87         cr.move_to(x1, y1)
88         cr.line_to(x2, y2)
89
90         rf, gf, bf, alpha= stroke_color
91         cr.set_source_rgba(rf, gf, bf, alpha)
92         cr.stroke()
93
94     def draw_circle(self, cx, cy, size=10.0, stroke_color=[0, 0, 0, 1]):
95         cr = self.cr
96
97         cr.arc(cx, cy, size, 0, 2*pi)
98
99         rf, gf, bf, alpha = stroke_color
100         cr.set_source_rgba(rf, gf, bf, alpha)
101         cr.stroke()
102
103
104 class PolarGridDiagram(Diagram):
105     def __init__(self, filename, sectors, rings, internal_radius, scale):
106         width = (map_height + internal_radius) * 2 * scale
107         height = width
108         Diagram.__init__(self, filename, width, height)
109
110         self.rings = rings
111         self.sectors = sectors
112
113         self.internal_radius = internal_radius * scale
114         self.external_radius = width / 2.
115         self.radius = self.external_radius - self.internal_radius
116
117     def draw_samples(self, stroke_color=[0, 0, 0, 0.5]):
118         for i in range(0, self.rings):
119             for j in range(0, self.sectors):
120                 r = self.radius / self.rings * (i + 0.5) + self.internal_radius
121                 theta = (360. / self.sectors) * (j + 0.5)
122                 x, y = polar2cartesian(r, theta, -pi/2.)
123                 self.draw_circle(x, y, 1, stroke_color)
124
125     def draw_grid(self, stroke_color=[0, 0, 0, 0.5]):
126         line_width = self.cr.get_line_width()
127
128         for i in range(0, self.rings):
129             self.draw_circle(0, 0, self.radius / self.rings * i + self.internal_radius, stroke_color)
130
131         # outmost circle
132         self.draw_circle(0, 0, self.external_radius - line_width, stroke_color)
133
134         min_r = self.internal_radius
135         max_r = self.external_radius - line_width
136         for i in range(0, self.sectors):
137             theta = (360. / self.sectors) * i
138             x1, y1 = polar2cartesian(min_r, theta, -pi/2.)
139             x2, y2 = polar2cartesian(max_r, theta, -pi/2.)
140             self.draw_line(x1, y1, x2, y2, stroke_color)
141
142     def draw_image(self, image):
143         cr = self.cr
144         img = cairo.ImageSurface.create_from_png(image)
145         cr.set_source_surface(img, -self.external_radius, -self.external_radius)
146         cr.paint()
147
148     def draw(self, bg):
149         if bg:
150             self.draw_image(bg)
151
152         self.draw_grid()
153         self.draw_samples()
154
155 if __name__ == '__main__':
156
157     if len(sys.argv) > 1:
158         bg = sys.argv[1]
159     else:
160         bg = None
161
162     map_width = 101
163     map_height = 30
164     internal_radius = 2
165     scale = 10
166
167     grid = PolarGridDiagram('polar_grid', map_width, map_height, internal_radius, scale)
168     grid.draw(bg)
169     grid.show()