Hack in hittesting
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 1d9a53a..9cee3f7 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -1,13 +1,16 @@ #ifndef _RIVE_ARTBOARD_HPP_ #define _RIVE_ARTBOARD_HPP_ + #include "rive/animation/linear_animation.hpp" #include "rive/animation/state_machine.hpp" #include "rive/core_context.hpp" #include "rive/generated/artboard_base.hpp" +#include "rive/hit_info.hpp" #include "rive/math/aabb.hpp" #include "rive/renderer.hpp" #include "rive/shapes/shape_paint_container.hpp" #include <vector> + namespace rive { class File; class Drawable; @@ -56,6 +59,10 @@ Core* resolve(int id) const override; + // EXPERIMENTAL -- for internal testing only for now. + // DO NOT RELY ON THIS as it may change/disappear in the future. + Core* hitTest(HitInfo*, const Mat2D* = nullptr); + void onComponentDirty(Component* component); /// Update components that depend on each other in DAG order.
diff --git a/include/rive/drawable.hpp b/include/rive/drawable.hpp index d693baa..99fde46 100644 --- a/include/rive/drawable.hpp +++ b/include/rive/drawable.hpp
@@ -1,6 +1,7 @@ #ifndef _RIVE_DRAWABLE_HPP_ #define _RIVE_DRAWABLE_HPP_ #include "rive/generated/drawable_base.hpp" +#include "rive/hit_info.hpp" #include "rive/renderer.hpp" #include <vector> @@ -24,6 +25,7 @@ BlendMode blendMode() const { return (BlendMode)blendModeValue(); } bool clip(Renderer* renderer) const; virtual void draw(Renderer* renderer) = 0; + virtual Core* hitTest(HitInfo*, const Mat2D&) = 0; void addClippingShape(ClippingShape* shape); inline const std::vector<ClippingShape*>& clippingShapes() const { return m_ClippingShapes; @@ -37,4 +39,4 @@ }; } // namespace rive -#endif \ No newline at end of file +#endif
diff --git a/include/rive/hit_info.hpp b/include/rive/hit_info.hpp new file mode 100644 index 0000000..9ca9136 --- /dev/null +++ b/include/rive/hit_info.hpp
@@ -0,0 +1,21 @@ +/* + * Copyright 2022 Rive + */ + +#ifndef _RIVE_HITINFO_HPP_ +#define _RIVE_HITINFO_HPP_ + +#include "rive/math/aabb.hpp" +#include <vector> + +namespace rive { + +class NestedArtboard; + +struct HitInfo { + IAABB area; // input + std::vector<NestedArtboard*> mounts; // output +}; + +} +#endif
diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp index b7deee4..76cb46a 100644 --- a/include/rive/nested_artboard.hpp +++ b/include/rive/nested_artboard.hpp
@@ -1,7 +1,10 @@ #ifndef _RIVE_NESTED_ARTBOARD_HPP_ #define _RIVE_NESTED_ARTBOARD_HPP_ + #include "rive/generated/nested_artboard_base.hpp" +#include "rive/hit_info.hpp" #include <stdio.h> + namespace rive { class NestedAnimation; class NestedArtboard : public NestedArtboardBase { @@ -14,6 +17,7 @@ ~NestedArtboard(); StatusCode onAddedClean(CoreContext* context) override; void draw(Renderer* renderer) override; + Core* hitTest(HitInfo*, const Mat2D&) override; void addNestedAnimation(NestedAnimation* nestedAnimation); void nest(Artboard* artboard); @@ -25,4 +29,4 @@ }; } // namespace rive -#endif \ No newline at end of file +#endif
diff --git a/include/rive/shapes/image.hpp b/include/rive/shapes/image.hpp index 491d9ec..364a3f3 100644 --- a/include/rive/shapes/image.hpp +++ b/include/rive/shapes/image.hpp
@@ -1,7 +1,10 @@ #ifndef _RIVE_IMAGE_HPP_ #define _RIVE_IMAGE_HPP_ + +#include "rive/hit_info.hpp" #include "rive/generated/shapes/image_base.hpp" #include "rive/assets/file_asset_referencer.hpp" + namespace rive { class ImageAsset; class Mesh; @@ -15,10 +18,11 @@ void setMesh(Mesh* mesh); ImageAsset* imageAsset() const { return m_ImageAsset; } void draw(Renderer* renderer) override; + Core* hitTest(HitInfo*, const Mat2D&) override; StatusCode import(ImportStack& importStack) override; void assets(const std::vector<FileAsset*>& assets) override; Core* clone() const override; }; } // namespace rive -#endif \ No newline at end of file +#endif
diff --git a/include/rive/shapes/shape.hpp b/include/rive/shapes/shape.hpp index 6361a2f..4112d46 100644 --- a/include/rive/shapes/shape.hpp +++ b/include/rive/shapes/shape.hpp
@@ -1,5 +1,7 @@ #ifndef _RIVE_SHAPE_HPP_ #define _RIVE_SHAPE_HPP_ + +#include "rive/hit_info.hpp" #include "rive/generated/shapes/shape_base.hpp" #include "rive/shapes/path_composer.hpp" #include "rive/shapes/shape_paint_container.hpp" @@ -25,6 +27,7 @@ void update(ComponentDirt value) override; void draw(Renderer* renderer) override; + Core* hitTest(HitInfo*, const Mat2D&) override; PathComposer* pathComposer() const { return (PathComposer*)&m_PathComposer; @@ -36,4 +39,4 @@ }; } // namespace rive -#endif \ No newline at end of file +#endif
diff --git a/src/artboard.cpp b/src/artboard.cpp index b7b8ed6..090f776 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp
@@ -13,6 +13,8 @@ #include "rive/importers/import_stack.hpp" #include "rive/importers/backboard_importer.hpp" #include "rive/nested_artboard.hpp" + +#include <stack> #include <unordered_map> using namespace rive; @@ -364,6 +366,37 @@ return updateComponents(); } +Core* Artboard::hitTest(HitInfo* hinfo, const Mat2D* xform) { + if (clip()) { + // TODO: can we get the rawpath for the clip? + } + + auto mx = xform ? *xform : Mat2D(); + if (m_FrameOrigin) { + mx *= Mat2D::fromTranslate(width() * originX(), height() * originY()); + } + + Drawable* last = m_FirstDrawable; + if (last) { + // walk to the end, so we can visit in reverse-order + while (last->prev) { + last = last->prev; + } + } + for (auto drawable = last; drawable; drawable = drawable->next) { + if (drawable->isHidden()) { + continue; + } + if (auto c = drawable->hitTest(hinfo, mx)) { + return c; + } + } + + // TODO: should we hit-test the background? + + return nullptr; +} + void Artboard::draw(Renderer* renderer, DrawOption option) { renderer->save(); if (clip()) {
diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp index bd5c608..a4658ad 100644 --- a/src/nested_artboard.cpp +++ b/src/nested_artboard.cpp
@@ -28,18 +28,34 @@ m_NestedInstance->advance(0.0f); } +static Mat2D makeTranslate(const Artboard* artboard) { + return Mat2D::fromTranslate(-artboard->originX() * artboard->width(), + -artboard->originY() * artboard->height()); +} + void NestedArtboard::draw(Renderer* renderer) { if (m_NestedInstance == nullptr) { return; } renderer->save(); - renderer->transform(worldTransform()); - renderer->translate(-m_NestedInstance->originX() * m_NestedInstance->width(), - -m_NestedInstance->originY() * m_NestedInstance->height()); + renderer->transform(worldTransform() * makeTranslate(m_NestedInstance)); m_NestedInstance->draw(renderer); renderer->restore(); } +Core* NestedArtboard::hitTest(HitInfo* hinfo, const Mat2D& xform) { + if (m_NestedInstance == nullptr) { + return nullptr; + } + hinfo->mounts.push_back(this); + auto mx = xform * worldTransform() * makeTranslate(m_NestedInstance); + if (auto c = m_NestedInstance->hitTest(hinfo, &mx)) { + return c; + } + hinfo->mounts.pop_back(); + return nullptr; +} + StatusCode NestedArtboard::import(ImportStack& importStack) { auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey);
diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp index bb8ac07..6fe50c6 100644 --- a/src/shapes/image.cpp +++ b/src/shapes/image.cpp
@@ -1,3 +1,4 @@ +#include "rive/math/hit_test.hpp" #include "rive/shapes/image.hpp" #include "rive/backboard.hpp" #include "rive/importers/backboard_importer.hpp" @@ -33,6 +34,28 @@ renderer->restore(); } +Core* Image::hitTest(HitInfo* hinfo, const Mat2D& xform) { + // TODO: handle clip? + + auto renderImage = m_ImageAsset->renderImage(); + auto width = renderImage->width(); + auto height = renderImage->height(); + + if (m_Mesh) { + printf("Missing mesh\n"); + // TODO: hittest mesh + } else { + auto mx = xform * worldTransform() * Mat2D::fromTranslate(-width*0.5f, -height*0.5f); + HitTester tester(hinfo->area); + tester.addRect(AABB(0, 0, width, height), mx); + if (tester.test()) { + return this; + } + } + return nullptr; +} + + StatusCode Image::import(ImportStack& importStack) { auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey); @@ -61,4 +84,4 @@ } void Image::setMesh(Mesh* mesh) { m_Mesh = mesh; } -Mesh* Image::mesh() const { return m_Mesh; } \ No newline at end of file +Mesh* Image::mesh() const { return m_Mesh; }
diff --git a/src/shapes/shape.cpp b/src/shapes/shape.cpp index 78fbb30..b7c4cdc 100644 --- a/src/shapes/shape.cpp +++ b/src/shapes/shape.cpp
@@ -1,3 +1,5 @@ +#include "rive/hittest_command_path.hpp" +#include "rive/shapes/path.hpp" #include "rive/shapes/shape.hpp" #include "rive/shapes/clipping_shape.hpp" #include "rive/shapes/paint/blend_mode.hpp" @@ -57,6 +59,48 @@ } } +Core* Shape::hitTest(HitInfo* hinfo, const Mat2D& xform) { + if (renderOpacity() == 0.0f) { + return nullptr; + } + + // TODO: clip: + + const bool shapeIsLocal = (pathSpace() & PathSpace::Local) == PathSpace::Local; + + for (auto rit = m_ShapePaints.rbegin(); rit != m_ShapePaints.rend(); ++rit) { + auto shapePaint = *rit; + if (shapePaint->isTranslucent()) { + continue; + } + if (!shapePaint->isVisible()) { + continue; + } + + auto paintIsLocal = (shapePaint->pathSpace() & PathSpace::Local) == PathSpace::Local; + + auto mx = xform; + if (paintIsLocal) { + mx *= worldTransform(); + } + + HitTestCommandPath tester(hinfo->area); + + for (auto path : m_Paths) { + if (shapeIsLocal) { + tester.setXform(xform * path->pathTransform()); + } else { + tester.setXform(mx * path->pathTransform()); + } + path->buildPath(tester); + } + if (tester.wasHit()) { + return this; + } + } + return nullptr; +} + void Shape::buildDependencies() { // Make sure to propagate the call to PathComposer as it's no longer part of // Core and owned only by the Shape.