From a4091ca1efbd64b625f085a6d9e05e39c85f8fed Mon Sep 17 00:00:00 2001 From: Hannes Suhr Date: Mon, 16 Mar 2026 21:49:22 +0100 Subject: [PATCH] =?UTF-8?q?rename:=20FastPlot=20=E2=86=92=20FastSense?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename project from FastPlot to FastSense to better reflect the sensor monitoring and dashboarding platform it has become. - Rename all FastPlot* classes to FastSense* - Rename libs/FastPlot/ directory to libs/FastSense/ - Rename Python bridge package fastplot_bridge to fastsense_bridge - Update MEX C source error identifiers - Update all tests, examples, benchmarks, docs, wiki, CI - Update runtime string keys (UserData, appdata, tags) - Update setup.m, README.md, CITATION.cff No functional changes — pure rename operation. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/benchmark.yml | 6 +- .github/workflows/generate-docs.yml | 2 +- .github/workflows/release.yml | 6 +- .github/workflows/wiki-links.yml | 4 +- CITATION.cff | 6 +- README.md | 46 +-- benchmarks/benchmark.m | 42 +-- benchmarks/benchmark_datastore.m | 6 +- benchmarks/benchmark_features.m | 30 +- benchmarks/benchmark_memory.m | 2 +- benchmarks/benchmark_zoom.m | 22 +- benchmarks/profile_datastore.m | 14 +- bridge/python/fastplot_bridge/__init__.py | 1 - bridge/python/fastsense_bridge/__init__.py | 1 + .../__main__.py | 8 +- .../blob_decoder.py | 0 .../server.py | 6 +- .../sqlite_reader.py | 4 +- .../tcp_client.py | 0 bridge/python/pyproject.toml | 4 +- bridge/python/tests/test_blob_decoder.py | 2 +- bridge/python/tests/test_server.py | 4 +- bridge/python/tests/test_sqlite_reader.py | 6 +- bridge/python/tests/test_tcp_client.py | 2 +- bridge/web/css/style.css | 2 +- bridge/web/index.html | 4 +- bridge/web/js/chart.js | 2 +- bridge/web/js/widgets.js | 6 +- ...sign.md => 2026-03-06-fastsense-design.md} | 26 +- docs/generate_readme_images.m | 24 +- .../2026-03-06-dashboard-visual-design.md | 66 ++-- .../plans/2026-03-06-dashboard-visual-impl.md | 312 +++++++++--------- ...=> 2026-03-06-fastsense-implementation.md} | 274 +++++++-------- docs/plans/2026-03-06-lazy-pyramid-design.md | 10 +- .../2026-03-06-mex-acceleration-design.md | 8 +- ...6-03-06-mex-acceleration-implementation.md | 94 +++--- docs/plans/2026-03-07-datetime-design.md | 20 +- docs/plans/2026-03-07-datetime.md | 52 +-- .../2026-03-09-event-detection-design.md | 6 +- docs/plans/2026-03-09-event-detection-plan.md | 48 +-- ...09-sensor-threshold-optimization-design.md | 8 +- ...026-03-09-sensor-threshold-optimization.md | 38 +-- ...2026-03-09-unified-threshold-api-design.md | 4 +- .../plans/2026-03-09-unified-threshold-api.md | 36 +- .../2026-03-09-violation-cull-mex-design.md | 4 +- docs/plans/2026-03-09-violation-cull-mex.md | 48 +-- ...026-03-09-violation-marker-optimization.md | 40 +-- .../2026-03-09-violations-toggle-button.md | 84 ++--- .../2026-03-10-live-event-pipeline-design.md | 4 +- docs/plans/2026-03-10-live-event-pipeline.md | 22 +- .../2026-03-11-eventdetection-optimization.md | 20 +- .../plans/2026-03-11-sensor-detail-plot.md | 132 ++++---- .../plans/2026-03-13-web-bridge-mvp.md | 164 ++++----- .../plans/2026-03-16-ci-readme-wiki.md | 72 ++-- .../2026-03-16-dashboard-widget-rework.md | 70 ++-- .../2026-03-11-sensor-detail-plot-design.md | 54 +-- .../specs/2026-03-13-web-bridge-design.md | 16 +- .../specs/2026-03-16-ci-readme-wiki-design.md | 12 +- ...26-03-16-dashboard-widget-rework-design.md | 14 +- examples/demo_all.m | 12 +- examples/example_100M.m | 6 +- examples/example_alarm_bands.m | 4 +- examples/example_basic.m | 8 +- examples/example_dashboard.m | 12 +- examples/example_dashboard_9tile.m | 4 +- examples/example_dashboard_all_widgets.m | 10 +- examples/example_dashboard_engine.m | 8 +- examples/example_dashboard_live.m | 10 +- examples/example_datetime.m | 14 +- examples/example_disk_storage.m | 16 +- examples/example_dock.m | 16 +- examples/example_dock_disk.m | 14 +- examples/example_dock_many_tabs.m | 6 +- examples/example_dynamic_thresholds_100M.m | 4 +- examples/example_ecg.m | 4 +- examples/example_event_detection_live.m | 18 +- examples/example_linked.m | 10 +- examples/example_live_pipeline.m | 6 +- examples/example_lttb_vs_minmax.m | 6 +- examples/example_mixed_tiles.m | 14 +- examples/example_multi.m | 6 +- examples/example_multi_sensor_linked.m | 10 +- examples/example_nan_gaps.m | 6 +- examples/example_sensor_dashboard.m | 8 +- examples/example_sensor_detail.m | 12 +- examples/example_sensor_detail_dashboard.m | 10 +- examples/example_sensor_detail_dock.m | 22 +- examples/example_sensor_multi_state.m | 4 +- examples/example_sensor_registry.m | 4 +- examples/example_sensor_static.m | 2 +- examples/example_sensor_threshold.m | 6 +- examples/example_sensor_todisk.m | 10 +- examples/example_stress_test.m | 18 +- examples/example_themes.m | 4 +- examples/example_toolbar.m | 14 +- examples/example_uneven_sampling.m | 6 +- examples/example_vibration.m | 4 +- examples/example_visual_features.m | 4 +- ..._fastplot.m => example_widget_fastsense.m} | 20 +- examples/example_widget_number.m | 4 +- examples/example_widget_status.m | 8 +- examples/run_all_examples.m | 8 +- libs/Dashboard/DashboardBuilder.m | 14 +- libs/Dashboard/DashboardEngine.m | 12 +- libs/Dashboard/DashboardSerializer.m | 16 +- libs/Dashboard/DashboardTheme.m | 8 +- libs/Dashboard/DashboardWidget.m | 2 +- .../{FastPlotWidget.m => FastSenseWidget.m} | 48 +-- libs/EventDetection/EventViewer.m | 4 +- libs/EventDetection/NotificationService.m | 6 +- libs/EventDetection/generateEventSnapshot.m | 2 +- libs/EventDetection/private/parseOpts.m | 6 +- .../ConsoleProgressBar.m | 2 +- .../FastPlot.m => FastSense/FastSense.m} | 266 +++++++-------- .../FastSenseDataStore.m} | 34 +- .../FastSenseDefaults.m} | 14 +- .../FastSenseDock.m} | 78 ++--- .../FastSenseGrid.m} | 106 +++--- .../FastSenseTheme.m} | 28 +- .../FastSenseToolbar.m} | 192 +++++------ .../NavigatorOverlay.m | 0 .../SensorDetailPlot.m | 38 +-- libs/{FastPlot => FastSense}/binary_search.m | 0 libs/{FastPlot => FastSense}/build_mex.m | 2 +- libs/{FastPlot => FastSense}/mksqlite.c | 0 .../private/binary_search.m | 2 +- .../private/clearDefaultsCache.m | 12 +- .../private/compute_violations.m | 4 +- .../private/compute_violations_dynamic.m | 4 +- .../private/downsample_violations.m | 4 +- .../private/getDefaults.m | 28 +- .../private/loadMetaStruct.m | 6 +- .../private/lttb_downsample.m | 2 +- .../private/mergeTheme.m | 6 +- .../private/mex_src/binary_search_mex.c | 8 +- .../private/mex_src/build_store_mex.c | 28 +- .../private/mex_src/compute_violations_mex.c | 2 +- .../private/mex_src/lttb_core_mex.c | 4 +- .../private/mex_src/minmax_core_mex.c | 4 +- .../private/mex_src/resolve_disk_mex.c | 10 +- .../private/mex_src/simd_utils.h | 2 +- .../private/mex_src/sqlite3.c | 0 .../private/mex_src/sqlite3.h | 0 .../private/mex_src/violation_cull_mex.c | 2 +- .../private/minmax_downsample.m | 2 +- .../private/parseOpts.m | 6 +- .../private/resolveTheme.m | 20 +- .../private/struct2nvpairs.m | 2 +- .../private/violation_cull.m | 2 +- libs/SensorThreshold/Sensor.m | 12 +- .../private/compute_violations_disk.m | 4 +- libs/WebBridge/WebBridge.m | 8 +- scripts/generate_api_docs.py | 26 +- scripts/run_tests_with_coverage.m | 2 +- setup.m | 12 +- ...te_path.m => add_fastsense_private_path.m} | 8 +- tests/run_all_tests.m | 6 +- tests/suite/TestAddBand.m | 14 +- tests/suite/TestAddLine.m | 18 +- tests/suite/TestAddMarker.m | 12 +- tests/suite/TestAddSensor.m | 10 +- tests/suite/TestAddShaded.m | 24 +- tests/suite/TestAddThreshold.m | 16 +- tests/suite/TestBinarySearch.m | 2 +- tests/suite/TestComputeViolations.m | 2 +- tests/suite/TestComputeViolationsDynamic.m | 2 +- tests/suite/TestDashboardEngine.m | 24 +- tests/suite/TestDashboardSerializer.m | 12 +- tests/suite/TestDashboardTheme.m | 12 +- tests/suite/TestDataStoreWAL.m | 6 +- tests/suite/TestDatastore.m | 40 +-- tests/suite/TestDatastoreEdgeCases.m | 44 +-- tests/suite/TestDatetime.m | 16 +- tests/suite/TestDiskAdvanced.m | 34 +- tests/suite/TestDiskStorage.m | 44 +-- tests/suite/TestDownsampleViolations.m | 2 +- tests/suite/TestEventSnapshot.m | 2 +- ...stFastplotTheme.m => TestFastSenseTheme.m} | 26 +- ...FastPlotWidget.m => TestFastSenseWidget.m} | 42 +-- tests/suite/TestFigureLayout.m | 48 +-- tests/suite/TestLinkedAxes.m | 10 +- tests/suite/TestLivePipeline.m | 2 +- tests/suite/TestLttbDownsample.m | 2 +- tests/suite/TestMexEdgeCases.m | 2 +- tests/suite/TestMexParity.m | 2 +- tests/suite/TestMinmaxDownsample.m | 2 +- tests/suite/TestMksqliteEdgeCases.m | 2 +- tests/suite/TestMksqliteTypes.m | 42 +-- tests/suite/TestMultiThreshold.m | 20 +- tests/suite/TestNotificationService.m | 2 +- tests/suite/TestRender.m | 26 +- tests/suite/TestSensorDetailPlot.m | 22 +- tests/suite/TestSensorTodisk.m | 4 +- tests/suite/TestTheme.m | 26 +- tests/suite/TestToolbar.m | 62 ++-- tests/suite/TestViolationCullMex.m | 2 +- tests/suite/TestViolationsMexParity.m | 2 +- tests/suite/TestWebBridgeE2E.m | 6 +- tests/suite/TestZoomPan.m | 8 +- tests/test_NavigatorOverlay.m | 2 +- tests/test_SensorDetailPlot.m | 24 +- tests/test_add_band.m | 16 +- tests/test_add_line.m | 18 +- tests/test_add_marker.m | 14 +- tests/test_add_sensor.m | 10 +- tests/test_add_shaded.m | 26 +- tests/test_add_threshold.m | 16 +- tests/test_binary_search.m | 4 +- tests/test_compute_violations.m | 2 +- tests/test_compute_violations_dynamic.m | 2 +- tests/test_dashboard_builder_interaction.m | 2 +- tests/test_datastore.m | 38 +-- tests/test_datastore_edge_cases.m | 42 +-- tests/test_datetime.m | 16 +- tests/test_disk_advanced.m | 34 +- tests/test_disk_storage.m | 44 +-- tests/test_downsample_violations.m | 2 +- tests/test_event_snapshot.m | 2 +- ...astplot_theme.m => test_fastsense_theme.m} | 28 +- tests/test_figure_layout.m | 50 +-- tests/test_linked_axes.m | 10 +- tests/test_live_pipeline.m | 2 +- tests/test_lttb_downsample.m | 2 +- tests/test_mex_edge_cases.m | 2 +- tests/test_mex_parity.m | 2 +- tests/test_minmax_downsample.m | 2 +- tests/test_mksqlite_types.m | 12 +- tests/test_multi_threshold.m | 20 +- tests/test_notification_service.m | 2 +- tests/test_render.m | 28 +- tests/test_sensor_todisk.m | 4 +- tests/test_theme.m | 28 +- tests/test_toolbar.m | 64 ++-- tests/test_violation_cull_mex.m | 2 +- tests/test_violations_mex_parity.m | 2 +- tests/test_zoom_pan.m | 8 +- 236 files changed, 2403 insertions(+), 2403 deletions(-) delete mode 100644 bridge/python/fastplot_bridge/__init__.py create mode 100644 bridge/python/fastsense_bridge/__init__.py rename bridge/python/{fastplot_bridge => fastsense_bridge}/__main__.py (91%) rename bridge/python/{fastplot_bridge => fastsense_bridge}/blob_decoder.py (100%) rename bridge/python/{fastplot_bridge => fastsense_bridge}/server.py (97%) rename bridge/python/{fastplot_bridge => fastsense_bridge}/sqlite_reader.py (98%) rename bridge/python/{fastplot_bridge => fastsense_bridge}/tcp_client.py (100%) rename docs/{2026-03-06-fastplot-design.md => 2026-03-06-fastsense-design.md} (93%) rename docs/plans/{2026-03-06-fastplot-implementation.md => 2026-03-06-fastsense-implementation.md} (89%) rename examples/{example_widget_fastplot.m => example_widget_fastsense.m} (86%) rename libs/Dashboard/{FastPlotWidget.m => FastSenseWidget.m} (86%) rename libs/{FastPlot => FastSense}/ConsoleProgressBar.m (99%) rename libs/{FastPlot/FastPlot.m => FastSense/FastSense.m} (94%) rename libs/{FastPlot/FastPlotDataStore.m => FastSense/FastSenseDataStore.m} (97%) rename libs/{FastPlot/FastPlotDefaults.m => FastSense/FastSenseDefaults.m} (93%) rename libs/{FastPlot/FastPlotDock.m => FastSense/FastSenseDock.m} (93%) rename libs/{FastPlot/FastPlotGrid.m => FastSense/FastSenseGrid.m} (91%) rename libs/{FastPlot/FastPlotTheme.m => FastSense/FastSenseTheme.m} (92%) rename libs/{FastPlot/FastPlotToolbar.m => FastSense/FastSenseToolbar.m} (89%) rename libs/{FastPlot => FastSense}/NavigatorOverlay.m (100%) rename libs/{FastPlot => FastSense}/SensorDetailPlot.m (95%) rename libs/{FastPlot => FastSense}/binary_search.m (100%) rename libs/{FastPlot => FastSense}/build_mex.m (99%) rename libs/{FastPlot => FastSense}/mksqlite.c (100%) rename libs/{FastPlot => FastSense}/private/binary_search.m (97%) rename libs/{FastPlot => FastSense}/private/clearDefaultsCache.m (58%) rename libs/{FastPlot => FastSense}/private/compute_violations.m (94%) rename libs/{FastPlot => FastSense}/private/compute_violations_dynamic.m (97%) rename libs/{FastPlot => FastSense}/private/downsample_violations.m (97%) rename libs/{FastPlot => FastSense}/private/getDefaults.m (73%) rename libs/{FastPlot => FastSense}/private/loadMetaStruct.m (92%) rename libs/{FastPlot => FastSense}/private/lttb_downsample.m (99%) rename libs/{FastPlot => FastSense}/private/mergeTheme.m (88%) rename libs/{FastPlot => FastSense}/private/mex_src/binary_search_mex.c (90%) rename libs/{FastPlot => FastSense}/private/mex_src/build_store_mex.c (92%) rename libs/{FastPlot => FastSense}/private/mex_src/compute_violations_mex.c (99%) rename libs/{FastPlot => FastSense}/private/mex_src/lttb_core_mex.c (97%) rename libs/{FastPlot => FastSense}/private/mex_src/minmax_core_mex.c (96%) rename libs/{FastPlot => FastSense}/private/mex_src/resolve_disk_mex.c (97%) rename libs/{FastPlot => FastSense}/private/mex_src/simd_utils.h (98%) rename libs/{FastPlot => FastSense}/private/mex_src/sqlite3.c (100%) rename libs/{FastPlot => FastSense}/private/mex_src/sqlite3.h (100%) rename libs/{FastPlot => FastSense}/private/mex_src/violation_cull_mex.c (99%) rename libs/{FastPlot => FastSense}/private/minmax_downsample.m (99%) rename libs/{FastPlot => FastSense}/private/parseOpts.m (94%) rename libs/{FastPlot => FastSense}/private/resolveTheme.m (65%) rename libs/{FastPlot => FastSense}/private/struct2nvpairs.m (94%) rename libs/{FastPlot => FastSense}/private/violation_cull.m (98%) rename tests/{add_fastplot_private_path.m => add_fastsense_private_path.m} (79%) rename tests/suite/{TestFastplotTheme.m => TestFastSenseTheme.m} (86%) rename tests/suite/{TestFastPlotWidget.m => TestFastSenseWidget.m} (72%) rename tests/{test_fastplot_theme.m => test_fastsense_theme.m} (83%) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index c0a3f620..e6d48f19 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -24,7 +24,7 @@ jobs: run: | xvfb-run octave --eval " addpath(pwd); setup(); - addpath(fullfile(pwd, 'libs', 'FastPlot', 'private')); + addpath(fullfile(pwd, 'libs', 'FastSense', 'private')); % Quick benchmark: 1M points, 10 zooms n = 1e6; @@ -46,7 +46,7 @@ jobs: t_bs = toc / 1000; % Measure full render cycle - fp = FastPlot(); + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'bench'); fp.addThreshold(1.5, 'Direction', 'upper', 'ShowViolations', true); fp.render(); @@ -79,7 +79,7 @@ jobs: - name: Store benchmark results uses: benchmark-action/github-action-benchmark@v1 with: - name: FastPlot Performance + name: FastSense Performance tool: customSmallerIsBetter output-file-path: benchmark-results.json github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index dfe7cb16..079551c2 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -27,7 +27,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - git clone "https://x-access-token:${GITHUB_TOKEN}@github.com/HanSur94/FastPlot.wiki.git" wiki + git clone "https://x-access-token:${GITHUB_TOKEN}@github.com/HanSur94/FastSense.wiki.git" wiki - name: Generate API docs run: python3 scripts/generate_api_docs.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index daa9e8df..396c160f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,7 +51,7 @@ jobs: - name: Package release run: | VERSION="${{ steps.version.outputs.VERSION }}" - DIRNAME="FastPlot-${VERSION}" + DIRNAME="FastSense-${VERSION}" mkdir -p "${DIRNAME}" # Copy release contents (allowlist — excludes tests/, benchmarks/, docs/, bridge/, private/, .git/) @@ -82,5 +82,5 @@ jobs: Download the archive, extract it, and run `setup` in MATLAB/Octave to add libraries to path and compile MEX accelerators. files: | - FastPlot-${{ steps.version.outputs.VERSION }}.tar.gz - FastPlot-${{ steps.version.outputs.VERSION }}.zip + FastSense-${{ steps.version.outputs.VERSION }}.tar.gz + FastSense-${{ steps.version.outputs.VERSION }}.zip diff --git a/.github/workflows/wiki-links.yml b/.github/workflows/wiki-links.yml index bd2295f5..40278431 100644 --- a/.github/workflows/wiki-links.yml +++ b/.github/workflows/wiki-links.yml @@ -18,7 +18,7 @@ jobs: - name: Clone wiki run: | - git clone https://github.com/HanSur94/FastPlot.wiki.git wiki-check + git clone https://github.com/HanSur94/FastSense.wiki.git wiki-check - name: Check markdown links uses: lycheeverse/lychee-action@v2 @@ -26,7 +26,7 @@ jobs: args: >- --no-progress --exclude-loopback - --exclude 'github.com/HanSur94/FastPlot/wiki' + --exclude 'github.com/HanSur94/FastSense/wiki' --suggest wiki-check/*.md fail: true diff --git a/CITATION.cff b/CITATION.cff index a5880490..24226621 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,11 +1,11 @@ cff-version: 1.2.0 -message: "If you use FastPlot in your research, please cite it as below." -title: "FastPlot: Ultra-Fast Time Series Plotting for MATLAB and GNU Octave" +message: "If you use FastSense in your research, please cite it as below." +title: "FastSense: Ultra-Fast Time Series Plotting for MATLAB and GNU Octave" type: software authors: - family-names: Suhr given-names: Hannes -repository-code: "https://github.com/HanSur94/FastPlot" +repository-code: "https://github.com/HanSur94/FastSense" license: MIT keywords: - MATLAB diff --git a/README.md b/README.md index 49b59c2c..530b1ca2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# FastPlot +# FastSense -[![Tests](https://github.com/HanSur94/FastPlot/actions/workflows/tests.yml/badge.svg)](https://github.com/HanSur94/FastPlot/actions/workflows/tests.yml) -[![codecov](https://codecov.io/gh/HanSur94/FastPlot/graph/badge.svg)](https://codecov.io/gh/HanSur94/FastPlot) +[![Tests](https://github.com/HanSur94/FastSense/actions/workflows/tests.yml/badge.svg)](https://github.com/HanSur94/FastSense/actions/workflows/tests.yml) +[![codecov](https://codecov.io/gh/HanSur94/FastSense/graph/badge.svg)](https://codecov.io/gh/HanSur94/FastSense) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![MATLAB](https://img.shields.io/badge/MATLAB-R2020b%2B-orange.svg)](https://www.mathworks.com/products/matlab.html) [![Octave](https://img.shields.io/badge/GNU%20Octave-7%2B-blue.svg)](https://octave.org) @@ -9,7 +9,7 @@ Ultra-fast time series plotting for MATLAB and GNU Octave. Plot 100M+ data points with fluid zoom and pan — rendering only ~4,000 points at any zoom level.

- FastPlot Dashboard + FastSense Dashboard

## Performance @@ -47,7 +47,7 @@ setup; % adds libraries to path + compiles MEX x = linspace(0, 100, 1e7); y = sin(x) + 0.1 * randn(size(x)); -fp = FastPlot('Theme', 'dark'); +fp = FastSense('Theme', 'dark'); fp.addLine(x, y, 'DisplayName', 'Sensor'); fp.addThreshold(0.8, 'Direction', 'upper', 'ShowViolations', true); fp.render(); @@ -57,8 +57,8 @@ fp.render(); ## Installation ```bash -git clone https://github.com/HanSur94/FastPlot.git -cd FastPlot +git clone https://github.com/HanSur94/FastSense.git +cd FastSense ``` Then in MATLAB or Octave: @@ -73,26 +73,26 @@ No toolbox dependencies. MEX compilation is optional — pure MATLAB fallbacks a ## Documentation -Full documentation is available in the [Wiki](https://github.com/HanSur94/FastPlot/wiki): +Full documentation is available in the [Wiki](https://github.com/HanSur94/FastSense/wiki): -- [Getting Started](https://github.com/HanSur94/FastPlot/wiki/Getting-Started) — tutorial with examples -- [API Reference: FastPlot](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-FastPlot) — core plotting class -- [API Reference: Dashboard](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-Dashboard) — layouts, widgets, engine -- [API Reference: Sensors](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-Sensors) — sensor system -- [API Reference: Event Detection](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-Event-Detection) — event pipeline -- [Architecture](https://github.com/HanSur94/FastPlot/wiki/Architecture) — render pipeline, data flow -- [MEX Acceleration](https://github.com/HanSur94/FastPlot/wiki/MEX-Acceleration) — SIMD details -- [Performance](https://github.com/HanSur94/FastPlot/wiki/Performance) — benchmarks +- [Getting Started](https://github.com/HanSur94/FastSense/wiki/Getting-Started) — tutorial with examples +- [API Reference: FastSense](https://github.com/HanSur94/FastSense/wiki/API-Reference:-FastSense) — core plotting class +- [API Reference: Dashboard](https://github.com/HanSur94/FastSense/wiki/API-Reference:-Dashboard) — layouts, widgets, engine +- [API Reference: Sensors](https://github.com/HanSur94/FastSense/wiki/API-Reference:-Sensors) — sensor system +- [API Reference: Event Detection](https://github.com/HanSur94/FastSense/wiki/API-Reference:-Event-Detection) — event pipeline +- [Architecture](https://github.com/HanSur94/FastSense/wiki/Architecture) — render pipeline, data flow +- [MEX Acceleration](https://github.com/HanSur94/FastSense/wiki/MEX-Acceleration) — SIMD details +- [Performance](https://github.com/HanSur94/FastSense/wiki/Performance) — benchmarks ## Examples -See the [`examples/`](examples/) directory for 40+ runnable scripts covering basic plotting, dashboards, sensors, event detection, live mode, and disk-backed storage. A categorized guide is in the [wiki](https://github.com/HanSur94/FastPlot/wiki/Examples). +See the [`examples/`](examples/) directory for 40+ runnable scripts covering basic plotting, dashboards, sensors, event detection, live mode, and disk-backed storage. A categorized guide is in the [wiki](https://github.com/HanSur94/FastSense/wiki/Examples). ## Libraries | Library | Path | Description | |---------|------|-------------| -| FastPlot | `libs/FastPlot/` | Core plotting engine, layouts, toolbar, themes, disk storage | +| FastSense | `libs/FastSense/` | Core plotting engine, layouts, toolbar, themes, disk storage | | SensorThreshold | `libs/SensorThreshold/` | Sensor containers, state channels, threshold rules | | EventDetection | `libs/EventDetection/` | Event detection, viewer, live pipeline, notifications | | Dashboard | `libs/Dashboard/` | Dashboard engine with widgets and JSON persistence | @@ -100,17 +100,17 @@ See the [`examples/`](examples/) directory for 40+ runnable scripts covering bas ## Contributing -Contributions are welcome! Please open an issue to discuss your idea before submitting a pull request. See the [wiki](https://github.com/HanSur94/FastPlot/wiki) for architecture details and API references. +Contributions are welcome! Please open an issue to discuss your idea before submitting a pull request. See the [wiki](https://github.com/HanSur94/FastSense/wiki) for architecture details and API references. ## Citation -If you use FastPlot in your research, please cite it: +If you use FastSense in your research, please cite it: ```bibtex -@software{fastplot, +@software{fastsense, author = {Suhr, Hannes}, - title = {FastPlot: Ultra-Fast Time Series Plotting for MATLAB and GNU Octave}, - url = {https://github.com/HanSur94/FastPlot}, + title = {FastSense: Ultra-Fast Time Series Plotting for MATLAB and GNU Octave}, + url = {https://github.com/HanSur94/FastSense}, license = {MIT} } ``` diff --git a/benchmarks/benchmark.m b/benchmarks/benchmark.m index 24246d50..f47a3035 100644 --- a/benchmarks/benchmark.m +++ b/benchmarks/benchmark.m @@ -1,8 +1,8 @@ -%% FastPlot Benchmark — Compare FastPlot vs standard plot() +%% FastSense Benchmark — Compare FastSense vs standard plot() % Measures render time, zoom stress, point reduction, and memory footprint. % % NOTE: Octave's Qt/OpenGL backend clips lines in hardware, making raw -% plot() very fast even with millions of points. FastPlot's interpreted +% plot() very fast even with millions of points. FastSense's interpreted % downsampling adds overhead in Octave. The primary wins are: % 1. Point reduction (99%+ fewer points in the pipeline) % 2. Lower GPU memory usage (critical when datasets exceed VRAM) @@ -11,7 +11,7 @@ % In MATLAB (with JIT compilation), the downsampling cost is much lower. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); -addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastPlot', 'private')); +addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastSense', 'private')); sizes = [1e4, 1e5, 1e6, 5e6, 10e6, 50e6]; labels = {'10K', '100K', '1M', '5M', '10M', '50M'}; @@ -20,7 +20,7 @@ n_cases = numel(sizes); t_std_init = NaN(1, n_cases); % figure + axes + plot() call t_std_render = NaN(1, n_cases); % drawnow after plot() -t_fp_init = NaN(1, n_cases); % FastPlot() + addLine + addThreshold +t_fp_init = NaN(1, n_cases); % FastSense() + addLine + addThreshold t_fp_render = NaN(1, n_cases); % render() + drawnow t_std_zoom = NaN(1, n_cases); t_fp_zoom = NaN(1, n_cases); @@ -31,7 +31,7 @@ t_fp_ds = NaN(1, n_cases); % downsampling time only fprintf('================================================================\n'); -fprintf(' FastPlot Benchmark\n'); +fprintf(' FastSense Benchmark\n'); fprintf(' Initial render + %d zoom ops + point reduction + memory\n', n_zooms); fprintf('================================================================\n\n'); @@ -75,9 +75,9 @@ [~, ~] = minmax_downsample(x, y, 1000); t_fp_ds(c) = toc; - % ======== FastPlot ======== + % ======== FastSense ======== tic; - fp = FastPlot(); + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.addThreshold(1.5, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp.addThreshold(-1.5, 'Direction', 'lower', 'ShowViolations', true, 'Color', 'r'); @@ -100,13 +100,13 @@ close(fp.hFigure); reduction = (1 - pts_fp(c)/pts_std(c)) * 100; - fprintf(' Init: plot()=%7.3fs FastPlot=%7.3fs\n', t_std_init(c), t_fp_init(c)); - fprintf(' Render: plot()=%7.3fs FastPlot=%7.3fs\n', t_std_render(c), t_fp_render(c)); - fprintf(' Total: plot()=%7.3fs FastPlot=%7.3fs\n', t_std_init(c)+t_std_render(c), t_fp_init(c)+t_fp_render(c)); - fprintf(' %2d zooms: plot()=%7.3fs FastPlot=%7.3fs\n', n_zooms, t_std_zoom(c), t_fp_zoom(c)); + fprintf(' Init: plot()=%7.3fs FastSense=%7.3fs\n', t_std_init(c), t_fp_init(c)); + fprintf(' Render: plot()=%7.3fs FastSense=%7.3fs\n', t_std_render(c), t_fp_render(c)); + fprintf(' Total: plot()=%7.3fs FastSense=%7.3fs\n', t_std_init(c)+t_std_render(c), t_fp_init(c)+t_fp_render(c)); + fprintf(' %2d zooms: plot()=%7.3fs FastSense=%7.3fs\n', n_zooms, t_std_zoom(c), t_fp_zoom(c)); fprintf(' Downsample: %.3fs (pure computation, no rendering)\n', t_fp_ds(c)); - fprintf(' GPU points: plot()=%-10d FastPlot=%-6d (%.1f%% reduction)\n', pts_std(c), pts_fp(c), reduction); - fprintf(' GPU memory: plot()=%7.1f MB FastPlot=%7.3f MB\n', mem_std_MB(c), mem_fp_MB(c)); + fprintf(' GPU points: plot()=%-10d FastSense=%-6d (%.1f%% reduction)\n', pts_std(c), pts_fp(c), reduction); + fprintf(' GPU memory: plot()=%7.1f MB FastSense=%7.3f MB\n', mem_std_MB(c), mem_fp_MB(c)); fprintf('\n'); clear x y fp; @@ -130,22 +130,22 @@ fprintf('\n'); fprintf('Key takeaways:\n'); -fprintf(' - FastPlot reduces GPU pipeline data by 91-100%%\n'); +fprintf(' - FastSense reduces GPU pipeline data by 91-100%%\n'); fprintf(' - At 50M points: %.1f MB in pipeline vs %.3f MB (%.0fx reduction)\n', ... mem_std_MB(end), mem_fp_MB(end), mem_std_MB(end)/mem_fp_MB(end)); -fprintf(' - FastPlot adds threshold lines + color-coded violation markers\n'); +fprintf(' - FastSense adds threshold lines + color-coded violation markers\n'); fprintf(' - Octave Qt/OpenGL backend clips efficiently in hardware,\n'); fprintf(' so raw plot() zoom appears fast despite pushing all points.\n'); fprintf(' - In MATLAB (JIT-compiled), downsampling overhead is much lower.\n'); fprintf('\n'); % ---- Plot results ---- -fig = figure('Name', 'FastPlot Benchmark', 'Position', [100 100 1800 800]); +fig = figure('Name', 'FastSense Benchmark', 'Position', [100 100 1800 800]); subplot(2,3,1); loglog(sizes, t_std_init, 'ro-', 'LineWidth', 2, 'DisplayName', 'plot()'); hold on; -loglog(sizes, t_fp_init, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastPlot'); +loglog(sizes, t_fp_init, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastSense'); xlabel('Data points'); ylabel('Time (s)'); title('Instantiation Time'); @@ -155,7 +155,7 @@ subplot(2,3,2); loglog(sizes, t_std_render, 'ro-', 'LineWidth', 2, 'DisplayName', 'plot()'); hold on; -loglog(sizes, t_fp_render, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastPlot'); +loglog(sizes, t_fp_render, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastSense'); xlabel('Data points'); ylabel('Time (s)'); title('Render Time (drawnow)'); @@ -165,7 +165,7 @@ subplot(2,3,3); loglog(sizes, t_std_init + t_std_render, 'ro-', 'LineWidth', 2, 'DisplayName', 'plot()'); hold on; -loglog(sizes, t_fp_init + t_fp_render, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastPlot'); +loglog(sizes, t_fp_init + t_fp_render, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastSense'); xlabel('Data points'); ylabel('Time (s)'); title('Total Time (Init + Render)'); @@ -175,7 +175,7 @@ subplot(2,3,4); loglog(sizes, t_std_zoom, 'ro-', 'LineWidth', 2, 'DisplayName', 'plot()'); hold on; -loglog(sizes, t_fp_zoom, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastPlot'); +loglog(sizes, t_fp_zoom, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastSense'); loglog(sizes, t_fp_ds, 'g^--', 'LineWidth', 1.5, 'DisplayName', 'Downsample only'); xlabel('Data points'); ylabel('Time (s)'); @@ -186,7 +186,7 @@ subplot(2,3,5); loglog(sizes, mem_std_MB, 'ro-', 'LineWidth', 2, 'DisplayName', 'plot()'); hold on; -loglog(sizes, mem_fp_MB, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastPlot'); +loglog(sizes, mem_fp_MB, 'bs-', 'LineWidth', 2, 'DisplayName', 'FastSense'); xlabel('Data points'); ylabel('GPU Memory (MB)'); title('GPU Pipeline Memory'); diff --git a/benchmarks/benchmark_datastore.m b/benchmarks/benchmark_datastore.m index 04415ab9..e521faab 100644 --- a/benchmarks/benchmark_datastore.m +++ b/benchmarks/benchmark_datastore.m @@ -13,7 +13,7 @@ function benchmark_datastore() matMaxSize = 50e6; fprintf('\n============================================================\n'); - fprintf(' FastPlot DataStore Benchmark: .mat vs SQLite\n'); + fprintf(' FastSense DataStore Benchmark: .mat vs SQLite\n'); fprintf('============================================================\n\n'); hasSqlite = (exist('mksqlite', 'file') == 3); @@ -72,7 +72,7 @@ function benchmark_datastore() % --- DataStore create (SQLite or binary) --- tic; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); tDsCreate = toc; % Free source data immediately to reclaim memory @@ -181,7 +181,7 @@ function benchmark_datastore() ce = min(c + chunkSz - 1, zoomN); y(c:ce) = y(c:ce) + 0.1 * randn(1, ce - c + 1); end - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); clear x y; % Simulate 20 zoom levels from full view down to 0.01% of data diff --git a/benchmarks/benchmark_features.m b/benchmarks/benchmark_features.m index f87ca9c7..76f10ac6 100644 --- a/benchmarks/benchmark_features.m +++ b/benchmarks/benchmark_features.m @@ -1,17 +1,17 @@ -%% FastPlot Feature Benchmark — Theme, Band, Shaded, Fill, Marker, Dashboard -% Measures overhead of each new feature vs baseline FastPlot rendering. +%% FastSense Feature Benchmark — Theme, Band, Shaded, Fill, Marker, Dashboard +% Measures overhead of each new feature vs baseline FastSense rendering. % % Tests: -% 1. FastPlotTheme creation overhead +% 1. FastSenseTheme creation overhead % 2. addBand rendering cost (constant-y fill — trivial geometry) % 3. addShaded rendering + zoom cost (data-driven fill — downsampled) % 4. addFill rendering + zoom cost (area under curve) % 5. addMarker rendering cost -% 6. FastPlotGrid tiled dashboard overhead +% 6. FastSenseGrid tiled dashboard overhead % 7. Combined: all features together vs baseline addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); -addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastPlot', 'private')); +addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastSense', 'private')); sizes = [1e4, 1e5, 1e6, 5e6, 10e6]; labels = {'10K', '100K', '1M', '5M', '10M'}; @@ -21,7 +21,7 @@ % Timing arrays t_theme_create = NaN(1, 5); % theme creation (5 presets) -t_baseline = NaN(1, n_cases); % FastPlot + addLine + render +t_baseline = NaN(1, n_cases); % FastSense + addLine + render t_band = NaN(1, n_cases); % + 4 bands t_shaded = NaN(1, n_cases); % + shaded region t_fill = NaN(1, n_cases); % + fill region @@ -34,7 +34,7 @@ t_combined_zoom = NaN(1, n_cases); fprintf('================================================================\n'); -fprintf(' FastPlot Feature Benchmark\n'); +fprintf(' FastSense Feature Benchmark\n'); fprintf(' Render + %d zoom ops per size\n', n_zooms); fprintf('================================================================\n\n'); @@ -44,7 +44,7 @@ for i = 1:5 tic; for rep = 1:10000 - t = FastPlotTheme(presets{i}); + t = FastSenseTheme(presets{i}); end t_theme_create(i) = toc / 10000; end @@ -72,7 +72,7 @@ % ---- Baseline: addLine only ---- tic; - fp = FastPlot(); + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.render(); t_baseline(c) = toc; @@ -88,7 +88,7 @@ % ---- addBand: 4 bands (trivial geometry) ---- tic; - fp = FastPlot(); + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.addBand(60, 65, 'FaceColor', [1 0.8 0.3], 'FaceAlpha', 0.2, 'Label', 'Warning'); fp.addBand(65, 75, 'FaceColor', [1 0.3 0.3], 'FaceAlpha', 0.2, 'Label', 'Alarm'); @@ -100,7 +100,7 @@ % ---- addShaded: confidence envelope ---- tic; - fp = FastPlot(); + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.addShaded(x, envelope_hi, envelope_lo, 'FaceColor', [0.2 0.5 0.9], 'FaceAlpha', 0.15); fp.render(); @@ -118,7 +118,7 @@ % ---- addFill: area under curve ---- y_abs = abs(y); tic; - fp = FastPlot(); + fp = FastSense(); fp.addLine(x, y_abs, 'DisplayName', 'Signal'); fp.addFill(x, y_abs, 'FaceColor', [0.2 0.7 0.3], 'FaceAlpha', 0.3); fp.render(); @@ -127,7 +127,7 @@ % ---- addMarker: 50 event markers ---- tic; - fp = FastPlot(); + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.addMarker(event_x, event_y, 'Marker', 'v', 'MarkerSize', 10, ... 'Color', [0.9 0.2 0.2], 'Label', 'Anomaly'); @@ -137,7 +137,7 @@ % ---- Combined: all features together ---- tic; - fp = FastPlot('Theme', 'light'); + fp = FastSense('Theme', 'light'); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.addBand(60, 65, 'FaceColor', [1 0.8 0.3], 'FaceAlpha', 0.2); fp.addBand(65, 75, 'FaceColor', [1 0.3 0.3], 'FaceAlpha', 0.2); @@ -158,7 +158,7 @@ % ---- Dashboard: 2x2 with all features ---- tic; - fig = FastPlotGrid(2, 2, 'Theme', 'light', 'Name', 'Bench'); + fig = FastSenseGrid(2, 2, 'Theme', 'light', 'Name', 'Bench'); fp1 = fig.tile(1); fp1.addLine(x, y); fp1.addBand(60, 65, 'FaceColor', [1 0.8 0.3]); fp2 = fig.tile(2); fp2.addLine(x, y); fp2.addShaded(x, envelope_hi, envelope_lo); fp3 = fig.tile(3); fp3.addLine(x, y_abs); fp3.addFill(x, y_abs, 'FaceColor', [0.2 0.7 0.3]); diff --git a/benchmarks/benchmark_memory.m b/benchmarks/benchmark_memory.m index 5c572342..be874502 100644 --- a/benchmarks/benchmark_memory.m +++ b/benchmarks/benchmark_memory.m @@ -13,7 +13,7 @@ function benchmark_memory() addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - fprintf('=== FastPlot Memory Benchmark ===\n\n'); + fprintf('=== FastSense Memory Benchmark ===\n\n'); %% 1. Scaling: memory vs disk across sizes sizes = [1e5, 5e5, 1e6, 2e6, 5e6, 10e6]; diff --git a/benchmarks/benchmark_zoom.m b/benchmarks/benchmark_zoom.m index 18cea701..a0493411 100644 --- a/benchmarks/benchmark_zoom.m +++ b/benchmarks/benchmark_zoom.m @@ -1,9 +1,9 @@ -%% FastPlot Zoom & Pan Latency Benchmark +%% FastSense Zoom & Pan Latency Benchmark % Measures per-frame latency for individual zoom and pan operations. % Forces GPU flush with getframe() to get true frame delivery time. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); -addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastPlot', 'private')); +addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastSense', 'private')); sizes = [1e5, 1e6, 10e6, 50e6]; labels = {'100K', '1M', '10M', '50M'}; @@ -15,7 +15,7 @@ n_pan_steps = 20; % pan operations per zoom level fprintf('================================================================\n'); -fprintf(' FastPlot Zoom & Pan Latency Benchmark\n'); +fprintf(' FastSense Zoom & Pan Latency Benchmark\n'); fprintf(' Per-frame timing with forced GPU flush (getframe)\n'); fprintf('================================================================\n\n'); @@ -32,7 +32,7 @@ plot(ax_std, x, y); drawnow; - fprintf(' %-6s | %-22s | %-22s\n', 'Zoom', 'plot() per-frame (ms)', 'FastPlot per-frame (ms)'); + fprintf(' %-6s | %-22s | %-22s\n', 'Zoom', 'plot() per-frame (ms)', 'FastSense per-frame (ms)'); fprintf(' %-6s-+-%-22s-+-%-22s\n', '------', '----------------------', '----------------------'); std_zoom_times = {}; @@ -59,8 +59,8 @@ close(fig_std); - % --- FastPlot --- - fp = FastPlot(); + % --- FastSense --- + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.addThreshold(1.5, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp.addThreshold(-1.5, 'Direction', 'lower', 'ShowViolations', true, 'Color', 'r'); @@ -120,8 +120,8 @@ end close(fig_std); - % FastPlot - fp = FastPlot(); + % FastSense + fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Signal'); fp.addThreshold(1.5, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp.addThreshold(-1.5, 'Direction', 'lower', 'ShowViolations', true, 'Color', 'r'); @@ -140,12 +140,12 @@ fprintf(' plot(): avg=%5.1f ms p95=%5.1f ms max=%5.1f ms\n', ... mean(std_pan), prctile(std_pan, 95), max(std_pan)); - fprintf(' FastPlot: avg=%5.1f ms p95=%5.1f ms max=%5.1f ms\n', ... + fprintf(' FastSense: avg=%5.1f ms p95=%5.1f ms max=%5.1f ms\n', ... mean(fp_pan), prctile(fp_pan, 95), max(fp_pan)); std_fps = 1000 / mean(std_pan); fp_fps = 1000 / mean(fp_pan); - fprintf(' Effective FPS: plot()=%.0f FastPlot=%.0f\n', std_fps, fp_fps); + fprintf(' Effective FPS: plot()=%.0f FastSense=%.0f\n', std_fps, fp_fps); fprintf('\n'); clear x y fp; @@ -155,7 +155,7 @@ fprintf(' Notes\n'); fprintf('================================================================\n'); fprintf(' - getframe() forces GPU pipeline flush (true frame delivery).\n'); -fprintf(' - At deep zoom (0.1%%), FastPlot only processes ~0.1%% of data\n'); +fprintf(' - At deep zoom (0.1%%), FastSense only processes ~0.1%% of data\n'); fprintf(' via binary search, while plot() still has all points in GPU.\n'); fprintf(' - "p95" = 95th percentile latency (worst-case interactive feel).\n'); fprintf(' - Target for smooth interaction: <33 ms (30 fps) per frame.\n'); diff --git a/benchmarks/profile_datastore.m b/benchmarks/profile_datastore.m index 6c8ead89..483ec8ce 100644 --- a/benchmarks/profile_datastore.m +++ b/benchmarks/profile_datastore.m @@ -1,5 +1,5 @@ function profile_datastore() -%PROFILE_DATASTORE Profile FastPlotDataStore and disk-backed rendering. +%PROFILE_DATASTORE Profile FastSenseDataStore and disk-backed rendering. % Uses MATLAB's profiler to identify bottlenecks in: % 1. DataStore creation (chunked write) % 2. Range queries (zoom simulation) @@ -16,14 +16,14 @@ function profile_datastore() %% 1. Profile DataStore creation fprintf('\n=== Profiling DataStore creation (%dM pts) ===\n', n/1e6); profile on; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); profile off; printTopFunctions('DataStore creation'); ds.cleanup(); %% 2. Profile range queries fprintf('\n=== Profiling 50 range queries ===\n'); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); profile on; for i = 1:50 xCenter = rand * 800 + 100; @@ -35,7 +35,7 @@ function profile_datastore() %% 3. Profile slice reads fprintf('\n=== Profiling 50 slice reads ===\n'); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); profile on; for i = 1:50 startIdx = randi(n - 10000); @@ -47,7 +47,7 @@ function profile_datastore() %% 4. Profile full render with disk-backed line fprintf('\n=== Profiling render (disk-backed, %dM pts) ===\n', n/1e6); - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); fp.addLine(x, y, 'DisplayName', 'Profile Test'); profile on; fp.render(); @@ -69,7 +69,7 @@ function profile_datastore() %% 6. Profile render with memory-backed line (for comparison) fprintf('\n=== Profiling render (memory-backed, %dM pts) ===\n', n/1e6); - fp2 = FastPlot('StorageMode', 'memory'); + fp2 = FastSense('StorageMode', 'memory'); fp2.addLine(x, y, 'DisplayName', 'Memory Test'); profile on; fp2.render(); @@ -89,7 +89,7 @@ function profile_datastore() y(c:ce) = y(c:ce) + 0.1 * randn(1, ce - c + 1); end profile on; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); profile off; printTopFunctions('DataStore creation (50M)'); clear x y; diff --git a/bridge/python/fastplot_bridge/__init__.py b/bridge/python/fastplot_bridge/__init__.py deleted file mode 100644 index 7ec79e67..00000000 --- a/bridge/python/fastplot_bridge/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""FastPlot Bridge — serves MATLAB dashboard data via REST/WebSocket.""" diff --git a/bridge/python/fastsense_bridge/__init__.py b/bridge/python/fastsense_bridge/__init__.py new file mode 100644 index 00000000..21d37678 --- /dev/null +++ b/bridge/python/fastsense_bridge/__init__.py @@ -0,0 +1 @@ +"""FastSense Bridge — serves MATLAB dashboard data via REST/WebSocket.""" diff --git a/bridge/python/fastplot_bridge/__main__.py b/bridge/python/fastsense_bridge/__main__.py similarity index 91% rename from bridge/python/fastplot_bridge/__main__.py rename to bridge/python/fastsense_bridge/__main__.py index fdbdaf56..3dead316 100644 --- a/bridge/python/fastplot_bridge/__main__.py +++ b/bridge/python/fastsense_bridge/__main__.py @@ -1,11 +1,11 @@ -"""CLI entry point for the FastPlot bridge server. +"""CLI entry point for the FastSense bridge server. Connects to MATLAB's TCP server, receives the init message, starts the FastAPI HTTP/WebSocket server, and notifies MATLAB when ready. Usage: - fastplot-bridge --matlab-port 5555 - fastplot-bridge --matlab-port 5555 --host 0.0.0.0 --port 9090 + fastsense-bridge --matlab-port 5555 + fastsense-bridge --matlab-port 5555 --host 0.0.0.0 --port 9090 """ import argparse @@ -61,7 +61,7 @@ async def notify_ready() -> None: def main() -> None: """Parse CLI arguments and run the bridge server.""" parser = argparse.ArgumentParser( - description="FastPlot Bridge Server" + description="FastSense Bridge Server" ) parser.add_argument( "--matlab-port", diff --git a/bridge/python/fastplot_bridge/blob_decoder.py b/bridge/python/fastsense_bridge/blob_decoder.py similarity index 100% rename from bridge/python/fastplot_bridge/blob_decoder.py rename to bridge/python/fastsense_bridge/blob_decoder.py diff --git a/bridge/python/fastplot_bridge/server.py b/bridge/python/fastsense_bridge/server.py similarity index 97% rename from bridge/python/fastplot_bridge/server.py rename to bridge/python/fastsense_bridge/server.py index 6795b89f..ff5d619d 100644 --- a/bridge/python/fastplot_bridge/server.py +++ b/bridge/python/fastsense_bridge/server.py @@ -119,7 +119,7 @@ def create_app(state: AppState) -> FastAPI: Returns: Configured FastAPI app instance. """ - app = FastAPI(title="FastPlot Bridge") + app = FastAPI(title="FastSense Bridge") # --- REST API --- @@ -212,8 +212,8 @@ async def websocket_endpoint(ws: WebSocket) -> None: # --- Static files --- - # server.py is at bridge/python/fastplot_bridge/server.py - # Go up: fastplot_bridge -> python -> bridge, then into /web + # server.py is at bridge/python/fastsense_bridge/server.py + # Go up: fastsense_bridge -> python -> bridge, then into /web web_dir = Path(__file__).resolve().parent.parent.parent / "web" if web_dir.is_dir(): diff --git a/bridge/python/fastplot_bridge/sqlite_reader.py b/bridge/python/fastsense_bridge/sqlite_reader.py similarity index 98% rename from bridge/python/fastplot_bridge/sqlite_reader.py rename to bridge/python/fastsense_bridge/sqlite_reader.py index 97c46e67..a940cbdd 100644 --- a/bridge/python/fastplot_bridge/sqlite_reader.py +++ b/bridge/python/fastsense_bridge/sqlite_reader.py @@ -1,4 +1,4 @@ -"""Read FastPlotDataStore SQLite files and decode typed BLOBs. +"""Read FastSenseDataStore SQLite files and decode typed BLOBs. Opens .fpdb files in read-only mode and provides methods to query chunks by x-range, decode mksqlite typed BLOBs, and optionally apply minmax @@ -13,7 +13,7 @@ class SqliteReader: - """Synchronous reader for .fpdb files created by FastPlotDataStore. + """Synchronous reader for .fpdb files created by FastSenseDataStore. Opens the database in read-only mode (URI mode) so it can safely read while MATLAB writes with WAL mode enabled. diff --git a/bridge/python/fastplot_bridge/tcp_client.py b/bridge/python/fastsense_bridge/tcp_client.py similarity index 100% rename from bridge/python/fastplot_bridge/tcp_client.py rename to bridge/python/fastsense_bridge/tcp_client.py diff --git a/bridge/python/pyproject.toml b/bridge/python/pyproject.toml index a4bc099d..fe3443bc 100644 --- a/bridge/python/pyproject.toml +++ b/bridge/python/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "fastplot-bridge" +name = "fastsense-bridge" version = "0.1.0" requires-python = ">=3.11" dependencies = [ @@ -13,7 +13,7 @@ dependencies = [ dev = ["pytest>=7.0", "pytest-asyncio>=0.21", "httpx>=0.25"] [project.scripts] -fastplot-bridge = "fastplot_bridge.__main__:main" +fastsense-bridge = "fastsense_bridge.__main__:main" [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/bridge/python/tests/test_blob_decoder.py b/bridge/python/tests/test_blob_decoder.py index f32675e7..b01fe806 100644 --- a/bridge/python/tests/test_blob_decoder.py +++ b/bridge/python/tests/test_blob_decoder.py @@ -5,7 +5,7 @@ import numpy as np import pytest -from fastplot_bridge.blob_decoder import MKSQ_MAGIC, decode_typed_blob +from fastsense_bridge.blob_decoder import MKSQ_MAGIC, decode_typed_blob MX_DOUBLE = 6 MX_SINGLE = 7 diff --git a/bridge/python/tests/test_server.py b/bridge/python/tests/test_server.py index 4504b672..f8114ef5 100644 --- a/bridge/python/tests/test_server.py +++ b/bridge/python/tests/test_server.py @@ -10,8 +10,8 @@ import pytest from fastapi.testclient import TestClient -from fastplot_bridge.blob_decoder import MKSQ_MAGIC -from fastplot_bridge.server import AppState, create_app +from fastsense_bridge.blob_decoder import MKSQ_MAGIC +from fastsense_bridge.server import AppState, create_app def _make_double_blob(values: list[float]) -> bytes: diff --git a/bridge/python/tests/test_sqlite_reader.py b/bridge/python/tests/test_sqlite_reader.py index dd627f9c..c7b721f9 100644 --- a/bridge/python/tests/test_sqlite_reader.py +++ b/bridge/python/tests/test_sqlite_reader.py @@ -7,8 +7,8 @@ import numpy as np import pytest -from fastplot_bridge.blob_decoder import MKSQ_MAGIC -from fastplot_bridge.sqlite_reader import SqliteReader, _minmax_downsample +from fastsense_bridge.blob_decoder import MKSQ_MAGIC +from fastsense_bridge.sqlite_reader import SqliteReader, _minmax_downsample def _make_double_blob(values: list[float]) -> bytes: @@ -20,7 +20,7 @@ def _make_double_blob(values: list[float]) -> bytes: @pytest.fixture def sample_db(tmp_path: Path) -> Path: - """Create a minimal .fpdb file matching FastPlotDataStore schema.""" + """Create a minimal .fpdb file matching FastSenseDataStore schema.""" db_path = tmp_path / "test.fpdb" conn = sqlite3.connect(str(db_path)) diff --git a/bridge/python/tests/test_tcp_client.py b/bridge/python/tests/test_tcp_client.py index beda72ad..5758d09a 100644 --- a/bridge/python/tests/test_tcp_client.py +++ b/bridge/python/tests/test_tcp_client.py @@ -6,7 +6,7 @@ import pytest import pytest_asyncio -from fastplot_bridge.tcp_client import MatlabTcpClient +from fastsense_bridge.tcp_client import MatlabTcpClient @pytest_asyncio.fixture diff --git a/bridge/web/css/style.css b/bridge/web/css/style.css index e28ed783..3f3a665a 100644 --- a/bridge/web/css/style.css +++ b/bridge/web/css/style.css @@ -1,5 +1,5 @@ /* ============================================================ - FastPlot Dashboard — Styles + FastSense Dashboard — Styles ============================================================ */ /* --- CSS Variables ----------------------------------------- */ diff --git a/bridge/web/index.html b/bridge/web/index.html index 9f86d846..2042acc7 100644 --- a/bridge/web/index.html +++ b/bridge/web/index.html @@ -3,13 +3,13 @@ - FastPlot Dashboard + FastSense Dashboard
diff --git a/bridge/web/js/chart.js b/bridge/web/js/chart.js index 599209b2..7dbd963f 100644 --- a/bridge/web/js/chart.js +++ b/bridge/web/js/chart.js @@ -1,5 +1,5 @@ /* ============================================================ - chart.js — uPlot wrapper for FastPlot time-series signals + chart.js — uPlot wrapper for FastSense time-series signals ============================================================ */ var Chart = (function () { diff --git a/bridge/web/js/widgets.js b/bridge/web/js/widgets.js index 425f6ca8..19ce4322 100644 --- a/bridge/web/js/widgets.js +++ b/bridge/web/js/widgets.js @@ -9,7 +9,7 @@ var Widgets = (function () { function render(config, bodyEl) { var type = config.type || "text"; switch (type) { - case "fastplot": return renderFastplot(config, bodyEl); + case "fastsense": return renderFastSense(config, bodyEl); case "kpi": return renderKPI(config, bodyEl); case "status": return renderStatus(config, bodyEl); case "table": return renderTable(config, bodyEl); @@ -22,8 +22,8 @@ var Widgets = (function () { } } - /* --- fastplot (uPlot chart) ---------------------------- */ - function renderFastplot(cfg, el) { + /* --- fastsense (uPlot chart) ---------------------------- */ + function renderFastSense(cfg, el) { el.classList.add("chart-body"); var signalId = cfg.signalId || cfg.id; Chart.create(signalId, el); diff --git a/docs/2026-03-06-fastplot-design.md b/docs/2026-03-06-fastsense-design.md similarity index 93% rename from docs/2026-03-06-fastplot-design.md rename to docs/2026-03-06-fastsense-design.md index 33198c3d..b16b9e3d 100644 --- a/docs/2026-03-06-fastplot-design.md +++ b/docs/2026-03-06-fastsense-design.md @@ -1,4 +1,4 @@ -# FastPlot — Ultra-Fast Time Series Plotting Library for MATLAB 2020b +# FastSense — Ultra-Fast Time Series Plotting Library for MATLAB 2020b ## Design Document @@ -41,7 +41,7 @@ MATLAB's built-in `plot()` chokes on large time series data (10M-100M points). Z ## 3. Architecture ``` -FastPlot (handle class) +FastSense (handle class) | +-- Properties | +-- Lines[] — array of line config structs @@ -131,14 +131,14 @@ During zoom/pan callbacks, axis limits are managed manually. Auto-limit recalcul ```matlab % Create with new figure (default) -fp = FastPlot(); +fp = FastSense(); % Create targeting existing axes -fp = FastPlot('Parent', ax); +fp = FastSense('Parent', ax); % Linked axes (opt-in) -fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'sensors'); -fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'sensors'); +fp1 = FastSense('Parent', ax1, 'LinkGroup', 'sensors'); +fp2 = FastSense('Parent', ax2, 'LinkGroup', 'sensors'); ``` ### 5.2 Adding Lines @@ -186,13 +186,13 @@ This: ### 5.5 UserData Tagging -Every graphics object created by FastPlot carries identification metadata: +Every graphics object created by FastSense carries identification metadata: ```matlab -h.UserData.FastPlot = struct( ... +h.UserData.FastSense = struct( ... 'Type', 'data_line', ... % 'data_line' | 'threshold' | 'violation_marker' 'Name', 'Sensor1', ... % DisplayName or threshold Label - 'LineIndex', 1, ... % index in FastPlot's line array + 'LineIndex', 1, ... % index in FastSense's line array 'ThresholdValue', [] ... % populated for threshold/violation types ); ``` @@ -201,7 +201,7 @@ Usage example: ```matlab % Find all threshold lines programmatically lines = findobj(ax, '-function', ... - @(h) isfield(h.UserData, 'FastPlot') && strcmp(h.UserData.FastPlot.Type, 'threshold')); + @(h) isfield(h.UserData, 'FastSense') && strcmp(h.UserData.FastSense.Type, 'threshold')); ``` --- @@ -229,7 +229,7 @@ Recompute violation markers for visible range | v If LinkGroup active: - |-- propagate XLim to all linked FastPlot instances (skip self) + |-- propagate XLim to all linked FastSense instances (skip self) | v drawnow limitrate @@ -240,8 +240,8 @@ drawnow limitrate ## 7. File Structure ``` -FastPlot/ -+-- FastPlot.m — main handle class +FastSense/ ++-- FastSense.m — main handle class +-- private/ | +-- minmax_downsample.m — MinMax per-pixel downsampling | +-- lttb_downsample.m — Largest Triangle Three Buckets diff --git a/docs/generate_readme_images.m b/docs/generate_readme_images.m index 9b459433..c277661c 100644 --- a/docs/generate_readme_images.m +++ b/docs/generate_readme_images.m @@ -1,5 +1,5 @@ -% generate_readme_images.m — Generate README screenshots for FastPlot features -% Run from the FastPlot root directory: +% generate_readme_images.m — Generate README screenshots for FastSense features +% Run from the FastSense root directory: % octave --no-gui --eval "setup; run('docs/generate_readme_images.m')" img_dir = fullfile(fileparts(mfilename('fullpath')), 'images'); @@ -12,7 +12,7 @@ x = linspace(0, 100, 1e6); y = sin(x * 2*pi / 10) + 0.5 * randn(1, numel(x)); -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Sensor1', 'Color', 'b'); fp.addThreshold(1.5, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp.addThreshold(-1.5, 'Direction', 'lower', 'ShowViolations', true, 'Color', 'r'); @@ -30,7 +30,7 @@ y3 = sin(x * 2*pi / 8) * 15 + 30 + 4 * randn(1, numel(x)); y4 = cumsum(randn(1, numel(x))) / 100 + 10; -fig = FastPlotGrid(2, 2, 'Theme', 'dark'); +fig = FastSenseGrid(2, 2, 'Theme', 'dark'); fig.setTileSpan(1, [1 2]); fp1 = fig.tile(1); @@ -63,7 +63,7 @@ hFig = figure('Position', [50 50 1600 600], 'Color', [0.95 0.95 0.95]); for i = 1:5 ax = subplot(1, 5, i, 'Parent', hFig); - fp = FastPlot('Parent', ax, 'Theme', themes{i}); + fp = FastSense('Parent', ax, 'Theme', themes{i}); fp.addLine(x, y1, 'DisplayName', 'Sine'); fp.addLine(x, y2, 'DisplayName', 'Cosine'); fp.addThreshold(1.2, 'Direction', 'upper', 'ShowViolations', true); @@ -83,21 +83,21 @@ hFig = figure('Position', [100 100 1200 700]); ax1 = subplot(3, 1, 1, 'Parent', hFig); -fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'sensors'); +fp1 = FastSense('Parent', ax1, 'LinkGroup', 'sensors'); fp1.addLine(x, y_pressure, 'DisplayName', 'Pressure', 'Color', [0 0.45 0.74]); fp1.addThreshold(125, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp1.render(); title(ax1, 'Pressure (linked)'); ax2 = subplot(3, 1, 2, 'Parent', hFig); -fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'sensors'); +fp2 = FastSense('Parent', ax2, 'LinkGroup', 'sensors'); fp2.addLine(x, y_temp, 'DisplayName', 'Temperature', 'Color', [0.85 0.33 0.1]); fp2.addThreshold(75, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp2.render(); title(ax2, 'Temperature (linked)'); ax3 = subplot(3, 1, 3, 'Parent', hFig); -fp3 = FastPlot('Parent', ax3, 'LinkGroup', 'sensors'); +fp3 = FastSense('Parent', ax3, 'LinkGroup', 'sensors'); fp3.addLine(x, y_vibration, 'DisplayName', 'Vibration', 'Color', [0.47 0.67 0.19]); fp3.render(); title(ax3, 'Vibration (linked)'); @@ -113,7 +113,7 @@ y(250000:280000) = NaN; y(400000:410000) = NaN; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Sensor (with dropouts)', 'Color', [0 0.45 0.74]); fp.render(); title(fp.hAxes, 'NaN Gap Handling'); @@ -126,7 +126,7 @@ x = linspace(0, 100, 5e5); y = sin(x * 2*pi / 15) * 40 + 50 + 8 * randn(1, numel(x)); -fp = FastPlot('Theme', 'dark'); +fp = FastSense('Theme', 'dark'); fp.addLine(x, y, 'DisplayName', 'Process Value', 'Color', [0.3 0.75 0.93]); fp.addBand(85, 95, 'FaceColor', [1 0 0], 'FaceAlpha', 0.12, 'Label', 'HH Alarm'); fp.addBand(75, 85, 'FaceColor', [1 0.6 0], 'FaceAlpha', 0.10, 'Label', 'H Alarm'); @@ -161,7 +161,7 @@ s.addThresholdRule(struct('machine', 1), 30, 'Direction', 'lower', 'Label', 'Run LO'); s.resolve(); -fp = FastPlot('Theme', 'dark'); +fp = FastSense('Theme', 'dark'); fp.addSensor(s); fp.render(); title(fp.hAxes, 'Condition-Dependent Sensor Thresholds'); @@ -174,7 +174,7 @@ x = datenum(2024,1,1) + (0:499999)/86400; y = sin((1:500000) * 2*pi/3600) + 0.3 * randn(1, 500000); -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'XType', 'datenum', 'DisplayName', 'Sensor'); fp.addThreshold(1.0, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp.render(); diff --git a/docs/plans/2026-03-06-dashboard-visual-design.md b/docs/plans/2026-03-06-dashboard-visual-design.md index 1231c3f0..4f3927a6 100644 --- a/docs/plans/2026-03-06-dashboard-visual-design.md +++ b/docs/plans/2026-03-06-dashboard-visual-design.md @@ -1,4 +1,4 @@ -# FastPlot Enhancement: Visual Customization + Dashboard Layouts +# FastSense Enhancement: Visual Customization + Dashboard Layouts **Date:** 2026-03-06 **Status:** Approved @@ -7,9 +7,9 @@ ## 1. Problem Statement -FastPlot today is a single-axes class. Building multi-panel dashboards requires manual `subplot()` calls, manual `Parent`/`LinkGroup` wiring, and no theming support. Visual customization is limited to pass-through MATLAB line properties. There are no shaded regions, band fills, or custom markers. +FastSense today is a single-axes class. Building multi-panel dashboards requires manual `subplot()` calls, manual `Parent`/`LinkGroup` wiring, and no theming support. Visual customization is limited to pass-through MATLAB line properties. There are no shaded regions, band fills, or custom markers. -**Goal:** Add a figure-level layout manager (`FastPlotFigure`) for tiled dashboards with spanning, a comprehensive theming system with inheritance, and new visual elements (shaded fills, bands, markers) — all while keeping full backward compatibility. +**Goal:** Add a figure-level layout manager (`FastSenseFigure`) for tiled dashboards with spanning, a comprehensive theming system with inheritance, and new visual elements (shaded fills, bands, markers) — all while keeping full backward compatibility. --- @@ -18,7 +18,7 @@ FastPlot today is a single-axes class. Building multi-panel dashboards requires ### Functional - Tiled grid layout with configurable rows/columns - Tiles can span multiple rows and/or columns -- Every tile is a FastPlot axes (no non-plot tiles for now) +- Every tile is a FastSense axes (no non-plot tiles for now) - Theme system with presets and custom definitions - Theme inheritance: element override > tile theme > figure theme > default preset - Auto line color cycling from theme palette @@ -27,7 +27,7 @@ FastPlot today is a single-axes class. Building multi-panel dashboards requires - Area fills from line to baseline - Custom event markers with configurable shape/size/color - Link groups remain explicit (not auto-linked in dashboards) -- Standalone FastPlot gains theme support without requiring FastPlotFigure +- Standalone FastSense gains theme support without requiring FastSenseFigure ### Non-Functional - Full backward compatibility — all existing scripts unchanged @@ -41,13 +41,13 @@ FastPlot today is a single-axes class. Building multi-panel dashboards requires ### 3.1 Class Structure ``` -FastPlotFigure (handle class) -- figure + layout + theme +FastSenseFigure (handle class) -- figure + layout + theme hFigure -- figure handle - Theme -- FastPlotTheme struct + Theme -- FastSenseTheme struct Grid [rows, cols] -- layout grid dimensions - Tiles{} -- cell array of FastPlot instances + Tiles{} -- cell array of FastSense instances TileSpans{} -- per-tile [rowSpan, colSpan] - tile(n) -> FastPlot -- get/create FastPlot for tile n + tile(n) -> FastSense -- get/create FastSense for tile n setTileSpan(n, [r,c]) -- make tile span multiple rows/cols tileTitle(n, str) -- set title for tile n tileXLabel(n, str) -- set xlabel for tile n @@ -56,7 +56,7 @@ FastPlotFigure (handle class) -- figure + layout + theme renderAll() -- render all unrendered tiles render() -- alias for renderAll() -FastPlot (handle class) -- per-axes (extended) +FastSense (handle class) -- per-axes (extended) existing API unchanged addShaded(x, y1, y2, ...) -- fill between two curves addBand(yLow, yHigh, ...) -- horizontal band fill @@ -65,7 +65,7 @@ FastPlot (handle class) -- per-axes (extended) Theme (inherited or overridden) -- per-tile theme auto line color cycling -- from theme LineColorOrder -FastPlotTheme (function, not class) -- returns theme struct +FastSenseTheme (function, not class) -- returns theme struct Background, AxesColor, GridColor, GridAlpha GridStyle, FontName, FontSize, TitleFontSize ForegroundColor -- text, tick labels, axis lines @@ -80,10 +80,10 @@ FastPlotTheme (function, not class) -- returns theme struct | File | Purpose | |------|---------| -| `FastPlotFigure.m` | Figure-level layout manager with tiled grid, spanning, theming | -| `FastPlotTheme.m` | Function returning theme preset structs and merge logic | +| `FastSenseFigure.m` | Figure-level layout manager with tiled grid, spanning, theming | +| `FastSenseTheme.m` | Function returning theme preset structs and merge logic | -### 3.3 Extended: `FastPlot.m` +### 3.3 Extended: `FastSense.m` | Addition | Description | |----------|-------------| @@ -96,20 +96,20 @@ FastPlotTheme (function, not class) -- returns theme struct --- -## 4. FastPlotFigure API +## 4. FastSenseFigure API ```matlab %% Construction -fig = FastPlotFigure(rows, cols); -fig = FastPlotFigure(rows, cols, 'Theme', 'dark'); -fig = FastPlotFigure(rows, cols, 'Theme', myCustomTheme); -fig = FastPlotFigure(rows, cols, 'Position', [100 100 1400 800], 'Name', 'Dashboard'); +fig = FastSenseFigure(rows, cols); +fig = FastSenseFigure(rows, cols, 'Theme', 'dark'); +fig = FastSenseFigure(rows, cols, 'Theme', myCustomTheme); +fig = FastSenseFigure(rows, cols, 'Position', [100 100 1400 800], 'Name', 'Dashboard'); %% Tile spanning fig.setTileSpan(1, [1 2]); % tile 1 spans 1 row, 2 columns fig.setTileSpan(3, [2 1]); % tile 3 spans 2 rows, 1 column -%% Getting tiles (returns FastPlot instance, creates axes on first call) +%% Getting tiles (returns FastSense instance, creates axes on first call) fp = fig.tile(1); fp.addLine(x, y, 'DisplayName', 'Sensor1'); fp.render(); @@ -132,7 +132,7 @@ Tiles are numbered left-to-right, top-to-bottom (same as MATLAB's `subplot`). Wh ### Key Behaviors -- `tile(n)` is lazy -- axes and FastPlot are created on first access +- `tile(n)` is lazy -- axes and FastSense are created on first access - `renderAll()` calls `render()` on all tiles that haven't been rendered yet - Figure is kept invisible until `renderAll()` or the first `tile.render()` call - Standard figure properties (`Name`, `Position`, `Color`) forwarded to underlying figure handle @@ -170,7 +170,7 @@ fp.addMarker(x_events, y_events, ... - `addBand` creates a rectangle patch -- no downsampling (constant bounds) - `addFill` is sugar for `addShaded(x, y, baseline)` - `addMarker` creates a line object with `'none'` LineStyle and configurable marker props -- All new elements get `UserData.FastPlot` tagging (`'shaded'`, `'band'`, `'fill'`, `'marker'`) +- All new elements get `UserData.FastSense` tagging (`'shaded'`, `'band'`, `'fill'`, `'marker'`) ### Rendering Order (back to front) @@ -227,7 +227,7 @@ myTheme = struct( ... | `'colorblind'` | Deuteranopia-safe 8-color palette | | Custom | Nx3 matrix of RGB values | -Auto-cycling: when `addLine` is called without a `Color` argument, the next color from `LineColorOrder` is assigned automatically. Each FastPlot instance tracks its position in the cycle. +Auto-cycling: when `addLine` is called without a `Color` argument, the next color from `LineColorOrder` is assigned automatically. Each FastSense instance tracks its position in the cycle. ### 6.4 Theme Inheritance @@ -235,27 +235,27 @@ Auto-cycling: when `addLine` is called without a `Color` argument, the next colo Element override > Tile theme > Figure theme > 'default' preset ``` -- `FastPlotTheme('dark')` returns the full dark preset struct -- `FastPlotTheme('dark', 'LineColorOrder', 'colorblind')` returns dark with one field overridden +- `FastSenseTheme('dark')` returns the full dark preset struct +- `FastSenseTheme('dark', 'LineColorOrder', 'colorblind')` returns dark with one field overridden - Merging: simple `fieldnames` loop, override wins - Unset fields in custom themes inherit from `'default'` --- -## 7. Standalone FastPlot Enhancements +## 7. Standalone FastSense Enhancements ```matlab -%% Standalone with theme (no FastPlotFigure required) -fp = FastPlot('Theme', 'dark'); +%% Standalone with theme (no FastSenseFigure required) +fp = FastSense('Theme', 'dark'); fp.addLine(x, y); fp.render(); %% Full constructor signature -fp = FastPlot(); -fp = FastPlot('Parent', ax); -fp = FastPlot('LinkGroup', 'sensors'); -fp = FastPlot('Theme', 'dark'); -fp = FastPlot('Parent', ax, 'LinkGroup', 'g1', 'Theme', myTheme); +fp = FastSense(); +fp = FastSense('Parent', ax); +fp = FastSense('LinkGroup', 'sensors'); +fp = FastSense('Theme', 'dark'); +fp = FastSense('Parent', ax, 'LinkGroup', 'g1', 'Theme', myTheme); ``` No theme = `'default'` preset. All new methods are optional additions. diff --git a/docs/plans/2026-03-06-dashboard-visual-impl.md b/docs/plans/2026-03-06-dashboard-visual-impl.md index ea21fc55..5e034371 100644 --- a/docs/plans/2026-03-06-dashboard-visual-impl.md +++ b/docs/plans/2026-03-06-dashboard-visual-impl.md @@ -2,9 +2,9 @@ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. -**Goal:** Add FastPlotFigure (tiled dashboard layouts), FastPlotTheme (5 presets + custom themes), and new visual elements (addShaded, addBand, addFill, addMarker) to FastPlot while maintaining full backward compatibility. +**Goal:** Add FastSenseFigure (tiled dashboard layouts), FastSenseTheme (5 presets + custom themes), and new visual elements (addShaded, addBand, addFill, addMarker) to FastSense while maintaining full backward compatibility. -**Architecture:** Three layers — `FastPlotTheme.m` (function returning theme structs), extended `FastPlot.m` (theme support + 4 new visual methods), and `FastPlotFigure.m` (figure layout manager). Theme inheritance flows: element override > tile theme > figure theme > 'default' preset. All visual enhancements use `patch()` or `line()` objects with UserData tagging and downsampling on zoom. +**Architecture:** Three layers — `FastSenseTheme.m` (function returning theme structs), extended `FastSense.m` (theme support + 4 new visual methods), and `FastSenseFigure.m` (figure layout manager). Theme inheritance flows: element override > tile theme > figure theme > 'default' preset. All visual enhancements use `patch()` or `line()` objects with UserData tagging and downsampling on zoom. **Tech Stack:** Pure MATLAB/Octave — no toolboxes, no MEX. Uses `figure()`, `axes()`, `patch()`, `line()`, `set()`/`get()`. @@ -12,10 +12,10 @@ --- -### Task 1: FastPlotTheme — Default Preset & Merge Logic +### Task 1: FastSenseTheme — Default Preset & Merge Logic **Files:** -- Create: `FastPlotTheme.m` +- Create: `FastSenseTheme.m` - Create: `tests/test_theme.m` **Step 1: Write the failing test** @@ -24,12 +24,12 @@ Create `tests/test_theme.m`: ```matlab function test_theme() -%TEST_THEME Tests for FastPlotTheme function. +%TEST_THEME Tests for FastSenseTheme function. addpath(fullfile(fileparts(mfilename('fullpath')), '..')); % testDefaultPreset - t = FastPlotTheme('default'); + t = FastSenseTheme('default'); assert(isstruct(t), 'testDefaultPreset: must return struct'); assert(isequal(t.Background, [1 1 1]), 'testDefaultPreset: Background'); assert(isfield(t, 'AxesColor'), 'testDefaultPreset: AxesColor field'); @@ -50,12 +50,12 @@ function test_theme() assert(size(t.LineColorOrder, 2) == 3, 'testDefaultPreset: LineColorOrder must be Nx3'); % testNoArgsReturnsDefault - t0 = FastPlotTheme(); - t1 = FastPlotTheme('default'); + t0 = FastSenseTheme(); + t1 = FastSenseTheme('default'); assert(isequal(t0, t1), 'testNoArgsReturnsDefault'); % testMergeOverrides - t = FastPlotTheme('default', 'FontSize', 14, 'LineWidth', 2.0); + t = FastSenseTheme('default', 'FontSize', 14, 'LineWidth', 2.0); assert(t.FontSize == 14, 'testMergeOverrides: FontSize'); assert(t.LineWidth == 2.0, 'testMergeOverrides: LineWidth'); assert(isequal(t.Background, [1 1 1]), 'testMergeOverrides: Background unchanged'); @@ -63,7 +63,7 @@ function test_theme() % testInvalidPresetErrors threw = false; try - FastPlotTheme('nonexistent'); + FastSenseTheme('nonexistent'); catch threw = true; end @@ -76,18 +76,18 @@ end **Step 2: Run test to verify it fails** Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_theme;"` -Expected: FAIL — `FastPlotTheme` not found +Expected: FAIL — `FastSenseTheme` not found **Step 3: Write minimal implementation** -Create `FastPlotTheme.m`: +Create `FastSenseTheme.m`: ```matlab -function theme = FastPlotTheme(preset, varargin) -%FASTPLOTTHEME Return a theme struct for FastPlot styling. -% theme = FastPlotTheme() — returns 'default' preset -% theme = FastPlotTheme('dark') — returns named preset -% theme = FastPlotTheme('dark', 'FontSize', 14) — preset with overrides +function theme = FastSenseTheme(preset, varargin) +%FASTSENSETHEME Return a theme struct for FastSense styling. +% theme = FastSenseTheme() — returns 'default' preset +% theme = FastSenseTheme('dark') — returns named preset +% theme = FastSenseTheme('dark', 'FontSize', 14) — preset with overrides if nargin == 0 preset = 'default'; @@ -209,7 +209,7 @@ function t = getPreset(name) 'BandAlpha', 0.1 ... ); otherwise - error('FastPlotTheme:unknownPreset', ... + error('FastSenseTheme:unknownPreset', ... 'Unknown theme preset: ''%s''. Use ''default'', ''dark'', ''light'', ''industrial'', or ''scientific''.', name); end end @@ -251,7 +251,7 @@ function colors = getPalette(name) 0.00 0.00 0.00; ... % black ]; otherwise - error('FastPlotTheme:unknownPalette', ... + error('FastSenseTheme:unknownPalette', ... 'Unknown palette: ''%s''. Use ''vibrant'', ''muted'', or ''colorblind''.', name); end end @@ -273,13 +273,13 @@ Expected: PASS — All 4 theme tests passed. **Step 5: Commit** ```bash -git add FastPlotTheme.m tests/test_theme.m -git commit -m "Add FastPlotTheme with 5 presets, 3 palettes, and merge logic" +git add FastSenseTheme.m tests/test_theme.m +git commit -m "Add FastSenseTheme with 5 presets, 3 palettes, and merge logic" ``` --- -### Task 2: FastPlotTheme — All Presets & Palettes +### Task 2: FastSenseTheme — All Presets & Palettes **Files:** - Modify: `tests/test_theme.m` @@ -290,23 +290,23 @@ Append to `tests/test_theme.m` (before the final fprintf): ```matlab % testDarkPreset - t = FastPlotTheme('dark'); + t = FastSenseTheme('dark'); assert(all(t.Background < [0.2 0.2 0.2]), 'testDarkPreset: Background should be dark'); assert(all(t.ForegroundColor > [0.7 0.7 0.7]), 'testDarkPreset: ForegroundColor should be light'); assert(size(t.LineColorOrder, 2) == 3, 'testDarkPreset: LineColorOrder Nx3'); % testLightPreset - t = FastPlotTheme('light'); + t = FastSenseTheme('light'); assert(all(t.Background > [0.9 0.9 0.9]), 'testLightPreset: Background'); assert(size(t.LineColorOrder, 2) == 3, 'testLightPreset: LineColorOrder Nx3'); % testIndustrialPreset - t = FastPlotTheme('industrial'); + t = FastSenseTheme('industrial'); assert(t.LineWidth >= 1.0, 'testIndustrialPreset: LineWidth'); assert(size(t.LineColorOrder, 2) == 3, 'testIndustrialPreset: LineColorOrder Nx3'); % testScientificPreset - t = FastPlotTheme('scientific'); + t = FastSenseTheme('scientific'); assert(strcmp(t.FontName, 'Times New Roman'), 'testScientificPreset: FontName'); assert(t.GridAlpha == 0, 'testScientificPreset: no grid'); assert(t.LineWidth < 1.0, 'testScientificPreset: thin lines'); @@ -314,18 +314,18 @@ Append to `tests/test_theme.m` (before the final fprintf): % testStructAsPreset custom = struct('Background', [0 0 0], 'FontSize', 16); - t = FastPlotTheme(custom); + t = FastSenseTheme(custom); assert(isequal(t.Background, [0 0 0]), 'testStructAsPreset: Background'); assert(t.FontSize == 16, 'testStructAsPreset: FontSize'); assert(isfield(t, 'GridColor'), 'testStructAsPreset: inherits defaults'); % testPaletteResolution - t = FastPlotTheme('default'); + t = FastSenseTheme('default'); assert(size(t.LineColorOrder, 1) >= 6, 'testPaletteResolution: at least 6 colors'); % testCustomPaletteMatrix customColors = [1 0 0; 0 1 0; 0 0 1]; - t = FastPlotTheme('default', 'LineColorOrder', customColors); + t = FastSenseTheme('default', 'LineColorOrder', customColors); assert(isequal(t.LineColorOrder, customColors), 'testCustomPaletteMatrix'); ``` @@ -349,42 +349,42 @@ git commit -m "Add comprehensive theme preset and palette tests" --- -### Task 3: FastPlot Theme Integration — Constructor & applyTheme +### Task 3: FastSense Theme Integration — Constructor & applyTheme **Files:** -- Modify: `FastPlot.m:12-54` (properties + constructor) -- Modify: `FastPlot.m:161-328` (render method) -- Create: `tests/test_fastplot_theme.m` +- Modify: `FastSense.m:12-54` (properties + constructor) +- Modify: `FastSense.m:161-328` (render method) +- Create: `tests/test_fastsense_theme.m` **Step 1: Write the failing test** -Create `tests/test_fastplot_theme.m`: +Create `tests/test_fastsense_theme.m`: ```matlab -function test_fastplot_theme() -%TEST_FASTPLOT_THEME Tests for FastPlot theme integration. +function test_fastsense_theme() +%TEST_FASTSENSE_THEME Tests for FastSense theme integration. addpath(fullfile(fileparts(mfilename('fullpath')), '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'private')); % testThemeConstructorString - fp = FastPlot('Theme', 'dark'); + fp = FastSense('Theme', 'dark'); assert(isstruct(fp.Theme), 'testThemeConstructorString: Theme must be struct'); assert(all(fp.Theme.Background < [0.2 0.2 0.2]), 'testThemeConstructorString: dark bg'); % testThemeConstructorStruct custom = struct('Background', [0.5 0.5 0.5]); - fp = FastPlot('Theme', custom); + fp = FastSense('Theme', custom); assert(isequal(fp.Theme.Background, [0.5 0.5 0.5]), 'testThemeConstructorStruct'); assert(isfield(fp.Theme, 'FontSize'), 'testThemeConstructorStruct: inherits defaults'); % testDefaultThemeWhenNoneSpecified - fp = FastPlot(); + fp = FastSense(); assert(isstruct(fp.Theme), 'testDefaultTheme: must have theme'); assert(isequal(fp.Theme.Background, [1 1 1]), 'testDefaultTheme: default bg'); % testThemeAppliedOnRender - fp = FastPlot('Theme', 'dark'); + fp = FastSense('Theme', 'dark'); fp.addLine(1:100, rand(1,100)); fp.render(); bgColor = get(fp.hFigure, 'Color'); @@ -394,7 +394,7 @@ function test_fastplot_theme() close(fp.hFigure); % testThemeFontApplied - fp = FastPlot('Theme', 'scientific'); + fp = FastSense('Theme', 'scientific'); fp.addLine(1:100, rand(1,100)); fp.render(); assert(strcmp(get(fp.hAxes, 'FontName'), 'Times New Roman'), 'testThemeFontApplied'); @@ -403,7 +403,7 @@ function test_fastplot_theme() % testThemeWithParentAxes fig = figure('Visible', 'off'); ax = axes('Parent', fig); - fp = FastPlot('Parent', ax, 'Theme', 'dark'); + fp = FastSense('Parent', ax, 'Theme', 'dark'); fp.addLine(1:100, rand(1,100)); fp.render(); axColor = get(ax, 'Color'); @@ -411,7 +411,7 @@ function test_fastplot_theme() close(fig); % testBackwardCompatNoTheme - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); assert(isgraphics(fp.hAxes), 'testBackwardCompatNoTheme'); @@ -423,16 +423,16 @@ end **Step 2: Run test to verify it fails** -Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastplot_theme;"` +Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastsense_theme;"` Expected: FAIL — `fp.Theme` property does not exist **Step 3: Write implementation** -Modify `FastPlot.m`: +Modify `FastSense.m`: Add to public properties (after line 14): ```matlab - Theme = [] % theme struct (from FastPlotTheme) + Theme = [] % theme struct (from FastSenseTheme) ``` Add to private properties (after line 35): @@ -442,7 +442,7 @@ Add to private properties (after line 35): Modify constructor (lines 45-54) to handle 'theme': ```matlab - function obj = FastPlot(varargin) + function obj = FastSense(varargin) for k = 1:2:numel(varargin) switch lower(varargin{k}) case 'parent' @@ -452,7 +452,7 @@ Modify constructor (lines 45-54) to handle 'theme': case 'theme' val = varargin{k+1}; if ischar(val) || isstruct(val) - obj.Theme = FastPlotTheme(val); + obj.Theme = FastSenseTheme(val); else obj.Theme = val; end @@ -460,12 +460,12 @@ Modify constructor (lines 45-54) to handle 'theme': end % Default theme if none set if isempty(obj.Theme) - obj.Theme = FastPlotTheme('default'); + obj.Theme = FastSenseTheme('default'); end end ``` -Add private method `applyTheme` to FastPlot.m (in the `methods (Access = private)` block): +Add private method `applyTheme` to FastSense.m (in the `methods (Access = private)` block): ```matlab function applyTheme(obj) t = obj.Theme; @@ -501,7 +501,7 @@ In `render()`, call `obj.applyTheme()` right after creating/assigning axes (afte **Step 4: Run test to verify it passes** -Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastplot_theme;"` +Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastsense_theme;"` Expected: PASS — All 7 theme integration tests passed. **Step 5: Run existing tests to verify backward compatibility** @@ -512,8 +512,8 @@ Expected: All existing tests pass. **Step 6: Commit** ```bash -git add FastPlot.m tests/test_fastplot_theme.m -git commit -m "Add theme support to FastPlot constructor and render" +git add FastSense.m tests/test_fastsense_theme.m +git commit -m "Add theme support to FastSense constructor and render" ``` --- @@ -521,16 +521,16 @@ git commit -m "Add theme support to FastPlot constructor and render" ### Task 4: Auto Line Color Cycling **Files:** -- Modify: `FastPlot.m` (addLine method, lines 56-116) -- Modify: `tests/test_fastplot_theme.m` +- Modify: `FastSense.m` (addLine method, lines 56-116) +- Modify: `tests/test_fastsense_theme.m` **Step 1: Write the failing test** -Append to `tests/test_fastplot_theme.m` (before final fprintf): +Append to `tests/test_fastsense_theme.m` (before final fprintf): ```matlab % testAutoColorCycling - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.addLine(1:10, rand(1,10)); fp.addLine(1:10, rand(1,10)); @@ -541,7 +541,7 @@ Append to `tests/test_fastplot_theme.m` (before final fprintf): assert(~isequal(c2, c3), 'testAutoColorCycling: colors 2 and 3 differ'); % testExplicitColorSkipsCycle - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'Color', [1 0 0]); fp.addLine(1:10, rand(1,10)); assert(isequal(fp.Lines(1).Options.Color, [1 0 0]), 'testExplicitColorSkipsCycle: explicit'); @@ -557,7 +557,7 @@ Expected: FAIL — auto-assigned colors not present in Options **Step 3: Write implementation** -Modify `addLine` in `FastPlot.m`. After the name-value parsing loop (around line 100), before building the line struct, add: +Modify `addLine` in `FastSense.m`. After the name-value parsing loop (around line 100), before building the line struct, add: ```matlab % Auto-assign color from theme palette if not explicitly set @@ -571,7 +571,7 @@ Modify `addLine` in `FastPlot.m`. After the name-value parsing loop (around line **Step 4: Run test to verify it passes** -Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastplot_theme;"` +Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastsense_theme;"` Expected: PASS **Step 5: Run all tests** @@ -582,7 +582,7 @@ Expected: All pass. **Step 6: Commit** ```bash -git add FastPlot.m tests/test_fastplot_theme.m +git add FastSense.m tests/test_fastsense_theme.m git commit -m "Add auto line color cycling from theme palette" ``` @@ -591,7 +591,7 @@ git commit -m "Add auto line color cycling from theme palette" ### Task 5: addBand — Horizontal Band Fill **Files:** -- Modify: `FastPlot.m` (new property struct, addBand method, render updates) +- Modify: `FastSense.m` (new property struct, addBand method, render updates) - Create: `tests/test_add_band.m` **Step 1: Write the failing test** @@ -600,13 +600,13 @@ Create `tests/test_add_band.m`: ```matlab function test_add_band() -%TEST_ADD_BAND Tests for FastPlot.addBand method. +%TEST_ADD_BAND Tests for FastSense.addBand method. addpath(fullfile(fileparts(mfilename('fullpath')), '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'private')); % testAddBand - fp = FastPlot(); + fp = FastSense(); fp.addBand(-1, 1, 'FaceColor', [1 0.9 0.9], 'FaceAlpha', 0.3, 'Label', 'Safe'); assert(numel(fp.Bands) == 1, 'testAddBand: count'); assert(fp.Bands(1).YLow == -1, 'testAddBand: YLow'); @@ -614,24 +614,24 @@ function test_add_band() assert(strcmp(fp.Bands(1).Label, 'Safe'), 'testAddBand: Label'); % testAddMultipleBands - fp = FastPlot(); + fp = FastSense(); fp.addBand(-2, -1); fp.addBand(1, 2); assert(numel(fp.Bands) == 2, 'testAddMultipleBands'); % testBandRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addBand(0.2, 0.8, 'FaceColor', [0 1 0], 'FaceAlpha', 0.2); fp.render(); assert(~isempty(fp.Bands(1).hPatch), 'testBandRendered: hPatch created'); assert(ishandle(fp.Bands(1).hPatch), 'testBandRendered: hPatch valid'); ud = get(fp.Bands(1).hPatch, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'band'), 'testBandRendered: UserData type'); + assert(strcmp(ud.FastSense.Type, 'band'), 'testBandRendered: UserData type'); close(fp.hFigure); % testBandRejectsAfterRender - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); threw = false; @@ -644,7 +644,7 @@ function test_add_band() close(fp.hFigure); % testBandDefaults - fp = FastPlot(); + fp = FastSense(); fp.addBand(0, 1); assert(fp.Bands(1).FaceAlpha > 0, 'testBandDefaults: FaceAlpha'); assert(numel(fp.Bands(1).FaceColor) == 3, 'testBandDefaults: FaceColor'); @@ -659,7 +659,7 @@ Expected: FAIL — `addBand` method does not exist **Step 3: Write implementation** -Add new property struct in `FastPlot.m` properties (SetAccess = private), after the Thresholds line (around line 24): +Add new property struct in `FastSense.m` properties (SetAccess = private), after the Thresholds line (around line 24): ```matlab Bands = struct('YLow', {}, 'YHigh', {}, 'FaceColor', {}, ... @@ -676,7 +676,7 @@ Add `addBand` method in the public methods block (after `addThreshold`): % fp.addBand(yLow, yHigh, 'FaceColor', [1 0.9 0.9], 'FaceAlpha', 0.3) if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add bands after render() has been called.'); end @@ -733,7 +733,7 @@ Actually, bands need the X range which is computed later. Better approach: rende 'FaceAlpha', B.FaceAlpha, ... 'EdgeColor', B.EdgeColor, ... 'HandleVisibility', 'off'); - udB.FastPlot = struct( ... + udB.FastSense = struct( ... 'Type', 'band', ... 'Name', B.Label, ... 'LineIndex', [], ... @@ -758,7 +758,7 @@ Expected: All pass. **Step 6: Commit** ```bash -git add FastPlot.m tests/test_add_band.m +git add FastSense.m tests/test_add_band.m git commit -m "Add addBand for horizontal band fills" ``` @@ -767,7 +767,7 @@ git commit -m "Add addBand for horizontal band fills" ### Task 6: addMarker — Custom Event Markers **Files:** -- Modify: `FastPlot.m` (new Markers property, addMarker method, render) +- Modify: `FastSense.m` (new Markers property, addMarker method, render) - Create: `tests/test_add_marker.m` **Step 1: Write the failing test** @@ -776,37 +776,37 @@ Create `tests/test_add_marker.m`: ```matlab function test_add_marker() -%TEST_ADD_MARKER Tests for FastPlot.addMarker method. +%TEST_ADD_MARKER Tests for FastSense.addMarker method. addpath(fullfile(fileparts(mfilename('fullpath')), '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'private')); % testAddMarker - fp = FastPlot(); + fp = FastSense(); fp.addMarker([10 20 30], [1 2 3], 'Marker', 'v', 'Color', [1 0 0], 'Label', 'Faults'); assert(numel(fp.Markers) == 1, 'testAddMarker: count'); assert(isequal(fp.Markers(1).X, [10 20 30]), 'testAddMarker: X'); assert(strcmp(fp.Markers(1).Label, 'Faults'), 'testAddMarker: Label'); % testMarkerRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addMarker([10 50], [0.5 0.8], 'Marker', 'd', 'MarkerSize', 10); fp.render(); assert(~isempty(fp.Markers(1).hLine), 'testMarkerRendered: hLine'); assert(ishandle(fp.Markers(1).hLine), 'testMarkerRendered: valid handle'); ud = get(fp.Markers(1).hLine, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'marker'), 'testMarkerRendered: UserData type'); + assert(strcmp(ud.FastSense.Type, 'marker'), 'testMarkerRendered: UserData type'); close(fp.hFigure); % testMarkerDefaults - fp = FastPlot(); + fp = FastSense(); fp.addMarker([5], [1]); assert(~isempty(fp.Markers(1).Marker), 'testMarkerDefaults: Marker shape'); assert(fp.Markers(1).MarkerSize > 0, 'testMarkerDefaults: MarkerSize'); % testMarkerRejectsAfterRender - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); threw = false; @@ -845,7 +845,7 @@ Add `addMarker` method (after `addBand`): % fp.addMarker(x, y, 'Marker', 'v', 'MarkerSize', 8, 'Color', [1 0 0]) if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add markers after render() has been called.'); end @@ -893,7 +893,7 @@ In `render()`, add marker rendering after threshold/violation markers section (r 'MarkerSize', M.MarkerSize, ... 'Color', M.Color, ... 'HandleVisibility', 'off'); - udM.FastPlot = struct( ... + udM.FastSense = struct( ... 'Type', 'marker', ... 'Name', M.Label, ... 'LineIndex', [], ... @@ -916,7 +916,7 @@ Expected: All pass. **Step 6: Commit** ```bash -git add FastPlot.m tests/test_add_marker.m +git add FastSense.m tests/test_add_marker.m git commit -m "Add addMarker for custom event markers" ``` @@ -925,7 +925,7 @@ git commit -m "Add addMarker for custom event markers" ### Task 7: addShaded — Fill Between Two Curves **Files:** -- Modify: `FastPlot.m` (new Shaded property, addShaded method, render + zoom updates) +- Modify: `FastSense.m` (new Shaded property, addShaded method, render + zoom updates) - Create: `tests/test_add_shaded.m` **Step 1: Write the failing test** @@ -934,7 +934,7 @@ Create `tests/test_add_shaded.m`: ```matlab function test_add_shaded() -%TEST_ADD_SHADED Tests for FastPlot.addShaded method. +%TEST_ADD_SHADED Tests for FastSense.addShaded method. addpath(fullfile(fileparts(mfilename('fullpath')), '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'private')); @@ -943,7 +943,7 @@ function test_add_shaded() x = 1:100; y1 = ones(1,100) * 2; y2 = ones(1,100) * -2; - fp = FastPlot(); + fp = FastSense(); fp.addShaded(x, y1, y2, 'FaceColor', [0 0 1], 'FaceAlpha', 0.2); assert(numel(fp.Shadings) == 1, 'testAddShaded: count'); assert(isequal(fp.Shadings(1).X, x), 'testAddShaded: X'); @@ -951,18 +951,18 @@ function test_add_shaded() assert(isequal(fp.Shadings(1).Y2, y2), 'testAddShaded: Y2'); % testShadedRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addShaded(1:100, ones(1,100), zeros(1,100), 'FaceColor', [1 0 0]); fp.render(); assert(~isempty(fp.Shadings(1).hPatch), 'testShadedRendered: hPatch'); assert(ishandle(fp.Shadings(1).hPatch), 'testShadedRendered: valid'); ud = get(fp.Shadings(1).hPatch, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'shaded'), 'testShadedRendered: type'); + assert(strcmp(ud.FastSense.Type, 'shaded'), 'testShadedRendered: type'); close(fp.hFigure); % testShadedValidation - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addShaded(1:10, 1:10, 1:5); % mismatched lengths @@ -972,7 +972,7 @@ function test_add_shaded() assert(threw, 'testShadedValidation: length mismatch'); % testShadedMonotonicX - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addShaded([3 1 2], [1 1 1], [0 0 0]); @@ -982,7 +982,7 @@ function test_add_shaded() assert(threw, 'testShadedMonotonicX'); % testShadedRejectsAfterRender - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); threw = false; @@ -995,7 +995,7 @@ function test_add_shaded() close(fp.hFigure); % testShadedColumnVectors - fp = FastPlot(); + fp = FastSense(); fp.addShaded((1:10)', (1:10)', zeros(10,1)); assert(isrow(fp.Shadings(1).X), 'testShadedColumnVectors: X row'); assert(isrow(fp.Shadings(1).Y1), 'testShadedColumnVectors: Y1 row'); @@ -1029,7 +1029,7 @@ Add `addShaded` method (after `addMarker`): % fp.addShaded(x, y1, y2, 'FaceColor', [0 0 1], 'FaceAlpha', 0.2) if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add shaded regions after render() has been called.'); end @@ -1038,14 +1038,14 @@ Add `addShaded` method (after `addMarker`): if ~isrow(y2); y2 = y2(:)'; end if numel(x) ~= numel(y1) || numel(x) ~= numel(y2) - error('FastPlot:sizeMismatch', ... + error('FastSense:sizeMismatch', ... 'X, Y1, and Y2 must have the same number of elements.'); end if numel(x) > 1 dx = diff(x); if any(dx(~isnan(dx)) < 0) - error('FastPlot:nonMonotonicX', ... + error('FastSense:nonMonotonicX', ... 'X must be monotonically increasing.'); end end @@ -1094,7 +1094,7 @@ In `render()`, add shading rendering after bands and before data lines: 'FaceAlpha', S.FaceAlpha, ... 'EdgeColor', S.EdgeColor, ... 'HandleVisibility', 'off'); - udS.FastPlot = struct( ... + udS.FastSense = struct( ... 'Type', 'shaded', ... 'Name', S.DisplayName, ... 'LineIndex', [], ... @@ -1158,7 +1158,7 @@ Expected: All pass. **Step 6: Commit** ```bash -git add FastPlot.m tests/test_add_shaded.m +git add FastSense.m tests/test_add_shaded.m git commit -m "Add addShaded for fill between two curves with zoom downsampling" ``` @@ -1167,7 +1167,7 @@ git commit -m "Add addShaded for fill between two curves with zoom downsampling" ### Task 8: addFill — Area Fill to Baseline **Files:** -- Modify: `FastPlot.m` (addFill method) +- Modify: `FastSense.m` (addFill method) - Modify: `tests/test_add_shaded.m` **Step 1: Write the failing test** @@ -1176,7 +1176,7 @@ Append to `tests/test_add_shaded.m` (before final fprintf): ```matlab % testAddFill - fp = FastPlot(); + fp = FastSense(); x = 1:50; y = rand(1,50); fp.addFill(x, y, 'FaceColor', [0 0.5 1], 'FaceAlpha', 0.2); @@ -1184,18 +1184,18 @@ Append to `tests/test_add_shaded.m` (before final fprintf): assert(all(fp.Shadings(1).Y2 == 0), 'testAddFill: baseline is 0'); % testAddFillCustomBaseline - fp = FastPlot(); + fp = FastSense(); fp.addFill(1:10, rand(1,10), 'Baseline', -1); assert(all(fp.Shadings(1).Y2 == -1), 'testAddFillCustomBaseline'); % testAddFillRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addFill(1:100, rand(1,100), 'FaceColor', [0 1 0]); fp.render(); assert(ishandle(fp.Shadings(1).hPatch), 'testAddFillRendered: valid patch'); ud = get(fp.Shadings(1).hPatch, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'shaded'), 'testAddFillRendered: type is shaded'); + assert(strcmp(ud.FastSense.Type, 'shaded'), 'testAddFillRendered: type is shaded'); close(fp.hFigure); ``` @@ -1248,7 +1248,7 @@ Expected: All pass. **Step 6: Commit** ```bash -git add FastPlot.m tests/test_add_shaded.m +git add FastSense.m tests/test_add_shaded.m git commit -m "Add addFill as sugar for addShaded with baseline" ``` @@ -1257,22 +1257,22 @@ git commit -m "Add addFill as sugar for addShaded with baseline" ### Task 9: Theme Defaults for Thresholds & Violations **Files:** -- Modify: `FastPlot.m` (addThreshold method, render) -- Modify: `tests/test_fastplot_theme.m` +- Modify: `FastSense.m` (addThreshold method, render) +- Modify: `tests/test_fastsense_theme.m` **Step 1: Write the failing test** -Append to `tests/test_fastplot_theme.m`: +Append to `tests/test_fastsense_theme.m`: ```matlab % testThresholdUsesThemeDefaults - fp = FastPlot('Theme', struct('ThresholdColor', [0 1 0], 'ThresholdStyle', ':')); + fp = FastSense('Theme', struct('ThresholdColor', [0 1 0], 'ThresholdStyle', ':')); fp.addThreshold(5.0); assert(isequal(fp.Thresholds(1).Color, [0 1 0]), 'testThresholdThemeDefaults: Color'); assert(strcmp(fp.Thresholds(1).LineStyle, ':'), 'testThresholdThemeDefaults: Style'); % testThresholdExplicitOverridesTheme - fp = FastPlot('Theme', struct('ThresholdColor', [0 1 0])); + fp = FastSense('Theme', struct('ThresholdColor', [0 1 0])); fp.addThreshold(5.0, 'Color', [1 0 0]); assert(isequal(fp.Thresholds(1).Color, [1 0 0]), 'testThresholdOverride: Color'); ``` @@ -1285,7 +1285,7 @@ Expected: FAIL — threshold defaults are hardcoded, not from theme **Step 3: Write implementation** -Modify `addThreshold` in `FastPlot.m`. Change the defaults section (around line 132-133) to use theme: +Modify `addThreshold` in `FastSense.m`. Change the defaults section (around line 132-133) to use theme: ```matlab t.Color = obj.Theme.ThresholdColor; @@ -1296,7 +1296,7 @@ These theme values are set in the constructor, so they're always available befor **Step 4: Run test to verify it passes** -Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastplot_theme;"` +Run: `octave --no-gui --eval "addpath('.'); addpath('tests'); addpath('private'); test_fastsense_theme;"` Expected: PASS **Step 5: Run all tests** @@ -1306,16 +1306,16 @@ Expected: All pass. **Step 6: Commit** ```bash -git add FastPlot.m tests/test_fastplot_theme.m +git add FastSense.m tests/test_fastsense_theme.m git commit -m "Use theme defaults for threshold color and style" ``` --- -### Task 10: FastPlotFigure — Basic Grid Layout +### Task 10: FastSenseFigure — Basic Grid Layout **Files:** -- Create: `FastPlotFigure.m` +- Create: `FastSenseFigure.m` - Create: `tests/test_figure_layout.m` **Step 1: Write the failing test** @@ -1324,33 +1324,33 @@ Create `tests/test_figure_layout.m`: ```matlab function test_figure_layout() -%TEST_FIGURE_LAYOUT Tests for FastPlotFigure layout manager. +%TEST_FIGURE_LAYOUT Tests for FastSenseFigure layout manager. addpath(fullfile(fileparts(mfilename('fullpath')), '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'private')); % testConstruction - fig = FastPlotFigure(2, 3); + fig = FastSenseFigure(2, 3); assert(isequal(fig.Grid, [2 3]), 'testConstruction: Grid'); assert(~isempty(fig.hFigure), 'testConstruction: hFigure'); assert(ishandle(fig.hFigure), 'testConstruction: hFigure valid'); close(fig.hFigure); - % testTileReturnsFastPlot - fig = FastPlotFigure(2, 1); + % testTileReturnsFastSense + fig = FastSenseFigure(2, 1); fp = fig.tile(1); - assert(isa(fp, 'FastPlot'), 'testTileReturnsFastPlot'); + assert(isa(fp, 'FastSense'), 'testTileReturnsFastSense'); close(fig.hFigure); % testTileLazy - fig = FastPlotFigure(2, 1); + fig = FastSenseFigure(2, 1); fp1a = fig.tile(1); fp1b = fig.tile(1); assert(fp1a == fp1b, 'testTileLazy: same object on repeat call'); close(fig.hFigure); % testTileCreatesAxes - fig = FastPlotFigure(2, 1); + fig = FastSenseFigure(2, 1); fp = fig.tile(1); fp.addLine(1:100, rand(1,100)); fp.render(); @@ -1359,7 +1359,7 @@ function test_figure_layout() close(fig.hFigure); % testMultipleTiles - fig = FastPlotFigure(2, 2); + fig = FastSenseFigure(2, 2); for i = 1:4 fp = fig.tile(i); fp.addLine(1:50, rand(1,50)); @@ -1372,7 +1372,7 @@ function test_figure_layout() close(fig.hFigure); % testRenderAllSkipsRendered - fig = FastPlotFigure(2, 1); + fig = FastSenseFigure(2, 1); fp1 = fig.tile(1); fp1.addLine(1:10, rand(1,10)); fp1.render(); @@ -1383,7 +1383,7 @@ function test_figure_layout() close(fig.hFigure); % testOutOfBoundsTileErrors - fig = FastPlotFigure(2, 2); + fig = FastSenseFigure(2, 2); threw = false; try fig.tile(5); % only 4 tiles in 2x2 @@ -1399,26 +1399,26 @@ end **Step 2: Run test to verify it fails** -Expected: FAIL — `FastPlotFigure` does not exist +Expected: FAIL — `FastSenseFigure` does not exist **Step 3: Write implementation** -Create `FastPlotFigure.m`: +Create `FastSenseFigure.m`: ```matlab -classdef FastPlotFigure < handle - %FASTPLOTFIGURE Tiled layout manager for FastPlot dashboards. - % fig = FastPlotFigure(rows, cols) - % fig = FastPlotFigure(rows, cols, 'Theme', 'dark') +classdef FastSenseFigure < handle + %FASTSENSEFIGURE Tiled layout manager for FastSense dashboards. + % fig = FastSenseFigure(rows, cols) + % fig = FastSenseFigure(rows, cols, 'Theme', 'dark') properties (Access = public) Grid = [1 1] % [rows, cols] - Theme = [] % FastPlotTheme struct + Theme = [] % FastSenseTheme struct hFigure = [] % figure handle end properties (SetAccess = private) - Tiles = {} % cell array of FastPlot instances + Tiles = {} % cell array of FastSense instances TileAxes = {} % cell array of axes handles TileSpans = {} % cell array of [rowSpan, colSpan] TileThemes = {} % cell array of theme override structs @@ -1432,7 +1432,7 @@ classdef FastPlotFigure < handle end methods (Access = public) - function obj = FastPlotFigure(rows, cols, varargin) + function obj = FastSenseFigure(rows, cols, varargin) obj.Grid = [rows, cols]; nTiles = rows * cols; obj.Tiles = cell(1, nTiles); @@ -1452,7 +1452,7 @@ classdef FastPlotFigure < handle case 'theme' val = varargin{k+1}; if ischar(val) || isstruct(val) - obj.Theme = FastPlotTheme(val); + obj.Theme = FastSenseTheme(val); else obj.Theme = val; end @@ -1463,7 +1463,7 @@ classdef FastPlotFigure < handle end if isempty(obj.Theme) - obj.Theme = FastPlotTheme('default'); + obj.Theme = FastSenseTheme('default'); end obj.hFigure = figure('Visible', 'off', ... @@ -1471,10 +1471,10 @@ classdef FastPlotFigure < handle end function fp = tile(obj, n) - %TILE Get or create the FastPlot instance for tile n. + %TILE Get or create the FastSense instance for tile n. nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotFigure:outOfBounds', ... + error('FastSenseFigure:outOfBounds', ... 'Tile %d is out of range (1-%d).', n, nTiles); end @@ -1491,7 +1491,7 @@ classdef FastPlotFigure < handle end end - fp = FastPlot('Parent', ax, 'Theme', tileTheme); + fp = FastSense('Parent', ax, 'Theme', tileTheme); obj.Tiles{n} = fp; else fp = obj.Tiles{n}; @@ -1503,7 +1503,7 @@ classdef FastPlotFigure < handle % fig.setTileSpan(1, [2 1]) — tile 1 spans 2 rows, 1 col nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotFigure:outOfBounds', ... + error('FastSenseFigure:outOfBounds', ... 'Tile %d is out of range (1-%d).', n, nTiles); end obj.TileSpans{n} = span; @@ -1519,7 +1519,7 @@ classdef FastPlotFigure < handle %SETTILETHEME Set per-tile theme overrides. nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotFigure:outOfBounds', ... + error('FastSenseFigure:outOfBounds', ... 'Tile %d is out of range (1-%d).', n, nTiles); end obj.TileThemes{n} = themeOverrides; @@ -1622,13 +1622,13 @@ Expected: All pass. **Step 6: Commit** ```bash -git add FastPlotFigure.m tests/test_figure_layout.m -git commit -m "Add FastPlotFigure with tiled grid layout and lazy tile creation" +git add FastSenseFigure.m tests/test_figure_layout.m +git commit -m "Add FastSenseFigure with tiled grid layout and lazy tile creation" ``` --- -### Task 11: FastPlotFigure — Tile Spanning & Theme Inheritance +### Task 11: FastSenseFigure — Tile Spanning & Theme Inheritance **Files:** - Modify: `tests/test_figure_layout.m` @@ -1639,7 +1639,7 @@ Append to `tests/test_figure_layout.m` (before final fprintf): ```matlab % testTileSpanning - fig = FastPlotFigure(2, 2); + fig = FastSenseFigure(2, 2); fig.setTileSpan(1, [1 2]); % tile 1 spans both columns fp1 = fig.tile(1); fp1.addLine(1:50, rand(1,50)); @@ -1650,13 +1650,13 @@ Append to `tests/test_figure_layout.m` (before final fprintf): close(fig.hFigure); % testFigureThemePassedToTiles - fig = FastPlotFigure(2, 1, 'Theme', 'dark'); + fig = FastSenseFigure(2, 1, 'Theme', 'dark'); fp = fig.tile(1); assert(all(fp.Theme.Background < [0.2 0.2 0.2]), 'testFigureThemePassedToTiles'); close(fig.hFigure); % testTileThemeOverride - fig = FastPlotFigure(2, 1, 'Theme', 'dark'); + fig = FastSenseFigure(2, 1, 'Theme', 'dark'); fig.setTileTheme(1, struct('AxesColor', [0.3 0 0])); fp = fig.tile(1); assert(isequal(fp.Theme.AxesColor, [0.3 0 0]), 'testTileThemeOverride: AxesColor'); @@ -1664,13 +1664,13 @@ Append to `tests/test_figure_layout.m` (before final fprintf): close(fig.hFigure); % testFigureProperties - fig = FastPlotFigure(1, 1, 'Name', 'MyDash', 'Position', [50 50 800 600]); + fig = FastSenseFigure(1, 1, 'Name', 'MyDash', 'Position', [50 50 800 600]); name = get(fig.hFigure, 'Name'); assert(strcmp(name, 'MyDash'), 'testFigureProperties: Name'); close(fig.hFigure); % testTileLabels - fig = FastPlotFigure(2, 1); + fig = FastSenseFigure(2, 1); fp = fig.tile(1); fp.addLine(1:50, rand(1,50)); fp.render(); @@ -1733,8 +1733,8 @@ git commit -m "Fix integration issues from full test suite" Create `examples/example_dashboard.m`: ```matlab -%% FastPlot Dashboard — Tiled layout with themes and visual enhancements -% Demonstrates FastPlotFigure, theming, bands, shading, and markers +%% FastSense Dashboard — Tiled layout with themes and visual enhancements +% Demonstrates FastSenseFigure, theming, bands, shading, and markers addpath(fullfile(fileparts(mfilename('fullpath')), '..')); @@ -1744,8 +1744,8 @@ x = linspace(0, 300, n); fprintf('Dashboard example: 4 tiles, %d points each, dark theme...\n', n); tic; -fig = FastPlotFigure(2, 2, 'Theme', 'dark', ... - 'Name', 'FastPlot Dashboard Demo', 'Position', [50 50 1400 800]); +fig = FastSenseFigure(2, 2, 'Theme', 'dark', ... + 'Name', 'FastSense Dashboard Demo', 'Position', [50 50 1400 800]); % --- Tile 1: Temperature with alarm bands (spans 2 columns) --- fig.setTileSpan(1, [1 2]); @@ -1813,7 +1813,7 @@ git commit -m "Add dashboard example with tiled layout, bands, shading, and mark Create `examples/example_themes.m`: ```matlab -%% FastPlot Theme Comparison — All 5 built-in themes side by side +%% FastSense Theme Comparison — All 5 built-in themes side by side % Opens one figure per theme to compare visual styles addpath(fullfile(fileparts(mfilename('fullpath')), '..')); @@ -1828,7 +1828,7 @@ themes = {'default', 'dark', 'light', 'industrial', 'scientific'}; for i = 1:numel(themes) themeName = themes{i}; - fp = FastPlot('Theme', themeName); + fp = FastSense('Theme', themeName); fp.addLine(x, y1, 'DisplayName', 'Signal A'); fp.addLine(x, y2, 'DisplayName', 'Signal B'); fp.addLine(x, y3, 'DisplayName', 'Signal C'); @@ -1863,7 +1863,7 @@ git commit -m "Add theme comparison example showing all 5 presets" **Step 1: Update README** Add sections for: -- FastPlotFigure (tiled layouts with spanning) +- FastSenseFigure (tiled layouts with spanning) - Theming system (5 presets, custom themes, color palettes) - New visual methods (addShaded, addBand, addFill, addMarker) - Updated example table with new examples @@ -1872,9 +1872,9 @@ Add sections for: Key additions to the API Reference section: ```markdown -### `FastPlotFigure(rows, cols, ...)` -- Dashboard Layout +### `FastSenseFigure(rows, cols, ...)` -- Dashboard Layout -### `FastPlotTheme(preset, ...)` -- Theme Presets +### `FastSenseTheme(preset, ...)` -- Theme Presets ### `addShaded(x, y1, y2, ...)` -- Fill Between Curves diff --git a/docs/plans/2026-03-06-fastplot-implementation.md b/docs/plans/2026-03-06-fastsense-implementation.md similarity index 89% rename from docs/plans/2026-03-06-fastplot-implementation.md rename to docs/plans/2026-03-06-fastsense-implementation.md index 591f619b..962e50f7 100644 --- a/docs/plans/2026-03-06-fastplot-implementation.md +++ b/docs/plans/2026-03-06-fastsense-implementation.md @@ -1,37 +1,37 @@ -# FastPlot Implementation Plan +# FastSense Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Build a pure-MATLAB plotting library that renders 100M time series points with fluid zoom/pan via dynamic MinMax downsampling. -**Architecture:** Handle class `FastPlot` with builder API. Data lines, thresholds, and violation markers are configured before `render()`. On zoom/pan, an XLim listener triggers binary search → MinMax downsample → XData/YData update on reused line handles. No object recreation, `drawnow limitrate` for frame capping. +**Architecture:** Handle class `FastSense` with builder API. Data lines, thresholds, and violation markers are configured before `render()`. On zoom/pan, an XLim listener triggers binary search → MinMax downsample → XData/YData update on reused line handles. No object recreation, `drawnow limitrate` for frame capping. **Tech Stack:** Pure MATLAB 2020b, classic `figure()`, OpenGL renderer, `matlab.unittest` for testing. -**Design doc:** `FastPlot/docs/2026-03-06-fastplot-design.md` +**Design doc:** `FastSense/docs/2026-03-06-fastsense-design.md` --- ## Task 1: Project Scaffolding **Files:** -- Create: `FastPlot/FastPlot.m` (skeleton only) -- Create: `FastPlot/private/` (directory) -- Create: `FastPlot/tests/` (directory) -- Create: `FastPlot/examples/` (directory) +- Create: `FastSense/FastSense.m` (skeleton only) +- Create: `FastSense/private/` (directory) +- Create: `FastSense/tests/` (directory) +- Create: `FastSense/examples/` (directory) -**Step 1: Create the FastPlot class skeleton** +**Step 1: Create the FastSense class skeleton** -Create `FastPlot/FastPlot.m`: +Create `FastSense/FastSense.m`: ```matlab -classdef FastPlot < handle - %FASTPLOT Ultra-fast time series plotting with dynamic downsampling. +classdef FastSense < handle + %FASTSENSE Ultra-fast time series plotting with dynamic downsampling. % Handles 1K to 100M data points with fluid zoom/pan. % Uses MinMax downsampling to reduce data to screen resolution. % % Usage: - % fp = FastPlot(); + % fp = FastSense(); % fp.addLine(x, y, 'DisplayName', 'Sensor1'); % fp.addThreshold(4.5, 'Direction', 'upper', 'ShowViolations', true); % fp.render(); @@ -66,7 +66,7 @@ classdef FastPlot < handle end methods (Access = public) - function obj = FastPlot(varargin) + function obj = FastSense(varargin) % Parse name-value pairs: 'Parent', 'LinkGroup' p = inputParser; addParameter(p, 'Parent', [], @(x) isempty(x) || isgraphics(x, 'axes')); @@ -110,7 +110,7 @@ end Create empty files so the directory structure exists: -`FastPlot/private/binary_search.m`: +`FastSense/private/binary_search.m`: ```matlab function idx = binary_search(x, val, direction) %BINARY_SEARCH Find index in sorted array via binary search. @@ -120,7 +120,7 @@ function idx = binary_search(x, val, direction) end ``` -`FastPlot/private/minmax_downsample.m`: +`FastSense/private/minmax_downsample.m`: ```matlab function [xOut, yOut] = minmax_downsample(x, y, numBuckets) %MINMAX_DOWNSAMPLE Reduce data to min/max pairs per bucket. @@ -129,7 +129,7 @@ function [xOut, yOut] = minmax_downsample(x, y, numBuckets) end ``` -`FastPlot/private/lttb_downsample.m`: +`FastSense/private/lttb_downsample.m`: ```matlab function [xOut, yOut] = lttb_downsample(x, y, numOut) %LTTB_DOWNSAMPLE Largest Triangle Three Buckets downsampling. @@ -138,7 +138,7 @@ function [xOut, yOut] = lttb_downsample(x, y, numOut) end ``` -`FastPlot/private/compute_violations.m`: +`FastSense/private/compute_violations.m`: ```matlab function [xViol, yViol] = compute_violations(x, y, thresholdValue, direction) %COMPUTE_VIOLATIONS Find points that violate a threshold. @@ -149,10 +149,10 @@ end **Step 3: Create test runner helper** -Create `FastPlot/tests/run_all_tests.m`: +Create `FastSense/tests/run_all_tests.m`: ```matlab function results = run_all_tests() -%RUN_ALL_TESTS Execute all FastPlot unit tests. +%RUN_ALL_TESTS Execute all FastSense unit tests. import matlab.unittest.TestSuite; suite = TestSuite.fromFolder(fileparts(mfilename('fullpath'))); results = run(suite); @@ -164,7 +164,7 @@ end In MATLAB, run: ```matlab -cd FastPlot +cd FastSense dir dir private dir tests @@ -176,12 +176,12 @@ Expected: all files present. ## Task 2: Binary Search **Files:** -- Modify: `FastPlot/private/binary_search.m` -- Create: `FastPlot/tests/TestBinarySearch.m` +- Modify: `FastSense/private/binary_search.m` +- Create: `FastSense/tests/TestBinarySearch.m` **Step 1: Write the test** -Create `FastPlot/tests/TestBinarySearch.m`: +Create `FastSense/tests/TestBinarySearch.m`: ```matlab classdef TestBinarySearch < matlab.unittest.TestCase @@ -258,13 +258,13 @@ end **Step 2: Run test — expect FAIL** ```matlab -cd FastPlot; runtests('tests/TestBinarySearch'); +cd FastSense; runtests('tests/TestBinarySearch'); ``` Expected: failures (placeholder returns 1 always). **Step 3: Implement binary_search** -Replace `FastPlot/private/binary_search.m`: +Replace `FastSense/private/binary_search.m`: ```matlab function idx = binary_search(x, val, direction) %BINARY_SEARCH Find index in sorted array via binary search. @@ -319,12 +319,12 @@ Expected: all 11 tests pass. ## Task 3: MinMax Downsampling **Files:** -- Modify: `FastPlot/private/minmax_downsample.m` -- Create: `FastPlot/tests/TestMinMaxDownsample.m` +- Modify: `FastSense/private/minmax_downsample.m` +- Create: `FastSense/tests/TestMinMaxDownsample.m` **Step 1: Write the test** -Create `FastPlot/tests/TestMinMaxDownsample.m`: +Create `FastSense/tests/TestMinMaxDownsample.m`: ```matlab classdef TestMinMaxDownsample < matlab.unittest.TestCase @@ -426,7 +426,7 @@ runtests('tests/TestMinMaxDownsample'); **Step 3: Implement minmax_downsample** -Replace `FastPlot/private/minmax_downsample.m`: +Replace `FastSense/private/minmax_downsample.m`: ```matlab function [xOut, yOut] = minmax_downsample(x, y, numBuckets) %MINMAX_DOWNSAMPLE Reduce time series to min/max pairs per bucket. @@ -545,12 +545,12 @@ Expected: all 8 tests pass. ## Task 4: LTTB Downsampling **Files:** -- Modify: `FastPlot/private/lttb_downsample.m` -- Create: `FastPlot/tests/TestLTTBDownsample.m` +- Modify: `FastSense/private/lttb_downsample.m` +- Create: `FastSense/tests/TestLTTBDownsample.m` **Step 1: Write the test** -Create `FastPlot/tests/TestLTTBDownsample.m`: +Create `FastSense/tests/TestLTTBDownsample.m`: ```matlab classdef TestLTTBDownsample < matlab.unittest.TestCase @@ -617,7 +617,7 @@ runtests('tests/TestLTTBDownsample'); **Step 3: Implement lttb_downsample** -Replace `FastPlot/private/lttb_downsample.m`: +Replace `FastSense/private/lttb_downsample.m`: ```matlab function [xOut, yOut] = lttb_downsample(x, y, numOut) %LTTB_DOWNSAMPLE Largest Triangle Three Buckets downsampling. @@ -759,12 +759,12 @@ Expected: all 6 tests pass. ## Task 5: Compute Violations **Files:** -- Modify: `FastPlot/private/compute_violations.m` -- Create: `FastPlot/tests/TestComputeViolations.m` +- Modify: `FastSense/private/compute_violations.m` +- Create: `FastSense/tests/TestComputeViolations.m` **Step 1: Write the test** -Create `FastPlot/tests/TestComputeViolations.m`: +Create `FastSense/tests/TestComputeViolations.m`: ```matlab classdef TestComputeViolations < matlab.unittest.TestCase @@ -821,7 +821,7 @@ runtests('tests/TestComputeViolations'); **Step 3: Implement compute_violations** -Replace `FastPlot/private/compute_violations.m`: +Replace `FastSense/private/compute_violations.m`: ```matlab function [xViol, yViol] = compute_violations(x, y, thresholdValue, direction) %COMPUTE_VIOLATIONS Find data points that strictly violate a threshold. @@ -855,21 +855,21 @@ Expected: all 5 tests pass. --- -## Task 6: FastPlot.addLine() +## Task 6: FastSense.addLine() **Files:** -- Modify: `FastPlot/FastPlot.m` — `addLine` method -- Create: `FastPlot/tests/TestAddLine.m` +- Modify: `FastSense/FastSense.m` — `addLine` method +- Create: `FastSense/tests/TestAddLine.m` **Step 1: Write the test** -Create `FastPlot/tests/TestAddLine.m`: +Create `FastSense/tests/TestAddLine.m`: ```matlab classdef TestAddLine < matlab.unittest.TestCase methods (Test) function testAddSingleLine(testCase) - fp = FastPlot(); + fp = FastSense(); x = 1:100; y = rand(1, 100); fp.addLine(x, y); @@ -879,7 +879,7 @@ classdef TestAddLine < matlab.unittest.TestCase end function testAddMultipleLines(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.addLine(1:20, rand(1,20)); fp.addLine(1:5, rand(1,5)); @@ -887,47 +887,47 @@ classdef TestAddLine < matlab.unittest.TestCase end function testLineOptions(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'Color', 'r', 'DisplayName', 'S1'); testCase.verifyEqual(fp.Lines(1).Options.Color, 'r'); testCase.verifyEqual(fp.Lines(1).Options.DisplayName, 'S1'); end function testDownsampleMethodDefault(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); testCase.verifyEqual(fp.Lines(1).DownsampleMethod, 'minmax'); end function testDownsampleMethodOverride(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'DownsampleMethod', 'lttb'); testCase.verifyEqual(fp.Lines(1).DownsampleMethod, 'lttb'); end function testRejectsNonMonotonicX(testCase) - fp = FastPlot(); + fp = FastSense(); testCase.verifyError(@() fp.addLine([1 3 2 4], rand(1,4)), ... - 'FastPlot:nonMonotonicX'); + 'FastSense:nonMonotonicX'); end function testRejectsMismatchedLengths(testCase) - fp = FastPlot(); + fp = FastSense(); testCase.verifyError(@() fp.addLine(1:10, rand(1,5)), ... - 'FastPlot:sizeMismatch'); + 'FastSense:sizeMismatch'); end function testRejectsAfterRender(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); testCase.verifyError(@() fp.addLine(1:5, rand(1,5)), ... - 'FastPlot:alreadyRendered'); + 'FastSense:alreadyRendered'); close(fp.hFigure); end function testColumnVectorsAccepted(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine((1:10)', rand(10,1)); testCase.verifyEqual(numel(fp.Lines(1).X), 10); testCase.verifyTrue(isrow(fp.Lines(1).X)); @@ -942,9 +942,9 @@ end runtests('tests/TestAddLine'); ``` -**Step 3: Implement addLine in FastPlot.m** +**Step 3: Implement addLine in FastSense.m** -Replace the `addLine` method in `FastPlot.m`: +Replace the `addLine` method in `FastSense.m`: ```matlab function addLine(obj, x, y, varargin) %ADDLINE Add a data line to the plot. @@ -953,7 +953,7 @@ function addLine(obj, x, y, varargin) % fp.addLine(x, y, 'DownsampleMethod', 'lttb') if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add lines after render() has been called.'); end @@ -963,14 +963,14 @@ function addLine(obj, x, y, varargin) % Validate sizes match if numel(x) ~= numel(y) - error('FastPlot:sizeMismatch', ... + error('FastSense:sizeMismatch', ... 'X and Y must have the same number of elements.'); end % Validate monotonically increasing X (ignoring NaN) xValid = x(~isnan(x)); if any(diff(xValid) < 0) - error('FastPlot:nonMonotonicX', ... + error('FastSense:nonMonotonicX', ... 'X must be monotonically increasing.'); end @@ -1021,21 +1021,21 @@ Expected: all 9 tests pass (the `testRejectsAfterRender` will fail until render( --- -## Task 7: FastPlot.addThreshold() +## Task 7: FastSense.addThreshold() **Files:** -- Modify: `FastPlot/FastPlot.m` — `addThreshold` method -- Create: `FastPlot/tests/TestAddThreshold.m` +- Modify: `FastSense/FastSense.m` — `addThreshold` method +- Create: `FastSense/tests/TestAddThreshold.m` **Step 1: Write the test** -Create `FastPlot/tests/TestAddThreshold.m`: +Create `FastSense/tests/TestAddThreshold.m`: ```matlab classdef TestAddThreshold < matlab.unittest.TestCase methods (Test) function testAddUpperThreshold(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(4.5, 'Direction', 'upper'); testCase.verifyEqual(numel(fp.Thresholds), 1); testCase.verifyEqual(fp.Thresholds(1).Value, 4.5); @@ -1043,13 +1043,13 @@ classdef TestAddThreshold < matlab.unittest.TestCase end function testAddLowerThreshold(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(-2.0, 'Direction', 'lower'); testCase.verifyEqual(fp.Thresholds(1).Direction, 'lower'); end function testDefaults(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(5.0); testCase.verifyEqual(fp.Thresholds(1).Direction, 'upper'); testCase.verifyEqual(fp.Thresholds(1).ShowViolations, false); @@ -1058,7 +1058,7 @@ classdef TestAddThreshold < matlab.unittest.TestCase end function testCustomOptions(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(3.0, 'Direction', 'lower', ... 'ShowViolations', true, 'Color', [1 0 0], ... 'LineStyle', ':', 'Label', 'LowerBound'); @@ -1070,7 +1070,7 @@ classdef TestAddThreshold < matlab.unittest.TestCase end function testMultipleThresholds(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(1.0); fp.addThreshold(2.0); fp.addThreshold(3.0); @@ -1086,7 +1086,7 @@ end runtests('tests/TestAddThreshold'); ``` -**Step 3: Implement addThreshold in FastPlot.m** +**Step 3: Implement addThreshold in FastSense.m** Replace the `addThreshold` method: ```matlab @@ -1096,7 +1096,7 @@ function addThreshold(obj, value, varargin) % fp.addThreshold(4.5, 'Direction', 'upper', 'ShowViolations', true) if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add thresholds after render() has been called.'); end @@ -1132,21 +1132,21 @@ Expected: all 5 tests pass. --- -## Task 8: FastPlot.render() — Core Rendering +## Task 8: FastSense.render() — Core Rendering **Files:** -- Modify: `FastPlot/FastPlot.m` — `render`, `getAxesPixelWidth`, private helpers -- Create: `FastPlot/tests/TestRender.m` +- Modify: `FastSense/FastSense.m` — `render`, `getAxesPixelWidth`, private helpers +- Create: `FastSense/tests/TestRender.m` **Step 1: Write the test** -Create `FastPlot/tests/TestRender.m`: +Create `FastSense/tests/TestRender.m`: ```matlab classdef TestRender < matlab.unittest.TestCase methods (Test) function testCreatesNewFigure(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'Test'); fp.render(); testCase.verifyTrue(isgraphics(fp.hFigure, 'figure')); @@ -1157,7 +1157,7 @@ classdef TestRender < matlab.unittest.TestCase function testUsesExistingAxes(testCase) fig = figure('Visible', 'off'); ax = axes(fig); - fp = FastPlot('Parent', ax); + fp = FastSense('Parent', ax); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.verifyEqual(fp.hAxes, ax); @@ -1165,7 +1165,7 @@ classdef TestRender < matlab.unittest.TestCase end function testCreatesLineObjects(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'L1'); fp.addLine(1:100, rand(1,100), 'DisplayName', 'L2'); fp.render(); @@ -1176,31 +1176,31 @@ classdef TestRender < matlab.unittest.TestCase end function testUserDataTagging(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'Sensor1'); fp.addThreshold(0.5, 'Label', 'UpperLim'); fp.render(); % Check data line tag ud = fp.Lines(1).hLine.UserData; - testCase.verifyEqual(ud.FastPlot.Type, 'data_line'); - testCase.verifyEqual(ud.FastPlot.Name, 'Sensor1'); - testCase.verifyEqual(ud.FastPlot.LineIndex, 1); + testCase.verifyEqual(ud.FastSense.Type, 'data_line'); + testCase.verifyEqual(ud.FastSense.Name, 'Sensor1'); + testCase.verifyEqual(ud.FastSense.LineIndex, 1); close(fp.hFigure); end function testThresholdLineCreated(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addThreshold(0.5, 'Direction', 'upper', 'Label', 'UL'); fp.render(); testCase.verifyTrue(isgraphics(fp.hThreshLines, 'line')); ud = fp.hThreshLines.UserData; - testCase.verifyEqual(ud.FastPlot.Type, 'threshold'); + testCase.verifyEqual(ud.FastSense.Type, 'threshold'); close(fp.hFigure); end function testViolationMarkersCreated(testCase) - fp = FastPlot(); + fp = FastSense(); y = [0.1 0.2 0.8 0.9 0.3 0.1]; fp.addLine(1:6, y); fp.addThreshold(0.5, 'Direction', 'upper', 'ShowViolations', true); @@ -1215,15 +1215,15 @@ classdef TestRender < matlab.unittest.TestCase end function testDoubleRenderError(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); - testCase.verifyError(@() fp.render(), 'FastPlot:alreadyRendered'); + testCase.verifyError(@() fp.render(), 'FastSense:alreadyRendered'); close(fp.hFigure); end function testStaticAxisLimits(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); ax = fp.hAxes; @@ -1241,7 +1241,7 @@ end runtests('tests/TestRender'); ``` -**Step 3: Implement render and helper methods in FastPlot.m** +**Step 3: Implement render and helper methods in FastSense.m** Update the `hAxes` and `hThreshLines` and `hViolMarkers` to `SetAccess = private` (public read): ```matlab @@ -1265,11 +1265,11 @@ function render(obj) %RENDER Create the plot with all configured lines and thresholds. if obj.IsRendered - error('FastPlot:alreadyRendered', ... - 'render() has already been called on this FastPlot.'); + error('FastSense:alreadyRendered', ... + 'render() has already been called on this FastSense.'); end if isempty(obj.Lines) - error('FastPlot:noLines', 'Add at least one line before render().'); + error('FastSense:noLines', 'Add at least one line before render().'); end % Create or use axes @@ -1316,7 +1316,7 @@ function render(obj) if isfield(opts, 'DisplayName') displayName = opts.DisplayName; end - h.UserData.FastPlot = struct( ... + h.UserData.FastSense = struct( ... 'Type', 'data_line', ... 'Name', displayName, ... 'LineIndex', i, ... @@ -1353,7 +1353,7 @@ function render(obj) 'Color', obj.Thresholds(1).Color, ... 'LineStyle', obj.Thresholds(1).LineStyle, ... 'HandleVisibility', 'off'); - obj.hThreshLines.UserData.FastPlot = struct( ... + obj.hThreshLines.UserData.FastSense = struct( ... 'Type', 'threshold', ... 'Name', 'thresholds', ... 'LineIndex', [], ... @@ -1385,7 +1385,7 @@ function render(obj) 'LineStyle', 'none', 'Marker', 'o', ... 'MarkerSize', 4, 'Color', 'r', ... 'HandleVisibility', 'off'); - obj.hViolMarkers.UserData.FastPlot = struct( ... + obj.hViolMarkers.UserData.FastSense = struct( ... 'Type', 'violation_marker', ... 'Name', 'violations', ... 'LineIndex', [], ... @@ -1450,18 +1450,18 @@ Expected: all 8 tests pass. (Note: `SizeChangedFcn` listener may need adjustment ## Task 9: Zoom/Pan Callbacks **Files:** -- Modify: `FastPlot/FastPlot.m` — `onXLimChanged`, `updateLines`, `updateViolations`, `onResize` -- Create: `FastPlot/tests/TestZoomPan.m` +- Modify: `FastSense/FastSense.m` — `onXLimChanged`, `updateLines`, `updateViolations`, `onResize` +- Create: `FastSense/tests/TestZoomPan.m` **Step 1: Write the test** -Create `FastPlot/tests/TestZoomPan.m`: +Create `FastSense/tests/TestZoomPan.m`: ```matlab classdef TestZoomPan < matlab.unittest.TestCase methods (Test) function testZoomUpdatesPlottedData(testCase) - fp = FastPlot(); + fp = FastSense(); n = 100000; x = linspace(0, 100, n); y = sin(x); @@ -1485,7 +1485,7 @@ classdef TestZoomPan < matlab.unittest.TestCase end function testLazySkipsRedundantUpdate(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:1000, rand(1,1000)); fp.render(); @@ -1501,7 +1501,7 @@ classdef TestZoomPan < matlab.unittest.TestCase end function testViolationsUpdateOnZoom(testCase) - fp = FastPlot(); + fp = FastSense(); y = [zeros(1,500), ones(1,500)*10, zeros(1,500)]; x = 1:1500; fp.addLine(x, y); @@ -1540,7 +1540,7 @@ end runtests('tests/TestZoomPan'); ``` -**Step 3: Implement zoom/pan callbacks in FastPlot.m** +**Step 3: Implement zoom/pan callbacks in FastSense.m** Replace `onXLimChanged`: ```matlab @@ -1679,12 +1679,12 @@ Expected: all 3 tests pass. ## Task 10: Linked Axes **Files:** -- Modify: `FastPlot/FastPlot.m` — add `propagateXLim` method, class-level registry -- Create: `FastPlot/tests/TestLinkedAxes.m` +- Modify: `FastSense/FastSense.m` — add `propagateXLim` method, class-level registry +- Create: `FastSense/tests/TestLinkedAxes.m` **Step 1: Write the test** -Create `FastPlot/tests/TestLinkedAxes.m`: +Create `FastSense/tests/TestLinkedAxes.m`: ```matlab classdef TestLinkedAxes < matlab.unittest.TestCase @@ -1694,11 +1694,11 @@ classdef TestLinkedAxes < matlab.unittest.TestCase ax1 = subplot(2,1,1, 'Parent', fig); ax2 = subplot(2,1,2, 'Parent', fig); - fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'testgroup'); + fp1 = FastSense('Parent', ax1, 'LinkGroup', 'testgroup'); fp1.addLine(1:1000, rand(1,1000)); fp1.render(); - fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'testgroup'); + fp2 = FastSense('Parent', ax2, 'LinkGroup', 'testgroup'); fp2.addLine(1:1000, rand(1,1000)); fp2.render(); @@ -1718,11 +1718,11 @@ classdef TestLinkedAxes < matlab.unittest.TestCase ax1 = subplot(2,1,1, 'Parent', fig); ax2 = subplot(2,1,2, 'Parent', fig); - fp1 = FastPlot('Parent', ax1); + fp1 = FastSense('Parent', ax1); fp1.addLine(1:1000, rand(1,1000)); fp1.render(); - fp2 = FastPlot('Parent', ax2); + fp2 = FastSense('Parent', ax2); fp2.addLine(1:1000, rand(1,1000)); fp2.render(); @@ -1747,11 +1747,11 @@ runtests('tests/TestLinkedAxes'); **Step 3: Implement linked axes** -Add a persistent registry. Add to `FastPlot.m` a static method section: +Add a persistent registry. Add to `FastSense.m` a static method section: ```matlab methods (Static, Access = private) function registry = getLinkRegistry(action, group, obj) - %GETLINKREGISTRY Persistent registry for linked FastPlot instances. + %GETLINKREGISTRY Persistent registry for linked FastSense instances. persistent reg; if isempty(reg) reg = struct(); @@ -1790,7 +1790,7 @@ end Add `propagateXLim` to private methods: ```matlab function propagateXLim(obj, newXLim) - members = FastPlot.getLinkRegistry('get', obj.LinkGroup, []); + members = FastSense.getLinkRegistry('get', obj.LinkGroup, []); for i = 1:numel(members) other = members{i}; if other ~= obj && isvalid(other) && isgraphics(other.hAxes) @@ -1807,7 +1807,7 @@ In `render()`, after setting `obj.IsRendered = true`, add registration: ```matlab % Register in link group if ~isempty(obj.LinkGroup) - FastPlot.getLinkRegistry('register', obj.LinkGroup, obj); + FastSense.getLinkRegistry('register', obj.LinkGroup, obj); end ``` @@ -1823,7 +1823,7 @@ Expected: both tests pass. ## Task 11: Resize Handling Fix **Files:** -- Modify: `FastPlot/FastPlot.m` — fix resize listener for 2020b +- Modify: `FastSense/FastSense.m` — fix resize listener for 2020b **Step 1: Fix resize listener** @@ -1846,7 +1846,7 @@ obj.hFigure.ResizeFcn = @(s,e) obj.onResize(s,e); **Step 2: Run all tests** ```matlab -cd FastPlot; runtests('tests'); +cd FastSense; runtests('tests'); ``` Expected: all tests pass. @@ -1855,25 +1855,25 @@ Expected: all tests pass. ## Task 12: Example Scripts **Files:** -- Create: `FastPlot/examples/example_basic.m` -- Create: `FastPlot/examples/example_multi.m` -- Create: `FastPlot/examples/example_100M.m` -- Create: `FastPlot/examples/example_linked.m` +- Create: `FastSense/examples/example_basic.m` +- Create: `FastSense/examples/example_multi.m` +- Create: `FastSense/examples/example_100M.m` +- Create: `FastSense/examples/example_linked.m` **Step 1: Create example_basic.m** ```matlab -%% FastPlot Basic Example — 10M points single line +%% FastSense Basic Example — 10M points single line % Demonstrates basic usage with a large time series n = 10e6; x = linspace(0, 100, n); y = sin(x * 2 * pi / 10) + 0.5 * randn(1, n); -fprintf('Creating FastPlot with %d points...\n', n); +fprintf('Creating FastSense with %d points...\n', n); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Noisy Sine', 'Color', [0 0.4470 0.7410]); fp.addThreshold(1.5, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', 'r', 'Label', 'Upper'); @@ -1882,14 +1882,14 @@ fp.addThreshold(-1.5, 'Direction', 'lower', 'ShowViolations', true, ... fp.render(); fprintf('Rendered in %.3f seconds. Try zooming and panning!\n', toc); -title(fp.hAxes, 'FastPlot — 10M Points'); +title(fp.hAxes, 'FastSense — 10M Points'); legend(fp.hAxes, 'show'); ``` **Step 2: Create example_multi.m** ```matlab -%% FastPlot Multi-Line Example — 5 sensors, 1M points each +%% FastSense Multi-Line Example — 5 sensors, 1M points each % Demonstrates multiple lines with thresholds n = 1e6; @@ -1898,7 +1898,7 @@ x = linspace(0, 60, n); % 60 seconds fprintf('Creating 5 lines x %d points = %d total...\n', n, 5*n); tic; -fp = FastPlot(); +fp = FastSense(); colors = lines(5); for i = 1:5 y = sin(x * 2 * pi * i / 10) + 0.3 * randn(1, n) + i * 2; @@ -1910,14 +1910,14 @@ fp.addThreshold(10, 'Direction', 'upper', 'ShowViolations', true, ... fp.render(); fprintf('Rendered in %.3f seconds.\n', toc); -title(fp.hAxes, 'FastPlot — 5 Lines x 1M Points'); +title(fp.hAxes, 'FastSense — 5 Lines x 1M Points'); legend(fp.hAxes, 'show'); ``` **Step 3: Create example_100M.m** ```matlab -%% FastPlot Stress Test — 100M points +%% FastSense Stress Test — 100M points % Demonstrates performance at maximum scale n = 100e6; @@ -1930,30 +1930,30 @@ fprintf('Data generated in %.1f seconds.\n', toc); fprintf('Rendering...\n'); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', '100M Random Walk', 'Color', [0 0.4 0.8]); fp.addThreshold(3, 'Direction', 'upper', 'ShowViolations', true); fp.addThreshold(-3, 'Direction', 'lower', 'ShowViolations', true); fp.render(); fprintf('Rendered in %.3f seconds. Zoom in to see detail!\n', toc); -title(fp.hAxes, 'FastPlot — 100M Points Stress Test'); +title(fp.hAxes, 'FastSense — 100M Points Stress Test'); ``` **Step 4: Create example_linked.m** ```matlab -%% FastPlot Linked Axes Example +%% FastSense Linked Axes Example % Demonstrates synchronized zoom/pan across subplots n = 5e6; x = linspace(0, 100, n); -fig = figure('Name', 'FastPlot Linked Axes', 'Position', [100 100 1200 600]); +fig = figure('Name', 'FastSense Linked Axes', 'Position', [100 100 1200 600]); % Top plot ax1 = subplot(3,1,1, 'Parent', fig); -fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'sync'); +fp1 = FastSense('Parent', ax1, 'LinkGroup', 'sync'); fp1.addLine(x, sin(x * 2 * pi / 5) + 0.2*randn(1,n), ... 'DisplayName', 'Pressure', 'Color', 'b'); fp1.addThreshold(1.2, 'Direction', 'upper', 'ShowViolations', true); @@ -1962,7 +1962,7 @@ title(ax1, 'Pressure'); % Middle plot ax2 = subplot(3,1,2, 'Parent', fig); -fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'sync'); +fp2 = FastSense('Parent', ax2, 'LinkGroup', 'sync'); fp2.addLine(x, cos(x * 2 * pi / 8) + 0.3*randn(1,n), ... 'DisplayName', 'Temperature', 'Color', [0.8 0.3 0]); fp2.render(); @@ -1970,7 +1970,7 @@ title(ax2, 'Temperature'); % Bottom plot ax3 = subplot(3,1,3, 'Parent', fig); -fp3 = FastPlot('Parent', ax3, 'LinkGroup', 'sync'); +fp3 = FastSense('Parent', ax3, 'LinkGroup', 'sync'); fp3.addLine(x, cumsum(randn(1,n))/sqrt(n), ... 'DisplayName', 'Vibration', 'Color', [0 0.6 0]); fp3.render(); @@ -1984,13 +1984,13 @@ fprintf('All 3 plots linked. Zoom one — all follow!\n'); ## Task 13: Final Assembly and README **Files:** -- Create: `FastPlot/README.md` +- Create: `FastSense/README.md` - Verify full test suite passes **Step 1: Create README.md** ```markdown -# FastPlot +# FastSense Ultra-fast time series plotting for MATLAB 2020b. Plot 100M+ data points with fluid zoom and pan. @@ -2008,9 +2008,9 @@ Ultra-fast time series plotting for MATLAB 2020b. Plot 100M+ data points with fl ## Quick Start ```matlab -addpath('FastPlot'); +addpath('FastSense'); -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Sensor1', 'Color', 'b'); fp.addThreshold(4.5, 'Direction', 'upper', 'ShowViolations', true); fp.render(); @@ -2028,7 +2028,7 @@ See `examples/` folder for complete demos. **Step 2: Run full test suite** ```matlab -cd FastPlot; runtests('tests'); +cd FastSense; runtests('tests'); ``` Expected: all tests pass across all test files. diff --git a/docs/plans/2026-03-06-lazy-pyramid-design.md b/docs/plans/2026-03-06-lazy-pyramid-design.md index 25c2c552..e66ffb06 100644 --- a/docs/plans/2026-03-06-lazy-pyramid-design.md +++ b/docs/plans/2026-03-06-lazy-pyramid-design.md @@ -1,4 +1,4 @@ -# Lazy Multi-Resolution Pyramid for FastPlot +# Lazy Multi-Resolution Pyramid for FastSense ## Design Document @@ -77,9 +77,9 @@ Replaces the inner loop of `updateLines()`: | Component | Change | |---|---| -| `FastPlot.m` Lines struct | Add `Pyramid` field (cell array) | -| `FastPlot.m` `updateLines()` | Level selection + pyramid query | -| `FastPlot.m` new private method | `buildPyramidLevel()`, `selectPyramidLevel()` | +| `FastSense.m` Lines struct | Add `Pyramid` field (cell array) | +| `FastSense.m` `updateLines()` | Level selection + pyramid query | +| `FastSense.m` new private method | `buildPyramidLevel()`, `selectPyramidLevel()` | | `private/minmax_downsample.m` | No change | | Tests | New `test_pyramid.m` | @@ -95,6 +95,6 @@ Full zoom-out query: read level 2 (5K points) → downsample to ~4K → **<1ms** ## 6. Constraints -- Pyramid is invalidated if data changes (FastPlot data is immutable after `addLine()`, so this is safe) +- Pyramid is invalidated if data changes (FastSense data is immutable after `addLine()`, so this is safe) - NaN-aware: pyramid levels preserve NaN segment structure via `minmax_downsample` - No render-time cost: zero overhead if user never zooms out far enough to trigger pyramid build diff --git a/docs/plans/2026-03-06-mex-acceleration-design.md b/docs/plans/2026-03-06-mex-acceleration-design.md index 91ee4df8..bc46871c 100644 --- a/docs/plans/2026-03-06-mex-acceleration-design.md +++ b/docs/plans/2026-03-06-mex-acceleration-design.md @@ -1,4 +1,4 @@ -# FastPlot MEX Acceleration — Design Document +# FastSense MEX Acceleration — Design Document **Date:** 2026-03-06 **Status:** Approved @@ -7,7 +7,7 @@ ## 1. Goal -Add C MEX implementations with SIMD intrinsics (AVX2 for x86_64, NEON for ARM64) for the three performance-critical functions in FastPlot. Existing pure-MATLAB implementations remain as automatic fallbacks. +Add C MEX implementations with SIMD intrinsics (AVX2 for x86_64, NEON for ARM64) for the three performance-critical functions in FastSense. Existing pure-MATLAB implementations remain as automatic fallbacks. --- @@ -28,7 +28,7 @@ Add C MEX implementations with SIMD intrinsics (AVX2 for x86_64, NEON for ARM64) ### File Layout ``` -FastPlot/ +FastSense/ ├── private/ │ ├── minmax_downsample.m (modified: calls minmax_core_mex or minmax_core) │ ├── lttb_downsample.m (modified: calls lttb_core_mex or lttb_core) @@ -130,7 +130,7 @@ Provides unified macros for: load, store, min, max, fabs, multiply, subtract ope ## 8. What Does NOT Change - All existing `.m` files keep their pure-MATLAB fallback implementations -- Public FastPlot API is unchanged +- Public FastSense API is unchanged - Test suite passes with or without MEX compiled - Octave compatibility preserved for pure-MATLAB path - No new toolbox dependencies diff --git a/docs/plans/2026-03-06-mex-acceleration-implementation.md b/docs/plans/2026-03-06-mex-acceleration-implementation.md index 212f271c..219a0731 100644 --- a/docs/plans/2026-03-06-mex-acceleration-implementation.md +++ b/docs/plans/2026-03-06-mex-acceleration-implementation.md @@ -8,22 +8,22 @@ **Tech Stack:** C11, MATLAB MEX API (`mex.h`), AVX2/SSE2 intrinsics (`immintrin.h`), ARM NEON intrinsics (`arm_neon.h`), MATLAB R2020b+. -**Design doc:** `FastPlot/docs/plans/2026-03-06-mex-acceleration-design.md` +**Design doc:** `FastSense/docs/plans/2026-03-06-mex-acceleration-design.md` --- ## Task 1: SIMD Abstraction Header **Files:** -- Create: `FastPlot/private/mex_src/simd_utils.h` +- Create: `FastSense/private/mex_src/simd_utils.h` **Step 1: Create directory and write simd_utils.h** ```bash -mkdir -p FastPlot/private/mex_src +mkdir -p FastSense/private/mex_src ``` -Create `FastPlot/private/mex_src/simd_utils.h`: +Create `FastSense/private/mex_src/simd_utils.h`: ```c #ifndef SIMD_UTILS_H @@ -33,7 +33,7 @@ Create `FastPlot/private/mex_src/simd_utils.h`: #include /* - * simd_utils.h — Compile-time SIMD abstraction for FastPlot MEX files. + * simd_utils.h — Compile-time SIMD abstraction for FastSense MEX files. * * Provides unified operations on packed doubles: * - AVX2: 4 doubles per vector (__m256d) @@ -177,7 +177,7 @@ static inline double simd_hmin(simd_double v) { return v; } **Step 2: Commit** ```bash -cd FastPlot +cd FastSense git add private/mex_src/simd_utils.h git commit -m "Add SIMD abstraction header for MEX acceleration @@ -190,11 +190,11 @@ Provides unified macros for packed-double min/max/mul/sub/abs." ## Task 2: binary_search_mex **Files:** -- Create: `FastPlot/private/mex_src/binary_search_mex.c` +- Create: `FastSense/private/mex_src/binary_search_mex.c` **Step 1: Write binary_search_mex.c** -Create `FastPlot/private/mex_src/binary_search_mex.c`: +Create `FastSense/private/mex_src/binary_search_mex.c`: ```c /* @@ -223,19 +223,19 @@ void mexFunction(int nlhs, mxArray *plhs[], { /* Validate inputs */ if (nrhs != 3) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:nrhs", "Three inputs required: x, val, direction."); } if (!mxIsDouble(prhs[0]) || mxIsComplex(prhs[0])) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:notDouble", "x must be a real double array."); } if (!mxIsDouble(prhs[1]) || mxGetNumberOfElements(prhs[1]) != 1) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:notScalar", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:notScalar", "val must be a scalar double."); } if (!mxIsChar(prhs[2])) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:notChar", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:notChar", "direction must be a char array."); } @@ -288,7 +288,7 @@ void mexFunction(int nlhs, mxArray *plhs[], **Step 2: Commit** ```bash -cd FastPlot +cd FastSense git add private/mex_src/binary_search_mex.c git commit -m "Add binary_search MEX implementation @@ -301,11 +301,11 @@ loop overhead for O(log n) search on sorted double arrays." ## Task 3: minmax_core_mex **Files:** -- Create: `FastPlot/private/mex_src/minmax_core_mex.c` +- Create: `FastSense/private/mex_src/minmax_core_mex.c` **Step 1: Write minmax_core_mex.c** -Create `FastPlot/private/mex_src/minmax_core_mex.c`: +Create `FastSense/private/mex_src/minmax_core_mex.c`: ```c /* @@ -335,11 +335,11 @@ void mexFunction(int nlhs, mxArray *plhs[], { /* Validate inputs */ if (nrhs != 3) { - mexErrMsgIdAndTxt("FastPlot:minmax_core_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:minmax_core_mex:nrhs", "Three inputs required: x, y, numBuckets."); } if (!mxIsDouble(prhs[0]) || !mxIsDouble(prhs[1])) { - mexErrMsgIdAndTxt("FastPlot:minmax_core_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:minmax_core_mex:notDouble", "x and y must be real double arrays."); } @@ -433,7 +433,7 @@ void mexFunction(int nlhs, mxArray *plhs[], **Step 2: Commit** ```bash -cd FastPlot +cd FastSense git add private/mex_src/minmax_core_mex.c git commit -m "Add minmax_core MEX implementation with SIMD @@ -446,11 +446,11 @@ index-tracking pass. Handles remainder by extending last bucket." ## Task 4: lttb_core_mex **Files:** -- Create: `FastPlot/private/mex_src/lttb_core_mex.c` +- Create: `FastSense/private/mex_src/lttb_core_mex.c` **Step 1: Write lttb_core_mex.c** -Create `FastPlot/private/mex_src/lttb_core_mex.c`: +Create `FastSense/private/mex_src/lttb_core_mex.c`: ```c /* @@ -479,11 +479,11 @@ void mexFunction(int nlhs, mxArray *plhs[], { /* Validate inputs */ if (nrhs != 3) { - mexErrMsgIdAndTxt("FastPlot:lttb_core_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:lttb_core_mex:nrhs", "Three inputs required: x, y, numOut."); } if (!mxIsDouble(prhs[0]) || !mxIsDouble(prhs[1])) { - mexErrMsgIdAndTxt("FastPlot:lttb_core_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:lttb_core_mex:notDouble", "x and y must be real double arrays."); } @@ -611,7 +611,7 @@ void mexFunction(int nlhs, mxArray *plhs[], **Step 2: Commit** ```bash -cd FastPlot +cd FastSense git add private/mex_src/lttb_core_mex.c git commit -m "Add lttb_core MEX implementation with SIMD @@ -624,15 +624,15 @@ Sequential outer loop preserved (each bucket depends on previous)." ## Task 5: Build Script **Files:** -- Create: `FastPlot/build_mex.m` +- Create: `FastSense/build_mex.m` **Step 1: Write build_mex.m** -Create `FastPlot/build_mex.m`: +Create `FastSense/build_mex.m`: ```matlab function build_mex() -%BUILD_MEX Compile FastPlot MEX files with platform-appropriate SIMD flags. +%BUILD_MEX Compile FastSense MEX files with platform-appropriate SIMD flags. % build_mex() % % Detects CPU architecture, sets compiler flags for AVX2/SSE2/NEON, @@ -734,7 +734,7 @@ end **Step 2: Commit** ```bash -cd FastPlot +cd FastSense git add build_mex.m git commit -m "Add build_mex.m script for MEX compilation @@ -747,9 +747,9 @@ Compiles all 3 MEX files into private/. Safe to re-run." ## Task 6: Wire MEX Fallback into MATLAB Wrappers **Files:** -- Modify: `FastPlot/private/binary_search.m` (lines 1-39) -- Modify: `FastPlot/private/minmax_downsample.m` (lines 21, 73) -- Modify: `FastPlot/private/lttb_downsample.m` (lines 59) +- Modify: `FastSense/private/binary_search.m` (lines 1-39) +- Modify: `FastSense/private/minmax_downsample.m` (lines 21, 73) +- Modify: `FastSense/private/lttb_downsample.m` (lines 59) **Step 1: Modify binary_search.m — add MEX dispatch** @@ -874,7 +874,7 @@ with: **Step 4: Run existing tests to verify fallback path still works** ```bash -cd FastPlot && matlab -batch "addpath('tests'); addpath('private'); run_all_tests()" +cd FastSense && matlab -batch "addpath('tests'); addpath('private'); run_all_tests()" ``` Expected: All existing tests pass (MEX not compiled yet, so fallback path runs). @@ -882,7 +882,7 @@ Expected: All existing tests pass (MEX not compiled yet, so fallback path runs). **Step 5: Commit** ```bash -cd FastPlot +cd FastSense git add private/binary_search.m private/minmax_downsample.m private/lttb_downsample.m git commit -m "Wire MEX fallback dispatch into MATLAB wrappers @@ -895,11 +895,11 @@ availability. Falls back to existing pure-MATLAB code if not compiled." ## Task 7: MEX Parity Tests **Files:** -- Create: `FastPlot/tests/test_mex_parity.m` +- Create: `FastSense/tests/test_mex_parity.m` **Step 1: Write test_mex_parity.m** -Create `FastPlot/tests/test_mex_parity.m`: +Create `FastSense/tests/test_mex_parity.m`: ```matlab function test_mex_parity() @@ -1117,7 +1117,7 @@ end **Step 2: Commit** ```bash -cd FastPlot +cd FastSense git add tests/test_mex_parity.m git commit -m "Add MEX parity tests @@ -1131,11 +1131,11 @@ Skips gracefully if MEX not compiled." ## Task 8: MEX Edge Case Tests **Files:** -- Create: `FastPlot/tests/test_mex_edge_cases.m` +- Create: `FastSense/tests/test_mex_edge_cases.m` **Step 1: Write test_mex_edge_cases.m** -Create `FastPlot/tests/test_mex_edge_cases.m`: +Create `FastSense/tests/test_mex_edge_cases.m`: ```matlab function test_mex_edge_cases() @@ -1263,7 +1263,7 @@ end **Step 2: Commit** ```bash -cd FastPlot +cd FastSense git add tests/test_mex_edge_cases.m git commit -m "Add MEX edge case tests @@ -1278,7 +1278,7 @@ negative values, and remainder handling for all 3 MEX functions." **Step 1: Compile MEX files** ```matlab -cd FastPlot +cd FastSense build_mex() ``` @@ -1287,7 +1287,7 @@ Expected: `3/3 MEX files compiled successfully.` **Step 2: Run full test suite** ```matlab -cd FastPlot +cd FastSense addpath('tests'); addpath('private'); run_all_tests() ``` @@ -1304,18 +1304,18 @@ Add a "Building MEX (optional)" section after "Requirements" in `README.md`: For maximum performance, compile the C MEX accelerators: ```matlab -cd FastPlot +cd FastSense build_mex() ``` Requires a C compiler (Xcode on macOS, GCC on Linux, MSVC on Windows). Uses AVX2/NEON SIMD intrinsics when available. -If MEX files are not compiled, FastPlot automatically uses the pure-MATLAB implementations — no functionality is lost. +If MEX files are not compiled, FastSense automatically uses the pure-MATLAB implementations — no functionality is lost. ``` **Step 4: Add `*.mex*` to .gitignore** -Create or update `FastPlot/.gitignore`: +Create or update `FastSense/.gitignore`: ``` *.mexmaci64 @@ -1328,7 +1328,7 @@ Create or update `FastPlot/.gitignore`: **Step 5: Commit** ```bash -cd FastPlot +cd FastSense git add README.md .gitignore git commit -m "Update README with MEX build instructions, add .gitignore @@ -1343,16 +1343,16 @@ binaries and .DS_Store files." **Step 1: Run existing benchmark to compare** ```matlab -cd FastPlot/examples +cd FastSense/examples benchmark() ``` -Expected: With MEX compiled, FastPlot zoom/downsample times should be noticeably faster, especially for LTTB at large data sizes. +Expected: With MEX compiled, FastSense zoom/downsample times should be noticeably faster, especially for LTTB at large data sizes. **Step 2: Verify benchmark_zoom still works** ```matlab -cd FastPlot/examples +cd FastSense/examples benchmark_zoom() ``` diff --git a/docs/plans/2026-03-07-datetime-design.md b/docs/plans/2026-03-07-datetime-design.md index 83143a54..19127e6a 100644 --- a/docs/plans/2026-03-07-datetime-design.md +++ b/docs/plans/2026-03-07-datetime-design.md @@ -1,8 +1,8 @@ -# FastPlot Datetime Support — Design +# FastSense Datetime Support — Design ## Goal -Add datetime-aware X axis display to FastPlot. Users pass `datenum` values (or MATLAB `datetime` objects, auto-converted) and get human-readable date/time tick labels that adapt to zoom level. +Add datetime-aware X axis display to FastSense. Users pass `datenum` values (or MATLAB `datetime` objects, auto-converted) and get human-readable date/time tick labels that adapt to zoom level. ## Architecture @@ -18,7 +18,7 @@ Add datetime-aware X axis display to FastPlot. Users pass `datenum` values (or M ### Storage - New field in `Lines` struct: `XType` — `'numeric'` (default) or `'datenum'` -- New property on `FastPlot`: `XType` — set from first line that declares `'datenum'`, enforced consistent across all lines on same axes +- New property on `FastSense`: `XType` — set from first line that declares `'datenum'`, enforced consistent across all lines on same axes ### Tick Formatting @@ -37,18 +37,18 @@ Tick formatter re-runs on every zoom/pan (inside `onXLimChanged`). - **Crosshair text:** `datestr(xp, 'mmm dd HH:MM:SS')` instead of `sprintf('x=%.4g', xp)` - **Data cursor label:** `datestr(sx, 'mmm dd HH:MM:SS')` instead of `sprintf('(%.4g, %.4g)', sx, sy)` -- Toolbar checks `fp.XType` (or first `FastPlots{i}.XType`) to decide formatting +- Toolbar checks `fp.XType` (or first `FastSenses{i}.XType`) to decide formatting -### FastPlotFigure +### FastSenseFigure -Each tile can independently have `XType == 'datenum'` or `'numeric'`. No figure-level setting needed — it inherits from the FastPlot instances. +Each tile can independently have `XType == 'datenum'` or `'numeric'`. No figure-level setting needed — it inherits from the FastSense instances. ## What Changes | File | Change | |---|---| -| `FastPlot.m` | Add `XType` property; detect datetime input in `addLine`; install tick formatter in `render`; update ticks in `onXLimChanged` | -| `FastPlotToolbar.m` | Format crosshair/cursor display based on `XType` | +| `FastSense.m` | Add `XType` property; detect datetime input in `addLine`; install tick formatter in `render`; update ticks in `onXLimChanged` | +| `FastSenseToolbar.m` | Format crosshair/cursor display based on `XType` | | `tests/test_toolbar.m` | Add datetime formatting tests | | `tests/test_datetime.m` | New: datenum axes, tick formatting, datetime auto-conversion | | `examples/example_toolbar.m` | Add datetime demo section | @@ -61,8 +61,8 @@ Each tile can independently have `XType == 'datenum'` or `'numeric'`. No figure- - `lttb_downsample.m` / MEX - `compute_violations.m` - Zoom/pan pipeline (internally) -- `FastPlotFigure.m` -- `FastPlotTheme.m` +- `FastSenseFigure.m` +- `FastSenseTheme.m` ## Compatibility diff --git a/docs/plans/2026-03-07-datetime.md b/docs/plans/2026-03-07-datetime.md index d3b8b7a2..1ee1f30b 100644 --- a/docs/plans/2026-03-07-datetime.md +++ b/docs/plans/2026-03-07-datetime.md @@ -4,7 +4,7 @@ **Goal:** Add datetime-aware X axis display so users can pass `datenum` values (or MATLAB `datetime` objects) and get auto-formatted date/time tick labels that adapt to zoom level. -**Architecture:** Store `XType` (`'numeric'` or `'datenum'`) on each FastPlot instance. Internally everything stays as doubles — no changes to binary_search, downsampling, or MEX. On render and zoom, a tick formatter picks date format based on visible range. Toolbar crosshair/cursor uses `datestr()` for display. MATLAB `datetime` input is auto-converted to `datenum`. +**Architecture:** Store `XType` (`'numeric'` or `'datenum'`) on each FastSense instance. Internally everything stays as doubles — no changes to binary_search, downsampling, or MEX. On render and zoom, a tick formatter picks date format based on visible range. Toolbar crosshair/cursor uses `datestr()` for display. MATLAB `datetime` input is auto-converted to `datenum`. **Tech Stack:** MATLAB/Octave `datenum`/`datestr`, `XTick`/`XTickLabel` axis properties, `datetick` (reference only — we roll our own for zoom-aware formatting). @@ -13,7 +13,7 @@ ### Task 1: XType property + datenum detection in addLine **Files:** -- Modify: `FastPlot.m` (properties, addLine method) +- Modify: `FastSense.m` (properties, addLine method) - Create: `tests/test_datetime.m` **Step 1: Write the failing test** @@ -31,12 +31,12 @@ function test_datetime() drawnow; % testXTypeDefaultIsNumeric - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); assert(strcmp(fp.XType, 'numeric'), 'testXTypeDefault: should be numeric'); % testXTypeDatenum - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:99)/24; fp.addLine(x, rand(1,100), 'XType', 'datenum'); assert(strcmp(fp.XType, 'datenum'), 'testXTypeDatenum: should be datenum'); @@ -44,7 +44,7 @@ function test_datetime() % testDatetimeAutoConvert % Only run in MATLAB where datetime exists if exist('datetime', 'class') - fp = FastPlot(); + fp = FastSense(); dt = datetime(2024,1,1) + hours(0:99); fp.addLine(dt, rand(1,100)); assert(strcmp(fp.XType, 'datenum'), 'testDatetimeAutoConvert: should be datenum'); @@ -62,13 +62,13 @@ Expected: FAIL — `XType` property not defined **Step 3: Write minimal implementation** -In `FastPlot.m`, add `XType` to public properties (after `Theme`): +In `FastSense.m`, add `XType` to public properties (after `Theme`): ```matlab XType = 'numeric' % 'numeric' or 'datenum' ``` -In `FastPlot.m`, modify `addLine` method. After the row vector coercion (line 92-93) and before the size validation (line 96), add datetime detection and conversion: +In `FastSense.m`, modify `addLine` method. After the row vector coercion (line 92-93) and before the size validation (line 96), add datetime detection and conversion: ```matlab % Detect and convert datetime input @@ -88,7 +88,7 @@ In `addLine`, inside the varargin parsing loop (after the `DownsampleMethod` che if strcmp(obj.XType, 'numeric') || strcmp(obj.XType, val) obj.XType = val; else - error('FastPlot:mixedXType', ... + error('FastSense:mixedXType', ... 'All lines must use the same XType.'); end ``` @@ -101,7 +101,7 @@ Expected: PASS **Step 5: Commit** ```bash -git add FastPlot.m tests/test_datetime.m +git add FastSense.m tests/test_datetime.m git commit -m "Add XType property and datenum/datetime detection in addLine" ``` @@ -110,7 +110,7 @@ git commit -m "Add XType property and datenum/datetime detection in addLine" ### Task 2: Tick formatter — auto-format based on zoom level **Files:** -- Modify: `FastPlot.m` (render, onXLimChanged, new private method) +- Modify: `FastSense.m` (render, onXLimChanged, new private method) - Modify: `tests/test_datetime.m` **Step 1: Write the failing test** @@ -119,7 +119,7 @@ Append to `tests/test_datetime.m` before the final fprintf: ```matlab % testTickLabelsAreDateStrings - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:99)/24; % ~4 days of hourly data fp.addLine(x, rand(1,100), 'XType', 'datenum'); fp.render(); @@ -136,7 +136,7 @@ Append to `tests/test_datetime.m` before the final fprintf: close(fp.hFigure); % testTickFormatChangesOnZoom - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:9999)/86400; % ~0.1s resolution fp.addLine(x, rand(1,10000), 'XType', 'datenum'); fp.render(); @@ -165,7 +165,7 @@ Expected: FAIL — tick labels are plain numbers **Step 3: Write minimal implementation** -Add a new private method to `FastPlot.m`: +Add a new private method to `FastSense.m`: ```matlab function updateDatetimeTicks(obj) @@ -210,7 +210,7 @@ Expected: PASS **Step 5: Commit** ```bash -git add FastPlot.m tests/test_datetime.m +git add FastSense.m tests/test_datetime.m git commit -m "Add auto-format datetime tick labels based on zoom level" ``` @@ -219,7 +219,7 @@ git commit -m "Add auto-format datetime tick labels based on zoom level" ### Task 3: Toolbar datetime display — crosshair and cursor **Files:** -- Modify: `FastPlotToolbar.m` (onMouseMove, onMouseClick) +- Modify: `FastSenseToolbar.m` (onMouseMove, onMouseClick) - Modify: `tests/test_datetime.m` **Step 1: Write the failing test** @@ -230,9 +230,9 @@ Append to `tests/test_datetime.m`: % testToolbarFormatX % Verify the static helper returns date string for datenum XType xVal = datenum(2024, 3, 15, 10, 30, 45); - result = FastPlotToolbar.formatX(xVal, 'datenum'); + result = FastSenseToolbar.formatX(xVal, 'datenum'); assert(any(result == ':'), 'testToolbarFormatX: should contain colon'); - resultNum = FastPlotToolbar.formatX(42.5, 'numeric'); + resultNum = FastSenseToolbar.formatX(42.5, 'numeric'); assert(~any(resultNum == ':'), 'testToolbarFormatXNum: should not contain colon'); ``` @@ -242,7 +242,7 @@ Expected: FAIL — `formatX` not defined **Step 3: Write minimal implementation** -Add a new static method to `FastPlotToolbar.m` (in the `methods (Static)` block, after `makeIcon`): +Add a new static method to `FastSenseToolbar.m` (in the `methods (Static)` block, after `makeIcon`): ```matlab function str = formatX(xVal, xtype) @@ -277,10 +277,10 @@ With: ```matlab 'String', sprintf('x=%s y=%.4g', ... - FastPlotToolbar.formatX(xp, obj.getXType([])), yp)); + FastSenseToolbar.formatX(xp, obj.getXType([])), yp)); ``` -But we need the active FastPlot to get XType. Modify `onMouseMove` to also get `fp`: +But we need the active FastSense to get XType. Modify `onMouseMove` to also get `fp`: Change line 197 from: ```matlab @@ -295,7 +295,7 @@ And update both crosshair text lines to use: ```matlab 'String', sprintf('x=%s y=%.4g', ... - FastPlotToolbar.formatX(xp, obj.getXType(fp)), yp)); + FastSenseToolbar.formatX(xp, obj.getXType(fp)), yp)); ``` In `onMouseClick`, replace the cursor label (line 250): @@ -308,7 +308,7 @@ With: ```matlab label = sprintf('(%s, %.4g)', ... - FastPlotToolbar.formatX(sx, obj.getXType(fp)), sy); + FastSenseToolbar.formatX(sx, obj.getXType(fp)), sy); ``` **Step 4: Run test to verify it passes** @@ -319,7 +319,7 @@ Expected: PASS **Step 5: Commit** ```bash -git add FastPlotToolbar.m tests/test_datetime.m +git add FastSenseToolbar.m tests/test_datetime.m git commit -m "Add datetime formatting to toolbar crosshair and cursor" ``` @@ -340,12 +340,12 @@ Append to `examples/example_toolbar.m`: x = datenum(2024,1,1) + (0:99999)/86400; % ~1 day at 1-second resolution y = sin((1:100000) * 2*pi/3600) + 0.2*randn(1,100000); -fp3 = FastPlot('Theme', 'dark'); +fp3 = FastSense('Theme', 'dark'); fp3.addLine(x, y, 'DisplayName', 'Sensor', 'XType', 'datenum'); fp3.render(); title(fp3.hAxes, 'Datetime Axis — zoom to see format change'); -tb3 = FastPlotToolbar(fp3); +tb3 = FastSenseToolbar(fp3); fprintf('Datetime axis with toolbar ready. Zoom to see tick format adapt.\n'); ``` @@ -362,7 +362,7 @@ Pass `datenum` values as X data with `'XType', 'datenum'` to get auto-formatted x = datenum(2024,1,1) + (0:99999)/86400; % 1-second resolution y = sin((1:100000) * 2*pi/3600); -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'XType', 'datenum'); fp.render(); \``` diff --git a/docs/plans/2026-03-09-event-detection-design.md b/docs/plans/2026-03-09-event-detection-design.md index 7e06f592..c09f98b9 100644 --- a/docs/plans/2026-03-09-event-detection-design.md +++ b/docs/plans/2026-03-09-event-detection-design.md @@ -2,7 +2,7 @@ ## Overview -Third library (`libs/EventDetection/`) for FastPlot — detects events from threshold violations, groups consecutive violations into events with statistics, and supports configurable callbacks, debounce, UI viewer, console output, and live detection. +Third library (`libs/EventDetection/`) for FastSense — detects events from threshold violations, groups consecutive violations into events with statistics, and supports configurable callbacks, debounce, UI viewer, console output, and live detection. ## Structure @@ -102,7 +102,7 @@ Standalone MATLAB figure with two panels. - Columns: Start, End, Duration, Sensor, Threshold, Direction, Peak, NumPts, Min, Max, Mean, RMS, Std - Dropdown filter: sensor name - Dropdown filter: threshold label -- Clicking a row → opens FastPlot showing that sensor's signal zoomed to the event time range +- Clicking a row → opens FastSense showing that sensor's signal zoomed to the event time range ### Methods @@ -174,6 +174,6 @@ events = detectEventsFromSensor(sensor, detector) ## Integration -- No direct FastPlot dependency in core classes — `EventViewer` uses FastPlot for click-to-plot +- No direct FastSense dependency in core classes — `EventViewer` uses FastSense for click-to-plot - No direct SensorThreshold dependency in core — `detectEventsFromSensor` and `EventConfig` bridge the two libraries - Path setup: root `setup.m` updated to add `libs/EventDetection/` diff --git a/docs/plans/2026-03-09-event-detection-plan.md b/docs/plans/2026-03-09-event-detection-plan.md index 85a38b4a..40576b5b 100644 --- a/docs/plans/2026-03-09-event-detection-plan.md +++ b/docs/plans/2026-03-09-event-detection-plan.md @@ -79,7 +79,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event"` Expected: FAIL — `Event` class not found. **Step 3: Write minimal implementation** @@ -155,7 +155,7 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event"` Expected: PASS **Step 5: Commit** @@ -242,7 +242,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_group_violations"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_group_violations"` Expected: FAIL — `groupViolations` not found. **Step 3: Write minimal implementation** @@ -285,7 +285,7 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_group_violations"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_group_violations"` Expected: PASS **Step 5: Commit** @@ -406,7 +406,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_detector"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_detector"` Expected: FAIL — `EventDetector` class not found. **Step 3: Write minimal implementation** @@ -509,7 +509,7 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_detector"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_detector"` Expected: PASS **Step 5: Commit** @@ -587,7 +587,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_detect_events_from_sensor"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_detect_events_from_sensor"` Expected: FAIL — `detectEventsFromSensor` not found. **Step 3: Write minimal implementation** @@ -672,7 +672,7 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_detect_events_from_sensor"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_detect_events_from_sensor"` Expected: PASS **Step 5: Commit** @@ -753,7 +753,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_integration"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_integration"` Expected: Fails if setup.m hasn't been updated yet (EventDetection not on path). **Step 3: Update setup.m** @@ -762,25 +762,25 @@ In `setup.m`, add the EventDetection library path: ```matlab function setup() -%SETUP Add FastPlot, SensorThreshold, and EventDetection libraries to the MATLAB path. +%SETUP Add FastSense, SensorThreshold, and EventDetection libraries to the MATLAB path. % Run this once per session to make all library classes available. root = fileparts(mfilename('fullpath')); - addpath(fullfile(root, 'libs', 'FastPlot')); + addpath(fullfile(root, 'libs', 'FastSense')); addpath(fullfile(root, 'libs', 'SensorThreshold')); addpath(fullfile(root, 'libs', 'EventDetection')); - fprintf('FastPlot + SensorThreshold + EventDetection libraries added to path.\n'); + fprintf('FastSense + SensorThreshold + EventDetection libraries added to path.\n'); end ``` **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_integration"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_integration"` Expected: PASS **Step 5: Run full test suite** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); run_all_tests"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); run_all_tests"` Expected: All tests pass, including the 4 new test files. **Step 6: Commit** @@ -850,7 +850,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_console"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_console"` Expected: FAIL — `printEventSummary` not found. **Step 3: Write printEventSummary implementation** @@ -916,7 +916,7 @@ end **Step 5: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_console"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_console"` Expected: PASS **Step 6: Commit** @@ -1005,7 +1005,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_config"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_config"` Expected: FAIL — `EventConfig` class not found. **Step 3: Write minimal implementation** @@ -1106,7 +1106,7 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_config"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_config"` Expected: PASS **Step 5: Commit** @@ -1206,7 +1206,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_viewer"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_viewer"` Expected: FAIL — `EventViewer` class not found. **Step 3: Write implementation** @@ -1454,8 +1454,8 @@ classdef EventViewer < handle sd = obj.SensorData(sIdx); - % Open FastPlot for this sensor, zoomed to event - fp = FastPlot(); + % Open FastSense for this sensor, zoomed to event + fp = FastSense(); fp.addLine(sd.t, sd.y, 'Label', sd.name); % Add threshold line @@ -1477,7 +1477,7 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); test_event_viewer"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); test_event_viewer"` Expected: PASS **Step 5: Commit** @@ -1665,7 +1665,7 @@ end **Step 2: Verify example runs** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('examples'); example_event_detection_live"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('examples'); example_event_detection_live"` Expected: Console output shows initial detection + live logging. EventViewer figure opens with Gantt timeline and table. Timer appends data every 2 seconds. **Step 3: Commit** @@ -1681,7 +1681,7 @@ git commit -m "feat: add live event detection demo with industrial mock data" **Step 1: Run full test suite** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); run_all_tests"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); run_all_tests"` Expected: All tests pass, including the 6 new test files: - `test_event.m` - `test_group_violations.m` diff --git a/docs/plans/2026-03-09-sensor-threshold-optimization-design.md b/docs/plans/2026-03-09-sensor-threshold-optimization-design.md index 2e6a252f..fbf182ae 100644 --- a/docs/plans/2026-03-09-sensor-threshold-optimization-design.md +++ b/docs/plans/2026-03-09-sensor-threshold-optimization-design.md @@ -164,10 +164,10 @@ A dedicated benchmark script compares old `resolve()` vs new at multiple data sc |------|--------| | `libs/SensorThreshold/ThresholdRule.m` | `ConditionFn` -> `Condition` (struct), add `matchesState()` | | `libs/SensorThreshold/Sensor.m` | Rewrite `resolve()` with segment algorithm + condition batching | -| `libs/FastPlot/private/compute_violations_mex.c` | New: SIMD multi-threshold violation scan | -| `libs/FastPlot/private/compute_violations_vec.m` | New: vectorized pure-MATLAB fallback | -| `libs/FastPlot/private/compute_violations.m` | Keep for standalone FastPlot thresholds (no sensor) | -| `libs/FastPlot/build_mex.m` | Add `compute_violations_mex` target | +| `libs/FastSense/private/compute_violations_mex.c` | New: SIMD multi-threshold violation scan | +| `libs/FastSense/private/compute_violations_vec.m` | New: vectorized pure-MATLAB fallback | +| `libs/FastSense/private/compute_violations.m` | Keep for standalone FastSense thresholds (no sensor) | +| `libs/FastSense/build_mex.m` | Add `compute_violations_mex` target | | `examples/*.m` | Update to new struct condition API | | `examples/benchmark_resolve.m` | New: before/after benchmark comparison | diff --git a/docs/plans/2026-03-09-sensor-threshold-optimization.md b/docs/plans/2026-03-09-sensor-threshold-optimization.md index b527f5dc..6c9c620e 100644 --- a/docs/plans/2026-03-09-sensor-threshold-optimization.md +++ b/docs/plans/2026-03-09-sensor-threshold-optimization.md @@ -72,7 +72,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/test_declarative_condition.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/test_declarative_condition.m')"` Expected: FAIL — ThresholdRule constructor expects function_handle, not struct **Step 3: Implement ThresholdRule with declarative conditions** @@ -158,7 +158,7 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/test_declarative_condition.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/test_declarative_condition.m')"` Expected: PASS — All 6 tests **Step 5: Commit** @@ -235,7 +235,7 @@ Replace all `@(st) st.xxx == N` with `struct('xxx', N)` in `tests/test_sensor.m` **Step 5: Run tests** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/test_sensor.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/test_sensor.m')"` Expected: PASS — All 5 tests **Step 6: Commit** @@ -342,7 +342,7 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/test_resolve_segments.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/test_resolve_segments.m')"` Expected: FAIL — addThresholdRule still expects old API in resolve() **Step 3: Rewrite resolve() in Sensor.m** @@ -646,7 +646,7 @@ In `tests/test_sensor_resolve.m`, replace all function-handle conditions: **Step 6: Run all sensor tests** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/test_sensor_resolve.m'); run('tests/test_resolve_segments.m'); run('tests/test_declarative_condition.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/test_sensor_resolve.m'); run('tests/test_resolve_segments.m'); run('tests/test_declarative_condition.m')"` Expected: PASS — All tests **Step 7: Commit** @@ -665,8 +665,8 @@ git commit -m "feat: rewrite Sensor.resolve() with segment-based vectorized algo ### Task 4: SIMD MEX — compute_violations_mex.c **Files:** -- Create: `libs/FastPlot/private/mex_src/compute_violations_mex.c` -- Modify: `libs/FastPlot/build_mex.m:61-65` (add to mex_files list) +- Create: `libs/FastSense/private/mex_src/compute_violations_mex.c` +- Modify: `libs/FastSense/build_mex.m:61-65` (add to mex_files list) - Create: `tests/test_violations_mex_parity.m` **Step 1: Write MEX parity test** @@ -780,7 +780,7 @@ end **Step 2: Write compute_violations_mex.c** -Create `libs/FastPlot/private/mex_src/compute_violations_mex.c`: +Create `libs/FastSense/private/mex_src/compute_violations_mex.c`: ```c /* @@ -874,7 +874,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if (nrhs != 5) { - mexErrMsgIdAndTxt("FastPlot:compute_violations_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:compute_violations_mex:nrhs", "Five inputs required: sensorY, segLo, segHi, thresholdValues, directions."); } @@ -942,7 +942,7 @@ void mexFunction(int nlhs, mxArray *plhs[], **Step 3: Add to build_mex.m** -In `libs/FastPlot/build_mex.m`, add to the `mex_files` cell array (line 61-65): +In `libs/FastSense/build_mex.m`, add to the `mex_files` cell array (line 61-65): ```matlab mex_files = { @@ -955,19 +955,19 @@ mex_files = { **Step 4: Compile MEX** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('setup.m'); build_mex()"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('setup.m'); build_mex()"` Expected: All 4 MEX files compile, including compute_violations_mex **Step 5: Run parity test** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/test_violations_mex_parity.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/test_violations_mex_parity.m')"` Expected: PASS — All 5 parity tests **Step 6: Commit** ```bash -git add libs/FastPlot/private/mex_src/compute_violations_mex.c \ - libs/FastPlot/build_mex.m tests/test_violations_mex_parity.m +git add libs/FastSense/private/mex_src/compute_violations_mex.c \ + libs/FastSense/build_mex.m tests/test_violations_mex_parity.m git commit -m "feat: add SIMD MEX for batch threshold violation detection" ``` @@ -1046,7 +1046,7 @@ end **Step 2: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/run_all_tests.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/run_all_tests.m')"` Expected: All 31+ tests pass **Step 3: Commit** @@ -1157,7 +1157,7 @@ end **Step 2: Run benchmark** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('examples/benchmark_resolve.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('examples/benchmark_resolve.m')"` Expected: Sub-second times for all data sizes (vs multi-second for old implementation) **Step 3: Commit** @@ -1175,17 +1175,17 @@ git commit -m "feat: add before/after benchmark for Sensor.resolve() optimizatio **Step 1: Compile MEX** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('setup.m'); build_mex()"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('setup.m'); build_mex()"` Expected: All 4 MEX files compiled **Step 2: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('tests/run_all_tests.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('tests/run_all_tests.m')"` Expected: All tests pass (31 original + 3 new = 34 tests) **Step 3: Run benchmark** -Run: `cd /Users/hannessuhr/FastPlot && octave --eval "run('examples/benchmark_resolve.m')"` +Run: `cd /Users/hannessuhr/FastSense && octave --eval "run('examples/benchmark_resolve.m')"` Expected: Performance table printed with sub-second resolve times **Step 4: Final commit (if any fixups needed)** diff --git a/docs/plans/2026-03-09-unified-threshold-api-design.md b/docs/plans/2026-03-09-unified-threshold-api-design.md index 2d2ad3d7..3583ac26 100644 --- a/docs/plans/2026-03-09-unified-threshold-api-design.md +++ b/docs/plans/2026-03-09-unified-threshold-api-design.md @@ -2,9 +2,9 @@ **Goal:** Eliminate the dual rendering path for threshold violations by extending `addThreshold` to accept time-varying thresholds (X/Y arrays), then routing `addSensor` through it. -**Architecture:** Sensor/resolve handles domain logic (which threshold value at which time). FastPlot handles rendering logic (what to show on screen now). Pre-computed violations are no longer fed to the renderer. +**Architecture:** Sensor/resolve handles domain logic (which threshold value at which time). FastSense handles rendering logic (what to show on screen now). Pre-computed violations are no longer fed to the renderer. -**Tech Stack:** MATLAB/Octave, existing FastPlot private function pattern. +**Tech Stack:** MATLAB/Octave, existing FastSense private function pattern. --- diff --git a/docs/plans/2026-03-09-unified-threshold-api.md b/docs/plans/2026-03-09-unified-threshold-api.md index d9bff40f..963cbe15 100644 --- a/docs/plans/2026-03-09-unified-threshold-api.md +++ b/docs/plans/2026-03-09-unified-threshold-api.md @@ -6,15 +6,15 @@ **Architecture:** Extend `addThreshold` to accept X/Y arrays (time-varying step functions) in addition to scalar values. Add `compute_violations_dynamic` for comparing data against interpolated thresholds. Rewrite `addSensor` to use `addThreshold` exclusively. Remove `addThresholdConnectors` (step transitions become native). -**Tech Stack:** MATLAB/Octave, existing FastPlot private function pattern, `interp1(..., 'previous')` for step-function lookup. +**Tech Stack:** MATLAB/Octave, existing FastSense private function pattern, `interp1(..., 'previous')` for step-function lookup. --- ### Task 1: Extend Thresholds struct and `addThreshold` to accept X/Y arrays **Files:** -- Modify: `libs/FastPlot/FastPlot.m:90-93` (Thresholds struct definition) -- Modify: `libs/FastPlot/FastPlot.m:395-425` (addThreshold method) +- Modify: `libs/FastSense/FastSense.m:90-93` (Thresholds struct definition) +- Modify: `libs/FastSense/FastSense.m:395-425` (addThreshold method) - Test: `tests/test_add_threshold.m` **Step 1: Write the failing test** @@ -23,7 +23,7 @@ Add to `tests/test_add_threshold.m` before the final fprintf: ```matlab % testTimeVaryingThreshold - fp = FastPlot(); + fp = FastSense(); thX = [0 10 20 30]; thY = [5.0 5.0 7.0 7.0]; fp.addThreshold(thX, thY, 'Direction', 'upper', 'ShowViolations', true, 'Label', 'StepTh'); @@ -35,7 +35,7 @@ Add to `tests/test_add_threshold.m` before the final fprintf: assert(fp.Thresholds(1).ShowViolations == true, 'testTimeVarying: ShowViolations'); % testMixedThresholds — scalar and time-varying coexist - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(4.5); fp.addThreshold([0 10], [3.0 5.0], 'Direction', 'lower'); assert(numel(fp.Thresholds) == 2, 'testMixed: count'); @@ -52,7 +52,7 @@ Expected: FAIL — addThreshold doesn't accept 3+ positional args. **Step 3: Implement the changes** -In `libs/FastPlot/FastPlot.m`, modify the Thresholds struct definition (line 90-93) to add X and Y fields: +In `libs/FastSense/FastSense.m`, modify the Thresholds struct definition (line 90-93) to add X and Y fields: ```matlab Thresholds = struct('Value', {}, 'X', {}, 'Y', {}, ... @@ -72,7 +72,7 @@ Rewrite `addThreshold` (line 395-425) to detect scalar vs X/Y: % fp.addThreshold(thX, thY, 'Direction', 'upper', 'ShowViolations', true) if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add thresholds after render() has been called.'); end @@ -141,7 +141,7 @@ feat: extend addThreshold to accept time-varying X/Y arrays ### Task 2: Create `compute_violations_dynamic` for time-varying thresholds **Files:** -- Create: `libs/FastPlot/private/compute_violations_dynamic.m` +- Create: `libs/FastSense/private/compute_violations_dynamic.m` - Create: `tests/test_compute_violations_dynamic.m` **Step 1: Write the failing test** @@ -153,7 +153,7 @@ function test_compute_violations_dynamic() %TEST_COMPUTE_VIOLATIONS_DYNAMIC Tests for time-varying threshold violations. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testUpperStepFunction: threshold steps from 5 to 8 at x=10 thX = [0 10 20]; @@ -235,7 +235,7 @@ Expected: FAIL — `compute_violations_dynamic` not found. **Step 3: Implement `compute_violations_dynamic.m`** -Create `libs/FastPlot/private/compute_violations_dynamic.m`: +Create `libs/FastSense/private/compute_violations_dynamic.m`: ```matlab function [xViol, yViol] = compute_violations_dynamic(x, y, thX, thY, direction) @@ -252,7 +252,7 @@ function [xViol, yViol] = compute_violations_dynamic(x, y, thX, thY, direction) % direction — 'upper' (violation when y > threshold) % 'lower' (violation when y < threshold) % -% See also compute_violations, FastPlot.addThreshold. +% See also compute_violations, FastSense.addThreshold. if isempty(x) xViol = zeros(1, 0); @@ -296,7 +296,7 @@ feat: add compute_violations_dynamic for time-varying thresholds ### Task 3: Update threshold rendering for time-varying thresholds **Files:** -- Modify: `libs/FastPlot/FastPlot.m:770-834` (render method — threshold section) +- Modify: `libs/FastSense/FastSense.m:770-834` (render method — threshold section) **Step 1: Modify threshold line rendering** @@ -324,7 +324,7 @@ Replace the threshold line creation block (lines 774-785) with logic that handle 'LineWidth', 1.5, ... 'HandleVisibility', 'off'); end - udT.FastPlot = struct( ... + udT.FastSense = struct( ... 'Type', 'threshold', ... 'Name', T.Label, ... 'LineIndex', [], ... @@ -391,7 +391,7 @@ feat: render time-varying thresholds and their violation markers ### Task 4: Update `updateViolations` for time-varying thresholds **Files:** -- Modify: `libs/FastPlot/FastPlot.m:1977-2015` (updateViolations method) +- Modify: `libs/FastSense/FastSense.m:1977-2015` (updateViolations method) **Step 1: Modify updateViolations inner loop** @@ -450,7 +450,7 @@ feat: updateViolations supports time-varying thresholds ### Task 5: Rewrite `addSensor` to use unified `addThreshold` path **Files:** -- Modify: `libs/FastPlot/FastPlot.m:344-393` (addSensor method) +- Modify: `libs/FastSense/FastSense.m:344-393` (addSensor method) - Test: `tests/test_add_sensor.m` **Step 1: Update test expectations** @@ -474,7 +474,7 @@ Update it to check Thresholds instead of Lines: s.addThresholdRule(struct('machine', 1), 10, 'Direction', 'upper', 'Label', 'HH'); s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); assert(numel(fp.Lines) == 1, 'testWithThresholds: only data line'); assert(numel(fp.Thresholds) >= 1, 'testWithThresholds: threshold(s) added'); @@ -485,7 +485,7 @@ Update it to check Thresholds instead of Lines: Also update `testAddSensorNoThresholds`: ```matlab - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s, 'ShowThresholds', false); assert(numel(fp.Lines) == 1, 'testNoThresholds: only data line'); assert(numel(fp.Thresholds) == 0, 'testNoThresholds: no thresholds'); @@ -543,7 +543,7 @@ feat: addSensor uses unified addThreshold path for time-varying thresholds ### Task 6: Clean up dead code **Files:** -- Modify: `libs/FastPlot/FastPlot.m:1438-1477` (remove addThresholdConnectors) +- Modify: `libs/FastSense/FastSense.m:1438-1477` (remove addThresholdConnectors) **Step 1: Remove `addThresholdConnectors` method** diff --git a/docs/plans/2026-03-09-violation-cull-mex-design.md b/docs/plans/2026-03-09-violation-cull-mex-design.md index be445e1d..21fed4f9 100644 --- a/docs/plans/2026-03-09-violation-cull-mex-design.md +++ b/docs/plans/2026-03-09-violation-cull-mex-design.md @@ -45,7 +45,7 @@ Single-pass over data array: ## MATLAB Wrapper -`compute_violations.m` and `compute_violations_dynamic.m` gain MEX fast-paths. The render/updateViolations loops in `FastPlot.m` are updated to call a fused path that skips `downsample_violations` when MEX is available. +`compute_violations.m` and `compute_violations_dynamic.m` gain MEX fast-paths. The render/updateViolations loops in `FastSense.m` are updated to call a fused path that skips `downsample_violations` when MEX is available. ## Files Changed @@ -55,7 +55,7 @@ Single-pass over data array: - **Modify:** `compute_violations.m` — add MEX fast-path - **Modify:** `compute_violations_dynamic.m` — add MEX fast-path - **Modify:** `downsample_violations.m` — no change (still used as MATLAB fallback) -- **Modify:** `FastPlot.m` render + updateViolations — fused call path +- **Modify:** `FastSense.m` render + updateViolations — fused call path - **Create:** `tests/test_violation_cull_mex.m` — parity tests against MATLAB fallback ## Compatibility diff --git a/docs/plans/2026-03-09-violation-cull-mex.md b/docs/plans/2026-03-09-violation-cull-mex.md index dc2bdb7c..cbc2a9f8 100644 --- a/docs/plans/2026-03-09-violation-cull-mex.md +++ b/docs/plans/2026-03-09-violation-cull-mex.md @@ -4,7 +4,7 @@ **Goal:** Replace the separate compute_violations + downsample_violations pipeline with a single SIMD-accelerated MEX function that fuses step-function threshold lookup, violation detection, and pixel-density culling into one pass. -**Architecture:** A new `violation_cull_mex.c` replaces the unused `compute_violations_mex.c`. It accepts data arrays, threshold knots, direction, and pixel parameters, returning culled violation points directly. The MATLAB wrappers (`compute_violations.m`, `compute_violations_dynamic.m`) gain MEX fast-paths, and `FastPlot.m` render/updateViolations call a fused path skipping `downsample_violations` when MEX is available. +**Architecture:** A new `violation_cull_mex.c` replaces the unused `compute_violations_mex.c`. It accepts data arrays, threshold knots, direction, and pixel parameters, returning culled violation points directly. The MATLAB wrappers (`compute_violations.m`, `compute_violations_dynamic.m`) gain MEX fast-paths, and `FastSense.m` render/updateViolations call a fused path skipping `downsample_violations` when MEX is available. **Tech Stack:** C with `simd_utils.h` (AVX2/SSE2/NEON/scalar), MATLAB MEX API, Octave mkoctfile. @@ -13,18 +13,18 @@ ### Task 1: Delete unused `compute_violations_mex` and clean up `build_mex.m` **Files:** -- Delete: `libs/FastPlot/private/mex_src/compute_violations_mex.c` -- Delete: `libs/FastPlot/private/compute_violations_mex.mex` -- Delete: `libs/FastPlot/private/compute_violations_mex.mexmaca64` -- Modify: `libs/FastPlot/build_mex.m:84-90` (mex_files list) -- Modify: `libs/FastPlot/build_mex.m:142-144` (copy_mex_to call) +- Delete: `libs/FastSense/private/mex_src/compute_violations_mex.c` +- Delete: `libs/FastSense/private/compute_violations_mex.mex` +- Delete: `libs/FastSense/private/compute_violations_mex.mexmaca64` +- Modify: `libs/FastSense/build_mex.m:84-90` (mex_files list) +- Modify: `libs/FastSense/build_mex.m:142-144` (copy_mex_to call) **Step 1: Delete the old MEX source and compiled binaries** ```bash -rm libs/FastPlot/private/mex_src/compute_violations_mex.c -rm -f libs/FastPlot/private/compute_violations_mex.mex -rm -f libs/FastPlot/private/compute_violations_mex.mexmaca64 +rm libs/FastSense/private/mex_src/compute_violations_mex.c +rm -f libs/FastSense/private/compute_violations_mex.mex +rm -f libs/FastSense/private/compute_violations_mex.mexmaca64 rm -f libs/SensorThreshold/private/compute_violations_mex.mex rm -f libs/SensorThreshold/private/compute_violations_mex.mexmaca64 ``` @@ -73,11 +73,11 @@ refactor: remove unused compute_violations_mex, prepare for violation_cull_mex ### Task 2: Create `violation_cull_mex.c` **Files:** -- Create: `libs/FastPlot/private/mex_src/violation_cull_mex.c` +- Create: `libs/FastSense/private/mex_src/violation_cull_mex.c` **Step 1: Write the MEX C source** -Create `libs/FastPlot/private/mex_src/violation_cull_mex.c`: +Create `libs/FastSense/private/mex_src/violation_cull_mex.c`: ```c /* @@ -131,7 +131,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if (nrhs != 7) { - mexErrMsgIdAndTxt("FastPlot:violation_cull_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:violation_cull_mex:nrhs", "Seven inputs required: x, y, thX, thY, direction, pixelWidth, xmin."); } @@ -317,7 +317,7 @@ void mexFunction(int nlhs, mxArray *plhs[], **Step 2: Compile** -Run: `octave --eval "cd libs/FastPlot; build_mex"` (or in MATLAB: `cd libs/FastPlot; build_mex`) +Run: `octave --eval "cd libs/FastSense; build_mex"` (or in MATLAB: `cd libs/FastSense; build_mex`) Expected: `violation_cull_mex.c ... OK` @@ -343,7 +343,7 @@ function test_violation_cull_mex() %TEST_VIOLATION_CULL_MEX Parity tests: MEX vs MATLAB fallback. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); hasMex = (exist('violation_cull_mex', 'file') == 3); if ~hasMex @@ -471,7 +471,7 @@ end **Step 2: Run test** -Run: `octave --eval "cd libs/FastPlot; build_mex" && octave --eval "cd tests; addpath('..'); setup; test_violation_cull_mex"` +Run: `octave --eval "cd libs/FastSense; build_mex" && octave --eval "cd tests; addpath('..'); setup; test_violation_cull_mex"` Expected: All 7 parity tests pass. @@ -486,8 +486,8 @@ test: add violation_cull_mex parity tests against MATLAB reference ### Task 4: Wire MEX into MATLAB wrappers **Files:** -- Modify: `libs/FastPlot/private/compute_violations.m` -- Modify: `libs/FastPlot/private/compute_violations_dynamic.m` +- Modify: `libs/FastSense/private/compute_violations.m` +- Modify: `libs/FastSense/private/compute_violations_dynamic.m` **Step 1: Add MEX fast-path to `compute_violations.m`** @@ -500,7 +500,7 @@ function [xViol, yViol] = compute_violations(x, y, thresholdValue, direction) % % Uses violation_cull_mex if compiled (without pixel culling — pw=0 skips it). % -% See also compute_violations_dynamic, FastPlot.addThreshold. +% See also compute_violations_dynamic, FastSense.addThreshold. if strcmp(direction, 'upper') mask = y > thresholdValue; @@ -513,15 +513,15 @@ function [xViol, yViol] = compute_violations(x, y, thresholdValue, direction) end ``` -Note: we keep this function simple — the MEX fast-path is wired at the FastPlot.m level in Task 5 where we have pixel parameters available. This function remains the pure "find violations" step for callers that don't need culling. +Note: we keep this function simple — the MEX fast-path is wired at the FastSense.m level in Task 5 where we have pixel parameters available. This function remains the pure "find violations" step for callers that don't need culling. **Step 2: Same for `compute_violations_dynamic.m`** -No changes needed — keep as-is. The MEX fast-path is at the FastPlot.m level. +No changes needed — keep as-is. The MEX fast-path is at the FastSense.m level. **Step 3: Create a new helper `violation_cull.m`** -Create `libs/FastPlot/private/violation_cull.m` — the MATLAB-side entry point that tries MEX, falls back to MATLAB: +Create `libs/FastSense/private/violation_cull.m` — the MATLAB-side entry point that tries MEX, falls back to MATLAB: ```matlab function [xOut, yOut] = violation_cull(x, y, thX, thY, direction, pixelWidth, xmin) @@ -580,11 +580,11 @@ feat: add violation_cull wrapper with MEX fast-path --- -### Task 5: Wire fused path into FastPlot.m render and updateViolations +### Task 5: Wire fused path into FastSense.m render and updateViolations **Files:** -- Modify: `libs/FastPlot/FastPlot.m:827-865` (render violation section) -- Modify: `libs/FastPlot/FastPlot.m:2003-2051` (updateViolations) +- Modify: `libs/FastSense/FastSense.m:827-865` (render violation section) +- Modify: `libs/FastSense/FastSense.m:2003-2051` (updateViolations) **Step 1: Update render violation loop** diff --git a/docs/plans/2026-03-09-violation-marker-optimization.md b/docs/plans/2026-03-09-violation-marker-optimization.md index d759e7c1..8f8961ec 100644 --- a/docs/plans/2026-03-09-violation-marker-optimization.md +++ b/docs/plans/2026-03-09-violation-marker-optimization.md @@ -4,20 +4,20 @@ **Goal:** Optimize violation marker rendering with faster marker style, pixel-density downsampling, and dirty-flag caching to skip redundant recomputation. -**Architecture:** Three independent optimizations applied to `FastPlot.m` and its private functions: (1) swap `'o'` → `'.'` marker, (2) cull violations to 1-per-pixel-column via new `downsample_violations.m`, (3) cache `[xlim ylim]` to skip `updateViolations()` when view unchanged. +**Architecture:** Three independent optimizations applied to `FastSense.m` and its private functions: (1) swap `'o'` → `'.'` marker, (2) cull violations to 1-per-pixel-column via new `downsample_violations.m`, (3) cache `[xlim ylim]` to skip `updateViolations()` when view unchanged. -**Tech Stack:** MATLAB/Octave, existing FastPlot private function pattern. +**Tech Stack:** MATLAB/Octave, existing FastSense private function pattern. --- ### Task 1: Change marker style from `'o'` to `'.'` **Files:** -- Modify: `libs/FastPlot/FastPlot.m:813` (render method) +- Modify: `libs/FastSense/FastSense.m:813` (render method) **Step 1: Change marker in render()** -In `libs/FastPlot/FastPlot.m:811-813`, change the `line()` call marker from `'o'` to `'.'`: +In `libs/FastSense/FastSense.m:811-813`, change the `line()` call marker from `'o'` to `'.'`: ```matlab hM = line(vxAll, vyAll, 'Parent', obj.hAxes, ... @@ -30,7 +30,7 @@ Note: `MarkerSize` bumped from 4 to 6 because `'.'` renders smaller than `'o'` a **Step 2: Run existing tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup; test_compute_violations"` (or Octave equivalent) +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup; test_compute_violations"` (or Octave equivalent) Expected: All 5 tests pass (marker style is a rendering detail, not tested by compute_violations). **Step 3: Commit** @@ -44,10 +44,10 @@ feat: use '.' marker for violation rendering (faster than 'o') ### Task 2: Pixel-density downsampling of violation markers **Files:** -- Create: `libs/FastPlot/private/downsample_violations.m` +- Create: `libs/FastSense/private/downsample_violations.m` - Create: `tests/test_downsample_violations.m` -- Modify: `libs/FastPlot/FastPlot.m:785-810` (render — apply downsampling after compute_violations) -- Modify: `libs/FastPlot/FastPlot.m:1953-2000` (updateViolations — apply downsampling after compute_violations) +- Modify: `libs/FastSense/FastSense.m:785-810` (render — apply downsampling after compute_violations) +- Modify: `libs/FastSense/FastSense.m:1953-2000` (updateViolations — apply downsampling after compute_violations) **Step 1: Write the test** @@ -58,7 +58,7 @@ function test_downsample_violations() %TEST_DOWNSAMPLE_VIOLATIONS Tests for violation marker pixel-density culling. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testBasicDownsample: 10 violations in 3 pixel columns -> 3 points xV = [1.0, 1.1, 1.2, 2.0, 2.1, 2.2, 3.0, 3.1, 3.2, 3.3]; @@ -119,12 +119,12 @@ end **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup; test_downsample_violations"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup; test_downsample_violations"` Expected: FAIL — `downsample_violations` not found. **Step 3: Implement `downsample_violations.m`** -Create `libs/FastPlot/private/downsample_violations.m`: +Create `libs/FastSense/private/downsample_violations.m`: ```matlab function [xOut, yOut] = downsample_violations(xViol, yViol, pixelWidth, thresholdValue) @@ -137,7 +137,7 @@ function [xOut, yOut] = downsample_violations(xViol, yViol, pixelWidth, threshol % pixelWidth — X-axis span per pixel (diff(xlim) / axesWidthPixels) % thresholdValue — threshold value (used to pick max-deviation point) % -% See also compute_violations, FastPlot.updateViolations. +% See also compute_violations, FastSense.updateViolations. if isempty(xViol) xOut = xViol; @@ -180,12 +180,12 @@ end **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup; test_downsample_violations"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup; test_downsample_violations"` Expected: All 6 tests pass. **Step 5: Integrate into `render()` and `updateViolations()`** -In `libs/FastPlot/FastPlot.m`, the `render()` method at lines 785-810 — after computing violations for each line, downsample before rendering. The key change is adding a downsample call after the violation collection loop, before the `line()` call. +In `libs/FastSense/FastSense.m`, the `render()` method at lines 785-810 — after computing violations for each line, downsample before rendering. The key change is adding a downsample call after the violation collection loop, before the `line()` call. In the render section (after line 806, before line 811), add: @@ -209,7 +209,7 @@ And update the corresponding `set()` call to use `vxAll, vyAll` directly (no mor **Step 6: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup; test_compute_violations; test_downsample_violations"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup; test_compute_violations; test_downsample_violations"` Expected: All tests pass. **Step 7: Commit** @@ -223,13 +223,13 @@ feat: downsample violation markers to pixel density ### Task 3: Dirty-flag caching to skip redundant `updateViolations()` **Files:** -- Modify: `libs/FastPlot/FastPlot.m:113-124` (add `CachedViolationLim` property) -- Modify: `libs/FastPlot/FastPlot.m:1953-2000` (check cache at top of `updateViolations()`) -- Modify: `libs/FastPlot/FastPlot.m:892` (invalidate on render) +- Modify: `libs/FastSense/FastSense.m:113-124` (add `CachedViolationLim` property) +- Modify: `libs/FastSense/FastSense.m:1953-2000` (check cache at top of `updateViolations()`) +- Modify: `libs/FastSense/FastSense.m:892` (invalidate on render) **Step 1: Add cached property** -In `libs/FastPlot/FastPlot.m`, properties block at line 117, add after `CachedXLim`: +In `libs/FastSense/FastSense.m`, properties block at line 117, add after `CachedXLim`: ```matlab CachedViolationLim = [] % [xmin xmax ymin ymax pw] for violation skip @@ -268,7 +268,7 @@ before the `obj.updateViolations()` call. This ensures data updates always trigg **Step 4: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup; test_compute_violations; test_downsample_violations"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup; test_compute_violations; test_downsample_violations"` Expected: All tests pass. **Step 5: Commit** diff --git a/docs/plans/2026-03-09-violations-toggle-button.md b/docs/plans/2026-03-09-violations-toggle-button.md index 68237881..44aa0016 100644 --- a/docs/plans/2026-03-09-violations-toggle-button.md +++ b/docs/plans/2026-03-09-violations-toggle-button.md @@ -4,22 +4,22 @@ **Goal:** Add a toolbar toggle button that globally shows/hides all violation markers without changing per-threshold `ShowViolations` settings. -**Architecture:** A `ViolationsVisible` property on `FastPlot` controls marker visibility. The toolbar gets a new `uitoggletool` with a `'violations'` icon. Toggling it calls `setViolationsVisible()` on each FastPlot instance, which sets `Visible` on all `hMarkers` handles and gates marker computation in `render()` and `updateViolations()`. +**Architecture:** A `ViolationsVisible` property on `FastSense` controls marker visibility. The toolbar gets a new `uitoggletool` with a `'violations'` icon. Toggling it calls `setViolationsVisible()` on each FastSense instance, which sets `Visible` on all `hMarkers` handles and gates marker computation in `render()` and `updateViolations()`. **Tech Stack:** MATLAB (uitoggletool, graphics handle Visible property) --- -### Task 1: Add `ViolationsVisible` property and `setViolationsVisible` method to FastPlot +### Task 1: Add `ViolationsVisible` property and `setViolationsVisible` method to FastSense **Files:** -- Modify: `libs/FastPlot/FastPlot.m:64-81` (public properties) -- Modify: `libs/FastPlot/FastPlot.m:828` (render gate) -- Modify: `libs/FastPlot/FastPlot.m:1995` (updateViolations gate) +- Modify: `libs/FastSense/FastSense.m:64-81` (public properties) +- Modify: `libs/FastSense/FastSense.m:828` (render gate) +- Modify: `libs/FastSense/FastSense.m:1995` (updateViolations gate) **Step 1: Add the `ViolationsVisible` property** -In `libs/FastPlot/FastPlot.m`, in the public properties block (after line 80, `YScale`), add: +In `libs/FastSense/FastSense.m`, in the public properties block (after line 80, `YScale`), add: ```matlab ViolationsVisible = true % global toggle for violation markers @@ -27,7 +27,7 @@ In `libs/FastPlot/FastPlot.m`, in the public properties block (after line 80, `Y **Step 2: Add the `setViolationsVisible` public method** -In `libs/FastPlot/FastPlot.m`, add a new public method (in the public methods section, near other setter-style methods). Place it after the existing public methods like `addThreshold`, etc.: +In `libs/FastSense/FastSense.m`, add a new public method (in the public methods section, near other setter-style methods). Place it after the existing public methods like `addThreshold`, etc.: ```matlab function setViolationsVisible(obj, on) @@ -50,7 +50,7 @@ In `libs/FastPlot/FastPlot.m`, add a new public method (in the public methods se **Step 3: Gate violation marker creation in `render()`** -In `libs/FastPlot/FastPlot.m:828`, change: +In `libs/FastSense/FastSense.m:828`, change: ```matlab if T.ShowViolations @@ -64,7 +64,7 @@ to: **Step 4: Gate violation marker update in `updateViolations()`** -In `libs/FastPlot/FastPlot.m:1984`, add an early return after the existing `ishandle` check. Before the existing dirty-flag block, insert: +In `libs/FastSense/FastSense.m:1984`, add an early return after the existing `ishandle` check. Before the existing dirty-flag block, insert: ```matlab if ~obj.ViolationsVisible @@ -83,28 +83,28 @@ So lines 1984+ become: **Step 5: Run existing tests to verify no regression** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run('tests/test_toolbar.m')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run('tests/test_toolbar.m')"` Expected: All 13 toolbar tests pass. **Step 6: Commit** ```bash -git add libs/FastPlot/FastPlot.m -git commit -m "feat: add ViolationsVisible property and setViolationsVisible method to FastPlot" +git add libs/FastSense/FastSense.m +git commit -m "feat: add ViolationsVisible property and setViolationsVisible method to FastSense" ``` --- -### Task 2: Add violations toggle button to FastPlotToolbar +### Task 2: Add violations toggle button to FastSenseToolbar **Files:** -- Modify: `libs/FastPlot/FastPlotToolbar.m:48` (add handle property) -- Modify: `libs/FastPlot/FastPlotToolbar.m:367-371` (createToolbar — insert before theme btn) -- Modify: `libs/FastPlot/FastPlotToolbar.m:240-246` (rebind — sync state) +- Modify: `libs/FastSense/FastSenseToolbar.m:48` (add handle property) +- Modify: `libs/FastSense/FastSenseToolbar.m:367-371` (createToolbar — insert before theme btn) +- Modify: `libs/FastSense/FastSenseToolbar.m:240-246` (rebind — sync state) **Step 1: Add the `hViolationsBtn` handle property** -In `libs/FastPlot/FastPlotToolbar.m`, in the private properties block, after line 48 (`hThemeBtn`), add: +In `libs/FastSense/FastSenseToolbar.m`, in the private properties block, after line 48 (`hThemeBtn`), add: ```matlab hViolationsBtn = [] % uitoggletool handle for violations toggle @@ -112,11 +112,11 @@ In `libs/FastPlot/FastPlotToolbar.m`, in the private properties block, after lin **Step 2: Create the toggle button in `createToolbar()`** -In `libs/FastPlot/FastPlotToolbar.m`, insert after the metadata button block (after line 366) and before the theme button (line 368): +In `libs/FastSense/FastSenseToolbar.m`, insert after the metadata button block (after line 366) and before the theme button (line 368): ```matlab obj.hViolationsBtn = uitoggletool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('violations'), ... + 'CData', FastSenseToolbar.makeIcon('violations'), ... 'TooltipString', 'Toggle Violations', ... 'State', 'on', ... 'OnCallback', @(s,e) obj.onViolationsOn(), ... @@ -125,30 +125,30 @@ In `libs/FastPlot/FastPlotToolbar.m`, insert after the metadata button block (af **Step 3: Add the On/Off callback methods** -In `libs/FastPlot/FastPlotToolbar.m`, in the private methods section (after the `onMetadataOff` method around line 392), add: +In `libs/FastSense/FastSenseToolbar.m`, in the private methods section (after the `onMetadataOff` method around line 392), add: ```matlab function onViolationsOn(obj) - for i = 1:numel(obj.FastPlots) - obj.FastPlots{i}.setViolationsVisible(true); + for i = 1:numel(obj.FastSenses) + obj.FastSenses{i}.setViolationsVisible(true); end end function onViolationsOff(obj) - for i = 1:numel(obj.FastPlots) - obj.FastPlots{i}.setViolationsVisible(false); + for i = 1:numel(obj.FastSenses) + obj.FastSenses{i}.setViolationsVisible(false); end end ``` **Step 4: Sync state in `rebind()`** -In `libs/FastPlot/FastPlotToolbar.m`, after line 246 (`setappdata(obj.hFigure, 'FastPlotMetadataEnabled', obj.MetadataEnabled);`), add: +In `libs/FastSense/FastSenseToolbar.m`, after line 246 (`setappdata(obj.hFigure, 'FastSenseMetadataEnabled', obj.MetadataEnabled);`), add: ```matlab - % Sync violations toggle to first FastPlot's state - if ~isempty(obj.FastPlots) - if obj.FastPlots{1}.ViolationsVisible + % Sync violations toggle to first FastSense's state + if ~isempty(obj.FastSenses) + if obj.FastSenses{1}.ViolationsVisible set(obj.hViolationsBtn, 'State', 'on'); else set(obj.hViolationsBtn, 'State', 'off'); @@ -159,7 +159,7 @@ In `libs/FastPlot/FastPlotToolbar.m`, after line 246 (`setappdata(obj.hFigure, ' **Step 5: Commit** ```bash -git add libs/FastPlot/FastPlotToolbar.m +git add libs/FastSense/FastSenseToolbar.m git commit -m "feat: add violations toggle button to toolbar" ``` @@ -168,13 +168,13 @@ git commit -m "feat: add violations toggle button to toolbar" ### Task 3: Add violations icon to `makeIcon()` **Files:** -- Modify: `libs/FastPlot/FastPlotToolbar.m:1027` (add case before `end` of switch) -- Modify: `libs/FastPlot/FastPlotToolbar.m:1033-1034` (add to initIcons list) -- Modify: `libs/FastPlot/FastPlotToolbar.m:880-881` (update docstring) +- Modify: `libs/FastSense/FastSenseToolbar.m:1027` (add case before `end` of switch) +- Modify: `libs/FastSense/FastSenseToolbar.m:1033-1034` (add to initIcons list) +- Modify: `libs/FastSense/FastSenseToolbar.m:880-881` (update docstring) **Step 1: Add the `'violations'` icon case** -In `libs/FastPlot/FastPlotToolbar.m`, before the `end` of the switch block (line 1027), add a new case. The icon is a small exclamation mark in a triangle (warning style), drawn in orange/red: +In `libs/FastSense/FastSenseToolbar.m`, before the `end` of the switch block (line 1027), add a new case. The icon is a small exclamation mark in a triangle (warning style), drawn in orange/red: ```matlab case 'violations' @@ -200,7 +200,7 @@ In `libs/FastPlot/FastPlotToolbar.m`, before the `end` of the switch block (line **Step 2: Add `'violations'` to initIcons list** -In `libs/FastPlot/FastPlotToolbar.m:1033-1034`, change: +In `libs/FastSense/FastSenseToolbar.m:1033-1034`, change: ```matlab names = {'cursor', 'crosshair', 'grid', 'legend', 'autoscale', ... @@ -216,7 +216,7 @@ to: **Step 3: Update makeIcon docstring** -In `libs/FastPlot/FastPlotToolbar.m:880-881`, change: +In `libs/FastSense/FastSenseToolbar.m:880-881`, change: ```matlab % Available names: 'cursor', 'crosshair', 'grid', 'legend', @@ -234,7 +234,7 @@ to: **Step 4: Commit** ```bash -git add libs/FastPlot/FastPlotToolbar.m +git add libs/FastSense/FastSenseToolbar.m git commit -m "feat: add violations icon to makeIcon()" ``` @@ -281,11 +281,11 @@ In `tests/test_toolbar.m`, before the final `fprintf` (line 149), add: ```matlab % testViolationsToggle - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100) * 10); fp.addThreshold(5, 'Direction', 'upper', 'ShowViolations', true); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); % Violations should be visible initially assert(fp.ViolationsVisible, 'testViolationsToggle: default true'); hM = fp.Thresholds(1).hMarkers; @@ -316,7 +316,7 @@ to: **Step 5: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run('tests/test_toolbar.m')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run('tests/test_toolbar.m')"` Expected: All 14 toolbar tests pass. **Step 6: Commit** @@ -331,11 +331,11 @@ git commit -m "test: add violations toggle test and update button count" ### Task 5: Update toolbar docstring **Files:** -- Modify: `libs/FastPlot/FastPlotToolbar.m:1-22` (classdef docstring) +- Modify: `libs/FastSense/FastSenseToolbar.m:1-22` (classdef docstring) **Step 1: Add Violations to the button list in the docstring** -In `libs/FastPlot/FastPlotToolbar.m`, after line 20 (`% Metadata — show/hide metadata in data cursor tooltips`), add: +In `libs/FastSense/FastSenseToolbar.m`, after line 20 (`% Metadata — show/hide metadata in data cursor tooltips`), add: ```matlab % Violations — toggle violation marker visibility @@ -344,6 +344,6 @@ In `libs/FastPlot/FastPlotToolbar.m`, after line 20 (`% Metadata — sho **Step 2: Commit** ```bash -git add libs/FastPlot/FastPlotToolbar.m +git add libs/FastSense/FastSenseToolbar.m git commit -m "docs: add violations button to toolbar docstring" ``` diff --git a/docs/plans/2026-03-10-live-event-pipeline-design.md b/docs/plans/2026-03-10-live-event-pipeline-design.md index ce495020..037cfc73 100644 --- a/docs/plans/2026-03-10-live-event-pipeline-design.md +++ b/docs/plans/2026-03-10-live-event-pipeline-design.md @@ -137,7 +137,7 @@ Two plots generated per notification: - Same threshold line + violation markers - Shows signal trend leading into the violation -Both generated headless (`'Visible', 'off'`) via FastPlot, saved as PNG, attached to email. Old snapshots auto-cleaned after configurable retention (default: 7 days). +Both generated headless (`'Visible', 'off'`) via FastSense, saved as PNG, attached to email. Old snapshots auto-cleaned after configurable retention (default: 7 days). ### 9. LiveEventPipeline (Orchestrator) @@ -166,7 +166,7 @@ Both generated headless (`'Visible', 'off'`) via FastPlot, saved as PNG, attache - `Event`, `EventDetector`, `EventConfig` - `EventViewer` (clients use `fromFile()` + `startAutoRefresh()`) - `Sensor`, `ThresholdRule`, `StateChannel`, `SensorRegistry` -- `FastPlot` +- `FastSense` ## Configuration diff --git a/docs/plans/2026-03-10-live-event-pipeline.md b/docs/plans/2026-03-10-live-event-pipeline.md index 9b334cbd..2d148d5c 100644 --- a/docs/plans/2026-03-10-live-event-pipeline.md +++ b/docs/plans/2026-03-10-live-event-pipeline.md @@ -4,9 +4,9 @@ **Goal:** Build a live event detection pipeline that reads continuously-updated sensor data, detects threshold violations incrementally, stores events to a shared `.mat` file, and sends email notifications with event snapshot plots. -**Architecture:** Monolithic orchestrator (`LiveEventPipeline`) owns a 15-second timer. Each cycle it reads new data via swappable `DataSource` objects, runs incremental detection with open-event carry-over, writes to an atomic `EventStore`, and triggers rule-based `NotificationService` with two FastPlot snapshot PNGs per event. +**Architecture:** Monolithic orchestrator (`LiveEventPipeline`) owns a 15-second timer. Each cycle it reads new data via swappable `DataSource` objects, runs incremental detection with open-event carry-over, writes to an atomic `EventStore`, and triggers rule-based `NotificationService` with two FastSense snapshot PNGs per event. -**Tech Stack:** MATLAB classes, `containers.Map`, MATLAB `timer`, `sendmail`, FastPlot for snapshot rendering. +**Tech Stack:** MATLAB classes, `containers.Map`, MATLAB `timer`, `sendmail`, FastSense for snapshot rendering. **Test runner:** `cd tests && matlab -batch "run_all_tests"` or individual: `matlab -batch "test_data_source"` @@ -1574,7 +1574,7 @@ function add_event_path() repoRoot = fileparts(thisDir); addpath(fullfile(repoRoot, 'libs', 'EventDetection')); addpath(fullfile(repoRoot, 'libs', 'SensorThreshold')); - addpath(fullfile(repoRoot, 'libs', 'FastPlot')); + addpath(fullfile(repoRoot, 'libs', 'FastSense')); setup(); end @@ -1654,7 +1654,7 @@ Expected: FAIL — `generateEventSnapshot` not found ```matlab function files = generateEventSnapshot(event, sensorData, varargin) - % generateEventSnapshot Create two FastPlot PNG snapshots for an event. + % generateEventSnapshot Create two FastSense PNG snapshots for an event. % % files = generateEventSnapshot(event, sensorData, ...) % @@ -1796,7 +1796,7 @@ function add_event_path() repoRoot = fileparts(thisDir); addpath(fullfile(repoRoot, 'libs', 'EventDetection')); addpath(fullfile(repoRoot, 'libs', 'SensorThreshold')); - addpath(fullfile(repoRoot, 'libs', 'FastPlot')); + addpath(fullfile(repoRoot, 'libs', 'FastSense')); setup(); end @@ -1910,7 +1910,7 @@ classdef NotificationService < handle SmtpPort = 25 SmtpUser = '' SmtpPassword = '' - FromAddress = 'fastplot@noreply.com' + FromAddress = 'fastsense@noreply.com' NotificationCount = 0 end @@ -1921,7 +1921,7 @@ classdef NotificationService < handle p.addParameter('DryRun', false, @islogical); p.addParameter('SnapshotDir', '', @ischar); p.addParameter('SmtpServer', '', @ischar); - p.addParameter('FromAddress', 'fastplot@noreply.com', @ischar); + p.addParameter('FromAddress', 'fastsense@noreply.com', @ischar); p.parse(varargin{:}); obj.Enabled = p.Results.Enabled; obj.DryRun = p.Results.DryRun; @@ -1929,7 +1929,7 @@ classdef NotificationService < handle obj.SmtpServer = p.Results.SmtpServer; obj.FromAddress = p.Results.FromAddress; if isempty(obj.SnapshotDir) - obj.SnapshotDir = fullfile(tempdir, 'fastplot_snapshots'); + obj.SnapshotDir = fullfile(tempdir, 'fastsense_snapshots'); end end @@ -2070,7 +2070,7 @@ function add_event_path() repoRoot = fileparts(thisDir); addpath(fullfile(repoRoot, 'libs', 'EventDetection')); addpath(fullfile(repoRoot, 'libs', 'SensorThreshold')); - addpath(fullfile(repoRoot, 'libs', 'FastPlot')); + addpath(fullfile(repoRoot, 'libs', 'FastSense')); setup(); end @@ -2447,7 +2447,7 @@ dsMap.add('vibration', MockDataSource( ... 'SampleInterval', 3, 'Seed', 7)); %% 3. Configure event store -storeFile = fullfile(tempdir, 'fastplot_live_events.mat'); +storeFile = fullfile(tempdir, 'fastsense_live_events.mat'); fprintf('Event store: %s\n', storeFile); %% 4. Create pipeline @@ -2460,7 +2460,7 @@ pipeline = LiveEventPipeline(sensors, dsMap, ... notif = NotificationService('DryRun', true); notif.setDefaultRule(NotificationRule( ... 'Recipients', {{'ops-team@company.com'}}, ... - 'Subject', '[FastPlot] {sensor}: {threshold} violation', ... + 'Subject', '[FastSense] {sensor}: {threshold} violation', ... 'Message', 'Sensor {sensor} violated {threshold} ({direction}) from {startTime} to {endTime}. Peak: {peak}', ... 'IncludeSnapshot', false)); diff --git a/docs/superpowers/plans/2026-03-11-eventdetection-optimization.md b/docs/superpowers/plans/2026-03-11-eventdetection-optimization.md index bf178ddb..444fb9a5 100644 --- a/docs/superpowers/plans/2026-03-11-eventdetection-optimization.md +++ b/docs/superpowers/plans/2026-03-11-eventdetection-optimization.md @@ -63,7 +63,7 @@ Add `test_slice_detection_consistency();` to the function list at the top of `te Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); test_incremental_detector +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); test_incremental_detector ``` Expected: ALL PASSED (this test should pass with current code too — it's a regression test) @@ -214,7 +214,7 @@ end Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); test_incremental_detector +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); test_incremental_detector ``` Expected: ALL PASSED (8 tests including the new one) @@ -222,7 +222,7 @@ Expected: ALL PASSED (8 tests including the new one) Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); run_all_tests +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); run_all_tests ``` Expected: 52/52 passed, 0 failed @@ -275,7 +275,7 @@ Add `test_bar_positions_cached();` to the test function list in `test_event_view Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); test_event_viewer +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); test_event_viewer ``` Expected: FAIL — `BarPositions` property does not exist @@ -361,7 +361,7 @@ end Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); test_event_viewer +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); test_event_viewer ``` Expected: ALL PASSED (7 tests including the new one) @@ -369,7 +369,7 @@ Expected: ALL PASSED (7 tests including the new one) Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); run_all_tests +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); run_all_tests ``` Expected: All passed, 0 failed @@ -426,7 +426,7 @@ delete(tmpFile); Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); test_event_config +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); test_event_config ``` Expected: ALL PASSED (this should pass with the current EventConfig.saveEvents too — it's a regression test) @@ -518,7 +518,7 @@ Remove the `pruneBackups` method (lines 140-153) from `EventConfig.m`. EventStor Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); test_event_config +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); test_event_config ``` Expected: ALL PASSED (9 tests including the new one) @@ -526,7 +526,7 @@ Expected: ALL PASSED (9 tests including the new one) Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); run_all_tests +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); run_all_tests ``` Expected: All passed, 0 failed @@ -551,7 +551,7 @@ Single backup implementation to maintain." Run in MATLAB: ``` -cd('/Users/hannessuhr/FastPlot'); setup(); cd('tests'); run_all_tests +cd('/Users/hannessuhr/FastSense'); setup(); cd('tests'); run_all_tests ``` Expected: All passed, 0 failed diff --git a/docs/superpowers/plans/2026-03-11-sensor-detail-plot.md b/docs/superpowers/plans/2026-03-11-sensor-detail-plot.md index b508f832..b23196d8 100644 --- a/docs/superpowers/plans/2026-03-11-sensor-detail-plot.md +++ b/docs/superpowers/plans/2026-03-11-sensor-detail-plot.md @@ -4,7 +4,7 @@ **Goal:** Build a two-panel sensor overview+detail plot with interactive navigator, threshold bands, and optional event overlay. -**Architecture:** Two new classes (`SensorDetailPlot`, `NavigatorOverlay`) in `libs/FastPlot/`, one new method (`tilePanel`) on `FastPlotFigure`. `SensorDetailPlot` coordinates two `FastPlot` instances; `NavigatorOverlay` handles the zoom rectangle, dimming, and drag interaction on the navigator axes. +**Architecture:** Two new classes (`SensorDetailPlot`, `NavigatorOverlay`) in `libs/FastSense/`, one new method (`tilePanel`) on `FastSenseFigure`. `SensorDetailPlot` coordinates two `FastSense` instances; `NavigatorOverlay` handles the zoom rectangle, dimming, and drag interaction on the navigator axes. **Tech Stack:** MATLAB (handle classes, uipanel layout, axes listeners, WindowButton callbacks) @@ -16,9 +16,9 @@ | File | Action | Responsibility | |------|--------|---------------| -| `libs/FastPlot/NavigatorOverlay.m` | Create | Zoom rectangle, dimming patches, drag interaction | -| `libs/FastPlot/SensorDetailPlot.m` | Create | Coordinator: two-panel layout, sensor rendering, event overlay, sync | -| `libs/FastPlot/FastPlotFigure.m` | Modify | Add `tilePanel(n)` method | +| `libs/FastSense/NavigatorOverlay.m` | Create | Zoom rectangle, dimming patches, drag interaction | +| `libs/FastSense/SensorDetailPlot.m` | Create | Coordinator: two-panel layout, sensor rendering, event overlay, sync | +| `libs/FastSense/FastSenseFigure.m` | Modify | Add `tilePanel(n)` method | | `tests/test_NavigatorOverlay.m` | Create | Unit tests for NavigatorOverlay | | `tests/test_SensorDetailPlot.m` | Create | Unit tests for SensorDetailPlot | | `examples/example_sensor_detail.m` | Create | Demo script showing standalone + events usage | @@ -30,7 +30,7 @@ ### Task 1: NavigatorOverlay — Class Skeleton + Visual Elements **Files:** -- Create: `libs/FastPlot/NavigatorOverlay.m` +- Create: `libs/FastSense/NavigatorOverlay.m` - Create: `tests/test_NavigatorOverlay.m` - [ ] **Step 1: Write failing tests for NavigatorOverlay construction and visual elements** @@ -43,7 +43,7 @@ function tests = test_NavigatorOverlay end function setup(testCase) - addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastPlot')); + addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastSense')); addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'SensorThreshold')); testCase.TestData.hFig = figure('Visible', 'off'); testCase.TestData.hAxes = axes('Parent', testCase.TestData.hFig); @@ -161,12 +161,12 @@ end - [ ] **Step 2: Run tests to verify they fail** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_NavigatorOverlay'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_NavigatorOverlay'); disp(results)"` Expected: FAIL — NavigatorOverlay class not found - [ ] **Step 3: Implement NavigatorOverlay class — visual elements + setRange** -Create `libs/FastPlot/NavigatorOverlay.m`: +Create `libs/FastSense/NavigatorOverlay.m`: ```matlab classdef NavigatorOverlay < handle @@ -488,13 +488,13 @@ end - [ ] **Step 4: Run tests to verify they pass** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_NavigatorOverlay'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_NavigatorOverlay'); disp(results)"` Expected: All 6 tests PASS - [ ] **Step 5: Commit** ```bash -git add libs/FastPlot/NavigatorOverlay.m tests/test_NavigatorOverlay.m +git add libs/FastSense/NavigatorOverlay.m tests/test_NavigatorOverlay.m git commit -m "feat: add NavigatorOverlay with visual elements and drag interaction" ``` @@ -543,7 +543,7 @@ end - [ ] **Step 2: Run tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_NavigatorOverlay'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_NavigatorOverlay'); disp(results)"` Expected: All 10 tests PASS - [ ] **Step 3: Commit** @@ -560,7 +560,7 @@ git commit -m "test: add NavigatorOverlay boundary clamping tests" ### Task 3: SensorDetailPlot — Constructor + Layout **Files:** -- Create: `libs/FastPlot/SensorDetailPlot.m` +- Create: `libs/FastSense/SensorDetailPlot.m` - Create: `tests/test_SensorDetailPlot.m` - [ ] **Step 1: Write failing tests for constructor and layout** @@ -573,7 +573,7 @@ function tests = test_SensorDetailPlot end function setup(testCase) - addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastPlot')); + addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastSense')); addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'SensorThreshold')); addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'EventDetection')); @@ -617,12 +617,12 @@ function test_constructor_custom_options(testCase) delete(sdp); end -%% Render creates two FastPlot instances +%% Render creates two FastSense instances function test_render_creates_main_and_navigator(testCase) sdp = SensorDetailPlot(testCase.TestData.sensor); sdp.render(); - verifyClass(testCase, sdp.MainPlot, ?FastPlot); - verifyClass(testCase, sdp.NavigatorPlot, ?FastPlot); + verifyClass(testCase, sdp.MainPlot, ?FastSense); + verifyClass(testCase, sdp.NavigatorPlot, ?FastSense); delete(sdp); end @@ -664,12 +664,12 @@ end - [ ] **Step 2: Run tests to verify they fail** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` Expected: FAIL — SensorDetailPlot class not found - [ ] **Step 3: Implement SensorDetailPlot — constructor + render + layout** -Create `libs/FastPlot/SensorDetailPlot.m`: +Create `libs/FastSense/SensorDetailPlot.m`: ```matlab classdef SensorDetailPlot < handle @@ -679,7 +679,7 @@ classdef SensorDetailPlot < handle % sdp = SensorDetailPlot(sensor, Name, Value, ...) % % Name-Value Options: - % 'Theme' - FastPlot theme (default: 'default') + % 'Theme' - FastSense theme (default: 'default') % 'NavigatorHeight' - Fraction 0-1 for navigator (default: 0.20) % 'ShowThresholds' - Show thresholds in main plot (default: true) % 'ShowThresholdBands' - Show threshold bands in navigator (default: true) @@ -690,8 +690,8 @@ classdef SensorDetailPlot < handle properties (SetAccess = private) Sensor % Sensor object - MainPlot % FastPlot instance for upper panel - NavigatorPlot % FastPlot instance for lower panel + MainPlot % FastSense instance for upper panel + NavigatorPlot % FastSense instance for lower panel NavigatorOverlayObj % NavigatorOverlay instance end @@ -763,8 +763,8 @@ classdef SensorDetailPlot < handle % Create layout obj.createLayout(); - % Create main FastPlot - obj.MainPlot = FastPlot('Parent', obj.hMainAxes, 'Theme', obj.Theme); + % Create main FastSense + obj.MainPlot = FastSense('Parent', obj.hMainAxes, 'Theme', obj.Theme); obj.MainPlot.addSensor(obj.Sensor, 'ShowThresholds', obj.ShowThresholds); % Render main plot @@ -775,8 +775,8 @@ classdef SensorDetailPlot < handle title(obj.hMainAxes, obj.Title); end - % Create navigator FastPlot - obj.NavigatorPlot = FastPlot('Parent', obj.hNavAxes, 'Theme', obj.Theme); + % Create navigator FastSense + obj.NavigatorPlot = FastSense('Parent', obj.hNavAxes, 'Theme', obj.Theme); obj.NavigatorPlot.addLine(obj.Sensor.X, obj.Sensor.Y, ... 'DisplayName', obj.Sensor.Name); @@ -1084,13 +1084,13 @@ end - [ ] **Step 4: Run tests to verify they pass** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` Expected: All 8 tests PASS - [ ] **Step 5: Commit** ```bash -git add libs/FastPlot/SensorDetailPlot.m tests/test_SensorDetailPlot.m +git add libs/FastSense/SensorDetailPlot.m tests/test_SensorDetailPlot.m git commit -m "feat: add SensorDetailPlot with two-panel layout and navigator sync" ``` @@ -1158,7 +1158,7 @@ end - [ ] **Step 2: Run tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` Expected: All 12 tests PASS - [ ] **Step 3: Commit** @@ -1170,7 +1170,7 @@ git commit -m "test: add threshold display tests for SensorDetailPlot" --- -## Chunk 3: Events, FastPlotFigure Integration, and Example +## Chunk 3: Events, FastSenseFigure Integration, and Example ### Task 5: SensorDetailPlot — Event Overlay Tests @@ -1343,7 +1343,7 @@ end - [ ] **Step 2: Run tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` Expected: All 19 tests PASS - [ ] **Step 3: Commit** @@ -1355,10 +1355,10 @@ git commit -m "test: add event overlay tests for SensorDetailPlot" --- -### Task 6: FastPlotFigure — tilePanel Method +### Task 6: FastSenseFigure — tilePanel Method **Files:** -- Modify: `libs/FastPlot/FastPlotFigure.m:211-251` (add after `axes(n)` method) +- Modify: `libs/FastSense/FastSenseFigure.m:211-251` (add after `axes(n)` method) - Modify: `tests/test_SensorDetailPlot.m` (add integration test) - [ ] **Step 1: Write failing test for tilePanel** @@ -1366,30 +1366,30 @@ git commit -m "test: add event overlay tests for SensorDetailPlot" Append to `tests/test_SensorDetailPlot.m`: ```matlab -%% FastPlotFigure tilePanel integration +%% FastSenseFigure tilePanel integration function test_tilePanel_returns_uipanel(testCase) - fig = FastPlotFigure(2, 1); + fig = FastSenseFigure(2, 1); hp = fig.tilePanel(1); verifyTrue(testCase, isa(hp, 'matlab.ui.container.Panel')); delete(fig); end function test_tilePanel_conflict_with_tile(testCase) - fig = FastPlotFigure(2, 1); - fig.tile(1); % Occupy tile 1 as FastPlot - verifyError(testCase, @() fig.tilePanel(1), 'FastPlotFigure:tileConflict'); + fig = FastSenseFigure(2, 1); + fig.tile(1); % Occupy tile 1 as FastSense + verifyError(testCase, @() fig.tilePanel(1), 'FastSenseFigure:tileConflict'); delete(fig); end -%% Embedded in FastPlotFigure +%% Embedded in FastSenseFigure function test_embedded_in_figure_tile(testCase) s = testCase.TestData.sensor; - fig = FastPlotFigure(1, 1); + fig = FastSenseFigure(1, 1); hp = fig.tilePanel(1); sdp = SensorDetailPlot(s, 'Parent', hp); sdp.render(); verifyTrue(testCase, sdp.IsRendered); - verifyClass(testCase, sdp.MainPlot, ?FastPlot); + verifyClass(testCase, sdp.MainPlot, ?FastSense); delete(sdp); delete(fig); end @@ -1397,16 +1397,16 @@ end - [ ] **Step 2: Run tests to verify they fail** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot', 'ProcedureName', 'test_tilePanel*'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot', 'ProcedureName', 'test_tilePanel*'); disp(results)"` Expected: FAIL — tilePanel method not found -- [ ] **Step 3: Read FastPlotFigure.m to find insertion point** +- [ ] **Step 3: Read FastSenseFigure.m to find insertion point** -Read: `libs/FastPlot/FastPlotFigure.m:211-260` to see the `axes(n)` method and find where to add `tilePanel(n)`. +Read: `libs/FastSense/FastSenseFigure.m:211-260` to see the `axes(n)` method and find where to add `tilePanel(n)`. -- [ ] **Step 4: Add tilePanel method to FastPlotFigure** +- [ ] **Step 4: Add tilePanel method to FastSenseFigure** -Add after the `axes(n)` method (around line 251) in `libs/FastPlot/FastPlotFigure.m`. The method follows the same pattern as `axes(n)`: +Add after the `axes(n)` method (around line 251) in `libs/FastSense/FastSenseFigure.m`. The method follows the same pattern as `axes(n)`: ```matlab function hp = tilePanel(obj, n) @@ -1416,11 +1416,11 @@ Add after the `axes(n)` method (around line 251) in `libs/FastPlot/FastPlotFigur % composite widgets (e.g. SensorDetailPlot) into a tile. % % Throws an error if tile n is already occupied by a - % FastPlot (via tile()) or raw axes (via axes()). + % FastSense (via tile()) or raw axes (via axes()). nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotFigure:invalidTile', ... + error('FastSenseFigure:invalidTile', ... 'Tile index %d is out of range [1, %d].', n, nTiles); end @@ -1430,15 +1430,15 @@ Add after the `axes(n)` method (around line 251) in `libs/FastPlot/FastPlotFigur return; end - % Conflict check: occupied by FastPlot? + % Conflict check: occupied by FastSense? if ~isempty(obj.Tiles{n}) - error('FastPlotFigure:tileConflict', ... - 'Tile %d is a FastPlot tile. Use tile(%d) to access it.', n, n); + error('FastSenseFigure:tileConflict', ... + 'Tile %d is a FastSense tile. Use tile(%d) to access it.', n, n); end % Conflict check: occupied by raw axes? if obj.RawAxesTiles(n) - error('FastPlotFigure:tileConflict', ... + error('FastSenseFigure:tileConflict', ... 'Tile %d is a raw axes tile. Use axes(%d) to access it.', n, n); end @@ -1457,12 +1457,12 @@ Add after the `axes(n)` method (around line 251) in `libs/FastPlot/FastPlotFigur - [ ] **Step 5: Run tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` Expected: All 22 tests PASS - [ ] **Step 6: Also make `IsRendered` accessible for test** -In `libs/FastPlot/SensorDetailPlot.m`, change `IsRendered` access from `(Access = private)` to `(SetAccess = private, GetAccess = ?matlab.unittest.TestCase)` or simply move it to the `(SetAccess = private, GetAccess = public)` block since it's a useful read-only property: +In `libs/FastSense/SensorDetailPlot.m`, change `IsRendered` access from `(Access = private)` to `(SetAccess = private, GetAccess = ?matlab.unittest.TestCase)` or simply move it to the `(SetAccess = private, GetAccess = public)` block since it's a useful read-only property: Move `IsRendered` from the private properties block to the public-readable block: @@ -1481,14 +1481,14 @@ Move `IsRendered` from the private properties block to the public-readable block - [ ] **Step 7: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('test_SensorDetailPlot'); disp(results)"` Expected: All 22 tests PASS - [ ] **Step 8: Commit** ```bash -git add libs/FastPlot/FastPlotFigure.m libs/FastPlot/SensorDetailPlot.m tests/test_SensorDetailPlot.m -git commit -m "feat: add tilePanel method to FastPlotFigure for composite widget embedding" +git add libs/FastSense/FastSenseFigure.m libs/FastSense/SensorDetailPlot.m tests/test_SensorDetailPlot.m +git commit -m "feat: add tilePanel method to FastSenseFigure for composite widget embedding" ``` --- @@ -1508,10 +1508,10 @@ Create `examples/example_sensor_detail.m`: % Demonstrates: % 1. Standalone sensor detail plot with thresholds % 2. Adding events from EventStore -% 3. Embedding in a FastPlotFigure tile +% 3. Embedding in a FastSenseFigure tile %% Setup path -addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastPlot')); +addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'FastSense')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'SensorThreshold')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', 'libs', 'EventDetection')); @@ -1589,20 +1589,20 @@ sdp2.render(); fprintf(' Press any key to continue...\n'); pause; -%% 5. Embedded in FastPlotFigure -fprintf('=== SensorDetailPlot: Embedded in FastPlotFigure ===\n'); -fig = FastPlotFigure(1, 2, 'Theme', 'dark', 'Name', 'Sensor Dashboard'); +%% 5. Embedded in FastSenseFigure +fprintf('=== SensorDetailPlot: Embedded in FastSenseFigure ===\n'); +fig = FastSenseFigure(1, 2, 'Theme', 'dark', 'Name', 'Sensor Dashboard'); sdp3 = SensorDetailPlot(s, 'Parent', fig.tilePanel(1), ... 'Events', events, 'Title', 'Temperature'); sdp3.render(); -% Second tile: plain FastPlot for comparison +% Second tile: plain FastSense for comparison fp = fig.tile(2); fp.addLine(t, data, 'DisplayName', 'Raw Data'); fig.tileTitle(2, 'Raw Data'); fig.renderAll(); -fprintf(' Two tiles: SensorDetailPlot + plain FastPlot\n'); +fprintf(' Two tiles: SensorDetailPlot + plain FastSense\n'); fprintf(' Press any key to exit...\n'); pause; @@ -1611,7 +1611,7 @@ fprintf('Done.\n'); - [ ] **Step 2: Run example to verify it works** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run('examples/example_sensor_detail.m')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run('examples/example_sensor_detail.m')"` Expected: Two figure windows open showing the sensor detail plots. No errors. - [ ] **Step 3: Commit** @@ -1627,12 +1627,12 @@ git commit -m "feat: add example_sensor_detail demo script" - [ ] **Step 1: Run all tests to ensure nothing is broken** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "addpath('tests'); results = runtests('tests'); disp(table(results))"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "addpath('tests'); results = runtests('tests'); disp(table(results))"` Expected: All tests PASS (existing tests + new tests) - [ ] **Step 2: Fix any failures** -If any existing tests fail, investigate and fix. The new classes should not affect existing behavior since they are additive (new files) with only one modification to `FastPlotFigure.m` (adding a new method, no changes to existing methods). +If any existing tests fail, investigate and fix. The new classes should not affect existing behavior since they are additive (new files) with only one modification to `FastSenseFigure.m` (adding a new method, no changes to existing methods). - [ ] **Step 3: Final commit if fixes were needed** diff --git a/docs/superpowers/plans/2026-03-13-web-bridge-mvp.md b/docs/superpowers/plans/2026-03-13-web-bridge-mvp.md index 229b0a8b..527572f2 100644 --- a/docs/superpowers/plans/2026-03-13-web-bridge-mvp.md +++ b/docs/superpowers/plans/2026-03-13-web-bridge-mvp.md @@ -29,7 +29,7 @@ | File | Change | |------|--------| -| `libs/FastPlot/FastPlotDataStore.m` | Add `enableWAL()` / `disableWAL()` methods | +| `libs/FastSense/FastSenseDataStore.m` | Add `enableWAL()` / `disableWAL()` methods | | `setup.m` | Add `libs/WebBridge` to MATLAB path | | `tests/suite/TestDataStoreWAL.m` | Tests for WAL methods | @@ -38,12 +38,12 @@ | File | Responsibility | |------|---------------| | `bridge/python/pyproject.toml` | Package config, dependencies | -| `bridge/python/fastplot_bridge/__init__.py` | Package init | -| `bridge/python/fastplot_bridge/__main__.py` | CLI entry point | -| `bridge/python/fastplot_bridge/blob_decoder.py` | mksqlite typed BLOB header parser | -| `bridge/python/fastplot_bridge/sqlite_reader.py` | SQLite queries + BLOB decoding + downsampling | -| `bridge/python/fastplot_bridge/tcp_client.py` | Async NDJSON-over-TCP client to MATLAB | -| `bridge/python/fastplot_bridge/server.py` | FastAPI app: REST API + WebSocket + static files | +| `bridge/python/fastsense_bridge/__init__.py` | Package init | +| `bridge/python/fastsense_bridge/__main__.py` | CLI entry point | +| `bridge/python/fastsense_bridge/blob_decoder.py` | mksqlite typed BLOB header parser | +| `bridge/python/fastsense_bridge/sqlite_reader.py` | SQLite queries + BLOB decoding + downsampling | +| `bridge/python/fastsense_bridge/tcp_client.py` | Async NDJSON-over-TCP client to MATLAB | +| `bridge/python/fastsense_bridge/server.py` | FastAPI app: REST API + WebSocket + static files | | `bridge/python/tests/test_blob_decoder.py` | Unit tests for BLOB parser | | `bridge/python/tests/test_sqlite_reader.py` | Unit tests for SQLite reader | | `bridge/python/tests/test_tcp_client.py` | Unit tests for TCP client | @@ -95,10 +95,10 @@ git commit -m "chore: add WebBridge to MATLAB path in setup.m" --- -### Task 1: Add WAL Methods to FastPlotDataStore +### Task 1: Add WAL Methods to FastSenseDataStore **Files:** -- Modify: `libs/FastPlot/FastPlotDataStore.m` +- Modify: `libs/FastSense/FastSenseDataStore.m` - Test: `tests/suite/TestDataStoreWAL.m` - [ ] **Step 1: Write the failing test** @@ -120,7 +120,7 @@ classdef TestDataStoreWAL < matlab.unittest.TestCase % Create a DataStore with some data x = 1:1000; y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() delete(ds)); % Enable WAL mode @@ -135,7 +135,7 @@ classdef TestDataStoreWAL < matlab.unittest.TestCase function testDisableWAL(testCase) x = 1:1000; y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() delete(ds)); ds.enableWAL(); @@ -149,7 +149,7 @@ classdef TestDataStoreWAL < matlab.unittest.TestCase function testDataAccessAfterWAL(testCase) x = 1:1000; y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() delete(ds)); ds.enableWAL(); @@ -165,12 +165,12 @@ end - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestDataStoreWAL')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestDataStoreWAL')"` Expected: FAIL — `enableWAL` method not found - [ ] **Step 3: Implement enableWAL and disableWAL** -Add to `libs/FastPlot/FastPlotDataStore.m` in the public methods block: +Add to `libs/FastSense/FastSenseDataStore.m` in the public methods block: ```matlab function enableWAL(obj) @@ -192,14 +192,14 @@ end - [ ] **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestDataStoreWAL')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestDataStoreWAL')"` Expected: PASS (3/3 tests) - [ ] **Step 5: Commit** ```bash -git add libs/FastPlot/FastPlotDataStore.m tests/suite/TestDataStoreWAL.m -git commit -m "feat: add enableWAL/disableWAL to FastPlotDataStore for concurrent reads" +git add libs/FastSense/FastSenseDataStore.m tests/suite/TestDataStoreWAL.m +git commit -m "feat: add enableWAL/disableWAL to FastSenseDataStore for concurrent reads" ``` --- @@ -291,7 +291,7 @@ end - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestWebBridgeProtocol')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestWebBridgeProtocol')"` Expected: FAIL — WebBridgeProtocol not found - [ ] **Step 3: Create libs/WebBridge directory and implement WebBridgeProtocol** @@ -358,7 +358,7 @@ end - [ ] **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestWebBridgeProtocol')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestWebBridgeProtocol')"` Expected: PASS (7/7 tests) - [ ] **Step 5: Commit** @@ -511,7 +511,7 @@ end - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestWebBridge')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestWebBridge')"` Expected: FAIL — WebBridge not found - [ ] **Step 3: Implement WebBridge.m** @@ -694,7 +694,7 @@ classdef WebBridge < handle idx = 0; for i = 1:numel(obj.Dashboard.Widgets) w = obj.Dashboard.Widgets{i}; - if ~isa(w, 'FastPlotWidget'); continue; end + if ~isa(w, 'FastSenseWidget'); continue; end idx = idx + 1; if isprop(w, 'Sensor') && ~isempty(w.Sensor) && isprop(w.Sensor, 'Key') sid = w.Sensor.Key; @@ -779,7 +779,7 @@ classdef WebBridge < handle % Find the bridge script relative to this file bridgeDir = fullfile(fileparts(mfilename('fullpath')), ... '..', '..', 'bridge', 'python'); - cmd = sprintf('python -m fastplot_bridge --matlab-port %d', obj.TcpPort); + cmd = sprintf('python -m fastsense_bridge --matlab-port %d', obj.TcpPort); if ispc fullCmd = sprintf('start /B %s', cmd); else @@ -798,7 +798,7 @@ classdef WebBridge < handle end obj.stop(); error('WebBridge:timeout', ... - 'Bridge did not start within 10s. Check that fastplot-bridge is installed.'); + 'Bridge did not start within 10s. Check that fastsense-bridge is installed.'); end function enableWALOnDataStores(obj) @@ -822,7 +822,7 @@ classdef WebBridge < handle end for i = 1:numel(obj.Dashboard.Widgets) w = obj.Dashboard.Widgets{i}; - if ~isa(w, 'FastPlotWidget'); continue; end + if ~isa(w, 'FastSenseWidget'); continue; end ds = []; if isprop(w, 'DataStore') && ~isempty(w.DataStore) ds = w.DataStore; @@ -841,7 +841,7 @@ end - [ ] **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestWebBridge')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestWebBridge')"` Expected: PASS (7/7 tests) - [ ] **Step 5: Commit** @@ -859,21 +859,21 @@ git commit -m "feat: add WebBridge class with TCP server, action registry, and l **Files:** - Create: `bridge/python/pyproject.toml` -- Create: `bridge/python/fastplot_bridge/__init__.py` -- Create: `bridge/python/fastplot_bridge/blob_decoder.py` +- Create: `bridge/python/fastsense_bridge/__init__.py` +- Create: `bridge/python/fastsense_bridge/blob_decoder.py` - Test: `bridge/python/tests/test_blob_decoder.py` - [ ] **Step 1: Create project structure** ```bash -mkdir -p bridge/python/fastplot_bridge bridge/python/tests +mkdir -p bridge/python/fastsense_bridge bridge/python/tests ``` Create `bridge/python/pyproject.toml`: ```toml [project] -name = "fastplot-bridge" +name = "fastsense-bridge" version = "0.1.0" requires-python = ">=3.11" dependencies = [ @@ -888,16 +888,16 @@ dependencies = [ dev = ["pytest>=7.0", "pytest-asyncio>=0.21", "httpx>=0.25"] [project.scripts] -fastplot-bridge = "fastplot_bridge.__main__:main" +fastsense-bridge = "fastsense_bridge.__main__:main" [tool.pytest.ini_options] asyncio_mode = "auto" ``` -Create `bridge/python/fastplot_bridge/__init__.py`: +Create `bridge/python/fastsense_bridge/__init__.py`: ```python -"""FastPlot Bridge — serves MATLAB dashboard data via REST/WebSocket.""" +"""FastSense Bridge — serves MATLAB dashboard data via REST/WebSocket.""" ``` - [ ] **Step 2: Write the failing test for blob_decoder** @@ -908,7 +908,7 @@ Create `bridge/python/tests/test_blob_decoder.py`: import struct import numpy as np import pytest -from fastplot_bridge.blob_decoder import decode_typed_blob, MKSQ_MAGIC +from fastsense_bridge.blob_decoder import decode_typed_blob, MKSQ_MAGIC MX_DOUBLE = 6 MX_SINGLE = 7 @@ -972,12 +972,12 @@ class TestBlobDecoder: - [ ] **Step 3: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pip install -e ".[dev]" && pytest tests/test_blob_decoder.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pip install -e ".[dev]" && pytest tests/test_blob_decoder.py -v` Expected: FAIL — module not found - [ ] **Step 4: Implement blob_decoder.py** -Create `bridge/python/fastplot_bridge/blob_decoder.py`: +Create `bridge/python/fastsense_bridge/blob_decoder.py`: ```python """Decoder for mksqlite typed BLOB format (24-byte header + raw data).""" @@ -1047,7 +1047,7 @@ def decode_typed_blob(data: bytes | memoryview) -> np.ndarray | str | list: - [ ] **Step 5: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pytest tests/test_blob_decoder.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pytest tests/test_blob_decoder.py -v` Expected: PASS (8/8 tests) - [ ] **Step 6: Commit** @@ -1062,7 +1062,7 @@ git commit -m "feat: add Python bridge project setup and mksqlite BLOB decoder" ### Task 5: Python SQLite Reader **Files:** -- Create: `bridge/python/fastplot_bridge/sqlite_reader.py` +- Create: `bridge/python/fastsense_bridge/sqlite_reader.py` - Test: `bridge/python/tests/test_sqlite_reader.py` **Dependencies:** Task 4 (blob_decoder) @@ -1078,8 +1078,8 @@ import tempfile import numpy as np import pytest from pathlib import Path -from fastplot_bridge.blob_decoder import MKSQ_MAGIC -from fastplot_bridge.sqlite_reader import SqliteReader +from fastsense_bridge.blob_decoder import MKSQ_MAGIC +from fastsense_bridge.sqlite_reader import SqliteReader def _make_double_blob(values: list[float]) -> bytes: @@ -1090,7 +1090,7 @@ def _make_double_blob(values: list[float]) -> bytes: @pytest.fixture def sample_db(tmp_path) -> Path: - """Create a minimal .fpdb file matching FastPlotDataStore schema.""" + """Create a minimal .fpdb file matching FastSenseDataStore schema.""" db_path = tmp_path / "test.fpdb" conn = sqlite3.connect(str(db_path)) @@ -1178,15 +1178,15 @@ class TestSqliteReader: - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pytest tests/test_sqlite_reader.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pytest tests/test_sqlite_reader.py -v` Expected: FAIL — SqliteReader not found - [ ] **Step 3: Implement sqlite_reader.py** -Create `bridge/python/fastplot_bridge/sqlite_reader.py`: +Create `bridge/python/fastsense_bridge/sqlite_reader.py`: ```python -"""Read FastPlotDataStore SQLite files and decode typed BLOBs.""" +"""Read FastSenseDataStore SQLite files and decode typed BLOBs.""" import sqlite3 import numpy as np @@ -1194,7 +1194,7 @@ from .blob_decoder import decode_typed_blob class SqliteReader: - """Synchronous reader for .fpdb files created by FastPlotDataStore.""" + """Synchronous reader for .fpdb files created by FastSenseDataStore.""" def __init__(self, db_path: str): self._conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) @@ -1352,13 +1352,13 @@ def _minmax_downsample( - [ ] **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pytest tests/test_sqlite_reader.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pytest tests/test_sqlite_reader.py -v` Expected: PASS (5/5 tests) - [ ] **Step 5: Commit** ```bash -git add bridge/python/fastplot_bridge/sqlite_reader.py bridge/python/tests/test_sqlite_reader.py +git add bridge/python/fastsense_bridge/sqlite_reader.py bridge/python/tests/test_sqlite_reader.py git commit -m "feat: add SQLite reader with BLOB decoding and minmax downsampling" ``` @@ -1367,7 +1367,7 @@ git commit -m "feat: add SQLite reader with BLOB decoding and minmax downsamplin ### Task 6: Python TCP Client **Files:** -- Create: `bridge/python/fastplot_bridge/tcp_client.py` +- Create: `bridge/python/fastsense_bridge/tcp_client.py` - Test: `bridge/python/tests/test_tcp_client.py` - [ ] **Step 1: Write the failing test** @@ -1379,7 +1379,7 @@ import asyncio import json import pytest import pytest_asyncio -from fastplot_bridge.tcp_client import MatlabTcpClient +from fastsense_bridge.tcp_client import MatlabTcpClient @pytest_asyncio.fixture @@ -1473,12 +1473,12 @@ class TestMatlabTcpClient: - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pytest tests/test_tcp_client.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pytest tests/test_tcp_client.py -v` Expected: FAIL — MatlabTcpClient not found - [ ] **Step 3: Implement tcp_client.py** -Create `bridge/python/fastplot_bridge/tcp_client.py`: +Create `bridge/python/fastsense_bridge/tcp_client.py`: ```python """Async NDJSON-over-TCP client for connecting to MATLAB's WebBridge.""" @@ -1559,13 +1559,13 @@ class MatlabTcpClient: - [ ] **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pytest tests/test_tcp_client.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pytest tests/test_tcp_client.py -v` Expected: PASS (4/4 tests) - [ ] **Step 5: Commit** ```bash -git add bridge/python/fastplot_bridge/tcp_client.py bridge/python/tests/test_tcp_client.py +git add bridge/python/fastsense_bridge/tcp_client.py bridge/python/tests/test_tcp_client.py git commit -m "feat: add async NDJSON TCP client for MATLAB communication" ``` @@ -1574,8 +1574,8 @@ git commit -m "feat: add async NDJSON TCP client for MATLAB communication" ### Task 7: Python FastAPI Server **Files:** -- Create: `bridge/python/fastplot_bridge/server.py` -- Create: `bridge/python/fastplot_bridge/__main__.py` +- Create: `bridge/python/fastsense_bridge/server.py` +- Create: `bridge/python/fastsense_bridge/__main__.py` - Test: `bridge/python/tests/test_server.py` **Dependencies:** Task 5 (sqlite_reader), Task 6 (tcp_client) @@ -1593,8 +1593,8 @@ import numpy as np from pathlib import Path from unittest.mock import AsyncMock, MagicMock from fastapi.testclient import TestClient -from fastplot_bridge.blob_decoder import MKSQ_MAGIC -from fastplot_bridge.server import create_app, AppState +from fastsense_bridge.blob_decoder import MKSQ_MAGIC +from fastsense_bridge.server import create_app, AppState def _make_double_blob(values: list[float]) -> bytes: @@ -1709,12 +1709,12 @@ class TestServerAPI: - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pytest tests/test_server.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pytest tests/test_server.py -v` Expected: FAIL — create_app / AppState not found - [ ] **Step 3: Implement server.py** -Create `bridge/python/fastplot_bridge/server.py`: +Create `bridge/python/fastsense_bridge/server.py`: ```python """FastAPI server: REST API + WebSocket + static file serving.""" @@ -1792,7 +1792,7 @@ class AppState: def create_app(state: AppState) -> FastAPI: - app = FastAPI(title="FastPlot Bridge") + app = FastAPI(title="FastSense Bridge") # --- REST API --- @@ -1867,8 +1867,8 @@ def create_app(state: AppState) -> FastAPI: # --- Static files --- - # server.py is at bridge/python/fastplot_bridge/server.py - # Go up to bridge/python/fastplot_bridge → bridge/python → bridge, then /web + # server.py is at bridge/python/fastsense_bridge/server.py + # Go up to bridge/python/fastsense_bridge → bridge/python → bridge, then /web web_dir = Path(__file__).resolve().parent.parent.parent / "web" if web_dir.is_dir(): @app.get("/") @@ -1882,10 +1882,10 @@ def create_app(state: AppState) -> FastAPI: - [ ] **Step 4: Implement __main__.py** -Create `bridge/python/fastplot_bridge/__main__.py`: +Create `bridge/python/fastsense_bridge/__main__.py`: ```python -"""CLI entry point for the FastPlot bridge server.""" +"""CLI entry point for the FastSense bridge server.""" import argparse import asyncio @@ -1927,7 +1927,7 @@ async def run(matlab_port: int, http_host: str, http_port: int): def main(): - parser = argparse.ArgumentParser(description="FastPlot Bridge Server") + parser = argparse.ArgumentParser(description="FastSense Bridge Server") parser.add_argument("--matlab-port", type=int, required=True, help="MATLAB TCP port") parser.add_argument("--host", default="localhost", help="HTTP bind host") parser.add_argument("--port", type=int, default=8080, help="HTTP port") @@ -1942,13 +1942,13 @@ if __name__ == "__main__": - [ ] **Step 5: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot/bridge/python && pytest tests/test_server.py -v` +Run: `cd /Users/hannessuhr/FastSense/bridge/python && pytest tests/test_server.py -v` Expected: PASS (9/9 tests) - [ ] **Step 6: Commit** ```bash -git add bridge/python/fastplot_bridge/server.py bridge/python/fastplot_bridge/__main__.py bridge/python/tests/test_server.py +git add bridge/python/fastsense_bridge/server.py bridge/python/fastsense_bridge/__main__.py bridge/python/tests/test_server.py git commit -m "feat: add FastAPI bridge server with REST API, WebSocket, and CLI entry point" ``` @@ -1979,13 +1979,13 @@ Create `bridge/web/index.html`: - FastPlot Dashboard + FastSense Dashboard
@@ -2109,7 +2109,7 @@ Create `bridge/web/js/app.js`: ```javascript /** - * FastPlot Bridge — Main entry point. + * FastSense Bridge — Main entry point. * Connects WebSocket, loads dashboard, handles live updates. */ const App = (() => { @@ -2126,7 +2126,7 @@ const App = (() => { try { const resp = await fetch('/api/dashboard'); const config = await resp.json(); - document.getElementById('dashboard-title').textContent = config.name || 'FastPlot Dashboard'; + document.getElementById('dashboard-title').textContent = config.name || 'FastSense Dashboard'; Dashboard.render(config); } catch (e) { showToast('Failed to load dashboard', 'error'); @@ -2219,7 +2219,7 @@ git commit -m "feat: add web frontend shell with HTML, CSS grid layout, and WebS - [ ] **Step 1: Download uPlot** ```bash -cd /Users/hannessuhr/FastPlot/bridge/web/vendor +cd /Users/hannessuhr/FastSense/bridge/web/vendor curl -L -o uPlot.min.js "https://unpkg.com/uplot@1.6.31/dist/uPlot.iife.min.js" curl -L -o uPlot.min.css "https://unpkg.com/uplot@1.6.31/dist/uPlot.min.css" ``` @@ -2349,7 +2349,7 @@ const Widgets = (() => { function render(widgetConfig, bodyEl) { const type = widgetConfig.type || ''; switch (type) { - case 'fastplot': return renderFastPlot(widgetConfig, bodyEl); + case 'fastsense': return renderFastSense(widgetConfig, bodyEl); case 'kpi': return renderKpi(widgetConfig, bodyEl); case 'status': return renderStatus(widgetConfig, bodyEl); case 'table': return renderTable(widgetConfig, bodyEl); @@ -2361,7 +2361,7 @@ const Widgets = (() => { } } - function renderFastPlot(config, el) { + function renderFastSense(config, el) { // Determine signal ID from config source let signalId = ''; if (config.source) { @@ -2596,9 +2596,9 @@ git commit -m "feat: add action panel with button rendering and invocation" ## Chunk 4: Integration & Wiring -### Task 12: Wire FastPlotWidget Signal IDs into Dashboard Config +### Task 12: Wire FastSenseWidget Signal IDs into Dashboard Config -The web frontend needs to know which signal ID maps to each FastPlotWidget. The `DashboardSerializer.widgetsToConfig` output must include this mapping. +The web frontend needs to know which signal ID maps to each FastSenseWidget. The `DashboardSerializer.widgetsToConfig` output must include this mapping. **Files:** - Modify: `libs/WebBridge/WebBridge.m` (buildDashboardConfig method) @@ -2621,7 +2621,7 @@ function config = buildDashboardConfig(obj) sigIdx = 0; for i = 1:numel(config.widgets) w = config.widgets{i}; - if strcmp(w.type, 'fastplot') && sigIdx < numel(signals) + if strcmp(w.type, 'fastsense') && sigIdx < numel(signals) sigIdx = sigIdx + 1; config.widgets{i}.signalId = signals(sigIdx).id; end @@ -2631,7 +2631,7 @@ end - [ ] **Step 2: Run existing WebBridge tests to verify no regressions** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestWebBridge')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestWebBridge')"` Expected: PASS - [ ] **Step 3: Commit** @@ -2658,7 +2658,7 @@ Create `tests/suite/TestWebBridgeE2E.m`: classdef TestWebBridgeE2E < matlab.unittest.TestCase %TESTWEBBRIDGEE2E End-to-end test: MATLAB WebBridge + Python bridge. % - % Requires: Python 3.11+ with fastplot-bridge installed. + % Requires: Python 3.11+ with fastsense-bridge installed. % Skip if not available. methods (TestClassSetup) @@ -2671,15 +2671,15 @@ classdef TestWebBridgeE2E < matlab.unittest.TestCase methods (Test) function testServeAndFetchData(testCase) % Skip if Python bridge not installed - [status, ~] = system('python -c "import fastplot_bridge"'); + [status, ~] = system('python -c "import fastsense_bridge"'); testCase.assumeTrue(status == 0, ... - 'fastplot-bridge Python package not installed'); + 'fastsense-bridge Python package not installed'); % Create a dashboard with one signal x = linspace(0, 100, 10000); y = sin(x); engine = DashboardEngine('E2E Test'); - engine.addWidget('fastplot', 'Title', 'Sine Wave', ... + engine.addWidget('fastsense', 'Title', 'Sine Wave', ... 'XData', x, 'YData', y, 'Position', [1 1 6 3]); bridge = WebBridge(engine); @@ -2710,7 +2710,7 @@ end - [ ] **Step 2: Run the E2E test (requires Python bridge installed)** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "run_all_tests('TestWebBridgeE2E')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "run_all_tests('TestWebBridgeE2E')"` Expected: PASS (or SKIP if Python not set up) - [ ] **Step 3: Commit** diff --git a/docs/superpowers/plans/2026-03-16-ci-readme-wiki.md b/docs/superpowers/plans/2026-03-16-ci-readme-wiki.md index 694f48e9..8e1f4235 100644 --- a/docs/superpowers/plans/2026-03-16-ci-readme-wiki.md +++ b/docs/superpowers/plans/2026-03-16-ci-readme-wiki.md @@ -142,7 +142,7 @@ jobs: - name: Package release run: | VERSION="${{ steps.version.outputs.VERSION }}" - DIRNAME="FastPlot-${VERSION}" + DIRNAME="FastSense-${VERSION}" mkdir -p "${DIRNAME}" # Copy release contents @@ -173,8 +173,8 @@ jobs: Download the archive, extract it, and run `setup` in MATLAB/Octave to add libraries to path and compile MEX accelerators. files: | - FastPlot-${{ steps.version.outputs.VERSION }}.tar.gz - FastPlot-${{ steps.version.outputs.VERSION }}.zip + FastSense-${{ steps.version.outputs.VERSION }}.tar.gz + FastSense-${{ steps.version.outputs.VERSION }}.zip ``` - [ ] **Step 2: Verify YAML syntax** @@ -205,9 +205,9 @@ git commit -m "ci: add release pipeline with test gate and auto-packaging" Replace the entire `README.md` with a concise version. Key content: ```markdown -# FastPlot +# FastSense -[![Tests](https://github.com/HanSur94/FastPlot/actions/workflows/tests.yml/badge.svg)](https://github.com/HanSur94/FastPlot/actions/workflows/tests.yml) +[![Tests](https://github.com/HanSur94/FastSense/actions/workflows/tests.yml/badge.svg)](https://github.com/HanSur94/FastSense/actions/workflows/tests.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![MATLAB](https://img.shields.io/badge/MATLAB-R2020b%2B-orange.svg)](https://www.mathworks.com/products/matlab.html) [![Octave](https://img.shields.io/badge/GNU%20Octave-7%2B-blue.svg)](https://octave.org) @@ -215,7 +215,7 @@ Replace the entire `README.md` with a concise version. Key content: Ultra-fast time series plotting for MATLAB and GNU Octave. Plot 100M+ data points with fluid zoom and pan — rendering only ~4,000 points at any zoom level.

- FastPlot Dashboard + FastSense Dashboard

## Performance @@ -253,7 +253,7 @@ setup; % adds libraries to path + compiles MEX x = linspace(0, 100, 1e7); y = sin(x) + 0.1 * randn(size(x)); -fp = FastPlot('Theme', 'dark'); +fp = FastSense('Theme', 'dark'); fp.addLine(x, y, 'DisplayName', 'Sensor'); fp.addThreshold(0.8, 'Direction', 'upper', 'ShowViolations', true); fp.render(); @@ -263,8 +263,8 @@ fp.render(); ## Installation ```bash -git clone https://github.com/HanSur94/FastPlot.git -cd FastPlot +git clone https://github.com/HanSur94/FastSense.git +cd FastSense ``` Then in MATLAB or Octave: @@ -279,26 +279,26 @@ No toolbox dependencies. MEX compilation is optional — pure MATLAB fallbacks a ## Documentation -Full documentation is available in the [Wiki](https://github.com/HanSur94/FastPlot/wiki): +Full documentation is available in the [Wiki](https://github.com/HanSur94/FastSense/wiki): -- [Getting Started](https://github.com/HanSur94/FastPlot/wiki/Getting-Started) — tutorial with examples -- [API Reference: FastPlot](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-FastPlot) — core plotting class -- [API Reference: Dashboard](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-Dashboard) — layouts, widgets, engine -- [API Reference: Sensors](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-Sensors) — sensor system -- [API Reference: Event Detection](https://github.com/HanSur94/FastPlot/wiki/API-Reference:-Event-Detection) — event pipeline -- [Architecture](https://github.com/HanSur94/FastPlot/wiki/Architecture) — render pipeline, data flow -- [MEX Acceleration](https://github.com/HanSur94/FastPlot/wiki/MEX-Acceleration) — SIMD details -- [Performance](https://github.com/HanSur94/FastPlot/wiki/Performance) — benchmarks +- [Getting Started](https://github.com/HanSur94/FastSense/wiki/Getting-Started) — tutorial with examples +- [API Reference: FastSense](https://github.com/HanSur94/FastSense/wiki/API-Reference:-FastSense) — core plotting class +- [API Reference: Dashboard](https://github.com/HanSur94/FastSense/wiki/API-Reference:-Dashboard) — layouts, widgets, engine +- [API Reference: Sensors](https://github.com/HanSur94/FastSense/wiki/API-Reference:-Sensors) — sensor system +- [API Reference: Event Detection](https://github.com/HanSur94/FastSense/wiki/API-Reference:-Event-Detection) — event pipeline +- [Architecture](https://github.com/HanSur94/FastSense/wiki/Architecture) — render pipeline, data flow +- [MEX Acceleration](https://github.com/HanSur94/FastSense/wiki/MEX-Acceleration) — SIMD details +- [Performance](https://github.com/HanSur94/FastSense/wiki/Performance) — benchmarks ## Examples -See the [`examples/`](examples/) directory for 40+ runnable scripts covering basic plotting, dashboards, sensors, event detection, live mode, and disk-backed storage. A categorized guide is in the [wiki](https://github.com/HanSur94/FastPlot/wiki/Examples). +See the [`examples/`](examples/) directory for 40+ runnable scripts covering basic plotting, dashboards, sensors, event detection, live mode, and disk-backed storage. A categorized guide is in the [wiki](https://github.com/HanSur94/FastSense/wiki/Examples). ## Libraries | Library | Path | Description | |---------|------|-------------| -| FastPlot | `libs/FastPlot/` | Core plotting engine, layouts, toolbar, themes, disk storage | +| FastSense | `libs/FastSense/` | Core plotting engine, layouts, toolbar, themes, disk storage | | SensorThreshold | `libs/SensorThreshold/` | Sensor containers, state channels, threshold rules | | EventDetection | `libs/EventDetection/` | Event detection, viewer, live pipeline, notifications | | Dashboard | `libs/Dashboard/` | Dashboard engine with widgets and JSON persistence | @@ -306,17 +306,17 @@ See the [`examples/`](examples/) directory for 40+ runnable scripts covering bas ## Contributing -Contributions are welcome! Please open an issue to discuss your idea before submitting a pull request. See the [wiki](https://github.com/HanSur94/FastPlot/wiki) for architecture details and API references. +Contributions are welcome! Please open an issue to discuss your idea before submitting a pull request. See the [wiki](https://github.com/HanSur94/FastSense/wiki) for architecture details and API references. ## Citation -If you use FastPlot in your research, please cite it: +If you use FastSense in your research, please cite it: ```bibtex -@software{fastplot, +@software{fastsense, author = {Suhr, Hannes}, - title = {FastPlot: Ultra-Fast Time Series Plotting for MATLAB and GNU Octave}, - url = {https://github.com/HanSur94/FastPlot}, + title = {FastSense: Ultra-Fast Time Series Plotting for MATLAB and GNU Octave}, + url = {https://github.com/HanSur94/FastSense}, license = {MIT} } ``` @@ -353,7 +353,7 @@ quick start, feature highlights, and links to detailed docs." Run: ```bash -gh repo edit HanSur94/FastPlot \ +gh repo edit HanSur94/FastSense \ --description "Ultra-fast time series plotting for MATLAB & Octave — 10M+ points at 200+ FPS with interactive zoom/pan" ``` @@ -361,7 +361,7 @@ gh repo edit HanSur94/FastPlot \ Run: ```bash -gh repo edit HanSur94/FastPlot \ +gh repo edit HanSur94/FastSense \ --add-topic matlab \ --add-topic octave \ --add-topic plotting \ @@ -375,7 +375,7 @@ gh repo edit HanSur94/FastPlot \ - [ ] **Step 3: Verify** -Run: `gh repo view HanSur94/FastPlot --json description,repositoryTopics` +Run: `gh repo view HanSur94/FastSense --json description,repositoryTopics` --- @@ -390,7 +390,7 @@ The wiki lives in `wiki/` as a separate git repo. All edits are made to files in - [ ] **Step 1: Update the libraries table** -Add the WebBridge row to the libraries table in `wiki/Home.md`. The current table lists 4 libraries (FastPlot, SensorThreshold, EventDetection, Dashboard) — add a 5th row: +Add the WebBridge row to the libraries table in `wiki/Home.md`. The current table lists 4 libraries (FastSense, SensorThreshold, EventDetection, Dashboard) — add a 5th row: ```markdown | WebBridge | `libs/WebBridge/` | TCP server for web-based visualization | @@ -437,7 +437,7 @@ Read the following files to extract current method signatures, properties, and c - `libs/Dashboard/TextWidget.m` - `libs/Dashboard/RawAxesWidget.m` - `libs/Dashboard/EventTimelineWidget.m` -- `libs/Dashboard/FastPlotWidget.m` +- `libs/Dashboard/FastSenseWidget.m` - `libs/Dashboard/DashboardLayout.m` - `libs/Dashboard/DashboardSerializer.m` - `libs/Dashboard/DashboardTheme.m` @@ -532,11 +532,11 @@ cd wiki && git add -A && git commit -m "docs: add IncrementalEventDetector, Data - Modify: `wiki/Architecture.md` - Modify: `wiki/Examples.md` - Modify: `wiki/_Sidebar.md` -- Verify (read-only): `wiki/Installation.md`, `wiki/Getting-Started.md`, `wiki/API-Reference:-FastPlot.md`, `wiki/API-Reference:-Themes.md`, `wiki/Live-Mode-Guide.md`, `wiki/Datetime-Guide.md`, `wiki/MEX-Acceleration.md`, `wiki/Performance.md`, `wiki/Use-Case:-Multi-Sensor-Shared-Threshold.md` +- Verify (read-only): `wiki/Installation.md`, `wiki/Getting-Started.md`, `wiki/API-Reference:-FastSense.md`, `wiki/API-Reference:-Themes.md`, `wiki/Live-Mode-Guide.md`, `wiki/Datetime-Guide.md`, `wiki/MEX-Acceleration.md`, `wiki/Performance.md`, `wiki/Use-Case:-Multi-Sensor-Shared-Threshold.md` - [ ] **Step 1: Update Utilities API reference** -Read `wiki/API-Reference:-Utilities.md` and `libs/FastPlot/ConsoleProgressBar.m`. Add documentation for hierarchical progress display features (nested bars, `addChild()`, etc.) if missing. +Read `wiki/API-Reference:-Utilities.md` and `libs/FastSense/ConsoleProgressBar.m`. Add documentation for hierarchical progress display features (nested bars, `addChild()`, etc.) if missing. - [ ] **Step 2: Update Architecture page** @@ -568,7 +568,7 @@ Add to `wiki/_Sidebar.md` under Guides: Read each of these pages and compare key details against current source code. Fix any discrepancies found: - `wiki/Installation.md` — verify requirements, setup steps - `wiki/Getting-Started.md` — verify example code runs -- `wiki/API-Reference:-FastPlot.md` — verify method signatures +- `wiki/API-Reference:-FastSense.md` — verify method signatures - `wiki/API-Reference:-Themes.md` — verify theme names and options - `wiki/Live-Mode-Guide.md` — verify live mode API - `wiki/Datetime-Guide.md` — verify datetime handling @@ -601,7 +601,7 @@ Read: - [ ] **Step 2: Write the guide** Create `wiki/Dashboard-Engine-Guide.md` covering: -- Overview of DashboardEngine vs FastPlotFigure (when to use which) +- Overview of DashboardEngine vs FastSenseFigure (when to use which) - Building dashboards with DashboardBuilder (fluent API walkthrough) - Widget types and their options (with small code examples) - Saving and loading dashboards (JSON serialization) @@ -646,9 +646,9 @@ Expected: A workflow run in progress or completed - [ ] **Step 3: Verify repo metadata** -Run: `gh repo view HanSur94/FastPlot --json description,repositoryTopics` +Run: `gh repo view HanSur94/FastSense --json description,repositoryTopics` Expected: Description and topics are set correctly - [ ] **Step 4: Verify README renders on GitHub** -Open `https://github.com/HanSur94/FastPlot` and confirm the new README looks correct with badges, image, and formatting. +Open `https://github.com/HanSur94/FastSense` and confirm the new README looks correct with badges, image, and formatting. diff --git a/docs/superpowers/plans/2026-03-16-dashboard-widget-rework.md b/docs/superpowers/plans/2026-03-16-dashboard-widget-rework.md index e00b0ca4..1809a1ad 100644 --- a/docs/superpowers/plans/2026-03-16-dashboard-widget-rework.md +++ b/docs/superpowers/plans/2026-03-16-dashboard-widget-rework.md @@ -4,7 +4,7 @@ **Goal:** Rework all dashboard widgets to use Sensor-first data binding, rename KpiWidget→NumberWidget, add 4 gauge styles, rework StatusWidget layout, and add Description tooltip to all widgets. -**Architecture:** Move `SensorObj` from FastPlotWidget to the `DashboardWidget` base class. Each widget derives its display (value, units, range, colors) from the bound Sensor and its ThresholdRules. Add `Description` property with info icon hover to base class. GaugeWidget gains 4 rendering styles. StatusWidget uses ThresholdRule.Color for dot color. +**Architecture:** Move `SensorObj` from FastSenseWidget to the `DashboardWidget` base class. Each widget derives its display (value, units, range, colors) from the bound Sensor and its ThresholdRules. Add `Description` property with info icon hover to base class. GaugeWidget gains 4 rendering styles. StatusWidget uses ThresholdRule.Color for dot color. **Tech Stack:** MATLAB R2020b, figure-based UI (uipanel, uicontrol, axes), no uifigure. @@ -33,7 +33,7 @@ end - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestSensor', 'ProcedureName', 'testUnitsProperty')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestSensor', 'ProcedureName', 'testUnitsProperty')"` Expected: FAIL — 'Units' is not a property - [ ] **Step 3: Add Units property to Sensor.m** @@ -48,7 +48,7 @@ And in the constructor, add support for the name-value pair (the existing loop ` - [ ] **Step 4: Run test to verify it passes** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestSensor', 'ProcedureName', 'testUnitsProperty')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestSensor', 'ProcedureName', 'testUnitsProperty')"` Expected: PASS - [ ] **Step 5: Commit** @@ -104,7 +104,7 @@ end - [ ] **Step 2: Run tests to verify they fail** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestDashboardWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestDashboardWidget')"` Expected: FAIL — Description and SensorObj not recognized - [ ] **Step 3: Implement base class changes** @@ -174,7 +174,7 @@ Add a `getTheme` utility method (currently duplicated in every widget — centra - [ ] **Step 4: Run tests to verify they pass** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestDashboardWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestDashboardWidget')"` Expected: PASS - [ ] **Step 5: Commit** @@ -203,7 +203,7 @@ Each file has a private method block at the end with only `getTheme` in it. Dele - [ ] **Step 2: Run all existing widget tests to verify nothing breaks** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestKpiWidget'); runtests('tests/suite/TestGaugeWidget'); runtests('tests/suite/TestStatusWidget'); runtests('tests/suite/TestFastPlotWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestKpiWidget'); runtests('tests/suite/TestGaugeWidget'); runtests('tests/suite/TestStatusWidget'); runtests('tests/suite/TestFastSenseWidget')"` Expected: All PASS - [ ] **Step 3: Commit** @@ -273,7 +273,7 @@ In `libs/Dashboard/DashboardSerializer.m`, at the switch block (lines 68-88), re - [ ] **Step 5: Run TestNumberWidget** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget')"` Expected: All PASS - [ ] **Step 6: Commit** @@ -325,7 +325,7 @@ end - [ ] **Step 2: Run tests to verify they fail** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget', 'ProcedureName', 'testSensorBinding')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget', 'ProcedureName', 'testSensorBinding')"` Expected: FAIL - [ ] **Step 3: Implement Sensor binding in NumberWidget** @@ -471,7 +471,7 @@ Update `fromStruct()` to handle sensor source: - [ ] **Step 4: Run all NumberWidget tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget')"` Expected: All PASS - [ ] **Step 5: Commit** @@ -530,7 +530,7 @@ end - [ ] **Step 2: Run tests to verify they fail** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestStatusWidget', 'ProcedureName', 'testSensorBindingNoViolation')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestStatusWidget', 'ProcedureName', 'testSensorBindingNoViolation')"` Expected: FAIL - [ ] **Step 3: Implement Sensor-bound StatusWidget** @@ -757,7 +757,7 @@ end - [ ] **Step 4: Run all StatusWidget tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestStatusWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestStatusWidget')"` Expected: All PASS - [ ] **Step 5: Commit** @@ -803,7 +803,7 @@ end - [ ] **Step 2: Run test to verify it fails** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget', 'ProcedureName', 'testSensorBinding')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget', 'ProcedureName', 'testSensorBinding')"` Expected: FAIL - [ ] **Step 3: Implement Sensor binding** @@ -928,7 +928,7 @@ Extract the arc rendering into `updateDisplay()` (refactored from existing refre - [ ] **Step 4: Run all GaugeWidget tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget')"` Expected: All PASS - [ ] **Step 5: Commit** @@ -994,7 +994,7 @@ end - [ ] **Step 2: Run tests to verify they fail** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget', 'ProcedureName', 'testDonutStyle')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget', 'ProcedureName', 'testDonutStyle')"` Expected: FAIL — Style property not recognized or donut rendering not implemented - [ ] **Step 3: Implement the render dispatcher and three new styles** @@ -1265,7 +1265,7 @@ Update `toStruct()` and `fromStruct()` to serialize `style`: - [ ] **Step 4: Run all GaugeWidget tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestGaugeWidget')"` Expected: All PASS - [ ] **Step 5: Commit** @@ -1279,18 +1279,18 @@ git commit -m "feat: add donut, bar, thermometer styles to GaugeWidget" ## Chunk 5: Remaining Widget Updates -### Task 9: Update FastPlotWidget to use base class SensorObj +### Task 9: Update FastSenseWidget to use base class SensorObj **Files:** -- Modify: `libs/Dashboard/FastPlotWidget.m` -- Test: `tests/suite/TestFastPlotWidget.m` +- Modify: `libs/Dashboard/FastSenseWidget.m` +- Test: `tests/suite/TestFastSenseWidget.m` -- [ ] **Step 1: Remove SensorObj property from FastPlotWidget** +- [ ] **Step 1: Remove SensorObj property from FastSenseWidget** -`SensorObj` is now on the base class. Remove it from `libs/Dashboard/FastPlotWidget.m` line 13. Add 'Sensor' shorthand mapping in the constructor. Update constructor to delegate to base class: +`SensorObj` is now on the base class. Remove it from `libs/Dashboard/FastSenseWidget.m` line 13. Add 'Sensor' shorthand mapping in the constructor. Update constructor to delegate to base class: ```matlab - function obj = FastPlotWidget(varargin) + function obj = FastSenseWidget(varargin) for k = 1:2:numel(varargin) if strcmp(varargin{k}, 'Sensor') varargin{k} = 'SensorObj'; @@ -1318,16 +1318,16 @@ git commit -m "feat: add donut, bar, thermometer styles to GaugeWidget" Remove the `SensorObj = []` from the public properties block (line 13). The rest of the class already references `obj.SensorObj` which will now resolve to the base class property. -- [ ] **Step 2: Run existing FastPlotWidget tests** +- [ ] **Step 2: Run existing FastSenseWidget tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestFastPlotWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestFastSenseWidget')"` Expected: All PASS - [ ] **Step 3: Commit** ```bash -git add libs/Dashboard/FastPlotWidget.m -git commit -m "refactor: FastPlotWidget uses base class SensorObj" +git add libs/Dashboard/FastSenseWidget.m +git commit -m "refactor: FastSenseWidget uses base class SensorObj" ``` --- @@ -1426,7 +1426,7 @@ Add `'Sensor'` shorthand in constructor. Update `refresh()`: - [ ] **Step 3: Run tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestTableWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestTableWidget')"` Expected: All PASS - [ ] **Step 4: Commit** @@ -1491,7 +1491,7 @@ Add `'Sensor'` shorthand mapping in constructor. - [ ] **Step 2: Run existing tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite')"` Expected: All PASS - [ ] **Step 3: Commit** @@ -1560,7 +1560,7 @@ This replaces the existing color selection block. When `ColorSource == 'theme'`, - [ ] **Step 3: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite')"` Expected: All PASS - [ ] **Step 4: Commit** @@ -1626,8 +1626,8 @@ Update `configToWidgets` signature to accept an optional resolver function handl for i = 1:numel(config.widgets) ws = config.widgets{i}; switch ws.type - case 'fastplot' - widgets{i} = FastPlotWidget.fromStruct(ws); + case 'fastsense' + widgets{i} = FastSenseWidget.fromStruct(ws); case {'number', 'kpi'} widgets{i} = NumberWidget.fromStruct(ws); case 'status' @@ -1662,7 +1662,7 @@ Update `configToWidgets` signature to accept an optional resolver function handl - [ ] **Step 3: Run all tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite')"` Expected: All PASS - [ ] **Step 4: Commit** @@ -1682,7 +1682,7 @@ git commit -m "feat: add SensorResolver to DashboardEngine.load()" - [ ] **Step 1: Verify NumberWidget works as replacement** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestNumberWidget')"` Expected: All PASS - [ ] **Step 2: Delete old files** @@ -1707,7 +1707,7 @@ Run grep to find all usages, then update each file. - [ ] **Step 2: Run full test suite** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite')"` Expected: All PASS - [ ] **Step 3: Commit** @@ -1730,7 +1730,7 @@ The palette sidebar creates buttons for each widget type. Update the 'kpi' butto - [ ] **Step 2: Run builder tests** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); runtests('tests/suite/TestDashboardBuilder')"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); runtests('tests/suite/TestDashboardBuilder')"` Expected: All PASS - [ ] **Step 3: Commit** @@ -1746,7 +1746,7 @@ git commit -m "chore: update DashboardBuilder palette for NumberWidget" - [ ] **Step 1: Run the full test suite** -Run: `cd /Users/hannessuhr/FastPlot && matlab -batch "setup(); results = runtests('tests/suite'); disp(table(results))"` +Run: `cd /Users/hannessuhr/FastSense && matlab -batch "setup(); results = runtests('tests/suite'); disp(table(results))"` Expected: All PASS, no warnings about deprecated 'kpi' in test code - [ ] **Step 2: Verify all changes are committed** diff --git a/docs/superpowers/specs/2026-03-11-sensor-detail-plot-design.md b/docs/superpowers/specs/2026-03-11-sensor-detail-plot-design.md index 1a3b2008..fc9bf2a4 100644 --- a/docs/superpowers/specs/2026-03-11-sensor-detail-plot-design.md +++ b/docs/superpowers/specs/2026-03-11-sensor-detail-plot-design.md @@ -6,21 +6,21 @@ A two-panel composite plot for sensor data. The upper panel shows a zoomable det ## Architecture -Two new classes in `libs/FastPlot/`: +Two new classes in `libs/FastSense/`: -- **`SensorDetailPlot.m`** — Coordinator. Creates two `FastPlot` instances (main + navigator), wires bidirectional zoom synchronization, manages event rendering. +- **`SensorDetailPlot.m`** — Coordinator. Creates two `FastSense` instances (main + navigator), wires bidirectional zoom synchronization, manages event rendering. - **`NavigatorOverlay.m`** — Handles the zoom rectangle, dimming patches, and drag interaction on the navigator axes. ## Layout ``` ┌──────────────────────────────────────┐ -│ Main Plot (80%) FastPlot │ +│ Main Plot (80%) FastSense │ │ - Sensor data line │ │ - Threshold lines + violations │ │ - Event shading (optional) │ ├──────────────────────────────────────┤ -│ Navigator (20%) FastPlot │ +│ Navigator (20%) FastSense │ │ - Full data range line │ │ - Threshold bands (subtle fills) │ │ - Event vertical lines (optional) │ @@ -46,7 +46,7 @@ sdp = SensorDetailPlot(sensor, Name, Value, ...) | Name | Type | Default | Description | |------|------|---------|-------------| -| `Theme` | string or struct | `'default'` | FastPlot theme preset or custom struct | +| `Theme` | string or struct | `'default'` | FastSense theme preset or custom struct | | `NavigatorHeight` | double (0–1) | `0.20` | Fraction of total height for navigator | | `ShowThresholds` | logical | `true` | Show threshold lines + violations in main plot | | `ShowThresholdBands` | logical | `true` | Show threshold bands in navigator | @@ -64,13 +64,13 @@ sdp.setZoomRange(xMin, xMax) % Programmatically set visible range sdp.delete() % Clean up listeners, callbacks, handles ``` -Calling `render()` twice throws an error (same guard as `FastPlot`). +Calling `render()` twice throws an error (same guard as `FastSense`). ### Properties (read-only) ```matlab -sdp.MainPlot % FastPlot instance for the upper panel -sdp.NavigatorPlot % FastPlot instance for the lower panel +sdp.MainPlot % FastSense instance for the upper panel +sdp.NavigatorPlot % FastSense instance for the lower panel ``` `MainPlot` is exposed so users can add extra elements (markers, bands, etc.) to the detail view. @@ -102,8 +102,8 @@ sdp.render(); sdp = SensorDetailPlot(s, 'Events', store, 'Theme', 'dark'); sdp.render(); -% Inside FastPlotFigure (uses new tilePanel method) -fig = FastPlotFigure(2, 1, 'Theme', 'dark'); +% Inside FastSenseFigure (uses new tilePanel method) +fig = FastSenseFigure(2, 1, 'Theme', 'dark'); sdp = SensorDetailPlot(s, 'Parent', fig.tilePanel(1), 'Events', store); sdp.render(); fig.renderAll(); @@ -114,7 +114,7 @@ fig.renderAll(); ### Sensor Data + Thresholds - When `SensorDetailPlot.ShowThresholds = true`, calls `MainPlot.addSensor(sensor, 'ShowThresholds', true)` which renders the data line, threshold lines, and violation markers. When `false`, calls `addSensor(sensor, 'ShowThresholds', false)` (data line only, no thresholds). -- Inherits all existing FastPlot behavior: downsampling on zoom, pyramid caching, NaN gap handling. +- Inherits all existing FastSense behavior: downsampling on zoom, pyramid caching, NaN gap handling. ### Event Shading @@ -128,14 +128,14 @@ When `Events` is provided: - `Direction = 'low'` — cool colors (blue, alpha 0.12) - Two-tier escalation: if `ThresholdLabel` contains `'HH'` or `'LL'` (case-insensitive), use stronger color (red / dark blue, alpha 0.15). This matches the `escalateTo()` convention where escalated events get a new `ThresholdLabel`. - Fallback — theme accent color, alpha 0.10 -5. All Event statistics are attached to the patch `UserData` struct: `ThresholdLabel`, `Direction`, `Duration`, `PeakValue`, `MeanValue`, `MinValue`, `MaxValue`, `RmsValue`, `StdValue`, `NumPoints`. This makes all event data available to `FastPlotToolbar` cursor/crosshair. +5. All Event statistics are attached to the patch `UserData` struct: `ThresholdLabel`, `Direction`, `Duration`, `PeakValue`, `MeanValue`, `MinValue`, `MaxValue`, `RmsValue`, `StdValue`, `NumPoints`. This makes all event data available to `FastSenseToolbar` cursor/crosshair. 6. No permanent text labels on the plot. ## Lower Plot (Navigator) ### Full Data Range -- Renders the sensor data line using `FastPlot.addLine()` across the full time range. +- Renders the sensor data line using `FastSense.addLine()` across the full time range. - Navigator axes XLim is fixed to `[min(sensor.X), max(sensor.X)]` and does not change. - Navigator axes YLim is computed from `[min(sensor.Y), max(sensor.Y)]` with 5% padding, set after all elements are drawn, then fixed. This prevents threshold bands and dim patches from affecting the Y scale. - MATLAB's built-in zoom and pan tools are disabled on the navigator axes (`zoom(hNavAxes, 'off')`, `pan(hNavAxes, 'off')`) to preserve the full-range invariant. @@ -201,7 +201,7 @@ overlay.setRange(xMin, xMax); % Programmatic update ``` User zooms/pans in main plot - → FastPlot XLim PostSet listener fires + → FastSense XLim PostSet listener fires → SensorDetailPlot receives new XLim → Calls NavigatorOverlay.setRange(xMin, xMax) → Overlay updates rectangle + dim patches @@ -210,48 +210,48 @@ User drags in navigator → NavigatorOverlay fires OnRangeChanged(xMin, xMax) → SensorDetailPlot receives callback → Sets MainPlot axes XLim - → FastPlot zoom listener fires, re-downsamples visible data + → FastSense zoom listener fires, re-downsamples visible data ``` A guard flag (`IsPropagating`) prevents infinite callback loops (main→navigator→main→...). -**Design note:** This intentionally does not use FastPlot's `LinkGroup` mechanism. `LinkGroup` propagates XLim changes between peer FastPlot instances that all re-downsample on zoom. The navigator must not re-downsample or change its XLim — it always shows the full range. The bespoke guard flag approach is simpler and avoids `resetplotview` / `XLimMode='auto'` side effects that `LinkGroup` handles for peer plots but that would break navigator invariants. +**Design note:** This intentionally does not use FastSense's `LinkGroup` mechanism. `LinkGroup` propagates XLim changes between peer FastSense instances that all re-downsample on zoom. The navigator must not re-downsample or change its XLim — it always shows the full range. The bespoke guard flag approach is simpler and avoids `resetplotview` / `XLimMode='auto'` side effects that `LinkGroup` handles for peer plots but that would break navigator invariants. ## Cleanup -Both classes implement `delete()` methods following the `onCleanup`/`DeleteFcn` pattern used in `FastPlotFigure`: +Both classes implement `delete()` methods following the `onCleanup`/`DeleteFcn` pattern used in `FastSenseFigure`: - **`SensorDetailPlot.delete()`** — Removes the XLim PostSet listener on the main axes. Calls `NavigatorOverlay.delete()`. If the figure was self-created (no `Parent`), sets `CloseRequestFcn` to trigger cleanup on figure close. - **`NavigatorOverlay.delete()`** — Removes `WindowButtonDownFcn`, `WindowButtonMotionFcn`, `WindowButtonUpFcn` callbacks. Removes `SizeChangedFcn` listener. Deletes graphics handles (`hRegion`, `hDimLeft`, `hDimRight`, `hEdgeLeft`, `hEdgeRight`). This prevents dead listeners and stale figure callbacks when plots are closed and re-opened in the same MATLAB session. -## Integration with FastPlotFigure +## Integration with FastSenseFigure When `Parent` is provided: -- `SensorDetailPlot` accepts a `uipanel` handle as `Parent`. This handle is NOT passed through to `FastPlot`'s `'Parent'` option. -- Instead, `SensorDetailPlot` creates two sub-panels inside it, then creates axes within each sub-panel, and passes those axes to the internal `FastPlot` instances via `FastPlot('Parent', hAxes)`. +- `SensorDetailPlot` accepts a `uipanel` handle as `Parent`. This handle is NOT passed through to `FastSense`'s `'Parent'` option. +- Instead, `SensorDetailPlot` creates two sub-panels inside it, then creates axes within each sub-panel, and passes those axes to the internal `FastSense` instances via `FastSense('Parent', hAxes)`. - Does not create its own figure window. -**New method required on `FastPlotFigure`:** `tilePanel(n)` — returns a `uipanel` handle at the computed position for tile `n`. This is distinct from the existing `tile(n)` (returns a `FastPlot`) and `axes(n)` (returns a raw axes). The `tilePanel(n)` method creates an empty `uipanel` at the tile's grid position, allowing composite widgets like `SensorDetailPlot` to manage their own internal layout within a dashboard tile. Calling `tilePanel(n)` on a tile already occupied by `tile(n)` or `axes(n)` throws a `tileConflict` error, same as the existing `tile(n)` vs `axes(n)` mutual exclusion guard. +**New method required on `FastSenseFigure`:** `tilePanel(n)` — returns a `uipanel` handle at the computed position for tile `n`. This is distinct from the existing `tile(n)` (returns a `FastSense`) and `axes(n)` (returns a raw axes). The `tilePanel(n)` method creates an empty `uipanel` at the tile's grid position, allowing composite widgets like `SensorDetailPlot` to manage their own internal layout within a dashboard tile. Calling `tilePanel(n)` on a tile already occupied by `tile(n)` or `axes(n)` throws a `tileConflict` error, same as the existing `tile(n)` vs `axes(n)` mutual exclusion guard. -**Axes creation in Parent path:** `SensorDetailPlot` is responsible for creating the axes within each sub-panel via `axes('Parent', subPanel)`, then passing those axes handles to the internal `FastPlot` instances via `FastPlot('Parent', hAxes)`. +**Axes creation in Parent path:** `SensorDetailPlot` is responsible for creating the axes within each sub-panel via `axes('Parent', subPanel)`, then passing those axes handles to the internal `FastSense` instances via `FastSense('Parent', hAxes)`. ## File Locations | File | Location | |------|----------| -| `SensorDetailPlot.m` | `libs/FastPlot/SensorDetailPlot.m` | -| `NavigatorOverlay.m` | `libs/FastPlot/NavigatorOverlay.m` | +| `SensorDetailPlot.m` | `libs/FastSense/SensorDetailPlot.m` | +| `NavigatorOverlay.m` | `libs/FastSense/NavigatorOverlay.m` | | `example_sensor_detail.m` | `examples/example_sensor_detail.m` | | `test_SensorDetailPlot.m` | `tests/test_SensorDetailPlot.m` | | `test_NavigatorOverlay.m` | `tests/test_NavigatorOverlay.m` | ## Dependencies -- `FastPlot` — rendering engine for both panels +- `FastSense` — rendering engine for both panels - `Sensor`, `ThresholdRule`, `StateChannel` — sensor data model - `EventStore`, `Event` — event data (optional) -- `FastPlotFigure` — for dashboard embedding (optional) -- `FastPlotTheme` — theme system +- `FastSenseFigure` — for dashboard embedding (optional) +- `FastSenseTheme` — theme system diff --git a/docs/superpowers/specs/2026-03-13-web-bridge-design.md b/docs/superpowers/specs/2026-03-13-web-bridge-design.md index 76b37ac4..89ee9072 100644 --- a/docs/superpowers/specs/2026-03-13-web-bridge-design.md +++ b/docs/superpowers/specs/2026-03-13-web-bridge-design.md @@ -5,7 +5,7 @@ ## Overview -A bidirectional communication layer that makes FastPlot data, metadata, and dashboard state accessible to web frontends in real-time while MATLAB is running. MATLAB runs a minimal TCP server; a separate bridge process (Python or Node.js) serves a REST API, WebSocket, and web UI. +A bidirectional communication layer that makes FastSense data, metadata, and dashboard state accessible to web frontends in real-time while MATLAB is running. MATLAB runs a minimal TCP server; a separate bridge process (Python or Node.js) serves a REST API, WebSocket, and web UI. ## Goals @@ -138,10 +138,10 @@ A standalone process (Python or Node) connecting to MATLAB's TCP server. ```bash # Launched automatically by MATLAB's .serve() via system() # Or manually: -fastplot-bridge --matlab-port 5555 +fastsense-bridge --matlab-port 5555 # Node -npx fastplot-bridge --matlab-port 5555 +npx fastsense-bridge --matlab-port 5555 ``` ### Components @@ -210,7 +210,7 @@ For the data endpoint, X/Y chunks are always `mxDOUBLE` (class_id=6). Extra colu ### Server-Side Downsampling -The data endpoint accepts an optional `maxPoints` query parameter (default: 4000). When the requested range contains more points than `maxPoints`, the bridge applies minmax downsampling (keep min/max per bucket) server-side before returning JSON. This matches FastPlot's existing downsampling strategy and keeps browser payloads manageable. +The data endpoint accepts an optional `maxPoints` query parameter (default: 4000). When the requested range contains more points than `maxPoints`, the bridge applies minmax downsampling (keep min/max per bucket) server-side before returning JSON. This matches FastSense's existing downsampling strategy and keeps browser payloads manageable. ### Signal Identity @@ -225,7 +225,7 @@ Vanilla HTML/JS/CSS served by the bridge. 1. **Chart Viewer** — uPlot library for fast rendering of large datasets. Zoom/pan triggers data re-fetch via REST API with `maxPoints` parameter. Threshold lines and violation markers overlaid. 2. **Dashboard Layout** — Reads config from `/api/dashboard`, renders a CSS grid of widgets: - - FastPlotWidget → Chart Viewer (uPlot) + - FastSenseWidget → Chart Viewer (uPlot) - KpiWidget → Big number display - StatusWidget → Color-coded badge - TableWidget → HTML table @@ -259,7 +259,7 @@ libs/ bridge/ python/ - fastplot_bridge/ + fastsense_bridge/ __init__.py server.py — FastAPI app (REST + WebSocket) tcp_client.py — MATLAB TCP connection @@ -303,7 +303,7 @@ bridge/ 7. Bridge sends `{"type":"bridge_ready","httpPort":8080}` to MATLAB via TCP 8. MATLAB prints: `Dashboard served at http://localhost:` -**Blocking behavior:** `.serve()` blocks until `bridge_ready` is received or a 10-second timeout fires. If the timeout fires, WebBridge throws: `'Bridge did not start within 10s. Check that fastplot-bridge is installed.'` Actions can be registered before or after `.serve()` — the action list is sent as part of `init` and refreshed via `config_changed` when new actions are added. +**Blocking behavior:** `.serve()` blocks until `bridge_ready` is received or a 10-second timeout fires. If the timeout fires, WebBridge throws: `'Bridge did not start within 10s. Check that fastsense-bridge is installed.'` Actions can be registered before or after `.serve()` — the action list is sent as part of `init` and refreshed via `config_changed` when new actions are added. **Config change detection:** A MATLAB timer polls every 1 second, comparing a hash of the serialized dashboard config. Configurable via `WebBridge('ConfigPollInterval', N)`. @@ -345,7 +345,7 @@ MATLAB is single-threaded. Action callbacks execute on the MATLAB event queue (l ## SQLite Configuration for Concurrent Access -Each `FastPlotDataStore` gains two methods: `enableWAL()` and `disableWAL()`. These run on the DataStore's own mksqlite connection (ensuring no connection ownership conflicts): +Each `FastSenseDataStore` gains two methods: `enableWAL()` and `disableWAL()`. These run on the DataStore's own mksqlite connection (ensuring no connection ownership conflicts): **`enableWAL()`** — called by WebBridge on `.serve()`: ```sql diff --git a/docs/superpowers/specs/2026-03-16-ci-readme-wiki-design.md b/docs/superpowers/specs/2026-03-16-ci-readme-wiki-design.md index 1faf40b7..b6b3c789 100644 --- a/docs/superpowers/specs/2026-03-16-ci-readme-wiki-design.md +++ b/docs/superpowers/specs/2026-03-16-ci-readme-wiki-design.md @@ -60,7 +60,7 @@ Add GitHub Actions CI/CD pipelines (test + release), replace the 43KB reference- 1. **Gate:** Run Octave tests (same as test pipeline Octave job) 2. **Package:** Create archive containing: - - `libs/` (all 5 libraries: FastPlot, SensorThreshold, EventDetection, Dashboard, WebBridge) + - `libs/` (all 5 libraries: FastSense, SensorThreshold, EventDetection, Dashboard, WebBridge) - MEX C source files included, compiled `.mex*` binaries **excluded** (users compile via `setup.m`) - Must explicitly filter `*.mexmaca64`, `*.mexmaci64`, `*.mexa64`, `*.mexw64`, `*.mex` since some are tracked in git despite `.gitignore` - `setup.m` @@ -72,18 +72,18 @@ Add GitHub Actions CI/CD pipelines (test + release), replace the 43KB reference- 4. **Release:** Create GitHub Release via `softprops/action-gh-release@v2` - Title: tag name (e.g., `v1.5.0`) - Body: auto-generated changelog - - Assets: `FastPlot-v1.5.0.zip` and `FastPlot-v1.5.0.tar.gz` (version includes `v` prefix) + - Assets: `FastSense-v1.5.0.zip` and `FastSense-v1.5.0.tar.gz` (version includes `v` prefix) ### Archive Structure ``` -FastPlot-v1.5.0/ +FastSense-v1.5.0/ ├── setup.m ├── LICENSE ├── README.md ├── CITATION.cff ├── libs/ -│ ├── FastPlot/ +│ ├── FastSense/ │ ├── SensorThreshold/ │ ├── EventDetection/ │ ├── Dashboard/ @@ -103,7 +103,7 @@ Replace the current 43KB README with a concise (~150-200 lines) overview. ### Structure -1. **Title + tagline** — "FastPlot — Ultra-fast time series plotting for MATLAB & Octave" +1. **Title + tagline** — "FastSense — Ultra-fast time series plotting for MATLAB & Octave" 2. **Badges** — CI status (pointing to `tests.yml` on `main`), license (MIT), MATLAB R2020b+, Octave 7+ 3. **One-paragraph description** — what it does, key performance claim 4. **Screenshot** — existing `docs/images/` hero image @@ -151,7 +151,7 @@ Update all 17 wiki pages to reflect the current project state. | Home.md | Add Dashboard Engine v2, WebBridge, NumberWidget, new widget types | | Installation.md | Verify requirements still accurate | | Getting-Started.md | Ensure examples use current API | -| API-Reference: FastPlot.md | Verify method signatures match current code | +| API-Reference: FastSense.md | Verify method signatures match current code | | API-Reference: Dashboard.md | Add DashboardEngine, DashboardBuilder, new widgets (Gauge, Number, Status, Table, Text, RawAxes, EventTimeline) | | API-Reference: Sensors.md | Add SensorRegistry, verify ThresholdRule API | | API-Reference: Event-Detection.md | Add IncrementalEventDetector, DataSourceMap, NotificationRule updates | diff --git a/docs/superpowers/specs/2026-03-16-dashboard-widget-rework-design.md b/docs/superpowers/specs/2026-03-16-dashboard-widget-rework-design.md index 603ac238..1572b6dc 100644 --- a/docs/superpowers/specs/2026-03-16-dashboard-widget-rework-design.md +++ b/docs/superpowers/specs/2026-03-16-dashboard-widget-rework-design.md @@ -28,7 +28,7 @@ All widgets inherit from `DashboardWidget`. The following properties are added o |---|---|---|---| | `Title` | char | `Sensor.Name` or `''` | Display title. Defaults to Sensor name when bound. | | `Description` | char | `''` | Optional tooltip text, shown via info icon hover on the widget header. | -| `SensorObj` | Sensor | `[]` | Primary data binding. Moved from FastPlotWidget to base class. | +| `SensorObj` | Sensor | `[]` | Primary data binding. Moved from FastSenseWidget to base class. | | `Position` | 1x4 double | widget-specific | `[col, row, width, height]` in grid units (unchanged). | | `ThemeOverride` | struct | `struct()` | Per-widget theme overrides (unchanged). | | `UseGlobalTime` | logical | `true` | Follow global time slider (unchanged). | @@ -45,7 +45,7 @@ When `Description` is non-empty, the widget header renders a small `(i)` icon ne ## Widget Specifications -### 1. FastPlotWidget +### 1. FastSenseWidget **Binding:** Single Sensor (primary), DataStore, File, or inline XData/YData (fallbacks). @@ -54,7 +54,7 @@ When `Description` is non-empty, the widget header renders a small `(i)` icon ne - ThresholdRules auto-resolve — violation markers and bands render automatically - XLabel defaults to `'Time'`, YLabel defaults to `Sensor.Units` -**Unchanged behavior:** Creates a `FastPlot` instance inside its panel. Full zoom/pan/downsample support. `setTimeRange()` updates xlim when `UseGlobalTime` is true. User zoom sets `UseGlobalTime = false`. +**Unchanged behavior:** Creates a `FastSense` instance inside its panel. Full zoom/pan/downsample support. `setTimeRange()` updates xlim when `UseGlobalTime` is true. User zoom sets `UseGlobalTime = false`. **Default size:** 12 cols x 3 rows. @@ -223,7 +223,7 @@ The `DashboardEngine.addWidget()` method signature remains the same but all widg ```matlab % Sensor-first (recommended for all sensor-bound widgets) -d.addWidget('fastplot', 'Sensor', sTemp, 'Position', [1 1 12 3]); +d.addWidget('fastsense', 'Sensor', sTemp, 'Position', [1 1 12 3]); d.addWidget('number', 'Sensor', sTemp, 'Position', [13 1 6 1]); d.addWidget('gauge', 'Sensor', sPressure, 'Style', 'donut', 'Position', [13 2 6 2]); d.addWidget('status', 'Sensor', sTemp, 'Position', [19 1 6 1]); @@ -238,7 +238,7 @@ d.addWidget('text', 'Title', 'Section A', 'Content', 'Overview', 'Position', [1 ``` **Type string mapping:** -- `'fastplot'` → FastPlotWidget +- `'fastsense'` → FastSenseWidget - `'number'` → NumberWidget (was `'kpi'`) - `'gauge'` → GaugeWidget - `'status'` → StatusWidget @@ -306,7 +306,7 @@ The existing live timer architecture is unchanged. On each tick: - GaugeWidget: re-evaluates `Sensor.Y(end)`, updates needle/fill - StatusWidget: re-checks current violations, updates dot color - TableWidget: re-queries last N data points or events - - FastPlotWidget: re-renders with latest Sensor data + - FastSenseWidget: re-renders with latest Sensor data - RawAxesWidget: clears and re-calls PlotFcn with updated Sensor - EventTimelineWidget: re-queries EventStore - TextWidget: no-op (static) @@ -322,7 +322,7 @@ The existing live timer architecture is unchanged. On each tick: - `KpiWidget.m` → `NumberWidget.m` ### Moved Properties -- `SensorObj` moves from `FastPlotWidget` to `DashboardWidget` base class +- `SensorObj` moves from `FastSenseWidget` to `DashboardWidget` base class ### API Changes - `DashboardEngine.load(filepath)` gains an optional name-value parameter: `'SensorResolver'`, a function handle `@(key) -> Sensor`. Existing single-argument calls continue to work (default resolver attempts `SensorRegistry.get(key)` if available, otherwise leaves widget unbound). diff --git a/examples/demo_all.m b/examples/demo_all.m index d659a1ed..2eaf788c 100644 --- a/examples/demo_all.m +++ b/examples/demo_all.m @@ -1,25 +1,25 @@ -%% FastPlot Interactive Demo +%% FastSense Interactive Demo % Opens all example plots and keeps them alive for interactive exploration. % Zoom and pan on any figure as long as you want. % Press Enter in the command window to close all figures and exit. % % Usage: % From MATLAB/Octave command window: -% cd FastPlot/examples +% cd FastSense/examples % demo_all % % From terminal (Octave — requires GUI for interactive zoom/pan): -% cd FastPlot -% octave --gui --eval "run('setup.m'); addpath('libs/FastPlot/private'); addpath('examples'); demo_all;" +% cd FastSense +% octave --gui --eval "run('setup.m'); addpath('libs/FastSense/private'); addpath('examples'); demo_all;" projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); -addpath(fullfile(projectRoot, 'libs', 'FastPlot', 'private')); +addpath(fullfile(projectRoot, 'libs', 'FastSense', 'private')); addpath(fileparts(mfilename('fullpath'))); fprintf('\n'); fprintf(' =============================================\n'); -fprintf(' FastPlot Interactive Demo\n'); +fprintf(' FastSense Interactive Demo\n'); fprintf(' =============================================\n'); fprintf(' Opening all example plots...\n\n'); diff --git a/examples/example_100M.m b/examples/example_100M.m index 3ce6de65..3be5aaca 100644 --- a/examples/example_100M.m +++ b/examples/example_100M.m @@ -1,4 +1,4 @@ -%% FastPlot Stress Test — 100M points +%% FastSense Stress Test — 100M points % Demonstrates performance at maximum scale projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -14,11 +14,11 @@ fprintf('Rendering...\n'); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', '100M Random Walk', 'Color', [0 0.4 0.8]); fp.addThreshold(3, 'Direction', 'upper', 'ShowViolations', true); fp.addThreshold(-3, 'Direction', 'lower', 'ShowViolations', true); fp.render(); fprintf('Rendered in %.3f seconds. Zoom in to see detail!\n', toc); -title(fp.hAxes, 'FastPlot — 100M Points Stress Test'); +title(fp.hAxes, 'FastSense — 100M Points Stress Test'); diff --git a/examples/example_alarm_bands.m b/examples/example_alarm_bands.m index 1663098a..bfef3cf8 100644 --- a/examples/example_alarm_bands.m +++ b/examples/example_alarm_bands.m @@ -1,4 +1,4 @@ -%% FastPlot Alarm Bands — Industrial sensor with 4 thresholds +%% FastSense Alarm Bands — Industrial sensor with 4 thresholds % Demonstrates warning + alarm thresholds in both directions projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -17,7 +17,7 @@ fprintf('Alarm Bands: %d points, 4 thresholds...\n', n); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Process Variable', 'Color', [0.2 0.4 0.7]); % Alarm limits (red, dashed) — hard limits diff --git a/examples/example_basic.m b/examples/example_basic.m index 83fd8c7e..2a08c31f 100644 --- a/examples/example_basic.m +++ b/examples/example_basic.m @@ -1,4 +1,4 @@ -%% FastPlot Basic Example — 10M points single line +%% FastSense Basic Example — 10M points single line % Demonstrates basic usage with a large time series projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -8,10 +8,10 @@ x = linspace(0, 100, n); y = sin(x * 2 * pi / 10) + 0.5 * randn(1, n); -fprintf('Creating FastPlot with %d points...\n', n); +fprintf('Creating FastSense with %d points...\n', n); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Noisy Sine', 'Color', [0 0.4470 0.7410]); % Alarm thresholds (red, dashed) @@ -29,5 +29,5 @@ fp.render(); fprintf('Rendered in %.3f seconds. Try zooming and panning!\n', toc); -title(fp.hAxes, 'FastPlot — 10M Points'); +title(fp.hAxes, 'FastSense — 10M Points'); legend(fp.hAxes, 'show'); diff --git a/examples/example_dashboard.m b/examples/example_dashboard.m index c63d915b..b9c77045 100644 --- a/examples/example_dashboard.m +++ b/examples/example_dashboard.m @@ -1,14 +1,14 @@ -%% FastPlot Dashboard — Tiled layout with themes and visual enhancements -% Demonstrates FastPlotGrid, theming, bands, shading, and markers. +%% FastSense Dashboard — Tiled layout with themes and visual enhancements +% Demonstrates FastSenseGrid, theming, bands, shading, and markers. % % Choosing your dashboard approach: -% FastPlotGrid — Lightweight tiled grid of FastPlot instances. +% FastSenseGrid — Lightweight tiled grid of FastSense instances. % Best for: pure time-series dashboards with linked zoom, tile % spanning, and SensorDetailPlot embedding. No widget types beyond % plots. See also: example_dashboard_9tile, example_sensor_dashboard. % % DashboardEngine — Full widget-based dashboard with toolbar, edit mode, -% JSON save/load, and 8 widget types (fastplot, number, status, +% JSON save/load, and 8 widget types (fastsense, number, status, % gauge, text, table, rawaxes, timeline). Uses a 24-column grid % with Position = [col row width height]. % See also: example_dashboard_engine, example_dashboard_all_widgets. @@ -24,8 +24,8 @@ fprintf('Dashboard example: 4 tiles, %d points each, dark theme...\n', n); tic; -fig = FastPlotGrid(2, 2, 'Theme', 'light', ... - 'Name', 'FastPlot Dashboard Demo', 'Position', [50 50 1400 800]); +fig = FastSenseGrid(2, 2, 'Theme', 'light', ... + 'Name', 'FastSense Dashboard Demo', 'Position', [50 50 1400 800]); % --- Tile 1: Temperature with alarm bands (spans 2 columns) --- fig.setTileSpan(1, [1 2]); diff --git a/examples/example_dashboard_9tile.m b/examples/example_dashboard_9tile.m index 1b223b9a..0345d178 100644 --- a/examples/example_dashboard_9tile.m +++ b/examples/example_dashboard_9tile.m @@ -1,4 +1,4 @@ -%% FastPlot 3x3 Dashboard — Industrial Monitoring Console +%% FastSense 3x3 Dashboard — Industrial Monitoring Console % 9 tiles with different signals, data sizes, and features. close all force; @@ -9,7 +9,7 @@ fprintf('3x3 Dashboard: 9 tiles, mixed data sizes, dark theme...\n'); tic; -fig = FastPlotGrid(3, 3, 'Theme', 'light', ... +fig = FastSenseGrid(3, 3, 'Theme', 'light', ... 'Name', 'Industrial Monitoring Console', 'Position', [30 30 1800 1000]); % ========================================================================= diff --git a/examples/example_dashboard_all_widgets.m b/examples/example_dashboard_all_widgets.m index 450b1662..5455aaa3 100644 --- a/examples/example_dashboard_all_widgets.m +++ b/examples/example_dashboard_all_widgets.m @@ -2,7 +2,7 @@ % Demonstrates every widget type in a single dashboard, wired to Sensor % objects with dynamic thresholds via StateChannels. % -% FastPlot widgets: bound to Sensors with resolved thresholds +% FastSense widgets: bound to Sensors with resolved thresholds % Number/Gauge/Status: driven by sensor data (latest value / threshold check) % Text/Table: static display (no interactivity needed) % Timeline: derived from threshold violations @@ -212,17 +212,17 @@ 'Position', [20 1 5 2], ... 'Sensor', sPress); -% --- Row 3-10: Sensor-driven FastPlot widgets with thresholds --- -d.addWidget('fastplot', ... +% --- Row 3-10: Sensor-driven FastSense widgets with thresholds --- +d.addWidget('fastsense', ... 'Position', [1 3 12 8], ... 'Sensor', sTemp); -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [13 3 12 8], ... 'Sensor', sPress); % --- Row 11-18: Flow plot + Table + Histogram + Gauge --- -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [1 11 12 8], ... 'Sensor', sFlow); diff --git a/examples/example_dashboard_engine.m b/examples/example_dashboard_engine.m index dc23fad0..6b6b4c56 100644 --- a/examples/example_dashboard_engine.m +++ b/examples/example_dashboard_engine.m @@ -1,5 +1,5 @@ %% Dashboard Engine Example — Sensor-Driven -% Demonstrates: DashboardEngine with FastPlotWidgets bound to Sensors, +% Demonstrates: DashboardEngine with FastSenseWidgets bound to Sensors, % dynamic thresholds via StateChannels, JSON save/load. % % DashboardEngine uses a 24-column grid. Widget positions are specified as @@ -53,15 +53,15 @@ d.Theme = 'light'; d.LiveInterval = 5; -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [1 1 16 8], ... 'Sensor', sTemp); -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [17 1 8 8], ... 'Sensor', sPress); -d.addWidget('fastplot', 'Title', 'Temperature (full view)', ... +d.addWidget('fastsense', 'Title', 'Temperature (full view)', ... 'Position', [1 9 24 8], ... 'Sensor', sTemp); diff --git a/examples/example_dashboard_live.m b/examples/example_dashboard_live.m index 3f6aa2e6..7a1f9636 100644 --- a/examples/example_dashboard_live.m +++ b/examples/example_dashboard_live.m @@ -6,7 +6,7 @@ % text — static header % number — sensor-bound big number with trend arrow % status — sensor-bound colored indicator (auto ok/warning/alarm) -% fastplot — sensor-bound plots with thresholds + violation markers +% fastsense — sensor-bound plots with thresholds + violation markers % gauge — sensor-bound arc gauge % rawaxes — live histogram via PlotFcn % table — alarm log driven by DataFcn @@ -124,17 +124,17 @@ function example_dashboard_live_run() 'Position', [20 1 5 2], ... 'Sensor', sPress); - % --- Row 3-10: Sensor-bound FastPlot widgets (thresholds + violations) --- - d.addWidget('fastplot', ... + % --- Row 3-10: Sensor-bound FastSense widgets (thresholds + violations) --- + d.addWidget('fastsense', ... 'Position', [1 3 12 8], ... 'Sensor', sTemp); - d.addWidget('fastplot', ... + d.addWidget('fastsense', ... 'Position', [13 3 12 8], ... 'Sensor', sPress); % --- Row 11-18: Flow + Gauge + Histogram --- - d.addWidget('fastplot', ... + d.addWidget('fastsense', ... 'Position', [1 11 12 8], ... 'Sensor', sFlow); diff --git a/examples/example_datetime.m b/examples/example_datetime.m index c9d69180..44754bdf 100644 --- a/examples/example_datetime.m +++ b/examples/example_datetime.m @@ -1,4 +1,4 @@ -%% FastPlot Datetime X-Axis Demo +%% FastSense Datetime X-Axis Demo % Demonstrates auto-formatted date/time tick labels that adapt to zoom level. projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -16,27 +16,27 @@ %% Plot 1: With datetime X axis + toolbar tic; -fp1 = FastPlot('Theme', 'light'); +fp1 = FastSense('Theme', 'light'); fp1.addLine(x, y, 'DisplayName', 'Temperature', 'XType', 'datenum'); fp1.addThreshold(24, 'Direction', 'upper', 'ShowViolations', true); fp1.render(); title(fp1.hAxes, 'With datenum + toolbar'); -tb1 = FastPlotToolbar(fp1); +tb1 = FastSenseToolbar(fp1); fprintf('Datetime + toolbar rendered in %.3f seconds.\n', toc); %% Plot 2: Plain numeric X axis + toolbar tic; -fp2 = FastPlot('Theme', 'light'); +fp2 = FastSense('Theme', 'light'); fp2.addLine(x, y, 'DisplayName', 'Temperature'); fp2.addThreshold(24, 'Direction', 'upper', 'ShowViolations', true); fp2.render(); title(fp2.hAxes, 'Without datenum + toolbar'); -tb2 = FastPlotToolbar(fp2); +tb2 = FastSenseToolbar(fp2); fprintf('Numeric + toolbar rendered in %.3f seconds.\n', toc); %% Plot 3: With datetime X axis, no toolbar tic; -fp3 = FastPlot('Theme', 'light'); +fp3 = FastSense('Theme', 'light'); fp3.addLine(x, y, 'DisplayName', 'Temperature', 'XType', 'datenum'); fp3.addThreshold(24, 'Direction', 'upper', 'ShowViolations', true); fp3.render(); @@ -45,7 +45,7 @@ %% Plot 4: Plain numeric X axis, no toolbar tic; -fp4 = FastPlot('Theme', 'light'); +fp4 = FastSense('Theme', 'light'); fp4.addLine(x, y, 'DisplayName', 'Temperature'); fp4.addThreshold(24, 'Direction', 'upper', 'ShowViolations', true); fp4.render(); diff --git a/examples/example_disk_storage.m b/examples/example_disk_storage.m index 066933a6..7096b97d 100644 --- a/examples/example_disk_storage.m +++ b/examples/example_disk_storage.m @@ -1,4 +1,4 @@ -%% FastPlot Disk Storage Example — SQLite-backed large datasets +%% FastSense Disk Storage Example — SQLite-backed large datasets % Demonstrates how to use disk-backed storage to plot datasets that % exceed available RAM. Data is stored in a temporary SQLite database % and only the visible slice is loaded into memory on zoom/pan. @@ -7,7 +7,7 @@ run(fullfile(projectRoot, 'setup.m')); %% 1. Automatic disk offload (default behaviour) -% In 'auto' mode (the default), FastPlot stores data on disk when it +% In 'auto' mode (the default), FastSense stores data on disk when it % exceeds MemoryLimit (default 500 MB = ~31M double-precision points). fprintf('=== Auto mode: small data stays in memory ===\n'); @@ -15,7 +15,7 @@ x = linspace(0, 100, n_small); y = sin(x) + 0.3 * randn(1, n_small); -fp1 = FastPlot(); % StorageMode='auto', MemoryLimit=500e6 +fp1 = FastSense(); % StorageMode='auto', MemoryLimit=500e6 fp1.addLine(x, y, 'DisplayName', '1M pts (in memory)'); fp1.render(); title(fp1.hAxes, 'Auto Mode — 1M Points (in memory)'); @@ -29,7 +29,7 @@ y = sin(x / 20) .* cos(x / 7) + 0.2 * randn(1, n_medium); tic; -fp2 = FastPlot('StorageMode', 'disk'); +fp2 = FastSense('StorageMode', 'disk'); fp2.addLine(x, y, 'DisplayName', '5M pts (disk)', 'Color', [0.8 0.2 0.1]); fp2.addThreshold(1.0, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', 'r', 'LineStyle', '--', 'Label', 'Upper Limit'); @@ -46,7 +46,7 @@ x = linspace(0, 200, n_mid); y = cumsum(randn(1, n_mid)) / sqrt(n_mid); -fp3 = FastPlot('MemoryLimit', 10e6); % 10 MB threshold +fp3 = FastSense('MemoryLimit', 10e6); % 10 MB threshold fp3.addLine(x, y, 'DisplayName', '2M pts (auto-offloaded)', ... 'Color', [0.1 0.6 0.3]); fp3.render(); @@ -56,14 +56,14 @@ clear x y; %% 4. Direct DataStore usage for advanced workflows -fprintf('\n=== Direct FastPlotDataStore API ===\n'); +fprintf('\n=== Direct FastSenseDataStore API ===\n'); n_large = 10e6; x = linspace(0, 1000, n_large); y = sin(x / 50) + 0.1 * randn(1, n_large); fprintf(' Creating DataStore with %dM points...\n', n_large / 1e6); tic; -ds = FastPlotDataStore(x, y); +ds = FastSenseDataStore(x, y); fprintf(' Created in %.3f s\n', toc); clear x y; @@ -103,7 +103,7 @@ end tic; -fp5 = FastPlot('StorageMode', 'disk'); +fp5 = FastSense('StorageMode', 'disk'); fp5.addLine(x, y, 'DisplayName', '50M pts (disk)', 'Color', [0.2 0.3 0.8]); fp5.render(); tRender = toc; diff --git a/examples/example_dock.m b/examples/example_dock.m index 120fd8e7..deb1c28f 100644 --- a/examples/example_dock.m +++ b/examples/example_dock.m @@ -1,6 +1,6 @@ -%% FastPlotDock — Tabbed Dashboard Example +%% FastSenseDock — Tabbed Dashboard Example % Five dashboards docked in a single window with tab switching. -% All tabs use datetime X axes (auto-detected by FastPlot). +% All tabs use datetime X axes (auto-detected by FastSense). projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); @@ -12,7 +12,7 @@ fprintf('Docked Tabs: 5 dashboards in 1 window...\n'); tic; -dock = FastPlotDock('Theme', 'light', 'Name', 'Control Room', ... +dock = FastSenseDock('Theme', 'light', 'Name', 'Control Room', ... 'Position', [50 50 1400 800]); % Common time base: 1 hour of data starting now @@ -25,7 +25,7 @@ % ========================================================================= % Tab 1: Temperature Monitoring (2x2) % ========================================================================= -fig1 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig1 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); fp = fig1.tile(1); n = 2e6; t = linspace(t0, t1, n); s = sec(t); @@ -60,7 +60,7 @@ % ========================================================================= % Tab 2: Power Systems (1x2) % ========================================================================= -fig2 = FastPlotGrid(1, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig2 = FastSenseGrid(1, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); fp = fig2.tile(1); n = 3e6; t = linspace(t0, t1, n); s = sec(t); @@ -78,7 +78,7 @@ % ========================================================================= % Tab 3: Hydraulics (2x1) % ========================================================================= -fig3 = FastPlotGrid(2, 1, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig3 = FastSenseGrid(2, 1, 'ParentFigure', dock.hFigure, 'Theme', 'light'); fp = fig3.tile(1); n = 1.5e6; t = linspace(t0, t1, n); s = sec(t); @@ -99,7 +99,7 @@ % ========================================================================= % Tab 4: Electrical Grid (1x3) % ========================================================================= -fig4 = FastPlotGrid(1, 3, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig4 = FastSenseGrid(1, 3, 'ParentFigure', dock.hFigure, 'Theme', 'light'); fp = fig4.tile(1); n = 2e6; t = linspace(t0, t1, n); s = sec(t); @@ -124,7 +124,7 @@ % ========================================================================= % Tab 5: Environment (2x2) — 24h span % ========================================================================= -fig5 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig5 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); te0 = datetime(2026, 3, 8, 0, 0, 0); te1 = datetime(2026, 3, 8, 23, 59, 59); diff --git a/examples/example_dock_disk.m b/examples/example_dock_disk.m index a067e897..6482a268 100644 --- a/examples/example_dock_disk.m +++ b/examples/example_dock_disk.m @@ -1,4 +1,4 @@ -%% FastPlotDock — Disk-Backed Dashboard with Dynamic Thresholds +%% FastSenseDock — Disk-Backed Dashboard with Dynamic Thresholds % % Five dashboards in a tabbed dock window, all using disk-backed storage. % Each sensor has state-dependent (dynamic) thresholds that change with @@ -22,7 +22,7 @@ fprintf('Generating ~100M data points across 5 tabs (all disk-backed)...\n\n'); tic; -dock = FastPlotDock('Theme', 'dark', 'Name', 'Plant Control Room — Disk Mode', ... +dock = FastSenseDock('Theme', 'dark', 'Name', 'Plant Control Room — Disk Mode', ... 'Position', [50 50 1500 900]); % ---------- Shared time base: 8h shift ---------- @@ -35,7 +35,7 @@ % Tab 1: Turbine Monitoring (2x2) — ~28M points % ========================================================================= fprintf('Tab 1: Turbine Monitoring...\n'); -fig1 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); +fig1 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); % Turbine operates in 3 modes over the shift scTurbine = StateChannel('turbine'); @@ -162,7 +162,7 @@ % Tab 2: Chemical Process (2x2) — ~22M points % ========================================================================= fprintf('Tab 2: Chemical Process...\n'); -fig2 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); +fig2 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); % Reactor modes: batch phases scReactor = StateChannel('phase'); @@ -286,7 +286,7 @@ % Tab 3: Compressor Station (1x3) — ~18M points % ========================================================================= fprintf('Tab 3: Compressor Station...\n'); -fig3 = FastPlotGrid(1, 3, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); +fig3 = FastSenseGrid(1, 3, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); % Compressor modes scComp = StateChannel('stage'); @@ -386,7 +386,7 @@ % Tab 4: Power Generation (2x2) — ~24M points % ========================================================================= fprintf('Tab 4: Power Generation...\n'); -fig4 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); +fig4 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); % Generator modes scGen = StateChannel('gen'); @@ -499,7 +499,7 @@ % Tab 5: Environmental (1x3) — ~11M points % ========================================================================= fprintf('Tab 5: Environmental...\n'); -fig5 = FastPlotGrid(1, 3, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); +fig5 = FastSenseGrid(1, 3, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); % HVAC modes scHvac = StateChannel('hvac'); diff --git a/examples/example_dock_many_tabs.m b/examples/example_dock_many_tabs.m index 5ba0a878..8db97471 100644 --- a/examples/example_dock_many_tabs.m +++ b/examples/example_dock_many_tabs.m @@ -1,4 +1,4 @@ -%% FastPlotDock — Many Tabs (Scroll Arrows) Example +%% FastSenseDock — Many Tabs (Scroll Arrows) Example % 20 tabs in a single dock window to exercise the scrollable tab bar. % Each tab gets a simple 1x1 figure with a sine wave variant. @@ -11,7 +11,7 @@ fprintf('Docked Tabs: 20 tabs — testing scrollable tab bar...\n'); tic; -dock = FastPlotDock('Theme', 'dark', 'Name', 'Many Tabs Demo', ... +dock = FastSenseDock('Theme', 'dark', 'Name', 'Many Tabs Demo', ... 'Position', [50 50 1400 800]); % Common time base @@ -31,7 +31,7 @@ s = sec(t); for k = 1:20 - fig = FastPlotGrid(1, 1, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); + fig = FastSenseGrid(1, 1, 'ParentFigure', dock.hFigure, 'Theme', 'dark'); fp = fig.tile(1); % Vary the signal per tab diff --git a/examples/example_dynamic_thresholds_100M.m b/examples/example_dynamic_thresholds_100M.m index 46e5fb90..eb9713f5 100644 --- a/examples/example_dynamic_thresholds_100M.m +++ b/examples/example_dynamic_thresholds_100M.m @@ -173,8 +173,8 @@ fprintf('========================================\n'); %% Plot first sensor as demo -fprintf('\nPlotting first sensor with FastPlot...\n'); -fp = FastPlot(); +fprintf('\nPlotting first sensor with FastSense...\n'); +fp = FastSense(); fp.addSensor(sensors{1}, 'ShowThresholds', true); fp.render(); title(fp.hAxes, sprintf('%s — 100M pts, 6 Dynamic Thresholds', ... diff --git a/examples/example_ecg.m b/examples/example_ecg.m index ab31e0f3..fb540f54 100644 --- a/examples/example_ecg.m +++ b/examples/example_ecg.m @@ -1,4 +1,4 @@ -%% FastPlot ECG — Simulated heart rhythm with arrhythmia detection +%% FastSense ECG — Simulated heart rhythm with arrhythmia detection % Demonstrates high sample rate biomedical data with tight thresholds projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -44,7 +44,7 @@ fprintf('ECG: %d points at %d Hz (%.0f min recording)...\n', n, fs, n/fs/60); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'ECG Lead II', 'Color', [0 0.5 0]); % Arrhythmia detection thresholds diff --git a/examples/example_event_detection_live.m b/examples/example_event_detection_live.m index ce176c28..028716d7 100644 --- a/examples/example_event_detection_live.m +++ b/examples/example_event_detection_live.m @@ -2,7 +2,7 @@ function example_event_detection_live() %EXAMPLE_EVENT_DETECTION_LIVE Live event detection demo with industrial sensors. % Demonstrates the EventDetection library with 3 mock industrial sensors, % threshold-based event detection, console logging, EventViewer UI, -% and a live FastPlot dashboard using startLive for real-time plotting. +% and a live FastSense dashboard using startLive for real-time plotting. % % Run: example_event_detection_live() % Stop: Close the Event Viewer or Live Plot figure to stop. @@ -79,7 +79,7 @@ function example_event_detection_live() liveN = N; % --- 6. Write initial .mat files for live plotting --- - liveDir = fullfile(tempdir, 'fastplot_event_live'); + liveDir = fullfile(tempdir, 'fastsense_event_live'); if ~exist(liveDir, 'dir'); mkdir(liveDir); end tempFile = fullfile(liveDir, 'temperature.mat'); @@ -90,13 +90,13 @@ function example_event_detection_live() x = t; y = pressure; save(presFile, 'x', 'y'); x = t; y = vibration; save(vibFile, 'x', 'y'); - % --- 7. Open live FastPlot dashboard --- + % --- 7. Open live FastSense dashboard --- hPlotFig = figure('Name', 'Live Sensor Dashboard', ... 'NumberTitle', 'off', 'Position', [150 50 1200 700]); % Temperature plot ax1 = subplot(3,1,1, 'Parent', hPlotFig); - fpTemp = FastPlot('Parent', ax1, 'LinkGroup', 'live_demo'); + fpTemp = FastSense('Parent', ax1, 'LinkGroup', 'live_demo'); fpTemp.addLine(t, temp, 'DisplayName', 'Temperature', 'Color', [0.8 0.2 0.1]); fpTemp.addThreshold(85, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [1 0.8 0], 'LineStyle', '--', 'Label', 'temp warning'); @@ -108,7 +108,7 @@ function example_event_detection_live() % Pressure plot ax2 = subplot(3,1,2, 'Parent', hPlotFig); - fpPres = FastPlot('Parent', ax2, 'LinkGroup', 'live_demo'); + fpPres = FastSense('Parent', ax2, 'LinkGroup', 'live_demo'); fpPres.addLine(t, pressure, 'DisplayName', 'Pressure', 'Color', [0.2 0.5 1]); fpPres.addThreshold(4, 'Direction', 'lower', 'ShowViolations', true, ... 'Color', [0.2 0.5 1], 'LineStyle', '--', 'Label', 'pressure low'); @@ -118,7 +118,7 @@ function example_event_detection_live() % Vibration plot ax3 = subplot(3,1,3, 'Parent', hPlotFig); - fpVib = FastPlot('Parent', ax3, 'LinkGroup', 'live_demo'); + fpVib = FastSense('Parent', ax3, 'LinkGroup', 'live_demo'); fpVib.addLine(t, vibration, 'DisplayName', 'Vibration', 'Color', [0.8 0.3 0.8]); fpVib.addThreshold(5, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [0.8 0.3 0.8], 'LineStyle', '--', 'Label', 'vibration high'); @@ -127,7 +127,7 @@ function example_event_detection_live() ylabel(ax3, 'mm/s'); xlabel(ax3, 'Time (s)'); - % --- 8. Start FastPlot live mode (polls .mat files, auto-scrolls) --- + % --- 8. Start FastSense live mode (polls .mat files, auto-scrolls) --- fpTemp.startLive(tempFile, @(fp, d) fp.updateData(1, d.x, d.y), ... 'Interval', 2, 'ViewMode', 'follow'); fpPres.startLive(presFile, @(fp, d) fp.updateData(1, d.x, d.y), ... @@ -187,7 +187,7 @@ function generateData() liveCfg.SensorData(i).y = s.Y; end - % Write updated data to .mat files — FastPlot startLive picks them up + % Write updated data to .mat files — FastSense startLive picks them up sT = liveCfg.Sensors{1}; x = sT.X; y = sT.Y; save(tempFile, 'x', 'y'); sP = liveCfg.Sensors{2}; x = sP.X; y = sP.Y; save(presFile, 'x', 'y'); sV = liveCfg.Sensors{3}; x = sV.X; y = sV.Y; save(vibFile, 'x', 'y'); @@ -211,7 +211,7 @@ function stopAll() stop(dataTimer); delete(dataTimer); end - % Stop FastPlot live timers + % Stop FastSense live timers try fpTemp.stopLive(); catch; end try fpPres.stopLive(); catch; end try fpVib.stopLive(); catch; end diff --git a/examples/example_linked.m b/examples/example_linked.m index 0f7607b4..6c26af29 100644 --- a/examples/example_linked.m +++ b/examples/example_linked.m @@ -1,4 +1,4 @@ -%% FastPlot Linked Axes Example +%% FastSense Linked Axes Example % Demonstrates synchronized zoom/pan across subplots projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -7,11 +7,11 @@ n = 5e6; x = linspace(0, 100, n); -fig = figure('Name', 'FastPlot Linked Axes', 'Position', [100 100 1200 600]); +fig = figure('Name', 'FastSense Linked Axes', 'Position', [100 100 1200 600]); % Top plot ax1 = subplot(3,1,1, 'Parent', fig); -fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'sync'); +fp1 = FastSense('Parent', ax1, 'LinkGroup', 'sync'); fp1.addLine(x, sin(x * 2 * pi / 5) + 0.2*randn(1,n), ... 'DisplayName', 'Pressure', 'Color', 'b'); fp1.addThreshold(1.2, 'Direction', 'upper', 'ShowViolations', true); @@ -20,7 +20,7 @@ % Middle plot ax2 = subplot(3,1,2, 'Parent', fig); -fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'sync'); +fp2 = FastSense('Parent', ax2, 'LinkGroup', 'sync'); fp2.addLine(x, cos(x * 2 * pi / 8) + 0.3*randn(1,n), ... 'DisplayName', 'Temperature', 'Color', [0.8 0.3 0]); fp2.render(); @@ -28,7 +28,7 @@ % Bottom plot ax3 = subplot(3,1,3, 'Parent', fig); -fp3 = FastPlot('Parent', ax3, 'LinkGroup', 'sync'); +fp3 = FastSense('Parent', ax3, 'LinkGroup', 'sync'); fp3.addLine(x, cumsum(randn(1,n))/sqrt(n), ... 'DisplayName', 'Vibration', 'Color', [0 0.6 0]); fp3.render(); diff --git a/examples/example_live_pipeline.m b/examples/example_live_pipeline.m index 23a38fd8..d5bbf2ec 100644 --- a/examples/example_live_pipeline.m +++ b/examples/example_live_pipeline.m @@ -139,7 +139,7 @@ % 3. EVENT STORE — atomic write with backup rotation % ======================================================================== -storeFile = fullfile(tempdir, 'fastplot_live_events.mat'); +storeFile = fullfile(tempdir, 'fastsense_live_events.mat'); fprintf('Event store: %s\n', storeFile); %% ======================================================================== @@ -157,7 +157,7 @@ % 5. NOTIFICATIONS — rule-based with priority matching and snapshots % ======================================================================== -snapshotDir = fullfile(tempdir, 'fastplot_snapshots'); +snapshotDir = fullfile(tempdir, 'fastsense_snapshots'); fprintf('Snapshot directory: %s\n', snapshotDir); notif = NotificationService('DryRun', true, 'SnapshotDir', snapshotDir); @@ -165,7 +165,7 @@ % Default rule: catches all events not matched by specific rules (score=1) notif.setDefaultRule(NotificationRule( ... 'Recipients', {{'ops-team@company.com'}}, ... - 'Subject', '[FastPlot] {sensor}: {threshold} violation', ... + 'Subject', '[FastSense] {sensor}: {threshold} violation', ... 'Message', ['Sensor {sensor} violated {threshold} ({direction}) ' ... 'from {startTime} to {endTime}.\n' ... 'Peak: {peak}, Mean: {mean}, Std: {std}, Duration: {duration}'], ... diff --git a/examples/example_lttb_vs_minmax.m b/examples/example_lttb_vs_minmax.m index 35c00cfb..d41eb927 100644 --- a/examples/example_lttb_vs_minmax.m +++ b/examples/example_lttb_vs_minmax.m @@ -1,4 +1,4 @@ -%% FastPlot LTTB vs MinMax — Compare downsampling methods side by side +%% FastSense LTTB vs MinMax — Compare downsampling methods side by side % Shows visual difference between MinMax (preserves extremes) and LTTB (preserves shape) projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -15,7 +15,7 @@ % Top: MinMax (default) ax1 = subplot(2,1,1, 'Parent', fig); -fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'compare'); +fp1 = FastSense('Parent', ax1, 'LinkGroup', 'compare'); fp1.addLine(x, y, 'DisplayName', 'MinMax', 'Color', [0 0.45 0.74], ... 'DownsampleMethod', 'minmax'); fp1.addThreshold(0.8, 'Direction', 'upper', 'ShowViolations', true, ... @@ -25,7 +25,7 @@ % Bottom: LTTB ax2 = subplot(2,1,2, 'Parent', fig); -fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'compare'); +fp2 = FastSense('Parent', ax2, 'LinkGroup', 'compare'); fp2.addLine(x, y, 'DisplayName', 'LTTB', 'Color', [0.85 0.33 0.1], ... 'DownsampleMethod', 'lttb'); fp2.addThreshold(0.8, 'Direction', 'upper', 'ShowViolations', true, ... diff --git a/examples/example_mixed_tiles.m b/examples/example_mixed_tiles.m index abee432a..8bf3d0c9 100644 --- a/examples/example_mixed_tiles.m +++ b/examples/example_mixed_tiles.m @@ -1,6 +1,6 @@ -%% Mixed Tile Types — FastPlot + raw MATLAB axes in one dashboard +%% Mixed Tile Types — FastSense + raw MATLAB axes in one dashboard % Demonstrates using fig.axes(n) alongside fig.tile(n) for plot types -% that FastPlot doesn't handle (bar, scatter, histogram, stem, etc.) +% that FastSense doesn't handle (bar, scatter, histogram, stem, etc.) projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); @@ -8,13 +8,13 @@ n = 500000; x = linspace(0, 120, n); -fprintf('Mixed tile dashboard: 2x3 grid, FastPlot + raw axes...\n'); +fprintf('Mixed tile dashboard: 2x3 grid, FastSense + raw axes...\n'); tic; -fig = FastPlotGrid(2, 3, 'Theme', 'light', ... +fig = FastSenseGrid(2, 3, 'Theme', 'light', ... 'Name', 'Mixed Tile Types Demo', 'Position', [50 50 1500 800]); -% --- Tile 1: FastPlot time series (temperature) --- +% --- Tile 1: FastSense time series (temperature) --- fp1 = fig.tile(1); y_temp = 70 + 10*sin(x*2*pi/30) + 2*randn(1,n); fp1.addLine(x, y_temp, 'DisplayName', 'Temperature'); @@ -37,7 +37,7 @@ colormap(ax3, 'parula'); grid(ax3, 'on'); -% --- Tile 4: FastPlot time series (pressure, spans 2 cols) --- +% --- Tile 4: FastSense time series (pressure, spans 2 cols) --- fig.setTileSpan(4, [1 2]); fp4 = fig.tile(4); y_press = 100 + 15*sin(x*2*pi/60) + 3*randn(1,n); @@ -55,7 +55,7 @@ % Render all tiles fig.renderAll(); -% Apply labels (works on both FastPlot and raw axes tiles) +% Apply labels (works on both FastSense and raw axes tiles) fig.setTileTitle(1, 'Temperature (C)'); fig.setTileXLabel(1, 'Time (s)'); fig.setTileTitle(2, 'Alarm Events / Day'); diff --git a/examples/example_multi.m b/examples/example_multi.m index 01945e16..cc358132 100644 --- a/examples/example_multi.m +++ b/examples/example_multi.m @@ -1,4 +1,4 @@ -%% FastPlot Multi-Line Example — 5 sensors, 1M points each +%% FastSense Multi-Line Example — 5 sensors, 1M points each % Demonstrates multiple lines with thresholds projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -10,7 +10,7 @@ fprintf('Creating 5 lines x %d points = %d total...\n', n, 5*n); tic; -fp = FastPlot(); +fp = FastSense(); colors = [0 0.447 0.741; 0.85 0.325 0.098; 0.929 0.694 0.125; 0.494 0.184 0.556; 0.466 0.674 0.188]; for i = 1:5 y = sin(x * 2 * pi * i / 10) + 0.3 * randn(1, n) + i * 2; @@ -22,5 +22,5 @@ fp.render(); fprintf('Rendered in %.3f seconds.\n', toc); -title(fp.hAxes, 'FastPlot — 5 Lines x 1M Points'); +title(fp.hAxes, 'FastSense — 5 Lines x 1M Points'); legend(fp.hAxes, 'show'); diff --git a/examples/example_multi_sensor_linked.m b/examples/example_multi_sensor_linked.m index 7cdc26e9..7efa0f5a 100644 --- a/examples/example_multi_sensor_linked.m +++ b/examples/example_multi_sensor_linked.m @@ -1,4 +1,4 @@ -%% FastPlot Multi-Sensor Linked — 4 sensors with independent thresholds, synchronized zoom +%% FastSense Multi-Sensor Linked — 4 sensors with independent thresholds, synchronized zoom % Simulates a real monitoring dashboard with different alarm levels per channel projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -15,7 +15,7 @@ % --- Channel 1: Temperature --- ax1 = subplot(4,1,1, 'Parent', fig); y_temp = 75 + 8*sin(x*2*pi/120) + 2*randn(1,n); -fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'dashboard'); +fp1 = FastSense('Parent', ax1, 'LinkGroup', 'dashboard'); fp1.addLine(x, y_temp, 'DisplayName', 'Temperature', 'Color', [0.8 0.2 0.1]); fp1.addThreshold(90, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [0.8 0 0], 'LineStyle', '--', 'Label', 'HH'); @@ -31,7 +31,7 @@ % --- Channel 2: Pressure --- ax2 = subplot(4,1,2, 'Parent', fig); y_press = 100 + 15*sin(x*2*pi/200) + 5*randn(1,n); -fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'dashboard'); +fp2 = FastSense('Parent', ax2, 'LinkGroup', 'dashboard'); fp2.addLine(x, y_press, 'DisplayName', 'Pressure', 'Color', [0.1 0.4 0.8]); fp2.addThreshold(130, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [0.8 0 0], 'LineStyle', '--', 'Label', 'HH'); @@ -50,7 +50,7 @@ % Add a ramp-up event ramp_idx = round(n*0.6):round(n*0.65); y_flow(ramp_idx) = y_flow(ramp_idx) + linspace(0, 25, numel(ramp_idx)); -fp3 = FastPlot('Parent', ax3, 'LinkGroup', 'dashboard'); +fp3 = FastSense('Parent', ax3, 'LinkGroup', 'dashboard'); fp3.addLine(x, y_flow, 'DisplayName', 'Flow Rate', 'Color', [0.2 0.6 0.2]); fp3.addThreshold(75, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [0.8 0 0], 'LineStyle', '--'); @@ -69,7 +69,7 @@ idx = round(t_fault*n/600):min(round((t_fault+5)*n/600), n); y_vib(idx) = y_vib(idx) + 3*randn(1, numel(idx)); end -fp4 = FastPlot('Parent', ax4, 'LinkGroup', 'dashboard'); +fp4 = FastSense('Parent', ax4, 'LinkGroup', 'dashboard'); fp4.addLine(x, y_vib, 'DisplayName', 'Vibration', 'Color', [0.5 0.2 0.6]); fp4.addThreshold(4.0, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [0.8 0 0], 'LineStyle', '--', 'Label', 'Danger'); diff --git a/examples/example_nan_gaps.m b/examples/example_nan_gaps.m index f8881a99..4411c57c 100644 --- a/examples/example_nan_gaps.m +++ b/examples/example_nan_gaps.m @@ -1,5 +1,5 @@ -%% FastPlot NaN Gaps — Sensor dropout simulation -% Demonstrates how FastPlot handles missing data (NaN gaps) +%% FastSense NaN Gaps — Sensor dropout simulation +% Demonstrates how FastSense handles missing data (NaN gaps) projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); @@ -19,7 +19,7 @@ fprintf('NaN Gaps: %d points with %d dropout regions...\n', n, numel(gap_starts)); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Sensor (with dropouts)', 'Color', [0 0.5 0.3]); fp.addThreshold(1.0, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [0.9 0 0], 'LineStyle', '--', 'Label', 'Upper Limit'); diff --git a/examples/example_sensor_dashboard.m b/examples/example_sensor_dashboard.m index ce5dcaba..b8fdaa95 100644 --- a/examples/example_sensor_dashboard.m +++ b/examples/example_sensor_dashboard.m @@ -1,6 +1,6 @@ -%% Multi-Sensor Dashboard — FastPlotGrid + SensorRegistry +%% Multi-Sensor Dashboard — FastSenseGrid + SensorRegistry % Demonstrates: -% - FastPlotGrid tiled layout (2x2 grid) +% - FastSenseGrid tiled layout (2x2 grid) % - SensorRegistry for predefined sensor definitions % - Multiple sensors each with different threshold configurations % - Tile titles, labels, and per-tile theming @@ -90,9 +90,9 @@ s4.resolve(); % ======================================================== -% Build 2x2 dashboard with FastPlotGrid +% Build 2x2 dashboard with FastSenseGrid % ======================================================== -fig = FastPlotGrid(2, 2, 'Name', 'Sensor Dashboard'); +fig = FastSenseGrid(2, 2, 'Name', 'Sensor Dashboard'); % Tile 1: Pressure fp1 = fig.tile(1); diff --git a/examples/example_sensor_detail.m b/examples/example_sensor_detail.m index 4b0324f6..84df2686 100644 --- a/examples/example_sensor_detail.m +++ b/examples/example_sensor_detail.m @@ -3,7 +3,7 @@ % Demonstrates: % 1. Standalone sensor detail plot with thresholds % 2. Adding events from EventStore -% 3. Embedding in a FastPlotGrid tile +% 3. Embedding in a FastSenseGrid tile %% Setup path projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -83,20 +83,20 @@ fprintf(' Press any key to continue...\n'); pause; -%% 5. Embedded in FastPlotGrid -fprintf('=== SensorDetailPlot: Embedded in FastPlotGrid ===\n'); -fig = FastPlotGrid(1, 2, 'Theme', 'light', 'Name', 'Sensor Dashboard'); +%% 5. Embedded in FastSenseGrid +fprintf('=== SensorDetailPlot: Embedded in FastSenseGrid ===\n'); +fig = FastSenseGrid(1, 2, 'Theme', 'light', 'Name', 'Sensor Dashboard'); sdp3 = SensorDetailPlot(s, 'Parent', fig.tilePanel(1), ... 'Events', events, 'Title', 'Temperature'); sdp3.render(); -% Second tile: plain FastPlot for comparison +% Second tile: plain FastSense for comparison fp = fig.tile(2); fp.addLine(t, data, 'DisplayName', 'Raw Data'); fig.setTileTitle(2, 'Raw Data'); fig.renderAll(); -fprintf(' Two tiles: SensorDetailPlot + plain FastPlot\n'); +fprintf(' Two tiles: SensorDetailPlot + plain FastSense\n'); fprintf(' Press any key to exit...\n'); pause; diff --git a/examples/example_sensor_detail_dashboard.m b/examples/example_sensor_detail_dashboard.m index 5fa28171..37e9f8e5 100644 --- a/examples/example_sensor_detail_dashboard.m +++ b/examples/example_sensor_detail_dashboard.m @@ -1,5 +1,5 @@ %% Multi-Sensor Detail Dashboard -% Demonstrates embedding multiple SensorDetailPlots into a FastPlotGrid +% Demonstrates embedding multiple SensorDetailPlots into a FastSenseGrid % grid using tilePanel(), each with independent navigators. close all force; @@ -52,8 +52,8 @@ s3.Units = 'mm/s'; s3.X = t3; s3.Y = d3; -%% Build 2x2 dashboard: 3 SensorDetailPlots + 1 plain FastPlot -fig = FastPlotGrid(2, 2, 'Theme', 'light', 'Name', 'Multi-Sensor Dashboard'); +%% Build 2x2 dashboard: 3 SensorDetailPlots + 1 plain FastSense +fig = FastSenseGrid(2, 2, 'Theme', 'light', 'Name', 'Multi-Sensor Dashboard'); % Tile 1: Temperature with events sdp1 = SensorDetailPlot(s1, 'Parent', fig.tilePanel(1), ... @@ -71,12 +71,12 @@ 'NavigatorHeight', 0.15, 'Title', 'Motor Vibration'); sdp3.render(); -% Tile 4: Plain FastPlot comparison overlay +% Tile 4: Plain FastSense comparison overlay fp = fig.tile(4); fp.addLine(t1, (d1 - mean(d1))/std(d1), 'DisplayName', 'Temp (z-score)'); fp.addLine(t2, (d2 - mean(d2))/std(d2), 'DisplayName', 'Pressure (z-score)'); fig.setTileTitle(4, 'Normalized Overlay'); fig.renderAll(); -fprintf('Multi-sensor dashboard with 3 SensorDetailPlots + 1 FastPlot.\n'); +fprintf('Multi-sensor dashboard with 3 SensorDetailPlots + 1 FastSense.\n'); fprintf('Each navigator operates independently.\n'); diff --git a/examples/example_sensor_detail_dock.m b/examples/example_sensor_detail_dock.m index 4e496e10..f209a27c 100644 --- a/examples/example_sensor_detail_dock.m +++ b/examples/example_sensor_detail_dock.m @@ -1,12 +1,12 @@ %% SensorDetailPlot Dock — Multi-Tab Dashboard % -% Demonstrates a FastPlotDock with 4 tabs, each containing multiple plots. -% Tabs mix SensorDetailPlots (with navigators) and plain FastPlot tiles. +% Demonstrates a FastSenseDock with 4 tabs, each containing multiple plots. +% Tabs mix SensorDetailPlots (with navigators) and plain FastSense tiles. % % Tab 1: Process Overview — 4 SensorDetailPlots (2x2 grid) -% Tab 2: Correlation — 2 SensorDetailPlots + 2 plain FastPlots +% Tab 2: Correlation — 2 SensorDetailPlots + 2 plain FastSenses % Tab 3: Event Analysis — 1 large SensorDetailPlot + event details -% Tab 4: Trends — 6 plain FastPlots showing z-score overlays +% Tab 4: Trends — 6 plain FastSenses showing z-score overlays projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); @@ -78,11 +78,11 @@ allEvents = [ev1a, ev1b, ev2, ev3]; %% ===== Create Dock ===== -dock = FastPlotDock('Theme', 'light', 'Name', 'Sensor Detail Dashboard', ... +dock = FastSenseDock('Theme', 'light', 'Name', 'Sensor Detail Dashboard', ... 'Position', [50 50 1400 800]); %% ===== Tab 1: Process Overview (2x2 SensorDetailPlots) ===== -fig1 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig1 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); sensors = {s1, s2, s3, s4}; titles = {'Furnace Temperature', 'Chamber Pressure', ... @@ -100,7 +100,7 @@ dock.addTab(fig1, 'Process Overview'); %% ===== Tab 2: Correlation (2 SensorDetailPlots + 2 plain) ===== -fig2 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig2 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); % Top row: SensorDetailPlots for temperature and pressure sdpCorr1 = SensorDetailPlot(s1, 'Parent', fig2.tilePanel(1), ... @@ -111,7 +111,7 @@ 'Events', ev2, 'XType', 'datenum', 'Title', 'Pressure'); sdpCorr2.render(); -% Bottom row: plain FastPlots +% Bottom row: plain FastSenses % Tile 3: Temperature + Pressure overlay (z-score normalized) fp3 = fig2.tile(3); fp3.addLine(tNum, (d1-mean(d1))/std(d1), 'DisplayName', 'Temp (z)', 'XType', 'datenum'); @@ -128,7 +128,7 @@ dock.addTab(fig2, 'Correlation'); %% ===== Tab 3: Event Analysis (1 large + event table) ===== -fig3 = FastPlotGrid(1, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig3 = FastSenseGrid(1, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); % Left: large SensorDetailPlot of temperature sdpEvent = SensorDetailPlot(s1, 'Parent', fig3.tilePanel(1), ... @@ -149,8 +149,8 @@ dock.addTab(fig3, 'Event Analysis'); -%% ===== Tab 4: Trends (3x2 plain FastPlots) ===== -fig4 = FastPlotGrid(3, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +%% ===== Tab 4: Trends (3x2 plain FastSenses) ===== +fig4 = FastSenseGrid(3, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); % Column 1: Raw signals rawSensors = {s1, s2, s3}; diff --git a/examples/example_sensor_multi_state.m b/examples/example_sensor_multi_state.m index c5e594a7..409dc0ba 100644 --- a/examples/example_sensor_multi_state.m +++ b/examples/example_sensor_multi_state.m @@ -79,8 +79,8 @@ end end -% --- Plot with FastPlot --- -fp = FastPlot(); +% --- Plot with FastSense --- +fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); fp.render(); title('Gas Flow — Multi-State Dynamic Thresholds'); diff --git a/examples/example_sensor_registry.m b/examples/example_sensor_registry.m index 389e9820..42b928b8 100644 --- a/examples/example_sensor_registry.m +++ b/examples/example_sensor_registry.m @@ -46,8 +46,8 @@ fprintf(' [%d] key="%s", name="%s"\n', i, sensors{i}.Key, sensors{i}.Name); end -% --- Plot the pressure sensor with FastPlot --- -fp = FastPlot(); +% --- Plot the pressure sensor with FastSense --- +fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); fp.render(); title(sprintf('%s (from SensorRegistry)', s.Name)); diff --git a/examples/example_sensor_static.m b/examples/example_sensor_static.m index 72286cfb..4bd61c58 100644 --- a/examples/example_sensor_static.m +++ b/examples/example_sensor_static.m @@ -28,7 +28,7 @@ % --- Resolve and plot --- s.resolve(); -fp = FastPlot(); +fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); fp.render(); title('Motor Vibration — Static Upper & Lower Thresholds'); diff --git a/examples/example_sensor_threshold.m b/examples/example_sensor_threshold.m index c4a91062..4a03b2ba 100644 --- a/examples/example_sensor_threshold.m +++ b/examples/example_sensor_threshold.m @@ -1,4 +1,4 @@ -%EXAMPLE_SENSOR_THRESHOLD Demonstrates the Sensor/Threshold system with FastPlot. +%EXAMPLE_SENSOR_THRESHOLD Demonstrates the Sensor/Threshold system with FastSense. % Shows dynamic thresholds that change based on machine state. % Setup paths @@ -36,8 +36,8 @@ % --- Precompute everything --- s.resolve(); -% --- Plot with FastPlot --- -fp = FastPlot(); +% --- Plot with FastSense --- +fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); fp.render(); title('Chamber Pressure with Dynamic Thresholds'); diff --git a/examples/example_sensor_todisk.m b/examples/example_sensor_todisk.m index 2b2403c8..b8d91e6a 100644 --- a/examples/example_sensor_todisk.m +++ b/examples/example_sensor_todisk.m @@ -54,11 +54,11 @@ numel(s.ResolvedThresholds), numel(s.ResolvedViolations)); %% 3. Plot the disk-backed sensor -% addSensor passes the DataStore directly to FastPlot — no copying. +% addSensor passes the DataStore directly to FastSense — no copying. fprintf('\n=== 3. Plot disk-backed sensor ===\n'); -fp = FastPlot(); +fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); tic; fp.render(); @@ -109,7 +109,7 @@ % Query a range slice = s3.DataStore.getColumnSlice('status', 1, 6); -labels = FastPlotDataStore.toCategorical(slice); +labels = FastSenseDataStore.toCategorical(slice); fprintf(' First 6 status labels: '); if iscell(labels) fprintf('%s\n', strjoin(labels, ', ')); @@ -118,7 +118,7 @@ end % Plot it -fp2 = FastPlot(); +fp2 = FastSense(); fp2.addSensor(s3); tic; fp2.render(); @@ -145,7 +145,7 @@ sensors{i} = si; end -fpf = FastPlotGrid(2, 2); +fpf = FastSenseGrid(2, 2); for i = 1:4 fpf.tile(i).addSensor(sensors{i}, 'ShowThresholds', true); end diff --git a/examples/example_stress_test.m b/examples/example_stress_test.m index f8b1b554..e6516826 100644 --- a/examples/example_stress_test.m +++ b/examples/example_stress_test.m @@ -1,6 +1,6 @@ -%% FastPlot Stress Test — 5-Tab FastPlotDock with Sensors & Thresholds +%% FastSense Stress Test — 5-Tab FastSenseDock with Sensors & Thresholds % Demonstrates: -% - FastPlotDock with 5 tabbed dashboards + FastPlotToolbar +% - FastSenseDock with 5 tabbed dashboards + FastSenseToolbar % - 26 sensor tiles, each with 4 dynamic thresholds (Warn HH/LL, Alarm HH/LL) % - State-dependent thresholds that step at machine state transitions % - ~86M total data points across all tabs @@ -9,7 +9,7 @@ projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); -fprintf('\n=== FastPlot Stress Test: 5 Tabbed Dashboards ===\n'); +fprintf('\n=== FastSense Stress Test: 5 Tabbed Dashboards ===\n'); totalTic = tic; % --- Shared state channels (reused across dashboards) --- @@ -26,13 +26,13 @@ scZone.Y = [0 1 2]; % --- Create dock --- -dock = FastPlotDock('Theme', 'light', 'Name', 'Stress Test — 26 Sensors, 104 Thresholds', ... +dock = FastSenseDock('Theme', 'light', 'Name', 'Stress Test — 26 Sensors, 104 Thresholds', ... 'Position', [50 50 1800 1000]); % ========================================================================= % TAB 1: Vacuum Chamber — 3x2 grid, 6 sensors % ========================================================================= -fig1 = FastPlotGrid(3, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig1 = FastSenseGrid(3, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); % 1.1: Chamber Pressure — 5M pts s = make_sensor('pressure', 'Chamber Pressure', 5e6, 40, 18, 800, 4, {scMachine, scVacuum}); @@ -76,7 +76,7 @@ % ========================================================================= % TAB 2: Motor Diagnostics — 2x3 grid, 6 sensors % ========================================================================= -fig2 = FastPlotGrid(2, 3, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig2 = FastSenseGrid(2, 3, 'ParentFigure', dock.hFigure, 'Theme', 'light'); % 2.1: Motor Current A — 5M pts s = make_sensor('motor_A', 'Motor Current A', 5e6, 12, 4, 400, 1.5, {scMachine}); @@ -122,7 +122,7 @@ % ========================================================================= % TAB 3: Environmental — 2x2 grid, 4 sensors % ========================================================================= -fig3 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig3 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); % 3.1: Cleanroom Temp — 5M pts s = make_sensor('room_temp', 'Cleanroom Temp', 5e6, 22, 1.5, 1800, 0.3, {scMachine}); @@ -155,7 +155,7 @@ % ========================================================================= % TAB 4: Gas Delivery — 3x2 grid, 6 sensors % ========================================================================= -fig4 = FastPlotGrid(3, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig4 = FastSenseGrid(3, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); gasNames = {'Argon', 'Nitrogen', 'Oxygen', 'CF4', 'CHF3', 'Helium'}; gasNominal = [200 150 80 50 30 500]; @@ -185,7 +185,7 @@ % ========================================================================= % TAB 5: Power & Cooling — 2x2 grid, 4 sensors % ========================================================================= -fig5 = FastPlotGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); +fig5 = FastSenseGrid(2, 2, 'ParentFigure', dock.hFigure, 'Theme', 'light'); % 5.1: Chiller Supply — 3M pts s = make_sensor('chiller_supply', 'Chiller Supply', 3e6, 18, 2, 1200, 0.5, {scMachine}); diff --git a/examples/example_themes.m b/examples/example_themes.m index 4bc2939d..952fe8dd 100644 --- a/examples/example_themes.m +++ b/examples/example_themes.m @@ -1,4 +1,4 @@ -%% FastPlot Theme Comparison — All 5 built-in themes side by side +%% FastSense Theme Comparison — All 5 built-in themes side by side % Opens one figure per theme to compare visual styles projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -14,7 +14,7 @@ for i = 1:numel(themes) themeName = themes{i}; - fp = FastPlot('Theme', themeName); + fp = FastSense('Theme', themeName); fp.addLine(x, y1, 'DisplayName', 'Signal A'); fp.addLine(x, y2, 'DisplayName', 'Signal B'); fp.addLine(x, y3, 'DisplayName', 'Signal C'); diff --git a/examples/example_toolbar.m b/examples/example_toolbar.m index 8f3416a9..801b8496 100644 --- a/examples/example_toolbar.m +++ b/examples/example_toolbar.m @@ -1,4 +1,4 @@ -%% FastPlot Toolbar Demo +%% FastSense Toolbar Demo % Demonstrates the interactive toolbar: data cursor, crosshair, % grid toggle, legend toggle, autoscale Y, and PNG export. @@ -13,19 +13,19 @@ fprintf('Toolbar example: %d points, 2 lines...\n', n); tic; -fp = FastPlot('Theme', 'light'); +fp = FastSense('Theme', 'light'); fp.addLine(x, y1, 'DisplayName', 'Sine'); fp.addLine(x, y2, 'DisplayName', 'Cosine'); fp.addThreshold(1.2, 'Direction', 'upper', 'ShowViolations', true); fp.render(); -tb = FastPlotToolbar(fp); +tb = FastSenseToolbar(fp); fprintf('Rendered with toolbar in %.3f seconds.\n', toc); fprintf('Try: Data Cursor (click), Crosshair (hover), Grid, Legend, Autoscale Y, Export PNG\n'); %% Dashboard with toolbar -fig = FastPlotGrid(1, 2, 'Theme', 'industrial'); +fig = FastSenseGrid(1, 2, 'Theme', 'industrial'); fp1 = fig.tile(1); fp1.addLine(x, y1, 'DisplayName', 'Pressure'); fp1.addThreshold(1.0, 'Direction', 'upper', 'ShowViolations', true); @@ -35,17 +35,17 @@ fig.setTileTitle(1, 'Pressure'); fig.setTileTitle(2, 'Temperature'); -tb2 = FastPlotToolbar(fig); +tb2 = FastSenseToolbar(fig); fprintf('Dashboard with toolbar ready.\n'); %% Datetime X-Axis x = datenum(2024,1,1) + (0:99999)/86400; % ~1 day at 1-second resolution y = sin((1:100000) * 2*pi/3600) + 0.2*randn(1,100000); -fp3 = FastPlot('Theme', 'light'); +fp3 = FastSense('Theme', 'light'); fp3.addLine(x, y, 'DisplayName', 'Sensor', 'XType', 'datenum'); fp3.render(); title(fp3.hAxes, 'Datetime Axis — zoom to see format change'); -tb3 = FastPlotToolbar(fp3); +tb3 = FastSenseToolbar(fp3); fprintf('Datetime axis with toolbar ready. Zoom to see tick format adapt.\n'); diff --git a/examples/example_uneven_sampling.m b/examples/example_uneven_sampling.m index eb389128..65c97985 100644 --- a/examples/example_uneven_sampling.m +++ b/examples/example_uneven_sampling.m @@ -1,5 +1,5 @@ -%% FastPlot Unevenly Sampled Data — Event-driven acquisition -% Demonstrates that FastPlot handles non-uniform X spacing correctly +%% FastSense Unevenly Sampled Data — Event-driven acquisition +% Demonstrates that FastSense handles non-uniform X spacing correctly projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); @@ -38,7 +38,7 @@ fprintf('Uneven sampling: %d points, rate varies 10 Hz to 10 kHz...\n', numel(x)); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Event-Driven Sensor', 'Color', [0.2 0.5 0.7]); fp.addThreshold(4.0, 'Direction', 'upper', 'ShowViolations', true, ... 'Color', [0.8 0 0], 'LineStyle', '--', 'Label', 'High'); diff --git a/examples/example_vibration.m b/examples/example_vibration.m index b14c1ac7..e39a3c2d 100644 --- a/examples/example_vibration.m +++ b/examples/example_vibration.m @@ -1,4 +1,4 @@ -%% FastPlot Vibration Analysis — High-frequency data with envelope thresholds +%% FastSense Vibration Analysis — High-frequency data with envelope thresholds % Simulates accelerometer data from a rotating machine projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -27,7 +27,7 @@ fprintf('Vibration: %d points at %d kHz...\n', n, fs/1000); tic; -fp = FastPlot(); +fp = FastSense(); fp.addLine(x, y, 'DisplayName', 'Accelerometer', 'Color', [0.1 0.3 0.6]); % ISO 10816 style velocity thresholds (simplified for displacement) diff --git a/examples/example_visual_features.m b/examples/example_visual_features.m index 943c21ca..1d8b12eb 100644 --- a/examples/example_visual_features.m +++ b/examples/example_visual_features.m @@ -1,4 +1,4 @@ -%% FastPlot Visual Features — Bands, Shading, Fill, and Markers +%% FastSense Visual Features — Bands, Shading, Fill, and Markers % Demonstrates all new visual enhancement methods in a 2x2 dashboard projectRoot = fileparts(fileparts(mfilename('fullpath'))); @@ -10,7 +10,7 @@ fprintf('Visual features example: 4 tiles, %d points...\n', n); tic; -fig = FastPlotGrid(2, 2, 'Theme', 'default', ... +fig = FastSenseGrid(2, 2, 'Theme', 'default', ... 'Name', 'Visual Features Demo', 'Position', [50 50 1200 800]); % --- Tile 1: addBand — Alarm bands with thresholds --- diff --git a/examples/example_widget_fastplot.m b/examples/example_widget_fastsense.m similarity index 86% rename from examples/example_widget_fastplot.m rename to examples/example_widget_fastsense.m index 43992d7e..8e46016f 100644 --- a/examples/example_widget_fastplot.m +++ b/examples/example_widget_fastsense.m @@ -1,10 +1,10 @@ -%% FastPlotWidget — All Configuration Modes -% Demonstrates every data-binding mode of FastPlotWidget inside +%% FastSenseWidget — All Configuration Modes +% Demonstrates every data-binding mode of FastSenseWidget inside % DashboardEngine on a 24-column grid. % % Supported properties: % SensorObj (alias 'Sensor') — Sensor object for data binding -% DataStoreObj (alias 'DataStore') — FastPlotDataStore for disk-backed data +% DataStoreObj (alias 'DataStore') — FastSenseDataStore for disk-backed data % XData, YData — inline numeric arrays % File, XVar, YVar — load from .mat file % Thresholds — 'auto' (default, from Sensor) | false @@ -68,23 +68,23 @@ x = linspace(0, 10, N); y = 2.5*sin(2*pi*3*x) .* exp(-0.15*x) + 0.3*randn(1,N); -%% 3. Build dashboard — 2x2 grid of FastPlotWidgets -d = DashboardEngine('FastPlotWidget — Configuration Showcase'); +%% 3. Build dashboard — 2x2 grid of FastSenseWidgets +d = DashboardEngine('FastSenseWidget — Configuration Showcase'); d.Theme = 'light'; % Top-left: sensor-bound, auto thresholds + violations -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [1 1 12 10], ... 'Sensor', sTemp); % Top-right: sensor-bound, thresholds disabled -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [13 1 12 10], ... 'Sensor', sPress, ... 'Thresholds', false); % Bottom-left: inline XData/YData with manual labels -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [1 11 12 10], ... 'XData', x, 'YData', y, ... 'Title', 'Vibration Decay', ... @@ -92,7 +92,7 @@ 'YLabel', 'Amplitude [g]'); % Bottom-right: sensor-bound with custom axis labels -d.addWidget('fastplot', ... +d.addWidget('fastsense', ... 'Position', [13 11 12 10], ... 'Sensor', sTemp, ... 'YLabel', ['Temperature [' char(176) 'F]'], ... @@ -101,6 +101,6 @@ %% 4. Render and print summary d.render(); -fprintf('Dashboard rendered with %d FastPlotWidgets.\n', numel(d.Widgets)); +fprintf('Dashboard rendered with %d FastSenseWidgets.\n', numel(d.Widgets)); fprintf('Temperature violations: %d\n', sTemp.countViolations()); fprintf('Pressure violations: %d\n', sPress.countViolations()); diff --git a/examples/example_widget_number.m b/examples/example_widget_number.m index a4ba2a1d..378b0a2b 100644 --- a/examples/example_widget_number.m +++ b/examples/example_widget_number.m @@ -73,8 +73,8 @@ 'Units', 'items', ... 'Format', '%d'); -% --- FastPlot for visual context --- -d.addWidget('fastplot', ... +% --- FastSense for visual context --- +d.addWidget('fastsense', ... 'Position', [1 3 24 10], ... 'SensorObj', sTemp); diff --git a/examples/example_widget_status.m b/examples/example_widget_status.m index 4b0fe9bd..7a057015 100644 --- a/examples/example_widget_status.m +++ b/examples/example_widget_status.m @@ -63,10 +63,10 @@ d.addWidget('status', 'Position', [21 1 4 1], ... 'Title', 'Manual Override', 'StaticStatus', 'ok'); -% --- Row 3+: FastPlot widgets showing the underlying sensor data --- -d.addWidget('fastplot', 'Position', [1 3 8 6], 'SensorObj', sTemp); -d.addWidget('fastplot', 'Position', [9 3 8 6], 'SensorObj', sPress); -d.addWidget('fastplot', 'Position', [17 3 8 6], 'SensorObj', sFlow); +% --- Row 3+: FastSense widgets showing the underlying sensor data --- +d.addWidget('fastsense', 'Position', [1 3 8 6], 'SensorObj', sTemp); +d.addWidget('fastsense', 'Position', [9 3 8 6], 'SensorObj', sPress); +d.addWidget('fastsense', 'Position', [17 3 8 6], 'SensorObj', sFlow); %% 3. Render d.render(); diff --git a/examples/run_all_examples.m b/examples/run_all_examples.m index 252f5e69..026c47a1 100644 --- a/examples/run_all_examples.m +++ b/examples/run_all_examples.m @@ -1,5 +1,5 @@ function run_all_examples(mode) -%RUN_ALL_EXAMPLES Run each FastPlot example. +%RUN_ALL_EXAMPLES Run each FastSense example. % run_all_examples() — interactive: press ENTER between examples % run_all_examples('auto') — non-interactive: 5s pause between examples % @@ -11,7 +11,7 @@ function run_all_examples(mode) projectRoot = fileparts(fileparts(mfilename('fullpath'))); run(fullfile(projectRoot, 'setup.m')); - addpath(fullfile(projectRoot, 'libs', 'FastPlot', 'private')); + addpath(fullfile(projectRoot, 'libs', 'FastSense', 'private')); addpath(fileparts(mfilename('fullpath'))); examples = { @@ -33,12 +33,12 @@ function run_all_examples(mode) 'example_sensor_multi_state', 'Multi-state sensor, combined conditions, getThresholdsAt' 'example_sensor_registry', 'SensorRegistry API (list, get, getMultiple)' 'example_sensor_dashboard', 'Multi-sensor 2x2 dashboard with SensorRegistry' - 'example_mixed_tiles', 'Mixed tile types: FastPlot + bar/scatter/histogram' + 'example_mixed_tiles', 'Mixed tile types: FastSense + bar/scatter/histogram' }; fprintf('\n'); fprintf('========================================\n'); - fprintf(' FastPlot Examples (%d total)\n', size(examples, 1)); + fprintf(' FastSense Examples (%d total)\n', size(examples, 1)); if strcmp(mode, 'auto') fprintf(' Auto mode: 5s pause between examples\n'); else diff --git a/libs/Dashboard/DashboardBuilder.m b/libs/Dashboard/DashboardBuilder.m index 907ad3f9..336f7471 100644 --- a/libs/Dashboard/DashboardBuilder.m +++ b/libs/Dashboard/DashboardBuilder.m @@ -52,7 +52,7 @@ hPropDelete = [] hPropLabel = [] - % Axis label controls (fastplot only) + % Axis label controls (fastsense only) hPropXLabel = [] hPropYLabel = [] @@ -249,7 +249,7 @@ function applyProperties(obj) function pos = findNextSlot(obj, type) switch type - case 'fastplot', defW = 12; defH = 3; + case 'fastsense', defW = 12; defH = 3; case 'number', defW = 6; defH = 1; case 'status', defW = 4; defH = 1; case 'text', defW = 6; defH = 1; @@ -307,7 +307,7 @@ function createPalette(obj, hFig, theme) 'BackgroundColor', theme.ToolbarBackground, ... 'HorizontalAlignment', 'center'); - types = {'fastplot','number','status','text', ... + types = {'fastsense','number','status','text', ... 'gauge','table','rawaxes','timeline'}; labels = {'Plot','Number','Status','Text', ... 'Gauge','Table','Axes','Events'}; @@ -433,7 +433,7 @@ function createPropertiesPanel(obj, hFig, theme) 'String', '', 'Visible', 'off'); y = y - fh - gap*2; - % --- Axis Labels (shown for fastplot) --- + % --- Axis Labels (shown for fastsense) --- uicontrol('Parent', obj.hPropsPanel, 'Style', 'text', ... 'Units', 'normalized', 'Position', [0.04 y 0.44 lh], ... 'String', 'X:', 'FontSize', fs, ... @@ -785,7 +785,7 @@ function updatePropertiesDisplay(obj) set(obj.hPropWidth, 'String', num2str(w.Position(3))); set(obj.hPropHeight, 'String', num2str(w.Position(4))); - % Populate axis label fields (fastplot only) + % Populate axis label fields (fastsense only) if isprop(w, 'XLabel') set(obj.hPropXLabel, 'String', w.XLabel, 'Enable', 'on'); set(obj.hPropYLabel, 'String', w.YLabel, 'Enable', 'on'); @@ -935,7 +935,7 @@ function showProps(obj, vis) function populateSourceControls(obj, w) switch w.Type - case 'fastplot' + case 'fastsense' if ~isempty(w.Sensor) set(obj.hSourceType, 'Value', 2); % Sensor set(obj.hSourceKey, 'String', w.Sensor.Key); @@ -1022,7 +1022,7 @@ function onSourceBrowse(obj) function t = defaultTitleForType(~, type) switch type - case 'fastplot', t = 'New Plot'; + case 'fastsense', t = 'New Plot'; case 'number', t = 'New Number'; case 'status', t = 'New Status'; case 'text', t = 'New Text'; diff --git a/libs/Dashboard/DashboardEngine.m b/libs/Dashboard/DashboardEngine.m index df3b5bc3..ba01a1bd 100644 --- a/libs/Dashboard/DashboardEngine.m +++ b/libs/Dashboard/DashboardEngine.m @@ -5,7 +5,7 @@ % d = DashboardEngine('My Dashboard'); % d.Theme = 'light'; % d.LiveInterval = 5; -% d.addWidget('fastplot', 'Title', 'Temp', 'Position', [1 1 6 3], ... +% d.addWidget('fastsense', 'Title', 'Temp', 'Position', [1 1 6 3], ... % 'Sensor', SensorRegistry.get('temperature')); % d.render(); % @@ -16,8 +16,8 @@ % d = DashboardEngine.load('path/to/dashboard.json'); % d.render(); % -% For a lightweight tiled grid of FastPlot charts without widgets, -% see FastPlotGrid. +% For a lightweight tiled grid of FastSense charts without widgets, +% see FastSenseGrid. properties (Access = public) Name = '' @@ -63,8 +63,8 @@ function addWidget(obj, type, varargin) switch type - case 'fastplot' - w = FastPlotWidget(varargin{:}); + case 'fastsense' + w = FastSenseWidget(varargin{:}); case 'number' w = NumberWidget(varargin{:}); case 'kpi' @@ -432,7 +432,7 @@ function onLiveTick(obj) function types = widgetTypes() %WIDGETTYPES List supported widget type strings. types = { - 'fastplot', 'Time-series plot (FastPlotWidget)' + 'fastsense', 'Time-series plot (FastSenseWidget)' 'number', 'Single numeric value with trend (NumberWidget)' 'status', 'Status indicator with dot and label (StatusWidget)' 'gauge', 'Gauge display in arc/donut/bar/thermometer style (GaugeWidget)' diff --git a/libs/Dashboard/DashboardSerializer.m b/libs/Dashboard/DashboardSerializer.m index d2d570ed..4af9501b 100644 --- a/libs/Dashboard/DashboardSerializer.m +++ b/libs/Dashboard/DashboardSerializer.m @@ -71,8 +71,8 @@ function save(config, filepath) ws = config.widgets{i}; w = []; switch ws.type - case 'fastplot' - widgets{i} = FastPlotWidget.fromStruct(ws); + case 'fastsense' + widgets{i} = FastSenseWidget.fromStruct(ws); case 'number' widgets{i} = NumberWidget.fromStruct(ws); case 'kpi' @@ -127,28 +127,28 @@ function exportScript(config, filepath) ws.position.width, ws.position.height); switch ws.type - case 'fastplot' + case 'fastsense' if isfield(ws, 'source') switch ws.source.type case 'sensor' - lines{end+1} = sprintf('d.addWidget(''fastplot'', ''Title'', ''%s'', ...', ws.title); + lines{end+1} = sprintf('d.addWidget(''fastsense'', ''Title'', ''%s'', ...', ws.title); lines{end+1} = sprintf(' ''Position'', %s, ...', pos); lines{end+1} = sprintf(' ''Sensor'', SensorRegistry.get(''%s''));', ws.source.name); case 'file' - lines{end+1} = sprintf('d.addWidget(''fastplot'', ''Title'', ''%s'', ...', ws.title); + lines{end+1} = sprintf('d.addWidget(''fastsense'', ''Title'', ''%s'', ...', ws.title); lines{end+1} = sprintf(' ''Position'', %s, ...', pos); lines{end+1} = sprintf(' ''File'', ''%s'', ''XVar'', ''%s'', ''YVar'', ''%s'');', ... ws.source.path, ws.source.xVar, ws.source.yVar); case 'data' - lines{end+1} = sprintf('d.addWidget(''fastplot'', ''Title'', ''%s'', ...', ws.title); + lines{end+1} = sprintf('d.addWidget(''fastsense'', ''Title'', ''%s'', ...', ws.title); lines{end+1} = sprintf(' ''Position'', %s, ...', pos); lines{end+1} = sprintf(' ''XData'', %s, ''YData'', %s);', ... mat2str(ws.source.x), mat2str(ws.source.y)); otherwise - lines{end+1} = sprintf('d.addWidget(''fastplot'', ''Title'', ''%s'', ''Position'', %s);', ws.title, pos); + lines{end+1} = sprintf('d.addWidget(''fastsense'', ''Title'', ''%s'', ''Position'', %s);', ws.title, pos); end else - lines{end+1} = sprintf('d.addWidget(''fastplot'', ''Title'', ''%s'', ''Position'', %s);', ws.title, pos); + lines{end+1} = sprintf('d.addWidget(''fastsense'', ''Title'', ''%s'', ''Position'', %s);', ws.title, pos); end case 'number' line = sprintf('d.addWidget(''number'', ''Title'', ''%s'', ''Position'', %s', ws.title, pos); diff --git a/libs/Dashboard/DashboardTheme.m b/libs/Dashboard/DashboardTheme.m index f9e113bb..a1dda74f 100644 --- a/libs/Dashboard/DashboardTheme.m +++ b/libs/Dashboard/DashboardTheme.m @@ -1,11 +1,11 @@ function theme = DashboardTheme(preset, varargin) -%DASHBOARDTHEME Returns a theme struct with FastPlotTheme + dashboard fields. +%DASHBOARDTHEME Returns a theme struct with FastSenseTheme + dashboard fields. % % theme = DashboardTheme() % default preset % theme = DashboardTheme('dark') % named preset % theme = DashboardTheme('dark', 'DashboardBackground', [0.1 0.1 0.2]) % -% Returns a struct containing all FastPlotTheme fields plus dashboard-specific +% Returns a struct containing all FastSenseTheme fields plus dashboard-specific % fields: DashboardBackground, WidgetBackground, WidgetBorderColor, % WidgetBorderWidth, DragHandleColor, DropZoneColor, GridLineColor, % ToolbarBackground, ToolbarFontColor, HeaderFontSize, @@ -16,8 +16,8 @@ preset = 'default'; end - % Get base FastPlotTheme - base = FastPlotTheme(preset); + % Get base FastSenseTheme + base = FastSenseTheme(preset); % Append dashboard-specific fields dash = getDashboardDefaults(preset); diff --git a/libs/Dashboard/DashboardWidget.m b/libs/Dashboard/DashboardWidget.m index 8f6ffabf..cc29cd2b 100644 --- a/libs/Dashboard/DashboardWidget.m +++ b/libs/Dashboard/DashboardWidget.m @@ -5,7 +5,7 @@ % render(parentPanel) — create graphics objects inside the panel % refresh() — update data/display (called by live timer) % configure() — open properties UI for edit mode -% getType() — return widget type string (e.g. 'fastplot') +% getType() — return widget type string (e.g. 'fastsense') % % Subclasses must also provide a static fromStruct(s) method. diff --git a/libs/Dashboard/FastPlotWidget.m b/libs/Dashboard/FastSenseWidget.m similarity index 86% rename from libs/Dashboard/FastPlotWidget.m rename to libs/Dashboard/FastSenseWidget.m index fff2eb46..5880d1fb 100644 --- a/libs/Dashboard/FastPlotWidget.m +++ b/libs/Dashboard/FastSenseWidget.m @@ -1,11 +1,11 @@ -classdef FastPlotWidget < DashboardWidget -%FASTPLOTWIDGET Dashboard widget wrapping a FastPlot instance. +classdef FastSenseWidget < DashboardWidget +%FASTSENSEWIDGET Dashboard widget wrapping a FastSense instance. % % Supports three data binding modes: -% Sensor: w = FastPlotWidget('Sensor', sensorObj) -% DataStore: w = FastPlotWidget('DataStore', dsObj) -% Inline: w = FastPlotWidget('XData', x, 'YData', y) -% File: w = FastPlotWidget('File', 'path.mat', 'XVar', 'x', 'YVar', 'y') +% Sensor: w = FastSenseWidget('Sensor', sensorObj) +% DataStore: w = FastSenseWidget('DataStore', dsObj) +% Inline: w = FastSenseWidget('XData', x, 'YData', y) +% File: w = FastSenseWidget('File', 'path.mat', 'XVar', 'x', 'YVar', 'y') % % When bound to a Sensor, ThresholdRules apply automatically. @@ -22,12 +22,12 @@ end properties (SetAccess = private) - FastPlotObj = [] + FastSenseObj = [] IsSettingTime = false % guard to distinguish programmatic vs user xlim change end methods - function obj = FastPlotWidget(varargin) + function obj = FastSenseWidget(varargin) obj = obj@DashboardWidget(varargin{:}); if isequal(obj.Position, [1 1 6 2]) obj.Position = [1 1 12 3]; @@ -54,9 +54,9 @@ function render(obj, parentPanel) 'Units', 'normalized', ... 'Position', [0.08 0.12 0.88 0.78]); - % Create FastPlot on this axes - fp = FastPlot('Parent', ax); - obj.FastPlotObj = fp; + % Create FastSense on this axes + fp = FastSense('Parent', ax); + obj.FastSenseObj = fp; % Bind data if ~isempty(obj.Sensor) @@ -100,15 +100,15 @@ function refresh(obj) % Save zoom state before teardown savedXLim = []; - if ~isempty(obj.FastPlotObj) && ~isempty(obj.FastPlotObj.hAxes) ... - && ishandle(obj.FastPlotObj.hAxes) - savedXLim = get(obj.FastPlotObj.hAxes, 'XLim'); + if ~isempty(obj.FastSenseObj) && ~isempty(obj.FastSenseObj.hAxes) ... + && ishandle(obj.FastSenseObj.hAxes) + savedXLim = get(obj.FastSenseObj.hAxes, 'XLim'); end - % Delete old axes and FastPlot, then rebuild - if ~isempty(obj.FastPlotObj) - try delete(obj.FastPlotObj); catch, end - obj.FastPlotObj = []; + % Delete old axes and FastSense, then rebuild + if ~isempty(obj.FastSenseObj) + try delete(obj.FastSenseObj); catch, end + obj.FastSenseObj = []; end % Delete any leftover axes in the panel ch = findobj(obj.hPanel, 'Type', 'axes'); @@ -118,8 +118,8 @@ function refresh(obj) 'Units', 'normalized', ... 'Position', [0.08 0.12 0.88 0.78]); - fp = FastPlot('Parent', ax); - obj.FastPlotObj = fp; + fp = FastSense('Parent', ax); + obj.FastSenseObj = fp; fp.addSensor(obj.Sensor); if ~isempty(obj.Title) @@ -155,9 +155,9 @@ function setTimeRange(obj, tStart, tEnd) if ~obj.UseGlobalTime return; % widget has its own zoom, skip global time end - if ~isempty(obj.FastPlotObj) + if ~isempty(obj.FastSenseObj) try - ax = obj.FastPlotObj.hAxes; + ax = obj.FastSenseObj.hAxes; if ~isempty(ax) && ishandle(ax) obj.IsSettingTime = true; xlim(ax, [tStart tEnd]); @@ -191,7 +191,7 @@ function onXLimChanged(obj) end function t = getType(~) - t = 'fastplot'; + t = 'fastsense'; end function s = toStruct(obj) @@ -213,7 +213,7 @@ function onXLimChanged(obj) methods (Static) function obj = fromStruct(s) - obj = FastPlotWidget(); + obj = FastSenseWidget(); obj.Title = s.title; obj.Position = [s.position.col, s.position.row, ... s.position.width, s.position.height]; diff --git a/libs/EventDetection/EventViewer.m b/libs/EventDetection/EventViewer.m index 3680397f..f864475c 100644 --- a/libs/EventDetection/EventViewer.m +++ b/libs/EventDetection/EventViewer.m @@ -639,7 +639,7 @@ function openEventPlot(obj, row) minWidth = 1 / 1440; % --- Dashboard: 2 rows, 1 column (light theme) --- - dashboard = FastPlotGrid(2, 1, 'Theme', 'light', ... + dashboard = FastSenseGrid(2, 1, 'Theme', 'light', ... 'Name', sprintf('Event: %s — %s', ev.SensorName, ev.ThresholdLabel)); % Get selected event's threshold color @@ -703,7 +703,7 @@ function openEventPlot(obj, row) 'EdgeColor', evColor, 'LineWidth', 2, 'LineStyle', '-', ... 'HandleVisibility', 'off'); - FastPlotToolbar(fp1); + FastSenseToolbar(fp1); end function c = getThresholdColor(obj, label) diff --git a/libs/EventDetection/NotificationService.m b/libs/EventDetection/NotificationService.m index c4e864e9..9bcfb88a 100644 --- a/libs/EventDetection/NotificationService.m +++ b/libs/EventDetection/NotificationService.m @@ -12,7 +12,7 @@ SmtpPort = 25 SmtpUser = '' SmtpPassword = '' - FromAddress = 'fastplot@noreply.com' + FromAddress = 'fastsense@noreply.com' NotificationCount = 0 end @@ -23,7 +23,7 @@ p.addParameter('DryRun', false, @islogical); p.addParameter('SnapshotDir', '', @ischar); p.addParameter('SmtpServer', '', @ischar); - p.addParameter('FromAddress', 'fastplot@noreply.com', @ischar); + p.addParameter('FromAddress', 'fastsense@noreply.com', @ischar); p.parse(varargin{:}); obj.Enabled = p.Results.Enabled; obj.DryRun = p.Results.DryRun; @@ -31,7 +31,7 @@ obj.SmtpServer = p.Results.SmtpServer; obj.FromAddress = p.Results.FromAddress; if isempty(obj.SnapshotDir) - obj.SnapshotDir = fullfile(tempdir, 'fastplot_snapshots'); + obj.SnapshotDir = fullfile(tempdir, 'fastsense_snapshots'); end end diff --git a/libs/EventDetection/generateEventSnapshot.m b/libs/EventDetection/generateEventSnapshot.m index 0b69704f..42ff226e 100644 --- a/libs/EventDetection/generateEventSnapshot.m +++ b/libs/EventDetection/generateEventSnapshot.m @@ -1,5 +1,5 @@ function files = generateEventSnapshot(event, sensorData, varargin) - % generateEventSnapshot Create two FastPlot PNG snapshots for an event. + % generateEventSnapshot Create two FastSense PNG snapshots for an event. % % files = generateEventSnapshot(event, sensorData, ...) % diff --git a/libs/EventDetection/private/parseOpts.m b/libs/EventDetection/private/parseOpts.m index 2db738ee..242f3c9e 100644 --- a/libs/EventDetection/private/parseOpts.m +++ b/libs/EventDetection/private/parseOpts.m @@ -9,7 +9,7 @@ % [opts, unmatched] = PARSEOPTS(defaults, args, verbose) additionally % emits a warning for every unrecognized option when verbose is true. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % defaults — scalar struct whose field names define valid option keys @@ -32,7 +32,7 @@ % [opts, extra] = parseOpts(defs, {'color', 'b', 'Name', 'foo'}); % % opts.Color == 'b', opts.Width == 1, extra.Name == 'foo' % -% See also FastPlot, FastPlotGrid, struct2nvpairs. +% See also FastSense, FastSenseGrid, struct2nvpairs. if nargin < 3; verbose = false; end @@ -58,7 +58,7 @@ % Unmatched — collect for pass-through unmatched.(key) = val; if verbose - warning('FastPlot:unknownOption', ... + warning('FastSense:unknownOption', ... 'Unknown option ''%s''. Valid options: %s', ... key, strjoin(fnames, ', ')); end diff --git a/libs/FastPlot/ConsoleProgressBar.m b/libs/FastSense/ConsoleProgressBar.m similarity index 99% rename from libs/FastPlot/ConsoleProgressBar.m rename to libs/FastSense/ConsoleProgressBar.m index 878c5f5e..a63c3b5d 100644 --- a/libs/FastPlot/ConsoleProgressBar.m +++ b/libs/FastSense/ConsoleProgressBar.m @@ -40,7 +40,7 @@ % end % pb.freeze(); % becomes permanent line % -% See also FastPlot, FastPlotGrid, FastPlotDock. +% See also FastSense, FastSenseGrid, FastSenseDock. properties (Access = private) Label = '' diff --git a/libs/FastPlot/FastPlot.m b/libs/FastSense/FastSense.m similarity index 94% rename from libs/FastPlot/FastPlot.m rename to libs/FastSense/FastSense.m index 1b491824..b7e9ae6d 100644 --- a/libs/FastPlot/FastPlot.m +++ b/libs/FastSense/FastSense.m @@ -1,6 +1,6 @@ -classdef FastPlot < handle - %FASTPLOT Ultra-fast time series plotting with dynamic downsampling. - % FastPlot renders 1K to 100M data points with fluid zoom/pan by +classdef FastSense < handle + %FASTSENSE Ultra-fast time series plotting with dynamic downsampling. + % FastSense renders 1K to 100M data points with fluid zoom/pan by % dynamically downsampling data to screen resolution using MinMax or % LTTB algorithms. A multi-level pyramid cache provides instant % re-downsample on zoom without touching raw data. @@ -15,10 +15,10 @@ % - Metadata attachment and forward-fill lookup by X value % - Datetime X-axis support with auto-formatting % - Logarithmic axis support (X, Y, or both) - % - Theme-based styling via FastPlotTheme + % - Theme-based styling via FastSenseTheme % % Usage: - % fp = FastPlot(); + % fp = FastSense(); % fp.addLine(x, y, 'DisplayName', 'Sensor1'); % fp.addThreshold(4.5, 'Direction', 'upper', 'ShowViolations', true); % fp.render(); @@ -26,7 +26,7 @@ % Constructor options (name-value): % 'Parent' — axes handle (default: create new figure) % 'LinkGroup' — string ID for linked zoom/pan across plots - % 'Theme' — theme preset name, struct, or FastPlotTheme + % 'Theme' — theme preset name, struct, or FastSenseTheme % 'Verbose' — logical, print diagnostics (default: false) % 'MinPointsForDownsample' — threshold for raw vs downsampled % 'DownsampleFactor' — points per pixel (default: 2) @@ -37,40 +37,40 @@ % 'LiveInterval' — poll interval in seconds (default: 2.0) % % Example — linked zoom across two plots: - % fp1 = FastPlot('LinkGroup', 'group1'); + % fp1 = FastSense('LinkGroup', 'group1'); % fp1.addLine(x, y1, 'DisplayName', 'Temp'); % fp1.render(); - % fp2 = FastPlot('LinkGroup', 'group1'); + % fp2 = FastSense('LinkGroup', 'group1'); % fp2.addLine(x, y2, 'DisplayName', 'Pressure'); % fp2.render(); % % Example — logarithmic Y axis: - % fp = FastPlot('YScale', 'log'); + % fp = FastSense('YScale', 'log'); % fp.addLine(1:1000, logspace(-1, 3, 1000)); % fp.render(); % % Example — live mode: - % fp = FastPlot(); fp.addLine(x, y); fp.render(); + % fp = FastSense(); fp.addLine(x, y); fp.render(); % fp.startLive('data.mat', @(fp, s) fp.updateData(1, s.x, s.y)); % % Example — distribute figures on screen (bundled in vendor/): - % FastPlot.distFig() % auto-arrange all figures - % FastPlot.distFig('Rows', 2, 'Cols', 3) % 2x3 grid + % FastSense.distFig() % auto-arrange all figures + % FastSense.distFig('Rows', 2, 'Cols', 3) % 2x3 grid % % Notes: % addLine, addThreshold, addBand, addShaded, and addMarker must be % called BEFORE render(). After render(), use updateData(lineIndex, % newX, newY) to replace existing line data. To add a new series - % after render(), create a new FastPlot instance. + % after render(), create a new FastSense instance. % - % See also FastPlotGrid, FastPlotDock, FastPlotTheme, FastPlotToolbar. + % See also FastSenseGrid, FastSenseDock, FastSenseTheme, FastSenseToolbar. % ========================= PUBLIC PROPERTIES ========================= % User-configurable settings. Set before calling render(). properties (Access = public) ParentAxes = [] % axes handle, empty = create new LinkGroup = '' % string ID for linked zoom/pan - Theme = [] % theme struct (from FastPlotTheme) + Theme = [] % theme struct (from FastSenseTheme) Verbose = false % print diagnostics to console LiveViewMode = '' % 'preserve' | 'follow' | 'reset' (empty = no view mode applied) LiveFile = '' % path to .mat file for live mode @@ -151,22 +151,22 @@ end methods (Access = public) - function obj = FastPlot(varargin) - %FASTPLOT Construct a FastPlot instance. - % fp = FASTPLOT() creates a new FastPlot with default settings. - % fp = FASTPLOT('Parent', ax, 'Theme', 'dark') creates a plot + function obj = FastSense(varargin) + %FASTSENSE Construct a FastSense instance. + % fp = FASTSENSE() creates a new FastSense with default settings. + % fp = FASTSENSE('Parent', ax, 'Theme', 'dark') creates a plot % inside existing axes ax with the 'dark' theme. - % fp = FASTPLOT('LinkGroup', 'g1', 'Verbose', true) creates a + % fp = FASTSENSE('LinkGroup', 'g1', 'Verbose', true) creates a % plot that shares zoom/pan with other plots in group 'g1'. % % All options are name-value pairs. Defaults are loaded from - % FastPlotDefaults (via getDefaults) and can be overridden + % FastSenseDefaults (via getDefaults) and can be overridden % per-instance. % % Inputs (name-value pairs): % 'Parent' — axes handle; empty = create new figure % 'LinkGroup' — string ID for linked zoom/pan - % 'Theme' — theme name, struct, or FastPlotTheme object + % 'Theme' — theme name, struct, or FastSenseTheme object % 'Verbose' — logical, print diagnostics (default: false) % 'MinPointsForDownsample' — skip downsampling below this count % 'DownsampleFactor' — points per pixel (default: 2) @@ -177,14 +177,14 @@ % 'YScale' — 'linear' or 'log' (default: 'linear') % % Output: - % obj — new FastPlot handle object + % obj — new FastSense handle object % % Example: - % fp = FastPlot('Theme', 'dark', 'Verbose', true); + % fp = FastSense('Theme', 'dark', 'Verbose', true); % fp.addLine(1:1e6, randn(1,1e6)); % fp.render(); % - % See also addLine, render, FastPlotTheme, FastPlotDefaults. + % See also addLine, render, FastSenseTheme, FastSenseDefaults. cfg = getDefaults(); defaults.Parent = []; defaults.LinkGroup = ''; @@ -243,10 +243,10 @@ function reapplyTheme(obj) % Has no effect before render() has been called. % % Example: - % fp.Theme = FastPlotTheme('dark'); + % fp.Theme = FastSenseTheme('dark'); % fp.reapplyTheme(); % - % See also applyTheme, FastPlotTheme. + % See also applyTheme, FastSenseTheme. if ~obj.IsRendered; return; end obj.applyTheme(); % Update existing line widths @@ -288,10 +288,10 @@ function setScale(obj, varargin) % Validate validScales = {'linear', 'log'}; if ~ismember(opts.XScale, validScales) - error('FastPlot:invalidScale', 'XScale must be ''linear'' or ''log''.'); + error('FastSense:invalidScale', 'XScale must be ''linear'' or ''log''.'); end if ~ismember(opts.YScale, validScales) - error('FastPlot:invalidScale', 'YScale must be ''linear'' or ''log''.'); + error('FastSense:invalidScale', 'YScale must be ''linear'' or ''log''.'); end xChanged = ~strcmp(opts.XScale, obj.XScale); @@ -359,17 +359,17 @@ function addLine(obj, x, y, varargin) % 'Color','LineStyle','DisplayName',... — passed to line() % % Note: addLine must be called before render(). After rendering, use - % updateData() to modify existing lines, or create a new FastPlot. + % updateData() to modify existing lines, or create a new FastSense. % % Example: - % fp = FastPlot(); + % fp = FastSense(); % fp.addLine(1:1e6, cumsum(randn(1,1e6)), 'DisplayName', 'Walk'); % fp.render(); % % See also addThreshold, addShaded, render, updateData. if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add lines after render() has been called.'); end @@ -390,7 +390,7 @@ function addLine(obj, x, y, varargin) % Validate sizes match if numel(x) ~= numel(y) - error('FastPlot:sizeMismatch', ... + error('FastSense:sizeMismatch', ... 'X and Y must have the same number of elements.'); end @@ -440,7 +440,7 @@ function addLine(obj, x, y, varargin) lineStruct.Y = []; if obj.Verbose - fprintf('[FastPlot] addLine: %d pts -> pre-built DataStore\n', nPts); + fprintf('[FastSense] addLine: %d pts -> pre-built DataStore\n', nPts); end if isempty(obj.Lines) @@ -459,7 +459,7 @@ function addLine(obj, x, y, varargin) ce = min(ci + chunkSize, nX); dx = diff(x(ci:ce)); if any(dx(~isnan(dx)) < 0) - error('FastPlot:nonMonotonicX', ... + error('FastSense:nonMonotonicX', ... 'X must be monotonically increasing.'); end end @@ -491,11 +491,11 @@ function addLine(obj, x, y, varargin) % Decide storage: disk-backed for large datasets to avoid OOM useDisk = obj.shouldUseDisk(nPts); if useDisk - lineStruct.DataStore = FastPlotDataStore(x, y); + lineStruct.DataStore = FastSenseDataStore(x, y); lineStruct.X = []; lineStruct.Y = []; if obj.Verbose - fprintf('[FastPlot] addLine: %d pts (%.1f MB) -> disk-backed storage\n', ... + fprintf('[FastSense] addLine: %d pts (%.1f MB) -> disk-backed storage\n', ... nPts, nPts * 16 / 1e6); end else @@ -540,7 +540,7 @@ function addSensor(obj, sensor, varargin) % See also addLine, addThreshold. if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add sensors after render() has been called.'); end @@ -613,7 +613,7 @@ function addThreshold(obj, varargin) % 'Label' — text label for threshold % % Note: addThreshold must be called before render(). After - % rendering, create a new FastPlot to add new thresholds. + % rendering, create a new FastSense to add new thresholds. % % Example: % fp.addThreshold(4.5, 'Direction', 'upper', ... @@ -622,7 +622,7 @@ function addThreshold(obj, varargin) % See also addLine, addBand, updateViolations. if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add thresholds after render() has been called.'); end @@ -697,7 +697,7 @@ function addBand(obj, yLow, yHigh, varargin) % 'Label' — text label for the band % % Note: addBand must be called before render(). After rendering, - % create a new FastPlot to add new bands. + % create a new FastSense to add new bands. % % Example: % fp.addBand(3.5, 4.5, 'FaceColor', [1 0.9 0.9], ... @@ -706,7 +706,7 @@ function addBand(obj, yLow, yHigh, varargin) % See also addThreshold, addShaded. if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add bands after render() has been called.'); end @@ -754,7 +754,7 @@ function addMarker(obj, x, y, varargin) % 'Label' — text label for the marker group % % Note: addMarker must be called before render(). After rendering, - % create a new FastPlot to add new markers. + % create a new FastSense to add new markers. % % Example: % events_x = [100 500 900]; @@ -765,7 +765,7 @@ function addMarker(obj, x, y, varargin) % See also addLine, addThreshold. if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add markers after render() has been called.'); end @@ -821,7 +821,7 @@ function addShaded(obj, x, y1, y2, varargin) % 'DisplayName' — legend entry text % % Note: addShaded must be called before render(). After rendering, - % create a new FastPlot to add new shaded regions. + % create a new FastSense to add new shaded regions. % % Example: % x = linspace(0, 10, 1e5); @@ -831,7 +831,7 @@ function addShaded(obj, x, y1, y2, varargin) % See also addFill, addBand. if obj.IsRendered - error('FastPlot:alreadyRendered', ... + error('FastSense:alreadyRendered', ... 'Cannot add shaded regions after render() has been called.'); end @@ -840,7 +840,7 @@ function addShaded(obj, x, y1, y2, varargin) if ~isrow(y2); y2 = y2(:)'; end if numel(x) ~= numel(y1) || numel(x) ~= numel(y2) - error('FastPlot:sizeMismatch', ... + error('FastSense:sizeMismatch', ... 'X, Y1, and Y2 must have the same number of elements.'); end @@ -850,7 +850,7 @@ function addShaded(obj, x, y1, y2, varargin) ce = min(ci + chunkSize, nX); dx = diff(x(ci:ce)); if any(dx(~isnan(dx)) < 0) - error('FastPlot:nonMonotonicX', ... + error('FastSense:nonMonotonicX', ... 'X must be monotonically increasing.'); end end @@ -943,10 +943,10 @@ function render(obj, progressBar) % 10. Registers in LinkGroup for synchronized zoom/pan % % fp.RENDER(progressBar) uses an existing ConsoleProgressBar - % for progress reporting (used by FastPlotGrid for batch + % for progress reporting (used by FastSenseGrid for batch % rendering of multiple tiles). % - % Can only be called once per FastPlot instance. After render(), + % Can only be called once per FastSense instance. After render(), % addLine()/addThreshold()/addBand() will error. Use % updateData() for live data updates. % @@ -955,12 +955,12 @@ function render(obj, progressBar) % empty and ShowProgress is true, creates one % % Example: - % fp = FastPlot(); + % fp = FastSense(); % fp.addLine(x, y, 'DisplayName', 'Signal'); % fp.addThreshold(4.5, 'ShowViolations', true); % fp.render(); % - % See also addLine, updateData, startLive, FastPlotGrid. + % See also addLine, updateData, startLive, FastSenseGrid. if nargin < 2; progressBar = []; end @@ -976,11 +976,11 @@ function render(obj, progressBar) if obj.Verbose; renderTic = tic; end if obj.IsRendered - error('FastPlot:alreadyRendered', ... - 'render() has already been called on this FastPlot.'); + error('FastSense:alreadyRendered', ... + 'render() has already been called on this FastSense.'); end if isempty(obj.Lines) - error('FastPlot:noLines', 'Add at least one line before render().'); + error('FastSense:noLines', 'Add at least one line before render().'); end % Create or use axes @@ -1024,7 +1024,7 @@ function render(obj, progressBar) 'FaceAlpha', B.FaceAlpha, ... 'EdgeColor', B.EdgeColor, ... 'HandleVisibility', 'off'); - udB.FastPlot = struct( ... + udB.FastSense = struct( ... 'Type', 'band', ... 'Name', B.Label, ... 'LineIndex', [], ... @@ -1063,7 +1063,7 @@ function render(obj, progressBar) 'FaceAlpha', S.FaceAlpha, ... 'EdgeColor', S.EdgeColor, ... 'HandleVisibility', 'off'); - udS.FastPlot = struct( ... + udS.FastSense = struct( ... 'Type', 'shaded', ... 'Name', S.DisplayName, ... 'LineIndex', [], ... @@ -1132,18 +1132,18 @@ function render(obj, progressBar) if isfield(L.Options, 'DisplayName') displayName = L.Options.DisplayName; end - ud.FastPlot = struct( ... + ud.FastSense = struct( ... 'Type', 'data_line', ... 'Name', displayName, ... 'LineIndex', i, ... 'ThresholdValue', []); - ud.FastPlotInstance = obj; + ud.FastSenseInstance = obj; set(h, 'UserData', ud); obj.Lines(i).hLine = h; if obj.Verbose - fprintf('[FastPlot] render: line %d: %d pts -> %d displayed (pw=%d)\n', ... + fprintf('[FastSense] render: line %d: %d pts -> %d displayed (pw=%d)\n', ... i, nPtsLine, numel(xd), obj.PixelWidth); end if ~isempty(progressBar) @@ -1156,7 +1156,7 @@ function render(obj, progressBar) if ~obj.lineOnDisk(i) nNonPos = sum(obj.Lines(i).Y <= 0); if nNonPos > 0 - fprintf('[FastPlot] warning: line %d has %d non-positive values (clipped on log scale)\n', i, nNonPos); + fprintf('[FastSense] warning: line %d has %d non-positive values (clipped on log scale)\n', i, nNonPos); end end end @@ -1182,7 +1182,7 @@ function render(obj, progressBar) 'LineWidth', 1.5, ... 'HandleVisibility', 'off'); end - udT.FastPlot = struct( ... + udT.FastSense = struct( ... 'Type', 'threshold', ... 'Name', T.Label, ... 'LineIndex', [], ... @@ -1230,7 +1230,7 @@ function render(obj, progressBar) 'LineStyle', 'none', 'Marker', '.', ... 'MarkerSize', 6, 'Color', T.Color, ... 'HandleVisibility', 'off'); - udM.FastPlot = struct( ... + udM.FastSense = struct( ... 'Type', 'violation_marker', ... 'Name', T.Label, ... 'LineIndex', [], ... @@ -1240,10 +1240,10 @@ function render(obj, progressBar) if obj.Verbose if isempty(T.X) - fprintf('[FastPlot] render: threshold %.4g: %d violation markers\n', ... + fprintf('[FastSense] render: threshold %.4g: %d violation markers\n', ... T.Value, numel(vxAll)); else - fprintf('[FastPlot] render: time-varying threshold "%s": %d violation markers\n', ... + fprintf('[FastSense] render: time-varying threshold "%s": %d violation markers\n', ... T.Label, numel(vxAll)); end end @@ -1259,7 +1259,7 @@ function render(obj, progressBar) 'MarkerSize', M.MarkerSize, ... 'Color', M.Color, ... 'HandleVisibility', 'off'); - udM.FastPlot = struct( ... + udM.FastSense = struct( ... 'Type', 'marker', ... 'Name', M.Label, ... 'LineIndex', [], ... @@ -1336,7 +1336,7 @@ function render(obj, progressBar) % when visible lines are downsampled to a zoomed sub-range. line(obj.hAxes, [xmin xmax], [NaN NaN], ... 'HandleVisibility', 'off', 'Visible', 'off', ... - 'Tag', 'FastPlotAnchor', ... + 'Tag', 'FastSenseAnchor', ... 'XLimInclude', 'on', 'YLimInclude', 'off'); % --- Install listeners --- @@ -1415,13 +1415,13 @@ function render(obj, progressBar) if obj.Verbose totalPts = 0; for i = 1:numel(obj.Lines); totalPts = totalPts + obj.lineNumPoints(i); end - fprintf('[FastPlot] render complete: %d total pts, pw=%d, %.3fs\n', ... + fprintf('[FastSense] render complete: %d total pts, pw=%d, %.3fs\n', ... totalPts, obj.PixelWidth, toc(renderTic)); end % Register in link group if ~isempty(obj.LinkGroup) - FastPlot.getLinkRegistry('register', obj.LinkGroup, obj); + FastSense.getLinkRegistry('register', obj.LinkGroup, obj); end hold(obj.hAxes, 'off'); @@ -1545,11 +1545,11 @@ function updateData(obj, lineIdx, newX, newY, varargin) % See also startLive, render, addLine. if ~obj.IsRendered - error('FastPlot:notRendered', ... + error('FastSense:notRendered', ... 'Cannot update data before render() has been called.'); end if lineIdx < 1 || lineIdx > numel(obj.Lines) - error('FastPlot:indexOutOfRange', ... + error('FastSense:indexOutOfRange', ... 'Line index %d is out of range (1-%d).', lineIdx, numel(obj.Lines)); end @@ -1558,7 +1558,7 @@ function updateData(obj, lineIdx, newX, newY, varargin) if ~isrow(newY); newY = newY(:)'; end if numel(newX) ~= numel(newY) - error('FastPlot:sizeMismatch', ... + error('FastSense:sizeMismatch', ... 'X and Y must have the same number of elements.'); end @@ -1587,7 +1587,7 @@ function updateData(obj, lineIdx, newX, newY, varargin) % Replace raw data (decide storage mode) nPtsNew = numel(newX); if obj.shouldUseDisk(nPtsNew) - obj.Lines(lineIdx).DataStore = FastPlotDataStore(newX, newY); + obj.Lines(lineIdx).DataStore = FastSenseDataStore(newX, newY); obj.Lines(lineIdx).X = []; obj.Lines(lineIdx).Y = []; else @@ -1661,7 +1661,7 @@ function startLive(obj, filepath, updateFcn, varargin) obj.stopRefineTimer(); if ~obj.IsRendered - error('FastPlot:notRendered', ... + error('FastSense:notRendered', ... 'Cannot start live mode before render() has been called.'); end @@ -1724,7 +1724,7 @@ function startLive(obj, filepath, updateFcn, varargin) set(obj.hFigure, 'DeleteFcn', @(s,e) obj.onFigureCloseLive(existingDeleteFcn, s, e)); if obj.Verbose - fprintf('[FastPlot] live mode started: %s (%.1fs interval, %s mode)\n', ... + fprintf('[FastSense] live mode started: %s (%.1fs interval, %s mode)\n', ... filepath, obj.LiveInterval, obj.LiveViewMode); end end @@ -1755,13 +1755,13 @@ function stopLive(obj) end if obj.Verbose - fprintf('[FastPlot] live mode stopped\n'); + fprintf('[FastSense] live mode stopped\n'); end end function delete(obj) %DELETE Clean up timers and release resources. - % Called automatically when the FastPlot object is destroyed + % Called automatically when the FastSense object is destroyed % (e.g., cleared from workspace or figure closed). Stops the % refinement timer and live polling timer to prevent dangling % timer callbacks referencing deleted objects. @@ -1793,11 +1793,11 @@ function refresh(obj) % % See also startLive, onLiveTimer. if isempty(obj.LiveFile) || isempty(obj.LiveUpdateFcn) - error('FastPlot:noLiveSource', ... + error('FastSense:noLiveSource', ... 'No live source configured. Call startLive() first or set LiveFile and LiveUpdateFcn.'); end if ~exist(obj.LiveFile, 'file') - warning('FastPlot:fileNotFound', 'Live file not found: %s', obj.LiveFile); + warning('FastSense:fileNotFound', 'Live file not found: %s', obj.LiveFile); return; end @@ -1806,7 +1806,7 @@ function refresh(obj) obj.LiveUpdateFcn(obj, data); catch e if obj.Verbose - fprintf('[FastPlot] refresh error: %s\n', e.message); + fprintf('[FastSense] refresh error: %s\n', e.message); end end @@ -1869,7 +1869,7 @@ function runLive(obj) cleanupObj = onCleanup(@() obj.stopLive()); if obj.Verbose - fprintf('[FastPlot] runLive: entering poll loop (%.1fs interval)\n', obj.LiveInterval); + fprintf('[FastSense] runLive: entering poll loop (%.1fs interval)\n', obj.LiveInterval); end while obj.LiveIsActive && ishandle(obj.hFigure) @@ -1882,13 +1882,13 @@ function runLive(obj) data = load(obj.LiveFile); obj.LiveUpdateFcn(obj, data); if obj.Verbose - fprintf('[FastPlot] live update: %s\n', datestr(now, 'HH:MM:SS')); + fprintf('[FastSense] live update: %s\n', datestr(now, 'HH:MM:SS')); end end end catch e if obj.Verbose - fprintf('[FastPlot] runLive error: %s\n', e.message); + fprintf('[FastSense] runLive error: %s\n', e.message); end end drawnow; @@ -1896,7 +1896,7 @@ function runLive(obj) end if obj.Verbose - fprintf('[FastPlot] runLive: exited poll loop\n'); + fprintf('[FastSense] runLive: exited poll loop\n'); end end @@ -1914,7 +1914,7 @@ function setLineMetadata(obj, lineIdx, meta) %SETLINEMETADATA Set metadata on a line after construction. % fp.SETLINEMETADATA(lineIdx, meta) attaches or replaces the % metadata struct on the specified line. Primarily used by - % FastPlotGrid to attach metadata loaded from a separate + % FastSenseGrid to attach metadata loaded from a separate % file after the plot has been rendered. % % The metadata struct should contain a .datenum field with @@ -1928,7 +1928,7 @@ function setLineMetadata(obj, lineIdx, meta) % meta = struct('datenum', [1 5], 'batch', {{'A','B'}}); % fp.setLineMetadata(1, meta); % - % See also lookupMetadata, addLine, FastPlotGrid. + % See also lookupMetadata, addLine, FastSenseGrid. if lineIdx >= 1 && lineIdx <= numel(obj.Lines) obj.Lines(lineIdx).Metadata = meta; end @@ -1953,7 +1953,7 @@ function setViolationsVisible(obj, on) % fp.setViolationsVisible(false); % hide all markers % fp.setViolationsVisible(true); % show and recompute % - % See also updateViolations, addThreshold, FastPlotToolbar. + % See also updateViolations, addThreshold, FastSenseToolbar. obj.ViolationsVisible = on; if on obj.CachedViolationLim = []; % force recompute @@ -1978,12 +1978,12 @@ function setViolationsVisible(obj, on) function openLoupe(obj) %OPENLOUPE Open a standalone enlarged copy of this tile. - % fp.OPENLOUPE() creates a new FastPlot in a separate figure + % fp.OPENLOUPE() creates a new FastSense in a separate figure % containing deep copies of all lines, thresholds, bands, % shadings, and markers from the current plot. The new figure % preserves the current zoom state (XLim/YLim), is offset % by [+30, -30] pixels from the source figure, and receives - % its own FastPlotToolbar. + % its own FastSenseToolbar. % % The loupe uses DeferDraw=true during render so the figure % appears once with correct zoom rather than flashing at @@ -1996,13 +1996,13 @@ function openLoupe(obj) % Example: % fp.openLoupe(); % or double-click on the axes % - % See also onAxesDoubleClick, loupeButtonFilter, FastPlotToolbar. + % See also onAxesDoubleClick, loupeButtonFilter, FastSenseToolbar. % Use the source tile's title as the loupe figure name title_str = get(get(obj.hAxes, 'Title'), 'String'); - if isempty(title_str); title_str = 'FastPlot'; end + if isempty(title_str); title_str = 'FastSense'; end - % Deep-copy all data into a fresh FastPlot instance - fp = FastPlot(); + % Deep-copy all data into a fresh FastSense instance + fp = FastSense(); fp.Theme = obj.Theme; % Copy lines (converting datenum back to datetime if needed) @@ -2078,8 +2078,8 @@ function openLoupe(obj) srcPos = get(ancestor(obj.hAxes, 'figure'), 'Position'); set(fp.hFigure, 'Position', srcPos + [30 -30 0 0]); - tb = FastPlotToolbar(fp); - setappdata(fp.hFigure, 'FastPlotToolbar', tb); + tb = FastSenseToolbar(fp); + setappdata(fp.hFigure, 'FastSenseToolbar', tb); set(fp.hFigure, 'Visible', 'on'); drawnow; @@ -2156,12 +2156,12 @@ function onLiveTimer(obj) obj.LiveUpdateFcn(obj, data); obj.drawnowLimitRate(); if obj.Verbose - fprintf('[FastPlot] live update: %s\n', datestr(now, 'HH:MM:SS')); + fprintf('[FastSense] live update: %s\n', datestr(now, 'HH:MM:SS')); end end catch e if obj.Verbose - fprintf('[FastPlot] live timer error: %s\n', e.message); + fprintf('[FastSense] live timer error: %s\n', e.message); end end @@ -2262,11 +2262,11 @@ function applyViewMode(obj, newX) function applyTheme(obj) %APPLYTHEME Apply the current Theme struct to axes and figure. % APPLYTHEME(obj) configures the figure background (only when - % FastPlot owns the figure), axes background, foreground + % FastSense owns the figure), axes background, foreground % colors, font name/size, and grid style from the Theme % struct. Called during render() and by reapplyTheme(). % - % See also reapplyTheme, render, FastPlotTheme. + % See also reapplyTheme, render, FastSenseTheme. t = obj.Theme; % Figure background (only if we own the figure) @@ -2341,7 +2341,7 @@ function refineLines(obj) set(L.hLine, 'XData', xd, 'YData', yd); if obj.Verbose - fprintf('[FastPlot] refine: line %d: %d pts -> %d displayed\n', ... + fprintf('[FastSense] refine: line %d: %d pts -> %d displayed\n', ... i, nPtsI, numel(xd)); end end @@ -2439,7 +2439,7 @@ function onXLimChanged(obj, ~, ~) obj.CachedXLim = newXLim; if obj.Verbose - fprintf('[FastPlot] onXLimChanged: range=[%.4g, %.4g]\n', newXLim(1), newXLim(2)); + fprintf('[FastSense] onXLimChanged: range=[%.4g, %.4g]\n', newXLim(1), newXLim(2)); end obj.updateLines(); @@ -2547,7 +2547,7 @@ function deferredXLimCheck(obj) function onResize(obj, ~, ~) %ONRESIZE Re-downsample all data when the figure is resized. % ONRESIZE(obj, src, evt) is the figure ResizeFcn callback - % installed during render() (only when FastPlot owns the + % installed during render() (only when FastSense owns the % figure). Reads the new axes pixel width; if it changed, % re-downsamples all lines, shadings, and violations at the % new resolution and flushes the display. @@ -2560,7 +2560,7 @@ function onResize(obj, ~, ~) newPW = obj.getAxesPixelWidth(); if newPW ~= obj.PixelWidth if obj.Verbose - fprintf('[FastPlot] onResize: pw %d -> %d\n', obj.PixelWidth, newPW); + fprintf('[FastSense] onResize: pw %d -> %d\n', obj.PixelWidth, newPW); end obj.PixelWidth = newPW; obj.updateLines(); @@ -2752,7 +2752,7 @@ function updateLines(obj) else pyrStr = 'raw'; end - fprintf('[FastPlot] line %d: %d visible -> %d displayed (%s, pw=%d)\n', ... + fprintf('[FastSense] line %d: %d visible -> %d displayed (%s, pw=%d)\n', ... i, nVis, numel(xd), pyrStr, pw); end end @@ -2953,7 +2953,7 @@ function updateViolations(obj) 'LineStyle', 'none', 'Marker', '.', ... 'MarkerSize', 6, 'Color', T.Color, ... 'HandleVisibility', 'off'); - udM.FastPlot = struct( ... + udM.FastSense = struct( ... 'Type', 'violation_marker', ... 'Name', T.Label, ... 'LineIndex', [], ... @@ -2998,13 +2998,13 @@ function updateViolations(obj) vyAll = [vyCell{1:nViols}]; set(obj.Thresholds(t).hMarkers, 'XData', vxAll, 'YData', vyAll); if obj.Verbose - fprintf('[FastPlot] violations T%d: %d markers\n', ... + fprintf('[FastSense] violations T%d: %d markers\n', ... t, numel(vxAll)); end else set(obj.Thresholds(t).hMarkers, 'XData', NaN, 'YData', NaN); if obj.Verbose - fprintf('[FastPlot] violations T%d: 0 markers\n', t); + fprintf('[FastSense] violations T%d: 0 markers\n', t); end end end @@ -3090,7 +3090,7 @@ function drawnowLimitRate(obj) function propagateXLim(obj, newXLim) %PROPAGATEXLIM Sync XLim to all members of the link group. - % PROPAGATEXLIM(obj, newXLim) retrieves all FastPlot instances + % PROPAGATEXLIM(obj, newXLim) retrieves all FastSense instances % registered in the same LinkGroup, then sets each one's XLim % to newXLim and re-downsamples its lines, shadings, and % violations. Each member's IsPropagating flag is set to true @@ -3100,7 +3100,7 @@ function propagateXLim(obj, newXLim) % newXLim — 1-by-2 vector [xmin xmax] to apply % % See also onXLimChanged, getLinkRegistry. - members = FastPlot.getLinkRegistry('get', obj.LinkGroup, []); + members = FastSense.getLinkRegistry('get', obj.LinkGroup, []); for i = 1:numel(members) other = members{i}; if other.hAxes ~= obj.hAxes && ishandle(other.hAxes) @@ -3117,13 +3117,13 @@ function propagateXLim(obj, newXLim) end % ======================== STATIC HELPERS ============================= - % Persistent registry for linked FastPlot groups. + % Persistent registry for linked FastSense groups. methods (Static, Access = private) function registry = getLinkRegistry(action, group, obj) - %GETLINKREGISTRY Persistent registry for linked FastPlot instances. + %GETLINKREGISTRY Persistent registry for linked FastSense instances. % registry = GETLINKREGISTRY(action, group, obj) manages a % persistent struct that maps group names to cell arrays of - % FastPlot handles. Group names are sanitized via + % FastSense handles. Group names are sanitized via % matlab.lang.makeValidName. % % Actions: @@ -3134,10 +3134,10 @@ function propagateXLim(obj, newXLim) % Inputs: % action — 'register', 'get', or 'cleanup' % group — string group name (from LinkGroup property) - % obj — FastPlot instance (used for 'register' only) + % obj — FastSense instance (used for 'register' only) % % Output: - % registry — cell array of FastPlot handles (for 'get'), + % registry — cell array of FastSense handles (for 'get'), % or [] for other actions % % See also propagateXLim, render. @@ -3178,19 +3178,19 @@ function propagateXLim(obj, newXLim) methods (Static) function fp = plot(x, y, varargin) %PLOT One-liner convenience for quick plotting. - % FastPlot.plot(x, y) - % FastPlot.plot(x, y, 'DisplayName', 'Signal', 'Theme', 'dark') + % FastSense.plot(x, y) + % FastSense.plot(x, y, 'DisplayName', 'Signal', 'Theme', 'dark') % - % Creates a FastPlot, adds a single line, and renders immediately. - % Returns the FastPlot handle for further customization. + % Creates a FastSense, adds a single line, and renders immediately. + % Returns the FastSense handle for further customization. % % For multi-line plots or advanced features, use the builder pattern: - % fp = FastPlot(); + % fp = FastSense(); % fp.addLine(x1, y1); % fp.addLine(x2, y2); % fp.render(); - % Separate FastPlot constructor args from addLine args + % Separate FastSense constructor args from addLine args fpArgs = {}; lineArgs = {}; fpProps = {'Parent', 'LinkGroup', 'Theme', 'Verbose', ... @@ -3208,32 +3208,32 @@ function propagateXLim(obj, newXLim) end end - fp = FastPlot(fpArgs{:}); + fp = FastSense(fpArgs{:}); fp.addLine(x, y, lineArgs{:}); fp.render(); end function resetDefaults() - %RESETDEFAULTS Force reload of FastPlotDefaults on next use. - % FastPlot.RESETDEFAULTS() clears the cached defaults struct - % so the next FastPlot constructor will re-read - % FastPlotDefaults.m. Useful after editing the defaults file + %RESETDEFAULTS Force reload of FastSenseDefaults on next use. + % FastSense.RESETDEFAULTS() clears the cached defaults struct + % so the next FastSense constructor will re-read + % FastSenseDefaults.m. Useful after editing the defaults file % in a running session. % % Example: - % FastPlot.resetDefaults(); - % fp = FastPlot(); % picks up new defaults + % FastSense.resetDefaults(); + % fp = FastSense(); % picks up new defaults % - % See also FastPlot, getDefaults, clearDefaultsCache. + % See also FastSense, getDefaults, clearDefaultsCache. clearDefaultsCache(); end function distFig(varargin) %DISTFIG Distribute figure windows across the screen. - % FastPlot.DISTFIG() auto-arranges all open figure windows + % FastSense.DISTFIG() auto-arranges all open figure windows % in a grid that fills the screen. Figures are sorted by % number and tiled left-to-right, top-to-bottom. - % FastPlot.DISTFIG('Rows', 2, 'Cols', 3) uses a 2-by-3 grid. + % FastSense.DISTFIG('Rows', 2, 'Cols', 3) uses a 2-by-3 grid. % % Delegates to the vendored distFig function (in vendor/). % Adds the vendor path automatically if not already on path. @@ -3244,10 +3244,10 @@ function distFig(varargin) % 'Cols' — number of grid columns % % Example: - % FastPlot.distFig(); - % FastPlot.distFig('Rows', 2, 'Cols', 3); + % FastSense.distFig(); + % FastSense.distFig('Rows', 2, 'Cols', 3); % - % See also FastPlotDock. + % See also FastSenseDock. vendorDir = fullfile(fileparts(mfilename('fullpath')), 'vendor', 'distFig'); if ~exist('distFig', 'file') addpath(vendorDir); diff --git a/libs/FastPlot/FastPlotDataStore.m b/libs/FastSense/FastSenseDataStore.m similarity index 97% rename from libs/FastPlot/FastPlotDataStore.m rename to libs/FastSense/FastSenseDataStore.m index 96b82327..eb280b46 100644 --- a/libs/FastPlot/FastPlotDataStore.m +++ b/libs/FastSense/FastSenseDataStore.m @@ -1,5 +1,5 @@ -classdef FastPlotDataStore < handle - %FASTPLOTDATASTORE SQLite-backed data storage for large time series. +classdef FastSenseDataStore < handle + %FASTSENSEDATASTORE SQLite-backed data storage for large time series. % Stores X/Y data in a temporary SQLite database via mksqlite using % chunked typed BLOBs for fast bulk insert and range-based retrieval. % This avoids loading full datasets into MATLAB memory, preventing @@ -18,7 +18,7 @@ % storage (extra columns require mksqlite). % % Usage: - % ds = FastPlotDataStore(x, y); + % ds = FastSenseDataStore(x, y); % [xVis, yVis] = ds.getRange(xMin, xMax); % [xSlice, ySlice] = ds.readSlice(1000, 2000); % @@ -28,7 +28,7 @@ % vals = ds.getColumnSlice('labels', 1, 100); % names = ds.listColumns(); % - % See also FastPlot, FastPlotDefaults, mksqlite. + % See also FastSense, FastSenseDefaults, mksqlite. properties (SetAccess = private) NumPoints = 0 @@ -52,8 +52,8 @@ end methods (Access = public) - function obj = FastPlotDataStore(x, y) - %FASTPLOTDATASTORE Create a disk-backed store from X/Y arrays. + function obj = FastSenseDataStore(x, y) + %FASTSENSEDATASTORE Create a disk-backed store from X/Y arrays. if nargin < 2; return; end obj.NumPoints = numel(x); @@ -75,7 +75,7 @@ % Pre-compute L1 minmax pyramid while data is in memory. % This avoids re-reading the full dataset from disk on first - % render. Reduction factor 100 matches FastPlot.PyramidReduction. + % render. Reduction factor 100 matches FastSense.PyramidReduction. obj.buildPyramidFromMemory(x, y, 100); end @@ -118,18 +118,18 @@ function addColumn(obj, name, data) % Categorical arrays auto-convert to codes+categories struct. % String arrays auto-convert to cell of char. if ~obj.UseSqlite - error('FastPlotDataStore:noSqlite', ... + error('FastSenseDataStore:noSqlite', ... 'addColumn requires mksqlite (SQLite backend).'); end obj.ensureOpen(); if ~obj.IsValid - error('FastPlotDataStore:notValid', ... + error('FastSenseDataStore:notValid', ... 'DataStore is not initialized.'); end % Auto-convert types if isa(data, 'categorical') - data = FastPlotDataStore.fromCategorical(data); + data = FastSenseDataStore.fromCategorical(data); end if isa(data, 'string') data = cellstr(data); @@ -138,7 +138,7 @@ function addColumn(obj, name, data) % Validate length nData = columnLength(data); if nData ~= obj.NumPoints - error('FastPlotDataStore:sizeMismatch', ... + error('FastSenseDataStore:sizeMismatch', ... 'Column data length (%d) must match NumPoints (%d).', ... nData, obj.NumPoints); end @@ -157,7 +157,7 @@ function addColumn(obj, name, data) end if any(strcmp(name, obj.ColumnNames)) - error('FastPlotDataStore:duplicateColumn', ... + error('FastSenseDataStore:duplicateColumn', ... 'Column ''%s'' already exists.', name); end @@ -381,7 +381,7 @@ function disableWAL(obj) function c = toCategorical(s) %TOCATEGORICAL Convert a codes+categories struct back to categorical. if ~isCategoricalStruct(s) - error('FastPlotDataStore:badInput', ... + error('FastSenseDataStore:badInput', ... 'Input must be a struct with ''codes'' and ''categories'' fields.'); end catNames = s.categories(:)'; @@ -395,7 +395,7 @@ function disableWAL(obj) function c = fromCategorical(data) %FROMCATEGORICAL Convert a MATLAB categorical to codes+categories struct. if ~isa(data, 'categorical') - error('FastPlotDataStore:badInput', ... + error('FastSenseDataStore:badInput', ... 'Input must be a categorical array.'); end catNames = categories(data); @@ -545,7 +545,7 @@ function initSqlite(obj, x, y) obj.IsValid = true; return; catch me - warning('FastPlot:build_store_mex:fallback', ... + warning('FastSense:build_store_mex:fallback', ... 'build_store_mex failed (%s), falling back to MATLAB path.', me.message); if exist(obj.DbPath, 'file'); delete(obj.DbPath); end end @@ -750,7 +750,7 @@ function initBinaryFallback(obj, x, y) obj.BinPath = [tempname, '.fpdat']; fid = fopen(obj.BinPath, 'wb'); if fid == -1 - error('FastPlotDataStore:fileError', ... + error('FastSenseDataStore:fileError', ... 'Cannot create temp file: %s', obj.BinPath); end try @@ -790,7 +790,7 @@ function initBinaryFallback(obj, x, y) function [xOut, yOut] = readSliceBinary(obj, startIdx, endIdx) fid = fopen(obj.BinPath, 'rb'); if fid == -1 - error('FastPlotDataStore:fileError', ... + error('FastSenseDataStore:fileError', ... 'Cannot read temp file: %s', obj.BinPath); end count = endIdx - startIdx + 1; diff --git a/libs/FastPlot/FastPlotDefaults.m b/libs/FastSense/FastSenseDefaults.m similarity index 93% rename from libs/FastPlot/FastPlotDefaults.m rename to libs/FastSense/FastSenseDefaults.m index 06c62294..43b076e6 100644 --- a/libs/FastPlot/FastPlotDefaults.m +++ b/libs/FastSense/FastSenseDefaults.m @@ -1,7 +1,7 @@ -function cfg = FastPlotDefaults() -%FASTPLOTDEFAULTS User-editable default settings for FastPlot. -% cfg = FASTPLOTDEFAULTS() returns a struct of global defaults used by -% FastPlot, FastPlotGrid, and FastPlotDock. Edit this file to +function cfg = FastSenseDefaults() +%FASTSENSEDEFAULTS User-editable default settings for FastSense. +% cfg = FASTSENSEDEFAULTS() returns a struct of global defaults used by +% FastSense, FastSenseGrid, and FastSenseDock. Edit this file to % customize behavior project-wide. % % Values are loaded once per MATLAB session via getDefaults() and cached @@ -77,16 +77,16 @@ % SensorDetailPlot (default: 0.20) % % Example — switch to dark theme with LTTB downsampling: -% cfg = FastPlotDefaults(); +% cfg = FastSenseDefaults(); % cfg.Theme = 'dark'; % cfg.DefaultDownsampleMethod = 'lttb'; % % Example — widen tile gaps for a spacious dashboard: -% cfg = FastPlotDefaults(); +% cfg = FastSenseDefaults(); % cfg.DashboardGapH = 0.06; % cfg.DashboardGapV = 0.10; % -% See also getDefaults, clearDefaultsCache, FastPlotTheme. +% See also getDefaults, clearDefaultsCache, FastSenseTheme. % --- Theme --- cfg.Theme = 'default'; % preset name or struct diff --git a/libs/FastPlot/FastPlotDock.m b/libs/FastSense/FastSenseDock.m similarity index 93% rename from libs/FastPlot/FastPlotDock.m rename to libs/FastSense/FastSenseDock.m index 946ccf00..2560565e 100644 --- a/libs/FastPlot/FastPlotDock.m +++ b/libs/FastSense/FastSenseDock.m @@ -1,27 +1,27 @@ -classdef FastPlotDock < handle - %FASTPLOTDOCK Tabbed container for multiple FastPlotGrid dashboards. - % Manages multiple FastPlotGrid instances as switchable tabs in a +classdef FastSenseDock < handle + %FASTSENSEDOCK Tabbed container for multiple FastSenseGrid dashboards. + % Manages multiple FastSenseGrid instances as switchable tabs in a % single window. Each tab has its own panel, toolbar, close button, % and undock button. Tabs can be dynamically added, removed, or % popped out into standalone figures. % - % dock = FastPlotDock() - % dock = FastPlotDock('Theme', 'dark') - % dock = FastPlotDock('Theme', 'dark', 'Name', 'My Dock') + % dock = FastSenseDock() + % dock = FastSenseDock('Theme', 'dark') + % dock = FastSenseDock('Theme', 'dark', 'Name', 'My Dock') % % Constructor options (name-value): - % 'Theme' — theme preset name, struct, or FastPlotTheme + % 'Theme' — theme preset name, struct, or FastSenseTheme % Any additional name-value pairs are passed to figure(). % - % FastPlotDock Properties: - % Theme — FastPlotTheme struct applied to all tabs + % FastSenseDock Properties: + % Theme — FastSenseTheme struct applied to all tabs % hFigure — shared figure handle for the dock window % ShowProgress — show console progress bar during renderAll % TabBarHeight — normalized height of the tab bar (default 0.03) % - % FastPlotDock Methods: - % FastPlotDock — construct a tabbed dock container - % addTab — register a FastPlotGrid as a tab + % FastSenseDock Methods: + % FastSenseDock — construct a tabbed dock container + % addTab — register a FastSenseGrid as a tab % render — render active tab, create tab bar, show first tab % renderAll — eagerly render all tabs with hierarchical progress % selectTab — switch to tab n, rendering lazily if needed @@ -32,20 +32,20 @@ % delete — clean up dock: stop all live timers and close figure % % Example: - % dock = FastPlotDock('Theme', 'dark', 'Name', 'Dashboard'); - % fig1 = FastPlotGrid(2, 1, 'ParentFigure', dock.hFigure); + % dock = FastSenseDock('Theme', 'dark', 'Name', 'Dashboard'); + % fig1 = FastSenseGrid(2, 1, 'ParentFigure', dock.hFigure); % fig1.tile(1).addLine(x, y1); fig1.tile(2).addLine(x, y2); % dock.addTab(fig1, 'Temperature'); - % fig2 = FastPlotGrid(1, 1, 'ParentFigure', dock.hFigure); + % fig2 = FastSenseGrid(1, 1, 'ParentFigure', dock.hFigure); % fig2.tile(1).addLine(x, y3); % dock.addTab(fig2, 'Pressure'); % dock.render(); % - % See also FastPlotGrid, FastPlot, FastPlotToolbar, FastPlotTheme. + % See also FastSenseGrid, FastSense, FastSenseToolbar, FastSenseTheme. % ========================= PUBLIC PROPERTIES ========================= properties (Access = public) - Theme = [] % FastPlotTheme struct + Theme = [] % FastSenseTheme struct hFigure = [] % shared figure handle ShowProgress = true % show console progress bar during renderAll end @@ -53,7 +53,7 @@ % ====================== INTERNAL STATE =============================== properties (SetAccess = private) Tabs = struct('Name', {}, 'Figure', {}, 'Panel', {}, 'IsRendered', {}) - Toolbar = [] % shared FastPlotToolbar instance + Toolbar = [] % shared FastSenseToolbar instance ActiveTab = 0 % index of currently visible tab hTabButtons = {} % cell array of uicontrol handles hCloseButtons = {} % cell array of close button handles @@ -73,13 +73,13 @@ end methods (Access = public) - function obj = FastPlotDock(varargin) - %FASTPLOTDOCK Construct a tabbed dock container. - % dock = FastPlotDock() - % dock = FastPlotDock('Theme', 'dark', 'Name', 'My Dock') + function obj = FastSenseDock(varargin) + %FASTSENSEDOCK Construct a tabbed dock container. + % dock = FastSenseDock() + % dock = FastSenseDock('Theme', 'dark', 'Name', 'My Dock') % % Creates a figure with a tab bar. Use addTab() to register - % FastPlotGrid instances, then call render(). + % FastSenseGrid instances, then call render(). cfg = getDefaults(); obj.TabBarHeight = cfg.TabBarHeight; obj.MinTabWidth = cfg.MinTabWidth; @@ -93,12 +93,12 @@ 'Color', obj.Theme.Background, figOptsCell{:}); set(obj.hFigure, 'SizeChangedFcn', @(s,e) obj.recomputeLayout()); set(obj.hFigure, 'CloseRequestFcn', @(s,e) obj.onClose()); - setappdata(obj.hFigure, 'FastPlotDock', obj); + setappdata(obj.hFigure, 'FastSenseDock', obj); end function addTab(obj, fig, name) - %ADDTAB Register a FastPlotGrid as a tab. - % dock.addTab(fig, name) adds a FastPlotGrid as a new tab + %ADDTAB Register a FastSenseGrid as a tab. + % dock.addTab(fig, name) adds a FastSenseGrid as a new tab % in the dock. The figure's ParentFigure and hFigure are % redirected to the dock's shared figure. A uipanel is created % for the tab's content, offset below the tab bar. @@ -107,7 +107,7 @@ function addTab(obj, fig, name) % the new tab is rendered immediately and its button is added. % % Inputs: - % fig — FastPlotGrid instance to dock + % fig — FastSenseGrid instance to dock % name — display name for the tab button % % See also removeTab, undockTab, selectTab. @@ -147,7 +147,7 @@ function render(obj) %RENDER Render active tab, create tab bar, show first tab. % dock.render() renders only the first tab (lazy rendering), % creates tab bar buttons for all tabs, attaches a shared - % FastPlotToolbar, selects tab 1, and makes the figure visible. + % FastSenseToolbar, selects tab 1, and makes the figure visible. % Subsequent tabs are rendered on-demand when selectTab is called. % % See also renderAll, selectTab. @@ -173,7 +173,7 @@ function render(obj) obj.createTabBar(); % Create shared toolbar for first tab - obj.Toolbar = FastPlotToolbar(obj.Tabs(1).Figure); + obj.Toolbar = FastSenseToolbar(obj.Tabs(1).Figure); % Show the first tab obj.selectTab(1); @@ -225,7 +225,7 @@ function renderAll(obj) obj.createTabBar(); % Create shared toolbar, then show tab 1 - obj.Toolbar = FastPlotToolbar(obj.Tabs(1).Figure); + obj.Toolbar = FastSenseToolbar(obj.Tabs(1).Figure); obj.selectTab(1); set(obj.hFigure, 'Visible', 'on'); @@ -236,14 +236,14 @@ function selectTab(obj, n) %SELECTTAB Switch to tab n, rendering it lazily if needed. % dock.selectTab(n) hides the currently active tab, renders % tab n if it hasn't been rendered yet, rebinds the shared - % toolbar to the new tab's FastPlotGrid, and shows tab n. + % toolbar to the new tab's FastSenseGrid, and shows tab n. % % Input: % n — tab index (1 to numel(Tabs)) % % See also addTab, removeTab, render. if n < 1 || n > numel(obj.Tabs) - error('FastPlotDock:outOfBounds', ... + error('FastSenseDock:outOfBounds', ... 'Tab %d is out of range (1-%d).', n, numel(obj.Tabs)); end @@ -261,7 +261,7 @@ function selectTab(obj, n) if ~isempty(obj.Toolbar) obj.Toolbar.rebind(obj.Tabs(n).Figure); else - obj.Toolbar = FastPlotToolbar(obj.Tabs(n).Figure); + obj.Toolbar = FastSenseToolbar(obj.Tabs(n).Figure); end % Hide current tab @@ -349,7 +349,7 @@ function undockTab(obj, n) % dock.undockTab(n) creates a new standalone figure, stops % live mode, reparents all tile axes from the dock panel to % the new figure, recomputes tile positions for standalone - % layout, creates a fresh FastPlotToolbar, and removes the + % layout, creates a fresh FastSenseToolbar, and removes the % tab from the dock. The remaining dock tabs are reindexed % and the tab bar is rebuilt. % @@ -416,7 +416,7 @@ function undockTab(obj, n) obj.hUndockButtons(n) = []; % Create toolbar on the new standalone figure - FastPlotToolbar(fig); + FastSenseToolbar(fig); % Show the new figure (suppress MATLAB R2025b reparenting warnings) set(newFig, 'Visible', 'on'); @@ -549,10 +549,10 @@ function reapplyTheme(obj) %REAPPLYTHEME Re-apply theme to dock, tab bar, panels, and all tabs. % dock.reapplyTheme() updates the figure background, re-styles % all tab/undock/close buttons, updates panel backgrounds, and - % propagates the theme to every tab's FastPlotGrid (calling + % propagates the theme to every tab's FastSenseGrid (calling % reapplyTheme on rendered figures). % - % See also FastPlotGrid.reapplyTheme. + % See also FastSenseGrid.reapplyTheme. set(obj.hFigure, 'Color', obj.Theme.Background); for i = 1:numel(obj.hTabButtons) if ishandle(obj.hTabButtons{i}) @@ -619,7 +619,7 @@ function delete(obj) function reparentAxes(obj, idx) %REPARENTAXES Move all tile axes into the tab's panel. % reparentAxes(obj, idx) sets the Parent of every tile axes - % in tab idx's FastPlotGrid to the tab's uipanel. This + % in tab idx's FastSenseGrid to the tab's uipanel. This % ensures axes are clipped to the panel and hidden/shown % with it during tab switching. % @@ -637,7 +637,7 @@ function reparentAxes(obj, idx) function renderTab(obj, idx) %RENDERTAB Render a single tab: figure and axes reparenting. % renderTab(obj, idx) calls renderAll on the tab's - % FastPlotGrid, reparents the axes into the tab's panel, + % FastSenseGrid, reparents the axes into the tab's panel, % and marks the tab as rendered. % % Input: diff --git a/libs/FastPlot/FastPlotGrid.m b/libs/FastSense/FastSenseGrid.m similarity index 91% rename from libs/FastPlot/FastPlotGrid.m rename to libs/FastSense/FastSenseGrid.m index 835a15a8..164a3b7b 100644 --- a/libs/FastPlot/FastPlotGrid.m +++ b/libs/FastSense/FastSenseGrid.m @@ -1,24 +1,24 @@ -classdef FastPlotGrid < handle - %FASTPLOTGRID Tiled layout manager for FastPlot dashboards. - % Creates a grid of FastPlot tiles in a single figure window with +classdef FastSenseGrid < handle + %FASTSENSEGRID Tiled layout manager for FastSense dashboards. + % Creates a grid of FastSense tiles in a single figure window with % configurable spacing, per-tile theme overrides, and tile spanning. % Supports live mode that synchronizes file polling across all tiles. % % For widget-based dashboards with gauges, numbers, status indicators, % and edit mode, see DashboardEngine. % - % fig = FastPlotGrid(rows, cols) - % fig = FastPlotGrid(rows, cols, 'Theme', 'dark') - % fig = FastPlotGrid(rows, cols, 'ParentFigure', hFig) + % fig = FastSenseGrid(rows, cols) + % fig = FastSenseGrid(rows, cols, 'Theme', 'dark') + % fig = FastSenseGrid(rows, cols, 'ParentFigure', hFig) % % Constructor options (name-value): - % 'Theme' — theme preset name, struct, or FastPlotTheme + % 'Theme' — theme preset name, struct, or FastSenseTheme % 'ParentFigure' — existing figure handle (skip figure creation) % Any additional name-value pairs are passed to figure(). % - % FastPlotGrid Properties: + % FastSenseGrid Properties: % Grid — [rows, cols] grid dimensions - % Theme — FastPlotTheme struct for all tiles + % Theme — FastSenseTheme struct for all tiles % hFigure — figure handle % ParentFigure — external figure handle (skip figure creation) % ContentOffset — [left bottom width height] normalized content area @@ -36,9 +36,9 @@ % GapH — horizontal gap between tiles (normalized) % GapV — vertical gap between tiles (normalized) % - % FastPlotGrid Methods: - % FastPlotGrid — construct a tiled dashboard - % tile — get or create the FastPlot instance for tile n + % FastSenseGrid Methods: + % FastSenseGrid — construct a tiled dashboard + % tile — get or create the FastSense instance for tile n % axes — get or create a raw MATLAB axes for tile n % setTileSpan — set the row/column span for tile n % setTileTheme — set per-tile theme overrides @@ -56,27 +56,27 @@ % computeTilePosition — calculate normalized [x y w h] for tile n % % Example — 2x2 dashboard: - % fig = FastPlotGrid(2, 2, 'Theme', 'dark'); + % fig = FastSenseGrid(2, 2, 'Theme', 'dark'); % fig.tile(1).addLine(x1, y1); fig.tile(2).addLine(x2, y2); % fig.tile(3).addLine(x3, y3); fig.tile(4).addLine(x4, y4); % fig.renderAll(); % % Example — tile spanning: - % fig = FastPlotGrid(2, 2); + % fig = FastSenseGrid(2, 2); % fig.setTileSpan(1, [1, 2]); % tile 1 spans full width % fig.tile(1).addLine(x, y); % fig.renderAll(); % % Example — mixed tile types: - % fig = FastPlotGrid(2, 2, 'Theme', 'dark'); - % fig.tile(1).addLine(t, y1); % FastPlot (optimized) + % fig = FastSenseGrid(2, 2, 'Theme', 'dark'); + % fig.tile(1).addLine(t, y1); % FastSense (optimized) % ax = fig.axes(2); bar(ax, x, y2); % raw axes (bar chart) % ax = fig.axes(3); scatter(ax, x, y3); % raw axes (scatter) - % fig.tile(4).addLine(t, y4); % FastPlot (optimized) + % fig.tile(4).addLine(t, y4); % FastSense (optimized) % fig.renderAll(); % % Example — embedding custom content in a tile: - % fig = FastPlotGrid(2, 2); + % fig = FastSenseGrid(2, 2); % fig.tile(1).addLine(x, y1); % hp = fig.tilePanel(2); % raw uipanel for custom content % uicontrol(hp, 'Style', 'text', 'String', 'KPI: 42.5', ... @@ -85,12 +85,12 @@ % fig.tile(3).addLine(x, y2); % fig.renderAll(); % - % See also FastPlot, FastPlotDock, FastPlotTheme, FastPlotToolbar, DashboardEngine. + % See also FastSense, FastSenseDock, FastSenseTheme, FastSenseToolbar, DashboardEngine. % ========================= PUBLIC PROPERTIES ========================= properties (Access = public) Grid = [1 1] % [rows, cols] - Theme = [] % FastPlotTheme struct + Theme = [] % FastSenseTheme struct hFigure = [] % figure handle ParentFigure = [] % external figure handle (skip figure creation) ContentOffset = [0 0 1 1] % [left bottom width height] normalized content area @@ -108,14 +108,14 @@ % ====================== INTERNAL STATE =============================== properties (SetAccess = private) - Tiles = {} % cell array of FastPlot instances + Tiles = {} % cell array of FastSense instances TileAxes = {} % cell array of axes handles TileSpans = {} % cell array of [rowSpan, colSpan] TileThemes = {} % cell array of theme override structs TileTitles = {} % cell array of buffered title strings TileXLabels = {} % cell array of buffered xlabel strings TileYLabels = {} % cell array of buffered ylabel strings - RawAxesTiles = [] % logical array: true = raw axes, false = FastPlot + RawAxesTiles = [] % logical array: true = raw axes, false = FastSense IsRendered = false LiveTimer = [] % timer object LiveFileDate = 0 % last known file datenum @@ -131,10 +131,10 @@ end methods (Access = public) - function obj = FastPlotGrid(rows, cols, varargin) - %FASTPLOTGRID Construct a tiled dashboard. - % fig = FastPlotGrid(rows, cols) - % fig = FastPlotGrid(rows, cols, 'Theme', 'dark') + function obj = FastSenseGrid(rows, cols, varargin) + %FASTSENSEGRID Construct a tiled dashboard. + % fig = FastSenseGrid(rows, cols) + % fig = FastSenseGrid(rows, cols, 'Theme', 'dark') % % rows, cols — grid dimensions % Remaining name-value pairs: 'Theme', 'ParentFigure', or @@ -181,8 +181,8 @@ end function fp = tile(obj, n) - %TILE Get or create the FastPlot instance for tile n. - % fp = fig.tile(n) returns the FastPlot for tile n, creating + %TILE Get or create the FastSense instance for tile n. + % fp = fig.tile(n) returns the FastSense for tile n, creating % it (and its axes) on first access. Tile themes are merged % from the figure theme and any per-tile overrides. % @@ -190,17 +190,17 @@ % n — tile index (1 to rows*cols, row-major order) % % Output: - % fp — FastPlot instance for the requested tile + % fp — FastSense instance for the requested tile % % See also setTileSpan, setTileTheme. nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotGrid:outOfBounds', ... + error('FastSenseGrid:outOfBounds', ... 'Tile %d is out of range (1-%d).', n, nTiles); end if obj.RawAxesTiles(n) - error('FastPlotGrid:tileConflict', ... + error('FastSenseGrid:tileConflict', ... 'Tile %d is a raw axes tile. Use axes(%d) to access it.', n, n); end @@ -214,7 +214,7 @@ tileTheme = mergeTheme(tileTheme, obj.TileThemes{n}); end - fp = FastPlot('Parent', ax, 'Theme', tileTheme); + fp = FastSense('Parent', ax, 'Theme', tileTheme); obj.Tiles{n} = fp; else fp = obj.Tiles{n}; @@ -224,12 +224,12 @@ function ax = axes(obj, n) %AXES Get or create a raw MATLAB axes for tile n. % ax = fig.axes(n) returns a themed MATLAB axes handle at the - % position for tile n. Use for non-FastPlot plot types (bar, + % position for tile n. Use for non-FastSense plot types (bar, % scatter, histogram, stem, etc.). The axes gets theme colors - % applied but no FastPlot optimization. + % applied but no FastSense optimization. % % Mutually exclusive with tile(n) — a tile cannot be both a - % FastPlot and a raw axes. + % FastSense and a raw axes. % % Input: % n — tile index (1 to rows*cols, row-major order) @@ -244,13 +244,13 @@ % See also tile, setTileSpan. nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotGrid:outOfBounds', ... + error('FastSenseGrid:outOfBounds', ... 'Tile %d is out of range (1-%d).', n, nTiles); end if ~isempty(obj.Tiles{n}) - error('FastPlotGrid:tileConflict', ... - 'Tile %d is a FastPlot tile. Use tile(%d) to access it.', n, n); + error('FastSenseGrid:tileConflict', ... + 'Tile %d is a FastSense tile. Use tile(%d) to access it.', n, n); end if isempty(obj.TileAxes{n}) @@ -270,11 +270,11 @@ % composite widgets (e.g. SensorDetailPlot) into a tile. % % Throws an error if tile n is already occupied by a - % FastPlot (via tile()) or raw axes (via axes()). + % FastSense (via tile()) or raw axes (via axes()). nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotGrid:invalidTile', ... + error('FastSenseGrid:invalidTile', ... 'Tile index %d is out of range [1, %d].', n, nTiles); end @@ -284,15 +284,15 @@ return; end - % Conflict check: occupied by FastPlot? + % Conflict check: occupied by FastSense? if ~isempty(obj.Tiles{n}) - error('FastPlotGrid:tileConflict', ... - 'Tile %d is a FastPlot tile. Use tile(%d) to access it.', n, n); + error('FastSenseGrid:tileConflict', ... + 'Tile %d is a FastSense tile. Use tile(%d) to access it.', n, n); end % Conflict check: occupied by raw axes? if obj.RawAxesTiles(n) - error('FastPlotGrid:tileConflict', ... + error('FastSenseGrid:tileConflict', ... 'Tile %d is a raw axes tile. Use axes(%d) to access it.', n, n); end @@ -305,7 +305,7 @@ % Attach theme so embedded widgets (e.g. SensorDetailPlot) can % auto-inherit the figure's theme without explicit Theme arg. - hp.UserData = struct('FastPlotTheme', obj.Theme); + hp.UserData = struct('FastSenseTheme', obj.Theme); % Store panel handle (reuses TileAxes cell for storage) obj.TileAxes{n} = hp; @@ -331,7 +331,7 @@ function setTileSpan(obj, n, span) % See also tile, computeTilePosition. nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotGrid:outOfBounds', ... + error('FastSenseGrid:outOfBounds', ... 'Tile %d is out of range (1-%d).', n, nTiles); end obj.TileSpans{n} = span; @@ -359,7 +359,7 @@ function setTileTheme(obj, n, themeOverrides) % See also tile, reapplyTheme. nTiles = obj.Grid(1) * obj.Grid(2); if n < 1 || n > nTiles - error('FastPlotGrid:outOfBounds', ... + error('FastSenseGrid:outOfBounds', ... 'Tile %d is out of range (1-%d).', n, nTiles); end obj.TileThemes{n} = themeOverrides; @@ -436,7 +436,7 @@ function renderAll(obj, parentProgressBar) % See also render, tile. if nargin < 2; parentProgressBar = []; end - % Collect FastPlot tiles that need rendering (skip raw axes tiles) + % Collect FastSense tiles that need rendering (skip raw axes tiles) tilesToRender = []; for i = 1:numel(obj.Tiles) if obj.RawAxesTiles(i) @@ -531,7 +531,7 @@ function reapplyTheme(obj) % % Use after changing fig.Theme to update all visuals. % - % See also setTileTheme, FastPlot.reapplyTheme. + % See also setTileTheme, FastSense.reapplyTheme. set(obj.hFigure, 'Color', obj.Theme.Background); for i = 1:numel(obj.Tiles) if obj.RawAxesTiles(i) @@ -592,7 +592,7 @@ function startLive(obj, filepath, updateFcn, varargin) obj.LiveViewMode = 'preserve'; end - % Set view mode on FastPlot tiles only (raw axes tiles are unmanaged) + % Set view mode on FastSense tiles only (raw axes tiles are unmanaged) for i = 1:numel(obj.Tiles) if ~obj.RawAxesTiles(i) && ~isempty(obj.Tiles{i}) obj.Tiles{i}.LiveViewMode = obj.LiveViewMode; @@ -653,11 +653,11 @@ function refresh(obj) % % See also startLive, stopLive. if isempty(obj.LiveFile) || isempty(obj.LiveUpdateFcn) - error('FastPlotGrid:noLiveSource', ... + error('FastSenseGrid:noLiveSource', ... 'No live source configured.'); end if ~exist(obj.LiveFile, 'file') - warning('FastPlotGrid:fileNotFound', 'File not found: %s', obj.LiveFile); + warning('FastSenseGrid:fileNotFound', 'File not found: %s', obj.LiveFile); return; end try @@ -818,7 +818,7 @@ function onFigureCloseLive(obj, existingDeleteFcn, src, evt) end function ax = getTileAxesHandle(obj, n) - %GETTILEAXESHANDLE Get the axes handle for tile n (FastPlot or raw). + %GETTILEAXESHANDLE Get the axes handle for tile n (FastSense or raw). if obj.RawAxesTiles(n) ax = obj.TileAxes{n}; if ~isempty(ax) && ~ishandle(ax); ax = []; end diff --git a/libs/FastPlot/FastPlotTheme.m b/libs/FastSense/FastSenseTheme.m similarity index 92% rename from libs/FastPlot/FastPlotTheme.m rename to libs/FastSense/FastSenseTheme.m index 2fba60a3..413d2114 100644 --- a/libs/FastPlot/FastPlotTheme.m +++ b/libs/FastSense/FastSenseTheme.m @@ -1,15 +1,15 @@ -function theme = FastPlotTheme(preset, varargin) -%FASTPLOTTHEME Return a theme struct for FastPlot styling. -% theme = FASTPLOTTHEME() returns the 'default' theme preset. +function theme = FastSenseTheme(preset, varargin) +%FASTSENSETHEME Return a theme struct for FastSense styling. +% theme = FASTSENSETHEME() returns the 'default' theme preset. % -% theme = FASTPLOTTHEME(preset) returns the named preset. Valid preset +% theme = FASTSENSETHEME(preset) returns the named preset. Valid preset % names are 'default', 'dark', 'light', 'industrial', 'scientific', % and 'ocean'. % -% theme = FASTPLOTTHEME(preset, Name, Value) returns the named preset +% theme = FASTSENSETHEME(preset, Name, Value) returns the named preset % with individual fields overridden by the supplied name-value pairs. % -% theme = FASTPLOTTHEME(S) where S is a struct uses S as a set of +% theme = FASTSENSETHEME(S) where S is a struct uses S as a set of % overrides on top of the 'default' preset. Missing fields are filled % from 'default'. % @@ -44,14 +44,14 @@ % BandAlpha — double in [0,1]; transparency of shaded bands % % Example: -% theme = FastPlotTheme('dark', 'FontSize', 14, 'LineWidth', 1.5); +% theme = FastSenseTheme('dark', 'FontSize', 14, 'LineWidth', 1.5); % % Example — struct override: % overrides.FontSize = 14; % overrides.GridAlpha = 0.5; -% theme = FastPlotTheme(overrides); +% theme = FastSenseTheme(overrides); % -% See also FastPlotDefaults, getDefaults. +% See also FastSenseDefaults, getDefaults. if nargin == 0 preset = 'default'; @@ -85,9 +85,9 @@ % 'scientific', 'ocean' (case-insensitive) % % Output: -% t — struct; fully populated theme (see FastPlotTheme for fields) +% t — struct; fully populated theme (see FastSenseTheme for fields) % -% Throws FastPlotTheme:unknownPreset if the name is not recognized. +% Throws FastSenseTheme:unknownPreset if the name is not recognized. switch lower(name) case 'default' t = struct( ... @@ -204,7 +204,7 @@ 'BandAlpha', 0.15 ... ); otherwise - error('FastPlotTheme:unknownPreset', ... + error('FastSenseTheme:unknownPreset', ... 'Unknown theme preset: ''%s''. Use ''default'', ''dark'', ''light'', ''industrial'', ''scientific'', or ''ocean''.', name); end end @@ -223,7 +223,7 @@ % Output: % colors — 8x3 double; each row is an [R G B] triplet in [0,1] % -% Throws FastPlotTheme:unknownPalette if the name is not recognized. +% Throws FastSenseTheme:unknownPalette if the name is not recognized. switch lower(name) case 'vibrant' colors = [ ... @@ -272,7 +272,7 @@ 0.16 0.62 0.24; ... % forest green ]; otherwise - error('FastPlotTheme:unknownPalette', ... + error('FastSenseTheme:unknownPalette', ... 'Unknown palette: ''%s''. Use ''vibrant'', ''muted'', ''colorblind'', or ''ocean''.', name); end end diff --git a/libs/FastPlot/FastPlotToolbar.m b/libs/FastSense/FastSenseToolbar.m similarity index 89% rename from libs/FastPlot/FastPlotToolbar.m rename to libs/FastSense/FastSenseToolbar.m index 0ae44910..dbae7acc 100644 --- a/libs/FastPlot/FastPlotToolbar.m +++ b/libs/FastSense/FastSenseToolbar.m @@ -1,12 +1,12 @@ -classdef FastPlotToolbar < handle - %FASTPLOTTOOLBAR Interactive toolbar for FastPlot and FastPlotGrid. +classdef FastSenseToolbar < handle + %FASTSENSETOOLBAR Interactive toolbar for FastSense and FastSenseGrid. % Adds a uitoolbar with data cursor, crosshair, grid/legend toggles, % Y-axis autoscale, PNG export, live mode controls, and metadata % display. Integrates with MATLAB's built-in datacursormode for % enhanced tooltips. % - % tb = FastPlotToolbar(fp) — attach to a FastPlot instance - % tb = FastPlotToolbar(fig) — attach to a FastPlotGrid instance + % tb = FastSenseToolbar(fp) — attach to a FastSense instance + % tb = FastSenseToolbar(fig) — attach to a FastSenseGrid instance % % Toolbar buttons: % Data Cursor — click to snap to nearest data point, shows value @@ -20,11 +20,11 @@ % Metadata — show/hide metadata in data cursor tooltips % Violations — toggle violation marker visibility % - % FastPlotToolbar Properties: + % FastSenseToolbar Properties: % MetadataEnabled — whether metadata fields are shown in tooltips % - % FastPlotToolbar Methods: - % FastPlotToolbar — construct and attach toolbar to a plot target + % FastSenseToolbar Methods: + % FastSenseToolbar — construct and attach toolbar to a plot target % toggleGrid — toggle grid visibility on all managed axes % toggleLegend — toggle legend visibility on all managed axes % autoscaleY — fit Y-axis limits to visible data on all axes @@ -44,12 +44,12 @@ % formatX — format an X value based on XType % % Example: - % fp = FastPlot('Parent', ax); + % fp = FastSense('Parent', ax); % fp.addLine(x, y); % fp.render(); - % tb = FastPlotToolbar(fp); + % tb = FastSenseToolbar(fp); % - % See also FastPlot, FastPlotGrid, FastPlotDock. + % See also FastSense, FastSenseGrid, FastSenseDock. % ========================= PUBLIC STATE ============================== properties (SetAccess = private, GetAccess = public) @@ -59,10 +59,10 @@ % ====================== INTERNAL STATE =============================== % Graphics handles, mode tracking, and saved callbacks. properties (SetAccess = private) - Target = [] % FastPlot or FastPlotGrid + Target = [] % FastSense or FastSenseGrid hFigure = [] % figure handle hToolbar = [] % uitoolbar handle - FastPlots = {} % cell array of all FastPlot instances + FastSenses = {} % cell array of all FastSense instances Mode = 'none' % 'none' | 'cursor' | 'crosshair' hCursorBtn = [] % uitoggletool handle hCrosshairBtn = [] % uitoggletool handle @@ -80,30 +80,30 @@ end methods (Access = public) - function obj = FastPlotToolbar(target) - %FASTPLOTTOOLBAR Construct and attach a toolbar to a plot target. - % tb = FastPlotToolbar(fp) — FastPlot instance - % tb = FastPlotToolbar(fig) — FastPlotGrid instance + function obj = FastSenseToolbar(target) + %FASTSENSETOOLBAR Construct and attach a toolbar to a plot target. + % tb = FastSenseToolbar(fp) — FastSense instance + % tb = FastSenseToolbar(fig) — FastSenseGrid instance % - % Resolves the figure handle, collects all FastPlot instances, + % Resolves the figure handle, collects all FastSense instances, % creates the uitoolbar, and installs the datacursor callback. obj.Target = target; - % Resolve figure handle and FastPlot instances - if isa(target, 'FastPlotGrid') + % Resolve figure handle and FastSense instances + if isa(target, 'FastSenseGrid') obj.hFigure = target.hFigure; - obj.FastPlots = {}; + obj.FastSenses = {}; for i = 1:numel(target.Tiles) if ~isempty(target.Tiles{i}) - obj.FastPlots{end+1} = target.Tiles{i}; + obj.FastSenses{end+1} = target.Tiles{i}; end end - elseif isa(target, 'FastPlot') + elseif isa(target, 'FastSense') obj.hFigure = target.hFigure; - obj.FastPlots = {target}; + obj.FastSenses = {target}; else - error('FastPlotToolbar:invalidTarget', ... - 'Target must be a FastPlot or FastPlotGrid instance.'); + error('FastSenseToolbar:invalidTarget', ... + 'Target must be a FastSense or FastSenseGrid instance.'); end obj.createToolbar(); @@ -112,22 +112,22 @@ function toggleGrid(obj) %TOGGLEGRID Toggle grid visibility on all managed axes. - for i = 1:numel(obj.FastPlots) - obj.toggleGridOnAxes(obj.FastPlots{i}.hAxes); + for i = 1:numel(obj.FastSenses) + obj.toggleGridOnAxes(obj.FastSenses{i}.hAxes); end end function toggleLegend(obj) %TOGGLELEGEND Toggle legend visibility on all managed axes. - for i = 1:numel(obj.FastPlots) - obj.toggleLegendOnAxes(obj.FastPlots{i}.hAxes); + for i = 1:numel(obj.FastSenses) + obj.toggleLegendOnAxes(obj.FastSenses{i}.hAxes); end end function autoscaleY(obj) %AUTOSCALEY Fit Y-axis limits to visible data on all axes. - for i = 1:numel(obj.FastPlots) - obj.autoscaleYOnAxes(obj.FastPlots{i}); + for i = 1:numel(obj.FastSenses) + obj.autoscaleYOnAxes(obj.FastSenses{i}); end end @@ -213,7 +213,7 @@ function toggleLive(obj) if isprop(target, 'MetadataLineIndex') args = [args, 'MetadataLineIndex', target.MetadataLineIndex]; end - if isprop(target, 'MetadataTileIndex') && isa(target, 'FastPlotGrid') + if isprop(target, 'MetadataTileIndex') && isa(target, 'FastSenseGrid') args = [args, 'MetadataTileIndex', target.MetadataTileIndex]; end target.startLive(target.LiveFile, target.LiveUpdateFcn, args{:}); @@ -227,7 +227,7 @@ function setMetadata(obj, on) % tb.setMetadata(true) — show metadata fields in cursor % tb.setMetadata(false) — hide metadata obj.MetadataEnabled = on; - setappdata(obj.hFigure, 'FastPlotMetadataEnabled', on); + setappdata(obj.hFigure, 'FastSenseMetadataEnabled', on); if on set(obj.hMetadataBtn, 'State', 'on'); else @@ -239,15 +239,15 @@ function setMetadata(obj, on) function setViolationsVisible(obj, on) %SETVIOLATIONSVISIBLE Toggle violation markers on all tiles. % setViolationsVisible(obj, on) iterates over all managed - % FastPlot instances and calls setViolationsVisible(on) on + % FastSense instances and calls setViolationsVisible(on) on % each, then syncs the toolbar toggle button state. % % Input: % on — logical, true to show markers, false to hide % - % See also FastPlot.setViolationsVisible. - for i = 1:numel(obj.FastPlots) - obj.FastPlots{i}.setViolationsVisible(on); + % See also FastSense.setViolationsVisible. + for i = 1:numel(obj.FastSenses) + obj.FastSenses{i}.setViolationsVisible(on); end if on set(obj.hViolationsBtn, 'State', 'on'); @@ -275,17 +275,17 @@ function rebind(obj, target) % Update target references obj.Target = target; - if isa(target, 'FastPlotGrid') + if isa(target, 'FastSenseGrid') obj.hFigure = target.hFigure; - obj.FastPlots = {}; + obj.FastSenses = {}; for i = 1:numel(target.Tiles) if ~isempty(target.Tiles{i}) - obj.FastPlots{end+1} = target.Tiles{i}; + obj.FastSenses{end+1} = target.Tiles{i}; end end - elseif isa(target, 'FastPlot') + elseif isa(target, 'FastSense') obj.hFigure = target.hFigure; - obj.FastPlots = {target}; + obj.FastSenses = {target}; end % Sync toggle states to new target @@ -294,12 +294,12 @@ function rebind(obj, target) else set(obj.hLiveBtn, 'State', 'off'); end - setappdata(obj.hFigure, 'FastPlotMetadataEnabled', obj.MetadataEnabled); + setappdata(obj.hFigure, 'FastSenseMetadataEnabled', obj.MetadataEnabled); % Sync violations toggle to first tile's state (all tiles % share the same ViolationsVisible after any toolbar action) - if ~isempty(obj.FastPlots) - if obj.FastPlots{1}.ViolationsVisible + if ~isempty(obj.FastSenses) + if obj.FastSenses{1}.ViolationsVisible set(obj.hViolationsBtn, 'State', 'on'); else set(obj.hViolationsBtn, 'State', 'off'); @@ -377,68 +377,68 @@ function createToolbar(obj) % Creates toggle and push tools for cursor, crosshair, grid, % legend, autoscale, export, refresh, live mode, metadata, % violations, and theme selection. Pre-warms the icon cache. - FastPlotToolbar.initIcons(); + FastSenseToolbar.initIcons(); obj.hToolbar = uitoolbar(obj.hFigure); % Buttons: cursor, crosshair, grid, legend, autoscale, export obj.hCursorBtn = uitoggletool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('cursor'), ... + 'CData', FastSenseToolbar.makeIcon('cursor'), ... 'TooltipString', 'Data Cursor', ... 'OnCallback', @(s,e) obj.onCursorOn(), ... 'OffCallback', @(s,e) obj.onCursorOff()); obj.hCrosshairBtn = uitoggletool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('crosshair'), ... + 'CData', FastSenseToolbar.makeIcon('crosshair'), ... 'TooltipString', 'Crosshair', ... 'OnCallback', @(s,e) obj.onCrosshairOn(), ... 'OffCallback', @(s,e) obj.onCrosshairOff()); uipushtool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('grid'), ... + 'CData', FastSenseToolbar.makeIcon('grid'), ... 'TooltipString', 'Toggle Grid', ... 'ClickedCallback', @(s,e) obj.onToggleGrid()); uipushtool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('legend'), ... + 'CData', FastSenseToolbar.makeIcon('legend'), ... 'TooltipString', 'Toggle Legend', ... 'ClickedCallback', @(s,e) obj.onToggleLegend()); uipushtool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('autoscale'), ... + 'CData', FastSenseToolbar.makeIcon('autoscale'), ... 'TooltipString', 'Autoscale Y', ... 'ClickedCallback', @(s,e) obj.onAutoscaleY()); uipushtool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('export'), ... + 'CData', FastSenseToolbar.makeIcon('export'), ... 'TooltipString', 'Export PNG', ... 'ClickedCallback', @(s,e) obj.onExportPNG()); obj.hRefreshBtn = uipushtool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('refresh'), ... + 'CData', FastSenseToolbar.makeIcon('refresh'), ... 'TooltipString', 'Refresh Data', ... 'ClickedCallback', @(s,e) obj.onRefresh()); obj.hLiveBtn = uitoggletool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('live'), ... + 'CData', FastSenseToolbar.makeIcon('live'), ... 'TooltipString', 'Live Mode', ... 'OnCallback', @(s,e) obj.onLiveOn(), ... 'OffCallback', @(s,e) obj.onLiveOff()); obj.hMetadataBtn = uitoggletool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('metadata'), ... + 'CData', FastSenseToolbar.makeIcon('metadata'), ... 'TooltipString', 'Metadata', ... 'OnCallback', @(s,e) obj.onMetadataOn(), ... 'OffCallback', @(s,e) obj.onMetadataOff()); obj.hViolationsBtn = uitoggletool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('violations'), ... + 'CData', FastSenseToolbar.makeIcon('violations'), ... 'TooltipString', 'Toggle Violations', ... 'State', 'on', ... 'OnCallback', @(s,e) obj.onViolationsOn(), ... 'OffCallback', @(s,e) obj.onViolationsOff()); obj.hThemeBtn = uipushtool(obj.hToolbar, ... - 'CData', FastPlotToolbar.makeIcon('theme'), ... + 'CData', FastSenseToolbar.makeIcon('theme'), ... 'TooltipString', 'Change Theme', ... 'ClickedCallback', @(s,e) obj.onThemeClick()); end @@ -564,7 +564,7 @@ function onSelect(src) % name — theme name string (e.g. 'dark') or '' name = ''; target = obj.Target; - if isa(target, 'FastPlotGrid') || isa(target, 'FastPlot') + if isa(target, 'FastSenseGrid') || isa(target, 'FastSense') currentTheme = target.Theme; else return; @@ -574,7 +574,7 @@ function onSelect(src) % Check built-in presets presets = {'default', 'dark', 'light', 'industrial', 'scientific'}; for i = 1:numel(presets) - ref = FastPlotTheme(presets{i}); + ref = FastSenseTheme(presets{i}); if obj.themesEqual(currentTheme, ref) name = presets{i}; return; @@ -586,7 +586,7 @@ function onSelect(src) if isfield(cfg, 'CustomThemes') customs = fieldnames(cfg.CustomThemes); for i = 1:numel(customs) - ref = mergeTheme(FastPlotTheme('default'), cfg.CustomThemes.(customs{i})); + ref = mergeTheme(FastSenseTheme('default'), cfg.CustomThemes.(customs{i})); if obj.themesEqual(currentTheme, ref) name = customs{i}; return; @@ -629,9 +629,9 @@ function applyThemeByName(obj, name) %APPLYTHEMEBYNAME Resolve theme by name and apply to hierarchy. % applyThemeByName(obj, name) resolves a theme by name % (checking custom themes from getDefaults first, then - % built-in FastPlotTheme presets). The resolved theme is + % built-in FastSenseTheme presets). The resolved theme is % applied to the target and, if the target belongs to a - % FastPlotDock (via AppData), to the entire dock hierarchy. + % FastSenseDock (via AppData), to the entire dock hierarchy. % % Input: % name — theme name string (e.g. 'dark', 'scientific') @@ -639,23 +639,23 @@ function applyThemeByName(obj, name) % Resolve: check custom themes first, then built-in if isfield(cfg, 'CustomThemes') && isfield(cfg.CustomThemes, name) - newTheme = mergeTheme(FastPlotTheme('default'), cfg.CustomThemes.(name)); + newTheme = mergeTheme(FastSenseTheme('default'), cfg.CustomThemes.(name)); else - newTheme = FastPlotTheme(name); + newTheme = FastSenseTheme(name); end target = obj.Target; - if isa(target, 'FastPlotGrid') + if isa(target, 'FastSenseGrid') % Check if the figure belongs to a dock (via AppData) - dock = getappdata(obj.hFigure, 'FastPlotDock'); - if ~isempty(dock) && isa(dock, 'FastPlotDock') + dock = getappdata(obj.hFigure, 'FastSenseDock'); + if ~isempty(dock) && isa(dock, 'FastSenseDock') dock.Theme = newTheme; dock.reapplyTheme(); else target.Theme = newTheme; target.reapplyTheme(); end - elseif isa(target, 'FastPlot') + elseif isa(target, 'FastSense') target.Theme = newTheme; target.reapplyTheme(); end @@ -812,11 +812,11 @@ function cleanupCursor(obj) function onToggleGrid(obj) %ONTOGGLEGRID Callback: toggle grid on active axes or all tiles. % If the mouse is over an axes, toggles that axes only; - % otherwise toggles grid on every managed FastPlot axes. + % otherwise toggles grid on every managed FastSense axes. [~, ax] = obj.getActiveTarget(); if isempty(ax) - for i = 1:numel(obj.FastPlots) - obj.toggleGridOnAxes(obj.FastPlots{i}.hAxes); + for i = 1:numel(obj.FastSenses) + obj.toggleGridOnAxes(obj.FastSenses{i}.hAxes); end else obj.toggleGridOnAxes(ax); @@ -840,11 +840,11 @@ function toggleGridOnAxes(~, ax) function onToggleLegend(obj) %ONTOGGLELEGEND Callback: toggle legend on active axes or all tiles. % If the mouse is over an axes, toggles that legend only; - % otherwise toggles legend on every managed FastPlot axes. + % otherwise toggles legend on every managed FastSense axes. [~, ax] = obj.getActiveTarget(); if isempty(ax) - for i = 1:numel(obj.FastPlots) - obj.toggleLegendOnAxes(obj.FastPlots{i}.hAxes); + for i = 1:numel(obj.FastSenses) + obj.toggleLegendOnAxes(obj.FastSenses{i}.hAxes); end else obj.toggleLegendOnAxes(ax); @@ -869,11 +869,11 @@ function toggleLegendOnAxes(~, ax) function onAutoscaleY(obj) %ONAUTOSCALEY Callback: autoscale Y on active axes or all tiles. % If the mouse is over an axes, autoscales that axes only; - % otherwise autoscales every managed FastPlot axes. + % otherwise autoscales every managed FastSense axes. [fp, ~] = obj.getActiveTarget(); if isempty(fp) - for i = 1:numel(obj.FastPlots) - obj.autoscaleYOnAxes(obj.FastPlots{i}); + for i = 1:numel(obj.FastSenses) + obj.autoscaleYOnAxes(obj.FastSenses{i}); end else obj.autoscaleYOnAxes(fp); @@ -881,13 +881,13 @@ function onAutoscaleY(obj) end function autoscaleYOnAxes(~, fp) - %AUTOSCALEYONAXES Fit Y-axis limits to visible data on one FastPlot. + %AUTOSCALEYONAXES Fit Y-axis limits to visible data on one FastSense. % autoscaleYOnAxes(obj, fp) examines all lines in fp, finds the % min/max Y values within the current X-axis limits using binary % search, and sets YLim with 5% padding. % % Input: - % fp — FastPlot instance whose axes to autoscale + % fp — FastSense instance whose axes to autoscale ax = fp.hAxes; xlims = get(ax, 'XLim'); ymin = Inf; ymax = -Inf; @@ -925,20 +925,20 @@ function onExportPNG(obj) end function [fp, ax] = getActiveTarget(obj) - %GETACTIVETARGET Find the FastPlot instance under the mouse. + %GETACTIVETARGET Find the FastSense instance under the mouse. % [fp, ax] = getActiveTarget(obj) checks the current mouse - % position against all managed FastPlot axes (in pixel units). - % Returns the FastPlot instance and axes handle if the mouse + % position against all managed FastSense axes (in pixel units). + % Returns the FastSense instance and axes handle if the mouse % is inside an axes, or empty arrays if outside all axes. % % Outputs: - % fp — FastPlot instance under cursor, or [] + % fp — FastSense instance under cursor, or [] % ax — axes handle under cursor, or [] fp = []; ax = []; cp = get(obj.hFigure, 'CurrentPoint'); - for i = 1:numel(obj.FastPlots) - a = obj.FastPlots{i}.hAxes; + for i = 1:numel(obj.FastSenses) + a = obj.FastSenses{i}.hAxes; if ~ishandle(a); continue; end oldUnits = get(a, 'Units'); set(a, 'Units', 'pixels'); @@ -946,7 +946,7 @@ function onExportPNG(obj) set(a, 'Units', oldUnits); if cp(1) >= pos(1) && cp(1) <= pos(1)+pos(3) && ... cp(2) >= pos(2) && cp(2) <= pos(2)+pos(4) - fp = obj.FastPlots{i}; + fp = obj.FastSenses{i}; ax = a; return; end @@ -1004,15 +1004,15 @@ function refreshDataCursors(obj) hTarget = evt.Target; end - % Look up which FastPlot and line index from the line's UserData + % Look up which FastSense and line index from the line's UserData fp = []; lineIdx = []; ud = get(hTarget, 'UserData'); - if isstruct(ud) && isfield(ud, 'FastPlot') && isfield(ud.FastPlot, 'LineIndex') - lineIdx = ud.FastPlot.LineIndex; + if isstruct(ud) && isfield(ud, 'FastSense') && isfield(ud.FastSense, 'LineIndex') + lineIdx = ud.FastSense.LineIndex; end - if isstruct(ud) && isfield(ud, 'FastPlotInstance') - fp = ud.FastPlotInstance; + if isstruct(ud) && isfield(ud, 'FastSenseInstance') + fp = ud.FastSenseInstance; end % Format X value (datetime-aware) @@ -1040,7 +1040,7 @@ function refreshDataCursors(obj) metaOn = false; try hFig = ancestor(hTarget, 'figure'); - metaOn = getappdata(hFig, 'FastPlotMetadataEnabled'); + metaOn = getappdata(hFig, 'FastSenseMetadataEnabled'); catch end if isempty(metaOn); metaOn = false; end @@ -1069,7 +1069,7 @@ function refreshDataCursors(obj) methods (Static) function icon = makeIcon(name) %MAKEICON Generate a 16x16x3 RGB icon for toolbar buttons. - % icon = FastPlotToolbar.makeIcon(name) + % icon = FastSenseToolbar.makeIcon(name) % % Draws simple pixel-art icons on a light gray background. % Available names: 'cursor', 'crosshair', 'grid', 'legend', @@ -1248,14 +1248,14 @@ function initIcons() names = {'cursor', 'crosshair', 'grid', 'legend', 'autoscale', ... 'export', 'refresh', 'live', 'metadata', 'violations', 'theme'}; for i = 1:numel(names) - FastPlotToolbar.makeIcon(names{i}); + FastSenseToolbar.makeIcon(names{i}); end end function s = formatX(xVal, xType) %FORMATX Format an X value based on XType. - % s = FastPlotToolbar.formatX(xVal, 'datenum') - % s = FastPlotToolbar.formatX(xVal, 'numeric') + % s = FastSenseToolbar.formatX(xVal, 'datenum') + % s = FastSenseToolbar.formatX(xVal, 'numeric') if strcmp(xType, 'datenum') try s = datestr(xVal, 'mmm dd HH:MM:SS'); diff --git a/libs/FastPlot/NavigatorOverlay.m b/libs/FastSense/NavigatorOverlay.m similarity index 100% rename from libs/FastPlot/NavigatorOverlay.m rename to libs/FastSense/NavigatorOverlay.m diff --git a/libs/FastPlot/SensorDetailPlot.m b/libs/FastSense/SensorDetailPlot.m similarity index 95% rename from libs/FastPlot/SensorDetailPlot.m rename to libs/FastSense/SensorDetailPlot.m index eef150cc..ad90ec1c 100644 --- a/libs/FastPlot/SensorDetailPlot.m +++ b/libs/FastSense/SensorDetailPlot.m @@ -5,7 +5,7 @@ % sdp = SensorDetailPlot(sensor, Name, Value, ...) % % Name-Value Options: - % 'Theme' - FastPlot theme (default: 'default') + % 'Theme' - FastSense theme (default: 'default') % 'NavigatorHeight' - Fraction 0-1 for navigator (default: 0.20) % 'ShowThresholds' - Show thresholds in main plot (default: true) % 'ShowThresholdBands' - Show threshold bands in navigator (default: true) @@ -17,8 +17,8 @@ properties (SetAccess = private) Sensor % Sensor object - MainPlot % FastPlot instance for upper panel - NavigatorPlot % FastPlot instance for lower panel + MainPlot % FastSense instance for upper panel + NavigatorPlot % FastSense instance for lower panel NavigatorOverlayObj % NavigatorOverlay instance end @@ -55,7 +55,7 @@ obj.IsPropagating = false; obj.OwnsFigure = false; - % Load cached defaults (same pattern as FastPlot / FastPlotGrid) + % Load cached defaults (same pattern as FastSense / FastSenseGrid) cfg = getDefaults(); % Parse options via standard parseOpts @@ -70,13 +70,13 @@ conDefaults.XType = 'numeric'; [opts, ~] = parseOpts(conDefaults, varargin); - % Inherit theme from parent panel (set by FastPlotGrid.tilePanel) + % Inherit theme from parent panel (set by FastSenseGrid.tilePanel) % when no explicit Theme was given. if isempty(opts.Theme) && ~isempty(opts.Parent) try ud = get(opts.Parent, 'UserData'); - if isstruct(ud) && isfield(ud, 'FastPlotTheme') - opts.Theme = ud.FastPlotTheme; + if isstruct(ud) && isfield(ud, 'FastSenseTheme') + opts.Theme = ud.FastSenseTheme; end catch end @@ -101,7 +101,7 @@ function render(obj) end % Auto-resolve sensor if not yet resolved (avoids struct() - % default in ResolvedThresholds crashing FastPlot.addSensor) + % default in ResolvedThresholds crashing FastSense.addSensor) if isstruct(obj.Sensor.ResolvedThresholds) && isempty(fieldnames(obj.Sensor.ResolvedThresholds)) obj.Sensor.resolve(); end @@ -109,8 +109,8 @@ function render(obj) % Create layout obj.createLayout(); - % Create main FastPlot - obj.MainPlot = FastPlot('Parent', obj.hMainAxes, 'Theme', obj.Theme); + % Create main FastSense + obj.MainPlot = FastSense('Parent', obj.hMainAxes, 'Theme', obj.Theme); displayName = obj.Sensor.Name; if isempty(displayName); displayName = obj.Sensor.Key; end obj.MainPlot.addLine(obj.Sensor.X, obj.Sensor.Y, ... @@ -141,15 +141,15 @@ function render(obj) set(obj.hMainAxes, 'XTickLabel', []); xlabel(obj.hMainAxes, ''); - % Set title with theme formatting (matches FastPlotGrid.setTileTitle) + % Set title with theme formatting (matches FastSenseGrid.setTileTitle) if ~isempty(obj.Title) title(obj.hMainAxes, obj.Title, ... 'FontSize', obj.Theme.TitleFontSize, ... 'Color', obj.Theme.ForegroundColor); end - % Create navigator FastPlot - obj.NavigatorPlot = FastPlot('Parent', obj.hNavAxes, 'Theme', obj.Theme); + % Create navigator FastSense + obj.NavigatorPlot = FastSense('Parent', obj.hNavAxes, 'Theme', obj.Theme); obj.NavigatorPlot.addLine(obj.Sensor.X, obj.Sensor.Y, ... 'DisplayName', obj.Sensor.Name, 'XType', obj.XType); @@ -183,7 +183,7 @@ function render(obj) rotate3d(obj.hNavAxes, 'off'); % Re-apply datetime tick formatting after all nav axes - % modifications to guarantee it matches normal FastPlot tiles. + % modifications to guarantee it matches normal FastSense tiles. % Sync main axes XTick to match, keeping labels suppressed. if strcmp(obj.XType, 'datenum') obj.formatDatetimeTicks(obj.hNavAxes); @@ -315,7 +315,7 @@ function createLayout(obj) end % Use Position + innerposition for both modes so the plot - % area width matches normal FastPlot tiles exactly. + % area width matches normal FastSense tiles exactly. % In embedded mode add vertical margins inside the panel for % the title (top) and navigator X-tick labels (bottom). navFrac = obj.NavigatorHeight; @@ -537,7 +537,7 @@ function onMainXLimChanged(obj) end % Re-apply correct tick density and suppress labels after - % FastPlot's onXLimChanged calls datetick (which re-sets + % FastSense's onXLimChanged calls datetick (which re-sets % XTick and XTickLabel with wrong density). if strcmp(obj.XType, 'datenum') obj.formatDatetimeTicks(obj.hMainAxes); @@ -572,9 +572,9 @@ function refreshDatetimeTicks(obj) end function formatDatetimeTicks(~, ax) - %FORMATEDATETIMETICKS Apply datetime tick formatting matching FastPlot. + %FORMATEDATETIMETICKS Apply datetime tick formatting matching FastSense. % Computes nice datetime tick positions and labels directly, - % matching the format selection in FastPlot.updateDatetimeTicks. + % matching the format selection in FastSense.updateDatetimeTicks. % Avoids datetick() which produces inconsistent tick density % for axes embedded in uipanels. if ~ishandle(ax); return; end @@ -583,7 +583,7 @@ function formatDatetimeTicks(~, ax) span = diff(xl); % in days (datenum units) % Choose interval and format based on span (same thresholds - % as FastPlot.updateDatetimeTicks) + % as FastSense.updateDatetimeTicks) if span > 365 fmt = 'yyyy mmm dd HH:MM'; % Round to months diff --git a/libs/FastPlot/binary_search.m b/libs/FastSense/binary_search.m similarity index 100% rename from libs/FastPlot/binary_search.m rename to libs/FastSense/binary_search.m diff --git a/libs/FastPlot/build_mex.m b/libs/FastSense/build_mex.m similarity index 99% rename from libs/FastPlot/build_mex.m rename to libs/FastSense/build_mex.m index 4bd21ed7..ea75f789 100644 --- a/libs/FastPlot/build_mex.m +++ b/libs/FastSense/build_mex.m @@ -1,5 +1,5 @@ function build_mex() -%BUILD_MEX Compile all FastPlot MEX files with platform-appropriate SIMD flags. +%BUILD_MEX Compile all FastSense MEX files with platform-appropriate SIMD flags. % BUILD_MEX() detects the CPU architecture (x86_64 or ARM64) and the % best available C compiler, then compiles every MEX source file found % in private/mex_src/ into the private/ directory. diff --git a/libs/FastPlot/mksqlite.c b/libs/FastSense/mksqlite.c similarity index 100% rename from libs/FastPlot/mksqlite.c rename to libs/FastSense/mksqlite.c diff --git a/libs/FastPlot/private/binary_search.m b/libs/FastSense/private/binary_search.m similarity index 97% rename from libs/FastPlot/private/binary_search.m rename to libs/FastSense/private/binary_search.m index d32eb2da..42ad7ed8 100644 --- a/libs/FastPlot/private/binary_search.m +++ b/libs/FastSense/private/binary_search.m @@ -6,7 +6,7 @@ % idx = BINARY_SEARCH(x, val, 'right') returns the last (rightmost) % index where x(idx) <= val (upper bound). % -% This function is a private helper for FastPlot, used extensively by +% This function is a private helper for FastSense, used extensively by % the downsampling and viewport-clipping routines. % % Inputs: diff --git a/libs/FastPlot/private/clearDefaultsCache.m b/libs/FastSense/private/clearDefaultsCache.m similarity index 58% rename from libs/FastPlot/private/clearDefaultsCache.m rename to libs/FastSense/private/clearDefaultsCache.m index 028429cd..38f0095d 100644 --- a/libs/FastPlot/private/clearDefaultsCache.m +++ b/libs/FastSense/private/clearDefaultsCache.m @@ -1,13 +1,13 @@ function clearDefaultsCache() -%CLEARDEFAULTSCACHE Force getDefaults to reload FastPlotDefaults on next call. +%CLEARDEFAULTSCACHE Force getDefaults to reload FastSenseDefaults on next call. % CLEARDEFAULTSCACHE() invalidates the persistent cache held by % getDefaults() so that the next call to getDefaults() re-executes -% FastPlotDefaults() and rebuilds the configuration from scratch. +% FastSenseDefaults() and rebuilds the configuration from scratch. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % -% Use this after editing FastPlotDefaults.m or any custom theme file -% during a MATLAB session, so that subsequent FastPlot constructions +% Use this after editing FastSenseDefaults.m or any custom theme file +% during a MATLAB session, so that subsequent FastSense constructions % pick up the new settings without restarting MATLAB. % % Inputs: @@ -20,7 +20,7 @@ function clearDefaultsCache() % CLEAR clears the persistent variables inside that % function, effectively resetting its cached state. % -% See also getDefaults, FastPlotDefaults. +% See also getDefaults, FastSenseDefaults. % Clear the persistent variable inside getDefaults clear getDefaults; diff --git a/libs/FastPlot/private/compute_violations.m b/libs/FastSense/private/compute_violations.m similarity index 94% rename from libs/FastPlot/private/compute_violations.m rename to libs/FastSense/private/compute_violations.m index 2b6d3e86..0a0a7322 100644 --- a/libs/FastPlot/private/compute_violations.m +++ b/libs/FastSense/private/compute_violations.m @@ -4,7 +4,7 @@ % returns the X and Y coordinates of all points where the data strictly % exceeds (or falls below) the given scalar threshold value. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % x — numeric vector of X coordinates @@ -29,7 +29,7 @@ % compute_violations_dynamic instead. % % See also compute_violations_dynamic, downsample_violations, -% violation_cull, FastPlot.addThreshold. +% violation_cull, FastSense.addThreshold. % Build logical mask of violating points if strcmp(direction, 'upper') diff --git a/libs/FastPlot/private/compute_violations_dynamic.m b/libs/FastSense/private/compute_violations_dynamic.m similarity index 97% rename from libs/FastPlot/private/compute_violations_dynamic.m rename to libs/FastSense/private/compute_violations_dynamic.m index 5abdde4d..ce3234d9 100644 --- a/libs/FastPlot/private/compute_violations_dynamic.m +++ b/libs/FastSense/private/compute_violations_dynamic.m @@ -7,7 +7,7 @@ % corresponding thX is at or before x(i) — i.e., zero-order hold % (previous-value) interpolation. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % x — numeric vector of data X coordinates @@ -36,7 +36,7 @@ % automatically excluded (IEEE 754: NaN comparisons return false). % % See also compute_violations, downsample_violations, violation_cull, -% FastPlot.addThreshold. +% FastSense.addThreshold. % Guard: empty data if isempty(x) diff --git a/libs/FastPlot/private/downsample_violations.m b/libs/FastSense/private/downsample_violations.m similarity index 97% rename from libs/FastPlot/private/downsample_violations.m rename to libs/FastSense/private/downsample_violations.m index e8124410..db01e706 100644 --- a/libs/FastPlot/private/downsample_violations.m +++ b/libs/FastSense/private/downsample_violations.m @@ -7,7 +7,7 @@ % violations while eliminating sub-pixel overlap that would waste GPU % marker-rendering budget. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % xViol — numeric vector of violation X coordinates (from @@ -34,7 +34,7 @@ % 3. For each unique bucket, keep the point with max |y - threshold|. % % See also compute_violations, compute_violations_dynamic, violation_cull, -% FastPlot.updateViolations. +% FastSense.updateViolations. % Guard: empty input or degenerate pixel width if isempty(xViol) || pixelWidth <= 0 diff --git a/libs/FastPlot/private/getDefaults.m b/libs/FastSense/private/getDefaults.m similarity index 73% rename from libs/FastPlot/private/getDefaults.m rename to libs/FastSense/private/getDefaults.m index 5a483fa2..0d0e93d9 100644 --- a/libs/FastPlot/private/getDefaults.m +++ b/libs/FastSense/private/getDefaults.m @@ -1,32 +1,32 @@ function cfg = getDefaults() -%GETDEFAULTS Return cached FastPlotDefaults struct. -% cfg = GETDEFAULTS() returns the FastPlot configuration struct produced -% by FastPlotDefaults(). The result is cached in a persistent variable so -% that FastPlotDefaults() is called only once per MATLAB session, avoiding -% repeated file parsing on every FastPlot construction. +%GETDEFAULTS Return cached FastSenseDefaults struct. +% cfg = GETDEFAULTS() returns the FastSense configuration struct produced +% by FastSenseDefaults(). The result is cached in a persistent variable so +% that FastSenseDefaults() is called only once per MATLAB session, avoiding +% repeated file parsing on every FastSense construction. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % On the first call the cache is populated by: -% 1. Calling FastPlotDefaults() to obtain the base configuration. +% 1. Calling FastSenseDefaults() to obtain the base configuration. % 2. Calling loadCustomThemes() to scan cfg.ThemeDir for user themes. % % Inputs: % (none) % % Outputs: -% cfg — scalar struct of FastPlot default settings, including a +% cfg — scalar struct of FastSense default settings, including a % CustomThemes field populated from the user's theme directory % % Call clearDefaultsCache() to invalidate the cache and force a fresh -% reload on the next invocation (e.g., after editing FastPlotDefaults.m). +% reload on the next invocation (e.g., after editing FastSenseDefaults.m). % -% See also FastPlotDefaults, clearDefaultsCache, loadCustomThemes. +% See also FastSenseDefaults, clearDefaultsCache, loadCustomThemes. persistent cachedCfg; if isempty(cachedCfg) % First call: build and cache the configuration - cachedCfg = FastPlotDefaults(); + cachedCfg = FastSenseDefaults(); cachedCfg.CustomThemes = loadCustomThemes(cachedCfg); end cfg = cachedCfg; @@ -41,7 +41,7 @@ % This function is a local helper for getDefaults. % % Inputs: -% cfg — FastPlotDefaults struct (must contain field ThemeDir) +% cfg — FastSenseDefaults struct (must contain field ThemeDir) % % Outputs: % themes — struct whose field names are theme file basenames and whose @@ -50,7 +50,7 @@ % % Path resolution: % If cfg.ThemeDir is not an absolute folder, it is resolved relative to -% the FastPlot library root (one level above this private/ directory). +% the FastSense library root (one level above this private/ directory). % % Error handling: % Broken or non-struct-returning theme files are silently skipped. @@ -64,7 +64,7 @@ return; end - % Resolve relative paths against FastPlot root + % Resolve relative paths against FastSense root themeDir = cfg.ThemeDir; if ~isfolder(themeDir) root = fileparts(mfilename('fullpath')); diff --git a/libs/FastPlot/private/loadMetaStruct.m b/libs/FastSense/private/loadMetaStruct.m similarity index 92% rename from libs/FastPlot/private/loadMetaStruct.m rename to libs/FastSense/private/loadMetaStruct.m index b4ecfcba..9b1a928a 100644 --- a/libs/FastPlot/private/loadMetaStruct.m +++ b/libs/FastSense/private/loadMetaStruct.m @@ -3,9 +3,9 @@ % meta = LOADMETASTRUCT(filepath, vars) loads the specified .mat file, % extracts a timestamp vector (from a field named 'datenum' or % 'datetime') and the requested variable names, and returns them in a -% flat struct suitable for FastPlot metadata lookup and live-data feeds. +% flat struct suitable for FastSense metadata lookup and live-data feeds. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % filepath — char, path to a .mat file. The file must contain at least @@ -26,7 +26,7 @@ % Variables listed in vars that are absent from the file are silently % skipped (no error or warning). % -% See also FastPlot.lookupMetadata, FastPlot.startLive. +% See also FastSense.lookupMetadata, FastSense.startLive. meta = []; diff --git a/libs/FastPlot/private/lttb_downsample.m b/libs/FastSense/private/lttb_downsample.m similarity index 99% rename from libs/FastPlot/private/lttb_downsample.m rename to libs/FastSense/private/lttb_downsample.m index 5970681b..89a64d0b 100644 --- a/libs/FastPlot/private/lttb_downsample.m +++ b/libs/FastSense/private/lttb_downsample.m @@ -11,7 +11,7 @@ % is true, yielding visually accurate point selection on logarithmic % axes. Output values remain in original (linear) space. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % x — sorted numeric row vector of X coordinates (ascending) diff --git a/libs/FastPlot/private/mergeTheme.m b/libs/FastSense/private/mergeTheme.m similarity index 88% rename from libs/FastPlot/private/mergeTheme.m rename to libs/FastSense/private/mergeTheme.m index cd1d317e..87f17105 100644 --- a/libs/FastPlot/private/mergeTheme.m +++ b/libs/FastSense/private/mergeTheme.m @@ -6,11 +6,11 @@ % is a shallow (non-recursive) merge — nested structs are replaced % wholesale, not recursively merged. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % base — scalar struct, the complete theme with all fields present -% (typically produced by FastPlotTheme) +% (typically produced by FastSenseTheme) % overrides — scalar struct, partial set of fields whose values should % replace those in base. May contain any subset of fields % from base, or even new fields. @@ -25,7 +25,7 @@ % result = mergeTheme(base, ov); % % result.Color == 'w', result.FontSize == 12, result.Grid == true % -% See also FastPlotTheme, resolveTheme, getDefaults. +% See also FastSenseTheme, resolveTheme, getDefaults. result = base; fnames = fieldnames(overrides); diff --git a/libs/FastPlot/private/mex_src/binary_search_mex.c b/libs/FastSense/private/mex_src/binary_search_mex.c similarity index 90% rename from libs/FastPlot/private/mex_src/binary_search_mex.c rename to libs/FastSense/private/mex_src/binary_search_mex.c index 29d42797..bd96966d 100644 --- a/libs/FastPlot/private/mex_src/binary_search_mex.c +++ b/libs/FastSense/private/mex_src/binary_search_mex.c @@ -24,19 +24,19 @@ void mexFunction(int nlhs, mxArray *plhs[], { /* Validate inputs */ if (nrhs != 3) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:nrhs", "Three inputs required: x, val, direction."); } if (!mxIsDouble(prhs[0]) || mxIsComplex(prhs[0])) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:notDouble", "x must be a real double array."); } if (!mxIsDouble(prhs[1]) || mxGetNumberOfElements(prhs[1]) != 1) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:notScalar", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:notScalar", "val must be a scalar double."); } if (!mxIsChar(prhs[2])) { - mexErrMsgIdAndTxt("FastPlot:binary_search_mex:notChar", + mexErrMsgIdAndTxt("FastSense:binary_search_mex:notChar", "direction must be a char array."); } diff --git a/libs/FastPlot/private/mex_src/build_store_mex.c b/libs/FastSense/private/mex_src/build_store_mex.c similarity index 92% rename from libs/FastPlot/private/mex_src/build_store_mex.c rename to libs/FastSense/private/mex_src/build_store_mex.c index 2d15795c..8efe0158 100644 --- a/libs/FastPlot/private/mex_src/build_store_mex.c +++ b/libs/FastSense/private/mex_src/build_store_mex.c @@ -1,5 +1,5 @@ /* - * build_store_mex.c — MEX-based bulk SQLite writer for FastPlotDataStore. + * build_store_mex.c — MEX-based bulk SQLite writer for FastSenseDataStore. * * numChunks = build_store_mex(dbPath, X, Y, chunkSize) * @@ -15,7 +15,7 @@ * resolved_violations tables. Writes all data as typed BLOBs (mksqlite- * compatible 24-byte header) with SIMD-accelerated Y min/max metadata. * - * Replaces the MATLAB loop in FastPlotDataStore.initSqlite, eliminating + * Replaces the MATLAB loop in FastSenseDataStore.initSqlite, eliminating * ~20K mksqlite round-trips and saving ~3-4s on 100M+ point datasets. */ @@ -133,26 +133,26 @@ void mexFunction(int nlhs, mxArray *plhs[], /* Sanity check: typed BLOB class constant must match MATLAB's mxDOUBLE_CLASS */ if (TYPED_BLOB_CLASS_DBL != mxDOUBLE_CLASS) { - mexErrMsgIdAndTxt("FastPlot:build_store_mex:classSync", + mexErrMsgIdAndTxt("FastSense:build_store_mex:classSync", "TYPED_BLOB_CLASS_DBL (%d) != mxDOUBLE_CLASS (%d)", TYPED_BLOB_CLASS_DBL, (int)mxDOUBLE_CLASS); } /* ---- Validate inputs ---- */ if (nrhs != 4) { - mexErrMsgIdAndTxt("FastPlot:build_store_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:build_store_mex:nrhs", "Four inputs required: dbPath, X, Y, chunkSize."); } if (!mxIsChar(prhs[0])) { - mexErrMsgIdAndTxt("FastPlot:build_store_mex:badPath", + mexErrMsgIdAndTxt("FastSense:build_store_mex:badPath", "First input must be a char array (database path)."); } if (!mxIsDouble(prhs[1]) || mxIsComplex(prhs[1])) { - mexErrMsgIdAndTxt("FastPlot:build_store_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:build_store_mex:notDouble", "X must be a real double array."); } if (!mxIsDouble(prhs[2]) || mxIsComplex(prhs[2])) { - mexErrMsgIdAndTxt("FastPlot:build_store_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:build_store_mex:notDouble", "Y must be a real double array."); } @@ -177,7 +177,7 @@ void mexFunction(int nlhs, mxArray *plhs[], snprintf(errbuf, sizeof(errbuf), "%s", sqlite3_errmsg(db)); sqlite3_close(db); mxFree(dbPath); - mexErrMsgIdAndTxt("FastPlot:build_store_mex:dbOpen", + mexErrMsgIdAndTxt("FastSense:build_store_mex:dbOpen", "Cannot open database: %s", errbuf); } @@ -191,7 +191,7 @@ void mexFunction(int nlhs, mxArray *plhs[], sqlite3_exec(db, "PRAGMA mmap_size = 268435456", NULL, NULL, NULL); /* ---- Create tables ---- */ - /* KEEP IN SYNC with FastPlotDataStore.initSqlite MATLAB fallback */ + /* KEEP IN SYNC with FastSenseDataStore.initSqlite MATLAB fallback */ rc = sqlite3_exec(db, "CREATE TABLE chunks (" " chunk_id INTEGER PRIMARY KEY," @@ -209,11 +209,11 @@ void mexFunction(int nlhs, mxArray *plhs[], snprintf(errbuf, sizeof(errbuf), "%s", sqlite3_errmsg(db)); sqlite3_close(db); mxFree(dbPath); - mexErrMsgIdAndTxt("FastPlot:build_store_mex:createTable", + mexErrMsgIdAndTxt("FastSense:build_store_mex:createTable", "CREATE TABLE chunks failed: %s", errbuf); } - /* KEEP IN SYNC with FastPlotDataStore.initSqlite MATLAB fallback */ + /* KEEP IN SYNC with FastSenseDataStore.initSqlite MATLAB fallback */ sqlite3_exec(db, "CREATE TABLE resolved_thresholds (" " idx INTEGER PRIMARY KEY," @@ -226,7 +226,7 @@ void mexFunction(int nlhs, mxArray *plhs[], " value REAL NOT NULL" ")", NULL, NULL, NULL); - /* KEEP IN SYNC with FastPlotDataStore.initSqlite MATLAB fallback */ + /* KEEP IN SYNC with FastSenseDataStore.initSqlite MATLAB fallback */ sqlite3_exec(db, "CREATE TABLE resolved_violations (" " idx INTEGER PRIMARY KEY," @@ -245,7 +245,7 @@ void mexFunction(int nlhs, mxArray *plhs[], snprintf(errbuf, sizeof(errbuf), "%s", sqlite3_errmsg(db)); sqlite3_close(db); mxFree(dbPath); - mexErrMsgIdAndTxt("FastPlot:build_store_mex:prepare", + mexErrMsgIdAndTxt("FastSense:build_store_mex:prepare", "Prepare failed: %s", errbuf); } @@ -306,7 +306,7 @@ void mexFunction(int nlhs, mxArray *plhs[], mxFree(xBlobBuf); mxFree(yBlobBuf); mxFree(dbPath); - mexErrMsgIdAndTxt("FastPlot:build_store_mex:insert", + mexErrMsgIdAndTxt("FastSense:build_store_mex:insert", "Insert failed at chunk %d: %s", (int)chunkId, errbuf); } } diff --git a/libs/FastPlot/private/mex_src/compute_violations_mex.c b/libs/FastSense/private/mex_src/compute_violations_mex.c similarity index 99% rename from libs/FastPlot/private/mex_src/compute_violations_mex.c rename to libs/FastSense/private/mex_src/compute_violations_mex.c index 232e8c4f..6882f5d8 100644 --- a/libs/FastPlot/private/mex_src/compute_violations_mex.c +++ b/libs/FastSense/private/mex_src/compute_violations_mex.c @@ -66,7 +66,7 @@ void mexFunction(int nlhs, mxArray *plhs[], dirD = mxGetPr(prhs[4]); nTh = mxGetNumberOfElements(prhs[3]); } else { - mexErrMsgIdAndTxt("FastPlot:compute_violations_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:compute_violations_mex:nrhs", "Five or six inputs required."); } diff --git a/libs/FastPlot/private/mex_src/lttb_core_mex.c b/libs/FastSense/private/mex_src/lttb_core_mex.c similarity index 97% rename from libs/FastPlot/private/mex_src/lttb_core_mex.c rename to libs/FastSense/private/mex_src/lttb_core_mex.c index a7370e26..8031ed45 100644 --- a/libs/FastPlot/private/mex_src/lttb_core_mex.c +++ b/libs/FastSense/private/mex_src/lttb_core_mex.c @@ -24,11 +24,11 @@ void mexFunction(int nlhs, mxArray *plhs[], { /* Validate inputs */ if (nrhs != 3) { - mexErrMsgIdAndTxt("FastPlot:lttb_core_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:lttb_core_mex:nrhs", "Three inputs required: x, y, numOut."); } if (!mxIsDouble(prhs[0]) || !mxIsDouble(prhs[1])) { - mexErrMsgIdAndTxt("FastPlot:lttb_core_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:lttb_core_mex:notDouble", "x and y must be real double arrays."); } diff --git a/libs/FastPlot/private/mex_src/minmax_core_mex.c b/libs/FastSense/private/mex_src/minmax_core_mex.c similarity index 96% rename from libs/FastPlot/private/mex_src/minmax_core_mex.c rename to libs/FastSense/private/mex_src/minmax_core_mex.c index 136a9a89..f63009c9 100644 --- a/libs/FastPlot/private/mex_src/minmax_core_mex.c +++ b/libs/FastSense/private/mex_src/minmax_core_mex.c @@ -25,11 +25,11 @@ void mexFunction(int nlhs, mxArray *plhs[], { /* Validate inputs */ if (nrhs != 3) { - mexErrMsgIdAndTxt("FastPlot:minmax_core_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:minmax_core_mex:nrhs", "Three inputs required: x, y, numBuckets."); } if (!mxIsDouble(prhs[0]) || !mxIsDouble(prhs[1])) { - mexErrMsgIdAndTxt("FastPlot:minmax_core_mex:notDouble", + mexErrMsgIdAndTxt("FastSense:minmax_core_mex:notDouble", "x and y must be real double arrays."); } diff --git a/libs/FastPlot/private/mex_src/resolve_disk_mex.c b/libs/FastSense/private/mex_src/resolve_disk_mex.c similarity index 97% rename from libs/FastPlot/private/mex_src/resolve_disk_mex.c rename to libs/FastSense/private/mex_src/resolve_disk_mex.c index d906fd05..e5919514 100644 --- a/libs/FastPlot/private/mex_src/resolve_disk_mex.c +++ b/libs/FastSense/private/mex_src/resolve_disk_mex.c @@ -95,11 +95,11 @@ void mexFunction(int nlhs, mxArray *plhs[], /* ---- Validate inputs ---- */ if (nrhs != 5) { - mexErrMsgIdAndTxt("FastPlot:resolve_disk_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:resolve_disk_mex:nrhs", "Five inputs required: dbPath, segLo, segHi, thresholdValues, directions."); } if (!mxIsChar(prhs[0])) { - mexErrMsgIdAndTxt("FastPlot:resolve_disk_mex:badPath", + mexErrMsgIdAndTxt("FastSense:resolve_disk_mex:badPath", "First input must be a char array (database path)."); } @@ -145,7 +145,7 @@ void mexFunction(int nlhs, mxArray *plhs[], const char *errmsg = sqlite3_errmsg(db); sqlite3_close(db); mxFree(dbPath); - mexErrMsgIdAndTxt("FastPlot:resolve_disk_mex:dbOpen", + mexErrMsgIdAndTxt("FastSense:resolve_disk_mex:dbOpen", "Cannot open database: %s", errmsg); } @@ -167,7 +167,7 @@ void mexFunction(int nlhs, mxArray *plhs[], if (rc != SQLITE_OK) { sqlite3_close(db); mxFree(dbPath); - mexErrMsgIdAndTxt("FastPlot:resolve_disk_mex:prepare", + mexErrMsgIdAndTxt("FastSense:resolve_disk_mex:prepare", "SQL prepare failed: %s", sqlite3_errmsg(db)); } @@ -176,7 +176,7 @@ void mexFunction(int nlhs, mxArray *plhs[], sqlite3_finalize(stmtUpper); sqlite3_close(db); mxFree(dbPath); - mexErrMsgIdAndTxt("FastPlot:resolve_disk_mex:prepare", + mexErrMsgIdAndTxt("FastSense:resolve_disk_mex:prepare", "SQL prepare failed: %s", sqlite3_errmsg(db)); } diff --git a/libs/FastPlot/private/mex_src/simd_utils.h b/libs/FastSense/private/mex_src/simd_utils.h similarity index 98% rename from libs/FastPlot/private/mex_src/simd_utils.h rename to libs/FastSense/private/mex_src/simd_utils.h index 559c53db..5907e72c 100644 --- a/libs/FastPlot/private/mex_src/simd_utils.h +++ b/libs/FastSense/private/mex_src/simd_utils.h @@ -5,7 +5,7 @@ #include /* - * simd_utils.h — Compile-time SIMD abstraction for FastPlot MEX files. + * simd_utils.h — Compile-time SIMD abstraction for FastSense MEX files. * * Provides unified operations on packed doubles: * - AVX2: 4 doubles per vector (__m256d) diff --git a/libs/FastPlot/private/mex_src/sqlite3.c b/libs/FastSense/private/mex_src/sqlite3.c similarity index 100% rename from libs/FastPlot/private/mex_src/sqlite3.c rename to libs/FastSense/private/mex_src/sqlite3.c diff --git a/libs/FastPlot/private/mex_src/sqlite3.h b/libs/FastSense/private/mex_src/sqlite3.h similarity index 100% rename from libs/FastPlot/private/mex_src/sqlite3.h rename to libs/FastSense/private/mex_src/sqlite3.h diff --git a/libs/FastPlot/private/mex_src/violation_cull_mex.c b/libs/FastSense/private/mex_src/violation_cull_mex.c similarity index 99% rename from libs/FastPlot/private/mex_src/violation_cull_mex.c rename to libs/FastSense/private/mex_src/violation_cull_mex.c index 8b5892f1..ea2000ac 100644 --- a/libs/FastPlot/private/mex_src/violation_cull_mex.c +++ b/libs/FastSense/private/mex_src/violation_cull_mex.c @@ -49,7 +49,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if (nrhs != 7) { - mexErrMsgIdAndTxt("FastPlot:violation_cull_mex:nrhs", + mexErrMsgIdAndTxt("FastSense:violation_cull_mex:nrhs", "Seven inputs required: x, y, thX, thY, direction, pixelWidth, xmin."); } diff --git a/libs/FastPlot/private/minmax_downsample.m b/libs/FastSense/private/minmax_downsample.m similarity index 99% rename from libs/FastPlot/private/minmax_downsample.m rename to libs/FastSense/private/minmax_downsample.m index 3f0b4577..2c489f0e 100644 --- a/libs/FastPlot/private/minmax_downsample.m +++ b/libs/FastSense/private/minmax_downsample.m @@ -13,7 +13,7 @@ % logarithmically-spaced bucket edges when logX is true, producing % visually uniform bin widths on a log-scale X axis. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % x — sorted numeric row vector of X coordinates (ascending) diff --git a/libs/FastPlot/private/parseOpts.m b/libs/FastSense/private/parseOpts.m similarity index 94% rename from libs/FastPlot/private/parseOpts.m rename to libs/FastSense/private/parseOpts.m index 2db738ee..242f3c9e 100644 --- a/libs/FastPlot/private/parseOpts.m +++ b/libs/FastSense/private/parseOpts.m @@ -9,7 +9,7 @@ % [opts, unmatched] = PARSEOPTS(defaults, args, verbose) additionally % emits a warning for every unrecognized option when verbose is true. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % defaults — scalar struct whose field names define valid option keys @@ -32,7 +32,7 @@ % [opts, extra] = parseOpts(defs, {'color', 'b', 'Name', 'foo'}); % % opts.Color == 'b', opts.Width == 1, extra.Name == 'foo' % -% See also FastPlot, FastPlotGrid, struct2nvpairs. +% See also FastSense, FastSenseGrid, struct2nvpairs. if nargin < 3; verbose = false; end @@ -58,7 +58,7 @@ % Unmatched — collect for pass-through unmatched.(key) = val; if verbose - warning('FastPlot:unknownOption', ... + warning('FastSense:unknownOption', ... 'Unknown option ''%s''. Valid options: %s', ... key, strjoin(fnames, ', ')); end diff --git a/libs/FastPlot/private/resolveTheme.m b/libs/FastSense/private/resolveTheme.m similarity index 65% rename from libs/FastPlot/private/resolveTheme.m rename to libs/FastSense/private/resolveTheme.m index 5ea9a240..df937e86 100644 --- a/libs/FastPlot/private/resolveTheme.m +++ b/libs/FastSense/private/resolveTheme.m @@ -2,17 +2,17 @@ %RESOLVETHEME Normalize a theme specification to a complete theme struct. % theme = RESOLVETHEME(val, fallbackName) accepts a theme specification % in one of several flexible formats and returns a fully populated theme -% struct suitable for FastPlot rendering. +% struct suitable for FastSense rendering. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Supported input formats for val: -% [] — empty: uses FastPlotTheme(fallbackName) to load the named +% [] — empty: uses FastSenseTheme(fallbackName) to load the named % preset specified by fallbackName % char — character vector: treated as a preset name and passed to -% FastPlotTheme(val) (e.g., 'dark', 'light') +% FastSenseTheme(val) (e.g., 'dark', 'light') % struct — partial struct of theme overrides: passed to -% FastPlotTheme(val) which merges them onto the base theme +% FastSenseTheme(val) which merges them onto the base theme % other — any other type is assumed to be a pre-built, complete % theme struct and is returned as-is % @@ -22,16 +22,16 @@ % % Outputs: % theme — complete theme struct with all required fields populated, -% ready for use in FastPlot rendering +% ready for use in FastSense rendering % -% See also FastPlotTheme, mergeTheme, getDefaults. +% See also FastSenseTheme, mergeTheme, getDefaults. if isempty(val) % No theme specified — load the fallback preset - theme = FastPlotTheme(fallbackName); + theme = FastSenseTheme(fallbackName); elseif ischar(val) || isstruct(val) - % Preset name or partial overrides — delegate to FastPlotTheme - theme = FastPlotTheme(val); + % Preset name or partial overrides — delegate to FastSenseTheme + theme = FastSenseTheme(val); else % Pre-built theme object — pass through unchanged theme = val; diff --git a/libs/FastPlot/private/struct2nvpairs.m b/libs/FastSense/private/struct2nvpairs.m similarity index 94% rename from libs/FastPlot/private/struct2nvpairs.m rename to libs/FastSense/private/struct2nvpairs.m index abe94f98..41d17718 100644 --- a/libs/FastPlot/private/struct2nvpairs.m +++ b/libs/FastSense/private/struct2nvpairs.m @@ -5,7 +5,7 @@ % passing to MATLAB functions that accept name-value syntax such as % figure(), set(), or uicontrol(). % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % s — scalar struct with N fields (any field value types) diff --git a/libs/FastPlot/private/violation_cull.m b/libs/FastSense/private/violation_cull.m similarity index 98% rename from libs/FastPlot/private/violation_cull.m rename to libs/FastSense/private/violation_cull.m index 37707f1c..7de8b1f8 100644 --- a/libs/FastPlot/private/violation_cull.m +++ b/libs/FastSense/private/violation_cull.m @@ -6,7 +6,7 @@ % compute_violations (or compute_violations_dynamic) and % downsample_violations in a single call. % -% This function is a private helper for FastPlot. +% This function is a private helper for FastSense. % % Inputs: % x — numeric vector of data X coordinates diff --git a/libs/SensorThreshold/Sensor.m b/libs/SensorThreshold/Sensor.m index 3680fd4b..be1a64dc 100644 --- a/libs/SensorThreshold/Sensor.m +++ b/libs/SensorThreshold/Sensor.m @@ -6,7 +6,7 @@ % limit values). The resolve() method evaluates all rules against % the state channels to produce pre-computed threshold time series, % violation indices, and state-band regions that can be rendered by - % a plotting layer such as FastPlot. + % a plotting layer such as FastSense. % % Typical workflow: % 1. Create a Sensor and set X/Y data (or call load()). @@ -63,7 +63,7 @@ X % 1xN double: datenum time stamps Y % 1xN (or MxN) double: sensor values Units % char: measurement unit (e.g., 'degC', 'bar', 'rpm') - DataStore % FastPlotDataStore: disk-backed storage (set by toDisk) + DataStore % FastSenseDataStore: disk-backed storage (set by toDisk) StateChannels % cell array of StateChannel objects ThresholdRules % cell array of ThresholdRule objects ResolvedThresholds % struct array: precomputed threshold step-function lines @@ -214,11 +214,11 @@ function addThresholdRule(obj, condition, value, varargin) function toDisk(obj) %TODISK Move sensor X/Y data to disk-backed DataStore. - % s.toDisk() creates a FastPlotDataStore from the sensor's + % s.toDisk() creates a FastSenseDataStore from the sensor's % X and Y arrays, then clears X and Y from memory. The data % remains accessible via s.DataStore.getRange() and % s.DataStore.readSlice(). Subsequent calls to resolve(), - % addSensor(), and FastPlot rendering all work transparently. + % addSensor(), and FastSense rendering all work transparently. % % Call toDisk() after setting X and Y but before or after % resolve(). resolve() automatically reads from the DataStore @@ -233,7 +233,7 @@ function toDisk(obj) % fp.addSensor(s); % fp.render(); % - % See also toMemory, isOnDisk, FastPlotDataStore. + % See also toMemory, isOnDisk, FastSenseDataStore. if isempty(obj.X) && ~isempty(obj.DataStore) return; % already on disk @@ -241,7 +241,7 @@ function toDisk(obj) if isempty(obj.X) error('Sensor:noData', 'No X/Y data to move to disk.'); end - obj.DataStore = FastPlotDataStore(obj.X, obj.Y); + obj.DataStore = FastSenseDataStore(obj.X, obj.Y); % Pre-compute resolve() while X/Y are still in memory (fastest % path). Results are stored in the SQLite database so that diff --git a/libs/SensorThreshold/private/compute_violations_disk.m b/libs/SensorThreshold/private/compute_violations_disk.m index 9cfcbc85..b242a157 100644 --- a/libs/SensorThreshold/private/compute_violations_disk.m +++ b/libs/SensorThreshold/private/compute_violations_disk.m @@ -8,7 +8,7 @@ % single MEX call (direct SQLite + SIMD), eliminating MATLAB<->MEX overhead. % % Inputs: -% ds — FastPlotDataStore, disk-backed data source +% ds — FastSenseDataStore, disk-backed data source % segLo — 1xS integer, start indices of active segments % segHi — 1xS integer, end indices of active segments % thresholdValues — 1xT double, threshold value for each rule @@ -18,7 +18,7 @@ % batchViolX — 1xT cell array of violation X coordinates % batchViolY — 1xT cell array of violation Y coordinates % -% See also compute_violations_batch, Sensor.resolve, FastPlotDataStore. +% See also compute_violations_batch, Sensor.resolve, FastSenseDataStore. persistent hasMex if isempty(hasMex) diff --git a/libs/WebBridge/WebBridge.m b/libs/WebBridge/WebBridge.m index 82a91478..363a17df 100644 --- a/libs/WebBridge/WebBridge.m +++ b/libs/WebBridge/WebBridge.m @@ -144,7 +144,7 @@ function sendInit(obj) idx = 0; for i = 1:numel(obj.Dashboard.Widgets) w = obj.Dashboard.Widgets{i}; - if ~isa(w, 'FastPlotWidget'); continue; end + if ~isa(w, 'FastSenseWidget'); continue; end idx = idx + 1; if isprop(w, 'Sensor') && ~isempty(w.Sensor) && isprop(w.Sensor, 'Key') sid = w.Sensor.Key; @@ -225,7 +225,7 @@ function checkConfigChanged(obj) end function launchBridge(obj) bridgeDir = fullfile(fileparts(mfilename('fullpath')), '..', '..', 'bridge', 'python'); - cmd = sprintf('python -m fastplot_bridge --matlab-port %d', obj.TcpPort); + cmd = sprintf('python -m fastsense_bridge --matlab-port %d', obj.TcpPort); if ispc fullCmd = sprintf('start /B %s', cmd); else @@ -239,7 +239,7 @@ function launchBridge(obj) pause(0.1); end obj.stop(); - error('WebBridge:timeout', 'Bridge did not start within 10s. Check that fastplot-bridge is installed.'); + error('WebBridge:timeout', 'Bridge did not start within 10s. Check that fastsense-bridge is installed.'); end function enableWALOnDataStores(obj) stores = obj.collectDataStores(); @@ -254,7 +254,7 @@ function disableWALOnDataStores(obj) if isempty(obj.Dashboard) || isempty(obj.Dashboard.Widgets); return; end for i = 1:numel(obj.Dashboard.Widgets) w = obj.Dashboard.Widgets{i}; - if ~isa(w, 'FastPlotWidget'); continue; end + if ~isa(w, 'FastSenseWidget'); continue; end ds = []; if isprop(w, 'DataStore') && ~isempty(w.DataStore) ds = w.DataStore; diff --git a/scripts/generate_api_docs.py b/scripts/generate_api_docs.py index e80246cf..6d1c8f87 100644 --- a/scripts/generate_api_docs.py +++ b/scripts/generate_api_docs.py @@ -640,16 +640,16 @@ def _escape_md_table(text: str) -> str: # Each page: (output filename, page title, library dir, class order) PAGES = [ ( - "API-Reference:-FastPlot.md", - "API Reference: FastPlot", - "FastPlot", + "API-Reference:-FastSense.md", + "API Reference: FastSense", + "FastSense", [ - "FastPlot", - "FastPlotFigure", - "FastPlotDock", - "FastPlotToolbar", - "FastPlotTheme", - "FastPlotDataStore", + "FastSense", + "FastSenseFigure", + "FastSenseDock", + "FastSenseToolbar", + "FastSenseTheme", + "FastSenseDataStore", "NavigatorOverlay", "SensorDetailPlot", ], @@ -662,7 +662,7 @@ def _escape_md_table(text: str) -> str: "DashboardEngine", "DashboardBuilder", "DashboardWidget", - "FastPlotWidget", + "FastSenseWidget", "GaugeWidget", "NumberWidget", "StatusWidget", @@ -705,7 +705,7 @@ def _escape_md_table(text: str) -> str: "API-Reference:-Utilities.md", "API Reference: Utilities", None, # special: pulls from multiple dirs - ["ConsoleProgressBar", "FastPlotDefaults"], + ["ConsoleProgressBar", "FastSenseDefaults"], ), ] @@ -762,7 +762,7 @@ def generate_page(filename, title, classes_by_name, class_order): # --------------------------------------------------------------------------- def main(): - print(f"FastPlot API Doc Generator") + print(f"FastSense API Doc Generator") print(f"Project root: {PROJECT_ROOT}") print(f"Libs dir: {LIBS_DIR}") print(f"Wiki dir: {WIKI_DIR}") @@ -778,7 +778,7 @@ def main(): all_classes = {} # name -> MatlabClass lib_classes = {} # lib_name -> {name: MatlabClass} - for lib_name in ["FastPlot", "Dashboard", "SensorThreshold", "EventDetection", "WebBridge"]: + for lib_name in ["FastSense", "Dashboard", "SensorThreshold", "EventDetection", "WebBridge"]: lib_dir = LIBS_DIR / lib_name print(f"[{lib_name}]") parsed = collect_classes(lib_dir) diff --git a/scripts/run_tests_with_coverage.m b/scripts/run_tests_with_coverage.m index 5eb2419f..d2a0a19e 100644 --- a/scripts/run_tests_with_coverage.m +++ b/scripts/run_tests_with_coverage.m @@ -18,7 +18,7 @@ function run_tests_with_coverage() % Add code coverage for all library source files sourceFiles = {}; - libDirs = {'FastPlot', 'SensorThreshold', 'EventDetection', 'Dashboard', 'WebBridge'}; + libDirs = {'FastSense', 'SensorThreshold', 'EventDetection', 'Dashboard', 'WebBridge'}; for i = 1:numel(libDirs) libPath = fullfile(repo_root, 'libs', libDirs{i}); files = dir(fullfile(libPath, '*.m')); diff --git a/setup.m b/setup.m index e5cd193e..acea679d 100644 --- a/setup.m +++ b/setup.m @@ -1,7 +1,7 @@ function setup() %SETUP Add libraries to path and compile MEX files (including mksqlite/SQLite). % SETUP() locates the project root (the directory containing this -% file), then adds the FastPlot, SensorThreshold, and EventDetection +% file), then adds the FastSense, SensorThreshold, and EventDetection % library folders to the MATLAB search path using addpath. It then % compiles all MEX files, including mksqlite for SQLite-backed % DataStore support. @@ -10,7 +10,7 @@ function setup() % for automatic initialization. % % The following directories are added: -% /libs/FastPlot +% /libs/FastSense % /libs/SensorThreshold % /libs/EventDetection % /libs/Dashboard @@ -20,25 +20,25 @@ function setup() % - mksqlite (SQLite3 MEX interface for large-dataset disk storage) % % SQLite3 is bundled as the amalgamation (sqlite3.c + sqlite3.h) in -% libs/FastPlot/private/mex_src/, so no system SQLite installation is +% libs/FastSense/private/mex_src/, so no system SQLite installation is % required. It compiles directly into the MEX files on all platforms % (Linux, macOS, Windows). % % Example: % setup(); % adds libraries to path, compiles MEX; prints status % -% See also addpath, FastPlotDefaults, build_mex, FastPlotDataStore. +% See also addpath, FastSenseDefaults, build_mex, FastSenseDataStore. % Determine the project root from this file's location root = fileparts(mfilename('fullpath')); % Add library directories to the MATLAB search path - addpath(fullfile(root, 'libs', 'FastPlot')); + addpath(fullfile(root, 'libs', 'FastSense')); addpath(fullfile(root, 'libs', 'SensorThreshold')); addpath(fullfile(root, 'libs', 'EventDetection')); addpath(fullfile(root, 'libs', 'Dashboard')); addpath(fullfile(root, 'libs', 'WebBridge')); - fprintf('FastPlot + SensorThreshold + EventDetection + Dashboard + WebBridge libraries added to path.\n'); + fprintf('FastSense + SensorThreshold + EventDetection + Dashboard + WebBridge libraries added to path.\n'); % Compile all MEX files (SIMD kernels + bundled SQLite3) fprintf('\n--- Compiling MEX files ---\n'); diff --git a/tests/add_fastplot_private_path.m b/tests/add_fastsense_private_path.m similarity index 79% rename from tests/add_fastplot_private_path.m rename to tests/add_fastsense_private_path.m index 54af0a5e..172fa16d 100644 --- a/tests/add_fastplot_private_path.m +++ b/tests/add_fastsense_private_path.m @@ -1,11 +1,11 @@ -function add_fastplot_private_path() -%ADD_FASTPLOT_PRIVATE_PATH Make libs/FastPlot/private/ functions accessible from tests. +function add_fastsense_private_path() +%ADD_FASTSENSE_PRIVATE_PATH Make libs/FastSense/private/ functions accessible from tests. % On R2025b+, addpath('private') is rejected, so we copy files to a % temp directory proxy. test_dir = fileparts(mfilename('fullpath')); repo_root = fileparts(test_dir); - privDir = fullfile(repo_root, 'libs', 'FastPlot', 'private'); + privDir = fullfile(repo_root, 'libs', 'FastSense', 'private'); w = warning('off', 'all'); addpath(privDir); @@ -16,7 +16,7 @@ function add_fastplot_private_path() if ~any(strcmp(dirs, privDir)) % R2025b+: addpath('private') was rejected. % Make private functions available via a temp directory with copies. - tmpDir = fullfile(tempdir, 'fastplot_private_proxy'); + tmpDir = fullfile(tempdir, 'fastsense_private_proxy'); if ~exist(tmpDir, 'dir') mkdir(tmpDir); end diff --git a/tests/run_all_tests.m b/tests/run_all_tests.m index 89e60a2a..cde10b95 100644 --- a/tests/run_all_tests.m +++ b/tests/run_all_tests.m @@ -1,5 +1,5 @@ function results = run_all_tests() -%RUN_ALL_TESTS Execute all FastPlot unit tests. +%RUN_ALL_TESTS Execute all FastSense unit tests. % On MATLAB: runs the class-based test suite in tests/suite/ using % matlab.unittest. On Octave: runs function-based test_*.m files. % @@ -29,7 +29,7 @@ suite_dir = fullfile(test_dir, 'suite'); addpath(suite_dir); - fprintf('=== FastPlot Test Suite (MATLAB) ===\n\n'); + fprintf('=== FastSense Test Suite (MATLAB) ===\n\n'); suite = TestSuite.fromFolder(suite_dir); @@ -72,7 +72,7 @@ function results = run_octave_tests(test_dir) %RUN_OCTAVE_TESTS Run function-based tests for Octave compatibility. - add_fastplot_private_path(); + add_fastsense_private_path(); files = dir(fullfile(test_dir, 'test_*.m')); total = 0; diff --git a/tests/suite/TestAddBand.m b/tests/suite/TestAddBand.m index d7544578..dd3894ac 100644 --- a/tests/suite/TestAddBand.m +++ b/tests/suite/TestAddBand.m @@ -3,13 +3,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testAddBand(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addBand(-1, 1, 'FaceColor', [1 0.9 0.9], 'FaceAlpha', 0.3, 'Label', 'Safe'); testCase.verifyEqual(numel(fp.Bands), 1, 'testAddBand: count'); testCase.verifyEqual(fp.Bands(1).YLow, -1, 'testAddBand: YLow'); @@ -18,14 +18,14 @@ function testAddBand(testCase) end function testAddMultipleBands(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addBand(-2, -1); fp.addBand(1, 2); testCase.verifyEqual(numel(fp.Bands), 2, 'testAddMultipleBands'); end function testBandRendered(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addBand(0.2, 0.8, 'FaceColor', [0 1 0], 'FaceAlpha', 0.2); fp.render(); @@ -33,11 +33,11 @@ function testBandRendered(testCase) testCase.verifyNotEmpty(fp.Bands(1).hPatch, 'testBandRendered: hPatch created'); testCase.verifyTrue(ishandle(fp.Bands(1).hPatch), 'testBandRendered: hPatch valid'); ud = get(fp.Bands(1).hPatch, 'UserData'); - testCase.verifyEqual(ud.FastPlot.Type, 'band', 'testBandRendered: UserData type'); + testCase.verifyEqual(ud.FastSense.Type, 'band', 'testBandRendered: UserData type'); end function testBandRejectsAfterRender(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -51,7 +51,7 @@ function testBandRejectsAfterRender(testCase) end function testBandDefaults(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addBand(0, 1); testCase.verifyTrue(fp.Bands(1).FaceAlpha > 0, 'testBandDefaults: FaceAlpha'); testCase.verifyEqual(numel(fp.Bands(1).FaceColor), 3, 'testBandDefaults: FaceColor'); diff --git a/tests/suite/TestAddLine.m b/tests/suite/TestAddLine.m index 9dc830bf..0fbc26cd 100644 --- a/tests/suite/TestAddLine.m +++ b/tests/suite/TestAddLine.m @@ -3,13 +3,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testAddSingleLine(testCase) - fp = FastPlot(); + fp = FastSense(); x = 1:100; y = rand(1, 100); fp.addLine(x, y); @@ -19,7 +19,7 @@ function testAddSingleLine(testCase) end function testAddMultipleLines(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.addLine(1:20, rand(1,20)); fp.addLine(1:5, rand(1,5)); @@ -27,26 +27,26 @@ function testAddMultipleLines(testCase) end function testLineOptions(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'Color', 'r', 'DisplayName', 'S1'); testCase.verifyEqual(fp.Lines(1).Options.Color, 'r', 'testLineOptions: Color'); testCase.verifyEqual(fp.Lines(1).Options.DisplayName, 'S1', 'testLineOptions: DisplayName'); end function testDownsampleMethodDefault(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); testCase.verifyEqual(fp.Lines(1).DownsampleMethod, 'minmax', 'testDownsampleMethodDefault'); end function testDownsampleMethodOverride(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'DownsampleMethod', 'lttb'); testCase.verifyEqual(fp.Lines(1).DownsampleMethod, 'lttb', 'testDownsampleMethodOverride'); end function testRejectsNonMonotonicX(testCase) - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addLine([1 3 2 4], rand(1,4)); @@ -58,7 +58,7 @@ function testRejectsNonMonotonicX(testCase) end function testRejectsMismatchedLengths(testCase) - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addLine(1:10, rand(1,5)); @@ -70,7 +70,7 @@ function testRejectsMismatchedLengths(testCase) end function testColumnVectorsAccepted(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine((1:10)', rand(10,1)); testCase.verifyEqual(numel(fp.Lines(1).X), 10, 'testColumnVectors: numel'); testCase.verifyTrue(isrow(fp.Lines(1).X), 'testColumnVectors: must be row'); diff --git a/tests/suite/TestAddMarker.m b/tests/suite/TestAddMarker.m index 248e55d7..a3620a02 100644 --- a/tests/suite/TestAddMarker.m +++ b/tests/suite/TestAddMarker.m @@ -3,13 +3,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testAddMarker(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addMarker([10 20 30], [1 2 3], 'Marker', 'v', 'Color', [1 0 0], 'Label', 'Faults'); testCase.verifyEqual(numel(fp.Markers), 1, 'testAddMarker: count'); testCase.verifyEqual(fp.Markers(1).X, [10 20 30], 'testAddMarker: X'); @@ -17,7 +17,7 @@ function testAddMarker(testCase) end function testMarkerRendered(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addMarker([10 50], [0.5 0.8], 'Marker', 'd', 'MarkerSize', 10); fp.render(); @@ -25,18 +25,18 @@ function testMarkerRendered(testCase) testCase.verifyNotEmpty(fp.Markers(1).hLine, 'testMarkerRendered: hLine'); testCase.verifyTrue(ishandle(fp.Markers(1).hLine), 'testMarkerRendered: valid handle'); ud = get(fp.Markers(1).hLine, 'UserData'); - testCase.verifyEqual(ud.FastPlot.Type, 'marker', 'testMarkerRendered: UserData type'); + testCase.verifyEqual(ud.FastSense.Type, 'marker', 'testMarkerRendered: UserData type'); end function testMarkerDefaults(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addMarker([5], [1]); testCase.verifyNotEmpty(fp.Markers(1).Marker, 'testMarkerDefaults: Marker shape'); testCase.verifyTrue(fp.Markers(1).MarkerSize > 0, 'testMarkerDefaults: MarkerSize'); end function testMarkerRejectsAfterRender(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); testCase.addTeardown(@close, fp.hFigure); diff --git a/tests/suite/TestAddSensor.m b/tests/suite/TestAddSensor.m index 97a3e96a..002202c6 100644 --- a/tests/suite/TestAddSensor.m +++ b/tests/suite/TestAddSensor.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end @@ -14,7 +14,7 @@ function testAddSensorBasic(testCase) s.Y = rand(1, 100) * 10; s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s); testCase.verifyEqual(numel(fp.Lines), 1, 'testBasic: one line added'); testCase.verifyEqual(fp.Lines(1).Options.DisplayName, 'Chamber Pressure', 'testBasic: display name'); @@ -31,7 +31,7 @@ function testAddSensorWithThresholds(testCase) s.addThresholdRule(struct('machine', 1), 10, 'Direction', 'upper', 'Label', 'HH'); s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); testCase.verifyEqual(numel(fp.Lines), 1, 'testWithThresholds: only data line'); testCase.verifyTrue(numel(fp.Thresholds) >= 1, 'testWithThresholds: threshold(s) added'); @@ -46,7 +46,7 @@ function testAddSensorNoThresholds(testCase) s.addThresholdRule(struct(), 5, 'Direction', 'upper'); s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s, 'ShowThresholds', false); testCase.verifyEqual(numel(fp.Lines), 1, 'testNoThresholds: only data line'); testCase.verifyEqual(numel(fp.Thresholds), 0, 'testNoThresholds: no thresholds'); @@ -58,7 +58,7 @@ function testAddSensorUsesKeyAsFallbackName(testCase) s.Y = rand(1, 10); s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s); testCase.verifyEqual(fp.Lines(1).Options.DisplayName, 'flow_rate', 'testFallbackName: uses Key'); end diff --git a/tests/suite/TestAddShaded.m b/tests/suite/TestAddShaded.m index 4550c36f..c86c7693 100644 --- a/tests/suite/TestAddShaded.m +++ b/tests/suite/TestAddShaded.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end @@ -12,7 +12,7 @@ function testAddShaded(testCase) x = 1:100; y1 = ones(1,100) * 2; y2 = ones(1,100) * -2; - fp = FastPlot(); + fp = FastSense(); fp.addShaded(x, y1, y2, 'FaceColor', [0 0 1], 'FaceAlpha', 0.2); testCase.verifyEqual(numel(fp.Shadings), 1, 'testAddShaded: count'); testCase.verifyEqual(fp.Shadings(1).X, x, 'testAddShaded: X'); @@ -21,7 +21,7 @@ function testAddShaded(testCase) end function testShadedRendered(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addShaded(1:100, ones(1,100), zeros(1,100), 'FaceColor', [1 0 0]); fp.render(); @@ -29,11 +29,11 @@ function testShadedRendered(testCase) testCase.verifyNotEmpty(fp.Shadings(1).hPatch, 'testShadedRendered: hPatch'); testCase.verifyTrue(ishandle(fp.Shadings(1).hPatch), 'testShadedRendered: valid'); ud = get(fp.Shadings(1).hPatch, 'UserData'); - testCase.verifyEqual(ud.FastPlot.Type, 'shaded', 'testShadedRendered: type'); + testCase.verifyEqual(ud.FastSense.Type, 'shaded', 'testShadedRendered: type'); end function testShadedValidation(testCase) - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addShaded(1:10, 1:10, 1:5); % mismatched lengths @@ -44,7 +44,7 @@ function testShadedValidation(testCase) end function testShadedMonotonicX(testCase) - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addShaded([3 1 2], [1 1 1], [0 0 0]); @@ -55,7 +55,7 @@ function testShadedMonotonicX(testCase) end function testShadedRejectsAfterRender(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -69,7 +69,7 @@ function testShadedRejectsAfterRender(testCase) end function testShadedColumnVectors(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addShaded((1:10)', (1:10)', zeros(10,1)); testCase.verifyTrue(isrow(fp.Shadings(1).X), 'testShadedColumnVectors: X row'); testCase.verifyTrue(isrow(fp.Shadings(1).Y1), 'testShadedColumnVectors: Y1 row'); @@ -77,7 +77,7 @@ function testShadedColumnVectors(testCase) end function testAddFill(testCase) - fp = FastPlot(); + fp = FastSense(); x = 1:50; y = rand(1,50); fp.addFill(x, y, 'FaceColor', [0 0.5 1], 'FaceAlpha', 0.2); @@ -86,20 +86,20 @@ function testAddFill(testCase) end function testAddFillCustomBaseline(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addFill(1:10, rand(1,10), 'Baseline', -1); testCase.verifyTrue(all(fp.Shadings(1).Y2 == -1), 'testAddFillCustomBaseline'); end function testAddFillRendered(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addFill(1:100, rand(1,100), 'FaceColor', [0 1 0]); fp.render(); testCase.addTeardown(@close, fp.hFigure); testCase.verifyTrue(ishandle(fp.Shadings(1).hPatch), 'testAddFillRendered: valid patch'); ud = get(fp.Shadings(1).hPatch, 'UserData'); - testCase.verifyEqual(ud.FastPlot.Type, 'shaded', 'testAddFillRendered: type is shaded'); + testCase.verifyEqual(ud.FastSense.Type, 'shaded', 'testAddFillRendered: type is shaded'); end end end diff --git a/tests/suite/TestAddThreshold.m b/tests/suite/TestAddThreshold.m index 510f8e85..2172d6ab 100644 --- a/tests/suite/TestAddThreshold.m +++ b/tests/suite/TestAddThreshold.m @@ -3,13 +3,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testAddUpperThreshold(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(4.5, 'Direction', 'upper'); testCase.verifyEqual(numel(fp.Thresholds), 1, 'testAddUpperThreshold: count'); testCase.verifyEqual(fp.Thresholds(1).Value, 4.5, 'testAddUpperThreshold: value'); @@ -17,13 +17,13 @@ function testAddUpperThreshold(testCase) end function testAddLowerThreshold(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(-2.0, 'Direction', 'lower'); testCase.verifyEqual(fp.Thresholds(1).Direction, 'lower', 'testAddLowerThreshold'); end function testDefaults(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(5.0); testCase.verifyEqual(fp.Thresholds(1).Direction, 'upper', 'testDefaults: direction'); testCase.verifyEqual(fp.Thresholds(1).ShowViolations, false, 'testDefaults: ShowViolations'); @@ -32,7 +32,7 @@ function testDefaults(testCase) end function testCustomOptions(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(3.0, 'Direction', 'lower', ... 'ShowViolations', true, 'Color', [1 0 0], ... 'LineStyle', ':', 'Label', 'LowerBound'); @@ -44,7 +44,7 @@ function testCustomOptions(testCase) end function testMultipleThresholds(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(1.0); fp.addThreshold(2.0); fp.addThreshold(3.0); @@ -52,7 +52,7 @@ function testMultipleThresholds(testCase) end function testTimeVaryingThreshold(testCase) - fp = FastPlot(); + fp = FastSense(); thX = [0 10 20 30]; thY = [5.0 5.0 7.0 7.0]; fp.addThreshold(thX, thY, 'Direction', 'upper', 'ShowViolations', true, 'Label', 'StepTh'); @@ -65,7 +65,7 @@ function testTimeVaryingThreshold(testCase) end function testMixedThresholds(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(4.5); fp.addThreshold([0 10], [3.0 5.0], 'Direction', 'lower'); testCase.verifyEqual(numel(fp.Thresholds), 2, 'testMixed: count'); diff --git a/tests/suite/TestBinarySearch.m b/tests/suite/TestBinarySearch.m index c93b6028..f22e5344 100644 --- a/tests/suite/TestBinarySearch.m +++ b/tests/suite/TestBinarySearch.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestComputeViolations.m b/tests/suite/TestComputeViolations.m index 8d1fb5c7..82ae22f7 100644 --- a/tests/suite/TestComputeViolations.m +++ b/tests/suite/TestComputeViolations.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestComputeViolationsDynamic.m b/tests/suite/TestComputeViolationsDynamic.m index c3be25ef..a6641f24 100644 --- a/tests/suite/TestComputeViolationsDynamic.m +++ b/tests/suite/TestComputeViolationsDynamic.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestDashboardEngine.m b/tests/suite/TestDashboardEngine.m index d695cd0e..8a09f6f3 100644 --- a/tests/suite/TestDashboardEngine.m +++ b/tests/suite/TestDashboardEngine.m @@ -22,34 +22,34 @@ function testSetTheme(testCase) function testAddWidget(testCase) d = DashboardEngine('Test'); - d.addWidget('fastplot', 'Title', 'Plot 1', ... + d.addWidget('fastsense', 'Title', 'Plot 1', ... 'Position', [1 1 12 3], ... 'XData', 1:10, 'YData', rand(1,10)); testCase.verifyEqual(numel(d.Widgets), 1); - testCase.verifyTrue(isa(d.Widgets{1}, 'FastPlotWidget')); + testCase.verifyTrue(isa(d.Widgets{1}, 'FastSenseWidget')); end function testAddMultipleWidgets(testCase) d = DashboardEngine('Test'); - d.addWidget('fastplot', 'Title', 'Plot 1', ... + d.addWidget('fastsense', 'Title', 'Plot 1', ... 'Position', [1 1 12 3], 'XData', 1:10, 'YData', rand(1,10)); - d.addWidget('fastplot', 'Title', 'Plot 2', ... + d.addWidget('fastsense', 'Title', 'Plot 2', ... 'Position', [13 1 12 3], 'XData', 1:10, 'YData', rand(1,10)); testCase.verifyEqual(numel(d.Widgets), 2); end function testOverlapResolution(testCase) d = DashboardEngine('Test'); - d.addWidget('fastplot', 'Title', 'Plot 1', ... + d.addWidget('fastsense', 'Title', 'Plot 1', ... 'Position', [1 1 12 3], 'XData', 1:10, 'YData', rand(1,10)); - d.addWidget('fastplot', 'Title', 'Plot 2', ... + d.addWidget('fastsense', 'Title', 'Plot 2', ... 'Position', [5 1 12 3], 'XData', 1:10, 'YData', rand(1,10)); testCase.verifyEqual(d.Widgets{2}.Position(2), 4); end function testRender(testCase) d = DashboardEngine('Render Test'); - d.addWidget('fastplot', 'Title', 'Plot 1', ... + d.addWidget('fastsense', 'Title', 'Plot 1', ... 'Position', [1 1 24 3], 'XData', 1:100, 'YData', rand(1,100)); d.render(); testCase.addTeardown(@() close(d.hFigure)); @@ -62,7 +62,7 @@ function testSaveAndLoad(testCase) d = DashboardEngine('Save Test'); d.Theme = 'dark'; d.LiveInterval = 3; - d.addWidget('fastplot', 'Title', 'Temp', ... + d.addWidget('fastsense', 'Title', 'Temp', ... 'Position', [1 1 12 3], 'XData', 1:10, 'YData', [1:10]); filepath = fullfile(tempdir, 'test_save_dashboard.json'); @@ -79,7 +79,7 @@ function testSaveAndLoad(testCase) function testExportScript(testCase) d = DashboardEngine('Export Test'); - d.addWidget('fastplot', 'Title', 'Pressure', ... + d.addWidget('fastsense', 'Title', 'Pressure', ... 'Position', [1 1 12 3], 'XData', 1:5, 'YData', [5 4 3 2 1]); filepath = fullfile(tempdir, 'test_export_dashboard.m'); @@ -94,7 +94,7 @@ function testExportScript(testCase) function testLiveStartStop(testCase) d = DashboardEngine('Live Test'); d.LiveInterval = 1; - d.addWidget('fastplot', 'Title', 'Plot', ... + d.addWidget('fastsense', 'Title', 'Plot', ... 'Position', [1 1 24 3], 'XData', 1:10, 'YData', rand(1,10)); d.render(); testCase.addTeardown(@() close(d.hFigure)); @@ -115,7 +115,7 @@ function testAddWidgetWithSensor(testCase) s.resolve(); d = DashboardEngine('Sensor Test'); - d.addWidget('fastplot', 'Sensor', s, 'Position', [1 1 16 3]); + d.addWidget('fastsense', 'Sensor', s, 'Position', [1 1 16 3]); testCase.verifyEqual(d.Widgets{1}.Title, 'Temperature'); testCase.verifyEqual(d.Widgets{1}.Sensor, s); end @@ -123,7 +123,7 @@ function testAddWidgetWithSensor(testCase) function testCloseDeletesTimer(testCase) d = DashboardEngine('Timer Cleanup'); d.LiveInterval = 1; - d.addWidget('fastplot', 'Title', 'P', ... + d.addWidget('fastsense', 'Title', 'P', ... 'Position', [1 1 24 3], 'XData', 1:10, 'YData', rand(1,10)); d.render(); d.startLive(); diff --git a/tests/suite/TestDashboardSerializer.m b/tests/suite/TestDashboardSerializer.m index e7098e17..b25bef6a 100644 --- a/tests/suite/TestDashboardSerializer.m +++ b/tests/suite/TestDashboardSerializer.m @@ -26,7 +26,7 @@ function testSaveAndLoadRoundTrip(testCase) config.liveInterval = 5; config.grid = struct('columns', 24); config.widgets = {}; - config.widgets{1} = struct('type', 'fastplot', ... + config.widgets{1} = struct('type', 'fastsense', ... 'title', 'Temperature', ... 'position', struct('col', 1, 'row', 1, 'width', 12, 'height', 3), ... 'source', struct('type', 'data', 'x', 1:10, 'y', rand(1,10))); @@ -45,10 +45,10 @@ function testSaveAndLoadRoundTrip(testCase) end function testWidgetsToConfig(testCase) - w1 = FastPlotWidget('Title', 'Plot 1', 'Position', [1 1 12 3]); + w1 = FastSenseWidget('Title', 'Plot 1', 'Position', [1 1 12 3]); w1.XData = 1:10; w1.YData = rand(1,10); - w2 = FastPlotWidget('Title', 'Plot 2', 'Position', [13 1 12 3]); + w2 = FastSenseWidget('Title', 'Plot 2', 'Position', [13 1 12 3]); w2.XData = 1:10; w2.YData = rand(1,10); @@ -67,7 +67,7 @@ function testConfigToWidgets(testCase) config.grid = struct('columns', 24); ws = struct(); - ws.type = 'fastplot'; + ws.type = 'fastsense'; ws.title = 'Temp'; ws.position = struct('col', 1, 'row', 1, 'width', 12, 'height', 3); ws.source = struct('type', 'data', 'x', 1:5, 'y', [1 2 3 4 5]); @@ -75,7 +75,7 @@ function testConfigToWidgets(testCase) widgets = DashboardSerializer.configToWidgets(config); testCase.verifyEqual(numel(widgets), 1); - testCase.verifyTrue(isa(widgets{1}, 'FastPlotWidget')); + testCase.verifyTrue(isa(widgets{1}, 'FastSenseWidget')); testCase.verifyEqual(widgets{1}.Title, 'Temp'); end @@ -87,7 +87,7 @@ function testExportScript(testCase) config.grid = struct('columns', 24); ws = struct(); - ws.type = 'fastplot'; + ws.type = 'fastsense'; ws.title = 'Temperature'; ws.position = struct('col', 1, 'row', 1, 'width', 12, 'height', 3); ws.source = struct('type', 'data', 'x', 1:10, 'y', rand(1,10)); diff --git a/tests/suite/TestDashboardTheme.m b/tests/suite/TestDashboardTheme.m index 1e5c6083..0119e6a6 100644 --- a/tests/suite/TestDashboardTheme.m +++ b/tests/suite/TestDashboardTheme.m @@ -13,12 +13,12 @@ function testDefaultReturnsStruct(testCase) 'DashboardTheme should return a struct'); end - function testContainsFastPlotFields(testCase) + function testContainsFastSenseFields(testCase) theme = DashboardTheme(); testCase.verifyTrue(isfield(theme, 'Background'), ... - 'Should contain FastPlotTheme fields'); + 'Should contain FastSenseTheme fields'); testCase.verifyTrue(isfield(theme, 'FontSize'), ... - 'Should contain FastPlotTheme FontSize field'); + 'Should contain FastSenseTheme FontSize field'); end function testContainsDashboardFields(testCase) @@ -57,11 +57,11 @@ function testContainsDashboardFields(testCase) function testPresetInheritance(testCase) theme = DashboardTheme('dark'); - baseDark = FastPlotTheme('dark'); + baseDark = FastSenseTheme('dark'); testCase.verifyEqual(theme.Background, baseDark.Background, ... - 'Should inherit FastPlotTheme dark preset Background'); + 'Should inherit FastSenseTheme dark preset Background'); testCase.verifyEqual(theme.FontSize, baseDark.FontSize, ... - 'Should inherit FastPlotTheme dark preset FontSize'); + 'Should inherit FastSenseTheme dark preset FontSize'); end function testNameValueOverrides(testCase) diff --git a/tests/suite/TestDataStoreWAL.m b/tests/suite/TestDataStoreWAL.m index 8530d036..8515c3f6 100644 --- a/tests/suite/TestDataStoreWAL.m +++ b/tests/suite/TestDataStoreWAL.m @@ -8,7 +8,7 @@ function addPaths(testCase) methods (Test) function testEnableWAL(testCase) x = 1:1000; y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() delete(ds)); ds.enableWAL(); ds.ensureOpen(); @@ -17,7 +17,7 @@ function testEnableWAL(testCase) end function testDisableWAL(testCase) x = 1:1000; y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() delete(ds)); ds.enableWAL(); ds.disableWAL(); @@ -27,7 +27,7 @@ function testDisableWAL(testCase) end function testDataAccessAfterWAL(testCase) x = 1:1000; y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() delete(ds)); ds.enableWAL(); [xOut, yOut] = ds.getRange(1, 1000); diff --git a/tests/suite/TestDatastore.m b/tests/suite/TestDatastore.m index 976c3c08..2418e81c 100644 --- a/tests/suite/TestDatastore.m +++ b/tests/suite/TestDatastore.m @@ -1,6 +1,6 @@ classdef TestDatastore < matlab.unittest.TestCase -%TestDatastore Unit tests for FastPlotDataStore class. -% Tests the SQLite/binary-backed data storage used by FastPlot to handle +%TestDatastore Unit tests for FastSenseDataStore class. +% Tests the SQLite/binary-backed data storage used by FastSense to handle % large datasets without running out of memory. Covers creation, range % queries, slice reads, edge cases, and cleanup. @@ -8,7 +8,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end @@ -17,7 +17,7 @@ function testCreateStore(testCase) % basic construction stores metadata correctly x = linspace(0, 100, 10000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); testCase.verifyEqual(ds.NumPoints, 10000, 'testCreateStore: NumPoints'); testCase.verifyEqual(ds.XMin, x(1), 'testCreateStore: XMin'); @@ -30,14 +30,14 @@ function testCreateStoreWithNaN(testCase) x = linspace(0, 100, 10000); y = sin(x); y(500) = NaN; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); testCase.verifyEqual(ds.HasNaN, true, 'testCreateStoreWithNaN: HasNaN should be true'); end function testEmptyConstruction(testCase) % empty construction returns valid zero-state - ds = FastPlotDataStore(); + ds = FastSenseDataStore(); testCase.addTeardown(@() ds.cleanup()); testCase.verifyEqual(ds.NumPoints, 0, 'testEmptyConstruction: NumPoints'); testCase.verifyTrue(isnan(ds.XMin), 'testEmptyConstruction: XMin should be NaN'); @@ -45,7 +45,7 @@ function testEmptyConstruction(testCase) function testColumnVectorInput(testCase) % accepts column vectors - ds = FastPlotDataStore((1:100)', rand(100,1)); + ds = FastSenseDataStore((1:100)', rand(100,1)); testCase.addTeardown(@() ds.cleanup()); testCase.verifyEqual(ds.NumPoints, 100, 'testColumnVectorInput: NumPoints'); end @@ -54,7 +54,7 @@ function testGetRangeMiddle(testCase) % query a middle range returns correct data x = linspace(0, 100, 50000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr, yr] = ds.getRange(20, 40); testCase.verifyNotEmpty(xr, 'testGetRangeMiddle: should return data'); @@ -70,7 +70,7 @@ function testGetRangeFullSpan(testCase) % range covering entire dataset returns all points x = 1:1000; y = rand(1, 1000); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr, yr] = ds.getRange(1, 1000); testCase.verifyEqual(numel(xr), 1000, 'testGetRangeFullSpan: should get all points'); @@ -84,7 +84,7 @@ function testGetRangeSmall(testCase) n = 200000; x = linspace(0, 1000, n); y = cumsum(randn(1, n)); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr, yr] = ds.getRange(500, 501); % Should get a reasonable number of points (not all 200K) @@ -99,7 +99,7 @@ function testGetRangeOutside(testCase) % range outside data returns empty or edge points x = 1:100; y = rand(1, 100); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr, ~] = ds.getRange(200, 300); % Should either be empty or contain only the last data point @@ -110,7 +110,7 @@ function testReadSliceExact(testCase) % reading a specific index range returns correct data x = 1:5000; y = (1:5000) * 2; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xs, ys] = ds.readSlice(100, 200); testCase.verifyEqual(numel(xs), 101, 'testReadSliceExact: count'); @@ -124,7 +124,7 @@ function testReadSliceFullRange(testCase) % reading entire range matches original data x = linspace(0, 10, 1000); y = cos(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xs, ys] = ds.readSlice(1, 1000); tol = 1e-12; @@ -134,7 +134,7 @@ function testReadSliceFullRange(testCase) function testReadSliceClamped(testCase) % out-of-bounds indices are clamped - ds = FastPlotDataStore(1:100, rand(1, 100)); + ds = FastSenseDataStore(1:100, rand(1, 100)); testCase.addTeardown(@() ds.cleanup()); [xs, ~] = ds.readSlice(-5, 200); testCase.verifyEqual(numel(xs), 100, 'testReadSliceClamped: should clamp to full range'); @@ -148,7 +148,7 @@ function testDataIntegrity(testCase) n = 150000; x = sort(rand(1, n)) * 1000; y = randn(1, n); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xAll, yAll] = ds.readSlice(1, n); tol = 1e-12; @@ -161,7 +161,7 @@ function testDataIntegrityNaN(testCase) x = 1:100; y = rand(1, 100); y([10, 50, 90]) = NaN; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [~, yr] = ds.readSlice(1, 100); testCase.verifyTrue(isnan(yr(10)), 'testDataIntegrityNaN: NaN at 10'); @@ -171,7 +171,7 @@ function testDataIntegrityNaN(testCase) function testOutputShape(testCase) % getRange and readSlice return row vectors - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); testCase.addTeardown(@() ds.cleanup()); [xr, yr] = ds.getRange(100, 200); testCase.verifyTrue(isrow(xr), 'testOutputShape: getRange X must be row'); @@ -183,7 +183,7 @@ function testOutputShape(testCase) function testCleanupDeletesFile(testCase) % cleanup removes temp files from disk - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); % Store the path(s) before cleanup hasDb = ~isempty(ds.DbPath) && exist(ds.DbPath, 'file'); hasBin = ~isempty(ds.BinPath) && exist(ds.BinPath, 'file'); @@ -199,7 +199,7 @@ function testCleanupDeletesFile(testCase) function testDestructorCleanup(testCase) % deleting the object cleans up temp files - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); if ~isempty(ds.DbPath) && exist(ds.DbPath, 'file') fpath = ds.DbPath; else @@ -214,7 +214,7 @@ function testLargeDataset(testCase) n = 500000; x = linspace(0, 10000, n); y = sin(x / 100) + randn(1, n) * 0.1; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); testCase.verifyEqual(ds.NumPoints, n, 'testLargeDataset: NumPoints'); diff --git a/tests/suite/TestDatastoreEdgeCases.m b/tests/suite/TestDatastoreEdgeCases.m index f0d72d91..0f0833cf 100644 --- a/tests/suite/TestDatastoreEdgeCases.m +++ b/tests/suite/TestDatastoreEdgeCases.m @@ -1,5 +1,5 @@ classdef TestDatastoreEdgeCases < matlab.unittest.TestCase -%TestDatastoreEdgeCases Edge-case and stress tests for FastPlotDataStore. +%TestDatastoreEdgeCases Edge-case and stress tests for FastSenseDataStore. % Covers: boundary conditions, special values (Inf, NaN), single-point % datasets, repeated X values, multi-chunk queries, and binary fallback. @@ -7,13 +7,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testSinglePointDataset(testCase) - ds = FastPlotDataStore(5, 10); + ds = FastSenseDataStore(5, 10); testCase.addTeardown(@() ds.cleanup()); testCase.verifyEqual(ds.NumPoints, 1, 'singlePoint: NumPoints'); testCase.verifyEqual(ds.XMin, 5, 'singlePoint: XMin'); @@ -26,7 +26,7 @@ function testSinglePointDataset(testCase) end function testTwoPointDataset(testCase) - ds = FastPlotDataStore([1, 100], [10, 20]); + ds = FastSenseDataStore([1, 100], [10, 20]); testCase.addTeardown(@() ds.cleanup()); [xr, yr] = ds.getRange(1, 100); testCase.verifyEqual(numel(xr), 2, 'twoPoint: getRange count'); @@ -37,7 +37,7 @@ function testInfValuesInY(testCase) x = 1:100; y = rand(1, 100); y(25) = Inf; y(75) = -Inf; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [~, yr] = ds.readSlice(1, 100); testCase.verifyTrue(isinf(yr(25)) && yr(25) > 0, 'infValues: +Inf preserved'); @@ -47,7 +47,7 @@ function testInfValuesInY(testCase) function testAllNaNYData(testCase) x = 1:50; y = nan(1, 50); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); testCase.verifyEqual(ds.HasNaN, true, 'allNaN: HasNaN'); [~, yr] = ds.readSlice(1, 50); @@ -57,7 +57,7 @@ function testAllNaNYData(testCase) function testConstantX(testCase) x = ones(1, 100) * 42; y = rand(1, 100); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); testCase.verifyTrue(ds.XMin == 42 && ds.XMax == 42, 'constantX: XMin==XMax'); [xr, yr] = ds.getRange(41, 43); @@ -67,14 +67,14 @@ function testConstantX(testCase) function testRepeatedXValues(testCase) x = sort(repmat(1:50, 1, 3)); % [1 1 1 2 2 2 ... 50 50 50] y = rand(1, 150); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr, ~] = ds.getRange(10, 20); testCase.verifyTrue(all(xr >= 9 & xr <= 21), 'duplicateX: range respected'); end function testInvertedRange(testCase) - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); testCase.addTeardown(@() ds.cleanup()); [xr, ~] = ds.getRange(500, 100); testCase.verifyEmpty(xr, 'invertedRange: should return empty'); @@ -83,7 +83,7 @@ function testInvertedRange(testCase) function testExactBoundaryQueries(testCase) x = linspace(0, 100, 10000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr, ~] = ds.getRange(0, 0); % exact start testCase.verifyTrue(numel(xr) >= 1, 'exactBoundary: at start'); @@ -92,7 +92,7 @@ function testExactBoundaryQueries(testCase) end function testSinglePointSlice(testCase) - ds = FastPlotDataStore(1:1000, (1:1000)*3); + ds = FastSenseDataStore(1:1000, (1:1000)*3); testCase.addTeardown(@() ds.cleanup()); [xs, ys] = ds.readSlice(500, 500); testCase.verifyEqual(numel(xs), 1, 'singleSlice: count'); @@ -103,7 +103,7 @@ function testEndSlice(testCase) n = 5000; x = 1:n; y = x * 2; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xs, ys] = ds.readSlice(n-2, n); testCase.verifyEqual(numel(xs), 3, 'endSlice: count'); @@ -112,7 +112,7 @@ function testEndSlice(testCase) end function testStartSlice(testCase) - ds = FastPlotDataStore(1:5000, (1:5000)*2); + ds = FastSenseDataStore(1:5000, (1:5000)*2); testCase.addTeardown(@() ds.cleanup()); [xs, ys] = ds.readSlice(1, 3); testCase.verifyEqual(numel(xs), 3, 'startSlice: count'); @@ -122,7 +122,7 @@ function testStartSlice(testCase) function testRepeatedQueriesConsistent(testCase) x = linspace(0, 100, 50000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr1, ~] = ds.getRange(10, 20); [xr2, ~] = ds.getRange(50, 60); @@ -134,7 +134,7 @@ function testRepeatedQueriesConsistent(testCase) function testStressQueries(testCase) x = linspace(0, 1000, 100000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); tic; for i = 1:100 @@ -146,14 +146,14 @@ function testStressQueries(testCase) end function testIdempotentCleanup(testCase) - ds = FastPlotDataStore(1:100, rand(1, 100)); + ds = FastSenseDataStore(1:100, rand(1, 100)); ds.cleanup(); ds.cleanup(); % should not throw testCase.verifyTrue(true, 'idempotent cleanup: no error on double cleanup'); end function testPostCleanupState(testCase) - ds = FastPlotDataStore(1:100, rand(1, 100)); + ds = FastSenseDataStore(1:100, rand(1, 100)); if ~isempty(ds.DbPath); fpath = ds.DbPath; else; fpath = ds.BinPath; end ds.cleanup(); testCase.verifyTrue(~exist(fpath, 'file'), 'afterCleanup: file should be gone'); @@ -162,7 +162,7 @@ function testPostCleanupState(testCase) function testVeryLargeYValues(testCase) x = 1:100; y = ones(1, 100) * 1e300; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [~, yr] = ds.readSlice(1, 100); testCase.verifyTrue(all(yr == 1e300), 'largeValues: preserved'); @@ -171,7 +171,7 @@ function testVeryLargeYValues(testCase) function testVerySmallYValues(testCase) x = 1:100; y = ones(1, 100) * 1e-300; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [~, yr] = ds.readSlice(1, 100); testCase.verifyTrue(all(abs(yr - 1e-300) < 1e-312), 'smallValues: preserved'); @@ -180,7 +180,7 @@ function testVerySmallYValues(testCase) function testNegativeXValues(testCase) x = linspace(-100, -1, 500); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); testCase.verifyTrue(ds.XMin == -100 && ds.XMax == -1, 'negativeX: range'); [xr, ~] = ds.getRange(-60, -40); @@ -192,7 +192,7 @@ function testCrossChunkBoundaryQuery(testCase) n = 200000; x = linspace(0, 1000, n); y = x .* 3; % simple linear for verification - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); % Query range that spans chunk boundaries [xr, yr] = ds.getRange(400, 600); @@ -205,7 +205,7 @@ function testXMonotonicity(testCase) n = 100000; x = linspace(0, 1000, n); y = randn(1, n); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); [xr, ~] = ds.getRange(200, 800); testCase.verifyTrue(all(diff(xr) >= 0), 'monotonicity: X must be non-decreasing'); diff --git a/tests/suite/TestDatetime.m b/tests/suite/TestDatetime.m index e23e38dc..aad0f9dc 100644 --- a/tests/suite/TestDatetime.m +++ b/tests/suite/TestDatetime.m @@ -3,19 +3,19 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testXTypeDefaultIsNumeric(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); testCase.verifyEqual(fp.XType, 'numeric', 'testXTypeDefault: should be numeric'); end function testXTypeDatenum(testCase) - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:99)/24; fp.addLine(x, rand(1,100), 'XType', 'datenum'); testCase.verifyEqual(fp.XType, 'datenum', 'testXTypeDatenum: should be datenum'); @@ -23,7 +23,7 @@ function testXTypeDatenum(testCase) function testDatetimeAutoConvert(testCase) if exist('datetime', 'class') - fp = FastPlot(); + fp = FastSense(); dt = datetime(2024,1,1) + hours(0:99); fp.addLine(dt, rand(1,100)); testCase.verifyEqual(fp.XType, 'datenum', 'testDatetimeAutoConvert: should be datenum'); @@ -32,7 +32,7 @@ function testDatetimeAutoConvert(testCase) end function testTickLabelsAreDateStrings(testCase) - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:99)/24; % ~4 days of hourly data fp.addLine(x, rand(1,100), 'XType', 'datenum'); fp.render(); @@ -50,7 +50,7 @@ function testTickLabelsAreDateStrings(testCase) end function testTickFormatChangesOnZoom(testCase) - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:9999)/86400; % ~0.1s resolution fp.addLine(x, rand(1,10000), 'XType', 'datenum'); fp.render(); @@ -73,9 +73,9 @@ function testTickFormatChangesOnZoom(testCase) function testToolbarFormatX(testCase) % Verify the static helper returns date string for datenum XType xVal = datenum(2024, 3, 15, 10, 30, 45); - result = FastPlotToolbar.formatX(xVal, 'datenum'); + result = FastSenseToolbar.formatX(xVal, 'datenum'); testCase.verifyTrue(any(result == ':'), 'testToolbarFormatX: should contain colon'); - resultNum = FastPlotToolbar.formatX(42.5, 'numeric'); + resultNum = FastSenseToolbar.formatX(42.5, 'numeric'); testCase.verifyTrue(~any(resultNum == ':'), 'testToolbarFormatXNum: should not contain colon'); end end diff --git a/tests/suite/TestDiskAdvanced.m b/tests/suite/TestDiskAdvanced.m index 1f460f63..efb22bef 100644 --- a/tests/suite/TestDiskAdvanced.m +++ b/tests/suite/TestDiskAdvanced.m @@ -1,5 +1,5 @@ classdef TestDiskAdvanced < matlab.unittest.TestCase -%TestDiskAdvanced Advanced integration tests for FastPlot disk storage. +%TestDiskAdvanced Advanced integration tests for FastSense disk storage. % Covers: storage mode transitions, multiple disk lines, pyramid building, % updateData edge cases, re-render after update, and stress scenarios. @@ -7,13 +7,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testMultipleDiskLines(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); for i = 1:5 x = linspace(0, 100, 10000); y = sin(x + i); @@ -34,7 +34,7 @@ function testMultipleDiskLines(testCase) end function testMemoryToDiskTransition(testCase) - fp = FastPlot('MemoryLimit', 10000); + fp = FastSense('MemoryLimit', 10000); fp.addLine(1:50, rand(1, 50)); % memory (800 bytes < 10000) fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -46,7 +46,7 @@ function testMemoryToDiskTransition(testCase) end function testDiskToMemoryTransition(testCase) - fp = FastPlot('MemoryLimit', 10000); + fp = FastSense('MemoryLimit', 10000); fp.addLine(linspace(0, 100, 5000), rand(1, 5000)); % disk fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -67,7 +67,7 @@ function testDiskToMemoryTransition(testCase) end function testUpdateDataPreservesCleanup(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:5000, rand(1, 5000)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -82,7 +82,7 @@ function testUpdateDataPreservesCleanup(testCase) end function testRenderUpdateReRenderCycle(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 20000); fp.addLine(x, sin(x)); fp.render(); @@ -97,7 +97,7 @@ function testRenderUpdateReRenderCycle(testCase) end function testVeryNarrowZoomOnDisk(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 100000; x = linspace(0, 1000, n); y = sin(x); @@ -112,7 +112,7 @@ function testVeryNarrowZoomOnDisk(testCase) end function testZoomInThenOut(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 50000; x = linspace(0, 100, n); y = sin(x); @@ -132,7 +132,7 @@ function testZoomInThenOut(testCase) end function testMixedMemDiskDataFidelity(testCase) - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); x1 = 1:50; y1 = x1 * 2; % memory x2 = linspace(0, 100, 5000); y2 = x2 * 3; % disk fp.addLine(x1, y1, 'DisplayName', 'Mem'); @@ -150,7 +150,7 @@ function testMixedMemDiskDataFidelity(testCase) end function testDeleteCleansAllDiskFiles(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); paths = {}; for i = 1:3 fp.addLine(1:5000, rand(1, 5000)); @@ -165,7 +165,7 @@ function testDeleteCleansAllDiskFiles(testCase) end function testDiskLineWithNaN(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 10000; x = linspace(0, 100, n); y = sin(x); @@ -177,7 +177,7 @@ function testDiskLineWithNaN(testCase) end function testThresholdDiskLineWithNaN(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 10000); y = sin(x); y(500:600) = NaN; fp.addLine(x, y); @@ -188,7 +188,7 @@ function testThresholdDiskLineWithNaN(testCase) end function testLowerThresholdOnDiskLine(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 10000); y = sin(x); fp.addLine(x, y); @@ -199,7 +199,7 @@ function testLowerThresholdOnDiskLine(testCase) end function testMultipleThresholdsOnDiskLine(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 20000); y = sin(x); fp.addLine(x, y); @@ -212,7 +212,7 @@ function testMultipleThresholdsOnDiskLine(testCase) end function testMemoryLimitExactBoundary(testCase) - fp = FastPlot('MemoryLimit', 1600); % 100 points * 8 * 2 = 1600 + fp = FastSense('MemoryLimit', 1600); % 100 points * 8 * 2 = 1600 fp.addLine(1:100, rand(1, 100)); % 1600 bytes == 1600 limit -> not strictly greater, stays in memory testCase.verifyEmpty(fp.Lines(1).DataStore, 'boundary: at limit stays memory'); @@ -222,7 +222,7 @@ function testMemoryLimitExactBoundary(testCase) end function testRapidSequentialUpdates(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:5000, rand(1, 5000)); fp.render(); testCase.addTeardown(@close, fp.hFigure); diff --git a/tests/suite/TestDiskStorage.m b/tests/suite/TestDiskStorage.m index c61b56b1..bd09a421 100644 --- a/tests/suite/TestDiskStorage.m +++ b/tests/suite/TestDiskStorage.m @@ -1,43 +1,43 @@ classdef TestDiskStorage < matlab.unittest.TestCase -%TestDiskStorage Integration tests for FastPlot disk-backed storage. -% Tests that FastPlot correctly stores large datasets on disk via -% FastPlotDataStore and that render, zoom/pan, updateData, and cleanup +%TestDiskStorage Integration tests for FastSense disk-backed storage. +% Tests that FastSense correctly stores large datasets on disk via +% FastSenseDataStore and that render, zoom/pan, updateData, and cleanup % all work transparently with disk-backed lines. methods (TestClassSetup) function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testDefaultStorageMode(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); testCase.verifyEqual(fp.StorageMode, 'auto', 'testDefaultStorageMode'); end function testStorageModeConstructor(testCase) - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); testCase.verifyEqual(fp.StorageMode, 'disk', 'testStorageModeConstructor'); end function testMemoryLimitDefault(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); testCase.verifyEqual(fp.MemoryLimit, 500e6, 'testMemoryLimitDefault'); end function testMemoryLimitConstructor(testCase) - fp = FastPlot('MemoryLimit', 100e6); + fp = FastSense('MemoryLimit', 100e6); testCase.verifyEqual(fp.MemoryLimit, 100e6, 'testMemoryLimitConstructor'); end function testAutoModeDiskTrigger(testCase) % data exceeding MemoryLimit goes to disk - fp = FastPlot('MemoryLimit', 1000); % very low threshold: 1000 bytes + fp = FastSense('MemoryLimit', 1000); % very low threshold: 1000 bytes n = 1000; % 1000 points * 8 bytes * 2 = 16000 bytes > 1000 x = linspace(0, 10, n); y = sin(x); @@ -54,7 +54,7 @@ function testAutoModeDiskTrigger(testCase) function testAutoModeMemoryForSmall(testCase) % small data stays in memory - fp = FastPlot('MemoryLimit', 1e9); % 1 GB threshold + fp = FastSense('MemoryLimit', 1e9); % 1 GB threshold fp.addLine(1:100, rand(1, 100)); testCase.verifyEmpty(fp.Lines(1).DataStore, ... 'testAutoModeMemoryForSmall: should NOT have DataStore'); @@ -64,7 +64,7 @@ function testAutoModeMemoryForSmall(testCase) function testForceDiskMode(testCase) % StorageMode='disk' forces all data to disk - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:50, rand(1, 50)); testCase.verifyNotEmpty(fp.Lines(1).DataStore, ... 'testForceDiskMode: small data should still go to disk'); @@ -74,7 +74,7 @@ function testForceDiskMode(testCase) function testForceMemoryMode(testCase) % StorageMode='memory' keeps all data in RAM - fp = FastPlot('StorageMode', 'memory', 'MemoryLimit', 100); + fp = FastSense('StorageMode', 'memory', 'MemoryLimit', 100); fp.addLine(1:10000, rand(1, 10000)); testCase.verifyEmpty(fp.Lines(1).DataStore, ... 'testForceMemoryMode: should NOT use disk even if above limit'); @@ -82,7 +82,7 @@ function testForceMemoryMode(testCase) function testRenderDiskLine(testCase) % disk-backed line renders without error - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 20000; x = linspace(0, 100, n); y = sin(x); @@ -102,7 +102,7 @@ function testRenderDiskLine(testCase) function testRenderMixedLines(testCase) % mix of memory and disk lines renders - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); fp.addLine(1:10, rand(1, 10), 'DisplayName', 'Small'); % memory fp.addLine(linspace(0,100,5000), rand(1,5000), 'DisplayName', 'Large'); % disk fp.render(); @@ -114,7 +114,7 @@ function testRenderMixedLines(testCase) function testZoomDiskLine(testCase) % zooming re-downsamples from disk correctly - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 50000; x = linspace(0, 100, n); y = sin(x); @@ -136,7 +136,7 @@ function testZoomDiskLine(testCase) function testZoomDiskLineDataFidelity(testCase) % zoomed data preserves signal shape - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 100000; x = linspace(0, 100, n); y = x * 2; % simple linear — easy to verify @@ -158,7 +158,7 @@ function testZoomDiskLineDataFidelity(testCase) function testUpdateDataDisk(testCase) % updateData replaces disk-backed data - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 10, 10000); y = sin(x); fp.addLine(x, y); @@ -180,7 +180,7 @@ function testUpdateDataDisk(testCase) function testThresholdsDiskLine(testCase) % violations render correctly on disk lines - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 10000; x = linspace(0, 100, n); y = sin(x); % values between -1 and 1 @@ -193,8 +193,8 @@ function testThresholdsDiskLine(testCase) end function testDeleteCleansDiskFiles(testCase) - % deleting FastPlot cleans up DataStore files - fp = FastPlot('StorageMode', 'disk'); + % deleting FastSense cleans up DataStore files + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:5000, rand(1, 5000)); ds = fp.Lines(1).DataStore; % Get the file path before deletion @@ -212,7 +212,7 @@ function testDeleteCleansDiskFiles(testCase) function testLineNumPoints(testCase) % works for both memory and disk lines - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); fp.addLine(1:50, rand(1, 50)); % memory fp.addLine(1:5000, rand(1, 5000)); % disk testCase.verifyEqual(fp.lineNumPoints(1), 50, 'testLineNumPoints: memory line'); @@ -221,7 +221,7 @@ function testLineNumPoints(testCase) function testLineXRange(testCase) % returns correct endpoints for both storage types - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); fp.addLine(5:100, rand(1, 96)); % memory fp.addLine(linspace(10,200,5000), rand(1,5000)); % disk [mn1, mx1] = fp.lineXRange(1); diff --git a/tests/suite/TestDownsampleViolations.m b/tests/suite/TestDownsampleViolations.m index b8d47b97..ee63ba72 100644 --- a/tests/suite/TestDownsampleViolations.m +++ b/tests/suite/TestDownsampleViolations.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestEventSnapshot.m b/tests/suite/TestEventSnapshot.m index 6a4129cb..22a527ce 100644 --- a/tests/suite/TestEventSnapshot.m +++ b/tests/suite/TestEventSnapshot.m @@ -4,7 +4,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'EventDetection')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'SensorThreshold')); - addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastPlot')); + addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastSense')); setup(); end end diff --git a/tests/suite/TestFastplotTheme.m b/tests/suite/TestFastSenseTheme.m similarity index 86% rename from tests/suite/TestFastplotTheme.m rename to tests/suite/TestFastSenseTheme.m index 899ae762..ae451215 100644 --- a/tests/suite/TestFastplotTheme.m +++ b/tests/suite/TestFastSenseTheme.m @@ -1,34 +1,34 @@ -classdef TestFastplotTheme < matlab.unittest.TestCase +classdef TestFastSenseTheme < matlab.unittest.TestCase methods (TestClassSetup) function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testThemeConstructorString(testCase) - fp = FastPlot('Theme', 'dark'); + fp = FastSense('Theme', 'dark'); testCase.verifyTrue(isstruct(fp.Theme), 'testThemeConstructorString: Theme must be struct'); testCase.verifyTrue(all(fp.Theme.Background < [0.2 0.2 0.2]), 'testThemeConstructorString: dark bg'); end function testThemeConstructorStruct(testCase) custom = struct('Background', [0.5 0.5 0.5]); - fp = FastPlot('Theme', custom); + fp = FastSense('Theme', custom); testCase.verifyEqual(fp.Theme.Background, [0.5 0.5 0.5], 'testThemeConstructorStruct'); testCase.verifyTrue(isfield(fp.Theme, 'FontSize'), 'testThemeConstructorStruct: inherits defaults'); end function testDefaultThemeWhenNoneSpecified(testCase) - fp = FastPlot(); + fp = FastSense(); testCase.verifyTrue(isstruct(fp.Theme), 'testDefaultTheme: must have theme'); testCase.verifyEqual(fp.Theme.Background, [1 1 1], 'testDefaultTheme: default bg'); end function testThemeAppliedOnRender(testCase) - fp = FastPlot('Theme', 'dark'); + fp = FastSense('Theme', 'dark'); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -39,7 +39,7 @@ function testThemeAppliedOnRender(testCase) end function testThemeFontApplied(testCase) - fp = FastPlot('Theme', 'scientific'); + fp = FastSense('Theme', 'scientific'); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -50,7 +50,7 @@ function testThemeWithParentAxes(testCase) fig = figure('Visible', 'off'); testCase.addTeardown(@close, fig); ax = axes('Parent', fig); - fp = FastPlot('Parent', ax, 'Theme', 'dark'); + fp = FastSense('Parent', ax, 'Theme', 'dark'); fp.addLine(1:100, rand(1,100)); fp.render(); axColor = get(ax, 'Color'); @@ -58,7 +58,7 @@ function testThemeWithParentAxes(testCase) end function testBackwardCompatNoTheme(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -66,7 +66,7 @@ function testBackwardCompatNoTheme(testCase) end function testAutoColorCycling(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.addLine(1:10, rand(1,10)); fp.addLine(1:10, rand(1,10)); @@ -78,7 +78,7 @@ function testAutoColorCycling(testCase) end function testExplicitColorSkipsCycle(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'Color', [1 0 0]); fp.addLine(1:10, rand(1,10)); testCase.verifyEqual(fp.Lines(1).Options.Color, [1 0 0], 'testExplicitColorSkipsCycle: explicit'); @@ -87,14 +87,14 @@ function testExplicitColorSkipsCycle(testCase) end function testThresholdUsesThemeDefaults(testCase) - fp = FastPlot('Theme', struct('ThresholdColor', [0 1 0], 'ThresholdStyle', ':')); + fp = FastSense('Theme', struct('ThresholdColor', [0 1 0], 'ThresholdStyle', ':')); fp.addThreshold(5.0); testCase.verifyEqual(fp.Thresholds(1).Color, [0 1 0], 'testThresholdThemeDefaults: Color'); testCase.verifyEqual(fp.Thresholds(1).LineStyle, ':', 'testThresholdThemeDefaults: Style'); end function testThresholdExplicitOverridesTheme(testCase) - fp = FastPlot('Theme', struct('ThresholdColor', [0 1 0])); + fp = FastSense('Theme', struct('ThresholdColor', [0 1 0])); fp.addThreshold(5.0, 'Color', [1 0 0]); testCase.verifyEqual(fp.Thresholds(1).Color, [1 0 0], 'testThresholdOverride: Color'); end diff --git a/tests/suite/TestFastPlotWidget.m b/tests/suite/TestFastSenseWidget.m similarity index 72% rename from tests/suite/TestFastPlotWidget.m rename to tests/suite/TestFastSenseWidget.m index 0bfeadc7..2d10befb 100644 --- a/tests/suite/TestFastPlotWidget.m +++ b/tests/suite/TestFastSenseWidget.m @@ -1,4 +1,4 @@ -classdef TestFastPlotWidget < matlab.unittest.TestCase +classdef TestFastSenseWidget < matlab.unittest.TestCase methods (TestClassSetup) function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); @@ -7,15 +7,15 @@ function addPaths(testCase) end methods (Test) - function testTypeIsFastplot(testCase) - w = FastPlotWidget(); - testCase.verifyEqual(w.Type, 'fastplot'); + function testTypeIsFastSense(testCase) + w = FastSenseWidget(); + testCase.verifyEqual(w.Type, 'fastsense'); end function testDefaultPosition(testCase) - w = FastPlotWidget(); + w = FastSenseWidget(); testCase.verifyEqual(w.Position, [1 1 12 3], ... - 'Default FastPlotWidget size should be 12x3'); + 'Default FastSenseWidget size should be 12x3'); end function testSensorBinding(testCase) @@ -23,7 +23,7 @@ function testSensorBinding(testCase) s.X = 1:100; s.Y = rand(1,100); - w = FastPlotWidget('Sensor', s); + w = FastSenseWidget('Sensor', s); testCase.verifyEqual(w.Sensor, s); testCase.verifyEqual(w.Title, 'Temperature', ... 'Title should default to Sensor.Name'); @@ -32,9 +32,9 @@ function testSensorBinding(testCase) function testDataStoreBinding(testCase) x = 1:1000; y = rand(1,1000); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); - w = FastPlotWidget('DataStoreObj', ds); + w = FastSenseWidget('DataStoreObj', ds); testCase.verifyEqual(w.DataStoreObj, ds); end @@ -45,14 +45,14 @@ function testRenderCreatesAxes(testCase) hp = uipanel('Parent', hFig, 'Units', 'normalized', ... 'Position', [0 0 1 1]); - w = FastPlotWidget(); + w = FastSenseWidget(); w.XData = 1:100; w.YData = rand(1,100); w.render(hp); - testCase.verifyNotEmpty(w.FastPlotObj, ... - 'Should create a FastPlot instance'); - testCase.verifyTrue(isa(w.FastPlotObj, 'FastPlot')); + testCase.verifyNotEmpty(w.FastSenseObj, ... + 'Should create a FastSense instance'); + testCase.verifyTrue(isa(w.FastSenseObj, 'FastSense')); end function testRenderWithSensor(testCase) @@ -68,20 +68,20 @@ function testRenderWithSensor(testCase) s.addThresholdRule(struct(), 80, 'Direction', 'upper', 'Label', 'Hi Alarm'); s.resolve(); - w = FastPlotWidget('Sensor', s); + w = FastSenseWidget('Sensor', s); w.render(hp); - testCase.verifyNotEmpty(w.FastPlotObj); - testCase.verifyGreaterThanOrEqual(numel(w.FastPlotObj.Lines), 1); + testCase.verifyNotEmpty(w.FastSenseObj); + testCase.verifyGreaterThanOrEqual(numel(w.FastSenseObj.Lines), 1); end function testToStructRoundTrip(testCase) - w = FastPlotWidget('Title', 'My Plot', 'Position', [5 2 16 3]); + w = FastSenseWidget('Title', 'My Plot', 'Position', [5 2 16 3]); w.XData = 1:10; w.YData = rand(1,10); s = w.toStruct(); - testCase.verifyEqual(s.type, 'fastplot'); + testCase.verifyEqual(s.type, 'fastsense'); testCase.verifyEqual(s.title, 'My Plot'); testCase.verifyEqual(s.position.col, 5); end @@ -90,7 +90,7 @@ function testToStructWithSensor(testCase) sensor = Sensor('P-201', 'Name', 'Pressure'); sensor.X = 1:100; sensor.Y = rand(1,100); - w = FastPlotWidget('Sensor', sensor); + w = FastSenseWidget('Sensor', sensor); s = w.toStruct(); testCase.verifyEqual(s.source.type, 'sensor'); @@ -99,12 +99,12 @@ function testToStructWithSensor(testCase) function testFromStructWithData(testCase) s = struct(); - s.type = 'fastplot'; + s.type = 'fastsense'; s.title = 'Restored Plot'; s.position = struct('col', 1, 'row', 1, 'width', 12, 'height', 3); s.source = struct('type', 'data', 'x', 1:10, 'y', rand(1,10)); - w = FastPlotWidget.fromStruct(s); + w = FastSenseWidget.fromStruct(s); testCase.verifyEqual(w.Title, 'Restored Plot'); testCase.verifyEqual(w.Position, [1 1 12 3]); testCase.verifyLength(w.XData, 10); diff --git a/tests/suite/TestFigureLayout.m b/tests/suite/TestFigureLayout.m index fe7b91f0..d4ea89c6 100644 --- a/tests/suite/TestFigureLayout.m +++ b/tests/suite/TestFigureLayout.m @@ -3,28 +3,28 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testConstruction(testCase) - fig = FastPlotGrid(2, 3); + fig = FastSenseGrid(2, 3); testCase.addTeardown(@close, fig.hFigure); testCase.verifyEqual(fig.Grid, [2 3], 'testConstruction: Grid'); testCase.verifyNotEmpty(fig.hFigure, 'testConstruction: hFigure'); testCase.verifyTrue(ishandle(fig.hFigure), 'testConstruction: hFigure valid'); end - function testTileReturnsFastPlot(testCase) - fig = FastPlotGrid(2, 1); + function testTileReturnsFastSense(testCase) + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); fp = fig.tile(1); - testCase.verifyTrue(isa(fp, 'FastPlot'), 'testTileReturnsFastPlot'); + testCase.verifyTrue(isa(fp, 'FastSense'), 'testTileReturnsFastSense'); end function testTileLazy(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); fp1a = fig.tile(1); fp1b = fig.tile(1); @@ -34,7 +34,7 @@ function testTileLazy(testCase) end function testTileCreatesAxes(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); fp = fig.tile(1); fp.addLine(1:100, rand(1,100)); @@ -44,7 +44,7 @@ function testTileCreatesAxes(testCase) end function testMultipleTiles(testCase) - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); testCase.addTeardown(@close, fig.hFigure); for i = 1:4 fp = fig.tile(i); @@ -58,7 +58,7 @@ function testMultipleTiles(testCase) end function testRenderAllSkipsRendered(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); fp1 = fig.tile(1); fp1.addLine(1:10, rand(1,10)); @@ -70,7 +70,7 @@ function testRenderAllSkipsRendered(testCase) end function testOutOfBoundsTileErrors(testCase) - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); testCase.addTeardown(@close, fig.hFigure); threw = false; try @@ -82,7 +82,7 @@ function testOutOfBoundsTileErrors(testCase) end function testTileSpanning(testCase) - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); testCase.addTeardown(@close, fig.hFigure); fig.setTileSpan(1, [1 2]); % tile 1 spans both columns fp1 = fig.tile(1); @@ -94,14 +94,14 @@ function testTileSpanning(testCase) end function testFigureThemePassedToTiles(testCase) - fig = FastPlotGrid(2, 1, 'Theme', 'dark'); + fig = FastSenseGrid(2, 1, 'Theme', 'dark'); testCase.addTeardown(@close, fig.hFigure); fp = fig.tile(1); testCase.verifyTrue(all(fp.Theme.Background < [0.2 0.2 0.2]), 'testFigureThemePassedToTiles'); end function testTileThemeOverride(testCase) - fig = FastPlotGrid(2, 1, 'Theme', 'dark'); + fig = FastSenseGrid(2, 1, 'Theme', 'dark'); testCase.addTeardown(@close, fig.hFigure); fig.setTileTheme(1, struct('AxesColor', [0.3 0 0])); fp = fig.tile(1); @@ -110,14 +110,14 @@ function testTileThemeOverride(testCase) end function testFigureProperties(testCase) - fig = FastPlotGrid(1, 1, 'Name', 'MyDash', 'Position', [50 50 800 600]); + fig = FastSenseGrid(1, 1, 'Name', 'MyDash', 'Position', [50 50 800 600]); testCase.addTeardown(@close, fig.hFigure); name = get(fig.hFigure, 'Name'); testCase.verifyEqual(name, 'MyDash', 'testFigureProperties: Name'); end function testTileLabels(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); fp = fig.tile(1); fp.addLine(1:50, rand(1,50)); @@ -129,7 +129,7 @@ function testTileLabels(testCase) end function testAxesReturnsRawAxes(testCase) - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); testCase.addTeardown(@close, fig.hFigure); ax = fig.axes(1); testCase.verifyTrue(ishandle(ax), 'testAxesReturnsRawAxes: valid handle'); @@ -137,7 +137,7 @@ function testAxesReturnsRawAxes(testCase) end function testAxesLazy(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); ax1 = fig.axes(1); ax2 = fig.axes(1); @@ -145,7 +145,7 @@ function testAxesLazy(testCase) end function testTileThenAxesErrors(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); fig.tile(1); threw = false; @@ -158,7 +158,7 @@ function testTileThenAxesErrors(testCase) end function testAxesThenTileErrors(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); fig.axes(1); threw = false; @@ -171,7 +171,7 @@ function testAxesThenTileErrors(testCase) end function testMixedRenderAll(testCase) - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); testCase.addTeardown(@close, fig.hFigure); fig.tile(1).addLine(1:50, rand(1,50)); ax2 = fig.axes(2); bar(ax2, [1 2 3], [10 20 15]); @@ -186,7 +186,7 @@ function testMixedRenderAll(testCase) end function testAxesThemeApplied(testCase) - fig = FastPlotGrid(1, 1, 'Theme', 'dark'); + fig = FastSenseGrid(1, 1, 'Theme', 'dark'); testCase.addTeardown(@close, fig.hFigure); ax = fig.axes(1); bgColor = get(ax, 'Color'); @@ -194,7 +194,7 @@ function testAxesThemeApplied(testCase) end function testLabelsOnRawAxes(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); testCase.addTeardown(@close, fig.hFigure); ax = fig.axes(1); bar(ax, [1 2 3], [10 20 15]); @@ -207,7 +207,7 @@ function testLabelsOnRawAxes(testCase) end function testAxesOutOfBounds(testCase) - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); testCase.addTeardown(@close, fig.hFigure); threw = false; try @@ -219,7 +219,7 @@ function testAxesOutOfBounds(testCase) end function testAxesTileSpanning(testCase) - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); testCase.addTeardown(@close, fig.hFigure); fig.setTileSpan(1, [1 2]); ax = fig.axes(1); diff --git a/tests/suite/TestLinkedAxes.m b/tests/suite/TestLinkedAxes.m index a07eb03f..e7d353bf 100644 --- a/tests/suite/TestLinkedAxes.m +++ b/tests/suite/TestLinkedAxes.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end @@ -14,11 +14,11 @@ function testLinkedZoomPropagates(testCase) ax1 = subplot(2,1,1, 'Parent', fig); ax2 = subplot(2,1,2, 'Parent', fig); - fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'testgroup'); + fp1 = FastSense('Parent', ax1, 'LinkGroup', 'testgroup'); fp1.addLine(1:1000, rand(1,1000)); fp1.render(); - fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'testgroup'); + fp2 = FastSense('Parent', ax2, 'LinkGroup', 'testgroup'); fp2.addLine(1:1000, rand(1,1000)); fp2.render(); @@ -39,11 +39,11 @@ function testUnlinkedDoesNotPropagate(testCase) ax1 = subplot(2,1,1, 'Parent', fig); ax2 = subplot(2,1,2, 'Parent', fig); - fp1 = FastPlot('Parent', ax1); + fp1 = FastSense('Parent', ax1); fp1.addLine(1:1000, rand(1,1000)); fp1.render(); - fp2 = FastPlot('Parent', ax2); + fp2 = FastSense('Parent', ax2); fp2.addLine(1:1000, rand(1,1000)); fp2.render(); diff --git a/tests/suite/TestLivePipeline.m b/tests/suite/TestLivePipeline.m index dd4204c3..4a492849 100644 --- a/tests/suite/TestLivePipeline.m +++ b/tests/suite/TestLivePipeline.m @@ -4,7 +4,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'EventDetection')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'SensorThreshold')); - addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastPlot')); + addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastSense')); setup(); end end diff --git a/tests/suite/TestLttbDownsample.m b/tests/suite/TestLttbDownsample.m index 4826c73e..7bf2ad99 100644 --- a/tests/suite/TestLttbDownsample.m +++ b/tests/suite/TestLttbDownsample.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestMexEdgeCases.m b/tests/suite/TestMexEdgeCases.m index 9334bb4e..94cd7799 100644 --- a/tests/suite/TestMexEdgeCases.m +++ b/tests/suite/TestMexEdgeCases.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestMexParity.m b/tests/suite/TestMexParity.m index 2fa035bd..db605c45 100644 --- a/tests/suite/TestMexParity.m +++ b/tests/suite/TestMexParity.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestMinmaxDownsample.m b/tests/suite/TestMinmaxDownsample.m index a8ab8f22..30e6ceb0 100644 --- a/tests/suite/TestMinmaxDownsample.m +++ b/tests/suite/TestMinmaxDownsample.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestMksqliteEdgeCases.m b/tests/suite/TestMksqliteEdgeCases.m index 0df938e1..4ea014c1 100644 --- a/tests/suite/TestMksqliteEdgeCases.m +++ b/tests/suite/TestMksqliteEdgeCases.m @@ -14,7 +14,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestMksqliteTypes.m b/tests/suite/TestMksqliteTypes.m index 5e414689..eb081e17 100644 --- a/tests/suite/TestMksqliteTypes.m +++ b/tests/suite/TestMksqliteTypes.m @@ -13,7 +13,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end @@ -135,7 +135,7 @@ function testAddColumnCellStrings(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -151,7 +151,7 @@ function testAddColumnLogical(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -169,7 +169,7 @@ function testAddColumnCategoricalStruct(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -188,7 +188,7 @@ function testAddColumnChar(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); charData = char(mod(0:n-1, 26) + 65); % A-Z repeating @@ -201,7 +201,7 @@ function testGetColumnSliceLabels(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -219,7 +219,7 @@ function testGetColumnSliceLogical(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); flags = logical(mod(1:n, 2)); @@ -236,7 +236,7 @@ function testGetColumnSliceCategorical(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -260,7 +260,7 @@ function testGetColumnSliceChar(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); charData = char(mod(0:n-1, 26) + 65); @@ -276,7 +276,7 @@ function testGetColumnRangeLabels(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -292,7 +292,7 @@ function testGetColumnRangeFlags(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); flags = logical(mod(1:n, 2)); @@ -306,7 +306,7 @@ function testListColumns(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -330,7 +330,7 @@ function testAddColumnDuplicateRejection(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); labels = cell(1, n); @@ -350,7 +350,7 @@ function testAddColumnSizeMismatchRejection(testCase) n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); testCase.addTeardown(@() ds.cleanup()); threw = false; @@ -368,14 +368,14 @@ function testToCategoricalRoundTrip(testCase) n2 = 1000; x2 = linspace(0, 100, n2); y2 = sin(x2); - ds2 = FastPlotDataStore(x2, y2); + ds2 = FastSenseDataStore(x2, y2); testCase.addTeardown(@() ds2.cleanup()); catData2.codes = uint32(mod(0:n2-1, 3) + 1); catData2.categories = {'low', 'medium', 'high'}; ds2.addColumn('cat_struct', catData2); slice = ds2.getColumnSlice('cat_struct', 1, 6); - labels_back = FastPlotDataStore.toCategorical(slice); + labels_back = FastSenseDataStore.toCategorical(slice); if exist('categorical', 'class') testCase.verifyTrue(isa(labels_back, 'categorical'), ... 'toCategorical: should return categorical in MATLAB'); @@ -395,7 +395,7 @@ function testToCategoricalRoundTrip(testCase) function testToCategoricalBadInput(testCase) threw = false; try - FastPlotDataStore.toCategorical('not_a_struct'); + FastSenseDataStore.toCategorical('not_a_struct'); catch threw = true; end @@ -409,7 +409,7 @@ function testAutoConvertCategorical(testCase) n2 = 1000; x2 = linspace(0, 100, n2); y2 = sin(x2); - ds2 = FastPlotDataStore(x2, y2); + ds2 = FastSenseDataStore(x2, y2); testCase.addTeardown(@() ds2.cleanup()); c_native = categorical({'a','b','c','a','b'}, {'a','b','c'}); @@ -418,7 +418,7 @@ function testAutoConvertCategorical(testCase) sliceN = ds2.getColumnSlice('cat_native', 1, 5); testCase.verifyTrue(isstruct(sliceN) && isfield(sliceN, 'codes'), ... 'auto-convert categorical: should be stored as struct'); - labelsN = FastPlotDataStore.toCategorical(sliceN); + labelsN = FastSenseDataStore.toCategorical(sliceN); testCase.verifyTrue(isa(labelsN, 'categorical'), ... 'auto-convert categorical: toCategorical should return categorical'); end @@ -428,7 +428,7 @@ function testFromCategorical(testCase) return; end c_test = categorical({'x','y','z','x'}, {'x','y','z'}); - s_test = FastPlotDataStore.fromCategorical(c_test); + s_test = FastSenseDataStore.fromCategorical(c_test); testCase.verifyEqual(s_test.codes, uint32([1 2 3 1]), ... 'fromCategorical: codes mismatch'); testCase.verifyEqual(s_test.categories, {'x','y','z'}, ... @@ -442,7 +442,7 @@ function testAutoConvertStringArray(testCase) n2 = 1000; x2 = linspace(0, 100, n2); y2 = sin(x2); - ds2 = FastPlotDataStore(x2, y2); + ds2 = FastSenseDataStore(x2, y2); testCase.addTeardown(@() ds2.cleanup()); labels = cell(1, n2); diff --git a/tests/suite/TestMultiThreshold.m b/tests/suite/TestMultiThreshold.m index d43524b7..70a652fb 100644 --- a/tests/suite/TestMultiThreshold.m +++ b/tests/suite/TestMultiThreshold.m @@ -3,13 +3,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testEachThresholdGetsOwnLine(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, randn(1,100)); fp.addThreshold(2.0, 'Direction', 'upper', 'Color', 'r', 'LineStyle', '--'); fp.addThreshold(1.0, 'Direction', 'upper', 'Color', [1 0.6 0], 'LineStyle', ':'); @@ -36,7 +36,7 @@ function testEachThresholdGetsOwnLine(testCase) end function testEachThresholdGetsOwnViolationMarkers(testCase) - fp = FastPlot(); + fp = FastSense(); y = [0 0 0 1.5 1.5 0 0 0 2.5 2.5 0 0]; fp.addLine(1:12, y); fp.addThreshold(2.0, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); @@ -63,7 +63,7 @@ function testEachThresholdGetsOwnViolationMarkers(testCase) end function testThresholdWithoutViolationsGetsNoMarkers(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, zeros(1,10)); fp.addThreshold(5.0, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp.render(); @@ -77,7 +77,7 @@ function testThresholdWithoutViolationsGetsNoMarkers(testCase) end function testShowViolationsFalseGetsNoMarkerHandle(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, 5*ones(1,10)); fp.addThreshold(2.0, 'Direction', 'upper', 'ShowViolations', false, 'Color', 'r'); fp.render(); @@ -87,7 +87,7 @@ function testShowViolationsFalseGetsNoMarkerHandle(testCase) end function testViolationsUpdateOnZoomPerThreshold(testCase) - fp = FastPlot(); + fp = FastSense(); y = [zeros(1,100), 1.5*ones(1,100), zeros(1,100), 2.5*ones(1,100), zeros(1,100)]; x = 1:500; fp.addLine(x, y); @@ -111,16 +111,16 @@ function testViolationsUpdateOnZoomPerThreshold(testCase) end function testUserDataTagging(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, randn(1,100)); fp.addThreshold(1.0, 'Direction', 'upper', 'Label', 'AlarmHi', 'Color', 'r'); fp.render(); testCase.addTeardown(@close, fp.hFigure); ud = get(fp.Thresholds(1).hLine, 'UserData'); - testCase.verifyEqual(ud.FastPlot.Type, 'threshold', 'UserData Type'); - testCase.verifyEqual(ud.FastPlot.Name, 'AlarmHi', 'UserData Name'); - testCase.verifyEqual(ud.FastPlot.ThresholdValue, 1.0, 'UserData ThresholdValue'); + testCase.verifyEqual(ud.FastSense.Type, 'threshold', 'UserData Type'); + testCase.verifyEqual(ud.FastSense.Name, 'AlarmHi', 'UserData Name'); + testCase.verifyEqual(ud.FastSense.ThresholdValue, 1.0, 'UserData ThresholdValue'); end end end diff --git a/tests/suite/TestNotificationService.m b/tests/suite/TestNotificationService.m index f46ade9c..6b2f39df 100644 --- a/tests/suite/TestNotificationService.m +++ b/tests/suite/TestNotificationService.m @@ -4,7 +4,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'EventDetection')); addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'SensorThreshold')); - addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastPlot')); + addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastSense')); setup(); end end diff --git a/tests/suite/TestRender.m b/tests/suite/TestRender.m index 77e15edf..83eb894e 100644 --- a/tests/suite/TestRender.m +++ b/tests/suite/TestRender.m @@ -3,13 +3,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testCreatesNewFigure(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'Test'); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -21,14 +21,14 @@ function testUsesExistingAxes(testCase) fig = figure('Visible', 'off'); testCase.addTeardown(@close, fig); ax = axes('Parent', fig); - fp = FastPlot('Parent', ax); + fp = FastSense('Parent', ax); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.verifyEqual(fp.hAxes, ax, 'testUsesExistingAxes'); end function testCreatesLineObjects(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'L1'); fp.addLine(1:100, rand(1,100), 'DisplayName', 'L2'); fp.render(); @@ -39,30 +39,30 @@ function testCreatesLineObjects(testCase) end function testUserDataTagging(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'Sensor1'); fp.addThreshold(0.5, 'Label', 'UpperLim'); fp.render(); testCase.addTeardown(@close, fp.hFigure); ud = get(fp.Lines(1).hLine, 'UserData'); - testCase.verifyEqual(ud.FastPlot.Type, 'data_line', 'testUserDataTagging: Type'); - testCase.verifyEqual(ud.FastPlot.Name, 'Sensor1', 'testUserDataTagging: Name'); - testCase.verifyEqual(ud.FastPlot.LineIndex, 1, 'testUserDataTagging: LineIndex'); + testCase.verifyEqual(ud.FastSense.Type, 'data_line', 'testUserDataTagging: Type'); + testCase.verifyEqual(ud.FastSense.Name, 'Sensor1', 'testUserDataTagging: Name'); + testCase.verifyEqual(ud.FastSense.LineIndex, 1, 'testUserDataTagging: LineIndex'); end function testThresholdLineCreated(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addThreshold(0.5, 'Direction', 'upper', 'Label', 'UL'); fp.render(); testCase.addTeardown(@close, fp.hFigure); testCase.verifyTrue(isgraphics(fp.Thresholds(1).hLine, 'line'), 'testThresholdLineCreated'); ud = get(fp.Thresholds(1).hLine, 'UserData'); - testCase.verifyEqual(ud.FastPlot.Type, 'threshold', 'testThresholdLineCreated: Type'); + testCase.verifyEqual(ud.FastSense.Type, 'threshold', 'testThresholdLineCreated: Type'); end function testViolationMarkersCreated(testCase) - fp = FastPlot(); + fp = FastSense(); y = [0.1 0.2 0.8 0.9 0.3 0.1]; fp.addLine(1:6, y); fp.addThreshold(0.5, 'Direction', 'upper', 'ShowViolations', true); @@ -76,7 +76,7 @@ function testViolationMarkersCreated(testCase) end function testDoubleRenderError(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -90,7 +90,7 @@ function testDoubleRenderError(testCase) end function testStaticAxisLimits(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); diff --git a/tests/suite/TestSensorDetailPlot.m b/tests/suite/TestSensorDetailPlot.m index f4d99411..d3b0f3b7 100644 --- a/tests/suite/TestSensorDetailPlot.m +++ b/tests/suite/TestSensorDetailPlot.m @@ -50,12 +50,12 @@ function testConstructorCustomOptions(testCase) delete(sdp); end - %% Render creates two FastPlot instances + %% Render creates two FastSense instances function testRenderCreatesMainAndNavigator(testCase) sdp = SensorDetailPlot(testCase.TestData.sensor); sdp.render(); - testCase.verifyClass(sdp.MainPlot, ?FastPlot); - testCase.verifyClass(sdp.NavigatorPlot, ?FastPlot); + testCase.verifyClass(sdp.MainPlot, ?FastSense); + testCase.verifyClass(sdp.NavigatorPlot, ?FastSense); delete(sdp); end @@ -285,30 +285,30 @@ function testEventPatchUserdataFields(testCase) delete(sdp); end - %% FastPlotGrid tilePanel integration + %% FastSenseGrid tilePanel integration function testTilePanelReturnsUipanel(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); hp = fig.tilePanel(1); testCase.verifyTrue(isa(hp, 'matlab.ui.container.Panel')); delete(fig); end function testTilePanelConflictWithTile(testCase) - fig = FastPlotGrid(2, 1); - fig.tile(1); % Occupy tile 1 as FastPlot - testCase.verifyError(@() fig.tilePanel(1), 'FastPlotGrid:tileConflict'); + fig = FastSenseGrid(2, 1); + fig.tile(1); % Occupy tile 1 as FastSense + testCase.verifyError(@() fig.tilePanel(1), 'FastSenseGrid:tileConflict'); delete(fig); end - %% Embedded in FastPlotGrid + %% Embedded in FastSenseGrid function testEmbeddedInFigureTile(testCase) s = testCase.TestData.sensor; - fig = FastPlotGrid(1, 1); + fig = FastSenseGrid(1, 1); hp = fig.tilePanel(1); sdp = SensorDetailPlot(s, 'Parent', hp); sdp.render(); testCase.verifyTrue(sdp.IsRendered); - testCase.verifyClass(sdp.MainPlot, ?FastPlot); + testCase.verifyClass(sdp.MainPlot, ?FastSense); delete(sdp); delete(fig); end diff --git a/tests/suite/TestSensorTodisk.m b/tests/suite/TestSensorTodisk.m index 8d74a238..1d9f085c 100644 --- a/tests/suite/TestSensorTodisk.m +++ b/tests/suite/TestSensorTodisk.m @@ -80,7 +80,7 @@ function testAddSensorWithDiskBacked(testCase) testCase.addTeardown(@() s2.DataStore.cleanup()); s2.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s2, 'ShowThresholds', true); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -94,7 +94,7 @@ function testAddSensorNoThresholds(testCase) s3.Y = rand(1, 10000); s3.toDisk(); testCase.addTeardown(@() s3.DataStore.cleanup()); - fp2 = FastPlot(); + fp2 = FastSense(); fp2.addSensor(s3); fp2.render(); testCase.addTeardown(@close, fp2.hFigure); diff --git a/tests/suite/TestTheme.m b/tests/suite/TestTheme.m index c83863c1..3d5dc030 100644 --- a/tests/suite/TestTheme.m +++ b/tests/suite/TestTheme.m @@ -8,7 +8,7 @@ function addPaths(testCase) methods (Test) function testDefaultPreset(testCase) - t = FastPlotTheme('default'); + t = FastSenseTheme('default'); testCase.verifyTrue(isstruct(t), 'testDefaultPreset: must return struct'); testCase.verifyEqual(t.Background, [1 1 1], 'testDefaultPreset: Background'); testCase.verifyTrue(isfield(t, 'AxesColor'), 'testDefaultPreset: AxesColor field'); @@ -30,13 +30,13 @@ function testDefaultPreset(testCase) end function testNoArgsReturnsDefault(testCase) - t0 = FastPlotTheme(); - t1 = FastPlotTheme('default'); + t0 = FastSenseTheme(); + t1 = FastSenseTheme('default'); testCase.verifyEqual(t0, t1, 'testNoArgsReturnsDefault'); end function testMergeOverrides(testCase) - t = FastPlotTheme('default', 'FontSize', 14, 'LineWidth', 2.0); + t = FastSenseTheme('default', 'FontSize', 14, 'LineWidth', 2.0); testCase.verifyEqual(t.FontSize, 14, 'testMergeOverrides: FontSize'); testCase.verifyEqual(t.LineWidth, 2.0, 'testMergeOverrides: LineWidth'); testCase.verifyEqual(t.Background, [1 1 1], 'testMergeOverrides: Background unchanged'); @@ -45,7 +45,7 @@ function testMergeOverrides(testCase) function testInvalidPresetErrors(testCase) threw = false; try - FastPlotTheme('nonexistent'); + FastSenseTheme('nonexistent'); catch threw = true; end @@ -53,26 +53,26 @@ function testInvalidPresetErrors(testCase) end function testDarkPreset(testCase) - t = FastPlotTheme('dark'); + t = FastSenseTheme('dark'); testCase.verifyTrue(all(t.Background < [0.2 0.2 0.2]), 'testDarkPreset: Background should be dark'); testCase.verifyTrue(all(t.ForegroundColor > [0.7 0.7 0.7]), 'testDarkPreset: ForegroundColor should be light'); testCase.verifyEqual(size(t.LineColorOrder, 2), 3, 'testDarkPreset: LineColorOrder Nx3'); end function testLightPreset(testCase) - t = FastPlotTheme('light'); + t = FastSenseTheme('light'); testCase.verifyTrue(all(t.Background > [0.9 0.9 0.9]), 'testLightPreset: Background'); testCase.verifyEqual(size(t.LineColorOrder, 2), 3, 'testLightPreset: LineColorOrder Nx3'); end function testIndustrialPreset(testCase) - t = FastPlotTheme('industrial'); + t = FastSenseTheme('industrial'); testCase.verifyTrue(t.LineWidth >= 1.0, 'testIndustrialPreset: LineWidth'); testCase.verifyEqual(size(t.LineColorOrder, 2), 3, 'testIndustrialPreset: LineColorOrder Nx3'); end function testScientificPreset(testCase) - t = FastPlotTheme('scientific'); + t = FastSenseTheme('scientific'); testCase.verifyEqual(t.FontName, 'Times New Roman', 'testScientificPreset: FontName'); testCase.verifyEqual(t.GridAlpha, 0, 'testScientificPreset: no grid'); testCase.verifyTrue(t.LineWidth < 1.0, 'testScientificPreset: thin lines'); @@ -80,7 +80,7 @@ function testScientificPreset(testCase) end function testOceanPreset(testCase) - t = FastPlotTheme('ocean'); + t = FastSenseTheme('ocean'); testCase.verifyEqual(t.Background, [1 1 1], 'testOceanPreset: Background should be white'); testCase.verifyEqual(t.AxesColor, [1 1 1], 'testOceanPreset: AxesColor should be white'); testCase.verifyEqual(size(t.LineColorOrder, 2), 3, 'testOceanPreset: LineColorOrder Nx3'); @@ -89,20 +89,20 @@ function testOceanPreset(testCase) function testStructAsPreset(testCase) custom = struct('Background', [0 0 0], 'FontSize', 16); - t = FastPlotTheme(custom); + t = FastSenseTheme(custom); testCase.verifyEqual(t.Background, [0 0 0], 'testStructAsPreset: Background'); testCase.verifyEqual(t.FontSize, 16, 'testStructAsPreset: FontSize'); testCase.verifyTrue(isfield(t, 'GridColor'), 'testStructAsPreset: inherits defaults'); end function testPaletteResolution(testCase) - t = FastPlotTheme('default'); + t = FastSenseTheme('default'); testCase.verifyTrue(size(t.LineColorOrder, 1) >= 6, 'testPaletteResolution: at least 6 colors'); end function testCustomPaletteMatrix(testCase) customColors = [1 0 0; 0 1 0; 0 0 1]; - t = FastPlotTheme('default', 'LineColorOrder', customColors); + t = FastSenseTheme('default', 'LineColorOrder', customColors); testCase.verifyEqual(t.LineColorOrder, customColors, 'testCustomPaletteMatrix'); end end diff --git a/tests/suite/TestToolbar.m b/tests/suite/TestToolbar.m index 30352f1c..aa88e322 100644 --- a/tests/suite/TestToolbar.m +++ b/tests/suite/TestToolbar.m @@ -3,62 +3,62 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) - function testConstructorWithFastPlot(testCase) - fp = FastPlot(); + function testConstructorWithFastSense(testCase) + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); - testCase.verifyNotEmpty(tb.hToolbar, 'testConstructorWithFastPlot: hToolbar'); - testCase.verifyTrue(ishandle(tb.hToolbar), 'testConstructorWithFastPlot: ishandle'); + tb = FastSenseToolbar(fp); + testCase.verifyNotEmpty(tb.hToolbar, 'testConstructorWithFastSense: hToolbar'); + testCase.verifyTrue(ishandle(tb.hToolbar), 'testConstructorWithFastSense: ishandle'); end - function testConstructorWithFastPlotGrid(testCase) - fig = FastPlotGrid(1, 2); + function testConstructorWithFastSenseGrid(testCase) + fig = FastSenseGrid(1, 2); fp1 = fig.tile(1); fp1.addLine(1:100, rand(1,100)); fp2 = fig.tile(2); fp2.addLine(1:100, rand(1,100)); fig.renderAll(); testCase.addTeardown(@close, fig.hFigure); - tb = FastPlotToolbar(fig); + tb = FastSenseToolbar(fig); testCase.verifyNotEmpty(tb.hToolbar, 'testConstructorWithFPFigure: hToolbar'); end function testToolbarHasAllButtons(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); children = get(tb.hToolbar, 'Children'); testCase.verifyEqual(numel(children), 11, ... sprintf('testToolbarHasAllButtons: got %d', numel(children))); end function testIconsAre16x16x3(testCase) - icons = FastPlotToolbar.makeIcon('grid'); + icons = FastSenseToolbar.makeIcon('grid'); testCase.verifyEqual(size(icons), [16 16 3], 'testIconsAre16x16x3'); end function testAllIconNames(testCase) names = {'cursor', 'crosshair', 'grid', 'legend', 'autoscale', 'export', 'violations'}; for i = 1:numel(names) - icon = FastPlotToolbar.makeIcon(names{i}); + icon = FastSenseToolbar.makeIcon(names{i}); testCase.verifyEqual(size(icon), [16 16 3], ... sprintf('testAllIconNames: %s', names{i})); end end function testToggleGrid(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); gridBefore = get(fp.hAxes, 'XGrid'); tb.toggleGrid(); gridAfter = get(fp.hAxes, 'XGrid'); @@ -66,11 +66,11 @@ function testToggleGrid(testCase) end function testToggleLegend(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'TestLine'); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tb.toggleLegend(); hLeg = findobj(fp.hFigure, 'Type', 'axes', 'Tag', 'legend'); if isempty(hLeg) @@ -83,12 +83,12 @@ function testToggleLegend(testCase) end function testAutoscaleY(testCase) - fp = FastPlot(); + fp = FastSense(); y = [zeros(1,50), 10*ones(1,50)]; fp.addLine(1:100, y); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); % Zoom into first half (all zeros) set(fp.hAxes, 'XLim', [1 50]); drawnow; @@ -100,11 +100,11 @@ function testAutoscaleY(testCase) end function testExportPNG(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tmpFile = [tempname, '.png']; testCase.addTeardown(@() TestToolbar.deleteIfExists(tmpFile)); tb.exportPNG(tmpFile); @@ -112,11 +112,11 @@ function testExportPNG(testCase) end function testCrosshairMode(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); testCase.verifyEqual(tb.Mode, 'none', 'testCrosshairMode: initial mode'); tb.setCrosshair(true); testCase.verifyEqual(tb.Mode, 'crosshair', 'testCrosshairMode: on'); @@ -125,11 +125,11 @@ function testCrosshairMode(testCase) end function testCrosshairMutualExclusion(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tb.setCursor(true); testCase.verifyEqual(tb.Mode, 'cursor', 'testMutualExcl: cursor on'); tb.setCrosshair(true); @@ -138,11 +138,11 @@ function testCrosshairMutualExclusion(testCase) end function testCursorMode(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tb.setCursor(true); testCase.verifyEqual(tb.Mode, 'cursor', 'testCursorMode: on'); tb.setCursor(false); @@ -150,23 +150,23 @@ function testCursorMode(testCase) end function testSnapToNearest(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine([1 2 3 4 5], [10 20 30 40 50]); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); [sx, sy, ~] = tb.snapToNearest(fp, 2.8, 25); testCase.verifyEqual(sx, 3, sprintf('testSnapToNearest: x should be 3, got %g', sx)); testCase.verifyEqual(sy, 30, sprintf('testSnapToNearest: y should be 30, got %g', sy)); end function testViolationsToggle(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, [ones(1,50)*2, ones(1,50)*8]); fp.addThreshold(5, 'Direction', 'upper', 'ShowViolations', true); fp.render(); testCase.addTeardown(@close, fp.hFigure); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); % Violations should be visible initially testCase.verifyTrue(fp.ViolationsVisible, 'testViolationsToggle: default true'); hM = fp.Thresholds(1).hMarkers; diff --git a/tests/suite/TestViolationCullMex.m b/tests/suite/TestViolationCullMex.m index fcae3864..f2fd0649 100644 --- a/tests/suite/TestViolationCullMex.m +++ b/tests/suite/TestViolationCullMex.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end diff --git a/tests/suite/TestViolationsMexParity.m b/tests/suite/TestViolationsMexParity.m index b4be0f5a..738fffda 100644 --- a/tests/suite/TestViolationsMexParity.m +++ b/tests/suite/TestViolationsMexParity.m @@ -3,7 +3,7 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastPlot', 'private')); + addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'libs', 'FastSense', 'private')); end end diff --git a/tests/suite/TestWebBridgeE2E.m b/tests/suite/TestWebBridgeE2E.m index 79c34d24..077b9f96 100644 --- a/tests/suite/TestWebBridgeE2E.m +++ b/tests/suite/TestWebBridgeE2E.m @@ -7,12 +7,12 @@ function addPaths(testCase) end methods (Test) function testServeAndFetchData(testCase) - [status, ~] = system('python -c "import fastplot_bridge"'); - testCase.assumeTrue(status == 0, 'fastplot-bridge Python package not installed'); + [status, ~] = system('python -c "import fastsense_bridge"'); + testCase.assumeTrue(status == 0, 'fastsense-bridge Python package not installed'); x = linspace(0, 100, 10000); y = sin(x); engine = DashboardEngine('E2E Test'); - engine.addWidget('fastplot', 'Title', 'Sine Wave', 'XData', x, 'YData', y, 'Position', [1 1 6 3]); + engine.addWidget('fastsense', 'Title', 'Sine Wave', 'XData', x, 'YData', y, 'Position', [1 1 6 3]); bridge = WebBridge(engine); testCase.addTeardown(@() bridge.stop()); bridge.serve(); diff --git a/tests/suite/TestZoomPan.m b/tests/suite/TestZoomPan.m index 9bd5ce45..36a2243d 100644 --- a/tests/suite/TestZoomPan.m +++ b/tests/suite/TestZoomPan.m @@ -3,13 +3,13 @@ function addPaths(testCase) addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..')); setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); end end methods (Test) function testZoomUpdatesPlottedData(testCase) - fp = FastPlot(); + fp = FastSense(); n = 100000; x = linspace(0, 100, n); y = sin(x); @@ -29,7 +29,7 @@ function testZoomUpdatesPlottedData(testCase) end function testLazySkipsRedundantUpdate(testCase) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:1000, rand(1,1000)); fp.render(); testCase.addTeardown(@close, fp.hFigure); @@ -43,7 +43,7 @@ function testLazySkipsRedundantUpdate(testCase) end function testViolationsUpdateOnZoom(testCase) - fp = FastPlot(); + fp = FastSense(); y = [zeros(1,500), ones(1,500)*10, zeros(1,500)]; x = 1:1500; fp.addLine(x, y); diff --git a/tests/test_NavigatorOverlay.m b/tests/test_NavigatorOverlay.m index 0dffb42b..48d1cd8e 100644 --- a/tests/test_NavigatorOverlay.m +++ b/tests/test_NavigatorOverlay.m @@ -8,7 +8,7 @@ end function setup(testCase) - addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastPlot')); + addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastSense')); addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'SensorThreshold')); testCase.TestData.hFig = figure('Visible', 'off'); testCase.TestData.hAxes = axes('Parent', testCase.TestData.hFig); diff --git a/tests/test_SensorDetailPlot.m b/tests/test_SensorDetailPlot.m index d62f0a5d..39b942e3 100644 --- a/tests/test_SensorDetailPlot.m +++ b/tests/test_SensorDetailPlot.m @@ -8,7 +8,7 @@ end function setup(testCase) - addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastPlot')); + addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'FastSense')); addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'SensorThreshold')); addpath(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'libs', 'EventDetection')); @@ -52,12 +52,12 @@ function test_constructor_custom_options(testCase) delete(sdp); end -%% Render creates two FastPlot instances +%% Render creates two FastSense instances function test_render_creates_main_and_navigator(testCase) sdp = SensorDetailPlot(testCase.TestData.sensor); sdp.render(); - verifyClass(testCase, sdp.MainPlot, ?FastPlot); - verifyClass(testCase, sdp.NavigatorPlot, ?FastPlot); + verifyClass(testCase, sdp.MainPlot, ?FastSense); + verifyClass(testCase, sdp.NavigatorPlot, ?FastSense); delete(sdp); end @@ -302,30 +302,30 @@ function test_event_patch_userdata_fields(testCase) delete(sdp); end -%% FastPlotGrid tilePanel integration +%% FastSenseGrid tilePanel integration function test_tilePanel_returns_uipanel(testCase) - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); hp = fig.tilePanel(1); verifyTrue(testCase, isa(hp, 'matlab.ui.container.Panel')); delete(fig); end function test_tilePanel_conflict_with_tile(testCase) - fig = FastPlotGrid(2, 1); - fig.tile(1); % Occupy tile 1 as FastPlot - verifyError(testCase, @() fig.tilePanel(1), 'FastPlotGrid:tileConflict'); + fig = FastSenseGrid(2, 1); + fig.tile(1); % Occupy tile 1 as FastSense + verifyError(testCase, @() fig.tilePanel(1), 'FastSenseGrid:tileConflict'); delete(fig); end -%% Embedded in FastPlotGrid +%% Embedded in FastSenseGrid function test_embedded_in_figure_tile(testCase) s = testCase.TestData.sensor; - fig = FastPlotGrid(1, 1); + fig = FastSenseGrid(1, 1); hp = fig.tilePanel(1); sdp = SensorDetailPlot(s, 'Parent', hp); sdp.render(); verifyTrue(testCase, sdp.IsRendered); - verifyClass(testCase, sdp.MainPlot, ?FastPlot); + verifyClass(testCase, sdp.MainPlot, ?FastSense); delete(sdp); delete(fig); end diff --git a/tests/test_add_band.m b/tests/test_add_band.m index fa78e72f..54ad4ac5 100644 --- a/tests/test_add_band.m +++ b/tests/test_add_band.m @@ -1,11 +1,11 @@ function test_add_band() -%TEST_ADD_BAND Tests for FastPlot.addBand method. +%TEST_ADD_BAND Tests for FastSense.addBand method. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testAddBand - fp = FastPlot(); + fp = FastSense(); fp.addBand(-1, 1, 'FaceColor', [1 0.9 0.9], 'FaceAlpha', 0.3, 'Label', 'Safe'); assert(numel(fp.Bands) == 1, 'testAddBand: count'); assert(fp.Bands(1).YLow == -1, 'testAddBand: YLow'); @@ -13,24 +13,24 @@ function test_add_band() assert(strcmp(fp.Bands(1).Label, 'Safe'), 'testAddBand: Label'); % testAddMultipleBands - fp = FastPlot(); + fp = FastSense(); fp.addBand(-2, -1); fp.addBand(1, 2); assert(numel(fp.Bands) == 2, 'testAddMultipleBands'); % testBandRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addBand(0.2, 0.8, 'FaceColor', [0 1 0], 'FaceAlpha', 0.2); fp.render(); assert(~isempty(fp.Bands(1).hPatch), 'testBandRendered: hPatch created'); assert(ishandle(fp.Bands(1).hPatch), 'testBandRendered: hPatch valid'); ud = get(fp.Bands(1).hPatch, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'band'), 'testBandRendered: UserData type'); + assert(strcmp(ud.FastSense.Type, 'band'), 'testBandRendered: UserData type'); close(fp.hFigure); % testBandRejectsAfterRender - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); threw = false; @@ -43,7 +43,7 @@ function test_add_band() close(fp.hFigure); % testBandDefaults - fp = FastPlot(); + fp = FastSense(); fp.addBand(0, 1); assert(fp.Bands(1).FaceAlpha > 0, 'testBandDefaults: FaceAlpha'); assert(numel(fp.Bands(1).FaceColor) == 3, 'testBandDefaults: FaceColor'); diff --git a/tests/test_add_line.m b/tests/test_add_line.m index 2d795bae..00ce3277 100644 --- a/tests/test_add_line.m +++ b/tests/test_add_line.m @@ -1,10 +1,10 @@ function test_add_line() -%TEST_ADD_LINE Tests for FastPlot.addLine method. +%TEST_ADD_LINE Tests for FastSense.addLine method. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); % testAddSingleLine - fp = FastPlot(); + fp = FastSense(); x = 1:100; y = rand(1, 100); fp.addLine(x, y); @@ -13,30 +13,30 @@ function test_add_line() assert(isequal(fp.Lines(1).Y, y), 'testAddSingleLine: Y mismatch'); % testAddMultipleLines - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.addLine(1:20, rand(1,20)); fp.addLine(1:5, rand(1,5)); assert(numel(fp.Lines) == 3, 'testAddMultipleLines: expected 3 lines'); % testLineOptions - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'Color', 'r', 'DisplayName', 'S1'); assert(strcmp(fp.Lines(1).Options.Color, 'r'), 'testLineOptions: Color'); assert(strcmp(fp.Lines(1).Options.DisplayName, 'S1'), 'testLineOptions: DisplayName'); % testDownsampleMethodDefault - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); assert(strcmp(fp.Lines(1).DownsampleMethod, 'minmax'), 'testDownsampleMethodDefault'); % testDownsampleMethodOverride - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'DownsampleMethod', 'lttb'); assert(strcmp(fp.Lines(1).DownsampleMethod, 'lttb'), 'testDownsampleMethodOverride'); % testRejectsNonMonotonicX - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addLine([1 3 2 4], rand(1,4)); @@ -47,7 +47,7 @@ function test_add_line() assert(threw, 'testRejectsNonMonotonicX: should have thrown'); % testRejectsMismatchedLengths - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addLine(1:10, rand(1,5)); @@ -58,7 +58,7 @@ function test_add_line() assert(threw, 'testRejectsMismatchedLengths: should have thrown'); % testColumnVectorsAccepted - fp = FastPlot(); + fp = FastSense(); fp.addLine((1:10)', rand(10,1)); assert(numel(fp.Lines(1).X) == 10, 'testColumnVectors: numel'); assert(isrow(fp.Lines(1).X), 'testColumnVectors: must be row'); diff --git a/tests/test_add_marker.m b/tests/test_add_marker.m index 9a7d0304..03f0abc7 100644 --- a/tests/test_add_marker.m +++ b/tests/test_add_marker.m @@ -1,35 +1,35 @@ function test_add_marker() -%TEST_ADD_MARKER Tests for FastPlot.addMarker method. +%TEST_ADD_MARKER Tests for FastSense.addMarker method. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testAddMarker - fp = FastPlot(); + fp = FastSense(); fp.addMarker([10 20 30], [1 2 3], 'Marker', 'v', 'Color', [1 0 0], 'Label', 'Faults'); assert(numel(fp.Markers) == 1, 'testAddMarker: count'); assert(isequal(fp.Markers(1).X, [10 20 30]), 'testAddMarker: X'); assert(strcmp(fp.Markers(1).Label, 'Faults'), 'testAddMarker: Label'); % testMarkerRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addMarker([10 50], [0.5 0.8], 'Marker', 'd', 'MarkerSize', 10); fp.render(); assert(~isempty(fp.Markers(1).hLine), 'testMarkerRendered: hLine'); assert(ishandle(fp.Markers(1).hLine), 'testMarkerRendered: valid handle'); ud = get(fp.Markers(1).hLine, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'marker'), 'testMarkerRendered: UserData type'); + assert(strcmp(ud.FastSense.Type, 'marker'), 'testMarkerRendered: UserData type'); close(fp.hFigure); % testMarkerDefaults - fp = FastPlot(); + fp = FastSense(); fp.addMarker([5], [1]); assert(~isempty(fp.Markers(1).Marker), 'testMarkerDefaults: Marker shape'); assert(fp.Markers(1).MarkerSize > 0, 'testMarkerDefaults: MarkerSize'); % testMarkerRejectsAfterRender - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); threw = false; diff --git a/tests/test_add_sensor.m b/tests/test_add_sensor.m index deb5ab9a..4fbd042a 100644 --- a/tests/test_add_sensor.m +++ b/tests/test_add_sensor.m @@ -1,5 +1,5 @@ function test_add_sensor() -%TEST_ADD_SENSOR Tests for FastPlot.addSensor() integration. +%TEST_ADD_SENSOR Tests for FastSense.addSensor() integration. add_sensor_path(); @@ -9,7 +9,7 @@ function test_add_sensor() s.Y = rand(1, 100) * 10; s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s); assert(numel(fp.Lines) == 1, 'testBasic: one line added'); assert(strcmp(fp.Lines(1).Options.DisplayName, 'Chamber Pressure'), 'testBasic: display name'); @@ -25,7 +25,7 @@ function test_add_sensor() s.addThresholdRule(struct('machine', 1), 10, 'Direction', 'upper', 'Label', 'HH'); s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s, 'ShowThresholds', true); assert(numel(fp.Lines) == 1, 'testWithThresholds: only data line'); assert(numel(fp.Thresholds) >= 1, 'testWithThresholds: threshold(s) added'); @@ -39,7 +39,7 @@ function test_add_sensor() s.addThresholdRule(struct(), 5, 'Direction', 'upper'); s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s, 'ShowThresholds', false); assert(numel(fp.Lines) == 1, 'testNoThresholds: only data line'); assert(numel(fp.Thresholds) == 0, 'testNoThresholds: no thresholds'); @@ -50,7 +50,7 @@ function test_add_sensor() s.Y = rand(1, 10); s.resolve(); - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s); assert(strcmp(fp.Lines(1).Options.DisplayName, 'flow_rate'), 'testFallbackName: uses Key'); diff --git a/tests/test_add_shaded.m b/tests/test_add_shaded.m index 3c1add5d..7e1211d0 100644 --- a/tests/test_add_shaded.m +++ b/tests/test_add_shaded.m @@ -1,14 +1,14 @@ function test_add_shaded() -%TEST_ADD_SHADED Tests for FastPlot.addShaded method. +%TEST_ADD_SHADED Tests for FastSense.addShaded method. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testAddShaded x = 1:100; y1 = ones(1,100) * 2; y2 = ones(1,100) * -2; - fp = FastPlot(); + fp = FastSense(); fp.addShaded(x, y1, y2, 'FaceColor', [0 0 1], 'FaceAlpha', 0.2); assert(numel(fp.Shadings) == 1, 'testAddShaded: count'); assert(isequal(fp.Shadings(1).X, x), 'testAddShaded: X'); @@ -16,18 +16,18 @@ function test_add_shaded() assert(isequal(fp.Shadings(1).Y2, y2), 'testAddShaded: Y2'); % testShadedRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addShaded(1:100, ones(1,100), zeros(1,100), 'FaceColor', [1 0 0]); fp.render(); assert(~isempty(fp.Shadings(1).hPatch), 'testShadedRendered: hPatch'); assert(ishandle(fp.Shadings(1).hPatch), 'testShadedRendered: valid'); ud = get(fp.Shadings(1).hPatch, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'shaded'), 'testShadedRendered: type'); + assert(strcmp(ud.FastSense.Type, 'shaded'), 'testShadedRendered: type'); close(fp.hFigure); % testShadedValidation - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addShaded(1:10, 1:10, 1:5); % mismatched lengths @@ -37,7 +37,7 @@ function test_add_shaded() assert(threw, 'testShadedValidation: length mismatch'); % testShadedMonotonicX - fp = FastPlot(); + fp = FastSense(); threw = false; try fp.addShaded([3 1 2], [1 1 1], [0 0 0]); @@ -47,7 +47,7 @@ function test_add_shaded() assert(threw, 'testShadedMonotonicX'); % testShadedRejectsAfterRender - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); threw = false; @@ -60,14 +60,14 @@ function test_add_shaded() close(fp.hFigure); % testShadedColumnVectors - fp = FastPlot(); + fp = FastSense(); fp.addShaded((1:10)', (1:10)', zeros(10,1)); assert(isrow(fp.Shadings(1).X), 'testShadedColumnVectors: X row'); assert(isrow(fp.Shadings(1).Y1), 'testShadedColumnVectors: Y1 row'); assert(isrow(fp.Shadings(1).Y2), 'testShadedColumnVectors: Y2 row'); % testAddFill - fp = FastPlot(); + fp = FastSense(); x = 1:50; y = rand(1,50); fp.addFill(x, y, 'FaceColor', [0 0.5 1], 'FaceAlpha', 0.2); @@ -75,18 +75,18 @@ function test_add_shaded() assert(all(fp.Shadings(1).Y2 == 0), 'testAddFill: baseline is 0'); % testAddFillCustomBaseline - fp = FastPlot(); + fp = FastSense(); fp.addFill(1:10, rand(1,10), 'Baseline', -1); assert(all(fp.Shadings(1).Y2 == -1), 'testAddFillCustomBaseline'); % testAddFillRendered - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addFill(1:100, rand(1,100), 'FaceColor', [0 1 0]); fp.render(); assert(ishandle(fp.Shadings(1).hPatch), 'testAddFillRendered: valid patch'); ud = get(fp.Shadings(1).hPatch, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'shaded'), 'testAddFillRendered: type is shaded'); + assert(strcmp(ud.FastSense.Type, 'shaded'), 'testAddFillRendered: type is shaded'); close(fp.hFigure); fprintf(' All 9 addShaded/addFill tests passed.\n'); diff --git a/tests/test_add_threshold.m b/tests/test_add_threshold.m index fb617eef..28100b4c 100644 --- a/tests/test_add_threshold.m +++ b/tests/test_add_threshold.m @@ -1,22 +1,22 @@ function test_add_threshold() -%TEST_ADD_THRESHOLD Tests for FastPlot.addThreshold method. +%TEST_ADD_THRESHOLD Tests for FastSense.addThreshold method. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); % testAddUpperThreshold - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(4.5, 'Direction', 'upper'); assert(numel(fp.Thresholds) == 1, 'testAddUpperThreshold: count'); assert(fp.Thresholds(1).Value == 4.5, 'testAddUpperThreshold: value'); assert(strcmp(fp.Thresholds(1).Direction, 'upper'), 'testAddUpperThreshold: direction'); % testAddLowerThreshold - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(-2.0, 'Direction', 'lower'); assert(strcmp(fp.Thresholds(1).Direction, 'lower'), 'testAddLowerThreshold'); % testDefaults - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(5.0); assert(strcmp(fp.Thresholds(1).Direction, 'upper'), 'testDefaults: direction'); assert(fp.Thresholds(1).ShowViolations == false, 'testDefaults: ShowViolations'); @@ -24,7 +24,7 @@ function test_add_threshold() assert(strcmp(fp.Thresholds(1).Label, ''), 'testDefaults: Label'); % testCustomOptions - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(3.0, 'Direction', 'lower', ... 'ShowViolations', true, 'Color', [1 0 0], ... 'LineStyle', ':', 'Label', 'LowerBound'); @@ -35,14 +35,14 @@ function test_add_threshold() assert(strcmp(t.Label, 'LowerBound'), 'testCustomOptions: Label'); % testMultipleThresholds - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(1.0); fp.addThreshold(2.0); fp.addThreshold(3.0); assert(numel(fp.Thresholds) == 3, 'testMultipleThresholds'); % testTimeVaryingThreshold - fp = FastPlot(); + fp = FastSense(); thX = [0 10 20 30]; thY = [5.0 5.0 7.0 7.0]; fp.addThreshold(thX, thY, 'Direction', 'upper', 'ShowViolations', true, 'Label', 'StepTh'); @@ -54,7 +54,7 @@ function test_add_threshold() assert(fp.Thresholds(1).ShowViolations == true, 'testTimeVarying: ShowViolations'); % testMixedThresholds — scalar and time-varying coexist - fp = FastPlot(); + fp = FastSense(); fp.addThreshold(4.5); fp.addThreshold([0 10], [3.0 5.0], 'Direction', 'lower'); assert(numel(fp.Thresholds) == 2, 'testMixed: count'); diff --git a/tests/test_binary_search.m b/tests/test_binary_search.m index 13b04e72..c9666219 100644 --- a/tests/test_binary_search.m +++ b/tests/test_binary_search.m @@ -1,9 +1,9 @@ function test_binary_search() %TEST_BINARY_SEARCH Tests for binary_search private function. - % We need access to the private function via the FastPlot directory + % We need access to the private function via the FastSense directory addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); x = [1 3 5 7 9]; diff --git a/tests/test_compute_violations.m b/tests/test_compute_violations.m index efcf7c75..b661424c 100644 --- a/tests/test_compute_violations.m +++ b/tests/test_compute_violations.m @@ -2,7 +2,7 @@ function test_compute_violations() %TEST_COMPUTE_VIOLATIONS Tests for compute_violations private function. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testUpperViolation x = 1:10; diff --git a/tests/test_compute_violations_dynamic.m b/tests/test_compute_violations_dynamic.m index 8bc99aed..f4057a50 100644 --- a/tests/test_compute_violations_dynamic.m +++ b/tests/test_compute_violations_dynamic.m @@ -2,7 +2,7 @@ function test_compute_violations_dynamic() %TEST_COMPUTE_VIOLATIONS_DYNAMIC Tests for time-varying threshold violations. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testUpperStepFunction: threshold steps from 5 to 8 at x=10 thX = [0 10 20]; diff --git a/tests/test_dashboard_builder_interaction.m b/tests/test_dashboard_builder_interaction.m index 28ec17e6..6e8a020a 100644 --- a/tests/test_dashboard_builder_interaction.m +++ b/tests/test_dashboard_builder_interaction.m @@ -432,7 +432,7 @@ function testPaletteButtonsAddWidgets() sprintf('testPaletteButtonsAddWidgets: only %d buttons', numel(btns))); initialCount = numel(engine.Widgets); - % Use the KPI button (2nd from last) to avoid FastPlot requiring a line + % Use the KPI button (2nd from last) to avoid FastSense requiring a line cb = get(btns(end-1), 'Callback'); cb(btns(end-1), []); diff --git a/tests/test_datastore.m b/tests/test_datastore.m index 612a5d0a..7a8cf6c0 100644 --- a/tests/test_datastore.m +++ b/tests/test_datastore.m @@ -1,6 +1,6 @@ function test_datastore() -%TEST_DATASTORE Unit tests for FastPlotDataStore class. -% Tests the SQLite/binary-backed data storage used by FastPlot to handle +%TEST_DATASTORE Unit tests for FastSenseDataStore class. +% Tests the SQLite/binary-backed data storage used by FastSense to handle % large datasets without running out of memory. Covers creation, range % queries, slice reads, edge cases, and cleanup. @@ -11,7 +11,7 @@ function test_datastore() % testCreateStore: basic construction stores metadata correctly x = linspace(0, 100, 10000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); assert(ds.NumPoints == 10000, 'testCreateStore: NumPoints'); assert(ds.XMin == x(1), 'testCreateStore: XMin'); assert(ds.XMax == x(end), 'testCreateStore: XMax'); @@ -20,18 +20,18 @@ function test_datastore() % testCreateStoreWithNaN: detects NaN in Y data y2 = y; y2(500) = NaN; - ds = FastPlotDataStore(x, y2); + ds = FastSenseDataStore(x, y2); assert(ds.HasNaN == true, 'testCreateStoreWithNaN: HasNaN should be true'); ds.cleanup(); % testEmptyConstruction: empty construction returns valid zero-state - ds = FastPlotDataStore(); + ds = FastSenseDataStore(); assert(ds.NumPoints == 0, 'testEmptyConstruction: NumPoints'); assert(isnan(ds.XMin), 'testEmptyConstruction: XMin should be NaN'); ds.cleanup(); % testColumnVectorInput: accepts column vectors - ds = FastPlotDataStore((1:100)', rand(100,1)); + ds = FastSenseDataStore((1:100)', rand(100,1)); assert(ds.NumPoints == 100, 'testColumnVectorInput: NumPoints'); ds.cleanup(); @@ -40,7 +40,7 @@ function test_datastore() % testGetRangeMiddle: query a middle range returns correct data x = linspace(0, 100, 50000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr, yr] = ds.getRange(20, 40); assert(~isempty(xr), 'testGetRangeMiddle: should return data'); % All interior points must be within range (edges may have 1-pt padding) @@ -54,7 +54,7 @@ function test_datastore() % testGetRangeFullSpan: range covering entire dataset returns all points x = 1:1000; y = rand(1, 1000); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr, yr] = ds.getRange(1, 1000); assert(numel(xr) == 1000, 'testGetRangeFullSpan: should get all points'); assert(isequal(xr, x), 'testGetRangeFullSpan: X data must match'); @@ -66,7 +66,7 @@ function test_datastore() n = 200000; x = linspace(0, 1000, n); y = cumsum(randn(1, n)); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr, yr] = ds.getRange(500, 501); % Should get a reasonable number of points (not all 200K) assert(numel(xr) > 10, 'testGetRangeSmall: too few points'); @@ -79,7 +79,7 @@ function test_datastore() % testGetRangeOutside: range outside data returns empty or edge points x = 1:100; y = rand(1, 100); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr, ~] = ds.getRange(200, 300); % Should either be empty or contain only the last data point assert(numel(xr) <= 1, 'testGetRangeOutside: should be empty or single edge'); @@ -90,7 +90,7 @@ function test_datastore() % testReadSliceExact: reading a specific index range returns correct data x = 1:5000; y = (1:5000) * 2; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xs, ys] = ds.readSlice(100, 200); assert(numel(xs) == 101, 'testReadSliceExact: count'); assert(xs(1) == 100, 'testReadSliceExact: first X'); @@ -102,7 +102,7 @@ function test_datastore() % testReadSliceFullRange: reading entire range matches original data x = linspace(0, 10, 1000); y = cos(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xs, ys] = ds.readSlice(1, 1000); tol = 1e-12; assert(max(abs(xs - x)) < tol, 'testReadSliceFullRange: X mismatch'); @@ -110,7 +110,7 @@ function test_datastore() ds.cleanup(); % testReadSliceClamped: out-of-bounds indices are clamped - ds = FastPlotDataStore(1:100, rand(1, 100)); + ds = FastSenseDataStore(1:100, rand(1, 100)); [xs, ~] = ds.readSlice(-5, 200); assert(numel(xs) == 100, 'testReadSliceClamped: should clamp to full range'); assert(xs(1) == 1, 'testReadSliceClamped: start clamped'); @@ -124,7 +124,7 @@ function test_datastore() n = 150000; x = sort(rand(1, n)) * 1000; y = randn(1, n); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xAll, yAll] = ds.readSlice(1, n); tol = 1e-12; assert(max(abs(xAll - x)) < tol, 'testDataIntegrity: X drift'); @@ -135,7 +135,7 @@ function test_datastore() x = 1:100; y = rand(1, 100); y([10, 50, 90]) = NaN; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [~, yr] = ds.readSlice(1, 100); assert(isnan(yr(10)), 'testDataIntegrityNaN: NaN at 10'); assert(isnan(yr(50)), 'testDataIntegrityNaN: NaN at 50'); @@ -145,7 +145,7 @@ function test_datastore() % --- Range query returns row vectors --- % testOutputShape: getRange and readSlice return row vectors - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); [xr, yr] = ds.getRange(100, 200); assert(isrow(xr), 'testOutputShape: getRange X must be row'); assert(isrow(yr), 'testOutputShape: getRange Y must be row'); @@ -157,7 +157,7 @@ function test_datastore() % --- Cleanup --- % testCleanupDeletesFile: cleanup removes temp files from disk - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); % Store the path(s) before cleanup hasDb = ~isempty(ds.DbPath) && exist(ds.DbPath, 'file'); hasBin = ~isempty(ds.BinPath) && exist(ds.BinPath, 'file'); @@ -171,7 +171,7 @@ function test_datastore() assert(~exist(fpath, 'file'), 'testCleanupDeletesFile: file should be gone'); % testDestructorCleanup: deleting the object cleans up temp files - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); if ~isempty(ds.DbPath) && exist(ds.DbPath, 'file') fpath = ds.DbPath; else @@ -186,7 +186,7 @@ function test_datastore() n = 500000; x = linspace(0, 10000, n); y = sin(x / 100) + randn(1, n) * 0.1; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); assert(ds.NumPoints == n, 'testLargeDataset: NumPoints'); % Query a narrow range — should be fast and return correct subset diff --git a/tests/test_datastore_edge_cases.m b/tests/test_datastore_edge_cases.m index f315e0d2..44899706 100644 --- a/tests/test_datastore_edge_cases.m +++ b/tests/test_datastore_edge_cases.m @@ -1,5 +1,5 @@ function test_datastore_edge_cases() -%TEST_DATASTORE_EDGE_CASES Edge-case and stress tests for FastPlotDataStore. +%TEST_DATASTORE_EDGE_CASES Edge-case and stress tests for FastSenseDataStore. % Covers: boundary conditions, special values (Inf, NaN), single-point % datasets, repeated X values, multi-chunk queries, and binary fallback. @@ -8,7 +8,7 @@ function test_datastore_edge_cases() fprintf(' --- DataStore edge cases ---\n'); % 1. Single-point dataset - ds = FastPlotDataStore(5, 10); + ds = FastSenseDataStore(5, 10); assert(ds.NumPoints == 1, 'singlePoint: NumPoints'); assert(ds.XMin == 5, 'singlePoint: XMin'); assert(ds.XMax == 5, 'singlePoint: XMax'); @@ -21,7 +21,7 @@ function test_datastore_edge_cases() fprintf(' single-point dataset: PASS\n'); % 2. Two-point dataset - ds = FastPlotDataStore([1, 100], [10, 20]); + ds = FastSenseDataStore([1, 100], [10, 20]); [xr, yr] = ds.getRange(1, 100); assert(numel(xr) == 2, 'twoPoint: getRange count'); assert(xr(1) == 1 && xr(2) == 100, 'twoPoint: getRange X'); @@ -32,7 +32,7 @@ function test_datastore_edge_cases() x = 1:100; y = rand(1, 100); y(25) = Inf; y(75) = -Inf; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [~, yr] = ds.readSlice(1, 100); assert(isinf(yr(25)) && yr(25) > 0, 'infValues: +Inf preserved'); assert(isinf(yr(75)) && yr(75) < 0, 'infValues: -Inf preserved'); @@ -42,7 +42,7 @@ function test_datastore_edge_cases() % 4. All-NaN Y data x = 1:50; y = nan(1, 50); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); assert(ds.HasNaN == true, 'allNaN: HasNaN'); [~, yr] = ds.readSlice(1, 50); assert(all(isnan(yr)), 'allNaN: all values NaN'); @@ -52,7 +52,7 @@ function test_datastore_edge_cases() % 5. Constant X (degenerate — all same timestamp) x = ones(1, 100) * 42; y = rand(1, 100); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); assert(ds.XMin == 42 && ds.XMax == 42, 'constantX: XMin==XMax'); [xr, yr] = ds.getRange(41, 43); assert(numel(xr) == 100, 'constantX: getRange returns all'); @@ -62,14 +62,14 @@ function test_datastore_edge_cases() % 6. Repeated X values (duplicates) x = sort(repmat(1:50, 1, 3)); % [1 1 1 2 2 2 ... 50 50 50] y = rand(1, 150); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr, ~] = ds.getRange(10, 20); assert(all(xr >= 9 & xr <= 21), 'duplicateX: range respected'); ds.cleanup(); fprintf(' repeated X values: PASS\n'); % 7. getRange with inverted range (xMin > xMax) - ds = FastPlotDataStore(1:1000, rand(1, 1000)); + ds = FastSenseDataStore(1:1000, rand(1, 1000)); [xr, ~] = ds.getRange(500, 100); assert(isempty(xr), 'invertedRange: should return empty'); ds.cleanup(); @@ -78,7 +78,7 @@ function test_datastore_edge_cases() % 8. getRange at exact data boundaries x = linspace(0, 100, 10000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr, ~] = ds.getRange(0, 0); % exact start assert(numel(xr) >= 1, 'exactBoundary: at start'); [xr, ~] = ds.getRange(100, 100); % exact end @@ -87,7 +87,7 @@ function test_datastore_edge_cases() fprintf(' exact boundary queries: PASS\n'); % 9. readSlice with startIdx == endIdx (single point) - ds = FastPlotDataStore(1:1000, (1:1000)*3); + ds = FastSenseDataStore(1:1000, (1:1000)*3); [xs, ys] = ds.readSlice(500, 500); assert(numel(xs) == 1, 'singleSlice: count'); assert(xs == 500 && ys == 1500, 'singleSlice: values'); @@ -98,7 +98,7 @@ function test_datastore_edge_cases() n = 5000; x = 1:n; y = x * 2; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xs, ys] = ds.readSlice(n-2, n); assert(numel(xs) == 3, 'endSlice: count'); assert(xs(end) == n, 'endSlice: last X'); @@ -107,7 +107,7 @@ function test_datastore_edge_cases() fprintf(' end-of-data slice: PASS\n'); % 11. readSlice at very start of dataset - ds = FastPlotDataStore(1:5000, (1:5000)*2); + ds = FastSenseDataStore(1:5000, (1:5000)*2); [xs, ys] = ds.readSlice(1, 3); assert(numel(xs) == 3, 'startSlice: count'); assert(xs(1) == 1 && ys(1) == 2, 'startSlice: first values'); @@ -117,7 +117,7 @@ function test_datastore_edge_cases() % 12. Multiple getRange calls on same DataStore (caching/state) x = linspace(0, 100, 50000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr1, ~] = ds.getRange(10, 20); [xr2, ~] = ds.getRange(50, 60); [xr3, ~] = ds.getRange(10, 20); % repeat first query @@ -129,7 +129,7 @@ function test_datastore_edge_cases() % 13. Large number of small getRange calls (stress) x = linspace(0, 1000, 100000); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); tic; for i = 1:100 xStart = rand * 900; @@ -141,13 +141,13 @@ function test_datastore_edge_cases() fprintf(' 100 random range queries: PASS (%.2fs)\n', elapsed); % 14. cleanup is idempotent (calling twice doesn't error) - ds = FastPlotDataStore(1:100, rand(1, 100)); + ds = FastSenseDataStore(1:100, rand(1, 100)); ds.cleanup(); ds.cleanup(); % should not throw fprintf(' idempotent cleanup: PASS\n'); % 15. Operations after cleanup — storage file is gone - ds = FastPlotDataStore(1:100, rand(1, 100)); + ds = FastSenseDataStore(1:100, rand(1, 100)); if ~isempty(ds.DbPath); fpath = ds.DbPath; else; fpath = ds.BinPath; end ds.cleanup(); assert(~exist(fpath, 'file'), 'afterCleanup: file should be gone'); @@ -156,7 +156,7 @@ function test_datastore_edge_cases() % 16. Very large Y values (1e300) x = 1:100; y = ones(1, 100) * 1e300; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [~, yr] = ds.readSlice(1, 100); assert(all(yr == 1e300), 'largeValues: preserved'); ds.cleanup(); @@ -164,7 +164,7 @@ function test_datastore_edge_cases() % 17. Very small Y values (1e-300) y = ones(1, 100) * 1e-300; - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [~, yr] = ds.readSlice(1, 100); assert(all(abs(yr - 1e-300) < 1e-312), 'smallValues: preserved'); ds.cleanup(); @@ -173,7 +173,7 @@ function test_datastore_edge_cases() % 18. Negative X values (descending not required but negative ok) x = linspace(-100, -1, 500); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); assert(ds.XMin == -100 && ds.XMax == -1, 'negativeX: range'); [xr, ~] = ds.getRange(-60, -40); assert(all(xr >= -61 & xr <= -39), 'negativeX: range query'); @@ -185,7 +185,7 @@ function test_datastore_edge_cases() n = 200000; x = linspace(0, 1000, n); y = x .* 3; % simple linear for verification - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); % Query range that spans chunk boundaries [xr, yr] = ds.getRange(400, 600); % Verify Y = 3*X relationship holds across chunks @@ -198,7 +198,7 @@ function test_datastore_edge_cases() n = 100000; x = linspace(0, 1000, n); y = randn(1, n); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); [xr, ~] = ds.getRange(200, 800); assert(all(diff(xr) >= 0), 'monotonicity: X must be non-decreasing'); ds.cleanup(); diff --git a/tests/test_datetime.m b/tests/test_datetime.m index 63a4f7ec..7b119539 100644 --- a/tests/test_datetime.m +++ b/tests/test_datetime.m @@ -2,18 +2,18 @@ function test_datetime() %TEST_DATETIME Tests for datetime X axis support. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); close all force; drawnow; % testXTypeDefaultIsNumeric - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); assert(strcmp(fp.XType, 'numeric'), 'testXTypeDefault: should be numeric'); % testXTypeDatenum - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:99)/24; fp.addLine(x, rand(1,100), 'XType', 'datenum'); assert(strcmp(fp.XType, 'datenum'), 'testXTypeDatenum: should be datenum'); @@ -21,7 +21,7 @@ function test_datetime() % testDatetimeAutoConvert % Only run in MATLAB where datetime exists if exist('datetime', 'class') - fp = FastPlot(); + fp = FastSense(); dt = datetime(2024,1,1) + hours(0:99); fp.addLine(dt, rand(1,100)); assert(strcmp(fp.XType, 'datenum'), 'testDatetimeAutoConvert: should be datenum'); @@ -29,7 +29,7 @@ function test_datetime() end % testTickLabelsAreDateStrings - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:99)/24; % ~4 days of hourly data fp.addLine(x, rand(1,100), 'XType', 'datenum'); fp.render(); @@ -47,7 +47,7 @@ function test_datetime() % testTickFormatChangesOnZoom (MATLAB only — Octave lacks PostSet listeners) if ~exist('OCTAVE_VERSION', 'builtin') - fp = FastPlot(); + fp = FastSense(); x = datenum(2024,1,1) + (0:9999)/86400; % ~0.1s resolution fp.addLine(x, rand(1,10000), 'XType', 'datenum'); fp.render(); @@ -70,9 +70,9 @@ function test_datetime() % testToolbarFormatX % Verify the static helper returns date string for datenum XType xVal = datenum(2024, 3, 15, 10, 30, 45); - result = FastPlotToolbar.formatX(xVal, 'datenum'); + result = FastSenseToolbar.formatX(xVal, 'datenum'); assert(any(result == ':'), 'testToolbarFormatX: should contain colon'); - resultNum = FastPlotToolbar.formatX(42.5, 'numeric'); + resultNum = FastSenseToolbar.formatX(42.5, 'numeric'); assert(~any(resultNum == ':'), 'testToolbarFormatXNum: should not contain colon'); fprintf(' All datetime tests passed.\n'); diff --git a/tests/test_disk_advanced.m b/tests/test_disk_advanced.m index c029e1f2..8e4b85f5 100644 --- a/tests/test_disk_advanced.m +++ b/tests/test_disk_advanced.m @@ -1,10 +1,10 @@ function test_disk_advanced() -%TEST_DISK_ADVANCED Advanced integration tests for FastPlot disk storage. +%TEST_DISK_ADVANCED Advanced integration tests for FastSense disk storage. % Covers: storage mode transitions, multiple disk lines, pyramid building, % updateData edge cases, re-render after update, and stress scenarios. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); if exist('OCTAVE_VERSION', 'builtin') fprintf(' SKIPPED: requires MATLAB PostSet listeners.\n'); @@ -14,7 +14,7 @@ function test_disk_advanced() fprintf(' --- Advanced disk storage tests ---\n'); % 1. Multiple disk lines in same figure - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); for i = 1:5 x = linspace(0, 100, 10000); y = sin(x + i); @@ -35,7 +35,7 @@ function test_disk_advanced() fprintf(' multiple disk lines: PASS\n'); % 2. updateData: memory -> disk transition - fp = FastPlot('MemoryLimit', 10000); + fp = FastSense('MemoryLimit', 10000); fp.addLine(1:50, rand(1, 50)); % memory (800 bytes < 10000) fp.render(); assert(isempty(fp.Lines(1).DataStore), 'memToDisk: starts in memory'); @@ -47,7 +47,7 @@ function test_disk_advanced() fprintf(' memory -> disk transition: PASS\n'); % 3. updateData: disk -> memory transition - fp = FastPlot('MemoryLimit', 10000); + fp = FastSense('MemoryLimit', 10000); fp.addLine(linspace(0, 100, 5000), rand(1, 5000)); % disk fp.render(); assert(~isempty(fp.Lines(1).DataStore), 'diskToMem: starts on disk'); @@ -68,7 +68,7 @@ function test_disk_advanced() fprintf(' disk -> memory transition: PASS\n'); % 4. updateData preserves old DataStore cleanup - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:5000, rand(1, 5000)); fp.render(); ds1 = fp.Lines(1).DataStore; @@ -83,7 +83,7 @@ function test_disk_advanced() fprintf(' updateData cleans old DataStore: PASS\n'); % 5. Render, update, re-render cycle - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 20000); fp.addLine(x, sin(x)); fp.render(); @@ -98,7 +98,7 @@ function test_disk_advanced() fprintf(' render-update-rerender cycle: PASS\n'); % 6. Zoom to very narrow range on disk line - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 100000; x = linspace(0, 1000, n); y = sin(x); @@ -113,7 +113,7 @@ function test_disk_advanced() fprintf(' very narrow zoom on disk: PASS\n'); % 7. Zoom out to full range after narrow zoom - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 50000; x = linspace(0, 100, n); y = sin(x); @@ -133,7 +133,7 @@ function test_disk_advanced() fprintf(' zoom in then out: PASS\n'); % 8. Mixed memory+disk lines render with correct data - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); x1 = 1:50; y1 = x1 * 2; % memory x2 = linspace(0, 100, 5000); y2 = x2 * 3; % disk fp.addLine(x1, y1, 'DisplayName', 'Mem'); @@ -151,7 +151,7 @@ function test_disk_advanced() fprintf(' mixed mem+disk data fidelity: PASS\n'); % 9. Delete with multiple disk lines cleans all files - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); paths = {}; for i = 1:3 fp.addLine(1:5000, rand(1, 5000)); @@ -166,7 +166,7 @@ function test_disk_advanced() fprintf(' delete cleans all disk files: PASS\n'); % 10. Disk line with NaN values renders without error - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 10000; x = linspace(0, 100, n); y = sin(x); @@ -178,7 +178,7 @@ function test_disk_advanced() fprintf(' disk line with NaN gap: PASS\n'); % 11. Threshold violations with disk line + NaN - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 10000); y = sin(x); y(500:600) = NaN; fp.addLine(x, y); @@ -189,7 +189,7 @@ function test_disk_advanced() fprintf(' threshold on disk+NaN line: PASS\n'); % 12. Threshold with lower direction on disk line - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 10000); y = sin(x); fp.addLine(x, y); @@ -200,7 +200,7 @@ function test_disk_advanced() fprintf(' lower threshold on disk line: PASS\n'); % 13. Multiple thresholds on disk line - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 100, 20000); y = sin(x); fp.addLine(x, y); @@ -213,7 +213,7 @@ function test_disk_advanced() fprintf(' multiple thresholds on disk: PASS\n'); % 14. MemoryLimit boundary: exactly at threshold - fp = FastPlot('MemoryLimit', 1600); % 100 points * 8 * 2 = 1600 + fp = FastSense('MemoryLimit', 1600); % 100 points * 8 * 2 = 1600 fp.addLine(1:100, rand(1, 100)); % 1600 bytes == 1600 limit → not strictly greater, stays in memory assert(isempty(fp.Lines(1).DataStore), 'boundary: at limit stays memory'); @@ -223,7 +223,7 @@ function test_disk_advanced() fprintf(' MemoryLimit exact boundary: PASS\n'); % 15. Rapid sequential updateData calls - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:5000, rand(1, 5000)); fp.render(); for i = 1:10 diff --git a/tests/test_disk_storage.m b/tests/test_disk_storage.m index 10560cf3..0ebd2cd6 100644 --- a/tests/test_disk_storage.m +++ b/tests/test_disk_storage.m @@ -1,11 +1,11 @@ function test_disk_storage() -%TEST_DISK_STORAGE Integration tests for FastPlot disk-backed storage. -% Tests that FastPlot correctly stores large datasets on disk via -% FastPlotDataStore and that render, zoom/pan, updateData, and cleanup +%TEST_DISK_STORAGE Integration tests for FastSense disk-backed storage. +% Tests that FastSense correctly stores large datasets on disk via +% FastSenseDataStore and that render, zoom/pan, updateData, and cleanup % all work transparently with disk-backed lines. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); if exist('OCTAVE_VERSION', 'builtin') fprintf(' SKIPPED: requires MATLAB PostSet listeners.\n'); @@ -15,27 +15,27 @@ function test_disk_storage() % --- StorageMode and MemoryLimit properties --- % testDefaultStorageMode: default is 'auto' - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); assert(strcmp(fp.StorageMode, 'auto'), 'testDefaultStorageMode'); % testStorageModeConstructor: can set via constructor - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); assert(strcmp(fp.StorageMode, 'disk'), 'testStorageModeConstructor'); % testMemoryLimitDefault: default is 500e6 - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); assert(fp.MemoryLimit == 500e6, 'testMemoryLimitDefault'); % testMemoryLimitConstructor: can override via constructor - fp = FastPlot('MemoryLimit', 100e6); + fp = FastSense('MemoryLimit', 100e6); assert(fp.MemoryLimit == 100e6, 'testMemoryLimitConstructor'); % --- Auto storage mode triggers disk for large data --- % testAutoModeDiskTrigger: data exceeding MemoryLimit goes to disk - fp = FastPlot('MemoryLimit', 1000); % very low threshold: 1000 bytes + fp = FastSense('MemoryLimit', 1000); % very low threshold: 1000 bytes n = 1000; % 1000 points * 8 bytes * 2 = 16000 bytes > 1000 x = linspace(0, 10, n); y = sin(x); @@ -50,7 +50,7 @@ function test_disk_storage() 'testAutoModeDiskTrigger: NumPoints must be correct'); % testAutoModeMemoryForSmall: small data stays in memory - fp = FastPlot('MemoryLimit', 1e9); % 1 GB threshold + fp = FastSense('MemoryLimit', 1e9); % 1 GB threshold fp.addLine(1:100, rand(1, 100)); assert(isempty(fp.Lines(1).DataStore), ... 'testAutoModeMemoryForSmall: should NOT have DataStore'); @@ -58,7 +58,7 @@ function test_disk_storage() 'testAutoModeMemoryForSmall: X should be in memory'); % testForceDiskMode: StorageMode='disk' forces all data to disk - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:50, rand(1, 50)); assert(~isempty(fp.Lines(1).DataStore), ... 'testForceDiskMode: small data should still go to disk'); @@ -66,7 +66,7 @@ function test_disk_storage() 'testForceDiskMode: NumPoints'); % testForceMemoryMode: StorageMode='memory' keeps all data in RAM - fp = FastPlot('StorageMode', 'memory', 'MemoryLimit', 100); + fp = FastSense('StorageMode', 'memory', 'MemoryLimit', 100); fp.addLine(1:10000, rand(1, 10000)); assert(isempty(fp.Lines(1).DataStore), ... 'testForceMemoryMode: should NOT use disk even if above limit'); @@ -74,7 +74,7 @@ function test_disk_storage() % --- Render with disk-backed data --- % testRenderDiskLine: disk-backed line renders without error - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 20000; x = linspace(0, 100, n); y = sin(x); @@ -92,7 +92,7 @@ function test_disk_storage() close(fp.hFigure); % testRenderMixedLines: mix of memory and disk lines renders - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); fp.addLine(1:10, rand(1, 10), 'DisplayName', 'Small'); % memory fp.addLine(linspace(0,100,5000), rand(1,5000), 'DisplayName', 'Large'); % disk fp.render(); @@ -104,7 +104,7 @@ function test_disk_storage() % --- Zoom/pan with disk-backed data --- % testZoomDiskLine: zooming re-downsamples from disk correctly - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 50000; x = linspace(0, 100, n); y = sin(x); @@ -124,7 +124,7 @@ function test_disk_storage() close(fp.hFigure); % testZoomDiskLineDataFidelity: zoomed data preserves signal shape - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 100000; x = linspace(0, 100, n); y = x * 2; % simple linear — easy to verify @@ -146,7 +146,7 @@ function test_disk_storage() % --- updateData with disk-backed storage --- % testUpdateDataDisk: updateData replaces disk-backed data - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); x = linspace(0, 10, 10000); y = sin(x); fp.addLine(x, y); @@ -168,7 +168,7 @@ function test_disk_storage() % --- Threshold violations with disk-backed data --- % testThresholdsDiskLine: violations render correctly on disk lines - fp = FastPlot('StorageMode', 'disk'); + fp = FastSense('StorageMode', 'disk'); n = 10000; x = linspace(0, 100, n); y = sin(x); % values between -1 and 1 @@ -181,8 +181,8 @@ function test_disk_storage() % --- Cleanup on delete --- - % testDeleteCleansDiskFiles: deleting FastPlot cleans up DataStore files - fp = FastPlot('StorageMode', 'disk'); + % testDeleteCleansDiskFiles: deleting FastSense cleans up DataStore files + fp = FastSense('StorageMode', 'disk'); fp.addLine(1:5000, rand(1, 5000)); ds = fp.Lines(1).DataStore; % Get the file path before deletion @@ -200,7 +200,7 @@ function test_disk_storage() % --- lineNumPoints helper --- % testLineNumPoints: works for both memory and disk lines - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); fp.addLine(1:50, rand(1, 50)); % memory fp.addLine(1:5000, rand(1, 5000)); % disk assert(fp.lineNumPoints(1) == 50, 'testLineNumPoints: memory line'); @@ -209,7 +209,7 @@ function test_disk_storage() % --- lineXRange helper --- % testLineXRange: returns correct endpoints for both storage types - fp = FastPlot('MemoryLimit', 1000); + fp = FastSense('MemoryLimit', 1000); fp.addLine(5:100, rand(1, 96)); % memory fp.addLine(linspace(10,200,5000), rand(1,5000)); % disk [mn1, mx1] = fp.lineXRange(1); diff --git a/tests/test_downsample_violations.m b/tests/test_downsample_violations.m index 38832cdf..3b2d00c3 100644 --- a/tests/test_downsample_violations.m +++ b/tests/test_downsample_violations.m @@ -2,7 +2,7 @@ function test_downsample_violations() %TEST_DOWNSAMPLE_VIOLATIONS Tests for violation marker pixel-density culling. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testBasicDownsample: 10 violations in 3 pixel columns -> 3 points xV = [1.0, 1.1, 1.2, 2.0, 2.1, 2.2, 3.0, 3.1, 3.2, 3.3]; diff --git a/tests/test_event_snapshot.m b/tests/test_event_snapshot.m index cc0616f2..073651fb 100644 --- a/tests/test_event_snapshot.m +++ b/tests/test_event_snapshot.m @@ -14,7 +14,7 @@ function add_event_path() addpath(repoRoot); addpath(fullfile(repoRoot, 'libs', 'EventDetection')); addpath(fullfile(repoRoot, 'libs', 'SensorThreshold')); - addpath(fullfile(repoRoot, 'libs', 'FastPlot')); + addpath(fullfile(repoRoot, 'libs', 'FastSense')); setup(); end diff --git a/tests/test_fastplot_theme.m b/tests/test_fastsense_theme.m similarity index 83% rename from tests/test_fastplot_theme.m rename to tests/test_fastsense_theme.m index 0dfb7b4b..812a33f7 100644 --- a/tests/test_fastplot_theme.m +++ b/tests/test_fastsense_theme.m @@ -1,27 +1,27 @@ -function test_fastplot_theme() -%TEST_FASTPLOT_THEME Tests for FastPlot theme integration. +function test_fastsense_theme() +%TEST_FASTSENSE_THEME Tests for FastSense theme integration. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testThemeConstructorString - fp = FastPlot('Theme', 'dark'); + fp = FastSense('Theme', 'dark'); assert(isstruct(fp.Theme), 'testThemeConstructorString: Theme must be struct'); assert(all(fp.Theme.Background < [0.2 0.2 0.2]), 'testThemeConstructorString: dark bg'); % testThemeConstructorStruct custom = struct('Background', [0.5 0.5 0.5]); - fp = FastPlot('Theme', custom); + fp = FastSense('Theme', custom); assert(isequal(fp.Theme.Background, [0.5 0.5 0.5]), 'testThemeConstructorStruct'); assert(isfield(fp.Theme, 'FontSize'), 'testThemeConstructorStruct: inherits defaults'); % testDefaultThemeWhenNoneSpecified - fp = FastPlot(); + fp = FastSense(); assert(isstruct(fp.Theme), 'testDefaultTheme: must have theme'); assert(isequal(fp.Theme.Background, [1 1 1]), 'testDefaultTheme: default bg'); % testThemeAppliedOnRender - fp = FastPlot('Theme', 'dark'); + fp = FastSense('Theme', 'dark'); fp.addLine(1:100, rand(1,100)); fp.render(); bgColor = get(fp.hFigure, 'Color'); @@ -31,7 +31,7 @@ function test_fastplot_theme() close(fp.hFigure); % testThemeFontApplied - fp = FastPlot('Theme', 'scientific'); + fp = FastSense('Theme', 'scientific'); fp.addLine(1:100, rand(1,100)); fp.render(); assert(strcmp(get(fp.hAxes, 'FontName'), 'Times New Roman'), 'testThemeFontApplied'); @@ -40,7 +40,7 @@ function test_fastplot_theme() % testThemeWithParentAxes fig = figure('Visible', 'off'); ax = axes('Parent', fig); - fp = FastPlot('Parent', ax, 'Theme', 'dark'); + fp = FastSense('Parent', ax, 'Theme', 'dark'); fp.addLine(1:100, rand(1,100)); fp.render(); axColor = get(ax, 'Color'); @@ -48,14 +48,14 @@ function test_fastplot_theme() close(fig); % testBackwardCompatNoTheme - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); assert(ishandle(fp.hAxes), 'testBackwardCompatNoTheme'); close(fp.hFigure); % testAutoColorCycling - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.addLine(1:10, rand(1,10)); fp.addLine(1:10, rand(1,10)); @@ -66,7 +66,7 @@ function test_fastplot_theme() assert(~isequal(c2, c3), 'testAutoColorCycling: colors 2 and 3 differ'); % testExplicitColorSkipsCycle - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10), 'Color', [1 0 0]); fp.addLine(1:10, rand(1,10)); assert(isequal(fp.Lines(1).Options.Color, [1 0 0]), 'testExplicitColorSkipsCycle: explicit'); @@ -74,13 +74,13 @@ function test_fastplot_theme() assert(isequal(fp.Lines(2).Options.Color, expected2), 'testExplicitColorSkipsCycle: auto gets first'); % testThresholdUsesThemeDefaults - fp = FastPlot('Theme', struct('ThresholdColor', [0 1 0], 'ThresholdStyle', ':')); + fp = FastSense('Theme', struct('ThresholdColor', [0 1 0], 'ThresholdStyle', ':')); fp.addThreshold(5.0); assert(isequal(fp.Thresholds(1).Color, [0 1 0]), 'testThresholdThemeDefaults: Color'); assert(strcmp(fp.Thresholds(1).LineStyle, ':'), 'testThresholdThemeDefaults: Style'); % testThresholdExplicitOverridesTheme - fp = FastPlot('Theme', struct('ThresholdColor', [0 1 0])); + fp = FastSense('Theme', struct('ThresholdColor', [0 1 0])); fp.addThreshold(5.0, 'Color', [1 0 0]); assert(isequal(fp.Thresholds(1).Color, [1 0 0]), 'testThresholdOverride: Color'); diff --git a/tests/test_figure_layout.m b/tests/test_figure_layout.m index f5a81994..69bb7da7 100644 --- a/tests/test_figure_layout.m +++ b/tests/test_figure_layout.m @@ -1,24 +1,24 @@ function test_figure_layout() -%TEST_FIGURE_LAYOUT Tests for FastPlotGrid layout manager. +%TEST_FIGURE_LAYOUT Tests for FastSenseGrid layout manager. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testConstruction - fig = FastPlotGrid(2, 3); + fig = FastSenseGrid(2, 3); assert(isequal(fig.Grid, [2 3]), 'testConstruction: Grid'); assert(~isempty(fig.hFigure), 'testConstruction: hFigure'); assert(ishandle(fig.hFigure), 'testConstruction: hFigure valid'); close(fig.hFigure); - % testTileReturnsFastPlot - fig = FastPlotGrid(2, 1); + % testTileReturnsFastSense + fig = FastSenseGrid(2, 1); fp = fig.tile(1); - assert(isa(fp, 'FastPlot'), 'testTileReturnsFastPlot'); + assert(isa(fp, 'FastSense'), 'testTileReturnsFastSense'); close(fig.hFigure); % testTileLazy - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); fp1a = fig.tile(1); fp1b = fig.tile(1); % In Octave, handle == isn't always defined; check axes handle identity @@ -27,7 +27,7 @@ function test_figure_layout() close(fig.hFigure); % testTileCreatesAxes - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); fp = fig.tile(1); fp.addLine(1:100, rand(1,100)); fp.render(); @@ -36,7 +36,7 @@ function test_figure_layout() close(fig.hFigure); % testMultipleTiles - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); for i = 1:4 fp = fig.tile(i); fp.addLine(1:50, rand(1,50)); @@ -49,7 +49,7 @@ function test_figure_layout() close(fig.hFigure); % testRenderAllSkipsRendered - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); fp1 = fig.tile(1); fp1.addLine(1:10, rand(1,10)); fp1.render(); @@ -60,7 +60,7 @@ function test_figure_layout() close(fig.hFigure); % testOutOfBoundsTileErrors - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); threw = false; try fig.tile(5); % only 4 tiles in 2x2 @@ -71,7 +71,7 @@ function test_figure_layout() close(fig.hFigure); % testTileSpanning - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); fig.setTileSpan(1, [1 2]); % tile 1 spans both columns fp1 = fig.tile(1); fp1.addLine(1:50, rand(1,50)); @@ -82,13 +82,13 @@ function test_figure_layout() close(fig.hFigure); % testFigureThemePassedToTiles - fig = FastPlotGrid(2, 1, 'Theme', 'dark'); + fig = FastSenseGrid(2, 1, 'Theme', 'dark'); fp = fig.tile(1); assert(all(fp.Theme.Background < [0.2 0.2 0.2]), 'testFigureThemePassedToTiles'); close(fig.hFigure); % testTileThemeOverride - fig = FastPlotGrid(2, 1, 'Theme', 'dark'); + fig = FastSenseGrid(2, 1, 'Theme', 'dark'); fig.setTileTheme(1, struct('AxesColor', [0.3 0 0])); fp = fig.tile(1); assert(isequal(fp.Theme.AxesColor, [0.3 0 0]), 'testTileThemeOverride: AxesColor'); @@ -96,13 +96,13 @@ function test_figure_layout() close(fig.hFigure); % testFigureProperties - fig = FastPlotGrid(1, 1, 'Name', 'MyDash', 'Position', [50 50 800 600]); + fig = FastSenseGrid(1, 1, 'Name', 'MyDash', 'Position', [50 50 800 600]); name = get(fig.hFigure, 'Name'); assert(strcmp(name, 'MyDash'), 'testFigureProperties: Name'); close(fig.hFigure); % testTileLabels - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); fp = fig.tile(1); fp.addLine(1:50, rand(1,50)); fp.render(); @@ -113,21 +113,21 @@ function test_figure_layout() close(fig.hFigure); % testAxesReturnsRawAxes - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); ax = fig.axes(1); assert(ishandle(ax), 'testAxesReturnsRawAxes: valid handle'); assert(strcmp(get(ax, 'Type'), 'axes'), 'testAxesReturnsRawAxes: is axes'); close(fig.hFigure); % testAxesLazy - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); ax1 = fig.axes(1); ax2 = fig.axes(1); assert(isequal(ax1, ax2), 'testAxesLazy: same handle on repeat call'); close(fig.hFigure); % testTileThenAxesErrors - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); fig.tile(1); threw = false; try @@ -139,7 +139,7 @@ function test_figure_layout() close(fig.hFigure); % testAxesThenTileErrors - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); fig.axes(1); threw = false; try @@ -151,7 +151,7 @@ function test_figure_layout() close(fig.hFigure); % testMixedRenderAll - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); fig.tile(1).addLine(1:50, rand(1,50)); ax2 = fig.axes(2); bar(ax2, [1 2 3], [10 20 15]); fig.tile(3).addLine(1:50, rand(1,50)); @@ -165,14 +165,14 @@ function test_figure_layout() close(fig.hFigure); % testAxesThemeApplied - fig = FastPlotGrid(1, 1, 'Theme', 'dark'); + fig = FastSenseGrid(1, 1, 'Theme', 'dark'); ax = fig.axes(1); bgColor = get(ax, 'Color'); assert(all(bgColor < [0.3 0.3 0.3]), 'testAxesThemeApplied: dark background'); close(fig.hFigure); % testLabelsOnRawAxes - fig = FastPlotGrid(2, 1); + fig = FastSenseGrid(2, 1); ax = fig.axes(1); bar(ax, [1 2 3], [10 20 15]); fig.setTileTitle(1, 'Bar Chart'); @@ -184,7 +184,7 @@ function test_figure_layout() close(fig.hFigure); % testAxesOutOfBounds - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); threw = false; try fig.axes(5); @@ -195,7 +195,7 @@ function test_figure_layout() close(fig.hFigure); % testAxesTileSpanning - fig = FastPlotGrid(2, 2); + fig = FastSenseGrid(2, 2); fig.setTileSpan(1, [1 2]); ax = fig.axes(1); pos = get(ax, 'Position'); diff --git a/tests/test_linked_axes.m b/tests/test_linked_axes.m index b8db6170..2f6e1024 100644 --- a/tests/test_linked_axes.m +++ b/tests/test_linked_axes.m @@ -3,7 +3,7 @@ function test_linked_axes() % Requires PostSet listeners (MATLAB only, skipped on Octave). addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); if exist('OCTAVE_VERSION', 'builtin') fprintf(' SKIPPED: Octave lacks PostSet listeners for axes properties.\n'); @@ -15,11 +15,11 @@ function test_linked_axes() ax1 = subplot(2,1,1, 'Parent', fig); ax2 = subplot(2,1,2, 'Parent', fig); - fp1 = FastPlot('Parent', ax1, 'LinkGroup', 'testgroup'); + fp1 = FastSense('Parent', ax1, 'LinkGroup', 'testgroup'); fp1.addLine(1:1000, rand(1,1000)); fp1.render(); - fp2 = FastPlot('Parent', ax2, 'LinkGroup', 'testgroup'); + fp2 = FastSense('Parent', ax2, 'LinkGroup', 'testgroup'); fp2.addLine(1:1000, rand(1,1000)); fp2.render(); @@ -40,11 +40,11 @@ function test_linked_axes() ax1 = subplot(2,1,1, 'Parent', fig); ax2 = subplot(2,1,2, 'Parent', fig); - fp1 = FastPlot('Parent', ax1); + fp1 = FastSense('Parent', ax1); fp1.addLine(1:1000, rand(1,1000)); fp1.render(); - fp2 = FastPlot('Parent', ax2); + fp2 = FastSense('Parent', ax2); fp2.addLine(1:1000, rand(1,1000)); fp2.render(); diff --git a/tests/test_live_pipeline.m b/tests/test_live_pipeline.m index 146e39bd..6a50554e 100644 --- a/tests/test_live_pipeline.m +++ b/tests/test_live_pipeline.m @@ -16,7 +16,7 @@ function add_event_path() addpath(repoRoot); addpath(fullfile(repoRoot, 'libs', 'EventDetection')); addpath(fullfile(repoRoot, 'libs', 'SensorThreshold')); - addpath(fullfile(repoRoot, 'libs', 'FastPlot')); + addpath(fullfile(repoRoot, 'libs', 'FastSense')); setup(); end diff --git a/tests/test_lttb_downsample.m b/tests/test_lttb_downsample.m index f39ad72b..b869a0c9 100644 --- a/tests/test_lttb_downsample.m +++ b/tests/test_lttb_downsample.m @@ -2,7 +2,7 @@ function test_lttb_downsample() %TEST_LTTB_DOWNSAMPLE Tests for lttb_downsample private function. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testOutputSize x = 1:1000; diff --git a/tests/test_mex_edge_cases.m b/tests/test_mex_edge_cases.m index 727d5159..1cc49e79 100644 --- a/tests/test_mex_edge_cases.m +++ b/tests/test_mex_edge_cases.m @@ -3,7 +3,7 @@ function test_mex_edge_cases() % Skips if MEX files are not compiled. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); has_bs = (exist('binary_search_mex', 'file') == 3); has_mm = (exist('minmax_core_mex', 'file') == 3); diff --git a/tests/test_mex_parity.m b/tests/test_mex_parity.m index b8dc32fa..fe5e7b50 100644 --- a/tests/test_mex_parity.m +++ b/tests/test_mex_parity.m @@ -4,7 +4,7 @@ function test_mex_parity() % Skips if MEX files are not compiled. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); has_bs = (exist('binary_search_mex', 'file') == 3); has_mm = (exist('minmax_core_mex', 'file') == 3); diff --git a/tests/test_minmax_downsample.m b/tests/test_minmax_downsample.m index 564ca87d..4c3da823 100644 --- a/tests/test_minmax_downsample.m +++ b/tests/test_minmax_downsample.m @@ -2,7 +2,7 @@ function test_minmax_downsample() %TEST_MINMAX_DOWNSAMPLE Tests for minmax_downsample private function. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testBasicReduction: 10 buckets -> 20 output points x = 1:100; diff --git a/tests/test_mksqlite_types.m b/tests/test_mksqlite_types.m index b080ce15..84f8e848 100644 --- a/tests/test_mksqlite_types.m +++ b/tests/test_mksqlite_types.m @@ -124,7 +124,7 @@ function test_mksqlite_types() n = 50000; x = linspace(0, 100, n); y = sin(x); - ds = FastPlotDataStore(x, y); + ds = FastSenseDataStore(x, y); % 10. addColumn with cell of strings labels = cell(1, n); @@ -234,14 +234,14 @@ function test_mksqlite_types() n2 = 1000; x2 = linspace(0, 100, n2); y2 = sin(x2); - ds2 = FastPlotDataStore(x2, y2); + ds2 = FastSenseDataStore(x2, y2); % 23. toCategorical round-trip via struct catData2.codes = uint32(mod(0:n2-1, 3) + 1); catData2.categories = {'low', 'medium', 'high'}; ds2.addColumn('cat_struct', catData2); slice = ds2.getColumnSlice('cat_struct', 1, 6); - labels_back = FastPlotDataStore.toCategorical(slice); + labels_back = FastSenseDataStore.toCategorical(slice); if exist('categorical', 'class') assert(isa(labels_back, 'categorical'), ... 'toCategorical: should return categorical in MATLAB'); @@ -261,7 +261,7 @@ function test_mksqlite_types() % 24. toCategorical with bad input should error threw = false; try - FastPlotDataStore.toCategorical('not_a_struct'); + FastSenseDataStore.toCategorical('not_a_struct'); catch threw = true; end @@ -276,14 +276,14 @@ function test_mksqlite_types() sliceN = ds2.getColumnSlice('cat_native', 1, 5); assert(isstruct(sliceN) && isfield(sliceN, 'codes'), ... 'auto-convert categorical: should be stored as struct'); - labelsN = FastPlotDataStore.toCategorical(sliceN); + labelsN = FastSenseDataStore.toCategorical(sliceN); assert(isa(labelsN, 'categorical'), ... 'auto-convert categorical: toCategorical should return categorical'); fprintf(' addColumn auto-convert categorical: PASS\n'); % 26. fromCategorical c_test = categorical({'x','y','z','x'}, {'x','y','z'}); - s_test = FastPlotDataStore.fromCategorical(c_test); + s_test = FastSenseDataStore.fromCategorical(c_test); assert(isequal(s_test.codes, uint32([1 2 3 1])), ... 'fromCategorical: codes mismatch'); assert(isequal(s_test.categories, {'x','y','z'}), ... diff --git a/tests/test_multi_threshold.m b/tests/test_multi_threshold.m index 8fd778a5..17c9e98f 100644 --- a/tests/test_multi_threshold.m +++ b/tests/test_multi_threshold.m @@ -2,10 +2,10 @@ function test_multi_threshold() %TEST_MULTI_THRESHOLD Tests for per-threshold rendering with independent colors/markers. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testEachThresholdGetsOwnLine - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, randn(1,100)); fp.addThreshold(2.0, 'Direction', 'upper', 'Color', 'r', 'LineStyle', '--'); fp.addThreshold(1.0, 'Direction', 'upper', 'Color', [1 0.6 0], 'LineStyle', ':'); @@ -32,7 +32,7 @@ function test_multi_threshold() close(fp.hFigure); % testEachThresholdGetsOwnViolationMarkers - fp = FastPlot(); + fp = FastSense(); y = [0 0 0 1.5 1.5 0 0 0 2.5 2.5 0 0]; fp.addLine(1:12, y); fp.addThreshold(2.0, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); @@ -59,7 +59,7 @@ function test_multi_threshold() close(fp.hFigure); % testThresholdWithoutViolationsGetsNoMarkers - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, zeros(1,10)); fp.addThreshold(5.0, 'Direction', 'upper', 'ShowViolations', true, 'Color', 'r'); fp.render(); @@ -73,7 +73,7 @@ function test_multi_threshold() close(fp.hFigure); % testShowViolationsFalseGetsNoMarkerHandle - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, 5*ones(1,10)); fp.addThreshold(2.0, 'Direction', 'upper', 'ShowViolations', false, 'Color', 'r'); fp.render(); @@ -84,7 +84,7 @@ function test_multi_threshold() % testViolationsUpdateOnZoomPerThreshold (MATLAB only — needs PostSet listeners) if ~exist('OCTAVE_VERSION', 'builtin') - fp = FastPlot(); + fp = FastSense(); y = [zeros(1,100), 1.5*ones(1,100), zeros(1,100), 2.5*ones(1,100), zeros(1,100)]; x = 1:500; fp.addLine(x, y); @@ -109,15 +109,15 @@ function test_multi_threshold() end % testUserDataTagging - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, randn(1,100)); fp.addThreshold(1.0, 'Direction', 'upper', 'Label', 'AlarmHi', 'Color', 'r'); fp.render(); ud = get(fp.Thresholds(1).hLine, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'threshold'), 'UserData Type'); - assert(strcmp(ud.FastPlot.Name, 'AlarmHi'), 'UserData Name'); - assert(ud.FastPlot.ThresholdValue == 1.0, 'UserData ThresholdValue'); + assert(strcmp(ud.FastSense.Type, 'threshold'), 'UserData Type'); + assert(strcmp(ud.FastSense.Name, 'AlarmHi'), 'UserData Name'); + assert(ud.FastSense.ThresholdValue == 1.0, 'UserData ThresholdValue'); close(fp.hFigure); diff --git a/tests/test_notification_service.m b/tests/test_notification_service.m index 20d51a6a..3da7a751 100644 --- a/tests/test_notification_service.m +++ b/tests/test_notification_service.m @@ -16,7 +16,7 @@ function add_event_path() addpath(repoRoot); addpath(fullfile(repoRoot, 'libs', 'EventDetection')); addpath(fullfile(repoRoot, 'libs', 'SensorThreshold')); - addpath(fullfile(repoRoot, 'libs', 'FastPlot')); + addpath(fullfile(repoRoot, 'libs', 'FastSense')); setup(); end diff --git a/tests/test_render.m b/tests/test_render.m index ddeab935..6ba081d2 100644 --- a/tests/test_render.m +++ b/tests/test_render.m @@ -1,11 +1,11 @@ function test_render() -%TEST_RENDER Tests for FastPlot.render method. +%TEST_RENDER Tests for FastSense.render method. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); % testCreatesNewFigure - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'Test'); fp.render(); assert(isfigure(fp.hFigure), 'testCreatesNewFigure: hFigure'); @@ -15,14 +15,14 @@ function test_render() % testUsesExistingAxes fig = figure('Visible', 'off'); ax = axes('Parent', fig); - fp = FastPlot('Parent', ax); + fp = FastSense('Parent', ax); fp.addLine(1:100, rand(1,100)); fp.render(); assert(fp.hAxes == ax, 'testUsesExistingAxes'); close(fig); % testCreatesLineObjects - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'L1'); fp.addLine(1:100, rand(1,100), 'DisplayName', 'L2'); fp.render(); @@ -32,28 +32,28 @@ function test_render() close(fp.hFigure); % testUserDataTagging - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'Sensor1'); fp.addThreshold(0.5, 'Label', 'UpperLim'); fp.render(); ud = get(fp.Lines(1).hLine, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'data_line'), 'testUserDataTagging: Type'); - assert(strcmp(ud.FastPlot.Name, 'Sensor1'), 'testUserDataTagging: Name'); - assert(ud.FastPlot.LineIndex == 1, 'testUserDataTagging: LineIndex'); + assert(strcmp(ud.FastSense.Type, 'data_line'), 'testUserDataTagging: Type'); + assert(strcmp(ud.FastSense.Name, 'Sensor1'), 'testUserDataTagging: Name'); + assert(ud.FastSense.LineIndex == 1, 'testUserDataTagging: LineIndex'); close(fp.hFigure); % testThresholdLineCreated (per-threshold) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.addThreshold(0.5, 'Direction', 'upper', 'Label', 'UL'); fp.render(); assert(isgraphics(fp.Thresholds(1).hLine, 'line'), 'testThresholdLineCreated'); ud = get(fp.Thresholds(1).hLine, 'UserData'); - assert(strcmp(ud.FastPlot.Type, 'threshold'), 'testThresholdLineCreated: Type'); + assert(strcmp(ud.FastSense.Type, 'threshold'), 'testThresholdLineCreated: Type'); close(fp.hFigure); % testViolationMarkersCreated (per-threshold) - fp = FastPlot(); + fp = FastSense(); y = [0.1 0.2 0.8 0.9 0.3 0.1]; fp.addLine(1:6, y); fp.addThreshold(0.5, 'Direction', 'upper', 'ShowViolations', true); @@ -66,7 +66,7 @@ function test_render() close(fp.hFigure); % testDoubleRenderError - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:10, rand(1,10)); fp.render(); threw = false; @@ -79,7 +79,7 @@ function test_render() close(fp.hFigure); % testStaticAxisLimits - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); assert(strcmp(get(fp.hAxes, 'XLimMode'), 'manual'), 'testStaticAxisLimits: XLimMode'); diff --git a/tests/test_sensor_todisk.m b/tests/test_sensor_todisk.m index 06f3172c..c6e17fdc 100644 --- a/tests/test_sensor_todisk.m +++ b/tests/test_sensor_todisk.m @@ -59,7 +59,7 @@ function test_sensor_todisk() fprintf(' resolve with disk data: PASS\n'); %% 4. addSensor with disk-backed sensor - fp = FastPlot(); + fp = FastSense(); fp.addSensor(s2, 'ShowThresholds', true); fp.render(); assert(numel(fp.Lines) >= 1, 'should have at least 1 line'); @@ -72,7 +72,7 @@ function test_sensor_todisk() s3.X = linspace(0, 100, 10000); s3.Y = rand(1, 10000); s3.toDisk(); - fp2 = FastPlot(); + fp2 = FastSense(); fp2.addSensor(s3); fp2.render(); assert(numel(fp2.Lines) == 1, 'should have 1 line'); diff --git a/tests/test_theme.m b/tests/test_theme.m index 231ed20a..8dca607c 100644 --- a/tests/test_theme.m +++ b/tests/test_theme.m @@ -1,10 +1,10 @@ function test_theme() -%TEST_THEME Tests for FastPlotTheme function. +%TEST_THEME Tests for FastSenseTheme function. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); % testDefaultPreset - t = FastPlotTheme('default'); + t = FastSenseTheme('default'); assert(isstruct(t), 'testDefaultPreset: must return struct'); assert(isequal(t.Background, [1 1 1]), 'testDefaultPreset: Background'); assert(isfield(t, 'AxesColor'), 'testDefaultPreset: AxesColor field'); @@ -25,12 +25,12 @@ function test_theme() assert(size(t.LineColorOrder, 2) == 3, 'testDefaultPreset: LineColorOrder must be Nx3'); % testNoArgsReturnsDefault - t0 = FastPlotTheme(); - t1 = FastPlotTheme('default'); + t0 = FastSenseTheme(); + t1 = FastSenseTheme('default'); assert(isequal(t0, t1), 'testNoArgsReturnsDefault'); % testMergeOverrides - t = FastPlotTheme('default', 'FontSize', 14, 'LineWidth', 2.0); + t = FastSenseTheme('default', 'FontSize', 14, 'LineWidth', 2.0); assert(t.FontSize == 14, 'testMergeOverrides: FontSize'); assert(t.LineWidth == 2.0, 'testMergeOverrides: LineWidth'); assert(isequal(t.Background, [1 1 1]), 'testMergeOverrides: Background unchanged'); @@ -38,37 +38,37 @@ function test_theme() % testInvalidPresetErrors threw = false; try - FastPlotTheme('nonexistent'); + FastSenseTheme('nonexistent'); catch threw = true; end assert(threw, 'testInvalidPresetErrors'); % testDarkPreset - t = FastPlotTheme('dark'); + t = FastSenseTheme('dark'); assert(all(t.Background < [0.2 0.2 0.2]), 'testDarkPreset: Background should be dark'); assert(all(t.ForegroundColor > [0.7 0.7 0.7]), 'testDarkPreset: ForegroundColor should be light'); assert(size(t.LineColorOrder, 2) == 3, 'testDarkPreset: LineColorOrder Nx3'); % testLightPreset - t = FastPlotTheme('light'); + t = FastSenseTheme('light'); assert(all(t.Background > [0.9 0.9 0.9]), 'testLightPreset: Background'); assert(size(t.LineColorOrder, 2) == 3, 'testLightPreset: LineColorOrder Nx3'); % testIndustrialPreset - t = FastPlotTheme('industrial'); + t = FastSenseTheme('industrial'); assert(t.LineWidth >= 1.0, 'testIndustrialPreset: LineWidth'); assert(size(t.LineColorOrder, 2) == 3, 'testIndustrialPreset: LineColorOrder Nx3'); % testScientificPreset - t = FastPlotTheme('scientific'); + t = FastSenseTheme('scientific'); assert(strcmp(t.FontName, 'Times New Roman'), 'testScientificPreset: FontName'); assert(t.GridAlpha == 0, 'testScientificPreset: no grid'); assert(t.LineWidth < 1.0, 'testScientificPreset: thin lines'); assert(size(t.LineColorOrder, 2) == 3, 'testScientificPreset: LineColorOrder Nx3'); % testOceanPreset - t = FastPlotTheme('ocean'); + t = FastSenseTheme('ocean'); assert(isequal(t.Background, [1 1 1]), 'testOceanPreset: Background should be white'); assert(isequal(t.AxesColor, [1 1 1]), 'testOceanPreset: AxesColor should be white'); assert(size(t.LineColorOrder, 2) == 3, 'testOceanPreset: LineColorOrder Nx3'); @@ -76,18 +76,18 @@ function test_theme() % testStructAsPreset custom = struct('Background', [0 0 0], 'FontSize', 16); - t = FastPlotTheme(custom); + t = FastSenseTheme(custom); assert(isequal(t.Background, [0 0 0]), 'testStructAsPreset: Background'); assert(t.FontSize == 16, 'testStructAsPreset: FontSize'); assert(isfield(t, 'GridColor'), 'testStructAsPreset: inherits defaults'); % testPaletteResolution - t = FastPlotTheme('default'); + t = FastSenseTheme('default'); assert(size(t.LineColorOrder, 1) >= 6, 'testPaletteResolution: at least 6 colors'); % testCustomPaletteMatrix customColors = [1 0 0; 0 1 0; 0 0 1]; - t = FastPlotTheme('default', 'LineColorOrder', customColors); + t = FastSenseTheme('default', 'LineColorOrder', customColors); assert(isequal(t.LineColorOrder, customColors), 'testCustomPaletteMatrix'); fprintf(' All 12 theme tests passed.\n'); diff --git a/tests/test_toolbar.m b/tests/test_toolbar.m index fc1a765a..20960fb4 100644 --- a/tests/test_toolbar.m +++ b/tests/test_toolbar.m @@ -1,57 +1,57 @@ function test_toolbar() -%TEST_TOOLBAR Tests for FastPlotToolbar class. +%TEST_TOOLBAR Tests for FastSenseToolbar class. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); close all force; drawnow; - % testConstructorWithFastPlot - fp = FastPlot(); + % testConstructorWithFastSense + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); - tb = FastPlotToolbar(fp); - assert(~isempty(tb.hToolbar), 'testConstructorWithFastPlot: hToolbar'); - assert(ishandle(tb.hToolbar), 'testConstructorWithFastPlot: ishandle'); + tb = FastSenseToolbar(fp); + assert(~isempty(tb.hToolbar), 'testConstructorWithFastSense: hToolbar'); + assert(ishandle(tb.hToolbar), 'testConstructorWithFastSense: ishandle'); close(fp.hFigure); - % testConstructorWithFastPlotGrid - fig = FastPlotGrid(1, 2); + % testConstructorWithFastSenseGrid + fig = FastSenseGrid(1, 2); fp1 = fig.tile(1); fp1.addLine(1:100, rand(1,100)); fp2 = fig.tile(2); fp2.addLine(1:100, rand(1,100)); fig.renderAll(); - tb = FastPlotToolbar(fig); + tb = FastSenseToolbar(fig); assert(~isempty(tb.hToolbar), 'testConstructorWithFPFigure: hToolbar'); close(fig.hFigure); % testToolbarHasAllButtons (cursor, crosshair, grid, legend, autoscale, export, refresh, live, metadata, theme) - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); children = get(tb.hToolbar, 'Children'); assert(numel(children) == 11, ... sprintf('testToolbarHasAllButtons: got %d', numel(children))); close(fp.hFigure); % testIconsAre16x16x3 - icons = FastPlotToolbar.makeIcon('grid'); + icons = FastSenseToolbar.makeIcon('grid'); assert(isequal(size(icons), [16 16 3]), 'testIconsAre16x16x3'); % testAllIconNames names = {'cursor', 'crosshair', 'grid', 'legend', 'autoscale', 'export', 'violations'}; for i = 1:numel(names) - icon = FastPlotToolbar.makeIcon(names{i}); + icon = FastSenseToolbar.makeIcon(names{i}); assert(isequal(size(icon), [16 16 3]), ... sprintf('testAllIconNames: %s', names{i})); end % testToggleGrid - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); gridBefore = get(fp.hAxes, 'XGrid'); tb.toggleGrid(); gridAfter = get(fp.hAxes, 'XGrid'); @@ -59,10 +59,10 @@ function test_toolbar() close(fp.hFigure); % testToggleLegend - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100), 'DisplayName', 'TestLine'); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tb.toggleLegend(); hLeg = findobj(fp.hFigure, 'Type', 'axes', 'Tag', 'legend'); if isempty(hLeg) @@ -75,11 +75,11 @@ function test_toolbar() close(fp.hFigure); % testAutoscaleY - fp = FastPlot(); + fp = FastSense(); y = [zeros(1,50), 10*ones(1,50)]; fp.addLine(1:100, y); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); % Zoom into first half (all zeros) set(fp.hAxes, 'XLim', [1 50]); drawnow; @@ -91,10 +91,10 @@ function test_toolbar() close(fp.hFigure); % testExportPNG - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tmpFile = [tempname, '.png']; tb.exportPNG(tmpFile); assert(exist(tmpFile, 'file') == 2, 'testExportPNG: file should exist'); @@ -102,10 +102,10 @@ function test_toolbar() close(fp.hFigure); % testCrosshairMode - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); assert(strcmp(tb.Mode, 'none'), 'testCrosshairMode: initial mode'); tb.setCrosshair(true); assert(strcmp(tb.Mode, 'crosshair'), 'testCrosshairMode: on'); @@ -114,10 +114,10 @@ function test_toolbar() close(fp.hFigure); % testCrosshairMutualExclusion - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tb.setCursor(true); assert(strcmp(tb.Mode, 'cursor'), 'testMutualExcl: cursor on'); tb.setCrosshair(true); @@ -126,10 +126,10 @@ function test_toolbar() close(fp.hFigure); % testCursorMode - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, rand(1,100)); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); tb.setCursor(true); assert(strcmp(tb.Mode, 'cursor'), 'testCursorMode: on'); tb.setCursor(false); @@ -137,21 +137,21 @@ function test_toolbar() close(fp.hFigure); % testSnapToNearest - fp = FastPlot(); + fp = FastSense(); fp.addLine([1 2 3 4 5], [10 20 30 40 50]); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); [sx, sy, ~] = tb.snapToNearest(fp, 2.8, 25); assert(sx == 3, sprintf('testSnapToNearest: x should be 3, got %g', sx)); assert(sy == 30, sprintf('testSnapToNearest: y should be 30, got %g', sy)); close(fp.hFigure); % testViolationsToggle - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:100, [ones(1,50)*2, ones(1,50)*8]); fp.addThreshold(5, 'Direction', 'upper', 'ShowViolations', true); fp.render(); - tb = FastPlotToolbar(fp); + tb = FastSenseToolbar(fp); % Violations should be visible initially assert(fp.ViolationsVisible, 'testViolationsToggle: default true'); hM = fp.Thresholds(1).hMarkers; diff --git a/tests/test_violation_cull_mex.m b/tests/test_violation_cull_mex.m index 8497d87d..157ba7dd 100644 --- a/tests/test_violation_cull_mex.m +++ b/tests/test_violation_cull_mex.m @@ -2,7 +2,7 @@ function test_violation_cull_mex() %TEST_VIOLATION_CULL_MEX Parity tests: MEX vs MATLAB fallback. addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); hasMex = (exist('violation_cull_mex', 'file') == 3); if ~hasMex diff --git a/tests/test_violations_mex_parity.m b/tests/test_violations_mex_parity.m index c10a2950..1049c73d 100644 --- a/tests/test_violations_mex_parity.m +++ b/tests/test_violations_mex_parity.m @@ -100,5 +100,5 @@ function add_sensor_path() repo_root = fileparts(test_dir); addpath(repo_root);setup(); % Add private dirs so MEX is directly accessible for parity testing - addpath(fullfile(repo_root, 'libs', 'FastPlot', 'private')); + addpath(fullfile(repo_root, 'libs', 'FastSense', 'private')); end diff --git a/tests/test_zoom_pan.m b/tests/test_zoom_pan.m index 6c32be5c..166fd55d 100644 --- a/tests/test_zoom_pan.m +++ b/tests/test_zoom_pan.m @@ -3,7 +3,7 @@ function test_zoom_pan() % Requires PostSet listeners (MATLAB only, skipped on Octave). addpath(fullfile(fileparts(mfilename('fullpath')), '..'));setup(); - add_fastplot_private_path(); + add_fastsense_private_path(); if exist('OCTAVE_VERSION', 'builtin') fprintf(' SKIPPED: Octave lacks PostSet listeners for axes properties.\n'); @@ -11,7 +11,7 @@ function test_zoom_pan() end % testZoomUpdatesPlottedData - fp = FastPlot(); + fp = FastSense(); n = 100000; x = linspace(0, 100, n); y = sin(x); @@ -31,7 +31,7 @@ function test_zoom_pan() close(fp.hFigure); % testLazySkipsRedundantUpdate - fp = FastPlot(); + fp = FastSense(); fp.addLine(1:1000, rand(1,1000)); fp.render(); @@ -44,7 +44,7 @@ function test_zoom_pan() close(fp.hFigure); % testViolationsUpdateOnZoom - fp = FastPlot(); + fp = FastSense(); y = [zeros(1,500), ones(1,500)*10, zeros(1,500)]; x = 1:1500; fp.addLine(x, y);