diff --git a/docs/pyodide/index.html b/docs/pyodide/index.html
index de2163ca..d6cdf177 100644
--- a/docs/pyodide/index.html
+++ b/docs/pyodide/index.html
@@ -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