Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scripts/browser-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ async function main() {
await page.locator('#exampleDropdownTrigger').click();
await page.locator(`[data-example="${id}"]`).click();
await ready(page);
assert.equal(await page.locator('#sequence').inputValue(), example.vaRRIParams.sequence);
assert.equal(await page.locator('#structure').inputValue(), example.vaRRIParams.structure);
assert.equal(await page.locator('#sequence').inputValue(), example.vaRRIParams.fields.sequence);
assert.equal(await page.locator('#structure').inputValue(), example.vaRRIParams.fields.structure);
assert.ok(await page.locator('#rendering-canvas circle[node_type="nucleotide"]').count() > 0);
if (id === 'crossing-rri') await checkCrossingRriExample(page, output);
}
Expand Down
19 changes: 16 additions & 3 deletions scripts/visual-baseline.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ function serve() {
});
}

async function exampleParameters(example) {
if (example.vaRRIParams.format !== 'vaRRI') {
return new URLSearchParams({ ...example.vaRRIParams,
forceLayout: '0', forceLayoutLinearRRI: '0', forceLayoutLinearStructure: '0',
freeTrailingEnds: '0', pullPseudoknotBasepairs: '0', textAnnotations: '[]' });
}
const codec = await import(pathToFileURL(path.join(root, 'src/core/model/index.js')).href);
const decoded = codec.decodeJsonState(example.vaRRIParams);
return codec.encodeUrlState({ ...decoded,
fields: { ...decoded.fields, forceLayout: false, forceLayoutLinearRRI: false,
forceLayoutLinearStructure: false, forceLayoutFreeTails: false, forceLayoutPullCrossing: false },
annotations: { ...decoded.annotations, textAnnotations: [] },
});
}

async function pixels(file) {
const image = await loadImage(file);
const canvas = createCanvas(image.width, image.height);
Expand Down Expand Up @@ -125,9 +140,7 @@ async function main() {
let seed = 83;
Math.random = () => ((seed = (seed * 1664525 + 1013904223) >>> 0) / 4294967296);
});
const parameters = new URLSearchParams({ ...examples[name].vaRRIParams,
forceLayout: '0', forceLayoutLinearRRI: '0', forceLayoutLinearStructure: '0',
freeTrailingEnds: '0', pullPseudoknotBasepairs: '0', textAnnotations: '[]' });
const parameters = await exampleParameters(examples[name]);
await page.goto(`${origin}/index.html?${parameters}`, { waitUntil: 'networkidle' });
await page.waitForFunction(() => document.querySelector('#rendering-canvas circle[node_num]') &&
document.getElementById('rendering-canvas')?.style.visibility !== 'hidden');
Expand Down
132 changes: 57 additions & 75 deletions src/ui/examples-data.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
const featureOverviewProfile = [
'# unpaired probabilities',
'1 0.9',
'2 0.7',
'3 0.3',
'4 0.1',
'7 0.3',
'8 0.7',
'9 0.6',
].join('\n');
import featureOverview from './examples/feature-overview.js';

const coronelTellezProfile1 = [
'61 0.6',
Expand Down Expand Up @@ -106,32 +97,15 @@ const coronelTellezProfile2 = [
'107 0.6',
].join('\n');

// vaRRIParams uses the Store/Load JSON session shape so a stored visualization
// can replace an example payload without conversion.
const examples = {
'2mol': {
name: 'RNA–RNA interaction feature overview',
nameShort: 'All feature showcase',
description: 'Demonstrates cropping, probability profiles, region and subsequence highlights, and point mutations in one interaction.',
descriptionShort: 'Showcases all features of vaRRI.',
vaRRIParams: {
sequence: 'ACGAUCAUGUGGUUUAGAGCAUUUUCGACAGCAG&ACGAAAAAAAGAGCAGACAGUAG',
structure: '..<<..<<...>>>>...((....(((...((..&............)))))..))..',
startIndex1: -6,
startIndex2: 100,
coloring: 'strand',
highlighting: 'region',
backgroundhighlighting: 'basepairs',
forceLayoutLinearRRI: 1,
distinctBpTypes: 1,
cropping: 2,
forceLayout: 0,
regionHighlights: '21-25&113-114:0d00ff:0.2',
subseqHighlights: '1:15-18:338a29:1,2:117-118:1e1ee4:0.7',
mutations: '1:20G:338a29,2:115C:1e1ee4',
profileData1: featureOverviewProfile,
profileIdxRef1: 1,
profileColorRepresentsOne1: 1,
profileColorRepresentsOne2: 0,
},
vaRRIParams: featureOverview,
},
'coronel-tellez-2022': {
name: 'sRNA-controlled iron sparing response in Staphylococci',
Expand All @@ -141,23 +115,22 @@ const examples = {
description: 'Reproduction of *Figure 6A-bottom* [(Coronel-Tellez, et al., 2022)](http://dx.doi.org/10.1093/nar/gkac648) showing an interaction with position-specific SHAPE probing data annotation and subsequence highlighting. The long input sequences are cropped to the region of interaction +3 nucleotides.',
descriptionShort: 'Position-specific SHAPE probing data annotation and cropping of long sequences to RRI region.',
vaRRIParams: {
sequence: 'AAUUCUAUCUGAAAGAUGUGUGGGGCAUCGUUAUUUUAGGUGGAUAUGAGCAAUUUAUUAAAAGUCAUUUACGGAAAAUAUAUAUAGACGGGGUGAGUAAUAUGCAAGAACAUUUGGUGGUUACACUUGAUAGCAAAGGAGAAGAACUU&UUGAAAAUGAUUAUCAAUACCACAUAGAACAUCCCCCCCACAACGUUUCGUUCUUGUUGGAUUGGUCAUUUUCAAAUAUUCCCCUUUUAUAUGCCCGUAAAAGACAAUAUACGUUAUAACAACGUUUUAUAAAAGCAGUAAACCCUUACGACACUUUAGGUUUACUGCUUUUGU',
structure: '...............................................................(((.(((((((......((((((((.((((.((((((.(((.............................................&..................................................................))))......))))))))))))))))).))))))).))).....................................................................',
colorSeq1: 'c9c1c9',
colorSeq2: 'c9c1c9',
startIndex1: 1,
startIndex2: 1,
cropping: 3,
forceLayout: 'off',
backgroundhighlighting: 'nothing',
highlighting: 'basepairs',
subseqHighlights: '1:88-93:0f50b8:1,1:102-104:0e7a06:1,2:81-84:e012dd:1,2:94-96:e012dd:1',
profileData1: coronelTellezProfile1,
profileColorRepresentsOne1: 1,
profileColor1: 'ea373c',
profileData2: coronelTellezProfile2,
profileColorRepresentsOne2: 1,
profileColor2: 'ea373c',
format: 'vaRRI', version: 1,
fields: {
sequence: 'AAUUCUAUCUGAAAGAUGUGUGGGGCAUCGUUAUUUUAGGUGGAUAUGAGCAAUUUAUUAAAAGUCAUUUACGGAAAAUAUAUAUAGACGGGGUGAGUAAUAUGCAAGAACAUUUGGUGGUUACACUUGAUAGCAAAGGAGAAGAACUU&UUGAAAAUGAUUAUCAAUACCACAUAGAACAUCCCCCCCACAACGUUUCGUUCUUGUUGGAUUGGUCAUUUUCAAAUAUUCCCCUUUUAUAUGCCCGUAAAAGACAAUAUACGUUAUAACAACGUUUUAUAAAAGCAGUAAACCCUUACGACACUUUAGGUUUACUGCUUUUGU',
structure: '...............................................................(((.(((((((......((((((((.((((.((((((.(((.............................................&..................................................................))))......))))))))))))))))).))))))).))).....................................................................',
colorSeq1: '#c9c1c9', colorSeq2: '#c9c1c9', startIndex1: '1', startIndex2: '1', cropping: '3',
forceLayout: false, backgroundhighlighting: 'nothing', highlighting: 'basepairs',
profileData1: coronelTellezProfile1, profileColorRepresentsOne1: true, profileColor1: '#ea373c',
profileData2: coronelTellezProfile2, profileColorRepresentsOne2: true, profileColor2: '#ea373c',
},
annotations: { subsequenceHighlights: [
{ sequence: '1', range: [[88, 93]], color: '#0f50b8', alpha: 1 },
{ sequence: '1', range: [[102, 104]], color: '#0e7a06', alpha: 1 },
{ sequence: '2', range: [[81, 84]], color: '#e012dd', alpha: 1 },
{ sequence: '2', range: [[94, 96]], color: '#e012dd', alpha: 1 },
] },
rotation: 0, showRenderingOnly: false,
},
},
'wu-2024': {
Expand All @@ -168,13 +141,22 @@ const examples = {
description: 'Reproduction of Figure 4C [(Wu et al., 2024)](https://doi.org/10.1101/2024.06.23.600155) showing an interaction with 4 mutations and a subsequence highlighting within a sequence context upstream of the start codon.',
descriptionShort: 'Subsequence highlighting and mutations within a sequence context.',
vaRRIParams: {
sequence: 'AACUCGCGAAAGCCAUAAAAACCAGGGAGACA&UUCCCUGGUGUUGGCGCAGUAUUCGCGCA',
structure: '....((((((.((((.....((((((((....&.))))))))..))))......))))))..',
startIndex1: -35,
startIndex2: 2,
subseqHighlights: '1:-12--6:0dec3f:0.9',
mutations: '1:-14G:fb0bcb,1:-13G:fb0bcb,2:8C:fb0bcb,2:9C:fb0bcb',
forceLayout: 'off',
format: 'vaRRI', version: 1,
fields: {
sequence: 'AACUCGCGAAAGCCAUAAAAACCAGGGAGACA&UUCCCUGGUGUUGGCGCAGUAUUCGCGCA',
structure: '....((((((.((((.....((((((((....&.))))))))..))))......))))))..',
startIndex1: '-35', startIndex2: '2', forceLayout: false,
},
annotations: {
subsequenceHighlights: [{ sequence: '1', range: [[-12, -6]], color: '#0dec3f', alpha: 0.9 }],
pointMutations: [
{ sequence: '1', position: -14, replacement: 'G', color: '#fb0bcb' },
{ sequence: '1', position: -13, replacement: 'G', color: '#fb0bcb' },
{ sequence: '2', position: 8, replacement: 'C', color: '#fb0bcb' },
{ sequence: '2', position: 9, replacement: 'C', color: '#fb0bcb' },
],
},
rotation: 0, showRenderingOnly: false,
},
},
'IntaRNA-seeds': {
Expand All @@ -183,20 +165,19 @@ const examples = {
description: 'Highlighting of the two potential seed interactions considered by IntaRNA to predict the shown interaction between the two sequences. The highlighting is done using a semi-transparent purple color, which allows to show overlapping regions.',
descriptionShort: 'Opaque highlighting of multiple potential IntaRNA seed regions.',
vaRRIParams: {
sequence:'CUUAGCCGUAAUUGGAUUAGCUGAUGAACAAACUUCUCGUGAGUCUGCUGUUGACCCUGGGUCUGAC&GAGAGACCCACGCAGUCGGACUCUUCAGAUUAUCUCCUCAUCAGGCUAAUCACGGUUUUU',
colorSeq1:'#add8e6',
startIndex1:-23,
colorSeq2:'#f4bb44',
startIndex2:64,
structure:'...((((((.....(((((((((((((.......(((...((((((((((.......(((((((...&...)))))))..))).)))))))...)))........)))))).)))))))))))))...',
colorBasepair:'#ff0000',
coloring:'strand',
highlighting:'region',
colorRriNodes:'#ff0000',
forceLayoutLinearRRI: 1,
distinctBpTypes:1,
regionHighlights:'35-41&67-73:0D00FF:0.4,-9--3&108-114:0D00FF:0.4',
forceLayout: 'off',
format: 'vaRRI', version: 1,
fields: {
sequence: 'CUUAGCCGUAAUUGGAUUAGCUGAUGAACAAACUUCUCGUGAGUCUGCUGUUGACCCUGGGUCUGAC&GAGAGACCCACGCAGUCGGACUCUUCAGAUUAUCUCCUCAUCAGGCUAAUCACGGUUUUU',
structure: '...((((((.....(((((((((((((.......(((...((((((((((.......(((((((...&...)))))))..))).)))))))...)))........)))))).)))))))))))))...',
colorSeq1: '#add8e6', startIndex1: '-23', colorSeq2: '#f4bb44', startIndex2: '64',
colorBasepair: '#ff0000', coloring: 'strand', highlighting: 'region', colorRriNodes: '#ff0000',
forceLayoutLinearRRI: true, distinctBpTypes: true, forceLayout: false,
},
annotations: { regionHighlights: [
{ sequence1Range: [35, 41], sequence2Range: [67, 73], color: '#0D00FF', alpha: 0.4 },
{ sequence1Range: [-9, -3], sequence2Range: [108, 114], color: '#0D00FF', alpha: 0.4 },
] },
rotation: 0, showRenderingOnly: false,
},
},
'crossing-rri': {
Expand All @@ -205,13 +186,14 @@ const examples = {
descriptionShort: 'Crossing RNA–RNA interaction resolved via relaxed force-field layout.',
description: 'Crossing RNA–RNA interactions (w.r.t. the underlying joint-structure layout algorithm of fornac) are typically problematic since their base pairing forms a crossing pseudoknot structure. Some can be layouted when relaxing the force field (see Visualization Settings).',
vaRRIParams: {
sequence: 'NNNNNNNNNNNNNNNNN&NNNNNNNNNNNNN',
structure:'((..((...<<<..<<<&))))...>>>>>>',
distinctBpTypes: 0,
forceLayout: 1,
forceLayoutFreeTails: 1,
forceLayoutPullCrossing: 1,
},
format: 'vaRRI', version: 1,
fields: {
sequence: 'NNNNNNNNNNNNNNNNN&NNNNNNNNNNNNN',
structure: '((..((...<<<..<<<&))))...>>>>>>',
distinctBpTypes: false, forceLayout: true, forceLayoutFreeTails: true, forceLayoutPullCrossing: true,
},
annotations: {}, rotation: 0, showRenderingOnly: false,
},
},
};

Expand Down
113 changes: 113 additions & 0 deletions src/ui/examples/feature-overview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
export default {
format: 'vaRRI',
version: 1,
fields: {
sequence: 'ACGAUCAUGUGGUUUAGAGCAUUUUCGACAGCAG&ACGAAAAAAAGAGCAGACAGUAG',
structure: '..<<..<<...>>>>...((....(((...((..&............)))))..))..',
startIndex1: '-6',
startIndex2: '100',
seqName1: 'blue RNA',
seqName2: 'the target',
cropping: '2',
colorSeq1: '#add8e6',
colorSeq2: '#f4bb44',
coloring: 'strand',
highlighting: 'region',
backgroundhighlighting: 'basepairs',
distinctBpTypes: true,
profileData1: '# unpaired probabilities\n1 0.9\n2 0.7\n3 0.3\n4 0.1\n7 0.3\n8 0.7\n9 0.6',
profileIdxRef1: '1',
profileColor1: '#800080',
profileColorRepresentsOne1: true,
profileData2: '',
profileIdxRef2: '1',
profileColor2: '#ff0000',
profileColorRepresentsOne2: false,
forceLayout: false,
forceLayoutFreeTails: false,
forceLayoutPullCrossing: false,
forceLayoutLinearStructure: false,
forceLayoutLinearRRI: true,
hideFooterAndHeader: false,
colorRriNodes: '#ff0000',
colorRriRegion: '#ff0000',
colorBasepair: '#ff0000',
},
annotations: {
pointMutations: [
{ sequence: '1', position: 20, replacement: 'G', color: '#338a29' },
{ sequence: '2', position: 115, replacement: 'C', color: '#1E1EE4' },
],
subsequenceHighlights: [
{ sequence: '1', range: [[15, 18]], color: '#338A29', alpha: 1 },
{ sequence: '2', range: [[117, 118]], color: '#1E1EE4', alpha: 0.7 },
],
regionHighlights: [
{ sequence1Range: [21, 25], sequence2Range: [113, 114], color: '#0D00FF', alpha: 0.2 },
],
textAnnotations: [
{
text: 'blue RNA', sequenceNameFor: '1', bold: true, italic: false, size: 8, color: '#2891c8',
position: { x: -200.7008741529051, y: 13.469304295038569 }, anchor: null,
},
{
text: 'the target', sequenceNameFor: '2', bold: true, italic: false, size: 8, color: '#e8be17',
position: { x: -200.57799194603135, y: -33.758161226681665 }, anchor: null,
},
{
text: 'subsequence', sequenceNameFor: null, bold: true, italic: true, size: 8, color: '#56af3d',
position: { x: -21.39257544906482, y: 47.81899107367843 }, anchor: null,
},
{
text: 'annotation', sequenceNameFor: null, bold: true, italic: true, size: 8, color: '#56af3d',
position: { x: -21.963319946538917, y: 62.08760351052973 }, anchor: null,
},
{
text: 'region', sequenceNameFor: null, bold: false, italic: false, size: 8, color: '#8049ab',
position: { x: -94.44787112574372, y: 36.40410112419755 }, anchor: null,
},
{
text: 'annotation', sequenceNameFor: null, bold: false, italic: false, size: 8, color: '#8049ab',
position: { x: -92.73563763332153, y: 47.81899107367843 }, anchor: null,
},
{
text: 'point', sequenceNameFor: null, bold: true, italic: false, size: 8, color: '#0737de',
position: { x: -61.66608138819855, y: -52.52624900153063 }, anchor: null,
},
{
text: 'mutation', sequenceNameFor: null, bold: true, italic: false, size: 8, color: '#0737de',
position: { x: -60.36931650324601, y: -41.50374747943411 }, anchor: null,
},
{
text: 'indexing', sequenceNameFor: null, bold: false, italic: true, size: 8, color: '#ff0000',
position: { x: -126.09563916228302, y: -45.965174958955686 }, anchor: null,
},
{
text: 'probability', sequenceNameFor: null, bold: true, italic: false, size: 8, color: '#a8488b',
position: { x: 100.16407087669964, y: -55.85866037891114 }, anchor: null,
},
{
text: 'profile', sequenceNameFor: null, bold: true, italic: false, size: 8, color: '#a8488b',
position: { x: 104.37348693340908, y: -40.64601818636274 }, anchor: null,
},
{
text: 'sequence names', sequenceNameFor: null, bold: false, italic: false, size: 8, color: '#000000',
position: { x: -200.6407749205137, y: -53.51437846108769 }, anchor: null,
},
{
text: 'base-pair', sequenceNameFor: null, bold: false, italic: false, size: 8, color: '#ff0000',
position: { x: 90.7051408510386, y: 25.763421748858548 }, anchor: null,
},
{
text: 'types', sequenceNameFor: null, bold: false, italic: false, size: 8, color: '#ff0000',
position: { x: 90.20965459972643, y: 38.15057803166269 }, anchor: null,
},
{
text: 'linear RRI helices', sequenceNameFor: null, bold: false, italic: false, size: 8, color: '#ff8080',
position: { x: -168.9296548365352, y: -8.425129591680786 }, anchor: null,
},
],
},
rotation: 0,
showRenderingOnly: false,
};
10 changes: 5 additions & 5 deletions src/ui/navigation-actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { nextTick } from 'vue';
import { decodeUrlState, encodeUrlState } from '../core/model/url-state.js';
import { decodeJsonState } from '../core/model/json-state.js';
import { marked } from './vendor/marked.js';

export function createNavigationActions({ api, state, defaults, initialColors, actions, document, examples }) {
Expand Down Expand Up @@ -81,14 +82,13 @@ export function createNavigationActions({ api, state, defaults, initialColors, a
async function loadExample(key) {
const example = examples[key];
if (!example?.vaRRIParams) return false;
const decoded = decodeJsonState(example.vaRRIParams);
api.setColors(initialColors);
clearAll();
const selectionGeneration = generation;
const params = new URLSearchParams();
Object.entries(example.vaRRIParams).forEach(([key, value]) => {
if (key !== 'showRenderingOnly' && value !== undefined && value !== null) params.set(key, value);
});
loadAllUrlParameters(params, { revealProfilePanel: false });
const fields = { ...defaults, ...decoded.fields };
state.renderingOnly = state.showFullPage = decoded.showRenderingOnly;
applySessionState({ ...decoded, fields }, { revealProfilePanel: false });
actions.resetFastaForm(); actions.resetSubseqForm(); actions.resetRegionForm(); actions.resetMutationForm();
actions.resetTextAnnotationForm();
state.selectedExample = key;
Expand Down
Loading
Loading