#!/usr/bin/env python
""" piasketch - The Pi-A-Sketch program draws a few circles
It is also a basic library for steppers controlling an X/Y device.
The main functions are line(x,y) and circle(radius).
"""
import RPi.GPIO as gpio
import time
PINS_A = [4, 25, 24, 23]
PINS_B = [22, 21, 18, 17]
PINS = PINS_A + PINS_B
SEQA = [(4, ), (4, 25), (25, ), (25, 24), (24, ), (24, 23), (23, ), (23, 4)]
RSEQA = SEQA[::-1][1:] + SEQA[::-1][:1]
SEQB = [(22, ), (22, 21), (21, ), (21, 18), (18, ), (18, 17), (17, ), (17, 22)]
RSEQB = SEQB[::-1][1:] + SEQB[::-1][:1]
CORRECTION = 22
def setallpins():
"""
Before we can use them, we need to set the pins for both motors
"""
gpio.setmode(gpio.BCM)
for pin in PINS:
gpio.setup(pin, gpio.OUT)
def stepper(sequence, pins, delay=0.002):
"""
One full step, based on an ordered sequence and corresponding pins
"""
for step in sequence:
for pin in pins:
gpio.output(pin, gpio.HIGH if pin in step else gpio.LOW)
time.sleep(delay)
def move(steps, axis="x"):
"""
Move a certain number of steps on a specific axis. sign for direction
"""
(seq, pins) = (SEQA, PINS_A) if axis == "x" else (SEQB, PINS_B)
if steps < 0:
steps = -steps
seq = RSEQA if axis == "x" else RSEQB
for _ in range(steps):
stepper(seq, pins)
def line(x1, y1):
"""
Bresenham's line algorithm from rosetta code (ADA/Python),
adapted to relative positioning. It will be drawn from current
position to x1,y1. Negative number goes in other direction.
"""
dx = abs(x1)
dy = abs(y1)
x, y = 0, 0
px, py = x, y
sx = -1 if 0 > x1 else 1
sy = -1 if 0 > y1 else 1
if dx > dy:
err = dx / 2.0
while x != x1:
if x - px is not 0:
move(x - px)
if y - py is not 0:
move(y - py, "y")
px, py = x, y
err -= dy
if err < 0:
y += sy
err += dx
x += sx
else:
err = dy / 2.0
while y != y1:
if x - px is not 0:
move(x - px)
if y - py is not 0:
move(y - py, "y")
px, py = x, y
err -= dx
if err < 0:
x += sx
err += dy
y += sy
if x - px is not 0:
move(x - px)
if y - py is not 0:
move(y - py, "y")
def _circlepoints(radius):
"""
mid point circle drawing algorithm - basically Bresenham's.
It was converted for use with relative. Cant use the bitmap
approach of the original algorithm. Points are sequential
clockwise. It works, but it is ugly. I only had a few
minutes to write this. It needs to be cleaned up.
"""
points = []
segment = []
for seg in range(8):
x0, y0 = 0, -radius
f = 1 - radius
ddf_x, ddf_y = 1, -2 * radius
x, y = 0, radius
segment = []
while x < y:
if seg == 0:
segment.append((x0 + x, y0 + y))
elif seg == 1:
segment.append((x0 + y, y0 + x))
elif seg == 2:
segment.append((x0 + y, y0 - x))
elif seg == 3:
segment.append((x0 + x, y0 - y))
elif seg == 4:
segment.append((x0 - x, y0 - y))
elif seg == 5:
segment.append((x0 - y, y0 - x))
elif seg == 6:
segment.append((x0 - y, y0 + x))
elif seg == 7:
segment.append((x0 - x, y0 + y))
if f >= 0:
y -= 1
ddf_y += 2
f += ddf_y
x += 1
ddf_x += 2
f += ddf_x
if seg % 2:
points.extend(segment[::-1])
else:
points.extend(segment)
return points
def circle(radius):
"""
Draw a circle of specified radius. It is not centered. It will start
drawing a circle at its current position, going right and down.
"""
points = _circlepoints(radius)
#print points
dx, dy = 1, 1
px, py = 0, 0
counter = 0
total = len(points)
quarter = total / 4
#print total, quarter
for (x, y) in points:
counter += 1
dx, dy = x - px, y - py
px, py = x, y
# print dx, dy
if dx is not 0:
move(dx)
if dy is not 0:
move(dy, "y")
if counter == quarter:
move(-CORRECTION)
elif counter == quarter * 2:
move(CORRECTION, "y")
elif counter == quarter * 3:
move(CORRECTION)
elif counter == total:
move(-CORRECTION, "y")
def main():
"""
As a standalone app, draw a few circles
"""
setallpins()
#line(-50, -200)
#line(5,0)
#circle(350)
for radius in range(100,400,25):
circle(radius)
gpio.cleanup()
if __name__ == "__main__":
main()