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
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
>>> 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