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

wip, shape

parent ee3fdc5b
Branches
Tags
No related merge requests found
...@@ -5,28 +5,31 @@ ...@@ -5,28 +5,31 @@
*/ */
import { Color } from 'mol-util/color'; import { Color } from 'mol-util/color';
import { UUID, ValueCell } from 'mol-util'; import { UUID } from 'mol-util';
import { OrderedSet } from 'mol-data/int'; import { OrderedSet } from 'mol-data/int';
import { Geometry } from 'mol-geo/geometry/geometry'; import { Geometry } from 'mol-geo/geometry/geometry';
import { Mat4 } from 'mol-math/linear-algebra';
export interface Shape<G extends Geometry = Geometry> { export interface Shape<G extends Geometry = Geometry> {
readonly id: UUID readonly id: UUID
readonly name: string readonly name: string
readonly geometry: G readonly geometry: G
readonly colors: ValueCell<Color[]> readonly transforms: Mat4[]
readonly labels: ValueCell<string[]>
readonly groupCount: number readonly groupCount: number
getColor(groupId: number): Color
getLabel(groupId: number): string
} }
export namespace Shape { export namespace Shape {
export function create<G extends Geometry>(name: string, geometry: G, colors: Color[], labels: string[]): Shape<G> { export function create<G extends Geometry>(name: string, geometry: G, getColor: Shape['getColor'], getLabel: Shape['getLabel'], transforms?: Mat4[]): Shape<G> {
return { return {
id: UUID.create22(), id: UUID.create22(),
name, name,
geometry, geometry,
transforms: transforms || [Mat4.identity()],
get groupCount() { return Geometry.getGroupCount(geometry) }, get groupCount() { return Geometry.getGroupCount(geometry) },
colors: ValueCell.create(colors), getColor,
labels: ValueCell.create(labels), getLabel
} }
} }
......
/** /**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
...@@ -12,14 +12,18 @@ import { ValueCell } from 'mol-util'; ...@@ -12,14 +12,18 @@ import { ValueCell } from 'mol-util';
import { Shape } from 'mol-model/shape'; import { Shape } from 'mol-model/shape';
import { OrderedSet, Interval } from 'mol-data/int'; import { OrderedSet, Interval } from 'mol-data/int';
import { ParamDefinition as PD } from 'mol-util/param-definition'; import { ParamDefinition as PD } from 'mol-util/param-definition';
import { createIdentityTransform } from 'mol-geo/geometry/transform-data'; import { createTransform, TransformData } from 'mol-geo/geometry/transform-data';
import { PickingId } from 'mol-geo/geometry/picking'; import { PickingId } from 'mol-geo/geometry/picking';
import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data'; import { MarkerAction, applyMarkerAction, createMarkers } from 'mol-geo/geometry/marker-data';
import { LocationIterator } from 'mol-geo/util/location-iterator'; import { LocationIterator } from 'mol-geo/util/location-iterator';
import { createEmptyTheme, Theme } from 'mol-theme/theme'; import { createEmptyTheme, Theme } from 'mol-theme/theme';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry'; import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry';
import { ShapeGroupColorTheme } from 'mol-theme/color/shape-group'; import { ShapeGroupColorTheme } from 'mol-theme/color/shape-group';
import { createColors } from 'mol-geo/geometry/color-data';
import { VisualUpdateState } from 'mol-repr/util';
import { Mat4 } from 'mol-math/linear-algebra';
import { Visual } from 'mol-repr/visual';
export interface ShapeRepresentation<D, G extends Geometry, P extends Geometry.Params<G>> extends Representation<D, P> { } export interface ShapeRepresentation<D, G extends Geometry, P extends Geometry.Params<G>> extends Representation<D, P> { }
...@@ -35,39 +39,83 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa ...@@ -35,39 +39,83 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
let currentParams: P let currentParams: P
let locationIt: LocationIterator let locationIt: LocationIterator
const updateState = VisualUpdateState.create()
function prepareUpdate(shape?: Shape<G>) {
VisualUpdateState.reset(updateState)
if (!shape && !_shape) {
console.error('no shape given')
return
} else if (shape && !_shape) {
console.log('first shape')
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
} else if (shape && _shape && shape.id !== _shape.id) {
console.log('new shape')
// assume that ValueCells in geometry of new shape where re-used from the old one
updateState.updateColor = true
updateState.updateTransform = true
} else if (!shape) {
console.log('only props')
// nothing to set
} else {
console.warn('unexpected state')
}
if (updateState.updateTransform) {
updateState.updateColor = true
}
}
function createOrUpdate(props: Partial<PD.Values<P>> = {}, data?: D) { function createOrUpdate(props: Partial<PD.Values<P>> = {}, data?: D) {
currentProps = Object.assign(currentProps, props) const newProps = Object.assign(currentProps, props)
const shape = data ? getShape(data, currentProps, _shape) : undefined const shape = data ? getShape(data, newProps, _shape) : undefined // TODO support async getShape
return Task.create('ShapeRepresentation.create', async runtime => { return Task.create('ShapeRepresentation.create', async runtime => {
if (!shape && !_shape) { prepareUpdate(shape)
console.error('no shape given')
return
} else if (shape && !_shape) {
console.log('first shape')
} else if (shape && _shape && shape.id === _shape.id) { if (shape) {
console.log('same shape') _shape = shape
_theme.color = ShapeGroupColorTheme({ shape: _shape }, {})
}
} else if (shape && _shape && shape.id !== _shape.id) { if (updateState.createNew) {
console.log('new shape') renderObjects.length = 0
locationIt = ShapeGroupIterator.fromShape(_shape)
const transform = createShapeTransform(_shape.transforms)
const values = geometryUtils.createValues(_shape.geometry, transform, locationIt, _theme, newProps)
const state = geometryUtils.createRenderableState(newProps)
_renderObject = createRenderObject(_shape.geometry.kind, values, state)
if (_renderObject) renderObjects.push(_renderObject)
} else { } else {
console.log('only props') if (!_renderObject) {
throw new Error('expected renderObject to be available')
}
} if (updateState.updateTransform) {
// console.log('update transform')
createShapeTransform(_shape.transforms, _renderObject.values)
locationIt = ShapeGroupIterator.fromShape(_shape)
const { instanceCount, groupCount } = locationIt
createMarkers(instanceCount * groupCount, _renderObject.values)
}
if (shape) _shape = shape if (updateState.updateColor) {
renderObjects.length = 0 // console.log('update color')
locationIt = ShapeGroupIterator.fromShape(_shape) createColors(locationIt, _theme.color, _renderObject.values)
_theme.color = ShapeGroupColorTheme({ shape: _shape }, {}) }
const transform = createIdentityTransform() geometryUtils.updateValues(_renderObject.values, newProps)
const values = geometryUtils.createValues(_shape.geometry, transform, locationIt, _theme, currentProps) geometryUtils.updateRenderableState(_renderObject.state, newProps)
const state = geometryUtils.createRenderableState(currentProps) }
_renderObject = createRenderObject(_shape.geometry.kind, values, state) currentProps = newProps
if (_renderObject) renderObjects.push(_renderObject)
updated.next(version++) updated.next(version++)
}); });
} }
...@@ -115,14 +163,16 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa ...@@ -115,14 +163,16 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
return changed return changed
}, },
setState(state: Partial<Representation.State>) { setState(state: Partial<Representation.State>) {
if (state.visible !== undefined) renderObjects.forEach(ro => ro.state.visible = !!state.visible) if (_renderObject) {
if (state.pickable !== undefined) renderObjects.forEach(ro => ro.state.pickable = !!state.pickable) if (state.visible !== undefined) Visual.setVisibility(_renderObject, state.visible)
// TODO state.transform if (state.pickable !== undefined) Visual.setPickable(_renderObject, state.pickable)
if (state.transform !== undefined) Visual.setTransform(_renderObject, state.transform)
}
Representation.updateState(_state, state) Representation.updateState(_state, state)
}, },
setTheme(theme: Theme) { setTheme(theme: Theme) {
_theme = theme console.warn('The `ShapeRepresentation` theme is fixed to `ShapeGroupColorTheme`. Colors are taken from `Shape.getColor`.')
}, },
destroy() { destroy() {
// TODO // TODO
...@@ -132,9 +182,17 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa ...@@ -132,9 +182,17 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
} }
} }
function createShapeTransform(transforms: Mat4[], transformData?: TransformData) {
const transformArray = transformData && transformData.aTransform.ref.value.length >= transforms.length * 16 ? transformData.aTransform.ref.value : new Float32Array(transforms.length * 16)
for (let i = 0, il = transforms.length; i < il; ++i) {
Mat4.toArray(transforms[i], transformArray, i * 16)
}
return createTransform(transformArray, transforms.length, transformData)
}
export namespace ShapeGroupIterator { export namespace ShapeGroupIterator {
export function fromShape(shape: Shape): LocationIterator { export function fromShape(shape: Shape): LocationIterator {
const instanceCount = 1 const instanceCount = shape.transforms.length
const location = Shape.Location(shape) const location = Shape.Location(shape)
const getLocation = (groupIndex: number) => { const getLocation = (groupIndex: number) => {
location.group = groupIndex location.group = groupIndex
......
...@@ -146,8 +146,6 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry ...@@ -146,8 +146,6 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry
throw new Error('expected renderObject to be available') throw new Error('expected renderObject to be available')
} }
locationIt.reset()
if (updateState.updateTransform) { if (updateState.updateTransform) {
// console.log('update transform') // console.log('update transform')
locationIt = createLocationIterator(newStructureGroup.group) locationIt = createLocationIterator(newStructureGroup.group)
......
...@@ -26,7 +26,7 @@ export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: PD.Values<Sha ...@@ -26,7 +26,7 @@ export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: PD.Values<Sha
granularity: 'group', granularity: 'group',
color: (location: Location): Color => { color: (location: Location): Color => {
if (Shape.isLocation(location)) { if (Shape.isLocation(location)) {
return location.shape.colors.ref.value[location.group] return location.shape.getColor(location.group)
} }
return DefaultColor return DefaultColor
}, },
......
...@@ -36,7 +36,7 @@ export function labelFirst(loci: Loci): string { ...@@ -36,7 +36,7 @@ export function labelFirst(loci: Loci): string {
case 'group-loci': case 'group-loci':
const g = loci.groups[0] const g = loci.groups[0]
if (g) { if (g) {
return loci.shape.labels.ref.value[OrderedSet.getAt(g.ids, 0)] return loci.shape.getLabel(OrderedSet.getAt(g.ids, 0))
} else { } else {
return 'Unknown' return 'Unknown'
} }
......
...@@ -52,10 +52,14 @@ builderState.currentGroup = 0 ...@@ -52,10 +52,14 @@ builderState.currentGroup = 0
MeshBuilder.addPrimitive(builderState, t, sphere) MeshBuilder.addPrimitive(builderState, t, sphere)
const mesh = MeshBuilder.getMesh(builderState) const mesh = MeshBuilder.getMesh(builderState)
const myData = { mesh, colors: [ColorNames.aquamarine], labels: ['FooBar'] } const myData = { mesh, colors: [ColorNames.aquamarine], labels: ['FooBaz'] }
type MyData = typeof myData type MyData = typeof myData
function getShape(data: MyData) { function getShape(data: MyData) {
return Shape.create('test', data.mesh, data.colors, data.labels) return Shape.create(
'test', data.mesh,
(groupId: number) => data.colors[groupId],
(groupId: number) => data.labels[groupId]
)
} }
const repr = ShapeRepresentation(getShape, Mesh.Utils) const repr = ShapeRepresentation(getShape, Mesh.Utils)
...@@ -66,4 +70,9 @@ async function add() { ...@@ -66,4 +70,9 @@ async function add() {
canvas3d.add(repr) canvas3d.add(repr)
canvas3d.resetCamera() canvas3d.resetCamera()
} }
add() add()
\ No newline at end of file
setTimeout(async () => {
myData.colors[0] = ColorNames.darkmagenta
await repr.createOrUpdate({}, myData).run()
}, 2000)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment