浏览代码

added cylinder primitive, default props, repr task

Alexander Rose 7 年之前
父节点
当前提交
747f12bd19

+ 23 - 12
src/apps/render-test/state.ts

@@ -14,7 +14,7 @@ import Box from 'mol-geo/primitive/box'
 import Spacefill from 'mol-geo/representation/structure/spacefill'
 
 import CIF from 'mol-io/reader/cif'
-import { Run } from 'mol-task'
+import { Run, Progress } from 'mol-task'
 import { AtomSet, Structure } from 'mol-model/structure'
 
 async function parseCif(data: string|Uint8Array) {
@@ -32,6 +32,7 @@ async function getPdb(pdb: string) {
 }
 
 import mcubes from './mcubes'
+import Cylinder from 'mol-geo/primitive/cylinder';
 
 export default class State {
     async initRenderer (container: HTMLDivElement) {
@@ -71,10 +72,20 @@ export default class State {
             transform: transformArray2
         })
 
-        const sphere = Icosahedron(1, 1)
+        const cylinder = Cylinder({ height: 3, radiusBottom: 0.5, radiusTop: 0.5 })
+        console.log(cylinder)
+        const cylinderMesh = createRenderObject('mesh', {
+            position: ValueCell.create(cylinder.vertices),
+            normal: ValueCell.create(cylinder.normals),
+            color,
+            transform: transformArray2
+        }, cylinder.indices)
+        renderer.add(cylinderMesh)
+
+        const sphere = Icosahedron()
         console.log(sphere)
 
-        const box = Box(1, 1, 1, 1, 1, 1)
+        const box = Box()
         console.log(box)
 
         const points2 = createRenderObject('point', {
@@ -86,19 +97,23 @@ export default class State {
         function cubesF(x: number, y: number, z: number) {
             return x * x + y * y + z * z - rr * rr;
         }
-
         let cubes = await mcubes(cubesF);
 
         const makeCubesMesh = () => createRenderObject('mesh', {
             position: cubes.surface.vertexBuffer,
-            normal: cubes.surface.normalBuffer as any,
+            normal: cubes.surface.normalBuffer,
             color,
             transform: transformArray1,
         }, cubes.surface.indexBuffer.ref.value);
         const mesh2 = makeCubesMesh();
         renderer.add(mesh2)
 
-        function createSpacefills (structure: Structure) {
+        function log(progress: Progress) {
+            const p = progress.root.progress
+            console.log(`${p.message} ${(p.current/p.max*100).toFixed(2)}%`)
+        }
+
+        async function createSpacefills (structure: Structure) {
             const spacefills: RenderObject[] = []
             const { atoms, units } = structure;
             const unitIds = AtomSet.unitIds(atoms);
@@ -108,18 +123,14 @@ export default class State {
                 const atomGroup = AtomSet.unitGetByIndex(atoms, i);
 
                 const spacefill = Spacefill()
-                spacefills.push(...spacefill.create(unit, atomGroup, {}))
+                spacefills.push(...await Run(spacefill.create(unit, atomGroup), log, 1))
             }
             return spacefills
         }
         const structures = await getPdb('1crn')
-        const spacefills = createSpacefills(structures[0])
+        const spacefills = await createSpacefills(structures[0])
         spacefills.forEach(renderer.add)
 
         renderer.frame()
     }
-
-    constructor() {
-
-    }
 }

+ 17 - 5
src/mol-geo/primitive/box.ts

@@ -8,10 +8,18 @@
 
 import { Vec3 } from 'mol-math/linear-algebra'
 
-export default function Box(width: number, height: number, depth: number, widthSegments: number, heightSegments: number, depthSegments: number) {
-    widthSegments = Math.floor(widthSegments)
-    heightSegments = Math.floor(heightSegments)
-    depthSegments = Math.floor(depthSegments)
+export const DefaultBoxProps = {
+    width: 1,
+    height: 1,
+    depth: 1,
+    widthSegments: 1,
+    heightSegments: 1,
+    depthSegments: 1
+}
+export type BoxProps = typeof DefaultBoxProps
+
+export default function Box(props?: Partial<BoxProps>) {
+    const { width, height, depth, widthSegments, heightSegments, depthSegments } = { ...DefaultBoxProps, ...props }
 
     // buffers
     const indices: number[] = [];
@@ -29,7 +37,11 @@ export default function Box(width: number, height: number, depth: number, widthS
     buildPlane(0, 1, 2, 1, -1, width, height, depth, widthSegments, heightSegments); // pz
     buildPlane(0, 1, 2, -1, -1, width, height, -depth, widthSegments, heightSegments); // nz
 
-    return { vertices, indices, normals }
+    return {
+        vertices: new Float32Array(vertices),
+        normals: new Float32Array(normals),
+        indices: new Uint32Array(indices)
+    }
 
     function buildPlane(u: number, v: number, w: number, udir: number, vdir: number, width: number, height: number, depth: number, gridX: number, gridY: number) {
 

+ 160 - 0
src/mol-geo/primitive/cylinder.ts

@@ -0,0 +1,160 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+// adapted from three.js, MIT License Copyright 2010-2018 three.js authors
+
+import { Vec3 } from 'mol-math/linear-algebra'
+
+export const DefaultCylinderProps = {
+    radiusTop: 1,
+    radiusBottom: 1,
+    height: 1,
+    radialSegments: 8,
+    heightSegments: 1,
+    openEnded: false,
+    thetaStart: 0.0,
+    thetaLength: Math.PI * 2
+}
+export type CylinderProps = typeof DefaultCylinderProps
+
+export default function Cylinder(props?: Partial<CylinderProps>) {
+    const { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength } = { ...DefaultCylinderProps, ...props };
+
+    // buffers
+    const indices: number[] = [];
+    const vertices: number[] = [];
+    const normals: number[] = [];
+
+    // helper variables
+    let index = 0;
+    const indexArray: number[][] = [];
+    const halfHeight = height / 2;
+
+    // generate geometry
+    generateTorso();
+
+    if (openEnded === false) {
+        if (radiusTop > 0) generateCap(true);
+        if (radiusBottom > 0) generateCap(false);
+    }
+
+    return {
+        vertices: new Float32Array(vertices),
+        normals: new Float32Array(normals),
+        indices: new Uint32Array(indices)
+    }
+
+    function generateTorso() {
+        const normal = Vec3.zero()
+
+        // this will be used to calculate the normal
+        const slope = (radiusBottom - radiusTop) / height;
+
+        // generate vertices, normals and uvs
+
+        for (let y = 0; y <= heightSegments; ++y) {
+            const indexRow: number[] = [];
+            const v = y / heightSegments;
+
+            // calculate the radius of the current row
+            const radius = v * (radiusBottom - radiusTop) + radiusTop;
+
+            for (let x = 0; x <= radialSegments; ++x) {
+                const u = x / radialSegments;
+                const theta = u * thetaLength + thetaStart;
+                const sinTheta = Math.sin(theta);
+                const cosTheta = Math.cos(theta);
+
+                // vertex
+                vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
+
+                // normal
+                Vec3.normalize(normal, Vec3.set(normal, sinTheta, slope, cosTheta));
+                normals.push(...normal);
+
+                // save index of vertex in respective row
+                indexRow.push(index++);
+            }
+
+            // now save vertices of the row in our index array
+            indexArray.push(indexRow);
+        }
+
+        // generate indices
+
+        for (let x = 0; x < radialSegments; ++x) {
+
+            for (let y = 0; y < heightSegments; ++y) {
+                // we use the index array to access the correct indices
+                const a = indexArray[ y ][ x ];
+                const b = indexArray[ y + 1 ][ x ];
+                const c = indexArray[ y + 1 ][ x + 1 ];
+                const d = indexArray[ y ][ x + 1 ];
+
+                // faces
+                indices.push(a, b, d);
+                indices.push(b, c, d);
+            }
+
+        }
+    }
+
+    function generateCap(top: boolean) {
+        const radius = (top === true) ? radiusTop : radiusBottom;
+        const sign = (top === true) ? 1 : - 1;
+
+        // save the index of the first center vertex
+        let centerIndexStart = index;
+
+        // first we generate the center vertex data of the cap.
+        // because the geometry needs one set of uvs per face,
+        // we must generate a center vertex per face/segment
+
+        for (let x = 1; x <= radialSegments; ++x) {
+            // vertex
+            vertices.push(0, halfHeight * sign, 0);
+
+            // normal
+            normals.push(0, sign, 0);
+
+            // increase index
+            ++index;
+        }
+
+        // save the index of the last center vertex
+        let centerIndexEnd = index;
+
+        // now we generate the surrounding vertices, normals and uvs
+        for (let x = 0; x <= radialSegments; ++x) {
+            const u = x / radialSegments;
+            const theta = u * thetaLength + thetaStart;
+
+            const cosTheta = Math.cos(theta);
+            const sinTheta = Math.sin(theta);
+
+            // vertex
+            vertices.push(radius * sinTheta, halfHeight * sign, radius * cosTheta);
+
+            // normal
+            normals.push(0, sign, 0);
+
+            // increase index
+            ++index;
+        }
+
+        // generate indices
+        for (let x = 0; x < radialSegments; ++x) {
+            const c = centerIndexStart + x;
+            const i = centerIndexEnd + x;
+
+            if (top === true) {
+                indices.push(i, i + 1, c); // face top
+            } else {
+                indices.push(i + 1, i, c); // face bottom
+            }
+        }
+    }
+}

+ 8 - 2
src/mol-geo/primitive/icosahedron.ts

@@ -23,6 +23,12 @@ const indices = [
      4, 9, 5, 	2, 4, 11,	6, 2, 10,	8, 6, 7,	9, 8, 1
 ];
 
-export default function Icosahedron(radius: number, detail: number) {
-    return Polyhedron(vertices, indices, radius, detail)
+export const DefaultIcosahedronProps = {
+    radius: 1,
+    detail: 0
+}
+export type IcosahedronProps = typeof DefaultIcosahedronProps
+
+export default function Icosahedron(props?: Partial<IcosahedronProps>) {
+    return Polyhedron(vertices, indices, { ...DefaultIcosahedronProps, ...props })
 }

+ 13 - 4
src/mol-geo/primitive/polyhedron.ts

@@ -9,9 +9,14 @@
 import { Vec3 } from 'mol-math/linear-algebra'
 import { computeVertexNormals, appplyRadius } from '../util'
 
-export default function Polyhedron(_vertices: Helpers.NumberArray, _indices: Helpers.NumberArray, radius: number, detail: number) {
-    radius = radius || 1;
-    detail = detail || 0;
+export const DefaultPolyhedronProps = {
+    radius: 1,
+    detail: 0
+}
+export type PolyhedronProps = typeof DefaultPolyhedronProps
+
+export default function Polyhedron(_vertices: Helpers.NumberArray, _indices: Helpers.NumberArray, props?: Partial<PolyhedronProps>) {
+    const { radius, detail } = { ...DefaultPolyhedronProps, ...props }
 
     const vertices: number[] = [];
     const indices: number[] = [];
@@ -26,7 +31,11 @@ export default function Polyhedron(_vertices: Helpers.NumberArray, _indices: Hel
     computeVertexNormals(vertices, normals)
     // this.normalizeNormals(); // smooth normals
 
-    return { vertices, indices, normals }
+    return {
+        vertices: new Float32Array(vertices),
+        normals: new Float32Array(normals),
+        indices: new Uint32Array(indices)
+    }
 
     // helper functions
 

+ 23 - 15
src/mol-geo/representation/structure/index.ts

@@ -8,13 +8,14 @@ import { AtomGroup, AtomSet, Structure, Unit } from 'mol-model/structure';
 import { RenderObject } from 'mol-gl/renderer';
 import { EquivalenceClasses } from 'mol-data/util';
 import { OrderedSet } from 'mol-data/int'
+import { Task } from 'mol-task'
 
 export interface RepresentationProps {
 
 }
 
 export interface UnitRepresentation {
-    create: (unit: Unit, atomGroup: AtomGroup, props: RepresentationProps) => RenderObject[],
+    create: (unit: Unit, atomGroup: AtomGroup, props?: Partial<RepresentationProps>) => Task<RenderObject[]>,
     update: (props: RepresentationProps) => boolean,
 }
 
@@ -25,27 +26,34 @@ export interface UnitRepresentation {
 
 export class StructureRepresentation {
     // map: uint.id -> atomGroup.hashCode[]
+    constructor(private repr: UnitRepresentation) {
+        // this.repr = props.representation();
+    }
+    create(structure: Structure) {
+        return Task.create('S. repr.', async ctx => {
 
-    constructor () {
+            const { atoms, units } = structure;
+            const uniqueGroups = EquivalenceClasses<number, AtomGroup>(
+                AtomGroup.hashCode,
+                (a, b) => units[a.id].model.id === units[b.id].model.id && OrderedSet.areEqual(a.atoms, b.atoms));
 
-    }
-    create (structure: Structure, props: RepresentationProps) {
-        const { atoms, units } = structure;
-        const uniqueGroups = EquivalenceClasses<number, AtomGroup>(
-            AtomGroup.hashCode,
-            (a, b) => units[a.id].model.id === units[b.id].model.id && OrderedSet.areEqual(a.atoms, b.atoms));
+            for (let i = 0, _i = AtomSet.unitCount(atoms); i < _i; i++) {
+                const group = AtomSet.unitGetByIndex(atoms, i);
+                uniqueGroups.add(group.id, group);
 
-        for (let i = 0, _i = AtomSet.unitCount(atoms); i < _i; i++) {
-            const group = AtomSet.unitGetByIndex(atoms, i);
-            uniqueGroups.add(group.id, group);
+            }
 
-        }
+            const u = this.repr.create(units[0], 0 as any, 0 as any);
+            await ctx.update('Building units...');
+            await ctx.runChild(u);
 
-        //uniqueGroups.groups
+            //ctx.runChild(...)
+            //uniqueGroups.groups
 
-        return true
+            return true
+        });
     }
-    update (atoms: AtomSet, props: RepresentationProps) {
+    update(atoms: AtomSet, props: RepresentationProps) {
         // TODO check model.id, conformation.id, unit.id, atomGroup(.hashCode/.areEqual)
         return false
     }

+ 9 - 4
src/mol-geo/representation/structure/spacefill.ts

@@ -14,6 +14,7 @@ import { OrderedSet } from 'mol-data/int'
 import { Atom, AtomGroup, Unit } from 'mol-model/structure';
 import P from 'mol-model/structure/query/properties';
 import { RepresentationProps, UnitRepresentation } from './index';
+import { Task } from 'mol-task'
 
 export default function Spacefill(): UnitRepresentation {
     let vertices: Float32Array
@@ -24,13 +25,13 @@ export default function Spacefill(): UnitRepresentation {
     // unit: Unit, atomGroup: AtomGroup
 
     return {
-        create: (unit: Unit, atomGroup: AtomGroup, props: RepresentationProps) => {
+        create: (unit: Unit, atomGroup: AtomGroup, props: Partial<RepresentationProps> = {}) => Task.create('Spacefill', async ctx => {
             const atomCount = OrderedSet.size(atomGroup.atoms)
 
             const l = Atom.Location();
             l.unit = unit;
 
-            const sphere = Icosahedron(1, 0)
+            const sphere = Icosahedron({ radius: 1, detail: 0 })
             const vertexCount = sphere.vertices.length / 3
 
             vertices = new Float32Array(atomCount * vertexCount * 3)
@@ -53,7 +54,11 @@ export default function Spacefill(): UnitRepresentation {
                     Vec3.toArray(v, vertices, i * vertexCount * 3 + j * 3)
                 }
 
-                normals.set(sphere.normals, i * vertexCount * 3)
+                normals.set(sphere.normals, i * vertexCount * 3);
+
+                if (i % 100 === 0 && ctx.shouldUpdate) {
+                    await ctx.update({ message: 'Spacefill', current: i, max: atomCount });
+                }
             }
 
             const transformArray = new Float32Array(16)
@@ -78,7 +83,7 @@ export default function Spacefill(): UnitRepresentation {
             renderObjects.push(spheres)
 
             return renderObjects
-        },
+        }),
         update: (props: RepresentationProps) => false
     }
 }