diff --git a/src/mol-geo/geometry/mesh/builder/sheet.ts b/src/mol-geo/geometry/mesh/builder/sheet.ts index c0d7a473318356bcf891dcb8088d45411065e91a..001e45a233a2866e2d4a2a6184b92ee7118019b6 100644 --- a/src/mol-geo/geometry/mesh/builder/sheet.ts +++ b/src/mol-geo/geometry/mesh/builder/sheet.ts @@ -15,6 +15,8 @@ const tV = Vec3.zero() const horizontalVector = Vec3.zero() const verticalVector = Vec3.zero() +const verticalRightVector = Vec3.zero() +const verticalLeftVector = Vec3.zero() const normalOffset = Vec3.zero() const positionVector = Vec3.zero() const normalVector = Vec3.zero() @@ -25,6 +27,41 @@ const p2 = Vec3.zero() const p3 = Vec3.zero() const p4 = Vec3.zero() +function addCap(offset: number, builder: MeshBuilder, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, width: number, leftHeight: number, rightHeight: number) { + const { vertices, normals, indices } = builder.state + const vertexCount = vertices.elementCount + + Vec3.fromArray(verticalLeftVector, normalVectors, offset) + Vec3.scale(verticalLeftVector, verticalLeftVector, leftHeight) + + Vec3.fromArray(verticalRightVector, normalVectors, offset) + Vec3.scale(verticalRightVector, verticalRightVector, rightHeight) + + Vec3.fromArray(horizontalVector, binormalVectors, offset) + Vec3.scale(horizontalVector, horizontalVector, width) + + Vec3.fromArray(positionVector, controlPoints, offset) + + Vec3.add(p1, Vec3.add(p1, positionVector, horizontalVector), verticalRightVector) + Vec3.sub(p2, Vec3.add(p2, positionVector, horizontalVector), verticalLeftVector) + Vec3.sub(p3, Vec3.sub(p3, positionVector, horizontalVector), verticalLeftVector) + Vec3.add(p4, Vec3.sub(p4, positionVector, horizontalVector), verticalRightVector) + + ChunkedArray.add3(vertices, p1[0], p1[1], p1[2]) + ChunkedArray.add3(vertices, p2[0], p2[1], p2[2]) + ChunkedArray.add3(vertices, p3[0], p3[1], p3[2]) + ChunkedArray.add3(vertices, p4[0], p4[1], p4[2]) + + Vec3.cross(normalVector, horizontalVector, verticalLeftVector) + + for (let i = 0; i < 4; ++i) { + ChunkedArray.add3(normals, normalVector[0], normalVector[1], normalVector[2]) + } + ChunkedArray.add3(indices, vertexCount + 2, vertexCount + 1, vertexCount) + ChunkedArray.add3(indices, vertexCount, vertexCount + 3, vertexCount + 2) +} + +/** set arrowHeight = 0 for no arrow */ export function addSheet(builder: MeshBuilder, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean) { const { currentGroup, vertices, normals, indices, groups } = builder.state @@ -112,67 +149,19 @@ export function addSheet(builder: MeshBuilder, controlPoints: ArrayLike<number>, } if (startCap) { - const offset = 0 - vertexCount = vertices.elementCount - - Vec3.fromArray(verticalVector, normalVectors, offset) - Vec3.scale(verticalVector, verticalVector, arrowHeight === 0 ? height : arrowHeight); - - Vec3.fromArray(horizontalVector, binormalVectors, offset) - Vec3.scale(horizontalVector, horizontalVector, width); - - Vec3.fromArray(positionVector, controlPoints, offset) - - Vec3.add(p1, Vec3.add(p1, positionVector, horizontalVector), verticalVector) - Vec3.sub(p2, Vec3.add(p2, positionVector, horizontalVector), verticalVector) - Vec3.sub(p3, Vec3.sub(p3, positionVector, horizontalVector), verticalVector) - Vec3.add(p4, Vec3.sub(p4, positionVector, horizontalVector), verticalVector) - - ChunkedArray.add3(vertices, p1[0], p1[1], p1[2]) - ChunkedArray.add3(vertices, p2[0], p2[1], p2[2]) - ChunkedArray.add3(vertices, p3[0], p3[1], p3[2]) - ChunkedArray.add3(vertices, p4[0], p4[1], p4[2]) - - Vec3.cross(normalVector, horizontalVector, verticalVector) - - for (let i = 0; i < 4; ++i) { - ChunkedArray.add3(normals, normalVector[0], normalVector[1], normalVector[2]) - } - ChunkedArray.add3(indices, vertexCount + 2, vertexCount + 1, vertexCount); - ChunkedArray.add3(indices, vertexCount, vertexCount + 3, vertexCount + 2); + const h = arrowHeight === 0 ? height : arrowHeight + addCap(0, builder, controlPoints, normalVectors, binormalVectors, width, h, h) + } else if (arrowHeight > 0) { + addCap(0, builder, controlPoints, normalVectors, binormalVectors, width, -arrowHeight, height) + addCap(0, builder, controlPoints, normalVectors, binormalVectors, width, arrowHeight, -height) } if (endCap && arrowHeight === 0) { - const offset = linearSegments * 3 - vertexCount = vertices.elementCount - - Vec3.fromArray(verticalVector, normalVectors, offset) - Vec3.scale(verticalVector, verticalVector, height); - - Vec3.fromArray(horizontalVector, binormalVectors, offset) - Vec3.scale(horizontalVector, horizontalVector, width); - - Vec3.fromArray(positionVector, controlPoints, offset) - - Vec3.add(p1, Vec3.add(p1, positionVector, horizontalVector), verticalVector) - Vec3.sub(p2, Vec3.add(p2, positionVector, horizontalVector), verticalVector) - Vec3.sub(p3, Vec3.sub(p3, positionVector, horizontalVector), verticalVector) - Vec3.add(p4, Vec3.sub(p4, positionVector, horizontalVector), verticalVector) - - ChunkedArray.add3(vertices, p1[0], p1[1], p1[2]) - ChunkedArray.add3(vertices, p2[0], p2[1], p2[2]) - ChunkedArray.add3(vertices, p3[0], p3[1], p3[2]) - ChunkedArray.add3(vertices, p4[0], p4[1], p4[2]) - - Vec3.cross(normalVector, horizontalVector, verticalVector) - - for (let i = 0; i < 4; ++i) { - ChunkedArray.add3(normals, normalVector[0], normalVector[1], normalVector[2]) - } - ChunkedArray.add3(indices, vertexCount + 2, vertexCount + 1, vertexCount); - ChunkedArray.add3(indices, vertexCount, vertexCount + 3, vertexCount + 2); + addCap(linearSegments * 3, builder, controlPoints, normalVectors, binormalVectors, width, height, height) } - const addedVertexCount = (linearSegments + 1) * 8 + (startCap ? 4 : 0) + (endCap && arrowHeight === 0 ? 4 : 0) + const addedVertexCount = (linearSegments + 1) * 8 + + (startCap ? 4 : (arrowHeight > 0 ? 8 : 0)) + + (endCap && arrowHeight === 0 ? 4 : 0) for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup) } \ No newline at end of file diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index ad5116ba188376a185411a9c8a39795260d75ae9..bfaff131f548ccdb73cd6add74e849067d3efc85 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -8,7 +8,7 @@ import * as React from 'react' import { ParamDefinition as PD } from 'mol-util/param-definition'; import { camelCaseToWords } from 'mol-util/string'; -import { ColorNames } from 'mol-util/color/tables'; +import { ColorNames, ColorNamesValueMap } from 'mol-util/color/tables'; import { Color } from 'mol-util/color'; import { Slider, Slider2 } from './slider'; @@ -172,6 +172,13 @@ function ColorOptions() { return _colors; } +function ColorValueOption(color: Color) { + return !ColorNamesValueMap.has(color) ? <option key={Color.toHexString(color)} value={color} style={{ background: `${Color.toStyle(color)}` }} > + {Color.toHexString(color)} + </option> : null +} + + export class ColorControl extends SimpleParam<PD.Color> { onChange = (e: React.ChangeEvent<HTMLSelectElement>) => { this.update(Color(parseInt(e.target.value))); @@ -179,6 +186,7 @@ export class ColorControl extends SimpleParam<PD.Color> { renderControl() { return <select value={this.props.value} onChange={this.onChange} style={{ borderLeft: `16px solid ${Color.toStyle(this.props.value)}` }}> + {ColorValueOption(this.props.value)} {ColorOptions()} </select>; } diff --git a/src/mol-repr/structure/visual/polymer-direction-wedge.ts b/src/mol-repr/structure/visual/polymer-direction-wedge.ts index 786ba2992b77deaf50e6ac687e74c8b93096a9c8..3c9dc41c38ed8bc2cbdd94b286962cf4a3e32b5e 100644 --- a/src/mol-repr/structure/visual/polymer-direction-wedge.ts +++ b/src/mol-repr/structure/visual/polymer-direction-wedge.ts @@ -62,7 +62,7 @@ async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, s interpolateCurveSegment(state, v, tension, shift) - if ((isSheet && !v.secStrucChange) || !isSheet) { + if ((isSheet && !v.secStrucLast) || !isSheet) { const size = theme.size.size(v.center) * sizeFactor const depth = depthFactor * size const width = widthFactor * size diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts index 65d6b070eacfdc1d0b933048b3da26af57fee671..6890b4853435a6a41d441132cd69346ae1676dda 100644 --- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-repr/structure/visual/polymer-trace-mesh.ts @@ -52,7 +52,7 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: const isNucleicType = isNucleic(v.moleculeType) const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta) const isHelix = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Helix) - const tension = (isNucleicType || isSheet) ? 0.5 : 0.9 + const tension = isNucleicType ? 0.5 : 0.9 const shift = isNucleicType ? 0.3 : 0.5 interpolateCurveSegment(state, v, tension, shift) @@ -62,8 +62,8 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: if (isSheet) { const height = width * aspectRatio - const arrowHeight = v.secStrucChange ? height * arrowFactor : 0 - addSheet(builder, curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true) + const arrowHeight = v.secStrucLast ? height * arrowFactor : 0 + addSheet(builder, curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, v.secStrucFirst, v.secStrucLast) } else { let height: number if (isHelix) { @@ -74,7 +74,7 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: } else { height = width } - addTube(builder, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true) + addTube(builder, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, v.secStrucFirst, v.secStrucLast) } if (i % 10000 === 0 && ctx.runtime.shouldUpdate) { diff --git a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts index 37bfc18d317b5c96c015f13bda2c9c15bafbb37b..4b3e38c4558eb15a02e4a5adea2d173619e913db 100644 --- a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts +++ b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts @@ -13,6 +13,7 @@ import SortedRanges from 'mol-data/int/sorted-ranges'; import { CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/structure/model/properties/coarse'; import { getAtomicMoleculeType, getElementIndexForAtomRole } from 'mol-model/structure/util'; import { getPolymerRanges } from '../polymer'; +import { AtomicConformation } from 'mol-model/structure/model/properties/atomic'; /** * Iterates over individual residues/coarse elements in polymers of a unit while @@ -30,20 +31,22 @@ export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> interface PolymerTraceElement { center: StructureElement first: boolean, last: boolean + secStrucFirst: boolean, secStrucLast: boolean secStrucType: SecondaryStructureType - secStrucChange: boolean moleculeType: MoleculeType p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3 d12: Vec3, d23: Vec3 } +const SecStrucTypeNA = SecondaryStructureType.create(SecondaryStructureType.Flag.NA) + function createPolymerTraceElement (unit: Unit): PolymerTraceElement { return { center: StructureElement.create(unit), first: false, last: false, - secStrucType: SecondaryStructureType.create(SecondaryStructureType.Flag.NA), - secStrucChange: false, + secStrucFirst: false, secStrucLast: false, + secStrucType: SecStrucTypeNA, moleculeType: MoleculeType.unknown, p0: Vec3.zero(), p1: Vec3.zero(), p2: Vec3.zero(), p3: Vec3.zero(), p4: Vec3.zero(), d12: Vec3.create(1, 0, 0), d23: Vec3.create(1, 0, 0), @@ -57,10 +60,15 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex> private residueIt: Segmentation.SegmentIterator<ResidueIndex> private polymerSegment: Segmentation.Segment<ResidueIndex> + private secondaryStructureType: ArrayLike<SecondaryStructureType> private residueSegmentMin: ResidueIndex private residueSegmentMax: ResidueIndex + private prevSecStrucType: SecondaryStructureType + private currSecStrucType: SecondaryStructureType + private nextSecStrucType: SecondaryStructureType private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer private residueAtomSegments: Segmentation<ElementIndex, ResidueIndex> + private atomicConformation: AtomicConformation private p0 = Vec3.zero(); private p1 = Vec3.zero(); @@ -78,13 +86,13 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> hasNext: boolean = false; private pos(target: Vec3, index: number) { - target[0] = this.unit.model.atomicConformation.x[index] - target[1] = this.unit.model.atomicConformation.y[index] - target[2] = this.unit.model.atomicConformation.z[index] + target[0] = this.atomicConformation.x[index] + target[1] = this.atomicConformation.y[index] + target[2] = this.atomicConformation.z[index] } private updateResidueSegmentRange(polymerSegment: Segmentation.Segment<ResidueIndex>) { - const { index } = this.unit.model.atomicHierarchy.residueAtomSegments + const { index } = this.residueAtomSegments this.residueSegmentMin = index[this.unit.elements[polymerSegment.start]] this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]] } @@ -111,8 +119,7 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> } private setControlPoint(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: ResidueIndex) { - const ss = this.unit.model.properties.secondaryStructure.type[residueIndex] - if (SecondaryStructureType.is(ss, SecondaryStructureType.Flag.Beta)) { + if (SecondaryStructureType.is(this.currSecStrucType, SecondaryStructureType.Flag.Beta)) { Vec3.scale(out, Vec3.add(out, p1, Vec3.add(out, p3, Vec3.add(out, p2, p2))), 1/4) } else { Vec3.copy(out, p2) @@ -129,6 +136,8 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> this.updateResidueSegmentRange(this.polymerSegment) if (residueIt.hasNext) { this.state = AtomicPolymerTraceIteratorState.nextResidue + this.currSecStrucType = SecStrucTypeNA + this.nextSecStrucType = this.secondaryStructureType[this.residueSegmentMin] break } } @@ -136,7 +145,17 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> if (this.state === AtomicPolymerTraceIteratorState.nextResidue) { const { index: residueIndex } = residueIt.move(); + this.prevSecStrucType = this.currSecStrucType + this.currSecStrucType = this.nextSecStrucType + this.nextSecStrucType = residueIt.hasNext ? this.secondaryStructureType[residueIndex + 1] : SecStrucTypeNA + + value.secStrucType = this.currSecStrucType value.center.element = this.getElementIndex(residueIndex, 'trace') + value.first = residueIndex === this.residueSegmentMin + value.last = residueIndex === this.residueSegmentMax + value.secStrucFirst = this.prevSecStrucType !== this.currSecStrucType + value.secStrucLast = this.currSecStrucType !== this.nextSecStrucType + value.moleculeType = getAtomicMoleculeType(this.unit.model, residueIndex) this.pos(this.p0, this.getElementIndex(residueIndex - 3 as ResidueIndex, 'trace')) this.pos(this.p1, this.getElementIndex(residueIndex - 2 as ResidueIndex, 'trace')) @@ -151,8 +170,6 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> this.pos(this.v23, this.getElementIndex(residueIndex, 'direction')) // this.pos(this.v34, this.getAtomIndex(residueIndex + 1 as ResidueIndex, 'direction')) - this.value.secStrucType = this.unit.model.properties.secondaryStructure.type[residueIndex] - this.setControlPoint(value.p0, this.p0, this.p1, this.p2, residueIndex - 2 as ResidueIndex) this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndex - 1 as ResidueIndex) this.setControlPoint(value.p2, this.p2, this.p3, this.p4, residueIndex) @@ -162,11 +179,6 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> Vec3.copy(value.d12, this.v12) Vec3.copy(value.d23, this.v23) - value.first = residueIndex === this.residueSegmentMin - value.last = residueIndex === this.residueSegmentMax - value.secStrucChange = this.unit.model.properties.secondaryStructure.key[residueIndex] !== this.unit.model.properties.secondaryStructure.key[residueIndex + 1] - value.moleculeType = getAtomicMoleculeType(this.unit.model, residueIndex) - if (!residueIt.hasNext) { this.state = AtomicPolymerTraceIteratorState.nextPolymer } @@ -178,7 +190,9 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> } constructor(private unit: Unit.Atomic) { + this.atomicConformation = unit.model.atomicConformation this.residueAtomSegments = unit.model.atomicHierarchy.residueAtomSegments + this.secondaryStructureType = unit.model.properties.secondaryStructure.type this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements) this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements); this.value = createPolymerTraceElement(unit) diff --git a/src/mol-util/color/tables.ts b/src/mol-util/color/tables.ts index 758fe153507c286071972d72d88fd2340e960322..b4380176ccfb35ad09a8681d2893aa0988ba9b96 100644 --- a/src/mol-util/color/tables.ts +++ b/src/mol-util/color/tables.ts @@ -259,4 +259,13 @@ export const ColorNames = ColorMap({ whitesmoke: 0xf5f5f5, yellow: 0xffff00, yellowgreen: 0x9acd32 -}) \ No newline at end of file +}) +export type ColorNames = typeof ColorNames +export type ColorName = keyof ColorNames +export const ColorNamesValueMap = (function(){ + const map = new Map<Color, ColorName>() + Object.keys(ColorNames).forEach(name => { + map.set(ColorNames[name as ColorName], name as ColorName) + }) + return map +})()