terrain_forge/lib.rs
1//! # TerrainForge
2//!
3//! A modular procedural generation engine for terrain, dungeons, and maps.
4//!
5//! ## Quick Start
6//!
7//! ```rust
8//! use terrain_forge::{Grid, Tile, algorithms};
9//!
10//! let mut grid = Grid::new(80, 60);
11//! let algo = algorithms::get("bsp").unwrap();
12//! algo.generate(&mut grid, 12345);
13//!
14//! println!("Generated {} floor tiles", grid.count(|t| t.is_floor()));
15//! ```
16//!
17//! ## Semantic Extraction
18//!
19//! Extract semantic information from any generated map:
20//!
21//! ```rust
22//! use terrain_forge::{algorithms, SemanticExtractor, Grid, Rng};
23//!
24//! // 1. Generate map using any method
25//! let mut grid = Grid::new(80, 60);
26//! algorithms::get("cellular").unwrap().generate(&mut grid, 12345);
27//!
28//! // 2. Extract semantic information
29//! let extractor = SemanticExtractor::for_caves();
30//! let mut rng = Rng::new(12345);
31//! let semantic = extractor.extract(&grid, &mut rng);
32//!
33//! // Works with any grid source - pipelines, external tools, etc.
34//! ```
35//!
36//! ## Algorithms
37//!
38//! 14 generation algorithms available via [`algorithms::get`]:
39//! - `bsp` - Binary Space Partitioning for structured rooms
40//! - `cellular` - Cellular automata for organic caves
41//! - `drunkard` - Random walk for winding corridors
42//! - `maze` - Perfect maze generation
43//! - `rooms` - Simple rectangular rooms
44//! - `voronoi` - Voronoi-based regions
45//! - `dla` - Diffusion-limited aggregation
46//! - `wfc` - Wave Function Collapse
47//! - `percolation` - Connected cluster generation
48//! - `diamond_square` - Heightmap terrain
49//! - `fractal` - Fractal noise terrain
50//! - `agent` - Multi-agent carving
51//! - `glass_seam` - Region connector
52//! - `room_accretion` - Brogue-style organic dungeons
53//!
54//! ## Composition
55//!
56//! Chain algorithms with [`compose::Pipeline`] or layer with [`compose::LayeredGenerator`]:
57//!
58//! ```rust
59//! use terrain_forge::{Grid, Tile, Algorithm, algorithms};
60//! use terrain_forge::compose::Pipeline;
61//!
62//! let mut grid = Grid::new(80, 60);
63//! let pipeline = Pipeline::new()
64//! .add(algorithms::get("rooms").unwrap())
65//! .add(algorithms::get("cellular").unwrap());
66//! pipeline.generate(&mut grid, 12345);
67//! ```
68//!
69//! ## Effects
70//!
71//! Post-process with [`effects`]: morphology, connectivity, filters, transforms.
72//!
73//! ## Noise
74//!
75//! [`noise`] module provides Perlin, Simplex, Value, Worley with FBM and modifiers.
76
77mod algorithm;
78mod grid;
79mod rng;
80mod semantic_extractor;
81mod semantic_visualization;
82
83#[cfg(test)]
84mod semantic_tests;
85
86pub mod algorithms;
87pub mod analysis;
88pub mod compose;
89pub mod constraints;
90pub mod effects;
91pub mod noise;
92pub mod pipeline;
93pub mod semantic;
94pub mod spatial;
95
96pub use algorithm::Algorithm;
97pub use grid::{Cell, Grid, Tile};
98pub use rng::Rng;
99pub use semantic::{ConnectivityGraph, Marker, Masks, Region, SemanticConfig, SemanticLayers};
100pub use semantic_extractor::{extract_semantics, extract_semantics_default, SemanticExtractor};
101pub use semantic_visualization::{
102 visualize_connectivity_graph, visualize_masks, visualize_region_ids, visualize_regions,
103 visualize_semantic_layers, VisualizationConfig,
104};
105
106/// Generate a map with semantic layers using the new extraction approach
107///
108/// **DEPRECATED**: This function is provided for backward compatibility.
109/// For new code, use the decoupled `SemanticExtractor` approach:
110///
111/// ```rust
112/// use terrain_forge::{algorithms, SemanticExtractor, Grid, Rng};
113///
114/// // Instead of this deprecated approach:
115/// // let (grid, semantic) = generate_with_semantic_tuple("cellular", 80, 60, 12345);
116///
117/// // Use this:
118/// let mut grid = Grid::new(80, 60);
119/// algorithms::get("cellular").unwrap().generate(&mut grid, 12345);
120/// let semantic = SemanticExtractor::for_caves().extract(&grid, &mut Rng::new(12345));
121/// ```
122#[deprecated(
123 since = "0.3.0",
124 note = "Use decoupled SemanticExtractor for better flexibility"
125)]
126pub fn generate_with_semantic(
127 algorithm_name: &str,
128 width: usize,
129 height: usize,
130 seed: u64,
131) -> (Grid<Tile>, Option<SemanticLayers>) {
132 let mut grid = Grid::new(width, height);
133 let mut rng = Rng::new(seed);
134
135 // Generate tiles using any algorithm
136 if let Some(algo) = algorithms::get(algorithm_name) {
137 algo.generate(&mut grid, seed);
138 }
139
140 // Extract semantic layers using the new standalone system
141 let extractor = match algorithm_name {
142 "cellular" => SemanticExtractor::for_caves(),
143 "bsp" | "rooms" | "room_accretion" => SemanticExtractor::for_rooms(),
144 "maze" => SemanticExtractor::for_mazes(),
145 _ => SemanticExtractor::default(),
146 };
147
148 let semantic = extractor.extract(&grid, &mut rng);
149
150 (grid, Some(semantic))
151}
152
153/// Generate a map that meets specific semantic requirements
154///
155/// This function attempts to generate a map that satisfies the given semantic requirements
156/// by trying different seeds and validating the results. It provides a simple wrapper
157/// around the existing generation system with requirement validation.
158///
159/// # Arguments
160/// * `algorithm_name` - Name of the generation algorithm to use
161/// * `width` - Grid width
162/// * `height` - Grid height
163/// * `requirements` - Semantic requirements that must be met
164/// * `max_attempts` - Maximum number of generation attempts (default: 10)
165/// * `base_seed` - Base seed for generation attempts
166///
167/// # Returns
168/// * `Ok((grid, semantic))` - Successfully generated map meeting requirements
169/// * `Err(String)` - Failed to meet requirements after max attempts
170///
171/// # Example
172/// ```rust
173/// use terrain_forge::{generate_with_requirements, semantic::SemanticRequirements};
174///
175/// let requirements = SemanticRequirements::basic_dungeon();
176/// match generate_with_requirements("bsp", 80, 60, requirements, Some(5), 12345) {
177/// Ok((grid, semantic)) => println!("Generated valid dungeon!"),
178/// Err(msg) => println!("Failed: {}", msg),
179/// }
180/// ```
181pub fn generate_with_requirements(
182 algorithm_name: &str,
183 width: usize,
184 height: usize,
185 requirements: semantic::SemanticRequirements,
186 max_attempts: Option<usize>,
187 base_seed: u64,
188) -> Result<(Grid<Tile>, semantic::SemanticLayers), String> {
189 let max_attempts = max_attempts.unwrap_or(10);
190
191 for attempt in 0..max_attempts {
192 let seed = base_seed.wrapping_add(attempt as u64);
193 let mut grid = Grid::new(width, height);
194 let mut rng = Rng::new(seed);
195
196 // Generate using specified algorithm
197 if let Some(algo) = algorithms::get(algorithm_name) {
198 algo.generate(&mut grid, seed);
199 } else {
200 return Err(format!("Unknown algorithm: {}", algorithm_name));
201 }
202
203 // Extract semantic layers
204 let extractor = match algorithm_name {
205 "cellular" => SemanticExtractor::for_caves(),
206 "bsp" | "rooms" | "room_accretion" => SemanticExtractor::for_rooms(),
207 "maze" => SemanticExtractor::for_mazes(),
208 _ => SemanticExtractor::default(),
209 };
210
211 let semantic = extractor.extract(&grid, &mut rng);
212
213 // Validate requirements
214 if requirements.validate(&semantic) {
215 return Ok((grid, semantic));
216 }
217 }
218
219 Err(format!(
220 "Failed to generate map meeting requirements after {} attempts",
221 max_attempts
222 ))
223}