跳到主要内容

选取、批量编辑与变换

编辑器把每个被选中的体素保存在源网格坐标中,并在模型上显示逐格半透明高亮。选区操作均以整数格为单位;它们会写入一条可撤销历史并只刷新受影响的 Chunk,而不是整模型重新导入。

单点选择与取色

先切换模式,再让用户直接触控模型:

this.editor.setMode(VoxelEditMode.SELECT); // 单击增减一个选中体素
this.editor.setMode(VoxelEditMode.PICK); // 单击吸取颜色
this.editor.setMode(VoxelEditMode.ADD); // 命中面外侧放置
this.editor.setMode(VoxelEditMode.REPLACE);
this.editor.setSelectedColor('#3A86FF');

setSelectedSymbol(symbol) 只接受当前调色板已存在的符号;setSelectedColor(hex) 接受 #RGB#RRGGBB,自动复用相同颜色或新增一个内部符号。新增符号会出现在 VoxelEditorState.palette,并随 JSON/VFP 保存。

开启 setSymmetryX(true) 后,ADD、DELETE、REPLACE 会同时尝试写入 X 轴镜像格;镜像目标越界、与原格重合或不符合该操作条件时会被安全忽略。

三种程序化选区

这些方法适合工具栏按钮、命令菜单或脚本化编辑。返回值均为当前选区体素数;未进入编辑或没有匹配项时返回 0

方法签名选择规则
全选selectAll(): number选择全部非空体素。
按颜色选择selectByColor(color?: string, append?: boolean): numbercolor 可为调色板符号或 #RRGGBB;默认使用当前颜色。
三维坐标盒selectByBox(min, max, append?: boolean): number选择包含端点在内的源网格轴对齐盒中的非空体素。
清空clearSelection(): void仅清空选择,不修改模型。
// 选择某个颜色,再把它和已选择内容合并。
this.editor.selectByColor('#6B9E47');
this.editor.selectByColor('#D995A8', true);

// 坐标会被取整并钳制到 0..gridSize-1,两个端点顺序不限。
this.editor.selectByBox(
new VoxelCoordinate(10, 0, 6),
new VoxelCoordinate(34, 22, 28)
);

可以用 getSelectionVoxels() 获取副本、getSelectionSize() 获取数量、getSelectionBounds() 获取包含式 min/max 边界。返回的数组和对象可安全用于 UI,不要修改后期待它们反写模型。

屏幕框选

将模式切换为 BOX_SELECT 后,用户可以从 Viewer 画布的任意位置按下并拖到任意位置:

  1. 拖动中显示半透明矩形和实时逐格高亮预览;预览使用低采样,避免拖动卡顿。
  2. 松手后使用更密的逐像素视线采样收敛最终结果。
  3. 每条视线只取最先命中的可见体素,因此不会把被前景遮挡的背面体素加入选区。

该模式单指拖动不会旋转模型。如果要重新旋转,切回 VIEW 或其他非框选模式。屏幕框选只负责可见表面;若需选取网格内部或不在当前视角内的体素,请使用 selectByBox()

批量删除与换色

方法结果返回值
deleteSelection()删除全部选中体素。实际删除数量。
setSelectionSymbol(symbol)用已有调色板符号批量换色。实际修改数量。
setSelectionColor(hex)用任意 #RGB / #RRGGBB 批量换色。实际修改数量。
if (this.editor.getSelectionSize() > 0) {
this.editor.setSelectionColor('#F1C7D6');
// 或 this.editor.deleteSelection();
}

空选区不会抛错,操作返回 0 并通过状态 message 提示。批量操作会作为一条 Undo 历史记录。

选区变换

方法签名行为
移动moveSelectionBy(dx?, dy?, dz?): number整数格平移,移动后仍选中移动后的体素。
复制duplicateSelection(offset?): number保留原选区,复制品成为当前选区。
镜像mirrorSelection(axis?): number围绕选区自身的包含式边界原地镜像。
旋转rotateSelection(axis?, quarterTurns?): number围绕选区自身边界按 90° 的整数倍旋转。
对齐alignSelection(mode?): number将每个选中体素压到选区的某个最小、中间或最大坐标平面。
// 复制一份到右侧两格。
this.editor.duplicateSelection(new VoxelCoordinate(2, 0, 0));

// 将当前选区绕 Y 轴顺时针旋转 90°,然后压到自身底部平面。
this.editor.rotateSelection(VoxelAxis.Y, 1);
this.editor.alignSelection(VoxelSelectionAlign.MIN_Y);

变换前会进行两类安全检查:

  • 任何目标格超出 0..gridSize-1 时,整次变换取消;
  • 目标若覆盖未被选中的实心体素,整次变换取消。

因此返回 0 既可能表示空选区,也可能表示越界/冲突;应读取 getState().message 显示具体原因。选区内部体素相互重叠是允许的,最终会合并为一个目标格。

撤销、重做与历史上限

if (this.state?.canUndo) this.editor.undo();
if (this.state?.canRedo) this.editor.redo();

每次添加、删除、替换、批量换色和选区变换记为一条历史记录;选择本身不进入历史。VoxelEditorOptions.maxHistory 默认 100,超出后最早的记录被丢弃。重新导入模型、退出编辑或再次进入一个新场景会清空历史。

选区高亮仅是视觉层,不属于模型数据,也不会写进 JSON/VFP。