@apollo-annotation/jbrowse-plugin-apollo 0.1.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.
- package/README.md +76 -0
- package/dist/index.esm.js +10248 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +7 -0
- package/dist/jbrowse-plugin-apollo.cjs.development.js +10298 -0
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +2 -0
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.umd.development.js +46957 -0
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +2 -0
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -0
- package/package.json +130 -0
- package/src/ApolloInternetAccount/addMenuItems.ts +94 -0
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +121 -0
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +62 -0
- package/src/ApolloInternetAccount/components/LoginIcons.tsx +74 -0
- package/src/ApolloInternetAccount/configSchema.ts +26 -0
- package/src/ApolloInternetAccount/index.ts +2 -0
- package/src/ApolloInternetAccount/model.ts +448 -0
- package/src/ApolloJobModel.ts +117 -0
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +186 -0
- package/src/ApolloSequenceAdapter/configSchema.ts +12 -0
- package/src/ApolloSequenceAdapter/index.ts +21 -0
- package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +12 -0
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +692 -0
- package/src/ApolloSixFrameRenderer/configSchema.ts +7 -0
- package/src/ApolloSixFrameRenderer/index.ts +3 -0
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +64 -0
- package/src/ApolloTextSearchAdapter/configSchema.ts +24 -0
- package/src/ApolloTextSearchAdapter/index.ts +18 -0
- package/src/BackendDrivers/BackendDriver.ts +31 -0
- package/src/BackendDrivers/CollaborationServerDriver.ts +318 -0
- package/src/BackendDrivers/DesktopFileDriver.ts +170 -0
- package/src/BackendDrivers/InMemoryFileDriver.ts +76 -0
- package/src/BackendDrivers/index.ts +4 -0
- package/src/ChangeManager.ts +148 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +248 -0
- package/src/LinearApolloDisplay/components/index.ts +1 -0
- package/src/LinearApolloDisplay/configSchema.ts +16 -0
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +422 -0
- package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +1191 -0
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +151 -0
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +382 -0
- package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +697 -0
- package/src/LinearApolloDisplay/glyphs/index.ts +4 -0
- package/src/LinearApolloDisplay/index.ts +2 -0
- package/src/LinearApolloDisplay/stateModel/base.ts +146 -0
- package/src/LinearApolloDisplay/stateModel/getGlyph.ts +39 -0
- package/src/LinearApolloDisplay/stateModel/glyphs.ts +45 -0
- package/src/LinearApolloDisplay/stateModel/index.ts +20 -0
- package/src/LinearApolloDisplay/stateModel/layouts.ts +230 -0
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +513 -0
- package/src/LinearApolloDisplay/stateModel/rendering.ts +441 -0
- package/src/LinearApolloDisplay/stateModel/trackHeightMixin.ts +43 -0
- package/src/LinearApolloDisplay/types.ts +1 -0
- package/src/OntologyManager/OntologyStore/__snapshots__/fulltext.test.ts.snap +208 -0
- package/src/OntologyManager/OntologyStore/__snapshots__/index.test.ts.snap +18846 -0
- package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +137 -0
- package/src/OntologyManager/OntologyStore/fulltext.test.ts +94 -0
- package/src/OntologyManager/OntologyStore/fulltext.ts +264 -0
- package/src/OntologyManager/OntologyStore/index.test.ts +130 -0
- package/src/OntologyManager/OntologyStore/index.ts +526 -0
- package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +89 -0
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +180 -0
- package/src/OntologyManager/OntologyStore/obo-graph-json-schema.ts +110 -0
- package/src/OntologyManager/OntologyStore/prefixes.ts +35 -0
- package/src/OntologyManager/index.ts +173 -0
- package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +19 -0
- package/src/SixFrameFeatureDisplay/components/index.ts +1 -0
- package/src/SixFrameFeatureDisplay/configSchema.ts +21 -0
- package/src/SixFrameFeatureDisplay/index.ts +2 -0
- package/src/SixFrameFeatureDisplay/stateModel.ts +413 -0
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +88 -0
- package/src/TabularEditor/HybridGrid/Feature.tsx +346 -0
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +34 -0
- package/src/TabularEditor/HybridGrid/Highlight.tsx +40 -0
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +138 -0
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +77 -0
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +59 -0
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +119 -0
- package/src/TabularEditor/HybridGrid/index.ts +1 -0
- package/src/TabularEditor/TabularEditorPane.tsx +34 -0
- package/src/TabularEditor/index.ts +3 -0
- package/src/TabularEditor/model.ts +44 -0
- package/src/TabularEditor/types.ts +3 -0
- package/src/components/AddAssembly.tsx +464 -0
- package/src/components/AddChildFeature.tsx +247 -0
- package/src/components/AddFeature.tsx +252 -0
- package/src/components/CopyFeature.tsx +328 -0
- package/src/components/DeleteAssembly.tsx +185 -0
- package/src/components/DeleteFeature.tsx +90 -0
- package/src/components/Dialog.tsx +47 -0
- package/src/components/DownloadGFF3.tsx +213 -0
- package/src/components/ImportFeatures.tsx +295 -0
- package/src/components/ManageChecks.tsx +280 -0
- package/src/components/ManageUsers.tsx +218 -0
- package/src/components/ModifyFeatureAttribute.tsx +457 -0
- package/src/components/OntologyTermAutocomplete.tsx +240 -0
- package/src/components/OntologyTermMultiSelect.tsx +349 -0
- package/src/components/OpenLocalFile.tsx +178 -0
- package/src/components/ViewChangeLog.tsx +208 -0
- package/src/components/ViewCheckResults.tsx +151 -0
- package/src/components/index.ts +12 -0
- package/src/config.ts +10 -0
- package/src/declare.d.ts +3 -0
- package/src/extensions/annotationFromPileup.ts +208 -0
- package/src/extensions/index.ts +1 -0
- package/src/index.ts +394 -0
- package/src/makeDisplayComponent.tsx +244 -0
- package/src/session/ClientDataStore.ts +282 -0
- package/src/session/index.ts +1 -0
- package/src/session/session.ts +373 -0
- package/src/types.ts +10 -0
- package/src/util/index.ts +31 -0
- package/src/util/loadAssemblyIntoClient.ts +291 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
|
|
2
|
+
import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
|
|
3
|
+
import { BaseDisplay } from '@jbrowse/core/pluggableElementTypes'
|
|
4
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
5
|
+
import {
|
|
6
|
+
AbstractSessionModel,
|
|
7
|
+
getContainingView,
|
|
8
|
+
getSession,
|
|
9
|
+
} from '@jbrowse/core/util'
|
|
10
|
+
import { getParentRenderProps } from '@jbrowse/core/util/tracks'
|
|
11
|
+
// import type LinearGenomeViewPlugin from '@jbrowse/plugin-linear-genome-view'
|
|
12
|
+
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
13
|
+
import { AnnotationFeatureI } from 'apollo-mst'
|
|
14
|
+
import { autorun } from 'mobx'
|
|
15
|
+
import { addDisposer, getRoot, types } from 'mobx-state-tree'
|
|
16
|
+
|
|
17
|
+
import { ApolloInternetAccountModel } from '../../ApolloInternetAccount/model'
|
|
18
|
+
import { ApolloSessionModel } from '../../session'
|
|
19
|
+
import { ApolloRootModel } from '../../types'
|
|
20
|
+
import { TrackHeightMixin } from './trackHeightMixin'
|
|
21
|
+
|
|
22
|
+
export function baseModelFactory(
|
|
23
|
+
_pluginManager: PluginManager,
|
|
24
|
+
configSchema: AnyConfigurationSchemaType,
|
|
25
|
+
) {
|
|
26
|
+
// TODO: Restore this when TRackHeightMixin is in LGV runtime exports
|
|
27
|
+
|
|
28
|
+
// const LGVPlugin = pluginManager.getPlugin(
|
|
29
|
+
// 'LinearGenomeViewPlugin',
|
|
30
|
+
// ) as LinearGenomeViewPlugin
|
|
31
|
+
// const { TrackHeightMixin } = LGVPlugin.exports
|
|
32
|
+
|
|
33
|
+
return types
|
|
34
|
+
.compose(BaseDisplay, TrackHeightMixin)
|
|
35
|
+
.named('BaseLinearApolloDisplay')
|
|
36
|
+
.props({
|
|
37
|
+
type: types.literal('LinearApolloDisplay'),
|
|
38
|
+
configuration: ConfigurationReference(configSchema),
|
|
39
|
+
})
|
|
40
|
+
.volatile((self) => ({
|
|
41
|
+
lgv: getContainingView(self) as unknown as LinearGenomeViewModel,
|
|
42
|
+
}))
|
|
43
|
+
.views((self) => {
|
|
44
|
+
const { configuration, renderProps: superRenderProps } = self
|
|
45
|
+
return {
|
|
46
|
+
renderProps() {
|
|
47
|
+
return {
|
|
48
|
+
...superRenderProps(),
|
|
49
|
+
...getParentRenderProps(self),
|
|
50
|
+
config: configuration.renderer,
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
.views((self) => ({
|
|
56
|
+
get rendererTypeName() {
|
|
57
|
+
return self.configuration.renderer.type
|
|
58
|
+
},
|
|
59
|
+
get session() {
|
|
60
|
+
return getSession(self) as unknown as ApolloSessionModel
|
|
61
|
+
},
|
|
62
|
+
get regions() {
|
|
63
|
+
const regions = self.lgv.dynamicBlocks.contentBlocks.map(
|
|
64
|
+
({ assemblyName, end, refName, start }) => ({
|
|
65
|
+
assemblyName,
|
|
66
|
+
refName,
|
|
67
|
+
start: Math.round(start),
|
|
68
|
+
end: Math.round(end),
|
|
69
|
+
}),
|
|
70
|
+
)
|
|
71
|
+
return regions
|
|
72
|
+
},
|
|
73
|
+
get displayedRegions() {
|
|
74
|
+
return self.lgv.displayedRegions
|
|
75
|
+
},
|
|
76
|
+
regionCannotBeRendered(/* region */) {
|
|
77
|
+
if (self.lgv && self.lgv.bpPerPx >= 200) {
|
|
78
|
+
return 'Zoom in to see annotations'
|
|
79
|
+
}
|
|
80
|
+
return
|
|
81
|
+
},
|
|
82
|
+
}))
|
|
83
|
+
.views((self) => ({
|
|
84
|
+
get apolloInternetAccount() {
|
|
85
|
+
const [region] = self.regions
|
|
86
|
+
const { internetAccounts } = getRoot<ApolloRootModel>(self)
|
|
87
|
+
const { assemblyName } = region
|
|
88
|
+
const { assemblyManager } =
|
|
89
|
+
self.session as unknown as AbstractSessionModel
|
|
90
|
+
const assembly = assemblyManager.get(assemblyName)
|
|
91
|
+
if (!assembly) {
|
|
92
|
+
throw new Error(`No assembly found with name ${assemblyName}`)
|
|
93
|
+
}
|
|
94
|
+
const { internetAccountConfigId } = getConf(assembly, [
|
|
95
|
+
'sequence',
|
|
96
|
+
'metadata',
|
|
97
|
+
]) as { internetAccountConfigId: string }
|
|
98
|
+
return internetAccounts.find(
|
|
99
|
+
(ia) => getConf(ia, 'internetAccountId') === internetAccountConfigId,
|
|
100
|
+
) as ApolloInternetAccountModel | undefined
|
|
101
|
+
},
|
|
102
|
+
get changeManager() {
|
|
103
|
+
return (self.session as unknown as ApolloSessionModel).apolloDataStore
|
|
104
|
+
?.changeManager
|
|
105
|
+
},
|
|
106
|
+
getAssemblyId(assemblyName: string) {
|
|
107
|
+
const { assemblyManager } =
|
|
108
|
+
self.session as unknown as AbstractSessionModel
|
|
109
|
+
const assembly = assemblyManager.get(assemblyName)
|
|
110
|
+
if (!assembly) {
|
|
111
|
+
throw new Error(`Could not find assembly named ${assemblyName}`)
|
|
112
|
+
}
|
|
113
|
+
return assembly.name
|
|
114
|
+
},
|
|
115
|
+
get selectedFeature(): AnnotationFeatureI | undefined {
|
|
116
|
+
return (self.session as unknown as ApolloSessionModel)
|
|
117
|
+
.apolloSelectedFeature
|
|
118
|
+
},
|
|
119
|
+
}))
|
|
120
|
+
.actions((self) => ({
|
|
121
|
+
setSelectedFeature(feature?: AnnotationFeatureI) {
|
|
122
|
+
return (
|
|
123
|
+
self.session as unknown as ApolloSessionModel
|
|
124
|
+
).apolloSetSelectedFeature(feature)
|
|
125
|
+
},
|
|
126
|
+
afterAttach() {
|
|
127
|
+
addDisposer(
|
|
128
|
+
self,
|
|
129
|
+
autorun(
|
|
130
|
+
() => {
|
|
131
|
+
if (!self.lgv.initialized || self.regionCannotBeRendered()) {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
void (
|
|
135
|
+
self.session as unknown as ApolloSessionModel
|
|
136
|
+
).apolloDataStore.loadFeatures(self.regions)
|
|
137
|
+
void (
|
|
138
|
+
self.session as unknown as ApolloSessionModel
|
|
139
|
+
).apolloDataStore.loadRefSeq(self.regions)
|
|
140
|
+
},
|
|
141
|
+
{ name: 'LinearApolloDisplayLoadFeatures', delay: 1000 },
|
|
142
|
+
),
|
|
143
|
+
)
|
|
144
|
+
},
|
|
145
|
+
}))
|
|
146
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AnnotationFeatureI } from 'apollo-mst'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BoxGlyph,
|
|
5
|
+
CanonicalGeneGlyph,
|
|
6
|
+
GenericChildGlyph,
|
|
7
|
+
ImplicitExonGeneGlyph,
|
|
8
|
+
} from '../glyphs'
|
|
9
|
+
import { Glyph } from '../glyphs/Glyph'
|
|
10
|
+
|
|
11
|
+
const boxGlyph = new BoxGlyph()
|
|
12
|
+
const canonicalGeneGlyph = new CanonicalGeneGlyph()
|
|
13
|
+
const genericChildGlyph = new GenericChildGlyph()
|
|
14
|
+
const implicitExonGeneGlyph = new ImplicitExonGeneGlyph()
|
|
15
|
+
|
|
16
|
+
/** get the appropriate glyph for the given top-level feature */
|
|
17
|
+
export function getGlyph(feature: AnnotationFeatureI, _bpPerPx: number): Glyph {
|
|
18
|
+
if (feature.type === 'gene') {
|
|
19
|
+
let hasExon = false
|
|
20
|
+
for (const [, mrna] of feature.children ?? new Map()) {
|
|
21
|
+
if (mrna.type !== 'mRNA') {
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
24
|
+
for (const [, possibleExon] of mrna.children ?? new Map()) {
|
|
25
|
+
if (possibleExon.type === 'exon') {
|
|
26
|
+
hasExon = true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (hasExon) {
|
|
31
|
+
return canonicalGeneGlyph
|
|
32
|
+
}
|
|
33
|
+
return implicitExonGeneGlyph
|
|
34
|
+
}
|
|
35
|
+
if (feature.children?.size) {
|
|
36
|
+
return genericChildGlyph
|
|
37
|
+
}
|
|
38
|
+
return boxGlyph
|
|
39
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AnnotationFeatureI } from 'apollo-mst'
|
|
2
|
+
import { ObservableMap, observable } from 'mobx'
|
|
3
|
+
import { types } from 'mobx-state-tree'
|
|
4
|
+
|
|
5
|
+
import { BoxGlyph } from '../glyphs/BoxGlyph'
|
|
6
|
+
import { Glyph } from '../glyphs/Glyph'
|
|
7
|
+
|
|
8
|
+
export default function Glyphs() {
|
|
9
|
+
return types
|
|
10
|
+
.model({})
|
|
11
|
+
.volatile(() => ({
|
|
12
|
+
glyphs: observable.map<number, ObservableMap<string, Glyph>>(),
|
|
13
|
+
}))
|
|
14
|
+
.actions((s) => {
|
|
15
|
+
const self = s
|
|
16
|
+
return {
|
|
17
|
+
getGlyphsForZoomLevel(bpPerPx: number): ObservableMap<string, Glyph> {
|
|
18
|
+
const existingZoomLevel = self.glyphs.get(bpPerPx)
|
|
19
|
+
if (existingZoomLevel) {
|
|
20
|
+
return existingZoomLevel
|
|
21
|
+
}
|
|
22
|
+
const newZoomLevel = observable.map()
|
|
23
|
+
self.glyphs.set(bpPerPx, newZoomLevel)
|
|
24
|
+
return newZoomLevel
|
|
25
|
+
},
|
|
26
|
+
createGlyph() {
|
|
27
|
+
return new BoxGlyph()
|
|
28
|
+
},
|
|
29
|
+
/** get the appropriate glyph for the given top-level feature */
|
|
30
|
+
getGlyph(feature: AnnotationFeatureI, bpPerPx: number) {
|
|
31
|
+
const glyphsForZoomLevel = this.getGlyphsForZoomLevel(bpPerPx)
|
|
32
|
+
const glyphForFeature = glyphsForZoomLevel.get(feature._id)
|
|
33
|
+
if (glyphForFeature) {
|
|
34
|
+
return glyphForFeature
|
|
35
|
+
}
|
|
36
|
+
const newGlyph = this.createGlyph()
|
|
37
|
+
glyphsForZoomLevel.set(feature._id, newGlyph)
|
|
38
|
+
return newGlyph
|
|
39
|
+
},
|
|
40
|
+
afterAttach() {
|
|
41
|
+
// autorun to clean up old glyph zoom levels
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
|
|
2
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
+
import { Instance, types } from 'mobx-state-tree'
|
|
4
|
+
|
|
5
|
+
import { TabularEditorStateModelType } from '../../TabularEditor'
|
|
6
|
+
import { mouseEventsModelFactory } from './mouseEvents'
|
|
7
|
+
|
|
8
|
+
export function stateModelFactory(
|
|
9
|
+
pluginManager: PluginManager,
|
|
10
|
+
configSchema: AnyConfigurationSchemaType,
|
|
11
|
+
) {
|
|
12
|
+
// TODO: this needs to be refactored so that the final composition of the
|
|
13
|
+
// state model mixins happens here in one central place
|
|
14
|
+
return mouseEventsModelFactory(pluginManager, configSchema)
|
|
15
|
+
.props({ tabularEditor: types.optional(TabularEditorStateModelType, {}) })
|
|
16
|
+
.named('LinearApolloDisplay')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type LinearApolloDisplayStateModel = ReturnType<typeof stateModelFactory>
|
|
20
|
+
export type LinearApolloDisplay = Instance<LinearApolloDisplayStateModel>
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
|
|
2
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
+
import { AbstractSessionModel, doesIntersect2 } from '@jbrowse/core/util'
|
|
4
|
+
import { AnnotationFeatureI } from 'apollo-mst'
|
|
5
|
+
import { autorun, observable } from 'mobx'
|
|
6
|
+
import { addDisposer, isAlive } from 'mobx-state-tree'
|
|
7
|
+
|
|
8
|
+
import { ApolloSessionModel } from '../../session'
|
|
9
|
+
import { baseModelFactory } from './base'
|
|
10
|
+
import { getGlyph } from './getGlyph'
|
|
11
|
+
|
|
12
|
+
export function layoutsModelFactory(
|
|
13
|
+
pluginManager: PluginManager,
|
|
14
|
+
configSchema: AnyConfigurationSchemaType,
|
|
15
|
+
) {
|
|
16
|
+
const BaseLinearApolloDisplay = baseModelFactory(pluginManager, configSchema)
|
|
17
|
+
|
|
18
|
+
return BaseLinearApolloDisplay.named('LinearApolloDisplayLayouts')
|
|
19
|
+
.props({
|
|
20
|
+
featuresMinMaxLimit: 500_000,
|
|
21
|
+
})
|
|
22
|
+
.volatile(() => ({
|
|
23
|
+
seenFeatures: observable.map<string, AnnotationFeatureI>(),
|
|
24
|
+
}))
|
|
25
|
+
.views((self) => ({
|
|
26
|
+
get featuresMinMax() {
|
|
27
|
+
const { assemblyManager } =
|
|
28
|
+
self.session as unknown as AbstractSessionModel
|
|
29
|
+
return self.displayedRegions.map((region) => {
|
|
30
|
+
const assembly = assemblyManager.get(region.assemblyName)
|
|
31
|
+
let min: number | undefined
|
|
32
|
+
let max: number | undefined
|
|
33
|
+
const { end, refName, start } = region
|
|
34
|
+
for (const [, feature] of self.seenFeatures) {
|
|
35
|
+
if (
|
|
36
|
+
refName !== assembly?.getCanonicalRefName(feature.refSeq) ||
|
|
37
|
+
!doesIntersect2(start, end, feature.min, feature.max) ||
|
|
38
|
+
feature.length > self.featuresMinMaxLimit
|
|
39
|
+
) {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
if (min === undefined) {
|
|
43
|
+
;({ min } = feature)
|
|
44
|
+
}
|
|
45
|
+
if (max === undefined) {
|
|
46
|
+
;({ max } = feature)
|
|
47
|
+
}
|
|
48
|
+
if (feature.min < min) {
|
|
49
|
+
;({ min } = feature)
|
|
50
|
+
}
|
|
51
|
+
if (feature.end > max) {
|
|
52
|
+
;({ max } = feature)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (min !== undefined && max !== undefined) {
|
|
56
|
+
return [min, max]
|
|
57
|
+
}
|
|
58
|
+
return
|
|
59
|
+
})
|
|
60
|
+
},
|
|
61
|
+
}))
|
|
62
|
+
.actions((self) => ({
|
|
63
|
+
addSeenFeature(feature: AnnotationFeatureI) {
|
|
64
|
+
self.seenFeatures.set(feature._id, feature)
|
|
65
|
+
},
|
|
66
|
+
deleteSeenFeature(featureId: string) {
|
|
67
|
+
self.seenFeatures.delete(featureId)
|
|
68
|
+
},
|
|
69
|
+
}))
|
|
70
|
+
.views((self) => ({
|
|
71
|
+
get featureLayouts() {
|
|
72
|
+
const { assemblyManager } =
|
|
73
|
+
self.session as unknown as AbstractSessionModel
|
|
74
|
+
return self.displayedRegions.map((region, idx) => {
|
|
75
|
+
const assembly = assemblyManager.get(region.assemblyName)
|
|
76
|
+
const featureLayout = new Map<
|
|
77
|
+
number,
|
|
78
|
+
[number, AnnotationFeatureI][]
|
|
79
|
+
>()
|
|
80
|
+
const minMax = self.featuresMinMax[idx]
|
|
81
|
+
if (!minMax) {
|
|
82
|
+
return featureLayout
|
|
83
|
+
}
|
|
84
|
+
const [min, max] = minMax
|
|
85
|
+
const rows: boolean[][] = []
|
|
86
|
+
const { end, refName, start } = region
|
|
87
|
+
for (const [id, feature] of self.seenFeatures.entries()) {
|
|
88
|
+
if (!isAlive(feature)) {
|
|
89
|
+
self.deleteSeenFeature(id)
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
if (
|
|
93
|
+
refName !== assembly?.getCanonicalRefName(feature.refSeq) ||
|
|
94
|
+
!doesIntersect2(start, end, feature.min, feature.max)
|
|
95
|
+
) {
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
const rowCount = getGlyph(feature, self.lgv.bpPerPx).getRowCount(
|
|
99
|
+
feature,
|
|
100
|
+
self.lgv.bpPerPx,
|
|
101
|
+
)
|
|
102
|
+
let startingRow = 0
|
|
103
|
+
let placed = false
|
|
104
|
+
while (!placed) {
|
|
105
|
+
let rowsForFeature = rows.slice(
|
|
106
|
+
startingRow,
|
|
107
|
+
startingRow + rowCount,
|
|
108
|
+
)
|
|
109
|
+
if (rowsForFeature.length < rowCount) {
|
|
110
|
+
for (let i = 0; i < rowCount - rowsForFeature.length; i++) {
|
|
111
|
+
const newRowNumber = rows.length
|
|
112
|
+
rows[newRowNumber] = Array.from({ length: max - min })
|
|
113
|
+
featureLayout.set(newRowNumber, [])
|
|
114
|
+
}
|
|
115
|
+
rowsForFeature = rows.slice(startingRow, startingRow + rowCount)
|
|
116
|
+
}
|
|
117
|
+
if (
|
|
118
|
+
rowsForFeature
|
|
119
|
+
.map((rowForFeature) => {
|
|
120
|
+
// zero-length features are allowed in the spec
|
|
121
|
+
const featureMax =
|
|
122
|
+
feature.max - feature.min === 0
|
|
123
|
+
? feature.min + 1
|
|
124
|
+
: feature.max
|
|
125
|
+
let start = feature.min - min,
|
|
126
|
+
end = featureMax - min
|
|
127
|
+
if (feature.min - min < 0) {
|
|
128
|
+
start = 0
|
|
129
|
+
end = featureMax - feature.min
|
|
130
|
+
}
|
|
131
|
+
return rowForFeature.slice(start, end).some(Boolean)
|
|
132
|
+
})
|
|
133
|
+
.some(Boolean)
|
|
134
|
+
) {
|
|
135
|
+
startingRow += 1
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
for (
|
|
139
|
+
let rowNum = startingRow;
|
|
140
|
+
rowNum < startingRow + rowCount;
|
|
141
|
+
rowNum++
|
|
142
|
+
) {
|
|
143
|
+
const row = rows[rowNum]
|
|
144
|
+
let start = feature.min - min,
|
|
145
|
+
end = feature.max - min
|
|
146
|
+
if (feature.min - min < 0) {
|
|
147
|
+
start = 0
|
|
148
|
+
end = feature.max - feature.min
|
|
149
|
+
}
|
|
150
|
+
row.fill(true, start, end)
|
|
151
|
+
const layoutRow = featureLayout.get(rowNum)
|
|
152
|
+
layoutRow?.push([rowNum - startingRow, feature])
|
|
153
|
+
}
|
|
154
|
+
placed = true
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return featureLayout
|
|
158
|
+
})
|
|
159
|
+
},
|
|
160
|
+
getFeatureLayoutPosition(feature: AnnotationFeatureI) {
|
|
161
|
+
const { featureLayouts } = this
|
|
162
|
+
for (const layout of featureLayouts) {
|
|
163
|
+
for (const [layoutRowNum, layoutRow] of layout) {
|
|
164
|
+
for (const [featureRowNum, layoutFeature] of layoutRow) {
|
|
165
|
+
if (featureRowNum !== 0) {
|
|
166
|
+
// Same top-level feature in all feature rows, so only need to
|
|
167
|
+
// check the first one
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
170
|
+
if (feature._id === layoutFeature._id) {
|
|
171
|
+
return { layoutRow: layoutRowNum, featureRow: featureRowNum }
|
|
172
|
+
}
|
|
173
|
+
if (layoutFeature.hasDescendant(feature._id)) {
|
|
174
|
+
const row = getGlyph(
|
|
175
|
+
layoutFeature,
|
|
176
|
+
self.lgv.bpPerPx,
|
|
177
|
+
).getRowForFeature(layoutFeature, feature)
|
|
178
|
+
if (row !== undefined) {
|
|
179
|
+
return { layoutRow: layoutRowNum, featureRow: row }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return
|
|
186
|
+
},
|
|
187
|
+
}))
|
|
188
|
+
.views((self) => ({
|
|
189
|
+
get highestRow() {
|
|
190
|
+
return Math.max(
|
|
191
|
+
0,
|
|
192
|
+
...self.featureLayouts.map((layout) => Math.max(...layout.keys())),
|
|
193
|
+
)
|
|
194
|
+
},
|
|
195
|
+
}))
|
|
196
|
+
.actions((self) => ({
|
|
197
|
+
afterAttach() {
|
|
198
|
+
addDisposer(
|
|
199
|
+
self,
|
|
200
|
+
autorun(
|
|
201
|
+
() => {
|
|
202
|
+
if (!self.lgv.initialized || self.regionCannotBeRendered()) {
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
for (const region of self.regions) {
|
|
206
|
+
const assembly = (
|
|
207
|
+
self.session as unknown as ApolloSessionModel
|
|
208
|
+
).apolloDataStore.assemblies.get(region.assemblyName)
|
|
209
|
+
const ref = assembly?.getByRefName(region.refName)
|
|
210
|
+
for (const [, feature] of ref?.features ?? new Map()) {
|
|
211
|
+
if (
|
|
212
|
+
doesIntersect2(
|
|
213
|
+
region.start,
|
|
214
|
+
region.end,
|
|
215
|
+
feature.start,
|
|
216
|
+
feature.end,
|
|
217
|
+
) &&
|
|
218
|
+
!self.seenFeatures.has(feature._id)
|
|
219
|
+
) {
|
|
220
|
+
self.addSeenFeature(feature)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{ name: 'LinearApolloDisplaySetSeenFeatures', delay: 1000 },
|
|
226
|
+
),
|
|
227
|
+
)
|
|
228
|
+
},
|
|
229
|
+
}))
|
|
230
|
+
}
|