Hi all,

recently I stumbled (once more) over a posting by our friend
Daniel Ajoy about the superformula (I think it was in a Logo-Forum):

http://en.wikipedia.org/wiki/Superformula

On the other side Kirby wrote interesting suggestions
about using generators.

Now, weekend, bad weather, some sparetime, I
assembled an implementation of a superformula-viewer
using pygame. It runs with Python 2.6 or higher, also
3.x, of course

You'll find it here for download:

http://dl.dropbox.com/u/2016850/superformula.py

or appended (which sometimes / for some of you)
doesn't work well depending on the browser or whatever.

The script uses generators for generating the pointlists
of the graph of the superformula and also for generating
colors for the segments of the graph-area.

Morover I've prepared a q&d slider class, which possibly
doesn't use the canonical way of processing events in
pygame but works fine for this application.

Critical comments and feedback and also questions, of course,
are welcome.

Perhaps someone is willing to amend the docstrings,
which suffer from my clumsy English and could well
be more clear.

Amendments of the code, espercially ones, which make it
more readable and easier to understand, also.

Useful for classroom use? Perhaps to difficult?

Best regards,
Gregor


# Autor: Gregor Lingl
# Superformula
# http://en.wikipedia.org/wiki/Superformula
# Version: 1.0  for Python 2.6 or higher; for 3.x also (!)
# Date: 2011-05-21

from math import sin, cos, pi
import sys
import os
import pygame
import random

os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()

SCREEN_SIZE = (SX, SY) = (540, 750)
CX, CY = 270, 270
R = 250

# colours
RED = (255, 0, 0)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
BLACK = (0, 0, 0)

# more ressources
CLOCK = pygame.time.Clock()
SMALLFONT = pygame.font.SysFont("Lucida Console", 12)
SMALLFONT.set_bold(True)
DEMO_TITLE = "SUPER-FORULA"

class Slider:
    """Sliders of this class change values in the mutable value_list argument.
       If fl=True, the slider sets floatingpoint-values, otherweise integer 
values.
    """
    def __init__(self, surf, left, top,
                 name, minval, maxval, value_list, defaultval = None, fl=True):
        """Slider-Position: (left, top) on surf
           name is used to label the Slider,
           Determine from the length of value_list the index of the
           values the slider sets, and append a default-value to value_list.
        """
        self.surface = surf
        self.name = name
        self.minval = minval
        self.maxval = maxval
        self.values = value_list
        self.index = len(value_list)
        self.fl = fl
        if defaultval is None:
            self.val = (minval+maxval) / 2
        else:
            self.val = defaultval
        if not fl:
            self.val = int(round(self.val)) # int() for compatibility with 2.7
        value_list.append(self.val)
        self.image = pygame.Surface((240, 50))
        self.left = left
        self.top = top
        self.marker = int(round(20 + (self.val - minval) * 200 / (maxval - 
minval)))
    def value(self):
        """Calculate value from marker position.
        """
        v = self.minval + (self.marker - 20) * (self.maxval - self.minval) / 200
        return v if self.fl else int(round(v))
    def draw(self):
        """Draw the slider, its label and its value to self.surface.
        """
        self.image.fill((32, 32, 32))
        pygame.draw.rect(self.image, YELLOW, (0, 14, 239, 35), 2)
        pygame.draw.line(self.image, GREEN, (20, 32), (220,32), 3)
        pygame.draw.circle(self.image, RED, (self.marker, 32), 12)
        val = (": {0:3.1f}" if self.fl else ": {0}").format(self.value())
        text = SMALLFONT.render(self.name + val, True, BLACK, YELLOW)
        w, h = text.get_size()
        w += 12
        h += 5
        ts = pygame.Surface((w,h))
        ts.fill(YELLOW)
        ts.blit(text, (6, 3))
        self.image.blit(ts, (10, 0))
        self.surface.blit(self.image, (self.left, self.top))
    def process(self, event):
        """If mouse is on slider-marker, left mouse-button is pressed
        and mouse is moved, change marker-position and value in value_list
        accordingly.
        """
        if event.type != pygame.MOUSEMOTION or event.buttons[0] != 1:
            return
        x, y = event.pos
        x -= self.left
        y -= self.top
        if (x - self.marker)**2 + (y - 32)**2 < 12**2:
            self.marker = min(max(20, x), 220)
            self.values[self.index] = self.value()
        
def superformula(params):
    """Calculate graph (list of paris of coordinates) for superformula from
    http://en.wikipedia.org/wiki/Superformula
    M, N1, N2, N3 are used according to that source.
    The graph may be rotated by changing the value of angle_offset.
    params is the value_list, whose values are changed by the sliders.
    params = [M, N1, N2, N3, Change_of_angle_offset, colour-rotation(here 
unused)]
    """
    M = N1 = N2 = N3 = None
    angles = [i * pi/255 for i in range(511)]
    angle_offset = 0.0
    while True:
        if M != params[0]:
            m = params[0]
            trigfuntable = [(w, abs(sin(m*w/4)), abs(cos(m*w/4))) for w in 
angles]
        if [M, N1, N2, N3] != params[:4]:
            M, N1, N2, N3 = params[:4]
            e1 = -1/N1
            points = []
            for (w, s4, c4) in trigfuntable:
                r = (c4**N2 + s4**N3) ** e1
                points.append((r, w))
            rmax = max(r for (r, w) in points)
            points = [(R*r/rmax, w) for (r,w) in points]
        pointlist = []
        angle_offset += params[4] * pi / 360
        for (r, w) in points:
            angle = w + angle_offset
            point = (CX + r*cos(angle), CY - r*sin(angle))
            pointlist.append(point)
        yield zip(pointlist, pointlist[1:])
    
def colours():
    """Generator that generates colour-triples starting from
    some startindex that can be sent to the generator.
    """
    cl1 = [(abs(i), 32, 255 - abs(i)) for i in range(-255, 255)]
    cl2 = [(3*r//4, 0, 3*b//4) for (r, g, b) in cl1]
    cl = list(zip(cl1, cl2))
    i = startindex = 0
    while True:
        dcc = yield cl[i]
        if dcc is not None:
            i = startindex = (startindex + dcc) % 510
        else:
            i = (i + 1) % 510


def demo(screen):
    """ Show and rotate the superformula-shape.
    Rotate colour shading with reference to the shape.
    Control via a set of four (form) + two (rotation) sliders.
    """
    # Startwerte: 
    M, N1, N2, N3, dWO, dCC = 3, 2., 8., 3., 0, 8 
    values = [] 
    
    sliders = [Slider(screen, 30, 550, "M", 0, 20, values, M, False),
               Slider(screen, 280, 550, "N1", 0.1, 20, values, N1, True),
               Slider(screen, 30, 610, "N2", 0, 20, values, N2, True),
               Slider(screen, 280, 610, "N3", 0, 20, values, N3, True),
               Slider(screen, 30, 690, "angle velocity", -10, 10, values, dWO, 
True),
               Slider(screen, 280, 690, "colour rotation", -10, 10, values, 
dCC, False)
              ]

    # Schleife für die Erkennung des QUIT-Ereignisses
    sf_gen = superformula(values)
    colour_gen = colours()
    while True:
        screen.fill(BLACK)
        pygame.draw.line(screen, (0, 128, 0), (10,675), (530,675), 3)
        for slider in sliders:
            slider.draw()
        for (p1, p2) in next(sf_gen): # segments:
            f1, f2 = next(colour_gen)
            pygame.draw.polygon(screen, f2, (p1, p2, (CX, CY)))
            pygame.draw.line(screen, f1, p1, p2, 7)
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                print(CLOCK.get_fps())
                sys.exit()
            for slider in sliders:
                slider.process(event)
        colour_gen.send(values[5]) # change start-index for colours
        CLOCK.tick()   

if __name__ == "__main__":
    pygame.display.set_caption(DEMO_TITLE)
    screen = pygame.display.set_mode(SCREEN_SIZE)
    try:
        demo(screen)
    except SystemExit:
        pass
    finally:
        pygame.quit()
_______________________________________________
Edu-sig mailing list
Edu-sig@python.org
http://mail.python.org/mailman/listinfo/edu-sig

Reply via email to