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