Two deps the Linux build picked up from the system were absent on Windows.
Bundle both, unconditionally (matching how libtiff/FFTW are vendored), so the
tree builds without system Eigen/JPEG:
- Eigen 5.0.1 via FetchContent with OVERRIDE_FIND_PACKAGE, so every
find_package(Eigen3) -- ours, Ceres', and ffbidx' -- resolves to this copy and
the requested 3.4 version is satisfied by the newer major. ffbidx's bundled
eigen submodule is disabled (GIT_SUBMODULES "" on fast-indexer) so it no longer
creates a duplicate Eigen3::Eigen target. Verified on a CUDA build: Ceres 2.3.0,
the analysis libs, and ffbidx's CUDA code all compile against 5.0.1.
- libjpeg-turbo 3.0.4 via ExternalProject (upstream discourages add_subdirectory);
built+installed static and imported as JPEG::JPEG, replacing find_package(JPEG)
in preview/. SIMD is lazy: WITH_SIMD=ON + REQUIRE_SIMD=OFF means NASM-accelerated
when nasm is present, otherwise a warning and a scalar build (no hard failure).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The recurring goal is a Windows jfjoch_viewer built with MSVC *and* CUDA ON
(GPU processing is wanted, not a CPU-only fallback); the non-CUDA path is the
macOS fallback. Also note the three deps a self-contained Windows build still
needs but FetchContent does not auto-provide: ZLIB (bundle zlib-ng as
ZLIB::ZLIB), libjpeg-turbo (ExternalProject), and Eigen (header-only, also
needed by Ceres) -- each guarded so the Linux build is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two spots used POSIX-only path logic that breaks on Windows:
- dataset_name() located the basename with rfind('/'), which misses
backslash separators, so a C:\...\foo_master.h5 file prefix kept the
whole directory. Use std::filesystem::path::filename() instead.
- Legacy-format data-file resolution joined paths with
fmt::format("{}/{}", ...), producing mixed \/ separators and prepending
the master directory even to absolute link targets. Reuse the existing
ResolveRelativeToMaster() helper, which joins via std::filesystem and
leaves absolute targets untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FFTW exposes fftw3.h only via $<INSTALL_INTERFACE:include>, so consuming
fftw3f from the build tree (FetchContent) leaves FFTIndexerCPU.h unable to
find the header. A system-installed fftw3.h masks this on Linux, so it only
bit on Windows; add the source-tree api/ dir to fftw3f's interface includes.
Also two viewer-reachable warnings:
- time_utc.h (C4477): %03ld was fed a chrono milliseconds::rep (64-bit), not
a long; cast the 0-999 value to int and use %03d.
- HDF5Objects.h (C4700): value-initialize the scalar read buffer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
libtiff's webp and lerc codec options default ON whenever their library is
found on the build host, dragging in libwebp + libsharpyuv (webp) and libLerc.
We only need DEFLATE (zlib) + the internal LZW codec, so turn both OFF.
httplib likewise auto-enables Brotli content-encoding when system Brotli is
present; gzip/zlib is enough for the broker, so disable it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
StreamWriter.cpp was compiled into both JFJochWriter and JFJochStreamWriter,
so JFJochWriter still transitively pulled in image_puller headers via
StreamWriter.h. Compile it only into JFJochStreamWriter (which already
links JFJochImagePuller); targets needing the streaming writer (jfjoch_writer,
jfjoch_test) link that lib, while JFJochWriter consumers (viewer, tools,
ImagePusher) no longer drag in the puller. Also drop a stray duplicate
HDF5DataFile source listing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address portability issues found building jfjoch_viewer with MSVC:
- common/{ADUHistogram,AzimuthalIntegrationProfile}.h and
image_analysis/spot_finding/StrongPixelSet.h only need DeviceOutput,
so include fpga/pcie_driver/jfjoch_fpga.h (plain-C, self-contained)
directly instead of acquisition_device/AcquisitionDevice.h, which
dragged <unistd.h> into the viewer tree.
- common/time_utc.h: guard gmtime_r/timegm/localtime_r with the MSVC
equivalents (gmtime_s/_mkgmtime/localtime_s) under _WIN32; drop the
duplicated includes and add the headers used directly.
- gemmi_gph/gemmi/utf.hpp: vendor the upstream gemmi header; fileutil.hpp
includes it on Windows for UTF8_to_wchar but it was never vendored.
- writer/HDF5Objects.cpp: ExtractFilename returns path.filename().string()
(std::filesystem::path has no implicit conversion to std::string on
Windows, where it is wchar_t-based).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switch the libtiff FetchContent from the personal GitHub mirror to the
upstream gitlab repo and bump v4.6.0 -> v4.7.1 -- cleaner provenance; the
mirror was only ever a network-reachability workaround.
Verified on Linux: fetches, builds static (libtiff.a/libtiffxx.a), and
jfjoch_viewer links cleanly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FFTW selects SIMD codelets at runtime via cpuid, so enabling AVX2 does not
require an AVX2 CPU -- it just speeds up the CPU FFT fallback indexer on
capable hardware and falls back to SSE2/scalar otherwise. Arch-guarded to
x86_64/AMD64 (ARM would need ENABLE_NEON). AVX-512 left off on purpose:
it's x86-64-v4 (not our v3 target), the FFT gain over AVX2 is marginal,
older Intel downclocks on it, and it's the riskiest to build under MSVC.
Verified on Linux: SSE2/AVX/AVX2 codelets compile, libfftw3f.a stays a
single static archive, jfjoch_viewer relinks cleanly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both are leaf deps (JFJochPreview / JFJochIndexing) that only our own code
looks for, so building them ourselves removes the system-package lottery and
gives a reproducible, statically-linked build on every platform.
- libtiff: FetchContent, library only (jbig/zstd/lzma/jpeg/old-jpeg/tools/
tests off). The C++ binding (TIFF::CXX / libtiffxx) is packaged
inconsistently across distros -- missing on Rocky 9 -- and absent on
Windows, so find_package(TIFF COMPONENTS CXX) was unreliable; that call is
removed and JFJochPreview links the tiff/tiffxx targets directly. GitHub
mirror because upstream (gitlab) is unreachable from some restricted hosts.
- FFTW: FetchContent single precision (ENABLE_FLOAT) from the release tarball
-- the git repo ships no pre-generated codelets (needs OCaml genfft). It's
now always available, so the CPU FFT indexer is always built and
JFJOCH_USE_FFTW always defined; the "FFTW disabled" path is gone. Static
(libfftw3f.a) via the global BUILD_SHARED_LIBS OFF.
Verified on Linux: jfjoch_viewer builds and links libfftw3f.a + libtiff*.a,
all static.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Under JFJOCH_VIEWER_ONLY, only the viewer's transitive deps are built, so
sls_detector_package (detector_control/receiver) and catch2 (tests) are
never used -- and sls does not configure under MSVC, which is the whole
point of the viewer-only mode. Skip fetching just those two; zstd, hdf5,
spdlog, libzmq and httplib are still required (JFJochReader links httplib).
Verified: cmake -DJFJOCH_VIEWER_ONLY=ON configures with sls/catch2 absent
and `make jfjoch_viewer` still links.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CMake >= 4.0 (4.3.1 ships bundled with Visual Studio 2026) makes any
cmake_minimum_required(VERSION < 3.5) a fatal error. The fetched libzmq
still declares VERSION 3.0.2, which aborts configuration before our own
CMakeLists gets a say. Set CMAKE_POLICY_VERSION_MINIMUM=3.5 before the
FetchContent_MakeAvailable calls so those subprojects configure; the
variable is simply unused (harmless) on CMake < 3.30.
Verified against CMake 4.3.1: the real libzmq source fails without it and
configures with it, and an in-file SET() propagates into the
add_subdirectory child exactly as a -D cache entry does.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New option (default OFF) that compiles only jfjoch_viewer and the
libraries it transitively links -- common, image_analysis, writer,
reader, preview, image_puller, frame_serialize, gemmi_gph, compression,
jungfrau, and the OpenAPI model (JFJochAPI) -- skipping the Linux-only
online stack (broker service, receiver, fpga, acquisition_device,
detector_control, image_pusher, xds-plugin, tests, tools) and the
frontend. This is the standalone build path for the cross-platform
viewer.
JFJochAPI is the only piece the viewer needs from broker/, so the broker
service library and executable are gated behind NOT JFJOCH_VIEWER_ONLY.
The full build and JFJOCH_VIEWER_BUILD (viewer on top of the full build)
are unchanged -- that path is the verbatim ELSE() branch.
Verified on Linux: cmake -DJFJOCH_VIEWER_ONLY=ON configures and
`make jfjoch_viewer` links cleanly with the online stack absent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the build less hostile to MSVC (needed for the viewer port) and
align the CUDA standard with C++.
- Remove the CMAKE_C/CXX_FLAGS_RELEASE overrides: -O3/-DNDEBUG only
duplicate CMake's own GCC/Clang Release defaults, and the SET()
clobbered the MSVC defaults. The lone meaningful flag,
-Wno-deprecated-enum-enum-conversion, merely silenced warnings from
the vendored Xilinx ap_private.h in the FPGA path; with no -Werror
it is just log noise, so drop it.
- CMAKE_CUDA_STANDARD 17 -> 20 (+ STANDARD_REQUIRED); CUDA >= 12.8
already supports C++20.
- Drop the file-scoped -Ofast on JFPedestalCalc.cpp /
JFConversionFloatingPoint.cpp: it is GCC-only and reaches the MSVC
viewer via JFJochCommon -> JFCalibration. Those files now build at
plain -O3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bare python3 is often too old for the Sphinx build. Auto-select the
newest python3.13/3.12/3.11/3 found on PATH, still allowing $PYTHON to
override.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When only one azimuthal phi bound is entered, fill the other so the
sector is well-defined and the server accepts it: a missing phi_min
becomes 0 and a missing phi_max becomes 360. Leaving both empty still
means a full ring.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface the optional azimuthal phi-sector bounds (phi_min_deg/phi_max_deg)
as editable columns, so a sector can be set from the web UI, not only the
viewer. Also fix the Q_max row's d header, which read d_max but is d_min.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ROI bound editors now have a white background, a capped width and
right-aligned text, which reads better for numeric entry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restore the ROI-to-mask action on the new list: "Add to mask" and
"Subtract from mask" buttons rasterise the selected ROI into the user
mask (set or clear), through the same UpdateUserMask_i path. The ROI is
evaluated with per-pixel resolution and phi from the geometry, so box,
circle and azimuthal (sector) ROIs all map correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With the scratch ROI panel gone, delete the now-orphaned
JFJochViewerImageROIStatistics(_Box/_Circle) widgets and the worker's
unused scratch-ROI plumbing (SetROIBox/SetROICircle, the ROIElement
member, AddROIToUserMask/SubtractROIFromUserMask). ROI-to-mask, if wanted
later, should act on the selected list ROI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clarify in FPGA_DATA_ANALYSIS.md that the ROI and azimuthal definitions
are also evaluated on CPU via the shared image_analysis library, so they
apply to the DECTRIS SIMPLON (EIGER) path that has no FPGA, not only the
FPGA-accelerated JUNGFRAU/PSI path. Document the three NXmx writer formats
(Legacy/VDS/Integrated) and the two acquisition workflows in CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two Mask-menu entries read a 16-bit TIFF (via ReadTIFFFromString16) and
apply it as the user mask: "replace" overwrites the current mask, "add"
unions the TIFF into it. The TIFF must match the detector dimensions; any
non-zero pixel masks. Both go through UpdateUserMask_i like the other
mask edits.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Download and "Upload to server" buttons to the ROI panel. The HTTP
reader gains GetROIDefinitions (GET /config/roi) and UploadROIDefinitions
(PUT /config/roi), converting between ROIDefinition and the generated
Roi_definitions model (including the optional azimuthal phi sector), the
same shapes OpenAPIConvert uses on the server. Download applies the
fetched ROIs through SetROIDefinition; upload pushes the current ones.
Both are no-ops unless the viewer is connected to a broker.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The interactive shift-drag (box) / shift+ctrl-drag (circle) now creates a
new persistent ROI in the list instead of feeding the old single-ROI
scratch panel. The base emits a roiScratchDrawn hook on release; the
diffraction image turns the drawn shape into a named ROI committed via
SetROIDefinition.
The old JFJochViewerImageROIStatistics scratch panel and all its wiring
(box/circle configuration, single-ROI result, add/subtract user mask) are
removed from the side panel and window; the ROI list is now the single
source.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ROI panel now shows editable numeric fields for the selected ROI,
labelled by type: box min/max X/Y, circle centre/radius, or azimuthal
Q-range and phi sector. Editing a field rebuilds that ROI and commits it
through the same SetROIDefinition path, so all three types can be created
(via +) and dialled in by typing, not only by dragging.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Azimuthal ROIs now show discrete grab handles on the selected ROI: the
inner/outer arc (resize Q/d) and, for a sector, the two phi edges
(rotate). Editing is by clicking a handle, with a generous tolerance,
which fixes the previously near-impossible phi-edge grab. Clicking the
(large) interior of an azimuthal ROI now only selects it and lets the
view pan, instead of capturing the gesture.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend canvas ROI editing beyond move:
- Box: drag corners/edges to resize (handles shown on the selected ROI).
- Circle: drag the perimeter to resize, interior to move.
- Azimuthal: drag the inner/outer arc to change the Q/d range, or a radial
edge to rotate a phi bound; sampled through the geometry so it tracks
the conic.
- Delete removes the selected ROI (the view now takes keyboard focus).
All edits go through the same live, throttled SetROIDefinition path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clicking a box/circle on the image now selects it (syncing the side-panel
combobox) and grabs it for moving, with a closed-hand cursor during the
drag. The move recomputes ROI statistics live rather than only on release,
throttled to at most one in-flight recompute so the worker is not flooded.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add base-class mouse hooks (roiEditPress/Move/Release) so the diffraction
image can edit the persistent ROI selected in the side-panel combobox.
The selected ROI is highlighted (dashed, thicker), and dragging its
interior moves a box or circle; on release the new geometry is committed
through SetROIDefinition, which recomputes only the ROIs. Resize,
azimuthal handles and delete-key follow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Edited ROIs now override the file's for every subsequently loaded image
and survive settings changes, until a new file is opened (previously a
delete/add was lost on the next image). Tracked via an roi_override_ that
is re-applied to curr_experiment and to each loaded image's dataset.
- The ROI selector is a compact combobox instead of a list widget.
- "Show labels" and "Translucent fill" share one row to save space.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add JFJochViewerROIList to the side panel: a list of the dataset's ROIs
with selection, add (box/circle/azimuthal), rename and delete, plus a
statistics readout (sum/mean/std/max/valid/masked/centre-of-mass) for the
selected ROI, taken from the analysis output for the current image. Edits
emit a full ROIDefinition, routed to the worker's SetROIDefinition.
Per-ROI statistics now live in this panel rather than the canvas labels;
the diffraction image's labels show only the ROI name, and the ad-hoc
ROIIntegrationCPU computation there is removed in favour of the analysis
pipeline. The result widget now reports std dev instead of variance.
The single-ROI scratch panel remains for now and will be retired once the
interactive canvas editing replaces it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The reading worker's experiment is the source of truth for ROIs. Add a
SetROIDefinition slot that updates curr_experiment.ROI(), rebuilds the
analysis ROI engine, mutates the dataset (so the canvas reflects the new
ROIs) and recomputes only the ROIs for the current image via RunROIOnly.
On image load when full re-analysis is off, ROIs are still computed via
AnalyzeROIOnly so the statistics stay current.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add three entry points so ROI statistics can be (re)computed without a
full re-analysis, in support of interactive ROI editing in the viewer:
- RebuildROI(): recreate the ROI engine after the ROI set changes (the
CudaStream is now kept as a member so the GPU engine can be rebuilt).
- AnalyzeROIOnly(): decompress + preprocess + ROI, skipping azimuthal
integration, spot finding and indexing (a new image when re-analysis
is off).
- RunROIOnly(): rerun only the ROI integration on the already-preprocessed
image (an interactive ROI move). A full Analyze() still computes ROIs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When ROI labels are shown, each ROI's label now also reports its sum,
max and pixel count for the current image. Rather than reimplementing the
accumulation, this reuses the existing ROIIntegrationCPU engine (the
software counterpart of the FPGA roi_calc), built from the experiment and
cached per dataset. A small adapter folds the viewer's gap sentinel
(INT32_MIN+1) onto the engine's masked sentinel (INT32_MIN) so masked and
saturated pixels are handled correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The full-ring annulus built the inner ring as a separate QPainterPath and
merged it with addPath, which stitched a spurious segment from (0,0) to
the ring. Build both concentric rings as subpaths of one path, each begun
with an explicit moveTo, so there is no connecting segment.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Azimuthal ROIs now render on the diffraction image as annular sectors
(or full-ring annuli), sampled through DiffractionGeometry::ResPhiToPxl
so the outline follows the ROI footprint, including wrap-around sectors.
Add two side-panel toggles (both default off): a translucent fill for
every ROI (helpful when outline colours clash with the image colour map,
and with many ROIs) and ROI name labels (constant on-screen size). Wired
side panel -> window -> diffraction image like the existing feature
toggles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a file with ROI definitions is opened (read into experiment.ROI()
by the HDF5 reader), the diffraction image now overlays the configured
box and circle ROIs as distinct colour-coded outlines, alongside the
existing resolution rings and spots. This is the first step of the
ROI-map-based multi-ROI canvas: showing the ROIs loaded from a file.
Azimuthal ROIs (wedge rendering) and per-ROI statistics from the bitmap
follow in subsequent steps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Footer "Paul Scherrer Institute" now links to https://www.psi.ch
- Reference drawer gains a "Source code" entry linking out to the Gitea
repository (new nav-item href that opens in a new tab)
- Reference drawer gains a "License" entry rendering the nicely formatted
docs/LICENSE.md inline (generalises the changelog renderer into MarkdownDoc)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The framed Sphinx changelog reused MUI with the same colours, so it read as
a picture-in-picture. Bundle docs/CHANGELOG.md at build time (?raw) and render
it with react-markdown, themed to match the app, for a quick frame-less view.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Documentation tab pointed at jungfraujoch.readthedocs.io, which lags
behind the installed version. Build the Sphinx docs into frontend/dist/docs
and serve them at /frontend/docs so they always match the package.
- make_doc.sh: accept an output dir (default public), absolutize it, run
from the script dir, and allow $PYTHON override
- package.json: add "docs" script building into dist/docs
- CMakeLists: run "npm run docs" in the frontend target so CPack packs the
docs via the existing dist/ install
- App.tsx: point the Documentation DocFrame at /frontend/docs/index.html
Also clean up the Sphinx build (911 -> 0 warnings):
- fix typo myst_heading_anchor -> myst_heading_anchors so cross-doc
#anchor links actually resolve
- suppress myst.header/myst.xref_missing (auto-generated OpenAPI client
docs only) and the harmless sphinx_material config.cache note
- drop a dangling "---" transition at the end of CPU_DATA_ANALYSIS.md
- add REPOSITORIES and the generated python_client model pages to toctrees
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>