render-shape.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import './index.html'
  7. import { resizeCanvas } from '../../mol-canvas3d/util';
  8. import { Representation } from '../../mol-repr/representation';
  9. import { Canvas3D } from '../../mol-canvas3d/canvas3d';
  10. import { lociLabel } from '../../mol-theme/label';
  11. import { MarkerAction } from '../../mol-util/marker-action';
  12. import { EveryLoci } from '../../mol-model/loci';
  13. import { RuntimeContext, Progress } from '../../mol-task';
  14. import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
  15. import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
  16. import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
  17. import { Sphere } from '../../mol-geo/primitive/sphere';
  18. import { ColorNames } from '../../mol-util/color/names';
  19. import { Shape } from '../../mol-model/shape';
  20. import { ShapeRepresentation } from '../../mol-repr/shape/representation';
  21. const parent = document.getElementById('app')!
  22. parent.style.width = '100%'
  23. parent.style.height = '100%'
  24. const canvas = document.createElement('canvas')
  25. parent.appendChild(canvas)
  26. resizeCanvas(canvas, parent)
  27. const info = document.createElement('div')
  28. info.style.position = 'absolute'
  29. info.style.fontFamily = 'sans-serif'
  30. info.style.fontSize = '24pt'
  31. info.style.bottom = '20px'
  32. info.style.right = '20px'
  33. info.style.color = 'white'
  34. parent.appendChild(info)
  35. let prevReprLoci = Representation.Loci.Empty
  36. const canvas3d = Canvas3D.fromCanvas(canvas)
  37. canvas3d.animate()
  38. canvas3d.input.move.subscribe(({x, y}) => {
  39. const pickingId = canvas3d.identify(x, y)
  40. let label = ''
  41. if (pickingId) {
  42. const reprLoci = canvas3d.getLoci(pickingId)
  43. label = lociLabel(reprLoci.loci)
  44. if (!Representation.Loci.areEqual(prevReprLoci, reprLoci)) {
  45. canvas3d.mark(prevReprLoci, MarkerAction.RemoveHighlight)
  46. canvas3d.mark(reprLoci, MarkerAction.Highlight)
  47. prevReprLoci = reprLoci
  48. }
  49. } else {
  50. canvas3d.mark({ loci: EveryLoci }, MarkerAction.RemoveHighlight)
  51. prevReprLoci = Representation.Loci.Empty
  52. }
  53. info.innerText = label
  54. })
  55. /**
  56. * Create a mesh of spheres at given centers
  57. * - asynchronous (using async/await)
  58. * - progress tracking (via `ctx.update`)
  59. * - re-use storage from an existing mesh if given
  60. */
  61. async function getSphereMesh(ctx: RuntimeContext, centers: number[], mesh?: Mesh) {
  62. const builderState = MeshBuilder.createState(centers.length * 128, centers.length * 128 / 2, mesh)
  63. const t = Mat4.identity()
  64. const v = Vec3.zero()
  65. const sphere = Sphere(3)
  66. builderState.currentGroup = 0
  67. for (let i = 0, il = centers.length / 3; i < il; ++i) {
  68. // for production, calls to update should be guarded by `if (ctx.shouldUpdate)`
  69. await ctx.update({ current: i, max: il, message: `adding sphere ${i}` })
  70. builderState.currentGroup = i
  71. Mat4.setTranslation(t, Vec3.fromArray(v, centers, i * 3))
  72. MeshBuilder.addPrimitive(builderState, t, sphere)
  73. }
  74. return MeshBuilder.getMesh(builderState)
  75. }
  76. const myData = {
  77. centers: [0, 0, 0, 0, 3, 0, 1, 0 , 4],
  78. colors: [ColorNames.tomato, ColorNames.springgreen, ColorNames.springgreen],
  79. labels: ['Sphere 0, Instance A', 'Sphere 1, Instance A', 'Sphere 0, Instance B', 'Sphere 1, Instance B'],
  80. transforms: [Mat4.identity(), Mat4.fromTranslation(Mat4.zero(), Vec3.create(3, 0, 0))]
  81. }
  82. type MyData = typeof myData
  83. /**
  84. * Get shape from `MyData` object
  85. */
  86. async function getShape(ctx: RuntimeContext, data: MyData, props: {}, shape?: Shape<Mesh>) {
  87. await ctx.update('async creation of shape from myData')
  88. const { centers, colors, labels, transforms } = data
  89. const mesh = await getSphereMesh(ctx, centers, shape && shape.geometry)
  90. const groupCount = centers.length / 3
  91. return Shape.create(
  92. 'test', data, mesh,
  93. (groupId: number) => colors[groupId], // color: per group, same for instances
  94. () => 1, // size: constant
  95. (groupId: number, instanceId: number) => labels[instanceId * groupCount + groupId], // label: per group and instance
  96. transforms
  97. )
  98. }
  99. // Init ShapeRepresentation container
  100. const repr = ShapeRepresentation(getShape, Mesh.Utils)
  101. export async function init() {
  102. // Create shape from myData and add to canvas3d
  103. await repr.createOrUpdate({}, myData).run((p: Progress) => console.log(Progress.format(p)))
  104. canvas3d.add(repr)
  105. canvas3d.resetCamera()
  106. // Change color after 1s
  107. setTimeout(async () => {
  108. myData.colors[0] = ColorNames.darkmagenta
  109. // Calling `createOrUpdate` with `data` will trigger color and transform update
  110. await repr.createOrUpdate({}, myData).run()
  111. }, 1000)
  112. }
  113. init()