#!/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 from array import array import wx from wx.lib.pubsub import setuparg1 from wx.lib.pubsub import pub class Canvas: def __init__(self, width, height, internal_radius, scale=10): self.width = width self.height = height self.scale = scale self.internal_radius = float(internal_radius * scale) self.external_radius = float(height + internal_radius) * scale self.radius = float(height * scale) self.ring_width = self.radius / height self.sector_width = 360. / width self.Reset() def Reset(self): self.pixels_array = array('B', [0] * self.width * self.height * 3) self.last_pixel = None def toCartesian(self, r, theta): x = int(theta / self.sector_width) y = int(r / self.ring_width) - int(self.internal_radius / self.scale) return (x, y) def toPolar(self, x, y): r = y * self.ring_width + self.internal_radius theta = x * self.sector_width return (r, theta) def setPixelColor(self, r, theta, color): if r < self.internal_radius or r > self.external_radius: print "invalid coordinates (r: %f)" % r return x, y = self.toCartesian(r, theta) self.last_pixel = (x, y) offset = y * self.width * 3 + x * 3 self.pixels_array[offset + 0] = color[0] self.pixels_array[offset + 1] = color[1] self.pixels_array[offset + 2] = color[2] # now tell anyone who cares that the value has been changed pub.sendMessage("NEW PIXEL", self.last_pixel) def getPixelColor(self, x, y): offset = y * self.width * 3 + x * 3 color = [self.pixels_array[offset + i] for i in range(0, 3)] return tuple(color) def getColors(self): colors = set() for x in range(self.width): for y in range(self.height): colors.add(self.getPixelColor(x, y)) return list(colors) # The code below has been copied from the C implementation in PatternPaint: # https://github.com/Blinkinlabs/PatternPaint def saveAsAnimation(self, filename): output_file = open(filename, "w") colors = self.getColors() output_file.write("const PROGMEM prog_uint8_t animationData[] = {\n") output_file.write("// Length of the color table - 1, in bytes. length: 1 byte\n") output_file.write(" %d,\n" % (len(colors) - 1)) output_file.write("// Color table section. Each entry is 3 bytes. length: %d bytes\n" % (len(colors) * 3)) color_map = {} for i, c in enumerate(colors): output_file.write(" %3d, %3d, %3d,\n" % (c[0], c[1], c[2])) color_map[c] = i output_file.write("// Pixel runs section. Each pixel run is 2 bytes. length: -1 bytes\n") for x in range(self.width): run_count = 0 for y in range(self.height): new_color = color_map[self.getPixelColor(x, y)] if run_count == 0: current_color = new_color if current_color != new_color: output_file.write(" %3d, %3d,\n" % (run_count, current_color)) run_count = 1 current_color = new_color else: run_count += 1 output_file.write(" %3d, %3d,\n" % (run_count, current_color)) output_file.write("};\n\n") output_file.write("#define NUM_FRAMES %d\n" % self.width) output_file.write("#define NUM_LEDS %d\n" % self.height) output_file.write("Animation animation(NUM_FRAMES, animationData, NUM_LEDS);\n") output_file.close()