Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding PVector wrapper/helper class to pyodide mode #137

Merged
merged 2 commits into from
Apr 17, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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