Skip to content

Python library for graphics-related maths

License

Notifications You must be signed in to change notification settings

r1chardj0n3s/euclid

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

euclid

Python module for 2D and 3D graphics-related maths functions: vectors, matrices, quaternions and simple plane/line/sphere geometry.

History

This library was written in 2006 and hosted on Google Code under an LGPL license. Alex relicensed the code in 2019 under a more permissive BSD license. It was updated to work with Python 3 soon after, and released as 1.0 in 2020 by the new maintainer, Richard Jones.

The rest of this README is the original documentation from 2006, converted from reStructured Text to Markdown using cloudconvert.com.

Overview

This document describes the euclid module, which provides vector, matrix and quaternion classes for 2D and 3D graphics applications. Everything is provided in the euclid namespace:

>>> from euclid import *
>>>

Type checking of arguments is done with assertions. The advantage of this is that in a usual Python session an exception will be raised earlier rather than later, with a message indicating the expected type. When Python is run with the -O or -OO flags, assertions are removed and the code executes faster.

Vector classes

Two mutable vector types are available: Vector2 and Vector3, for 2D and 3D vectors, respectively. Vectors are assumed to hold floats, but most operations will also work if you use ints or longs instead. Construct a vector in the obvious way:

>>> Vector2(1.5, 2.0)
Vector2(1.50, 2.00)

>>> Vector3(1.0, 2.0, 3.0)
Vector3(1.00, 2.00, 3.00)

Element access

Components may be accessed as attributes (examples that follow use Vector3, but all results are similar for Vector2, using only the x and y components):

>>> v = Vector3(1, 2, 3)
>>> v.x
1
>>> v.y
2
>>> v.z
3

Vectors support the list interface via slicing:

>>> v = Vector3(1, 2, 3)
>>> len(v)
3
>>> v[0]
1
>>> v[:]
(1, 2, 3)

You can also "swizzle" the components (a la GLSL or Cg):

>>> v = Vector3(1, 2, 3)
>>> v.xyz
(1, 2, 3)
>>> v.zx
(3, 1)
>>> v.zzzz
(3, 3, 3, 3)

All of the above accessors are also mutators[1]:

>>> v = Vector3(1, 2, 3)
>>> v.x = 5
>>> v
Vector3(5.00, 2.00, 3.00)
>>> v[1:] = (10, 20)
>>> v
Vector3(5.00, 10.00, 20.00)

[1] assignment via a swizzle (e.g., v.xyz = (1, 2, 3)) is supported only if the _enable_swizzle_set variable is set. This is disabled by default, as it impacts on the performance of ordinary attribute setting, and is slower than setting components sequentially anyway.

Operators

Addition and subtraction are supported via operator overloading (note that in-place operators perform faster than those that create a new object):

>>> v1 = Vector3(1, 2, 3)
>>> v2 = Vector3(4, 5, 6)
>>> v1 + v2
Vector3(5.00, 7.00, 9.00)
>>> v1 -= v2
>>> v1
Vector3(-3.00, -3.00, -3.00)

Multiplication and division can be performed with a scalar only:

>>> Vector3(1, 2, 3) * 2
Vector3(2.00, 4.00, 6.00)
>>> v1 = Vector3(1., 2., 3.)
>>> v1 /= 2
>>> v1
Vector3(0.50, 1.00, 1.50)

The magnitude of a vector can be found with abs:

>>> v = Vector3(1., 2., 3.)
>>> abs(v)
3.7416573867739413

A vector can be normalized in-place (note that the in-place method also returns self, so you can chain it with further operators):

>>> v = Vector3(1., 2., 3.)
>>> v.normalize()
Vector3(0.27, 0.53, 0.80)
>>> v
Vector3(0.27, 0.53, 0.80)

The following methods do not alter the original vector or their arguments:

copy()
Returns a copy of the vector. __copy__ is also implemented.

magnitude()
Returns the magnitude of the vector; equivalent to abs(v). Example:

>>> v = Vector3(1., 2., 3.)
>>> v.magnitude()
3.7416573867739413

magnitude_squared()
Returns the sum of the squares of each component. Useful for comparing the length of two vectors without the expensive square root operation. Example:

>>> v = Vector3(1., 2., 3.)
>>> v.magnitude_squared()
14.0

normalized()
Return a unit length vector in the same direction. Note that this method differs from normalize in that it does not modify the vector in-place. Example:

>>> v = Vector3(1., 2., 3.)
>>> v.normalized()
Vector3(0.27, 0.53, 0.80)
>>> v
Vector3(1.00, 2.00, 3.00)

dot(other)
Return the scalar "dot" product of two vectors. Example:

>>> v1 = Vector3(1., 2., 3.)
>>> v2 = Vector3(4., 5., 6.)
>>> v1.dot(v2)
32.0

cross() and cross(other)
Return the cross product of a vector (for Vector2), or the cross product of two vectors (for Vector3). The return type is a vector. Example:

>>> v1 = Vector3(1., 2., 3.)
>>> v2 = Vector3(4., 5., 6.)
>>> v1.cross(v2)
Vector3(-3.00, 6.00, -3.00)

In two dimensions there can be no argument to cross:

>>> v1 = Vector2(1., 2.)
>>> v1.cross()
Vector2(2.00, -1.00)

reflect(normal)
Return the vector reflected about the given normal. In two dimensions, normal is the normal to a line, in three dimensions it is the normal to a plane. The normal must have unit length. Example:

>>> v = Vector3(1., 2., 3.)
>>> v.reflect(Vector3(0, 1, 0))
Vector3(1.00, -2.00, 3.00)
>>> v = Vector2(1., 2.)
>>> v.reflect(Vector2(1, 0))
Vector2(-1.00, 2.00)

rotate_around(axes, theta)
For 3D vectors, return the vector rotated around axis by the angle theta.

>>> v = Vector3(1., 2., 3.)
>>> axes = Vector3(1.,1.,0)
>>> v.rotate_around(axes,math.pi/4)
Vector3(2.65, 0.35, 2.62)

angle(other)
Return the angle between two vectors.

project(other)
Return the projection (the component) of the vector on other.

Tests for equality include comparing against other sequences:

>>> v2 = Vector2(1, 2)
>>> v2 == Vector2(3, 4)
False
>>> v2 != Vector2(1, 2)
False
>>> v2 == (1, 2)
True

>>> v3 = Vector3(1, 2, 3)
>>> v3 == Vector3(3, 4, 5)
False
>>> v3 != Vector3(1, 2, 3)
False
>>> v3 == (1, 2, 3)
True

Vectors are not hashable, and hence cannot be put in sets nor used as dictionary keys:

>>> {Vector2(): 0}
Traceback (most recent call last):
    ...
TypeError: unhashable type: 'Vector2'

>>> {Vector3(): 0}
Traceback (most recent call last):
    ...
TypeError: unhashable type: 'Vector3'

Matrix classes

Two matrix classes are supplied, Matrix3, a 3x3 matrix for working with 2D affine transformations, and Matrix4, a 4x4 matrix for working with 3D affine transformations.

The default constructor intializes the matrix to the identity:

>>> Matrix3()
Matrix3([    1.00     0.00     0.00
             0.00     1.00     0.00
             0.00     0.00     1.00])
>>> Matrix4()
Matrix4([    1.00     0.00     0.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     1.00     0.00
             0.00     0.00     0.00     1.00])

Element access

Internally each matrix is stored as a set of attributes named a to p. The layout for Matrix3 is:

# a b c 
# e f g 
# i j k 

and for Matrix4:

# a b c d
# e f g h
# i j k l
# m n o p

If you wish to set or retrieve a number of elements at once, you can do so with a slice:

>>> m = Matrix4()
>>> m[:]
[1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0]
>>> m[12:15] = (5, 5, 5)
>>> m
Matrix4([    1.00     0.00     0.00     5.00
             0.00     1.00     0.00     5.00
             0.00     0.00     1.00     5.00
             0.00     0.00     0.00     1.00])

Note that slices operate in column-major order, which makes them suitable for working directly with OpenGL's glLoadMatrix and glGetFloatv functions.

Class constructors

There are class constructors for the most common types of transform.

new_identity
Equivalent to the default constructor. Example:

>>> m = Matrix4.new_identity()
>>> m
Matrix4([    1.00     0.00     0.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     1.00     0.00
             0.00     0.00     0.00     1.00])

new_scale(x, y) and new_scale(x, y, z)
The former is defined on Matrix3, the latter on Matrix4. Equivalent to the OpenGL call glScalef. Example:

>>> m = Matrix4.new_scale(2.0, 3.0, 4.0)
>>> m
Matrix4([    2.00     0.00     0.00     0.00
             0.00     3.00     0.00     0.00
             0.00     0.00     4.00     0.00
             0.00     0.00     0.00     1.00])

new_translate(x, y) and new_translate(x, y, z)
The former is defined on Matrix3, the latter on Matrix4. Equivalent to the OpenGL call glTranslatef. Example:

>>> m = Matrix4.new_translate(3.0, 4.0, 5.0)
>>> m
Matrix4([    1.00     0.00     0.00     3.00
             0.00     1.00     0.00     4.00
             0.00     0.00     1.00     5.00
             0.00     0.00     0.00     1.00])

new_rotate(angle)
Create a Matrix3 for a rotation around the origin. angle is specified in radians, anti-clockwise. This is not implemented in Matrix4 (see below for equivalent methods). Example:

>>> import math
>>> m = Matrix3.new_rotate(math.pi / 2)
>>> m
Matrix3([    0.00    -1.00     0.00
             1.00     0.00     0.00
             0.00     0.00     1.00])

The following constructors are defined for Matrix4 only.

new
Construct a matrix with 16 values in column-major order.

new_rotatex(angle), new_rotatey(angle), new_rotatez(angle)
Create a Matrix4 for a rotation around the X, Y or Z axis, respectively. angle is specified in radians. Example:

>>> m = Matrix4.new_rotatex(math.pi / 2)
>>> m
Matrix4([    1.00     0.00     0.00     0.00
             0.00     0.00    -1.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     0.00     1.00])

new_rotate_axis(angle, axis)
Create a Matrix4 for a rotation around the given axis. angle is specified in radians, and axis must be an instance of Vector3. It is not necessary to normalize the axis. Example:

>>> m = Matrix4.new_rotate_axis(math.pi / 2, Vector3(1.0, 0.0, 0.0))
>>> m        
Matrix4([    1.00     0.00     0.00     0.00
             0.00     0.00    -1.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     0.00     1.00])

new_rotate_euler(heading, attitude, bank)
Create a Matrix4 for the given Euler rotation. heading is a rotation around the Y axis, attitude around the X axis and bank around the Z axis. All rotations are performed simultaneously, so this method avoids "gimbal lock" and is the usual method for implemented 3D rotations in a game. Example:

>>> m = Matrix4.new_rotate_euler(math.pi / 2, math.pi / 2, 0.0)
>>> m
Matrix4([    0.00    -0.00     1.00     0.00
             1.00     0.00    -0.00     0.00
            -0.00     1.00     0.00     0.00
             0.00     0.00     0.00     1.00])

new_perspective(fov_y, aspect, near, far)
Create a Matrix4 for projection onto the 2D viewing plane. This method is equivalent to the OpenGL call gluPerspective. fov_y is the view angle in the Y direction, in radians. aspect is the aspect ration width / height of the viewing plane. near and far are the distance to the near and far clipping planes. They must be positive and non-zero. Example:

>>> m = Matrix4.new_perspective(math.pi / 2, 1024.0 / 768, 1.0, 100.0)
>>> m
Matrix4([    0.75     0.00     0.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00    -1.02    -2.02
             0.00     0.00    -1.00     0.00])

Operators

Matrices of the same dimension may be multiplied to give a new matrix. For example, to create a transform which translates and scales:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m2 = Matrix3.new_scale(1.0, 2.0)
>>> m1 * m2
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])

Note that multiplication is not commutative (the order that you apply transforms matters):

>>> m2 * m1
Matrix3([    1.00     0.00     5.00
             0.00     2.00    12.00
             0.00     0.00     1.00])

In-place multiplication is also permitted (and optimised):

>>> m1 *= m2
>>> m1
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])

Multiplying a matrix by a vector returns a vector, and is used to transform a vector:

>>> m1 = Matrix3.new_rotate(math.pi / 2)
>>> m1 * Vector2(1.0, 1.0)
Vector2(-1.00, 1.00)

Note that translations have no effect on vectors. They do affect points, however:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m1 * Vector2(1.0, 2.0)
Vector2(1.00, 2.00)
>>> m1 * Point2(1.0, 2.0)
Point2(6.00, 8.00)

Multiplication is currently incorrect between matrices and vectors -- the projection component is ignored. Use the Matrix4.transform method instead.

Matrix4 also defines transpose (in-place), transposed (functional), determinant and inverse (functional) methods.

A Matrix3 can be multiplied with a Vector2 or any of the 2D geometry objects (Point2, Line2, Circle, etc).

A Matrix4 can be multiplied with a Vector3 or any of the 3D geometry objects (Point3, Line3, Sphere, etc).

For convenience, each of the matrix constructors are also available as in-place operators. For example, instead of writing:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m2 = Matrix3.new_scale(1.0, 2.0)
>>> m1 *= m2

you can apply the scale directly to m1:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m1.scale(1.0, 2.0)
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])
>>> m1
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])

Note that these methods operate in-place (they modify the original matrix), and they also return themselves as a result. This allows you to chain transforms together directly:

>>> Matrix3().translate(1.0, 2.0).rotate(math.pi / 2).scale(4.0, 4.0)
Matrix3([    0.00    -4.00     1.00
             4.00     0.00     2.00
             0.00     0.00     1.00])

All constructors have an equivalent in-place method. For Matrix3, they are identity, translate, scale and rotate. For Matrix4, they are identity, translate, scale, rotatex, rotatey, rotatez, rotate_axis and rotate_euler. Both Matrix3 and Matrix4 also have an in-place transpose method.

The copy method is also implemented in both matrix classes and behaves in the obvious way.

Quaternions

A quaternion represents a three-dimensional rotation or reflection transformation. They are the preferred way to store and manipulate rotations in 3D applications, as they do not suffer the same numerical degredation that matrices do.

The quaternion constructor initializes to the identity transform:

>>> q = Quaternion()
>>> q
Quaternion(real=1.00, imag=<0.00, 0.00, 0.00>)

Element access

Internally, the quaternion is stored as four attributes: x, y and z forming the imaginary vector, and w the real component.

Constructors

Rotations can be formed using the constructors:

new_identity()
Equivalent to the default constructor.

new_rotate_axis(angle, axis)
Equivalent to the Matrix4 constructor of the same name. angle is specified in radians, axis is an instance of Vector3. It is not necessary to normalize the axis. Example:

>>> q = Quaternion.new_rotate_axis(math.pi / 2, Vector3(1, 0, 0))
>>> q
Quaternion(real=0.71, imag=<0.71, 0.00, 0.00>)

new_rotate_euler(heading, attitude, bank)
Equivalent to the Matrix4 constructor of the same name. heading is a rotation around the Y axis, attitude around the X axis and bank around the Z axis. All angles are given in radians. Example:

>>> q = Quaternion.new_rotate_euler(math.pi / 2, math.pi / 2, 0)
>>> q
Quaternion(real=0.50, imag=<0.50, 0.50, 0.50>)

new_interpolate(q1, q2, t)
Create a quaternion which gives a (SLERP) interpolated rotation between q1 and q2. q1 and q2 are instances of Quaternion, and t is a value between 0.0 and 1.0. For example:

>>> q1 = Quaternion.new_rotate_axis(math.pi / 2, Vector3(1, 0, 0))
>>> q2 = Quaternion.new_rotate_axis(math.pi / 2, Vector3(0, 1, 0))
>>> for i in range(11):
...     print Quaternion.new_interpolate(q1, q2, i / 10.0)
...
Quaternion(real=0.71, imag=<0.71, 0.00, 0.00>)
Quaternion(real=0.75, imag=<0.66, 0.09, 0.00>)
Quaternion(real=0.78, imag=<0.61, 0.17, 0.00>)
Quaternion(real=0.80, imag=<0.55, 0.25, 0.00>)
Quaternion(real=0.81, imag=<0.48, 0.33, 0.00>)
Quaternion(real=0.82, imag=<0.41, 0.41, 0.00>)
Quaternion(real=0.81, imag=<0.33, 0.48, 0.00>)
Quaternion(real=0.80, imag=<0.25, 0.55, 0.00>)
Quaternion(real=0.78, imag=<0.17, 0.61, 0.00>)
Quaternion(real=0.75, imag=<0.09, 0.66, 0.00>)
Quaternion(real=0.71, imag=<0.00, 0.71, 0.00>)

Operators

Quaternions may be multiplied to compound rotations. For example, to rotate 90 degrees around the X axis and then 90 degrees around the Y axis:

>>> q1 = Quaternion.new_rotate_axis(math.pi / 2, Vector3(1, 0, 0))
>>> q2 = Quaternion.new_rotate_axis(math.pi / 2, Vector3(0, 1, 0))
>>> q1 * q2
Quaternion(real=0.50, imag=<0.50, 0.50, 0.50>)

Multiplying a quaternion by a vector gives a vector, transformed appropriately:

>>> q = Quaternion.new_rotate_axis(math.pi / 2, Vector3(0, 1, 0))
>>> q * Vector3(1.0, 0, 0)
Vector3(0.00, 0.00, -1.00)

Similarly, any 3D object can be multiplied (e.g., Point3, Line3, Sphere, etc):

>>> q * Ray3(Point3(1., 1., 1.), Vector3(1., 1., 1.))
Ray3(<1.00, 1.00, -1.00> + u<1.00, 1.00, -1.00>)

As with the matrix classes, the constructors are also available as in-place operators. These are named identity, rotate_euler and rotate_axis. For example:

>>> q1 = Quaternion()
>>> q1.rotate_euler(math.pi / 2, math.pi / 2, 0)
Quaternion(real=0.50, imag=<0.50, 0.50, 0.50>)
>>> q1
Quaternion(real=0.50, imag=<0.50, 0.50, 0.50>)

Quaternions are usually unit length, but you may wish to use sized quaternions. In this case, you can find the magnitude using abs, magnitude and magnitude_squared, as with the vector classes. Example:

>>> q1 = Quaternion()
>>> abs(q1)
1.0
>>> q1.magnitude()
1.0

Similarly, the class implements normalize and normalized in the same way as the vectors.

The following methods do not alter the quaternion:

conjugated()
Returns a quaternion that is the conjugate of the instance. For example:

>>> q1 = Quaternion.new_rotate_axis(math.pi / 2, Vector3(1, 0, 0))
>>> q1.conjugated()
Quaternion(real=0.71, imag=<-0.71, -0.00, -0.00>)
>>> q1
Quaternion(real=0.71, imag=<0.71, 0.00, 0.00>)

get_angle_axis()
Returns a tuple (angle, axis), giving the angle to rotate around an axis equivalent to the quaternion. For example:

>>> q1 = Quaternion.new_rotate_axis(math.pi / 2, Vector3(1, 0, 0))
>>> q1.get_angle_axis()
(1.5707963267948966, Vector3(1.00, 0.00, 0.00))

get_matrix()
Returns a Matrix4 implementing the transformation of the quaternion. For example:

>>> q1 = Quaternion.new_rotate_axis(math.pi / 2, Vector3(1, 0, 0))
>>> q1.get_matrix()
Matrix4([    1.00     0.00     0.00     0.00
             0.00     0.00    -1.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     0.00     1.00])

2D Geometry

The following classes are available for dealing with simple 2D geometry. The interface to each shape is similar; in particular, the connect and distance methods are defined identically for each.

For example, to find the closest point on a line to a circle:

>>> circ = Circle(Point2(3., 2.), 2.)
>>> line = Line2(Point2(0., 0.), Point2(-1., 1.))
>>> line.connect(circ).p1
Point2(0.50, -0.50)

To find the corresponding closest point on the circle to the line:

>>> line.connect(circ).p2
Point2(1.59, 0.59)

Point2

A point on a 2D plane. Construct in the obvious way:

>>> p = Point2(1.0, 2.0)
>>> p
Point2(1.00, 2.00)

Point2 subclasses Vector2, so all of Vector2 operators and methods apply. In particular, subtracting two points gives a vector:

>>> Point2(2.0, 3.0) - Point2(1.0, 0.0)
Vector2(1.00, 3.00)

The following methods are also defined:

connect(other)
Returns a LineSegment2 which is the minimum length line segment that can connect the two shapes. other may be a Point2, Line2, Ray2, LineSegment2 or Circle.

distance(other)
Returns the absolute minimum distance to other. Internally this simply returns the length of the result of connect.

Line2, Ray2, LineSegment2

A Line2 is a line on a 2D plane extending to infinity in both directions; a Ray2 has a finite end-point and extends to infinity in a single direction; a LineSegment2 joins two points.

All three classes support the same constructors, operators and methods, but may behave differently when calculating intersections etc.

You may construct a line, ray or line segment using any of:

  • another line, ray or line segment
  • two points
  • a point and a vector
  • a point, a vector and a length

For example:

>>> Line2(Point2(1.0, 1.0), Point2(2.0, 3.0))
Line2(<1.00, 1.00> + u<1.00, 2.00>)
>>> Line2(Point2(1.0, 1.0), Vector2(1.0, 2.0))
Line2(<1.00, 1.00> + u<1.00, 2.00>)
>>> Ray2(Point2(1.0, 1.0), Vector2(1.0, 2.0), 1.0)
Ray2(<1.00, 1.00> + u<0.45, 0.89>)

Internally, lines, rays and line segments store a Point2 p and a Vector2 v. You can also access (but not set) the two endpoints p1 and p2. These may or may not be meaningful for all types of lines.

The following methods are supported by all three classes:

intersect(other)
If other is a Line2, Ray2 or LineSegment2, returns a Point2 of intersection, or None if the lines are parallel.

If other is a Circle, returns a LineSegment2 or Point2 giving the part of the line that intersects the circle, or None if there is no intersection.

connect(other)
Returns a LineSegment2 which is the minimum length line segment that can connect the two shapes. For two parallel lines, this line segment may be in an arbitrary position. other may be a Point2, Line2, Ray2, LineSegment2 or Circle.

distance(other)
Returns the absolute minimum distance to other. Internally this simply returns the length of the result of connect.

LineSegment2 also has a length property which is read-only.

Circle

Circles are constructed with a center Point2 and a radius:

>>> c = Circle(Point2(1.0, 1.0), 0.5)
>>> c
Circle(<1.00, 1.00>, radius=0.50)

Internally there are two attributes: c, giving the center point and r, giving the radius.

The following methods are supported:

intersect(other)
If other is a Line2, Ray2 or LineSegment2, returns a LineSegment2 giving the part of the line that intersects the circle, or None if there is no intersection.

connect(other)
Returns a LineSegment2 which is the minimum length line segment that can connect the two shapes. other may be a Point2, Line2, Ray2, LineSegment2 or Circle.

distance(other)
Returns the absolute minimum distance to other. Internally this simply returns the length of the result of connect.

3D Geometry

The following classes are available for dealing with simple 3D geometry. The interfaces are very similar to the 2D classes (but note that you cannot mix and match 2D and 3D operations).

For example, to find the closest point on a line to a sphere:

>>> sphere = Sphere(Point3(1., 2., 3.,), 2.)
>>> line = Line3(Point3(0., 0., 0.), Point3(-1., -1., 0.))
>>> line.connect(sphere).p1
Point3(1.50, 1.50, 0.00)

To find the corresponding closest point on the sphere to the line:

>>> line.connect(sphere).p2
Point3(1.32, 1.68, 1.05)

XXX I have not checked if these are correct.

Point3

A point on a 3D plane. Construct in the obvious way:

>>> p = Point3(1.0, 2.0, 3.0)
>>> p
Point3(1.00, 2.00, 3.00)

Point3 subclasses Vector3, so all of Vector3 operators and methods apply. In particular, subtracting two points gives a vector:

>>> Point3(1.0, 2.0, 3.0) - Point3(1.0, 0.0, -2.0)
Vector3(0.00, 2.00, 5.00)

The following methods are also defined:

intersect(other)
If other is a Sphere, returns True iff the point lies within the sphere.

connect(other)
Returns a LineSegment3 which is the minimum length line segment that can connect the two shapes. other may be a Point3, Line3, Ray3, LineSegment3, Sphere or Plane.

distance(other)
Returns the absolute minimum distance to other. Internally this simply returns the length of the result of connect.

Line3, Ray3, LineSegment3

A Line3 is a line on a 3D plane extending to infinity in both directions; a Ray3 has a finite end-point and extends to infinity in a single direction; a LineSegment3 joins two points.

All three classes support the same constructors, operators and methods, but may behave differently when calculating intersections etc.

You may construct a line, ray or line segment using any of:

  • another line, ray or line segment
  • two points
  • a point and a vector
  • a point, a vector and a length

For example:

>>> Line3(Point3(1.0, 1.0, 1.0), Point3(1.0, 2.0, 3.0))
Line3(<1.00, 1.00, 1.00> + u<0.00, 1.00, 2.00>)
>>> Line3(Point3(0.0, 1.0, 1.0), Vector3(1.0, 1.0, 2.0))
Line3(<0.00, 1.00, 1.00> + u<1.00, 1.00, 2.00>)
>>> Ray3(Point3(1.0, 1.0, 1.0), Vector3(1.0, 1.0, 2.0), 1.0)
Ray3(<1.00, 1.00, 1.00> + u<0.41, 0.41, 0.82>)

Internally, lines, rays and line segments store a Point3 p and a Vector3 v. You can also access (but not set) the two endpoints p1 and p2. These may or may not be meaningful for all types of lines.

The following methods are supported by all three classes:

intersect(other)
If other is a Sphere, returns a LineSegment3 which is the intersection of the sphere and line, or None if there is no intersection.

If other is a Plane, returns a Point3 of intersection, or None.

connect(other)
Returns a LineSegment3 which is the minimum length line segment that can connect the two shapes. For two parallel lines, this line segment may be in an arbitrary position. other may be a Point3, Line3, Ray3, LineSegment3, Sphere or Plane.

distance(other)
Returns the absolute minimum distance to other. Internally this simply returns the length of the result of connect.

LineSegment3 also has a length property which is read-only.

Sphere

Spheres are constructed with a center Point3 and a radius:

>>> s = Sphere(Point3(1.0, 1.0, 1.0), 0.5)
>>> s
Sphere(<1.00, 1.00, 1.00>, radius=0.50)

Internally there are two attributes: c, giving the center point and r, giving the radius.

The following methods are supported:

intersect(other):
If other is a Point3, returns True iff the point lies within the sphere.

If other is a Line3, Ray3 or LineSegment3, returns a LineSegment3 giving the intersection, or None if the line does not intersect the sphere.

connect(other)
Returns a LineSegment3 which is the minimum length line segment that can connect the two shapes. other may be a Point3, Line3, Ray3, LineSegment3, Sphere or Plane.

distance(other)
Returns the absolute minimum distance to other. Internally this simply returns the length of the result of connect.

Plane

Planes can be constructed with any of:

  • three Point3's lying on the plane
  • a Point3 on the plane and the Vector3 normal
  • a Vector3 normal and k, described below.

Internally, planes are stored with the normal n and constant k such that n.p = k for any point on the plane p.

The following methods are supported:

intersect(other)
If other is a Line3, Ray3 or LineSegment3, returns a Point3 of intersection, or None if there is no intersection.

If other is a Plane, returns the Line3 of intersection.

connect(other)
Returns a LineSegment3 which is the minimum length line segment that can connect the two shapes. other may be a Point3, Line3, Ray3, LineSegment3, Sphere or Plane.

distance(other)
Returns the absolute minimum distance to other. Internally this simply returns the length of the result of connect.

About

Python library for graphics-related maths

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 100.0%