From 63d76bac72d3bacb4b403b9354068ead4b5c8294 Mon Sep 17 00:00:00 2001 From: matei jordache Date: Mon, 30 Mar 2026 23:10:42 -0700 Subject: [PATCH] add help docs and ? --- backend/server.py | 13 +++ docs/nodes/3D View.md | 33 ++++++ docs/nodes/ACF 1D.md | 27 +++++ docs/nodes/ACF 2D.md | 26 +++++ docs/nodes/Angle Measure.md | 28 +++++ docs/nodes/Annotations.md | 30 ++++++ docs/nodes/Blind Tip Estimate.md | 32 ++++++ docs/nodes/Color Map.md | 26 +++++ docs/nodes/Colormap Adjust.md | 28 +++++ docs/nodes/Coordinate Pair.md | 24 +++++ docs/nodes/Coordinate.md | 24 +++++ docs/nodes/Crop-Resize.md | 31 ++++++ docs/nodes/Cross Section.md | 29 +++++ docs/nodes/Cross-Correlate.md | 28 +++++ docs/nodes/Cursors.md | 26 +++++ docs/nodes/Curvature.md | 31 ++++++ docs/nodes/Custom Convolution.md | 30 ++++++ docs/nodes/Draw Mask.md | 28 +++++ docs/nodes/Edge Detect.md | 27 +++++ docs/nodes/Entropy.md | 28 +++++ docs/nodes/FFT 1D.md | 26 +++++ docs/nodes/FFT 2D.md | 30 ++++++ docs/nodes/FFT Filter.md | 29 +++++ docs/nodes/Facet Level.md | 28 +++++ docs/nodes/Field Arithmetic.md | 27 +++++ docs/nodes/Fix Zero.md | 25 +++++ docs/nodes/Flip.md | 25 +++++ docs/nodes/Float Slider.md | 25 +++++ docs/nodes/Folder.md | 24 +++++ docs/nodes/Font.md | 25 +++++ docs/nodes/Fractal Dimension.md | 27 +++++ docs/nodes/Gaussian Filter.md | 26 +++++ docs/nodes/Gradient.md | 26 +++++ docs/nodes/Grain Analysis.md | 27 +++++ docs/nodes/Grain Distance Transform.md | 29 +++++ docs/nodes/Grain Filter.md | 28 +++++ docs/nodes/Histogram.md | 28 +++++ docs/nodes/Image (Demo).md | 25 +++++ docs/nodes/Image.md | 28 +++++ docs/nodes/Inverse 2D FFT.md | 28 +++++ docs/nodes/Kuwahara Filter.md | 26 +++++ docs/nodes/Line Correction.md | 33 ++++++ docs/nodes/Local Contrast.md | 27 +++++ docs/nodes/Markup.md | 29 +++++ docs/nodes/Mask Invert.md | 24 +++++ docs/nodes/Mask Morphology.md | 28 +++++ docs/nodes/Mask Operations.md | 26 +++++ docs/nodes/Median Filter.md | 26 +++++ docs/nodes/Note.md | 26 +++++ docs/nodes/Number.md | 23 ++++ docs/nodes/PSDF.md | 27 +++++ docs/nodes/Plane Level.md | 26 +++++ docs/nodes/Polynomial Level.md | 28 +++++ docs/nodes/Preview.md | 25 +++++ docs/nodes/Print Table.md | 21 ++++ docs/nodes/Radial Profile.md | 28 +++++ docs/nodes/Resample.md | 28 +++++ docs/nodes/Rotate.md | 28 +++++ docs/nodes/Save Layers.md | 29 +++++ docs/nodes/Save.md | 28 +++++ docs/nodes/Scar Removal.md | 31 ++++++ docs/nodes/Slope Distribution.md | 27 +++++ docs/nodes/Spot Removal.md | 28 +++++ docs/nodes/Statistics.md | 23 ++++ docs/nodes/Stats.md | 27 +++++ docs/nodes/Template Match.md | 28 +++++ docs/nodes/Text Note.md | 22 ++++ docs/nodes/Threshold Mask.md | 29 +++++ docs/nodes/Tip Deconvolution.md | 26 +++++ docs/nodes/Tip Model.md | 30 ++++++ docs/nodes/Value Display.md | 27 +++++ docs/nodes/Watershed Segmentation.md | 33 ++++++ docs/nodes/Wavelet Denoise.md | 29 +++++ frontend/src/CustomNode.jsx | 55 +++++++++- frontend/src/api.js | 6 ++ frontend/src/styles.css | 143 +++++++++++++++++++++++++ 76 files changed, 2184 insertions(+), 1 deletion(-) create mode 100644 docs/nodes/3D View.md create mode 100644 docs/nodes/ACF 1D.md create mode 100644 docs/nodes/ACF 2D.md create mode 100644 docs/nodes/Angle Measure.md create mode 100644 docs/nodes/Annotations.md create mode 100644 docs/nodes/Blind Tip Estimate.md create mode 100644 docs/nodes/Color Map.md create mode 100644 docs/nodes/Colormap Adjust.md create mode 100644 docs/nodes/Coordinate Pair.md create mode 100644 docs/nodes/Coordinate.md create mode 100644 docs/nodes/Crop-Resize.md create mode 100644 docs/nodes/Cross Section.md create mode 100644 docs/nodes/Cross-Correlate.md create mode 100644 docs/nodes/Cursors.md create mode 100644 docs/nodes/Curvature.md create mode 100644 docs/nodes/Custom Convolution.md create mode 100644 docs/nodes/Draw Mask.md create mode 100644 docs/nodes/Edge Detect.md create mode 100644 docs/nodes/Entropy.md create mode 100644 docs/nodes/FFT 1D.md create mode 100644 docs/nodes/FFT 2D.md create mode 100644 docs/nodes/FFT Filter.md create mode 100644 docs/nodes/Facet Level.md create mode 100644 docs/nodes/Field Arithmetic.md create mode 100644 docs/nodes/Fix Zero.md create mode 100644 docs/nodes/Flip.md create mode 100644 docs/nodes/Float Slider.md create mode 100644 docs/nodes/Folder.md create mode 100644 docs/nodes/Font.md create mode 100644 docs/nodes/Fractal Dimension.md create mode 100644 docs/nodes/Gaussian Filter.md create mode 100644 docs/nodes/Gradient.md create mode 100644 docs/nodes/Grain Analysis.md create mode 100644 docs/nodes/Grain Distance Transform.md create mode 100644 docs/nodes/Grain Filter.md create mode 100644 docs/nodes/Histogram.md create mode 100644 docs/nodes/Image (Demo).md create mode 100644 docs/nodes/Image.md create mode 100644 docs/nodes/Inverse 2D FFT.md create mode 100644 docs/nodes/Kuwahara Filter.md create mode 100644 docs/nodes/Line Correction.md create mode 100644 docs/nodes/Local Contrast.md create mode 100644 docs/nodes/Markup.md create mode 100644 docs/nodes/Mask Invert.md create mode 100644 docs/nodes/Mask Morphology.md create mode 100644 docs/nodes/Mask Operations.md create mode 100644 docs/nodes/Median Filter.md create mode 100644 docs/nodes/Note.md create mode 100644 docs/nodes/Number.md create mode 100644 docs/nodes/PSDF.md create mode 100644 docs/nodes/Plane Level.md create mode 100644 docs/nodes/Polynomial Level.md create mode 100644 docs/nodes/Preview.md create mode 100644 docs/nodes/Print Table.md create mode 100644 docs/nodes/Radial Profile.md create mode 100644 docs/nodes/Resample.md create mode 100644 docs/nodes/Rotate.md create mode 100644 docs/nodes/Save Layers.md create mode 100644 docs/nodes/Save.md create mode 100644 docs/nodes/Scar Removal.md create mode 100644 docs/nodes/Slope Distribution.md create mode 100644 docs/nodes/Spot Removal.md create mode 100644 docs/nodes/Statistics.md create mode 100644 docs/nodes/Stats.md create mode 100644 docs/nodes/Template Match.md create mode 100644 docs/nodes/Text Note.md create mode 100644 docs/nodes/Threshold Mask.md create mode 100644 docs/nodes/Tip Deconvolution.md create mode 100644 docs/nodes/Tip Model.md create mode 100644 docs/nodes/Value Display.md create mode 100644 docs/nodes/Watershed Segmentation.md create mode 100644 docs/nodes/Wavelet Denoise.md diff --git a/backend/server.py b/backend/server.py index 2340181..7776b8b 100644 --- a/backend/server.py +++ b/backend/server.py @@ -271,6 +271,18 @@ def create_app( content_type="application/json", ) + async def get_node_doc(request: web.Request) -> web.Response: + name = request.rel_url.query.get("name", "").strip() + if not name: + raise web.HTTPBadRequest(reason="Missing 'name' query parameter") + docs_dir = project_root() / "docs" / "nodes" + # Try exact match first, then fall back to replacing " / " with "-" + candidates = [docs_dir / f"{name}.md", docs_dir / f"{name.replace(' / ', '-')}.md"] + for path in candidates: + if path.exists() and path.is_file(): + return web.Response(text=path.read_text(encoding="utf-8"), content_type="text/plain") + raise web.HTTPNotFound(reason=f"No documentation found for '{name}'") + async def list_files(request: web.Request) -> web.Response: session_id = require_session_id(request) input_path = session_input_dir(session_id) @@ -532,6 +544,7 @@ def create_app( app.router.add_post("/download", download_file) app.router.add_post("/save-workflow-png", save_workflow_png) app.router.add_get("/channels", get_channels) + app.router.add_get("/docs", get_node_doc) app.router.add_post("/prompt", submit_prompt) app.router.add_get("/ws", websocket_handler) diff --git a/docs/nodes/3D View.md b/docs/nodes/3D View.md new file mode 100644 index 0000000..e495992 --- /dev/null +++ b/docs/nodes/3D View.md @@ -0,0 +1,33 @@ +# 3D View + +Interactive 3D surface view of a DATA_FIELD. Use the mesh input for geometry and optionally a second map input for coloring. Drag to rotate, middle-drag to pan, and right-drag or scroll to zoom. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Height field used for mesh geometry | +| map_field | DATA_FIELD | No | Optional separate field used for surface coloring | +| colormap_map | COLORMAP | No | Colormap socket; overrides the colormap widget | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| mesh | MESH_MODEL | Triangulated 3D surface mesh | +| viewport | IMAGE | Snapshot of the 3D viewport | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| colormap | dropdown | auto | Colormap preset applied to the surface; hidden when colormap_map is connected | +| z_scale | FLOAT | 1.0 | Height exaggeration factor (range 0.1–10.0) | +| resolution | INT | 128 | Downsampling resolution for mesh generation (32–512) | +| make_solid | BOOLEAN | False | When enabled adds a flat base and side walls to close the mesh | + +## Limitations + +- Resolution is applied by uniform subsampling; fine features smaller than one mesh step may be lost. +- Non-square pixels emit a warning and the 3D surface represents physical scan area, not pixel grid. +- Very high resolution values (close to 512) with large fields may be slow to render. diff --git a/docs/nodes/ACF 1D.md b/docs/nodes/ACF 1D.md new file mode 100644 index 0000000..1f77e24 --- /dev/null +++ b/docs/nodes/ACF 1D.md @@ -0,0 +1,27 @@ +# ACF 1D + +Compute the one-dimensional autocorrelation function of a line profile. Only positive lags are output on the x-axis. The measurement table reports the dominant period from the first positive peak. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| profile | LINE | Yes | Input line profile | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| acf | LINE | Autocorrelation function (positive lags only) | +| measurement | RECORD_TABLE | Table with the dominant peak period | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| level | dropdown | mean | Pre-processing: subtract mean before correlation, or none | + +## Limitations + +- Only one-sided (positive lag) ACF is returned. +- Peak period detection finds only the first local maximum; multi-periodic signals report only the shortest detected period. diff --git a/docs/nodes/ACF 2D.md b/docs/nodes/ACF 2D.md new file mode 100644 index 0000000..f20e723 --- /dev/null +++ b/docs/nodes/ACF 2D.md @@ -0,0 +1,26 @@ +# ACF 2D + +Compute the two-dimensional autocorrelation function with Gwyddion-style mean or plane levelling before correlation. The output is centered on zero shift and uses default half-range extents. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input height field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| acf | DATA_FIELD | 2D autocorrelation field centered on zero shift | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| level | dropdown | mean | Pre-processing applied before correlation: mean subtraction, plane subtraction, or none | + +## Limitations + +- Output is not normalized to [−1, 1]; peak value equals the field variance. +- Plane levelling assumes a linear trend; strongly curved surfaces may not detrend correctly. diff --git a/docs/nodes/Angle Measure.md b/docs/nodes/Angle Measure.md new file mode 100644 index 0000000..8f0f9c0 --- /dev/null +++ b/docs/nodes/Angle Measure.md @@ -0,0 +1,28 @@ +# Angle Measure + +Measure the included angle between two draggable line segments over a DATA_FIELD or IMAGE. Drag either endpoint to change that arm, drag the middle joint to move the whole widget, and drag the angle label to reposition it. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| input | ANNOTATION_SOURCE (DATA_FIELD or IMAGE) | Yes | Background image or field to annotate | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| output | ANNOTATION_SOURCE | Input with the angle overlay attached | +| measurements | RECORD_TABLE | Angle in degrees, arm lengths, and arm/vertex coordinates | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| color | STRING (color picker) | #ff9800 | Overlay color for the angle arms and arc | +| stroke_width | FLOAT | 1.35 | Line thickness in display pixels (0.35–6.0) | + +## Limitations + +- Coordinates are stored as fractions of the image dimensions; physical units depend on the input field's calibration. +- Angle measurement is the included angle at the vertex, always in [0°, 180°]. diff --git a/docs/nodes/Annotations.md b/docs/nodes/Annotations.md new file mode 100644 index 0000000..caa12d2 --- /dev/null +++ b/docs/nodes/Annotations.md @@ -0,0 +1,30 @@ +# Annotations + +Attach optional publication-style annotations (scale bar, color-map legend) to a DATA_FIELD without flattening the raw data, or annotate an IMAGE that carries viewport metadata from View3D. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| input | ANNOTATION_SOURCE (DATA_FIELD or IMAGE) | Yes | Field or image to annotate | +| colormap_map | COLORMAP | No | Colormap socket; overrides the colormap widget | +| font | FONT | No | Font specification for annotation text | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| output | ANNOTATION_SOURCE | Input with annotation overlay attached | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| colormap | dropdown | auto | Colormap for the legend; hidden when colormap_map is connected | +| show_scale_bar | BOOLEAN | True | Render a physical scale bar | +| show_color_map | BOOLEAN | True | Render a color-map legend with min/mid/max values | +| text_size | FLOAT | 14.0 | Font size in points for annotation labels (6–96) | + +## Limitations + +- Scale bar and color-map legend require the input to carry physical dimension and unit metadata; plain images without metadata will emit a warning and the feature will be skipped. diff --git a/docs/nodes/Blind Tip Estimate.md b/docs/nodes/Blind Tip Estimate.md new file mode 100644 index 0000000..d5400ce --- /dev/null +++ b/docs/nodes/Blind Tip Estimate.md @@ -0,0 +1,32 @@ +# Blind Tip Estimate + +Blind tip estimation from a measured SPM image using the Villarrubia algorithm. Iteratively refines the tip shape using only the sharpest image features without knowing the true surface in advance. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Measured SPM surface image | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| tip | DATA_FIELD | Estimated tip shape (apex = maximum value, edges = 0) | +| certainty | IMAGE | Map marking surface pixels where the tip was in unambiguous single contact | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| n_pixels | INT | 33 | Tip grid size in pixels (odd values only, 3–129) | +| threshold | FLOAT | 0.0 | Noise floor in metres; increase if the estimated tip is unrealistically sharp | +| method | dropdown | partial | partial: uses local maxima only (faster, needs sharp isolated features); full: uses all points above morphological opening (slower, more robust) | +| use_edges | BOOLEAN | False | When enabled, also uses image edge pixels as refinement candidates | + +## Limitations + +- Requires sharp, isolated features on the surface for reliable results. +- Output tip has the same pixel size as the input field; tip deconvolution requires matching pixel sizes. +- Large tip sizes (n_pixels > ~65) or the full method on large images can be slow. +- Equivalent to gwy_tip_estimate_partial / gwy_tip_estimate_full + gwy_tip_cmap. diff --git a/docs/nodes/Color Map.md b/docs/nodes/Color Map.md new file mode 100644 index 0000000..0fc2b25 --- /dev/null +++ b/docs/nodes/Color Map.md @@ -0,0 +1,26 @@ +# Color Map + +Build a reusable colormap. Choose a preset from the built-in palette list, or create a custom gradient with min/max colours and any number of intermediate stops. + +## Inputs + +None. + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| colormap | COLORMAP | Colormap specification for use with other nodes | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| mode | dropdown | preset | Switch between preset and custom gradient modes | +| preset | dropdown | viridis | Built-in colormap preset name; visible when mode is preset | +| stops | STRING (colormap editor) | default gradient | JSON array of color stops for the custom gradient; visible when mode is custom | + +## Limitations + +- Custom colormaps must include at least a minimum and maximum color stop. +- Stops must be valid JSON; invalid JSON will raise an error. diff --git a/docs/nodes/Colormap Adjust.md b/docs/nodes/Colormap Adjust.md new file mode 100644 index 0000000..5663661 --- /dev/null +++ b/docs/nodes/Colormap Adjust.md @@ -0,0 +1,28 @@ +# Colormap Adjust + +Adjust how a DATA_FIELD maps into its colormap without changing the underlying data. offset and scale operate in normalized display coordinates; Auto resets to the full data range. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field whose colormap mapping is adjusted | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| field | DATA_FIELD | Field with updated display_offset and display_scale metadata | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| offset | FLOAT | 0.0 | Shift the colormap center in normalized units (−1 to 1) | +| scale | FLOAT | 1.0 | Zoom the colormap range (0.05–4.0); values below 1 stretch contrast | +| auto | BUTTON | — | Reset offset to 0 and scale to 1 (full data range) | + +## Limitations + +- Only the display mapping metadata is changed; raw data values are unaffected. +- Scale must be positive and finite; zero or negative values raise an error. diff --git a/docs/nodes/Coordinate Pair.md b/docs/nodes/Coordinate Pair.md new file mode 100644 index 0000000..8c3f69a --- /dev/null +++ b/docs/nodes/Coordinate Pair.md @@ -0,0 +1,24 @@ +# Coordinate Pair + +Combine two COORD values into a single COORDPAIR for use with nodes that accept a line segment or marker pair input. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| a | COORD | Yes | First coordinate (x, y) in fractional [0, 1] units | +| b | COORD | Yes | Second coordinate (x, y) in fractional [0, 1] units | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| coord_pair | COORDPAIR | Pair of coordinates as a single value | + +## Controls + +None. + +## Limitations + +- Coordinates are expected to be in the [0, 1] fractional range; values outside this range may produce unexpected results in downstream nodes. diff --git a/docs/nodes/Coordinate.md b/docs/nodes/Coordinate.md new file mode 100644 index 0000000..d310013 --- /dev/null +++ b/docs/nodes/Coordinate.md @@ -0,0 +1,24 @@ +# Coordinate + +Output a fractional (x, y) coordinate pair in [0, 1] for use with Cross Section, Cursors, or other nodes that accept a COORD input. + +## Inputs + +None. + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| point | COORD | Fractional (x, y) coordinate | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| x | FLOAT | 0.5 | Horizontal position as a fraction of image width (0 = left, 1 = right) | +| y | FLOAT | 0.5 | Vertical position as a fraction of image height (0 = top, 1 = bottom) | + +## Limitations + +- Values are clamped to [0, 1] by downstream nodes; this node does not enforce clamping itself. diff --git a/docs/nodes/Crop-Resize.md b/docs/nodes/Crop-Resize.md new file mode 100644 index 0000000..808811c --- /dev/null +++ b/docs/nodes/Crop-Resize.md @@ -0,0 +1,31 @@ +# Crop / Resize + +Crop a DATA_FIELD with a draggable rectangle defined by two corners, then optionally resize it. Cropping updates physical extents and offsets; resizing preserves the cropped physical size. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to crop | +| corner_a | COORD | No | Locks the top-left corner of the crop box | +| corner_b | COORD | No | Locks the bottom-right corner of the crop box | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| field | DATA_FIELD | Cropped (and optionally resampled) field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| target_width | INT | 0 | Output pixel width after resampling (0 = keep cropped width) | +| target_height | INT | 0 | Output pixel height after resampling (0 = keep cropped height) | +| interpolation | dropdown | bilinear | Resampling interpolation: bilinear, nearest, or bicubic | + +## Limitations + +- The crop region must have non-zero width and height; an error is raised otherwise. +- If only one of target_width or target_height is set, the other dimension is computed to preserve aspect ratio. +- Physical extents are scaled proportionally when resampling. diff --git a/docs/nodes/Cross Section.md b/docs/nodes/Cross Section.md new file mode 100644 index 0000000..aacd067 --- /dev/null +++ b/docs/nodes/Cross Section.md @@ -0,0 +1,29 @@ +# Cross Section + +Extract a cross-section height profile along a line between two draggable points on a DATA_FIELD. Equivalent to gwy_data_field_get_profile. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input height field | +| marker_pair | COORDPAIR | No | Locks both line endpoints from an external coordinate pair | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| profile | LINE | Height profile along the cross-section line | +| marker_pair | COORDPAIR | Current endpoint coordinates (passthrough for chaining) | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| extend | dropdown | none | none: profile between the two markers; to_edges: extend line to image borders | +| n_samples | INT | 0 | Number of sample points along the profile (0 = auto, one per pixel diagonal) | + +## Limitations + +- Profile is sampled using cubic spline interpolation (order 3); sharp step edges may show ringing. +- Physical x-axis of the output profile is the Euclidean distance in field xy units. diff --git a/docs/nodes/Cross-Correlate.md b/docs/nodes/Cross-Correlate.md new file mode 100644 index 0000000..08a5633 --- /dev/null +++ b/docs/nodes/Cross-Correlate.md @@ -0,0 +1,28 @@ +# Cross-Correlate + +Compute 2D cross-correlation between two fields. The correlation peak indicates the offset where the two fields best match. Useful for drift measurement and feature alignment. Equivalent to Gwyddion crosscor.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field_a | DATA_FIELD | Yes | First input field | +| field_b | DATA_FIELD | Yes | Second input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| correlation | DATA_FIELD | Cross-correlation map | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| mode | dropdown | same | Output size: full (Na+Nb−1), same (same as field_a), or valid (overlapping region only) | +| normalize | BOOLEAN | True | Normalize the result to [−1, 1] by dividing by the product of RMS values | + +## Limitations + +- Both fields must be of compatible numpy array types; very different sizes in full mode produce large outputs. +- Mean is subtracted before correlation; absolute offset information is lost. diff --git a/docs/nodes/Cursors.md b/docs/nodes/Cursors.md new file mode 100644 index 0000000..2aafc63 --- /dev/null +++ b/docs/nodes/Cursors.md @@ -0,0 +1,26 @@ +# Cursors + +Place two draggable cursors on a line plot or 2D field to measure positions and deltas. On lines it reports x/y positions and dx/dy; on fields it reports x/y/z at both markers plus dx/dy/dz. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| line | LINE or DATA_FIELD | Yes | Input line profile or 2D field | +| coord_pair | COORDPAIR | No | Locks both cursor positions from an external coordinate pair | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| measurement | RECORD_TABLE | Positions, deltas, and lengths at the two cursor locations | +| coord_pair | COORDPAIR | Current cursor positions (passthrough for chaining) | + +## Controls + +None (cursor positions are set by dragging in the preview panel). + +## Limitations + +- When a COORDPAIR is connected, the cursor positions are locked and cannot be dragged interactively. +- On 2D fields, z values are sampled with bilinear (order 1) interpolation. diff --git a/docs/nodes/Curvature.md b/docs/nodes/Curvature.md new file mode 100644 index 0000000..73205e5 --- /dev/null +++ b/docs/nodes/Curvature.md @@ -0,0 +1,31 @@ +# Curvature + +Fit a quadratic surface and report the overall principal curvature radii and directions, matching Gwyddion's curvature feature. The output annotation marks the principal cross-sections and the node also returns the two corresponding height profiles. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input topographic field | +| mask | IMAGE | No | Binary mask for including or excluding regions from the quadratic fit | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| output | ANNOTATION_SOURCE | Field with principal-axis lines overlaid | +| measurements | RECORD_TABLE | Curvature radii, principal-axis directions, center position and value | +| profile_a | LINE | Height profile along principal axis 1 | +| profile_b | LINE | Height profile along principal axis 2 | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| masking | dropdown | ignore | How to use the mask: ignore (fit all pixels), include (fit only masked pixels), or exclude (fit unmasked pixels) | + +## Limitations + +- Requires at least six usable pixels for the quadratic fit; fewer pixels produce no output. +- The fit assumes a globally smooth quadratic surface; locally rough or step-like surfaces give unreliable results. +- Requires compatible XY and Z physical units (e.g. both in metres). diff --git a/docs/nodes/Custom Convolution.md b/docs/nodes/Custom Convolution.md new file mode 100644 index 0000000..c98a0a5 --- /dev/null +++ b/docs/nodes/Custom Convolution.md @@ -0,0 +1,30 @@ +# Custom Convolution + +Apply a user-defined convolution kernel to a DATA_FIELD. Enter rows of space-separated numbers. Equivalent to Gwyddion convolution_filter.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to filter | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| result | DATA_FIELD | Filtered field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| kernel | STRING (multiline) | "0 -1 0\n-1 5 -1\n0 -1 0" | Kernel rows, each as space-separated numbers on a new line | +| normalize | BOOLEAN | True | Divide the result by the sum of absolute kernel values to preserve amplitude | +| boundary | dropdown | reflect | Boundary handling: reflect, nearest, or wrap | + +## Limitations + +- Kernel must be a rectangle: all rows must have the same number of values. +- Maximum kernel size is 51×51; larger kernels are rejected. +- Non-finite values in the kernel are rejected and the input is returned unchanged. +- For large kernels, direct convolution can be slow; consider FFT Filter instead. diff --git a/docs/nodes/Draw Mask.md b/docs/nodes/Draw Mask.md new file mode 100644 index 0000000..1a77dac --- /dev/null +++ b/docs/nodes/Draw Mask.md @@ -0,0 +1,28 @@ +# Draw Mask + +Paint a binary mask directly over an image preview. Pen size controls newly drawn strokes, the Clear Mask button removes all strokes, and invert flips the final binary output. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Background field whose dimensions define the mask size | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| mask | IMAGE | Binary mask image (painted regions = white, background = black, or inverted) | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| pen_size | INT | 12 | Brush diameter in pixels for newly drawn strokes (1–128) | +| invert | BOOLEAN | False | When enabled, swaps painted and unpainted regions | +| clear_mask | BUTTON | — | Clears all painted strokes | + +## Limitations + +- Strokes are stored as path data; very long painting sessions with many strokes may accumulate large state. +- The mask resolution matches the input field resolution and cannot be changed independently. diff --git a/docs/nodes/Edge Detect.md b/docs/nodes/Edge Detect.md new file mode 100644 index 0000000..482451c --- /dev/null +++ b/docs/nodes/Edge Detect.md @@ -0,0 +1,27 @@ +# Edge Detect + +Detect edges using Sobel, Prewitt, Laplacian, or Laplacian-of-Gaussian (LoG) operators. Equivalent to gwy_data_field_filter_sobel / gwy_data_field_filter_laplacian. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| edges | DATA_FIELD | Edge-detected output field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| method | dropdown | sobel | Edge detection operator: sobel, prewitt, laplacian, or log (Laplacian of Gaussian) | +| sigma | FLOAT | 1.0 | Gaussian smoothing sigma used only for the LoG operator (0.1–10.0) | + +## Limitations + +- sigma is ignored for sobel, prewitt, and laplacian methods. +- Sobel and Prewitt return gradient magnitude; Laplacian and LoG return signed second-derivative values. diff --git a/docs/nodes/Entropy.md b/docs/nodes/Entropy.md new file mode 100644 index 0000000..354e317 --- /dev/null +++ b/docs/nodes/Entropy.md @@ -0,0 +1,28 @@ +# Entropy + +Compute the Shannon entropy of the height or slope distribution. H = −Σ p·ln(p). Equivalent to Gwyddion entropy.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| entropy | FLOAT | Shannon entropy in nats | +| normalised_entropy | FLOAT | Entropy divided by ln(n_bins), in [0, 1] | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| mode | dropdown | height values | Compute entropy of height values or slope magnitude | +| n_bins | INT | 256 | Number of histogram bins for probability estimation (16–1024) | + +## Limitations + +- Entropy is sensitive to n_bins; very few bins underestimate entropy while very many bins overestimate it for small fields. +- Non-finite pixel values are removed before binning. diff --git a/docs/nodes/FFT 1D.md b/docs/nodes/FFT 1D.md new file mode 100644 index 0000000..754d851 --- /dev/null +++ b/docs/nodes/FFT 1D.md @@ -0,0 +1,26 @@ +# FFT 1D + +Compute the FFT amplitude spectrum of a line profile and identify the dominant period. The output x-axis is period (not frequency), sorted from short to long. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| profile | LINE | Yes | Input line profile | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| frequency_plot | LINE | FFT amplitude spectrum vs. period | +| max | RECORD_TABLE | Table with the peak period | + +## Controls + +None. + +## Limitations + +- The DC component is excluded from the output. +- Spectrum is one-sided (real FFT); the x-axis shows period, not frequency. +- No windowing is applied; spectral leakage may affect results on non-periodic inputs. diff --git a/docs/nodes/FFT 2D.md b/docs/nodes/FFT 2D.md new file mode 100644 index 0000000..dff4d14 --- /dev/null +++ b/docs/nodes/FFT 2D.md @@ -0,0 +1,30 @@ +# FFT 2D + +Compute the 2D FFT with optional windowing and mean/plane subtraction. Outputs log magnitude, magnitude, phase, and PSDF as separate channels. Equivalent to gwy_data_field_2dfft / gwy_data_field_2dpsdf. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input spatial-domain field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| log_magnitude | DATA_FIELD | log(1 + |F|) of the 2D FFT, centered on DC | +| magnitude | DATA_FIELD | |F| of the 2D FFT, centered on DC | +| phase | DATA_FIELD | Phase angle of the 2D FFT in radians, centered on DC | +| psdf | DATA_FIELD | 2D power spectral density function | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| windowing | dropdown | hann | Window function applied before FFT: hann, hamming, blackman, or none | +| level | dropdown | mean | Pre-processing: subtract mean, subtract plane, or none | + +## Limitations + +- Output fields are in the frequency domain; physical units on axes are spatial frequency (1/m). +- Phase output uses the raw FFT phase and is sensitive to field origin; it is most useful when paired with the Inverse 2D FFT node. diff --git a/docs/nodes/FFT Filter.md b/docs/nodes/FFT Filter.md new file mode 100644 index 0000000..26e0304 --- /dev/null +++ b/docs/nodes/FFT Filter.md @@ -0,0 +1,29 @@ +# FFT Filter + +Frequency-domain filtering of a line profile or 2D data field using a Butterworth transfer function. Connect a LINE for 1D filtering or a DATA_FIELD for 2D filtering; the output mirrors the input type. Equivalent to Gwyddion fft_filter_1d / fft_filter_2d. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| input | LINE or DATA_FIELD | Yes | Input line or field to filter | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| filtered | LINE or DATA_FIELD | Filtered output matching the input type | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| filter_type | dropdown | lowpass | Filter mode: lowpass, highpass, bandpass, or notch (band-reject) | +| cutoff | FLOAT | 0.1 | Lower cutoff frequency as a fraction of Nyquist (0.001–1.0) | +| cutoff_high | FLOAT | 0.4 | Upper cutoff for bandpass/notch modes (0.001–1.0) | +| order | INT | 2 | Butterworth filter order; higher values give steeper roll-off (1–10) | + +## Limitations + +- cutoff_high is only used for bandpass and notch modes. +- The filter is applied in the frequency domain via FFT; very short lines may show wrap-around artefacts. diff --git a/docs/nodes/Facet Level.md b/docs/nodes/Facet Level.md new file mode 100644 index 0000000..e94c190 --- /dev/null +++ b/docs/nodes/Facet Level.md @@ -0,0 +1,28 @@ +# Facet Level + +Level a field by iteratively finding the dominant local facet orientation and subtracting the corresponding plane. Matches Gwyddion's facet-level behaviour. Supports mask include/exclude selection. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input topographic field | +| mask | IMAGE | No | Binary mask for including or excluding facets from plane fitting | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| leveled | DATA_FIELD | Field with dominant facet plane subtracted | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| masking | dropdown | exclude | How to use the mask: exclude (ignore masked facets), include (use only masked facets), or ignore (use all facets) | + +## Limitations + +- Requires compatible XY and Z physical units for correct facet gradient estimation. +- Needs at least four valid facet cells; fields smaller than 2×2 pixels are not processed. +- Flat surfaces with no variation in facet orientation may not converge to a meaningful plane. diff --git a/docs/nodes/Field Arithmetic.md b/docs/nodes/Field Arithmetic.md new file mode 100644 index 0000000..a19c4e5 --- /dev/null +++ b/docs/nodes/Field Arithmetic.md @@ -0,0 +1,27 @@ +# Field Arithmetic + +Apply a point-wise arithmetic operation to two DATA_FIELDs of the same resolution. Equivalent to gwy_data_field_sum_fields / subtract_fields / multiply_fields / divide_fields / min_of_fields / max_of_fields / hypot_of_fields. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field_a | DATA_FIELD | Yes | First operand | +| field_b | DATA_FIELD | Yes | Second operand | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| result | DATA_FIELD | Result of the arithmetic operation (inherits metadata from field_a) | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| operation | dropdown | add | Element-wise operation: add, subtract, multiply, divide, min, max, or hypot (√(a²+b²)) | + +## Limitations + +- Both fields must have exactly the same pixel dimensions; mismatched sizes raise an error. +- divide may produce NaN or Inf pixels where field_b is zero. diff --git a/docs/nodes/Fix Zero.md b/docs/nodes/Fix Zero.md new file mode 100644 index 0000000..af34cc5 --- /dev/null +++ b/docs/nodes/Fix Zero.md @@ -0,0 +1,25 @@ +# Fix Zero + +Shift data so that the minimum, mean, or median value becomes zero. Equivalent to fix_zero in Gwyddion's level.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| zeroed | DATA_FIELD | Field shifted so the chosen reference is at zero | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| method | dropdown | min | Reference value to set to zero: min (lowest pixel), mean (average), or median | + +## Limitations + +- None. diff --git a/docs/nodes/Flip.md b/docs/nodes/Flip.md new file mode 100644 index 0000000..fbf31bc --- /dev/null +++ b/docs/nodes/Flip.md @@ -0,0 +1,25 @@ +# Flip + +Reflect a DATA_FIELD across the X axis (top/bottom) or Y axis (left/right). Physical extents are preserved, and stored markup overlays are mirrored with the data. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to flip | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| field | DATA_FIELD | Flipped field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| axis | dropdown | x | Flip axis: x flips top/bottom (vertical mirror), y flips left/right (horizontal mirror) | + +## Limitations + +- None. diff --git a/docs/nodes/Float Slider.md b/docs/nodes/Float Slider.md new file mode 100644 index 0000000..504dbb0 --- /dev/null +++ b/docs/nodes/Float Slider.md @@ -0,0 +1,25 @@ +# Float Slider + +Interactive float slider node. Set min and max bounds, then drag the slider to output a FLOAT value. + +## Inputs + +None. + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| value | FLOAT | Current slider value | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| min_value | FLOAT | 0.0 | Lower bound of the slider range | +| max_value | FLOAT | 1.0 | Upper bound of the slider range | +| value | FLOAT (slider) | 0.5 | Current value within [min_value, max_value] | + +## Limitations + +- None. diff --git a/docs/nodes/Folder.md b/docs/nodes/Folder.md new file mode 100644 index 0000000..ee36737 --- /dev/null +++ b/docs/nodes/Folder.md @@ -0,0 +1,24 @@ +# Folder + +Pick a folder and output its directory path plus one file socket per compatible file inside it. Supported files include common images, .npy/.npz arrays, and .gwy/.sxm/.ibw scans. + +## Inputs + +None. + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| directory | DIRECTORY | The selected folder path | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| folder | FOLDER_PICKER | "" | Path to the folder to list | + +## Limitations + +- Only files with supported extensions are listed; subdirectories and unsupported file types are ignored. +- The number of file output sockets is determined at load time by the folder contents. diff --git a/docs/nodes/Font.md b/docs/nodes/Font.md new file mode 100644 index 0000000..d6b9666 --- /dev/null +++ b/docs/nodes/Font.md @@ -0,0 +1,25 @@ +# Font + +Build a reusable font specification for annotation overlays. Choose a discovered system font, use the default fallback stack, or point to a custom font file. + +## Inputs + +None. + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| font | FONT | Font specification for use with Annotations and similar nodes | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| family | dropdown | System default | Font family name; includes discovered system fonts plus "Custom file" option | +| font_file | FILE_PICKER | "" | Path to a custom font file; visible only when family is set to "Custom file" | + +## Limitations + +- Custom font files must be in a format supported by the underlying font rendering library (e.g. TTF, OTF). +- System fonts are enumerated at startup; newly installed fonts require restarting the application. diff --git a/docs/nodes/Fractal Dimension.md b/docs/nodes/Fractal Dimension.md new file mode 100644 index 0000000..b82ac07 --- /dev/null +++ b/docs/nodes/Fractal Dimension.md @@ -0,0 +1,27 @@ +# Fractal Dimension + +Calculate the surface fractal dimension using Gwyddion's partitioning, cube counting, triangulation, power-spectrum, or HHCF methods. The in-node graph shows the log-log curve and allows dragging the fit range. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input surface field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| dimension | FLOAT | Estimated fractal dimension | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| method | dropdown | partitioning | Algorithm: partitioning, cube_counting, triangulation, psdf (power spectrum), or hhcf (structure function) | +| interpolation | dropdown | linear | Interpolation used when resampling the field to a square grid: linear, nearest, or cubic | + +## Limitations + +- The field is resampled to a square grid internally; highly anisotropic scan sizes may introduce interpolation artefacts. +- Fit range can be adjusted interactively on the log-log plot; the default range covers the full data. diff --git a/docs/nodes/Gaussian Filter.md b/docs/nodes/Gaussian Filter.md new file mode 100644 index 0000000..a3c7c55 --- /dev/null +++ b/docs/nodes/Gaussian Filter.md @@ -0,0 +1,26 @@ +# Gaussian Filter + +Apply a Gaussian blur to a DATA_FIELD. Equivalent to gwy_data_field_filter_gaussian. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to filter | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| filtered | DATA_FIELD | Gaussian-blurred field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| sigma | FLOAT | 1.0 | Standard deviation of the Gaussian kernel in pixels (0.01–50.0) | + +## Limitations + +- sigma is specified in pixels, not physical units; the effective physical blur depends on pixel size. +- Large sigma values (> ~20 pixels) are slow due to the large kernel. diff --git a/docs/nodes/Gradient.md b/docs/nodes/Gradient.md new file mode 100644 index 0000000..3982602 --- /dev/null +++ b/docs/nodes/Gradient.md @@ -0,0 +1,26 @@ +# Gradient + +Compute the spatial gradient using a Sobel operator. Outputs the gradient magnitude, x-component, y-component, or azimuthal direction. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| gradient | DATA_FIELD | Gradient output in the selected component | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| component | dropdown | magnitude | Output component: magnitude (√(gx²+gy²)), x (horizontal gradient), y (vertical gradient), or azimuth (angle in degrees) | + +## Limitations + +- Gradient components are in units of z_unit/xy_unit; azimuth is in degrees. +- Sobel operator uses a 3×3 kernel; sub-pixel features smaller than one pixel cannot be resolved. diff --git a/docs/nodes/Grain Analysis.md b/docs/nodes/Grain Analysis.md new file mode 100644 index 0000000..748503b --- /dev/null +++ b/docs/nodes/Grain Analysis.md @@ -0,0 +1,27 @@ +# Grain Analysis + +Label connected grain regions in a binary mask and compute per-grain statistics: area, equivalent diameter, mean/max height, bounding box. Equivalent to Gwyddion's grain statistics tools. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Height field providing z values for per-grain statistics | +| mask | IMAGE | Yes | Binary mask defining grain regions (white = grain) | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| grain_stats | DATA_TABLE | Per-grain table with area, equivalent diameter, mean/max height, bounding box, and centroid | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| min_size | INT | 10 | Minimum grain area in pixels; smaller connected regions are ignored (1–100000) | + +## Limitations + +- Grain detection uses 2D connected-component labeling on the binary mask; the field and mask must have the same pixel dimensions. +- Physical area and diameter values require the field to carry valid physical calibration (xreal, yreal, si_unit_xy). diff --git a/docs/nodes/Grain Distance Transform.md b/docs/nodes/Grain Distance Transform.md new file mode 100644 index 0000000..9816818 --- /dev/null +++ b/docs/nodes/Grain Distance Transform.md @@ -0,0 +1,29 @@ +# Grain Distance Transform + +Compute the mask distance transform using Gwyddion-style interior, exterior, or signed output. Supports Euclidean, city-block, chessboard, and octagonal distance variants, with optional image-boundary handling matching mask_edt. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Field providing physical calibration for the output | +| mask | IMAGE | Yes | Binary mask to compute distances for | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| distance | DATA_FIELD | Distance transform field in physical units | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| distance_type | dropdown | euclidean | Distance metric: euclidean, cityblock, chess (Chebyshev), octagonal48, octagonal84, or octagonal | +| output_type | dropdown | interior | Output mode: interior (distances inside grains), exterior (distances outside), or signed (positive inside, negative outside) | +| from_border | BOOLEAN | True | When enabled, image borders are treated as mask boundaries | + +## Limitations + +- Output distances are in physical xy units derived from the field calibration. +- The mask must have the same pixel dimensions as the field. diff --git a/docs/nodes/Grain Filter.md b/docs/nodes/Grain Filter.md new file mode 100644 index 0000000..ce91567 --- /dev/null +++ b/docs/nodes/Grain Filter.md @@ -0,0 +1,28 @@ +# Grain Filter + +Remove grains from a binary mask based on size and border contact. Equivalent to Gwyddion's grain_filter module (grain_filter.c). + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| mask | IMAGE | Yes | Binary mask containing grain regions to filter | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| filtered_mask | IMAGE | Binary mask with unwanted grains removed | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| min_area | INT | 10 | Discard grains with fewer pixels than this value (0 = no lower limit) | +| max_area | INT | 0 | Discard grains with more pixels than this value (0 = no upper limit) | +| remove_border | BOOLEAN | False | Discard any grain that touches the image edge | + +## Limitations + +- Grain detection uses 4-connected or 8-connected labeling; the exact connectivity is determined by the implementation. +- min_area and max_area are in pixels, not physical units. diff --git a/docs/nodes/Histogram.md b/docs/nodes/Histogram.md new file mode 100644 index 0000000..239bd2f --- /dev/null +++ b/docs/nodes/Histogram.md @@ -0,0 +1,28 @@ +# Histogram + +Compute the height distribution histogram (DH). Use log scale to reveal small peaks next to a dominant background. Outputs marker measurements while showing the histogram interactively in-node. Equivalent to gwy_data_field_dh. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| measurements | RECORD_TABLE | Measurements at the two cursor markers (x/y positions and dx/dy) | +| marker_pair | COORDPAIR | Current cursor marker positions | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| n_bins | INT | 256 | Number of histogram bins (10–1000) | +| y_scale | dropdown | linear | Y-axis scale: linear or log | + +## Limitations + +- Cursor positions are stored as fractions of the histogram range and are set interactively. +- Log scale displays bins as log(count); bins with zero count appear as the minimum log value. diff --git a/docs/nodes/Image (Demo).md b/docs/nodes/Image (Demo).md new file mode 100644 index 0000000..32a2002 --- /dev/null +++ b/docs/nodes/Image (Demo).md @@ -0,0 +1,25 @@ +# Image (Demo) + +Load a bundled demo file so you can try the application without providing your own data. The file list is populated from the built-in demo directory at startup. + +## Inputs + +None. + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| field | DATA_FIELD | Loaded demo field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| name | dropdown | (first demo file) | Name of the bundled demo file to load | +| colormap | dropdown | viridis | Colormap applied to the field; hidden when colormap_map is connected | + +## Limitations + +- Only files present in the bundled demo directory are available; custom files cannot be added here. +- If no demo files are found, the dropdown shows "(no demo files found)". diff --git a/docs/nodes/Image.md b/docs/nodes/Image.md new file mode 100644 index 0000000..0a2d58c --- /dev/null +++ b/docs/nodes/Image.md @@ -0,0 +1,28 @@ +# Image + +Load any supported file. SPM formats (.gwy, .sxm, .ibw) and HDF5 (.h5, .hdf5) provide calibrated dimensions; each channel gets its own output. Images (.png, .tiff, .jpg) and arrays (.npy, .npz) are loaded as uncalibrated fields. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| path | FILE_PATH | No | File path input from a Folder node or other path source; overrides the filename widget | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| path | FILE_PATH | Resolved absolute file path | +| field | DATA_FIELD | Loaded data field (one per channel for multi-channel formats) | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| filename | FILE_PICKER | "" | Path to the file to load; hidden when path input is connected | +| colormap | dropdown | viridis | Colormap applied to the loaded field; hidden when colormap_map is connected | + +## Limitations + +- Uncalibrated formats (images, arrays) emit a warning and produce fields without physical dimensions. +- Multi-channel files (e.g. .gwy with multiple data channels) produce one DATA_FIELD output socket per channel; the socket count is determined at load time. diff --git a/docs/nodes/Inverse 2D FFT.md b/docs/nodes/Inverse 2D FFT.md new file mode 100644 index 0000000..bb33b26 --- /dev/null +++ b/docs/nodes/Inverse 2D FFT.md @@ -0,0 +1,28 @@ +# Inverse 2D FFT + +Reconstruct a spatial-domain image from a 2D frequency spectrum. For exact reconstruction, connect magnitude/phase (or log magnitude/phase, or PSDF/phase) from the 2D FFT node. If phase is omitted, zero phase is assumed. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| spectrum | DATA_FIELD | Yes | Frequency-domain field (output of FFT 2D) | +| phase | DATA_FIELD | No | Phase field; if omitted, zero phase is used | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| image | DATA_FIELD | Reconstructed spatial-domain field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| representation | dropdown | magnitude | How to interpret the spectrum input: magnitude, log_magnitude, or psdf | + +## Limitations + +- The spectrum input must be a frequency-domain DATA_FIELD; connecting a spatial-domain field raises an error. +- If phase is connected, it must also be a frequency-domain field with the same shape as the spectrum. +- Reconstruction from PSDF (which discards phase) assumes zero phase and produces a symmetric result. diff --git a/docs/nodes/Kuwahara Filter.md b/docs/nodes/Kuwahara Filter.md new file mode 100644 index 0000000..92314ec --- /dev/null +++ b/docs/nodes/Kuwahara Filter.md @@ -0,0 +1,26 @@ +# Kuwahara Filter + +Edge-preserving smoothing using Kuwahara's minimum-variance quadrant method. Unlike Gaussian blur, sharp boundaries are preserved. Equivalent to Gwyddion's Kuwahara filter. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to filter | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| filtered | DATA_FIELD | Smoothed field with preserved edges | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| iterations | INT | 1 | Number of times the 5×5 Kuwahara pass is applied (1–20) | + +## Limitations + +- The kernel is fixed at 5×5 pixels; coarser smoothing requires more iterations. +- Multiple iterations increase processing time proportionally; 20 iterations on a large field may be slow. diff --git a/docs/nodes/Line Correction.md b/docs/nodes/Line Correction.md new file mode 100644 index 0000000..16f7f5e --- /dev/null +++ b/docs/nodes/Line Correction.md @@ -0,0 +1,33 @@ +# Line Correction + +Correct scan-line mismatches using Gwyddion-derived row alignment methods. Supports median and trimmed row alignment, difference-based alignment, polynomial row leveling, and step-line correction from Gwyddion's linecorrect/linematch modules. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field with scan-line artifacts | +| mask | IMAGE | No | Binary mask to include or exclude regions during correction | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| corrected | DATA_FIELD | Field with scan-line offsets removed | +| background | DATA_FIELD | Estimated per-line background that was subtracted | +| row_shifts | LINE | Per-row shift values applied during correction | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| method | dropdown | median | Alignment method: median, median_diff, trimmed_mean, trimmed_diff, polynomial, or step | +| direction | dropdown | horizontal | Direction of scan lines to correct: horizontal or vertical | +| masking | dropdown | ignore | How to use the mask: ignore, include (correct using masked rows only), or exclude | +| trim_fraction | FLOAT | 0.05 | Fraction of extreme values to trim; visible only for trimmed_mean and trimmed_diff methods (0–0.5) | +| polynomial_degree | INT | 1 | Polynomial degree for the polynomial method (0–5); visible only for polynomial method | + +## Limitations + +- The step method is designed for step-like scan artifacts and may over-correct smooth surfaces. +- Mask shape must match the field shape if a mask is connected. diff --git a/docs/nodes/Local Contrast.md b/docs/nodes/Local Contrast.md new file mode 100644 index 0000000..b564b50 --- /dev/null +++ b/docs/nodes/Local Contrast.md @@ -0,0 +1,27 @@ +# Local Contrast + +Expand the local dynamic range at each pixel to reveal fine surface features that are hidden by global contrast range. Equivalent to Gwyddion local_contrast.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| result | DATA_FIELD | Field with enhanced local contrast | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| kernel_size | INT | 10 | Size of the local neighbourhood window in pixels (2–100) | +| weight | FLOAT | 0.5 | Blend weight between original and full-contrast output (0 = original, 1 = full local contrast; 0–1) | + +## Limitations + +- Large kernel sizes are slow; values above ~50 pixels may be noticeably slow on large fields. +- The enhancement is purely a display-contrast operation; it changes the underlying data values. diff --git a/docs/nodes/Markup.md b/docs/nodes/Markup.md new file mode 100644 index 0000000..6da7108 --- /dev/null +++ b/docs/nodes/Markup.md @@ -0,0 +1,29 @@ +# Markup + +Draw simple vector shapes (lines, rectangles, circles, arrows) over a DATA_FIELD or IMAGE without flattening the raw data. Shapes are drawn interactively in the preview panel. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| input | ANNOTATION_SOURCE (DATA_FIELD or IMAGE) | Yes | Background image or field to annotate | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| output | ANNOTATION_SOURCE | Input with vector markup overlay attached | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| shape | dropdown | arrow | Shape type to draw next: line, rectangle, circle, or arrow | +| stroke_color | STRING (color picker) | #ff0000 | Color for newly drawn shapes | +| stroke_width | INT | 3 | Line thickness in display pixels for newly drawn shapes (1–64) | +| clear_shapes | BUTTON | — | Remove all drawn shapes | + +## Limitations + +- Shapes are stored as fractional coordinates; physical positions depend on field calibration. +- Individual shape properties (color, width) cannot be changed after drawing; use Clear Shapes to start over. diff --git a/docs/nodes/Mask Invert.md b/docs/nodes/Mask Invert.md new file mode 100644 index 0000000..944fc1c --- /dev/null +++ b/docs/nodes/Mask Invert.md @@ -0,0 +1,24 @@ +# Mask Invert + +Invert a binary mask — swap masked and unmasked regions. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| mask | IMAGE | Yes | Binary mask to invert | +| field | DATA_FIELD | No | Optional field for preview background display | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| mask | IMAGE | Inverted binary mask | + +## Controls + +None. + +## Limitations + +- None. diff --git a/docs/nodes/Mask Morphology.md b/docs/nodes/Mask Morphology.md new file mode 100644 index 0000000..6e27780 --- /dev/null +++ b/docs/nodes/Mask Morphology.md @@ -0,0 +1,28 @@ +# Mask Morphology + +Apply morphological operations to a binary mask. Dilate expands regions, erode shrinks them, open (erode then dilate) removes small islands, and close (dilate then erode) fills small holes. Equivalent to Gwyddion's mask_morph.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| mask | IMAGE | Yes | Binary mask to process | +| field | DATA_FIELD | No | Optional field for preview background display | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| mask | IMAGE | Morphologically processed binary mask | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| operation | dropdown | dilate | Morphological operation: dilate, erode, open, or close | +| radius | INT | 1 | Structuring element radius in pixels (1–50) | +| shape | dropdown | disk | Structuring element shape: disk or square | + +## Limitations + +- Large radius values (> ~20 pixels) may be slow on large masks. diff --git a/docs/nodes/Mask Operations.md b/docs/nodes/Mask Operations.md new file mode 100644 index 0000000..ab5d7fe --- /dev/null +++ b/docs/nodes/Mask Operations.md @@ -0,0 +1,26 @@ +# Mask Operations + +Apply boolean logic to two binary masks. Supports AND, OR, XOR, NAND, NOR, XNOR, directional subtraction (A minus B, B minus A), implication, pass-through, and constant true/false outputs. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| mask_a | IMAGE | Yes | First binary mask operand | +| mask_b | IMAGE | Yes | Second binary mask operand | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| mask | IMAGE | Result of the boolean operation | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| operation | dropdown | and | Boolean operation: and, or, xor, xnor, nand, nor, a_minus_b, b_minus_a, a, b, not_a, not_b, a_implies_b, b_implies_a, false, true | + +## Limitations + +- Both masks must have the same pixel dimensions. diff --git a/docs/nodes/Median Filter.md b/docs/nodes/Median Filter.md new file mode 100644 index 0000000..2e64c2a --- /dev/null +++ b/docs/nodes/Median Filter.md @@ -0,0 +1,26 @@ +# Median Filter + +Apply a median filter to a DATA_FIELD. Equivalent to gwy_data_field_filter_median. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to filter | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| filtered | DATA_FIELD | Median-filtered field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| size | INT | 3 | Kernel size (side length) in pixels; odd values only (1–21) | + +## Limitations + +- The median filter is applied with a square kernel; non-square (e.g. rectangular) kernels are not supported. +- Large kernel sizes are significantly slower than the Gaussian filter for the same smoothing extent. diff --git a/docs/nodes/Note.md b/docs/nodes/Note.md new file mode 100644 index 0000000..ce0472b --- /dev/null +++ b/docs/nodes/Note.md @@ -0,0 +1,26 @@ +# Note + +Read the Note metadata from an .ibw (Igor binary wave) file and display all entries as a table of key/value pairs. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| path | FILE_PATH | No | File path from a Folder or other path node; overrides the filename widget | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| note | DATA_TABLE | Key/value table of all metadata entries from the IBW note | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| filename | FILE_PICKER | "" | Path to the .ibw file; hidden when path input is connected | + +## Limitations + +- Only .ibw files are supported; other formats raise an error. +- If the .ibw note section is empty or missing, an error is raised. diff --git a/docs/nodes/Number.md b/docs/nodes/Number.md new file mode 100644 index 0000000..34e7f0b --- /dev/null +++ b/docs/nodes/Number.md @@ -0,0 +1,23 @@ +# Number + +Output a fixed numeric value. When connected to FLOAT inputs the exact value is used; INT inputs round to the nearest integer at execution time. + +## Inputs + +None. + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| value | FLOAT | The numeric value set by the widget | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| value | FLOAT | 0.0 | The numeric value to output | + +## Limitations + +- None. diff --git a/docs/nodes/PSDF.md b/docs/nodes/PSDF.md new file mode 100644 index 0000000..1f76db2 --- /dev/null +++ b/docs/nodes/PSDF.md @@ -0,0 +1,27 @@ +# PSDF + +Compute the two-dimensional power spectral density function with Gwyddion-style window RMS compensation and centered zero frequency. Equivalent to psdf2d / gwy_data_field_2dpsdf. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input spatial-domain field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| psdf | DATA_FIELD | 2D power spectral density, centered on DC | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| windowing | dropdown | hann | Window function applied before FFT to reduce spectral leakage: hann, hamming, blackman, or none | +| level | dropdown | mean | Pre-processing: subtract mean, subtract plane, or none | + +## Limitations + +- Output is in the frequency domain; physical units on axes are spatial frequency (1/m) and PSDF units are z_unit²·m². +- Window RMS compensation is applied to normalize the spectral density consistently across window choices. diff --git a/docs/nodes/Plane Level.md b/docs/nodes/Plane Level.md new file mode 100644 index 0000000..10338f0 --- /dev/null +++ b/docs/nodes/Plane Level.md @@ -0,0 +1,26 @@ +# Plane Level + +Fit and subtract a least-squares plane from the data. Supports include/exclude mask fitting for flattening around features, similar to masked plane fitting workflows in Gwyddion. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to level | +| mask | IMAGE | No | Binary mask for selecting which pixels to include in the plane fit | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| leveled | DATA_FIELD | Field with the fitted plane subtracted | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| masking | dropdown | ignore | How to use the mask: ignore (use all pixels), include (fit plane to masked pixels only), or exclude (exclude masked pixels from fit) | + +## Limitations + +- When masking is include or exclude, the unmasked/masked region must contain at least three non-collinear pixels to fit a plane. diff --git a/docs/nodes/Polynomial Level.md b/docs/nodes/Polynomial Level.md new file mode 100644 index 0000000..8612a6d --- /dev/null +++ b/docs/nodes/Polynomial Level.md @@ -0,0 +1,28 @@ +# Polynomial Level + +Fit and subtract a polynomial background of given degree in x and y. Equivalent to gwy_data_field_fit_polynom. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to level | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| leveled | DATA_FIELD | Field with polynomial background subtracted | +| background | DATA_FIELD | The fitted polynomial background surface | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| degree_x | INT | 2 | Polynomial degree in the x direction (0–5) | +| degree_y | INT | 2 | Polynomial degree in the y direction (0–5) | + +## Limitations + +- High polynomial degrees (> 4) may overfit and introduce artificial long-range modulation. +- No masking support; all pixels contribute equally to the fit. diff --git a/docs/nodes/Preview.md b/docs/nodes/Preview.md new file mode 100644 index 0000000..9403650 --- /dev/null +++ b/docs/nodes/Preview.md @@ -0,0 +1,25 @@ +# Preview + +Display an IMAGE or DATA_FIELD as a coloured thumbnail in the node panel. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| input | ANNOTATION_SOURCE (DATA_FIELD or IMAGE) | No | Field or image to display | +| colormap_map | COLORMAP | No | Colormap socket; overrides the colormap widget | + +## Outputs + +None. + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| colormap | dropdown | auto | Colormap used when rendering a DATA_FIELD or grayscale IMAGE; hidden when colormap_map is connected | + +## Limitations + +- When no input is connected, an error is raised at execution time. +- Connecting a FILE_PATH socket (instead of DATA_FIELD) raises a type error; ensure the correct socket is connected. diff --git a/docs/nodes/Print Table.md b/docs/nodes/Print Table.md new file mode 100644 index 0000000..2eace36 --- /dev/null +++ b/docs/nodes/Print Table.md @@ -0,0 +1,21 @@ +# Print Table + +Send a measurement or record table to the browser as a WebSocket message for display. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| table | RECORD_TABLE or DATA_TABLE | Yes | Table to display | + +## Outputs + +None. + +## Controls + +None. + +## Limitations + +- None. diff --git a/docs/nodes/Radial Profile.md b/docs/nodes/Radial Profile.md new file mode 100644 index 0000000..06cba46 --- /dev/null +++ b/docs/nodes/Radial Profile.md @@ -0,0 +1,28 @@ +# Radial Profile + +Compute the azimuthally averaged radial profile from a centre point. The output x-axis is radius in physical xy units. Equivalent to gwy_data_field_angular_average used by Gwyddion's Radial Profile tool. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| profile | LINE | Radially averaged profile vs. radius | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| cx | FLOAT | 0.5 | Centre x position as a fraction of field width (0 = left, 1 = right) | +| cy | FLOAT | 0.5 | Centre y position as a fraction of field height (0 = top, 1 = bottom) | +| n_bins | INT | 128 | Number of radial bins (4–4096) | + +## Limitations + +- Pixels are assigned to radial bins by Euclidean distance; bins near the centre contain fewer pixels and may be noisier. +- Physical x-axis units come from the field's si_unit_xy; uncalibrated fields produce pixel-unit radii. diff --git a/docs/nodes/Resample.md b/docs/nodes/Resample.md new file mode 100644 index 0000000..0769327 --- /dev/null +++ b/docs/nodes/Resample.md @@ -0,0 +1,28 @@ +# Resample + +Resample a DATA_FIELD to a new pixel resolution while preserving physical dimensions. Physical size (xreal, yreal) is unchanged; pixel size dx/dy scales accordingly. Equivalent to gwy_data_field_new_resampled. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to resample | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| resampled | DATA_FIELD | Resampled field at the new resolution | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| width | INT | 256 | Output pixel width (2–16384) | +| height | INT | 256 | Output pixel height (2–16384) | +| interpolation | dropdown | linear | Interpolation method: linear, cubic, or nearest | + +## Limitations + +- Physical dimensions are preserved; upsampling does not add new information. +- Very large output sizes (e.g. 16384×16384) require substantial memory. diff --git a/docs/nodes/Rotate.md b/docs/nodes/Rotate.md new file mode 100644 index 0000000..6f6b5e4 --- /dev/null +++ b/docs/nodes/Rotate.md @@ -0,0 +1,28 @@ +# Rotate + +Rotate a DATA_FIELD counterclockwise by an angle in degrees. Optionally expand the canvas to keep the full rotated field while preserving the field center. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to rotate | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| field | DATA_FIELD | Rotated field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| angle | FLOAT | 90.0 | Rotation angle in degrees, counterclockwise (−360 to 360) | +| interpolation | dropdown | bilinear | Interpolation method for resampling: bilinear, nearest, or bicubic | +| expand_canvas | BOOLEAN | True | When True, canvas is expanded to contain the full rotated image; when False, canvas is clipped to original size | + +## Limitations + +- Rotation by angles other than multiples of 90° introduces interpolation artefacts. +- expand_canvas may produce fields with non-square pixel sizes for arbitrary angles. diff --git a/docs/nodes/Save Layers.md b/docs/nodes/Save Layers.md new file mode 100644 index 0000000..8ada90d --- /dev/null +++ b/docs/nodes/Save Layers.md @@ -0,0 +1,29 @@ +# Save Layers + +Save one or more image/field layers to a single file. Each layer input accepts either a DATA_FIELD or an IMAGE, including annotated images. Use this for composing multi-channel stacks. Does not auto-run; click Save to write. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| directory | DIRECTORY | No | Optional directory path from a Folder node; overrides the directory_path widget | +| field_0 … field_N | DATA_FIELD, IMAGE, or ANNOTATION_SOURCE | No | Layer inputs; a new slot appears as each one is filled | + +## Outputs + +None. + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| filename | STRING | "" | Output filename (without directory); used together with directory_path | +| directory_path | FOLDER_PICKER | "" | Output directory; hidden when directory input is connected | +| format | dropdown | TIFF | Output format: TIFF (multi-page, float32 for fields) or NPZ (named arrays) | +| layer_name_0 … layer_name_N | STRING | "" | Optional name for each layer; used as TIFF page descriptions or NPZ array keys | + +## Limitations + +- At least one layer must be connected; an error is raised otherwise. +- TIFF writes DATA_FIELD layers as float32; IMAGE layers are written as uint8. +- NPZ keys are sanitized to valid Python identifiers; duplicate names are made unique with numeric suffixes. diff --git a/docs/nodes/Save.md b/docs/nodes/Save.md new file mode 100644 index 0000000..7bf85e3 --- /dev/null +++ b/docs/nodes/Save.md @@ -0,0 +1,28 @@ +# Save + +Save a single value to disk. Supports fields, images, lines, tables, scalars, and 3D meshes. Does not auto-run; click Save to write. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| value | DATA_FIELD, IMAGE, ANNOTATION_SOURCE, LINE, RECORD_TABLE, DATA_TABLE, MESH_MODEL, or FLOAT | Yes | Value to save | +| directory | DIRECTORY | No | Optional directory path from a Folder node; overrides the directory_path widget | + +## Outputs + +None. + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| filename | STRING | "" | Output filename | +| directory_path | FOLDER_PICKER | "" | Output directory; hidden when directory input is connected | +| format | STRING (context-dependent) | TIFF | Output format; available choices depend on the connected input type | +| plot_title | STRING | "" | Optional title for line plots saved as PNG or TIFF | + +## Limitations + +- Available formats per input type: DATA_FIELD → TIFF, PNG, NPZ; IMAGE → PNG, TIFF, NPZ; LINE → PNG, TIFF, CSV, NPZ, JSON; RECORD_TABLE/DATA_TABLE → CSV, JSON; FLOAT → TXT, JSON; MESH_MODEL → OBJ, STL. +- divide-by-zero or NaN values in fields are preserved in TIFF/NPZ but may not render correctly in PNG. diff --git a/docs/nodes/Scar Removal.md b/docs/nodes/Scar Removal.md new file mode 100644 index 0000000..7941b28 --- /dev/null +++ b/docs/nodes/Scar Removal.md @@ -0,0 +1,31 @@ +# Scar Removal + +Detect and remove horizontal scan scars using Gwyddion-derived scar marking thresholds, then interpolate over the detected mask with a Laplace-style inpaint. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field with scan scars | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| corrected | DATA_FIELD | Field with scars removed and interpolated | +| scar_mask | IMAGE | Binary mask of the detected scar pixels | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| scar_type | dropdown | both | Which scar polarity to detect: both, positive (bright), or negative (dark) | +| threshold_high | FLOAT | 0.666 | High threshold relative to local RMS for strong scar detection (0–2) | +| threshold_low | FLOAT | 0.25 | Low threshold for extending already-detected scars (0–2) | +| min_length | INT | 16 | Minimum horizontal run length in pixels to classify as a scar (1–4096) | +| max_width | INT | 4 | Maximum vertical width in pixels for a scar candidate (1–32) | + +## Limitations + +- Designed for horizontal (fast-scan) scars only; vertical scars are not detected. +- Aggressive thresholds may remove legitimate surface features that resemble scars. diff --git a/docs/nodes/Slope Distribution.md b/docs/nodes/Slope Distribution.md new file mode 100644 index 0000000..0b2f9cb --- /dev/null +++ b/docs/nodes/Slope Distribution.md @@ -0,0 +1,27 @@ +# Slope Distribution + +Compute the angular slope distribution of a DATA_FIELD surface. Equivalent to Gwyddion's slope_dist module (slope_dist.c). + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input surface field | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| distribution | LINE | Slope distribution histogram | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| distribution | dropdown | theta | Distribution type: theta (inclination angle, probability density in 1/deg), phi (azimuthal direction, weighted by slope², 0–360°), or gradient (slope magnitude, probability density in 1/(z/xy)) | +| n_bins | INT | 90 | Number of histogram bins (10–1000) | + +## Limitations + +- The gradient magnitude distribution requires valid physical xy and z units for correct normalization. +- phi distribution is weighted by slope squared; flat surfaces with near-zero slopes produce a noisy azimuthal distribution. diff --git a/docs/nodes/Spot Removal.md b/docs/nodes/Spot Removal.md new file mode 100644 index 0000000..05960d1 --- /dev/null +++ b/docs/nodes/Spot Removal.md @@ -0,0 +1,28 @@ +# Spot Removal + +Fill defect pixels (hot pixels, dropouts, scan artifacts) by interpolation. The mask defines defect locations. Equivalent to Gwyddion spotremove.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field with defect pixels | +| mask | IMAGE | No | Binary mask marking defect pixel locations; if omitted, no inpainting is performed | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| result | DATA_FIELD | Field with defect pixels replaced by interpolated values | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| method | dropdown | laplace | Inpainting method: laplace (smooth Laplace equation solution), mean (local mean), or zero | +| max_iter | INT | 100 | Maximum number of iterations for the Laplace solver (1–2000) | + +## Limitations + +- Large masked regions may not converge fully within max_iter iterations using the Laplace method. +- The mean method uses a simple neighbourhood average and may leave visible discontinuities at large defect clusters. diff --git a/docs/nodes/Statistics.md b/docs/nodes/Statistics.md new file mode 100644 index 0000000..7d4943c --- /dev/null +++ b/docs/nodes/Statistics.md @@ -0,0 +1,23 @@ +# Statistics + +Compute basic surface statistics: min, max, mean, RMS roughness, median, and skewness. Equivalent to gwy_data_field_get_min/max/avg/rms. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to analyze | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| stats | RECORD_TABLE | Table of statistical quantities with values and units | + +## Controls + +None. + +## Limitations + +- None. diff --git a/docs/nodes/Stats.md b/docs/nodes/Stats.md new file mode 100644 index 0000000..4a753ba --- /dev/null +++ b/docs/nodes/Stats.md @@ -0,0 +1,27 @@ +# Stats + +Compute a contextual scalar statistic from a LINE, record table, DATA_FIELD, or IMAGE. The available operations adapt to the connected input type. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| input | DATA_FIELD, IMAGE, LINE, or DATA_TABLE | Yes | Input to compute statistics on | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| value | FLOAT | Computed scalar statistic | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| operation | STRING (context-dependent dropdown) | mean | Statistical operation; available choices depend on the input type | +| column | STRING | value | Column name for DATA_TABLE inputs; visible only when a DATA_TABLE is connected | + +## Limitations + +- Available operations vary by input type; connecting a different type changes the available options. +- For DATA_TABLE inputs, the column must match an existing column name; an error is raised otherwise. diff --git a/docs/nodes/Template Match.md b/docs/nodes/Template Match.md new file mode 100644 index 0000000..ce282c4 --- /dev/null +++ b/docs/nodes/Template Match.md @@ -0,0 +1,28 @@ +# Template Match + +Find a template pattern within a larger data field using normalised cross-correlation. The score output shows match quality (1 = perfect match). Equivalent to Gwyddion maskcor.c. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| image | DATA_FIELD | Yes | Larger field to search in | +| template | DATA_FIELD | Yes | Smaller template pattern to find | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| score | DATA_FIELD | Normalized cross-correlation score map | +| detections | IMAGE | Binary mask marking positions above the threshold | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| threshold | FLOAT | 0.8 | Minimum correlation score to mark as a detection (0.0–1.0) | + +## Limitations + +- The template must be smaller than the image; equal or larger sizes are not supported. +- Normalized cross-correlation assumes uniform background; strong global gradients reduce detection accuracy. diff --git a/docs/nodes/Text Note.md b/docs/nodes/Text Note.md new file mode 100644 index 0000000..cc581e0 --- /dev/null +++ b/docs/nodes/Text Note.md @@ -0,0 +1,22 @@ +# Text Note + +A floating text card for annotating workflows. Supports Markdown formatting. Does not process data and has no outputs. + +## Inputs + +None. + +## Outputs + +None. + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| text | STRING (multiline) | "# Guide\n\nDouble-click to edit this note.\n\n- Step 1\n- Step 2" | Markdown text displayed on the canvas card | +| color | dropdown | default | Card background color: default, blue, green, yellow, red, or purple | + +## Limitations + +- None. diff --git a/docs/nodes/Threshold Mask.md b/docs/nodes/Threshold Mask.md new file mode 100644 index 0000000..44d34cf --- /dev/null +++ b/docs/nodes/Threshold Mask.md @@ -0,0 +1,29 @@ +# Threshold Mask + +Create a binary mask by thresholding data. Otsu automatically finds the optimal threshold. Equivalent to Gwyddion's threshold and otsu_threshold modules. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to threshold | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| mask | IMAGE | Binary mask (white = selected pixels) | +| threshold | RECORD_TABLE | Table with the threshold value applied | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| method | dropdown | absolute | Thresholding method: absolute (raw data value), relative (fraction of min–max range), or otsu (automatic Otsu threshold) | +| threshold | FLOAT | 0.0 | Threshold value; for absolute: raw z value; for relative: fraction 0–1; ignored for otsu (socket-only input) | +| direction | dropdown | above | Which pixels to select: above or below the threshold | + +## Limitations + +- For the relative method, the threshold fraction is applied to the full data range [min, max]. +- Otsu thresholding may not give meaningful results on non-bimodal height distributions. diff --git a/docs/nodes/Tip Deconvolution.md b/docs/nodes/Tip Deconvolution.md new file mode 100644 index 0000000..1399f2f --- /dev/null +++ b/docs/nodes/Tip Deconvolution.md @@ -0,0 +1,26 @@ +# Tip Deconvolution + +Reconstruct the true surface from a tip-broadened measured AFM image. Uses morphological grey erosion (Villarrubia algorithm). Equivalent to gwy_tip_erosion (tip.c). + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Measured AFM image | +| tip | DATA_FIELD | Yes | Tip shape field, e.g. from Tip Model or Blind Tip Estimate | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| surface | DATA_FIELD | Reconstructed true surface | + +## Controls + +None. + +## Limitations + +- The tip pixel size must match the image pixel size exactly; mismatched calibrations will produce incorrect results. +- Deconvolution can only reduce tip broadening; it cannot recover information lost below the noise floor. +- Very large tip sizes are slow due to the nested loop over all tip pixels. diff --git a/docs/nodes/Tip Model.md b/docs/nodes/Tip Model.md new file mode 100644 index 0000000..3e74fae --- /dev/null +++ b/docs/nodes/Tip Model.md @@ -0,0 +1,30 @@ +# Tip Model + +Generate a synthetic AFM tip model DATA_FIELD. The input field sets the pixel size for the tip. The apex (centre pixel) is the maximum value; edges are shifted to zero. Equivalent to gwy_tip_model_preset_create (tip.c / tip_model.c). + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Reference field that sets the tip pixel size | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| tip | DATA_FIELD | Synthetic tip shape field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| shape | dropdown | parabola | Tip geometry: parabola (paraboloid with apex radius R), cone (sphere-capped cone), or sphere (ball-on-stick) | +| radius | FLOAT | 10 nm | Apex radius of curvature in metres (1e-10 to 1e-6) | +| half_angle | FLOAT | 20.0 | Half-cone angle from the tip axis in degrees for the cone shape (1–89°) | +| n_pixels | INT | 65 | Side length of the square tip grid in pixels (odd values only, 3–511) | + +## Limitations + +- half_angle is only used for the cone shape. +- The tip grid must be smaller than or equal to the image size for Tip Deconvolution to work correctly. +- Large n_pixels values produce large tip grids that make deconvolution slow. diff --git a/docs/nodes/Value Display.md b/docs/nodes/Value Display.md new file mode 100644 index 0000000..405d7be --- /dev/null +++ b/docs/nodes/Value Display.md @@ -0,0 +1,27 @@ +# Value Display + +Display a FLOAT value, or a selected numeric row from a measurement table, and pass the value through unchanged. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| value | FLOAT or RECORD_TABLE | No | Numeric value or measurement table to display; overrides the text input when connected | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| value | FLOAT | The numeric value (from socket or parsed from text) | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| number_input | STRING (text input) | "0" | Manual numeric entry, e.g. "1.5 nm"; hidden when value socket is connected | +| measurement | STRING (dropdown) | "" | Row selector when a RECORD_TABLE is connected; visible only for RECORD_TABLE inputs | + +## Limitations + +- When a RECORD_TABLE is connected, only rows with numeric values can be selected; non-numeric rows are not accessible. +- Manual text entry supports optional SI unit suffix (e.g. "1.5 nm") for display only; the output FLOAT is always the raw numeric value. diff --git a/docs/nodes/Watershed Segmentation.md b/docs/nodes/Watershed Segmentation.md new file mode 100644 index 0000000..660baf7 --- /dev/null +++ b/docs/nodes/Watershed Segmentation.md @@ -0,0 +1,33 @@ +# Watershed Segmentation + +Segment a height field into grains using the two-stage Gwyddion watershed workflow: drop-based seed location followed by watershed growth. Supports hill or valley detection and optional union/intersection with an existing mask. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input topographic field | +| mask | IMAGE | No | Optional existing mask to combine with the watershed result | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| mask | IMAGE | Binary grain mask from watershed segmentation | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| invert_height | BOOLEAN | False | When True, detects valleys instead of hills (inverts the height field) | +| locate_steps | INT | 10 | Number of drop steps in the seed location stage (1–200) | +| locate_threshold | INT | 10 | Minimum drop threshold for seed acceptance (0–100000) | +| locate_drop_size | FLOAT | 0.1 | Relative drop size for seed location stage (0.0001–1.0) | +| watershed_steps | INT | 20 | Number of steps in the watershed growth stage (1–2000) | +| watershed_drop_size | FLOAT | 0.1 | Relative drop size for watershed growth stage (0.0001–1.0) | +| combine_mode | dropdown | replace | How to combine with an existing mask: replace (ignore existing), union (OR), or intersection (AND) | + +## Limitations + +- Parameter tuning is required; the default settings work best for isolated round features. +- Very large locate_threshold values may reject all seeds and produce an empty mask. diff --git a/docs/nodes/Wavelet Denoise.md b/docs/nodes/Wavelet Denoise.md new file mode 100644 index 0000000..302ad87 --- /dev/null +++ b/docs/nodes/Wavelet Denoise.md @@ -0,0 +1,29 @@ +# Wavelet Denoise + +Denoise a DATA_FIELD using wavelet coefficient thresholding. BayesShrink adapts the threshold per sub-band; VisuShrink uses a global threshold. Equivalent to applying DWT from Gwyddion dwt.c with coefficient thresholding. + +## Inputs + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| field | DATA_FIELD | Yes | Input field to denoise | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| denoised | DATA_FIELD | Denoised field | + +## Controls + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| wavelet | dropdown | db4 | Wavelet family: db1 (Haar), db2, db4, db8, sym4, coif1, or bior1.3 | +| method | dropdown | BayesShrink | Threshold estimation method: BayesShrink (per sub-band adaptive) or VisuShrink (global universal) | +| sigma | FLOAT | 0.0 | Noise level estimate in data units; 0 = automatic estimation (0–1.0) | +| mode | dropdown | soft | Thresholding mode: soft (smooth shrinkage) or hard (zero below threshold) | + +## Limitations + +- The field size should ideally be a power of two for best wavelet decomposition; other sizes are handled by padding. +- sigma = 0 triggers automatic noise estimation from the finest-scale wavelet coefficients; this may be inaccurate on strongly structured surfaces. diff --git a/frontend/src/CustomNode.jsx b/frontend/src/CustomNode.jsx index 8ea5449..1305a84 100644 --- a/frontend/src/CustomNode.jsx +++ b/frontend/src/CustomNode.jsx @@ -1,7 +1,12 @@ import React, { useContext, useRef, useCallback, useState, useEffect, memo, lazy, Suspense } from 'react'; +import ReactDOM from 'react-dom'; import { Handle, NodeResizeControl, Position, useStore } from '@xyflow/react'; +import { marked } from 'marked'; +import { getNodeDoc } from './api'; import LinePlotOverlay from './LinePlotOverlay'; +marked.use({ breaks: true, gfm: true }); + const SurfaceView = lazy(() => import('./SurfaceView')); const CrossSectionOverlay = lazy(() => import('./CrossSectionOverlay')); const CropBoxOverlay = lazy(() => import('./CropBoxOverlay')); @@ -973,10 +978,48 @@ function NodeTable({ rows }) { ); } +// ── Node help panel (portal) ────────────────────────────────────────── + +function NodeHelpPanel({ title, content, onClose }) { + useEffect(() => { + const handler = (e) => { if (e.key === 'Escape') onClose(); }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + }, [onClose]); + + return ReactDOM.createPortal( +
+
+ {title} + +
+
+
, + document.body, + ); +} + // ── CustomNode component ────────────────────────────────────────────── function CustomNode({ id, data }) { const ctx = useContext(NodeContext); + const [helpOpen, setHelpOpen] = useState(false); + const [helpContent, setHelpContent] = useState(null); + + const onHelpClick = useCallback(async (e) => { + e.stopPropagation(); + if (helpOpen) { setHelpOpen(false); return; } + setHelpOpen(true); + if (helpContent === null) { + const text = await getNodeDoc(data.label); + setHelpContent(text || '*No documentation available for this node.*'); + } + }, [helpOpen, helpContent, data.label]); + if (data.className === 'Group') { return ; } @@ -1181,7 +1224,10 @@ function CustomNode({ id, data }) { {/* Title */}
{data.label} - {headerMeta && {headerMeta}} +
+ {headerMeta && {headerMeta}} + +
@@ -1528,6 +1574,13 @@ function CustomNode({ id, data }) { )}
+ {helpOpen && ( + setHelpOpen(false)} + /> + )} ); } diff --git a/frontend/src/api.js b/frontend/src/api.js index ecf6ff9..6f56265 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -62,6 +62,12 @@ export async function getNodes() { return r.json(); } +export async function getNodeDoc(displayName) { + const r = await sessionFetch(`/docs?name=${encodeURIComponent(displayName)}`); + if (!r.ok) return null; + return r.text(); +} + export async function getFiles() { const r = await sessionFetch('/files'); if (!r.ok) return []; diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 0390d06..9e218ad 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -435,6 +435,149 @@ html, body, #root { text-overflow: ellipsis; } +.node-title-right { + display: flex; + align-items: center; + gap: 5px; + flex-shrink: 0; +} + +.node-help-btn { + width: 15px; + height: 15px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.25); + color: rgba(255, 255, 255, 0.75); + font-size: 9px; + font-weight: 700; + line-height: 1; + padding: 0; + cursor: pointer; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.15s, border-color 0.15s; +} + +.node-help-btn:hover { + background: rgba(255, 255, 255, 0.28); + border-color: rgba(255, 255, 255, 0.5); +} + +/* ── Node help panel ─────────────────────────────────────── */ + +.node-help-panel { + position: fixed; + top: 60px; + right: 20px; + width: 420px; + max-height: calc(100vh - 80px); + background: #1e293b; + border: 1px solid #334155; + border-radius: 8px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55); + display: flex; + flex-direction: column; + z-index: 9999; + overflow: hidden; +} + +.node-help-panel-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 9px 12px; + border-bottom: 1px solid #334155; + background: #0f172a; + flex-shrink: 0; +} + +.node-help-panel-title { + font-weight: 600; + font-size: 13px; + color: #f1f5f9; +} + +.node-help-panel-close { + background: none; + border: none; + color: #64748b; + font-size: 20px; + line-height: 1; + padding: 0 2px; + cursor: pointer; + transition: color 0.12s; +} + +.node-help-panel-close:hover { + color: #f1f5f9; +} + +.node-help-panel-body { + padding: 14px 16px; + overflow-y: auto; + flex: 1; + font-size: 12.5px; + color: #cbd5e1; + line-height: 1.65; +} + +.node-help-panel-body h1, +.node-help-panel-body h2, +.node-help-panel-body h3 { + color: #f1f5f9; + margin: 14px 0 5px; + font-weight: 600; +} + +.node-help-panel-body h1 { font-size: 15px; margin-top: 0; } +.node-help-panel-body h2 { font-size: 13px; } +.node-help-panel-body h3 { font-size: 12px; } + +.node-help-panel-body p { margin: 4px 0 8px; } + +.node-help-panel-body table { + border-collapse: collapse; + width: 100%; + font-size: 11.5px; + margin: 8px 0 12px; +} + +.node-help-panel-body th, +.node-help-panel-body td { + border: 1px solid #334155; + padding: 4px 8px; + text-align: left; +} + +.node-help-panel-body th { + background: #0f172a; + color: #94a3b8; + font-weight: 600; +} + +.node-help-panel-body code { + background: #0f172a; + padding: 1px 5px; + border-radius: 3px; + font-size: 11px; + color: #7dd3fc; +} + +.node-help-panel-body ul, +.node-help-panel-body ol { + padding-left: 18px; + margin: 4px 0 8px; +} + +.node-help-panel-body li { margin: 2px 0; } + +.node-help-panel-body em { color: #94a3b8; } + +.node-help-panel-body strong { color: #e2e8f0; } + .node-body { padding: 4px 0; display: flex;