Skip to content

Commit

Permalink
Render alignment shape
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Nov 7, 2024
1 parent 58d5e36 commit 6999f63
Show file tree
Hide file tree
Showing 10 changed files with 495 additions and 383 deletions.
22 changes: 10 additions & 12 deletions plugins/alignments/src/LinearPileupDisplay/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import {
} from '@jbrowse/core/configuration'
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
import { getEnv, getSession, getContainingView } from '@jbrowse/core/util'
import { getUniqueModificationValues } from '../shared'

import { createAutorun, randomColor, modificationColors } from '../util'
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'

// utils
import { getUniqueModificationValues } from '../shared'
import { createAutorun, getColorForModification } from '../util'

// icons
import VisibilityIcon from '@mui/icons-material/Visibility'
import SortIcon from '@mui/icons-material/Sort'
Expand All @@ -26,7 +27,7 @@ import { observable } from 'mobx'
// lazies
const SortByTagDialog = lazy(() => import('./components/SortByTagDialog'))
const GroupByDialog = lazy(() => import('./components/GroupByDialog'))
const ModificationsDialog = lazy(
const ColorByModificationsDialog = lazy(
() => import('./components/ColorByModificationsDialog'),
)

Expand Down Expand Up @@ -92,14 +93,11 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) {
* #action
*/
updateModificationColorMap(uniqueModifications: string[]) {
uniqueModifications.forEach(value => {
if (!self.modificationTagMap.has(value)) {
self.modificationTagMap.set(
value,
modificationColors[value] || randomColor(value),
)
for (const m of uniqueModifications) {
if (!self.modificationTagMap.has(m)) {
self.modificationTagMap.set(m, getColorForModification(m))
}
})
}
},
/**
* #action
Expand Down Expand Up @@ -317,7 +315,7 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) {
label: 'Modifications or methylation',
onClick: () => {
getSession(self).queueDialog(doneCallback => [
ModificationsDialog,
ColorByModificationsDialog,
{
model: self,
handleClose: doneCallback,
Expand Down
14 changes: 5 additions & 9 deletions plugins/alignments/src/LinearSNPCoverageDisplay/models/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import SerializableFilterChain from '@jbrowse/core/pluggableElementTypes/rendere

// locals
import { FilterModel, IFilter, getUniqueModificationValues } from '../../shared'
import { createAutorun, modificationColors } from '../../util'
import { randomColor } from '../../util'
import { createAutorun, getColorForModification } from '../../util'

// lazies
const Tooltip = lazy(() => import('../components/Tooltip'))
Expand Down Expand Up @@ -111,14 +110,11 @@ function stateModelFactory(
* #action
*/
updateModificationColorMap(uniqueModifications: string[]) {
uniqueModifications.forEach(value => {
if (!self.modificationTagMap.has(value)) {
self.modificationTagMap.set(
value,
modificationColors[value] || randomColor(value),
)
for (const m of uniqueModifications) {
if (!self.modificationTagMap.has(m)) {
self.modificationTagMap.set(m, getColorForModification(m))
}
})
}
},
}))
.views(self => {
Expand Down
141 changes: 120 additions & 21 deletions plugins/alignments/src/PileupRenderer/renderAlignmentShape.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { bpSpanPx } from '@jbrowse/core/util'
import { RenderArgsDeserialized } from './PileupRenderer'
import { LayoutFeature } from './util'
import { parseCigar } from '../MismatchParser'

export function renderAlignmentShape({
ctx,
Expand All @@ -16,30 +17,128 @@ export function renderAlignmentShape({
const region = regions[0]!
const s = feature.get('start')
const e = feature.get('end')
const [leftPx, rightPx] = bpSpanPx(s, e, region, bpPerPx)
const CIGAR = feature.get('CIGAR')
const flip = region.reversed ? -1 : 1
const strand = feature.get('strand') * flip
if (bpPerPx < 10 && heightPx > 5) {
if (strand === -1) {
ctx.beginPath()
ctx.moveTo(leftPx - 5, topPx + heightPx / 2)
ctx.lineTo(leftPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx)
ctx.lineTo(leftPx, topPx)
ctx.closePath()
ctx.fill()
} else {
ctx.beginPath()
ctx.moveTo(leftPx, topPx)
ctx.lineTo(leftPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx + heightPx)
ctx.lineTo(rightPx + 5, topPx + heightPx / 2)
ctx.lineTo(rightPx, topPx)
ctx.closePath()
ctx.fill()
const cigarOps = parseCigar(CIGAR)
const renderChevrons = bpPerPx < 10 && heightPx > 5
if (CIGAR) {
if (strand === 1) {
let drawLen = 0
let drawStart = s
for (let i = 0; i < cigarOps.length; i += 2) {
const opLen = +cigarOps[i]!
const op = cigarOps[i + 1]!
if (op === 'M' || op === 'X' || op === '=' || op === 'D') {
drawLen += opLen
} else if (op === 'N') {
if (drawStart !== drawLen) {
const [leftPx, rightPx] = bpSpanPx(
drawStart,
drawStart + drawLen,
region,
bpPerPx,
)
const w = rightPx - leftPx
ctx.fillRect(leftPx, topPx, w, heightPx)
}
drawStart += drawLen + opLen
drawLen = 0
}
}

if (drawStart !== drawLen) {
const [leftPx, rightPx] = bpSpanPx(
drawStart,
drawStart + drawLen,
region,
bpPerPx,
)
const w = rightPx - leftPx

if (renderChevrons) {
ctx.beginPath()
ctx.moveTo(leftPx, topPx)
ctx.lineTo(leftPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx + heightPx)
ctx.lineTo(rightPx + 5, topPx + heightPx / 2)
ctx.lineTo(rightPx, topPx)
ctx.closePath()
ctx.fill()
} else {
ctx.fillRect(leftPx, topPx, w, heightPx)
}
}
} else if (strand === -1) {
let drawLen = 0
let drawStart = e
for (let i = cigarOps.length - 2; i >= 0; i -= 2) {
const opLen = +cigarOps[i]!
const op = cigarOps[i + 1]!
if (op === 'M' || op === 'X' || op === '=' || op === 'D') {
drawLen += opLen
} else if (op === 'N') {
if (drawLen !== 0) {
const [leftPx, rightPx] = bpSpanPx(
drawStart - drawLen,
drawStart,
region,
bpPerPx,
)
ctx.fillRect(leftPx, topPx, rightPx - leftPx, heightPx)
}
drawStart -= drawLen + opLen
drawLen = 0
}
}

if (drawLen !== 0) {
const [leftPx, rightPx] = bpSpanPx(
drawStart - drawLen,
drawStart,
region,
bpPerPx,
)
const w = rightPx - leftPx

if (renderChevrons) {
ctx.beginPath()
ctx.moveTo(leftPx - 5, topPx + heightPx / 2)
ctx.lineTo(leftPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx)
ctx.lineTo(leftPx, topPx)
ctx.closePath()
ctx.fill()
} else {
ctx.fillRect(leftPx, topPx, w, heightPx)
}
}
}
} else {
ctx.fillRect(leftPx, topPx, rightPx - leftPx, heightPx)
const [leftPx, rightPx] = bpSpanPx(s, e, region, bpPerPx)
if (bpPerPx < 10 && heightPx > 5) {
if (strand === -1) {
ctx.beginPath()
ctx.moveTo(leftPx - 5, topPx + heightPx / 2)
ctx.lineTo(leftPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx)
ctx.lineTo(leftPx, topPx)
ctx.closePath()
ctx.fill()
} else {
ctx.beginPath()
ctx.moveTo(leftPx, topPx)
ctx.lineTo(leftPx, topPx + heightPx)
ctx.lineTo(rightPx, topPx + heightPx)
ctx.lineTo(rightPx + 5, topPx + heightPx / 2)
ctx.lineTo(rightPx, topPx)
ctx.closePath()
ctx.fill()
}
} else {
ctx.fillRect(leftPx, topPx, rightPx - leftPx, heightPx)
}
}
}
16 changes: 6 additions & 10 deletions plugins/alignments/src/PileupRenderer/renderMismatches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,16 @@ export function renderMismatches({
} else if (mismatch.type === 'skip') {
// fix to avoid bad rendering note that this was also related to chrome
// bug https://bugs.chromium.org/p/chromium/issues/detail?id=1131528
//
// also affected firefox ref #1236 #2750
if (leftPx + widthPx > 0) {
// make small exons more visible when zoomed far out
const adjustPx = widthPx - (bpPerPx > 10 ? 1.5 : 0)
ctx.clearRect(leftPx, topPx, adjustPx, heightPx)
fillRect(
ctx,
Math.max(0, leftPx),
topPx + heightPx / 2 - 1,
adjustPx + Math.min(leftPx, 0),
1,
canvasWidth,
'#333',
)
const l = Math.max(0, leftPx)
const t = topPx + heightPx / 2 - 1
const w = adjustPx + Math.min(leftPx, 0)
const h = 1
fillRect(ctx, l, t, w, h, canvasWidth, 'rgb(151,184,201)')
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
end: skip.end,
strand: skip.strand,
score: skip.score,
xs: skip.xs,
effectiveStrand: skip.effectiveStrand,
},
}),
)
Expand Down
26 changes: 23 additions & 3 deletions plugins/alignments/src/SNPCoverageAdapter/generateCoverageBins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,31 @@ export default async function generateCoverageBins(
}

if (mismatch.type === 'skip') {
const hash = `${mstart}_${mend}_${fstrand}`
const xs = getTag(feature, 'XS')
const ts = getTag(feature, 'ts')
const TS = getTag(feature, 'TS')
let effectiveStrand = 0
if (xs === '+') {
effectiveStrand = 1
} else if (xs === '-') {
effectiveStrand = -1
} else if (ts === '-') {
effectiveStrand = fstrand * -1
} else if (ts === '+') {
effectiveStrand = fstrand
} else if (TS === '-') {
effectiveStrand = -1
} else if (TS === '+') {
effectiveStrand = 1
}
const hash = `${mstart}_${mend}_${effectiveStrand}`
if (skipmap[hash] === undefined) {
skipmap[hash] = {
feature: feature,
start: mstart,
end: mend,
strand: fstrand,
xs: getTag(feature, 'XS') || getTag(feature, 'TS'),
effectiveStrand,
score: 0,
}
}
Expand All @@ -249,5 +266,8 @@ export default async function generateCoverageBins(
}
}

return { bins, skipmap }
return {
bins,
skipmap,
}
}
2 changes: 1 addition & 1 deletion plugins/alignments/src/SNPCoverageAdapter/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type SkipMap = Record<
start: number
end: number
strand: number
xs: string
effectiveStrand: number
}
>

Expand Down
Loading

0 comments on commit 6999f63

Please sign in to comment.