in web/github_dataviz/lib/layered_chart.dart [35:163]
void buildPaths(
Size size,
List<DataSeries> dataToPlot,
List<WeekLabel> milestones,
int numPoints,
double graphGap,
double margin,
double capTheta,
double capSize) {
double screenRatio = size.width / size.height;
double degrees = MathUtils.clampedMap(screenRatio, 0.5, 2.5, 50, 5);
theta = pi * degrees / 180;
graphHeight = MathUtils.clampedMap(screenRatio, 0.5, 2.5, 50, 150);
int m = dataToPlot.length;
paths = [];
capPaths = [];
maxValues = [];
for (int i = 0; i < m; i++) {
int n = dataToPlot[i].series.length;
maxValues.add(0);
for (int j = 0; j < n; j++) {
double v = dataToPlot[i].series[j].toDouble();
if (v > maxValues[i]) {
maxValues[i] = v;
}
}
}
double totalGap = m * graphGap;
double xIndent = totalGap / tan(capTheta);
double startX = margin + xIndent;
double endX = size.width - margin;
double startY = size.height;
double endY = startY - (endX - startX) * tan(theta);
double xWidth = (endX - startX) / numPoints;
double capRangeX = capSize * cos(capTheta);
double tanCapTheta = tan(capTheta);
final curvePoints = <double>[];
for (int i = 0; i < m; i++) {
List<int> series = dataToPlot[i].series;
int n = series.length;
final controlPoints = <Point2D>[];
controlPoints.add(Point2D(-1, 0));
double last = 0;
for (int j = 0; j < n; j++) {
double v = series[j].toDouble();
controlPoints.add(Point2D(j.toDouble(), v));
last = v;
}
controlPoints.add(Point2D(n.toDouble(), last));
CatmullInterpolator curve = CatmullInterpolator(controlPoints);
ControlPointAndValue cpv = ControlPointAndValue();
for (int j = 0; j < numPoints; j++) {
cpv.value = MathUtils.map(
j.toDouble(), 0, (numPoints - 1).toDouble(), 0, (n - 1).toDouble());
curve.progressiveGet(cpv);
curvePoints.add(MathUtils.map(
max(0, cpv.value!), 0, maxValues[i].toDouble(), 0, graphHeight));
}
paths.add(Path());
capPaths.add(Path());
paths[i].moveTo(startX, startY);
capPaths[i].moveTo(startX, startY);
for (int j = 0; j < numPoints; j++) {
double v = curvePoints[j];
int k = j + 1;
double xDist = xWidth;
double capV = v;
while (k < numPoints && xDist <= capRangeX) {
double cy = curvePoints[k] + xDist * tanCapTheta;
capV = max(capV, cy);
k++;
xDist += xWidth;
}
double x = MathUtils.map(
j.toDouble(), 0, (numPoints - 1).toDouble(), startX, endX);
double baseY = MathUtils.map(
j.toDouble(), 0, (numPoints - 1).toDouble(), startY, endY);
double y = baseY - v;
double cY = baseY - capV;
paths[i].lineTo(x, y);
if (j == 0) {
int k = capRangeX ~/ xWidth;
double mx = MathUtils.map(
-k.toDouble(), 0, (numPoints - 1).toDouble(), startX, endX);
double my = MathUtils.map(
-k.toDouble(), 0, (numPoints - 1).toDouble(), startY, endY) -
capV;
capPaths[i].lineTo(mx, my);
}
capPaths[i].lineTo(x, cY);
}
paths[i].lineTo(endX, endY);
paths[i].lineTo(endX, endY + 1);
paths[i].lineTo(startX, startY + 1);
paths[i].close();
capPaths[i].lineTo(endX, endY);
capPaths[i].lineTo(endX, endY + 1);
capPaths[i].lineTo(startX, startY + 1);
capPaths[i].close();
}
labelPainter = [];
for (int i = 0; i < dataToPlot.length; i++) {
TextSpan span = TextSpan(
style: const TextStyle(
color: Color.fromARGB(255, 255, 255, 255), fontSize: 12),
text: dataToPlot[i].label.toUpperCase());
TextPainter tp = TextPainter(
text: span,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr);
tp.layout();
labelPainter.add(tp);
}
milestonePainter = [];
for (int i = 0; i < milestones.length; i++) {
TextSpan span = TextSpan(
style: const TextStyle(
color: Color.fromARGB(255, 255, 255, 255), fontSize: 10),
text: milestones[i].label.toUpperCase());
TextPainter tp = TextPainter(
text: span,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr);
tp.layout();
milestonePainter.add(tp);
}
lastSize = Size(size.width, size.height);
}