化学键绘BondDraw上架鸿蒙PC:不用 WebView 的纯 ArkTS 化学结构式编辑器

欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

化学结构式编辑器是最「值得重写」的一类移植对象:上游 JChemPaint 是 Java/Swing 生态,整套搬上鸿蒙既不可行也不划算;但它的领域模型——原子、化学键、环系、坐标清理——是纯粹的二维几何与图论,用 ArkTS 从模型到渲染完整重写,反而得到一个几百 KB、启动即画、零依赖的原生应用。本文按上架全流程复盘 BondDraw:领域模型怎么分层、Molfile 与 SMILES 双解析器怎么写、30° 角度吸附与双键平行线这类「专业感」怎么实现,以及 SDK 6.0.1 里 ImagePacker.pack 被移除后的导出适配。

项目上架信息 / 工程事实
应用名化学键绘BondDraw
包名org.jchempaint.jchempaint
版本1.1.0 (1000100)
设备形态2in1 / tablet(HarmonyOS PC)
上游项目JChemPaint(开源化学结构绘制器,LGPL)
技术路线纯 ArkTS 全原生实现,无 WebView、无第三方引擎
源码规模10 个 ArkTS 文件(模型 6 + 页面 3 + Ability 1)
权限零权限,文件与导出全部走系统选择器
上架状态已在鸿蒙应用市场正式上架

在这里插入图片描述

一、先看效果图

图 1 · 模板库:经典分子一键加载

模板库与经典分子

图 1:模板库面板。咖啡因、维生素 C、尼古丁等经典分子一键加载;左侧是键级工具(单键/双键/三键/未定义键/虚键)、原子与环系(三元环到八元环、苯环);底部元素栏 C/H/O/N/P/S/F/Cl/Br/I 与周期表入口。

图 2 · 网格辅助:每一根键都落在正确的角度上

网格辅助与角度吸附

图 2:30° 角度吸附、网格辅助、双键平行线与杂原子 CPK 配色。状态栏实时显示「103 原子 · 分子式 C8H10N4O2」——正在画的正是一分子咖啡因,画完即刻核对。

图 3 · 结构式绘制主界面

结构式绘制主界面

图 3:画布与工具栏。创建/打开/保存、撤销/重做、复制/粘贴、旋转/镜像/直角翻转、缩放一应俱全,完整键鼠交互。


二、需求设计:结构式绘制的「最小专业集」

化学课与实验报告需要随手画出规范的结构式。需求设计从「什么是规范」倒推出一张最小专业集清单——这也是纯 ArkTS 重写的功能边界:

必须有(专业底线)明确不做(首版边界)
单/双/三键与未定义键、虚键3D 构象与立体化学标注
原子标签、杂原子 CPK 配色反应机理箭头推导
环系模板 + 经典分子模板命名生成结构(IUPAC)
30° 角度吸附 + 网格辅助云端结构库检索
Molfile / SMILES 双格式读写多文档标签
一键结构清理(键角规整)原子属性编辑器面板

「不做清单」让首版聚焦在一条完整闭环上:画得出来 → 存得下去 → 导得出图。状态栏实时显示原子数、键数与 Hill 分子式,是这个闭环的「即时反馈器」——画完即核对,教学场景里它比任何功能按钮都常用。


三、架构:六个模型文件撑起的专业工具

纯 ArkTS 路线的代码组织刻意保持扁平——10 个文件,每层职责单一:

页面与交互层   Index.ets(画布、手势、菜单) / EditorController.ets(状态与命令)
领域模型层     ChemModel.ets  化学图模型(原子/键的邻接结构)
              MdlIO.ets      MDL Molfile 解析与写出
              SmilesIO.ets   SMILES 文本 → 化学图
              Cleanup.ets    结构清理(键角规整)
              Templates.ets  环系与经典分子模板库
渲染与输出层   Renderer.ets   自研二维绘制引擎

3.1 领域模型:化学图就是图论

ChemModel 把分子建模为标准图结构:原子是顶点(元素、坐标、电荷),键是带属性的边(键级、环标记)。这个决定让后续所有能力都「顺」了——

  • Molfile 解析就是读一个「原子块 + 键块」的文本图描述;
  • SMILES 解析是深度优先遍历 + 分支栈 + 环编号闭合;
  • 结构清理是图上的几何优化;
  • Hill 分子式是一次元素计数排序(C 在前、H 其次、其余按字母序)。

领域模型选对了,四个功能各自只剩几十行实现。

3.2 双解析器:专业格式的入场券

MdlIO 处理 MDL Molfile——化学软件界事实上的交换格式,counts line、原子块、键块逐行解析,坐标直接可用;写出时反向生成,保证画的东西能在其他化学软件里打开。

SmilesIO 处理 SMILES——CC(=O)O 这类一行文本描述整个分子。解析器是手写递归下降:原子符号、括号分支、环闭合编号三类语法,配合原子价态隐式氢补全。SMILES 的价值在输入效率:教学场景里老师给一串 SMILES 让学生「打开看看」,比点十次工具栏快得多。

3.3 Renderer:自研二维绘制的三个细节

化学结构式的「专业感」全在渲染细节里:

  • 双键平行线:两条平行线段,偏移量随键长缩放,端点收缩让交叉处不穿帮;
  • 杂原子标签避让:碳原子默认省略(骨架式),O/N/Cl 等杂原子显示标签;标签出现时键端要回缩一段文字避让区,否则线会穿过字母;
  • CPK 配色:O 红、N 蓝、Cl 绿等约定色,键为黑灰、骨架线宽统一。

三个细节都是像素级的较真,但它们合起来决定了「这是化学软件」还是「这是画图板」。


四、交互实现:吸附、清理与模板

4.1 30° 角度吸附与网格辅助

结构式有强约定:链角 120°、环内几何对称。实现上把画布角度量化为 30° 步进,新键端点在放置时吸附到最近量化角;网格辅助负责平移对齐。这两个「小约束」让新手画出的图也接近专业软件的产出——吸附不是限制,是把领域规范内置成交互。

4.2 Cleanup:一键规整键角

手画的分子七扭八歪是常态。Cleanup 实现了几何规整:以键长为单位重建布局,链段按 120° 展开,环系按正多边形闭合。它是「结构清理」按钮背后的全部逻辑,也是模板功能的姊妹能力——模板给标准起点,Cleanup 给手画稿兜底。

4.3 Templates:从环系到咖啡因

模板库两层:环系模板(三元到八元环、苯环,含芳香环内圈)给局部构件;经典分子模板(咖啡因、维生素 C、尼古丁)给完整起点——每个都是一段内嵌 Molfile/SMILES 文本,走同一条解析链路加载。模板即数据,不特判:加载咖啡因和加载用户文件的代码路径完全一致,这保证解析器的正确性被模板持续回归。

4.4 缩放与画布

视图缩放以 ×1.25/×0.8 步进、钳位在 0.2–5.0 之间,复位一键回 1.0;画布平移与框选配合键鼠。首启刻意给空画布而非自动加载示例——工具类应用的首启要尊重「我有一个明确任务」的用户,示例入口放在模板面板里随取随用。


五、导出链路:SDK 6.0.1 的 API 变更适配

结构式画完通常要去贴进实验报告,导出 PNG 是闭环的最后一环,也是踩到 SDK 变更的地方:

// 1. 组件快照:把画布组件渲染成 PixelMap
const snapshot = await componentSnapshot.get('chemcanvas');
// 2. 图像编码:SDK 6.0.1(21) 中 ImagePacker.pack 已移除,改用 packToData
const packer = image.createImagePacker();
const packOpts: image.PackingOption = { format: 'image/png', quality: 100 };
const buf = await packer.packToData(snapshot, packOpts);
// 3. 系统保存对话框:用户指定落点,零存储权限
const opts = new picker.DocumentSaveOptions();
opts.newFileNames = ['structure.png'];
const uris = await (new picker.DocumentViewPicker(ctx)).save(opts);
fileIo.writeSync(fileIo.openSync(uris[0], READ_WRITE).fd, buf);

三个环节三个要点:组件快照用 componentSnapshot.get 按组件 id 截取(画布要设固定 id);编码 API 在 SDK 6.0.1 里从 pack 换成了 packToData——API 迁移期的旧写法在编译期就报错,这反而是好事,逼迫适配而不是静默降级;落盘走 DocumentViewPicker.save,导出即授权,权限表继续保持为空。

文件读写(打开/保存 .mol)同样走系统选择器,用户授权制,二次打开经由授权持久化免重复确认。


六、调试与真机实测

真机验收按「画一个完整分子」的剧本走查:

  1. 模板加载:一键载入咖啡因,状态栏原子/键数与 Hill 分子式逐项核对(103 原子、C8H10N4O2);
  2. 手绘 + 吸附:从苯环起手连支链,验证 30° 吸附与双键平行线的落点;
  3. 往返一致性:画 → 存 Molfile → 重新打开 → 与原图逐键比对;SMILES 输入 CC(=O)O 生成乙酸后导出 Molfile,再用上游工具交叉验证;
  4. 清理实测:故意画歪的链与环,Cleanup 后键角归位、无重叠;
  5. 导出实测:导出 PNG 真机落盘,图像清晰度满足报告插图要求;
  6. 异常路径:格式错误的 Molfile/SMILES 不崩溃,状态栏给出可读错误。

调试方法论上,渲染类问题的定位手段是把坐标数据可视化:Renderer 暴露调试开关,在画布上叠加绘制原子序号与键向量,吸附、避让、平行线偏移的每个几何量都看得见——几何 bug 的解药是让几何可见。


七、上架资质与审核沟通

材料工程事实
身份与版本org.jchempaint.jchempaint,1.1.0 (1000100),签名 profile 绑定包名
权限表零权限——绘制全本地,文件与导出走选择器授权
隐私面无账号、无遥测、无网络;结构数据留在沙箱
预览图模板库、网格辅助、主界面三张真机实拍
开源合规上游 JChemPaint LGPL,应用内关于页声明一致

审核沟通的要点是把「纯 ArkTS 重写」讲成合规优势而非侵权风险:上游 LGPL 项目,重写实现的领域模型与格式规范不受版权保护,UI 与代码全部原创;关于页写明上游与许可,仓库 LICENSE 对齐。重写型移植的合规叙述要在提审前想清楚,避免审核阶段反复补充说明。


八、上架后迭代

1.1.0(versionCode 1000100)的迭代与后续路线围绕「教学场景加深」:

  1. 模板扩容:官能团与药物分子模板扩充,保持「模板即数据」架构不变;
  2. 更多格式:SMILES 写出(目前读入为主)与 InChI 读入,扩大互操作面;
  3. 原子属性编辑:电荷与同位素标注,为有机化学进阶场景铺路;
  4. 画布增强:多选批量操作与对齐分布。

每一条都建立在「领域模型是图论」的底座上——这就是当初把模型层独立出来的回报。


九、踩坑总结

  1. 领域模型选图论,解析/清理/统计全是顺产;模型层与渲染层严格分离,Renderer 只读模型。
  2. SMILES 手写递归下降可控:原子、括号分支、环闭合三类语法,几百行收口;隐式氢按价态补全别省。
  3. 杂原子标签必须回缩键端,否则线穿标签;骨架式省略碳标签是专业感的底线。
  4. 吸附要做成「放置时量化」而不是「事后校正」——交互语义完全不同,前者是帮助,后者是打扰。
  5. SDK 6.0.1 中 ImagePacker.pack 已移除,导出用 packToData;API 迁移让编译器替你把关。
  6. 首启给空画布,示例藏在模板面板;工具类应用尊重「带着任务来的用户」。
  7. 真机验收按完整剧本走(载入→手绘→清理→往返→导出→异常输入),逐项核对状态栏数据。

十、结语

BondDraw 证明了「重写」有时比「移植」更划算:当上游的 UI 框架(Java/Swing)注定带不上,而核心价值全在领域模型与格式规范里时,用目标平台的 first-class 语言重写,得到的是更小、更快、更贴合系统交互习惯的应用——几百 KB 的包体、零权限的模型、秒开的画布,全是重写的红利。

判断「该移植还是该重写」的两条标尺:一是上游价值在哪里(若在领域逻辑与文件格式,重写保值;若在交互资产与运行时,移植保值);二是目标平台的语言生态能否覆盖领域需求(ArkTS 的 Canvas、手势、组件快照恰好全覆盖)。两条都指向重写时,就不要犹豫。

源码与构建说明见开源仓库;本文代码摘录均来自工程实际提交。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐