|
@@ -8,7 +8,7 @@
|
|
|
import { ValueCell } from 'mol-util/value-cell'
|
|
|
|
|
|
import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
|
|
|
-import { Unit, Element } from 'mol-model/structure';
|
|
|
+import { Unit, Element, Bond } from 'mol-model/structure';
|
|
|
import { UnitsRepresentation, DefaultStructureProps } from './index';
|
|
|
import { Task } from 'mol-task'
|
|
|
import { createTransforms } from './utils';
|
|
@@ -21,20 +21,21 @@ import { MeshBuilder } from '../../shape/mesh-builder';
|
|
|
import { Vec3, Mat4 } from 'mol-math/linear-algebra';
|
|
|
import { createUniformColor } from '../../util/color-data';
|
|
|
import { defaults } from 'mol-util';
|
|
|
-import { Loci } from 'mol-model/loci';
|
|
|
-import { FlagAction, createEmptyFlags } from '../../util/flag-data';
|
|
|
+import { Loci, isEveryLoci } from 'mol-model/loci';
|
|
|
+import { FlagAction, applyFlagAction, createFlags } from '../../util/flag-data';
|
|
|
|
|
|
function createBondMesh(unit: Unit, mesh?: Mesh) {
|
|
|
return Task.create('Cylinder mesh', async ctx => {
|
|
|
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
|
|
|
|
|
|
const elements = unit.elements;
|
|
|
- const { count, offset, neighbor } = unit.bonds;
|
|
|
-
|
|
|
- if (!count) return Mesh.createEmpty(mesh)
|
|
|
+ const bonds = unit.bonds
|
|
|
+ const { edgeCount, a, b } = bonds
|
|
|
+
|
|
|
+ if (!edgeCount) return Mesh.createEmpty(mesh)
|
|
|
|
|
|
// TODO calculate vertextCount properly
|
|
|
- const vertexCount = 32 * count
|
|
|
+ const vertexCount = 32 * edgeCount
|
|
|
const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh)
|
|
|
|
|
|
const va = Vec3.zero()
|
|
@@ -46,34 +47,23 @@ function createBondMesh(unit: Unit, mesh?: Mesh) {
|
|
|
const l = Element.Location()
|
|
|
l.unit = unit
|
|
|
|
|
|
- for (let j = 0; j < offset.length - 1; ++j) {
|
|
|
- const start = offset[j]
|
|
|
- const end = offset[j + 1]
|
|
|
-
|
|
|
- if (end <= start) continue
|
|
|
-
|
|
|
- const aI = elements[j]
|
|
|
- va[0] = x(aI)
|
|
|
- va[1] = y(aI)
|
|
|
- va[2] = z(aI)
|
|
|
- for (let _bI = start; _bI < end; ++_bI) {
|
|
|
- const bI = elements[neighbor[_bI]]
|
|
|
- if (bI > aI) continue
|
|
|
-
|
|
|
- vb[0] = x(bI)
|
|
|
- vb[1] = y(bI)
|
|
|
- vb[2] = z(bI)
|
|
|
-
|
|
|
- Vec3.scale(vt, Vec3.add(vt, va, vb), 0.5)
|
|
|
- Vec3.makeRotation(m, Vec3.create(0, 1, 0), Vec3.sub(vb, vb, va))
|
|
|
- Mat4.setTranslation(m, vt)
|
|
|
-
|
|
|
- meshBuilder.setId(j)
|
|
|
- meshBuilder.addCylinder(m, { radiusTop: 0.2, radiusBottom: 0.2 })
|
|
|
- }
|
|
|
-
|
|
|
- if (j % 10000 === 0 && ctx.shouldUpdate) {
|
|
|
- await ctx.update({ message: 'Cylinder mesh', current: j, max: count });
|
|
|
+ for (let edgeIndex = 0, _eI = edgeCount * 2; edgeIndex < _eI; ++edgeIndex) {
|
|
|
+ const aI = elements[a[edgeIndex]], bI = elements[b[edgeIndex]];
|
|
|
+ // each edge is included twice because of the "adjacency list" structure
|
|
|
+ // keep only the 1st occurence.
|
|
|
+ if (aI >= bI) continue;
|
|
|
+ va[0] = x(aI); va[1] = y(aI); va[2] = z(aI)
|
|
|
+ vb[0] = x(bI); vb[1] = y(bI); vb[2] = z(bI)
|
|
|
+
|
|
|
+ Vec3.scale(vt, Vec3.add(vt, va, vb), 0.5)
|
|
|
+ Vec3.makeRotation(m, Vec3.create(0, 1, 0), Vec3.sub(vb, vb, va))
|
|
|
+ Mat4.setTranslation(m, vt)
|
|
|
+
|
|
|
+ meshBuilder.setId(edgeIndex)
|
|
|
+ meshBuilder.addCylinder(m, { radiusTop: 0.2, radiusBottom: 0.2 })
|
|
|
+
|
|
|
+ if (edgeIndex % 10000 === 0 && ctx.shouldUpdate) {
|
|
|
+ await ctx.update({ message: 'Cylinder mesh', current: edgeIndex, max: edgeCount });
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -88,7 +78,7 @@ export const DefaultBondProps = {
|
|
|
}
|
|
|
export type BondProps = Partial<typeof DefaultBondProps>
|
|
|
|
|
|
-export default function Bond(): UnitsRepresentation<BondProps> {
|
|
|
+export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps> {
|
|
|
const renderObjects: RenderObject[] = []
|
|
|
let cylinders: MeshRenderObject
|
|
|
let currentProps: typeof DefaultBondProps
|
|
@@ -105,7 +95,12 @@ export default function Bond(): UnitsRepresentation<BondProps> {
|
|
|
renderObjects.length = 0 // clear
|
|
|
currentGroup = group
|
|
|
|
|
|
- mesh = await createBondMesh(group.units[0]).runAsChild(ctx, 'Computing bond mesh')
|
|
|
+ const unit = group.units[0]
|
|
|
+ const elementCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0
|
|
|
+ const instanceCount = group.units.length
|
|
|
+
|
|
|
+ mesh = await createBondMesh(unit).runAsChild(ctx, 'Computing bond mesh')
|
|
|
+
|
|
|
// console.log(mesh)
|
|
|
// vertexMap = VertexMap.fromMesh(mesh)
|
|
|
|
|
@@ -116,9 +111,7 @@ export default function Bond(): UnitsRepresentation<BondProps> {
|
|
|
const color = createUniformColor({ value: 0xFF0000 })
|
|
|
|
|
|
await ctx.update('Computing bond flags');
|
|
|
- const flag = createEmptyFlags()
|
|
|
-
|
|
|
- const instanceCount = group.units.length
|
|
|
+ const flag = createFlags(instanceCount * elementCount)
|
|
|
|
|
|
const values: MeshValues = {
|
|
|
...getMeshData(mesh),
|
|
@@ -129,7 +122,7 @@ export default function Bond(): UnitsRepresentation<BondProps> {
|
|
|
|
|
|
uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
|
|
|
uInstanceCount: ValueCell.create(instanceCount),
|
|
|
- uElementCount: ValueCell.create(group.elements.length),
|
|
|
+ uElementCount: ValueCell.create(elementCount),
|
|
|
|
|
|
elements: mesh.indexBuffer,
|
|
|
|
|
@@ -168,17 +161,51 @@ export default function Bond(): UnitsRepresentation<BondProps> {
|
|
|
})
|
|
|
},
|
|
|
getLoci(pickingId: PickingId) {
|
|
|
- // const { objectId, instanceId, elementId } = pickingId
|
|
|
- // if (cylinders.id === objectId) {
|
|
|
- // const unit = currentGroup.units[instanceId]
|
|
|
- // const elements = SortedArray.ofSingleton(currentGroup.elements[elementId])
|
|
|
- // return Element.Loci([{ unit, elements }])
|
|
|
- // }
|
|
|
+ const { objectId, instanceId, elementId } = pickingId
|
|
|
+ const unit = currentGroup.units[instanceId]
|
|
|
+ if (cylinders.id === objectId && Unit.isAtomic(unit)) {
|
|
|
+ return Bond.Loci([{
|
|
|
+ aUnit: unit,
|
|
|
+ aIndex: unit.bonds.a[elementId],
|
|
|
+ bUnit: unit,
|
|
|
+ bIndex: unit.bonds.b[elementId]
|
|
|
+ }])
|
|
|
+ }
|
|
|
return null
|
|
|
},
|
|
|
applyFlags(loci: Loci, action: FlagAction) {
|
|
|
- currentGroup
|
|
|
- // TODO
|
|
|
+ const group = currentGroup
|
|
|
+ const tFlag = cylinders.values.tFlag
|
|
|
+ const unit = group.units[0]
|
|
|
+ if (!Unit.isAtomic(unit)) return
|
|
|
+
|
|
|
+ const elementCount = unit.bonds.edgeCount * 2
|
|
|
+ const instanceCount = group.units.length
|
|
|
+
|
|
|
+ let changed = false
|
|
|
+ const array = tFlag.ref.value.array
|
|
|
+ if (isEveryLoci(loci)) {
|
|
|
+ applyFlagAction(array, 0, elementCount * instanceCount, action)
|
|
|
+ changed = true
|
|
|
+ } else if (Bond.isLoci(loci)) {
|
|
|
+ for (const b of loci.bonds) {
|
|
|
+ const unitIdx = Unit.findUnitById(b.aUnit.id, group.units)
|
|
|
+ if (unitIdx !== -1) {
|
|
|
+ const _idx = unit.bonds.getEdgeIndex(b.aIndex, b.bIndex)
|
|
|
+ if (_idx !== -1) {
|
|
|
+ const idx = _idx
|
|
|
+ if (applyFlagAction(array, idx, idx + 1, action) && !changed) {
|
|
|
+ changed = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (changed) {
|
|
|
+ ValueCell.update(tFlag, tFlag.ref.value)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|