Skip to content

Commit

Permalink
Merge pull request #385 from Renumics/feature/use-color-transfer-hook…
Browse files Browse the repository at this point in the history
…-in-bbox-lens

feat: use useColorTransfer hook for colorscale of bboxes
  • Loading branch information
neindochoh authored Dec 7, 2023
2 parents b3b8d48 + 05cf895 commit 29b9399
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 69 deletions.
34 changes: 19 additions & 15 deletions src/hooks/useColorTransferFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,48 +111,52 @@ export const createConstantTransferFunction = (

export const createColorTransferFunction = (
data: ColumnData | undefined,
dType: DataType | undefined,
dtype: DataType | undefined,
robust = false,
continuousInts = false,
continuousCategories = false,
classBreaks?: number[]
): TransferFunction => {
if (dType === undefined) return createConstantTransferFunction(unknownDataType);
if (data === undefined) return createConstantTransferFunction(dType);
if (dtype === undefined) return createConstantTransferFunction(unknownDataType);
if (data === undefined) return createConstantTransferFunction(dtype);

if (['bool', 'str'].includes(dType.kind)) {
return createCategoricalTransferFunction(_.uniq(data), dType);
while (dtype.kind === 'Sequence') {
dtype = dtype.dtype;
}

if (dType.kind === 'int' && !continuousInts) {
if (['bool', 'str'].includes(dtype.kind)) {
return createCategoricalTransferFunction(_.uniq(data), dtype);
}

if (dtype.kind === 'int' && !continuousInts) {
const uniqValues = _.uniq(data);
const tooManyInts =
dType.kind === 'int' && uniqValues.length > MAX_VALUES_FOR_INT_CATEGORY;
dtype.kind === 'int' && uniqValues.length > MAX_VALUES_FOR_INT_CATEGORY;

if (!tooManyInts) {
return createCategoricalTransferFunction(uniqValues, dType);
return createCategoricalTransferFunction(uniqValues, dtype);
}
}

if (dType.kind === 'Category' && !continuousCategories) {
return createCategoricalTransferFunction(_.uniq(data), dType);
if (dtype.kind === 'Category' && !continuousCategories) {
return createCategoricalTransferFunction(_.uniq(data), dtype);
}

if (['int', 'float', 'Category'].includes(dType.kind)) {
const stats = makeStats(dType, data);
if (['int', 'float', 'Category'].includes(dtype.kind)) {
const stats = makeStats(dtype, data);
return createContinuousTransferFunction(
(robust ? stats?.p5 : stats?.min) ?? 0,
(robust ? stats?.p95 : stats?.max) ?? 1,
dType,
dtype,
classBreaks
);
}

return createConstantTransferFunction(dType);
return createConstantTransferFunction(dtype);
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const useColorTransferFunction = (data: any[], dtype: DataType) => {
export const useColorTransferFunction = (data: any[], dtype: DataType | undefined) => {
const colors = useColors();
return useMemo(
() =>
Expand Down
72 changes: 18 additions & 54 deletions src/lenses/BoundingBoxLens/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import useResizeObserver from '@react-hook/resize-observer';
import { useRef, useCallback } from 'react';
import { useRef } from 'react';
import { Lens } from '../../types';
import tw, { styled } from 'twin.macro';
import { CategoricalDataType, SequenceDataType } from '../../datatypes';
import { ColorsState, useColors } from '../../stores/colors';
import { Dataset, useDataset } from '../../stores/dataset';
import { CategoricalDataType } from '../../datatypes';
import * as d3 from 'd3';
import { useColorTransferFunction } from '../../hooks';

const Container = styled.div`
${tw`relative h-full w-full overflow-hidden`}
Expand Down Expand Up @@ -40,7 +39,6 @@ const BoundingBoxLens: Lens = ({ urls, values, columns }) => {

let boxes: [number[]] | [] = [];
let categories: number[] | [] = [];
let invertedCategories = (index: number) => index.toString();

// Check if single bounding box or multiple
if (bboxColumnIndex != -1) {
Expand All @@ -51,37 +49,23 @@ const BoundingBoxLens: Lens = ({ urls, values, columns }) => {

if (categoryColumnIndex != -1) {
categories = [values[categoryColumnIndex] as number];

invertedCategories = (index) =>
(columns[categoryColumnIndex].type as CategoricalDataType)
.invertedCategories[index];
} else if (categoriesColumnIndex != -1) {
categories = values[categoriesColumnIndex] as [number];

invertedCategories = (index) =>
(
(columns[categoriesColumnIndex].type as SequenceDataType)
.dtype as CategoricalDataType
).invertedCategories[index];
}

// eslint-disable-next-line react-hooks/exhaustive-deps
const colorTransferFunctionSelector = useCallback(
(d: Dataset) => {
if (categoryColumnIndex != -1) {
return d.colorTransferFunctions[columns[categoryColumnIndex].key]?.[
'full'
];
}
return undefined;
},
[columns, categoryColumnIndex]
const categoricalColumn =
columns[categoryColumnIndex] ?? columns[categoriesColumnIndex];
const categoricalDtype = (
categoricalColumn?.type?.kind === 'Sequence'
? categoricalColumn?.type?.dtype
: categoricalColumn?.type
) as CategoricalDataType | undefined;

const colorTransferFunction = useColorTransferFunction(
categories,
categoricalDtype
);

const colorPaletteSelector = (c: ColorsState) => c.continuousPalette;
const colorPalette = useColors(colorPaletteSelector);
const colorTransferFunction = useDataset(colorTransferFunctionSelector);

useResizeObserver(container.current, () => drawBoundingBoxes());

const drawBoundingBoxes = () => {
Expand All @@ -93,26 +77,6 @@ const BoundingBoxLens: Lens = ({ urls, values, columns }) => {
// Remove previous svg elements
d3.select(svgRef.current).select<SVGSVGElement>('g').remove();

let colorFunc: (i: number) => string;

if (categories.length === 0) {
colorFunc = (i: number) => colorPalette.scale().colors(boxes.length)[i];
} else {
if (colorTransferFunction !== undefined) {
colorFunc = (i: number) => colorTransferFunction(categories[i]).hex();
} else {
colorFunc = (i: number) => {
const dtype = (
columns[categoriesColumnIndex].type as SequenceDataType
).dtype as CategoricalDataType;
const index = dtype.categories[invertedCategories(categories[i])];
return colorPalette
.scale()
.colors(Object.keys(dtype.categories).length)[index];
};
}
}

const image = imgRef.current;

// Natural dimensions of the image
Expand Down Expand Up @@ -148,7 +112,7 @@ const BoundingBoxLens: Lens = ({ urls, values, columns }) => {
const y = box[1] * renderedHeight + offsetHeight;
const width = (box[2] - box[0]) * renderedWidth;
const height = (box[3] - box[1]) * renderedHeight;
const boxColor = colorFunc(i);
const boxColor = colorTransferFunction(i);

d3.select(svgRef.current)
.select<SVGSVGElement>('g')
Expand All @@ -159,18 +123,18 @@ const BoundingBoxLens: Lens = ({ urls, values, columns }) => {
.attr('height', height)
.attr('stroke', 'firebrick')
.attr('stroke-width', 2)
.attr('stroke', boxColor)
.attr('stroke', boxColor.css())
.attr('fill', 'none');

if (categories.length > 0) {
d3.select(svgRef.current)
.select<SVGSVGElement>('g')
.append('text')
.text(invertedCategories(categories[i]))
.text(categoricalDtype?.invertedCategories[categories[i]] ?? '')
.attr('x', x)
.attr('y', y - 3)
.attr('fontsize', 12)
.attr('fill', boxColor);
.attr('fill', boxColor.css());
}
}
};
Expand Down
1 change: 1 addition & 0 deletions src/widgets/DataGrid/Cell/HeaderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const HeaderCell: FunctionComponent<Props> = ({ style, columnIndex }) => {
() => (
<div tw="flex flex-col max-w-xl">
<div tw="font-bold break-all">{column.name}</div>
<div tw="font-bold">{column.type.kind}</div>
<div tw="text-gray-700 space-x-1.5 text-xs">
<span css={[!column.editable && tw`line-through`]}>editable</span>
<span css={[!column.optional && tw`line-through`]}>optional</span>
Expand Down

0 comments on commit 29b9399

Please sign in to comment.