diff --git a/src/mol-repr/structure/registry.ts b/src/mol-repr/structure/registry.ts index d6d28f25330e7a4a0fd59e02bddc5a10aaa8f022..5cadc109a7933a576c8e291c75c97d052765a476 100644 --- a/src/mol-repr/structure/registry.ts +++ b/src/mol-repr/structure/registry.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -15,6 +15,7 @@ import { SpacefillRepresentationProvider } from './representation/spacefill'; import { DistanceRestraintRepresentationProvider } from './representation/distance-restraint'; import { PointRepresentationProvider } from './representation/point'; import { StructureRepresentationState } from './representation'; +import { PuttyRepresentationProvider } from './representation/putty'; export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> { constructor() { @@ -34,6 +35,7 @@ export const BuiltInStructureRepresentations = { 'molecular-surface': MolecularSurfaceRepresentationProvider, 'molecular-volume': MolecularVolumeRepresentationProvider, 'point': PointRepresentationProvider, + 'putty': PuttyRepresentationProvider, 'spacefill': SpacefillRepresentationProvider, } export type BuiltInStructureRepresentationsName = keyof typeof BuiltInStructureRepresentations diff --git a/src/mol-repr/structure/representation/cartoon.ts b/src/mol-repr/structure/representation/cartoon.ts index d524d00d6c46b62e7253725c4d59e8aa8417c686..fa1a4f32aa77c5a530a663aead2f7c41da25066c 100644 --- a/src/mol-repr/structure/representation/cartoon.ts +++ b/src/mol-repr/structure/representation/cartoon.ts @@ -54,7 +54,7 @@ export function CartoonRepresentation(ctx: RepresentationContext, getParams: Rep export const CartoonRepresentationProvider: StructureRepresentationProvider<CartoonParams> = { label: 'Cartoon', - description: 'Displays a ribbon smoothly following the trace atoms of polymers.', + description: 'Displays ribbons, planks, tubes smoothly following the trace atoms of polymers.', factory: CartoonRepresentation, getParams: getCartoonParams, defaultValues: PD.getDefaultValues(CartoonParams), diff --git a/src/mol-repr/structure/representation/putty.ts b/src/mol-repr/structure/representation/putty.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2d7f9c98cd8afbfea14fdd2145df50a84b1ae90 --- /dev/null +++ b/src/mol-repr/structure/representation/putty.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { PolymerTubeVisual, PolymerTubeParams } from '../visual/polymer-tube-mesh'; +import { PolymerGapVisual, PolymerGapParams } from '../visual/polymer-gap-cylinder'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { UnitsRepresentation } from '../units-representation'; +import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder } from '../representation'; +import { Representation, RepresentationParamsGetter, RepresentationContext } from 'mol-repr/representation'; +import { Structure, Unit } from 'mol-model/structure'; +import { ThemeRegistryContext } from 'mol-theme/theme'; + +const PuttyVisuals = { + 'polymer-tube': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerTubeParams>) => UnitsRepresentation('Polymer tube mesh', ctx, getParams, PolymerTubeVisual), + 'polymer-gap': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerGapParams>) => UnitsRepresentation('Polymer gap cylinder', ctx, getParams, PolymerGapVisual), +} +type PuttyVisualName = keyof typeof PuttyVisuals +const PuttyVisualOptions = Object.keys(PuttyVisuals).map(name => [name, name] as [PuttyVisualName, string]) + +export const PuttyParams = { + ...PolymerTubeParams, + ...PolymerGapParams, + sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }), + visuals: PD.MultiSelect<PuttyVisualName>(['polymer-tube', 'polymer-gap'], PuttyVisualOptions), +} +export type PuttyParams = typeof PuttyParams +export function getPuttyParams(ctx: ThemeRegistryContext, structure: Structure) { + const params = PD.clone(PuttyParams) + let hasNucleotides = false + let hasGaps = false + structure.units.forEach(u => { + if (!hasNucleotides && Unit.isAtomic(u) && u.nucleotideElements.length) hasNucleotides = true + if (!hasGaps && u.gapElements.length) hasGaps = true + }) + params.visuals.defaultValue = ['polymer-tube'] + if (hasGaps) params.visuals.defaultValue.push('polymer-gap') + return params +} + +export type PuttyRepresentation = StructureRepresentation<PuttyParams> +export function PuttyRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PuttyParams>): PuttyRepresentation { + return Representation.createMulti('Putty', ctx, getParams, StructureRepresentationStateBuilder, PuttyVisuals as unknown as Representation.Def<Structure, PuttyParams>) +} + +export const PuttyRepresentationProvider: StructureRepresentationProvider<PuttyParams> = { + label: 'Putty', + description: 'Displays a tube smoothly following the trace atoms of polymers.', + factory: PuttyRepresentation, + getParams: getPuttyParams, + defaultValues: PD.getDefaultValues(PuttyParams), + defaultColorTheme: 'polymer-id', + defaultSizeTheme: 'uncertainty' +} \ No newline at end of file diff --git a/src/mol-repr/structure/visual/polymer-tube-mesh.ts b/src/mol-repr/structure/visual/polymer-tube-mesh.ts new file mode 100644 index 0000000000000000000000000000000000000000..94accbff631fddd68ac6de7a8b8640d53f64a7d2 --- /dev/null +++ b/src/mol-repr/structure/visual/polymer-tube-mesh.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Unit, Structure } from 'mol-model/structure'; +import { UnitsVisual } from '../representation'; +import { VisualUpdateState } from '../../util'; +import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, eachPolymerElement, interpolateSizes } from './util/polymer'; +import { isNucleic } from 'mol-model/structure/model/types'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { Mesh } from 'mol-geo/geometry/mesh/mesh'; +import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder'; +import { addTube } from 'mol-geo/geometry/mesh/builder/tube'; +import { VisualContext } from 'mol-repr/visual'; +import { Theme } from 'mol-theme/theme'; + +export const PolymerTubeMeshParams = { + sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }), + linearSegments: PD.Numeric(8, { min: 1, max: 48, step: 1 }), + radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), +} +export const DefaultPolymerTubeMeshProps = PD.getDefaultValues(PolymerTubeMeshParams) +export type PolymerTubeMeshProps = typeof DefaultPolymerTubeMeshProps + +// TODO handle polymer ends properly + +function createPolymerTubeMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerTubeMeshProps, mesh?: Mesh) { + const polymerElementCount = unit.polymerElements.length + + if (!polymerElementCount) return Mesh.createEmpty(mesh) + const { sizeFactor, linearSegments, radialSegments } = props + + const vertexCount = linearSegments * radialSegments * polymerElementCount + (radialSegments + 1) * polymerElementCount * 2 + const builderState = MeshBuilder.createState(vertexCount, vertexCount / 10, mesh) + + const state = createCurveSegmentState(linearSegments) + const { curvePoints, normalVectors, binormalVectors, widthValues, heightValues } = state + + let i = 0 + const polymerTraceIt = PolymerTraceIterator(unit) + while (polymerTraceIt.hasNext) { + const v = polymerTraceIt.move() + builderState.currentGroup = i + + const isNucleicType = isNucleic(v.moleculeType) + const tension = isNucleicType ? 0.5 : 0.9 + const shift = isNucleicType ? 0.3 : 0.5 + + interpolateCurveSegment(state, v, tension, shift) + + let s0 = theme.size.size(v.centerPrev) * sizeFactor + let s1 = theme.size.size(v.center) * sizeFactor + let s2 = theme.size.size(v.centerNext) * sizeFactor + + interpolateSizes(state, s0, s1, s2, s0, s1, s2, shift) + addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, widthValues, heightValues, 1, v.first, v.last) + + ++i + } + + return MeshBuilder.getMesh(builderState) +} + +export const PolymerTubeParams = { + ...UnitsMeshParams, + ...PolymerTubeMeshParams +} +export type PolymerTubeParams = typeof PolymerTubeParams + +export function PolymerTubeVisual(): UnitsVisual<PolymerTubeParams> { + return UnitsMeshVisual<PolymerTubeParams>({ + defaultProps: PD.getDefaultValues(PolymerTubeParams), + createGeometry: createPolymerTubeMesh, + createLocationIterator: PolymerLocationIterator.fromGroup, + getLoci: getPolymerElementLoci, + eachLocation: eachPolymerElement, + setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerTubeParams>, currentProps: PD.Values<PolymerTubeParams>) => { + state.createGeometry = ( + newProps.sizeFactor !== currentProps.sizeFactor || + newProps.linearSegments !== currentProps.linearSegments || + newProps.radialSegments !== currentProps.radialSegments + ) + } + }) +} \ No newline at end of file diff --git a/src/mol-theme/color/uncertainty.ts b/src/mol-theme/color/uncertainty.ts index 857951921bcf5efb4e6e241e2805d4e9c38ff2bb..874f441a3c59a9b30f3996fe58ffbcfcbc1a61dd 100644 --- a/src/mol-theme/color/uncertainty.ts +++ b/src/mol-theme/color/uncertainty.ts @@ -36,6 +36,7 @@ export function getUncertainty(unit: Unit, element: ElementIndex): number { export function UncertaintyColorTheme(ctx: ThemeDataContext, props: PD.Values<UncertaintyColorThemeParams>): ColorTheme<UncertaintyColorThemeParams> { const scale = ColorScale.create({ + reverse: true, domain: props.domain, listOrName: props.list, })