Skip to content
Snippets Groups Projects
Commit 49cfaa61 authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, link repr, dashed cylinders

parent 3f6a6b4c
No related branches found
No related tags found
No related merge requests found
......@@ -31,25 +31,20 @@ async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: S
if (!bondCount) return Mesh.createEmpty(mesh)
function referencePosition(edgeIndex: number): Vec3 | null {
// TODO
return null
}
function position(posA: Vec3, posB: Vec3, edgeIndex: number): void {
const b = bonds[edgeIndex]
const uA = b.unitA, uB = b.unitB
uA.conformation.position(uA.elements[b.indexA], posA)
uB.conformation.position(uB.elements[b.indexB], posB)
}
function order(edgeIndex: number): number {
return bonds[edgeIndex].order
const builderProps = {
linkCount: bondCount,
referencePosition: (edgeIndex: number) => null, // TODO
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
const b = bonds[edgeIndex]
const uA = b.unitA, uB = b.unitB
uA.conformation.position(uA.elements[b.indexA], posA)
uB.conformation.position(uB.elements[b.indexB], posB)
},
order: (edgeIndex: number) => bonds[edgeIndex].order,
flags: (edgeIndex: number) => bonds[edgeIndex].flag
}
const linkBuilder = { linkCount: bondCount, referencePosition, position, order }
return createLinkCylinderMesh(ctx, linkBuilder, props, mesh)
return createLinkCylinderMesh(ctx, builderProps, props, mesh)
}
export const DefaultInterUnitLinkProps = {
......
......@@ -32,38 +32,35 @@ async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit,
const elements = unit.elements;
const links = unit.links
const { edgeCount, a, b, edgeProps, offset } = links
const { order: _order } = edgeProps
const { order: _order, flags: _flags } = edgeProps
if (!edgeCount) return Mesh.createEmpty(mesh)
const vRef = Vec3.zero()
const pos = unit.conformation.invariantPosition
function referencePosition(edgeIndex: number): Vec3 | null {
let aI = a[edgeIndex], bI = b[edgeIndex];
if (aI > bI) [aI, bI] = [bI, aI]
for (let i = offset[aI], il = offset[aI + 1]; i < il; ++i) {
if (b[i] !== bI) return pos(elements[b[i]], vRef)
}
for (let i = offset[bI], il = offset[bI + 1]; i < il; ++i) {
if (a[i] !== aI) return pos(elements[a[i]], vRef)
}
return null
}
function position(posA: Vec3, posB: Vec3, edgeIndex: number): void {
pos(elements[a[edgeIndex]], posA)
pos(elements[b[edgeIndex]], posB)
}
function order(edgeIndex: number): number {
return _order[edgeIndex]
const builderProps = {
linkCount: edgeCount * 2,
referencePosition: (edgeIndex: number) => {
let aI = a[edgeIndex], bI = b[edgeIndex];
if (aI > bI) [aI, bI] = [bI, aI]
for (let i = offset[aI], il = offset[aI + 1]; i < il; ++i) {
if (b[i] !== bI) return pos(elements[b[i]], vRef)
}
for (let i = offset[bI], il = offset[bI + 1]; i < il; ++i) {
if (a[i] !== aI) return pos(elements[a[i]], vRef)
}
return null
},
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
pos(elements[a[edgeIndex]], posA)
pos(elements[b[edgeIndex]], posB)
},
order: (edgeIndex: number) => _order[edgeIndex],
flags: (edgeIndex: number) => _flags[edgeIndex]
}
const builder = { linkCount: edgeCount * 2, referencePosition, position, order }
return createLinkCylinderMesh(ctx, builder, props, mesh)
return createLinkCylinderMesh(ctx, builderProps, props, mesh)
}
export const DefaultIntraUnitLinkProps = {
......
......@@ -8,6 +8,7 @@ import { Vec3 } from 'mol-math/linear-algebra';
import { RuntimeContext } from 'mol-task';
import { Mesh } from '../../../../shape/mesh';
import { MeshBuilder } from '../../../../shape/mesh-builder';
import { LinkType } from 'mol-model/structure/model/types';
export const DefaultLinkCylinderProps = {
linkScale: 0.4,
......@@ -52,6 +53,7 @@ export interface LinkCylinderMeshBuilderProps {
referencePosition(edgeIndex: number): Vec3 | null
position(posA: Vec3, posB: Vec3, edgeIndex: number): void
order(edgeIndex: number): number
flags(edgeIndex: number): LinkType.Flag
}
/**
......@@ -59,12 +61,13 @@ export interface LinkCylinderMeshBuilderProps {
* the half closer to the first vertex, i.e. vertex a.
*/
export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: LinkCylinderMeshBuilderProps, props: LinkCylinderProps, mesh?: Mesh) {
const { linkCount, referencePosition, position, order } = linkBuilder
const { linkCount, referencePosition, position, order, flags } = linkBuilder
if (!linkCount) return Mesh.createEmpty(mesh)
// approximate vertextCount, exact calculation would need to take link orders into account
const vertexCount = 32 * linkCount
// approximate vertextCount (* 2), exact calculation would need to take
// multiple cylinders for bond orders and metall coordinations into account
const vertexCount = props.radialSegments * 2 * linkCount * 2
const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh)
const va = Vec3.zero()
......@@ -84,9 +87,15 @@ export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: L
position(va, vb, edgeIndex)
const o = order(edgeIndex)
const f = flags(edgeIndex) as any as LinkType // TODO
meshBuilder.setId(edgeIndex)
if (o === 2 || o === 3) {
if (LinkType.is(f, LinkType.Flag.MetallicCoordination)) {
// show metall coordinations with dashed cylinders
cylinderParams.radiusTop = cylinderParams.radiusBottom = linkRadius / 3
meshBuilder.addFixedCountDashedCylinder(va, vb, 0.5, 7, cylinderParams)
} 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))
const absOffset = (linkRadius - multiRadius) * linkSpacing
......
......@@ -21,12 +21,12 @@ type Primitive = {
}
export interface MeshBuilder {
add(t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices?: Uint32Array): number
addBox(t: Mat4, props?: BoxProps): number
addCylinder(start: Vec3, end: Vec3, scale: number, props: CylinderProps): number
addDoubleCylinder(start: Vec3, end: Vec3, scale: number, shift: Vec3, props: CylinderProps): number
// addFixedCountDashedCylinder(t: Mat4, props?: CylinderProps): number
addIcosahedron(center: Vec3, radius: number, detail: number): number
add(t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices?: Uint32Array): void
addBox(t: Mat4, props?: BoxProps): 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
addIcosahedron(center: Vec3, radius: number, detail: number): void
setId(id: number): void
getMesh(): Mesh
}
......@@ -43,7 +43,7 @@ const tmpCylinderMatDir = Vec3.zero()
const tmpCylinderCenter = Vec3.zero()
const tmpCylinderMat = Mat4.zero()
// const tmpCylinderMatTrans = Mat4.zero()
const tmpShiftedCylinderStart = Vec3.zero()
const tmpCylinderStart = Vec3.zero()
function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number) {
Vec3.setMagnitude(tmpCylinderMatDir, dir, length / 2)
......@@ -99,7 +99,7 @@ export namespace MeshBuilder {
let currentId = -1
function add(t: Mat4, _vertices: Float32Array, _normals: Float32Array, _indices: Uint32Array) {
const { elementCount, elementSize } = vertices
const { elementCount } = vertices
const n = getNormalMatrix(tmpMat3, t)
for (let i = 0, il = _vertices.length; i < il; i += 3) {
// position
......@@ -116,41 +116,64 @@ export namespace MeshBuilder {
for (let i = 0, il = _indices.length; i < il; i += 3) {
ChunkedArray.add3(indices, _indices[i] + elementCount, _indices[i + 1] + elementCount, _indices[i + 2] + elementCount);
}
return elementCount * elementSize
}
return {
add,
addBox: (t: Mat4, props?: BoxProps) => {
const box = Box(props)
return add(t, box.vertices, box.normals, box.indices)
add(t, box.vertices, box.normals, box.indices)
},
addCylinder: (start: Vec3, end: Vec3, scale: number, props: CylinderProps) => {
const d = Vec3.distance(start, end) * scale
addCylinder: (start: Vec3, end: Vec3, lengthScale: number, props: CylinderProps) => {
const d = Vec3.distance(start, end) * lengthScale
props.height = d
const { vertices, normals, indices } = getCylinder(props)
Vec3.sub(tmpCylinderDir, end, start)
setCylinderMat(tmpCylinderMat, start, tmpCylinderDir, d)
return add(tmpCylinderMat, vertices, normals, indices)
add(tmpCylinderMat, vertices, normals, indices)
},
addDoubleCylinder: (start: Vec3, end: Vec3, scale: number, shift: Vec3, props: CylinderProps) => {
const d = Vec3.distance(start, end) * scale
addDoubleCylinder: (start: Vec3, end: Vec3, lengthScale: number, shift: Vec3, props: CylinderProps) => {
const d = Vec3.distance(start, end) * lengthScale
props.height = d
const { vertices, normals, indices } = getCylinder(props)
Vec3.sub(tmpCylinderDir, end, start)
// positivly shifted cylinder
Vec3.add(tmpShiftedCylinderStart, start, shift)
setCylinderMat(tmpCylinderMat, tmpShiftedCylinderStart, tmpCylinderDir, d)
Vec3.add(tmpCylinderStart, start, shift)
setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d)
add(tmpCylinderMat, vertices, normals, indices)
// negativly shifted cylinder
Vec3.sub(tmpShiftedCylinderStart, start, shift)
setCylinderMat(tmpCylinderMat, tmpShiftedCylinderStart, tmpCylinderDir, d)
return add(tmpCylinderMat, vertices, normals, indices)
Vec3.sub(tmpCylinderStart, start, shift)
setCylinderMat(tmpCylinderMat, tmpCylinderStart, tmpCylinderDir, d)
add(tmpCylinderMat, vertices, normals, indices)
},
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 { vertices, normals, indices } = 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)
add(tmpCylinderMat, vertices, normals, indices)
}
},
addIcosahedron: (center: Vec3, radius: number, detail: number) => {
const { vertices, normals, indices } = getIcosahedron({ radius, detail })
setIcosahedronMat(tmpIcosahedronMat, center)
return add(tmpIcosahedronMat, vertices, normals, indices)
add(tmpIcosahedronMat, vertices, normals, indices)
},
setId: (id: number) => {
if (currentId !== id) {
......
......@@ -10,7 +10,7 @@ namespace BitFlags {
export function create<F>(flags: F): BitFlags<F> { return flags as any; }
export function has<F>(flags: BitFlags<F>, flag: F) { return ((flags as any) & (flag as any)) !== 0; }
// toCheck must be non-zero
/** toCheck must be non-zero */
export function hasAll<F>(flags: BitFlags<F>, toCheck: BitFlags<F>) { return !!toCheck && ((flags as any) & (toCheck as any)) === (toCheck as any); }
}
......
......@@ -42,9 +42,10 @@ export class Stage {
// this.loadPdbid('1jj2')
// this.loadPdbid('4umt') // ligand has bond with order 3
// this.loadPdbid('1crn')
this.loadPdbid('3pqr')
// this.loadPdbid('4v5a')
// this.loadPdbid('1crn') // small
this.loadPdbid('1blu') // metal coordination
// this.loadPdbid('3pqr') // inter unit bonds
// this.loadPdbid('4v5a') // ribosome
// this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment