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

smarter ply-shape repr update

parent f9a13782
No related branches found
No related tags found
No related merge requests found
......@@ -17,10 +17,53 @@ import { arrayMax, fillSerial } from 'mol-util/array';
import { Column } from 'mol-data/db';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { ColorNames } from 'mol-util/color/tables';
import { deepClone } from 'mol-util/object';
// 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) {
function createPlyShapeParams(vertex?: PlyTable) {
const options: [string, string][] = [['', '']]
const defaultValues = { group: '', red: '', green: '', blue: '' }
if (vertex) {
for (let i = 0, il = vertex.propertyNames.length; i < il; ++i) {
const name = vertex.propertyNames[i]
options.push([ name, name ])
}
// TODO hardcoded as convenience for data provided by MegaMol
if (vertex.propertyNames.includes('atomid')) defaultValues.group = 'atomid'
if (vertex.propertyNames.includes('red')) defaultValues.red = 'red'
if (vertex.propertyNames.includes('green')) defaultValues.green = 'green'
if (vertex.propertyNames.includes('blue')) defaultValues.blue = 'blue'
}
return {
...Mesh.Params,
coloring: PD.MappedStatic(defaultValues.red && defaultValues.green && defaultValues.blue ? 'vertex' : 'uniform', {
vertex: PD.Group({
red: PD.Select(defaultValues.red, options, { label: 'Red Property' }),
green: PD.Select(defaultValues.green, options, { label: 'Green Property' }),
blue: PD.Select(defaultValues.blue, options, { label: 'Blue Property' }),
}, { isFlat: true }),
uniform: PD.Group({
color: PD.Color(ColorNames.grey)
}, { isFlat: true })
}),
grouping: PD.MappedStatic(defaultValues.group ? 'vertex' : 'none', {
vertex: PD.Group({
group: PD.Select(defaultValues.group, options, { label: 'Group Property' }),
}, { isFlat: true }),
none: PD.Group({ })
}),
}
}
export const PlyShapeParams = createPlyShapeParams()
export type PlyShapeParams = typeof PlyShapeParams
async function getMesh(ctx: RuntimeContext, vertex: PlyTable, face: PlyList, groupIds: ArrayLike<number>, mesh?: Mesh) {
const builderState = MeshBuilder.createState(vertex.rowCount, vertex.rowCount / 4, mesh)
const { vertices, normals, indices, groups } = builderState
......@@ -36,7 +79,7 @@ async function getPlyMesh(ctx: RuntimeContext, vertex: PlyTable, face: PlyList,
const hasNormals = !!nx && !!ny && !!nz
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}` })
if (i % 100000 === 0 && ctx.shouldUpdate) await ctx.update({ current: i, max: il, message: `adding vertex ${i}` })
ChunkedArray.add3(vertices, x.value(i), y.value(i), z.value(i))
if (hasNormals) ChunkedArray.add3(normals, nx!.value(i), ny!.value(i), nz!.value(i));
......@@ -44,10 +87,13 @@ async function getPlyMesh(ctx: RuntimeContext, vertex: PlyTable, face: PlyList,
}
for (let i = 0, il = face.rowCount; i < il; ++i) {
if (i % 10000 === 0 && ctx.shouldUpdate) await ctx.update({ current: i, max: il, message: `adding face ${i}` })
if (i % 100000 === 0 && ctx.shouldUpdate) await ctx.update({ current: i, max: il, message: `adding face ${i}` })
const { entries } = face.value(i)
ChunkedArray.add3(indices, entries[0], entries[1], entries[2])
const { entries, count } = face.value(i)
if (count === 3) {
ChunkedArray.add3(indices, entries[0], entries[1], entries[2])
}
// TODO support quadriliterals
}
const m = MeshBuilder.getMesh(builderState);
......@@ -57,24 +103,25 @@ async function getPlyMesh(ctx: RuntimeContext, vertex: PlyTable, face: PlyList,
return m
}
function getGrouping(count: number, column?: Column<number>) {
const ids = column ? column.toArray({ array: Int32Array }) : fillSerial(new Uint32Array(count))
const int = Column.Schema.int
type Grouping = { ids: ArrayLike<number>, map: ArrayLike<number> }
function getGrouping(vertex: PlyTable, props: PD.Values<PlyShapeParams>): Grouping {
const { grouping } = props
const { rowCount } = vertex
const column = grouping.name === 'vertex' ? vertex.getProperty(grouping.params.group) : undefined
const ids = column ? column.toArray({ array: Uint32Array }) : fillSerial(new Uint32Array(rowCount))
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 }
}
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 { coloring, grouping } = props
const vertex = plyFile.getElement('vertex') as PlyTable
if (!vertex) throw new Error('missing vertex element')
type Coloring = { red: Column<number>, green: Column<number>, blue: Column<number> }
function getColoring(vertex: PlyTable, props: PD.Values<PlyShapeParams>): Coloring {
const { coloring } = props
const { rowCount } = vertex
const int = Column.Schema.int
let red: Column<number>, green: Column<number>, blue: Column<number>
if (coloring.name === 'vertex') {
......@@ -88,16 +135,14 @@ async function getShape(ctx: RuntimeContext, plyFile: PlyFile, props: PD.Values<
blue = Column.ofConst(b, rowCount, int)
}
const face = plyFile.getElement('face') as PlyList
if (!face) throw new Error('missing face element')
const { ids, map } = getGrouping(vertex.rowCount, grouping.name === 'vertex' ? vertex.getProperty(grouping.params.group) : undefined)
return { red, green, blue }
}
const mesh = await getPlyMesh(ctx, vertex, face, ids, shape && shape.geometry)
function createShape(plyFile: PlyFile, mesh: Mesh, coloring: Coloring, grouping: Grouping) {
const { red, green, blue } = coloring
const { ids, map } = grouping
return Shape.create(
'test', plyFile, mesh,
'ply-mesh', plyFile, mesh,
(groupId: number) => {
const idx = map[groupId]
return Color.fromRgb(red.value(idx), green.value(idx), blue.value(idx))
......@@ -109,57 +154,64 @@ async function getShape(ctx: RuntimeContext, plyFile: PlyFile, props: PD.Values<
)
}
function createPlyShapeParams(vertex?: PlyTable) {
const options: [string, string][] = [['', '']]
const defaultValues = { group: '', red: '', green: '', blue: '' }
if (vertex) {
for (let i = 0, il = vertex.propertyNames.length; i < il; ++i) {
const name = vertex.propertyNames[i]
options.push([ name, name ])
function makeShapeGetter() {
let _plyFile: PlyFile | undefined
let _props: PD.Values<PlyShapeParams> | undefined
let _shape: Shape<Mesh>
let _mesh: Mesh
let _coloring: Coloring
let _grouping: Grouping
const getShape = async (ctx: RuntimeContext, plyFile: PlyFile, props: PD.Values<PlyShapeParams>, shape?: Shape<Mesh>) => {
const vertex = plyFile.getElement('vertex') as PlyTable
if (!vertex) throw new Error('missing vertex element')
const face = plyFile.getElement('face') as PlyList
if (!face) throw new Error('missing face element')
let newMesh = false
let newColor = false
if (!_plyFile || _plyFile !== plyFile) {
newMesh = true
}
// TODO harcoded as convenience for data provided by MegaMol
if (vertex.propertyNames.includes('atomid')) defaultValues.group = 'atomid'
if (!_props || !PD.isParamEqual(PlyShapeParams.grouping, _props.grouping, props.grouping)) {
newMesh = true
}
if (vertex.propertyNames.includes('red')) defaultValues.red = 'red'
if (vertex.propertyNames.includes('green')) defaultValues.green = 'green'
if (vertex.propertyNames.includes('blue')) defaultValues.blue = 'blue'
}
if (!_props || !PD.isParamEqual(PlyShapeParams.coloring, _props.coloring, props.coloring)) {
newColor = true
}
return {
...Mesh.Params,
if (newMesh) {
_coloring = getColoring(vertex, props)
_grouping = getGrouping(vertex, props)
_mesh = await getMesh(ctx, vertex, face, _grouping.ids, shape && shape.geometry)
_shape = createShape(plyFile, _mesh, _coloring, _grouping)
} else if (newColor) {
_coloring = getColoring(vertex, props)
_shape = createShape(plyFile, _mesh, _coloring, _grouping)
}
coloring: PD.MappedStatic(defaultValues.red && defaultValues.green && defaultValues.blue ? 'vertex' : 'uniform', {
vertex: PD.Group({
red: PD.Select(defaultValues.red, options, { label: 'Red Property' }),
green: PD.Select(defaultValues.green, options, { label: 'Green Property' }),
blue: PD.Select(defaultValues.blue, options, { label: 'Blue Property' }),
}, { isFlat: true }),
uniform: PD.Group({
color: PD.Color(ColorNames.grey)
}, { isFlat: true })
}),
grouping: PD.MappedStatic(defaultValues.group ? 'vertex' : 'none', {
vertex: PD.Group({
group: PD.Select(defaultValues.group, options, { label: 'Group Property' }),
}, { isFlat: true }),
none: PD.Group({ })
}),
_plyFile = plyFile
_props = deepClone(props)
return _shape
}
return getShape
}
export const PlyShapeParams = createPlyShapeParams()
export type PlyShapeParams = typeof PlyShapeParams
export function shapeFromPly(source: PlyFile, params?: {}) {
return Task.create<ShapeProvider<PlyFile, Mesh, PlyShapeParams>>('Shape Provider', async ctx => {
return {
label: 'Mesh',
data: source,
params: createPlyShapeParams(source.getElement('vertex') as PlyTable),
getShape,
getShape: makeShapeGetter(),
geometryUtils: Mesh.Utils
}
})
}
\ No newline at end of file
......@@ -56,9 +56,7 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
updateState.createNew = true
} else if (shape && _shape && shape.id === _shape.id) {
// console.log('same shape')
// trigger color update when shape has not changed
updateState.updateColor = true
updateState.updateTransform = true
// nothing to set
} else if (shape && _shape && shape.id !== _shape.id) {
// console.log('new shape')
updateState.updateTransform = true
......
......@@ -284,7 +284,7 @@ export namespace ParamDefinition {
return true;
}
function isParamEqual(p: Any, a: any, b: any): boolean {
export function isParamEqual(p: Any, a: any, b: any): boolean {
if (a === b) return true;
if (!a) return !b;
if (!b) return !a;
......
......@@ -96,7 +96,7 @@ async function getShape(ctx: RuntimeContext, data: MyData, props: {}, shape?: Sh
const { centers, colors, labels, transforms } = data
const mesh = await getSphereMesh(ctx, centers, shape && shape.geometry)
const groupCount = centers.length / 3
return shape || Shape.create(
return Shape.create(
'test', data, mesh,
(groupId: number) => colors[groupId], // color: per group, same for instances
() => 1, // size: constant
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment