Add a getKeyframes method to Sequence (#426)
This commit is contained in:
parent
35fe1c375c
commit
4db16f1a7e
3 changed files with 123 additions and 0 deletions
62
theatre/core/src/sequences/Sequence.test.ts
Normal file
62
theatre/core/src/sequences/Sequence.test.ts
Normal 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)
|
||||||
|
})
|
||||||
|
})
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue