diff --git a/package-lock.json b/package-lock.json index a6e919d8410914b5d76a52a8e4ad3b668941885d..edafd4d1f9c93c4ded380346f45198682ee709a0 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index 2a8e33f0618a18dee2d3a64009f085d4f5e82f8b..eede657509d5f0454525f990cd395429c99582ee 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@types/react-dom": "^16.0.9", "@types/webgl2": "0.0.4", "benchmark": "^2.1.4", + "circular-dependency-plugin": "^5.0.2", "cpx": "^1.5.0", "css-loader": "^1.0.1", "extra-watch-webpack-plugin": "^1.0.3", diff --git a/src/apps/canvas/app.ts b/src/apps/canvas/app.ts index 35732c7e5bdd0eeeee726fd313a95a061e1d7c39..2dd92e472497572264c5a63c230c2199aeb5766d 100644 --- a/src/apps/canvas/app.ts +++ b/src/apps/canvas/app.ts @@ -12,6 +12,10 @@ import { CifBlock } from 'mol-io/reader/cif'; import { VolumeView } from './volume-view'; import { Ccp4File } from 'mol-io/reader/ccp4/schema'; import { Progress } from 'mol-task'; +import { ColorTheme } from 'mol-theme/color'; +import { SizeTheme } from 'mol-theme/size'; +import { StructureRepresentationRegistry } from 'mol-repr/structure/registry'; +import { VolumeRepresentationRegistry } from 'mol-repr/volume/registry'; export class App { canvas3d: Canvas3D @@ -23,6 +27,11 @@ export class App { structureLoaded: BehaviorSubject<StructureView | null> = new BehaviorSubject<StructureView | null>(null) volumeLoaded: BehaviorSubject<VolumeView | null> = new BehaviorSubject<VolumeView | null>(null) + colorThemeRegistry = new ColorTheme.Registry() + sizeThemeRegistry = new SizeTheme.Registry() + structureRepresentationRegistry = new StructureRepresentationRegistry() + volumeRepresentationRegistry = new VolumeRepresentationRegistry() + initViewer(_canvas: HTMLCanvasElement, _container: HTMLDivElement) { this.canvas = _canvas this.container = _container @@ -64,6 +73,14 @@ export class App { console.log(Progress.format(progress)) } + get reprCtx () { + return { + webgl: this.canvas3d.webgl, + colorThemeRegistry: this.colorThemeRegistry, + sizeThemeRegistry: this.sizeThemeRegistry + } + } + // async loadMmcif(cif: CifBlock, assemblyId?: string) { diff --git a/src/apps/canvas/assembly-symmetry.ts b/src/apps/canvas/assembly-symmetry.ts index f4cdb2888466a56c6f9babcc311ea010249714db..e43357c196bd3f16120d60250bd10f502ce5f4e0 100644 --- a/src/apps/canvas/assembly-symmetry.ts +++ b/src/apps/canvas/assembly-symmetry.ts @@ -66,10 +66,10 @@ export function getClusterColorTheme(symmetryId: number, assemblySymmetry: Assem const DefaultColor = Color(0xCCCCCC) const s = assemblySymmetry.db.rcsb_assembly_symmetry const symmetry = Table.pickRow(s, i => s.id.value(i) === symmetryId) - if (!symmetry) return { features: {}, granularity: 'uniform', color: () => DefaultColor } + if (!symmetry) return { granularity: 'uniform', color: () => DefaultColor, props: {} } const clusters = assemblySymmetry.getClusters(symmetryId) - if (!clusters._rowCount) return { features: {}, granularity: 'uniform', color: () => DefaultColor } + if (!clusters._rowCount) return { granularity: 'uniform', color: () => DefaultColor, props: {} } const clusterByMember = new Map<string, number>() for (let i = 0, il = clusters._rowCount; i < il; ++i) { @@ -83,7 +83,6 @@ export function getClusterColorTheme(symmetryId: number, assemblySymmetry: Assem const scale = ColorScale.create({ domain: [ 0, clusters._rowCount - 1 ] }) return { - features: {}, granularity: 'instance', color: (location: Location): Color => { if (StructureElement.isLocation(location)) { @@ -94,6 +93,7 @@ export function getClusterColorTheme(symmetryId: number, assemblySymmetry: Assem return cluster !== undefined ? scale.color(cluster) : DefaultColor } return DefaultColor - } + }, + props: {} } } \ No newline at end of file diff --git a/src/apps/canvas/component/representation.tsx b/src/apps/canvas/component/representation.tsx index 0e4aedd7bbeee312a4c4a7e95a5ec1048c8aed24..7d395d5e9828882b9c26aa699339ef6a6acdac0d 100644 --- a/src/apps/canvas/component/representation.tsx +++ b/src/apps/canvas/component/representation.tsx @@ -8,16 +8,13 @@ import * as React from 'react' import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { App } from '../app'; import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { Representation } from 'mol-repr'; +import { Representation } from 'mol-repr/representation'; import { ParametersComponent } from 'mol-app/component/parameters'; -import { ColorTheme } from 'mol-theme/color'; -import { getColorThemeProps } from 'mol-geo/geometry/color-data'; -import { ColorThemeComponent } from 'mol-app/component/color-theme'; -export interface RepresentationComponentProps { +export interface RepresentationComponentProps<P extends PD.Params> { app: App canvas3d: Canvas3D - repr: Representation<PD.Params> + repr: Representation<P> } export interface RepresentationComponentState { @@ -26,13 +23,13 @@ export interface RepresentationComponentState { reprProps: Readonly<{}> } -export class RepresentationComponent extends React.Component<RepresentationComponentProps, RepresentationComponentState> { +export class RepresentationComponent<P extends PD.Params> extends React.Component<RepresentationComponentProps<P>, RepresentationComponentState> { - private stateFromRepr(repr: Representation<PD.Params>) { + private stateFromRepr(repr: Representation<P>) { return { - label: this.props.repr.label, - reprParams: this.props.repr.params, - reprProps: this.props.repr.props + label: repr.label, + reprParams: repr.params, + reprProps: repr.props } } @@ -41,21 +38,18 @@ export class RepresentationComponent extends React.Component<RepresentationCompo } async onChange(k: string, v: any) { - const ctx = { webgl: this.props.canvas3d.webgl } - await this.props.app.runTask(this.props.repr.createOrUpdate(ctx, { [k]: v }).run( + await this.props.app.runTask(this.props.repr.createOrUpdate(this.props.app.reprCtx, { [k]: v }).run( progress => this.props.app.log(progress) ), 'Representation Update') - this.props.canvas3d.add(this.props.repr) - this.props.canvas3d.requestDraw(true) this.setState(this.stateFromRepr(this.props.repr)) } render() { const { label, reprParams, reprProps } = this.state - let colorTheme: ColorTheme | undefined = undefined - if ('colorTheme' in reprProps) { - colorTheme = ColorTheme(getColorThemeProps(reprProps)) - } + // let colorTheme: ColorTheme | undefined = undefined + // if ('colorTheme' in reprProps) { + // colorTheme = ColorTheme(getColorThemeProps(reprProps)) + // } return <div> <div> @@ -68,7 +62,7 @@ export class RepresentationComponent extends React.Component<RepresentationCompo onChange={(k, v) => this.onChange(k as string, v)} /> </div> - { colorTheme !== undefined ? <ColorThemeComponent colorTheme={colorTheme} /> : '' } + {/* { colorTheme !== undefined ? <ColorThemeComponent colorTheme={colorTheme} /> : '' } */} </div>; } } \ No newline at end of file diff --git a/src/apps/canvas/component/structure-view.tsx b/src/apps/canvas/component/structure-view.tsx index 9b8fd7d785b34911588028d951b22b6224668994..6cd0828674ea1d63321f71e07b944c023bfa8614 100644 --- a/src/apps/canvas/component/structure-view.tsx +++ b/src/apps/canvas/component/structure-view.tsx @@ -7,8 +7,8 @@ import * as React from 'react' import { StructureView } from '../structure-view'; import { RepresentationComponent } from './representation'; -import { Representation } from 'mol-repr'; -import { StructureRepresentation } from 'mol-repr/structure/index'; +import { Representation } from 'mol-repr/representation'; +import { StructureRepresentation } from 'mol-repr/structure/representation'; export interface StructureViewComponentProps { structureView: StructureView @@ -37,6 +37,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone structureView: sv, label: sv.label, + structure: sv.structure, modelId: sv.modelId, modelIds: sv.getModelIds(), assemblyId: sv.assemblyId, diff --git a/src/apps/canvas/component/volume-view.tsx b/src/apps/canvas/component/volume-view.tsx index 159a0d2176dc108377d34cfa1147038d75bbb852..6a9f309612f8ac006a8aff94e8f7d403e197c9f6 100644 --- a/src/apps/canvas/component/volume-view.tsx +++ b/src/apps/canvas/component/volume-view.tsx @@ -6,9 +6,9 @@ import * as React from 'react' import { RepresentationComponent } from './representation'; -import { Representation } from 'mol-repr'; +import { Representation } from 'mol-repr/representation'; import { VolumeView } from '../volume-view'; -import { VolumeRepresentation } from 'mol-repr/volume/index'; +import { VolumeRepresentation } from 'mol-repr/volume/representation'; export interface VolumeViewComponentProps { volumeView: VolumeView @@ -28,6 +28,7 @@ export class VolumeViewComponent extends React.Component<VolumeViewComponentProp return { volumeView: vv, label: vv.label, + volume: vv.volume, active: vv.active, volumeRepresentations: vv.volumeRepresentations } diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index 4ba79d8bb50992aed72744b6349f6475c8e52170..33a4f9faab2fffc3a19cdf3d8193eb741afb0862 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -17,15 +17,8 @@ import { Canvas3D } from 'mol-canvas3d/canvas3d'; // import { addBoundingBox } from 'mol-geo/mesh/builder/bounding-box'; import { BehaviorSubject } from 'rxjs'; import { App } from './app'; -import { StructureRepresentation } from 'mol-repr/structure/index'; -import { ShapeRepresentation, ShapeProps } from 'mol-repr/shape/index'; -import { CartoonRepresentation } from 'mol-repr/structure/representation/cartoon'; -import { MolecularSurfaceRepresentation } from 'mol-repr/structure/representation/molecular-surface'; -import { PointRepresentation } from 'mol-repr/structure/representation/point'; -import { BallAndStickRepresentation } from 'mol-repr/structure/representation/ball-and-stick'; -import { CarbohydrateRepresentation } from 'mol-repr/structure/representation/carbohydrate'; -import { SpacefillRepresentation } from 'mol-repr/structure/representation/spacefill'; -import { DistanceRestraintRepresentation } from 'mol-repr/structure/representation/distance-restraint'; +import { StructureRepresentation } from 'mol-repr/structure/representation'; +import { ShapeRepresentation, ShapeParams } from 'mol-repr/shape/representation'; export interface StructureView { readonly app: App @@ -39,7 +32,7 @@ export interface StructureView { readonly active: { [k: string]: boolean } readonly structureRepresentations: { [k: string]: StructureRepresentation<any> } readonly updated: BehaviorSubject<null> - readonly symmetryAxes: ShapeRepresentation<ShapeProps> + readonly symmetryAxes: ShapeRepresentation<ShapeParams> setSymmetryAxes(value: boolean): void setStructureRepresentation(name: string, value: boolean): void @@ -65,26 +58,18 @@ interface StructureViewProps { export async function StructureView(app: App, canvas3d: Canvas3D, models: ReadonlyArray<Model>, props: StructureViewProps = {}): Promise<StructureView> { const active: { [k: string]: boolean } = { - cartoon: true, - point: false, - surface: false, - ballAndStick: false, - carbohydrate: false, - spacefill: false, - distanceRestraint: false, - symmetryAxes: true, + 'cartoon': true, + 'ball-and-stick': true, + // point: false, + // surface: false, + // carbohydrate: false, + // spacefill: false, + // distanceRestraint: false, + // symmetryAxes: true, // polymerSphere: false, } - const structureRepresentations: { [k: string]: StructureRepresentation<any> } = { - cartoon: CartoonRepresentation(), - surface: MolecularSurfaceRepresentation(), - point: PointRepresentation(), - ballAndStick: BallAndStickRepresentation(), - carbohydrate: CarbohydrateRepresentation(), - spacefill: SpacefillRepresentation(), - distanceRestraint: DistanceRestraintRepresentation(), - } + const structureRepresentations: { [k: string]: StructureRepresentation<any> } = {} const symmetryAxes = ShapeRepresentation() const polymerSphere = ShapeRepresentation() @@ -206,14 +191,25 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon async function createStructureRepr() { if (structure) { console.log('createStructureRepr') - for (const k in structureRepresentations) { + for (const k in active) { if (active[k]) { - await app.runTask(structureRepresentations[k].createOrUpdate({ webgl: canvas3d.webgl }, {}, structure).run( + let repr: StructureRepresentation + if (structureRepresentations[k]) { + repr = structureRepresentations[k] + } else { + repr = app.structureRepresentationRegistry.create(k, app.reprCtx, structure) + structureRepresentations[k] = repr + canvas3d.add(repr) + } + await app.runTask(repr.createOrUpdate(app.reprCtx, {}, {}, structure).run( progress => app.log(progress) ), 'Create/update representation') - canvas3d.add(structureRepresentations[k]) } else { - canvas3d.remove(structureRepresentations[k]) + if (structureRepresentations[k]) { + canvas3d.remove(structureRepresentations[k]) + structureRepresentations[k].destroy() + delete structureRepresentations[k] + } } } @@ -264,7 +260,7 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon // colorFunction: colorTheme.color, // colorGranularity: colorTheme.granularity, // }).run() - await symmetryAxes.createOrUpdate({ webgl: canvas3d.webgl }, {}, axesShape).run() + await symmetryAxes.createOrUpdate(app.reprCtx, {}, {}, axesShape).run() canvas3d.add(symmetryAxes) } else { canvas3d.remove(symmetryAxes) diff --git a/src/apps/canvas/volume-view.ts b/src/apps/canvas/volume-view.ts index 9d7b8cd1c56372db40d847f429e42a64a7c17861..850cba42831ac8cc7bb4a2d55d97a08cc9ae503f 100644 --- a/src/apps/canvas/volume-view.ts +++ b/src/apps/canvas/volume-view.ts @@ -8,7 +8,7 @@ import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { BehaviorSubject } from 'rxjs'; import { App } from './app'; import { VolumeData } from 'mol-model/volume'; -import { VolumeRepresentation } from 'mol-repr/volume/index'; +import { VolumeRepresentation } from 'mol-repr/volume/representation'; import { IsosurfaceRepresentation } from 'mol-repr/volume/isosurface-mesh'; import { DirectVolumeRepresentation } from 'mol-repr/volume/direct-volume'; @@ -54,7 +54,7 @@ export async function VolumeView(app: App, viewer: Canvas3D, volume: VolumeData, async function createVolumeRepr() { for (const k in volumeRepresentations) { if (active[k]) { - await app.runTask(volumeRepresentations[k].createOrUpdate({ webgl: viewer.webgl }, {}, volume).run( + await app.runTask(volumeRepresentations[k].createOrUpdate(app.reprCtx, {}, {}, volume).run( progress => app.log(progress) ), 'Create/update representation') viewer.add(volumeRepresentations[k]) diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 3151bab2fc748a979fdc4ee5674850919c04c63a..e803e1e18137fb68787e7cc4d36db8507eb2201e 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, Subscription } from 'rxjs'; import { now } from 'mol-util/now'; import { Vec3 } from 'mol-math/linear-algebra' @@ -17,7 +17,7 @@ import TrackballControls from './controls/trackball' import { Viewport } from './camera/util' import { resizeCanvas } from './util'; import { createContext, getGLContext, WebGLContext } from 'mol-gl/webgl/context'; -import { Representation } from 'mol-repr'; +import { Representation } from 'mol-repr/representation'; import { createRenderTarget } from 'mol-gl/webgl/render-target'; import Scene from 'mol-gl/scene'; import { RenderVariant } from 'mol-gl/webgl/render-item'; @@ -41,11 +41,11 @@ export { Canvas3D } interface Canvas3D { readonly webgl: WebGLContext, - hide: (repr: Representation<any>) => void - show: (repr: Representation<any>) => void + hide: (repr: Representation.Any) => void + show: (repr: Representation.Any) => void - add: (repr: Representation<any>) => void - remove: (repr: Representation<any>) => void + add: (repr: Representation.Any) => void + remove: (repr: Representation.Any) => void update: () => void clear: () => void @@ -54,8 +54,8 @@ interface Canvas3D { animate: () => void pick: () => void identify: (x: number, y: number) => Promise<PickingId | undefined> - mark: (loci: Loci, action: MarkerAction) => void - getLoci: (pickingId: PickingId) => { loci: Loci, repr?: Representation<any> } + mark: (loci: Loci, action: MarkerAction, repr?: Representation.Any) => void + getLoci: (pickingId: PickingId) => { loci: Loci, repr?: Representation.Any } readonly didDraw: BehaviorSubject<now.Timestamp> @@ -77,7 +77,9 @@ namespace Canvas3D { export function create(canvas: HTMLCanvasElement, container: Element, props: Partial<Canvas3DProps> = {}): Canvas3D { const p = { ...props, ...DefaultCanvas3DProps } - const reprMap = new Map<Representation<any>, Set<RenderObject>>() + const reprRenderObjects = new Map<Representation.Any, Set<RenderObject>>() + const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>() + const reprCount = new BehaviorSubject(0) const startTime = now() const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp) @@ -120,7 +122,7 @@ namespace Canvas3D { function getLoci(pickingId: PickingId) { let loci: Loci = EmptyLoci let repr: Representation.Any = Representation.Empty - reprMap.forEach((_, _repr) => { + reprRenderObjects.forEach((_, _repr) => { const _loci = _repr.getLoci(pickingId) if (!isEmptyLoci(_loci)) { if (!isEmptyLoci(loci)) console.warn('found another loci') @@ -131,10 +133,12 @@ namespace Canvas3D { return { loci, repr } } - function mark(loci: Loci, action: MarkerAction) { + function mark(loci: Loci, action: MarkerAction, repr?: Representation.Any) { let changed = false - reprMap.forEach((roSet, repr) => { - changed = repr.mark(loci, action) || changed + reprRenderObjects.forEach((_, _repr) => { + if (!repr || repr === _repr) { + changed = _repr.mark(loci, action) || changed + } }) if (changed) { // console.log('changed') @@ -270,45 +274,57 @@ namespace Canvas3D { } } + function add(repr: Representation.Any) { + const oldRO = reprRenderObjects.get(repr) + const newRO = new Set<RenderObject>() + repr.renderObjects.forEach(o => newRO.add(o)) + if (oldRO) { + SetUtils.difference(newRO, oldRO).forEach(o => scene.add(o)) + SetUtils.difference(oldRO, newRO).forEach(o => scene.remove(o)) + scene.update() + } else { + repr.renderObjects.forEach(o => scene.add(o)) + } + reprRenderObjects.set(repr, newRO) + reprCount.next(reprRenderObjects.size) + scene.update() + requestDraw(true) + } + handleResize() return { webgl, - hide: (repr: Representation<any>) => { - const renderObjectSet = reprMap.get(repr) + hide: (repr: Representation.Any) => { + const renderObjectSet = reprRenderObjects.get(repr) if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = false) }, - show: (repr: Representation<any>) => { - const renderObjectSet = reprMap.get(repr) + show: (repr: Representation.Any) => { + const renderObjectSet = reprRenderObjects.get(repr) if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = true) }, - add: (repr: Representation<any>) => { - const oldRO = reprMap.get(repr) - const newRO = new Set<RenderObject>() - repr.renderObjects.forEach(o => newRO.add(o)) - if (oldRO) { - SetUtils.difference(newRO, oldRO).forEach(o => scene.add(o)) - SetUtils.difference(oldRO, newRO).forEach(o => scene.remove(o)) - scene.update() - } else { - repr.renderObjects.forEach(o => scene.add(o)) - } - reprMap.set(repr, newRO) - scene.update() + add: (repr: Representation.Any) => { + add(repr) + reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => add(repr))) }, - remove: (repr: Representation<any>) => { - const renderObjectSet = reprMap.get(repr) - if (renderObjectSet) { - renderObjectSet.forEach(o => scene.remove(o)) - reprMap.delete(repr) + remove: (repr: Representation.Any) => { + const updatedSubscription = reprUpdatedSubscriptions.get(repr) + if (updatedSubscription) { + updatedSubscription.unsubscribe() + } + const renderObjects = reprRenderObjects.get(repr) + if (renderObjects) { + renderObjects.forEach(o => scene.remove(o)) + reprRenderObjects.delete(repr) + reprCount.next(reprRenderObjects.size) scene.update() } }, update: () => scene.update(), clear: () => { - reprMap.clear() + reprRenderObjects.clear() scene.clear() }, diff --git a/src/mol-geo/geometry/color-data.ts b/src/mol-geo/geometry/color-data.ts index d9d798acc569fbbace36806b517015e0690e11fd..fe85ae1bc6e206d2a03a0c57aaf68830d3446894 100644 --- a/src/mol-geo/geometry/color-data.ts +++ b/src/mol-geo/geometry/color-data.ts @@ -6,14 +6,13 @@ import { ValueCell } from 'mol-util'; import { TextureImage, createTextureImage } from 'mol-gl/renderable/util'; -import { Color, ColorMap } from 'mol-util/color'; +import { Color } from 'mol-util/color'; import { Vec2, Vec3 } from 'mol-math/linear-algebra'; import { LocationIterator } from '../util/location-iterator'; import { NullLocation } from 'mol-model/location'; -import { LocationColor, ColorThemeProps, ColorTheme, ColorThemeName, ScaleLegend, TableLegend, ColorScaleName, getColorScaleFromName } from 'mol-theme/color'; +import { LocationColor, ColorTheme } from 'mol-theme/color'; import { RuntimeContext } from 'mol-task'; import { getGranularity } from './geometry'; -import { Structure } from 'mol-model/structure'; export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' @@ -25,37 +24,6 @@ export type ColorData = { dColorType: ValueCell<string>, } -export interface ColorProps { - colorTheme: ColorThemeName - colorList?: Color[] | ColorScaleName - colorMap?: ColorMap<any> - colorDomain?: [number, number] - colorValue?: Color - colorFunction?: LocationColor, - colorGranularity?: ColorType, - colorDescription?: string, - colorLegend?: ScaleLegend | TableLegend - structure?: Structure -} - -export function getColorThemeProps(props: ColorProps): ColorThemeProps { - const p: ColorThemeProps = { - name: props.colorTheme - } - if (props.colorDomain !== undefined) p.domain = props.colorDomain - if (props.colorList !== undefined) { - p.list = typeof props.colorList === 'string' ? getColorScaleFromName(props.colorList) : props.colorList - } - if (props.colorMap !== undefined) p.map = props.colorMap - if (props.colorValue !== undefined) p.value = props.colorValue - if (props.structure !== undefined) p.structure = props.structure - if (props.colorFunction !== undefined) p.color = props.colorFunction - if (props.colorGranularity !== undefined) p.granularity = props.colorGranularity - if (props.colorDescription !== undefined) p.description = props.colorDescription - if (props.colorLegend !== undefined) p.legend = props.colorLegend - return p -} - export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, colorTheme: ColorTheme, colorData?: ColorData): Promise<ColorData> { switch (getGranularity(locationIt, colorTheme.granularity)) { case 'uniform': return createUniformColor(ctx, locationIt, colorTheme.color, colorData) diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index 1e6575fbf7ea4bab48105808a5e02b754bf1f0c9..50dfc577d7e0cca54bd64994313d39bdc6d0d0af 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -17,9 +17,10 @@ import { LocationIterator } from 'mol-geo/util/location-iterator'; import { TransformData } from '../transform-data'; import { createColors } from '../color-data'; import { createMarkers } from '../marker-data'; -import { Geometry, Theme } from '../geometry'; +import { Geometry } from '../geometry'; import { transformPositionArray } from 'mol-geo/util'; import { calculateBoundingSphere } from 'mol-gl/renderable/util'; +import { Theme } from 'mol-theme/theme'; const VolumeBox = Box() const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][] diff --git a/src/mol-geo/geometry/direct-volume/transfer-function.ts b/src/mol-geo/geometry/direct-volume/transfer-function.ts index 1679daa27fe7867c4e128141509e5bdd362f4a41..e93515f9c3e814445f6701078fb4dddcb13a83e2 100644 --- a/src/mol-geo/geometry/direct-volume/transfer-function.ts +++ b/src/mol-geo/geometry/direct-volume/transfer-function.ts @@ -29,7 +29,7 @@ export function createTransferFunctionTexture(controlPoints: ControlPoint[], tex ] const scale = ColorScale.create({ domain: [0, 1], - list: ColorMatplotlib.viridis + listOrName: ColorMatplotlib.viridis }) const n = 256 diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts index 2bf275c6a9590a367dcbdf54fcb75e886fa87985..37cdf4b43955673e25acfdef657fcc058de5eebe 100644 --- a/src/mol-geo/geometry/geometry.ts +++ b/src/mol-geo/geometry/geometry.ts @@ -9,15 +9,16 @@ import { Points } from './points/points'; import { RenderableState } from 'mol-gl/renderable'; import { ValueCell } from 'mol-util'; import { BaseValues } from 'mol-gl/renderable/schema'; -import { Color } from 'mol-util/color'; -import { ColorThemeOptions, ColorThemeName, ColorScaleOptions, ColorScaleName, ColorTheme } from 'mol-theme/color'; import { LocationIterator } from '../util/location-iterator'; -import { ColorType, getColorThemeProps } from './color-data'; -import { SizeType, getSizeThemeProps } from './size-data'; +import { ColorType } from './color-data'; +import { SizeType } from './size-data'; import { Lines } from './lines/lines'; import { ParamDefinition as PD } from 'mol-util/param-definition' import { DirectVolume } from './direct-volume/direct-volume'; -import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; +import { BuiltInSizeThemeOptions, BuiltInSizeThemeName } from 'mol-theme/size'; +import { BuiltInColorThemeName, BuiltInColorThemeOptions } from 'mol-theme/color'; +import { Color } from 'mol-util/color'; +import { Vec3 } from 'mol-math/linear-algebra'; // @@ -36,18 +37,6 @@ export type VisualQuality = keyof typeof VisualQualityInfo export const VisualQualityNames = Object.keys(VisualQualityInfo) export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [VisualQuality, string]) -export interface Theme { - color: ColorTheme - size: SizeTheme -} - -export function createTheme(props: Geometry.Props) { - return { - color: ColorTheme(getColorThemeProps(props)), - size: SizeTheme(getSizeThemeProps(props)) - } -} - // export type GeometryKindType = { @@ -76,15 +65,13 @@ export namespace Geometry { visible: PD.Boolean('Visible', '', true), depthMask: PD.Boolean('Depth Mask', '', true), useFog: PD.Boolean('Use Fog', '', false), - quality: PD.Select<VisualQuality>('Quality', '', 'auto', VisualQualityOptions), + highlightColor: PD.Color('Highlight Color', '', Color.fromNormalizedRgb(1.0, 0.4, 0.6)), + selectColor: PD.Color('Select Color', '', Color.fromNormalizedRgb(0.2, 1.0, 0.1)), - colorTheme: PD.Select<ColorThemeName>('Color Name', '', 'uniform', ColorThemeOptions), - colorList: PD.Select<ColorScaleName>('Color Scale', '', 'default', ColorScaleOptions), - colorValue: PD.Color('Color Value', '', Color(0xCCCCCC)), + quality: PD.Select<VisualQuality>('Quality', '', 'auto', VisualQualityOptions), - sizeTheme: PD.Select<SizeThemeName>('Size Name', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1), - sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1), + colorTheme: PD.Select<BuiltInColorThemeName>('Color Name', '', 'uniform', BuiltInColorThemeOptions), + sizeTheme: PD.Select<BuiltInSizeThemeName>('Size Name', '', 'uniform', BuiltInSizeThemeOptions), } export const DefaultProps = PD.getDefaultValues(Params) export type Props = typeof DefaultProps @@ -94,6 +81,8 @@ export namespace Geometry { export function createValues(props: Props, counts: Counts) { return { uAlpha: ValueCell.create(props.alpha), + uHighlightColor: ValueCell.create(Color.toArrayNormalized(props.highlightColor, Vec3.zero(), 0)), + uSelectColor: ValueCell.create(Color.toArrayNormalized(props.selectColor, Vec3.zero(), 0)), uGroupCount: ValueCell.create(counts.groupCount), drawCount: ValueCell.create(counts.drawCount), dUseFog: ValueCell.create(props.useFog), @@ -101,6 +90,12 @@ export namespace Geometry { } export function updateValues(values: BaseValues, props: Props) { + if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) { + ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0)) + } + if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) { + ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0)) + } ValueCell.updateIfChanged(values.uAlpha, props.alpha) ValueCell.updateIfChanged(values.dUseFog, props.useFog) } diff --git a/src/mol-geo/geometry/lines/lines.ts b/src/mol-geo/geometry/lines/lines.ts index 54954156927bbeeed26faf7d6b57115a64ad1256..4c5a48a7313b0a431ba858066795b32a77a2d1e5 100644 --- a/src/mol-geo/geometry/lines/lines.ts +++ b/src/mol-geo/geometry/lines/lines.ts @@ -7,7 +7,7 @@ import { ValueCell } from 'mol-util' import { Mat4 } from 'mol-math/linear-algebra' import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util'; -import { Geometry, Theme } from '../geometry'; +import { Geometry } from '../geometry'; import { RuntimeContext } from 'mol-task'; import { createColors } from '../color-data'; import { createMarkers } from '../marker-data'; @@ -20,6 +20,7 @@ import { LinesBuilder } from './lines-builder'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { calculateBoundingSphere } from 'mol-gl/renderable/util'; import { Sphere3D } from 'mol-math/geometry'; +import { Theme } from 'mol-theme/theme'; /** Wide line */ export interface Lines { diff --git a/src/mol-geo/geometry/mesh/mesh.ts b/src/mol-geo/geometry/mesh/mesh.ts index 9d4ca0deba32cafc054e75dce3b86452c416eba7..0c1a8f5cf5556010e58731ced5275e18481845ed 100644 --- a/src/mol-geo/geometry/mesh/mesh.ts +++ b/src/mol-geo/geometry/mesh/mesh.ts @@ -9,8 +9,7 @@ import { ValueCell } from 'mol-util' import { Vec3, Mat4 } from 'mol-math/linear-algebra' import { Sphere3D } from 'mol-math/geometry' import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util'; -import { MeshValues } from 'mol-gl/renderable'; -import { Geometry, Theme } from '../geometry'; +import { Geometry } from '../geometry'; import { createMarkers } from '../marker-data'; import { TransformData } from '../transform-data'; import { LocationIterator } from '../../util/location-iterator'; @@ -18,6 +17,8 @@ import { createColors } from '../color-data'; import { ChunkedArray } from 'mol-data/util'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { calculateBoundingSphere } from 'mol-gl/renderable/util'; +import { Theme } from 'mol-theme/theme'; +import { MeshValues } from 'mol-gl/renderable/mesh'; export interface Mesh { readonly kind: 'mesh', diff --git a/src/mol-geo/geometry/points/points.ts b/src/mol-geo/geometry/points/points.ts index 84a191bf7559f158dc926384f9d44200c4902270..616e7e057ed2e98404bb6185d41e127ba2fc08c9 100644 --- a/src/mol-geo/geometry/points/points.ts +++ b/src/mol-geo/geometry/points/points.ts @@ -7,8 +7,7 @@ import { ValueCell } from 'mol-util' import { Mat4 } from 'mol-math/linear-algebra' import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util'; -import { Geometry, Theme } from '../geometry'; -import { PointsValues } from 'mol-gl/renderable'; +import { Geometry } from '../geometry'; import { RuntimeContext } from 'mol-task'; import { createColors } from '../color-data'; import { createMarkers } from '../marker-data'; @@ -18,6 +17,8 @@ import { LocationIterator } from '../../util/location-iterator'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { calculateBoundingSphere } from 'mol-gl/renderable/util'; import { Sphere3D } from 'mol-math/geometry'; +import { Theme } from 'mol-theme/theme'; +import { PointsValues } from 'mol-gl/renderable/points'; /** Point cloud */ export interface Points { diff --git a/src/mol-geo/geometry/size-data.ts b/src/mol-geo/geometry/size-data.ts index 06cdcef0c6d48ef6d47a333bf9fc58edb3eb14c1..6b791ed0f5839d00c4c10ccdcaa8f7aff90b6840 100644 --- a/src/mol-geo/geometry/size-data.ts +++ b/src/mol-geo/geometry/size-data.ts @@ -10,9 +10,8 @@ import { TextureImage, createTextureImage } from 'mol-gl/renderable/util'; import { LocationIterator } from '../util/location-iterator'; import { Location, NullLocation } from 'mol-model/location'; import { RuntimeContext } from 'mol-task'; -import { SizeThemeProps, SizeTheme, SizeThemeName } from 'mol-theme/size'; +import { SizeTheme } from 'mol-theme/size'; import { getGranularity } from './geometry'; -import { Structure } from 'mol-model/structure'; export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance' @@ -24,22 +23,6 @@ export type SizeData = { dSizeType: ValueCell<string>, } -export interface SizeProps { - sizeTheme: SizeThemeName - sizeValue?: number - sizeFactor?: number - structure?: Structure -} - -export function getSizeThemeProps(props: SizeProps): SizeThemeProps { - return { - name: props.sizeTheme, - value: props.sizeValue, - factor: props.sizeFactor, - structure: props.structure, - } -} - export async function createSizes(ctx: RuntimeContext, locationIt: LocationIterator, sizeTheme: SizeTheme, sizeData?: SizeData): Promise<SizeData> { switch (getGranularity(locationIt, sizeTheme.granularity)) { case 'uniform': return createUniformSize(ctx, locationIt, sizeTheme.size, sizeData) diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index 06cf3256925a27bc717b7529cb3529368bdec630..51552ebf2dac61950224535024a725c0574b78d6 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -69,6 +69,8 @@ function createPoints() { ...size, uAlpha: ValueCell.create(1.0), + uHighlightColor: ValueCell.create(Vec3.create(1.0, 0.4, 0.6)), + uSelectColor: ValueCell.create(Vec3.create(0.2, 1.0, 0.1)), uInstanceCount: ValueCell.create(1), uGroupCount: ValueCell.create(3), diff --git a/src/mol-gl/render-object.ts b/src/mol-gl/render-object.ts index 84e892862ffb3bb336c6ed7b716a124cb106a0d3..f4761dea481de4ed1278c44b68947e64d6eb31e1 100644 --- a/src/mol-gl/render-object.ts +++ b/src/mol-gl/render-object.ts @@ -4,12 +4,15 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { PointsRenderable, MeshRenderable, RenderableState, MeshValues, PointsValues, LinesValues, LinesRenderable, Renderable } from './renderable' +import { RenderableState, Renderable } from './renderable' import { RenderableValues } from './renderable/schema'; import { idFactory } from 'mol-util/id-factory'; import { WebGLContext } from './webgl/context'; import { GaussianDensityValues, GaussianDensityRenderable } from './renderable/gaussian-density'; import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume'; +import { MeshValues, MeshRenderable } from './renderable/mesh'; +import { PointsValues, PointsRenderable } from './renderable/points'; +import { LinesValues, LinesRenderable } from './renderable/lines'; const getNextId = idFactory(0, 0x7FFFFFFF) diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts index 8512933e0d78e1beea97d2ba39da357f7bdf8322..370437898cfee60a0df4e37d155640412c21a6fb 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -46,8 +46,4 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem: update: () => renderItem.update(), dispose: () => renderItem.destroy() } -} - -export { MeshRenderable, MeshSchema, MeshValues } from './renderable/mesh' -export { PointsRenderable, PointsSchema, PointsValues } from './renderable/points' -export { LinesRenderable, LinesSchema, LinesValues } from './renderable/lines' \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index 765f6dc2e237c9a383a5e8163f8b79e248436f54..bac604bea5230fd1a8f6eb2d8f453d14a9247d00 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -146,9 +146,6 @@ export const GlobalUniformSchema = { uViewportHeight: UniformSpec('f'), uViewport: UniformSpec('v4'), - uHighlightColor: UniformSpec('v3'), - uSelectColor: UniformSpec('v3'), - uFogNear: UniformSpec('f'), uFogFar: UniformSpec('f'), uFogColor: UniformSpec('v3'), @@ -193,6 +190,8 @@ export const BaseSchema = { uInstanceCount: UniformSpec('i'), uGroupCount: UniformSpec('i'), uMarkerTexDim: UniformSpec('v2'), + uHighlightColor: UniformSpec('v3'), + uSelectColor: UniformSpec('v3'), tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'), diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 9050d0cf5e0669988e7eb6cd60dc74bd6ae21a8f..f5ec7890e703c217ed8529e6b29d7bb353c7fe01 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -60,8 +60,6 @@ namespace Renderer { // const lightPosition = Vec3.create(0, 0, -100) const lightColor = Vec3.create(1.0, 1.0, 1.0) const lightAmbient = Vec3.create(0.5, 0.5, 0.5) - const highlightColor = Vec3.create(1.0, 0.4, 0.6) - const selectColor = Vec3.create(0.2, 1.0, 0.1) const fogColor = Vec3.create(0.0, 0.0, 0.0) function setClearColor(color: Color) { @@ -97,9 +95,6 @@ namespace Renderer { uLightColor: ValueCell.create(Vec3.clone(lightColor)), uLightAmbient: ValueCell.create(Vec3.clone(lightAmbient)), - uHighlightColor: ValueCell.create(Vec3.clone(highlightColor)), - uSelectColor: ValueCell.create(Vec3.clone(selectColor)), - uFogNear: ValueCell.create(camera.state.near), uFogFar: ValueCell.create(camera.state.far / 50), uFogColor: ValueCell.create(Vec3.clone(fogColor)), diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts index 2f0a9f0a73b3b1e48bed20f747e126505c8c4689..4d2e8f8c832270c0b2ae4c23494cb597c06bcf22 100644 --- a/src/mol-gl/webgl/context.ts +++ b/src/mol-gl/webgl/context.ts @@ -199,7 +199,7 @@ export function createContext(gl: GLRenderingContext): WebGLContext { const pbo = gl.createBuffer() readPixelsAsync = async (x: number, y: number, width: number, height: number, buffer: Uint8Array) => { gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo) - gl.bufferData(gl.PIXEL_PACK_BUFFER, width * height * 4, gl.STATIC_COPY) + gl.bufferData(gl.PIXEL_PACK_BUFFER, width * height * 4, gl.STREAM_READ) gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, 0) gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null) // need to unbind/bind PBO before/after async awaiting the fence diff --git a/src/mol-model/structure/structure/unit/gaussian-density.ts b/src/mol-model/structure/structure/unit/gaussian-density.ts index 9e63a7ee5e2d3600260e94dd18c7388438aa7725..0acfa52fdea9448162cfda3b52fd94aeb2367424 100644 --- a/src/mol-model/structure/structure/unit/gaussian-density.ts +++ b/src/mol-model/structure/structure/unit/gaussian-density.ts @@ -5,7 +5,6 @@ */ import { Unit, StructureElement, ElementIndex } from 'mol-model/structure'; -import { SizeTheme } from 'mol-theme/size'; import { GaussianDensity } from 'mol-math/geometry/gaussian-density'; import { Task, RuntimeContext } from 'mol-task'; import { DensityData } from 'mol-math/geometry'; @@ -13,6 +12,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; import { GaussianDensityTexture } from 'mol-math/geometry/gaussian-density/gpu'; import { Texture } from 'mol-gl/webgl/texture'; import { WebGLContext } from 'mol-gl/webgl/context'; +import { PhysicalSizeTheme } from 'mol-theme/size/physical'; export const GaussianDensityParams = { resolution: PD.Numeric('Resolution', '', 1, 0.1, 10, 0.1), @@ -43,7 +43,7 @@ function getConformationAndRadius(unit: Unit) { } const l = StructureElement.create(unit) - const sizeTheme = SizeTheme({ name: 'physical' }) + const sizeTheme = PhysicalSizeTheme({}, {}) const radius = (index: number) => { l.element = index as ElementIndex return sizeTheme.size(l) diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 77bc40d00b94d5f7d1402e5a6ccdfd69b3ebaf0c..3a80ae51ce42397b2371fd339113f6816ae21469 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -15,7 +15,7 @@ import { Task } from 'mol-task'; import { merge } from 'rxjs'; import { PluginBehaviors, BuiltInPluginBehaviors } from './behavior'; import { Loci, EmptyLoci } from 'mol-model/loci'; -import { Representation } from 'mol-repr'; +import { Representation } from 'mol-repr/representation'; import { CreateStructureFromPDBe } from './state/actions/basic'; import { LogEntry } from 'mol-util/log-entry'; import { TaskManager } from './util/task-manager'; diff --git a/src/mol-plugin/state/objects.ts b/src/mol-plugin/state/objects.ts index 337fad1e059ff731056e61fe4a9647909c9289ef..24347d8c0a1d1856036b4ee43286a3b9aaa5218f 100644 --- a/src/mol-plugin/state/objects.ts +++ b/src/mol-plugin/state/objects.ts @@ -8,9 +8,9 @@ import { CifFile } from 'mol-io/reader/cif'; import { Model as _Model, Structure as _Structure } from 'mol-model/structure'; import { VolumeData } from 'mol-model/volume'; import { PluginBehavior } from 'mol-plugin/behavior/behavior'; -import { Representation } from 'mol-repr'; -import { StructureRepresentation } from 'mol-repr/structure/index'; -import { VolumeRepresentation } from 'mol-repr/volume'; +import { Representation } from 'mol-repr/representation'; +import { StructureRepresentation } from 'mol-repr/structure/representation'; +import { VolumeRepresentation } from 'mol-repr/volume/representation'; import { StateObject, Transformer } from 'mol-state'; export type TypeClass = 'root' | 'data' | 'prop' diff --git a/src/mol-plugin/state/transforms/visuals.ts b/src/mol-plugin/state/transforms/visuals.ts index f67ca17a24aaf492a425de7581f0294e47186075..3c76a56f5d2c3f49b30cdab808c6f6c44f40a2e3 100644 --- a/src/mol-plugin/state/transforms/visuals.ts +++ b/src/mol-plugin/state/transforms/visuals.ts @@ -8,8 +8,14 @@ import { Transformer } from 'mol-state'; import { Task } from 'mol-task'; import { PluginStateTransform } from '../objects'; import { PluginStateObject as SO } from '../objects'; -import { BallAndStickRepresentation, DefaultBallAndStickProps } from 'mol-repr/structure/representation/ball-and-stick'; import { PluginContext } from 'mol-plugin/context'; +import { ColorTheme } from 'mol-theme/color'; +import { SizeTheme } from 'mol-theme/size'; +import { RepresentationRegistry } from 'mol-repr/representation'; + +const colorThemeRegistry = new ColorTheme.Registry() +const sizeThemeRegistry = new SizeTheme.Registry() +const representationRegistry = new RepresentationRegistry() export { CreateStructureRepresentation } namespace CreateStructureRepresentation { export interface Params { } } @@ -20,14 +26,14 @@ const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.St to: [SO.Molecule.Representation3D], apply({ a, params }, plugin: PluginContext) { return Task.create('Structure Representation', async ctx => { - const repr = BallAndStickRepresentation(); // CartoonRepresentation(); - await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl }, DefaultBallAndStickProps, a.data).runInContext(ctx); + const repr = representationRegistry.create('cartoon', { colorThemeRegistry, sizeThemeRegistry }, a.data) + await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl, colorThemeRegistry, sizeThemeRegistry }, {}, {}, a.data).runInContext(ctx); return new SO.Molecule.Representation3D(repr); }); }, update({ a, b }, plugin: PluginContext) { return Task.create('Structure Representation', async ctx => { - await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl }, b.data.props, a.data).runInContext(ctx); + await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl, colorThemeRegistry, sizeThemeRegistry }, b.data.props, {}, a.data).runInContext(ctx); return Transformer.UpdateResult.Updated; }); } diff --git a/src/mol-plugin/util/canvas3d-identify.ts b/src/mol-plugin/util/canvas3d-identify.ts index e5b561304fd200fbc7bf32edc2933fd0d2ea5152..f2cf8c442edc73cfc87cd3f1b0211d2158664d9c 100644 --- a/src/mol-plugin/util/canvas3d-identify.ts +++ b/src/mol-plugin/util/canvas3d-identify.ts @@ -7,7 +7,7 @@ import { PluginContext } from '../context'; import { PickingId } from 'mol-geo/geometry/picking'; import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci'; -import { Representation } from 'mol-repr'; +import { Representation } from 'mol-repr/representation'; export class Canvas3dIdentifyHelper { private cX = -1; diff --git a/src/mol-repr/index.ts b/src/mol-repr/index.ts deleted file mode 100644 index 2ebfe22338168d79a89269562d1033fd98045b11..0000000000000000000000000000000000000000 --- a/src/mol-repr/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import { Task, RuntimeContext } from 'mol-task' -import { RenderObject } from 'mol-gl/render-object' -import { PickingId } from '../mol-geo/geometry/picking'; -import { Loci, isEmptyLoci, EmptyLoci } from 'mol-model/loci'; -import { MarkerAction } from '../mol-geo/geometry/marker-data'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { WebGLContext } from 'mol-gl/webgl/context'; -import { getQualityProps } from './util'; -import { Theme } from 'mol-geo/geometry/geometry'; -// import { ColorTheme } from 'mol-theme/color'; - -// export interface RepresentationProps { -// visuals?: string[] -// } -export type RepresentationProps = { [k: string]: any } - -export interface RepresentationContext { - webgl?: WebGLContext -} - -export interface Representation<D, P extends RepresentationProps = {}> { - readonly label: string - readonly params: PD.Params - readonly renderObjects: ReadonlyArray<RenderObject> - readonly props: Readonly<P> - createOrUpdate: (ctx: RepresentationContext, props?: Partial<P>, data?: D) => Task<void> - getLoci: (pickingId: PickingId) => Loci - mark: (loci: Loci, action: MarkerAction) => boolean - destroy: () => void -} - -export namespace Representation { - export type Any = Representation<any> - export const Empty: Representation<undefined> = { - label: '', params: {}, renderObjects: [], props: {}, - createOrUpdate: () => Task.constant('', undefined), - getLoci: () => EmptyLoci, - mark: () => false, - destroy: () => {} - } - - export function createMulti<D, P extends RepresentationProps = {}>(label: string, params: PD.Params, defaultProps: P, reprList: Representation<D, P>[]): Representation<D, P> { - let currentProps: P - let currentData: D - - const visualsOptions: [string, string][] = [] - for (let i = 0, il = reprList.length; i < il; ++i) { - visualsOptions.push([ i.toString(), reprList[i].label ]) - } - params['visuals'] = PD.MultiSelect<string>('Visuals', '', ['surface'], visualsOptions) - - if (!defaultProps.visuals) { - defaultProps.visuals = reprList.map((r, i) => i.toString()) - } - - return { - label, - params, - get renderObjects() { - const { visuals } = currentProps - const renderObjects: RenderObject[] = [] - for (let i = 0, il = reprList.length; i < il; ++i) { - if (!visuals || visuals.includes(i.toString())) { - renderObjects.push(...reprList[i].renderObjects) - } - } - return renderObjects - }, - get props() { - const props = {} - reprList.forEach(r => Object.assign(props, r.props)) - return props as P - }, - createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, data?: D) => { - if (data) currentData = data - const qualityProps = getQualityProps(Object.assign({}, currentProps, props), data) - currentProps = Object.assign({}, defaultProps, currentProps, props, qualityProps) - - const { visuals } = currentProps - return Task.create(`Creating '${label}' representation`, async runtime => { - for (let i = 0, il = reprList.length; i < il; ++i) { - if (!visuals || visuals.includes(i.toString())) { - await reprList[i].createOrUpdate(ctx, currentProps, currentData).runInContext(runtime) - } - } - }) - }, - getLoci: (pickingId: PickingId) => { - for (let i = 0, il = reprList.length; i < il; ++i) { - const loci = reprList[i].getLoci(pickingId) - if (!isEmptyLoci(loci)) return loci - } - return EmptyLoci - }, - mark: (loci: Loci, action: MarkerAction) => { - let marked = false - for (let i = 0, il = reprList.length; i < il; ++i) { - marked = reprList[i].mark(loci, action) || marked - } - return marked - }, - destroy() { - for (let i = 0, il = reprList.length; i < il; ++i) { - reprList[i].destroy() - } - } - } - } -} - -// - -export interface VisualContext extends RepresentationContext { - runtime: RuntimeContext, -} - -export interface Visual<D, P extends RepresentationProps> { - readonly renderObject: RenderObject | undefined - createOrUpdate: (ctx: VisualContext, theme: Theme, props?: Partial<P>, data?: D) => Promise<void> - getLoci: (pickingId: PickingId) => Loci - mark: (loci: Loci, action: MarkerAction) => boolean - destroy: () => void -} \ No newline at end of file diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts new file mode 100644 index 0000000000000000000000000000000000000000..948d6a033b88614a89252f1cfa74296ba277dc0d --- /dev/null +++ b/src/mol-repr/representation.ts @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Task, RuntimeContext } from 'mol-task' +import { RenderObject } from 'mol-gl/render-object' +import { PickingId } from '../mol-geo/geometry/picking'; +import { Loci, isEmptyLoci, EmptyLoci } from 'mol-model/loci'; +import { MarkerAction } from '../mol-geo/geometry/marker-data'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { WebGLContext } from 'mol-gl/webgl/context'; +import { getQualityProps } from './util'; +import { ColorTheme } from 'mol-theme/color'; +import { SizeTheme } from 'mol-theme/size'; +import { ThemeProps, Theme, ThemeRegistryContext } from 'mol-theme/theme'; +import { BehaviorSubject } from 'rxjs'; + +// export interface RepresentationProps { +// visuals?: string[] +// } +export type RepresentationProps = { [k: string]: any } + +export type RepresentationParamsGetter<D, P extends PD.Params> = (ctx: ThemeRegistryContext, data: D) => P + +// + +export interface RepresentationProvider<D, P extends PD.Params> { + readonly factory: (getParams: RepresentationParamsGetter<D, P>) => Representation<D, P> + readonly getParams: (ctx: ThemeRegistryContext, data: D) => P +} + +export class RepresentationRegistry<D> { + private _list: { name: string, provider: RepresentationProvider<D, any> }[] = [] + private _map = new Map<string, RepresentationProvider<D, any>>() + + constructor() {}; + + add<P extends PD.Params>(name: string, factory: RepresentationProvider<D, P>['factory'], getParams: RepresentationProvider<D, P>['getParams']) { + const provider = { factory, getParams } as RepresentationProvider<D, P> + this._list.push({ name, provider }) + this._map.set(name, provider) + } + + get(id: string) { + return this._map.get(id) + } + + create(id: string, ctx: ThemeRegistryContext, data: D, props = {}): Representation<D, any> { + const provider = this.get(id) + return provider ? provider.factory(provider.getParams) : Representation.Empty + } + + get list() { + return this._list + } +} + +// + +export interface RepresentationContext { + webgl?: WebGLContext + colorThemeRegistry: ColorTheme.Registry + sizeThemeRegistry: SizeTheme.Registry +} + +export { Representation } +interface Representation<D, P extends PD.Params = {}> { + readonly label: string + readonly updated: BehaviorSubject<number> + readonly renderObjects: ReadonlyArray<RenderObject> + readonly props: Readonly<PD.DefaultValues<P>> + readonly params: Readonly<P> + createOrUpdate: (ctx: RepresentationContext, props?: Partial<PD.DefaultValues<P>>, themeProps?: ThemeProps, data?: D) => Task<void> + getLoci: (pickingId: PickingId) => Loci + mark: (loci: Loci, action: MarkerAction) => boolean + destroy: () => void +} +namespace Representation { + export type Any = Representation<any> + export const Empty: Representation<any> = { + label: '', renderObjects: [], props: {}, params: {}, updated: new BehaviorSubject(0), + createOrUpdate: () => Task.constant('', undefined), + getLoci: () => EmptyLoci, + mark: () => false, + destroy: () => {} + } + + export type Def<D, P extends PD.Params = {}> = { [k: string]: (getParams: RepresentationParamsGetter<D, P>) => Representation<any, P> } + + export function createMulti<D, P extends PD.Params = {}>(label: string, getParams: RepresentationParamsGetter<D, P>, reprDefs: Def<D, P>): Representation<D, P> { + const updated = new BehaviorSubject(0) + + let currentParams: P + let currentProps: PD.DefaultValues<P> + let currentData: D + + const reprMap: { [k: number]: string } = {} + const reprList: Representation<D, P>[] = Object.keys(reprDefs).map((name, i) => { + reprMap[i] = name + return reprDefs[name](getParams) + }) + + return { + label, + updated, + get renderObjects() { + const renderObjects: RenderObject[] = [] + if (currentProps) { + const { visuals } = currentProps + for (let i = 0, il = reprList.length; i < il; ++i) { + if (!visuals || visuals.includes(reprMap[i])) { + renderObjects.push(...reprList[i].renderObjects) + } + } + } + return renderObjects + }, + get props() { + const props = {} + reprList.forEach(r => Object.assign(props, r.props)) + return props as P + }, + get params() { return currentParams }, + createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, data?: D) => { + if (data && data !== currentData) { + currentParams = getParams(ctx, data) + currentData = data + if (!currentProps) currentProps = PD.getDefaultValues(currentParams) as P + } + const qualityProps = getQualityProps(Object.assign({}, currentProps, props), currentData) + Object.assign(currentProps, props, qualityProps) + + const { visuals } = currentProps + return Task.create(`Creating '${label}' representation`, async runtime => { + for (let i = 0, il = reprList.length; i < il; ++i) { + if (!visuals || visuals.includes(reprMap[i])) { + await reprList[i].createOrUpdate(ctx, currentProps, themeProps, currentData).runInContext(runtime) + } + } + updated.next(updated.getValue() + 1) + }) + }, + getLoci: (pickingId: PickingId) => { + for (let i = 0, il = reprList.length; i < il; ++i) { + const loci = reprList[i].getLoci(pickingId) + if (!isEmptyLoci(loci)) return loci + } + return EmptyLoci + }, + mark: (loci: Loci, action: MarkerAction) => { + let marked = false + for (let i = 0, il = reprList.length; i < il; ++i) { + marked = reprList[i].mark(loci, action) || marked + } + return marked + }, + destroy() { + for (let i = 0, il = reprList.length; i < il; ++i) { + reprList[i].destroy() + } + } + } + } +} + +// + +export interface VisualContext { + webgl?: WebGLContext + runtime: RuntimeContext, +} + +export interface Visual<D, P extends PD.Params> { + readonly renderObject: RenderObject | undefined + createOrUpdate: (ctx: VisualContext, theme: Theme, props?: Partial<PD.DefaultValues<P>>, data?: D) => Promise<void> + getLoci: (pickingId: PickingId) => Loci + mark: (loci: Loci, action: MarkerAction) => boolean + destroy: () => void +} \ No newline at end of file diff --git a/src/mol-repr/shape/index.ts b/src/mol-repr/shape/representation.ts similarity index 78% rename from src/mol-repr/shape/index.ts rename to src/mol-repr/shape/representation.ts index 632458983dd63be0c085552b95fa36daae7cfeaf..b48f16439458b61a9de504fc2f085099be570e3c 100644 --- a/src/mol-repr/shape/index.ts +++ b/src/mol-repr/shape/representation.ts @@ -6,40 +6,40 @@ import { Task } from 'mol-task' import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'; -import { RepresentationProps, Representation, RepresentationContext } from '..'; +import { Representation, RepresentationContext } from '../representation'; import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci'; import { ValueCell } from 'mol-util'; -import { ColorThemeName, ColorThemeOptions } from 'mol-theme/color'; import { Shape } from 'mol-model/shape'; import { OrderedSet, Interval } from 'mol-data/int'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { createIdentityTransform } from 'mol-geo/geometry/transform-data'; -import { createRenderableState, createTheme } from 'mol-geo/geometry/geometry'; +import { createRenderableState } from 'mol-geo/geometry/geometry'; import { PickingId } from 'mol-geo/geometry/picking'; import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data'; import { LocationIterator } from 'mol-geo/util/location-iterator'; +import { ThemeProps, createTheme } from 'mol-theme/theme'; +import { BehaviorSubject } from 'rxjs'; -export interface ShapeRepresentation<P extends RepresentationProps = {}> extends Representation<Shape, P> { } +export interface ShapeRepresentation<P extends ShapeParams> extends Representation<Shape, P> { } export const ShapeParams = { ...Mesh.Params, - colorTheme: PD.Select<ColorThemeName>('Color Theme', '', 'shape-group', ColorThemeOptions) + // TODO + // colorTheme: PD.Select<ColorThemeName>('Color Theme', '', 'shape-group', ColorThemeOptions) } -export const DefaultShapeProps = PD.getDefaultValues(ShapeParams) -export type ShapeProps = typeof DefaultShapeProps +export type ShapeParams = typeof ShapeParams -// TODO -// export type ShapeRepresentation = ShapeRepresentation<ShapeProps> - -export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation<P> { +export function ShapeRepresentation<P extends ShapeParams>(): ShapeRepresentation<P> { + const updated = new BehaviorSubject(0) const renderObjects: RenderObject[] = [] let _renderObject: MeshRenderObject | undefined let _shape: Shape - let currentProps: P + let currentProps: PD.DefaultValues<P> = PD.getDefaultValues(ShapeParams) as PD.DefaultValues<P> + let currentParams: P - function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, shape?: Shape) { - currentProps = Object.assign({}, DefaultShapeProps, currentProps, props) + function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, shape?: Shape) { + currentProps = Object.assign({}, currentProps, props) if (shape) _shape = shape return Task.create('ShapeRepresentation.create', async runtime => { @@ -49,7 +49,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation const mesh = _shape.mesh const locationIt = ShapeGroupIterator.fromShape(_shape) - const theme = createTheme(currentProps) + const theme = createTheme(ctx, currentProps, themeProps, {}) const transform = createIdentityTransform() const values = await Mesh.createValues(runtime, mesh, transform, locationIt, theme, currentProps) @@ -57,13 +57,15 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation _renderObject = createMeshRenderObject(values, state) renderObjects.push(_renderObject) + updated.next(updated.getValue() + 1) }); } return { label: 'Shape mesh', - params: ShapeParams, + updated, get renderObjects () { return renderObjects }, + get params () { return currentParams }, get props () { return currentProps }, createOrUpdate, getLoci(pickingId: PickingId) { diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts index 58529f5dd1b6c43a45a5ace7b7f01a0544052ea2..7486dd23f89ba5a64c144a0ac68ccc0279a2f559 100644 --- a/src/mol-repr/structure/complex-representation.ts +++ b/src/mol-repr/structure/complex-representation.ts @@ -8,27 +8,37 @@ import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task' import { Loci, EmptyLoci } from 'mol-model/loci'; -import { StructureProps, StructureRepresentation, StructureParams } from './index'; +import { StructureRepresentation, StructureParams } from './representation'; import { ComplexVisual } from './complex-visual'; import { PickingId } from 'mol-geo/geometry/picking'; import { MarkerAction } from 'mol-geo/geometry/marker-data'; -import { RepresentationContext } from 'mol-repr'; -import { createTheme, Theme } from 'mol-geo/geometry/geometry'; +import { RepresentationContext, RepresentationParamsGetter } from 'mol-repr/representation'; +import { Theme, ThemeProps, createTheme } from 'mol-theme/theme'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { BehaviorSubject } from 'rxjs'; -export function ComplexRepresentation<P extends StructureProps>(label: string, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> { +export function ComplexRepresentation<P extends StructureParams>(label: string, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> { + const updated = new BehaviorSubject(0) let visual: ComplexVisual<P> | undefined + let _structure: Structure - let _props: P + let _params: P + let _props: PD.DefaultValues<P> let _theme: Theme - function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, structure?: Structure) { - if (structure) _structure = structure - _props = Object.assign({}, _props, props, { structure: _structure }) - _theme = createTheme(_props) + function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) { + if (structure && structure !== _structure) { + _params = getParams(ctx, structure) + _structure = structure + if (!_props) _props = PD.getDefaultValues(_params) + } + _props = Object.assign({}, _props, props) + _theme = createTheme(ctx, { structure: _structure }, props, themeProps, _theme) return Task.create('Creating or updating ComplexRepresentation', async runtime => { if (!visual) visual = visualCtor() await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, structure) + updated.next(updated.getValue() + 1) }); } @@ -46,11 +56,12 @@ export function ComplexRepresentation<P extends StructureProps>(label: string, v return { label, - params: StructureParams, // TODO get renderObjects() { return visual && visual.renderObject ? [ visual.renderObject ] : [] }, get props() { return _props }, + get params() { return _params }, + get updated() { return updated }, createOrUpdate, getLoci, mark, diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index 4440129f999ebde9726e73d8ada58117e3db3743..e8f9944a290266f1b18274333fa32226d7500d70 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/src/mol-repr/structure/complex-visual.ts @@ -5,64 +5,68 @@ */ import { Structure } from 'mol-model/structure'; -import { Visual, VisualContext } from '..'; +import { Visual, VisualContext } from '../representation'; import { MeshRenderObject, LinesRenderObject, PointsRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object'; import { createComplexMeshRenderObject, UnitKind, UnitKindOptions } from './visual/util/common'; -import { StructureProps, StructureMeshParams, StructureParams } from './index'; +import { StructureMeshParams, StructureParams } from './representation'; import { deepEqual, ValueCell } from 'mol-util'; import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci'; import { Interval } from 'mol-data/int'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { RenderableValues } from 'mol-gl/renderable/schema'; import { createSizes } from 'mol-geo/geometry/size-data'; -import { Geometry, updateRenderableState, Theme } from 'mol-geo/geometry/geometry'; +import { Geometry, updateRenderableState } 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 { Mesh } from 'mol-geo/geometry/mesh/mesh'; -import { VisualUpdateState, colorChanged, sizeChanged } from 'mol-repr/util'; +import { VisualUpdateState } from 'mol-repr/util'; +import { Theme } from 'mol-theme/theme'; +import { ColorTheme } from 'mol-theme/color'; +import { SizeTheme } from 'mol-theme/size'; -export interface ComplexVisual<P extends StructureProps> extends Visual<Structure, P> { } +export interface ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { } const ComplexParams = { ...StructureParams, unitKinds: PD.MultiSelect<UnitKind>('Unit Kind', '', ['atomic', 'spheres'], UnitKindOptions), } -const DefaultComplexProps = PD.getDefaultValues(ComplexParams) -type ComplexProps = typeof DefaultComplexProps +type ComplexParams = typeof ComplexParams type ComplexRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject -interface ComplexVisualBuilder<P extends ComplexProps, G extends Geometry> { - defaultProps: P - createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: P, geometry?: G): Promise<G> +interface ComplexVisualBuilder<P extends ComplexParams, G extends Geometry> { + defaultProps: PD.DefaultValues<P> + createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.DefaultValues<P>, geometry?: G): Promise<G> createLocationIterator(structure: Structure): LocationIterator getLoci(pickingId: PickingId, structure: Structure, id: number): Loci mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean, - setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void + setUpdateState(state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme): void } -interface ComplexVisualGeometryBuilder<P extends ComplexProps, G extends Geometry> extends ComplexVisualBuilder<P, G> { +interface ComplexVisualGeometryBuilder<P extends ComplexParams, G extends Geometry> extends ComplexVisualBuilder<P, G> { createEmptyGeometry(geometry?: G): G - createRenderObject(ctx: VisualContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: P): Promise<ComplexRenderObject> - updateValues(values: RenderableValues, newProps: P): void + createRenderObject(ctx: VisualContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.DefaultValues<P>): Promise<ComplexRenderObject> + updateValues(values: RenderableValues, newProps: PD.DefaultValues<P>): void } -export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisualGeometryBuilder<P, Geometry>): ComplexVisual<P> { +export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeometryBuilder<P, Geometry>): ComplexVisual<P> { const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder const { createRenderObject, updateValues } = builder const updateState = VisualUpdateState.create() let renderObject: ComplexRenderObject | undefined - let currentProps: P + let currentProps: PD.DefaultValues<P> + let currentTheme: Theme let geometry: Geometry let currentStructure: Structure let locationIt: LocationIterator let conformationHash: number - async function create(ctx: VisualContext, structure: Structure, theme: Theme, props: Partial<P> = {}) { + async function create(ctx: VisualContext, structure: Structure, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) { currentProps = Object.assign({}, defaultProps, props) + currentTheme = theme currentStructure = structure conformationHash = Structure.conformationHash(currentStructure) @@ -72,14 +76,14 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual renderObject = await createRenderObject(ctx, structure, geometry, locationIt, theme, currentProps) } - async function update(ctx: VisualContext, theme: Theme, props: Partial<P>) { + async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>>) { const newProps = Object.assign({}, currentProps, props, { structure: currentStructure }) if (!renderObject) return false locationIt.reset() VisualUpdateState.reset(updateState) - setUpdateState(updateState, newProps, currentProps) + setUpdateState(updateState, newProps, currentProps, theme, currentTheme) const newConformationHash = Structure.conformationHash(currentStructure) if (newConformationHash !== conformationHash) { @@ -87,7 +91,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual updateState.createGeometry = true } - if (colorChanged(currentProps, newProps)) updateState.updateColor = true + if (ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true // @@ -113,12 +117,13 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual updateRenderableState(renderObject.state, newProps) currentProps = newProps + currentTheme = theme return true } return { get renderObject () { return renderObject }, - async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<P> = {}, structure?: Structure) { + async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}, structure?: Structure) { if (!structure && !currentStructure) { throw new Error('missing structure') } else if (structure && (!currentStructure || !renderObject)) { @@ -170,17 +175,16 @@ export const ComplexMeshParams = { ...StructureMeshParams, unitKinds: PD.MultiSelect<UnitKind>('Unit Kind', '', [ 'atomic', 'spheres' ], UnitKindOptions), } -export const DefaultComplexMeshProps = PD.getDefaultValues(ComplexMeshParams) -export type ComplexMeshProps = typeof DefaultComplexMeshProps +export type ComplexMeshParams = typeof ComplexMeshParams -export interface ComplexMeshVisualBuilder<P extends ComplexMeshProps> extends ComplexVisualBuilder<P, Mesh> { } +export interface ComplexMeshVisualBuilder<P extends ComplexMeshParams> extends ComplexVisualBuilder<P, Mesh> { } -export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> { +export function ComplexMeshVisual<P extends ComplexMeshParams>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> { return ComplexVisual({ ...builder, - setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => { - builder.setUpdateState(state, newProps, currentProps) - if (sizeChanged(currentProps, newProps)) state.createGeometry = true + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => { + builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme) + if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true }, createEmptyGeometry: Mesh.createEmpty, createRenderObject: createComplexMeshRenderObject, diff --git a/src/mol-repr/structure/registry.ts b/src/mol-repr/structure/registry.ts new file mode 100644 index 0000000000000000000000000000000000000000..5dcfe8aaff6045f9f2424a96b67d4e28cd517911 --- /dev/null +++ b/src/mol-repr/structure/registry.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Structure } from 'mol-model/structure'; +import { RepresentationProvider, RepresentationRegistry } from '../representation'; +import { CartoonRepresentationProvider } from './representation/cartoon'; +import { BallAndStickRepresentationProvider } from './representation/ball-and-stick'; + +export class StructureRepresentationRegistry extends RepresentationRegistry<Structure> { + constructor() { + super() + Object.keys(BuiltInStructureRepresentations).forEach(name => { + const p = (BuiltInStructureRepresentations as { [k: string]: RepresentationProvider<Structure, any> })[name] + this.add(name, p.factory, p.getParams) + }) + } +} + +export const BuiltInStructureRepresentations = { + 'cartoon': CartoonRepresentationProvider, + 'ball-and-stick': BallAndStickRepresentationProvider, +} +export type BuiltInStructureRepresentationsName = keyof typeof BuiltInStructureRepresentations +export const BuiltInStructureRepresentationsNames = Object.keys(BuiltInStructureRepresentations) +export const BuiltInStructureRepresentationsOptions = BuiltInStructureRepresentationsNames.map(n => [n, n] as [BuiltInStructureRepresentationsName, string]) \ No newline at end of file diff --git a/src/mol-repr/structure/index.ts b/src/mol-repr/structure/representation.ts similarity index 52% rename from src/mol-repr/structure/index.ts rename to src/mol-repr/structure/representation.ts index a0fb183ab0af993ff82b53c30450c6c08635bc00..bd9cfd6ece8e262d4ee8a070544f39a9e65a6659 100644 --- a/src/mol-repr/structure/index.ts +++ b/src/mol-repr/structure/representation.ts @@ -6,9 +6,7 @@ */ import { Structure } from 'mol-model/structure'; -import { ColorThemeName, ColorThemeOptions } from 'mol-theme/color'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; -import { Representation, RepresentationProps } from '..'; +import { Representation, RepresentationProps, RepresentationProvider } from '../representation'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Geometry } from 'mol-geo/geometry/geometry'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; @@ -17,43 +15,39 @@ import { Lines } from 'mol-geo/geometry/lines/lines'; import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { } -// export interface StructureVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { } + +export type StructureRepresentationProvider<P extends PD.Params> = RepresentationProvider<Structure, P> + +// export const StructureParams = { ...Geometry.Params, - colorTheme: PD.Select<ColorThemeName>('Color Theme', '', 'polymer-index', ColorThemeOptions), - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), } -export const DefaultStructureProps = PD.getDefaultValues(StructureParams) -export type StructureProps = typeof DefaultStructureProps +export type StructureParams = typeof StructureParams export const StructureMeshParams = { ...Mesh.Params, ...StructureParams, } -export const DefaultStructureMeshProps = PD.getDefaultValues(StructureMeshParams) -export type StructureMeshProps = typeof DefaultStructureMeshProps +export type StructureMeshParams = typeof StructureMeshParams export const StructurePointsParams = { ...Points.Params, ...StructureParams, } -export const DefaultStructurePointsProps = PD.getDefaultValues(StructurePointsParams) -export type StructurePointsProps = typeof DefaultStructurePointsProps +export type StructurePointsParams = typeof StructurePointsParams export const StructureLinesParams = { ...Lines.Params, ...StructureParams, } -export const DefaultStructureLinesProps = PD.getDefaultValues(StructureLinesParams) -export type StructureLinesProps = typeof DefaultStructureLinesProps +export type StructureLinesParams = typeof StructureLinesParams export const StructureDirectVolumeParams = { ...DirectVolume.Params, ...StructureParams, } -export const DefaultStructureDirectVolumeProps = PD.getDefaultValues(StructureDirectVolumeParams) -export type StructureDirectVolumeProps = typeof DefaultStructureDirectVolumeProps +export type StructureDirectVolumeParams = typeof StructureDirectVolumeParams export { ComplexRepresentation } from './complex-representation' export { UnitsRepresentation } from './units-representation' diff --git a/src/mol-repr/structure/representation/backbone.ts b/src/mol-repr/structure/representation/backbone.ts index 33731218d0a9387e5bc7aaa6594ca5807f93f3df..0105026f30b8f29a1e5212a38805aa6d50c6b1f8 100644 --- a/src/mol-repr/structure/representation/backbone.ts +++ b/src/mol-repr/structure/representation/backbone.ts @@ -1,25 +1,29 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ +// /** +// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. +// * +// * @author Alexander Rose <alexander.rose@weirdbyte.de> +// */ -import { PolymerBackboneVisual, PolymerBackboneParams } from '../visual/polymer-backbone-cylinder'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { UnitsRepresentation } from '../units-representation'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +// import { PolymerBackboneVisual, PolymerBackboneParams } from '../visual/polymer-backbone-cylinder'; +// import { ParamDefinition as PD } from 'mol-util/param-definition'; +// import { UnitsRepresentation } from '../units-representation'; +// import { StructureRepresentation } from '../representation'; +// import { Representation } from 'mol-repr/representation'; +// import { ThemeRegistryContext } from 'mol-theme/theme'; +// import { Structure } from 'mol-model/structure'; -export const BackboneParams = { - ...PolymerBackboneParams -} -export const DefaultBackboneProps = PD.getDefaultValues(BackboneParams) -export type BackboneProps = typeof DefaultBackboneProps +// export const BackboneParams = { +// ...PolymerBackboneParams, +// } +// export function getBackboneParams(ctx: ThemeRegistryContext, structure: Structure) { +// return BackboneParams // TODO return copy +// } +// export type BackboneProps = PD.DefaultValues<typeof BackboneParams> -export type BackboneRepresentation = StructureRepresentation<BackboneProps> +// export type BackboneRepresentation = StructureRepresentation<BackboneProps> -export function BackboneRepresentation(): BackboneRepresentation { - return Representation.createMulti('Backbone', BackboneParams, DefaultBackboneProps, [ - UnitsRepresentation('Polymer backbone cylinder', PolymerBackboneVisual) - ] as StructureRepresentation<BackboneProps>[]) -} \ No newline at end of file +// export function BackboneRepresentation(defaultProps: BackboneProps): BackboneRepresentation { +// return Representation.createMulti('Backbone', defaultProps, [ +// UnitsRepresentation('Polymer backbone cylinder', defaultProps, PolymerBackboneVisual) +// ]) +// } \ No newline at end of file diff --git a/src/mol-repr/structure/representation/ball-and-stick.ts b/src/mol-repr/structure/representation/ball-and-stick.ts index 7f94d0bf8be200c636df1461daa11fcacd889986..3cd9a158bdd22ec41c11bc7b5d7e87154aadc186 100644 --- a/src/mol-repr/structure/representation/ball-and-stick.ts +++ b/src/mol-repr/structure/representation/ball-and-stick.ts @@ -7,32 +7,45 @@ import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere'; import { IntraUnitLinkVisual, IntraUnitLinkParams } from '../visual/intra-unit-link-cylinder'; import { InterUnitLinkVisual, InterUnitLinkParams } from '../visual/inter-unit-link-cylinder'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { UnitKind, UnitKindOptions } from '../visual/util/common'; import { UnitsRepresentation } from '../units-representation'; import { ComplexRepresentation } from '../complex-representation'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +import { StructureRepresentation, StructureRepresentationProvider } from '../representation'; +import { Representation, RepresentationParamsGetter } from 'mol-repr/representation'; +import { ThemeRegistryContext } from 'mol-theme/theme'; +import { Structure } from 'mol-model/structure'; +import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size'; +import { BuiltInColorThemeName, BuiltInColorThemeOptions } from 'mol-theme/color'; +import { UnitKind, UnitKindOptions } from '../visual/util/common'; + +const BallAndStickVisuals = { + 'element-sphere': (getParams: RepresentationParamsGetter<Structure, ElementSphereParams>) => UnitsRepresentation('Element sphere mesh', getParams, ElementSphereVisual), + 'intra-link': (getParams: RepresentationParamsGetter<Structure, IntraUnitLinkParams>) => UnitsRepresentation('Intra-unit link cylinder', getParams, IntraUnitLinkVisual), + 'inter-link': (getParams: RepresentationParamsGetter<Structure, InterUnitLinkParams>) => ComplexRepresentation('Inter-unit link cylinder', getParams, InterUnitLinkVisual), +} +type BallAndStickVisualName = keyof typeof BallAndStickVisuals +const BallAndStickVisualOptions = Object.keys(BallAndStickVisuals).map(name => [name, name] as [BallAndStickVisualName, string]) export const BallAndStickParams = { ...ElementSphereParams, ...IntraUnitLinkParams, ...InterUnitLinkParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1), - sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1), unitKinds: PD.MultiSelect<UnitKind>('Unit Kind', '', ['atomic'], UnitKindOptions), + sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0.01, 10, 0.01), + sizeTheme: PD.Select<BuiltInSizeThemeName>('Size Theme', '', 'uniform', BuiltInSizeThemeOptions), + colorTheme: PD.Select<BuiltInColorThemeName>('Color Theme', '', 'polymer-index', BuiltInColorThemeOptions), + visuals: PD.MultiSelect<BallAndStickVisualName>('Visuals', '', ['element-sphere', 'intra-link', 'inter-link'], BallAndStickVisualOptions), +} +export type BallAndStickParams = typeof BallAndStickParams +export function getBallAndStickParams(ctx: ThemeRegistryContext, structure: Structure) { + return PD.clone(BallAndStickParams) } -export const DefaultBallAndStickProps = PD.getDefaultValues(BallAndStickParams) -export type BallAndStickProps = typeof DefaultBallAndStickProps -export type BallAndStickRepresentation = StructureRepresentation<BallAndStickProps> +export type BallAndStickRepresentation = StructureRepresentation<BallAndStickParams> +export function BallAndStickRepresentation(getParams: RepresentationParamsGetter<Structure, BallAndStickParams>): BallAndStickRepresentation { + return Representation.createMulti('Ball & Stick', getParams, BallAndStickVisuals as unknown as Representation.Def<Structure, BallAndStickParams>) +} -export function BallAndStickRepresentation(): BallAndStickRepresentation { - return Representation.createMulti('Ball & Stick', BallAndStickParams, DefaultBallAndStickProps, [ - UnitsRepresentation('Element sphere mesh', ElementSphereVisual), - UnitsRepresentation('Intra-unit link cylinder', IntraUnitLinkVisual), - ComplexRepresentation('Inter-unit link cylinder', InterUnitLinkVisual) - ] as unknown as StructureRepresentation<BallAndStickProps>[]) // TODO avoid cast to unknown +export const BallAndStickRepresentationProvider: StructureRepresentationProvider<typeof BallAndStickParams> = { + factory: BallAndStickRepresentation, getParams: getBallAndStickParams } \ No newline at end of file diff --git a/src/mol-repr/structure/representation/carbohydrate.ts b/src/mol-repr/structure/representation/carbohydrate.ts index 3a0c79e9ad004992b88dcfc125ee2343c186fdbd..6493be0aeaaf44ebb3372d507ee41f7e074efbb7 100644 --- a/src/mol-repr/structure/representation/carbohydrate.ts +++ b/src/mol-repr/structure/representation/carbohydrate.ts @@ -1,32 +1,32 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ +// /** +// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. +// * +// * @author Alexander Rose <alexander.rose@weirdbyte.de> +// */ -import { CarbohydrateSymbolVisual, CarbohydrateSymbolParams } from '../visual/carbohydrate-symbol-mesh'; -import { CarbohydrateLinkVisual, CarbohydrateLinkParams } from '../visual/carbohydrate-link-cylinder'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { ComplexRepresentation } from '../complex-representation'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +// import { CarbohydrateSymbolVisual, CarbohydrateSymbolParams } from '../visual/carbohydrate-symbol-mesh'; +// import { CarbohydrateLinkVisual, CarbohydrateLinkParams } from '../visual/carbohydrate-link-cylinder'; +// import { ParamDefinition as PD } from 'mol-util/param-definition'; +// import { ComplexRepresentation } from '../complex-representation'; +// import { StructureRepresentation } from '../representation'; +// import { Representation } from 'mol-repr/representation'; +// import { ThemeRegistryContext } from 'mol-theme/theme'; +// import { Structure } from 'mol-model/structure'; -export const CarbohydrateParams = { - ...CarbohydrateSymbolParams, - ...CarbohydrateLinkParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 0.1, 20), - sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1), -} -export const DefaultCarbohydrateProps = PD.getDefaultValues(CarbohydrateParams) -export type CarbohydrateProps = typeof DefaultCarbohydrateProps +// export const CarbohydrateParams = { +// ...CarbohydrateSymbolParams, +// ...CarbohydrateLinkParams, +// } +// export function getCarbohydrateParams(ctx: ThemeRegistryContext, structure: Structure) { +// return CarbohydrateParams // TODO return copy +// } +// export type CarbohydrateProps = PD.DefaultValues<typeof CarbohydrateParams> -export type CarbohydrateRepresentation = StructureRepresentation<CarbohydrateProps> +// export type CarbohydrateRepresentation = StructureRepresentation<CarbohydrateProps> -export function CarbohydrateRepresentation(): CarbohydrateRepresentation { - return Representation.createMulti('Carbohydrate', CarbohydrateParams, DefaultCarbohydrateProps, [ - ComplexRepresentation('Carbohydrate symbol mesh', CarbohydrateSymbolVisual), - ComplexRepresentation('Carbohydrate link cylinder', CarbohydrateLinkVisual) - ] as StructureRepresentation<CarbohydrateProps>[]) -} \ No newline at end of file +// export function CarbohydrateRepresentation(defaultProps: CarbohydrateProps): CarbohydrateRepresentation { +// return Representation.createMulti('Carbohydrate', defaultProps, [ +// ComplexRepresentation('Carbohydrate symbol mesh', defaultProps, CarbohydrateSymbolVisual), +// ComplexRepresentation('Carbohydrate link cylinder', defaultProps, CarbohydrateLinkVisual) +// ]) +// } \ No newline at end of file diff --git a/src/mol-repr/structure/representation/cartoon.ts b/src/mol-repr/structure/representation/cartoon.ts index 59e0639758375d2600943412da45f82a8ef3a2e2..16beffdc93ccdd33a656b0c3b9794cce0a0e3057 100644 --- a/src/mol-repr/structure/representation/cartoon.ts +++ b/src/mol-repr/structure/representation/cartoon.ts @@ -7,31 +7,45 @@ import { PolymerTraceVisual, PolymerTraceParams } from '../visual/polymer-trace-mesh'; import { PolymerGapVisual, PolymerGapParams } from '../visual/polymer-gap-cylinder'; import { NucleotideBlockVisual, NucleotideBlockParams } from '../visual/nucleotide-block-mesh'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { UnitsRepresentation } from '../units-representation'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +import { StructureRepresentation, StructureRepresentationProvider } from '../representation'; +import { Representation, RepresentationParamsGetter } from 'mol-repr/representation'; import { PolymerDirectionVisual, PolymerDirectionParams } from '../visual/polymer-direction-wedge'; +import { Structure } from 'mol-model/structure'; +import { ThemeRegistryContext } from 'mol-theme/theme'; +import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size'; +import { BuiltInColorThemeOptions, BuiltInColorThemeName } from 'mol-theme/color'; + +const CartoonVisuals = { + 'polymer-trace': (getParams: RepresentationParamsGetter<Structure, PolymerTraceParams>) => UnitsRepresentation('Polymer trace mesh', getParams, PolymerTraceVisual), + 'polymer-gap': (getParams: RepresentationParamsGetter<Structure, PolymerGapParams>) => UnitsRepresentation('Polymer gap cylinder', getParams, PolymerGapVisual), + 'nucleotide-block': (getParams: RepresentationParamsGetter<Structure, NucleotideBlockParams>) => UnitsRepresentation('Nucleotide block mesh', getParams, NucleotideBlockVisual), + 'direction-wedge': (getParams: RepresentationParamsGetter<Structure, PolymerDirectionParams>) => UnitsRepresentation('Polymer direction wedge', getParams, PolymerDirectionVisual) +} +type CartoonVisualName = keyof typeof CartoonVisuals +const CartoonVisualOptions = Object.keys(CartoonVisuals).map(name => [name, name] as [CartoonVisualName, string]) export const CartoonParams = { ...PolymerTraceParams, ...PolymerGapParams, ...NucleotideBlockParams, ...PolymerDirectionParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1), + sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01), + sizeTheme: PD.Select<BuiltInSizeThemeName>('Size Theme', '', 'uniform', BuiltInSizeThemeOptions), + colorTheme: PD.Select<BuiltInColorThemeName>('Color Theme', '', 'polymer-index', BuiltInColorThemeOptions), + visuals: PD.MultiSelect<CartoonVisualName>('Visuals', '', ['polymer-trace', 'polymer-gap', 'nucleotide-block'], CartoonVisualOptions), +} +export type CartoonParams = typeof CartoonParams +export function getCartoonParams(ctx: ThemeRegistryContext, structure: Structure) { + return PD.clone(CartoonParams) } -export const DefaultCartoonProps = { ...PD.getDefaultValues(CartoonParams), visuals: [ '0', '1', '2' ] } -export type CartoonProps = typeof DefaultCartoonProps -export type CartoonRepresentation = StructureRepresentation<CartoonProps> +export type CartoonRepresentation = StructureRepresentation<CartoonParams> +export function CartoonRepresentation(getParams: RepresentationParamsGetter<Structure, CartoonParams>): CartoonRepresentation { + return Representation.createMulti('Cartoon', getParams, CartoonVisuals as unknown as Representation.Def<Structure, CartoonParams>) +} -export function CartoonRepresentation(): CartoonRepresentation { - return Representation.createMulti('Cartoon', CartoonParams, DefaultCartoonProps, [ - UnitsRepresentation('Polymer trace mesh', PolymerTraceVisual), - UnitsRepresentation('Polymer gap cylinder', PolymerGapVisual), - UnitsRepresentation('Nucleotide block mesh', NucleotideBlockVisual), - UnitsRepresentation('Polymer direction wedge', PolymerDirectionVisual) - ] as unknown as StructureRepresentation<CartoonProps>[]) // TODO avoid cast to unknown +export const CartoonRepresentationProvider: StructureRepresentationProvider<CartoonParams> = { + factory: CartoonRepresentation, getParams: getCartoonParams } \ No newline at end of file diff --git a/src/mol-repr/structure/representation/distance-restraint.ts b/src/mol-repr/structure/representation/distance-restraint.ts index 08ed872669f2632e0d3c9ba64acaeeb7ffc67063..94edde13a8cb99ff0ec108ef8f90f4018a4e99c3 100644 --- a/src/mol-repr/structure/representation/distance-restraint.ts +++ b/src/mol-repr/structure/representation/distance-restraint.ts @@ -1,28 +1,29 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ +// /** +// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. +// * +// * @author Alexander Rose <alexander.rose@weirdbyte.de> +// */ -import { CrossLinkRestraintVisual, CrossLinkRestraintParams } from '../visual/cross-link-restraint-cylinder'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { ComplexRepresentation } from '../complex-representation'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +// import { CrossLinkRestraintVisual, CrossLinkRestraintParams } from '../visual/cross-link-restraint-cylinder'; +// import { ParamDefinition as PD } from 'mol-util/param-definition'; +// import { ComplexRepresentation } from '../complex-representation'; +// import { StructureRepresentation } from '../representation'; +// import { Representation } from 'mol-repr/representation'; +// import { ThemeRegistryContext } from 'mol-theme/theme'; +// import { Structure } from 'mol-model/structure'; -export const DistanceRestraintParams = { - ...CrossLinkRestraintParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 0.25, 0, 0.05, 20), -} -export const DefaultDistanceRestraintProps = PD.getDefaultValues(DistanceRestraintParams) -export type DistanceRestraintProps = typeof DefaultDistanceRestraintProps +// export const DistanceRestraintParams = { +// ...CrossLinkRestraintParams, +// } +// export function getDistanceRestraintParams(ctx: ThemeRegistryContext, structure: Structure) { +// return DistanceRestraintParams // TODO return copy +// } +// export type DistanceRestraintProps = PD.DefaultValues<typeof DistanceRestraintParams> -export type DistanceRestraintRepresentation = StructureRepresentation<DistanceRestraintProps> +// export type DistanceRestraintRepresentation = StructureRepresentation<DistanceRestraintProps> -export function DistanceRestraintRepresentation(): DistanceRestraintRepresentation { - return Representation.createMulti('Distance restraint', DistanceRestraintParams, DefaultDistanceRestraintProps, [ - ComplexRepresentation('Cross-link restraint', CrossLinkRestraintVisual) - ] as StructureRepresentation<DistanceRestraintProps>[]) -} \ No newline at end of file +// export function DistanceRestraintRepresentation(defaultProps: DistanceRestraintProps): DistanceRestraintRepresentation { +// return Representation.createMulti('Distance restraint', defaultProps, [ +// ComplexRepresentation('Cross-link restraint', defaultProps, CrossLinkRestraintVisual) +// ]) +// } \ No newline at end of file diff --git a/src/mol-repr/structure/representation/molecular-surface.ts b/src/mol-repr/structure/representation/molecular-surface.ts index 0e8fa626b5c7a151ccd1a6a54c5dd347d2cd13dd..b55aa5faedede552c07c3d9aa8aa7e0a671fb2a7 100644 --- a/src/mol-repr/structure/representation/molecular-surface.ts +++ b/src/mol-repr/structure/representation/molecular-surface.ts @@ -1,33 +1,35 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ +// /** +// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. +// * +// * @author Alexander Rose <alexander.rose@weirdbyte.de> +// */ -import { GaussianSurfaceVisual, GaussianSurfaceParams } from '../visual/gaussian-surface-mesh'; -import { UnitsRepresentation } from '../units-representation'; -import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +// import { GaussianSurfaceVisual, GaussianSurfaceParams } from '../visual/gaussian-surface-mesh'; +// import { UnitsRepresentation } from '../units-representation'; +// import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe'; +// import { ParamDefinition as PD } from 'mol-util/param-definition'; +// import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume'; +// import { StructureRepresentation } from '../representation'; +// import { Representation } from 'mol-repr/representation'; +// import { ThemeRegistryContext } from 'mol-theme/theme'; +// import { Structure } from 'mol-model/structure'; -export const MolecularSurfaceParams = { - ...GaussianSurfaceParams, - ...GaussianWireframeParams, - ...GaussianDensityVolumeParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), -} -export const DefaultMolecularSurfaceProps = { ...PD.getDefaultValues(MolecularSurfaceParams), visuals: [ '0' ] } -export type MolecularSurfaceProps = typeof DefaultMolecularSurfaceProps +// export const MolecularSurfaceParams = { +// ...GaussianSurfaceParams, +// ...GaussianWireframeParams, +// ...GaussianDensityVolumeParams, +// } +// export function getMolecularSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) { +// return MolecularSurfaceParams // TODO return copy +// } +// export type MolecularSurfaceProps = PD.DefaultValues<typeof MolecularSurfaceParams> -export type MolecularSurfaceRepresentation = StructureRepresentation<MolecularSurfaceProps> +// export type MolecularSurfaceRepresentation = StructureRepresentation<MolecularSurfaceProps> -export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation { - return Representation.createMulti('Molecular Surface', MolecularSurfaceParams, DefaultMolecularSurfaceProps, [ - UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual), - UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual), - UnitsRepresentation('Gaussian volume', GaussianDensityVolumeVisual) - ] as unknown as StructureRepresentation<MolecularSurfaceProps>[]) // TODO avoid cast to unknown -} \ No newline at end of file +// export function MolecularSurfaceRepresentation(defaultProps: MolecularSurfaceProps): MolecularSurfaceRepresentation { +// return Representation.createMulti('Molecular Surface', defaultProps, [ +// UnitsRepresentation('Gaussian surface', defaultProps, GaussianSurfaceVisual), +// UnitsRepresentation('Gaussian wireframe', defaultProps, GaussianWireframeVisual), +// UnitsRepresentation('Gaussian volume', defaultProps, GaussianDensityVolumeVisual) +// ]) +// } \ No newline at end of file diff --git a/src/mol-repr/structure/representation/point.ts b/src/mol-repr/structure/representation/point.ts index 0e483ee9e0c8e2e8fff096f68a7289365eb7ce8d..efc91f8df4b9d209ffd7cba231bb777863c8e3fe 100644 --- a/src/mol-repr/structure/representation/point.ts +++ b/src/mol-repr/structure/representation/point.ts @@ -1,25 +1,29 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ +// /** +// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. +// * +// * @author Alexander Rose <alexander.rose@weirdbyte.de> +// */ -import { ElementPointVisual, ElementPointParams } from '../visual/element-point'; -import { UnitsRepresentation } from '../units-representation'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +// import { ElementPointVisual, ElementPointParams } from '../visual/element-point'; +// import { UnitsRepresentation } from '../units-representation'; +// import { ParamDefinition as PD } from 'mol-util/param-definition'; +// import { StructureRepresentation } from '../representation'; +// import { Representation } from 'mol-repr/representation'; +// import { ThemeRegistryContext } from 'mol-theme/theme'; +// import { Structure } from 'mol-model/structure'; -export const PointParams = { - ...ElementPointParams, -} -export const DefaultPointProps = PD.getDefaultValues(PointParams) -export type PointProps = typeof DefaultPointProps +// export const PointParams = { +// ...ElementPointParams, +// } +// export function getPointParams(ctx: ThemeRegistryContext, structure: Structure) { +// return PointParams // TODO return copy +// } +// export type PointProps = PD.DefaultValues<typeof PointParams> -export type PointRepresentation = StructureRepresentation<PointProps> +// export type PointRepresentation = StructureRepresentation<PointProps> -export function PointRepresentation(): PointRepresentation { - return Representation.createMulti('Point', PointParams, DefaultPointProps, [ - UnitsRepresentation('Point', ElementPointVisual) - ] as StructureRepresentation<PointProps>[]) -} \ No newline at end of file +// export function PointRepresentation(defaultProps: PointProps): PointRepresentation { +// return Representation.createMulti('Point', defaultProps, [ +// UnitsRepresentation('Point', defaultProps, ElementPointVisual) +// ]) +// } \ No newline at end of file diff --git a/src/mol-repr/structure/representation/spacefill.ts b/src/mol-repr/structure/representation/spacefill.ts index 46a8a3e64d37ced19b1ad30bd09aabc95c83b9b3..42f35ae58b1ed7f670570809e92c1753d01cbb26 100644 --- a/src/mol-repr/structure/representation/spacefill.ts +++ b/src/mol-repr/structure/representation/spacefill.ts @@ -1,25 +1,29 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ +// /** +// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. +// * +// * @author Alexander Rose <alexander.rose@weirdbyte.de> +// */ -import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere'; -import { UnitsRepresentation } from '../units-representation'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { StructureRepresentation } from '../index'; -import { Representation } from 'mol-repr'; +// import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere'; +// import { UnitsRepresentation } from '../units-representation'; +// import { ParamDefinition as PD } from 'mol-util/param-definition'; +// import { StructureRepresentation } from '../representation'; +// import { Representation } from 'mol-repr/representation'; +// import { ThemeRegistryContext } from 'mol-theme/theme'; +// import { Structure } from 'mol-model/structure'; -export const SpacefillParams = { - ...ElementSphereParams -} -export const DefaultSpacefillProps = PD.getDefaultValues(SpacefillParams) -export type SpacefillProps = typeof DefaultSpacefillProps +// export const SpacefillParams = { +// ...ElementSphereParams, +// } +// export function getSpacefillParams(ctx: ThemeRegistryContext, structure: Structure) { +// return SpacefillParams // TODO return copy +// } +// export type SpacefillProps = PD.DefaultValues<typeof SpacefillParams> -export type SpacefillRepresentation = StructureRepresentation<SpacefillProps> +// export type SpacefillRepresentation = StructureRepresentation<SpacefillProps> -export function SpacefillRepresentation(): SpacefillRepresentation { - return Representation.createMulti('Spacefill', SpacefillParams, DefaultSpacefillProps, [ - UnitsRepresentation('Sphere mesh', ElementSphereVisual) - ] as StructureRepresentation<SpacefillProps>[]) -} \ No newline at end of file +// export function SpacefillRepresentation(defaultProps: SpacefillProps): SpacefillRepresentation { +// return Representation.createMulti('Spacefill', defaultProps, [ +// UnitsRepresentation('Sphere mesh', defaultProps, ElementSphereVisual) +// ]) +// } \ No newline at end of file diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts index 0b419bc140e3a411bbc0c24c4aa5617ce4e4aaba..44897ad4feca3d32df5d78966e3438023d7ddf0e 100644 --- a/src/mol-repr/structure/units-representation.ts +++ b/src/mol-repr/structure/units-representation.ts @@ -8,27 +8,42 @@ import { Structure, Unit } from 'mol-model/structure'; import { Task } from 'mol-task' import { RenderObject } from 'mol-gl/render-object'; -import { RepresentationProps, Visual, RepresentationContext } from '..'; +import { Visual, RepresentationContext, RepresentationParamsGetter } from '../representation'; import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci'; import { StructureGroup } from './units-visual'; -import { StructureProps, StructureParams, StructureRepresentation } from './index'; +import { StructureRepresentation, StructureParams } from './representation'; import { PickingId } from 'mol-geo/geometry/picking'; import { MarkerAction } from 'mol-geo/geometry/marker-data'; -import { Theme, createTheme } from 'mol-geo/geometry/geometry'; +import { Theme, ThemeProps, createTheme } from 'mol-theme/theme'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { UnitKind, UnitKindOptions } from './visual/util/common'; +import { BehaviorSubject } from 'rxjs'; -export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { } +export const UnitsParams = { + ...StructureParams, + unitKinds: PD.MultiSelect<UnitKind>('Unit Kind', '', ['atomic', 'spheres'], UnitKindOptions), +} +export type UnitsParams = typeof UnitsParams -export function UnitsRepresentation<P extends StructureProps>(label: string, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> { +export interface UnitsVisual<P extends UnitsParams> extends Visual<StructureGroup, P> { } + +export function UnitsRepresentation<P extends UnitsParams>(label: string, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> { + const updated = new BehaviorSubject(0) let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>() - let _props: P - let _theme: Theme let _structure: Structure let _groups: ReadonlyArray<Unit.SymmetryGroup> + let _params: P + let _props: PD.DefaultValues<P> + let _theme: Theme - function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, structure?: Structure) { - _props = Object.assign({}, _props, props, { structure: structure || _structure }) - _theme = createTheme(_props) + function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) { + if (structure && structure !== _structure) { + _params = getParams(ctx, structure) + if (!_props) _props = PD.getDefaultValues(_params) + } + _props = Object.assign({}, _props, props) + _theme = createTheme(ctx, { structure: structure || _structure }, props, themeProps, _theme) return Task.create('Creating or updating UnitsRepresentation', async runtime => { if (!_structure && !structure) { @@ -78,7 +93,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis // visuals.set(group.hashCode, { visual, group }) // }) // unusedVisuals.forEach(visual => visual.destroy()) - } else if (structure && _structure.hashCode === structure.hashCode) { + } else if (structure && structure !== _structure && _structure.hashCode === structure.hashCode) { // console.log('_structure.hashCode === structure.hashCode') // Expects that for structures with the same hashCode, // the unitSymmetryGroups are the same as well. @@ -100,11 +115,12 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis const visualsList: [ UnitsVisual<P>, Unit.SymmetryGroup ][] = [] // TODO avoid allocation visuals.forEach(({ visual, group }) => visualsList.push([ visual, group ])) for (let i = 0, il = visualsList.length; i < il; ++i) { - const [ visual, group ] = visualsList[i] - await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, { group, structure: _structure }) + const [ visual ] = visualsList[i] + await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props) } } if (structure) _structure = structure + updated.next(updated.getValue() + 1) }); } @@ -132,7 +148,6 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis return { label, - params: StructureParams, // TODO get renderObjects() { const renderObjects: RenderObject[] = [] visuals.forEach(({ visual }) => { @@ -140,9 +155,9 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis }) return renderObjects }, - get props() { - return _props - }, + get props() { return _props }, + get params() { return _params }, + get updated() { return updated }, createOrUpdate, getLoci, mark, diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index 29a00fc88bb991cf23f2c88a0fa9b921bea4d1c1..6656c585bd2d94b81f8e6e33c7a67428389782fe 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/src/mol-repr/structure/units-visual.ts @@ -5,16 +5,16 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { RepresentationProps, Visual, VisualContext } from '../'; -import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureParams } from './index'; +import { RepresentationProps, Visual, VisualContext } from '../representation'; +import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams } from './representation'; import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci'; import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object'; -import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject, UnitKind, UnitKindOptions, includesUnitKind } from './visual/util/common'; +import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject, includesUnitKind } from './visual/util/common'; import { deepEqual, ValueCell, UUID } from 'mol-util'; import { Interval } from 'mol-data/int'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { RenderableValues } from 'mol-gl/renderable/schema'; -import { Geometry, updateRenderableState, Theme } from 'mol-geo/geometry/geometry'; +import { Geometry, updateRenderableState } 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'; @@ -24,7 +24,11 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { Points } from 'mol-geo/geometry/points/points'; import { Lines } from 'mol-geo/geometry/lines/lines'; import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; -import { VisualUpdateState, colorChanged, sizeChanged } from 'mol-repr/util'; +import { VisualUpdateState } from 'mol-repr/util'; +import { Theme } from 'mol-theme/theme'; +import { ColorTheme } from 'mol-theme/color'; +import { SizeTheme } from 'mol-theme/size'; +import { UnitsParams } from './units-representation'; export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup } @@ -37,45 +41,40 @@ function sameGroupConformation(groupA: Unit.SymmetryGroup, groupB: Unit.Symmetry ) } -const UnitsParams = { - ...StructureParams, - unitKinds: PD.MultiSelect<UnitKind>('Unit Kind', '', ['atomic', 'spheres'], UnitKindOptions), -} -const DefaultUnitsProps = PD.getDefaultValues(UnitsParams) -type UnitsProps = typeof DefaultUnitsProps - type UnitsRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject -interface UnitsVisualBuilder<P extends UnitsProps, G extends Geometry> { - defaultProps: P - createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: P, geometry?: G): Promise<G> +interface UnitsVisualBuilder<P extends UnitsParams, G extends Geometry> { + defaultProps: PD.DefaultValues<P> + createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.DefaultValues<P>, geometry?: G): Promise<G> createLocationIterator(group: Unit.SymmetryGroup): LocationIterator getLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number): Loci mark(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean): boolean - setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void + setUpdateState(state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme): void } -interface UnitsVisualGeometryBuilder<P extends UnitsProps, G extends Geometry> extends UnitsVisualBuilder<P, G> { +interface UnitsVisualGeometryBuilder<P extends UnitsParams, G extends Geometry> extends UnitsVisualBuilder<P, G> { createEmptyGeometry(geometry?: G): G - createRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: P): Promise<UnitsRenderObject> - updateValues(values: RenderableValues, newProps: P): void + createRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.DefaultValues<P>): Promise<UnitsRenderObject> + updateValues(values: RenderableValues, newProps: PD.DefaultValues<P>): void } -export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBuilder<P, Geometry>): UnitsVisual<P> { +export function UnitsVisual<P extends UnitsParams>(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: UnitsRenderObject | undefined - let currentProps: P + let currentProps: PD.DefaultValues<P> + let currentTheme: Theme let geometry: Geometry let currentGroup: Unit.SymmetryGroup let currentStructure: Structure let locationIt: LocationIterator let currentConformationId: UUID - async function create(ctx: VisualContext, group: Unit.SymmetryGroup, theme: Theme, props: Partial<P> = {}) { + async function create(ctx: VisualContext, group: Unit.SymmetryGroup, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) { currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure }) + currentTheme = theme currentGroup = group const unit = group.units[0] @@ -89,7 +88,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu renderObject = await createRenderObject(ctx, group, geometry, locationIt, theme, currentProps) } - async function update(ctx: VisualContext, theme: Theme, props: Partial<P> = {}) { + async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) { if (!renderObject) return const newProps = Object.assign({}, currentProps, props, { structure: currentStructure }) @@ -97,7 +96,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu locationIt.reset() VisualUpdateState.reset(updateState) - setUpdateState(updateState, newProps, currentProps) + setUpdateState(updateState, newProps, currentProps, theme, currentTheme) const newConformationId = Unit.conformationId(unit) if (newConformationId !== currentConformationId) { @@ -107,7 +106,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true - if (colorChanged(currentProps, newProps)) updateState.updateColor = true + if (ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true // @@ -143,11 +142,12 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu updateRenderableState(renderObject.state, newProps) currentProps = newProps + currentTheme = theme } return { get renderObject () { return renderObject }, - async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<P> = {}, structureGroup?: StructureGroup) { + async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}, structureGroup?: StructureGroup) { if (structureGroup) currentStructure = structureGroup.structure const group = structureGroup ? structureGroup.group : undefined if (!group && !currentGroup) { @@ -205,16 +205,15 @@ export const UnitsMeshParams = { ...StructureMeshParams, ...UnitsParams, } -export const DefaultUnitsMeshProps = PD.getDefaultValues(UnitsMeshParams) -export type UnitsMeshProps = typeof DefaultUnitsMeshProps -export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> extends UnitsVisualBuilder<P, Mesh> { } +export type UnitsMeshParams = typeof UnitsMeshParams +export interface UnitsMeshVisualBuilder<P extends UnitsMeshParams> extends UnitsVisualBuilder<P, Mesh> { } -export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> { +export function UnitsMeshVisual<P extends UnitsMeshParams>(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 + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => { + builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme) + if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true }, createEmptyGeometry: Mesh.createEmpty, createRenderObject: createUnitsMeshRenderObject, @@ -228,18 +227,17 @@ export const UnitsPointsParams = { ...StructurePointsParams, ...UnitsParams, } -export const DefaultUnitsPointsProps = PD.getDefaultValues(UnitsPointsParams) -export type UnitsPointsProps = typeof DefaultUnitsPointsProps -export interface UnitsPointVisualBuilder<P extends UnitsPointsProps> extends UnitsVisualBuilder<P, Points> { } +export type UnitsPointsParams = typeof UnitsPointsParams +export interface UnitsPointVisualBuilder<P extends UnitsPointsParams> extends UnitsVisualBuilder<P, Points> { } -export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPointVisualBuilder<P>): UnitsVisual<P> { +export function UnitsPointsVisual<P extends UnitsPointsParams>(builder: UnitsPointVisualBuilder<P>): UnitsVisual<P> { 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 + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => { + builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme) + if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true }, updateValues: Points.updateValues }) @@ -251,18 +249,17 @@ export const UnitsLinesParams = { ...StructureLinesParams, ...UnitsParams, } -export const DefaultUnitsLinesProps = PD.getDefaultValues(UnitsLinesParams) -export type UnitsLinesProps = typeof DefaultUnitsLinesProps -export interface UnitsLinesVisualBuilder<P extends UnitsLinesProps> extends UnitsVisualBuilder<P, Lines> { } +export type UnitsLinesParams = typeof UnitsLinesParams +export interface UnitsLinesVisualBuilder<P extends UnitsLinesParams> extends UnitsVisualBuilder<P, Lines> { } -export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesVisualBuilder<P>): UnitsVisual<P> { +export function UnitsLinesVisual<P extends UnitsLinesParams>(builder: UnitsLinesVisualBuilder<P>): UnitsVisual<P> { 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 + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => { + builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme) + if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true }, updateValues: Lines.updateValues }) @@ -274,18 +271,17 @@ export const UnitsDirectVolumeParams = { ...StructureDirectVolumeParams, ...UnitsParams, } -export const DefaultUnitsDirectVolumeProps = PD.getDefaultValues(UnitsDirectVolumeParams) -export type UnitsDirectVolumeProps = typeof DefaultUnitsDirectVolumeProps -export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeProps> extends UnitsVisualBuilder<P, DirectVolume> { } +export type UnitsDirectVolumeParams = typeof UnitsDirectVolumeParams +export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeParams> extends UnitsVisualBuilder<P, DirectVolume> { } -export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builder: UnitsDirectVolumeVisualBuilder<P>): UnitsVisual<P> { +export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeParams>(builder: UnitsDirectVolumeVisualBuilder<P>): UnitsVisual<P> { 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 + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => { + builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme) + if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true }, updateValues: DirectVolume.updateValues }) diff --git a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts index 9654d7b089baa34308453ae38255c723c5a63d78..7aca257fd4b7256a5d63a1b1717ce7d46db5dfbe 100644 --- a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts +++ b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts @@ -10,7 +10,6 @@ import { Vec3 } from 'mol-math/linear-algebra'; import { createLinkCylinderMesh, LinkCylinderProps, LinkCylinderParams } from './util/link'; import { OrderedSet, Interval } from 'mol-data/int'; import { ComplexMeshVisual, ComplexVisual } from '../complex-visual'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; import { LinkType } from 'mol-model/structure/model/types'; import { BitFlags } from 'mol-util'; import { UnitsMeshParams } from '../units-visual'; @@ -19,8 +18,8 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { PickingId } from 'mol-geo/geometry/picking'; import { VisualUpdateState } from '../../util'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; // TODO create seperate visual // for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) { @@ -64,21 +63,18 @@ async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: export const CarbohydrateLinkParams = { ...UnitsMeshParams, ...LinkCylinderParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1), detail: PD.Numeric('Sphere Detail', '', 0, 0, 3, 1), } -export const DefaultCarbohydrateLinkProps = PD.getDefaultValues(CarbohydrateLinkParams) -export type CarbohydrateLinkProps = typeof DefaultCarbohydrateLinkProps +export type CarbohydrateLinkParams = typeof CarbohydrateLinkParams -export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkProps> { - return ComplexMeshVisual<CarbohydrateLinkProps>({ - defaultProps: DefaultCarbohydrateLinkProps, +export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkParams> { + return ComplexMeshVisual<CarbohydrateLinkParams>({ + defaultProps: PD.getDefaultValues(CarbohydrateLinkParams), createGeometry: createCarbohydrateLinkCylinderMesh, createLocationIterator: CarbohydrateLinkIterator, getLoci: getLinkLoci, mark: markLink, - setUpdateState: (state: VisualUpdateState, newProps: CarbohydrateLinkProps, currentProps: CarbohydrateLinkProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<CarbohydrateLinkParams>, currentProps: PD.DefaultValues<CarbohydrateLinkParams>) => { state.createGeometry = newProps.radialSegments !== currentProps.radialSegments } }) diff --git a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts index 2d760f0b10d0b122fa6bb8b28d55470327d4be92..96b3dbafa74b05c3182689d7e0351803ba6bf3ac 100644 --- a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts @@ -13,19 +13,18 @@ import { DiamondPrism, PentagonalPrism, HexagonalPrism } from 'mol-geo/primitive import { Structure, StructureElement } from 'mol-model/structure'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants'; import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere'; import { ComplexMeshParams, ComplexMeshVisual } from '../complex-visual'; import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { ComplexVisual } from '../index'; +import { ComplexVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { PickingId } from 'mol-geo/geometry/picking'; import { OrderedSet, Interval } from 'mol-data/int'; import { EmptyLoci, Loci } from 'mol-model/loci'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; const t = Mat4.identity() const sVec = Vec3.zero() @@ -45,7 +44,7 @@ const diamondPrism = DiamondPrism() const pentagonalPrism = PentagonalPrism() const hexagonalPrism = HexagonalPrism() -async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: CarbohydrateSymbolProps, mesh?: Mesh) { +async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.DefaultValues<CarbohydrateSymbolParams>, mesh?: Mesh) { const builder = MeshBuilder.create(256, 128, mesh) const { detail } = props @@ -148,21 +147,18 @@ async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Struc export const CarbohydrateSymbolParams = { ...ComplexMeshParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 10, 0.1), detail: PD.Numeric('Sphere Detail', '', 0, 0, 3, 1), } -export const DefaultCarbohydrateSymbolProps = PD.getDefaultValues(CarbohydrateSymbolParams) -export type CarbohydrateSymbolProps = typeof DefaultCarbohydrateSymbolProps +export type CarbohydrateSymbolParams = typeof CarbohydrateSymbolParams -export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolProps> { - return ComplexMeshVisual<CarbohydrateSymbolProps>({ - defaultProps: DefaultCarbohydrateSymbolProps, +export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolParams> { + return ComplexMeshVisual<CarbohydrateSymbolParams>({ + defaultProps: PD.getDefaultValues(CarbohydrateSymbolParams), createGeometry: createCarbohydrateSymbolMesh, createLocationIterator: CarbohydrateElementIterator, getLoci: getCarbohydrateLoci, mark: markCarbohydrate, - setUpdateState: (state: VisualUpdateState, newProps: CarbohydrateSymbolProps, currentProps: CarbohydrateSymbolProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<CarbohydrateSymbolParams>, currentProps: PD.DefaultValues<CarbohydrateSymbolParams>) => { state.createGeometry = newProps.detail !== currentProps.detail } }) diff --git a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts index 1f37817871e789dbd3ce08395919e4bf522b41b3..9f949e27f0565246d2504d7f115e3502e1289ef5 100644 --- a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts +++ b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts @@ -5,22 +5,21 @@ */ import { Link, Structure, StructureElement } from 'mol-model/structure'; -import { ComplexVisual } from '../index'; +import { ComplexVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { LinkCylinderProps, createLinkCylinderMesh, LinkCylinderParams } from './util/link'; import { Vec3 } from 'mol-math/linear-algebra'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual'; import { Interval } from 'mol-data/int'; -import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size'; import { BitFlags } from 'mol-util'; import { LinkType } from 'mol-model/structure/model/types'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { PickingId } from 'mol-geo/geometry/picking'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) { @@ -54,20 +53,17 @@ async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structur export const CrossLinkRestraintParams = { ...ComplexMeshParams, ...LinkCylinderParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1), } -export const DefaultCrossLinkRestraintProps = PD.getDefaultValues(CrossLinkRestraintParams) -export type CrossLinkRestraintProps = typeof DefaultCrossLinkRestraintProps +export type CrossLinkRestraintParams = typeof CrossLinkRestraintParams -export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintProps> { - return ComplexMeshVisual<CrossLinkRestraintProps>({ - defaultProps: DefaultCrossLinkRestraintProps, +export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintParams> { + return ComplexMeshVisual<CrossLinkRestraintParams>({ + defaultProps: PD.getDefaultValues(CrossLinkRestraintParams), createGeometry: createCrossLinkRestraintCylinderMesh, createLocationIterator: CrossLinkRestraintIterator, getLoci: getLinkLoci, mark: markLink, - setUpdateState: (state: VisualUpdateState, newProps: CrossLinkRestraintProps, currentProps: CrossLinkRestraintProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<CrossLinkRestraintParams>, currentProps: PD.DefaultValues<CrossLinkRestraintParams>) => { state.createGeometry = newProps.radialSegments !== currentProps.radialSegments } }) diff --git a/src/mol-repr/structure/visual/element-point.ts b/src/mol-repr/structure/visual/element-point.ts index 6747ae9f889c15f9fcbdc2af94ad71b8cea30241..649d54138543ca8a1212a95b4b613cb0b4969574 100644 --- a/src/mol-repr/structure/visual/element-point.ts +++ b/src/mol-repr/structure/visual/element-point.ts @@ -5,30 +5,26 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { getElementLoci, StructureElementIterator, markElement } from './util/element'; import { Vec3 } from 'mol-math/linear-algebra'; -import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size'; import { UnitsPointsVisual, UnitsPointsParams } from '../units-visual'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Points } from 'mol-geo/geometry/points/points'; import { PointsBuilder } from 'mol-geo/geometry/points/points-builder'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; export const ElementPointParams = { ...UnitsPointsParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 3, 0, 20, 0.1), pointSizeAttenuation: PD.Boolean('Point Size Attenuation', '', false), } -export const DefaultElementPointProps = PD.getDefaultValues(ElementPointParams) -export type ElementPointProps = typeof DefaultElementPointProps +export type ElementPointParams = typeof ElementPointParams // TODO size -export async function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementPointProps, points: Points) { +export async function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.DefaultValues<ElementPointParams>, points: Points) { const elements = unit.elements const n = elements.length const builder = PointsBuilder.create(n, n / 10, points) @@ -47,14 +43,14 @@ export async function createElementPoint(ctx: VisualContext, unit: Unit, structu return builder.getPoints() } -export function ElementPointVisual(): UnitsVisual<ElementPointProps> { - return UnitsPointsVisual<ElementPointProps>({ - defaultProps: DefaultElementPointProps, +export function ElementPointVisual(): UnitsVisual<ElementPointParams> { + return UnitsPointsVisual<ElementPointParams>({ + defaultProps: PD.getDefaultValues(ElementPointParams), createGeometry: createElementPoint, createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, - setUpdateState: (state: VisualUpdateState, newProps: ElementPointProps, currentProps: ElementPointProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<ElementPointParams>, currentProps: PD.DefaultValues<ElementPointParams>) => { } }) diff --git a/src/mol-repr/structure/visual/element-sphere.ts b/src/mol-repr/structure/visual/element-sphere.ts index 73d25eb01ea69f678a8b73c7ecbd1299c5efc3db..31e01cd664f81d95f595fd7f4721209214962434 100644 --- a/src/mol-repr/structure/visual/element-sphere.ts +++ b/src/mol-repr/structure/visual/element-sphere.ts @@ -5,31 +5,29 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { createElementSphereMesh, markElement, getElementLoci, StructureElementIterator } from './util/element'; import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; +import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size'; export const ElementSphereParams = { ...UnitsMeshParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1), + sizeTheme: PD.Select<BuiltInSizeThemeName>('Size Theme', '', 'physical', BuiltInSizeThemeOptions), sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1), detail: PD.Numeric('Sphere Detail', '', 0, 0, 3, 1), } -export const DefaultElementSphereProps = PD.getDefaultValues(ElementSphereParams) -export type ElementSphereProps = typeof DefaultElementSphereProps +export type ElementSphereParams = typeof ElementSphereParams -export function ElementSphereVisual(): UnitsVisual<ElementSphereProps> { - return UnitsMeshVisual<ElementSphereProps>({ - defaultProps: DefaultElementSphereProps, +export function ElementSphereVisual(): UnitsVisual<ElementSphereParams> { + return UnitsMeshVisual<ElementSphereParams>({ + defaultProps: PD.getDefaultValues(ElementSphereParams), createGeometry: createElementSphereMesh, createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, - setUpdateState: (state: VisualUpdateState, newProps: ElementSphereProps, currentProps: ElementSphereProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<ElementSphereParams>, currentProps: PD.DefaultValues<ElementSphereParams>) => { state.createGeometry = newProps.detail !== currentProps.detail } }) diff --git a/src/mol-repr/structure/visual/gaussian-density-point.ts b/src/mol-repr/structure/visual/gaussian-density-point.ts index 5fd0069f1d7482b63d89baf3118d13b966a72781..2e8e1eaa1e9e3075e687494dce3760988bbee9f9 100644 --- a/src/mol-repr/structure/visual/gaussian-density-point.ts +++ b/src/mol-repr/structure/visual/gaussian-density-point.ts @@ -5,29 +5,25 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { StructureElementIterator } from './util/element'; import { EmptyLoci } from 'mol-model/loci'; import { Vec3 } from 'mol-math/linear-algebra'; import { UnitsPointsVisual, UnitsPointsParams } from '../units-visual'; -import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size'; import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Points } from 'mol-geo/geometry/points/points'; import { PointsBuilder } from 'mol-geo/geometry/points/points-builder'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; export const GaussianDensityPointParams = { ...UnitsPointsParams, ...GaussianDensityParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1), pointSizeAttenuation: PD.Boolean('Point Size Attenuation', '', false), } -export const DefaultGaussianDensityPointProps = PD.getDefaultValues(GaussianDensityPointParams) -export type GaussianDensityPointProps = typeof DefaultGaussianDensityPointProps +export type GaussianDensityPointParams = typeof GaussianDensityPointParams export async function createGaussianDensityPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, points?: Points) { const { transform, field: { space, data } } = await unit.computeGaussianDensity(props, ctx.runtime, ctx.webgl) @@ -59,14 +55,14 @@ export async function createGaussianDensityPoint(ctx: VisualContext, unit: Unit, return builder.getPoints() } -export function GaussianDensityPointVisual(): UnitsVisual<GaussianDensityPointProps> { - return UnitsPointsVisual<GaussianDensityPointProps>({ - defaultProps: DefaultGaussianDensityPointProps, +export function GaussianDensityPointVisual(): UnitsVisual<GaussianDensityPointParams> { + return UnitsPointsVisual<GaussianDensityPointParams>({ + defaultProps: PD.getDefaultValues(GaussianDensityPointParams), createGeometry: createGaussianDensityPoint, createLocationIterator: StructureElementIterator.fromGroup, getLoci: () => EmptyLoci, mark: () => false, - setUpdateState: (state: VisualUpdateState, newProps: GaussianDensityPointProps, currentProps: GaussianDensityPointProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianDensityPointParams>, currentProps: PD.DefaultValues<GaussianDensityPointParams>) => { if (newProps.resolution !== currentProps.resolution) state.createGeometry = true if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true diff --git a/src/mol-repr/structure/visual/gaussian-density-volume.ts b/src/mol-repr/structure/visual/gaussian-density-volume.ts index 89f8b90f0f191a0201e72288c454ffe1c892bdc5..78e48960de447d3a6a61b24b308e227da73fb3d2 100644 --- a/src/mol-repr/structure/visual/gaussian-density-volume.ts +++ b/src/mol-repr/structure/visual/gaussian-density-volume.ts @@ -5,15 +5,15 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { UnitsDirectVolumeVisual, UnitsDirectVolumeParams } from '../units-visual'; import { StructureElementIterator, getElementLoci, markElement } from './util/element'; import { GaussianDensityProps, GaussianDensityParams, computeUnitGaussianDensityTexture } from 'mol-model/structure/structure/unit/gaussian-density'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; async function createGaussianDensityVolume(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> { const { runtime, webgl } = ctx @@ -31,17 +31,16 @@ export const GaussianDensityVolumeParams = { ...UnitsDirectVolumeParams, ...GaussianDensityParams, } -export const DefaultGaussianDensityVolumeProps = PD.getDefaultValues(GaussianDensityVolumeParams) -export type GaussianDensityVolumeProps = typeof DefaultGaussianDensityVolumeProps +export type GaussianDensityVolumeParams = typeof GaussianDensityVolumeParams -export function GaussianDensityVolumeVisual(): UnitsVisual<GaussianDensityVolumeProps> { - return UnitsDirectVolumeVisual<GaussianDensityVolumeProps>({ - defaultProps: DefaultGaussianDensityVolumeProps, +export function GaussianDensityVolumeVisual(): UnitsVisual<GaussianDensityVolumeParams> { + return UnitsDirectVolumeVisual<GaussianDensityVolumeParams>({ + defaultProps: PD.getDefaultValues(GaussianDensityVolumeParams), createGeometry: createGaussianDensityVolume, createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, - setUpdateState: (state: VisualUpdateState, newProps: GaussianDensityVolumeProps, currentProps: GaussianDensityVolumeProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianDensityVolumeParams>, currentProps: PD.DefaultValues<GaussianDensityVolumeParams>) => { if (newProps.resolution !== currentProps.resolution) state.createGeometry = true if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true if (newProps.smoothness !== currentProps.smoothness) { diff --git a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts index 185cc9a3ce9e89a88755fd0c64d9093378016df6..cc52de76708ab8149f2515353d4da44cd00d770d 100644 --- a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts +++ b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts @@ -5,7 +5,7 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; import { StructureElementIterator, getElementLoci, markElement } from './util/element'; @@ -13,8 +13,8 @@ import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> { const { smoothness } = props @@ -38,17 +38,16 @@ export const GaussianSurfaceParams = { ...UnitsMeshParams, ...GaussianDensityParams, } -export const DefaultGaussianSurfaceProps = PD.getDefaultValues(GaussianSurfaceParams) -export type GaussianSurfaceProps = typeof DefaultGaussianSurfaceProps +export type GaussianSurfaceParams = typeof GaussianSurfaceParams -export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceProps> { - return UnitsMeshVisual<GaussianSurfaceProps>({ - defaultProps: DefaultGaussianSurfaceProps, +export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceParams> { + return UnitsMeshVisual<GaussianSurfaceParams>({ + defaultProps: PD.getDefaultValues(GaussianSurfaceParams), createGeometry: createGaussianSurfaceMesh, createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, - setUpdateState: (state: VisualUpdateState, newProps: GaussianSurfaceProps, currentProps: GaussianSurfaceProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianSurfaceParams>, currentProps: PD.DefaultValues<GaussianSurfaceParams>) => { if (newProps.resolution !== currentProps.resolution) state.createGeometry = true if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true diff --git a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts index b8f0d342e21e49c6743fc60945471b7725d865c8..1ed95db64705df238c8461b2c360004d1b932447 100644 --- a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts +++ b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts @@ -5,17 +5,16 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { UnitsLinesVisual, UnitsLinesParams } from '../units-visual'; import { StructureElementIterator, getElementLoci, markElement } from './util/element'; import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density'; import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; import { Lines } from 'mol-geo/geometry/lines/lines'; import { computeMarchingCubesLines } from 'mol-geo/util/marching-cubes/algorithm'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, lines?: Lines): Promise<Lines> { const { smoothness } = props @@ -36,21 +35,18 @@ async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure export const GaussianWireframeParams = { ...UnitsLinesParams, ...GaussianDensityParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 2, 0, 10, 0.1), lineSizeAttenuation: PD.Boolean('Line Size Attenuation', '', false), } -export const DefaultGaussianWireframeProps = PD.getDefaultValues(GaussianWireframeParams) -export type GaussianWireframeProps = typeof DefaultGaussianWireframeProps +export type GaussianWireframeParams = typeof GaussianWireframeParams -export function GaussianWireframeVisual(): UnitsVisual<GaussianWireframeProps> { - return UnitsLinesVisual<GaussianWireframeProps>({ - defaultProps: DefaultGaussianWireframeProps, +export function GaussianWireframeVisual(): UnitsVisual<GaussianWireframeParams> { + return UnitsLinesVisual<GaussianWireframeParams>({ + defaultProps: PD.getDefaultValues(GaussianWireframeParams), createGeometry: createGaussianWireframe, createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, - setUpdateState: (state: VisualUpdateState, newProps: GaussianWireframeProps, currentProps: GaussianWireframeProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianWireframeParams>, currentProps: PD.DefaultValues<GaussianWireframeParams>) => { if (newProps.resolution !== currentProps.resolution) state.createGeometry = true if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true diff --git a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts index 7a96419a33dff1f19d4710eb2a24768692e05ccb..f38fb1683ace88a9a3757915621ef0339fcd3f86 100644 --- a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts +++ b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts @@ -5,7 +5,7 @@ */ import { Link, Structure, StructureElement } from 'mol-model/structure'; -import { ComplexVisual } from '../index'; +import { ComplexVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link'; import { Vec3 } from 'mol-math/linear-algebra'; @@ -16,8 +16,8 @@ import { BitFlags } from 'mol-util'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { PickingId } from 'mol-geo/geometry/picking'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) { const links = structure.links @@ -53,18 +53,19 @@ export const InterUnitLinkParams = { ...ComplexMeshParams, ...LinkCylinderParams, } -export const DefaultInterUnitLinkProps = PD.getDefaultValues(InterUnitLinkParams) -export type InterUnitLinkProps = typeof DefaultInterUnitLinkProps +export type InterUnitLinkParams = typeof InterUnitLinkParams -export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkProps> { - return ComplexMeshVisual<InterUnitLinkProps>({ - defaultProps: DefaultInterUnitLinkProps, +export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkParams> { + return ComplexMeshVisual<InterUnitLinkParams>({ + defaultProps: PD.getDefaultValues(InterUnitLinkParams), createGeometry: createInterUnitLinkCylinderMesh, createLocationIterator: LinkIterator.fromStructure, getLoci: getLinkLoci, mark: markLink, - setUpdateState: (state: VisualUpdateState, newProps: InterUnitLinkProps, currentProps: InterUnitLinkProps) => { - state.createGeometry = newProps.radialSegments !== currentProps.radialSegments + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<InterUnitLinkParams>, currentProps: PD.DefaultValues<InterUnitLinkParams>) => { + if (newProps.linkScale !== currentProps.linkScale) state.createGeometry = true + if (newProps.linkSpacing !== currentProps.linkSpacing) state.createGeometry = true + if (newProps.radialSegments !== currentProps.radialSegments) state.createGeometry = true } }) } diff --git a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts index 69d2aafd1fc1f377ebe200cd00511dc477d0d439..d9f55abcbab9d2ff8a8e30759036252751e68ec6 100644 --- a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts +++ b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts @@ -6,22 +6,21 @@ */ import { Unit, Link, StructureElement, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link'; import { Vec3 } from 'mol-math/linear-algebra'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { UnitsMeshVisual, UnitsMeshParams, StructureGroup } from '../units-visual'; import { Interval } from 'mol-data/int'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; import { BitFlags } from 'mol-util'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { PickingId } from 'mol-geo/geometry/picking'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; -async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) { +async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.DefaultValues<IntraUnitLinkParams>, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) const location = StructureElement.create(unit) @@ -30,6 +29,7 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s const links = unit.links const { edgeCount, a, b, edgeProps, offset } = links const { order: _order, flags: _flags } = edgeProps + const { sizeFactor } = props if (!edgeCount) return Mesh.createEmpty(mesh) @@ -57,7 +57,7 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s flags: (edgeIndex: number) => BitFlags.create(_flags[edgeIndex]), radius: (edgeIndex: number) => { location.element = elements[a[edgeIndex]] - return theme.size.size(location) + return theme.size.size(location) * sizeFactor } } @@ -67,22 +67,21 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s export const IntraUnitLinkParams = { ...UnitsMeshParams, ...LinkCylinderParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1), - sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1), + sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01), } -export const DefaultIntraUnitLinkProps = PD.getDefaultValues(IntraUnitLinkParams) -export type IntraUnitLinkProps = typeof DefaultIntraUnitLinkProps +export type IntraUnitLinkParams = typeof IntraUnitLinkParams -export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> { - return UnitsMeshVisual<IntraUnitLinkProps>({ - defaultProps: DefaultIntraUnitLinkProps, +export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkParams> { + return UnitsMeshVisual<IntraUnitLinkParams>({ + defaultProps: PD.getDefaultValues(IntraUnitLinkParams), createGeometry: createIntraUnitLinkCylinderMesh, createLocationIterator: LinkIterator.fromGroup, getLoci: getLinkLoci, mark: markLink, setUpdateState: (state: VisualUpdateState, newProps: LinkCylinderProps, currentProps: LinkCylinderProps) => { - state.createGeometry = newProps.radialSegments !== currentProps.radialSegments + if (newProps.linkScale !== currentProps.linkScale) state.createGeometry = true + if (newProps.linkSpacing !== currentProps.linkSpacing) state.createGeometry = true + if (newProps.radialSegments !== currentProps.radialSegments) state.createGeometry = true } }) } diff --git a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts index e92c42fa78d777109294ad211ae44056fd300415..2d9d82716184d6fea1a108a5b9bac1329c990834 100644 --- a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts +++ b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts @@ -5,7 +5,7 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Segmentation } from 'mol-data/int'; import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types'; @@ -17,8 +17,8 @@ import { Box } from 'mol-geo/primitive/box'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; const p1 = Vec3.zero() const p2 = Vec3.zero() @@ -34,10 +34,17 @@ const t = Mat4.identity() const sVec = Vec3.zero() const box = Box() -// TODO define props, should be scalable -async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: {}, mesh?: Mesh) { +export const NucleotideBlockMeshParams = { + sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01), +} +export const DefaultNucleotideBlockMeshProps = PD.getDefaultValues(NucleotideBlockMeshParams) +export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps + +async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: NucleotideBlockMeshProps, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) + const { sizeFactor } = props + // TODO better vertex count estimate const builder = MeshBuilder.create(256, 128, mesh) @@ -64,7 +71,7 @@ async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structu const parentId = modifiedResidues.parentId.get(compId) if (parentId !== undefined) compId = parentId let idx1 = -1, idx2 = -1, idx3 = -1, idx4 = -1, idx5 = -1, idx6 = -1 - let width = 4.5, height = 4.5, depth = 0.5 + let width = 4.5, height = 4.5, depth = 2.5 * sizeFactor if (isPurinBase(compId)) { height = 4.5 @@ -87,7 +94,7 @@ async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structu if (idx5 !== -1 && idx6 !== -1) { pos(idx5, p5); pos(idx6, p6) builder.setGroup(i) - addCylinder(builder, p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 }) + addCylinder(builder, p5, p6, 1, { radiusTop: 1 * sizeFactor, radiusBottom: 1 * sizeFactor }) if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1) { pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4); Vec3.normalize(v12, Vec3.sub(v12, p2, p1)) @@ -113,14 +120,14 @@ async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structu } export const NucleotideBlockParams = { - ...UnitsMeshParams + ...UnitsMeshParams, + ...NucleotideBlockMeshParams } -export const DefaultNucleotideBlockProps = PD.getDefaultValues(NucleotideBlockParams) -export type NucleotideBlockProps = typeof DefaultNucleotideBlockProps +export type NucleotideBlockParams = typeof NucleotideBlockParams -export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockProps> { - return UnitsMeshVisual<NucleotideBlockProps>({ - defaultProps: DefaultNucleotideBlockProps, +export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockParams> { + return UnitsMeshVisual<NucleotideBlockParams>({ + defaultProps: PD.getDefaultValues(NucleotideBlockParams), createGeometry: createNucleotideBlockMesh, createLocationIterator: NucleotideLocationIterator.fromGroup, getLoci: getNucleotideElementLoci, diff --git a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts index 6e3d2a7ca6965267f8bccedfa4da95500d78dd41..11b9329754206a9bb79281f8b876e489909c3089 100644 --- a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts +++ b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts @@ -5,7 +5,7 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { PolymerBackboneIterator } from './util/polymer'; import { getElementLoci, markElement, StructureElementIterator } from './util/element'; @@ -17,8 +17,8 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { CylinderProps } from 'mol-geo/primitive/cylinder'; import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; export const PolymerBackboneCylinderParams = { radialSegments: PD.Numeric('Radial Segments', '', 16, 3, 56, 1), @@ -69,18 +69,17 @@ export const PolymerBackboneParams = { ...UnitsMeshParams, ...PolymerBackboneCylinderParams, } -export const DefaultPolymerBackboneProps = PD.getDefaultValues(PolymerBackboneParams) -export type PolymerBackboneProps = typeof DefaultPolymerBackboneProps +export type PolymerBackboneParams = typeof PolymerBackboneParams -export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> { - return UnitsMeshVisual<PolymerBackboneProps>({ - defaultProps: DefaultPolymerBackboneProps, +export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneParams> { + return UnitsMeshVisual<PolymerBackboneParams>({ + defaultProps: PD.getDefaultValues(PolymerBackboneParams), createGeometry: createPolymerBackboneCylinderMesh, // TODO create a specialized location iterator createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, - setUpdateState: (state: VisualUpdateState, newProps: PolymerBackboneProps, currentProps: PolymerBackboneProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<PolymerBackboneParams>, currentProps: PD.DefaultValues<PolymerBackboneParams>) => { state.createGeometry = newProps.radialSegments !== currentProps.radialSegments } }) diff --git a/src/mol-repr/structure/visual/polymer-direction-wedge.ts b/src/mol-repr/structure/visual/polymer-direction-wedge.ts index d0f1fe4f5bc51174ea7555d4ef094d4a65931c40..1bca05adcfdb1c1a0de29ae367b39fdf042f0bb8 100644 --- a/src/mol-repr/structure/visual/polymer-direction-wedge.ts +++ b/src/mol-repr/structure/visual/polymer-direction-wedge.ts @@ -5,18 +5,17 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types'; import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; -import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Wedge } from 'mol-geo/primitive/wedge'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; const t = Mat4.identity() const sVec = Vec3.zero() @@ -31,15 +30,16 @@ const heightFactor = 6 const wedge = Wedge() export const PolymerDirectionWedgeParams = { - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01), } export const DefaultPolymerDirectionWedgeProps = PD.getDefaultValues(PolymerDirectionWedgeParams) export type PolymerDirectionWedgeProps = typeof DefaultPolymerDirectionWedgeProps async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerDirectionWedgeProps, mesh?: Mesh) { const polymerElementCount = unit.polymerElements.length + if (!polymerElementCount) return Mesh.createEmpty(mesh) + const { sizeFactor } = props const vertexCount = polymerElementCount * 24 const builder = MeshBuilder.create(vertexCount, vertexCount / 10, mesh) @@ -62,7 +62,7 @@ async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, s interpolateCurveSegment(state, v, tension, shift) if ((isSheet && !v.secStrucChange) || !isSheet) { - const size = theme.size.size(v.center) + const size = theme.size.size(v.center) * sizeFactor const depth = depthFactor * size const width = widthFactor * size const height = heightFactor * size @@ -92,12 +92,11 @@ export const PolymerDirectionParams = { ...UnitsMeshParams, ...PolymerDirectionWedgeParams } -export const DefaultPolymerDirectionProps = PD.getDefaultValues(PolymerDirectionParams) -export type PolymerDirectionProps = typeof DefaultPolymerDirectionProps +export type PolymerDirectionParams = typeof PolymerDirectionParams -export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionProps> { - return UnitsMeshVisual<PolymerDirectionProps>({ - defaultProps: DefaultPolymerDirectionProps, +export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionParams> { + return UnitsMeshVisual<PolymerDirectionParams>({ + defaultProps: PD.getDefaultValues(PolymerDirectionParams), createGeometry: createPolymerDirectionWedgeMesh, createLocationIterator: PolymerLocationIterator.fromGroup, getLoci: getPolymerElementLoci, diff --git a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts index 60585ab0d8965770c9662f1eb28825c750b64bf9..d26da145855cc7b2b095d42b7ff2c8640fb2610d 100644 --- a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts +++ b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts @@ -5,12 +5,11 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { PolymerGapIterator, PolymerGapLocationIterator, markPolymerGapElement, getPolymerGapElementLoci } from './util/polymer'; import { Vec3 } from 'mol-math/linear-algebra'; import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; -import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { LinkCylinderParams } from './util/link'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; @@ -18,12 +17,13 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { CylinderProps } from 'mol-geo/primitive/cylinder'; import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere'; import { addFixedCountDashedCylinder } from 'mol-geo/geometry/mesh/builder/cylinder'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; const segmentCount = 10 export const PolymerGapCylinderParams = { + sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01), radialSegments: PD.Numeric('Radial Segments', '', 16, 3, 56, 1), } export const DefaultPolymerGapCylinderProps = PD.getDefaultValues(PolymerGapCylinderParams) @@ -33,7 +33,7 @@ async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, stru const polymerGapCount = unit.gapElements.length if (!polymerGapCount) return Mesh.createEmpty(mesh) - const { radialSegments } = props + const { sizeFactor, radialSegments } = props const vertexCountEstimate = segmentCount * radialSegments * 2 * polymerGapCount * 2 const builder = MeshBuilder.create(vertexCountEstimate, vertexCountEstimate / 10, mesh) @@ -57,11 +57,11 @@ async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, stru pos(centerA.element, pA) pos(centerB.element, pB) - cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA) + cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA) * sizeFactor builder.setGroup(i) addFixedCountDashedCylinder(builder, pA, pB, 0.5, segmentCount, cylinderProps) - cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB) + cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB) * sizeFactor builder.setGroup(i + 1) addFixedCountDashedCylinder(builder, pB, pA, 0.5, segmentCount, cylinderProps) } @@ -78,9 +78,6 @@ async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, stru export const InterUnitLinkParams = { ...UnitsMeshParams, ...LinkCylinderParams, - sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), - sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1), - sizeFactor: PD.Numeric('Size Factor', '', 0.3, 0, 10, 0.1), } export const DefaultIntraUnitLinkProps = PD.getDefaultValues(InterUnitLinkParams) export type IntraUnitLinkProps = typeof DefaultIntraUnitLinkProps @@ -89,17 +86,16 @@ export const PolymerGapParams = { ...UnitsMeshParams, ...PolymerGapCylinderParams } -export const DefaultPolymerGapProps = PD.getDefaultValues(PolymerGapParams) -export type PolymerGapProps = typeof DefaultPolymerGapProps +export type PolymerGapParams = typeof PolymerGapParams -export function PolymerGapVisual(): UnitsVisual<PolymerGapProps> { - return UnitsMeshVisual<PolymerGapProps>({ - defaultProps: DefaultPolymerGapProps, +export function PolymerGapVisual(): UnitsVisual<PolymerGapParams> { + return UnitsMeshVisual<PolymerGapParams>({ + defaultProps: PD.getDefaultValues(PolymerGapParams), createGeometry: createPolymerGapCylinderMesh, createLocationIterator: PolymerGapLocationIterator.fromGroup, getLoci: getPolymerGapElementLoci, mark: markPolymerGapElement, - setUpdateState: (state: VisualUpdateState, newProps: PolymerGapProps, currentProps: PolymerGapProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<PolymerGapParams>, currentProps: PD.DefaultValues<PolymerGapParams>) => { state.createGeometry = newProps.radialSegments !== currentProps.radialSegments } }) diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts index 8f75d29ed49f88e8e72ec399f79b6f6801835a9d..21c20324bd032d98cf1d3b448dbc618d6e09b0de 100644 --- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-repr/structure/visual/polymer-trace-mesh.ts @@ -5,7 +5,7 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { UnitsVisual } from '../index'; +import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer'; import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types'; @@ -15,10 +15,11 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { addSheet } from 'mol-geo/geometry/mesh/builder/sheet'; import { addTube } from 'mol-geo/geometry/mesh/builder/tube'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; export const PolymerTraceMeshParams = { + sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01), linearSegments: PD.Numeric('Linear Segments', '', 8, 1, 48, 1), radialSegments: PD.Numeric('Radial Segments', '', 16, 3, 56, 1), aspectRatio: PD.Numeric('Aspect Ratio', '', 5, 0.1, 5, 0.1), @@ -33,7 +34,7 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: const polymerElementCount = unit.polymerElements.length if (!polymerElementCount) return Mesh.createEmpty(mesh) - const { linearSegments, radialSegments, aspectRatio, arrowFactor } = props + const { sizeFactor, linearSegments, radialSegments, aspectRatio, arrowFactor } = props const vertexCount = linearSegments * radialSegments * polymerElementCount + (radialSegments + 1) * polymerElementCount * 2 const builder = MeshBuilder.create(vertexCount, vertexCount / 10, mesh) @@ -56,7 +57,7 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: interpolateCurveSegment(state, v, tension, shift) - let width = theme.size.size(v.center) + let width = theme.size.size(v.center) * sizeFactor if (isCoarse) width *= aspectRatio / 2 if (isSheet) { @@ -89,17 +90,16 @@ export const PolymerTraceParams = { ...UnitsMeshParams, ...PolymerTraceMeshParams } -export const DefaultPolymerTraceProps = PD.getDefaultValues(PolymerTraceParams) -export type PolymerTraceProps = typeof DefaultPolymerTraceProps +export type PolymerTraceParams = typeof PolymerTraceParams -export function PolymerTraceVisual(): UnitsVisual<PolymerTraceProps> { - return UnitsMeshVisual<PolymerTraceProps>({ - defaultProps: DefaultPolymerTraceProps, +export function PolymerTraceVisual(): UnitsVisual<PolymerTraceParams> { + return UnitsMeshVisual<PolymerTraceParams>({ + defaultProps: PD.getDefaultValues(PolymerTraceParams), createGeometry: createPolymerTraceMesh, createLocationIterator: PolymerLocationIterator.fromGroup, getLoci: getPolymerElementLoci, mark: markPolymerElement, - setUpdateState: (state: VisualUpdateState, newProps: PolymerTraceProps, currentProps: PolymerTraceProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<PolymerTraceParams>, currentProps: PD.DefaultValues<PolymerTraceParams>) => { state.createGeometry = ( newProps.linearSegments !== currentProps.linearSegments || newProps.radialSegments !== currentProps.radialSegments || diff --git a/src/mol-repr/structure/visual/util/common.ts b/src/mol-repr/structure/visual/util/common.ts index 25598d6e34efc0d16031ab5984b1b25f4548f2a8..ba4b10e1abbedf706eff43930065417da81d1997 100644 --- a/src/mol-repr/structure/visual/util/common.ts +++ b/src/mol-repr/structure/visual/util/common.ts @@ -5,17 +5,19 @@ */ import { Unit, Structure } from 'mol-model/structure'; -import { StructureProps } from '../../index'; import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolumeRenderObject } from 'mol-gl/render-object'; import { Mat4 } from 'mol-math/linear-algebra'; import { TransformData, createTransform, createIdentityTransform } from 'mol-geo/geometry/transform-data'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { LocationIterator } from 'mol-geo/util/location-iterator'; -import { createRenderableState, Theme } from 'mol-geo/geometry/geometry'; +import { createRenderableState } from 'mol-geo/geometry/geometry'; import { Points } from 'mol-geo/geometry/points/points'; import { Lines } from 'mol-geo/geometry/lines/lines'; import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; -import { VisualContext } from 'mol-repr'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams } from 'mol-repr/structure/representation'; export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) { const unitCount = units.length @@ -47,16 +49,14 @@ export function includesUnitKind(unitKinds: UnitKind[], unit: Unit) { // mesh -type StructureMeshProps = Mesh.Props & StructureProps - -export async function createComplexMeshRenderObject(ctx: VisualContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: StructureMeshProps) { +export async function createComplexMeshRenderObject(ctx: VisualContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureMeshParams>) { const transform = createIdentityTransform() const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, theme, props) const state = createRenderableState(props) return createMeshRenderObject(values, state) } -export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: StructureMeshProps) { +export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureMeshParams>) { const transform = createUnitsTransform(group) const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, theme, props) const state = createRenderableState(props) @@ -65,9 +65,7 @@ export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Uni // points -type StructurePointsProps = Points.Props & StructureProps - -export async function createUnitsPointsRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, theme: Theme, props: StructurePointsProps) { +export async function createUnitsPointsRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructurePointsParams>) { const transform = createUnitsTransform(group) const values = await Points.createValues(ctx.runtime, points, transform, locationIt, theme, props) const state = createRenderableState(props) @@ -76,9 +74,7 @@ export async function createUnitsPointsRenderObject(ctx: VisualContext, group: U // lines -type StructureLinesProps = Lines.Props & StructureProps - -export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, theme: Theme, props: StructureLinesProps) { +export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureLinesParams>) { const transform = createUnitsTransform(group) const values = await Lines.createValues(ctx.runtime, lines, transform, locationIt, theme, props) const state = createRenderableState(props) @@ -87,9 +83,7 @@ export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Un // direct-volume -type StructureDirectVolumeProps = DirectVolume.Props & StructureProps - -export async function createUnitsDirectVolumeRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: StructureDirectVolumeProps) { +export async function createUnitsDirectVolumeRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureDirectVolumeParams>) { const transform = createUnitsTransform(group) const values = await DirectVolume.createValues(ctx.runtime, directVolume, transform, locationIt, theme, props) const state = createRenderableState(props) diff --git a/src/mol-repr/structure/visual/util/element.ts b/src/mol-repr/structure/visual/util/element.ts index 5bc0407967feadb297c55e6570e0d1549d32398a..38188c4380c4094574a36ca89319b2486844503e 100644 --- a/src/mol-repr/structure/visual/util/element.ts +++ b/src/mol-repr/structure/visual/util/element.ts @@ -14,16 +14,17 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere'; import { PickingId } from 'mol-geo/geometry/picking'; import { LocationIterator } from 'mol-geo/util/location-iterator'; -import { VisualContext } from 'mol-repr'; -import { Theme } from 'mol-geo/geometry/geometry'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme } from 'mol-theme/theme'; import { StructureGroup } from 'mol-repr/structure/units-visual'; export interface ElementSphereMeshProps { detail: number, + sizeFactor: number } export async function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereMeshProps, mesh?: Mesh) { - const { detail } = props + const { detail, sizeFactor } = props const { elements } = unit; const elementCount = elements.length; @@ -40,7 +41,7 @@ export async function createElementSphereMesh(ctx: VisualContext, unit: Unit, st pos(elements[i], v) meshBuilder.setGroup(i) - addSphere(meshBuilder, v, theme.size.size(l), detail) + addSphere(meshBuilder, v, theme.size.size(l) * sizeFactor, detail) if (i % 10000 === 0 && ctx.runtime.shouldUpdate) { await ctx.runtime.update({ message: 'Sphere mesh', current: i, max: elementCount }); diff --git a/src/mol-repr/structure/visual/util/link.ts b/src/mol-repr/structure/visual/util/link.ts index 2ea26bff5c8311b488d343fbee6d9e18795eaf6a..179464f52096739384cce2cc571078bb9a5afe36 100644 --- a/src/mol-repr/structure/visual/util/link.ts +++ b/src/mol-repr/structure/visual/util/link.ts @@ -13,12 +13,11 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; import { CylinderProps } from 'mol-geo/primitive/cylinder'; import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from 'mol-geo/geometry/mesh/builder/cylinder'; import { LocationIterator } from 'mol-geo/util/location-iterator'; -import { VisualContext } from 'mol-repr'; +import { VisualContext } from 'mol-repr/representation'; export const LinkCylinderParams = { linkScale: PD.Range('Link Scale', '', 0.4, 0, 1, 0.1), linkSpacing: PD.Range('Link Spacing', '', 1, 0, 2, 0.01), - linkRadius: PD.Range('Link Radius', '', 0.25, 0, 10, 0.05), radialSegments: PD.Numeric('Radial Segments', '', 16, 3, 56, 1), } export const DefaultLinkCylinderProps = PD.getDefaultValues(LinkCylinderParams) diff --git a/src/mol-repr/util.ts b/src/mol-repr/util.ts index 4db8e0cee7c100f8650074fb216192c9bec3379b..ad7d101646a55f50ca2820049e251d538b4ff006 100644 --- a/src/mol-repr/util.ts +++ b/src/mol-repr/util.ts @@ -7,8 +7,6 @@ import { defaults } from 'mol-util'; import { Structure } from 'mol-model/structure'; import { VisualQuality } from 'mol-geo/geometry/geometry'; -import { SizeProps } from 'mol-geo/geometry/size-data'; -import { ColorProps } from 'mol-geo/geometry/color-data'; export interface VisualUpdateState { updateTransform: boolean @@ -33,24 +31,6 @@ export namespace VisualUpdateState { } } -export function sizeChanged(oldProps: SizeProps, newProps: SizeProps) { - return ( - oldProps.sizeTheme !== newProps.sizeTheme || - oldProps.sizeValue !== newProps.sizeValue || - oldProps.sizeFactor !== newProps.sizeFactor - ) -} - -export function colorChanged(oldProps: ColorProps, newProps: ColorProps) { - return ( - oldProps.colorTheme !== newProps.colorTheme || - oldProps.colorValue !== newProps.colorValue || - oldProps.colorDomain !== newProps.colorDomain || - oldProps.colorList !== newProps.colorList || - oldProps.colorMap !== newProps.colorMap - ) -} - // export interface QualityProps { diff --git a/src/mol-repr/volume/direct-volume.ts b/src/mol-repr/volume/direct-volume.ts index 50e4cc7a210b0e6bdc842251b5b8431f68c6374f..d3890e7e5e58554526cd1a76d20a1a093ccf5ca2 100644 --- a/src/mol-repr/volume/direct-volume.ts +++ b/src/mol-repr/volume/direct-volume.ts @@ -6,9 +6,9 @@ import { VolumeData } from 'mol-model/volume' import { RuntimeContext } from 'mol-task' -import { VolumeVisual, VolumeRepresentation } from './index'; +import { VolumeVisual, VolumeRepresentation } from './representation'; import { createDirectVolumeRenderObject } from 'mol-gl/render-object'; -import { Loci, EmptyLoci } from 'mol-model/loci'; +import { EmptyLoci } from 'mol-model/loci'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Box3D } from 'mol-math/geometry'; @@ -17,11 +17,10 @@ import { createTexture } from 'mol-gl/webgl/texture'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { createIdentityTransform } from 'mol-geo/geometry/transform-data'; import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; -import { Geometry, createRenderableState, Theme } from 'mol-geo/geometry/geometry'; -import { PickingId } from 'mol-geo/geometry/picking'; -import { MarkerAction } from 'mol-geo/geometry/marker-data'; +import { Geometry, createRenderableState } from 'mol-geo/geometry/geometry'; import { VisualUpdateState } from 'mol-repr/util'; -import { VisualContext, RepresentationContext } from 'mol-repr'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme, ThemeRegistryContext } from 'mol-theme/theme'; function getBoundingBox(gridDimension: Vec3, transform: Mat4) { const bbox = Box3D.empty() @@ -113,8 +112,6 @@ function createVolumeTexture3d(volume: VolumeData) { const array = new Uint8Array(width * height * depth * 4) const textureVolume = { array, width, height, depth } - console.log('stats', stats) - let i = 0 for (let z = 0; z < depth; ++z) { for (let y = 0; y < height; ++y) { @@ -123,36 +120,11 @@ function createVolumeTexture3d(volume: VolumeData) { console.log(get(data, x, y, z), ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255) } array[i + 3] = ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255 - // array[i + 3] = ((get(data, x, z, y) - stats.min) / (stats.max - stats.min)) * 255 - // array[i + 3] = ((get(data, y, x, z) - stats.min) / (stats.max - stats.min)) * 255 - // array[i + 3] = ((get(data, y, z, x) - stats.min) / (stats.max - stats.min)) * 255 - // array[i + 3] = ((get(data, z, y, x) - stats.min) / (stats.max - stats.min)) * 255 - // array[i + 3] = ((get(data, z, x, y) - stats.min) / (stats.max - stats.min)) * 255 i += 4 } } } - // let i = 0 - // for (let z = 0; z < depth; ++z) { - // for (let x = 0; x < width; ++x) { - // for (let y = 0; y < height; ++y) { - // array[i + 3] = ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255 - // i += 4 - // } - // } - // } - - // let i = 0 - // for (let x = 0; x < width; ++x) { - // for (let y = 0; y < height; ++y) { - // for (let z = 0; z < depth; ++z) { - // array[i + 3] = ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255 - // i += 4 - // } - // } - // } - return textureVolume } @@ -171,7 +143,7 @@ export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, v // -export async function createDirectVolume(ctx: VisualContext, volume: VolumeData, props: DirectVolumeProps, directVolume?: DirectVolume) { +export async function createDirectVolume(ctx: VisualContext, volume: VolumeData, props: PD.DefaultValues<DirectVolumeParams>, directVolume?: DirectVolume) { const { runtime, webgl } = ctx if (webgl === undefined) throw new Error('DirectVolumeVisual requires `webgl` in props') @@ -187,18 +159,20 @@ export const DirectVolumeParams = { ...Geometry.Params, ...DirectVolume.Params } -export const DefaultDirectVolumeProps = PD.getDefaultValues(DirectVolumeParams) -export type DirectVolumeProps = typeof DefaultDirectVolumeProps +export type DirectVolumeParams = typeof DirectVolumeParams +export function getDirectVolumeParams(ctx: ThemeRegistryContext, volume: VolumeData) { + return PD.clone(DirectVolumeParams) +} -export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> { - return VolumeVisual<DirectVolumeProps>({ - defaultProps: DefaultDirectVolumeProps, +export function DirectVolumeVisual(): VolumeVisual<DirectVolumeParams> { + return VolumeVisual<DirectVolumeParams>({ + defaultProps: PD.getDefaultValues(DirectVolumeParams), createGeometry: createDirectVolume, getLoci: () => EmptyLoci, mark: () => false, - setUpdateState: (state: VisualUpdateState, newProps: DirectVolumeProps, currentProps: DirectVolumeProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<DirectVolumeParams>, currentProps: PD.DefaultValues<DirectVolumeParams>) => { }, - createRenderObject: async (ctx: VisualContext, geometry: DirectVolume, locationIt: LocationIterator, theme: Theme, props: DirectVolumeProps) => { + createRenderObject: async (ctx: VisualContext, geometry: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<DirectVolumeParams>) => { const transform = createIdentityTransform() const values = await DirectVolume.createValues(ctx.runtime, geometry, transform, locationIt, theme, props) const state = createRenderableState(props) @@ -208,30 +182,6 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> { }) } -export function DirectVolumeRepresentation(): VolumeRepresentation<DirectVolumeProps> { - let currentProps: DirectVolumeProps - const volumeRepr = VolumeRepresentation(DirectVolumeVisual) - return { - label: 'Direct Volume', - params: DirectVolumeParams, - get renderObjects() { - return [ ...volumeRepr.renderObjects ] - }, - get props() { - return { ...volumeRepr.props } - }, - createOrUpdate: (ctx: RepresentationContext, props: Partial<DirectVolumeProps> = {}, volume?: VolumeData) => { - currentProps = Object.assign({}, DefaultDirectVolumeProps, currentProps, props) - return volumeRepr.createOrUpdate(ctx, currentProps, volume) - }, - getLoci: (pickingId: PickingId) => { - return volumeRepr.getLoci(pickingId) - }, - mark: (loci: Loci, action: MarkerAction) => { - return volumeRepr.mark(loci, action) - }, - destroy() { - volumeRepr.destroy() - } - } +export function DirectVolumeRepresentation(): VolumeRepresentation<DirectVolumeParams> { + return VolumeRepresentation('Direct Volume', getDirectVolumeParams, DirectVolumeVisual) } \ No newline at end of file diff --git a/src/mol-repr/volume/isosurface-mesh.ts b/src/mol-repr/volume/isosurface-mesh.ts index 13eb6f3744de657b50995a8c4ab0a736f23d8789..fd54afcefbdd4ac1f4370f124dd8361cfe18ef42 100644 --- a/src/mol-repr/volume/isosurface-mesh.ts +++ b/src/mol-repr/volume/isosurface-mesh.ts @@ -6,19 +6,18 @@ */ import { VolumeData } from 'mol-model/volume' -import { VolumeVisual, VolumeRepresentation } from './index'; +import { VolumeVisual, VolumeRepresentation } from './representation'; import { createMeshRenderObject } from 'mol-gl/render-object'; -import { Loci, EmptyLoci } from 'mol-model/loci'; +import { EmptyLoci } from 'mol-model/loci'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm'; import { LocationIterator } from 'mol-geo/util/location-iterator'; import { createIdentityTransform } from 'mol-geo/geometry/transform-data'; -import { createRenderableState, Theme } from 'mol-geo/geometry/geometry'; -import { PickingId } from 'mol-geo/geometry/picking'; -import { MarkerAction } from 'mol-geo/geometry/marker-data'; +import { createRenderableState } from 'mol-geo/geometry/geometry'; import { VisualUpdateState } from 'mol-repr/util'; -import { RepresentationContext, VisualContext } from 'mol-repr'; +import { VisualContext } from 'mol-repr/representation'; +import { Theme, ThemeRegistryContext } from 'mol-theme/theme'; interface VolumeIsosurfaceProps { isoValueAbsolute: number @@ -45,19 +44,21 @@ export const IsosurfaceParams = { isoValueAbsolute: PD.Range('Iso Value Absolute', '', 0.22, -1, 1, 0.01), isoValueRelative: PD.Range('Iso Value Relative', '', 2, -10, 10, 0.1), } -export const DefaultIsosurfaceProps = PD.getDefaultValues(IsosurfaceParams) -export type IsosurfaceProps = typeof DefaultIsosurfaceProps +export type IsosurfaceParams = typeof IsosurfaceParams +export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: VolumeData) { + return PD.clone(IsosurfaceParams) +} -export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> { - return VolumeVisual<IsosurfaceProps>({ - defaultProps: DefaultIsosurfaceProps, +export function IsosurfaceVisual(): VolumeVisual<IsosurfaceParams> { + return VolumeVisual<IsosurfaceParams>({ + defaultProps: PD.getDefaultValues(IsosurfaceParams), createGeometry: createVolumeIsosurface, getLoci: () => EmptyLoci, mark: () => false, - setUpdateState: (state: VisualUpdateState, newProps: IsosurfaceProps, currentProps: IsosurfaceProps) => { + setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<IsosurfaceParams>, currentProps: PD.DefaultValues<IsosurfaceParams>) => { if (newProps.isoValueAbsolute !== currentProps.isoValueAbsolute) state.createGeometry = true }, - createRenderObject: async (ctx: VisualContext, geometry: Mesh, locationIt: LocationIterator, theme: Theme, props: IsosurfaceProps) => { + createRenderObject: async (ctx: VisualContext, geometry: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<IsosurfaceParams>) => { const transform = createIdentityTransform() const values = await Mesh.createValues(ctx.runtime, geometry, transform, locationIt, theme, props) const state = createRenderableState(props) @@ -67,30 +68,6 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> { }) } -export function IsosurfaceRepresentation(): VolumeRepresentation<IsosurfaceProps> { - let currentProps: IsosurfaceProps - const volumeRepr = VolumeRepresentation(IsosurfaceVisual) - return { - label: 'Isosurface', - params: IsosurfaceParams, - get renderObjects() { - return [ ...volumeRepr.renderObjects ] - }, - get props() { - return { ...volumeRepr.props } - }, - createOrUpdate: (ctx: RepresentationContext, props: Partial<IsosurfaceProps> = {}, volume?: VolumeData) => { - currentProps = Object.assign({}, DefaultIsosurfaceProps, currentProps, props) - return volumeRepr.createOrUpdate(ctx, currentProps, volume) - }, - getLoci: (pickingId: PickingId) => { - return volumeRepr.getLoci(pickingId) - }, - mark: (loci: Loci, action: MarkerAction) => { - return volumeRepr.mark(loci, action) - }, - destroy() { - volumeRepr.destroy() - } - } +export function IsosurfaceRepresentation(): VolumeRepresentation<IsosurfaceParams> { + return VolumeRepresentation('Isosurface', getIsosurfaceParams, IsosurfaceVisual) } \ No newline at end of file diff --git a/src/mol-repr/volume/registry.ts b/src/mol-repr/volume/registry.ts new file mode 100644 index 0000000000000000000000000000000000000000..30f5902bc10bd64d28b2a52a084c3614a9914e7c --- /dev/null +++ b/src/mol-repr/volume/registry.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { RepresentationProvider, RepresentationRegistry } from '../representation'; +import { VolumeData } from 'mol-model/volume'; + +export class VolumeRepresentationRegistry extends RepresentationRegistry<VolumeData> { + constructor() { + super() + Object.keys(BuiltInVolumeRepresentations).forEach(name => { + const p = (BuiltInVolumeRepresentations as { [k: string]: RepresentationProvider<VolumeData, any> })[name] + this.add(name, p.factory, p.getParams) + }) + } +} + +export const BuiltInVolumeRepresentations = { + // TODO +} +export type BuiltInVolumeRepresentationsName = keyof typeof BuiltInVolumeRepresentations +export const BuiltInVolumeRepresentationsNames = Object.keys(BuiltInVolumeRepresentations) +export const BuiltInVolumeRepresentationsOptions = BuiltInVolumeRepresentationsNames.map(n => [n, n] as [BuiltInVolumeRepresentationsName, string]) \ No newline at end of file diff --git a/src/mol-repr/volume/index.ts b/src/mol-repr/volume/representation.ts similarity index 66% rename from src/mol-repr/volume/index.ts rename to src/mol-repr/volume/representation.ts index 729f53cff94fda9ccffa9a722ea05f9143ec643f..0831340cf2e8576834d0240357cae77ffb8044c6 100644 --- a/src/mol-repr/volume/index.ts +++ b/src/mol-repr/volume/representation.ts @@ -5,10 +5,10 @@ */ import { Task } from 'mol-task' -import { RepresentationProps, Representation, Visual, RepresentationContext, VisualContext } from '..'; +import { Representation, Visual, RepresentationContext, VisualContext, RepresentationProvider, RepresentationParamsGetter } from '../representation'; import { VolumeData, VolumeIsoValue } from 'mol-model/volume'; import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci'; -import { Geometry, updateRenderableState, Theme, createTheme } from 'mol-geo/geometry/geometry'; +import { Geometry, updateRenderableState } 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'; @@ -19,36 +19,38 @@ import { LocationIterator } from 'mol-geo/util/location-iterator'; import { NullLocation } from 'mol-model/location'; import { VisualUpdateState } from 'mol-repr/util'; import { ValueCell } from 'mol-util'; +import { ThemeProps, Theme, createTheme } from 'mol-theme/theme'; +import { BehaviorSubject } from 'rxjs'; -export interface VolumeVisual<P extends RepresentationProps = {}> extends Visual<VolumeData, P> { } +export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { } type VolumeRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject -interface VolumeVisualBuilder<P extends VolumeProps, G extends Geometry> { - defaultProps: P - createGeometry(ctx: VisualContext, volumeData: VolumeData, props: P, geometry?: G): Promise<G> +interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> { + defaultProps: PD.DefaultValues<P> + createGeometry(ctx: VisualContext, volumeData: VolumeData, props: PD.DefaultValues<P>, geometry?: G): Promise<G> getLoci(pickingId: PickingId, id: number): Loci mark(loci: Loci, apply: (interval: Interval) => boolean): boolean - setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void + setUpdateState(state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>): void } -interface VolumeVisualGeometryBuilder<P extends VolumeProps, G extends Geometry> extends VolumeVisualBuilder<P, G> { - createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: P): Promise<VolumeRenderObject> - updateValues(values: RenderableValues, newProps: P): void +interface VolumeVisualGeometryBuilder<P extends VolumeParams, G extends Geometry> extends VolumeVisualBuilder<P, G> { + createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: PD.DefaultValues<P>): Promise<VolumeRenderObject> + updateValues(values: RenderableValues, newProps: PD.DefaultValues<P>): void } -export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometryBuilder<P, Geometry>) { +export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeometryBuilder<P, Geometry>) { const { defaultProps, createGeometry, getLoci, mark, setUpdateState } = builder const { createRenderObject, updateValues } = builder const updateState = VisualUpdateState.create() - let currentProps: P + let currentProps: PD.DefaultValues<P> let renderObject: VolumeRenderObject | undefined let currentVolume: VolumeData let geometry: Geometry let locationIt: LocationIterator - async function create(ctx: VisualContext, volume: VolumeData, theme: Theme, props: Partial<VolumeProps> = {}) { + async function create(ctx: VisualContext, volume: VolumeData, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) { currentProps = Object.assign({}, defaultProps, props) if (props.isoValueRelative) { currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative) @@ -60,7 +62,7 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr renderObject = await createRenderObject(ctx, geometry, locationIt, theme, currentProps) } - async function update(ctx: VisualContext, theme: Theme, props: Partial<VolumeProps> = {}) { + async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) { if (!renderObject) return const newProps = Object.assign({}, currentProps, props) @@ -85,7 +87,7 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr return { get renderObject () { return renderObject }, - async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<VolumeProps> = {}, volume?: VolumeData) { + async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}, volume?: VolumeData) { if (!volume && !currentVolume) { throw new Error('missing volume') } else if (volume && (!currentVolume || !renderObject)) { @@ -132,63 +134,81 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr } } -export interface VolumeRepresentation<P extends RepresentationProps = {}> extends Representation<VolumeData, P> { } +export interface VolumeRepresentation<P extends VolumeParams> extends Representation<VolumeData, P> { } + +export type VolumeRepresentationProvider<P extends VolumeParams> = RepresentationProvider<VolumeData, P> + +// export const VolumeParams = { ...Geometry.Params, isoValueAbsolute: PD.Range('Iso Value Absolute', '', 0.22, -1, 1, 0.01), isoValueRelative: PD.Range('Iso Value Relative', '', 2, -10, 10, 0.1), } -export const DefaultVolumeProps = PD.getDefaultValues(VolumeParams) -export type VolumeProps = typeof DefaultVolumeProps +export type VolumeParams = typeof VolumeParams + +export function VolumeRepresentation<P extends VolumeParams>(label: string, getParams: RepresentationParamsGetter<VolumeData, P>, visualCtor: (volume: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> { + const updated = new BehaviorSubject(0) + let visual: VolumeVisual<P> -export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeData: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> { - let visual: VolumeVisual<any> - let _props: P + let _volume: VolumeData + let _props: PD.DefaultValues<P> + let _params: P let _theme: Theme let busy = false - function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, volumeData?: VolumeData) { - _props = Object.assign({}, DefaultVolumeProps, _props, props) - _theme = createTheme(_props) + function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) { + if (volume && volume !== _volume) { + _params = getParams(ctx, volume) + _volume = volume + if (!_props) _props = PD.getDefaultValues(_params) + } + _props = Object.assign({}, _props, props) + _theme = createTheme(ctx, _props, themeProps, {}, _theme) return Task.create('VolumeRepresentation.create', async runtime => { // TODO queue it somehow if (busy) return - if (!visual && !volumeData) { - throw new Error('volumeData missing') - } else if (volumeData && !visual) { + if (!visual && !volume) { + throw new Error('volume data missing') + } else if (volume && !visual) { busy = true - visual = visualCtor(volumeData) - await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volumeData) + visual = visualCtor(volume) + await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volume) busy = false } else { busy = true - await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volumeData) + await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volume) busy = false } + updated.next(updated.getValue() + 1) }); } + function getLoci(pickingId: PickingId) { + return visual ? visual.getLoci(pickingId) : EmptyLoci + } + + function mark(loci: Loci, action: MarkerAction) { + return visual ? visual.mark(loci, action) : false + } + + function destroy() { + if (visual) visual.destroy() + } + return { - label: 'Volume', - params: VolumeParams, + label, get renderObjects() { return visual && visual.renderObject ? [ visual.renderObject ] : [] }, get props () { return _props }, + get params() { return _params }, + get updated() { return updated }, createOrUpdate, - getLoci(pickingId: PickingId) { - // TODO - return EmptyLoci - }, - mark(loci: Loci, action: MarkerAction) { - // TODO - return false - }, - destroy() { - // TODO - } + getLoci, + mark, + destroy } } \ No newline at end of file diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts index d087e249c80cbd17e3e30f49102a5bdde537f012..b92620b7c7e8109b03a5ff84cb228c2194865c9d 100644 --- a/src/mol-theme/color.ts +++ b/src/mol-theme/color.ts @@ -4,26 +4,25 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Color, ColorMap } from 'mol-util/color'; -import { Structure } from 'mol-model/structure'; +import { Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; import { ColorType } from 'mol-geo/geometry/color-data'; - -import { ElementIndexColorTheme } from './color/element-index'; -import { CarbohydrateSymbolColorTheme } from './color/carbohydrate-symbol'; -import { ChainIdColorTheme } from './color/chain-id'; -import { ElementSymbolColorTheme } from './color/element-symbol'; -import { UnitIndexColorTheme } from './color/unit-index'; -import { UniformColorTheme } from './color/uniform'; -import { CrossLinkColorTheme } from './color/cross-link'; -import { ShapeGroupColorTheme } from './color/shape-group'; -import { CustomColorTheme } from './color/custom'; -import { ResidueNameColorTheme } from './color/residue-name'; -import { SequenceIdColorTheme } from './color/sequence-id'; -import { SecondaryStructureColorTheme } from './color/secondary-structure'; -import { MoleculeTypeColorTheme } from './color/molecule-type'; -import { PolymerIndexColorTheme } from './color/polymer-index'; -import { ColorMatplotlib, ColorBrewer, ColorOther } from 'mol-util/color/tables'; +import { CarbohydrateSymbolColorThemeProvider } from './color/carbohydrate-symbol'; +import { UniformColorTheme, UniformColorThemeProvider } from './color/uniform'; +import { deepEqual } from 'mol-util'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { ThemeDataContext } from 'mol-theme/theme'; +import { ChainIdColorThemeProvider } from './color/chain-id'; +import { CrossLinkColorThemeProvider } from './color/cross-link'; +import { ElementIndexColorThemeProvider } from './color/element-index'; +import { ElementSymbolColorThemeProvider } from './color/element-symbol'; +import { MoleculeTypeColorThemeProvider } from './color/molecule-type'; +import { PolymerIndexColorThemeProvider } from './color/polymer-index'; +import { ResidueNameColorThemeProvider } from './color/residue-name'; +import { SecondaryStructureColorThemeProvider } from './color/secondary-structure'; +import { SequenceIdColorThemeProvider } from './color/sequence-id'; +import { ShapeGroupColorThemeProvider } from './color/shape-group'; +import { UnitIndexColorThemeProvider } from './color/unit-index'; export type LocationColor = (location: Location, isSecondary: boolean) => Color @@ -37,29 +36,6 @@ export function ScaleLegend(minLabel: string, maxLabel: string, colors: Color[]) return { kind: 'scale-legend', minLabel, maxLabel, colors } } -export type ColorScaleName = ( - 'default' | - keyof typeof ColorBrewer | keyof typeof ColorMatplotlib | keyof typeof ColorOther -) -export const ColorScaleNames = [ - 'default', - ...Object.keys(ColorBrewer), ...Object.keys(ColorMatplotlib), ...Object.keys(ColorOther) -] -export const ColorScaleOptions = ColorScaleNames.map(n => [n, n] as [ColorScaleName, string]) - -export function getColorScaleFromName(name: string) { - if (name === 'default') { - return - } else if (name in ColorBrewer) { - return ColorBrewer[name as keyof typeof ColorBrewer] - } else if (name in ColorMatplotlib) { - return ColorMatplotlib[name as keyof typeof ColorMatplotlib] - } else if (name in ColorOther) { - return ColorOther[name as keyof typeof ColorOther] - } - console.warn(`unknwon color list named '${name}'`) -} - export interface TableLegend { kind: 'table-legend' table: [ string, Color ][] @@ -68,77 +44,76 @@ export function TableLegend(table: [ string, Color ][]): TableLegend { return { kind: 'table-legend', table } } -export interface ColorThemeFeatures { - /** Does allow providing a structure object */ - structure?: boolean - /** Does allow providing a volume object */ - volume?: boolean - /** Does allow providing a list of colors (for creating a scale) */ - list?: boolean - /** Does allow providing a map of colors */ - map?: boolean - /** Does allow providing the boundaries for the scale */ - domain?: boolean - /** Does allow providing a single/special color value */ - value?: boolean -} +export type ColorThemeProps = { [k: string]: any } -export interface ColorTheme { - features: ColorThemeFeatures - granularity: ColorType - color: LocationColor - description?: string - legend?: ScaleLegend | TableLegend +export { ColorTheme } +interface ColorTheme<P extends ColorThemeProps = {}> { + readonly granularity: ColorType + readonly color: LocationColor + readonly props: Readonly<P> + readonly description?: string + readonly legend?: Readonly<ScaleLegend | TableLegend> } +namespace ColorTheme { + export type Props = { [k: string]: any } + export const Empty = UniformColorTheme({}, { value: Color(0xCCCCCC) }) -export function ColorTheme(props: ColorThemeProps): ColorTheme { - switch (props.name) { - case 'carbohydrate-symbol': return CarbohydrateSymbolColorTheme(props) - case 'chain-id': return ChainIdColorTheme(props) - case 'cross-link': return CrossLinkColorTheme(props) - case 'custom': return CustomColorTheme(props) - case 'element-index': return ElementIndexColorTheme(props) - case 'element-symbol': return ElementSymbolColorTheme(props) - case 'molecule-type': return MoleculeTypeColorTheme(props) - case 'polymer-index': return PolymerIndexColorTheme(props) - case 'residue-name': return ResidueNameColorTheme(props) - case 'secondary-structure': return SecondaryStructureColorTheme(props) - case 'sequence-id': return SequenceIdColorTheme(props) - case 'shape-group': return ShapeGroupColorTheme(props) - case 'unit-index': return UnitIndexColorTheme(props) - case 'uniform': return UniformColorTheme(props) + export function areEqual(themeA: ColorTheme, themeB: ColorTheme) { + return themeA === themeB && deepEqual(themeA.props, themeB.props) } -} -export interface ColorThemeProps { - name: ColorThemeName - domain?: [number, number] - value?: Color - list?: Color[] - map?: ColorMap<any> - structure?: Structure - color?: LocationColor - granularity?: ColorType, - description?: string, - legend?: ScaleLegend | TableLegend + export interface Provider<P extends PD.Params> { + readonly factory: (ctx: ThemeDataContext, props: PD.DefaultValues<P>) => ColorTheme<PD.DefaultValues<P>> + readonly params: (ctx: ThemeDataContext) => P + } + + export class Registry { + private _list: { name: string, provider: Provider<any> }[] = [] + private _map = new Map<string, Provider<any>>() + + constructor() { + Object.keys(BuiltInColorThemes).forEach(name => { + const p = (BuiltInColorThemes as { [k: string]: Provider<any> })[name] + this.add(name, p.factory, p.params) + }) + } + + add<P extends PD.Params>(name: string, factory: Provider<P>['factory'], params: Provider<P>['params']) { + const provider = { factory, params } as Provider<P> + this._list.push({ name, provider }) + this._map.set(name, provider) + } + + get(id: string) { + return this._map.get(id) + } + + create(id: string, ctx: ThemeDataContext, props = {}) { + const provider = this.get(id) + return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.params(ctx)), ...props }) : Empty + } + + get list() { + return this._list + } + } } -export const ColorThemeInfo = { - 'carbohydrate-symbol': {}, - 'chain-id': {}, - 'cross-link': {}, - 'custom': {}, - 'element-index': {}, - 'element-symbol': {}, - 'molecule-type': {}, - 'polymer-index': {}, - 'residue-name': {}, - 'secondary-structure': {}, - 'sequence-id': {}, - 'shape-group': {}, - 'unit-index': {}, - 'uniform': {}, +export const BuiltInColorThemes = { + 'carbohydrate-symbol': CarbohydrateSymbolColorThemeProvider, + 'chain-id': ChainIdColorThemeProvider, + 'cross-link': CrossLinkColorThemeProvider, + 'element-index': ElementIndexColorThemeProvider, + 'element-symbol': ElementSymbolColorThemeProvider, + 'molecule-type': MoleculeTypeColorThemeProvider, + 'polymer-index': PolymerIndexColorThemeProvider, + 'residue-name': ResidueNameColorThemeProvider, + 'secondary-structure': SecondaryStructureColorThemeProvider, + 'sequence-id': SequenceIdColorThemeProvider, + 'shape-group': ShapeGroupColorThemeProvider, + 'unit-index': UnitIndexColorThemeProvider, + 'uniform': UniformColorThemeProvider, } -export type ColorThemeName = keyof typeof ColorThemeInfo -export const ColorThemeNames = Object.keys(ColorThemeInfo) -export const ColorThemeOptions = ColorThemeNames.map(n => [n, n] as [ColorThemeName, string]) \ No newline at end of file +export type BuiltInColorThemeName = keyof typeof BuiltInColorThemes +export const BuiltInColorThemeNames = Object.keys(BuiltInColorThemes) +export const BuiltInColorThemeOptions = BuiltInColorThemeNames.map(n => [n, n] as [BuiltInColorThemeName, string]) \ No newline at end of file diff --git a/src/mol-theme/color/carbohydrate-symbol.ts b/src/mol-theme/color/carbohydrate-symbol.ts index a21853d87f941f899f7071ff3260b55b9d346541..24940f7a6e813e9c837e738615758ca21b02304f 100644 --- a/src/mol-theme/color/carbohydrate-symbol.ts +++ b/src/mol-theme/color/carbohydrate-symbol.ts @@ -8,17 +8,34 @@ import { StructureElement, Link, ElementIndex, Unit } from 'mol-model/structure' import { SaccharideColors, MonosaccharidesColorTable } from 'mol-model/structure/structure/carbohydrates/constants'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, LocationColor, TableLegend } from '../color'; +import { ColorTheme, LocationColor, TableLegend } from '../color'; import { Color } from 'mol-util/color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; const DefaultColor = Color(0xCCCCCC) const Description = 'Assigns colors according to the Symbol Nomenclature for Glycans (SNFG).' -export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme { +// name: ColorThemeName +// domain?: [number, number] +// value?: Color +// list?: Color[] +// map?: ColorMap<any> + +export const CarbohydrateSymbolColorThemeParams = { + // domain: PD.Interval('Color Domain', '', [0, 1]), + // value: PD.Color('Color Value', '', DefaultColor), +} +export function getCarbohydrateSymbolColorThemeParams(ctx: ThemeDataContext) { + return CarbohydrateSymbolColorThemeParams // TODO return copy +} +export type CarbohydrateSymbolColorThemeProps = PD.DefaultValues<typeof CarbohydrateSymbolColorThemeParams> + +export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: CarbohydrateSymbolColorThemeProps): ColorTheme<CarbohydrateSymbolColorThemeProps> { let color: LocationColor - if (props.structure) { - const { elements, getElementIndex, getAnomericCarbon } = props.structure.carbohydrates + if (ctx.structure) { + const { elements, getElementIndex, getAnomericCarbon } = ctx.structure.carbohydrates const getColor = (unit: Unit, index: ElementIndex) => { const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index[index] @@ -47,10 +64,14 @@ export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme } return { - features: {}, granularity: 'group', color: color, + props: props, description: Description, legend: TableLegend(MonosaccharidesColorTable) } +} + +export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<typeof CarbohydrateSymbolColorThemeParams> = { + factory: CarbohydrateSymbolColorTheme, params: getCarbohydrateSymbolColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/chain-id.ts b/src/mol-theme/color/chain-id.ts index c91825e4729d16b1777eceb17796f75906109baf..90045dc3a4a795a072b1b88726bb6c9227405d83 100644 --- a/src/mol-theme/color/chain-id.ts +++ b/src/mol-theme/color/chain-id.ts @@ -8,11 +8,22 @@ import { Unit, StructureProperties, StructureElement, Link } from 'mol-model/str import { ColorScale, Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, LocationColor } from '../color'; +import { ColorTheme, LocationColor } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; +import { ColorListOptions, ColorListName } from 'mol-util/color/scale'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives every chain a color based on its `asym_id` value.' +export const ChainIdColorThemeParams = { + list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions), +} +export function getChainIdColorThemeParams(ctx: ThemeDataContext) { + return ChainIdColorThemeParams // TODO return copy +} +export type ChainIdColorThemeProps = PD.DefaultValues<typeof ChainIdColorThemeParams> + function getAsymId(unit: Unit): StructureElement.Property<string> { switch (unit.kind) { case Unit.Kind.Atomic: @@ -23,14 +34,13 @@ function getAsymId(unit: Unit): StructureElement.Property<string> { } } -export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme { +export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThemeProps): ColorTheme<ChainIdColorThemeProps> { let color: LocationColor - const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' }) - // const table: [string, Color][] = [] + const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' }) - if (props.structure) { + if (ctx.structure) { const l = StructureElement.create() - const { models } = props.structure + const { models } = ctx.structure const asymIdSerialMap = new Map<string, number>() let j = 0 for (let i = 0, il = models.length; i <il; ++i) { @@ -44,8 +54,6 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme { scale.setDomain(0, asymIdSerialMap.size - 1) const scaleColor = scale.color - // asymIdSerialMap.forEach((v, k) => table.push([k, scaleColor(v)])) - color = (location: Location): Color => { if (StructureElement.isLocation(location)) { const asym_id = getAsymId(location.unit) @@ -63,11 +71,14 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: { structure: true, list: true }, granularity: 'group', color, + props, description: Description, - // legend: scale ? TableLegend(table) : undefined legend: scale ? scale.legend : undefined } +} + +export const ChainIdColorThemeProvider: ColorTheme.Provider<typeof ChainIdColorThemeParams> = { + factory: ChainIdColorTheme, params: getChainIdColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/cross-link.ts b/src/mol-theme/color/cross-link.ts index d24b79e4a92b4e45f7f887df5e333066f0be69c0..031c57eeff17860c584b2fb3e6c8f8f26accd22e 100644 --- a/src/mol-theme/color/cross-link.ts +++ b/src/mol-theme/color/cross-link.ts @@ -8,14 +8,24 @@ import { Link } from 'mol-model/structure'; import { Color, ColorScale } from 'mol-util/color'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, LocationColor } from '../color'; +import { ColorTheme, LocationColor } from '../color'; import { Vec3 } from 'mol-math/linear-algebra'; -import { ColorBrewer } from 'mol-util/color/tables'; -import { defaults } from 'mol-util'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; +import { ColorListName, ColorListOptions } from 'mol-util/color/scale'; const DefaultColor = Color(0xCCCCCC) const Description = 'Colors cross-links by the deviation of the observed distance versus the modeled distance (e.g. `ihm_cross_link_restraint.distance_threshold`).' +export const CrossLinkColorThemeParams = { + domain: PD.Interval('Color Domain', '', [-10, 10]), + list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions), +} +export function getCrossLinkColorThemeParams(ctx: ThemeDataContext) { + return CrossLinkColorThemeParams // TODO return copy +} +export type CrossLinkColorThemeProps = PD.DefaultValues<typeof CrossLinkColorThemeParams> + const distVecA = Vec3.zero(), distVecB = Vec3.zero() function linkDistance(link: Link.Location) { link.aUnit.conformation.position(link.aIndex, distVecA) @@ -23,15 +33,15 @@ function linkDistance(link: Link.Location) { return Vec3.distance(distVecA, distVecB) } -export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme { +export function CrossLinkColorTheme(ctx: ThemeDataContext, props: CrossLinkColorThemeProps): ColorTheme<CrossLinkColorThemeProps> { let color: LocationColor let scale: ColorScale | undefined = undefined - if (props.structure) { - const crosslinks = props.structure.crossLinkRestraints + if (ctx.structure) { + const crosslinks = ctx.structure.crossLinkRestraints scale = ColorScale.create({ - domain: defaults(props.domain, [ -10, 10 ]), - list: defaults(props.list, ColorBrewer.RdYlBu) + domain: props.domain, + listOrName: props.list }) const scaleColor = scale.color @@ -49,10 +59,14 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: { list: true, domain: true, structure: true }, granularity: 'group', color, + props, description: Description, legend: scale ? scale.legend : undefined } +} + +export const CrossLinkColorThemeProvider: ColorTheme.Provider<typeof CrossLinkColorThemeParams> = { + factory: CrossLinkColorTheme, params: getCrossLinkColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/custom.ts b/src/mol-theme/color/custom.ts deleted file mode 100644 index 4eb5a10ea4497ba5c419607e1467245b4758dc0e..0000000000000000000000000000000000000000 --- a/src/mol-theme/color/custom.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import { Color } from 'mol-util/color'; -import { ColorThemeProps, ColorTheme } from '../color'; -import { defaults } from 'mol-util'; - -const DefaultColor = Color(0xCCCCCC) - -export function CustomColorTheme(props: ColorThemeProps): ColorTheme { - const value = defaults(props.value, DefaultColor) - return { - features: {}, - granularity: defaults(props.granularity, 'uniform'), - color: defaults(props.color, () => value), - description: props.description, - legend: props.legend - } -} \ No newline at end of file diff --git a/src/mol-theme/color/element-index.ts b/src/mol-theme/color/element-index.ts index 2ba30ce687307d87d5caa9fb34a9db98c71c4e91..68264878048d981b7e93296a629113c13222022b 100644 --- a/src/mol-theme/color/element-index.ts +++ b/src/mol-theme/color/element-index.ts @@ -8,17 +8,28 @@ import { ColorScale, Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; import { StructureElement, Link } from 'mol-model/structure'; import { OrderedSet } from 'mol-data/int'; -import { ColorThemeProps, ColorTheme, LocationColor } from '../color'; +import { ColorTheme, LocationColor } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; +import { ColorListOptions, ColorListName } from 'mol-util/color/scale'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives every element (atom or coarse sphere/gaussian) a unique color based on the position (index) of the element in the list of elements in the structure.' -export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme { +export const ElementIndexColorThemeParams = { + list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions), +} +export function getElementIndexColorThemeParams(ctx: ThemeDataContext) { + return ElementIndexColorThemeParams // TODO return copy +} +export type ElementIndexColorThemeProps = PD.DefaultValues<typeof ElementIndexColorThemeParams> + +export function ElementIndexColorTheme(ctx: ThemeDataContext, props: ElementIndexColorThemeProps): ColorTheme<ElementIndexColorThemeProps> { let color: LocationColor - let scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' }) + let scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' }) - if (props.structure) { - const { units } = props.structure + if (ctx.structure) { + const { units } = ctx.structure const unitCount = units.length const cummulativeElementCount = new Map<number, number>() const unitIdIndex = new Map<number, number>() @@ -48,10 +59,14 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: { structure: true, list: true }, granularity: 'groupInstance', color, + props, description: Description, legend: scale ? scale.legend : undefined } +} + +export const ElementIndexColorThemeProvider: ColorTheme.Provider<typeof ElementIndexColorThemeParams> = { + factory: ElementIndexColorTheme, params: getElementIndexColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/element-symbol.ts b/src/mol-theme/color/element-symbol.ts index 8d7f87b21d0d99e686fea90c4011a3df572d7b2d..8c5d2bb095de8e752289d60c33e263a35fcc326e 100644 --- a/src/mol-theme/color/element-symbol.ts +++ b/src/mol-theme/color/element-symbol.ts @@ -8,7 +8,9 @@ import { ElementSymbol } from 'mol-model/structure/model/types'; import { Color, ColorMap } from 'mol-util/color'; import { StructureElement, Unit, Link } from 'mol-model/structure'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, TableLegend } from '../color'; +import { ColorTheme, TableLegend } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF) export const ElementSymbolColors = ColorMap({ @@ -18,12 +20,18 @@ export const ElementSymbolColors = ColorMap({ const DefaultElementSymbolColor = Color(0xFFFFFF) const Description = 'Assigns a color to every atom according to its chemical element.' +export const ElementSymbolColorThemeParams = {} +export function getElementSymbolColorThemeParams(ctx: ThemeDataContext) { + return ElementSymbolColorThemeParams // TODO return copy +} +export type ElementSymbolColorThemeProps = PD.DefaultValues<typeof ElementSymbolColorThemeParams> + export function elementSymbolColor(element: ElementSymbol): Color { const c = (ElementSymbolColors as { [k: string]: Color })[element]; return c === undefined ? DefaultElementSymbolColor : c } -export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme { +export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: ElementSymbolColorThemeProps): ColorTheme<ElementSymbolColorThemeProps> { function color(location: Location): Color { if (StructureElement.isLocation(location)) { if (Unit.isAtomic(location.unit)) { @@ -40,12 +48,16 @@ export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: {}, granularity: 'group', color, + props, description: Description, legend: TableLegend(Object.keys(ElementSymbolColors).map(name => { return [name, (ElementSymbolColors as any)[name] as Color] as [string, Color] })) } +} + +export const ElementSymbolColorThemeProvider: ColorTheme.Provider<typeof ElementSymbolColorThemeParams> = { + factory: ElementSymbolColorTheme, params: getElementSymbolColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/molecule-type.ts b/src/mol-theme/color/molecule-type.ts index 9bdc21d39a52dae10358d5f0d3c61feda2cabd44..18dd0440caf9890fd1ffeb02b71d844cd98e8034 100644 --- a/src/mol-theme/color/molecule-type.ts +++ b/src/mol-theme/color/molecule-type.ts @@ -7,9 +7,11 @@ import { Color, ColorMap } from 'mol-util/color'; import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, TableLegend } from '../color'; +import { ColorTheme, TableLegend } from '../color'; import { MoleculeType } from 'mol-model/structure/model/types'; import { getElementMoleculeType } from 'mol-model/structure/util'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; const MoleculeTypeColors = ColorMap({ water: 0x386cb0, @@ -24,6 +26,12 @@ const MoleculeTypeColors = ColorMap({ const DefaultMoleculeTypeColor = Color(0xffff99) const Description = 'Assigns a color based on the molecule type of a residue.' +export const MoleculeTypeColorThemeParams = {} +export function getMoleculeTypeColorThemeParams(ctx: ThemeDataContext) { + return MoleculeTypeColorThemeParams // TODO return copy +} +export type MoleculeTypeColorThemeProps = PD.DefaultValues<typeof MoleculeTypeColorThemeParams> + export function moleculeTypeColor(unit: Unit, element: ElementIndex): Color { const moleculeType = getElementMoleculeType(unit, element) switch (moleculeType) { @@ -38,7 +46,7 @@ export function moleculeTypeColor(unit: Unit, element: ElementIndex): Color { return DefaultMoleculeTypeColor } -export function MoleculeTypeColorTheme(props: ColorThemeProps): ColorTheme { +export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: MoleculeTypeColorThemeProps): ColorTheme<MoleculeTypeColorThemeProps> { function color(location: Location): Color { if (StructureElement.isLocation(location)) { return moleculeTypeColor(location.unit, location.element) @@ -49,12 +57,16 @@ export function MoleculeTypeColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: {}, granularity: 'group', color, + props, description: Description, legend: TableLegend(Object.keys(MoleculeTypeColors).map(name => { return [name, (MoleculeTypeColors as any)[name] as Color] as [string, Color] }).concat([[ 'Other/unknown', DefaultMoleculeTypeColor ]])) } +} + +export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<typeof MoleculeTypeColorThemeParams> = { + factory: MoleculeTypeColorTheme, params: getMoleculeTypeColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/polymer-index.ts b/src/mol-theme/color/polymer-index.ts index b17a57dfe8a526c52dc1de93d0e5cee1ba558c0f..4b54924ef469aca559fc65b70b55886f21cd5755 100644 --- a/src/mol-theme/color/polymer-index.ts +++ b/src/mol-theme/color/polymer-index.ts @@ -7,17 +7,28 @@ import { ColorScale, Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; import { StructureElement, Link } from 'mol-model/structure'; -import { ColorTheme, ColorThemeProps, LocationColor } from '../color'; +import { ColorTheme, LocationColor } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; +import { ColorListName, ColorListOptions } from 'mol-util/color/scale'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives every polymer a unique color based on the position (index) of the polymer in the list of polymers in the structure.' -export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme { +export const PolymerIndexColorThemeParams = { + list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions), +} +export function getPolymerIndexColorThemeParams(ctx: ThemeDataContext) { + return PolymerIndexColorThemeParams // TODO return copy +} +export type PolymerIndexColorThemeProps = PD.DefaultValues<typeof PolymerIndexColorThemeParams> + +export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PolymerIndexColorThemeProps): ColorTheme<PolymerIndexColorThemeProps> { let color: LocationColor - const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' }) + const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' }) - if (props.structure) { - const { units } = props.structure + if (ctx.structure) { + const { units } = ctx.structure let polymerCount = 0 for (let i = 0, il = units.length; i <il; ++i) { if (units[i].polymerElements.length > 0) ++polymerCount @@ -45,10 +56,14 @@ export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: { structure: true, list: true }, granularity: 'instance', color, + props, description: Description, legend: scale ? scale.legend : undefined } +} + +export const PolymerIndexColorThemeProvider: ColorTheme.Provider<typeof PolymerIndexColorThemeParams> = { + factory: PolymerIndexColorTheme, params: getPolymerIndexColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/residue-name.ts b/src/mol-theme/color/residue-name.ts index 756ef8c482458704ceaca1f097cfbe8f9fa9216a..ec2a645feacd0e73b395fd0fbc438f229354ab3b 100644 --- a/src/mol-theme/color/residue-name.ts +++ b/src/mol-theme/color/residue-name.ts @@ -7,7 +7,9 @@ import { Color, ColorMap } from 'mol-util/color'; import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, TableLegend } from '../color'; +import { ColorTheme, TableLegend } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; // protein colors from Jmol http://jmol.sourceforge.net/jscolors/ const ResidueNameColors = ColorMap({ @@ -59,6 +61,12 @@ const ResidueNameColors = ColorMap({ const DefaultResidueNameColor = Color(0xFF00FF) const Description = 'Assigns a color to every residue according to its name.' +export const ResidueNameColorThemeParams = {} +export function getResidueNameColorThemeParams(ctx: ThemeDataContext) { + return ResidueNameColorThemeParams // TODO return copy +} +export type ResidueNameColorThemeProps = PD.DefaultValues<typeof ResidueNameColorThemeParams> + export function residueNameColor(residueName: string): Color { const c = (ResidueNameColors as { [k: string]: Color })[residueName]; return c === undefined ? DefaultResidueNameColor : c @@ -84,7 +92,7 @@ function getCoarseCompId(unit: Unit.Spheres | Unit.Gaussians, element: ElementIn } } -export function ResidueNameColorTheme(props: ColorThemeProps): ColorTheme { +export function ResidueNameColorTheme(ctx: ThemeDataContext, props: ResidueNameColorThemeProps): ColorTheme<ResidueNameColorThemeProps> { function color(location: Location): Color { if (StructureElement.isLocation(location)) { if (Unit.isAtomic(location.unit)) { @@ -105,12 +113,16 @@ export function ResidueNameColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: {}, granularity: 'group', color, + props, description: Description, legend: TableLegend(Object.keys(ResidueNameColors).map(name => { return [name, (ResidueNameColors as any)[name] as Color] as [string, Color] }).concat([[ 'Unknown', DefaultResidueNameColor ]])) } +} + +export const ResidueNameColorThemeProvider: ColorTheme.Provider<typeof ResidueNameColorThemeParams> = { + factory: ResidueNameColorTheme, params: getResidueNameColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/secondary-structure.ts b/src/mol-theme/color/secondary-structure.ts index d9a6fd7b6e15518dd354c721417e7e1c73ca75b6..b53171be85c73d1c7209d571a127bc19c8371670 100644 --- a/src/mol-theme/color/secondary-structure.ts +++ b/src/mol-theme/color/secondary-structure.ts @@ -7,9 +7,11 @@ import { Color, ColorMap } from 'mol-util/color'; import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, TableLegend } from '../color'; +import { ColorTheme, TableLegend } from '../color'; import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types'; import { getElementMoleculeType } from 'mol-model/structure/util'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; // from Jmol http://jmol.sourceforge.net/jscolors/ (shapely) const SecondaryStructureColors = ColorMap({ @@ -29,6 +31,12 @@ const SecondaryStructureColors = ColorMap({ const DefaultSecondaryStructureColor = Color(0x808080) const Description = 'Assigns a color based on the type of secondary structure and basic molecule type.' +export const SecondaryStructureColorThemeParams = {} +export function getSecondaryStructureColorThemeParams(ctx: ThemeDataContext) { + return SecondaryStructureColorThemeParams // TODO return copy +} +export type SecondaryStructureColorThemeProps = PD.DefaultValues<typeof SecondaryStructureColorThemeParams> + export function secondaryStructureColor(unit: Unit, element: ElementIndex): Color { let secStrucType = SecondaryStructureType.create(SecondaryStructureType.Flag.None) if (Unit.isAtomic(unit)) { @@ -61,7 +69,7 @@ export function secondaryStructureColor(unit: Unit, element: ElementIndex): Colo return DefaultSecondaryStructureColor } -export function SecondaryStructureColorTheme(props: ColorThemeProps): ColorTheme { +export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: SecondaryStructureColorThemeProps): ColorTheme<SecondaryStructureColorThemeProps> { function color(location: Location): Color { if (StructureElement.isLocation(location)) { return secondaryStructureColor(location.unit, location.element) @@ -72,12 +80,16 @@ export function SecondaryStructureColorTheme(props: ColorThemeProps): ColorTheme } return { - features: {}, granularity: 'group', color, + props, description: Description, legend: TableLegend(Object.keys(SecondaryStructureColors).map(name => { return [name, (SecondaryStructureColors as any)[name] as Color] as [string, Color] }).concat([[ 'Other', DefaultSecondaryStructureColor ]])) } +} + +export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<typeof SecondaryStructureColorThemeParams> = { + factory: SecondaryStructureColorTheme, params: getSecondaryStructureColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/sequence-id.ts b/src/mol-theme/color/sequence-id.ts index 808e4ace55c17a952ee46d6dd8fc14f8c68dd8e5..a0b1b22d4a5b3d35661d67bbe3bb6dcf5db3f232 100644 --- a/src/mol-theme/color/sequence-id.ts +++ b/src/mol-theme/color/sequence-id.ts @@ -8,13 +8,22 @@ import { Unit, StructureElement, Link, ElementIndex } from 'mol-model/structure' import { ColorScale, Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme } from '../color'; -import { ColorOther } from 'mol-util/color/tables'; -import { defaults } from 'mol-util'; +import { ColorTheme } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; +import { ColorListOptions, ColorListName } from 'mol-util/color/scale'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives every polymer residue a color based on its `seq_id` value.' +export const SequenceIdColorThemeParams = { + list: PD.Select<ColorListName>('Color Scale', '', 'rainbow', ColorListOptions), +} +export function getSequenceIdColorThemeParams(ctx: ThemeDataContext) { + return SequenceIdColorThemeParams // TODO return copy +} +export type SequenceIdColorThemeProps = PD.DefaultValues<typeof SequenceIdColorThemeParams> + function getSeqId(unit: Unit, element: ElementIndex): number { const { model } = unit switch (unit.kind) { @@ -55,15 +64,12 @@ function getSequenceLength(unit: Unit, element: ElementIndex) { return model.sequence.byEntityKey[entityIndex].sequence.sequence.length } -export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme { - const p = { - ...props, - list: defaults(props.list, ColorOther.rainbow), +export function SequenceIdColorTheme(ctx: ThemeDataContext, props: SequenceIdColorThemeProps): ColorTheme<SequenceIdColorThemeProps> { + const scale = ColorScale.create({ + listOrName: props.list, minLabel: 'Start', maxLabel: 'End', - } - - const scale = ColorScale.create(p) + }) const color = (location: Location): Color => { if (StructureElement.isLocation(location)) { const { unit, element } = location @@ -84,11 +90,14 @@ export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: { list: true }, granularity: 'group', color, + props, description: Description, - // legend: scale ? TableLegend(table) : undefined legend: scale ? scale.legend : undefined } +} + +export const SequenceIdColorThemeProvider: ColorTheme.Provider<typeof SequenceIdColorThemeParams> = { + factory: SequenceIdColorTheme, params: getSequenceIdColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/shape-group.ts b/src/mol-theme/color/shape-group.ts index 0d74a08c77afa0c23f265036e0279daf2c1d76b5..172052bd7e0037c4141e207ee614b7c1304f1da1 100644 --- a/src/mol-theme/color/shape-group.ts +++ b/src/mol-theme/color/shape-group.ts @@ -4,16 +4,24 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { ColorTheme, ColorThemeProps } from '../color'; +import { ColorTheme } from '../color'; import { Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; import { Shape } from 'mol-model/shape'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; const DefaultColor = Color(0xCCCCCC) +const Description = 'Assigns colors as defined by the shape object.' -export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme { +export const ShapeGroupColorThemeParams = {} +export function getShapeGroupColorThemeParams(ctx: ThemeDataContext) { + return ShapeGroupColorThemeParams // TODO return copy +} +export type ShapeGroupColorThemeProps = PD.DefaultValues<typeof ShapeGroupColorThemeParams> + +export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: ShapeGroupColorThemeProps): ColorTheme<ShapeGroupColorThemeProps> { return { - features: {}, granularity: 'group', color: (location: Location): Color => { if (Shape.isLocation(location)) { @@ -21,7 +29,11 @@ export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme { } return DefaultColor }, - description: props.description, - legend: props.legend + props, + description: Description } +} + +export const ShapeGroupColorThemeProvider: ColorTheme.Provider<typeof ShapeGroupColorThemeParams> = { + factory: ShapeGroupColorTheme, params: getShapeGroupColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/uniform.ts b/src/mol-theme/color/uniform.ts index f7fb57769ecbdcfbdbe6a9ba6bda5507eedbd45f..c02644b3f2e9f209fa772a2c799fce408143b33b 100644 --- a/src/mol-theme/color/uniform.ts +++ b/src/mol-theme/color/uniform.ts @@ -4,20 +4,34 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { ColorTheme, ColorThemeProps, TableLegend } from '../color'; +import { ColorTheme, TableLegend } from '../color'; import { Color } from 'mol-util/color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives everything the same, uniform color.' -export function UniformColorTheme(props: ColorThemeProps): ColorTheme { +export const UniformColorThemeParams = { + value: PD.Color('Color Value', '', DefaultColor), +} +export function getUniformColorThemeParams(ctx: ThemeDataContext) { + return UniformColorThemeParams // TODO return copy +} +export type UniformColorThemeProps = PD.DefaultValues<typeof UniformColorThemeParams> + +export function UniformColorTheme(ctx: ThemeDataContext, props: UniformColorThemeProps): ColorTheme<UniformColorThemeProps> { const color = props.value || DefaultColor return { - features: {}, granularity: 'uniform', color: () => color, + props: props, description: Description, legend: TableLegend([['uniform', color]]) } +} + +export const UniformColorThemeProvider: ColorTheme.Provider<typeof UniformColorThemeParams> = { + factory: UniformColorTheme, params: getUniformColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/color/unit-index.ts b/src/mol-theme/color/unit-index.ts index a17dc82b6e40a5c7ac2782a970d248b682e0fd7b..06760c3120e24769b6f7acf0712d267b0d19bcd8 100644 --- a/src/mol-theme/color/unit-index.ts +++ b/src/mol-theme/color/unit-index.ts @@ -7,17 +7,28 @@ import { ColorScale, Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; import { StructureElement, Link } from 'mol-model/structure'; -import { ColorTheme, ColorThemeProps, LocationColor } from '../color'; +import { ColorTheme, LocationColor } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; +import { ColorListOptions, ColorListName } from 'mol-util/color/scale'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives every unit (single chain or collection of single elements) a unique color based on the position (index) of the unit in the list of units in the structure.' -export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme { +export const UnitIndexColorThemeParams = { + list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions), +} +export function getUnitIndexColorThemeParams(ctx: ThemeDataContext) { + return UnitIndexColorThemeParams // TODO return copy +} +export type UnitIndexColorThemeProps = PD.DefaultValues<typeof UnitIndexColorThemeParams> + +export function UnitIndexColorTheme(ctx: ThemeDataContext, props: UnitIndexColorThemeProps): ColorTheme<UnitIndexColorThemeProps> { let color: LocationColor - const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' }) + const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' }) - if (props.structure) { - const { units } = props.structure + if (ctx.structure) { + const { units } = ctx.structure scale.setDomain(0, units.length - 1) const unitIdColor = new Map<number, Color>() for (let i = 0, il = units.length; i <il; ++i) { @@ -37,10 +48,14 @@ export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme { } return { - features: { structure: true, list: true }, granularity: 'instance', color, + props, description: Description, legend: scale ? scale.legend : undefined } +} + +export const UnitIndexColorThemeProvider: ColorTheme.Provider<typeof UnitIndexColorThemeParams> = { + factory: UnitIndexColorTheme, params: getUnitIndexColorThemeParams } \ No newline at end of file diff --git a/src/mol-theme/size.ts b/src/mol-theme/size.ts index 65428f9d0ce22ffd84f4865a529cded363f4e083..55487c2663c3c311021a533ee82581ee32b6da92 100644 --- a/src/mol-theme/size.ts +++ b/src/mol-theme/size.ts @@ -4,35 +4,69 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Structure } from 'mol-model/structure'; import { SizeType, LocationSize } from 'mol-geo/geometry/size-data'; +import { UniformSizeTheme, UniformSizeThemeProvider } from './size/uniform'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { ThemeDataContext } from 'mol-theme/theme'; +import { PhysicalSizeThemeProvider } from './size/physical'; +import { deepEqual } from 'mol-util'; -import { PhysicalSizeTheme } from './size/physical'; -import { UniformSizeTheme } from './size/uniform'; - -export interface SizeTheme { - granularity: SizeType - size: LocationSize +export { SizeTheme } +interface SizeTheme<P extends SizeTheme.Props = {}> { + readonly granularity: SizeType + readonly size: LocationSize + readonly props: Readonly<P> + readonly description?: string } +namespace SizeTheme { + export type Props = { [k: string]: any } + export const Empty = UniformSizeTheme({}, { value: 1 }) -export function SizeTheme(props: SizeThemeProps): SizeTheme { - switch (props.name) { - case 'physical': return PhysicalSizeTheme(props) - case 'uniform': return UniformSizeTheme(props) + export function areEqual(themeA: SizeTheme, themeB: SizeTheme) { + return themeA === themeB && deepEqual(themeA.props, themeB.props) } -} -export interface SizeThemeProps { - name: 'physical' | 'uniform' - value?: number - factor?: number - structure?: Structure + export interface Provider<P extends PD.Params> { + readonly factory: (ctx: ThemeDataContext, props: PD.DefaultValues<P>) => SizeTheme<PD.DefaultValues<P>> + readonly params: (ctx: ThemeDataContext) => P + } + + export class Registry { + private _list: { name: string, provider: Provider<any> }[] = [] + private _map = new Map<string, Provider<any>>() + + constructor() { + Object.keys(BuiltInSizeThemes).forEach(name => { + const p = (BuiltInSizeThemes as { [k: string]: Provider<any> })[name] + this.add(name, p.factory, p.params) + }) + } + + add<P extends PD.Params>(name: string, factory: Provider<P>['factory'], params: Provider<P>['params']) { + const provider = { factory, params } as Provider<P> + this._list.push({ name, provider }) + this._map.set(name, provider) + } + + get(id: string) { + return this._map.get(id) + } + + create(id: string, ctx: ThemeDataContext, props = {}) { + const provider = this.get(id) + return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.params(ctx)), ...props }) : Empty + } + + get list() { + return this._list + } + } } -export const SizeThemeInfo = { - 'physical': {}, - 'uniform': {} +export const BuiltInSizeThemes = { + 'physical': PhysicalSizeThemeProvider, + 'uniform': UniformSizeThemeProvider } -export type SizeThemeName = keyof typeof SizeThemeInfo -export const SizeThemeNames = Object.keys(SizeThemeInfo) -export const SizeThemeOptions = SizeThemeNames.map(n => [n, n] as [SizeThemeName, string]) \ No newline at end of file +export type BuiltInSizeThemeName = keyof typeof BuiltInSizeThemes +export const BuiltInSizeThemeNames = Object.keys(BuiltInSizeThemes) +export const BuiltInSizeThemeOptions = BuiltInSizeThemeNames.map(n => [n, n] as [BuiltInSizeThemeName, string]) \ No newline at end of file diff --git a/src/mol-theme/size/physical.ts b/src/mol-theme/size/physical.ts index 43a885989c0faf14da2a57fbc8b3f02eabc2aede..45d43ceac6f8549e4f49c2c0db63854a047c0500 100644 --- a/src/mol-theme/size/physical.ts +++ b/src/mol-theme/size/physical.ts @@ -6,11 +6,19 @@ import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure'; import { Location } from 'mol-model/location'; -import { SizeThemeProps, SizeTheme } from '../size'; +import { SizeTheme } from '../size'; import { VdwRadius } from 'mol-model/structure/model/properties/atomic'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; const DefaultSize = 1 -const DefaultFactor = 1 +const Description = 'Assigns a physical size.' + +export const PhysicalSizeThemeParams = {} +export function getPhysicalSizeThemeParams(ctx: ThemeDataContext) { + return PhysicalSizeThemeParams // TODO return copy +} +export type PhysicalSizeThemeProps = PD.DefaultValues<typeof PhysicalSizeThemeParams> export function getPhysicalRadius(unit: Unit, element: ElementIndex): number { if (Unit.isAtomic(unit)) { @@ -26,10 +34,8 @@ export function getPhysicalRadius(unit: Unit, element: ElementIndex): number { * Create attribute data with the physical size of an element, * i.e. vdw for atoms and radius for coarse spheres */ -export function PhysicalSizeTheme(props: SizeThemeProps): SizeTheme { - const factor = props.factor || DefaultFactor - - function sizeFn(location: Location): number { +export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PhysicalSizeThemeProps): SizeTheme<PhysicalSizeThemeProps> { + function size(location: Location): number { let size: number if (StructureElement.isLocation(location)) { size = getPhysicalRadius(location.unit, location.element) @@ -38,11 +44,17 @@ export function PhysicalSizeTheme(props: SizeThemeProps): SizeTheme { } else { size = DefaultSize } - return factor * size + return size } return { granularity: 'group', - size: sizeFn + size, + props, + description: Description } +} + +export const PhysicalSizeThemeProvider: SizeTheme.Provider<typeof PhysicalSizeThemeParams> = { + factory: PhysicalSizeTheme, params: getPhysicalSizeThemeParams } \ No newline at end of file diff --git a/src/mol-theme/size/uniform.ts b/src/mol-theme/size/uniform.ts index 28634f5ee67867bd55df0f95c95158cce5a27dbf..139a4bcbc3505fde6ef80ce5270234d868deafd1 100644 --- a/src/mol-theme/size/uniform.ts +++ b/src/mol-theme/size/uniform.ts @@ -4,18 +4,31 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { SizeTheme, SizeThemeProps } from '../size'; +import { SizeTheme } from '../size'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; -const DefaultSize = 1 -const DefaultFactor = 1 +const Description = 'Gives everything the same, uniform size.' -export function UniformSizeTheme(props: SizeThemeProps): SizeTheme { - const value = props.value || DefaultSize - const factor = props.factor || DefaultFactor - const size = value * factor +export const UniformSizeThemeParams = { + value: PD.Numeric('Size Value', '', 1, 0, 20, 0.1), +} +export function getUniformSizeThemeParams(ctx: ThemeDataContext) { + return UniformSizeThemeParams // TODO return copy +} +export type UniformSizeThemeProps = PD.DefaultValues<typeof UniformSizeThemeParams> + +export function UniformSizeTheme(ctx: ThemeDataContext, props: UniformSizeThemeProps): SizeTheme<UniformSizeThemeProps> { + const size = props.value return { granularity: 'uniform', - size: () => size + size: () => size, + props, + description: Description } +} + +export const UniformSizeThemeProvider: SizeTheme.Provider<typeof UniformSizeThemeParams> = { + factory: UniformSizeTheme, params: getUniformSizeThemeParams } \ No newline at end of file diff --git a/src/mol-theme/theme.ts b/src/mol-theme/theme.ts new file mode 100644 index 0000000000000000000000000000000000000000..a5f1a3e80e8ec39bd68a122ab4eee34a3f6a2c60 --- /dev/null +++ b/src/mol-theme/theme.ts @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ColorTheme } from './color'; +import { SizeTheme } from './size'; +import { Structure } from 'mol-model/structure'; +import { VolumeData } from 'mol-model/volume'; + +export interface ThemeRegistryContext { + colorThemeRegistry: ColorTheme.Registry + sizeThemeRegistry: SizeTheme.Registry +} + +export interface ThemeDataContext { + [k: string]: any + structure?: Structure + volume?: VolumeData +} + +export interface ThemeProps { color?: {}, size?: {} } + +export interface Theme { + color: ColorTheme + size: SizeTheme + // label: LabelTheme // TODO +} + +type Props = { [k: string]: any } + +export function createTheme(ctx: ThemeRegistryContext, data: ThemeDataContext, props: Props, themeProps: ThemeProps = {}, theme?: Theme) { + theme = theme || { + color: ColorTheme.Empty, + size: SizeTheme.Empty + } + // TODO check if props have changed + if (typeof props.colorTheme === 'string') { + theme.color = ctx.colorThemeRegistry.create(props.colorTheme, data, themeProps.color) + } + if (typeof props.sizeTheme === 'string') { + theme.size = ctx.sizeThemeRegistry.create(props.sizeTheme, data, themeProps.size) + } + return theme +} \ No newline at end of file diff --git a/src/mol-util/color/color.ts b/src/mol-util/color/color.ts index 426c639b538118909cfc392688197f6db691020a..9fd800b4b0a78ee9a32606efb172666ed4e326ca 100644 --- a/src/mol-util/color/color.ts +++ b/src/mol-util/color/color.ts @@ -26,26 +26,36 @@ export namespace Color { return [ (hexColor >> 16 & 255) / 255, (hexColor >> 8 & 255) / 255, (hexColor & 255) / 255 ] } - export function fromRgb(r: number, g: number, b: number ): Color { + export function fromRgb(r: number, g: number, b: number): Color { return ((r << 16) | (g << 8) | b) as Color } - export function fromNormalizedRgb(r: number, g: number, b: number ): Color { + export function fromNormalizedRgb(r: number, g: number, b: number): Color { return (((r * 255) << 16) | ((g * 255) << 8) | (b * 255)) as Color } + export function fromArray(array: Helpers.NumberArray, offset: number): Color { + return fromRgb(array[offset], array[offset + 1], array[offset + 2]) + } + + export function fromNormalizedArray(array: Helpers.NumberArray, offset: number): Color { + return fromNormalizedRgb(array[offset], array[offset + 1], array[offset + 2]) + } + /** Copies hex color to rgb array */ export function toArray(hexColor: Color, array: Helpers.NumberArray, offset: number) { array[ offset ] = (hexColor >> 16 & 255) array[ offset + 1 ] = (hexColor >> 8 & 255) array[ offset + 2 ] = (hexColor & 255) + return array } /** Copies normalized (0 to 1) hex color to rgb array */ - export function toArrayNormalized(hexColor: Color, array: Helpers.NumberArray, offset: number) { + export function toArrayNormalized<T extends Helpers.NumberArray>(hexColor: Color, array: T, offset: number) { array[ offset ] = (hexColor >> 16 & 255) / 255 array[ offset + 1 ] = (hexColor >> 8 & 255) / 255 array[ offset + 2 ] = (hexColor & 255) / 255 + return array } /** Linear interpolation between two colors */ diff --git a/src/mol-util/color/scale.ts b/src/mol-util/color/scale.ts index c7cf7980c114e7b5363ede0cc7b80a1b176c0f5e..7ed018496fcfdd4666f29f77325fe9f20fa92047 100644 --- a/src/mol-util/color/scale.ts +++ b/src/mol-util/color/scale.ts @@ -5,10 +5,32 @@ */ import { Color } from './color' -import { ColorBrewer } from './tables' +import { ColorBrewer, ColorMatplotlib, ColorOther } from './tables' import { ScaleLegend } from 'mol-theme/color'; import { defaults } from 'mol-util'; +export type ColorListName = ( + keyof typeof ColorBrewer | keyof typeof ColorMatplotlib | keyof typeof ColorOther +) +export const ColorListNames = [ + ...Object.keys(ColorBrewer), ...Object.keys(ColorMatplotlib), ...Object.keys(ColorOther) +] +export const ColorListOptions = ColorListNames.map(n => [n, n] as [ColorListName, string]) + +export function getColorListFromName(name: ColorListName) { + if (name in ColorBrewer) { + return ColorBrewer[name as keyof typeof ColorBrewer] + } else if (name in ColorMatplotlib) { + return ColorMatplotlib[name as keyof typeof ColorMatplotlib] + } else if (name in ColorOther) { + return ColorOther[name as keyof typeof ColorOther] + } + console.warn(`unknown color list named '${name}'`) + return ColorBrewer.RdYlBu +} + +// + export interface ColorScale { /** Returns hex color for given value */ color: (value: number) => Color @@ -22,20 +44,20 @@ export interface ColorScale { readonly legend: ScaleLegend } -export const DefaultColorScale = { +export const DefaultColorScaleProps = { domain: [0, 1], reverse: false, - list: ColorBrewer.RdYlBu, + listOrName: ColorBrewer.RdYlBu as Color[] | ColorListName, minLabel: '' as string | undefined, maxLabel: '' as string | undefined, } -export type ColorScaleProps = Partial<typeof DefaultColorScale> +export type ColorScaleProps = Partial<typeof DefaultColorScaleProps> export namespace ColorScale { export function create(props: ColorScaleProps): ColorScale { - // ensure that no undefined .list property exists so that the default assignment works - if (props.list === undefined) delete props.list - const { domain, reverse, list } = { ...DefaultColorScale, ...props } + const { domain, reverse, listOrName } = { ...DefaultColorScaleProps, ...props } + const list = typeof listOrName === 'string' ? getColorListFromName(listOrName) : listOrName + const colors = reverse ? list.slice().reverse() : list const count1 = colors.length - 1 diff --git a/src/mol-util/object.ts b/src/mol-util/object.ts index 2ee047d59ee226390d30cf68beb09f6f21b7df22..28405be92bf9360173e2ef79375dbab29530f7b9 100644 --- a/src/mol-util/object.ts +++ b/src/mol-util/object.ts @@ -2,6 +2,7 @@ * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ const hasOwnProperty = Object.prototype.hasOwnProperty; @@ -54,4 +55,27 @@ export function shallowMerge<T>(source: T, ...rest: (Partial<T> | undefined)[]): } } return ret; -} \ No newline at end of file +} + +/** Simple deep clone for number, boolean, string, null, undefined, object, array */ +export function deepClone<T>(source: T): T { + if (null === source || "object" !== typeof source) return source; + + if (source instanceof Array) { + const copy: any[] = []; + for (let i = 0, len = source.length; i < len; i++) { + copy[i] = deepClone(source[i]); + } + return copy as any as T; + } + + if (source instanceof Object) { + const copy: { [k: string]: any } = {}; + for (let k in source) { + if (hasOwnProperty.call(source, k)) copy[k] = deepClone(source[k]); + } + return copy as any as T; + } + + throw new Error(`Can't clone, type "${typeof source}" unsupported`); + } \ No newline at end of file diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index 7d3d7cdb5ca7ea0fff33824dd2ad93bf81aa465b..dc7b6811161cbe369cb74dde2e49b29988e0b9a8 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -6,6 +6,7 @@ */ import { Color as ColorData } from './color'; +import { shallowClone } from 'mol-util'; export namespace ParamDefinition { export interface Base<T> { @@ -82,13 +83,25 @@ export namespace ParamDefinition { return { type: 'number', label, description, defaultValue, min, max, step } } - export type Any = /* ValueParam<any> | */ Select<any> | MultiSelect<any> | Boolean | Range | Text | Color | Numeric + export interface Interval extends Base<[number, number]> { + type: 'interval' + } + export function Interval(label: string, description: string, defaultValue: [number, number]): Interval { + return { type: 'interval', label, description, defaultValue } + } + + export type Any = /* GenericValue<any> | */ Select<any> | MultiSelect<any> | Boolean | Range | Text | Color | Numeric | Interval export type Params = { [k: string]: Any } + export type DefaultValues<T extends Params> = { [k in keyof T]: T[k]['defaultValue'] } export function getDefaultValues<T extends Params>(params: T) { const d: { [k: string]: any } = {} Object.keys(params).forEach(k => d[k] = params[k].defaultValue) - return d as { [k in keyof T]: T[k]['defaultValue'] } + return d as DefaultValues<T> + } + + export function clone<P extends Params>(params: P): P { + return shallowClone(params) } /** diff --git a/webpack.config.js b/webpack.config.js index b1aba103a11afae350c6929a901a92b15f0a37a7..0f85d141c04135fc627cc8855fcab338f179ef88 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ const path = require('path'); const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin'); -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +// const CircularDependencyPlugin = require('circular-dependency-plugin'); module.exports = { module: { rules: [ @@ -30,6 +31,11 @@ module.exports = { ] }, plugins: [ + // new CircularDependencyPlugin({ + // include: [ path.resolve(__dirname, 'build/node_modules/') ], + // failOnError: false, + // cwd: process.cwd(), + // }), new ExtraWatchWebpackPlugin({ files: [ './build/node_modules/**/*.vert',