Skip to content

Instantly share code, notes, and snippets.

@bernhard-42
Last active October 15, 2022 09:13
Show Gist options
  • Save bernhard-42/962a3a9dc0a26790fe898d6e990bdccf to your computer and use it in GitHub Desktop.
Save bernhard-42/962a3a9dc0a26790fe898d6e990bdccf to your computer and use it in GitHub Desktop.
A PEP8 facade for CadQuery Vector class
import math
from typing import Union, Sequence, overload
import cadquery as cq
from OCP.gp import gp, gp_Vec, gp_Pnt, gp_Dir, gp_XYZ
class Vector:
"""Create a 3-dimensional vector
Args:
args: a 3D vector, with x-y-z parts.
you can either provide:
* nothing (in which case the null vector is return)
* a gp_Vec
* a vector ( in which case it is copied )
* a 3-tuple
* a 2-tuple (z assumed to be 0)
* three float values: x, y, and z
* two float values: x,y
Returns:
"""
@overload
def __init__(self, X: float, Y: float, Z: float) -> None:
...
@overload
def __init__(self, X: float, Y: float) -> None:
...
@overload
def __init__(self, v: "Vector") -> None:
...
@overload
def __init__(self, v: Sequence[float]) -> None:
...
@overload
def __init__(self, v: Union[gp_Vec, gp_Pnt, gp_Dir, gp_XYZ]) -> None:
...
@overload
def __init__(self) -> None:
...
# ADDITIONAL
@overload
def __init__(self, v: cq.Vector) -> None: # cq.Vector => Vector
...
def __init__(self, *args):
# ADDITIONAL
if len(args) == 1 and isinstance(args[0], cq.Vector): # cq.Vector => Vector
v = args[0]
self._vector = cq.Vector(v.x, v.y, v.z)
else:
self._vector = cq.Vector(*args)
@property
def X(self) -> float:
return self._vector.x
@X.setter
def X(self, value: float) -> None:
self._compound = value
@property
def Y(self) -> float:
return self._vector.y
@Y.setter
def Y(self, value: float) -> None:
self._vector.y = value
@property
def Z(self) -> float:
return self._vector.z
@Z.setter
def Z(self, value: float) -> None:
self._vector.z = value
@property
def length(self) -> float:
return self._vector.length
@property
def wrapped(self) -> gp_Vec:
return self._vector._wrapped
def to_tuple(self) -> tuple[float, float, float]:
return self._vector.toTuple()
def cross(self, v: "Vector") -> "Vector":
return Vector(self._vector.cross(v._vector))
def dot(self, v: "Vector") -> float:
return self._vector.dot(v._vector)
def sub(self, v: "Vector") -> "Vector":
return Vector(self._vector.sub(v._vector))
def __sub__(self, v: "Vector") -> "Vector":
return Vector(self._vector.__sub__(v._vector))
def add(self, v: "Vector") -> "Vector":
return Vector(self._vector.add(v._vector))
def __add__(self, v: "Vector") -> "Vector":
return self._vector.__add__(v._vector)
def multiply(self, scale: float) -> "Vector":
return Vector(self._vector.multiply(scale))
def __mul__(self, scale: float) -> "Vector":
return Vector(self._vector.__mul__(scale))
def __truediv__(self, denom: float) -> "Vector":
return Vector(self._vector.__truediv__(denom))
def __rmul__(self, scale: float) -> "Vector":
return Verctor(self._vector.__rmul__(scale))
def normalized(self) -> "Vector":
return Vector(self._vector.normalized())
def center(self) -> "Vector":
return self
def get_angle(self, v: "Vector") -> float:
return self._vector.getAngle(v._vector)
def get_signed_angle(self, v: "Vector", normal: "Vector" = None) -> float:
"""Signed Angle Between Vectors
Return the signed angle in RADIANS between two vectors with the given normal
based on this math: angle = atan2((Va × Vb) ⋅ Vn, Va ⋅ Vb)
Args:
v: Second Vector
normal: Vector
v: Vector:
normal: Vector: (Default value = None)
Returns:
Angle between vectors
"""
return self._vector.get_signed_angle(v._vector, normal._vector)
def distance_to_line(self):
return self._vector.distanceToLine()
def project_to_line(self, line: "Vector") -> "Vector":
"""Returns a new vector equal to the projection of this Vector onto the line
represented by Vector <line>
Args:
args: Vector
Returns the projected vector.
line: Vector:
Returns:
"""
return Vector(self._vector(line._vector))
def distance_to_plane(self):
return self._vector.distanceToPlane()
def project_to_plane(self, plane: cq.Plane) -> "Vector": # to be changed when Matrix is available
"""Vector is projected onto the plane provided as input.
Args:
args: Plane object
Returns the projected vector.
plane: Plane:
Returns:
"""
return Vector(self._vector.projectToLine(plane))
def __neg__(self) -> "Vector":
return Vector(self._vector.__neg__())
def __abs__(self) -> float:
return self._vector.__abs__()
def __repr__(self) -> str:
return "Vector: " + str((self.X, self.Y, self.Z))
def __str__(self) -> str:
return "Vector: " + str((self.X, self.Y, self.Z))
def __eq__(self, other: "Vector") -> bool: # type: ignore[override]
return self._vector == other._vector
def to_pnt(self) -> gp_Pnt:
return self._vector.toPnt()
def to_dir(self) -> gp_Dir:
return self._vector.toDir()
def transform(self, t: cq.Matrix) -> "Vector": # to be changed when Matrix is available
return Vector(self._vector.transform(t))
def rotate_x(self, angle: float) -> "Vector":
"""Rotate Vector about X-Axis
Args:
angle: Angle in degrees
angle: float:
Returns:
: Rotated Vector
"""
return Vector(gp_Vec(self.X, self.Y, self.Z).Rotated(gp.OX_s(), math.pi * angle / 180))
def rotate_y(self, angle: float) -> "Vector":
"""Rotate Vector about Y-Axis
Args:
angle: Angle in degrees
angle: float:
Returns:
: Rotated Vector
"""
return Vector(gp_Vec(self.X, self.Y, self.Z).Rotated(gp.OY_s(), math.pi * angle / 180))
def rotate_z(self, angle: float) -> "Vector":
"""Rotate Vector about Z-Axis
Args:
angle: Angle in degrees
angle: float:
Returns:
: Rotated Vector
"""
return Vector(gp_Vec(self.X, self.Y, self.Z).Rotated(gp.OZ_s(), math.pi * angle / 180))
if __name__ == "__main__":
v1 = Vector(1, 2, 3)
v2 = Vector(3, 2, 1)
v3 = Vector(1, 2, 3)
print(v1)
print(v1.to_tuple())
print(v1 * 3)
print(v1 + v2)
print(v1.cross(v2))
print(v1.dot(v2))
print(v1 == v2)
print(v1 == v1)
print(v1 == v3)
print(v1.to_dir())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment