""" Unit tests for PageRenderer coordinate transformations """ import pytest from pyPhotoAlbum.page_renderer import PageRenderer class TestPageRendererCoordinates: """Test coordinate transformation methods""" def test_page_to_screen_no_zoom_no_pan(self): """Test page_to_screen conversion with zoom=1.0 and no pan""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Element at page origin should map to screen_x, screen_y screen_x, screen_y = renderer.page_to_screen(0, 0) assert screen_x == 100.0 assert screen_y == 200.0 # Element at (50, 75) should be offset by that amount screen_x, screen_y = renderer.page_to_screen(50, 75) assert screen_x == 150.0 assert screen_y == 275.0 def test_page_to_screen_with_zoom(self): """Test page_to_screen conversion with zoom applied""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=2.0 ) # With zoom=2.0, distances should be doubled screen_x, screen_y = renderer.page_to_screen(50, 75) assert screen_x == 200.0 # 100 + 50*2 assert screen_y == 350.0 # 200 + 75*2 def test_page_to_screen_with_fractional_zoom(self): """Test page_to_screen conversion with fractional zoom""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=0.5 ) # With zoom=0.5, distances should be halved screen_x, screen_y = renderer.page_to_screen(100, 150) assert screen_x == 150.0 # 100 + 100*0.5 assert screen_y == 275.0 # 200 + 150*0.5 def test_screen_to_page_no_zoom_no_pan(self): """Test screen_to_page conversion with zoom=1.0 and no pan""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Screen position at screen_x, screen_y should map to page origin page_x, page_y = renderer.screen_to_page(100.0, 200.0) assert page_x == 0.0 assert page_y == 0.0 # Screen position offset should map to same offset in page coords page_x, page_y = renderer.screen_to_page(150.0, 275.0) assert page_x == 50.0 assert page_y == 75.0 def test_screen_to_page_with_zoom(self): """Test screen_to_page conversion with zoom applied""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=2.0 ) # With zoom=2.0, screen distances should be divided by 2 to get page coords page_x, page_y = renderer.screen_to_page(200.0, 350.0) assert page_x == 50.0 # (200-100)/2 assert page_y == 75.0 # (350-200)/2 def test_roundtrip_conversion_no_zoom(self): """Test that page->screen->page conversion is accurate with no zoom""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Start with page coordinates orig_page_x, orig_page_y = 123.45, 678.90 # Convert to screen and back screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y) page_x, page_y = renderer.screen_to_page(screen_x, screen_y) # Should get back the original values assert abs(page_x - orig_page_x) < 0.001 assert abs(page_y - orig_page_y) < 0.001 def test_roundtrip_conversion_with_zoom(self): """Test that page->screen->page conversion is accurate with zoom""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.5 ) # Start with page coordinates orig_page_x, orig_page_y = 123.45, 678.90 # Convert to screen and back screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y) page_x, page_y = renderer.screen_to_page(screen_x, screen_y) # Should get back the original values (with floating point tolerance) assert abs(page_x - orig_page_x) < 0.001 assert abs(page_y - orig_page_y) < 0.001 def test_roundtrip_conversion_extreme_zoom(self): """Test coordinate conversion with extreme zoom levels""" for zoom in [0.1, 0.5, 1.0, 2.0, 5.0]: renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=50.0, screen_y=100.0, dpi=96, zoom=zoom ) orig_page_x, orig_page_y = 250.0, 400.0 screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y) page_x, page_y = renderer.screen_to_page(screen_x, screen_y) assert abs(page_x - orig_page_x) < 0.001 assert abs(page_y - orig_page_y) < 0.001 class TestPageRendererBounds: """Test page bounds and point detection""" def test_is_point_in_page_inside(self): """Test is_point_in_page for points inside the page""" renderer = PageRenderer( page_width_mm=210.0, # A4 width page_height_mm=297.0, # A4 height screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Calculate page dimensions in pixels page_width_px = 210.0 * 96 / 25.4 # ~794 pixels page_height_px = 297.0 * 96 / 25.4 # ~1123 pixels # Point in center should be inside center_x = 100.0 + page_width_px / 2 center_y = 200.0 + page_height_px / 2 assert renderer.is_point_in_page(center_x, center_y) # Point at origin should be inside assert renderer.is_point_in_page(100.0, 200.0) # Point at bottom-right corner should be inside assert renderer.is_point_in_page( 100.0 + page_width_px, 200.0 + page_height_px ) def test_is_point_in_page_outside(self): """Test is_point_in_page for points outside the page""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Point before page start assert not renderer.is_point_in_page(50.0, 150.0) # Point way beyond page assert not renderer.is_point_in_page(2000.0, 2000.0) # Point to the left of page assert not renderer.is_point_in_page(50.0, 500.0) # Point above page assert not renderer.is_point_in_page(500.0, 150.0) def test_is_point_in_page_with_zoom(self): """Test is_point_in_page with different zoom levels""" for zoom in [0.5, 1.0, 2.0]: renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=zoom ) # Center of page should always be inside regardless of zoom page_width_px = 210.0 * 96 / 25.4 page_height_px = 297.0 * 96 / 25.4 center_x = 100.0 + (page_width_px * zoom) / 2 center_y = 200.0 + (page_height_px * zoom) / 2 assert renderer.is_point_in_page(center_x, center_y) def test_get_page_bounds_screen(self): """Test get_page_bounds_screen returns correct screen coordinates""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.5 ) x, y, w, h = renderer.get_page_bounds_screen() assert x == 100.0 assert y == 200.0 # Width and height should be scaled by zoom page_width_px = 210.0 * 96 / 25.4 page_height_px = 297.0 * 96 / 25.4 assert abs(w - page_width_px * 1.5) < 0.1 assert abs(h - page_height_px * 1.5) < 0.1 def test_get_page_bounds_page(self): """Test get_page_bounds_page returns correct page-local coordinates""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.5 ) x, y, w, h = renderer.get_page_bounds_page() # Origin should be at 0,0 in page-local coordinates assert x == 0.0 assert y == 0.0 # Width and height should NOT be affected by zoom (page-local coords) page_width_px = 210.0 * 96 / 25.4 page_height_px = 297.0 * 96 / 25.4 assert abs(w - page_width_px) < 0.1 assert abs(h - page_height_px) < 0.1 class TestPageRendererSubPages: """Test sub-page detection for facing pages""" def test_get_sub_page_at_single_page(self): """Test that get_sub_page_at returns None for single pages""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # For non-facing pages, should return None result = renderer.get_sub_page_at(500.0, is_facing_page=False) assert result is None def test_get_sub_page_at_facing_page_left(self): """Test get_sub_page_at for left side of facing page""" renderer = PageRenderer( page_width_mm=420.0, # Double width for facing page page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Calculate center line page_width_px = 420.0 * 96 / 25.4 center_x = 100.0 + page_width_px / 2 # Point before center should be 'left' result = renderer.get_sub_page_at(center_x - 10, is_facing_page=True) assert result == 'left' def test_get_sub_page_at_facing_page_right(self): """Test get_sub_page_at for right side of facing page""" renderer = PageRenderer( page_width_mm=420.0, # Double width for facing page page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Calculate center line page_width_px = 420.0 * 96 / 25.4 center_x = 100.0 + page_width_px / 2 # Point after center should be 'right' result = renderer.get_sub_page_at(center_x + 10, is_facing_page=True) assert result == 'right' class TestPageRendererDimensions: """Test page dimension calculations""" def test_page_dimensions_calculated_correctly(self): """Test that page dimensions are calculated correctly from mm to pixels""" renderer = PageRenderer( page_width_mm=210.0, # A4 width page_height_mm=297.0, # A4 height screen_x=0.0, screen_y=0.0, dpi=96, zoom=1.0 ) # A4 at 96 DPI expected_width = 210.0 * 96 / 25.4 # ~794 pixels expected_height = 297.0 * 96 / 25.4 # ~1123 pixels assert abs(renderer.page_width_px - expected_width) < 0.1 assert abs(renderer.page_height_px - expected_height) < 0.1 def test_screen_dimensions_with_zoom(self): """Test that screen dimensions account for zoom""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=0.0, screen_y=0.0, dpi=96, zoom=2.0 ) # Screen dimensions should be doubled due to zoom expected_width = (210.0 * 96 / 25.4) * 2.0 expected_height = (297.0 * 96 / 25.4) * 2.0 assert abs(renderer.screen_width - expected_width) < 0.1 assert abs(renderer.screen_height - expected_height) < 0.1 def test_different_dpi_values(self): """Test page dimensions with different DPI values""" dpi_values = [72, 96, 150, 300] for dpi in dpi_values: renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=0.0, screen_y=0.0, dpi=dpi, zoom=1.0 ) expected_width = 210.0 * dpi / 25.4 expected_height = 297.0 * dpi / 25.4 assert abs(renderer.page_width_px - expected_width) < 0.1 assert abs(renderer.page_height_px - expected_height) < 0.1 class TestPageRendererEdgeCases: """Test edge cases and boundary conditions""" def test_zero_coordinates(self): """Test handling of zero coordinates""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) screen_x, screen_y = renderer.page_to_screen(0, 0) assert screen_x == 100.0 assert screen_y == 200.0 page_x, page_y = renderer.screen_to_page(100.0, 200.0) assert page_x == 0.0 assert page_y == 0.0 def test_negative_page_coordinates(self): """Test handling of negative page coordinates""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) # Negative page coordinates should still convert correctly screen_x, screen_y = renderer.page_to_screen(-50, -75) assert screen_x == 50.0 assert screen_y == 125.0 # And back again page_x, page_y = renderer.screen_to_page(50.0, 125.0) assert page_x == -50.0 assert page_y == -75.0 def test_very_large_coordinates(self): """Test handling of very large coordinates""" renderer = PageRenderer( page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0 ) large_x, large_y = 10000.0, 20000.0 screen_x, screen_y = renderer.page_to_screen(large_x, large_y) page_x, page_y = renderer.screen_to_page(screen_x, screen_y) assert abs(page_x - large_x) < 0.001 assert abs(page_y - large_y) < 0.001