From 20453dfa0718cd9962cd7525f887c018484bab3f Mon Sep 17 00:00:00 2001 From: Joshua Munn Date: Thu, 22 Jun 2023 14:41:02 +0100 Subject: [PATCH] Flip sign of translation factor so it can be added rather... ...than subtracted from the original bounding rect. This seems to be more conventional. --- tests/test_svg_coordinate_transforms.py | 48 ++++++++++++------------- willow/svg.py | 31 ++++++++++------ 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/tests/test_svg_coordinate_transforms.py b/tests/test_svg_coordinate_transforms.py index ccf3d53e..69e5c9bd 100644 --- a/tests/test_svg_coordinate_transforms.py +++ b/tests/test_svg_coordinate_transforms.py @@ -54,12 +54,12 @@ def test_portrait_view_box(self): ("xMinYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 0)), ("xMinYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 0)), ("xMinYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 0)), - ("xMidYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, 18.75, 0)), - ("xMidYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 18.75, 0)), - ("xMidYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 18.75, 0)), - ("xMaxYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, 37.5, 0)), - ("xMaxYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 37.5, 0)), - ("xMaxYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 37.5, 0)), + ("xMidYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, -18.75, 0)), + ("xMidYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, -18.75, 0)), + ("xMidYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, -18.75, 0)), + ("xMaxYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, -37.5, 0)), + ("xMaxYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, -37.5, 0)), + ("xMaxYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, -37.5, 0)), ] for preserve_aspect_ratio, expected_result in params: with self.subTest(preserve_aspect_ratio=preserve_aspect_ratio): @@ -78,12 +78,12 @@ def test_landscape_view_box(self): ("xMinYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 0)), ("xMidYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 0)), ("xMaxYMin meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 0)), - ("xMinYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 18.75)), - ("xMidYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 18.75)), - ("xMaxYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 18.75)), - ("xMinYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 37.5)), - ("xMidYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 37.5)), - ("xMaxYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, 37.5)), + ("xMinYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, -18.75)), + ("xMidYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, -18.75)), + ("xMaxYMid meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, -18.75)), + ("xMinYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, -37.5)), + ("xMidYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, -37.5)), + ("xMaxYMax meet", ViewportToUserSpaceTransform(1.25, 1.25, 0, -37.5)), ] for preserve_aspect_ratio, expected_result in params: with self.subTest(preserve_aspect_ratio=preserve_aspect_ratio): @@ -105,12 +105,12 @@ def test_portrait_view_box(self): ("xMinYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 0)), ("xMidYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 0)), ("xMaxYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 0)), - ("xMinYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, -50)), - ("xMidYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, -50)), - ("xMaxYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, -50)), - ("xMinYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, -100)), - ("xMidYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, -100)), - ("xMaxYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, -100)), + ("xMinYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 50)), + ("xMidYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 50)), + ("xMaxYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 50)), + ("xMinYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 100)), + ("xMidYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 100)), + ("xMaxYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 100)), ] for preserve_aspect_ratio, expected_result in params: with self.subTest(preserve_aspect_ratio=preserve_aspect_ratio): @@ -129,12 +129,12 @@ def test_landscape_view_box(self): ("xMinYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 0)), ("xMinYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 0)), ("xMinYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 0, 0)), - ("xMidYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, -50, 0)), - ("xMidYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, -50, 0)), - ("xMidYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, -50, 0)), - ("xMaxYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, -100, 0)), - ("xMaxYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, -100, 0)), - ("xMaxYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, -100, 0)), + ("xMidYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, 50, 0)), + ("xMidYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 50, 0)), + ("xMidYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 50, 0)), + ("xMaxYMin slice", ViewportToUserSpaceTransform(2.5, 2.5, 100, 0)), + ("xMaxYMid slice", ViewportToUserSpaceTransform(2.5, 2.5, 100, 0)), + ("xMaxYMax slice", ViewportToUserSpaceTransform(2.5, 2.5, 100, 0)), ] for preserve_aspect_ratio, expected_result in params: with self.subTest(preserve_aspect_ratio=preserve_aspect_ratio): diff --git a/willow/svg.py b/willow/svg.py index 34a5c6ab..cf42a3b4 100644 --- a/willow/svg.py +++ b/willow/svg.py @@ -36,6 +36,13 @@ def __init__(self, scale_x, scale_y, translate_x, translate_y): self.translate_x = translate_x self.translate_y = translate_y + def __repr__(self): + return ( + f"{self.__class__.__name__}(scale_x={self.scale_x}, scale_y=" + f"{self.scale_y}, translate_x={self.translate_x}, " + f"translate_y={self.translate_y})" + ) + def __eq__(self, other): if not isinstance(other, self.__class__): return False @@ -49,10 +56,10 @@ def __eq__(self, other): def __call__(self, rect): left, top, right, bottom = rect return ( - (left - self.translate_x) / self.scale_x, - (top - self.translate_y) / self.scale_y, - (right - self.translate_x) / self.scale_x, - (bottom - self.translate_y) / self.scale_y, + (left + self.translate_x) / self.scale_x, + (top + self.translate_y) / self.scale_y, + (right + self.translate_x) / self.scale_x, + (bottom + self.translate_y) / self.scale_y, ) @@ -86,17 +93,21 @@ def get_viewport_to_user_space_transform( # coefficient and use it for scaling both axes scale_x = scale_y = choose_coefficient(scale_x, scale_y) - translate_x = -view_box.min_x * scale_x + # initial offsets to account for non-zero viewBox min-x and min-y + translate_x = view_box.min_x * scale_x + translate_y = view_box.min_y * scale_y + + # adjust the offsets by the amount the viewBox has been translated + # to fit into the viewport (if any) if x_position == "mid": - translate_x += (svg.image.width - view_box.width * scale_x) / 2 + translate_x -= (svg.image.width - view_box.width * scale_x) / 2 elif x_position == "max": - translate_x += svg.image.width - view_box.width * scale_x + translate_x -= svg.image.width - view_box.width * scale_x - translate_y = -view_box.min_y * scale_y if y_position == "mid": - translate_y += (svg.image.height - view_box.height * scale_y) / 2 + translate_y -= (svg.image.height - view_box.height * scale_y) / 2 elif y_position == "max": - translate_y += svg.image.height - view_box.height * scale_y + translate_y -= svg.image.height - view_box.height * scale_y return ViewportToUserSpaceTransform(scale_x, scale_y, translate_x, translate_y)