Skip to content
Snippets Groups Projects
Commit 2e1df7b6 authored by Julian Matschinske's avatar Julian Matschinske Committed by Julian Späth
Browse files

Optimize selection and deselection of nodes

parent 1358f06f
No related branches found
No related tags found
No related merge requests found
...@@ -37,7 +37,7 @@ export const MAX_TASKS = 3; ...@@ -37,7 +37,7 @@ export const MAX_TASKS = 3;
export class AnalysisService { export class AnalysisService {
private selectedItems = new Map<string, Wrapper>(); private selectedItems = new Map<string, Wrapper>();
private selectSubject = new Subject<{ item: Wrapper, selected: boolean }>(); private selectListSubject = new Subject<{ items: Wrapper[], selected: boolean | null }>();
public tokens: string[] = []; public tokens: string[] = [];
public finishedTokens: string[] = []; public finishedTokens: string[] = [];
...@@ -82,40 +82,62 @@ export class AnalysisService { ...@@ -82,40 +82,62 @@ export class AnalysisService {
}); });
} }
public addItem(wrapper: Wrapper) { public addItems(wrappers: Wrapper[]) {
if (!this.inSelection(wrapper)) { const addedWrappers: Wrapper[] = [];
this.selectedItems.set(wrapper.nodeId, wrapper); for (const wrapper of wrappers) {
this.selectSubject.next({item: wrapper, selected: true}); if (!this.inSelection(wrapper)) {
addedWrappers.push(wrapper);
this.selectedItems.set(wrapper.nodeId, wrapper);
}
}
this.selectListSubject.next({items: addedWrappers, selected: true});
}
public removeItems(wrappers: Wrapper[]) {
const removedWrappers: Wrapper[] = [];
for (const wrapper of wrappers) {
if (this.selectedItems.delete(wrapper.nodeId)) {
removedWrappers.push(wrapper);
}
} }
this.selectListSubject.next({items: removedWrappers, selected: false});
} }
public addAllHostProteins(nodes, proteins) { public addAllHostProteins(nodes, proteins) {
const items: Wrapper[] = [];
const visibleIds = new Set<string>(nodes.getIds()); const visibleIds = new Set<string>(nodes.getIds());
for (const protein of proteins) { for (const protein of proteins) {
const wrapper = getWrapperFromProtein(protein); const wrapper = getWrapperFromProtein(protein);
const found = visibleIds.has(wrapper.nodeId); const found = visibleIds.has(wrapper.nodeId);
if (found && !this.inSelection(wrapper)) { if (found && !this.inSelection(wrapper)) {
this.addItem(wrapper); items.push(wrapper);
this.selectedItems.set(wrapper.nodeId, wrapper);
} }
} }
this.selectListSubject.next({items, selected: true});
} }
public addAllViralProteins(nodes, viralProteins) { public addAllViralProteins(nodes, viralProteins) {
const items: Wrapper[] = [];
const visibleIds = new Set<string>(nodes.getIds()); const visibleIds = new Set<string>(nodes.getIds());
for (const viralProtein of viralProteins) { for (const viralProtein of viralProteins) {
const wrapper = getWrapperFromViralProtein(viralProtein); const wrapper = getWrapperFromViralProtein(viralProtein);
const found = visibleIds.has(wrapper.nodeId); const found = visibleIds.has(wrapper.nodeId);
if (found && !this.inSelection(wrapper)) { if (found && !this.inSelection(wrapper)) {
this.addItem(wrapper); items.push(wrapper);
this.selectedItems.set(wrapper.nodeId, wrapper);
} }
} }
this.selectListSubject.next({items, selected: true});
} }
resetSelection() { resetSelection() {
const oldSelection = this.selectedItems.values(); this.selectListSubject.next({items: [], selected: null});
for (const item of oldSelection) { this.selectedItems.clear();
this.removeItem(item); }
}
idInSelection(nodeId: string): boolean {
return this.selectedItems.has(nodeId);
} }
inSelection(wrapper: Wrapper): boolean { inSelection(wrapper: Wrapper): boolean {
...@@ -130,13 +152,6 @@ export class AnalysisService { ...@@ -130,13 +152,6 @@ export class AnalysisService {
return this.inSelection(getWrapperFromViralProtein(viralProtein)); return this.inSelection(getWrapperFromViralProtein(viralProtein));
} }
removeItem(wrapper: Wrapper) {
const item = this.selectedItems.get(wrapper.nodeId);
if (this.selectedItems.delete(wrapper.nodeId)) {
this.selectSubject.next({item, selected: false});
}
}
getSelection(): Wrapper[] { getSelection(): Wrapper[] {
return Array.from(this.selectedItems.values()); return Array.from(this.selectedItems.values());
} }
...@@ -145,9 +160,9 @@ export class AnalysisService { ...@@ -145,9 +160,9 @@ export class AnalysisService {
return this.selectedItems.size; return this.selectedItems.size;
} }
subscribe(cb: (item: Wrapper, selected: boolean) => void) { subscribeList(cb: (items: Array<Wrapper>, selected: boolean | null) => void) {
this.selectSubject.subscribe((event) => { this.selectListSubject.subscribe((event) => {
cb(event.item, event.selected); cb(event.items, event.selected);
}); });
} }
......
...@@ -154,10 +154,10 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -154,10 +154,10 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
} }
const wrapper = node.wrapper; const wrapper = node.wrapper;
if (this.analysis.inSelection(wrapper)) { if (this.analysis.inSelection(wrapper)) {
this.analysis.removeItem(wrapper); this.analysis.removeItems([wrapper]);
this.analysis.getCount(); this.analysis.getCount();
} else { } else {
this.analysis.addItem(wrapper); this.analysis.addItems([wrapper]);
this.analysis.getCount(); this.analysis.getCount();
} }
} }
...@@ -174,40 +174,78 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -174,40 +174,78 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
} }
}); });
this.analysis.subscribe((item, selected) => { this.analysis.subscribeList((items, selected) => {
if (item.type === 'host') { if (selected !== null) {
// TODO: Refactor! const updatedNodes = [];
const found = this.tableSelectedProteins.findIndex((i) => getProteinNodeId(i) === item.nodeId); for (const item of items) {
const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.nodeId); const node = this.nodeData.nodes.get(item.nodeId);
if (selected && found === -1 && tableItem) { if (!node) {
this.tableSelectedProteins.push(tableItem); continue;
} }
if (!selected && found !== -1 && tableItem) { const pos = this.network.getPositions([item.nodeId]);
this.tableSelectedProteins.splice(found, 1); node.x = pos[item.nodeId].x;
node.y = pos[item.nodeId].y;
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, node.isSeed, selected));
updatedNodes.push(node);
} }
this.tableSelectedProteins = [...this.tableSelectedProteins]; this.nodeData.nodes.update(updatedNodes);
} else if (item.type === 'virus') {
// TODO: Refactor! const proteinSelection = this.tableSelectedProteins;
const found = this.tableSelectedViralProteins.findIndex((i) => getViralProteinNodeId(i) === item.nodeId); const viralProteinSelection = this.tableSelectedViralProteins;
const tableItem = this.tableViralProteins.find((i) => getViralProteinNodeId(i) === item.nodeId); for (const item of items) {
if (selected && found === -1 && tableItem) { if (item.type === 'host') {
this.tableSelectedViralProteins.push(tableItem); // TODO: Refactor!
const found = proteinSelection.findIndex((i) => getProteinNodeId(i) === item.nodeId);
const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
proteinSelection.push(tableItem);
}
if (!selected && found !== -1 && tableItem) {
proteinSelection.splice(found, 1);
}
} else if (item.type === 'virus') {
// TODO: Refactor!
const found = viralProteinSelection.findIndex((i) => getViralProteinNodeId(i) === item.nodeId);
const tableItem = this.tableViralProteins.find((i) => getViralProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
viralProteinSelection.push(tableItem);
}
if (!selected && found !== -1 && tableItem) {
viralProteinSelection.splice(found, 1);
}
}
} }
if (!selected && found !== -1 && tableItem) { this.tableSelectedProteins = [...proteinSelection];
this.tableSelectedViralProteins.splice(found, 1); this.tableSelectedViralProteins = [...viralProteinSelection];
} else {
const updatedNodes = [];
this.nodeData.nodes.forEach((node) => {
const nodeSelected = this.analysis.idInSelection(node.id);
if (selected !== nodeSelected) {
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, true, selected));
updatedNodes.push(node);
}
});
this.nodeData.nodes.update(updatedNodes);
const proteinSelection = [];
const viralProteinSelection = [];
for (const item of items) {
if (item.type === 'host') {
const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.nodeId);
if (tableItem) {
proteinSelection.push(tableItem);
}
} else if (item.type === 'virus') {
const tableItem = this.tableViralProteins.find((i) => getViralProteinNodeId(i) === item.nodeId);
if (tableItem) {
viralProteinSelection.push(tableItem);
}
}
} }
this.tableSelectedViralProteins = [...this.tableSelectedViralProteins]; this.tableSelectedProteins = [...proteinSelection];
} this.tableSelectedViralProteins = [...viralProteinSelection];
const node = this.nodeData.nodes.get(item.nodeId);
if (!node) {
return;
} }
const pos = this.network.getPositions([item.nodeId]);
node.x = pos[item.nodeId].x;
node.y = pos[item.nodeId].y;
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, node.isSeed, selected));
this.nodeData.nodes.update(node);
}); });
} }
} }
...@@ -390,20 +428,20 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -390,20 +428,20 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
if (this.showDrugs) { if (this.showDrugs) {
const result = await this.http.get<any>( const result = await this.http.get<any>(
`${environment.backend}drug_interactions/?token=${this.token}`).toPromise().catch( `${environment.backend}drug_interactions/?token=${this.token}`).toPromise().catch(
(err: HttpErrorResponse) => { (err: HttpErrorResponse) => {
// simple logging, but you can do a lot more, see below // simple logging, but you can do a lot more, see below
toast({ toast({
message: 'An error occured while fetching the drugs.', message: 'An error occured while fetching the drugs.',
duration: 5000, duration: 5000,
dismissible: true, dismissible: true,
pauseOnHover: true, pauseOnHover: true,
type: 'is-danger', type: 'is-danger',
position: 'top-center', position: 'top-center',
animate: {in: 'fadeIn', out: 'fadeOut'} animate: {in: 'fadeIn', out: 'fadeOut'}
});
this.showDrugs = false;
return;
}); });
this.showDrugs = false;
return;
});
const drugs = result.drugs; const drugs = result.drugs;
const edges = result.edges; const edges = result.edges;
...@@ -458,35 +496,43 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { ...@@ -458,35 +496,43 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
public tableProteinSelection(e) { public tableProteinSelection(e) {
const oldSelection = [...this.tableSelectedProteins]; const oldSelection = [...this.tableSelectedProteins];
this.tableSelectedProteins = e; this.tableSelectedProteins = e;
const addItems = [];
const removeItems = [];
for (const i of this.tableSelectedProteins) { for (const i of this.tableSelectedProteins) {
const wrapper = getWrapperFromProtein(i); const wrapper = getWrapperFromProtein(i);
if (oldSelection.indexOf(i) === -1) { if (oldSelection.indexOf(i) === -1) {
this.analysis.addItem(wrapper); addItems.push(wrapper);
} }
} }
for (const i of oldSelection) { for (const i of oldSelection) {
const wrapper = getWrapperFromProtein(i); const wrapper = getWrapperFromProtein(i);
if (this.tableSelectedProteins.indexOf(i) === -1) { if (this.tableSelectedProteins.indexOf(i) === -1) {
this.analysis.removeItem(wrapper); removeItems.push(wrapper);
} }
} }
this.analysis.addItems(addItems);
this.analysis.removeItems(removeItems);
} }
public tableViralProteinSelection(e) { public tableViralProteinSelection(e) {
const oldSelection = [...this.tableSelectedViralProteins]; const oldSelection = [...this.tableSelectedViralProteins];
this.tableSelectedViralProteins = e; this.tableSelectedViralProteins = e;
const addItems = [];
const removeItems = [];
for (const i of this.tableSelectedViralProteins) { for (const i of this.tableSelectedViralProteins) {
const wrapper = getWrapperFromViralProtein(i); const wrapper = getWrapperFromViralProtein(i);
if (oldSelection.indexOf(i) === -1) { if (oldSelection.indexOf(i) === -1) {
this.analysis.addItem(wrapper); addItems.push(wrapper);
} }
} }
for (const i of oldSelection) { for (const i of oldSelection) {
const wrapper = getWrapperFromViralProtein(i); const wrapper = getWrapperFromViralProtein(i);
if (this.tableSelectedViralProteins.indexOf(i) === -1) { if (this.tableSelectedViralProteins.indexOf(i) === -1) {
this.analysis.removeItem(wrapper); removeItems.push(wrapper);
} }
} }
this.analysis.addItems(addItems);
this.analysis.removeItems(removeItems);
} }
} }
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
<div class="field has-addons add-remove-toggle" *ngIf="wrapper.type !== 'drug'"> <div class="field has-addons add-remove-toggle" *ngIf="wrapper.type !== 'drug'">
<app-toggle [value]="analysis.inSelection(wrapper)" <app-toggle [value]="analysis.inSelection(wrapper)"
(valueChange)="$event ? analysis.addItem(wrapper) : analysis.removeItem(wrapper)" textOn="Selected" (valueChange)="$event ? analysis.addItems([wrapper]) : analysis.removeItems([wrapper])" textOn="Selected"
textOff="Deselected" tooltipOn="Add protein to selection." tooltipOff="Remove protein from selection." textOff="Deselected" tooltipOn="Add protein to selection." tooltipOff="Remove protein from selection."
icon="fa-plus"></app-toggle> icon="fa-plus"></app-toggle>
</div> </div>
......
...@@ -57,7 +57,7 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { ...@@ -57,7 +57,7 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges {
constructor(public analysis: AnalysisService) { constructor(public analysis: AnalysisService) {
this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'virus'); this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'virus');
analysis.subscribe(() => { analysis.subscribeList(() => {
this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'virus'); this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'virus');
}); });
} }
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
</a> </a>
</header> </header>
<div *ngIf="collapseData"> <div *ngIf="collapseData">
<div class="card-content"> <div class="card-content">
<app-select-dataset [datasetItems]="datasetItems" [selectedDataset]="selectedDataset" <app-select-dataset [datasetItems]="datasetItems" [selectedDataset]="selectedDataset"
(selectedDatasetChange)="selectedDataset = $event; createNetwork($event.data)"> (selectedDatasetChange)="selectedDataset = $event; createNetwork($event.data)">
...@@ -371,7 +370,6 @@ ...@@ -371,7 +370,6 @@
</div> </div>
</div> </div>
<div class="card bar-large"> <div class="card bar-large">
<header class="card-header"> <header class="card-header">
<p class="card-header-title"> <p class="card-header-title">
...@@ -408,7 +406,7 @@ ...@@ -408,7 +406,7 @@
<td *ngIf="p.type == 'virus'">{{p.data.effectName}}</td> <td *ngIf="p.type == 'virus'">{{p.data.effectName}}</td>
<td *ngIf="p.type == 'host'">{{p.data.name}}</td> <td *ngIf="p.type == 'host'">{{p.data.name}}</td>
<td> <td>
<button (click)="analysis.removeItem(p)" class="button is-small is-danger is-outlined has-tooltip" <button (click)="analysis.removeItems([p])" class="button is-small is-danger is-outlined has-tooltip"
data-tooltip="Remove from selection."> data-tooltip="Remove from selection.">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</button> </button>
......
...@@ -114,16 +114,38 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -114,16 +114,38 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
this.showDetails = false; this.showDetails = false;
this.analysis.subscribe((item, selected) => { this.analysis.subscribeList((items, selected) => {
const node = this.nodeData.nodes.get(item.nodeId); if (!this.nodeData.nodes) {
if (!node) {
return; return;
} }
const pos = this.network.getPositions([item.nodeId]); if (selected !== null) {
node.x = pos[item.nodeId].x; if (items.length === 0) {
node.y = pos[item.nodeId].y; return;
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, true, selected)); }
this.nodeData.nodes.update(node); const updatedNodes = [];
for (const item of items) {
const node = this.nodeData.nodes.get(item.nodeId);
if (!node) {
continue;
}
const pos = this.network.getPositions([item.nodeId]);
node.x = pos[item.nodeId].x;
node.y = pos[item.nodeId].y;
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, true, selected));
updatedNodes.push(node);
}
this.nodeData.nodes.update(updatedNodes);
} else {
const updatedNodes = [];
this.nodeData.nodes.forEach((node) => {
const nodeSelected = this.analysis.idInSelection(node.id);
if (selected !== nodeSelected) {
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, true, selected));
updatedNodes.push(node);
}
});
this.nodeData.nodes.update(updatedNodes);
}
}); });
} }
...@@ -234,9 +256,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -234,9 +256,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
const node = this.nodeData.nodes.get(nodeId); const node = this.nodeData.nodes.get(nodeId);
const wrapper = node.wrapper; const wrapper = node.wrapper;
if (this.analysis.inSelection(wrapper)) { if (this.analysis.inSelection(wrapper)) {
this.analysis.removeItem(wrapper); this.analysis.removeItems([wrapper]);
} else { } else {
this.analysis.addItem(wrapper); this.analysis.addItems([wrapper]);
} }
} }
}); });
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment