theatre/theatre/core/src/sheets/Sheet.ts
Aria Minaei ae8be59366 Implement a basic benchmark test, and deprecate derivation.map()/flatMap()
This starts a new workspace at `packages/benchmarks` where future benchmarks
are going to sit. For now, it only contains a basic profile of a `sequence.play()`
setup.

It also removes all uses of `AbstractDerivation.map()/flatMap()` and uses prisms
instead.
2023-01-04 20:49:35 +01:00

124 lines
3.8 KiB
TypeScript

import type Project from '@theatre/core/projects/Project'
import Sequence from '@theatre/core/sequences/Sequence'
import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import type {
SheetObjectActionsConfig,
SheetObjectPropTypeConfig,
} from '@theatre/core/sheets/TheatreSheet'
import TheatreSheet from '@theatre/core/sheets/TheatreSheet'
import type {SheetAddress} from '@theatre/shared/utils/addresses'
import {Atom, prism, val} from '@theatre/dataverse'
import type SheetTemplate from './SheetTemplate'
import type {ObjectAddressKey, SheetInstanceId} from '@theatre/shared/utils/ids'
import type {StrictRecord} from '@theatre/shared/utils/types'
import type {ILogger} from '@theatre/shared/logger'
import {isInteger} from 'lodash-es'
type SheetObjectMap = StrictRecord<ObjectAddressKey, SheetObject>
/**
* Future: `nativeObject` Idea is to potentially allow the user to provide their own
* object in to the object call as a way to keep a handle to an underlying object via
* the {@link ISheetObject}.
*
* For example, a THREEjs object or an HTMLElement is passed in.
*/
export type ObjectNativeObject = unknown
export default class Sheet {
private readonly _objects: Atom<SheetObjectMap> = new Atom<SheetObjectMap>({})
private _sequence: undefined | Sequence
readonly address: SheetAddress
readonly publicApi: TheatreSheet
readonly project: Project
readonly objectsP = this._objects.pointer
type: 'Theatre_Sheet' = 'Theatre_Sheet'
readonly _logger: ILogger
constructor(
readonly template: SheetTemplate,
public readonly instanceId: SheetInstanceId,
) {
this._logger = template.project._logger.named('Sheet', instanceId)
this._logger._trace('creating sheet')
this.project = template.project
this.address = {
...template.address,
sheetInstanceId: this.instanceId,
}
this.publicApi = new TheatreSheet(this)
}
/**
* @remarks At some point, we have to reconcile the concept of "an object"
* with that of "an element."
*/
createObject(
objectKey: ObjectAddressKey,
nativeObject: ObjectNativeObject,
config: SheetObjectPropTypeConfig,
actions: SheetObjectActionsConfig = {},
): SheetObject {
const objTemplate = this.template.getObjectTemplate(
objectKey,
nativeObject,
config,
actions,
)
const object = objTemplate.createInstance(this, nativeObject, config)
this._objects.setIn([objectKey], object)
return object
}
getObject(key: ObjectAddressKey): SheetObject | undefined {
return this._objects.getState()[key]
}
deleteObject(objectKey: ObjectAddressKey) {
this._objects.reduceState([], (state) => {
const newState = {...state}
delete newState[objectKey]
return newState
})
}
getSequence(): Sequence {
if (!this._sequence) {
const lengthD = prism(() => {
const unsanitized = val(
this.project.pointers.historic.sheetsById[this.address.sheetId]
.sequence.length,
)
return sanitizeSequenceLength(unsanitized)
})
const subUnitsPerUnitD = prism(() => {
const unsanitized = val(
this.project.pointers.historic.sheetsById[this.address.sheetId]
.sequence.subUnitsPerUnit,
)
return sanitizeSequenceSubUnitsPerUnit(unsanitized)
})
this._sequence = new Sequence(
this.template.project,
this,
lengthD,
subUnitsPerUnitD,
)
}
return this._sequence
}
}
const sanitizeSequenceLength = (len: number | undefined): number =>
typeof len === 'number' && isFinite(len) && len > 0 ? len : 10
const sanitizeSequenceSubUnitsPerUnit = (subs: number | undefined): number =>
typeof subs === 'number' && isInteger(subs) && subs >= 1 && subs <= 1000
? subs
: 30