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