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