massive commenting pass to explain random functions and their purpose

This commit is contained in:
grimsace
2026-05-18 10:30:59 -05:00
parent ab713a5a4f
commit c71cc73406
21 changed files with 229 additions and 0 deletions
+22
View File
@@ -56,6 +56,7 @@ pub struct DungeonApp {
} }
impl Default for DungeonApp { impl Default for DungeonApp {
// Provides default settings for this type.
fn default() -> Self { fn default() -> Self {
let settings = settings::load_settings().unwrap_or_default(); let settings = settings::load_settings().unwrap_or_default();
let mut app = Self { let mut app = Self {
@@ -89,6 +90,7 @@ impl Default for DungeonApp {
} }
impl Drop for DungeonApp { impl Drop for DungeonApp {
// Cleans up background resources when the app is dropped.
fn drop(&mut self) { fn drop(&mut self) {
if let Err(err) = settings::save_settings(&self.settings) { if let Err(err) = settings::save_settings(&self.settings) {
eprintln!("Failed to save settings: {err}"); eprintln!("Failed to save settings: {err}");
@@ -97,6 +99,7 @@ impl Drop for DungeonApp {
} }
impl eframe::App for DungeonApp { impl eframe::App for DungeonApp {
// Runs one UI frame and handles app interaction.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let panel_result = draw_side_panel( let panel_result = draw_side_panel(
ctx, ctx,
@@ -299,6 +302,7 @@ impl eframe::App for DungeonApp {
} }
} }
// Generates all levels.
pub fn generate_all_levels(settings: &UiSettings) -> Vec<DungeonLayout> { pub fn generate_all_levels(settings: &UiSettings) -> Vec<DungeonLayout> {
let level_count = if settings.min_levels == settings.max_levels { let level_count = if settings.min_levels == settings.max_levels {
settings.min_levels settings.min_levels
@@ -315,6 +319,7 @@ pub fn generate_all_levels(settings: &UiSettings) -> Vec<DungeonLayout> {
levels levels
} }
// Generates a single dungeon level from the UI settings.
pub fn generate_level(settings: &UiSettings, level_index: usize) -> DungeonLayout { pub fn generate_level(settings: &UiSettings, level_index: usize) -> DungeonLayout {
let level_seed = crate::seed::derive_seed(settings.seed, level_index as u64); let level_seed = crate::seed::derive_seed(settings.seed, level_index as u64);
let layout = layout::generate_layout( let layout = layout::generate_layout(
@@ -337,6 +342,7 @@ pub fn generate_level(settings: &UiSettings, level_index: usize) -> DungeonLayou
populate_random_markers(layout, settings) populate_random_markers(layout, settings)
} }
// Clamps dependent settings.
pub fn clamp_dependent_settings(settings: &mut UiSettings) { pub fn clamp_dependent_settings(settings: &mut UiSettings) {
if settings.min_room_size > settings.max_room_size { if settings.min_room_size > settings.max_room_size {
settings.max_room_size = settings.min_room_size; settings.max_room_size = settings.min_room_size;
@@ -377,6 +383,7 @@ pub fn clamp_dependent_settings(settings: &mut UiSettings) {
} }
impl DungeonApp { impl DungeonApp {
// Clears hover targets.
pub fn clear_hover_targets(&mut self) { pub fn clear_hover_targets(&mut self) {
self.hover_room_idx = None; self.hover_room_idx = None;
self.hover_corridor_idx = None; self.hover_corridor_idx = None;
@@ -387,6 +394,7 @@ impl DungeonApp {
self.hover_stair_idx = None; self.hover_stair_idx = None;
} }
// Resets transient state.
pub fn reset_transient_state(&mut self) { pub fn reset_transient_state(&mut self) {
self.drag_state = None; self.drag_state = None;
self.add_corridor_drag = None; self.add_corridor_drag = None;
@@ -395,6 +403,7 @@ impl DungeonApp {
self.pending_delete = false; self.pending_delete = false;
} }
// Captures the current app state for undo history.
pub fn snapshot(&self) -> AppSnapshot { pub fn snapshot(&self) -> AppSnapshot {
AppSnapshot { AppSnapshot {
settings: self.settings.clone(), settings: self.settings.clone(),
@@ -403,6 +412,7 @@ impl DungeonApp {
} }
} }
// Pushes undo snapshot.
pub fn push_undo_snapshot(&mut self) { pub fn push_undo_snapshot(&mut self) {
let snapshot = self.snapshot(); let snapshot = self.snapshot();
if self.undo_stack.last() == Some(&snapshot) { if self.undo_stack.last() == Some(&snapshot) {
@@ -415,6 +425,7 @@ impl DungeonApp {
self.redo_stack.clear(); self.redo_stack.clear();
} }
// Restores a saved app state snapshot.
pub fn restore_snapshot(&mut self, snapshot: AppSnapshot) { pub fn restore_snapshot(&mut self, snapshot: AppSnapshot) {
self.settings = snapshot.settings; self.settings = snapshot.settings;
self.levels = snapshot.levels; self.levels = snapshot.levels;
@@ -422,6 +433,7 @@ impl DungeonApp {
self.reset_transient_state(); self.reset_transient_state();
} }
// Restores the previous undo snapshot. undo.
pub fn undo(&mut self) { pub fn undo(&mut self) {
let Some(snapshot) = self.undo_stack.pop() else { let Some(snapshot) = self.undo_stack.pop() else {
return; return;
@@ -430,6 +442,7 @@ impl DungeonApp {
self.restore_snapshot(snapshot); self.restore_snapshot(snapshot);
} }
// Restores the next redo snapshot. redo.
pub fn redo(&mut self) { pub fn redo(&mut self) {
let Some(snapshot) = self.redo_stack.pop() else { let Some(snapshot) = self.redo_stack.pop() else {
return; return;
@@ -438,6 +451,7 @@ impl DungeonApp {
self.restore_snapshot(snapshot); self.restore_snapshot(snapshot);
} }
// Clears all generated layout content and resets editing state.
pub fn clear_layout(&mut self) { pub fn clear_layout(&mut self) {
if self.settings.active_level_index >= self.levels.len() { if self.settings.active_level_index >= self.levels.len() {
return; return;
@@ -448,6 +462,7 @@ impl DungeonApp {
self.reset_transient_state(); self.reset_transient_state();
} }
// Switches to composition layout.
pub fn enter_composition_layout(&mut self) { pub fn enter_composition_layout(&mut self) {
self.levels = vec![DungeonLayout::empty( self.levels = vec![DungeonLayout::empty(
self.settings.pack_rooms_without_corridors, self.settings.pack_rooms_without_corridors,
@@ -458,6 +473,7 @@ impl DungeonApp {
self.reset_transient_state(); self.reset_transient_state();
} }
// Regenerates every level from the current settings.
pub fn regenerate_layout(&mut self) { pub fn regenerate_layout(&mut self) {
self.drag_state = None; self.drag_state = None;
self.suppressed_auto_door_edges.clear(); self.suppressed_auto_door_edges.clear();
@@ -468,6 +484,7 @@ impl DungeonApp {
} }
} }
// Deletes a level and keeps the active level index valid.
pub fn delete_level(&mut self, level_idx: usize) { pub fn delete_level(&mut self, level_idx: usize) {
if self.levels.len() <= 1 { if self.levels.len() <= 1 {
return; return;
@@ -483,6 +500,7 @@ impl DungeonApp {
self.reset_transient_state(); self.reset_transient_state();
} }
// Regenerates one level while preserving manual annotations where possible.
pub fn refresh_level(&mut self, level_idx: usize) { pub fn refresh_level(&mut self, level_idx: usize) {
if level_idx >= self.levels.len() { if level_idx >= self.levels.len() {
return; return;
@@ -534,6 +552,7 @@ impl DungeonApp {
); );
} }
// Recomputes staircase links across the current levels.
pub fn refresh_stairs(&mut self) { pub fn refresh_stairs(&mut self) {
let results = populate_stairs(self.levels.clone(), &self.settings); let results = populate_stairs(self.levels.clone(), &self.settings);
self.levels = results.iter().map(|(layout, _)| layout.clone()).collect(); self.levels = results.iter().map(|(layout, _)| layout.clone()).collect();
@@ -545,6 +564,7 @@ impl DungeonApp {
} }
} }
// Recomputes generated doors while keeping manual doors.
pub fn refresh_doors(&mut self) { pub fn refresh_doors(&mut self) {
if self.settings.active_level_index >= self.levels.len() { if self.settings.active_level_index >= self.levels.len() {
return; return;
@@ -573,6 +593,7 @@ impl DungeonApp {
} }
} }
// Computes settings from ui.
pub fn door_settings_from_ui(settings: &UiSettings) -> DoorSettings { pub fn door_settings_from_ui(settings: &UiSettings) -> DoorSettings {
DoorSettings { DoorSettings {
frequency_percent: settings.door_frequency_percent, frequency_percent: settings.door_frequency_percent,
@@ -583,6 +604,7 @@ pub fn door_settings_from_ui(settings: &UiSettings) -> DoorSettings {
} }
} }
// Computes settings from ui.
pub fn window_settings_from_ui(settings: &UiSettings) -> WindowSettings { pub fn window_settings_from_ui(settings: &UiSettings) -> WindowSettings {
WindowSettings { WindowSettings {
enabled: settings.windows_enabled, enabled: settings.windows_enabled,
+14
View File
@@ -24,6 +24,7 @@ pub use types::*;
type ProgressSender = mpsc::Sender<ExportEvent>; type ProgressSender = mpsc::Sender<ExportEvent>;
// Selects export target.
pub fn select_export_target(settings: &UiSettings) -> Result<ExportTarget, String> { pub fn select_export_target(settings: &UiSettings) -> Result<ExportTarget, String> {
if settings.export_format == ExportFormat::Folder || settings.export_level_index == 999 { if settings.export_format == ExportFormat::Folder || settings.export_level_index == 999 {
let title = if settings.export_level_index == 999 { let title = if settings.export_level_index == 999 {
@@ -65,6 +66,7 @@ pub fn select_export_target(settings: &UiSettings) -> Result<ExportTarget, Strin
Ok(ExportTarget::File(path)) Ok(ExportTarget::File(path))
} }
// Exports to target.
pub fn export_to_target( pub fn export_to_target(
layouts: &[DungeonLayout], layouts: &[DungeonLayout],
settings: &UiSettings, settings: &UiSettings,
@@ -112,6 +114,7 @@ pub fn export_to_target(
} }
} }
// Exports image count.
fn export_image_count(settings: &UiSettings, target: &ExportTarget, level_count: usize) -> usize { fn export_image_count(settings: &UiSettings, target: &ExportTarget, level_count: usize) -> usize {
match target { match target {
ExportTarget::File(_) => 1, ExportTarget::File(_) => 1,
@@ -122,6 +125,7 @@ fn export_image_count(settings: &UiSettings, target: &ExportTarget, level_count:
} }
} }
// Counts images per level.
fn composite_images_per_level(settings: &UiSettings) -> usize { fn composite_images_per_level(settings: &UiSettings) -> usize {
let base_masks = 12; let base_masks = 12;
let grid_mask = usize::from(settings.export_show_grid); let grid_mask = usize::from(settings.export_show_grid);
@@ -129,10 +133,12 @@ fn composite_images_per_level(settings: &UiSettings) -> usize {
base_masks + grid_mask + composite base_masks + grid_mask + composite
} }
// Sends an export progress update to the UI channel.
fn report_progress(progress_tx: &ProgressSender, completed: usize, total: usize) { fn report_progress(progress_tx: &ProgressSender, completed: usize, total: usize) {
let _ = progress_tx.send(ExportEvent::Progress(ExportProgress { completed, total })); let _ = progress_tx.send(ExportEvent::Progress(ExportProgress { completed, total }));
} }
// Exports level images to folder.
fn export_level_images_to_folder( fn export_level_images_to_folder(
layouts: &[DungeonLayout], layouts: &[DungeonLayout],
settings: &UiSettings, settings: &UiSettings,
@@ -155,6 +161,7 @@ fn export_level_images_to_folder(
}) })
} }
// Saves layout to file.
fn save_layout_to_file( fn save_layout_to_file(
layout: &DungeonLayout, layout: &DungeonLayout,
settings: &UiSettings, settings: &UiSettings,
@@ -182,6 +189,7 @@ fn save_layout_to_file(
Ok(()) Ok(())
} }
// Exports composite masks for levels.
fn export_composite_masks_for_levels( fn export_composite_masks_for_levels(
layouts: &[DungeonLayout], layouts: &[DungeonLayout],
settings: &UiSettings, settings: &UiSettings,
@@ -265,6 +273,7 @@ fn export_composite_masks_for_levels(
Ok(()) Ok(())
} }
// Saves composite image.
fn save_composite_image( fn save_composite_image(
context: &CompositeExportContext<'_>, context: &CompositeExportContext<'_>,
settings: &UiSettings, settings: &UiSettings,
@@ -285,6 +294,7 @@ fn save_composite_image(
Ok(()) Ok(())
} }
// Renders mask pixmap.
fn render_mask_pixmap( fn render_mask_pixmap(
context: &CompositeExportContext<'_>, context: &CompositeExportContext<'_>,
task: MaskTask, task: MaskTask,
@@ -441,6 +451,7 @@ fn render_mask_pixmap(
Ok(result) Ok(result)
} }
// Creates an empty pixmap for a named export mask.
fn blank_mask(g: &ExportGeometry, name: &str) -> Result<Pixmap, String> { fn blank_mask(g: &ExportGeometry, name: &str) -> Result<Pixmap, String> {
let mut pixmap = Pixmap::new(g.width, g.height) let mut pixmap = Pixmap::new(g.width, g.height)
.ok_or_else(|| format!("Failed to allocate {name} mask canvas"))?; .ok_or_else(|| format!("Failed to allocate {name} mask canvas"))?;
@@ -448,6 +459,7 @@ fn blank_mask(g: &ExportGeometry, name: &str) -> Result<Pixmap, String> {
Ok(pixmap) Ok(pixmap)
} }
// Fills marker mask.
fn fill_marker_mask( fn fill_marker_mask(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -458,6 +470,7 @@ fn fill_marker_mask(
} }
} }
// Saves mask image.
fn save_mask_image( fn save_mask_image(
folder: &std::path::Path, folder: &std::path::Path,
stem: &str, stem: &str,
@@ -472,6 +485,7 @@ fn save_mask_image(
Ok(()) Ok(())
} }
// Selects image format.
fn mask_image_format(format: MaskFormat) -> ImageFormat { fn mask_image_format(format: MaskFormat) -> ImageFormat {
match format { match format {
MaskFormat::Png => ImageFormat::Png, MaskFormat::Png => ImageFormat::Png,
+25
View File
@@ -16,12 +16,14 @@ use image::{DynamicImage, RgbaImage, imageops::FilterType};
use std::collections::HashSet; use std::collections::HashSet;
use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Rect, Stroke, StrokeDash, Transform}; use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Rect, Stroke, StrokeDash, Transform};
// Renders a dungeon layout into a raster pixmap.
pub fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result<Pixmap, String> { pub fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result<Pixmap, String> {
let g = ExportGeometry::new(settings.cols, settings.rows); let g = ExportGeometry::new(settings.cols, settings.rows);
let scene = collect_scene_data(layout, settings); let scene = collect_scene_data(layout, settings);
render_pixmap_with_scene(layout, settings, &g, &scene) render_pixmap_with_scene(layout, settings, &g, &scene)
} }
// Renders pixmap with scene.
pub fn render_pixmap_with_scene( pub fn render_pixmap_with_scene(
layout: &DungeonLayout, layout: &DungeonLayout,
settings: &UiSettings, settings: &UiSettings,
@@ -150,6 +152,7 @@ pub fn render_pixmap_with_scene(
Ok(pixmap) Ok(pixmap)
} }
// Fills the entire pixmap with a background color.
pub fn fill_bg(pixmap: &mut Pixmap, color: (u8, u8, u8, u8)) { pub fn fill_bg(pixmap: &mut Pixmap, color: (u8, u8, u8, u8)) {
let mut paint = Paint::default(); let mut paint = Paint::default();
paint.set_color_rgba8(color.0, color.1, color.2, color.3); paint.set_color_rgba8(color.0, color.1, color.2, color.3);
@@ -160,6 +163,7 @@ pub fn fill_bg(pixmap: &mut Pixmap, color: (u8, u8, u8, u8)) {
pixmap.fill_rect(rect, &paint, Transform::identity(), None); pixmap.fill_rect(rect, &paint, Transform::identity(), None);
} }
// Draws grid color.
pub fn draw_grid_color(pixmap: &mut Pixmap, g: &ExportGeometry, color: (u8, u8, u8, u8)) { pub fn draw_grid_color(pixmap: &mut Pixmap, g: &ExportGeometry, color: (u8, u8, u8, u8)) {
let left = g.left(); let left = g.left();
let top = g.top(); let top = g.top();
@@ -185,6 +189,7 @@ pub fn draw_grid_color(pixmap: &mut Pixmap, g: &ExportGeometry, color: (u8, u8,
} }
} }
// Fills rect cell.
pub fn fill_rect_cell( pub fn fill_rect_cell(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -200,6 +205,7 @@ pub fn fill_rect_cell(
pixmap.fill_rect(rect, &paint, Transform::identity(), None); pixmap.fill_rect(rect, &paint, Transform::identity(), None);
} }
// Fills rect room.
pub fn fill_rect_room( pub fn fill_rect_room(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -220,6 +226,7 @@ pub fn fill_rect_room(
pixmap.fill_rect(rect, &paint, Transform::identity(), None); pixmap.fill_rect(rect, &paint, Transform::identity(), None);
} }
// Fills rect marker.
pub fn fill_rect_marker( pub fn fill_rect_marker(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -239,6 +246,7 @@ pub fn fill_rect_marker(
pixmap.fill_rect(rect, &paint, Transform::identity(), None); pixmap.fill_rect(rect, &paint, Transform::identity(), None);
} }
// Draws area marker.
pub fn draw_area_marker( pub fn draw_area_marker(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -374,6 +382,7 @@ pub fn draw_area_marker(
} }
} }
// Fills rect staircase.
pub fn fill_rect_staircase( pub fn fill_rect_staircase(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -393,6 +402,7 @@ pub fn fill_rect_staircase(
pixmap.fill_rect(rect, &paint, Transform::identity(), None); pixmap.fill_rect(rect, &paint, Transform::identity(), None);
} }
// Draws a staircase marker into the raster export.
pub fn draw_staircase( pub fn draw_staircase(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -451,6 +461,7 @@ pub fn draw_staircase(
} }
} }
// Draws staircase group.
pub fn draw_staircase_group( pub fn draw_staircase_group(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -463,6 +474,7 @@ pub fn draw_staircase_group(
} }
} }
// Draws area marker group.
pub fn draw_area_marker_group( pub fn draw_area_marker_group(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -476,6 +488,7 @@ pub fn draw_area_marker_group(
} }
} }
// Draws a filled circular dot at a pixel position.
pub fn draw_dot(pixmap: &mut Pixmap, cx: f32, cy: f32, radius: f32, color: (u8, u8, u8, u8)) { pub fn draw_dot(pixmap: &mut Pixmap, cx: f32, cy: f32, radius: f32, color: (u8, u8, u8, u8)) {
let mut pb = PathBuilder::new(); let mut pb = PathBuilder::new();
pb.push_circle(cx, cy, radius); pb.push_circle(cx, cy, radius);
@@ -493,6 +506,7 @@ pub fn draw_dot(pixmap: &mut Pixmap, cx: f32, cy: f32, radius: f32, color: (u8,
); );
} }
// Draws room crosshatch.
pub fn draw_room_crosshatch( pub fn draw_room_crosshatch(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -529,6 +543,7 @@ pub fn draw_room_crosshatch(
} }
} }
// Draws cell walls.
pub fn draw_cell_walls( pub fn draw_cell_walls(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -611,6 +626,7 @@ pub fn draw_cell_walls(
} }
} }
// Draws wall segment.
pub fn draw_wall_segment( pub fn draw_wall_segment(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
x1: f32, x1: f32,
@@ -647,6 +663,7 @@ pub fn draw_wall_segment(
pixmap.fill_rect(rect, &paint, Transform::identity(), None); pixmap.fill_rect(rect, &paint, Transform::identity(), None);
} }
// Snaps even width.
pub fn snap_even_width(value: f32) -> f32 { pub fn snap_even_width(value: f32) -> f32 {
let mut w = value.round().max(1.0); let mut w = value.round().max(1.0);
if (w as u32) % 2 == 1 { if (w as u32) % 2 == 1 {
@@ -655,6 +672,7 @@ pub fn snap_even_width(value: f32) -> f32 {
w w
} }
// Draws a styled door line onto the pixmap.
pub fn draw_door( pub fn draw_door(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -670,6 +688,7 @@ pub fn draw_door(
draw_line(pixmap, x1, y1, x2, y2, width, color, style); draw_line(pixmap, x1, y1, x2, y2, width, color, style);
} }
// Draws a styled window line onto the pixmap.
pub fn draw_window( pub fn draw_window(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
g: &ExportGeometry, g: &ExportGeometry,
@@ -684,6 +703,7 @@ pub fn draw_window(
draw_line(pixmap, x1, y1, x2, y2, width, color, style); draw_line(pixmap, x1, y1, x2, y2, width, color, style);
} }
// Draws a solid or patterned line segment.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn draw_line( pub fn draw_line(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
@@ -707,6 +727,7 @@ pub fn draw_line(
} }
} }
// Strokes a path segment with an optional dash pattern.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn stroke_path( pub fn stroke_path(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
@@ -739,6 +760,7 @@ pub fn stroke_path(
pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None); pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
} }
// Handles dotted line.
pub fn dotted_line( pub fn dotted_line(
pixmap: &mut Pixmap, pixmap: &mut Pixmap,
x1: f32, x1: f32,
@@ -766,6 +788,7 @@ pub fn dotted_line(
} }
} }
// Handles raster image from pixmap.
pub fn raster_image_from_pixmap( pub fn raster_image_from_pixmap(
pixmap: &Pixmap, pixmap: &Pixmap,
settings: &UiSettings, settings: &UiSettings,
@@ -782,6 +805,7 @@ pub fn raster_image_from_pixmap(
Ok(img) Ok(img)
} }
// Handles raster mask image from pixmap.
pub fn raster_mask_image_from_pixmap( pub fn raster_mask_image_from_pixmap(
pixmap: &Pixmap, pixmap: &Pixmap,
settings: &UiSettings, settings: &UiSettings,
@@ -805,6 +829,7 @@ pub fn raster_mask_image_from_pixmap(
Ok(DynamicImage::ImageRgba8(out)) Ok(DynamicImage::ImageRgba8(out))
} }
// Handles raster target size.
pub fn raster_target_size(settings: &UiSettings) -> (u32, u32) { pub fn raster_target_size(settings: &UiSettings) -> (u32, u32) {
let width = settings.export_width.clamp(1, 10_000); let width = settings.export_width.clamp(1, 10_000);
let mut height = settings.export_height.clamp(1, 10_000); let mut height = settings.export_height.clamp(1, 10_000);
+4
View File
@@ -14,6 +14,7 @@ use crate::ui::UiSettings;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::Write as _; use std::fmt::Write as _;
// Builds the complete SVG document for a dungeon layout.
pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String { pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
let g = ExportGeometry::new(settings.cols, settings.rows); let g = ExportGeometry::new(settings.cols, settings.rows);
let wall_w = (g.cell / 2.5).max(1.0); let wall_w = (g.cell / 2.5).max(1.0);
@@ -253,6 +254,7 @@ pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
s s
} }
// Appends svg walls.
pub fn append_svg_walls( pub fn append_svg_walls(
out: &mut String, out: &mut String,
g: &ExportGeometry, g: &ExportGeometry,
@@ -329,6 +331,7 @@ pub fn append_svg_walls(
} }
} }
// Appends svg area marker.
pub fn append_svg_area_marker( pub fn append_svg_area_marker(
out: &mut String, out: &mut String,
g: &ExportGeometry, g: &ExportGeometry,
@@ -419,6 +422,7 @@ pub fn append_svg_area_marker(
} }
} }
// Appends svg area marker group.
pub fn append_svg_area_marker_group( pub fn append_svg_area_marker_group(
out: &mut String, out: &mut String,
g: &ExportGeometry, g: &ExportGeometry,
+9
View File
@@ -66,6 +66,7 @@ pub struct ExportGeometry {
} }
impl ExportGeometry { impl ExportGeometry {
// Creates a new instance with the given inputs.
pub fn new(cols: usize, rows: usize) -> Self { pub fn new(cols: usize, rows: usize) -> Self {
let cell = 64.0; let cell = 64.0;
let pad = 0.0; let pad = 0.0;
@@ -81,14 +82,17 @@ impl ExportGeometry {
} }
} }
// Returns the left edge of the export canvas.
pub fn left(&self) -> f32 { pub fn left(&self) -> f32 {
self.pad self.pad
} }
// Returns the top edge of the export canvas.
pub fn top(&self) -> f32 { pub fn top(&self) -> f32 {
self.pad self.pad
} }
// Returns the pixel rectangle for a grid cell.
pub fn cell_rect(&self, col: usize, row: usize) -> Option<Rect> { pub fn cell_rect(&self, col: usize, row: usize) -> Option<Rect> {
Rect::from_xywh( Rect::from_xywh(
self.left() + col as f32 * self.cell, self.left() + col as f32 * self.cell,
@@ -98,6 +102,7 @@ impl ExportGeometry {
) )
} }
// Returns the pixel center point for a grid cell.
pub fn cell_center(&self, col: usize, row: usize) -> (f32, f32) { pub fn cell_center(&self, col: usize, row: usize) -> (f32, f32) {
( (
self.left() + (col as f32 + 0.5) * self.cell, self.left() + (col as f32 + 0.5) * self.cell,
@@ -147,10 +152,12 @@ pub struct CompositeExportContext<'a> {
pub door_width: f32, pub door_width: f32,
} }
// Normalizes a cell edge so either endpoint order compares equal.
pub fn norm_edge(a: Cell, b: Cell) -> CellEdge { pub fn norm_edge(a: Cell, b: Cell) -> CellEdge {
if a <= b { (a, b) } else { (b, a) } if a <= b { (a, b) } else { (b, a) }
} }
// Computes edges for.
pub fn door_edges_for(door: &Door, cols: usize, rows: usize) -> Vec<CellEdge> { pub fn door_edges_for(door: &Door, cols: usize, rows: usize) -> Vec<CellEdge> {
let mut edges = Vec::new(); let mut edges = Vec::new();
if door.from.0.abs_diff(door.to.0) + door.from.1.abs_diff(door.to.1) != 1 { if door.from.0.abs_diff(door.to.0) + door.from.1.abs_diff(door.to.1) != 1 {
@@ -188,6 +195,7 @@ pub fn door_edges_for(door: &Door, cols: usize, rows: usize) -> Vec<CellEdge> {
edges edges
} }
// Computes render width.
pub fn door_render_width(door: &Door) -> usize { pub fn door_render_width(door: &Door) -> usize {
if door.span_width { if door.span_width {
door.width.max(1) door.width.max(1)
@@ -196,6 +204,7 @@ pub fn door_render_width(door: &Door) -> usize {
} }
} }
// Computes render width.
pub fn window_render_width(window: &Window) -> usize { pub fn window_render_width(window: &Window) -> usize {
if window.span_width { if window.span_width {
window.width.max(1) window.width.max(1)
+5
View File
@@ -9,6 +9,7 @@ use crate::layout::{DungeonLayout, Room, Window, WindowSide, corridor_cells};
use crate::ui::UiSettings; use crate::ui::UiSettings;
use std::collections::HashSet; use std::collections::HashSet;
// Collects scene data.
pub fn collect_scene_data(layout: &DungeonLayout, settings: &UiSettings) -> SceneData { pub fn collect_scene_data(layout: &DungeonLayout, settings: &UiSettings) -> SceneData {
let mut scene = SceneData::default(); let mut scene = SceneData::default();
@@ -33,6 +34,7 @@ pub fn collect_scene_data(layout: &DungeonLayout, settings: &UiSettings) -> Scen
scene scene
} }
// Computes edges from cells.
pub fn corridor_edges_from_cells( pub fn corridor_edges_from_cells(
corridor_cells: &HashSet<(usize, usize)>, corridor_cells: &HashSet<(usize, usize)>,
) -> HashSet<((usize, usize), (usize, usize))> { ) -> HashSet<((usize, usize), (usize, usize))> {
@@ -50,6 +52,7 @@ pub fn corridor_edges_from_cells(
edges edges
} }
// Computes edges from rooms.
pub fn room_edges_from_rooms(rooms: &[Room]) -> HashSet<((usize, usize), (usize, usize))> { pub fn room_edges_from_rooms(rooms: &[Room]) -> HashSet<((usize, usize), (usize, usize))> {
let mut edges = HashSet::new(); let mut edges = HashSet::new();
for room in rooms { for room in rooms {
@@ -69,6 +72,7 @@ pub fn room_edges_from_rooms(rooms: &[Room]) -> HashSet<((usize, usize), (usize,
edges edges
} }
// Computes line points.
pub fn door_line_points( pub fn door_line_points(
g: &ExportGeometry, g: &ExportGeometry,
a: (usize, usize), a: (usize, usize),
@@ -102,6 +106,7 @@ pub fn door_line_points(
} }
} }
// Computes line points.
pub fn window_line_points(g: &ExportGeometry, window: &Window) -> Option<(f32, f32, f32, f32)> { pub fn window_line_points(g: &ExportGeometry, window: &Window) -> Option<(f32, f32, f32, f32)> {
let width_cells = window_render_width(window).max(1) as isize; let width_cells = window_render_width(window).max(1) as isize;
let min_offset = -((width_cells - 1) / 2); let min_offset = -((width_cells - 1) / 2);
+10
View File
@@ -17,6 +17,7 @@ use crate::layout::{
use crate::ui::AddTool; use crate::ui::AddTool;
use eframe::egui; use eframe::egui;
// Handles add tool.
pub fn handle_add_tool(app: &mut DungeonApp, response: &egui::Response, geometry: &GridGeometry) { pub fn handle_add_tool(app: &mut DungeonApp, response: &egui::Response, geometry: &GridGeometry) {
if app.settings.add_tool != AddTool::None && response.secondary_clicked() { if app.settings.add_tool != AddTool::None && response.secondary_clicked() {
app.settings.add_tool = AddTool::None; app.settings.add_tool = AddTool::None;
@@ -122,6 +123,7 @@ pub fn handle_add_tool(app: &mut DungeonApp, response: &egui::Response, geometry
} }
} }
// Deletes at pointer.
pub fn delete_at_pointer(app: &mut DungeonApp, pointer_pos: egui::Pos2, geometry: &GridGeometry) { pub fn delete_at_pointer(app: &mut DungeonApp, pointer_pos: egui::Pos2, geometry: &GridGeometry) {
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
return; return;
@@ -236,6 +238,7 @@ pub fn delete_at_pointer(app: &mut DungeonApp, pointer_pos: egui::Pos2, geometry
} }
} }
// Adds room at cell.
pub fn add_room_at_cell(app: &mut DungeonApp, cell: (usize, usize)) { pub fn add_room_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.push_undo_snapshot(); app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
@@ -275,6 +278,7 @@ pub fn add_room_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.refresh_doors(); app.refresh_doors();
} }
// Adds corridor between.
pub fn add_corridor_between(app: &mut DungeonApp, start: (usize, usize), end: (usize, usize)) { pub fn add_corridor_between(app: &mut DungeonApp, start: (usize, usize), end: (usize, usize)) {
app.push_undo_snapshot(); app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
@@ -319,6 +323,7 @@ pub fn add_corridor_between(app: &mut DungeonApp, start: (usize, usize), end: (u
app.refresh_doors(); app.refresh_doors();
} }
// Adds text at cell.
pub fn add_text_at_cell(app: &mut DungeonApp, cell: (usize, usize)) { pub fn add_text_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.push_undo_snapshot(); app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
@@ -351,6 +356,7 @@ pub fn add_text_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
}); });
} }
// Adds marker at cell.
pub fn add_marker_at_cell(app: &mut DungeonApp, cell: (usize, usize), kind: MarkerKind) { pub fn add_marker_at_cell(app: &mut DungeonApp, cell: (usize, usize), kind: MarkerKind) {
app.push_undo_snapshot(); app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
@@ -374,6 +380,7 @@ pub fn add_marker_at_cell(app: &mut DungeonApp, cell: (usize, usize), kind: Mark
} }
} }
// Adds staircase at cell.
pub fn add_staircase_at_cell(app: &mut DungeonApp, cell: (usize, usize)) { pub fn add_staircase_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.push_undo_snapshot(); app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
@@ -416,6 +423,7 @@ pub fn add_staircase_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
} }
} }
// Deletes a room and removes dependent generated elements.
pub fn delete_room(app: &mut DungeonApp, room_idx: usize) { pub fn delete_room(app: &mut DungeonApp, room_idx: usize) {
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
return; return;
@@ -441,6 +449,7 @@ pub fn delete_room(app: &mut DungeonApp, room_idx: usize) {
app.refresh_doors(); app.refresh_doors();
} }
// Adds door at pointer.
pub fn add_door_at_pointer( pub fn add_door_at_pointer(
app: &mut DungeonApp, app: &mut DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -525,6 +534,7 @@ pub fn add_door_at_pointer(
app.suppressed_auto_door_edges.remove(&target); app.suppressed_auto_door_edges.remove(&target);
} }
// Checks whether manual door edge allowed.
pub fn manual_door_edge_allowed( pub fn manual_door_edge_allowed(
a_room: Option<usize>, a_room: Option<usize>,
b_room: Option<usize>, b_room: Option<usize>,
+7
View File
@@ -14,6 +14,7 @@ use crate::layout::{blocked_room_cells, shortest_path_cells};
use crate::ui::AddTool; use crate::ui::AddTool;
use eframe::egui; use eframe::egui;
// Handles active drag gestures for rooms, corridors, markers, stairs, and text.
pub fn handle_drag( pub fn handle_drag(
app: &mut DungeonApp, app: &mut DungeonApp,
ctx: &egui::Context, ctx: &egui::Context,
@@ -80,6 +81,7 @@ pub fn handle_drag(
} }
} }
// Drags room to pointer.
pub fn drag_room_to_pointer( pub fn drag_room_to_pointer(
app: &mut DungeonApp, app: &mut DungeonApp,
drag: RoomDragState, drag: RoomDragState,
@@ -121,6 +123,7 @@ pub fn drag_room_to_pointer(
app.refresh_doors(); app.refresh_doors();
} }
// Drags text to pointer.
pub fn drag_text_to_pointer( pub fn drag_text_to_pointer(
app: &mut DungeonApp, app: &mut DungeonApp,
drag: TextDragState, drag: TextDragState,
@@ -152,6 +155,7 @@ pub fn drag_text_to_pointer(
app.levels[app.settings.active_level_index].text_labels[drag.text_idx].cell = target; app.levels[app.settings.active_level_index].text_labels[drag.text_idx].cell = target;
} }
// Reroutes corridors for room.
pub fn reroute_corridors_for_room(app: &mut DungeonApp, room_idx: usize) { pub fn reroute_corridors_for_room(app: &mut DungeonApp, room_idx: usize) {
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
return; return;
@@ -181,6 +185,7 @@ pub fn reroute_corridors_for_room(app: &mut DungeonApp, room_idx: usize) {
} }
} }
// Drags corridor to pointer.
pub fn drag_corridor_to_pointer( pub fn drag_corridor_to_pointer(
app: &mut DungeonApp, app: &mut DungeonApp,
drag: CorridorDragState, drag: CorridorDragState,
@@ -225,6 +230,7 @@ pub fn drag_corridor_to_pointer(
app.refresh_doors(); app.refresh_doors();
} }
// Drags marker to pointer.
pub fn drag_marker_to_pointer( pub fn drag_marker_to_pointer(
app: &mut DungeonApp, app: &mut DungeonApp,
drag: MarkerDragState, drag: MarkerDragState,
@@ -258,6 +264,7 @@ pub fn drag_marker_to_pointer(
marker.cell = (target_x.min(max_x), target_y.min(max_y)); marker.cell = (target_x.min(max_x), target_y.min(max_y));
} }
// Drags stair to pointer.
pub fn drag_stair_to_pointer( pub fn drag_stair_to_pointer(
app: &mut DungeonApp, app: &mut DungeonApp,
drag: StaircaseDragState, drag: StaircaseDragState,
+32
View File
@@ -28,6 +28,7 @@ pub struct GridGeometry {
pub rows: usize, pub rows: usize,
} }
// Draws the editable grid and returns its screen geometry.
pub fn draw_grid( pub fn draw_grid(
painter: &egui::Painter, painter: &egui::Painter,
area: egui::Rect, area: egui::Rect,
@@ -82,6 +83,7 @@ pub fn draw_grid(
} }
} }
// Handles update hover targets.
pub fn update_hover_targets(app: &mut DungeonApp, ctx: &egui::Context, geometry: &GridGeometry) { pub fn update_hover_targets(app: &mut DungeonApp, ctx: &egui::Context, geometry: &GridGeometry) {
if app.drag_state.is_some() || app.resize_state.is_some() || app.add_corridor_drag.is_some() { if app.drag_state.is_some() || app.resize_state.is_some() || app.add_corridor_drag.is_some() {
return; return;
@@ -158,6 +160,7 @@ pub fn update_hover_targets(app: &mut DungeonApp, ctx: &egui::Context, geometry:
} }
} }
// Computes edge at pointer.
pub fn door_edge_at_pointer( pub fn door_edge_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -210,6 +213,7 @@ pub fn door_edge_at_pointer(
Some(((ax as usize, ay as usize), (bx as usize, by as usize))) Some(((ax as usize, ay as usize), (bx as usize, by as usize)))
} }
// Computes at pointer.
pub fn room_at_pointer( pub fn room_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -236,6 +240,7 @@ pub fn room_at_pointer(
None None
} }
// Computes at pointer.
pub fn text_at_pointer( pub fn text_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -254,6 +259,7 @@ pub fn text_at_pointer(
.position(|label| label.cell == clicked) .position(|label| label.cell == clicked)
} }
// Computes at pointer.
pub fn marker_at_pointer( pub fn marker_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -298,6 +304,7 @@ pub fn marker_at_pointer(
}) })
} }
// Computes drag at pointer.
pub fn marker_drag_at_pointer( pub fn marker_drag_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -349,6 +356,7 @@ pub fn marker_drag_at_pointer(
} }
} }
// Handles stair at pointer.
pub fn stair_at_pointer( pub fn stair_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -369,6 +377,7 @@ pub fn stair_at_pointer(
}) })
} }
// Handles stair drag at pointer.
pub fn stair_drag_at_pointer( pub fn stair_drag_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -390,6 +399,7 @@ pub fn stair_drag_at_pointer(
}) })
} }
// Resizes room at pointer.
pub fn resize_room_at_pointer( pub fn resize_room_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -421,6 +431,7 @@ pub fn resize_room_at_pointer(
None None
} }
// Computes drag at pointer.
pub fn corridor_drag_at_pointer( pub fn corridor_drag_at_pointer(
app: &DungeonApp, app: &DungeonApp,
pointer_pos: egui::Pos2, pointer_pos: egui::Pos2,
@@ -445,6 +456,7 @@ pub fn corridor_drag_at_pointer(
}) })
} }
// Converts to grid.
pub fn pointer_to_grid(pointer_pos: egui::Pos2, geometry: &GridGeometry) -> Option<(f32, f32)> { pub fn pointer_to_grid(pointer_pos: egui::Pos2, geometry: &GridGeometry) -> Option<(f32, f32)> {
if !geometry.rect.contains(pointer_pos) { if !geometry.rect.contains(pointer_pos) {
return None; return None;
@@ -455,6 +467,7 @@ pub fn pointer_to_grid(pointer_pos: egui::Pos2, geometry: &GridGeometry) -> Opti
Some((x, y)) Some((x, y))
} }
// Reroutes path through cell.
pub fn reroute_path_through_cell( pub fn reroute_path_through_cell(
original_path: &[(usize, usize)], original_path: &[(usize, usize)],
original_cell: (usize, usize), original_cell: (usize, usize),
@@ -512,6 +525,7 @@ pub fn reroute_path_through_cell(
Some(full_path) Some(full_path)
} }
// Simplifies path loops.
pub fn simplify_path_loops(path: &mut Vec<(usize, usize)>) { pub fn simplify_path_loops(path: &mut Vec<(usize, usize)>) {
let mut out = Vec::new(); let mut out = Vec::new();
for &cell in path.iter() { for &cell in path.iter() {
@@ -524,6 +538,7 @@ pub fn simplify_path_loops(path: &mut Vec<(usize, usize)>) {
*path = out; *path = out;
} }
// Resizes hit cells.
pub fn resize_hit_cells( pub fn resize_hit_cells(
room: &layout::Room, room: &layout::Room,
room_idx: usize, room_idx: usize,
@@ -587,6 +602,7 @@ pub fn resize_hit_cells(
cells cells
} }
// Computes edges for.
pub fn door_edges_for( pub fn door_edges_for(
door: &layout::Door, door: &layout::Door,
cols: usize, cols: usize,
@@ -628,6 +644,7 @@ pub fn door_edges_for(
edges edges
} }
// Computes render width.
pub fn door_render_width(door: &layout::Door) -> usize { pub fn door_render_width(door: &layout::Door) -> usize {
if door.span_width { if door.span_width {
door.width.max(1) door.width.max(1)
@@ -636,6 +653,7 @@ pub fn door_render_width(door: &layout::Door) -> usize {
} }
} }
// Computes render width.
pub fn window_render_width(window: &layout::Window) -> usize { pub fn window_render_width(window: &layout::Window) -> usize {
if window.span_width { if window.span_width {
window.width.max(1) window.width.max(1)
@@ -644,10 +662,12 @@ pub fn window_render_width(window: &layout::Window) -> usize {
} }
} }
// Normalizes a cell edge so either endpoint order compares equal.
pub fn normalized_edge(a: (usize, usize), b: (usize, usize)) -> ((usize, usize), (usize, usize)) { pub fn normalized_edge(a: (usize, usize), b: (usize, usize)) -> ((usize, usize), (usize, usize)) {
if a <= b { (a, b) } else { (b, a) } if a <= b { (a, b) } else { (b, a) }
} }
// Returns the screen center point for a grid cell.
pub fn cell_center(geometry: &GridGeometry, col: usize, row: usize) -> egui::Pos2 { pub fn cell_center(geometry: &GridGeometry, col: usize, row: usize) -> egui::Pos2 {
egui::pos2( egui::pos2(
geometry.rect.left() + (col as f32 + 0.5) * geometry.cell_size, geometry.rect.left() + (col as f32 + 0.5) * geometry.cell_size,
@@ -655,6 +675,7 @@ pub fn cell_center(geometry: &GridGeometry, col: usize, row: usize) -> egui::Pos
) )
} }
// Returns the screen rectangle for a grid cell.
pub fn cell_rect(geometry: &GridGeometry, col: usize, row: usize) -> egui::Rect { pub fn cell_rect(geometry: &GridGeometry, col: usize, row: usize) -> egui::Rect {
let left = geometry.rect.left() + col as f32 * geometry.cell_size; let left = geometry.rect.left() + col as f32 * geometry.cell_size;
let top = geometry.rect.top() + row as f32 * geometry.cell_size; let top = geometry.rect.top() + row as f32 * geometry.cell_size;
@@ -664,6 +685,7 @@ pub fn cell_rect(geometry: &GridGeometry, col: usize, row: usize) -> egui::Rect
) )
} }
// Computes the screen rectangle occupied by an area marker.
pub fn marker_rect(marker: &layout::AreaMarker, geometry: &GridGeometry) -> egui::Rect { pub fn marker_rect(marker: &layout::AreaMarker, geometry: &GridGeometry) -> egui::Rect {
let left = geometry.rect.left() + marker.cell.0 as f32 * geometry.cell_size; let left = geometry.rect.left() + marker.cell.0 as f32 * geometry.cell_size;
let top = geometry.rect.top() + marker.cell.1 as f32 * geometry.cell_size; let top = geometry.rect.top() + marker.cell.1 as f32 * geometry.cell_size;
@@ -676,6 +698,7 @@ pub fn marker_rect(marker: &layout::AreaMarker, geometry: &GridGeometry) -> egui
) )
} }
// Computes contains cell.
pub fn marker_contains_cell(marker: Option<&layout::AreaMarker>, cell: (usize, usize)) -> bool { pub fn marker_contains_cell(marker: Option<&layout::AreaMarker>, cell: (usize, usize)) -> bool {
let Some(marker) = marker else { let Some(marker) = marker else {
return false; return false;
@@ -686,6 +709,7 @@ pub fn marker_contains_cell(marker: Option<&layout::AreaMarker>, cell: (usize, u
&& cell.1 < marker.cell.1 + marker.size && cell.1 < marker.cell.1 + marker.size
} }
// Computes marker origin.
pub fn centered_marker_origin( pub fn centered_marker_origin(
cell: (usize, usize), cell: (usize, usize),
size: usize, size: usize,
@@ -702,6 +726,7 @@ pub fn centered_marker_origin(
) )
} }
// Draws add overlay.
pub fn draw_add_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) { pub fn draw_add_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
if app.settings.add_tool != AddTool::Corridor { if app.settings.add_tool != AddTool::Corridor {
return; return;
@@ -717,6 +742,7 @@ pub fn draw_add_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &Gr
crate::rendering::draw_dashed_line(painter, start, end, stroke, 8.0, 6.0); crate::rendering::draw_dashed_line(painter, start, end, stroke, 8.0, 6.0);
} }
// Draws add tool ghost.
pub fn draw_add_tool_ghost(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) { pub fn draw_add_tool_ghost(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
let tool = app.settings.add_tool; let tool = app.settings.add_tool;
if tool == AddTool::None || tool == AddTool::Corridor { if tool == AddTool::None || tool == AddTool::Corridor {
@@ -915,6 +941,7 @@ pub fn draw_add_tool_ghost(app: &DungeonApp, painter: &egui::Painter, geometry:
} }
} }
// Draws resize overlay.
pub fn draw_resize_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) { pub fn draw_resize_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
return; return;
@@ -969,6 +996,7 @@ pub fn draw_resize_overlay(app: &DungeonApp, painter: &egui::Painter, geometry:
} }
} }
// Draws room resize visuals.
pub fn draw_room_resize_visuals( pub fn draw_room_resize_visuals(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
@@ -1013,6 +1041,7 @@ pub fn draw_room_resize_visuals(
} }
} }
// Draws staircase resize visuals.
pub fn draw_staircase_resize_visuals( pub fn draw_staircase_resize_visuals(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
@@ -1045,6 +1074,7 @@ pub fn draw_staircase_resize_visuals(
} }
} }
// Draws corridor hover overlay.
pub fn draw_corridor_hover_overlay( pub fn draw_corridor_hover_overlay(
app: &DungeonApp, app: &DungeonApp,
painter: &egui::Painter, painter: &egui::Painter,
@@ -1071,6 +1101,7 @@ pub fn draw_corridor_hover_overlay(
} }
} }
// Draws door hover overlay.
pub fn draw_door_hover_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) { pub fn draw_door_hover_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
let Some(idx) = app.hover_door_idx else { let Some(idx) = app.hover_door_idx else {
return; return;
@@ -1096,6 +1127,7 @@ pub fn draw_door_hover_overlay(app: &DungeonApp, painter: &egui::Painter, geomet
); );
} }
// Draws resize handle.
pub fn draw_resize_handle( pub fn draw_resize_handle(
painter: &egui::Painter, painter: &egui::Painter,
corner: egui::Pos2, corner: egui::Pos2,
+7
View File
@@ -13,6 +13,7 @@ use crate::layout::{rects_overlap, rooms_overlap};
use crate::ui::AddTool; use crate::ui::AddTool;
use eframe::egui; use eframe::egui;
// Handles secondary-button resize gestures for resizable layout elements.
pub fn handle_resize( pub fn handle_resize(
app: &mut DungeonApp, app: &mut DungeonApp,
ctx: &egui::Context, ctx: &egui::Context,
@@ -71,6 +72,7 @@ pub fn handle_resize(
false false
} }
// Starts text resize.
pub fn start_text_resize(app: &DungeonApp, text_idx: usize) -> Option<TextResizeState> { pub fn start_text_resize(app: &DungeonApp, text_idx: usize) -> Option<TextResizeState> {
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
return None; return None;
@@ -85,6 +87,7 @@ pub fn start_text_resize(app: &DungeonApp, text_idx: usize) -> Option<TextResize
}) })
} }
// Starts a room resize from a pointer position.
pub fn start_resize( pub fn start_resize(
app: &DungeonApp, app: &DungeonApp,
room_idx: usize, room_idx: usize,
@@ -117,6 +120,7 @@ pub fn start_resize(
}) })
} }
// Starts staircase resize.
pub fn start_staircase_resize( pub fn start_staircase_resize(
app: &DungeonApp, app: &DungeonApp,
stair_idx: usize, stair_idx: usize,
@@ -149,6 +153,7 @@ pub fn start_staircase_resize(
}) })
} }
// Resizes a room and reroutes affected corridors.
pub fn resize_room(app: &mut DungeonApp, state: &RoomResizeState, target_cell: (usize, usize)) { pub fn resize_room(app: &mut DungeonApp, state: &RoomResizeState, target_cell: (usize, usize)) {
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
return; return;
@@ -207,6 +212,7 @@ pub fn resize_room(app: &mut DungeonApp, state: &RoomResizeState, target_cell: (
} }
} }
// Resizes a text label based on the drag target cell.
pub fn resize_text(app: &mut DungeonApp, state: &TextResizeState, target_cell: (usize, usize)) { pub fn resize_text(app: &mut DungeonApp, state: &TextResizeState, target_cell: (usize, usize)) {
if app.settings.active_level_index >= app.levels.len() { if app.settings.active_level_index >= app.levels.len() {
return; return;
@@ -225,6 +231,7 @@ pub fn resize_text(app: &mut DungeonApp, state: &TextResizeState, target_cell: (
label.font_size = new_size as u16; label.font_size = new_size as u16;
} }
// Resizes a staircase while clamping it to the grid.
pub fn resize_staircase( pub fn resize_staircase(
app: &mut DungeonApp, app: &mut DungeonApp,
state: &StaircaseResizeState, state: &StaircaseResizeState,
+6
View File
@@ -14,6 +14,7 @@ pub type Cell = (usize, usize);
pub type CellEdge = (Cell, Cell); pub type CellEdge = (Cell, Cell);
pub type RoomBoundary = (usize, usize, Vec<CellEdge>); pub type RoomBoundary = (usize, usize, Vec<CellEdge>);
// Builds room connection edges.
pub fn build_room_connection_edges( pub fn build_room_connection_edges(
centers: &[(usize, usize)], centers: &[(usize, usize)],
randomness: f32, randomness: f32,
@@ -73,6 +74,7 @@ pub fn build_room_connection_edges(
edges edges
} }
// Orders core rooms.
pub fn ordered_core_rooms( pub fn ordered_core_rooms(
core_rooms: &[usize], core_rooms: &[usize],
centers: &[(usize, usize)], centers: &[(usize, usize)],
@@ -107,6 +109,7 @@ pub fn ordered_core_rooms(
ordered ordered
} }
// Pushes unique room edge.
pub fn push_unique_room_edge( pub fn push_unique_room_edge(
a: usize, a: usize,
b: usize, b: usize,
@@ -122,6 +125,7 @@ pub fn push_unique_room_edge(
} }
} }
// Computes exit edge.
pub fn room_exit_edge( pub fn room_exit_edge(
path: &[(usize, usize)], path: &[(usize, usize)],
room: &Room, room: &Room,
@@ -151,6 +155,7 @@ pub fn room_exit_edge(
None None
} }
// Computes collision edges.
pub fn room_collision_edges( pub fn room_collision_edges(
path: &[(usize, usize)], path: &[(usize, usize)],
room: &Room, room: &Room,
@@ -180,6 +185,7 @@ pub fn room_collision_edges(
edges edges
} }
// Finds room boundaries.
pub fn shared_room_boundaries(rooms: &[Room]) -> Vec<RoomBoundary> { pub fn shared_room_boundaries(rooms: &[Room]) -> Vec<RoomBoundary> {
let mut boundaries = Vec::new(); let mut boundaries = Vec::new();
for a_idx in 0..rooms.len() { for a_idx in 0..rooms.len() {
+24
View File
@@ -10,6 +10,7 @@ use crate::seed;
use crate::ui::UiSettings; use crate::ui::UiSettings;
use std::collections::HashSet; use std::collections::HashSet;
// Populates random markers.
pub fn populate_random_markers(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout { pub fn populate_random_markers(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout {
layout.start_markers.clear(); layout.start_markers.clear();
layout.end_markers.clear(); layout.end_markers.clear();
@@ -86,6 +87,7 @@ pub fn populate_random_markers(mut layout: DungeonLayout, settings: &UiSettings)
layout layout
} }
// Generates range inclusive.
pub fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize { pub fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize {
let min = min.max(1); let min = min.max(1);
let max = max.max(min); let max = max.max(min);
@@ -93,6 +95,7 @@ pub fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize
min + (seed_value as usize % span) min + (seed_value as usize % span)
} }
// Computes in room.
pub fn marker_in_room( pub fn marker_in_room(
room: &Room, room: &Room,
min_size: usize, min_size: usize,
@@ -120,6 +123,7 @@ pub fn marker_in_room(
AreaMarker { cell: (x, y), size } AreaMarker { cell: (x, y), size }
} }
// Handles assign extra markers.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn assign_extra_markers( pub fn assign_extra_markers(
markers: &mut Vec<AreaMarker>, markers: &mut Vec<AreaMarker>,
@@ -149,6 +153,7 @@ pub fn assign_extra_markers(
} }
} }
// Finds room pair.
pub fn farthest_room_pair( pub fn farthest_room_pair(
rooms: &[Room], rooms: &[Room],
available_start_rooms: &[usize], available_start_rooms: &[usize],
@@ -182,6 +187,7 @@ pub fn farthest_room_pair(
best best
} }
// Computes distance sq.
pub fn room_distance_sq(a: &Room, b: &Room) -> usize { pub fn room_distance_sq(a: &Room, b: &Room) -> usize {
let ac = a.center_cell(); let ac = a.center_cell();
let bc = b.center_cell(); let bc = b.center_cell();
@@ -190,12 +196,14 @@ pub fn room_distance_sq(a: &Room, b: &Room) -> usize {
dx * dx + dy * dy dx * dx + dy * dy
} }
// Removes a room id from the available marker placement list.
pub fn consume_room(available_rooms: &mut Vec<usize>, room_idx: usize) { pub fn consume_room(available_rooms: &mut Vec<usize>, room_idx: usize) {
if let Some(pos) = available_rooms.iter().position(|&idx| idx == room_idx) { if let Some(pos) = available_rooms.iter().position(|&idx| idx == room_idx) {
available_rooms.remove(pos); available_rooms.remove(pos);
} }
} }
// Picks room index.
pub fn pick_room_index(available_rooms: &[usize], room_count: usize, seed_value: u64) -> usize { pub fn pick_room_index(available_rooms: &[usize], room_count: usize, seed_value: u64) -> usize {
if !available_rooms.is_empty() { if !available_rooms.is_empty() {
available_rooms[seed_value as usize % available_rooms.len()] available_rooms[seed_value as usize % available_rooms.len()]
@@ -204,6 +212,7 @@ pub fn pick_room_index(available_rooms: &[usize], room_count: usize, seed_value:
} }
} }
// Populates random traps.
pub fn populate_random_traps(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout { pub fn populate_random_traps(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout {
layout.trap_markers = populate_random_area_markers( layout.trap_markers = populate_random_area_markers(
&layout, &layout,
@@ -217,6 +226,7 @@ pub fn populate_random_traps(mut layout: DungeonLayout, settings: &UiSettings) -
layout layout
} }
// Populates random monsters.
pub fn populate_random_monsters(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout { pub fn populate_random_monsters(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout {
layout.monster_markers = populate_random_area_markers( layout.monster_markers = populate_random_area_markers(
&layout, &layout,
@@ -230,6 +240,7 @@ pub fn populate_random_monsters(mut layout: DungeonLayout, settings: &UiSettings
layout layout
} }
// Populates random area markers.
pub fn populate_random_area_markers( pub fn populate_random_area_markers(
layout: &DungeonLayout, layout: &DungeonLayout,
master_seed: u64, master_seed: u64,
@@ -272,6 +283,7 @@ pub fn populate_random_area_markers(
markers markers
} }
// Appends room markers.
pub fn append_room_markers( pub fn append_room_markers(
markers: &mut Vec<AreaMarker>, markers: &mut Vec<AreaMarker>,
room: &Room, room: &Room,
@@ -295,6 +307,7 @@ pub fn append_room_markers(
} }
} }
// Appends corridor markers.
pub fn append_corridor_markers( pub fn append_corridor_markers(
markers: &mut Vec<AreaMarker>, markers: &mut Vec<AreaMarker>,
corridor: &Corridor, corridor: &Corridor,
@@ -317,10 +330,12 @@ pub fn append_corridor_markers(
} }
} }
// Checks whether passes frequency roll.
pub fn passes_frequency_roll(area_seed: u64, frequency_percent: usize) -> bool { pub fn passes_frequency_roll(area_seed: u64, frequency_percent: usize) -> bool {
(area_seed % 100) < frequency_percent as u64 (area_seed % 100) < frequency_percent as u64
} }
// Picks random corridor cell.
pub fn pick_random_corridor_cell(corridor: &Corridor, seed_value: u64) -> Option<(usize, usize)> { pub fn pick_random_corridor_cell(corridor: &Corridor, seed_value: u64) -> Option<(usize, usize)> {
if corridor.path.is_empty() { if corridor.path.is_empty() {
return None; return None;
@@ -329,6 +344,7 @@ pub fn pick_random_corridor_cell(corridor: &Corridor, seed_value: u64) -> Option
Some(corridor.path[idx]) Some(corridor.path[idx])
} }
// Populates staircase transitions between generated levels.
pub fn populate_stairs( pub fn populate_stairs(
mut layouts: Vec<DungeonLayout>, mut layouts: Vec<DungeonLayout>,
settings: &UiSettings, settings: &UiSettings,
@@ -396,6 +412,7 @@ pub fn populate_stairs(
result result
} }
// Gets stair count.
pub fn get_stair_count(settings: &UiSettings, gap_idx: usize) -> usize { pub fn get_stair_count(settings: &UiSettings, gap_idx: usize) -> usize {
if settings.min_stairs_per_level == settings.max_stairs_per_level { if settings.min_stairs_per_level == settings.max_stairs_per_level {
settings.min_stairs_per_level settings.min_stairs_per_level
@@ -406,6 +423,7 @@ pub fn get_stair_count(settings: &UiSettings, gap_idx: usize) -> usize {
} }
} }
// Ensures stair in room.
pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &Staircase) -> bool { pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &Staircase) -> bool {
let stair_x = stair.cell.0; let stair_x = stair.cell.0;
let stair_y = stair.cell.1; let stair_y = stair.cell.1;
@@ -458,6 +476,7 @@ pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &Staircase) -> bo
} }
} }
// Computes to stair min dist.
pub fn room_to_stair_min_dist(room: &Room, stair: &Staircase) -> usize { pub fn room_to_stair_min_dist(room: &Room, stair: &Staircase) -> usize {
let dx = if stair.cell.0 + stair.width <= room.x { let dx = if stair.cell.0 + stair.width <= room.x {
room.x - (stair.cell.0 + stair.width) room.x - (stair.cell.0 + stair.width)
@@ -474,6 +493,7 @@ pub fn room_to_stair_min_dist(room: &Room, stair: &Staircase) -> usize {
dx + dy dx + dy
} }
// Picks stair positions.
pub fn pick_stair_positions( pub fn pick_stair_positions(
layout: &DungeonLayout, layout: &DungeonLayout,
settings: &UiSettings, settings: &UiSettings,
@@ -548,6 +568,7 @@ pub fn pick_stair_positions(
.collect() .collect()
} }
// Checks whether has start or end on bottom row.
pub fn has_start_or_end_on_bottom_row( pub fn has_start_or_end_on_bottom_row(
room: &Room, room: &Room,
start_markers: &[AreaMarker], start_markers: &[AreaMarker],
@@ -563,6 +584,7 @@ pub fn has_start_or_end_on_bottom_row(
false false
} }
// Generates stair size.
pub fn random_stair_size(settings: &UiSettings, seed_value: u64) -> (usize, usize) { pub fn random_stair_size(settings: &UiSettings, seed_value: u64) -> (usize, usize) {
let w_span = settings let w_span = settings
.max_stair_width .max_stair_width
@@ -590,10 +612,12 @@ pub struct StairShuffleRng {
} }
impl StairShuffleRng { impl StairShuffleRng {
// Creates a new instance with the given inputs.
pub fn new(seed: u64) -> Self { pub fn new(seed: u64) -> Self {
Self { state: seed } Self { state: seed }
} }
// Generates the next pseudo-random 32-bit value.
pub fn next_u32(&mut self) -> u32 { pub fn next_u32(&mut self) -> u32 {
self.state = self self.state = self
.state .state
+1
View File
@@ -18,6 +18,7 @@ pub use markers::*;
pub use passages::*; pub use passages::*;
pub use room_placement::*; pub use room_placement::*;
// Generates a complete dungeon layout from generation settings.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn generate_layout( pub fn generate_layout(
cols: usize, cols: usize,
+15
View File
@@ -14,6 +14,7 @@ use super::connections::{room_collision_edges, room_exit_edge, shared_room_bound
use crate::seed; use crate::seed;
use std::collections::HashSet; use std::collections::HashSet;
// Applies generated doors and archways to room-corridor transitions.
pub fn apply_doors( pub fn apply_doors(
layout: &mut DungeonLayout, layout: &mut DungeonLayout,
seed: u64, seed: u64,
@@ -139,6 +140,7 @@ pub fn apply_doors(
apply_secret_doors(layout, seed, settings); apply_secret_doors(layout, seed, settings);
} }
// Applies packed room doors.
pub fn apply_packed_room_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorSettings) { pub fn apply_packed_room_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorSettings) {
let mut rng = SimpleRng::new(seed::derive_seed(seed, 0xD005_5EED_u64)); let mut rng = SimpleRng::new(seed::derive_seed(seed, 0xD005_5EED_u64));
let door_chance = ((settings.frequency_percent.min(100) as f32) / 100.0) let door_chance = ((settings.frequency_percent.min(100) as f32) / 100.0)
@@ -175,6 +177,7 @@ pub fn apply_packed_room_doors(layout: &mut DungeonLayout, seed: u64, settings:
apply_secret_doors(layout, seed, settings); apply_secret_doors(layout, seed, settings);
} }
// Applies secret doors.
pub fn apply_secret_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorSettings) { pub fn apply_secret_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorSettings) {
let secret_percent = settings.secret_percent.min(100); let secret_percent = settings.secret_percent.min(100);
let chance = (secret_percent as f32) / 100.0; let chance = (secret_percent as f32) / 100.0;
@@ -206,6 +209,7 @@ pub fn apply_secret_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorS
} }
} }
// Computes is secret eligible for rooms.
pub fn door_is_secret_eligible_for_rooms( pub fn door_is_secret_eligible_for_rooms(
layout: &DungeonLayout, layout: &DungeonLayout,
room_door_map: &[Vec<usize>], room_door_map: &[Vec<usize>],
@@ -230,6 +234,7 @@ pub fn door_is_secret_eligible_for_rooms(
true true
} }
// Computes to door indices.
pub fn room_to_door_indices(layout: &DungeonLayout) -> Vec<Vec<usize>> { pub fn room_to_door_indices(layout: &DungeonLayout) -> Vec<Vec<usize>> {
let mut room_door_map = vec![Vec::new(); layout.rooms.len()]; let mut room_door_map = vec![Vec::new(); layout.rooms.len()];
for (door_idx, door) in layout.doors.iter().enumerate() { for (door_idx, door) in layout.doors.iter().enumerate() {
@@ -242,6 +247,7 @@ pub fn room_to_door_indices(layout: &DungeonLayout) -> Vec<Vec<usize>> {
room_door_map room_door_map
} }
// Handles secret room ids.
pub fn secret_room_ids(layout: &DungeonLayout) -> HashSet<usize> { pub fn secret_room_ids(layout: &DungeonLayout) -> HashSet<usize> {
let mut secret_rooms = HashSet::new(); let mut secret_rooms = HashSet::new();
for door in layout.doors.iter().filter(|door| door.secret) { for door in layout.doors.iter().filter(|door| door.secret) {
@@ -254,10 +260,12 @@ pub fn secret_room_ids(layout: &DungeonLayout) -> HashSet<usize> {
secret_rooms secret_rooms
} }
// Computes touches room.
pub fn door_touches_room(door: &Door, room: &Room) -> bool { pub fn door_touches_room(door: &Door, room: &Room) -> bool {
cell_in_room(door.from, room) || cell_in_room(door.to, room) cell_in_room(door.from, room) || cell_in_room(door.to, room)
} }
// Computes directly connects rooms.
pub fn door_directly_connects_rooms(layout: &DungeonLayout, door_idx: usize) -> bool { pub fn door_directly_connects_rooms(layout: &DungeonLayout, door_idx: usize) -> bool {
let Some(door) = layout.doors.get(door_idx) else { let Some(door) = layout.doors.get(door_idx) else {
return false; return false;
@@ -273,6 +281,7 @@ pub fn door_directly_connects_rooms(layout: &DungeonLayout, door_idx: usize) ->
touching_rooms.len() >= 2 touching_rooms.len() >= 2
} }
// Builds in room.
pub fn cell_in_room(cell: (usize, usize), room: &Room) -> bool { pub fn cell_in_room(cell: (usize, usize), room: &Room) -> bool {
cell.0 >= room.x cell.0 >= room.x
&& cell.0 < room.x + room.width && cell.0 < room.x + room.width
@@ -280,6 +289,7 @@ pub fn cell_in_room(cell: (usize, usize), room: &Room) -> bool {
&& cell.1 < room.y + room.height && cell.1 < room.y + room.height
} }
// Applies generated windows to eligible room boundaries.
pub fn apply_windows( pub fn apply_windows(
layout: &mut DungeonLayout, layout: &mut DungeonLayout,
seed: u64, seed: u64,
@@ -341,6 +351,7 @@ pub enum WindowTarget {
Room(usize), Room(usize),
} }
// Collects window segments.
pub fn collect_window_segments( pub fn collect_window_segments(
layout: &DungeonLayout, layout: &DungeonLayout,
cols: usize, cols: usize,
@@ -404,6 +415,7 @@ pub fn collect_window_segments(
segments segments
} }
// Collects room side segments.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn collect_room_side_segments( pub fn collect_room_side_segments(
room_idx: usize, room_idx: usize,
@@ -467,6 +479,7 @@ pub fn collect_room_side_segments(
push_window_segment(current_target, side, &mut current_cells, segments); push_window_segment(current_target, side, &mut current_cells, segments);
} }
// Pushes window segment.
pub fn push_window_segment( pub fn push_window_segment(
target: Option<WindowTarget>, target: Option<WindowTarget>,
side: WindowSide, side: WindowSide,
@@ -488,6 +501,7 @@ pub fn push_window_segment(
}); });
} }
// Finds the grid cell outside a room side.
pub fn outward_neighbor( pub fn outward_neighbor(
cell: (usize, usize), cell: (usize, usize),
side: WindowSide, side: WindowSide,
@@ -502,6 +516,7 @@ pub fn outward_neighbor(
} }
} }
// Selects segment cell.
pub fn select_segment_cell( pub fn select_segment_cell(
cells: &[(usize, usize)], cells: &[(usize, usize)],
width: usize, width: usize,
+7
View File
@@ -8,6 +8,7 @@ use super::super::types::Room;
use super::super::utils::{SimpleRng, rooms_overlap, rooms_touch, shuffle_indices}; use super::super::utils::{SimpleRng, rooms_overlap, rooms_touch, shuffle_indices};
use std::collections::VecDeque; use std::collections::VecDeque;
// Generates room sizes.
pub fn generate_room_sizes( pub fn generate_room_sizes(
target_room_count: usize, target_room_count: usize,
cols: usize, cols: usize,
@@ -48,6 +49,7 @@ pub fn generate_room_sizes(
sizes sizes
} }
// Generates random room center points inside the grid.
pub fn random_centers( pub fn random_centers(
count: usize, count: usize,
cols: usize, cols: usize,
@@ -64,6 +66,7 @@ pub fn random_centers(
centers centers
} }
// Places packed rooms.
pub fn place_packed_rooms( pub fn place_packed_rooms(
room_sizes: &[(usize, usize)], room_sizes: &[(usize, usize)],
room_edges: &[(usize, usize)], room_edges: &[(usize, usize)],
@@ -116,6 +119,7 @@ pub fn place_packed_rooms(
placed.into_iter().flatten().collect() placed.into_iter().flatten().collect()
} }
// Computes the order used to place packed rooms.
pub fn placement_order(room_count: usize, room_edges: &[(usize, usize)]) -> Vec<usize> { pub fn placement_order(room_count: usize, room_edges: &[(usize, usize)]) -> Vec<usize> {
if room_count == 0 { if room_count == 0 {
return Vec::new(); return Vec::new();
@@ -152,6 +156,7 @@ pub fn placement_order(room_count: usize, room_edges: &[(usize, usize)]) -> Vec<
order order
} }
// Tries to place packed room.
pub fn try_place_packed_room( pub fn try_place_packed_room(
room_idx: usize, room_idx: usize,
room_sizes: &[(usize, usize)], room_sizes: &[(usize, usize)],
@@ -214,6 +219,7 @@ pub fn try_place_packed_room(
None None
} }
// Builds room candidates.
pub fn packed_room_candidates( pub fn packed_room_candidates(
anchor: &Room, anchor: &Room,
width: usize, width: usize,
@@ -261,6 +267,7 @@ pub fn packed_room_candidates(
candidates candidates
} }
// Shuffles rooms with the deterministic layout RNG.
pub fn shuffle_rooms(rooms: &mut [Room], rng: &mut SimpleRng) { pub fn shuffle_rooms(rooms: &mut [Room], rng: &mut SimpleRng) {
if rooms.len() <= 1 { if rooms.len() <= 1 {
return; return;
+6
View File
@@ -181,6 +181,7 @@ pub fn corridor_cells(layout: &DungeonLayout, cols: usize, rows: usize) -> HashS
cells cells
} }
// Computes index at cell.
pub fn room_index_at_cell(rooms: &[Room], cell: (usize, usize)) -> Option<usize> { pub fn room_index_at_cell(rooms: &[Room], cell: (usize, usize)) -> Option<usize> {
rooms.iter().position(|room| { rooms.iter().position(|room| {
cell.0 >= room.x cell.0 >= room.x
@@ -218,6 +219,7 @@ pub fn overlaps_with_padding(a: &Room, b: &Room, padding: usize) -> bool {
a_left < b_right && a_right > b_left && a_top < b_bottom && a_bottom > b_top a_left < b_right && a_right > b_left && a_top < b_bottom && a_bottom > b_top
} }
// Checks whether rects overlap.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn rects_overlap( pub fn rects_overlap(
ax: usize, ax: usize,
@@ -237,14 +239,17 @@ pub fn rects_overlap(
ax < b_right && a_right > bx && ay < b_bottom && a_bottom > by ax < b_right && a_right > bx && ay < b_bottom && a_bottom > by
} }
// Checks whether rooms overlap.
pub fn rooms_overlap(a: &Room, b: &Room) -> bool { pub fn rooms_overlap(a: &Room, b: &Room) -> bool {
rects_overlap(a.x, a.y, a.width, a.height, b.x, b.y, b.width, b.height) rects_overlap(a.x, a.y, a.width, a.height, b.x, b.y, b.width, b.height)
} }
// Checks whether rooms touch.
pub fn rooms_touch(a: &Room, b: &Room) -> bool { pub fn rooms_touch(a: &Room, b: &Room) -> bool {
!shared_boundary_edges(a, b).is_empty() !shared_boundary_edges(a, b).is_empty()
} }
// Finds boundary edges.
pub fn shared_boundary_edges(a: &Room, b: &Room) -> Vec<((usize, usize), (usize, usize))> { pub fn shared_boundary_edges(a: &Room, b: &Room) -> Vec<((usize, usize), (usize, usize))> {
let mut edges = Vec::new(); let mut edges = Vec::new();
@@ -277,6 +282,7 @@ pub fn shared_boundary_edges(a: &Room, b: &Room) -> Vec<((usize, usize), (usize,
edges edges
} }
// Finds opening width.
pub fn shared_opening_width(a: &Room, b: &Room, span: usize, default_width: usize) -> usize { pub fn shared_opening_width(a: &Room, b: &Room, span: usize, default_width: usize) -> usize {
let max_width = if a.x + a.width == b.x || b.x + b.width == a.x { let max_width = if a.x + a.width == b.x || b.x + b.width == a.x {
a.height.min(b.height) a.height.min(b.height)
+16
View File
@@ -13,6 +13,7 @@ use eframe::egui;
use egui::{Color32, Stroke}; use egui::{Color32, Stroke};
use std::collections::HashSet; use std::collections::HashSet;
// Draws the full dungeon layout into the interactive canvas.
pub fn draw_layout( pub fn draw_layout(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
@@ -325,6 +326,7 @@ pub fn draw_layout(
} }
} }
// Draws a staircase marker into the interactive canvas.
pub fn draw_staircase( pub fn draw_staircase(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
@@ -380,6 +382,7 @@ pub fn draw_staircase(
} }
} }
// Computes label rect.
pub fn text_label_rect(geometry: &GridGeometry, label: &crate::layout::TextLabel) -> egui::Rect { pub fn text_label_rect(geometry: &GridGeometry, label: &crate::layout::TextLabel) -> egui::Rect {
let center = cell_center(geometry, label.cell.0, label.cell.1); let center = cell_center(geometry, label.cell.0, label.cell.1);
let font_size = label.font_size as f32; let font_size = label.font_size as f32;
@@ -389,6 +392,7 @@ pub fn text_label_rect(geometry: &GridGeometry, label: &crate::layout::TextLabel
egui::Rect::from_center_size(center, egui::vec2(width, height)) egui::Rect::from_center_size(center, egui::vec2(width, height))
} }
// Draws area marker.
pub fn draw_area_marker( pub fn draw_area_marker(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
@@ -430,6 +434,7 @@ pub fn draw_area_marker(
); );
} }
// Draws area marker group.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn draw_area_marker_group( pub fn draw_area_marker_group(
painter: &egui::Painter, painter: &egui::Painter,
@@ -461,6 +466,7 @@ pub fn draw_area_marker_group(
} }
} }
// Draws wall segment.
pub fn draw_wall_segment( pub fn draw_wall_segment(
painter: &egui::Painter, painter: &egui::Painter,
from: egui::Pos2, from: egui::Pos2,
@@ -490,6 +496,7 @@ pub fn draw_wall_segment(
} }
} }
// Computes edges from cells.
pub fn corridor_edges_from_cells( pub fn corridor_edges_from_cells(
corridor_cells: &HashSet<(usize, usize)>, corridor_cells: &HashSet<(usize, usize)>,
) -> HashSet<((usize, usize), (usize, usize))> { ) -> HashSet<((usize, usize), (usize, usize))> {
@@ -507,6 +514,7 @@ pub fn corridor_edges_from_cells(
edges edges
} }
// Computes edges from rooms.
pub fn room_edges_from_rooms( pub fn room_edges_from_rooms(
rooms: &[crate::layout::Room], rooms: &[crate::layout::Room],
) -> HashSet<((usize, usize), (usize, usize))> { ) -> HashSet<((usize, usize), (usize, usize))> {
@@ -538,6 +546,7 @@ pub enum DoorLineStyle {
DashDotDot, DashDotDot,
} }
// Draws door line.
pub fn draw_door_line( pub fn draw_door_line(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
@@ -574,6 +583,7 @@ pub fn draw_door_line(
} }
} }
// Draws window line.
pub fn draw_window_line( pub fn draw_window_line(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
@@ -625,6 +635,7 @@ pub fn draw_window_line(
} }
} }
// Draws styled line.
pub fn draw_styled_line( pub fn draw_styled_line(
painter: &egui::Painter, painter: &egui::Painter,
from: egui::Pos2, from: egui::Pos2,
@@ -644,6 +655,7 @@ pub fn draw_styled_line(
} }
} }
// Draws dashed line.
pub fn draw_dashed_line( pub fn draw_dashed_line(
painter: &egui::Painter, painter: &egui::Painter,
from: egui::Pos2, from: egui::Pos2,
@@ -672,6 +684,7 @@ pub fn draw_dashed_line(
} }
} }
// Draws dotted line.
pub fn draw_dotted_line(painter: &egui::Painter, from: egui::Pos2, to: egui::Pos2, stroke: Stroke) { pub fn draw_dotted_line(painter: &egui::Painter, from: egui::Pos2, to: egui::Pos2, stroke: Stroke) {
let dx = to.x - from.x; let dx = to.x - from.x;
let dy = to.y - from.y; let dy = to.y - from.y;
@@ -692,6 +705,7 @@ pub fn draw_dotted_line(painter: &egui::Painter, from: egui::Pos2, to: egui::Pos
} }
} }
// Draws dash dot line.
pub fn draw_dash_dot_line( pub fn draw_dash_dot_line(
painter: &egui::Painter, painter: &egui::Painter,
from: egui::Pos2, from: egui::Pos2,
@@ -723,6 +737,7 @@ pub fn draw_dash_dot_line(
} }
} }
// Draws dash dot dot line.
pub fn draw_dash_dot_dot_line( pub fn draw_dash_dot_dot_line(
painter: &egui::Painter, painter: &egui::Painter,
from: egui::Pos2, from: egui::Pos2,
@@ -761,6 +776,7 @@ pub fn draw_dash_dot_dot_line(
} }
} }
// Draws room crosshatch.
pub fn draw_room_crosshatch( pub fn draw_room_crosshatch(
painter: &egui::Painter, painter: &egui::Painter,
geometry: &GridGeometry, geometry: &GridGeometry,
+2
View File
@@ -19,6 +19,7 @@ pub struct DungeonSave {
pub svgs: Vec<String>, pub svgs: Vec<String>,
} }
// Saves dungeon state to a user-selected JSON file.
pub fn save_dungeon( pub fn save_dungeon(
settings: &UiSettings, settings: &UiSettings,
layouts: Vec<DungeonLayout>, layouts: Vec<DungeonLayout>,
@@ -45,6 +46,7 @@ pub fn save_dungeon(
Ok(path) Ok(path)
} }
// Loads dungeon state from a user-selected JSON file.
pub fn load_dungeon() -> Result<DungeonSave, String> { pub fn load_dungeon() -> Result<DungeonSave, String> {
let path = FileDialog::new() let path = FileDialog::new()
.set_title("Load Dungeon State") .set_title("Load Dungeon State")
+12
View File
@@ -19,6 +19,7 @@ pub enum Tab {
} }
impl Default for Tab { impl Default for Tab {
// Provides default settings for this type.
fn default() -> Self { fn default() -> Self {
Self::Generate Self::Generate
} }
@@ -34,12 +35,14 @@ pub enum ExportFormat {
} }
impl Default for ExportFormat { impl Default for ExportFormat {
// Provides default settings for this type.
fn default() -> Self { fn default() -> Self {
Self::Png Self::Png
} }
} }
impl ExportFormat { impl ExportFormat {
// Returns the file extension for this format.
pub fn extension(self) -> &'static str { pub fn extension(self) -> &'static str {
match self { match self {
ExportFormat::Png => "png", ExportFormat::Png => "png",
@@ -50,6 +53,7 @@ impl ExportFormat {
} }
} }
// Returns the user-facing label for this option.
pub fn label(self) -> &'static str { pub fn label(self) -> &'static str {
match self { match self {
ExportFormat::Png => ".png", ExportFormat::Png => ".png",
@@ -60,6 +64,7 @@ impl ExportFormat {
} }
} }
// Reports whether this format supports resolution scaling.
pub fn supports_resolution(self) -> bool { pub fn supports_resolution(self) -> bool {
!matches!(self, ExportFormat::Svg) !matches!(self, ExportFormat::Svg)
} }
@@ -73,12 +78,14 @@ pub enum MaskFormat {
} }
impl Default for MaskFormat { impl Default for MaskFormat {
// Provides default settings for this type.
fn default() -> Self { fn default() -> Self {
Self::Png Self::Png
} }
} }
impl MaskFormat { impl MaskFormat {
// Returns the file extension for this format.
pub fn extension(self) -> &'static str { pub fn extension(self) -> &'static str {
match self { match self {
MaskFormat::Png => "png", MaskFormat::Png => "png",
@@ -87,6 +94,7 @@ impl MaskFormat {
} }
} }
// Returns the user-facing label for this option.
pub fn label(self) -> &'static str { pub fn label(self) -> &'static str {
match self { match self {
MaskFormat::Png => ".png", MaskFormat::Png => ".png",
@@ -169,6 +177,7 @@ pub struct UiSettings {
} }
impl Default for UiSettings { impl Default for UiSettings {
// Provides default settings for this type.
fn default() -> Self { fn default() -> Self {
UiSettings { UiSettings {
seed: 0, seed: 0,
@@ -270,6 +279,7 @@ pub struct SidePanelResult {
pub new_level_clicked: bool, pub new_level_clicked: bool,
} }
// Draws side panel.
pub fn draw_side_panel( pub fn draw_side_panel(
ctx: &egui::Context, ctx: &egui::Context,
settings: &mut UiSettings, settings: &mut UiSettings,
@@ -343,6 +353,7 @@ pub fn draw_side_panel(
result result
} }
// Draws legend panel.
pub fn draw_legend_panel(ctx: &egui::Context, settings: &mut UiSettings) -> bool { pub fn draw_legend_panel(ctx: &egui::Context, settings: &mut UiSettings) -> bool {
let mut composition_mode_changed = false; let mut composition_mode_changed = false;
use egui::Color32; use egui::Color32;
@@ -418,6 +429,7 @@ pub fn draw_legend_panel(ctx: &egui::Context, settings: &mut UiSettings) -> bool
composition_mode_changed composition_mode_changed
} }
// Draws level tabs.
pub fn draw_level_tabs( pub fn draw_level_tabs(
ui: &mut egui::Ui, ui: &mut egui::Ui,
level_count: usize, level_count: usize,
+3
View File
@@ -787,6 +787,7 @@ pub fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut S
} }
} }
// Draws start and end tab.
pub fn draw_start_and_end_tab( pub fn draw_start_and_end_tab(
ui: &mut egui::Ui, ui: &mut egui::Ui,
settings: &mut UiSettings, settings: &mut UiSettings,
@@ -869,6 +870,7 @@ pub fn draw_start_and_end_tab(
ui.label("Markers are generated inside rooms. Matching start/end indices are paired as far apart as possible."); ui.label("Markers are generated inside rooms. Matching start/end indices are paired as far apart as possible.");
} }
// Draws monsters and traps tab.
pub fn draw_monsters_and_traps_tab( pub fn draw_monsters_and_traps_tab(
ui: &mut egui::Ui, ui: &mut egui::Ui,
settings: &mut UiSettings, settings: &mut UiSettings,
@@ -932,6 +934,7 @@ pub fn draw_monsters_and_traps_tab(
); );
} }
// Draws usize slider row.
pub fn draw_usize_slider_row( pub fn draw_usize_slider_row(
ui: &mut egui::Ui, ui: &mut egui::Ui,
result: &mut SidePanelResult, result: &mut SidePanelResult,
+2
View File
@@ -218,6 +218,7 @@ pub fn draw_dotted_line(painter: &egui::Painter, from: egui::Pos2, to: egui::Pos
} }
} }
// Draws dash dot line.
pub fn draw_dash_dot_line( pub fn draw_dash_dot_line(
painter: &egui::Painter, painter: &egui::Painter,
from: egui::Pos2, from: egui::Pos2,
@@ -249,6 +250,7 @@ pub fn draw_dash_dot_line(
} }
} }
// Draws dash dot dot line.
pub fn draw_dash_dot_dot_line( pub fn draw_dash_dot_dot_line(
painter: &egui::Painter, painter: &egui::Painter,
from: egui::Pos2, from: egui::Pos2,