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.

Demonstration of running the visualizer

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

Code

- All of the code is available under the MIT license on the protein-visualizer GitHub repository. +

All of the code is available under the MIT license on the protein-visualizer GitHub repository. + Feel free to take any part of the code to expand or transform, just make sure you keep the attributions to ProSE and DeepFRI!

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)