@jbrowse/plugin-canvas 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +201 -0
  2. package/esm/CanvasFeatureRenderer/CanvasFeatureRenderer.d.ts +6 -0
  3. package/esm/CanvasFeatureRenderer/CanvasFeatureRenderer.js +30 -0
  4. package/esm/CanvasFeatureRenderer/CanvasFeatureRendering.d.ts +17 -0
  5. package/esm/CanvasFeatureRenderer/CanvasFeatureRendering.js +192 -0
  6. package/esm/CanvasFeatureRenderer/configSchema.d.ts +136 -0
  7. package/esm/CanvasFeatureRenderer/configSchema.js +152 -0
  8. package/esm/CanvasFeatureRenderer/configSchema2.d.ts +136 -0
  9. package/esm/CanvasFeatureRenderer/configSchema2.js +5 -0
  10. package/esm/CanvasFeatureRenderer/doAll.d.ts +21 -0
  11. package/esm/CanvasFeatureRenderer/doAll.js +42 -0
  12. package/esm/CanvasFeatureRenderer/drawChevrons.d.ts +1 -0
  13. package/esm/CanvasFeatureRenderer/drawChevrons.js +25 -0
  14. package/esm/CanvasFeatureRenderer/drawFeature.d.ts +3 -0
  15. package/esm/CanvasFeatureRenderer/drawFeature.js +27 -0
  16. package/esm/CanvasFeatureRenderer/filterSubparts.d.ts +5 -0
  17. package/esm/CanvasFeatureRenderer/filterSubparts.js +92 -0
  18. package/esm/CanvasFeatureRenderer/floatingLabels.d.ts +23 -0
  19. package/esm/CanvasFeatureRenderer/floatingLabels.js +65 -0
  20. package/esm/CanvasFeatureRenderer/glyphs/box.d.ts +2 -0
  21. package/esm/CanvasFeatureRenderer/glyphs/box.js +87 -0
  22. package/esm/CanvasFeatureRenderer/glyphs/cds.d.ts +2 -0
  23. package/esm/CanvasFeatureRenderer/glyphs/cds.js +93 -0
  24. package/esm/CanvasFeatureRenderer/glyphs/childGlyphs.d.ts +5 -0
  25. package/esm/CanvasFeatureRenderer/glyphs/childGlyphs.js +18 -0
  26. package/esm/CanvasFeatureRenderer/glyphs/glyphUtils.d.ts +14 -0
  27. package/esm/CanvasFeatureRenderer/glyphs/glyphUtils.js +125 -0
  28. package/esm/CanvasFeatureRenderer/glyphs/index.d.ts +9 -0
  29. package/esm/CanvasFeatureRenderer/glyphs/index.js +29 -0
  30. package/esm/CanvasFeatureRenderer/glyphs/matureProteinRegion.d.ts +2 -0
  31. package/esm/CanvasFeatureRenderer/glyphs/matureProteinRegion.js +121 -0
  32. package/esm/CanvasFeatureRenderer/glyphs/processed.d.ts +2 -0
  33. package/esm/CanvasFeatureRenderer/glyphs/processed.js +53 -0
  34. package/esm/CanvasFeatureRenderer/glyphs/repeatRegion.d.ts +2 -0
  35. package/esm/CanvasFeatureRenderer/glyphs/repeatRegion.js +104 -0
  36. package/esm/CanvasFeatureRenderer/glyphs/segments.d.ts +2 -0
  37. package/esm/CanvasFeatureRenderer/glyphs/segments.js +65 -0
  38. package/esm/CanvasFeatureRenderer/glyphs/subfeatures.d.ts +2 -0
  39. package/esm/CanvasFeatureRenderer/glyphs/subfeatures.js +132 -0
  40. package/esm/CanvasFeatureRenderer/index.d.ts +3 -0
  41. package/esm/CanvasFeatureRenderer/index.js +23 -0
  42. package/esm/CanvasFeatureRenderer/labelUtils.d.ts +9 -0
  43. package/esm/CanvasFeatureRenderer/labelUtils.js +35 -0
  44. package/esm/CanvasFeatureRenderer/layout/index.d.ts +3 -0
  45. package/esm/CanvasFeatureRenderer/layout/index.js +3 -0
  46. package/esm/CanvasFeatureRenderer/layout/layoutFeature.d.ts +14 -0
  47. package/esm/CanvasFeatureRenderer/layout/layoutFeature.js +40 -0
  48. package/esm/CanvasFeatureRenderer/layout/layoutFeatures.d.ts +13 -0
  49. package/esm/CanvasFeatureRenderer/layout/layoutFeatures.js +59 -0
  50. package/esm/CanvasFeatureRenderer/layout/layoutUtils.d.ts +19 -0
  51. package/esm/CanvasFeatureRenderer/layout/layoutUtils.js +129 -0
  52. package/esm/CanvasFeatureRenderer/makeImageData.d.ts +14 -0
  53. package/esm/CanvasFeatureRenderer/makeImageData.js +90 -0
  54. package/esm/CanvasFeatureRenderer/peptides/aggregateAminoAcids.d.ts +8 -0
  55. package/esm/CanvasFeatureRenderer/peptides/aggregateAminoAcids.js +45 -0
  56. package/esm/CanvasFeatureRenderer/peptides/drawCDSBackground.d.ts +16 -0
  57. package/esm/CanvasFeatureRenderer/peptides/drawCDSBackground.js +24 -0
  58. package/esm/CanvasFeatureRenderer/peptides/drawPeptidesOnCDS.d.ts +15 -0
  59. package/esm/CanvasFeatureRenderer/peptides/drawPeptidesOnCDS.js +28 -0
  60. package/esm/CanvasFeatureRenderer/peptides/index.d.ts +5 -0
  61. package/esm/CanvasFeatureRenderer/peptides/index.js +5 -0
  62. package/esm/CanvasFeatureRenderer/peptides/peptideUtils.d.ts +5 -0
  63. package/esm/CanvasFeatureRenderer/peptides/peptideUtils.js +129 -0
  64. package/esm/CanvasFeatureRenderer/peptides/prepareAminoAcidData.d.ts +9 -0
  65. package/esm/CanvasFeatureRenderer/peptides/prepareAminoAcidData.js +5 -0
  66. package/esm/CanvasFeatureRenderer/renderConfig.d.ts +26 -0
  67. package/esm/CanvasFeatureRenderer/renderConfig.js +35 -0
  68. package/esm/CanvasFeatureRenderer/types.d.ts +97 -0
  69. package/esm/CanvasFeatureRenderer/types.js +1 -0
  70. package/esm/CanvasFeatureRenderer/util.d.ts +22 -0
  71. package/esm/CanvasFeatureRenderer/util.js +50 -0
  72. package/esm/CanvasFeatureRenderer/zoomThresholds.d.ts +4 -0
  73. package/esm/CanvasFeatureRenderer/zoomThresholds.js +8 -0
  74. package/esm/glyphs/index.d.ts +2 -0
  75. package/esm/glyphs/index.js +2 -0
  76. package/esm/index.d.ts +6 -0
  77. package/esm/index.js +10 -0
  78. package/package.json +53 -0
@@ -0,0 +1,104 @@
1
+ import { readCachedConfig } from "../renderConfig.js";
2
+ import { isOffScreen } from "../util.js";
3
+ import { drawConnectingLine, drawStrandArrow, getStrandArrowPadding, layoutChild, } from "./glyphUtils.js";
4
+ const REPEAT_COLOR_MAP = {
5
+ CACTA_TIR_transposon: '#e6194b',
6
+ centromeric_repeat: '#3cb44b',
7
+ Copia_LTR_retrotransposon: '#118119',
8
+ Gypsy_LTR_retrotransposon: '#4363d8',
9
+ hAT_TIR_transposon: '#f58231',
10
+ helitron: '#911eb4',
11
+ knob: '#46f0f0',
12
+ L1_LINE_retrotransposon: '#f032e6',
13
+ LINE_element: '#bcf60c',
14
+ long_terminal_repeat: '#fb0',
15
+ low_complexity: '#008080',
16
+ LTR_retrotransposon: '#e6beff',
17
+ Mutator_TIR_transposon: '#9a6324',
18
+ PIF_Harbinger_TIR_transposon: '#fffac8',
19
+ rDNA_intergenic_spacer_element: '#800000',
20
+ repeat_region: '#aaffc3',
21
+ RTE_LINE_retrotransposon: '#808000',
22
+ subtelomere: '#ffd8b1',
23
+ target_site_duplication: '#000075',
24
+ Tc1_Mariner_TIR_transposon: '#808080',
25
+ };
26
+ const SHORTEN_HEIGHT_FRACTION = 0.65;
27
+ export const repeatRegionGlyph = {
28
+ type: 'RepeatRegion',
29
+ match(feature) {
30
+ const type = feature.get('type');
31
+ const subfeatures = feature.get('subfeatures');
32
+ return type === 'repeat_region' && !!subfeatures?.length;
33
+ },
34
+ layout(args) {
35
+ const { feature, bpPerPx, reversed, configContext } = args;
36
+ const { config, displayMode, featureHeight } = configContext;
37
+ const start = feature.get('start');
38
+ const end = feature.get('end');
39
+ const heightPx = readCachedConfig(featureHeight, config, 'height', feature);
40
+ const baseHeightPx = displayMode === 'compact' ? heightPx / 2 : heightPx;
41
+ const widthPx = (end - start) / bpPerPx;
42
+ const strand = feature.get('strand');
43
+ const arrowPadding = getStrandArrowPadding(strand, reversed);
44
+ const subfeatures = feature.get('subfeatures') || [];
45
+ const children = subfeatures.map(child => layoutChild(child, feature, args));
46
+ return {
47
+ feature,
48
+ glyphType: 'RepeatRegion',
49
+ x: 0,
50
+ y: 0,
51
+ width: widthPx,
52
+ height: baseHeightPx,
53
+ totalLayoutHeight: baseHeightPx,
54
+ totalLayoutWidth: widthPx + arrowPadding.left + arrowPadding.right,
55
+ leftPadding: arrowPadding.left,
56
+ children,
57
+ };
58
+ },
59
+ draw(ctx, layout, dc) {
60
+ const { children } = layout;
61
+ const { theme, canvasWidth } = dc;
62
+ const left = layout.x;
63
+ const width = layout.width;
64
+ const top = layout.y;
65
+ const height = layout.height;
66
+ if (isOffScreen(left, width, canvasWidth)) {
67
+ return;
68
+ }
69
+ const strokeColor = theme.palette.text.secondary;
70
+ drawConnectingLine(ctx, left, top, width, height, strokeColor);
71
+ const sortedChildren = [...children].sort((a, b) => {
72
+ const aType = a.feature.get('type');
73
+ const bType = b.feature.get('type');
74
+ if (aType.endsWith('_retrotransposon')) {
75
+ return -1;
76
+ }
77
+ if (bType.endsWith('_retrotransposon')) {
78
+ return 1;
79
+ }
80
+ return 0;
81
+ });
82
+ for (const childLayout of sortedChildren) {
83
+ const childType = childLayout.feature.get('type');
84
+ const color = REPEAT_COLOR_MAP[childType] || '#000';
85
+ const shorten = childType.endsWith('_retrotransposon');
86
+ drawRepeatBox(ctx, childLayout, canvasWidth, color, shorten);
87
+ }
88
+ drawStrandArrow(ctx, layout, dc, strokeColor);
89
+ },
90
+ };
91
+ function drawRepeatBox(ctx, layout, canvasWidth, color, shorten) {
92
+ const { x: left, width, y, height: origHeight } = layout;
93
+ if (isOffScreen(left, width, canvasWidth)) {
94
+ return;
95
+ }
96
+ let top = y;
97
+ let height = origHeight;
98
+ if (shorten) {
99
+ top += ((1 - SHORTEN_HEIGHT_FRACTION) / 2) * height;
100
+ height *= SHORTEN_HEIGHT_FRACTION;
101
+ }
102
+ ctx.fillStyle = color;
103
+ ctx.fillRect(left, top, width, height);
104
+ }
@@ -0,0 +1,2 @@
1
+ import type { Glyph } from '../types.ts';
2
+ export declare const segmentsGlyph: Glyph;
@@ -0,0 +1,65 @@
1
+ import { readCachedConfig } from "../renderConfig.js";
2
+ import { boxGlyph } from "./box.js";
3
+ import { cdsGlyph } from "./cds.js";
4
+ import { drawSegmentedFeature, getStrandArrowPadding, layoutChild, } from "./glyphUtils.js";
5
+ export const segmentsGlyph = {
6
+ type: 'Segments',
7
+ match(feature, configContext) {
8
+ const type = feature.get('type');
9
+ if (type === 'CDS') {
10
+ return false;
11
+ }
12
+ const subfeatures = feature.get('subfeatures');
13
+ if (!subfeatures?.length) {
14
+ return false;
15
+ }
16
+ const { transcriptTypes, containerTypes } = configContext;
17
+ if (transcriptTypes.includes(type)) {
18
+ const hasCDS = subfeatures.some((f) => f.get('type') === 'CDS');
19
+ if (hasCDS) {
20
+ return false;
21
+ }
22
+ }
23
+ if (containerTypes.includes(type)) {
24
+ return false;
25
+ }
26
+ const isTopLevel = !feature.parent?.();
27
+ const hasNestedSubfeatures = subfeatures.some((f) => f.get('subfeatures')?.length);
28
+ if (isTopLevel && hasNestedSubfeatures) {
29
+ return false;
30
+ }
31
+ return true;
32
+ },
33
+ layout(args) {
34
+ const { feature, bpPerPx, reversed, configContext } = args;
35
+ const { config, displayMode, featureHeight } = configContext;
36
+ const start = feature.get('start');
37
+ const end = feature.get('end');
38
+ const heightPx = readCachedConfig(featureHeight, config, 'height', feature);
39
+ const baseHeightPx = displayMode === 'compact' ? heightPx / 2 : heightPx;
40
+ const widthPx = (end - start) / bpPerPx;
41
+ const strand = feature.get('strand');
42
+ const arrowPadding = getStrandArrowPadding(strand, reversed);
43
+ const subfeatures = feature.get('subfeatures') || [];
44
+ const children = subfeatures.map(child => {
45
+ const childType = child.get('type');
46
+ const glyphType = childType === 'CDS' ? 'CDS' : 'Box';
47
+ return layoutChild(child, feature, args, glyphType);
48
+ });
49
+ return {
50
+ feature,
51
+ glyphType: 'Segments',
52
+ x: 0,
53
+ y: 0,
54
+ width: widthPx,
55
+ height: baseHeightPx,
56
+ totalLayoutHeight: baseHeightPx,
57
+ totalLayoutWidth: widthPx + arrowPadding.left + arrowPadding.right,
58
+ leftPadding: arrowPadding.left,
59
+ children,
60
+ };
61
+ },
62
+ draw(ctx, layout, dc) {
63
+ drawSegmentedFeature(ctx, layout, dc, boxGlyph, cdsGlyph);
64
+ },
65
+ };
@@ -0,0 +1,2 @@
1
+ import type { Glyph } from '../types.ts';
2
+ export declare const subfeaturesGlyph: Glyph;
@@ -0,0 +1,132 @@
1
+ import { applyLabelDimensions } from "../labelUtils.js";
2
+ import { readCachedConfig } from "../renderConfig.js";
3
+ import { boxGlyph } from "./box.js";
4
+ import { findChildGlyph } from "./childGlyphs.js";
5
+ const TRANSCRIPT_PADDING = 2;
6
+ const CODING_TYPES = new Set(['CDS', 'cds']);
7
+ function hasCodingSubfeature(feature) {
8
+ const subfeatures = feature.get('subfeatures') || [];
9
+ return subfeatures.some((sub) => CODING_TYPES.has(sub.get('type')) || hasCodingSubfeature(sub));
10
+ }
11
+ function filterByGeneGlyphMode(subfeatures, transcriptTypes, mode) {
12
+ if (subfeatures.length <= 1) {
13
+ return subfeatures;
14
+ }
15
+ const transcriptSubfeatures = subfeatures.filter(sub => transcriptTypes.includes(sub.get('type')));
16
+ let candidates = transcriptSubfeatures.length > 0 ? transcriptSubfeatures : subfeatures;
17
+ if (mode === 'longestCoding') {
18
+ const codingCandidates = candidates.filter(hasCodingSubfeature);
19
+ if (codingCandidates.length > 0) {
20
+ candidates = codingCandidates;
21
+ }
22
+ }
23
+ const longest = candidates.reduce((a, b) => a.get('end') - a.get('start') > b.get('end') - b.get('start') ? a : b);
24
+ return [longest];
25
+ }
26
+ export const subfeaturesGlyph = {
27
+ type: 'Subfeatures',
28
+ match(feature, configContext) {
29
+ const type = feature.get('type');
30
+ const subfeatures = feature.get('subfeatures');
31
+ if (!subfeatures?.length) {
32
+ return false;
33
+ }
34
+ const { containerTypes } = configContext;
35
+ if (containerTypes.includes(type)) {
36
+ return true;
37
+ }
38
+ const isTopLevel = !feature.parent?.();
39
+ const hasNestedSubfeatures = subfeatures.some((f) => f.get('subfeatures')?.length);
40
+ return isTopLevel && hasNestedSubfeatures;
41
+ },
42
+ layout(args) {
43
+ const { feature, bpPerPx, reversed, configContext } = args;
44
+ const { config, displayMode, featureHeight, geneGlyphMode, transcriptTypes, } = configContext;
45
+ const featureBp = {
46
+ start: feature.get('start'),
47
+ end: feature.get('end'),
48
+ };
49
+ const heightPx = readCachedConfig(featureHeight, config, 'height', feature);
50
+ const baseHeightPx = displayMode === 'compact' ? heightPx / 2 : heightPx;
51
+ const widthPx = (featureBp.end - featureBp.start) / bpPerPx;
52
+ let subfeatures = [...(feature.get('subfeatures') || [])];
53
+ const codingStatus = new Map(subfeatures.map(f => [f.id(), hasCodingSubfeature(f)]));
54
+ subfeatures.sort((a, b) => {
55
+ const aHasCDS = codingStatus.get(a.id());
56
+ const bHasCDS = codingStatus.get(b.id());
57
+ if (aHasCDS && !bHasCDS) {
58
+ return -1;
59
+ }
60
+ if (!aHasCDS && bHasCDS) {
61
+ return 1;
62
+ }
63
+ return 0;
64
+ });
65
+ if (geneGlyphMode === 'longest' || geneGlyphMode === 'longestCoding') {
66
+ subfeatures = filterByGeneGlyphMode(subfeatures, transcriptTypes, geneGlyphMode);
67
+ }
68
+ const children = [];
69
+ let currentYPx = 0;
70
+ const { subfeatureLabels } = configContext;
71
+ for (let i = 0; i < subfeatures.length; i++) {
72
+ const child = subfeatures[i];
73
+ const childType = child.get('type');
74
+ const isChildTranscript = transcriptTypes.includes(childType);
75
+ const childGlyph = findChildGlyph(child, configContext);
76
+ const childLayout = childGlyph.layout({
77
+ ...args,
78
+ feature: child,
79
+ parentFeature: feature,
80
+ });
81
+ applyLabelDimensions(childLayout, {
82
+ feature: child,
83
+ configContext,
84
+ isNested: true,
85
+ isTranscriptChild: isChildTranscript,
86
+ });
87
+ const childBp = {
88
+ start: child.get('start'),
89
+ end: child.get('end'),
90
+ };
91
+ const offsetBp = reversed
92
+ ? featureBp.end - childBp.end
93
+ : childBp.start - featureBp.start;
94
+ const xRelativePx = offsetBp / bpPerPx;
95
+ childLayout.x = xRelativePx;
96
+ childLayout.y = currentYPx;
97
+ children.push(childLayout);
98
+ const useExtraHeightForLabels = subfeatureLabels === 'below' && isChildTranscript;
99
+ const heightForStacking = useExtraHeightForLabels
100
+ ? childLayout.totalLayoutHeight
101
+ : childLayout.height;
102
+ currentYPx += heightForStacking;
103
+ if (i < subfeatures.length - 1) {
104
+ currentYPx += TRANSCRIPT_PADDING;
105
+ }
106
+ }
107
+ const totalHeightPx = currentYPx > 0 ? currentYPx : baseHeightPx;
108
+ return {
109
+ feature,
110
+ glyphType: 'Subfeatures',
111
+ x: 0,
112
+ y: 0,
113
+ width: widthPx,
114
+ height: totalHeightPx,
115
+ totalLayoutHeight: totalHeightPx,
116
+ totalLayoutWidth: widthPx,
117
+ leftPadding: 0,
118
+ children,
119
+ };
120
+ },
121
+ draw(ctx, layout, dc) {
122
+ const { children } = layout;
123
+ if (children.length === 0) {
124
+ boxGlyph.draw(ctx, { ...layout, glyphType: 'Box' }, dc);
125
+ return;
126
+ }
127
+ for (const childLayout of children) {
128
+ const childGlyph = findChildGlyph(childLayout.feature, dc.configContext);
129
+ childGlyph.draw(ctx, childLayout, dc);
130
+ }
131
+ },
132
+ };
@@ -0,0 +1,3 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ export default function CanvasFeatureRendererF(pluginManager: PluginManager): void;
3
+ export { default as configSchema } from './configSchema.ts';
@@ -0,0 +1,23 @@
1
+ import { lazy } from 'react';
2
+ import CanvasFeatureRenderer from "./CanvasFeatureRenderer.js";
3
+ import configSchema from "./configSchema.js";
4
+ import configSchema2 from "./configSchema2.js";
5
+ export default function CanvasFeatureRendererF(pluginManager) {
6
+ pluginManager.addRendererType(() => {
7
+ return new CanvasFeatureRenderer({
8
+ name: 'CanvasFeatureRenderer',
9
+ ReactComponent: lazy(() => import("./CanvasFeatureRendering.js")),
10
+ configSchema,
11
+ pluginManager,
12
+ });
13
+ });
14
+ pluginManager.addRendererType(() => {
15
+ return new CanvasFeatureRenderer({
16
+ name: 'SvgFeatureRenderer',
17
+ ReactComponent: lazy(() => import("./CanvasFeatureRendering.js")),
18
+ configSchema: configSchema2,
19
+ pluginManager,
20
+ });
21
+ });
22
+ }
23
+ export { default as configSchema } from "./configSchema.js";
@@ -0,0 +1,9 @@
1
+ import type { RenderConfigContext } from './renderConfig.ts';
2
+ import type { FeatureLayout } from './types.ts';
3
+ import type { Feature } from '@jbrowse/core/util';
4
+ export declare function applyLabelDimensions(layout: FeatureLayout, args: {
5
+ feature: Feature;
6
+ configContext: RenderConfigContext;
7
+ isNested: boolean;
8
+ isTranscriptChild: boolean;
9
+ }): void;
@@ -0,0 +1,35 @@
1
+ import { readConfObject } from '@jbrowse/core/configuration';
2
+ import { measureText } from '@jbrowse/core/util';
3
+ import { readCachedConfig } from "./renderConfig.js";
4
+ import { truncateLabel } from "./util.js";
5
+ export function applyLabelDimensions(layout, args) {
6
+ const { feature, configContext, isNested, isTranscriptChild } = args;
7
+ const { config, showLabels, showDescriptions, subfeatureLabels, fontHeight, labelAllowed, } = configContext;
8
+ const showSubfeatureLabels = subfeatureLabels !== 'none';
9
+ const shouldCalculateLabels = labelAllowed && (!isNested || (isTranscriptChild && showSubfeatureLabels));
10
+ if (!shouldCalculateLabels) {
11
+ return;
12
+ }
13
+ const effectiveShowLabels = isTranscriptChild ? true : showLabels;
14
+ const effectiveShowDescriptions = isTranscriptChild ? false : showDescriptions;
15
+ const name = truncateLabel(String(readConfObject(config, ['labels', 'name'], { feature }) || ''));
16
+ const shouldShowName = /\S/.test(name) && effectiveShowLabels;
17
+ const description = truncateLabel(String(readConfObject(config, ['labels', 'description'], { feature }) || ''));
18
+ const shouldShowDescription = /\S/.test(description) && effectiveShowDescriptions;
19
+ const actualFontHeight = readCachedConfig(fontHeight, config, ['labels', 'fontSize'], feature);
20
+ let extraHeightPx = 0;
21
+ let maxLabelWidthPx = 0;
22
+ if (shouldShowName) {
23
+ extraHeightPx += actualFontHeight;
24
+ maxLabelWidthPx = Math.max(maxLabelWidthPx, measureText(name, actualFontHeight));
25
+ }
26
+ if (shouldShowDescription) {
27
+ extraHeightPx += actualFontHeight;
28
+ maxLabelWidthPx = Math.max(maxLabelWidthPx, measureText(description, actualFontHeight));
29
+ }
30
+ const isOverlayMode = isTranscriptChild && subfeatureLabels === 'overlay';
31
+ if (!isOverlayMode) {
32
+ layout.totalLayoutHeight = layout.height + extraHeightPx;
33
+ }
34
+ layout.totalLayoutWidth = Math.max(layout.totalLayoutWidth, maxLabelWidthPx);
35
+ }
@@ -0,0 +1,3 @@
1
+ export { applyLabelDimensions, layoutFeature } from './layoutFeature.ts';
2
+ export { layoutFeatures } from './layoutFeatures.ts';
3
+ export { buildChildrenIndex, convertToCanvasCoords } from './layoutUtils.ts';
@@ -0,0 +1,3 @@
1
+ export { applyLabelDimensions, layoutFeature } from "./layoutFeature.js";
2
+ export { layoutFeatures } from "./layoutFeatures.js";
3
+ export { buildChildrenIndex, convertToCanvasCoords } from "./layoutUtils.js";
@@ -0,0 +1,14 @@
1
+ import type { RenderConfigContext } from '../renderConfig.ts';
2
+ import type { FeatureLayout } from '../types.ts';
3
+ import type PluginManager from '@jbrowse/core/PluginManager';
4
+ import type { Feature } from '@jbrowse/core/util';
5
+ export { applyLabelDimensions } from '../labelUtils.ts';
6
+ export declare function layoutFeature(args: {
7
+ feature: Feature;
8
+ bpPerPx: number;
9
+ reversed: boolean;
10
+ configContext: RenderConfigContext;
11
+ pluginManager?: PluginManager;
12
+ isNested?: boolean;
13
+ isTranscriptChild?: boolean;
14
+ }): FeatureLayout;
@@ -0,0 +1,40 @@
1
+ import { builtinGlyphs, findGlyph, findPluggableGlyph, } from "../glyphs/index.js";
2
+ import { applyLabelDimensions } from "../labelUtils.js";
3
+ export { applyLabelDimensions } from "../labelUtils.js";
4
+ export function layoutFeature(args) {
5
+ const { feature, bpPerPx, reversed, configContext, pluginManager, isNested = false, isTranscriptChild = false, } = args;
6
+ const pluggableGlyph = findPluggableGlyph(feature, pluginManager);
7
+ let layout;
8
+ if (pluggableGlyph) {
9
+ const heightMultiplier = pluggableGlyph.getHeightMultiplier?.(feature, configContext.config) ?? 1;
10
+ const baseGlyph = findGlyph(feature, configContext, builtinGlyphs);
11
+ const layoutArgs = {
12
+ feature,
13
+ bpPerPx,
14
+ reversed,
15
+ configContext,
16
+ pluginManager,
17
+ };
18
+ layout = baseGlyph.layout(layoutArgs);
19
+ layout.height *= heightMultiplier;
20
+ layout.totalLayoutHeight *= heightMultiplier;
21
+ }
22
+ else {
23
+ const glyph = findGlyph(feature, configContext, builtinGlyphs);
24
+ const layoutArgs = {
25
+ feature,
26
+ bpPerPx,
27
+ reversed,
28
+ configContext,
29
+ pluginManager,
30
+ };
31
+ layout = glyph.layout(layoutArgs);
32
+ }
33
+ applyLabelDimensions(layout, {
34
+ feature,
35
+ configContext,
36
+ isNested,
37
+ isTranscriptChild,
38
+ });
39
+ return layout;
40
+ }
@@ -0,0 +1,13 @@
1
+ import type { RenderConfigContext } from '../renderConfig.ts';
2
+ import type { LayoutRecord } from '../types.ts';
3
+ import type PluginManager from '@jbrowse/core/PluginManager';
4
+ import type { Feature, Region } from '@jbrowse/core/util';
5
+ import type { BaseLayout } from '@jbrowse/core/util/layouts';
6
+ export declare function layoutFeatures({ features, bpPerPx, region, configContext, layout, pluginManager, }: {
7
+ features: Map<string, Feature>;
8
+ bpPerPx: number;
9
+ region: Region;
10
+ configContext: RenderConfigContext;
11
+ layout: BaseLayout<unknown>;
12
+ pluginManager: PluginManager;
13
+ }): LayoutRecord[];
@@ -0,0 +1,59 @@
1
+ import { readConfObject } from '@jbrowse/core/configuration';
2
+ import { createFeatureFloatingLabels } from "../floatingLabels.js";
3
+ import { layoutFeature } from "./layoutFeature.js";
4
+ const yPadding = 5;
5
+ export function layoutFeatures({ features, bpPerPx, region, configContext, layout, pluginManager, }) {
6
+ const reversed = region.reversed || false;
7
+ const layoutRecords = [];
8
+ const { config } = configContext;
9
+ for (const feature of features.values()) {
10
+ const featureLayout = layoutFeature({
11
+ feature,
12
+ bpPerPx,
13
+ reversed,
14
+ configContext,
15
+ pluginManager,
16
+ });
17
+ const totalLayoutWidth = featureLayout.totalLayoutWidth;
18
+ const totalLayoutHeight = featureLayout.totalLayoutHeight;
19
+ const name = String(readConfObject(config, ['labels', 'name'], { feature }) || '');
20
+ const description = String(readConfObject(config, ['labels', 'description'], { feature }) || '');
21
+ const floatingLabels = createFeatureFloatingLabels({
22
+ feature,
23
+ config,
24
+ configContext,
25
+ nameColor: 'black',
26
+ descriptionColor: 'blue',
27
+ name,
28
+ description,
29
+ });
30
+ const featureStart = feature.get('start');
31
+ const featureEnd = feature.get('end');
32
+ const leftPaddingBp = featureLayout.leftPadding * bpPerPx;
33
+ const rightPaddingBp = (totalLayoutWidth - featureLayout.width - featureLayout.leftPadding) *
34
+ bpPerPx;
35
+ const layoutStart = featureStart - leftPaddingBp;
36
+ const layoutEnd = featureEnd + rightPaddingBp;
37
+ const topPx = layout.addRect(feature.id(), layoutStart, layoutEnd, totalLayoutHeight + yPadding, feature, {
38
+ label: name,
39
+ description,
40
+ refName: feature.get('refName'),
41
+ floatingLabels,
42
+ totalFeatureHeight: featureLayout.height,
43
+ totalLayoutWidth,
44
+ featureWidth: featureLayout.width,
45
+ featureStartBp: featureStart,
46
+ featureEndBp: featureEnd,
47
+ });
48
+ if (topPx !== null) {
49
+ layoutRecords.push({
50
+ feature,
51
+ layout: featureLayout,
52
+ topPx,
53
+ label: name,
54
+ description,
55
+ });
56
+ }
57
+ }
58
+ return layoutRecords;
59
+ }
@@ -0,0 +1,19 @@
1
+ import type { RenderConfigContext } from '../renderConfig.ts';
2
+ import type { FeatureLayout, SubfeatureInfo } from '../types.ts';
3
+ import type PluginManager from '@jbrowse/core/PluginManager';
4
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
5
+ import type { BaseLayout } from '@jbrowse/core/util/layouts';
6
+ export declare function convertToCanvasCoords(layout: FeatureLayout, offsetX: number, offsetY: number): FeatureLayout;
7
+ export declare function buildChildrenIndex({ layout, featureLayout, subfeatureCoords, subfeatureInfos, config, configContext, pluginManager, subfeatureLabels, transcriptTypes, labelColor, parentTooltip, }: {
8
+ layout: BaseLayout<unknown>;
9
+ featureLayout: FeatureLayout;
10
+ subfeatureCoords: number[];
11
+ subfeatureInfos: SubfeatureInfo[];
12
+ config: AnyConfigurationModel;
13
+ configContext: RenderConfigContext;
14
+ pluginManager?: PluginManager;
15
+ subfeatureLabels: string;
16
+ transcriptTypes: string[];
17
+ labelColor: string;
18
+ parentTooltip: string;
19
+ }): void;