Skip to content

Commit

Permalink
feat(partition): flame and icicle performance and tweening (opensearc…
Browse files Browse the repository at this point in the history
  • Loading branch information
monfera authored Mar 5, 2021
1 parent 67d9a85 commit 2ac7550
Show file tree
Hide file tree
Showing 36 changed files with 575 additions and 263 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
Expand Up @@ -314,6 +314,7 @@ export const configMetadata: Record<string, ConfigItem> = {
backgroundColor: { dflt: '#ffffff', type: 'color' },
sectorLineWidth: { dflt: 1, min: 0, max: 4, type: 'number' },
sectorLineStroke: { dflt: 'white', type: 'string' },
animation: { type: 'group', values: { duration: { dflt: 0, min: 0, max: 3000, type: 'number' } } },
};

export const config: Config = configMap<Config>((item: ConfigItem) => item.dflt, configMetadata);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
import { Font, VerticalAlignments } from '../../../../common/text_utils';
import { LegendPath } from '../../../../state/actions/legend';
import { Color } from '../../../../utils/common';
import { ContinuousDomainFocus } from '../../renderer/canvas/partition';
import { Layer } from '../../specs';
import { config, MODEL_KEY, ValueGetterName } from '../config';
import { ArrayNode, HierarchyOfArrays } from '../utils/group_by_rollup';
import { LinkLabelsViewModelSpec } from '../viewmodel/link_text_layout';
Expand Down Expand Up @@ -90,6 +92,7 @@ export interface QuadViewModel extends ShapeTreeNode {
strokeWidth: number;
strokeStyle: string;
fillColor: string;
textColor: string;
}

/** @internal */
Expand All @@ -98,11 +101,12 @@ export interface OutsideLinksViewModel {
}

/** @internal */
export type PickFunction = (x: Pixels, y: Pixels) => Array<QuadViewModel>;
export type PickFunction = (x: Pixels, y: Pixels, focus: ContinuousDomainFocus) => Array<QuadViewModel>;

/** @internal */
export type ShapeViewModel = {
config: Config;
layers: Layer[];
quadViewModel: QuadViewModel[];
rowSets: RowSet[];
linkLabelViewModels: LinkLabelsViewModelSpec;
Expand All @@ -124,6 +128,7 @@ const defaultFont: Font = {
/** @internal */
export const nullShapeViewModel = (specifiedConfig?: Config, diskCenter?: PointObject): ShapeViewModel => ({
config: specifiedConfig || config,
layers: [],
quadViewModel: [],
rowSets: [],
linkLabelViewModels: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,44 +108,33 @@ export function groupByRollup(
identity: () => any;
},
factTable: Relation,
drilldown: boolean,
drilldownSelection: CategoryKey[],
): HierarchyOfMaps {
const statistics: Statistics = {
globalAggregate: NaN,
};
const reductionMap: HierarchyOfMaps = factTable.reduce((p: HierarchyOfMaps, n, index) => {
const keyCount = keyAccessors.length;
let pointer: HierarchyOfMaps = p;
keyAccessors
.filter(
() =>
!drilldown ||
keyAccessors
.slice(0, drilldownSelection.length)
.map((keyAccessor) => keyAccessor(n, index))
.join(' | ') === drilldownSelection.slice(0, drilldownSelection.length).join(' | '),
)
.forEach((keyAccessor, i) => {
const key: Key = keyAccessor(n, index);
const last = i === keyCount - 1;
const node = pointer.get(key);
const inputIndices = node?.[INPUT_KEY] ?? [];
const childrenMap = node?.[CHILDREN_KEY] ?? new Map();
const aggregate = node?.[AGGREGATE_KEY] ?? identity();
const reductionValue = reducer(aggregate, valueAccessor(n));
pointer.set(key, {
[AGGREGATE_KEY]: reductionValue,
[STATISTICS_KEY]: statistics,
[INPUT_KEY]: [...inputIndices, index],
[DEPTH_KEY]: i,
...(!last && { [CHILDREN_KEY]: childrenMap }),
});
if (childrenMap) {
// will always be true except when exiting from forEach, ie. upon encountering the leaf node
pointer = childrenMap;
}
keyAccessors.forEach((keyAccessor, i) => {
const key: Key = keyAccessor(n, index);
const last = i === keyCount - 1;
const node = pointer.get(key);
const inputIndices = node?.[INPUT_KEY] ?? [];
const childrenMap = node?.[CHILDREN_KEY] ?? new Map();
const aggregate = node?.[AGGREGATE_KEY] ?? identity();
const reductionValue = reducer(aggregate, valueAccessor(n));
pointer.set(key, {
[AGGREGATE_KEY]: reductionValue,
[STATISTICS_KEY]: statistics,
[INPUT_KEY]: [...inputIndices, index],
[DEPTH_KEY]: i,
...(!last && { [CHILDREN_KEY]: childrenMap }),
});
if (childrenMap) {
// will always be true except when exiting from forEach, ie. upon encountering the leaf node
pointer = childrenMap;
}
});
return p;
}, new Map());
if (reductionMap.get(HIERARCHY_ROOT_KEY) !== undefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
* under the License.
*/

import { getRectangleRowGeometry, getFillTextColor } from './fill_text_layout';
import { fillTextColor } from '../../../../common/fill_text_color';
import { getRectangleRowGeometry } from './fill_text_layout';

describe('Test that getRectangleRowGeometry works with:', () => {
const container = { x0: 0, y0: 0, x1: 200, y1: 100 };
Expand Down Expand Up @@ -281,7 +282,7 @@ describe('Test getTextColor function', () => {
const fillColor = 'rgba(55, 126, 184, 0.7)';
const containerBackgroundColor = 'white';
const expectedAdjustedTextColor = 'black';
expect(getFillTextColor(textColor, textInvertible, textContrast, fillColor, containerBackgroundColor)).toEqual(
expect(fillTextColor(textColor, textInvertible, textContrast, fillColor, containerBackgroundColor)).toEqual(
expectedAdjustedTextColor,
);
});
Expand All @@ -292,7 +293,7 @@ describe('Test getTextColor function', () => {
const fillColor = 'rgba(55, 126, 184, 0.7)';
const containerBackgroundColor = 'white';
const expectedAdjustedTextColor = 'black';
expect(getFillTextColor(textColor, textInvertible, textContrast, fillColor, containerBackgroundColor)).toEqual(
expect(fillTextColor(textColor, textInvertible, textContrast, fillColor, containerBackgroundColor)).toEqual(
expectedAdjustedTextColor,
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@
* under the License.
*/

import chroma from 'chroma-js';

import {
colorIsDark,
combineColors,
getTextColorIfTextInvertible,
isColorValid,
makeHighContrastColor,
} from '../../../../common/color_calcs';
import { TAU } from '../../../../common/constants';
import {
Coordinate,
Expand All @@ -40,9 +31,8 @@ import {
} from '../../../../common/geometry';
import { logarithm } from '../../../../common/math';
import { integerSnap, monotonicHillClimb } from '../../../../common/optimize';
import { Box, Font, PartialFont, TextContrast, TextMeasure, VerticalAlignments } from '../../../../common/text_utils';
import { Color, ValueFormatter } from '../../../../utils/common';
import { Logger } from '../../../../utils/logger';
import { Box, Font, PartialFont, TextMeasure, VerticalAlignments } from '../../../../common/text_utils';
import { ValueFormatter } from '../../../../utils/common';
import { Layer } from '../../specs';
import { Config, Padding } from '../types/config_types';
import {
Expand Down Expand Up @@ -211,60 +201,6 @@ function getWordSpacing(fontSize: number) {
return fontSize / 4;
}

/**
* Determine the color for the text hinging on the parameters of textInvertible and textContrast
* @internal
*/
export function getFillTextColor(
textColor: Color,
textInvertible: boolean,
textContrast: TextContrast,
sliceFillColor: string,
containerBackgroundColor?: Color,
): string | undefined {
const bgColorAlpha = isColorValid(containerBackgroundColor) ? chroma(containerBackgroundColor).alpha() : 1;
if (!isColorValid(containerBackgroundColor) || bgColorAlpha < 1) {
if (bgColorAlpha < 1) {
Logger.expected('Text contrast requires a background color with an alpha value of 1', 1, bgColorAlpha);
} else if (containerBackgroundColor !== 'transparent') {
Logger.warn(`Invalid background color "${String(containerBackgroundColor)}"`);
}

return getTextColorIfTextInvertible(
colorIsDark(sliceFillColor),
colorIsDark(textColor),
textColor,
false,
'white', // never used
);
}

let adjustedTextColor: string | undefined = textColor;
const containerBackground = combineColors(sliceFillColor, containerBackgroundColor);
const textShouldBeInvertedAndTextContrastIsFalse = textInvertible && !textContrast;
const textShouldBeInvertedAndTextContrastIsSetToTrue = textInvertible && typeof textContrast !== 'number';
const textContrastIsSetToANumberValue = typeof textContrast === 'number';
const textShouldNotBeInvertedButTextContrastIsDefined = textContrast && !textInvertible;

// change the contrast for the inverted slices
if (textShouldBeInvertedAndTextContrastIsFalse || textShouldBeInvertedAndTextContrastIsSetToTrue) {
const backgroundIsDark = colorIsDark(combineColors(sliceFillColor, containerBackgroundColor));
const specifiedTextColorIsDark = colorIsDark(textColor);
adjustedTextColor = getTextColorIfTextInvertible(
backgroundIsDark,
specifiedTextColorIsDark,
textColor,
textContrast,
containerBackground,
);
// if textContrast is a number then take that into account or if textInvertible is set to false
} else if (textContrastIsSetToANumberValue || textShouldNotBeInvertedButTextContrastIsDefined) {
return makeHighContrastColor(adjustedTextColor, containerBackground);
}

return adjustedTextColor;
}

type GetShapeRowGeometry<C> = (
container: C,
cx: Distance,
Expand All @@ -286,7 +222,6 @@ function fill<C>(
shapeConstructor: ShapeConstructor<C>,
getShapeRowGeometry: GetShapeRowGeometry<C>,
getRotation: GetRotation,
containerBackgroundColor?: Color,
) {
return function fillClosure(
config: Config,
Expand All @@ -310,31 +245,12 @@ function fill<C>(
? VerticalAlignments.bottom
: VerticalAlignments.top;
const fontSizes = allFontSizes[Math.min(node.depth, allFontSizes.length) - 1];
const {
textColor,
textInvertible,
fontStyle,
fontVariant,
fontFamily,
fontWeight,
valueFormatter,
padding,
textContrast,
textOpacity,
clipText,
} = {
const { fontStyle, fontVariant, fontFamily, fontWeight, valueFormatter, padding, textOpacity, clipText } = {
...fillLabel,
valueFormatter: formatter,
...layer.fillLabel,
...layer.shape,
};
const fillTextColor = getFillTextColor(
textColor,
textInvertible,
textContrast,
node.fillColor,
containerBackgroundColor,
);

const valueFont = {
...fillLabel,
Expand All @@ -348,7 +264,7 @@ function fill<C>(
fontVariant,
fontWeight,
fontFamily,
textColor,
textColor: node.textColor,
textOpacity,
};
const allBoxes = getAllBoxes(rawTextGetter, valueGetter, valueFormatter, sizeInvariantFont, valueFont, node);
Expand All @@ -371,7 +287,7 @@ function fill<C>(
node,
clipText,
),
fillTextColor,
fillTextColor: node.textColor,
};
};
};
Expand Down Expand Up @@ -555,9 +471,8 @@ export function fillTextLayout<C>(
shapeConstructor: ShapeConstructor<C>,
getShapeRowGeometry: GetShapeRowGeometry<C>,
getRotation: GetRotation,
containerBackgroundColor?: Color,
) {
const specificFiller = fill(shapeConstructor, getShapeRowGeometry, getRotation, containerBackgroundColor);
const specificFiller = fill(shapeConstructor, getShapeRowGeometry, getRotation);
return function fillTextLayoutClosure(
measure: TextMeasure,
rawTextGetter: RawTextGetter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const groupByRollupAccessors = [() => null, (d: any) => d.sitc1];

describe('Test', () => {
test('getHierarchyOfArrays should omit zero and negative values', () => {
const outerResult = getHierarchyOfArrays(rawFacts, valueAccessor, groupByRollupAccessors, null, false, []);
const outerResult = getHierarchyOfArrays(rawFacts, valueAccessor, groupByRollupAccessors, null);
expect(outerResult.length).toBe(1);

const results = outerResult[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* under the License.
*/

import { CategoryKey } from '../../../../common/category';
import { LegendItemExtraValues } from '../../../../common/legend';
import { SeriesKey } from '../../../../common/series_id';
import { Relation } from '../../../../common/text_utils';
Expand Down Expand Up @@ -45,8 +44,6 @@ export function getHierarchyOfArrays(
valueAccessor: ValueAccessor,
groupByRollupAccessors: IndexedAccessorFn[],
sorter: Sorter | null = childOrders.descending,
drilldown: boolean,
drilldownSelection: CategoryKey[],
): HierarchyOfArrays {
const aggregator = aggregators.sum;

Expand All @@ -64,7 +61,7 @@ export function getHierarchyOfArrays(
// By introducing `scale`, we no longer need to deal with the dichotomy of
// size as data value vs size as number of pixels in the rectangle
return mapsToArrays(
groupByRollup(groupByRollupAccessors, valueAccessor, aggregator, facts, drilldown, drilldownSelection),
groupByRollup(groupByRollupAccessors, valueAccessor, aggregator, facts),
sorter && aggregateComparator(mapEntryValue, sorter),
);
}
Expand All @@ -76,8 +73,6 @@ export function partitionTree(
layers: Layer[],
defaultLayout: PartitionLayout,
layout: PartitionLayout = defaultLayout,
drilldown: boolean,
drilldownSelection: CategoryKey[],
) {
const sorter = isTreemap(layout) || isSunburst(layout) ? childOrders.descending : null;
return getHierarchyOfArrays(
Expand All @@ -86,8 +81,6 @@ export function partitionTree(
// eslint-disable-next-line no-shadow
[() => HIERARCHY_ROOT_KEY, ...layers.map(({ groupByRollup }) => groupByRollup)],
sorter,
drilldown,
drilldownSelection,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,24 @@

import { LayerValue } from '../../../../specs';
import { Point } from '../../../../utils/point';
import { ContinuousDomainFocus } from '../../renderer/canvas/partition';
import { MODEL_KEY } from '../config';
import { QuadViewModel, ShapeViewModel } from '../types/viewmodel_types';
import { AGGREGATE_KEY, DEPTH_KEY, getNodeName, PARENT_KEY, PATH_KEY, SORT_INDEX_KEY } from '../utils/group_by_rollup';

/** @internal */
export const pickedShapes = (models: ShapeViewModel[], pointerPosition: Point): QuadViewModel[] => {
export const pickedShapes = (
models: ShapeViewModel[],
pointerPosition: Point,
foci: ContinuousDomainFocus[],
): QuadViewModel[] => {
const geoms = models[0];
const focus = foci[0];
const picker = geoms.pickQuads;
const { diskCenter } = geoms;
const x = pointerPosition.x - diskCenter.x;
const y = pointerPosition.y - diskCenter.y;
return picker(x, y);
return picker(x, y, focus);
};

/** @internal */
Expand Down
Loading

0 comments on commit 2ac7550

Please sign in to comment.