Module sverchok.utils.field.vector
Expand source code
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
from math import sqrt, copysign, pi
import numpy as np
from mathutils import Vector
from mathutils import bvhtree
from mathutils import kdtree
from mathutils import noise
from sverchok.utils.curve import SvCurveLengthSolver, SvNormalTrack, MathutilsRotationCalculator
from sverchok.utils.geom import LineEquation, CircleEquation3D
from sverchok.utils.math import from_cylindrical, from_spherical, np_dot
from sverchok.utils.kdtree import SvKdTree
from sverchok.utils.field.voronoi import SvVoronoiFieldData
##################
# #
# Vector Fields #
# #
##################
class SvVectorField(object):
def __repr__(self):
if hasattr(self, '__description__'):
description = self.__description__
else:
description = self.__class__.__name__
return "<{} vector field>".format(description)
def evaluate(self, point):
raise Exception("not implemented")
def evaluate_grid(self, xs, ys, zs):
raise Exception("not implemented")
def evaluate_array(self, points):
xs = points[:,0]
ys = points[:,1]
zs = points[:,2]
return self.evaluate_grid(xs, ys, zs)
class SvMatrixVectorField(SvVectorField):
def __init__(self, matrix):
self.matrix = matrix
self.__description__ = "Matrix"
def evaluate(self, x, y, z):
v = Vector((x, y, z))
v = (self.matrix @ v) - v
return np.array(v)
def evaluate_grid(self, xs, ys, zs):
matrix = np.array(self.matrix.to_3x3())
translation = np.array(self.matrix.translation)
points = np.stack((xs, ys, zs)).T
R = np.apply_along_axis(lambda v : matrix @ v + translation - v, 1, points).T
return R[0], R[1], R[2]
class SvConstantVectorField(SvVectorField):
def __init__(self, vector):
self.vector = np.array(vector)
self.__description__ = "Constant = {}".format(vector)
def evaluate(self, x, y, z):
return self.vector
def evaluate_grid(self, xs, ys, zs):
x, y, z = self.vector
rx = np.full_like(xs, x)
ry = np.full_like(ys, y)
rz = np.full_like(zs, z)
return rx, ry, rz
class SvComposedVectorField(SvVectorField):
def __init__(self, coords, sfield1, sfield2, sfield3):
self.coords = coords
self.sfield1 = sfield1
self.sfield2 = sfield2
self.sfield3 = sfield3
self.__description__ = "{}({}, {}, {})".format(coords, sfield1, sfield2, sfield3)
def evaluate(self, x, y, z):
v1 = self.sfield1.evaluate(x, y, z)
v2 = self.sfield2.evaluate(x, y, z)
v3 = self.sfield3.evaluate(x, y, z)
if self.coords == 'XYZ':
return np.array((v1, v2, v3))
elif self.coords == 'CYL':
return np.array(from_cylindrical(v1, v2, v3, mode='radians'))
else: # SPH:
return np.array(from_spherical(v1, v2, v3, mode='radians'))
def evaluate_grid(self, xs, ys, zs):
v1s = self.sfield1.evaluate_grid(xs, ys, zs)
v2s = self.sfield2.evaluate_grid(xs, ys, zs)
v3s = self.sfield3.evaluate_grid(xs, ys, zs)
if self.coords == 'XYZ':
return v1s, v2s, v3s
elif self.coords == 'CYL':
vectors = np.stack((v1s, v2s, v3s)).T
vectors = np.apply_along_axis(lambda v: np.array(from_cylindrical(*tuple(v), mode='radians')), 1, vectors).T
return vectors[0], vectors[1], vectors[2]
else: # SPH:
vectors = np.stack((v1s, v2s, v3s)).T
vectors = np.apply_along_axis(lambda v: np.array(from_spherical(*tuple(v), mode='radians')), 1, vectors).T
return vectors[0], vectors[1], vectors[2]
class SvAbsoluteVectorField(SvVectorField):
def __init__(self, field):
self.field = field
self.__description__ = "Absolute({})".format(field)
def evaluate(self, x, y, z):
r = self.field.evaluate(x, y, z)
return r + np.array([x, y, z])
def evaluate_grid(self, xs, ys, zs):
rxs, rys, rzs = self.field.evaluate_grid(xs, ys, zs)
return rxs + xs, rys + ys, rzs + zs
class SvRelativeVectorField(SvVectorField):
def __init__(self, field):
self.field = field
self.__description__ = "Relative({})".format(field)
def evaluate(self, x, y, z):
r = self.field.evaluate(x, y, z)
return r - np.array([x, y, z])
def evaluate_grid(self, xs, ys, zs):
rxs, rys, rzs = self.field.evaluate_grid(xs, ys, zs)
return rxs - xs, rys - ys, rzs - zs
class SvVectorFieldLambda(SvVectorField):
__description__ = "Formula"
def __init__(self, function, variables, in_field, function_numpy = None):
self.function = function
self.function_numpy = function_numpy
self.variables = variables
self.in_field = in_field
def evaluate_grid(self, xs, ys, zs):
if self.in_field is None:
Vs = np.zeros(xs.shape[0])
else:
vx, vy, vz = self.in_field.evaluate_grid(xs, ys, zs)
Vs = np.stack((vx, vy, vz)).T
if self.function_numpy is None:
return np.vectorize(self.function,
signature = "(),(),(),(3)->(),(),()")(xs, ys, zs, Vs)
else:
Vs = Vs.T
return self.function_numpy(xs, ys, zs, Vs)
def evaluate(self, x, y, z):
if self.in_field is None:
V = None
else:
V = self.in_field.evaluate(x, y, z)
return np.array(self.function(x, y, z, V))
class SvVectorFieldBinOp(SvVectorField):
def __init__(self, field1, field2, function):
self.function = function
self.field1 = field1
self.field2 = field2
self.__description__ = f"<BinOp ({field1}, {field2})>"
def evaluate(self, x, y, z):
return self.function(self.field1.evaluate(x, y, z), self.field2.evaluate(x, y, z))
def evaluate_grid(self, xs, ys, zs):
def func(xs, ys, zs):
vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs)
vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs)
R = self.function(np.array([vx1, vy1, vz1]), np.array([vx2, vy2, vz2]))
return R[0], R[1], R[2]
return np.vectorize(func, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
class SvAverageVectorField(SvVectorField):
def __init__(self, fields):
self.fields = fields
self.__description__ = "Average"
def evaluate(self, x, y, z):
vectors = np.array([field.evaluate(x, y, z) for field in self.fields])
return np.mean(vectors, axis=0)
def evaluate_grid(self, xs, ys, zs):
def func(xs, ys, zs):
data = []
for field in self.fields:
vx, vy, vz = field.evaluate_grid(xs, ys, zs)
vectors = np.stack((vx, vy, vz)).T
data.append(vectors)
data = np.array(data)
mean = np.mean(data, axis=0).T
return mean[0], mean[1], mean[2]
return np.vectorize(func, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
class SvVectorFieldCrossProduct(SvVectorField):
def __init__(self, field1, field2):
self.field1 = field1
self.field2 = field2
self.__description__ = "{} x {}".format(field1, field2)
def evaluate(self, x, y, z):
v1 = self.field1.evaluate(x, y, z)
v2 = self.field2.evaluate(x, y, z)
return np.cross(v1, v2)
def evaluate_grid(self, xs, ys, zs):
vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs)
vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs)
vectors1 = np.stack((vx1, vy1, vz1)).T
vectors2 = np.stack((vx2, vy2, vz2)).T
R = np.cross(vectors1, vectors2).T
return R[0], R[1], R[2]
class SvVectorFieldMultipliedByScalar(SvVectorField):
def __init__(self, vector_field, scalar_field):
self.vector_field = vector_field
self.scalar_field = scalar_field
self.__description__ = "{} * {}".format(scalar_field, vector_field)
def evaluate(self, x, y, z):
scalar = self.scalar_field.evaluate(x, y, z)
vector = self.vector_field.evaluate(x, y, z)
return scalar * vector
def evaluate_grid(self, xs, ys, zs):
def product(xs, ys, zs):
scalars = self.scalar_field.evaluate_grid(xs, ys, zs)
vx, vy, vz = self.vector_field.evaluate_grid(xs, ys, zs)
vectors = np.stack((vx, vy, vz))
R = (scalars * vectors)
return R[0], R[1], R[2]
return np.vectorize(product, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
class SvVectorFieldsLerp(SvVectorField):
def __init__(self, vfield1, vfield2, scalar_field):
self.vfield1 = vfield1
self.vfield2 = vfield2
self.scalar_field = scalar_field
self.__description__ = "Lerp"
def evaluate(self, x, y, z):
scalar = self.scalar_field.evaluate(x, y, z)
vector1 = self.vfield1.evaluate(x, y, z)
vector2 = self.vfield2.evaluate(x, y, z)
return (1 - scalar) * vector1 + scalar * vector2
def evaluate_grid(self, xs, ys, zs):
scalars = self.scalar_field.evaluate_grid(xs, ys, zs)
vx1, vy1, vz1 = self.vfield1.evaluate_grid(xs, ys, zs)
vectors1 = np.stack((vx1, vy1, vz1))
vx2, vy2, vz2 = self.vfield2.evaluate_grid(xs, ys, zs)
vectors2 = np.stack((vx2, vy2, vz2))
R = (1 - scalars) * vectors1 + scalars * vectors2
return R[0], R[1], R[2]
class SvNoiseVectorField(SvVectorField):
def __init__(self, noise_type, seed):
self.noise_type = noise_type
self.seed = seed
self.__description__ = "{} noise".format(noise_type)
def evaluate(self, x, y, z):
noise.seed_set(self.seed)
v = noise.noise_vector((x, y, z), noise_basis=self.noise_type)
return np.array(v)
def evaluate_grid(self, xs, ys, zs):
noise.seed_set(self.seed)
def mk_noise(v):
r = noise.noise_vector(v, noise_basis=self.noise_type)
return r[0], r[1], r[2]
vectors = np.stack((xs,ys,zs)).T
return np.vectorize(mk_noise, signature="(3)->(),(),()")(vectors)
class SvKdtVectorField(SvVectorField):
def __init__(self, vertices=None, kdt=None, falloff=None, negate=False, power=2):
self.falloff = falloff
self.negate = negate
if kdt is not None:
self.kdt = kdt
elif vertices is not None:
self.kdt = SvKdTree.new(SvKdTree.best_available_implementation(), vertices, power=power)
else:
raise Exception("Either kdt or vertices must be provided")
self.__description__ = "KDT Attractor"
def evaluate(self, x, y, z):
nearest, i, distance = self.kdt.query(np.array([x, y, z]))
vector = nearest - np.array([x, y, z])
if self.falloff is not None:
value = self.falloff(np.array([distance]))[0]
if self.negate:
value = - value
norm = np.linalg.norm(vector)
return value * vector / norm
else:
if self.negate:
return - vector
else:
return vector
def evaluate_grid(self, xs, ys, zs):
points = np.stack((xs, ys, zs)).T
locs, idxs, distances = self.kdt.query_array(points)
vectors = locs - points
if self.negate:
vectors = - vectors
if self.falloff is not None:
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
lens = self.falloff(norms)
nonzero = (norms > 0)[:,0]
lens = self.falloff(norms)
vectors[nonzero] = vectors[nonzero] / norms[nonzero]
R = (lens * vectors).T
return R[0], R[1], R[2]
else:
R = vectors.T
return R[0], R[1], R[2]
class SvVectorFieldPointDistance(SvVectorField):
def __init__(self, center, metric='EUCLIDEAN', falloff=None, power=2):
self.center = center
self.falloff = falloff
self.metric = metric
self.power = power
self.__description__ = "Distance from {}".format(tuple(center))
def evaluate_grid(self, xs, ys, zs):
x0, y0, z0 = tuple(self.center)
xs = x0 - xs
ys = y0 - ys
zs = z0 - zs
vectors = np.stack((xs, ys, zs))
if self.metric == 'EUCLIDEAN':
norms = np.linalg.norm(vectors, axis=0)
elif self.metric == 'CHEBYSHEV':
norms = np.max(np.abs(vectors), axis=0)
elif self.metric == 'MANHATTAN':
norms = np.sum(np.abs(vectors), axis=0)
elif self.metric == 'CUSTOM':
norms = np.linalg.norm(vectors, axis=0, ord=self.power)
else:
raise Exception('Unknown metric')
if self.falloff is not None:
lens = self.falloff(norms)
R = lens * vectors / norms
else:
R = vectors
return R[0], R[1], R[2]
def evaluate(self, x, y, z):
point = np.array([x, y, z]) - self.center
if self.metric == 'EUCLIDEAN':
norm = np.linalg.norm(point)
elif self.metric == 'CHEBYSHEV':
norm = np.max(point)
elif self.metric == 'MANHATTAN':
norm = np.sum(np.abs(point))
elif self.metric == 'CUSTOM':
norm = np.linalg.norm(point, ord=self.power)
else:
raise Exception('Unknown metric')
if self.falloff is not None:
value = self.falloff(np.array([norm]))[0]
return value * point / norm
else:
return point
class SvLineAttractorVectorField(SvVectorField):
def __init__(self, center, direction, falloff=None):
self.center = center
self.direction = direction
self.falloff = falloff
self.__description__ = "Line Attractor"
def evaluate(self, x, y, z):
vertex = np.array([x,y,z])
direction = self.direction
to_center = self.center - vertex
projection = np.dot(to_center, direction) * direction / np.dot(direction, direction)
dv = to_center - projection
if self.falloff is not None:
norm = np.linalg.norm(dv)
dv = self.falloff(norm) * dv / norm
return dv
def evaluate_grid(self, xs, ys, zs):
direction = self.direction
direction2 = np.dot(direction, direction)
def func(vertex):
to_center = self.center - vertex
projection = np.dot(to_center, direction) * direction / direction2
dv = to_center - projection
return dv
points = np.stack((xs, ys, zs)).T
vectors = np.vectorize(func, signature='(3)->(3)')(points)
if self.falloff is not None:
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
nonzero = (norms > 0)[:,0]
lens = self.falloff(norms)
vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T
R = (lens * vectors).T
return R[0], R[1], R[2]
else:
R = vectors.T
return R[0], R[1], R[2]
class SvPlaneAttractorVectorField(SvVectorField):
def __init__(self, center, direction, falloff=None):
self.center = center
self.direction = direction
self.falloff = falloff
self.__description__ = "Plane Attractor"
def evaluate(self, x, y, z):
vertex = np.array([x,y,z])
direction = self.direction
to_center = self.center - vertex
dv = np.dot(to_center, direction) * direction / np.dot(direction, direction)
if self.falloff is not None:
norm = np.linalg.norm(dv)
dv = self.falloff(norm) * dv / norm
return dv
def evaluate_grid(self, xs, ys, zs):
direction = self.direction
direction2 = np.dot(direction, direction)
def func(vertex):
to_center = self.center - vertex
projection = np.dot(to_center, direction) * direction / direction2
return projection
points = np.stack((xs, ys, zs)).T
vectors = np.vectorize(func, signature='(3)->(3)')(points)
if self.falloff is not None:
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
lens = self.falloff(norms)
nonzero = (norms > 0)[:,0]
vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T
R = (lens * vectors).T
return R[0], R[1], R[2]
else:
R = vectors.T
return R[0], R[1], R[2]
class SvCircleAttractorVectorField(SvVectorField):
__description__ = "Circle Attractor"
def __init__(self, center, radius, normal, falloff=None):
self.circle = CircleEquation3D.from_center_radius_normal(center, radius, normal)
self.falloff = falloff
def evaluate(self, x, y, z):
v = np.array([x,y,z])
projection = self.circle.get_projections([v])[0]
vector = projection - v
if self.fallof is not None:
new_len = self.falloff(np.array([distance]))[0]
norm = np.linalg.norm(vector)
return new_len * vector / norm
else:
return vector
def evaluate_grid(self, xs, ys, zs):
vs = np.stack((xs, ys, zs)).T
projections = self.circle.get_projections(vs)
vectors = projections - vs
if self.falloff is not None:
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
lens = self.falloff(norms)
nonzero = (norms > 0)[:,0]
vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T
R = (lens * vectors).T
return R[0], R[1], R[2]
else:
R = vectors.T
return R[0], R[1], R[2]
class SvEdgeAttractorVectorField(SvVectorField):
__description__ = "Edge attractor"
def __init__(self, v1, v2, falloff=None):
self.falloff = falloff
self.v1 = Vector(v1)
self.v2 = Vector(v2)
def evaluate(self, x, y, z):
v = Vector([x,y,z])
dv1 = (v - self.v1).length
dv2 = (v - self.v2).length
if dv1 > dv2:
distance_to_nearest = dv2
nearest_vert = self.v2
another_vert = self.v1
else:
distance_to_nearest = dv1
nearest_vert = self.v1
another_vert = self.v2
edge = another_vert - nearest_vert
to_nearest = v - nearest_vert
if to_nearest.length == 0:
return 0
angle = edge.angle(to_nearest)
if angle > pi/2:
distance = distance_to_nearest
vector = - to_nearest
else:
vector = LineEquation.from_two_points(self.v1, self.v2).projection_of_points(v)
distance = vector.length
vector = np.array(vector)
if self.falloff is not None:
return self.falloff(distance) * vector / distance
else:
return vector
def evaluate_grid(self, xs, ys, zs):
n = len(xs)
vs = np.stack((xs, ys, zs)).T
v1 = np.array(self.v1)
v2 = np.array(self.v2)
dv1s = np.linalg.norm(vs - v1, axis=1)
dv2s = np.linalg.norm(vs - v2, axis=1)
v1_is_nearest = (dv1s < dv2s)
v2_is_nearest = np.logical_not(v1_is_nearest)
nearest_verts = np.empty_like(vs)
other_verts = np.empty_like(vs)
nearest_verts[v1_is_nearest] = v1
nearest_verts[v2_is_nearest] = v2
other_verts[v1_is_nearest] = v2
other_verts[v2_is_nearest] = v1
to_nearest = vs - nearest_verts
edges = other_verts - nearest_verts
dot = (to_nearest * edges).sum(axis=1)
at_edge = (dot > 0)
at_vertex = np.logical_not(at_edge)
at_v1 = np.logical_and(at_vertex, v1_is_nearest)
at_v2 = np.logical_and(at_vertex, v2_is_nearest)
line = LineEquation.from_two_points(self.v1, self.v2)
vectors = np.empty((n,3))
vectors[at_edge] = line.projection_of_points(vs[at_edge]) - vs[at_edge]
vectors[at_v1] = v1 - vs[at_v1]
vectors[at_v2] = v2 - vs[at_v2]
if self.falloff is not None:
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
lens = self.falloff(norms)
nonzero = (norms > 0)[:,0]
vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T
R = (lens * vectors).T
return R[0], R[1], R[2]
else:
R = vectors.T
return R[0], R[1], R[2]
class SvBvhAttractorVectorField(SvVectorField):
def __init__(self, bvh=None, verts=None, faces=None, falloff=None, use_normal=False, signed_normal=False):
self.falloff = falloff
self.use_normal = use_normal
self.signed_normal = signed_normal
if bvh is not None:
self.bvh = bvh
elif verts is not None and faces is not None:
self.bvh = bvhtree.BVHTree.FromPolygons(verts, faces)
else:
raise Exception("Either bvh or verts and faces must be provided!")
self.__description__ = "BVH Attractor"
def evaluate(self, x, y, z):
vertex = Vector((x,y,z))
nearest, normal, idx, distance = self.bvh.find_nearest(vertex)
if self.use_normal:
if self.signed_normal:
sign = (v - nearest).dot(normal)
sign = copysign(1, sign)
else:
sign = 1
return sign * np.array(normal)
else:
dv = np.array(nearest - vertex)
if self.falloff is not None:
norm = np.linalg.norm(dv)
len = self.falloff(norm)
dv = len * dv
return dv
else:
return dv
def evaluate_grid(self, xs, ys, zs):
def find(v):
nearest, normal, idx, distance = self.bvh.find_nearest(v)
if nearest is None:
raise Exception("No nearest point on mesh found for vertex %s" % v)
if self.use_normal:
if self.signed_normal:
sign = (v - nearest).dot(normal)
sign = copysign(1, sign)
else:
sign = 1
return sign * np.array(normal)
else:
return np.array(nearest) - v
points = np.stack((xs, ys, zs)).T
vectors = np.vectorize(find, signature='(3)->(3)')(points)
if self.falloff is not None:
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
nonzero = (norms > 0)[:,0]
lens = self.falloff(norms)
vectors[nonzero] = vectors[nonzero] / norms[nonzero]
R = (lens * vectors).T
return R[0], R[1], R[2]
else:
R = vectors.T
return R[0], R[1], R[2]
class SvRotationVectorField(SvVectorField):
def __init__(self, center, direction, falloff=None):
self.center = center
self.direction = direction
self.falloff = falloff
self.__description__ = "Rotation Field"
def evaluate(self, x, y, z):
vertex = np.array([x,y,z])
direction = self.direction
to_center = self.center - vertex
projection = np.dot(to_center, direction) * direction / np.dot(direction, direction)
dv = np.cross(to_center - projection, direction)
if self.falloff is not None:
norm = np.linalg.norm(dv)
dv = self.falloff(norm) * dv / norm
return dv
def evaluate_grid(self, xs, ys, zs):
direction = self.direction
direction2 = np.dot(direction, direction)
points = np.stack((xs, ys, zs)).T
to_center = self.center[np.newaxis, :] - points
projection = direction[np.newaxis, :] * (np_dot(to_center, direction[np.newaxis,:]) / direction2)[:, np.newaxis]
vectors = np.cross(to_center - projection, direction)
if self.falloff is not None:
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
nonzero = (norms > 0)[:,0]
lens = self.falloff(norms)
vectors[nonzero] = vectors[nonzero] / norms[nonzero][:, 0][np.newaxis].T
R = (lens * vectors).T
return R[0], R[1], R[2]
else:
R = vectors.T
return R[0], R[1], R[2]
class SvSelectVectorField(SvVectorField):
def __init__(self, fields, mode):
self.fields = fields
self.mode = mode
self.__description__ = "{}({})".format(mode, fields)
def evaluate(self, x, y, z):
vectors = [field.evaluate(x, y, z) for field in self.fields]
vectors = np.array(vectors)
norms = np.linalg.norm(vectors, axis=1)
if self.mode == 'MIN':
selected = np.argmin(norms)
else: # MAX
selected = np.argmax(norms)
return vectors[selected]
def evaluate_grid(self, xs, ys, zs):
n = len(xs)
vectors = [field.evaluate_grid(xs, ys, zs) for field in self.fields]
vectors = np.stack(vectors)
vectors = np.transpose(vectors, axes=(2,0,1))
norms = np.linalg.norm(vectors, axis=2)
if self.mode == 'MIN':
selected = np.argmin(norms, axis=1)
else: # MAX
selected = np.argmax(norms, axis=1)
all_points = list(range(n))
vectors = vectors[all_points, selected, :]
#print(vectors.shape)
return vectors.T
class SvVectorFieldTangent(SvVectorField):
def __init__(self, field1, field2):
self.field1 = field1
self.field2 = field2
self.__description__ = "Tangent"
def evaluate(self, x, y, z):
v1 = self.field1.evaluate(x,y,z)
v2 = self.field2.evaluate(x,y,z)
projection = np.dot(v1, v2) * v2 / np.dot(v2, v2)
return projection
def evaluate_grid(self, xs, ys, zs):
vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs)
vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs)
vectors1 = np.stack((vx1, vy1, vz1)).T
vectors2 = np.stack((vx2, vy2, vz2)).T
def project(v1, v2):
projection = np.dot(v1, v2) * v2 / np.dot(v2, v2)
vx, vy, vz = projection
return vx, vy, vz
return np.vectorize(project, signature="(3),(3)->(),(),()")(vectors1, vectors2)
class SvVectorFieldCotangent(SvVectorField):
def __init__(self, field1, field2):
self.field1 = field1
self.field2 = field2
self.__description__ = "Cotangent"
def evaluate(self, x, y, z):
v1 = self.field1.evaluate(x,y,z)
v2 = self.field2.evaluate(x,y,z)
projection = np.dot(v1, v2) * v2 / np.dot(v2, v2)
return v1 - projection
def evaluate_grid(self, xs, ys, zs):
vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs)
vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs)
vectors1 = np.stack((vx1, vy1, vz1)).T
vectors2 = np.stack((vx2, vy2, vz2)).T
def project(v1, v2):
projection = np.dot(v1, v2) * v2 / np.dot(v2, v2)
coprojection = v1 - projection
vx, vy, vz = coprojection
return vx, vy, vz
return np.vectorize(project, signature="(3),(3)->(),(),()")(vectors1, vectors2)
class SvVectorFieldComposition(SvVectorField):
def __init__(self, field1, field2):
self.field1 = field1
self.field2 = field2
self.__description__ = "Composition"
def evaluate(self, x, y, z):
x1, y1, z1 = self.field1.evaluate(x,y,z)
v2 = self.field2.evaluate(x1,y1,z1)
return v2
def evaluate_grid(self, xs, ys, zs):
r = self.field1.evaluate_grid(xs, ys, zs)
vx1, vy1, vz1 = r
return self.field2.evaluate_grid(vx1, vy1, vz1)
class SvScalarFieldGradient(SvVectorField):
def __init__(self, field, step):
self.field = field
self.step = step
self.__description__ = "Grad({})".format(field)
def evaluate(self, x, y, z):
return self.field.gradient([x, y, z], step=self.step)
def evaluate_grid(self, xs, ys, zs):
return self.field.gradient_grid(xs, ys, zs, step=self.step)
class SvVectorFieldRotor(SvVectorField):
def __init__(self, field, step):
self.field = field
self.step = step
self.__description__ = "Rot({})".format(field)
def evaluate(self, x, y, z):
step = self.step
_, y_dx_plus, z_dx_plus = self.field.evaluate(x+step,y,z)
_, y_dx_minus, z_dx_minus = self.field.evaluate(x-step,y,z)
x_dy_plus, _, z_dy_plus = self.field.evaluate(x, y+step, z)
x_dy_minus, _, z_dy_minus = self.field.evaluate(x, y-step, z)
x_dz_plus, y_dz_plus, _ = self.field.evaluate(x, y, z+step)
x_dz_minus, y_dz_minus, _ = self.field.evaluate(x, y, z-step)
dy_dx = (y_dx_plus - y_dx_minus) / (2*step)
dz_dx = (z_dx_plus - z_dx_minus) / (2*step)
dx_dy = (x_dy_plus - x_dy_minus) / (2*step)
dz_dy = (z_dy_plus - z_dy_minus) / (2*step)
dx_dz = (x_dz_plus - x_dz_minus) / (2*step)
dy_dz = (y_dz_plus - y_dz_minus) / (2*step)
rx = dz_dy - dy_dz
ry = - (dz_dx - dx_dz)
rz = dy_dx - dx_dy
return np.array([rx, ry, rz])
def evaluate_grid(self, xs, ys, zs):
step = self.step
_, y_dx_plus, z_dx_plus = self.field.evaluate_grid(xs+step,ys,zs)
_, y_dx_minus, z_dx_minus = self.field.evaluate_grid(xs-step,ys,zs)
x_dy_plus, _, z_dy_plus = self.field.evaluate_grid(xs, ys+step, zs)
x_dy_minus, _, z_dy_minus = self.field.evaluate_grid(xs, ys-step, zs)
x_dz_plus, y_dz_plus, _ = self.field.evaluate_grid(xs, ys, zs+step)
x_dz_minus, y_dz_minus, _ = self.field.evaluate_grid(xs, ys, zs-step)
dy_dx = (y_dx_plus - y_dx_minus) / (2*step)
dz_dx = (z_dx_plus - z_dx_minus) / (2*step)
dx_dy = (x_dy_plus - x_dy_minus) / (2*step)
dz_dy = (z_dy_plus - z_dy_minus) / (2*step)
dx_dz = (x_dz_plus - x_dz_minus) / (2*step)
dy_dz = (y_dz_plus - y_dz_minus) / (2*step)
rx = dz_dy - dy_dz
ry = - (dz_dx - dx_dz)
rz = dy_dx - dx_dy
R = np.stack((rx, ry, rz))
return R[0], R[1], R[2]
class SvBendAlongCurveField(SvVectorField):
ZERO = 'ZERO'
FRENET = 'FRENET'
HOUSEHOLDER = 'householder'
TRACK = 'track'
DIFF = 'diff'
TRACK_NORMAL = 'track_normal'
def __init__(self, curve, algorithm, scale_all, axis, t_min, t_max, up_axis=None, resolution=50, length_mode='T'):
self.curve = curve
self.axis = axis
self.t_min = t_min
self.t_max = t_max
self.algorithm = algorithm
self.scale_all = scale_all
self.up_axis = up_axis
self.length_mode = length_mode
if algorithm == SvBendAlongCurveField.ZERO:
self.curve.pre_calc_torsion_integral(resolution)
elif algorithm == SvBendAlongCurveField.TRACK_NORMAL:
self.normal_tracker = SvNormalTrack(curve, resolution)
if length_mode == 'L':
self.length_solver = SvCurveLengthSolver(curve)
self.length_solver.prepare('SPL', resolution)
self.__description__ = "Bend along {}".format(curve)
def get_matrix(self, tangent, scale):
return MathutilsRotationCalculator.get_matrix(tangent, scale, self.axis,
self.algorithm, self.scale_all, self.up_axis)
def get_matrices(self, ts, scale):
n = len(ts)
if self.scale_all:
scale_matrix = np.array([
[scale, 0, 0],
[0, scale, 0],
[0, 0, 1/scale]
])
else:
scale_matrix = np.array([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1/scale]
])
if self.algorithm == SvBendAlongCurveField.FRENET:
frenet, _ , _ = self.curve.frame_array(ts)
return frenet @ scale_matrix
elif self.algorithm == SvBendAlongCurveField.ZERO:
frenet, _ , _ = self.curve.frame_array(ts)
angles = - self.curve.torsion_integral(ts)
zeros = np.zeros((n,))
ones = np.ones((n,))
row1 = np.stack((np.cos(angles), np.sin(angles), zeros)).T # (n, 3)
row2 = np.stack((-np.sin(angles), np.cos(angles), zeros)).T # (n, 3)
row3 = np.stack((zeros, zeros, ones)).T # (n, 3)
rotation_matrices = np.dstack((row1, row2, row3))
return frenet @ rotation_matrices @ scale_matrix
elif self.algorithm == SvBendAlongCurveField.TRACK_NORMAL:
matrices = self.normal_tracker.evaluate_array(ts)
return matrices @ scale_matrix
else:
raise Exception("Unsupported algorithm")
def get_t_value(self, x, y, z):
curve_t_min, curve_t_max = self.curve.get_u_bounds()
t = [x, y, z][self.axis]
if self.length_mode == 'T':
t = (curve_t_max - curve_t_min) * (t - self.t_min) / (self.t_max - self.t_min) + curve_t_min
else:
t = (t - self.t_min) / (self.t_max - self.t_min) # 0 .. 1
t = t * self.length_solver.get_total_length()
t = self.length_solver.solve(np.array([t]))[0]
return t
def get_t_values(self, xs, ys, zs):
curve_t_min, curve_t_max = self.curve.get_u_bounds()
ts = [xs, ys, zs][self.axis]
if self.length_mode == 'T':
ts = (curve_t_max - curve_t_min) * (ts - self.t_min) / (self.t_max - self.t_min) + curve_t_min
else:
ts = (ts - self.t_min) / (self.t_max - self.t_min) # 0 .. 1
ts = ts * self.length_solver.get_total_length()
ts = self.length_solver.solve(ts)
return ts
def get_scale(self):
if self.length_mode == 'T':
curve_t_min, curve_t_max = self.curve.get_u_bounds()
t_range = curve_t_max - curve_t_min
else:
t_range = self.length_solver.get_total_length()
return (self.t_max - self.t_min) / t_range
def evaluate(self, x, y, z):
t = self.get_t_value(x, y, z)
spline_tangent = self.curve.tangent(t)
spline_vertex = self.curve.evaluate(t)
scale = self.get_scale()
if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}:
matrix = self.get_matrices(np.array([t]), scale)
else:
matrix = self.get_matrix(spline_tangent, scale)
src_vector_projection = np.array([x, y, z])
src_vector_projection[self.axis] = 0
new_vertex = np.matmul(matrix, src_vector_projection) + spline_vertex
vector = new_vertex - np.array([x, y, z])
return vector
def evaluate_grid(self, xs, ys, zs):
def multiply(matrices, vectors):
vectors = vectors[np.newaxis]
vectors = np.transpose(vectors, axes=(1,2,0))
r = matrices @ vectors
return r[:,:,0]
ts = self.get_t_values(xs, ys, zs).flatten()
spline_tangents = self.curve.tangent_array(ts)
spline_vertices = self.curve.evaluate_array(ts)
scale = self.get_scale()
if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}:
matrices = self.get_matrices(ts, scale)
else:
matrices = np.vectorize(lambda t : self.get_matrix(t, scale), signature='(3)->(3,3)')(spline_tangents)
src_vectors = np.stack((xs, ys, zs)).T
src_vector_projections = src_vectors.copy()
src_vector_projections[:,self.axis] = 0
#multiply = np.vectorize(lambda m, v: m @ v, signature='(3,3),(3)->(3)')
new_vertices = multiply(matrices, src_vector_projections) + spline_vertices
R = (new_vertices - src_vectors).T
return R[0], R[1], R[2]
class SvBendAlongSurfaceField(SvVectorField):
def __init__(self, surface, axis, autoscale=False, flip=False, only_2D=False):
self.surface = surface
self.orient_axis = axis
self.autoscale = autoscale
self.flip = flip
self.u_bounds = (0, 1)
self.v_bounds = (0, 1)
self.only_2D = only_2D
self.__description__ = "Bend along {}".format(surface)
def get_other_axes(self):
# Select U and V to be two axes except orient_axis
if self.orient_axis == 0:
u_index, v_index = 1,2
elif self.orient_axis == 1:
u_index, v_index = 2,0
else:
u_index, v_index = 1,0
return u_index, v_index
def get_uv(self, vertices):
"""
Translate source vertices to UV space of future spline.
vertices must be np.array of shape (n, 3).
"""
u_index, v_index = self.get_other_axes()
# Rescale U and V coordinates to [0, 1], drop third coordinate
us = vertices[:,u_index].flatten()
vs = vertices[:,v_index].flatten()
min_u, max_u = self.u_bounds
min_v, max_v = self.v_bounds
size_u = max_u - min_u
size_v = max_v - min_v
if size_u < 0.00001:
raise Exception("Object has too small size in U direction")
if size_v < 0.00001:
raise Exception("Object has too small size in V direction")
us = self.surface.u_size * (us - min_u) / size_u + self.surface.get_u_min()
vs = self.surface.v_size * (vs - min_v) / size_v + self.surface.get_v_min()
return size_u, size_v, us, vs
def _evaluate(self, vertices):
src_size_u, src_size_v, us, vs = self.get_uv(vertices)
if self.autoscale:
u_index, v_index = self.get_other_axes()
scale_u = src_size_u / self.surface.u_size
scale_v = src_size_v / self.surface.v_size
scale_z = sqrt(scale_u * scale_v)
else:
if self.orient_axis == 2:
scale_z = -1.0
else:
scale_z = 1.0
if self.flip:
scale_z = - scale_z
if self.only_2D:
return self.surface.evaluate_array(us, vs)
surf_vertices = self.surface.evaluate_array(us, vs)
spline_normals = self.surface.normal_array(us, vs)
zs = vertices[:,self.orient_axis].flatten()
zs = zs[np.newaxis].T
v1 = zs * spline_normals
v2 = scale_z * v1
new_vertices = surf_vertices + v2
return new_vertices
def evaluate_grid(self, xs, ys, zs):
vertices = np.stack((xs, ys, zs)).T
new_vertices = self._evaluate(vertices)
R = (new_vertices - vertices).T
return R[0], R[1], R[2]
def evaluate(self, x, y, z):
xs, ys, zs = self.evaluate_grid(np.array([x]), np.array([y]), np.array([z]))
return np.array([xs[0], ys[0], zs[0]])
class SvVoronoiVectorField(SvVectorField):
def __init__(self, vertices=None, voronoi=None, metric='DISTANCE'):
if vertices is None and voronoi is None:
raise Exception("Either vertices or voronoi must be specified")
if voronoi is not None:
self.voronoi = voronoi
else:
self.voronoi = SvVoronoiFieldData(vertices, metric=metric)
self.__description__ = "Voronoi"
def evaluate(self, x, y, z):
r = self.voronoi.query(np.array([x,y,z]))
return r[1]
def evaluate_grid(self, xs, ys, zs):
vs = np.stack((xs,ys,zs)).T
r = self.voronoi.query_array(vs)
vs = r[1]
return vs[:,0], vs[:,1], vs[:,2]
class SvScalarFieldCurveMap(SvVectorField):
def __init__(self, scalar_field, curve, mode):
self.scalar_field = scalar_field
self.curve = curve
self.mode = mode
def evaluate(self, x, y, z):
t = self.scalar_field.evaluate(x,y,z)
if self.mode == 'VALUE':
return self.curve.evaluate(t)
elif self.mode == 'TANGENT':
return self.curve.tangent(t)
else: # NORMAL
return self.curve.main_normal(t)
def evaluate_grid(self, xs, ys, zs):
ts = self.scalar_field.evaluate_grid(xs, ys, zs)
if self.mode == 'VALUE':
vectors = self.curve.evaluate_array(ts)
elif self.mode == 'TANGENT':
vectors = self.curve.tangent_array(ts)
else: # NORMAL
vectors = self.curve.main_normal_array(ts)
return vectors[:,0], vectors[:,1], vectors[:,2]
Classes
class SvAbsoluteVectorField (field)
-
Expand source code
class SvAbsoluteVectorField(SvVectorField): def __init__(self, field): self.field = field self.__description__ = "Absolute({})".format(field) def evaluate(self, x, y, z): r = self.field.evaluate(x, y, z) return r + np.array([x, y, z]) def evaluate_grid(self, xs, ys, zs): rxs, rys, rzs = self.field.evaluate_grid(xs, ys, zs) return rxs + xs, rys + ys, rzs + zs
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): r = self.field.evaluate(x, y, z) return r + np.array([x, y, z])
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): rxs, rys, rzs = self.field.evaluate_grid(xs, ys, zs) return rxs + xs, rys + ys, rzs + zs
class SvAverageVectorField (fields)
-
Expand source code
class SvAverageVectorField(SvVectorField): def __init__(self, fields): self.fields = fields self.__description__ = "Average" def evaluate(self, x, y, z): vectors = np.array([field.evaluate(x, y, z) for field in self.fields]) return np.mean(vectors, axis=0) def evaluate_grid(self, xs, ys, zs): def func(xs, ys, zs): data = [] for field in self.fields: vx, vy, vz = field.evaluate_grid(xs, ys, zs) vectors = np.stack((vx, vy, vz)).T data.append(vectors) data = np.array(data) mean = np.mean(data, axis=0).T return mean[0], mean[1], mean[2] return np.vectorize(func, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): vectors = np.array([field.evaluate(x, y, z) for field in self.fields]) return np.mean(vectors, axis=0)
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): def func(xs, ys, zs): data = [] for field in self.fields: vx, vy, vz = field.evaluate_grid(xs, ys, zs) vectors = np.stack((vx, vy, vz)).T data.append(vectors) data = np.array(data) mean = np.mean(data, axis=0).T return mean[0], mean[1], mean[2] return np.vectorize(func, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
class SvBendAlongCurveField (curve, algorithm, scale_all, axis, t_min, t_max, up_axis=None, resolution=50, length_mode='T')
-
Expand source code
class SvBendAlongCurveField(SvVectorField): ZERO = 'ZERO' FRENET = 'FRENET' HOUSEHOLDER = 'householder' TRACK = 'track' DIFF = 'diff' TRACK_NORMAL = 'track_normal' def __init__(self, curve, algorithm, scale_all, axis, t_min, t_max, up_axis=None, resolution=50, length_mode='T'): self.curve = curve self.axis = axis self.t_min = t_min self.t_max = t_max self.algorithm = algorithm self.scale_all = scale_all self.up_axis = up_axis self.length_mode = length_mode if algorithm == SvBendAlongCurveField.ZERO: self.curve.pre_calc_torsion_integral(resolution) elif algorithm == SvBendAlongCurveField.TRACK_NORMAL: self.normal_tracker = SvNormalTrack(curve, resolution) if length_mode == 'L': self.length_solver = SvCurveLengthSolver(curve) self.length_solver.prepare('SPL', resolution) self.__description__ = "Bend along {}".format(curve) def get_matrix(self, tangent, scale): return MathutilsRotationCalculator.get_matrix(tangent, scale, self.axis, self.algorithm, self.scale_all, self.up_axis) def get_matrices(self, ts, scale): n = len(ts) if self.scale_all: scale_matrix = np.array([ [scale, 0, 0], [0, scale, 0], [0, 0, 1/scale] ]) else: scale_matrix = np.array([ [1, 0, 0], [0, 1, 0], [0, 0, 1/scale] ]) if self.algorithm == SvBendAlongCurveField.FRENET: frenet, _ , _ = self.curve.frame_array(ts) return frenet @ scale_matrix elif self.algorithm == SvBendAlongCurveField.ZERO: frenet, _ , _ = self.curve.frame_array(ts) angles = - self.curve.torsion_integral(ts) zeros = np.zeros((n,)) ones = np.ones((n,)) row1 = np.stack((np.cos(angles), np.sin(angles), zeros)).T # (n, 3) row2 = np.stack((-np.sin(angles), np.cos(angles), zeros)).T # (n, 3) row3 = np.stack((zeros, zeros, ones)).T # (n, 3) rotation_matrices = np.dstack((row1, row2, row3)) return frenet @ rotation_matrices @ scale_matrix elif self.algorithm == SvBendAlongCurveField.TRACK_NORMAL: matrices = self.normal_tracker.evaluate_array(ts) return matrices @ scale_matrix else: raise Exception("Unsupported algorithm") def get_t_value(self, x, y, z): curve_t_min, curve_t_max = self.curve.get_u_bounds() t = [x, y, z][self.axis] if self.length_mode == 'T': t = (curve_t_max - curve_t_min) * (t - self.t_min) / (self.t_max - self.t_min) + curve_t_min else: t = (t - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 t = t * self.length_solver.get_total_length() t = self.length_solver.solve(np.array([t]))[0] return t def get_t_values(self, xs, ys, zs): curve_t_min, curve_t_max = self.curve.get_u_bounds() ts = [xs, ys, zs][self.axis] if self.length_mode == 'T': ts = (curve_t_max - curve_t_min) * (ts - self.t_min) / (self.t_max - self.t_min) + curve_t_min else: ts = (ts - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 ts = ts * self.length_solver.get_total_length() ts = self.length_solver.solve(ts) return ts def get_scale(self): if self.length_mode == 'T': curve_t_min, curve_t_max = self.curve.get_u_bounds() t_range = curve_t_max - curve_t_min else: t_range = self.length_solver.get_total_length() return (self.t_max - self.t_min) / t_range def evaluate(self, x, y, z): t = self.get_t_value(x, y, z) spline_tangent = self.curve.tangent(t) spline_vertex = self.curve.evaluate(t) scale = self.get_scale() if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}: matrix = self.get_matrices(np.array([t]), scale) else: matrix = self.get_matrix(spline_tangent, scale) src_vector_projection = np.array([x, y, z]) src_vector_projection[self.axis] = 0 new_vertex = np.matmul(matrix, src_vector_projection) + spline_vertex vector = new_vertex - np.array([x, y, z]) return vector def evaluate_grid(self, xs, ys, zs): def multiply(matrices, vectors): vectors = vectors[np.newaxis] vectors = np.transpose(vectors, axes=(1,2,0)) r = matrices @ vectors return r[:,:,0] ts = self.get_t_values(xs, ys, zs).flatten() spline_tangents = self.curve.tangent_array(ts) spline_vertices = self.curve.evaluate_array(ts) scale = self.get_scale() if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}: matrices = self.get_matrices(ts, scale) else: matrices = np.vectorize(lambda t : self.get_matrix(t, scale), signature='(3)->(3,3)')(spline_tangents) src_vectors = np.stack((xs, ys, zs)).T src_vector_projections = src_vectors.copy() src_vector_projections[:,self.axis] = 0 #multiply = np.vectorize(lambda m, v: m @ v, signature='(3,3),(3)->(3)') new_vertices = multiply(matrices, src_vector_projections) + spline_vertices R = (new_vertices - src_vectors).T return R[0], R[1], R[2]
Ancestors
Class variables
var DIFF
var FRENET
var HOUSEHOLDER
var TRACK
var TRACK_NORMAL
var ZERO
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): t = self.get_t_value(x, y, z) spline_tangent = self.curve.tangent(t) spline_vertex = self.curve.evaluate(t) scale = self.get_scale() if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}: matrix = self.get_matrices(np.array([t]), scale) else: matrix = self.get_matrix(spline_tangent, scale) src_vector_projection = np.array([x, y, z]) src_vector_projection[self.axis] = 0 new_vertex = np.matmul(matrix, src_vector_projection) + spline_vertex vector = new_vertex - np.array([x, y, z]) return vector
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): def multiply(matrices, vectors): vectors = vectors[np.newaxis] vectors = np.transpose(vectors, axes=(1,2,0)) r = matrices @ vectors return r[:,:,0] ts = self.get_t_values(xs, ys, zs).flatten() spline_tangents = self.curve.tangent_array(ts) spline_vertices = self.curve.evaluate_array(ts) scale = self.get_scale() if self.algorithm in {SvBendAlongCurveField.ZERO, SvBendAlongCurveField.FRENET, SvBendAlongCurveField.TRACK_NORMAL}: matrices = self.get_matrices(ts, scale) else: matrices = np.vectorize(lambda t : self.get_matrix(t, scale), signature='(3)->(3,3)')(spline_tangents) src_vectors = np.stack((xs, ys, zs)).T src_vector_projections = src_vectors.copy() src_vector_projections[:,self.axis] = 0 #multiply = np.vectorize(lambda m, v: m @ v, signature='(3,3),(3)->(3)') new_vertices = multiply(matrices, src_vector_projections) + spline_vertices R = (new_vertices - src_vectors).T return R[0], R[1], R[2]
def get_matrices(self, ts, scale)
-
Expand source code
def get_matrices(self, ts, scale): n = len(ts) if self.scale_all: scale_matrix = np.array([ [scale, 0, 0], [0, scale, 0], [0, 0, 1/scale] ]) else: scale_matrix = np.array([ [1, 0, 0], [0, 1, 0], [0, 0, 1/scale] ]) if self.algorithm == SvBendAlongCurveField.FRENET: frenet, _ , _ = self.curve.frame_array(ts) return frenet @ scale_matrix elif self.algorithm == SvBendAlongCurveField.ZERO: frenet, _ , _ = self.curve.frame_array(ts) angles = - self.curve.torsion_integral(ts) zeros = np.zeros((n,)) ones = np.ones((n,)) row1 = np.stack((np.cos(angles), np.sin(angles), zeros)).T # (n, 3) row2 = np.stack((-np.sin(angles), np.cos(angles), zeros)).T # (n, 3) row3 = np.stack((zeros, zeros, ones)).T # (n, 3) rotation_matrices = np.dstack((row1, row2, row3)) return frenet @ rotation_matrices @ scale_matrix elif self.algorithm == SvBendAlongCurveField.TRACK_NORMAL: matrices = self.normal_tracker.evaluate_array(ts) return matrices @ scale_matrix else: raise Exception("Unsupported algorithm")
def get_matrix(self, tangent, scale)
-
Expand source code
def get_matrix(self, tangent, scale): return MathutilsRotationCalculator.get_matrix(tangent, scale, self.axis, self.algorithm, self.scale_all, self.up_axis)
def get_scale(self)
-
Expand source code
def get_scale(self): if self.length_mode == 'T': curve_t_min, curve_t_max = self.curve.get_u_bounds() t_range = curve_t_max - curve_t_min else: t_range = self.length_solver.get_total_length() return (self.t_max - self.t_min) / t_range
def get_t_value(self, x, y, z)
-
Expand source code
def get_t_value(self, x, y, z): curve_t_min, curve_t_max = self.curve.get_u_bounds() t = [x, y, z][self.axis] if self.length_mode == 'T': t = (curve_t_max - curve_t_min) * (t - self.t_min) / (self.t_max - self.t_min) + curve_t_min else: t = (t - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 t = t * self.length_solver.get_total_length() t = self.length_solver.solve(np.array([t]))[0] return t
def get_t_values(self, xs, ys, zs)
-
Expand source code
def get_t_values(self, xs, ys, zs): curve_t_min, curve_t_max = self.curve.get_u_bounds() ts = [xs, ys, zs][self.axis] if self.length_mode == 'T': ts = (curve_t_max - curve_t_min) * (ts - self.t_min) / (self.t_max - self.t_min) + curve_t_min else: ts = (ts - self.t_min) / (self.t_max - self.t_min) # 0 .. 1 ts = ts * self.length_solver.get_total_length() ts = self.length_solver.solve(ts) return ts
class SvBendAlongSurfaceField (surface, axis, autoscale=False, flip=False, only_2D=False)
-
Expand source code
class SvBendAlongSurfaceField(SvVectorField): def __init__(self, surface, axis, autoscale=False, flip=False, only_2D=False): self.surface = surface self.orient_axis = axis self.autoscale = autoscale self.flip = flip self.u_bounds = (0, 1) self.v_bounds = (0, 1) self.only_2D = only_2D self.__description__ = "Bend along {}".format(surface) def get_other_axes(self): # Select U and V to be two axes except orient_axis if self.orient_axis == 0: u_index, v_index = 1,2 elif self.orient_axis == 1: u_index, v_index = 2,0 else: u_index, v_index = 1,0 return u_index, v_index def get_uv(self, vertices): """ Translate source vertices to UV space of future spline. vertices must be np.array of shape (n, 3). """ u_index, v_index = self.get_other_axes() # Rescale U and V coordinates to [0, 1], drop third coordinate us = vertices[:,u_index].flatten() vs = vertices[:,v_index].flatten() min_u, max_u = self.u_bounds min_v, max_v = self.v_bounds size_u = max_u - min_u size_v = max_v - min_v if size_u < 0.00001: raise Exception("Object has too small size in U direction") if size_v < 0.00001: raise Exception("Object has too small size in V direction") us = self.surface.u_size * (us - min_u) / size_u + self.surface.get_u_min() vs = self.surface.v_size * (vs - min_v) / size_v + self.surface.get_v_min() return size_u, size_v, us, vs def _evaluate(self, vertices): src_size_u, src_size_v, us, vs = self.get_uv(vertices) if self.autoscale: u_index, v_index = self.get_other_axes() scale_u = src_size_u / self.surface.u_size scale_v = src_size_v / self.surface.v_size scale_z = sqrt(scale_u * scale_v) else: if self.orient_axis == 2: scale_z = -1.0 else: scale_z = 1.0 if self.flip: scale_z = - scale_z if self.only_2D: return self.surface.evaluate_array(us, vs) surf_vertices = self.surface.evaluate_array(us, vs) spline_normals = self.surface.normal_array(us, vs) zs = vertices[:,self.orient_axis].flatten() zs = zs[np.newaxis].T v1 = zs * spline_normals v2 = scale_z * v1 new_vertices = surf_vertices + v2 return new_vertices def evaluate_grid(self, xs, ys, zs): vertices = np.stack((xs, ys, zs)).T new_vertices = self._evaluate(vertices) R = (new_vertices - vertices).T return R[0], R[1], R[2] def evaluate(self, x, y, z): xs, ys, zs = self.evaluate_grid(np.array([x]), np.array([y]), np.array([z])) return np.array([xs[0], ys[0], zs[0]])
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): xs, ys, zs = self.evaluate_grid(np.array([x]), np.array([y]), np.array([z])) return np.array([xs[0], ys[0], zs[0]])
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): vertices = np.stack((xs, ys, zs)).T new_vertices = self._evaluate(vertices) R = (new_vertices - vertices).T return R[0], R[1], R[2]
def get_other_axes(self)
-
Expand source code
def get_other_axes(self): # Select U and V to be two axes except orient_axis if self.orient_axis == 0: u_index, v_index = 1,2 elif self.orient_axis == 1: u_index, v_index = 2,0 else: u_index, v_index = 1,0 return u_index, v_index
def get_uv(self, vertices)
-
Translate source vertices to UV space of future spline. vertices must be np.array of shape (n, 3).
Expand source code
def get_uv(self, vertices): """ Translate source vertices to UV space of future spline. vertices must be np.array of shape (n, 3). """ u_index, v_index = self.get_other_axes() # Rescale U and V coordinates to [0, 1], drop third coordinate us = vertices[:,u_index].flatten() vs = vertices[:,v_index].flatten() min_u, max_u = self.u_bounds min_v, max_v = self.v_bounds size_u = max_u - min_u size_v = max_v - min_v if size_u < 0.00001: raise Exception("Object has too small size in U direction") if size_v < 0.00001: raise Exception("Object has too small size in V direction") us = self.surface.u_size * (us - min_u) / size_u + self.surface.get_u_min() vs = self.surface.v_size * (vs - min_v) / size_v + self.surface.get_v_min() return size_u, size_v, us, vs
class SvBvhAttractorVectorField (bvh=None, verts=None, faces=None, falloff=None, use_normal=False, signed_normal=False)
-
Expand source code
class SvBvhAttractorVectorField(SvVectorField): def __init__(self, bvh=None, verts=None, faces=None, falloff=None, use_normal=False, signed_normal=False): self.falloff = falloff self.use_normal = use_normal self.signed_normal = signed_normal if bvh is not None: self.bvh = bvh elif verts is not None and faces is not None: self.bvh = bvhtree.BVHTree.FromPolygons(verts, faces) else: raise Exception("Either bvh or verts and faces must be provided!") self.__description__ = "BVH Attractor" def evaluate(self, x, y, z): vertex = Vector((x,y,z)) nearest, normal, idx, distance = self.bvh.find_nearest(vertex) if self.use_normal: if self.signed_normal: sign = (v - nearest).dot(normal) sign = copysign(1, sign) else: sign = 1 return sign * np.array(normal) else: dv = np.array(nearest - vertex) if self.falloff is not None: norm = np.linalg.norm(dv) len = self.falloff(norm) dv = len * dv return dv else: return dv def evaluate_grid(self, xs, ys, zs): def find(v): nearest, normal, idx, distance = self.bvh.find_nearest(v) if nearest is None: raise Exception("No nearest point on mesh found for vertex %s" % v) if self.use_normal: if self.signed_normal: sign = (v - nearest).dot(normal) sign = copysign(1, sign) else: sign = 1 return sign * np.array(normal) else: return np.array(nearest) - v points = np.stack((xs, ys, zs)).T vectors = np.vectorize(find, signature='(3)->(3)')(points) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero] R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): vertex = Vector((x,y,z)) nearest, normal, idx, distance = self.bvh.find_nearest(vertex) if self.use_normal: if self.signed_normal: sign = (v - nearest).dot(normal) sign = copysign(1, sign) else: sign = 1 return sign * np.array(normal) else: dv = np.array(nearest - vertex) if self.falloff is not None: norm = np.linalg.norm(dv) len = self.falloff(norm) dv = len * dv return dv else: return dv
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): def find(v): nearest, normal, idx, distance = self.bvh.find_nearest(v) if nearest is None: raise Exception("No nearest point on mesh found for vertex %s" % v) if self.use_normal: if self.signed_normal: sign = (v - nearest).dot(normal) sign = copysign(1, sign) else: sign = 1 return sign * np.array(normal) else: return np.array(nearest) - v points = np.stack((xs, ys, zs)).T vectors = np.vectorize(find, signature='(3)->(3)')(points) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero] R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
class SvCircleAttractorVectorField (center, radius, normal, falloff=None)
-
Expand source code
class SvCircleAttractorVectorField(SvVectorField): __description__ = "Circle Attractor" def __init__(self, center, radius, normal, falloff=None): self.circle = CircleEquation3D.from_center_radius_normal(center, radius, normal) self.falloff = falloff def evaluate(self, x, y, z): v = np.array([x,y,z]) projection = self.circle.get_projections([v])[0] vector = projection - v if self.fallof is not None: new_len = self.falloff(np.array([distance]))[0] norm = np.linalg.norm(vector) return new_len * vector / norm else: return vector def evaluate_grid(self, xs, ys, zs): vs = np.stack((xs, ys, zs)).T projections = self.circle.get_projections(vs) vectors = projections - vs if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): v = np.array([x,y,z]) projection = self.circle.get_projections([v])[0] vector = projection - v if self.fallof is not None: new_len = self.falloff(np.array([distance]))[0] norm = np.linalg.norm(vector) return new_len * vector / norm else: return vector
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): vs = np.stack((xs, ys, zs)).T projections = self.circle.get_projections(vs) vectors = projections - vs if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
class SvComposedVectorField (coords, sfield1, sfield2, sfield3)
-
Expand source code
class SvComposedVectorField(SvVectorField): def __init__(self, coords, sfield1, sfield2, sfield3): self.coords = coords self.sfield1 = sfield1 self.sfield2 = sfield2 self.sfield3 = sfield3 self.__description__ = "{}({}, {}, {})".format(coords, sfield1, sfield2, sfield3) def evaluate(self, x, y, z): v1 = self.sfield1.evaluate(x, y, z) v2 = self.sfield2.evaluate(x, y, z) v3 = self.sfield3.evaluate(x, y, z) if self.coords == 'XYZ': return np.array((v1, v2, v3)) elif self.coords == 'CYL': return np.array(from_cylindrical(v1, v2, v3, mode='radians')) else: # SPH: return np.array(from_spherical(v1, v2, v3, mode='radians')) def evaluate_grid(self, xs, ys, zs): v1s = self.sfield1.evaluate_grid(xs, ys, zs) v2s = self.sfield2.evaluate_grid(xs, ys, zs) v3s = self.sfield3.evaluate_grid(xs, ys, zs) if self.coords == 'XYZ': return v1s, v2s, v3s elif self.coords == 'CYL': vectors = np.stack((v1s, v2s, v3s)).T vectors = np.apply_along_axis(lambda v: np.array(from_cylindrical(*tuple(v), mode='radians')), 1, vectors).T return vectors[0], vectors[1], vectors[2] else: # SPH: vectors = np.stack((v1s, v2s, v3s)).T vectors = np.apply_along_axis(lambda v: np.array(from_spherical(*tuple(v), mode='radians')), 1, vectors).T return vectors[0], vectors[1], vectors[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): v1 = self.sfield1.evaluate(x, y, z) v2 = self.sfield2.evaluate(x, y, z) v3 = self.sfield3.evaluate(x, y, z) if self.coords == 'XYZ': return np.array((v1, v2, v3)) elif self.coords == 'CYL': return np.array(from_cylindrical(v1, v2, v3, mode='radians')) else: # SPH: return np.array(from_spherical(v1, v2, v3, mode='radians'))
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): v1s = self.sfield1.evaluate_grid(xs, ys, zs) v2s = self.sfield2.evaluate_grid(xs, ys, zs) v3s = self.sfield3.evaluate_grid(xs, ys, zs) if self.coords == 'XYZ': return v1s, v2s, v3s elif self.coords == 'CYL': vectors = np.stack((v1s, v2s, v3s)).T vectors = np.apply_along_axis(lambda v: np.array(from_cylindrical(*tuple(v), mode='radians')), 1, vectors).T return vectors[0], vectors[1], vectors[2] else: # SPH: vectors = np.stack((v1s, v2s, v3s)).T vectors = np.apply_along_axis(lambda v: np.array(from_spherical(*tuple(v), mode='radians')), 1, vectors).T return vectors[0], vectors[1], vectors[2]
class SvConstantVectorField (vector)
-
Expand source code
class SvConstantVectorField(SvVectorField): def __init__(self, vector): self.vector = np.array(vector) self.__description__ = "Constant = {}".format(vector) def evaluate(self, x, y, z): return self.vector def evaluate_grid(self, xs, ys, zs): x, y, z = self.vector rx = np.full_like(xs, x) ry = np.full_like(ys, y) rz = np.full_like(zs, z) return rx, ry, rz
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): return self.vector
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): x, y, z = self.vector rx = np.full_like(xs, x) ry = np.full_like(ys, y) rz = np.full_like(zs, z) return rx, ry, rz
class SvEdgeAttractorVectorField (v1, v2, falloff=None)
-
Expand source code
class SvEdgeAttractorVectorField(SvVectorField): __description__ = "Edge attractor" def __init__(self, v1, v2, falloff=None): self.falloff = falloff self.v1 = Vector(v1) self.v2 = Vector(v2) def evaluate(self, x, y, z): v = Vector([x,y,z]) dv1 = (v - self.v1).length dv2 = (v - self.v2).length if dv1 > dv2: distance_to_nearest = dv2 nearest_vert = self.v2 another_vert = self.v1 else: distance_to_nearest = dv1 nearest_vert = self.v1 another_vert = self.v2 edge = another_vert - nearest_vert to_nearest = v - nearest_vert if to_nearest.length == 0: return 0 angle = edge.angle(to_nearest) if angle > pi/2: distance = distance_to_nearest vector = - to_nearest else: vector = LineEquation.from_two_points(self.v1, self.v2).projection_of_points(v) distance = vector.length vector = np.array(vector) if self.falloff is not None: return self.falloff(distance) * vector / distance else: return vector def evaluate_grid(self, xs, ys, zs): n = len(xs) vs = np.stack((xs, ys, zs)).T v1 = np.array(self.v1) v2 = np.array(self.v2) dv1s = np.linalg.norm(vs - v1, axis=1) dv2s = np.linalg.norm(vs - v2, axis=1) v1_is_nearest = (dv1s < dv2s) v2_is_nearest = np.logical_not(v1_is_nearest) nearest_verts = np.empty_like(vs) other_verts = np.empty_like(vs) nearest_verts[v1_is_nearest] = v1 nearest_verts[v2_is_nearest] = v2 other_verts[v1_is_nearest] = v2 other_verts[v2_is_nearest] = v1 to_nearest = vs - nearest_verts edges = other_verts - nearest_verts dot = (to_nearest * edges).sum(axis=1) at_edge = (dot > 0) at_vertex = np.logical_not(at_edge) at_v1 = np.logical_and(at_vertex, v1_is_nearest) at_v2 = np.logical_and(at_vertex, v2_is_nearest) line = LineEquation.from_two_points(self.v1, self.v2) vectors = np.empty((n,3)) vectors[at_edge] = line.projection_of_points(vs[at_edge]) - vs[at_edge] vectors[at_v1] = v1 - vs[at_v1] vectors[at_v2] = v2 - vs[at_v2] if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): v = Vector([x,y,z]) dv1 = (v - self.v1).length dv2 = (v - self.v2).length if dv1 > dv2: distance_to_nearest = dv2 nearest_vert = self.v2 another_vert = self.v1 else: distance_to_nearest = dv1 nearest_vert = self.v1 another_vert = self.v2 edge = another_vert - nearest_vert to_nearest = v - nearest_vert if to_nearest.length == 0: return 0 angle = edge.angle(to_nearest) if angle > pi/2: distance = distance_to_nearest vector = - to_nearest else: vector = LineEquation.from_two_points(self.v1, self.v2).projection_of_points(v) distance = vector.length vector = np.array(vector) if self.falloff is not None: return self.falloff(distance) * vector / distance else: return vector
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): n = len(xs) vs = np.stack((xs, ys, zs)).T v1 = np.array(self.v1) v2 = np.array(self.v2) dv1s = np.linalg.norm(vs - v1, axis=1) dv2s = np.linalg.norm(vs - v2, axis=1) v1_is_nearest = (dv1s < dv2s) v2_is_nearest = np.logical_not(v1_is_nearest) nearest_verts = np.empty_like(vs) other_verts = np.empty_like(vs) nearest_verts[v1_is_nearest] = v1 nearest_verts[v2_is_nearest] = v2 other_verts[v1_is_nearest] = v2 other_verts[v2_is_nearest] = v1 to_nearest = vs - nearest_verts edges = other_verts - nearest_verts dot = (to_nearest * edges).sum(axis=1) at_edge = (dot > 0) at_vertex = np.logical_not(at_edge) at_v1 = np.logical_and(at_vertex, v1_is_nearest) at_v2 = np.logical_and(at_vertex, v2_is_nearest) line = LineEquation.from_two_points(self.v1, self.v2) vectors = np.empty((n,3)) vectors[at_edge] = line.projection_of_points(vs[at_edge]) - vs[at_edge] vectors[at_v1] = v1 - vs[at_v1] vectors[at_v2] = v2 - vs[at_v2] if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
class SvKdtVectorField (vertices=None, kdt=None, falloff=None, negate=False, power=2)
-
Expand source code
class SvKdtVectorField(SvVectorField): def __init__(self, vertices=None, kdt=None, falloff=None, negate=False, power=2): self.falloff = falloff self.negate = negate if kdt is not None: self.kdt = kdt elif vertices is not None: self.kdt = SvKdTree.new(SvKdTree.best_available_implementation(), vertices, power=power) else: raise Exception("Either kdt or vertices must be provided") self.__description__ = "KDT Attractor" def evaluate(self, x, y, z): nearest, i, distance = self.kdt.query(np.array([x, y, z])) vector = nearest - np.array([x, y, z]) if self.falloff is not None: value = self.falloff(np.array([distance]))[0] if self.negate: value = - value norm = np.linalg.norm(vector) return value * vector / norm else: if self.negate: return - vector else: return vector def evaluate_grid(self, xs, ys, zs): points = np.stack((xs, ys, zs)).T locs, idxs, distances = self.kdt.query_array(points) vectors = locs - points if self.negate: vectors = - vectors if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero] R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): nearest, i, distance = self.kdt.query(np.array([x, y, z])) vector = nearest - np.array([x, y, z]) if self.falloff is not None: value = self.falloff(np.array([distance]))[0] if self.negate: value = - value norm = np.linalg.norm(vector) return value * vector / norm else: if self.negate: return - vector else: return vector
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): points = np.stack((xs, ys, zs)).T locs, idxs, distances = self.kdt.query_array(points) vectors = locs - points if self.negate: vectors = - vectors if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero] R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
class SvLineAttractorVectorField (center, direction, falloff=None)
-
Expand source code
class SvLineAttractorVectorField(SvVectorField): def __init__(self, center, direction, falloff=None): self.center = center self.direction = direction self.falloff = falloff self.__description__ = "Line Attractor" def evaluate(self, x, y, z): vertex = np.array([x,y,z]) direction = self.direction to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / np.dot(direction, direction) dv = to_center - projection if self.falloff is not None: norm = np.linalg.norm(dv) dv = self.falloff(norm) * dv / norm return dv def evaluate_grid(self, xs, ys, zs): direction = self.direction direction2 = np.dot(direction, direction) def func(vertex): to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / direction2 dv = to_center - projection return dv points = np.stack((xs, ys, zs)).T vectors = np.vectorize(func, signature='(3)->(3)')(points) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): vertex = np.array([x,y,z]) direction = self.direction to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / np.dot(direction, direction) dv = to_center - projection if self.falloff is not None: norm = np.linalg.norm(dv) dv = self.falloff(norm) * dv / norm return dv
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): direction = self.direction direction2 = np.dot(direction, direction) def func(vertex): to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / direction2 dv = to_center - projection return dv points = np.stack((xs, ys, zs)).T vectors = np.vectorize(func, signature='(3)->(3)')(points) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
class SvMatrixVectorField (matrix)
-
Expand source code
class SvMatrixVectorField(SvVectorField): def __init__(self, matrix): self.matrix = matrix self.__description__ = "Matrix" def evaluate(self, x, y, z): v = Vector((x, y, z)) v = (self.matrix @ v) - v return np.array(v) def evaluate_grid(self, xs, ys, zs): matrix = np.array(self.matrix.to_3x3()) translation = np.array(self.matrix.translation) points = np.stack((xs, ys, zs)).T R = np.apply_along_axis(lambda v : matrix @ v + translation - v, 1, points).T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): v = Vector((x, y, z)) v = (self.matrix @ v) - v return np.array(v)
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): matrix = np.array(self.matrix.to_3x3()) translation = np.array(self.matrix.translation) points = np.stack((xs, ys, zs)).T R = np.apply_along_axis(lambda v : matrix @ v + translation - v, 1, points).T return R[0], R[1], R[2]
class SvNoiseVectorField (noise_type, seed)
-
Expand source code
class SvNoiseVectorField(SvVectorField): def __init__(self, noise_type, seed): self.noise_type = noise_type self.seed = seed self.__description__ = "{} noise".format(noise_type) def evaluate(self, x, y, z): noise.seed_set(self.seed) v = noise.noise_vector((x, y, z), noise_basis=self.noise_type) return np.array(v) def evaluate_grid(self, xs, ys, zs): noise.seed_set(self.seed) def mk_noise(v): r = noise.noise_vector(v, noise_basis=self.noise_type) return r[0], r[1], r[2] vectors = np.stack((xs,ys,zs)).T return np.vectorize(mk_noise, signature="(3)->(),(),()")(vectors)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): noise.seed_set(self.seed) v = noise.noise_vector((x, y, z), noise_basis=self.noise_type) return np.array(v)
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): noise.seed_set(self.seed) def mk_noise(v): r = noise.noise_vector(v, noise_basis=self.noise_type) return r[0], r[1], r[2] vectors = np.stack((xs,ys,zs)).T return np.vectorize(mk_noise, signature="(3)->(),(),()")(vectors)
class SvPlaneAttractorVectorField (center, direction, falloff=None)
-
Expand source code
class SvPlaneAttractorVectorField(SvVectorField): def __init__(self, center, direction, falloff=None): self.center = center self.direction = direction self.falloff = falloff self.__description__ = "Plane Attractor" def evaluate(self, x, y, z): vertex = np.array([x,y,z]) direction = self.direction to_center = self.center - vertex dv = np.dot(to_center, direction) * direction / np.dot(direction, direction) if self.falloff is not None: norm = np.linalg.norm(dv) dv = self.falloff(norm) * dv / norm return dv def evaluate_grid(self, xs, ys, zs): direction = self.direction direction2 = np.dot(direction, direction) def func(vertex): to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / direction2 return projection points = np.stack((xs, ys, zs)).T vectors = np.vectorize(func, signature='(3)->(3)')(points) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): vertex = np.array([x,y,z]) direction = self.direction to_center = self.center - vertex dv = np.dot(to_center, direction) * direction / np.dot(direction, direction) if self.falloff is not None: norm = np.linalg.norm(dv) dv = self.falloff(norm) * dv / norm return dv
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): direction = self.direction direction2 = np.dot(direction, direction) def func(vertex): to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / direction2 return projection points = np.stack((xs, ys, zs)).T vectors = np.vectorize(func, signature='(3)->(3)')(points) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) lens = self.falloff(norms) nonzero = (norms > 0)[:,0] vectors[nonzero] = vectors[nonzero] / norms[nonzero][:,0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
class SvRelativeVectorField (field)
-
Expand source code
class SvRelativeVectorField(SvVectorField): def __init__(self, field): self.field = field self.__description__ = "Relative({})".format(field) def evaluate(self, x, y, z): r = self.field.evaluate(x, y, z) return r - np.array([x, y, z]) def evaluate_grid(self, xs, ys, zs): rxs, rys, rzs = self.field.evaluate_grid(xs, ys, zs) return rxs - xs, rys - ys, rzs - zs
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): r = self.field.evaluate(x, y, z) return r - np.array([x, y, z])
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): rxs, rys, rzs = self.field.evaluate_grid(xs, ys, zs) return rxs - xs, rys - ys, rzs - zs
class SvRotationVectorField (center, direction, falloff=None)
-
Expand source code
class SvRotationVectorField(SvVectorField): def __init__(self, center, direction, falloff=None): self.center = center self.direction = direction self.falloff = falloff self.__description__ = "Rotation Field" def evaluate(self, x, y, z): vertex = np.array([x,y,z]) direction = self.direction to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / np.dot(direction, direction) dv = np.cross(to_center - projection, direction) if self.falloff is not None: norm = np.linalg.norm(dv) dv = self.falloff(norm) * dv / norm return dv def evaluate_grid(self, xs, ys, zs): direction = self.direction direction2 = np.dot(direction, direction) points = np.stack((xs, ys, zs)).T to_center = self.center[np.newaxis, :] - points projection = direction[np.newaxis, :] * (np_dot(to_center, direction[np.newaxis,:]) / direction2)[:, np.newaxis] vectors = np.cross(to_center - projection, direction) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero][:, 0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): vertex = np.array([x,y,z]) direction = self.direction to_center = self.center - vertex projection = np.dot(to_center, direction) * direction / np.dot(direction, direction) dv = np.cross(to_center - projection, direction) if self.falloff is not None: norm = np.linalg.norm(dv) dv = self.falloff(norm) * dv / norm return dv
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): direction = self.direction direction2 = np.dot(direction, direction) points = np.stack((xs, ys, zs)).T to_center = self.center[np.newaxis, :] - points projection = direction[np.newaxis, :] * (np_dot(to_center, direction[np.newaxis,:]) / direction2)[:, np.newaxis] vectors = np.cross(to_center - projection, direction) if self.falloff is not None: norms = np.linalg.norm(vectors, axis=1, keepdims=True) nonzero = (norms > 0)[:,0] lens = self.falloff(norms) vectors[nonzero] = vectors[nonzero] / norms[nonzero][:, 0][np.newaxis].T R = (lens * vectors).T return R[0], R[1], R[2] else: R = vectors.T return R[0], R[1], R[2]
class SvScalarFieldCurveMap (scalar_field, curve, mode)
-
Expand source code
class SvScalarFieldCurveMap(SvVectorField): def __init__(self, scalar_field, curve, mode): self.scalar_field = scalar_field self.curve = curve self.mode = mode def evaluate(self, x, y, z): t = self.scalar_field.evaluate(x,y,z) if self.mode == 'VALUE': return self.curve.evaluate(t) elif self.mode == 'TANGENT': return self.curve.tangent(t) else: # NORMAL return self.curve.main_normal(t) def evaluate_grid(self, xs, ys, zs): ts = self.scalar_field.evaluate_grid(xs, ys, zs) if self.mode == 'VALUE': vectors = self.curve.evaluate_array(ts) elif self.mode == 'TANGENT': vectors = self.curve.tangent_array(ts) else: # NORMAL vectors = self.curve.main_normal_array(ts) return vectors[:,0], vectors[:,1], vectors[:,2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): t = self.scalar_field.evaluate(x,y,z) if self.mode == 'VALUE': return self.curve.evaluate(t) elif self.mode == 'TANGENT': return self.curve.tangent(t) else: # NORMAL return self.curve.main_normal(t)
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): ts = self.scalar_field.evaluate_grid(xs, ys, zs) if self.mode == 'VALUE': vectors = self.curve.evaluate_array(ts) elif self.mode == 'TANGENT': vectors = self.curve.tangent_array(ts) else: # NORMAL vectors = self.curve.main_normal_array(ts) return vectors[:,0], vectors[:,1], vectors[:,2]
class SvScalarFieldGradient (field, step)
-
Expand source code
class SvScalarFieldGradient(SvVectorField): def __init__(self, field, step): self.field = field self.step = step self.__description__ = "Grad({})".format(field) def evaluate(self, x, y, z): return self.field.gradient([x, y, z], step=self.step) def evaluate_grid(self, xs, ys, zs): return self.field.gradient_grid(xs, ys, zs, step=self.step)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): return self.field.gradient([x, y, z], step=self.step)
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): return self.field.gradient_grid(xs, ys, zs, step=self.step)
class SvSelectVectorField (fields, mode)
-
Expand source code
class SvSelectVectorField(SvVectorField): def __init__(self, fields, mode): self.fields = fields self.mode = mode self.__description__ = "{}({})".format(mode, fields) def evaluate(self, x, y, z): vectors = [field.evaluate(x, y, z) for field in self.fields] vectors = np.array(vectors) norms = np.linalg.norm(vectors, axis=1) if self.mode == 'MIN': selected = np.argmin(norms) else: # MAX selected = np.argmax(norms) return vectors[selected] def evaluate_grid(self, xs, ys, zs): n = len(xs) vectors = [field.evaluate_grid(xs, ys, zs) for field in self.fields] vectors = np.stack(vectors) vectors = np.transpose(vectors, axes=(2,0,1)) norms = np.linalg.norm(vectors, axis=2) if self.mode == 'MIN': selected = np.argmin(norms, axis=1) else: # MAX selected = np.argmax(norms, axis=1) all_points = list(range(n)) vectors = vectors[all_points, selected, :] #print(vectors.shape) return vectors.T
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): vectors = [field.evaluate(x, y, z) for field in self.fields] vectors = np.array(vectors) norms = np.linalg.norm(vectors, axis=1) if self.mode == 'MIN': selected = np.argmin(norms) else: # MAX selected = np.argmax(norms) return vectors[selected]
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): n = len(xs) vectors = [field.evaluate_grid(xs, ys, zs) for field in self.fields] vectors = np.stack(vectors) vectors = np.transpose(vectors, axes=(2,0,1)) norms = np.linalg.norm(vectors, axis=2) if self.mode == 'MIN': selected = np.argmin(norms, axis=1) else: # MAX selected = np.argmax(norms, axis=1) all_points = list(range(n)) vectors = vectors[all_points, selected, :] #print(vectors.shape) return vectors.T
class SvVectorField
-
Expand source code
class SvVectorField(object): def __repr__(self): if hasattr(self, '__description__'): description = self.__description__ else: description = self.__class__.__name__ return "<{} vector field>".format(description) def evaluate(self, point): raise Exception("not implemented") def evaluate_grid(self, xs, ys, zs): raise Exception("not implemented") def evaluate_array(self, points): xs = points[:,0] ys = points[:,1] zs = points[:,2] return self.evaluate_grid(xs, ys, zs)
Subclasses
- SvImageVectorField
- SvBvhRbfNormalVectorField
- SvRbfVectorField
- SvAbsoluteVectorField
- SvAverageVectorField
- SvBendAlongCurveField
- SvBendAlongSurfaceField
- SvBvhAttractorVectorField
- SvCircleAttractorVectorField
- SvComposedVectorField
- SvConstantVectorField
- SvEdgeAttractorVectorField
- SvKdtVectorField
- SvLineAttractorVectorField
- SvMatrixVectorField
- SvNoiseVectorField
- SvPlaneAttractorVectorField
- SvRelativeVectorField
- SvRotationVectorField
- SvScalarFieldCurveMap
- SvScalarFieldGradient
- SvSelectVectorField
- SvVectorFieldBinOp
- SvVectorFieldComposition
- SvVectorFieldCotangent
- SvVectorFieldCrossProduct
- SvVectorFieldLambda
- SvVectorFieldMultipliedByScalar
- SvVectorFieldPointDistance
- SvVectorFieldRotor
- SvVectorFieldTangent
- SvVectorFieldsLerp
- SvVoronoiVectorField
Methods
def evaluate(self, point)
-
Expand source code
def evaluate(self, point): raise Exception("not implemented")
def evaluate_array(self, points)
-
Expand source code
def evaluate_array(self, points): xs = points[:,0] ys = points[:,1] zs = points[:,2] return self.evaluate_grid(xs, ys, zs)
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): raise Exception("not implemented")
class SvVectorFieldBinOp (field1, field2, function)
-
Expand source code
class SvVectorFieldBinOp(SvVectorField): def __init__(self, field1, field2, function): self.function = function self.field1 = field1 self.field2 = field2 self.__description__ = f"<BinOp ({field1}, {field2})>" def evaluate(self, x, y, z): return self.function(self.field1.evaluate(x, y, z), self.field2.evaluate(x, y, z)) def evaluate_grid(self, xs, ys, zs): def func(xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) R = self.function(np.array([vx1, vy1, vz1]), np.array([vx2, vy2, vz2])) return R[0], R[1], R[2] return np.vectorize(func, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): return self.function(self.field1.evaluate(x, y, z), self.field2.evaluate(x, y, z))
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): def func(xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) R = self.function(np.array([vx1, vy1, vz1]), np.array([vx2, vy2, vz2])) return R[0], R[1], R[2] return np.vectorize(func, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
class SvVectorFieldComposition (field1, field2)
-
Expand source code
class SvVectorFieldComposition(SvVectorField): def __init__(self, field1, field2): self.field1 = field1 self.field2 = field2 self.__description__ = "Composition" def evaluate(self, x, y, z): x1, y1, z1 = self.field1.evaluate(x,y,z) v2 = self.field2.evaluate(x1,y1,z1) return v2 def evaluate_grid(self, xs, ys, zs): r = self.field1.evaluate_grid(xs, ys, zs) vx1, vy1, vz1 = r return self.field2.evaluate_grid(vx1, vy1, vz1)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): x1, y1, z1 = self.field1.evaluate(x,y,z) v2 = self.field2.evaluate(x1,y1,z1) return v2
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): r = self.field1.evaluate_grid(xs, ys, zs) vx1, vy1, vz1 = r return self.field2.evaluate_grid(vx1, vy1, vz1)
class SvVectorFieldCotangent (field1, field2)
-
Expand source code
class SvVectorFieldCotangent(SvVectorField): def __init__(self, field1, field2): self.field1 = field1 self.field2 = field2 self.__description__ = "Cotangent" def evaluate(self, x, y, z): v1 = self.field1.evaluate(x,y,z) v2 = self.field2.evaluate(x,y,z) projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) return v1 - projection def evaluate_grid(self, xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)).T vectors2 = np.stack((vx2, vy2, vz2)).T def project(v1, v2): projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) coprojection = v1 - projection vx, vy, vz = coprojection return vx, vy, vz return np.vectorize(project, signature="(3),(3)->(),(),()")(vectors1, vectors2)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): v1 = self.field1.evaluate(x,y,z) v2 = self.field2.evaluate(x,y,z) projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) return v1 - projection
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)).T vectors2 = np.stack((vx2, vy2, vz2)).T def project(v1, v2): projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) coprojection = v1 - projection vx, vy, vz = coprojection return vx, vy, vz return np.vectorize(project, signature="(3),(3)->(),(),()")(vectors1, vectors2)
class SvVectorFieldCrossProduct (field1, field2)
-
Expand source code
class SvVectorFieldCrossProduct(SvVectorField): def __init__(self, field1, field2): self.field1 = field1 self.field2 = field2 self.__description__ = "{} x {}".format(field1, field2) def evaluate(self, x, y, z): v1 = self.field1.evaluate(x, y, z) v2 = self.field2.evaluate(x, y, z) return np.cross(v1, v2) def evaluate_grid(self, xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)).T vectors2 = np.stack((vx2, vy2, vz2)).T R = np.cross(vectors1, vectors2).T return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): v1 = self.field1.evaluate(x, y, z) v2 = self.field2.evaluate(x, y, z) return np.cross(v1, v2)
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)).T vectors2 = np.stack((vx2, vy2, vz2)).T R = np.cross(vectors1, vectors2).T return R[0], R[1], R[2]
class SvVectorFieldLambda (function, variables, in_field, function_numpy=None)
-
Expand source code
class SvVectorFieldLambda(SvVectorField): __description__ = "Formula" def __init__(self, function, variables, in_field, function_numpy = None): self.function = function self.function_numpy = function_numpy self.variables = variables self.in_field = in_field def evaluate_grid(self, xs, ys, zs): if self.in_field is None: Vs = np.zeros(xs.shape[0]) else: vx, vy, vz = self.in_field.evaluate_grid(xs, ys, zs) Vs = np.stack((vx, vy, vz)).T if self.function_numpy is None: return np.vectorize(self.function, signature = "(),(),(),(3)->(),(),()")(xs, ys, zs, Vs) else: Vs = Vs.T return self.function_numpy(xs, ys, zs, Vs) def evaluate(self, x, y, z): if self.in_field is None: V = None else: V = self.in_field.evaluate(x, y, z) return np.array(self.function(x, y, z, V))
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): if self.in_field is None: V = None else: V = self.in_field.evaluate(x, y, z) return np.array(self.function(x, y, z, V))
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): if self.in_field is None: Vs = np.zeros(xs.shape[0]) else: vx, vy, vz = self.in_field.evaluate_grid(xs, ys, zs) Vs = np.stack((vx, vy, vz)).T if self.function_numpy is None: return np.vectorize(self.function, signature = "(),(),(),(3)->(),(),()")(xs, ys, zs, Vs) else: Vs = Vs.T return self.function_numpy(xs, ys, zs, Vs)
class SvVectorFieldMultipliedByScalar (vector_field, scalar_field)
-
Expand source code
class SvVectorFieldMultipliedByScalar(SvVectorField): def __init__(self, vector_field, scalar_field): self.vector_field = vector_field self.scalar_field = scalar_field self.__description__ = "{} * {}".format(scalar_field, vector_field) def evaluate(self, x, y, z): scalar = self.scalar_field.evaluate(x, y, z) vector = self.vector_field.evaluate(x, y, z) return scalar * vector def evaluate_grid(self, xs, ys, zs): def product(xs, ys, zs): scalars = self.scalar_field.evaluate_grid(xs, ys, zs) vx, vy, vz = self.vector_field.evaluate_grid(xs, ys, zs) vectors = np.stack((vx, vy, vz)) R = (scalars * vectors) return R[0], R[1], R[2] return np.vectorize(product, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): scalar = self.scalar_field.evaluate(x, y, z) vector = self.vector_field.evaluate(x, y, z) return scalar * vector
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): def product(xs, ys, zs): scalars = self.scalar_field.evaluate_grid(xs, ys, zs) vx, vy, vz = self.vector_field.evaluate_grid(xs, ys, zs) vectors = np.stack((vx, vy, vz)) R = (scalars * vectors) return R[0], R[1], R[2] return np.vectorize(product, signature="(m),(m),(m)->(m),(m),(m)")(xs, ys, zs)
class SvVectorFieldPointDistance (center, metric='EUCLIDEAN', falloff=None, power=2)
-
Expand source code
class SvVectorFieldPointDistance(SvVectorField): def __init__(self, center, metric='EUCLIDEAN', falloff=None, power=2): self.center = center self.falloff = falloff self.metric = metric self.power = power self.__description__ = "Distance from {}".format(tuple(center)) def evaluate_grid(self, xs, ys, zs): x0, y0, z0 = tuple(self.center) xs = x0 - xs ys = y0 - ys zs = z0 - zs vectors = np.stack((xs, ys, zs)) if self.metric == 'EUCLIDEAN': norms = np.linalg.norm(vectors, axis=0) elif self.metric == 'CHEBYSHEV': norms = np.max(np.abs(vectors), axis=0) elif self.metric == 'MANHATTAN': norms = np.sum(np.abs(vectors), axis=0) elif self.metric == 'CUSTOM': norms = np.linalg.norm(vectors, axis=0, ord=self.power) else: raise Exception('Unknown metric') if self.falloff is not None: lens = self.falloff(norms) R = lens * vectors / norms else: R = vectors return R[0], R[1], R[2] def evaluate(self, x, y, z): point = np.array([x, y, z]) - self.center if self.metric == 'EUCLIDEAN': norm = np.linalg.norm(point) elif self.metric == 'CHEBYSHEV': norm = np.max(point) elif self.metric == 'MANHATTAN': norm = np.sum(np.abs(point)) elif self.metric == 'CUSTOM': norm = np.linalg.norm(point, ord=self.power) else: raise Exception('Unknown metric') if self.falloff is not None: value = self.falloff(np.array([norm]))[0] return value * point / norm else: return point
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): point = np.array([x, y, z]) - self.center if self.metric == 'EUCLIDEAN': norm = np.linalg.norm(point) elif self.metric == 'CHEBYSHEV': norm = np.max(point) elif self.metric == 'MANHATTAN': norm = np.sum(np.abs(point)) elif self.metric == 'CUSTOM': norm = np.linalg.norm(point, ord=self.power) else: raise Exception('Unknown metric') if self.falloff is not None: value = self.falloff(np.array([norm]))[0] return value * point / norm else: return point
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): x0, y0, z0 = tuple(self.center) xs = x0 - xs ys = y0 - ys zs = z0 - zs vectors = np.stack((xs, ys, zs)) if self.metric == 'EUCLIDEAN': norms = np.linalg.norm(vectors, axis=0) elif self.metric == 'CHEBYSHEV': norms = np.max(np.abs(vectors), axis=0) elif self.metric == 'MANHATTAN': norms = np.sum(np.abs(vectors), axis=0) elif self.metric == 'CUSTOM': norms = np.linalg.norm(vectors, axis=0, ord=self.power) else: raise Exception('Unknown metric') if self.falloff is not None: lens = self.falloff(norms) R = lens * vectors / norms else: R = vectors return R[0], R[1], R[2]
class SvVectorFieldRotor (field, step)
-
Expand source code
class SvVectorFieldRotor(SvVectorField): def __init__(self, field, step): self.field = field self.step = step self.__description__ = "Rot({})".format(field) def evaluate(self, x, y, z): step = self.step _, y_dx_plus, z_dx_plus = self.field.evaluate(x+step,y,z) _, y_dx_minus, z_dx_minus = self.field.evaluate(x-step,y,z) x_dy_plus, _, z_dy_plus = self.field.evaluate(x, y+step, z) x_dy_minus, _, z_dy_minus = self.field.evaluate(x, y-step, z) x_dz_plus, y_dz_plus, _ = self.field.evaluate(x, y, z+step) x_dz_minus, y_dz_minus, _ = self.field.evaluate(x, y, z-step) dy_dx = (y_dx_plus - y_dx_minus) / (2*step) dz_dx = (z_dx_plus - z_dx_minus) / (2*step) dx_dy = (x_dy_plus - x_dy_minus) / (2*step) dz_dy = (z_dy_plus - z_dy_minus) / (2*step) dx_dz = (x_dz_plus - x_dz_minus) / (2*step) dy_dz = (y_dz_plus - y_dz_minus) / (2*step) rx = dz_dy - dy_dz ry = - (dz_dx - dx_dz) rz = dy_dx - dx_dy return np.array([rx, ry, rz]) def evaluate_grid(self, xs, ys, zs): step = self.step _, y_dx_plus, z_dx_plus = self.field.evaluate_grid(xs+step,ys,zs) _, y_dx_minus, z_dx_minus = self.field.evaluate_grid(xs-step,ys,zs) x_dy_plus, _, z_dy_plus = self.field.evaluate_grid(xs, ys+step, zs) x_dy_minus, _, z_dy_minus = self.field.evaluate_grid(xs, ys-step, zs) x_dz_plus, y_dz_plus, _ = self.field.evaluate_grid(xs, ys, zs+step) x_dz_minus, y_dz_minus, _ = self.field.evaluate_grid(xs, ys, zs-step) dy_dx = (y_dx_plus - y_dx_minus) / (2*step) dz_dx = (z_dx_plus - z_dx_minus) / (2*step) dx_dy = (x_dy_plus - x_dy_minus) / (2*step) dz_dy = (z_dy_plus - z_dy_minus) / (2*step) dx_dz = (x_dz_plus - x_dz_minus) / (2*step) dy_dz = (y_dz_plus - y_dz_minus) / (2*step) rx = dz_dy - dy_dz ry = - (dz_dx - dx_dz) rz = dy_dx - dx_dy R = np.stack((rx, ry, rz)) return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): step = self.step _, y_dx_plus, z_dx_plus = self.field.evaluate(x+step,y,z) _, y_dx_minus, z_dx_minus = self.field.evaluate(x-step,y,z) x_dy_plus, _, z_dy_plus = self.field.evaluate(x, y+step, z) x_dy_minus, _, z_dy_minus = self.field.evaluate(x, y-step, z) x_dz_plus, y_dz_plus, _ = self.field.evaluate(x, y, z+step) x_dz_minus, y_dz_minus, _ = self.field.evaluate(x, y, z-step) dy_dx = (y_dx_plus - y_dx_minus) / (2*step) dz_dx = (z_dx_plus - z_dx_minus) / (2*step) dx_dy = (x_dy_plus - x_dy_minus) / (2*step) dz_dy = (z_dy_plus - z_dy_minus) / (2*step) dx_dz = (x_dz_plus - x_dz_minus) / (2*step) dy_dz = (y_dz_plus - y_dz_minus) / (2*step) rx = dz_dy - dy_dz ry = - (dz_dx - dx_dz) rz = dy_dx - dx_dy return np.array([rx, ry, rz])
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): step = self.step _, y_dx_plus, z_dx_plus = self.field.evaluate_grid(xs+step,ys,zs) _, y_dx_minus, z_dx_minus = self.field.evaluate_grid(xs-step,ys,zs) x_dy_plus, _, z_dy_plus = self.field.evaluate_grid(xs, ys+step, zs) x_dy_minus, _, z_dy_minus = self.field.evaluate_grid(xs, ys-step, zs) x_dz_plus, y_dz_plus, _ = self.field.evaluate_grid(xs, ys, zs+step) x_dz_minus, y_dz_minus, _ = self.field.evaluate_grid(xs, ys, zs-step) dy_dx = (y_dx_plus - y_dx_minus) / (2*step) dz_dx = (z_dx_plus - z_dx_minus) / (2*step) dx_dy = (x_dy_plus - x_dy_minus) / (2*step) dz_dy = (z_dy_plus - z_dy_minus) / (2*step) dx_dz = (x_dz_plus - x_dz_minus) / (2*step) dy_dz = (y_dz_plus - y_dz_minus) / (2*step) rx = dz_dy - dy_dz ry = - (dz_dx - dx_dz) rz = dy_dx - dx_dy R = np.stack((rx, ry, rz)) return R[0], R[1], R[2]
class SvVectorFieldTangent (field1, field2)
-
Expand source code
class SvVectorFieldTangent(SvVectorField): def __init__(self, field1, field2): self.field1 = field1 self.field2 = field2 self.__description__ = "Tangent" def evaluate(self, x, y, z): v1 = self.field1.evaluate(x,y,z) v2 = self.field2.evaluate(x,y,z) projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) return projection def evaluate_grid(self, xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)).T vectors2 = np.stack((vx2, vy2, vz2)).T def project(v1, v2): projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) vx, vy, vz = projection return vx, vy, vz return np.vectorize(project, signature="(3),(3)->(),(),()")(vectors1, vectors2)
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): v1 = self.field1.evaluate(x,y,z) v2 = self.field2.evaluate(x,y,z) projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) return projection
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): vx1, vy1, vz1 = self.field1.evaluate_grid(xs, ys, zs) vx2, vy2, vz2 = self.field2.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)).T vectors2 = np.stack((vx2, vy2, vz2)).T def project(v1, v2): projection = np.dot(v1, v2) * v2 / np.dot(v2, v2) vx, vy, vz = projection return vx, vy, vz return np.vectorize(project, signature="(3),(3)->(),(),()")(vectors1, vectors2)
class SvVectorFieldsLerp (vfield1, vfield2, scalar_field)
-
Expand source code
class SvVectorFieldsLerp(SvVectorField): def __init__(self, vfield1, vfield2, scalar_field): self.vfield1 = vfield1 self.vfield2 = vfield2 self.scalar_field = scalar_field self.__description__ = "Lerp" def evaluate(self, x, y, z): scalar = self.scalar_field.evaluate(x, y, z) vector1 = self.vfield1.evaluate(x, y, z) vector2 = self.vfield2.evaluate(x, y, z) return (1 - scalar) * vector1 + scalar * vector2 def evaluate_grid(self, xs, ys, zs): scalars = self.scalar_field.evaluate_grid(xs, ys, zs) vx1, vy1, vz1 = self.vfield1.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)) vx2, vy2, vz2 = self.vfield2.evaluate_grid(xs, ys, zs) vectors2 = np.stack((vx2, vy2, vz2)) R = (1 - scalars) * vectors1 + scalars * vectors2 return R[0], R[1], R[2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): scalar = self.scalar_field.evaluate(x, y, z) vector1 = self.vfield1.evaluate(x, y, z) vector2 = self.vfield2.evaluate(x, y, z) return (1 - scalar) * vector1 + scalar * vector2
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): scalars = self.scalar_field.evaluate_grid(xs, ys, zs) vx1, vy1, vz1 = self.vfield1.evaluate_grid(xs, ys, zs) vectors1 = np.stack((vx1, vy1, vz1)) vx2, vy2, vz2 = self.vfield2.evaluate_grid(xs, ys, zs) vectors2 = np.stack((vx2, vy2, vz2)) R = (1 - scalars) * vectors1 + scalars * vectors2 return R[0], R[1], R[2]
class SvVoronoiVectorField (vertices=None, voronoi=None, metric='DISTANCE')
-
Expand source code
class SvVoronoiVectorField(SvVectorField): def __init__(self, vertices=None, voronoi=None, metric='DISTANCE'): if vertices is None and voronoi is None: raise Exception("Either vertices or voronoi must be specified") if voronoi is not None: self.voronoi = voronoi else: self.voronoi = SvVoronoiFieldData(vertices, metric=metric) self.__description__ = "Voronoi" def evaluate(self, x, y, z): r = self.voronoi.query(np.array([x,y,z])) return r[1] def evaluate_grid(self, xs, ys, zs): vs = np.stack((xs,ys,zs)).T r = self.voronoi.query_array(vs) vs = r[1] return vs[:,0], vs[:,1], vs[:,2]
Ancestors
Methods
def evaluate(self, x, y, z)
-
Expand source code
def evaluate(self, x, y, z): r = self.voronoi.query(np.array([x,y,z])) return r[1]
def evaluate_grid(self, xs, ys, zs)
-
Expand source code
def evaluate_grid(self, xs, ys, zs): vs = np.stack((xs,ys,zs)).T r = self.voronoi.query_array(vs) vs = r[1] return vs[:,0], vs[:,1], vs[:,2]