diff --git a/src/mol-app/ui/transform/file-loader.tsx b/src/mol-app/ui/transform/file-loader.tsx index c486e4a12de88d56191c7a21a8089a993f559d2c..02f76c55c4db309e79f59d9b083f0ed6b013283e 100644 --- a/src/mol-app/ui/transform/file-loader.tsx +++ b/src/mol-app/ui/transform/file-loader.tsx @@ -11,10 +11,10 @@ import { TransformListController } from '../../controller/transform/list'; import { FileEntity } from 'mol-view/state/entity'; import { MmcifFileToModel, ModelToStructure, StructureToBallAndStick, StructureToSpacefill, StructureToDistanceRestraint, StructureToBackbone } from 'mol-view/state/transform'; import { StateContext } from 'mol-view/state/context'; -import { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; -import { BallAndStickProps } from 'mol-geo/representation/structure/ball-and-stick'; -import { DistanceRestraintProps } from 'mol-geo/representation/structure/distance-restraint'; -import { BackboneProps } from 'mol-geo/representation/structure/backbone'; +import { SpacefillProps } from 'mol-geo/representation/structure/representation/spacefill'; +import { BallAndStickProps } from 'mol-geo/representation/structure/representation/ball-and-stick'; +import { DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; +import { BackboneProps } from 'mol-geo/representation/structure/representation/backbone'; const spacefillProps: SpacefillProps = { doubleSided: true, diff --git a/src/mol-app/ui/transform/url-loader.tsx b/src/mol-app/ui/transform/url-loader.tsx index e97eff11f34c68579202f962cca790180349502f..ed0b8ef8b2f5904d82f86ac65a473132436fa06b 100644 --- a/src/mol-app/ui/transform/url-loader.tsx +++ b/src/mol-app/ui/transform/url-loader.tsx @@ -10,11 +10,11 @@ import { TransformListController } from '../../controller/transform/list'; import { UrlEntity } from 'mol-view/state/entity'; import { ModelToStructure, StructureToBallAndStick, StructureToSpacefill, StructureToDistanceRestraint, StructureToBackbone, MmcifUrlToModel, StructureToCartoon } from 'mol-view/state/transform'; import { StateContext } from 'mol-view/state/context'; -import { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; -import { BallAndStickProps } from 'mol-geo/representation/structure/ball-and-stick'; -import { DistanceRestraintProps } from 'mol-geo/representation/structure/distance-restraint'; -import { BackboneProps } from 'mol-geo/representation/structure/backbone'; -import { CartoonProps } from 'mol-geo/representation/structure/cartoon'; +import { SpacefillProps } from 'mol-geo/representation/structure/representation/spacefill'; +import { BallAndStickProps } from 'mol-geo/representation/structure/representation/ball-and-stick'; +import { DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; +import { BackboneProps } from 'mol-geo/representation/structure/representation/backbone'; +import { CartoonProps } from 'mol-geo/representation/structure/representation/cartoon'; const spacefillProps: SpacefillProps = { doubleSided: true, diff --git a/src/mol-geo/representation/structure/complex-representation.ts b/src/mol-geo/representation/structure/complex-representation.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ddb9d8cb336f5fd3193547452185127fbe37943 --- /dev/null +++ b/src/mol-geo/representation/structure/complex-representation.ts @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Structure } from 'mol-model/structure'; +import { Task } from 'mol-task' +import { PickingId } from '../../util/picking'; +import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci'; +import { MarkerAction } from '../../util/marker-data'; +import { getQualityProps } from '../util'; +import { StructureProps, DefaultStructureProps, StructureRepresentation } from '.'; +import { ComplexVisual } from './complex-visual'; + +export function ComplexRepresentation<P extends StructureProps>(visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> { + let visual: ComplexVisual<P> + + let _props: Required<P> + let _structure: Structure + + function create(structure: Structure, props: P = {} as P) { + _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, structure)) + _props.colorTheme!.structure = structure + + return Task.create('Creating StructureRepresentation', async ctx => { + if (!_structure) { + visual = visualCtor() + await visual.create(ctx, structure, _props) + } else { + if (_structure.hashCode === structure.hashCode) { + await update(_props) + } else { + if (!await visual.update(ctx, _props)) { + await visual.create(ctx, _structure, _props) + } + } + } + _structure = structure + }); + } + + function update(props: P) { + return Task.create('Updating StructureRepresentation', async ctx => { + _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, _structure)) + _props.colorTheme!.structure = _structure + + if (!await visual.update(ctx, _props)) { + await visual.create(ctx, _structure, _props) + } + }) + } + + function getLoci(pickingId: PickingId) { + let loci: Loci = EmptyLoci + const _loci = visual.getLoci(pickingId) + if (!isEmptyLoci(_loci)) loci = _loci + return loci + } + + function mark(loci: Loci, action: MarkerAction) { + visual.mark(loci, action) + } + + function destroy() { + visual.destroy() + } + + return { + get renderObjects() { return [ visual.renderObject ] }, + get props() { return _props }, + create, + update, + getLoci, + mark, + destroy + } +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/complex-visual.ts b/src/mol-geo/representation/structure/complex-visual.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c193d515d9bcceb17642d0ae5939c58183d407f --- /dev/null +++ b/src/mol-geo/representation/structure/complex-visual.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Structure } from 'mol-model/structure'; +import { RepresentationProps, Visual } from '..'; + +export interface ComplexVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index 7b95c2c54e9f71eb35e7fc66fe9d3a6868c9cce0..2b0b0da9cb03674cc8b3262793970a4e72a98bb6 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -5,18 +5,10 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Structure, Unit } from 'mol-model/structure'; -import { Task } from 'mol-task' -import { RenderObject } from 'mol-gl/render-object'; -import { Representation, RepresentationProps, Visual } from '..'; +import { Structure } from 'mol-model/structure'; +import { Representation, RepresentationProps } from '..'; import { ColorTheme, SizeTheme } from '../../theme'; -import { PickingId } from '../../util/picking'; -import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci'; -import { MarkerAction } from '../../util/marker-data'; -import { getQualityProps, DefaultBaseProps } from '../util'; - -export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<Unit.SymmetryGroup, P> { } -export interface StructureVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { } +import { DefaultBaseProps } from '../util'; export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { } @@ -27,174 +19,7 @@ export const DefaultStructureProps = { } export type StructureProps = Partial<typeof DefaultStructureProps> -export function StructureRepresentation<P extends StructureProps>(visualCtor: () => StructureVisual<P>): StructureRepresentation<P> { - let visual: StructureVisual<P> - - let _props: Required<P> - let _structure: Structure - - function create(structure: Structure, props: P = {} as P) { - _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, structure)) - _props.colorTheme!.structure = structure - - return Task.create('Creating StructureRepresentation', async ctx => { - if (!_structure) { - visual = visualCtor() - await visual.create(ctx, structure, _props) - } else { - if (_structure.hashCode === structure.hashCode) { - await update(_props) - } else { - if (!await visual.update(ctx, _props)) { - await visual.create(ctx, _structure, _props) - } - } - } - _structure = structure - }); - } - - function update(props: P) { - return Task.create('Updating StructureRepresentation', async ctx => { - _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, _structure)) - _props.colorTheme!.structure = _structure - - if (!await visual.update(ctx, _props)) { - await visual.create(ctx, _structure, _props) - } - }) - } - - function getLoci(pickingId: PickingId) { - let loci: Loci = EmptyLoci - const _loci = visual.getLoci(pickingId) - if (!isEmptyLoci(_loci)) loci = _loci - return loci - } - - function mark(loci: Loci, action: MarkerAction) { - visual.mark(loci, action) - } - - function destroy() { - visual.destroy() - } - - return { - get renderObjects() { return [ visual.renderObject ] }, - get props() { return _props }, - create, - update, - getLoci, - mark, - destroy - } -} - -export function StructureUnitsRepresentation<P extends StructureProps>(visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> { - let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>() - - let _props: Required<P> - let _structure: Structure - let _groups: ReadonlyArray<Unit.SymmetryGroup> - - function create(structure: Structure, props: P = {} as P) { - _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, structure)) - _props.colorTheme!.structure = structure - - return Task.create('Creating StructureRepresentation', async ctx => { - if (!_structure) { - _groups = structure.unitSymmetryGroups; - for (let i = 0; i < _groups.length; i++) { - const group = _groups[i]; - const visual = visualCtor() - await visual.create(ctx, group, _props) - visuals.set(group.hashCode, { visual, group }) - } - } else { - if (_structure.hashCode === structure.hashCode) { - await update(_props) - } else { - _groups = structure.unitSymmetryGroups; - const newGroups: Unit.SymmetryGroup[] = [] - const oldUnitsVisuals = visuals - visuals = new Map() - for (let i = 0; i < _groups.length; i++) { - const group = _groups[i]; - const visualGroup = oldUnitsVisuals.get(group.hashCode) - if (visualGroup) { - const { visual, group } = visualGroup - if (!await visual.update(ctx, _props)) { - await visual.create(ctx, group, _props) - } - oldUnitsVisuals.delete(group.hashCode) - } else { - newGroups.push(group) - const visual = visualCtor() - await visual.create(ctx, group, _props) - visuals.set(group.hashCode, { visual, group }) - } - } - - // for new groups, reuse leftover visuals - const unusedVisuals: UnitsVisual<P>[] = [] - oldUnitsVisuals.forEach(({ visual }) => unusedVisuals.push(visual)) - newGroups.forEach(async group => { - const visual = unusedVisuals.pop() || visualCtor() - await visual.create(ctx, group, _props) - visuals.set(group.hashCode, { visual, group }) - }) - unusedVisuals.forEach(visual => visual.destroy()) - } - } - _structure = structure - }); - } - - function update(props: P) { - return Task.create('Updating StructureRepresentation', async ctx => { - _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, _structure)) - _props.colorTheme!.structure = _structure - - visuals.forEach(async ({ visual, group }) => { - if (!await visual.update(ctx, _props)) { - await visual.create(ctx, group, _props) - } - }) - }) - } - - function getLoci(pickingId: PickingId) { - let loci: Loci = EmptyLoci - visuals.forEach(({ visual }) => { - const _loci = visual.getLoci(pickingId) - if (!isEmptyLoci(_loci)) loci = _loci - }) - return loci - } - - function mark(loci: Loci, action: MarkerAction) { - visuals.forEach(({ visual }) => visual.mark(loci, action)) - } - - function destroy() { - visuals.forEach(({ visual }) => visual.destroy()) - visuals.clear() - } - - return { - get renderObjects() { - const renderObjects: RenderObject[] = [] - visuals.forEach(({ visual }) => renderObjects.push(visual.renderObject)) - return renderObjects - }, - get props() { - return _props - }, - create, - update, - getLoci, - mark, - destroy - } -} \ No newline at end of file +export { ComplexRepresentation } from './complex-representation' +export { UnitsRepresentation } from './units-representation' +export { ComplexVisual } from './complex-visual' +export { UnitsVisual } from './units-visual' \ No newline at end of file diff --git a/src/mol-geo/representation/structure/backbone.ts b/src/mol-geo/representation/structure/representation/backbone.ts similarity index 84% rename from src/mol-geo/representation/structure/backbone.ts rename to src/mol-geo/representation/structure/representation/backbone.ts index 9fec379019ec0b853306079119f5d1a32c4bd96b..4930d97755a48854a96af2078d9e5c6d8f58e690 100644 --- a/src/mol-geo/representation/structure/backbone.ts +++ b/src/mol-geo/representation/structure/representation/backbone.ts @@ -4,13 +4,13 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { StructureRepresentation, StructureUnitsRepresentation } from '.'; -import { PickingId } from '../../util/picking'; +import { StructureRepresentation, UnitsRepresentation } from '..'; +import { PickingId } from '../../../util/picking'; import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci } from 'mol-model/loci'; -import { MarkerAction } from '../../util/marker-data'; -import { PolymerBackboneVisual, DefaultPolymerBackboneProps } from './visual/polymer-backbone-cylinder'; +import { MarkerAction } from '../../../util/marker-data'; +import { PolymerBackboneVisual, DefaultPolymerBackboneProps } from '../visual/polymer-backbone-cylinder'; export const DefaultBackboneProps = { ...DefaultPolymerBackboneProps @@ -18,7 +18,7 @@ export const DefaultBackboneProps = { export type BackboneProps = Partial<typeof DefaultBackboneProps> export function BackboneRepresentation(): StructureRepresentation<BackboneProps> { - const traceRepr = StructureUnitsRepresentation(PolymerBackboneVisual) + const traceRepr = UnitsRepresentation(PolymerBackboneVisual) return { get renderObjects() { diff --git a/src/mol-geo/representation/structure/ball-and-stick.ts b/src/mol-geo/representation/structure/representation/ball-and-stick.ts similarity index 83% rename from src/mol-geo/representation/structure/ball-and-stick.ts rename to src/mol-geo/representation/structure/representation/ball-and-stick.ts index 2883582a5081172bcde9672c71a6c111af4eb1ec..094eb1fcbffea4e0351ddc26dfd30fa3bbb3c1d7 100644 --- a/src/mol-geo/representation/structure/ball-and-stick.ts +++ b/src/mol-geo/representation/structure/representation/ball-and-stick.ts @@ -4,16 +4,16 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { StructureRepresentation, StructureUnitsRepresentation } from '.'; -import { ElementSphereVisual, DefaultElementSphereProps } from './visual/element-sphere'; -import { IntraUnitLinkVisual, DefaultIntraUnitLinkProps } from './visual/intra-unit-link-cylinder'; -import { PickingId } from '../../util/picking'; +import { ComplexRepresentation, StructureRepresentation, UnitsRepresentation } from '..'; +import { ElementSphereVisual, DefaultElementSphereProps } from '../visual/element-sphere'; +import { IntraUnitLinkVisual, DefaultIntraUnitLinkProps } from '../visual/intra-unit-link-cylinder'; +import { PickingId } from '../../../util/picking'; import { Structure, Unit } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci, isEmptyLoci } from 'mol-model/loci'; -import { MarkerAction } from '../../util/marker-data'; -import { SizeTheme } from '../../theme'; -import { InterUnitLinkVisual } from './visual/inter-unit-link-cylinder'; +import { MarkerAction } from '../../../util/marker-data'; +import { SizeTheme } from '../../../theme'; +import { InterUnitLinkVisual } from '../visual/inter-unit-link-cylinder'; export const DefaultBallAndStickProps = { ...DefaultElementSphereProps, @@ -25,9 +25,9 @@ export const DefaultBallAndStickProps = { export type BallAndStickProps = Partial<typeof DefaultBallAndStickProps> export function BallAndStickRepresentation(): StructureRepresentation<BallAndStickProps> { - const elmementRepr = StructureUnitsRepresentation(ElementSphereVisual) - const intraLinkRepr = StructureUnitsRepresentation(IntraUnitLinkVisual) - const interLinkRepr = StructureRepresentation(InterUnitLinkVisual) + const elmementRepr = UnitsRepresentation(ElementSphereVisual) + const intraLinkRepr = UnitsRepresentation(IntraUnitLinkVisual) + const interLinkRepr = ComplexRepresentation(InterUnitLinkVisual) return { get renderObjects() { diff --git a/src/mol-geo/representation/structure/carbohydrate.ts b/src/mol-geo/representation/structure/representation/carbohydrate.ts similarity index 84% rename from src/mol-geo/representation/structure/carbohydrate.ts rename to src/mol-geo/representation/structure/representation/carbohydrate.ts index a78c780b35efd802e8cc4911f0937ed1df134f87..75e63d9300e5c74323db481064f3387c277ec38b 100644 --- a/src/mol-geo/representation/structure/carbohydrate.ts +++ b/src/mol-geo/representation/structure/representation/carbohydrate.ts @@ -4,14 +4,14 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { StructureRepresentation } from '.'; -import { PickingId } from '../../util/picking'; +import { ComplexRepresentation, StructureRepresentation } from '..'; +import { PickingId } from '../../../util/picking'; import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci, isEmptyLoci } from 'mol-model/loci'; -import { MarkerAction } from '../../util/marker-data'; -import { CarbohydrateSymbolVisual, DefaultCarbohydrateSymbolProps } from './visual/carbohydrate-symbol-mesh'; -import { CarbohydrateLinkVisual, DefaultCarbohydrateLinkProps } from './visual/carbohydrate-link-cylinder'; +import { MarkerAction } from '../../../util/marker-data'; +import { CarbohydrateSymbolVisual, DefaultCarbohydrateSymbolProps } from '../visual/carbohydrate-symbol-mesh'; +import { CarbohydrateLinkVisual, DefaultCarbohydrateLinkProps } from '../visual/carbohydrate-link-cylinder'; export const DefaultCartoonProps = { ...DefaultCarbohydrateSymbolProps, @@ -20,8 +20,8 @@ export const DefaultCartoonProps = { export type CarbohydrateProps = Partial<typeof DefaultCartoonProps> export function CarbohydrateRepresentation(): StructureRepresentation<CarbohydrateProps> { - const carbohydrateSymbolRepr = StructureRepresentation(CarbohydrateSymbolVisual) - const carbohydrateLinkRepr = StructureRepresentation(CarbohydrateLinkVisual) + const carbohydrateSymbolRepr = ComplexRepresentation(CarbohydrateSymbolVisual) + const carbohydrateLinkRepr = ComplexRepresentation(CarbohydrateLinkVisual) return { get renderObjects() { diff --git a/src/mol-geo/representation/structure/cartoon.ts b/src/mol-geo/representation/structure/representation/cartoon.ts similarity index 57% rename from src/mol-geo/representation/structure/cartoon.ts rename to src/mol-geo/representation/structure/representation/cartoon.ts index c374e27a885843fd44e060f917ca53631a09e13c..498c0f30b0a7a7cb7b7af5294b382354243f5629 100644 --- a/src/mol-geo/representation/structure/cartoon.ts +++ b/src/mol-geo/representation/structure/representation/cartoon.ts @@ -4,18 +4,16 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { StructureRepresentation, StructureUnitsRepresentation } from '.'; -import { PickingId } from '../../util/picking'; +import { StructureRepresentation, UnitsRepresentation } from '..'; +import { PickingId } from '../../../util/picking'; import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci, isEmptyLoci } from 'mol-model/loci'; -import { MarkerAction } from '../../util/marker-data'; -import { PolymerTraceVisual, DefaultPolymerTraceProps } from './visual/polymer-trace-mesh'; -import { PolymerGapVisual, DefaultPolymerGapProps } from './visual/polymer-gap-cylinder'; -import { NucleotideBlockVisual, DefaultNucleotideBlockProps } from './visual/nucleotide-block-mesh'; -import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from './visual/polymer-direction-wedge'; -import { CarbohydrateSymbolVisual } from './visual/carbohydrate-symbol-mesh'; -import { CarbohydrateLinkVisual } from './visual/carbohydrate-link-cylinder'; +import { MarkerAction } from '../../../util/marker-data'; +import { PolymerTraceVisual, DefaultPolymerTraceProps } from '../visual/polymer-trace-mesh'; +import { PolymerGapVisual, DefaultPolymerGapProps } from '../visual/polymer-gap-cylinder'; +import { NucleotideBlockVisual, DefaultNucleotideBlockProps } from '../visual/nucleotide-block-mesh'; +import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from '../visual/polymer-direction-wedge'; export const DefaultCartoonProps = { ...DefaultPolymerTraceProps, @@ -26,24 +24,18 @@ export const DefaultCartoonProps = { export type CartoonProps = Partial<typeof DefaultCartoonProps> export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { - const traceRepr = StructureUnitsRepresentation(PolymerTraceVisual) - const gapRepr = StructureUnitsRepresentation(PolymerGapVisual) - const blockRepr = StructureUnitsRepresentation(NucleotideBlockVisual) - const directionRepr = StructureUnitsRepresentation(PolymerDirectionVisual) - - // TODO move to own repr - const carbohydrateSymbolRepr = StructureRepresentation(CarbohydrateSymbolVisual) - const carbohydrateLinkRepr = StructureRepresentation(CarbohydrateLinkVisual) + const traceRepr = UnitsRepresentation(PolymerTraceVisual) + const gapRepr = UnitsRepresentation(PolymerGapVisual) + const blockRepr = UnitsRepresentation(NucleotideBlockVisual) + const directionRepr = UnitsRepresentation(PolymerDirectionVisual) return { get renderObjects() { return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects, - ...blockRepr.renderObjects, ...directionRepr.renderObjects, - ...carbohydrateSymbolRepr.renderObjects, ...carbohydrateLinkRepr.renderObjects ] + ...blockRepr.renderObjects, ...directionRepr.renderObjects ] }, get props() { - return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props, - ...carbohydrateSymbolRepr.props, ...carbohydrateLinkRepr.props } + return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props } }, create: (structure: Structure, props: CartoonProps = {} as CartoonProps) => { const p = Object.assign({}, DefaultCartoonProps, props) @@ -52,8 +44,6 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { await gapRepr.create(structure, p).runInContext(ctx) await blockRepr.create(structure, p).runInContext(ctx) await directionRepr.create(structure, p).runInContext(ctx) - await carbohydrateSymbolRepr.create(structure, p).runInContext(ctx) - await carbohydrateLinkRepr.create(structure, p).runInContext(ctx) }) }, update: (props: CartoonProps) => { @@ -63,8 +53,6 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { await gapRepr.update(p).runInContext(ctx) await blockRepr.update(p).runInContext(ctx) await directionRepr.update(p).runInContext(ctx) - await carbohydrateSymbolRepr.update(p).runInContext(ctx) - await carbohydrateLinkRepr.update(p).runInContext(ctx) }) }, getLoci: (pickingId: PickingId) => { @@ -72,30 +60,22 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { const gapLoci = gapRepr.getLoci(pickingId) const blockLoci = blockRepr.getLoci(pickingId) const directionLoci = directionRepr.getLoci(pickingId) - const carbohydrateSymbolLoci = carbohydrateSymbolRepr.getLoci(pickingId) - const carbohydrateLinkLoci = carbohydrateLinkRepr.getLoci(pickingId) return !isEmptyLoci(traceLoci) ? traceLoci : !isEmptyLoci(gapLoci) ? gapLoci : !isEmptyLoci(blockLoci) ? blockLoci - : !isEmptyLoci(directionLoci) ? directionLoci - : !isEmptyLoci(carbohydrateSymbolLoci) ? carbohydrateSymbolLoci - : carbohydrateLinkLoci + : directionLoci }, mark: (loci: Loci, action: MarkerAction) => { traceRepr.mark(loci, action) gapRepr.mark(loci, action) blockRepr.mark(loci, action) directionRepr.mark(loci, action) - carbohydrateSymbolRepr.mark(loci, action) - carbohydrateLinkRepr.mark(loci, action) }, destroy() { traceRepr.destroy() gapRepr.destroy() blockRepr.destroy() directionRepr.destroy() - carbohydrateSymbolRepr.destroy() - carbohydrateLinkRepr.destroy() } } } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/distance-restraint.ts b/src/mol-geo/representation/structure/representation/distance-restraint.ts similarity index 83% rename from src/mol-geo/representation/structure/distance-restraint.ts rename to src/mol-geo/representation/structure/representation/distance-restraint.ts index 9cb1dd01c6c17bfed21429df49e0c74f4cf70f87..68d2d994c35413abc78987c9ef759005f4a6ebe0 100644 --- a/src/mol-geo/representation/structure/distance-restraint.ts +++ b/src/mol-geo/representation/structure/representation/distance-restraint.ts @@ -4,14 +4,14 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { StructureRepresentation } from '.'; -import { PickingId } from '../../util/picking'; +import { ComplexRepresentation, StructureRepresentation } from '..'; +import { PickingId } from '../../../util/picking'; import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci } from 'mol-model/loci'; -import { MarkerAction } from '../../util/marker-data'; -import { SizeTheme } from '../../theme'; -import { CrossLinkRestraintVisual, DefaultCrossLinkRestraintProps } from './visual/cross-link-restraint-cylinder'; +import { MarkerAction } from '../../../util/marker-data'; +import { SizeTheme } from '../../../theme'; +import { CrossLinkRestraintVisual, DefaultCrossLinkRestraintProps } from '../visual/cross-link-restraint-cylinder'; export const DefaultDistanceRestraintProps = { ...DefaultCrossLinkRestraintProps, @@ -21,7 +21,7 @@ export const DefaultDistanceRestraintProps = { export type DistanceRestraintProps = Partial<typeof DefaultDistanceRestraintProps> export function DistanceRestraintRepresentation(): StructureRepresentation<DistanceRestraintProps> { - const crossLinkRepr = StructureRepresentation(CrossLinkRestraintVisual) + const crossLinkRepr = ComplexRepresentation(CrossLinkRestraintVisual) return { get renderObjects() { diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/representation/spacefill.ts similarity index 75% rename from src/mol-geo/representation/structure/spacefill.ts rename to src/mol-geo/representation/structure/representation/spacefill.ts index 37ab7eba9d60c3d8167f714487c3bca3d2c72c7a..0fab7f5a9446015f6b1e00c17fd6a3e15b62536f 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/representation/spacefill.ts @@ -4,8 +4,8 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { StructureUnitsRepresentation } from '.'; -import { ElementSphereVisual, DefaultElementSphereProps } from './visual/element-sphere'; +import { UnitsRepresentation } from '..'; +import { ElementSphereVisual, DefaultElementSphereProps } from '../visual/element-sphere'; export const DefaultSpacefillProps = { ...DefaultElementSphereProps, @@ -13,5 +13,5 @@ export const DefaultSpacefillProps = { export type SpacefillProps = Partial<typeof DefaultSpacefillProps> export function SpacefillRepresentation() { - return StructureUnitsRepresentation(ElementSphereVisual) + return UnitsRepresentation(ElementSphereVisual) } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/units-representation.ts b/src/mol-geo/representation/structure/units-representation.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb9a9efdb7bda4db8336b079887f29daab006918 --- /dev/null +++ b/src/mol-geo/representation/structure/units-representation.ts @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Structure, Unit } from 'mol-model/structure'; +import { Task } from 'mol-task' +import { RenderObject } from 'mol-gl/render-object'; +import { Representation, RepresentationProps, Visual } from '..'; +import { PickingId } from '../../util/picking'; +import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci'; +import { MarkerAction } from '../../util/marker-data'; +import { getQualityProps } from '../util'; +import { DefaultStructureProps, StructureProps } from '.'; + +export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<Unit.SymmetryGroup, P> { } +export interface StructureVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { } + +export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { } + +export function UnitsRepresentation<P extends StructureProps>(visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> { + let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>() + + let _props: Required<P> + let _structure: Structure + let _groups: ReadonlyArray<Unit.SymmetryGroup> + + function create(structure: Structure, props: P = {} as P) { + _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, structure)) + _props.colorTheme!.structure = structure + + return Task.create('Creating StructureRepresentation', async ctx => { + if (!_structure) { + _groups = structure.unitSymmetryGroups; + for (let i = 0; i < _groups.length; i++) { + const group = _groups[i]; + const visual = visualCtor() + await visual.create(ctx, group, _props) + visuals.set(group.hashCode, { visual, group }) + } + } else { + if (_structure.hashCode === structure.hashCode) { + await update(_props) + } else { + _groups = structure.unitSymmetryGroups; + const newGroups: Unit.SymmetryGroup[] = [] + const oldUnitsVisuals = visuals + visuals = new Map() + for (let i = 0; i < _groups.length; i++) { + const group = _groups[i]; + const visualGroup = oldUnitsVisuals.get(group.hashCode) + if (visualGroup) { + const { visual, group } = visualGroup + if (!await visual.update(ctx, _props)) { + await visual.create(ctx, group, _props) + } + oldUnitsVisuals.delete(group.hashCode) + } else { + newGroups.push(group) + const visual = visualCtor() + await visual.create(ctx, group, _props) + visuals.set(group.hashCode, { visual, group }) + } + } + + // for new groups, reuse leftover visuals + const unusedVisuals: UnitsVisual<P>[] = [] + oldUnitsVisuals.forEach(({ visual }) => unusedVisuals.push(visual)) + newGroups.forEach(async group => { + const visual = unusedVisuals.pop() || visualCtor() + await visual.create(ctx, group, _props) + visuals.set(group.hashCode, { visual, group }) + }) + unusedVisuals.forEach(visual => visual.destroy()) + } + } + _structure = structure + }); + } + + function update(props: P) { + return Task.create('Updating StructureRepresentation', async ctx => { + _props = Object.assign({}, DefaultStructureProps, _props, props, getQualityProps(props, _structure)) + _props.colorTheme!.structure = _structure + + visuals.forEach(async ({ visual, group }) => { + if (!await visual.update(ctx, _props)) { + await visual.create(ctx, group, _props) + } + }) + }) + } + + function getLoci(pickingId: PickingId) { + let loci: Loci = EmptyLoci + visuals.forEach(({ visual }) => { + const _loci = visual.getLoci(pickingId) + if (!isEmptyLoci(_loci)) loci = _loci + }) + return loci + } + + function mark(loci: Loci, action: MarkerAction) { + visuals.forEach(({ visual }) => visual.mark(loci, action)) + } + + function destroy() { + visuals.forEach(({ visual }) => visual.destroy()) + visuals.clear() + } + + return { + get renderObjects() { + const renderObjects: RenderObject[] = [] + visuals.forEach(({ visual }) => renderObjects.push(visual.renderObject)) + return renderObjects + }, + get props() { + return _props + }, + create, + update, + getLoci, + mark, + destroy + } +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts new file mode 100644 index 0000000000000000000000000000000000000000..540aa7feca4fbc43fb094c86b6fcfbf7011214b9 --- /dev/null +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { RepresentationProps, Visual } from '..'; +import { Unit } from 'mol-model/structure'; + +export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<Unit.SymmetryGroup, P> { } diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts index 1d449f218c0271843443e7fa7f005c9e1a798e24..1792cf5865572d1055ad417237912ccfda50026a 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts @@ -8,7 +8,7 @@ import { ValueCell } from 'mol-util/value-cell' import { MeshRenderObject } from 'mol-gl/render-object' import { Unit, Structure, Link, StructureElement } from 'mol-model/structure'; -import { DefaultStructureProps, StructureVisual } from '..'; +import { DefaultStructureProps, ComplexVisual } from '..'; import { RuntimeContext } from 'mol-task' import { createColors, createStructureMeshRenderObject } from './util/common'; import { Mesh } from '../../../shape/mesh'; @@ -63,7 +63,7 @@ export const DefaultCarbohydrateLinkProps = { } export type CarbohydrateLinkProps = Partial<typeof DefaultCarbohydrateLinkProps> -export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> { +export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkProps> { let renderObject: MeshRenderObject let currentProps: typeof DefaultCarbohydrateLinkProps let mesh: Mesh diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts index 327de3198ad30db947a50228afed17bca7d789e3..f84782fff3841d9360bed94c0d714d96677ce352 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts @@ -8,7 +8,7 @@ import { ValueCell } from 'mol-util/value-cell' import { MeshRenderObject } from 'mol-gl/render-object' import { Unit, Structure, StructureElement } from 'mol-model/structure'; -import { DefaultStructureProps, StructureVisual } from '..'; +import { DefaultStructureProps, ComplexVisual } from '..'; import { RuntimeContext } from 'mol-task' import { createColors, createStructureMeshRenderObject } from './util/common'; import { Mesh } from '../../../shape/mesh'; @@ -126,7 +126,7 @@ export const DefaultCarbohydrateSymbolProps = { } export type CarbohydrateSymbolProps = Partial<typeof DefaultCarbohydrateSymbolProps> -export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolProps> { +export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolProps> { let renderObject: MeshRenderObject let currentProps: typeof DefaultCarbohydrateSymbolProps let mesh: Mesh diff --git a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts index ea48347d29c81fb83e3d897f7396e9f6e6b120a2..7a2435546a29c2c5b82f9b6fd034152352fc461c 100644 --- a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts @@ -8,7 +8,7 @@ import { ValueCell } from 'mol-util/value-cell' import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object' import { Link, Structure, StructureElement } from 'mol-model/structure'; -import { DefaultStructureProps, StructureVisual } from '..'; +import { DefaultStructureProps, ComplexVisual } from '..'; import { RuntimeContext } from 'mol-task' import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link'; import { MeshValues } from 'mol-gl/renderable'; @@ -56,7 +56,7 @@ export const DefaultCrossLinkRestraintProps = { } export type CrossLinkRestraintProps = Partial<typeof DefaultCrossLinkRestraintProps> -export function CrossLinkRestraintVisual(): StructureVisual<CrossLinkRestraintProps> { +export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintProps> { let renderObject: MeshRenderObject let currentProps: typeof DefaultCrossLinkRestraintProps let mesh: Mesh diff --git a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts index 654e3ab70bd242c92920438f93b0b687303e2220..7587b44fa879680feadc2f6d2663788b244165d7 100644 --- a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts @@ -8,7 +8,7 @@ import { ValueCell } from 'mol-util/value-cell' import { MeshRenderObject } from 'mol-gl/render-object' import { Link, Structure, StructureElement } from 'mol-model/structure'; -import { DefaultStructureProps, StructureVisual } from '..'; +import { DefaultStructureProps, ComplexVisual } from '..'; import { RuntimeContext } from 'mol-task' import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link'; import { Mesh } from '../../../shape/mesh'; @@ -51,7 +51,7 @@ export const DefaultInterUnitLinkProps = { } export type InterUnitLinkProps = Partial<typeof DefaultInterUnitLinkProps> -export function InterUnitLinkVisual(): StructureVisual<InterUnitLinkProps> { +export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkProps> { let renderObject: MeshRenderObject let currentProps: typeof DefaultInterUnitLinkProps let mesh: Mesh diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts index 64a897cdae349666d4a2fdc851a282a67336dd6f..06c561e744e8d4b90a5f934559d2fc6ff1ba917a 100644 --- a/src/mol-view/stage.ts +++ b/src/mol-view/stage.ts @@ -9,12 +9,12 @@ import { StateContext } from './state/context'; import { Progress } from 'mol-task'; import { MmcifUrlToModel, ModelToStructure, StructureToSpacefill, StructureToBallAndStick, StructureToDistanceRestraint, StructureToCartoon, StructureToBackbone, StructureCenter, StructureToCarbohydrate } from './state/transform'; import { UrlEntity } from './state/entity'; -import { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; +import { SpacefillProps } from 'mol-geo/representation/structure/representation/spacefill'; import { Context } from 'mol-app/context/context'; -import { BallAndStickProps } from 'mol-geo/representation/structure/ball-and-stick'; -import { CartoonProps } from 'mol-geo/representation/structure/cartoon'; -import { DistanceRestraintProps } from 'mol-geo/representation/structure/distance-restraint'; -import { BackboneProps } from 'mol-geo/representation/structure/backbone'; +import { BallAndStickProps } from 'mol-geo/representation/structure/representation/ball-and-stick'; +import { CartoonProps } from 'mol-geo/representation/structure/representation/cartoon'; +import { DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; +import { BackboneProps } from 'mol-geo/representation/structure/representation/backbone'; // import { Queries as Q, StructureProperties as SP, Query, Selection } from 'mol-model/structure'; const spacefillProps: SpacefillProps = { diff --git a/src/mol-view/state/entity.ts b/src/mol-view/state/entity.ts index 96e96d8ffaab6fb2c74fa7dd28d6944350c85efc..22c94076fdee8baa43786daa94ca8abc65ebe352 100644 --- a/src/mol-view/state/entity.ts +++ b/src/mol-view/state/entity.ts @@ -12,12 +12,12 @@ import { CifFile, CifFrame } from 'mol-io/reader/cif'; import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; import { Model, Structure } from 'mol-model/structure'; import { StructureRepresentation } from 'mol-geo/representation/structure'; -import { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; -import { BallAndStickProps } from 'mol-geo/representation/structure/ball-and-stick'; -import { DistanceRestraintProps } from 'mol-geo/representation/structure/distance-restraint'; -import { CartoonProps } from 'mol-geo/representation/structure/cartoon'; -import { BackboneProps } from 'mol-geo/representation/structure/backbone'; -import { CarbohydrateProps } from 'mol-geo/representation/structure/carbohydrate'; +import { SpacefillProps } from 'mol-geo/representation/structure/representation/spacefill'; +import { BallAndStickProps } from 'mol-geo/representation/structure/representation/ball-and-stick'; +import { DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; +import { CartoonProps } from 'mol-geo/representation/structure/representation/cartoon'; +import { BackboneProps } from 'mol-geo/representation/structure/representation/backbone'; +import { CarbohydrateProps } from 'mol-geo/representation/structure/representation/carbohydrate'; const getNextId = idFactory(1) diff --git a/src/mol-view/state/transform.ts b/src/mol-view/state/transform.ts index 32157001c0cdae42fb709b36d61cdd7fab8a1124..b4381c8b3d471732cda52a6dcafe08e46aa5e13d 100644 --- a/src/mol-view/state/transform.ts +++ b/src/mol-view/state/transform.ts @@ -10,12 +10,12 @@ import { Model, Structure, Format } from 'mol-model/structure'; import { StateContext } from './context'; import StructureSymmetry from 'mol-model/structure/structure/symmetry'; -import { SpacefillProps, SpacefillRepresentation } from 'mol-geo/representation/structure/spacefill'; -import { BallAndStickProps, BallAndStickRepresentation } from 'mol-geo/representation/structure/ball-and-stick'; -import { DistanceRestraintRepresentation, DistanceRestraintProps } from 'mol-geo/representation/structure/distance-restraint'; -import { CartoonRepresentation, CartoonProps } from 'mol-geo/representation/structure/cartoon'; -import { BackboneProps, BackboneRepresentation } from 'mol-geo/representation/structure/backbone'; -import { CarbohydrateProps, CarbohydrateRepresentation } from 'mol-geo/representation/structure/carbohydrate'; +import { SpacefillProps, SpacefillRepresentation } from 'mol-geo/representation/structure/representation/spacefill'; +import { BallAndStickProps, BallAndStickRepresentation } from 'mol-geo/representation/structure/representation/ball-and-stick'; +import { DistanceRestraintRepresentation, DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; +import { CartoonRepresentation, CartoonProps } from 'mol-geo/representation/structure/representation/cartoon'; +import { BackboneProps, BackboneRepresentation } from 'mol-geo/representation/structure/representation/backbone'; +import { CarbohydrateProps, CarbohydrateRepresentation } from 'mol-geo/representation/structure/representation/carbohydrate'; type transformer<I extends AnyEntity, O extends AnyEntity, P extends {}> = (ctx: StateContext, inputEntity: I, props?: P) => Promise<O>