PolylineGeometry constructor

PolylineGeometry(
  1. List<Vector3> points, {
  2. double width = 8.0,
  3. PolylineWidthMode widthMode = PolylineWidthMode.screenPixels,
  4. PolylineCap cap = PolylineCap.butt,
  5. DashPattern? dash,
  6. List<double>? perVertexWidth,
  7. 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,
  );
}