loci-label.ts 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /**
  2. * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { PluginContext } from '../../mol-plugin/context';
  8. import { Loci } from '../../mol-model/loci';
  9. import { Representation } from '../../mol-repr/representation';
  10. import { MarkerAction } from '../../mol-util/marker-action';
  11. import { arrayRemoveAtInPlace } from '../../mol-util/array';
  12. export type LociLabel = JSX.Element | string
  13. export type LociLabelProvider = {
  14. label: (loci: Loci, repr?: Representation<any>) => LociLabel | undefined
  15. group?: (entry: LociLabel) => string
  16. /** Labels from providers with higher priority are shown first */
  17. priority?: number
  18. }
  19. export class LociLabelManager {
  20. providers: LociLabelProvider[] = [];
  21. addProvider(provider: LociLabelProvider) {
  22. this.providers.push(provider);
  23. this.providers.sort((a, b) => (b.priority || 0) - (a.priority || 0))
  24. this.isDirty = true
  25. this.showLabels()
  26. }
  27. removeProvider(provider: LociLabelProvider) {
  28. this.providers = this.providers.filter(p => p !== provider);
  29. this.isDirty = true
  30. this.showLabels()
  31. }
  32. private locis: Representation.Loci[] = []
  33. private mark(loci: Representation.Loci, action: MarkerAction) {
  34. const idx = this.locis.findIndex(l => Representation.Loci.areEqual(loci, l))
  35. if (idx === -1 && action === MarkerAction.Highlight) {
  36. this.locis.push(loci)
  37. this.isDirty = true
  38. } else if(idx !== -1 && action === MarkerAction.RemoveHighlight) {
  39. arrayRemoveAtInPlace(this.locis, idx)
  40. this.isDirty = true
  41. }
  42. }
  43. private isDirty = false
  44. private labels: LociLabel[] = []
  45. private groupedLabels = new Map<string, LociLabel[]>()
  46. private showLabels() {
  47. this.ctx.behaviors.labels.highlight.next({ labels: this.getLabels() })
  48. }
  49. private getLabels() {
  50. if (this.isDirty) {
  51. this.groupedLabels.clear()
  52. this.labels.length = 0
  53. for (const provider of this.providers) {
  54. for (const loci of this.locis) {
  55. if (Loci.isEmpty(loci.loci)) continue
  56. const label = provider.label(loci.loci, loci.repr)
  57. if (label) {
  58. const hash = provider.group ? provider.group(label) : label.toString()
  59. const group = this.groupedLabels.get(hash)
  60. if (group) group.push(label)
  61. else this.groupedLabels.set(hash, [label])
  62. }
  63. }
  64. }
  65. this.labels.length = 0
  66. this.groupedLabels.forEach((group, hash) => {
  67. const count = group.length
  68. const entry = count > 1 && group[0] !== group[1]
  69. ? hash : group[0]
  70. this.labels.push(count === 1 ? entry : `${entry} <small>|| \u00D7 ${count}</small>`)
  71. })
  72. this.isDirty = false
  73. }
  74. return this.labels
  75. }
  76. constructor(public ctx: PluginContext) {
  77. ctx.managers.interactivity.lociHighlights.addProvider((loci, action) => {
  78. this.mark(loci, action)
  79. this.showLabels()
  80. })
  81. }
  82. }