diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts index 234cc137d3367dc3fa7b9bdedcb4a82d9c4fded0..84d811e25e5120b2a54b70b68db7890c4dfd18b4 100644 --- a/src/mol-plugin/behavior/dynamic/representation.ts +++ b/src/mol-plugin/behavior/dynamic/representation.ts @@ -193,27 +193,27 @@ export namespace ColorRepresentation3D { this.currentColorMappings = colorMappings; if (!this.repr.data || !this.structure.data) return true; - const list: Overpaint.Layer[] = [] + const layers: Overpaint.Layer[] = [] for (let i = 0, il = this.currentColorMappings.length; i < il; ++i) { const { query, color } = this.currentColorMappings[i] const compiled = compile<StructureSelection>(query); const result = compiled(new QueryContext(this.structure.data)); const loci = StructureSelection.toLoci2(result) - list.push({ loci, color }) + layers.push({ loci, color }) } - return this.applyLayers({ alpha, list }, true) + return this.applyLayers({ alpha, layers }) } - private applyLayers(layers: Overpaint.Layers, clear: boolean): boolean { + private applyLayers(overpaint: Overpaint): boolean { if (!this.repr.data) return true; - this.repr.data.repr.setOverpaint(layers) + this.repr.data.repr.setState({ overpaint }) this.ctx.canvas3d.add(this.repr.data.repr); this.ctx.canvas3d.requestDraw(true); return true; } unregister(): void { - this.applyLayers(Overpaint.EmptyLayers, true) // clear + this.applyLayers(Overpaint.Empty) // clear this.repr.cell = void 0; this.structure.cell = void 0; } diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts index fc4316575b35cb732f4258e67a733f382706caf6..5bc851e2fdbac8163d79b00a4fdd8a5c18b319b9 100644 --- a/src/mol-repr/representation.ts +++ b/src/mol-repr/representation.ts @@ -122,7 +122,6 @@ interface Representation<D, P extends PD.Params = {}, S extends Representation.S createOrUpdate: (props?: Partial<PD.Values<P>>, data?: D) => Task<void> setState: (state: Partial<S>) => void setTheme: (theme: Theme) => void - setOverpaint: (layers: Overpaint.Layers, clear?: boolean) => void getLoci: (pickingId: PickingId) => ModelLoci mark: (loci: ModelLoci, action: MarkerAction) => boolean destroy: () => void @@ -145,18 +144,21 @@ namespace Representation { alphaFactor: number /** Controls if the representation's renderobjects are pickable or not */ pickable: boolean + /** Overpaint applied to the representation's renderobjects */ + overpaint: Overpaint /** Controls if the representation's renderobjects are synced automatically with GPU or not */ syncManually: boolean /** A transformation applied to the representation's renderobjects */ transform: Mat4 } export function createState(): State { - return { visible: false, alphaFactor: 0, pickable: false, syncManually: false, transform: Mat4.identity() } + return { visible: false, alphaFactor: 0, pickable: false, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty } } export function updateState(state: State, update: Partial<State>) { if (update.visible !== undefined) state.visible = update.visible if (update.alphaFactor !== undefined) state.alphaFactor = update.alphaFactor if (update.pickable !== undefined) state.pickable = update.pickable + if (update.overpaint !== undefined) state.overpaint = update.overpaint if (update.syncManually !== undefined) state.syncManually = update.syncManually if (update.transform !== undefined) Mat4.copy(state.transform, update.transform) } @@ -172,7 +174,6 @@ namespace Representation { createOrUpdate: () => Task.constant('', undefined), setState: () => {}, setTheme: () => {}, - setOverpaint: () => false, getLoci: () => EmptyLoci, mark: () => false, destroy: () => {} @@ -271,11 +272,6 @@ namespace Representation { reprList[i].setTheme(theme) } }, - setOverpaint: (layers: Overpaint.Layers) => { - for (let i = 0, il = reprList.length; i < il; ++i) { - reprList[i].setOverpaint(layers) - } - }, destroy() { for (let i = 0, il = reprList.length; i < il; ++i) { reprList[i].destroy() @@ -323,12 +319,14 @@ namespace Representation { if (state.visible !== undefined) Visual.setVisibility(renderObject, state.visible) if (state.alphaFactor !== undefined) Visual.setAlphaFactor(renderObject, state.alphaFactor) if (state.pickable !== undefined) Visual.setPickable(renderObject, state.pickable) + if (state.overpaint !== undefined) { + // TODO + } if (state.transform !== undefined) Visual.setTransform(renderObject, state.transform) Representation.updateState(currentState, state) }, setTheme: () => { }, - setOverpaint: () => false, destroy() { } } } diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts index d64b46f74f3f54905a4b2e4fd6541f60952cc916..83f1edb96e25a5ed817d4cba918fb1d2a9ec803b 100644 --- a/src/mol-repr/shape/representation.ts +++ b/src/mol-repr/shape/representation.ts @@ -14,7 +14,7 @@ import { OrderedSet, Interval } from 'mol-data/int'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { createTransform, TransformData } from 'mol-geo/geometry/transform-data'; import { PickingId } from 'mol-geo/geometry/picking'; -import { MarkerAction, applyMarkerAction, createMarkers } from 'mol-geo/geometry/marker-data'; +import { MarkerAction, createMarkers } from 'mol-geo/geometry/marker-data'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { createEmptyTheme, Theme } from 'mol-theme/theme'; import { Subject } from 'rxjs'; @@ -26,8 +26,6 @@ import { Mat4 } from 'mol-math/linear-algebra'; import { Visual } from 'mol-repr/visual'; import { createSizes } from 'mol-geo/geometry/size-data'; import { ShapeGroupSizeTheme } from 'mol-theme/size/shape-group'; -import { Overpaint } from 'mol-theme/overpaint'; -import { applyOverpaintColor, createOverpaint, clearOverpaint } from 'mol-geo/geometry/overpaint-data'; export interface ShapeRepresentation<D, G extends Geometry, P extends Geometry.Params<G>> extends Representation<D, P> { } @@ -151,6 +149,14 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa }); } + function lociApply(loci: Loci, apply: (interval: Interval) => boolean) { + if (isEveryLoci(loci) || (Shape.isLoci(loci) && loci.shape === _shape)) { + return apply(Interval.ofBounds(0, _shape.groupCount * _shape.transforms.length)) + } else { + return eachShapeGroup(loci, _shape, apply) + } + } + return { label: 'Shape geometry', get groupCount () { return locationIt ? locationIt.count : 0 }, @@ -169,32 +175,16 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa return EmptyLoci }, 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) || (Shape.isLoci(loci) && loci.shape === _shape)) { - changed = apply(Interval.ofBounds(0, groupCount * instanceCount)) - } else { - changed = eachShapeLocation(loci, _shape, apply) - } - if (changed) { - ValueCell.update(tMarker, tMarker.ref.value) - } - return changed + return Visual.mark(_renderObject, loci, action, lociApply) }, setState(state: Partial<Representation.State>) { if (_renderObject) { if (state.visible !== undefined) Visual.setVisibility(_renderObject, state.visible) if (state.alphaFactor !== undefined) Visual.setAlphaFactor(_renderObject, state.alphaFactor) if (state.pickable !== undefined) Visual.setPickable(_renderObject, state.pickable) + if (state.overpaint !== undefined) { + Visual.setOverpaint(_renderObject, state.overpaint, lociApply, true) + } if (state.transform !== undefined) Visual.setTransform(_renderObject, state.transform) } @@ -203,33 +193,6 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa setTheme(theme: Theme) { console.warn('The `ShapeRepresentation` theme is fixed to `ShapeGroupColorTheme` and `ShapeGroupSizeTheme`. Colors are taken from `Shape.getColor` and sizes from `Shape.getSize`') }, - setOverpaint(layers: Overpaint.Layers, clear?: boolean) { - if (!_renderObject) return false - const { tOverpaint } = _renderObject.values - const count = locationIt.groupCount * locationIt.instanceCount - - // ensure texture has right size - createOverpaint(layers.list.length ? count : 0, _renderObject.values) - - // clear if requested - if (clear) clearOverpaint(tOverpaint.ref.value.array, 0, count) - - for (let i = 0, il = layers.list.length; i < il; ++i) { - const { loci, color } = layers.list[i] - const apply = (interval: Interval) => { - const start = Interval.start(interval) - const end = Interval.end(interval) - return applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, layers.alpha) - } - - if (isEveryLoci(loci) || (Shape.isLoci(loci) && loci.shape === _shape)) { - apply(Interval.ofBounds(0, count)) - } else { - eachShapeLocation(loci, _shape, apply) - } - } - ValueCell.update(tOverpaint, tOverpaint.ref.value) - }, destroy() { // TODO renderObjects.length = 0 @@ -246,7 +209,7 @@ function createShapeTransform(transforms: Mat4[], transformData?: TransformData) return createTransform(transformArray, transforms.length, transformData) } -function eachShapeLocation(loci: Loci, shape: Shape, apply: (interval: Interval) => boolean) { +function eachShapeGroup(loci: Loci, shape: Shape, apply: (interval: Interval) => boolean) { if (!Shape.isLoci(loci)) return false if (loci.shape !== shape) return false let changed = false diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts index 35664955fb2e2aad5cdaef99657ba061dd7e4aed..bbb8325859e481f190099c5e212bb79588ce0b95 100644 --- a/src/mol-repr/structure/complex-representation.ts +++ b/src/mol-repr/structure/complex-representation.ts @@ -17,7 +17,6 @@ import { Theme, createEmptyTheme } from 'mol-theme/theme'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Subject } from 'rxjs'; import { GraphicsRenderObject } from 'mol-gl/render-object'; -import { Overpaint } from 'mol-theme/overpaint'; export function ComplexRepresentation<P extends StructureParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> { let version = 0 @@ -66,7 +65,9 @@ export function ComplexRepresentation<P extends StructureParams>(label: string, // hide visual when _unitTransforms is set visual.setVisibility(state.visible && _state.unitTransforms === null) } + if (state.alphaFactor !== undefined && visual) visual.setAlphaFactor(state.alphaFactor) if (state.pickable !== undefined && visual) visual.setPickable(state.pickable) + if (state.overpaint !== undefined && visual) visual.setOverpaint(state.overpaint) if (state.transform !== undefined && visual) visual.setTransform(state.transform) if (state.unitTransforms !== undefined && visual) { // Since ComplexVisuals always renders geometries between units the application of `unitTransforms` @@ -79,10 +80,6 @@ export function ComplexRepresentation<P extends StructureParams>(label: string, _theme = theme } - function setOverpaint(layers: Overpaint.Layers, clear?: boolean) { - if (visual) visual.setOverpaint(layers, clear) - } - function destroy() { if (visual) visual.destroy() } @@ -101,7 +98,6 @@ export function ComplexRepresentation<P extends StructureParams>(label: string, createOrUpdate, setState, setTheme, - setOverpaint, getLoci, mark, destroy diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index 72cde149fa62f183d48122e9818efb887bf4495f..7f5407789c126d221f45642205eab9576b0150df 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/src/mol-repr/structure/complex-visual.ts @@ -18,7 +18,7 @@ import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { PickingId } from 'mol-geo/geometry/picking'; import { createColors } from 'mol-geo/geometry/color-data'; -import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data'; +import { MarkerAction } from 'mol-geo/geometry/marker-data'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { VisualUpdateState } from 'mol-repr/util'; import { Theme, createEmptyTheme } from 'mol-theme/theme'; @@ -29,7 +29,6 @@ import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; import { Mat4 } from 'mol-math/linear-algebra'; import { createIdentityTransform } from 'mol-geo/geometry/transform-data'; import { Overpaint } from 'mol-theme/overpaint'; -import { applyOverpaintColor, createOverpaint, clearOverpaint } from 'mol-geo/geometry/overpaint-data'; export interface ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { } @@ -159,6 +158,14 @@ export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geom if (newGeometry) geometry = newGeometry } + function lociApply(loci: Loci, apply: (interval: Interval) => boolean) { + if (isEveryLoci(loci) || (Structure.isLoci(loci) && Structure.areEquivalent(loci.structure, currentStructure))) { + return apply(Interval.ofBounds(0, locationIt.groupCount * locationIt.instanceCount)) + } else { + return eachLocation(loci, currentStructure, apply) + } + } + return { get groupCount() { return locationIt ? locationIt.count : 0 }, get renderObject () { return locationIt && locationIt.count ? renderObject : undefined }, @@ -175,26 +182,7 @@ export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geom return renderObject ? getLoci(pickingId, currentStructure, renderObject.id) : EmptyLoci }, 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) || (Structure.isLoci(loci) && Structure.areEquivalent(loci.structure, currentStructure))) { - changed = apply(Interval.ofBounds(0, groupCount * instanceCount)) - } else { - changed = eachLocation(loci, currentStructure, apply) - } - if (changed) { - ValueCell.update(tMarker, tMarker.ref.value) - } - return changed + return Visual.mark(renderObject, loci, action, lociApply) }, setVisibility(visible: boolean) { Visual.setVisibility(renderObject, visible) @@ -208,32 +196,8 @@ export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geom setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) { Visual.setTransform(renderObject, matrix, instanceMatrices) }, - setOverpaint(layers: Overpaint.Layers, clear = false) { - if (!renderObject) return false - const { tOverpaint } = renderObject.values - const count = locationIt.groupCount * locationIt.instanceCount - - // ensure texture has right size - createOverpaint(layers.list.length ? count : 0, renderObject.values) - - // clear if requested - if (clear) clearOverpaint(tOverpaint.ref.value.array, 0, count) - - for (let i = 0, il = layers.list.length; i < il; ++i) { - const { loci, color } = layers.list[i] - const apply = (interval: Interval) => { - const start = Interval.start(interval) - const end = Interval.end(interval) - return applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, layers.alpha) - } - - if (isEveryLoci(loci) || (Structure.isLoci(loci) && Structure.areEquivalent(loci.structure, currentStructure))) { - apply(Interval.ofBounds(0, count)) - } else { - eachLocation(loci, currentStructure, apply) - } - } - ValueCell.update(tOverpaint, tOverpaint.ref.value) + setOverpaint(overpaint: Overpaint, clear = false) { + return Visual.setOverpaint(renderObject, overpaint, lociApply, true) }, destroy() { // TODO diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts index 7d7b65d8ed4140fce1478005dc24dfc076d2ac15..92e0c6e28cb1581a2aa9843a2eb2921898ebaa19 100644 --- a/src/mol-repr/structure/units-representation.ts +++ b/src/mol-repr/structure/units-representation.ts @@ -19,7 +19,6 @@ import { Theme, createEmptyTheme } from 'mol-theme/theme'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { UnitKind, UnitKindOptions } from './visual/util/common'; import { Subject } from 'rxjs'; -import { Overpaint } from 'mol-theme/overpaint'; export const UnitsParams = { ...StructureParams, @@ -172,9 +171,11 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R } function setState(state: Partial<StructureRepresentationState>) { - const { visible, pickable, transform, unitTransforms } = state + const { visible, alphaFactor, pickable, overpaint, transform, unitTransforms } = state if (visible !== undefined) visuals.forEach(({ visual }) => visual.setVisibility(visible)) + if (alphaFactor !== undefined) visuals.forEach(({ visual }) => visual.setAlphaFactor(alphaFactor)) if (pickable !== undefined) visuals.forEach(({ visual }) => visual.setPickable(pickable)) + if (overpaint !== undefined) visuals.forEach(({ visual }) => visual.setOverpaint(overpaint)) if (transform !== undefined) visuals.forEach(({ visual }) => visual.setTransform(transform)) if (unitTransforms !== undefined) { visuals.forEach(({ visual, group }) => { @@ -194,10 +195,6 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R _theme = theme } - function setOverpaint(layers: Overpaint.Layers, clear?: boolean) { - visuals.forEach(({ visual }) => visual.setOverpaint(layers, clear)) - } - function destroy() { visuals.forEach(({ visual }) => visual.destroy()) visuals.clear() @@ -221,7 +218,6 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R createOrUpdate, setState, setTheme, - setOverpaint, getLoci, mark, destroy diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index 52ddc036b2dd94898fb777b62f0b66ea79ba31b4..4f3c5bf22635d6cd2a7c1a1f09d8d7e5059241f9 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/src/mol-repr/structure/units-visual.ts @@ -16,7 +16,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { PickingId } from 'mol-geo/geometry/picking'; -import { createMarkers, MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data'; +import { createMarkers, MarkerAction } from 'mol-geo/geometry/marker-data'; import { createSizes } from 'mol-geo/geometry/size-data'; import { createColors } from 'mol-geo/geometry/color-data'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; @@ -32,7 +32,6 @@ import { Mat4 } from 'mol-math/linear-algebra'; import { Spheres } from 'mol-geo/geometry/spheres/spheres'; import { createUnitsTransform, includesUnitKind } from './visual/util/common'; import { Overpaint } from 'mol-theme/overpaint'; -import { applyOverpaintColor, createOverpaint, clearOverpaint } from 'mol-geo/geometry/overpaint-data'; export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup } @@ -205,6 +204,14 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry : createEmptyGeometry(geometry) } + function lociApply(loci: Loci, apply: (interval: Interval) => boolean) { + if (isEveryLoci(loci) || (Structure.isLoci(loci) && Structure.areEquivalent(loci.structure, currentStructureGroup.structure))) { + return apply(Interval.ofBounds(0, locationIt.groupCount * locationIt.instanceCount)) + } else { + return eachLocation(loci, currentStructureGroup, apply) + } + } + return { get groupCount() { return locationIt ? locationIt.count : 0 }, get renderObject () { return locationIt && locationIt.count ? renderObject : undefined }, @@ -221,26 +228,7 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry return renderObject ? getLoci(pickingId, currentStructureGroup, renderObject.id) : EmptyLoci }, 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) || (Structure.isLoci(loci) && Structure.areEquivalent(loci.structure, currentStructureGroup.structure))) { - changed = apply(Interval.ofBounds(0, groupCount * instanceCount)) - } else { - changed = eachLocation(loci, currentStructureGroup, apply) - } - if (changed) { - ValueCell.update(tMarker, tMarker.ref.value) - } - return changed + return Visual.mark(renderObject, loci, action, lociApply) }, setVisibility(visible: boolean) { Visual.setVisibility(renderObject, visible) @@ -254,32 +242,8 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) { Visual.setTransform(renderObject, matrix, instanceMatrices) }, - setOverpaint(layers: Overpaint.Layers, clear = false) { - if (!renderObject) return false - const { tOverpaint } = renderObject.values - const count = locationIt.groupCount * locationIt.instanceCount - - // ensure texture has right size - createOverpaint(layers.list.length ? count : 0, renderObject.values) - - // clear if requested - if (clear) clearOverpaint(tOverpaint.ref.value.array, 0, count) - - for (let i = 0, il = layers.list.length; i < il; ++i) { - const { loci, color } = layers.list[i] - const apply = (interval: Interval) => { - const start = Interval.start(interval) - const end = Interval.end(interval) - return applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, layers.alpha) - } - - if (isEveryLoci(loci) || (Structure.isLoci(loci) && Structure.areEquivalent(loci.structure, currentStructureGroup.structure))) { - apply(Interval.ofBounds(0, count)) - } else { - eachLocation(loci, currentStructureGroup, apply) - } - } - ValueCell.update(tOverpaint, tOverpaint.ref.value) + setOverpaint(overpaint: Overpaint) { + return Visual.setOverpaint(renderObject, overpaint, lociApply, true) }, destroy() { // TODO diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts index 9e62ed398d3827dcc4ba9ae61e3510749a24f045..92072569ebe9dae974c23f7e6966aebefdafabfe 100644 --- a/src/mol-repr/visual.ts +++ b/src/mol-repr/visual.ts @@ -8,7 +8,7 @@ import { RuntimeContext } from 'mol-task' import { GraphicsRenderObject } from 'mol-gl/render-object' import { PickingId } from '../mol-geo/geometry/picking'; import { Loci } from 'mol-model/loci'; -import { MarkerAction } from '../mol-geo/geometry/marker-data'; +import { MarkerAction, applyMarkerAction } from '../mol-geo/geometry/marker-data'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { WebGLContext } from 'mol-gl/webgl/context'; import { Theme } from 'mol-theme/theme'; @@ -17,6 +17,8 @@ import { updateTransformData, fillIdentityTransform } from 'mol-geo/geometry/tra import { calculateTransformBoundingSphere } from 'mol-gl/renderable/util'; import { ValueCell } from 'mol-util'; import { Overpaint } from 'mol-theme/overpaint'; +import { createOverpaint, clearOverpaint, applyOverpaintColor } from 'mol-geo/geometry/overpaint-data'; +import { Interval } from 'mol-data/int'; export interface VisualContext { readonly runtime: RuntimeContext @@ -36,10 +38,12 @@ interface Visual<D, P extends PD.Params> { setAlphaFactor: (alphaFactor: number) => void setPickable: (pickable: boolean) => void setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array | null) => void - setOverpaint: (layers: Overpaint.Layers, clear?: boolean) => void + setOverpaint: (overpaint: Overpaint) => void destroy: () => void } namespace Visual { + export type LociApply = (loci: Loci, apply: (interval: Interval) => boolean) => boolean + export function setVisibility(renderObject: GraphicsRenderObject | undefined, visible: boolean) { if (renderObject) renderObject.state.visible = visible } @@ -52,23 +56,63 @@ namespace Visual { if (renderObject) renderObject.state.pickable = pickable } - export function setTransform(renderObject: GraphicsRenderObject | undefined, transform?: Mat4, instanceTransforms?: Float32Array | null) { - if (renderObject && (transform || instanceTransforms)) { - const { values } = renderObject - if (transform) { - Mat4.copy(values.matrix.ref.value, transform) - ValueCell.update(values.matrix, values.matrix.ref.value) - } - if (instanceTransforms) { - values.extraTransform.ref.value.set(instanceTransforms) - ValueCell.update(values.extraTransform, values.extraTransform.ref.value) - } else if (instanceTransforms === null) { - fillIdentityTransform(values.extraTransform.ref.value, values.instanceCount.ref.value) - ValueCell.update(values.extraTransform, values.extraTransform.ref.value) + export function mark(renderObject: GraphicsRenderObject | undefined, loci: Loci, action: MarkerAction, lociApply: LociApply) { + if (!renderObject) return false + + const { tMarker } = renderObject.values + + function apply(interval: Interval) { + const start = Interval.start(interval) + const end = Interval.end(interval) + return applyMarkerAction(tMarker.ref.value.array, start, end, action) + } + + const changed = lociApply(loci, apply) + if (changed) ValueCell.update(tMarker, tMarker.ref.value) + return changed + } + + export function setOverpaint(renderObject: GraphicsRenderObject | undefined, overpaint: Overpaint, lociApply: LociApply, clear: boolean) { + if (!renderObject) return + + const { tOverpaint, uGroupCount, instanceCount } = renderObject.values + const count = uGroupCount.ref.value * instanceCount.ref.value + + // ensure texture has right size + createOverpaint(overpaint.layers.length ? count : 0, renderObject.values) + + // clear if requested + if (clear) clearOverpaint(tOverpaint.ref.value.array, 0, count) + + for (let i = 0, il = overpaint.layers.length; i < il; ++i) { + const { loci, color } = overpaint.layers[i] + const apply = (interval: Interval) => { + const start = Interval.start(interval) + const end = Interval.end(interval) + return applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, overpaint.alpha) } - updateTransformData(values) - const boundingSphere = calculateTransformBoundingSphere(values.invariantBoundingSphere.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value) - ValueCell.update(values.boundingSphere, boundingSphere) + lociApply(loci, apply) + } + ValueCell.update(tOverpaint, tOverpaint.ref.value) + } + + export function setTransform(renderObject: GraphicsRenderObject | undefined, transform?: Mat4, instanceTransforms?: Float32Array | null) { + if (!renderObject || (!transform && !instanceTransforms)) return + + const { values } = renderObject + if (transform) { + Mat4.copy(values.matrix.ref.value, transform) + ValueCell.update(values.matrix, values.matrix.ref.value) + } + if (instanceTransforms) { + values.extraTransform.ref.value.set(instanceTransforms) + ValueCell.update(values.extraTransform, values.extraTransform.ref.value) + } else if (instanceTransforms === null) { + fillIdentityTransform(values.extraTransform.ref.value, values.instanceCount.ref.value) + ValueCell.update(values.extraTransform, values.extraTransform.ref.value) } + updateTransformData(values) + const boundingSphere = calculateTransformBoundingSphere(values.invariantBoundingSphere.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value) + ValueCell.update(values.boundingSphere, boundingSphere) } } \ No newline at end of file diff --git a/src/mol-repr/volume/direct-volume.ts b/src/mol-repr/volume/direct-volume.ts index 5957435a25a47937333b2503780abc9720e37c5a..78bc897a815b03c2d2adecf9c28fdd431b9fe7f5 100644 --- a/src/mol-repr/volume/direct-volume.ts +++ b/src/mol-repr/volume/direct-volume.ts @@ -169,7 +169,7 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeParams> { createGeometry: createDirectVolume, createLocationIterator: (volume: VolumeData) => LocationIterator(1, 1, () => NullLocation), getLoci: () => EmptyLoci, - mark: () => false, + eachLocation: () => false, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<DirectVolumeParams>, currentProps: PD.Values<DirectVolumeParams>) => { }, geometryUtils: DirectVolume.Utils diff --git a/src/mol-repr/volume/isosurface.ts b/src/mol-repr/volume/isosurface.ts index 30c8ba3278f4bb26b0625de9f007c04989294675..da429a9eb4dcfc4086c13840054c78f2e542c54a 100644 --- a/src/mol-repr/volume/isosurface.ts +++ b/src/mol-repr/volume/isosurface.ts @@ -95,7 +95,7 @@ export function IsosurfaceMeshVisual(): VolumeVisual<IsosurfaceMeshParams> { createGeometry: createVolumeIsosurfaceMesh, createLocationIterator: (volume: VolumeData) => LocationIterator(1, 1, () => NullLocation), getLoci: () => EmptyLoci, - mark: () => false, + eachLocation: () => false, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IsosurfaceMeshParams>, currentProps: PD.Values<IsosurfaceMeshParams>) => { if (newProps.isoValue !== currentProps.isoValue) state.createGeometry = true }, @@ -131,7 +131,7 @@ export function IsosurfaceWireframeVisual(): VolumeVisual<IsosurfaceWireframePar createGeometry: createVolumeIsosurfaceWireframe, createLocationIterator: (volume: VolumeData) => LocationIterator(1, 1, () => NullLocation), getLoci: () => EmptyLoci, - mark: () => false, + eachLocation: () => false, setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IsosurfaceWireframeParams>, currentProps: PD.Values<IsosurfaceWireframeParams>) => { if (newProps.isoValue !== currentProps.isoValue) state.createGeometry = true }, diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts index c527c22874e8d9d667da6e405e163d03d663390f..97324e213931741ca4b903964560001247083a05 100644 --- a/src/mol-repr/volume/representation.ts +++ b/src/mol-repr/volume/representation.ts @@ -12,7 +12,7 @@ import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci'; import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { PickingId } from 'mol-geo/geometry/picking'; -import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data'; +import { MarkerAction } from 'mol-geo/geometry/marker-data'; import { GraphicsRenderObject, createRenderObject } from 'mol-gl/render-object'; import { Interval } from 'mol-data/int'; import { LocationIterator } from 'mol-geo/util/location-iterator'; @@ -43,7 +43,7 @@ interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> { createGeometry(ctx: VisualContext, volume: VolumeData, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G createLocationIterator(volume: VolumeData): LocationIterator getLoci(pickingId: PickingId, id: number): Loci - mark(loci: Loci, apply: (interval: Interval) => boolean): boolean + eachLocation(loci: Loci, apply: (interval: Interval) => boolean): boolean setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void } @@ -52,7 +52,7 @@ interface VolumeVisualGeometryBuilder<P extends VolumeParams, G extends Geometry } export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geometry.Params<G>>(builder: VolumeVisualGeometryBuilder<P, G>): VolumeVisual<P> { - const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder + const { defaultProps, createGeometry, createLocationIterator, getLoci, eachLocation, setUpdateState } = builder const { updateValues, updateBoundingSphere, updateRenderableState } = builder.geometryUtils const updateState = VisualUpdateState.create() @@ -145,6 +145,14 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet if (newGeometry) geometry = newGeometry } + function lociApply(loci: Loci, apply: (interval: Interval) => boolean) { + if (isEveryLoci(loci)) { + return apply(Interval.ofBounds(0, locationIt.groupCount * locationIt.instanceCount)) + } else { + return eachLocation(loci, apply) + } + } + return { get groupCount() { return locationIt ? locationIt.count : 0 }, get renderObject () { return renderObject }, @@ -161,26 +169,7 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet return renderObject ? getLoci(pickingId, renderObject.id) : EmptyLoci }, 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, apply) - } - if (changed) { - ValueCell.update(tMarker, tMarker.ref.value) - } - return changed + return Visual.mark(renderObject, loci, action, lociApply) }, setVisibility(visible: boolean) { Visual.setVisibility(renderObject, visible) @@ -194,8 +183,8 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) { Visual.setTransform(renderObject, matrix, instanceMatrices) }, - setOverpaint(layers: Overpaint.Layers, clear?: boolean) { - return false // TODO + setOverpaint(overpaint: Overpaint) { + return Visual.setOverpaint(renderObject, overpaint, lociApply, true) }, destroy() { // TODO @@ -257,7 +246,9 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: function setState(state: Partial<Representation.State>) { if (state.visible !== undefined && visual) visual.setVisibility(state.visible) + if (state.alphaFactor !== undefined && visual) visual.setAlphaFactor(state.alphaFactor) if (state.pickable !== undefined && visual) visual.setPickable(state.pickable) + if (state.overpaint !== undefined && visual) visual.setOverpaint(state.overpaint) if (state.transform !== undefined && visual) visual.setTransform(state.transform) Representation.updateState(_state, state) @@ -267,10 +258,6 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: _theme = theme } - function setOverpaint(layers: Overpaint.Layers) { - return visual ? visual.setOverpaint(layers) : false - } - function destroy() { if (visual) visual.destroy() } @@ -289,7 +276,6 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: createOrUpdate, setState, setTheme, - setOverpaint, getLoci, mark, destroy diff --git a/src/mol-theme/overpaint.ts b/src/mol-theme/overpaint.ts index cf61621ef8f9185e7067df84b650ffe7e916bc55..b1468590f574faca768519259cc7b003a6148d93 100644 --- a/src/mol-theme/overpaint.ts +++ b/src/mol-theme/overpaint.ts @@ -8,18 +8,20 @@ import { Loci } from 'mol-model/loci'; import { Color } from 'mol-util/color'; export { Overpaint } + +type Overpaint = { layers: ReadonlyArray<Overpaint.Layer>, readonly alpha: number } + namespace Overpaint { export type Layer = { readonly loci: Loci, readonly color: Color } - export type Layers = { list: ReadonlyArray<Layer>, readonly alpha: number } - export const EmptyLayers: Layers = { list: [], alpha: 1 } + export const Empty: Overpaint = { layers: [], alpha: 1 } - export function areEqual(layersA: Layers, layersB: Layers) { - if (layersA.list.length === 0 && layersB.list.length === 0) return true - if (layersA.list.length !== layersB.list.length) return false - if (layersA.alpha !== layersB.alpha) return false - for (let i = 0, il = layersA.list.length; i < il; ++i) { - if (layersA.list[i].color !== layersB.list[i].color) return false - if (!Loci.areEqual(layersA.list[i].loci, layersB.list[i].loci)) return false + export function areEqual(oA: Overpaint, oB: Overpaint) { + if (oA.layers.length === 0 && oB.layers.length === 0) return true + if (oA.layers.length !== oB.layers.length) return false + if (oA.alpha !== oB.alpha) return false + for (let i = 0, il = oA.layers.length; i < il; ++i) { + if (oA.layers[i].color !== oB.layers[i].color) return false + if (!Loci.areEqual(oA.layers[i].loci, oB.layers[i].loci)) return false } return true }