1cb93a386Sopenharmony_ci// Copyright 2020 Google LLC.
2cb93a386Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3cb93a386Sopenharmony_ci#include "tools/fiddle/examples.h"
4cb93a386Sopenharmony_ciREG_FIDDLE(SmoothBezierSplineInterpolation, 1024, 1024, false, 0) {
5cb93a386Sopenharmony_ci// Smooth Bézier Spline Interpolation
6cb93a386Sopenharmony_ci
7cb93a386Sopenharmony_ciSkPath MakeCubicSplineInterpolation(const SkPoint* pts, size_t N) {
8cb93a386Sopenharmony_ci    // Code borrowed from https://www.particleincell.com/2012/bezier-splines/
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci    SkPath path;
11cb93a386Sopenharmony_ci    if (N < 2) {
12cb93a386Sopenharmony_ci        return path;
13cb93a386Sopenharmony_ci    }
14cb93a386Sopenharmony_ci    if (N == 2) {
15cb93a386Sopenharmony_ci        path.moveTo(pts[0]);
16cb93a386Sopenharmony_ci        path.lineTo(pts[1]);
17cb93a386Sopenharmony_ci        return path;
18cb93a386Sopenharmony_ci    }
19cb93a386Sopenharmony_ci    size_t n = N - 1;  // number of segments
20cb93a386Sopenharmony_ci    struct Scratch {
21cb93a386Sopenharmony_ci        SkPoint a, b, c, r, p;
22cb93a386Sopenharmony_ci    };
23cb93a386Sopenharmony_ci    // Can I do this will less allocation?
24cb93a386Sopenharmony_ci    std::unique_ptr<Scratch[]> s(new Scratch[n]);
25cb93a386Sopenharmony_ci    s[0].a = {0, 0};
26cb93a386Sopenharmony_ci    s[0].b = {2, 2};
27cb93a386Sopenharmony_ci    s[0].c = {1, 1};
28cb93a386Sopenharmony_ci    s[0].r = {pts[0].x() + 2 * pts[1].x(), pts[0].y() + 2 * pts[1].y()};
29cb93a386Sopenharmony_ci    for (size_t i = 1; i < n - 1; ++i) {
30cb93a386Sopenharmony_ci        s[i].a = {1, 1};
31cb93a386Sopenharmony_ci        s[i].b = {4, 4};
32cb93a386Sopenharmony_ci        s[i].c = {1, 1};
33cb93a386Sopenharmony_ci        s[i].r = {4 * pts[i].x() + 2 * pts[i + 1].x(), 4 * pts[i].y() + 2 * pts[i + 1].y()};
34cb93a386Sopenharmony_ci    }
35cb93a386Sopenharmony_ci    s[n - 1].a = {2, 2};
36cb93a386Sopenharmony_ci    s[n - 1].b = {7, 7};
37cb93a386Sopenharmony_ci    s[n - 1].c = {0, 0};
38cb93a386Sopenharmony_ci    s[n - 1].r = {8 * pts[n - 1].x() + pts[N - 1].x(), 8 * pts[n - 1].y() + pts[N - 1].y()};
39cb93a386Sopenharmony_ci    for (size_t i = 1; i < n; i++) {
40cb93a386Sopenharmony_ci        float mx = s[i].a.x() / s[i - 1].b.x();
41cb93a386Sopenharmony_ci        float my = s[i].a.y() / s[i - 1].b.y();
42cb93a386Sopenharmony_ci        s[i].b -= {mx * s[i - 1].c.x(), my * s[i - 1].c.y()};
43cb93a386Sopenharmony_ci        s[i].r -= {mx * s[i - 1].r.x(), my * s[i - 1].r.y()};
44cb93a386Sopenharmony_ci    }
45cb93a386Sopenharmony_ci    s[n - 1].p = {s[n - 1].r.x() / s[n - 1].b.x(), s[n - 1].r.y() / s[n - 1].b.y()};
46cb93a386Sopenharmony_ci    for (int i = (int)N - 3; i >= 0; --i) {
47cb93a386Sopenharmony_ci        s[i].p = {(s[i].r.x() - s[i].c.x() * s[i + 1].p.fX) / s[i].b.x(),
48cb93a386Sopenharmony_ci                  (s[i].r.y() - s[i].c.y() * s[i + 1].p.fY) / s[i].b.y()};
49cb93a386Sopenharmony_ci    }
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_ci    path.moveTo(pts[0]);
52cb93a386Sopenharmony_ci    for (size_t i = 0; i < n - 1; i++) {
53cb93a386Sopenharmony_ci        SkPoint q = {2 * pts[i + 1].x() - s[i + 1].p.fX, 2 * pts[i + 1].y() - s[i + 1].p.fY};
54cb93a386Sopenharmony_ci        path.cubicTo(s[i].p, q, pts[i + 1]);
55cb93a386Sopenharmony_ci    }
56cb93a386Sopenharmony_ci    SkPoint q = {0.5f * (pts[N - 1].x() + s[n - 1].p.x()),
57cb93a386Sopenharmony_ci                 0.5f * (pts[N - 1].y() + s[n - 1].p.y())};
58cb93a386Sopenharmony_ci    path.cubicTo(s[n - 1].p, q, pts[n]);
59cb93a386Sopenharmony_ci    return path;
60cb93a386Sopenharmony_ci}
61cb93a386Sopenharmony_ci
62cb93a386Sopenharmony_civoid draw(SkCanvas* canvas) {
63cb93a386Sopenharmony_ci    SkPaint p;
64cb93a386Sopenharmony_ci    p.setColor(SK_ColorRED);
65cb93a386Sopenharmony_ci    p.setAntiAlias(true);
66cb93a386Sopenharmony_ci    p.setStyle(SkPaint::kStroke_Style);
67cb93a386Sopenharmony_ci    p.setStrokeWidth(3);
68cb93a386Sopenharmony_ci    p.setStrokeCap(SkPaint::kRound_Cap);
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci    // randomly generated y values in range [12,1024].
71cb93a386Sopenharmony_ci    SkPoint pts[] = {
72cb93a386Sopenharmony_ci            {62, 511}, {162, 605}, {262, 610}, {362, 402}, {462, 959},
73cb93a386Sopenharmony_ci            {562, 58}, {662, 272}, {762, 99},  {862, 759}, {962, 945},
74cb93a386Sopenharmony_ci    };
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci    canvas->drawPath(MakeCubicSplineInterpolation(pts, SK_ARRAY_COUNT(pts)), p);
77cb93a386Sopenharmony_ci
78cb93a386Sopenharmony_ci    p.setStrokeWidth(10);
79cb93a386Sopenharmony_ci    p.setColor(SK_ColorBLACK);
80cb93a386Sopenharmony_ci    canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(pts), pts, p);
81cb93a386Sopenharmony_ci}
82cb93a386Sopenharmony_ci}  // END FIDDLE
83