From 6e8182bf661b958278985fe1d44f7e143dfca039 Mon Sep 17 00:00:00 2001 From: Ben Reynolds Date: Wed, 8 Oct 2025 17:37:00 -0400 Subject: [PATCH 1/5] adds first version of timeline snapping, with a button to enable it, and snapping added for steps, VA/NVA, and walking/standing, with a 10px tolerance --- css/index.css | 4 ++ src/humanPose/WalkingLens.js | 4 +- src/motionStudy/TrackSidebar.js | 21 ++++++- src/motionStudy/timeline.js | 98 ++++++++++++++++++++++++++++- src/store/slices/motionStudy.js | 13 ++++ src/store/tests/motionStudy.test.js | 4 ++ svg/motionStudy/magnet.svg | 9 +++ 7 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 svg/motionStudy/magnet.svg diff --git a/css/index.css b/css/index.css index 265e2e06..cf2e4bf1 100755 --- a/css/index.css +++ b/css/index.css @@ -2130,6 +2130,10 @@ Logic Interface CSS background-image: url('../svg/motionStudy/table-view.svg'); } +.analytics-timeline-snapping-button { + background-image: url('../svg/motionStudy/magnet.svg'); +} + .analytics-timeline-selection-row { height: var(--mst-timeline-selection-row-height); font-size: var(--mst-timeline-selection-row-font-size); diff --git a/src/humanPose/WalkingLens.js b/src/humanPose/WalkingLens.js index 30ca9010..13ca495d 100644 --- a/src/humanPose/WalkingLens.js +++ b/src/humanPose/WalkingLens.js @@ -26,6 +26,8 @@ function groundDistMeters(a, b) { return Math.hypot(dx, dz); } +export const WALKING_LENS_NAME = 'Walking vs Standing'; + /** * Step Number lens is a lens that stores the current step number (or 0) at the current timestamp * This assessement is done by a analyst and is marked manually. @@ -36,7 +38,7 @@ export class WalkingLens extends MotionStudyLens { * @param {MotionStudy} motionStudy */ constructor(motionStudy) { - super('Walking vs Standing'); + super(WALKING_LENS_NAME); this.motionStudy = motionStudy; /* sliding window of the latest poses: [{pos: THREE.Vector3, t: ms}, …] */ diff --git a/src/motionStudy/TrackSidebar.js b/src/motionStudy/TrackSidebar.js index 1d415e02..7d64926f 100644 --- a/src/motionStudy/TrackSidebar.js +++ b/src/motionStudy/TrackSidebar.js @@ -1,4 +1,5 @@ import { intents, selectors, store, subscribeSelector } from '../store/index.js'; +import { WALKING_LENS_NAME } from '../humanPose/WalkingLens.js'; export class TrackSidebar { /** @@ -93,9 +94,27 @@ export class TrackSidebar { tableButton.classList.toggle('selected', newValue); }, undefined, { fireImmediately: true }); + // 4. --- snapping button --- toggles magnetic snapping on/off + + let snappingButton = document.createElement('div'); + snappingButton.classList.add('analytics-timeline-header-button'); + let snappingButtonIcon = document.createElement('div'); + snappingButtonIcon.classList.add('analytics-timeline-snapping-button', 'analytics-timeline-header-button-icon'); + snappingButton.appendChild(snappingButtonIcon); + + snappingButton.addEventListener('click', () => { + let isSnappingToggled = selectors.motionStudy.isTimelineSnappingToggled(store.getState()); + store.dispatch(intents.motionStudy.toggleTimelineSnapping(!isSnappingToggled)); + }); + + subscribeSelector(store, selectors.motionStudy.isTimelineSnappingToggled, (newValue) => { + snappingButton.classList.toggle('selected', newValue); + }, undefined, { fireImmediately: true }); + shortcutRow.append(settingsButton); shortcutRow.append(mapButton); shortcutRow.append(tableButton); + shortcutRow.append(snappingButton); return shortcutRow; } @@ -194,7 +213,7 @@ export class TrackSidebar { ergonomicsRow.setAttribute(LENS_NAME_ATTR, 'Muri Ergonomics'); stepsRow.setAttribute(LENS_NAME_ATTR, 'Step Number'); tagRow.setAttribute(LENS_NAME_ATTR, 'Value Add/Waste Time'); - walkingRow.setAttribute(LENS_NAME_ATTR, 'Walking vs Standing'); + walkingRow.setAttribute(LENS_NAME_ATTR, WALKING_LENS_NAME); const rows = [ergonomicsRow, stepsRow, tagRow, walkingRow]; rows[0].classList.add('analytics-timeline-first-track-row'); diff --git a/src/motionStudy/timeline.js b/src/motionStudy/timeline.js index e208ae7a..2f47255a 100644 --- a/src/motionStudy/timeline.js +++ b/src/motionStudy/timeline.js @@ -6,6 +6,7 @@ import { } from './ValueAddWasteTimeManager.js'; import { intents, selectors, store } from '../store/index.js'; import { TrackSidebar } from './TrackSidebar.js'; +import { WALKING_LENS_NAME } from '../humanPose/WalkingLens.js'; // Note: Some of these values propagate into the css variables via the `setCSSVariables` function. @@ -312,7 +313,7 @@ export class Timeline { this.drawPinnedRegionCards(); this.drawPatches(); this.drawValueAddWasteTime(); - this.drawPoses('Walking vs Standing', {insetRect: true}); + this.drawPoses(WALKING_LENS_NAME, {insetRect: true}); this.drawSensors(); this.drawHighlightRegion(); @@ -1681,8 +1682,99 @@ export class Timeline { } } + getSnappedX(mouseX, options = { excludeSelectedStep: true }) { + if (!selectors.motionStudy.isTimelineSnappingToggled(store.getState())) { + return mouseX; // don't snap if the magnet mode is off + } + + // get a list of all the key points and check if the mouseX is within a few pixels of any + const ergonomicsKeyTimes = this.getErgonomicsSnappableTimes(); + const stepKeyTimes = this.getStepSnappableTimes(options); + const valueWasteKeyTimes = this.getValueWasteSnappableTimes(); + const walkStandKeyTimes = this.getWalkStandSnappableTimes(); + const allKeyTimes = [ergonomicsKeyTimes, stepKeyTimes, valueWasteKeyTimes, walkStandKeyTimes].flat(); + if (allKeyTimes.length === 0) return mouseX; + + const allKeyXCoords = allKeyTimes.map(t => this.timeToX(t)); + const closestKeyXCoord = allKeyXCoords.reduce((prev, curr) => Math.abs(curr - mouseX) < Math.abs(prev - mouseX) ? curr : prev); + + // within this number of pixels, snap to the closest key moment - can be tuned if needed + const SNAP_THRESHOLD = 10; + if (Math.abs(closestKeyXCoord - mouseX) < SNAP_THRESHOLD) { + return closestKeyXCoord; + } + + return mouseX; + } + + getErgonomicsSnappableTimes() { + return []; // TODO: detect significant peaks/valleys to snap to + } + + getStepSnappableTimes(options = { excludeSelectedStep: true }) { + // add the start and end times of all step cards + const snappableTimes = []; + for (const prc of this.motionStudy.pinnedRegionCards) { + // use this option to prevent snapping to a step's own endpoints when resizing it + if (prc.displayActive && options.excludeSelectedStep) continue; + snappableTimes.push(prc.startTime); + snappableTimes.push(prc.endTime); + } + return snappableTimes; // might have some duplicates but not a big issue + } + + getWalkStandSnappableTimes() { + let hpa = realityEditor.motionStudy.getActiveHumanPoseAnalyzer(); + const historyLines = hpa.historyLines[WALKING_LENS_NAME]?.all; + if (!historyLines) return []; + const snappableTimes = []; + for (let spaghetti of Object.values(historyLines)) { + const breakpoints = this.getPoseBreakTimestamps(spaghetti.points); + for (let bp of breakpoints) { + snappableTimes.push(bp); + } + } + return snappableTimes; + } + + getValueWasteSnappableTimes() { + const snappableTimes = []; + this.motionStudy.valueAddWasteTimeManager.regions.forEach((region) => { + snappableTimes.push(region.startTime); + snappableTimes.push(region.endTime); + }); + return snappableTimes; + } + + // Returns the start/end times of colored regions in the poses - with similar logic as in drawSpaghettiPoses + getPoseBreakTimestamps(poses) { + if (!poses?.length) return []; + + const maxPoseDelayLenience = 500; // same as drawSpaghettiPoses + const breakTimestamps = []; + + let lastPose = poses[0]; + let lastPoseTime = lastPose.timestamp; + + for (let i = 1; i < poses.length; i++) { + const pose = poses[i]; + const timeGap = pose.timestamp - lastPoseTime; + const isGap = timeGap > maxPoseDelayLenience; + const isColorSwap = !rgbaEquals(pose.originalTimelineColor, lastPose.originalTimelineColor); + + if (isGap || isColorSwap) { + breakTimestamps.push(pose.timestamp); + } + + lastPose = pose; + lastPoseTime = pose.timestamp; + } + + return breakTimestamps; + } + onPointerMoveDragModeMoveCursor(_event) { - let newCursorTime = this.xToTime(this.mouseX); + let newCursorTime = this.xToTime(this.getSnappedX(this.mouseX), { excludeSelectedStep: false }); if (newCursorTime < this.timeMin) { newCursorTime = this.timeMin; } @@ -1707,7 +1799,7 @@ export class Timeline { } this.canvas.style.cursor = 'col-resize'; - let highlightEndTime = this.xToTime(this.mouseX); + let highlightEndTime = this.xToTime(this.getSnappedX(this.mouseX), { excludeSelectedStep: true }); let startTime = Math.min(this.highlightStartTime, highlightEndTime); let endTime = Math.max(this.highlightStartTime, highlightEndTime); diff --git a/src/store/slices/motionStudy.js b/src/store/slices/motionStudy.js index c3469171..b9302700 100644 --- a/src/store/slices/motionStudy.js +++ b/src/store/slices/motionStudy.js @@ -13,6 +13,7 @@ export const types = { // various check-box settings from the settings menu: TOGGLE_PATH_MAP: 'motionStudy/TOGGLE_PATH_MAP', TOGGLE_TABLE_VIEW: 'motionStudy/TOGGLE_TABLE_VIEW', + TOGGLE_TIMELINE_SNAPPING: 'motionStudy/TOGGLE_TIMELINE_SNAPPING', TOGGLE_CHILD_HUMAN_OBJECTS: 'motionStudy/TOGGLE_CHILD_HUMAN_OBJECTS', TOGGLE_LIVE_HISTORY_LINES: 'motionStudy/TOGGLE_LIVE_HISTORY_LINES', TOGGLE_FILTER_UNRELIABLE_JOINTS: 'motionStudy/TOGGLE_FILTER_UNRELIABLE_JOINTS', @@ -38,6 +39,7 @@ export const motionStudyInitial = { // various check-box settings from the settings menu: isPathMapToggled: false, // doesn't say for which tool - derive that from the focused tool isTableViewToggled: false, // doesn't say for which tool - derive that from the focused tool + isTimelineSnappingToggled: false, // if true, dragging the timeline will snap to significant points areChildHumanObjectsVisible: false, // auxiliary human objects supporting fused human objects areLiveHistoryLinesVisible: false, jointConfidenceThreshold: JOINT_CONFIDENCE_THRESHOLD, // toggles to 0 if "Filter unreliable joints" turned off @@ -61,6 +63,7 @@ export const actions = { // various check-box settings from the settings menu: togglePathMap: (newIsToggled) => ({ type: types.TOGGLE_PATH_MAP, payload: { value: newIsToggled } }), toggleTableView: (newIsToggled) => ({ type: types.TOGGLE_TABLE_VIEW, payload: { value: newIsToggled } }), + toggleTimelineSnapping: (newIsToggled) => ({ type: types.TOGGLE_TIMELINE_SNAPPING, payload: { value: newIsToggled } }), toggleChildHumanObjectsVisible: (newIsToggled) => ({ type: types.TOGGLE_CHILD_HUMAN_OBJECTS, payload: { value: newIsToggled } }), toggleLiveHistoryLinesVisible: (newIsToggled) => ({ type: types.TOGGLE_LIVE_HISTORY_LINES, payload: { value: newIsToggled } }), toggleFilterUnreliableJoints: (newIsToggled) => ({ type: types.TOGGLE_FILTER_UNRELIABLE_JOINTS, payload: { value: newIsToggled } }), @@ -89,6 +92,8 @@ export function motionStudyReducer(state = motionStudyInitial, action) { return { ...state, isPathMapToggled: action.payload.value }; case types.TOGGLE_TABLE_VIEW: return { ...state, isTableViewToggled: action.payload.value }; + case types.TOGGLE_TIMELINE_SNAPPING: + return { ...state, isTimelineSnappingToggled: action.payload.value }; case types.EXPAND_SETTINGS_MENU: return { ...state, isSettingsMenuExpanded: action.payload.value }; case types.ALWAYS_SHOW_ANALYTICS_MENU: @@ -142,6 +147,7 @@ function makeSelectors(_sliceName, { slice }) { const currentLensName = (s) => root(s)?.currentLensName ?? null; const isPathMapToggled = (s) => root(s)?.isPathMapToggled ?? false; const isTableViewToggled = (s) => root(s)?.isTableViewToggled ?? false; + const isTimelineSnappingToggled = (s) => root(s)?.isTimelineSnappingToggled ?? false; const liveHistoryLastResetTimestamp = (s) => root(s)?.liveHistoryLastResetTimestamp ?? null; const isSettingsMenuExpanded = (s) => root(s)?.isSettingsMenuExpanded ?? false; const isSettingsMenuAlwaysInBar = (s) => root(s)?.isSettingsMenuAlwaysInBar ?? false; @@ -171,6 +177,7 @@ function makeSelectors(_sliceName, { slice }) { currentLensName, isPathMapToggled, isTableViewToggled, + isTimelineSnappingToggled, liveHistoryLastResetTimestamp, isSettingsMenuExpanded, isSettingsMenuAlwaysInBar, @@ -240,6 +247,12 @@ function makeIntents(_sliceName, /* { actions } */) { if (newIsToggled === prevToggled) return; dispatch(actions.toggleTableView(newIsToggled)); }, + toggleTimelineSnapping: (newIsToggled) => (dispatch, getState) => { + const s = getState(); + const prevToggled = s.motionStudy.isTimelineSnappingToggled; + if (newIsToggled === prevToggled) return; + dispatch(actions.toggleTimelineSnapping(newIsToggled)); + }, toggleChildHumanObjectsVisible: (newIsToggled) => (dispatch, getState) => { const s = getState(); const prevToggled = s.motionStudy.areChildHumanObjectsVisible; diff --git a/src/store/tests/motionStudy.test.js b/src/store/tests/motionStudy.test.js index 0eb374b7..54ce0fd7 100644 --- a/src/store/tests/motionStudy.test.js +++ b/src/store/tests/motionStudy.test.js @@ -50,6 +50,9 @@ describe('motionStudy reducer: core ops', () => { s = motionStudyReducer(s, motionStudyActions.toggleTableView(true)); expect(s.isTableViewToggled).toBe(true); + s = motionStudyReducer(s, motionStudyActions.toggleTimelineSnapping(true)); + expect(s.isTimelineSnappingToggled).toBe(true); + s = motionStudyReducer(s, motionStudyActions.toggleChildHumanObjectsVisible(true)); expect(s.areChildHumanObjectsVisible).toBe(true); @@ -121,6 +124,7 @@ describe('motionStudy selectors', () => { expect(selectors.currentLensName(base)).toBe('Muri Ergonomics'); expect(selectors.isPathMapToggled(base)).toBe(false); expect(selectors.isTableViewToggled(base)).toBe(false); + expect(selectors.isTimelineSnappingToggled(base)).toBe(false); expect(selectors.isSettingsMenuExpanded(base)).toBe(false); expect(selectors.isSettingsMenuAlwaysInBar(base)).toBe(false); expect(selectors.areChildHumanObjectsVisible(base)).toBe(false); diff --git a/svg/motionStudy/magnet.svg b/svg/motionStudy/magnet.svg new file mode 100644 index 00000000..66fa74d5 --- /dev/null +++ b/svg/motionStudy/magnet.svg @@ -0,0 +1,9 @@ + + + + + + + + + From 34abd8400270cbe9f78c2a0dad76b3ff4567dfc5 Mon Sep 17 00:00:00 2001 From: Ben Reynolds Date: Thu, 9 Oct 2025 10:18:28 -0400 Subject: [PATCH 2/5] calculate walk/stand breakpoints in drawSpaghettiPoses instead of duplicating logic --- src/motionStudy/timeline.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/motionStudy/timeline.js b/src/motionStudy/timeline.js index 2f47255a..4010cb85 100644 --- a/src/motionStudy/timeline.js +++ b/src/motionStudy/timeline.js @@ -142,6 +142,7 @@ export class Timeline { this.highlightStartTime = -1; this.regionCard = null; this.lastRegionCardCacheKey = ''; + this.lastBreakTimeStampsPerLens = {}; this.dragMode = DragMode.NONE; this.mouseX = -1; @@ -313,7 +314,7 @@ export class Timeline { this.drawPinnedRegionCards(); this.drawPatches(); this.drawValueAddWasteTime(); - this.drawPoses(WALKING_LENS_NAME, {insetRect: true}); + this.drawPoses(WALKING_LENS_NAME, { insetRect: true, calculateBreakPoints: true }); this.drawSensors(); this.drawHighlightRegion(); @@ -623,7 +624,7 @@ export class Timeline { } } - drawPoses(lensName, options = {insetRect: false}) { + drawPoses(lensName, options = { insetRect: false, calculateBreakPoints: false }) { let hpa = realityEditor.motionStudy.getActiveHumanPoseAnalyzer(); if (!lensName) { lensName = hpa.activeLens.name; @@ -634,7 +635,7 @@ export class Timeline { return; } for (let spaghetti of Object.values(historyLines)) { - this.drawSpaghettiPoses(spaghetti.points, options); + this.drawSpaghettiPoses(spaghetti.points, lensName, options); } this.rowIndex += 1; } @@ -663,7 +664,7 @@ export class Timeline { return [rgba[0] * dim, rgba[1] * dim, rgba[2] * dim, rgba[3] * dim]; } - drawSpaghettiPoses(poses, options) { + drawSpaghettiPoses(poses, lensName, options) { let lastPose = poses[0]; let lastPoseTime = lastPose.timestamp; let startSectionTime = lastPoseTime; @@ -674,6 +675,10 @@ export class Timeline { const timeMax = this.timeMin + this.widthMs; + if (options.calculateBreakPoints) { + this.lastBreakTimeStampsPerLens[lensName] = []; + } + for (const pose of poses) { if (pose.timestamp < this.timeMin) { startSectionTime = pose.timestamp; @@ -688,6 +693,13 @@ export class Timeline { const poseColor = this.recolorPoseForHighlight(pose.timestamp, pose.originalTimelineColor); const lastPoseColor = this.recolorPoseForHighlight(lastPose.timestamp, lastPose.originalTimelineColor); const isColorSwap = !rgbaEquals(poseColor, lastPoseColor); + + // A gap or a color swap is a "snappable" moment + const isColorSwapIgnoringHighlight = !rgbaEquals(pose.originalTimelineColor, lastPose.originalTimelineColor); + if (options.calculateBreakPoints && (isGap || isColorSwapIgnoringHighlight)) { + this.lastBreakTimeStampsPerLens[lensName].push(lastPoseTime); + } + if (!isGap && !isColorSwap) { lastPose = pose; lastPoseTime = lastPose.timestamp; @@ -1724,17 +1736,7 @@ export class Timeline { } getWalkStandSnappableTimes() { - let hpa = realityEditor.motionStudy.getActiveHumanPoseAnalyzer(); - const historyLines = hpa.historyLines[WALKING_LENS_NAME]?.all; - if (!historyLines) return []; - const snappableTimes = []; - for (let spaghetti of Object.values(historyLines)) { - const breakpoints = this.getPoseBreakTimestamps(spaghetti.points); - for (let bp of breakpoints) { - snappableTimes.push(bp); - } - } - return snappableTimes; + return this.lastBreakTimeStampsPerLens[WALKING_LENS_NAME] ?? []; } getValueWasteSnappableTimes() { From 620e09170736a4fdf90d30238812019e126df347 Mon Sep 17 00:00:00 2001 From: Ben Reynolds Date: Thu, 9 Oct 2025 12:34:05 -0400 Subject: [PATCH 3/5] fix snapping rounding error in start needle, and snap to beginnind and end of entire timeline --- src/motionStudy/timeline.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/motionStudy/timeline.js b/src/motionStudy/timeline.js index 4010cb85..db80d75a 100644 --- a/src/motionStudy/timeline.js +++ b/src/motionStudy/timeline.js @@ -1700,11 +1700,18 @@ export class Timeline { } // get a list of all the key points and check if the mouseX is within a few pixels of any + const startEndTimes = this.getStartEndSnappableTimes(); const ergonomicsKeyTimes = this.getErgonomicsSnappableTimes(); const stepKeyTimes = this.getStepSnappableTimes(options); const valueWasteKeyTimes = this.getValueWasteSnappableTimes(); const walkStandKeyTimes = this.getWalkStandSnappableTimes(); - const allKeyTimes = [ergonomicsKeyTimes, stepKeyTimes, valueWasteKeyTimes, walkStandKeyTimes].flat(); + const allKeyTimes = [ + startEndTimes, + ergonomicsKeyTimes, + stepKeyTimes, + valueWasteKeyTimes, + walkStandKeyTimes + ].flat(); if (allKeyTimes.length === 0) return mouseX; const allKeyXCoords = allKeyTimes.map(t => this.timeToX(t)); @@ -1719,6 +1726,11 @@ export class Timeline { return mouseX; } + getStartEndSnappableTimes() { + // the start and end of the timeline are always snappable + return [this.minTimeMin, this.maxTimeMax]; + } + getErgonomicsSnappableTimes() { return []; // TODO: detect significant peaks/valleys to snap to } @@ -1804,6 +1816,9 @@ export class Timeline { let highlightEndTime = this.xToTime(this.getSnappedX(this.mouseX), { excludeSelectedStep: true }); let startTime = Math.min(this.highlightStartTime, highlightEndTime); + if (selectors.motionStudy.isTimelineSnappingToggled(store.getState())) { + startTime += 1; // fix rounding error when snapping start needle + } let endTime = Math.max(this.highlightStartTime, highlightEndTime); this.motionStudy.setHighlightRegion({ startTime, From 51271996c9badb5a31c3ab30fa4ea98ace12d1bb Mon Sep 17 00:00:00 2001 From: Ben Reynolds Date: Thu, 9 Oct 2025 12:48:24 -0400 Subject: [PATCH 4/5] adds snap point at the end of a gap in addition to the one at the start of the gap --- src/motionStudy/timeline.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/motionStudy/timeline.js b/src/motionStudy/timeline.js index db80d75a..f8b4ce4c 100644 --- a/src/motionStudy/timeline.js +++ b/src/motionStudy/timeline.js @@ -697,7 +697,15 @@ export class Timeline { // A gap or a color swap is a "snappable" moment const isColorSwapIgnoringHighlight = !rgbaEquals(pose.originalTimelineColor, lastPose.originalTimelineColor); if (options.calculateBreakPoints && (isGap || isColorSwapIgnoringHighlight)) { - this.lastBreakTimeStampsPerLens[lensName].push(lastPoseTime); + if (isGap) { + // this will add the last point before a gap + this.lastBreakTimeStampsPerLens[lensName].push(lastPoseTime); + // this will add the first point after a gap + this.lastBreakTimeStampsPerLens[lensName].push(pose.timestamp); + } else { + // this adds the color swap point + this.lastBreakTimeStampsPerLens[lensName].push(lastPoseTime); + } } if (!isGap && !isColorSwap) { @@ -1817,7 +1825,7 @@ export class Timeline { let startTime = Math.min(this.highlightStartTime, highlightEndTime); if (selectors.motionStudy.isTimelineSnappingToggled(store.getState())) { - startTime += 1; // fix rounding error when snapping start needle + startTime += 1; // fix rounding error when snapping start needle, to render correctly on timeline } let endTime = Math.max(this.highlightStartTime, highlightEndTime); this.motionStudy.setHighlightRegion({ From 678c06b88c0facc5a788dc69f5ee1cf1ea1a79ff Mon Sep 17 00:00:00 2001 From: Ben Reynolds Date: Thu, 9 Oct 2025 13:20:03 -0400 Subject: [PATCH 5/5] remove unused pose breakpoint function --- src/motionStudy/timeline.js | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/motionStudy/timeline.js b/src/motionStudy/timeline.js index f8b4ce4c..47a5a691 100644 --- a/src/motionStudy/timeline.js +++ b/src/motionStudy/timeline.js @@ -1768,33 +1768,6 @@ export class Timeline { return snappableTimes; } - // Returns the start/end times of colored regions in the poses - with similar logic as in drawSpaghettiPoses - getPoseBreakTimestamps(poses) { - if (!poses?.length) return []; - - const maxPoseDelayLenience = 500; // same as drawSpaghettiPoses - const breakTimestamps = []; - - let lastPose = poses[0]; - let lastPoseTime = lastPose.timestamp; - - for (let i = 1; i < poses.length; i++) { - const pose = poses[i]; - const timeGap = pose.timestamp - lastPoseTime; - const isGap = timeGap > maxPoseDelayLenience; - const isColorSwap = !rgbaEquals(pose.originalTimelineColor, lastPose.originalTimelineColor); - - if (isGap || isColorSwap) { - breakTimestamps.push(pose.timestamp); - } - - lastPose = pose; - lastPoseTime = pose.timestamp; - } - - return breakTimestamps; - } - onPointerMoveDragModeMoveCursor(_event) { let newCursorTime = this.xToTime(this.getSnappedX(this.mouseX), { excludeSelectedStep: false }); if (newCursorTime < this.timeMin) {