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