Skip to content

Commit

Permalink
Add options for locking slider transforms
Browse files Browse the repository at this point in the history
Previously, slider transforms could only be locked if ROI subpanels
with groups were used.

Now, slider transforms can be locked regardless. An
"Instrument Rigid Body" lock will results in the entire instrument
being transformed together. "Group Rigid Body" lock is only available
if every detector has a specified group, and it allows detectors within
a group to be transformed together.

Additionally, the center of rotation may be specified. It defaults to
the "Mean Center", which is the mean center of the instrument (for
"Instrument Rigid Body") or the mean center of the group (for "Group
Rigid Body"). "Origin" is the lab origin at (0, 0, 0).

Signed-off-by: Patrick Avery <[email protected]>
  • Loading branch information
psavery committed Dec 11, 2024
1 parent 92dae39 commit 5631656
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 60 deletions.
111 changes: 90 additions & 21 deletions hexrdgui/calibration_slider_widget.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from PySide6.QtCore import QObject
from PySide6.QtWidgets import QMessageBox

import numpy as np

Expand Down Expand Up @@ -38,8 +39,10 @@ def setup_connections(self):

self.ui.push_reset_config.pressed.connect(self.reset_config)

self.ui.roi_lock_group_transforms.toggled.connect(
HexrdConfig().set_roi_lock_group_transforms)
self.ui.lock_relative_transforms.toggled.connect(
self.on_lock_relative_transforms_toggled)
self.ui.lock_relative_transforms_setting.currentIndexChanged.connect(
self.on_lock_relative_transforms_setting_changed)

HexrdConfig().euler_angle_convention_changed.connect(
self.update_labels)
Expand Down Expand Up @@ -160,16 +163,68 @@ def update_gui_from_config(self):
for widget in self.config_widgets:
self.update_widget_value(widget)

self.ui.roi_lock_group_transforms.setChecked(
HexrdConfig().roi_lock_group_transforms)

self.update_ranges()
self.update_labels()
self.validate_relative_transforms_setting()
self.update_visibility_states()

def update_visibility_states(self):
self.ui.roi_lock_group_transforms.setVisible(
HexrdConfig().instrument_has_roi)
visible = self.lock_relative_transforms
widgets = [
self.ui.lock_relative_transforms_setting,
self.ui.locked_center_of_rotation_label,
self.ui.locked_center_of_rotation,
]
for w in widgets:
w.setVisible(visible)

def on_lock_relative_transforms_toggled(self):
self.update_visibility_states()

def on_lock_relative_transforms_setting_changed(self):
self.validate_relative_transforms_setting()

def validate_relative_transforms_setting(self):
if not self.lock_relative_transforms:
return

if self.transform_group_rigid_body:
# If it is set to 'Group Rigid Body', verify that there are
# actually groups. Otherwise, print an error message and set it
# back to instrument.
if not all(
HexrdConfig().detector_group(det_key) is not None
for det_key in HexrdConfig().detector_names
):
msg = (
'To use "Group Rigid Body", all detectors must have '
'assigned groups.\n\nSwitching back to "Instrument Rigid '
'Body"'
)
QMessageBox.critical(self.ui, 'HEXRD', msg)
w = self.ui.lock_relative_transforms_setting
w.setCurrentIndex(0)
return

@property
def lock_relative_transforms(self) -> bool:
return self.ui.lock_relative_transforms.isChecked()

@property
def lock_relative_transforms_setting(self) -> str:
return self.ui.lock_relative_transforms_setting.currentText()

@property
def transform_instrument_rigid_body(self) -> bool:
return self.lock_relative_transforms_setting == 'Instrument Rigid Body'

@property
def transform_group_rigid_body(self) -> bool:
return self.lock_relative_transforms_setting == 'Group Rigid Body'

@property
def locked_center_of_rotation(self) -> str:
return self.ui.locked_center_of_rotation.currentText()

def update_detectors_from_config(self):
widget = self.ui.detector
Expand Down Expand Up @@ -212,8 +267,8 @@ def update_config_from_gui(self, val):
# Convert to radians, and to the native python type before save
val = np.radians(val).item()

if HexrdConfig().roi_lock_group_transforms:
self._transform_locked_group(det, key, ind, val)
if self.lock_relative_transforms:
self._transform_locked(det, key, ind, val)
else:
det['transform'][key]['value'][ind] = val

Expand All @@ -235,15 +290,22 @@ def update_config_from_gui(self, val):
beam_dict['vector'][key]['value'] = val
HexrdConfig().beam_vector_changed.emit()

def _transform_locked_group(self, det, key, ind, val):
group = det.get('group', {}).get('value')
if not group:
raise Exception(f'Detector does not have a group: {det}')

group_dets = {}
for name in HexrdConfig().detector_names:
if HexrdConfig().detector_group(name) == group:
group_dets[name] = HexrdConfig().detector(name)
def _transform_locked(self, det, key, ind, val):
det_names = HexrdConfig().detector_names
if self.transform_instrument_rigid_body:
# All detectors will be transformed as a group
group_dets = {
name: HexrdConfig().detector(name)
for name in det_names
}
elif self.transform_group_rigid_body:
group = det.get('group', {}).get('value')
group_dets = {}
for name in HexrdConfig().detector_names:
if HexrdConfig().detector_group(name) == group:
group_dets[name] = HexrdConfig().detector(name)
else:
raise NotImplementedError(self.lock_relative_transforms_setting)

if key == 'translation':
# Compute the diff
Expand All @@ -255,9 +317,16 @@ def _transform_locked_group(self, det, key, ind, val):
detector['transform']['translation']['value'][ind] += diff
else:
# It is tilt. Compute the center of rotation first.
detector_centers = np.array([x['transform']['translation']['value']
for x in group_dets.values()])
center_of_rotation = detector_centers.mean(axis=0)
if self.locked_center_of_rotation == 'Mean Center':
detector_centers = np.array(
[x['transform']['translation']['value']
for x in group_dets.values()]
)
center_of_rotation = detector_centers.mean(axis=0)
elif self.locked_center_of_rotation == 'Origin':
center_of_rotation = np.array([0.0, 0.0, 0.0])
else:
raise NotImplementedError(self.locked_center_of_rotation)

# Gather the old tilt and the new tilt to compute a difference.
old_tilt = det['transform']['tilt']['value']
Expand Down
11 changes: 0 additions & 11 deletions hexrdgui/hexrd_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@ def __init__(self):
self.live_update = True
self._show_saturation_level = False
self._stitch_raw_roi_images = False
self._roi_lock_group_transforms = False
self._tab_images = False
self.previous_active_material = None
self.collapsed_state = []
Expand Down Expand Up @@ -406,7 +405,6 @@ def _attributes_to_persist(self):
('config_indexing', None),
('config_image', None),
('_stitch_raw_roi_images', False),
('_roi_lock_group_transforms', False),
('font_size', 11),
('images_dir', None),
('working_dir', '.'),
Expand Down Expand Up @@ -2670,15 +2668,6 @@ def set_stitch_raw_roi_images(self, v):
stitch_raw_roi_images = property(get_stitch_raw_roi_images,
set_stitch_raw_roi_images)

def get_roi_lock_group_transforms(self):
return self._roi_lock_group_transforms and self.instrument_has_roi

def set_roi_lock_group_transforms(self, v):
self._roi_lock_group_transforms = v

roi_lock_group_transforms = property(get_roi_lock_group_transforms,
set_roi_lock_group_transforms)

def tab_images(self):
return self._tab_images

Expand Down
106 changes: 78 additions & 28 deletions hexrdgui/resources/ui/calibration_slider_widget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>448</width>
<height>673</height>
<height>781</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
Expand All @@ -23,7 +23,17 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="3" column="0" colspan="2">
<item row="9" column="0" colspan="2">
<widget class="QPushButton" name="push_reset_config">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reset the instrument configuration to the state when the most recent instrument file was loaded, or when the program started.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Reset Configuration</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QGroupBox" name="translation_group">
<property name="title">
<string>Translation</string>
Expand Down Expand Up @@ -184,7 +194,10 @@
</layout>
</widget>
</item>
<item row="5" column="0" colspan="2">
<item row="1" column="1">
<widget class="QComboBox" name="detector"/>
</item>
<item row="8" column="0" colspan="2">
<widget class="QGroupBox" name="beam_group">
<property name="title">
<string>Beam</string>
Expand Down Expand Up @@ -355,6 +368,44 @@
</widget>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="locked_center_of_rotation_label">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The center of rotation when tilts are applied.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&amp;quot;Mean Center&amp;quot; means the center of the whole instrument if &amp;quot;Instrument Rigid Body&amp;quot; is selected, and it means the center of the selected detector's group if &amp;quot;Group Rigid Body&amp;quot; is selected.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&amp;quot;Origin&amp;quot; means [0, 0, 0] (the lab origin).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Center of Rotation:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="locked_center_of_rotation">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The center of rotation when tilts are applied.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&amp;quot;Mean Center&amp;quot; means the center of the whole instrument if &amp;quot;Instrument Rigid Body&amp;quot; is selected, and it means the center of the selected detector's group if &amp;quot;Group Rigid Body&amp;quot; is selected.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&amp;quot;Origin&amp;quot; means [0, 0, 0] (the lab origin).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>Mean Center</string>
</property>
</item>
<item>
<property name="text">
<string>Origin</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Detector:</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QGroupBox" name="tilt_group">
<property name="title">
<string>Tilt</string>
Expand Down Expand Up @@ -524,27 +575,7 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Detector:</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QPushButton" name="push_reset_config">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reset the instrument configuration to the state when the most recent instrument file was loaded, or when the program started.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Reset Configuration</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="detector"/>
</item>
<item row="7" column="0" colspan="2">
<item row="10" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
Expand All @@ -558,13 +589,30 @@
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="roi_lock_group_transforms">
<widget class="QCheckBox" name="lock_relative_transforms">
<property name="toolTip">
<string>If checked, when a detector is transformed (translation or tilt), all other detectors in the same group will be transformed in a similar way. For translation, all detectors will be translated by the same amount, and in the same direction. For tilt, all detectors in the same group will be rotated about the mean of the detector centers.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lock relative transformations between the detectors.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If checked, several additional options will appear that define how the detector transformations will be locked. For example, all detectors in the same instrument or group may be transformed via rigid body constraints. And, the center of rotation when tilts are applied may be specified as well.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Lock ROI Group Transformations</string>
<string>Lock relative transformations</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QComboBox" name="lock_relative_transforms_setting">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&amp;quot;Instrument Rigid Body&amp;quot; means to transform all detectors in the entire instrument in the same way.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&amp;quot;Group Rigid Body&amp;quot; means to transform all detectors in the selected detector's group in the same way. This setting requires all detectors to have their &amp;quot;group&amp;quot; specified in the instrument config.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>Instrument Rigid Body</string>
</property>
</item>
<item>
<property name="text">
<string>Group Rigid Body</string>
</property>
</item>
</widget>
</item>
</layout>
Expand All @@ -578,7 +626,9 @@
</customwidgets>
<tabstops>
<tabstop>detector</tabstop>
<tabstop>roi_lock_group_transforms</tabstop>
<tabstop>lock_relative_transforms</tabstop>
<tabstop>lock_relative_transforms_setting</tabstop>
<tabstop>locked_center_of_rotation</tabstop>
<tabstop>sb_translation_range</tabstop>
<tabstop>slider_translation_0</tabstop>
<tabstop>sb_translation_0</tabstop>
Expand Down

0 comments on commit 5631656

Please sign in to comment.