
# point.py
# https://bazaar.launchpad.net/~razvancosmin-radulescu/geo2d/trunk/view/head:/geo2d/geometry.py

import math, random

import matplotlib.pyplot as plt


# acceptable uncertainty for calculating intersections and such
EPS = 1e-8


class Point:
  def __init__(self, *args):
    if len(args) == 0:
      self.x = 0.
      self.y = 0.
    elif len(args) == 1:
      p = args[0]
      if Point.isPoint(p):
        self.x = float(p.x)
        self.y = float(p.y)
    elif len(args) == 2:
      self.x = float(args[0])
      self.y = float(args[1])
    else:
      raise TypeError('The construct needs no arguments, '
              'Point, list, tuple (of length 2) or two '
              'values, but got instead: {0}'.format(args))

  def get(self):
    return (self.x, self.y)


  def __str__(self):
    return f"({self.x:.1f}, {self.y:.1f})"

  def __hash__(self):
    return hash((self.x, self.y))

  def __eq__(self, p):
    if not Point.isPoint(p):
      return False

    return abs(self.x - p.x) < EPS and \
             abs(self.y - p.y) < EPS


  def __lt__(self, other):
    if not Point.isPoint(other):
      return False

    if self.x < other.x:
      return True
    elif self.x > other.x:
      return False
    elif self.y < self.y:
      return True
    elif self.y > other.y:
      return False

  def __gt__(self, other):
    return not self.__lt__(other)


  def distSq(self, p):
    if Point.isPoint(p):
      return ((self.x - p.x)**2 + (self.y - p.y)**2)
    else:
      raise ValueError(f"Expected a Point, but got: {p}")

  def distTo(self, p):
    if Point.isPoint(p):
      return math.sqrt((self.x - p.x)**2 + (self.y - p.y)**2)
    else:
      raise ValueError(f"Expected a Point, but got: {p}")


  def midPoint(self, p):
    if Point.isPoint(p):
      return point((self.x + p.x)/2.0, (self.y + p.y)/2.0)
    else:
      raise ValueError(f"Expected a Point, but got: {p}")


  def polarAngle(self, p):
    return math.atan2(self.y-p.y, self.x-p.x)


  def length(self):
    # treating the point as a vector
    return self.distance_to(Point(0,0))


  def draw(self, color="blue", label=None):
    plt.scatter(self.x, self.y, color=color)
    if label != None:
      ax = plt.gca()
      ax.annotate(" "+label, (self.x, self.y), fontsize=12)


  @staticmethod
  def isPoint(p):
    return isinstance(p, Point)

