xref: /aosp_15_r20/external/skia/tools/viewer/PathSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkColorPriv.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkGraphics.h"
14 #include "include/core/SkPathBuilder.h"
15 #include "include/core/SkPathEffect.h"
16 #include "include/core/SkPathUtils.h"
17 #include "include/core/SkRegion.h"
18 #include "include/core/SkShader.h"
19 #include "include/core/SkTypeface.h"
20 #include "include/effects/SkGradientShader.h"
21 #include "include/utils/SkParsePath.h"
22 #include "src/base/SkTime.h"
23 #include "src/base/SkUTF.h"
24 #include "src/core/SkGeometry.h"
25 #include "tools/fonts/FontToolUtils.h"
26 #include "tools/timer/TimeUtils.h"
27 #include "tools/viewer/ClickHandlerSlide.h"
28 
29 #include <stdlib.h>
30 
31 // http://code.google.com/p/skia/issues/detail?id=32
test_cubic()32 static void test_cubic() {
33     SkPoint src[4] = {
34         { 556.25000f, 523.03003f },
35         { 556.23999f, 522.96002f },
36         { 556.21997f, 522.89001f },
37         { 556.21997f, 522.82001f }
38     };
39     SkPoint dst[11];
40     dst[10].set(42, -42);   // one past the end, that we don't clobber these
41     SkScalar tval[] = { 0.33333334f, 0.99999994f };
42 
43     SkChopCubicAt(src, dst, tval, 2);
44 
45 #if 0
46     for (int i = 0; i < 11; i++) {
47         SkDebugf("--- %d [%g %g]\n", i, dst[i].fX, dst[i].fY);
48     }
49 #endif
50 }
51 
test_cubic2()52 static void test_cubic2() {
53     const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z";
54     SkPath path;
55     SkParsePath::FromSVGString(str, &path);
56 
57     {
58         SkRect r = path.getBounds();
59         SkIRect ir;
60         r.round(&ir);
61         SkDebugf("[%g %g %g %g] [%x %x %x %x]\n",
62                 SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop),
63                 SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom),
64                 (uint32_t)ir.fLeft, (uint32_t)ir.fTop, (uint32_t)ir.fRight, (uint32_t)ir.fBottom);
65     }
66 
67     SkBitmap bitmap;
68     bitmap.allocN32Pixels(300, 200);
69 
70     SkCanvas canvas(bitmap);
71     SkPaint paint;
72     paint.setAntiAlias(true);
73     canvas.drawPath(path, paint);
74 }
75 
76 class PathSlide : public ClickHandlerSlide {
77     SkScalar fPrevSecs;
78     SkScalar fDStroke, fStroke, fMinStroke, fMaxStroke;
79     SkPath fPath[6];
80     bool fShowHairline;
81 
82 public:
PathSlide()83     PathSlide() {
84         fPrevSecs = 0;
85         fName = "Paths";
86     }
87 
load(SkScalar w,SkScalar h)88     void load(SkScalar w, SkScalar h) override {
89         test_cubic();
90         test_cubic2();
91 
92         fShowHairline = false;
93 
94         fDStroke = 1;
95         fStroke = 10;
96         fMinStroke = 10;
97         fMaxStroke = 180;
98 
99         const SkScalar V = 85;
100 
101         fPath[0].moveTo(40, 70);
102         fPath[0].lineTo(70, 70 + SK_ScalarHalf);
103         fPath[0].lineTo(110, 70);
104 
105         fPath[1].moveTo(40, 70);
106         fPath[1].lineTo(70, 70 - SK_ScalarHalf);
107         fPath[1].lineTo(110, 70);
108 
109         fPath[2].moveTo(V, V);
110         fPath[2].lineTo(50, V);
111         fPath[2].lineTo(50, 50);
112 
113         fPath[3].moveTo(50, 50);
114         fPath[3].lineTo(50, V);
115         fPath[3].lineTo(V, V);
116 
117         fPath[4].moveTo(50, 50);
118         fPath[4].lineTo(50, V);
119         fPath[4].lineTo(52, 50);
120 
121         fPath[5].moveTo(52, 50);
122         fPath[5].lineTo(50, V);
123         fPath[5].lineTo(50, 50);
124     }
125 
drawPath(SkCanvas * canvas,const SkPath & path,SkPaint::Join j)126     void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) {
127         SkPaint paint;
128 
129         paint.setAntiAlias(true);
130         paint.setStyle(SkPaint::kStroke_Style);
131         paint.setStrokeJoin(j);
132         paint.setStrokeWidth(fStroke);
133 
134         if (fShowHairline) {
135             SkPath fill;
136 
137             skpathutils::FillPathWithPaint(path, paint, &fill);
138             paint.setStrokeWidth(0);
139             canvas->drawPath(fill, paint);
140         } else {
141             canvas->drawPath(path, paint);
142         }
143 
144         paint.setColor(SK_ColorRED);
145         paint.setStrokeWidth(0);
146         canvas->drawPath(path, paint);
147     }
148 
draw(SkCanvas * canvas)149     void draw(SkCanvas* canvas) override {
150         canvas->clear(0xFFDDDDDD);
151 
152         canvas->translate(50, 50);
153 
154         static const SkPaint::Join gJoins[] = {
155             SkPaint::kBevel_Join,
156             SkPaint::kMiter_Join,
157             SkPaint::kRound_Join
158         };
159 
160         for (size_t i = 0; i < std::size(gJoins); i++) {
161             canvas->save();
162             for (size_t j = 0; j < std::size(fPath); j++) {
163                 this->drawPath(canvas, fPath[j], gJoins[i]);
164                 canvas->translate(200, 0);
165             }
166             canvas->restore();
167 
168             canvas->translate(0, 200);
169         }
170     }
171 
animate(double nanos)172     bool animate(double nanos) override {
173         SkScalar currSecs = TimeUtils::Scaled(1e-9 * nanos, 100);
174         SkScalar delta = currSecs - fPrevSecs;
175         fPrevSecs = currSecs;
176 
177         fStroke += fDStroke * delta;
178         if (fStroke > fMaxStroke || fStroke < fMinStroke) {
179             fDStroke = -fDStroke;
180         }
181         return true;
182     }
183 
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)184     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
185         fShowHairline = !fShowHairline;
186         return nullptr;
187     }
188 
onClick(ClickHandlerSlide::Click *)189     bool onClick(ClickHandlerSlide::Click*) override { return false; }
190 };
191 
192 DEF_SLIDE( return new PathSlide; )
193 
194 //////////////////////////////////////////////////////////////////////////////
195 
196 #include "include/effects/SkCornerPathEffect.h"
197 #include "src/base/SkRandom.h"
198 
199 class ArcToSlide : public ClickHandlerSlide {
200     bool fDoFrame, fDoCorner, fDoConic;
201     SkPaint fPtsPaint, fSkeletonPaint, fCornerPaint;
202     enum {
203         N = 4
204     };
205     SkPoint fPts[N];
206 
207 public:
ArcToSlide()208     ArcToSlide() : fDoFrame(false), fDoCorner(false), fDoConic(false) {
209         SkRandom rand;
210         for (int i = 0; i < N; ++i) {
211             fPts[i].fX = 20 + rand.nextUScalar1() * 640;
212             fPts[i].fY = 20 + rand.nextUScalar1() * 480;
213         }
214 
215         const SkScalar rad = 50;
216 
217         fPtsPaint.setAntiAlias(true);
218         fPtsPaint.setStrokeWidth(15);
219         fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
220 
221         fCornerPaint.setAntiAlias(true);
222         fCornerPaint.setStyle(SkPaint::kStroke_Style);
223         fCornerPaint.setStrokeWidth(13);
224         fCornerPaint.setColor(SK_ColorGREEN);
225         fCornerPaint.setPathEffect(SkCornerPathEffect::Make(rad*2));
226 
227         fSkeletonPaint.setAntiAlias(true);
228         fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
229         fSkeletonPaint.setColor(SK_ColorRED);
230 
231         fName = "ArcTo";
232     }
233 
onChar(SkUnichar uni)234     bool onChar(SkUnichar uni) override {
235             switch (uni) {
236                 case '1': this->toggle(fDoFrame); return true;
237                 case '2': this->toggle(fDoCorner); return true;
238                 case '3': this->toggle(fDoConic); return true;
239                 default: break;
240             }
241             return false;
242     }
draw(SkCanvas * canvas)243     void draw(SkCanvas* canvas) override {
244         canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
245 
246         SkPath path;
247         this->makePath(&path);
248 
249         if (fDoCorner) {
250             canvas->drawPath(path, fCornerPaint);
251         }
252 
253         canvas->drawPath(path, fSkeletonPaint);
254     }
255 
256 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)257     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
258         const SkScalar tol = 4;
259         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
260         for (int i = 0; i < N; ++i) {
261             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
262                 return new Click([this, i](Click* c) {
263                     fPts[i] = c->fCurr;
264                     return true;
265                 });
266             }
267         }
268         return nullptr;
269     }
270 
onClick(ClickHandlerSlide::Click *)271     bool onClick(ClickHandlerSlide::Click *) override { return false; }
272 
273 private:
makePath(SkPath * path)274     void makePath(SkPath* path) {
275         path->moveTo(fPts[0]);
276         for (int i = 1; i < N; ++i) {
277             path->lineTo(fPts[i]);
278         }
279         if (!fDoFrame) {
280             path->close();
281         }
282     }
283 
toggle(bool & value)284     void toggle(bool& value) {
285         value = !value;
286     }
287 };
288 DEF_SLIDE( return new ArcToSlide; )
289 
290 /////////////
291 
292 class FatStrokeSlide : public ClickHandlerSlide {
293     bool fClosed, fShowStroke, fShowHidden, fShowSkeleton, fAsCurves = false;
294     int  fJoinType, fCapType;
295     float fWidth = 30;
296     SkPaint fPtsPaint, fHiddenPaint, fSkeletonPaint, fStrokePaint;
297 
298     enum {
299         N = 4
300     };
301     SkPoint fPts[N];
302 
303 public:
FatStrokeSlide()304     FatStrokeSlide()
305             : fClosed(false)
306             , fShowStroke(true)
307             , fShowHidden(false)
308             , fShowSkeleton(true)
309             , fJoinType(0)
310             , fCapType(0) {
311         SkRandom rand;
312         for (int i = 0; i < N; ++i) {
313             fPts[i].fX = 20 + rand.nextUScalar1() * 640;
314             fPts[i].fY = 20 + rand.nextUScalar1() * 480;
315         }
316 
317         fPtsPaint.setAntiAlias(true);
318         fPtsPaint.setStrokeWidth(10);
319         fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
320 
321         fHiddenPaint.setAntiAlias(true);
322         fHiddenPaint.setStyle(SkPaint::kStroke_Style);
323         fHiddenPaint.setColor(0xFF0000FF);
324 
325         fStrokePaint.setAntiAlias(true);
326         fStrokePaint.setStyle(SkPaint::kStroke_Style);
327         fStrokePaint.setStrokeWidth(50);
328         fStrokePaint.setColor(0x8000FF00);
329 
330         fSkeletonPaint.setAntiAlias(true);
331         fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
332         fSkeletonPaint.setColor(SK_ColorRED);
333 
334         fName = "FatStroke";
335     }
onChar(SkUnichar uni)336     bool onChar(SkUnichar uni) override {
337             switch (uni) {
338                 case '1': this->toggle(fShowSkeleton); return true;
339                 case '2': this->toggle(fShowStroke); return true;
340                 case '3': this->toggle(fShowHidden); return true;
341                 case '4': this->toggle3(fJoinType); return true;
342                 case '5': this->toggle3(fCapType); return true;
343                 case '6': this->toggle(fClosed); return true;
344                 case 'c': this->toggle(fAsCurves); return true;
345                 case '-': fWidth -= 5; return true;
346                 case '=': fWidth += 5; return true;
347                 default: break;
348             }
349             return false;
350     }
351 
draw(SkCanvas * canvas)352     void draw(SkCanvas* canvas) override {
353         canvas->drawColor(0xFFEEEEEE);
354 
355         SkPath path;
356         this->makePath(&path);
357 
358         fStrokePaint.setStrokeWidth(fWidth);
359         fStrokePaint.setStrokeJoin((SkPaint::Join)fJoinType);
360         fStrokePaint.setStrokeCap((SkPaint::Cap)fCapType);
361 
362         if (fShowStroke) {
363             canvas->drawPath(path, fStrokePaint);
364         }
365         if (fShowHidden) {
366             SkPath hidden;
367             skpathutils::FillPathWithPaint(path, fStrokePaint, &hidden);
368             canvas->drawPath(hidden, fHiddenPaint);
369         }
370         if (fShowSkeleton) {
371             canvas->drawPath(path, fSkeletonPaint);
372         }
373         canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
374     }
375 
376 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)377     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
378         const SkScalar tol = 4;
379         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
380         for (int i = 0; i < N; ++i) {
381             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
382                 return new Click([this, i](Click* c) {
383                     fPts[i] = c->fCurr;
384                     return true;
385                 });
386             }
387         }
388         return nullptr;
389     }
390 
onClick(ClickHandlerSlide::Click *)391     bool onClick(ClickHandlerSlide::Click *) override { return false; }
392 
393 private:
toggle(bool & value)394     void toggle(bool& value) {
395         value = !value;
396     }
397 
toggle3(int & value)398     void toggle3(int& value) {
399         value = (value + 1) % 3;
400     }
401 
makePath(SkPath * path)402     void makePath(SkPath* path) {
403         path->moveTo(fPts[0]);
404         if (fAsCurves) {
405             for (int i = 1; i < N-2; ++i) {
406                 path->quadTo(fPts[i], (fPts[i+1] + fPts[i]) * 0.5f);
407             }
408             path->quadTo(fPts[N-2], fPts[N-1]);
409         } else {
410             for (int i = 1; i < N; ++i) {
411                 path->lineTo(fPts[i]);
412             }
413         }
414         if (fClosed) {
415             path->close();
416         }
417     }
418 };
DEF_SLIDE(return new FatStrokeSlide;)419 DEF_SLIDE( return new FatStrokeSlide; )
420 
421 static int compute_parallel_to_base(const SkPoint pts[4], SkScalar t[2]) {
422     // F = At^3 + Bt^2 + Ct + D
423     SkVector A = pts[3] - pts[0] + (pts[1] - pts[2]) * 3.0f;
424     SkVector B = (pts[0] - pts[1] - pts[1] + pts[2]) * 3.0f;
425     SkVector C = (pts[1] - pts[0]) * 3.0f;
426     SkVector DA = pts[3] - pts[0];
427 
428     // F' = 3At^2 + 2Bt + C
429     SkScalar a = 3 * A.cross(DA);
430     SkScalar b = 2 * B.cross(DA);
431     SkScalar c = C.cross(DA);
432 
433     int n = SkFindUnitQuadRoots(a, b, c, t);
434     SkString str;
435     for (int i = 0; i < n; ++i) {
436         str.appendf(" %g", t[i]);
437     }
438     SkDebugf("roots %s\n", str.c_str());
439     return n;
440 }
441 
442 class CubicCurveSlide : public ClickHandlerSlide {
443     enum {
444         N = 4
445     };
446     SkPoint fPts[N];
447 
448 public:
CubicCurveSlide()449     CubicCurveSlide() {
450         SkRandom rand;
451         for (int i = 0; i < N; ++i) {
452             fPts[i].fX = 20 + rand.nextUScalar1() * 640;
453             fPts[i].fY = 20 + rand.nextUScalar1() * 480;
454         }
455         fName = "CubicCurve";
456     }
457 
draw(SkCanvas * canvas)458     void draw(SkCanvas* canvas) override {
459         SkPaint paint;
460         paint.setAntiAlias(true);
461 
462         {
463             SkPath path;
464             path.moveTo(fPts[0]);
465             path.cubicTo(fPts[1], fPts[2], fPts[3]);
466             paint.setStyle(SkPaint::kStroke_Style);
467             canvas->drawPath(path, paint);
468         }
469 
470         {
471             paint.setColor(SK_ColorRED);
472             SkScalar t[2];
473             int n = compute_parallel_to_base(fPts, t);
474             SkPoint loc;
475             SkVector tan;
476             for (int i = 0; i < n; ++i) {
477                 SkEvalCubicAt(fPts, t[i], &loc, &tan, nullptr);
478                 tan.setLength(30);
479                 canvas->drawLine(loc - tan, loc + tan, paint);
480             }
481             paint.setStrokeWidth(0.5f);
482             canvas->drawLine(fPts[0], fPts[3], paint);
483 
484             paint.setColor(SK_ColorBLUE);
485             paint.setStrokeWidth(6);
486             SkEvalCubicAt(fPts, 0.5f, &loc, nullptr, nullptr);
487             canvas->drawPoint(loc, paint);
488 
489             paint.setColor(0xFF008800);
490             SkEvalCubicAt(fPts, 1.0f/3, &loc, nullptr, nullptr);
491             canvas->drawPoint(loc, paint);
492             SkEvalCubicAt(fPts, 2.0f/3, &loc, nullptr, nullptr);
493             canvas->drawPoint(loc, paint);
494 
495        //     n = SkFindCubicInflections(fPts, t);
496        //     printf("inflections %d %g %g\n", n, t[0], t[1]);
497         }
498 
499         {
500             paint.setStyle(SkPaint::kFill_Style);
501             paint.setColor(SK_ColorRED);
502             for (SkPoint p : fPts) {
503                 canvas->drawCircle(p.fX, p.fY, 8, paint);
504             }
505         }
506     }
507 
508 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)509     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
510         const SkScalar tol = 8;
511         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
512         for (int i = 0; i < N; ++i) {
513             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
514                 return new Click([this, i](Click* c) {
515                     fPts[i] = c->fCurr;
516                     return true;
517                 });
518             }
519         }
520         return nullptr;
521     }
522 
onClick(ClickHandlerSlide::Click *)523     bool onClick(ClickHandlerSlide::Click *) override { return false; }
524 };
DEF_SLIDE(return new CubicCurveSlide;)525 DEF_SLIDE( return new CubicCurveSlide; )
526 
527 static SkPoint lerp(SkPoint a, SkPoint b, float t) {
528     return a * (1 - t) + b * t;
529 }
530 
find_max_deviation_cubic(const SkPoint src[4],SkScalar ts[2])531 static int find_max_deviation_cubic(const SkPoint src[4], SkScalar ts[2]) {
532     // deviation = F' x (d - a) == 0, solve for t(s)
533     // F = At^3 + Bt^2 + Ct + D
534     // F' = 3At^2 + 2Bt + C
535     // Z = d - a
536     // F' x Z = 3(A x Z)t^2 + 2(B x Z)t + (C x Z)
537     //
538     SkVector A = src[3] + (src[1] - src[2]) * 3 - src[0];
539     SkVector B = (src[2] - src[1] - src[1] + src[0]) * 3;
540     SkVector C = (src[1] - src[0]) * 3;
541     SkVector Z = src[3] - src[0];
542     // now forumlate the quadratic coefficients we need to solve for t : F' x Z
543     return SkFindUnitQuadRoots(3 * A.cross(Z), 2 * B.cross(Z), C.cross(Z), ts);
544 }
545 
546 class CubicCurve2Slide : public ClickHandlerSlide {
547     enum {
548         N = 7
549     };
550     SkPoint fPts[N];
551     SkPoint* fQuad = fPts + 4;
552     SkScalar fT = 0.5f;
553     bool fShowSub = false;
554     bool fShowFlatness = false;
555     bool fShowInnerQuads = false;
556     SkScalar fScale = 0.75;
557 
558 public:
CubicCurve2Slide()559     CubicCurve2Slide() {
560         fPts[0] = { 90, 300 };
561         fPts[1] = { 30, 60 };
562         fPts[2] = { 250, 30 };
563         fPts[3] = { 350, 200 };
564 
565         fQuad[0] = fPts[0] + SkVector{ 300, 0};
566         fQuad[1] = fPts[1] + SkVector{ 300, 0};
567         fQuad[2] = fPts[2] + SkVector{ 300, 0};
568 
569         fName = "CubicCurve2";
570     }
571 
onChar(SkUnichar uni)572     bool onChar(SkUnichar uni) override {
573             switch (uni) {
574                 case 's': fShowSub = !fShowSub; break;
575                 case 'f': fShowFlatness = !fShowFlatness; break;
576                 case '-': fT -= 1.0f / 32; break;
577                 case '=': fT += 1.0f / 32; break;
578                 case 'q': fShowInnerQuads = !fShowInnerQuads; break;
579                 default: return false;
580             }
581             fT = std::min(1.0f, std::max(0.0f, fT));
582             return true;
583     }
584 
Dot(SkCanvas * canvas,SkPoint p,SkScalar radius,SkColor c)585     static void Dot(SkCanvas* canvas, SkPoint p, SkScalar radius, SkColor c) {
586         SkPaint paint;
587         paint.setAntiAlias(true);
588         paint.setColor(c);
589         canvas->drawCircle(p.fX, p.fY, radius, paint);
590     }
591 
draw(SkCanvas * canvas)592     void draw(SkCanvas* canvas) override {
593         SkPaint paint;
594         paint.setAntiAlias(true);
595 
596         {
597             paint.setStyle(SkPaint::kStroke_Style);
598             SkPath path;
599             path.moveTo(fPts[0]);
600             path.cubicTo(fPts[1], fPts[2], fPts[3]);
601             path.moveTo(fQuad[0]);
602             path.quadTo(fQuad[1], fQuad[2]);
603             canvas->drawPath(path, paint);
604         }
605 
606         if (fShowSub) {
607             paint.setColor(SK_ColorRED);
608             paint.setStrokeWidth(1.7f);
609             this->showFrame(canvas, fPts, 3, paint);
610             this->showFrame(canvas, fQuad, 2, paint);
611 
612             paint.setColor(SK_ColorBLACK);
613             paint.setStyle(SkPaint::kFill_Style);
614             SkFont font(ToolUtils::DefaultTypeface(), 20);
615             canvas->drawString(SkStringPrintf("t = %g", fT), 20, 20, font, paint);
616         }
617 
618         if (fShowFlatness) {
619             this->showFlattness(canvas);
620         }
621 
622         if (fShowInnerQuads) {
623             this->showInnerQuads(canvas);
624         }
625 
626         paint.setColor(SK_ColorGRAY);
627         paint.setStroke(true);
628         canvas->drawPath(SkPathBuilder().addPolygon(fPts, 4, false).detach(), paint);
629         canvas->drawPath(SkPathBuilder().addPolygon(fQuad, 3, false).detach(), paint);
630 
631         for (SkPoint p : fPts) {
632             Dot(canvas, p, 7, SK_ColorBLACK);
633         }
634 
635         if ((false)) {
636             SkScalar ts[2];
637             int n = SkFindCubicInflections(fPts, ts);
638             for (int i = 0; i < n; ++i) {
639                 SkPoint p;
640                 SkEvalCubicAt(fPts, ts[i], &p, nullptr, nullptr);
641                 canvas->drawCircle(p.fX, p.fY, 3, paint);
642             }
643         }
644 
645     }
646 
647 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)648     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
649         const SkScalar tol = 8;
650         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
651         for (int i = 0; i < N; ++i) {
652             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
653                 return new Click([this, i](Click* c) {
654                     fPts[i] = c->fCurr;
655                     return true;
656                 });
657             }
658         }
659         return nullptr;
660     }
661 
onClick(ClickHandlerSlide::Click *)662     bool onClick(ClickHandlerSlide::Click *) override { return false; }
663 
664 private:
showFrame(SkCanvas * canvas,const SkPoint pts[],int count,const SkPaint & p)665     void showFrame(SkCanvas* canvas, const SkPoint pts[], int count, const SkPaint& p) {
666         SkPaint paint(p);
667         SkPoint storage[3 + 2 + 1];
668         SkPoint* tmp = storage;
669         const SkPoint* prev = pts;
670         for (int n = count; n > 0; --n) {
671             for (int i = 0; i < n; ++i) {
672                 canvas->drawLine(prev[i], prev[i+1], paint);
673                 tmp[i] = lerp(prev[i], prev[i+1], fT);
674             }
675             prev = tmp;
676             tmp += n;
677         }
678 
679         paint.setColor(SK_ColorBLUE);
680         paint.setStyle(SkPaint::kFill_Style);
681         int n = tmp - storage;
682         for (int i = 0; i < n; ++i) {
683             Dot(canvas, storage[i], 4, SK_ColorBLUE);
684         }
685     }
686 
showFlattness(SkCanvas * canvas)687     void showFlattness(SkCanvas* canvas) {
688         SkPaint paint;
689         paint.setStyle(SkPaint::kStroke_Style);
690         paint.setAntiAlias(true);
691 
692         SkPaint paint2(paint);
693         paint2.setColor(0xFF008800);
694 
695         paint.setColor(0xFF888888);
696         canvas->drawLine(fPts[0], fPts[3], paint);
697         canvas->drawLine(fQuad[0], fQuad[2], paint);
698 
699         paint.setColor(0xFF0000FF);
700         SkPoint pts[2];
701         pts[0] = (fQuad[0] + fQuad[1] + fQuad[1] + fQuad[2])*0.25;
702         pts[1] = (fQuad[0] + fQuad[2]) * 0.5;
703         canvas->drawLine(pts[0], pts[1], paint);
704 
705         // cubic
706 
707         SkVector v0 = (fPts[0] - fPts[1] - fPts[1] + fPts[2]) * fScale;
708         SkVector v1 = (fPts[1] - fPts[2] - fPts[2] + fPts[3]) * fScale;
709         SkVector v = (v0 + v1) * 0.5f;
710 
711         SkPoint anchor;
712         SkScalar ts[2];
713         int n = find_max_deviation_cubic(fPts, ts);
714         if (n > 0) {
715             SkEvalCubicAt(fPts, ts[0], &anchor, nullptr, nullptr);
716             canvas->drawLine(anchor, anchor + v, paint2);
717             canvas->drawLine(anchor, anchor + v0, paint);
718             if (n == 2) {
719                 SkEvalCubicAt(fPts, ts[1], &anchor, nullptr, nullptr);
720                 canvas->drawLine(anchor, anchor + v, paint2);
721             }
722             canvas->drawLine(anchor, anchor + v1, paint);
723         }
724         // not sure we can get here
725     }
726 
showInnerQuads(SkCanvas * canvas)727     void showInnerQuads(SkCanvas* canvas) {
728         auto draw_quad = [canvas](SkPoint a, SkPoint b, SkPoint c, SkColor color) {
729             SkPaint paint;
730             paint.setAntiAlias(true);
731             paint.setStroke(true);
732             paint.setColor(color);
733 
734             canvas->drawPath(SkPathBuilder().moveTo(a).quadTo(b, c).detach(), paint);
735         };
736 
737         SkPoint p0 = SkEvalQuadAt(&fPts[0], fT),
738                 p1 = SkEvalQuadAt(&fPts[1], fT),
739                 p2 = lerp(p0, p1, fT);
740 
741         draw_quad(fPts[0], fPts[1], fPts[2], SK_ColorRED);
742         Dot(canvas, p0, 4, SK_ColorRED);
743 
744         draw_quad(fPts[1], fPts[2], fPts[3], SK_ColorBLUE);
745         Dot(canvas, p1, 4, SK_ColorBLUE);
746 
747         SkPaint paint;
748         paint.setAntiAlias(true);
749         paint.setColor(0xFF008800);
750         canvas->drawLine(p0, p1, paint);
751         Dot(canvas, p2, 4, 0xFF00AA00);
752     }
753 };
754 DEF_SLIDE( return new CubicCurve2Slide; )
755 
756