Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:RBVI/ChimeraX into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
tomgoddard committed Jan 24, 2025
2 parents 3b45348 + 823d403 commit 439d3cb
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 45 deletions.
10 changes: 8 additions & 2 deletions docs/user/commands/color.html
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ <h3>Coloring Surfaces by Map Value</h3>
[&nbsp;<a href="#palette"><b>palette</b></a>&nbsp;&nbsp;<i>palette</i>&nbsp;]
[&nbsp;<b>range</b>&nbsp;&nbsp;<i>low</i>,<i>high</i>&nbsp;|&nbsp;full&nbsp;]
[&nbsp;<a href="#transparency-bfactor"><b>transparency</b></a>&nbsp;&nbsp;<i>percent</i>&nbsp;]
[&nbsp;<b>outsideColor</b>&nbsp&nbsp;<a href="colornames.html"><i>color-spec</i></a>&nbsp;]
</blockquote>
<p>
The <b>sample</b>, <b>electrostatic</b>, and <b>gradient</b> coloring options
Expand All @@ -627,7 +628,12 @@ <h3>Coloring Surfaces by Map Value</h3>
<td bgcolor=white width="10px" title="white"></td>
<td bgcolor=blue width="10px" title="blue"></td>
</tr></table> and the <a href="#range"><b>range</b></a> default depends on the
specific type of map coloring, explained below.
specific type of map coloring, explained below;
the <b>outsideColor</b> is used for surface vertices
that fall outside the volume data grid box
(default <b>#808080</b> <table border cellpadding="4" cellspacing="0"
style="display:inline-block"><tr>
<td bgcolor="#808080" width="10px" title="#808080"></td></tr></table>).
</p><p>
When the coloring command is run interactively (in gui mode and not via a
<a href="usageconventions.html#cxc-files">script</a>),
Expand Down Expand Up @@ -1311,5 +1317,5 @@ <h3 class="usage"><a href="usageconventions.html">Usage</a>:

<hr>
<address>UCSF Resource for Biocomputing, Visualization, and Informatics /
November 2024</address>
January 2025</address>
</body></html>
2 changes: 1 addition & 1 deletion src/bundles/alignment_headers/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors= [{name = "UCSF RBVI", email="[email protected]"}]
description = "Alignment header support"
dependencies = [
"ChimeraX-Core ~=1.0"
, "ChimeraX-Alignments ~=2.17"
, "ChimeraX-Alignments ~=2.18"
, "ChimeraX-AlignmentMatrices ~=2.0"
, "ChimeraX-Geometry ~=1.0"
]
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/alignment_headers/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# This notice must be embedded in or attached to all copies, including partial
# copies, of the software or any revisions or derivations thereof.
# === UCSF ChimeraX Copyright ===
__version__ = "3.5"
__version__ = "3.6"

import os

Expand Down
20 changes: 4 additions & 16 deletions src/bundles/alignment_headers/src/conservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,24 +171,12 @@ def position_color(self, pos):

def percent_identity(self, pos, for_histogram=False):
"""actually returns a fraction"""
occur = {}
for seq in self.alignment.seqs:
let = seq[pos]
try:
occur[let] += 1
except KeyError:
occur[let] = 1
best = 0
for let, num in occur.items():
if not let.isalpha():
continue
if num > best:
best = num
if best == 0:
char, count = self.alignment.most_common(pos)
if count == 0:
return 0.0
if for_histogram:
return (best - 1) / (len(self.alignment.seqs) - 1)
return best / len(self.alignment.seqs)
return (count - 1) / (len(self.alignment.seqs) - 1)
return count / len(self.alignment.seqs)

def reevaluate(self, pos1=0, pos2=None, *, evaluation_func=None):
if self.style == self.STYLE_AL2CO:
Expand Down
23 changes: 15 additions & 8 deletions src/bundles/dicom/src/dicom_hierarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,14 +890,21 @@ def pixel_spacing(self):
return x_scale, y_scale, z_scale

def rotation(self):
affine = self.affine
x_scale, y_scale, z_scale = self.pixel_spacing()
rotation_matrix = [
[affine[0][0] / x_scale, affine[0][1] / y_scale, affine[0][2] / z_scale],
[affine[1][0] / x_scale, affine[1][1] / y_scale, affine[1][2] / z_scale],
[affine[2][0] / x_scale, affine[2][1] / y_scale, affine[2][2] / z_scale],
]
return rotation_matrix
#affine = self.affine
#x_scale, y_scale, z_scale = self.pixel_spacing()
#rotation_matrix = [
# [affine[0][0] / x_scale, affine[0][1] / y_scale, affine[0][2] / z_scale],
# [affine[1][0] / x_scale, affine[1][1] / y_scale, affine[1][2] / z_scale],
# [affine[2][0] / x_scale, affine[2][1] / y_scale, affine[2][2] / z_scale],
#]
# We're ignoring the rotation given by the DICOM files until someone complains about it.
# Doing this simplifies other areas of the codebase significantly.
# 1) The plane viewers use orthographic cameras pointed down the X, Y, and Z axes, and
# ignoring the rotations of the files means we don't have to calculate new axes to
# point the cameras down when files aren't axis aligned.
# 2) We don't have to modify the raycasting shader to do such calculations either.
return [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
#return rotation_matrix

def origin(self):
affine = self.affine
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/seqalign/bundle_info.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<BundleInfo name="ChimeraX-Alignments" version="2.17" package="chimerax.seqalign"
<BundleInfo name="ChimeraX-Alignments" version="2.18" package="chimerax.seqalign"
minSessionVersion="1" maxSessionVersion="1">

<!-- Additional information about bundle source -->
Expand Down
53 changes: 37 additions & 16 deletions src/bundles/seqalign/src/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def __init__(self, session, seqs, ident, file_attrs, file_markups, auto_destroy,
self.viewers = []
self.viewers_by_subcommand = {}
self.viewer_to_subcommand = {}
self._column_counts_cache = None
self._observer_notification_suspended = 0
self._ob_note_suspended_data = []
self._modified_mmaps = []
Expand Down Expand Up @@ -490,6 +491,19 @@ def auto_associate(self, assoc):
def being_destroyed(self):
return self._in_destroy

def column_counts(self):
"""Returns a dictionary keyed on column index, with values that are 2-tuples of numpy
arrays; the first member is the array of unique characters in that column, and the
second array is the corresponding counts for those characters.
"""
if self._column_counts_cache is None:
import numpy
data = numpy.array([list(seq.characters) for seq in self._seqs])
cache = self._column_counts_cache = {}
for i in range(len(data[0])):
cache[i] = numpy.unique(data[:,i], return_counts=True)
return self._column_counts_cache.copy()

def detach_viewer(self, viewer):
"""Called when a viewer is done with the alignment (see attach_viewer)"""
self.viewers.remove(viewer)
Expand Down Expand Up @@ -666,6 +680,27 @@ def match(self, ref_chain, match_chains, *, iterate=-1, restriction=None):
return_vals.append((None, None, None, None, None))
return return_vals

def most_common(self, col_index, *, non_gap=True):
"""Returns most common character in the column given by 'col_index' and the count for that character.
For ties, one of the most common characters, chosen arbitrarily, will be returned. If 'non_gap'
is True, gap characters will be ignored.
"""
chars, counts = self.column_counts()[col_index]
if non_gap:
max_count = 0
for char, count in zip(chars, counts):
if not char.isalpha():
continue
if count > max_count:
max_count = count
max_char = char
if max_count == 0:
return ' ', 0
return max_char, max_count
import numpy
max_index = numpy.argmax(counts)
return chars[max_index], counts[max_index]

def notify(self, note_name, note_data):
"""Used by headers to issue notifications, but theoretically could be used by anyone"""
self._notify_observers(note_name, note_data)
Expand Down Expand Up @@ -982,6 +1017,7 @@ def _rmsd_atomic_cb(self, trig_name, changes):
break

def _seq_characters_changed_cb(self, trig_name, seq):
self._column_counts_cache = None
if not getattr(self, '_realigning', False):
self._notify_observers(self.NOTE_SEQ_CONTENTS, seq)

Expand Down Expand Up @@ -1021,22 +1057,7 @@ def process_attr(attr_name, col_vals):
if headers is None:
headers = [hdr for hdr in self._headers if hdr.shown or hdr.eval_while_hidden]
if len(self.seqs) > 1:
values = []
for i in range(len(self._seqs[0])):
counts = {}
for seq in self._seqs:
c = seq[i]
if not c.isalnum():
continue
counts[c] = counts.get(c, 0) + 1
if not counts:
values.append(0.0)
continue
max_count = None
for c, count in counts.items():
if max_count is None or count > max_count:
max_count = count
values.append(100.0 * max_count / len(self._seqs))
values = [100.0 * self.most_common(col)[1] for col in range(len(self._seqs[0]))]
process_attr(self.COL_IDENTITY_ATTR, values)
from chimerax.atomic import Residue
for header in headers:
Expand Down

0 comments on commit 439d3cb

Please sign in to comment.