HarmonyOS API 26 3DGS 编辑实战:高斯球选择、变换、上色与删除 HarmonyOS API 26 3DGS 编辑实战高斯球选择、变换、上色与删除实际项目里高斯球编辑事务最容易出现的问题不是“接口不会调用”而是状态分散、异步结果乱序、失败后没有回退。选择、变换、上色和删除可以撤销并安全提交需要把能力边界、生命周期、数据责任和验收证据放进同一条工程链路而不是把所有逻辑堆在页面点击事件里。本文围绕一个可落地目标展开选择、变换、上色和删除可以撤销并安全提交。官方资料确认能力方向示例则使用项目自定义适配器表达事务、撤销与持久化边界具体系统方法签名以 API 26 SDK 声明和真机验证为准。1. 这篇文章解决什么API 26 新增对 3DGS 高斯球的选择、变换、上色和删除能力编辑操作与渲染展示属于不同职责破坏性删除需要撤销或提交确认大批量编辑要避免每一步都永久写盘完成接入后读者应能回答四个问题能力是否适用于当前版本和设备一次请求由谁拥有失败后从哪里重试怎样证明结果确实可用。这四个问题比单独展示一段 API 调用更接近真实交付。文章不会把宣传页上的能力名称直接翻译成代码而是先确认官方资料能证明什么再把项目必须自行实现的控制器、状态和存储责任单独列出。这样既能迁移工程方法也能避免把示例封装误认成系统原生接口。2. 先确认能力边界检查项接入前要确认不满足时的处理SDK 与系统版本项目编译版本、运行设备版本和 API 起始版本一致隐藏入口或走旧版本方案设备与区域官方支持范围覆盖目标用户给出明确不可用提示生命周期请求归属页面、Ability 或长期服务退出后取消或忽略旧结果数据责任输入来源、保存位置和清理策略明确不启动不可回收任务验收方式有可重复步骤和失败样例不把界面出现当完成边界检查应该发生在业务动作之前。若能力不可用持续重试只会制造更多日志和资源消耗正确行为是降级到已有路径并说明限制。API 26 起版本号采用X.Y.Z语义化格式因此工程配置、运行时判断和文档记录应写成26.0.0不要只比较字符串“26”。公开的新特性说明确认 Spatial Recon Kit 增加高斯球选择、变换、上色和删除术语文档说明每个高斯点包含位置、颜色、透明度、旋转和缩放等属性。这里必须区分两层对象GSNode表示整个 3DGS 模型在场景树中的节点修改它的位置或缩放会作用于整个模型高斯球编辑针对模型内部的一组 Gaussian Point。若把两者混为一谈用户点击“移动选区”时可能移动整个场景节点。公开页面对spatialEdit的具体方法签名可能随 Beta SDK 调整所以本文不编造系统方法名。下面的GaussianEditAdapter是项目边界接口落地时由开发者根据当前 SDK 声明实现。3. 把流程画成可检查的状态机本文采用的五个关键检查点是空选择集 → 单点与批量选择 → 变换撤销 → 颜色边界 → 删除二次确认。每一步都要有明确输入与输出后一步只能消费前一步已经确认的结果。网络响应、回调触发或命令退出码只能证明某一步发生过不能自动代表最终业务成功。空间能力通常不支持用一个布尔值描述全过程。初始化、可交互、局部降级和致命失败要分开否则页面会把可恢复问题误判为整页不可用。export type WorkflowStage | idle | checking | running | degraded | succeeded | failed export interface GaussianEditSessionState { requestId: string stage: WorkflowStage message: string retryable: boolean updatedAt: number }状态对象只保存页面真正需要的结果不保存 Context、组件实例或底层句柄。这样页面重建后仍能读取稳定状态也降低了生命周期泄漏风险。4. 用分层结构隔离平台能力推荐把职责拆成四层ArkUI 页面呈现进度与错误空间控制器管理场景和任务生命周期Spatial Recon / ArkGraphics 3D执行空间能力资源层模型、清单与缓存。页面只负责用户输入和展示控制器负责顺序、去重与状态平台适配器只包装官方接口数据层负责持久化和缓存。export interface PlatformActionInput, Output { check(input: Input): Promisevoid execute(input: Input): PromiseOutput release(): Promisevoid } export class GaussianEditSessionInput, Output { private generation: number 0 private state: GaussianEditSessionState { requestId: , stage: idle, message: , retryable: false, updatedAt: Date.now() } constructor(private readonly action: PlatformActionInput, Output) {} async run(requestId: string, input: Input): PromiseOutput | null { const current: number this.generation this.update(requestId, checking, 正在检查运行条件, false) try { await this.action.check(input) this.update(requestId, running, 任务执行中, false) const output: Output await this.action.execute(input) if (current ! this.generation) return null this.update(requestId, succeeded, 任务已完成, false) return output } catch (error) { if (current this.generation) { this.update(requestId, failed, this.toMessage(error), true) } return null } } private update(requestId: string, stage: WorkflowStage, message: string, retryable: boolean): void { this.state { requestId, stage, message, retryable, updatedAt: Date.now() } } private toMessage(error: Object): string { return error instanceof Error ? error.message : 未知错误 } }generation用来阻止旧任务覆盖新任务。它不是取消底层操作的替代品但能保证页面只接受当前世代的结果。若平台接口支持取消还应在适配器的release()中执行真正的资源回收。5. 先定义编辑事务再接平台接口一次编辑不能只保存“点了删除”。它至少需要会话 ID、模型版本、选择集、操作类型、操作前快照和操作后快照。平台适配器只负责把命令应用到当前模型业务层负责校验和历史记录。export interface GaussianSelection { modelId: string pointIds: number[] revision: number } export interface Vector3Value { x: number y: number z: number } export type GaussianEditCommand | { kind: translate; selection: GaussianSelection; delta: Vector3Value } | { kind: scale; selection: GaussianSelection; factor: Vector3Value } | { kind: recolor; selection: GaussianSelection; rgba: number[] } | { kind: delete; selection: GaussianSelection } export interface GaussianEditAdapter { apply(command: GaussianEditCommand): Promisevoid restore(commandId: string): Promisevoid exportModel(targetUri: string): Promisevoid release(): Promisevoid }这些名称属于工程封装不是系统原生 API。它们的价值在于固定业务语义选择必须属于当前模型revision 必须匹配颜色必须有四个通道缩放不能包含零、负数或非有限值删除必须经过确认。export function validateCommand(command: GaussianEditCommand, currentRevision: number): void { if (!command.selection.modelId.trim()) throw new Error(modelId 不能为空) if (command.selection.pointIds.length 0) throw new Error(请先选择高斯球) if (command.selection.revision ! currentRevision) throw new Error(模型已变化请重新选择) if (new Set(command.selection.pointIds).size ! command.selection.pointIds.length) { throw new Error(选择集中存在重复 pointId) } if (command.kind recolor) { if (command.rgba.length ! 4 || command.rgba.some((value) value 0 || value 1)) { throw new Error(颜色通道必须位于 01) } } if (command.kind scale) { const factors [command.factor.x, command.factor.y, command.factor.z] if (factors.some((value) !Number.isFinite(value) || value 0)) { throw new Error(缩放系数必须为正的有限数) } } }颜色范围和缩放规则是本文业务约束需与当前 SDK 的真实类型换算。关键是让所有入口共用同一个校验器不能让快捷键、工具栏和触控手势各自实现一套规则。6. 生命周期与重复操作页面退出、窗口切换或场景销毁后旧异步结果不能继续写入已经失效的节点。使用任务世代号或显式 dispose 标记是比单纯 try/catch 更重要的保护。更稳的做法是让控制器拥有一次操作的完整生命周期开始前去重运行中禁止重复副作用结束后释放资源页面退出时使当前世代失效。需要持久化的状态交给 Service 或 Repository不要寄希望于页面对象一直存在。export class RequestGuard { private readonly running: Setstring new Set() begin(requestId: string): boolean { if (this.running.has(requestId)) return false this.running.add(requestId) return true } finish(requestId: string): void { this.running.delete(requestId) } }在finally中调用finish()可以保证成功和失败都释放占用。对于下单、删除、授权等有副作用的动作还需要服务端或数据层幂等键不能只依赖内存集合。撤销栈记录命令不复制整个模型大型 3DGS 模型不适合每次操作都复制完整文件。更合理的方式是让平台适配器返回最小反向数据例如被修改点的原始位置、原始颜色或删除前数据块再由历史记录保存 commandId 与撤销句柄。export interface AppliedEdit { commandId: string kind: GaussianEditCommand[kind] selectionCount: number revisionBefore: number revisionAfter: number } export class EditHistory { private undoStack: AppliedEdit[] [] private redoStack: AppliedEdit[] [] push(edit: AppliedEdit): void { this.undoStack.push(edit) this.redoStack [] } takeUndo(): AppliedEdit | undefined { const edit this.undoStack.pop() if (edit) this.redoStack.push(edit) return edit } takeRedo(): AppliedEdit | undefined { const edit this.redoStack.pop() if (edit) this.undoStack.push(edit) return edit } }真正的撤销不能只移动两个数组必须等待adapter.restore(commandId)成功后才更新 UI revision。若恢复失败历史指针保持原位并显示可重试错误否则页面会声称已撤销而模型仍停留在修改后状态。删除先进入墓碑态提交时再固化删除是最危险的操作。用户确认后业务层先把选中点标记为待删除并更新预览只有点击“保存副本”或“提交编辑”时适配器才导出新模型。取消会话时清除墓碑原始模型文件保持不变。这种设计让撤销成本更低也避免一次误触直接覆盖源文件。若产品确实需要原位保存必须先生成可恢复备份并在写入后重新打开目标文件做完整性校验。7. 失败处理表风险复现方式修复方向空选择仍执行命令清空选择后点击工具栏统一校验 selectionCount保持按钮禁用缩放系数为零破坏数据输入 0、负数与 NaNService 层拒绝保留上一个有效预览颜色通道范围错误输入 -0.1 或 1.2按当前 SDK 类型换算并执行范围校验删除无撤销批量选择后点击删除墓碑态预览、二次确认、提交前可撤销编辑中途退出留下半成品导出过程中返回页面临时文件写入成功后再原子替换目标排查时先找第一个真实失败点能力检查失败就不要继续看页面渲染平台执行成功但页面没更新再检查状态映射页面显示成功而服务端或文件不存在则回到最终结果回读。8. 性能、隐私与日志日志只记录 requestId、阶段、耗时、可公开错误码和脱敏资源标识。账号令牌、证书密码、完整个人数据、原始文件内容都不应进入日志。高频回调要聚合后更新 UI避免每次回调都触发重绘。性能优化要先建立基线。记录相同设备、相同版本、相同输入下的首帧、完成耗时、峰值内存或失败率再修改并复测。不同条件下的两个数字不能直接作为结论。对于需要联网、跨设备或访问本地敏感资源的能力还要把权限说明、用户授权、数据保存期限和清理入口写进实现清单。能力关闭或权限被撤销后控制器应立即停止新增任务并让已有任务进入可解释的降级状态而不是在后台反复请求。选择集可能包含大量 pointId日志不能逐点输出。记录 selectionCount、模型脱敏 ID、revision、命令类型、应用耗时、撤销耗时、导出耗时和结果码即可。颜色、变换参数可以记录归一化摘要但用户文件 URI 与模型原始内容不进入日志。性能基线至少区分单点、100 点、1 万点和大范围选择。对每组记录选择响应、预览更新、撤销、导出耗时与峰值内存。编辑操作流畅不代表导出可靠导出成功也不代表文件可再次加载所以最终证据必须包含“新文件加载成功并能渲染”。9. 页面如何呈现状态checking 阶段显示轻量提示running 展示真实进度或不确定进度degraded 保留已经可用的内容并提供局部重试failed 给出可执行的下一步succeeded 仍应允许用户核对最终结果。Component export struct WorkflowStatusCard { Prop state: GaussianEditSessionState build() { Column({ space: 8 }) { Text(this.state.message).fontSize(16).maxLines(2) if (this.state.stage running) { Progress({ value: 40, total: 100, type: ProgressType.Linear }) } if (this.state.retryable) { Text(可重试).fontColor(#0A59F7) } } .width(100%) .padding(16) } }示例中的进度值不是业务真实值。若底层没有精确进度就使用不确定进度样式不要制造虚假的百分比。10. 最小验收清单空选择集记录输入、环境、预期与实际结果。单点与批量选择记录输入、环境、预期与实际结果。变换撤销记录输入、环境、预期与实际结果。颜色边界记录输入、环境、预期与实际结果。删除二次确认记录输入、环境、预期与实际结果。此外还要验证快速重复点击、页面返回、横竖屏或窗口变化、应用切后台、资源被清理和导出空间不足等路径。交付记录逐项填写设备、API 版本、模型摘要、选择数量、命令、期望与运行证据缺少证据的项目保持“待复测”。通过标准至少包含空选择不能执行选择 revision 过期后必须重选平移与缩放可撤销和重做颜色边界被校验删除需要确认且提交前可恢复退出页面后不再写旧会话导出失败不覆盖源文件导出成功后新模型能够重新加载。11. 常见误区把接口返回当成最终成功。最终结果可能还要保存、回读或由另一个组件消费应验证真正的业务产物。把所有逻辑写进页面。页面重建后状态丢失资源释放和重复请求都会变得难以控制。失败后无限重试。版本、权限、配置和资源不存在不会通过重试恢复应先分类错误。只验证顺利路径。真正暴露工程问题的往往是取消、重复、超时、退出和部分成功。这些误区可以用代码评审清单提前拦截页面是否直接持有平台句柄、是否存在无上限重试、是否缺少 requestId、是否把回调触发当成最终完成、是否记录了敏感原文。只要其中一项为“是”就应在进入设备验收前先修正职责边界。12. 四类编辑动作如何落到同一条事务链路选择只产生选区不修改模型点选、框选和按条件选择都输出GaussianSelection。选区必须绑定 modelId 与 revision模型发生任何修改后旧选区要么明确迁移到新 revision要么失效并要求重新选择不能静默应用到不同版本。为了避免页面卡顿大选区只向 UI 返回数量和包围盒摘要不回传全部点数据。高亮渲染由平台适配层处理业务层只保存可校验的选择句柄。变换预览与提交分离拖动手柄时不断更新临时 delta画面使用预览接口手势结束才生成一条正式命令并进入撤销栈。若每个 move 事件都写历史记录一次拖动会产生数百条撤销步骤。export class TransformGestureSession { private base?: GaussianSelection private latest: Vector3Value { x: 0, y: 0, z: 0 } begin(selection: GaussianSelection): void { this.base selection this.latest { x: 0, y: 0, z: 0 } } update(delta: Vector3Value): void { if (!this.base) return this.latest delta } finish(): GaussianEditCommand | undefined { if (!this.base) return undefined const command: GaussianEditCommand { kind: translate, selection: this.base, delta: this.latest } this.base undefined return command } }上色统一颜色空间和透明度策略UI 颜色选择器、模型数据和渲染管线可能使用不同通道顺序或取值范围。转换只能集中在适配器中进行。业务层使用固定 RGBA 归一化结构同时明确是否允许修改透明度若产品只允许改颜色alpha 应保持原值而不是默认写成 1。删除从预览墓碑到安全导出删除命令先写入会话状态并产生预览撤销只移除墓碑。提交时生成目标临时文件校验长度、格式和重新加载结果后再让用户选择保存位置。源文件默认只读是最稳妥的策略。13. 保存模型时建立两阶段提交第一阶段把编辑结果导出到临时 URI并计算可复核摘要第二阶段重新加载临时文件检查模型可渲染、点数量符合预期、会话 revision 一致全部通过后才把它标记为用户可用成品。export interface ExportEvidence { sessionId: string revision: number targetUri: string byteLength: number reloadPassed: boolean completedAt: number } export async function exportWithVerification( adapter: GaussianEditAdapter, tempUri: string, verify: (uri: string) Promisenumber ): PromiseExportEvidence { await adapter.exportModel(tempUri) const byteLength await verify(tempUri) if (byteLength 0) throw new Error(导出文件为空) return { sessionId: current-session, revision: 1, targetUri: tempUri, byteLength, reloadPassed: true, completedAt: Date.now() } }verify()与临时路径生成属于项目基础设施示例没有假定具体文件 API。生产实现还需处理存储空间不足、目标文件已存在、应用切后台、系统中断和用户取消。任何异常都只清理本次临时文件不删除源模型。14. 五类故障的排查顺序选区看得见但命令提示为空。检查高亮层与业务 selection 是否使用同一个 selectionId不要从画面颜色反推选择集。撤销后画面没恢复。核对反向数据是否在应用命令前采集、restore 是否等待完成、revision 是否在成功后更新。仅移动撤销栈指针不会改变模型。大选区操作卡住。分别测量选择、预览、正式应用和历史记录耗时确认是否把全部 pointId 复制到页面状态或日志。大数据应留在平台层用句柄和摘要跨层传递。删除后重新进入又出现。先判断用户是否只做了墓碑预览而没有提交再核对导出目标是否被真正选中和重新加载。UI toast 不能代替目标文件回读。导出后模型打不开。保留临时文件与可公开错误码检查导出是否被中断、目标 URI 是否可写、版本与格式是否兼容。不要覆盖源文件后再排查。15. 代码评审与真机验收矩阵评审先检查四条边界页面是否直接调用 Beta 接口选区是否绑定 revision删除是否可恢复导出是否回读。随后检查 Context、Scene、模型句柄和临时文件的释放路径。真机矩阵至少包含 Phone、Tablet、PC/2in1 中产品实际支持的设备类型以及单点、小选区、大选区三种规模。每组执行选择、平移、缩放、上色、删除、撤销、重做、导出、重新加载与页面退出。API 26 使用语义化版本号后测试报告应记录完整的26.0.0或修订版本而不是模糊写“API 26”。Beta 能力升级时重新核对 SDK 声明与返回类型再运行适配器单测和真机矩阵。16. 总结HarmonyOS API 26 3DGS 编辑实战高斯球选择、变换、上色与删除的重点是围绕“选择、变换、上色和删除可以撤销并安全提交”建立完整链路。先核对官方能力边界再把任务放进独立控制器通过稳定 requestId、显式状态、输入校验、生命周期保护和最终结果回读让页面展示与真实结果保持一致。参考资料华为开发者联盟文档中心华为开发者联盟文档中心华为开发者联盟文档中心华为开发者联盟文档中心验证边界本文依据公开官方资料核对能力方向、术语与版本规则适配器、命令、撤销栈和两阶段导出属于工程设计。具体spatialEdit方法签名、设备结果与性能阈值需结合当前 API 26 SDK 和真机复测。CSDN-SERIES:HARMONYOS-BATCH-005