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