diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index 616bda5937..710a8b7ad9 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -524,6 +524,7 @@ export const ChartType: Readonly<{ Goal: "goal"; Partition: "partition"; Flame: "flame"; + PlotMatrix: "plot_matrix"; Timeslip: "timeslip"; XYAxis: "xy_axis"; Heatmap: "heatmap"; @@ -2043,6 +2044,55 @@ export const Placement: Readonly<{ // @public export type Placement = $Values; +// Warning: (ae-forgotten-export) The symbol "buildProps_2" needs to be exported by the entry point index.d.ts +// +// @public +export const PlotMatrix: (props: SFProps) => null; + +// @public +export interface PlotMatrixSpec extends Spec { + // (undocumented) + chartType: typeof ChartType.PlotMatrix; + // (undocumented) + columnarData: PlotMatrixViewModel; + // (undocumented) + specType: typeof SpecType.Series; +} + +// @alpha (undocumented) +export interface PlotMatrixStyle { + // (undocumented) + navigation: { + textColor: Color; + buttonTextColor: Color; + buttonDisabledTextColor: Color; + buttonBackgroundColor: Color; + buttonDisabledBackgroundColor: Color; + }; + // (undocumented) + scrollbarThumb: Color; + // (undocumented) + scrollbarTrack: Color; +} + +// @public +export interface PlotMatrixViewModel { + // (undocumented) + color: Float32Array; + // (undocumented) + label: string[]; + // (undocumented) + position0: Float32Array; + // (undocumented) + position1: Float32Array; + // (undocumented) + size0: Float32Array; + // (undocumented) + size1: Float32Array; + // (undocumented) + value: Float64Array; +} + // @public (undocumented) type PointerEvent_2 = PointerOverEvent | PointerOutEvent; export { PointerEvent_2 as PointerEvent } @@ -2724,6 +2774,8 @@ export interface Theme { markSizeRatio?: number; metric: MetricStyle; partition: PartitionStyle; + // @alpha (undocumented) + plotMatrix: PlotMatrixStyle; // (undocumented) scales: ScalesConfig; // (undocumented) @@ -2769,10 +2821,10 @@ export interface TimeScale { type: typeof ScaleType.Time; } -// Warning: (ae-forgotten-export) The symbol "buildProps_2" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "buildProps_3" needs to be exported by the entry point index.d.ts // // @public -export const Timeslip: (props: SFProps) => null; +export const Timeslip: (props: SFProps) => null; // @public export interface TimeslipSpec extends Spec { diff --git a/packages/charts/package.json b/packages/charts/package.json index a9d84e397b..585501c36b 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -42,6 +42,7 @@ "d3-interpolate": "^3.0.1", "d3-scale": "^3.3.0", "d3-shape": "^2.0.0", + "d3": "3.5.17", "prop-types": "^15.7.2", "re-reselect": "^3.4.0", "react-redux": "^7.1.0", diff --git a/packages/charts/src/chart_types/index.ts b/packages/charts/src/chart_types/index.ts index 2726dc7ce7..50e5b840e5 100644 --- a/packages/charts/src/chart_types/index.ts +++ b/packages/charts/src/chart_types/index.ts @@ -17,6 +17,7 @@ export const ChartType = Object.freeze({ Goal: 'goal' as const, Partition: 'partition' as const, Flame: 'flame' as const, + PlotMatrix: 'plot_matrix' as const, Timeslip: 'timeslip' as const, XYAxis: 'xy_axis' as const, Heatmap: 'heatmap' as const, diff --git a/packages/charts/src/chart_types/plot_matrix/internal_chart_state.ts b/packages/charts/src/chart_types/plot_matrix/internal_chart_state.ts new file mode 100644 index 0000000000..5c67ef6af2 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/internal_chart_state.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ChartType } from '..'; +import { DEFAULT_CSS_CURSOR } from '../../common/constants'; +import { LegendItemExtraValues } from '../../common/legend'; +import { SeriesKey } from '../../common/series_id'; +import { InternalChartState } from '../../state/chart_state'; +import { InitStatus } from '../../state/selectors/get_internal_is_intialized'; +import { PlotMatrix } from './plot_matrix'; + +/** @internal */ +export class PlotMatrixState implements InternalChartState { + chartType = ChartType.PlotMatrix; + getChartTypeDescription = () => 'Plot matrix'; + chartRenderer = PlotMatrix; + + // default empty properties, unused in Plot matrix + eventCallbacks = () => {}; + isInitialized = () => InitStatus.Initialized; + isBrushAvailable = () => false; + isBrushing = () => false; + isChartEmpty = () => false; + getLegendItemsLabels = () => []; + getLegendItems = () => []; + getLegendExtraValues = () => new Map(); + getPointerCursor = () => DEFAULT_CSS_CURSOR; + getTooltipAnchor = () => ({ x: 0, y: 0, width: 0, height: 0 }); + isTooltipVisible = () => ({ visible: false, isExternal: false, displayOnly: false, isPinnable: false }); + getTooltipInfo = () => ({ header: null, values: [] }); + getProjectionContainerArea = () => ({ width: 0, height: 0, top: 0, left: 0 }); + getMainProjectionArea = () => ({ width: 0, height: 0, top: 0, left: 0 }); + getBrushArea = () => null; + getDebugState = () => ({}); +} diff --git a/packages/charts/src/chart_types/plot_matrix/plom/colors.ts b/packages/charts/src/chart_types/plot_matrix/plom/colors.ts new file mode 100644 index 0000000000..808d413b75 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/colors.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const turboString = `30123b32154333184a341b51351e5836215f37246638276d392a733a2d793b2f803c32863d358b3e38913f3b973f3e9c40 +40a24143a74146ac4249b1424bb5434eba4351bf4454c34456c74559cb455ccf455ed34661d64664da4666dd4669e0466be3476ee64771e94773eb4 +776ee4778f0477bf2467df44680f64682f84685fa4687fb458afc458cfd448ffe4391fe4294ff4196ff4099ff3e9bfe3d9efe3ba0fd3aa3fc38a5fb +37a8fa35abf833adf731aff52fb2f42eb4f22cb7f02ab9ee28bceb27bee925c0e723c3e422c5e220c7df1fc9dd1ecbda1ccdd81bd0d51ad2d21ad4d +019d5cd18d7ca18d9c818dbc518ddc218dec018e0bd19e2bb19e3b91ae4b61ce6b41de7b21fe9af20eaac23ebaa25eca727eea42aefa12cf09e2ff1 +9b32f29835f39438f4913cf58e3ff68a43f78746f8844af8804ef97d52fa7a55fa7659fb735dfc6f61fc6c65fd6969fd666dfe6271fe5f75fe5c79f +e597dff5680ff5384ff5188ff4e8bff4b8fff4992ff4796fe4499fe429cfe409ffd3fa1fd3da4fc3ca7fc3aa9fb39acfb38affa37b1f936b4f836b7 +f735b9f635bcf534bef434c1f334c3f134c6f034c8ef34cbed34cdec34d0ea34d2e935d4e735d7e535d9e436dbe236dde037dfdf37e1dd37e3db38e +5d938e7d739e9d539ebd339ecd13aeecf3aefcd3af1cb3af2c93af4c73af5c53af6c33af7c13af8be39f9bc39faba39fbb838fbb637fcb336fcb136 +fdae35fdac34fea933fea732fea431fea130fe9e2ffe9b2dfe992cfe962bfe932afe9029fd8d27fd8a26fc8725fc8423fb8122fb7e21fa7b1ff9781 +ef9751df8721cf76f1af66c19f56918f46617f36315f26014f15d13f05b12ef5811ed5510ec530feb500eea4e0de84b0ce7490ce5470be4450ae243 +0ae14109df3f08dd3d08dc3a07da3907d83706d63506d43305d23105d02f05ce2d04cc2c04ca2a04c82803c52603c32503c12302be2102bc2002b91 +e02b71d02b41b01b21a01af1801ac1701a91601a71401a41301a112019e10019b0f01980e01950d01920b018e0a018b09028808028507028106027e +05027a0403`.replace(/[\n\r]/g, ''); + +const magmaString = `00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a +08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e2 +2115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75 +440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f60188062198 +0641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125 +818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa13 +07ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c2 +3b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e +04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675c +f4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c6 +3fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb2 +7afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed +799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfc +fbbdfcfdbf`.replace(/[\n\r]/g, ''); + +const toColorPalette = (paletteString: string) => + [...new Array(Math.floor(paletteString.length / 6))].map((_, i) => [ + parseInt(paletteString.slice(i * 6, i * 6 + 2), 16), + parseInt(paletteString.slice(i * 6 + 2, i * 6 + 4), 16), + parseInt(paletteString.slice(i * 6 + 4, i * 6 + 6), 16), + Math.round((i < 248 ? 255 : 32 * (255 - i) + 31) * 0.75), // optional: make the end of the palette less opaque + ]); + +/** @internal */ +export const turboPalette = toColorPalette(turboString); + +/** @internal */ +export const magmaPalette = toColorPalette(magmaString); diff --git a/packages/charts/src/chart_types/plot_matrix/plom/dom/axis_brush.ts b/packages/charts/src/chart_types/plot_matrix/plom/dom/axis_brush.ts new file mode 100644 index 0000000000..727ffb0536 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/dom/axis_brush.ts @@ -0,0 +1,360 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* The below file has been derived from src/traces/parcoords/axisbrush.js as of March 2018, commit + * Original MIT license in the plotly.js repo as of 40b0c2bce9f8705e33c5badb6888131dcd74bf17 + * The file heavily depends on D3 DOM manipulation + * todo: replace this with either Canvas2d and plain functions, or a React tree + * todo: alternatively clean up the copyright block + */ + +/** + * Copyright 2012-2018, Plotly, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +import d3 from 'd3'; + +import { classNames, VERTICAL_PADDING } from '../src/config'; +import { keyFun, repeat } from '../src/utils'; + +const barConfig = { + fillWidth: 4, + fillColor: 'magenta', + fillOpacity: 1, + captureWidth: 15, + strokeWidth: 0, + strokeColor: 'transparent', + strokeOpacity: 1, + handleHeight: 8, +}; + +const barHorizontalSetup = (selection) => { + selection.attr('x', -barConfig.captureWidth / 2).attr('width', barConfig.captureWidth); +}; + +const backgroundBarHorizontalSetup = (selection) => { + selection.attr('visibility', 'visible').style('visibility', 'visible').attr('fill', 'yellow').attr('opacity', 0); +}; + +/** @internal */ +export const filterActive = (brush) => brush.filterSpecified; + +const setHighlight = (d) => { + if (!filterActive(d.brush)) { + return `0 ${d.height}`; + } + const unitRanges = d.brush.filter.get(); + const pixelRanges = [unitRanges.map((pr) => d.unitScaleInOrder(pr))]; + const dashArray = [0]; // we start with a 0 length selection as filter ranges are inclusive, not exclusive + let p, sectionHeight, iNext; + let currentGap = pixelRanges.length ? pixelRanges[0][0] : null; + for (let i = 0; i < pixelRanges.length; i++) { + p = pixelRanges[i]; + sectionHeight = p[1] - p[0]; + dashArray.push(currentGap); + dashArray.push(sectionHeight); + iNext = i + 1; + if (iNext < pixelRanges.length) currentGap = pixelRanges[iNext][0] - p[1]; + } + dashArray.push(d.height); + // d.height is added at the end to ensure that (1) we have an even number of dasharray points, MDN page says + // "If an odd number of values is provided, then the list of values is repeated to yield an even number of values." + // and (2) it's _at least_ as long as the full height (even if range is minuscule and at the bottom) though this + // may not be necessary, maybe duplicating the last point would do too. But no harm in a longer dasharray than line. + return dashArray; +}; + +const differentInterval = (int1) => { + // An interval is different if the extents don't match, which is a safe test only because the intervals + // get consolidated anyway (ie. the identity of overlapping intervals won't be preserved; they get fused) + return (int2) => int1[0] !== int2[0] || int1[1] !== int2[1]; +}; + +const clearCursor = () => d3.select(document.body).style('cursor', null); +const north = (fPix, y) => fPix[1] <= y && y <= fPix[1] + barConfig.handleHeight; +const middle = (fPix, y) => fPix[0] < y && y < fPix[1]; +const south = (fPix, y) => fPix[0] - barConfig.handleHeight <= y && y <= fPix[0]; + +const styleHighlight = (selection) => { + // stroke-dasharray is used to minimize the number of created DOM nodes, because the requirement calls for up to + // 1000 individual selections on an axis, and there can be 60 axes per plom, and multiple plom per + // dashboard. The technique is similar to https://codepen.io/monfera/pen/rLYqWR and using a `polyline` with + // multiple sections, or a `path` element via its `d` attribute would also be DOM-sparing alternatives. + selection.attr('stroke-dasharray', setHighlight); +}; + +const renderHighlight = (root) => styleHighlight(d3.select(root).selectAll('.highlight, .highlightShadow')); + +const getInterval = (b, unitScaleInOrder, y) => { + const interval = b.filter.get(); + const intervalPix = interval.map(unitScaleInOrder); + return { + interval, + intervalPix, + n: north(intervalPix, y), + m: middle(intervalPix, y), + s: south(intervalPix, y), + }; +}; + +const brushClear = (brush) => { + brush.filterSpecified = false; + brush.svgBrush.extent = [0, 1]; +}; + +const attachDragBehavior = function (selection) { + // There's some fiddling with pointer cursor styling so that the cursor preserves its shape while dragging a brush + // even if the cursor strays from the interacting bar, which is bound to happen as bars are thin and the user + // will inevitably leave the hotspot strip. In this regard, it does something similar to what the D3 brush would do. + selection + .on('mousemove', function (d) { + d3.event.preventDefault(); + const b = d.brush; + if (d.parent.inBrushDrag) return; + const y = d.unitScaleInOrder(d.unitScale.invert(d3.mouse(this)[1])); + const interval = getInterval(b, d.unitScaleInOrder, y); + const h = d.special === 'top' || d.special === 'bottom'; + d3.select(document.body).style( + 'cursor', + interval.n + ? h + ? 'e-resize' + : 'n-resize' + : interval.s + ? h + ? 'w-resize' + : 's-resize' + : !interval.m + ? 'crosshair' + : filterActive(b) + ? h + ? 'ew-resize' + : 'ns-resize' + : 'crosshair', + ); + }) + .on('mouseleave', (d) => { + if (d.parent.inBrushDrag) return; + clearCursor(); + }) + .call( + d3.behavior + .drag() + .on('dragstart', function (d) { + const e = d3.event; + e.sourceEvent.stopPropagation(); + const y = d.unitScaleInOrder(d.unitScale.invert(d3.mouse(this)[1])); + const unitLocation = d.unitScaleInOrder.invert(y); + const b = d.brush; + const intData = getInterval(b, d.unitScaleInOrder, y); + const unitRange = intData.interval; + const pixelRange = unitRange.map(d.unitScaleInOrder); + const s = b.svgBrush; + const active = filterActive(b); + const barInteraction = unitRange && (intData.m || intData.s || intData.n); + s.wasDragged = false; // we start assuming there won't be a drag - useful for reset + s.grabPoint = d.unitScaleInOrder(unitLocation) - pixelRange[0] - VERTICAL_PADDING; + s.barLength = pixelRange[1] - pixelRange[0]; + s.grabbingBar = active && intData.m && unitRange; + s.stayingIntervals = barInteraction ? b.filter.get().filter(differentInterval(unitRange)) : b.filter.get(); // keep all preexisting bars if interaction wasn't a barInteraction + const grabbingBarNorth = intData.n; + const grabbingBarSouth = intData.s; + const newBrushing = !s.grabbingBar && !grabbingBarNorth && !grabbingBarSouth; + s.startExtent = newBrushing ? d.unitScaleInOrder.invert(y) : grabbingBarSouth ? unitRange[1] : unitRange[0]; + d.parent.inBrushDrag = true; + s.brushStartCallback(); + }) + .on('drag', function (d) { + const e = d3.event; + const y = d.unitScaleInOrder(d.unitScale.invert(e.y)); + const s = d.brush.svgBrush; + s.wasDragged = true; + e.sourceEvent.stopPropagation(); + + if (s.grabbingBar) { + // moving the bar + s.newExtent = [y - s.grabPoint, y + s.barLength - s.grabPoint].map(d.unitScaleInOrder.invert); + } else { + // south/north drag or new bar creation + s.newExtent = + d.unitScaleInOrder(s.startExtent) < y + ? [s.startExtent, d.unitScaleInOrder.invert(y)] + : [d.unitScaleInOrder.invert(y), s.startExtent]; + } + + // take care of the plom axis height constraint: bar can't breach it + const bottomViolation = Math.max(0, -s.newExtent[0]); + const topViolation = Math.max(0, s.newExtent[1] - 1); + s.newExtent[0] += bottomViolation; + s.newExtent[1] -= topViolation; + if (s.grabbingBar) { + // in case of bar dragging (non-resizing interaction, unlike north/south resize or new bar creation) + // the constraint adjustment must apply to the other end of the bar as well, otherwise it'd + // shorten or lengthen + s.newExtent[1] += bottomViolation; + s.newExtent[0] -= topViolation; + } + + d.brush.filterSpecified = true; + s.extent = s.newExtent; + s.brushCallback(d); + renderHighlight(this.parentElement); + }) + .on('dragend', function (d) { + const e = d3.event; + e.sourceEvent.stopPropagation(); + const { brush } = d; + const { filter } = brush; + const s = brush.svgBrush; + const { grabbingBar } = s; + s.grabbingBar = false; + s.grabLocation = undefined; + d.parent.inBrushDrag = false; + clearCursor(); // instead of clearing, a nicer thing would be to set it according to current location + if (!s.wasDragged) { + // a click+release on the same spot (ie. w/o dragging) means a bar or full reset + s.wasDragged = undefined; // logic-wise unneded, just shows `wasDragged` has no longer a meaning + if (grabbingBar) { + s.extent = s.stayingIntervals; + if (s.extent.length === 0) { + brushClear(brush); + } + } else { + brushClear(brush); + } + s.brushCallback(d); + renderHighlight(this.parentElement); + s.brushEndCallback(filter.get()); + return; + } + s.brushEndCallback(filter.get()); + }), + ); +}; + +const renderAxisBrush = (axisBrush) => { + const background = axisBrush.selectAll('.background').data(repeat); + + background + .enter() + .append('rect') + .classed('background', true) + .call(barHorizontalSetup) + .call(backgroundBarHorizontalSetup) + .style('pointer-events', 'auto') // parent pointer events are disabled; we must have it to register events + .attr('transform', `translate(0 ${VERTICAL_PADDING})`); + + background.call(attachDragBehavior).attr('height', (d) => d.height - VERTICAL_PADDING); + + const highlightShadow = axisBrush.selectAll('.highlightShadow').data(repeat); // we have a set here, can't call it `extent` + + highlightShadow + .enter() + .append('line') + .classed('highlightShadow', true) + .attr('x', -barConfig.fillWidth / 2) + .attr('stroke-width', barConfig.fillWidth + barConfig.strokeWidth) + .attr('stroke', barConfig.strokeColor) + .attr('opacity', barConfig.strokeOpacity) + .attr('stroke-linecap', 'butt'); + + highlightShadow.attr('y1', (d) => d.height).call(styleHighlight); + + const highlight = axisBrush.selectAll('.highlight').data(repeat); // we have a set here, can't call it `extent` + + highlight + .enter() + .append('line') + .classed('highlight', true) + .attr('x', -barConfig.fillWidth / 2) + .attr('stroke-width', barConfig.fillWidth - barConfig.strokeWidth) + .attr('stroke', barConfig.fillColor) + .attr('opacity', barConfig.fillOpacity) + .attr('stroke-linecap', 'butt'); + + highlight.attr('y1', (d) => d.height).call(styleHighlight); +}; + +const setAxisBrush = (axisBrush, root) => { + axisBrush.each((d) => { + // Set the brush programmatically if data requires so, eg. `constraintRange` specifies a proper subset + // This is only to ensure the SVG brush is correct; WebGL lines are controlled from `d.brush.filter` directly + const b = d.brush; + const f = b.filter.get(); + if (filterActive(b)) { + setBrushExtent(b, f); + } else { + brushClear(b, root); + } + }); +}; + +/** @internal */ +export const ensureAxisBrush = (axisOverlays) => { + const axisBrush = axisOverlays.selectAll(`.${classNames.axisBrush}`).data(repeat, keyFun); + + const rotator = (d) => + d.special === 'top' || d.special === 'bottom' + ? `rotate(90deg) translate(0, -110px)` // fixme magic offset for demo + : `rotate(0)`; + + const axisBrushEnter = axisBrush.enter().append('g').classed(classNames.axisBrush, true).style('transform', rotator); + + const axisBrush2 = axisOverlays.selectAll(`.${classNames.axisBrush}2`).data(repeat, keyFun); + + axisBrush2.enter().append('g').classed(`${classNames.axisBrush}2`, true).style('transform', rotator); + + setAxisBrush(axisBrush, axisBrushEnter); + renderAxisBrush(axisBrush2); +}; + +const setBrushExtent = (brush, range) => { + brush.svgBrush.extent[0] = range[0]; + brush.svgBrush.extent[1] = range[1]; +}; + +const getBrushExtent = (brush) => brush.svgBrush.extent; + +const axisBrushMoved = (callback) => (dimension) => { + const { brush } = dimension; + const extent = getBrushExtent(brush); + const newExtent = extent.slice(); + brush.filter.set(newExtent); + callback(); +}; + +const makeFilter = () => { + let filter = []; + return { + set: (interval) => (filter = interval.slice()), + get: () => filter.slice(), + }; +}; + +/** @internal */ +export const makeBrush = (state, rangeSpecified, initialRange, brushStartCallback, brushCallback, brushEndCallback) => { + const filter = makeFilter(); + filter.set(initialRange); + return { + filter, + filterSpecified: rangeSpecified, // there's a difference between not filtering and filtering a non-proper subset + svgBrush: { + extent: [0, 1], // this is where the svgBrush writes contents into + brushStartCallback, + brushCallback: axisBrushMoved(brushCallback), + brushEndCallback, + }, + }; +}; diff --git a/packages/charts/src/chart_types/plot_matrix/plom/dom/d3utils.ts b/packages/charts/src/chart_types/plot_matrix/plom/dom/d3utils.ts new file mode 100644 index 0000000000..3d99610ac2 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/dom/d3utils.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { scaleLinear } from 'd3-scale'; + +import { getExtent } from '../src/utils'; + +/** @internal */ +export const unitScale = (size: number, padding: number) => scaleLinear().range([size - padding, padding]); +/** @internal */ +export const unitScaleInOrder = (size: number, padding: number) => scaleLinear().range([padding, size - padding]); +/** @internal */ +export const domainToUnitScale = (values: number[]) => scaleLinear().domain(getExtent(values)); +/** @internal */ +export const domainScale = (size: number, padding: number, values: number[]) => { + return scaleLinear() + .domain(getExtent(values)) + .range([size - padding, padding]); +}; + +/** @internal */ +export const unitToColorScale = (colorPalette: [number, [number, number, number]][]) => { + const colorStops = colorPalette.map((d) => d[0]); + const colorTuples = colorPalette.map((d) => d[1]); + const piecewiseLinearUnitScales = [0, 1, 2].map((index) => + scaleLinear() + .clamp(true) + .domain(colorStops) + .range(colorTuples.map((v) => v[index])), + ); + + return (d: number): [number, number, number] => [ + piecewiseLinearUnitScales[0](d), + piecewiseLinearUnitScales[1](d), + piecewiseLinearUnitScales[2](d), + ]; +}; diff --git a/packages/charts/src/chart_types/plot_matrix/plom/dom/plom.ts b/packages/charts/src/chart_types/plot_matrix/plom/dom/plom.ts new file mode 100644 index 0000000000..6c34c25f95 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/dom/plom.ts @@ -0,0 +1,649 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* The below file has been partly derived from src/traces/parcoords/parcoords.js as of March 2018, commit + * Original MIT license in the plotly.js repo as of 40b0c2bce9f8705e33c5badb6888131dcd74bf17 + * The file heavily depends on D3 DOM manipulation + * todo: replace this file with a simple React tree and plain functions + * todo: alternatively clean up the copyright block + */ + +/** + * Copyright 2012-2018, Plotly, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +import d3 from 'd3'; + +import { resetState } from '../../../../common/kingly'; +import { c, classNames, DARK_MODE, VERTICAL_PADDING } from '../src/config'; +import { geomLayerMaker } from '../src/geoms'; +import { keyFun, repeat, simulateContextLoss } from '../src/utils'; +import { ensureAxisBrush, makeBrush } from './axis_brush'; +import { domainScale, domainToUnitScale, unitScale, unitScaleInOrder, unitToColorScale } from './d3utils'; + +const GL_RENDER = true; +const AXIS_TITLE_OFFSET_Y = 30; +const DESIRED_TICK_DISTANCE_PX = 50; + +const getModel = (config, i) => { + const { colorPalette, line, domain, dimensions, screenDimensions: layout, canvasWidth, canvasHeight } = config; + const groupWidth = Math.floor(layout.width * (domain.x[1] - domain.x[0])); + const groupHeight = Math.floor(layout.height * (domain.y[1] - domain.y[0])); + + const pad = layout.margin; + const rowContentWidth = groupWidth; + const rowHeight = groupHeight; + + const visible = (d) => layout.visibleDimensions.has(d.label); + return { + key: i, + rowCount: dimensions.filter(visible).length + 1, // the extra 1 is for parcoords + colCount: dimensions.filter(visible).length, + dimensions, + tickDistance: DESIRED_TICK_DISTANCE_PX, + unitToColor: unitToColorScale(colorPalette), + line, + layoutWidth: layout.width, + layoutHeight: layout.height, + domain, + translateX: domain.x[0] * layout.width, + translateY: layout.height - domain.y[1] * layout.height, + pad, + canvasWidth, + canvasHeight, + width: rowContentWidth, + height: rowHeight, + dpr: layout.dpr, + visibleDimensions: layout.visibleDimensions, + }; +}; + +const getViewModel = (state, callbacks, config, i) => { + const model = getModel(config, i); + const { width } = model; + const height = model.height / model.rowCount; + const { dimensions } = model; + + const xScale = (d) => (width * d) / Math.max(1, model.colCount); + const unitPad = VERTICAL_PADDING / height; + const unitPadScale = 1 - 2 * unitPad; + const paddedUnitScale = (d) => unitPad + unitPadScale * d; + const uScaleInOrder = unitScaleInOrder(height, VERTICAL_PADDING); + + const viewModel = { + key: model.key, + xScale, + model, + inBrushDrag: false, // consider factoring it out and putting it in a centralized global-ish gesture state object + }; + + const uniqueKeys = {}; + + const visible = (d) => model.visibleDimensions.has(d.label); + + viewModel.dimensions = dimensions.filter(visible).map((dim, i) => { + const domainToUnit = domainToUnitScale(dim.values); + const foundKey = uniqueKeys[dim.label]; + uniqueKeys[dim.label] = (foundKey || 0) + 1; + const key = dim.label + (foundKey ? `__${foundKey}` : ''); + const uScale = unitScale(height, VERTICAL_PADDING); + const specifiedConstraint = dim.constraintRange; + const filterRangeSpecified = specifiedConstraint && specifiedConstraint.length > 0; + const filterRange = filterRangeSpecified ? specifiedConstraint.map((d) => d.map(domainToUnit)) : [0, 1]; + const brushMove = () => GL_RENDER && viewModel.render(viewModel.panels, true); + + return { + key, + label: dim.label, + tickFormat: dim.tickFormat, + tickValues: dim.tickValues, + tickLabels: dim.tickLabels, + xIndex: i, + crossfilterDimensionIndex: i, + visibleIndex: dim.originalDimensionIndex, + height, + values: dim.values, + paddedUnitValues: dim.values.map(domainToUnit).map(paddedUnitScale), + xScale, + x: xScale(i), + canvasX: xScale(i), + // fixme remove the old unitScale + unitScale: uScale, + unitScaleInOrder: uScaleInOrder, + domainScale: domainScale(height, VERTICAL_PADDING, dim.values), + domainScale2: domainScale(height * c.splomPanelSizeRatio * 1.035, VERTICAL_PADDING, dim.values), + domainScale3: domainScale(-height * c.splomPanelSizeRatio * 0.95, VERTICAL_PADDING, dim.values), + domainToUnitScale: domainToUnit, + parent: viewModel, + model, + brush: makeBrush( + state, + filterRangeSpecified, + filterRange, + () => state.linePickActive(false), + brushMove, + (f) => { + const p = viewModel; + if (GL_RENDER) p.render(p.panels, true, true); + state.linePickActive(true); + if (callbacks && callbacks.filterChanged) { + const invScale = domainToUnit.invert; + const newRanges = f.map(invScale); + callbacks.filterChanged(p.key, dim.originalDimensionIndex, newRanges); // invoke callback + } + }, + ), + }; + }); + + return viewModel; +}; + +const plomInteractionState = () => { + let linePickActive = true; + return { + linePickActive: (d) => (typeof d === 'boolean' ? (linePickActive = Boolean(d)) : linePickActive), + }; +}; + +const updatePanelLayout = (dimAxis, vm) => { + const panels = vm.panels || (vm.panels = new Map()); + // eslint-disable-next-line no-underscore-dangle + const yAxes = dimAxis.each((d) => d)[vm.key].map((e) => e.__data__); + const panelCount = yAxes.length; + const rowCount = panelCount + 1; + const assignPanelProps = (panel, renderLayer, p, row) => { + const dim1 = yAxes[p]; + const dim2 = yAxes[row === 0 ? p + 1 : row - 1]; + // transposing the dimensions for the 2d panels as it feels more intuitive when brushing + panel.dim1 = row === 0 ? dim1 : dim2; + panel.dim2 = row === 0 ? dim2 : dim1; + panel.canvasX = dim1.canvasX; + panel.panelSizeX = yAxes[p + 1] ? yAxes[p + 1].canvasX - dim1.canvasX : dim1.xScale(p + 1) - dim1.canvasX; + panel.panelSizeY = vm.model.height / rowCount; + panel.y = row * panel.panelSizeY; + panel.canvasY = vm.model.height - panel.y - panel.panelSizeY; + panel.visible = true; + panel.xIndex = p; + panel.yIndex = row; + panel.renderLayer = renderLayer; + panel.zoomScale = vm.zoomScale; + panel.zoomOffset = vm.zoomOffset; + }; + for (let row = 0; row < rowCount; row++) { + for (let p = 0; p < (row === 0 ? yAxes.length - 1 : yAxes.length); p++) { + const gridColumn = p; + const gridRow = row - 1; + const renderLayerNames = + row === 0 // combo: parcoords row + ? ['parcoords', 'parcoordsPick'] + : gridColumn === gridRow + ? ['binningRaster1d', 'binRanges1d', 'areaChart', 'panelBorder'] + : gridColumn < gridRow // lower triangle + ? [ + 'binningRaster2d', + 'binRanges2d', + ...(c.hex ? ['hexHeatmapChart'] : ['heatmapChart']), + ...(c.kernelDensity ? ['contours2d'] : []), + 'scatterPick', + 'panelBorder', + ] + : ['scatterplot', 'scatterPick', 'panelBorder']; // upper triangle + const panelIndex = p + row * panelCount; + renderLayerNames.forEach((renderLayerName) => { + const key = `${panelIndex}_${renderLayerName}`; + const panel = panels.get(key) || panels.set(key, {}).get(key); + assignPanelProps(panel, renderLayerName, p, row); + }); + } + } +}; + +const verticalAxisSpecials = new Set(['left', 'right']); + +const setupCanvas = (canvas, d, gl: WebGL2RenderingContext, viewModel, renderLayers, textures, attributes) => { + d.gl = gl; + + const model = viewModel?.model ?? null; + d.model = model; + + const ensureContextAndInitialRender = () => { + resetState(d.gl); + d.geomLayer = geomLayerMaker(d.gl, d.model, viewModel.dimensions, renderLayers, textures, attributes); + viewModel.render = d.geomLayer.render; + if (GL_RENDER) d.geomLayer.render(viewModel.panels, true, true); + }; + + ensureContextAndInitialRender(); + + // handle context loss + canvas.addEventListener('webglcontextlost', (event) => event.preventDefault(), false); // we could log it for telemetry etc todo add the option for a callback + canvas.addEventListener( + 'webglcontextrestored', + () => { + // browser hack: the duplicate calling of ensureContextAndInitialRender and changing/resetting the width are needed for Chrome and Safari to properly restore the context upon loss + // we could log context loss/regain for telemetry etc todo add the option for a callback + ensureContextAndInitialRender(); + const widthCss = canvas.style.width; + const widthNum = parseFloat(widthCss); + canvas.style.width = `${widthNum + 0.1}px`; + window.setTimeout(() => { + canvas.style.width = widthCss; + ensureContextAndInitialRender(); + }, 0); + }, + false, + ); + + if (c.glTestContextLoss) simulateContextLoss(d.gl); +}; + +const renderPlomControlOverlay = (svg, viewModels, toggleLinePickRendering, axisMovedCallback) => { + const plomControlOverlay = svg.selectAll(`.${classNames.plom}`).data(viewModels, keyFun); + + plomControlOverlay.exit().remove(); + + plomControlOverlay + .enter() + .append('g') + .classed(classNames.plom, true) + .attr('overflow', 'visible') + .style('box-sizing', 'content-box') + .style('position', 'absolute') + .style('left', 0) + .style('overflow', 'visible') + .style('shape-rendering', 'crispEdges') + .style('pointer-events', 'none'); + + plomControlOverlay + .attr('width', (d) => d.model.width + d.model.pad.l + d.model.pad.r) + .attr('height', (d) => d.model.height / d.model.rowCount + d.model.pad.t + d.model.pad.b) + .attr('transform', (d) => `translate(${d.model.translateX}, ${d.model.translateY})`); + + const plomControlView = plomControlOverlay.selectAll(`.${classNames.plomControlView}`).data(repeat, keyFun); + + plomControlView.enter().append('g').classed(classNames.plomControlView, true).style('box-sizing', 'content-box'); + + plomControlView.attr('transform', (d) => `translate(${d.model.pad.l}, ${d.model.pad.t})`); + + const dimAxis = plomControlView.selectAll(`.${classNames.dimAxis}`).data((vm) => vm.dimensions, keyFun); + + dimAxis.enter().append('g').classed(classNames.dimAxis, true); + + plomControlView.each((vm) => updatePanelLayout(dimAxis, vm)); + + const axisTransformFunction = (d) => + d.special === 'left' || d.special === 'right' + ? `translate(0, ${d.xScale(d.xIndex) + d.xIndex * 7.4})` + : d.special === 'top' || d.special === 'bottom' + ? `translate(${d.xScale(d.xIndex) + 104}, 0)` + : `translate(${d.xScale(d.xIndex)}, 0)`; + + dimAxis.attr('transform', axisTransformFunction); + + const horizontalAxisDrag = (d, selection) => { + const p = d.parent; + toggleLinePickRendering(false); + d.x = Math.max(0, Math.min(d.model.width, d3.event.x)); + d.canvasX = d.x; + dimAxis + .sort((a, b) => a.x - b.x) + .each((dd, i) => { + dd.xIndex = i; + dd.x = d === dd ? dd.x : dd.xScale(dd.xIndex); + dd.canvasX = dd.x; + }); + + updatePanelLayout(dimAxis, p); + + dimAxis.filter((dd) => Math.abs(d.xIndex - dd.xIndex) !== 0).attr('transform', axisTransformFunction); + selection.attr('transform', `translate(${d.x}, 0)`); + dimAxis.each((dd, i, ii) => { + if (ii === d.parent.key) p.dimensions[i] = dd; + }); + if (GL_RENDER) p.render(p.panels, true); + }; + + const horizontalAxisDragEnd = (d, selection) => { + const p = d.parent; + d.x = d.xScale(d.xIndex); + d.canvasX = d.x; + updatePanelLayout(dimAxis, p); + selection.attr('transform', (d) => `translate(${d.x}, 0)`); + if (GL_RENDER) p.render(p.panels, true, true); + toggleLinePickRendering(true); + + axisMovedCallback( + p.key, + p.dimensions.map((dd) => dd.crossfilterDimensionIndex), + p.model, + ); + }; + + const verticalAxisDrag = () => {}; // noop: not supported yet + const verticalAxisDragEnd = () => {}; // noop: not supported yet + + dimAxis.call( + d3.behavior + .drag() + .origin((d) => d) + .on('drag', function (d) { + return verticalAxisSpecials.has(d.special) + ? verticalAxisDrag(d, d3.select(this)) + : horizontalAxisDrag(d, d3.select(this)); + }) + .on('dragend', function (d) { + return verticalAxisSpecials.has(d.special) + ? verticalAxisDragEnd(d, d3.select(this)) + : horizontalAxisDragEnd(d, d3.select(this)); + }), + ); + + dimAxis.on('wheel', function (d) { + const p = d.parent; + const e = d3.event; + + const { y } = e; + const panelHeight = d.height; + const topMargin = 153; + const yIndex = Math.min(6, Math.max(0, Math.floor((y - topMargin) / panelHeight))); + const inPanelY = y - topMargin - yIndex * panelHeight; + + if (!p.zoomOffset) p.zoomOffset = [0, 0]; + p.zoomOffset[0] = 0; + p.zoomOffset[1] = panelHeight - inPanelY; + + if (!p.zoomScale) p.zoomScale = [1.001, 1.001]; + p.zoomScale[0] *= 1 + e.wheelDelta / 5000; + p.zoomScale[1] *= 1 + e.wheelDelta / 5000; + updatePanelLayout(dimAxis, p); + if (GL_RENDER) p.render(p.panels, true); + }); + + dimAxis.exit().remove(); + + const axisOverlays = dimAxis.selectAll(`.${classNames.axisOverlays}`).data(repeat, keyFun); + + axisOverlays.enter().append('g').classed(classNames.axisOverlays, true); + + axisOverlays.selectAll(`.${classNames.axis}`).remove(); + + const axis = axisOverlays.selectAll(`.${classNames.axis}`).data(repeat, keyFun); + + axis.enter().append('g').classed(classNames.axis, true); + + axis.each(function (d) { + const wantedTickCount = d.model.height / d.model.rowCount / d.model.tickDistance; + const scale = + d.special === 'left' || d.special === 'right' + ? d.domainScale2 + : d.special === 'top' || d.special === 'bottom' + ? d.domainScale3 + : d.domainScale; + d3.select(this).call( + d3.svg + .axis() + .orient( + d.special === 'right' ? 'right' : d.special === 'top' ? 'top' : d.special === 'bottom' ? 'bottom' : 'left', + ) + .tickSize(4) + .outerTickSize(2) + .ticks(wantedTickCount, d.tickFormat) // works for continuous scales only... + .tickValues(null) + .tickFormat(null) + .scale(scale), + ); + }); + + axis + .selectAll('.domain, .tick>line') + .attr('fill', 'none') + .attr('stroke', DARK_MODE ? '#999' : '#999') + .attr('stroke-opacity', 1) + .attr('stroke-width', '1px'); + + axis + .selectAll('text') + // interactions + .style('cursor', 'default') + .style('user-select', 'none') + + // text color + .style('fill', DARK_MODE ? 'white' : 'black') + + // make text readable with an inverse outline + .style('stroke', DARK_MODE ? 'black' : 'rgba(255,255,255,0.75)') + .style('paint-order', 'stroke') + .style('stroke-width', '2px') + .style('stroke-linecap', 'butt') + .style('stroke-linejoin', 'miter'); + + const axisHeading = axisOverlays.selectAll(`.${classNames.axisHeading}`).data(repeat, keyFun); + + axisHeading + .enter() + .append('g') + .classed(classNames.axisHeading, true) + .style('fill', DARK_MODE ? 'white' : 'black'); + + const axisTitle = axisHeading.selectAll(`.${classNames.axisTitle}`).data(repeat, keyFun); + + axisTitle + .enter() + .append('text') + .classed(classNames.axisTitle, true) + .attr('text-anchor', 'middle') + .style('cursor', (d) => (verticalAxisSpecials.has(d.special) ? 'normal' : 'ew-resize')) // vertical drag not yet supported + .style('user-select', 'none') + .style('pointer-events', 'auto'); + + axisTitle + .attr('transform', (d) => + d.special === 'left' + ? 'rotate(-90) translate(-57, -50)' + : d.special === 'right' + ? 'rotate(90) translate(57, -46)' + : d.special === 'bottom' + ? `translate(-52, ${AXIS_TITLE_OFFSET_Y + 9})` + : d.special === 'top' + ? `translate(-52, ${-AXIS_TITLE_OFFSET_Y})` + : `translate(0, ${-AXIS_TITLE_OFFSET_Y + 12})`, + ) + .text((d) => d.label); + + ensureAxisBrush(axisOverlays); +}; + +let lastHovered = null; + +const handleMouseMove = (render, hover, unhover, canvasWidth, canvasHeight, key, readPixel, clientX, clientY, x, y) => { + if (x < 0 || y < 0 || x >= canvasWidth || y >= canvasHeight) return; + const pixel = readPixel(canvasHeight, x, y); + const found = pixel[3] !== 0; + // inverse of the calcPickColor in `lines.js`; detailed comment there + const datumIndex = found ? pixel[2] + 256 * (256 * pixel[0] + pixel[1]) : null; + const eventData = { x, y, clientX, clientY, dataIndex: key, datumIndex }; + + render(datumIndex); // rendering even if point not found, to remove previous point hover effect + + if (datumIndex !== lastHovered) { + // don't unnecessarily repeat the same hit (or miss) + if (found) { + hover(eventData); + } else if (unhover) { + unhover(eventData); + } + lastHovered = datumIndex; + } +}; + +const plom = (gl, svg, plomGeomCanvas, configs, renderLayers, textures, attributes, callbacks) => { + const state = plomInteractionState(); + + const viewModels = configs + .filter((d) => d.visible !== false) + .map((config, i) => getViewModel(state, callbacks, config, i)); + + const viewModel = viewModels[0]; + + // emit hover / unhover event + plomGeomCanvas.on('mousemove', function (d) { + if (!state.linePickActive() || !d.geomLayer || !callbacks?.hover) return; + const [x, y] = d3.mouse(this); + const { canvasWidth, canvasHeight, key } = d.model; + const { clientX, clientY } = d3.event; + const { readPixel } = d.geomLayer; + const { hover, unhover } = callbacks; + const render = (pointId) => { + d.geomLayer.render(viewModel.panels, true, false, pointId); + }; + handleMouseMove(render, hover, unhover, canvasWidth, canvasHeight, key, readPixel, clientX, clientY, x, y); + }); + + const vmSplomLeft = { + ...viewModel, + key: 1, + dimensions: viewModel.dimensions.map((dim) => ({ ...dim, special: 'left' })), + model: { + ...viewModel.model, + pad: { + ...viewModel.model.pad, + l: 120, + t: 192 - 41, + b: -80, + }, + }, + }; + + const vmSplomRight = { + ...viewModel, + key: 2, + dimensions: viewModel.dimensions.map((dim) => ({ ...dim, special: 'right' })), + model: { + ...viewModel.model, + pad: { + ...viewModel.model.pad, + l: 932, + t: 192 - 41, + b: -80, + }, + }, + }; + + const vmSplomTop = { + ...viewModel, + key: 3, + dimensions: viewModel.dimensions.map((dim) => ({ ...dim, special: 'top' })), + model: { + ...viewModel.model, + pad: { + ...viewModel.model.pad, + l: 120 + 10, + t: 192 - 41 - 10 + 1, + b: -80, + }, + }, + }; + + const vmSplomBottom = { + ...viewModel, + key: 4, + dimensions: viewModel.dimensions.map((dim) => ({ ...dim, special: 'bottom' })), + model: { + ...viewModel.model, + pad: { + ...viewModel.model.pad, + l: 120 + 10, + t: 1008, + b: -80, + }, + }, + }; + + const extendedViewModels = [viewModel, vmSplomLeft, vmSplomRight, vmSplomTop, vmSplomBottom]; + + renderPlomControlOverlay(svg, extendedViewModels, state.linePickActive, callbacks.axisMoved); + + plomGeomCanvas.each(function (d) { + setupCanvas(this, d, gl, viewModel, renderLayers, textures, attributes); + }); +}; + +/** @internal */ +export const renderPlom = ( + gl: WebGL2RenderingContext, + svgElement, + glCanvasElement, + emit, + configs, + { renderLayers, attributes }, + textures, +) => { + const svg = d3.select(svgElement); + const plomGeomCanvas = d3.select(glCanvasElement).data([[{ key: 'focusLayer' }]]); + + // const data = [[{ key: 'focusLayer' }]]; + // const svg = d3.selectAll(d3.select(svgElement)).data(data); + // const plomGeomCanvas = d3.selectAll(d3.select(glCanvas)).data(data); + + const dimensionsArray = configs.map((vm) => vm.dimensions); + const dimensionsArrayOriginalOrder = dimensionsArray.map((dimensions) => dimensions.slice()); + + const filterChanged = (i, originalDimensionIndex, newRanges) => { + const dimension = dimensionsArrayOriginalOrder[i][originalDimensionIndex]; + dimension.constraintRange = newRanges.slice(); + emit('Filter Changed'); + }; + + const hover = (eventData) => emit('Hovered', eventData); + const unhover = (eventData) => emit('Unhovered', eventData); + const axisMoved = (i, visibleIndices, layout) => { + const visible = (d) => layout.visibleDimensions.has(d.label); + const newIdx = (visibleIndices, orig, dim) => { + const origIndex = orig.indexOf(dim); + let currentIndex = visibleIndices.indexOf(origIndex); + if (currentIndex === -1) { + // invisible dimensions initially go to the end + currentIndex += orig.length; + } + return currentIndex; + }; + + const sorter = (orig) => (d1, d2) => newIdx(visibleIndices, orig, d1) - newIdx(visibleIndices, orig, d2); + + // drag&drop sorting of the visible dimensions + const orig = sorter(dimensionsArrayOriginalOrder[i].filter(visible)); + dimensionsArray[i].sort(orig); + + // invisible dimensions are not interpreted in the context of drag&drop sorting as an invisible dimension + // cannot be dragged; they're interspersed into their original positions by this subsequent merging step + dimensionsArrayOriginalOrder[i] + .filter((d) => !visible(d)) + .sort((d) => dimensionsArrayOriginalOrder[i].indexOf(d)) + .forEach((d) => { + dimensionsArray[i].splice(dimensionsArray[i].indexOf(d), 1); // remove from the end + dimensionsArray[i].splice(dimensionsArrayOriginalOrder[i].indexOf(d), 0, d); // insert at original index + }); + + emit('Axis Moved'); + }; + + plom(gl, svg, plomGeomCanvas, configs, renderLayers, textures, attributes, { + filterChanged, + hover, + unhover, + axisMoved, + }); +}; diff --git a/packages/charts/src/chart_types/plot_matrix/plom/lib/sample_data.ts b/packages/charts/src/chart_types/plot_matrix/plom/lib/sample_data.ts new file mode 100644 index 0000000000..0fd36f3422 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/lib/sample_data.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* The below file has been partly derived from src/traces/parcoords/parcoords.js as of March 2018, commit + * Original MIT license in the plotly.js repo as of 40b0c2bce9f8705e33c5badb6888131dcd74bf17 + * The file heavily depends on D3 DOM manipulation + * todo: replace this file with a simple React tree and plain functions + * todo: alternatively clean up the copyright block + */ + +/** + * Copyright 2012-2018, Plotly, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// prettier-ignore +/** @internal */ +export const sampleData = [ + { + label: "Block count", + originalDimensionIndex: 0, + tickFormat: '3s', + values: [32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,32000,162666,86600,163400,162600,90000,93100,163000,140500,130000,100700,164500,147700,121700,107500,176600,133600,111100,93100,72400,130100,54500,182600,160300,218100,66500,95800,164800,107200,101600,91100,91100,78100,63300,75700,69600,88500,115800,195700,88900,74800,65400,74900,115300,91400,67800,85300,171300,32000,227900,163200,122899,112300,101500,111199,73300,120800,93100,117200,118500,104800,108500,146500,90300,32000,93000,110700,103500,93300,106300,118500,93000,67600,51400,89000,94000,88200,109600,102100,123600,81900,100000,218100,223200,97300,162100,97400,145800,67400,156500,201900,94300,67800,76100,98300,112400,141800,111400,74700,67300,71100,68900,62100,73800,85500,94400,68300,79400,96300,70400,67300,67300,128400,80800,87600,165900,35900,76200,79000,38900,157200,64400,67700,65700,125800,59300,66100,198700,96100,65600,70300,69200,65500,129300,62600,64200,61800,60800,83100,58800,69600,60400,63500,63500,59300,56600,77200,62900,57300,68000,227900,53099,59100,63099,65900,60500,158100,60199,66600,57699,58900,62699,62000,58600,78700,60700,60400,66000,66000,133300,60800,63200,60400,68400,64600,61400,61900,59000,155600,53500,62000,60599,63700,118500,48000,57400,58200,56800,62300,54500,61300,62200,53200,51600,88500,125100,60800,61500,59200,56300,60800,179300,58300,199500,102100,60800,183200,58800,125000,56900,58600,53700,56100,127600,96100,56700,120500,58600,53600,59000,113800,59000,59200,54500,135700,161200,57300,53000,127000,54600,55300,89700,59400,54900,64700,56200,127100,57000,35400,51800,170100,54000,56700,73600,83200,53000,60500,52699,51600,92100,56600,57400,65500,58400,97600,52900,56700,207600,221900,56500,57600,63900,54700,56900,56000,49700,51600,53400,58500,65000,57100,55900,56100,59800,55300,57699,57600,53200,53900,63000,58400,57500,61200,62699,61000,60199,57600,128800,57699,59900,94200,60900,55100,56300,54100,61400,55000,223100,57699,52200,55300,54000,61000,54800,59500,59000,54400,57800,57100,53200,56300,58300,56100,53900,55100,54100,59500,188900,92300,105500,67100,100000,202500,101800,55900,86800,55300,56700,59400,51400,49000,54600,49400,65300,56800,55599,184500,55000,55000,86900,60100,89700,52500,52500,55500,91700,104600,67500,50700,63300,55400,54500,62600,52699,54100,53400,54800,53200,59900,74500,134800,52400,105300,53700,215200,101700,57200,54400,60300,50800,129400,55100,165700,55000,51700,56000,54000,56500,54500,56000,78600,56300,54700,59600,58300,58800,52400,54500,57100,119100,50100,56500,58900,66500,55599,225400,57699,97200,57000,212900,57300,101600,216500,55400,65800,53000,164200,124600,86300,56000,95200,152700,208299,32000,60400,60700,196600,55000,77200,37500,52000,55300,54100,53500,52800,55100,60100,125000,62400,63700,106900,54300,54100,217500,56400,69700,101000,57500,55100,153500,53700,54700,58099,53900,37000,53200,62000,52100,53900,54500,61600,59700,218200,54700,189200,55599,70600,63700,107100,68800,55700,56400,57400,32000,83200,61200,217900,62100,62400,60900,60700,56300,57600,105399,60000,56700,221200,157700,58200,58099,54700,60900,61300,162600,60900,224600,57699,52699,59600,71200,52100,57500,60500,134900,67200,63700,55000,65199,161600,80300,70000,73300,56200,68400,183700,180100,56400,77600,197800,215799,56600,73300,57500,57500,55199,54800,55900,63300,106400,163200,70800,71900,166000,114100,100900,164300,93600,150000,98200,161400,200400,92600,127600,97600,84400,96600,55599,100500,112000,110000,89800,97700,84200,79600,102400,84800,86900,166600,91300,79600,78100,85800,88200,104200,76600,83300,110800,75600,76100,113800,86400,86800,74800,77100,95200,86900,172300,69700,87500,79600,65300,80800,82400,67100,79400,78500,84800,76100,47200,79400,78200,220300,66100,78300,85900,92100,80800,74500,72800,36200,75800,77400,69600,62600,90700,70700,93100,40400,62600,94400,73600,72000,138200,75400,73300,170700,75500,144500,76600,72000,78200,33200,74100,67900,35300,72000,69900,68600,69400,74300,73400,128400,71800,217399,86500,174200,67800,86500,136800,125399,75200,108000,72700,223900,67300,73600,86500,83800,70900,72900,71300,71800,88600,60300,77200,65300,63300,68900,127100,70700,223600,68700,66000,70800,69000,60599,65700,64200,150700,65000,68700,75800,154200,63300,103400,70200,66400,69200,71000,65700,62300,70100,72200,68700,64600,67600,61800,68100,129200,54900,77600,169700,67100,64100,69300,65700,64600,71800,65199,61800,198400,208800,72100,59800,66200,62900,152500,72200,57600,61300,61900,54900,177200,79500,60800,67000,61100,155600,63400,61500,65100,150000,64500,56800,59600,59000,63000,59000,56700,59000,58400,60700,56700,56900,54900,54800,52200,54700,54800,62000,134700,115700,59700,51500,66900,55599,56400,52900,56800,52900,58200,57699,59100,53200,55800,53300,60400,58200,107500,55500,56800,54400,56100,201400,56600,176200,84400,54100,214700,55800,123699,173700,73600,55199,169200,82600,56500,62300,59100,151900,122700,66700,58200,56400,57400,55000,56000,111900,60400,149700,71700,56600,93800,178700,80900,54700,87700,118900,52300,195900,190200,58000,51300,211300,54500,56600,56900,111800,54900,79400,69700,62800,78600,57600,55199,57000,56600,57200,68600,53800,53900,54900,122500,63500,179300,57800,55700,89400,61800,32000,79000,214100,61400,56000,54900,61900,55100,58800,54400,82600,176300,56600,55100,59100,141700,60100,54500,71700,58900,58800,52100,60000,56900,77700,62600,57900,53300,52400,59800,218900,70700,218200,144700,53300,57200,53900,54000,55400,107700,53800,121300,52400,54600,199500,118200,80900,202900,56600,53400,52100,56600,222600,50300,65100,54200,54600,60199,183700,134800,146300,62500,52600,56500,53099,180700,57800,53500,52000,54300,87300,57699,54300,64400,68500,213299,57300,51800,56300,50000,67200,49500,51100,63500,62400,165500,49300,51200,58099,77700,40800,55000,63500,78000,208000,56900,58099,53900,83600,55599,53800,86300,55100,55700,58400,59100,206600,55000,51100,61600,187000,54400,57699,55700,56800,54000,52200,53700,57100,55100,56600,76700,56400,58700,58200,58900,56300,182300,59500,57200,52300,54600,77100,54300,57400,54200,54200,55100,54600,57400,57500,55900,54900,54400,54400,54600,82300,54400,41600,56500,54000,55900,53400,55100,57300,55199,53500,56300,57500,55800,64600,53900,54100,58500,49700,53400,59500,53700,56900,53700,57000,53800,51100,55900,54700,47900,53500,57400,54700,88600,58400,56900,53200,127200,58900,57400,56600,52600,121800,56900,44700,120600,90300,61300,60700,58900,56800,120300,57600,54700,56800,62600,85700,86700,52400,83900,51800,64500,51100,59200,56100,43000,53700,56000,63600,56900,57800,54100,53700,116199,52200,73800,56100,157600,54400,54600,53300,68400,55100,198900,54500,52800,51600,225100,129100,63800,55400,157100,54300,53800,69400,55700,72100,54600,71900,54300,53800,55700,53900,60900,52300,53000,56300,53400,54300,179100,57500,54300,84000,53300,53500,51500,54300,37700,105600,55100,53600,206700,51700,54200,64900,52600,57699,79900,52500,51300,53900,54100,59300,54100,55800,57100,63800,56800,65900,54400,56900,79000,59300,44200,53099,57500,49300,123600,180700,112500,97500,58600,220400,54000,55300,53500,51900,172200,58600,99300,65900,54800,52900,148300,53099,65300,50500,86100,54500,109600,60400,90200,56500,56500,73100,153500,120900,118300,144300,57100,52500,53400,51300,98900,54800,53400,54600,101100,54500,136800,54700,50500,55599,52200,54000,57699,46900,208800,105399,55500,32100,174200,32599,52100,104700,137900,61300,46100,151600,55700,191100,168600,107500,59500,58300,83100,65400,60100,65300,73500,63900,59600,58700,57800,175200,159100,56100,61600,57800,58000,204800,52500,60800,51200,56900,158200,193900,56200,154800,58500,54100,45400,53500,57000,58900,57600,55100,130399,59800,57400,59600,161700,52600,57800,57600,57400,55900,55500,59600,138800,212200,102300,66700,39800,128400,53300,75600,59000,45300,65100,55700,59100,160900,111500,170700,56600,57500,61000,53300,42900,82100,54700,197200,141700,53800,72400,72800,227900,183900,53700,120700,92600,51800,54100,57100,120700,99900,52600,65300,59900,112899,56800,53300,57200,139700,63500,65800,64600,50500,55700,55900,55800,45800,63800,56400,151500,200100,51100,56100,55599,64500,192300,76100,55300,55199,48000,65800,66300,64400,127100,54300,57000,56100,52000,91000,51800,206800,91900,196200,65199,220500,214899,85700,50800,56000,57699,188500,72100,67200,67000,53000,86100,147100,92600,63500,99800,209400,32000,66500,113699,86800,65100,64000,62400,60300,144700,73100,67200,42100,78400,58600,61800,67900,56500,56400,59700,59000,51900,60100,58300,54900,67800,55700,55599,58400,59800,62400,51300,59800,41700,51400,59700,61700,56500,147400,56500,98300,183500,58200,56200,56800,82700,52900,50700,55700,53500,58500,117100,180400,61500,154900,54900,225000,59900,123300,99300,63000,136000,57300,93900,127000,50800,102700,133500,93200,150900,113300,111000,53300,85600,68800,54000,181900,193000,76500,70500,50900,62100,113600,66700,49800,182900,51300,71000,67100,51800,61300,68500,67500,153900,98200,69700,101100,61700,55700,62800,61100,57600,204200,57400,56400,56800,188400,79500,115600,46300,62300,63200,60500,61200,94900,59400,85900,63800,61100,131300,86500,59400,80800,64100,58000,59100,47300,67400,57500,62400,95200,65600,57600,171100,62300,54100,60900,97200,65000,45800,61200,64300,43300,32700,62100,69500,58400,61600,223700,41200,60800,91500,226800,58200,89200,64800,63200,71900,52400,59600,52000,39700,65600,148900,59600,50000,136800,62900,64100,58900,135400,63099,58600,53200,55599,117800,73300,70900,72100,55100,72400,67200,53600,55000,63800,56000,53600,54000,68900,51200,57500,56700,54700,56100,53700,58200,135500,57300,182200,57900,85200,121100,58600,111300,60199,55300,57500,118400,222000,123200,193400,131300,77300,57800,70300,58400,63099,118900,79200,52800,74600,58600,172100,86500,55400,65300,57500,60100,50000,110200,54400,49600,51900,62100,66100,63000,44900,67200,59400,54800,56200,179700,56600,63900,56900,54900,59800,58500,55100,55000,56100,57100,146400,83200,54500,57600,53600,56600,57200,56200,110200,51700,87300,57200,73700,57000,58600,58700,177800,54400,60599,58000,53000,68400,147200,103600,53900,56100,54700,57699,155400,57400,225799,56100,202000,52100,55900,80100,59300,59000,89200,65300,86100,78400,58900,63500,63900,55000,55599,64700,68200,53200,60900,93600,132900,67000,61400,64600,42600,93500,54800,55900,56100,54900,59600,58800,137400,87500,59300,54400,51500,183200,57200,55900,65000,58000,50500,48700,58600,60500,140700,122500,192500,65300,122600,59600,162700,106700,61300,60599,57699,74400,55100,49600,49800,190300,80200,81600,58000,128100,40300,64900,81300,61900,55199,59900,59100,57000,55800,57600,56300,68800,69300,58800,145700,58600,164700,61000,55700,168000,139200,167700,172000,65600,155200,46200,84200,64000,54000,51600,54200,95000,47200,58900,140400,53400,52400,56500,51500,223800,181600,65800,92700,114800,169200,138500,59300,60300,87100,63600,83000,53600,55300,53600,140500,155100,66700,56000,57699,60900,173700,84700,86100,104700,65700,61300,38600,57500,59000,61100,57100,57000,77500,58800,173800,73600,71200,69400,61200,60400,55100,60599,53800,58500,57400,59200,58099,62600,57699,168800,72600,58800,65400,35800,152500,32000,60100,60300,57200,223900,55900,54200,147200,55700,57300,68800,56800,60100,58200,58600,56100,74800,54500,70600,49400,132700,56700,57300,57000,189800,46300,86100,58700,62500,38900,74000,65500,73200,188900,71000,60199,81700,50100,221900,52500,53099,163500,61000,69000,51800,51200,197600,56600,61600,68800,56900,59700,47800,54600,54800,62200,69300,52300,36000,213600,56600,94200,52500,39100,49600,181000,64200,57300,60100,169800,54400,55400,62600,112700,118500,58000,61100,52600,85800,84400,57600,58800,62200,75200,95900,51800,60100,56500,87300,54300,56300,51500,68300,56700,49100,56800,127700,59400,54100,66900,50900,94900,62900,56700,71300,55700,57600,184300,100500,63000,39000,69400,65100,32000,69300,113500,60000,80100,87600,62100,216000,74700,64300,65500,61700,54100,135300,73100,55100,61500,59200,60900,63600,62200,105800,63500,49500,214600,63400,59900,95600,62000,64000,58099,63300,35500,58700,63300,44200,55599,56000,32000,60199,57000,86100,74700,49600,59100,57400,65400,55900,60700,53200,177300,53200,52100,92700,74300,73900,129600,64500,52699,56000,60800,58300,76400,57000,65500,177100,66800,49900,58900,57300,66700,58200,61600,62100,68200,55199,64300,53500,60199,60700,47600,57000,94400,95200,59600,61200,161400,51800,56900,59000,165500,69500,61800,62600,205200,52600,58800,61100,55599,58600,53900,60400,53400,115399,50100,58700,56300,37900,61800,63400,75100,52400,59800,113300,210700,55800,138900,59600,185300,102700,42100,54900,87200,40900,58200,135400,51700,62500,171300,109000,59800,102700,62600,77800,198400,61200,65400,55100,153700,165900,62000,58600,71000,56500,63099,40900,83200,60599,73800,59800,70900,58500,61300,56300,55100,58000,57800,38000,53500,61800,53800,55500,54300,56000,54700,55700,71500,179800,53500,64100,58400,63700,55500,54600,60800,64300,58800,56600,186600,58200,61500,35100,60100,212399,58600,171800,58300,96700,56900,66400,60300,58500,71300,65100,56300,59600,196000,57800,60000,42600,181600,57300,195500,54200,55400,61500,44500,55000,56300,66700,52699,143500,54400,58400,56300,53300,191900,38000,76000,111000,198900,160900,54500,55400,56100,55000,81500,57100,119400,74200,55700,70200,57600,58600,134700,53000,55100,60199,224100,58000,62500,56500,134200,60300,73800,209400,57000,55900,62200,55900,57500,159200,61200,55300,168000,55700,55300,219200,76600,128800,211000,87500,58600,57500,56300,202600,56200,61100,56800,153900,88400,97400,166600,54900,220000,81800,98600,66900,86500,152300,59100,86400,73100,55000,57100,54800,65500,138200,177700,51600,108699,67000,61500,45500,58500,55400,133800,59200,213100,66600,122100,97100,92100,135300,49600,73000,58200,51900,61400,60700,58099,53300,192600,55800,121500,139700,40100,115700,54300,56000,58400,54400,57699,98800,214800,161400,39300,56900,41700,57000,39600,65600,56600,65900,219100,55800,54300,56300,52600,61100,60800,65500,53700,57100,54800,52500,46500,50900,55199,46300,57100,56300,58900,53500,59800,217399,59300,185900,43300,59000,51700,133000,75300,58000,57699,97800,117400,57200,185500,53800,56700,115600,170200,65900,79000,55199,100000,96700,123699,76700,169400,136700,71400,61000,127400,58800,62400,48400,44200,55599,91600,67000,55599,62800,58000,65600,61700,59300,54300,201700,62699,108400,64100,71100,59900,56300,227900,54100,53900,61700,165200,59100,115800,62400,92400,55599,59500,58800,60500,57400,80700,58500,56500,59800,60400,63300,55199,118900,63500,195000,54700,53099,67100,55700,219200,56900,70300,156000,83600,95100,210200,67000,48000,56700,172100,59400,62800,195400,133800,44800,54900,57300,63000,95000,57600,32000,138600,71000,61200,73500,53400,194800,56900,49200,51900,60300,60900,57000,116100,61300,81000,205000,57800,117000,61300,51800,54300,58600,57800,61500,82400,55900,156600,59300,227800,55000,54400,52000,58900,54100,55599,177500,92500,57100,148600,53400,52200,113200,57300,87100,227100,215200,195700,144300,91500,148700,91100,198200,52600,108500,90700,68600,56500,174600,103400,215600,60500,41600,102899,81100,121300,79200,67400,64400,109400,57000,61400,54800,56300,41500,175700,140300,61000,68200,59700,84600,43300,82600,112300,34900,168200,71700,79800,60800,79500,61700,60800,54800,69300,60100,64100,64500,52100,58600,58900,56200,65500,58700,70400,216700,103400,62699,64600,66600,56300,66300,73800,50500,170100,122000,55300,56000,58099,126500,52200,147000,65900,193200,115300,62600,65900,202300,52800,53900,101000,95000,53700,73300,84400,227900,54300,93200,60500,38400,57300,61400,170400,224500,57100,95000,53400,208900,138800,73200,59600,223600,70400,111900,72900,100500,76000,71600,68000,71100,67700,75900,74200,72800,66500,69400,66200,59400,61000,52300,62900,71200,55400,67000,59300,63200,64500,65100,222600,183400,59600,40400,55599,58500,125000,64700,115399,66400,54500,81700,124400,60500,56300,53900,32000,64200,64800,58300,56800,65000,62800,62300,53200,58300,61300,58600,64400,58099,55700,158400,155100,49700,170000,213500,75600,162300,59900,47000,223600,187300] + }, + { + label: "Block size", + originalDimensionIndex: 1, + tickFormat: '3s', + values: [268630,489543,379086,600000,489543,268630,600000,379086,268630,489543,379086,600000,489543,268630,600000,379086,268630,489543,379086,600000,489543,268630,600000,379086,268630,489543,436900,373600,268630,439000,381800,491200,402800,381400,485600,600000,372200,394700,383800,401100,356500,313200,487100,490700,432299,530300,521500,456700,333800,343200,394000,328200,487700,383600,317700,324600,482900,515100,500100,562700,486000,453400,352900,331300,496200,469099,472500,354700,507700,509000,439300,525300,347600,379000,432800,378800,383200,523800,600000,313600,497700,411300,420000,381100,342800,385900,268630,495800,488000,446400,397900,373300,385700,396500,475700,510600,586800,596000,407600,439200,440800,393100,274600,291900,509799,432800,303000,530600,515900,376800,377400,553500,433600,270800,288000,488000,306800,418000,456900,526300,524500,524000,418800,442800,443200,497200,427500,510300,476000,382700,472200,380400,472900,541100,528200,441599,374600,414099,437299,272500,550000,417299,443700,268630,545800,403400,534400,557200,545200,537400,464300,571200,589400,564000,466500,514500,433200,293600,550100,466599,450100,557200,489000,512200,445600,531000,504400,459799,528800,579800,352900,520700,532000,477900,395600,478900,461300,481400,507500,487200,522299,496000,499600,485200,491500,548300,526000,489600,444000,500900,527600,533200,450000,566500,486300,500700,534900,493000,481100,482500,490900,510200,584700,560700,492000,474600,495300,594100,433700,522500,513100,507500,488100,506400,510400,482700,500400,532000,477700,369600,491200,499400,489200,509099,513600,479600,507299,547600,415300,485900,410400,507700,546700,503300,503600,547700,509200,522400,319700,517200,473000,506800,511400,559200,537900,515300,487500,519000,361100,270800,519000,518200,508200,503900,516500,508600,504900,506500,505300,522000,507500,521400,504600,521400,418700,516700,508400,515400,387000,522299,557300,516599,510600,383600,514700,516700,515600,528200,491500,517900,505000,430300,425300,517800,512600,511599,511300,501599,519099,359700,522400,510400,501100,524600,516000,513800,503100,517000,511400,513700,504000,506900,517000,535700,572700,497900,501900,512600,513800,515000,520700,295100,509700,522299,338800,521300,517200,519500,520800,509300,520700,356000,523000,513600,525100,515700,510000,528300,565200,509300,515500,526900,514200,522200,518500,557400,565100,530700,511100,538200,483700,588800,285200,569100,520800,406300,278300,569500,513900,289000,519600,513600,526900,525500,530300,502000,480000,518500,513000,516900,507000,534300,520400,488500,536000,518900,519000,509400,535500,533600,384900,542700,517200,535700,520500,517100,538700,525900,524200,517100,523600,512900,542400,306800,544600,516599,582200,519900,510900,427200,521300,525900,438200,523500,480000,519600,325400,523300,525200,526500,516400,523800,525400,518700,530800,526400,524400,566400,519500,519700,518400,580400,520600,567600,585000,519300,524300,520800,520900,533400,529500,583500,531400,450000,557600,420700,402900,524500,518700,521800,316400,497100,518900,504400,539400,507000,339700,493800,496700,517500,523400,524400,526200,311300,519200,327600,520400,524800,526400,519200,485200,361000,519300,574400,330400,511800,511200,592800,520900,390400,322200,524600,513300,542400,517200,527300,512700,516100,570800,508900,523500,543500,513700,519700,469000,557400,515000,511000,382700,521400,497900,571100,514099,461100,505600,563300,509200,446300,458000,518000,316500,525600,515700,468600,516300,515400,499000,487500,486900,519099,270200,290900,518000,509900,519300,517299,468700,582300,517200,461100,558600,490500,499099,507100,517600,535200,512100,375300,506100,502200,519600,559900,531200,508100,553600,510700,538600,525200,286400,581800,534900,503500,345400,428300,539400,552000,523400,516300,514099,519200,515800,508000,500400,374900,529900,527100,600000,309600,583500,600000,376000,473900,484799,419300,287200,596600,464600,398900,383900,487000,431900,490700,600000,381100,515800,381500,540900,539100,416500,428800,439300,441300,496000,550800,414300,422100,598200,387800,561100,364000,359700,523400,455300,600000,380400,424000,458600,382500,401100,425100,550500,436599,493200,393200,437900,401100,386300,432299,444799,428200,368800,415700,369900,413400,413000,482100,436000,368600,369300,401300,400400,418300,432299,278800,436100,411800,431300,551100,406300,541500,404200,547200,437000,390200,417900,415800,460600,434900,442600,309300,452000,272200,459700,423300,414799,468900,429300,436700,427400,438800,421100,421700,434000,470700,474799,521599,414400,307300,416700,579600,432400,566100,416200,432500,483100,373200,494799,507400,489700,440800,389200,436599,418000,446500,450700,445200,594100,443900,489000,493300,448900,449700,406000,437400,278100,453400,509500,433000,431200,308600,507500,452500,524700,451700,468700,423600,447200,472600,418500,466000,457000,442299,455000,458400,463800,450900,452900,544200,456500,521700,490900,457200,502500,499099,446800,597000,440600,478700,428700,556900,498300,442000,437900,419000,486000,490700,463000,505500,496700,486200,528300,434200,502600,502400,477800,470500,533400,520700,508400,346400,498900,511300,486900,499300,508100,545700,458400,524300,504700,497299,454300,501100,499900,497800,506900,494500,501000,515300,515000,505300,520200,524099,502600,483000,413400,562300,511700,523500,478800,514600,511100,505500,505300,519099,499600,506900,510500,519900,512400,520400,504500,500900,433200,515900,500700,517100,508200,505900,498900,274800,523200,518100,434600,491100,439900,508300,338800,513900,464600,524000,511100,565900,503700,331700,484200,533800,530700,519799,544000,513200,466100,484799,537100,541300,513500,515100,495600,580400,514600,517000,519600,552000,538400,399700,448000,522100,503600,353100,511300,515700,487700,421700,509200,600000,422400,523700,526700,513000,521500,516400,518100,513300,516700,524400,499099,517299,428600,514700,540300,507500,517200,464900,519099,512100,537200,440900,507900,522500,514799,513900,523500,510100,525100,548400,361100,512700,518100,524400,588900,527800,528500,341500,500100,510300,516000,504200,502900,424200,506599,502700,511000,512200,524099,595200,415700,460400,361800,519700,514300,527300,516900,512900,532100,514900,312800,510400,515100,518500,383900,269400,547800,515000,515100,514300,518700,316300,508700,503200,512700,511599,515500,506599,384900,362200,527700,527500,515600,510100,289800,517400,510600,531900,513800,530100,517600,522700,522900,525700,376000,520100,512100,513900,512400,528600,521900,498500,530200,514700,494200,315900,527900,514700,521599,521599,526700,516400,507900,542600,518600,521500,523500,459700,513400,517900,515400,521400,512900,517600,522100,274700,520800,511599,517000,428600,520500,519799,522800,526400,506700,514000,513100,517900,520200,517600,583600,526800,524400,523900,515900,523300,498700,524200,519200,517500,525100,549400,526000,517200,516900,520700,521000,521300,525700,527200,519400,518600,516900,519600,517400,496300,527400,322400,518300,519200,518400,523400,523100,516599,516100,517900,512500,514600,519500,514200,517600,520200,519200,515200,520100,517000,519600,509900,520900,519799,519900,518200,512400,517700,526200,516700,519799,519900,382200,518900,520000,518200,488700,523600,522299,522299,521900,295200,511599,499400,526900,572800,478500,570100,520000,516599,530600,509799,526000,512000,491599,530200,581800,525800,529700,522200,598700,517400,508500,510200,428700,522200,507100,517299,527600,534400,526500,520700,486900,526200,525600,514799,510400,514900,529600,520100,330400,520800,401100,520600,516000,525100,388800,404700,525800,526400,480100,525700,515300,391100,518100,431599,527800,430300,517400,518700,523500,512400,514300,516100,517299,508200,520900,527200,296700,514400,520100,289600,513900,528000,522700,527600,308500,415200,522800,527500,402800,521400,523300,518800,521200,522200,547400,525300,522800,518000,524800,518300,518300,534100,518400,530400,512800,520500,524900,522500,520200,537200,307300,517200,527300,514099,557900,521599,525600,492800,533800,359300,522299,546000,514200,401500,475200,523900,394000,517800,525400,517600,382800,513200,414900,519000,517100,525300,421700,515300,453800,522299,525300,522200,297000,402100,440400,462900,517700,514200,522299,526700,382000,513200,519799,521000,436200,516400,490400,514099,526800,510600,513400,509900,518900,512400,500400,521700,517100,390400,589900,327700,517100,499799,565300,522299,589000,495000,519099,502299,433100,413000,540000,503200,563400,521599,553100,511500,503200,558600,510200,499799,510100,308500,410700,518400,522700,532000,502400,386700,512700,538900,522900,516300,396900,545300,517400,393900,495600,529600,552900,516900,519300,521800,495100,513500,583300,516800,516300,526400,550500,526300,517100,516800,510400,525000,526500,518200,414300,548400,395800,447800,419799,530100,532300,493200,522600,373100,445500,530000,520500,560400,440000,297700,524200,526100,518600,528400,400700,503000,508600,545500,555400,520600,494900,473200,521599,471700,509099,526800,510800,525700,516900,498300,595700,526300,520600,525600,519400,380300,514099,506100,524000,321100,522500,520000,520600,529700,521800,510300,520500,385100,522200,518300,577600,588400,536100,513100,518000,419000,480700,392500,515900,517400,353900,520800,448300,511200,502700,518500,512900,521300,519799,535700,512299,407000,517100,322600,518700,297300,366500,600000,515200,516300,490700,408800,525700,504300,509600,521300,481400,359700,371300,511900,504300,451300,467600,507700,532300,470600,516800,590300,522000,526000,463900,455600,518500,406100,475600,529000,547300,529000,531400,517500,527800,526600,459500,494200,500000,487000,469000,511300,511700,508600,504500,519900,521700,522299,400400,511100,513900,513900,511100,408500,498900,490600,559100,514200,505500,506300,503800,516599,524200,510100,510600,505700,395700,523100,506599,542700,512700,366700,511800,386800,397700,511000,404200,518300,499400,520500,511800,555900,465500,268630,582200,305800,500800,518400,472200,512700,483300,405400,573000,465600,516500,476000,472000,546000,496200,503400,414000,516800,514000,520800,523700,502900,513900,515400,318200,511500,469900,379700,515300,504200,506500,495500,523200,297100,508100,525600,522800,321100,507299,423900,536700,394000,527100,506900,515400,447800,509900,490900,508000,493400,523200,429900,486800,522100,505400,517299,506800,448600,489000,496800,504300,486200,500300,525300,278500,501900,512400,510400,530000,511500,370200,507600,504500,373400,485300,497600,504200,501100,505400,587500,373900,511500,492299,560600,506500,421500,506100,509099,509300,508600,511300,515200,376500,499900,558600,500200,514400,293900,470700,407400,490100,375800,406100,506100,520900,514099,415600,506200,581600,435400,512500,432800,504200,513900,529300,506000,507000,518400,520600,515900,512200,508200,517900,497500,497100,523300,506800,471000,500600,269500,508100,512600,584400,505900,425000,501900,513300,526800,584700,472500,287400,573200,510700,492200,517600,506300,502800,509200,514400,508800,520600,518200,515100,340000,510500,521000,517400,516800,519900,476700,357200,514000,404600,519300,514900,501200,521200,559700,503000,514500,518100,515100,458600,519200,518300,530500,501100,520600,503800,521599,527200,519300,534200,433200,521599,512100,510400,518500,511599,516500,505600,594900,528900,407600,510400,425800,517100,508700,516599,543700,520400,506300,511599,523000,554000,332400,379900,511500,525100,513700,516400,513600,503400,510400,511200,352600,515200,524000,382400,521900,536300,445300,427200,508700,523800,518800,538100,470400,511300,520900,518700,516200,520700,526600,367700,362200,507200,528800,508200,380900,372500,520300,521500,517900,464000,525900,522299,345600,597300,549000,507000,508800,569100,513400,511599,539300,519700,517299,519300,507600,523200,493300,465700,577300,538600,589000,460100,325400,440200,552600,537500,514200,528100,474900,460000,463300,401500,340400,340200,518700,517400,560500,505200,276800,510800,530500,513100,515000,523300,512000,510600,517400,365200,596600,517500,358600,515700,545500,553000,505000,487000,484700,349200,488700,532400,436200,411300,492600,477600,511000,506500,517299,358800,400700,555400,494900,372200,507800,511300,520300,542100,355900,571500,552800,467500,430700,339400,564100,600000,478700,454500,554800,458400,519300,521700,484099,359500,567700,535200,555500,541900,520500,479700,501100,533700,554300,552300,379700,522500,404900,508300,538600,573200,532700,557100,565900,518500,309200,522100,538500,509400,533200,539900,511900,535400,519900,542900,556800,523100,505900,382700,527400,451500,518400,479300,535600,493100,516300,510800,529600,274700,516500,522400,470500,526800,519300,401300,519000,518900,441599,526800,510100,349400,505000,531900,461100,272200,521500,514799,519200,535000,424300,505000,525600,510500,375100,407500,408600,425000,583700,420700,508100,574900,513700,324400,512299,522900,552100,510800,418200,498000,510900,444700,528100,526300,504099,521300,509500,565800,509700,531100,512400,506400,528700,368900,346700,520300,599300,519400,364400,520100,445700,520200,531600,504500,295700,500200,513200,460600,547600,481400,515700,518100,499700,337200,330600,517500,525300,479000,460400,536200,470500,516100,506500,482200,507800,499600,519099,504400,518800,331800,498100,469400,486200,518400,504099,518000,399400,511000,512200,571100,518000,524900,447600,368500,511200,382200,521000,513400,515600,514300,529500,519400,347900,516200,522900,295600,464799,514099,465600,512000,522000,334300,401000,522700,514200,513900,459099,507700,510500,316200,518900,550100,458700,513200,480300,445900,513400,519000,510700,519000,448300,486200,514400,441700,471200,537400,381700,503700,484799,485800,483000,505100,496599,502100,497800,518000,502500,517100,363100,511400,489600,484900,485300,508000,419300,473900,511800,505800,483300,503700,501200,515200,486100,589600,479300,520400,517299,513300,522100,451700,510600,495600,434900,513400,498600,527900,507400,500300,489300,500700,290900,432000,493800,553300,327400,519799,510100,501500,451400,512400,504400,498200,522200,517800,498900,507200,508300,494099,507400,485200,523800,351500,529700,500700,504500,386000,506400,508300,491700,432100,502800,357100,512700,503000,560900,514400,352000,333500,377100,500500,450100,377500,499799,303100,501800,511000,356300,425300,513500,418000,490600,410700,543000,496700,533000,520000,528400,360400,504900,497400,445000,508600,509300,431700,387900,444799,436200,496000,490500,496900,499000,516800,493000,498700,503200,387400,512900,516900,499400,519400,505500,531300,510900,517700,556400,538300,512500,581200,497200,464200,506200,515000,505400,529900,506100,502600,440800,505600,589800,503100,504099,508200,511700,497200,507800,530800,495000,513100,505600,500700,551700,502600,501599,497900,566800,511100,501800,352200,562800,513500,563100,503000,499099,587700,512900,508300,512700,591100,514200,523800,511000,516700,511900,514300,279100,381800,511700,527600,598300,434600,511000,519900,522200,531000,483500,519700,600000,503700,505800,498400,515800,516900,322900,523700,511500,519600,399600,518500,519700,397500,484600,513700,268630,315400,517200,514500,505900,515300,516000,473100,509099,514500,301900,490500,486900,380200,531800,455000,425100,448300,515200,527100,522299,344700,514200,518500,519099,497800,475100,440200,459700,515900,376300,304500,445500,485800,375000,354000,513200,309700,472900,507299,516300,521900,569700,546300,327100,517900,578300,518800,524000,386000,531300,510600,378600,521400,354000,520200,596800,524200,557800,525100,383600,433600,528500,305900,526000,530100,519200,523900,543400,527400,310200,525900,442000,404400,515800,516599,558500,523300,514200,479700,357800,457800,380700,514900,341800,524500,407400,516300,532800,476599,281100,520600,519500,524300,527000,526200,522400,488700,523100,524200,536500,530300,396000,514700,540900,428100,531600,520600,525500,525100,537000,330700,552700,497800,318000,537300,472299,298100,377500,529200,533300,531200,380000,537300,583800,535800,511200,379700,472600,579400,444400,509799,433500,430500,523700,500600,277800,535700,518500,530200,560800,516200,522700,525000,561000,531700,549000,517200,522600,514000,512200,516500,511400,522600,519700,402100,524900,339000,534900,541700,546700,519600,348100,527100,523700,498600,535500,504700,456200,509300,499500,503600,517700,507100,513500,520900,545600,515300,525500,515300,412700,550000,507800,594700,275700,284100,531200,524300,497600,526100,424000,513200,406100,520600,524000,484600,590700,573700,402900,521400,450200,520500,366400,414500,531300,404700,504400,503100,531300,546200,517299,348800,351000,496900,513600,514900,515200,393600,515800,400400,521400,497700,537300,484200,535100,510100,426100,502400,537200,305700,509200,510900,516200,509900,523000,490000,433000,508700,337600,515800,347200,507700,498500,514099,498000,521300,513600,467700,597800,512600,535700,517600,510400,361500,508400,550900,367600,326100,352700,579200,483200,352800,434600,462100,510700,564600,600000,268630,519400,565500,555200,466900,521900,390200,552600,512000,563600,513800,519600,439900,383100,544900,522200,525900,529000,361000,420000,490000,522000,410200,540600,563200,493900,507500,523200,600000,556000,453800,507800,517100,504300,532700,512000,529500,501800,467100,515700,526900,522299,534600,511400,522400,508400,554200,512900,341500,455800,525300,509400,524099,519400,471200,597300,448200,402800,519300,537200,513000,532300,529800,535000,522700,516200,455400,429700,479799,516400,359000,518400,520400,427200,536700,515900,576100,518100,320200,297400,368200,432600,571800,517600,515400,534900,474200,513300,530600,524099,399500,497299,507500,516500,362100,471800,480600,538100,500400,515800,518200,522000,498000,538500,440800,533600,514300,538000,552600,556500,512000,523900,514099,505700,535800,505900,485100,515300,503500,510400,492200,462600,375000,511599,384700,514500,516400,414300,577900,320200,513300,504300,522200,308000,521500,531300,499300,535800,502600,509300,505600,545000,453800,505700,502000,526500,452900,522100,510900,503100,523600,526500,512800,272000,427299,365300,554800,590700,407900,475500,575700,412299,351000] + }, + { + label: "Total size", + originalDimensionIndex: 3, + tickFormat: '3s', + values: [160,1324,252,1711,173,624,228,2474,371,3103,312,2408,460,1727,754,2457,227,1963,354,2797,377,1327,462,1009,166,1415,1118,1063,593,498,959,1331,1146,645,921,1596,663,568,492,833,630,445,504,394,647,314,1039,829,911,282,925,681,581,506,368,373,628,538,418,419,478,884,847,369,410,344,396,501,510,379,733,1649,239,1035,1839,967,514,875,794,414,664,447,839,540,636,499,1059,743,173,473,519,465,623,726,631,521,643,323,424,472,657,510,521,661,690,497,1664,2162,828,1062,479,1452,574,604,1069,766,268,369,755,644,811,636,363,375,359,378,336,413,680,431,361,493,761,412,386,339,628,390,648,611,323,369,555,142,2133,653,392,597,740,345,345,1211,600,396,369,390,481,498,371,337,316,363,676,330,353,527,352,329,341,349,334,357,501,363,1066,284,307,338,367,327,901,330,367,311,543,370,355,318,398,335,346,382,337,807,365,349,350,415,346,330,337,330,2201,489,338,322,349,744,239,327,327,317,763,303,343,334,293,298,472,558,331,769,321,315,342,2161,325,1667,493,328,878,328,737,315,325,317,529,727,390,320,953,729,300,538,663,333,320,309,596,591,325,300,709,303,312,501,737,306,360,320,1585,324,196,294,827,305,316,415,557,302,550,332,289,730,319,324,369,335,532,422,315,1423,1091,320,323,358,307,314,317,218,414,299,323,371,322,314,311,338,310,324,319,674,305,366,356,316,338,352,343,339,327,497,323,780,427,346,311,319,307,343,312,1695,329,293,316,429,341,315,548,330,307,331,321,427,319,530,516,310,344,724,479,2748,667,977,426,696,755,944,474,331,314,319,381,294,282,302,264,370,449,314,1029,318,312,706,349,508,297,293,322,531,480,396,286,367,315,308,365,302,467,302,313,421,351,295,792,296,651,305,1207,501,325,311,302,290,693,312,681,474,296,321,305,322,312,317,453,322,312,550,331,334,297,560,324,812,311,320,336,533,316,1305,502,857,329,1089,521,495,1025,316,373,301,935,685,547,311,622,851,880,175,331,343,1122,314,494,211,329,228,307,306,302,312,324,549,354,390,443,305,303,1364,320,352,412,328,522,1509,303,314,326,304,376,297,354,305,303,309,324,543,1231,306,865,477,388,388,603,532,310,1249,321,162,431,346,880,355,789,320,383,317,317,571,324,321,810,1075,329,363,310,385,322,1005,344,1169,526,286,328,558,295,505,339,608,526,352,312,391,933,448,590,411,463,391,697,1113,327,604,845,1066,330,435,750,325,709,523,315,353,882,736,456,1111,1050,454,955,1728,423,796,793,1348,761,970,668,459,386,783,276,820,708,501,764,445,492,464,496,419,967,1243,751,472,377,419,628,480,701,368,486,949,668,720,570,425,576,352,449,427,1551,349,478,370,327,381,379,333,403,387,378,368,450,382,376,1184,330,349,416,435,381,362,362,426,379,372,345,841,432,952,441,238,313,437,392,763,719,376,370,679,387,531,591,352,377,174,366,340,383,361,341,335,346,392,390,731,477,861,419,1073,337,523,972,624,404,485,398,1249,366,371,400,419,344,823,365,506,925,305,628,357,323,489,662,354,833,353,369,352,342,493,366,329,862,333,361,816,785,500,502,368,343,349,366,340,325,359,371,403,333,385,336,352,716,424,395,1525,338,342,504,392,808,362,327,300,1073,1137,376,332,363,340,876,396,319,339,330,290,1026,452,339,286,336,873,514,339,363,883,334,324,331,324,324,326,313,324,325,332,313,321,309,692,296,312,303,333,649,697,374,294,357,313,316,294,316,422,450,321,331,302,713,303,335,321,916,350,313,307,313,1121,312,1299,481,306,1072,304,622,970,310,310,980,472,317,535,328,898,661,386,335,320,337,309,293,1362,351,876,403,319,514,1102,456,309,498,706,305,922,1655,330,284,915,305,319,522,546,307,502,341,358,450,324,314,322,320,321,388,307,297,310,605,358,1048,322,315,468,350,179,1056,1079,342,319,345,348,314,329,311,488,775,318,312,510,884,776,313,304,488,329,294,501,315,381,348,450,299,414,481,1377,342,1135,637,302,322,309,305,311,696,303,486,293,308,1131,541,295,1198,319,301,293,321,1268,281,361,304,306,339,1439,618,644,359,302,319,297,690,327,299,300,305,503,326,309,367,392,964,325,291,317,281,386,282,281,366,351,906,198,294,733,442,350,315,359,435,1220,322,330,307,434,313,304,1111,313,313,330,336,764,312,286,348,924,309,327,317,364,423,294,337,323,313,718,676,323,335,332,332,321,1004,340,324,296,312,512,311,324,306,308,313,310,367,330,317,311,307,308,309,451,312,169,320,462,316,304,314,324,311,303,316,324,316,364,305,307,332,628,303,336,305,318,305,323,305,289,314,309,274,302,326,469,404,331,323,301,690,755,327,322,299,470,319,246,692,551,327,369,334,321,694,322,313,319,758,494,536,300,483,295,407,289,330,314,212,306,312,360,326,335,310,461,1065,299,1554,316,883,306,314,302,381,313,939,310,298,295,1040,613,365,317,842,311,427,322,315,358,313,356,307,305,727,303,343,674,300,314,303,311,694,456,434,320,422,307,293,311,149,510,314,704,1070,294,309,368,299,328,471,300,292,305,309,336,306,323,323,367,319,374,311,324,449,345,175,300,330,390,740,1029,644,532,339,966,307,325,301,245,916,334,463,373,313,671,678,298,315,286,820,312,535,340,464,322,323,416,595,571,596,753,725,295,304,294,451,308,303,310,506,308,744,308,289,311,293,455,327,263,1153,1348,473,148,1811,134,294,577,834,349,287,1848,316,1058,839,759,799,323,501,372,598,366,454,383,334,324,323,696,763,318,351,334,321,1631,295,354,292,321,741,1141,318,722,320,311,303,302,323,335,442,310,807,338,324,341,958,301,327,458,321,319,318,337,670,1254,1019,340,193,740,308,413,336,203,330,321,336,966,561,1178,323,329,582,306,202,455,305,1161,845,341,396,428,1298,973,300,692,519,296,306,441,759,572,299,628,340,514,320,296,326,569,823,373,618,410,317,313,712,210,363,717,1324,1247,297,315,315,313,1031,354,312,312,208,374,338,361,704,433,320,319,295,528,291,1356,520,802,369,856,954,612,286,316,440,901,413,373,375,301,462,645,414,356,554,2389,377,371,735,458,368,400,355,345,756,377,381,275,417,337,611,390,326,319,383,338,375,329,730,416,532,312,312,326,741,396,292,480,196,288,375,347,477,969,311,535,1560,328,312,719,459,299,324,312,505,325,547,1029,342,908,308,999,336,568,466,353,645,324,518,722,285,613,699,610,1555,792,683,302,676,582,290,865,1675,559,398,271,459,1523,366,276,882,488,562,381,295,339,543,381,623,551,367,459,348,309,350,335,328,792,320,323,324,767,443,567,380,290,362,337,345,483,560,467,356,334,749,631,321,460,356,328,329,241,366,703,346,515,362,465,841,344,304,341,560,364,355,381,357,194,176,341,386,322,342,1393,185,341,499,3154,324,435,542,353,402,292,334,413,179,806,892,494,281,527,332,655,724,1253,300,326,302,701,570,408,669,360,466,795,373,301,316,355,312,303,307,434,287,321,321,300,686,306,324,716,316,875,323,479,751,326,547,371,438,329,734,1176,495,2745,736,421,327,436,323,353,670,442,300,595,369,727,484,352,369,325,341,266,481,306,323,294,493,365,400,382,372,540,310,317,932,321,404,328,303,340,324,313,315,318,331,729,1063,306,322,339,317,455,312,693,332,416,320,362,322,327,331,1044,309,337,363,426,407,613,471,337,321,308,326,976,318,1265,314,874,294,319,366,337,343,453,322,481,447,749,370,337,308,316,516,385,426,349,416,585,373,353,360,194,419,311,318,317,287,341,335,587,551,393,303,288,1113,322,313,863,329,285,276,327,345,769,956,1183,866,765,309,668,1849,516,395,363,427,293,359,260,1235,339,465,329,725,242,361,302,347,318,337,333,325,313,544,318,304,436,332,638,741,969,362,309,909,751,721,934,535,1328,221,459,341,302,287,306,883,222,351,769,326,292,353,412,3017,791,401,1250,603,839,584,358,381,465,455,495,609,313,305,1283,680,404,325,344,356,987,453,530,606,391,364,175,327,610,341,333,794,448,351,1603,417,283,395,540,338,319,354,302,339,364,526,347,357,542,840,416,301,370,191,1251,174,512,338,329,828,721,309,777,319,325,325,322,341,293,336,314,321,303,408,257,955,322,323,323,1101,227,478,336,350,175,486,313,359,1170,346,336,500,282,910,295,303,971,1226,335,285,653,1002,325,353,382,323,334,289,305,316,349,430,677,160,914,453,595,419,173,281,920,364,331,502,657,421,311,360,665,636,327,346,407,632,471,326,336,749,391,557,381,478,442,469,303,310,292,379,453,274,312,673,321,516,371,288,446,352,318,617,315,329,939,646,497,178,394,409,180,390,922,380,465,495,396,835,391,362,511,346,308,565,345,314,346,333,316,355,348,427,360,415,1113,813,321,486,349,363,363,359,181,317,356,223,294,326,146,372,307,465,402,275,324,317,359,316,336,506,782,298,283,500,401,412,631,342,296,311,327,323,422,321,354,1106,357,399,333,322,380,298,345,340,340,310,354,307,336,470,258,315,361,473,326,553,666,1079,318,326,848,390,516,344,1169,449,324,340,310,320,300,326,305,498,288,324,312,174,344,354,409,260,331,494,1185,309,835,336,801,428,190,303,446,185,321,532,286,493,746,536,336,499,340,373,1164,336,378,313,883,728,344,322,360,315,353,203,384,426,369,328,386,321,337,318,301,319,320,175,300,349,296,315,686,323,698,315,427,1174,506,396,321,464,309,308,338,370,327,313,941,323,384,194,333,1186,329,1325,325,558,311,373,335,323,977,542,311,460,1188,324,332,184,2534,322,1325,300,305,383,250,307,316,417,297,1378,305,330,316,506,717,173,426,637,1256,803,305,314,718,317,439,324,755,411,310,386,325,331,550,302,309,341,1055,328,355,265,1013,477,533,1710,322,315,346,719,324,844,342,311,700,424,419,998,442,664,1037,446,754,369,321,864,316,346,322,847,470,490,865,310,994,322,501,361,390,661,332,344,657,431,322,440,398,1876,732,292,1528,380,351,209,476,467,1304,337,925,378,769,555,551,774,227,503,334,205,351,493,368,304,1131,320,484,800,281,549,306,316,809,310,325,529,939,836,179,321,176,326,189,370,327,351,822,317,308,321,301,494,346,355,306,326,318,302,298,287,323,316,329,320,475,306,527,902,575,1023,309,520,273,723,341,373,334,854,534,333,1296,312,318,525,1259,406,441,309,498,663,706,423,631,794,452,393,766,371,356,309,265,321,542,379,317,394,325,522,346,378,308,1310,358,457,372,698,799,319,978,310,307,339,959,328,598,491,509,463,337,328,340,326,475,330,323,337,290,375,308,747,310,736,315,303,369,536,1075,539,717,1493,477,513,1314,409,494,454,881,337,278,2065,1757,212,458,708,363,560,326,137,598,390,785,928,301,1965,321,232,295,751,354,307,674,343,398,1135,336,462,343,290,306,328,329,334,410,312,659,335,976,342,449,293,324,703,313,2091,583,731,863,337,292,1122,320,784,1010,885,1148,1264,492,644,454,1033,294,655,574,250,320,1056,617,1132,344,192,611,686,732,672,382,449,500,474,350,314,324,182,856,1291,347,327,349,1909,536,461,641,507,1004,369,445,344,441,356,341,315,855,315,404,370,297,339,465,732,366,349,396,918,534,359,508,380,357,388,465,257,805,975,321,315,335,1028,458,838,372,997,571,335,372,886,299,306,498,552,303,449,478,927,280,415,301,234,324,346,1107,1192,321,548,305,983,763,455,337,985,372,600,476,555,429,612,387,391,394,530,429,410,387,412,395,333,348,294,350,413,308,361,334,350,361,355,1161,1772,334,185,313,330,603,397,469,373,302,465,493,344,325,297,185,355,362,324,334,334,349,345,305,299,349,328,357,467,319,891,570,245,1310,1273,472,775,318,288,1076,1412] + }, + { + label: "Total cost", + originalDimensionIndex: 4, + tickFormat: '3s', + values: [1125,9274,2735,16920,2193,20704,4814,34689,5565,46554,5225,38887,9746,37715,17798,55250,5697,49083,11226,81939,24258,62488,27363,61924,12511,106196,24444,7443,9111,12527,50738,9320,12291,17571,9508,11176,9142,7687,6724,11219,17825,6506,6367,4966,8495,3860,12844,10688,13026,4007,57281,9790,7335,13748,5358,5393,4401,3769,5233,5058,6047,6188,11899,5297,5154,4406,5061,7022,6358,4721,7723,16745,2651,14194,100772,10504,7021,6131,16730,2904,8334,5968,5876,7394,4454,6800,18376,5204,2190,6146,7013,6412,4361,5082,8028,6484,4504,3832,5687,6174,4603,6913,3651,4631,4833,6532,97138,21918,5801,7435,13138,14597,6070,19943,7487,5362,3945,4907,5287,7937,9998,7852,4820,9385,4686,4751,8594,5144,4760,5899,4614,30326,5332,5030,4759,4429,17294,5192,4542,9333,2261,4911,34968,2179,34658,16325,4811,4185,9017,4224,4435,14536,7129,4772,4726,4840,3369,7428,4504,4316,4097,4399,4733,4105,4597,3691,4405,4245,4195,4168,4694,4421,3507,4615,14408,3607,3956,4295,4583,4138,11128,4147,4602,3940,5590,4505,4377,4016,5191,4199,4269,4686,4375,9716,8664,4371,4294,9779,4396,4183,4247,4112,129671,3426,4257,4102,4386,8822,3138,4040,4066,3950,12502,3787,4273,4238,3678,3660,6006,7725,4172,12580,4056,3920,4249,35461,4054,108185,6568,4154,11735,4090,8971,3943,4062,3857,5408,8982,5662,3974,6673,18248,3739,3770,8110,4129,4050,3826,8314,9053,4022,3718,8838,3786,3874,6244,18434,3815,4491,3954,39629,4009,2456,3643,10976,3783,3946,5151,3900,3730,3854,7621,3597,7909,3959,4022,4585,4130,6699,27180,3935,89225,14401,3962,4022,4459,3816,3938,3931,3041,26734,3722,4047,4583,3998,3907,3887,4191,3858,4033,3994,11019,3777,4482,4277,3968,4237,4378,4264,4212,4049,7410,4020,12723,12452,4283,3862,3953,3803,4276,3866,18642,4063,3648,3901,27631,4251,3875,3837,4109,3808,4083,3993,27553,3950,3715,3617,3819,7947,11786,3358,44432,38355,6845,9723,4878,11458,6609,3321,4964,3884,3963,8633,3627,3471,3780,3359,4582,28916,3896,12827,3908,3865,4945,4276,6296,3685,3657,3948,6513,6549,4829,3554,4503,3893,3820,4464,3720,3270,3743,3861,27079,4284,4335,9658,3671,7768,3772,15008,6611,4023,3840,3957,3579,8798,3870,9817,3319,3647,3955,3782,3981,3845,3930,5570,3975,3856,3851,4094,4130,3676,5579,4013,17649,3704,3967,4152,34352,3909,16007,3517,56056,4041,14115,3652,6567,13776,3906,4618,3729,6550,8592,12494,3885,13919,10616,12510,2200,4164,4256,13850,3877,11216,1477,7529,3283,3802,3773,3728,3868,4104,7658,4381,4671,6364,3789,3774,16177,3965,9465,5965,4054,5330,15225,3764,3865,4058,3775,3758,3704,4368,3729,3767,3828,4150,3804,15268,3815,11823,3345,4872,4659,7489,3729,3868,29343,3997,2114,5553,4292,12800,4382,19742,4101,8777,3940,3978,7211,4103,3980,12415,12316,4082,8375,3841,8810,4128,11997,4269,15031,3683,3614,4116,35845,3653,3538,4223,8373,33766,4411,3863,4728,11456,5587,38362,5111,29991,4825,10478,13283,4010,38799,11943,14042,4038,5282,12235,4027,11570,5328,3914,4404,6180,10126,10305,23044,12414,6658,6690,17090,5813,10147,5552,14343,11440,9605,8565,6188,5280,5483,3629,5741,8375,6864,5353,6098,6015,5678,6594,5520,15985,8704,5263,5731,5020,5553,13258,6541,4909,5117,6779,61602,6992,8510,3993,5627,4035,4817,6048,5638,10862,4568,6015,5021,4284,5133,5166,4381,5240,5107,5232,4897,7559,5099,5020,15005,4330,4830,11538,5852,5130,4805,4753,9282,4965,4964,4540,21046,5789,15483,5931,2900,4104,5939,10136,12664,9245,4934,4829,9958,5014,8126,4141,4664,5028,2236,4825,4450,6349,4727,4520,4438,4538,5013,4969,9032,29764,12658,5571,12826,4427,6303,6807,8189,5126,6691,5004,15571,4613,4841,5437,5492,4572,13590,4729,31885,9172,3977,4397,4489,4192,30894,17398,4636,12650,4567,4597,4625,4501,8431,4569,4265,10628,4315,4627,13535,10198,3503,6671,4717,4428,4557,4727,4387,4179,4651,4798,4920,4306,4756,4240,4542,8949,27217,5130,100064,4412,4353,3534,4753,45902,4727,4278,3988,13557,14324,4833,4152,4563,4299,10786,10025,3990,4245,4201,3703,12584,5588,4231,4049,4219,10855,3598,4248,4530,10756,4306,4004,4135,4069,4192,4082,3919,4071,4059,4177,3922,3982,3841,11321,3668,3855,3796,4226,8651,8405,8613,3628,4544,3889,3934,3673,3943,27240,28881,4011,4120,3737,40767,3746,4190,4026,9678,8023,3928,3813,3904,13989,3909,22474,5945,3795,14047,3829,8130,12089,4416,3859,23854,5822,3941,34877,4097,6289,8372,4738,4124,3961,4110,3843,3763,22330,4302,10698,5011,3960,6460,13165,5659,3833,6159,8569,3728,12430,17360,4081,3556,12850,3802,3962,5382,7233,3823,5937,4512,4425,5552,4024,3882,3993,3970,3997,4806,3793,3723,3848,7973,4442,12803,4020,3904,6002,4338,2233,17184,14084,4271,3942,7932,4327,3882,4098,3837,5936,10801,3953,3865,3573,10508,19422,3855,4313,3419,4098,3648,3507,3942,5038,4350,28905,3717,26636,31080,16310,4550,14594,8871,3743,4000,3809,3784,3870,15692,3764,7100,3653,3821,13999,7394,4537,14574,3960,3737,3643,3972,8878,3501,4511,3785,3810,4214,92394,8440,8972,4418,3717,3955,3701,10342,4052,3730,3688,3796,6183,4046,3823,4535,4834,13248,4026,3616,3936,3491,4754,3483,3528,4498,4365,11386,2894,3620,18349,5466,2455,3885,4448,5426,14879,3993,4086,3797,5588,3885,3773,18127,3875,3891,4095,4159,11648,3866,3565,4317,12171,3823,4053,3922,8253,27165,3649,7753,4005,3871,17967,44240,3984,4138,4101,4124,3966,12588,4194,4016,3667,3851,11327,3833,4023,3798,3810,3874,3840,8337,4063,3925,3852,3812,3820,3828,5671,3844,2457,3964,3237,3922,3762,3880,4014,3866,3752,3931,4022,3918,4517,3779,3801,4107,15710,3752,4170,3771,3965,3775,4004,3779,3585,3903,3836,3382,3748,4032,3283,5534,4099,3997,3732,8711,18895,4040,3984,3701,7008,3970,3088,8520,6614,4162,4436,4138,3979,8524,4013,3861,3965,18961,6070,6394,3698,5940,3645,4818,3582,4120,3910,2799,3779,3893,4458,4022,4108,3820,3227,10985,3685,22218,3925,10987,3806,3865,3745,22845,3873,12636,3830,3697,3640,14144,8228,4503,3912,10683,3832,27508,4369,3907,4705,3860,4686,3807,3775,11854,3763,4259,10998,3715,3918,3754,3837,10320,29350,28013,4806,27181,3783,3626,3838,2197,6793,3880,11481,28248,3636,3818,4555,3699,4061,5737,3704,3612,3780,3815,4160,3795,3964,4006,4520,3967,4631,3837,4005,5551,4224,2573,3722,4063,25150,8949,12712,7939,6700,4162,13481,3801,3966,3741,3298,11662,4130,6268,4621,3866,16793,9268,3710,4199,3545,8357,3845,7090,4227,5999,3976,3986,5145,8848,7687,7778,9672,18129,3671,3758,3623,6176,3829,3751,3839,6623,3818,9382,3824,3567,3876,3648,3187,4050,3274,14438,33701,3315,2019,17972,1935,3651,7236,10042,4314,3418,46207,3910,13235,11016,5315,12999,4040,6042,4600,6015,4556,10560,4629,4154,4057,4028,10213,10194,3936,4337,4100,4017,17640,3666,4336,3605,3985,10013,13900,3940,9771,4029,3830,6681,3749,4002,4144,28335,3851,9628,4190,4020,4209,11639,3714,4051,29534,4001,3943,3919,4181,8921,15249,25498,4413,2570,9094,3781,5196,4153,2806,4299,3944,4153,11672,7329,13429,3990,4059,5928,3770,2724,5689,3807,14139,10239,7795,4983,10307,16032,12418,3739,8526,6457,3656,3791,28264,8997,7054,3697,6385,4206,7040,3971,3702,4031,8243,47305,4623,6289,26518,3919,3896,17804,2868,4490,17926,86514,14833,3636,3919,3900,4163,13083,4797,3872,3870,2921,4626,4389,4492,8805,27930,3981,3945,3652,6474,3616,84260,6441,11593,4576,12714,13232,12893,3555,3922,28141,12058,5088,4661,4667,3727,5861,9001,5727,4432,6923,39382,6197,4625,16568,5855,4562,4751,4391,4257,9707,4869,4715,17117,5311,4147,6155,4805,4006,3954,8680,4167,23759,4134,11938,26580,3728,3886,3880,4065,18544,9038,3609,30977,2647,3585,8622,4313,3341,60270,3902,6742,101532,4069,3902,11753,5734,3706,7359,3882,5163,4062,7403,12706,4274,11082,3834,13857,4180,7733,6289,4394,8664,4020,6487,8926,3545,7424,8967,7105,15471,8990,15930,3739,4733,4080,3681,11600,109361,35468,4939,3449,29173,88421,4596,3452,11753,4978,36214,4717,3650,4247,34932,4724,9055,6852,4699,6300,4318,3863,4364,4208,4057,11771,3994,3980,3999,11116,5528,7493,24623,3933,4465,4205,4283,6279,5729,5893,4439,4200,9248,4421,4061,5686,4450,4066,4108,3132,4617,17583,4328,6506,4535,30003,48630,4313,3777,4245,6884,4535,3881,8812,4461,2683,2233,4284,4820,4040,4277,16571,2554,4242,6285,51154,4044,5770,3800,4401,5008,3647,4158,26583,2467,20174,10786,3461,3497,7861,4243,16393,11861,76828,4026,4071,3740,17541,7580,5092,4687,4720,3266,13157,4661,3747,3893,4432,3893,3761,3795,9961,3574,4001,3976,3773,17165,3782,4045,9144,3962,50212,4029,5950,8949,4070,7221,8644,28169,4062,8752,15000,15842,44456,9155,5309,4053,10116,4046,4394,8316,5514,3711,38351,8468,10339,6030,8029,4578,4029,4222,3390,6727,3803,20095,3644,31728,4573,9131,24869,4656,40536,3844,3932,12001,3974,9248,4031,3798,4203,4056,3876,3886,3939,4057,9567,26598,3804,4015,7758,3949,29312,3902,8209,7521,5578,3987,4785,3995,4079,4112,12730,3823,4210,8367,27490,4937,8779,6457,7775,3957,3823,4042,22441,3978,15741,3913,12279,3646,3940,5004,4172,4199,5890,4245,5994,5525,18737,4526,4310,3837,3909,33292,4777,27477,4301,5769,8151,4658,4344,4495,2657,5789,3851,3932,3934,3683,4207,4138,8298,6530,8710,3782,3585,13380,3997,3900,49974,4074,3540,3419,4076,4261,9672,6695,14148,50140,9092,3985,9640,42657,33535,8852,8334,5261,3730,22730,3339,76534,4819,28020,4070,8981,2923,4505,4593,4316,3911,4185,4135,4015,3895,5559,3947,4231,5169,4122,8906,18540,11809,4399,3866,11489,9502,10162,11780,34617,14012,2961,5785,4343,3766,3585,3799,14853,2997,4256,9665,19975,3645,8150,26579,49058,11073,4814,31254,7724,11033,8315,4314,4509,5916,28810,5994,15238,3883,3771,13249,9488,4866,3982,4169,4354,12208,5757,12358,7437,4743,4418,2405,4048,10157,4252,4071,19869,5500,4254,11221,5164,4153,4884,3785,4207,3912,4325,3755,4161,8314,3687,4203,4409,5546,22831,5131,3903,4589,2432,80943,2199,3585,4205,4049,12623,11762,3815,9929,3934,4023,4371,3987,4218,3830,4139,3910,4533,3782,5007,3306,23891,3988,4008,4002,13496,3002,5976,4142,4357,2414,30186,4189,4749,13952,4589,4189,5993,3502,13135,3665,3739,11784,28602,4450,3574,10675,13041,4002,4349,4771,4001,4159,3482,3804,3884,4343,9972,16927,2221,12913,29211,7041,27050,2402,3485,11956,4511,4063,3515,9775,27027,3871,8808,8094,8066,4060,4286,26107,7034,28205,4038,4148,12288,5029,6825,24260,30775,28412,5946,3777,3890,3615,4738,29181,16464,3920,8606,4059,5257,4640,3570,6020,4387,3958,40271,3907,4062,12193,4527,31964,2436,4879,9400,2240,4846,59578,8689,28097,6135,9008,12433,5015,4496,3579,4307,3807,8084,4643,3879,4300,4138,4068,4424,4336,6213,4457,26961,14333,13269,4073,6316,4332,4492,8378,4443,2350,4011,4427,2909,3752,3989,1997,8651,3891,5882,5091,3443,4074,3974,4512,3921,4204,5164,10883,3711,3570,6328,5074,5141,8367,4363,3678,3889,4145,4042,5286,3989,4476,13140,4539,25758,4129,4004,4694,3864,4295,4277,4463,3857,4439,3782,4186,30163,3261,3942,5408,6214,4099,3875,9581,15415,3965,4083,10985,4853,3614,4321,14442,3146,4061,4248,3869,4031,3748,4124,3763,7007,3547,4060,3906,2375,4294,4412,5156,3421,4143,6916,14716,3866,10080,4168,11257,6132,2617,3796,5781,2543,4022,7851,3579,31698,10449,7074,4179,6623,4294,4985,14197,4219,4643,3871,10871,10158,4303,4042,4687,3933,4395,2669,5223,26888,4835,4120,4863,4033,4234,3945,3787,4005,4006,2384,3737,4331,3717,3897,11221,3970,11405,3906,5170,26275,5172,4725,4027,29448,3856,3820,4221,4553,4084,3920,12274,4041,4563,2432,4168,14781,4089,84860,4055,6853,3917,4639,4187,4046,15864,3795,3896,29479,14289,4031,4153,2588,41096,4004,28909,3755,3826,4556,3108,3827,3932,4954,3685,14009,3794,4092,3929,5161,10867,2372,5304,7846,14855,10527,3801,3892,17953,3898,5557,4010,8929,5144,3868,4846,4033,4106,7961,3734,3844,4228,14218,4070,4390,3578,64666,30739,9257,42766,3995,3910,4320,11739,4026,10762,4262,3868,21774,27155,26769,13667,5432,8575,13691,5792,12304,8357,3962,12225,3930,4287,3987,10619,5986,6403,11136,3844,13668,4749,6511,4570,5367,9270,4129,5042,6824,27699,3999,28366,4786,30493,10545,3619,38201,4702,4334,2851,30809,3274,21828,4164,12971,4680,9109,6845,6667,9545,3102,31606,4117,3017,4334,31897,8413,3756,13786,3943,7094,9861,17684,7372,3802,3923,13130,3832,4035,6716,13120,10771,2451,3981,2509,4019,2530,4595,4017,4468,12432,3922,3813,3969,3716,31879,4280,4485,3782,4025,3901,3719,18455,3560,3943,19801,4049,3957,30692,3774,3690,12946,43153,12827,3480,3642,3492,5065,4683,8439,4097,5979,7318,4074,27698,3828,3955,7204,80055,4851,11026,3846,6536,41579,8716,5304,9580,9725,10335,8879,9245,8502,4393,7028,3207,3943,6586,4696,3914,9070,4049,33617,4304,8600,3814,81235,4422,6506,4557,7048,19991,3954,13795,3822,3798,4260,11751,4101,7718,31546,6384,3247,4172,4088,4228,4035,5786,4094,3987,4185,3877,4554,3840,8856,17877,11098,3877,3743,4629,5448,14210,5504,17927,15188,5892,6491,15609,4910,8230,29322,11412,4175,3866,34283,101349,2855,3210,17724,4466,6815,4038,1938,8412,4895,12817,23221,3737,32782,3984,3123,3650,12300,4338,3889,8256,4272,5260,14199,4117,6801,4269,3612,3803,4083,4070,4216,5383,3891,9386,4152,13778,7919,3149,3636,4064,11473,3886,34380,6906,11939,10572,7726,3639,58427,3988,5494,13997,12759,69547,82624,6238,9041,5960,13276,3667,7896,6782,3844,3967,12716,7470,14500,4257,2617,7419,4804,8821,4705,4733,28271,6839,30762,4321,3868,3984,2542,11350,13299,4292,4368,4264,27377,8775,5744,7910,12689,12160,4769,5551,4261,5514,4379,4244,3879,21386,4042,9266,4556,3667,4165,29895,11939,4558,4237,4917,13036,6889,4424,32664,4694,8153,9377,5507,3342,10823,62847,3939,3912,4122,66439,3210,10350,4615,12868,7511,4255,4616,12370,3704,3788,6565,6764,3760,5382,5920,13436,16417,5747,3951,2810,4018,4297,24861,15191,3990,6731,3764,13253,9573,10539,4175,13712,4754,7612,10652,6949,5321,4285,4785,4906,4827,33385,5270,5091,4739,5003,4787,4146,4299,3657,4368,5066,3848,4575,4150,4381,4496,4471,14916,29694,4159,2529,3889,4098,8034,4757,6803,4640,3780,5750,7248,4255,3991,3723,2276,4447,4513,4048,4071,4323,4361,4313,3757,3874,4314,4087,4463,30169,3933,11064,8721,3231,14337,15420,5613,10373,4057,3450,14347,15572] + }, + { + label: "Excess charge", + originalDimensionIndex: 5, + tickFormat: '3s', + values: [9,794,34,1409,17,536,39,7133,115,9843,107,7017,273,5131,1210,9032,82,7312,252,16778,760,4349,891,4228,123,10800,1593,467,260,226,2940,803,689,420,422,1229,275,198,150,423,423,134,146,89,248,55,612,401,546,52,4208,307,194,257,91,93,178,133,99,98,131,345,461,90,96,69,91,160,148,81,274,1332,31,667,11733,502,163,354,480,67,252,120,303,181,162,153,1142,251,17,131,165,135,162,222,230,154,198,58,109,132,189,159,102,167,218,147,11969,2288,316,467,234,1025,169,497,435,265,49,82,252,234,372,229,79,127,76,82,105,97,207,115,75,1014,259,95,84,68,404,91,183,273,49,82,1378,14,5405,769,86,168,308,67,69,818,200,87,79,86,100,174,77,66,58,74,206,62,73,129,71,63,65,67,71,72,116,76,696,46,55,66,77,61,460,62,77,55,146,77,71,58,93,64,68,82,66,364,113,69,69,145,69,62,65,62,19984,113,65,60,69,307,33,60,60,57,676,52,67,64,49,50,129,196,63,690,59,56,66,5409,60,13549,146,62,466,61,305,56,60,56,138,299,102,58,406,1019,51,137,247,63,59,54,226,257,59,51,286,52,55,143,1039,53,73,58,4807,59,22,49,411,52,57,98,129,51,143,90,47,284,57,59,77,63,162,849,56,8935,711,58,59,73,53,56,57,30,822,50,59,78,59,56,55,65,54,60,58,532,52,75,71,57,65,70,67,65,60,173,59,717,203,68,55,57,53,67,55,1579,61,49,56,877,66,56,142,62,53,62,58,873,57,132,126,54,98,622,103,9106,1860,454,148,206,412,423,103,77,55,57,118,49,45,52,40,77,960,56,603,57,55,225,68,146,50,49,58,159,142,88,46,76,56,54,75,51,100,51,55,842,69,59,353,49,236,52,829,150,60,54,54,47,277,55,309,104,49,58,52,59,55,57,116,58,55,143,62,63,50,151,59,515,53,58,64,1357,56,961,117,3664,61,696,128,147,639,56,79,51,342,268,245,55,311,413,505,17,63,67,713,55,199,17,89,34,53,53,51,55,60,192,71,84,130,52,52,1033,58,122,113,61,134,1110,52,55,60,52,68,50,71,52,52,54,61,139,861,53,464,105,86,84,207,125,54,3281,58,15,108,68,523,71,1197,59,120,57,57,187,60,58,483,696,61,109,54,121,60,563,67,797,130,47,61,1473,49,119,65,231,1307,71,55,85,491,114,1705,96,1039,86,346,690,60,1724,462,677,61,106,664,59,590,134,56,71,355,338,168,1600,612,141,437,1438,111,367,283,942,412,453,259,128,92,277,45,304,278,156,269,123,136,121,148,104,1063,676,257,125,85,105,301,142,232,85,150,4912,226,287,135,108,147,77,123,109,1131,72,131,84,63,88,88,66,95,89,90,81,222,88,85,808,64,76,179,115,88,78,77,236,85,83,71,1381,113,1075,118,31,58,117,144,653,301,84,81,315,88,207,155,74,86,17,80,68,166,77,69,67,71,89,88,303,989,509,105,642,67,153,405,231,94,147,90,889,76,81,98,104,71,773,78,1146,413,55,178,73,61,1077,421,74,503,73,77,73,69,255,76,63,420,65,76,752,363,112,151,78,68,72,78,67,61,75,80,91,65,84,65,72,292,847,91,11709,67,67,110,86,2467,77,63,54,662,742,82,63,75,66,434,144,58,65,63,48,594,115,65,53,64,433,119,65,75,438,65,59,62,60,61,60,56,60,60,63,56,58,54,561,49,55,52,64,254,271,115,48,73,55,56,49,56,853,954,59,62,51,1951,52,64,59,430,100,56,53,55,717,55,1725,131,53,681,53,229,536,63,54,842,126,57,1413,61,321,252,84,63,58,63,54,50,2153,69,432,92,57,151,677,118,54,140,279,52,519,1391,61,46,538,53,58,135,178,53,140,69,72,115,59,56,58,58,58,85,53,50,54,218,72,618,59,56,127,69,18,1321,688,67,57,98,69,56,61,54,133,382,57,55,120,434,1165,55,60,108,61,49,114,56,86,69,957,50,814,1112,1052,70,751,257,51,59,54,52,55,392,52,160,49,53,726,181,64,806,57,51,49,58,629,45,74,52,53,65,9789,237,263,72,51,57,50,337,60,51,50,53,143,60,54,76,87,580,60,48,57,44,84,45,45,75,70,470,26,48,1034,110,56,56,73,108,837,59,62,53,110,55,52,1450,55,55,62,64,426,55,46,68,509,54,60,57,107,846,49,93,59,55,992,2282,59,63,62,62,58,577,65,59,49,55,208,54,59,53,53,55,54,109,61,57,55,53,54,54,116,55,19,58,98,56,52,56,59,55,52,56,59,56,75,52,53,62,758,52,64,52,57,52,59,52,47,56,54,42,51,60,101,101,62,59,51,274,1101,60,58,50,155,58,34,270,169,62,76,63,58,272,59,55,57,1092,137,159,51,132,49,92,47,62,56,26,53,55,73,60,63,54,98,564,50,3025,56,444,53,55,52,564,55,537,54,50,49,667,228,75,57,409,54,869,63,56,76,55,75,53,52,623,52,66,534,51,56,52,54,337,989,902,72,848,53,48,55,15,157,55,586,1108,49,54,76,50,61,125,51,48,52,54,64,53,59,59,76,58,79,54,59,114,67,21,51,61,726,306,600,234,162,65,594,53,59,51,36,485,63,131,79,55,867,285,50,60,46,330,55,171,66,126,58,59,98,247,199,210,330,1010,49,52,48,126,54,52,54,151,53,318,53,47,55,49,95,60,39,760,3499,103,13,1582,12,49,190,388,69,45,6497,56,640,418,246,757,59,140,78,174,76,172,82,63,60,59,331,352,57,69,63,58,1417,49,70,48,58,336,732,57,319,58,54,72,52,59,63,918,54,363,64,59,66,515,51,60,1002,58,57,57,64,270,883,1867,68,22,309,53,97,64,25,64,58,64,523,186,826,59,61,166,53,24,118,53,758,400,95,90,158,954,549,51,271,153,49,53,914,320,185,50,193,65,164,58,50,60,217,2632,79,187,810,57,55,976,27,74,988,8719,865,49,56,56,59,614,77,55,55,27,79,67,74,283,897,58,57,49,157,48,7908,153,430,77,511,575,285,46,56,904,492,96,79,80,51,123,265,108,72,175,6523,163,78,437,122,76,88,71,67,333,83,82,326,100,64,181,86,60,57,119,64,639,62,622,806,126,55,55,60,1051,128,48,1104,23,47,116,68,104,4048,55,164,11963,61,55,605,120,50,85,55,125,60,183,600,67,464,54,631,64,199,132,71,253,59,153,295,46,210,284,233,1168,369,390,51,204,156,48,454,13920,1427,90,42,967,9234,76,43,469,117,1506,82,49,65,1401,82,262,172,78,131,68,54,69,64,61,438,58,59,59,395,112,192,700,51,74,64,67,137,155,125,72,63,318,173,59,120,72,61,61,34,77,941,68,152,75,1036,2493,67,52,66,177,75,68,120,72,23,17,66,85,59,67,1079,21,66,143,11880,59,113,134,71,92,48,63,812,20,1241,446,111,45,195,64,778,609,7556,54,60,51,944,195,95,214,77,100,716,79,51,56,72,55,52,53,155,47,58,58,51,897,53,60,297,57,2646,59,130,313,60,178,115,911,61,300,801,317,9038,308,102,60,158,59,70,255,111,51,1691,112,345,133,101,77,60,66,41,147,53,449,49,1156,76,131,717,79,1595,54,57,507,58,134,60,52,65,60,55,56,57,61,315,2179,53,59,94,57,987,55,266,89,105,58,78,59,61,62,613,54,65,109,870,93,248,138,94,58,53,60,785,57,911,56,491,49,57,83,64,66,120,61,132,113,1080,77,66,54,56,1274,84,868,69,109,217,79,70,74,23,110,55,57,57,48,65,63,223,169,123,52,47,692,58,56,2946,61,46,43,60,67,339,407,779,2965,325,56,298,6838,1303,125,108,103,49,585,39,6512,75,852,61,298,32,74,66,68,57,64,63,59,55,145,57,58,105,62,259,1056,528,73,54,476,325,335,501,1382,903,29,121,67,52,47,53,846,30,69,339,438,48,103,812,10805,400,89,3049,211,419,223,71,80,125,938,137,693,55,52,820,294,91,59,66,71,553,118,235,207,85,74,19,60,416,66,62,1242,113,69,1218,98,54,88,136,65,57,70,51,65,108,129,67,72,145,710,98,53,78,21,7564,17,120,65,61,500,611,54,350,57,60,64,58,65,50,63,56,66,52,93,38,1462,59,59,59,684,30,130,63,69,19,1015,59,77,762,71,64,139,45,552,49,52,529,3101,67,46,501,592,59,70,83,59,63,46,53,56,69,153,885,16,541,981,196,841,18,45,498,75,61,115,302,836,55,114,248,233,60,68,780,225,860,60,64,649,89,175,668,1088,925,127,52,55,48,82,979,293,55,263,59,130,78,47,121,70,57,1886,56,61,519,172,1172,19,88,137,18,86,4092,118,859,139,127,489,89,74,116,68,53,210,72,55,68,63,58,71,69,123,73,842,723,775,59,139,69,74,109,73,19,58,72,29,50,59,13,115,54,124,93,43,60,57,74,56,64,126,388,50,46,144,92,97,238,67,49,55,61,59,102,58,72,679,73,763,63,59,81,52,67,66,68,54,71,53,64,1041,38,56,92,133,60,144,294,1454,57,60,422,86,121,67,775,93,60,66,54,58,51,61,52,159,47,60,55,18,67,71,96,40,62,156,798,54,390,64,412,121,22,52,116,21,59,195,46,1153,356,171,64,149,66,84,762,64,80,55,441,337,67,59,76,56,71,24,90,815,80,61,85,59,65,57,51,58,58,18,51,69,50,56,551,59,572,56,102,1107,126,87,59,983,54,53,65,77,61,56,523,59,82,21,63,802,61,8238,60,175,55,79,64,59,1136,134,55,994,788,59,63,21,7677,59,1377,51,53,81,35,53,57,96,50,932,53,61,56,126,371,18,103,229,875,382,53,56,993,56,111,59,316,96,54,85,60,62,203,51,54,66,679,61,71,42,4768,1085,289,4911,59,56,68,608,59,412,66,55,604,842,817,619,110,258,642,117,668,110,58,484,57,68,58,410,127,142,437,54,617,71,147,75,95,280,62,80,216,879,59,926,88,4185,356,48,4600,81,69,27,1094,100,1870,64,548,81,328,174,170,339,31,1122,63,28,70,1173,111,52,719,58,160,362,352,183,53,56,781,54,60,161,562,408,19,58,20,60,21,78,60,71,486,57,53,58,51,1170,68,72,53,60,57,51,377,46,58,439,61,58,1085,53,129,538,1842,598,55,126,43,201,72,113,62,339,177,62,1294,55,57,172,7283,91,176,54,147,1940,282,102,288,355,167,125,328,113,71,78,39,58,164,81,56,128,60,1299,68,116,53,7338,72,136,78,237,1244,57,618,54,53,66,519,61,209,1141,148,98,64,61,65,60,126,61,59,64,51,78,54,310,336,388,56,52,77,141,691,143,929,1094,129,151,960,93,272,989,455,64,49,4784,12101,27,96,959,74,176,60,12,230,87,724,1656,51,4285,58,32,49,659,70,54,256,67,94,736,63,146,66,48,53,61,61,64,100,55,284,63,616,97,92,48,60,583,55,5038,189,627,420,93,48,3879,58,289,644,522,5278,7955,139,266,122,622,49,240,182,46,58,623,213,745,67,22,209,216,299,207,83,899,155,1094,69,55,59,21,440,828,68,64,68,4618,334,121,232,511,565,79,113,67,111,71,66,56,1396,57,134,77,49,65,1026,632,76,68,89,549,166,72,1224,82,104,131,120,39,394,4543,58,56,63,5089,98,398,78,581,194,64,78,500,50,53,148,172,52,112,129,577,286,108,53,30,59,68,987,823,58,169,52,590,333,172,64,616,80,207,181,176,104,172,85,87,87,1255,104,95,84,95,87,63,68,49,69,96,54,75,63,70,74,72,786,3447,63,21,55,62,219,88,148,79,52,122,167,67,59,50,19,72,74,60,62,65,69,67,52,52,69,61,72,1047,57,451,238,35,934,908,124,364,58,46,698,1102] + }, + { + label: "Bandwidth", + originalDimensionIndex: 6, + tickFormat: '3s', + values: [236630,326876,347086,437333,457543,105963,568000,216420,236630,326876,347086,437333,457543,105963,568000,216420,236630,326876,347086,437333,457543,105963,568000,216420,236630,326876,350300,210200,106030,349000,288700,328200,262300,251400,384900,435500,224500,273000,276300,224500,222900,202100,394000,418300,302200,475800,338900,296400,115700,276700,298200,163400,380500,282000,226600,233500,404799,451800,424400,493100,397500,337600,157200,242399,421400,403700,397600,239400,416300,441200,354000,354000,315599,151100,269600,255900,270900,422299,488800,240300,376900,318200,302800,262600,238000,277400,122130,405500,455999,353400,287200,269800,292400,290200,357200,417600,519200,544600,318600,345200,352600,283500,172500,168299,427900,332800,84900,307400,418600,214700,280000,407700,366200,114300,86100,393700,239000,341900,358600,413900,382700,412600,344099,375500,372100,428300,365400,436500,390500,288300,403900,301000,376599,470700,460900,374300,246200,333300,349700,106600,514099,341100,364700,229730,388600,339000,466700,491500,419400,478100,398200,372500,493300,498400,396200,445300,367700,164300,487500,402400,388300,496400,405900,453400,376000,470600,440900,396300,469500,523200,275700,457800,474700,409900,167700,425800,402200,418300,441599,426700,364200,435800,433000,427500,432600,485600,464000,431000,365300,440200,467200,467200,384000,433200,425500,437500,474500,424600,416500,421100,429000,451200,429099,507200,430000,414000,431599,475600,385700,465100,454900,450700,425800,451900,449099,420500,447200,480400,389200,244500,430400,437900,430000,452800,452800,300300,449000,348100,313200,425100,227200,448900,421700,446400,445000,494000,453100,394799,223600,460500,352500,448200,457800,500200,424099,456300,428300,464500,225400,109600,461700,465200,381200,449300,461200,418900,445500,451599,440600,465800,380400,464400,469200,469600,248600,462700,451700,441800,303800,469300,496800,463900,459000,291500,458100,459300,450100,469799,393900,465000,448300,222700,203400,461300,455000,447700,456599,444700,463100,310000,470800,457000,442600,459600,458900,457900,447000,457200,456100,456000,446400,453700,463100,472700,514300,440400,440700,449900,452800,454799,463100,166300,452000,462400,244600,460400,462100,463200,466700,447900,465700,132900,465300,461400,469799,461700,449000,473500,505700,450300,461100,469099,457100,469000,462200,499099,509000,476800,456000,484099,424200,399900,192900,463600,453700,306300,75800,467700,458000,202200,464300,456900,467500,474099,481300,447400,430600,453200,456200,461300,322500,479300,465400,401599,475900,429200,466500,456900,480000,441900,280300,475200,466500,472400,465100,462600,476100,473200,470100,463700,468800,459700,482500,232300,409799,464200,476900,466200,295700,325500,464099,471500,377900,472700,350600,464500,159700,468300,473500,470500,462400,467299,470900,462700,452200,470100,469700,506800,461200,460900,466000,525900,463500,448500,534900,462800,465400,454300,465300,308000,471800,486300,474400,237100,500300,319100,186400,469099,452900,468800,152200,372500,432600,448400,444200,354300,131400,461799,436300,456800,326800,469400,449000,273800,467200,272300,466300,471300,473600,464099,425100,236000,456900,510700,223500,457500,457100,375300,464500,320700,221200,467100,458200,388900,463500,472600,454600,462200,533800,455700,461500,491400,459799,465200,407400,497700,296800,456300,193500,465800,427299,507400,407000,392299,449900,506900,451800,414299,374799,456800,98600,463500,453300,407700,455600,459099,441400,382100,426900,462400,49000,133200,459799,451800,464600,456400,407400,419700,456300,236500,500900,437800,439500,435900,465500,477700,451599,240400,438900,438500,464600,494700,369600,427800,483600,437400,482400,456800,102700,401700,478500,425900,147600,212500,482800,478700,465900,458800,458900,464400,459900,444700,394000,211700,459099,455200,434000,195500,482600,435700,282400,323900,386599,257900,86800,504000,337000,301300,299500,390400,376300,390200,488000,271100,426000,283800,456700,459500,314100,344000,352400,274700,404700,471200,336200,336300,510000,283600,484500,280700,248900,447800,379200,486200,294000,337200,383800,305400,305900,338200,378200,366900,405700,313600,372600,320300,303900,365200,365400,349700,284000,339600,322700,334000,334799,261800,369900,290300,283400,309200,319600,343800,359500,242600,360300,334400,361700,488500,315600,470800,311100,506800,374400,295800,344300,343800,322400,359500,369300,138600,376500,127700,383100,351300,336599,435700,355200,368800,392100,366800,351200,353100,364600,396400,401400,393200,342600,89900,330200,405400,364600,479600,279400,307100,407900,265200,422100,283500,422400,367200,302700,352800,347100,373600,379400,373400,505500,383600,411800,428000,385600,380800,278900,366700,54500,384700,443500,362200,362200,248000,441800,388300,374000,386700,400000,347800,293000,409300,315100,395800,390600,373100,384000,392700,401500,380800,380700,475500,391900,454099,429099,389099,373300,444200,369200,427299,373500,414600,359400,491200,433700,370200,372700,357200,287600,281900,390900,445700,430500,423300,375800,362000,445000,441100,415900,415600,356200,441200,447600,279400,437800,355700,423500,437800,443000,395700,393900,467500,445100,438300,391300,442100,443200,438800,448500,433800,444300,458400,460100,450500,468000,469400,447800,421000,278700,446599,452000,472000,411900,459000,454700,452600,448500,466200,441400,449200,451400,466700,456599,467100,444099,442700,325700,460400,443900,462700,452100,304500,442299,98600,438800,464000,219899,435300,316200,334600,265200,458700,295400,441400,454600,503600,444600,179800,361500,467100,472500,463400,486599,458200,410100,372900,476700,391599,441800,458500,401800,401700,433700,462299,431900,433100,486100,203800,257800,464099,452299,141800,456800,459099,430800,309900,454300,520600,352700,460900,448100,455400,466300,459400,461500,456100,448100,470600,445200,462400,306100,451200,361000,449700,461500,375500,457299,480099,458200,226800,446500,466500,459900,452000,468400,451300,470700,465800,184800,456100,463000,465300,447200,467700,474000,269800,441200,451500,463900,444200,446000,346500,444000,444799,457700,459799,464300,376300,345000,242200,217100,466400,457100,473400,462900,457500,424400,461100,191500,458000,460500,319000,265700,188500,344900,458400,461700,462200,462100,93699,458400,438100,458500,457000,455300,322900,250100,215900,465200,474900,459099,457000,109100,459600,457100,479900,459500,442800,459900,468400,458500,457200,162700,462800,460300,457600,462400,461400,472400,447400,466700,452299,328700,266600,476700,456599,443900,480800,471700,452900,429900,334600,461700,463400,469600,376100,457800,464099,429099,466300,457200,459200,463000,68100,465800,460500,455400,241600,466100,462100,467100,469600,452700,461800,459400,460800,465100,461000,506900,470400,465700,465700,457000,467000,316400,464700,462000,465200,470500,472299,471700,459799,462700,466500,465900,466700,468300,469700,463500,463700,462500,465200,462800,414000,473000,280800,461800,465200,462500,470000,468000,459300,460900,464400,456200,457100,463700,449600,463700,466100,460700,465500,466700,457500,465900,453000,467200,462800,466100,467100,456500,463000,478300,463200,462400,465200,293600,460500,463100,465000,361500,464700,464900,465700,469300,173400,454700,454700,406300,482500,417200,509400,461100,459799,410300,452200,471300,455200,429000,444500,495100,473400,445800,470400,534200,466300,449300,454099,385700,468500,451100,453700,470700,476599,472400,467000,370700,474000,451800,458700,352800,460500,475000,466800,262000,465700,202200,466100,463200,473500,163700,275600,462000,471000,323000,471400,461500,321700,462400,359500,473200,358400,463100,464900,467800,458500,453400,463800,464300,451900,467500,472900,117600,456900,465800,205600,460600,474500,471200,473300,270800,309600,467700,473900,196100,469700,469099,453900,468600,464500,467500,472800,471500,464099,470700,459000,464200,478300,461300,466599,456000,454600,470500,465600,441200,477900,263100,464099,469799,464799,434300,340900,413100,395300,475200,138900,468300,490700,460700,349600,303000,465300,294700,451900,470600,464700,234500,460100,349600,468500,431000,470800,312100,454900,363600,465800,468800,449099,143500,281200,322100,318600,460600,461700,468900,475400,283100,458400,466400,466400,335100,461900,353600,459400,476300,455000,461200,455900,461200,465500,291600,416300,461599,358300,415700,295100,465000,395100,427400,461000,542900,343400,463400,311200,264500,305500,480500,444900,480300,456200,493000,446200,429700,494700,450600,441100,452299,133300,251600,462299,461100,474200,444400,181900,460200,478100,471700,459400,238700,351400,461200,239100,437100,475500,507500,463400,462299,462900,437500,458400,452900,457000,458900,466800,388800,473700,459300,459200,453000,469099,471000,458600,275500,336200,293500,381100,380000,401700,479000,417600,463600,327800,380400,474300,461400,399500,328500,127000,467600,468600,457600,475100,357800,420900,453900,348300,413700,466800,422500,400400,293700,287800,455400,406100,418200,473900,462800,441200,475000,426400,468000,460300,459500,267400,457299,452800,466800,181400,459000,454200,456000,479200,466100,454400,464700,339300,458400,461900,426100,388300,485000,457000,462400,354500,288400,316400,460600,462200,305900,455000,382000,446800,375600,464200,455900,465200,467800,444700,460500,200200,425200,126400,453500,76800,151600,514300,464400,460300,433000,220300,453600,437100,442600,468300,395300,212600,278700,448400,404500,241900,435599,441200,418600,383800,451700,526300,459600,465700,319200,382500,451300,364000,397200,470400,485500,461100,474900,461100,468100,467600,407600,434099,441700,432100,401200,455600,456100,450200,444700,457500,470400,462500,358700,459700,454200,452200,454600,261100,442400,392299,375600,456000,449300,449500,421100,463700,473500,454400,457100,447200,278600,342700,445100,387800,457800,141700,451900,263500,298400,448000,268200,461000,405500,393500,461000,453200,332000,175430,431300,192500,389799,465100,386599,443900,429300,223500,380000,389099,446000,425100,409900,432400,429500,453600,231100,465500,443000,453700,471900,441599,445400,447900,164300,413300,400200,278600,453600,448500,443700,434400,465600,92899,450700,469200,466000,132700,427800,308300,490400,331700,463900,446400,454200,352900,450500,405000,444200,432299,391900,343400,427400,441300,441300,459300,447700,401300,421599,439300,441900,391000,434700,467700,107400,439600,458300,449500,432800,446500,324400,446400,440200,330100,452600,435500,434700,442700,443800,363800,332700,450700,400800,333800,448300,332299,441300,445900,437400,456200,451700,463200,336800,434300,409700,440600,464400,157100,407800,343300,431200,240400,343000,447500,467700,458500,297800,432900,510700,363300,457400,360400,437000,460300,474300,442200,451000,464799,466599,447000,461000,450700,461200,442800,441000,469600,448600,335500,443300,87300,450200,427400,463300,447299,313700,441700,458000,469300,466300,250500,164200,379799,379400,414900,459799,436000,444400,446100,395500,429600,467800,443600,456500,167900,424000,465600,452100,459300,459799,426700,247000,459600,355000,467400,452800,435100,458200,514799,435800,455100,463300,458900,278900,462600,454400,473600,446200,460800,445300,466500,472200,463200,477100,286800,438400,457600,452800,464900,455000,459300,449400,484700,477200,320300,453200,352100,460100,450100,457900,365900,466000,445700,453600,470000,485600,185200,276300,457600,469000,459000,458700,358200,446000,284600,455100,150600,463100,468100,302300,462600,477299,356100,361900,422600,445400,459900,474600,406500,456300,465300,454000,448000,467500,465700,274100,229300,440200,467400,443600,338300,279000,465500,465600,461800,409099,466300,463500,208200,509799,489700,452600,457299,385900,456200,455700,474300,461700,466800,470600,449000,462700,352600,343200,384799,473300,466400,400500,162700,333500,491300,476900,456500,453700,419799,410400,413500,211200,260200,258600,460700,389300,520200,440300,195500,448900,475300,453200,455900,466300,456200,453000,461100,296400,527300,458700,212900,457100,380800,492000,449300,319000,345500,181500,316700,466800,281000,365100,408400,413600,457000,454900,463100,263800,353500,496500,354500,318800,455400,454799,468800,318300,174300,505700,460100,352700,261500,200900,504799,539700,391599,390900,471800,404799,464000,468100,343600,204400,501000,479200,497800,481000,346800,395000,415000,429000,488600,491000,341100,465000,345900,447200,481500,516200,455200,498300,392100,444900,238000,452700,477299,449000,478100,479300,458100,476900,462500,483700,498700,460500,448200,213900,454799,392700,453000,443500,383100,461099,456200,450500,472400,50800,460600,468200,323300,471100,462000,332500,462200,458800,383400,468200,454000,274600,450500,461300,411700,139500,464799,457500,462200,345200,378000,418900,466900,448000,336200,333500,343100,351800,394799,349700,447900,493200,463600,102500,459799,469799,388600,449799,349200,446200,459700,247100,471500,464700,435300,464400,449799,518000,455100,476300,450200,437100,476400,332900,133100,463700,505100,466900,325300,470500,264700,456000,474300,444400,125900,445800,457800,398000,434900,362900,457700,457000,447100,251400,246200,459900,466500,416800,385200,440300,418700,456000,450000,394900,453500,443300,467600,436100,462100,282700,441300,341700,426800,464300,437200,467100,304500,448100,455500,499799,462299,467299,263300,268000,448200,343200,451599,448300,483599,445000,416000,459400,267800,428600,460800,79600,390100,449799,400100,450300,467900,199000,327900,467600,452700,454700,398200,444099,448300,210400,455400,500600,244100,449799,420400,350300,451400,455000,452600,455700,412800,427500,451100,397500,415600,481400,349699,443500,427800,399700,408300,455500,437500,444700,432400,462100,441800,463900,185799,458200,437500,392200,411000,434099,289700,409400,459099,449799,422500,445400,424799,458200,420600,412500,412500,470500,458400,456000,455400,393500,449000,433500,366700,458200,434300,474400,447200,439600,441700,443700,196500,336800,434200,492100,166000,468000,453200,442500,285900,442900,442600,435600,317000,465200,440100,446100,452700,435500,453500,424799,470400,236100,479600,442000,448200,348100,444600,444900,416599,379700,443000,243800,302000,447200,422000,454799,166700,230799,335000,445600,362900,336599,441599,167700,450100,448500,185000,316300,453700,315300,428000,332900,344600,435500,467600,464900,374700,194500,442900,438800,374000,452100,446200,390800,304700,384200,362400,436200,419600,438400,437700,460500,437900,440700,445400,349400,459400,455100,445600,463900,451200,475300,456200,462000,484900,358500,459000,517100,438800,400500,450700,460400,444600,465600,447299,446000,254200,447400,528300,468000,444000,295800,453100,325400,449500,434099,438100,446700,445300,442200,480400,437500,445300,438300,370800,453300,441800,309600,381200,456200,367600,448800,443700,526200,468400,453300,456400,524400,461500,380300,456599,458300,455600,461000,87200,343800,435700,416599,399400,273700,456500,464500,466100,476000,402000,462600,480600,429500,450100,428200,458200,458300,188200,470700,456400,459400,175500,460500,457200,341000,350400,453400,194830,106000,460200,458600,443700,459400,458500,313900,447900,459200,133900,434799,431599,161000,455200,326200,214100,360800,456599,469600,466000,142100,458000,457400,462299,343900,386700,342800,293100,461000,156300,222700,346900,418900,288500,201700,454099,223299,399799,452299,459200,467100,504200,408100,149400,466300,469600,451800,462500,340500,472800,455200,244800,462200,140900,453600,474700,427100,465700,389799,334000,360600,470300,254000,464600,469400,461100,470600,350800,471599,188700,386200,401900,288700,461500,460600,500100,468900,456500,380900,143000,296400,341400,458000,300100,467500,367800,450700,476200,410700,62000,464799,465200,468000,474400,465100,461599,423200,469400,467100,481700,477800,349500,463800,485700,381800,474500,464300,466599,471599,477200,113300,493400,311900,274700,478300,420600,165100,302200,471200,475600,433400,262600,480100,398300,482000,454500,264100,302400,513500,365400,454600,333500,333800,400000,423900,108400,399000,447100,469200,433400,457400,460300,476599,516800,476100,457400,450200,467000,451200,454200,450900,449700,463300,465400,200400,462200,230600,470800,470600,486800,463300,120200,473000,469799,436900,370300,445600,340400,446900,407100,448000,458200,448300,453000,463500,464900,456800,469000,455500,352299,486700,452600,475800,212200,89100,476500,471200,430500,470400,204800,456300,335800,364600,440400,389500,380500,506700,354900,464700,278100,461100,303600,219100,397500,359900,449500,445800,468300,451200,459700,316799,212399,425900,452400,441400,461800,198800,458900,351200,469500,437400,476400,427200,419000,448800,345100,297400,479400,188700,447900,459099,461900,451300,465200,428500,350600,452800,181000,456500,119400,452700,444099,462100,439099,467200,458000,290200,505300,455500,387100,464200,458200,248299,451100,463800,140500,110900,157000,434900,391700,204100,343500,263900,458100,456100,509300,200030,462900,390900,451800,251300,461400,348600,449700,430900,442299,434600,452200,375500,273700,487900,460800,471100,472700,319500,244300,349700,461000,342000,480900,478600,450600,424900,410900,565100,387800,382100,428000,456300,424799,471000,451200,474700,432500,407000,451599,462400,470200,476000,452500,466200,442900,495500,442500,124800,352400,462600,444799,457500,463100,404900,523500,397700,232700,397299,481900,457000,474200,403300,482800,375700,450300,262200,314400,417200,450500,156700,465600,466500,326200,441700,462200,502800,433700,92300,243100,275000,372100,533400,460300,454000,364500,249700,456200,435600,470700,190600,358500,434300,456900,138500,401400,368700,465200,399900,439799,446599,454000,426900,470800,364900,459400,441500,471500,483200,490300,452600,462900,461800,442800,464600,450500,418100,456000,440300,445900,427100,240000,191600,452000,344300,458900,457900,289300,513200,204800,446900,449799,440500,183600,461000,475000,445400,503799,438400,444500,447299,488200,388800,442900,439700,473300,394600,460800,452299,438700,465500,470800,354400,116900,377600,195300,341300,515100,245600,415600,528700,188700,163700] + }, + { + label: "Payload", + originalDimensionIndex: 7, + tickFormat: '3s', + values: [5137,113712,-5909,102666,-16955,135803,-28000,124758,5137,113712,-5909,102666,-16955,135803,-28000,124758,5137,113712,-5909,102666,-16955,135803,-28000,124758,5137,113712,42910,126039,135736,46100,54920,113880,100220,91860,52140,104500,110480,82230,69120,136490,97950,79780,44390,23330,86870,1469,130450,114629,184720,32180,56400,131980,58430,63240,59330,58640,29810,11790,25690,13330,39900,70460,160410,55770,25179,18490,27650,79830,40630,16900,41369,118770,-2760,190000,119920,85020,73980,49120,51200,41940,71030,51970,75200,80390,70520,69910,119636,40720,-16800,48360,70910,66170,54730,66650,70930,41940,8919,-8201,48240,50080,44120,70290,74640,94410,30920,56720,187800,170140,45710,124420,59660,90450,24040,129420,173100,45500,37120,34300,52610,59770,89350,59000,32820,23020,26780,19180,19350,22770,37900,56130,21080,41360,49010,16290,14480,23140,90940,39390,43869,138650,-19100,34470,34630,12036,102620,24060,14260,9980,71280,5559,19670,141580,37160,9199,23650,17750,22180,99940,7589,17540,16790,5080,34200,7580,25040,7300,13060,17520,6419,-1381,41910,10830,4100,20210,188340,5209,12970,14960,15149,11780,105870,10600,16640,9179,9750,7869,9400,9640,34300,10610,7640,12680,21000,76650,12170,13130,6909,19100,16490,13150,12810,7980,97129,-2571,12800,13140,14170,59090,4630,5150,6889,6050,13490,3860,10260,13930,3160,-1600,40730,88140,11680,11560,10280,5390,9440,131340,7570,144740,60570,12210,142160,8029,70330,6570,8240,-1070,5180,75360,64130,4979,73200,7920,2460,3080,60010,7470,10450,2600,99590,134120,5400,1179,76180,4209,3650,38840,8910,4250,14170,3999,76350,4860,-15060,-341,128230,2330,5859,22060,44500,770,4769,1039,539,53740,5130,5729,13939,5580,48450,1110,6199,164570,179370,4720,6339,12740,3569,6740,4089,13730,-641,2360,8390,12540,5500,4519,5790,8100,4159,6329,7200,2510,2199,9430,1130,7710,11010,11439,9619,8699,5529,99290,6729,7670,60320,8769,3379,4350,2020,10470,2929,187500,5399,839,2790,2430,9999,1970,2979,8070,2849,5110,5679,979,4449,2559,-410,829,3989,279,11130,130020,63780,48590,15020,59370,174670,44850,4510,57900,3340,5340,6710,-1150,-4031,4400,1400,13450,5500,3909,133800,1570,2959,38050,6499,37810,599,1560,1949,38340,66110,13230,-1020,9730,3350,2790,8729,109,1679,1690,2440,1910,5659,43820,80340,739,47080,1709,164110,58980,5069,1810,16480,-1550,81400,3139,133160,2669,-821,3349,2360,4120,1960,4129,25520,3660,2260,2959,6350,6830,559,-3540,5040,62340,-8401,4570,6469,14420,3509,172060,4749,38850,3860,167900,1540,59530,176210,2949,13930,819,132560,74890,34410,5559,41260,102000,174330,-17380,10730,8950,144260,2560,24580,6370,80,22540,2060,1019,160,3180,11580,88900,10470,6260,73860,3119,2980,158220,4309,30660,68780,5040,3770,99260,1980,1970,6829,2289,-20080,2310,9650,-2251,2529,2530,14700,3959,166700,3599,150930,3459,20810,6590,55690,22690,5139,69,6480,-12630,37400,9400,186250,9540,10830,14040,9070,4760,7700,56650,11310,4790,194180,128610,6400,7109,2769,9170,14430,104370,9179,178490,1839,3649,9690,20490,339,3980,9289,97370,16590,13480,3039,9209,108480,29490,14640,22230,2340,15880,155060,121920,2909,27250,163260,172970,2660,18100,5159,5870,3790,2880,4320,12500,56360,125710,17810,19190,106000,83140,42550,104300,56000,102609,49720,119469,171680,32940,81140,57710,46010,47900,12410,51430,52000,71890,38220,59550,30110,25690,60750,41920,42970,122470,41700,24520,36670,43590,28380,65420,20490,46900,74830,23260,30570,53800,48360,44400,28940,38850,55090,44390,117250,26040,38180,40279,21509,40690,43770,23870,34920,35680,47920,34530,10209,38060,36900,172090,22500,41440,48970,51970,40759,32670,29570,8320,32190,36220,26469,7490,50070,16550,52680,-14320,18900,55380,31810,30419,92140,31910,29040,139770,30299,117280,30630,29670,36720,-13690,31170,24230,-7440,28120,27790,26429,26000,27230,25920,76240,30360,186670,44830,116240,24560,29890,95180,82150,26890,70680,23220,173160,18330,29520,47580,40140,29100,28250,26230,27280,29190,15910,28300,15970,18410,23929,86500,26960,195790,23360,15049,27500,25880,29740,14950,18950,98230,19830,21830,33440,109480,16040,61550,23600,20700,24970,25500,19860,15920,25010,26910,14280,18950,15430,12709,22380,78950,4990,32920,110000,23040,16230,26429,10009,14770,27599,21410,19900,149800,159730,25800,9250,16530,14280,99670,28780,7340,11060,14120,7850,123859,27430,9960,32360,11210,104469,14710,11570,14290,95429,18660,4369,9129,9270,17570,8890,6709,9220,7710,11250,6599,5370,3400,4270,179,2290,4540,13700,93360,59470,8529,-851,19020,4139,5289,2350,6269,990,8240,7010,8050,1210,4560,1259,9949,8110,64180,3909,6729,2690,5280,150810,6709,148720,32080,2289,171240,6690,79710,122870,39720,3809,122739,30200,5390,5709,8730,118730,74280,13320,5130,4419,3000,3679,9389,63420,6690,95570,20350,5090,44240,120660,29440,2999,35740,63700,-1540,155930,145400,5790,939,175990,3370,5030,8129,69630,3980,19400,27460,10430,25930,6300,3050,5360,4789,5870,16930,1360,3990,3170,79640,12030,125269,7050,3980,42910,9890,-19210,25280,170010,10610,3749,3420,10510,2749,7790,1890,27759,140190,5329,3289,6660,82810,7319,1650,37550,8889,7769,499,9579,6610,35280,11940,7629,2200,1180,7390,159380,29129,172160,108520,1330,5769,1170,2310,4110,54490,2309,90020,1360,3090,147650,79810,53960,148120,5100,1890,669,4730,190970,-570,14780,2929,3439,8649,133040,96310,110079,9729,-151,4940,2090,151720,6060,2439,-1190,2919,34290,5939,2029,12110,15930,175700,5290,589,4910,-1241,14340,-2690,1250,10480,10930,116080,17710,-1590,6629,25540,-11360,2330,11860,27210,153740,5040,5950,1549,37630,4259,2010,34760,2959,4410,6640,6889,179130,2920,-60,9899,144140,2350,5719,3420,4159,3330,799,2390,5310,3079,4840,18340,3719,6260,5810,7309,3969,132430,7079,5280,549,2090,22160,1699,5679,2509,2129,2999,2469,4830,4780,3960,3040,2710,2440,2859,32670,1659,9360,4670,2080,4059,1059,2789,5639,3589,1709,5050,6039,3850,13180,2140,2079,6579,-1821,1389,7799,1739,5910,1610,5020,1809,-721,4659,2929,-4720,1829,5420,2709,50380,6510,4900,1380,78330,6539,5170,4370,409,92280,5740,-5240,67910,33020,13450,3690,6899,5139,67240,6620,2099,5599,13440,32679,28520,-180,30930,-421,4630,-641,8349,5080,129,1480,5289,11870,4140,4360,1450,1629,67510,-421,21240,4620,106560,2909,1640,1290,35360,3020,158790,2440,1200,-911,186220,88630,11220,2760,109090,1729,2269,30290,3889,28940,1820,28870,2559,1930,3350,2659,9469,690,1270,5479,1309,1579,149430,6060,2289,55039,1910,699,-770,1540,6850,64080,2819,850,166420,-441,1869,13020,480,5479,25160,-31,-981,2100,1620,7470,2270,2390,5259,10760,5520,13850,1910,4650,26980,5580,13470,1379,4770,-2110,67810,128539,59940,48220,5219,184470,1770,699,2079,11750,124680,6210,59900,14120,2260,1140,110020,1779,23809,-1401,34390,1970,67430,8870,44820,4270,3970,20880,123800,80690,74260,98010,5329,1080,1170,-1370,60700,3479,1419,2499,57480,2859,87760,3290,-2181,4539,859,3010,5810,-4340,158760,53230,3790,-6940,115210,-171,389,54720,81370,9070,-12800,102100,3790,140870,125290,66200,5500,7980,26760,13240,4789,14150,23180,8039,8580,8720,6790,144350,118030,4259,9330,4600,7759,166130,1229,6909,-1090,5270,118510,139370,4460,115410,8939,1140,-9890,1809,5070,6720,8089,3749,72070,8120,5770,6959,106649,-31,6090,5920,6360,3400,2849,7780,97370,157360,62720,21920,-2180,75390,69,26280,6740,7990,20550,2700,7050,104860,67500,140930,4179,4890,9140,459,2829,31800,3839,142650,86160,1739,22910,25480,175740,136730,2790,68020,41520,-771,2410,7269,61130,47270,539,12740,7960,74870,5390,2690,4800,107590,11250,13799,12540,-2471,3520,4869,3750,7290,11580,4570,93740,141260,-2510,4790,3800,22599,144230,36850,3710,3459,12609,13720,21469,13280,76830,2449,5709,3969,19,37430,569,166100,40190,163939,13330,190770,178250,25700,-721,4369,8630,147620,19530,16770,16040,869,37960,111130,55470,12310,49370,164270,-14760,15730,60470,39740,13420,4970,10200,7700,98310,27540,15350,1490,30840,5699,7070,15000,3360,4649,6919,6340,5950,10680,8300,6199,20900,4570,4429,7540,9350,10410,-871,7570,1659,289,8310,10310,5390,106550,6610,49240,127590,6780,5649,6169,32320,1240,-1721,4690,2439,7929,77530,128090,10840,100630,3630,188329,8720,84620,59530,11900,95580,5470,43960,74950,-381,47110,86950,66336,92680,82720,60920,1459,38380,17530,5669,141360,135700,29940,18850,3299,14900,59000,17080,-541,141500,-381,19600,15020,-571,11010,17110,15960,122080,47050,22710,63130,10169,5280,12150,11550,5280,174490,6590,3839,4519,156290,28769,73210,-7371,22900,10490,9810,9660,50119,8410,36810,13000,11760,78980,43510,10720,28590,13560,6270,8420,2440,18500,7820,11970,46580,15570,5070,143250,12110,2859,9860,44200,13850,8780,10440,13850,5960,-15830,12340,19080,8290,11060,164950,3810,9650,42270,170740,7550,47050,14190,12290,20970,1540,8470,479,2050,15610,93040,9579,-1440,107410,15830,23360,9890,97820,22490,7990,1110,4190,76240,22679,12740,28560,3850,29120,16780,2210,2070,13200,5299,1759,1940,17310,-20,6679,4910,4949,6390,1370,7520,88400,7240,155250,7090,33940,62660,8009,68800,10009,3970,4819,59930,174750,94460,136080,80230,28080,6040,19670,8119,12180,67460,28320,739,22780,7090,138100,35450,3299,13560,5820,8109,2330,74480,3000,9140,-31,10610,15980,10880,-11070,16900,7950,2989,4690,133840,4680,12070,3850,4790,7740,8119,2940,2280,4170,3679,103080,31040,3290,6560,1750,5440,5550,5639,50710,-1191,46540,6160,31120,5290,7730,7040,123430,2360,9969,6840,699,13000,113960,65610,2749,3590,3329,6059,104040,7060,174760,4980,166740,579,3500,41860,7109,5370,44670,22580,35230,26020,7019,9689,16860,3870,3509,12830,16580,1130,8240,56830,96679,16279,8519,13779,4509,56250,2770,3750,4310,8500,7010,6570,102840,27770,4400,3700,619,126290,5859,4740,11070,6030,-1230,-3230,7840,8179,91370,75930,134770,11439,63700,13590,130160,62680,6040,6850,6279,21589,7609,3599,3469,150150,46160,47580,6130,76360,-15750,14380,53620,10820,2150,8590,7600,4670,4599,6539,4560,32280,9640,7049,109840,7030,110150,5699,5200,119300,90730,132780,123130,12360,111580,5070,34940,16240,2900,949,2470,59120,7129,3359,90910,16180,1620,5370,-530,169590,146010,8650,37420,68050,126130,104560,2890,300,39230,18150,27519,7760,3370,1429,92090,119149,9929,2479,2150,6709,121649,36730,35990,51330,10270,6070,629,5249,18510,10270,3240,-320,24230,3089,117210,21750,40279,17190,7349,9460,1779,6609,2610,4960,5409,4910,2419,10290,7109,130530,19860,13650,13560,-12130,98940,-17310,8470,9220,4239,196430,4250,1960,100150,3020,5370,28670,4899,8210,14040,5920,5090,39860,3999,17410,3290,105480,4550,5820,5080,136300,3870,35600,6140,11450,1390,33250,24640,30700,130530,28929,9389,24210,-1271,189460,1270,809,108290,9919,27180,1999,109,153130,3790,8969,18390,4769,8749,-8780,3630,1690,10960,18660,-570,-890,178930,4570,34270,560,2660,-2411,136430,12180,4140,9649,140230,4380,4079,16540,57939,70360,6430,9289,2630,52080,51340,5849,6270,14300,29160,42280,4750,8489,5850,39080,3519,6339,-410,17860,4819,15920,6989,80760,10780,2259,16490,-900,54960,11800,5480,14190,3900,5109,139540,63650,11880,779,17300,13759,-19560,17870,60550,8060,45310,35980,9810,186439,28220,12890,18940,10500,1900,101870,33000,2829,10080,7810,14990,12830,11150,74180,11610,-5510,168730,12080,11870,51010,10660,12100,7029,11399,-9330,10080,11860,29,8480,2259,-6170,9830,8520,37520,26400,-911,9440,7189,15620,4100,10450,1490,140990,2060,3139,44210,25770,23100,87670,17110,1519,5420,12470,7930,26280,5479,16890,118140,18870,-2140,7170,5970,14490,13030,10540,12540,24710,3859,14440,709,9459,10670,-1331,6929,65310,52000,10220,5870,128660,-181,5890,8850,120360,18260,11359,12780,152980,819,8909,10380,4769,9190,3159,11880,1019,80250,-2871,8630,5849,-701,11160,12569,25930,9190,9520,77590,159430,5500,82810,8159,150100,69350,4390,4850,42190,3150,8220,105090,1519,11399,135670,66470,8450,60900,13540,36730,144100,11530,12100,3100,100860,129860,11510,8860,26500,5639,12170,-2270,44410,16120,30179,10200,21850,8809,11399,4620,5800,8129,7480,-741,2209,10109,3860,3560,3750,2869,3609,3930,15860,125970,2250,5980,8679,17280,4880,3100,10260,11310,8189,6340,142520,7639,2520,-15210,9689,161580,7429,122080,7520,43620,7400,15089,9739,8429,16130,14840,6140,9810,139320,6690,9819,7380,125320,5950,139190,3900,5490,2730,-6791,4170,5029,7589,1279,91120,3300,6729,5110,1869,163990,-181,24830,58240,139070,117440,3400,3409,3880,1900,33150,5130,59400,23830,5120,20360,6019,6909,102410,629,3950,8239,184140,6149,10530,16750,85740,8930,46936,177860,5280,4449,11610,4369,5900,111890,10290,3850,137810,6650,6610,181180,23420,83300,168490,42670,7079,4789,4069,168130,4780,9250,4890,104120,40890,53380,120630,3310,182369,51350,54050,18320,49000,116900,7780,55430,25810,4270,5470,2610,8530,83570,144990,-191,50870,15120,9100,6899,5369,4339,95940,7060,177700,14580,62420,44680,36320,82790,11240,29640,5350,21309,8799,7690,6179,909,138260,3059,90480,87110,-4101,75260,2719,4339,2549,2070,6279,50830,179020,115620,1230,5409,7520,4549,-1141,13970,3320,18240,190990,3740,2349,3870,-101,8480,8560,16630,1389,4679,1150,-531,6899,-571,1109,3489,3940,4239,6350,989,6099,184330,4029,136120,11500,5270,4470,103190,37550,5079,4369,44680,79400,3469,127120,219,5580,77630,122940,7959,34560,4220,56650,53650,71330,26640,141620,83130,19550,7979,71320,7180,10129,-4100,-11900,2429,36700,15280,3340,11399,6780,13950,10560,7040,2330,161490,10209,74500,10610,16930,5230,4339,193090,1389,1530,11840,111649,8630,70180,11470,42450,5239,7730,8089,9149,5310,26140,6969,3950,8270,19130,8300,4419,59429,35930,166590,1579,669,17340,3090,176800,5580,29690,103940,31200,46640,151130,9630,7710,4560,127080,7350,26160,153950,80670,4330,4460,6990,9869,40380,5870,-2880,103500,21309,9840,22010,1879,155440,5320,9159,-240,10530,7170,8580,62590,10290,38390,154760,4079,86430,10380,709,2680,7610,5500,12500,39100,5030,122840,7719,193080,4229,4550,590,9099,1969,4239,130730,32719,5840,95030,1640,1160,77050,6460,32010,190340,182590,160430,86380,43179,113420,47640,151990,1530,52040,30700,41736,4560,118050,47880,168909,8310,2580,47640,29900,64940,27820,15440,20410,71090,2509,9179,2210,3400,5400,133700,91300,8799,27180,5639,28280,-6090,31850,59980,-25100,112600,26320,29020,9090,29070,8429,9599,1850,19120,13390,12529,11810,-130,5140,7759,3960,14660,3279,19110,182550,57820,10169,13660,14190,4360,19180,14070,5679,129820,70070,1580,4700,4869,73520,-1301,94730,14280,147660,72330,14620,14260,166400,959,1859,58280,41330,2109,15690,32590,195880,24560,56380,17240,-18780,5540,9860,116910,177079,5770,41940,990,168950,89070,22450,7950,187390,23220,63840,19090,50460,24420,19780,15800,21300,13850,31820,20840,21370,12700,14140,10550,8199,8609,890,12330,17620,4809,18490,7769,12850,13460,15880,176340,145900,8440,1930,4149,6859,83570,6909,83379,15069,4069,29480,93600,8350,3169,3969,-21580,13939,13870,7739,2300,19620,12230,12100,549,13010,9090,7509,14090,5739,3050,107120,127899,6969,133470,158020,16530,121510,12350,-10570,182369,152200] + }, + { + label: "CRC", + originalDimensionIndex: 8, + tickFormat: '3s', + values: [-41,-1317,-164,-1856,-79,-931,-191,-2983,-341,-3846,-278,-3019,-523,-2357,-985,-3447,-211,-2842,-458,-4388,-784,-2563,-935,-2253,-313,-3667,-1479,-1030,-632,-623,-1945,-1324,-1221,-878,-947,-1615,-697,-575,-482,-905,-869,-433,-484,-349,-667,-248,-1135,-888,-1019,-223,-2067,-729,-579,-659,-334,-340,-554,-455,-377,-375,-453,-834,-934,-334,-369,-290,-354,-497,-490,-329,-729,-1794,-151,-1162,-3935,-1013,-509,-825,-997,-320,-680,-422,-785,-542,-563,-489,-1283,-680,-78,-450,-514,-449,-548,-661,-641,-503,-570,-257,-394,-450,-586,-503,-437,-590,-622,-482,-3687,-2394,-773,-1029,-619,-1561,-543,-894,-1037,-705,-208,-325,-693,-652,-855,-642,-317,-435,-310,-329,-380,-371,-610,-406,-310,-1034,-700,-367,-337,-286,-856,-350,-576,-653,-219,-325,-1200,-49,-2664,-856,-344,-520,-766,-286,-291,-1334,-591,-346,-320,-342,-393,-503,-316,-280,-256,-307,-606,-269,-302,-443,-297,-272,-281,-288,-287,-302,-414,-312,-1196,-214,-245,-282,-315,-268,-966,-270,-315,-248,-502,-316,-299,-256,-358,-276,-288,-331,-282,-845,-409,-293,-292,-481,-291,-271,-279,-270,-4948,-402,-280,-261,-293,-765,-162,-265,-266,-253,-869,-237,-285,-276,-225,-229,-446,-567,-272,-876,-260,-251,-284,-2709,-263,-3948,-479,-269,-959,-267,-762,-252,-263,-251,-485,-753,-362,-257,-910,-972,-233,-455,-673,-272,-259,-243,-616,-628,-262,-232,-733,-236,-247,-479,-983,-240,-306,-256,-2269,-262,-106,-225,-893,-238,-253,-373,-476,-234,-469,-354,-219,-731,-255,-262,-317,-275,-518,-894,-251,-3278,-1219,-256,-261,-304,-241,-251,-253,-140,-877,-231,-261,-319,-259,-250,-246,-279,-245,-262,-257,-752,-238,-311,-298,-253,-280,-296,-285,-280,-265,-503,-260,-890,-555,-289,-246,-256,-241,-286,-247,-1881,-267,-225,-252,-911,-283,-250,-466,-269,-241,-270,-259,-908,-255,-447,-432,-244,-373,-816,-391,-3466,-1384,-937,-490,-629,-837,-900,-385,-290,-249,-256,-422,-224,-210,-235,-191,-318,-960,-250,-1124,-254,-247,-639,-291,-487,-229,-225,-258,-513,-466,-347,-216,-313,-250,-242,-310,-234,-377,-234,-247,-891,-293,-242,-830,-227,-654,-238,-1341,-487,-263,-246,-240,-219,-717,-247,-730,-385,-226,-257,-238,-259,-246,-253,-418,-259,-247,-468,-270,-273,-228,-518,-262,-1035,-242,-257,-276,-1165,-252,-1456,-416,-1974,-267,-1211,-437,-481,-1143,-252,-321,-234,-891,-705,-667,-246,-771,-907,-978,-80,-272,-285,-1236,-249,-589,-96,-349,-155,-241,-239,-235,-247,-264,-557,-298,-338,-428,-238,-236,-1515,-257,-415,-389,-267,-477,-1628,-237,-248,-265,-237,-304,-229,-297,-238,-236,-243,-265,-461,-1370,-241,-948,-389,-341,-336,-603,-449,-245,-1716,-258,-66,-397,-289,-985,-299,-1063,-260,-428,-254,-254,-566,-264,-258,-911,-1156,-268,-399,-245,-430,-263,-1083,-286,-1307,-442,-217,-268,-1223,-226,-419,-281,-629,-1145,-297,-247,-341,-1003,-414,-1312,-368,-998,-343,-760,-1214,-264,-1336,-932,-1188,-268,-395,-851,-262,-797,-478,-251,-298,-833,-788,-532,-1440,-1135,-444,-913,-1876,-395,-845,-734,-1458,-843,-994,-688,-438,-349,-724,-208,-764,-722,-493,-703,-423,-465,-431,-482,-385,-1141,-1228,-689,-439,-334,-386,-761,-466,-634,-328,-477,-2190,-652,-736,-490,-394,-497,-306,-426,-395,-1566,-297,-452,-328,-271,-341,-340,-279,-363,-346,-340,-323,-462,-341,-334,-1320,-275,-304,-523,-408,-340,-315,-314,-480,-335,-328,-294,-1142,-403,-1115,-416,-155,-254,-412,-468,-873,-752,-332,-324,-731,-344,-551,-514,-303,-335,-81,-320,-286,-371,-313,-289,-282,-294,-348,-345,-758,-1006,-964,-386,-1166,-283,-501,-931,-638,-363,-474,-354,-1394,-314,-324,-365,-385,-293,-951,-316,-1082,-943,-243,-554,-303,-265,-1044,-889,-304,-938,-302,-317,-302,-290,-523,-314,-272,-917,-277,-311,-943,-836,-414,-490,-319,-289,-298,-317,-285,-267,-309,-323,-357,-277,-335,-278,-300,-742,-897,-353,-3626,-284,-287,-418,-343,-1691,-314,-270,-239,-1182,-1260,-329,-273,-311,-283,-935,-469,-256,-281,-272,-222,-1116,-417,-281,-227,-278,-933,-429,-281,-310,-940,-278,-262,-271,-263,-266,-265,-249,-263,-264,-273,-249,-258,-244,-776,-228,-247,-237,-275,-673,-712,-415,-224,-305,-248,-252,-226,-252,-896,-960,-259,-270,-234,-1483,-235,-276,-259,-946,-379,-250,-241,-249,-1238,-247,-1602,-453,-240,-1193,-238,-636,-1053,-258,-245,-1337,-441,-253,-1179,-267,-850,-678,-336,-275,-257,-276,-244,-227,-1657,-294,-932,-359,-256,-496,-1201,-423,-243,-474,-724,-237,-1016,-1814,-269,-214,-1019,-239,-256,-478,-544,-241,-473,-289,-303,-415,-261,-249,-259,-257,-259,-339,-241,-229,-245,-616,-303,-1142,-260,-250,-442,-294,-85,-1252,-1201,-285,-255,-373,-291,-249,-268,-245,-459,-841,-254,-247,-425,-935,-1043,-248,-250,-401,-268,-225,-414,-252,-339,-292,-961,-231,-874,-1041,-1530,-291,-1264,-667,-235,-259,-243,-239,-246,-882,-236,-484,-224,-242,-1247,-543,-247,-1323,-256,-234,-224,-257,-1255,-209,-307,-238,-240,-281,-3366,-639,-676,-303,-234,-255,-229,-751,-265,-232,-232,-239,-479,-264,-243,-314,-344,-1073,-263,-221,-253,-209,-337,-210,-210,-312,-295,-976,-118,-224,-978,-406,-250,-250,-304,-398,-1350,-259,-269,-241,-401,-248,-238,-1325,-248,-248,-269,-276,-850,-247,-216,-291,-1012,-243,-266,-253,-398,-895,-225,-361,-260,-248,-955,-1530,-260,-275,-271,-272,-258,-1096,-280,-262,-227,-247,-607,-245,-262,-240,-242,-248,-245,-403,-268,-253,-246,-241,-243,-243,-419,-246,-81,-257,-372,-252,-237,-249,-261,-246,-236,-252,-262,-252,-310,-238,-241,-271,-818,-236,-277,-238,-255,-238,-261,-239,-219,-249,-244,-200,-235,-263,-379,-372,-270,-260,-234,-713,-1011,-265,-259,-231,-468,-256,-167,-710,-534,-268,-313,-274,-258,-711,-260,-248,-255,-1015,-468,-514,-232,-455,-226,-358,-219,-270,-250,-129,-239,-247,-305,-264,-274,-244,-370,-1115,-230,-1834,-252,-946,-240,-249,-235,-755,-248,-1036,-244,-230,-226,-1166,-629,-311,-253,-900,-245,-907,-268,-251,-309,-248,-307,-241,-238,-820,-236,-285,-751,-232,-250,-236,-246,-754,-977,-925,-277,-894,-240,-224,-246,-56,-500,-249,-791,-1523,-225,-243,-315,-231,-267,-439,-232,-223,-238,-243,-276,-240,-259,-261,-314,-256,-323,-245,-261,-414,-286,-89,-232,-268,-818,-764,-1122,-652,-518,-279,-1081,-241,-262,-234,-171,-992,-274,-444,-321,-248,-884,-714,-231,-258,-215,-825,-246,-530,-282,-438,-259,-260,-374,-627,-578,-603,-794,-965,-227,-237,-224,-431,-242,-236,-245,-492,-242,-779,-242,-219,-246,-224,-364,-265,-188,-1278,-1909,-384,-51,-1974,-36,-225,-573,-878,-292,-213,-2668,-251,-1161,-906,-697,-914,-261,-474,-320,-564,-314,-536,-331,-274,-262,-261,-753,-815,-254,-294,-273,-259,-1798,-226,-297,-222,-258,-791,-1254,-254,-767,-259,-245,-304,-235,-260,-275,-940,-244,-843,-279,-262,-282,-1031,-233,-265,-983,-259,-256,-254,-278,-699,-1391,-1412,-285,-106,-768,-241,-372,-276,-121,-274,-257,-276,-1039,-560,-1277,-260,-267,-547,-239,-118,-423,-239,-1278,-893,-366,-352,-505,-1450,-1063,-233,-710,-500,-227,-239,-937,-784,-564,-231,-601,-281,-509,-256,-229,-264,-589,-1738,-322,-589,-868,-253,-248,-945,-128,-309,-953,-3122,-1375,-227,-251,-250,-255,-1133,-307,-247,-247,-128,-322,-283,-307,-728,-922,-257,-255,-226,-509,-221,-3099,-501,-884,-317,-961,-1064,-738,-216,-252,-933,-988,-369,-322,-324,-234,-433,-677,-386,-301,-544,-3013,-362,-320,-938,-430,-315,-349,-299,-287,-798,-331,-331,-522,-379,-277,-579,-342,-263,-255,-426,-278,-770,-269,-825,-875,-449,-248,-247,-265,-990,-446,-222,-1037,-110,-218,-417,-290,-388,-2178,-247,-522,-3693,-266,-248,-811,-427,-231,-339,-247,-457,-264,-549,-1122,-285,-971,-243,-1121,-277,-575,-447,-297,-670,-262,-500,-747,-214,-611,-727,-601,-1677,-814,-875,-234,-606,-504,-222,-943,-3983,-1216,-352,-199,-975,-3352,-314,-204,-963,-437,-1236,-331,-226,-281,-1188,-331,-658,-539,-318,-441,-291,-244,-294,-276,-267,-879,-258,-259,-261,-841,-408,-569,-796,-228,-308,-278,-287,-463,-522,-439,-301,-275,-780,-557,-260,-427,-302,-266,-268,-164,-314,-932,-290,-497,-309,-1000,-1786,-288,-237,-283,-548,-311,-288,-426,-303,-110,-82,-284,-338,-261,-285,-1551,-98,-283,-478,-4001,-262,-406,-460,-298,-357,-224,-274,-872,-90,-1089,-949,-407,-210,-541,-274,-860,-818,-2829,-240,-265,-235,-929,-574,-365,-599,-312,-377,-914,-322,-234,-251,-300,-247,-236,-240,-503,-217,-258,-258,-234,-906,-239,-262,-747,-253,-1855,-261,-451,-775,-265,-544,-413,-932,-268,-755,-1312,-698,-3463,-766,-383,-265,-509,-262,-297,-684,-407,-232,-1317,-407,-785,-458,-381,-317,-263,-282,-193,-471,-240,-637,-225,-1066,-313,-452,-803,-321,-1316,-245,-253,-1015,-258,-459,-265,-237,-281,-263,-248,-250,-254,-269,-769,-1479,-240,-260,-363,-254,-975,-248,-703,-351,-384,-257,-315,-259,-266,-271,-1136,-243,-278,-400,-905,-361,-642,-456,-362,-257,-242,-264,-1300,-255,-1413,-250,-968,-225,-255,-324,-278,-283,-425,-265,-454,-412,-1002,-316,-281,-243,-252,-1125,-336,-905,-291,-389,-602,-323,-296,-307,-108,-392,-246,-254,-254,-219,-282,-275,-607,-532,-436,-237,-218,-1216,-259,-249,-1838,-268,-215,-203,-265,-287,-808,-913,-1299,-1845,-791,-247,-714,-2586,-1130,-441,-398,-387,-226,-731,-186,-2805,-294,-954,-267,-751,-159,-307,-254,-290,-253,-277,-273,-262,-249,-503,-255,-248,-393,-272,-668,-990,-1045,-306,-244,-982,-788,-775,-1011,-1174,-1431,-141,-429,-285,-236,-217,-240,-1035,-143,-292,-808,-637,-223,-386,-871,-3825,-862,-352,-1761,-609,-906,-605,-300,-326,-438,-964,-467,-790,-249,-238,-1371,-721,-356,-261,-284,-300,-1071,-423,-649,-605,-341,-308,-85,-266,-672,-283,-271,-1070,-412,-293,-1622,-375,-226,-348,-458,-279,-254,-296,-235,-279,-399,-443,-287,-301,-500,-1182,-374,-238,-318,-101,-2923,-80,-427,-279,-267,-933,-812,-243,-822,-255,-263,-270,-259,-282,-229,-275,-250,-271,-236,-363,-182,-1314,-259,-260,-260,-1208,-147,-451,-276,-294,-85,-1024,-255,-312,-1283,-296,-277,-472,-210,-1021,-226,-235,-1046,-1677,-282,-215,-724,-1105,-262,-296,-333,-261,-274,-216,-239,-251,-293,-500,-892,-67,-1020,-971,-585,-889,-83,-210,-1002,-311,-269,-416,-706,-890,-246,-407,-675,-647,-266,-288,-855,-619,-964,-264,-276,-851,-347,-545,-788,-1030,-942,-442,-236,-246,-222,-330,-970,-506,-248,-694,-260,-469,-320,-218,-422,-297,-255,-1382,-251,-267,-1026,-574,-1076,-88,-347,-466,-86,-343,-2118,-423,-955,-470,-445,-935,-347,-308,-426,-289,-242,-582,-296,-249,-289,-273,-255,-300,-292,-409,-305,-883,-1238,-933,-260,-466,-292,-309,-400,-304,-89,-255,-301,-141,-227,-263,-48,-415,-243,-437,-359,-203,-263,-255,-306,-252,-277,-458,-849,-230,-213,-480,-358,-370,-649,-287,-227,-247,-267,-261,-383,-258,-300,-1203,-305,-840,-273,-260,-329,-234,-288,-283,-287,-245,-299,-240,-276,-1009,-183,-251,-329,-452,-265,-472,-710,-1232,-255,-265,-913,-343,-431,-288,-1293,-358,-262,-282,-245,-258,-233,-266,-238,-494,-217,-263,-248,-83,-287,-299,-368,-188,-271,-488,-1314,-244,-879,-276,-876,-408,-104,-237,-416,-97,-259,-545,-216,-1066,-806,-530,-277,-485,-283,-330,-1282,-277,-327,-248,-943,-782,-287,-260,-311,-252,-297,-117,-345,-892,-323,-268,-339,-259,-279,-254,-234,-257,-258,-84,-233,-292,-229,-250,-768,-259,-783,-251,-384,-1574,-459,-345,-259,-987,-244,-242,-279,-317,-266,-249,-1029,-262,-330,-104,-274,-1317,-268,-3084,-264,-546,-248,-322,-276,-261,-1147,-459,-247,-983,-1307,-262,-272,-98,-3188,-260,-1776,-233,-239,-329,-171,-241,-252,-370,-228,-1478,-238,-269,-252,-458,-788,-82,-387,-643,-1383,-860,-239,-250,-954,-252,-405,-261,-778,-369,-245,-339,-263,-270,-565,-235,-243,-283,-1181,-267,-299,-196,-2322,-1029,-579,-2459,-259,-250,-289,-810,-262,-904,-284,-246,-1027,-896,-882,-1115,-405,-685,-1152,-417,-856,-405,-257,-957,-252,-288,-259,-903,-444,-472,-932,-244,-1112,-277,-484,-309,-354,-698,-272,-304,-638,-915,-260,-938,-349,-2327,-795,-222,-2182,-330,-294,-127,-1030,-378,-1591,-277,-1031,-328,-796,-543,-535,-810,-150,-1074,-274,-127,-295,-1071,-405,-237,-1242,-256,-483,-842,-541,-550,-240,-252,-927,-244,-263,-516,-1047,-896,-90,-258,-89,-263,-101,-318,-264,-297,-922,-253,-242,-258,-234,-1071,-288,-302,-239,-264,-254,-234,-575,-216,-259,-623,-267,-256,-1026,-239,-443,-1010,-1410,-1119,-235,-435,-202,-658,-293,-411,-272,-801,-534,-271,-1721,-246,-254,-524,-2910,-357,-535,-243,-483,-1456,-728,-384,-678,-833,-528,-440,-795,-410,-300,-318,-188,-257,-524,-329,-252,-445,-264,-1138,-289,-419,-242,-2986,-303,-444,-319,-681,-1078,-256,-1100,-244,-241,-282,-1034,-267,-603,-1060,-490,-374,-277,-267,-282,-264,-444,-269,-260,-278,-227,-321,-242,-769,-572,-811,-250,-236,-318,-493,-1200,-497,-953,-1612,-448,-495,-1456,-362,-519,-975,-953,-278,-216,-2591,-3872,-130,-368,-940,-309,-547,-264,-39,-620,-343,-897,-1274,-234,-2464,-258,-155,-226,-854,-297,-243,-686,-285,-360,-1256,-275,-455,-285,-221,-240,-267,-268,-276,-374,-248,-699,-275,-1098,-370,-358,-223,-263,-789,-248,-2618,-571,-826,-917,-361,-223,-2277,-257,-725,-1135,-989,-2561,-2975,-470,-677,-429,-1139,-226,-661,-559,-189,-257,-1147,-615,-1260,-286,-106,-609,-617,-754,-602,-333,-945,-492,-1027,-293,-248,-260,-95,-929,-1380,-290,-272,-290,-2285,-570,-429,-648,-635,-1086,-321,-410,-286,-406,-300,-283,-250,-1162,-254,-459,-317,-228,-280,-997,-827,-313,-290,-349,-1027,-524,-303,-1103,-330,-389,-447,-428,-184,-869,-2244,-257,-250,-275,-2377,-368,-889,-320,-1095,-573,-277,-320,-981,-231,-240,-483,-539,-236,-410,-450,-1044,-510,-387,-238,-149,-262,-289,-1478,-1332,-258,-533,-238,-1092,-801,-536,-277,-1104,-324,-603,-558,-545,-390,-536,-338,-345,-346,-1140,-389,-367,-337,-367,-346,-273,-291,-225,-294,-369,-243,-310,-274,-295,-307,-301,-1297,-2212,-274,-97,-248,-269,-616,-347,-462,-322,-236,-434,-495,-286,-262,-229,-92,-301,-309,-262,-272,-278,-293,-288,-238,-235,-292,-267,-303,-1006,-255,-954,-601,-169,-1422,-1413,-437,-830,-257,-215,-1204,-1546] + } +]; diff --git a/packages/charts/src/chart_types/plot_matrix/plom/src/config.ts b/packages/charts/src/chart_types/plot_matrix/plom/src/config.ts new file mode 100644 index 0000000000..c8255dce1f --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/src/config.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { magmaPalette, turboPalette } from '../colors'; + +const KERNEL_DENSITY = true; +const HEX = !KERNEL_DENSITY && true; // hex vs square grid +const BINNING_SIMPLIFICATION = KERNEL_DENSITY ? 4 : 23; // try anything approx. 16..32 for the latter number + +/** @internal */ +export const DARK_MODE = false; + +/** @internal */ +export const OUTER_WIDTH = 1900; +/** @internal */ +export const OUTER_HEIGHT = 1054; +/** @internal */ +export const VERTICAL_PADDING = 3; // otherwise lines on top or bottom will be truncated (like it used to be in xy) + +/** @internal */ +export const c = { + dpr: 1.5, // can be tweaked: going with window.devicePixelRatio can yield too much frag shader work; 1 may be a bit too coarse + voronoiDistance: 32, + glDebug: false, // allowing it may incur some cost + glTestContextLoss: false, // test the occurrence of a context loss and restoration + eventDebug: false, + binCountX: 256 / BINNING_SIMPLIFICATION + (!KERNEL_DENSITY && HEX ? -1 : 0), // without this hacky adjustment, hexagons are a bit too tall + binCountY: 256 / BINNING_SIMPLIFICATION, + kernelDensity: KERNEL_DENSITY, + hex: !KERNEL_DENSITY && HEX, + kernelRadius: 128 / BINNING_SIMPLIFICATION, // note: Safari's largest gl_PointSize is 64; beyond it, for more than that (hopefully not needed), we can switch to triangle tessellation + kernelExponent: 24, + kernelRadiusKeepInPanelRatio: 0, // points on the side protrude; add some padding + splomPanelSizeRatio: 0.9, + parcoordsX: 950, + parcoordsY: 120, + palettes: { turbo: turboPalette, magma: DARK_MODE ? magmaPalette.reverse() : magmaPalette }, +}; + +/** @internal */ +export const classNames = { + axis: 'axis', + axisBrush: 'axisBrush', + axisHeading: 'axisHeading', + axisOverlays: 'axisOverlays', + axisTitle: 'axisTitle', + dimAxis: 'dimAxis', + plom: 'plom', + plomControlView: 'plomControlView', + plomGeomLayer: 'plomGeoms', + plomGeomLayers: 'plomGeomLayers', +}; diff --git a/packages/charts/src/chart_types/plot_matrix/plom/src/geoms.ts b/packages/charts/src/chart_types/plot_matrix/plom/src/geoms.ts new file mode 100644 index 0000000000..0d315f4ae4 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/src/geoms.ts @@ -0,0 +1,387 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +import { bindFramebuffer, bindVertexArray, readPixel } from '../../../../common/kingly'; +import { GL } from '../../../../common/webgl_constants'; +import { c, VERTICAL_PADDING } from './config'; +import { clamp, range } from './utils'; + +const DIMENSION_COUNT = 64; // max would be 64 scalar dimensions, but gl_VertexID counts as an attrib; change implies shader change +const FLOAT_COUNT_PER_VEC4 = 4; // each vec4 has 4 scalars (floats) +const ATTRIBUTE_COUNT = DIMENSION_COUNT / FLOAT_COUNT_PER_VEC4; // 16 is not coincidentally the maximum attribute count +const VERTEX_COUNT_PER_LINE_SECTION = 2; // each line section has two vertices +const DEPTH_EPSILON = 0.000001; // without this, near or far lines may be lost due to imprecise fp representation +const PICK_INDEX_BASE = 1; +const PALETTE_LENGTH = 256; +const PALETTE_MAX_INDEX = PALETTE_LENGTH - 1; +const VEC_INDICES = range(ATTRIBUTE_COUNT); +const AXIS_COUNT_PER_PANEL = 2; // parcoords: yLeft, yRight; scatterplot: x, y +const RANGE_POINT_COUNT = 2; // minimum and maximum + +// Mapping the tuple index [0...tupleCount - 1] to the unit range [0, 1] per RGB channel since the shader expects it like that +// The tuple index is shifted by 0, 8 or 16 bits depending on rgbIndex [0..2] so each point/line gets a unique color +const nullPickIdentifier = [0, 0, 0]; // zero means, no point, so we add 1 to j (datum index 0 will get the value of 1) +const getPickColor = (i, rgbIndex) => (((i + PICK_INDEX_BASE) >>> (8 * rgbIndex)) % PALETTE_LENGTH) / PALETTE_MAX_INDEX; + +const getAttributeArray = (dimensionColumns, color) => { + const dimensionCount = dimensionColumns.length; + const tupleCount = dimensionColumns[0].length ?? 0; + return Object.fromEntries( + VEC_INDICES.map((vecIndex) => { + const pointPairs = []; + for (let j = 0; j < tupleCount; j++) { + for (let k = 0; k < VERTEX_COUNT_PER_LINE_SECTION; k++) { + for (let i = 0; i < FLOAT_COUNT_PER_VEC4; i++) { + const dimensionIndex = vecIndex * FLOAT_COUNT_PER_VEC4 + i; + const attribValue = + dimensionIndex < dimensionCount + ? dimensionColumns[dimensionIndex][j] // within dimensionColumns? move dimension value for the point + : dimensionIndex === DIMENSION_COUNT - 1 + ? clamp(color[j], DEPTH_EPSILON, 1 - DEPTH_EPSILON) // last dimension is always depth, with a small FP precision margin + : dimensionIndex >= DIMENSION_COUNT - 4 + ? getPickColor(j, DIMENSION_COUNT - 2 - dimensionIndex) // the three preceding dimensionColumns encode color + : 0.5; // just put the midpoint value in the unused dimensionColumns + // we can't use gl_VertexID and gl_InstanceID because they'd count as one vertex array each, but we already use 64 + pointPairs.push(dimensionIndex === DIMENSION_COUNT - 1 && k % 2 === 0 ? -attribValue : attribValue); + } + } + } + return [`v${vecIndex.toString(16)}`, pointPairs]; + }), + ); +}; + +// representation: out of the 64 dimensions, put an 1 where either of the two axes used by a specific panel is used, 0 otherwise +const dimensionSelections = range(AXIS_COUNT_PER_PANEL).map(() => + range(ATTRIBUTE_COUNT).map(() => new Float32Array(ATTRIBUTE_COUNT)), +); + +// representation: for each of the 64 dimensions, 0 indicates the lower bound, 1 the upper bound of the range (brush) filter +const rangeLimits = range(RANGE_POINT_COUNT).map(() => + range(ATTRIBUTE_COUNT).map(() => new Float32Array(ATTRIBUTE_COUNT)), +); + +/** @internal */ +export const geomLayerMaker = (gl: WebGL2RenderingContext, model, dimensions, renderLayers, textures, attributes) => { + if (gl.isContextLost()) return; + + const { dpr } = model; + const initialDims = dimensions.slice(); + const { width: preDprCanvasWidth, height: preDprCanvasHeight, line, domain } = model; + + const canvasWidth = dpr * preDprCanvasWidth; + const canvasHeight = dpr * preDprCanvasHeight; + + const dimensionCount = initialDims.length; + + const isPickLayer = false; + const color = isPickLayer ? line.color.map((_, i) => i / line.color.length) : line.color; + + const attributeData = getAttributeArray( + initialDims.map((d) => d.paddedUnitValues), + color, + ); + + // add two triangles to form a quad with GL.TRIANGLE_STRIP; also interpretable as two vertical lines (left/right side) with GL.LINES + Object.entries(attributeData).forEach( + ([, value]) => value.push(0, 0, 0, 0, /**/ 0, 1, 0, 1, /**/ 1, 0, 0, 2, /**/ 1, 1, 0, 3), // two triangles with GL.TRIANGLE_STRIP + ); + + // add two lines, so, with the GL.LINES interpretation of the previous addition above, a rectangle can be drawn from 4 lines + Object.entries(attributeData).forEach( + ([, value]) => value.push(0, 0, 0, 4, /**/ 1, 0, 0, 5, /**/ 0, 1, 0, 6, /**/ 1, 1, 0, 7), // two horizontal lines with GL.LINES + ); + + const preexistingAxisOrder = []; + + /** + * Vertex arrays: setting up and filling the `datum` array for webgl + * + * We have a single global shared vertex array object now, so we can bind it once and forget it + */ + // spectorLog(gl, 'create/bind vao, enable array attribs, buffer data') + const vao = gl.createVertexArray(); + bindVertexArray(gl, vao); + attributes.forEach((setValue, name) => setValue(new Float32Array(attributeData[name]))); + + const tupleCount = initialDims[0] ? initialDims[0].values.length : 0; + const vertexCount = VERTEX_COUNT_PER_LINE_SECTION * tupleCount; + + const render = (panels, setChanged, renderPickTexture, hoveredPointId) => { + // spectorLog(gl, 'render the scene') + if (gl.isContextLost()) return; + + const isHoveredPointId = Number.isFinite(hoveredPointId); + + const hoverIdentifier = isHoveredPointId + ? [2, 1, 0].map((byteIndex) => getPickColor(hoveredPointId - PICK_INDEX_BASE, byteIndex)) + : nullPickIdentifier; + + let first = Infinity; + let last = -Infinity; + + for (const [, panel] of panels) { + last = Math.max(last, panel.xIndex); + first = Math.min(first, panel.xIndex); + } + + textures.binningRaster1d.clear(); + textures.binningRaster2d.clear(); + textures.binRanges1d.clear(); + textures.binRanges2d.clear(); + + for (const panel of [...panels.values()] + .flat() + .sort( + (a, b) => + (renderLayers[a.renderLayer]?.zIndex ?? 0) - (renderLayers[b.renderLayer]?.zIndex ?? 0) || + a.yIndex - b.yIndex || + a.xIndex - b.xIndex, + )) { + const renderLayerName = panel.renderLayer; + const renderLayer = renderLayers[renderLayerName]; + if (!renderLayer) continue; // useful for debugging, with certain programs commented out to bypass + const { xIndex } = panel; + const { yIndex } = panel; + if (!panel || !panel.visible) { + continue; + } + const x = dpr * panel.canvasX; + const y = dpr * panel.canvasY; + const axisIndex0 = panel.dim1.crossfilterDimensionIndex; + const axisIndex1 = panel.dim2.crossfilterDimensionIndex; + const panelSizeX = dpr * panel.panelSizeX; + const panelSizeY = dpr * panel.panelSizeY; + const xTo = x + panelSizeX; + if ( + setChanged || + (panel.yIndex === 0 && + (!preexistingAxisOrder[axisIndex0] || + preexistingAxisOrder[axisIndex0][0] !== x || + preexistingAxisOrder[axisIndex0][1] !== xTo)) + ) { + if (panel.yIndex === 0) preexistingAxisOrder[axisIndex0] = [x, xTo]; + const hasHoverHighlight = renderLayerName === 'scatterplot' || renderLayerName === 'parcoords'; + const isAreaChart = renderLayerName === 'areaChart'; + const is1d = renderLayerName === 'binningRaster1d' || isAreaChart || renderLayerName === 'binRanges1d'; + + const { binCountX } = c; + const binCountY = is1d ? 1 : c.binCountY; + + const isDiagonal = yIndex - 1 === xIndex; + + const binningRaster = renderLayerName === 'binningRaster1d' || renderLayerName === 'binningRaster2d'; + const binRanges = renderLayerName === 'binRanges1d' || renderLayerName === 'binRanges2d'; + const parcoords = renderLayerName === 'parcoords'; + const parcoordsPick = renderLayerName === 'parcoordsPick'; + const scatterplot = renderLayerName === 'scatterplot'; + const scatterPick = renderLayerName === 'scatterPick'; + const panelsAcross = dimensionCount; + + const leftRight = [axisIndex0, axisIndex1]; + const filterEpsilon = (dpr * VERTICAL_PADDING) / panelSizeY; + + const upTo = panelsAcross; + + for (let loHi = 0; loHi < 2; loHi++) { + // lower vs higher boundary: + // for `dimensionSelections[loHi]` the `loHi` value represents min(loHi == 0) or max(loHi == 1) of the range + // for `rangeLimits[loHi]` the `loHi` value represents the axis0(loHi == 0) or axis1(loHi == 1) picked for a panel + // where axis0 and axis1 are the two Y axes of a parcoords panel (or the X and Y axes of a scatterplot panel) + for (let vecElemIndex = 0; vecElemIndex < 4; vecElemIndex++) { + for (let d = 0; d < ATTRIBUTE_COUNT; d++) { + const dimP = d + ATTRIBUTE_COUNT * vecElemIndex; + dimensionSelections[loHi][vecElemIndex][d] = + d + ATTRIBUTE_COUNT * vecElemIndex === leftRight[loHi] ? 1 : 0; + rangeLimits[loHi][vecElemIndex][d] = + (d + Math.max(1, ATTRIBUTE_COUNT * vecElemIndex) <= upTo + ? initialDims[dimP === 0 ? 0 : 1 + ((dimP - 1) % (initialDims.length - 1))].brush.filter.get()[loHi] + : loHi) + + (2 * loHi - 1) * filterEpsilon; + } + } + } + + const blotRadius = c.kernelDensity ? c.kernelRadius : 1; + const blotMarginRatio = c.kernelDensity + ? (c.kernelRadiusKeepInPanelRatio * blotRadius) / Math.min(panelSizeX, panelSizeY) + : 0; + const blotUsefulRatio = 1 - 2 * blotMarginRatio; + + const xOffset = dpr * (yIndex === 0 ? 0 : -c.parcoordsX); + const yOffset = dpr * (yIndex === 0 ? 0 : -c.parcoordsY); + const viewportX = dpr * model.pad.l + dpr * model.layoutWidth * domain.x[0]; + const viewportY = dpr * model.pad.b + dpr * model.layoutHeight * domain.y[0]; + const s = yIndex === 0 ? 1 : c.splomPanelSizeRatio; + const rasterOrRange = binningRaster || binRanges; + + const item = { + key: axisIndex0, + + binCount: [binCountX, binCountY], + + // target framebuffer resolution (canvas, or target texture) + resolution: binningRaster + ? [binCountX * panelsAcross, binCountY * panelsAcross] + : binRanges + ? [panelsAcross, panelsAcross] + : [canvasWidth, canvasHeight], + + // top left corner of the rectangle to draw to on the target framebuffer (in pixels/texels) + viewBoxPosition: binningRaster + ? [ + xIndex * binCountX + blotMarginRatio * binCountX, + (panelsAcross - yIndex) * binCountY + blotMarginRatio * binCountY, + ] + : binRanges + ? [xIndex, panelsAcross - yIndex] + : [x, y], + + // size of the rectangle to draw to on the target framebuffer (in pixels/texels) + viewBoxSize: binningRaster + ? [blotUsefulRatio * binCountX, blotUsefulRatio * binCountY] + : binRanges + ? [1, 1] + : [s * panelSizeX, s * panelSizeY], + + // near-zero if parcoords, one if scatterplot + scatter: yIndex === 0 ? 0.00001 : 1, // todo why does it not work with an exact zero as designed and as it works with parcoords (issue shows when filtering) + + // gl_PointSize will get this + pointSize: scatterPick + ? c.voronoiDistance // will be overridden below anyway + : binningRaster + ? c.kernelDensity + ? c.kernelRadius + : 1 + : binRanges + ? 1 + : dpr > 1 + ? 3 + : 2, + kernelExponent: c.kernelExponent, + i: axisIndex0, + ii: axisIndex1, + panelIndex: xIndex, + + dim1A: dimensionSelections[0][0], + dim1B: dimensionSelections[0][1], + dim1C: dimensionSelections[0][2], + dim1D: dimensionSelections[0][3], + dim2A: dimensionSelections[1][0], + dim2B: dimensionSelections[1][1], + dim2C: dimensionSelections[1][2], + dim2D: dimensionSelections[1][3], + + contextPointRadius: scatterplot ? 0.1 : 0, // eliminating crossfiltered points from binning or point picking + + scissorRect: [ + rasterOrRange + ? (binningRaster ? binCountX : 1) * xIndex + : xOffset + (xIndex === first ? 0 : x) + dpr * model.pad.l + dpr * model.layoutWidth * domain.x[0], + binningRaster + ? binCountY * (panelsAcross - yIndex) + : binRanges + ? panelsAcross - yIndex + : -yOffset + y + dpr * model.pad.b + dpr * model.layoutHeight * domain.y[0], + binningRaster + ? binCountX + : binRanges + ? 1 + : panelSizeX * (parcoords || parcoordsPick ? 1 : c.splomPanelSizeRatio), + binningRaster + ? binCountY + : binRanges + ? 1 + : panelSizeY * (parcoords || parcoordsPick ? 1 : c.splomPanelSizeRatio), + ], + + loA: rangeLimits[0][0], + loB: rangeLimits[0][1], + loC: rangeLimits[0][2], + loD: rangeLimits[0][3], + hiA: rangeLimits[1][0], + hiB: rangeLimits[1][1], + hiC: rangeLimits[1][2], + hiD: rangeLimits[1][3], + + // the viewport, interpreted as pixel/texel position/size on the target framebuffer, will mean the -1..1 horiz and vert ranges of clip space + viewportX: rasterOrRange ? 0 : xOffset + viewportX, + viewportY: rasterOrRange ? 0 : -yOffset + viewportY, + viewportWidth: binningRaster ? panelsAcross * binCountX : binRanges ? panelsAcross : canvasWidth, + viewportHeight: binningRaster ? panelsAcross * binCountY : binRanges ? panelsAcross : canvasHeight, + + palette: textures.palette, + heatmapPalette: textures.heatmapPalette, + pickTexture: textures.pickTexture, // maybe for future 1px by 1px point pick texture + hoverIdentifier: hasHoverHighlight ? hoverIdentifier : nullPickIdentifier, + binningRaster: is1d ? textures.binningRaster1d : textures.binningRaster2d, + binRanges: is1d && !isAreaChart ? textures.binRanges1d : textures.binRanges2d, + zoomScale: yIndex === 0 || isDiagonal ? [1, 1] : panel.zoomScale || [1, 1], + zoomOffset: yIndex === 0 || isDiagonal ? [0, 0] : panel.zoomOffset || [0, 0], + }; + + const offset = Number.isFinite(renderLayer.offset) ? renderLayer.offset : vertexCount; + const count = Number.isFinite(renderLayer.count) ? renderLayer.count : vertexCount; + + const renderer = (item, retain) => + renderLayer.useWith({ + uniformValues: item, + viewport: { x: item.viewportX, y: item.viewportY, width: item.viewportWidth, height: item.viewportHeight }, + target: renderLayer.targetTexture || null, + clear: !retain && + ![ + 'panelBorder', + 'binningRaster1d', + 'binningRaster2d', + 'binRanges1d', + 'binRanges2d', + 'contours2d', + ].includes(renderLayerName) && { + rect: item.scissorRect, + color: [0, 0, 0, 0], + depth: 1, + }, + scissor: renderLayerName !== 'panelBorder' ? item.scissorRect : undefined, + draw: { + geom: renderLayer.geom, + offset, + count, + instanceCount: 1, + }, + }); + + if (renderLayerName === 'scatterPick' && renderPickTexture) { + const minVoronoiDistance = 1; + const maxVoronoiDistance = c.voronoiDistance; + const decrement = 1; + if (c.glDebug) console.log('Rendering scatterPick'); + for (let i = maxVoronoiDistance; i >= minVoronoiDistance; i -= decrement) { + renderer({ ...item, pointSize: i, contextPointRadius: 0 }, i !== maxVoronoiDistance); + } + } else if (renderLayerName !== 'scatterPick') { + renderer(item); + } + } + } + }; + + return { + readPixel: (height, x, y) => { + x -= 2; // slight offset so hover highlight doesn't vanish under mouse pointer tip + y -= 2; // slight offset so hover highlight doesn't vanish under mouse pointer tip + bindFramebuffer(gl, GL.READ_FRAMEBUFFER, textures.pickTexture.target()); + const pixel = readPixel(gl, dpr * x, dpr * (height - y)); + return pixel; + }, + render, + }; +}; diff --git a/packages/charts/src/chart_types/plot_matrix/plom/src/utils.ts b/packages/charts/src/chart_types/plot_matrix/plom/src/utils.ts new file mode 100644 index 0000000000..a279542f8b --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plom/src/utils.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Logger } from '../../../../utils/logger'; + +/** @internal */ +export const keyFun = (d: { key: unknown }) => d.key; +/** @internal */ +export const repeat = (d: unknown) => [d]; +/** @internal */ +export const descend = (d: unknown) => d; +/** @internal */ +export const clamp = (x: number, min: number, max: number) => Math.min(Math.max(x, min), max); +/** @internal */ +export const range = (n: number) => Array.from({ length: n }, (_, i) => i); + +const DEGENERATE_DOMAIN_EXTENSION_RATIO = 0.1; +const DEG_LO = 1 - DEGENERATE_DOMAIN_EXTENSION_RATIO; +const DEG_HI = 1 + DEGENERATE_DOMAIN_EXTENSION_RATIO; + +/** @internal */ +export const getExtent = (values: number[]) => { + const lo = values.reduce((p, n) => Math.min(p, n), Infinity); + const hi = values.reduce((p, n) => Math.max(p, n), -Infinity); + return lo < hi ? [lo, hi] : lo === hi ? (lo === 0 ? [-1, 1] : [lo * DEG_LO, hi * DEG_HI]) : [-1, 1]; +}; + +/** @internal */ +export const format = (formatter: any, strings: any[]) => (v: any, i: number) => { + const text = strings[i]; + return text === null || text === undefined ? formatter(v) : text; +}; + +/** @internal */ +export const getGLExtensions = (gl: WebGL2RenderingContext) => { + // spectorLog(gl, 'activate extensions') + const ext1 = gl.getExtension('EXT_color_buffer_float'); + if (!ext1) { + throw new Error('This chart type currently needs WebGL2 with the EXT_color_buffer_float extension'); + } + const ext2 = gl.getExtension('OES_texture_float_linear'); + if (!ext2) { + throw new Error( + 'This chart type currently needs linear filtering of floating point textures with OES_texture_float_linear', + ); + } + const ext3 = gl.getExtension('KHR_parallel_shader_compile'); + if (!ext3) { + Logger.warn('KHR_parallel_shader_compile unavailable; no cause for concern'); + } +}; + +/** @internal */ +export const simulateContextLoss = (gl: WebGL2RenderingContext) => { + // simulates a context loss at `lossTimeMs` and context recovery at `regainTimeMs` after that + const lossTimeMs = 5000; + const regainTimeMs = 0; + const ext = gl.getExtension('WEBGL_lose_context'); + if (ext) { + window.setTimeout(() => { + Logger.warn('Context loss test triggered, the webgl rendering will freeze or disappear'); + ext.loseContext(); + window.setTimeout(() => ext.restoreContext(), regainTimeMs); + }, lossTimeMs); + } +}; + +/** @internal */ +export const prop = (n: any) => (o: Array) => o[n]; diff --git a/packages/charts/src/chart_types/plot_matrix/plot_matrix.tsx b/packages/charts/src/chart_types/plot_matrix/plot_matrix.tsx new file mode 100644 index 0000000000..1da323fc8b --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plot_matrix.tsx @@ -0,0 +1,418 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { createRef, CSSProperties, RefObject } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; + +import { ChartType } from '..'; +import { DEFAULT_CSS_CURSOR } from '../../common/constants'; +import { bindFramebuffer, createTexture, NullTexture, Texture } from '../../common/kingly'; +import { GL } from '../../common/webgl_constants'; +import { SettingsSpec, SpecType } from '../../specs'; +import { onChartRendered } from '../../state/actions/chart'; +import { BackwardRef, GlobalChartState } from '../../state/chart_state'; +import { getA11ySettingsSelector } from '../../state/selectors/get_accessibility_config'; +import { getChartThemeSelector } from '../../state/selectors/get_chart_theme'; +import { getSettingsSpecSelector } from '../../state/selectors/get_settings_spec'; +import { getSpecsFromStore } from '../../state/utils'; +import { Size } from '../../utils/dimensions'; +import { PlotMatrixStyle } from '../../utils/themes/theme'; +import { domainToUnitScale, unitToColorScale } from './plom/dom/d3utils'; +import { renderPlom } from './plom/dom/plom'; +import { sampleData as dimensions } from './plom/lib/sample_data'; +import { c, DARK_MODE, OUTER_HEIGHT, OUTER_WIDTH } from './plom/src/config'; +import { PlotMatrixSpec } from './plot_matrix_api'; +import { ensureWebgl } from './render/ensure_webgl'; +import { GLResources, NULL_GL_RESOURCES, nullPlotMatrixViewModel } from './types'; + +const PALETTE_LENGTH = 256; +const PALETTE_MAX_INDEX = PALETTE_LENGTH - 1; + +const makePalette = (unitValueToColor: (n: number) => [number, number, number], alpha: number) => + [...new Array(PALETTE_LENGTH)].map((_, i) => [...unitValueToColor(i / PALETTE_MAX_INDEX), alpha]); + +const colorPalette: [number, [number, number, number]][] = [...c.palettes.turbo] + .reverse() + .map(([r, g, b], i) => [i / 255, [r, g, b]]); +const paletteData = makePalette(unitToColorScale(colorPalette), PALETTE_MAX_INDEX); + +/** Get the config: todo get it via normal data flow in the future */ + +const lineValues = dimensions.find((d: any) => d.label === 'CRC')!.values; + +const visibleDimensions = new Set([ + 'Block count', + 'Block size', + 'Total size', + 'Total cost', + 'Excess charge', + 'Bandwidth', + 'Payload', +]); + +const downPushOfParcoords = c.parcoordsY; + +const layoutCombined = { + margin: { t: 20 + downPushOfParcoords, r: 20, b: 60 - downPushOfParcoords, l: 130 + c.parcoordsX }, + visibleDimensions, +}; + +const baseConfig = { + colorPalette: [...c.palettes.turbo].reverse().map(([r, g, b], i) => [i / 255, `rgb(${r},${g},${b})`]), + heatmapPalette: c.palettes.magma.map(([r, g, b], i) => [i / 255, `rgb(${r},${g},${b})`]), + lineColor: lineValues, + domain: { x: [0, 1], y: [0, 1] }, + line: { + color: lineValues.map(domainToUnitScale(lineValues)), + colorValueMin: lineValues.reduce((p, n) => Math.min(p, n), Infinity), + colorValueMax: lineValues.reduce((p, n) => Math.max(p, n), -Infinity), + }, +}; + +const configs = [{ ...baseConfig, layout: layoutCombined, dimensions }].map((config: any) => ({ + ...config, + canvasWidth: OUTER_WIDTH, + canvasHeight: OUTER_HEIGHT, + screenDimensions: { + width: OUTER_WIDTH - config.layout.margin.l - config.layout.margin.r, + height: OUTER_HEIGHT - config.layout.margin.t - config.layout.margin.b, + margin: config.layout.margin, + dpr: c.dpr, + visibleDimensions: config.layout.visibleDimensions, + }, +})); + +const eventCallback = (event: unknown, data: { datumIndex: number }) => + c.eventDebug && data?.datumIndex && console.log('Event:', event, data.datumIndex); + +interface StateProps { + theme: PlotMatrixStyle; + plotMatrixViewModel: PlotMatrixSpec['columnarData']; + chartDimensions: Size; + a11ySettings: ReturnType; + onElementOver: NonNullable; + onElementClick: NonNullable; + onElementOut: NonNullable; + onRenderChange: NonNullable; + forwardStageRef2: RefObject; +} + +interface DispatchProps { + onChartRendered: typeof onChartRendered; +} + +interface OwnProps { + containerRef: BackwardRef; + forwardStageRef: RefObject; +} + +type PlotMatrixProps = StateProps & DispatchProps & OwnProps; + +class PlotMatrixComponent extends React.Component { + static displayName = 'PlotMatrix'; + + // DOM API Canvas2d and WebGL resources + private glContext: WebGL2RenderingContext | null = null; + private palette: Texture = NullTexture; + private heatmapPalette: Texture = NullTexture; + private pickTexture: Texture = NullTexture; + private binningRaster1d: Texture = NullTexture; + private binningRaster2d: Texture = NullTexture; + private binRanges1d: Texture = NullTexture; + private binRanges2d: Texture = NullTexture; + + private glResources: GLResources = NULL_GL_RESOURCES; + private readonly glCanvasRef: RefObject = createRef(); + + constructor(props: Readonly) { + super(props); + const columns = this.props.plotMatrixViewModel; + + // vector length checks + const datumCount = columns.position1.length / 2; + if (datumCount % 1) throw new Error('plotMatrix error: position1 vector must have even values (x/y pairs)'); + if (datumCount * 2 !== columns.position0.length) + throw new Error('plotMatrix error: Mismatch between position0 (xy) and position1 (xy) length'); + if (datumCount !== columns.size0.length) + throw new Error('plotMatrix error: Mismatch between position1 (xy) and size0 length'); + if (datumCount !== columns.size1.length) + throw new Error('plotMatrix error: Mismatch between position1 (xy) and size1 length'); + if (datumCount * 4 !== columns.color.length) + throw new Error('plotMatrix error: Mismatch between position1 (xy) and color (rgba) length'); + if (datumCount !== columns.value.length) + throw new Error('plotMatrix error: Mismatch between position1 (xy) and value length'); + if (datumCount !== columns.label.length) + throw new Error('plotMatrix error: Mismatch between position1 (xy) and label length'); + } + + componentDidMount = () => { + /* + * the DOM element has just been appended, and getContext('2d') is always non-null, + * so we could use a couple of ! non-null assertions but no big plus + */ + this.tryCanvasContext(); + this.drawCanvas(); + this.props.onChartRendered(); + }; + + private ensureTextureAndDraw = () => { + this.ensurePickTexture(); + this.drawCanvas(); + }; + + // componentDidUpdate = () => this.ensureTextureAndDraw(); + + private getActiveCursor = (): CSSProperties['cursor'] => DEFAULT_CSS_CURSOR; + + render = () => { + const { + forwardStageRef, + forwardStageRef2, + chartDimensions: { width: requestedWidth, height: requestedHeight }, + a11ySettings, + } = this.props; + const width = requestedWidth; + const height = requestedHeight; + const style: CSSProperties = { + width, + height, + top: 0, + left: 0, + padding: 0, + margin: 0, + border: 0, + position: 'absolute', + cursor: this.getActiveCursor(), + }; + + const { dpr } = c; + const canvasWidth = width * dpr; + const canvasHeight = height * dpr; + + return ( + <> +
+ + + +
+ + ); + }; + + private drawCanvas = () => { + if ( + !this.props.forwardStageRef?.current || + !this.props.forwardStageRef2?.current || + !this.glContext || + !this.pickTexture + ) + return; + + const glCanvasElement = this.props.forwardStageRef.current; + const svgElement = this.props.forwardStageRef2.current; + + const textures = { + palette: this.palette, + heatmapPalette: this.heatmapPalette, + pickTexture: this.pickTexture, + binningRaster1d: this.binningRaster1d, + binningRaster2d: this.binningRaster2d, + binRanges1d: this.binRanges1d, + binRanges2d: this.binRanges2d, + }; + renderPlom(this.glContext, svgElement, glCanvasElement, eventCallback, configs, this.glResources, textures); + + this.props.onRenderChange(true); // emit API callback + }; + + private ensurePickTexture = () => { + const { width, height } = this.props.chartDimensions; + const pr = c.dpr; + const textureWidth = pr * width; + const textureHeight = pr * height; + const current = this.pickTexture; + const gl = this.glContext; + const { dpr } = c; + const dimensionCount = 7; // todo fix this + const binningRasterRowCount = dimensionCount; + const binningRasterColumnCount = dimensionCount; + + if (gl && (current === NullTexture || current.width !== textureWidth || current.height !== textureHeight)) { + // (re)create texture + current.delete(); + this.palette = + createTexture(gl, { + textureIndex: 0, + width: PALETTE_LENGTH, + height: 1, + internalFormat: GL.RGBA8, + data: new Uint8Array(paletteData.flat()), + }) ?? NullTexture; + this.heatmapPalette = + createTexture(gl, { + textureIndex: 1, + width: PALETTE_LENGTH, + height: 1, + internalFormat: GL.RGBA8, + data: new Uint8Array(c.palettes.magma.flat()), + }) ?? NullTexture; + this.binningRaster1d = + createTexture(gl, { + textureIndex: 2, + width: c.binCountX * binningRasterColumnCount, + height: binningRasterRowCount, + internalFormat: GL.RGBA32F, + data: null, + }) ?? NullTexture; + this.binningRaster2d = + createTexture(gl, { + textureIndex: 3, + width: c.binCountX * binningRasterColumnCount, + height: c.binCountY * binningRasterRowCount, + internalFormat: GL.RGBA32F, + min: c.kernelDensity ? GL.LINEAR : GL.NEAREST, + mag: c.kernelDensity ? GL.LINEAR : GL.NEAREST, + data: null, + }) ?? NullTexture; + this.binRanges1d = + createTexture(gl, { + textureIndex: 4, + width: binningRasterColumnCount, + height: binningRasterRowCount, + internalFormat: GL.RGBA32F, + data: null, + }) ?? NullTexture; + this.binRanges2d = + createTexture(gl, { + textureIndex: 5, + width: binningRasterColumnCount, + height: binningRasterRowCount, + internalFormat: GL.RGBA32F, + data: null, + }) ?? NullTexture; + this.pickTexture = + createTexture(gl, { + textureIndex: 6, + width: Math.ceil(dpr * outerWidth), + height: Math.ceil(dpr * outerHeight), + internalFormat: GL.RGBA8, + data: null, + }) ?? NullTexture; + + bindFramebuffer(gl, GL.READ_FRAMEBUFFER, this.pickTexture.target()); + } + }; + + private initializeGL = (gl: WebGL2RenderingContext) => { + this.glResources = ensureWebgl( + gl, + this.pickTexture, + this.binningRaster1d, + this.binningRaster2d, + this.binRanges1d, + this.binRanges2d, + ); + }; + + private restoreGL = (gl: WebGL2RenderingContext) => { + this.initializeGL(gl); + this.pickTexture = NullTexture; + this.ensureTextureAndDraw(); + }; + + private tryCanvasContext = () => { + const glCanvas = this.glCanvasRef.current; + + this.glContext = glCanvas && glCanvas.getContext('webgl2'); + + this.ensurePickTexture(); + + if (glCanvas && this.glContext && this.glResources === NULL_GL_RESOURCES) { + glCanvas.addEventListener('webglcontextlost', this.contextLossHandler, false); + glCanvas.addEventListener('webglcontextrestored', this.contextRestoreHandler, false); + + this.initializeGL(this.glContext); + // testContextLoss(this.glContext); + } + }; + + private contextLossHandler = (event: { preventDefault: () => void }) => { + // we could log it for telemetry etc todo add the option for a callback + event.preventDefault(); // this is needed for the context restoration callback to happen + }; + + private contextRestoreHandler = () => { + // browser trivia: the duplicate calling of ensureContextAndInitialRender and changing/resetting the width are needed for Chrome and Safari to properly restore the context upon loss + // we could log context loss/regain for telemetry etc todo add the option for a callback + const glCanvas = this.glCanvasRef.current; + if (!glCanvas || !this.glContext) return; + this.restoreGL(this.glContext); + const widthCss = glCanvas.style.width; + const widthNum = parseFloat(widthCss); + glCanvas.style.width = `${widthNum + 0.1}px`; + window.setTimeout(() => { + glCanvas.style.width = widthCss; + if (this.glContext) this.restoreGL(this.glContext); + }, 0); + }; +} + +const mapStateToProps = (state: GlobalChartState): StateProps => { + const Spec = getSpecsFromStore(state.specs, ChartType.PlotMatrix, SpecType.Series)[0]; + const settingsSpec = getSettingsSpecSelector(state); + return { + theme: getChartThemeSelector(state).plotMatrix, + plotMatrixViewModel: Spec?.columnarData ?? nullPlotMatrixViewModel, + chartDimensions: state.parentDimensions, + a11ySettings: getA11ySettingsSelector(state), + + // mandatory charts API protocol; todo extract these mappings as now there are multiple charts like this + onElementOver: settingsSpec.onElementOver ?? (() => {}), + onElementClick: settingsSpec.onElementClick ?? (() => {}), + onElementOut: settingsSpec.onElementOut ?? (() => {}), + onRenderChange: settingsSpec.onRenderChange ?? (() => {}), // todo eventually also update data props on a local .echChartStatus element: data-ech-render-complete={rendered} data-ech-render-count={renderedCount} data-ech-debug-state={debugStateString} + + forwardStageRef2: React.createRef(), + }; +}; + +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => bindActionCreators({ onChartRendered }, dispatch); + +const PlotMatrixLayers = connect(mapStateToProps, mapDispatchToProps)(PlotMatrixComponent); + +/** @internal */ +export const PlotMatrix = (containerRef: BackwardRef, forwardStageRef: RefObject) => ( + +); diff --git a/packages/charts/src/chart_types/plot_matrix/plot_matrix_api.ts b/packages/charts/src/chart_types/plot_matrix/plot_matrix_api.ts new file mode 100644 index 0000000000..1ede1c1158 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/plot_matrix_api.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ChartType } from '..'; +import { Spec } from '../../specs'; +import { SpecType } from '../../specs/constants'; // kept as long-winded import on separate line otherwise import circularity emerges +import { buildSFProps, SFProps, useSpecFactory } from '../../state/spec_factory'; +import { stripUndefined } from '../../utils/common'; + +/** + * Column oriented data input for N data points: + * - label: array of N strings + * - value: Float64Array of N numbers, for tooltip value display + * - color: Float32Array of 4 * N numbers, eg. green[i] = color[4 * i + 1] + * - position0: Tween from: Float32Array of 2 * N numbers with unit coordinates [x0, y0, x1, y1, ..., xN-1, yN-1] + * - position1: Tween to: Float32Array of 2 * N numbers with unit coordinates [x0, y0, x1, y1, ..., xN-1, yN-1] + * - size0: Tween from: Float32Array of N numbers with unit widths [width0, width1, ... , widthN-1] + * - size1: Tween to: Float32Array of N numbers with unit widths [width0, width1, ... , widthN-1] + * If position0 === position1 and size0 === size1, then the nodes are not animated + * @public + */ +export interface PlotMatrixViewModel { + label: string[]; + value: Float64Array; + color: Float32Array; + position0: Float32Array; + position1: Float32Array; + size0: Float32Array; + size1: Float32Array; +} + +/** + * Specifies the plot matrix + * @public + */ +export interface PlotMatrixSpec extends Spec { + specType: typeof SpecType.Series; + chartType: typeof ChartType.PlotMatrix; + columnarData: PlotMatrixViewModel; +} + +const buildProps = buildSFProps()( + { + chartType: ChartType.PlotMatrix, + specType: SpecType.Series, + }, + {}, +); + +/** + * Adds plot matrix spec to chart specs + * @public + */ +export const PlotMatrix = function ( + props: SFProps< + PlotMatrixSpec, + keyof typeof buildProps['overrides'], + keyof typeof buildProps['defaults'], + keyof typeof buildProps['optionals'], + keyof typeof buildProps['requires'] + >, +) { + const { defaults, overrides } = buildProps; + useSpecFactory({ ...defaults, ...stripUndefined(props), ...overrides }); + return null; +}; diff --git a/packages/charts/src/chart_types/plot_matrix/render/ensure_webgl.ts b/packages/charts/src/chart_types/plot_matrix/render/ensure_webgl.ts new file mode 100644 index 0000000000..9345ee239f --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/render/ensure_webgl.ts @@ -0,0 +1,226 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + bindVertexArray, + createCompiledShader, + createLinkedProgram, + getAttributes, + getRenderer, + resetState, + Texture, +} from '../../../common/kingly'; +import { GL } from '../../../common/webgl_constants'; +import { c } from '../plom/src/config'; +import { getGLExtensions } from '../plom/src/utils'; +import { GLResources, NULL_GL_RESOURCES } from '../types'; +import { + attributeLocations, + fragAreaChart, + fragBinGrid, + fragBinRange, + fragBorder, + fragContours, + fragHeatmap, + fragHexHeatmap, + fragNormalSplat, + fragPlom, + fragPlomPick, + vertBorder, + vertHeatmap, + vertPlom, +} from './shaders'; + +/** @internal */ +export function ensureWebgl( + gl: WebGL2RenderingContext, + pickTexture: Texture, + binningRaster1d: Texture, + binningRaster2d: Texture, + binRanges1d: Texture, + binRanges2d: Texture, +): GLResources { + resetState(gl); + getGLExtensions(gl); + + /** + * Vertex array attributes + */ + + const vao = gl.createVertexArray(); + if (!vao) return NULL_GL_RESOURCES; + + bindVertexArray(gl, vao); + + /** + * Programs + */ + + const fragBinning = c.kernelDensity ? fragNormalSplat : fragBinGrid; + const vertPlomShader = createCompiledShader(gl, GL.VERTEX_SHADER, vertPlom); + const fragPlomShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragPlom); + const fragPlomPickShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragPlomPick); + const fragBinningShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragBinning); + const vertHeatmapShader = createCompiledShader(gl, GL.VERTEX_SHADER, vertHeatmap); + const fragBinRangeShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragBinRange); + const fragAreaChartShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragAreaChart); + const fragHeatmapShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragHeatmap); + const fragHexHeatmapShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragHexHeatmap); + const fragContoursShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragContours); + const vertBorderShader = createCompiledShader(gl, GL.VERTEX_SHADER, vertBorder); + const fragBorderShader = createCompiledShader(gl, GL.FRAGMENT_SHADER, fragBorder); + + // render scatterplot and parallel coordinates + const programScatterParcoords = createLinkedProgram(gl, vertPlomShader, fragPlomShader, attributeLocations); + + // render pick layer for scatterplot and parallel coordinates + const programPickScatterParcoords = createLinkedProgram(gl, vertPlomShader, fragPlomPickShader, attributeLocations); + + // bin points or density kernel circles into an 1d/2d raster + const programBinning = createLinkedProgram(gl, vertPlomShader, fragBinningShader, attributeLocations); + + // compute the min/max values, as well as the offset and scale min and 1/(max - min) from a binned raster + const programBinRange = createLinkedProgram(gl, vertHeatmapShader, fragBinRangeShader, attributeLocations); + + // render an area chart with the density estimates + const programAreaChart = createLinkedProgram(gl, vertHeatmapShader, fragAreaChartShader, attributeLocations); + + // render a 2d continuous heatmap in color + const programHeatmapChart = createLinkedProgram(gl, vertHeatmapShader, fragHeatmapShader, attributeLocations); + const programHexHeatmapChart = createLinkedProgram(gl, vertHeatmapShader, fragHexHeatmapShader, attributeLocations); + + // render a 2d contour layer + const programContoursChart = createLinkedProgram(gl, vertHeatmapShader, fragContoursShader, attributeLocations); + + // render panel borders + const programPanelBorder = createLinkedProgram(gl, vertBorderShader, fragBorderShader, attributeLocations); + + /** + * Resource allocation: Render layer setup + * Each layer is a tuple of a things like layer depth, GL renderer, geometry and offset/count for using the desired slice of the attrib arrays + * todo: the flame graph could also switch to this layer array approach for uniformity, though that's a much simpler use case + */ + + const renderLayers = { + parcoords: { + zIndex: 0, // lowest value: farthest from viewer, 1st to execute + targetTexture: null, + useWith: getRenderer(gl, programScatterParcoords, vao, { depthTest: true, frontFace: GL.CW }), + program: programScatterParcoords, + geom: GL.LINES, + offset: 0, + count: Infinity, + }, + scatterplot: { + zIndex: 0, // lowest value: farthest from viewer, 1st to execute + targetTexture: null, + useWith: getRenderer(gl, programScatterParcoords, vao, { depthTest: true, frontFace: GL.CW }), + geom: GL.POINTS, + offset: 0, + count: Infinity, + }, + parcoordsPick: { + zIndex: 1, // lowest value: farthest from viewer, 1st to execute + targetTexture: pickTexture.target(), + useWith: getRenderer(gl, programPickScatterParcoords, vao, { + depthTest: false, + frontFace: GL.CW, + }), + geom: GL.LINES, + offset: 0, + count: Infinity, + }, + scatterPick: { + zIndex: 1, // lowest value: farthest from viewer, 1st to execute + targetTexture: pickTexture.target(), + useWith: getRenderer(gl, programPickScatterParcoords, vao, { + depthTest: false, + frontFace: GL.CW, + }), + geom: GL.POINTS, + offset: 0, + count: Infinity, + }, + binningRaster2d: { + zIndex: 2, + targetTexture: binningRaster2d.target(), + useWith: getRenderer(gl, programBinning, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.POINTS, + offset: 0, + count: Infinity, + }, + binRanges2d: { + zIndex: 3, + targetTexture: binRanges2d.target(), + useWith: getRenderer(gl, programBinRange, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.TRIANGLE_STRIP, + offset: Infinity, + count: 4, + }, + heatmapChart: { + zIndex: 4, + targetTexture: null, + useWith: getRenderer(gl, programHeatmapChart, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.TRIANGLE_STRIP, + offset: Infinity, + count: 4, + }, + hexHeatmapChart: { + zIndex: 5, + targetTexture: null, + useWith: getRenderer(gl, programHexHeatmapChart, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.TRIANGLE_STRIP, + offset: Infinity, + count: 4, + }, + contours2d: { + zIndex: 6, + targetTexture: null, + useWith: getRenderer(gl, programContoursChart, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.TRIANGLE_STRIP, + offset: Infinity, + count: 4, + }, + binningRaster1d: { + zIndex: 7, + targetTexture: binningRaster1d.target(), + useWith: getRenderer(gl, programBinning, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.POINTS, + offset: 0, + count: Infinity, + }, + binRanges1d: { + zIndex: 8, + targetTexture: binRanges1d.target(), + useWith: getRenderer(gl, programBinRange, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.TRIANGLE_STRIP, + offset: Infinity, + count: 4, + }, + areaChart: { + zIndex: 9, + targetTexture: null, + useWith: getRenderer(gl, programAreaChart, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.TRIANGLE_STRIP, + offset: Infinity, + count: 4, + }, + panelBorder: { + zIndex: 10, + targetTexture: null, + useWith: getRenderer(gl, programPanelBorder, vao, { depthTest: false, frontFace: GL.CW }), + geom: GL.LINES, + offset: Infinity, + count: 8, + }, + }; + + const attributes = getAttributes(gl, programScatterParcoords, attributeLocations); + + return { attributes, renderLayers }; +} diff --git a/packages/charts/src/chart_types/plot_matrix/render/shaders.ts b/packages/charts/src/chart_types/plot_matrix/render/shaders.ts new file mode 100644 index 0000000000..2c6b8ce533 --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/render/shaders.ts @@ -0,0 +1,488 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** @internal */ +export const attributeLocations = { + v0: 0, + v1: 1, + v2: 2, + v3: 3, + v4: 4, + v5: 5, + v6: 6, + v7: 7, + v8: 8, + v9: 9, + va: 10, + vb: 11, + vc: 12, + vd: 13, + ve: 14, + vf: 15, +}; + +const vertTop = /* language=GLSL */ `#version 300 es + #pragma STDGL invariant(all) + precision highp int; + precision highp float; +`; + +const fragTop = /* language=GLSL */ `#version 300 es + precision highp int; + precision highp float; +`; + +/** + * Vertex shader for crossfiltering data points and projecting it to a Cartesian plane + * @internal + */ + +export const vertPlom = /* language=GLSL */ `${vertTop} + +uniform float scatter; // 0.0: lines; needs gl.LINES; 1.0: scatter; needs gl.POINTS + +uniform vec2 resolution; +uniform vec2 viewBoxPosition; +uniform vec2 viewBoxSize; + +uniform mat4 loA, hiA, loB, hiB, loC, hiC, loD, hiD; +uniform mat4 dim1A, dim2A, dim1B, dim2B, dim1C, dim2C, dim1D, dim2D; +uniform vec2 zoomScale, zoomOffset; +uniform float contextPointRadius; + +uniform sampler2D palette; +uniform vec3 hoverIdentifier; + +uniform float pointSize; + +// 16 * 4 == 64 dimensions +layout(location=${attributeLocations.v0}) in vec4 v0; +layout(location=${attributeLocations.v1}) in vec4 v1; +layout(location=${attributeLocations.v2}) in vec4 v2; +layout(location=${attributeLocations.v3}) in vec4 v3; +layout(location=${attributeLocations.v4}) in vec4 v4; +layout(location=${attributeLocations.v5}) in vec4 v5; +layout(location=${attributeLocations.v6}) in vec4 v6; +layout(location=${attributeLocations.v7}) in vec4 v7; +layout(location=${attributeLocations.v8}) in vec4 v8; +layout(location=${attributeLocations.v9}) in vec4 v9; +layout(location=${attributeLocations.va}) in vec4 va; +layout(location=${attributeLocations.vb}) in vec4 vb; +layout(location=${attributeLocations.vc}) in vec4 vc; +layout(location=${attributeLocations.vd}) in vec4 vd; +layout(location=${attributeLocations.ve}) in vec4 ve; +layout(location=${attributeLocations.vf}) in vec4 vf; + +out vec4 fragColor; +flat out vec4 pickColor; +flat out float side; // 0: left, 1: right +flat out float filteredPointSize; + +const vec4 UNIT = vec4(1, 1, 1, 1); +const float OUTSIDE_FRUSTUM_Z = 2.0; // setting the frustum properly will ensure that this will be outside + +float mulSum(mat4 p, mat4 v) { + return dot(matrixCompMult(p, v) * UNIT, UNIT); // elementwise multiply the two matrices, then sum all values +} + +mat4 matrixClamp(mat4 m, mat4 lo, mat4 hi) { + return mat4( + clamp(m[0], lo[0], hi[0]), + clamp(m[1], lo[1], hi[1]), + clamp(m[2], lo[2], hi[2]), + clamp(m[3], lo[3], hi[3]) + ); +} + +bool matrixShow(mat4 v, mat4 lo, mat4 hi) { + return matrixClamp(v, lo, hi) == v; +} + +float axisY(float x, mat4 v[4]) { + float y1 = mulSum(v[0], dim1A) + mulSum(v[1], dim1B) + mulSum(v[2], dim1C) + mulSum(v[3], dim1D); + float y2 = mulSum(v[0], dim2A) + mulSum(v[1], dim2B) + mulSum(v[2], dim2C) + mulSum(v[3], dim2D); + return y1 * (1.0 - x) + y2 * x; +} + +bool insideLoHi(mat4 v[4]) { + return matrixShow(v[0], loA, hiA) + && matrixShow(v[1], loB, hiB) + && matrixShow(v[2], loC, hiC) + && matrixShow(v[3], loD, hiD); +} + +void main() { + vec4 abs_vf = abs(vf); + float proximity = abs_vf[3]; // the higher the value, the more proximal (closer) to the user, in Z order + mat4 values[4] = mat4[4](mat4(v0, v1, v2, v3), mat4(v4, v5, v6, v7), mat4(v8, v9, va, vb), mat4(vc, vd, ve, abs_vf)); + float leftOrRightVertex = sign(vf[3]); + float x = scatter * axisY(1.0, values) + (1.0 - scatter) * (0.5 * leftOrRightVertex + 0.5); + float y = axisY((1.0 - scatter) * x, values); + vec2 baseBase = viewBoxSize * vec2(x, y); + vec2 baseViewBoxXY = (baseBase - zoomOffset) * zoomScale + zoomOffset; + vec2 viewBoxXY = viewBoxPosition + baseViewBoxXY; + bool show = insideLoHi(values); + + vec3 hoverMatchDiff = abs(hoverIdentifier - abs_vf.rgb); + bool isHighlight = hoverMatchDiff == clamp(hoverMatchDiff, vec3(-1./256.), vec3(1./256.)); // diff less than 1/255th + + float depth = isHighlight ? 0.0 : 1.0 - proximity; + gl_Position = vec4(2.0 * viewBoxXY / resolution - 1.0, show ? depth - 1.: depth, 1.); + + pickColor = vec4(abs_vf.rgb, 1.0); + + // color: ternary hell, fixme + fragColor = isHighlight + ? vec4(1, 0, 1, 1) + : show + ? texelFetch(palette, ivec2(proximity * 255.0, 0), 0) + : contextPointRadius == 0. + ? vec4(0.) + : vec4(119., 119., 119., scatter == 1. ? 1. : .1); + + // for when gl.drawArrays is called with gl.POINTS + filteredPointSize = show ? pointSize : contextPointRadius; // parcoords discards too on zero "point" size + gl_PointSize = isHighlight ? 3.0 * filteredPointSize : filteredPointSize; + side = round(scatter) * leftOrRightVertex; +}`; + +/** + * Fragment shader for splatting a point as scatter points + * @internal + */ + +export const fragPlom = /* language=GLSL */ `${fragTop} + +uniform float pointSize; +in vec4 fragColor; +flat in float side; +flat in float filteredPointSize; +out vec4 theFragColor; +// Only one of the 4 channels (rgba) is used; for count. The others may gain sum, sum of suqares etc. + +void main() { + if(side > 0.0 || filteredPointSize == 0.0) discard; + + if(side < 0.0) { + float R = 1.0; + vec2 cxy = 2.0 * gl_PointCoord - 1.0; + float r = dot(cxy, cxy); + float delta = fwidth(r); + float alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r); + if (r > R) discard; + theFragColor = vec4(fragColor.rgb, alpha * fragColor.a); + } else { + theFragColor = fragColor; + } +}`; + +/** @internal */ +export const fragPlomPick = /* language=GLSL */ `${fragTop} + +out vec4 theFragColor; +uniform float pointSize; +flat in float side; +flat in float filteredPointSize; +flat in vec4 pickColor; +const float R = 1.0; + +void main() { + if(side > 0.0 || filteredPointSize == 0.0) discard; + + if(side < 0.0) { + vec2 cxy = 2.0 * gl_PointCoord - 1.0; + float r = dot(cxy, cxy); + if (r > R) discard; + } + theFragColor = pickColor; +}`; + +/** @internal */ +export const fragBinGrid = /* language=GLSL */ `${fragTop} + +out vec4 theFragColor; +void main() { + theFragColor = vec4(0, 0, 0, 1); +}`; + +/** @internal */ +export const fragNormalSplat = /* language=GLSL */ `${fragTop} + +uniform float kernelExponent; +in vec4 fragColor; +flat in float side; +out vec4 theFragColor; +// Only one of the 4 channels (rgba) is used; for count. The others may gain sum, sum of suqares etc. + +void main() { + if(side >= 0.0) discard; + vec2 cxy = 2.0 * gl_PointCoord - 1.0; + float r = dot(cxy, cxy); + if (r > 1.0) discard; + float delta = fwidth(r); + float alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r); + theFragColor = vec4(fragColor.rgb, alpha * fragColor.a * pow(1.0 - r, kernelExponent)); +}`; + +/** + * Fragment shader: determine the value domain on a binCount.xy grid area + * @internal + */ + +export const fragBinRange = /* language=GLSL */ `${fragTop} + +uniform highp ivec2 binCount; // eg. [32, 16] means a raster of 32 columns, 16 rows +uniform sampler2D binningRaster; +flat in ivec2 textureOffset; +out vec4 theFragColor; + +void main() { + float lo = 0.0; + float hi = 0.0; + for(int i = 0; i < binCount.x; i++) { + for(int j = 0; j < binCount.y; j++) { + ivec2 binCoord = ivec2(i, j); + float val = texelFetch(binningRaster, textureOffset + binCoord, 0).a; + lo = min(lo, val); + hi = max(hi, val); + } + } + + theFragColor = vec4(lo, 1.0 / (hi - lo), hi, 1); +}`; + +/** @internal */ +export const vertHeatmap = /* language=GLSL */ `${vertTop} + +uniform highp ivec2 binCount; // eg. [32, 16] means a raster of 32 columns, 16 rows + +uniform vec2 resolution; +uniform vec2 viewBoxPosition; +uniform vec2 viewBoxSize; + +layout(location=${attributeLocations.v0}) in vec4 v0; +out vec2 xy; +out vec2 viewBoxXY; +flat out ivec2 textureOffset; + +void main() { + xy = v0.xy; + + viewBoxXY = viewBoxPosition + viewBoxSize * xy; + gl_Position = vec4(2.0 * viewBoxXY / resolution - 1.0, 0, 1); + + textureOffset = ivec2(viewBoxPosition / (viewBoxSize + vec2(0, 0))) * binCount; +}`; + +/** @internal */ +export const fragHeatmap = /* language=GLSL */ `${fragTop} + +uniform highp ivec2 binCount; // eg. [32, 16] means a raster of 32 columns, 16 rows +uniform vec2 viewBoxSize; +uniform sampler2D binningRaster; +uniform sampler2D binRanges; +uniform sampler2D heatmapPalette; + +in vec2 xy; +flat in ivec2 textureOffset; + +out vec4 theFragColor; + +const float colorFudgeForWhite = 0.0; // -0.02; + +void main() { + vec2 cellCount = vec2(binCount); + vec2 maxCellIndex = cellCount - 1.0; + vec3 binColorScale = texelFetch(binRanges, ivec2(xy), 0).rgb; + vec2 binLookupWithinPanel = vec2(min(xy * cellCount, maxCellIndex)); + vec2 binLookup = vec2(textureOffset) + binLookupWithinPanel; + float unitValue = texture(binningRaster, binLookup / vec2(textureSize(binningRaster, 0))).a * binColorScale.g; + bool zeroDomain = binColorScale.r == binColorScale.b; + float tamperedUnitValue = zeroDomain ? colorFudgeForWhite : sqrt(unitValue) + colorFudgeForWhite; + // subtraction to make yellow starting color white + theFragColor = texelFetch(heatmapPalette, ivec2(255.0 - min(tamperedUnitValue, 1.0) * 255.0, 0), 0); +}`; + +/** @internal */ +export const fragHexHeatmap = /* language=GLSL */ `${fragTop} + + uniform highp ivec2 binCount; // eg. [32, 16] means a raster of 32 columns, 16 rows + uniform vec2 resolution; + uniform vec2 viewBoxSize; + uniform vec2 viewBoxPosition; + uniform sampler2D binningRaster; + uniform sampler2D binRanges; + uniform sampler2D heatmapPalette; + + in vec2 xy; + in vec2 viewBoxXY; + flat in ivec2 textureOffset; + + out vec4 theFragColor; + + const float colorFudgeForWhite = -0.02; + const vec2 normalizedHexVector = normalize(vec2(1, 1.732)); // 1.732 is the square root of 3 + const float hexWidthFudge = 1.1; // otherwise the hexagon is too wide; todo look into it + + void main() { + float alpha = 1.0; + vec2 cellCount = vec2(binCount); + vec2 maxCellIndex = cellCount - 1.0; + vec3 binColorScale = texelFetch(binRanges, ivec2(xy), 0).rgb; + vec2 cellXY = xy * cellCount; + bool hexOffsetRow = int(cellXY.y) % 2 == 1; + vec2 hexXY = hexOffsetRow ? xy : vec2(xy.x + 0.5 / cellCount.x, xy.y); + + vec2 binLookupWithinPanel = vec2(min(hexXY * cellCount, maxCellIndex)); + vec2 binLookup = vec2(textureOffset) + binLookupWithinPanel; + + vec2 cellSize = viewBoxSize / cellCount; + + vec2 hexOffset = hexOffsetRow ? vec2(0, 0) : vec2(0.5 * cellSize.x, 0); + vec2 unitDistanceFromCorner0 = fract((viewBoxXY - viewBoxPosition - hexOffset) / cellSize); + vec2 cxy0 = 2.0 * unitDistanceFromCorner0 - 1.0; + + // per cell hexagon: + vec2 uv = abs(cxy0); + float d = max(dot(uv, normalizedHexVector), hexWidthFudge * uv.x); + float delta = fwidth(d); // not sure if d is rock solid here but looks good enough + alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, d); + + // ... or circle: + // float r0 = dot(cxy0, cxy0); + // if (r0 > 1.) discard; + + // per panel circle + vec2 unitDistanceFromCorner1 = xy; + vec2 cxy1 = 2.0 * unitDistanceFromCorner1 - vec2(1); + float r1 = dot(cxy1, cxy1); + //if (r1 > 1.0) discard; + + // per entire SPLOM circle + vec2 unitDistanceFromCorner2 = viewBoxXY / resolution; + vec2 cxy2 = 2.0 * unitDistanceFromCorner2 - vec2(1); + float r2 = dot(cxy2, cxy2); + //if (r2 > 1.0) discard; + + float unitValue = texture(binningRaster, binLookup / vec2(textureSize(binningRaster, 0))).a * binColorScale.g; + bool zeroDomain = binColorScale.r == binColorScale.b; + float tamperedUnitValue = zeroDomain ? colorFudgeForWhite : sqrt(unitValue) + colorFudgeForWhite; + // subtraction to make yellow starting color white + theFragColor = texelFetch( + heatmapPalette, + ivec2(255.0 - min(tamperedUnitValue, 1.0) * 255.0, 0), 0 + ) * vec4(1, 1, 1, alpha); + }`; + +/** @internal */ +export const fragAreaChart = /* language=GLSL */ `${fragTop} + +uniform highp ivec2 binCount; // eg. [32, 16] means a raster of 32 columns, 16 rows +uniform vec2 viewBoxSize; +uniform sampler2D binningRaster; +uniform sampler2D binRanges; + +in vec2 xy; +flat in ivec2 textureOffset; + +out vec4 theFragColor; + +const float colorFudgeForWhite = 0.0; // -0.02; + +void main() { + vec2 cellCount = vec2(binCount); + vec2 maxCellIndex = cellCount - 1.0; + vec3 binColorScale = texelFetch(binRanges, ivec2(xy), 0).rgb; + vec2 binLookupWithinPanel = vec2(min(xy * cellCount, maxCellIndex)); + vec2 binLookup = vec2(textureOffset) + binLookupWithinPanel; + float unitValue = texture(binningRaster, binLookup / vec2(textureSize(binningRaster, 0))).a * binColorScale.g; + + float ratio = 0.9; // from \`const s = yIndex === 0 ? 1 : 0.9\` - todo unify eg. via a uniform + float top = 300.0; // this is the panel height in px - todo put it in a uniform + float bottom = 93.0; // todo check where it's coming from + float span = top - bottom; + float valueRatio = unitValue * .8 * .5; + // todo the unitValue isn't exactly 0..1, there's clipping, + // solve with \`texelFetch(binRanges, ivec2(0, 0), 0).rgb\`; also, don't use the top 10%, to make place for text + float limit = bottom + valueRatio * span; + + float inPanelHeight = gl_FragCoord.y - float(textureOffset.y) / ratio * viewBoxSize.y; + bool predicate = inPanelHeight >= limit; + + if (predicate) discard; + + float solidPartOfArea = 0.9; // todo consider \`fwidth\` derivative + float alpha = 1.0 - smoothstep( + 0.0, + 1.0, + clamp((inPanelHeight - solidPartOfArea * limit) / (limit - solidPartOfArea * limit), 0.0, 1.0) + ); + + theFragColor = vec4(1, 0, 1, 0.5 * alpha); +}`; + +/** @internal */ +export const fragContours = /* language=GLSL */ `${fragTop} + +uniform highp ivec2 binCount; // eg. [32, 16] means a raster of 32 columns, 16 rows +uniform sampler2D binningRaster; +uniform sampler2D binRanges; + +in vec2 xy; +flat in ivec2 textureOffset; + +out vec4 theFragColor; + +const float modCycle = 2.0; + +void main() { + vec2 cellCount = vec2(binCount); + vec2 maxCellIndex = cellCount - 1.0; + vec3 binColorScale = texelFetch(binRanges, ivec2(xy), 0).rgb; + ivec2 binLookupWithinPanel = ivec2(min(floor(xy * cellCount), maxCellIndex)); + ivec2 binLookup = textureOffset + binLookupWithinPanel; + float unitValue = texture( + binningRaster, + (vec2(textureOffset) + min(xy * cellCount, maxCellIndex)) / vec2(textureSize(binningRaster, 0)) + ).a * binColorScale.g; + + float logUnitValue = log(0.001 + unitValue); // subtraction to make yellow starting color white + + float z = logUnitValue; + float d = fract(z); + if(mod(z, modCycle) > 1.0) d = 1.0 - d; + float val = 1.0 - 0.7 * d / fwidth(z); + theFragColor = vec4(val); +}`; + +/** + * Rectangle (border around the panels) + * @internal + */ +export const vertBorder = /* language=GLSL */ `${vertTop} + +uniform vec2 resolution; +uniform vec2 viewBoxPosition; +uniform vec2 viewBoxSize; + +const vec2 fudge = vec2(0, 0.5); // otherwise bottom border line is thinner :shrug: + +layout(location=${attributeLocations.v0}) in vec4 v0; + +void main() { + vec2 viewBoxXY = viewBoxPosition + viewBoxSize * v0.xy; + gl_Position = vec4(2.0 * (viewBoxXY + fudge) / resolution - 1.0, -0.1, 1); +}`; + +/** @internal */ +export const fragBorder = /* language=GLSL */ `${fragTop} + +out vec4 theFragColor; +void main() { + theFragColor = vec4(0.5, 0.5, 0.5, 1); +}`; diff --git a/packages/charts/src/chart_types/plot_matrix/types.ts b/packages/charts/src/chart_types/plot_matrix/types.ts new file mode 100644 index 0000000000..373eaa140f --- /dev/null +++ b/packages/charts/src/chart_types/plot_matrix/types.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Attributes, Render } from '../../common/kingly'; +import { GL } from '../../common/webgl_constants'; + +/** @internal */ +export interface PlotMatrixRenderLayer { + zIndex: number; + targetTexture: WebGLFramebuffer | null; + useWith: Render; + program?: WebGLProgram; + geom: number; // 0 | 1 | 2 | 3 | 4 | 5 | 6 ie. GL.POINTS | GL.LINES | GL.LINE_STRIP | GL.LINE_LOOP | GL.TRIANGLES | GL.TRIANGLE_STRIP | GL.TRIANGLE_FAN; + offset: number; + count: number; +} + +/** @internal */ +export interface PlotMatrixRenderLayers { + parcoords: PlotMatrixRenderLayer; + scatterplot: PlotMatrixRenderLayer; + parcoordsPick: PlotMatrixRenderLayer; + scatterPick: PlotMatrixRenderLayer; + binningRaster2d: PlotMatrixRenderLayer; + binRanges2d: PlotMatrixRenderLayer; + heatmapChart: PlotMatrixRenderLayer; + hexHeatmapChart: PlotMatrixRenderLayer; + contours2d: PlotMatrixRenderLayer; + binningRaster1d: PlotMatrixRenderLayer; + binRanges1d: PlotMatrixRenderLayer; + areaChart: PlotMatrixRenderLayer; + panelBorder: PlotMatrixRenderLayer; +} + +/** @internal */ +export interface GLResources { + renderLayers: PlotMatrixRenderLayers; + attributes: Attributes; +} + +const NULL_LAYER: PlotMatrixRenderLayer = { + zIndex: 0, + targetTexture: null, + useWith: () => {}, + geom: GL.POINTS, + offset: 0, + count: 0, +}; + +/** @internal */ +export const NULL_GL_RESOURCES: GLResources = { + renderLayers: { + parcoords: NULL_LAYER, + scatterplot: NULL_LAYER, + parcoordsPick: NULL_LAYER, + scatterPick: NULL_LAYER, + binningRaster2d: NULL_LAYER, + binRanges2d: NULL_LAYER, + heatmapChart: NULL_LAYER, + hexHeatmapChart: NULL_LAYER, + contours2d: NULL_LAYER, + binningRaster1d: NULL_LAYER, + binRanges1d: NULL_LAYER, + areaChart: NULL_LAYER, + panelBorder: NULL_LAYER, + }, + attributes: new Map(), +}; + +/** @internal */ +export const nullPlotMatrixViewModel = { + label: [], + value: new Float64Array(), + color: new Float32Array(), + position0: new Float32Array(), + position1: new Float32Array(), + size0: new Float32Array(), + size1: new Float32Array(), +}; diff --git a/packages/charts/src/common/kingly.ts b/packages/charts/src/common/kingly.ts index 536f7604b8..94dd6ef4da 100644 --- a/packages/charts/src/common/kingly.ts +++ b/packages/charts/src/common/kingly.ts @@ -442,7 +442,8 @@ export const createTexture = ( if (GL_DEBUG) { const framebufferStatus = gl.checkFramebufferStatus(GL.DRAW_FRAMEBUFFER); if (framebufferStatus !== GL.FRAMEBUFFER_COMPLETE) { - Logger.warn(`kinGLy exception: target framebuffer is not complete`); + debugger; + Logger.warn(`kinGLy exception: target framebuffer is not complete: ${framebufferStatus}`); } } } @@ -506,7 +507,10 @@ export const getAttributes = ( const offset = 0; // start at the beginning of the buffer const activeAttribInfo = gl.getActiveAttrib(program, index); - if (!activeAttribInfo) throw new Error(`kinGLy exception: active attribute info could not be read`); // just appeasing the TS linter + if (!activeAttribInfo) { + debugger; + throw new Error(`kinGLy exception: active attribute info could not be read`); + } const { name, type } = activeAttribInfo; if (name.startsWith('gl_')) return [name, () => {}]; // only populate expressly supplied attributes, NOT gl_VertexID or gl_InstanceID diff --git a/packages/charts/src/index.ts b/packages/charts/src/index.ts index bb94735a4f..84de19ef25 100644 --- a/packages/charts/src/index.ts +++ b/packages/charts/src/index.ts @@ -137,5 +137,6 @@ export { GroupKeysOrKeyFn, GroupByKeyFn } from './chart_types/xy_chart/utils/gro export { computeRatioByGroups } from './utils/data/data_processing'; export { TimeFunction } from './utils/time_functions'; export * from './chart_types/flame_chart/flame_api'; +export * from './chart_types/plot_matrix/plot_matrix_api'; export * from './chart_types/timeslip/timeslip_api'; export { LegacyAnimationConfig } from './common/animation'; diff --git a/packages/charts/src/state/chart_state.ts b/packages/charts/src/state/chart_state.ts index 4ca095e566..20e4c95fd1 100644 --- a/packages/charts/src/state/chart_state.ts +++ b/packages/charts/src/state/chart_state.ts @@ -14,6 +14,7 @@ import { GoalState } from '../chart_types/goal_chart/state/chart_state'; import { HeatmapState } from '../chart_types/heatmap/state/chart_state'; import { MetricState } from '../chart_types/metric/state/chart_state'; import { PartitionState } from '../chart_types/partition_chart/state/chart_state'; +import { PlotMatrixState } from '../chart_types/plot_matrix/internal_chart_state'; import { TimeslipState } from '../chart_types/timeslip/internal_chart_state'; import { WordcloudState } from '../chart_types/wordcloud/state/chart_state'; import { XYAxisChartState } from '../chart_types/xy_chart/state/chart_state'; @@ -464,6 +465,7 @@ const constructors: Record InternalChartState | null> = { [ChartType.Goal]: () => new GoalState(), [ChartType.Partition]: () => new PartitionState(), [ChartType.Flame]: () => new FlameState(), + [ChartType.PlotMatrix]: () => new PlotMatrixState(), [ChartType.Timeslip]: () => new TimeslipState(), [ChartType.XYAxis]: () => new XYAxisChartState(), [ChartType.Heatmap]: () => new HeatmapState(), diff --git a/packages/charts/src/utils/themes/dark_theme.ts b/packages/charts/src/utils/themes/dark_theme.ts index d5e41941b8..0a5f7dca8a 100644 --- a/packages/charts/src/utils/themes/dark_theme.ts +++ b/packages/charts/src/utils/themes/dark_theme.ts @@ -422,4 +422,15 @@ export const DARK_THEME: Theme = { scrollbarThumb: 'rgb(223, 229, 239)', scrollbarTrack: 'rgb(52, 55, 65)', }, + plotMatrix: { + navigation: { + textColor: 'rgb(223, 229, 239)', + buttonTextColor: 'rgb(54, 162, 239)', + buttonDisabledTextColor: 'rgb(81, 87, 97)', + buttonBackgroundColor: '#36a2ef33', + buttonDisabledBackgroundColor: 'rgba(52, 55, 65, 0.15)', + }, + scrollbarThumb: 'rgb(223, 229, 239)', + scrollbarTrack: 'rgb(52, 55, 65)', + }, }; diff --git a/packages/charts/src/utils/themes/light_theme.ts b/packages/charts/src/utils/themes/light_theme.ts index bcd70c7f44..8b16aebcaa 100644 --- a/packages/charts/src/utils/themes/light_theme.ts +++ b/packages/charts/src/utils/themes/light_theme.ts @@ -421,4 +421,15 @@ export const LIGHT_THEME: Theme = { scrollbarThumb: 'rgb(52, 55, 65)', scrollbarTrack: 'rgb(211, 218, 230)', }, + plotMatrix: { + navigation: { + textColor: 'rgb(52, 55, 65)', + buttonTextColor: 'rgb(0, 97, 166)', + buttonDisabledTextColor: 'rgb(162, 171, 186)', + buttonBackgroundColor: 'rgb(204, 228, 245)', + buttonDisabledBackgroundColor: 'rgba(211, 218, 230, 0.15)', + }, + scrollbarThumb: 'rgb(52, 55, 65)', + scrollbarTrack: 'rgb(211, 218, 230)', + }, }; diff --git a/packages/charts/src/utils/themes/theme.ts b/packages/charts/src/utils/themes/theme.ts index 3f0b4a33b3..fae7d0a11e 100644 --- a/packages/charts/src/utils/themes/theme.ts +++ b/packages/charts/src/utils/themes/theme.ts @@ -341,6 +341,19 @@ export interface FlamegraphStyle { scrollbarThumb: Color; } +/** @alpha */ +export interface PlotMatrixStyle { + navigation: { + textColor: Color; + buttonTextColor: Color; + buttonDisabledTextColor: Color; + buttonBackgroundColor: Color; + buttonDisabledBackgroundColor: Color; + }; + scrollbarTrack: Color; + scrollbarThumb: Color; +} + /** @public */ export interface ScalesConfig { /** @@ -504,6 +517,9 @@ export interface Theme { /** @alpha */ flamegraph: FlamegraphStyle; + + /** @alpha */ + plotMatrix: PlotMatrixStyle; } /** @public */ diff --git a/playground/playground.tsx b/playground/playground.tsx index 3c027ae313..d3c46161d4 100644 --- a/playground/playground.tsx +++ b/playground/playground.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Example } from '../storybook/stories/icicle/04_cpu_profile_gl_flame.story'; +import { Example } from '../storybook/stories/plot_matrix/01_gl_plot_matrix.story'; export function Playground() { return ; diff --git a/storybook/stories/plot_matrix/01_gl_plot_matrix.story.tsx b/storybook/stories/plot_matrix/01_gl_plot_matrix.story.tsx new file mode 100644 index 0000000000..a9d9d29ed8 --- /dev/null +++ b/storybook/stories/plot_matrix/01_gl_plot_matrix.story.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +import { Chart, PlotMatrix, Settings, PartialTheme, PlotMatrixViewModel } from '@elastic/charts'; +import columnarMock from '@elastic/charts/src/mocks/hierarchical/cpu_profile_tree_mock_columnar.json'; +import { getRandomNumberGenerator } from '@elastic/charts/src/mocks/utils'; + +import { useBaseTheme } from '../../use_base_theme'; + +const position = new Float32Array(columnarMock.position); +const size = new Float32Array(columnarMock.size); + +const pseudoRandom = getRandomNumberGenerator('a_seed'); + +const paletteColorBrewerCat12 = [ + [141, 211, 199], + [255, 255, 179], + [190, 186, 218], + [251, 128, 114], + [128, 177, 211], + [253, 180, 98], + [179, 222, 105], + [252, 205, 229], + [217, 217, 217], + [188, 128, 189], + [204, 235, 197], + [255, 237, 111], +]; + +const columnarData: PlotMatrixViewModel = { + label: columnarMock.label.map((index: number) => columnarMock.dictionary[index]), // reversing the dictionary encoding + value: new Float64Array(columnarMock.value), + color: new Float32Array( + columnarMock.label.flatMap(() => [...paletteColorBrewerCat12[pseudoRandom(0, 11)].map((c) => c / 255), 1]), + ), + position0: position.map((p, i) => (i % 2 === 0 ? 1 - p - size[i / 2] : p)), //.map((p, i) => (i % 2 === 0 ? 1 - p - size[i / 2] : p)), // new Float32Array([...position].slice(1)), // try with the wrong array length + position1: position, + size0: size.map((s) => 0.8 * s), + size1: size, +}; + +export const Example = () => { + const theme: PartialTheme = { + chartMargins: { top: 0, left: 0, bottom: 0, right: 0 }, + chartPaddings: { left: 0, right: 0, top: 0, bottom: 0 }, + }; + return ( + + + + + ); +}; diff --git a/storybook/stories/plot_matrix/plot_matrix.stories.tsx b/storybook/stories/plot_matrix/plot_matrix.stories.tsx new file mode 100644 index 0000000000..3d95d27ad4 --- /dev/null +++ b/storybook/stories/plot_matrix/plot_matrix.stories.tsx @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export default { + title: 'Plot matrix (@alpha)', +}; + +export { Example as plotMatrix } from './01_gl_plot_matrix.story'; diff --git a/yarn.lock b/yarn.lock index 76c7a08be6..3c44b317f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9644,6 +9644,11 @@ d3-shape@^2.0.0: dependencies: d3-array "2" +d3@3.5.17: + version "3.5.17" + resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" + integrity sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg== + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"