PolylineGeometry constructor
PolylineGeometry(
- List<
Vector3> points, { - double width = 8.0,
- PolylineWidthMode widthMode = PolylineWidthMode.screenPixels,
- PolylineCap cap = PolylineCap.butt,
- DashPattern? dash,
- List<
double> ? perVertexWidth, - List<
Vector4> ? perVertexColor,
Creates a polyline through points (at least two).
width is measured per widthMode. perVertexWidth overrides it
per point for tapering, and perVertexColor sets a color per
point for gradients. cap selects rounded or flat ends, and
dash breaks the line into dashes. The strip is a placeholder
until the first updateForCamera call.
Implementation
factory PolylineGeometry(
List<Vector3> points, {
double width = 8.0,
PolylineWidthMode widthMode = PolylineWidthMode.screenPixels,
PolylineCap cap = PolylineCap.butt,
DashPattern? dash,
List<double>? perVertexWidth,
List<Vector4>? perVertexColor,
}) {
if (points.length < 2) {
throw ArgumentError('A polyline needs at least two points');
}
final inputCount = points.length;
if (perVertexWidth != null && perVertexWidth.length != inputCount) {
throw ArgumentError('perVertexWidth must have one entry per point');
}
if (perVertexColor != null && perVertexColor.length != inputCount) {
throw ArgumentError('perVertexColor must have one entry per point');
}
var working = <Vector3>[for (final p in points) p.clone()];
var workingWidths =
perVertexWidth != null
? List<double>.of(perVertexWidth)
: List<double>.filled(inputCount, width);
var workingColors = <Vector4>[
for (var i = 0; i < inputCount; i++)
perVertexColor?[i] ?? Vector4(1.0, 1.0, 1.0, 1.0),
];
// Dashes resample the line into dash segments joined by zero-width
// gap points, so the gap regions draw nothing.
var dashCapIndices = const <int>[];
if (dash != null) {
final dashed = resampleDashed(
working,
workingWidths,
workingColors,
dash,
);
working = dashed.points;
workingWidths = dashed.widths;
workingColors = dashed.colors;
dashCapIndices = dashed.dashCapIndices;
}
final copied = working;
final widths = workingWidths;
final count = copied.length;
// Cumulative arc length at each point, for the texture v coordinate.
final distances = List<double>.filled(count, 0.0);
for (var i = 1; i < count; i++) {
distances[i] = distances[i - 1] + copied[i].distanceTo(copied[i - 1]);
}
Vector4 colorOf(int i) => workingColors[i];
// Texture coordinates and colors do not depend on the camera, so
// they are set once here. The placeholder positions collapse the
// strip onto the points until updateForCamera runs.
//
// Round dash caps put a disk at every dash boundary; otherwise the
// disks are just the line's two round ends.
final diskPoints =
(dash != null && dash.cap == PolylineCap.round)
? dashCapIndices
: diskPointIndices(count, cap);
final stripVertexCount = count * 2;
final vertexCount =
stripVertexCount + diskPoints.length * (_diskSegments + 1);
final positions = Float32List(vertexCount * 3);
final normals = Float32List(vertexCount * 3);
final texCoords = Float32List(vertexCount * 2);
final colors = Float32List(vertexCount * 4);
void writeVertex(int v, Vector3 at, double u, double texV, Vector4 color) {
positions[v * 3] = at.x;
positions[v * 3 + 1] = at.y;
positions[v * 3 + 2] = at.z;
normals[v * 3 + 2] = 1.0;
texCoords[v * 2] = u;
texCoords[v * 2 + 1] = texV;
colors[v * 4] = color.x;
colors[v * 4 + 1] = color.y;
colors[v * 4 + 2] = color.z;
colors[v * 4 + 3] = color.w;
}
final indices = <int>[];
// The strip: two vertices per point.
for (var i = 0; i < count; i++) {
final color = colorOf(i);
writeVertex(i * 2, copied[i], 0.0, distances[i], color);
writeVertex(i * 2 + 1, copied[i], 1.0, distances[i], color);
}
for (var i = 0; i < count - 1; i++) {
final a = i * 2;
indices
..addAll([a, a + 2, a + 1])
..addAll([a + 1, a + 2, a + 3]);
}
// A triangle-fan disk for each round cap.
for (var ord = 0; ord < diskPoints.length; ord++) {
final point = diskPoints[ord];
final base = stripVertexCount + ord * (_diskSegments + 1);
final color = colorOf(point);
for (var k = 0; k <= _diskSegments; k++) {
writeVertex(base + k, copied[point], 0.5, distances[point], color);
}
for (var k = 0; k < _diskSegments; k++) {
final next = (k + 1) % _diskSegments;
indices.addAll([base, base + 1 + next, base + 1 + k]);
}
}
return PolylineGeometry._(
copied,
widths,
widthMode,
diskPoints,
positions: positions,
normals: normals,
texCoords: texCoords,
colors: colors,
indices: indices,
);
}