Add my vispy scratch code
This commit is contained in:
parent
10aa28e4b0
commit
14db67426e
117
python/curl_flow.py
Normal file
117
python/curl_flow.py
Normal file
@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Scratch code which (very slowly) animates 3D flow via curl noise.
|
||||
|
||||
import functools
|
||||
|
||||
import numpy as np
|
||||
import vispy
|
||||
import vispy.scene
|
||||
from vispy.scene import visuals
|
||||
|
||||
import opensimplex
|
||||
|
||||
def gradient(fn, eps=1e-3):
|
||||
eps_inv = 1.0 / eps
|
||||
# Numerical gradient by finite differences
|
||||
def _grad(x, y, z):
|
||||
p = fn(x, y, z)
|
||||
p_dx = fn(x+eps, y, z)
|
||||
p_dy = fn(x, y+eps, z)
|
||||
p_dz = fn(x, y, z+eps)
|
||||
return (p_dx - p)*eps_inv, (p_dy - p)*eps_inv, (p_dz - p)*eps_inv
|
||||
return _grad
|
||||
|
||||
def curl_3d(grads):
|
||||
# 'grads' should be of shape (..., 3, 3);
|
||||
# 2nd-to-last dimension is gradient of potential x, y, z.
|
||||
# Last dimension is gradient of that w.r.t. x, y, and z.
|
||||
cx = grads[..., 2, 1] - grads[..., 1, 2]
|
||||
cy = grads[..., 0, 2] - grads[..., 2, 0]
|
||||
cz = grads[..., 1, 0] - grads[..., 0, 1]
|
||||
return np.stack((cx, cy, cz), axis=-1)
|
||||
|
||||
def generate(grid):
|
||||
x_spx = opensimplex.OpenSimplex(seed=0)
|
||||
y_spx = opensimplex.OpenSimplex(seed=12345)
|
||||
z_spx = opensimplex.OpenSimplex(seed=45678)
|
||||
grad_x = gradient(x_spx.noise3d)
|
||||
grad_y = gradient(y_spx.noise3d)
|
||||
grad_z = gradient(z_spx.noise3d)
|
||||
# grad_x = gradient(functools.partial(x_spx.noise4d, t))
|
||||
# grad_y = gradient(functools.partial(y_spx.noise4d, t))
|
||||
# grad_z = gradient(functools.partial(z_spx.noise4d, t))
|
||||
# grad_x = gradient(lambda x,y,z: x_spx.noise3d(x + t, y, z))
|
||||
# grad_y = gradient(lambda x,y,z: y_spx.noise3d(x + t, y, z))
|
||||
# grad_z = gradient(lambda x,y,z: z_spx.noise3d(x + t, y, z))
|
||||
grads = np.array([
|
||||
(grad_x(x,y,z), grad_y(x,y,z), grad_z(x,y,z))
|
||||
for (x,y,z) in grid
|
||||
])
|
||||
curl = curl_3d(grads)
|
||||
return curl
|
||||
|
||||
class Data(object):
|
||||
def __init__(self, view):
|
||||
self.view = view
|
||||
self.visual = vispy.scene.visuals.Line(
|
||||
color=(1,1,1,0.75),
|
||||
connect='segments',
|
||||
)
|
||||
self.view.add(self.visual)
|
||||
self.view.camera = 'turntable' # or try 'arcball'
|
||||
#view.camera = 'arcball'
|
||||
s = 0.1
|
||||
count = 9
|
||||
xs = zs = np.linspace(-s, s, count)
|
||||
ys = np.array([0])
|
||||
self.points = np.array([i.flatten() for i in np.meshgrid(xs,ys,zs)]).T
|
||||
self.points_old = np.zeros((0,3))
|
||||
self.update()
|
||||
def update(self, ev=None):
|
||||
t = 0 if ev is None else ev.elapsed
|
||||
# Get velocity for current points:
|
||||
curl = generate(self.points)
|
||||
#
|
||||
a1 = self.points
|
||||
a2 = self.points + curl*0.005
|
||||
# So I guess if I give argument 'pos', it needs to be of shape
|
||||
# (2*N, 3) and consist of alternating starting & ending vertices?
|
||||
# The docs don't say a thing about this.
|
||||
lines = np.hstack((a1, a2)).reshape(self.points.shape[0]*2, -1)
|
||||
self.points_old = np.vstack((self.points_old, lines))
|
||||
maxpoints = self.points.shape[0] * 200
|
||||
extra = self.points_old.shape[0] - maxpoints
|
||||
if extra > 0:
|
||||
self.points_old = self.points_old[extra:]
|
||||
self.visual.set_data(
|
||||
pos=self.points_old,
|
||||
#arrows=np.hstack((a1, a2)),
|
||||
)
|
||||
self.points = a2
|
||||
# self.scatter = visuals.Markers()
|
||||
# self.scatter.set_data(self.d, edge_color=None, face_color=(1, 0.5, 1, .5), size=4)
|
||||
# view.add(self.scatter)
|
||||
#m = np.array([[np.cos(t), np.sin(t*1.01), np.cos(t*1.02)]])
|
||||
#d2 = self.d*m
|
||||
#self.scatter.set_data(d2, edge_color=None, face_color=(1, 0.5, 1, .5), size=4)
|
||||
|
||||
def main():
|
||||
#
|
||||
# Make a canvas and add simple view
|
||||
#
|
||||
canvas = vispy.scene.SceneCanvas(keys='interactive', show=True)
|
||||
view = canvas.central_widget.add_view()
|
||||
import sys
|
||||
# add a colored 3D axis for orientation
|
||||
axis = visuals.XYZAxis(parent=view.scene)
|
||||
timer = vispy.app.Timer()
|
||||
da = Data(view)
|
||||
# Problem is how slow update() is:
|
||||
timer.connect(da.update)
|
||||
timer.start(0.05)
|
||||
if sys.flags.interactive != 1:
|
||||
vispy.app.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
151
python/curlnoise_fibers.py
Normal file
151
python/curlnoise_fibers.py
Normal file
@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (setq python-shell-interpreter (shell-command-to-string "nix-shell --command \"which python3 | tr -d '\n'\""))
|
||||
|
||||
import functools
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
import vispy
|
||||
import vispy.scene
|
||||
from vispy.scene import visuals
|
||||
|
||||
import opensimplex
|
||||
|
||||
def gradient(fn, eps=1e-3):
|
||||
eps_inv = 1.0 / eps
|
||||
# Numerical gradient by finite differences
|
||||
def _grad(x, y, z):
|
||||
p = fn(x, y, z)
|
||||
p_dx = fn(x+eps, y, z)
|
||||
p_dy = fn(x, y+eps, z)
|
||||
p_dz = fn(x, y, z+eps)
|
||||
return (p_dx - p)*eps_inv, (p_dy - p)*eps_inv, (p_dz - p)*eps_inv
|
||||
return _grad
|
||||
|
||||
def curl_3d(grads):
|
||||
# 'grads' should be of shape (..., 3, 3). Each 3x3 matrix should
|
||||
# be the Jacobian of the potential.
|
||||
cx = grads[..., 2, 1] - grads[..., 1, 2]
|
||||
cy = grads[..., 0, 2] - grads[..., 2, 0]
|
||||
cz = grads[..., 1, 0] - grads[..., 0, 1]
|
||||
return np.stack((cx, cy, cz), axis=-1)
|
||||
|
||||
class Potential(object):
|
||||
def __init__(self):
|
||||
self.x_spx = opensimplex.OpenSimplex(seed=0)
|
||||
self.y_spx = opensimplex.OpenSimplex(seed=12345)
|
||||
self.z_spx = opensimplex.OpenSimplex(seed=45678)
|
||||
def eval(self, x, y, z):
|
||||
y2 = y# + 0.5*math.sin(3*x) + 0.5*math.sin(2.5*z)
|
||||
x2 = x
|
||||
z2 = z
|
||||
f1 = np.array([
|
||||
self.x_spx.noise3d(x2, y2, z2),
|
||||
self.y_spx.noise3d(x2, y2, z2),
|
||||
self.z_spx.noise3d(x2, y2, z2),
|
||||
])
|
||||
f2 = np.array([z*0.5, 0, 0])
|
||||
return f1# + f2
|
||||
def grad(self, x, y, z, eps=1e-3):
|
||||
# Returns gradients in matrix form.
|
||||
#
|
||||
# Element (i, j) is gradient of i'th component with respect to
|
||||
# j'th component, where components are (x,y,z) - i.e. row 0 is
|
||||
# (d/dx Px, d/dy Px, d/dz Px). I'm fairly sure this is just
|
||||
# the Jacobian.
|
||||
p = self.eval(x, y, z)
|
||||
p_dx = self.eval(x+eps, y, z)
|
||||
p_dy = self.eval(x, y+eps, z)
|
||||
p_dz = self.eval(x, y, z+eps)
|
||||
return (np.stack((p_dx, p_dy, p_dz)) - p).T / eps
|
||||
|
||||
def generate(grid):
|
||||
p = Potential()
|
||||
grads = np.array([p.grad(*pt) for pt in grid])
|
||||
curl = curl_3d(grads)
|
||||
return curl
|
||||
|
||||
class Data(object):
|
||||
def __init__(self, view):
|
||||
self.use_tubes = False
|
||||
self.view = view
|
||||
s = 0.1
|
||||
self.s = s
|
||||
count = 8
|
||||
xs = zs = np.linspace(-s, s, count)
|
||||
ys = np.array([0])
|
||||
self.points = np.array([i.flatten() for i in np.meshgrid(xs,ys,zs)]).T
|
||||
self.points_old = None
|
||||
if self.use_tubes:
|
||||
self.visual = vispy.scene.visuals.Line(
|
||||
color=(1,1,1,0.75),
|
||||
connect='segments',
|
||||
)
|
||||
self.view.add(self.visual)
|
||||
self.view.camera = 'turntable'
|
||||
self.update()
|
||||
else:
|
||||
p = self.points
|
||||
points = [p]
|
||||
for i in range(200):
|
||||
print(i)
|
||||
curl = generate(p)
|
||||
p2 = p + curl*0.1*s
|
||||
p = p2
|
||||
points.append(p)
|
||||
points = np.stack(points, axis=1) / s
|
||||
# points = (count*count, N, 3) where first dimension chooses which
|
||||
# trajectory, and second dimension proceeds along time/iterations
|
||||
# of that trajectory.
|
||||
for traj in points:
|
||||
tube = vispy.scene.visuals.Tube(points=traj, radius=0.4*s)
|
||||
self.view.add(tube)
|
||||
self.view.camera = 'turntable'
|
||||
def update(self, ev=None):
|
||||
if not self.use_tubes:
|
||||
return
|
||||
t = 0 if ev is None else ev.elapsed
|
||||
# Get velocity for current points:
|
||||
curl = generate(self.points)
|
||||
#
|
||||
a1 = self.points
|
||||
a2 = self.points + curl*0.1*self.s
|
||||
lines = np.hstack((a1, a2)).reshape(self.points.shape[0]*2, -1) / self.s
|
||||
self.points_old = np.vstack((self.points_old, lines))
|
||||
maxpoints = self.points.shape[0] * 200
|
||||
extra = self.points_old.shape[0] - maxpoints
|
||||
if extra > 0:
|
||||
self.points_old = self.points_old[extra:]
|
||||
self.visual.set_data(
|
||||
pos=self.points_old,
|
||||
#arrows=np.hstack((a1, a2)),
|
||||
)
|
||||
self.points = a2
|
||||
# self.scatter = visuals.Markers()
|
||||
# self.scatter.set_data(self.d, edge_color=None, face_color=(1, 0.5, 1, .5), size=4)
|
||||
# view.add(self.scatter)
|
||||
#m = np.array([[np.cos(t), np.sin(t*1.01), np.cos(t*1.02)]])
|
||||
#d2 = self.d*m
|
||||
#self.scatter.set_data(d2, edge_color=None, face_color=(1, 0.5, 1, .5), size=4)
|
||||
|
||||
def main():
|
||||
#
|
||||
# Make a canvas and add simple view
|
||||
#
|
||||
canvas = vispy.scene.SceneCanvas(keys='interactive', show=True)
|
||||
view = canvas.central_widget.add_view()
|
||||
import sys
|
||||
# add a colored 3D axis for orientation
|
||||
axis = visuals.XYZAxis(parent=view.scene)
|
||||
timer = vispy.app.Timer()
|
||||
da = Data(view)
|
||||
# Problem is how slow update() is:
|
||||
timer.connect(da.update)
|
||||
timer.start(0.05)
|
||||
if sys.flags.interactive != 1:
|
||||
vispy.app.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
5
python/mac_m1_notes.txt
Normal file
5
python/mac_m1_notes.txt
Normal file
@ -0,0 +1,5 @@
|
||||
How I coaxed this into working on ARM64 & Apple Silicon:
|
||||
- Yes, you need Rosetta.
|
||||
- Install Conda.
|
||||
- Run:
|
||||
conda install -c conda-force python=3.9 vispy pytest pyqt
|
||||
1922
python/opensimplex.py
Normal file
1922
python/opensimplex.py
Normal file
File diff suppressed because it is too large
Load Diff
121
python/quiver_plot.py
Normal file
121
python/quiver_plot.py
Normal file
@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Scratch code for visualizing a 3D vector field with arrows
|
||||
|
||||
import functools
|
||||
|
||||
import numpy as np
|
||||
import vispy
|
||||
import vispy.scene
|
||||
from vispy.scene import visuals
|
||||
|
||||
import opensimplex
|
||||
|
||||
def gradient(fn, eps=1e-3):
|
||||
eps_inv = 1.0 / eps
|
||||
# Numerical gradient by finite differences
|
||||
def _grad(x, y, z):
|
||||
p = fn(x, y, z)
|
||||
p_dx = fn(x+eps, y, z)
|
||||
p_dy = fn(x, y+eps, z)
|
||||
p_dz = fn(x, y, z+eps)
|
||||
return (p_dx - p)*eps_inv, (p_dy - p)*eps_inv, (p_dz - p)*eps_inv
|
||||
return _grad
|
||||
|
||||
def curl_3d(grads):
|
||||
# 'grads' should be of shape (..., 3, 3);
|
||||
# 2nd-to-last dimension is gradient of potential x, y, z.
|
||||
# Last dimension is gradient of that w.r.t. x, y, and z.
|
||||
cx = grads[..., 2, 1] - grads[..., 1, 2]
|
||||
cy = grads[..., 0, 2] - grads[..., 2, 0]
|
||||
cz = grads[..., 1, 0] - grads[..., 0, 1]
|
||||
return np.stack((cx, cy, cz), axis=-1)
|
||||
|
||||
def generate(grid, t=0):
|
||||
x_spx = opensimplex.OpenSimplex(seed=0)
|
||||
y_spx = opensimplex.OpenSimplex(seed=12345)
|
||||
z_spx = opensimplex.OpenSimplex(seed=45678)
|
||||
# grad_x = gradient(x_spx.noise3d)
|
||||
# grad_y = gradient(y_spx.noise3d)
|
||||
# grad_z = gradient(z_spx.noise3d)
|
||||
# grad_x = gradient(functools.partial(x_spx.noise4d, t))
|
||||
# grad_y = gradient(functools.partial(y_spx.noise4d, t))
|
||||
# grad_z = gradient(functools.partial(z_spx.noise4d, t))
|
||||
grad_x = gradient(lambda x,y,z: x_spx.noise3d(x + t, y, z))
|
||||
grad_y = gradient(lambda x,y,z: y_spx.noise3d(x + t, y, z))
|
||||
grad_z = gradient(lambda x,y,z: z_spx.noise3d(x + t, y, z))
|
||||
grads = np.array([
|
||||
(grad_x(x,y,z), grad_y(x,y,z), grad_z(x,y,z))
|
||||
for (x,y,z) in grid
|
||||
])
|
||||
curl = curl_3d(grads)
|
||||
return grid, curl
|
||||
#pos = np.random.normal(size=(count, 3), scale=0.2)
|
||||
# one could stop here for the data generation, the rest is just to make the
|
||||
# data look more interesting. Copied over from magnify.py
|
||||
#return pos
|
||||
# centers = np.random.normal(size=(50, 3))
|
||||
# indexes = np.random.normal(size=count, loc=centers.shape[0]/2.,
|
||||
# scale=centers.shape[0]/3.)
|
||||
# indexes = np.clip(indexes, 0, centers.shape[0]-1).astype(int)
|
||||
# scales = 2**(np.linspace(-2, 0.5, centers.shape[0]))[indexes][:, np.newaxis]
|
||||
# pos *= scales
|
||||
# pos += centers[indexes]
|
||||
# return pos
|
||||
|
||||
class Data(object):
|
||||
def __init__(self, view):
|
||||
self.view = view
|
||||
self.visual = vispy.scene.visuals.Arrow(
|
||||
color=(1,1,1,0.75),
|
||||
connect='segments',
|
||||
arrow_size=8,
|
||||
arrow_type="triangle_30",
|
||||
)
|
||||
self.view.add(self.visual)
|
||||
self.view.camera = 'turntable' # or try 'arcball'
|
||||
#view.camera = 'arcball'
|
||||
s = 1
|
||||
count = 8
|
||||
xs = zs = np.linspace(-s, s, count)
|
||||
ys = np.array([0])
|
||||
self.grid = np.array([i.flatten() for i in np.meshgrid(xs,ys,zs)]).T
|
||||
self.update()
|
||||
def update(self, ev=None):
|
||||
t = 0 if ev is None else ev.elapsed
|
||||
grid, curl = generate(self.grid, t*0.4)
|
||||
a1 = grid
|
||||
a2 = grid + curl*0.1
|
||||
# So I guess if I give argument 'pos', it needs to be of shape
|
||||
# (2*N, 3) and consist of alternating starting & ending vertices?
|
||||
# The docs don't say a thing about this.
|
||||
self.visual.set_data(
|
||||
pos=np.hstack((a1, a2)).reshape(grid.shape[0]*2, -1),
|
||||
arrows=np.hstack((a1, a2)),
|
||||
)
|
||||
# self.scatter = visuals.Markers()
|
||||
# self.scatter.set_data(self.d, edge_color=None, face_color=(1, 0.5, 1, .5), size=4)
|
||||
# view.add(self.scatter)
|
||||
#m = np.array([[np.cos(t), np.sin(t*1.01), np.cos(t*1.02)]])
|
||||
#d2 = self.d*m
|
||||
#self.scatter.set_data(d2, edge_color=None, face_color=(1, 0.5, 1, .5), size=4)
|
||||
|
||||
def main():
|
||||
#
|
||||
# Make a canvas and add simple view
|
||||
#
|
||||
canvas = vispy.scene.SceneCanvas(keys='interactive', show=True)
|
||||
view = canvas.central_widget.add_view()
|
||||
import sys
|
||||
# add a colored 3D axis for orientation
|
||||
axis = visuals.XYZAxis(parent=view.scene)
|
||||
timer = vispy.app.Timer()
|
||||
da = Data(view)
|
||||
# Problem is how slow update() is:
|
||||
timer.connect(da.update)
|
||||
timer.start(0.05)
|
||||
if sys.flags.interactive != 1:
|
||||
vispy.app.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
15
python/shell.nix
Normal file
15
python/shell.nix
Normal file
@ -0,0 +1,15 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
name = "vispy-test";
|
||||
|
||||
# PyQt5 should be fine but seems to have other issues
|
||||
buildInputs = [
|
||||
(pkgs.python3.withPackages
|
||||
(ps: [
|
||||
ps.vispy
|
||||
ps.wxPython_4_0
|
||||
ps.sympy
|
||||
ps.jupyterlab
|
||||
]))
|
||||
];
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user