in src/chart/line/poly.ts [38:212]
function drawSegment(
ctx: PathProxy,
points: ArrayLike<number>,
start: number,
segLen: number,
allLen: number,
dir: number,
smooth: number,
smoothMonotone: 'x' | 'y' | 'none',
connectNulls: boolean
) {
let prevX: number;
let prevY: number;
let cpx0: number;
let cpy0: number;
let cpx1: number;
let cpy1: number;
let idx = start;
let k = 0;
for (; k < segLen; k++) {
let x = points[idx * 2];
let y = points[idx * 2 + 1];
if (idx >= allLen || idx < 0) {
break;
}
if (isPointNull(x, y)) {
if (connectNulls) {
idx += dir;
continue;
}
break;
}
if (idx === start) {
ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y);
cpx0 = x;
cpy0 = y;
}
else {
let dx = x - prevX;
let dy = y - prevY;
// Ignore tiny segment.
if ((dx * dx + dy * dy) < 0.5) {
idx += dir;
continue;
}
if (smooth > 0) {
let nextIdx = idx + dir;
let nextX = points[nextIdx * 2];
let nextY = points[nextIdx * 2 + 1];
// Ignore duplicate point
while (nextX === x && nextY === y && k < segLen) {
k++;
nextIdx += dir;
idx += dir;
nextX = points[nextIdx * 2];
nextY = points[nextIdx * 2 + 1];
x = points[idx * 2];
y = points[idx * 2 + 1];
dx = x - prevX;
dy = y - prevY;
}
let tmpK = k + 1;
if (connectNulls) {
// Find next point not null
while (isPointNull(nextX, nextY) && tmpK < segLen) {
tmpK++;
nextIdx += dir;
nextX = points[nextIdx * 2];
nextY = points[nextIdx * 2 + 1];
}
}
let ratioNextSeg = 0.5;
let vx: number = 0;
let vy: number = 0;
let nextCpx0;
let nextCpy0;
// Is last point
if (tmpK >= segLen || isPointNull(nextX, nextY)) {
cpx1 = x;
cpy1 = y;
}
else {
vx = nextX - prevX;
vy = nextY - prevY;
const dx0 = x - prevX;
const dx1 = nextX - x;
const dy0 = y - prevY;
const dy1 = nextY - y;
let lenPrevSeg;
let lenNextSeg;
if (smoothMonotone === 'x') {
lenPrevSeg = Math.abs(dx0);
lenNextSeg = Math.abs(dx1);
const dir = vx > 0 ? 1 : -1;
cpx1 = x - dir * lenPrevSeg * smooth;
cpy1 = y;
nextCpx0 = x + dir * lenNextSeg * smooth;
nextCpy0 = y;
}
else if (smoothMonotone === 'y') {
lenPrevSeg = Math.abs(dy0);
lenNextSeg = Math.abs(dy1);
const dir = vy > 0 ? 1 : -1;
cpx1 = x;
cpy1 = y - dir * lenPrevSeg * smooth;
nextCpx0 = x;
nextCpy0 = y + dir * lenNextSeg * smooth;
}
else {
lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0);
lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1);
// Use ratio of seg length
ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
cpx1 = x - vx * smooth * (1 - ratioNextSeg);
cpy1 = y - vy * smooth * (1 - ratioNextSeg);
// cp0 of next segment
nextCpx0 = x + vx * smooth * ratioNextSeg;
nextCpy0 = y + vy * smooth * ratioNextSeg;
// Smooth constraint between point and next point.
// Avoid exceeding extreme after smoothing.
nextCpx0 = mathMin(nextCpx0, mathMax(nextX, x));
nextCpy0 = mathMin(nextCpy0, mathMax(nextY, y));
nextCpx0 = mathMax(nextCpx0, mathMin(nextX, x));
nextCpy0 = mathMax(nextCpy0, mathMin(nextY, y));
// Reclaculate cp1 based on the adjusted cp0 of next seg.
vx = nextCpx0 - x;
vy = nextCpy0 - y;
cpx1 = x - vx * lenPrevSeg / lenNextSeg;
cpy1 = y - vy * lenPrevSeg / lenNextSeg;
// Smooth constraint between point and prev point.
// Avoid exceeding extreme after smoothing.
cpx1 = mathMin(cpx1, mathMax(prevX, x));
cpy1 = mathMin(cpy1, mathMax(prevY, y));
cpx1 = mathMax(cpx1, mathMin(prevX, x));
cpy1 = mathMax(cpy1, mathMin(prevY, y));
// Adjust next cp0 again.
vx = x - cpx1;
vy = y - cpy1;
nextCpx0 = x + vx * lenNextSeg / lenPrevSeg;
nextCpy0 = y + vy * lenNextSeg / lenPrevSeg;
}
}
ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y);
cpx0 = nextCpx0;
cpy0 = nextCpy0;
}
else {
ctx.lineTo(x, y);
}
}
prevX = x;
prevY = y;
idx += dir;
}
return k;
}