Here's an example:
uses
Skia;
function MakeCubicSplineInterpolation(const APoints: TArray<TPointF>): ISkPath;
var
LPathBuilder: ISkPathBuilder;
LSegments: Integer;
I: Integer;
mx: Single;
my: Single;
LScratches: array of
record
a, b, c, r, p: TPointF;
end;
begin
LPathBuilder := TSkPathBuilder.Create;
if Length(APoints) < 2 then
Exit(LPathBuilder.Detach);
if Length(APoints) = 2 then
begin
LPathBuilder.MoveTo(APoints[0]);
LPathBuilder.LineTo(APoints[1]);
Exit(LPathBuilder.Detach);
end;
LSegments := Length(APoints) - 1;
SetLength(LScratches, LSegments);
LScratches[0].a := PointF(0, 0);
LScratches[0].b := PointF(2, 2);
LScratches[0].c := PointF(1, 1);
LScratches[0].r := PointF(APoints[0].X + 2 * APoints[1].X, APoints[0].Y + 2 * APoints[1].Y);
for I := 1 to LSegments - 2 do
begin
LScratches[I].a := PointF(1, 1);
LScratches[I].b := PointF(4, 4);
LScratches[I].c := PointF(1, 1);
LScratches[I].r := PointF(4 * APoints[i].X + 2 * APoints[I + 1].X, 4 * APoints[I].Y + 2 * APoints[I + 1].Y);
end;
LScratches[LSegments - 1].a := PointF(2, 2);
LScratches[LSegments - 1].b := PointF(7, 7);
LScratches[LSegments - 1].c := PointF(0, 0);
LScratches[LSegments - 1].r := PointF(8 * APoints[LSegments - 1].X + APoints[LSegments].X, 8 * APoints[LSegments - 1].Y + APoints[LSegments].Y);
for I := 1 to LSegments - 1 do
begin
mx := LScratches[I].a.X / LScratches[I - 1].b.X;
my := LScratches[I].a.Y / LScratches[I - 1].b.Y;
LScratches[I].b := LScratches[I].b - PointF(mx * LScratches[I - 1].c.X, my * LScratches[I - 1].c.Y);
LScratches[I].r := LScratches[I].r - PointF(mx * LScratches[I - 1].r.X, my * LScratches[I - 1].r.Y);
end;
LScratches[LSegments - 1].p := PointF(LScratches[LSegments - 1].r.X / LScratches[LSegments - 1].b.X,
LScratches[LSegments - 1].r.Y / LScratches[LSegments - 1].b.Y);
for I := Length(APoints) - 3 downto 0 do
begin
LScratches[I].p := PointF((LScratches[I].r.X - LScratches[I].c.X * LScratches[I + 1].p.X) / LScratches[I].b.X,
(LScratches[I].r.Y - LScratches[I].c.Y * LScratches[I + 1].p.Y) / LScratches[I].b.Y);
end;
LPathBuilder.MoveTo(APoints[0]);
for I := 0 to LSegments - 2 do
begin
LPathBuilder.CubicTo(LScratches[I].p,
PointF(2 * APoints[I + 1].X - LScratches[I + 1].p.X, 2 * APoints[I + 1].Y - LScratches[I + 1].p.Y), APoints[I + 1]);
end;
LPathBuilder.CubicTo(LScratches[LSegments - 1].p,
PointF(0.5 * (APoints[LSegments].X + LScratches[LSegments - 1].p.X),
0.5 * (APoints[LSegments].Y + LScratches[LSegments - 1].p.Y)), APoints[LSegments]);
Result := LPathBuilder.Detach;
end;
procedure TForm1.SkPaintBox1Draw(ASender: TObject; const ACanvas: ISkCanvas;
const ADest: TRectF; const AOpacity: Single);
var
LPaint: ISkPaint;
LMyPoints: TArray<TPointF>;
begin
LMyPoints := [PointF(62, 511), PointF(162, 605), PointF(262, 610),
PointF(362, 402), PointF(462, 959), PointF(562, 58), PointF(662, 272),
PointF(762, 99), PointF(862, 759), PointF(962, 945)];
LPaint := TSkPaint.Create(TSkPaintStyle.Stroke);
LPaint.Color := TAlphaColors.Red;
LPaint.AntiAlias := True;
LPaint.StrokeWidth := 3;
LPaint.StrokeCap := TSkStrokeCap.Round;
ACanvas.DrawPath(MakeCubicSplineInterpolation(LMyPoints), LPaint);
LPaint.StrokeWidth := 10;
LPaint.Color := TAlphaColors.Black;
ACanvas.DrawPoints(TSkDrawPointsMode.Points, LMyPoints, LPaint);
end;
Result:
Note: You don't need to use Skia, it was just a facilitator for the example.