using AcDream.App.Studio; namespace AcDream.App.Tests.Studio; /// /// Pure-math tests for the canvas → panel-local coordinate mapping used by /// . /// /// No GL context required — we're just verifying the formula: /// panel_local = (raw_mouse_screen) - (image_screen_top_left) /// /// The image_screen_top_left is what ImGui.GetItemRectMin() returns after /// ImGui.Image: the sub-window top-left + title-bar height + inner padding + any /// scrolling. We model it as a constant offset in these tests. /// /// V-flip: the image is drawn with uv0=(0,1) / uv1=(1,0) so GL's bottom-left /// origin is flipped to top-left on screen. After the flip, screen Y=0 (top of image) /// = panel Y=0 (top of the UI), so NO additional Y inversion is applied. /// public class CanvasCoordMappingTests { // Simulates the mapping DrawCanvas performs: // panel pixel = mouse_screen - image_rectMin // Returns null when the result falls outside [0, width) x [0, height). private static (int px, int py)? Map( float mouseScreenX, float mouseScreenY, float imageOriginX, float imageOriginY, int panelWidth, int panelHeight) { int ix = (int)(mouseScreenX - imageOriginX); int iy = (int)(mouseScreenY - imageOriginY); if (ix < 0 || ix >= panelWidth || iy < 0 || iy >= panelHeight) return null; return (ix, iy); } [Fact] public void TopLeft_of_image_maps_to_panel_origin() { // The canvas image starts at screen (300, 50) (after sub-window chrome). // A click exactly at the image's screen top-left → panel (0, 0). var result = Map(mouseScreenX: 300f, mouseScreenY: 50f, imageOriginX: 300f, imageOriginY: 50f, panelWidth: 1280, panelHeight: 720); Assert.Equal((0, 0), result); } [Fact] public void Interior_point_maps_correctly() { // Image origin at screen (300, 50). Mouse at screen (780, 230). // Expected panel coord: (780-300, 230-50) = (480, 180). var result = Map(mouseScreenX: 780f, mouseScreenY: 230f, imageOriginX: 300f, imageOriginY: 50f, panelWidth: 1280, panelHeight: 720); Assert.Equal((480, 180), result); } [Fact] public void Bottom_right_corner_maps_to_last_valid_pixel() { // Image is 1280×720, origin at screen (300, 50). // Last pixel in bottom-right is panel (1279, 719) → screen (1579, 769). var result = Map(mouseScreenX: 1579f, mouseScreenY: 769f, imageOriginX: 300f, imageOriginY: 50f, panelWidth: 1280, panelHeight: 720); Assert.Equal((1279, 719), result); } [Fact] public void Mouse_on_ImGui_chrome_above_image_returns_null() { // The ImGui window title-bar / padding is above rectMin, i.e. at screenY < 50. // The mouse there should NOT produce a panel event. var result = Map(mouseScreenX: 400f, mouseScreenY: 40f, // 10px above image origin imageOriginX: 300f, imageOriginY: 50f, panelWidth: 1280, panelHeight: 720); Assert.Null(result); } [Fact] public void Mouse_below_image_returns_null() { // screenY = 50 + 720 = 770 → iy = 720 which is >= panelHeight (720). var result = Map(mouseScreenX: 400f, mouseScreenY: 770f, imageOriginX: 300f, imageOriginY: 50f, panelWidth: 1280, panelHeight: 720); Assert.Null(result); } [Fact] public void Y_is_not_inverted_after_vflip() { // Confirm the "no extra Y inversion" contract: // The image is V-flipped in ImGui (uv0.Y=1, uv1.Y=0), so screen top row = panel Y=0. // A click near the TOP of the image should give a SMALL panel Y, not a large one. // Image origin at (300, 50). Click at (400, 55) → panel (100, 5). Y is small (near top). var result = Map(mouseScreenX: 400f, mouseScreenY: 55f, imageOriginX: 300f, imageOriginY: 50f, panelWidth: 1280, panelHeight: 720); Assert.Equal((100, 5), result); // NOT (100, 715) — which would be the result if Y were incorrectly inverted. Assert.True(result!.Value.py < 720 / 2, "Y near screen top should map to small panel Y, not near bottom"); } [Fact] public void LargeChrome_offset_is_fully_absorbed() { // Simulate a canvas sub-window with large chrome: ImGui title (20px) + padding (8px) // puts the image origin at screenY = menuBar(22) + titleBar(20) + padding(8) = 50. // Also a wide tree pane puts imageOriginX = 280 + padding. // A click at screen (400, 110) with origin (290, 50) → panel (110, 60). var result = Map(mouseScreenX: 400f, mouseScreenY: 110f, imageOriginX: 290f, imageOriginY: 50f, panelWidth: 1280, panelHeight: 720); Assert.Equal((110, 60), result); } }