Commit 59ff53bf authored by Kruyff,D.L.W. (Dylan)'s avatar Kruyff,D.L.W. (Dylan)
Browse files

Added state management


Former-commit-id: 8fd91b0f
parent 05a4fef8
......@@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
export interface RawData {
index: string[];
values: number[];
name?: string;
}
export interface LshData {
......@@ -33,6 +34,19 @@ export interface Parameters {
stepsize: number;
}
export interface State {
lshData: LshData;
queryWindow: number[][];
weights: number[];
labels: any;
}
export interface StateNode {
id: number;
state: State;
children: number[];
}
@Injectable({
providedIn: 'root'
})
......@@ -115,7 +129,6 @@ export class ApiService {
* Get query window based on windows labeled correct
*/
async getQueryWindow(indices: number | {[index: number]: boolean}, query_size: number, start_index:number): Promise<number[][]> {
console.log('sending query')
const response = await fetch('http://127.0.0.1:5000/query', {
method: 'POST',
headers: {
......
......@@ -21,6 +21,7 @@ import { MatProgressBarModule} from '@angular/material/progress-bar';
import {MatSliderModule} from '@angular/material/slider';
import { TrainingWindowComponent } from './training-window/training-window.component';
import {MatCheckboxModule} from '@angular/material/checkbox';
import { StateManagementComponent } from './state-management/state-management.component';
PlotlyModule.plotlyjs = PlotlyJS;
......@@ -36,6 +37,7 @@ PlotlyModule.plotlyjs = PlotlyJS;
ProgressViewComponent,
MainComponent,
TrainingWindowComponent,
StateManagementComponent,
],
imports: [
BrowserModule,
......
.subplot {
margin-right: 20px;
display: flex;
justify-content: center;
border-right: 1px solid;
border-bottom: 1px solid;
......@@ -23,14 +22,22 @@
.button-holder {
display: flex;
flex-direction: column;
}
.label-button {
width: 30px;
width: 100%;
height: 30px;
}
.channel_header {
height: 20px;
border-bottom: 1px solid lightgray;
color: black;
font-weight: 400;
text-align: center;
vertical-align: middle;
}
.correct-selected {
background-color: #4caf50
}
......
<div class="container">
<div class="subplot-container">
<div class="subplot" *ngFor="let subplot of subplots">
<plotly-plot [data]="subplot.data" [layout]="subplot.layout"></plotly-plot>
<div class="channel_header" [ngStyle]="{'background-color': subplot.color}">
index: {{ subplot.index }}
</div>
<div class="button-holder">
<button class="correct-button label-button" [class.correct-selected]="labels[subplot.index]" (click)="labelCorrect(subplot.index)">&#10004;</button>
<button class="neutral-button label-button" [class.neutral-selected]="labels[subplot.index] == undefined" (click)="labelUndefined(subplot.index)">&#8226;</button>
<button class="incorrect-button label-button" [class.incorrect-selected]="labels[subplot.index] == false" (click)="labelIncorrect(subplot.index)">&#10006;</button>
</div>
<plotly-plot [data]="subplot.data" [layout]="subplot.layout"></plotly-plot>
</div>
</div>
</div>
......
......@@ -64,6 +64,7 @@ export class LabelingWindowComponent implements OnInit {
});
const plot = {
index: this.topk[idx],
color: this.getColor(this.state.lshData.average_candidates.indexOf(this.topk[idx]) / this.state.lshData.average_candidates.length),
data: data,
layout: {
grid: {
......@@ -133,6 +134,7 @@ export class LabelingWindowComponent implements OnInit {
});
const plot = {
index: this.topk[idx],
color: this.getColor(this.state.lshData.average_candidates.indexOf(this.topk[idx]) / this.state.lshData.average_candidates.length),
data: data,
layout: {
grid: {
......@@ -141,7 +143,6 @@ export class LabelingWindowComponent implements OnInit {
subplots: subplots,
},
showlegend: false,
title: `Index: ${this.topk[idx].toString()}`,
hovermode: 'closest',
autosize: true,
margin: {
......@@ -176,4 +177,9 @@ export class LabelingWindowComponent implements OnInit {
this.subplots.push(plot);
}
}
getColor(value) {
const hue=((1-value)*120).toString(10);
return ["hsl(",hue,",100%,50%)"].join("");
}
}
......@@ -7,17 +7,7 @@
<div class="channel_header">
Feedback
</div>
<mat-tab-group animationDuration="0ms" (selectedTabChange)="changeTab($event)">
<mat-tab label="Training">
<app-training-window></app-training-window>
</mat-tab>
<mat-tab label="Labeled data">
<app-labels></app-labels>
</mat-tab>
<mat-tab label="Settings">
<app-settings></app-settings>
</mat-tab>
</mat-tab-group>
<app-training-window></app-training-window>
</div>
</div>
<div class="right">
......@@ -29,7 +19,7 @@
</div>
<div class="bot">
<div class="channel_header">
Progress
Results
</div>
<app-progress-view></app-progress-view>
</div>
......
......@@ -11,10 +11,6 @@ export class MainComponent {
constructor(private state: StateService) {
}
changeTab(tab) {
this.state.currentTab = tab.index;
}
public get greyOut() {
return this.state.querySelectionMode;
}
......
......@@ -8,7 +8,7 @@
<div class="list-element" *ngFor="let index of allChannels">
<mat-checkbox [checked]="isInSelection(index)"
(change)="changeSelection(index)">
Channel {{index}}
{{ names[index] }}
</mat-checkbox>
</div>
</div>
......
......@@ -45,7 +45,6 @@ export class OverviewWindowComponent implements OnInit {
y_val += Math.random() - 0.5;
return {x: x, y: y_val, id: i};
});
console.log(values);
let margin = {top: 30, right: 30, bottom: 30, left: 20 };
let width = 600 - margin.left - margin.right;
let height = 150 - margin.top - margin.bottom;
......@@ -85,7 +84,6 @@ export class OverviewWindowComponent implements OnInit {
.attr('d', (d: any) => line(d));
const zoomed = function() {
console.log(d3.event.transform);
const transform = d3.event.transform;
// lines.attr("transform", d3.event.transform);
xscale = transform.rescaleX(x2);
......@@ -120,8 +118,7 @@ export class OverviewWindowComponent implements OnInit {
scaleX: {
zooming: true,
minValue: 0,
maxValue: this.state.rawData[0].index[this.state.rawData[0].index.length - 1], // //this.state.rawData[0].values.length,
zoomTo: [0, 200],
maxValue: this.state.rawData[0].values.length,
'auto-fit': true,
visible: false,
guide: {
......@@ -174,14 +171,11 @@ export class OverviewWindowComponent implements OnInit {
});
// Initialize channels
console.log(this.state);
console.log(this.state.selection);
this.state.selection.forEach((channelIndex, index) => {
const channel = this.state.rawData[channelIndex];
const data = [];
for (let i = 0; i < channel.values.length; i++) {
data.push([channel.index[i].toString(), channel.values[i]]);
data.push([i, channel.values[i]]);
}
this.graphset.push({
id: index,
......@@ -191,7 +185,6 @@ export class OverviewWindowComponent implements OnInit {
height: '50px',
scaleX: {
zooming: true,
zoomTo: [0, this.state.windowSize],
markers: []
},
'scale-y': {
......@@ -249,8 +242,9 @@ export class OverviewWindowComponent implements OnInit {
this.chart.setdata({
data: this.config
});
console.log(this.config);
console.log("showing plot");
this.minx = 0;
this.maxx = 120;
// await this.debugClicked();
}
......@@ -266,7 +260,7 @@ export class OverviewWindowComponent implements OnInit {
const channel = this.state.rawData[channelIndex];
const data = [];
for (let i = 0; i < channel.values.length; i++) {
data.push([channel.index[i].toString(), channel.values[i]]);
data.push([i, channel.values[i]]);
}
this.graphset.push({
id: channelIndex,
......@@ -276,7 +270,6 @@ export class OverviewWindowComponent implements OnInit {
height: '50px',
scaleX: {
zooming: true,
zoomTo: [0, this.state.windowSize],
markers: []
},
'scale-y': {
......@@ -308,8 +301,9 @@ export class OverviewWindowComponent implements OnInit {
data: this.config
});
// this.chart.addgraph(this.graphset[4]);
console.log(this.config);
console.log("showing plot");
this.minx = 0;
this.maxx = 120;
}
async updatePlot() {
......@@ -326,7 +320,7 @@ export class OverviewWindowComponent implements OnInit {
this.markers.push({
type: 'area',
// BUG: For some reason the range values are multiplied by 10
range: [Number(index) / 100, (Number(index) + this.state.windowSize) / 100],
range: [Number(index) / 10, (Number(index) + this.state.windowSize) / 10],
backgroundColor: "#4caf50",
});
} else {
......@@ -334,7 +328,7 @@ export class OverviewWindowComponent implements OnInit {
this.markers.push({
type: 'area',
// BUG: For some reason the range values are multiplied by 10
range: [Number(index) / 100, (Number(index) + this.state.windowSize) / 100],
range: [Number(index) / 10, (Number(index) + this.state.windowSize) / 10],
backgroundColor: "#f44336",
});
}
......@@ -344,7 +338,7 @@ export class OverviewWindowComponent implements OnInit {
this.markers.push({
type: 'area',
// BUG: For some reason the range values are multiplied by 10
range: [Number(index) / 100, (Number(index) + this.state.windowSize) / 100],
range: [Number(index) / 10, (Number(index) + this.state.windowSize) / 10],
backgroundColor: '#b1a343',
});
}
......@@ -365,7 +359,7 @@ export class OverviewWindowComponent implements OnInit {
}
ping() {
console.log("ping");
console.log("zoom");
}
async updateCandidates(sliderIndex) {
......@@ -442,7 +436,7 @@ export class OverviewWindowComponent implements OnInit {
}
isInSelection(i): boolean {
return i in this.state.selection;
return this.state.selection.includes(i);
}
changeSelection(i: number): void {
......@@ -466,11 +460,11 @@ export class OverviewWindowComponent implements OnInit {
}
get maxx(): number {
return this._maxx * 100;
return this._maxx * 10;
}
set maxx(max: number) {
this._maxx = max / 100;
this._maxx = max / 10;
this.chart.zoomto({
graphid: `zingchart-ng-1-graph-preview`,
xmin: this._minx,
......@@ -488,11 +482,11 @@ export class OverviewWindowComponent implements OnInit {
}
get minx(): number {
return this._minx * 100;
return this._minx * 10;
}
set minx(min: number) {
this._minx = min / 100;
this._minx = min / 10;
this.chart.zoomto({
graphid: `zingchart-ng-1-graph-preview`,
xmin: this._minx,
......@@ -508,4 +502,8 @@ export class OverviewWindowComponent implements OnInit {
});
}
}
get names(): string[] {
return this.state.rawData.map((data, index) => data.name ? data.name : 'Channel ' + index.toString());
}
}
<!--<svg id="visual" width='500' height='300'></svg>-->
<div style="overflow-y: scroll">
<div *ngIf="data" class="histogram">
<div class="container">
<plotly-plot (hover)="onHover($event)" [data]="hist.data" [layout]="hist.layout"></plotly-plot>
</div>
<div class="slider">
<mat-slider [min]="0" [max]="maxLength" step="1" [value]="sliderValue" (input)="setSliderValue($event)" thumbLabel tickInterval="5"></mat-slider>
</div>
</div>
<div *ngIf="data" class="container">
<div class="window">
<div class="plots">
<plotly-plot *ngFor="let data of this.data; index as i;" [class.hide]="i != sliderValue" [data]="data" [layout]="layout"></plotly-plot>
<mat-tab-group animationDuration="0ms">
<mat-tab label="Classifier">
<div style="overflow-y: scroll">
<div *ngIf="data" class="histogram">
<div class="container">
<plotly-plot (hover)="onHover($event)" [data]="hist.data" [layout]="hist.layout"></plotly-plot>
</div>
<div class="slider">
<mat-slider [min]="0" [max]="maxLength" step="1" [value]="sliderValue" (input)="setSliderValue($event)" thumbLabel tickInterval="5"></mat-slider>
</div>
</div>
<div *ngIf="data" class="container">
<div class="window">
<div class="plots">
<plotly-plot *ngFor="let data of this.data; index as i;" [class.hide]="i != sliderValue" [data]="data" [layout]="layout"></plotly-plot>
</div>
</div>
</div>
<div *ngIf="data" class="container">
<button>
Show {{ nrOfCandidates }} similar candidates
</button>
</div>
</div>
</div>
</div>
</mat-tab>
<mat-tab label="History">
<ng-template matTabContent>
<app-state-management></app-state-management>
</ng-template>
</mat-tab>
</mat-tab-group>
<script src='https://d3js.org/d3.v4.min.js'></script>
......@@ -273,6 +273,15 @@ export class ProgressViewComponent implements OnInit {
return this._sliderValue;
}
public get nrOfCandidates(): number {
// let output = 0;
// for (let i = 0; i < this.sliderValue; i++) {
// output += this.state.lshData.average_table[i].length;
// }
// return output;
return 428;
}
public get maxLength(): number {
return Object.keys(this.table).length - 1;
}
......
::ng-deep .node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
::ng-deep .node text {
font: 12px sans-serif;
}
::ng-deep .link {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
::ng-deep .current {
fill: none;
stroke: #f44336;
stroke-width: 2px;
}
import { Component, OnInit } from '@angular/core';
import * as d3 from 'd3';
import {StateService} from '../state.service';
import {style} from '@angular/animations';
import {State} from '../api.service';
@Component({
selector: 'app-state-management',
templateUrl: './state-management.component.html',
styleUrls: ['./state-management.component.css']
})
export class StateManagementComponent implements OnInit {
private treeCreated = false;
private selectedNode;
private treemap;
constructor(private state: StateService) { }
ngOnInit(): void {
if (this.state.lshData) {
this.createStateTree();
}
this.state.onNewLshData.subscribe(() => {
if (!this.treeCreated) {
this.createStateTree();
} else {
this.update();
}
});
}
createStateTree() {
console.log('Creating tree!');
const margin = {top: 20, right: 90, bottom: 30, left: 90};
const width = 960 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
const svg = d3.select('.state').append('svg')
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate("
+ margin.left + "," + margin.top + ")");
let root;
const data = this.state.states;
this.selectedNode = d3.hierarchy(data[1], function(d) { return d.children ? d.children.map(function(child) {return data[child]}) : undefined });
this.selectedNode.x0 = height / 2;
this.selectedNode.y0 = 20;
this.treeCreated = true;
this.update();
}
update() {
const margin = {top: 20, right: 90, bottom: 30, left: 90};
const width = 960 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
if (!this.treemap) {
this.treemap = d3.tree().size([height, width]);
}
let i = 0;
const duration = 750;
const svg = d3.select('.state').select('svg');
console.log(svg);
const data = this.state.states;
const currIndex = this.state.currStateIndex;
const root = d3.hierarchy(data[1], function(d) { return d.children ? d.children.map(function(child) {return data[child]}) : undefined });
const source = this.selectedNode;
const loadState = (id) => {
this.state.loadState(id);
};
// // Assigns the x and y position for the nodes
// let root;
// const data = this.state.states;
// root = d3.hierarchy(data[1], function(d) { return d.children ? d.children.map(function(child) {return data[child]}) : undefined });
const treeData = this.treemap(root);
// Compute the new tree layout.
const nodes = treeData.descendants();
console.log('descendants');
console.log(nodes);
const links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180
});
// ****************** Nodes section ***************************
svg.selectAll('g.node').select('circle').style("fill", "#fff");
// Update the nodes...
const node = svg.selectAll('g.node')
.data(nodes, function(d: any) {
console.log('all-nodes');
console.log(d);
return d.id;
});
console.log('node');
console.log(node);
// Enter any new modes at the parent's previous position.
const nodeEnter = node.enter().append('g')
.attr('class',function(d) {
console.log('new nodes');
return 'node';
})
// .attr("transform", function(d) {
// console.log('new nodes');
// return "translate(" + source.y0 + "," + source.x0 + ")";
// })
.on('click', function(d: any) {
loadState(d.data.id);
});
// Add Circle for the nodes
nodeEnter.append('circle')