diff --git a/src/apps/canvas/index.ts b/src/apps/canvas/index.ts index 6a7f58e00a81e4872cb1d455c73a109ae4fc257e..5166630cac8f8059edde039026cabcaaf9bade2c 100644 --- a/src/apps/canvas/index.ts +++ b/src/apps/canvas/index.ts @@ -22,6 +22,8 @@ import { ShapeRepresentation } from 'mol-geo/representation/shape'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Shape } from 'mol-model/shape'; import { Color } from 'mol-util/color'; +import { addSphere } from 'mol-geo/mesh/builder/sphere'; +import { Box } from 'mol-geo/primitive/box'; const container = document.getElementById('container') if (!container) throw new Error('Can not find element with id "container".') @@ -121,7 +123,7 @@ async function init() { meshBuilder.setGroup(0) colors[0] = Color(0xFF2233) labels[0] = 'red sphere' - meshBuilder.addSphere(Vec3.create(0, 0, 0), 4, 2) + addSphere(meshBuilder, Vec3.create(0, 0, 0), 4, 2) // green cube meshBuilder.setGroup(1) colors[1] = Color(0x2233FF) @@ -129,7 +131,7 @@ async function init() { const t = Mat4.identity() Mat4.fromTranslation(t, Vec3.create(10, 0, 0)) Mat4.scale(t, t, Vec3.create(3, 3, 3)) - meshBuilder.addBox(t) + meshBuilder.add(t, Box()) const mesh = meshBuilder.getMesh() // const mesh = getObjFromUrl('mesh.obj') diff --git a/src/mol-geo/mesh/builder/cylinder.ts b/src/mol-geo/mesh/builder/cylinder.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4538c1c81a0a802392a622ece8199e2f997e77e --- /dev/null +++ b/src/mol-geo/mesh/builder/cylinder.ts @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2018 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 { Cylinder, CylinderProps } from '../../primitive/cylinder'; + +const cylinderMap = new Map<string, Primitive>() +const up = Vec3.create(0, 1, 0) + +const tmpCylinderDir = Vec3.zero() +const tmpCylinderMatDir = Vec3.zero() +const tmpCylinderCenter = Vec3.zero() +const tmpCylinderMat = Mat4.zero() +const tmpCylinderStart = Vec3.zero() +const tmpUp = Vec3.zero() + +function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number) { + Vec3.setMagnitude(tmpCylinderMatDir, dir, length / 2) + Vec3.add(tmpCylinderCenter, start, tmpCylinderMatDir) + // ensure the direction used to create the rotation is always pointing in the same + // direction so the triangles of adjacent cylinder will line up + Vec3.copy(tmpUp, up) + if (Vec3.dot(tmpCylinderMatDir, tmpUp) < 0) Vec3.scale(tmpUp, tmpUp, -1) + Vec3.makeRotation(m, tmpUp, tmpCylinderMatDir) + return Mat4.setTranslation(m, tmpCylinderCenter) +} + +function getCylinder(props: CylinderProps) { + const key = JSON.stringify(props) + let cylinder = cylinderMap.get(key) + if (cylinder === undefined) { + cylinder = Cylinder(props) + cylinderMap.set(key, cylinder) + } + return cylinder +} + +export function addCylinder(builder: MeshBuilder, start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps) { + const d = Vec3.distance(start, end) * lengthScale + props.height = d + Vec3.sub(tmpCylinderDir, end, start) + setCylinderMat(tmpCylinderMat, start, tmpCylinderDir, d) + builder.add(tmpCylinderMat, getCylinder(props)) +} + +export function addDoubleCylinder(builder: MeshBuilder, start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps) { + const d = Vec3.distance(start, end) * lengthScale + props.height = d + const cylinder = getCylinder(props) + Vec3.sub(tmpCylinderDir, end, start) + // positivly shifted cylinder + Vec3.add(tmpCylinderStart, start, shift) + setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d) + builder.add(tmpCylinderMat, cylinder) + // negativly shifted cylinder + Vec3.sub(tmpCylinderStart, start, shift) + setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d) + builder.add(tmpCylinderMat, cylinder) +} + +export function addFixedCountDashedCylinder(builder: MeshBuilder, start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps) { + const s = Math.floor(segmentCount / 2) + const step = 1 / segmentCount + + // automatically adjust length so links/bonds that are rendered as two half cylinders + // have evenly spaced dashed cylinders + if (lengthScale < 1) { + const bias = lengthScale / 2 / segmentCount + lengthScale += segmentCount % 2 === 1 ? bias : -bias + } + + const d = Vec3.distance(start, end) * lengthScale + props.height = d * step + const cylinder = getCylinder(props) + Vec3.sub(tmpCylinderDir, end, start) + + for (let j = 0; j < s; ++j) { + const f = step * (j * 2 + 1) + Vec3.setMagnitude(tmpCylinderDir, tmpCylinderDir, d * f) + Vec3.add(tmpCylinderStart, start, tmpCylinderDir) + setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d * step) + builder.add(tmpCylinderMat, cylinder) + } +} \ No newline at end of file diff --git a/src/mol-geo/mesh/sheet.ts b/src/mol-geo/mesh/builder/sheet.ts similarity index 92% rename from src/mol-geo/mesh/sheet.ts rename to src/mol-geo/mesh/builder/sheet.ts index ef60f0a4588344b83e1b577cd3748dd13e3e26ae..c0d7a473318356bcf891dcb8088d45411065e91a 100644 --- a/src/mol-geo/mesh/sheet.ts +++ b/src/mol-geo/mesh/builder/sheet.ts @@ -7,7 +7,7 @@ import { Vec3 } from 'mol-math/linear-algebra'; import { ChunkedArray } from 'mol-data/util'; -import { MeshBuilderState } from './mesh-builder'; +import { MeshBuilder } from '../mesh-builder'; const tA = Vec3.zero() const tB = Vec3.zero() @@ -25,8 +25,8 @@ const p2 = Vec3.zero() const p3 = Vec3.zero() const p4 = Vec3.zero() -export function addSheet(controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean, state: MeshBuilderState) { - const { vertices, normals, indices } = state +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 let vertexCount = vertices.elementCount let offsetLength = 0 @@ -173,5 +173,6 @@ export function addSheet(controlPoints: ArrayLike<number>, normalVectors: ArrayL ChunkedArray.add3(indices, vertexCount, vertexCount + 3, vertexCount + 2); } - return (linearSegments + 1) * 8 + (startCap ? 4 : 0) + (endCap && arrowHeight === 0 ? 4 : 0) + const addedVertexCount = (linearSegments + 1) * 8 + (startCap ? 4 : 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-geo/mesh/builder/sphere.ts b/src/mol-geo/mesh/builder/sphere.ts new file mode 100644 index 0000000000000000000000000000000000000000..d9196c3a2840223e833369e133c79c237b3e4c63 --- /dev/null +++ b/src/mol-geo/mesh/builder/sphere.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2018 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 { Sphere } from '../../primitive/sphere'; + +const sphereMap = new Map<number, Primitive>() +const tmpSphereMat = Mat4.identity() + +function setSphereMat(m: Mat4, center: Vec3, radius: number) { + return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius) +} + +function getSphere(detail: number) { + let sphere = sphereMap.get(detail) + if (sphere === undefined) { + sphere = Sphere(detail) + sphereMap.set(detail, sphere) + } + return sphere +} + +export function addSphere(builder: MeshBuilder, center: Vec3, radius: number, detail: number) { + builder.add(setSphereMat(tmpSphereMat, center, radius), getSphere(detail)) +} \ No newline at end of file diff --git a/src/mol-geo/mesh/tube.ts b/src/mol-geo/mesh/builder/tube.ts similarity index 87% rename from src/mol-geo/mesh/tube.ts rename to src/mol-geo/mesh/builder/tube.ts index 333b7cc8718b435ffb60a6982aefa478dbc790d1..944429468a81cbf653b99bc69295b8c5ca012c9a 100644 --- a/src/mol-geo/mesh/tube.ts +++ b/src/mol-geo/mesh/builder/tube.ts @@ -7,7 +7,7 @@ import { Vec3 } from 'mol-math/linear-algebra'; import { ChunkedArray } from 'mol-data/util'; -import { MeshBuilderState } from '../mesh/mesh-builder'; +import { MeshBuilder } from '../mesh-builder'; const normalVector = Vec3.zero() const binormalVector = Vec3.zero() @@ -18,8 +18,8 @@ const b = Vec3.zero() const u = Vec3.zero() const v = Vec3.zero() -export function addTube(controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean, state: MeshBuilderState) { - const { vertices, normals, indices } = state +export function addTube(builder: MeshBuilder, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean) { + const { currentGroup, vertices, normals, indices, groups } = builder.state let vertexCount = vertices.elementCount const di = 1 / linearSegments @@ -127,7 +127,7 @@ export function addTube(controlPoints: ArrayLike<number>, normalVectors: ArrayLi vertexCount = vertices.elementCount for (let i = 0; i < radialSegments; ++i) { - const t = 2 * Math.PI * i / radialSegments; + const t = 2 * Math.PI * i / radialSegments Vec3.copy(a, u) Vec3.copy(b, v) @@ -150,5 +150,6 @@ export function addTube(controlPoints: ArrayLike<number>, normalVectors: ArrayLi } } - return (linearSegments + 1) * radialSegments + (startCap ? radialSegments + 1 : 0) + (endCap ? radialSegments + 1 : 0) + const addedVertexCount = (linearSegments + 1) * radialSegments + (startCap ? radialSegments + 1 : 0) + (endCap ? radialSegments + 1 : 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-geo/mesh/mesh-builder.ts b/src/mol-geo/mesh/mesh-builder.ts index be66b801a7e177ae432496ee650c8a6fe90eb917..d7d264bfc777b0a5548aa229e43b620862c6af95 100644 --- a/src/mol-geo/mesh/mesh-builder.ts +++ b/src/mol-geo/mesh/mesh-builder.ts @@ -7,119 +7,39 @@ import { ValueCell } from 'mol-util/value-cell' import { Vec3, Mat4, Mat3 } from 'mol-math/linear-algebra'; import { ChunkedArray } from 'mol-data/util'; - -import { Plane } from '../primitive/plane'; -import { Cylinder, CylinderProps } from '../primitive/cylinder'; -import { Sphere } from '../primitive/sphere'; import { Mesh } from './mesh'; import { getNormalMatrix } from '../util'; -import { addSheet } from './sheet'; -import { addTube } from './tube'; -import { StarProps, Star } from '../primitive/star'; -import { Octahedron, PerforatedOctahedron } from '../primitive/octahedron'; import { Primitive } from '../primitive/primitive'; -import { DiamondPrism, PentagonalPrism, HexagonalPrism } from '../primitive/prism'; -import { OctagonalPyramide, PerforatedOctagonalPyramid } from '../primitive/pyramid'; -import { PerforatedBox, Box } from '../primitive/box'; -import { Wedge } from '../primitive/wedge'; export interface MeshBuilderState { - vertices: ChunkedArray<number, 3> - normals: ChunkedArray<number, 3> - indices: ChunkedArray<number, 3> + readonly currentGroup: number + readonly vertices: ChunkedArray<number, 3> + readonly normals: ChunkedArray<number, 3> + readonly indices: ChunkedArray<number, 3> + readonly groups: ChunkedArray<number, 1> } export interface MeshBuilder { - add(t: Mat4, _vertices: ArrayLike<number>, _normals: ArrayLike<number>, _indices?: ArrayLike<number>): void - addPrimitive(t: Mat4, primitive: Primitive): void, - - addBox(t: Mat4): void - addPerforatedBox(t: Mat4): void - addPlane(t: Mat4): void - addWedge(t: Mat4): void - addDiamondPrism(t: Mat4): void - addPentagonalPrism(t: Mat4): void - addHexagonalPrism(t: Mat4): void - addOctagonalPyramid(t: Mat4): void - addPerforatedOctagonalPyramid(t: Mat4): void - addStar(t: Mat4, props?: StarProps): void - addOctahedron(t: Mat4): void - addPerforatedOctahedron(t: Mat4): void - - addCylinder(start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps): void - addDoubleCylinder(start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps): void - addFixedCountDashedCylinder(start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps): void - addSphere(center: Vec3, radius: number, detail: number): void - - addTube(centers: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean): void - addSheet(centers: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean): void - + state: MeshBuilderState + add(t: Mat4, primitive: Primitive): void setGroup(id: number): void getMesh(): Mesh } -const cylinderMap = new Map<string, Primitive>() -const sphereMap = new Map<number, Primitive>() - -const up = Vec3.create(0, 1, 0) const tmpV = Vec3.zero() const tmpMat3 = Mat3.zero() -const tmpCylinderDir = Vec3.zero() -const tmpCylinderMatDir = Vec3.zero() -const tmpCylinderCenter = Vec3.zero() -const tmpCylinderMat = Mat4.zero() -const tmpCylinderStart = Vec3.zero() -const tmpUp = Vec3.zero() - -function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number) { - Vec3.setMagnitude(tmpCylinderMatDir, dir, length / 2) - Vec3.add(tmpCylinderCenter, start, tmpCylinderMatDir) - // ensure the direction used to create the rotation is always pointing in the same - // direction so the triangles of adjacent cylinder will line up - Vec3.copy(tmpUp, up) - if (Vec3.dot(tmpCylinderMatDir, tmpUp) < 0) Vec3.scale(tmpUp, tmpUp, -1) - Vec3.makeRotation(m, tmpUp, tmpCylinderMatDir) - return Mat4.setTranslation(m, tmpCylinderCenter) -} - -function getCylinder(props: CylinderProps) { - const key = JSON.stringify(props) - let cylinder = cylinderMap.get(key) - if (cylinder === undefined) { - cylinder = Cylinder(props) - cylinderMap.set(key, cylinder) - } - return cylinder -} - -const tmpSphereMat = Mat4.identity() - -function setSphereMat(m: Mat4, center: Vec3, radius: number) { - return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius) -} - -function getSphere(detail: number) { - let sphere = sphereMap.get(detail) - if (sphere === undefined) { - sphere = Sphere(detail) - sphereMap.set(detail, sphere) - } - return sphere -} - export namespace MeshBuilder { export function create(initialCount = 2048, chunkSize = 1024, mesh?: Mesh): MeshBuilder { const vertices = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.vertexBuffer.ref.value : initialCount); const normals = ChunkedArray.create(Float32Array, 3, chunkSize, mesh ? mesh.normalBuffer.ref.value : initialCount); const indices = ChunkedArray.create(Uint32Array, 3, chunkSize * 3, mesh ? mesh.indexBuffer.ref.value : initialCount * 3); - const state: MeshBuilderState = { vertices, normals, indices }; - const groups = ChunkedArray.create(Float32Array, 1, chunkSize, mesh ? mesh.groupBuffer.ref.value : initialCount); let currentGroup = -1 - function add(t: Mat4, va: ArrayLike<number>, na: ArrayLike<number>, ia: ArrayLike<number>) { + function add(t: Mat4, primitive: Primitive) { + const { vertices: va, normals: na, indices: ia } = primitive const offset = vertices.elementCount const n = getNormalMatrix(tmpMat3, t) for (let i = 0, il = va.length; i < il; i += 3) { @@ -137,89 +57,10 @@ export namespace MeshBuilder { } } - function addPrimitive(t: Mat4, primitive: Primitive) { - const { vertices, normals, indices } = primitive - add(t, vertices, normals, indices) - } - return { + state: { get currentGroup() { return currentGroup }, vertices, normals, indices, groups }, add, - addPrimitive, - - addBox: (t: Mat4) => addPrimitive(t, Box()), - addPerforatedBox: (t: Mat4) => addPrimitive(t, PerforatedBox()), - addPlane: (t: Mat4) => addPrimitive(t, Plane()), - addWedge: (t: Mat4) => addPrimitive(t, Wedge()), - addDiamondPrism: (t: Mat4) => addPrimitive(t, DiamondPrism()), - addPentagonalPrism: (t: Mat4) => addPrimitive(t, PentagonalPrism()), - addHexagonalPrism: (t: Mat4) => addPrimitive(t, HexagonalPrism()), - addOctagonalPyramid: (t: Mat4) => addPrimitive(t, OctagonalPyramide()), - addPerforatedOctagonalPyramid: (t: Mat4) => addPrimitive(t, PerforatedOctagonalPyramid()), - addStar: (t: Mat4, props?: StarProps) => addPrimitive(t, Star(props)), - addOctahedron: (t: Mat4) => addPrimitive(t, Octahedron()), - addPerforatedOctahedron: (t: Mat4) => addPrimitive(t, PerforatedOctahedron()), - - addCylinder: (start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps) => { - const d = Vec3.distance(start, end) * lengthScale - props.height = d - Vec3.sub(tmpCylinderDir, end, start) - setCylinderMat(tmpCylinderMat, start, tmpCylinderDir, d) - addPrimitive(tmpCylinderMat, getCylinder(props)) - }, - addDoubleCylinder: (start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps) => { - const d = Vec3.distance(start, end) * lengthScale - props.height = d - const cylinder = getCylinder(props) - Vec3.sub(tmpCylinderDir, end, start) - // positivly shifted cylinder - Vec3.add(tmpCylinderStart, start, shift) - setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d) - addPrimitive(tmpCylinderMat, cylinder) - // negativly shifted cylinder - Vec3.sub(tmpCylinderStart, start, shift) - setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d) - addPrimitive(tmpCylinderMat, cylinder) - }, - addFixedCountDashedCylinder: (start: Vec3, end: Vec3, lengthScale: number, segmentCount: number, props: CylinderProps) => { - const s = Math.floor(segmentCount / 2) - const step = 1 / segmentCount - - // automatically adjust length so links/bonds that are rendered as two half cylinders - // have evenly spaced dashed cylinders - if (lengthScale < 1) { - const bias = lengthScale / 2 / segmentCount - lengthScale += segmentCount % 2 === 1 ? bias : -bias - } - - const d = Vec3.distance(start, end) * lengthScale - props.height = d * step - const cylinder = getCylinder(props) - Vec3.sub(tmpCylinderDir, end, start) - - for (let j = 0; j < s; ++j) { - const f = step * (j * 2 + 1) - Vec3.setMagnitude(tmpCylinderDir, tmpCylinderDir, d * f) - Vec3.add(tmpCylinderStart, start, tmpCylinderDir) - setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d * step) - addPrimitive(tmpCylinderMat, cylinder) - } - }, - addSphere: (center: Vec3, radius: number, detail: number) => { - addPrimitive(setSphereMat(tmpSphereMat, center, radius), getSphere(detail)) - }, - - addTube: (centers: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean) => { - const addedVertexCount = addTube(centers, normals, binormals, linearSegments, radialSegments, width, height, waveFactor, startCap, endCap, state) - for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup); - }, - addSheet: (controls: ArrayLike<number>, normals: ArrayLike<number>, binormals: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean) => { - const addedVertexCount = addSheet(controls, normals, binormals, linearSegments, width, height, arrowHeight, startCap, endCap, state) - for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup); - }, - - setGroup: (group: number) => { - currentGroup = group - }, + setGroup: (group: number) => { currentGroup = group }, getMesh: () => { const vb = ChunkedArray.compact(vertices, true) as Float32Array const ib = ChunkedArray.compact(indices, true) as Uint32Array diff --git a/src/mol-geo/primitive/pyramid.ts b/src/mol-geo/primitive/pyramid.ts index 95c80197eeea63751a76c87fc6749d56d8f9e710..c00b84320cd714f967c8a89519fef216f5b95487 100644 --- a/src/mol-geo/primitive/pyramid.ts +++ b/src/mol-geo/primitive/pyramid.ts @@ -12,9 +12,9 @@ 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() /** - * Create a pyramide with a poligonal base + * Create a pyramid with a poligonal base */ -export function Pyramide(points: ArrayLike<number>): Primitive { +export function Pyramid(points: ArrayLike<number>): Primitive { const sideCount = points.length / 2 const baseCount = sideCount === 3 ? 1 : sideCount === 4 ? 2 : sideCount const count = 2 * baseCount + 2 * sideCount @@ -53,17 +53,17 @@ export function Pyramide(points: ArrayLike<number>): Primitive { return builder.getPrimitive() } -let octagonalPyramide: Primitive -export function OctagonalPyramide() { - if (!octagonalPyramide) octagonalPyramide = Pyramide(polygon(8, true)) - return octagonalPyramide +let octagonalPyramid: Primitive +export function OctagonalPyramid() { + if (!octagonalPyramid) octagonalPyramid = Pyramid(polygon(8, true)) + return octagonalPyramid } // -let perforatedOctagonalPyramide: Primitive +let perforatedOctagonalPyramid: Primitive export function PerforatedOctagonalPyramid() { - if (!perforatedOctagonalPyramide) { + if (!perforatedOctagonalPyramid) { const points = polygon(8, true) const vertices = new Float32Array(8 * 3 + 6) for (let i = 0; i < 8; ++i) { @@ -81,7 +81,7 @@ export function PerforatedOctagonalPyramid() { 0, 1, 8, 1, 2, 8, 4, 5, 8, 5, 6, 8, 2, 3, 9, 3, 4, 9, 6, 7, 9, 7, 0, 9 ]; - perforatedOctagonalPyramide = createPrimitive(vertices, indices) + perforatedOctagonalPyramid = createPrimitive(vertices, indices) } - return perforatedOctagonalPyramide + return perforatedOctagonalPyramid } \ No newline at end of file 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 7832c86b0dce966cec6b87d13d634e4f3e273074..6546a2b2ac14106953307cabfb2655f3db9b7311 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts @@ -17,6 +17,12 @@ import { LocationIterator } from '../../../util/location-iterator'; import { OrderedSet, Interval } from 'mol-data/int'; import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual'; import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { addSphere } from '../../../mesh/builder/sphere'; +import { Box, PerforatedBox } from '../../../primitive/box'; +import { OctagonalPyramid, PerforatedOctagonalPyramid } from '../../../primitive/pyramid'; +import { Star } from '../../../primitive/star'; +import { Octahedron, PerforatedOctahedron } from '../../../primitive/octahedron'; +import { DiamondPrism, PentagonalPrism, HexagonalPrism } from '../../../primitive/prism'; const t = Mat4.identity() const sVec = Vec3.zero() @@ -25,6 +31,17 @@ const pd = Vec3.zero() const sideFactor = 1.75 * 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4) const radiusFactor = 1.75 +const box = Box() +const perforatedBox = PerforatedBox() +const octagonalPyramid = OctagonalPyramid() +const perforatedOctagonalPyramid = PerforatedOctagonalPyramid() +const star = Star({ outerRadius: 1, innerRadius: 0.5, thickness: 0.5, pointCount: 5 }) +const octahedron = Octahedron() +const perforatedOctahedron = PerforatedOctahedron() +const diamondPrism = DiamondPrism() +const pentagonalPrism = PentagonalPrism() +const hexagonalPrism = HexagonalPrism() + async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, props: CarbohydrateSymbolProps, mesh?: Mesh) { const builder = MeshBuilder.create(256, 128, mesh) @@ -53,67 +70,68 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru switch (shapeType) { case SaccharideShapes.FilledSphere: - builder.addSphere(center, radius, detail) + addSphere(builder, center, radius, detail) break; case SaccharideShapes.FilledCube: Mat4.scaleUniformly(t, t, side) - builder.addBox(t) + builder.add(t, box) break; case SaccharideShapes.CrossedCube: Mat4.scaleUniformly(t, t, side) - builder.addPerforatedBox(t) + builder.add(t, perforatedBox) Mat4.mul(t, t, Mat4.rotZ90X180) builder.setGroup(i * 2 + 1) - builder.addPerforatedBox(t) + builder.add(t, perforatedBox) break; case SaccharideShapes.FilledCone: Mat4.scaleUniformly(t, t, side * 1.2) - builder.addOctagonalPyramid(t) + builder.add(t, octagonalPyramid) break case SaccharideShapes.DevidedCone: Mat4.scaleUniformly(t, t, side * 1.2) - builder.addPerforatedOctagonalPyramid(t) + builder.add(t, perforatedOctagonalPyramid) Mat4.mul(t, t, Mat4.rotZ90) builder.setGroup(i * 2 + 1) - builder.addPerforatedOctagonalPyramid(t) + builder.add(t, perforatedOctagonalPyramid) break case SaccharideShapes.FlatBox: Mat4.mul(t, t, Mat4.rotZY90) Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2)) - builder.addBox(t) + builder.add(t, box) break case SaccharideShapes.FilledStar: + Mat4.scaleUniformly(t, t, side) Mat4.mul(t, t, Mat4.rotZY90) - builder.addStar(t, { outerRadius: side, innerRadius: side / 2, thickness: side / 2, pointCount: 5 }) + builder.add(t, star) break case SaccharideShapes.FilledDiamond: Mat4.mul(t, t, Mat4.rotZY90) Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) - builder.addOctahedron(t) + builder.add(t, octahedron) break case SaccharideShapes.DividedDiamond: Mat4.mul(t, t, Mat4.rotZY90) Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) - builder.addPerforatedOctahedron(t) + builder.add(t, perforatedOctahedron) Mat4.mul(t, t, Mat4.rotY90) builder.setGroup(i * 2 + 1) - builder.addPerforatedOctahedron(t) + builder.add(t, perforatedOctahedron) break case SaccharideShapes.FlatDiamond: Mat4.mul(t, t, Mat4.rotZY90) Mat4.scale(t, t, Vec3.set(sVec, side, side / 2, side / 2)) - builder.addDiamondPrism(t) + builder.add(t, diamondPrism) break case SaccharideShapes.Pentagon: Mat4.mul(t, t, Mat4.rotZY90) Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2)) - builder.addPentagonalPrism(t) + builder.add(t, pentagonalPrism) break case SaccharideShapes.FlatHexagon: default: Mat4.mul(t, t, Mat4.rotZYZ90) Mat4.scale(t, t, Vec3.set(sVec, side / 1.5, side , side / 2)) - builder.addHexagonalPrism(t) + builder.add(t, hexagonalPrism) break } } diff --git a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts index 29659c6d6a5a9070015e74f2a0cfb89be99c1346..0ace3c5262d2d6ad41fe037b03a982400857c89f 100644 --- a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts +++ b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts @@ -15,6 +15,8 @@ import { Segmentation, SortedArray } from 'mol-data/int'; import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types'; import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util'; import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual'; +import { addCylinder } from '../../../mesh/builder/cylinder'; +import { Box } from '../../../primitive/box'; const p1 = Vec3.zero() const p2 = Vec3.zero() @@ -28,6 +30,7 @@ const vC = Vec3.zero() const center = Vec3.zero() const t = Mat4.identity() const sVec = Vec3.zero() +const box = Box() // TODO define props, should be scalable async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, props: {}, mesh?: Mesh) { @@ -89,8 +92,8 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, props: Mat4.scale(t, t, Vec3.set(sVec, width, depth, height)) Mat4.setTranslation(t, center) builder.setGroup(SortedArray.findPredecessorIndex(elements, idx6)) - builder.addBox(t) - builder.addCylinder(p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 }) + builder.add(t, box) + addCylinder(builder, p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 }) } } diff --git a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts index ed141df814c330834c3ef894ee5388e4ed1e579a..e3b1b00ae8c4923e157908bd9ecd404e10285b79 100644 --- a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts @@ -16,6 +16,7 @@ import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual'; import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; import { CylinderProps } from '../../../primitive/cylinder'; import { OrderedSet } from 'mol-data/int'; +import { addCylinder } from '../../../mesh/builder/cylinder'; export interface PolymerBackboneCylinderProps { sizeTheme: SizeThemeProps @@ -47,11 +48,11 @@ async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerA) builder.setGroup(OrderedSet.findPredecessorIndex(elements, centerA.element)) - builder.addCylinder(pA, pB, 0.5, cylinderProps) + addCylinder(builder, pA, pB, 0.5, cylinderProps) cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerB) builder.setGroup(OrderedSet.findPredecessorIndex(elements, centerB.element)) - builder.addCylinder(pB, pA, 0.5, cylinderProps) + addCylinder(builder, pB, pA, 0.5, cylinderProps) if (i % 10000 === 0 && ctx.shouldUpdate) { await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount }); diff --git a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts index 07b965453e6ed83a4a291a37ec9130ab941d3e3a..6ad043eba547f99896cfbc6e620c709b76dfcc7d 100644 --- a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts +++ b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts @@ -16,6 +16,7 @@ import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/ import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual'; import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; import { OrderedSet } from 'mol-data/int'; +import { Wedge } from '../../../primitive/wedge'; const t = Mat4.identity() const sVec = Vec3.zero() @@ -27,6 +28,8 @@ const depthFactor = 4 const widthFactor = 4 const heightFactor = 6 +const wedge = Wedge() + export interface PolymerDirectionWedgeProps { sizeTheme: SizeThemeProps } @@ -71,7 +74,7 @@ async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, Mat4.mul(t, t, Mat4.rotY90Z180) Mat4.scale(t, t, Vec3.set(sVec, height, width, depth)) Mat4.setTranslation(t, v.p2) - builder.addWedge(t) + builder.add(t, wedge) } if (i % 10000 === 0 && ctx.shouldUpdate) { diff --git a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts index e01feada50bdbb3e72ed9ea9bbd45bdfec4c36c6..047850c67aae193be0cb0035aed18925e054bfcd 100644 --- a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts @@ -15,6 +15,8 @@ import { Vec3 } from 'mol-math/linear-algebra'; import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual'; import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; import { CylinderProps } from '../../../primitive/cylinder'; +import { addSphere } from '../../../mesh/builder/sphere'; +import { addFixedCountDashedCylinder } from '../../../mesh/builder/cylinder'; const segmentCount = 10 @@ -49,7 +51,7 @@ async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, pro if (centerA.element === centerB.element) { builder.setGroup(centerA.element) pos(elements[centerA.element], pA) - builder.addSphere(pA, 0.6, 0) + addSphere(builder, pA, 0.6, 0) } else { const elmA = elements[centerA.element] const elmB = elements[centerB.element] @@ -59,12 +61,12 @@ async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, pro l.element = elmA cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(l) builder.setGroup(centerA.element) - builder.addFixedCountDashedCylinder(pA, pB, 0.5, segmentCount, cylinderProps) + addFixedCountDashedCylinder(builder, pA, pB, 0.5, segmentCount, cylinderProps) l.element = elmB cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(l) builder.setGroup(centerB.element) - builder.addFixedCountDashedCylinder(pB, pA, 0.5, segmentCount, cylinderProps) + addFixedCountDashedCylinder(builder, pB, pA, 0.5, segmentCount, cylinderProps) } if (i % 10000 === 0 && ctx.shouldUpdate) { diff --git a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts index eb8efab672a2858fa1248f4fbe1afc10ce01e618..3f3d745615c1ed1bf2794879ec593ddc6c055931 100644 --- a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts @@ -15,6 +15,8 @@ import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/ import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual'; import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; import { OrderedSet } from 'mol-data/int'; +import { addSheet } from '../../../mesh/builder/sheet'; +import { addTube } from '../../../mesh/builder/tube'; export interface PolymerTraceMeshProps { sizeTheme: SizeThemeProps @@ -57,7 +59,7 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, props: Po if (isSheet) { const height = width * aspectRatio const arrowHeight = v.secStrucChange ? height * arrowFactor : 0 - builder.addSheet(curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true) + addSheet(builder, curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true) } else { let height: number if (isHelix) { @@ -68,7 +70,7 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, props: Po } else { height = width } - builder.addTube(curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true) + addTube(builder, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true) } if (i % 10000 === 0 && ctx.shouldUpdate) { diff --git a/src/mol-geo/representation/structure/visual/util/element.ts b/src/mol-geo/representation/structure/visual/util/element.ts index dd1ea7d1314e10815b4eefdd0829973c3dbff467..d469d6f2f53d63d0233fcf6c3defd0122c4483dc 100644 --- a/src/mol-geo/representation/structure/visual/util/element.ts +++ b/src/mol-geo/representation/structure/visual/util/element.ts @@ -15,6 +15,7 @@ import { Interval, OrderedSet } from 'mol-data/int'; import { PickingId } from '../../../../util/picking'; import { SizeTheme, SizeThemeProps } from 'mol-view/theme/size'; import { LocationIterator } from '../../../../util/location-iterator'; +import { addSphere } from '../../../../mesh/builder/sphere'; export interface ElementSphereMeshProps { sizeTheme: SizeThemeProps, @@ -40,7 +41,7 @@ export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, p pos(elements[i], v) meshBuilder.setGroup(i) - meshBuilder.addSphere(v, sizeTheme.size(l), detail) + addSphere(meshBuilder, v, sizeTheme.size(l), detail) if (i % 10000 === 0 && ctx.shouldUpdate) { await ctx.update({ message: 'Sphere mesh', current: i, max: elementCount }); diff --git a/src/mol-geo/representation/structure/visual/util/link.ts b/src/mol-geo/representation/structure/visual/util/link.ts index f41776cfc29091f0beb02c9bdb9cc5e86bcf4ddd..f2f820ce237db37e09f88af3e603e627632654f2 100644 --- a/src/mol-geo/representation/structure/visual/util/link.ts +++ b/src/mol-geo/representation/structure/visual/util/link.ts @@ -14,6 +14,7 @@ import { SizeThemeProps } from 'mol-view/theme/size'; import { CylinderProps } from '../../../../primitive/cylinder'; import { LocationIterator } from '../../../../util/location-iterator'; import { Unit, StructureElement, Structure, Link } from 'mol-model/structure'; +import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from '../../../../mesh/builder/cylinder'; export const DefaultLinkCylinderProps = { ...DefaultMeshProps, @@ -94,7 +95,7 @@ export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: L if (LinkType.is(f, LinkType.Flag.MetallicCoordination)) { // show metall coordinations with dashed cylinders cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius / 3 - meshBuilder.addFixedCountDashedCylinder(va, vb, 0.5, 7, cylinderProps) + addFixedCountDashedCylinder(meshBuilder, va, vb, 0.5, 7, cylinderProps) } else if (o === 2 || o === 3) { // show bonds with order 2 or 3 using 2 or 3 parallel cylinders const multiRadius = linkRadius * (linkScale / (0.5 * o)) @@ -105,11 +106,11 @@ export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: L cylinderProps.radiusTop = cylinderProps.radiusBottom = multiRadius - if (o === 3) meshBuilder.addCylinder(va, vb, 0.5, cylinderProps) - meshBuilder.addDoubleCylinder(va, vb, 0.5, vShift, cylinderProps) + if (o === 3) addCylinder(meshBuilder, va, vb, 0.5, cylinderProps) + addDoubleCylinder(meshBuilder, va, vb, 0.5, vShift, cylinderProps) } else { cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius - meshBuilder.addCylinder(va, vb, 0.5, cylinderProps) + addCylinder(meshBuilder, va, vb, 0.5, cylinderProps) } if (edgeIndex % 10000 === 0 && ctx.shouldUpdate) { diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts index 52e3f619e0ee243b9c0f33434d248982e97cf394..f8626d61c46af0ad3073f5c6afd8c0be4b040afb 100644 --- a/src/mol-view/stage.ts +++ b/src/mol-view/stage.ts @@ -107,9 +107,9 @@ export class Stage { // this.loadPdbid('1sfi') // contains cyclic peptid // this.loadPdbid('3sn6') // discontinuous chains // this.loadPdbid('2zex') // contains carbohydrate polymer - this.loadPdbid('3sgj') // contains carbohydrate polymer + // this.loadPdbid('3sgj') // contains carbohydrate polymer // this.loadPdbid('3ina') // contains GlcN and IdoA - // this.loadPdbid('1umz') // contains Xyl (Xyloglucan) + this.loadPdbid('1umz') // contains Xyl (Xyloglucan) // this.loadPdbid('1mfb') // contains Abe // this.loadPdbid('2gdu') // contains sucrose // this.loadPdbid('2fnc') // contains maltotriose