Skip to content

Commit

Permalink
Render AA-seqs as translations
Browse files Browse the repository at this point in the history
  • Loading branch information
jjtimmons committed Nov 13, 2022
1 parent 1d5ea21 commit 33394f4
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 51 deletions.
3 changes: 2 additions & 1 deletion demo/lib/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export default class App extends React.Component<any, AppState> {

componentDidMount = async () => {
const seq = await seqparse(file);
this.setState({ annotations: seq.annotations, name: seq.name, seq: seq.seq });
// this.setState({ annotations: seq.annotations, name: seq.name, seq: seq.seq });
this.setState({ annotations: seq.annotations, name: seq.name, seq: "MSKGEELFTGVVPILVELDGDVNGHKFSVSGEGEGDATYGKLTLKFICTTGKLPVPWPTLVTTFSYGVQCFSRYPDHMKQHDFFKSAMPEGYVQERTIFFKDDGNYKTRAEVKFEGDTLVNRIELKGIDFKEDGNILGHKLEYNYNSHNVYIMADKQKNGIKVNFKIRHNIEDGSVQLADHYQQNTPIGDGPVLLPDNHYLSTQSALSKDPNEKRDHMVLLEFVTAAGITHGMDELYK" });
};

toggleSidebar = () => {
Expand Down
10 changes: 8 additions & 2 deletions src/Linear/Index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from "react";

import { Size } from "../elements";
import { SeqType, Size } from "../elements";
import { FindXAndWidthType } from "./SeqBlock";

interface IndexProps {
Expand All @@ -9,6 +9,7 @@ interface IndexProps {
firstBase: number;
lastBase: number;
seq: string;
seqType: SeqType;
showIndex: boolean;
size: Size;
yDiff: number;
Expand All @@ -23,7 +24,7 @@ export default class Index extends React.PureComponent<IndexProps> {
// by the number set for tally thresholding and, if it is, 2) add its location to the list
// of positions for tickInc
genTicks = () => {
const { charWidth, findXAndWidth, firstBase, seq, size, zoom } = this.props;
const { charWidth, findXAndWidth, firstBase, seq, seqType, size, zoom } = this.props;
const seqLength = seq.length;

// the tally's distance on the x-axis is zoom dependent:
Expand All @@ -49,6 +50,11 @@ export default class Index extends React.PureComponent<IndexProps> {
tickInc = 10;
}

// if rendering amino acids, double the tick frequency
if (seqType === "aa") {
tickInc = tickInc / 2;
}

// create the array that will hold all the indexes in the array
const tickIndexes: number[] = [];
if (firstBase === 0) {
Expand Down
10 changes: 7 additions & 3 deletions src/Linear/Linear.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createMultiRows, createSingleRows, stackElements } from "../elementsToR
import withViewerHOCs from "../handlers";
import { Selection } from "../handlers/selection";
import isEqual from "../isEqual";
import { createLinearTranslations } from "../sequence";
import { createTranslations } from "../sequence";
import InfiniteScroll from "./InfiniteScroll";
import SeqBlock from "./SeqBlock";

Expand Down Expand Up @@ -124,7 +124,7 @@ class Linear extends React.Component<LinearProps> {
const highlightRows = createSingleRows(highlights, bpsPerBlock, arrSize);

const translationRows = translations.length
? createSingleRows(createLinearTranslations(translations, seq, seqType), bpsPerBlock, arrSize)
? createSingleRows(createTranslations(translations, seq, seqType), bpsPerBlock, arrSize)
: new Array(arrSize).fill([]);

for (let i = 0; i < arrSize; i += 1) {
Expand All @@ -139,7 +139,10 @@ class Linear extends React.Component<LinearProps> {
ids[i] = seqs[i] + String(i);

// find the line height for the seq block based on how many rows need to be shown
let blockHeight = lineHeight * 2.1; // this is for padding between the SeqBlocks
let blockHeight = lineHeight * 1.1; // this is for padding between the SeqBlocks
if (seqType != "aa") {
blockHeight += lineHeight; // for sequence row
}
if (zoomed) {
blockHeight += showComplement ? lineHeight : 0; // double for complement + 2px margin
}
Expand Down Expand Up @@ -184,6 +187,7 @@ class Linear extends React.Component<LinearProps> {
searchRows={searchRows[i]}
seq={seqs[i]}
seqFontSize={this.props.seqFontSize}
seqType={seqType}
showComplement={showComplement}
showIndex={showIndex}
size={size}
Expand Down
22 changes: 18 additions & 4 deletions src/Linear/SeqBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import * as React from "react";

import { Annotation, CutSite, Highlight, InputRefFunc, NameRange, Range, Size, Translation } from "../elements";
import {
Annotation,
CutSite,
Highlight,
InputRefFunc,
NameRange,
Range,
SeqType,
Size,
Translation,
} from "../elements";
import AnnotationRows from "./Annotations";
import CutSiteRow from "./CutSites";
import Find from "./Find";
Expand Down Expand Up @@ -44,6 +54,7 @@ interface SeqBlockProps {
searchRows: Range[];
seq: string;
seqFontSize: number;
seqType: SeqType;
showComplement: boolean;
showIndex: boolean;
size: Size;
Expand Down Expand Up @@ -230,6 +241,7 @@ export default class SeqBlock extends React.PureComponent<SeqBlockProps> {
searchRows,
seq,
seqFontSize,
seqType,
showComplement,
showIndex,
size,
Expand Down Expand Up @@ -264,7 +276,7 @@ export default class SeqBlock extends React.PureComponent<SeqBlockProps> {

// height and yDiff of the sequence strand
const indexYDiff = cutSiteYDiff + cutSiteHeight;
const indexHeight = lineHeight;
const indexHeight = seqType === "aa" ? 0 : lineHeight; // if aa, no seq row is shown

// height and yDiff of the complement strand
const compYDiff = indexYDiff + indexHeight;
Expand Down Expand Up @@ -312,6 +324,7 @@ export default class SeqBlock extends React.PureComponent<SeqBlockProps> {
firstBase={firstBase}
lastBase={lastBase}
seq={seq}
seqType={seqType}
showIndex={showIndex}
size={size}
yDiff={indexRowYDiff}
Expand Down Expand Up @@ -371,6 +384,7 @@ export default class SeqBlock extends React.PureComponent<SeqBlockProps> {
inputRef={inputRef}
lastBase={lastBase}
seqBlockRef={this}
seqType={seqType}
translations={translations}
yDiff={translationYDiff}
onUnmount={onUnmount}
Expand Down Expand Up @@ -405,12 +419,12 @@ export default class SeqBlock extends React.PureComponent<SeqBlockProps> {
zoom={zoom}
/>
)}
{zoomed ? (
{zoomed && seqType !== "aa" ? (
<text {...textProps} className="la-vz-seq" data-testid="la-vz-seq" id={id} y={indexYDiff}>
{seq.split("").map(this.seqTextSpan)}
</text>
) : null}
{compSeq && zoomed && showComplement ? (
{compSeq && zoomed && showComplement && seqType !== "aa" ? (
<text {...textProps} className="la-vz-comp-seq" data-testid="la-vz-comp-seq" id={id} y={compYDiff}>
{compSeq.split("").map(this.seqTextSpan)}
</text>
Expand Down
47 changes: 28 additions & 19 deletions src/Linear/Translations.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react";

import { borderColorByIndex, colorByIndex } from "../colors";
import { InputRefFunc, Translation } from "../elements";
import { InputRefFunc, SeqType, Translation } from "../elements";
import randomid from "../randomid";
import { FindXAndWidthType } from "./SeqBlock";

Expand All @@ -16,6 +16,7 @@ interface TranslationRowsProps {
lastBase: number;
onUnmount: (a: unknown) => void;
seqBlockRef: unknown;
seqType: SeqType;
translations: Translation[];
yDiff: number;
}
Expand All @@ -32,6 +33,7 @@ const TranslationRows = ({
lastBase,
onUnmount,
seqBlockRef,
seqType,
translations,
yDiff,
}: TranslationRowsProps) => (
Expand All @@ -49,6 +51,7 @@ const TranslationRows = ({
inputRef={inputRef}
lastBase={lastBase}
seqBlockRef={seqBlockRef}
seqType={seqType}
translation={t}
y={yDiff + elementHeight * i}
onUnmount={onUnmount}
Expand All @@ -69,6 +72,7 @@ interface TranslationRowProps {
lastBase: number;
onUnmount: (a: unknown) => void;
seqBlockRef: unknown;
seqType: SeqType;
translation: Translation;
y: number;
}
Expand All @@ -88,14 +92,13 @@ class TranslationRow extends React.Component<TranslationRowProps> {

/**
* make the actual path string
*
* c = base pair count
* m = multiplier (FWD or REV)
*/
genPath = (count: number, multiplier: number) => {
const { charWidth, height: h } = this.props; // width adjust

const nW = count * charWidth;
const wA = multiplier * 3;

return `M 0 0
L ${nW} 0
L ${nW + wA} ${h / 2}
Expand All @@ -116,6 +119,7 @@ class TranslationRow extends React.Component<TranslationRowProps> {
inputRef,
lastBase,
seqBlockRef: element,
seqType,
translation,
y,
} = this.props;
Expand All @@ -124,8 +128,11 @@ class TranslationRow extends React.Component<TranslationRowProps> {

// build up a reference to this whole translation for
// selection handler (used only for context clicking right now)
const type = "TRANSLATION";
const ref = { element, end, name: "translation", start, type };
const ref = { element, end, name: "translation", start, type: "TRANSLATION" };

// if rendering an amino-acid sequence directly, each amino acid block is 1:1 with a "base pair".
// otherwise, each amino-acid covers three bases.
const bpPerBlockCount = seqType === "aa" ? 1 : 3;

// substring and split only the amino acids that are relevant to this
// particular sequence block
Expand All @@ -139,8 +146,8 @@ class TranslationRow extends React.Component<TranslationRowProps> {

// calculate the start and end point of each amino acid
// modulo needed here for translations that cross zero index
let AAStart = (start + i * 3) % fullSeq.length;
let AAEnd = start + i * 3 + 3;
let AAStart = (start + i * bpPerBlockCount) % fullSeq.length;
let AAEnd = start + i * bpPerBlockCount + bpPerBlockCount;

// build up a reference to this whole translation for
// selection handler (used only for context clicking right now)
Expand Down Expand Up @@ -170,20 +177,20 @@ class TranslationRow extends React.Component<TranslationRowProps> {
// larger translation

// the amino acid doesn't fit within this SeqBlock (even partially)
if (AAStart > lastBase || AAEnd < firstBase) return null;
if (AAStart >= lastBase || AAEnd <= firstBase) return null;

let textShow = true; // whether to show amino acids abbreviation
let bpCount = 3; // start off assuming the full thing is shown
let showAminoAcidLabel = true; // whether to show amino acids abbreviation
let bpCount = bpPerBlockCount; // start off assuming the full thing is shown
if (AAStart < firstBase) {
bpCount = Math.min(3, AAEnd - firstBase);
if (bpCount < 2) {
bpCount = Math.min(bpPerBlockCount, AAEnd - firstBase);
if (bpCount < 2 && seqType !== "aa") {
// w/ one bp, the amino acid is probably too small for an abbreviation
textShow = false;
showAminoAcidLabel = false;
}
} else if (AAEnd > lastBase) {
bpCount = Math.min(3, lastBase - AAStart);
if (bpCount < 2) {
textShow = false;
bpCount = Math.min(bpPerBlockCount, lastBase - AAStart);
if (bpCount < 2 && seqType !== "aa") {
showAminoAcidLabel = false;
}
}

Expand All @@ -207,9 +214,11 @@ class TranslationRow extends React.Component<TranslationRowProps> {
strokeWidth: 0.8,
}}
/>
{textShow && (

{showAminoAcidLabel && (
<text
cursor="pointer"
data-testid="la-vz-translation"
dominantBaseline="middle"
id={aaId}
style={{
Expand All @@ -218,7 +227,7 @@ class TranslationRow extends React.Component<TranslationRowProps> {
fontWeight: 400,
}}
textAnchor="middle"
x={charWidth * 1.5}
x={bpCount * 0.5 * charWidth}
y={`${h / 2 + 1}`}
>
{a}
Expand Down
8 changes: 6 additions & 2 deletions src/SeqViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { withResizeDetector } from "react-resize-detector";

import Circular from "./Circular/Circular";
import Linear from "./Linear/Linear";
import { Annotation, CutSite, Highlight, NameRange, Range } from "./elements";
import { Annotation, CutSite, Highlight, NameRange, Range, SeqType } from "./elements";
import CentralIndexContext from "./handlers/centralIndex";
import { Selection, SelectionContext } from "./handlers/selection";
import isEqual from "./isEqual";
Expand All @@ -19,6 +19,7 @@ interface SeqViewerProps {
name: string;
search: NameRange[];
seq: string;
seqType: SeqType;
setSelection: (update: Selection) => void;
showComplement: boolean;
showIndex: boolean;
Expand Down Expand Up @@ -48,7 +49,7 @@ class SeqViewer extends React.Component<SeqViewerProps> {
* on the screen at a given time and what should their size be
*/
linearProps = () => {
const { seq } = this.props;
const { seq, seqType } = this.props;
const size = this.props.testSize || { height: this.props.height, width: this.props.width };
const zoom = this.props.zoom.linear;

Expand All @@ -57,6 +58,9 @@ class SeqViewer extends React.Component<SeqViewerProps> {
// otherwise the sequence needs to be cut into smaller subsequences
// a sliding scale in width related to the degree of zoom currently active
let bpsPerBlock = Math.round((size.width / seqFontSize) * 1.4) || 1; // width / 1 * seqFontSize
if (seqType === "aa") {
bpsPerBlock = Math.round(bpsPerBlock / 3); // more space for each amino acid
}

if (zoom <= 5) {
bpsPerBlock *= 3;
Expand Down
3 changes: 2 additions & 1 deletion src/SeqViewerContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react";

import SeqViewer from "./SeqViewer";
import { Annotation, CutSite, Highlight, NameRange, Range } from "./elements";
import { Annotation, CutSite, Highlight, NameRange, Range, SeqType } from "./elements";
import CentralIndexContext from "./handlers/centralIndex";
import { Selection, SelectionContext, defaultSelection } from "./handlers/selection";
import isEqual from "./isEqual";
Expand All @@ -21,6 +21,7 @@ interface SeqViewerContainerProps {
start: number;
};
seq: string;
seqType: SeqType;
showComplement: boolean;
showIndex: boolean;
translations: Range[];
Expand Down
Loading

0 comments on commit 33394f4

Please sign in to comment.