From aaf7fc23d23ca82821d1f3d3250418a2d8a8521e Mon Sep 17 00:00:00 2001 From: mozman Date: Tue, 2 Jan 2024 12:57:30 +0100 Subject: [PATCH] fix clipping of image boundaries --- src/ezdxf/addons/drawing/backend.py | 25 ++++++++++++++++++++++++- src/ezdxf/addons/drawing/designer.py | 16 +++++++++------- src/ezdxf/entities/image.py | 3 ++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/ezdxf/addons/drawing/backend.py b/src/ezdxf/addons/drawing/backend.py index 7a9dd9628..8720053ba 100644 --- a/src/ezdxf/addons/drawing/backend.py +++ b/src/ezdxf/addons/drawing/backend.py @@ -18,6 +18,15 @@ BkPath2d: TypeAlias = NumpyPath2d BkPoints2d: TypeAlias = NumpyPoints2d +# fmt: off +_IMAGE_FLIP_MATRIX = [ + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 999, 0.0, 1.0 # index 13: 999 = image height +] +# fmt: on + @dataclasses.dataclass class ImageData: @@ -27,7 +36,8 @@ class ImageData: image: an array of RGBA pixels transform: the transformation to apply to the image when drawing (the transform from pixel coordinates to wcs) - pixel_boundary_path: boundary path vertices in pixel coordinates + pixel_boundary_path: boundary path vertices in pixel coordinates, the image + coordinate system has an inverted y-axis and the top-left corner is (0, 0) """ @@ -36,6 +46,19 @@ class ImageData: pixel_boundary_path: NumpyPoints2d use_clipping_boundary: bool = False + def image_size(self) -> tuple[int, int]: + """Returns the image size as tuple (width, height).""" + image_height, image_width, *_ = self.image.shape + return image_width, image_height + + def flip_matrix(self) -> Matrix44: + """Returns the transformation matrix to align the image coordinate system with + the WCS. + """ + _, image_height = self.image_size() + _IMAGE_FLIP_MATRIX[13] = image_height + return Matrix44(_IMAGE_FLIP_MATRIX) + class BackendInterface(ABC): """Public interface for 2D rendering backends.""" diff --git a/src/ezdxf/addons/drawing/designer.py b/src/ezdxf/addons/drawing/designer.py index 651dfbcb7..3f4312eb6 100644 --- a/src/ezdxf/addons/drawing/designer.py +++ b/src/ezdxf/addons/drawing/designer.py @@ -438,11 +438,7 @@ def draw_text( def draw_image(self, image_data: ImageData, properties: Properties) -> None: if self.clipping_portal.is_active: # the pixel boundary path can be split into multiple paths - boundary_paths = _clip_image_boundary_path( - self.clipping_portal, - image_data.pixel_boundary_path, - image_data.transform, - ) + boundary_paths = _clip_image_boundary_path(self.clipping_portal, image_data) image_data.transform = self.clipping_portal.transform_matrix( image_data.transform ) @@ -497,10 +493,15 @@ def _mask_image(image_data: ImageData) -> None: def _clip_image_boundary_path( - clipping_portal: ClippingPortal, pixel_boundary_path: BkPoints2d, m: Matrix44 + clipping_portal: ClippingPortal, image_data: ImageData ) -> list[BkPoints2d]: + pixel_boundary_path = image_data.pixel_boundary_path + + # flip image coordinate system + m = image_data.flip_matrix() @ image_data.transform original = [pixel_boundary_path] - # include transformation applied by the clipping portal + + # inverse matrix includes the transformation applied by the clipping portal inverse = clipping_portal.transform_matrix(m) try: inverse.inverse() @@ -512,6 +513,7 @@ def _clip_image_boundary_path( wcs_polygon.transform_inplace(m) clipped_wcs_polygons = clipping_portal.clip_polygon(wcs_polygon) if (len(clipped_wcs_polygons) == 1) and (clipped_wcs_polygons[0] is wcs_polygon): + # this shows the caller that the image boundary path wasn't clipped return original for polygon in clipped_wcs_polygons: polygon.transform_inplace(inverse) diff --git a/src/ezdxf/entities/image.py b/src/ezdxf/entities/image.py index 0d7e203f9..7944d3fe6 100644 --- a/src/ezdxf/entities/image.py +++ b/src/ezdxf/entities/image.py @@ -185,7 +185,8 @@ def get_wcs_transform(self) -> Matrix44: def pixel_boundary_path(self) -> list[Vec2]: """Returns the boundary path as closed loop in pixel coordinates. Resolves the - simple form of two vertices as a rectangle. + simple form of two vertices as a rectangle. The image coordinate system has an + inverted y-axis and the top-left corner is (0, 0). .. versionchanged:: 1.2.0