summaryrefslogtreecommitdiff
path: root/interacticle/visual
diff options
context:
space:
mode:
Diffstat (limited to 'interacticle/visual')
-rw-r--r--interacticle/visual/__init__.py5
-rw-r--r--interacticle/visual/box.py67
-rw-r--r--interacticle/visual/camera.py106
-rw-r--r--interacticle/visual/links.py48
-rw-r--r--interacticle/visual/plot.py248
-rw-r--r--interacticle/visual/shader.py28
-rw-r--r--interacticle/visual/view.py242
7 files changed, 744 insertions, 0 deletions
diff --git a/interacticle/visual/__init__.py b/interacticle/visual/__init__.py
new file mode 100644
index 0000000..a9f9870
--- /dev/null
+++ b/interacticle/visual/__init__.py
@@ -0,0 +1,5 @@
+from .box import WireBox
+from .view import View
+from .links import MolecularLinks
+from .plot import VelocityHistogram
+from .plot import TemperaturePlot
diff --git a/interacticle/visual/box.py b/interacticle/visual/box.py
new file mode 100644
index 0000000..fb18d84
--- /dev/null
+++ b/interacticle/visual/box.py
@@ -0,0 +1,67 @@
+from OpenGL.GL import *
+
+class WireBox:
+ def __init__(self, size):
+ self.x0 = 0
+ self.x1 = size
+ self.y0 = 0
+ self.y1 = size
+ self.z0 = 0
+ self.z1 = size
+
+ def setup(self):
+ pass
+
+ def update(self):
+ pass
+
+ def shutdown(self):
+ pass
+
+ def display_decoration(self, uniform):
+ glUniform3fv(uniform['color'], 1, [0.6,0.6,0.6])
+ glBegin(GL_LINE_STRIP)
+ glVertex(self.x0, self.y0, self.z0)
+ glVertex(self.x0, self.y1, self.z0)
+ glVertex(self.x0, self.y1, self.z1)
+ glVertex(self.x0, self.y0, self.z1)
+ glVertex(self.x0, self.y0, self.z0)
+ glEnd()
+ glBegin(GL_LINE_STRIP)
+ glVertex(self.x1, self.y0, self.z0)
+ glVertex(self.x1, self.y1, self.z0)
+ glVertex(self.x1, self.y1, self.z1)
+ glVertex(self.x1, self.y0, self.z1)
+ glVertex(self.x1, self.y0, self.z0)
+ glEnd()
+ glBegin(GL_LINE_STRIP)
+ glVertex(self.x0, self.y0, self.z1)
+ glVertex(self.x1, self.y0, self.z1)
+ glVertex(self.x1, self.y1, self.z1)
+ glVertex(self.x0, self.y1, self.z1)
+ glVertex(self.x0, self.y0, self.z1)
+ glEnd()
+ glBegin(GL_LINE_STRIP)
+ glVertex(self.x0, self.y0, self.z0)
+ glVertex(self.x1, self.y0, self.z0)
+ glVertex(self.x1, self.y1, self.z0)
+ glVertex(self.x0, self.y1, self.z0)
+ glVertex(self.x0, self.y0, self.z0)
+ glEnd()
+ glBegin(GL_LINE_STRIP)
+ glVertex(self.x0, self.y0, self.z0)
+ glVertex(self.x1, self.y0, self.z0)
+ glVertex(self.x1, self.y0, self.z1)
+ glVertex(self.x0, self.y0, self.z1)
+ glVertex(self.x0, self.y0, self.z0)
+ glEnd()
+ glBegin(GL_LINE_STRIP)
+ glVertex(self.x0,self.y1,self.z0)
+ glVertex(self.x1,self.y1,self.z0)
+ glVertex(self.x1,self.y1,self.z1)
+ glVertex(self.x0,self.y1,self.z1)
+ glVertex(self.x0,self.y1,self.z0)
+ glEnd()
+
+ def display_window(self, uniform):
+ pass
diff --git a/interacticle/visual/camera.py b/interacticle/visual/camera.py
new file mode 100644
index 0000000..b1e9650
--- /dev/null
+++ b/interacticle/visual/camera.py
@@ -0,0 +1,106 @@
+from OpenGL.GL import *
+from OpenGL.GLUT import *
+
+from pyrr import matrix44, quaternion
+
+import numpy as np
+
+class Projection:
+ def __init__(self, distance):
+ self.distance = distance
+ self.ratio = 4./3.
+ self.x = 0
+ self.y = 0
+ self.update()
+
+ def update(self):
+ projection = matrix44.create_perspective_projection(20.0, self.ratio, 0.1, 5000.0)
+ look = matrix44.create_look_at(
+ eye = [self.x, -self.distance, self.y],
+ target = [self.x, 0, self.y],
+ up = [ 0, 0,-1])
+
+ self.matrix = np.matmul(look, projection)
+
+ def update_ratio(self, width, height, update_viewport = True):
+ if update_viewport:
+ glViewport(0,0,width,height)
+
+ self.ratio = width/height
+ self.update()
+
+ def update_distance(self, change):
+ self.distance += change
+ self.update()
+
+ def shift(self, x, y):
+ self.x -= x
+ self.y -= y
+ self.update()
+
+ def get(self):
+ return self.matrix
+
+class Rotation:
+ def __init__(self, shift, x = np.pi, y = np.pi):
+ self.matrix = matrix44.create_from_translation(shift),
+ self.rotation_x = quaternion.Quaternion()
+ self.update(x,y)
+
+ def shift(self, x, y):
+ self.matrix = np.matmul(
+ self.matrix,
+ matrix44.create_from_translation([x,y,0])
+ )
+ self.inverse_matrix = np.linalg.inv(self.matrix)
+
+ def update(self, x, y):
+ rotation_x = quaternion.Quaternion(quaternion.create_from_eulers([x,0,0]))
+ rotation_y = self.rotation_x.conjugate.cross(
+ quaternion.Quaternion(quaternion.create_from_eulers([0,y,0])))
+ self.rotation_x = self.rotation_x.cross(rotation_x)
+
+ self.matrix = np.matmul(
+ self.matrix,
+ matrix44.create_from_quaternion(rotation_y.cross(self.rotation_x))
+ )
+ self.inverse_matrix = np.linalg.inv(self.matrix)
+
+ def get(self):
+ return self.matrix
+
+ def get_inverse(self):
+ return self.inverse_matrix
+
+class MouseDragMonitor:
+ def __init__(self, button, callback):
+ self.button = button
+ self.active = False
+ self.callback = callback
+
+ def on_mouse(self, button, state, x, y):
+ if button == self.button:
+ self.active = (state == GLUT_DOWN)
+ self.last_x = x
+ self.last_y = y
+
+ def on_mouse_move(self, x, y):
+ if self.active:
+ delta_x = self.last_x - x
+ delta_y = y - self.last_y
+ self.last_x = x
+ self.last_y = y
+ self.callback(delta_x, delta_y)
+
+class MouseScrollMonitor:
+ def __init__(self, callback):
+ self.callback = callback
+
+ def on_mouse(self, button, state, x, y):
+ if button == 3:
+ self.callback(-1.0)
+ elif button == 4:
+ self.callback(1.0)
+
+ def on_mouse_move(self, x, y):
+ pass
diff --git a/interacticle/visual/links.py b/interacticle/visual/links.py
new file mode 100644
index 0000000..52e9d52
--- /dev/null
+++ b/interacticle/visual/links.py
@@ -0,0 +1,48 @@
+import numpy as np
+
+import pyopencl as cl
+mf = cl.mem_flags
+
+import OpenGL.GL as gl
+from OpenGL.arrays import vbo
+
+class MolecularLinks:
+ def __init__(self, simulation):
+ self.simulation = simulation
+
+ def setup(self):
+ if self.simulation.n_bonds > 0:
+ self.np_view_link_indices = np.zeros((int(np.sum(self.simulation.np_bond_count)*2),1), dtype=np.uint32)
+ curr = 0
+ for i in range(self.simulation.n_atoms):
+ for j in range(int(self.simulation.np_bond_count[i])):
+ self.np_view_link_indices[2*curr+0] = i
+ self.np_view_link_indices[2*curr+1] = self.simulation.np_bond_indices[self.simulation.max_bonds*i+j][0]
+ curr = curr + 1
+
+ self.cl_view_link_indices = cl.Buffer(self.simulation.context, mf.COPY_HOST_PTR, hostbuf=self.np_view_link_indices)
+ self.np_view_link_pos = np.zeros((self.np_view_link_indices.shape[0],4), dtype=np.float32)
+ self.gl_view_link_pos = vbo.VBO(data=self.np_view_link_pos, usage=gl.GL_DYNAMIC_DRAW, target=gl.GL_ARRAY_BUFFER)
+ self.gl_view_link_pos.bind()
+ self.cl_view_link_pos = cl.GLBuffer(self.simulation.context, mf.READ_WRITE, int(self.gl_view_link_pos))
+
+ def update(self):
+ pass
+
+ def shutdown(self):
+ pass
+
+ def display_decoration(self, uniform):
+ if self.simulation.n_bonds > 0:
+ cl.enqueue_acquire_gl_objects(self.simulation.queue, [self.cl_view_link_pos])
+ self.simulation.program.update_view_links(self.simulation.queue, (int(self.np_view_link_indices.shape[0] / 2),), None, self.simulation.cl_position, self.cl_view_link_indices, self.cl_view_link_pos).wait()
+
+ gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
+ gl.glUniform3fv(uniform['color'], 1, [0.5,0.5,0.5])
+ self.gl_view_link_pos.bind()
+ gl.glVertexPointer(4, gl.GL_FLOAT, 0, self.simulation.gl_position)
+ gl.glDrawArrays(gl.GL_LINES, 0, self.np_view_link_indices.shape[0])
+ gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
+
+ def display_window(self, uniform):
+ pass
diff --git a/interacticle/visual/plot.py b/interacticle/visual/plot.py
new file mode 100644
index 0000000..60b4937
--- /dev/null
+++ b/interacticle/visual/plot.py
@@ -0,0 +1,248 @@
+from OpenGL.GL import *
+
+import numpy as np
+
+import scipy.stats as stats
+import scipy.constants as const
+from scipy.optimize import minimize
+
+import matplotlib
+import matplotlib.pyplot as plt
+
+from concurrent.futures import Future, ProcessPoolExecutor
+
+def get_histogram(velocities):
+ upper = np.ceil(np.max(velocities) / 100) * 100
+ maxwellian = stats.maxwell.fit(velocities)
+
+ plt.style.use('dark_background')
+
+ fig, ax = plt.subplots(1, figsize=(8,4))
+
+ #plt.ylim(0, 0.004)
+ plt.ylabel('Probability')
+
+ plt.xlim(0, upper)
+ plt.xlabel('Velocity magnitude [m/s]')
+
+ plt.hist(velocities, bins=50, density=True, alpha=0.5, label='Simulated velocities')
+
+ xs = np.linspace(0, upper, 100)
+ ys = stats.maxwell.pdf(xs, *maxwellian)
+ plt.plot(xs, ys, label='Maxwell-Boltzmann distribution')
+
+ plt.legend(loc='upper right')
+
+ fig.canvas.draw()
+ buf = fig.canvas.tostring_rgb()
+ width, height = fig.canvas.get_width_height()
+ texture = np.frombuffer(buf, dtype=np.uint8).reshape(width, height, 3)
+
+ plt.close(fig)
+
+ return (texture, width, height)
+
+
+class VelocityHistogram:
+ def __init__(self, gas, origin, extend):
+ self.gas = gas
+ self.origin = origin
+ self.extend = extend
+ self.steps = -1
+
+ self.pool = ProcessPoolExecutor(max_workers=1)
+ self.plotter = None
+
+ self.tick = False
+ self.mixing = 0.0
+
+ self.vertices = np.array([
+ self.origin[0] , self.origin[1] , 0., 1.,
+ self.origin[0] + self.extend[0], self.origin[1] , 1., 1.,
+ self.origin[0] , self.origin[1] + self.extend[1], 0., 0.,
+ self.origin[0] + self.extend[0], self.origin[1] + self.extend[1], 1., 0.
+ ], dtype=np.float32)
+
+ def setup(self):
+ self.vao = glGenVertexArrays(1)
+ self.vbo = glGenBuffers(1)
+
+ glBindVertexArray(self.vao)
+ glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
+ glBufferData(GL_ARRAY_BUFFER, self.vertices.tostring(), GL_STATIC_DRAW)
+
+ glEnableVertexAttribArray(0)
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(0))
+ glEnableVertexAttribArray(1)
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(2*np.dtype(np.float32).itemsize))
+
+ self.texture_id = glGenTextures(2)
+
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[0])
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[1])
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ def shutdown(self):
+ self.pool.shutdown(wait=True)
+
+ def update(self):
+ self.steps = self.steps + 1
+
+ if self.steps % 50 == 0 and self.plotter == None:
+ self.plotter = self.pool.submit(get_histogram, self.gas.get_velocity_norms() * 1000)
+
+ else:
+ if self.plotter != None and self.plotter.done():
+ texture, width, height = self.plotter.result()
+ if self.tick:
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[0])
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture)
+ self.tick = False
+ self.mixing = 1.0
+
+ else:
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[1])
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture)
+ self.tick = True
+ self.mixing = 0.0
+
+ self.plotter = None
+
+ def display_decoration(self, uniform):
+ pass
+
+ def display_window(self, uniform):
+ if self.tick:
+ self.mixing = min(self.mixing+0.05, 1.0);
+ else:
+ self.mixing = max(self.mixing-0.05, 0.0);
+
+ glBindTextures(self.texture_id[0], 2, self.texture_id)
+ glUniform1iv(uniform['picture'], len(self.texture_id), self.texture_id)
+ glUniform1f(uniform['mixing'], self.mixing)
+
+ glBindVertexArray(self.vao);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
+ glBindVertexArray(0)
+
+def get_plot(data):
+ xs = data[0]
+ ys = data[1]
+
+ plt.style.use('dark_background')
+
+ fig, ax = plt.subplots(1, figsize=(8,4))
+
+ plt.ylim(0, 350)
+ plt.ylabel('Temperature [K]')
+
+ plt.xlim(0, 200)
+ plt.xlabel('Time [ps]')
+
+ plt.plot(xs, ys, label='Temperature')
+
+ plt.legend(loc='upper right')
+
+ fig.canvas.draw()
+ buf = fig.canvas.tostring_rgb()
+ width, height = fig.canvas.get_width_height()
+ texture = np.frombuffer(buf, dtype=np.uint8).reshape(width, height, 3)
+
+ plt.close(fig)
+
+ return (texture, width, height)
+
+class TemperaturePlot:
+ def __init__(self, gas, origin, extend):
+ self.gas = gas
+ self.origin = origin
+ self.extend = extend
+ self.steps = -1
+
+ self.pool = ProcessPoolExecutor(max_workers=1)
+ self.plotter = None
+
+ self.tick = False
+ self.mixing = 0.0
+
+ self.xs = []
+ self.ys = []
+
+ self.vertices = np.array([
+ self.origin[0] , self.origin[1] , 0., 1.,
+ self.origin[0] + self.extend[0], self.origin[1] , 1., 1.,
+ self.origin[0] , self.origin[1] + self.extend[1], 0., 0.,
+ self.origin[0] + self.extend[0], self.origin[1] + self.extend[1], 1., 0.
+ ], dtype=np.float32)
+
+ def setup(self):
+ self.vao = glGenVertexArrays(1)
+ self.vbo = glGenBuffers(1)
+
+ glBindVertexArray(self.vao)
+ glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
+ glBufferData(GL_ARRAY_BUFFER, self.vertices.tostring(), GL_STATIC_DRAW)
+
+ glEnableVertexAttribArray(0)
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(0))
+ glEnableVertexAttribArray(1)
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(2*np.dtype(np.float32).itemsize))
+
+ self.texture_id = glGenTextures(2)
+
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[0])
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[1])
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ def shutdown(self):
+ self.pool.shutdown(wait=True)
+
+ def update(self):
+ self.steps = self.steps + 1
+
+ if self.steps % 10 == 0 and self.plotter == None:
+ self.xs.append(self.gas.step * self.gas.tau)
+ self.ys.append(self.gas.get_temperature())
+ self.plotter = self.pool.submit(get_plot, (self.xs, self.ys))
+
+ else:
+ if self.plotter != None and self.plotter.done():
+ texture, width, height = self.plotter.result()
+ if self.tick:
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[0])
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture)
+ self.tick = False
+ self.mixing = 1.0
+
+ else:
+ glBindTexture(GL_TEXTURE_2D, self.texture_id[1])
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture)
+ self.tick = True
+ self.mixing = 0.0
+
+ self.plotter = None
+
+ def display_decoration(self, uniform):
+ pass
+
+ def display_window(self, uniform):
+ if self.tick:
+ self.mixing = min(self.mixing+0.05, 1.0);
+ else:
+ self.mixing = max(self.mixing-0.05, 0.0);
+
+ glBindTextures(self.texture_id[0], 2, self.texture_id)
+ glUniform1iv(uniform['picture'], len(self.texture_id), self.texture_id)
+ glUniform1f(uniform['mixing'], self.mixing)
+
+ glBindVertexArray(self.vao);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
+ glBindVertexArray(0)
diff --git a/interacticle/visual/shader.py b/interacticle/visual/shader.py
new file mode 100644
index 0000000..c6c035b
--- /dev/null
+++ b/interacticle/visual/shader.py
@@ -0,0 +1,28 @@
+from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER
+from OpenGL.GL import shaders
+
+class Shader:
+ def __init__(self, fragment_src, vertex_src, uniform):
+ self.program = shaders.compileProgram(
+ shaders.compileShader(vertex_src, GL_VERTEX_SHADER),
+ shaders.compileShader(fragment_src, GL_FRAGMENT_SHADER))
+ self.uniform = { }
+ for name in uniform:
+ self.uniform[name] = shaders.glGetUniformLocation(self.program, name)
+
+ def use(self):
+ shaders.glUseProgram(self.program)
+
+class GeometryShader:
+ def __init__(self, fragment_src, geometry_src, vertex_src, uniform):
+ self.program = shaders.compileProgram(
+ shaders.compileShader(vertex_src, GL_VERTEX_SHADER),
+ shaders.compileShader(geometry_src, GL_GEOMETRY_SHADER),
+ shaders.compileShader(fragment_src, GL_FRAGMENT_SHADER))
+ self.uniform = { }
+ for name in uniform:
+ self.uniform[name] = shaders.glGetUniformLocation(self.program, name)
+
+ def use(self):
+ shaders.glUseProgram(self.program)
+
diff --git a/interacticle/visual/view.py b/interacticle/visual/view.py
new file mode 100644
index 0000000..14ff487
--- /dev/null
+++ b/interacticle/visual/view.py
@@ -0,0 +1,242 @@
+from OpenGL.GL import *
+from OpenGL.GLUT import *
+
+from pyrr import matrix44
+
+import numpy as np
+
+from .shader import Shader, GeometryShader
+from .camera import Projection, Rotation, MouseDragMonitor, MouseScrollMonitor
+
+particle_shader = (
+ """
+ #version 430
+
+ uniform vec4 camera_pos;
+
+ in GS_OUT
+ {
+ vec3 particle_pos;
+ vec3 atom_color;
+ vec2 tex_coord;
+ } fs_in;
+
+ void main(){
+ if (length(fs_in.tex_coord - vec2(0.5)) > 0.5) {
+ discard;
+ }
+
+ vec3 n = vec3(fs_in.tex_coord - vec2(0.5), 0.);
+ n.z = -sqrt(1 - length(n))*0.6;
+ n = normalize(n);
+
+ vec3 dir = normalize(camera_pos.xyz - fs_in.particle_pos);
+ vec3 color = fs_in.atom_color * dot(dir, n);
+
+ gl_FragColor = vec4(color.xyz, 1.0);
+ }
+ """,
+ """
+ #version 430
+
+ layout (points) in;
+ layout (triangle_strip, max_vertices=4) out;
+
+ out GS_OUT
+ {
+ vec3 particle_pos;
+ vec3 atom_color;
+ vec2 tex_coord;
+ } gs_out;
+
+ uniform mat4 projection;
+ uniform mat4 rotation;
+
+ uniform vec4 camera_pos;
+
+ vec4 project(vec3 v) {
+ return projection * rotation * vec4(v, 1.);
+ }
+
+ void emitSquareAt(vec3 position, float mass) {
+ mat4 m = projection * rotation;
+ vec3 camera_right = normalize(vec3(m[0][0], m[1][0], m[2][0]));
+ vec3 camera_up = normalize(vec3(m[0][1], m[1][1], m[2][1]));
+
+ gs_out.particle_pos = project(position).xyz;
+ gs_out.atom_color = float(mass == 39.948) * vec3(1,0,1) * 0.7
+ + float(mass == 20.18) * vec3(0.62,0.45,0.79)
+ + float(mass == 12.011) * vec3(0.5)
+ + float(mass == 15.9992) * vec3(1,0,0) * 0.6
+ + float(mass == 1.008) * vec3(1) * 0.7
+ + float(mass == 1.0079) * vec3(1) * 0.7;
+
+ float r = 0.02 * pow(mass, 0.2);
+
+ gl_Position = project(position + camera_right * -r + camera_up * -r);
+ gs_out.tex_coord = vec2(0.,0.);
+ EmitVertex();
+ gl_Position = project(position + camera_right * r + camera_up * -r);
+ gs_out.tex_coord = vec2(1.,0.);
+ EmitVertex();
+ gl_Position = project(position + camera_right * -r + camera_up * r);
+ gs_out.tex_coord = vec2(0.,1.);
+ EmitVertex();
+ gl_Position = project(position + camera_right * r + camera_up * r);
+ gs_out.tex_coord = vec2(1.,1.);
+ EmitVertex();
+ }
+
+ void main() {
+ emitSquareAt(gl_in[0].gl_Position.xyz, gl_in[0].gl_Position.w);
+ EndPrimitive();
+ }
+ """,
+ """
+ #version 430
+
+ layout (location=0) in vec4 particle_pos;
+
+ void main() {
+ gl_Position = particle_pos;
+ }
+ """,
+ ['projection', 'rotation', 'camera_pos']
+)
+
+decoration_shader = (
+ """
+ #version 430
+
+ uniform vec3 color;
+
+ void main(){
+ gl_FragColor = vec4(color, 1);
+ }
+ """,
+ """
+ #version 430
+
+ layout (location=0) in vec3 vertex;
+
+ uniform mat4 projection;
+ uniform mat4 rotation;
+
+ void main() {
+ gl_Position = projection * rotation * vec4(vertex.xyz, 1.);
+ }
+ """,
+ ['projection', 'rotation', 'color']
+)
+
+texture_shader = (
+ """
+ #version 430
+
+ in vec2 tex_coord;
+
+ uniform sampler2D picture[2];
+
+ uniform float mixing;
+
+ void main() {
+ vec3 color = mix(texture(picture[0], tex_coord), texture(picture[1], tex_coord), mixing).xyz;
+ if (color == vec3(0.,0.,0.)) {
+ gl_FragColor = vec4(color, 0.5);
+ } else {
+ gl_FragColor = vec4(color, 1.0);
+ }
+ }
+ """,
+ """
+ #version 430
+
+ layout (location=0) in vec2 screen_vertex;
+ layout (location=1) in vec2 texture_vertex;
+
+ out vec2 tex_coord;
+
+ uniform mat4 projection;
+
+ void main() {
+ gl_Position = projection * vec4(screen_vertex, 0.0, 1.0);
+ tex_coord = texture_vertex;
+ }
+ """,
+ ['picture','projection','mixing']
+)
+
+
+class View:
+ def __init__(self, simulation, instruments):
+ self.simulation = simulation
+ self.instruments = instruments
+
+ self.texture_shader = Shader(*texture_shader)
+ self.particle_shader = GeometryShader(*particle_shader)
+ self.decoration_shader = Shader(*decoration_shader)
+
+ self.camera_projection = Projection(distance = 5*simulation.domain_size)
+ self.camera_rotation = Rotation([-0.5*simulation.domain_size, -0.5*simulation.domain_size, -0.5*simulation.domain_size], np.pi/2, 0)
+ self.camera_pos = np.matmul([0,-self.camera_projection.distance,0,1], self.camera_rotation.get_inverse())
+
+ self.mouse_monitors = [
+ MouseDragMonitor(GLUT_LEFT_BUTTON, lambda dx, dy: self.camera_rotation.update(0.005*dy, 0.005*dx)),
+ MouseDragMonitor(GLUT_RIGHT_BUTTON, lambda dx, dy: self.camera_projection.shift(0.005*dx, 0.005*dy)),
+ MouseScrollMonitor(lambda zoom: self.camera_projection.update_distance(0.1*zoom))
+ ]
+
+ self.show_windows = False
+ self.show_decorations = True
+
+ def reshape(self, width, height):
+ glViewport(0,0,width,height)
+
+ world_height = 1.4
+ world_width = world_height / height * width
+
+ projection = Projection(10)
+
+ projection = matrix44.create_orthogonal_projection(-world_width/2, world_width/2, -world_height/2, world_height/2, -1, 1)
+ translation = matrix44.create_from_translation([-1.05, -1.0/2, 0])
+
+ self.camera_projection.update_ratio(width, height)
+
+ self.pixels_per_unit = height / world_height
+ self.projection = np.matmul(translation, projection)
+
+
+ def display(self):
+ glClearColor(0,0,0,1.)
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+ glEnable(GL_DEPTH_TEST)
+ glDepthMask(GL_TRUE)
+ glDepthFunc(GL_LEQUAL)
+
+ glEnable(GL_BLEND)
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
+
+ self.particle_shader.use()
+ glUniformMatrix4fv(self.particle_shader.uniform['projection'], 1, False, np.ascontiguousarray(self.camera_projection.get()))
+ glUniformMatrix4fv(self.particle_shader.uniform['rotation'], 1, False, np.ascontiguousarray(self.camera_rotation.get()))
+ glUniform4fv(self.particle_shader.uniform['camera_pos'], 1, self.camera_pos)
+
+ self.simulation.gl_draw_particles()
+ glBindVertexArray(0)
+
+ if self.show_decorations:
+ self.decoration_shader.use()
+ glUniformMatrix4fv(self.decoration_shader.uniform['projection'], 1, False, np.ascontiguousarray(self.camera_projection.get()))
+ glUniformMatrix4fv(self.decoration_shader.uniform['rotation'], 1, False, np.ascontiguousarray(self.camera_rotation.get()))
+ glLineWidth(3)
+ for instrument in self.instruments:
+ instrument.display_decoration(self.decoration_shader.uniform)
+
+ if self.show_windows:
+ self.texture_shader.use()
+ glUniformMatrix4fv(self.texture_shader.uniform['projection'], 1, False, np.asfortranarray(self.projection))
+ for instrument in self.instruments:
+ instrument.display_window(self.texture_shader.uniform)
+
+ glutSwapBuffers()