Code for points/vectors

Once upon a time, we wanted to run some things without having to install NumPy or some other advanced math framework.

Two-Dimensional Vectors (point2d.py)

Two-Dimensional Point/Vector class with common operations.

Point2d() vectors support standard vector arithmetic: +, - (unary and binary), and componentwise test for (in)equality. Multiplication of vectors a*b gives the regular dot product; vdot(a,b) is also available. Scalar multiplication is defined as usual: k*a for the scalar k and vector a.

Many other operations are available; see below for details.

Todo

Better Exception handling for the various arithmetic operators; see the code for __mul__ (dot product) for an example of what we should be doing.

class point2d.Point2d(x=0.0, y=0.0)

A two-dimensional vector of floats, defaulting to <0.0, 0.0>.

Parameters
  • x (float) – x-coordinate (defaults to 0.0).

  • y (float) – y-coordinate (defaults to 0.0).

property x

Get the first (x) coordinate of this point; see example above.

property y

Get the second (y) coordinate of this point; see example above.

property ntuple

Get this point as a Python tuple; see example above.

zero()

Set all coordinates of this point to zero.

>>> a = Point2d(3,-2)
>>> print(a)
Point2d: <3.000000000, -2.000000000>
>>> a.zero()
>>> print(a)
Point2d: <0.000000000, 0.000000000>
__mul__(term)

Dot product: vector*vector.

Examples below show the available types of vector/scalar multiplcation.

>>> a = Point2d(1,-2)
>>> b = Point2d(3,5)
>>> a*b         # Dot product as a binary operator
-7.0
>>> vdot(a,b)   # Dot product as a function of two vectors
-7.0
>>> print(3*a)  # Scalar multiplication, note the order
Point2d: <3.000000000, -6.000000000>
>>> a*3         # Vector*Scalar is undefined in this order
Traceback (most recent call last):
TypeError: unsupported operand type(s) for *: 'Point2d' and 'int'
>>> a *= a      # To avoid later confusion, this raises a TypeError.
Traceback (most recent call last):
NotImplementedError: Compound assignment for dot product is not supported.
__rmul__(scalar)

Scalar multiplication: scalar*vector; see examples above.

rotated_by(angle, use_deg=False)

Get this vector rotated anticlockwise.

Parameters
  • angle (float) – Directed anticlockwise angle in radians.

  • use_deg (bool, optional) – Set True to use degrees instead of radians.

>>> from math import pi
>>> a = Point2d(2,-2)
>>> print(a.rotated_by(pi))
Point2d: <-2.000000000, 2.000000000>
>>> print(a.rotated_by(-pi/3))
Point2d: <-0.732050808, -2.732050808>
>>> print(a.rotated_by(90,True))
Point2d: <2.000000000, 2.000000000>
norm()

Get the norm (length) of this vector.

>>> Point2d(1,-2).norm()
2.23606797749979
sqnorm()

Get the squared norm (length) of this vector.

>>> Point2d(1,-2).sqnorm()
5.0
unit()

Get a unit vector in the same direction as this one.

Raises

ZeroDivisionError – If called on a zero vector.

Note

Be aware of round-off errors; see the example below.

>>> a = Point2d(1,-2)
>>> print(a.unit())
Point2d: <0.447213595, -0.894427191>
>>> a.unit().norm()
0.9999999999999999
normalize()

Rescale this vector to have length 1.

Raises

ZeroDivisionError – If called on a zero vector.

Note

Be aware of round-off errors; see the example below.

>>> a = Point2d(1,-2)
>>> a.normalize()
>>> print(a)
Point2d: <0.447213595, -0.894427191>
>>> a.norm()
0.9999999999999999
truncate(maxlength)

Rescale this vector in-place to a maximum length.

Parameters

maxlength (float) – Upper limit on the length. If the current length exceeds this, the vector is rescaled in-place.

Returns

True if rescaling was done, False otherwise.

Return type

(bool)

>>> a = Point2d(1,-2)
>>> a.truncate(1.0)
True
>>> print(a)
Point2d: <0.447213595, -0.894427191>
>>> b = Point2d(-1,2)
>>> b.truncate(5.0)
False
>>> print(b)
Point2d: <-1.000000000, 2.000000000>
scale_to(mag)

Change this vector in-place to have the given magnitude.

Parameters

mag (float) – New magnitude; negative value also reverses direction.

>>> a = Point2d(3,4)
>>> a.scale_to(10)
>>> print(a, a.norm())
Point2d: <6.000000000, 8.000000000> 10.0
>>> a.scale_to(-0.5)
>>> print(a, a.norm())
Point2d: <-0.300000000, -0.400000000> 0.5
angle()

Get the polar angle of this vector in radians; range (-pi,pi]

Raises

ZeroDivisionError – If called on a zero vector.

>>> Point2d(1,-2).angle()
-1.1071487177940904
>>> Point2d(1,0).angle()
0.0
>>> Point2d(0,1).angle()
1.5707963267948966
>>> Point2d(-1,0).angle()
3.141592653589793
>>> Point2d(-2,-2).angle()
-2.356194490192345
>>> Point2d(0,0).angle()
Traceback (most recent call last):
    ...
ZeroDivisionError: float division by zero
__truediv__(direction)

Length of an orthogonal projection; overrides the / operator.

Parameters

direction (Point2d) – The vector we project onto.

Returns

The length of the orthogonal projection.

Return type

float

Returns the scalar q such that self = q*u + r, where u is a unit vector (normalized direction) and r is orthogonal to u. This is algebraically identical to exact division (/). If you want the result as a vector, use Point2d.proj(direction) or compound assignment.

Note

Compound assignment /= behaves differently; see below.

>>> a = Point2d(2,2)
>>> b = Point2d(3,0)
>>> a/b
2.0
>>> b/a
2.1213203435596424
__itruediv__(direction)

In-place orthogonal projection; overrides the /= operator.

Parameters

direction (Point2d) – The vector we project onto.

Replaces this vector by the unique vector v satisfying all of the following: (1) self = v + w, (2) v is parallel to direction, (3) w is orthogonal to v.

Note

Non-compound division / behaves differently; see above.

>>> a = Point2d(2,4)
>>> b = Point2d(3,-2)
>>> dotp = a*b
>>> a /= b
>>> print(a)
Point2d: <-0.461538462, 0.307692308>
>>> print(b)
Point2d: <3.000000000, -2.000000000>
>>> a*b == dotp  # Dot product is preserved by orthogonal projection.
True
proj(direction)

Get the orthogonal projection of this vector onto another.

Parameters

direction (Point2d) – The vector we project onto.

Returns

The unique vector v such that self = v + w, where v is parallel to direction and w is orthogonal to v.

Return type

Point2d

Note

If you want both v2 and v3, use Point2d.resolve(direction) instead. See divison / and compound division /= above for other options.

>>> a = Point2d(2,4)
>>> b = Point2d(3,-2)
>>> print(a.proj(b))
Point2d: <-0.461538462, 0.307692308>
>>> print(b.proj(a))
Point2d: <-0.200000000, -0.400000000>
resolve(direction)

Orthogonal decomposition of this vector in a given direction.

Parameters

direction (Point2d) – The vector we project onto.

Returns

The unique vectors v, w such that self = v + w, where v is parallel to direction and w is orthogonal to v.

Return type

Point2d, Point2d

>>> a = Point2d(2,-3)
>>> b = Point2d(1,4)
>>> print(a.resolve(b)[0])
Point2d: <-0.588235294, -2.352941176>
>>> print(a.resolve(b)[1])
Point2d: <2.588235294, -0.647058824>
>>> print(a.resolve(b)[0]+a.resolve(b)[1])
Point2d: <2.000000000, -3.000000000>
left_normal()

Returns the left-facing normal of this vector.

>>> print(Point2d(1,-2).left_normal())
Point2d: <2.000000000, 1.000000000>
>>> print(Point2d(0,0).left_normal())
Point2d: <-0.000000000, 0.000000000>

Note

If applied to the zero vector, returns a zero vector (see example).

point2d.ZERO_VECTOR = Point2d(0.000000000, 0.000000000)

Static zero vector for convenience.

point2d.vdot(v1, v2)

Dot product of two vectors; functional form.

>>> Point2d(1,-2)*Point2d(3,5)           # As a binary operator
-7.0
>>> vdot(Point2d(1,-2), Point2d(3,5))    # As a function of two vectors.
-7.0