Skip to content

Commit

Permalink
refactor(react-chart): move point coordinates calculations specifics …
Browse files Browse the repository at this point in the history
…down to the pointComponent (#1753)

BREAKING CHANGES:

Previously, `pointComponent` of `BarSeries` and `PieSeries` accepted precalculated fields (`width` and `d`, respectively). Now, `pointComponent` accepts fields that provide raw data for calculation. This makes `pointComponent` more flexible as the `width` and `d` fields can now be calculated the way you need.
 
The following substitutions took place:
 
- `width` => `barWidth` and `maxBarWidth`
- `d` => `innerRadius`, `outerRadius`, `maxRadius`, `startAngle`, and `endAngle`
  • Loading branch information
Krijovnick committed Jan 15, 2019
1 parent c5ca5f4 commit fde7756
Show file tree
Hide file tree
Showing 20 changed files with 278 additions and 197 deletions.
89 changes: 44 additions & 45 deletions packages/dx-chart-core/src/plugins/series/computeds.js
Expand Up @@ -15,8 +15,6 @@ const getX = ({ x }) => x;
const getY = ({ y }) => y;
const getY1 = ({ y1 }) => y1;

const DEFAULT_POINT_SIZE = 7;

export const dArea = area()
.x(getX)
.y1(getY)
Expand All @@ -32,29 +30,23 @@ export const dSpline = line()
.curve(curveMonotoneX);

export const getPiePointTransformer = ({
argumentScale, valueScale, points, innerRadius, outerRadius, palette,
argumentScale, valueScale, points, palette,
}) => {
const x = Math.max(...argumentScale.range()) / 2;
const y = Math.max(...valueScale.range()) / 2;
const radius = Math.min(x, y);
const maxRadius = Math.min(x, y);
const pieData = pie().sort(null).value(d => d.value)(points);
const inner = innerRadius * radius;
const outer = outerRadius * radius;
const gen = arc().innerRadius(inner).outerRadius(outer);
const colorScale = scaleOrdinal().range(palette);
return (point) => {
const { startAngle, endAngle } = pieData[point.index];
return {
...point,
// TODO: It should be calculated in *pointComponent*.
d: gen.startAngle(startAngle).endAngle(endAngle)(),
color: point.color || colorScale(point.index),
x,
y,
innerRadius: inner,
outerRadius: outer,
startAngle,
endAngle,
maxRadius,
};
};
};
Expand All @@ -68,6 +60,8 @@ export const getLinePointTransformer = ({ argumentScale, valueScale }) => {
});
};

export const getScatterPointTransformer = getLinePointTransformer;

export const getAreaPointTransformer = (series) => {
const transform = getLinePointTransformer(series);
const y1 = series.valueScale(0);
Expand All @@ -81,18 +75,16 @@ export const getAreaPointTransformer = (series) => {
getAreaPointTransformer.isStartedFromZero = true;

export const getBarPointTransformer = ({
argumentScale, valueScale, barWidth,
argumentScale, valueScale,
}) => {
const y1 = valueScale(0);
const categoryWidth = getWidth(argumentScale);
const offset = categoryWidth * (1 - barWidth) / 2;
const width = categoryWidth * barWidth;
const fixedArgumentScale = fixOffset(argumentScale);
return point => ({
...point,
x: argumentScale(point.argument) + offset,
x: fixedArgumentScale(point.argument),
y: valueScale(point.value),
y1,
width,
maxBarWidth: getWidth(argumentScale),
});
};
// Used for domain calculation and stacking.
Expand All @@ -107,35 +99,36 @@ export const findSeriesByName = (
export const dBar = ({
x, y, y1, width,
}) => ({
x, y: Math.min(y, y1), width: width || 2, height: Math.abs(y1 - y),
x: x - width / 2, y: Math.min(y, y1), width: width || 2, height: Math.abs(y1 - y),
});

export const pointAttributes = ({ size = DEFAULT_POINT_SIZE }) => {
const dPoint = symbol().size([size ** 2]).type(symbolCircle)();
return item => ({
// TODO: It should be calculated in *pointComponent*.
d: dPoint,
x: item.x,
y: item.y,
});
};
export const dSymbol = ({ size }) => symbol().size([size ** 2]).type(symbolCircle)();

export const dPie = ({
maxRadius, innerRadius, outerRadius, startAngle, endAngle,
}) => arc()
.innerRadius(innerRadius * maxRadius)
.outerRadius(outerRadius * maxRadius)
.startAngle(startAngle)
.endAngle(endAngle)();

getBarPointTransformer.getTargetElement = ({
x, y, y1, width,
x, y, y1, barWidth, maxBarWidth,
}) => {
const width = barWidth * maxBarWidth;
const height = Math.abs(y1 - y);
return {
x,
x: x - width / 2,
y,
d: `M0,0 ${width},0 ${width},${height} 0,${height}`,
};
};
getPiePointTransformer.getTargetElement = ({
x, y, innerRadius, outerRadius, startAngle, endAngle,
x, y, innerRadius, outerRadius, maxRadius, startAngle, endAngle,
}) => {
const center = arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.innerRadius(innerRadius * maxRadius)
.outerRadius(outerRadius * maxRadius)
.startAngle(startAngle)
.endAngle(endAngle)
.centroid();
Expand All @@ -144,17 +137,20 @@ getPiePointTransformer.getTargetElement = ({
};
};

getAreaPointTransformer.getTargetElement = ({ x, y }) => {
const size = DEFAULT_POINT_SIZE; // TODO get user size
return {
x,
y,
d: symbol().size([size ** 2]).type(symbolCircle)(),
};
};
getAreaPointTransformer.getTargetElement = ({ x, y }) => ({
x,
y,
d: symbol().size([2 ** 2]).type(symbolCircle)(),
});

getLinePointTransformer.getTargetElement = getAreaPointTransformer.getTargetElement;

getScatterPointTransformer.getTargetElement = ({ x, y, point }) => ({
x,
y,
d: symbol().size([point.size ** 2]).type(symbolCircle)(),
});

const getUniqueName = (list, name) => {
const names = new Set(list.map(item => item.name));
let ret = name;
Expand All @@ -166,29 +162,32 @@ const getUniqueName = (list, name) => {

// TODO: Memoization is much needed here.
// Though "series" list never persists, single "series" item most often does.
const createPoints = (argumentField, valueField, data) => {
const createPoints = (argumentField, valueField, data, props) => {
const points = [];
data.forEach((dataItem, index) => {
const argument = dataItem[argumentField];
const value = dataItem[valueField];
if (argument !== undefined && value !== undefined) {
points.push({ argument, value, index });
points.push({
argument, value, index, ...props,
});
}
});
return points;
};

export const addSeries = (series, data, palette, props) => {
export const addSeries = (series, data, palette, props, restProps) => {
// It is used to generate unique series dependent attribute names for patterns.
// *symbolName* cannot be used as it cannot be part of DOM attribute name.
const index = series.length;
const color = props.color || palette[index % palette.length];
return [...series, {
...props,
name: getUniqueName(series, props.name),
index,
points: createPoints(props.argumentField, props.valueField, data),
points: createPoints(props.argumentField, props.valueField, data, { ...restProps, color }),
palette, // TODO: For Pie only. Find a better place for it.
color: props.color || palette[index % palette.length],
color,
}];
};

Expand Down

0 comments on commit fde7756

Please sign in to comment.