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