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