diff --git a/src/mol-geo/primitive/polygon.ts b/src/mol-geo/primitive/polygon.ts new file mode 100644 index 0000000000000000000000000000000000000000..83ff7c8c0da766f3ad7176fc57e81085e4f93744 --- /dev/null +++ b/src/mol-geo/primitive/polygon.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +/** + * Create 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) { + const points = new Float32Array(sideCount * 2) + const radius = sideCount <= 4 ? Math.sqrt(2) / 2 : 0.6 + + const offset = shift ? 0 : 1 + + for (let i = 0, il = 2 * sideCount; i < il; i += 2) { + const c = (i + offset) / sideCount * Math.PI + points[i] = Math.cos(c) * radius + points[i + 1] = Math.sin(c) * radius + } + return points +} \ No newline at end of file diff --git a/src/mol-geo/primitive/polyhedron.ts b/src/mol-geo/primitive/polyhedron.ts index 120d127be6e49b4b3151113e8fa3018ef2349520..02a69df21812f30d8b081cc88988e05b84b9d905 100644 --- a/src/mol-geo/primitive/polyhedron.ts +++ b/src/mol-geo/primitive/polyhedron.ts @@ -29,7 +29,6 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num const normals = new Float32Array(vertices.length); computeIndexedVertexNormals(vertices, indices, normals) - // this.normalizeNormals(); // smooth normals return { vertices: new Float32Array(vertices), diff --git a/src/mol-geo/primitive/primitive.ts b/src/mol-geo/primitive/primitive.ts index c49d17f09485552ade99c076a3450f8c30243887..b84dbf379fdf281965bb178e0f1f1d498e93c91b 100644 --- a/src/mol-geo/primitive/primitive.ts +++ b/src/mol-geo/primitive/primitive.ts @@ -12,29 +12,48 @@ export interface Primitive { indices: ArrayLike<number> } -const tri = [ Vec3.zero(), Vec3.zero(), Vec3.zero() ] -const n = Vec3.zero() +const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero() /** Create primitive with face normals from vertices and indices */ -export function createPrimitive(_vertices: ArrayLike<number>, _indices: ArrayLike<number>): Primitive { - const count = _indices.length - const vertices = new Float32Array(count * 3) - const normals = new Float32Array(count * 3) - const indices = new Uint32Array(count) +export function createPrimitive(vertices: ArrayLike<number>, indices: ArrayLike<number>): Primitive { + const count = indices.length + const builder = PrimitiveBuilder(count / 3) for (let i = 0; i < count; i += 3) { - for (let j = 0; j < 3; ++j) { - Vec3.fromArray(tri[j], _vertices, _indices[i + j] * 3) - Vec3.toArray(tri[j], vertices, i * 3 + j * 3) - } + Vec3.fromArray(a, vertices, indices[i] * 3) + Vec3.fromArray(b, vertices, indices[i + 1] * 3) + Vec3.fromArray(c, vertices, indices[i + 2] * 3) + builder.add(a, b, c) + } + return builder.getPrimitive() +} - Vec3.triangleNormal(n, tri[0], tri[1], tri[2]) +export interface PrimitiveBuilder { + add(a: Vec3, b: Vec3, c: Vec3): void + getPrimitive(): Primitive +} - for (let j = 0; j < 3; ++j) { - Vec3.toArray(n, normals, i * 3 + j * 3) - indices[i + j] = i + j - } +const vn = Vec3.zero() + +/** Builder to create primitive with face normals */ +export function PrimitiveBuilder(triangleCount: number): PrimitiveBuilder { + const vertices = new Float32Array(triangleCount * 3 * 3) + const normals = new Float32Array(triangleCount * 3 * 3) + const indices = new Uint32Array(triangleCount * 3) + let offset = 0 + + return { + add: (a: Vec3, b: Vec3, c: Vec3) => { + Vec3.toArray(a, vertices, offset) + Vec3.toArray(b, vertices, offset + 3) + Vec3.toArray(c, vertices, offset + 6) + Vec3.triangleNormal(vn, a, b, c) + for (let j = 0; j < 3; ++j) { + Vec3.toArray(vn, normals, offset + 3 * j) + indices[offset / 3 + j] = offset / 3 + j + } + offset += 9 + }, + getPrimitive: () => ({ vertices, normals, indices }) } - - return { vertices, normals, indices } } \ No newline at end of file diff --git a/src/mol-geo/primitive/prism.ts b/src/mol-geo/primitive/prism.ts new file mode 100644 index 0000000000000000000000000000000000000000..445ceed901872f4320f646621fa62fbe45353cf7 --- /dev/null +++ b/src/mol-geo/primitive/prism.ts @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2018 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 { Primitive, PrimitiveBuilder } from './primitive'; +import { polygon } from './polygon' + +const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero() + +/** + * Create a prism with a poligonal base + */ +export function Prism(points: ArrayLike<number>): Primitive { + const sideCount = points.length / 2 + const baseCount = sideCount === 3 ? 1 : sideCount === 4 ? 2 : sideCount + const count = 2 * baseCount + 2 * sideCount + const builder = PrimitiveBuilder(count) + + // create sides + for (let i = 0; i < sideCount; ++i) { + const ni = (i + 1) % sideCount + Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5) + Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5) + Vec3.set(c, points[ni * 2], points[ni * 2 + 1], 0.5) + Vec3.set(d, points[i * 2], points[i * 2 + 1], 0.5) + builder.add(a, b, c) + builder.add(c, d, a) + } + + // create bases + if (sideCount === 3) { + Vec3.set(a, points[0], points[1], -0.5) + Vec3.set(b, points[2], points[3], -0.5) + Vec3.set(c, points[4], points[5], -0.5) + builder.add(a, b, c) + Vec3.set(a, points[0], points[1], 0.5) + Vec3.set(b, points[2], points[3], 0.5) + Vec3.set(c, points[4], points[5], 0.5) + builder.add(c, b, a) + } else if (sideCount === 4) { + Vec3.set(a, points[0], points[1], -0.5) + Vec3.set(b, points[2], points[3], -0.5) + Vec3.set(c, points[4], points[5], -0.5) + Vec3.set(d, points[6], points[7], -0.5) + builder.add(a, b, c) + builder.add(c, d, a) + Vec3.set(a, points[0], points[1], 0.5) + Vec3.set(b, points[2], points[3], 0.5) + Vec3.set(c, points[4], points[5], 0.5) + Vec3.set(d, points[6], points[7], 0.5) + builder.add(a, b, c) + builder.add(c, d, a) + } else { + for (let i = 0; i < sideCount; ++i) { + const ni = (i + 1) % sideCount + Vec3.set(a, points[i * 2], points[i * 2 + 1], -0.5) + Vec3.set(b, points[ni * 2], points[ni * 2 + 1], -0.5) + Vec3.set(c, 0, 0, -0.5) + builder.add(a, b, c) + Vec3.set(a, points[i * 2], points[i * 2 + 1], 0.5) + Vec3.set(b, points[ni * 2], points[ni * 2 + 1], 0.5) + Vec3.set(c, 0, 0, 0.5) + builder.add(c, b, a) + } + } + + return builder.getPrimitive() +} + +let wedge: Primitive +export function Wedge() { + if (!wedge) wedge = Prism(polygon(3, false)) + return wedge +} + +let box: Primitive +export function Box() { + if (!box) box = Prism(polygon(4, false)) + return box +} + +let diamond: Primitive +export function DiamondPrism() { + if (!diamond) diamond = Prism(polygon(4, false)) + return diamond +} + +let hexagonalPrism: Primitive +export function HexagonalPrism() { + if (!hexagonalPrism) hexagonalPrism = Prism(polygon(5, false)) + return hexagonalPrism +} + +let pentagonalPrism: Primitive +export function PentagonalPrism() { + if (!pentagonalPrism) pentagonalPrism = Prism(polygon(6, true)) + return pentagonalPrism +} \ No newline at end of file diff --git a/src/mol-geo/primitive/star.ts b/src/mol-geo/primitive/star.ts index fbbf5043906fbe131db051731b4f817213f996d2..f24ac248bfe1e48276434564a695364ad909a413 100644 --- a/src/mol-geo/primitive/star.ts +++ b/src/mol-geo/primitive/star.ts @@ -5,7 +5,7 @@ */ import { Vec3 } from 'mol-math/linear-algebra' -import { Primitive } from './primitive'; +import { Primitive, PrimitiveBuilder } from './primitive'; export const DefaultStarProps = { pointCount: 5, @@ -15,29 +15,21 @@ export const DefaultStarProps = { } export type StarProps = Partial<typeof DefaultStarProps> -const op = Vec3.zero() -const on = Vec3.zero() -const vn = Vec3.zero() -const p1 = Vec3.zero() -const p2 = Vec3.zero() -const p3 = Vec3.zero() +const op = Vec3.zero(), on = Vec3.zero() +const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero() export function Star(props?: StarProps): Primitive { const { outerRadius, innerRadius, thickness, pointCount } = { ...DefaultStarProps, ...props } const triangleCount = pointCount * 2 * 2 - const vertexCount = triangleCount * 3 - - const vertices = new Float32Array(vertexCount * 3) - const normals = new Float32Array(vertexCount * 3) - const indices = new Uint32Array(triangleCount * 3) + const builder = PrimitiveBuilder(triangleCount) const innerPoints = new Float32Array(pointCount * 2) const outerPoints = new Float32Array(pointCount * 2) for (let i = 0; i < pointCount; ++i) { const io = i * 2, ii = i * 2 + 1 - const co = io / pointCount * Math.PI, ci = ii / pointCount * Math.PI + const co = (io + 1) / pointCount * Math.PI, ci = (ii + 1) / pointCount * Math.PI outerPoints[io] = Math.cos(co) * outerRadius outerPoints[ii] = Math.sin(co) * outerRadius innerPoints[io] = Math.cos(ci) * innerRadius @@ -47,29 +39,17 @@ export function Star(props?: StarProps): Primitive { Vec3.set(op, 0, 0, thickness / 2) Vec3.set(on, 0, 0, -thickness / 2) - function add(a: Vec3, b: Vec3, c: Vec3, offset: number) { - Vec3.toArray(a, vertices, offset) - Vec3.toArray(b, vertices, offset + 3) - Vec3.toArray(c, vertices, offset + 6) - Vec3.triangleNormal(vn, a, b, c) - for (let j = 0; j < 3; ++j) { - Vec3.toArray(vn, normals, offset + 3 * j) - indices[offset / 3 + j] = offset / 3 + j - } - } - for (let i = 0; i < pointCount; ++i) { const ni = (i + 1) % pointCount - Vec3.set(p1, outerPoints[i * 2], outerPoints[i * 2 + 1], 0) - Vec3.set(p2, innerPoints[i * 2], innerPoints[i * 2 + 1], 0) - Vec3.set(p3, outerPoints[ni * 2], outerPoints[ni * 2 + 1], 0) - - const offset = i * 3 * 3 * 4 - add(op, p1, p2, offset) - add(on, p1, p2, offset + 9) - add(op, p2, p3, offset + 18) - add(on, p2, p3, offset + 27) + Vec3.set(a, outerPoints[i * 2], outerPoints[i * 2 + 1], 0) + Vec3.set(b, innerPoints[i * 2], innerPoints[i * 2 + 1], 0) + Vec3.set(c, outerPoints[ni * 2], outerPoints[ni * 2 + 1], 0) + + builder.add(op, a, b) + builder.add(on, a, b) + builder.add(op, b, c) + builder.add(on, b, c) } - return { vertices, normals, indices } + return builder.getPrimitive() } \ 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 3430597c8fb0cfea92165a62c1911f4acdfb163e..265e32e7897df348245d86e4ca894099f53b3673 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts @@ -24,16 +24,16 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { createUniformColor } from '../../../util/color-data'; import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants'; +const t = Mat4.identity() +const sVec = Vec3.zero() +const p = Vec3.zero() +const pd = Vec3.zero() +const p1 = Vec3.zero() +const p2 = Vec3.zero() + async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, mesh?: Mesh) { const builder = MeshBuilder.create(256, 128, mesh) - const t = Mat4.identity() - const sMat = Mat4.identity() - const sVec = Vec3.zero() - const p = Vec3.zero() - const pd = Vec3.zero() - const p1 = Vec3.zero() - const p2 = Vec3.zero() const carbohydrates = structure.carbohydrates function centerAlign(center: Vec3, normal: Vec3, direction: Vec3) { @@ -44,7 +44,7 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru const side = 1.75 * 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4) const radius = 1.75 - const coneParams = { radiusTop: 0.0, radiusBottom: radius, bottomCap: true } + const coneParams = { radiusTop: radius, radiusBottom: 0.0, topCap: true } const linkParams = { radiusTop: 0.4, radiusBottom: 0.4 } @@ -54,61 +54,74 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru const cGeo = c.geometry! const shapeType = getSaccharideShape(c.component.type) + switch (shapeType) { case SaccharideShapes.FilledSphere: builder.addSphere(cGeo.center, radius, 2) break; case SaccharideShapes.FilledCube: centerAlign(cGeo.center, cGeo.normal, cGeo.direction) - builder.addBox(t, { width: side, height: side, depth: side }) - builder.addOctahedron(t) + Mat4.scaleUniformly(t, t, side) + builder.addBox(t) break; case SaccharideShapes.CrossedCube: // TODO split centerAlign(cGeo.center, cGeo.normal, cGeo.direction) - builder.addBox(t, { width: side, height: side, depth: side }) + Mat4.scaleUniformly(t, t, side) + builder.addBox(t) break; case SaccharideShapes.FilledCone: - Vec3.scaleAndAdd(p1, cGeo.center, cGeo.direction, radius) - Vec3.scaleAndSub(p2, cGeo.center, cGeo.direction, radius) + Vec3.scaleAndSub(p1, cGeo.center, cGeo.direction, radius) + Vec3.scaleAndAdd(p2, cGeo.center, cGeo.direction, radius) builder.addCylinder(p1, p2, 1, coneParams) break case SaccharideShapes.DevidedCone: // TODO split - Vec3.scaleAndAdd(p1, cGeo.center, cGeo.direction, radius) - Vec3.scaleAndSub(p2, cGeo.center, cGeo.direction, radius) + Vec3.scaleAndSub(p1, cGeo.center, cGeo.direction, radius) + Vec3.scaleAndAdd(p2, cGeo.center, cGeo.direction, radius) builder.addCylinder(p1, p2, 1, coneParams) break case SaccharideShapes.FlatBox: centerAlign(cGeo.center, cGeo.normal, cGeo.direction) - builder.addBox(t, { width: side, height: side / 2, depth: side }) + Mat4.mul(t, t, Mat4.rotY90) + Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2)) + builder.addBox(t) break case SaccharideShapes.FilledStar: - centerAlign(cGeo.center, cGeo.direction, cGeo.normal) + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) + Mat4.mul(t, t, Mat4.rotY90) builder.addStar(t, { outerRadius: side, innerRadius: side / 2, thickness: side / 2, pointCount: 5 }) break case SaccharideShapes.FilledDiamond: centerAlign(cGeo.center, cGeo.normal, cGeo.direction) - Mat4.fromScaling(sMat, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) - Mat4.mul(t, t, sMat) + Mat4.mul(t, t, Mat4.rotY90) + Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) builder.addOctahedron(t) break case SaccharideShapes.DividedDiamond: // TODO split centerAlign(cGeo.center, cGeo.normal, cGeo.direction) - Mat4.fromScaling(sMat, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) - Mat4.mul(t, t, sMat) + Mat4.mul(t, t, Mat4.rotY90) + Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) builder.addOctahedron(t) break case SaccharideShapes.FlatDiamond: + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) + Mat4.mul(t, t, Mat4.rotY90) + Mat4.scale(t, t, Vec3.set(sVec, side, side / 2, side / 2)) + builder.addDiamondPrism(t) case SaccharideShapes.Pentagon: centerAlign(cGeo.center, cGeo.normal, cGeo.direction) - builder.addBox(t, { width: side, height: side, depth: 0.5 }) + Mat4.mul(t, t, Mat4.rotY90) + Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2)) + builder.addPentagonalPrism(t) break case SaccharideShapes.FlatHexagon: default: centerAlign(cGeo.center, cGeo.normal, cGeo.direction) - builder.addBox(t, { width: side, height: side, depth: 0.1 }) + Mat4.mul(t, t, Mat4.rotY90) + Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2)) + builder.addHexagonalPrism(t) 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 cbb68b1d1ba0e2ff36dad1260e4f466e540d3576..dc081e27d32eafcc7cd82e70bcddc3fc39923e91 100644 --- a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts +++ b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts @@ -27,6 +27,19 @@ 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'; +const p1 = Vec3.zero() +const p2 = Vec3.zero() +const p3 = Vec3.zero() +const p4 = Vec3.zero() +const p5 = Vec3.zero() +const p6 = Vec3.zero() +const v12 = Vec3.zero() +const v34 = Vec3.zero() +const vC = Vec3.zero() +const center = Vec3.zero() +const t = Mat4.identity() +const sVec = Vec3.zero() + async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) @@ -41,18 +54,6 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: const chainIt = Segmentation.transientSegments(chainAtomSegments, elements) const residueIt = Segmentation.transientSegments(residueAtomSegments, elements) - const p1 = Vec3.zero() - const p2 = Vec3.zero() - const p3 = Vec3.zero() - const p4 = Vec3.zero() - const p5 = Vec3.zero() - const p6 = Vec3.zero() - const v12 = Vec3.zero() - const v34 = Vec3.zero() - const vC = Vec3.zero() - const center = Vec3.zero() - const t = Mat4.identity() - let i = 0 while (chainIt.hasNext) { residueIt.setSegment(chainIt.move()); @@ -94,9 +95,10 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: Vec3.normalize(vC, Vec3.cross(vC, v12, v34)) Mat4.targetTo(t, p1, p2, vC) Vec3.scaleAndAdd(center, p1, v12, height / 2 - 0.2) + Mat4.scale(t, t, Vec3.set(sVec, width, depth, height)) Mat4.setTranslation(t, center) builder.setId(SortedArray.findPredecessorIndex(elements, idx6)) - builder.addBox(t, { width: width, height: depth, depth: height }) + builder.addBox(t) builder.addCylinder(p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 }) } } 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 8cea9508887e465e487f69d2b3a98909fdf0a989..76390e3efe676e43233bac93c2324863712ea81f 100644 --- a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts +++ b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts @@ -28,6 +28,10 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types'; const t = Mat4.identity() +const sVec = Vec3.zero() +const n0 = Vec3.zero() +const n1 = Vec3.zero() +const upVec = Vec3.zero() async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) { const polymerElementCount = getPolymerElementCount(unit) @@ -51,23 +55,27 @@ async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta) const tension = (isNucleic || isSheet) ? 0.5 : 0.9 - // console.log('ELEMENT', i) interpolateCurveSegment(state, v, tension) if ((isSheet && !v.secStrucChange) || !isSheet) { - const upVec = Vec3.zero() + let width = 0.5, height = 1.2, depth = 0.6 if (isNucleic) { - Vec3.fromArray(upVec, binormalVectors, Math.round(linearSegments / 2) * 3) + Vec3.fromArray(n0, binormalVectors, 0) + Vec3.fromArray(n1, binormalVectors, 3) + Vec3.normalize(upVec, Vec3.add(upVec, n0, n1)) depth = 0.9 } else { - Vec3.fromArray(upVec, normalVectors, Math.round(linearSegments / 2) * 3) + Vec3.fromArray(n0, normalVectors, 0) + Vec3.fromArray(n1, normalVectors, 3) + Vec3.normalize(upVec, Vec3.add(upVec, n0, n1)) } Mat4.targetTo(t, v.p3, v.p1, upVec) - Mat4.mul(t, t, Mat4.rotXY90) + Mat4.mul(t, t, Mat4.rotY90) + Mat4.scale(t, t, Vec3.set(sVec, height, width, depth)) Mat4.setTranslation(t, v.p2) - builder.addWedge(t, { width, height, depth }) + builder.addWedge(t) } if (i % 10000 === 0 && ctx.shouldUpdate) { diff --git a/src/mol-geo/shape/mesh-builder.ts b/src/mol-geo/shape/mesh-builder.ts index 9427faa6981313720e1e77208d1c375090507b30..535b86ca955b123b7f5c1b725f2aed8d8814b0b1 100644 --- a/src/mol-geo/shape/mesh-builder.ts +++ b/src/mol-geo/shape/mesh-builder.ts @@ -8,9 +8,7 @@ import { ValueCell } from 'mol-util/value-cell' import { Vec3, Mat4, Mat3 } from 'mol-math/linear-algebra'; import { ChunkedArray } from 'mol-data/util'; -import { Box, BoxProps } from '../primitive/box'; import { Plane, PlaneProps } from '../primitive/plane'; -import { Wedge, WedgeProps } from '../primitive/wedge'; import { Cylinder, CylinderProps } from '../primitive/cylinder'; import { Sphere, SphereProps } from '../primitive/sphere'; import { Mesh } from './mesh'; @@ -20,6 +18,7 @@ import { addTube } from '../primitive/tube'; import { StarProps, Star } from '../primitive/star'; import { Octahedron } from '../primitive/octahedron'; import { Primitive } from '../primitive/primitive'; +import { Wedge, Box, DiamondPrism, PentagonalPrism, HexagonalPrism } from '../primitive/prism'; export interface MeshBuilderState { vertices: ChunkedArray<number, 3> @@ -29,9 +28,12 @@ export interface MeshBuilderState { export interface MeshBuilder { add(t: Mat4, _vertices: ArrayLike<number>, _normals: ArrayLike<number>, _indices?: ArrayLike<number>): void - addBox(t: Mat4, props?: BoxProps): void + addBox(t: Mat4): void addPlane(t: Mat4, props?: PlaneProps): void - addWedge(t: Mat4, props?: WedgeProps): void + addWedge(t: Mat4): void + addDiamondPrism(t: Mat4): void + addPentagonalPrism(t: Mat4): void + addHexagonalPrism(t: Mat4): void addStar(t: Mat4, props?: StarProps): void addOctahedron(t: Mat4): void addCylinder(start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps): void @@ -132,16 +134,28 @@ export namespace MeshBuilder { return { add, - addBox: (t: Mat4, props?: BoxProps) => { - const { vertices, normals, indices } = Box(props) + addBox: (t: Mat4) => { + const { vertices, normals, indices } = Box() add(t, vertices, normals, indices) }, addPlane: (t: Mat4, props?: PlaneProps) => { const { vertices, normals, indices } = Plane(props) add(t, vertices, normals, indices) }, - addWedge: (t: Mat4, props?: WedgeProps) => { - const { vertices, normals, indices } = Wedge(props) + addWedge: (t: Mat4) => { + const { vertices, normals, indices } = Wedge() + add(t, vertices, normals, indices) + }, + addDiamondPrism: (t: Mat4) => { + const { vertices, normals, indices } = DiamondPrism() + add(t, vertices, normals, indices) + }, + addPentagonalPrism: (t: Mat4) => { + const { vertices, normals, indices } = PentagonalPrism() + add(t, vertices, normals, indices) + }, + addHexagonalPrism: (t: Mat4) => { + const { vertices, normals, indices } = HexagonalPrism() add(t, vertices, normals, indices) }, addStar: (t: Mat4, props?: StarProps) => { diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts index 92ae72e4cd7ab07ef097e2f953a41b8edc24d152..dd56c80e624ba93813ab5949cf40cb2895dc75e9 100644 --- a/src/mol-math/linear-algebra/3d/mat4.ts +++ b/src/mol-math/linear-algebra/3d/mat4.ts @@ -489,6 +489,26 @@ namespace Mat4 { return out; } + export function scaleUniformly(out: Mat4, a: Mat4, scale: number) { + out[0] = a[0] * scale; + out[1] = a[1] * scale; + out[2] = a[2] * scale; + out[3] = a[3] * scale; + out[4] = a[4] * scale; + out[5] = a[5] * scale; + out[6] = a[6] * scale; + out[7] = a[7] * scale; + out[8] = a[8] * scale; + out[9] = a[9] * scale; + out[10] = a[10] * scale; + out[11] = a[11] * scale; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + } + export function fromScaling(out: Mat4, v: Vec3) { out[0] = v[0]; out[1] = 0; @@ -509,6 +529,26 @@ namespace Mat4 { return out; } + export function fromUniformScaling(out: Mat4, scale: number) { + out[0] = scale; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = scale; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = scale; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + export function makeTable(m: Mat4) { let ret = ''; for (let i = 0; i < 4; i++) { diff --git a/src/mol-model/structure/structure/carbohydrates/compute.ts b/src/mol-model/structure/structure/carbohydrates/compute.ts index eb441c0d1cce720a116ddeee1b79d0ce797ce86e..308780d5ae6f341f9a166cd2cb1175ac957a2c4c 100644 --- a/src/mol-model/structure/structure/carbohydrates/compute.ts +++ b/src/mol-model/structure/structure/carbohydrates/compute.ts @@ -206,7 +206,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { const elementIndexB = elementsWithRingMap.get(elementKey(getResidueIndex(indexB, unitB), unitB.id)) if (elementIndexA !== undefined && elementIndexB !== undefined) { - if (getAtomId(unitA, indexA).startsWith('C1')) { + const atomIdA = getAtomId(unitA, indexA) + if (atomIdA.startsWith('O1') || atomIdA.startsWith('C1')) { fixLinkDirection(elementIndexA, elementIndexB) } links.push({ @@ -214,7 +215,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { carbohydrateIndexB: elementIndexB }) } else if (elementIndexA !== undefined) { - if (getAtomId(unitA, indexA).startsWith('C1')) { + const atomIdA = getAtomId(unitA, indexA) + if (atomIdA.startsWith('O1') || atomIdA.startsWith('C1')) { fixTerminalLinkDirection(elementIndexA, indexB, unitB) } terminalLinks.push({ diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts index 96a24b36b5b657dc82b6e1435c343f77ae0b554a..b806d5407083f99be48362a139c4f1766f0a231b 100644 --- a/src/mol-view/stage.ts +++ b/src/mol-view/stage.ts @@ -98,8 +98,8 @@ export class Stage { // this.loadPdbid('3sn6') // discontinuous chains // this.loadPdbid('2zex') // contains carbohydrate polymer // this.loadPdbid('3sgj') // contains carbohydrate polymer - // this.loadPdbid('3ina') // contains GlcN and IdoA - this.loadPdbid('1umz') // contains Xyl (Xyloglucan) + this.loadPdbid('3ina') // contains GlcN and IdoA + // this.loadPdbid('1umz') // contains Xyl (Xyloglucan) // this.loadPdbid('1mfb') // contains Abe // this.loadPdbid('2gdu') // contains sucrose // this.loadPdbid('2fnc') // contains maltotriose