Last active
March 14, 2022 16:53
-
-
Save PierceLBrooks/4c3f6872679c498a1c6280f597c90450 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| # Author: Pierce Brooks | |
| # https://gist.github.com/PierceLBrooks/d180e8445e3c59fe757e99c932257e9e | |
| import os | |
| import sys | |
| import copy | |
| import math | |
| import cmath | |
| import random | |
| import logging | |
| import functools | |
| import traceback | |
| import numpy as np | |
| import pygame as pg | |
| import pscreen as ps | |
| from datetime import datetime | |
| from scipy.spatial.transform import Rotation as R | |
| def segment(p, q, r): | |
| if ((q[0] <= max(p[0], r[0])) and (q[0] >= min(p[0], r[0])) and (q[1] <= max(p[1], r[1])) and (q[1] >= min(p[1], r[1]))): | |
| return True | |
| return False | |
| def orientation(p, q, r): | |
| value = (((q[1]-p[1])*(r[0]-q[0]))-((q[0]-p[0])*(r[1]-q[1]))) | |
| if (value == 0): | |
| return 0 | |
| if (value > 0): | |
| return 1 | |
| return 2 | |
| def intersect(p1, q1, p2, q2): | |
| o1 = orientation(p1, q1, p2) | |
| o2 = orientation(p1, q1, q2) | |
| o3 = orientation(p2, q2, p1) | |
| o4 = orientation(p2, q2, q1) | |
| if ((o1 != o2) and (o3 != o4)): | |
| return True | |
| if ((o1 == 0) and (segment(p1, p2, q1))): | |
| return True | |
| if ((o2 == 0) and (segment(p1, q2, q1))): | |
| return True | |
| if ((o3 == 0) and (segment(p2, p1, q2))): | |
| return True | |
| if ((o4 == 0) and (segment(p2, q1, q2))): | |
| return True | |
| return False | |
| def contain(points, p): | |
| n = len(points) | |
| if (n < 3): | |
| return False | |
| extreme = (10000, p[1]) | |
| count = 0 | |
| i = 0 | |
| while (True): | |
| next = (i+1)%n | |
| if (intersect(points[i], points[next], p, extreme)): | |
| if (orientation(points[i], p, points[next]) == 0): | |
| return segment(points[i], p, points[next]) | |
| count += 1 | |
| i = next | |
| if (i == 0): | |
| break | |
| if (count % 2 == 1): | |
| return True | |
| return False | |
| def dot(left, right): | |
| return (left[0]*right[0])+(left[1]*right[1]) | |
| def distance(left, right): | |
| return (((left[0]-right[0])**2.0)+((left[1]-right[1])**2.0))**0.5 | |
| def direction(left, right): | |
| return math.atan2(right[1]-left[1], right[0]-left[0]) | |
| def magnitude(vector): | |
| result = (vector[0]**2.0)+(vector[1]**2.0) | |
| return result**0.5 | |
| def normalize(vector): | |
| temp = magnitude(vector) | |
| return tuple([vector[0]/temp, vector[1]/temp]) | |
| def determinant(a, b): | |
| return (a[0]*b[1])-(a[1]*b[0]) | |
| def intersection(a, b): | |
| if not (intersect(a[0], a[1], b[0], b[1])): | |
| return None | |
| xdiff = (a[0][0]-a[1][0], b[0][0]-b[1][0]) | |
| ydiff = (a[0][1]-a[1][1], b[0][1]-b[1][1]) | |
| div = determinant(xdiff, ydiff) | |
| if (div == 0): | |
| return None | |
| d = (determinant(a[0], a[1]), determinant(b[0], b[1])) | |
| x = determinant(d, xdiff)/div | |
| y = determinant(d, ydiff)/div | |
| return (x, y) | |
| def trisides(a, b, c): | |
| sides = [] | |
| sides.append(distance(b, c)) | |
| sides.append(distance(c, a)) | |
| sides.append(distance(a, b)) | |
| return sides | |
| def triangles(sides): | |
| angles = [0.0, 0.0, 0.0] | |
| if (len(sides) < 3): | |
| return angles | |
| ab = sides[0] | |
| bc = sides[1] | |
| ca = sides[2] | |
| check = None | |
| index = 0 | |
| temp = 0.0 | |
| total = 180.0 | |
| king = 0 | |
| major = 1 | |
| minor = 2 | |
| if (ab > bc): | |
| if (ab > ca): | |
| index = 0 | |
| king = 0 | |
| if (bc > ca): | |
| check = True | |
| major = 1 | |
| minor = 2 | |
| else: | |
| check = False | |
| major = 2 | |
| minor = 1 | |
| else: | |
| check = True | |
| index = 2 | |
| king = 2 | |
| major = 0 | |
| minor = 1 | |
| else: | |
| if (bc > ca): | |
| index = 1 | |
| king = 0 | |
| if (ab > ca): | |
| check = True | |
| major = 0 | |
| minor = 2 | |
| else: | |
| char = False | |
| major = 2 | |
| minor = 0 | |
| else: | |
| check = False | |
| index = 2 | |
| king = 2 | |
| major = 1 | |
| minor = 0 | |
| temp = ((sides[major])**2.0)+((sides[minor])**2.0) | |
| temp -= (sides[king])**2.0 | |
| temp /= sides[major]*sides[minor]*2.0 | |
| temp = math.acos(temp) | |
| if (index == 0): | |
| angles[0] = temp | |
| elif (index == 1): | |
| angles[1] = temp | |
| elif (index == 2): | |
| angles[2] = temp | |
| else: | |
| return angles | |
| total -= temp | |
| temp = sides[major]*math.sin(temp) | |
| temp /= sides[king] | |
| temp = math.asin(temp) | |
| while (True): | |
| if (index%3 == 0): | |
| if (check): | |
| angles[1] = temp | |
| else: | |
| angles[2] = temp | |
| elif (index%3 == 1): | |
| if (check): | |
| angles[0] = temp | |
| else: | |
| angles[2] = temp | |
| elif (index%3 == 2): | |
| if (check): | |
| angles[0] = temp | |
| else: | |
| angles[1] = temp | |
| else: | |
| return angles | |
| if (index < 3): | |
| index += 3 | |
| total -= temp | |
| temp = total | |
| if (check): | |
| check = False | |
| else: | |
| check = True | |
| else: | |
| break | |
| return angles | |
| def centroid(points): | |
| if (points == None): | |
| return (0.0, 0.0) | |
| if (len(points) == 0): | |
| return (0.0, 0.0) | |
| center = complex(0.0, 0.0) | |
| for i in range(len(points)): | |
| point = points[i] | |
| center += complex(point[0], point[1]) | |
| center /= float(len(points)) | |
| return tuple([float(center.real), float(center.imag)]) | |
| def compare(left, right): | |
| if (left[0] > right[0]): | |
| return 1 | |
| if (left[0] < right[0]): | |
| return -1 | |
| return 0 | |
| def sort(points): | |
| if (points == None): | |
| return [] | |
| angles = [] | |
| center = centroid(points) | |
| for i in range(len(points)): | |
| point = points[i] | |
| angle = direction(center, point) | |
| angles.append([angle, i]) | |
| sorting = sorted(angles, key=functools.cmp_to_key(compare)) | |
| result = [] | |
| for i in range(len(sorting)): | |
| result.append(points[sorting[i][1]]) | |
| return result | |
| def interpolate(left, right, key): | |
| return ((left[0]*(1.0-key))+(right[0]*key), (left[1]*(1.0-key))+(right[1]*key)) | |
| def split(points, lines): | |
| if ((len(points) < 2) or (lines < 2)): | |
| return [] | |
| splits = [] | |
| shares = [] | |
| distances = [] | |
| progresses = [] | |
| progress = 0 | |
| count = 0 | |
| last = 0 | |
| all = 0 | |
| total = 0.0 | |
| for i in range(len(points)): | |
| if (i == 0): | |
| continue | |
| distances.append(distance(points[i-1], points[i])) | |
| total += distances[len(distances)-1] | |
| for i in range(len(distances)): | |
| if not (total > 0.0): | |
| break | |
| share = distances[i]/total | |
| if not (i == 0): | |
| share += shares[len(shares)-1] | |
| shares.append(share) | |
| splits.append(points[0]) | |
| if (lines > 2): | |
| for i in range(lines): | |
| line = float(i+1)/float(lines) | |
| line *= total | |
| count += 1 | |
| all += 1 | |
| if (line > shares[progress]): | |
| last = progress | |
| while (line > shares[progress]): | |
| progress += 1 | |
| if (progress == len(shares)): | |
| break | |
| for i in range(progress-last): | |
| progresses.append(int(count/(progress-last))) | |
| count = 0 | |
| if (progress == len(shares)): | |
| break | |
| if (len(progresses) == 0): | |
| progresses.append(lines) | |
| all = lines | |
| if (all < lines): | |
| all = int((lines-all)/len(progresses)) | |
| for i in range(len(progresses)): | |
| progresses[i] += all | |
| for i in range(len(progresses)): | |
| for j in range(progresses[i]): | |
| splits.append(interpolate(points[i], points[i+1], float(j)/float(progresses[i]))) | |
| if (len(splits) == lines): | |
| break | |
| splits.append(points[len(points)-1]) | |
| return splits | |
| def circumellipse(triangle, lines, polygon): | |
| result = [] | |
| adjust = [] | |
| if (len(triangle) < 3): | |
| return result | |
| try: | |
| a = triangle[0] | |
| b = triangle[1] | |
| c = triangle[2] | |
| sides = trisides(a, b, c) | |
| angles = triangles(sides) | |
| complexA = complex(a[0], a[1]) | |
| complexB = complex(b[0], b[1]) | |
| complexC = complex(c[0], c[1]) | |
| center = (complexA+complexB+complexC)/3.0 | |
| #print(str(center)) | |
| pi = math.pi | |
| z = (sides[0]**4.0)+(sides[1]**4.0)+(sides[2]**4.0) | |
| z -= ((sides[0])**2.0)*((sides[1])**2.0) | |
| z -= ((sides[1])**2.0)*((sides[2])**2.0) | |
| z -= ((sides[2])**2.0)*((sides[0])**2.0) | |
| z = z**0.5 | |
| temp = z*2.0 | |
| semimajor = (sides[0]**2.0)+(sides[1]**2.0)+(sides[2]**2.0) | |
| semiminor = semimajor | |
| semimajor += temp | |
| semiminor -= temp | |
| temp = float(1.0/3.0) | |
| semimajor = temp*(semimajor**0.5) | |
| semiminor = temp*(semiminor**0.5) | |
| focusFirst = center**2.0 | |
| focusFirst -= ((complexA*complexB)+(complexA*complexC)+(complexB*complexC))/3.0 | |
| focusFirst = (focusFirst**0.5)*2.0 | |
| focusSecond = -focusFirst | |
| focusFirst += center | |
| focusSecond += center | |
| dir = direction(tuple([float(focusFirst.real), float(focusFirst.imag)]), tuple([float(focusSecond.real), float(focusSecond.imag)]))*(180.0/pi) | |
| r = R.from_euler('z', dir, degrees=True) | |
| for i in range(lines): | |
| index = float(i*2) | |
| temp = ((pi/float(lines))*index)-(pi*0.5) | |
| point = [] | |
| point.append(semimajor*math.cos(temp)) | |
| point.append(semiminor*math.sin(temp)) | |
| point.append(0.0) | |
| point = r.apply(point) | |
| result.append(tuple([float(point[0])+float(center.real), float(point[1])+float(center.imag)])) | |
| adjust.append(result[len(result)-1]) | |
| if not (polygon == None): | |
| center = tuple([float(center.real), float(center.imag)]) | |
| loop = 0 | |
| exceptions = [] | |
| while (True): | |
| lastPolygon = -1 | |
| last = -1 | |
| count = 0 | |
| inside = False | |
| change = False | |
| for i in range(len(result)*2): | |
| if (contain(polygon, result[i%len(result)])): | |
| if not (inside): | |
| if ((last > -1) and (lastPolygon > -1)): | |
| for j in range(len(polygon)): | |
| if (j in exceptions): | |
| continue | |
| point = intersection(tuple([polygon[j], polygon[(j+1)%len(polygon)]]), tuple([result[(i-1)%len(result)], result[i%len(result)]])) | |
| if (point == None): | |
| continue | |
| other = intersection(tuple([polygon[lastPolygon], polygon[(lastPolygon+1)%len(polygon)]]), tuple([result[last%len(result)], result[(last+1)%len(result)]])) | |
| if (other == None): | |
| continue | |
| others = [] | |
| if (lastPolygon <= j): | |
| for k in range((j+1)-lastPolygon): | |
| if (k == 0): | |
| others.append(other) | |
| continue | |
| others.append(polygon[(k+lastPolygon)%len(polygon)]) | |
| others.append(point) | |
| else: | |
| for k in range((j+len(polygon)+1)-lastPolygon): | |
| if (k == 0): | |
| others.append(other) | |
| continue | |
| others.append(polygon[(k+lastPolygon)%len(polygon)]) | |
| others.append(point) | |
| #others = list(reversed(others)) | |
| splits = split(others, (i+1)-last) | |
| if (len(splits) > 0): | |
| point = result[i%len(result)] | |
| other = result[last%len(result)] | |
| exceptions.append(j) | |
| if (lastPolygon <= j): | |
| for k in range((j+1)-lastPolygon): | |
| if not ((k+lastPolygon)%len(polygon) in exceptions): | |
| exceptions.append((k+lastPolygon)%len(polygon)) | |
| else: | |
| for k in range((j+len(polygon)+1)-lastPolygon): | |
| if not ((k+lastPolygon)%len(polygon) in exceptions): | |
| exceptions.append((k+lastPolygon)%len(polygon)) | |
| for k in range(len(splits)): | |
| adjust[(k+last)%len(result)] = splits[k] | |
| if (last%len(result) < i%len(result)): | |
| adjust = adjust[:(last%len(result))]+[other]+adjust[(last%len(result)):(i%len(result))]+[point]+adjust[(i%len(result)):] | |
| else: | |
| adjust = adjust[:(i%len(result))]+[point]+adjust[(i%len(result)):(last%len(result))]+[other]+adjust[(last%len(result)):] | |
| change = True | |
| break | |
| inside = True | |
| lastPolygon = -1 | |
| last = i | |
| count += 1 | |
| else: | |
| if (inside): | |
| for j in range(len(polygon)): | |
| if (j in exceptions): | |
| continue | |
| point = intersection(tuple([polygon[j], polygon[(j+1)%len(polygon)]]), tuple([result[(i-1)%len(result)], result[i%len(result)]])) | |
| if (point == None): | |
| continue | |
| lastPolygon = j | |
| break | |
| if (lastPolygon > -1): | |
| inside = False | |
| if (change): | |
| break | |
| if not (change): | |
| if ((count == 0) and (loop == 0)): | |
| result = polygon | |
| break | |
| adjust = sort(adjust) | |
| result = copy.deepcopy(adjust) | |
| loop += 1 | |
| if (len(exceptions) == len(polygon)): | |
| break | |
| except Exception as exception: | |
| now = datetime.now() | |
| print(now.strftime("%d-%m-%Y@%H:%M:%S")) | |
| logging.error(traceback.format_exc()) | |
| return result | |
| def run(size): | |
| lines = 50 | |
| radius = 3.0 | |
| triangle = [] | |
| ellipse = [] | |
| polygon = [] | |
| buttonsNext = [False, False, False] | |
| buttonsPrevious = [False, False, False] | |
| triangleTarget = -1 | |
| polygonTarget = -1 | |
| triangleColor = (255, 0, 0, 255) | |
| ellipseColor = (0, 255, 0, 255) | |
| polygonColor = (0, 0, 255, 128) | |
| backgroundColor = (128, 128, 128) | |
| move = False | |
| quit = False | |
| next = None | |
| previous = None | |
| mouse = None | |
| triangleCenter = None | |
| ellipseCenter = None | |
| polygonCenter = None | |
| ps.LoadScreen(size) | |
| while (True): | |
| mouse = ps.MouseGetPosition() | |
| mouse = tuple([float(mouse[0]), float(mouse[1])]) | |
| buttonsPrevious[0] = buttonsNext[0] | |
| buttonsPrevious[1] = buttonsNext[1] | |
| buttonsPrevious[2] = buttonsNext[2] | |
| buttonsNext[0] = ps.MouseGetButtonL() | |
| buttonsNext[1] = ps.MouseGetButtonM() | |
| buttonsNext[2] = ps.MouseGetButtonR() | |
| if (ps.KeyIsPressed("escape")): | |
| print("quit") | |
| quit = True | |
| if ((triangleTarget < 0) or (polygonTarget < 0)): | |
| move = False | |
| ps.ClearScreen(backgroundColor) | |
| for i in range(len(triangle)): | |
| next = triangle[i] | |
| dist = distance(next, mouse) | |
| #print(str(i)+" "+str(dist)) | |
| if ((triangleTarget == i) or (dist < radius*2.0)): | |
| if (buttonsNext[1]): | |
| if (len(triangle) == 0): | |
| triangle = [] | |
| elif (i == 0): | |
| triangle = triangle[1:] | |
| elif (i < len(triangle)-1): | |
| triangle = triangle[0:i]+triangle[(i+1):] | |
| else: | |
| triangle = triangle[0:i] | |
| ellipse = [] | |
| ellipseCenter = None | |
| triangleCenter = None | |
| break | |
| elif (buttonsNext[0]): | |
| if not ((triangleTarget > -1) and not (triangleTarget == i)): | |
| if ((triangleTarget == i) or not (buttonsPrevious[0])): | |
| move = True | |
| triangleTarget = i | |
| triangle[i] = mouse | |
| next = mouse | |
| if (len(triangle) >= 3): | |
| old = triangleCenter | |
| ellipse = circumellipse(triangle, lines, None) | |
| ellipseCenter = centroid(ellipse) | |
| triangleCenter = centroid(triangle) | |
| color = triangleColor | |
| ps.Circle(int(next[0]-(radius*0.5)), int(next[1]-(radius*0.5)), int(radius*2.0), color=color) | |
| if (i == 0): | |
| previous = triangle[len(triangle)-1] | |
| else: | |
| previous = triangle[i-1] | |
| ps.Line(int(previous[0]), int(previous[1]), int(next[0]), int(next[1]), color=color) | |
| if not (triangleCenter == None): | |
| ps.Circle(int(triangleCenter[0]-(radius*0.5)), int(triangleCenter[1]-(radius*0.5)), int(radius*2.0), color=triangleColor) | |
| for i in range(len(ellipse)): | |
| next = ellipse[i] | |
| color = ellipseColor | |
| if (i == 0): | |
| color = (255, 0, 255, 255) | |
| elif (i == 1): | |
| color = (255, 255, 0, 255) | |
| ps.Circle(int(next[0]-(radius*0.5)), int(next[1]-(radius*0.5)), int(radius*2.0), color=color) | |
| if (i == 0): | |
| previous = ellipse[len(ellipse)-1] | |
| else: | |
| previous = ellipse[i-1] | |
| ps.Line(int(previous[0]), int(previous[1]), int(next[0]), int(next[1]), color=color) | |
| if not (ellipseCenter == None): | |
| ps.Circle(int(ellipseCenter[0]-(radius*0.5)), int(ellipseCenter[1]-(radius*0.5)), int(radius*2.0), color=ellipseColor) | |
| for i in range(len(polygon)): | |
| next = polygon[i] | |
| dist = distance(next, mouse) | |
| #print(str(i)+" "+str(dist)) | |
| if ((polygonTarget == i) or (dist < radius*2.0)): | |
| if (buttonsNext[1]): | |
| if (len(polygon) == 0): | |
| polygon = [] | |
| elif (i == 0): | |
| polygon = polygon[1:] | |
| elif (i < len(polygon)-1): | |
| polygon = polygon[0:i]+polygon[(i+1):] | |
| else: | |
| polygon = polygon[0:i] | |
| if (len(polygon) >= 3): | |
| old = polygonCenter | |
| polygonCenter = centroid(polygon) | |
| break | |
| elif (buttonsNext[2]): | |
| if not ((polygonTarget > -1) and not (polygonTarget == i)): | |
| if ((polygonTarget == i) or not (buttonsPrevious[2])): | |
| move = True | |
| polygonTarget = i | |
| polygon[i] = mouse | |
| next = mouse | |
| if (len(polygon) >= 3): | |
| old = polygonCenter | |
| polygonCenter = centroid(polygon) | |
| color = polygonColor | |
| ps.Circle(int(next[0]-(radius*0.5)), int(next[1]-(radius*0.5)), int(radius*2.0), color=color) | |
| if (i == 0): | |
| previous = polygon[len(polygon)-1] | |
| else: | |
| previous = polygon[i-1] | |
| ps.Line(int(previous[0]), int(previous[1]), int(next[0]), int(next[1]), color=color) | |
| if not (polygonCenter == None): | |
| ps.Circle(int(polygonCenter[0]-(radius*0.5)), int(polygonCenter[1]-(radius*0.5)), int(radius*2.0), color=polygonColor) | |
| ps.UpdateScreen() | |
| if not (move): | |
| if (triangleTarget < 0): | |
| if (len(triangle) < 3): | |
| if ((buttonsNext[0]) and not (buttonsPrevious[0])): | |
| triangle.append(mouse) | |
| triangleTarget = len(triangle) | |
| if (len(triangle) >= 3): | |
| polygonReal = None | |
| if (len(polygon) >= 3): | |
| polygonReal = polygon | |
| ellipse = circumellipse(triangle, lines, sort(polygonReal)) | |
| ellipseCenter = centroid(ellipse) | |
| triangleCenter = centroid(triangle) | |
| else: | |
| if not (buttonsNext[0]): | |
| triangleTarget = -1 | |
| if (len(triangle) >= 3): | |
| polygonReal = None | |
| if (len(polygon) >= 3): | |
| polygonReal = polygon | |
| ellipse = circumellipse(triangle, lines, sort(polygonReal)) | |
| ellipseCenter = centroid(ellipse) | |
| triangleCenter = centroid(triangle) | |
| if (polygonTarget < 0): | |
| if ((buttonsNext[2]) and not (buttonsPrevious[2])): | |
| polygon.append(mouse) | |
| polygonTarget = len(polygon) | |
| if (len(polygon) >= 3): | |
| old = polygonCenter | |
| polygonCenter = centroid(polygon) | |
| else: | |
| if not (buttonsNext[2]): | |
| polygonTarget = -1 | |
| if (len(triangle) >= 3): | |
| polygonReal = None | |
| if (len(polygon) >= 3): | |
| polygonReal = polygon | |
| ellipse = circumellipse(triangle, lines, sort(polygonReal)) | |
| ellipseCenter = centroid(ellipse) | |
| triangleCenter = centroid(triangle) | |
| if (quit): | |
| break | |
| ps.UnloadScreen() | |
| return 0 | |
| def launch(arguments): | |
| result = run((1280, 720)) | |
| print(str(result)) | |
| if not (result == 0): | |
| return False | |
| return True | |
| if (__name__ == "__main__"): | |
| print(str(launch(sys.argv))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment