Running rotation indexing on a sub-range (e.g. images 60-120) segfaulted: the
first pass passed the *global* image number to RotationIndexer::ProcessImage,
which indexes v_ (sized to the run's image count) -> out-of-bounds write.
- ProcessImage now takes an explicit mid-exposure angle (optional; falls back to
the goniometer at the image index), so the indexer no longer assumes its slot
index equals the goniometer image index. IndexAndRefine supplies it via
RotationAngle(), matching the angle used for prediction. Added a bounds guard in
ProcessImage so a bad index can never corrupt memory.
- JFJochProcess feeds the rotation indexer the local ordinal (not the global
index), and shifts the goniometer (start += start_image*incr, incr *= stride,
per-image wedge preserved) so local index i maps to the angle of original image
start+i*stride - fixing rotation angles for the whole sub-range pipeline
(prediction, refinement, output), not just the indexer.
- Expose "Rotation images" (number used for the first pass) in the job dialog,
enabled when rotation indexing is on. (Count > available is already clamped by
select_equally_spaced_image_ordinals.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Foundation for making processing a first-class GUI activity:
- process/JFJochProcessCommandLine: reconstruct the equivalent jfjoch_process / jfjoch_azint
command line from a ProcessConfig + DiffractionExperiment + input path, for handing a job
off to a cluster. Unit-tested (full + azint).
- viewer/JFJochProcessController: runs one JFJochProcess job off the GUI thread (its own private
reader; HDF5 access is globally serialized so it is safe next to the interactive reader) and
reports back via queued Qt signals (started/phaseChanged/progress/finished/failed). Cancel()
forwards to JFJochProcess::Cancel(). Progress is throttled to ~200 updates per run.
The visible processing UI (jobs window + snapshot switcher + converged settings) builds on this.
Verified: tests/jfjoch_test [process]; jfjoch_viewer links and builds against JFJochProcess.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The full processing workflow no longer lives in the CLIs. New process/JFJochProcess
encapsulates it for jfjoch_process, jfjoch_azint and (later) the viewer:
- ProcessMode {AzimuthalIntegration, FullAnalysis}; ProcessConfig carries run control
(range, threads, output prefix, spot finding, rotation + scaling options, reference
data) while the DiffractionExperiment carries all algorithm settings.
- Run() executes setup -> optional two-pass rotation pre-pass -> parallel per-image loop
(std::thread) -> optional scaling/merging post-pass -> NXmxIntegrated _process.h5 that
links back to the original images. ProcessResult returns stats + merge text.
- Cancel() / std::atomic<bool> (receiver style), checked between images; the CLIs install
a SIGINT handler that calls it (fixes the previous Ctrl+C gap), the viewer will use the
same hook. JFJochProcessObserver streams progress / per-image results for a live GUI.
jfjoch_process.cpp and jfjoch_azint.cpp are now thin: argument parsing + experiment
configuration, then JFJochProcess::Run + stats printing. Behaviour and usage messages
are unchanged.
Adds JFJochProcessTest (azimuthal integration round-trip, no-output run, pre-cancel) over
a small generated dataset.
Verified: tests/jfjoch_test [HDF5] (83 cases / 1854 assertions); jfjoch_azint and
jfjoch_process run end-to-end on lyso_test (azint 20 images; full analysis recovers the
lysozyme cell at 25% indexing).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>