Last active
August 2, 2022 15:43
-
-
Save PennRobotics/534a5b3338cc82c8c03bfa8795253779 to your computer and use it in GitHub Desktop.
Remake of baoilleach/bresenham.py for Python 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
DEBUG = print if 0 else lambda *_:() | |
from math import cos, sin, pi | |
ASPECT = 15./10. | |
def bresenham_line(start, end): | |
symbols = getsymbols(start, end) | |
(x1, y1), (x2, y2) = start, end | |
steep = False | |
coords = [] | |
dx = abs(x2 - x1) | |
dy = abs(y2 - y1) | |
sx = 1 if x2 > x1 else -1 | |
sy = 1 if y2 > y1 else -1 | |
if dy > dx: | |
steep = True | |
x1,y1 = y1,x1 | |
dx,dy = dy,dx | |
sx,sy = sy,sx | |
d = (2 * dy) - dx | |
syms = [] | |
delta = 0 | |
for i in range(dx): | |
syms.append(symbols[d>=0]) | |
delta = 0 | |
if syms[-1] in "/\\": | |
delta = getdelta(start,end) | |
if steep: | |
coords.append((y1,x1 + delta)) | |
else: | |
coords.append((x1,y1 + delta)) | |
while d >= 0: | |
y1 = y1 + sy | |
d = d - (2 * dx) | |
x1 = x1 + sx | |
d = d + (2 * dy) | |
syms.append(symbols[d>=0]) | |
delta = 0 | |
if syms[-1] in "/\\": | |
delta = getdelta(start,end) | |
coords.append((x2,y2 + delta)) | |
if len(coords) > 1: | |
if syms[0] == "|": | |
if coords[0][1] < coords[1][1]: | |
syms, coords = syms[1:], coords[1:] | |
if syms[-1] == "|": | |
DEBUG("HAPPENED!") | |
if coords[-1][1] < coords[-2][1]: | |
syms, coords = syms[:-1], coords[:-1] | |
return coords, syms | |
class Screen(object): | |
def __init__(self, width, height): | |
self.w = width | |
self.h = height | |
self.buf = [[" "]*self.w for x in range(self.h)] | |
def __str__(self): | |
ans=["-----START"] | |
for x in self.buf: | |
ans.append("".join(x)) | |
ans.append("-----END") | |
return "\n".join(ans) | |
def line(self, start, end): | |
coords, syms = bresenham_line( start, end ) | |
DEBUG(coords, syms) | |
for (a, b), sym in zip(coords, syms): | |
if 0<=b<self.h and 0<=a<self.w: | |
self.buf[b][a] = sym | |
def text(self, text, pos): | |
start = pos[0] | |
for i, x in enumerate(text): | |
if 0 <= pos[1] < self.h and 0 <= start+i < self.w: | |
self.buf[pos[1]][start + i] = x | |
def getsymbols(start ,end): | |
""" | |
>>> getsymbols((9, 4), (19, 5)) | |
['_', '\\\\'] | |
>>> getsymbols((9, 4), (0, 3)) | |
['_', '\\\\'] | |
>>> getsymbols((9, 4), (15, 8)) | |
['_', '\\\\'] | |
>>> getsymbols((9, 4), (7, 9)) | |
['|', '/'] | |
>>> getsymbols((9, 4), (3, 0)) | |
['_', '\\\\'] | |
>>> getsymbols((9, 4), (1, 7)) | |
['_', '/'] | |
>>> getsymbols((9, 4), (12, 9)) | |
['|', '\\\\'] | |
""" | |
if end[0] < start[0]: | |
start, end = end, start | |
if end[0] == start[0]: | |
slope = 1e99 | |
else: | |
slope = (end[1] - start[1]) / float((end[0] - start[0])) | |
if slope > 0: | |
if slope > 1.0: | |
ans = ["|", '\\'] | |
else: | |
ans = ["_", '\\'] | |
else: | |
if slope > -1.0: | |
ans = ["_", "/"] | |
else: | |
ans = ["|", "/"] | |
return ans | |
def getdelta(start, end): | |
""" | |
>>> getdelta((9, 4), (18, 7)) | |
1 | |
>>> getdelta((9, 4), (12, 9)) | |
0 | |
>>> getdelta((9, 4), (7, 9)) | |
0 | |
""" | |
return end[1] > start[1] and abs((end[1] - start[1]) / (end[0] - start[0])) <= 1.0 | |
def mytest(): | |
screen = Screen(10,10) | |
screen.buf[0] = list("0123456789") | |
for i in range(10): | |
screen.buf[i][0] = str(i) | |
screen.line( (2,3), (3,5)) | |
print(screen) | |
DEBUG(getsymbols((25,14), (17,11))) | |
if __name__ == "__main__": | |
mytest() | |
star = True | |
square = True | |
normal = True | |
if star: | |
w, h = 170 ,48 | |
w, h = 78, 48 | |
screen = Screen(w, h) | |
start = 0 | |
x, y = round(w/2.)-1, round(h/2.)-1 | |
oldx, oldy = w-1, y | |
for ang in range(start, 360 + start + 10, 10): | |
x2 = round(x + w/2*cos(ang*pi/180)) | |
y2 = round(y + h/2*sin(ang*pi/180)) | |
screen.line((x, y), (x2, y2)) | |
screen.line((oldx, oldy), (x2, y2)) | |
oldx,oldy=x2,y2 | |
print(screen) | |
if square: | |
w = 30 | |
h = round(w/ASPECT) | |
screen = Screen(w, h) | |
points = [(0, 0), (w-1, 0), (w-1, h-1), (0, h-1), (0, 0)] | |
for src, dest in zip(points, points[1:]): | |
screen.line(src, dest) | |
start = 30 | |
x, y = round(w/2.)-1, round(h/2.)-1 | |
for i, size in enumerate([w/2., w/2. - 4]): | |
x2, y2 = round(x+size*cos(start*pi/180.)),round(y+(size/ASPECT)*sin(start*pi/180.)) | |
oldx, oldy = x2, y2 | |
for ang in range(start, 360 + start + 60, 60): | |
x2, y2 = round(x+size*cos(ang*pi/180.)),round(y+(size/ASPECT)*sin(ang*pi/180.)) | |
if i==0 or (i==1 and (ang/60)%2==0 ): | |
screen.line((oldx,oldy),(x2,y2)) | |
oldx,oldy=x2,y2 | |
print(screen) | |
if normal: | |
output = open('test.bbl','r').read() | |
DEBUG(output) | |
w, h = 76, 30 | |
scale = 0.3 | |
screen = Screen(w, h) | |
for line in output.split("\n"): | |
DEBUG(line) | |
fields = line.split() | |
if line.startswith("NewCanvas"): | |
vals = [float(x) for x in fields[1:3]] | |
cw, ch = vals[0], vals[1]/ASPECT | |
sx, sy = w/cw, h/ch | |
scale = min(sx, sy) | |
elif line.startswith("DrawLine"): | |
tmp = [fields[i] for i in [1,2,4,5]] | |
vals = [float(x)*scale for x in tmp] | |
screen.line((round(vals[0]), round(vals[1]/ASPECT)), | |
(round(vals[2]), round(vals[3]/ASPECT))) | |
elif line.startswith("DrawText"): | |
vals = [float(x)*scale for x in fields[1:3]] | |
screen.text(fields[3][1:-1], | |
(round(vals[0]), round(vals[1]/ASPECT))) | |
print(screen) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NewCanvas 149.3 140.0 | |
SetPenColor 0.0 0.0 0.0 1.0 (rgba) | |
DrawLine 109.3 100.0 to 74.6 80.0 | |
SetPenColor 0.0 0.0 0.0 1.0 (rgba) | |
DrawLine 71.6 80.0 to 71.6 53.0 | |
DrawLine 77.6 80.0 to 77.6 53.0 | |
SetPenColor 0.0 0.0 0.0 1.0 (rgba) | |
DrawLine 74.6 80.0 to 51.3 93.5 | |
SetPenColor 0.4 0.4 0.4 1.0 (rgba) | |
SetPenColor 0.4 0.4 0.4 1.0 (rgba) | |
SetPenColor 1.0 0.1 0.1 1.0 (rgba) | |
SetFontSize 16 | |
SetFontSize 16 | |
SetFontSize 16 | |
DrawText 74.6 40.0 "O" | |
SetPenColor 0.1 0.9 0.1 1.0 (rgba) | |
SetFontSize 16 | |
SetFontSize 16 | |
SetFontSize 16 | |
SetFontSize 16 | |
DrawText 40.0 100.0 "Cl" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There are still plenty of opportunities for simplification or condensing, such as removing unnecessary
float(...)
calls or combining lines, e.g.scale = min(w/cw, h/ch)
The top line sets whether debug info is printed.
DEBUG(...)
can contain any number of arguments which are then passed toprint
when this line has a true condition. If the condition is false, a lambda takes the arguments and does nothing with them. Presto change-o!