Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New display type for drawing arcs #2534

Merged
merged 25 commits into from Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f992d02
migrating arc plugin
carolinebridge Nov 17, 2021
c3c18dc
forgot this in there
carolinebridge Nov 17, 2021
2ff14d7
updating package.json
carolinebridge Nov 17, 2021
d30c5d5
changing configs attempt to compile
carolinebridge Nov 22, 2021
4f7330b
lint and missing dependency
carolinebridge Nov 22, 2021
ff35fe1
missing properties on package
carolinebridge Nov 22, 2021
fdbfdbe
core plugin adjustmtnets
carolinebridge Nov 22, 2021
2a7ad86
Copy alignments/package.json -> arc/package.json and then rename alig…
cmdcolin Nov 22, 2021
c67605f
update tests
carolinebridge Nov 22, 2021
221c50b
update tests
carolinebridge Nov 22, 2021
7150ac4
tests
carolinebridge Nov 22, 2021
8a0e7ff
adding variant arc display, updating config demo and config with demo…
carolinebridge Nov 24, 2021
c260479
Merge branch 'main' into add-arc-plugin
carolinebridge Nov 25, 2021
0e41d56
snapshots
carolinebridge Nov 25, 2021
de4c81f
debugging failed tests when adding new variant display
carolinebridge Nov 25, 2021
036389c
adding test and onclick colour change
carolinebridge Nov 26, 2021
2c75cd0
updating tests
carolinebridge Nov 29, 2021
4e69140
lint
carolinebridge Nov 29, 2021
f6ca644
Merge branch 'main' into add-arc-plugin
carolinebridge Dec 3, 2021
2fbffeb
removing variant ; fixing overlap click issues ; fixing disappearing …
carolinebridge Dec 7, 2021
a28ab74
updating test
carolinebridge Dec 7, 2021
b62948d
updating tests
carolinebridge Dec 7, 2021
b67c8fb
colin's comments update
carolinebridge Dec 10, 2021
88eb919
adding arc height proportional to log as well as label height calcula…
carolinebridge Dec 15, 2021
b759efa
adding wiki link to formula
carolinebridge Dec 15, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/util/jexl.ts
Expand Up @@ -42,6 +42,7 @@ export default function (/* config?: any*/): JexlNonBuildable {
j.addFunction('floor', Math.floor)
j.addFunction('round', Math.round)
j.addFunction('abs', Math.abs)
j.addFunction('log10', Math.log10)
j.addFunction('parseInt', Number.parseInt)
j.addFunction('parseFloat', Number.parseFloat)

Expand Down
53 changes: 53 additions & 0 deletions plugins/arc/package.json
@@ -0,0 +1,53 @@
{
"name": "@jbrowse/plugin-arc",
"version": "1.5.1",
"description": "JBrowse 2 arc adapters, tracks, etc.",
"keywords": [
"jbrowse",
"jbrowse2"
],
"license": "Apache-2.0",
"homepage": "https://jbrowse.org",
"bugs": "https://github.com/GMOD/jbrowse-components/issues",
"repository": {
"type": "git",
"url": "https://github.com/GMOD/jbrowse-components.git",
"directory": "plugins/arc"
},
"author": "JBrowse Team",
"distMain": "dist/index.js",
"srcMain": "src/index.ts",
"main": "src/index.ts",
"distModule": "dist/plugin-arc.esm.js",
"module": "",
"files": [
"dist",
"src"
],
"scripts": {
"start": "tsdx watch --verbose --noClean",
"build": "tsdx build",
"test": "cd ../..; jest plugins/arc",
"prepublishOnly": "yarn test",
"prepack": "yarn build; yarn useDist",
"postpack": "yarn useSrc",
"useDist": "node ../../scripts/useDist.js",
"useSrc": "node ../../scripts/useSrc.js"
},
"dependencies": {
"react-svg-tooltip": "^0.0.11"
},
"peerDependencies": {
"@jbrowse/core": "^1.0.0",
"@jbrowse/plugin-linear-genome-view": "^1.0.0",
"@jbrowse/plugin-wiggle": "^1.0.0",
"@material-ui/core": "^4.12.2",
"mobx": "^5.10.1",
"mobx-react": "^6.0.0",
"mobx-state-tree": "3.14.1",
"prop-types": "^15.0.0",
"react": ">=16.8.0",
"rxjs": "^6.0.0"
},
"private": true
}
3 changes: 3 additions & 0 deletions plugins/arc/src/ArcRenderer/ArcRenderer.tsx
@@ -0,0 +1,3 @@
import FeatureRendererType from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType'

export default class extends FeatureRendererType {}
45 changes: 45 additions & 0 deletions plugins/arc/src/ArcRenderer/ArcRendering.test.tsx
@@ -0,0 +1,45 @@
import SimpleFeature from '@jbrowse/core/util/simpleFeature'
import React from 'react'
import { render } from '@testing-library/react'
import Rendering from './ArcRendering'

test('no features', () => {
const { container } = render(
<Rendering
width={500}
height={500}
regions={[{ refName: 'zonk', start: 0, end: 300 }]}
blockKey={1}
config={{}}
bpPerPx={3}
displayModel={{}}
features={new Map()}
/>,
)

expect(container.firstChild).toMatchSnapshot()
})

test('one feature', () => {
const { container } = render(
<Rendering
width={500}
height={500}
regions={[{ refName: 'zonk', start: 0, end: 1000 }]}
blockKey={1}
features={
new Map([
[
'one',
new SimpleFeature({ uniqueId: 'one', score: 10, start: 1, end: 3 }),
],
])
}
config={{ type: 'DummyRenderer' }}
bpPerPx={3}
displayModel={{}}
/>,
)

expect(container.firstChild).toMatchSnapshot()
})
131 changes: 131 additions & 0 deletions plugins/arc/src/ArcRenderer/ArcRendering.tsx
@@ -0,0 +1,131 @@
import React from 'react'
import { readConfObject } from '@jbrowse/core/configuration'
import { bpSpanPx, measureText } from '@jbrowse/core/util'
import { observer } from 'mobx-react'
import { Tooltip } from 'react-svg-tooltip'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function ArcRendering(props: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onClick = (event: any, id: any) => {
const { onFeatureClick: handler } = props
if (!handler) {
return undefined
}
return handler(event, id)
}

const {
features,
config,
regions,
blockKey,
bpPerPx,
displayModel: { selectedFeatureId },
} = props
const [region] = regions
const arcsRendered = []

for (const feature of features.values()) {
const [left, right] = bpSpanPx(
feature.get('start'),
feature.get('end'),
region,
bpPerPx,
)

const featureId = feature.id()
const id = blockKey + '-' + featureId
let stroke = readConfObject(config, 'color', { feature })
let textStroke = 'black'
if (
selectedFeatureId &&
String(selectedFeatureId) === String(feature.id())
) {
stroke = textStroke = 'red'
}
const label = readConfObject(config, 'label', { feature })
const caption = readConfObject(config, 'caption', { feature })
const strokeWidth = readConfObject(config, 'thickness', { feature }) || 1
const height = readConfObject(config, 'height', { feature }) || 100
const ref = React.createRef<SVGPathElement>()
const tooltipWidth = 20 + measureText(caption?.toString())

const t = 0.5
// formula: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
const textYCoord =
(1 - t) * (1 - t) * (1 - t) * 0 +
3 * ((1 - t) * (1 - t)) * (t * height) +
3 * (1 - t) * (t * t) * height +
t * t * t * 0

arcsRendered.push(
<g key={id} onClick={e => onClick(e, featureId)}>
<path
id={id}
d={`M ${left} 0 C ${left} ${height}, ${right} ${height}, ${right} 0`}
stroke={stroke}
strokeWidth={strokeWidth}
fill="transparent"
onClick={e => onClick(e, featureId)}
ref={ref}
pointerEvents="stroke"
/>
<Tooltip triggerRef={ref}>
<rect
x={12}
y={0}
width={tooltipWidth}
height={20}
rx={5}
ry={5}
fill="black"
fillOpacity="50%"
/>
<text
x={22}
y={14}
fontSize={10}
fill="white"
textLength={tooltipWidth - 20}
>
{caption}
</text>
</Tooltip>
<text
x={left + (right - left) / 2}
y={textYCoord + 3}
style={{ stroke: 'white', strokeWidth: '0.6em' }}
>
{label}
</text>
<text
x={left + (right - left) / 2}
y={textYCoord + 3}
style={{ stroke: textStroke }}
>
{label}
</text>
</g>,
)
}

const width = (region.end - region.start) / bpPerPx
const height = 500

return (
<svg
className="ArcRendering"
width={width}
height={height}
style={{
outline: 'none',
position: 'relative',
}}
>
{arcsRendered}
</svg>
)
}

export default observer(ArcRendering)
@@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`no features 1`] = `
<svg
class="ArcRendering"
height="500"
style="outline: none; position: relative;"
width="100"
/>
`;

exports[`one feature 1`] = `
<svg
class="ArcRendering"
height="500"
style="outline: none; position: relative;"
width="333.3333333333333"
>
<g>
<path
d="M 0.3 0 C 0.3 100, 1 100, 1 0"
fill="transparent"
id="1-one"
pointer-events="stroke"
stroke-width="1"
/>
<g />
<text
style="stroke: white; stroke-width: 0.6em;"
x="0.6499999999999999"
y="78"
/>
<text
style="stroke: black;"
x="0.6499999999999999"
y="78"
/>
</g>
</svg>
`;
36 changes: 36 additions & 0 deletions plugins/arc/src/ArcRenderer/configSchema.tsx
@@ -0,0 +1,36 @@
import { ConfigurationSchema } from '@jbrowse/core/configuration'

export default ConfigurationSchema(
'ArcRenderer',
{
color: {
type: 'color',
description: 'the color of the arcs',
defaultValue: 'darkblue',
},
thickness: {
type: 'number',
description: 'the thickness of the arcs',
defaultValue: `jexl:logThickness(feature,'score')`,
},
label: {
type: 'string',
description: 'the label to appear at the apex of the arcs',
defaultValue: `jexl:get(feature,'score')`,
contextVariable: ['feature'],
},
height: {
type: 'number',
description: 'the height of the arcs',
defaultValue: `jexl:log10(get(feature,'end')-get(feature,'start'))*50`,
},
caption: {
type: 'string',
description:
'the caption to appear when hovering over any point on the arcs',
defaultValue: `jexl:get(feature,'name')`,
contextVariable: ['feature'],
},
},
{ explicitlyTyped: true },
)
3 changes: 3 additions & 0 deletions plugins/arc/src/ArcRenderer/index.tsx
@@ -0,0 +1,3 @@
export { default as ReactComponent } from './ArcRendering'
export { default as configSchema } from './configSchema'
export { default } from './ArcRenderer'
24 changes: 24 additions & 0 deletions plugins/arc/src/LinearArcDisplay/configSchema.tsx
@@ -0,0 +1,24 @@
import PluginManager from '@jbrowse/core/PluginManager'
import { types } from 'mobx-state-tree'
import { ConfigurationSchema } from '@jbrowse/core/configuration'

export function configSchemaFactory(pluginManager: PluginManager) {
const LGVPlugin = pluginManager.getPlugin(
'LinearGenomeViewPlugin',
) as import('@jbrowse/plugin-linear-genome-view').default
// @ts-ignore
const { baseLinearDisplayConfigSchema } = LGVPlugin.exports
return ConfigurationSchema(
'LinearArcDisplay',
{
renderer: types.optional(
pluginManager.pluggableConfigSchemaType('renderer'),
{ type: 'ArcRenderer' },
),
},
{
baseConfiguration: baseLinearDisplayConfigSchema,
explicitlyTyped: true,
},
)
}
2 changes: 2 additions & 0 deletions plugins/arc/src/LinearArcDisplay/index.tsx
@@ -0,0 +1,2 @@
export { configSchemaFactory } from './configSchema'
export { stateModelFactory } from './model'