blob: 6f943fcae8cd394ca70be1c500d985fcd33dbf60 [file] [log] [blame]
Chris Dalton6f5e77a2018-04-23 21:14:42 -06001/*
2 * Copyright 2018 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 "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkCanvas.h"
10#include "include/core/SkColor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkPaint.h"
12#include "include/core/SkPath.h"
13#include "include/core/SkPoint.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040014#include "include/core/SkScalar.h"
15#include "include/core/SkSize.h"
16#include "include/core/SkString.h"
17#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "include/utils/SkRandom.h"
19#include "src/core/SkGeometry.h"
Hal Canary8a001442018-09-19 11:31:27 -040020
Chris Dalton6f5e77a2018-04-23 21:14:42 -060021#include <math.h>
22
23namespace skiagm {
24
25// Slices paths into sliver-size contours shaped like ice cream cones.
26class MandolineSlicer {
27public:
28 static constexpr int kDefaultSubdivisions = 10;
29
30 MandolineSlicer(SkPoint anchorPt) {
Mike Reed7d34dc72019-11-26 12:17:17 -050031 fPath.setFillType(SkPathFillType::kEvenOdd);
Chris Dalton6f5e77a2018-04-23 21:14:42 -060032 fPath.setIsVolatile(true);
33 this->reset(anchorPt);
34 }
35
36 void reset(SkPoint anchorPt) {
37 fPath.reset();
38 fLastPt = fAnchorPt = anchorPt;
39 }
40
41 void sliceLine(SkPoint pt, int numSubdivisions = kDefaultSubdivisions) {
42 if (numSubdivisions <= 0) {
43 fPath.moveTo(fAnchorPt);
44 fPath.lineTo(fLastPt);
45 fPath.lineTo(pt);
46 fPath.close();
47 fLastPt = pt;
48 return;
49 }
50 float T = this->chooseChopT(numSubdivisions);
51 if (0 == T) {
Chris Dalton6f5e77a2018-04-23 21:14:42 -060052 return;
53 }
54 SkPoint midpt = fLastPt * (1 - T) + pt * T;
55 this->sliceLine(midpt, numSubdivisions - 1);
56 this->sliceLine(pt, numSubdivisions - 1);
57 }
58
59 void sliceQuadratic(SkPoint p1, SkPoint p2, int numSubdivisions = kDefaultSubdivisions) {
60 if (numSubdivisions <= 0) {
61 fPath.moveTo(fAnchorPt);
62 fPath.lineTo(fLastPt);
63 fPath.quadTo(p1, p2);
64 fPath.close();
65 fLastPt = p2;
66 return;
67 }
68 float T = this->chooseChopT(numSubdivisions);
69 if (0 == T) {
Chris Dalton6f5e77a2018-04-23 21:14:42 -060070 return;
71 }
72 SkPoint P[3] = {fLastPt, p1, p2}, PP[5];
73 SkChopQuadAt(P, PP, T);
74 this->sliceQuadratic(PP[1], PP[2], numSubdivisions - 1);
75 this->sliceQuadratic(PP[3], PP[4], numSubdivisions - 1);
76 }
77
78 void sliceCubic(SkPoint p1, SkPoint p2, SkPoint p3,
79 int numSubdivisions = kDefaultSubdivisions) {
80 if (numSubdivisions <= 0) {
81 fPath.moveTo(fAnchorPt);
82 fPath.lineTo(fLastPt);
83 fPath.cubicTo(p1, p2, p3);
84 fPath.close();
85 fLastPt = p3;
86 return;
87 }
88 float T = this->chooseChopT(numSubdivisions);
89 if (0 == T) {
Chris Dalton6f5e77a2018-04-23 21:14:42 -060090 return;
91 }
92 SkPoint P[4] = {fLastPt, p1, p2, p3}, PP[7];
93 SkChopCubicAt(P, PP, T);
94 this->sliceCubic(PP[1], PP[2], PP[3], numSubdivisions - 1);
95 this->sliceCubic(PP[4], PP[5], PP[6], numSubdivisions - 1);
96 }
97
98 void sliceConic(SkPoint p1, SkPoint p2, float w, int numSubdivisions = kDefaultSubdivisions) {
99 if (numSubdivisions <= 0) {
100 fPath.moveTo(fAnchorPt);
101 fPath.lineTo(fLastPt);
102 fPath.conicTo(p1, p2, w);
103 fPath.close();
104 fLastPt = p2;
105 return;
106 }
107 float T = this->chooseChopT(numSubdivisions);
108 if (0 == T) {
Chris Dalton6f5e77a2018-04-23 21:14:42 -0600109 return;
110 }
111 SkConic conic(fLastPt, p1, p2, w), halves[2];
112 if (!conic.chopAt(T, halves)) {
113 SK_ABORT("SkConic::chopAt failed");
114 }
115 this->sliceConic(halves[0].fPts[1], halves[0].fPts[2], halves[0].fW, numSubdivisions - 1);
116 this->sliceConic(halves[1].fPts[1], halves[1].fPts[2], halves[1].fW, numSubdivisions - 1);
117 }
118
119 const SkPath& path() const { return fPath; }
120
121private:
122 float chooseChopT(int numSubdivisions) {
123 SkASSERT(numSubdivisions > 0);
124 if (numSubdivisions > 1) {
125 return .5f;
126 }
127 float T = (0 == fRand.nextU() % 10) ? 0 : scalbnf(1, -(int)fRand.nextRangeU(10, 149));
128 SkASSERT(T >= 0 && T < 1);
129 return T;
130 }
131
132 SkRandom fRand;
133 SkPath fPath;
134 SkPoint fAnchorPt;
135 SkPoint fLastPt;
136};
137
138class SliverPathsGM : public GM {
139public:
140 SliverPathsGM() {
Mike Kleind46dce32018-08-16 10:17:03 -0400141 this->setBGColor(SK_ColorBLACK);
Chris Dalton6f5e77a2018-04-23 21:14:42 -0600142 }
143
144protected:
145 SkString onShortName() override {
146 return SkString("mandoline");
147 }
148
149 SkISize onISize() override {
150 return SkISize::Make(560, 475);
151 }
152
153 void onDraw(SkCanvas* canvas) override {
154 SkPaint paint;
155 paint.setColor(SK_ColorWHITE);
156 paint.setAntiAlias(true);
157
158 MandolineSlicer mandoline({41, 43});
159 mandoline.sliceCubic({5, 277}, {381, -74}, {243, 162});
160 mandoline.sliceLine({41, 43});
161 canvas->drawPath(mandoline.path(), paint);
162
163 mandoline.reset({357.049988f, 446.049988f});
164 mandoline.sliceCubic({472.750000f, -71.950012f}, {639.750000f, 531.950012f},
165 {309.049988f, 347.950012f});
166 mandoline.sliceLine({309.049988f, 419});
167 mandoline.sliceLine({357.049988f, 446.049988f});
168 canvas->drawPath(mandoline.path(), paint);
169
170 canvas->save();
171 canvas->translate(421, 105);
172 canvas->scale(100, 81);
173 mandoline.reset({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))});
174 mandoline.sliceConic({-2, 0},
175 {-cosf(SkDegreesToRadians(60)), sinf(SkDegreesToRadians(60))}, .5f);
176 mandoline.sliceConic({-cosf(SkDegreesToRadians(120))*2, sinf(SkDegreesToRadians(120))*2},
177 {1, 0}, .5f);
178 mandoline.sliceLine({0, 0});
179 mandoline.sliceLine({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))});
180 canvas->drawPath(mandoline.path(), paint);
181 canvas->restore();
182
183 canvas->save();
184 canvas->translate(150, 300);
185 canvas->scale(75, 75);
186 mandoline.reset({1, 0});
187 constexpr int nquads = 5;
188 for (int i = 0; i < nquads; ++i) {
189 float theta1 = 2*SK_ScalarPI/nquads * (i + .5f);
190 float theta2 = 2*SK_ScalarPI/nquads * (i + 1);
191 mandoline.sliceQuadratic({cosf(theta1)*2, sinf(theta1)*2},
192 {cosf(theta2), sinf(theta2)});
193 }
194 canvas->drawPath(mandoline.path(), paint);
195 canvas->restore();
196 }
197};
198
199DEF_GM(return new SliverPathsGM;)
200
201}