pub trait TileSetVirtual<EdgeData>{
// Required methods
fn build_tile_set(&mut self) -> Result<(), GridError>;
fn judge_possibility(
&self,
neighbor_possibilities: &[Vec<TileId>],
candidate: TileId,
) -> bool;
fn get_tile(&self, tile_id: TileId) -> Option<&Tile<EdgeData>>;
fn get_tile_count(&self) -> usize;
fn get_all_tile_ids(&self) -> Vec<TileId> ⓘ;
}Expand description
瓷砖集虚函数特性 - 仅包含C++的两个虚函数
这个trait专门提取了原C++代码中的两个纯虚函数,实现了与原C++设计的完全对应:
virtual void buildTileSet() = 0;virtual bool judgePossibility(...) = 0;
§设计理念
§职责分离
将虚函数逻辑从数据管理中分离出来,带来以下好处:
- 清晰的接口:只包含需要自定义实现的方法
- 类型安全:编译时确保所有必要方法都被实现
- 测试友好:可以独立模拟和测试虚函数逻辑
§与原C++的一致性
| C++虚函数 | Rust trait方法 | 功能 |
|---|---|---|
buildTileSet() | build_tile_set() | 构建瓷砖集 |
judgePossibility(...) | judge_possibility(...) | 判断瓷砖可能性 |
§泛型参数
EdgeData 类型参数表示瓷砖边的数据类型,需要满足:
Clone:支持复制操作PartialEq:支持相等性比较Debug:支持调试输出
§实现示例
use rlwfc::{TileSetVirtual, TileSet, TileId, Tile, GridError};
struct SimpleTileSet {
tiles: TileSet<&'static str>,
}
impl TileSetVirtual<&'static str> for SimpleTileSet {
fn build_tile_set(&mut self) -> Result<(), GridError> {
// 清空现有瓷砖
self.tiles.clear();
// 添加具体的瓷砖
self.tiles.add_tile(vec!["A", "B", "C", "D"], 10);
self.tiles.add_tile(vec!["B", "A", "D", "C"], 15);
Ok(())
}
fn judge_possibility(
&self,
neighbor_possibilities: &[Vec<TileId>],
candidate: TileId
) -> bool {
// 实现具体的约束判断逻辑
if let Some(_tile) = self.tiles.get_tile(candidate) {
// 检查候选瓷砖是否与邻居兼容
// 这里应该实现具体的兼容性检查逻辑
!neighbor_possibilities.is_empty()
} else {
false
}
}
fn get_tile(&self, tile_id: TileId) -> Option<&Tile<&'static str>> {
self.tiles.get_tile(tile_id)
}
fn get_tile_count(&self) -> usize {
self.tiles.get_tile_count()
}
fn get_all_tile_ids(&self) -> Vec<TileId> {
self.tiles.get_all_tile_ids()
}
}Required Methods§
Sourcefn build_tile_set(&mut self) -> Result<(), GridError>
fn build_tile_set(&mut self) -> Result<(), GridError>
构建瓷砖集 - 对应C++的buildTileSet()虚函数
这个方法负责初始化和填充瓷砖集合。具体的实现由各种不同的瓷砖集类型决定。
§实现要求
实现者应该在此方法中:
- 清理现有状态:清空或重置瓷砖集合
- 创建瓷砖:添加所有需要的瓷砖到集合中
- 设置属性:配置每个瓷砖的权重、边数据等
- 验证完整性:确保瓷砖集合的一致性和完整性
§调用时机
这个方法通常在以下时机被调用:
- WFC系统初始化时
- 重新开始新的生成过程时
- 动态改变瓷砖集配置时
§示例实现
fn build_tile_set(&mut self) -> Result<(), rlwfc::GridError> {
// 1. 清理现有瓷砖
self.tiles.clear();
// 2. 添加基础瓷砖
self.tiles.add_tile(vec!["grass", "grass", "grass", "grass"], 50);
self.tiles.add_tile(vec!["water", "water", "water", "water"], 30);
// 3. 添加过渡瓷砖
self.tiles.add_tile(vec!["grass", "water", "grass", "water"], 20);
// 4. 可选:添加验证逻辑
debug_assert!(!self.tiles.is_empty());
Ok(())
}Sourcefn judge_possibility(
&self,
neighbor_possibilities: &[Vec<TileId>],
candidate: TileId,
) -> bool
fn judge_possibility( &self, neighbor_possibilities: &[Vec<TileId>], candidate: TileId, ) -> bool
判断瓷砖可能性 - 对应C++的judgePossibility()虚函数
这是WFC算法的核心约束判断方法。它决定了在给定邻居约束的情况下, 某个候选瓷砖是否可以放置在当前位置。
§⚠️ 重要:利用边数据顺序约定
由于瓷砖的边数据严格按照 neighbors() 返回顺序排列,
本方法可以直接通过索引访问对应方向的边数据,实现高效的兼容性检查。
§索引到方向的直接映射
neighbor_possibilities[0] <-> candidate_tile.edges[0] (北方向)
neighbor_possibilities[1] <-> candidate_tile.edges[1] (西方向)
neighbor_possibilities[2] <-> candidate_tile.edges[2] (南方向)
neighbor_possibilities[3] <-> candidate_tile.edges[3] (东方向)§高效实现模式
fn judge_possibility(
&self,
neighbor_possibilities: &[Vec<TileId>],
candidate: TileId
) -> bool {
let Some(candidate_tile) = self.get_tile(candidate) else {
return false;
};
for (direction_index, neighbor_tiles) in neighbor_possibilities.iter().enumerate() {
// 🎯 直接获取候选瓷砖在该方向的边数据
let candidate_edge = &candidate_tile.edges[direction_index];
// 检查与该方向所有可能邻居的兼容性
let is_compatible = neighbor_tiles.iter().any(|&neighbor_id| {
if let Some(neighbor_tile) = self.get_tile(neighbor_id) {
// 获取邻居瓷砖相对方向的边数据
let opposite_index = match direction_index {
0 => 2, // 北 ↔ 南
1 => 3, // 西 ↔ 东
2 => 0, // 南 ↔ 北
3 => 1, // 东 ↔ 西
_ => return false,
};
let neighbor_edge = &neighbor_tile.edges[opposite_index];
// 边兼容性检查(具体规则由应用定义)
candidate_edge == neighbor_edge
} else {
false
}
});
if !is_compatible {
return false;
}
}
true
}§性能优势
通过边数据顺序约定,该方法获得了显著的性能优势:
- 零成本索引映射:无需运行时的方向转换或查找表
- O(1) 边数据访问:直接数组索引,最高效的访问方式
- 缓存友好:连续的内存访问模式,提高CPU缓存命中率
- 编译时优化:编译器可以更好地优化索引访问代码
§参数
-
neighbor_possibilities- 邻居单元格的可能瓷砖列表数组- 每个元素是一个邻居的可能瓷砖ID列表
- 数组的顺序对应方向顺序:[北, 西, 南, 东]
- 空列表表示该方向没有邻居或邻居未确定
-
candidate- 候选瓷砖的ID
§返回值
true- 该瓷砖在当前邻居约束下是可能的false- 该瓷砖与邻居约束冲突,不能放置
§错误情况
实现者应该处理以下错误情况:
- 候选瓷砖ID无效(不存在对应的瓷砖)
- 邻居瓷砖ID无效
- 边数据索引越界(瓷砖边数量不足)
§算法逻辑
典型的实现流程:
- 验证候选瓷砖:确认候选瓷砖存在且有效
- 遍历方向约束:检查每个方向的邻居约束
- 获取边数据:直接通过索引获取对应方向的边数据
- 兼容性检查:验证候选瓷砖的边与邻居瓷砖的边是否兼容
- 返回结果:所有方向都兼容则返回true,否则返回false
§性能考虑
这个方法在WFC算法中会被频繁调用,因此性能很重要:
- 考虑缓存计算结果
- 优先检查最容易失败的约束
- 使用快速的边比较算法
- 利用边数据顺序约定避免额外的映射开销
Sourcefn get_tile_count(&self) -> usize
fn get_tile_count(&self) -> usize
获取瓷砖总数
Sourcefn get_all_tile_ids(&self) -> Vec<TileId> ⓘ
fn get_all_tile_ids(&self) -> Vec<TileId> ⓘ
获取所有瓷砖ID列表