Skip to content

Commit

Permalink
Introduce a Primer element (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacguerreir authored Nov 22, 2023
1 parent 13fcc68 commit eaaef94
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 18 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ annotations = [

In the example above, the "Strong promoter" would span the first to twenty-second base pair.

#### `primers (=[])`

An array of `Primer`s to render. Each `Primer` requires 0-based start (inclusive) and end (exclusive) indexes. `name`s are rendered on top of the primers. Set the primer's direction to `1` for forward primer and `-1` for reverse primer.

```js
primers = [
{ start: 33, end: 53, name: "LacZ Foward Primer", direction: 1 },
{ start: 3098, end: 3128, name: "LacZ Reverse Primer", direction: -1, color: "#FAA887" },
];
```

In the example above, the forward and reverse primers of LacZ are define by the direction parameter. Notice that color could be used optionally.

#### `translations (=[])`

An array of `translations`: sequence ranges to translate and render as amino acids sequences. Requires 0-based `start` (inclusive) and `end` (exclusive) indexes relative the DNA sequence. A direction is required: `1` (FWD) or `-1` (REV).
Expand Down
48 changes: 47 additions & 1 deletion demo/lib/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import seqparse from "seqparse";
import Circular from "../../src/Circular/Circular";
import Linear from "../../src/Linear/Linear";
import SeqViz from "../../src/SeqViz";
import { AnnotationProp } from "../../src/elements";
import { chooseRandomColor } from "../../src/colors";
import { AnnotationProp, Primer } from "../../src/elements";
import Header from "./Header";
import file from "./file";

Expand All @@ -33,6 +34,7 @@ interface AppState {
customChildren: boolean;
enzymes: any[];
name: string;
primers: Primer[];
search: { query: string };
searchResults: any;
selection: any;
Expand All @@ -52,6 +54,48 @@ export default class App extends React.Component<any, AppState> {
customChildren: true,
enzymes: ["PstI", "EcoRI", "XbaI", "SpeI"],
name: "",
primers: [
{
color: chooseRandomColor(),
direction: 1,
end: 653,
id: "527923581",
name: "pLtetO-1 fw primer",
start: 633,
},
{
color: chooseRandomColor(),
direction: -1,
end: 706,
id: "5279asdf582",
name: "pLtetO-1 rev primer",
start: 686,
},
{
color: chooseRandomColor(),
direction: 1,
end: 535,
id: "5279fd582",
name: "pLtetO-1 fwd primer",
start: 512,
},
{
color: chooseRandomColor(),
direction: -1,
end: 535,
id: "527923dfd582",
name: "pLtetO-1 rev primer",
start: 512,
},
{
color: chooseRandomColor(),
direction: -1,
end: 535,
id: "52792saf3582",
name: "pLtetO-1 rev primer",
start: 512,
},
],
search: { query: "ttnnnaat" },
searchResults: {},
selection: {},
Expand All @@ -73,6 +117,7 @@ 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 });
};

Expand Down Expand Up @@ -215,6 +260,7 @@ export default class App extends React.Component<any, AppState> {
// accession="MN623123"
key={`${this.state.viewer}${this.state.customChildren}`}
annotations={this.state.annotations}
primers={this.state.primers}
enzymes={this.state.enzymes}
highlights={[{ start: 0, end: 10 }]}
name={this.state.name}
Expand Down
32 changes: 27 additions & 5 deletions src/Linear/Linear.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react";

import { InputRefFunc } from "../SelectionHandler";
import { Annotation, CutSite, Highlight, NameRange, Range, SeqType, Size } from "../elements";
import { Annotation, CutSite, Highlight, NameRange, Primer, Range, SeqType, Size } from "../elements";
import { createMultiRows, createSingleRows, stackElements } from "../elementsToRows";
import { isEqual } from "../isEqual";
import { createTranslations } from "../sequence";
Expand All @@ -21,6 +21,7 @@ export interface LinearProps {
inputRef: InputRefFunc;
lineHeight: number;
onUnmount: (id: string) => void;
primers: Primer[];
search: NameRange[];
seq: string;
seqFontSize: number;
Expand All @@ -35,13 +36,14 @@ export interface LinearProps {
/**
* A linear sequence viewer.
*
* Comprised of SeqBlock(s), which are themselves comprised of:
* Comprised of SeqBlock(s) which are comprised of:
* text (seq)
* Index (axis)
* Annotations
* Finds
* Translations
* Selections
* Primers
*/
export default class Linear extends React.Component<LinearProps> {
/**
Expand All @@ -68,6 +70,7 @@ export default class Linear extends React.Component<LinearProps> {
highlights,
lineHeight,
onUnmount,
primers,
search,
seq,
seqType,
Expand Down Expand Up @@ -97,15 +100,26 @@ export default class Linear extends React.Component<LinearProps> {
: new Array(arrSize).fill([]);

/**
* Vet the annotations for starts and ends at zero index
* Mutate elements that start or end at zero index
*/
const vetAnnotations = (annotations: Annotation[]) => {
function vetAnnotations<T extends NameRange>(annotations: T[]): T[] {
annotations.forEach(ann => {
if (ann.end === 0 && ann.start > ann.end) ann.end = seqLength;
if (ann.start === seqLength && ann.end < ann.start) ann.start = 0;
});
return annotations;
};
}

const primerFwdRows = createMultiRows(
stackElements(vetAnnotations(primers.filter(p => p.direction === 1)), seq.length),
bpsPerBlock,
arrSize
);
const primerRevRows = createMultiRows(
stackElements(vetAnnotations(primers.filter(p => p.direction === -1)), seq.length),
bpsPerBlock,
arrSize
);

const annotationRows = createMultiRows(
stackElements(vetAnnotations(annotations), seq.length),
Expand Down Expand Up @@ -141,6 +155,12 @@ export default class Linear extends React.Component<LinearProps> {
if (zoomed) {
blockHeight += showComplement ? lineHeight : 0; // double for complement + 2px margin
}
if (primerFwdRows[i].length) {
blockHeight += primerFwdRows[i].length * lineHeight;
}
if (primerRevRows[i].length) {
blockHeight += primerRevRows[i].length * lineHeight;
}
if (showIndex) {
blockHeight += lineHeight; // another for index row
}
Expand Down Expand Up @@ -179,6 +199,8 @@ export default class Linear extends React.Component<LinearProps> {
id={ids[i]}
inputRef={this.props.inputRef}
lineHeight={lineHeight}
primerFwdRows={primerFwdRows[i]}
primerRevRows={primerRevRows[i]}
searchRows={searchRows[i]}
seq={seqs[i]}
seqFontSize={this.props.seqFontSize}
Expand Down
Loading

0 comments on commit eaaef94

Please sign in to comment.