Skip to content

Commit

Permalink
Merge pull request #137 from villares/pvector-wrapper
Browse files Browse the repository at this point in the history
Adding PVector wrapper/helper class to pyodide mode
  • Loading branch information
berinhard authored Apr 17, 2021
2 parents dd440ea + a2990ee commit 1cfdb61
Showing 1 changed file with 321 additions and 0 deletions.
321 changes: 321 additions & 0 deletions docs/pyodide/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,327 @@
pushMatrix = push
pushStyle = push
# PVector is a wrapper/helper class for p5.Vector objets
# providing names similar to Processing Python or Java modes
# but mostly keeping p5js functionality
from numbers import Number
class PVector:
def __init__(self, x=0, y=0, z=0):
self.__vector = createVector(x, y, z)
self.add = self.__instance_add__
self.sub = self.__instance_sub__
self.mult = self.__instance_mult__
self.div = self.__instance_div__
self.cross = self.__instance_cross__
self.dist = self.__instance_dist__
self.dot = self.__instance_dot__
self.lerp = self.__instance_lerp__
@property
def x(self):
return self.__vector.x
@x.setter
def x(self, x):
self.__vector.x = x
@property
def y(self):
return self.__vector.y
@y.setter
def y(self, y):
self.__vector.y = y
@property
def z(self):
return self.__vector.z
@z.setter
def z(self, z):
self.__vector.z = z
def mag(self):
return self.__vector.mag()
def magSq(self):
return self.__vector.magSq()
def setMag(self, mag):
self.__vector.setMag(mag)
return self
def normalize(self):
self.__vector.normalize()
return self
def limit(self, max):
self.__vector.limit(max)
return self
def heading(self):
return self.__vector.heading()
def rotate(self, angle):
self.__vector.rotate(angle)
return self
def __instance_add__(self, *args):
if len(args) == 1:
return PVector.add(self, args[0], self)
else:
return PVector.add(self, PVector(*args), self)
def __instance_sub__(self, *args):
if len(args) == 1:
return PVector.sub(self, args[0], self)
else:
return PVector.sub(self, PVector(*args), self)
def __instance_mult__(self, o):
return PVector.mult(self, o, self)
def __instance_div__(self, f):
return PVector.div(self, f, self)
def __instance_cross__(self, o):
return PVector.cross(self, o, self)
def __instance_dist__(self, o):
return PVector.dist(self, o)
def __instance_dot__(self, *args):
if len(args) == 1:
v = args[0]
else:
v = args
return self.x * v[0] + self.y * v[1] + self.z * v[2]
def __instance_lerp__(self, *args):
if len(args) == 2:
return PVector.lerp(self, args[0], args[1], self)
else:
vx, vy, vz, f = args
return PVector.lerp(self, PVector(vx, vy, vz), f, self)
def get(self):
return PVector(self.x, self.y, self.z)
def copy(self):
return PVector(self.x, self.y, self.z)
def __getitem__(self, k):
return getattr(self, ('x', 'y', 'z')[k])
def __setitem__(self, k, v):
setattr(self, ('x', 'y', 'z')[k], v)
def __copy__(self):
return PVector(self.x, self.y, self.z)
def __deepcopy__(self, memo):
return PVector(self.x, self.y, self.z)
def __repr__(self): # PROVISÓRIO
return f'PVector({self.x}, {self.y}, {self.z})'
def set(self, *args):
"""
Sets the x, y, and z component of the vector using two or three separate
variables, the data from a p5.Vector, or the values from a float array.
"""
self.__vector.set(*args)
@classmethod
def add(cls, a, b, dest=None):
if dest is None:
return PVector(a.x + b[0], a.y + b[1], a.z + b[2])
dest.__vector.set(a.x + b[0], a.y + b[1], a.z + b[2])
return dest
@classmethod
def sub(cls, a, b, dest=None):
if dest is None:
return PVector(a.x - b[0], a.y - b[1], a.z - b[2])
dest.__vector.set(a.x - b[0], a.y - b[1], a.z - b[2])
return dest
@classmethod
def mult(cls, a, b, dest=None):
if dest is None:
return PVector(a.x * b, a.y * b, a.z * b)
dest.__vector.set(a.x * b, a.y * b, a.z * b)
return dest
@classmethod
def div(cls, a, b, dest=None):
if dest is None:
return PVector(a.x / b, a.y / b, a.z / b)
dest.__vector.set(a.x / b, a.y / b, a.z / b)
return dest
@classmethod
def dist(cls, a, b):
return a.__vector.dist(b.__vector)
@classmethod
def dot(cls, a, b):
return a.__vector.dot(b.__vector)
def __add__(a, b):
return PVector.add(a, b, None)
def __sub__(a, b):
return PVector.sub(a, b, None)
def __isub__(a, b):
a.sub(b)
return a
def __iadd__(a, b):
a.add(b)
return a
def __mul__(a, b):
if not isinstance(b, Number):
raise TypeError(
"The * operator can only be used to multiply a PVector by a number")
return PVector.mult(a, float(b), None)
def __rmul__(a, b):
if not isinstance(b, Number):
raise TypeError(
"The * operator can only be used to multiply a PVector by a number")
return PVector.mult(a, float(b), None)
def __imul__(a, b):
if not isinstance(b, Number):
raise TypeError(
"The *= operator can only be used to multiply a PVector by a number")
a.__vector.mult(float(b))
return a
def __truediv__(a, b):
if not isinstance(b, Number):
raise TypeError(
"The * operator can only be used to multiply a PVector by a number")
return PVector(a.x / float(b), a.y / float(b), a.z / float(b))
def __itruediv__(a, b):
if not isinstance(b, Number):
raise TypeError(
"The /= operator can only be used to multiply a PVector by a number")
a.__vector.set(a.x / float(b), a.y / float(b), a.z / float(b))
return a
def __eq__(a, b):
return a.x == b[0] and a.y == b[1] and a.z == b[2]
def __lt__(a, b):
return a.magSq() < b.magSq()
def __le__(a, b):
return a.magSq() <= b.magSq()
def __gt__(a, b):
return a.magSq() > b.magSq()
def __ge__(a, b):
return a.magSq() >= b.magSq()
# Problematic class methods, we would rather use p5.Vector when possible...
@classmethod
def lerp(cls, a, b, f, dest=None):
v = createVector(a.x, a.y, a.z)
v.lerp(b.__vector, f)
if dest is None:
return PVector(v.x, v.y, v.z)
dest.set(v.x, v.y, v.z)
return dest
@classmethod
def cross(cls, a, b, dest=None):
x = a.y * b[2] - b[1] * a.z
y = a.z * b[0] - b[2] * a.x
z = a.x * b[1] - b[0] * a.y
if dest is None:
return PVector(x, y, z)
dest.set(x, y, z)
return dest
@classmethod
def fromAngle(cls, angle, length=1):
# https://github.com/processing/p5.js/blob/3f0b2f0fe575dc81c724474154f5b23a517b7233/src/math/p5.Vector.js
return PVector(length * cos(angle), length * sin(angle), 0)
@classmethod
def fromAngles(theta, phi, length=1):
# https://github.com/processing/p5.js/blob/3f0b2f0fe575dc81c724474154f5b23a517b7233/src/math/p5.Vector.js
cosPhi = cos(phi)
sinPhi = sin(phi)
cosTheta = cos(theta)
sinTheta = sin(theta)
return PVector(length * sinTheta * sinPhi,
-length * cosTheta,
length * sinTheta * cosPhi)
@classmethod
def random2D(cls):
return PVector.fromAngle(random(TWO_PI))
@classmethod
def random3D(cls, dest=None):
angle = random(TWO_PI)
vz = random(2) - 1
mult = sqrt(1 - vz * vz)
vx = mult * cos(angle)
vy = mult * sin(angle)
if dest is None:
return PVector(vx, vy, vz)
dest.set(vx, vy, vz)
return dest
@classmethod
def angleBetween(cls, a, b):
return acos(a.dot(b) / sqrt(a.magSq() * b.magSq()))
# Other harmless p5js methods
def equals(self, v):
return self == v
def heading2D(self):
return self.__vector.heading()
def reflect(self, *args):
# Reflect the incoming vector about a normal to a line in 2D, or about
# a normal to a plane in 3D This method acts on the vector directly
r = self.__vector.reflect(*args)
return r
def array(self):
# Return a representation of this vector as a float array. This is only
# for temporary use. If used in any w fashion, the contents should be
# copied by using the p5.Vector.copy() method to copy into your own
# array.
return self.__vector.array()
def toString(self):
# Returns a string representation of a vector v by calling String(v) or v.toString().
# return self.__vector.toString() would be something like "p5.vector
# Object […, …, …]"
return str(self)
def rem(self, *args):
# Gives remainder of a vector when it is divided by anw vector. See
# examples for more context.
self.__vector.rem(*args)
return self
def pre_draw(p5_instance, draw_func):
"""
We need to run this before the actual draw to insert and update p5 env variables
Expand Down

0 comments on commit 1cfdb61

Please sign in to comment.