Skip to content

Commit

Permalink
feat: color map of high-dimensional page (#950)
Browse files Browse the repository at this point in the history
* feat: show more menu in navbar

* feat: able to drag and change run sidebar width

* feat: color map of high-dimensional page

* fix: disable color map when category is more than color size
  • Loading branch information
PeterPanZH authored Apr 9, 2021
1 parent 6da5a64 commit 858d698
Show file tree
Hide file tree
Showing 26 changed files with 408 additions and 147 deletions.
3 changes: 3 additions & 0 deletions frontend/packages/core/public/icons/a.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/packages/core/public/icons/chevron-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/packages/core/public/icons/chevron-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/packages/core/public/icons/chevron-up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/packages/core/public/icons/reset.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/packages/core/public/icons/selection.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions frontend/packages/core/public/icons/three-d.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"3d-label": "Enable/disable 3D labels mode",
"3d-label": "Show/Hide data detail",
"component": "Component #{{index}}",
"continue": "Resume",
"data": "Data",
Expand All @@ -22,6 +22,7 @@
},
"matched-result-count": "{{count}} matched.",
"neighbors": "Neighbors",
"no-color-map": "No color map",
"pause": "Pause",
"perplexity": "Perplexity",
"points": "Points",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"3d-label": "开启/关闭3D数据标签",
"3d-label": "显示/隐藏数据详情",
"component": "主成分{{index}}",
"continue": "继续",
"data": "数据",
Expand All @@ -22,6 +22,7 @@
},
"matched-result-count": "匹配结果 {{count}}",
"neighbors": "相邻数据点数量",
"no-color-map": "无颜色",
"pause": "暂停",
"perplexity": "困惑度",
"points": "数据点",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,8 @@ const Operations = styled.div`
}
}
&.three-d {
font-size: ${rem(20)};
}
&:hover {
&:hover,
&.active {
color: var(--primary-focused-color);
}
Expand All @@ -69,11 +66,12 @@ const Operations = styled.div`
`;

type ChartOperationsProps = {
labelCloud: boolean;
onToggleLabelCloud?: () => unknown;
onReset?: () => unknown;
};

const ChartOperations: FunctionComponent<ChartOperationsProps> = ({onToggleLabelCloud, onReset}) => {
const ChartOperations: FunctionComponent<ChartOperationsProps> = ({labelCloud, onToggleLabelCloud, onReset}) => {
const {t} = useTranslation('high-dimensional');

return (
Expand All @@ -86,9 +84,9 @@ const ChartOperations: FunctionComponent<ChartOperationsProps> = ({onToggleLabel
</a>
</Tippy> */}
<Tippy content={t('high-dimensional:3d-label')} placement="bottom" theme="tooltip">
<a className="three-d" onClick={() => onToggleLabelCloud?.()}>
<a onClick={() => onToggleLabelCloud?.()} className={labelCloud ? 'active' : ''}>
<span>
<Icon type="three-d" />
<Icon type="a" />
</span>
</a>
</Tippy>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const Chart = styled.div`
type HighDimensionalChartProps = {
vectors: Float32Array;
labels: string[];
colorMap?: ScatterChartProps['colorMap'];
shape: Shape;
dim: number;
is3D: boolean;
Expand All @@ -94,6 +95,7 @@ const HighDimensionalChart = React.forwardRef<HighDimensionalChartRef, HighDimen
{
vectors,
labels,
colorMap,
shape,
dim,
is3D,
Expand Down Expand Up @@ -265,6 +267,7 @@ const HighDimensionalChart = React.forwardRef<HighDimensionalChartRef, HighDimen
{shape[1]}
</div>
<ChartOperations
labelCloud={showLabelCloud}
onToggleLabelCloud={() => setShowLabelCloud(s => !s)}
onReset={() => chart.current?.reset()}
/>
Expand All @@ -276,6 +279,7 @@ const HighDimensionalChart = React.forwardRef<HighDimensionalChartRef, HighDimen
height={height}
data={data?.vectors ?? []}
labels={labels}
colorMap={colorMap}
is3D={is3D}
rotate={reduction !== 'tsne'}
focusedIndices={focusedIndices}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
*/

import React, {FunctionComponent, useEffect, useState} from 'react';
import Select, {SelectProps} from '~/components/Select';
import type {SelectListItem, SelectProps} from '~/components/Select';

import SearchInput from '~/components/SearchInput';
import Select from '~/components/Select';
import {rem} from '~/utils/style';
import styled from 'styled-components';
import useSearchValue from '~/hooks/useSearchValue';
Expand All @@ -27,7 +28,7 @@ const Wrapper = styled.div`
display: flex;
`;

const LabelSelect = styled<React.FunctionComponent<SelectProps<string>>>(Select)`
const LabelSelect = styled<React.FunctionComponent<SelectProps<number>>>(Select)`
width: 45%;
min-width: ${rem(80)};
max-width: ${rem(200)};
Expand All @@ -44,43 +45,34 @@ const LabelInput = styled(SearchInput)`
`;

type LabelSearchResult = {
labelBy: string | undefined;
labelIndex: number | undefined;
value: string;
};

export type LabelSearchInputProps = {
labels: string[];
labels: SelectListItem<number>[];
onChange?: (result: LabelSearchResult) => unknown;
};

const LabelSearchInput: FunctionComponent<LabelSearchInputProps> = ({labels, onChange}) => {
const [labelBy, setLabelBy] = useState<string | undefined>(labels[0] ?? undefined);
const [labelIndex, setLabelIndex] = useState<number>(0);
const [value, setValue] = useState('');
useEffect(() => {
if (labels.length) {
setLabelBy(label => {
if (label && labels.includes(label)) {
return label;
}
return labels[0];
});
} else {
setLabelBy(undefined);
}
setLabelIndex(0);
}, [labels]);

const debouncedValue = useSearchValue(value);

useEffect(() => {
onChange?.({
labelBy,
labelIndex,
value: debouncedValue
});
}, [labelBy, onChange, debouncedValue]);
}, [labelIndex, onChange, debouncedValue]);

return (
<Wrapper>
<LabelSelect list={labels} value={labelBy} onChange={setLabelBy} />
<LabelSelect list={labels} value={labelIndex} onChange={setLabelIndex} />
<LabelInput value={value} onChange={setValue} />
</Wrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
* limitations under the License.
*/

import type {ColorMap, Point3D} from './types';
import React, {useEffect, useImperativeHandle, useRef} from 'react';

import type Chart from './ScatterChart';
import type {ScatterChartOptions as ChartOptions} from './ScatterChart';
import LabelChart from './Labels';
import type {Point3D} from './types';
import PointChart from './Points';
import type {WithStyled} from '~/utils/style';
import styled from 'styled-components';
Expand All @@ -37,6 +37,7 @@ export type ScatterChartProps = {
height: number;
data: Point3D[];
labels: string[];
colorMap?: ColorMap | null;
is3D: boolean;
rotate?: boolean;
focusedIndices?: number[];
Expand All @@ -49,7 +50,7 @@ export type ScatterChartRef = {
};

const ScatterChart = React.forwardRef<ScatterChartRef, ScatterChartProps & WithStyled>(
({width, height, data, labels, is3D, rotate, focusedIndices, highlightIndices, type, className}, ref) => {
({width, height, data, labels, colorMap, is3D, rotate, focusedIndices, highlightIndices, type, className}, ref) => {
const theme = useTheme();

const element = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -84,8 +85,8 @@ const ScatterChart = React.forwardRef<ScatterChartRef, ScatterChartProps & WithS
}, [is3D, rotate]);

useEffect(() => {
chart.current?.setData(data, labels);
}, [data, labels, type]);
chart.current?.setData(data, labels, colorMap);
}, [data, labels, colorMap, type]);

useEffect(() => {
chart.current?.setFocusedPointIndices(focusedIndices ?? []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ export default class LabelScatterChart extends ScatterChart {
return this.mesh;
}

get defaultColor() {
return LabelScatterChart.LABEL_BACKGROUND_COLOR_DEFAULT;
}
get hoveredColor() {
return LabelScatterChart.LABEL_BACKGROUND_COLOR_HOVER;
}
get focusedColor() {
return LabelScatterChart.LABEL_BACKGROUND_COLOR_FOCUS;
}
get highLightColor() {
return LabelScatterChart.LABEL_BACKGROUND_COLOR_HIGHLIGHT;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected createShaderUniforms(picking: boolean): Record<string, THREE.IUniform<any>> {
return {
Expand Down Expand Up @@ -154,16 +167,7 @@ export default class LabelScatterChart extends ScatterChart {
const count = this.dataCount;
const colors = new Float32Array(count * VERTEX_COUNT_PER_LABEL * 3);
for (let i = 0; i < count; i++) {
let color: THREE.Color;
if (this.hoveredDataIndices.includes(i)) {
color = LabelScatterChart.LABEL_BACKGROUND_COLOR_HOVER;
} else if (this.focusedDataIndices.includes(i)) {
color = LabelScatterChart.LABEL_BACKGROUND_COLOR_FOCUS;
} else if (this.highLightDataIndices.includes(i)) {
color = LabelScatterChart.LABEL_BACKGROUND_COLOR_HIGHLIGHT;
} else {
color = LabelScatterChart.LABEL_BACKGROUND_COLOR_DEFAULT;
}
const color = this.getColorByIndex(i);
for (let j = 0; j < VERTEX_COUNT_PER_LABEL; j++) {
colors[i * VERTEX_COUNT_PER_LABEL * 3 + j * 3] = color.r;
colors[i * VERTEX_COUNT_PER_LABEL * 3 + j * 3 + 1] = color.g;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type {ScatterChartOptions} from '../ScatterChart';
import fragmentShader from './fragment.glsl';
import vertexShader from './vertex.glsl';

export default class PointScatter extends ScatterChart {
export default class PointScatterChart extends ScatterChart {
static readonly NUM_POINTS_FOG_THRESHOLD = 5000;

static readonly POINT_COLOR_DEFAULT = new THREE.Color(0x7e7e7e);
Expand All @@ -50,6 +50,19 @@ export default class PointScatter extends ScatterChart {
return this.points;
}

get defaultColor() {
return PointScatterChart.POINT_COLOR_DEFAULT;
}
get hoveredColor() {
return PointScatterChart.POINT_COLOR_HOVER;
}
get focusedColor() {
return PointScatterChart.POINT_COLOR_FOCUS;
}
get highLightColor() {
return PointScatterChart.POINT_COLOR_HIGHLIGHT;
}

constructor(container: HTMLElement, options: ScatterChartOptions) {
super(container, options);
this.label = new ScatterChartLabel(this.container, {
Expand Down Expand Up @@ -82,25 +95,11 @@ export default class PointScatter extends ScatterChart {
private convertPointsColor() {
const count = this.dataCount;
const colors = new Float32Array(count * 3);
let dst = 0;
for (let i = 0; i < count; i++) {
if (this.hoveredDataIndices.includes(i)) {
colors[dst++] = PointScatter.POINT_COLOR_HOVER.r;
colors[dst++] = PointScatter.POINT_COLOR_HOVER.g;
colors[dst++] = PointScatter.POINT_COLOR_HOVER.b;
} else if (this.focusedDataIndices.includes(i)) {
colors[dst++] = PointScatter.POINT_COLOR_FOCUS.r;
colors[dst++] = PointScatter.POINT_COLOR_FOCUS.g;
colors[dst++] = PointScatter.POINT_COLOR_FOCUS.b;
} else if (this.highLightDataIndices.includes(i)) {
colors[dst++] = PointScatter.POINT_COLOR_HIGHLIGHT.r;
colors[dst++] = PointScatter.POINT_COLOR_HIGHLIGHT.g;
colors[dst++] = PointScatter.POINT_COLOR_HIGHLIGHT.b;
} else {
colors[dst++] = PointScatter.POINT_COLOR_DEFAULT.r;
colors[dst++] = PointScatter.POINT_COLOR_DEFAULT.g;
colors[dst++] = PointScatter.POINT_COLOR_DEFAULT.b;
}
const color = this.getColorByIndex(i);
colors[i * 3] = color.r;
colors[i * 3 + 1] = color.g;
colors[i * 3 + 2] = color.b;
}
return colors;
}
Expand All @@ -110,13 +109,13 @@ export default class PointScatter extends ScatterChart {
const scaleFactor = new Float32Array(count);
for (let i = 0; i < count; i++) {
if (this.hoveredDataIndices.includes(i)) {
scaleFactor[i] = PointScatter.POINT_SCALE_HOVER;
scaleFactor[i] = PointScatterChart.POINT_SCALE_HOVER;
} else if (this.focusedDataIndices.includes(i)) {
scaleFactor[i] = PointScatter.POINT_SCALE_FOCUS;
scaleFactor[i] = PointScatterChart.POINT_SCALE_FOCUS;
} else if (this.highLightDataIndices.includes(i)) {
scaleFactor[i] = PointScatter.POINT_SCALE_HIGHLIGHT;
scaleFactor[i] = PointScatterChart.POINT_SCALE_HIGHLIGHT;
} else {
scaleFactor[i] = PointScatter.POINT_SCALE_DEFAULT;
scaleFactor[i] = PointScatterChart.POINT_SCALE_DEFAULT;
}
}
return scaleFactor;
Expand Down Expand Up @@ -190,7 +189,9 @@ export default class PointScatter extends ScatterChart {
}

const multiplier =
2 - Math.min(n, PointScatter.NUM_POINTS_FOG_THRESHOLD) / PointScatter.NUM_POINTS_FOG_THRESHOLD;
2 -
Math.min(n, PointScatterChart.NUM_POINTS_FOG_THRESHOLD) /
PointScatterChart.NUM_POINTS_FOG_THRESHOLD;

fog.near = shortestDist;
fog.far = furthestDist * multiplier;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
* limitations under the License.
*/

import PointScatter from './Points';
import PointScatterChart from './Points';

export default PointScatter;
export default PointScatterChart;
Loading

0 comments on commit 858d698

Please sign in to comment.