/
filler.target.stack.js
110 lines (101 loc) · 2.97 KB
/
filler.target.stack.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
* @typedef { import('../../core/core.controller').default } Chart
* @typedef { import('../../core/core.scale').default } Scale
* @typedef { import('../../elements/element.point').default } PointElement
*/
import {LineElement} from '../../elements';
import {_isBetween} from '../../helpers';
import {_createBoundaryLine} from './filler.helper';
/**
* @param {{ chart: Chart; scale: Scale; index: number; line: LineElement; }} source
* @return {LineElement}
*/
export function _buildStackLine(source) {
const {scale, index, line} = source;
const points = [];
const segments = line.segments;
const sourcePoints = line.points;
const linesBelow = getLinesBelow(scale, index);
linesBelow.push(_createBoundaryLine({x: null, y: scale.bottom}, line));
for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
for (let j = segment.start; j <= segment.end; j++) {
addPointsBelow(points, sourcePoints[j], linesBelow);
}
}
return new LineElement({points, options: {}});
}
/**
* @param {Scale} scale
* @param {number} index
* @return {LineElement[]}
*/
function getLinesBelow(scale, index) {
const below = [];
const metas = scale.getMatchingVisibleMetas('line');
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
if (meta.index === index) {
break;
}
if (!meta.hidden) {
below.unshift(meta.dataset);
}
}
return below;
}
/**
* @param {PointElement[]} points
* @param {PointElement} sourcePoint
* @param {LineElement[]} linesBelow
*/
function addPointsBelow(points, sourcePoint, linesBelow) {
const postponed = [];
for (let j = 0; j < linesBelow.length; j++) {
const line = linesBelow[j];
const {first, last, point} = findPoint(line, sourcePoint, 'x');
if (!point || (first && last)) {
continue;
}
if (first) {
// First point of an segment -> need to add another point before this,
// from next line below.
postponed.unshift(point);
} else {
points.push(point);
if (!last) {
// In the middle of an segment, no need to add more points.
break;
}
}
}
points.push(...postponed);
}
/**
* @param {LineElement} line
* @param {PointElement} sourcePoint
* @param {string} property
* @returns {{point?: PointElement, first?: boolean, last?: boolean}}
*/
function findPoint(line, sourcePoint, property) {
const point = line.interpolate(sourcePoint, property);
if (!point) {
return {};
}
const pointValue = point[property];
const segments = line.segments;
const linePoints = line.points;
let first = false;
let last = false;
for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
const firstValue = linePoints[segment.start][property];
const lastValue = linePoints[segment.end][property];
if (_isBetween(pointValue, firstValue, lastValue)) {
first = pointValue === firstValue;
last = pointValue === lastValue;
break;
}
}
return {first, last, point};
}