diff --git a/src/mol-geo/geometry/mesh/builder/tube.ts b/src/mol-geo/geometry/mesh/builder/tube.ts index d60dd528dae400f318347d87753903715b48f367..693bc69d8cd9d0a90ed6f3160f1dab7db7170a56 100644 --- a/src/mol-geo/geometry/mesh/builder/tube.ts +++ b/src/mol-geo/geometry/mesh/builder/tube.ts @@ -27,7 +27,7 @@ function add3AndScale2(out: Vec3, a: Vec3, b: Vec3, c: Vec3, sa: number, sb: num out[2] = (a[2] * sa) + (b[2] * sb) + c[2]; } -export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean) { +export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, waveFactor: number, startCap: boolean, endCap: boolean) { const { currentGroup, vertices, normals, indices, groups } = state let vertexCount = vertices.elementCount @@ -39,6 +39,9 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe Vec3.fromArray(v, binormalVectors, i3) Vec3.fromArray(controlPoint, controlPoints, i3) + const width = widthValues[i] + const height = heightValues[i] + const tt = di * i - 0.5; const ff = 1 + (waveFactor - 1) * (Math.cos(2 * Math.PI * tt) + 1); const w = ff * width, h = ff * height; @@ -83,6 +86,9 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe ChunkedArray.add3(vertices, controlPoint[0], controlPoint[1], controlPoint[2]); ChunkedArray.add3(normals, normalVector[0], normalVector[1], normalVector[2]); + const width = widthValues[0] + const height = heightValues[0] + vertexCount = vertices.elementCount for (let i = 0; i < radialSegments; ++i) { const t = 2 * Math.PI * i / radialSegments; @@ -112,6 +118,9 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe ChunkedArray.add3(vertices, controlPoint[0], controlPoint[1], controlPoint[2]); ChunkedArray.add3(normals, normalVector[0], normalVector[1], normalVector[2]); + const width = widthValues[linearSegments] + const height = heightValues[linearSegments] + vertexCount = vertices.elementCount for (let i = 0; i < radialSegments; ++i) { const t = 2 * Math.PI * i / radialSegments diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts index deb5042742b2b45d282c1fedbbca8d1285f26f95..1da10764176ca24e053608db0e38aa325d3be4db 100644 --- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-repr/structure/visual/polymer-trace-mesh.ts @@ -7,7 +7,7 @@ import { Unit, Structure } from 'mol-model/structure'; import { UnitsVisual } from '../representation'; import { VisualUpdateState } from '../../util'; -import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer'; +import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement, interpolateSizes } from './util/polymer'; import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types'; import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; import { ParamDefinition as PD } from 'mol-util/param-definition'; @@ -41,7 +41,7 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc const isCoarse = Unit.isCoarse(unit) const state = createCurveSegmentState(linearSegments) - const { curvePoints, normalVectors, binormalVectors } = state + const { curvePoints, normalVectors, binormalVectors, widthValues, heightValues } = state let i = 0 const polymerTraceIt = PolymerTraceIterator(unit) @@ -57,24 +57,39 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc interpolateCurveSegment(state, v, tension, shift) - let width = theme.size.size(v.center) * sizeFactor - if (isCoarse) width *= aspectRatio / 2 + let w0 = theme.size.size(v.centerPrev) * sizeFactor + let w1 = theme.size.size(v.center) * sizeFactor + let w2 = theme.size.size(v.centerNext) * sizeFactor + if (isCoarse) { + w0 *= aspectRatio / 2 + w1 *= aspectRatio / 2 + w2 *= aspectRatio / 2 + } if (isSheet) { - const height = width * aspectRatio - const arrowHeight = v.secStrucLast ? height * arrowFactor : 0 - addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, v.secStrucFirst, v.secStrucLast) + const h1 = w1 * aspectRatio + const arrowHeight = v.secStrucLast ? h1 * arrowFactor : 0 + addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, w1, h1, arrowHeight, v.secStrucFirst, v.secStrucLast) } else { - let height: number + let h0: number, h1: number, h2: number if (isHelix) { - height = width * aspectRatio + h0 = w0 * aspectRatio + h1 = w1 * aspectRatio + h2 = w2 * aspectRatio } else if (isNucleicType) { - height = width * aspectRatio; - [width, height] = [height, width] + h0 = w0 * aspectRatio; + [w0, h0] = [h0, w0] + h1 = w1 * aspectRatio; + [w1, h1] = [h1, w1] + h2 = w2 * aspectRatio; + [w2, h2] = [h2, w2] } else { - height = width + h0 = w0 + h1 = w1 + h2 = w2 } - addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, v.secStrucFirst, v.secStrucLast) + interpolateSizes(state, w0, w1, w2, h0, h1, h2, shift) + addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, widthValues, heightValues, 1, v.secStrucFirst, v.secStrucLast) } ++i diff --git a/src/mol-repr/structure/visual/util/polymer/curve-segment.ts b/src/mol-repr/structure/visual/util/polymer/curve-segment.ts index 1231c6bb0f9e160f9ed62b36cae038b867f9c2f8..07ebb90f650415900756038b8a3d779e3df85a9a 100644 --- a/src/mol-repr/structure/visual/util/polymer/curve-segment.ts +++ b/src/mol-repr/structure/visual/util/polymer/curve-segment.ts @@ -6,12 +6,15 @@ import { Vec3 } from 'mol-math/linear-algebra'; import { NumberArray } from 'mol-util/type-helpers'; +import { lerp } from 'mol-math/interpolate'; export interface CurveSegmentState { curvePoints: NumberArray, tangentVectors: NumberArray, normalVectors: NumberArray, binormalVectors: NumberArray, + widthValues: NumberArray, + heightValues: NumberArray, linearSegments: number } @@ -21,12 +24,15 @@ export interface CurveSegmentControls { } export function createCurveSegmentState(linearSegments: number): CurveSegmentState { - const pn = (linearSegments + 1) * 3 + const n = linearSegments + 1 + const pn = n * 3 return { curvePoints: new Float32Array(pn), tangentVectors: new Float32Array(pn), normalVectors: new Float32Array(pn), binormalVectors: new Float32Array(pn), + widthValues: new Float32Array(n), + heightValues: new Float32Array(n), linearSegments } } @@ -112,4 +118,21 @@ export function interpolateNormals(state: CurveSegmentState, controls: CurveSegm Vec3.normalize(binormalVec, Vec3.cross(binormalVec, tangentVec, normalVec)) Vec3.toArray(binormalVec, binormalVectors, i * 3) } +} + +export function interpolateSizes(state: CurveSegmentState, w0: number, w1: number, w2: number, h0: number, h1: number, h2: number, shift: number) { + const { widthValues, heightValues, linearSegments } = state + + const shift1 = 1 - shift + + for (let i = 0; i <= linearSegments; ++i) { + const t = i * 1.0 / linearSegments; + if (t < shift1) { + widthValues[i] = lerp(w0, w1, t + shift) + heightValues[i] = lerp(h0, h1, t + shift) + } else { + widthValues[i] = lerp(w1, w2, t - shift1) + heightValues[i] = lerp(h1, h2, t - shift1) + } + } } \ No newline at end of file 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 fa3d7555572402e4978bbbb5ad81c5e46a48340f..5332a697035c9b1f42f7ed175fb709af5280caa0 100644 --- a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts +++ b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts @@ -29,6 +29,8 @@ export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> interface PolymerTraceElement { center: StructureElement + centerPrev: StructureElement + centerNext: StructureElement first: boolean, last: boolean secStrucFirst: boolean, secStrucLast: boolean secStrucType: SecondaryStructureType @@ -43,6 +45,8 @@ const SecStrucTypeNA = SecondaryStructureType.create(SecondaryStructureType.Flag function createPolymerTraceElement (unit: Unit): PolymerTraceElement { return { center: StructureElement.create(unit), + centerPrev: StructureElement.create(unit), + centerNext: StructureElement.create(unit), first: false, last: false, secStrucFirst: false, secStrucLast: false, secStrucType: SecStrucTypeNA, @@ -154,23 +158,35 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> this.nextSecStrucType = residueIt.hasNext ? this.secondaryStructureType[residueIndex + 1] : SecStrucTypeNA value.secStrucType = this.currSecStrucType - value.center.element = this.traceElementIndex[residueIndex] value.first = residueIndex === this.residueSegmentMin value.last = residueIndex === this.residueSegmentMax value.secStrucFirst = this.prevSecStrucType !== this.currSecStrucType value.secStrucLast = this.currSecStrucType !== this.nextSecStrucType value.moleculeType = this.moleculeType[residueIndex] + const residueIndexPrev3 = this.getResidueIndex(residueIndex - 3) + const residueIndexPrev2 = this.getResidueIndex(residueIndex - 2) + const residueIndexPrev1 = this.getResidueIndex(residueIndex - 1) + const residueIndexNext1 = this.getResidueIndex(residueIndex + 1) + const residueIndexNext2 = this.getResidueIndex(residueIndex + 2) + const residueIndexNext3 = this.getResidueIndex(residueIndex + 3) + if (value.first) { - this.pos(this.p0, this.traceElementIndex[this.getResidueIndex(residueIndex - 3)]) - this.pos(this.p1, this.traceElementIndex[this.getResidueIndex(residueIndex - 2)]) - this.pos(this.p2, this.traceElementIndex[this.getResidueIndex(residueIndex - 1)]) - this.pos(this.p3, value.center.element) - this.pos(this.p4, this.traceElementIndex[this.getResidueIndex(residueIndex + 1)]) - this.pos(this.p5, this.traceElementIndex[this.getResidueIndex(residueIndex + 2)]) - - this.pos(this.v12, this.directionElementIndex[this.getResidueIndex(residueIndex - 1)]) + value.centerPrev.element = this.traceElementIndex[residueIndexPrev1] + value.center.element = this.traceElementIndex[residueIndex] + + this.pos(this.p0, this.traceElementIndex[residueIndexPrev3]) + this.pos(this.p1, this.traceElementIndex[residueIndexPrev2]) + this.pos(this.p2, this.traceElementIndex[residueIndexPrev1]) + this.pos(this.p3, this.traceElementIndex[residueIndex]) + this.pos(this.p4, this.traceElementIndex[residueIndexNext1]) + this.pos(this.p5, this.traceElementIndex[residueIndexNext2]) + + this.pos(this.v12, this.directionElementIndex[residueIndexPrev1]) } else { + value.centerPrev.element = value.center.element + value.center.element = value.centerNext.element + Vec3.copy(this.p0, this.p1) Vec3.copy(this.p1, this.p2) Vec3.copy(this.p2, this.p3) @@ -180,14 +196,15 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> Vec3.copy(this.v12, this.v23) } - this.pos(this.p6, this.traceElementIndex[this.getResidueIndex(residueIndex + 3 as ResidueIndex)]) + value.centerNext.element = this.traceElementIndex[residueIndexNext1] + this.pos(this.p6, this.traceElementIndex[residueIndexNext3]) this.pos(this.v23, this.directionElementIndex[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.p0, this.p0, this.p1, this.p2, residueIndexPrev2) + this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndexPrev1) this.setControlPoint(value.p2, this.p2, this.p3, this.p4, residueIndex) - this.setControlPoint(value.p3, this.p3, this.p4, this.p5, residueIndex + 1 as ResidueIndex) - this.setControlPoint(value.p4, this.p4, this.p5, this.p6, residueIndex + 2 as ResidueIndex) + this.setControlPoint(value.p3, this.p3, this.p4, this.p5, residueIndexNext1) + this.setControlPoint(value.p4, this.p4, this.p5, this.p6, residueIndexNext2) Vec3.copy(value.d12, this.v12) Vec3.copy(value.d23, this.v23) @@ -228,8 +245,11 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement> private elementIndex: number hasNext: boolean = false; + private getElementIndex(elementIndex: number) { + return Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1) as ElementIndex + } + private pos(target: Vec3, elementIndex: number) { - elementIndex = Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1) const index = this.unit.elements[elementIndex] target[0] = this.conformation.x[index] target[1] = this.conformation.y[index] @@ -251,13 +271,21 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement> if (this.state === CoarsePolymerTraceIteratorState.nextElement) { this.elementIndex += 1 + + const elementIndexPrev2 = this.getElementIndex(this.elementIndex - 2) + const elementIndexPrev1 = this.getElementIndex(this.elementIndex - 1) + const elementIndexNext1 = this.getElementIndex(this.elementIndex + 1) + const elementIndexNext2 = this.getElementIndex(this.elementIndex + 2) + + this.value.centerPrev.element = this.value.center.unit.elements[elementIndexPrev1] this.value.center.element = this.value.center.unit.elements[this.elementIndex] + this.value.centerNext.element = this.value.center.unit.elements[elementIndexNext1] - this.pos(this.value.p0, this.elementIndex - 2) - this.pos(this.value.p1, this.elementIndex - 1) + this.pos(this.value.p0, elementIndexPrev2) + this.pos(this.value.p1, elementIndexPrev1) this.pos(this.value.p2, this.elementIndex) - this.pos(this.value.p3, this.elementIndex + 1) - this.pos(this.value.p4, this.elementIndex + 2) + this.pos(this.value.p3, elementIndexNext1) + this.pos(this.value.p4, elementIndexNext2) this.value.first = this.elementIndex === this.polymerSegment.start this.value.last = this.elementIndex === this.polymerSegment.end - 1