Module piecad.lin_math

This is a small package of linear algebra functions for 2D and 3D vectors. It is used internally in Piecad because Numpy is actually rather slow for small vector operations. Consider the Numpy "cross" function, it is about 65 times slower than the simple ones below. Additionally Numpy wraps data with types like np.float64 which has overhead and can cause problems in normal python code. In short, Numpy is a bad fit for this Computation Geometry package.

This linear algebra package is unusual in that it does not work from a class, but rather is a series of "static" methods designed to operate on plain tuples representing vectors. This is because Python is much more efficent at making a plain tuple than a class. Also accessing elements of a tuple is faster than accessing elements of a python list or a class attribute. Tuples are also more memory efficient.

This is an ADVANCED package intended for users who are familiar with linear algebra and its use in Computational Geometry. For speed reasons none of its functions perform any type checking on their inputs.

Classes

class Geom
Expand source code
class Geom:
    """
    Namespace holding geometric functions and utilities.
    """

    @staticmethod
    def coplanar(vl: list[tuple[float, float, float]]) -> bool:
        """
        Checks if a list of vectors are coplanar.
        """
        from . import Const

        if len(vl) < 4:
            return True
        v0, v1, v2 = vl[0], vl[1], vl[2]
        normal = Vec3.cross(Vec3.sub(v1, v0), Vec3.sub(v2, v0))
        for v in vl[3:]:
            if abs(Vec3.dot(normal, Vec3.sub(v, v0))) > Const.epsilon:
                return False
        return True

Namespace holding geometric functions and utilities.

Static methods

def coplanar(vl: list[tuple[float, float, float]]) ‑> bool
Expand source code
@staticmethod
def coplanar(vl: list[tuple[float, float, float]]) -> bool:
    """
    Checks if a list of vectors are coplanar.
    """
    from . import Const

    if len(vl) < 4:
        return True
    v0, v1, v2 = vl[0], vl[1], vl[2]
    normal = Vec3.cross(Vec3.sub(v1, v0), Vec3.sub(v2, v0))
    for v in vl[3:]:
        if abs(Vec3.dot(normal, Vec3.sub(v, v0))) > Const.epsilon:
            return False
    return True

Checks if a list of vectors are coplanar.

class Mat3
Expand source code
class Mat3:
    @staticmethod
    def determinant(m: list[tuple[float, float, float]]) -> float:
        """
        Calculates the determinant of a 3x3 matrix.
        """
        return (
            m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
            - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
            + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0])
        )

Static methods

def determinant(m: list[tuple[float, float, float]]) ‑> float
Expand source code
@staticmethod
def determinant(m: list[tuple[float, float, float]]) -> float:
    """
    Calculates the determinant of a 3x3 matrix.
    """
    return (
        m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
        - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
        + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0])
    )

Calculates the determinant of a 3x3 matrix.

class Vec2
Expand source code
class Vec2:
    """
    Namespace holding functions that operate on plain 2-tuples.
    """

    @staticmethod
    def abs(v1: tuple[float, float]) -> tuple[float, float]:
        """
        Returns the absolute value of the vector v1.
        """
        return (math.fabs(v1[0]), math.fabs(v1[1]))

    @staticmethod
    def add(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
        """
        Returns a vector that is vector v1 added to vector v2)
        """
        return (v1[0] + v2[0], v1[1] + v2[1])

    @staticmethod
    def mul(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
        """
        Returns a vector that is vector v1 multiplied by vector v2.
        """
        return (v1[0] * v2[0], v1[1] * v2[1])

    @staticmethod
    def neg(v1: tuple[float, float]) -> tuple[float, float]:
        """
        Returns a vector that is the negation of vector v1.
        """
        return (-v1[0], -v1[1])

    @staticmethod
    def pos(v1: tuple[float, float]) -> tuple[float, float]:
        """
        Returns a vector that is the positive of vector v1 (same as vector v1).
        """
        return v1

    @staticmethod
    def sub(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
        """
        Returns a vector that is vector v1 subtracted by vector v2.
        """
        return (v1[0] - v2[0], v1[1] - v2[1])

    @staticmethod
    def div(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
        """
        Returns a vector that is vector v1 divided by vector v2.
        """
        return (v1[0] / v2[0], v1[1] / v2[1])

    @staticmethod
    def cross(
        v1: tuple[float, float], v2: tuple[float, float]
    ) -> tuple[float, float, float]:
        """
        Returns the cross product of vector v1 with vector v2.
        """
        return (0, 0, v1[0] * v2[1] - v1[1] * v2[0])

    @staticmethod
    def distance(v1: tuple[float, float], v2: tuple[float, float]) -> float:
        """
        Returns the distance between vector v1 and vector v2.
        """
        x = v2[0] - v1[0]
        y = v2[1] - v1[1]
        return math.sqrt(x * x + y * y)

    @staticmethod
    def dot(v1: tuple[float, float], v2: tuple[float, float]) -> float:
        """
        Returns the dot product of vector v1 with vector v2.
        """
        return v1[0] * v2[0] + v1[1] * v2[1]

    @staticmethod
    def length(v1: tuple[float, float]) -> float:
        """
        Returns the length of vector v1.
        """
        return math.sqrt(v1[0] * v1[0] + v1[1] * v1[1])

    @staticmethod
    def normalize(v1: tuple[float, float]) -> tuple[float, float]:
        """
        Returns a normalized (unit length) vector in the same direction as vector v1.
        If the vector has zero length, returns a zero vector.
        """
        length = math.sqrt(v1[0] * v1[0] + v1[1] * v1[1])
        if length > 0:
            return (v1[0] / length, v1[1] / length)
        return (0, 0)

    @staticmethod
    def squaredDistance(v1: tuple[float, float], v2: tuple[float, float]) -> float:
        x = v2[0] - v1[0]
        y = v2[1] - v1[1]
        return x * x + y * y

    @staticmethod
    def squaredLength(v1: tuple[float, float]) -> float:
        return v1[0] * v1[0] + v1[1] * v1[1]

Namespace holding functions that operate on plain 2-tuples.

Static methods

def abs(v1: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def abs(v1: tuple[float, float]) -> tuple[float, float]:
    """
    Returns the absolute value of the vector v1.
    """
    return (math.fabs(v1[0]), math.fabs(v1[1]))

Returns the absolute value of the vector v1.

def add(v1: tuple[float, float], v2: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def add(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
    """
    Returns a vector that is vector v1 added to vector v2)
    """
    return (v1[0] + v2[0], v1[1] + v2[1])

Returns a vector that is vector v1 added to vector v2)

def cross(v1: tuple[float, float], v2: tuple[float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def cross(
    v1: tuple[float, float], v2: tuple[float, float]
) -> tuple[float, float, float]:
    """
    Returns the cross product of vector v1 with vector v2.
    """
    return (0, 0, v1[0] * v2[1] - v1[1] * v2[0])

Returns the cross product of vector v1 with vector v2.

def distance(v1: tuple[float, float], v2: tuple[float, float]) ‑> float
Expand source code
@staticmethod
def distance(v1: tuple[float, float], v2: tuple[float, float]) -> float:
    """
    Returns the distance between vector v1 and vector v2.
    """
    x = v2[0] - v1[0]
    y = v2[1] - v1[1]
    return math.sqrt(x * x + y * y)

Returns the distance between vector v1 and vector v2.

def div(v1: tuple[float, float], v2: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def div(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
    """
    Returns a vector that is vector v1 divided by vector v2.
    """
    return (v1[0] / v2[0], v1[1] / v2[1])

Returns a vector that is vector v1 divided by vector v2.

def dot(v1: tuple[float, float], v2: tuple[float, float]) ‑> float
Expand source code
@staticmethod
def dot(v1: tuple[float, float], v2: tuple[float, float]) -> float:
    """
    Returns the dot product of vector v1 with vector v2.
    """
    return v1[0] * v2[0] + v1[1] * v2[1]

Returns the dot product of vector v1 with vector v2.

def length(v1: tuple[float, float]) ‑> float
Expand source code
@staticmethod
def length(v1: tuple[float, float]) -> float:
    """
    Returns the length of vector v1.
    """
    return math.sqrt(v1[0] * v1[0] + v1[1] * v1[1])

Returns the length of vector v1.

def mul(v1: tuple[float, float], v2: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def mul(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
    """
    Returns a vector that is vector v1 multiplied by vector v2.
    """
    return (v1[0] * v2[0], v1[1] * v2[1])

Returns a vector that is vector v1 multiplied by vector v2.

def neg(v1: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def neg(v1: tuple[float, float]) -> tuple[float, float]:
    """
    Returns a vector that is the negation of vector v1.
    """
    return (-v1[0], -v1[1])

Returns a vector that is the negation of vector v1.

def normalize(v1: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def normalize(v1: tuple[float, float]) -> tuple[float, float]:
    """
    Returns a normalized (unit length) vector in the same direction as vector v1.
    If the vector has zero length, returns a zero vector.
    """
    length = math.sqrt(v1[0] * v1[0] + v1[1] * v1[1])
    if length > 0:
        return (v1[0] / length, v1[1] / length)
    return (0, 0)

Returns a normalized (unit length) vector in the same direction as vector v1. If the vector has zero length, returns a zero vector.

def pos(v1: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def pos(v1: tuple[float, float]) -> tuple[float, float]:
    """
    Returns a vector that is the positive of vector v1 (same as vector v1).
    """
    return v1

Returns a vector that is the positive of vector v1 (same as vector v1).

def squaredDistance(v1: tuple[float, float], v2: tuple[float, float]) ‑> float
Expand source code
@staticmethod
def squaredDistance(v1: tuple[float, float], v2: tuple[float, float]) -> float:
    x = v2[0] - v1[0]
    y = v2[1] - v1[1]
    return x * x + y * y
def squaredLength(v1: tuple[float, float]) ‑> float
Expand source code
@staticmethod
def squaredLength(v1: tuple[float, float]) -> float:
    return v1[0] * v1[0] + v1[1] * v1[1]
def sub(v1: tuple[float, float], v2: tuple[float, float]) ‑> tuple[float, float]
Expand source code
@staticmethod
def sub(v1: tuple[float, float], v2: tuple[float, float]) -> tuple[float, float]:
    """
    Returns a vector that is vector v1 subtracted by vector v2.
    """
    return (v1[0] - v2[0], v1[1] - v2[1])

Returns a vector that is vector v1 subtracted by vector v2.

class Vec3
Expand source code
class Vec3:
    """
    Namespace holding functions that operate on plain 3-tuples.
    """

    @staticmethod
    def abs(v1: tuple[float, float, float]) -> tuple[float, float, float]:
        """
        Returns a vector that is the absolute value of the given vector.
        """
        return (math.fabs(v1[0]), math.fabs(v1[1]), math.fabs(v1[2]))

    @staticmethod
    def add(
        v1: tuple[float, float, float], v2: tuple[float, float, float]
    ) -> tuple[float, float, float]:
        """
        Returns a vector that is vector v1 added to vector v2)
        """
        return (v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2])

    @staticmethod
    def mul(
        v1: tuple[float, float, float], v2: tuple[float, float, float]
    ) -> tuple[float, float, float]:
        """
        Returns a vector that is vector v1 multiplied by vector v2.
        """
        return (v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2])

    @staticmethod
    def neg(v1: tuple[float, float, float]) -> tuple[float, float, float]:
        """
        Returns a vector that is the negation of vector v1.
        """
        return (-v1[0], -v1[1], -v1[2])

    @staticmethod
    def pos(v1: tuple[float, float, float]) -> tuple[float, float, float]:
        """
        Returns a vector that is the positive of vector v1 (same as vector v1).
        """
        return v1

    @staticmethod
    def sub(
        v1: tuple[float, float, float], v2: tuple[float, float, float]
    ) -> tuple[float, float, float]:
        """
        Subtracts vector v2 from vector v1.
        """
        return (v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2])

    @staticmethod
    def div(
        v1: tuple[float, float, float], v2: tuple[float, float, float]
    ) -> tuple[float, float, float]:
        """
        Returns a vector that is vector v1 divided by vector v2.
        """
        return (v1[0] / v2[0], v1[1] / v2[1], v1[2] / v2[2])

    @staticmethod
    def cross(
        v1: tuple[float, float, float], v2: tuple[float, float, float]
    ) -> tuple[float, float, float]:
        """
        Computes the cross product of vector v1 with vector v2.
        """
        ax = v1[0]
        ay = v1[1]
        az = v1[2]
        bx = v2[0]
        by = v2[1]
        bz = v2[2]

        return (ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx)

    @staticmethod
    def distance(
        v1: tuple[float, float, float], v2: tuple[float, float, float]
    ) -> float:
        """
        Calculates the Euclidian distance between vector v1 and v2 vector.
        """
        x = v2[0] - v1[0]
        y = v2[1] - v1[1]
        z = v2[2] - v1[2]
        return (x * x + y * y + z * z) ** 0.5

    @staticmethod
    def dot(v1: tuple[float, float, float], v2: tuple[float, float, float]) -> float:
        """
        Calculates the dot product of vector v1 with vector v2.
        """
        return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]

    @staticmethod
    def length(v1: tuple[float, float, float]) -> float:
        """
        Calculates the length (magnitude) of the vector.
        """
        return (v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) ** 0.5

    @staticmethod
    def normalize(v1: tuple[float, float, float]) -> tuple[float, float, float]:
        """
        Normalizes the vector (makes it have a length of 1).
        """
        x = v1[0]
        y = v1[1]
        z = v1[2]
        len = x * x + y * y + z * z
        if len > 0:
            len = 1 / (len**0.5)
        return (x * len, y * len, z * len)

    @staticmethod
    def squaredDistance(
        v1: tuple[float, float, float], v2: tuple[float, float, float]
    ) -> float:
        """
        Calculates the squared Euclidian distance between v1 vector and v2 vector.
        """
        x = v2[0] - v1[0]
        y = v2[1] - v1[1]
        z = v2[2] - v1[2]
        return x * x + y * y + z * z

    @staticmethod
    def squaredLength(v1: tuple[float, float, float]) -> float:
        """
        Calculates the squared length of the vector.
        """
        x = v1[0]
        y = v1[1]
        z = v1[2]
        return x * x + y * y + z * z

Namespace holding functions that operate on plain 3-tuples.

Static methods

def abs(v1: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def abs(v1: tuple[float, float, float]) -> tuple[float, float, float]:
    """
    Returns a vector that is the absolute value of the given vector.
    """
    return (math.fabs(v1[0]), math.fabs(v1[1]), math.fabs(v1[2]))

Returns a vector that is the absolute value of the given vector.

def add(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def add(
    v1: tuple[float, float, float], v2: tuple[float, float, float]
) -> tuple[float, float, float]:
    """
    Returns a vector that is vector v1 added to vector v2)
    """
    return (v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2])

Returns a vector that is vector v1 added to vector v2)

def cross(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def cross(
    v1: tuple[float, float, float], v2: tuple[float, float, float]
) -> tuple[float, float, float]:
    """
    Computes the cross product of vector v1 with vector v2.
    """
    ax = v1[0]
    ay = v1[1]
    az = v1[2]
    bx = v2[0]
    by = v2[1]
    bz = v2[2]

    return (ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx)

Computes the cross product of vector v1 with vector v2.

def distance(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> float
Expand source code
@staticmethod
def distance(
    v1: tuple[float, float, float], v2: tuple[float, float, float]
) -> float:
    """
    Calculates the Euclidian distance between vector v1 and v2 vector.
    """
    x = v2[0] - v1[0]
    y = v2[1] - v1[1]
    z = v2[2] - v1[2]
    return (x * x + y * y + z * z) ** 0.5

Calculates the Euclidian distance between vector v1 and v2 vector.

def div(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def div(
    v1: tuple[float, float, float], v2: tuple[float, float, float]
) -> tuple[float, float, float]:
    """
    Returns a vector that is vector v1 divided by vector v2.
    """
    return (v1[0] / v2[0], v1[1] / v2[1], v1[2] / v2[2])

Returns a vector that is vector v1 divided by vector v2.

def dot(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> float
Expand source code
@staticmethod
def dot(v1: tuple[float, float, float], v2: tuple[float, float, float]) -> float:
    """
    Calculates the dot product of vector v1 with vector v2.
    """
    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]

Calculates the dot product of vector v1 with vector v2.

def length(v1: tuple[float, float, float]) ‑> float
Expand source code
@staticmethod
def length(v1: tuple[float, float, float]) -> float:
    """
    Calculates the length (magnitude) of the vector.
    """
    return (v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) ** 0.5

Calculates the length (magnitude) of the vector.

def mul(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def mul(
    v1: tuple[float, float, float], v2: tuple[float, float, float]
) -> tuple[float, float, float]:
    """
    Returns a vector that is vector v1 multiplied by vector v2.
    """
    return (v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2])

Returns a vector that is vector v1 multiplied by vector v2.

def neg(v1: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def neg(v1: tuple[float, float, float]) -> tuple[float, float, float]:
    """
    Returns a vector that is the negation of vector v1.
    """
    return (-v1[0], -v1[1], -v1[2])

Returns a vector that is the negation of vector v1.

def normalize(v1: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def normalize(v1: tuple[float, float, float]) -> tuple[float, float, float]:
    """
    Normalizes the vector (makes it have a length of 1).
    """
    x = v1[0]
    y = v1[1]
    z = v1[2]
    len = x * x + y * y + z * z
    if len > 0:
        len = 1 / (len**0.5)
    return (x * len, y * len, z * len)

Normalizes the vector (makes it have a length of 1).

def pos(v1: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def pos(v1: tuple[float, float, float]) -> tuple[float, float, float]:
    """
    Returns a vector that is the positive of vector v1 (same as vector v1).
    """
    return v1

Returns a vector that is the positive of vector v1 (same as vector v1).

def squaredDistance(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> float
Expand source code
@staticmethod
def squaredDistance(
    v1: tuple[float, float, float], v2: tuple[float, float, float]
) -> float:
    """
    Calculates the squared Euclidian distance between v1 vector and v2 vector.
    """
    x = v2[0] - v1[0]
    y = v2[1] - v1[1]
    z = v2[2] - v1[2]
    return x * x + y * y + z * z

Calculates the squared Euclidian distance between v1 vector and v2 vector.

def squaredLength(v1: tuple[float, float, float]) ‑> float
Expand source code
@staticmethod
def squaredLength(v1: tuple[float, float, float]) -> float:
    """
    Calculates the squared length of the vector.
    """
    x = v1[0]
    y = v1[1]
    z = v1[2]
    return x * x + y * y + z * z

Calculates the squared length of the vector.

def sub(v1: tuple[float, float, float], v2: tuple[float, float, float]) ‑> tuple[float, float, float]
Expand source code
@staticmethod
def sub(
    v1: tuple[float, float, float], v2: tuple[float, float, float]
) -> tuple[float, float, float]:
    """
    Subtracts vector v2 from vector v1.
    """
    return (v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2])

Subtracts vector v2 from vector v1.