diff --git a/src/mol-geo/geometry/mesh/builder/cylinder.ts b/src/mol-geo/geometry/mesh/builder/cylinder.ts index 34032db0803c376e9b0ec0d0b6bfbe92ed5cf0a3..42e5a4e8e66e114cb5ae07a9c12cb953ea41158f 100644 --- a/src/mol-geo/geometry/mesh/builder/cylinder.ts +++ b/src/mol-geo/geometry/mesh/builder/cylinder.ts @@ -1,13 +1,15 @@ /** - * 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> */ import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra'; import { MeshBuilder } from '../mesh-builder'; -import { Primitive } from '../../../primitive/primitive'; +import { Primitive, transformPrimitive } from '../../../primitive/primitive'; import { Cylinder, CylinderProps } from '../../../primitive/cylinder'; +import { Prism } from '../../../primitive/prism'; +import { polygon } from '../../../primitive/polygon'; const cylinderMap = new Map<string, Primitive>() const up = Vec3.create(0, 1, 0) @@ -34,7 +36,12 @@ function getCylinder(props: CylinderProps) { const key = JSON.stringify(props) let cylinder = cylinderMap.get(key) if (cylinder === undefined) { - cylinder = Cylinder(props) + if (props.radialSegments && props.radialSegments <= 4) { + const box = Prism(polygon(4, true, props.radiusTop), props) + cylinder = transformPrimitive(box, Mat4.rotX90) + } else { + cylinder = Cylinder(props) + } cylinderMap.set(key, cylinder) } return cylinder diff --git a/src/mol-geo/geometry/mesh/builder/ribbon.ts b/src/mol-geo/geometry/mesh/builder/ribbon.ts new file mode 100644 index 0000000000000000000000000000000000000000..c227b68fd55233a539dac47c6476e9fb0532685c --- /dev/null +++ b/src/mol-geo/geometry/mesh/builder/ribbon.ts @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2018-2019 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 { Vec3 } from '../../../../mol-math/linear-algebra'; +import { ChunkedArray } from '../../../../mol-data/util'; +import { MeshBuilder } from '../mesh-builder'; + +const tA = Vec3.zero() +const tB = Vec3.zero() +const tV = Vec3.zero() + +const horizontalVector = Vec3.zero() +const verticalVector = Vec3.zero() +const normalOffset = Vec3.zero() +const positionVector = Vec3.zero() +const normalVector = Vec3.zero() +const torsionVector = Vec3.zero() + +/** set arrowHeight = 0 for no arrow */ +export function addRibbon(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, arrowHeight: number) { + const { currentGroup, vertices, normals, indices, groups } = state + + let vertexCount = vertices.elementCount + let offsetLength = 0 + + if (arrowHeight > 0) { + Vec3.fromArray(tA, controlPoints, 0) + Vec3.fromArray(tB, controlPoints, linearSegments * 3) + offsetLength = arrowHeight / Vec3.magnitude(Vec3.sub(tV, tB, tA)) + } + + for (let i = 0; i <= linearSegments; ++i) { + const width = widthValues[i] + const height = heightValues[i] + + const actualHeight = arrowHeight === 0 ? height : arrowHeight * (1 - i / linearSegments); + const i3 = i * 3 + + Vec3.fromArray(verticalVector, normalVectors, i3) + Vec3.scale(verticalVector, verticalVector, actualHeight); + + Vec3.fromArray(horizontalVector, binormalVectors, i3) + Vec3.scale(horizontalVector, horizontalVector, width); + + if (arrowHeight > 0) { + Vec3.fromArray(tA, normalVectors, i3) + Vec3.fromArray(tB, binormalVectors, i3) + Vec3.scale(normalOffset, Vec3.cross(normalOffset, tA, tB), offsetLength) + } + + Vec3.fromArray(positionVector, controlPoints, i3) + Vec3.fromArray(normalVector, normalVectors, i3) + Vec3.fromArray(torsionVector, binormalVectors, i3) + + Vec3.add(tA, positionVector, verticalVector) + Vec3.negate(tB, torsionVector) + ChunkedArray.add3(vertices, tA[0], tA[1], tA[2]) + ChunkedArray.add3(normals, tB[0], tB[1], tB[2]) + + Vec3.sub(tA, positionVector, verticalVector) + ChunkedArray.add3(vertices, tA[0], tA[1], tA[2]) + ChunkedArray.add3(normals, tB[0], tB[1], tB[2]) + + Vec3.add(tA, positionVector, verticalVector) + Vec3.copy(tB, torsionVector) + ChunkedArray.add3(vertices, tA[0], tA[1], tA[2]) + ChunkedArray.add3(normals, tB[0], tB[1], tB[2]) + + Vec3.sub(tA, positionVector, verticalVector) + ChunkedArray.add3(vertices, tA[0], tA[1], tA[2]) + ChunkedArray.add3(normals, tB[0], tB[1], tB[2]) + } + + for (let i = 0; i < linearSegments; ++i) { + ChunkedArray.add3( + indices, + vertexCount + i * 4, + vertexCount + (i + 1) * 4 + 1, + vertexCount + i * 4 + 1 + ); + ChunkedArray.add3( + indices, + vertexCount + i * 4, + vertexCount + (i + 1) * 4, + vertexCount + (i + 1) * 4 + 1 + ); + + ChunkedArray.add3( + indices, + vertexCount + i * 4 + 2 + 1, + vertexCount + (i + 1) * 4 + 2 + 1, + vertexCount + i * 4 + 2 + ); + ChunkedArray.add3( + indices, + vertexCount + i * 4 + 2, + vertexCount + (i + 1) * 4 + 2 + 1, + vertexCount + (i + 1) * 4 + 2 + ); + } + + const addedVertexCount = (linearSegments + 1) * 4 + for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup) +} \ No newline at end of file diff --git a/src/mol-geo/geometry/mesh/builder/sheet.ts b/src/mol-geo/geometry/mesh/builder/sheet.ts index 8854ca7038f38b5a08bfcc3cd2c9015fd7672cab..89fde7afb160baf8976ee8d880e93e0b25c11133 100644 --- a/src/mol-geo/geometry/mesh/builder/sheet.ts +++ b/src/mol-geo/geometry/mesh/builder/sheet.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> * @author David Sehnal <david.sehnal@gmail.com> @@ -71,7 +71,7 @@ function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLi } /** set arrowHeight = 0 for no arrow */ -export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean) { +export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, arrowHeight: number, startCap: boolean, endCap: boolean) { const { currentGroup, vertices, normals, indices, groups } = state let vertexCount = vertices.elementCount @@ -84,6 +84,9 @@ export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<numb } for (let i = 0; i <= linearSegments; ++i) { + const width = widthValues[i] + const height = heightValues[i] + const actualHeight = arrowHeight === 0 ? height : arrowHeight * (1 - i / linearSegments); const i3 = i * 3 @@ -141,32 +144,55 @@ export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<numb } for (let i = 0; i < linearSegments; ++i) { - for (let j = 0; j < 4; j++) { + // the triangles are arranged such that opposing triangles of the sheet align + // which prevents triangle intersection within tight curves + for (let j = 0; j < 2; j++) { + ChunkedArray.add3( + indices, + vertexCount + i * 8 + 2 * j, // a + vertexCount + (i + 1) * 8 + 2 * j + 1, // c + vertexCount + i * 8 + 2 * j + 1 // b + ); + ChunkedArray.add3( + indices, + vertexCount + i * 8 + 2 * j, // a + vertexCount + (i + 1) * 8 + 2 * j, // d + vertexCount + (i + 1) * 8 + 2 * j + 1 // c + ); + } + for (let j = 2; j < 4; j++) { ChunkedArray.add3( indices, - vertexCount + i * 8 + 2 * j, - vertexCount + (i + 1) * 8 + 2 * j + 1, - vertexCount + i * 8 + 2 * j + 1 + vertexCount + i * 8 + 2 * j, // a + vertexCount + (i + 1) * 8 + 2 * j, // d + vertexCount + i * 8 + 2 * j + 1, // b ); ChunkedArray.add3( indices, - vertexCount + i * 8 + 2 * j, - vertexCount + (i + 1) * 8 + 2 * j, - vertexCount + (i + 1) * 8 + 2 * j + 1 + vertexCount + (i + 1) * 8 + 2 * j, // d + vertexCount + (i + 1) * 8 + 2 * j + 1, // c + vertexCount + i * 8 + 2 * j + 1, // b ); } } if (startCap) { + const width = widthValues[0] + const height = heightValues[0] const h = arrowHeight === 0 ? height : arrowHeight addCap(0, state, controlPoints, normalVectors, binormalVectors, width, h, h) } else if (arrowHeight > 0) { + const width = widthValues[0] + const height = heightValues[0] addCap(0, state, controlPoints, normalVectors, binormalVectors, width, arrowHeight, -height) addCap(0, state, controlPoints, normalVectors, binormalVectors, width, -arrowHeight, height) } if (endCap && arrowHeight === 0) { - addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, height, height) + const width = widthValues[linearSegments] + const height = heightValues[linearSegments] + // use negative height to flip the direction the cap's triangles are facing + addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, -height, -height) } const addedVertexCount = (linearSegments + 1) * 8 + diff --git a/src/mol-geo/geometry/mesh/builder/tube.ts b/src/mol-geo/geometry/mesh/builder/tube.ts index 71f15063250a0dc5fcf228bd1792ebc41f8e41db..401b753c6e018df32faf22fecdaed161947103d9 100644 --- a/src/mol-geo/geometry/mesh/builder/tube.ts +++ b/src/mol-geo/geometry/mesh/builder/tube.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> * @author David Sehnal <david.sehnal@gmail.com> @@ -50,7 +50,15 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe const t = 2 * Math.PI * j / radialSegments; add3AndScale2(surfacePoint, u, v, controlPoint, h * Math.cos(t), w * Math.sin(t)) - add2AndScale2(normalVector, u, v, w * Math.cos(t), h * Math.sin(t)) + if (radialSegments === 2) { + // add2AndScale2(normalVector, u, v, w * Math.cos(t), h * Math.sin(t)) + Vec3.copy(normalVector, v) + console.log(i, t) + Vec3.normalize(normalVector, normalVector) + if (t !== 0 || i % 2 === 0) Vec3.negate(normalVector, normalVector) + } else { + add2AndScale2(normalVector, u, v, w * Math.cos(t), h * Math.sin(t)) + } Vec3.normalize(normalVector, normalVector) ChunkedArray.add3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]); diff --git a/src/mol-geo/primitive/polygon.ts b/src/mol-geo/primitive/polygon.ts index d26aaed98f05b0abc48cdb0817962b522422f77b..a440e579978137daab31ebb1af34dee3e7c06f20 100644 --- a/src/mol-geo/primitive/polygon.ts +++ b/src/mol-geo/primitive/polygon.ts @@ -8,16 +8,18 @@ * Create 3d points for a polygon: * 3 for a triangle, 4 for a rectangle, 5 for a pentagon, 6 for a hexagon... */ -export function polygon(sideCount: number, shift: boolean) { +export function polygon(sideCount: number, shift: boolean, radius = -1) { const points = new Float32Array(sideCount * 3) - const radius = sideCount <= 4 ? Math.sqrt(2) / 2 : 0.6 + const r = radius === -1 + ? (sideCount <= 4 ? Math.sqrt(2) / 2 : 0.6) + : radius const offset = shift ? 1 : 0 for (let i = 0, il = sideCount; i < il; ++i) { const c = (i * 2 + offset) / sideCount * Math.PI - points[i * 3] = Math.cos(c) * radius - points[i * 3 + 1] = Math.sin(c) * radius + points[i * 3] = Math.cos(c) * r + points[i * 3 + 1] = Math.sin(c) * r points[i * 3 + 2] = 0 } return points diff --git a/src/mol-geo/primitive/primitive.ts b/src/mol-geo/primitive/primitive.ts index 55a6922a8b4ffcad1354b4d601e3d4d9f8f232f8..c565dbc7063079f9348cf42c47a86c249fc90050 100644 --- a/src/mol-geo/primitive/primitive.ts +++ b/src/mol-geo/primitive/primitive.ts @@ -1,10 +1,12 @@ /** - * 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> */ -import { Vec3 } from '../../mol-math/linear-algebra'; +import { Vec3, Mat4, Mat3 } from '../../mol-math/linear-algebra'; +import { getNormalMatrix } from '../util'; +import { NumberArray } from '../../mol-util/type-helpers'; export interface Primitive { vertices: ArrayLike<number> @@ -56,4 +58,22 @@ export function PrimitiveBuilder(triangleCount: number): PrimitiveBuilder { }, getPrimitive: () => ({ vertices, normals, indices }) } +} + +const tmpV = Vec3.zero() +const tmpMat3 = Mat3.zero() + +/** Transform primitive in-place */ +export function transformPrimitive(primitive: Primitive, t: Mat4) { + const { vertices, normals } = primitive + const n = getNormalMatrix(tmpMat3, t) + for (let i = 0, il = vertices.length; i < il; i += 3) { + // position + Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t) + Vec3.toArray(tmpV, vertices as NumberArray, i) + // normal + Vec3.transformMat3(tmpV, Vec3.fromArray(tmpV, normals, i), n) + Vec3.toArray(tmpV, normals as NumberArray, i) + } + return primitive } \ No newline at end of file diff --git a/src/mol-geo/primitive/prism.ts b/src/mol-geo/primitive/prism.ts index e7d4af4803bc65da3c53df694ac0a816375335a4..22f12872e9490c7b792a3c0c5efd80e77765d755 100644 --- a/src/mol-geo/primitive/prism.ts +++ b/src/mol-geo/primitive/prism.ts @@ -9,26 +9,39 @@ import { Primitive, PrimitiveBuilder } from './primitive'; import { polygon } from './polygon' import { Cage } from './cage'; -const on = Vec3.create(0, 0, -0.5), op = Vec3.create(0, 0, 0.5) -const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero() +const on = Vec3(), op = Vec3() +const a = Vec3(), b = Vec3(), c = Vec3(), d = Vec3() + +export const DefaultPrismProps = { + height: 1, + topCap: true, + bottomCap: true, +} +export type PrismProps = Partial<typeof DefaultPrismProps> /** * Create a prism with a base of 4 or more points */ -export function Prism(points: ArrayLike<number>): Primitive { +export function Prism(points: ArrayLike<number>, props?: PrismProps): Primitive { const sideCount = points.length / 3 if (sideCount < 4) throw new Error('need at least 4 points to build a prism') + const { height, topCap, bottomCap } = { ...DefaultPrismProps, ...props }; + const count = 4 * sideCount const builder = PrimitiveBuilder(count) + const halfHeight = height * 0.5 + + Vec3.set(on, 0, 0, -halfHeight) + Vec3.set(op, 0, 0, halfHeight) // create sides for (let i = 0; i < sideCount; ++i) { const ni = (i + 1) % sideCount - Vec3.set(a, points[i * 3], points[i * 3 + 1], -0.5) - Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -0.5) - Vec3.set(c, points[ni * 3], points[ni * 3 + 1], 0.5) - Vec3.set(d, points[i * 3], points[i * 3 + 1], 0.5) + Vec3.set(a, points[i * 3], points[i * 3 + 1], -halfHeight) + Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -halfHeight) + Vec3.set(c, points[ni * 3], points[ni * 3 + 1], halfHeight) + Vec3.set(d, points[i * 3], points[i * 3 + 1], halfHeight) builder.add(a, b, c) builder.add(c, d, a) } @@ -36,12 +49,16 @@ export function Prism(points: ArrayLike<number>): Primitive { // create bases for (let i = 0; i < sideCount; ++i) { const ni = (i + 1) % sideCount - Vec3.set(a, points[i * 3], points[i * 3 + 1], -0.5) - Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -0.5) - builder.add(on, b, a) - Vec3.set(a, points[i * 3], points[i * 3 + 1], 0.5) - Vec3.set(b, points[ni * 3], points[ni * 3 + 1], 0.5) - builder.add(a, b, op) + if (topCap) { + Vec3.set(a, points[i * 3], points[i * 3 + 1], -halfHeight) + Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -halfHeight) + builder.add(on, b, a) + } + if (bottomCap) { + Vec3.set(a, points[i * 3], points[i * 3 + 1], halfHeight) + Vec3.set(b, points[ni * 3], points[ni * 3 + 1], halfHeight) + builder.add(a, b, op) + } } return builder.getPrimitive() @@ -70,20 +87,21 @@ export function HexagonalPrism() { /** * Create a prism cage */ -export function PrismCage(points: ArrayLike<number>): Cage { +export function PrismCage(points: ArrayLike<number>, height = 1): Cage { const sideCount = points.length / 3 - // const count = 4 * sideCount const vertices: number[] = [] const edges: number[] = [] + const halfHeight = height * 0.5 + let offset = 0 // vertices and side edges for (let i = 0; i < sideCount; ++i) { vertices.push( - points[i * 3], points[i * 3 + 1], -0.5, - points[i * 3], points[i * 3 + 1], 0.5 + points[i * 3], points[i * 3 + 1], -halfHeight, + points[i * 3], points[i * 3 + 1], halfHeight ) edges.push(offset, offset + 1) offset += 2 diff --git a/src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts b/src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts index 88b7fa39bd529b05b0118ee68625917756db8751..9cfdd869e13cf2bf3ee7617f9fda80910fb22e8a 100644 --- a/src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts +++ b/src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts @@ -31,7 +31,7 @@ export const AssemblySymmetryAxesParams = { sizeFactor: PD.Numeric(0.4, { min: 0, max: 3, step: 0.01 }), ...ComplexMeshParams, - radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }), } export type AssemblySymmetryAxesParams = typeof AssemblySymmetryAxesParams diff --git a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts index 55c8dfa68e7329e6d071048260f871e9ba58bfa1..c7b4b2d9c21b6c6282890d83e485a6051785ed7a 100644 --- a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts +++ b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts @@ -36,7 +36,7 @@ const box = Box() export const NucleotideBlockMeshParams = { sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }), - radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), } export const DefaultNucleotideBlockMeshProps = PD.getDefaultValues(NucleotideBlockMeshParams) export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps diff --git a/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts b/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts index 871fe5364818ef593b93ed25e1634f56a7545377..c205c3f5af71e495c8a23a30ac560dbd134b5d73 100644 --- a/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts +++ b/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts @@ -35,7 +35,7 @@ const normal = Vec3.zero() export const NucleotideRingMeshParams = { sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }), - radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }), } export const DefaultNucleotideRingMeshProps = PD.getDefaultValues(NucleotideRingMeshParams) diff --git a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts index 830a0266b21679af9fb46531e13b139fdc07a69b..1993c714b11b213f27773e5943f716d7fce99dcb 100644 --- a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts +++ b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts @@ -21,7 +21,7 @@ import { VisualUpdateState } from '../../util'; export const PolymerBackboneCylinderParams = { sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }), - radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), } export const DefaultPolymerBackboneCylinderProps = PD.getDefaultValues(PolymerBackboneCylinderParams) export type PolymerBackboneCylinderProps = typeof DefaultPolymerBackboneCylinderProps diff --git a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts index 957fda462efc6decef15842ba676ba6c7ab255cc..5c662460e7a27b0f9ca99955253ccf1f9f41e38b 100644 --- a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts +++ b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts @@ -23,7 +23,7 @@ const segmentCount = 10 export const PolymerGapCylinderParams = { sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }), - radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), } export const DefaultPolymerGapCylinderProps = PD.getDefaultValues(PolymerGapCylinderParams) export type PolymerGapCylinderProps = typeof DefaultPolymerGapCylinderProps diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts index 4c94290bbc7b2f920e05ce61bc1e47e6600f8fa4..25a0aacf37310b7468b7c702dbb6ece1a97cd2f7 100644 --- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-repr/structure/visual/polymer-trace-mesh.ts @@ -17,13 +17,14 @@ import { addTube } from '../../../mol-geo/geometry/mesh/builder/tube'; import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual, StructureGroup } from '../units-visual'; import { VisualUpdateState } from '../../util'; import { ComputedSecondaryStructure } from '../../../mol-model-props/computed/secondary-structure'; +import { addRibbon } from '../../../mol-geo/geometry/mesh/builder/ribbon'; export const PolymerTraceMeshParams = { 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 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), aspectRatio: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }), - arrowFactor: PD.Numeric(1.5, { min: 0.1, max: 5, step: 0.1 }), + arrowFactor: PD.Numeric(1.5, { min: 0, max: 3, step: 0.1 }), } export const DefaultPolymerTraceMeshProps = PD.getDefaultValues(PolymerTraceMeshParams) export type PolymerTraceMeshProps = typeof DefaultPolymerTraceMeshProps @@ -67,9 +68,18 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc } if (isSheet) { + const h0 = w0 * aspectRatio const h1 = w1 * aspectRatio + const h2 = w2 * aspectRatio const arrowHeight = v.secStrucLast ? h1 * arrowFactor : 0 - addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, w1, h1, arrowHeight, v.secStrucFirst, v.secStrucLast) + + interpolateSizes(state, w0, w1, w2, h0, h1, h2, shift) + + if (radialSegments === 2) { + addRibbon(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, widthValues, heightValues, arrowHeight) + } else { + addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, widthValues, heightValues, arrowHeight, v.secStrucFirst, v.secStrucLast) + } } else { let h0: number, h1: number, h2: number if (isHelix && !v.isCoarseBackbone) { @@ -78,18 +88,32 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc h2 = w2 * aspectRatio } else if (isNucleicType && !v.isCoarseBackbone) { h0 = w0 * aspectRatio; - [w0, h0] = [h0, w0] h1 = w1 * aspectRatio; - [w1, h1] = [h1, w1] h2 = w2 * aspectRatio; - [w2, h2] = [h2, w2] + [w0, h0] = [h0, w0]; + [w1, h1] = [h1, w1]; + [w2, h2] = [h2, w2]; } else { h0 = w0 h1 = w1 h2 = w2 } + interpolateSizes(state, w0, w1, w2, h0, h1, h2, shift) - addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, widthValues, heightValues, 1, v.secStrucFirst || v.coarseBackboneFirst, v.secStrucLast || v.coarseBackboneLast) + + if (radialSegments === 2) { + if (isNucleicType && !v.isCoarseBackbone) { + // TODO find a cleaner way to swap normal and binormal for nucleic types + for (let i = 0, il = binormalVectors.length; i < il; i++) binormalVectors[i] *= -1 + addRibbon(builderState, curvePoints, binormalVectors, normalVectors, linearSegments, heightValues, widthValues, 0) + } else { + addRibbon(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, widthValues, heightValues, 0) + } + } else if (radialSegments === 4) { + addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, widthValues, heightValues, 0, v.secStrucFirst || v.coarseBackboneFirst, v.secStrucLast || v.coarseBackboneLast) + } else { + addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, widthValues, heightValues, 1, v.secStrucFirst || v.coarseBackboneFirst, v.secStrucLast || v.coarseBackboneLast) + } } ++i diff --git a/src/mol-repr/structure/visual/polymer-tube-mesh.ts b/src/mol-repr/structure/visual/polymer-tube-mesh.ts index dd9184143cfe6e91ba2d5cc3b0803883e9c3155c..b1025682576f17d664322e2d1d138bdd530c716f 100644 --- a/src/mol-repr/structure/visual/polymer-tube-mesh.ts +++ b/src/mol-repr/structure/visual/polymer-tube-mesh.ts @@ -15,11 +15,13 @@ import { isNucleic } from '../../../mol-model/structure/model/types'; import { addTube } from '../../../mol-geo/geometry/mesh/builder/tube'; import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../units-visual'; import { VisualUpdateState } from '../../util'; +import { addSheet } from '../../../mol-geo/geometry/mesh/builder/sheet'; +import { addRibbon } from '../../../mol-geo/geometry/mesh/builder/ribbon'; 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 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), } export const DefaultPolymerTubeMeshProps = PD.getDefaultValues(PolymerTubeMeshParams) export type PolymerTubeMeshProps = typeof DefaultPolymerTubeMeshProps @@ -55,7 +57,14 @@ function createPolymerTubeMesh(ctx: VisualContext, unit: Unit, structure: Struct 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) + + if (radialSegments === 2) { + addRibbon(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, widthValues, heightValues, 0) + } else if (radialSegments === 4) { + addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, widthValues, heightValues, 0, v.secStrucFirst || v.coarseBackboneFirst, v.secStrucLast || v.coarseBackboneLast) + } else { + addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, widthValues, heightValues, 1, v.first, v.last) + } ++i } diff --git a/src/mol-repr/structure/visual/util/link.ts b/src/mol-repr/structure/visual/util/link.ts index 69079b62e9ab1b83eb18c8cdc7efb6c15482af37..2f913a312573fffdc252261dc1e87db96f48f30a 100644 --- a/src/mol-repr/structure/visual/util/link.ts +++ b/src/mol-repr/structure/visual/util/link.ts @@ -18,7 +18,7 @@ import { VisualContext } from '../../../../mol-repr/visual'; export const LinkCylinderParams = { linkScale: PD.Numeric(0.4, { min: 0, max: 1, step: 0.1 }), linkSpacing: PD.Numeric(1, { min: 0, max: 2, step: 0.01 }), - radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }), + radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), } export const DefaultLinkCylinderProps = PD.getDefaultValues(LinkCylinderParams) export type LinkCylinderProps = typeof DefaultLinkCylinderProps