zoom factor

This commit is contained in:
2026-05-31 23:51:51 -05:00
parent 067bb9bee0
commit dc5b903741
5 changed files with 44 additions and 17 deletions
+2
View File
@@ -320,6 +320,8 @@ pub struct Board {
pub portals: Vec<PortalDef>,
/// Optional font override for this board. When `None`, the app default is used.
pub font: Option<FontSpec>,
/// Integer scale factor applied to tiles when rendering this board. 1 = natural size.
pub zoom: u32,
/// Named Rhai scripts available on this board: script name → source text.
///
/// All script source lives here; [`ObjectDef`]s and [`Board::board_script_name`]
+7
View File
@@ -50,6 +50,9 @@ pub struct MapHeader {
/// Name of the board-level script in the `[scripts]` table, if any.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub board_script_name: Option<String>,
/// Integer tile scale factor. 1 = natural size. Omitted from files when 1 (the default).
#[serde(default = "default_zoom", skip_serializing_if = "is_zoom_default")]
pub zoom: u32,
}
/// The `[font]` section of a map file, specifying a bitmap font for this board.
@@ -150,6 +153,8 @@ pub(crate) struct MapFileObjectEntry {
}
fn default_true() -> bool { true }
fn default_zoom() -> u32 { 1 }
fn is_zoom_default(z: &u32) -> bool { *z == 1 }
/// Parses an `"#RRGGBB"` hex color string into an [`Rgba8`].
/// Returns opaque black on any parse failure.
@@ -282,6 +287,7 @@ impl TryFrom<MapFile> for Board {
objects,
portals: mf.portals,
font,
zoom: mf.map.zoom,
scripts: mf.scripts,
board_script_name: mf.map.board_script_name,
})
@@ -399,6 +405,7 @@ impl From<&Board> for MapFile {
height: board.height,
player_start: [board.player.x, board.player.y],
board_script_name: board.board_script_name.clone(),
zoom: board.zoom,
},
palette,
grid: GridData { content },
+4
View File
@@ -227,6 +227,10 @@ pub(crate) fn show_editor_panel(
FontDialogState::from_spec(board.font.as_ref());
editor.font_dialog_open = true;
}
ui.separator();
ui.label("Zoom");
ui.add(egui::Slider::new(&mut board.zoom, 1..=8).text("×"));
}
EditorTab::Scripts => {
+7 -5
View File
@@ -197,8 +197,9 @@ impl eframe::App for App {
// Snapshot the active font for this frame so all rendering uses the same one.
// We re-borrow active_font here since apply_font_spec may have changed board_font.
let font = self.board_font.as_ref().unwrap_or(&self.default_font);
let tw = font.tile_w as f32;
let th = font.tile_h as f32;
let zoom = self.state.board.zoom;
let tw = font.tile_w as f32 * zoom as f32;
let th = font.tile_h as f32 * zoom as f32;
// --- Board rendering ---
// The board is drawn using the egui Painter API (direct 2D drawing).
@@ -211,7 +212,7 @@ impl eframe::App for App {
// Edit mode: ScrollArea lets the user scroll when the board overflows.
egui::ScrollArea::new([true, true]).show(ui, |ui| {
let (rect, response) = ui.allocate_exact_size(board_px, Sense::click());
draw_board(ui.painter(), rect.min, &self.state.board, font);
draw_board(ui.painter(), rect.min, &self.state.board, font, zoom);
// When the Objects tab is active, overlay the object highlights.
if self.editor.tab == EditorTab::Objects {
@@ -221,6 +222,7 @@ impl eframe::App for App {
&self.state.board,
font,
self.editor.selected_object,
zoom,
);
}
@@ -264,8 +266,8 @@ impl eframe::App for App {
// Play mode: player-centered viewport, no scroll bars.
let available = ui.available_rect_before_wrap();
let player = self.state.board.player;
let origin = board_origin(available, board_w, board_h, player, font);
draw_board(ui.painter(), origin, &self.state.board, font);
let origin = board_origin(available, board_w, board_h, player, font, zoom);
draw_board(ui.painter(), origin, &self.state.board, font, zoom);
}
});
+24 -12
View File
@@ -65,7 +65,7 @@ pub(crate) fn paint_glyph(painter: &egui::Painter, rect: Rect, glyph: &Glyph, fo
///
/// `origin` is the pixel position of cell `(0, 0)` — the top-left corner of
/// the board within the panel. Cell positions are computed from `origin` using
/// the font's tile dimensions.
/// the font's tile dimensions multiplied by `zoom`.
pub(crate) fn draw_glyph(
painter: &egui::Painter,
origin: Pos2,
@@ -73,9 +73,10 @@ pub(crate) fn draw_glyph(
y: usize,
glyph: &Glyph,
font: &BitmapFont,
zoom: u32,
) {
let tw = font.tile_w as f32;
let th = font.tile_h as f32;
let tw = font.tile_w as f32 * zoom as f32;
let th = font.tile_h as f32 * zoom as f32;
let tl = origin + vec2(x as f32 * tw, y as f32 * th);
let rect = Rect::from_min_size(tl, vec2(tw, th));
paint_glyph(painter, rect, glyph, font);
@@ -85,17 +86,24 @@ pub(crate) fn draw_glyph(
///
/// Rendering order: grid (floor) → objects → player. Objects are drawn on top
/// of their grid cell's background, which is revealed if the object moves away.
pub(crate) fn draw_board(painter: &egui::Painter, origin: Pos2, board: &Board, font: &BitmapFont) {
/// `zoom` scales each tile by an integer factor.
pub(crate) fn draw_board(
painter: &egui::Painter,
origin: Pos2,
board: &Board,
font: &BitmapFont,
zoom: u32,
) {
// Pass 1: grid floor.
for y in 0..board.height {
for x in 0..board.width {
let (glyph, _) = board.get(x, y);
draw_glyph(painter, origin, x, y, glyph, font);
draw_glyph(painter, origin, x, y, glyph, font, zoom);
}
}
// Pass 2: objects drawn on top of the floor.
for obj in &board.objects {
draw_glyph(painter, origin, obj.x, obj.y, &obj.glyph, font);
draw_glyph(painter, origin, obj.x, obj.y, &obj.glyph, font, zoom);
}
// Pass 3: player overlay. Will be removed once the player becomes a scripted object.
let player_glyph = Glyph::player();
@@ -106,6 +114,7 @@ pub(crate) fn draw_board(painter: &egui::Painter, origin: Pos2, board: &Board, f
board.player.y as usize,
&player_glyph,
font,
zoom,
);
}
@@ -114,16 +123,18 @@ pub(crate) fn draw_board(painter: &egui::Painter, origin: Pos2, board: &Board, f
/// Dims the entire board with a semi-transparent fill, then for each
/// [`ObjectDef`](crate::game::ObjectDef) redraws that cell at full brightness
/// and outlines it in white. The selected object (by index into
/// `board.objects`) gets a yellow outline instead.
/// `board.objects`) gets a yellow outline instead. `zoom` must match the zoom
/// used to draw the board.
pub(crate) fn draw_object_overlays(
painter: &egui::Painter,
origin: Pos2,
board: &Board,
font: &BitmapFont,
selected: Option<usize>,
zoom: u32,
) {
let tw = font.tile_w as f32;
let th = font.tile_h as f32;
let tw = font.tile_w as f32 * zoom as f32;
let th = font.tile_h as f32 * zoom as f32;
// Dim the whole board so non-object cells recede visually.
let board_rect = Rect::from_min_size(
@@ -166,16 +177,17 @@ pub(crate) fn pos_to_cell(origin: Pos2, pos: Pos2, tile_w: f32, tile_h: f32) ->
///
/// If the board fits along an axis, it is centered. If it overflows, the
/// viewport is centered on the player and clamped so no empty space appears
/// at the board edges.
/// at the board edges. `zoom` scales tile dimensions before computing pixel sizes.
pub(crate) fn board_origin(
available: Rect,
board_w: usize,
board_h: usize,
player: Player,
font: &BitmapFont,
zoom: u32,
) -> Pos2 {
let tw = font.tile_w as f32;
let th = font.tile_h as f32;
let tw = font.tile_w as f32 * zoom as f32;
let th = font.tile_h as f32 * zoom as f32;
let board_px_w = board_w as f32 * tw;
let board_px_h = board_h as f32 * th;