diff --git a/src/editor/artboard/canvas.zig b/src/editor/artboard/canvas.zig index e043094..77adffa 100644 --- a/src/editor/artboard/canvas.zig +++ b/src/editor/artboard/canvas.zig @@ -97,7 +97,7 @@ pub fn draw(file: *pixi.storage.Internal.PixiFile, core: *Core) void { sprite_camera.setNearestZoomFloor(); const min_zoom = @min(sprite_camera.zoom, 1.0); - file.camera.processPanZoom(); + file.camera.processPanZoom(.primary); // Lock camera from zooming in or out too far for the flipbook file.camera.zoom = std.math.clamp(file.camera.zoom, min_zoom, pixi.state.settings.zoom_steps[pixi.state.settings.zoom_steps.len - 1]); diff --git a/src/editor/artboard/canvas_pack.zig b/src/editor/artboard/canvas_pack.zig index be78375..2aad2dd 100644 --- a/src/editor/artboard/canvas_pack.zig +++ b/src/editor/artboard/canvas_pack.zig @@ -35,7 +35,7 @@ pub fn draw(mode: PackTexture) void { sprite_camera.setNearestZoomFloor(); const min_zoom = @min(sprite_camera.zoom, 1.0); - camera.processPanZoom(); + camera.processPanZoom(.primary); // Lock camera from zooming in or out too far for the flipbook camera.zoom = std.math.clamp(camera.zoom, min_zoom, pixi.state.settings.zoom_steps[pixi.state.settings.zoom_steps.len - 1]); diff --git a/src/editor/artboard/flipbook/canvas.zig b/src/editor/artboard/flipbook/canvas.zig index b69be55..4caca9e 100644 --- a/src/editor/artboard/flipbook/canvas.zig +++ b/src/editor/artboard/flipbook/canvas.zig @@ -34,7 +34,7 @@ pub fn draw(file: *pixi.storage.Internal.PixiFile) void { if (pixi.state.settings.flipbook_view == .sequential) sprite_camera.setNearestZoomFloor() else sprite_camera.setNearZoomFloor(); const min_zoom = sprite_camera.zoom; - file.flipbook_camera.processPanZoom(); + file.flipbook_camera.processPanZoom(.flipbook); // Lock camera from zooming in or out too far for the flipbook file.flipbook_camera.zoom = std.math.clamp(file.flipbook_camera.zoom, min_zoom, max_zoom); diff --git a/src/editor/artboard/flipbook/timeline.zig b/src/editor/artboard/flipbook/timeline.zig index 0f88973..dce7fcc 100644 --- a/src/editor/artboard/flipbook/timeline.zig +++ b/src/editor/artboard/flipbook/timeline.zig @@ -165,7 +165,7 @@ pub fn draw(file: *pixi.storage.Internal.PixiFile) void { sprite_camera.setNearZoomFloor(); const min_zoom = 1.0; - file.flipbook_camera.processPanZoom(); + file.flipbook_camera.processPanZoom(.flipbook); // Lock camera from zooming in or out too far for the flipbook file.flipbook_camera.zoom = std.math.clamp(file.flipbook_camera.zoom, min_zoom, max_zoom); diff --git a/src/editor/popups/references.zig b/src/editor/popups/references.zig index 4e111d4..9874675 100644 --- a/src/editor/popups/references.zig +++ b/src/editor/popups/references.zig @@ -111,7 +111,7 @@ pub fn draw() void { camera.setNearestZoomFloor(); const min_zoom = @min(camera.zoom, pixi.state.settings.zoom_steps[0]); - reference.camera.processPanZoom(); + reference.camera.processPanZoom(.reference); // Lock camera from zooming in or out too far for the flipbook reference.camera.zoom = std.math.clamp(reference.camera.zoom, min_zoom, pixi.state.settings.zoom_steps[pixi.state.settings.zoom_steps.len - 1]); diff --git a/src/gfx/camera.zig b/src/gfx/camera.zig index 53ed3c9..c47a2de 100644 --- a/src/gfx/camera.zig +++ b/src/gfx/camera.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zm = @import("zmath"); const pixi = @import("../Pixi.zig"); -const core = @import("mach").core; +const mach = @import("mach"); const gpu = @import("mach").gpu; const imgui = @import("zig-imgui"); @@ -598,6 +598,26 @@ pub const Camera = struct { return pixel_pos; } + pub fn coordinatesRaw(camera: Camera, options: PixelCoordinatesOptions) [2]f32 { + const screen_position = imgui.getCursorScreenPos(); + var tl = camera.matrix().transformVec2(options.texture_position); + tl[0] += screen_position.x; + tl[1] += screen_position.y; + var br = options.texture_position; + br[0] += @as(f32, @floatFromInt(options.width)); + br[1] += @as(f32, @floatFromInt(options.height)); + br = camera.matrix().transformVec2(br); + br[0] += screen_position.x; + br[1] += screen_position.y; + + var pixel_pos: [2]f32 = .{ 0.0, 0.0 }; + + pixel_pos[0] = (options.position[0] - tl[0]) / camera.zoom; + pixel_pos[1] = (options.position[1] - tl[1]) / camera.zoom; + + return pixel_pos; + } + const FlipbookPixelCoordinatesOptions = struct { sprite_position: [2]f32, position: [2]f32, @@ -629,7 +649,13 @@ pub const Camera = struct { } else return null; } - pub fn processPanZoom(camera: *Camera) void { + pub const PanZoomTarget = enum { + primary, + flipbook, + reference, + }; + + pub fn processPanZoom(camera: *Camera, target: PanZoomTarget) void { var zoom_key = if (pixi.state.hotkeys.hotkey(.{ .proc = .zoom })) |hotkey| hotkey.down() else false; if (pixi.state.settings.input_scheme != .trackpad) zoom_key = true; @@ -642,6 +668,29 @@ pub const Camera = struct { } pixi.state.mouse.scroll_x = null; } + const previous_zoom = camera.zoom; + + const previous_mouse = switch (target) { + .primary => camera.coordinatesRaw(.{ + .texture_position = pixi.state.open_files.items[pixi.state.open_file_index].canvasCenterOffset(.primary), + .position = pixi.state.mouse.position, + .width = pixi.state.open_files.items[pixi.state.open_file_index].width, + .height = pixi.state.open_files.items[pixi.state.open_file_index].height, + }), + .flipbook => camera.coordinatesRaw(.{ + .texture_position = pixi.state.open_files.items[pixi.state.open_file_index].canvasCenterOffset(.flipbook), + .position = pixi.state.mouse.position, + .width = pixi.state.open_files.items[pixi.state.open_file_index].width, + .height = pixi.state.open_files.items[pixi.state.open_file_index].height, + }), + .reference => camera.coordinatesRaw(.{ + .texture_position = pixi.state.open_references.items[pixi.state.open_reference_index].canvasCenterOffset(), + .position = pixi.state.mouse.position, + .width = pixi.state.open_references.items[pixi.state.open_reference_index].texture.image.width, + .height = pixi.state.open_references.items[pixi.state.open_reference_index].texture.image.height, + }), + }; + if (pixi.state.mouse.scroll_y) |y| { if (zoom_key) { camera.zoom_timer = 0.0; @@ -685,6 +734,35 @@ pub const Camera = struct { pixi.state.mouse.magnify = null; } + const zoom_delta = camera.zoom - previous_zoom; + if (@abs(zoom_delta) > 0.0) { + // Get the distance to the mouse from camera center before the zoom + const current_mouse = switch (target) { + .primary => camera.coordinatesRaw(.{ + .texture_position = pixi.state.open_files.items[pixi.state.open_file_index].canvasCenterOffset(.primary), + .position = pixi.state.mouse.position, + .width = pixi.state.open_files.items[pixi.state.open_file_index].width, + .height = pixi.state.open_files.items[pixi.state.open_file_index].height, + }), + .flipbook => camera.coordinatesRaw(.{ + .texture_position = pixi.state.open_files.items[pixi.state.open_file_index].canvasCenterOffset(.flipbook), + .position = pixi.state.mouse.position, + .width = pixi.state.open_files.items[pixi.state.open_file_index].width, + .height = pixi.state.open_files.items[pixi.state.open_file_index].height, + }), + .reference => camera.coordinatesRaw(.{ + .texture_position = pixi.state.open_references.items[pixi.state.open_reference_index].canvasCenterOffset(), + .position = pixi.state.mouse.position, + .width = pixi.state.open_references.items[pixi.state.open_reference_index].texture.image.width, + .height = pixi.state.open_references.items[pixi.state.open_reference_index].texture.image.height, + }), + }; + const difference: [2]f32 = .{ previous_mouse[0] - current_mouse[0], previous_mouse[1] - current_mouse[1] }; + + camera.position[0] += difference[0]; + camera.position[1] += difference[1]; + } + const mouse_drag_delta = imgui.getMouseDragDelta(imgui.MouseButton_Middle, 0.0); if (mouse_drag_delta.x != 0.0 or mouse_drag_delta.y != 0.0) { camera.position[0] -= mouse_drag_delta.x * (1.0 / camera.zoom);