Add a getKeyframes method to Sequence (#426)

This commit is contained in:
Adam Krebs 2023-07-24 06:26:19 -04:00 committed by GitHub
parent 35fe1c375c
commit 4db16f1a7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 123 additions and 0 deletions

View file

@ -0,0 +1,62 @@
/*
* @jest-environment jsdom
*/
import {setupTestSheet} from '@theatre/shared/testUtils'
import {encodePathToProp} from '@theatre/shared/utils/addresses'
import {asKeyframeId, asSequenceTrackId} from '@theatre/shared/utils/ids'
import type {ObjectAddressKey, SequenceTrackId} from '@theatre/shared/utils/ids'
describe(`Sequence`, () => {
test('sequence.getKeyframesOfSimpleProp()', async () => {
const {objPublicAPI, sheet} = await setupTestSheet({
staticOverrides: {
byObject: {},
},
sequence: {
type: 'PositionalSequence',
length: 20,
subUnitsPerUnit: 30,
tracksByObject: {
['obj' as ObjectAddressKey]: {
trackIdByPropPath: {
[encodePathToProp(['position', 'y'])]: asSequenceTrackId('1'),
},
trackData: {
['1' as SequenceTrackId]: {
type: 'BasicKeyframedTrack',
keyframes: [
{
id: asKeyframeId('0'),
position: 10,
connectedRight: true,
handles: [0.5, 0.5, 0.5, 0.5],
type: 'bezier',
value: 3,
},
{
id: asKeyframeId('1'),
position: 20,
connectedRight: false,
handles: [0.5, 0.5, 0.5, 0.5],
type: 'bezier',
value: 6,
},
],
},
},
},
},
},
})
const seq = sheet.publicApi.sequence
const keyframes = seq.__experimental_getKeyframes(
objPublicAPI.props.position.y,
)
expect(keyframes).toHaveLength(2)
expect(keyframes[0].value).toEqual(3)
expect(keyframes[1].value).toEqual(6)
expect(keyframes[0].position).toEqual(10)
})
})

View file

@ -1,5 +1,6 @@
import type Project from '@theatre/core/projects/Project' import type Project from '@theatre/core/projects/Project'
import type Sheet from '@theatre/core/sheets/Sheet' import type Sheet from '@theatre/core/sheets/Sheet'
import {encodePathToProp} from '@theatre/shared/utils/addresses'
import type {SequenceAddress} from '@theatre/shared/utils/addresses' import type {SequenceAddress} from '@theatre/shared/utils/addresses'
import didYouMean from '@theatre/shared/utils/didYouMean' import didYouMean from '@theatre/shared/utils/didYouMean'
import {InvalidArgumentError} from '@theatre/shared/utils/errors' import {InvalidArgumentError} from '@theatre/shared/utils/errors'
@ -20,10 +21,12 @@ import type {
} from './playbackControllers/DefaultPlaybackController' } from './playbackControllers/DefaultPlaybackController'
import DefaultPlaybackController from './playbackControllers/DefaultPlaybackController' import DefaultPlaybackController from './playbackControllers/DefaultPlaybackController'
import TheatreSequence from './TheatreSequence' import TheatreSequence from './TheatreSequence'
import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic'
import type {ILogger} from '@theatre/shared/logger' import type {ILogger} from '@theatre/shared/logger'
import type {ISequence} from '..' import type {ISequence} from '..'
import {notify} from '@theatre/shared/notify' import {notify} from '@theatre/shared/notify'
import type {$IntentionalAny} from '@theatre/dataverse/src/types' import type {$IntentionalAny} from '@theatre/dataverse/src/types'
import {isSheetObject} from '@theatre/shared/instanceTypes'
export type IPlaybackRange = [from: number, to: number] export type IPlaybackRange = [from: number, to: number]
@ -114,6 +117,46 @@ export default class Sequence implements PointerToPrismProvider {
} }
} }
/**
* Takes a pointer to a property of a SheetObject and returns the keyframes of that property.
*
* Theoretically, this method can be called from inside a prism so it can be reactive.
*/
getKeyframesOfSimpleProp<V>(prop: Pointer<any>): Keyframe[] {
const {path, root} = getPointerParts(prop)
if (!isSheetObject(root)) {
throw new InvalidArgumentError(
'Argument prop must be a pointer to a SheetObject property',
)
}
const trackP = val(
this._project.pointers.historic.sheetsById[this._sheet.address.sheetId]
.sequence.tracksByObject[root.address.objectKey],
)
if (!trackP) {
return []
}
const {trackData, trackIdByPropPath} = trackP
const objectAddress = encodePathToProp(path)
const id = trackIdByPropPath[objectAddress]
if (!id) {
return []
}
const track = trackData[id]
if (!track) {
return []
}
return track.keyframes
}
get positionFormatter(): ISequencePositionFormatter { get positionFormatter(): ISequencePositionFormatter {
return this._positionFormatterD.getValue() return this._positionFormatterD.getValue()
} }

View file

@ -2,6 +2,7 @@ import {privateAPI, setPrivateAPI} from '@theatre/core/privateAPIs'
import {defer} from '@theatre/shared/utils/defer' import {defer} from '@theatre/shared/utils/defer'
import type Sequence from './Sequence' import type Sequence from './Sequence'
import type {IPlaybackDirection, IPlaybackRange} from './Sequence' import type {IPlaybackDirection, IPlaybackRange} from './Sequence'
import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic'
import AudioPlaybackController from './playbackControllers/AudioPlaybackController' import AudioPlaybackController from './playbackControllers/AudioPlaybackController'
import {getCoreTicker} from '@theatre/core/coreTicker' import {getCoreTicker} from '@theatre/core/coreTicker'
import type {Pointer} from '@theatre/dataverse' import type {Pointer} from '@theatre/dataverse'
@ -133,6 +134,19 @@ export interface ISequence {
position: number position: number
}> }>
/**
* Given a property, returns a list of keyframes that affect that property.
*
* @example
* Usage:
* ```ts
* // let's assume `sheet` is a sheet and obj is one of its objects
* const keyframes = sheet.sequence.__experimental_getKeyframes(obj.pointer.x)
* console.log(keyframes) // an array of keyframes
* ```
*/
__experimental_getKeyframes(prop: Pointer<{}>): Keyframe[]
/** /**
* Attaches an audio source to the sequence. Playing the sequence automatically * Attaches an audio source to the sequence. Playing the sequence automatically
* plays the audio source and their times are kept in sync. * plays the audio source and their times are kept in sync.
@ -291,6 +305,10 @@ export default class TheatreSequence implements ISequence {
privateAPI(this).position = position privateAPI(this).position = position
} }
__experimental_getKeyframes(prop: Pointer<any>): Keyframe[] {
return privateAPI(this).getKeyframesOfSimpleProp(prop)
}
async attachAudio(args: IAttachAudioArgs): Promise<{ async attachAudio(args: IAttachAudioArgs): Promise<{
decodedBuffer: AudioBuffer decodedBuffer: AudioBuffer
audioContext: AudioContext audioContext: AudioContext