blob: 115071b79be221264a0ea723bfb8147e8a99a559 [file] [log] [blame]
reed@google.com3bcf8d32011-10-10 15:42:36 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkColorPriv.h"
10#include "include/core/SkFont.h"
11#include "include/core/SkPaint.h"
Mike Reedd5b51a02020-07-30 21:50:30 -040012#include "include/core/SkPathBuilder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/utils/SkRandom.h"
14#include "samplecode/Sample.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/core/SkClipOpPriv.h"
Mike Reedfae46e42019-12-04 16:15:23 -050016#include "src/core/SkPathPriv.h"
Mike Reed00a97642020-01-25 18:42:23 -050017#include "tools/Resources.h"
reed@google.com3bcf8d32011-10-10 15:42:36 +000018
Ben Wagner8a1036c2016-11-09 15:00:49 -050019constexpr int W = 150;
20constexpr int H = 200;
reed@google.com3bcf8d32011-10-10 15:42:36 +000021
reed@google.com045e62d2011-10-24 12:19:46 +000022static void show_text(SkCanvas* canvas, bool doAA) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000023 SkRandom rand;
reed@google.com3bcf8d32011-10-10 15:42:36 +000024 SkPaint paint;
Hal Canary4484b8f2019-01-08 14:00:08 -050025 SkFont font(nullptr, 20);
26 font.setEdging(doAA ? SkFont::Edging::kSubpixelAntiAlias : SkFont::Edging::kAlias);
rmistry@google.comae933ce2012-08-23 18:19:56 +000027
reed@google.com045e62d2011-10-24 12:19:46 +000028 for (int i = 0; i < 200; ++i) {
reed@google.com3bcf8d32011-10-10 15:42:36 +000029 paint.setColor((SK_A32_MASK << SK_A32_SHIFT) | rand.nextU());
Hal Canary4484b8f2019-01-08 14:00:08 -050030 canvas->drawString("Hamburgefons", rand.nextSScalar1() * W, rand.nextSScalar1() * H + 20,
31 font, paint);
reed@google.com3bcf8d32011-10-10 15:42:36 +000032 }
33}
34
reed@google.com045e62d2011-10-24 12:19:46 +000035static void show_fill(SkCanvas* canvas, bool doAA) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000036 SkRandom rand;
reed@google.com3bcf8d32011-10-10 15:42:36 +000037 SkPaint paint;
reed@google.com045e62d2011-10-24 12:19:46 +000038 paint.setAntiAlias(doAA);
rmistry@google.comae933ce2012-08-23 18:19:56 +000039
reed@google.com045e62d2011-10-24 12:19:46 +000040 for (int i = 0; i < 50; ++i) {
reed@google.com3bcf8d32011-10-10 15:42:36 +000041 SkRect r;
rmistry@google.comae933ce2012-08-23 18:19:56 +000042
reed@google.com3bcf8d32011-10-10 15:42:36 +000043 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
44 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
reed@google.com3bcf8d32011-10-10 15:42:36 +000045 paint.setColor(rand.nextU());
46 canvas->drawRect(r, paint);
rmistry@google.comae933ce2012-08-23 18:19:56 +000047
reed@google.com3bcf8d32011-10-10 15:42:36 +000048 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
49 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
reed@google.com3bcf8d32011-10-10 15:42:36 +000050 paint.setColor(rand.nextU());
Mike Reedd5b51a02020-07-30 21:50:30 -040051 canvas->drawOval(r, paint);
reed@google.com3bcf8d32011-10-10 15:42:36 +000052 }
53}
54
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000055static SkScalar randRange(SkRandom& rand, SkScalar min, SkScalar max) {
reed@google.com045e62d2011-10-24 12:19:46 +000056 SkASSERT(min <= max);
Mike Reeddf85c382017-02-14 10:59:19 -050057 return min + rand.nextUScalar1() * (max - min);
reed@google.com045e62d2011-10-24 12:19:46 +000058}
59
60static void show_stroke(SkCanvas* canvas, bool doAA, SkScalar strokeWidth, int n) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000061 SkRandom rand;
reed@google.com045e62d2011-10-24 12:19:46 +000062 SkPaint paint;
63 paint.setAntiAlias(doAA);
64 paint.setStyle(SkPaint::kStroke_Style);
65 paint.setStrokeWidth(strokeWidth);
rmistry@google.comae933ce2012-08-23 18:19:56 +000066
reed@google.com045e62d2011-10-24 12:19:46 +000067 for (int i = 0; i < n; ++i) {
68 SkRect r;
rmistry@google.comae933ce2012-08-23 18:19:56 +000069
reed@google.com045e62d2011-10-24 12:19:46 +000070 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
71 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
72 paint.setColor(rand.nextU());
73 canvas->drawRect(r, paint);
rmistry@google.comae933ce2012-08-23 18:19:56 +000074
reed@google.com045e62d2011-10-24 12:19:46 +000075 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
76 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
77 paint.setColor(rand.nextU());
Mike Reedd5b51a02020-07-30 21:50:30 -040078 canvas->drawOval(r, paint);
reed@google.com045e62d2011-10-24 12:19:46 +000079
80 const SkScalar minx = -SkIntToScalar(W)/4;
81 const SkScalar maxx = 5*SkIntToScalar(W)/4;
82 const SkScalar miny = -SkIntToScalar(H)/4;
83 const SkScalar maxy = 5*SkIntToScalar(H)/4;
84 paint.setColor(rand.nextU());
85 canvas->drawLine(randRange(rand, minx, maxx), randRange(rand, miny, maxy),
86 randRange(rand, minx, maxx), randRange(rand, miny, maxy),
87 paint);
88 }
89}
90
91static void show_hair(SkCanvas* canvas, bool doAA) {
92 show_stroke(canvas, doAA, 0, 150);
93}
94
95static void show_thick(SkCanvas* canvas, bool doAA) {
96 show_stroke(canvas, doAA, SkIntToScalar(5), 50);
97}
98
99typedef void (*CanvasProc)(SkCanvas*, bool);
100
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400101class ClipView : public Sample {
Hal Canaryd7639af2019-07-17 09:08:11 -0400102 SkString name() override { return SkString("Clip"); }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000103
Hal Canaryd7639af2019-07-17 09:08:11 -0400104 void onDrawContent(SkCanvas* canvas) override {
reed@google.com045e62d2011-10-24 12:19:46 +0000105 canvas->drawColor(SK_ColorWHITE);
reed@google.com3bcf8d32011-10-10 15:42:36 +0000106 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
107
108 static const CanvasProc gProc[] = {
reed@google.com045e62d2011-10-24 12:19:46 +0000109 show_text, show_thick, show_hair, show_fill
reed@google.com3bcf8d32011-10-10 15:42:36 +0000110 };
rmistry@google.comae933ce2012-08-23 18:19:56 +0000111
reed@google.com3bcf8d32011-10-10 15:42:36 +0000112 SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
reed@google.com3bcf8d32011-10-10 15:42:36 +0000113 r.inset(SK_Scalar1 / 4, SK_Scalar1 / 4);
Mike Reedd5b51a02020-07-30 21:50:30 -0400114 SkPath clipPath = SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, 20, 20)).detach();
reed@google.com045e62d2011-10-24 12:19:46 +0000115
116// clipPath.toggleInverseFillType();
reed@google.com3bcf8d32011-10-10 15:42:36 +0000117
118 for (int aa = 0; aa <= 1; ++aa) {
119 canvas->save();
120 for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); ++i) {
121 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500122 canvas->clipPath(clipPath, kIntersect_SkClipOp, SkToBool(aa));
reed@google.com045e62d2011-10-24 12:19:46 +0000123// canvas->drawColor(SK_ColorWHITE);
124 gProc[i](canvas, SkToBool(aa));
reed@google.com3bcf8d32011-10-10 15:42:36 +0000125 canvas->restore();
126 canvas->translate(W * SK_Scalar1 * 8 / 7, 0);
127 }
128 canvas->restore();
129 canvas->translate(0, H * SK_Scalar1 * 8 / 7);
130 }
131 }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000132};
133
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400134DEF_SAMPLE( return new ClipView(); )
Mike Reed63edc572019-11-15 21:58:01 -0500135
136///////////////////////////////////////////////////////////////////////////////
137
138struct SkHalfPlane {
139 SkScalar fA, fB, fC;
140
Mike Reed6344c292019-12-04 12:45:39 -0500141 SkScalar eval(SkScalar x, SkScalar y) const {
Mike Reed63edc572019-11-15 21:58:01 -0500142 return fA * x + fB * y + fC;
143 }
Mike Reed6344c292019-12-04 12:45:39 -0500144 SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
Mike Reed63edc572019-11-15 21:58:01 -0500145
146 bool twoPts(SkPoint pts[2]) const {
Jim Van Verthc3363152019-12-03 12:05:14 -0500147 // normalize plane to help with the perpendicular step, below
148 SkScalar len = SkScalarSqrt(fA*fA + fB*fB);
149 if (!len) {
150 return false;
151 }
152 SkScalar denom = SkScalarInvert(len);
153 SkScalar a = fA * denom;
154 SkScalar b = fB * denom;
155 SkScalar c = fC * denom;
156
157 // We compute p0 on the half-plane by setting one of the components to 0
158 // We compute p1 by stepping from p0 along a perpendicular to the normal
159 if (b) {
160 pts[0] = { 0, -c / b };
161 pts[1] = { b, pts[0].fY - a};
162 } else if (a) {
163 pts[0] = { -c / a, 0 };
164 pts[1] = { pts[0].fX + b, -a };
Mike Reed63edc572019-11-15 21:58:01 -0500165 } else {
166 return false;
167 }
Jim Van Verthc3363152019-12-03 12:05:14 -0500168
169 SkASSERT(SkScalarNearlyZero(this->operator()(pts[0].fX, pts[0].fY)));
170 SkASSERT(SkScalarNearlyZero(this->operator()(pts[1].fX, pts[1].fY)));
Mike Reed63edc572019-11-15 21:58:01 -0500171 return true;
172 }
Mike Reed6344c292019-12-04 12:45:39 -0500173
174 enum Result {
175 kAllNegative,
176 kAllPositive,
177 kMixed
178 };
179 Result test(const SkRect& bounds) const {
180 SkPoint diagMin, diagMax;
181 if (fA >= 0) {
182 diagMin.fX = bounds.fLeft;
183 diagMax.fX = bounds.fRight;
184 } else {
185 diagMin.fX = bounds.fRight;
186 diagMax.fX = bounds.fLeft;
187 }
188 if (fB >= 0) {
189 diagMin.fY = bounds.fTop;
190 diagMax.fY = bounds.fBottom;
191 } else {
192 diagMin.fY = bounds.fBottom;
193 diagMax.fY = bounds.fTop;
194 }
195 SkScalar test = this->eval(diagMin.fX, diagMin.fY);
196 SkScalar sign = test*this->eval(diagMax.fX, diagMin.fY);
197 if (sign > 0) {
198 // the path is either all on one side of the half-plane or the other
199 if (test < 0) {
200 return kAllNegative;
201 } else {
202 return kAllPositive;
203 }
204 }
205 return kMixed;
206 }
Mike Reed63edc572019-11-15 21:58:01 -0500207};
208
209#include "src/core/SkEdgeClipper.h"
210
Mike Reedd5b51a02020-07-30 21:50:30 -0400211static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
Mike Reed63edc572019-11-15 21:58:01 -0500212 SkMatrix mx, inv;
213 SkVector v = p1 - p0;
214 mx.setAll(v.fX, -v.fY, p0.fX,
215 v.fY, v.fX, p0.fY,
216 0, 0, 1);
217 SkAssertResult(mx.invert(&inv));
218
219 SkPath rotated;
220 path.transform(inv, &rotated);
221
222 SkScalar big = 1e28f;
223 SkRect clip = {-big, 0, big, big };
224
225 struct Rec {
Mike Reedd5b51a02020-07-30 21:50:30 -0400226 SkPathBuilder fResult;
227 SkPoint fPrev = {0, 0};
228 } rec;
Mike Reed63edc572019-11-15 21:58:01 -0500229
Mike Klein71f12662020-10-28 09:07:09 -0500230 SkEdgeClipper::ClipPath(rotated, clip, false,
Mike Reed63edc572019-11-15 21:58:01 -0500231 [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
232 Rec* rec = (Rec*)ctx;
233
234 bool addLineTo = false;
235 SkPoint pts[4];
236 SkPath::Verb verb;
237 while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
238 if (newCtr) {
Mike Reedd5b51a02020-07-30 21:50:30 -0400239 rec->fResult.moveTo(pts[0]);
Mike Reed63edc572019-11-15 21:58:01 -0500240 rec->fPrev = pts[0];
241 newCtr = false;
242 }
243
244 if (addLineTo || pts[0] != rec->fPrev) {
Mike Reedd5b51a02020-07-30 21:50:30 -0400245 rec->fResult.lineTo(pts[0]);
Mike Reed63edc572019-11-15 21:58:01 -0500246 }
247
248 switch (verb) {
249 case SkPath::kLine_Verb:
Mike Reedd5b51a02020-07-30 21:50:30 -0400250 rec->fResult.lineTo(pts[1]);
Mike Reed63edc572019-11-15 21:58:01 -0500251 rec->fPrev = pts[1];
252 break;
253 case SkPath::kQuad_Verb:
Mike Reedd5b51a02020-07-30 21:50:30 -0400254 rec->fResult.quadTo(pts[1], pts[2]);
Mike Reed63edc572019-11-15 21:58:01 -0500255 rec->fPrev = pts[2];
256 break;
257 case SkPath::kCubic_Verb:
Mike Reedd5b51a02020-07-30 21:50:30 -0400258 rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
Mike Reed63edc572019-11-15 21:58:01 -0500259 rec->fPrev = pts[3];
260 break;
261 default: break;
262 }
263 addLineTo = true;
264 }
265 }, &rec);
266
Mike Reedd5b51a02020-07-30 21:50:30 -0400267 return rec.fResult.detach().makeTransform(mx);
Mike Reed63edc572019-11-15 21:58:01 -0500268}
269
Mike Reed63edc572019-11-15 21:58:01 -0500270static void draw_halfplane(SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
271 SkVector v = p1 - p0;
272 p0 = p0 - v * 1000;
273 p1 = p1 + v * 1000;
274
275 SkPaint paint;
276 paint.setColor(c);
277 canvas->drawLine(p0, p1, paint);
278}
279
280static SkPath make_path() {
281 SkRandom rand;
Mike Reed0483b462019-12-05 17:29:45 -0500282 auto rand_pt = [&rand]() {
283 auto x = rand.nextF();
284 auto y = rand.nextF();
285 return SkPoint{x * 400, y * 400};
286 };
Mike Reed63edc572019-11-15 21:58:01 -0500287
Mike Reedd5b51a02020-07-30 21:50:30 -0400288 SkPathBuilder path;
Mike Reed63edc572019-11-15 21:58:01 -0500289 for (int i = 0; i < 4; ++i) {
Mike Reed0483b462019-12-05 17:29:45 -0500290 SkPoint pts[6];
291 for (auto& p : pts) {
292 p = rand_pt();
293 }
294 path.moveTo(pts[0]).quadTo(pts[1], pts[2]).quadTo(pts[3], pts[4]).lineTo(pts[5]);
Mike Reed63edc572019-11-15 21:58:01 -0500295 }
Mike Reedd5b51a02020-07-30 21:50:30 -0400296 return path.detach();
Mike Reed63edc572019-11-15 21:58:01 -0500297}
298
299class HalfPlaneView : public Sample {
300 SkPoint fPts[2];
301 SkPath fPath;
302
303 SkString name() override { return SkString("halfplane"); }
304
305 void onOnceBeforeDraw() override {
306 fPts[0] = {0, 0};
307 fPts[1] = {3, 2};
308 fPath = make_path();
309 }
310
311 void onDrawContent(SkCanvas* canvas) override {
312 SkPaint paint;
313
314 paint.setColor({0.5f, 0.5f, 0.5f, 1.0f}, nullptr);
315 canvas->drawPath(fPath, paint);
316
317 paint.setColor({0, 0, 0, 1}, nullptr);
Mike Reed6344c292019-12-04 12:45:39 -0500318
Mike Reedd5b51a02020-07-30 21:50:30 -0400319 canvas->drawPath(clip(fPath, fPts[0], fPts[1]), paint);
Mike Reed63edc572019-11-15 21:58:01 -0500320
321 draw_halfplane(canvas, fPts[0], fPts[1], SK_ColorRED);
322 }
323
324 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
325 return new Click;
326 }
327
328 bool onClick(Click* click) override {
329 fPts[0] = click->fCurr;
330 fPts[1] = fPts[0] + SkPoint{3, 2};
331 return true;
332 }
333};
334DEF_SAMPLE( return new HalfPlaneView(); )
335
336static void draw_halfplane(SkCanvas* canvas, const SkHalfPlane& p, SkColor c) {
337 SkPoint pts[2];
338 p.twoPts(pts);
339 draw_halfplane(canvas, pts[0], pts[1], c);
340}
341
342static void compute_half_planes(const SkMatrix& mx, SkScalar W, SkScalar H,
343 SkHalfPlane planes[4]) {
344 SkScalar a = mx[0], b = mx[1], c = mx[2],
345 d = mx[3], e = mx[4], f = mx[5],
346 g = mx[6], h = mx[7], i = mx[8];
347
348 planes[0] = { 2*g - 2*a/W, 2*h - 2*b/W, 2*i - 2*c/W };
349 planes[1] = { 2*a/W, 2*b/W, 2*c/W };
350 planes[2] = { 2*g - 2*d/H, 2*h - 2*e/H, 2*i - 2*f/H };
351 planes[3] = { 2*d/H, 2*e/H, 2*f/H };
352}
353
354class HalfPlaneView2 : public Sample {
355 SkPoint fPts[4];
356 SkPath fPath;
357
358 SkString name() override { return SkString("halfplane2"); }
359
360 void onOnceBeforeDraw() override {
361 fPath = make_path();
362 SkRect r = fPath.getBounds();
363 r.toQuad(fPts);
364 }
365
366 void onDrawContent(SkCanvas* canvas) override {
367 SkMatrix mx;
368 {
369 SkRect r = fPath.getBounds();
370 SkPoint src[4];
371 r.toQuad(src);
372 mx.setPolyToPoly(src, fPts, 4);
373 }
374
375 SkPaint paint;
376 canvas->drawPath(fPath, paint);
377
378 canvas->save();
379 canvas->concat(mx);
380 paint.setColor(0x40FF0000);
381 canvas->drawPath(fPath, paint);
382 canvas->restore();
383
384 // draw the frame
385 paint.setStrokeWidth(10);
386 paint.setColor(SK_ColorGREEN);
387 canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPts, paint);
388
389 // draw the half-planes
390 SkHalfPlane planes[4];
391 compute_half_planes(mx, 400, 400, planes);
392 for (auto& p : planes) {
393 draw_halfplane(canvas, p, SK_ColorRED);
394 }
395 }
396
397 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
398 SkScalar r = 8;
399 SkRect rect = SkRect::MakeXYWH(x - r, y - r, 2*r, 2*r);
400 for (int i = 0; i < 4; ++i) {
401 if (rect.contains(fPts[i].fX, fPts[i].fY)) {
402 Click* c = new Click;
403 c->fMeta.setS32("index", i);
404 return c;
405 }
406 }
407 return nullptr;
408 }
409
410 bool onClick(Click* click) override {
411 int32_t index;
412 SkAssertResult(click->fMeta.findS32("index", &index));
413 SkASSERT(index >= 0 && index < 4);
414 fPts[index] = click->fCurr;
415 return true;
416 }
417};
418DEF_SAMPLE( return new HalfPlaneView2(); )
419
Mike Reed00a97642020-01-25 18:42:23 -0500420static SkM44 inv(const SkM44& m) {
421 SkM44 inverse;
Mike Reed63edc572019-11-15 21:58:01 -0500422 SkAssertResult(m.invert(&inverse));
423 return inverse;
424}
425
Mike Reed6344c292019-12-04 12:45:39 -0500426static SkHalfPlane half_plane_w0(const SkMatrix& m) {
427 return { m[SkMatrix::kMPersp0], m[SkMatrix::kMPersp1], m[SkMatrix::kMPersp2] - 0.05f };
Jim Van Verthc3363152019-12-03 12:05:14 -0500428}
429
Mike Reedcbeabd92019-12-16 17:13:58 -0500430class SampleCameraView : public Sample {
Mike Reed63edc572019-11-15 21:58:01 -0500431 float fNear = 0.05f;
432 float fFar = 4;
433 float fAngle = SK_ScalarPI / 4;
434
Mike Reed00a97642020-01-25 18:42:23 -0500435 SkV3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
436 SkV3 fCOA { 0, 0, 0 };
437 SkV3 fUp { 0, 1, 0 };
Mike Reed63edc572019-11-15 21:58:01 -0500438
Mike Reed00a97642020-01-25 18:42:23 -0500439 SkM44 fRot;
440 SkV3 fTrans;
Mike Reed63edc572019-11-15 21:58:01 -0500441
Mike Reed63edc572019-11-15 21:58:01 -0500442 void rotate(float x, float y, float z) {
Mike Reed00a97642020-01-25 18:42:23 -0500443 SkM44 r;
Mike Reed63edc572019-11-15 21:58:01 -0500444 if (x) {
Mike Reed00a97642020-01-25 18:42:23 -0500445 r.setRotateUnit({1, 0, 0}, x);
Mike Reed63edc572019-11-15 21:58:01 -0500446 } else if (y) {
Mike Reed00a97642020-01-25 18:42:23 -0500447 r.setRotateUnit({0, 1, 0}, y);
Mike Reed63edc572019-11-15 21:58:01 -0500448 } else {
Mike Reed00a97642020-01-25 18:42:23 -0500449 r.setRotateUnit({0, 0, 1}, z);
Mike Reed63edc572019-11-15 21:58:01 -0500450 }
Mike Reed00a97642020-01-25 18:42:23 -0500451 fRot = r * fRot;
Mike Reed63edc572019-11-15 21:58:01 -0500452 }
453
Mike Reedcbeabd92019-12-16 17:13:58 -0500454public:
Mike Reed00a97642020-01-25 18:42:23 -0500455 SkM44 get44(const SkRect& r) const {
Mike Reedcbeabd92019-12-16 17:13:58 -0500456 SkScalar w = r.width();
457 SkScalar h = r.height();
458
Mike Reed00a97642020-01-25 18:42:23 -0500459 SkM44 camera = Sk3LookAt(fEye, fCOA, fUp),
460 perspective = Sk3Perspective(fNear, fFar, fAngle),
461 translate = SkM44::Translate(fTrans.x, fTrans.y, fTrans.z),
462 viewport = SkM44::Translate(r.centerX(), r.centerY(), 0) *
463 SkM44::Scale(w*0.5f, h*0.5f, 1);
Mike Reed63edc572019-11-15 21:58:01 -0500464
Jim Van Verthc3363152019-12-03 12:05:14 -0500465 return viewport * perspective * camera * translate * fRot * inv(viewport);
Mike Reed63edc572019-11-15 21:58:01 -0500466 }
467
Mike Reedcbeabd92019-12-16 17:13:58 -0500468 bool onChar(SkUnichar uni) override {
469 float delta = SK_ScalarPI / 30;
470 switch (uni) {
471 case '8': this->rotate( delta, 0, 0); return true;
472 case '2': this->rotate(-delta, 0, 0); return true;
473 case '4': this->rotate(0, delta, 0); return true;
474 case '6': this->rotate(0, -delta, 0); return true;
475 case '-': this->rotate(0, 0, delta); return true;
476 case '+': this->rotate(0, 0, -delta); return true;
477
Mike Reed00a97642020-01-25 18:42:23 -0500478 case 'i': fTrans.z += 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
479 case 'k': fTrans.z -= 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
Mike Reedcbeabd92019-12-16 17:13:58 -0500480
481 case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
482 case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
483 case 'f': fFar += 0.1f; SkDebugf("far %g\n", fFar); return true;
484 case 'F': fFar -= 0.1f; SkDebugf("far %g\n", fFar); return true;
485 default: break;
486 }
487 return false;
488 }
489};
490
491class HalfPlaneView3 : public SampleCameraView {
492 SkPath fPath;
493 sk_sp<SkShader> fShader;
494 bool fShowUnclipped = false;
495
496 SkString name() override { return SkString("halfplane3"); }
497
498 void onOnceBeforeDraw() override {
499 fPath = make_path();
500 fShader = GetResourceAsImage("images/mandrill_128.png")
Mike Reed99c94462020-12-08 13:16:56 -0500501 ->makeShader(SkSamplingOptions(), SkMatrix::Scale(3, 3));
Mike Reedcbeabd92019-12-16 17:13:58 -0500502 }
503
504 bool onChar(SkUnichar uni) override {
505 switch (uni) {
506 case 'u': fShowUnclipped = !fShowUnclipped; return true;
507 default: break;
508 }
509 return this->SampleCameraView::onChar(uni);
510 }
511
Mike Reed63edc572019-11-15 21:58:01 -0500512 void onDrawContent(SkCanvas* canvas) override {
Mike Reed00a97642020-01-25 18:42:23 -0500513 SkM44 mx = this->get44({0, 0, 400, 400});
Mike Reed63edc572019-11-15 21:58:01 -0500514
515 SkPaint paint;
516 paint.setColor({0.75, 0.75, 0.75, 1});
517 canvas->drawPath(fPath, paint);
518
519 paint.setShader(fShader);
520
Mike Reed6344c292019-12-04 12:45:39 -0500521 if (fShowUnclipped) {
522 canvas->save();
Mike Reed3ef77dd2020-04-06 10:41:09 -0400523 canvas->concat(mx);
Mike Reed6344c292019-12-04 12:45:39 -0500524 paint.setAlphaf(0.33f);
525 canvas->drawPath(fPath, paint);
526 paint.setAlphaf(1.f);
527 canvas->restore();
528 }
529
Mike Reed6344c292019-12-04 12:45:39 -0500530
531 SkColor planeColor = SK_ColorBLUE;
532 SkPath clippedPath, *path = &fPath;
Mike Reed00a97642020-01-25 18:42:23 -0500533 if (SkPathPriv::PerspectiveClip(fPath, mx.asM33(), &clippedPath)) {
Mike Reed6344c292019-12-04 12:45:39 -0500534 path = &clippedPath;
535 planeColor = SK_ColorRED;
536 }
Mike Reed63edc572019-11-15 21:58:01 -0500537 canvas->save();
Mike Reed3ef77dd2020-04-06 10:41:09 -0400538 canvas->concat(mx);
Mike Reed6344c292019-12-04 12:45:39 -0500539 canvas->drawPath(*path, paint);
Mike Reed63edc572019-11-15 21:58:01 -0500540 canvas->restore();
541
Mike Reed00a97642020-01-25 18:42:23 -0500542 SkHalfPlane hpw = half_plane_w0(mx.asM33());
Mike Reed6344c292019-12-04 12:45:39 -0500543 draw_halfplane(canvas, hpw, planeColor);
Mike Reed63edc572019-11-15 21:58:01 -0500544 }
Mike Reed63edc572019-11-15 21:58:01 -0500545};
546DEF_SAMPLE( return new HalfPlaneView3(); )
Mike Reedcbeabd92019-12-16 17:13:58 -0500547
548class HalfPlaneCoons : public SampleCameraView {
549 SkPoint fPatch[12];
Mike Reed190b82d2019-12-17 17:25:27 -0500550 SkColor fColors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
551 SkPoint fTex[4] = {{0, 0}, {256, 0}, {256, 256}, {0, 256}};
552 sk_sp<SkShader> fShader;
553
554 bool fShowHandles = false;
555 bool fShowSkeleton = false;
556 bool fShowTex = false;
Mike Reedcbeabd92019-12-16 17:13:58 -0500557
558 SkString name() override { return SkString("halfplane-coons"); }
559
560 void onOnceBeforeDraw() override {
561 fPatch[0] = { 0, 0 };
562 fPatch[1] = { 100, 0 };
563 fPatch[2] = { 200, 0 };
564 fPatch[3] = { 300, 0 };
565 fPatch[4] = { 300, 100 };
566 fPatch[5] = { 300, 200 };
567 fPatch[6] = { 300, 300 };
568 fPatch[7] = { 200, 300 };
569 fPatch[8] = { 100, 300 };
570 fPatch[9] = { 0, 300 };
571 fPatch[10] = { 0, 200 };
572 fPatch[11] = { 0, 100 };
Mike Reed190b82d2019-12-17 17:25:27 -0500573
Mike Reed99c94462020-12-08 13:16:56 -0500574 fShader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
Mike Reedcbeabd92019-12-16 17:13:58 -0500575 }
576
577 void onDrawContent(SkCanvas* canvas) override {
Mike Reedcbeabd92019-12-16 17:13:58 -0500578 SkPaint paint;
579
580 canvas->save();
Mike Reed3ef77dd2020-04-06 10:41:09 -0400581 canvas->concat(this->get44({0, 0, 300, 300}));
Mike Reed190b82d2019-12-17 17:25:27 -0500582
583 const SkPoint* tex = nullptr;
584 const SkColor* col = nullptr;
585 if (!fShowSkeleton) {
586 if (fShowTex) {
587 paint.setShader(fShader);
588 tex = fTex;
589 } else {
590 col = fColors;
591 }
592 }
593 canvas->drawPatch(fPatch, col, tex, SkBlendMode::kSrc, paint);
594 paint.setShader(nullptr);
595
596 if (fShowHandles) {
597 paint.setAntiAlias(true);
598 paint.setStrokeCap(SkPaint::kRound_Cap);
599 paint.setStrokeWidth(8);
600 canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
601 paint.setColor(SK_ColorWHITE);
602 paint.setStrokeWidth(6);
603 canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
604 }
605
Mike Reedcbeabd92019-12-16 17:13:58 -0500606 canvas->restore();
607 }
Mike Reed190b82d2019-12-17 17:25:27 -0500608
609 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
610 auto dist = [](SkPoint a, SkPoint b) { return (b - a).length(); };
611
612 const float tol = 15;
613 for (int i = 0; i < 12; ++i) {
614 if (dist({x,y}, fPatch[i]) <= tol) {
Mike Reed0ce3e882020-04-22 11:09:30 -0400615 return new Click([this, i](Click* c) {
616 fPatch[i] = c->fCurr;
617 return true;
618 });
Mike Reed190b82d2019-12-17 17:25:27 -0500619 }
620 }
621 return nullptr;
622 }
623
Mike Reed190b82d2019-12-17 17:25:27 -0500624 bool onChar(SkUnichar uni) override {
625 switch (uni) {
626 case 'h': fShowHandles = !fShowHandles; return true;
Mike Reedc8803462019-12-18 13:34:15 -0500627 case 'k': fShowSkeleton = !fShowSkeleton; return true;
Mike Reed190b82d2019-12-17 17:25:27 -0500628 case 't': fShowTex = !fShowTex; return true;
629 default: break;
630 }
631 return this->SampleCameraView::onChar(uni);
632 }
633
Mike Reedcbeabd92019-12-16 17:13:58 -0500634};
635DEF_SAMPLE( return new HalfPlaneCoons(); )