Program Listing for File RotatedRect.hpp

Return to documentation for file (include/depthai/common/RotatedRect.hpp)

#pragma once

// std
#include <cstdint>

// shared
#include "depthai/common/Point2f.hpp"
#include "depthai/common/Rect.hpp"
#include "depthai/common/Size2f.hpp"
#include "depthai/utility/Serialization.hpp"

namespace dai {

struct RotatedRect {
    Point2f center;
    Size2f size;
    float angle = 0.f;

    RotatedRect() = default;
    RotatedRect(const Point2f& center, const Size2f& size, float angle) : center(center), size(size), angle(angle) {
        if(size.isNormalized() != center.isNormalized()) {
            throw std::runtime_error("Cannot create RotatedRect with mixed normalization");
        }
    }
    RotatedRect(const Rect& rect, float angle = 0.f)
        : center(rect.x + rect.width / 2.0f, rect.y + rect.height / 2.0f, rect.isNormalized()),
          size(rect.width, rect.height, rect.isNormalized()),
          angle(angle) {}

    operator Rect() const {
        const auto [minx, miny, maxx, maxy] = getOuterRect();
        return Rect(minx, miny, maxx - minx, maxy - miny);
    }

    bool isNormalized() const {
        if(size.isNormalized() != center.isNormalized()) {
            throw std::runtime_error("Cannot denormalize RotatedRect with mixed normalization");
        }
        return size.isNormalized();
    }

    RotatedRect normalize(unsigned int width, unsigned int height) const {
        if(width == 0 || height == 0) {
            throw std::runtime_error("Width and height must be non-zero to denormalize RotatedRect");
        }
        if(isNormalized()) return *this;
        RotatedRect normalized = *this;
        normalized.center = dai::Point2f(center.x / width, center.y / height, true);
        normalized.size = dai::Size2f(size.width / width, size.height / height, true);
        return normalized;
    }

    RotatedRect denormalize(unsigned int width, unsigned int height, bool force = false) const {
        if(width == 0 || height == 0) {
            throw std::runtime_error("Width and height must be non-zero to denormalize RotatedRect");
        }
        if(!force && !isNormalized()) return *this;
        RotatedRect denormalized = *this;
        denormalized.center = dai::Point2f(center.x * width, center.y * height, false);
        denormalized.size = dai::Size2f(size.width * width, size.height * height, false);
        return denormalized;
    }

    std::array<dai::Point2f, 4> getPoints() const {
        float angleRad = angle * 3.14159265358979323846f / 180.0f;

        float halfWidth = size.width / 2.0f;
        float halfHeight = size.height / 2.0f;

        // Precompute sin and cos of the angle
        float cosAngle = std::cos(angleRad);
        float sinAngle = std::sin(angleRad);

        // Corners relative to the center before rotation
        std::array<dai::Point2f, 4> corners;
        corners[0] = {-halfWidth, -halfHeight};
        corners[1] = {halfWidth, -halfHeight};
        corners[2] = {halfWidth, halfHeight};
        corners[3] = {-halfWidth, halfHeight};

        // Rotate each corner and translate it back to the center position
        for(auto& corner : corners) {
            float x = corner.x;
            float y = corner.y;
            corner.x = x * cosAngle - y * sinAngle + center.x;
            corner.y = x * sinAngle + y * cosAngle + center.y;
        }

        return corners;
    }
    std::array<float, 4> getOuterRect() const {
        auto points = getPoints();
        float minx = std::min({points[0].x, points[1].x, points[2].x, points[3].x});
        float maxx = std::max({points[0].x, points[1].x, points[2].x, points[3].x});
        float miny = std::min({points[0].y, points[1].y, points[2].y, points[3].y});
        float maxy = std::max({points[0].y, points[1].y, points[2].y, points[3].y});
        return {minx, miny, maxx, maxy};
    }
};

DEPTHAI_SERIALIZE_EXT(RotatedRect, center, size, angle);

}  // namespace dai