From d4cecf0372f81f034b728aa681b2c3edbd3084b4 Mon Sep 17 00:00:00 2001 From: matei jordache Date: Wed, 1 Apr 2026 23:07:05 -0700 Subject: [PATCH] more quality of life updates --- arhdf.log | 51 ------------------------- backend/nodes/mask_threshold.py | 2 +- backend/server.py | 41 ++++++++++++++++++++ docs/nodes/3D View.md | 2 +- docs/nodes/ACF 1D.md | 2 +- docs/nodes/ACF 2D.md | 2 +- docs/nodes/Angle Measure.md | 2 +- docs/nodes/Annotations.md | 2 +- docs/nodes/Blind Tip Estimate.md | 2 +- docs/nodes/Color Map.md | 2 +- docs/nodes/Colormap Adjust.md | 2 +- docs/nodes/Coordinate Pair.md | 2 +- docs/nodes/Coordinate.md | 2 +- docs/nodes/Crop-Resize.md | 2 +- docs/nodes/Cross Section.md | 2 +- docs/nodes/Cross-Correlate.md | 2 +- docs/nodes/Cursors.md | 2 +- docs/nodes/Curvature.md | 2 +- docs/nodes/Custom Convolution.md | 2 +- docs/nodes/Draw Mask.md | 2 +- docs/nodes/Edge Detect.md | 2 +- docs/nodes/Entropy.md | 2 +- docs/nodes/FFT 1D.md | 2 +- docs/nodes/FFT 2D.md | 2 +- docs/nodes/FFT Filter.md | 2 +- docs/nodes/Facet Level.md | 2 +- docs/nodes/Field Arithmetic.md | 2 +- docs/nodes/Fix Zero.md | 2 +- docs/nodes/Flip.md | 2 +- docs/nodes/Float Slider.md | 2 +- docs/nodes/Folder.md | 2 +- docs/nodes/Font.md | 2 +- docs/nodes/Fractal Dimension.md | 2 +- docs/nodes/Gaussian Filter.md | 2 +- docs/nodes/Gradient.md | 2 +- docs/nodes/Grain Analysis.md | 2 +- docs/nodes/Grain Distance Transform.md | 2 +- docs/nodes/Grain Filter.md | 2 +- docs/nodes/Histogram.md | 2 +- docs/nodes/Image (Demo).md | 2 +- docs/nodes/Image.md | 2 +- docs/nodes/Inverse 2D FFT.md | 2 +- docs/nodes/Kuwahara Filter.md | 2 +- docs/nodes/Line Correction.md | 2 +- docs/nodes/Local Contrast.md | 2 +- docs/nodes/Markup.md | 2 +- docs/nodes/Mask Invert.md | 2 +- docs/nodes/Mask Morphology.md | 2 +- docs/nodes/Mask Operations.md | 2 +- docs/nodes/Median Filter.md | 2 +- docs/nodes/Note.md | 2 +- docs/nodes/Number.md | 2 +- docs/nodes/PSDF.md | 2 +- docs/nodes/Plane Level.md | 2 +- docs/nodes/Polynomial Level.md | 2 +- docs/nodes/Preview.md | 2 +- docs/nodes/Print Table.md | 2 +- docs/nodes/Radial Profile.md | 2 +- docs/nodes/Resample.md | 2 +- docs/nodes/Rotate.md | 2 +- docs/nodes/Save Layers.md | 2 +- docs/nodes/Save.md | 2 +- docs/nodes/Scar Removal.md | 2 +- docs/nodes/Slope Distribution.md | 2 +- docs/nodes/Spot Removal.md | 2 +- docs/nodes/Statistics.md | 2 +- docs/nodes/Stats.md | 2 +- docs/nodes/Template Match.md | 2 +- docs/nodes/Text Note.md | 2 +- docs/nodes/Threshold Mask.md | 2 +- docs/nodes/Tip Deconvolution.md | 2 +- docs/nodes/Tip Model.md | 2 +- docs/nodes/Value Display.md | 2 +- docs/nodes/Watershed Segmentation.md | 2 +- docs/nodes/Wavelet Denoise.md | 2 +- frontend/src/App.tsx | 7 ++++ frontend/src/HelpPanelManager.tsx | 12 ++++++ frontend/src/styles.css | 9 +++-- profile.png | Bin 0 -> 15790 bytes 79 files changed, 139 insertions(+), 127 deletions(-) delete mode 100644 arhdf.log create mode 100644 profile.png diff --git a/arhdf.log b/arhdf.log deleted file mode 100644 index c343a3c..0000000 --- a/arhdf.log +++ /dev/null @@ -1,51 +0,0 @@ - -=== Channel: Adhesion:Retrace === - DimScaling shape: (2, 2) - DimScaling[0,:]: [8.656250e-06 1.665625e-05] (row 0) - DimScaling[1,:]: [-4.e-06 4.e-06] (row 1) - --- If [start, end] interpretation --- - xreal (row1 range) = 8e-06 - yreal (row0 range) = 7.999999999999998e-06 - --- If [step, offset] interpretation --- - row0: step=8.656249999999648e-06 offset=1.6656249999999646e-05 - row1: step=-4e-06 offset=4e-06 - DimUnits: ['m' 'm'] - DataUnits: N - DimExtents ('Resolution 0'): [512 384] - -=== Channel: FFMZSensor:Retrace === - DimScaling shape: (2, 2) - DimScaling[0,:]: [8.656250e-06 1.665625e-05] (row 0) - DimScaling[1,:]: [-4.e-06 4.e-06] (row 1) - --- If [start, end] interpretation --- - xreal (row1 range) = 8e-06 - yreal (row0 range) = 7.999999999999998e-06 - --- If [step, offset] interpretation --- - row0: step=8.656249999999648e-06 offset=1.6656249999999646e-05 - row1: step=-4e-06 offset=4e-06 - DimUnits: ['m' 'm'] - DataUnits: m - DimExtents ('Resolution 0'): [512 384] - -=== Channel: MaxForce:Retrace === - DimScaling shape: (2, 2) - DimScaling[0,:]: [8.656250e-06 1.665625e-05] (row 0) - DimScaling[1,:]: [-4.e-06 4.e-06] (row 1) - --- If [start, end] interpretation --- - xreal (row1 range) = 8e-06 - yreal (row0 range) = 7.999999999999998e-06 - --- If [step, offset] interpretation --- - row0: step=8.656249999999648e-06 offset=1.6656249999999646e-05 - row1: step=-4e-06 offset=4e-06 - DimUnits: ['m' 'm'] - DataUnits: N - DimExtents ('Resolution 0'): [512 384] - -=== 2D dataset shapes === - Image/DataSet/Resolution 0/Frame 0/Adhesion:Retrace/Image shape=(512, 384) - Image/DataSet/Resolution 0/Frame 0/FFMZSensor:Retrace/Image shape=(512, 384) - Image/DataSet/Resolution 0/Frame 0/MaxForce:Retrace/Image shape=(512, 384) - Image/DataSetInfo/Global/Channels/Adhesion:Retrace/Thumbnail shape=(128, 128) - Image/DataSetInfo/Global/Channels/FFMZSensor:Retrace/Thumbnail shape=(128, 128) - Image/DataSetInfo/Global/Channels/MaxForce:Retrace/Thumbnail shape=(128, 128) - Image/DataSetInfo/Global/Thumbnail shape=(128, 128) diff --git a/backend/nodes/mask_threshold.py b/backend/nodes/mask_threshold.py index 47f94a4..75b1639 100644 --- a/backend/nodes/mask_threshold.py +++ b/backend/nodes/mask_threshold.py @@ -23,7 +23,7 @@ class ThresholdMask: OUTPUTS = ( ('IMAGE', 'mask'), - ('RECORD_TABLE', 'threshold'), + ('FLOAT', 'threshold'), ) FUNCTION = "process" diff --git a/backend/server.py b/backend/server.py index 0c065bd..0887561 100644 --- a/backend/server.py +++ b/backend/server.py @@ -60,6 +60,22 @@ FRONTEND_DIR = frontend_dir() DIST_DIR = frontend_dist_dir() PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n" +GITHUB_REPO = "vipqualitypost/tono" +_APP_VERSION: str | None = None + + +def _get_app_version() -> str: + global _APP_VERSION + if _APP_VERSION is not None: + return _APP_VERSION + try: + import tomllib + with open(project_root() / "pyproject.toml", "rb") as f: + _APP_VERSION = tomllib.load(f)["project"]["version"] + except Exception: + _APP_VERSION = "0.0.0" + return _APP_VERSION + class _SafeEncoder(json.JSONEncoder): def default(self, obj): @@ -568,6 +584,30 @@ def create_app( ) return ws + async def check_update(_request: web.Request) -> web.Response: + import aiohttp as _aiohttp + + current = _get_app_version() + url = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest" + try: + async with _aiohttp.ClientSession() as session: + async with session.get(url, timeout=_aiohttp.ClientTimeout(total=5), + headers={"Accept": "application/vnd.github.v3+json"}) as resp: + if resp.status != 200: + return web.json_response({"current": current, "latest": None, "update_available": False}) + data = await resp.json() + latest = str(data.get("tag_name", "")).lstrip("vV") + html_url = str(data.get("html_url", "")) + update_available = latest != "" and latest != current + return web.json_response({ + "current": current, + "latest": latest, + "update_available": update_available, + "url": html_url, + }) + except Exception: + return web.json_response({"current": current, "latest": None, "update_available": False}) + app = web.Application() app["allow_local_filesystem"] = allow_local_filesystem @@ -587,6 +627,7 @@ def create_app( app.router.add_get("/help-docs", get_help_docs) app.router.add_get("/help-docs/{filename}", get_help_doc_file) app.router.add_post("/prompt", submit_prompt) + app.router.add_get("/check-update", check_update) app.router.add_get("/ws", websocket_handler) if (DIST_DIR / "assets").exists(): diff --git a/docs/nodes/3D View.md b/docs/nodes/3D View.md index e495992..a37f942 100644 --- a/docs/nodes/3D View.md +++ b/docs/nodes/3D View.md @@ -26,7 +26,7 @@ Interactive 3D surface view of a DATA_FIELD. Use the mesh input for geometry and | 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 +## Notes - 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. diff --git a/docs/nodes/ACF 1D.md b/docs/nodes/ACF 1D.md index 1f77e24..3217e34 100644 --- a/docs/nodes/ACF 1D.md +++ b/docs/nodes/ACF 1D.md @@ -21,7 +21,7 @@ Compute the one-dimensional autocorrelation function of a line profile. Only pos |------|------|---------|-------------| | level | dropdown | mean | Pre-processing: subtract mean before correlation, or none | -## Limitations +## Notes - 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 index f20e723..bd3b392 100644 --- a/docs/nodes/ACF 2D.md +++ b/docs/nodes/ACF 2D.md @@ -20,7 +20,7 @@ Compute the two-dimensional autocorrelation function with Gwyddion-style mean or |------|------|---------|-------------| | level | dropdown | mean | Pre-processing applied before correlation: mean subtraction, plane subtraction, or none | -## Limitations +## Notes - 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 index 8f0f9c0..5bbf681 100644 --- a/docs/nodes/Angle Measure.md +++ b/docs/nodes/Angle Measure.md @@ -22,7 +22,7 @@ Measure the included angle between two draggable line segments over a DATA_FIELD | 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 +## Notes - 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 index caa12d2..e11c560 100644 --- a/docs/nodes/Annotations.md +++ b/docs/nodes/Annotations.md @@ -25,6 +25,6 @@ Attach optional publication-style annotations (scale bar, color-map legend) to a | 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 +## Notes - 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 index d5400ce..39df15a 100644 --- a/docs/nodes/Blind Tip Estimate.md +++ b/docs/nodes/Blind Tip Estimate.md @@ -24,7 +24,7 @@ Blind tip estimation from a measured SPM image using the Villarrubia algorithm. | 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 +## Notes - 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. diff --git a/docs/nodes/Color Map.md b/docs/nodes/Color Map.md index 0fc2b25..422b68f 100644 --- a/docs/nodes/Color Map.md +++ b/docs/nodes/Color Map.md @@ -20,7 +20,7 @@ None. | 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 +## Notes - 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 index 5663661..2d3a66b 100644 --- a/docs/nodes/Colormap Adjust.md +++ b/docs/nodes/Colormap Adjust.md @@ -22,7 +22,7 @@ Adjust how a DATA_FIELD maps into its colormap without changing the underlying d | 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 +## Notes - 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 index 8c3f69a..268623e 100644 --- a/docs/nodes/Coordinate Pair.md +++ b/docs/nodes/Coordinate Pair.md @@ -19,6 +19,6 @@ Combine two COORD values into a single COORDPAIR for use with nodes that accept None. -## Limitations +## Notes - 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 index d310013..bf4b0ec 100644 --- a/docs/nodes/Coordinate.md +++ b/docs/nodes/Coordinate.md @@ -19,6 +19,6 @@ None. | 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 +## Notes - 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 index 808811c..987f732 100644 --- a/docs/nodes/Crop-Resize.md +++ b/docs/nodes/Crop-Resize.md @@ -24,7 +24,7 @@ Crop a DATA_FIELD with a draggable rectangle defined by two corners, then option | target_height | INT | 0 | Output pixel height after resampling (0 = keep cropped height) | | interpolation | dropdown | bilinear | Resampling interpolation: bilinear, nearest, or bicubic | -## Limitations +## Notes - 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. diff --git a/docs/nodes/Cross Section.md b/docs/nodes/Cross Section.md index aacd067..e35f508 100644 --- a/docs/nodes/Cross Section.md +++ b/docs/nodes/Cross Section.md @@ -23,7 +23,7 @@ Extract a cross-section height profile along a line between two draggable points | 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 +## Notes - 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 index 08a5633..02fa1a1 100644 --- a/docs/nodes/Cross-Correlate.md +++ b/docs/nodes/Cross-Correlate.md @@ -22,7 +22,7 @@ Compute 2D cross-correlation between two fields. The correlation peak indicates | 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 +## Notes - 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 index 2aafc63..557b0bb 100644 --- a/docs/nodes/Cursors.md +++ b/docs/nodes/Cursors.md @@ -20,7 +20,7 @@ Place two draggable cursors on a line plot or 2D field to measure positions and None (cursor positions are set by dragging in the preview panel). -## Limitations +## Notes - 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 index 73205e5..c3d06b1 100644 --- a/docs/nodes/Curvature.md +++ b/docs/nodes/Curvature.md @@ -24,7 +24,7 @@ Fit a quadratic surface and report the overall principal curvature radii and dir |------|------|---------|-------------| | masking | dropdown | ignore | How to use the mask: ignore (fit all pixels), include (fit only masked pixels), or exclude (fit unmasked pixels) | -## Limitations +## Notes - 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. diff --git a/docs/nodes/Custom Convolution.md b/docs/nodes/Custom Convolution.md index c98a0a5..89c3562 100644 --- a/docs/nodes/Custom Convolution.md +++ b/docs/nodes/Custom Convolution.md @@ -22,7 +22,7 @@ Apply a user-defined convolution kernel to a DATA_FIELD. Enter rows of space-sep | 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 +## Notes - Kernel must be a rectangle: all rows must have the same number of values. - Maximum kernel size is 51×51; larger kernels are rejected. diff --git a/docs/nodes/Draw Mask.md b/docs/nodes/Draw Mask.md index 1a77dac..da72c37 100644 --- a/docs/nodes/Draw Mask.md +++ b/docs/nodes/Draw Mask.md @@ -22,7 +22,7 @@ Paint a binary mask directly over an image preview. Pen size controls newly draw | invert | BOOLEAN | False | When enabled, swaps painted and unpainted regions | | clear_mask | BUTTON | — | Clears all painted strokes | -## Limitations +## Notes - 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 index 482451c..baba948 100644 --- a/docs/nodes/Edge Detect.md +++ b/docs/nodes/Edge Detect.md @@ -21,7 +21,7 @@ Detect edges using Sobel, Prewitt, Laplacian, or Laplacian-of-Gaussian (LoG) ope | 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 +## Notes - 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 index 354e317..b5894f5 100644 --- a/docs/nodes/Entropy.md +++ b/docs/nodes/Entropy.md @@ -22,7 +22,7 @@ Compute the Shannon entropy of the height or slope distribution. H = −Σ p·ln | 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 +## Notes - 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 index 754d851..2594661 100644 --- a/docs/nodes/FFT 1D.md +++ b/docs/nodes/FFT 1D.md @@ -19,7 +19,7 @@ Compute the FFT amplitude spectrum of a line profile and identify the dominant p None. -## Limitations +## Notes - The DC component is excluded from the output. - Spectrum is one-sided (real FFT); the x-axis shows period, not frequency. diff --git a/docs/nodes/FFT 2D.md b/docs/nodes/FFT 2D.md index dff4d14..dd09556 100644 --- a/docs/nodes/FFT 2D.md +++ b/docs/nodes/FFT 2D.md @@ -24,7 +24,7 @@ Compute the 2D FFT with optional windowing and mean/plane subtraction. Outputs l | 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 +## Notes - 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 index 26e0304..8b100a9 100644 --- a/docs/nodes/FFT Filter.md +++ b/docs/nodes/FFT Filter.md @@ -23,7 +23,7 @@ Frequency-domain filtering of a line profile or 2D data field using a Butterwort | 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 +## Notes - 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 index e94c190..4c2ee76 100644 --- a/docs/nodes/Facet Level.md +++ b/docs/nodes/Facet Level.md @@ -21,7 +21,7 @@ Level a field by iteratively finding the dominant local facet orientation and su |------|------|---------|-------------| | masking | dropdown | exclude | How to use the mask: exclude (ignore masked facets), include (use only masked facets), or ignore (use all facets) | -## Limitations +## Notes - 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. diff --git a/docs/nodes/Field Arithmetic.md b/docs/nodes/Field Arithmetic.md index a19c4e5..eb663e6 100644 --- a/docs/nodes/Field Arithmetic.md +++ b/docs/nodes/Field Arithmetic.md @@ -21,7 +21,7 @@ Apply a point-wise arithmetic operation to two DATA_FIELDs of the same resolutio |------|------|---------|-------------| | operation | dropdown | add | Element-wise operation: add, subtract, multiply, divide, min, max, or hypot (√(a²+b²)) | -## Limitations +## Notes - 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 index af34cc5..5ca1d73 100644 --- a/docs/nodes/Fix Zero.md +++ b/docs/nodes/Fix Zero.md @@ -20,6 +20,6 @@ Shift data so that the minimum, mean, or median value becomes zero. Equivalent t |------|------|---------|-------------| | method | dropdown | min | Reference value to set to zero: min (lowest pixel), mean (average), or median | -## Limitations +## Notes - None. diff --git a/docs/nodes/Flip.md b/docs/nodes/Flip.md index fbf31bc..015c056 100644 --- a/docs/nodes/Flip.md +++ b/docs/nodes/Flip.md @@ -20,6 +20,6 @@ Reflect a DATA_FIELD across the X axis (top/bottom) or Y axis (left/right). Phys |------|------|---------|-------------| | axis | dropdown | x | Flip axis: x flips top/bottom (vertical mirror), y flips left/right (horizontal mirror) | -## Limitations +## Notes - None. diff --git a/docs/nodes/Float Slider.md b/docs/nodes/Float Slider.md index 504dbb0..2a41724 100644 --- a/docs/nodes/Float Slider.md +++ b/docs/nodes/Float Slider.md @@ -20,6 +20,6 @@ None. | max_value | FLOAT | 1.0 | Upper bound of the slider range | | value | FLOAT (slider) | 0.5 | Current value within [min_value, max_value] | -## Limitations +## Notes - None. diff --git a/docs/nodes/Folder.md b/docs/nodes/Folder.md index ee36737..97bc591 100644 --- a/docs/nodes/Folder.md +++ b/docs/nodes/Folder.md @@ -18,7 +18,7 @@ None. |------|------|---------|-------------| | folder | FOLDER_PICKER | "" | Path to the folder to list | -## Limitations +## Notes - 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 index d6b9666..6891e09 100644 --- a/docs/nodes/Font.md +++ b/docs/nodes/Font.md @@ -19,7 +19,7 @@ None. | 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 +## Notes - 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 index b82ac07..ef3384a 100644 --- a/docs/nodes/Fractal Dimension.md +++ b/docs/nodes/Fractal Dimension.md @@ -21,7 +21,7 @@ Calculate the surface fractal dimension using Gwyddion's partitioning, cube coun | 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 +## Notes - 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 index a3c7c55..c7aa1b4 100644 --- a/docs/nodes/Gaussian Filter.md +++ b/docs/nodes/Gaussian Filter.md @@ -20,7 +20,7 @@ Apply a Gaussian blur to a DATA_FIELD. Equivalent to gwy_data_field_filter_gauss |------|------|---------|-------------| | sigma | FLOAT | 1.0 | Standard deviation of the Gaussian kernel in pixels (0.01–50.0) | -## Limitations +## Notes - 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 index 3982602..cca81e0 100644 --- a/docs/nodes/Gradient.md +++ b/docs/nodes/Gradient.md @@ -20,7 +20,7 @@ Compute the spatial gradient using a Sobel operator. Outputs the gradient magnit |------|------|---------|-------------| | component | dropdown | magnitude | Output component: magnitude (√(gx²+gy²)), x (horizontal gradient), y (vertical gradient), or azimuth (angle in degrees) | -## Limitations +## Notes - 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 index 748503b..646b562 100644 --- a/docs/nodes/Grain Analysis.md +++ b/docs/nodes/Grain Analysis.md @@ -21,7 +21,7 @@ Label connected grain regions in a binary mask and compute per-grain statistics: |------|------|---------|-------------| | min_size | INT | 10 | Minimum grain area in pixels; smaller connected regions are ignored (1–100000) | -## Limitations +## Notes - 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 index 9816818..8a2e098 100644 --- a/docs/nodes/Grain Distance Transform.md +++ b/docs/nodes/Grain Distance Transform.md @@ -23,7 +23,7 @@ Compute the mask distance transform using Gwyddion-style interior, exterior, or | 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 +## Notes - 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 index ce91567..a5ecb28 100644 --- a/docs/nodes/Grain Filter.md +++ b/docs/nodes/Grain Filter.md @@ -22,7 +22,7 @@ Remove grains from a binary mask based on size and border contact. Equivalent to | 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 +## Notes - 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 index 239bd2f..46ceeae 100644 --- a/docs/nodes/Histogram.md +++ b/docs/nodes/Histogram.md @@ -22,7 +22,7 @@ Compute the height distribution histogram (DH). Use log scale to reveal small pe | n_bins | INT | 256 | Number of histogram bins (10–1000) | | y_scale | dropdown | linear | Y-axis scale: linear or log | -## Limitations +## Notes - 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 index 32a2002..b26987a 100644 --- a/docs/nodes/Image (Demo).md +++ b/docs/nodes/Image (Demo).md @@ -19,7 +19,7 @@ None. | 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 +## Notes - 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 index 0a2d58c..aebe315 100644 --- a/docs/nodes/Image.md +++ b/docs/nodes/Image.md @@ -22,7 +22,7 @@ Load any supported file. SPM formats (.gwy, .sxm, .ibw) and HDF5 (.h5, .hdf5) pr | 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 +## Notes - 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 index bb33b26..650618e 100644 --- a/docs/nodes/Inverse 2D FFT.md +++ b/docs/nodes/Inverse 2D FFT.md @@ -21,7 +21,7 @@ Reconstruct a spatial-domain image from a 2D frequency spectrum. For exact recon |------|------|---------|-------------| | representation | dropdown | magnitude | How to interpret the spectrum input: magnitude, log_magnitude, or psdf | -## Limitations +## Notes - 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. diff --git a/docs/nodes/Kuwahara Filter.md b/docs/nodes/Kuwahara Filter.md index 92314ec..6a9ba17 100644 --- a/docs/nodes/Kuwahara Filter.md +++ b/docs/nodes/Kuwahara Filter.md @@ -20,7 +20,7 @@ Edge-preserving smoothing using Kuwahara's minimum-variance quadrant method. Unl |------|------|---------|-------------| | iterations | INT | 1 | Number of times the 5×5 Kuwahara pass is applied (1–20) | -## Limitations +## Notes - 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 index 16f7f5e..ee713e2 100644 --- a/docs/nodes/Line Correction.md +++ b/docs/nodes/Line Correction.md @@ -27,7 +27,7 @@ Correct scan-line mismatches using Gwyddion-derived row alignment methods. Suppo | 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 +## Notes - 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 index b564b50..210f53f 100644 --- a/docs/nodes/Local Contrast.md +++ b/docs/nodes/Local Contrast.md @@ -21,7 +21,7 @@ Expand the local dynamic range at each pixel to reveal fine surface features tha | 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 +## Notes - 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 index 6da7108..caba27e 100644 --- a/docs/nodes/Markup.md +++ b/docs/nodes/Markup.md @@ -23,7 +23,7 @@ Draw simple vector shapes (lines, rectangles, circles, arrows) over a DATA_FIELD | stroke_width | INT | 3 | Line thickness in display pixels for newly drawn shapes (1–64) | | clear_shapes | BUTTON | — | Remove all drawn shapes | -## Limitations +## Notes - 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 index 944fc1c..5eb47c4 100644 --- a/docs/nodes/Mask Invert.md +++ b/docs/nodes/Mask Invert.md @@ -19,6 +19,6 @@ Invert a binary mask — swap masked and unmasked regions. None. -## Limitations +## Notes - None. diff --git a/docs/nodes/Mask Morphology.md b/docs/nodes/Mask Morphology.md index 6e27780..d357337 100644 --- a/docs/nodes/Mask Morphology.md +++ b/docs/nodes/Mask Morphology.md @@ -23,6 +23,6 @@ Apply morphological operations to a binary mask. Dilate expands regions, erode s | radius | INT | 1 | Structuring element radius in pixels (1–50) | | shape | dropdown | disk | Structuring element shape: disk or square | -## Limitations +## Notes - 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 index ab5d7fe..cb9ace0 100644 --- a/docs/nodes/Mask Operations.md +++ b/docs/nodes/Mask Operations.md @@ -21,6 +21,6 @@ Apply boolean logic to two binary masks. Supports AND, OR, XOR, NAND, NOR, XNOR, |------|------|---------|-------------| | 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 +## Notes - Both masks must have the same pixel dimensions. diff --git a/docs/nodes/Median Filter.md b/docs/nodes/Median Filter.md index 2e64c2a..f85929c 100644 --- a/docs/nodes/Median Filter.md +++ b/docs/nodes/Median Filter.md @@ -20,7 +20,7 @@ Apply a median filter to a DATA_FIELD. Equivalent to gwy_data_field_filter_media |------|------|---------|-------------| | size | INT | 3 | Kernel size (side length) in pixels; odd values only (1–21) | -## Limitations +## Notes - 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 index ce0472b..bb2c1e6 100644 --- a/docs/nodes/Note.md +++ b/docs/nodes/Note.md @@ -20,7 +20,7 @@ Read the Note metadata from an .ibw (Igor binary wave) file and display all entr |------|------|---------|-------------| | filename | FILE_PICKER | "" | Path to the .ibw file; hidden when path input is connected | -## Limitations +## Notes - 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 index 34e7f0b..a84f97e 100644 --- a/docs/nodes/Number.md +++ b/docs/nodes/Number.md @@ -18,6 +18,6 @@ None. |------|------|---------|-------------| | value | FLOAT | 0.0 | The numeric value to output | -## Limitations +## Notes - None. diff --git a/docs/nodes/PSDF.md b/docs/nodes/PSDF.md index 1f76db2..95136b3 100644 --- a/docs/nodes/PSDF.md +++ b/docs/nodes/PSDF.md @@ -21,7 +21,7 @@ Compute the two-dimensional power spectral density function with Gwyddion-style | 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 +## Notes - 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 index 10338f0..ccfb9f7 100644 --- a/docs/nodes/Plane Level.md +++ b/docs/nodes/Plane Level.md @@ -21,6 +21,6 @@ Fit and subtract a least-squares plane from the data. Supports include/exclude m |------|------|---------|-------------| | 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 +## Notes - 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 index 8612a6d..9b95298 100644 --- a/docs/nodes/Polynomial Level.md +++ b/docs/nodes/Polynomial Level.md @@ -22,7 +22,7 @@ Fit and subtract a polynomial background of given degree in x and y. Equivalent | 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 +## Notes - 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 index 9403650..c11700e 100644 --- a/docs/nodes/Preview.md +++ b/docs/nodes/Preview.md @@ -19,7 +19,7 @@ None. |------|------|---------|-------------| | colormap | dropdown | auto | Colormap used when rendering a DATA_FIELD or grayscale IMAGE; hidden when colormap_map is connected | -## Limitations +## Notes - 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 index 2eace36..a215619 100644 --- a/docs/nodes/Print Table.md +++ b/docs/nodes/Print Table.md @@ -16,6 +16,6 @@ None. None. -## Limitations +## Notes - None. diff --git a/docs/nodes/Radial Profile.md b/docs/nodes/Radial Profile.md index 06cba46..87ec4f8 100644 --- a/docs/nodes/Radial Profile.md +++ b/docs/nodes/Radial Profile.md @@ -22,7 +22,7 @@ Compute the azimuthally averaged radial profile from a centre point. The output | 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 +## Notes - 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 index 0769327..47361dc 100644 --- a/docs/nodes/Resample.md +++ b/docs/nodes/Resample.md @@ -22,7 +22,7 @@ Resample a DATA_FIELD to a new pixel resolution while preserving physical dimens | height | INT | 256 | Output pixel height (2–16384) | | interpolation | dropdown | linear | Interpolation method: linear, cubic, or nearest | -## Limitations +## Notes - 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 index 6f6b5e4..46599f3 100644 --- a/docs/nodes/Rotate.md +++ b/docs/nodes/Rotate.md @@ -22,7 +22,7 @@ Rotate a DATA_FIELD counterclockwise by an angle in degrees. Optionally expand t | 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 +## Notes - 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 index 8ada90d..8ba5158 100644 --- a/docs/nodes/Save Layers.md +++ b/docs/nodes/Save Layers.md @@ -22,7 +22,7 @@ None. | 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 +## Notes - 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. diff --git a/docs/nodes/Save.md b/docs/nodes/Save.md index 7bf85e3..c6e6795 100644 --- a/docs/nodes/Save.md +++ b/docs/nodes/Save.md @@ -22,7 +22,7 @@ None. | 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 +## Notes - 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 index 7941b28..d995b9d 100644 --- a/docs/nodes/Scar Removal.md +++ b/docs/nodes/Scar Removal.md @@ -25,7 +25,7 @@ Detect and remove horizontal scan scars using Gwyddion-derived scar marking thre | 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 +## Notes - 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 index 0b2f9cb..f0db138 100644 --- a/docs/nodes/Slope Distribution.md +++ b/docs/nodes/Slope Distribution.md @@ -21,7 +21,7 @@ Compute the angular slope distribution of a DATA_FIELD surface. Equivalent to Gw | 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 +## Notes - 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 index 05960d1..8a004ec 100644 --- a/docs/nodes/Spot Removal.md +++ b/docs/nodes/Spot Removal.md @@ -22,7 +22,7 @@ Fill defect pixels (hot pixels, dropouts, scan artifacts) by interpolation. The | 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 +## Notes - 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 index 7d4943c..30b0041 100644 --- a/docs/nodes/Statistics.md +++ b/docs/nodes/Statistics.md @@ -18,6 +18,6 @@ Compute basic surface statistics: min, max, mean, RMS roughness, median, and ske None. -## Limitations +## Notes - None. diff --git a/docs/nodes/Stats.md b/docs/nodes/Stats.md index 4a753ba..9c9e792 100644 --- a/docs/nodes/Stats.md +++ b/docs/nodes/Stats.md @@ -21,7 +21,7 @@ Compute a contextual scalar statistic from a LINE, record table, DATA_FIELD, or | 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 +## Notes - 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 index ce282c4..f6c782c 100644 --- a/docs/nodes/Template Match.md +++ b/docs/nodes/Template Match.md @@ -22,7 +22,7 @@ Find a template pattern within a larger data field using normalised cross-correl |------|------|---------|-------------| | threshold | FLOAT | 0.8 | Minimum correlation score to mark as a detection (0.0–1.0) | -## Limitations +## Notes - 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 index cc581e0..86eb9c2 100644 --- a/docs/nodes/Text Note.md +++ b/docs/nodes/Text Note.md @@ -17,6 +17,6 @@ None. | 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 +## Notes - None. diff --git a/docs/nodes/Threshold Mask.md b/docs/nodes/Threshold Mask.md index 44d34cf..36a7599 100644 --- a/docs/nodes/Threshold Mask.md +++ b/docs/nodes/Threshold Mask.md @@ -23,7 +23,7 @@ Create a binary mask by thresholding data. Otsu automatically finds the optimal | 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 +## Notes - 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 index 1399f2f..b618fb3 100644 --- a/docs/nodes/Tip Deconvolution.md +++ b/docs/nodes/Tip Deconvolution.md @@ -19,7 +19,7 @@ Reconstruct the true surface from a tip-broadened measured AFM image. Uses morph None. -## Limitations +## Notes - 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. diff --git a/docs/nodes/Tip Model.md b/docs/nodes/Tip Model.md index 3e74fae..2bfcf4b 100644 --- a/docs/nodes/Tip Model.md +++ b/docs/nodes/Tip Model.md @@ -23,7 +23,7 @@ Generate a synthetic AFM tip model DATA_FIELD. The input field sets the pixel si | 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 +## Notes - 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. diff --git a/docs/nodes/Value Display.md b/docs/nodes/Value Display.md index 405d7be..9c26937 100644 --- a/docs/nodes/Value Display.md +++ b/docs/nodes/Value Display.md @@ -21,7 +21,7 @@ Display a FLOAT value, or a selected numeric row from a measurement table, and p | 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 +## Notes - 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 index 660baf7..843889a 100644 --- a/docs/nodes/Watershed Segmentation.md +++ b/docs/nodes/Watershed Segmentation.md @@ -27,7 +27,7 @@ Segment a height field into grains using the two-stage Gwyddion watershed workfl | 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 +## Notes - 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 index 302ad87..e0ee58c 100644 --- a/docs/nodes/Wavelet Denoise.md +++ b/docs/nodes/Wavelet Denoise.md @@ -23,7 +23,7 @@ Denoise a DATA_FIELD using wavelet coefficient thresholding. BayesShrink adapts | 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 +## Notes - 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/App.tsx b/frontend/src/App.tsx index 9bfee39..d7feb8b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3001,6 +3001,13 @@ function Flow() { return () => document.removeEventListener('pointerdown', handler); }, [menuOpen]); + // Auto-dismiss status toast after 5 seconds + useEffect(() => { + if (!status.text) return; + const timer = setTimeout(() => setStatus({ text: '', level: 'info' }), 5000); + return () => clearTimeout(timer); + }, [status]); + useEffect(() => { const handler = (e: KeyboardEvent) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { diff --git a/frontend/src/HelpPanelManager.tsx b/frontend/src/HelpPanelManager.tsx index 06755c8..f8fe29e 100644 --- a/frontend/src/HelpPanelManager.tsx +++ b/frontend/src/HelpPanelManager.tsx @@ -2,6 +2,18 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import { marked } from 'marked'; +// Open external links in new tabs +const renderer = new marked.Renderer(); +const defaultLinkRenderer = renderer.link.bind(renderer); +renderer.link = function (token) { + const html = defaultLinkRenderer(token); + if (token.href && /^https?:\/\//.test(token.href)) { + return html.replace(/^GV+?1rUvNBFlB&+PLG!U}) zN*%(n;vAdvdmhyt)%|?#{rtY?pL00x^M1eP^Z9&?m){u`g-siFY(NlX(@Di+>Ikyt zDT2_Aq3Ga02U%QpBZ#!)$zw+~U7|<3H(l1E#Kk zCEhZ&wPah0ZQa{4nkK2+PPcU(``I{bGiLjq$z7^S{6rb(wK8;;oz*Q zG({8DS0mP&^5n_zi}G%V#?uTe0(x1O>VI3yaEC*(Wv+rrYT|jh%=EL5Y@N<%~A=+PJObPu?}@(^Kph*%lIg(KHDCaO}K4VRk5O6O`uJ97z= z&SSCeW9M^iy5Zs?+ujnQeqpCg;pkv}e4;90*%v?S%Vt~j3|?_@ADWUh;!oUulA4N} zZa34Aofjd@Rd?E!#zafaorfC>3JS_Pk9EC&ado_Kyac+LX;v$sm-iHY$<{l2Vlp#W zB}O9K=;d{IO?|4xkGuCSbbXR(Ej46A9Igt)BgRS4VEd7_XO(#T;zusHpWxov!Kaiy zU#=b)>P*v`4!EUr=|Xo+l-PJzzKG96%(#cy#QPVdmQ+jW5ZX;ALkL|(u2VY$q$b-g z23$kuShWk|=E?lJIg76i7hkt$nsqJ>?o_z5OSYgtQ`EM{qw%!ZZYsVijDPp;-B;p~Z*GMbZkmF~?Z}fy$%PeM?!!)U$C>lcYHCJh_X;hdPW~KBl{5{^M;2iZ=!{au<(${vwg62 z3Xb5B3Y0TyRfW3Y+*@thwp?E7r7lhMy)2z?PR|+YiS%f!hM9eHhhxD{)#JlUCfNf5 z0C`mGX-7)4 z_ITT~i}i8GVMevIv|s{Y>PeFy=;YRI9GrixhE%MErBe|tZvW;MD{U>p%CYJx5^vuyi` z5;;+}yU=+Y*7v7c0`;|^=Rt9yAjP}k;bNkqvC{6-X@z42%k!j8j?8ZJPRD`j$c&7P z5MEVUK&lECo184b0zq^6q#5@jfjUR{6zgu&m~gs0 zfD8ZLn?BZUIEgp`x4y+DHm2JBZ9>;`Re09od%U zcQ~d7YTO4O_y*(MAb_Yw9|?MM4Wf00_Rxs1+a$3Pw%fKXTW+z7!{-qGm$t=}mh+jW z*U&v4Q#EmMaZcsR9Com2Liw}>42ovQ3dd!fM$VC#Bs^v(Zn27BQ6i?*M;r#vR87E2 zgm!f~by}P2?S+8v5&BNjhp>DVUFN--zJ&ZpFnc4Hgu^A2QUBY}!-o$;5VmoyZo=s1 z+QdL(XFq?0wyEjp%)eq*&8mL-?Aa9v0vMmZm%cEVIUDhP*I3@%W^-;g34e6u0nGHI z);$7dBc_PbqFXC9RSn;DZZv#$;q{BF^aS^@{K^ns=R>9>%-0><=XWeFDajnRl0pDy zjI_Ix0#~tRYnuCbvDl~*q^-d426j|!jO5Jwt4v{GVUURkR;+mtHm@Yt7DJ1Q zisBD;Zp^>jR|W%1&&b}^)TF7ct$pT<5CmXiL}+N}?c29^p15`C(j~>7{m``e;beJV zMhM-WQ zMrA$_!-%P69Fk61%lG$bB#iWy%KALTQq9W|XAxY6wHqU0IvnWSLw(S&Q$nXG=}C zzye|C`B!Db|FTQ}U19Rw6ExKl*q}#syLXeAEAg8AtapcUi0dI*{&8Kn+Ti-`tuQ>R z$7p@YcQ_t<2r8UEF8Edwbth0I5M=J?x4-{dA_cMuUU{q|yti>vrP!B}>85ZuJi4OO zIk+(Xc$mHKmuku=mhU`4aapi&(<dAYqqiyShU;?M8MG5&R7Ka6c?=kA zgLbd?7TQp$Jh%sg$l>i6TUchqJ(7ohACXH{g|4dxHwBCa%C9LqmzT@fefzfyM%uI1 zY=fJ0&uomo8q`l~M1=wWf{pgUnFWpE;|>}Pgn zGVoxKb~)~9U48M2N1x@|QsZ@a32m|p<^BR0R~>xRljAHZ?}sV3G4%(fjmGimI*6y;YPGwrtkKIa^UGEb#{8+@e!}gfNqur6+ z9g=P?FJ*3|ZIS0{K3S0C-_$X7DQD*NQUEEfFpZQs+JaNm)sPu_{CIwEk3;cw$;cuK zHP_sll<%0u9izuIJf@y_y*4ZsMa*r{u4(W*UNSZ3u%scZep5P6?qml%Y%D-RqKray z)qPXDMPuicH&JVb_Ls@*AGkBknUp9=j8wsT?(k2|$;s)iVou6WvmbS`8XJu4dq zOs}okX~oT7`gmZSyOe}Zy?GI18-*=^74MTcr6g2viNfYBBp}c>Gka9nci5wUdx(<_ zj+lIl>u3kDK)~RgW#cn(M;EuKRzFd{CL3PtQeE%EsX6kjn}Y1+xX`1XPCNqqEIUQI z(cHD|weY+2c2Q?09jcXl6ydhHbH_$bdR>{ABj*=lNRNBH8T^-?bCfOK)r!G-D`qVW zGfv8%jR|pF&PhDvv|*G%l$pB|3OANdJGMFmHFhnAX_0c{-xyDe{;F+k{nG0Q-~qXk z%>~>{c|5(c-ylNW?$ln5@Sz;YfNm%VIDw*`b$f5?1&!0w8F6N?(Rs0dIJ%Gf>Kg1I zC%qNoWgO)rSJb6KkMMl*9eR-bYTaPF?d1hl8^o=8^TYIfd6cyA%o;>4jT!Yo zn|z5L;dWl{GxtVB4S5*8lR=J?`Md2}AtY_*tqes6lJOf$=&B`E?lnC9(yV~TFyF9@ zB5${91%~$bLtpx%mEwPV^Cy(^H3k@0x$i3uUNdM$he7WwJ;j7@(*ngSC0Zb`^Al(> z-}N;JR`YTC!#4#gKMfIbXD2?q()(o1NaXQGd&v;jh_#BpSX*HWcje&CLy(jIsSW=e znj!adPqpx3QCdptObRe{(*NUKZ#GC40yeSj%6pcOVA@-P<6j-XF0Ow5{CPb+kG`^N zb5oxT?Q0GT7Lyy|17sxZh!yBEmXe8U`Szcj4p|pML9KE2tP>O@N`l1>bCdn@*EhOC z3A1Cz4lD{PEm_aS+00s*sGfehN#*9?qmedr#EqRWmrC~BdVoD3n_5~}=rHLOyHTJn zkfkK4LRF&+HK+SpadUjJ80We|7F{ivPYi~V?mD^$PhC3p zAzoeyDzY1!cS;bZ0lD$Pbww_pyPbP+P}R0vr4Oc~$O)g~@z=#mtYlu(DFVBL#KZn>TO5`UHz6%JQy!6et|Y zKtCc@hTV~d{(G! z(L37@#yxoOpuglpa(an`!(d%~yu!@OUm&{@y-Swb{p>%zyM{gyz^SjT&4P9557j<# zd&gF68`La-0V9;JTzqp2V%B=*J-ZBrG<0{O0Z5g$`!==bV>@ zOOhTezV?I4g_gLKb9d*y-i}HKI1(6cpiG=<=>OQW{Y*_4e)CXpf|gFs0{L;?>`AJAqW zligQ^yV1ye7>)2E#uASw6W1PaQ~r79$U z{*t97Q!nDZTF!XlshIhwH~Rv zCx<%rBHIb^()|Bz;D76pTbwZtq6KaA*yyBrzg7fLA>7bZb2D=W!sj-dSW8pWNM~-k z$*(*?dj$n;t*oSgcA>G5OO#eFal41WE#&3R0;dH;Sn0q$ONo0q`j8d&@$pHzP7)u5 z8x(lVO=9G)OOfm3H*JEj2G?;xDs49-oF2EMMODHf`ZANMu+s#YV&If0&wolYq#G2w zeSCA9FyEBe<3>t_>!pE3&V{?AoX^CfY)j@$flu8` zhSVnRI%$mRTbk)r-Sx|;iu;fJ1M7dwq==lssVR;!K=6+kA=6$G9&c`HN--#w@OkPm z)UcLen;wqR45W%6AD^Xj2gF~>%tRlM47XWt-SU4*-Gt^yy{z zZGV41#z(Bj<>PfW8x%%4O4NF^BYRZE5sIj6%T{3Zo+c$FL5{wE|5qRcDrL3)Y3AH* z$Thz>RqKm}v>WVg4ebHm5#yt6;A+zJSZ!2A68HxqgDE5&=aIJhc~y-B<@QoM9%V#b zUV_gj8M~lZ8_n(QN%UV0WxDG8G-KatCaI@3-39PPnohRG%vd*24UZl@sx(81S+$|D z+l798o?%ke5##h{CeZp#{?t23;2NGJCx^&NZpJ=NPsald3lq`y?cTf4Ig5es3mi#U z6tKv^#9t(B21>Hfb!y;UVlfmIttsal$Bsamc+yGQ`bx8ww)G#0v)74rEu;%eO0VvT zOa*Ie>k2;RsVfgIoL_rZPd4L`-Z4B*{OmE6iU|vK46EVVth_dzrwJL}*>!ve*OdX} z6Cj4LFuUfRxsvi*M~4ny8nsP5*NiGw+SC$zD*ZWMoM{VY#2})w6B; z`S=zty(_pyhu}3cmg6<}5FX5*Hudik$&J@s2EnV4=UfnTmZu;bc{zPc3{!n2zIQG| z%wqbN!I@ioH$F&mj?_i=`aQD!of3oG?+i}QRxbJQi(UACzj#IGslx}4o2iy0b08H* z{)5Kz&(mq0?h4pg4{3p!4)gG9(m;^zoj)5fn8=@tN`HD_wd>bA%FQ#4eMt&|)HP-7 z14kgpk*`BX7iG!Pt8VzxJ_+JfkqhuBwdnT+A6FrPKM}Ex$57`Cm-`K!#=0UE7e8;F z0!E&KONRYg;lSNh>^=h(CzPJeGyIU|y58S5dDURzQ0B893StA$gz(u=zj=-1$sd6& zHSRf6c>MTr;N+DV>uD?)_f5IkYLVK-*>YJsd@BaDTgO0Q{eS-&m6A#Upd&|@aoA4hCaDN&rT8{aBIsTKu{XhFbnVwdL z4D9w5+6yMeE>sRBS9;2z@{~a{9U}an3(j?d?!1Kmv@sTgW{i@pD>!T|MG+1!&u%*oG`>VhzhlhnRvWcejtAa+?10e`RoD$pu7WK0n zn{~@|u#^yRZPsw&C>$FTSCr z!#8ZUDZsZ|Sy?S5wsb4VqacM=VqW?(*|JUnY8)?F7z30J^fVUry#UZLA9it=2*6kW z>8~%EwGDBQm7%tQu0S*_Ic3IufS?@q#|_bQDc%gDsV$kvQ#UPRqP{607@a@yk=bT%LLGDk&*}5-8-L z?5HIIdFHMM)3INZcsGg z6BcE-JQ)s|_}LKCIwsB{sN`t4R>K7PN@A(alAMDKU0k zQo~2?a*$q^985l|K$J*1VadzTzi=ukC1oUDoU{@_|C1l#FX6C40+Poy^r;p+xzPf_ zDL@4Id4w#BzU%QyYAz4_5OTFY4@F*Sll$o>|2D*YOZ6}&|I$J6lOHUmudJvw$UMYR z#3z{I2Lg=O#-I8#CB`&Bc-jLBxMH3`S!oWgdljT-XFKyZo<^>tFR~ zMPK;_rxn?}(G|Lkod^>1Ju&<9X#f!UBkB5MD!icmr(|=#^k+j~eUNhsmUja!X{q)KLE3=?{`ro0am;cl$gUR5(Br>D1dNr5?MgKT*^>xpYd`1M_92t<@SLfV?`Pvh? zb-U7MNV5nM`MV_awTp5s^^e%+Y1Pq6TPs+hhN`3jAB>Qh1@He>vImWo5Vtw%d6}}x zOYysvpRb@GgLq}_-GF;v_dp&*Zm?wFCuN!ajsc%_@#04SJ>vzWG*Wu$GJppI@JG;d zc?h{oyl-e|Six#T#>+oTsO&!hxbqkC5pF@j^j!q}y8B!LW@eoNVoT1bKaygAnmzmh zL9a8*T;urh*DC-5EC|kBSIQGkM}ZzcMH8DPA2O@T%n>09)9fdH%)Dc5&vyFIObp|Y zPT9n4im1yAU>S;;UJ=e^hnRdIJw^dK_FA=C=Xw{QVCcsNOwrsxROS8Ic~ZLSy+iwa zXnqSoD_N4n9}_6o6+o510sEj$t0S&Jsemrq9j<2wj5oS$VZ4-}IH9T02I+m4>#J#`uqC>wd~r9n*o;ckdF}D27ENv(H0gKz;AbQWI#{;Vx&nm z1ItXrhz3o!CsusTt})I%bH(Ghad0D@9LKIg^E0JjPvi;6$L_2A5PwpFfPu8ilxx+V zVbT1=iCS>s^-Xw)%uJU(Fx~>+Wnp%#A_wTbx`u{?XwmN&VwIS=p8;cYJptIgMR(uH z&H6)P9cj;Gl3l!v>H}zLVqyY|hY4c^CIG;?@B&tvUl9JY2u2e;q+b{T5{U#ne+P%% z-=JZpCn+gVfv_LEa7n}CMIBdnc*K#*BwwsWvAkTs^ zMs(=~sORB=hMs_`(Pcx8i3I%e92%>y-wlaY9z}ClfHOfJ-usah+!BDag%5kVf&rBcAn)KTDQ z`AJ@&baC>qWlNUi6c9zb3S)D&OFE4V)I>EFK-4f+5-fzgQXQ*mHYYA^Twkk#7_U~a zLA*9Pv(~79(E_7=y8839LL6IiWyi=6OJ}HH`*eeJj;I)g8&xGI<>a(2KoC#U)rD5W(a?XY}qec4Oe?vE!=) z1yqr*Ju36ZhF9+u>%z5R?44UOFrz4o-_pF za}rPEBFV%^z84W~oZJV7%0tB9BA!rYTS?@`>62801vCo7la@*+=bZ0=^<+k2w4t52 zSLR$uWP_bWtF82-wb%;#G)ru2ei@ydA?fY;^Z3~O^*(`9lgnFj;+07ptI4ZbG%AiP zNK`289<^>_&A%gv#xjyxmdp07ZQtW-A7d%Ub*wO92oD9p?e_puwc>k8gY zV{NW1-QXlqGxk*`3Fy!ng}r^?-W9k>N?9>D#*8J~<9NPZ30Ri& z5rr4_jwlMet*gDz=HzygyV~F_i`!+-_sTq{+eb>sXGpRjkt41H7WPJgm|zCRho6Mruop; zp3Uayg0isSaQbT_n!#&xe_Mm)W^#={XN>x+&L};l%O$K+>R9<;m+ZL!j?Jh{H`_rT zXUxU%WQ>CL)?xmlnUoLh^t!XBm z2okZIKM-ejdwi^fhr;Zq5#?{Pn<}==sW`ABl`s}|VLJNyYc`alV6sXIJy9G(Vk!zbLa-P4h=+4iXf+Y4cC0m+YI zB_)`~-ikIXT=b}l;D~Xa&b!{ZL>7=?ik5CYRC(jh&BU1*iC{NzU;h>}TY>qkNY`QY z&TWd0^^=c>9SL`1vlIQN2$H$!bY^sZA;X=MV&YjgFsnaaXFa0?T z%B}5B#`U^IPDcqi6<6dJ?=Nl|lkAmDr`mM?V(Z*7W{@yiB)w$#ENAmPnR3xQQk8dV zyxk}*#DAe~^VWv6JUSy5$9^7-Tmjz1baP@vnpEgriIWzk(=s^iV^IbP=hdI3U44}x zRxIJ(URhqn%UwF&Dd!YhtVtP5bJ;P+QO4MQus#u1TY>E`K1;GF-dmcq(^)Mw{(c6p zMsRj`(gtPR31m=JdQhs_%-Z`>>qqq>c z(xH}d(MHh2z7_d@_}3p2SSo?vX8sw#!E#LsVE`TcN%JFaEzbHI3&)>T%@siivjkoX z;cj?n-rrKG``tR z`N>ED4*rYNpVoB9m~P1{vM`4CZ<_v3(Az&CeSc`sar0>HpD1O#z65dm1Yich?HUe7 z+{ykb-;JydO-)&#?X()Wr#^l>xC|xuhnsvfo2~*E&Vg7Fgz*vd=wkrG!6@;ny4fuF zF4NCv4qRoD*)HXB+1z~SsnN@ZJSYSZ*L}da%#5^)fCCf!%HR!1(alXWnGI2uaRo*f z)Dg!Iul%5>LSYs=OP4y!WtTsLR)L#}>g~!fQ9?a<_Uto_10>VOB))JmJEx`=6zu^d zovPI5V>?)Y3kDOZ2%09m{SG;fAIBHXxcq=8YCWejb#3x@`&6DCD3KLhMM zl;LLnmuzfoA-E&HOk6!}ZQHR&zB36KqL#0o<4^%0)7m%G+R)z0PS52kUskbkFd*r!z0pjN0UT; z;13s8aGkq3%7;H-(fT{YVasX+|M`=%w;dcbRbMDQFt|(%#s^$Sn*t>`*B_qC`4cE| zzyB)eL7mkTpJb_XV4&B7m!YAdhDPJp=MH&XpZJpj*aoyXjL(X>B|$kd1N^?|GH|RU zDo6f;jR1)e_%(K{3p@?xjqRJ=WjoddT=wO+SedaA;P4mjXl`lQ)63q-<9Pjhtn!r_ zw2-s6w=Wo=El2Pp!`N?S0aty|Y$IWrEIree2}`xf(9Z=#jI-$mIDkQAWo2MTGH~q= zg(DGQ*H;9J5S%;XpnUQH>j+pf>G5O5UB7&B_ZPwj(vjKgc)hX`ygdLjaxc}#{q`H< zRk)Kq?P!eyzcBfPwe=`Ch{K1@K3CA(*)D%LSQ_5q$JLc3RvW3HQI(vGBF0S3iqmIq z)801j+w+A&tiT-X!Bjgi!(u5=2v>u*hLg0npT&|VdtYHFxg#RoJa%nCI~8Cm067Zk z21KnD@L=>GT&VKxbV- z*eMI~;-%?M%a)Wm@Kga+tEZzQ0U%aIX1C*{%~p}1*&$VYf%A9|u^?!KnPkScVzp|G# zhBxA;GYQ>xRs0wq8VfZ=pn8-Z5NNl7QwiV!v=R}@)>N+=`c5nvvN@cvO(ckBq<|2{ zuwlcAJM58NCv#fdR3mh9zqc@ShimRE4$@JBS~bc-S^91A3AgkbnWxd`VJWnmx~uKCPc~G=oUB(!GX54lKEjc7!V#B>S$p42kT=1iJ(jPzsFSG zSeLjER!*>WTw^gf0=yb6-L0E60uTqd1Je34aM;rJ8j0QAvNI-xQ%;;}HA+o`+&C%2YnN~t)z3JBO;^H4Upz!$j-^>qWRWlicQ`4hR8Cw#d) zCXT{3f}=km)AV**DD#^hfpzR$G8F@d+Nf62LXNZ*Mrut5&Cnww8qTzBYgQC|be7im zf*l$GlTU(r1}|~7*+{|S5|y8i+i}tL`sKHG_f=}o!+{Yze2TPT%>Ultv65i2bxnVe zm7o|Yp8entP7p(!BP>SDDBRUHgF%!hAr!ho*wIVhhu(@xiBM<6)=|RI@!ZeIDzHJgJhxH&77d(4#uWW z1iXyT8EtJbH}a(~9fKPsXYnbxHY?gb99u0ZVXORg*Q~x56(zrBab%4-IjVQj6s(zC z*0NyD)X5rDT_&o2J?H}41`ae%0m%hNSh!q?L6EoL08)>Atu&Zh`X$GUG+-|&?%Ke> z5YDgLG9SJR3Obt8)u4DDQWIn^AV(?2bk-XV_ynfBS(z#0!=i@|g9Xb5j4Nx`uI+yE zrs=}FK^D1z(e6U$rFjZa%nX=~oKkskkmmgD(QOt{RH-W;Ucp+2ZFAEfj@uY4LfqGZ zrIxfc6mGizbn2U?_#%s4`oShG^{y8!a0!e<{uZ}ZsQ9`Q2Udi#OY%PhB;S|W|IWMk z-<)Xw-CglRq5VanSUIp#VMH5x5IPM!=lX4NBwa_m5TrslCm9uK!-63Nja=u>)Lsjp{} literal 0 HcmV?d00001