Skip to content

[SUPERSEDED by #122] feat: FilterTransform contract; lift Point2; tags#120

Closed
pabloinigoblasco wants to merge 6 commits into
mainfrom
feat/filter-transform-contract-sdk
Closed

[SUPERSEDED by #122] feat: FilterTransform contract; lift Point2; tags#120
pabloinigoblasco wants to merge 6 commits into
mainfrom
feat/filter-transform-contract-sdk

Conversation

@pabloinigoblasco

@pabloinigoblasco pabloinigoblasco commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three additive changes that together close PR #160's 160-B (duplicated transform catalogue + drift) and 160-F (hardcoded plugin id in the host) review items, plus a vocab-type cleanup that drops the SDK's accidental double-definition of a 2D point.

1. pj_plugins/filter_protocol/ — Filter Editor Strategy contract

A new header-only sub-module exposing the abstract contract plugins implement for Filter Editor transforms. The SDK owns the interface + registry; concrete strategies (MovingAverage, Derivative, …) are provided by the plugin that ships them (today: pj-official-plugins/toolbox_filter_editor in pj-official-plugins#141).

  • pj_plugins/sdk/filter_transform.hpp — abstract PJ::sdk::FilterTransform. Two streaming surfaces: calculateNextPoint (SISO, PJ3 mirror) and appendTail (chunked; default loops calculateNextPoint; override per-transform when running state makes it O(Δsamples)). Plus saveParams/loadParams JSON, and clone() for host hand-off across the plugin DSO boundary.
  • pj_plugins/sdk/filter_transform_factory.hpp — registry singleton with stable registration order. PJ_REGISTER_FILTER_TRANSFORM(Class) macro for auto-register at static init.

pj_filter_sdk wires into the plugin SDK umbrella so plugin authors get the contract transitively from plotjuggler_sdk::plugin_sdk.

2. pj_base/point2.hpp — lift Point2 out of image_annotations.hpp

PJ::sdk::Point2 lived inside pj_base/builtin/image_annotations.hpp as a 2D-pixel-coordinates point. The Filter Editor contract needs the same plain {double x; double y;} shape for time/value samples but has no relation to image annotations — including that header just for the type would be the wrong kind of coupling.

This PR moves the struct to its own pj_base/point2.hpp. image_annotations.hpp now #includes the new header. The struct definition + ABI layout are unchanged; the only difference is the file location. abidiff shows additions only.

3. Surface manifest tags in RuntimeToolboxPlugin

PluginDescriptor already had capabilities: vector<string> (parsed from the manifest's "capabilities" key). It did not parse the manifest's "tags" array — Pmarin's plot_action tag in the Filter Editor plugin's manifest.json was dead metadata.

This PR adds a parallel tags: vector<string> field, parsed from the manifest's "tags" array, surfaced on PluginDescriptor and on RuntimeToolboxPlugin. Empty when the manifest omits the array (fully backward compatible).

This lets the host route generic actions — e.g. the plot's right-click "Apply Filter…" slot in PJ4#161 — to whichever installed toolbox declares the matching tag, instead of hardcoding a plugin id in the host. Closes 160-F.

Stack

  • pj-official-plugins#141 — the 12 concrete FilterTransform strategies, derived from PJ::sdk::FilterTransform, registered via the factory at loaderInit. Manifest carries "tags": ["plot_action", ...].
  • PJ4#161 — the host adapter consumes shared_ptr<sdk::FilterTransform> directly; the parallel IncrementalCurveTransform host hierarchy is removed; launchFilterEditor routes by plot_action tag instead of literal plugin id.

Both stack on this PR. Their CI on the matrix is red until this lands.

Versioning

Additive — no existing header, struct layout, or ABI signature changes. Required release level when merged: MINOR. Version bump intentionally NOT included; coordinated at release-tag time per the SDK versioning policy.

Test plan

  • pre-commit (clang-format) green locally.
  • Linux / macOS / Windows builds green on this branch.
  • abidiff against the previous baseline shows additions only.
  • Plugin author check: #include "pj_plugins/sdk/filter_transform.hpp" resolves transitively from plotjuggler_sdk::plugin_sdk.
  • Plugin author check: a toolbox manifest with "tags": ["plot_action"] shows up in RuntimeToolboxPlugin::tags after scanDirectory.

@pabloinigoblasco pabloinigoblasco force-pushed the feat/filter-transform-contract-sdk branch 5 times, most recently from e70458e to 8bee0fb Compare June 11, 2026 01:09
@pabloinigoblasco pabloinigoblasco changed the title feat(filter_protocol): unified FilterTransform SDK contract feat: FilterTransform contract; lift Point2 to pj_base; surface manifest tags Jun 11, 2026
…t tags

Add pj_plugins/filter_protocol/ — a header-only sub-module exposing
the Strategy contract for Filter Editor transforms. The SDK ships
only the abstract interface + registry; plugins provide the concrete
strategies and register them with the factory at load time.

Headers:

  - pj_plugins/sdk/filter_transform.hpp — abstract
    PJ::sdk::FilterTransform. Two streaming surfaces: calculateNextPoint
    (SISO, PJ3 mirror) and appendTail (chunked; default loops
    calculateNextPoint; override per-transform when running state
    makes it O(Δsamples)). Plus saveParams / loadParams JSON, and
    clone() for host hand-off across the plugin DSO boundary.

  - pj_plugins/sdk/filter_transform_factory.hpp — registry singleton,
    stable registration order, plus PJ_REGISTER_FILTER_TRANSFORM macro
    for auto-register at static init.

Lift PJ::sdk::Point2 to its own header:

  - pj_base/point2.hpp — generic 2D vocab type (double x, double y).
    Was defined inside image_annotations.hpp; promote so the Filter
    Editor transform contract can reuse it without an artificial
    coupling to image annotations. image_annotations.hpp now just
    includes the new header.

Surface manifest tags:

  - PluginDescriptor / RuntimeToolboxPlugin gain a tags field,
    parsed from the manifest's tags array. Lets the host route a
    generic action — e.g. the plot's right-click 'Apply Filter…'
    slot — to whichever installed toolbox declares the matching tag,
    instead of hardcoding a plugin id in the host. Empty for plugins
    whose manifest omits the array (backward compatible).

The pj_filter_sdk INTERFACE target is declared inline in
pj_plugins/CMakeLists.txt (alongside dialog_protocol). It's a
header-only sub-module with one target, so a dedicated
filter_protocol/CMakeLists.txt was overhead — kept the layout to
match dialog_protocol/ for header organisation, but the build wiring
is now where it belongs.

Wires pj_filter_sdk into the plugin SDK umbrella so plugin authors
get the contract transitively from plotjuggler_sdk::plugin_sdk.

Additive surface — no existing header / struct / ABI changes
(Point2's struct definition + ABI layout are unchanged; only its
file location moved). Required release level when merged: MINOR.
Version bump kept out of this PR for release coordination.
@pabloinigoblasco pabloinigoblasco force-pushed the feat/filter-transform-contract-sdk branch from 8bee0fb to 421ad1b Compare June 11, 2026 01:11
pabloinigoblasco and others added 5 commits June 12, 2026 11:57
The Filter Editor plugin and the host (PJ4 app) need the same math to
keep preview, layout-restore, and streaming render in lockstep. Until
now each side had its own copy. This commit lifts the 12 concrete
strategy classes plus the BinaryOp enum and the registerAllTransforms()
factory hook into the SDK header so there is one source of truth.

Plugins are dlopen'd RTLD_LOCAL, so each DSO still has its own factory
singleton — what is unified is the code, not the runtime instances.
JSON saveParams/loadParams remains the on-the-wire contract between
plugin and host.

Header is part of pj_filter_sdk; reaches plugin authors transitively
via plotjuggler_sdk::plugin_sdk (no CMake changes needed).
Adds the pj.filter_registry.v1 host service so the plugin can register
its FilterTransform classes into the host's single FilterTransformFactory
during loaderInit and resolve them by id from the same registry — preview,
layout restore, and the streaming read path all go through the same
instances now.

- filter_registry_abi.h: 5-slot C ABI vtable + service id + min-size pin.
  registration carries paired (factory_fn, deleter_fn) so destruction
  happens in the DSO that did the new; library_owner shared_ptr<void>
  pins that DSO for the entry's life.
- filter_registry_service.hpp: typed View + Traits. registerTransform<C>()
  generates the trampolines from a C++ class.
- filter_transform_factory.hpp: drop the global static singleton. Each
  entry now carries (create_fn, delete_fn, library_owner shared_ptr<void>).
  create() returns shared_ptr<FilterTransform> whose deleter pins owner.
- builtin_transforms.hpp: drop registerAllTransforms() helper — the
  plugin walks the catalogue itself in loaderInit now.
- include/pj_plugins/host/filter_registry_host.hpp: C-ABI adapter that
  wraps an in-process FilterTransformFactory as the service ctx. Trampolines
  bridge the C ABI to the C++ factory; an optional LibraryOwnerResolver
  bridges plugin-side void* tokens to host-side shared_ptr<void>.

pj_filter_sdk now links pj_base for the C ABI primitives.
…casts

Two compile errors in the previous commit:

1. std::function<void(FilterTransform*) noexcept> is ill-formed under
   C++20: std::function does not specialise on noexcept function types.
   Drop the qualifier; the no-throw contract is enforced at the C ABI
   boundary instead (PJ_filter_transform_deleter_fn is still noexcept).
2. The View's generated factory lambda did not declare noexcept, so the
   conversion to PJ_filter_transform_factory_fn (a noexcept function
   pointer) failed under conformant ABIs. Add the qualifier.

Verified with a g++ -std=c++20 smoke-test round-trip: registerHost ->
service fat pointer -> View::registerTransform<ScaleTransform> ->
View::create -> loadParams -> calculateNextPoint returns the expected
value.
@pabloinigoblasco pabloinigoblasco changed the title feat: FilterTransform contract; lift Point2 to pj_base; surface manifest tags [SUPERSEDED by #122] feat: FilterTransform contract; lift Point2; tags Jun 12, 2026
@pabloinigoblasco

Copy link
Copy Markdown
Collaborator Author

Superseded by #122 — Filter Editor SDK contract + registry service. 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