Source code for petrify.plane.point

import math
import operator

from .planar import Planar
from .. import generic, units
from ..geometry import AbstractPolygon, Geometry, tau, valid_scalar

def operate(op, self, other):
    if valid_scalar(other):
        return self.__class__(op(self.x, other), op(self.y, other))
    else:
        return NotImplemented

def partial(v):
    return (isinstance(v, tuple) or isinstance(v, list)) and len(v) == 2

[docs]class Vector2(generic.Concrete, generic.Vector, Planar): """ A two-dimensional vector supporting all corresponding built-in math operators: >>> Vector(1, 2) + Vector(2, 2) Vector(3, 4) >>> Vector(1, 2) - Vector(2, 2) Vector(-1, 0) >>> Vector(1, 1) * 5 Vector(5, 5) >>> Vector(1, 1) / 5 Vector(0.2, 0.2) >>> Vector(1, 1) == Vector(1, 1) True In addition to many other specialized vector operations. """ def __init__(self, x=0, y=0): assert valid_scalar(x) and valid_scalar(y) self.x = x self.y = y def __copy__(self): return self.__class__(self.x, self.y) copy = __copy__ def __repr__(self): return 'Vector({0!r}, {1!r})'.format(self.x, self.y) def __hash__(self): return hash((self.x, self.y)) def __eq__(self, other): if isinstance(other, Vector2): return self.x == other.x and \ self.y == other.y elif partial(other): return self.x == other[0] and \ self.y == other[1] else: return NotImplemented def __ne__(self, other): return not self.__eq__(other) def __nonzero__(self): return bool(self.x != 0 or self.y != 0) def __len__(self): return 2 def __getitem__(self, key): return (self.x, self.y)[key] def __setitem__(self, key, value): l = [self.x, self.y] l[key] = value self.x, self.y = l def __iter__(self): return iter((self.x, self.y)) def __getattr__(self, name): try: return tuple([(self.x, self.y)['xy'.index(c)] \ for c in name]) except ValueError: raise AttributeError(name) def __add__(self, other): if isinstance(other, Vector2): # Vector + Vector -> Vector # Vector + Point -> Point # Point + Point -> Vector if self.__class__ is other.__class__: _class = Vector2 else: _class = Point2 return _class(self.x + other.x, self.y + other.y) elif partial(other): return Vector2(self.x + other[0], self.y + other[1]) else: return NotImplemented __radd__ = __add__ def __iadd__(self, other): if isinstance(other, Vector2): self.x += other.x self.y += other.y else: self.x += other[0] self.y += other[1] return self def __sub__(self, other): if isinstance(other, Vector2): # Vector - Vector -> Vector # Vector - Point -> Point # Point - Point -> Vector if self.__class__ is other.__class__: _class = Vector2 else: _class = Point2 return _class(self.x - other.x, self.y - other.y) elif partial(other): return Vector2(self.x - other[0], self.y - other[1]) else: return NotImplemented def __rsub__(self, other): if isinstance(other, Vector2): return Vector2(other.x - self.x, other.y - self.y) elif partial(other): return Vector2(other.x - self[0], other.y - self[1]) else: return NotImplemented def __mul__(self, other): if valid_scalar(other): return self.__class__(self.x * other, self.y * other) else: return NotImplemented __rmul__ = __mul__ def __div__(self, other): return operate(operator.div, self, other) def __rdiv__(self, other): return operate(operator.div, self, other) def __floordiv__(self, other): return operate(operator.floordiv, self, other) def __rfloordiv__(self, other): return operate(operator.floordiv, self, other) def __truediv__(self, other): return operate(operator.truediv, self, other) def __rtruediv__(self, other): return operate(operator.truediv, self, other) def __neg__(self): return self.__class__(-self.x, -self.y) __pos__ = __copy__ def __abs__(self): return math.sqrt(self.x ** 2 + \ self.y ** 2) magnitude = __abs__ def magnitude_squared(self): return self.x ** 2 + \ self.y ** 2
[docs] def normalized(self): """ Return a new vector normalized to unit length: >>> Vector(0, 5).normalized() Vector(0.0, 1.0) """ d = self.magnitude() if d: return self.__class__(self.x / d, self.y / d) return self.copy()
[docs] def dot(self, other): """ The dot product of this vector and `other`: >>> Vector(2, 1).dot(Vector(2, 3)) 7 """ assert isinstance(other, Vector2) return self.x * other.x + \ self.y * other.y
def cross(self): return self.__class__(self.y, -self.x)
[docs] def reflected(self, normal): """ Reflects this vector across a line with the given perpendicular `normal`: >>> Vector(1, 1).reflected(Vector(0, 1)) Vector(1, -1) .. warning:: Assumes `normal` is normalized (has unit length). """ # assume normal is normalized assert isinstance(normal, Vector2) d = 2 * (self.x * normal.x + self.y * normal.y) return self.__class__(self.x - d * normal.x, self.y - d * normal.y)
[docs] def angle(self, other): """ Return the angle to the vector other: >>> Vector(1, 0).angle(Vector(0, 1)) == tau / 4 True """ ratio = self.dot(other) / (self.magnitude()*other.magnitude()) ratio = max(-1.0, min(1.0, ratio)) return math.acos(ratio)
[docs] def project(self, other): """ Return one vector projected on the vector other """ n = other.normalized() return self.dot(n)*n
[docs] def round(self, places): """ Rounds this vector to the given decimal place: >>> Vector(1.00001, 1.00001).round(2) Vector(1.0, 1.0) """ return self.__class__(round(self.x, places), round(self.y, places))
[docs] def snap(self, grid): """ Snaps this vector to a `grid`: >>> Vector(1.15, 1.15).snap(0.25) Vector(1.25, 1.25) """ def snap(v): return round(v / grid) * grid return self.__class__(snap(self.x), snap(self.y))
Vector = Vector2
[docs]class Point2(Vector2, Geometry, generic.Point): """ A close cousin of :py:class:`Vector2` used to represent points: >>> Point(1, 1) + Vector(2, 3) Point(3, 4) >>> Point(1, 2) * 2 Point(2, 4) """ def __repr__(self): return 'Point({0!r}, {1!r})'.format(self.x, self.y)
[docs] def intersect(self, other): """ Used to determine if this point is within a circle: >>> from petrify.plane import Circle >>> Point(1, 1).intersect(Circle(Point(0, 0), 2)) True >>> Point(3, 3).intersect(Circle(Point(0, 0), 2)) False """ return other._intersect_point2(self)
def _intersect_circle(self, other): from .util import _intersect_point2_circle return _intersect_point2_circle(self, other)
[docs] def connect(self, other): """ Connects this point to the other given geometry: >>> from petrify.plane import Circle, Line >>> Point(1, 1).connect(Line(Point(0, 0), Vector(1, 0))) LineSegment(Point(1, 1), Point(1.0, 0.0)) >>> Point(1, 1).connect(Point(0, 0)) LineSegment(Point(1, 1), Point(0, 0)) >>> Point(0, 2).connect(Circle(Point(0, 0), 1)) LineSegment(Point(0, 2), Point(0.0, 1.0)) """ return other._connect_point2(self)
def _connect_point2(self, other): from . import LineSegment2 return LineSegment2(other, self) def _connect_line2(self, other): from .util import _connect_point2_line2 c = _connect_point2_line2(self, other) if c: return c._swap() def _connect_circle(self, other): from .util import _connect_point2_circle c = _connect_point2_circle(self, other) if c: return c._swap()
Point = Point2 Point2.origin = Point2(0, 0)