diff --git a/hexrd/ui/indexing/fiber_pick_utils.py b/hexrd/ui/indexing/fiber_pick_utils.py new file mode 100644 index 000000000..cd8e32ba4 --- /dev/null +++ b/hexrd/ui/indexing/fiber_pick_utils.py @@ -0,0 +1,157 @@ +import numpy as np + +from hexrd import constants as cnst +from hexrd.rotations import discreteFiber +from hexrd.transforms import xfcapi + + +def _pick_to_fiber(pick_coords, eta_ome_maps, map_index, step=0.5, + beam_vec=None, chi=0., as_expmap=True): + """ + Returns the orientations for the specified fiber parameters. + + Parameters + ---------- + pick_coords : array_like + The (2, ) list/vector containing the pick coordinates as (eta, omega) + in DEGREES. This corresponds to the (column, row) or (x, y) dimensions + on the map. + eta_ome_maps : hexrd.xrdutil.utils.EtaOmeMaps + The eta-omega maps. + map_index : int + The index of the current map. + step : scalar, optional + The step size along the fiber in DEGREES. The default is 0.5. + chi : scalar, optional + The chi angle from the associated instrument (specifically, + `instr.chi`). The default is 0. + beam_vec : array_like, optional + The beam vector of the associated instrument (specifically, + `instr.beam_vector`). The default is None (giving [0, 0, -1]). + as_expmap : bool, optional + Flag for converting the output from qauternions to exponential map. + The default is True. + + Returns + ------- + qfib : numpy.ndarray + The array containing the fiber points as quaternions or exponential + map parameters, according to the `as_expmap` kwarg. + + """ + pick_coords = np.atleast_1d(pick_coords).flatten() + + if beam_vec is None: + beam_vec = cnst.beam_vec + + ndiv = int(np.round(360./float(step))) + + # grab the planeData instance from the maps + # !!! this should have a copy of planeData that has hkls consistent with + # the map data. + pd = eta_ome_maps.planeData + bmat = pd.latVecOps['B'] + + # the crystal direction (plane normal) + crys_dir = pd.hkls[:, map_index].reshape(3, 1) + + # the sample direction + tth = pd.getTTh()[map_index] # !!! in radians + angs = np.atleast_2d(np.hstack([tth, np.radians(pick_coords)])) + samp_dir = xfcapi.anglesToGVec( + angs, bHat_l=beam_vec, chi=chi + ).reshape(3, 1) + + # make the fiber + qfib = discreteFiber( + crys_dir, samp_dir, + B=bmat, ndiv=ndiv, + invert=False, + csym=pd.getQSym(), ssym=None + )[0] + + if as_expmap: + phis = 2.*np.arccos(qfib[0, :]) + ns = xfcapi.unitRowVector(qfib[1:, :].T) + expmaps = phis*ns.T + return expmaps.T # (3, ndiv) + else: + return qfib.T # (4, ndiv) + + +def _angles_from_orientation(instr, eta_ome_maps, orientation): + """ + Return the (eta, omega) angles for a specified orientation consistent with + input EtaOmeMaps. + + Parameters + ---------- + instr : hexrd.instrument.HEDMInstrument + The instrument instance used to generate the EtaOmeMaps. + eta_ome_maps : hexrd.xrdutil.utils.EtaOmeMaps + The eta-omega maps. + orientation : array_like + Either a (3, ) or (4, ) element vector specifying an orientation. + + Raises + ------ + RuntimeError + If orientation has more than 4 elements. + + Returns + ------- + simulated_angles : list + A list with length = len(eta_ome_maps.dataStore) containing the angular + coordinates of all valid reflections for each map. If no valid points + exist for a particular map, the entry contains `None`. Otherwise, + the entry is a (2, p) array of the (eta, omega) coordinates in DEGREES + for the p valid reflections. + + """ + plane_data = eta_ome_maps.planeData + + # angle ranges from maps + eta_range = (eta_ome_maps.etaEdges[0], eta_ome_maps.etaEdges[-1]) + ome_range = (eta_ome_maps.omeEdges[0], eta_ome_maps.omeEdges[-1]) + ome_period = eta_ome_maps.omeEdges[0] + np.r_[0., 2*np.pi] + + # need the hklids + hklids = [i['hklID'] for i in plane_data.hklDataList] + + expmap = np.atleast_1d(orientation).flatten() + if len(expmap) == 4: + # have a quat; convert here + phi = 2.*np.arccos(expmap[0]) + n = xfcapi.unitRowVector(expmap[1:]) + expmap = phi*n + elif len(expmap) > 4: + raise RuntimeError( + "orientation must be a single exponential map or quaternion" + ) + + grain_param_list = [np.hstack([expmap, cnst.zeros_3, cnst.identity_6x1]), ] + sim_dict = instr.simulate_rotation_series( + plane_data, grain_param_list, + eta_ranges=[eta_range, ], ome_ranges=[ome_range, ], + ome_period=ome_period, wavelength=None + ) + + rids = [] + angs = [] + for sim in sim_dict.values(): + rids.append(sim[0][0]) + angs.append(sim[2][0]) + rids = np.hstack(rids) + angs = np.vstack(angs) + + simulated_angles = [] + for rid in hklids: + this_idx = rids == rid + if np.any(this_idx): + simulated_angles.append( + np.degrees(np.atleast_2d(angs[this_idx, 1:])) + ) + else: + simulated_angles.append(None) + + return simulated_angles diff --git a/hexrd/ui/indexing/ome_maps_select_dialog.py b/hexrd/ui/indexing/ome_maps_select_dialog.py index a11d047d9..9de91197d 100644 --- a/hexrd/ui/indexing/ome_maps_select_dialog.py +++ b/hexrd/ui/indexing/ome_maps_select_dialog.py @@ -150,6 +150,7 @@ def update_config(self): # Set the new config options on the internal config indexing_config = HexrdConfig().indexing_config maps_config = indexing_config['find_orientations']['orientation_maps'] + maps_config['_select_method'] = self.method_name maps_config['file'] = self.file_name maps_config['threshold'] = self.threshold maps_config['bin_frames'] = self.bin_frames @@ -162,6 +163,8 @@ def update_gui(self): indexing_config = HexrdConfig().indexing_config maps_config = indexing_config['find_orientations']['orientation_maps'] + self.method_name = maps_config.get('_select_method', 'load') + file_name = maps_config['file'] if maps_config['file'] else '' self.ui.file_name.setText(file_name) diff --git a/hexrd/ui/indexing/ome_maps_viewer_dialog.py b/hexrd/ui/indexing/ome_maps_viewer_dialog.py index d8baea7f7..fc7e24fcd 100644 --- a/hexrd/ui/indexing/ome_maps_viewer_dialog.py +++ b/hexrd/ui/indexing/ome_maps_viewer_dialog.py @@ -6,10 +6,12 @@ import numpy as np import yaml -from PySide2.QtCore import Signal, QObject, QSignalBlocker, QTimer, Qt +from PySide2.QtCore import ( + Signal, QItemSelectionModel, QObject, QSignalBlocker, QTimer, Qt +) from PySide2.QtWidgets import ( QCheckBox, QComboBox, QDoubleSpinBox, QFileDialog, QMessageBox, - QSizePolicy, QSpinBox + QSizePolicy, QSpinBox, QTableWidgetItem ) from hexrd.constants import sigma_to_fwhm @@ -17,14 +19,20 @@ clean_map, filter_maps_if_requested, filter_stdev_DFLT ) from hexrd.imageutil import find_peaks_2d +from hexrd.rotations import quatOfExpMap from hexrd.ui import resource_loader from hexrd.ui.color_map_editor import ColorMapEditor +from hexrd.ui.create_hedm_instrument import create_hedm_instrument from hexrd.ui.hexrd_config import HexrdConfig +from hexrd.ui.indexing.fiber_pick_utils import ( + _angles_from_orientation, _pick_to_fiber +) from hexrd.ui.navigation_toolbar import NavigationToolbar from hexrd.ui.select_items_widget import SelectItemsWidget from hexrd.ui.ui_loader import UiLoader +from hexrd.ui.utils import block_signals import hexrd.ui.constants import hexrd.ui.resources.indexing @@ -51,6 +59,13 @@ def __init__(self, data, parent=None): self.spots = None self.reset_internal_config() + self.cached_hand_picked_spots = {} + self.generated_fibers = np.empty((0,)) + self.current_fiber_spots = np.empty((0,)) + self.hand_picked_fibers = np.empty((0, 3)) + self.latest_picked_eta = None + self.latest_picked_ome = None + self.setup_widget_paths() self.setup_combo_box_item_data() @@ -80,20 +95,11 @@ def setup_connections(self): self.ui.rejected.connect(self.on_rejected) self.ui.quaternion_method.currentIndexChanged.connect( - self.update_quaternion_method_tab) - self.ui.quaternion_method.currentIndexChanged.connect( - self.update_seed_search_visibilities) - self.ui.quaternion_method.currentIndexChanged.connect( - self.update_config) - self.ui.quaternion_method.currentIndexChanged.connect( - self.update_spots) + self.quaternion_method_changed) self.ui.seed_search_method.currentIndexChanged.connect( - self.update_seed_search_method_tab) - self.ui.seed_search_method.currentIndexChanged.connect( - self.update_config) - self.ui.seed_search_method.currentIndexChanged.connect( - self.update_spots) + self.seed_search_method_changed) + self.color_map_editor.ui.minimum.valueChanged.connect( self.update_config) @@ -123,11 +129,33 @@ def changed_signal(w): self.ui.select_quaternion_grid_file.pressed.connect( self.select_quaternion_grid_file) + self.ui.current_fiber_slider.valueChanged.connect( + self.current_fiber_slider_value_changed) + + self.ui.current_fiber_angle.valueChanged.connect( + self.current_fiber_angle_value_changed) + + self.ui.add_fiber_button.clicked.connect(self.add_current_fiber) + + self.ui.picked_fibers_table.selectionModel().selectionChanged.connect( + self.picked_fibers_table_selection_changed) + + self.ui.picked_fibers_delete_selected.clicked.connect( + self.delete_selected_fiber_rows) + + self.ui.fiber_step.valueChanged.connect( + self.synchronize_fiber_step_boxes) + self.ui.hand_picked_fiber_step.valueChanged.connect( + self.synchronize_fiber_step_boxes) + + self.ui.fiber_step.valueChanged.connect(self.fiber_step_value_changed) + def setup_combo_box_item_data(self): # Set the item data for the combo boxes to be the names we want item_data = [ 'seed_search', 'grid_search', + 'hand_picked', ] for i, data in enumerate(item_data): self.ui.quaternion_method.setItemData(i, data) @@ -205,7 +233,11 @@ def on_rejected(self): self.rejected.emit() def validate(self): - if self.quaternion_method_name == 'grid_search': + if self.quaternion_method_name == 'hand_picked': + if self.hand_picked_fibers.size == 0: + msg = 'At least one fiber must be picked' + raise ValidationException(msg) + elif self.quaternion_method_name == 'grid_search': # Make sure the file exists. q_file = self.config['find_orientations']['use_quaternion_grid'] if not q_file or not Path(q_file).exists(): @@ -225,11 +257,14 @@ def validate(self): f'(4, n), but instead has a shape of "{quats.shape}".' ) raise ValidationException(msg) - else: + elif self.quaternion_method_name == 'seed_search': # Seed search. Make sure hkls were chosen. hkls = self.config['find_orientations']['seed_search']['hkl_seeds'] if not hkls: raise ValidationException('No hkls selected') + else: + msg = f'Unhandled quaternion method: {self.quaternion_method_name}' + raise ValidationException(msg) def setup_widget_paths(self): text = resource_loader.load_resource(hexrd.ui.resources.indexing, @@ -268,12 +303,16 @@ def quaternion_method_name(self, v): for i in range(w.count()): if v == w.itemData(i): w.setCurrentIndex(i) - self.update_seed_search_visibilities() + self.update_visibilities() self.update_quaternion_method_tab() return raise Exception(f'Unable to set quaternion_method: {v}') + @property + def quaternions_hand_picked(self): + return self.quaternion_method_name == 'hand_picked' + @property def quaternion_grid_file(self): return self.ui.quaternion_grid_file.text() @@ -282,16 +321,41 @@ def quaternion_grid_file(self): def quaternion_grid_file(self, v): self.ui.quaternion_grid_file.setText(v) - def update_seed_search_visibilities(self): - visible = self.quaternion_method_name == 'seed_search' + def update_visibilities(self): + self.update_seed_search_visibilities() + self.update_indexing_visibilities() + self.update_clustering_visibilities() + + @staticmethod + def set_widgets_visible(widgets, visible): + for w in widgets: + w.setVisible(visible) + def update_seed_search_visibilities(self): widgets = [ self.ui.select_hkls_group, self.ui.label_spots, ] + visible = self.quaternion_method_name == 'seed_search' - for w in widgets: - w.setVisible(visible) + self.set_widgets_visible(widgets, visible) + + def update_indexing_visibilities(self): + widgets = [ + self.ui.omega_group_box, + self.ui.eta_group_box, + ] + visible = self.quaternion_method_name in ('seed_search', 'grid_search') + + self.set_widgets_visible(widgets, visible) + + def update_clustering_visibilities(self): + widgets = [ + self.ui.clustering_group_box, + ] + visible = self.quaternion_method_name in ('seed_search', 'grid_search') + + self.set_widgets_visible(widgets, visible) @property def seed_search_method_name(self): @@ -348,6 +412,21 @@ def filter_maps(self, v): self.ui.filtering_apply_gaussian_laplace.setChecked(apply_gl) self.ui.filtering_fwhm.setValue(fwhm) + def quaternion_method_changed(self): + self.clear_generated_fibers() + self.clear_selected_fibers_artists() + self.select_fiber_rows([]) + + self.update_quaternion_method_tab() + self.update_visibilities() + self.update_config() + self.update_spots() + + def seed_search_method_changed(self): + self.update_seed_search_method_tab() + self.update_config() + self.update_spots() + def update_quaternion_method_tab(self): # Take advantage of the naming scheme... method_tab = getattr(self.ui, self.quaternion_method_name + '_tab') @@ -366,9 +445,9 @@ def hkls(self): def update_hkl_options(self): # This won't trigger a re-draw. Can change in the future if needed. - blocker = QSignalBlocker(self.ui.active_hkl) # noqa: F841 - self.ui.active_hkl.clear() - self.ui.active_hkl.addItems(self.hkls) + with block_signals(self.ui.active_hkl): + self.ui.active_hkl.clear() + self.ui.active_hkl.addItems(self.hkls) def setup_plot(self): # Create the figure and axes to use @@ -391,6 +470,8 @@ def setup_plot(self): # Center the toolbar self.ui.canvas_layout.setAlignment(self.toolbar, Qt.AlignCenter) + canvas.mpl_connect('button_press_event', self.plot_clicked) + self.fig = fig self.ax = ax self.canvas = canvas @@ -520,6 +601,8 @@ def update_plot(self): im.set_norm(self.norm) self.update_spots() + self.update_current_fiber_plot() + self.draw_selected_fibers() im.set_extent(self.extent) @@ -527,6 +610,9 @@ def update_plot(self): ax.autoscale_view() ax.axis('auto') + # Now disable autoscaling + ax.autoscale(False) + self.draw() def draw(self): @@ -661,7 +747,9 @@ def set_val(w, path): find_orientations = config['find_orientations'] - if find_orientations['use_quaternion_grid']: + if find_orientations.get('_hand_picked_quaternions', False): + self.quaternion_method_name = 'hand_picked' + elif find_orientations['use_quaternion_grid']: self.quaternion_method_name = 'grid_search' else: self.quaternion_method_name = 'seed_search' @@ -682,15 +770,23 @@ def set_val(w, path): self.working_dir = config.get('working_dir', HexrdConfig().working_dir) + self.synchronize_fiber_step_boxes(self.fiber_step) + + self.ui.current_fiber_slider.setRange( + 0, self.num_hand_picked_fibers - 1) + def update_config(self): # Update all of the config with their settings from the widgets config = self.config find_orientations = config['find_orientations'] - if self.quaternion_method_name == 'seed_search': - quat_file = None - else: + key = '_hand_picked_quaternions' + find_orientations[key] = self.quaternions_hand_picked + + if self.quaternion_method_name == 'grid_search': quat_file = self.quaternion_grid_file + else: + quat_file = None find_orientations['use_quaternion_grid'] = quat_file find_orientations['_quat_file'] = self.quaternion_grid_file @@ -763,6 +859,301 @@ class Cfg: # Perform the filtering filter_maps_if_requested(self.data, cfg) + def clear_generated_fibers(self): + # Reset the latest picks to None + self.latest_picked_eta = None + self.latest_picked_ome = None + + self.generated_fibers = np.empty((0,)) + self.ui.current_fiber_slider.setValue(0) + # In case the value didn't change. This shouldn't be expensive, + # so it's okay to run it twice. + self.update_current_fiber() + + def plot_clicked(self, event): + if not self.quaternions_hand_picked: + # If we are not hand picking quaternions, just return + return + + if not event.button == 3: + # Hand-picking quaternions is right-click only + return + + self.latest_picked_eta = event.xdata + self.latest_picked_ome = event.ydata + + self.recreate_generated_fibers() + + def recreate_generated_fibers(self): + pick_coords = (self.latest_picked_eta, self.latest_picked_ome) + if any(x is None for x in pick_coords): + # No picked coords. Just return. + return + + instr = create_hedm_instrument() + + kwargs = { + 'pick_coords': pick_coords, + 'eta_ome_maps': self.data, + 'map_index': self.current_hkl_index, + 'step': self.fiber_step, + 'beam_vec': instr.beam_vector, + 'chi': instr.chi, + 'as_expmap': True, + } + self.generated_fibers = _pick_to_fiber(**kwargs) + + self.ui.current_fiber_slider.setValue(0) + # In case the value didn't change. This shouldn't be expensive, + # so it's okay to run it twice. + self.update_current_fiber() + + def update_current_fiber(self): + enable = len(self.generated_fibers) > 0 + + enable_list = [ + self.ui.current_fiber_slider, + self.ui.current_fiber_angle, + self.ui.selected_fiber_orientation_0, + self.ui.selected_fiber_orientation_1, + self.ui.selected_fiber_orientation_2, + self.ui.add_fiber_button, + ] + for w in enable_list: + w.setEnabled(enable) + + for i, v in enumerate(self.current_fiber_orientation): + w = getattr(self.ui, f'selected_fiber_orientation_{i}') + w.setValue(v) + + angle = self.current_fiber_index * self.fiber_step + self.ui.current_fiber_angle.setValue(angle) + + self.generate_current_fiber_spots() + self.update_current_fiber_plot() + + def generate_current_fiber_spots(self): + if self.current_fiber_index >= len(self.generated_fibers): + fibers = [] + else: + fibers = self.generated_fibers[self.current_fiber_index] + + self.current_fiber_spots = self.generate_fiber_spots(fibers) + + def generate_fiber_spots(self, fibers): + if len(fibers) == 0: + return np.empty((0,)) + + kwargs = { + 'instr': create_hedm_instrument(), + 'eta_ome_maps': self.data, + 'orientation': fibers, + } + return _angles_from_orientation(**kwargs) + + def clear_current_fiber_plot(self): + if hasattr(self, '_current_fiber_lines'): + self._current_fiber_lines.remove() + del self._current_fiber_lines + + def update_current_fiber_plot(self): + self.clear_current_fiber_plot() + hkl_idx = self.current_hkl_index + if len(self.current_fiber_spots) <= hkl_idx: + self.draw() + return + + current = self.current_fiber_spots[hkl_idx] + if current.size: + kwargs = { + 'x': current[:, 0], + 'y': current[:, 1], + 's': 36, + 'c': 'm', + 'marker': '+', + } + self._current_fiber_lines = self.ax.scatter(**kwargs) + + self.draw() + + @property + def current_fiber_orientation(self): + if len(self.generated_fibers) == 0: + return np.array([0, 0, 0]) + + return self.generated_fibers[self.current_fiber_index] + + @property + def current_fiber_index(self): + return self.ui.current_fiber_slider.value() + + def current_fiber_slider_value_changed(self): + self.update_current_fiber() + + def current_fiber_angle_value_changed(self, v): + new_slider_index = round(v / self.fiber_step) + self.ui.current_fiber_slider.setValue(new_slider_index) + + # This usually already happens, but make sure the angle gets + # updated to its new value (it may need to round to the nearest). + angle = self.current_fiber_index * self.fiber_step + self.ui.current_fiber_angle.setValue(angle) + + def add_current_fiber(self): + to_stack = (self.hand_picked_fibers, self.current_fiber_orientation) + self.hand_picked_fibers = np.vstack(to_stack) + self.update_picked_fibers_table() + + self.clear_generated_fibers() + + table = self.ui.picked_fibers_table + last_row = table.rowCount() - 1 + self.select_fiber_rows([last_row]) + + def update_picked_fibers_table(self): + table = self.ui.picked_fibers_table + table.clearContents() + table.setColumnCount(3) + table.setRowCount(len(self.hand_picked_fibers)) + for i, orientation in enumerate(self.hand_picked_fibers): + for j in range(3): + item = QTableWidgetItem(f'{orientation[j]:.4f}') + item.setTextAlignment(Qt.AlignCenter) + item.setFlags(item.flags() & ~Qt.ItemIsEditable) + table.setItem(i, j, item) + + @property + def hand_picked_quaternions(self): + # We store these as 3D exp maps. Convert and return as quaternions. + quats = quatOfExpMap(self.hand_picked_fibers.T) + if quats.ndim == 1: + # quatOfExpMap() squeezes the output. We must reshape it. + quats = np.atleast_2d(quats).T + + return quats + + @property + def hand_picked_fibers(self): + return self._hand_picked_fibers + + @hand_picked_fibers.setter + def hand_picked_fibers(self, v): + self._hand_picked_fibers = v + # Clear the cache for hand picked spots + self.cached_hand_picked_spots.clear() + + def clear_selected_fibers_artists(self): + lines = getattr(self, '_selected_fibers_artists', []) + while lines: + lines.pop(0).remove() + + @property + def selected_fibers_rows(self): + selected = self.ui.picked_fibers_table.selectionModel().selectedRows() + selected = [] if None else selected + return [x.row() for x in selected] + + def picked_fibers_table_selection_changed(self): + self.draw_selected_fibers() + + enable_delete = len(self.selected_fibers_rows) > 0 + self.ui.picked_fibers_delete_selected.setEnabled(enable_delete) + + def spots_for_hand_picked_quaternion(self, i): + if i >= len(self.hand_picked_fibers): + return None + + cache = self.cached_hand_picked_spots + + # Check the cache first. If not present, add to the cache. + if i not in cache: + fiber = self.hand_picked_fibers[i] + if not fiber.size: + return None + + cache[i] = self.generate_fiber_spots(fiber) + + return cache[i][self.current_hkl_index] + + def draw_selected_fibers(self): + self.clear_selected_fibers_artists() + + artists = [] + for i in self.selected_fibers_rows: + spots = self.spots_for_hand_picked_quaternion(i) + if spots is None: + continue + + kwargs = { + 'x': spots[:, 0], + 'y': spots[:, 1], + 's': 36, + 'marker': 'o', + 'facecolors': 'none', + 'edgecolors': 'c', + 'linewidths': 1, + } + artists.append(self.ax.scatter(**kwargs)) + + self._selected_fibers_artists = artists + self.draw() + + def select_fiber_rows(self, rows): + table = self.ui.picked_fibers_table + selection_model = table.selectionModel() + + with block_signals(selection_model): + selection_model.clearSelection() + command = QItemSelectionModel.Select | QItemSelectionModel.Rows + + for i in rows: + if i is None or i >= table.rowCount(): + # Out of range. Don't do anything. + continue + + # Select the row + model_index = selection_model.model().index(i, 0) + selection_model.select(model_index, command) + + self.picked_fibers_table_selection_changed() + + def delete_selected_fiber_rows(self): + selected = self.selected_fibers_rows + self.hand_picked_fibers = np.delete(self.hand_picked_fibers, + selected, 0) + # There should be no selection now + self.select_fiber_rows([]) + self.update_picked_fibers_table() + + def synchronize_fiber_step_boxes(self, v): + self.ui.fiber_step.setValue(v) + self.ui.hand_picked_fiber_step.setValue(v) + + def fiber_step_value_changed(self, v): + prev_angle = self.ui.current_fiber_angle.value() + + self.ui.current_fiber_slider.setRange( + 0, self.num_hand_picked_fibers - 1) + self.ui.current_fiber_angle.setSingleStep(self.fiber_step) + + if self.quaternions_hand_picked: + # Re-create the generated fibers + # Restore the closest value to the previous angle + self.recreate_generated_fibers() + self.ui.current_fiber_angle.setValue(prev_angle) + + @property + def fiber_step(self): + return self.ui.fiber_step.value() + + @fiber_step.setter + def fiber_step(self, v): + self.ui.fiber_step.setValue(v) + + @property + def num_hand_picked_fibers(self): + return round(360 / self.fiber_step) + class ValidationException(Exception): pass diff --git a/hexrd/ui/indexing/run.py b/hexrd/ui/indexing/run.py index 11a7ba22f..cbb831a77 100644 --- a/hexrd/ui/indexing/run.py +++ b/hexrd/ui/indexing/run.py @@ -135,6 +135,15 @@ def view_ome_maps(self): self.ome_maps_viewer_dialog = dialog def ome_maps_viewed(self): + # If we did the hand-picked method, go ahead and skip now to + # generating the grains table and running fit grains + dialog = self.ome_maps_viewer_dialog + if dialog.quaternions_hand_picked: + self.qbar = dialog.hand_picked_quaternions + self.generate_grains_table() + self.start_fit_grains_runner() + return + # The dialog should have automatically updated our internal config # Let's go ahead and run the indexing! @@ -298,7 +307,9 @@ def generate_grains_table(self): print('No grains found') return - msg = f'{num_grains} grains found' + plural = 's' if num_grains != 1 else '' + + msg = f'{num_grains} grain{plural} found' self.update_progress_text(msg) print(msg) diff --git a/hexrd/ui/resources/ui/ome_maps_viewer_dialog.ui b/hexrd/ui/resources/ui/ome_maps_viewer_dialog.ui index f41b5b6c4..b10c6b60c 100644 --- a/hexrd/ui/resources/ui/ome_maps_viewer_dialog.ui +++ b/hexrd/ui/resources/ui/ome_maps_viewer_dialog.ui @@ -7,7 +7,7 @@ 0 0 1900 - 1108 + 1149 @@ -404,7 +404,7 @@ - 0 + 2 @@ -886,6 +886,235 @@ + + + Hand Picked + + + + + + + 0 + 0 + + + + Picked Fibers + + + + + + QAbstractItemView::SelectRows + + + false + + + true + + + + + + + false + + + Delete Selected + + + + + + + + + + Fiber Step: + + + + + + + + 0 + 0 + + + + Current Fiber + + + + + + + + false + + + Qt::AlignCenter + + + true + + + QAbstractSpinBox::NoButtons + + + false + + + 6 + + + -10.000000000000000 + + + 10.000000000000000 + + + + + + + false + + + Qt::AlignCenter + + + true + + + QAbstractSpinBox::NoButtons + + + false + + + 6 + + + -10.000000000000000 + + + 10.000000000000000 + + + + + + + false + + + Qt::AlignCenter + + + true + + + QAbstractSpinBox::NoButtons + + + false + + + 6 + + + -10.000000000000000 + + + 10.000000000000000 + + + + + + + + + false + + + Add Fiber + + + + + + + Right-click the canvas to generate fibers for a position + + + + + + + false + + + 719 + + + Qt::Horizontal + + + + + + + false + + + false + + + ° + + + 8 + + + 360.000000000000000 + + + + + + + + + + false + + + ° + + + 8 + + + 0.000000000000000 + + + 360.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + @@ -900,6 +1129,11 @@ Grid Search + + + Hand Picked + + @@ -915,9 +1149,6 @@ - apply_filtering - filtering_apply_gaussian_laplace - filtering_fwhm quaternion_method quaternion_method_tab_widget seed_search_method @@ -935,6 +1166,16 @@ bl_threshold bl_overlap fiber_step + quaternion_grid_file + select_quaternion_grid_file + current_fiber_slider + current_fiber_angle + selected_fiber_orientation_0 + selected_fiber_orientation_1 + selected_fiber_orientation_2 + add_fiber_button + picked_fibers_table + picked_fibers_delete_selected omega_tolerance eta_tolerance eta_mask @@ -943,7 +1184,12 @@ clustering_algorithm write_scored_orientations select_working_dir + apply_filtering + filtering_apply_gaussian_laplace + filtering_fwhm + active_hkl label_spots + export_button