From 5631656edc5a4580bb80fd2809f078cc9078abd0 Mon Sep 17 00:00:00 2001 From: Patrick Avery Date: Wed, 11 Dec 2024 15:55:55 -0600 Subject: [PATCH] Add options for locking slider transforms 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 --- hexrdgui/calibration_slider_widget.py | 111 ++++++++++++++---- hexrdgui/hexrd_config.py | 11 -- .../resources/ui/calibration_slider_widget.ui | 106 ++++++++++++----- 3 files changed, 168 insertions(+), 60 deletions(-) diff --git a/hexrdgui/calibration_slider_widget.py b/hexrdgui/calibration_slider_widget.py index b732575e1..da0a484d0 100644 --- a/hexrdgui/calibration_slider_widget.py +++ b/hexrdgui/calibration_slider_widget.py @@ -1,4 +1,5 @@ from PySide6.QtCore import QObject +from PySide6.QtWidgets import QMessageBox import numpy as np @@ -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) @@ -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 @@ -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 @@ -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 @@ -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'] diff --git a/hexrdgui/hexrd_config.py b/hexrdgui/hexrd_config.py index e2fbeccfa..bc16c669a 100644 --- a/hexrdgui/hexrd_config.py +++ b/hexrdgui/hexrd_config.py @@ -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 = [] @@ -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', '.'), @@ -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 diff --git a/hexrdgui/resources/ui/calibration_slider_widget.ui b/hexrdgui/resources/ui/calibration_slider_widget.ui index 0a627e8eb..3ae9adf70 100644 --- a/hexrdgui/resources/ui/calibration_slider_widget.ui +++ b/hexrdgui/resources/ui/calibration_slider_widget.ui @@ -7,7 +7,7 @@ 0 0 448 - 673 + 781 @@ -23,7 +23,17 @@ 0 - + + + + <html><head/><body><p>Reset the instrument configuration to the state when the most recent instrument file was loaded, or when the program started.</p></body></html> + + + Reset Configuration + + + + Translation @@ -184,7 +194,10 @@ - + + + + Beam @@ -355,6 +368,44 @@ + + + + + <html><head/><body><p>The center of rotation when tilts are applied.</p><p><br/></p><p>&quot;Mean Center&quot; means the center of the whole instrument if &quot;Instrument Rigid Body&quot; is selected, and it means the center of the selected detector's group if &quot;Group Rigid Body&quot; is selected.</p><p><br/></p><p>&quot;Origin&quot; means [0, 0, 0] (the lab origin).</p></body></html> + + + Center of Rotation: + + + + + + + <html><head/><body><p>The center of rotation when tilts are applied.</p><p><br/></p><p>&quot;Mean Center&quot; means the center of the whole instrument if &quot;Instrument Rigid Body&quot; is selected, and it means the center of the selected detector's group if &quot;Group Rigid Body&quot; is selected.</p><p><br/></p><p>&quot;Origin&quot; means [0, 0, 0] (the lab origin).</p></body></html> + + + + Mean Center + + + + + Origin + + + + + + + + + + Detector: + + + + Tilt @@ -524,27 +575,7 @@ - - - - Detector: - - - - - - - <html><head/><body><p>Reset the instrument configuration to the state when the most recent instrument file was loaded, or when the program started.</p></body></html> - - - Reset Configuration - - - - - - - + Qt::Vertical @@ -558,13 +589,30 @@ - + - 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. + <html><head/><body><p>Lock relative transformations between the detectors.</p><p><br/></p><p>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.</p></body></html> - Lock ROI Group Transformations + Lock relative transformations + + + + + + + <html><head/><body><p>&quot;Instrument Rigid Body&quot; means to transform all detectors in the entire instrument in the same way.</p><p><br/></p><p>&quot;Group Rigid Body&quot; means to transform all detectors in the selected detector's group in the same way. This setting requires all detectors to have their &quot;group&quot; specified in the instrument config.</p></body></html> + + + Instrument Rigid Body + + + + + Group Rigid Body + + @@ -578,7 +626,9 @@ detector - roi_lock_group_transforms + lock_relative_transforms + lock_relative_transforms_setting + locked_center_of_rotation sb_translation_range slider_translation_0 sb_translation_0