label.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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 { Loci } from '../../../mol-model/loci';
  7. import { RuntimeContext } from '../../../mol-task';
  8. import { Text } from '../../../mol-geo/geometry/text/text';
  9. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  10. import { ShapeRepresentation } from '../representation';
  11. import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../representation';
  12. import { Shape } from '../../../mol-model/shape';
  13. import { TextBuilder } from '../../../mol-geo/geometry/text/text-builder';
  14. import { Sphere3D } from '../../../mol-math/geometry';
  15. import { lociLabel } from '../../../mol-theme/label';
  16. import { LociLabelTextParams } from './common';
  17. export interface LabelData {
  18. infos: { loci: Loci, label?: string }[]
  19. }
  20. const TextParams = {
  21. ...LociLabelTextParams,
  22. };
  23. type TextParams = typeof TextParams
  24. const LabelVisuals = {
  25. 'text': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<LabelData, TextParams>) => ShapeRepresentation(getTextShape, Text.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
  26. };
  27. export const LabelParams = {
  28. ...TextParams,
  29. scaleByRadius: PD.Boolean(true),
  30. visuals: PD.MultiSelect(['text'], PD.objectToOptions(LabelVisuals)),
  31. };
  32. export type LabelParams = typeof LabelParams
  33. export type LabelProps = PD.Values<LabelParams>
  34. //
  35. const tmpSphere = Sphere3D();
  36. function label(info: { loci: Loci, label?: string }, condensed = false) {
  37. return info.label || lociLabel(info.loci, { hidePrefix: true, htmlStyling: false, condensed });
  38. }
  39. function getLabelName(data: LabelData) {
  40. return data.infos.length === 1 ? label(data.infos[0]) : `${data.infos.length} Labels`;
  41. }
  42. //
  43. function buildText(data: LabelData, props: LabelProps, text?: Text): Text {
  44. const builder = TextBuilder.create(props, 128, 64, text);
  45. const customLabel = props.customText.trim();
  46. for (let i = 0, il = data.infos.length; i < il; ++i) {
  47. const info = data.infos[i];
  48. const sphere = Loci.getBoundingSphere(info.loci, tmpSphere);
  49. if (!sphere) continue;
  50. const { center, radius } = sphere;
  51. const text = customLabel || label(info, true);
  52. builder.add(text, center[0], center[1], center[2], props.scaleByRadius ? radius / 0.9 : 0, props.scaleByRadius ? Math.max(1, radius) : 1, i);
  53. }
  54. return builder.getText();
  55. }
  56. function getTextShape(ctx: RuntimeContext, data: LabelData, props: LabelProps, shape?: Shape<Text>) {
  57. const text = buildText(data, props, shape && shape.geometry);
  58. const name = getLabelName(data);
  59. const customLabel = props.customText.trim();
  60. const getLabel = customLabel
  61. ? function (groupId: number) {
  62. return customLabel;
  63. } : function (groupId: number) {
  64. return label(data.infos[groupId]);
  65. };
  66. return Shape.create(name, data, text, () => props.textColor, () => props.textSize, getLabel);
  67. }
  68. //
  69. export type LabelRepresentation = Representation<LabelData, LabelParams>
  70. export function LabelRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<LabelData, LabelParams>): LabelRepresentation {
  71. return Representation.createMulti('Label', ctx, getParams, Representation.StateBuilder, LabelVisuals as unknown as Representation.Def<LabelData, LabelParams>);
  72. }