Skip to content

Commit

Permalink
Unity compute bounds
Browse files Browse the repository at this point in the history
This is something that the Pocketwatch team requested but I found myself needing it when doing a Unity test this weekend.

It adds the ability to dynamically retrieve the dimensions of any Shape from C++.

In Unity, it uses a similar API to GameKit where it exposes elements of the Artboard as "Components" and then you can call different methods that will only function on certain types but it hides needing to expose all different kinds of types to the end user.

I also added the ability to change a run's value so I could check that the shape was resizing properly from Unity. @HayesGordon we really should land your text run api too, but we should modify it to use the same component logic (might require changing from the base TransformComponent to just Component in the C++ plugin code).

The Unity Artboard Components are wrapped and track the owning Artboard. This is to make sure that if the C# code releases all references of Artboard while someone is still holding a Component, Artboard will still be available at the C++ level (so you can't accidentally create a race condition that causes a crash in native). I didn't need to do this for the Text Runs as I just have a set api that doesn't store the TextValueRun reference in Unity yet.

Diffs=
5cb42a9b0 Unity compute bounds (#6649)

Co-authored-by: Luigi Rosso <[email protected]>
  • Loading branch information
luigi-rosso and luigi-rosso committed Feb 20, 2024
1 parent a040296 commit c6d407a
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b765280df32889e6df18690d840fe8f240871c09
5cb42a9b0033a1b9d2360292b7ef14bf67e3d45e
3 changes: 3 additions & 0 deletions include/rive/shapes/shape.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class Shape : public ShapeBase, public ShapePaintContainer
StatusCode onAddedDirty(CoreContext* context) override;
bool isEmpty();
void pathCollapseChanged();

AABB computeWorldBounds(const Mat2D* xform = nullptr) const;
AABB computeLocalBounds() const;
};
} // namespace rive

Expand Down
74 changes: 74 additions & 0 deletions src/shapes/shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "rive/shapes/paint/shape_paint.hpp"
#include "rive/shapes/path_composer.hpp"
#include "rive/clip_result.hpp"
#include "rive/math/raw_path.hpp"
#include <algorithm>

using namespace rive;
Expand Down Expand Up @@ -216,3 +217,76 @@ bool Shape::isEmpty()

// Do constraints need to be marked as dirty too? From tests it doesn't seem they do.
void Shape::pathCollapseChanged() { m_PathComposer.pathCollapseChanged(); }

class ComputeBoundsCommandPath : public CommandPath
{
public:
ComputeBoundsCommandPath() {}

AABB bounds(const Mat2D& xform)
{
m_rawPath.transformInPlace(xform);
return m_rawPath.bounds();
}

void rewind() override { m_rawPath.rewind(); }
void fillRule(FillRule value) override {}
void addPath(CommandPath* path, const Mat2D& transform) override { assert(false); }

void moveTo(float x, float y) override { m_rawPath.moveTo(x, y); }
void lineTo(float x, float y) override { m_rawPath.lineTo(x, y); }
void cubicTo(float ox, float oy, float ix, float iy, float x, float y) override
{
m_rawPath.cubicTo(ox, oy, ix, iy, x, y);
}
void close() override { m_rawPath.close(); }

RenderPath* renderPath() override
{
assert(false);
return nullptr;
}

private:
RawPath m_rawPath;
};

AABB Shape::computeWorldBounds(const Mat2D* xform) const
{
bool first = true;
AABB computedBounds = AABB::forExpansion();

ComputeBoundsCommandPath boundsCalculator;
for (auto path : m_Paths)
{
if (path->isCollapsed())
{
continue;
}

path->buildPath(boundsCalculator);

AABB aabb = boundsCalculator.bounds(xform == nullptr ? path->pathTransform()
: path->pathTransform() * *xform);

if (first)
{
first = false;
computedBounds = aabb;
}
else
{
computedBounds.expand(aabb);
}
boundsCalculator.rewind();
}

return computedBounds;
}

AABB Shape::computeLocalBounds() const
{
const Mat2D& world = worldTransform();
Mat2D inverseWorld = world.invertOrIdentity();
return computeWorldBounds(&inverseWorld);
}
Binary file added test/assets/background_measure.riv
Binary file not shown.
48 changes: 48 additions & 0 deletions test/bounds_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "rive/file.hpp"
#include "rive/node.hpp"
#include "rive/shapes/shape.hpp"
#include "rive/math/transform_components.hpp"
#include "rive/text/text_value_run.hpp"
#include "utils/no_op_renderer.hpp"
#include "rive_file_reader.hpp"
#include "rive_testing.hpp"
#include <cstdio>

TEST_CASE("compute bounds of background shape", "[bounds]")
{
auto file = ReadRiveFile("../../test/assets/background_measure.riv");

auto artboard = file->artboard();

REQUIRE(artboard->find<rive::Shape>("background") != nullptr);
auto background = artboard->find<rive::Shape>("background");
REQUIRE(artboard->find<rive::TextValueRun>("nameRun") != nullptr);
auto name = artboard->find<rive::TextValueRun>("nameRun");
artboard->advance(0.0f);

auto bounds = background->computeWorldBounds();
CHECK(bounds.width() == Approx(42.010925f));
CHECK(bounds.height() == Approx(29.995453f));

// Change the text and verify the bounds extended further.
name->text("much much longer");
artboard->advance(0.0f);

bounds = background->computeWorldBounds();
CHECK(bounds.width() == Approx(138.01093f));
CHECK(bounds.height() == Approx(29.995453f));

// Apply a transform to the whole artboard.
rive::Mat2D& world = artboard->mutableWorldTransform();
world.scaleByValues(0.5f, 0.5f);
artboard->markWorldTransformDirty();
artboard->advance(0.0f);

bounds = background->computeWorldBounds();
CHECK(bounds.width() == Approx(138.01093f / 2.0f));
CHECK(bounds.height() == Approx(29.995453f / 2.0f));

bounds = background->computeLocalBounds();
CHECK(bounds.width() == Approx(138.01093f));
CHECK(bounds.height() == Approx(29.995453f));
}

0 comments on commit c6d407a

Please sign in to comment.