Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: color map of high-dimensional page #950

Merged
merged 4 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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