From cee1570760cd25222a284628c843a35370fa2d99 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 17:05:56 +0000 Subject: [PATCH 01/15] rebase on main --- web/src/app/components/cdf/cdf.component.ts | 273 +++++++++++++++++++- web/src/app/components/duration.utils.ts | 8 +- 2 files changed, 267 insertions(+), 14 deletions(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 4ab0eaa..c922c4a 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -20,7 +20,7 @@ import * as d3 from 'd3'; import {step189_2020} from '../../../proto/step189_2020'; import {findDurationUnit} from '../duration.utils'; -import {addCurrentPushLine, generateQuantiles, generateYPosition, populateData} from './cdf.utils'; +import {addCurrentPushLine, generateQuantiles, generateYPosition, getProbabilityForDuration, populateData} from './cdf.utils'; import {COMPLETED_BLUE, d3SVG, Item, STROKE_COLOR} from './cdf.utils'; @Component({ @@ -114,6 +114,11 @@ export class CDFComponent implements AfterViewInit { probability: 1, endState: 5 }); + const maxExtendedDuration = d3.max(extendedData, d => d.duration); + if (!maxExtendedDuration) { + return; + } + this.svg = (d3.select(element).append('svg') as d3SVG) .attr('width', elementWidth) @@ -250,22 +255,274 @@ export class CDFComponent implements AfterViewInit { yPosition = generateYPosition(radius * 2 + 0.1, xScale, xVals); } + const currentPushLine = + this.svg.append('g') + .attr('id', 'current-push-line') + .attr('transform', `translate(${margin.left}, ${margin.top})`); + + addCurrentPushLine( + this.currentPush, currentPushLine, this.data, height, xScale, yScale); + + // Mouse click + let startValue = minDuration + 1e-6; + + const clipRect = cdfChart.append('clipPath') + .attr('id', 'area-clip') + .append('rect') + .attr('class', 'area-clip-rect') + .attr('x', 0) + .attr('y', 0) + .attr('width', xScale(startValue)) + .attr('height', height); + + cdfChart.datum(extendedData) + .append('path') + .attr('class', 'cdf-curve') + .attr( + 'd', + d3.area() + .x(d => xScale(d.duration)) + .y1(d => yScale(d.probability)) + .y0(yScale(0)) + .curve(d3.curveStepAfter)) + .attr('fill-opacity', '0.6') + .attr('fill', 'white') + .attr('clip-path', 'url(#area-clip)'); + for (let i = 0; i < this.data.length; i++) { const cx = xScale(this.data[i].duration); const cy = height - yPosition[i] - radius; - dotplotContainer.append('circle') + cdfChart.append('circle') + .attr('class', 'dots') .attr('cx', cx) .attr('r', radius) .attr('cy', cy) .attr('fill', 'black'); } - const currentPushLine = - this.svg.append('g') - .attr('id', 'current-push-line') - .attr('transform', `translate(${margin.left}, ${margin.top})`); + const lineY = + cdfChart.append('line') + .attr('class', 'click-line-y') + .attr('x1', xScale(startValue)) + .attr('x2', xScale(startValue)) + .attr( + 'y1', yScale(getProbabilityForDuration(this.data, startValue))) + .attr('y2', height) + .attr('stroke', 'black') + .attr('stroke-width', '1px') + .attr('opacity', 0); + + const lineX = + cdfChart.append('line') + .attr('class', 'click-line-x') + .attr('x1', 0) + .attr('x2', xScale(startValue)) + .attr( + 'y1', yScale(getProbabilityForDuration(this.data, startValue))) + .attr( + 'y2', yScale(getProbabilityForDuration(this.data, startValue))) + .attr('stroke', 'black') + .attr('stroke-width', '1px') + .attr('opacity', 0); - addCurrentPushLine( - this.currentPush, currentPushLine, this.data, height, xScale, yScale); + cdfChart.append('text') + .attr('x', xScale(startValue) - 35) + .attr( + 'y', + yScale(getProbabilityForDuration(this.data, startValue) / 100) + 30) + .attr('class', 'click-line-y-text') + .attr('font-size', '10px') + .text(`${this.data.filter(c => c.duration <= startValue).length}/${ + this.data.length}`) + .attr('opacity', 0); + + cdfChart.append('text') + .attr('x', xScale(startValue) / 2) + .attr( + 'y', + yScale(getProbabilityForDuration(this.data, startValue) / 100) + 10) + .attr('class', 'click-line-x-text') + .attr('font-size', '10px') + .text(`${getProbabilityForDuration(this.data, startValue).toFixed(2)}%`) + .attr('opacity', 0); + + cdfChart.on('click', (d: unknown, i: number): void => { + if (!d) { + return; + } + const coordinates = d3.mouse(d3.event.currentTarget); + const xValue = xScale.invert(coordinates[0]); + if (xValue > minDuration) { + startValue = xValue; + const yValue = getProbabilityForDuration(d as Item[], startValue); + d3.select(d3.event.currentTarget) + .select('.area-clip-rect') + .attr('width', xScale(startValue)); + d3.select(d3.event.currentTarget) + .select('.click-line-y') + .attr('y1', yScale(yValue)) + .attr('x1', xScale(startValue)) + .attr('x2', xScale(startValue)) + .attr('opacity', 1); + d3.select(d3.event.currentTarget) + .select('.click-line-x') + .attr('y1', yScale(yValue)) + .attr('y2', yScale(yValue)) + .attr('x2', xScale(startValue)) + .attr('opacity', 1); + d3.select(d3.event.currentTarget) + .select('.click-line-y-text') + .attr('x', xScale(startValue) - 40) + .attr('y', yScale(yValue) + 20) + .text(`${this.data.filter(c => c.duration <= startValue).length}/${ + this.data.length}`) + .attr('opacity', 1); + d3.select(d3.event.currentTarget) + .select('.click-line-x-text') + .attr('x', xScale(startValue) / 2) + .attr('y', yScale(yValue) + 10) + .text(`${ + (getProbabilityForDuration(this.data, startValue) * 100) + .toFixed(1)}%`) + .attr('opacity', 1); + d3.select(d3.event.currentTarget) + .selectAll('.dots') + .data(this.data) + .attr( + 'fill', + (dp: Item) => dp.duration <= startValue ? 'grey' : 'black'); + } + }); + + // hover + const highlightColor = '#b9edc4'; + const strokeColor = '#167364'; + const circleColor = '#a0aade'; + const labelsize = [40, 25]; + const labelFontSize = labelsize[1] / 2; + + cdfChart.append('rect') + .attr('class', 'x-label-bg') + .attr('x', 0) + .attr('y', height) + .attr('height', labelsize[1]) + .attr('width', labelsize[0]) + .attr('fill', highlightColor) + .attr('opacity', 0); + + cdfChart.append('text') + .attr('text-anchor', 'middle') + .attr('class', 'x-label') + .attr('x', 0) + .attr('y', height + 5 + labelFontSize) + .attr('opacity', 0) + .style('font-size', `${labelFontSize}px`) + .style('font-weight', 'bold') + .attr('fill', 'black'); + + cdfChart.append('rect') + .attr('class', 'y-label-bg') + .attr('x', -labelsize[0]) + .attr('y', 0) + .attr('height', labelsize[1]) + .attr('width', labelsize[0]) + .attr('fill', highlightColor) + .attr('opacity', 0); + + cdfChart.append('text') + .attr('text-anchor', 'middle') + .attr('class', 'y-label') + .attr('x', -labelsize[0] / 2) + .attr('y', 0) + .attr('opacity', 0) + .attr('fill', 'black') + .style('font-size', `${labelFontSize}px`); + + cdfChart.append('line') + .attr('x1', 0) + .attr('x2', 0) + .attr('y1', height) + .attr('y2', 0) + .attr('class', 'v-ruler') + .attr('stroke', 'grey') + .attr('stroke-width', 2) + .attr('stroke-dasharray', '5,2') + .attr('opacity', 0); + + cdfChart.append('line') + .attr('x1', 0) + .attr('x2', width) + .attr('y1', 0) + .attr('y2', 0) + .attr('class', 'h-ruler') + .attr('stroke', 'grey') + .attr('stroke-width', 2) + .attr('stroke-dasharray', '5,2') + .attr('opacity', 0); + + cdfChart.append('circle') + .attr('class', 'marker') + .attr('cx', 0) + .attr('cy', 0) + .attr('r', 5) + .attr('stroke', strokeColor) + .attr('stroke-width', 2) + .attr('fill', circleColor) + .style('opacity', 0); + + cdfChart.on('mousemove', (d: unknown, i: number): void => { + const mouseX = xScale.invert(d3.mouse(d3.event.currentTarget)[0]); + const vruler = d3.select('.v-ruler'); + const hruler = d3.select('.h-ruler'); + const marker = d3.select('.marker'); + const xlabel = d3.select('.x-label'); + const xlabelbg = d3.select('.x-label-bg'); + const ylabel = d3.select('.y-label'); + const ylabelbg = d3.select('.y-label-bg'); + const checkX = (mouseX >= minDuration) && (mouseX <= maxExtendedDuration); + + if (checkX) { + const xVal = d3.mouse(d3.event.currentTarget)[0]; + const xInverted = xScale.invert(xVal); + const yVal = yScale(getProbabilityForDuration(d as Item[], xInverted)); + + marker.attr('cx', xVal).attr('cy', yVal); + hruler.attr('y1', yVal).attr('y2', yVal); + vruler.attr('x1', xVal).attr('x2', xVal); + + const xText = d3.format(',.1f')(xInverted); + const yText = d3.format(',.1%')(yScale.invert(yVal)); + xlabel.attr('x', +marker.attr('cx')).text(xText); + xlabelbg.attr('x', +marker.attr('cx') - labelsize[0] / 2); + ylabel.attr('y', +marker.attr('cy') + labelsize[1] / 5).text(yText); + ylabelbg.attr('y', +marker.attr('cy') - labelsize[1] / 2); + + marker.style('opacity', 1); + hruler.style('opacity', 1); + vruler.style('opacity', 1); + xlabel.style('opacity', 1); + xlabelbg.style('opacity', 1); + ylabel.style('opacity', 1); + ylabelbg.style('opacity', 1); + } else { + marker.style('opacity', 0); + hruler.style('opacity', 0); + vruler.style('opacity', 0); + xlabel.style('opacity', 0); + xlabelbg.style('opacity', 0); + ylabel.style('opacity', 0); + ylabelbg.style('opacity', 0); + } + }); + + cdfChart.on('mouseleave', (d: unknown, i: number): void => { + const vruler = d3.select('.v-ruler').style('opacity', 0); + const hruler = d3.select('.h-ruler').style('opacity', 0); + const marker = d3.select('.marker').style('opacity', 0); + const xlabel = d3.select('.x-label').style('opacity', 0); + const xlabelbg = d3.select('.x-label-bg').style('opacity', 0); + const ylabel = d3.select('.y-label').style('opacity', 0); + const ylabelbg = d3.select('.y-label-bg').style('opacity', 0); + }); } } diff --git a/web/src/app/components/duration.utils.ts b/web/src/app/components/duration.utils.ts index e2378a5..6ab943a 100644 --- a/web/src/app/components/duration.utils.ts +++ b/web/src/app/components/duration.utils.ts @@ -41,12 +41,8 @@ export const UNIT_CONVERSION: {[unit: string]: number} = { * @return one of [seconds, minutes, hours, days] */ export function findDurationUnit(pushInfos: step189_2020.IPushInfo[]): string { - const unitCounter: {[unit: string]: number} = { - seconds: 0, - minutes: 0, - hours: 0, - days: 0 - }; + const unitCounter: + {[unit: string]: number} = {seconds: 0, minutes: 0, hours: 0, days: 0}; pushInfos.forEach(pushInfo => { if (!pushInfo) { From eea69e3b93a1eb1f39e268f955792d01a8ed9457 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 17:17:25 +0000 Subject: [PATCH 02/15] fix svg structure comment --- web/src/app/components/cdf/cdf.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index c922c4a..5557054 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -57,9 +57,10 @@ export class CDFComponent implements AfterViewInit { * * * - * + * * * + * * * (Only if the visited push is completed) * From 8928b98309027e0947181f5c976f0aa6a3a36ed8 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 19:55:27 +0000 Subject: [PATCH 03/15] update axis issues --- web/src/app/components/cdf/cdf.component.ts | 36 ++++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 5557054..315ee38 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -53,9 +53,23 @@ export class CDFComponent implements AfterViewInit { * * * + * + * + * [...] + * + * + * + * + * + * + * + * + * + * + * + * + * * - * - * * * * @@ -157,19 +171,16 @@ export class CDFComponent implements AfterViewInit { // Remove axis' vertical line and keep the tick marks yAxisRight.select('.domain').remove(); - const xAxis = - this.svg.append('g') - .attr('id', 'x-axis') - .attr( - 'transform', - `translate(${margin.left}, ${elementHeight - margin.bottom})`) - .call(d3.axisBottom(xScale)); + const xAxis = cdfChart.append('g') + .attr('id', 'x-axis') + .attr('transform', `translate(0, ${height})`) + .call(d3.axisBottom(xScale)); - this.svg.append('text') + cdfChart.append('text') .attr('id', 'x-axis-label') .attr( 'transform', - `translate(${width / 2}, ${elementHeight - margin.left / 4})`) + `translate(${width / 2}, ${height + margin.left / 1.5})`) .style('text-anchor', 'middle') .style('font-size', '12px') .text(`Duration (${this.durationUnit})`); @@ -278,7 +289,7 @@ export class CDFComponent implements AfterViewInit { cdfChart.datum(extendedData) .append('path') - .attr('class', 'cdf-curve') + .attr('class', 'cdf-clipped') .attr( 'd', d3.area() @@ -436,6 +447,7 @@ export class CDFComponent implements AfterViewInit { .attr('x', -labelsize[0] / 2) .attr('y', 0) .attr('opacity', 0) + .style('font-weight', 'bold') .attr('fill', 'black') .style('font-size', `${labelFontSize}px`); From e4a0df17f38450da505818a21d9ce1d6bd31e4b9 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Fri, 28 Aug 2020 22:59:35 +0000 Subject: [PATCH 04/15] Add material slider toggle to cdf graph --- web/angular.json | 4 ++- web/package-lock.json | 25 ++++++++++++++++ web/package.json | 2 ++ web/src/app/app.module.ts | 29 ++++++------------- .../pages/one-push/one-push.component.html | 9 ++++-- .../app/pages/one-push/one-push.component.ts | 2 ++ web/src/index.html | 4 ++- web/src/styles.scss | 3 ++ 8 files changed, 54 insertions(+), 24 deletions(-) diff --git a/web/angular.json b/web/angular.json index e6549cf..2962eb5 100644 --- a/web/angular.json +++ b/web/angular.json @@ -31,6 +31,7 @@ "src/assets" ], "styles": [ + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "node_modules/tachyons/css/tachyons.min.css", "src/styles.scss" ], @@ -96,6 +97,7 @@ "src/assets" ], "styles": [ + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.scss" ], "scripts": [] @@ -133,4 +135,4 @@ "cli": { "analytics": "bf853fd1-38d6-4df6-a388-35a46c9a7ff1" } -} +} \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 5073499..228bc82 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -531,6 +531,23 @@ "tslib": "^2.0.0" } }, + "@angular/cdk": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-10.1.3.tgz", + "integrity": "sha512-xMV1M41mfuaQod4rtAG/duYiWffGIC2C87E1YuyHTh8SEcHopGVRQd2C8PWH+iwinPbes7AjU1uzCEvmOYikrA==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.0.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + } + } + }, "@angular/cli": { "version": "10.0.5", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-10.0.5.tgz", @@ -788,6 +805,14 @@ "tslib": "^2.0.0" } }, + "@angular/material": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-10.1.3.tgz", + "integrity": "sha512-6ygbCVcejFydmZUlOcNreiWQTvL4kOrEp/M51DV70hqffTnxajCzaRe2MQhxisENB/bR8mtMvf8YY3Rsys/HCw==", + "requires": { + "tslib": "^2.0.0" + } + }, "@angular/platform-browser": { "version": "10.0.8", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-10.0.8.tgz", diff --git a/web/package.json b/web/package.json index 4d918f3..47f68c7 100644 --- a/web/package.json +++ b/web/package.json @@ -17,10 +17,12 @@ "private": true, "dependencies": { "@angular/animations": "~10.0.4", + "@angular/cdk": "^10.1.3", "@angular/common": "~10.0.4", "@angular/compiler": "~10.0.4", "@angular/core": "~10.0.4", "@angular/forms": "~10.0.4", + "@angular/material": "^10.1.3", "@angular/platform-browser": "~10.0.4", "@angular/platform-browser-dynamic": "~10.0.4", "@angular/router": "~10.0.4", diff --git a/web/src/app/app.module.ts b/web/src/app/app.module.ts index ca1991f..93cb764 100644 --- a/web/src/app/app.module.ts +++ b/web/src/app/app.module.ts @@ -1,22 +1,9 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - import {HttpClientModule} from '@angular/common/http'; import {NgModule} from '@angular/core'; +import {FormsModule} from '@angular/forms'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; import {BrowserModule} from '@angular/platform-browser'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {AppRoutingModule} from './app-routing.module'; import {AppComponent} from './app.component'; @@ -25,7 +12,6 @@ import {ButtonRowComponent} from './components/button-row/button-row.component'; import {ButtonComponent} from './components/button/button.component'; import {CDFComponent} from './components/cdf/cdf.component'; import {PageNameComponent} from './components/page-name/page-name.component'; -import {TimelineComponent} from './components/timeline/timeline.component'; import {AllPushesComponent} from './pages/all-pushes/all-pushes.component'; import {MyPushesComponent} from './pages/my-pushes/my-pushes.component'; import {OnePushComponent} from './pages/one-push/one-push.component'; @@ -35,11 +21,14 @@ import {DateNsecPipe} from './pipes/date-nsec.pipe'; declarations: [ AppComponent, AllPushesComponent, BarChartComponent, ButtonComponent, ButtonRowComponent, CDFComponent, DateNsecPipe, MyPushesComponent, - OnePushComponent, PageNameComponent, TimelineComponent + OnePushComponent, PageNameComponent + ], + imports: [ + AppRoutingModule, BrowserModule, HttpClientModule, BrowserAnimationsModule, + MatSlideToggleModule, FormsModule ], - imports: [AppRoutingModule, BrowserModule, HttpClientModule], providers: [], bootstrap: [AppComponent] }) export class AppModule { -} +} \ No newline at end of file diff --git a/web/src/app/pages/one-push/one-push.component.html b/web/src/app/pages/one-push/one-push.component.html index c9e561e..44b7683 100644 --- a/web/src/app/pages/one-push/one-push.component.html +++ b/web/src/app/pages/one-push/one-push.component.html @@ -11,10 +11,15 @@ [currentPush]='pushInfo | async'> -Show CDF + +
+ - + +
+ diff --git a/web/src/app/pages/one-push/one-push.component.ts b/web/src/app/pages/one-push/one-push.component.ts index 7b4fb21..9cfe4e6 100644 --- a/web/src/app/pages/one-push/one-push.component.ts +++ b/web/src/app/pages/one-push/one-push.component.ts @@ -22,12 +22,14 @@ import {flatMap, map, shareReplay} from 'rxjs/operators'; import {step189_2020} from '../../../proto/step189_2020'; + @Component({ selector: 'app-one-push', templateUrl: './one-push.component.html', styleUrls: ['./one-push.component.scss'] }) export class OnePushComponent { + showcdf = false; readonly pushHandle: Observable; readonly pushDefName: Observable; readonly pushInfos: Observable; diff --git a/web/src/index.html b/web/src/index.html index e027e10..83cc1da 100644 --- a/web/src/index.html +++ b/web/src/index.html @@ -6,8 +6,10 @@ + + - + diff --git a/web/src/styles.scss b/web/src/styles.scss index 11baa2f..1ba3e02 100644 --- a/web/src/styles.scss +++ b/web/src/styles.scss @@ -27,3 +27,6 @@ a { .hover-cursor-default:hover { cursor: default; } + +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } From 93baa9dede01d457fa41df589dcb5a6bf3de9f97 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 18:01:21 +0000 Subject: [PATCH 05/15] update toggle --- web/src/app/app.module.ts | 3 ++- web/src/app/pages/one-push/one-push.component.html | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/web/src/app/app.module.ts b/web/src/app/app.module.ts index 93cb764..fc92405 100644 --- a/web/src/app/app.module.ts +++ b/web/src/app/app.module.ts @@ -11,6 +11,7 @@ import {BarChartComponent} from './components/bar-chart/bar-chart.component'; import {ButtonRowComponent} from './components/button-row/button-row.component'; import {ButtonComponent} from './components/button/button.component'; import {CDFComponent} from './components/cdf/cdf.component'; +import {TimelineComponent} from './components/timeline/timeline.component'; import {PageNameComponent} from './components/page-name/page-name.component'; import {AllPushesComponent} from './pages/all-pushes/all-pushes.component'; import {MyPushesComponent} from './pages/my-pushes/my-pushes.component'; @@ -21,7 +22,7 @@ import {DateNsecPipe} from './pipes/date-nsec.pipe'; declarations: [ AppComponent, AllPushesComponent, BarChartComponent, ButtonComponent, ButtonRowComponent, CDFComponent, DateNsecPipe, MyPushesComponent, - OnePushComponent, PageNameComponent + OnePushComponent, PageNameComponent, TimelineComponent ], imports: [ AppRoutingModule, BrowserModule, HttpClientModule, BrowserAnimationsModule, diff --git a/web/src/app/pages/one-push/one-push.component.html b/web/src/app/pages/one-push/one-push.component.html index 44b7683..d098017 100644 --- a/web/src/app/pages/one-push/one-push.component.html +++ b/web/src/app/pages/one-push/one-push.component.html @@ -13,12 +13,10 @@ Show CDF -
- - -
+ + From 488a73e0e1a2b090ac2fb5288adb965d5c541ec1 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 20:23:56 +0000 Subject: [PATCH 06/15] change to view checked --- web/src/app/components/cdf/cdf.component.ts | 8 +++++--- web/src/app/pages/one-push/one-push.component.html | 7 ++++--- web/src/app/pages/one-push/one-push.component.ts | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 315ee38..8e34263 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import {AfterViewInit, Component, ElementRef, Input, ViewChild} from '@angular/core'; +import {AfterViewChecked, Component, ElementRef, Input, ViewChild} from '@angular/core'; import * as d3 from 'd3'; import {step189_2020} from '../../../proto/step189_2020'; @@ -29,10 +29,11 @@ import {COMPLETED_BLUE, d3SVG, Item, STROKE_COLOR} from './cdf.utils'; styleUrls: ['./cdf.component.scss'] }) -export class CDFComponent implements AfterViewInit { +export class CDFComponent implements AfterViewChecked { @ViewChild('cdf') private CDFContainer!: ElementRef; @Input() pushInfos!: step189_2020.IPushInfo[]|null; @Input() currentPush!: step189_2020.IPushInfo|null; + @Input() showDots: boolean; private data: Item[] = []; private svg: d3SVG|undefined; @@ -87,13 +88,14 @@ export class CDFComponent implements AfterViewInit { * * */ - ngAfterViewInit(): void { + ngAfterViewChecked(): void { if (!this.pushInfos) { return; } if (!this.currentPush) { return; } + console.log(this.showDots) this.durationUnit = findDurationUnit(this.pushInfos); this.data = populateData(this.pushInfos); diff --git a/web/src/app/pages/one-push/one-push.component.html b/web/src/app/pages/one-push/one-push.component.html index d098017..59db99c 100644 --- a/web/src/app/pages/one-push/one-push.component.html +++ b/web/src/app/pages/one-push/one-push.component.html @@ -11,11 +11,12 @@ [currentPush]='pushInfo | async'> -Show CDF +Show dots - + [currentPush]='pushInfo | async' + [showDots]='showDots'> diff --git a/web/src/app/pages/one-push/one-push.component.ts b/web/src/app/pages/one-push/one-push.component.ts index 9cfe4e6..bf85fba 100644 --- a/web/src/app/pages/one-push/one-push.component.ts +++ b/web/src/app/pages/one-push/one-push.component.ts @@ -29,7 +29,7 @@ import {step189_2020} from '../../../proto/step189_2020'; styleUrls: ['./one-push.component.scss'] }) export class OnePushComponent { - showcdf = false; + showDots = false; readonly pushHandle: Observable; readonly pushDefName: Observable; readonly pushInfos: Observable; From c065d0afcffbabd7f667844f11b84b08eaf56200 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 20:43:34 +0000 Subject: [PATCH 07/15] try to fix viewChecked --- web/src/app/components/cdf/cdf.component.ts | 5 +++++ web/src/app/components/duration.utils.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 8e34263..18662cb 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -38,6 +38,7 @@ export class CDFComponent implements AfterViewChecked { private data: Item[] = []; private svg: d3SVG|undefined; private durationUnit = ''; + private showDotsBoolean; /** * Creates a CDF chart by plotting the duration of completed pushes against @@ -95,6 +96,10 @@ export class CDFComponent implements AfterViewChecked { if (!this.currentPush) { return; } + if (this.showDotsBoolean === this.showDots) { + return; + } + this.showDotsBoolean = this.showDots console.log(this.showDots) this.durationUnit = findDurationUnit(this.pushInfos); this.data = populateData(this.pushInfos); diff --git a/web/src/app/components/duration.utils.ts b/web/src/app/components/duration.utils.ts index 6ab943a..6e847c0 100644 --- a/web/src/app/components/duration.utils.ts +++ b/web/src/app/components/duration.utils.ts @@ -41,8 +41,12 @@ export const UNIT_CONVERSION: {[unit: string]: number} = { * @return one of [seconds, minutes, hours, days] */ export function findDurationUnit(pushInfos: step189_2020.IPushInfo[]): string { - const unitCounter: - {[unit: string]: number} = {seconds: 0, minutes: 0, hours: 0, days: 0}; + const unitCounter: {[unit: string]: number} = { + seconds: 0, + minutes: 0, + hours: 0, + days: 0, + }; pushInfos.forEach(pushInfo => { if (!pushInfo) { From b7c6dd5d05e4c5a34796c1797ea8952ea2317fd6 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 20:48:50 +0000 Subject: [PATCH 08/15] add boolean value --- web/src/app/components/cdf/cdf.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 18662cb..c9a06ab 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -38,7 +38,7 @@ export class CDFComponent implements AfterViewChecked { private data: Item[] = []; private svg: d3SVG|undefined; private durationUnit = ''; - private showDotsBoolean; + private showDotsBoolean = true; /** * Creates a CDF chart by plotting the duration of completed pushes against @@ -545,4 +545,5 @@ export class CDFComponent implements AfterViewChecked { const ylabelbg = d3.select('.y-label-bg').style('opacity', 0); }); } + } From df7291c23267bbbe1d50d55312e24debe28aa762 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Tue, 1 Sep 2020 21:18:53 +0000 Subject: [PATCH 09/15] toggle to show dots works --- web/src/app/components/cdf/cdf.component.ts | 42 +++++++++++++-------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index c9a06ab..ab1ef4a 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import {AfterViewChecked, Component, ElementRef, Input, ViewChild} from '@angular/core'; +import {AfterViewChecked, AfterViewInit, Component, ElementRef, Input, ViewChild} from '@angular/core'; import * as d3 from 'd3'; import {step189_2020} from '../../../proto/step189_2020'; @@ -29,17 +29,34 @@ import {COMPLETED_BLUE, d3SVG, Item, STROKE_COLOR} from './cdf.utils'; styleUrls: ['./cdf.component.scss'] }) -export class CDFComponent implements AfterViewChecked { +export class CDFComponent implements AfterViewChecked, AfterViewInit { @ViewChild('cdf') private CDFContainer!: ElementRef; @Input() pushInfos!: step189_2020.IPushInfo[]|null; @Input() currentPush!: step189_2020.IPushInfo|null; - @Input() showDots: boolean; + @Input() showDots!: boolean; private data: Item[] = []; private svg: d3SVG|undefined; private durationUnit = ''; - private showDotsBoolean = true; + private showDotsBoolean: boolean; + ngAfterViewChecked() { + if (this.showDotsBoolean === this.showDots) { + return; + } + if (!this.svg) { + return; + } + this.showDotsBoolean = this.showDots; + console.log(this.showDots); + if (!this.showDotsBoolean) { + this.svg.select('#cdf-chart').selectAll('.dots').attr('opacity', 0); + } + else { + this.svg.select('#cdf-chart').selectAll('.dots').attr('opacity', 1); + } + + } /** * Creates a CDF chart by plotting the duration of completed pushes against * the probability of a push taking less time than that duration. Adds lines @@ -89,18 +106,14 @@ export class CDFComponent implements AfterViewChecked { * * */ - ngAfterViewChecked(): void { + ngAfterViewInit(): void { if (!this.pushInfos) { return; } if (!this.currentPush) { return; } - if (this.showDotsBoolean === this.showDots) { - return; - } - this.showDotsBoolean = this.showDots - console.log(this.showDots) + this.showDotsBoolean = this.showDots; this.durationUnit = findDurationUnit(this.pushInfos); this.data = populateData(this.pushInfos); @@ -255,11 +268,6 @@ export class CDFComponent implements AfterViewChecked { .attr('font-size', '10px') .text((d: Item) => `${d.probability * 100}%`); - const dotplotContainer = - this.svg.append('g') - .attr('id', 'dotplot-container') - .attr('transform', `translate(${margin.left}, ${margin.top})`); - let radius = 2.5; const xVals = this.data.map(d => d.duration); let yPosition = generateYPosition(radius * 2 + 0.1, xScale, xVals); @@ -319,6 +327,9 @@ export class CDFComponent implements AfterViewChecked { .attr('fill', 'black'); } + if (!this.showDotsBoolean) { + cdfChart.selectAll('.dots').attr('opacity', 0); + } const lineY = cdfChart.append('line') .attr('class', 'click-line-y') @@ -545,5 +556,4 @@ export class CDFComponent implements AfterViewChecked { const ylabelbg = d3.select('.y-label-bg').style('opacity', 0); }); } - } From 2a83f4996356c9b0996ede39d023ee6dbca920a2 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Fri, 28 Aug 2020 21:59:20 +0000 Subject: [PATCH 10/15] add all states to cdf --- web/src/app/components/cdf/cdf.component.ts | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index ab1ef4a..8046fd6 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -30,6 +30,23 @@ import {COMPLETED_BLUE, d3SVG, Item, STROKE_COLOR} from './cdf.utils'; }) export class CDFComponent implements AfterViewChecked, AfterViewInit { + private static readonly NANO_TO_MINUTES: number = (10 ** 9) * 60; + private static readonly STATE_TO_COLOR: { [index: number]: string } = { + 1: '#eee', + 3: '#2196f3', + 4: '#d50000', + 5: '#34a853', + 6: '#d50000', + 7: '#2196f3', + 8: '#2196f3', + 9: '#d50000', + 10: '#2196f3', + 12: '#d50000', + 13: '#2196f3', + 15: '#2196f3', + 16: '#d50000' + }; + @ViewChild('cdf') private CDFContainer!: ElementRef; @Input() pushInfos!: step189_2020.IPushInfo[]|null; @Input() currentPush!: step189_2020.IPushInfo|null; @@ -555,5 +572,27 @@ export class CDFComponent implements AfterViewChecked, AfterViewInit { const ylabel = d3.select('.y-label').style('opacity', 0); const ylabelbg = d3.select('.y-label-bg').style('opacity', 0); }); + .attr('class', 'graph-title') + .attr('x', elementWidth / 2) + .attr('y', margin.top / 2) + .attr('text-anchor', 'middle') + .style('font-size', '16px') + .text('CDF of push durations'); + + const rectHeight = Math.floor(height / this.data.length) + 1; + console.log(rectHeight) + for (let i = this.data.length - 1; i >= 0; i--) { + const elem = this.data[i]; + cdfChart.append('rect') + .attr('class', 'rect-area') + .attr('fill', CDFComponent.STATE_TO_COLOR[elem.endState]) + //.attr('stroke', 'none') hi + //.attr('stroke-width', 0.5) + .attr('x', xScale(elem.duration)) + .attr('y', yScale(elem.probability)) + .attr('height', rectHeight) + .attr('width', width - xScale(elem.duration)) + .attr('opacity', 1); + } } } From 98c2cf037710907cf6f7fd04ce078c5d759798f9 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Wed, 2 Sep 2020 21:12:38 +0000 Subject: [PATCH 11/15] revert --- web/src/app/components/cdf/cdf.component.ts | 22 --------------------- 1 file changed, 22 deletions(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 8046fd6..70e5079 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -55,25 +55,7 @@ export class CDFComponent implements AfterViewChecked, AfterViewInit { private data: Item[] = []; private svg: d3SVG|undefined; private durationUnit = ''; - private showDotsBoolean: boolean; - ngAfterViewChecked() { - if (this.showDotsBoolean === this.showDots) { - return; - } - if (!this.svg) { - return; - } - this.showDotsBoolean = this.showDots; - console.log(this.showDots); - if (!this.showDotsBoolean) { - this.svg.select('#cdf-chart').selectAll('.dots').attr('opacity', 0); - } - else { - this.svg.select('#cdf-chart').selectAll('.dots').attr('opacity', 1); - } - - } /** * Creates a CDF chart by plotting the duration of completed pushes against * the probability of a push taking less time than that duration. Adds lines @@ -130,7 +112,6 @@ export class CDFComponent implements AfterViewChecked, AfterViewInit { if (!this.currentPush) { return; } - this.showDotsBoolean = this.showDots; this.durationUnit = findDurationUnit(this.pushInfos); this.data = populateData(this.pushInfos); @@ -344,9 +325,6 @@ export class CDFComponent implements AfterViewChecked, AfterViewInit { .attr('fill', 'black'); } - if (!this.showDotsBoolean) { - cdfChart.selectAll('.dots').attr('opacity', 0); - } const lineY = cdfChart.append('line') .attr('class', 'click-line-y') From ccc1e791c1c5972b8258705f7616f14aaf0dc469 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Thu, 3 Sep 2020 18:42:11 +0000 Subject: [PATCH 12/15] change colors of all states --- web/src/app/components/cdf/cdf.component.ts | 382 +----------------- web/src/app/components/cdf/cdf.utils.ts | 12 +- web/src/app/components/colors.ts | 51 +++ .../{duration.utils.ts => duration-utils.ts} | 0 4 files changed, 75 insertions(+), 370 deletions(-) create mode 100644 web/src/app/components/colors.ts rename web/src/app/components/{duration.utils.ts => duration-utils.ts} (100%) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 70e5079..560f592 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import {AfterViewChecked, AfterViewInit, Component, ElementRef, Input, ViewChild} from '@angular/core'; +import {AfterViewInit, Component, ElementRef, Input, ViewChild} from '@angular/core'; import * as d3 from 'd3'; import {step189_2020} from '../../../proto/step189_2020'; -import {findDurationUnit} from '../duration.utils'; +import {findDurationUnit} from '../duration-utils'; import {addCurrentPushLine, generateQuantiles, generateYPosition, getProbabilityForDuration, populateData} from './cdf.utils'; import {COMPLETED_BLUE, d3SVG, Item, STROKE_COLOR} from './cdf.utils'; @@ -29,22 +29,22 @@ import {COMPLETED_BLUE, d3SVG, Item, STROKE_COLOR} from './cdf.utils'; styleUrls: ['./cdf.component.scss'] }) -export class CDFComponent implements AfterViewChecked, AfterViewInit { +export class CDFComponent implements AfterViewInit { private static readonly NANO_TO_MINUTES: number = (10 ** 9) * 60; private static readonly STATE_TO_COLOR: { [index: number]: string } = { 1: '#eee', - 3: '#2196f3', - 4: '#d50000', - 5: '#34a853', - 6: '#d50000', - 7: '#2196f3', - 8: '#2196f3', - 9: '#d50000', - 10: '#2196f3', - 12: '#d50000', - 13: '#2196f3', - 15: '#2196f3', - 16: '#d50000' + 3: '#ba68c8', + 4: '#ff6e40', + 5: '#00bfa5', + 6: '#ff6e40', + 7: '#ba68c8', + 8: '#ba68c8', + 9: '#ff6e40', + 10: '#ba68c8', + 12: '#ff6e40', + 13: '#ba68c8', + 15: '#ba68c8', + 16: '#ff6e40' }; @ViewChild('cdf') private CDFContainer!: ElementRef; @@ -113,7 +113,7 @@ export class CDFComponent implements AfterViewChecked, AfterViewInit { return; } this.durationUnit = findDurationUnit(this.pushInfos); - this.data = populateData(this.pushInfos); + this.data = populateData(this.pushInfos, false); const element = this.CDFContainer.nativeElement; const elementWidth = element.clientWidth; @@ -209,353 +209,7 @@ export class CDFComponent implements AfterViewChecked, AfterViewInit { .attr('y', margin.top / 2) .attr('text-anchor', 'middle') .style('font-size', '16px') - .text('CDF of completed push durations'); - - cdfChart.datum(extendedData) - .append('path') - .attr('id', 'cdf-area') - .attr('fill', COMPLETED_BLUE) - .attr( - 'd', - d3.area() - .x(d => xScale(d.duration)) - .y1(d => yScale(d.probability)) - .y0(yScale(0)) - .curve(d3.curveStepAfter)); - - cdfChart.datum(extendedData) - .append('path') - .attr('fill', 'none') - .attr( - 'd', - d3.line() - .x(d => xScale(d.duration)) - .y(d => yScale(d.probability)) - .curve(d3.curveStepAfter)) - .attr('id', 'cdf-stroke') - .attr('stroke', STROKE_COLOR) - .attr('stroke-width', 2); - - const percentileLines = - this.svg.append('g') - .attr('id', 'percentile-lines') - .attr('transform', `translate(${margin.left}, ${margin.top})`); - - const percentiles = generateQuantiles(this.data, [0.1, 0.5, 0.9], xScale); - - percentileLines.selectAll('.percentile-lines') - .data(percentiles) - .enter() - .append('line') - .attr('class', 'percentile-line') - .attr('stroke', 'lightgrey') - .attr('stroke-dasharray', '5,2') - .attr('x1', (d: Item) => xScale(d.duration)) - .attr('y1', height) - .attr('x2', (d: Item) => xScale(d.duration)) - .attr('y2', 0); - - percentileLines.selectAll('.percentile-lines') - .data(percentiles) - .enter() - .append('text') - .attr('text-anchor', 'middle') - .attr('x', (d: Item) => xScale(d.duration)) - .attr('y', -10) - .attr('class', 'percentile-text') - .attr('font-size', '10px') - .text((d: Item) => `${d.probability * 100}%`); - - let radius = 2.5; - const xVals = this.data.map(d => d.duration); - let yPosition = generateYPosition(radius * 2 + 0.1, xScale, xVals); - - const maxYPosition = d3.max(yPosition); - if (!maxYPosition) { - return; - } - - if (maxYPosition > height) { - radius = 1.4; - yPosition = generateYPosition(radius * 2 + 0.1, xScale, xVals); - } - - const currentPushLine = - this.svg.append('g') - .attr('id', 'current-push-line') - .attr('transform', `translate(${margin.left}, ${margin.top})`); - - addCurrentPushLine( - this.currentPush, currentPushLine, this.data, height, xScale, yScale); - - // Mouse click - let startValue = minDuration + 1e-6; - - const clipRect = cdfChart.append('clipPath') - .attr('id', 'area-clip') - .append('rect') - .attr('class', 'area-clip-rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', xScale(startValue)) - .attr('height', height); - - cdfChart.datum(extendedData) - .append('path') - .attr('class', 'cdf-clipped') - .attr( - 'd', - d3.area() - .x(d => xScale(d.duration)) - .y1(d => yScale(d.probability)) - .y0(yScale(0)) - .curve(d3.curveStepAfter)) - .attr('fill-opacity', '0.6') - .attr('fill', 'white') - .attr('clip-path', 'url(#area-clip)'); - - for (let i = 0; i < this.data.length; i++) { - const cx = xScale(this.data[i].duration); - const cy = height - yPosition[i] - radius; - cdfChart.append('circle') - .attr('class', 'dots') - .attr('cx', cx) - .attr('r', radius) - .attr('cy', cy) - .attr('fill', 'black'); - } - - const lineY = - cdfChart.append('line') - .attr('class', 'click-line-y') - .attr('x1', xScale(startValue)) - .attr('x2', xScale(startValue)) - .attr( - 'y1', yScale(getProbabilityForDuration(this.data, startValue))) - .attr('y2', height) - .attr('stroke', 'black') - .attr('stroke-width', '1px') - .attr('opacity', 0); - - const lineX = - cdfChart.append('line') - .attr('class', 'click-line-x') - .attr('x1', 0) - .attr('x2', xScale(startValue)) - .attr( - 'y1', yScale(getProbabilityForDuration(this.data, startValue))) - .attr( - 'y2', yScale(getProbabilityForDuration(this.data, startValue))) - .attr('stroke', 'black') - .attr('stroke-width', '1px') - .attr('opacity', 0); - - cdfChart.append('text') - .attr('x', xScale(startValue) - 35) - .attr( - 'y', - yScale(getProbabilityForDuration(this.data, startValue) / 100) + 30) - .attr('class', 'click-line-y-text') - .attr('font-size', '10px') - .text(`${this.data.filter(c => c.duration <= startValue).length}/${ - this.data.length}`) - .attr('opacity', 0); - - cdfChart.append('text') - .attr('x', xScale(startValue) / 2) - .attr( - 'y', - yScale(getProbabilityForDuration(this.data, startValue) / 100) + 10) - .attr('class', 'click-line-x-text') - .attr('font-size', '10px') - .text(`${getProbabilityForDuration(this.data, startValue).toFixed(2)}%`) - .attr('opacity', 0); - - cdfChart.on('click', (d: unknown, i: number): void => { - if (!d) { - return; - } - const coordinates = d3.mouse(d3.event.currentTarget); - const xValue = xScale.invert(coordinates[0]); - if (xValue > minDuration) { - startValue = xValue; - const yValue = getProbabilityForDuration(d as Item[], startValue); - d3.select(d3.event.currentTarget) - .select('.area-clip-rect') - .attr('width', xScale(startValue)); - d3.select(d3.event.currentTarget) - .select('.click-line-y') - .attr('y1', yScale(yValue)) - .attr('x1', xScale(startValue)) - .attr('x2', xScale(startValue)) - .attr('opacity', 1); - d3.select(d3.event.currentTarget) - .select('.click-line-x') - .attr('y1', yScale(yValue)) - .attr('y2', yScale(yValue)) - .attr('x2', xScale(startValue)) - .attr('opacity', 1); - d3.select(d3.event.currentTarget) - .select('.click-line-y-text') - .attr('x', xScale(startValue) - 40) - .attr('y', yScale(yValue) + 20) - .text(`${this.data.filter(c => c.duration <= startValue).length}/${ - this.data.length}`) - .attr('opacity', 1); - d3.select(d3.event.currentTarget) - .select('.click-line-x-text') - .attr('x', xScale(startValue) / 2) - .attr('y', yScale(yValue) + 10) - .text(`${ - (getProbabilityForDuration(this.data, startValue) * 100) - .toFixed(1)}%`) - .attr('opacity', 1); - d3.select(d3.event.currentTarget) - .selectAll('.dots') - .data(this.data) - .attr( - 'fill', - (dp: Item) => dp.duration <= startValue ? 'grey' : 'black'); - } - }); - - // hover - const highlightColor = '#b9edc4'; - const strokeColor = '#167364'; - const circleColor = '#a0aade'; - const labelsize = [40, 25]; - const labelFontSize = labelsize[1] / 2; - - cdfChart.append('rect') - .attr('class', 'x-label-bg') - .attr('x', 0) - .attr('y', height) - .attr('height', labelsize[1]) - .attr('width', labelsize[0]) - .attr('fill', highlightColor) - .attr('opacity', 0); - - cdfChart.append('text') - .attr('text-anchor', 'middle') - .attr('class', 'x-label') - .attr('x', 0) - .attr('y', height + 5 + labelFontSize) - .attr('opacity', 0) - .style('font-size', `${labelFontSize}px`) - .style('font-weight', 'bold') - .attr('fill', 'black'); - - cdfChart.append('rect') - .attr('class', 'y-label-bg') - .attr('x', -labelsize[0]) - .attr('y', 0) - .attr('height', labelsize[1]) - .attr('width', labelsize[0]) - .attr('fill', highlightColor) - .attr('opacity', 0); - - cdfChart.append('text') - .attr('text-anchor', 'middle') - .attr('class', 'y-label') - .attr('x', -labelsize[0] / 2) - .attr('y', 0) - .attr('opacity', 0) - .style('font-weight', 'bold') - .attr('fill', 'black') - .style('font-size', `${labelFontSize}px`); - - cdfChart.append('line') - .attr('x1', 0) - .attr('x2', 0) - .attr('y1', height) - .attr('y2', 0) - .attr('class', 'v-ruler') - .attr('stroke', 'grey') - .attr('stroke-width', 2) - .attr('stroke-dasharray', '5,2') - .attr('opacity', 0); - - cdfChart.append('line') - .attr('x1', 0) - .attr('x2', width) - .attr('y1', 0) - .attr('y2', 0) - .attr('class', 'h-ruler') - .attr('stroke', 'grey') - .attr('stroke-width', 2) - .attr('stroke-dasharray', '5,2') - .attr('opacity', 0); - - cdfChart.append('circle') - .attr('class', 'marker') - .attr('cx', 0) - .attr('cy', 0) - .attr('r', 5) - .attr('stroke', strokeColor) - .attr('stroke-width', 2) - .attr('fill', circleColor) - .style('opacity', 0); - - cdfChart.on('mousemove', (d: unknown, i: number): void => { - const mouseX = xScale.invert(d3.mouse(d3.event.currentTarget)[0]); - const vruler = d3.select('.v-ruler'); - const hruler = d3.select('.h-ruler'); - const marker = d3.select('.marker'); - const xlabel = d3.select('.x-label'); - const xlabelbg = d3.select('.x-label-bg'); - const ylabel = d3.select('.y-label'); - const ylabelbg = d3.select('.y-label-bg'); - const checkX = (mouseX >= minDuration) && (mouseX <= maxExtendedDuration); - - if (checkX) { - const xVal = d3.mouse(d3.event.currentTarget)[0]; - const xInverted = xScale.invert(xVal); - const yVal = yScale(getProbabilityForDuration(d as Item[], xInverted)); - - marker.attr('cx', xVal).attr('cy', yVal); - hruler.attr('y1', yVal).attr('y2', yVal); - vruler.attr('x1', xVal).attr('x2', xVal); - - const xText = d3.format(',.1f')(xInverted); - const yText = d3.format(',.1%')(yScale.invert(yVal)); - xlabel.attr('x', +marker.attr('cx')).text(xText); - xlabelbg.attr('x', +marker.attr('cx') - labelsize[0] / 2); - ylabel.attr('y', +marker.attr('cy') + labelsize[1] / 5).text(yText); - ylabelbg.attr('y', +marker.attr('cy') - labelsize[1] / 2); - - marker.style('opacity', 1); - hruler.style('opacity', 1); - vruler.style('opacity', 1); - xlabel.style('opacity', 1); - xlabelbg.style('opacity', 1); - ylabel.style('opacity', 1); - ylabelbg.style('opacity', 1); - } else { - marker.style('opacity', 0); - hruler.style('opacity', 0); - vruler.style('opacity', 0); - xlabel.style('opacity', 0); - xlabelbg.style('opacity', 0); - ylabel.style('opacity', 0); - ylabelbg.style('opacity', 0); - } - }); - - cdfChart.on('mouseleave', (d: unknown, i: number): void => { - const vruler = d3.select('.v-ruler').style('opacity', 0); - const hruler = d3.select('.h-ruler').style('opacity', 0); - const marker = d3.select('.marker').style('opacity', 0); - const xlabel = d3.select('.x-label').style('opacity', 0); - const xlabelbg = d3.select('.x-label-bg').style('opacity', 0); - const ylabel = d3.select('.y-label').style('opacity', 0); - const ylabelbg = d3.select('.y-label-bg').style('opacity', 0); - }); - .attr('class', 'graph-title') - .attr('x', elementWidth / 2) - .attr('y', margin.top / 2) - .attr('text-anchor', 'middle') - .style('font-size', '16px') - .text('CDF of push durations'); + .text('CDF of push durations'); const rectHeight = Math.floor(height / this.data.length) + 1; console.log(rectHeight) @@ -564,8 +218,6 @@ export class CDFComponent implements AfterViewChecked, AfterViewInit { cdfChart.append('rect') .attr('class', 'rect-area') .attr('fill', CDFComponent.STATE_TO_COLOR[elem.endState]) - //.attr('stroke', 'none') hi - //.attr('stroke-width', 0.5) .attr('x', xScale(elem.duration)) .attr('y', yScale(elem.probability)) .attr('height', rectHeight) diff --git a/web/src/app/components/cdf/cdf.utils.ts b/web/src/app/components/cdf/cdf.utils.ts index f3e1a79..bad645d 100644 --- a/web/src/app/components/cdf/cdf.utils.ts +++ b/web/src/app/components/cdf/cdf.utils.ts @@ -17,7 +17,7 @@ import * as d3 from 'd3'; import {step189_2020} from '../../../proto/step189_2020'; -import {DurationItem, findDuration, findDurationUnit, UNIT_CONVERSION} from '../duration.utils'; +import {DurationItem, findDuration, findDurationUnit, UNIT_CONVERSION} from '../duration-utils'; export interface Item { duration: number; // Time between last stage and first non-empty stage @@ -52,9 +52,9 @@ export const STROKE_COLOR = '#167364'; * @param pushInfos Array of pushes for a single push def * @return Array of Items sorted by increasing duration */ -export function populateData(pushInfos: step189_2020.IPushInfo[]): Item[] { +export function populateData(pushInfos: step189_2020.IPushInfo[], completedBool: boolean): Item[] { const divisor = UNIT_CONVERSION[findDurationUnit(pushInfos)]; - const pushes: Item[] = []; + let pushes: Item[] = []; pushInfos.forEach(pushInfo => { if (!pushInfo) { return; @@ -81,9 +81,11 @@ export function populateData(pushInfos: step189_2020.IPushInfo[]): Item[] { } as Item); } }); - const completed = pushes.filter(d => d.endState === 5); + if (completedBool) { + pushes = pushes.filter(d => d.endState === 5); + } const sortedArray: Item[] = - completed.sort((n1, n2) => n1.duration - n2.duration); + pushes.sort((n1, n2) => n1.duration - n2.duration); const data: Item[] = []; const durationLength = sortedArray.length; diff --git a/web/src/app/components/colors.ts b/web/src/app/components/colors.ts new file mode 100644 index 0000000..63a8672 --- /dev/null +++ b/web/src/app/components/colors.ts @@ -0,0 +1,51 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const DARK_GRAY = '#a9a9a9'; +export const MED_GRAY = '#d3d3d3'; +export const LIGHT_GRAY = '#eee'; + +const BLUE = '#2196f3'; +const GREEN = '#34a853'; +const ORANGE = '#f9ab00'; +const RED = '#d50000'; + +// Mapping from states to corresponding colors. +export const STATE_TO_COLOR: {[index: number]: string} = { + 1: LIGHT_GRAY, + 3: BLUE, + 4: RED, + 5: GREEN, + 6: RED, + 7: BLUE, + 8: BLUE, + 9: RED, + 10: BLUE, + 11: BLUE, + 12: RED, + 13: BLUE, + 14: LIGHT_GRAY, + 15: BLUE, + 16: RED, + 17: LIGHT_GRAY, + 18: RED, + 19: LIGHT_GRAY, + 20: LIGHT_GRAY, + 21: ORANGE, + 24: ORANGE, + 25: ORANGE, + 29: RED +}; \ No newline at end of file diff --git a/web/src/app/components/duration.utils.ts b/web/src/app/components/duration-utils.ts similarity index 100% rename from web/src/app/components/duration.utils.ts rename to web/src/app/components/duration-utils.ts From c7d15530b36774f326ff55660106c0e804209062 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Thu, 3 Sep 2020 18:44:30 +0000 Subject: [PATCH 13/15] fix lint --- web/src/app/app.module.ts | 2 +- web/src/app/components/cdf/cdf.component.ts | 17 ++++++++--------- web/src/app/components/cdf/cdf.utils.ts | 5 +++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/web/src/app/app.module.ts b/web/src/app/app.module.ts index fc92405..cc1d516 100644 --- a/web/src/app/app.module.ts +++ b/web/src/app/app.module.ts @@ -32,4 +32,4 @@ import {DateNsecPipe} from './pipes/date-nsec.pipe'; bootstrap: [AppComponent] }) export class AppModule { -} \ No newline at end of file +} diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index 560f592..d5ad938 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -31,7 +31,7 @@ import {COMPLETED_BLUE, d3SVG, Item, STROKE_COLOR} from './cdf.utils'; export class CDFComponent implements AfterViewInit { private static readonly NANO_TO_MINUTES: number = (10 ** 9) * 60; - private static readonly STATE_TO_COLOR: { [index: number]: string } = { + private static readonly STATE_TO_COLOR: {[index: number]: string} = { 1: '#eee', 3: '#ba68c8', 4: '#ff6e40', @@ -212,17 +212,16 @@ export class CDFComponent implements AfterViewInit { .text('CDF of push durations'); const rectHeight = Math.floor(height / this.data.length) + 1; - console.log(rectHeight) for (let i = this.data.length - 1; i >= 0; i--) { const elem = this.data[i]; cdfChart.append('rect') - .attr('class', 'rect-area') - .attr('fill', CDFComponent.STATE_TO_COLOR[elem.endState]) - .attr('x', xScale(elem.duration)) - .attr('y', yScale(elem.probability)) - .attr('height', rectHeight) - .attr('width', width - xScale(elem.duration)) - .attr('opacity', 1); + .attr('class', 'rect-area') + .attr('fill', CDFComponent.STATE_TO_COLOR[elem.endState]) + .attr('x', xScale(elem.duration)) + .attr('y', yScale(elem.probability)) + .attr('height', rectHeight) + .attr('width', width - xScale(elem.duration)) + .attr('opacity', 1); } } } diff --git a/web/src/app/components/cdf/cdf.utils.ts b/web/src/app/components/cdf/cdf.utils.ts index bad645d..96f31c2 100644 --- a/web/src/app/components/cdf/cdf.utils.ts +++ b/web/src/app/components/cdf/cdf.utils.ts @@ -52,7 +52,8 @@ export const STROKE_COLOR = '#167364'; * @param pushInfos Array of pushes for a single push def * @return Array of Items sorted by increasing duration */ -export function populateData(pushInfos: step189_2020.IPushInfo[], completedBool: boolean): Item[] { +export function populateData( + pushInfos: step189_2020.IPushInfo[], completedBool: boolean): Item[] { const divisor = UNIT_CONVERSION[findDurationUnit(pushInfos)]; let pushes: Item[] = []; pushInfos.forEach(pushInfo => { @@ -85,7 +86,7 @@ export function populateData(pushInfos: step189_2020.IPushInfo[], completedBool: pushes = pushes.filter(d => d.endState === 5); } const sortedArray: Item[] = - pushes.sort((n1, n2) => n1.duration - n2.duration); + pushes.sort((n1, n2) => n1.duration - n2.duration); const data: Item[] = []; const durationLength = sortedArray.length; From 96982af4cc4c666b0cb92a4bc04dae04f96ff128 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Thu, 3 Sep 2020 18:57:21 +0000 Subject: [PATCH 14/15] fix issue --- web/src/app/components/cdf/cdf.component.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/web/src/app/components/cdf/cdf.component.ts b/web/src/app/components/cdf/cdf.component.ts index f3573e3..4c7c84d 100644 --- a/web/src/app/components/cdf/cdf.component.ts +++ b/web/src/app/components/cdf/cdf.component.ts @@ -57,20 +57,6 @@ export class CDFComponent implements AfterViewInit { private durationUnit = ''; private showDotsBoolean = false; - ngAfterViewChecked(): void { - if (this.showDotsBoolean === this.showDots) { - return; - } - if (!this.svg) { - return; - } - this.showDotsBoolean = this.showDots; - if (!this.showDotsBoolean) { - this.svg.select('#cdf-chart').selectAll('.dots').attr('opacity', 0); - } else { - this.svg.select('#cdf-chart').selectAll('.dots').attr('opacity', 1); - } - } /** * Creates a CDF chart by plotting the duration of completed pushes against * the probability of a push taking less time than that duration. Adds lines @@ -167,12 +153,6 @@ export class CDFComponent implements AfterViewInit { return; } - - const maxExtendedDuration = d3.max(extendedData, d => d.duration); - if (!maxExtendedDuration) { - return; - } - this.svg = (d3.select(element).append('svg') as d3SVG) .attr('width', elementWidth) .attr('height', elementHeight); From 523788e84353dc2787d3e8676b6daf4eea1ed438 Mon Sep 17 00:00:00 2001 From: Carol Sun Date: Thu, 3 Sep 2020 19:05:10 +0000 Subject: [PATCH 15/15] remove slider --- web/src/app/pages/one-push/one-push.component.html | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/app/pages/one-push/one-push.component.html b/web/src/app/pages/one-push/one-push.component.html index 32c79b4..13ea3f5 100644 --- a/web/src/app/pages/one-push/one-push.component.html +++ b/web/src/app/pages/one-push/one-push.component.html @@ -13,7 +13,6 @@

-Show dots