label-studio icon indicating copy to clipboard operation
label-studio copied to clipboard

fix: Snap TimeSeries seek events to nearest data point to eliminate floating-point precision errors

Open cloudmark opened this issue 3 months ago • 8 comments

Reason for Change

Problem: When clicking on a TimeSeries visualization, seek events emit inaccurate time values that don't match the clicked data point due to floating-point precision errors in pixel-to-time conversion calculations.

For example:

  • Clicking on data point 1770.677344 emits seek event 1770.6719467913167
  • Clicking on data point 1733.719 emits seek event 1733.72516

These incorrect values don't exist in the original dataset, causing:

  • Synchronization issues with audio/video players
  • Annotation accuracy problems in time-based labeling tasks
  • Confusion between displayed values and actual seek positions

Root Cause: The pixel-to-time conversion uses continuous interpolation with multiple floating-point operations that accumulate rounding errors:

const timeClicked = brushTimeStartNative + (clickX / plottingAreaWidth) * brushDurationNative;

Solution: Implemented data point snapping using binary search (O(log n)) to find and return the nearest actual data point from the dataset instead of using interpolated values. Created a shared snapToNearestDataPoint() helper function to eliminate code duplication across three locations.


Videos

  • Bug demonstration: https://www.loom.com/share/5f1f429a21f0438ca5f11e7146570bfe
  • Fix demonstration: https://www.loom.com/share/b1b2b9ea3230461eb6e58848c40edfe2

Rollout Strategy

No special rollout needed - This is a bug fix with backward compatibility:

  • No feature flags required
  • No environment variables needed
  • No API changes
  • Existing TimeSeries functionality is preserved and improved
  • Change applies automatically to all TimeSeries visualizations

Testing

Manual Testing Performed:

  1. Basic Click Test

    • Clicked on various data points in TimeSeries visualization
    • Verified seek event values now match exact data point values (see fix demo video)
    • Tested with both numeric and date-formatted time columns
  2. Edge Cases

    • Clicks at the very start/end of timeseries
    • Clicks between two closely-spaced data points
    • Sparse data (few points)
    • Dense data (many points)
  3. Synchronization Test

    • TimeSeries synced with Audio using sync attribute
    • Verified audio seeks to exact time when clicking TimeSeries
    • No drift or offset between components
  4. MultiChannel Test

    • Tested with MultiChannel TimeSeries visualization
    • Verified plotClickHandler works correctly with snapping

Code Quality:

  • No linting errors introduced
  • Follows existing code patterns and conventions
  • Well-documented with JSDoc comments

Risks

Risk Level: LOW

Performance:

  • Binary search is O(log n) - efficient even for large datasets
  • No performance degradation compared to original implementation
  • Actually improved by eliminating ~135 lines of duplicated code

Backward Compatibility:

  • No breaking changes
  • Snapping to nearest data point is expected and desired behavior
  • Fallback to original value if data is unavailable
  • All existing TimeSeries functionality preserved

Security:

  • No security implications
  • No new dependencies added
  • No changes to data handling or storage

Reviewer Notes

Key Points to Review:

  1. Helper Function (web/libs/editor/src/tags/object/TimeSeries/helpers.js)

    • New snapToNearestDataPoint() function uses binary search
    • Handles edge cases (null data, empty arrays)
    • Well-documented with JSDoc
  2. Three Refactored Locations (web/libs/editor/src/tags/object/TimeSeries.jsx)

    • emitSeekSync() - Line ~1164
    • plotClickHandler() - Line ~1189
    • handleMainAreaClick() - Line ~1650
    • All now use the shared helper instead of duplicated logic
  3. Code Quality Improvements:

    • Eliminated ~135 lines of duplicated binary search logic
    • Single source of truth for snapping behavior
    • Consistent behavior across all interaction points

Testing the Fix:

  • Watch the fix demonstration video to see the exact values now match
  • Load any TimeSeries task and click on data points
  • Observe that seek events emit exact data point values from the dataset

General Notes

Implementation Details:

The snapToNearestDataPoint() helper function:

  • Takes a calculated time value and the array of actual data points
  • Uses binary search to efficiently find the closest data point
  • Checks adjacent points to ensure absolute closest match
  • Returns exact data point value from the dataset

Files Changed:

  1. web/libs/editor/src/tags/object/TimeSeries/helpers.js - Added shared helper function
  2. web/libs/editor/src/tags/object/TimeSeries.jsx - Refactored three locations to use helper

Benefits:

  • Seek events now emit exact data point values
  • Eliminates floating-point precision errors
  • Improves synchronization accuracy with video/audio
  • Better annotation precision
  • Reduced code duplication
  • Easier to maintain and test

No Migration Required - This is a transparent bug fix that improves accuracy without requiring any user action or configuration changes.


Related Issues

This fix addresses floating-point precision errors that may have affected:

  • TimeSeries synchronization with Audio/Video players
  • Annotation accuracy in time-based labeling tasks
  • TimeSeries region creation precision

cloudmark avatar Oct 08 '25 08:10 cloudmark

Deploy request for heartex-docs pending review.

Visit the deploys page to approve it

Name Link
Latest commit 2bdc2b9a59c477e2e94ba33df4edc5294f2aae9d

netlify[bot] avatar Oct 08 '25 08:10 netlify[bot]

Deploy request for label-studio-docs-new-theme pending review.

Visit the deploys page to approve it

Name Link
Latest commit 2bdc2b9a59c477e2e94ba33df4edc5294f2aae9d

netlify[bot] avatar Oct 08 '25 08:10 netlify[bot]

Deploy Preview for label-studio-storybook ready!

Name Link
Latest commit 2bdc2b9a59c477e2e94ba33df4edc5294f2aae9d
Latest deploy log https://app.netlify.com/projects/label-studio-storybook/deploys/68f0bde3eeefd600071cc07e
Deploy Preview https://deploy-preview-8602--label-studio-storybook.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

netlify[bot] avatar Oct 08 '25 08:10 netlify[bot]

Deploy Preview for label-studio-playground ready!

Name Link
Latest commit 2bdc2b9a59c477e2e94ba33df4edc5294f2aae9d
Latest deploy log https://app.netlify.com/projects/label-studio-playground/deploys/68f0bde3485ecd0008918dbd
Deploy Preview https://deploy-preview-8602--label-studio-playground.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

netlify[bot] avatar Oct 08 '25 08:10 netlify[bot]

/jira create

Workflow run Jira issue TRIAG-1675 is created

yyassi-heartex avatar Oct 10 '25 18:10 yyassi-heartex

/git merge

Workflow run Successfully merged: create mode 100644 web/libs/core/src/providers/api-provider.tsx

yyassi-heartex avatar Oct 10 '25 18:10 yyassi-heartex

/git merge

Workflow run Successfully merged: create mode 100644 web/libs/core/src/providers/api-provider.tsx

yyassi-heartex avatar Oct 16 '25 09:10 yyassi-heartex

This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.

robot-ci-heartex avatar Dec 01 '25 02:12 robot-ci-heartex

This PR was closed because it has been stalled for 10 days with no activity.

robot-ci-heartex avatar Dec 12 '25 02:12 robot-ci-heartex