Skip to content

[SUPERSEDED by #149] feat(toolbox): add Filter Editor toolbox plugin#141

Closed
pabloinigoblasco wants to merge 11 commits into
mainfrom
feat/toolbox-transform-editor
Closed

[SUPERSEDED by #149] feat(toolbox): add Filter Editor toolbox plugin#141
pabloinigoblasco wants to merge 11 commits into
mainfrom
feat/toolbox-transform-editor

Conversation

@pabloinigoblasco

@pabloinigoblasco pabloinigoblasco commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Port PlotJuggler 3's "Apply filter to data" dialog to a PJ4 toolbox panel (toolbox_filter_editor), presented like the colormap / quaternion toolboxes (embedded panel, not a modal dialog).

Pick one or more source curves (multi-selection applies the same transform to several timeseries at once, matching PJ3), choose a transform, tune its parameters with a live preview, optionally set an alias for the derived series, and Save to create it; Cancel reverts. Layout mirrors PJ3: source list | transform list | parameter panel. Parameter panels are aligned to PJ3 (labels, layout, Guess dT button). Output name auto-derived from the alias (or <source>[<Transform>] when the alias is empty).

Catalogue

Built-in transforms ported from plotjuggler_app/transforms/* (pure C++ in transforms.hpp):

  • Absolute, Scale/Offset
  • Derivative, Integral
  • Moving Average, Moving RMS, Moving Variance
  • Outlier Removal
  • Samples Counter
  • Binary Filter
  • Time-since-previous

The catalogue is closed by design — arbitrary scripting belongs in the separate scripting toolbox.

Each transform declares its incremental look-back so streaming append-only can recompute only the bounded suffix needed for the new tail (incrementalStartIndex + hasMonotonicOutput in transforms.hpp).

Lifecycle

  • Volatile filter: the host installs the transform in the read path of the curve adapter (no Topic written to the datastore).
  • Clipboard: process-global JSON for copy/paste of a transform between curves.
  • Manifest tag: plot_action — so the plot's right-click menu launches the editor directly and the editor stays out of the global Extensions menu.

Tests

  • transforms_test — per-transform deterministic-causal + look-back correctness against full recompute. Pure C++ gtest, no Qt.

Other changes

  • data_load_mcap dialog: remove a now-redundant array-size control that is now handled by the embedded parser dialog.

Test plan

  • CI green on the matrix.
  • Manual smoke: load a CSV, apply Moving Average to a curve → see the derived series in the catalogue tree.
  • Manual smoke: copy a transform from one curve, paste onto another → same transform applies.
  • Manual smoke: streaming source (ROS bridge) with Moving Average active → incremental append, no full recompute per tick.
  • Manual smoke: multi-select 3 curves → apply Moving Average → 3 derived series created in one gesture.

@pabloinigoblasco pabloinigoblasco force-pushed the feat/toolbox-transform-editor branch 2 times, most recently from 2e345fb to f2911a4 Compare June 9, 2026 12:37
@pabloinigoblasco pabloinigoblasco changed the title feat(toolbox): add Transform Editor toolbox plugin feat(toolbox): add Filter Editor toolbox plugin Jun 10, 2026
@pabloinigoblasco pabloinigoblasco force-pushed the feat/toolbox-transform-editor branch 2 times, most recently from f37cfb2 to 584ecd8 Compare June 10, 2026 23:29
@pabloinigoblasco pabloinigoblasco force-pushed the feat/toolbox-transform-editor branch 7 times, most recently from 219a09c to c433792 Compare June 11, 2026 01:16
PJ3's 'Apply filter to data' as a PJ4 toolbox panel: multi-curve source
selection, closed predefined-transform catalogue, live preview, optional
alias, copy/paste clipboard. Launches from the plot right-click via the
manifest plot_action tag.

Strategy pattern across the plugin / SDK boundary:
  - Contract (plotjuggler_sdk#120 pj_plugins/filter_protocol/):
    abstract PJ::sdk::FilterTransform + FilterTransformFactory.
  - Strategies (this plugin, builtin_transforms.hpp): 12 concrete classes
    deriving from PJ::sdk::FilterTransform; SeriesAccessor for the
    dialog preview path is plugin-local too.
  - Registration: registerAllTransforms() at loaderInit.

Tests (builtin_transforms_test.cpp): per-transform deterministic-causal
+ bit-identity. Pure C++ gtest, no Qt.

Bumps extern/plotjuggler_core submodule to the contract+tags commit
(plotjuggler_sdk#120).
@pabloinigoblasco pabloinigoblasco force-pushed the feat/toolbox-transform-editor branch from c433792 to e01297a Compare June 11, 2026 01:35
pabloinigoblasco and others added 10 commits June 11, 2026 03:38
The dialog uses pj_custom_function::SeriesAccessor for its preview path,
not a SDK contract type. Include the engine header and drop the stale
PJ::sdk:: qualifier.
….hpp

The dialog's preview path needs a plain timestamp/value view next to the
BinaryOp enum that already lives in PJ::sdk inside this plugin header.
Restoring it here avoids pulling sol2 through custom_function_engine.hpp
(unavailable in the per-plugin-build sandbox), and matches the BinaryOp
'plugin-private type living in PJ::sdk' pattern already established.
MSVC treats #pragma GCC as unknown (C4068) and -Werror promotes it to a
build error. Each push had no matching pop, so the suppression was both
non-portable and globally leaking. The (void)json_str cast already
silences unused-param on every compiler — keep that, drop the pragmas.
Write the filtered preview as a persistent time series via the toolbox
write host. Lets users materialize a transform result into a saved curve
without leaving the dialog.
…is intentional

Mirrors PJ3 behaviour where the outlier detector buffers the previous
sample to compare against the next. The one-sample output lag is by
design, not a streaming-mode regression.
Generate-time-series path still referenced the old Point name; rename
matches the SDK contract type used everywhere else in this file.
…ccessor inline

The 12 strategy classes now live in the SDK header
pj_plugins/sdk/builtin_transforms.hpp — this plugin's builtin_transforms.hpp
becomes a thin re-export so the dialog (preview/save/load) and the host (live
filter / streaming) build against the same source.

SeriesAccessor is plugin-internal (only the dialog's preview uses it) so it
moves out of the header and into the .cpp next to its only consumer.
…stry.v1

The plugin no longer owns its own FilterTransformFactory singleton. At
bind() it grabs the host-provided FilterRegistryView and registers the
12 builtin classes into the host's single factory; the dialog resolves
transforms from the same view so preview, save/load, and the host's
streaming read path all hit one source of truth.

- bind() override: walk the catalogue of 12 strategies (NoneTransform
  through TimeSincePreviousTransform), registering each via
  view.registerTransform<Class>("id", {}).
- FilterEditorDialog: hold a FilterRegistryView member, set by the
  toolbox after bind. All transform_ creations now go through
  registry_view_.create(); transform_ moves from unique_ptr to
  shared_ptr to match the view's return type.
- getDialog(): drop registerAllTransforms() — the registration now
  happens once at bind, not on every panel open.
- Drop the local FilterTransformFactory / registerAllTransforms
  using-decls — both are gone from the SDK header.
@pabloinigoblasco pabloinigoblasco changed the title feat(toolbox): add Filter Editor toolbox plugin [SUPERSEDED by #149] feat(toolbox): add Filter Editor toolbox plugin Jun 12, 2026
@pabloinigoblasco

Copy link
Copy Markdown
Contributor Author

Superseded by #149 — Filter Editor toolbox plugin. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants