Skip to content

Commit

Permalink
camera: Make zooming keep mouse position consistent
Browse files Browse the repository at this point in the history
  • Loading branch information
foxnne committed Dec 19, 2024
1 parent 74178c0 commit e3ad4a1
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/editor/artboard/canvas.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
2 changes: 1 addition & 1 deletion src/editor/artboard/canvas_pack.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
2 changes: 1 addition & 1 deletion src/editor/artboard/flipbook/canvas.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/editor/artboard/flipbook/timeline.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/editor/popups/references.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
82 changes: 80 additions & 2 deletions src/gfx/camera.zig
Original file line number Diff line number Diff line change
@@ -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");

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit e3ad4a1

Please sign in to comment.