Created
September 4, 2023 04:56
-
-
Save rene-d/df0be5d7613acb08ace1861ef5e959ae to your computer and use it in GitHub Desktop.
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
#!/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