From 1bfe3ca4d3ed56529b674d62eb882006f4bd3768 Mon Sep 17 00:00:00 2001
From: kiwijuice56
Date: Thu, 25 Jan 2024 18:10:49 -0500
Subject: [PATCH] Added embedding mouse detection
---
docs/index.html | 10 ++++----
gui/user_interface.py | 4 +++-
main.py | 3 +++
renderers/embedding_renderer.py | 41 +++++++++++++++++++++++++++++++++
renderers/pdb_renderer.py | 13 ++++++-----
5 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/docs/index.html b/docs/index.html
index 8c98d44..0bff758 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -20,14 +20,15 @@ protein-visualizer
through deep learning.
How does it work?
- This interactive visualizer uses a deep learning model to identify functional and structural clusters within
+
This interactive visualizer uses a neural network to identify functional and structural clusters within
a protein by calculating embeddings, numerical expressions of each amino acid. The high-dimensional
data is then transformed into 2D in order to easily interpret the possible functional components of a
- protein. Next, a different deep learning model uses both the 3D structure and amino acid sequence to predict
+ protein. Next, a graph convolutional network uses both the 3D structure and amino acid sequence to predict
Gene Ontology (GO) annotations that describe the molecular function of the protein. Through
Gradient-weighted Class Activation Mapping (GradCAM), the program can calculate how much each amino
acid contributed to each GO prediction and subsequently allow for the identification of functional residues.
- Note: All of the data (except for 3D structure) shown in this program is generated through machine
+
This program was written entirely with Python, using Pyglet as an OpenGL interface.
+ Disclaimer: All of the data (except for 3D structure) shown in this program is generated through machine
learning and thus cannot be verified to be completely accurate. It is recommended to double-check
the predicted functions through experimental sources
@@ -75,7 +76,8 @@ Controls
Attribution
diff --git a/gui/user_interface.py b/gui/user_interface.py
index b6c97c9..a52b4be 100644
--- a/gui/user_interface.py
+++ b/gui/user_interface.py
@@ -67,7 +67,9 @@ def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
def on_mouse_motion(self, x, y, dx, dy):
prev_hl_idx = self.hl_idx
- self.hl_idx = self.pdb_renderer.hovered_residue
+ self.hl_idx = self.embedding_renderer.hovered_residue
+ if self.hl_idx == -1:
+ self.hl_idx = self.pdb_renderer.hovered_residue
if not prev_hl_idx == -1:
self.protein.residues[prev_hl_idx].highlighted = False
diff --git a/main.py b/main.py
index c050240..8fe4c1d 100644
--- a/main.py
+++ b/main.py
@@ -50,6 +50,9 @@
@window.event
def on_draw():
+ embedding_renderer.detect_mouse()
+ pdb_renderer.detect_mouse()
+
# Draw the 3D protein
pdb_renderer.draw()
diff --git a/renderers/embedding_renderer.py b/renderers/embedding_renderer.py
index de5fecc..57800f5 100644
--- a/renderers/embedding_renderer.py
+++ b/renderers/embedding_renderer.py
@@ -83,6 +83,9 @@ def __init__(self, protein, window, bounding_box=None, point_size=8):
# Define the pyglet vertex list
self.vertices = None
self.outline_vertices = None
+ self.id_vertices = None
+
+ self.hovered_residue = -1
# Default to full-screen render
self.bounding_box = []
@@ -148,10 +151,15 @@ def set_bounding_box(self, bounding_box):
('c4B', self.GRID_LINE_COLOR * (len(grid_points) // 2))
)
+ rgb_id = [[(i >> 16) & 255, (i >> 8) & 255, i & 255] for i in range(1, len(self.scaled_points) // 2 + 1)]
self.vertices = pyglet.graphics.vertex_list(
len(self.protein.embedding_points) // 2,
('v2f', self.scaled_points),
('c4B', np.zeros(len(self.protein.embedding_points) // 2 * 4, dtype=np.byte)))
+ self.id_vertices = pyglet.graphics.vertex_list(
+ len(self.protein.embedding_points) // 2,
+ ('v2f', self.scaled_points),
+ ('c3B', [x for xs in rgb_id for x in xs]))
self.update_colors()
def update_colors(self, start=0, end=-1):
@@ -166,6 +174,39 @@ def update_colors(self, start=0, end=-1):
self.vertices.colors[i * 4: i * 4 + 3] = self.protein.residues[i].color
self.vertices.colors[i * 4 + 3] = self.POINT_OPACITY
+ def detect_mouse(self):
+ glClearColor(*[0] * 4)
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+ glEnable(GL_SCISSOR_TEST)
+ glDisable(GL_POINT_SMOOTH)
+
+ glScissor(*self.bounding_box)
+
+ glLoadIdentity()
+ glDisable(GL_DEPTH_TEST)
+ glEnable(GL_BLEND)
+ glOrtho(0, self.window.width, 0, self.window.height, 0, 1000)
+
+ if self.camera.updated:
+ self.set_bounding_box(self.bounding_box)
+ self.camera.updated = False
+
+ glPointSize(int(self.point_size * 2.0))
+ self.id_vertices.draw(pyglet.gl.GL_POINTS)
+
+ read = (GLubyte * 3)(0)
+ glReadPixels(self.camera.input_handler.x, self.camera.input_handler.y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, read)
+ selected_id = sum([read[i] << (8 * (2 - i)) for i in range(3)])
+ if not selected_id == 0:
+ self.hovered_residue = selected_id - 1
+ else:
+ self.hovered_residue = -1
+
+ # Clean up
+ glDisable(GL_SCISSOR_TEST)
+ glDisable(GL_DEPTH_TEST)
+ glDisable(GL_BLEND)
+
def draw(self):
glEnable(GL_SCISSOR_TEST)
glEnable(GL_POINT_SMOOTH)
diff --git a/renderers/pdb_renderer.py b/renderers/pdb_renderer.py
index 856d21e..b268ad5 100644
--- a/renderers/pdb_renderer.py
+++ b/renderers/pdb_renderer.py
@@ -191,17 +191,13 @@ def set_bounding_box(self, bounding_box):
self.bounding_box = bounding_box
self.camera.input_handler.bounding_box = bounding_box
- def draw(self):
+ def detect_mouse(self):
glClearColor(*[0] * 4)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
- glEnable(GL_POINT_SMOOTH)
- glEnable(GL_SCISSOR_TEST)
-
glLoadIdentity()
glMatrixMode(GL_PROJECTION)
gluPerspective(self.FOV, self.window.width / float(self.window.height), self.Z_NEAR, self.Z_FAR)
self.camera.draw()
-
glScissor(*self.bounding_box)
glPointSize(int(1.5 * self.point_size))
@@ -217,9 +213,14 @@ def draw(self):
else:
self.hovered_residue = -1
- glClearColor(*[c / 255.0 for c in self.BACKGROUND_COLOR])
+ def draw(self):
+ glClearColor(*[0] * 4)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+ glEnable(GL_POINT_SMOOTH)
+ glEnable(GL_SCISSOR_TEST)
glEnable(GL_LINE_SMOOTH)
+ glEnable(GL_BLEND)
+ glScissor(*self.bounding_box)
glLoadIdentity()
glMatrixMode(GL_PROJECTION)