From 112aa2d22e6c59da10cfc4a1461406230b1c63e9 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Fri, 15 Feb 2019 10:45:56 -0500 Subject: [PATCH] Add curve visualization back, guard eval Editing curves is still cumbersome, but that's fine. Visualization is just for feedback (and imgui's path renderer is a little wonky), but this helps a bit. Bug: skia: Change-Id: I3dace6d822d472314513bb1ad72bcea1e8991b77 Reviewed-on: https://skia-review.googlesource.com/c/192828 Reviewed-by: Brian Osman Commit-Queue: Brian Osman --- modules/particles/include/SkCurve.h | 1 + modules/particles/include/SkReflected.h | 7 ++ modules/particles/src/SkCurve.cpp | 20 +++++ tools/viewer/ParticlesSlide.cpp | 104 +++++++++++------------- 4 files changed, 74 insertions(+), 58 deletions(-) diff --git a/modules/particles/include/SkCurve.h b/modules/particles/include/SkCurve.h index cca41a1a90..a3686457a3 100644 --- a/modules/particles/include/SkCurve.h +++ b/modules/particles/include/SkCurve.h @@ -39,6 +39,7 @@ struct SkCurve { SkScalar eval(SkScalar x, SkRandom& random) const; void visitFields(SkFieldVisitor* v); + void getExtents(SkScalar extents[2]) const; SkTArray fXValues; SkTArray fSegments; diff --git a/modules/particles/include/SkReflected.h b/modules/particles/include/SkReflected.h index 93a18a0da4..a0b20d011e 100644 --- a/modules/particles/include/SkReflected.h +++ b/modules/particles/include/SkReflected.h @@ -9,6 +9,7 @@ #define SkReflected_DEFINED #include "SkColor.h" +#include "SkCurve.h" #include "SkRefCnt.h" #include "SkString.h" #include "SkTArray.h" @@ -118,6 +119,12 @@ public: virtual void visit(const char*, SkPoint&, SkField = SkField()) = 0; virtual void visit(const char*, SkColor4f&, SkField = SkField()) = 0; + virtual void visit(const char* name, SkCurve& c, SkField = SkField()) { + this->enterObject(name); + c.visitFields(this); + this->exitObject(); + } + template void visit(const char* name, T& value) { this->enterObject(name); diff --git a/modules/particles/src/SkCurve.cpp b/modules/particles/src/SkCurve.cpp index a206e76ed0..8bebc90f9e 100644 --- a/modules/particles/src/SkCurve.cpp +++ b/modules/particles/src/SkCurve.cpp @@ -53,6 +53,9 @@ SkScalar SkCurve::eval(SkScalar x, SkRandom& random) const { SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1]; SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i]; SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin); + if (!SkScalarIsFinite(segmentX)) { + segmentX = rangeMin; + } SkASSERT(0.0f <= segmentX && segmentX <= 1.0f); return fSegments[i].eval(segmentX, random); } @@ -70,3 +73,20 @@ void SkCurve::visitFields(SkFieldVisitor* v) { fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f); } } + +void SkCurve::getExtents(SkScalar extents[2]) const { + extents[0] = INFINITY; + extents[1] = -INFINITY; + auto extend = [=](SkScalar y) { + extents[0] = SkTMin(extents[0], y); + extents[1] = SkTMax(extents[1], y); + }; + for (const auto& segment : fSegments) { + for (int i = 0; i < (segment.fConstant ? 1 : 4); ++i) { + extend(segment.fMin[i]); + if (segment.fRanged) { + extend(segment.fMax[i]); + } + } + } +} diff --git a/tools/viewer/ParticlesSlide.cpp b/tools/viewer/ParticlesSlide.cpp index 420570f949..928b390e3c 100644 --- a/tools/viewer/ParticlesSlide.cpp +++ b/tools/viewer/ParticlesSlide.cpp @@ -7,6 +7,7 @@ #include "ParticlesSlide.h" +#include "ImGuiLayer.h" #include "SkParticleAffector.h" #include "SkParticleEffect.h" #include "SkParticleEmitter.h" @@ -38,55 +39,6 @@ static int InputTextCallback(ImGuiInputTextCallbackData* data) { return 0; } -#if 0 -static ImVec2 map_point(float x, float y, ImVec2 pos, ImVec2 size, float yMin, float yMax) { - // Turn y into 0 - 1 value - float yNorm = 1.0f - ((y - yMin) / (yMax - yMin)); - return ImVec2(pos.x + size.x * x, pos.y + size.y * yNorm); -} - -static void ImGui_DrawCurve(SkScalar* pts) { - ImDrawList* drawList = ImGui::GetWindowDrawList(); - - // Fit our image/canvas to the available width, and scale the height to maintain aspect ratio. - float canvasWidth = SkTMax(ImGui::GetContentRegionAvailWidth(), 50.0f); - ImVec2 size = ImVec2(canvasWidth, canvasWidth); - ImVec2 pos = ImGui::GetCursorScreenPos(); - - // Background rectangle - drawList->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0, 0, 0, 128)); - - // Determine min/max extents - float yMin = pts[0], yMax = pts[0]; - for (int i = 1; i < 4; ++i) { - yMin = SkTMin(yMin, pts[i]); - yMax = SkTMax(yMax, pts[i]); - } - - // Grow the extents by 10%, at least 1.0f - float grow = SkTMax((yMax - yMin) * 0.1f, 1.0f); - - yMin -= grow; - yMax += grow; - - ImVec2 a = map_point(0.0f , pts[0], pos, size, yMin, yMax), - b = map_point(1 / 3.0f, pts[1], pos, size, yMin, yMax), - c = map_point(2 / 3.0f, pts[2], pos, size, yMin, yMax), - d = map_point(1.0f , pts[3], pos, size, yMin, yMax); - - drawList->AddBezierCurve(a, b, c, d, IM_COL32(255, 255, 255, 255), 1.0f); - - // Draw markers - drawList->AddCircle(a, 5.0f, 0xFFFFFFFF); - drawList->AddCircle(b, 5.0f, 0xFFFFFFFF); - drawList->AddCircle(c, 5.0f, 0xFFFFFFFF); - drawList->AddCircle(d, 5.0f, 0xFFFFFFFF); - - ImGui::SetCursorScreenPos(ImVec2(pos.x, pos.y + size.y)); - ImGui::Spacing(); -} -#endif - class SkGuiVisitor : public SkFieldVisitor { public: SkGuiVisitor() { @@ -130,22 +82,58 @@ public: #undef IF_OPEN - /* void visit(const char* name, SkCurve& c, SkField) override { this->enterObject(item(name)); if (fTreeStack.back()) { - ImGui::Checkbox("Ranged", &c.fRanged); - ImGui::DragFloat4("Min", c.fMin); - ImGui_DrawCurve(c.fMin); - if (c.fRanged) { - ImGui::DragFloat4("Max", c.fMax); - ImGui_DrawCurve(c.fMax); - } + // Get vertical extents of the curve + SkScalar extents[2]; + c.getExtents(extents); + // Grow the extents by 10%, at least 1.0f + SkScalar grow = SkTMax((extents[1] - extents[0]) * 0.1f, 1.0f); + extents[0] -= grow; + extents[1] += grow; + + { + ImGui::DragCanvas dc(&c, { 0.0f, extents[1] }, { 1.0f, extents[0] }, 0.5f); + dc.fillColor(IM_COL32(0, 0, 0, 128)); + + for (int i = 0; i < c.fSegments.count(); ++i) { + SkSTArray<8, ImVec2, true> pts; + SkScalar rangeMin = (i == 0) ? 0.0f : c.fXValues[i - 1]; + SkScalar rangeMax = (i == c.fXValues.count()) ? 1.0f : c.fXValues[i]; + auto screenPoint = [&](int idx, bool useMax) { + SkScalar xVal = rangeMin + (idx / 3.0f) * (rangeMax - rangeMin); + SkScalar* yVals = useMax ? c.fSegments[i].fMax : c.fSegments[i].fMin; + SkScalar yVal = yVals[c.fSegments[i].fConstant ? 0 : idx]; + SkPoint pt = dc.fLocalToScreen.mapXY(xVal, yVal); + return ImVec2(pt.fX, pt.fY); + }; + for (int i = 0; i < 4; ++i) { + pts.push_back(screenPoint(i, false)); + } + if (c.fSegments[i].fRanged) { + for (int i = 3; i >= 0; --i) { + pts.push_back(screenPoint(i, true)); + } + } + + if (c.fSegments[i].fRanged) { + dc.fDrawList->PathLineTo(pts[0]); + dc.fDrawList->PathBezierCurveTo(pts[1], pts[2], pts[3]); + dc.fDrawList->PathLineTo(pts[4]); + dc.fDrawList->PathBezierCurveTo(pts[5], pts[6], pts[7]); + dc.fDrawList->PathFillConvex(IM_COL32(255, 255, 255, 128)); + } else { + dc.fDrawList->AddBezierCurve(pts[0], pts[1], pts[2], pts[3], + IM_COL32(255, 255, 255, 255), 1.0f); + } + } + } + c.visitFields(this); } this->exitObject(); } - */ void visit(sk_sp& e, const SkReflected::Type* baseType) override { if (fTreeStack.back()) {