| /* | |
| * Copyright 2022 Rive | |
| * Copyright 2022 Rive | |
| */ | |
| #include "gm.hpp" | |
| #include "gmutils.hpp" | |
| #include "bicubic.hpp" | |
| #include "rive/math/math_types.hpp" | |
| #include "rive/math/hit_test.hpp" | |
| #include "assets/nomoon.png.hpp" | |
| class Random | |
| { | |
| // See "Numerical Recipes in C", 1992 page 284 for these constants | |
| // For the LCG that sets the initial state from a seed | |
| enum | |
| { | |
| kMul = 1664525, | |
| kAdd = 1013904223 | |
| }; | |
| uint32_t m_Seed; | |
| public: | |
| Random(uint32_t seed = 0) : m_Seed(seed) {} | |
| uint32_t nextU() | |
| { | |
| m_Seed = m_Seed * kMul + kAdd; | |
| return m_Seed; | |
| } | |
| float nextF() | |
| { | |
| double x = this->nextU() * (1.0 / (1 << 30)); | |
| return (float)(x - std::floor(x)); | |
| } | |
| float nextF(float min, float max) | |
| { | |
| return this->nextF() * (max - min) + min; | |
| } | |
| }; | |
| using namespace rivegm; | |
| static void make_patch(rive::Vec2D pts[16], const rive::AABB& r) | |
| { | |
| const float sx = r.width() / 3; | |
| const float sy = r.height() / 3; | |
| for (int y = 0; y < 4; ++y) | |
| { | |
| for (int x = 0; x < 4; ++x) | |
| { | |
| const int index = y * 4 + x; | |
| pts[index] = { | |
| r.left() + x * sx, | |
| r.top() + y * sy, | |
| }; | |
| } | |
| } | |
| } | |
| static void perterb_patch(rive::Vec2D pts[16], float scale) | |
| { | |
| Random rand; | |
| auto rf = [&]() { return rand.nextF(-scale, scale); }; | |
| for (int i = 0; i < 16; ++i) | |
| { | |
| auto dx = rf(); | |
| auto dy = rf(); | |
| pts[i] += {dx, dy}; | |
| } | |
| } | |
| class MeshGM : public GM | |
| { | |
| public: | |
| MeshGM() : GM(800, 600) {} | |
| void onDraw(rive::Renderer* ren) override | |
| { | |
| auto img = LoadImage(assets::nomoon_png()); | |
| constexpr float kGridCellSize = 20; | |
| Path grid; | |
| grid->fillRule(rive::FillRule::evenOdd); | |
| for (size_t v = 0; v < 400 / (kGridCellSize * 2); ++v) | |
| { | |
| grid->addRect(0, v * kGridCellSize * 2, 1600, kGridCellSize); | |
| } | |
| for (size_t u = 0; u < 1600 / (kGridCellSize * 2); ++u) | |
| { | |
| grid->addRect(u * kGridCellSize * 2, 0, kGridCellSize, 400); | |
| } | |
| Paint gray; | |
| gray->color(0xa0a0a0a0); | |
| BicubicPatch patch; | |
| rive::AABB r = {50, 50, 350, 350}; | |
| make_patch(patch.m_Pts, r); | |
| perterb_patch(patch.m_Pts, 50); | |
| auto mesh = patch.mesh(TestingWindow::Get()->factory()); | |
| uint32_t vertexCount = | |
| mesh.pts ? rive::math::lossless_numeric_cast<uint32_t>( | |
| mesh.pts->sizeInBytes() / sizeof(rive::Vec2D)) | |
| : 0; | |
| uint32_t indexCount = | |
| mesh.indices ? rive::math::lossless_numeric_cast<uint32_t>( | |
| mesh.indices->sizeInBytes() / sizeof(uint16_t)) | |
| : 0; | |
| ren->scale(.5f, .5f); | |
| constexpr rive::BlendMode blendModes[] = {rive::BlendMode::srcOver, | |
| rive::BlendMode::darken, | |
| rive::BlendMode::luminosity, | |
| rive::BlendMode::exclusion}; | |
| // Draw meshes that exercise all variations in the mesh shader: | |
| // viewMatrix, opacity, blendMode, clipRect, clip. | |
| for (size_t j = 0; j < 3; ++j) | |
| { | |
| if (j == 1) | |
| { | |
| ren->clipPath( | |
| PathBuilder::Rect({50, 120, 1600 - 50, 800 - 120})); | |
| } | |
| ren->drawPath(grid, gray); | |
| ren->save(); | |
| for (size_t i = 0; i < 4; ++i) | |
| { | |
| ren->save(); | |
| ren->translate(200, 200); | |
| ren->rotate(i); | |
| ren->translate(-200, -200); | |
| if (j == 2) | |
| { | |
| ren->clipPath(PathBuilder::Circle(200, 200, 150)); | |
| } | |
| if (img != nullptr) | |
| { | |
| ren->drawImageMesh(img.get(), | |
| rive::ImageSampler::LinearClamp(), | |
| mesh.pts, | |
| mesh.uvs, | |
| mesh.indices, | |
| vertexCount, | |
| indexCount, | |
| blendModes[i], | |
| 1.f - .1f * (i + 1)); | |
| } | |
| ren->restore(); | |
| ren->translate(400, 0); | |
| } | |
| ren->restore(); | |
| ren->translate(0, 400); | |
| } | |
| if (false) | |
| { | |
| Paint paint; | |
| for (int i = 0; i < 16; ++i) | |
| { | |
| auto p = patch.m_Pts[i]; | |
| draw_rect(ren, {p.x - 2, p.y - 2, p.x + 3, p.y + 3}, paint); | |
| } | |
| } | |
| } | |
| }; | |
| GMREGISTER(mesh, return new MeshGM) | |
| class MeshHitTestGM : public GM | |
| { | |
| BicubicPatch::Rec rec; | |
| rive::AABB r = {15, 15, 45, 45}; | |
| int fN; | |
| public: | |
| MeshHitTestGM(int N) : GM(60, 60) { fN = N; } | |
| void onOnceBeforeDraw() override | |
| { | |
| BicubicPatch patch; | |
| make_patch(patch.m_Pts, r); | |
| perterb_patch(patch.m_Pts, 6); | |
| rec = patch.buffers(); | |
| } | |
| void onDraw(rive::Renderer* ren) override | |
| { | |
| Paint paint; | |
| for (float y = 0; y < 60; y += 1) | |
| { | |
| for (float x = 0; x < 60; x += 1) | |
| { | |
| auto area = rive::AABB(x, y, x + fN, y + fN); | |
| rive::ColorInt color = 0xFF000000; | |
| auto ia = area.round(); | |
| if (rive::HitTester::testMesh(ia, rec.pts, rec.indices)) | |
| { | |
| color = 0xFFFFFFFF; | |
| } | |
| paint->color(color); | |
| draw_rect(ren, area, paint.get()); | |
| } | |
| } | |
| } | |
| }; | |
| GMREGISTER(mesh_ht_7, return new MeshHitTestGM(7)) | |
| GMREGISTER(mesh_ht_1, return new MeshHitTestGM(1)) |