Skip to content

Instantly share code, notes, and snippets.

@rene-d
Created September 4, 2023 04:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rene-d/df0be5d7613acb08ace1861ef5e959ae to your computer and use it in GitHub Desktop.
Save rene-d/df0be5d7613acb08ace1861ef5e959ae to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
from curses import wrapper
from math import pi, sin, cos
def bresenham(x0, y0, x1, y1):
"""
Bresenham's line algorithm.
Yield integer coordinates on the line from (x0, y0) to (x1, y1).
Input coordinates should be integers.
The result will contain both the start and the end point.
"""
dx = x1 - x0
dy = y1 - y0
xsign = 1 if dx > 0 else -1
ysign = 1 if dy > 0 else -1
dx = abs(dx)
dy = abs(dy)
if dx > dy:
xx, xy, yx, yy = xsign, 0, 0, ysign
else:
dx, dy = dy, dx
xx, xy, yx, yy = 0, ysign, xsign, 0
D = 2 * dy - dx
y = 0
for x in range(dx + 1):
yield x0 + x * xx + y * yx, y0 + x * xy + y * yy
if D >= 0:
y += 1
D -= 2 * dx
D += 2 * dy
def is_inside_postgis(polygon, point):
"""
Determine if a point is inside a given polygon or not.
"""
length = len(polygon)
intersections = 0
dx2 = point[0] - polygon[0][0]
dy2 = point[1] - polygon[0][1]
ii = 0
jj = 1
while jj < length:
dx = dx2
dy = dy2
dx2 = point[0] - polygon[jj][0]
dy2 = point[1] - polygon[jj][1]
F = (dx - dx2) * dy - dx * (dy - dy2)
if 0.0 == F and dx * dx2 <= 0 and dy * dy2 <= 0:
return 2
if (dy >= 0 and dy2 < 0) or (dy2 >= 0 and dy < 0):
if F > 0:
intersections += 1
elif F < 0:
intersections -= 1
ii = jj
jj += 1
return intersections != 0
def envelope(polygon):
"""
Return the envelope of a polygon.
"""
xmin = min(polygon, key=lambda p: p[0])[0]
xmax = max(polygon, key=lambda p: p[0])[0]
ymin = min(polygon, key=lambda p: p[1])[1]
ymax = max(polygon, key=lambda p: p[1])[1]
return xmin, ymin, xmax, ymax
def fill_polygon(polygon, contour=False):
"""
Fill a polygon.
Yield integer coordinates inside the polygon.
"""
xmin, ymin, xmax, ymax = envelope(polygon)
seen = set()
if contour:
for p in zip(polygon, polygon[1:]):
x1, y1, x2, y2 = p[0][0], p[0][1], p[1][0], p[1][1]
for x, y in bresenham(x1, y1, x2, y2):
if (x, y) not in seen:
yield x, y
seen.add((x, y))
for x in range(xmin, xmax + 1):
for y in range(ymin, ymax + 1):
if is_inside_postgis(polygon, (x, y)):
if (x, y) not in seen:
yield x, y
def setpixel(stdscr, x, y, c):
stdscr.addstr(y, x, c)
def demo1(stdscr):
polygon = [(5, 5), (10, 20), (50, 20), (70, 10), (50, 5), (20, 10), (5, 5)]
def sommets():
for x, y in polygon:
setpixel(stdscr, x, y, "+")
def contour():
for p in zip(polygon, polygon[1:]):
x1, y1, x2, y2 = p[0][0], p[0][1], p[1][0], p[1][1]
for x, y in bresenham(x1, y1, x2, y2):
setpixel(stdscr, x, y, "x")
def remplissage():
for x, y in fill_polygon(polygon, False):
setpixel(stdscr, x, y, "o")
def remplissage_contour():
for x, y in fill_polygon(polygon, True):
setpixel(stdscr, x, y, "o")
def enveloppe():
xmin, ymin, xmax, ymax = envelope(polygon)
for x, y in bresenham(xmin, ymin, xmax, ymin):
setpixel(stdscr, x, y, ".")
for x, y in bresenham(xmax, ymin, xmax, ymax):
setpixel(stdscr, x, y, ".")
for x, y in bresenham(xmax, ymax, xmin, ymax):
setpixel(stdscr, x, y, ".")
for x, y in bresenham(xmin, ymax, xmin, ymin):
setpixel(stdscr, x, y, ".")
def demo(titre, *actions):
stdscr.clear()
for action in actions:
action()
setpixel(stdscr, 0, 0, titre)
stdscr.refresh()
stdscr.getkey()
demo("contour", contour, sommets)
demo("remplissage", remplissage, sommets)
demo("remplissage et contour", remplissage, contour, sommets)
demo("remplissage avec contour et enveloppe", remplissage_contour, enveloppe, sommets)
def demo2(stdscr):
a = 10
b = 5
xc = a + 1
yc = b + 1
for i in range(0, 360 * 2, 6):
stdscr.clear()
x0 = xc + a
y0 = yc
for theta in range(0, 360 + 10, 10):
x1 = round(xc + a * cos(theta * pi / 180))
y1 = round(yc + b * sin(theta * pi / 180))
for x, y in bresenham(x0, y0, x1, y1):
setpixel(stdscr, x, y, "X")
x0 = x1
y0 = y1
for x, y in bresenham(
xc,
yc,
round(xc + (a - 1) * cos(i * pi / 180)),
round(yc + (b - 1) * sin(i * pi / 180)),
):
setpixel(stdscr, x, y, "o")
setpixel(stdscr, 0, 0, " ")
stdscr.refresh()
stdscr.timeout(20)
try:
stdscr.getkey()
break
except Exception: # nosec B110
pass
stdscr.timeout(-1)
stdscr.getkey()
if __name__ == "__main__":
wrapper(demo1)
wrapper(demo2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment