diff --git a/examples/LottieTweening.cpp b/examples/LottieTweening.cpp index 56433189..5d9883bb 100644 --- a/examples/LottieTweening.cpp +++ b/examples/LottieTweening.cpp @@ -97,13 +97,30 @@ struct UserExample : tvgexam::Example bool clicked(tvg::Canvas* canvas, int32_t x, int32_t y) override { + auto intersect = [&](float x, float y, tvg::Point* obb) -> bool { + //compute edge vectors + tvg::Point e1 = {obb[1].x - obb[0].x, obb[1].y - obb[0].y}; // Edge from obb[0] to obb[1] + tvg::Point e2 = {obb[3].x - obb[0].x, obb[3].y - obb[0].y}; // Edge from obb[0] to obb[3] + + //compute vectors from obb[0] to the test point + tvg::Point o = {x - obb[0].x, y - obb[0].y}; + + /* compute dot products to express `o` in terms of edge1 and edge2 + and then compute barycentric coordinates (u, v) within the box space */ + auto u = (o.x * e1.x + o.y * e1.y) / (e1.x * e1.x + e1.y * e1.y); + auto v = (o.x * e2.x + o.y * e2.y) / (e2.x * e2.x + e2.y * e2.y); + + // Check if point is inside the OBB + return (u >= 0.0f && u <= 1.0f && v >= 0.0f && v <= 1.0f); + }; + int i = 0; for (auto& state : states) { if (auto paint = lottie->picture()->paint(tvg::Accessor::id(state.name.c_str()))) { - float px, py, pw, ph; - paint->bounds(&px, &py, &pw, &ph, true); + tvg::Point obb[4]; + paint->bounds(obb); //hit a emoji layer! - if (x >= px && x <= px + pw && y >= py && y <= py + ph) { + if (intersect(x, y, obb)) { tweening(i); return true; } diff --git a/inc/thorvg.h b/inc/thorvg.h index 18cf3372..6ff72860 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -412,20 +412,42 @@ public: Result blend(BlendMethod method) noexcept; /** - * @brief Gets the axis-aligned bounding box of the paint object. + * @brief Retrieves the object-oriented bounding box (OBB) of the paint object in canvas space. + * + * This function returns the bounding box of the paint, as an oriented bounding box (OBB) after transformations are applied. * - * @param[out] x The x-coordinate of the upper-left corner of the object. - * @param[out] y The y-coordinate of the upper-left corner of the object. - * @param[out] w The width of the object. - * @param[out] h The height of the object. - * @param[in] transformed If @c true, the paint's transformations are taken into account in the scene it belongs to. Otherwise they aren't. + * @param[out] pt4 An array of four points representing the bounding box. The array size must be 4. * - * @note This is useful when you need to figure out the bounding box of the paint in the canvas space. - * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. - * @note If @p transformed is @c true, the paint needs to be pushed into a canvas and updated before this api is called. + * @retval Result::InvalidArguments @p pt4 is @c nullptr. + * @retval Result::InsufficientCondition If it failed to compute the bounding box (mostly due to invalid path information). + * + * @note The paint must be pushed into a canvas and updated before calling this function. + * + * @see Paint::bounds(float* x, float* y, folat* w, float* h) + * @see Canvas::update() + * + * @since 1.0 + */ + Result bounds(Point* pt4) const noexcept; + + /** + * @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in local space. + * + * This function returns the bounding box of the paint object relative to its local coordinate system, without applying any transformations. + * + * @param[out] x The x-coordinate of the upper-left corner of the bounding box. + * @param[out] y The y-coordinate of the upper-left corner of the bounding box. + * @param[out] w The width of the bounding box. + * @param[out] h The height of the bounding box. + * + * @retval Result::InsufficientCondition If it failed to compute the bounding box (mostly due to invalid path information). + * + * @note The bounding box is calculated in the object's local space, meaning transformations such as scaling, rotation, or translation are not applied. + * + * @see Paint::bounds(Point* pt4) * @see Canvas::update() */ - Result bounds(float* x, float* y, float* w, float* h, bool transformed = false) const noexcept; + Result bounds(float* x, float* y, float* w, float* h) const noexcept; /** * @brief Duplicates the object. diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index 43e23af6..8f5e6197 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -912,25 +912,49 @@ TVG_API Tvg_Result tvg_paint_get_opacity(const Tvg_Paint* paint, uint8_t* opacit TVG_API Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint); -/*! -* @brief Gets the axis-aligned bounding box of the Tvg_Paint object. -* -* @param[in] paint The Tvg_Paint object of which to get the bounds. -* @param[out] x The x-coordinate of the upper-left corner of the object. -* @param[out] y The y-coordinate of the upper-left corner of the object. -* @param[out] w The width of the object. -* @param[out] h The height of the object. -* @param[in] transformed If @c true, the paint's transformations are taken into account in the scene it belongs to. Otherwise they aren't. -* -* @return Tvg_Result enumeration. -* @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. -* -* @note This is useful when you need to figure out the bounding box of the paint in the canvas space. -* @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. -* @note If @p transformed is @c true, the paint needs to be pushed into a canvas and updated before this api is called. -* @see tvg_canvas_update_paint() -*/ -TVG_API Tvg_Result tvg_paint_get_bounds(const Tvg_Paint* paint, float* x, float* y, float* w, float* h, bool transformed); +/** + * @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in local space. + * + * This function returns the bounding box of the paint object relative to its local coordinate system, without applying any transformations. + * + * @param[in] paint The Tvg_Paint object of which to get the bounds. + * @param[out] x The x-coordinate of the upper-left corner of the bounding box. + * @param[out] y The y-coordinate of the upper-left corner of the bounding box. + * @param[out] w The width of the bounding box. + * @param[out] h The height of the bounding box. + * + * @return Tvg_Result enumeration. + * @retval TVG_RESULT_INVALID_ARGUMENT An invalid @p paint. + * @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information). + * + * @note The bounding box is calculated in the object's local space, meaning transformations such as scaling, rotation, or translation are not applied. + * + * @see tvg_paint_get_obb() + * @see tvg_canvas_update_paint() + */ +TVG_API Tvg_Result tvg_paint_get_aabb(const Tvg_Paint* paint, float* x, float* y, float* w, float* h); + + +/** + * @brief Retrieves the object-oriented bounding box (OBB) of the paint object in canvas space. + * + * This function returns the bounding box of the paint, as an oriented bounding box (OBB) after transformations are applied. + * + * @param[in] paint The Tvg_Paint object of which to get the bounds. + * @param[out] pt4 An array of four points representing the bounding box. The array size must be 4. + * + * @return Tvg_Result enumeration. + * @retval TVG_RESULT_INVALID_ARGUMENT @p paint or @p pt4 is invalid. + * @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information). + * + * @note The paint must be pushed into a canvas and updated before calling this function. + * + * @see tvg_paint_get_aabb() + * @see tvg_canvas_update_paint() + * + * @since 1.0 + */ +TVG_API Tvg_Result tvg_paint_get_obb(const Tvg_Paint* paint, Tvg_Point* pt4); /*! @@ -941,7 +965,7 @@ TVG_API Tvg_Result tvg_paint_get_bounds(const Tvg_Paint* paint, float* x, float* * @param[in] method The method used to mask the source object with the target. * * @return Tvg_Result enumeration. -* @retval TVG_RESULT_INVALID_ARGUMENT An invalid @p paint or @p target object or the @p method equal to TVG_MASK_METHOD_NONE. + */ TVG_API Tvg_Result tvg_paint_set_mask_method(Tvg_Paint* paint, Tvg_Paint* target, Tvg_Mask_Method method); diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index 358bc4f3..04d65ec8 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -256,12 +256,17 @@ TVG_API Tvg_Result tvg_paint_get_opacity(const Tvg_Paint* paint, uint8_t* opacit } -TVG_API Tvg_Result tvg_paint_get_bounds(const Tvg_Paint* paint, float* x, float* y, float* w, float* h, bool transformed) +TVG_API Tvg_Result tvg_paint_get_aabb(const Tvg_Paint* paint, float* x, float* y, float* w, float* h) { if (!paint) return TVG_RESULT_INVALID_ARGUMENT; - return (Tvg_Result) reinterpret_cast(paint)->bounds(x, y, w, h, transformed); + return (Tvg_Result) reinterpret_cast(paint)->bounds(x, y, w, h); } +TVG_API Tvg_Result tvg_paint_get_obb(const Tvg_Paint* paint, Tvg_Point* pt4) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->bounds((Point*)pt4); +} TVG_API Tvg_Result tvg_paint_set_mask_method(Tvg_Paint* paint, Tvg_Paint* target, Tvg_Mask_Method method) { diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 2695f07d..c55419fb 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -927,7 +927,7 @@ static void _fontText(LottieText* text, Scene* scene, float frameNo, LottieExpre txt->fill(doc.color.rgb[0], doc.color.rgb[1], doc.color.rgb[2]); float width; - txt->bounds(nullptr, nullptr, &width, nullptr, false); + txt->bounds(nullptr, nullptr, &width, nullptr); auto cursorX = width * doc.justify; txt->translate(cursorX, -doc.size * 100.0f); diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index dbd5dede..f868d8b9 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -47,26 +47,10 @@ static inline bool _isGroupType(SvgNodeType type) //According to: https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits (the last paragraph) //a stroke width should be ignored for bounding box calculations -static Box _boundingBox(const Shape* shape) +static Box _boundingBox(Paint* shape) { float x, y, w, h; - shape->bounds(&x, &y, &w, &h, false); - - if (auto strokeW = shape->strokeWidth()) { - x += 0.5f * strokeW; - y += 0.5f * strokeW; - w -= strokeW; - h -= strokeW; - } - - return {x, y, w, h}; -} - - -static Box _boundingBox(const Text* text) -{ - float x, y, w, h; - text->bounds(&x, &y, &w, &h, false); + PAINT(shape)->bounds(&x, &y, &w, &h, false); return {x, y, w, h}; } @@ -260,7 +244,7 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg } if (!compNode->node.clip.userSpace) { float x, y, w, h; - PAINT(paint)->bounds(&x, &y, &w, &h, false, false); + PAINT(paint)->bounds(&x, &y, &w, &h, false); m *= {w, 0, x, 0, h, y, 0, 0, 1}; } return m; @@ -346,7 +330,7 @@ static Paint* _applyFilter(SvgLoaderData& loaderData, Paint* paint, const SvgNod auto scene = Scene::gen(); Box bbox{}; - paint->bounds(&bbox.x, &bbox.y, &bbox.w, &bbox.h, false); + paint->bounds(&bbox.x, &bbox.y, &bbox.w, &bbox.h); Box clipBox = filter.filterUserSpace ? filter.box : Box{bbox.x + filter.box.x * bbox.w, bbox.y + filter.box.y * bbox.h, filter.box.w * bbox.w, filter.box.h * bbox.h}; auto primitiveUserSpace = filter.primitiveUserSpace; auto sx = paint->transform().e11; @@ -943,13 +927,13 @@ static Scene* _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, } -static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag) +static void _updateInvalidViewSize(Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag) { bool validWidth = (viewFlag & SvgViewFlag::Width); bool validHeight = (viewFlag & SvgViewFlag::Height); float x, y; - scene->bounds(&x, &y, &vBox.w, &vBox.h, false); + scene->bounds(&x, &y, &vBox.w, &vBox.h); if (!validWidth && !validHeight) { vBox.x = x; vBox.y = y; diff --git a/src/renderer/tvgPaint.cpp b/src/renderer/tvgPaint.cpp index 7c7da372..b368734c 100644 --- a/src/renderer/tvgPaint.cpp +++ b/src/renderer/tvgPaint.cpp @@ -277,48 +277,41 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array max.x) max.x = pts[i].x; + if (pts[i].y < min.y) min.y = pts[i].y; + if (pts[i].y > max.y) max.y = pts[i].y; + } + + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; + return true; +} + + +bool Paint::Impl::bounds(Point* pt4, bool transformed, bool stroking, bool origin) { bool ret; + PAINT_METHOD(ret, bounds(pt4, stroking)); + + if (!ret || !transformed) return ret; + const auto& m = this->transform(origin); - - //Case: No transformed, quick return! - if (!transformed || identity(&m)) { - PAINT_METHOD(ret, bounds(x, y, w, h, stroking)); - return ret; - } - - //Case: Transformed - auto tx = 0.0f; - auto ty = 0.0f; - auto tw = 0.0f; - auto th = 0.0f; - - PAINT_METHOD(ret, bounds(&tx, &ty, &tw, &th, stroking)); - - //Get vertices - Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}}; - - //New bounding box - auto x1 = FLT_MAX; - auto y1 = FLT_MAX; - auto x2 = -FLT_MAX; - auto y2 = -FLT_MAX; - - //Compute the AABB after transformation - for (int i = 0; i < 4; i++) { - pt[i] *= m; - - if (pt[i].x < x1) x1 = pt[i].x; - if (pt[i].x > x2) x2 = pt[i].x; - if (pt[i].y < y1) y1 = pt[i].y; - if (pt[i].y > y2) y2 = pt[i].y; - } - - if (x) *x = x1; - if (y) *y = y1; - if (w) *w = x2 - x1; - if (h) *h = y2 - y1; + pt4[0] *= m; + pt4[1] *= m; + pt4[2] *= m; + pt4[3] *= m; return ret; } @@ -373,9 +366,17 @@ Matrix& Paint::transform() noexcept } -Result Paint::bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept +Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept { - if (pImpl->bounds(x, y, w, h, transformed, true, transformed)) return Result::Success; + if (pImpl->bounds(x, y, w, h, true)) return Result::Success; + return Result::InsufficientCondition; +} + + +Result Paint::bounds(Point* pt4) const noexcept +{ + if (!pt4) return Result::InvalidArguments; + if (pImpl->bounds(pt4, true, true, true)) return Result::Success; return Result::InsufficientCondition; } diff --git a/src/renderer/tvgPaint.h b/src/renderer/tvgPaint.h index e52580e2..290b9511 100644 --- a/src/renderer/tvgPaint.h +++ b/src/renderer/tvgPaint.h @@ -256,7 +256,8 @@ namespace tvg RenderRegion bounds(RenderMethod* renderer) const; Iterator* iterator(); - bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin = false); + bool bounds(float* x, float* y, float* w, float* h, bool stroking); + bool bounds(Point* pt4, bool transformed, bool stroking, bool origin = false); RenderData update(RenderMethod* renderer, const Matrix& pm, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); bool render(RenderMethod* renderer); Paint* duplicate(Paint* ret = nullptr); diff --git a/src/renderer/tvgPicture.h b/src/renderer/tvgPicture.h index febd9e85..12eb7e25 100644 --- a/src/renderer/tvgPicture.h +++ b/src/renderer/tvgPicture.h @@ -114,13 +114,12 @@ struct Picture::Impl : Paint::Impl return Result::Success; } - - bool bounds(float* x, float* y, float* w, float* h, bool stroking) + bool bounds(Point* pt4, bool stroking) { - if (x) *x = 0; - if (y) *y = 0; - if (w) *w = this->w; - if (h) *h = this->h; + pt4[0] = {0.0f, 0.0f}; + pt4[1] = {w, 0.0f}; + pt4[2] = {w, h}; + pt4[3] = {0.0f, h}; return true; } diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index d0db9234..70a2ea0d 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -194,34 +194,28 @@ struct Scene::Impl : Paint::Impl return ret; } - bool bounds(float* px, float* py, float* pw, float* ph, bool stroking) + bool bounds(Point* pt4, bool stroking) { if (paints.empty()) return false; - auto x1 = FLT_MAX; - auto y1 = FLT_MAX; - auto x2 = -FLT_MAX; - auto y2 = -FLT_MAX; + Point min = {FLT_MAX, FLT_MAX}; + Point max = {-FLT_MAX, -FLT_MAX}; for (auto paint : paints) { - auto x = FLT_MAX; - auto y = FLT_MAX; - auto w = 0.0f; - auto h = 0.0f; - - if (!PAINT(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue; - + Point tmp[4]; + if (!PAINT(paint)->bounds(tmp, true, stroking)) continue; //Merge regions - if (x < x1) x1 = x; - if (x2 < x + w) x2 = (x + w); - if (y < y1) y1 = y; - if (y2 < y + h) y2 = (y + h); + for (int i = 0; i < 4; ++i) { + if (tmp[i].x < min.x) min.x = tmp[i].x; + if (tmp[i].x > max.x) max.x = tmp[i].x; + if (tmp[i].y < min.y) min.y = tmp[i].y; + if (tmp[i].y > max.y) max.y = tmp[i].y; + } } - - if (px) *px = x1; - if (py) *py = y1; - if (pw) *pw = (x2 - x1); - if (ph) *ph = (y2 - y1); + pt4[0] = min; + pt4[1] = {max.x, min.y}; + pt4[2] = max; + pt4[3] = {min.x, max.y}; return true; } diff --git a/src/renderer/tvgShape.h b/src/renderer/tvgShape.h index 0c675e41..6eb2d621 100644 --- a/src/renderer/tvgShape.h +++ b/src/renderer/tvgShape.h @@ -116,17 +116,24 @@ struct Shape::Impl : Paint::Impl return renderer->region(rd); } - bool bounds(float* x, float* y, float* w, float* h, bool stroking) + bool bounds(Point* pt4, bool stroking) { - if (!rs.path.bounds(x, y, w, h)) return false; + float x, y, w, h; + if (!rs.path.bounds(&x, &y, &w, &h)) return false; //Stroke feathering if (stroking && rs.stroke) { - if (x) *x -= rs.stroke->width * 0.5f; - if (y) *y -= rs.stroke->width * 0.5f; - if (w) *w += rs.stroke->width; - if (h) *h += rs.stroke->width; + x -= rs.stroke->width * 0.5f; + y -= rs.stroke->width * 0.5f; + w += rs.stroke->width; + h += rs.stroke->width; } + + pt4[0] = {x, y}; + pt4[1] = {x + w, y}; + pt4[2] = {x + w, y + h}; + pt4[3] = {x, y + h}; + return true; } diff --git a/src/renderer/tvgText.h b/src/renderer/tvgText.h index a587d697..e5729b9e 100644 --- a/src/renderer/tvgText.h +++ b/src/renderer/tvgText.h @@ -134,10 +134,10 @@ struct Text::Impl : Paint::Impl return PAINT(shape)->update(renderer, transform, clips, opacity, pFlag, false); } - bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking) + bool bounds(Point* pt4, TVG_UNUSED bool stroking) { if (!load()) return false; - PAINT(shape)->bounds(x, y, w, h, true, true, false); + PAINT(shape)->bounds(pt4, true, true, false); return true; } diff --git a/src/savers/gif/tvgGifSaver.cpp b/src/savers/gif/tvgGifSaver.cpp index 345637f4..1e9b0c5e 100644 --- a/src/savers/gif/tvgGifSaver.cpp +++ b/src/savers/gif/tvgGifSaver.cpp @@ -124,7 +124,7 @@ bool GifSaver::save(Animation* animation, Paint* bg, const char* filename, TVG_U auto picture = animation->picture(); float x, y; x = y = 0; - picture->bounds(&x, &y, &vsize[0], &vsize[1], false); + picture->bounds(&x, &y, &vsize[0], &vsize[1]); //cut off the negative space if (x < 0) vsize[0] += x; diff --git a/test/testPaint.cpp b/test/testPaint.cpp index f7219f5d..92d27f5a 100644 --- a/test/testPaint.cpp +++ b/test/testPaint.cpp @@ -127,23 +127,28 @@ TEST_CASE("Bounding Box", "[tvgPaint]") //Negative float x = 0, y = 0, w = 0, h = 0; - REQUIRE(shape->bounds(&x, &y, &w, &h, false) == Result::InsufficientCondition); + REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::InsufficientCondition); //Case 1 REQUIRE(shape->appendRect(0.0f, 10.0f, 20.0f, 100.0f, 50.0f, 50.0f) == Result::Success); REQUIRE(shape->translate(100.0f, 111.0f) == Result::Success); - REQUIRE(shape->bounds(&x, &y, &w, &h, false) == Result::Success); + REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success); REQUIRE(x == 0.0f); REQUIRE(y == 10.0f); REQUIRE(w == 20.0f); REQUIRE(h == 100.0f); REQUIRE(canvas->update(shape) == Result::Success); - REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success); - REQUIRE(x == 100.0f); - REQUIRE(y == 121.0f); - REQUIRE(w == 20.0f); - REQUIRE(h == 100.0f); + Point pts[4]; + REQUIRE(shape->bounds(pts) == Result::Success); + REQUIRE(pts[0].x == 100.0f); + REQUIRE(pts[3].x == 100.0f); + REQUIRE(pts[0].y == 121.0f); + REQUIRE(pts[1].y == 121.0f); + REQUIRE(pts[1].x == 120.0f); + REQUIRE(pts[2].x == 120.0f); + REQUIRE(pts[2].y == 221.0f); + REQUIRE(pts[3].y == 221.0f); //Case 2 REQUIRE(shape->reset() == Result::Success); @@ -151,18 +156,22 @@ TEST_CASE("Bounding Box", "[tvgPaint]") REQUIRE(shape->lineTo(20.0f, 210.0f) == Result::Success); auto identity = Matrix{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; REQUIRE(shape->transform(identity) == Result::Success); - REQUIRE(shape->bounds(&x, &y, &w, &h, false) == Result::Success); + REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success); REQUIRE(x == 0.0f); REQUIRE(y == 10.0f); REQUIRE(w == 20.0f); REQUIRE(h == 200.0f); REQUIRE(canvas->update(shape) == Result::Success); - REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success); - REQUIRE(x == 0.0f); - REQUIRE(y == 10.0f); - REQUIRE(w == 20.0f); - REQUIRE(h == 200.0f); + REQUIRE(shape->bounds(pts) == Result::Success); + REQUIRE(pts[0].x == 0.0f); + REQUIRE(pts[3].x == 0.0f); + REQUIRE(pts[0].y == 10.0f); + REQUIRE(pts[1].y == 10.0f); + REQUIRE(pts[1].x == 20.0f); + REQUIRE(pts[2].x == 20.0f); + REQUIRE(pts[2].y == 210.0f); + REQUIRE(pts[3].y == 210.0f); Initializer::term(); }