aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Mike Reed <reed@google.com>2018-04-06 19:56:50 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-04-10 11:12:22 +0000
commit66fa45616c5e2d7449361f05d1522f02e70bdfd9 (patch)
treed0dafc425aa54056435a2bd85b54519b679ac7cf
parent1eeeae0491bac9ee76e785245f4a6aaa11f7a540 (diff)
identify interesting cubic points
Bug: skia: Change-Id: Ib649fe2c487c4f0183c2e7e5f5aadd7381a865ba Reviewed-on: https://skia-review.googlesource.com/117623 Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Mike Reed <reed@google.com>
-rw-r--r--samplecode/SamplePath.cpp256
1 files changed, 256 insertions, 0 deletions
diff --git a/samplecode/SamplePath.cpp b/samplecode/SamplePath.cpp
index 56bdbffc77..11b4167ac6 100644
--- a/samplecode/SamplePath.cpp
+++ b/samplecode/SamplePath.cpp
@@ -453,3 +453,259 @@ private:
typedef SampleView INHERITED;
};
DEF_SAMPLE( return new FatStroke; )
+
+static int compute_parallel_to_base(const SkPoint pts[4], SkScalar t[2]) {
+ // F = At^3 + Bt^2 + Ct + D
+ SkVector A = pts[3] - pts[0] + (pts[1] - pts[2]) * 3.0f;
+ SkVector B = (pts[0] - pts[1] - pts[1] + pts[2]) * 3.0f;
+ SkVector C = (pts[1] - pts[0]) * 3.0f;
+ SkVector DA = pts[3] - pts[0];
+
+ // F' = 3At^2 + 2Bt + C
+ SkScalar a = 3 * A.cross(DA);
+ SkScalar b = 2 * B.cross(DA);
+ SkScalar c = C.cross(DA);
+
+ int n = SkFindUnitQuadRoots(a, b, c, t);
+ SkString str;
+ for (int i = 0; i < n; ++i) {
+ str.appendf(" %g", t[i]);
+ }
+ SkDebugf("roots %s\n", str.c_str());
+ return n;
+}
+
+class CubicCurve : public SampleView {
+public:
+ enum {
+ N = 4
+ };
+ SkPoint fPts[N];
+
+ CubicCurve() {
+ SkRandom rand;
+ for (int i = 0; i < N; ++i) {
+ fPts[i].fX = 20 + rand.nextUScalar1() * 640;
+ fPts[i].fY = 20 + rand.nextUScalar1() * 480;
+ }
+ }
+
+protected:
+ bool onQuery(SkEvent* evt) override {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "CubicCurve");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ {
+ SkPath path;
+ path.moveTo(fPts[0]);
+ path.cubicTo(fPts[1], fPts[2], fPts[3]);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawPath(path, paint);
+ }
+
+ {
+ paint.setColor(SK_ColorRED);
+ SkScalar t[2];
+ int n = compute_parallel_to_base(fPts, t);
+ SkPoint loc;
+ SkVector tan;
+ for (int i = 0; i < n; ++i) {
+ SkEvalCubicAt(fPts, t[i], &loc, &tan, nullptr);
+ tan.setLength(30);
+ canvas->drawLine(loc - tan, loc + tan, paint);
+ }
+ paint.setStrokeWidth(0.5f);
+ canvas->drawLine(fPts[0], fPts[3], paint);
+
+ paint.setColor(SK_ColorBLUE);
+ paint.setStrokeWidth(6);
+ SkEvalCubicAt(fPts, 0.5f, &loc, nullptr, nullptr);
+ canvas->drawPoint(loc, paint);
+
+ paint.setColor(0xFF008800);
+ SkEvalCubicAt(fPts, 1.0f/3, &loc, nullptr, nullptr);
+ canvas->drawPoint(loc, paint);
+ SkEvalCubicAt(fPts, 2.0f/3, &loc, nullptr, nullptr);
+ canvas->drawPoint(loc, paint);
+
+ // n = SkFindCubicInflections(fPts, t);
+ // printf("inflections %d %g %g\n", n, t[0], t[1]);
+ }
+
+ {
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorRED);
+ for (SkPoint p : fPts) {
+ canvas->drawCircle(p.fX, p.fY, 8, paint);
+ }
+ }
+ }
+
+ bool onClick(Click* click) override {
+ int32_t index;
+ if (click->fMeta.findS32("index", &index)) {
+ SkASSERT((unsigned)index < N);
+ fPts[index] = click->fCurr;
+ return true;
+ }
+ return false;
+ }
+
+ SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
+ const SkScalar tol = 8;
+ const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
+ for (int i = 0; i < N; ++i) {
+ if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
+ Click* click = new Click(this);
+ click->fMeta.setS32("index", i);
+ return click;
+ }
+ }
+ return this->INHERITED::onFindClickHandler(x, y, modi);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+DEF_SAMPLE( return new CubicCurve; )
+
+static SkPoint lerp(SkPoint a, SkPoint b, float t) {
+ return a * (1 - t) + b * t;
+}
+
+class CubicCurve2 : public SampleView {
+public:
+ enum {
+ N = 7
+ };
+ SkPoint fPts[N];
+ SkPoint* fQuad = fPts + 4;
+ SkScalar fT = 0.5f;
+ bool fShowSub = false;
+
+ CubicCurve2() {
+ fPts[0] = { 90, 300 };
+ fPts[1] = { 30, 60 };
+ fPts[2] = { 250, 30 };
+ fPts[3] = { 350, 200 };
+
+ fQuad[0] = fPts[0] + SkVector{ 300, 0};
+ fQuad[1] = fPts[1] + SkVector{ 300, 0};
+ fQuad[2] = fPts[2] + SkVector{ 300, 0};
+ }
+
+protected:
+ bool onQuery(SkEvent* evt) override {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "CubicCurve2");
+ return true;
+ }
+ SkUnichar uni;
+ if (SampleCode::CharQ(*evt, &uni)) {
+ switch (uni) {
+ case 's': fShowSub = !fShowSub; break;
+ case '-': fT -= 1.0f / 32; break;
+ case '=': fT += 1.0f / 32; break;
+ default: goto DONE;
+ }
+ fT = std::min(1.0f, std::max(0.0f, fT));
+ return true;
+ }
+ DONE:
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void showFrame(SkCanvas* canvas, const SkPoint pts[], int count, const SkPaint& p) {
+ SkPaint paint(p);
+ SkPoint storage[3 + 2 + 1];
+ SkPoint* tmp = storage;
+ const SkPoint* prev = pts;
+ int n = count;
+ for (int n = count; n > 0; --n) {
+ for (int i = 0; i < n; ++i) {
+ canvas->drawLine(prev[i], prev[i+1], paint);
+ tmp[i] = lerp(prev[i], prev[i+1], fT);
+ }
+ prev = tmp;
+ tmp += n;
+ }
+
+ paint.setColor(SK_ColorBLUE);
+ paint.setStyle(SkPaint::kFill_Style);
+ n = tmp - storage;
+ for (int i = 0; i < n; ++i) {
+ canvas->drawCircle(storage[i].fX, storage[i].fY, 4, paint);
+ }
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ {
+ paint.setStyle(SkPaint::kStroke_Style);
+ SkPath path;
+ path.moveTo(fPts[0]);
+ path.cubicTo(fPts[1], fPts[2], fPts[3]);
+ path.moveTo(fQuad[0]);
+ path.quadTo(fQuad[1], fQuad[2]);
+ canvas->drawPath(path, paint);
+ }
+
+ if (fShowSub) {
+ paint.setColor(SK_ColorRED);
+ paint.setStrokeWidth(1.7f);
+ this->showFrame(canvas, fPts, 3, paint);
+ this->showFrame(canvas, fQuad, 2, paint);
+
+ SkString str;
+ str.printf("t = %g", fT);
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setTextSize(20);
+ canvas->drawText(str.c_str(), str.size(), 20, 20, paint);
+ }
+
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorRED);
+ for (SkPoint p : fPts) {
+ canvas->drawCircle(p.fX, p.fY, 7, paint);
+ }
+ }
+
+ bool onClick(Click* click) override {
+ int32_t index;
+ if (click->fMeta.findS32("index", &index)) {
+ SkASSERT((unsigned)index < N);
+ fPts[index] = click->fCurr;
+ return true;
+ }
+ return false;
+ }
+
+ SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
+ const SkScalar tol = 8;
+ const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
+ for (int i = 0; i < N; ++i) {
+ if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
+ Click* click = new Click(this);
+ click->fMeta.setS32("index", i);
+ return click;
+ }
+ }
+ return this->INHERITED::onFindClickHandler(x, y, modi);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+DEF_SAMPLE( return new CubicCurve2; )
+