跳到主要内容

编辑器概览与接入

VoxelEditorControllerVoxelViewer 的可选编辑能力。它复用已经显示的 ArkGraphics 场景、相机和手势,只在调用 enterEdit() 后才恢复可编辑的体素数据。换句话说:一个页面既可以先作为轻量预览器使用,又可以在用户点“编辑”时无缝进入体素编辑,而不必替换 Viewer 或重新载入模型。

编辑器不提供固定业务工具栏。模式按钮、调色板、保存按钮和提示文案由宿主页面按自己的产品风格实现;HAR 负责选取、局部网格更新、选区高亮、撤销/重做和保存字节生成。

能力范围

能力说明
表面操作单击模型表面以查看、取色、添加、删除、替换或切换选中体素。
自由颜色#RGB / #RRGGBB 可直接作为放置、替换或批量换色颜色;新颜色自动加入可保存调色板。
多选与框选支持逐个多选、按色、三维坐标盒、屏幕矩形框选。屏幕框选可从画布任意位置开始。
选区变换批量删除、换色、移动、复制、镜像、按 90° 旋转和沿选区边界对齐。
保存JSON/VFP 可原地保存、保存到指定 URI 或拉起系统“另存为”;VFP 派生区段按来源继承或按需覆盖。
视觉反馈已选体素显示逐格半透明实体高亮;框选拖动中显示屏幕选框和实时高亮。

当前编辑器面向 1..128 的体素网格。它编辑 JSON 源或 VFP 的权威 PAL0/VOX0 数据,不编辑任意 GLB 的顶点/拓扑;PRVW 只是 VFP 的派生 GLB 预览,不能作为编辑源。

接入结构

一个页面应创建一组稳定对象:VoxelViewerOptionsVoxelViewerControllerVoxelEditorController。编辑控制器构造时接收同一个 Viewer Controller,并作为 VoxelViewer.editor 传入。

import {
VoxelEditorController,
VoxelEditorOptions,
VoxelViewer,
VoxelViewerController,
VoxelViewerOptions
} from 'voxel-kit';

@Entry
@Component
struct EditableVoxelPage {
private readonly viewerOptions = new VoxelViewerOptions();
private readonly viewer = new VoxelViewerController(this.viewerOptions);
private readonly editorOptions = new VoxelEditorOptions();
private readonly editor = new VoxelEditorController(this.viewer, this.editorOptions);

aboutToAppear(): void {
this.viewer.loadUri(this.modelUri).catch((error: Error) => {
console.error(error.message);
});
}

build() {
VoxelViewer({ controller: this.viewer, editor: this.editor })
.width('100%')
.height(520)
.borderRadius(20)
.clip(true)
}

aboutToDisappear(): void {
this.viewer.dispose();
}

private modelUri: string = '';
}

不要在 build() 中创建 Controller,也不要把一个 VoxelEditorController 绑定给多个同时显示的 Viewer。重新导入模型后,已有编辑会话、选区与撤销记录会自动失效;应等待新模型加载完成后再次调用 enterEdit()

何时进入编辑

普通预览不会读取 VFP 的完整权威 VOX0。这使 JSON/VFP 预览继续走快速渲染缓存路径;当用户明确进入编辑时,编辑器才恢复数据:

输入来源enterEdit() 的行为
loadText() / JSON loadBytes()使用已经保存的 JSON 文本创建编辑文档。
VFP loadBytes()读取 META/PAL0/VOX0,校验 source hash,再创建编辑文档。
VFP loadUri()在 Picker URI 权限仍有效时重新读取完整 VFP,再执行权威校验。
private async openEditor(): Promise<void> {
try {
await this.editor.enterEdit();
// 进入后默认是 SELECT;可按页面业务切换模式。
} catch (error) {
console.error('进入编辑失败:' + (error as Error).message);
}
}

enterEdit() 成功前不要允许用户触发批量编辑或保存。可以用 editor.isUpdating() 或状态回调中的 preparing / updating / saving 控制按钮禁用状态。

状态回调与宿主 UI

VoxelEditorOptions.onStateChanged 是编辑页面的主状态来源。它会在进入/退出编辑、切换模式、选择、局部更新、保存、撤销和重做后触发。回调中只更新 ArkUI 状态;不要在回调里再次调用编辑 API。

@State private editorState: VoxelEditorState | undefined = undefined;

private readonly editorOptions: VoxelEditorOptions = (() => {
const options = new VoxelEditorOptions();
options.maxHistory = 100;
options.onStateChanged = (state: VoxelEditorState): void => {
this.editorState = state;
};
return options;
})();

常用字段如下:

字段用途
active / preparing / updating显示编辑器是否可操作及局部网格是否仍在提交。
mode / selectedSymbol / symmetryX驱动工具栏按钮和当前颜色指示。
selection / selectionCount / selectionBounds显示最后一个选中体素、多选数和包围范围。
palette渲染宿主自己的调色板控件。
canUndo / canRedo / dirty / saving驱动撤销、重做和保存按钮。
voxelCount / message展示模型规模和面向用户的状态提示。

完整字段与配置参数见 编辑器类型与保存 API

模式与触控

模式单指点击或拖动的效果
VIEW查看一个体素及其坐标,不修改数据。
SELECT单击切换单个体素是否加入多选。
BOX_SELECT单指拖出屏幕矩形,选取矩形内可见、最前方的体素;不触发模型旋转。
PICK从点击体素读取颜色并设为当前颜色。
ADD在命中面的外侧放置一个体素。
DELETE删除命中的体素。
REPLACE将命中的体素替换为当前颜色。

BOX_SELECT 外,单指保持 Viewer 的常规旋转行为;双指始终用于平移/缩放。编辑器进入准备阶段和保存阶段应临时禁用编辑按钮,避免业务层连续提交互相覆盖的操作。

下一步阅读 选取、批量编辑与变换;保存策略请阅读 保存与性能