def intersection_line_and_circle(cp: Position, cr: float, lp1: Position, lp2: Position) -> List[Position]: # Based on http://mathworld.wolfram.com/Circle-LineIntersection.html lp1 = lp1.copy() - cp lp2 = lp2.copy() - cp d = lp2 - lp1 det = lp1.x * lp2.y - lp2.x * lp1.y delta = cr**2 * d.norm**2 - det**2 if delta < 0: return [] # No intersection x1 = (det * d.y + np.sign(d.y) * d.x * np.sqrt(delta)) / (d.norm**2) y1 = (-det * d.x + abs(d.y) * np.sqrt(delta)) / (d.norm**2) if delta == 0: return [Position(x1, y1) + cp] # Tangential x2 = (det * d.y - np.sign(d.y) * d.x * np.sqrt(delta)) / (d.norm**2) y2 = (-det * d.x - abs(d.y) * np.sqrt(delta)) / (d.norm**2) return [Position(x1, y1) + cp, Position(x2, y2) + cp]
def normalize(vec: Position) -> Position: if vec.norm == 0: raise ZeroDivisionError return vec.copy() / vec.norm