From 8bdbe950759a70cd8847089e348ff33d208116b3 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Thu, 21 Mar 2019 14:11:51 -0700 Subject: [PATCH] ply provider params for use in plugin --- src/mol-model-formats/shape/ply.ts | 86 ++++++++++++++----- src/mol-model/shape/provider.ts | 1 + src/mol-plugin/state/transforms/model.ts | 1 - .../state/transforms/representation.ts | 4 +- 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/mol-model-formats/shape/ply.ts b/src/mol-model-formats/shape/ply.ts index 4eb687020..e5488ea98 100644 --- a/src/mol-model-formats/shape/ply.ts +++ b/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 } diff --git a/src/mol-model/shape/provider.ts b/src/mol-model/shape/provider.ts index 2cd0950db..dea45e8ba 100644 --- a/src/mol-model/shape/provider.ts +++ b/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> } \ No newline at end of file diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 1a6158d7b..5f890882a 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/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' } diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts index 0b11a2870..0392341d7 100644 --- a/src/mol-plugin/state/transforms/representation.ts +++ b/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); -- GitLab