Browse Source

ply provider params for use in plugin

Alexander Rose 6 years ago
parent
commit
8bdbe95075

+ 63 - 23
src/mol-model-formats/shape/ply.ts

@@ -13,10 +13,14 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { Shape } from 'mol-model/shape';
 import { ChunkedArray } from 'mol-data/util';
-import { arrayMax } from 'mol-util/array';
+import { arrayMax, fillSerial } from 'mol-util/array';
+import { Column } from 'mol-data/db';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+
+// TODO support 'edge' and 'material' elements, see https://www.mathworks.com/help/vision/ug/the-ply-format.html
 
 async function getPlyMesh(ctx: RuntimeContext, vertex: PlyTable, face: PlyList, groupIds: ArrayLike<number>, mesh?: Mesh) {
-    const builderState = MeshBuilder.createState(face.rowCount, face.rowCount, mesh)
+    const builderState = MeshBuilder.createState(vertex.rowCount, vertex.rowCount / 4, mesh)
     const { vertices, normals, indices, groups } = builderState
 
     const x = vertex.getProperty('x')
@@ -27,7 +31,7 @@ async function getPlyMesh(ctx: RuntimeContext, vertex: PlyTable, face: PlyList,
     const nx = vertex.getProperty('nx')
     const ny = vertex.getProperty('ny')
     const nz = vertex.getProperty('nz')
-    if (!nx || !ny || !nz) throw new Error('missing normal properties')
+    if (!nx || !ny || !nz) throw new Error('missing normal properties') // TODO calculate normals when not provided
 
     for (let i = 0, il = vertex.rowCount; i < il; ++i) {
         if (i % 10000 === 0 && ctx.shouldUpdate) await ctx.update({ current: i, max: il, message: `adding vertex ${i}` })
@@ -46,53 +50,89 @@ async function getPlyMesh(ctx: RuntimeContext, vertex: PlyTable, face: PlyList,
     return MeshBuilder.getMesh(builderState);
 }
 
-async function getShape(ctx: RuntimeContext, plyFile: PlyFile, props: {}, shape?: Shape<Mesh>) {
-    await ctx.update('async creation of shape from  myData')
+function getGrouping(count: number, column?: Column<number>) {
+    const ids = column ? column.toArray({ array: Int32Array }) : fillSerial(new Uint32Array(count))
+    const maxId = arrayMax(ids) // assumes uint ids
+    const map = new Uint32Array(maxId + 1)
+    for (let i = 0, il = ids.length; i < il; ++i) map[ids[i]] = i
+    return { ids, map }
+}
 
-    const vertex = plyFile.getElement('vertex') as PlyTable
-    if (!vertex) throw new Error('missing vertex element')
+async function getShape(ctx: RuntimeContext, plyFile: PlyFile, props: PD.Values<PlyShapeParams>, shape?: Shape<Mesh>) {
+    await ctx.update('async creation of shape from ply file')
 
-    const atomid = vertex.getProperty('atomid')
-    if (!atomid) throw new Error('missing atomid property')
+    const { vertexProperties: vp } = props
 
-    const groupIds = atomid.toArray({ array: Int32Array })
-    const maxGroupId = arrayMax(groupIds) // assumes uint group ids
-    const groupIdMap = new Uint32Array(maxGroupId + 1)
-    for (let i = 0, il = groupIds.length; i < il; ++i) groupIdMap[groupIds[i]] = i
+    const vertex = plyFile.getElement('vertex') as PlyTable
+    if (!vertex) throw new Error('missing vertex element')
 
-    const red = vertex.getProperty('red')
-    const green = vertex.getProperty('green')
-    const blue = vertex.getProperty('blue')
+    const red = vertex.getProperty(vp.red)
+    const green = vertex.getProperty(vp.green)
+    const blue = vertex.getProperty(vp.blue)
     if (!red || !green || !blue) throw new Error('missing color properties')
 
     const face = plyFile.getElement('face') as PlyList
     if (!face) throw new Error('missing face element')
 
-    const mesh = await getPlyMesh(ctx, vertex, face, groupIds, shape && shape.geometry)
-    return shape || Shape.create(
+    const { ids, map } = getGrouping(vertex.rowCount, vertex.getProperty(vp.group))
+
+    const mesh = await getPlyMesh(ctx, vertex, face, ids, shape && shape.geometry)
+    return Shape.create(
         'test', plyFile, mesh,
         (groupId: number) => {
-            const idx = groupIdMap[groupId]
+            const idx = map[groupId]
             return Color.fromRgb(red.value(idx), green.value(idx), blue.value(idx))
         },
         () => 1, // size: constant
         (groupId: number) => {
-            return atomid.value(groupIdMap[groupId]).toString()
+            return ids[groupId].toString()
         }
     )
 }
 
 export const PlyShapeParams = {
-    ...Mesh.Params
+    ...Mesh.Params,
+
+    vertexProperties: PD.Group({
+        group: PD.Select('' as string, [['', '']]),
+        red: PD.Select('red' as string, [['red', 'red']]),
+        green: PD.Select('green' as string, [['green', 'green']]),
+        blue: PD.Select('blue' as string, [['blue', 'blue']]),
+    }, { isExpanded: true }),
 }
 export type PlyShapeParams = typeof PlyShapeParams
 
+
+export function getPlyShapeParams(plyFile: PlyFile) {
+    const params = PD.clone(PlyShapeParams)
+    const vertex = plyFile.getElement('vertex') as PlyTable
+    if (vertex) {
+        const options: [string, string][] = [['', '']]
+        for (let i = 0, il = vertex.propertyNames.length; i < il; ++i) {
+            const name = vertex.propertyNames[i]
+            options.push([ name, name ])
+        }
+        const vp = params.vertexProperties.params;
+        (vp.group as PD.Select<string>).options = options;
+        (vp.red as PD.Select<string>).options = options;
+        (vp.green as PD.Select<string>).options = options;
+        (vp.blue as PD.Select<string>).options = options;
+
+        // TODO harcoded as convenience for data provided by MegaMol
+        if (vertex.propertyNames.includes('atomid')) {
+            vp.group.defaultValue = 'atomid'
+            params.vertexProperties.defaultValue.group = 'atomid'
+        }
+    }
+    return params
+}
+
 export function shapeFromPly(source: PlyFile, params?: {}) {
-    return Task.create<ShapeProvider<PlyFile, Mesh, PlyShapeParams>>('Parse Shape Data', async ctx => {
-        console.log('source', source)
+    return Task.create<ShapeProvider<PlyFile, Mesh, PlyShapeParams>>('Shape Provider', async ctx => {
         return {
             label: 'Mesh',
             data: source,
+            params: getPlyShapeParams(source),
             getShape,
             geometryUtils: Mesh.Utils
         }

+ 1 - 0
src/mol-model/shape/provider.ts

@@ -10,6 +10,7 @@ import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry';
 export interface ShapeProvider<D, G extends Geometry, P extends Geometry.Params<G>> {
     label: string
     data: D
+    params: P
     getShape: ShapeGetter<D, G, P>
     geometryUtils: GeometryUtils<G>
 }

+ 0 - 1
src/mol-plugin/state/transforms/model.ts

@@ -339,7 +339,6 @@ function updateStructureFromQuery(query: QueryFn<Sel>, src: Structure, obj: SO.M
     return true;
 }
 
-
 namespace StructureComplexElement {
     export type Types = 'atomic-sequence' | 'water' | 'atomic-het' | 'spheres'
 }

+ 2 - 2
src/mol-plugin/state/transforms/representation.ts

@@ -484,7 +484,7 @@ const ShapeRepresentation3D = PluginStateTransform.BuiltIn({
     from: SO.Shape.Provider,
     to: SO.Shape.Representation3D,
     params: (a, ctx: PluginContext) => {
-        return a ? a.data.geometryUtils.Params : BaseGeometry.Params
+        return a ? a.data.params : BaseGeometry.Params
     }
 })({
     canAutoUpdate() {
@@ -492,7 +492,7 @@ const ShapeRepresentation3D = PluginStateTransform.BuiltIn({
     },
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('Shape Representation', async ctx => {
-            const props = { ...PD.getDefaultValues(a.data.geometryUtils.Params), params }
+            const props = { ...PD.getDefaultValues(a.data.params), params }
             const repr = ShapeRepresentation(a.data.getShape, a.data.geometryUtils)
             // TODO set initial state, repr.setState({})
             await repr.createOrUpdate(props, a.data.data).runInContext(ctx);