add experimental keyframe functions
This commit is contained in:
parent
7874d3c291
commit
78a67ee665
3 changed files with 488 additions and 4 deletions
|
@ -1,13 +1,26 @@
|
|||
import get from 'lodash-es/get'
|
||||
import type {IProject, IRafDriver, ISheet, ISheetObject} from '@theatre/core'
|
||||
import type {Prism, Pointer} from '@theatre/dataverse'
|
||||
import {prism} from '@theatre/dataverse'
|
||||
import {getPointerParts, prism, val} from '@theatre/dataverse'
|
||||
import SimpleCache from '@theatre/shared/utils/SimpleCache'
|
||||
import type {$IntentionalAny, VoidFn} from '@theatre/shared/utils/types'
|
||||
import {
|
||||
getPropConfigByPath,
|
||||
isPropConfigComposite,
|
||||
iteratePropType,
|
||||
} from '@theatre/shared/propTypes/utils'
|
||||
import type {PropTypeConfig} from '@theatre/core/propTypes'
|
||||
import type {PathToProp} from '@theatre/shared/src/utils/addresses'
|
||||
import type {KeyframeId, SequenceTrackId} from '@theatre/shared/utils/ids'
|
||||
import pointerDeep from '@theatre/shared/utils/pointerDeep'
|
||||
import forEachPropDeep from '@theatre/shared/utils/forEachDeep'
|
||||
import getDeep from '@theatre/shared/utils/getDeep'
|
||||
import type {$FixMe, $IntentionalAny, VoidFn} from '@theatre/shared/utils/types'
|
||||
import type {IScrub} from '@theatre/studio/Scrub'
|
||||
import type {Studio} from '@theatre/studio/Studio'
|
||||
import {
|
||||
isSheetObjectPublicAPI,
|
||||
isSheetPublicAPI,
|
||||
isSheetObject,
|
||||
} from '@theatre/shared/instanceTypes'
|
||||
import {getOutlineSelection} from './selectors'
|
||||
import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
|
||||
|
@ -78,6 +91,18 @@ export interface ITransactionAPI {
|
|||
* Makes Theatre forget about this sheet.
|
||||
*/
|
||||
__experimental_forgetSheet(sheet: TheatreSheet): void
|
||||
|
||||
__experimental_sequenceProp<V>(pointer: Pointer<V>): void
|
||||
__experimental_staticProp<V>(pointer: Pointer<V>): void
|
||||
__experimental_deleteKeyframes<V>(
|
||||
pointer: Pointer<V>,
|
||||
from: number,
|
||||
to: number,
|
||||
): void
|
||||
__experimental_addKeyframes<V>(pointer: Pointer<V>, keyframes: []): void
|
||||
// transaction
|
||||
//
|
||||
//__experimental_setSequenceDuration<V>(pointer: Pointer<V>, duration: number): void
|
||||
}
|
||||
/**
|
||||
*
|
||||
|
@ -462,6 +487,14 @@ export interface IStudio {
|
|||
__experimental_createContentOfSaveFileTyped(
|
||||
projectId: string,
|
||||
): __UNSTABLE_Project_OnDiskState
|
||||
|
||||
__experimental_setPropAsSequenced<V>(prop: Pointer<V>): void
|
||||
__experimental_setPropAsStatic<V>(prop: Pointer<V>): void
|
||||
__experimental_isPropSequenced<V>(prop: Pointer<V>): boolean
|
||||
|
||||
// __experimental
|
||||
//
|
||||
//__experimental_setSequenceDuration<V>(prop: Pointer<V>, duration: number): boolean
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,6 +540,97 @@ export default class TheatreStudio implements IStudio {
|
|||
): __UNSTABLE_Project_OnDiskState {
|
||||
return getStudio().createContentOfSaveFile(projectId) as $IntentionalAny
|
||||
},
|
||||
// WARNING: for some reason this is not immediately applied
|
||||
// but needs a timeout.. urgh
|
||||
__experimental_setPropAsSequenced<V>(prop: Pointer<V>): void {
|
||||
const {path, root} = getPointerParts(prop)
|
||||
if (!isSheetObject(root)) {
|
||||
throw new Error(
|
||||
'Argument prop must be a pointer to a SheetObject property',
|
||||
)
|
||||
}
|
||||
|
||||
const propAddress = {...root.address, pathToProp: path}
|
||||
const propConfig = getPropConfigByPath(root.template.staticConfig, path)
|
||||
if (typeof propConfig !== 'undefined')
|
||||
getStudio()!.transaction(({stateEditors}) => {
|
||||
stateEditors.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsSequenced(
|
||||
propAddress,
|
||||
propConfig,
|
||||
)
|
||||
console.log('inner setPropAsSequenced', performance.now())
|
||||
})
|
||||
},
|
||||
__experimental_setPropAsStatic<V>(prop: Pointer<V>): void {
|
||||
const {path, root} = getPointerParts(prop)
|
||||
if (!isSheetObject(root)) {
|
||||
throw new Error(
|
||||
'Argument prop must be a pointer to a SheetObject property',
|
||||
)
|
||||
}
|
||||
|
||||
const propAddress = {...root.address, pathToProp: path}
|
||||
const propConfig = getPropConfigByPath(root.template.staticConfig, path)
|
||||
if (typeof propConfig !== 'undefined') {
|
||||
for (const {path: subPath, conf} of iteratePropType(propConfig, [])) {
|
||||
if (isPropConfigComposite(conf)) continue
|
||||
getStudio()!.transaction(({stateEditors}) => {
|
||||
const pointerToSub = pointerDeep(prop, subPath)
|
||||
|
||||
stateEditors.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsStatic(
|
||||
{
|
||||
...propAddress,
|
||||
value: root.getValueByPointer(pointerToSub as $IntentionalAny),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
__experimental_isPropSequenced<V>(prop: Pointer<V>): boolean {
|
||||
const {path, root} = getPointerParts(prop)
|
||||
if (!isSheetObject(root)) {
|
||||
throw new Error(
|
||||
'Argument prop must be a pointer to a SheetObject property',
|
||||
)
|
||||
}
|
||||
|
||||
const propAddress = {...root.address, pathToProp: path}
|
||||
const propConfig = getPropConfigByPath(root.template.staticConfig, path)
|
||||
const validTracks = root.template
|
||||
.getArrayOfValidSequenceTracks()
|
||||
.getValue()
|
||||
tf: for (let t = 0; t < validTracks.length; t++) {
|
||||
const otherPath = validTracks[t].pathToProp
|
||||
if (otherPath.length === path.length) {
|
||||
for (let p = 0; p < path.length; p++) {
|
||||
if (path[p] !== otherPath[p]) {
|
||||
continue tf
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
// __experimental
|
||||
//
|
||||
//__experimental_setSequenceDuration<V>(prop: Pointer<V>, duration: number): boolean {
|
||||
//const {path, root} = getPointerParts(prop)
|
||||
//if (!isSheet(root)) {
|
||||
//throw new Error(
|
||||
//'Argument prop must be a pointer to a Sheet property',
|
||||
//)
|
||||
//}
|
||||
|
||||
//getStudio()!.transaction(({stateEditors}) => {
|
||||
//stateEditors.coreByProject.historic.sheetsById.sequence.setLength({
|
||||
//...root.address,
|
||||
//length: duration,
|
||||
//})
|
||||
//})
|
||||
//return false
|
||||
//},
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -552,11 +676,250 @@ export default class TheatreStudio implements IStudio {
|
|||
)
|
||||
}
|
||||
|
||||
const __experimental_sequenceProp = <V>(prop: Pointer<V>) => {
|
||||
const {path, root} = getPointerParts(prop)
|
||||
|
||||
// NOT IMPLEMENTED FULLY
|
||||
// only works for simple props. not something like color
|
||||
if (!isSheetObject(root)) {
|
||||
throw new Error(
|
||||
'Argument prop must be a pointer to a SheetObject property',
|
||||
)
|
||||
}
|
||||
|
||||
const propAddress = {...root.address, pathToProp: path}
|
||||
const propConfig = getPropConfigByPath(root.template.staticConfig, path)
|
||||
|
||||
if (propConfig === undefined) {
|
||||
throw new Error('propConfig is undefined. so, yeah.')
|
||||
}
|
||||
console.log({propConfig, propAddress})
|
||||
stateEditors.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsSequenced(
|
||||
propAddress,
|
||||
propConfig,
|
||||
)
|
||||
}
|
||||
const __experimental_staticProp = <V>(prop: Pointer<V>) => {
|
||||
const {path, root} = getPointerParts(prop)
|
||||
|
||||
// NOT IMPLEMENTED FULLY
|
||||
// only works for simple props. not something like color
|
||||
if (!isSheetObject(root)) {
|
||||
throw new Error(
|
||||
'Argument prop must be a pointer to a SheetObject property',
|
||||
)
|
||||
}
|
||||
|
||||
const propAddress = {...root.address, pathToProp: path}
|
||||
const propConfig = getPropConfigByPath(root.template.staticConfig, path)
|
||||
|
||||
if (propConfig === undefined) {
|
||||
throw new Error('propConfig is undefined. so, yeah.')
|
||||
}
|
||||
|
||||
for (const {path: subPath, conf} of iteratePropType(propConfig, [])) {
|
||||
if (isPropConfigComposite(conf)) continue
|
||||
const pointerToSub = pointerDeep(prop, subPath)
|
||||
|
||||
stateEditors.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsStatic(
|
||||
{
|
||||
...propAddress,
|
||||
value: root.getValueByPointer(pointerToSub as $IntentionalAny),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const __experimental_deleteKeyframes = <V>(
|
||||
prop: Pointer<V>,
|
||||
from: number = 0,
|
||||
to: number = 0,
|
||||
) => {
|
||||
const {root, path} = getPointerParts(prop as Pointer<$FixMe>)
|
||||
if (isSheetObject(root)) {
|
||||
const sequenceTracksTree = root.template
|
||||
.getMapOfValidSequenceTracks_forStudio()
|
||||
.getValue()
|
||||
|
||||
const defaultValue = getDeep(
|
||||
root.template.getDefaultValues().getValue(),
|
||||
path,
|
||||
)
|
||||
|
||||
const propConfig = getPropConfigByPath(
|
||||
root.template.staticConfig,
|
||||
path,
|
||||
) as PropTypeConfig
|
||||
|
||||
const unsetStaticOrKeyframeProp = <T>(value: T, path: PathToProp) => {
|
||||
const propAddress = {...root.address, pathToProp: path}
|
||||
|
||||
const trackId = get(sequenceTracksTree, path) as $FixMe as
|
||||
| SequenceTrackId
|
||||
| undefined
|
||||
|
||||
const sequence = root.sheet.getSequence()
|
||||
const trackP = val(
|
||||
sequence._project.pointers.historic.sheetsById[
|
||||
sequence._sheet.address.sheetId
|
||||
].sequence.tracksByObject[root.address.objectKey],
|
||||
)
|
||||
if (!trackP) {
|
||||
throw new Error('whatever, man')
|
||||
}
|
||||
const {trackData, trackIdByPropPath} = trackP
|
||||
//const objectAddress = encodePathToProp(path)
|
||||
//const id = trackIdByPropPath[objectAddress]
|
||||
|
||||
if (
|
||||
typeof trackId === 'string' &&
|
||||
typeof trackData !== 'undefined'
|
||||
) {
|
||||
const track = trackData[trackId]
|
||||
if (!track) {
|
||||
throw new Error('whatever, man')
|
||||
}
|
||||
|
||||
const keyframeIds: KeyframeId[] = []
|
||||
if (to > from) {
|
||||
track.keyframes.forEach((kf) => {
|
||||
if (kf.position >= from && kf.position <= to) {
|
||||
} else {
|
||||
keyframeIds.push(kf.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
const objectKey = propAddress.objectKey
|
||||
stateEditors.coreByProject.historic.sheetsById.sequence.keepKeyframes(
|
||||
{
|
||||
...root.address,
|
||||
objectKey,
|
||||
trackId,
|
||||
keyframeIds,
|
||||
},
|
||||
)
|
||||
} else if (propConfig !== undefined) {
|
||||
stateEditors.coreByProject.historic.sheetsById.staticOverrides.byObject.unsetValueOfPrimitiveProp(
|
||||
propAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (propConfig.type === 'compound') {
|
||||
forEachPropDeep(
|
||||
defaultValue,
|
||||
(v, pathToProp) => {
|
||||
unsetStaticOrKeyframeProp(v, pathToProp)
|
||||
},
|
||||
getPointerParts(prop).path,
|
||||
)
|
||||
} else {
|
||||
unsetStaticOrKeyframeProp(defaultValue, path)
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Only setting props of SheetObject-s is supported in a transaction so far',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const __experimental_addKeyframes = <V>(
|
||||
prop: Pointer<V>,
|
||||
keyframes: [],
|
||||
) => {
|
||||
const {root, path} = getPointerParts(prop as Pointer<$FixMe>)
|
||||
if (isSheetObject(root)) {
|
||||
let sequenceTracksTree = root.template
|
||||
.getMapOfValidSequenceTracks_forStudio()
|
||||
.getValue()
|
||||
|
||||
const defaultValue = getDeep(
|
||||
root.template.getDefaultValues().getValue(),
|
||||
path,
|
||||
)
|
||||
|
||||
const propConfig = getPropConfigByPath(
|
||||
root.template.staticConfig,
|
||||
path,
|
||||
) as PropTypeConfig
|
||||
console.log(path, propConfig)
|
||||
|
||||
const addStaticOrKeyframeProp = <T>(value: T, path: PathToProp) => {
|
||||
const propAddress = {...root.address, pathToProp: path}
|
||||
|
||||
let trackId = get(sequenceTracksTree, path) as $FixMe as
|
||||
| SequenceTrackId
|
||||
| undefined
|
||||
|
||||
if (typeof trackId !== 'string' && propConfig !== undefined) {
|
||||
throw Error('can only add keyframes to sequenced prop')
|
||||
}
|
||||
|
||||
if (typeof trackId === 'string') {
|
||||
const objectKey = propAddress.objectKey
|
||||
stateEditors.coreByProject.historic.sheetsById.sequence.addKeyframes(
|
||||
{
|
||||
...root.address,
|
||||
objectKey,
|
||||
trackId,
|
||||
keyframes,
|
||||
},
|
||||
)
|
||||
console.log({
|
||||
a: {...root.address},
|
||||
objectKey,
|
||||
trackId,
|
||||
keyframes,
|
||||
})
|
||||
} else if (propConfig !== undefined) {
|
||||
throw Error('hmmm')
|
||||
}
|
||||
}
|
||||
|
||||
if (propConfig.type === 'compound') {
|
||||
forEachPropDeep(
|
||||
defaultValue,
|
||||
(v, pathToProp) => {
|
||||
console.log('comp compoumnD')
|
||||
addStaticOrKeyframeProp(v, pathToProp)
|
||||
},
|
||||
getPointerParts(prop).path,
|
||||
)
|
||||
} else {
|
||||
console.log('singlerer', {defaultValue, path})
|
||||
addStaticOrKeyframeProp(defaultValue, path)
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Only setting props of SheetObject-s is supported in a transaction so far',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// transaction
|
||||
//
|
||||
//const __experimental_setSequenceDuration = <V>(prop: Pointer<V>, duration: number) => {
|
||||
//const {root, path} = getPointerParts(prop as Pointer<$FixMe>)
|
||||
//if (isSheet(root)) {
|
||||
//stateEditors.coreByProject.historic.sheetsById.sequence.setLength({
|
||||
//...root.address,
|
||||
//length: duration,
|
||||
//})
|
||||
//}
|
||||
//}
|
||||
|
||||
return fn({
|
||||
set,
|
||||
unset,
|
||||
__experimental_forgetObject,
|
||||
__experimental_forgetSheet,
|
||||
__experimental_sequenceProp,
|
||||
__experimental_staticProp,
|
||||
__experimental_deleteKeyframes,
|
||||
__experimental_addKeyframes,
|
||||
// transaction
|
||||
//
|
||||
//__experimental_setSequenceDuration,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -293,7 +293,6 @@ function createPrism<T extends SerializablePrimitive>(
|
|||
callback: () => {
|
||||
getStudio()!.transaction(({stateEditors}) => {
|
||||
const propAddress = {...obj.address, pathToProp}
|
||||
|
||||
stateEditors.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsSequenced(
|
||||
propAddress,
|
||||
propConfig,
|
||||
|
|
|
@ -772,6 +772,63 @@ namespace stateEditors {
|
|||
stateEditors.coreByProject.historic.sheetsById.staticOverrides.byObject.setValueOfCompoundProp(
|
||||
p,
|
||||
)
|
||||
const toVT = {
|
||||
sequenced: false,
|
||||
panelID: p.objectKey,
|
||||
prop: p.pathToProp,
|
||||
origin: 'stateEditors.ts',
|
||||
}
|
||||
const event = new CustomEvent('sequenceEvent', {
|
||||
bubbles: false,
|
||||
detail: toVT,
|
||||
})
|
||||
window.dispatchEvent(event)
|
||||
}
|
||||
|
||||
export function setCompoundPropAsSequenced(
|
||||
p: WithoutSheetInstance<PropAddress>,
|
||||
) {
|
||||
const tracks = _ensureTracksOfObject(p)
|
||||
|
||||
for (const encodedPropPath of Object.keys(
|
||||
tracks.trackIdByPropPath,
|
||||
)) {
|
||||
const propPath = JSON.parse(encodedPropPath)
|
||||
const isSubOfTargetPath = p.pathToProp.every(
|
||||
(key, i) => propPath[i] === key,
|
||||
)
|
||||
if (isSubOfTargetPath) {
|
||||
const possibleTrackId =
|
||||
tracks.trackIdByPropPath[encodedPropPath]
|
||||
if (typeof possibleTrackId === 'string') return
|
||||
|
||||
const trackId = generateSequenceTrackId()
|
||||
|
||||
const track: BasicKeyframedTrack = {
|
||||
type: 'BasicKeyframedTrack',
|
||||
__debugName: `${p.objectKey}:${encodedPropPath}`,
|
||||
keyframes: [],
|
||||
}
|
||||
|
||||
tracks.trackData[trackId] = track
|
||||
tracks.trackIdByPropPath[encodedPropPath] = trackId
|
||||
}
|
||||
}
|
||||
|
||||
//stateEditors.coreByProject.historic.sheetsById.staticOverrides.byObject.setValueOfCompoundProp(
|
||||
//p,
|
||||
//)
|
||||
const toVT = {
|
||||
sequenced: false,
|
||||
panelID: p.objectKey,
|
||||
prop: p.pathToProp,
|
||||
origin: 'stateEditors.ts',
|
||||
}
|
||||
const event = new CustomEvent('sequenceEvent', {
|
||||
bubbles: false,
|
||||
detail: toVT,
|
||||
})
|
||||
window.dispatchEvent(event)
|
||||
}
|
||||
|
||||
function _getTrack(
|
||||
|
@ -979,7 +1036,54 @@ namespace stateEditors {
|
|||
}
|
||||
}
|
||||
|
||||
export function deleteKeyframes(
|
||||
export function addKeyframes<T extends SerializableValue>(
|
||||
p: WithoutSheetInstance<SheetObjectAddress> & {
|
||||
trackId: SequenceTrackId
|
||||
keyframes: {
|
||||
position: number
|
||||
handles?: [number, number, number, number]
|
||||
value: T
|
||||
type?: KeyframeType
|
||||
}[]
|
||||
},
|
||||
override: boolean = true,
|
||||
) {
|
||||
const track = _getTrack(p)
|
||||
if (!track) return
|
||||
if (p.keyframes.length < 1)
|
||||
throw new Error(
|
||||
'holy shit, you are trying to add non-existing keyframes',
|
||||
)
|
||||
//const {keyframes} = track
|
||||
if (!override)
|
||||
throw new Error('whoopsie, not overriding is not implemented')
|
||||
|
||||
// is it necessary to be so stupid here?
|
||||
const oldKeyframes = track.keyframes
|
||||
track.keyframes = []
|
||||
oldKeyframes.forEach((kf) => {
|
||||
if (p.keyframes[0].position > kf.position) {
|
||||
track.keyframes.push(kf)
|
||||
}
|
||||
})
|
||||
p.keyframes.forEach((pkf) => {
|
||||
track.keyframes.push({
|
||||
id: generateKeyframeId(),
|
||||
position: pkf.position,
|
||||
connectedRight: true,
|
||||
handles: pkf.handles || [0.5, 1, 0.5, 0],
|
||||
type: pkf.type || 'bezier',
|
||||
value: pkf.value,
|
||||
})
|
||||
})
|
||||
oldKeyframes.forEach((kf) => {
|
||||
if (p.keyframes[p.keyframes.length - 1].position < kf.position) {
|
||||
track.keyframes.push(kf)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function keepKeyframes(
|
||||
p: WithoutSheetInstance<SheetObjectAddress> & {
|
||||
trackId: SequenceTrackId
|
||||
keyframeIds: KeyframeId[]
|
||||
|
@ -988,6 +1092,24 @@ namespace stateEditors {
|
|||
const track = _getTrack(p)
|
||||
if (!track) return
|
||||
|
||||
// is it necessary to be so stupid here?
|
||||
const keyframes = track.keyframes
|
||||
track.keyframes = []
|
||||
keyframes.forEach((kf) => {
|
||||
if (p.keyframeIds.indexOf(kf.id) >= 0) {
|
||||
track.keyframes.push(kf)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteKeyframes(
|
||||
p: WithoutSheetInstance<SheetObjectAddress> & {
|
||||
trackId: SequenceTrackId
|
||||
keyframeIds: KeyframeId[]
|
||||
},
|
||||
) {
|
||||
const track = _getTrack(p)
|
||||
if (!track) return
|
||||
track.keyframes = track.keyframes.filter(
|
||||
(kf) => p.keyframeIds.indexOf(kf.id) === -1,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue