Advertisement
sweeneyde

Python Plane Geometry Library

Mar 6th, 2021
926
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.76 KB | None | 0 0
  1. from collections import namedtuple
  2. import math
  3.  
  4. class Point(namedtuple("_Point", "x y")):
  5.     def __add__(self, other):
  6.         return Point(self.x + other.x, self.y + other.y)
  7.     def __sub__(self, other):
  8.         return Point(self.x - other.x, self.y - other.y)
  9.     def __mul__(self, scalar):
  10.         return Point(scalar * self.x, scalar * self.y)
  11.     def __truediv__(self, scalar):
  12.         return Point(self.x / scalar, self.y / scalar)
  13.     __rmul__ = __mul__
  14.     def dist2(self):
  15.         return self.x**2 + self.y**2
  16.     def dist(self):
  17.         return math.sqrt(self.dist2())
  18.     __abs__ = dist
  19.     def theta(self):
  20.         return math.atan2(self.y, self.x)
  21.     def dot(self, other):
  22.         return self.x*other.x + self.y*other.y
  23.     def cross(self, other):
  24.         return self.x*other.y - self.y * other.x
  25.     def unit(self):
  26.         return (1/abs(self)) * self
  27.     def rotate(self, theta):
  28.         cos_t = math.cos(theta)
  29.         sin_t = math.sin(theta)
  30.         return Point(self.x*cos_t - self.y*sin_t,
  31.                      self.x*sin_t + self.y*cos_t)
  32.     def perp(self):
  33.         return Point(-self.y, self.x)
  34.  
  35. class Circle(namedtuple("_Circle", "center radius")):
  36.     pass
  37.  
  38. def circle_circle_intersect(circle1, circle2):
  39.     a, r1 = circle1
  40.     b, r2 = circle2
  41.     if (a == b):
  42.         if r1 == r2:
  43.             raise ValueError
  44.         return []
  45.     vec = b - a
  46.     d2 = vec.dist2()
  47.     sum = r1 + r2
  48.     dif = r1 - r2
  49.     p = (d2 + r1 * r1 - r2 * r2) / (d2 * 2)
  50.     h2 = r1 * r1 - p * p * d2
  51.     if sum * sum < d2 or dif * dif > d2:
  52.         return []
  53.     mid = a + vec * p
  54.     per = vec.perp() * math.sqrt(max(0, h2) / d2)
  55.     return [mid + per, mid - per]
  56.  
  57.  
  58. def circle_tangents(circle1, circle2):
  59.     """
  60.    Get 0, 1, or 2 outer tangents as a list of pairs of points.
  61.    Negate r2 to get the inner tangents.
  62.    """
  63.     c1, r1 = circle1
  64.     c2, r2 = circle2
  65.     d = c2 - c1
  66.     dr = r1 - r2
  67.     d2 = d.dist2()
  68.     h2 = d2 - dr * dr
  69.     if d2 == 0 or h2 < 0:
  70.         return []
  71.     out = []
  72.     for sign in (-1, 1):
  73.         v = (d * dr + d.perp() * math.sqrt(h2) * sign) / d2
  74.         pair = (c1 + v * r1,
  75.                 c2 + v * r2)
  76.         out.append(pair)
  77.     if h2 == 0:
  78.         out.pop()
  79.     return out
  80.  
  81.  
  82. def circle_line_intersect(circle, a, b):
  83.     """a and b are endpoints"""
  84.     assert isinstance(a, Point) and isinstance(b, Point)
  85.     c, r = circle
  86.     ab = b - a
  87.     p = a + ab * (c - a).dot(ab) / ab.dist2()
  88.     s = Point.cross(b-a, c-a)
  89.     h2 = r*r - s * s / ab.dist2()
  90.     if h2 < 0:
  91.         return ()
  92.     if h2 == 0:
  93.         return (p,)
  94.     h = ab.unit() * math.sqrt(h2)
  95.     return (p - h, p + h)
  96.  
  97. def segment_intersection(seg1, seg2):
  98.     a, b = seg1
  99.     c, d = seg2
  100.     oa = Point.cross(d-c, a-c)
  101.     ob = Point.cross(d-c, b-c)
  102.     oc = Point.cross(b-a, c-a)
  103.     od = Point.cross(b-a, d-a)
  104.     # Checks if intersection is single non - endpoint point
  105.     if oa * ob < 0 and oc * od < 0:
  106.         return (a * ob - b * oa) / (ob - oa)
  107.     return None
  108.     # set < P > s;
  109.     # if (onSegment(c, d, a)) s.insert(a);
  110.     # if (onSegment(c, d, b)) s.insert(b);
  111.     # if (onSegment(a, b, c)) s.insert(c);
  112.     # if (onSegment(a, b, d)) s.insert(d);
  113.     # return {all(s)};
  114.  
  115.  
  116. def on_segment(start, end, x):
  117.     u = start - x
  118.     v = end - x
  119.     return u.cross(v) == 0 and u.dot(v) <= 0
  120.  
  121. def distance_to_segment(start, end, x):
  122.     if start == end:
  123.         return (start-x).dist()
  124.     d = (end-start).dist2()
  125.     t = min(d, max(0, (x-start).dot(end-start)))
  126.     return abs( (x-start)*d - (end-start)*t ) / d
  127.  
  128. def in_polygon(poly, point):
  129.     """poly is a list of points"""
  130.     wind = 0
  131.     for i, p in enumerate(poly):
  132.         q = poly[(i + 1) % len(poly)]
  133.         # maybe handle on_segment here
  134.         if p.y <= point.y:
  135.             if q.y > point.y:
  136.                 # upward crossing
  137.                 if turnLeft(p, q, point):
  138.                     wind += 1
  139.         else:
  140.             if q.y <= point.y:
  141.                 if turnLeft(q, p, point):
  142.                     wind -= 1
  143.     return wind
  144.  
  145.  
  146. def turnLeft(p0, p1, p2):
  147.     # Are the points counterclockwise-oriented?
  148.     u = p1 - p0
  149.     v = p2 - p1
  150.     return u.cross(v) >= 0
  151.  
  152.  
  153. def hull_one_side(points):
  154.     hull = []
  155.     for point in points:
  156.         while len(hull) >= 2 and turnLeft(hull[-2], hull[-1], point):
  157.             hull.pop()
  158.         hull.append(point)
  159.     return hull
  160.  
  161. def convex_hull(points):
  162.     if len(points) <= 1:
  163.         return points
  164.     points = sorted(points)
  165.     h1 = hull_one_side(points)
  166.     points.reverse()
  167.     h1.pop()
  168.     h2 = hull_one_side(points)
  169.     h2.pop()
  170.     h1.extend(h2)
  171.     return h1
  172.  
  173. def shoelace(points):
  174.     return sum(points[i-1].cross(points[i])
  175.                for i in range(len(points))) / 2
  176.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement