仿 SolidWorks 的浏览器端参数化 CAD,几何内核使用 OpenCASCADE (OCCT) 的 WebAssembly 构建
(opencascade.js,工业级 B-rep 精确布尔/圆角/STEP 读写,非 CSG 网格近似)。
技术栈:Vite + TypeScript + three.js + opencascade.js。
- AI 建模助手(自然语言 → 参数化特征树):一句话描述零件,自动解析成特征并建模。
例如「
60x40x10 的长方体,中心开半径 8 的通孔,四角倒圆角 R5」→ 直接生成 3 个特征的可编辑特征树。- 分层架构,两层各自独立可测:
文本 →(解析层)→ AiOp[] →(编译层)→ Feature[]。 解析层(src/ai/parse.ts)产出与内核无关的「意图」,编译层(src/ai/compile.ts)把意图落成文档特征。 两层都是纯计算、零依赖(不 import OCCT / three),所以主线程与 Node 单测都能跑。 - 解析层:归一化(全角→半角、乘号/直径符号统一、小写)→ 按标点与连接词切子句 → 最长匹配扫关键词
→ 按「数字前紧邻的角色词」给数字归类(长/宽/高/半径/直径/深/边),并单独识别
60x40x10连乘写法。 支持 5 种基本体、孔(通孔/盲孔/柱形沉头/锥形埋头/螺纹)、圆角/倒角(四角/顶面/全部棱)、抽壳、 线性/圆周阵列、弹簧(中径/线径/螺距/圈数,可左旋、带锥度)、 涡状线(起始半径/螺距/圈数/带厚/带宽,可左旋)、整体或非均匀缩放、镜像(前视/右视/上视基准面)、 拔模(侧面倾斜给定角度,可反向)、多实体编辑(移动/复制实体、删除实体、组合), 布尔三态(新建/添加/切除/相交)、相对摆放(中心/上面/右边…)、单位(mm/cm/m/in)。 读不懂的子句进issues并给出可操作提示,不静默丢弃。 - 编译层维护解析式包围盒:引擎的基本体约定是「角在原点、沿 +Z 生长」,直接拼装会得到一堆挤在第一象限的重叠体。
编译层统一给每个基本体算「居中到原点的 origin 偏移」,再按
place相对当前累计包围盒平移—— 于是「圆柱上面加一个圆柱」能自然落到位,「中心开孔」也能算准孔位。 - 长方体有解析式几何引用:12 条棱、6 个面都有闭式坐标,所以圆角/倒角/抽壳不需要先有模型就能构造引用 (「四角倒圆角」= 4 条竖直棱,「顶面抽壳」= 顶面 FaceRef);非长方体时给出明确报错而不是丢一个必失败的引用。
- 异型孔:
中心开Φ8的沉头孔,沉头直径14深3→ 柱形沉头孔;埋头孔→ 锥形埋头孔;螺纹孔→ 螺纹孔。 「沉头直径14深6」这类孔细节子句会被回填到上一个孔上(不必挤在一句话里), 且沉头/埋头直径 ≤ 孔径时会提前拦下并给出明确报错,而不是丢给内核做不出台阶。 - 弹簧:
中径20线径2螺距5圈数10的弹簧(也可用「外径+线径」,中径自动 = 外径 − 线径), 支持左旋、锥度15;轴向固定 +Z,无先验实体时轴起点在原点、有先验实体时坐在其顶面。 编译层按「外径 = 中径 + 线径、高 = 螺距×圈数 + 线径」解析式给出包围盒,供后续特征落位。 - 涡状线(涡卷弹簧 / 发条):
涡状线起始半径12螺距6圈数4厚度2.5宽度5(可加左旋), 编译层按「外轮廓半径 = 起始半径 + 螺距×圈数 + 带厚/2、法向厚 = 带宽」解析式给出包围盒。 - 变换三件套:
- 缩放:
整体放大1.5倍/缩小一半/缩放2倍/放大到3倍/沿X放大2倍(非均匀), 以当前模型包围盒中心为不动点;缩放后的长方体仍是长方体,解析式棱引用继续有效 (于是…放大2倍,四角倒圆角R2照样能跑)。 - 镜像:
沿右视基准面镜像(默认右视,可写前视/上视),以「模型自己的原点」为基准面位置 (新建模式下即世界原点,追加模式下取现有模型包围盒中心),种子是上一个特征; 包围盒按cur ∪ mirror(cur)解析式更新。 - 拔模:
侧面拔模5度/拔模角3度/拔模3度,反向;中性面取长方体底面、拔模面取 4 个侧面, 都是解析式算出来的 FaceRef(不需要先有模型)。拔模后侧面已倾斜,解析式棱引用会主动失效, 因此「先倒圆角再拔模」可行,「先拔模再倒圆角」会给出明确提示。
- 缩放:
- 多实体编辑(对标 SolidWorks「移动/复制实体 · 删除实体 · 组合」):
- 移动/复制:
整体沿Z移动50/整体向右移动20/绕Z轴旋转90度/绕X轴逆时针旋转30度/复制一份沿X偏移30。作用于全部实体;旋转轴过「当前模型包围盒中心」,顺序固定先转后移(与引擎一致)。 包围盒用 8 个角点绕轴旋转求精确解 —— 长方体绕轴向对齐的轴旋转后仍是长方体,所以这不是保守估计。 - 删除实体:
删除最后一个实体/删除第2个实体/删掉第一个实体。负数下标(-1 = 最后一个) 因为bodiesOf的展开顺序由拓扑决定,「最后一个」比「第 2 个」可靠得多。 - 组合:
把两个实体合并/把两个实体相减/把两个实体的交集取出来。主体固定取第一个实体、 工具体为其余全部(引擎侧把空toolIndices解析成「所有其它实体」)。 ⚠️ 「删除实体」与「删除孔 / 圆角」是两回事:只有带「实体」二字才走多实体逻辑,删掉孔依然是删特征 —— 这条由parseBodyOp在动作词判定之前拦截保证。
- 移动/复制:
- 几何结果与解析解逐位吻合(
tools/ai-shape-test.ts):长方体 −Φ16 通孔 = 21989.38, 四角圆角 R5 = 23785.40,顶面抽壳 3 = 28068.00,柱形沉头孔 = 23186.33,锥形埋头孔 = 23409.38, 弹簧 ≈ πr²·螺旋长;缩放 2× = 64000、镜像 = 13570.80、涡状线 = 带材多边形面积×宽度 = 7501.60、 拔模 5° =W·D·H + (W+D)·t·H² + 4/3·t²·H³= 24885.09; 多实体:绕 Z 转 90° 的 X/Y 尺寸互换(80×60 → 60×80)、两块相交 20³ 并集 = 12000 / 差集 = 4000 / 交集 = 4000、 平移后四角圆角 R2 = 7931.33 —— 都是精确 B-rep 体积,不是网格近似。 - AI 编辑现有模型(
src/ai/edit.ts):在已有特征树上继续用中文「追加 / 改尺寸 / 删除」, 一句话可含多个动作:- 追加:
上面加一个半径8高12的圆柱、中心开半径8的通孔、四角倒圆角R3; - 改尺寸:
把高改成20、把直径改成12、把长改成80、把长方体改成80x60x20; - 指定对象:
把长方体的高改成20、把孔的直径改为6、把最后一个圆角改成R3; - 删除:
删掉孔、删掉圆角、删掉最后一个倒角(删除后自动剪掉悬空引用, 如阵列的种子特征被删时阵列一并移除,不留注定求值失败的特征); - 无主语的改尺寸按「谁支持这个角色」智能选目标——
把高改成20改的是那块板而不是板上的孔; 刚追加过特征时优先改刚加的那个,于是再加一个半径8高12的圆柱,把高改成20也说得通。 - 依赖特征会跟着新尺寸走:板变厚时,它上面的通孔自动移到新顶面(仍是通孔)、圆角/倒角引用重新算到新角点、 抽壳开口面跟到新面、叠在顶面的凸台整体抬升同样的增量 —— 几何验证里这些编辑后的体积与解析解逐位吻合。
- 追加:
- 后端可插拔:解析后端收敛到
ModelProvider接口(src/ai/types.ts)。 当前内置localProvider(本地规则解析,离线可用、零依赖); 云端大模型后端只需实现同一接口、吐出同样的AiOp[],编译层与 UI 一行都不用改。 该分支的云服务环境(WorkBuddy Cloud Service 免密钥 LLM)在开通时会返回云服务资源紧张,请稍后再试,因此本版本先落地离线规则解析版,云端 provider 待环境就绪后接入。
- 分层架构,两层各自独立可测:
- 真 B-rep 内核:精确布尔、圆角/倒角、变半径圆角、抽壳、放样、扫描、旋转、阵列、镜像、异型孔向导、参考基准面、拔模、筋、弹簧、涡状线、曲面特征(拉伸曲面/旋转曲面/平面区域/放样曲面/扫描曲面/裁剪曲面/缝合曲面)、拉伸终止条件(给定深度/成形到顶点/成形到面/完全贯穿/两侧对称/起始等距/方向2)、薄壁特征(单向/两侧对称/两侧不对称,开环成带、闭环成环带,拉伸/旋转/扫描三处通用)。
- 界面与热键对标 SolidWorks:CommandManager 右键页签可显隐/折叠(双击页签折叠,记忆到本机); 热键按 SW 默认映射且支持自定义重映射(工具 → 自定义 → 键盘,捕获式录入、冲突检测、逐项/全部恢复默认, 覆盖存本机,菜单文本与分发同一份数据);Enter 重复上一命令;状态栏带模式上下文徽标 (零件 / 正在编辑:草图N / 装配体 / 工程图)。
- 多视口布局(视图 → 显示 → 视口):单视图 / 水平双视图 / 垂直双视图 / 四视图(前视/俯视/右视/等轴测), 每格独立相机与「上一视图」历史,悬停切换激活格(激活格外框高亮),标准视图/缩放至区域/拾取只作用于激活格, 布局记忆到本机。
- 曲线族特征(弹簧 / 涡状线):螺旋弹簧由「圆截面沿螺旋线逐站放样」生成(
src/sketch/curve.ts纯数学计算螺旋点列 + 平行移动标架,主线程对话框可直接复用),实测体积与解析式πr²·√((2πR)²+pitch²)·n吻合到 0.1%; 支持左/右旋、锥度、任意轴向、布尔加减。涡状线(涡卷弹簧 / 发条)用阿基米德螺线加厚成闭合带材再沿法向拉伸。 - 弹簧的两个关键工程优化(都带性能回归护栏):
- 周期性快路径:
ThruSections的代价随截面数近似平方增长(8 圈 193 截面要 ~50 s,且偶发失败要靠抽稀兜底)。 而螺旋线是周期结构——第 k 圈恰是第 0 圈绕轴旋转 2πk 并沿轴平移 k·螺距,于是只放样一圈再刚体复制, 合成复合体(本绑定下Fuse对共面端盖要 20 s+,不可用)。8 圈求值 ~50 s → 81 ms,体积精度不变。 - 截面缝合点对齐:
gp_Ax2(P, N)的 X 轴按法向自动推导,相邻截面法向略异时 X 轴会跳变、各截面的"缝"彼此错位,ThruSections为了迁就它会把本应整片的侧面切成十几片(实测单圈 24 截面 = 12 面 / 30 边)。 沿路径用平行移动标架把缝搬连续后,每圈回到 3 面(1 侧面 + 2 端盖), 网格 2.6×、质量属性 15× 提速,整个弹簧重建 567 ms → 164 ms。
- 周期性快路径:
- 扫描支持开放路径:路径草图可以是开放折线 / 线+弧串 / 纯圆弧 / 闭环(旧实现只认闭环,开放路径直接报错);
采用「平行移动标架 + 逐站截面放样」替代本绑定下失效的
MakePipe,纯圆弧路径体积精度 99.96%。 - 内核跑在 Web Worker:主线程永不
import opencascade、不持有任何TopoDS句柄; 只消费可序列化的MeshResult(ArrayBuffer零拷贝转移)。内核崩溃被隔离,界面不卡顿 (npm run verify:bundle校验该不变量)。 - 懒加载:界面先出(不依赖内核),后台流式下载 63MB WASM 并显示真实字节进度。
- 参数化特征树 + 几何命名引用:边/面引用用「中点+方向 / 质心+法向」记录,重建时在最新拓扑里就近匹配, 参数小幅变化时引用稳定。
- 表达式 / 参数化公式(参考 FreeCAD Expressions):任意数值参数都能写成以
=开头的公式, 引用全局命名参数或其他特征的属性,改一处、整棵特征树自动重算。- 完整表达式引擎(
src/doc/expr.ts,纯计算、零依赖):tokenize → Pratt 解析 → 求值。 支持+ - * / % ^(^右结合、优先级高于一元负号:-2^2 = -4)、比较与逻辑(and/or/not,也接受&&/||)、sin/cos/atan2/sqrt/pow/min/max/avg/round/...等函数、pi/e常量。 - 引用:裸标识符 → 全局参数;
特征名.属性键→ 其他特征的数值参数(如拉伸1.distance);特征名.属性键[下标]→ 数组元素(如长方体1.dims[0])。 - 单位后缀(紧贴数字):
10mm/2in/1rad/30deg/45°,长度归一到 mm、角度归一到度。 - 依赖图 + 环检测:参数之间、参数与特征属性之间可以任意串联,惰性求值并检测循环引用; 出错时保留字面值并回写到特征(特征树标红),不会让模型崩掉。
- 主线程求值:
App.resolvedFeatures()在把特征发给内核前把表达式替换成具体数值, 因此内核协议与几何代码完全不需要知道表达式的存在;装配体组件也同样先解析。 - UI:参数表对话框(增删参数、实时预览、点击插入可用引用)与表达式对话框(给当前特征的某个参数绑定公式); 属性面板显示每个特征的表达式绑定与计算值。
- 完整表达式引擎(
- 草图高级曲线族(对标 FreeCAD Sketcher 的曲线工具组):在椭圆之外补齐抛物线、双曲线、B 样条:
- 抛物线:顶点 + 焦距
focal+ 对称轴方向rot+ 参数范围[t0, t1]。 参数方程X(t) = t²/(4·focal)、Y(t) = t—— 与 OCCTGeom_Parabola::D0(U)完全一致, 所以实体的参数范围可以原样交给Geom_TrimmedCurve,预览折线与实际边严格同形。 由「顶点 → 焦点 → 开口范围」三次点击生成;抛物线 + 弦围成的面积解析解为T³/(3f)(实测精确吻合)。 - 双曲线:中心 + 实半轴
a+ 虚半轴b+ 实轴方向rot,参数方程X = a·cosh t、Y = b·sinh t(只取右支)。 由「中心 → 实轴端点 → 虚轴距离」三次点击生成;与弦围成的面积解析解为a·b·(sinh(2T)/2 − T)(实测精确吻合)。 - B 样条:通过点(插值)表示 —— 用户连续点击一串点,曲线严格穿过每一个点。
控制点由
src/sketch/curve.ts的全局曲线插值(Piegl & Tiller A9.1:弦长参数化 → 平均技术节点向量 → 解N·P = Q线性方程组)算出,再用Geom_BSplineCurve_1(poles, knots, mults, degree)精确构造边。 点数不足自动降次(2 个点 → 一次直线);末点落回起点附近自动闭合,闭合的 B 样条可直接拉伸成体。 实测:16 点近圆环 B 样条拉伸体积与π·R²·h相差 −0.003%(面是精确 B 样条,不是多边形近似)。 - 用途:三种曲线都是开放曲线实体(闭合 B 样条除外),因此不像直线/圆弧那样参与草图闭环搜索,
但可以作为扫描路径(
sketchPathPolylines已纳入),也可以被「镜像实体」镜像 (抛物线/双曲线关于轴翻转rot → 2θ轴 − θ,B 样条逐个镜像通过点)。 - Handle 陷阱(踩坑记录):
BRepBuilderAPI_MakeEdge_24与Geom_TrimmedCurve要的是Handle_Geom_Curve,而Geom_Parabola_2(...)/GeomAPI_PointsToBSpline_2(...).Curve()返回的是具体类型的 handle,embind 不会自动向上转型。必须.get()取裸对象再用new Handle_Geom_Curve_2(obj)重新包一层,否则报Expected null or instance of Handle_Geom_Curve, got an instance of Handle_Geom_XXX。
- 抛物线:顶点 + 焦距
- 约束草图:LM 最小二乘求解器,18 种约束 + 自由度分析、过定义检测。
- 草图实体族(对标 SolidWorks):在直线 / 矩形 / 圆 / 圆弧 / 多边形之外新增椭圆、直槽口、点,并提供镜像实体:
- 椭圆用精确 OCCT 边(
BRepBuilderAPI_MakeEdge_12(gp_Elips);_8只接受gp_Circ,传gp_Elips会报错), 长 / 短半轴rx/ry与旋转角rot都参与约束求解,可被「半径 / 短半轴」尺寸驱动。 - 直槽口由「两圆心 + 半径」经纯函数
slotTemplate推出 6 个点 + 2 条直线 + 2 个半圆,切点天然相切; 整体顺时针绕行 ⇒ 两个端帽ccw均为 false(右端帽若写成 true 会向内鼓、面积退化成2r·L矩形)。面积恒为2rL + πr²。 - 点是独立参考点,不参与闭环搜索(不会影响轮廓),可作异型孔向导的孔心或用于标注尺寸。
- 镜像实体以选中的一条直线为轴、就地镜像其余选中实体,并翻转手性:圆弧
ccw取反、椭圆rot → 2θ轴 − θ。
- 椭圆用精确 OCCT 边(
- 异型孔向导:在任意平面放置简单孔/柱形沉头/锥形沉头/螺纹孔(通孔或盲孔), 孔心支持面内 U/V 偏移或按草图点定位,可被阵列复用。
- 拔模 / 筋:拔模用
BRepOffsetAPI_DraftAngle(中性面 + 角度 + 反向);筋把草图开口轮廓在面内加厚成薄壁再沿法向拉伸融合。 - 实体编辑族(缩放 / 分割 / 移动复制 / 删除实体 / 组合 / 变半径圆角)——完整的多实体工作流:
- 缩放支持非均匀(各轴独立系数)。用
BRepBuilderAPI_GTransform+gp_GTrsf——gp_Trsf在 OCCT 里 只允许等比缩放,非均匀会直接抛异常。指定中心作为不动点(平移列取center − S·center)。 - 分割用
BRepAlgoAPI_Splitter+ 按包围盒生成的有限矩形面作工具;可选保留法向正侧/负侧/两侧, 单侧保留按各子实体的质心投影符号筛选;平面没真正切到实体时显式报错,杜绝"静默成功"。 - 移动/复制实体:先绕给定轴旋转、再平移(顺序固定「先转后移」);可按实体勾选作用对象,
勾选「复制」时保留原件并按 i 倍变换生成多份副本。多实体状态统一用「实体集合」表示
(内核
bodiesOf把单实体/复合体都展开成实体列表),因此移动、删除、组合可以互相衔接。 - 删除实体:按下标移除实体;全删、空选、下标越界均显式报错(不允许把模型删空)。
- 组合:多实体布尔(添加/减除/共同),未被选中的实体原样保留。
差集/交集可能把主体整体吃掉——OCCT 此时返回的是空复合体(
IsNull()仍为 false), 内核会额外检查实体数,避免静默产出一个"空模型"。 - 变半径圆角(SolidWorks 的「变量大小圆角」)沿边从
r1线性过渡到r2,Add_3(r1, r2, edge)变半径 /Add_2(r, edge)定半径;测试用「体积必须严格介于两个定半径结果之间」作护栏。
- 缩放支持非均匀(各轴独立系数)。用
- 布尔操作三态:
添加(并集融合)、切除(差集)、新建实体。新建实体保留已有实体、把新实体作为独立实体追加(compoundOf,不做布尔融合)——与 SolidWorks 一致: 多实体零件里「新建」产生新实体而非并入。因此两个相交的「新建实体」仍是 2 个实体、体积相加; 要合并必须显式选「添加」。(旧实现把new误当fuse,相交时会被静默合并成一个,与 UI 标签「新建实体」不符。) - 参考基准面:基于标准基准面或选中面偏移创建,可直接在其上绘制草图。
- 装配 / 工程图:配合求解、干涉检查、爆炸视图,配合参考支持视口可视化拾取(点选组件面 / 棱边 / 圆边 / 圆柱面,带 3D 标记与智能默认「同心」,两轴参考自动选同心); 三视图自动布局、逐点 HLR 隐藏线、交互式尺寸标注、一键自动标注(总宽高 / 各直线边)、尺寸公差(±/上下偏差)与形位公差特征控制框(11 种符号)、 孔位自动标注(中心定位 + 直径)、明细表(BOM,装配态按同零件自动合并数量)。
- 构建优化:Brotli 预压缩把 63MB WASM 的传输体积降到约 9MB(
npm run build自动执行); 提供「内核精简构建」流水线(扫描所用 OCCT 符号 → 最小头文件清单,供自定义构建,见tools/kernel-build/)。 - 桌面版(Windows 免安装 exe):
npm run desktop:build一条命令装配出Solid2.exe(约 200 MB,内含 WASM 内核), 双击即用、不装 Node、不装浏览器、不联网。全程零外部下载 —— 手工装配 Electron 运行时、 纯 Python 生成图标、纯标准库改写 PE 资源段写入 exe 图标。详见下文「桌面版」。
只关心「跑起来看效果」的话,三步就够:
npm install # 已用项目级 .npmrc 直连 npmmirror 并关掉失效代理
npm run dev # 打开 http://127.0.0.1:5173第一次加载会拉取 62 MB 的 OCCT WebAssembly 内核(Worker 线程里加载,主线程不阻塞), 随后就能在图形区里建模。想快速体验建模能力,点 Ribbon 的 AI 建模 页签,输入一句话:
60x40x10 的长方体,中心开Φ8的沉头孔,沉头直径14深3,四角倒圆角 R4
想要一个双击即用的绿色版可执行文件(不装 Node、不装浏览器、不联网):
npm run desktop:build # → release/Solid2-win32-x64/Solid2.exesrc/
core/ 几何内核封装(仅 Worker 侧 import OCCT)
occt.ts 枚举/变换/去重等薄封装
solid.ts 基本体/布尔/圆角倒角抽壳/阵列镜像/度量
mesh.ts 三角剖分、边离散化、特征边筛选
io.ts STEP 导入导出(仅 Worker)
meshexport.ts STL/OBJ 纯网格导出 + 浏览器文件助手(主线程可用)
kernel.worker.ts Worker 入口,独占 OCCT,RPC 处理
kernel.ts 主线程客户端(KernelClient 门面)
kernel-types.ts 主线程↔Worker 序列化协议(不 import oc/three)
doc/ 文档模型与求值
model.ts 纯数据 CadDocument(无 OCCT,主线程可用)
expr.ts 表达式引擎(tokenize/Pratt/求值/依赖解析,纯计算,主线程可用)
engine.ts 特征树求值引擎(仅 Worker)
topo.ts 面/边元数据提取
types.ts 特征类型定义
sketch/ 约束草图
types.ts 草图数据模型
solver.ts LM 约束求解器
toShape.ts 草图 → 封闭区域 → 面
rib.ts 筋:开口轮廓 → 面内加厚封闭多边形(纯 2D 计算)
curve.ts 曲线族纯数学:螺旋/涡状线点列、草图路径采样、平行移动标架与截面放置
以及 **B 样条全局曲线插值**(弦长参数化 + 平均节点向量 + 高斯消元求控制点、
De Boor 求值、采样折线)
(**不 import OCCT**,主线程对话框与 Worker 引擎共用)
ai/ AI 建模助手(纯计算,不 import OCCT / three)
types.ts AiOp 中间表示 + ModelProvider 接口(后端可插拔)
parse.ts 本地规则解析:归一化 → 切子句 → 关键词最长匹配 → 角色化取数(离线可用)
compile.ts AiOp[] → Feature[]:解析式包围盒定位 + 长方体棱/面引用生成
edit.ts 编辑现有模型:追加 / 改尺寸 / 删特征 + 依赖特征跟随 + 悬空引用剪枝
index.ts 门面:interpret(text) → { plan, compiled }、editModel(text, ctx) → 新特征数组
view/ three.js 视图(viewport / shapeview / sketchview / materials)
app/ 应用控制器(app / sketchMode / assembly / drawing)
ui/ UI 外壳(ribbon / menubar / featuretree / propertypanel / dialogs / icons ...)
styles/ 主题样式
tools/ 测试与一次性脚本
kernel-build/ 内核精简构建:符号扫描 → 最小头文件清单(见其 README)
tour/scripts.mjs 全功能巡演脚本库(每个脚本一套参数 + 专属场景,运行器默认轮换)
desktop-verify.mjs 桌面版端到端验证:启动 exe → CDP → 内核就绪 → AI 建模 → 体积对拍
precompress.mjs 构建产物 brotli/gzip 预压缩
verify-bundle.mjs 校验"主线程不引用 OCCT"的架构不变量
desktop/ 桌面版(Electron)
main.cjs 主进程 + 只监听 127.0.0.1 随机端口的静态服务器
build-desktop.mjs 手工装配 → release/Solid2-win32-x64/Solid2.exe + zip
make-icon.py 纯 Python 光栅化 → icon.png / icon.ico(6 个尺寸,零依赖)
patch-exe-icon.py 纯标准库改写 PE 资源段,把 ICO 写进 exe(替代 rcedit)
npm install # 已用项目级 .npmrc 直连 npmmirror 并关掉失效代理;如需自定义:npm install --userconfig=.npmrc.noproxy
npm run dev # 开发服务器 http://127.0.0.1:5173
npm run build # 生产构建 + 预压缩 + 隔离校验
npm run preview # 预览构建产物
npm run kernel:scan # 扫描所用 OCCT 符号 → 精简构建头文件清单
npm run desktop:build # 打包 Windows 免安装 exe(见下文「桌面版」)
npm run demo:video # 自动巡演全部功能,录制为 MP4(需本机 Edge/Chrome)
npm run demo:list # 列出全部巡演脚本
npm run demo:probe # 只跑各脚本的「专属场景」并断言(不录视频,约 35s)npm run typecheck # tsc --noEmit
npm test # 依次跑全部测试套件
npm run test:sketch # 草图求解器 + 椭圆/槽口/镜像 + 抛物线/双曲线/B 样条纯数学(纯计算,快)
npm run test:expr # 表达式引擎:词法/优先级/函数/单位/引用/环检测(纯计算,快)
npm run test:ai # AI 建模助手:归一化/关键词/角色化取数/异型孔/弹簧/缩放/镜像/涡状线/拔模/多实体编辑/编译定位/编辑指令/错误边界(纯计算,快)
npm run test:drawing # 工程图 / 尺寸标注 / 自动标注(纯计算)
npm run test:assembly # 装配配合求解(纯 three.js)
npm run test:doc # 文档/特征引擎(需 OCCT)
npm run test:features # 异型孔 / 基准面 / 拔模 / 筋(需 OCCT)
npm run test:curve # 曲线族:螺旋/涡状线数学、平行移动标架、弹簧/涡状线/扫描实体(需 OCCT)
npm run test:edit # 实体编辑:缩放 / 分割 / 移动复制 / 删除实体 / 组合 / 新建实体 / 变半径圆角 / 负数下标 / 空工具体(需 OCCT)
npm run test:shape # 草图 → 实体:矩形/孔/圆弧 + 椭圆/槽口/草图点 + 闭合 B 样条 / 抛物线 / 双曲线(需 OCCT)
npm run test:ai-shape # AI 建模助手几何:自然语言 → 特征 → 真几何,体积与解析解对拍(需 OCCT)
npm run test:kernel # 几何内核封装(需 OCCT)
npm run desktop:verify # 桌面版端到端:启动 exe → CDP → 内核就绪 → AI 建模 → 体积对拍(18 项)测试经 tools/run-ts.mjs 用 Vite SSR 把 TS 入口打成 ESM 后交 Node 运行;
Node 侧的 OCCT 加载器在 tools/oc-loader.mjs。
上面所有套件都在 Node 里跑(用同一个 OCCT wasm),覆盖内核与引擎,但覆盖不到 UI 接线、Worker 启动、WASM 实际加载、渲染管线。补一层真实浏览器测试:
npm run dev # 另开一个终端先起开发服务器
npm run test:browser # 用 Edge/Chrome 的 CDP 驱动真实浏览器它零依赖(用 Node 内置 WebSocket 直连 CDP),做九件事:
- UI 接线(不依赖内核):校验 Ribbon 页签、「AI」页签(AI 建模/AI 编辑/语法帮助)、「实体编辑」分组、 草图页签「实体」组(椭圆/槽口/点)、「曲线」组(抛物线/双曲线/B 样条)与「编辑」组(镜像实体), 特征页签「参数化」组(参数表/表达式),以及缩放/分割等按钮的提示文案与图标 SVG 非空; 并实际点开「参数表」对话框,校验表达式表格的列头、添加按钮与用法说明都渲染出来了。
- 端到端(实体编辑)(真实 Worker 内核):
长方体 20³ → 缩放 2× → 变半径圆角 2→8 → 分割 → 组合(添加)→ 移动/复制, 断言体积守恒(8000 → 64000 → 63753.28 → 63753.28 → 63753.28 → 127506.56)、 面数(6→6→7→13→12→24)、实体数(1→1→1→2→1→2)。 - 端到端(草图实体族 / 多实体语义)(真实 Worker 内核):
椭圆 a=10 b=5 → 拉伸 10(体积 π·a·b·h)、直槽口 2rL+πr² → 拉伸 6、矩形 + φ12 孔 + 定位点 → 拉伸 8、镜像实体的圆心/半径校验, 以及「新建实体」相交时保持 2 个独立实体、体积相加(16000)。 - 端到端(表达式驱动几何)(真实 Worker 内核):全局参数
板厚=8驱动拉伸深度(体积 12000)→ 改板厚=20几何自动联动(30000)→ 复合公式板厚*2 + sqrt(16)(66000)→ 故意写错引用时特征被标红且退回字面值 → 参数表/表达式随文档序列化往返 → 「表达式」对话框可打开。 - 端到端(草图高级曲线族)(真实 Worker 内核):
16 点近圆环 B 样条(闭合)→ 拉伸 10(体积 ≈ π·20²·10,面是精确 B 样条)→ 抛物线/双曲线(开放曲线)不产生实体且不报错 →圆 r=4 沿 4 点 B 样条路径扫描成体。 - 端到端(AI 建模助手)(真实 Worker 内核):三句话直接建模并核对体积
(
长方体=24000、长方体+通孔+圆角=21774.78、板+倒角+抽壳< 实心); 再走一遍真实 UI:点「AI」页签 → 点「AI 建模」→ 弹窗出现 → 输入框敲入描述并实时解析出摘要 → 点「应用」→ 弹窗关闭且建出圆柱(体积 ≈ π·10²·20)。 - 端到端(AI 编辑现有模型)(真实 Worker 内核):在长方体上依次
上面加圆柱(26412.74)→把高改成20(50412.74,板变厚 + 凸台抬升)→删掉圆柱(48000); 再走一遍真实 UI:点「AI」页签 → 点「AI 编辑」→ 直接进入编辑模式并显示当前特征数 → 敲入中心开半径8的通孔并实时解析出「新增」改动 → 点「应用」→ 孔生效(43978.76)。 - 端到端(AI 异型孔 / 弹簧)(真实 Worker 内核):
长方体 + Φ8通孔 + 沉头Φ14深3(23186.33)、长方体 + Φ8通孔 + 埋头Φ12深3(23409.38)、中径20线径2螺距5圈数8的弹簧(≈ π·r²·螺旋长,1% 内); 并验证编辑模式里中心开Φ8的沉头孔,沉头直径14深3的孔细节子句回填能落到特征上。 - 端到端(AI 变换/曲线 · 多实体编辑)(真实 Worker 内核):
整体放大2倍(64000)、板 + 右侧圆柱 + 右视镜像(13570.80)、涡状线(7501.60)、侧面拔模5度(24885.09); 多实体:复制一份沿X偏移10 + 合并(1 实体 / 12000)、+ 相减(4000)、复制一份沿X偏移30 + 删除最后一个实体(1 实体 / 8000)、整体沿Z移动50(体积不变 96000); 以及编辑态整体沿X移动100,四角倒圆角R2(3 特征 / 7931.33,包围盒落在 X ∈ [90,110])。
驱动入口是开发态调试句柄 window.__solid2(见 src/main.ts,仅 import.meta.env.DEV 下注入,
生产构建会被 Vite 静态消除,不进产物)。环境变量:SMOKE_TIMEOUT_MS(等内核就绪上限)、
SMOKE_HEADFUL=1(显示窗口)。
在真实浏览器里把 Solid2 的全部功能逐项跑一遍,同时逐帧录屏、编码为 MP4:
npm run dev # 另开一个终端先起开发服务器(或让脚本自动拉起)
npm run demo:video # 自动巡演 + 录制,输出 videos/solid2-all-features.mp4录像默认落在
videos/(已 gitignore),不放dist/——npm run build会清空dist/,放那儿会被误删。
脚本 tools/all-features-demo.mjs 零依赖(Node 内置 WebSocket 直连 CDP)。
每次巡演使用不同的脚本。 巡演内容全部来自脚本库 tools/tour/scripts.mjs,
默认轮换:每次运行自动挑一个与上次不同的脚本(上次选择记在 tools/tour/.last-script.json),
于是每次录出来的视频都不一样。
npm run demo:list # 列出全部脚本
node tools/all-features-demo.mjs --script flange # 指定脚本
node tools/all-features-demo.mjs --probe # 只跑各脚本的专属场景并断言(约 35s,不录视频)
node tools/all-features-demo.mjs --speed 3 # 巡演加速 3×(缩短每步停留,视频更紧凑)
node tools/all-features-demo.mjs --music off # 不合成背景音乐(默认自动合成)
node tools/all-features-demo.mjs --music my.wav # 用指定 WAV 作为背景音乐内置 5 个脚本,各自描述「这次演什么」——AI 提示词(建模 / 编辑 / 变换 / 多实体各一句)、 底板尺寸、工程特征参数、基本体清单、曲线/阵列/缩放/表达式参数,外加 1–2 个专属场景(脚本独有的建模片段):
| id | 主题 | 专属场景 |
|---|---|---|
classic |
经典全功能 | 组合体(底板 + 4 角凸台 + 中心通孔) |
flange |
法兰 · 端盖 | 法兰盘(Φ140 + 8×沉头孔)、端盖(中心阶梯孔 + 6×均布通孔) |
bracket |
支架 · 筋板 | L 形支架、加强筋板 |
spring |
弹簧 · 曲线 | 双弹簧对(右旋 + 左旋)、锥度弹簧 + 座圈 |
fighter |
喷气战斗机 | 机身(边廓旋转)、后掠主翼、尾翼 + 立尾、座舱盖(4 个渐进场景) |
新脚本只需在 tools/tour/scripts.mjs 里加一个对象、写自己关心的字段(其余回落到「经典」值)即可。
已录好的成品:videos/solid2-tour-<id>.mp4(各脚本一份,各约 3.5 分钟 / 9 MB)、
videos/fighter.mp4(喷气战斗机,3× 加速 + 背景音乐),
videos/solid2-all-features.mp4 是最近一次运行(默认轮换)的输出。
22 个固定步骤覆盖:界面总览 → AI 建模助手(自然语言建模 + 编辑 + 变换/曲线 + 多实体编辑)→ 草图实体族(真实鼠标点击绘制矩形/圆/椭圆/槽口/圆弧/点/多边形 + 镜像)→ 草图曲线族(抛物线 / 双曲线 / B 样条 + 闭合 B 样条拉伸成体)→ 约束草图 → 拉伸/切除(SW 式:起始等距 / 给定深度 / 成形到顶点 / 成形到面 / 完全贯穿 / 两侧对称 / 方向2 / 薄壁特征)→ 工程特征(圆角/倒角/变半径圆角/抽壳/拔模/筋/异型孔)→ 五种基本体 → 旋转/放样/扫描(含薄壁:开环成筒、闭环成管)→ 阵列(线性/圆周/镜像)→ 实体编辑族(缩放/分割/移动复制/组合/删除/新建实体)→ 参数化(表达式/参数表)→ 曲线(弹簧/涡状线)→ 基准面 → 装配体(插入/配合/干涉检查/爆炸)→ 工程图(三视图/自动标注/孔位/明细表/PNG 导出)→ 评估(质量属性/测量/统计)→ 曲面(拉伸曲面/旋转曲面/平面区域/放样曲面/扫描曲面/裁剪曲面/缝合曲面,均为不参与实体布尔的壳) → 视图(显示样式/方向/基准面/网格)→ 文件导出(STEP/STL/OBJ)→ 撤销/重做/重建。 随后是脚本的专属场景,最后收尾环绕与汇总。
每个功能组结束时切换到等轴测视角并自动取景(isoView()),画面更生动。录制流程:逐帧
Page.captureScreenshot(jpeg) 存帧并记录真实时间戳,编码时用 ffmpeg concat demuxer 写入每帧实际
duration 保持节奏,再统一为 CFR 30fps H.264。编码后自动混入背景音乐——tools/tour/gen_music.py
用 numpy 合成一段温暖舒缓的 C 大调氛围乐(Pad + 马林巴琶音 + 贝斯 + 钟琴 + 空间延迟),长度取
「视频时长 + 2s」,ffmpeg -c:v copy -c:a aac -shortest 只重编音频、不损失画质。
环境变量:DEMO_W/DEMO_H(默认 1600×900)、DEMO_SPEED(速度倍率,同 --speed)、
DEMO_MUSIC(auto/off/<wav>,同 --music)、DEMO_KEEP_FRAMES=1(保留抽帧)、
FFMPEG(自定义 ffmpeg 路径,默认用 imageio-ffmpeg 内置的 7.1)、PYTHON(合成音乐用,默认探测项目 venv)。
写专属场景时的几何约定:
__D.box/prim的origin是角点(不是中心),圆柱/圆锥/圆环底面在origin沿 +Z 生长,只有球是中心落位;__D.cirPattern(...)必须传seedFeatureId(见下文已知限制)。
把整个 Solid2 打包成一个双击即用的绿色版 exe,不装 Node、不装浏览器、不需要联网:
npm run desktop:build # 装配 → release/Solid2-win32-x64/Solid2.exe + Solid2-win32-x64.zip
npm run desktop:verify # 真的启动 exe,走 CDP 端到端验证(19 项断言)
npm run desktop # 开发态直接跑 Electron 壳(复用 dist/)产物:
| 路径 | 说明 |
|---|---|
release/Solid2-win32-x64/ |
绿色目录,整个文件夹可拷到 U 盘 / 别的电脑 |
release/Solid2-win32-x64/Solid2.exe |
主程序,约 200 MB(内含 62 MB WASM 内核) |
release/Solid2-win32-x64.zip |
分发用压缩包,约 133 MB |
release/使用说明.txt |
给最终用户看的说明 |
为什么要自己装配,而不用 electron-builder?
electron-builder 的 NSIS / winCodeSign 组件要从 GitHub Releases 下载,本机网络拿不到。
desktop/build-desktop.mjs 改走等价的手工装配(electron-packager 也是这么干的):
拿现成的 Electron 运行时 → 删掉 default_app.asar → 精简语言包(只留中英文,省 50 个 .pak)→
把 resources/app/{main.cjs, icon.png, package.json, dist/} 塞进去 → electron.exe 改名 —— 全程零外部下载。
为什么主进程要自带一个静态服务器?
Solid2 是「ES module + Web Worker + 62 MB WASM」的纯前端应用,用 file:// 直接打开会踩三个坑:
① ES module 的 import 被 CORS 拦;② Worker 无法从 file:// 加载;③ fetch() 读 wasm 被判跨源。
所以 desktop/main.cjs 在只监听 127.0.0.1 随机端口的静态服务器上服务 dist/,
窗口 loadURL 到它 —— 等价于 npm run preview,但用户看不到任何服务器进程。
安全基线:contextIsolation: true / nodeIntegration: false / sandbox: true,外部链接一律交给系统浏览器。
exe 图标怎么来的? 同样是零外部依赖:desktop/make-icon.py 用纯 Python 光栅化
(4× 超采样 + 盒式降采样,等轴测线框立方体)产出 256×256 PNG 与 6 尺寸 ICO;
desktop/patch-exe-icon.py 用标准库 struct 直接改写 PE 资源段 —— 解析出原有资源树
(版本信息 / 应用清单…)完整保留,只替换 RT_ICON(3) + RT_GROUP_ICON(14),
重建后放进一个新追加的 .rsrc2 节并改数据目录指向它,最后重算 PE 校验和。
(先写临时文件、校验通过才原子替换,失败不会留下坏 exe。)
本机验证环境有两个坑,只在
tools/desktop-verify.mjs里规避,不影响应用本体: shell 里设了ELECTRON_RUN_AS_NODE=1(Electron 会退化成纯 Node,直接退出), 以及 GPU 进程起不来(加--disable-gpu走软件光栅)。真实桌面上用户双击即可。
- Headless 浏览器加载 65MB WASM 的内存占用偏高,某些容器/CI 环境可能 OOM;
实测本机 headless Edge 可正常加载并跑通完整端到端(
npm run test:browser,约 7s)。 - 形位公差的锚点固定取视图中心(引线指向视图中央),暂不支持自由点选位置;尺寸公差通过「尺寸公差」命令设置。
- 筋仅支持单条连续开口轮廓;闭环草图需改用拉伸。
- 扫描的截面过渡是逐段直纹面:尖锐折角处会切掉一小角(L 形路径约 −2.9%、线+弧约 −8.4%);
直线 / 纯圆弧 / 闭环路径误差 ≤ 3%。根因是本绑定下
BRepOffsetAPI_MakePipe/MakePipeShell对多段弯曲路径算出退化结果(圆弧折线体积直接变 0),只能改用放样近似。 BRepOffsetAPI_ThruSections.Build()在某些截面数量下偶发抛异常(圆截面链 14/16/24/30 段每圈必失败, 10/12/20/36 段正常),疑为 wasm 侧不稳定;loftWires已内置「平滑 → 直纹 → 逐级抽稀」降级重试。- 圆周阵列必须指定种子特征:
circularPattern不给seedFeatureId时,引擎会把「整个当前模型」 绕轴复制 N 份再Fuse——对轴对称的圆盘等于什么都没做,还会把已经开好的孔盖回去(各份互相填补 对方的孔)。__D.cirPattern(...)因此把种子作为第 6 个参数,专属场景与巡演第 11 步都显式传入。 FaceMeta.normal是三角网格法向的面积加权平均,对闭合曲面(整圆柱侧面 / 球面 / 圆环面)会互相 抵消而退化;此时回落到该面最大三角形的法向(实测数据,圆柱侧面必然是径向)。因此「按normal[2] > 0.9挑顶面」还必须同时限定surfaceType === 'plane',否则2h > r的圆柱会选中侧面 (面积2πrh > πr²)。- 弹簧走周期性快路径时是复合体(每圈一个实体),状态栏会显示「N 实体」而非 1。
本绑定下
Fuse融合共面端盖要 20 s+(逐级降级与 Glue 模式都试过),远不如复合体的 0 ms, 且复合体的三角化、双向布尔、特征链续算均已实测正常。锥度 / 螺距 ≤ 线径 / 中径 ≤ 线径 时退回整条放样(单实体)。 - 弹簧穿过实体表面的布尔(如螺旋槽)代价偏高:8 圈切圆柱约 12.5 s(对齐缝合点前为 17.5 s), 瓶颈在布尔本身而非弹簧求值(求值仅 ~0.1 s)。
- 内核精简构建需在有 Docker 的环境执行(
tools/kernel-build/README.md);本仓库默认用全量opencascade.js+ Brotli 预压缩。 - OCCT 报错信息会被翻译:本 wasm 绑定未链接异常解栈符号,某些算子(典型是圆角半径过大)在
Build()内直接抛 C++ 异常时,JS 侧只能看到一句无意义的___cxa_is_pointer_type is not defined。 内核层的guardOc会把它统一翻译成「几何条件不满足(尺寸/半径过大,或与相邻面冲突)」这类可读中文。 实测这种异常不会破坏 wasm 运行时状态(抛错后同实例内新建文档求值、原文档改参数重算全部正常),故无需熔断。 BRepAlgoAPI_Defeaturing(去除特征)在本绑定下不可用:方法名是AddFaceToRemove而非AddFace, 且即便改对调用后IsDone()仍恒为false,无法得到结果,故该功能未实现。- 变半径圆角的半径约束:
r1/r2必须小于相邻面的可用宽度;超过时 OCCT 会抛异常(已被翻译为可读中文), 特征树会标红该特征但保留上一个成功结果,不会让整条链变成空。 - 「组合」的面数可能多于预期:OCCT 的
Fuse不总是把共面片合并成一片——例如「分割成两块再组合(添加)」 体积能精确复原(63753.28),但面数会是 12 而非原来的 7。几何与度量正确,仅拓扑分片更细,不影响后续特征。 - 区域面积是内接多边形近似:草图闭环的
region.area用采样多边形计算(用于嵌套/内外判定), 对含圆弧 / 椭圆的轮廓会略小于精确值(圆约 −0.6%、椭圆约 −0.03%、槽口约 −0.1%)。 但由该闭环构造出的 OCCT 面是精确的(椭圆面积恰为 π·a·b、槽口恰为 2rL + πr²), 因此拉伸/旋转的体积精确;仅当直接读region.area时需容忍这一近似。 - 抛物线 / 双曲线是开放曲线实体,不参与草图闭环搜索:它们不像直线 / 圆弧那样带
SkPoint端点 (端点位置由参数范围t0/t1派生),因此无法与其它实体连接成环。用途是扫描路径与参考几何; 要做出实体,请把曲线端点用直线手工连接(或在几何层用advancedCurveEdge+ 弦自建线框)。 - 桌面版 exe 未做代码签名:Electron 运行时自带的签名会因改写 PE 资源段而失效,Windows SmartScreen 首次运行可能提示「未知发布者」——点「更多信息 → 仍要运行」即可。要消除提示需购买代码签名证书。
- 桌面版打包产物不入库:
release/已 gitignore(解压后 345 MB)。npm run desktop:build可随时重建, 前提是node_modules/electron/dist存在(首次需npm install)。 闭合 B 样条(末点落回起点附近自动闭合)则自成一环,可以直接拉伸成体。 - B 样条闭合处的切向:闭合曲线用「末尾补回起点」实现(首尾点严格重合), 在起点处的切向由相邻两段决定,不是周期 B 样条那样天然 p 阶连续; 对光滑点集(如近圆环)差异极小,但极端折返的点集会看到轻微折角。
- AI 助手当前是本地规则解析,不是大模型:本分支的云服务环境(免密钥 LLM)开通时返回
「云服务资源紧张,请稍后再试」,因此先落地离线规则解析版——只认既定句式与关键词,
泛化能力弱于大模型(换个说法可能就解析不出来,此时会在预览区给出「没有识别出实体类型」提示)。
解析后端已抽象成
ModelProvider接口,云环境就绪后新增一个 provider 即可切换到云端大模型, 编译层与 UI 无需改动。 - AI 的圆角 / 倒角 / 抽壳 / 拔模只作用于长方体:这几类特征需要几何引用(棱 / 面), 只有长方体的 12 棱 6 面有闭式坐标可以直接算出来。作用在其他形状上时会明确报错, 而不是丢一个必然失败的引用给引擎。孔则不受此限制(用面中心构造孔平面即可)。 另外拔模会让侧面倾斜,所以拔模之后的解析式棱引用会主动失效——「先倒圆角再拔模」可行, 「先拔模再倒圆角」会给出提示(需要的话把顺序调过来)。
- AI 新建模式会替换当前文档(
生成模型先压一次撤销栈,可Ctrl+Z撤回); 要在已有模型上继续改,请切到「编辑当前模型」(或点 Ribbon 的「AI 编辑」)。 - AI 的线性阵列方向默认为 X 轴(可写「沿Y」「沿Z」),圆周阵列固定绕 Z 轴、整圈 360°。
- AI 的弹簧轴向固定为 Z 轴:无先验实体时轴起点在原点,有先验实体时坐在其顶面中心。 参数只认「中径 / 外径 + 线径 + 螺距 + 圈数」(可选左旋、锥度),不做任意轴向与自定义起点。
- AI 的涡状线落在前视基准面:有先验实体时坐在其顶面中心。参数只认 「起始半径 / 螺距 / 圈数 / 厚度 / 宽度」(可选左旋),不做任意平面与自定义起点。
- AI 的缩放以模型包围盒中心为不动点(不认自定义基点);「缩小 N 倍」按
1/N理解, 「缩小到 N 倍」按N理解。 - AI 的镜像以「模型自己的原点」为基准面位置:新建模式下编译层已把实体居中,基准面即世界原点;
追加模式下取现有模型包围盒中心。种子是上一个特征(与文档层
mirror特征语义一致), 因此「把整个模型镜像一份」要先让模型相对该基准面不对称。 - AI 的异型孔:柱形沉头 / 锥形埋头 / 螺纹三种孔型都映射到引擎的孔特征, 沉头(埋头)深度从放置面起算;沉头/埋头直径必须大于孔径,否则在编译层直接报错、不生成孔。
- AI 的移动 / 复制 / 删除实体作用于「全部实体」:不做按实体勾选(引擎支持
indices, 但 AI 层不产出)。旋转轴过「当前模型包围盒中心」、只支持轴向对齐的 X/Y/Z 轴。 旋转后长方体不再轴向对齐,解析式棱引用会主动失效(此时refBox变 null)。 - AI 的「删除实体」靠负数下标表达「最后一个」:
bodiesOf的展开顺序由拓扑决定, 不保证等于插入顺序,所以删除第2个实体的下标语义是「按拓扑展开的第 2 个」, 可能和界面上看到的顺序不一致;删除最后一个实体(-1)才是稳妥写法。 - AI 的组合固定「第一个实体为主体」:
把两个实体相减是「按拓扑展开的第 1 个 − 其余全部」。 另外两个分离实体做并集,OCCT 的Fuse不会把它们连成一个,结果仍是 2 实体复合体 (体积守恒,只是没有真正合并成一个体)——要合成一个体需要两个实体实际相交。 - AI 编辑的依赖跟随是「一段式」的:改某个基本体尺寸时,只跟踪它身后到下一个基本体之前的 孔 / 圆角 / 倒角 / 抽壳(以及恰好贴在新移动面上的那个后续基本体,并递归处理它自己的依赖), 不做全局依赖图。手工建的草图 / 拉伸 / 放样等特征不参与跟随。
- AI 编辑时「去掉 / 去除」按删除理解(如「去掉圆角」= 删掉圆角), 要表达布尔切除请用「切除 / 挖掉 / 减去」(如「挖掉一个半径5的圆柱」)。 新建模式下「去掉」仍是布尔切除,两种语境各取更自然的读法。
- 改尺寸不会自动求解「孔深 > 新板厚」这类约束:圆角/倒角/壁厚超过新尺寸时只给
warn提示并放行, 由内核在重建时给出最终判定,不做自动夹取(避免悄悄改掉用户给的数值)。
欢迎提 Issue 与 PR——仓库里已经配好了模板,照着填能省去大量来回沟通:
| 想做什么 | 去哪里 |
|---|---|
| 报告缺陷 | New issue → Bug 报告 |
| 提功能建议 | New issue → 功能建议 |
| 使用疑问 / 交流 | Discussions |
| 提功能前对照差距 | docs/solidworks-comparison.md |
提交 PR 前建议在本地跑一遍「三连」,这也是流水线会检查的内容:
npx tsc --noEmit # 类型干净
npm test # 19 个套件:草图/表达式/测量几何/AI/工程图/装配体/内核/单位制/截面属性/曲面/拉伸终止条件·薄壁特征/旋转扫描薄壁/热键层…
npm run build # 生产构建 + 预压缩 + 「主线程不引用 OCCT」架构不变量校验改动落在浏览器交互或几何语义上时,请补对应断言(tools/*-test.ts 的体积 / 面数 / 实体数,
或 npm run test:browser 的 DOM/端到端检查)——本项目的历史回归大多是「静默产出错误几何」,
一条解析解断言比十行描述更管用。PR 模板里列了完整的自查清单。
本项目采用 Apache License 2.0 授权。
这意味着你可以自由地使用、修改、分发本项目的代码(含商业用途), 只需保留原始版权声明与许可证文本,并在修改文件时说明「你改过」。 Apache-2.0 同时提供了显式的专利授权条款。
第三方组件保留其各自许可证:几何内核为 MIT 许可的 opencascade.js (封装的是 LGPL-2.1 的 OpenCASCADE 库),图形层为 MIT 许可的 three.js。
- OpenCASCADE —— 工业级开源几何内核,本项目的 B-rep 精确建模能力全部来自它。
- opencascade.js —— 把 OCCT 编译成 WebAssembly,使浏览器端获得 STEP 读写与精确布尔。
- three.js —— WebGL 渲染层。
- SolidWorks —— 界面与交互设计的对标对象;本项目为学习性质的复刻,与其无任何关联。



