blob: 358e3f805e9182a770dedb65c55f2cfd6d880e3a [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 Reedfae46e42019-12-04 16:15:23 -050015#include "src/core/SkPathPriv.h"
Mike Reed00a97642020-01-25 18:42:23 -050016#include "tools/Resources.h"
reed@google.com3bcf8d32011-10-10 15:42:36 +000017
Ben Wagner8a1036c2016-11-09 15:00:49 -050018constexpr int W = 150;
19constexpr int H = 200;
reed@google.com3bcf8d32011-10-10 15:42:36 +000020
reed@google.com045e62d2011-10-24 12:19:46 +000021static void show_text(SkCanvas* canvas, bool doAA) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000022 SkRandom rand;
reed@google.com3bcf8d32011-10-10 15:42:36 +000023 SkPaint paint;
Hal Canary4484b8f2019-01-08 14:00:08 -050024 SkFont font(nullptr, 20);
25 font.setEdging(doAA ? SkFont::Edging::kSubpixelAntiAlias : SkFont::Edging::kAlias);
rmistry@google.comae933ce2012-08-23 18:19:56 +000026
reed@google.com045e62d2011-10-24 12:19:46 +000027 for (int i = 0; i < 200; ++i) {
reed@google.com3bcf8d32011-10-10 15:42:36 +000028 paint.setColor((SK_A32_MASK << SK_A32_SHIFT) | rand.nextU());
Hal Canary4484b8f2019-01-08 14:00:08 -050029 canvas->drawString("Hamburgefons", rand.nextSScalar1() * W, rand.nextSScalar1() * H + 20,
30 font, paint);
reed@google.com3bcf8d32011-10-10 15:42:36 +000031 }
32}
33
reed@google.com045e62d2011-10-24 12:19:46 +000034static void show_fill(SkCanvas* canvas, bool doAA) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000035 SkRandom rand;
reed@google.com3bcf8d32011-10-10 15:42:36 +000036 SkPaint paint;
reed@google.com045e62d2011-10-24 12:19:46 +000037 paint.setAntiAlias(doAA);
rmistry@google.comae933ce2012-08-23 18:19:56 +000038
reed@google.com045e62d2011-10-24 12:19:46 +000039 for (int i = 0; i < 50; ++i) {
reed@google.com3bcf8d32011-10-10 15:42:36 +000040 SkRect r;
rmistry@google.comae933ce2012-08-23 18:19:56 +000041
reed@google.com3bcf8d32011-10-10 15:42:36 +000042 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
43 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
reed@google.com3bcf8d32011-10-10 15:42:36 +000044 paint.setColor(rand.nextU());
45 canvas->drawRect(r, paint);
rmistry@google.comae933ce2012-08-23 18:19:56 +000046
reed@google.com3bcf8d32011-10-10 15:42:36 +000047 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
48 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
reed@google.com3bcf8d32011-10-10 15:42:36 +000049 paint.setColor(rand.nextU());
Mike Reedd5b51a02020-07-30 21:50:30 -040050 canvas->drawOval(r, paint);
reed@google.com3bcf8d32011-10-10 15:42:36 +000051 }
52}
53
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000054static SkScalar randRange(SkRandom& rand, SkScalar min, SkScalar max) {
reed@google.com045e62d2011-10-24 12:19:46 +000055 SkASSERT(min <= max);
Mike Reeddf85c382017-02-14 10:59:19 -050056 return min + rand.nextUScalar1() * (max - min);
reed@google.com045e62d2011-10-24 12:19:46 +000057}
58
59static void show_stroke(SkCanvas* canvas, bool doAA, SkScalar strokeWidth, int n) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000060 SkRandom rand;
reed@google.com045e62d2011-10-24 12:19:46 +000061 SkPaint paint;
62 paint.setAntiAlias(doAA);
63 paint.setStyle(SkPaint::kStroke_Style);
64 paint.setStrokeWidth(strokeWidth);
rmistry@google.comae933ce2012-08-23 18:19:56 +000065
reed@google.com045e62d2011-10-24 12:19:46 +000066 for (int i = 0; i < n; ++i) {
67 SkRect r;
rmistry@google.comae933ce2012-08-23 18:19:56 +000068
reed@google.com045e62d2011-10-24 12:19:46 +000069 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
70 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
71 paint.setColor(rand.nextU());
72 canvas->drawRect(r, paint);
rmistry@google.comae933ce2012-08-23 18:19:56 +000073
reed@google.com045e62d2011-10-24 12:19:46 +000074 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
75 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
76 paint.setColor(rand.nextU());
Mike Reedd5b51a02020-07-30 21:50:30 -040077 canvas->drawOval(r, paint);
reed@google.com045e62d2011-10-24 12:19:46 +000078
79 const SkScalar minx = -SkIntToScalar(W)/4;
80 const SkScalar maxx = 5*SkIntToScalar(W)/4;
81 const SkScalar miny = -SkIntToScalar(H)/4;
82 const SkScalar maxy = 5*SkIntToScalar(H)/4;
83 paint.setColor(rand.nextU());
84 canvas->drawLine(randRange(rand, minx, maxx), randRange(rand, miny, maxy),
85 randRange(rand, minx, maxx), randRange(rand, miny, maxy),
86 paint);
87 }
88}
89
90static void show_hair(SkCanvas* canvas, bool doAA) {
91 show_stroke(canvas, doAA, 0, 150);
92}
93
94static void show_thick(SkCanvas* canvas, bool doAA) {
95 show_stroke(canvas, doAA, SkIntToScalar(5), 50);
96}
97
98typedef void (*CanvasProc)(SkCanvas*, bool);
99
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400100class ClipView : public Sample {
Hal Canaryd7639af2019-07-17 09:08:11 -0400101 SkString name() override { return SkString("Clip"); }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000102
Hal Canaryd7639af2019-07-17 09:08:11 -0400103 void onDrawContent(SkCanvas* canvas) override {
reed@google.com045e62d2011-10-24 12:19:46 +0000104 canvas->drawColor(SK_ColorWHITE);
reed@google.com3bcf8d32011-10-10 15:42:36 +0000105 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
106
107 static const CanvasProc gProc[] = {
reed@google.com045e62d2011-10-24 12:19:46 +0000108 show_text, show_thick, show_hair, show_fill
reed@google.com3bcf8d32011-10-10 15:42:36 +0000109 };
rmistry@google.comae933ce2012-08-23 18:19:56 +0000110
reed@google.com3bcf8d32011-10-10 15:42:36 +0000111 SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
reed@google.com3bcf8d32011-10-10 15:42:36 +0000112 r.inset(SK_Scalar1 / 4, SK_Scalar1 / 4);
Mike Reedd5b51a02020-07-30 21:50:30 -0400113 SkPath clipPath = SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, 20, 20)).detach();
reed@google.com045e62d2011-10-24 12:19:46 +0000114
115// clipPath.toggleInverseFillType();
reed@google.com3bcf8d32011-10-10 15:42:36 +0000116
117 for (int aa = 0; aa <= 1; ++aa) {
118 canvas->save();
119 for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); ++i) {
120 canvas->save();
Michael Ludwig2f6e2f82021-08-03 13:08:50 -0400121 canvas->clipPath(clipPath, SkClipOp::kIntersect, SkToBool(aa));
reed@google.com045e62d2011-10-24 12:19:46 +0000122// canvas->drawColor(SK_ColorWHITE);
123 gProc[i](canvas, SkToBool(aa));
reed@google.com3bcf8d32011-10-10 15:42:36 +0000124 canvas->restore();
125 canvas->translate(W * SK_Scalar1 * 8 / 7, 0);
126 }
127 canvas->restore();
128 canvas->translate(0, H * SK_Scalar1 * 8 / 7);
129 }
130 }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000131};
132
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400133DEF_SAMPLE( return new ClipView(); )
Mike Reed63edc572019-11-15 21:58:01 -0500134
135///////////////////////////////////////////////////////////////////////////////
136
137struct SkHalfPlane {
138 SkScalar fA, fB, fC;
139
Mike Reed6344c292019-12-04 12:45:39 -0500140 SkScalar eval(SkScalar x, SkScalar y) const {
Mike Reed63edc572019-11-15 21:58:01 -0500141 return fA * x + fB * y + fC;
142 }
Mike Reed6344c292019-12-04 12:45:39 -0500143 SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
Mike Reed63edc572019-11-15 21:58:01 -0500144
145 bool twoPts(SkPoint pts[2]) const {
Jim Van Verthc3363152019-12-03 12:05:14 -0500146 // normalize plane to help with the perpendicular step, below
147 SkScalar len = SkScalarSqrt(fA*fA + fB*fB);
148 if (!len) {
149 return false;
150 }
151 SkScalar denom = SkScalarInvert(len);
152 SkScalar a = fA * denom;
153 SkScalar b = fB * denom;
154 SkScalar c = fC * denom;
155
156 // We compute p0 on the half-plane by setting one of the components to 0
157 // We compute p1 by stepping from p0 along a perpendicular to the normal
158 if (b) {
159 pts[0] = { 0, -c / b };
160 pts[1] = { b, pts[0].fY - a};
161 } else if (a) {
162 pts[0] = { -c / a, 0 };
163 pts[1] = { pts[0].fX + b, -a };
Mike Reed63edc572019-11-15 21:58:01 -0500164 } else {
165 return false;
166 }
Jim Van Verthc3363152019-12-03 12:05:14 -0500167
168 SkASSERT(SkScalarNearlyZero(this->operator()(pts[0].fX, pts[0].fY)));
169 SkASSERT(SkScalarNearlyZero(this->operator()(pts[1].fX, pts[1].fY)));
Mike Reed63edc572019-11-15 21:58:01 -0500170 return true;
171 }
Mike Reed6344c292019-12-04 12:45:39 -0500172
173 enum Result {
174 kAllNegative,
175 kAllPositive,
176 kMixed
177 };
178 Result test(const SkRect& bounds) const {
179 SkPoint diagMin, diagMax;
180 if (fA >= 0) {
181 diagMin.fX = bounds.fLeft;
182 diagMax.fX = bounds.fRight;
183 } else {
184 diagMin.fX = bounds.fRight;
185 diagMax.fX = bounds.fLeft;
186 }
187 if (fB >= 0) {
188 diagMin.fY = bounds.fTop;
189 diagMax.fY = bounds.fBottom;
190 } else {
191 diagMin.fY = bounds.fBottom;
192 diagMax.fY = bounds.fTop;
193 }
194 SkScalar test = this->eval(diagMin.fX, diagMin.fY);
195 SkScalar sign = test*this->eval(diagMax.fX, diagMin.fY);
196 if (sign > 0) {
197 // the path is either all on one side of the half-plane or the other
198 if (test < 0) {
199 return kAllNegative;
200 } else {
201 return kAllPositive;
202 }
203 }
204 return kMixed;
205 }
Mike Reed63edc572019-11-15 21:58:01 -0500206};
207
208#include "src/core/SkEdgeClipper.h"
209
Mike Reedd5b51a02020-07-30 21:50:30 -0400210static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
Mike Reed63edc572019-11-15 21:58:01 -0500211 SkMatrix mx, inv;
212 SkVector v = p1 - p0;
213 mx.setAll(v.fX, -v.fY, p0.fX,
214 v.fY, v.fX, p0.fY,
215 0, 0, 1);
216 SkAssertResult(mx.invert(&inv));
217
218 SkPath rotated;
219 path.transform(inv, &rotated);
220
221 SkScalar big = 1e28f;
222 SkRect clip = {-big, 0, big, big };
223
224 struct Rec {
Mike Reedd5b51a02020-07-30 21:50:30 -0400225 SkPathBuilder fResult;
226 SkPoint fPrev = {0, 0};
227 } rec;
Mike Reed63edc572019-11-15 21:58:01 -0500228
Mike Klein71f12662020-10-28 09:07:09 -0500229 SkEdgeClipper::ClipPath(rotated, clip, false,
Mike Reed63edc572019-11-15 21:58:01 -0500230 [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
231 Rec* rec = (Rec*)ctx;
232
233 bool addLineTo = false;
234 SkPoint pts[4];
235 SkPath::Verb verb;
236 while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
237 if (newCtr) {
Mike Reedd5b51a02020-07-30 21:50:30 -0400238 rec->fResult.moveTo(pts[0]);
Mike Reed63edc572019-11-15 21:58:01 -0500239 rec->fPrev = pts[0];
240 newCtr = false;
241 }
242
243 if (addLineTo || pts[0] != rec->fPrev) {
Mike Reedd5b51a02020-07-30 21:50:30 -0400244 rec->fResult.lineTo(pts[0]);
Mike Reed63edc572019-11-15 21:58:01 -0500245 }
246
247 switch (verb) {
248 case SkPath::kLine_Verb:
Mike Reedd5b51a02020-07-30 21:50:30 -0400249 rec->fResult.lineTo(pts[1]);
Mike Reed63edc572019-11-15 21:58:01 -0500250 rec->fPrev = pts[1];
251 break;
252 case SkPath::kQuad_Verb:
Mike Reedd5b51a02020-07-30 21:50:30 -0400253 rec->fResult.quadTo(pts[1], pts[2]);
Mike Reed63edc572019-11-15 21:58:01 -0500254 rec->fPrev = pts[2];
255 break;
256 case SkPath::kCubic_Verb:
Mike Reedd5b51a02020-07-30 21:50:30 -0400257 rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
Mike Reed63edc572019-11-15 21:58:01 -0500258 rec->fPrev = pts[3];
259 break;
260 default: break;
261 }
262 addLineTo = true;
263 }
264 }, &rec);
265
Mike Reedd5b51a02020-07-30 21:50:30 -0400266 return rec.fResult.detach().makeTransform(mx);
Mike Reed63edc572019-11-15 21:58:01 -0500267}
268
Mike Reed63edc572019-11-15 21:58:01 -0500269static void draw_halfplane(SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
270 SkVector v = p1 - p0;
271 p0 = p0 - v * 1000;
272 p1 = p1 + v * 1000;
273
274 SkPaint paint;
275 paint.setColor(c);
276 canvas->drawLine(p0, p1, paint);
277}
278
279static SkPath make_path() {
280 SkRandom rand;
Mike Reed0483b462019-12-05 17:29:45 -0500281 auto rand_pt = [&rand]() {
282 auto x = rand.nextF();
283 auto y = rand.nextF();
284 return SkPoint{x * 400, y * 400};
285 };
Mike Reed63edc572019-11-15 21:58:01 -0500286
Mike Reedd5b51a02020-07-30 21:50:30 -0400287 SkPathBuilder path;
Mike Reed63edc572019-11-15 21:58:01 -0500288 for (int i = 0; i < 4; ++i) {
Mike Reed0483b462019-12-05 17:29:45 -0500289 SkPoint pts[6];
290 for (auto& p : pts) {
291 p = rand_pt();
292 }
293 path.moveTo(pts[0]).quadTo(pts[1], pts[2]).quadTo(pts[3], pts[4]).lineTo(pts[5]);
Mike Reed63edc572019-11-15 21:58:01 -0500294 }
Mike Reedd5b51a02020-07-30 21:50:30 -0400295 return path.detach();
Mike Reed63edc572019-11-15 21:58:01 -0500296}
297
298class HalfPlaneView : public Sample {
299 SkPoint fPts[2];
300 SkPath fPath;
301
302 SkString name() override { return SkString("halfplane"); }
303
304 void onOnceBeforeDraw() override {
305 fPts[0] = {0, 0};
306 fPts[1] = {3, 2};
307 fPath = make_path();
308 }
309
310 void onDrawContent(SkCanvas* canvas) override {
311 SkPaint paint;
312
313 paint.setColor({0.5f, 0.5f, 0.5f, 1.0f}, nullptr);
314 canvas->drawPath(fPath, paint);
315
316 paint.setColor({0, 0, 0, 1}, nullptr);
Mike Reed6344c292019-12-04 12:45:39 -0500317
Mike Reedd5b51a02020-07-30 21:50:30 -0400318 canvas->drawPath(clip(fPath, fPts[0], fPts[1]), paint);
Mike Reed63edc572019-11-15 21:58:01 -0500319
320 draw_halfplane(canvas, fPts[0], fPts[1], SK_ColorRED);
321 }
322
323 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
324 return new Click;
325 }
326
327 bool onClick(Click* click) override {
328 fPts[0] = click->fCurr;
329 fPts[1] = fPts[0] + SkPoint{3, 2};
330 return true;
331 }
332};
333DEF_SAMPLE( return new HalfPlaneView(); )
334
335static void draw_halfplane(SkCanvas* canvas, const SkHalfPlane& p, SkColor c) {
336 SkPoint pts[2];
337 p.twoPts(pts);
338 draw_halfplane(canvas, pts[0], pts[1], c);
339}
340
John Stilesc5fc9702021-08-10 19:54:46 -0400341static void compute_half_planes(const SkMatrix& mx, SkScalar width, SkScalar height,
Mike Reed63edc572019-11-15 21:58:01 -0500342 SkHalfPlane planes[4]) {
343 SkScalar a = mx[0], b = mx[1], c = mx[2],
344 d = mx[3], e = mx[4], f = mx[5],
345 g = mx[6], h = mx[7], i = mx[8];
346
John Stilesc5fc9702021-08-10 19:54:46 -0400347 planes[0] = { 2*g - 2*a/width, 2*h - 2*b/width, 2*i - 2*c/width };
348 planes[1] = { 2*a/width, 2*b/width, 2*c/width };
349 planes[2] = { 2*g - 2*d/height, 2*h - 2*e/height, 2*i - 2*f/height };
350 planes[3] = { 2*d/height, 2*e/height, 2*f/height };
Mike Reed63edc572019-11-15 21:58:01 -0500351}
352
353class HalfPlaneView2 : public Sample {
354 SkPoint fPts[4];
355 SkPath fPath;
356
357 SkString name() override { return SkString("halfplane2"); }
358
359 void onOnceBeforeDraw() override {
360 fPath = make_path();
361 SkRect r = fPath.getBounds();
362 r.toQuad(fPts);
363 }
364
365 void onDrawContent(SkCanvas* canvas) override {
366 SkMatrix mx;
367 {
368 SkRect r = fPath.getBounds();
369 SkPoint src[4];
370 r.toQuad(src);
371 mx.setPolyToPoly(src, fPts, 4);
372 }
373
374 SkPaint paint;
375 canvas->drawPath(fPath, paint);
376
377 canvas->save();
378 canvas->concat(mx);
379 paint.setColor(0x40FF0000);
380 canvas->drawPath(fPath, paint);
381 canvas->restore();
382
383 // draw the frame
384 paint.setStrokeWidth(10);
385 paint.setColor(SK_ColorGREEN);
386 canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPts, paint);
387
388 // draw the half-planes
389 SkHalfPlane planes[4];
390 compute_half_planes(mx, 400, 400, planes);
391 for (auto& p : planes) {
392 draw_halfplane(canvas, p, SK_ColorRED);
393 }
394 }
395
396 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
397 SkScalar r = 8;
398 SkRect rect = SkRect::MakeXYWH(x - r, y - r, 2*r, 2*r);
399 for (int i = 0; i < 4; ++i) {
400 if (rect.contains(fPts[i].fX, fPts[i].fY)) {
401 Click* c = new Click;
402 c->fMeta.setS32("index", i);
403 return c;
404 }
405 }
406 return nullptr;
407 }
408
409 bool onClick(Click* click) override {
410 int32_t index;
411 SkAssertResult(click->fMeta.findS32("index", &index));
412 SkASSERT(index >= 0 && index < 4);
413 fPts[index] = click->fCurr;
414 return true;
415 }
416};
417DEF_SAMPLE( return new HalfPlaneView2(); )
418
Mike Reed00a97642020-01-25 18:42:23 -0500419static SkM44 inv(const SkM44& m) {
420 SkM44 inverse;
Mike Reed63edc572019-11-15 21:58:01 -0500421 SkAssertResult(m.invert(&inverse));
422 return inverse;
423}
424
Mike Reed6344c292019-12-04 12:45:39 -0500425static SkHalfPlane half_plane_w0(const SkMatrix& m) {
426 return { m[SkMatrix::kMPersp0], m[SkMatrix::kMPersp1], m[SkMatrix::kMPersp2] - 0.05f };
Jim Van Verthc3363152019-12-03 12:05:14 -0500427}
428
Mike Reedcbeabd92019-12-16 17:13:58 -0500429class SampleCameraView : public Sample {
Mike Reed63edc572019-11-15 21:58:01 -0500430 float fNear = 0.05f;
431 float fFar = 4;
432 float fAngle = SK_ScalarPI / 4;
433
Mike Reed00a97642020-01-25 18:42:23 -0500434 SkV3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
435 SkV3 fCOA { 0, 0, 0 };
436 SkV3 fUp { 0, 1, 0 };
Mike Reed63edc572019-11-15 21:58:01 -0500437
Mike Reed00a97642020-01-25 18:42:23 -0500438 SkM44 fRot;
439 SkV3 fTrans;
Mike Reed63edc572019-11-15 21:58:01 -0500440
Mike Reed63edc572019-11-15 21:58:01 -0500441 void rotate(float x, float y, float z) {
Mike Reed00a97642020-01-25 18:42:23 -0500442 SkM44 r;
Mike Reed63edc572019-11-15 21:58:01 -0500443 if (x) {
Mike Reed00a97642020-01-25 18:42:23 -0500444 r.setRotateUnit({1, 0, 0}, x);
Mike Reed63edc572019-11-15 21:58:01 -0500445 } else if (y) {
Mike Reed00a97642020-01-25 18:42:23 -0500446 r.setRotateUnit({0, 1, 0}, y);
Mike Reed63edc572019-11-15 21:58:01 -0500447 } else {
Mike Reed00a97642020-01-25 18:42:23 -0500448 r.setRotateUnit({0, 0, 1}, z);
Mike Reed63edc572019-11-15 21:58:01 -0500449 }
Mike Reed00a97642020-01-25 18:42:23 -0500450 fRot = r * fRot;
Mike Reed63edc572019-11-15 21:58:01 -0500451 }
452
Mike Reedcbeabd92019-12-16 17:13:58 -0500453public:
Mike Reed00a97642020-01-25 18:42:23 -0500454 SkM44 get44(const SkRect& r) const {
Mike Reedcbeabd92019-12-16 17:13:58 -0500455 SkScalar w = r.width();
456 SkScalar h = r.height();
457
Michael Ludwige5016702021-03-23 15:42:59 -0400458 SkM44 camera = SkM44::LookAt(fEye, fCOA, fUp),
459 perspective = SkM44::Perspective(fNear, fFar, fAngle),
Mike Reed00a97642020-01-25 18:42:23 -0500460 translate = SkM44::Translate(fTrans.x, fTrans.y, fTrans.z),
461 viewport = SkM44::Translate(r.centerX(), r.centerY(), 0) *
462 SkM44::Scale(w*0.5f, h*0.5f, 1);
Mike Reed63edc572019-11-15 21:58:01 -0500463
Jim Van Verthc3363152019-12-03 12:05:14 -0500464 return viewport * perspective * camera * translate * fRot * inv(viewport);
Mike Reed63edc572019-11-15 21:58:01 -0500465 }
466
Mike Reedcbeabd92019-12-16 17:13:58 -0500467 bool onChar(SkUnichar uni) override {
468 float delta = SK_ScalarPI / 30;
469 switch (uni) {
470 case '8': this->rotate( delta, 0, 0); return true;
471 case '2': this->rotate(-delta, 0, 0); return true;
472 case '4': this->rotate(0, delta, 0); return true;
473 case '6': this->rotate(0, -delta, 0); return true;
474 case '-': this->rotate(0, 0, delta); return true;
475 case '+': this->rotate(0, 0, -delta); return true;
476
Mike Reed00a97642020-01-25 18:42:23 -0500477 case 'i': fTrans.z += 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
478 case 'k': fTrans.z -= 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
Mike Reedcbeabd92019-12-16 17:13:58 -0500479
480 case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
481 case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
482 case 'f': fFar += 0.1f; SkDebugf("far %g\n", fFar); return true;
483 case 'F': fFar -= 0.1f; SkDebugf("far %g\n", fFar); return true;
484 default: break;
485 }
486 return false;
487 }
488};
489
490class HalfPlaneView3 : public SampleCameraView {
491 SkPath fPath;
492 sk_sp<SkShader> fShader;
493 bool fShowUnclipped = false;
494
495 SkString name() override { return SkString("halfplane3"); }
496
497 void onOnceBeforeDraw() override {
498 fPath = make_path();
499 fShader = GetResourceAsImage("images/mandrill_128.png")
Mike Reed99c94462020-12-08 13:16:56 -0500500 ->makeShader(SkSamplingOptions(), SkMatrix::Scale(3, 3));
Mike Reedcbeabd92019-12-16 17:13:58 -0500501 }
502
503 bool onChar(SkUnichar uni) override {
504 switch (uni) {
505 case 'u': fShowUnclipped = !fShowUnclipped; return true;
506 default: break;
507 }
508 return this->SampleCameraView::onChar(uni);
509 }
510
Mike Reed63edc572019-11-15 21:58:01 -0500511 void onDrawContent(SkCanvas* canvas) override {
Mike Reed00a97642020-01-25 18:42:23 -0500512 SkM44 mx = this->get44({0, 0, 400, 400});
Mike Reed63edc572019-11-15 21:58:01 -0500513
514 SkPaint paint;
515 paint.setColor({0.75, 0.75, 0.75, 1});
516 canvas->drawPath(fPath, paint);
517
518 paint.setShader(fShader);
519
Mike Reed6344c292019-12-04 12:45:39 -0500520 if (fShowUnclipped) {
521 canvas->save();
Mike Reed3ef77dd2020-04-06 10:41:09 -0400522 canvas->concat(mx);
Mike Reed6344c292019-12-04 12:45:39 -0500523 paint.setAlphaf(0.33f);
524 canvas->drawPath(fPath, paint);
525 paint.setAlphaf(1.f);
526 canvas->restore();
527 }
528
Mike Reed6344c292019-12-04 12:45:39 -0500529
530 SkColor planeColor = SK_ColorBLUE;
531 SkPath clippedPath, *path = &fPath;
Mike Reed00a97642020-01-25 18:42:23 -0500532 if (SkPathPriv::PerspectiveClip(fPath, mx.asM33(), &clippedPath)) {
Mike Reed6344c292019-12-04 12:45:39 -0500533 path = &clippedPath;
534 planeColor = SK_ColorRED;
535 }
Mike Reed63edc572019-11-15 21:58:01 -0500536 canvas->save();
Mike Reed3ef77dd2020-04-06 10:41:09 -0400537 canvas->concat(mx);
Mike Reed6344c292019-12-04 12:45:39 -0500538 canvas->drawPath(*path, paint);
Mike Reed63edc572019-11-15 21:58:01 -0500539 canvas->restore();
540
Mike Reed00a97642020-01-25 18:42:23 -0500541 SkHalfPlane hpw = half_plane_w0(mx.asM33());
Mike Reed6344c292019-12-04 12:45:39 -0500542 draw_halfplane(canvas, hpw, planeColor);
Mike Reed63edc572019-11-15 21:58:01 -0500543 }
Mike Reed63edc572019-11-15 21:58:01 -0500544};
545DEF_SAMPLE( return new HalfPlaneView3(); )
Mike Reedcbeabd92019-12-16 17:13:58 -0500546
547class HalfPlaneCoons : public SampleCameraView {
548 SkPoint fPatch[12];
Mike Reed190b82d2019-12-17 17:25:27 -0500549 SkColor fColors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
550 SkPoint fTex[4] = {{0, 0}, {256, 0}, {256, 256}, {0, 256}};
551 sk_sp<SkShader> fShader;
552
553 bool fShowHandles = false;
554 bool fShowSkeleton = false;
555 bool fShowTex = false;
Mike Reedcbeabd92019-12-16 17:13:58 -0500556
557 SkString name() override { return SkString("halfplane-coons"); }
558
559 void onOnceBeforeDraw() override {
560 fPatch[0] = { 0, 0 };
561 fPatch[1] = { 100, 0 };
562 fPatch[2] = { 200, 0 };
563 fPatch[3] = { 300, 0 };
564 fPatch[4] = { 300, 100 };
565 fPatch[5] = { 300, 200 };
566 fPatch[6] = { 300, 300 };
567 fPatch[7] = { 200, 300 };
568 fPatch[8] = { 100, 300 };
569 fPatch[9] = { 0, 300 };
570 fPatch[10] = { 0, 200 };
571 fPatch[11] = { 0, 100 };
Mike Reed190b82d2019-12-17 17:25:27 -0500572
Mike Reed99c94462020-12-08 13:16:56 -0500573 fShader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
Mike Reedcbeabd92019-12-16 17:13:58 -0500574 }
575
576 void onDrawContent(SkCanvas* canvas) override {
Mike Reedcbeabd92019-12-16 17:13:58 -0500577 SkPaint paint;
578
579 canvas->save();
Mike Reed3ef77dd2020-04-06 10:41:09 -0400580 canvas->concat(this->get44({0, 0, 300, 300}));
Mike Reed190b82d2019-12-17 17:25:27 -0500581
582 const SkPoint* tex = nullptr;
583 const SkColor* col = nullptr;
584 if (!fShowSkeleton) {
585 if (fShowTex) {
586 paint.setShader(fShader);
587 tex = fTex;
588 } else {
589 col = fColors;
590 }
591 }
592 canvas->drawPatch(fPatch, col, tex, SkBlendMode::kSrc, paint);
593 paint.setShader(nullptr);
594
595 if (fShowHandles) {
596 paint.setAntiAlias(true);
597 paint.setStrokeCap(SkPaint::kRound_Cap);
598 paint.setStrokeWidth(8);
599 canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
600 paint.setColor(SK_ColorWHITE);
601 paint.setStrokeWidth(6);
602 canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
603 }
604
Mike Reedcbeabd92019-12-16 17:13:58 -0500605 canvas->restore();
606 }
Mike Reed190b82d2019-12-17 17:25:27 -0500607
608 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
609 auto dist = [](SkPoint a, SkPoint b) { return (b - a).length(); };
610
611 const float tol = 15;
612 for (int i = 0; i < 12; ++i) {
613 if (dist({x,y}, fPatch[i]) <= tol) {
Mike Reed0ce3e882020-04-22 11:09:30 -0400614 return new Click([this, i](Click* c) {
615 fPatch[i] = c->fCurr;
616 return true;
617 });
Mike Reed190b82d2019-12-17 17:25:27 -0500618 }
619 }
620 return nullptr;
621 }
622
Mike Reed190b82d2019-12-17 17:25:27 -0500623 bool onChar(SkUnichar uni) override {
624 switch (uni) {
625 case 'h': fShowHandles = !fShowHandles; return true;
Mike Reedc8803462019-12-18 13:34:15 -0500626 case 'k': fShowSkeleton = !fShowSkeleton; return true;
Mike Reed190b82d2019-12-17 17:25:27 -0500627 case 't': fShowTex = !fShowTex; return true;
628 default: break;
629 }
630 return this->SampleCameraView::onChar(uni);
631 }
632
Mike Reedcbeabd92019-12-16 17:13:58 -0500633};
634DEF_SAMPLE( return new HalfPlaneCoons(); )