#!/usr/bin/env python # # Copyright (C) 2014 Antonio Ospite # # 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 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import sys import cairo from math import * def cartesian2polar(x, y, offset_angle=0): """return the polar coordinates relative to a circle centered in (0,0) going counterclockwise. returned angle is in degrees """ r = sqrt(x*x + y*y) theta = atan2(float(y), float(x)) + offset_angle # report theta in the [0,360) range theta = degrees(theta) if theta < 0: theta = 360 + theta return r, theta def polar2cartesian(r, theta, offset_angle=0): """ theta expected in degrees """ theta = radians(theta) - offset_angle x = r * cos(theta) y = r * sin(theta) return x, y class Diagram(object): def __init__(self, filename, width, height): self.filename = filename self.width = width self.height = height self.surface = cairo.SVGSurface(filename + '.svg', width, height, units="px") cr = self.cr = cairo.Context(self.surface) cr.set_line_width(1) # draw background cr.rectangle(0, 0, width, height) cr.set_source_rgb(1, 1, 1) cr.fill() # center the origin cr.translate(width/2., height/2.) def save(self): self.surface.write_to_png(self.filename + '.png') self.show() def show(self): import Image import StringIO f = StringIO.StringIO() self.surface.write_to_png(f) f.seek(0) im = Image.open(f) im.show() def draw_line(self, x1, y1, x2, y2, stroke_color=[0, 0, 0, 1]): cr = self.cr cr.move_to(x1, y1) cr.line_to(x2, y2) rf, gf, bf, alpha= stroke_color cr.set_source_rgba(rf, gf, bf, alpha) cr.stroke() def draw_circle(self, cx, cy, size=10.0, stroke_color=[0, 0, 0, 1]): cr = self.cr cr.arc(cx, cy, size, 0, 2*pi) rf, gf, bf, alpha = stroke_color cr.set_source_rgba(rf, gf, bf, alpha) cr.stroke() class PolarGridDiagram(Diagram): def __init__(self, filename, sectors, rings, internal_radius, scale): width = (map_height + internal_radius) * 2 * scale height = width Diagram.__init__(self, filename, width, height) self.rings = rings self.sectors = sectors self.internal_radius = internal_radius * scale self.external_radius = width / 2. self.radius = self.external_radius - self.internal_radius def draw_samples(self, stroke_color=[0, 0, 0, 0.5]): for i in range(0, self.rings): for j in range(0, self.sectors): r = self.radius / self.rings * (i + 0.5) + self.internal_radius theta = (360. / self.sectors) * (j + 0.5) x, y = polar2cartesian(r, theta, -pi/2.) self.draw_circle(x, y, 1, stroke_color) def draw_grid(self, stroke_color=[0, 0, 0, 0.5]): line_width = self.cr.get_line_width() for i in range(0, self.rings): self.draw_circle(0, 0, self.radius / self.rings * i + self.internal_radius, stroke_color) # outmost circle self.draw_circle(0, 0, self.external_radius - line_width, stroke_color) min_r = self.internal_radius max_r = self.external_radius - line_width for i in range(0, self.sectors): theta = (360. / self.sectors) * i x1, y1 = polar2cartesian(min_r, theta, -pi/2.) x2, y2 = polar2cartesian(max_r, theta, -pi/2.) self.draw_line(x1, y1, x2, y2, stroke_color) def draw_image(self, image): cr = self.cr img = cairo.ImageSurface.create_from_png(image) cr.set_source_surface(img, -self.external_radius, -self.external_radius) cr.paint() def draw(self, bg): if bg: self.draw_image(bg) self.draw_grid() self.draw_samples() if __name__ == '__main__': if len(sys.argv) > 1: bg = sys.argv[1] else: bg = None map_width = 101 map_height = 30 internal_radius = 2 scale = 10 grid = PolarGridDiagram('polar_grid', map_width, map_height, internal_radius, scale) grid.draw(bg) grid.show()