/*
* SVG vector export logic.
* Handles building SVG representations of dungeon levels, including
* rooms, corridors, walls, doors, and markers.
*/
use super::types::{
BG, CORRIDOR, END_MARKER, ExportGeometry, GRID, MONSTER_MARKER, ROOM, START_MARKER,
TRAP_MARKER, WINDOW_COLOR, door_render_width, norm_edge,
};
use super::utils::{collect_scene_data, door_line_points, window_line_points};
use crate::layout::DungeonLayout;
use crate::ui::UiSettings;
use std::collections::HashSet;
use std::fmt::Write as _;
pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
let g = ExportGeometry::new(settings.cols, settings.rows);
let wall_w = (g.cell / 2.5).max(1.0);
let door_w = (g.cell / 5.0).max(1.0);
let mut s = String::new();
let _ = writeln!(
s,
"\n");
s
}
pub fn append_svg_walls(
out: &mut String,
g: &ExportGeometry,
cells: &HashSet<(usize, usize)>,
connected_edges: Option<&HashSet<((usize, usize), (usize, usize))>>,
door_edges: &HashSet<((usize, usize), (usize, usize))>,
width: f32,
) {
for &(col, row) in cells {
let left = g.left() + col as f32 * g.cell;
let top = g.top() + row as f32 * g.cell;
let right = left + g.cell;
let bottom = top + g.cell;
let right_cell = (col + 1, row);
let bottom_cell = (col, row + 1);
let left_edge = col == 0
|| (!cells.contains(&(col - 1, row))
&& !door_edges.contains(&norm_edge((col, row), (col - 1, row))));
let right_connected = connected_edges
.map(|set| set.contains(&norm_edge((col, row), right_cell)))
.unwrap_or(true);
let right_edge = (!cells.contains(&right_cell) || !right_connected)
&& !door_edges.contains(&norm_edge((col, row), right_cell));
let top_edge = row == 0
|| (!cells.contains(&(col, row - 1))
&& !door_edges.contains(&norm_edge((col, row), (col, row - 1))));
let bottom_connected = connected_edges
.map(|set| set.contains(&norm_edge((col, row), bottom_cell)))
.unwrap_or(true);
let bottom_edge = (!cells.contains(&bottom_cell) || !bottom_connected)
&& !door_edges.contains(&norm_edge((col, row), bottom_cell));
if left_edge {
let _ = writeln!(
out,
"",
left - (width * 0.5),
top - (width * 0.5),
(bottom - top) + width
);
}
if right_edge {
let _ = writeln!(
out,
"",
right - (width * 0.5),
top - (width * 0.5),
(bottom - top) + width
);
}
if top_edge {
let _ = writeln!(
out,
"",
left - (width * 0.5),
top - (width * 0.5),
(right - left) + width
);
}
if bottom_edge {
let _ = writeln!(
out,
"",
left - (width * 0.5),
bottom - (width * 0.5),
(right - left) + width
);
}
}
}
pub fn append_svg_area_marker(
out: &mut String,
g: &ExportGeometry,
marker: &crate::layout::AreaMarker,
label: &str,
color: (u8, u8, u8, u8),
colorblind_mode: bool,
) {
let x = g.left() + marker.cell.0 as f32 * g.cell;
let y = g.top() + marker.cell.1 as f32 * g.cell;
let w = marker.size as f32 * g.cell;
let h = marker.size as f32 * g.cell;
let alpha = if colorblind_mode { 0.22 } else { 0.35 };
let _ = writeln!(
out,
"",
color.0, color.1, color.2, color.0, color.1, color.2
);
let symbol = if colorblind_mode { "black" } else { "white" };
if label == "S" {
let _ = writeln!(
out,
"",
x + w * 0.25,
y + h * 0.75,
x + w * 0.5,
y + h * 0.25,
x + w * 0.75,
y + h * 0.75,
symbol
);
} else if label == "E" {
let _ = writeln!(
out,
"",
x + w * 0.25,
y + h * 0.25,
x + w * 0.75,
y + h * 0.75,
symbol
);
let _ = writeln!(
out,
"",
x + w * 0.75,
y + h * 0.25,
x + w * 0.25,
y + h * 0.75,
symbol
);
} else if label == "T" {
// Horizontal bar
let _ = writeln!(
out,
"",
x + w * 0.2,
y + h * 0.25,
x + w * 0.8,
y + h * 0.25,
symbol
);
// Vertical bar
let _ = writeln!(
out,
"",
x + w * 0.5,
y + h * 0.25,
x + w * 0.5,
y + h * 0.75,
symbol
);
} else if label == "M" {
let _ = writeln!(
out,
"",
x + w * 0.2,
y + h * 0.75,
x + w * 0.2,
y + h * 0.25,
x + w * 0.5,
y + h * 0.5,
x + w * 0.8,
y + h * 0.25,
x + w * 0.8,
y + h * 0.75,
symbol
);
}
}
pub fn append_svg_area_marker_group(
out: &mut String,
g: &ExportGeometry,
markers: &[crate::layout::AreaMarker],
label: &str,
color: (u8, u8, u8, u8),
colorblind_mode: bool,
) {
for marker in markers {
append_svg_area_marker(out, g, marker, label, color, colorblind_mode);
}
}