def denormalize_wrt_roi_shape(self, roi_shape: Rectangle) -> "Ellipse":
        """
        Transforming shape from the normalized coordinate system to the `roi` coordinate system.
        This function is the inverse of ``normalize_wrt_roi_shape``

        :example: Assume we have Ellipse `c1` which lives in the top-right quarter of the normalized coordinate space.
            The `roi` is a rectangle living in the half right of the normalized coordinate space.
            This function returns Ellipse `c1` expressed in the coordinate space of `roi`. (should return top-half)

            Ellipse denormalized to a rectangle as ROI

            >>> from ote_sdk.entities.annotation import Annotation
            >>> from ote_sdk.entities.shapes.ellipse import Ellipse
            >>> c1 = Ellipse(x1=0.5, x2=1.0, y1=0.0, y2=0.5)  # An ellipse in the top right
            >>> roi = Rectangle(x1=0.5, x2=1.0, y1=0.0, y2=1.0)  # the half-right
            >>> normalized = c1.denormalize_wrt_roi_shape(roi_shape)  # should return top half
            >>> normalized
            Ellipse(, x1=0.0, y1=0.0, x2=1.0, y2=0.5, scored_labels=[])

        :param roi_shape: Region of Interest
        :return: New polygon in the ROI coordinate system
        """
        if not isinstance(roi_shape, Rectangle):
            raise ValueError("roi_shape has to be a Rectangle.")

        roi_shape = roi_shape.clip_to_visible_region()

        x1 = (self.x1 - roi_shape.x1) / roi_shape.width
        y1 = (self.y1 - roi_shape.y1) / roi_shape.height
        x2 = (self.x2 - roi_shape.x1) / roi_shape.width
        y2 = (self.y2 - roi_shape.y1) / roi_shape.height

        return Ellipse(x1=x1, y1=y1, x2=x2, y2=y2)
    def normalize_wrt_roi_shape(self, roi_shape: Rectangle) -> "Ellipse":
        """
        Transforms from the `roi` coordinate system to the normalized coordinate system.
        This function is the inverse of ``denormalize_wrt_roi_shape``.

        :example: Assume we have Ellipse `c1` which lives in the top-right quarter of a 2D space.
            The 2D space where `c1` lives in is an `roi` living in the top-left quarter of the normalized coordinate
            space. This function returns Ellipse `c1` expressed in the normalized coordinate space.

            >>> from ote_sdk.entities.annotation import Annotation
            >>> from ote_sdk.entities.shapes.rectangle import Rectangle
            >>> from ote_sdk.entities.shapes.ellipse import Ellipse
            >>> c1 = Ellipse(x1=0.5, y1=0.5, x2=0.6, y2=0.6)
            >>> roi = Rectangle(x1=0.0, x2=0.5, y1=0.0, y2=0.5)
            >>> normalized = c1.normalize_wrt_roi_shape(roi_shape)
            >>> normalized
            Ellipse(, x1=0.25, y1=0.25, x2=0.3, y2=0.3, scored_labels=[])

        :param roi_shape: Region of Interest
        :return: New polygon in the image coordinate system
        """

        if not isinstance(roi_shape, Rectangle):
            raise ValueError("roi_shape has to be a Rectangle.")

        roi_shape = roi_shape.clip_to_visible_region()

        return Ellipse(
            x1=self.x1 * roi_shape.width + roi_shape.x1,
            y1=self.y1 * roi_shape.height + roi_shape.y1,
            x2=self.x2 * roi_shape.width + roi_shape.x1,
            y2=self.y2 * roi_shape.height + roi_shape.y1,
        )
    def denormalize_wrt_roi_shape(self, roi_shape: Rectangle) -> "Polygon":
        """
        Transforming shape from the normalized coordinate system to the `roi` coordinate system.
        This function is the inverse of ``normalize_wrt_roi_shape``

        :example: Assume we have Polygon `p1` which lives in the top-right quarter of the normalized coordinate space.
            The `roi` is a rectangle living in the half right of the normalized coordinate space.
            This function returns Polygon `p1` expressed in the coordinate space of `roi`. (should return top-half)

            Polygon denormalized to a rectangle as ROI

            >>> from ote_sdk.entities.shapes.rectangle import Rectangle
            >>> from ote_sdk.entities.annotation import Annotation
            >>> p1 = Polygon(points=[Point(x=0.5, y=0.0), Point(x=0.75, y=0.2), Point(x=0.6, y=0.1)])
            >>> roi = Rectangle(x1=0.5, x2=1.0, y1=0.0, y2=1.0)  # the half-right
            >>> normalized = p1.denormalize_wrt_roi_shape(roi_shape)
            >>> normalized
            Polygon(, len(points)=3, scored_labels=[])

        :param roi_shape: Region of Interest
        :return: New polygon in the ROI coordinate system
        """
        if not isinstance(roi_shape, Rectangle):
            raise ValueError("roi_shape has to be a Rectangle.")

        roi_shape = roi_shape.clip_to_visible_region()

        points = [p.denormalize_wrt_roi_shape(roi_shape) for p in self.points]
        return Polygon(points=points)
    def normalize_wrt_roi_shape(self, roi_shape: Rectangle) -> "Polygon":
        """
        Transforms from the `roi` coordinate system to the normalized coordinate system.
        This function is the inverse of ``denormalize_wrt_roi_shape``.

        :example: Assume we have Polygon `p1` which lives in the top-right quarter of a 2D space.
            The 2D space where `p1` lives in is an `roi` living in the top-left quarter of the normalized coordinate
            space. This function returns Polygon `p1` expressed in the normalized coordinate space.

            >>> from ote_sdk.entities.annotation import Annotation
            >>> from ote_sdk.entities.shapes.rectangle import Rectangle
            >>> p1 = Polygon(points=[Point(x=0.5, y=0.0), Point(x=0.75, y=0.2), Point(x=0.6, y=0.1)])
            >>> roi = Rectangle(x1=0.0, x2=0.5, y1=0.0, y2=0.5)
            >>> normalized = p1.normalize_wrt_roi_shape(roi_shape)
            >>> normalized
            Polygon(, len(points)=3, scored_labels=[])

        :param roi_shape: Region of Interest
        :return: New polygon in the image coordinate system
        """
        if not isinstance(roi_shape, Rectangle):
            raise ValueError("roi_shape has to be a Rectangle.")

        roi_shape = roi_shape.clip_to_visible_region()

        points = [p.normalize_wrt_roi(roi_shape) for p in self.points]
        return Polygon(points=points)
    def denormalize_wrt_roi_shape(self, roi_shape: Rectangle):
        """
        The inverse of normalize_wrt_roi_shape.
        Transforming Polygon from the normalized coordinate system to the `roi` coordinate system.
        This is used to pull ground truth during training process of the tasks.
        Examples given in the Shape implementations.

        :param roi_shape:
        """
        roi_shape = roi_shape.clip_to_visible_region()

        return Point(
            x=(self.x - roi_shape.x1) / roi_shape.width,
            y=(self.y - roi_shape.y1) / roi_shape.height,
        )
    def normalize_wrt_roi(self, roi_shape: Rectangle) -> "Point":
        """
        The inverse of denormalize_wrt_roi_shape.
        Transforming Polygon from the `roi` coordinate system to the normalized coordinate system.
        This is used when the tasks want to save the analysis results.

        For example in Detection -> Segmentation pipeline, the analysis results of segmentation
        needs to be normalized to the roi (bounding boxes) coming from the detection.

        :param roi_shape:
        """
        roi_shape = roi_shape.clip_to_visible_region()
        width = roi_shape.width
        height = roi_shape.height
        x1 = roi_shape.x1
        y1 = roi_shape.y1
        return Point(x=self.x * width + x1, y=self.y * height + y1)
    def test_rectangle_clip_to_visible_region(self):
        """
        <b>Description:</b>
        Check Rectangle clip_to_visible_region method

        <b>Input data:</b>
        Rectangle class initiation parameters

        <b>Expected results:</b>
        Test passes if clip_to_visible_region method return correct value

        <b>Steps</b>
        1. Check values returned by clip_to_visible_region method for 0<x1<x2, 0<y1<y2, x1<x2<1, y1<y2<1
        2. Check values returned by clip_to_visible_region method for x1<0, y1<0, x2>1, y2>1
        3. Check values returned by clip_to_visible_region method for x1=0, y1=0, x2=1, y2=1
        4. Check ValueError exception raised if x1<0 and x1<x2<0: clipped Rectangle width will be equal 0
        5. Check ValueError exception raised if 1<x1<x2 and x2>1: clipped Rectangle width will be equal 0
        6. Check ValueError exception raised if y1<0 and y1<y2<0: clipped Rectangle height will be equal 0
        7. Check ValueError exception raised if 1<y1<y2 and y2>1: clipped Rectangle height will be equal 0
        """
        positive_scenarios = [
            {
                "input_params": {
                    "x1": 0.3,
                    "y1": 0.2,
                    "x2": 0.6,
                    "y2": 0.4,
                    "labels": self.rectangle_labels(),
                },
                "params_expected": {"x1": 0.3, "y1": 0.2, "x2": 0.6, "y2": 0.4},
            },
            {
                "input_params": {
                    "x1": -0.2,
                    "y1": -0.3,
                    "x2": 1.6,
                    "y2": 1.4,
                    "labels": self.rectangle_labels(),
                },
                "params_expected": {"x1": 0.0, "y1": 0.0, "x2": 1.0, "y2": 1.0},
            },
            {
                "input_params": {
                    "x1": 0.0,
                    "y1": 0.0,
                    "x2": 1.0,
                    "y2": 1.0,
                    "labels": self.rectangle_labels(),
                },
                "params_expected": {"x1": 0.0, "y1": 0.0, "x2": 1.0, "y2": 1.0},
            },
        ]
        for scenario in positive_scenarios:
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", "Rectangle coordinates")
                rectangle_actual = Rectangle(**scenario.get("input_params"))
            rectangle_expected = Rectangle(**scenario.get("params_expected"))
            rectangle_actual.modification_date = self.modification_date
            rectangle_expected.modification_date = self.modification_date
            assert rectangle_actual.clip_to_visible_region() == rectangle_expected
        negative_scenarios = [
            {"x1": -0.4, "y1": 0.2, "x2": -0.2, "y2": 0.4},
            {"x1": 1.2, "y1": 0.2, "x2": 1.6, "y2": 0.4},
            {"x1": 0.4, "y1": -0.4, "x2": 0.6, "y2": -0.2},
            {"x1": 1.2, "y1": 1.2, "x2": 1.6, "y2": 1.4},
        ]
        for scenario in negative_scenarios:
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", "Rectangle coordinates")
                rectangle_actual = Rectangle(**scenario)
            with pytest.raises(ValueError):
                rectangle_actual.clip_to_visible_region()