|
{ |
|
"cells": [ |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"from collections import defaultdict, namedtuple\n", |
|
"from itertools import repeat\n", |
|
"import numpy as np\n", |
|
"import vispy\n", |
|
"from vispy import app\n", |
|
"app.use_app('ipynb_webgl')\n", |
|
"from vispy import gloo\n", |
|
"\n", |
|
"from plato.mesh import unfoldProperties" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"PLANE_COUNT = 8\n", |
|
"\n", |
|
"quatGLSL = \"\"\"\n", |
|
"vec4 quatquat(vec4 a, vec4 b)\n", |
|
"{\n", |
|
" float real = a.x*b.x - dot(a.yzw, b.yzw);\n", |
|
" vec3 imag = a.x*b.yzw + b.x*a.yzw + cross(a.yzw, b.yzw);\n", |
|
" return vec4(real, imag);\n", |
|
"}\n", |
|
"\n", |
|
"vec3 rotate(vec3 point, vec4 quat)\n", |
|
"{\n", |
|
" vec4 pointquat = vec4(0.0, point);\n", |
|
" vec4 quatconj = vec4(-quat.x, quat.yzw);\n", |
|
" return quatquat(quat, quatquat(pointquat, quatconj)).yzw;\n", |
|
"}\n", |
|
"\"\"\"\n", |
|
"\n", |
|
"vertexShader = \"\"\"\n", |
|
"\n", |
|
"uniform mat4 camera;\n", |
|
"\"\"\" + \"uniform vec4 planeEquations[{}];\".format(PLANE_COUNT) + \"\"\"\n", |
|
"uniform vec4 cam_rotation;\n", |
|
"uniform vec3 cam_translation;\n", |
|
"uniform float radius;\n", |
|
"\n", |
|
"attribute vec4 orientation;\n", |
|
"attribute vec4 color;\n", |
|
"attribute vec3 position;\n", |
|
"attribute vec2 image;\n", |
|
"\n", |
|
"varying vec4 v_color;\n", |
|
"varying vec4 v_orientation;\n", |
|
"varying vec2 v_image;\n", |
|
"\n", |
|
"\"\"\" + quatGLSL + \"\"\"\n", |
|
"\n", |
|
"void main()\n", |
|
"{\n", |
|
" vec3 vertexPos = position;\n", |
|
" vertexPos = rotate(vertexPos, cam_rotation) + vec3(image*radius, 0.0) + cam_translation;\n", |
|
" vec4 screenPosition = camera * vec4(vertexPos, 1.0);\n", |
|
"\n", |
|
" // transform to screen coordinates\n", |
|
" gl_Position = screenPosition;\n", |
|
" v_color = color;\n", |
|
" v_orientation = quatquat(cam_rotation, orientation);\n", |
|
" v_image = image;\n", |
|
"}\n", |
|
"\"\"\"\n", |
|
"\n", |
|
"fragmentShader = \"\"\"\n", |
|
"\n", |
|
"varying vec4 v_color;\n", |
|
"varying vec2 v_image;\n", |
|
"varying vec4 v_orientation;\n", |
|
"\n", |
|
"// base light level\n", |
|
"uniform float ambientLight;\n", |
|
"// (x, y, z) direction*intensity\n", |
|
"uniform vec3 diffuseLight;\n", |
|
"\"\"\" + \"uniform vec4 planeEquations[{}];\".format(PLANE_COUNT) + \"\"\"\n", |
|
"\n", |
|
"uniform float radius;\n", |
|
"\n", |
|
"\"\"\" + quatGLSL + \"\"\"\n", |
|
"\n", |
|
"void main()\n", |
|
"{\n", |
|
" vec3 normal = vec3(0.0, 0.0, 0.0);\n", |
|
" float mindist = 1.0e6;\n", |
|
" float rsq = dot(v_image, v_image);\n", |
|
"\n", |
|
" if(rsq > radius*radius)\n", |
|
" discard;\n", |
|
"\n", |
|
" vec3 r_local = vec3(v_image.xy, sqrt(radius*radius - rsq));\n", |
|
"\n", |
|
" mindist = sqrt(radius*radius - rsq);\n", |
|
" float maxdist = -mindist;\n", |
|
" normal = (1.0/sqrt(dot(r_local, r_local)))*r_local;\n", |
|
"\n", |
|
"\"\"\" + \"for(int planeidx = 0; planeidx < {}; ++planeidx)\".format(PLANE_COUNT) + \"\"\"\n", |
|
" {\n", |
|
" vec3 planeNormal = rotate(planeEquations[planeidx].xyz, v_orientation);\n", |
|
" vec4 planeEquation = vec4(planeNormal, planeEquations[planeidx].w);\n", |
|
"\n", |
|
" float planeZ = (planeEquation.w -\n", |
|
" dot(v_image, planeEquation.xy))/planeEquation.z;\n", |
|
"\n", |
|
" vec3 planeIntersection = vec3(v_image.xy, planeZ);\n", |
|
"\n", |
|
" // plane is facing viewer and the intersection is outside the sphere\n", |
|
" if(planeZ < maxdist && planeEquation.z >= 0.0)\n", |
|
" discard;\n", |
|
" // plane is facing viewer, intersection is inside the sphere\n", |
|
" else if(planeEquation.z >= 0.0 && planeZ < mindist &&\n", |
|
" dot(planeIntersection, planeIntersection) < radius*radius)\n", |
|
" {\n", |
|
" mindist = planeZ;\n", |
|
" normal = planeEquation.xyz;\n", |
|
" }\n", |
|
" // plane is not facing viewer, intersection is outside the sphere\n", |
|
" else if(planeEquation.z < 0.0 && planeZ >= mindist)\n", |
|
" discard;\n", |
|
"\n", |
|
" if(planeZ > maxdist && planeZ < mindist)\n", |
|
" maxdist = planeZ;\n", |
|
" }\n", |
|
"\n", |
|
" float light = max(0.0, dot(normal, diffuseLight));\n", |
|
" light += ambientLight;\n", |
|
" gl_FragColor = vec4(v_color.xyz*light, v_color.w);\n", |
|
"}\n", |
|
"\"\"\"" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"class MouseManager:\n", |
|
" def __init__(self, canvas, program, translation=[0, 0, 0], orientation=[1, 0, 0, 0], camera=np.eye(4)):\n", |
|
" self._canvas = canvas\n", |
|
" self._program = program\n", |
|
" self._translation = np.asarray(translation, dtype=np.float32)\n", |
|
" self._orientation = np.asarray(orientation, dtype=np.float32)\n", |
|
" self._camera = np.asarray(camera, dtype=np.float32)\n", |
|
"\n", |
|
" self._program['camera'] = self._camera\n", |
|
"\n", |
|
" self._mouse_origin = np.array([0, 0], dtype=np.float32)\n", |
|
" self._canvas_size = np.sqrt(1280*1024)\n", |
|
"\n", |
|
" self._canvas.connect(self.on_mouse_move)\n", |
|
" self._canvas.connect(self.on_mouse_press)\n", |
|
" self._canvas.connect(self.on_mouse_wheel)\n", |
|
" self._canvas.connect(self.on_resize)\n", |
|
"\n", |
|
" def on_mouse_move(self, event):\n", |
|
" if 1 in event.buttons:\n", |
|
" delta = (event.pos - self._mouse_origin)/self._canvas_size\n", |
|
" self._mouse_origin[:] = event.pos\n", |
|
" self.updateRotation(delta)\n", |
|
"\n", |
|
" def on_mouse_press(self, event):\n", |
|
" self._mouse_origin[:] = event.pos\n", |
|
"\n", |
|
" def on_mouse_wheel(self, event):\n", |
|
" self._camera[[0, 1], [0, 1]] *= 1.1**event.delta[1]\n", |
|
" self._program['camera'] = self._camera\n", |
|
" self._canvas.update()\n", |
|
"\n", |
|
" def on_resize(self, event):\n", |
|
" self._canvas_size = np.sqrt(event.size[0]*event.size[1])\n", |
|
"\n", |
|
" def updateRotation(self, delta=(0, 0)):\n", |
|
" delta = np.asarray(delta, dtype=np.float32)[::-1]\n", |
|
" theta = np.sqrt(np.sum(delta**2))\n", |
|
"\n", |
|
" if theta > 1e-5:\n", |
|
" norm = delta/theta\n", |
|
" theta *= 3.\n", |
|
" quat = np.array([np.cos(theta/2), np.sin(theta/2)*norm[0], np.sin(theta/2)*norm[1], 0])\n", |
|
" real = self._orientation[0]*quat[0] - np.dot(self._orientation[1:], quat[1:])\n", |
|
" imag = self._orientation[0]*quat[1:] + quat[0]*self._orientation[1:] + np.cross(quat[1:], self._orientation[1:])\n", |
|
" self._orientation[0] = real\n", |
|
" self._orientation[1:] = imag\n", |
|
"\n", |
|
" self._program['cam_rotation'] = self._orientation\n", |
|
" self._canvas.update()" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"positions = [[0, 0, 0], [.55, 0, 0]]\n", |
|
"orientations = [[1,0,0,0] for _ in positions]\n", |
|
"orientations[1] = [np.cos(np.pi/6), 0, np.sin(np.pi/6), 0]\n", |
|
"colors = [[.5, .5, .75, 1] for _ in positions]\n", |
|
"# radii = [0.5 for _ in positions]\n", |
|
"# planeEqn = [(1, 0, 0, 0.25) for _ in positions]\n", |
|
"# planeEqn2 = [(0.15, 0.7, 0, 0.28) for _ in positions]\n", |
|
"planeEqns = np.zeros((PLANE_COUNT, 4))\n", |
|
"planeEqns[:, 0] = 1\n", |
|
"planeEqns[:, 3] = 0.5\n", |
|
"\n", |
|
"planeEqns[0] = (0, 0, 1, .45)\n", |
|
"planeEqns[1] = (1, 1, 0, .25)\n", |
|
"planeEqns[2] = (-1, -.15, 0, .4)\n", |
|
"\n", |
|
"image = np.array([[2*np.sqrt(2)*1.05, -1.05],\n", |
|
" [-1.05, 2*np.sqrt(2)*1.05],\n", |
|
" [-1.05, -1.05]])\n", |
|
"\n", |
|
"indices = np.array([0, 1, 2], dtype=np.uint32)\n", |
|
"\n", |
|
"[positions, orientations, colors, image] = unfoldProperties([positions, orientations, colors], [image])\n", |
|
"indices = np.arange(positions.shape[0])[:, np.newaxis, np.newaxis]*positions.shape[1] + indices" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"c = app.Canvas(keys='interactive')\n", |
|
"program = gloo.Program(vertexShader, fragmentShader)\n", |
|
"\n", |
|
"program['ambientLight'] = 0.5\n", |
|
"program['diffuseLight'] = np.array([.25, .5, .75], dtype=np.float32)/np.sqrt(3)\n", |
|
"program['cam_rotation'] = np.array([1, 0, 0, 0], dtype=np.float32)\n", |
|
"program['cam_translation'] = np.array([0, 0, -5], dtype=np.float32)\n", |
|
"program['radius'] = 0.5\n", |
|
"for (i, eqn) in enumerate(planeEqns):\n", |
|
" program['planeEquations[{}]'.format(i)] = eqn\n", |
|
"\n", |
|
"indicesBuf = vispy.gloo.IndexBuffer(np.asarray(indices.flat, dtype=np.uint16))\n", |
|
"program['orientation'] = np.asarray(orientations.reshape((-1, 4)), dtype=np.float32)\n", |
|
"program['position'] = np.asarray(positions.reshape((-1, 3)), dtype=np.float32)\n", |
|
"program['color'] = np.asarray(colors.reshape((-1, 4)), dtype=np.float32)\n", |
|
"program['image'] = np.asarray(image.reshape((-1, 2)), dtype=np.float32)\n", |
|
"\n", |
|
"(width, height) = c.size\n", |
|
"camera = vispy.util.transforms.perspective(90, float(width)/height, 4, 6)\n", |
|
"camera[[0, 1], [0, 1]] *= 6\n", |
|
"mouse = MouseManager(c, program, camera=camera)\n", |
|
"\n", |
|
"@c.connect\n", |
|
"def on_resize(event):\n", |
|
" gloo.set_viewport(0, 0, *event.size)\n", |
|
"\n", |
|
"@c.connect\n", |
|
"def on_draw(event):\n", |
|
" gloo.clear((1,1,1,1))\n", |
|
" program.draw('triangles', indices=indicesBuf)\n", |
|
"\n", |
|
"gloo.set_state('opaque')\n", |
|
"c.show()" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
} |
|
], |
|
"metadata": { |
|
"kernelspec": { |
|
"display_name": "Python 3", |
|
"language": "python", |
|
"name": "python3" |
|
}, |
|
"language_info": { |
|
"codemirror_mode": { |
|
"name": "ipython", |
|
"version": 3 |
|
}, |
|
"file_extension": ".py", |
|
"mimetype": "text/x-python", |
|
"name": "python", |
|
"nbconvert_exporter": "python", |
|
"pygments_lexer": "ipython3", |
|
"version": "3.7.3" |
|
} |
|
}, |
|
"nbformat": 4, |
|
"nbformat_minor": 2 |
|
} |