jiehou commited on
Commit
8062cf7
·
verified ·
1 Parent(s): 91fbffc

Upload visualization.py

Browse files
Files changed (1) hide show
  1. visualization.py +140 -4
visualization.py CHANGED
@@ -4,6 +4,7 @@ Uses py3Dmol for interactive molecular visualization
4
  """
5
 
6
  import numpy as np
 
7
  from rmsd_utils import (
8
  parse_residue_atoms,
9
  translate_rotate_coords,
@@ -13,7 +14,8 @@ from rmsd_utils import (
13
 
14
 
15
  def create_structure_visualization(ref_path, query_path, ref_window_indices, query_window_indices,
16
- rotation_matrix, ref_com, query_com, rmsd=None):
 
17
  """
18
  Create an interactive 3D visualization of aligned structures.
19
 
@@ -26,11 +28,21 @@ def create_structure_visualization(ref_path, query_path, ref_window_indices, que
26
  ref_com: Center of mass of reference window
27
  query_com: Center of mass of query window
28
  rmsd: RMSD value (optional, for display)
 
 
 
 
29
 
30
  Returns:
31
  HTML string containing the py3Dmol visualization
32
  """
33
 
 
 
 
 
 
 
34
  # Read PDB files
35
  with open(ref_path) as f:
36
  ref_pdb = f.read()
@@ -84,6 +96,7 @@ def create_structure_visualization(ref_path, query_path, ref_window_indices, que
84
  <html>
85
  <head>
86
  <script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
 
87
  <style>
88
  #container {{
89
  width: 100%;
@@ -167,12 +180,25 @@ def create_structure_visualization(ref_path, query_path, ref_window_indices, que
167
  bottom: 10px;
168
  left: 10px;
169
  background: rgba(255, 255, 255, 0.95);
170
- padding: 10px 15px;
171
  border-radius: 8px;
172
  font-family: Arial, sans-serif;
173
  font-size: 13px;
174
  z-index: 1000;
175
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }}
177
  .section-title {{
178
  font-weight: bold;
@@ -181,6 +207,30 @@ def create_structure_visualization(ref_path, query_path, ref_window_indices, que
181
  font-size: 12px;
182
  text-transform: uppercase;
183
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  </style>
185
  </head>
186
  <body>
@@ -268,7 +318,28 @@ def create_structure_visualization(ref_path, query_path, ref_window_indices, que
268
  </div>
269
 
270
  <div class="rmsd-info">
271
- <strong>RMSD:</strong> <span style="color: #E94B3C; font-weight: bold;">{f"{rmsd:.3f}" if rmsd is not None else "N/A"} Å</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  </div>
273
 
274
  <script>
@@ -530,6 +601,71 @@ def create_structure_visualization(ref_path, query_path, ref_window_indices, que
530
  }});
531
  }}
532
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
  // Initialize on load
534
  initViewer();
535
  </script>
@@ -670,4 +806,4 @@ def transform_pdb_string(pdb_string, rotation_matrix, query_com, ref_com=None):
670
  else:
671
  transformed_lines.append(line)
672
 
673
- return '\n'.join(transformed_lines)
 
4
  """
5
 
6
  import numpy as np
7
+ from pathlib import Path
8
  from rmsd_utils import (
9
  parse_residue_atoms,
10
  translate_rotate_coords,
 
14
 
15
 
16
  def create_structure_visualization(ref_path, query_path, ref_window_indices, query_window_indices,
17
+ rotation_matrix, ref_com, query_com, rmsd=None,
18
+ ref_name=None, query_name=None, ref_sequence=None, query_sequence=None):
19
  """
20
  Create an interactive 3D visualization of aligned structures.
21
 
 
28
  ref_com: Center of mass of reference window
29
  query_com: Center of mass of query window
30
  rmsd: RMSD value (optional, for display)
31
+ ref_name: Reference structure name (optional, for display)
32
+ query_name: Query structure name (optional, for display)
33
+ ref_sequence: Reference sequence (optional, for display)
34
+ query_sequence: Query sequence (optional, for display)
35
 
36
  Returns:
37
  HTML string containing the py3Dmol visualization
38
  """
39
 
40
+ # Extract simple names if not provided
41
+ if ref_name is None:
42
+ ref_name = Path(ref_path).stem
43
+ if query_name is None:
44
+ query_name = Path(query_path).stem
45
+
46
  # Read PDB files
47
  with open(ref_path) as f:
48
  ref_pdb = f.read()
 
96
  <html>
97
  <head>
98
  <script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
99
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
100
  <style>
101
  #container {{
102
  width: 100%;
 
180
  bottom: 10px;
181
  left: 10px;
182
  background: rgba(255, 255, 255, 0.95);
183
+ padding: 12px 15px;
184
  border-radius: 8px;
185
  font-family: Arial, sans-serif;
186
  font-size: 13px;
187
  z-index: 1000;
188
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
189
+ max-width: 450px;
190
+ }}
191
+ .info-row {{
192
+ margin: 4px 0;
193
+ line-height: 1.4;
194
+ }}
195
+ .info-label {{
196
+ font-weight: bold;
197
+ color: #555;
198
+ }}
199
+ .info-value {{
200
+ color: #333;
201
+ font-family: 'Courier New', monospace;
202
  }}
203
  .section-title {{
204
  font-weight: bold;
 
207
  font-size: 12px;
208
  text-transform: uppercase;
209
  }}
210
+ .download-section {{
211
+ position: absolute;
212
+ bottom: 10px;
213
+ right: 10px;
214
+ background: rgba(255, 255, 255, 0.95);
215
+ padding: 10px;
216
+ border-radius: 8px;
217
+ font-family: Arial, sans-serif;
218
+ z-index: 1000;
219
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
220
+ }}
221
+ .download-btn {{
222
+ background: #4A90E2;
223
+ color: white;
224
+ border: none;
225
+ padding: 8px 16px;
226
+ border-radius: 4px;
227
+ cursor: pointer;
228
+ font-size: 13px;
229
+ font-weight: bold;
230
+ }}
231
+ .download-btn:hover {{
232
+ background: #357ABD;
233
+ }}
234
  </style>
235
  </head>
236
  <body>
 
318
  </div>
319
 
320
  <div class="rmsd-info">
321
+ <div class="info-row">
322
+ <span class="info-label">RMSD:</span>
323
+ <span style="color: #E94B3C; font-weight: bold; font-size: 14px;">{f"{rmsd:.3f}" if rmsd is not None else "N/A"} Å</span>
324
+ </div>
325
+ <div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #ddd;">
326
+ <div class="info-row">
327
+ <span class="info-label">Reference:</span>
328
+ <span class="info-value">{ref_name}</span>
329
+ </div>
330
+ {f'<div class="info-row" style="margin-left: 15px; font-size: 12px;"><span class="info-label">Seq:</span> <span class="info-value">{ref_sequence}</span></div>' if ref_sequence else ''}
331
+ </div>
332
+ <div style="margin-top: 6px;">
333
+ <div class="info-row">
334
+ <span class="info-label">Query:</span>
335
+ <span class="info-value">{query_name}</span>
336
+ </div>
337
+ {f'<div class="info-row" style="margin-left: 15px; font-size: 12px;"><span class="info-label">Seq:</span> <span class="info-value">{query_sequence}</span></div>' if query_sequence else ''}
338
+ </div>
339
+ </div>
340
+
341
+ <div class="download-section">
342
+ <button class="download-btn" onclick="downloadImage()">📷 Download PNG</button>
343
  </div>
344
 
345
  <script>
 
601
  }});
602
  }}
603
 
604
+ function downloadImage() {{
605
+ try {{
606
+ // Generate filename with metadata
607
+ var refName = "{ref_name}".replace('.pdb', '');
608
+ var queryName = "{query_name}".replace('.pdb', '');
609
+ var rmsdValue = "{f'{rmsd:.3f}' if rmsd is not None else 'NA'}";
610
+ var filename = 'alignment_' + refName + '_' + queryName + '_RMSD_' + rmsdValue + '.png';
611
+
612
+ // Ensure viewer is rendered
613
+ if (viewer) {{
614
+ viewer.render();
615
+ }}
616
+
617
+ // Get the container element (includes canvas + all overlays)
618
+ const container = document.getElementById('container');
619
+
620
+ if (!container) {{
621
+ alert('Container not ready. Please wait and try again.');
622
+ return;
623
+ }}
624
+
625
+ // Use html2canvas to capture the entire container with overlays
626
+ html2canvas(container, {{
627
+ backgroundColor: '#ffffff',
628
+ scale: 2, // Higher resolution
629
+ logging: false,
630
+ useCORS: true,
631
+ allowTaint: true
632
+ }}).then(function(canvas) {{
633
+ // Convert to PNG
634
+ const dataURL = canvas.toDataURL('image/png');
635
+
636
+ // Create download link
637
+ const link = document.createElement('a');
638
+ link.download = filename;
639
+ link.href = dataURL;
640
+
641
+ // Trigger download
642
+ document.body.appendChild(link);
643
+ link.click();
644
+ document.body.removeChild(link);
645
+ }}).catch(function(error) {{
646
+ console.error('html2canvas error:', error);
647
+
648
+ // Fallback: just capture the canvas without overlays
649
+ const canvas = document.querySelector('#container canvas');
650
+ if (canvas) {{
651
+ const dataURL = canvas.toDataURL('image/png');
652
+ const link = document.createElement('a');
653
+ link.download = filename;
654
+ link.href = dataURL;
655
+ document.body.appendChild(link);
656
+ link.click();
657
+ document.body.removeChild(link);
658
+ }} else {{
659
+ alert('Download failed. Please try again.');
660
+ }}
661
+ }});
662
+
663
+ }} catch (error) {{
664
+ console.error('PNG download error:', error);
665
+ alert('Error downloading PNG: ' + error.message);
666
+ }}
667
+ }}
668
+
669
  // Initialize on load
670
  initViewer();
671
  </script>
 
806
  else:
807
  transformed_lines.append(line)
808
 
809
+ return '\n'.join(transformed_lines)