diff --git a/frontend/packages/core/components/ScalarsPage/ScalarChart.tsx b/frontend/packages/core/components/ScalarsPage/ScalarChart.tsx index e49f83a58867c9a847526fe7caa5b1c61a7d52d5..d6c6de829d521d6496e3f2af74a6aebf8c10ee90 100644 --- a/frontend/packages/core/components/ScalarsPage/ScalarChart.tsx +++ b/frontend/packages/core/components/ScalarsPage/ScalarChart.tsx @@ -1,6 +1,7 @@ +import LineChart, {LineChartRef} from '~/components/LineChart'; import { - Dataset, Range, + ScalarDataset, SortingMethod, XAxis, chartData, @@ -13,7 +14,6 @@ import { transform, xAxisMap } from '~/resource/scalars'; -import LineChart, {LineChartRef} from '~/components/LineChart'; import React, {FunctionComponent, useCallback, useMemo, useRef, useState} from 'react'; import {rem, size} from '~/utils/style'; @@ -22,6 +22,7 @@ import {EChartOption} from 'echarts'; import {Run} from '~/types'; import {cycleFetcher} from '~/utils/fetch'; import ee from '~/utils/event'; +import {format} from 'd3-format'; import queryString from 'query-string'; import styled from 'styled-components'; import useHeavyWork from '~/hooks/useHeavyWork'; @@ -110,7 +111,7 @@ const ScalarChart: FunctionComponent = ({ const echart = useRef(null); - const {data: datasets, error, loading} = useRunningRequest<(Dataset | null)[]>( + const {data: datasets, error, loading} = useRunningRequest<(ScalarDataset | null)[]>( runs.map(run => `/scalars/list?${queryString.stringify({run: run.label, tag})}`), !!running, (...urls) => cycleFetcher(urls) @@ -171,15 +172,20 @@ const ScalarChart: FunctionComponent = ({ [smoothedDatasets, runs, xAxis] ); + const maxStepLength = useMemo( + () => String(Math.max(...smoothedDatasets.map(i => Math.max(...i.map(j => j[1]))))).length, + [smoothedDatasets] + ); + const formatter = useCallback( (params: EChartOption.Tooltip.Format | EChartOption.Tooltip.Format[]) => { const data = Array.isArray(params) ? params[0].data : params.data; const step = data[1]; const points = nearestPoint(smoothedDatasets ?? [], runs, step); const sort = sortingMethodMap[sortingMethod]; - return tooltip(sort ? sort(points, data) : points, i18n); + return tooltip(sort ? sort(points, data) : points, maxStepLength, i18n); }, - [smoothedDatasets, runs, sortingMethod, i18n] + [smoothedDatasets, runs, sortingMethod, maxStepLength, i18n] ); const options = useMemo( @@ -191,7 +197,13 @@ const ScalarChart: FunctionComponent = ({ }, xAxis: { type: xAxisType, - ...ranges.x + ...ranges.x, + axisPointer: { + label: { + formatter: + xAxisType === XAxisType.time ? undefined : ({value}: {value: number}) => format('.8')(value) + } + } }, yAxis: { type: yAxisType, diff --git a/frontend/packages/core/resource/scalars/chart.ts b/frontend/packages/core/resource/scalars/chart.ts index 4998472cd994105c33d2f1da36540185f5bfb5b7..a102fe2b9e45a2023bdf667bd682698463e736a3 100644 --- a/frontend/packages/core/resource/scalars/chart.ts +++ b/frontend/packages/core/resource/scalars/chart.ts @@ -65,7 +65,7 @@ export const chartData = ({data, runs, xAxis}: {data: Dataset[]; runs: Run[]; xA .flat(); // TODO: make it better, don't concat html -export const tooltip = (data: TooltipData[], i18n: I18n) => { +export const tooltip = (data: TooltipData[], stepLength: number, i18n: I18n) => { const indexPropMap = { time: 0, step: 1, @@ -73,10 +73,10 @@ export const tooltip = (data: TooltipData[], i18n: I18n) => { smoothed: 3, relative: 4 } as const; - const widthPropMap = { - run: [60, 180] as [number, number], + const widthPropMap: Record = { + run: [60, 180], time: 150, - step: 40, + step: Math.max(stepLength * 8, 40), value: 60, smoothed: 70, relative: 60 @@ -103,7 +103,7 @@ export const tooltip = (data: TooltipData[], i18n: I18n) => { } as const; }); - const renderContent = (content: string, width: number | [number, number]) => + const renderContent = (content: string, width: number | readonly [number, number]) => `
, showing: bool) -> Self { + pub fn new(name: &str, value: Vec, showing: bool) -> Self { Point { - name, + name: String::from(name), value, showing, } @@ -23,25 +23,27 @@ impl DividedPoints { } pub fn divide( - points: Vec>, - labels: Vec, + points: &Vec>, + labels: &Vec, visibility: bool, - keyword: String, + keyword: &str, ) -> DividedPoints { let mut matched: Vec = vec![]; let mut missing: Vec = vec![]; for (i, point) in points.iter().enumerate() { - let mut name: String = String::from(""); + let mut name: &str = ""; let ptr: *const String = &labels[i]; if !ptr.is_null() { - name = labels[i].clone(); + unsafe { + name = &(*ptr)[..]; + } } - let point_with_label: Point = Point::new(name.clone(), point.to_vec(), visibility); - if keyword == String::from("") { + let point_with_label: Point = Point::new(name, point.to_vec(), visibility); + if let "" = keyword { missing.push(point_with_label); } else { - if name.contains(&keyword) { + if String::from(name).contains(&keyword) { matched.push(point_with_label); } else { missing.push(point_with_label); diff --git a/frontend/packages/wasm/src/histogram.rs b/frontend/packages/wasm/src/histogram.rs index 499c03b186810c27ad2b860a57f686840ff9fa76..48e66a7ce24ce731a1166104dc97922bf242728d 100644 --- a/frontend/packages/wasm/src/histogram.rs +++ b/frontend/packages/wasm/src/histogram.rs @@ -1,5 +1,3 @@ -use std::f64; - trait FloatIterExt { fn float_min(&mut self) -> f64; fn float_max(&mut self) -> f64; @@ -108,7 +106,7 @@ fn compute_histogram( return result; } -pub fn transform_overlay(data: Vec) -> Overlay { +pub fn transform_overlay(data: &Vec) -> Overlay { struct Temp { time: f64, step: f64, @@ -154,8 +152,8 @@ pub fn transform_overlay(data: Vec) -> Overlay { }; } -pub fn transform_offset(data: Vec) -> Offset { - let overlay: Overlay = transform_overlay(data); +pub fn transform_offset(data: &Vec) -> Offset { + let overlay: Overlay = transform_overlay(&data); let mut min_step: f64 = std::f64::INFINITY; let mut max_step: f64 = std::f64::NEG_INFINITY; let mut min_z: f64 = std::f64::INFINITY; diff --git a/frontend/packages/wasm/src/main.rs b/frontend/packages/wasm/src/main.rs index dccd5f551880bd017b8dce7155b1f90996058874..ce84b245d72c9d1e3da96a639b983b07b46d2b4f 100644 --- a/frontend/packages/wasm/src/main.rs +++ b/frontend/packages/wasm/src/main.rs @@ -14,31 +14,33 @@ pub fn main() {} pub fn scalar_transform(js_datasets: &JsValue, smoothing: f64) -> JsValue { utils::set_panic_hook(); let datasets: Vec> = js_datasets.into_serde().unwrap(); - let result: Vec> = scalar::transform(datasets, smoothing); - return JsValue::from_serde(&result).unwrap(); + let result: Vec> = scalar::transform(&datasets, smoothing); + JsValue::from_serde(&result).unwrap() } #[wasm_bindgen] pub fn scalar_range(js_datasets: &JsValue, outlier: bool) -> JsValue { utils::set_panic_hook(); let datasets: Vec> = js_datasets.into_serde().unwrap(); - let result = scalar::range(datasets, outlier); - return JsValue::from_serde(&result).unwrap(); + let result = scalar::range(&datasets, outlier); + JsValue::from_serde(&result).unwrap() } #[wasm_bindgen] -pub fn histogram_transform(js_data: &JsValue, mode: String) -> JsValue { +pub fn histogram_transform(js_data: &JsValue, mode: &str) -> JsValue { utils::set_panic_hook(); let data: Vec = js_data.into_serde().unwrap(); - if mode == String::from("overlay") { - let result = histogram::transform_overlay(data); - return JsValue::from_serde(&result).unwrap(); + match mode { + "overlay" => { + let result = histogram::transform_overlay(&data); + JsValue::from_serde(&result).unwrap() + } + "offset" => { + let result = histogram::transform_offset(&data); + JsValue::from_serde(&result).unwrap() + } + _ => JsValue::null(), } - if mode == String::from("offset") { - let result = histogram::transform_offset(data); - return JsValue::from_serde(&result).unwrap(); - } - return JsValue::null(); } #[wasm_bindgen] @@ -46,12 +48,12 @@ pub fn high_dimensional_divide( js_points: &JsValue, js_labels: &JsValue, visibility: bool, - keyword: String, + keyword: &str, ) -> JsValue { utils::set_panic_hook(); let points: Vec> = js_points.into_serde().unwrap(); let labels: Vec = js_labels.into_serde().unwrap(); let result: high_dimensional::DividedPoints = - high_dimensional::divide(points, labels, visibility, keyword); - return JsValue::from_serde(&result).unwrap(); + high_dimensional::divide(&points, &labels, visibility, keyword); + JsValue::from_serde(&result).unwrap() } diff --git a/frontend/packages/wasm/src/scalar.rs b/frontend/packages/wasm/src/scalar.rs index ddadebf0267c48fc5e0f3bdb740ea6d865cdba43..61f91de8c9d636b00be1e8014c92b08f74e738ee 100644 --- a/frontend/packages/wasm/src/scalar.rs +++ b/frontend/packages/wasm/src/scalar.rs @@ -15,7 +15,7 @@ impl Range { } } -fn quantile(values: Vec, p: f64) -> f64 { +fn quantile(values: &Vec, p: f64) -> f64 { let n: usize = values.len(); if n == 0 { return std::f64::NAN; @@ -33,7 +33,7 @@ fn quantile(values: Vec, p: f64) -> f64 { return value0 + (value1 - value0) * (i - (i0 as f64)); } -pub fn transform(datasets: Vec>, smoothing: f64) -> Vec> { +pub fn transform(datasets: &Vec>, smoothing: f64) -> Vec> { let mut result: Vec> = vec![]; for dataset in datasets.iter() { let mut row: Vec = vec![]; @@ -72,7 +72,7 @@ pub fn transform(datasets: Vec>, smoothing: f64) -> Vec>, outlier: bool) -> Range { +pub fn range(datasets: &Vec>, outlier: bool) -> Range { let mut ranges: Vec = vec![]; for data in datasets.iter() { @@ -90,8 +90,8 @@ pub fn range(datasets: Vec>, outlier: bool) -> Range { ranges.push(Range::new(sorted[0], sorted[n - 1])); } else { ranges.push(Range::new( - quantile(sorted, 0.05_f64), - quantile(values, 0.95), + quantile(&sorted, 0.05_f64), + quantile(&values, 0.95), )); } }