blob: b5d6a78c14392649ddd766b9824341062e149e8d [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"
12#include "include/core/SkPath.h"
13#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"
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;
41 SkPath p;
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());
51 p.addOval(r);
52 canvas->drawPath(p, paint);
53 }
54}
55
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000056static SkScalar randRange(SkRandom& rand, SkScalar min, SkScalar max) {
reed@google.com045e62d2011-10-24 12:19:46 +000057 SkASSERT(min <= max);
Mike Reeddf85c382017-02-14 10:59:19 -050058 return min + rand.nextUScalar1() * (max - min);
reed@google.com045e62d2011-10-24 12:19:46 +000059}
60
61static void show_stroke(SkCanvas* canvas, bool doAA, SkScalar strokeWidth, int n) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000062 SkRandom rand;
reed@google.com045e62d2011-10-24 12:19:46 +000063 SkPaint paint;
64 paint.setAntiAlias(doAA);
65 paint.setStyle(SkPaint::kStroke_Style);
66 paint.setStrokeWidth(strokeWidth);
rmistry@google.comae933ce2012-08-23 18:19:56 +000067
reed@google.com045e62d2011-10-24 12:19:46 +000068 for (int i = 0; i < n; ++i) {
69 SkRect r;
70 SkPath p;
rmistry@google.comae933ce2012-08-23 18:19:56 +000071
reed@google.com045e62d2011-10-24 12:19:46 +000072 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
73 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
74 paint.setColor(rand.nextU());
75 canvas->drawRect(r, paint);
rmistry@google.comae933ce2012-08-23 18:19:56 +000076
reed@google.com045e62d2011-10-24 12:19:46 +000077 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
78 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
79 paint.setColor(rand.nextU());
80 p.addOval(r);
81 canvas->drawPath(p, paint);
82
83 const SkScalar minx = -SkIntToScalar(W)/4;
84 const SkScalar maxx = 5*SkIntToScalar(W)/4;
85 const SkScalar miny = -SkIntToScalar(H)/4;
86 const SkScalar maxy = 5*SkIntToScalar(H)/4;
87 paint.setColor(rand.nextU());
88 canvas->drawLine(randRange(rand, minx, maxx), randRange(rand, miny, maxy),
89 randRange(rand, minx, maxx), randRange(rand, miny, maxy),
90 paint);
91 }
92}
93
94static void show_hair(SkCanvas* canvas, bool doAA) {
95 show_stroke(canvas, doAA, 0, 150);
96}
97
98static void show_thick(SkCanvas* canvas, bool doAA) {
99 show_stroke(canvas, doAA, SkIntToScalar(5), 50);
100}
101
102typedef void (*CanvasProc)(SkCanvas*, bool);
103
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400104class ClipView : public Sample {
Hal Canaryd7639af2019-07-17 09:08:11 -0400105 SkString name() override { return SkString("Clip"); }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000106
Hal Canaryd7639af2019-07-17 09:08:11 -0400107 void onDrawContent(SkCanvas* canvas) override {
reed@google.com045e62d2011-10-24 12:19:46 +0000108 canvas->drawColor(SK_ColorWHITE);
reed@google.com3bcf8d32011-10-10 15:42:36 +0000109 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
110
111 static const CanvasProc gProc[] = {
reed@google.com045e62d2011-10-24 12:19:46 +0000112 show_text, show_thick, show_hair, show_fill
reed@google.com3bcf8d32011-10-10 15:42:36 +0000113 };
rmistry@google.comae933ce2012-08-23 18:19:56 +0000114
reed@google.com3bcf8d32011-10-10 15:42:36 +0000115 SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
116 SkPath clipPath;
117 r.inset(SK_Scalar1 / 4, SK_Scalar1 / 4);
Mike Reed4241f5e2019-09-14 19:13:23 +0000118 clipPath.addRoundRect(r, SkIntToScalar(20), SkIntToScalar(20));
reed@google.com045e62d2011-10-24 12:19:46 +0000119
120// clipPath.toggleInverseFillType();
reed@google.com3bcf8d32011-10-10 15:42:36 +0000121
122 for (int aa = 0; aa <= 1; ++aa) {
123 canvas->save();
124 for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); ++i) {
125 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500126 canvas->clipPath(clipPath, kIntersect_SkClipOp, SkToBool(aa));
reed@google.com045e62d2011-10-24 12:19:46 +0000127// canvas->drawColor(SK_ColorWHITE);
128 gProc[i](canvas, SkToBool(aa));
reed@google.com3bcf8d32011-10-10 15:42:36 +0000129 canvas->restore();
130 canvas->translate(W * SK_Scalar1 * 8 / 7, 0);
131 }
132 canvas->restore();
133 canvas->translate(0, H * SK_Scalar1 * 8 / 7);
134 }
135 }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000136};
137
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400138DEF_SAMPLE( return new ClipView(); )
Mike Reed63edc572019-11-15 21:58:01 -0500139
140///////////////////////////////////////////////////////////////////////////////
141
142struct SkHalfPlane {
143 SkScalar fA, fB, fC;
144
Mike Reed6344c292019-12-04 12:45:39 -0500145 SkScalar eval(SkScalar x, SkScalar y) const {
Mike Reed63edc572019-11-15 21:58:01 -0500146 return fA * x + fB * y + fC;
147 }
Mike Reed6344c292019-12-04 12:45:39 -0500148 SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
Mike Reed63edc572019-11-15 21:58:01 -0500149
150 bool twoPts(SkPoint pts[2]) const {
Jim Van Verthc3363152019-12-03 12:05:14 -0500151 // normalize plane to help with the perpendicular step, below
152 SkScalar len = SkScalarSqrt(fA*fA + fB*fB);
153 if (!len) {
154 return false;
155 }
156 SkScalar denom = SkScalarInvert(len);
157 SkScalar a = fA * denom;
158 SkScalar b = fB * denom;
159 SkScalar c = fC * denom;
160
161 // We compute p0 on the half-plane by setting one of the components to 0
162 // We compute p1 by stepping from p0 along a perpendicular to the normal
163 if (b) {
164 pts[0] = { 0, -c / b };
165 pts[1] = { b, pts[0].fY - a};
166 } else if (a) {
167 pts[0] = { -c / a, 0 };
168 pts[1] = { pts[0].fX + b, -a };
Mike Reed63edc572019-11-15 21:58:01 -0500169 } else {
170 return false;
171 }
Jim Van Verthc3363152019-12-03 12:05:14 -0500172
173 SkASSERT(SkScalarNearlyZero(this->operator()(pts[0].fX, pts[0].fY)));
174 SkASSERT(SkScalarNearlyZero(this->operator()(pts[1].fX, pts[1].fY)));
Mike Reed63edc572019-11-15 21:58:01 -0500175 return true;
176 }
Mike Reed6344c292019-12-04 12:45:39 -0500177
178 enum Result {
179 kAllNegative,
180 kAllPositive,
181 kMixed
182 };
183 Result test(const SkRect& bounds) const {
184 SkPoint diagMin, diagMax;
185 if (fA >= 0) {
186 diagMin.fX = bounds.fLeft;
187 diagMax.fX = bounds.fRight;
188 } else {
189 diagMin.fX = bounds.fRight;
190 diagMax.fX = bounds.fLeft;
191 }
192 if (fB >= 0) {
193 diagMin.fY = bounds.fTop;
194 diagMax.fY = bounds.fBottom;
195 } else {
196 diagMin.fY = bounds.fBottom;
197 diagMax.fY = bounds.fTop;
198 }
199 SkScalar test = this->eval(diagMin.fX, diagMin.fY);
200 SkScalar sign = test*this->eval(diagMax.fX, diagMin.fY);
201 if (sign > 0) {
202 // the path is either all on one side of the half-plane or the other
203 if (test < 0) {
204 return kAllNegative;
205 } else {
206 return kAllPositive;
207 }
208 }
209 return kMixed;
210 }
Mike Reed63edc572019-11-15 21:58:01 -0500211};
212
213#include "src/core/SkEdgeClipper.h"
214
Mike Reed6344c292019-12-04 12:45:39 -0500215static void clip(const SkPath& path, SkPoint p0, SkPoint p1, SkPath* clippedPath) {
Mike Reed63edc572019-11-15 21:58:01 -0500216 SkMatrix mx, inv;
217 SkVector v = p1 - p0;
218 mx.setAll(v.fX, -v.fY, p0.fX,
219 v.fY, v.fX, p0.fY,
220 0, 0, 1);
221 SkAssertResult(mx.invert(&inv));
222
223 SkPath rotated;
224 path.transform(inv, &rotated);
225
226 SkScalar big = 1e28f;
227 SkRect clip = {-big, 0, big, big };
228
229 struct Rec {
Mike Reed6344c292019-12-04 12:45:39 -0500230 SkPath* fResult;
Mike Reed63edc572019-11-15 21:58:01 -0500231 SkPoint fPrev;
Mike Reed6344c292019-12-04 12:45:39 -0500232 } rec = { clippedPath, {0, 0} };
Mike Reed63edc572019-11-15 21:58:01 -0500233
234 SkEdgeClipper::ClipPath(rotated, clip, false,
235 [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
236 Rec* rec = (Rec*)ctx;
237
238 bool addLineTo = false;
239 SkPoint pts[4];
240 SkPath::Verb verb;
241 while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
242 if (newCtr) {
Mike Reed6344c292019-12-04 12:45:39 -0500243 rec->fResult->moveTo(pts[0]);
Mike Reed63edc572019-11-15 21:58:01 -0500244 rec->fPrev = pts[0];
245 newCtr = false;
246 }
247
248 if (addLineTo || pts[0] != rec->fPrev) {
Mike Reed6344c292019-12-04 12:45:39 -0500249 rec->fResult->lineTo(pts[0]);
Mike Reed63edc572019-11-15 21:58:01 -0500250 }
251
252 switch (verb) {
253 case SkPath::kLine_Verb:
Mike Reed6344c292019-12-04 12:45:39 -0500254 rec->fResult->lineTo(pts[1]);
Mike Reed63edc572019-11-15 21:58:01 -0500255 rec->fPrev = pts[1];
256 break;
257 case SkPath::kQuad_Verb:
Mike Reed6344c292019-12-04 12:45:39 -0500258 rec->fResult->quadTo(pts[1], pts[2]);
Mike Reed63edc572019-11-15 21:58:01 -0500259 rec->fPrev = pts[2];
260 break;
261 case SkPath::kCubic_Verb:
Mike Reed6344c292019-12-04 12:45:39 -0500262 rec->fResult->cubicTo(pts[1], pts[2], pts[3]);
Mike Reed63edc572019-11-15 21:58:01 -0500263 rec->fPrev = pts[3];
264 break;
265 default: break;
266 }
267 addLineTo = true;
268 }
269 }, &rec);
270
Mike Reed6344c292019-12-04 12:45:39 -0500271 rec.fResult->transform(mx);
Mike Reed63edc572019-11-15 21:58:01 -0500272}
273
Mike Reed63edc572019-11-15 21:58:01 -0500274static void draw_halfplane(SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
275 SkVector v = p1 - p0;
276 p0 = p0 - v * 1000;
277 p1 = p1 + v * 1000;
278
279 SkPaint paint;
280 paint.setColor(c);
281 canvas->drawLine(p0, p1, paint);
282}
283
284static SkPath make_path() {
285 SkRandom rand;
Mike Reed0483b462019-12-05 17:29:45 -0500286 auto rand_pt = [&rand]() {
287 auto x = rand.nextF();
288 auto y = rand.nextF();
289 return SkPoint{x * 400, y * 400};
290 };
Mike Reed63edc572019-11-15 21:58:01 -0500291
292 SkPath path;
293 for (int i = 0; i < 4; ++i) {
Mike Reed0483b462019-12-05 17:29:45 -0500294 SkPoint pts[6];
295 for (auto& p : pts) {
296 p = rand_pt();
297 }
298 path.moveTo(pts[0]).quadTo(pts[1], pts[2]).quadTo(pts[3], pts[4]).lineTo(pts[5]);
Mike Reed63edc572019-11-15 21:58:01 -0500299 }
300 return path;
301}
302
303class HalfPlaneView : public Sample {
304 SkPoint fPts[2];
305 SkPath fPath;
306
307 SkString name() override { return SkString("halfplane"); }
308
309 void onOnceBeforeDraw() override {
310 fPts[0] = {0, 0};
311 fPts[1] = {3, 2};
312 fPath = make_path();
313 }
314
315 void onDrawContent(SkCanvas* canvas) override {
316 SkPaint paint;
317
318 paint.setColor({0.5f, 0.5f, 0.5f, 1.0f}, nullptr);
319 canvas->drawPath(fPath, paint);
320
321 paint.setColor({0, 0, 0, 1}, nullptr);
Mike Reed6344c292019-12-04 12:45:39 -0500322
323 SkPath clippedPath;
324 clip(fPath, fPts[0], fPts[1], &clippedPath);
325 canvas->drawPath(clippedPath, paint);
Mike Reed63edc572019-11-15 21:58:01 -0500326
327 draw_halfplane(canvas, fPts[0], fPts[1], SK_ColorRED);
328 }
329
330 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
331 return new Click;
332 }
333
334 bool onClick(Click* click) override {
335 fPts[0] = click->fCurr;
336 fPts[1] = fPts[0] + SkPoint{3, 2};
337 return true;
338 }
339};
340DEF_SAMPLE( return new HalfPlaneView(); )
341
342static void draw_halfplane(SkCanvas* canvas, const SkHalfPlane& p, SkColor c) {
343 SkPoint pts[2];
344 p.twoPts(pts);
345 draw_halfplane(canvas, pts[0], pts[1], c);
346}
347
348static void compute_half_planes(const SkMatrix& mx, SkScalar W, SkScalar H,
349 SkHalfPlane planes[4]) {
350 SkScalar a = mx[0], b = mx[1], c = mx[2],
351 d = mx[3], e = mx[4], f = mx[5],
352 g = mx[6], h = mx[7], i = mx[8];
353
354 planes[0] = { 2*g - 2*a/W, 2*h - 2*b/W, 2*i - 2*c/W };
355 planes[1] = { 2*a/W, 2*b/W, 2*c/W };
356 planes[2] = { 2*g - 2*d/H, 2*h - 2*e/H, 2*i - 2*f/H };
357 planes[3] = { 2*d/H, 2*e/H, 2*f/H };
358}
359
360class HalfPlaneView2 : public Sample {
361 SkPoint fPts[4];
362 SkPath fPath;
363
364 SkString name() override { return SkString("halfplane2"); }
365
366 void onOnceBeforeDraw() override {
367 fPath = make_path();
368 SkRect r = fPath.getBounds();
369 r.toQuad(fPts);
370 }
371
372 void onDrawContent(SkCanvas* canvas) override {
373 SkMatrix mx;
374 {
375 SkRect r = fPath.getBounds();
376 SkPoint src[4];
377 r.toQuad(src);
378 mx.setPolyToPoly(src, fPts, 4);
379 }
380
381 SkPaint paint;
382 canvas->drawPath(fPath, paint);
383
384 canvas->save();
385 canvas->concat(mx);
386 paint.setColor(0x40FF0000);
387 canvas->drawPath(fPath, paint);
388 canvas->restore();
389
390 // draw the frame
391 paint.setStrokeWidth(10);
392 paint.setColor(SK_ColorGREEN);
393 canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPts, paint);
394
395 // draw the half-planes
396 SkHalfPlane planes[4];
397 compute_half_planes(mx, 400, 400, planes);
398 for (auto& p : planes) {
399 draw_halfplane(canvas, p, SK_ColorRED);
400 }
401 }
402
403 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
404 SkScalar r = 8;
405 SkRect rect = SkRect::MakeXYWH(x - r, y - r, 2*r, 2*r);
406 for (int i = 0; i < 4; ++i) {
407 if (rect.contains(fPts[i].fX, fPts[i].fY)) {
408 Click* c = new Click;
409 c->fMeta.setS32("index", i);
410 return c;
411 }
412 }
413 return nullptr;
414 }
415
416 bool onClick(Click* click) override {
417 int32_t index;
418 SkAssertResult(click->fMeta.findS32("index", &index));
419 SkASSERT(index >= 0 && index < 4);
420 fPts[index] = click->fCurr;
421 return true;
422 }
423};
424DEF_SAMPLE( return new HalfPlaneView2(); )
425
426#include "include/core/SkMatrix44.h"
427#include "include/utils/Sk3D.h"
428#include "tools/Resources.h"
429
430static SkMatrix44 inv(const SkMatrix44& m) {
431 SkMatrix44 inverse;
432 SkAssertResult(m.invert(&inverse));
433 return inverse;
434}
435
Mike Reed6344c292019-12-04 12:45:39 -0500436#if 0 // Jim's general half-planes math
Mike Reed63edc572019-11-15 21:58:01 -0500437static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPlane planes[6]) {
438 float mx[16];
439 m44.asColMajorf(mx);
440
441 SkScalar a = mx[0], b = mx[4], /* c = mx[ 8], */ d = mx[12],
442 e = mx[1], f = mx[5], /* g = mx[ 9], */ h = mx[13],
443 i = mx[2], j = mx[6], /* k = mx[10], */ l = mx[14],
444 m = mx[3], n = mx[7], /* o = mx[11], */ p = mx[15];
445
446 a = 2*a/W - m; b = 2*b/W - n; d = 2*d/W - p;
447 e = 2*e/H - m; f = 2*f/H - n; h = 2*h/H - p;
Jim Van Verthc3363152019-12-03 12:05:14 -0500448// i = 2*i - m; j = 2*j - n; l = 2*l - p;
Mike Reed63edc572019-11-15 21:58:01 -0500449
450 planes[0] = { m - a, n - b, p - d }; // w - x
451 planes[1] = { m + a, n + b, p + d }; // w + x
452 planes[2] = { m - e, n - f, p - h }; // w - y
453 planes[3] = { m + e, n + f, p + h }; // w + y
454 planes[4] = { m - i, n - j, p - l }; // w - z
455 planes[5] = { m + i, n + j, p + l }; // w + z
456}
Mike Reed6344c292019-12-04 12:45:39 -0500457#endif
Mike Reed63edc572019-11-15 21:58:01 -0500458
Mike Reed6344c292019-12-04 12:45:39 -0500459static SkHalfPlane half_plane_w0(const SkMatrix& m) {
460 return { m[SkMatrix::kMPersp0], m[SkMatrix::kMPersp1], m[SkMatrix::kMPersp2] - 0.05f };
Jim Van Verthc3363152019-12-03 12:05:14 -0500461}
462
Mike Reedcbeabd92019-12-16 17:13:58 -0500463class SampleCameraView : public Sample {
Mike Reed63edc572019-11-15 21:58:01 -0500464 float fNear = 0.05f;
465 float fFar = 4;
466 float fAngle = SK_ScalarPI / 4;
467
468 SkPoint3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
469 SkPoint3 fCOA { 0, 0, 0 };
470 SkPoint3 fUp { 0, 1, 0 };
471
472 SkMatrix44 fRot;
Jim Van Verthc3363152019-12-03 12:05:14 -0500473 SkPoint3 fTrans;
Mike Reed63edc572019-11-15 21:58:01 -0500474
Mike Reed63edc572019-11-15 21:58:01 -0500475 void rotate(float x, float y, float z) {
476 SkMatrix44 r;
477 if (x) {
478 r.setRotateAboutUnit(1, 0, 0, x);
479 } else if (y) {
480 r.setRotateAboutUnit(0, 1, 0, y);
481 } else {
482 r.setRotateAboutUnit(0, 0, 1, z);
483 }
484 fRot.postConcat(r);
485 }
486
Mike Reedcbeabd92019-12-16 17:13:58 -0500487public:
488 SkMatrix44 get44(const SkRect& r) const {
Mike Reed63edc572019-11-15 21:58:01 -0500489 SkMatrix44 camera,
490 perspective,
Jim Van Verthc3363152019-12-03 12:05:14 -0500491 translate,
Mike Reed63edc572019-11-15 21:58:01 -0500492 viewport;
493
Mike Reedcbeabd92019-12-16 17:13:58 -0500494 SkScalar w = r.width();
495 SkScalar h = r.height();
496
Mike Reed63edc572019-11-15 21:58:01 -0500497 Sk3Perspective(&perspective, fNear, fFar, fAngle);
498 Sk3LookAt(&camera, fEye, fCOA, fUp);
Jim Van Verthc3363152019-12-03 12:05:14 -0500499 translate.setTranslate(fTrans.fX, fTrans.fY, fTrans.fZ);
Mike Reedcbeabd92019-12-16 17:13:58 -0500500 viewport.setScale(w*0.5f, h*0.5f, 1).postTranslate(r.centerX(), r.centerY(), 0);
Mike Reed63edc572019-11-15 21:58:01 -0500501
Jim Van Verthc3363152019-12-03 12:05:14 -0500502 return viewport * perspective * camera * translate * fRot * inv(viewport);
Mike Reed63edc572019-11-15 21:58:01 -0500503 }
504
Mike Reedcbeabd92019-12-16 17:13:58 -0500505 bool onChar(SkUnichar uni) override {
506 float delta = SK_ScalarPI / 30;
507 switch (uni) {
508 case '8': this->rotate( delta, 0, 0); return true;
509 case '2': this->rotate(-delta, 0, 0); return true;
510 case '4': this->rotate(0, delta, 0); return true;
511 case '6': this->rotate(0, -delta, 0); return true;
512 case '-': this->rotate(0, 0, delta); return true;
513 case '+': this->rotate(0, 0, -delta); return true;
514
515 case 'i': fTrans.fZ += 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true;
516 case 'k': fTrans.fZ -= 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true;
517
518 case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
519 case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
520 case 'f': fFar += 0.1f; SkDebugf("far %g\n", fFar); return true;
521 case 'F': fFar -= 0.1f; SkDebugf("far %g\n", fFar); return true;
522 default: break;
523 }
524 return false;
525 }
526};
527
528class HalfPlaneView3 : public SampleCameraView {
529 SkPath fPath;
530 sk_sp<SkShader> fShader;
531 bool fShowUnclipped = false;
532
533 SkString name() override { return SkString("halfplane3"); }
534
535 void onOnceBeforeDraw() override {
536 fPath = make_path();
537 fShader = GetResourceAsImage("images/mandrill_128.png")
538 ->makeShader(SkMatrix::MakeScale(3, 3));
539 }
540
541 bool onChar(SkUnichar uni) override {
542 switch (uni) {
543 case 'u': fShowUnclipped = !fShowUnclipped; return true;
544 default: break;
545 }
546 return this->SampleCameraView::onChar(uni);
547 }
548
Mike Reed63edc572019-11-15 21:58:01 -0500549 void onDrawContent(SkCanvas* canvas) override {
Mike Reedcbeabd92019-12-16 17:13:58 -0500550 SkMatrix mx = this->get44({0, 0, 400, 400});
Mike Reed63edc572019-11-15 21:58:01 -0500551
552 SkPaint paint;
553 paint.setColor({0.75, 0.75, 0.75, 1});
554 canvas->drawPath(fPath, paint);
555
556 paint.setShader(fShader);
557
Mike Reed6344c292019-12-04 12:45:39 -0500558 if (fShowUnclipped) {
559 canvas->save();
560 canvas->concat(mx);
561 paint.setAlphaf(0.33f);
562 canvas->drawPath(fPath, paint);
563 paint.setAlphaf(1.f);
564 canvas->restore();
565 }
566
Mike Reed6344c292019-12-04 12:45:39 -0500567
568 SkColor planeColor = SK_ColorBLUE;
569 SkPath clippedPath, *path = &fPath;
Mike Reedfae46e42019-12-04 16:15:23 -0500570 if (SkPathPriv::PerspectiveClip(fPath, mx, &clippedPath)) {
Mike Reed6344c292019-12-04 12:45:39 -0500571 path = &clippedPath;
572 planeColor = SK_ColorRED;
573 }
Mike Reed63edc572019-11-15 21:58:01 -0500574 canvas->save();
575 canvas->concat(mx);
Mike Reed6344c292019-12-04 12:45:39 -0500576 canvas->drawPath(*path, paint);
Mike Reed63edc572019-11-15 21:58:01 -0500577 canvas->restore();
578
Mike Reedfae46e42019-12-04 16:15:23 -0500579 SkHalfPlane hpw = half_plane_w0(mx);
Mike Reed6344c292019-12-04 12:45:39 -0500580 draw_halfplane(canvas, hpw, planeColor);
Mike Reed63edc572019-11-15 21:58:01 -0500581 }
Mike Reed63edc572019-11-15 21:58:01 -0500582};
583DEF_SAMPLE( return new HalfPlaneView3(); )
Mike Reedcbeabd92019-12-16 17:13:58 -0500584
585class HalfPlaneCoons : public SampleCameraView {
586 SkPoint fPatch[12];
Mike Reed190b82d2019-12-17 17:25:27 -0500587 SkColor fColors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
588 SkPoint fTex[4] = {{0, 0}, {256, 0}, {256, 256}, {0, 256}};
589 sk_sp<SkShader> fShader;
590
591 bool fShowHandles = false;
592 bool fShowSkeleton = false;
593 bool fShowTex = false;
Mike Reedcbeabd92019-12-16 17:13:58 -0500594
595 SkString name() override { return SkString("halfplane-coons"); }
596
597 void onOnceBeforeDraw() override {
598 fPatch[0] = { 0, 0 };
599 fPatch[1] = { 100, 0 };
600 fPatch[2] = { 200, 0 };
601 fPatch[3] = { 300, 0 };
602 fPatch[4] = { 300, 100 };
603 fPatch[5] = { 300, 200 };
604 fPatch[6] = { 300, 300 };
605 fPatch[7] = { 200, 300 };
606 fPatch[8] = { 100, 300 };
607 fPatch[9] = { 0, 300 };
608 fPatch[10] = { 0, 200 };
609 fPatch[11] = { 0, 100 };
Mike Reed190b82d2019-12-17 17:25:27 -0500610
611 fShader = GetResourceAsImage("images/mandrill_256.png")->makeShader();
Mike Reedcbeabd92019-12-16 17:13:58 -0500612 }
613
614 void onDrawContent(SkCanvas* canvas) override {
615 SkMatrix mx = this->get44({0, 0, 300, 300});
616
617 SkPaint paint;
618
619 canvas->save();
620 canvas->concat(mx);
Mike Reed190b82d2019-12-17 17:25:27 -0500621
622 const SkPoint* tex = nullptr;
623 const SkColor* col = nullptr;
624 if (!fShowSkeleton) {
625 if (fShowTex) {
626 paint.setShader(fShader);
627 tex = fTex;
628 } else {
629 col = fColors;
630 }
631 }
632 canvas->drawPatch(fPatch, col, tex, SkBlendMode::kSrc, paint);
633 paint.setShader(nullptr);
634
635 if (fShowHandles) {
636 paint.setAntiAlias(true);
637 paint.setStrokeCap(SkPaint::kRound_Cap);
638 paint.setStrokeWidth(8);
639 canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
640 paint.setColor(SK_ColorWHITE);
641 paint.setStrokeWidth(6);
642 canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
643 }
644
Mike Reedcbeabd92019-12-16 17:13:58 -0500645 canvas->restore();
646 }
Mike Reed190b82d2019-12-17 17:25:27 -0500647
648 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
649 auto dist = [](SkPoint a, SkPoint b) { return (b - a).length(); };
650
651 const float tol = 15;
652 for (int i = 0; i < 12; ++i) {
653 if (dist({x,y}, fPatch[i]) <= tol) {
654 Click* c = new Click;
655 c->fMeta.setS32("index", i);
656 return c;
657 }
658 }
659 return nullptr;
660 }
661
662 bool onClick(Click* click) override {
663 int32_t index;
664 SkAssertResult(click->fMeta.findS32("index", &index));
665 SkASSERT(index >= 0 && index < 12);
666 fPatch[index] = click->fCurr;
667 return true;
668 }
669
670 bool onChar(SkUnichar uni) override {
671 switch (uni) {
672 case 'h': fShowHandles = !fShowHandles; return true;
Mike Reedc8803462019-12-18 13:34:15 -0500673 case 'k': fShowSkeleton = !fShowSkeleton; return true;
Mike Reed190b82d2019-12-17 17:25:27 -0500674 case 't': fShowTex = !fShowTex; return true;
675 default: break;
676 }
677 return this->SampleCameraView::onChar(uni);
678 }
679
Mike Reedcbeabd92019-12-16 17:13:58 -0500680};
681DEF_SAMPLE( return new HalfPlaneCoons(); )