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.