1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2018 Google Inc. 3*c8dee2aaSAndroid Build Coastguard Worker * 4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be 5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file. 6*c8dee2aaSAndroid Build Coastguard Worker */ 7*c8dee2aaSAndroid Build Coastguard Worker 8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h" 9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h" 10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h" 11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h" 12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h" 13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h" 14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h" 15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h" 16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h" 17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h" 18*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h" 19*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkGeometry.h" 20*c8dee2aaSAndroid Build Coastguard Worker 21*c8dee2aaSAndroid Build Coastguard Worker #include <math.h> 22*c8dee2aaSAndroid Build Coastguard Worker 23*c8dee2aaSAndroid Build Coastguard Worker namespace skiagm { 24*c8dee2aaSAndroid Build Coastguard Worker 25*c8dee2aaSAndroid Build Coastguard Worker // Slices paths into sliver-size contours shaped like ice cream cones. 26*c8dee2aaSAndroid Build Coastguard Worker class MandolineSlicer { 27*c8dee2aaSAndroid Build Coastguard Worker public: 28*c8dee2aaSAndroid Build Coastguard Worker inline static constexpr int kDefaultSubdivisions = 10; 29*c8dee2aaSAndroid Build Coastguard Worker MandolineSlicer(SkPoint anchorPt)30*c8dee2aaSAndroid Build Coastguard Worker MandolineSlicer(SkPoint anchorPt) { 31*c8dee2aaSAndroid Build Coastguard Worker fPath.setFillType(SkPathFillType::kEvenOdd); 32*c8dee2aaSAndroid Build Coastguard Worker fPath.setIsVolatile(true); 33*c8dee2aaSAndroid Build Coastguard Worker this->reset(anchorPt); 34*c8dee2aaSAndroid Build Coastguard Worker } 35*c8dee2aaSAndroid Build Coastguard Worker reset(SkPoint anchorPt)36*c8dee2aaSAndroid Build Coastguard Worker void reset(SkPoint anchorPt) { 37*c8dee2aaSAndroid Build Coastguard Worker fPath.reset(); 38*c8dee2aaSAndroid Build Coastguard Worker fLastPt = fAnchorPt = anchorPt; 39*c8dee2aaSAndroid Build Coastguard Worker } 40*c8dee2aaSAndroid Build Coastguard Worker sliceLine(SkPoint pt,int numSubdivisions=kDefaultSubdivisions)41*c8dee2aaSAndroid Build Coastguard Worker void sliceLine(SkPoint pt, int numSubdivisions = kDefaultSubdivisions) { 42*c8dee2aaSAndroid Build Coastguard Worker if (numSubdivisions <= 0) { 43*c8dee2aaSAndroid Build Coastguard Worker fPath.moveTo(fAnchorPt); 44*c8dee2aaSAndroid Build Coastguard Worker fPath.lineTo(fLastPt); 45*c8dee2aaSAndroid Build Coastguard Worker fPath.lineTo(pt); 46*c8dee2aaSAndroid Build Coastguard Worker fPath.close(); 47*c8dee2aaSAndroid Build Coastguard Worker fLastPt = pt; 48*c8dee2aaSAndroid Build Coastguard Worker return; 49*c8dee2aaSAndroid Build Coastguard Worker } 50*c8dee2aaSAndroid Build Coastguard Worker float T = this->chooseChopT(numSubdivisions); 51*c8dee2aaSAndroid Build Coastguard Worker if (0 == T) { 52*c8dee2aaSAndroid Build Coastguard Worker return; 53*c8dee2aaSAndroid Build Coastguard Worker } 54*c8dee2aaSAndroid Build Coastguard Worker SkPoint midpt = fLastPt * (1 - T) + pt * T; 55*c8dee2aaSAndroid Build Coastguard Worker this->sliceLine(midpt, numSubdivisions - 1); 56*c8dee2aaSAndroid Build Coastguard Worker this->sliceLine(pt, numSubdivisions - 1); 57*c8dee2aaSAndroid Build Coastguard Worker } 58*c8dee2aaSAndroid Build Coastguard Worker sliceQuadratic(SkPoint p1,SkPoint p2,int numSubdivisions=kDefaultSubdivisions)59*c8dee2aaSAndroid Build Coastguard Worker void sliceQuadratic(SkPoint p1, SkPoint p2, int numSubdivisions = kDefaultSubdivisions) { 60*c8dee2aaSAndroid Build Coastguard Worker if (numSubdivisions <= 0) { 61*c8dee2aaSAndroid Build Coastguard Worker fPath.moveTo(fAnchorPt); 62*c8dee2aaSAndroid Build Coastguard Worker fPath.lineTo(fLastPt); 63*c8dee2aaSAndroid Build Coastguard Worker fPath.quadTo(p1, p2); 64*c8dee2aaSAndroid Build Coastguard Worker fPath.close(); 65*c8dee2aaSAndroid Build Coastguard Worker fLastPt = p2; 66*c8dee2aaSAndroid Build Coastguard Worker return; 67*c8dee2aaSAndroid Build Coastguard Worker } 68*c8dee2aaSAndroid Build Coastguard Worker float T = this->chooseChopT(numSubdivisions); 69*c8dee2aaSAndroid Build Coastguard Worker if (0 == T) { 70*c8dee2aaSAndroid Build Coastguard Worker return; 71*c8dee2aaSAndroid Build Coastguard Worker } 72*c8dee2aaSAndroid Build Coastguard Worker SkPoint P[3] = {fLastPt, p1, p2}, PP[5]; 73*c8dee2aaSAndroid Build Coastguard Worker SkChopQuadAt(P, PP, T); 74*c8dee2aaSAndroid Build Coastguard Worker this->sliceQuadratic(PP[1], PP[2], numSubdivisions - 1); 75*c8dee2aaSAndroid Build Coastguard Worker this->sliceQuadratic(PP[3], PP[4], numSubdivisions - 1); 76*c8dee2aaSAndroid Build Coastguard Worker } 77*c8dee2aaSAndroid Build Coastguard Worker sliceCubic(SkPoint p1,SkPoint p2,SkPoint p3,int numSubdivisions=kDefaultSubdivisions)78*c8dee2aaSAndroid Build Coastguard Worker void sliceCubic(SkPoint p1, SkPoint p2, SkPoint p3, 79*c8dee2aaSAndroid Build Coastguard Worker int numSubdivisions = kDefaultSubdivisions) { 80*c8dee2aaSAndroid Build Coastguard Worker if (numSubdivisions <= 0) { 81*c8dee2aaSAndroid Build Coastguard Worker fPath.moveTo(fAnchorPt); 82*c8dee2aaSAndroid Build Coastguard Worker fPath.lineTo(fLastPt); 83*c8dee2aaSAndroid Build Coastguard Worker fPath.cubicTo(p1, p2, p3); 84*c8dee2aaSAndroid Build Coastguard Worker fPath.close(); 85*c8dee2aaSAndroid Build Coastguard Worker fLastPt = p3; 86*c8dee2aaSAndroid Build Coastguard Worker return; 87*c8dee2aaSAndroid Build Coastguard Worker } 88*c8dee2aaSAndroid Build Coastguard Worker float T = this->chooseChopT(numSubdivisions); 89*c8dee2aaSAndroid Build Coastguard Worker if (0 == T) { 90*c8dee2aaSAndroid Build Coastguard Worker return; 91*c8dee2aaSAndroid Build Coastguard Worker } 92*c8dee2aaSAndroid Build Coastguard Worker SkPoint P[4] = {fLastPt, p1, p2, p3}, PP[7]; 93*c8dee2aaSAndroid Build Coastguard Worker SkChopCubicAt(P, PP, T); 94*c8dee2aaSAndroid Build Coastguard Worker this->sliceCubic(PP[1], PP[2], PP[3], numSubdivisions - 1); 95*c8dee2aaSAndroid Build Coastguard Worker this->sliceCubic(PP[4], PP[5], PP[6], numSubdivisions - 1); 96*c8dee2aaSAndroid Build Coastguard Worker } 97*c8dee2aaSAndroid Build Coastguard Worker sliceConic(SkPoint p1,SkPoint p2,float w,int numSubdivisions=kDefaultSubdivisions)98*c8dee2aaSAndroid Build Coastguard Worker void sliceConic(SkPoint p1, SkPoint p2, float w, int numSubdivisions = kDefaultSubdivisions) { 99*c8dee2aaSAndroid Build Coastguard Worker if (numSubdivisions <= 0) { 100*c8dee2aaSAndroid Build Coastguard Worker fPath.moveTo(fAnchorPt); 101*c8dee2aaSAndroid Build Coastguard Worker fPath.lineTo(fLastPt); 102*c8dee2aaSAndroid Build Coastguard Worker fPath.conicTo(p1, p2, w); 103*c8dee2aaSAndroid Build Coastguard Worker fPath.close(); 104*c8dee2aaSAndroid Build Coastguard Worker fLastPt = p2; 105*c8dee2aaSAndroid Build Coastguard Worker return; 106*c8dee2aaSAndroid Build Coastguard Worker } 107*c8dee2aaSAndroid Build Coastguard Worker float T = this->chooseChopT(numSubdivisions); 108*c8dee2aaSAndroid Build Coastguard Worker if (0 == T) { 109*c8dee2aaSAndroid Build Coastguard Worker return; 110*c8dee2aaSAndroid Build Coastguard Worker } 111*c8dee2aaSAndroid Build Coastguard Worker SkConic conic(fLastPt, p1, p2, w), halves[2]; 112*c8dee2aaSAndroid Build Coastguard Worker if (!conic.chopAt(T, halves)) { 113*c8dee2aaSAndroid Build Coastguard Worker SK_ABORT("SkConic::chopAt failed"); 114*c8dee2aaSAndroid Build Coastguard Worker } 115*c8dee2aaSAndroid Build Coastguard Worker this->sliceConic(halves[0].fPts[1], halves[0].fPts[2], halves[0].fW, numSubdivisions - 1); 116*c8dee2aaSAndroid Build Coastguard Worker this->sliceConic(halves[1].fPts[1], halves[1].fPts[2], halves[1].fW, numSubdivisions - 1); 117*c8dee2aaSAndroid Build Coastguard Worker } 118*c8dee2aaSAndroid Build Coastguard Worker path() const119*c8dee2aaSAndroid Build Coastguard Worker const SkPath& path() const { return fPath; } 120*c8dee2aaSAndroid Build Coastguard Worker 121*c8dee2aaSAndroid Build Coastguard Worker private: chooseChopT(int numSubdivisions)122*c8dee2aaSAndroid Build Coastguard Worker float chooseChopT(int numSubdivisions) { 123*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(numSubdivisions > 0); 124*c8dee2aaSAndroid Build Coastguard Worker if (numSubdivisions > 1) { 125*c8dee2aaSAndroid Build Coastguard Worker return .5f; 126*c8dee2aaSAndroid Build Coastguard Worker } 127*c8dee2aaSAndroid Build Coastguard Worker float T = (0 == fRand.nextU() % 10) ? 0 : scalbnf(1, -(int)fRand.nextRangeU(10, 149)); 128*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(T >= 0 && T < 1); 129*c8dee2aaSAndroid Build Coastguard Worker return T; 130*c8dee2aaSAndroid Build Coastguard Worker } 131*c8dee2aaSAndroid Build Coastguard Worker 132*c8dee2aaSAndroid Build Coastguard Worker SkRandom fRand; 133*c8dee2aaSAndroid Build Coastguard Worker SkPath fPath; 134*c8dee2aaSAndroid Build Coastguard Worker SkPoint fAnchorPt; 135*c8dee2aaSAndroid Build Coastguard Worker SkPoint fLastPt; 136*c8dee2aaSAndroid Build Coastguard Worker }; 137*c8dee2aaSAndroid Build Coastguard Worker 138*c8dee2aaSAndroid Build Coastguard Worker class SliverPathsGM : public GM { 139*c8dee2aaSAndroid Build Coastguard Worker public: SliverPathsGM()140*c8dee2aaSAndroid Build Coastguard Worker SliverPathsGM() { 141*c8dee2aaSAndroid Build Coastguard Worker this->setBGColor(SK_ColorBLACK); 142*c8dee2aaSAndroid Build Coastguard Worker } 143*c8dee2aaSAndroid Build Coastguard Worker 144*c8dee2aaSAndroid Build Coastguard Worker protected: getName() const145*c8dee2aaSAndroid Build Coastguard Worker SkString getName() const override { return SkString("mandoline"); } 146*c8dee2aaSAndroid Build Coastguard Worker getISize()147*c8dee2aaSAndroid Build Coastguard Worker SkISize getISize() override { return SkISize::Make(560, 475); } 148*c8dee2aaSAndroid Build Coastguard Worker onDraw(SkCanvas * canvas)149*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override { 150*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint; 151*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SK_ColorWHITE); 152*c8dee2aaSAndroid Build Coastguard Worker paint.setAntiAlias(true); 153*c8dee2aaSAndroid Build Coastguard Worker 154*c8dee2aaSAndroid Build Coastguard Worker MandolineSlicer mandoline({41, 43}); 155*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceCubic({5, 277}, {381, -74}, {243, 162}); 156*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceLine({41, 43}); 157*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(mandoline.path(), paint); 158*c8dee2aaSAndroid Build Coastguard Worker 159*c8dee2aaSAndroid Build Coastguard Worker mandoline.reset({357.049988f, 446.049988f}); 160*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceCubic({472.750000f, -71.950012f}, {639.750000f, 531.950012f}, 161*c8dee2aaSAndroid Build Coastguard Worker {309.049988f, 347.950012f}); 162*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceLine({309.049988f, 419}); 163*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceLine({357.049988f, 446.049988f}); 164*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(mandoline.path(), paint); 165*c8dee2aaSAndroid Build Coastguard Worker 166*c8dee2aaSAndroid Build Coastguard Worker canvas->save(); 167*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(421, 105); 168*c8dee2aaSAndroid Build Coastguard Worker canvas->scale(100, 81); 169*c8dee2aaSAndroid Build Coastguard Worker mandoline.reset({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))}); 170*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceConic({-2, 0}, 171*c8dee2aaSAndroid Build Coastguard Worker {-cosf(SkDegreesToRadians(60)), sinf(SkDegreesToRadians(60))}, .5f); 172*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceConic({-cosf(SkDegreesToRadians(120))*2, sinf(SkDegreesToRadians(120))*2}, 173*c8dee2aaSAndroid Build Coastguard Worker {1, 0}, .5f); 174*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceLine({0, 0}); 175*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceLine({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))}); 176*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(mandoline.path(), paint); 177*c8dee2aaSAndroid Build Coastguard Worker canvas->restore(); 178*c8dee2aaSAndroid Build Coastguard Worker 179*c8dee2aaSAndroid Build Coastguard Worker canvas->save(); 180*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(150, 300); 181*c8dee2aaSAndroid Build Coastguard Worker canvas->scale(75, 75); 182*c8dee2aaSAndroid Build Coastguard Worker mandoline.reset({1, 0}); 183*c8dee2aaSAndroid Build Coastguard Worker constexpr int nquads = 5; 184*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < nquads; ++i) { 185*c8dee2aaSAndroid Build Coastguard Worker float theta1 = 2*SK_ScalarPI/nquads * (i + .5f); 186*c8dee2aaSAndroid Build Coastguard Worker float theta2 = 2*SK_ScalarPI/nquads * (i + 1); 187*c8dee2aaSAndroid Build Coastguard Worker mandoline.sliceQuadratic({cosf(theta1)*2, sinf(theta1)*2}, 188*c8dee2aaSAndroid Build Coastguard Worker {cosf(theta2), sinf(theta2)}); 189*c8dee2aaSAndroid Build Coastguard Worker } 190*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(mandoline.path(), paint); 191*c8dee2aaSAndroid Build Coastguard Worker canvas->restore(); 192*c8dee2aaSAndroid Build Coastguard Worker } 193*c8dee2aaSAndroid Build Coastguard Worker }; 194*c8dee2aaSAndroid Build Coastguard Worker 195*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new SliverPathsGM;) 196*c8dee2aaSAndroid Build Coastguard Worker 197*c8dee2aaSAndroid Build Coastguard Worker } // namespace skiagm 198