|
@@ -4,11 +4,9 @@
|
|
|
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
*/
|
|
|
|
|
|
-// TODO refactor to make DRY
|
|
|
-
|
|
|
import { Unit, Structure } from 'mol-model/structure';
|
|
|
import { RepresentationProps, Visual } from '../';
|
|
|
-import { VisualUpdateState, StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureProps } from '.';
|
|
|
+import { VisualUpdateState, StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureProps, StructureParams } from '.';
|
|
|
import { RuntimeContext } from 'mol-task';
|
|
|
import { PickingId } from '../../geometry/picking';
|
|
|
import { LocationIterator } from '../../util/location-iterator';
|
|
@@ -26,6 +24,7 @@ import { createSizes, SizeProps } from '../../geometry/size-data';
|
|
|
import { Lines } from '../../geometry/lines/lines';
|
|
|
import { MultiSelectParam, paramDefaultValues } from 'mol-view/parameter';
|
|
|
import { DirectVolume } from '../../geometry/direct-volume/direct-volume';
|
|
|
+import { RenderableValues } from 'mol-gl/renderable/schema';
|
|
|
|
|
|
export const UnitKindInfo = {
|
|
|
'atomic': {},
|
|
@@ -72,8 +71,13 @@ function colorChanged(oldProps: ColorProps, newProps: ColorProps) {
|
|
|
}
|
|
|
|
|
|
const UnitsParams = {
|
|
|
+ ...StructureParams,
|
|
|
unitKinds: MultiSelectParam<UnitKind>('Unit Kind', '', ['atomic', 'spheres'], UnitKindOptions),
|
|
|
}
|
|
|
+const DefaultUnitsProps = paramDefaultValues(UnitsParams)
|
|
|
+type UnitsProps = typeof DefaultUnitsProps
|
|
|
+
|
|
|
+type UnitsRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
|
|
|
|
|
|
interface UnitsVisualBuilder<P extends StructureProps, G extends Geometry> {
|
|
|
defaultProps: P
|
|
@@ -84,23 +88,20 @@ interface UnitsVisualBuilder<P extends StructureProps, G extends Geometry> {
|
|
|
setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void
|
|
|
}
|
|
|
|
|
|
-// mesh
|
|
|
-
|
|
|
-export const UnitsMeshParams = {
|
|
|
- ...StructureMeshParams,
|
|
|
- ...UnitsParams,
|
|
|
+interface UnitsVisualGeometryBuilder<P extends StructureProps, G extends Geometry> extends UnitsVisualBuilder<P, G> {
|
|
|
+ createEmptyGeometry(geometry?: G): G
|
|
|
+ createRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, currentProps: P): Promise<UnitsRenderObject>
|
|
|
+ updateValues(values: RenderableValues, newProps: P): void
|
|
|
}
|
|
|
-export const DefaultUnitsMeshProps = paramDefaultValues(UnitsMeshParams)
|
|
|
-export type UnitsMeshProps = typeof DefaultUnitsMeshProps
|
|
|
-export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> extends UnitsVisualBuilder<P, Mesh> { }
|
|
|
|
|
|
-export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
|
|
|
+export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBuilder<P, Geometry>): UnitsVisual<P> {
|
|
|
const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder
|
|
|
+ const { createEmptyGeometry, createRenderObject, updateValues } = builder
|
|
|
const updateState = VisualUpdateState.create()
|
|
|
|
|
|
- let renderObject: MeshRenderObject | undefined
|
|
|
+ let renderObject: UnitsRenderObject | undefined
|
|
|
let currentProps: P
|
|
|
- let mesh: Mesh
|
|
|
+ let geometry: Geometry
|
|
|
let currentGroup: Unit.SymmetryGroup
|
|
|
let currentStructure: Structure
|
|
|
let locationIt: LocationIterator
|
|
@@ -112,13 +113,13 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
|
|
|
|
|
|
const unit = group.units[0]
|
|
|
currentConformationId = Unit.conformationId(unit)
|
|
|
- mesh = includesUnitKind(currentProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, currentProps, mesh)
|
|
|
- : Mesh.createEmpty(mesh)
|
|
|
+ geometry = includesUnitKind(currentProps.unitKinds, unit)
|
|
|
+ ? await createGeometry(ctx, unit, currentStructure, currentProps, geometry)
|
|
|
+ : createEmptyGeometry(geometry)
|
|
|
|
|
|
// TODO create empty location iterator when not in unitKinds
|
|
|
locationIt = createLocationIterator(group)
|
|
|
- renderObject = await createUnitsMeshRenderObject(ctx, group, mesh, locationIt, currentProps)
|
|
|
+ renderObject = await createRenderObject(ctx, group, geometry, locationIt, currentProps)
|
|
|
}
|
|
|
|
|
|
async function update(ctx: RuntimeContext, props: Partial<P> = {}) {
|
|
@@ -139,7 +140,6 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
|
|
|
|
|
|
if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true
|
|
|
|
|
|
- if (sizeChanged(currentProps, newProps)) updateState.createGeometry = true
|
|
|
if (colorChanged(currentProps, newProps)) updateState.updateColor = true
|
|
|
if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
|
|
|
|
|
@@ -154,20 +154,26 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
|
|
|
}
|
|
|
|
|
|
if (updateState.createGeometry) {
|
|
|
- mesh = includesUnitKind(newProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, newProps, mesh)
|
|
|
- : Mesh.createEmpty(mesh)
|
|
|
- ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
|
|
|
+ geometry = includesUnitKind(newProps.unitKinds, unit)
|
|
|
+ ? await createGeometry(ctx, unit, currentStructure, newProps, geometry)
|
|
|
+ : createEmptyGeometry(geometry)
|
|
|
+ ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(geometry))
|
|
|
updateState.updateColor = true
|
|
|
}
|
|
|
|
|
|
+ if (updateState.updateSize) {
|
|
|
+ // not all geometries have size data, so check here
|
|
|
+ if ('uSize' in renderObject.values) {
|
|
|
+ await createSizes(ctx, locationIt, newProps, renderObject.values)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (updateState.updateColor) {
|
|
|
await createColors(ctx, locationIt, newProps, renderObject.values)
|
|
|
}
|
|
|
|
|
|
- // TODO why do I need to cast here?
|
|
|
- Mesh.updateValues(renderObject.values, newProps as UnitsMeshProps)
|
|
|
- updateRenderableState(renderObject.state, newProps as UnitsMeshProps)
|
|
|
+ updateValues(renderObject.values, newProps)
|
|
|
+ updateRenderableState(renderObject.state, newProps)
|
|
|
|
|
|
currentProps = newProps
|
|
|
}
|
|
@@ -226,6 +232,29 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// mesh
|
|
|
+
|
|
|
+export const UnitsMeshParams = {
|
|
|
+ ...StructureMeshParams,
|
|
|
+ ...UnitsParams,
|
|
|
+}
|
|
|
+export const DefaultUnitsMeshProps = paramDefaultValues(UnitsMeshParams)
|
|
|
+export type UnitsMeshProps = typeof DefaultUnitsMeshProps
|
|
|
+export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> extends UnitsVisualBuilder<P, Mesh> { }
|
|
|
+
|
|
|
+export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
|
|
|
+ return UnitsVisual({
|
|
|
+ ...builder,
|
|
|
+ setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
|
|
|
+ builder.setUpdateState(state, newProps, currentProps)
|
|
|
+ if (sizeChanged(currentProps, newProps)) state.createGeometry = true
|
|
|
+ },
|
|
|
+ createEmptyGeometry: Mesh.createEmpty,
|
|
|
+ createRenderObject: createUnitsMeshRenderObject,
|
|
|
+ updateValues: Mesh.updateValues
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
// points
|
|
|
|
|
|
export const UnitsPointsParams = {
|
|
@@ -237,139 +266,16 @@ export type UnitsPointsProps = typeof DefaultUnitsPointsProps
|
|
|
export interface UnitsPointVisualBuilder<P extends UnitsPointsProps> extends UnitsVisualBuilder<P, Points> { }
|
|
|
|
|
|
export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPointVisualBuilder<P>): UnitsVisual<P> {
|
|
|
- const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder
|
|
|
- const updateState = VisualUpdateState.create()
|
|
|
-
|
|
|
- let renderObject: PointsRenderObject | undefined
|
|
|
- let currentProps: P
|
|
|
- let points: Points
|
|
|
- let currentGroup: Unit.SymmetryGroup
|
|
|
- let currentStructure: Structure
|
|
|
- let locationIt: LocationIterator
|
|
|
- let currentConformationId: UUID
|
|
|
-
|
|
|
- async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) {
|
|
|
- currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure })
|
|
|
- currentGroup = group
|
|
|
-
|
|
|
- const unit = group.units[0]
|
|
|
- currentConformationId = Unit.conformationId(unit)
|
|
|
- points = includesUnitKind(currentProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, currentProps, points)
|
|
|
- : Points.createEmpty(points)
|
|
|
-
|
|
|
- // TODO create empty location iterator when not in unitKinds
|
|
|
- locationIt = createLocationIterator(group)
|
|
|
- renderObject = await createUnitsPointsRenderObject(ctx, group, points, locationIt, currentProps)
|
|
|
- }
|
|
|
-
|
|
|
- async function update(ctx: RuntimeContext, props: Partial<P> = {}) {
|
|
|
- if (!renderObject) return
|
|
|
-
|
|
|
- const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
|
|
|
- const unit = currentGroup.units[0]
|
|
|
-
|
|
|
- locationIt.reset()
|
|
|
- VisualUpdateState.reset(updateState)
|
|
|
- setUpdateState(updateState, newProps, currentProps)
|
|
|
-
|
|
|
- const newConformationId = Unit.conformationId(unit)
|
|
|
- if (newConformationId !== currentConformationId) {
|
|
|
- currentConformationId = newConformationId
|
|
|
- updateState.createGeometry = true
|
|
|
- }
|
|
|
-
|
|
|
- if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true
|
|
|
-
|
|
|
- if (sizeChanged(currentProps, newProps)) updateState.updateSize = true
|
|
|
- if (colorChanged(currentProps, newProps)) updateState.updateColor = true
|
|
|
- if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
|
|
|
-
|
|
|
- //
|
|
|
-
|
|
|
- if (updateState.updateTransform) {
|
|
|
- locationIt = createLocationIterator(currentGroup)
|
|
|
- const { instanceCount, groupCount } = locationIt
|
|
|
- createUnitsTransform(currentGroup, renderObject.values)
|
|
|
- createMarkers(instanceCount * groupCount, renderObject.values)
|
|
|
- updateState.updateColor = true
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.createGeometry) {
|
|
|
- points = includesUnitKind(newProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, newProps, points)
|
|
|
- : Points.createEmpty(points)
|
|
|
- ValueCell.update(renderObject.values.drawCount, points.pointCount)
|
|
|
- updateState.updateColor = true
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.updateSize) {
|
|
|
- await createSizes(ctx, locationIt, newProps, renderObject.values)
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.updateColor) {
|
|
|
- await createColors(ctx, locationIt, newProps, renderObject.values)
|
|
|
- }
|
|
|
-
|
|
|
- // TODO why do I need to cast here?
|
|
|
- Points.updateValues(renderObject.values, newProps as UnitsPointsProps)
|
|
|
- updateRenderableState(renderObject.state, newProps as UnitsPointsProps)
|
|
|
-
|
|
|
- currentProps = newProps
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- get renderObject () { return renderObject },
|
|
|
- async createOrUpdate(ctx: RuntimeContext, props: Partial<P> = {}, structureGroup?: StructureGroup) {
|
|
|
- if (structureGroup) currentStructure = structureGroup.structure
|
|
|
- const group = structureGroup ? structureGroup.group : undefined
|
|
|
- if (!group && !currentGroup) {
|
|
|
- throw new Error('missing group')
|
|
|
- } else if (group && (!currentGroup || !renderObject)) {
|
|
|
- // console.log('unit-visual first create')
|
|
|
- await create(ctx, group, props)
|
|
|
- } else if (group && group.hashCode !== currentGroup.hashCode) {
|
|
|
- // console.log('unit-visual group.hashCode !== currentGroup.hashCode')
|
|
|
- await create(ctx, group, props)
|
|
|
- } else {
|
|
|
- // console.log('unit-visual update')
|
|
|
- if (group && !sameGroupConformation(group, currentGroup)) {
|
|
|
- // console.log('unit-visual new conformation')
|
|
|
- currentGroup = group
|
|
|
- }
|
|
|
- await update(ctx, props)
|
|
|
- }
|
|
|
- },
|
|
|
- getLoci(pickingId: PickingId) {
|
|
|
- return renderObject ? getLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci
|
|
|
+ return UnitsVisual({
|
|
|
+ ...builder,
|
|
|
+ createEmptyGeometry: Points.createEmpty,
|
|
|
+ createRenderObject: createUnitsPointsRenderObject,
|
|
|
+ setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
|
|
|
+ builder.setUpdateState(state, newProps, currentProps)
|
|
|
+ if (sizeChanged(currentProps, newProps)) state.updateSize = true
|
|
|
},
|
|
|
- mark(loci: Loci, action: MarkerAction) {
|
|
|
- if (!renderObject) return false
|
|
|
- const { tMarker } = renderObject.values
|
|
|
- const { groupCount, instanceCount } = locationIt
|
|
|
-
|
|
|
- function apply(interval: Interval) {
|
|
|
- const start = Interval.start(interval)
|
|
|
- const end = Interval.end(interval)
|
|
|
- return applyMarkerAction(tMarker.ref.value.array, start, end, action)
|
|
|
- }
|
|
|
-
|
|
|
- let changed = false
|
|
|
- if (isEveryLoci(loci)) {
|
|
|
- changed = apply(Interval.ofBounds(0, groupCount * instanceCount))
|
|
|
- } else {
|
|
|
- changed = mark(loci, currentGroup, apply)
|
|
|
- }
|
|
|
- if (changed) {
|
|
|
- ValueCell.update(tMarker, tMarker.ref.value)
|
|
|
- }
|
|
|
- return changed
|
|
|
- },
|
|
|
- destroy() {
|
|
|
- // TODO
|
|
|
- renderObject = undefined
|
|
|
- }
|
|
|
- }
|
|
|
+ updateValues: Points.updateValues
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
// lines
|
|
@@ -383,139 +289,16 @@ export type UnitsLinesProps = typeof DefaultUnitsLinesProps
|
|
|
export interface UnitsLinesVisualBuilder<P extends UnitsLinesProps> extends UnitsVisualBuilder<P, Lines> { }
|
|
|
|
|
|
export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesVisualBuilder<P>): UnitsVisual<P> {
|
|
|
- const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder
|
|
|
- const updateState = VisualUpdateState.create()
|
|
|
-
|
|
|
- let renderObject: LinesRenderObject | undefined
|
|
|
- let currentProps: P
|
|
|
- let lines: Lines
|
|
|
- let currentGroup: Unit.SymmetryGroup
|
|
|
- let currentStructure: Structure
|
|
|
- let locationIt: LocationIterator
|
|
|
- let currentConformationId: UUID
|
|
|
-
|
|
|
- async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) {
|
|
|
- currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure })
|
|
|
- currentGroup = group
|
|
|
-
|
|
|
- const unit = group.units[0]
|
|
|
- currentConformationId = Unit.conformationId(unit)
|
|
|
- lines = includesUnitKind(currentProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, currentProps, lines)
|
|
|
- : Lines.createEmpty(lines)
|
|
|
-
|
|
|
- // TODO create empty location iterator when not in unitKinds
|
|
|
- locationIt = createLocationIterator(group)
|
|
|
- renderObject = await createUnitsLinesRenderObject(ctx, group, lines, locationIt, currentProps)
|
|
|
- }
|
|
|
-
|
|
|
- async function update(ctx: RuntimeContext, props: Partial<P> = {}) {
|
|
|
- if (!renderObject) return
|
|
|
-
|
|
|
- const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
|
|
|
- const unit = currentGroup.units[0]
|
|
|
-
|
|
|
- locationIt.reset()
|
|
|
- VisualUpdateState.reset(updateState)
|
|
|
- setUpdateState(updateState, newProps, currentProps)
|
|
|
-
|
|
|
- const newConformationId = Unit.conformationId(unit)
|
|
|
- if (newConformationId !== currentConformationId) {
|
|
|
- currentConformationId = newConformationId
|
|
|
- updateState.createGeometry = true
|
|
|
- }
|
|
|
-
|
|
|
- if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true
|
|
|
-
|
|
|
- if (sizeChanged(currentProps, newProps)) updateState.updateSize = true
|
|
|
- if (colorChanged(currentProps, newProps)) updateState.updateColor = true
|
|
|
- if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
|
|
|
-
|
|
|
- //
|
|
|
-
|
|
|
- if (updateState.updateTransform) {
|
|
|
- locationIt = createLocationIterator(currentGroup)
|
|
|
- const { instanceCount, groupCount } = locationIt
|
|
|
- createUnitsTransform(currentGroup, renderObject.values)
|
|
|
- createMarkers(instanceCount * groupCount, renderObject.values)
|
|
|
- updateState.updateColor = true
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.createGeometry) {
|
|
|
- lines = includesUnitKind(newProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, newProps, lines)
|
|
|
- : Lines.createEmpty(lines)
|
|
|
- ValueCell.update(renderObject.values.drawCount, lines.lineCount * 2 * 3)
|
|
|
- updateState.updateColor = true
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.updateSize) {
|
|
|
- await createSizes(ctx, locationIt, newProps, renderObject.values)
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.updateColor) {
|
|
|
- await createColors(ctx, locationIt, newProps, renderObject.values)
|
|
|
- }
|
|
|
-
|
|
|
- // TODO why do I need to cast here?
|
|
|
- Lines.updateValues(renderObject.values, newProps as UnitsLinesProps)
|
|
|
- updateRenderableState(renderObject.state, newProps as UnitsLinesProps)
|
|
|
-
|
|
|
- currentProps = newProps
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- get renderObject () { return renderObject },
|
|
|
- async createOrUpdate(ctx: RuntimeContext, props: Partial<P> = {}, structureGroup?: StructureGroup) {
|
|
|
- if (structureGroup) currentStructure = structureGroup.structure
|
|
|
- const group = structureGroup ? structureGroup.group : undefined
|
|
|
- if (!group && !currentGroup) {
|
|
|
- throw new Error('missing group')
|
|
|
- } else if (group && (!currentGroup || !renderObject)) {
|
|
|
- // console.log('unit-visual first create')
|
|
|
- await create(ctx, group, props)
|
|
|
- } else if (group && group.hashCode !== currentGroup.hashCode) {
|
|
|
- // console.log('unit-visual group.hashCode !== currentGroup.hashCode')
|
|
|
- await create(ctx, group, props)
|
|
|
- } else {
|
|
|
- // console.log('unit-visual update')
|
|
|
- if (group && !sameGroupConformation(group, currentGroup)) {
|
|
|
- // console.log('unit-visual new conformation')
|
|
|
- currentGroup = group
|
|
|
- }
|
|
|
- await update(ctx, props)
|
|
|
- }
|
|
|
- },
|
|
|
- getLoci(pickingId: PickingId) {
|
|
|
- return renderObject ? getLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci
|
|
|
+ return UnitsVisual({
|
|
|
+ ...builder,
|
|
|
+ createEmptyGeometry: Lines.createEmpty,
|
|
|
+ createRenderObject: createUnitsLinesRenderObject,
|
|
|
+ setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
|
|
|
+ builder.setUpdateState(state, newProps, currentProps)
|
|
|
+ if (sizeChanged(currentProps, newProps)) state.updateSize = true
|
|
|
},
|
|
|
- mark(loci: Loci, action: MarkerAction) {
|
|
|
- if (!renderObject) return false
|
|
|
- const { tMarker } = renderObject.values
|
|
|
- const { groupCount, instanceCount } = locationIt
|
|
|
-
|
|
|
- function apply(interval: Interval) {
|
|
|
- const start = Interval.start(interval)
|
|
|
- const end = Interval.end(interval)
|
|
|
- return applyMarkerAction(tMarker.ref.value.array, start, end, action)
|
|
|
- }
|
|
|
-
|
|
|
- let changed = false
|
|
|
- if (isEveryLoci(loci)) {
|
|
|
- changed = apply(Interval.ofBounds(0, groupCount * instanceCount))
|
|
|
- } else {
|
|
|
- changed = mark(loci, currentGroup, apply)
|
|
|
- }
|
|
|
- if (changed) {
|
|
|
- ValueCell.update(tMarker, tMarker.ref.value)
|
|
|
- }
|
|
|
- return changed
|
|
|
- },
|
|
|
- destroy() {
|
|
|
- // TODO
|
|
|
- renderObject = undefined
|
|
|
- }
|
|
|
- }
|
|
|
+ updateValues: Lines.updateValues
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
// direct-volume
|
|
@@ -529,137 +312,14 @@ export type UnitsDirectVolumeProps = typeof DefaultUnitsDirectVolumeProps
|
|
|
export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeProps> extends UnitsVisualBuilder<P, DirectVolume> { }
|
|
|
|
|
|
export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builder: UnitsDirectVolumeVisualBuilder<P>): UnitsVisual<P> {
|
|
|
- const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder
|
|
|
- const updateState = VisualUpdateState.create()
|
|
|
-
|
|
|
- let renderObject: DirectVolumeRenderObject | undefined
|
|
|
- let currentProps: P
|
|
|
- let directVolume: DirectVolume
|
|
|
- let currentGroup: Unit.SymmetryGroup
|
|
|
- let currentStructure: Structure
|
|
|
- let locationIt: LocationIterator
|
|
|
- let currentConformationId: UUID
|
|
|
-
|
|
|
- async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) {
|
|
|
- const { webgl } = props
|
|
|
- if (webgl === undefined) throw new Error('UnitsDirectVolumeVisual requires `webgl` in props')
|
|
|
-
|
|
|
- currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure })
|
|
|
- currentGroup = group
|
|
|
-
|
|
|
- const unit = group.units[0]
|
|
|
- currentConformationId = Unit.conformationId(unit)
|
|
|
- directVolume = includesUnitKind(currentProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, currentProps, directVolume)
|
|
|
- : DirectVolume.createEmpty(directVolume)
|
|
|
-
|
|
|
- // TODO create empty location iterator when not in unitKinds
|
|
|
- locationIt = createLocationIterator(group)
|
|
|
- renderObject = await createUnitsDirectVolumeRenderObject(ctx, group, directVolume, locationIt, currentProps)
|
|
|
- }
|
|
|
-
|
|
|
- async function update(ctx: RuntimeContext, props: Partial<P> = {}) {
|
|
|
- const { webgl } = props
|
|
|
- if (webgl === undefined) throw new Error('UnitsDirectVolumeVisual requires `webgl` in props')
|
|
|
-
|
|
|
- if (!renderObject) return
|
|
|
-
|
|
|
- const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
|
|
|
- const unit = currentGroup.units[0]
|
|
|
-
|
|
|
- locationIt.reset()
|
|
|
- VisualUpdateState.reset(updateState)
|
|
|
- setUpdateState(updateState, newProps, currentProps)
|
|
|
-
|
|
|
- const newConformationId = Unit.conformationId(unit)
|
|
|
- if (newConformationId !== currentConformationId) {
|
|
|
- currentConformationId = newConformationId
|
|
|
- updateState.createGeometry = true
|
|
|
- }
|
|
|
-
|
|
|
- if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true
|
|
|
-
|
|
|
- if (sizeChanged(currentProps, newProps)) updateState.createGeometry = true
|
|
|
- if (colorChanged(currentProps, newProps)) updateState.updateColor = true
|
|
|
- if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
|
|
|
-
|
|
|
- //
|
|
|
-
|
|
|
- if (updateState.updateTransform) {
|
|
|
- locationIt = createLocationIterator(currentGroup)
|
|
|
- const { instanceCount, groupCount } = locationIt
|
|
|
- createUnitsTransform(currentGroup, renderObject.values)
|
|
|
- createMarkers(instanceCount * groupCount, renderObject.values)
|
|
|
- updateState.updateColor = true
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.createGeometry) {
|
|
|
- directVolume = includesUnitKind(newProps.unitKinds, unit)
|
|
|
- ? await createGeometry(ctx, unit, currentStructure, newProps, directVolume)
|
|
|
- : DirectVolume.createEmpty(directVolume)
|
|
|
- updateState.updateColor = true
|
|
|
- }
|
|
|
-
|
|
|
- if (updateState.updateColor) {
|
|
|
- await createColors(ctx, locationIt, newProps, renderObject.values)
|
|
|
- }
|
|
|
-
|
|
|
- DirectVolume.updateValues(renderObject.values, newProps)
|
|
|
- updateRenderableState(renderObject.state, newProps)
|
|
|
-
|
|
|
- currentProps = newProps
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- get renderObject () { return renderObject },
|
|
|
- async createOrUpdate(ctx: RuntimeContext, props: Partial<P> = {}, structureGroup?: StructureGroup) {
|
|
|
- if (structureGroup) currentStructure = structureGroup.structure
|
|
|
- const group = structureGroup ? structureGroup.group : undefined
|
|
|
- if (!group && !currentGroup) {
|
|
|
- throw new Error('missing group')
|
|
|
- } else if (group && (!currentGroup || !renderObject)) {
|
|
|
- // console.log('unit-visual first create')
|
|
|
- await create(ctx, group, props)
|
|
|
- } else if (group && group.hashCode !== currentGroup.hashCode) {
|
|
|
- // console.log('unit-visual group.hashCode !== currentGroup.hashCode')
|
|
|
- await create(ctx, group, props)
|
|
|
- } else {
|
|
|
- // console.log('unit-visual update')
|
|
|
- if (group && !sameGroupConformation(group, currentGroup)) {
|
|
|
- // console.log('unit-visual new conformation')
|
|
|
- currentGroup = group
|
|
|
- }
|
|
|
- await update(ctx, props)
|
|
|
- }
|
|
|
- },
|
|
|
- getLoci(pickingId: PickingId) {
|
|
|
- return renderObject ? getLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci
|
|
|
+ return UnitsVisual({
|
|
|
+ ...builder,
|
|
|
+ createEmptyGeometry: DirectVolume.createEmpty,
|
|
|
+ createRenderObject: createUnitsDirectVolumeRenderObject,
|
|
|
+ setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
|
|
|
+ builder.setUpdateState(state, newProps, currentProps)
|
|
|
+ if (sizeChanged(currentProps, newProps)) state.createGeometry = true
|
|
|
},
|
|
|
- mark(loci: Loci, action: MarkerAction) {
|
|
|
- if (!renderObject) return false
|
|
|
- const { tMarker } = renderObject.values
|
|
|
- const { groupCount, instanceCount } = locationIt
|
|
|
-
|
|
|
- function apply(interval: Interval) {
|
|
|
- const start = Interval.start(interval)
|
|
|
- const end = Interval.end(interval)
|
|
|
- return applyMarkerAction(tMarker.ref.value.array, start, end, action)
|
|
|
- }
|
|
|
-
|
|
|
- let changed = false
|
|
|
- if (isEveryLoci(loci)) {
|
|
|
- changed = apply(Interval.ofBounds(0, groupCount * instanceCount))
|
|
|
- } else {
|
|
|
- changed = mark(loci, currentGroup, apply)
|
|
|
- }
|
|
|
- if (changed) {
|
|
|
- ValueCell.update(tMarker, tMarker.ref.value)
|
|
|
- }
|
|
|
- return changed
|
|
|
- },
|
|
|
- destroy() {
|
|
|
- // TODO
|
|
|
- renderObject = undefined
|
|
|
- }
|
|
|
- }
|
|
|
+ updateValues: DirectVolume.updateValues
|
|
|
+ })
|
|
|
}
|