From a7d9e616fc857781cf1fb37d73d52ee11da72b60 Mon Sep 17 00:00:00 2001 From: Robert Monfera Date: Thu, 2 Apr 2020 17:14:27 +0200 Subject: [PATCH] fix: svg sector shadow (#4) --- .../renderer/canvas/highlighter.tsx | 48 +++++---- stories/sunburst/15_single_sunburst.tsx | 97 +++++++++++++++++++ stories/sunburst/sunburst.stories.tsx | 1 + 3 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 stories/sunburst/15_single_sunburst.tsx diff --git a/src/chart_types/partition_chart/renderer/canvas/highlighter.tsx b/src/chart_types/partition_chart/renderer/canvas/highlighter.tsx index 29ae011038..950ad3c5c8 100644 --- a/src/chart_types/partition_chart/renderer/canvas/highlighter.tsx +++ b/src/chart_types/partition_chart/renderer/canvas/highlighter.tsx @@ -33,30 +33,12 @@ interface HighlighterProps { radius: number; } +const EPSILON = 1e-6; + function getShapeFromValues(x: number, y: number, r: number, a0: number, a1: number, ccw: number): string { - const dx = r * Math.cos(a0); - const dy = r * Math.sin(a0); - const x0 = x + dx; - const y0 = y + dy; const cw = 1 ^ ccw; - let da = ccw ? a0 - a1 : a1 - a0; - const path: string[] = []; - path.push(`M${x0},${y0}`); - - if (!r) return ''; - if (da < 0) { - da = (da % TAU) + TAU; - } - if (da > TAU - 1e-6) { - path.push(`A${r},${r},0,1,${cw},${x - dx},${y - dy}A${r},${r},0,1,${cw},${x0},${y0}`); - } - - // Is this arc non-empty? Draw an arc! - else if (da > 1e-6) { - path.push(`A${r},${r},0,${+(da >= Math.PI)},${cw},${x + r * Math.cos(a1)},${y + r * Math.sin(a1)}`); - } - path.push(`L${x},${y}Z`); - return path.join(''); + const da = ccw ? a0 - a1 : a1 - a0; + return `A${r},${r},0,${+(da >= Math.PI)},${cw},${x + r * Math.cos(a1)},${y + r * Math.sin(a1)}`; } class HighlighterComponent extends React.Component { @@ -73,13 +55,27 @@ class HighlighterComponent extends React.Component { */} {geometries.map(({ x0, x1, y0px, y1px }, index) => { + if ((Math.abs(x0 - x1) + TAU) % TAU < EPSILON) { + return ( + + ); + } const X0 = x0 - TAU / 4; const X1 = x1 - TAU / 4; const path = [ - getShapeFromValues(0, 0, y0px, X0, X0, 0), - getShapeFromValues(0, 0, y1px, X0, X1, 0), - getShapeFromValues(0, 0, y0px, X1, X0, 1), - ].join(''); + `M${y0px * Math.cos(X0)},${y0px * Math.sin(X0)}`, + getShapeFromValues(0, 0, y0px, X0, X1, 0), + `L${y1px * Math.cos(X1)},${y1px * Math.sin(X1)}`, + getShapeFromValues(0, 0, y1px, X1, X0, 1), + 'Z', + ].join(' '); return ; })} {/* diff --git a/stories/sunburst/15_single_sunburst.tsx b/stories/sunburst/15_single_sunburst.tsx new file mode 100644 index 0000000000..5c2fe94d49 --- /dev/null +++ b/stories/sunburst/15_single_sunburst.tsx @@ -0,0 +1,97 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ + +import { Chart, Datum, Partition, PartitionLayout, Settings } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types'; +import { + categoricalFillColor, + colorBrewerCategoricalStark9, + countryLookup, + productLookup, + regionLookup, +} from '../utils/utils'; + +export const example = () => ( + + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: any) => productLookup[d].name, + shape: { + fillColor: (d: ShapeTreeNode) => { + return categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex); + }, + }, + }, + { + groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), + nodeLabel: (d: any) => regionLookup[d].regionName, + shape: { + fillColor: (d: ShapeTreeNode) => { + return categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex); + }, + }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d].name, + shape: { + fillColor: (d: ShapeTreeNode) => { + return categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex); + }, + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { + maxCount: 0, + fontSize: 14, + }, + fontFamily: 'Arial', + fillLabel: { + valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + fontStyle: 'italic', + textInvertible: true, + fontWeight: 900, + valueFont: { + fontFamily: 'Menlo', + fontStyle: 'normal', + fontWeight: 100, + }, + }, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 1, + idealFontSizeJump: 1.1, + outerSizeRatio: 1, + emptySizeRatio: 0, + circlePadding: 4, + backgroundColor: 'rgba(229,229,229,1)', + }} + /> + +); diff --git a/stories/sunburst/sunburst.stories.tsx b/stories/sunburst/sunburst.stories.tsx index 8cc0282a0b..9c006c21ac 100644 --- a/stories/sunburst/sunburst.stories.tsx +++ b/stories/sunburst/sunburst.stories.tsx @@ -41,6 +41,7 @@ export { example as veryLargeAndSmall } from './12_very_small'; export { example as nearFullNearEmpty } from './13_empty'; export { example as fullAndZeroSlices } from './14_full_zero'; export { example as singleSlice } from './15_single'; +export { example as singleSunburst } from './15_single_sunburst'; export { example as singleSmallSice } from './16_single_small'; export { example as singleVerySmall } from './17_single_very_small'; export { example as noSlice } from './18_no_sliced';