equalize method

Path<LatLng> equalize(
  1. num distanceInMeterPerTime, {
  2. bool smoothPath = true,
})

Splits the path into even sections.

The section size is defined with distanceInMeterPerTime. distanceInMeterPerTime means that the original size on the given path will stay the same but the created section could be smaller because of the "linear distance"

However - if you follow the steps in a given time then the distance from point to point (over time) is correct. (Almost - because of the curves generate with CatmullRomSpline2D

Path path = new Path.from(zigzag);

If smoothPath is turned on than the minimum of 3 coordinates is required otherwise we need two

Implementation

Path equalize(
  final num distanceInMeterPerTime, {
  final bool smoothPath = true,
}) {
  Validate.isTrue(
    distanceInMeterPerTime > 0,
    "Distance must be greater than 0",
  );

  Validate.isTrue(
    (smoothPath && coordinates.length >= 3) ||
        (!smoothPath && coordinates.length >= 2),
    "At least ${smoothPath ? 3 : 2} coordinates are needed to create the steps in between",
  );

  // If we "smooth" the path every second step becomes a spline - so every other step
  // becomes a "Keyframe". A step on the given path
  double stepDistance = smoothPath
      ? distanceInMeterPerTime * 2.0
      : distanceInMeterPerTime.toDouble();
  double baseLength = distance;

  Validate.isTrue(
    baseLength >= stepDistance,
    "Path distance must be at least ${stepDistance}m (step distance) but was $baseLength",
  );

  if (stepDistance > baseLength / 2) {
    _logger.warning(
        "Equalizing the path (L: $baseLength) with a key-frame distance of $stepDistance leads to"
        "weired results. Turn of path smooting.");
  }

  // no steps possible - so return an empty path
  if (baseLength == stepDistance) {
    return Path.from([coordinates.first, coordinates.last]);
  }

  List<T> tempCoordinates = coordinates.toList();
  Path path = Path();

  double remainingSteps = 0.0;
  double bearing;

  path.add(tempCoordinates.first);
  T baseStep = tempCoordinates.first;

  for (int index = 0; index < coordinates.length - 1; index++) {
    double distance = getDistance(
      tempCoordinates[index],
      tempCoordinates[index + 1],
    ) as double;

    // Remember the direction
    bearing = getDistance.bearing(
      tempCoordinates[index],
      tempCoordinates[index + 1],
    );

    if (remainingSteps <= distance ||
        (stepDistance - remainingSteps) <= distance) {
      // First step position
      double firstStepPos = stepDistance - remainingSteps;
      double steps = ((distance - firstStepPos) / stepDistance) + 1;

      int fullSteps = steps.toInt();

      remainingSteps = round(
            fullSteps > 0 ? steps % fullSteps : steps,
            decimals: 6,
          ) *
          stepDistance;

      baseStep = tempCoordinates[index];

      for (int stepCounter = 0; stepCounter < fullSteps; stepCounter++) {
        // Add step on the given path
        // Intermediate step is necessary to stay type-safe
        LatLng tempStep = getDistance.offset(baseStep, firstStepPos, bearing);
        LatLng nextStep = geoPositionFactory(tempStep.lat, tempStep.lng);
        path.add(nextStep);

        firstStepPos += stepDistance;

        if (smoothPath) {
          // Now - split it
          CatmullRomSpline2D<double> spline;

          if (path.numCoordinates == 3) {
            spline = _createSpline(path[0], path[0], path[1], path[2]);

            // Insert new point between 0 and 1
            path.coordinates.insert(
              1,
              _pointToPosition(spline.percentage(50)),
            );
          } else if (path.numCoordinates > 3) {
            int baseIndex = path.numCoordinates - 1;
            spline = _createSpline(
              path[baseIndex - 3],
              path[baseIndex - 2],
              path[baseIndex - 1],
              path[baseIndex],
            );

            // Insert new point at last position - 2 (pushes the next 2 items down)
            path.coordinates.insert(
              baseIndex - 1,
              _pointToPosition(spline.percentage(50)),
            );
          }
        }
      }
    } else {
      remainingSteps += distance;
    }
  }

  // If last step is on the same position as the last generated step
  // then don't add the last base step.
  if (baseStep.round() != tempCoordinates.last.round() &&
      baseStep.round() != tempCoordinates.first.round() &&
      round(getDistance(baseStep, tempCoordinates.last) as double) > 1) {
    path.add(tempCoordinates.last);
  }

  if (smoothPath) {
    // Last Spline between the last 4 elements
    int baseIndex = path.numCoordinates - 1;

    if (baseIndex > 3) {
      CatmullRomSpline2D<double> spline = _createSpline(
        path[baseIndex - 3],
        path[baseIndex - 2],
        path[baseIndex - 1],
        path[baseIndex - 0],
      );

      path.coordinates.insert(
        baseIndex - 1,
        _pointToPosition(spline.percentage(50)),
      );
    }

    // Check if there is a remaining gap between the last two elements - close it
    // Could be because of reminder from path divisions
    baseIndex = path.numCoordinates - 1;
    if (getDistance(path[baseIndex - 1], path[baseIndex]) >= stepDistance) {
      CatmullRomSpline2D<double> spline = _createSpline(
        path[baseIndex - 1],
        path[baseIndex - 1],
        path[baseIndex - 0],
        path[baseIndex - 0],
      );

      path.coordinates.insert(
        baseIndex,
        _pointToPosition(spline.percentage(50)),
      );
    }
  }

  return path;
}