diff --git a/src/mol-geo/geometry/overpaint-data.ts b/src/mol-geo/geometry/overpaint-data.ts index f7f4546ccc301e0d12155c46a787120cac15b0ad..875bef81b528dd5989f394dbfd4e0cfdf53870bb 100644 --- a/src/mol-geo/geometry/overpaint-data.ts +++ b/src/mol-geo/geometry/overpaint-data.ts @@ -23,6 +23,10 @@ export function applyOverpaintColor(array: Uint8Array, start: number, end: numbe return true } +export function clearOverpaint(array: Uint8Array, start: number, end: number) { + array.fill(0, start, end) +} + export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData { const overpaint = createTextureImage(Math.max(1, count), 4, overpaintData && overpaintData.tOverpaint.ref.value.array) if (overpaintData) { diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts index 99fb4cf56adc549b2a5a26bb5d788b905794b233..097850d21756e9cb4c2c4b5d1330766f32b4dfbd 100644 --- a/src/mol-plugin/behavior/dynamic/representation.ts +++ b/src/mol-plugin/behavior/dynamic/representation.ts @@ -7,7 +7,7 @@ import { MarkerAction } from 'mol-geo/geometry/marker-data'; import { Mat4, Vec3 } from 'mol-math/linear-algebra'; -import { EmptyLoci, EveryLoci } from 'mol-model/loci'; +import { EmptyLoci } from 'mol-model/loci'; import { StructureUnitTransforms } from 'mol-model/structure/structure/util/unit-transforms'; import { PluginContext } from 'mol-plugin/context'; import { PluginStateObject } from 'mol-plugin/state/objects'; @@ -216,11 +216,28 @@ namespace ColorMappings { export namespace ColorRepresentation3D { export const Params = { - query: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname LYS))' }), - color: PD.Color(ColorNames.blueviolet) - // colorMappings: PD.Value<ColorMappings>([{ query: MS.struct.generator.atomGroups({ - // 'residue-test': MS.core.rel.eq([MS.ammp('auth_comp_id'), 'ALA']) - // }), color: ColorNames.greenyellow }], { isHidden: true }), + layers: PD.ObjectList({ + query: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname LYS))' }), + color: PD.Color(ColorNames.blueviolet) + }, e => `${Color.toRgbString(e.color)}`, { + defaultValue: [ + { + query: { + language: 'mol-script', + expression: '(sel.atom.atom-groups :residue-test (= atom.resname LYS))' + }, + color: ColorNames.blueviolet + }, + { + query: { + language: 'mol-script', + expression: '(sel.atom.atom-groups :residue-test (= atom.resname ALA))' + }, + color: ColorNames.chartreuse + } + ] + }), + alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }), } export type Params = PD.Values<typeof Params> @@ -242,44 +259,43 @@ export namespace ColorRepresentation3D { } update(params: Params): boolean { - const parsed = parseMolScript(params.query.expression); - if (parsed.length === 0) throw new Error('No query'); - const query = transpileMolScript(parsed[0]); - - return this.applyMappings([{ query, color: params.color }]) + const { layers, alpha } = params + const colorMappings: ColorMappings = [] + for (let i = 0, il = params.layers.length; i < il; ++i) { + const { query, color } = layers[i] + const parsed = parseMolScript(query.expression); + if (parsed.length === 0) throw new Error('No query'); + colorMappings.push({ query: transpileMolScript(parsed[0]), color }) + } + return this.applyMappings(colorMappings, alpha) } - private applyMappings(colorMappings: ColorMappings): boolean { + private applyMappings(colorMappings: ColorMappings, alpha: number): boolean { if (!this.updateData() && ColorMappings.areEqual(colorMappings, this.currentColorMappings)) return false; this.currentColorMappings = colorMappings; if (!this.repr.data || !this.structure.data) return true; - const layers: Overpaint.Layers = [ - // unsets the overpaint - // TODO do smarter by looking at the current color mappings - { loci: EveryLoci, color: ColorNames.black, alpha: 0 } - ] - // console.log('currentColorMappings', this.currentColorMappings) + const list: 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) - layers.push({ loci, color, alpha: 1 }) + list.push({ loci, color }) } - return this.applyLayers(layers) + return this.applyLayers({ alpha, list }, true) } - private applyLayers(layers: Overpaint.Layers): boolean { + private applyLayers(layers: Overpaint.Layers, clear: boolean): boolean { if (!this.repr.data) return true; - this.repr.data.setOverpaint(layers) + this.repr.data.setOverpaint(layers, clear) this.ctx.canvas3d.add(this.repr.data); this.ctx.canvas3d.requestDraw(true); return true; } unregister(): void { - this.applyLayers([{ loci: EveryLoci, color: ColorNames.black, alpha: 0 }]) // clear + this.applyLayers(Overpaint.EmptyLayers, true) // 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 3dbdc3f7656d8a8481ea08b29a7582d2afbf76ea..fc4316575b35cb732f4258e67a733f382706caf6 100644 --- a/src/mol-repr/representation.ts +++ b/src/mol-repr/representation.ts @@ -122,7 +122,7 @@ 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) => void + setOverpaint: (layers: Overpaint.Layers, clear?: boolean) => void getLoci: (pickingId: PickingId) => ModelLoci mark: (loci: ModelLoci, action: MarkerAction) => boolean destroy: () => void diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts index a22a0e5051c2897c9c3d2a348deeb3f77e961cdf..d64b46f74f3f54905a4b2e4fd6541f60952cc916 100644 --- a/src/mol-repr/shape/representation.ts +++ b/src/mol-repr/shape/representation.ts @@ -27,7 +27,7 @@ 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 } from 'mol-geo/geometry/overpaint-data'; +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> { } @@ -203,21 +203,27 @@ 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) { + setOverpaint(layers: Overpaint.Layers, clear?: boolean) { if (!_renderObject) return false const { tOverpaint } = _renderObject.values - const { groupCount, instanceCount } = locationIt + 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.length; i < il; ++i) { - const { loci, color, alpha } = layers[i] + 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, alpha) + return applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, layers.alpha) } if (isEveryLoci(loci) || (Shape.isLoci(loci) && loci.shape === _shape)) { - apply(Interval.ofBounds(0, groupCount * instanceCount)) + apply(Interval.ofBounds(0, count)) } else { eachShapeLocation(loci, _shape, apply) } diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts index 4f5601885a1ef71cfe8b539f28c8b2fcccd05868..35664955fb2e2aad5cdaef99657ba061dd7e4aed 100644 --- a/src/mol-repr/structure/complex-representation.ts +++ b/src/mol-repr/structure/complex-representation.ts @@ -79,8 +79,8 @@ export function ComplexRepresentation<P extends StructureParams>(label: string, _theme = theme } - function setOverpaint(layers: Overpaint.Layers) { - if (visual) visual.setOverpaint(layers) + function setOverpaint(layers: Overpaint.Layers, clear?: boolean) { + if (visual) visual.setOverpaint(layers, clear) } function destroy() { diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index eae9e3c26b47e479af0d082bc4968ba03e1fe278..72cde149fa62f183d48122e9818efb887bf4495f 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/src/mol-repr/structure/complex-visual.ts @@ -29,7 +29,7 @@ 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 } from 'mol-geo/geometry/overpaint-data'; +import { applyOverpaintColor, createOverpaint, clearOverpaint } from 'mol-geo/geometry/overpaint-data'; export interface ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { } @@ -208,24 +208,27 @@ 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) { + setOverpaint(layers: Overpaint.Layers, clear = false) { if (!renderObject) return false const { tOverpaint } = renderObject.values - const { groupCount, instanceCount } = locationIt + const count = locationIt.groupCount * locationIt.instanceCount // ensure texture has right size - createOverpaint(layers.length ? groupCount * instanceCount : 0, renderObject.values) + 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.length; i < il; ++i) { - const { loci, color, alpha } = layers[i] + 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, alpha) + 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, groupCount * instanceCount)) + apply(Interval.ofBounds(0, count)) } else { eachLocation(loci, currentStructure, apply) } diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts index 7c479fd4a70083ef9af469b8d41f116e0b781261..7d7b65d8ed4140fce1478005dc24dfc076d2ac15 100644 --- a/src/mol-repr/structure/units-representation.ts +++ b/src/mol-repr/structure/units-representation.ts @@ -194,8 +194,8 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R _theme = theme } - function setOverpaint(layers: Overpaint.Layers) { - visuals.forEach(({ visual }) => visual.setOverpaint(layers)) + function setOverpaint(layers: Overpaint.Layers, clear?: boolean) { + visuals.forEach(({ visual }) => visual.setOverpaint(layers, clear)) } function destroy() { diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index c21157b2ab36cd433b113be9009d8a116277c1c8..52ddc036b2dd94898fb777b62f0b66ea79ba31b4 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/src/mol-repr/structure/units-visual.ts @@ -32,7 +32,7 @@ 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 } from 'mol-geo/geometry/overpaint-data'; +import { applyOverpaintColor, createOverpaint, clearOverpaint } from 'mol-geo/geometry/overpaint-data'; export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup } @@ -254,24 +254,27 @@ 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) { + setOverpaint(layers: Overpaint.Layers, clear = false) { if (!renderObject) return false const { tOverpaint } = renderObject.values - const { groupCount, instanceCount } = locationIt + const count = locationIt.groupCount * locationIt.instanceCount // ensure texture has right size - createOverpaint(layers.length ? groupCount * instanceCount : 0, renderObject.values) + 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.length; i < il; ++i) { - const { loci, color, alpha } = layers[i] + 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, alpha) + 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, groupCount * instanceCount)) + apply(Interval.ofBounds(0, count)) } else { eachLocation(loci, currentStructureGroup, apply) } diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts index 5bfef7954e8e0e1c88c28873b8186e4280d76465..9e62ed398d3827dcc4ba9ae61e3510749a24f045 100644 --- a/src/mol-repr/visual.ts +++ b/src/mol-repr/visual.ts @@ -36,7 +36,7 @@ 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) => void + setOverpaint: (layers: Overpaint.Layers, clear?: boolean) => void destroy: () => void } namespace Visual { diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts index 926f70f2ed5b28455b7f6be9eea4876cf701942b..c527c22874e8d9d667da6e405e163d03d663390f 100644 --- a/src/mol-repr/volume/representation.ts +++ b/src/mol-repr/volume/representation.ts @@ -194,7 +194,7 @@ 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) { + setOverpaint(layers: Overpaint.Layers, clear?: boolean) { return false // TODO }, destroy() { diff --git a/src/mol-theme/overpaint.ts b/src/mol-theme/overpaint.ts index b512a08aef9511dd36e11f740ce6c4d8f7dfcb4d..cf61621ef8f9185e7067df84b650ffe7e916bc55 100644 --- a/src/mol-theme/overpaint.ts +++ b/src/mol-theme/overpaint.ts @@ -9,20 +9,17 @@ import { Color } from 'mol-util/color'; export { Overpaint } namespace Overpaint { - export interface Layer { - loci: Loci - color: Color - alpha: number - } - export type Layers = Layer[] + 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 function areEqual(layersA: Layers, layersB: Layers) { - if (layersA.length === 0 && layersB.length === 0) return true - if (layersA.length !== layersB.length) return false - for (let i = 0, il = layersA.length; i < il; ++i) { - if (layersA[i].alpha !== layersB[i].alpha) return false - if (layersA[i].color !== layersB[i].color) return false - if (!Loci.areEqual(layersA[i].loci, layersB[i].loci)) return false + 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 } return true }