Skip to content

Commit

Permalink
nits: fix highlight top primer and height calc
Browse files Browse the repository at this point in the history
  • Loading branch information
jjti committed Nov 22, 2023
1 parent fbd24c1 commit 5212673
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 123 deletions.
39 changes: 31 additions & 8 deletions demo/lib/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import seqparse from "seqparse";
import Circular from "../../src/Circular/Circular";
import Linear from "../../src/Linear/Linear";
import SeqViz from "../../src/SeqViz";
import { chooseRandomColor } from "../../src/colors";
import { AnnotationProp, Primer } from "../../src/elements";
import Header from "./Header";
import file from "./file";
import { Direction } from "../../src/Linear/Primers";
import { COLORS, chooseRandomColor } from "../../src/colors";

const viewerTypeOptions = [
{ key: "both", text: "Both", value: "both" },
Expand All @@ -35,7 +34,7 @@ interface AppState {
customChildren: boolean;
enzymes: any[];
name: string;
primers: Primer[]
primers: Primer[];
search: { query: string };
searchResults: any;
selection: any;
Expand All @@ -58,20 +57,44 @@ export default class App extends React.Component<any, AppState> {
primers: [
{
color: chooseRandomColor(),
direction: Direction.FOWARD,
direction: 1,
end: 653,
id: "527923581",
name: "pLtetO-1 fw primer",
start: 633,
},
},
{
color: chooseRandomColor(),
direction: Direction.REVERSE,
direction: -1,
end: 706,
id: "527923582",
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: {},
Expand Down
40 changes: 17 additions & 23 deletions src/Linear/Linear.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { isEqual } from "../isEqual";
import { createTranslations } from "../sequence";
import { InfiniteScroll } from "./InfiniteScroll";
import { SeqBlock } from "./SeqBlock";
import { Direction } from "./Primers";

export interface LinearProps {
annotations: Annotation[];
Expand Down Expand Up @@ -37,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 Down Expand Up @@ -100,32 +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[] | Primer[]) => {
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 primerFwRows = createMultiRows(
stackElements(vetAnnotations(
primers.filter((p) => p.direction == Direction.FOWARD)),
seq.length
),
const primerFwdRows = createMultiRows(
stackElements(vetAnnotations(primers.filter(p => p.direction === 1)), seq.length),
bpsPerBlock,
arrSize
)
const primerRvRows = createMultiRows(
stackElements(vetAnnotations(
primers.filter((p) => p.direction == Direction.REVERSE)),
seq.length
),
);
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 @@ -161,11 +155,11 @@ export default class Linear extends React.Component<LinearProps> {
if (zoomed) {
blockHeight += showComplement ? lineHeight : 0; // double for complement + 2px margin
}
if (primerFwRows[i].length) {
blockHeight += lineHeight;
if (primerFwdRows[i].length) {
blockHeight += primerFwdRows[i].length * lineHeight;
}
if (primerRvRows[i].length) {
blockHeight += lineHeight;
if (primerRevRows[i].length) {
blockHeight += primerRevRows[i].length * lineHeight;
}
if (showIndex) {
blockHeight += lineHeight; // another for index row
Expand All @@ -190,8 +184,6 @@ export default class Linear extends React.Component<LinearProps> {
seqBlocks.push(
<SeqBlock
key={ids[i]}
primerFwRows={primerFwRows[i]}
primerRvRows={primerRvRows[i]}
annotationRows={annotationRows[i]}
blockHeight={blockHeights[i]}
bpColors={this.props.bpColors}
Expand All @@ -207,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
75 changes: 36 additions & 39 deletions src/Linear/Primers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import * as React from "react";

import { InputRefFunc } from "../SelectionHandler";
Expand All @@ -7,32 +6,28 @@ import { NameRange } from "../elements";
import { annotation, annotationLabel } from "../style";
import { FindXAndWidthElementType } from "./SeqBlock";

const hoverOtherAnnotationRows = (className: string, opacity: number) => {
const hoverOtherPrimerRows = (className: string, opacity: number) => {
if (!document) return;
const elements = document.getElementsByClassName(className) as HTMLCollectionOf<HTMLElement>;
for (let i = 0; i < elements.length; i += 1) {
elements[i].style.fillOpacity = `${opacity}`;
}
};

export enum Direction {
FOWARD = 1,
REVERSE = -1
}
/**
* Render each row of annotations into its own row.
* This is not a default export for sake of the React component displayName.
*/
const PrimeRows = (props: {
primerRows: NameRange[][];
direction: Direction
bpsPerBlock: number;
direction: 1 | -1;
elementHeight: number;
findXAndWidth: FindXAndWidthElementType;
firstBase: number;
fullSeq: string;
inputRef: InputRefFunc;
lastBase: number;
primerRows: NameRange[][];
seqBlockRef: unknown;
width: number;
yDiff: number;
Expand All @@ -41,15 +36,15 @@ const PrimeRows = (props: {
{props.primerRows.map((primers: NameRange[], i: number) => (
<PrimerRow
key={`annotation-linear-row-${primers[0].id}-${props.firstBase}-${props.lastBase}`}
primers={primers}
direction={props.direction}
bpsPerBlock={props.bpsPerBlock}
direction={props.direction}
findXAndWidth={props.findXAndWidth}
firstBase={props.firstBase}
fullSeq={props.fullSeq}
height={props.elementHeight}
inputRef={props.inputRef}
lastBase={props.lastBase}
primers={primers}
seqBlockRef={props.seqBlockRef}
width={props.width}
y={props.yDiff + props.elementHeight * i}
Expand All @@ -65,15 +60,15 @@ export default PrimeRows;
* vertically stacked on top of one another in non-overlapping arrays.
*/
const PrimerRow = (props: {
primers: NameRange[];
direction: Direction;
bpsPerBlock: number;
direction: 1 | -1;
findXAndWidth: FindXAndWidthElementType;
firstBase: number;
fullSeq: string;
height: number;
inputRef: InputRefFunc;
lastBase: number;
primers: NameRange[];
seqBlockRef: unknown;
width: number;
y: number;
Expand All @@ -85,19 +80,20 @@ const PrimerRow = (props: {
transform={`translate(0, ${props.y})`}
width={props.width}
>
{props.primers.filter((a) => a.direction == props.direction).map((a, i) => (
<SingleNamedElement
{...props} // include overflowLeft in the key to avoid two split annotations in the same row from sharing a key
key={`annotation-linear-${a.id}-${i}-${props.firstBase}-${props.lastBase}`}
element={a}
elements={props.primers}
index={i}
/>
)
)}
{props.primers
.filter(a => a.direction == props.direction)
.map((a, i) => (
<SingleNamedElement
{...props} // include overflowLeft in the key to avoid two split annotations in the same row from sharing a key
key={`annotation-linear-${a.id}-${i}-${props.firstBase}-${props.lastBase}`}
element={a}
elements={props.primers}
index={i}
/>
))}
</g>
);
}
};
/**
* SingleNamedElement is a single rectangular element in the SeqBlock.
* It does a bunch of stuff to avoid edge-cases from wrapping around the 0-index, edge of blocks, etc.
Expand All @@ -115,8 +111,8 @@ const SingleNamedElement = (props: {
const { element, elements, findXAndWidth, firstBase, index, inputRef, lastBase } = props;

const { color, direction, end, name, start } = element;
const forward = direction === Direction.FOWARD;
const reverse = direction === Direction.REVERSE;
const forward = direction === 1;
const reverse = direction === -1;
const { overflowLeft, overflowRight, width, x: origX } = findXAndWidth(index, element, elements);
const crossZero = start > end && end < firstBase;

Expand All @@ -125,22 +121,23 @@ const SingleNamedElement = (props: {
const endREV = reverse && start >= firstBase && start <= lastBase;

// create padding on either side, vertically, of an element
const height = props.height * 0.8;
const height = props.height * 0.7;

const cW = 4; // jagged cutoff width
const cH = height / 4; // jagged cutoff height
const aH = 3; // arrow height at edges of primers
const [x, w] = [origX, width];

// create the SVG path, starting at the topLeft and working clockwise
// there is additional logic here for if the element overflows
// to the left or right of this seqBlock, where a "jagged edge" is created
const topLeft = "M 0 0";
let topRight = endFWD ?
`
const topRight = endFWD
? `
L ${width - Math.min(8 * cW, w)} 0
L ${width - Math.min(8 * cW, w)} ${-1 * height}
` :
`L ${width} 0`;
L ${width - Math.min(8 * cW, w)} ${-aH}
`
: `L ${width} 0`;

let linePath = "";

Expand All @@ -167,7 +164,7 @@ const SingleNamedElement = (props: {
} else if (endREV) {
bottomLeft = `
L ${Math.min(8 * cW, w)} ${height}
L ${Math.min(8 * cW, w)} ${height * 2}`; // arrow reverse
L ${Math.min(8 * cW, w)} ${height + aH}`; // arrow reverse
}

linePath = `${topLeft} ${topRight} ${bottomRight} ${bottomLeft}`;
Expand Down Expand Up @@ -223,10 +220,10 @@ const SingleNamedElement = (props: {
name: element.name,
ref: element.id,
start: start,
type: "ANNOTATION",
type: "PRIMER",
viewer: "LINEAR",
})}
className={`${element.id} la-vz-annotation`}
className={`${element.id} la-vz-primer`}
cursor="pointer"
d={linePath}
fill={color}
Expand All @@ -239,11 +236,11 @@ const SingleNamedElement = (props: {
onFocus={() => {
// do nothing
}}
onMouseOut={() => hoverOtherAnnotationRows(element.id, 0.7)}
onMouseOver={() => hoverOtherAnnotationRows(element.id, 1.0)}
onMouseOut={() => hoverOtherPrimerRows(element.id, 0.7)}
onMouseOver={() => hoverOtherPrimerRows(element.id, 1.0)}
/>
<text
className="la-vz-annotation-label"
className="la-vz-primer-label"
cursor="pointer"
dominantBaseline="middle"
fontSize={fontSize}
Expand All @@ -258,8 +255,8 @@ const SingleNamedElement = (props: {
onFocus={() => {
// do nothing
}}
onMouseOut={() => hoverOtherAnnotationRows(element.id, 0.7)}
onMouseOver={() => hoverOtherAnnotationRows(element.id, 1.0)}
onMouseOut={() => hoverOtherPrimerRows(element.id, 0.7)}
onMouseOver={() => hoverOtherPrimerRows(element.id, 1.0)}
>
{displayName}
</text>
Expand Down
Loading

0 comments on commit 5212673

Please sign in to comment.