ohos_JChemPaint:基于 HarmonyOS 与 C++ 原生引擎的二维化学结构编辑器

在鸿蒙 PC 上原生绘制和编辑二维化学结构,覆盖分子式计算、SMILES 互转、子结构搜索与 PNG 导出。项目以 C++ 原生引擎处理核心算法,ArkUI 声明式界面呈现,支持鼠标与触控双模交互,零网络权限离线运行。【此简介由AI生成】

Branch1Tags0
FilesLast commitLast update
16 days ago
16 days ago
19 days ago
11 days ago
19 days ago
18 days ago
11 days ago
19 days ago
19 days ago
19 days ago
11 days ago

化学键绘(BondDraw)—— 鸿蒙 PC 化学结构编辑器

原生引擎 · 原生界面 · 原生交付

绘的是化学键,用的是原生栈。

Platform API Device Engine UI Runtime

化学键绘主界面

主界面:桌面级菜单栏 · 双工具栏 · 自绘画布 · 实时状态栏


目录


产品定位

化学键绘(BondDraw) 是一款面向 鸿蒙 PC(2in1 / 平板) 的二维化学结构编辑器,覆盖从「画一个分子」到「交付一份结构文件」的完整链路:绘制 → 标注 → 校验 → 排版 → 导出。

它移植自经典开源化学绘图软件 JChemPaint 的核心能力模型,但并非简单搬运:

维度 上游 JChemPaint 化学键绘(BondDraw)
运行时 Java 虚拟机 + Swing 图形栈 仅一个 C++ .so + 系统 ArkUI
界面范式 桌面窗口 + 菜单栏 鸿蒙原生 Stage 模型 + 声明式 UI
输入模型 鼠标优先 鼠标 / 触控 / 快捷键三模并存
渲染 Swing Graphics2D Canvas 2D 矢量自绘(可无限缩放)
部署形态 桌面安装包 HAP 上架鸿蒙 PC 应用市场
包体量级 JRE + 依赖,数百 MB 数 MB 级 HAP,含原生引擎

一句话概括:把一款「跑在 JVM 上的化学绘图工具」,重构成「跑在鸿蒙上的原生生产力工具」。

为什么是原生

化学结构编辑是典型的 高频微操作 + 精确命中 + 实时重绘 场景:一次键级切换需要毫秒级反馈,一次拖拽需要在 60fps 下持续命中测试。这类负载对「容器套壳」最不友好。

  • 不携带跨平台运行时:没有 Electron、没有 Qt、没有 WebView 容器、没有自带 JVM;
  • 不引入框架抽象层:手势、焦点、窗口、菜单全部走系统公开能力;
  • 不牺牲离线能力:应用零网络权限,全部计算在本地完成。

结果是:冷启动直接进入可绘制状态,交互延迟由系统渲染管线决定,而非框架层转译。


设计原则

01 · 桌面优先

菜单栏、双工具栏、状态栏、浮动对话框——不把桌面应用做成放大的手机 App。窗口尺寸变化时画布自适应,工具区保持稳定布局。
02 · 原生到底

UI 用 ArkUI 声明式语法编写,画布用 Canvas 2D 自绘,算法用 C++ 实现并经 NAPI 暴露。每一层都是目标平台的一等公民。
03 · 零依赖交付

工程无三方 UI 库、无网络库、无跨平台框架;依赖只有系统 SDK 与自研引擎库,供应链面积极小。
04 · 数据可迁移

读写业界通行的 MDL V2000 / SDF / SMILES,文件可被 ChemDraw、RDKit、CDK、Avogadro 等工具直接消费,不做格式锁定。

界面导览

主编辑界面

主编辑界面

界面自上而下分为五个稳定区域,遵循桌面编辑器的肌肉记忆:

区域 内容 交互
菜单栏 文件 · 编辑 · 视图 · 原子 · 键 · 反应 · 工具 · 帮助 下拉菜单,命令与工具栏按钮语义一致
主工具栏 新建 / 打开 / 保存 / 撤销 / 重做 / 剪切 / 复制 / 粘贴 / 旋转 / 翻转 / 整理 / 缩放 单击即执行,图标态表达可用性
工具面板 选择、套索、各类键工具、环模板、链工具、橡皮、移动、反应箭头、模板库 单选式工具,当前工具高亮,键盘单键直切
画布 自绘网格 + 结构矢量渲染,支持缩放 / 平移 / 悬停高亮 拖拽绘制、点击选中、框选、套索、捏合缩放
状态栏 原子数 · 键数 · 选中数 · 实时分子式 · 操作反馈 随模型变更即时刷新,无需手动查询

多分子图版

多分子示例

同一画布可承载任意多个独立分子、离子对与反应式,通过「工具 → 示例图版」一键载入经典结构组:

咖啡因 · 阿司匹林 · 尼古丁 · 香草醛 · 肾上腺素 · 维生素C

每个结构都经 SMILES 解析 + 结构整理(cleanup)生成规范二维坐标,而非硬编码图片——这意味着图版本身就是一次引擎能力的可视化自证。

应用介绍页

应用介绍

内置「应用介绍」页作为离线文档:功能总览、文件格式、快捷键、手势、许可声明一屏可查,不依赖浏览器或在线帮助中心。


功能全景

结构绘制

能力 说明
键类型 单键 / 双键 / 三键 / 四键;实楔、虚楔(Hash)、交叉键(Either)、波浪键(立体未定义)
角度吸附 新键方向自动对齐 30° 网格,键长归一,避免手抖造成的歪斜骨架
幻影预览 拖拽过程中实时绘制灰色幻影键与目标原子标签,落点可知可预期
碳链工具 按拖拽方向与长度自动生成之字形碳链,段数由位移反推
环模板 3–8 元环一键放置;苯环带芳香标记;环戊二烯按交替双键生成
原子合并 原子拖拽落点进入阈值内自动合并,同步重连键、剔除自环与重复键

选择与编辑

  • 三种选择范式:单点点击(原子 / 键 / 箭头逐个切换)、矩形框选、套索(任意多边形,射线法判定内部归属)
  • 原子级拖拽:移动原子时键随动,松手触发吸附合并
  • 剪贴板:剪切 / 复制 / 粘贴,剪贴板内容为 MDL mol 文本,可跨实例、跨工具流转
  • 批量操作:删除、翻转、旋转、整理均作用于当前选区或全模型
  • 无损撤销:整模型快照式撤销 / 重做,栈深 100,任意操作组合均可回退

化学标注

标注 呈现方式
形式电荷 + / − 上标,钳位 −8 ~ +8
同位素 质量数以左侧数字前缀显示,支持增减与恢复自然丰度
自由基 标签右上角圆点标记
显式氢 按价态规则计算隐式氢数,环形分布生成,可一键增删
双键立体 E / Z 标记,渲染层同步改写双键偏移样式并输出字母标注
R-基团 点击原子或空白自动分配下一个编号(R1、R2、…)
元素指定 常用元素快捷栏 + 周期表对话框(中文名 / 相对原子质量 / 族 / 周期)+ 自由输入符号

反应式绘制

  • 正向箭头:单线 + 三角头,拖拽定义起止点与走向
  • 平衡箭头:上下对置半箭头,表示可逆平衡
  • 逆反应箭头:双头样式,用于逆合成表述

模板库

9 大分类、55 个结构模板,全部以 SMILES 定义或程序化生成,插入时经布局引擎计算坐标:

分类 内容
环 3 / 4 / 5 / 6 / 7 / 8 元正多边形环
芳环 苯、萘、蒽、菲、茚
杂环 吡啶、嘧啶、呋喃、噻吩、吡咯、咪唑、噁唑、吡喃
官能团 羧酸、酯、酰胺、醛、酮、醇、醚、胺、硝基、氰基、卤代、硫醇
氨基酸 甘氨酸、丙氨酸、丝氨酸、半胱氨酸、天冬氨酸、苯丙氨酸
稠环芳烃 芘、苝
糖类 开链葡萄糖、开链果糖、核糖、脱氧核糖
甾体 胆固醇骨架、雌二醇骨架、睾酮骨架
常用试剂 乙酸、乙醇、丙酮、乙醚、四氢呋喃、DMSO、DMF、乙腈

计算与查询

  • 分子式:Hill 系统排序(C、H 优先,其余按字母序),随编辑实时刷新
  • 摩尔质量:按元素相对原子质量累加,与分子式同源计算
  • SMILES 生成:图遍历 + 环闭合数字分配,可用于跨工具交换
  • 结构属性面板:原子数、键数、分子式、摩尔质量、SMILES 一屏汇总
  • 子结构搜索:输入 SMILES 查询式,BFS 子图匹配(逐原子锚点尝试,符号与键级联合判定),命中原子自动选中并高亮

视图与排版

能力 说明
缩放 捏合 0.2x–5x,菜单 ×1.25 / ×0.8,一键复位
平移 双指拖拽 / 空白处拖动
配色 CPK(Jmol 风格元素着色)、黑白、柔和三套方案
显示开关 芳环内切圆、原子编号、网格吸附、端点碳标签
结构整理 环正多边形化(稠环共享边对齐)、链 120° 之字排列、质心保持不动
几何变换 水平 / 垂直翻转(实楔虚楔自动互换以保立体语义)、30° 步进旋转
导出 画布快照打包导出 PNG,分辨率与画布一致

系统架构

flowchart TB
    subgraph A["视图层 · ArkUI"]
        A1["Index.ets<br/>菜单栏 / 双工具栏 / Canvas / 状态栏"]
        A2["About.ets<br/>离线文档页"]
        A3["对话框组<br/>周期表 / 模板库 / 属性 / 搜索"]
    end

    subgraph B["控制层 · EditorController"]
        B1["指针状态机<br/>down / move / up"]
        B2["命令分发<br/>工具切换 · 菜单命令"]
        B3["会话能力<br/>剪贴板 · 文件 IO · 示例图版"]
    end

    subgraph C["模型层 · 纯逻辑"]
        C1["ChemModel<br/>原子 / 键 / 环 / 箭头 / 选择 / 撤销栈"]
        C2["Renderer<br/>Canvas 2D 矢量渲染"]
        C3["NativeEngine<br/>类型化 NAPI 桥"]
    end

    subgraph D["引擎层 · C++ / libjcp_engine.so"]
        D1["环感知 · 原子合并 · 结构统计"]
        D2["MDL / SDF / SMILES 读写"]
        D3["布局整理 · 变换 · 选择 · 子结构搜索"]
    end

    A1 <-->|事件转发 / onChanged 重绘| B
    A3 --> B
    B --> C1
    B --> C3
    C1 --> C2
    C3 <-->|NAPI| D

分层职责

层 目录 职责 依赖约束
视图层 pages/ 布局、控件、对话框、事件捕获与转发 只依赖控制层,不直接改模型
控制层 pages/EditorController.ets 工具语义、指针状态机、命令路由、文件会话 依赖模型层与引擎桥
模型层 model/ 数据结构、命中测试、渲染、IO 封装 零 ArkUI 依赖(渲染器仅依赖 2D 上下文抽象)
引擎层 cpp/ 全部计算密集型算法 只依赖系统 NAPI 与 C++ 标准库

三条关键约定:

  1. 模型层无 UI 依赖 —— model/ 下除渲染器外不 import 任何 ArkUI 能力,可脱离界面做单元测试。
  2. 控制器是唯一写入口 —— 视图只发意图,模型变更全部收口于 EditorController,通过 onChanged 回调驱动重绘,避免状态散落。
  3. 算法下沉到 C++ —— 环感知、布局、匹配等热点路径在原生侧完成,ArkTS 只做 JSON 序列化与调度。

引擎层:C++ 原生核心

libjcp_engine.so 由 10 个编译单元组成,编译期无三方依赖,仅链接 libace_napi.z.so。

模块构成

文件 职责
engine.h / json_util.h 引擎内部数据结构与轻量 JSON 序列化
engine_core.cpp 模型反序列化、环感知(DFS 回边提取 + 芳香性启发式)
engine_stats.cpp 原子邻域合并、Hill 分子式、摩尔质量统计
mdl_io.cpp MDL V2000 molfile 定长列解析 / 写出,SDF 分块
smiles_parse.cpp SMILES 子集解析 + 二维坐标生成(SDG 简化版)
smiles_write.cpp 图遍历生成 SMILES,环闭合数字分配
cleanup_layout.cpp 结构整理:稠环铺排、链之字展开、质心复位
cleanup_xform.cpp 水平 / 垂直翻转、旋转、矩形框选
cleanup_lasso_search.cpp 套索多边形判定、BFS 子图匹配
templates.cpp 程序化环模板与 SMILES 模板构建
napi_init.cpp NAPI 绑定层,导出 17 个引擎函数

NAPI 接口表

函数 作用
updateRings 重算环与芳香性
mergeNear 邻近原子合并与键重连
modelStats 原子数 / 键数 / 分子式 / 摩尔质量
parseMol · writeMol MDL V2000 读写
parseSdf SDF 多分子解析
parseSmiles · writeSmiles SMILES 解析(含布局)/ 生成
layoutModel 二维坐标生成
cleanupModel 结构整理
flipModel · rotateModel 几何变换
selectRect · selectLasso 矩形 / 套索选择
searchSubstructure 子结构匹配
buildTemplateRing · buildTemplateFromSmiles 模板构建

数据契约

跨语言边界统一走 JSON 字符串:

ArkTS  ──modelJson──▶  C++  parse / compute  ──modelJson'──▶  ArkTS
  • 原子、键、箭头、选择态、ID 计数器一次性序列化,避免跨层长生命周期句柄;
  • 引擎侧无全局可变状态,接口天然幂等,便于并行与重入;
  • 契约与语言无关,同一引擎可被未来其他壳层复用。

核心算法

环感知

对分子图做 DFS,回边触发时从当前路径切片提取环(长度 3–12),以原子集合去重。芳香性采用启发式判定:全碳六元环且键级 1/2 交替,或环上存在显式芳香标记。

二维布局(SDG 简化)

首个环置于正多边形(键长 40px),其余原子按 BFS 展开,依据入射方向在 120° 扇形内均分落点;断链分子按网格偏移铺排,避免重叠。

结构整理

稠环按依赖顺序铺排:与已放环共享 ≥2 原子时,以共享边为公共边向反侧铺正多边形;独立环以自身质心为中心;链原子按 120° 之字展开;末尾整体平移,保持质心不变。

子结构匹配

对每个与查询首原子同符号的模型原子做锚点尝试,BFS 逐层比对邻居符号与键级,当匹配图覆盖查询全部原子时判定命中。

隐式氢计算

按默认价态表(B:3, C:4, N:3, O:2, P:3, S:2, F/Cl/Br/I:1)减去键级和;带电原子按规则修正价态(如 N⁺ 记 4、O⁻ 记 1)。

撤销 / 重做

变更前 checkpoint() 序列化全模型(原子、键、箭头、选择、ID 计数器),恢复时重建对象。快照式方案的代价是内存,收益是在任意操作组合下都不会出现「撤销后状态错乱」。


工程结构

ohos_JChemPaint/
├── AppScope/                      # 应用级配置与图标
├── entry/src/main/
│   ├── cpp/                       # C++ 引擎(约 1.5k 行)
│   │   ├── engine_core.cpp        #   模型序列化、环感知
│   │   ├── engine_stats.cpp       #   原子合并、分子式、摩尔质量
│   │   ├── mdl_io.cpp             #   MDL V2000 / SDF
│   │   ├── smiles_parse.cpp       #   SMILES 解析 + 布局
│   │   ├── smiles_write.cpp       #   SMILES 生成
│   │   ├── cleanup_layout.cpp     #   结构整理
│   │   ├── cleanup_xform.cpp      #   翻转 / 旋转 / 框选
│   │   ├── cleanup_lasso_search.cpp # 套索 / 子结构搜索
│   │   ├── templates.cpp          #   模板构建
│   │   ├── napi_init.cpp          #   17 个 NAPI 导出
│   │   └── types/libjcp_engine/   #   引擎类型声明
│   ├── ets/
│   │   ├── entryability/          #   UIAbility 入口
│   │   ├── model/                 #   纯逻辑模型层(约 1.4k 行)
│   │   │   ├── ChemModel.ets      #     原子 / 键 / 环 / 箭头 / 撤销栈
│   │   │   ├── Renderer.ets       #     Canvas 2D 渲染器
│   │   │   ├── MdlIO.ets          #     mol / SDF 读写封装
│   │   │   ├── SmilesIO.ets       #     SMILES 封装
│   │   │   ├── Cleanup.ets        #     整理 / 变换封装
│   │   │   ├── Templates.ets      #     55 个模板定义
│   │   │   └── NativeEngine.ets   #     类型化 NAPI 桥
│   │   └── pages/                 #   界面与控制器
│   │       ├── Index.ets          #     主界面
│   │       ├── EditorController.ets #   控制器
│   │       └── About.ets          #     介绍页
│   ├── resources/base/media/      # 工具图标(48 个 SVG/PNG)
│   └── module.json5               # deviceTypes: 2in1 / tablet
├── 图片/                          # README 截图素材
├── build-profile.json5
├── hvigorfile.ts
└── oh-package.json5

代码规模:ArkTS ≈ 3 500 行,C++ ≈ 1 500 行,全部为本项目自持代码,无三方业务依赖。


构建与打包

环境要求

项 版本
DevEco Studio 支持 API 21(HarmonyOS 6.0.1)及以上
编译 SDK 6.0.1(21)
构建工具 工程内置 hvigor 包装器
签名 调试证书 / 发布证书(上架前必备)

IDE 构建

  1. File > Open 打开工程根目录,等待依赖同步(无外部依赖,同步很快);
  2. 选择 entry 模块,执行 Build > Build Hap(s)/APP(s) > Build Hap(s);
  3. 产物:entry/build/default/outputs/default/entry-default-unsigned.hap;
  4. 签名:File > Project Structure > Signing Configs 配置证书后自动签名。

命令行构建

# 需保证 JAVA_HOME 与 DEVECO_SDK_HOME 指向本地 SDK
$env:DEVECO_SDK_HOME = "C:\Program Files\Huawei\DevEco Studio\sdk"
$env:JAVA_HOME      = "C:\Program Files\Huawei\DevEco Studio\jbr"
$env:PATH           = "$env:JAVA_HOME\bin;$env:PATH"

hvigorw.bat --mode module -p module=entry@default -p product=default assembleHap --no-daemon

打包链路依赖 Java。若报 spawn java ENOENT,说明 JAVA_HOME 未生效;若 hvigor 报未知错误,先确认 git、ohpm 在 PATH 中,再重启 IDE 使其继承新环境变量。

上架前检查清单


交互体系

键盘快捷键

按键 功能
1 / 2 / 3 单键 / 双键 / 三键工具
S / E / M 框选 / 橡皮 / 移动
C / R / T / P 链工具 / R-基团 / 模板库 / 周期表
Del 删除选中对象
Esc 取消当前选择
Ctrl+Z / Ctrl+Y 撤销 / 重做
Ctrl+C / Ctrl+V 复制 / 粘贴
Ctrl+A / Ctrl+S 全选 / 保存

指针与手势

输入 行为
鼠标左键拖拽(空白处) 新建起始原子 + 键 + 目标原子
鼠标左键单击(原子上) 键级循环升级:单 → 双 → 三 → 单
鼠标悬停 原子 / 键高亮描边,命中反馈
双指捏合 缩放画布(0.2x – 5x,以手势中心为锚点)
双指拖拽 平移画布
右键 / 橡皮工具 删除命中对象

文件格式

格式 方向 说明
MDL V2000 (.mol) 读 / 写 定长列字段;电荷码(0–7)与立体码(1/3/4/6)双向映射
SDF (.sdf) 读 按 $$$$ 分块的多分子文件,逐分子水平排列导入
SMILES (.smi / .txt) 读 取首行首个 token;支持括号原子、分支、环闭合、点断链
SMILES 写 / 输出 由当前模型生成,可用于跨工具校验
PNG (.png) 导出 画布快照打包,分辨率随画布

文件访问全部经由系统 DocumentViewPicker,由用户在对话框内逐次授权目标文件,应用自身不持有任何文件权限。打开时自动嗅探:含 $$$$ 按 SDF 处理,含 V2000 按 mol 处理,否则回落为 SMILES 解析。


渲染参数

渲染参数集中在 RendererParams,用户可切换项:

参数 取值 说明
colorScheme 0 / 1 / 2 CPK(默认)/ 黑白 / 柔和
useAromaticCircles 开 / 关 芳环内切圆表示离域;关闭则按 Kekulé 双键渲染
showAtomNumbers 开 / 关 显示原子内部 ID
snapToGrid / gridSize 开 / 关 · 像素 背景网格与吸附间距
showEndCarbons 开 / 关 端点碳是否绘制标签(默认隐藏,符合 IUPAC 惯例)
showImplicitH 开 / 关 标签上的隐式氢计数

固定几何参数(键宽 2px、多键偏移 3px、楔形半宽 5px、标签内边距 4px、字号 16px)以保证不同缩放下结构比例稳定。


性能与资源

指标 表现 手段
冷启动 直接进入可绘制状态 无框架初始化、无网络等待、单 Ability
绘制响应 指针跟随无延迟感 Canvas 单层重绘 + 增量模型更新
大数据集 多分子图版流畅 热点算法在 C++ 侧执行,JSON 批量传递
内存 与结构规模线性相关 快照式撤销栈上限 100,超限淘汰最旧快照
包体 数 MB 级 无第三方运行时,资源仅图标与本地化字符串
网络 零流量 未声明 INTERNET 权限,物理上无法联网

隐私与安全

本项目在设计阶段即按「最小权限」收敛:

  • 不申请网络权限 —— 应用无法联网,也无任何埋点、统计、远程配置;
  • 不采集个人信息 —— 无账号体系、无设备标识读取、无剪贴板以外的数据外发;
  • 不持有文件权限 —— 文件读写由用户逐次授权的系统选择器完成,遵循沙箱模型;
  • 不加载外部代码 —— 无动态脚本、无远程资源、无插件机制;
  • 本地闭环 —— 所有结构数据仅存于用户选定位置。

一句话:你的分子式只属于你。


设备与兼容性

项 值
目标设备 2in1(鸿蒙 PC)、tablet
编译 SDK HarmonyOS 6.0.1 (API 21)
架构 arm64-v8a(原生引擎随 HAP 分发)
窗口适应 跟随系统窗口尺寸,画布区自适应扩展,工具区固定
输入设备 键鼠 / 触控 / 触控笔(笔输入按触控处理)

迁移能力定位

按业界通行的三级迁移能力模型评估,本项目属 T0 原生迁移:

等级 定义 本项目
T0 原生 目标平台原生技术栈重写,全量适配窗口 / 输入 / 手势 ✅ 界面 ArkUI、引擎 C++、无跨平台运行时
T1 框架 跨平台框架编译,共享业务代码,局部适配 —
T2 兼容 容器 / 转译运行原平台二进制 —

对照项:原生技术栈 ✅ · 窗口与页面路由 ✅ · 键盘快捷键 ✅ · 鼠标悬停反馈 ✅ · 多点触控手势 ✅ · 桌面级布局 ✅ · 沙箱文件访问 ✅ · 零私有运行时 ✅


已知限制与路线图

当前限制

  • SMILES 为子集实现:不支持 @/@@ 手性标记;环闭合支持 1–9 与 %nn 两位编号;生成端不输出立体信息
  • V3000 molfile、RXN 反应文件暂未支持
  • 结构整理对高度稠密的螺环 / 桥环体系可能出现局部重叠
  • 糖类、甾体模板为开链或骨架级表示,未做椅式构象

后续方向

  1. 立体化学增强 —— @/@@ 解析、楔形键自动推导、CIP 优先级标注
  2. 格式扩展 —— V3000、RXN、InChI 标准实现、SVG 矢量导出
  3. 布局质量 —— 力导向避让、官能团模板对齐、大环体系优化
  4. 协作与文本 —— 结构与 Markdown / 富文本混排、结构式粘贴识别
  5. 工程化 —— 模型层单元测试、引擎侧基准测试、构建流水线

许可与合规

  • 本项目为独立实现:数据模型、渲染引擎、控制器、文件格式处理与全部算法代码均为原创,未复制上游源代码。
  • 上游 JChemPaint(LGPL-2.1-or-later)仅作为功能与交互设计参考;化学信息学领域概念(Hill 系统、MDL molfile 规范、SMILES 语法)属公开标准。
  • 工程不含任何第三方二进制运行时或闭源组件。

化学键绘(BondDraw)

画得出苯环,也画得动工作流。

Introduction

在鸿蒙 PC 上原生绘制和编辑二维化学结构,覆盖分子式计算、SMILES 互转、子结构搜索与 PNG 导出。项目以 C++ 原生引擎处理核心算法,ArkUI 声明式界面呈现,支持鼠标与触控双模交互,零网络权限离线运行。【此简介由AI生成】

Customize your domain