Skip to content

The Geometry of Points and Vectors

The objective of this section is to cover basic concepts of computational geometry, namely working with 3D points and vectors, via the python programming language.

Mathematical Representation

Points and vectors, as mathematical objects, are ordered collections of numbers: [5, -2.5, 4/3, π, …, e]. The number of elements in the collection n determines the size or degrees of freedom or dimensions spanned by the vector. Some examples: 1D: [5.0], 2D: [6.1, 9.54], 3D: [3, 2, 1], 4D: [1.2, 2.3, 3.4, 4.5]. The numbers have no relationship with one another except the order they appear in the collection. They can appear multiple times in the same vector, unlike a set which is an unordered collection and each element can appear only once.

Point Coordinates Figure

Vector Components Figure

Geometric Semantics

Points and vectors are both represented as ordered collections of numbers. However, they are semantically slightly different: A point represents a location in relationship to a reference frame while a vector represents a displacement. Points represent spatial location identities: a point is a unique location in space no other point shares unless it shares exactly the same coordinates. A vector is a more abstract concept that represents the direction of all parallel lines. There are rules that allow us to perform operations among these different types of objects. For the time being it is important to keep this subtle distinction in mind.

Vector Semantics Figure

Computational Representation

There are several ways to represent points and vectors but the most commonly used in computing are:

  1. Using lists of numbers, such as p = [1, 2, 3] and u = [4, 5, 6]. The benefit of using lists is that they are more general, points and vectors have the same representation, and many python libraries, such as numpy and pytorch, follow this convention. The limitation of using lists is that performing operations with them is slightly less intuitive.
  2. Using objects, such as p = Point3d( 1, 2, 3 ) and u = Vector3d( 4, 5, 6 ). The benefit of using objects is that the operations performed on points and vectors are much more intuitive but on the other hand they are slower and require more memory.

Python Representation

Here we will use the object representation for all geometric entities exactly because it is easier to follow the geometric logic. Rhino's geometry library provides some fairly good definitions for points and vectors. To use those pre-fabricated data types, importing the relevant libraries is required as seen below

from Rhino.Geometry import Point3d, Vector3d

""" Constructing a new point by coordinates
"""
pnt = Point3d( 1, 2, 3 )

""" Constructing a new vector by components
"""
vec = Vector3d( 4, 5, 6 )

Getting and setting point coordinates and vector components may be performed using their X, Y and Z members using the dot notation as seen below.

pnt.X = 3.1415  ''' Setting the X-coordinate '''
print( pnt.X )  ''' Getting and printing the X-coordinate '''

vec.Y = 0.0     ''' Setting the Y-component to zero '''
vec.Z = vec.X   ''' Getting the X-component and setting it to the Z-component '''

World Coordinate System

In 3D space there are three special unit vectors, also known as basis vectors or world axes, X = [1.0, 0.0, 0.0], Y = [0.0, 1.0, 0.0] and Z = [0.0, 0.0, 1.0], which encode the three independent directions that span the space.

o =  Point3d( 0.0, 0.0, 0.0 )
O = Vector3d( 0.0, 0.0, 0.0 )
X = Vector3d( 1.0, 0.0, 0.0 )
Y = Vector3d( 0.0, 1.0, 0.0 )
Z = Vector3d( 0.0, 0.0, 1.0 )

In Rhino the world's origin is defined as static members of the Point3d class and the world's axes in the Vector3d class, as seen below.

o =  Point3d.Origin
O = Vector3d.Zero
X = Vector3d.XAxis
Y = Vector3d.YAxis
Z = Vector3d.ZAxis

Data Type Conversion

Point3d and Vector3d cannot be used interchangeably without conversion. Even though both provide access to the same internal attributes, namely their X, Y, and Z components, they are different types.

""" Conversions
"""
p =  Point3d( vec )  ''' Vector3d to Point3d '''
u = Vector3d( pnt )  ''' Point3d to Vector3d '''

Approximate Equality

The data type used for encoding the X, Y and Z point coordinates and vector components are floating point numbers which are approximations of real numbers in the sense that floats have only finite precision, see documentation. The limited precision of floats complicates comparing points and vectors. Comparing point and vector objects using the equality operator is not in generally appropriate.

""" Not Recommended!
"""
if( p == q ):
    print( 'The points are equal !?' )

This is because floats x and y cannot be compared like x == y in the mathematical sense. Instead the approximate equality expressed as -e ≤ x - y ≤ e or more concisely as | x - y | ≤ e, where e is a small value near zero such as 1e-5, also known as epsilon, error or tolerance, must be used. This concept can be extended to points and vectors as seen below.

""" Coordinate / Component-wise Equality
"""
if( ( abs( p.X - q.X ) <= 1e-5 ) and
    ( abs( p.Y - q.Y ) <= 1e-5 ) and
    ( abs( p.Z - q.Z ) <= 1e-5 ) ):
    print( 'p and q are approximately equal' )
However, it is also possible and actually more common to use the Euclidean distance and length as a measure for approximate equality.

""" Distance near Zero (slow)
"""
if( math.sqrt( ( p.X - q.X ) ** 2 +
               ( p.Y - q.Y ) ** 2 +
               ( p.Z - q.Z ) ** 2 ) <= 1e-5 ):
    print( 'p and q are approximately equal' )

""" Distance Squared near Zero (fast)
"""
if( ( ( p.X - q.X ) ** 2 +
      ( p.Y - q.Y ) ** 2 +
      ( p.Z - q.Z ) ** 2 ) <= 1e-10 ):
    print( 'p and q are approximately equal' )

Note that geometrically the coordinate test checks whether a point is inside a tiny square, whereas the distance test, checks whether a point is within a tiny circle.