2018-04-24 03:14:42 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2018 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gm.h"
|
2018-08-16 14:17:03 +00:00
|
|
|
#include "SkGeometry.h"
|
2018-04-24 03:14:42 +00:00
|
|
|
#include "SkPaint.h"
|
|
|
|
#include "SkPath.h"
|
|
|
|
#include "SkPoint.h"
|
2018-08-16 14:17:03 +00:00
|
|
|
#include "SkRandom.h"
|
2018-09-19 15:31:27 +00:00
|
|
|
|
2018-04-24 03:14:42 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
namespace skiagm {
|
|
|
|
|
|
|
|
// Slices paths into sliver-size contours shaped like ice cream cones.
|
|
|
|
class MandolineSlicer {
|
|
|
|
public:
|
|
|
|
static constexpr int kDefaultSubdivisions = 10;
|
|
|
|
|
|
|
|
MandolineSlicer(SkPoint anchorPt) {
|
|
|
|
fPath.setFillType(SkPath::kEvenOdd_FillType);
|
|
|
|
fPath.setIsVolatile(true);
|
|
|
|
this->reset(anchorPt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset(SkPoint anchorPt) {
|
|
|
|
fPath.reset();
|
|
|
|
fLastPt = fAnchorPt = anchorPt;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sliceLine(SkPoint pt, int numSubdivisions = kDefaultSubdivisions) {
|
|
|
|
if (numSubdivisions <= 0) {
|
|
|
|
fPath.moveTo(fAnchorPt);
|
|
|
|
fPath.lineTo(fLastPt);
|
|
|
|
fPath.lineTo(pt);
|
|
|
|
fPath.close();
|
|
|
|
fLastPt = pt;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
float T = this->chooseChopT(numSubdivisions);
|
|
|
|
if (0 == T) {
|
|
|
|
fPath.lineTo(fLastPt);
|
|
|
|
this->sliceLine(pt, numSubdivisions - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkPoint midpt = fLastPt * (1 - T) + pt * T;
|
|
|
|
this->sliceLine(midpt, numSubdivisions - 1);
|
|
|
|
this->sliceLine(pt, numSubdivisions - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sliceQuadratic(SkPoint p1, SkPoint p2, int numSubdivisions = kDefaultSubdivisions) {
|
|
|
|
if (numSubdivisions <= 0) {
|
|
|
|
fPath.moveTo(fAnchorPt);
|
|
|
|
fPath.lineTo(fLastPt);
|
|
|
|
fPath.quadTo(p1, p2);
|
|
|
|
fPath.close();
|
|
|
|
fLastPt = p2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
float T = this->chooseChopT(numSubdivisions);
|
|
|
|
if (0 == T) {
|
|
|
|
fPath.quadTo(fLastPt, fLastPt);
|
|
|
|
this->sliceQuadratic(p1, p2, numSubdivisions - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkPoint P[3] = {fLastPt, p1, p2}, PP[5];
|
|
|
|
SkChopQuadAt(P, PP, T);
|
|
|
|
this->sliceQuadratic(PP[1], PP[2], numSubdivisions - 1);
|
|
|
|
this->sliceQuadratic(PP[3], PP[4], numSubdivisions - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sliceCubic(SkPoint p1, SkPoint p2, SkPoint p3,
|
|
|
|
int numSubdivisions = kDefaultSubdivisions) {
|
|
|
|
if (numSubdivisions <= 0) {
|
|
|
|
fPath.moveTo(fAnchorPt);
|
|
|
|
fPath.lineTo(fLastPt);
|
|
|
|
fPath.cubicTo(p1, p2, p3);
|
|
|
|
fPath.close();
|
|
|
|
fLastPt = p3;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
float T = this->chooseChopT(numSubdivisions);
|
|
|
|
if (0 == T) {
|
|
|
|
fPath.cubicTo(fLastPt, fLastPt, fLastPt);
|
|
|
|
this->sliceCubic(p1, p2, p3, numSubdivisions - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkPoint P[4] = {fLastPt, p1, p2, p3}, PP[7];
|
|
|
|
SkChopCubicAt(P, PP, T);
|
|
|
|
this->sliceCubic(PP[1], PP[2], PP[3], numSubdivisions - 1);
|
|
|
|
this->sliceCubic(PP[4], PP[5], PP[6], numSubdivisions - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sliceConic(SkPoint p1, SkPoint p2, float w, int numSubdivisions = kDefaultSubdivisions) {
|
|
|
|
if (numSubdivisions <= 0) {
|
|
|
|
fPath.moveTo(fAnchorPt);
|
|
|
|
fPath.lineTo(fLastPt);
|
|
|
|
fPath.conicTo(p1, p2, w);
|
|
|
|
fPath.close();
|
|
|
|
fLastPt = p2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
float T = this->chooseChopT(numSubdivisions);
|
|
|
|
if (0 == T) {
|
|
|
|
fPath.conicTo(fLastPt, fLastPt, w);
|
|
|
|
this->sliceConic(p1, p2, w, numSubdivisions - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkConic conic(fLastPt, p1, p2, w), halves[2];
|
|
|
|
if (!conic.chopAt(T, halves)) {
|
|
|
|
SK_ABORT("SkConic::chopAt failed");
|
|
|
|
}
|
|
|
|
this->sliceConic(halves[0].fPts[1], halves[0].fPts[2], halves[0].fW, numSubdivisions - 1);
|
|
|
|
this->sliceConic(halves[1].fPts[1], halves[1].fPts[2], halves[1].fW, numSubdivisions - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const SkPath& path() const { return fPath; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
float chooseChopT(int numSubdivisions) {
|
|
|
|
SkASSERT(numSubdivisions > 0);
|
|
|
|
if (numSubdivisions > 1) {
|
|
|
|
return .5f;
|
|
|
|
}
|
|
|
|
float T = (0 == fRand.nextU() % 10) ? 0 : scalbnf(1, -(int)fRand.nextRangeU(10, 149));
|
|
|
|
SkASSERT(T >= 0 && T < 1);
|
|
|
|
return T;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkRandom fRand;
|
|
|
|
SkPath fPath;
|
|
|
|
SkPoint fAnchorPt;
|
|
|
|
SkPoint fLastPt;
|
|
|
|
};
|
|
|
|
|
|
|
|
class SliverPathsGM : public GM {
|
|
|
|
public:
|
|
|
|
SliverPathsGM() {
|
2018-08-16 14:17:03 +00:00
|
|
|
this->setBGColor(SK_ColorBLACK);
|
2018-04-24 03:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
|
|
return SkString("mandoline");
|
|
|
|
}
|
|
|
|
|
|
|
|
SkISize onISize() override {
|
|
|
|
return SkISize::Make(560, 475);
|
|
|
|
}
|
|
|
|
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorWHITE);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
|
|
|
|
MandolineSlicer mandoline({41, 43});
|
|
|
|
mandoline.sliceCubic({5, 277}, {381, -74}, {243, 162});
|
|
|
|
mandoline.sliceLine({41, 43});
|
|
|
|
canvas->drawPath(mandoline.path(), paint);
|
|
|
|
|
|
|
|
mandoline.reset({357.049988f, 446.049988f});
|
|
|
|
mandoline.sliceCubic({472.750000f, -71.950012f}, {639.750000f, 531.950012f},
|
|
|
|
{309.049988f, 347.950012f});
|
|
|
|
mandoline.sliceLine({309.049988f, 419});
|
|
|
|
mandoline.sliceLine({357.049988f, 446.049988f});
|
|
|
|
canvas->drawPath(mandoline.path(), paint);
|
|
|
|
|
|
|
|
canvas->save();
|
|
|
|
canvas->translate(421, 105);
|
|
|
|
canvas->scale(100, 81);
|
|
|
|
mandoline.reset({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))});
|
|
|
|
mandoline.sliceConic({-2, 0},
|
|
|
|
{-cosf(SkDegreesToRadians(60)), sinf(SkDegreesToRadians(60))}, .5f);
|
|
|
|
mandoline.sliceConic({-cosf(SkDegreesToRadians(120))*2, sinf(SkDegreesToRadians(120))*2},
|
|
|
|
{1, 0}, .5f);
|
|
|
|
mandoline.sliceLine({0, 0});
|
|
|
|
mandoline.sliceLine({-cosf(SkDegreesToRadians(-60)), sinf(SkDegreesToRadians(-60))});
|
|
|
|
canvas->drawPath(mandoline.path(), paint);
|
|
|
|
canvas->restore();
|
|
|
|
|
|
|
|
canvas->save();
|
|
|
|
canvas->translate(150, 300);
|
|
|
|
canvas->scale(75, 75);
|
|
|
|
mandoline.reset({1, 0});
|
|
|
|
constexpr int nquads = 5;
|
|
|
|
for (int i = 0; i < nquads; ++i) {
|
|
|
|
float theta1 = 2*SK_ScalarPI/nquads * (i + .5f);
|
|
|
|
float theta2 = 2*SK_ScalarPI/nquads * (i + 1);
|
|
|
|
mandoline.sliceQuadratic({cosf(theta1)*2, sinf(theta1)*2},
|
|
|
|
{cosf(theta2), sinf(theta2)});
|
|
|
|
}
|
|
|
|
canvas->drawPath(mandoline.path(), paint);
|
|
|
|
canvas->restore();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF_GM(return new SliverPathsGM;)
|
|
|
|
|
|
|
|
}
|