第三章:几何建模基础
3.1 OpenCascade内核介绍
3.1.1 什么是OpenCascade
OpenCascade Technology(简称OCCT或OCC)是一个开源的3D CAD/CAM/CAE内核,由法国Matra Datavision公司最初开发,后来开源并由OpenCascade公司维护。它是世界上最著名的开源几何建模内核之一,被广泛应用于工业设计、仿真分析、数控加工等领域。
OpenCascade的核心特性包括:
- 边界表示法(B-Rep)建模:采用精确的边界表示法来描述3D几何形状,支持任意复杂的曲面和实体
- 参数化曲线和曲面:支持NURBS曲线、Bezier曲线、圆锥曲线等多种参数化几何元素
- 布尔运算:提供高效可靠的布尔运算算法,包括并集、差集、交集
- 曲面操作:支持曲面偏移、缝合、填充等高级操作
- 数据交换:支持STEP、IGES、BREP等标准格式的导入导出
3.1.2 WebAssembly集成
Chili3D通过Emscripten工具链将OpenCascade编译为WebAssembly(WASM),使其能够在浏览器中高效运行。这种方式具有以下优势:
- 接近原生性能:WebAssembly提供接近原生代码的执行速度
- 无需安装:用户无需安装任何本地软件
- 跨平台兼容:在所有支持WebAssembly的浏览器中都能运行
- 安全沙箱:在浏览器沙箱中运行,安全性更高
WASM初始化流程:
// chili-wasm/src/wasm.ts
export async function initChiliWasm(): Promise<void> {
// 加载WASM模块
const wasmModule = await import("./chili_wasm.js");
// 初始化模块
await wasmModule.default();
// 注册到全局服务
Services.register("wasmModule", wasmModule);
}
3.1.3 C++绑定层
Chili3D使用Embind(Emscripten的C++绑定工具)来暴露OpenCascade的API给JavaScript:
// cpp/src/bindings.cpp
#include <emscripten/bind.h>
#include <BRepPrimAPI_MakeBox.hxx>
using namespace emscripten;
TopoDS_Shape makeBox(double x, double y, double z, double dx, double dy, double dz) {
gp_Pnt origin(x, y, z);
TopoDS_Shape box = BRepPrimAPI_MakeBox(origin, dx, dy, dz).Shape();
return box;
}
EMSCRIPTEN_BINDINGS(chili_wasm) {
function("makeBox", &makeBox);
// 更多绑定...
}
3.2 形状类型体系
3.2.1 拓扑结构
OpenCascade使用拓扑结构来描述形状的组成关系。在Chili3D中,这些概念被封装到TypeScript接口中:
// chili-core/src/shape/shapeType.ts
export enum ShapeType {
Compound = 1, // 复合体:多个形状的集合
CompoundSolid = 2, // 复合实体:多个实体的集合
Solid = 4, // 实体:封闭的3D体积
Shell = 8, // 壳:多个面的集合
Face = 16, // 面:有界的曲面
Wire = 32, // 线框:多条边的连续序列
Edge = 64, // 边:有界的曲线
Vertex = 128, // 顶点:空间中的点
Shape = 256 // 通用形状类型
}
拓扑层次关系:
Compound(复合体)
└── Solid(实体)
└── Shell(壳)
└── Face(面)
└── Wire(线框)
└── Edge(边)
└── Vertex(顶点)
3.2.2 形状接口
// chili-core/src/shape/shape.ts
export interface IShape {
readonly shapeType: ShapeType;
readonly isNull: boolean;
readonly isValid: boolean;
// 获取子形状
findSubShapes(type: ShapeType): IShape[];
findAncestors(subShape: IShape, ancestorType: ShapeType): IShape[];
// 几何信息
getBoundingBox(): BoundingBox;
getCenter(): XYZ;
// 变换
transform(matrix: Matrix4): IShape;
mirror(plane: Plane): IShape;
// 网格化
mesh(deflection: number): MeshData;
// 序列化
toBrep(): string;
static fromBrep(brep: string): IShape;
}
3.2.3 形状实现
// chili-wasm/src/shape.ts
export class Shape implements IShape {
private _handle: number; // WASM对象句柄
private _shapeType?: ShapeType;
constructor(handle: number) {
this._handle = handle;
}
get shapeType(): ShapeType {
if (this._shapeType === undefined) {
this._shapeType = wasm.getShapeType(this._handle);
}
return this._shapeType;
}
get isNull(): boolean {
return wasm.isShapeNull(this._handle);
}
get isValid(): boolean {
return !this.isNull && wasm.isShapeValid(this._handle);
}
findSubShapes(type: ShapeType): IShape[] {
const handles = wasm.findSubShapes(this._handle, type);
return handles.map(h => new Shape(h));
}
getBoundingBox(): BoundingBox {
const [xmin, ymin, zmin, xmax, ymax, zmax] = wasm.getBoundingBox(this._handle);
return new BoundingBox(
new XYZ(xmin, ymin, zmin),
new XYZ(xmax, ymax, zmax)
);
}
transform(matrix: Matrix4): IShape {
const newHandle = wasm.transformShape(this._handle, matrix.toArray());
return new Shape(newHandle);
}
mesh(deflection: number = 0.1): MeshData {
return wasm.meshShape(this._handle, deflection);
}
// 清理资源
dispose(): void {
wasm.disposeShape(this._handle);
this._handle = 0;
}
}
3.3 形状工厂
3.3.1 工厂接口
形状工厂负责创建各种几何形状:
// chili-core/src/shape/shapeFactory.ts
export interface IShapeFactory {
// 基本几何体
box(origin: XYZ, dx: number, dy: number, dz: number): Result<IShape>;
sphere(center: XYZ, radius: number): Result<IShape>;
cylinder(axis: Ray, radius: number, height: number): Result<IShape>;
cone(axis: Ray, r1: number, r2: number, height: number): Result<IShape>;
// 曲线
line(start: XYZ, end: XYZ): Result<IEdge>;
arc(center: XYZ, start: XYZ, end: XYZ): Result<IEdge>;
circle(center: XYZ, normal: XYZ, radius: number): Result<IEdge>;
bezier(poles: XYZ[], weights?: number[]): Result<IEdge>;
// 线框和面
wire(edges: IEdge[]): Result<IWire>;
face(wire: IWire): Result<IFace>;
// 实体操作
extrude(profile: IShape, direction: XYZ, length: number): Result<IShape>;
revolve(profile: IShape, axis: Ray, angle: number): Result<IShape>;
sweep(profile: IShape, path: IWire): Result<IShape>;
loft(sections: IWire[], solid: boolean): Result<IShape>;
// 布尔运算
booleanUnion(shape1: IShape, shape2: IShape): Result<IShape>;
booleanCut(shape1: IShape, shape2: IShape): Result<IShape>;
booleanIntersect(shape1: IShape, shape2: IShape): Result<IShape>;
// 修改操作
fillet(shape: IShape, edges: IEdge[], radius: number): Result<IShape>;
chamfer(shape: IShape, edges: IEdge[], distance: number): Result<IShape>;
offset(shape: IShape, distance: number): Result<IShape>;
}
3.3.2 工厂实现
// chili-wasm/src/factory.ts
export class ShapeFactory implements IShapeFactory {
box(origin: XYZ, dx: number, dy: number, dz: number): Result<IShape> {
try {
const handle = wasm.makeBox(origin.x, origin.y, origin.z, dx, dy, dz);
return Result.ok(new Shape(handle));
} catch (e) {
return Result.error(`Failed to create box: ${e}`);
}
}
sphere(center: XYZ, radius: number): Result<IShape> {
if (radius <= 0) {
return Result.error("Radius must be positive");
}
try {
const handle = wasm.makeSphere(center.x, center.y, center.z, radius);
return Result.ok(new Shape(handle));
} catch (e) {
return Result.error(`Failed to create sphere: ${e}`);
}
}
cylinder(axis: Ray, radius: number, height: number): Result<IShape> {
if (radius <= 0 || height <= 0) {
return Result.error("Radius and height must be positive");
}
try {
const handle = wasm.makeCylinder(
axis.origin.x, axis.origin.y, axis.origin.z,
axis.direction.x, axis.direction.y, axis.direction.z,
radius, height
);
return Result.ok(new Shape(handle));
} catch (e) {
return Result.error(`Failed to create cylinder: ${e}`);
}
}
extrude(profile: IShape, direction: XYZ, length: number): Result<IShape> {
if (length === 0) {
return Result.error("Extrusion length cannot be zero");
}
try {
const normalized = direction.normalize();
const handle = wasm.makePrism(
(profile as Shape).handle,
normalized.x * length,
normalized.y * length,
normalized.z * length
);
return Result.ok(new Shape(handle));
} catch (e) {
return Result.error(`Failed to extrude: ${e}`);
}
}
booleanUnion(shape1: IShape, shape2: IShape): Result<IShape> {
try {
const handle = wasm.booleanFuse(
(shape1 as Shape).handle,
(shape2 as Shape).handle
);
return Result.ok(new Shape(handle));
} catch (e) {
return Result.error(`Boolean union failed: ${e}`);
}
}
booleanCut(shape1: IShape, shape2: IShape): Result<IShape> {
try {
const handle = wasm.booleanCut(
(shape1 as Shape).handle,
(shape2 as Shape).handle
);
return Result.ok(new Shape(handle));
} catch (e) {
return Result.error(`Boolean cut failed: ${e}`);
}
}
fillet(shape: IShape, edges: IEdge[], radius: number): Result<IShape> {
if (radius <= 0) {
return Result.error("Fillet radius must be positive");
}
try {
const edgeHandles = edges.map(e => (e as Shape).handle);
const handle = wasm.makeFillet(
(shape as Shape).handle,
edgeHandles,
radius
);
return Result.ok(new Shape(handle));
} catch (e) {
return Result.error(`Fillet failed: ${e}`);
}
}
}
3.4 曲线与曲面
3.4.1 曲线类型
Chili3D支持多种曲线类型:
// chili-core/src/shape/curve.ts
export enum CurveType {
Line = 0, // 直线
Circle = 1, // 圆
Ellipse = 2, // 椭圆
Hyperbola = 3, // 双曲线
Parabola = 4, // 抛物线
BezierCurve = 5, // Bezier曲线
BSplineCurve = 6, // B样条曲线
TrimmedCurve = 7, // 裁剪曲线
OffsetCurve = 8, // 偏移曲线
OtherCurve = 9 // 其他曲线
}
export interface ICurve {
readonly curveType: CurveType;
readonly isClosed: boolean;
readonly isPeriodic: boolean;
// 参数化接口
parameter(point: XYZ): number;
point(parameter: number): XYZ;
tangent(parameter: number): XYZ;
// 范围
firstParameter(): number;
lastParameter(): number;
// 转换
toEdge(): IEdge;
trim(u1: number, u2: number): ICurve;
reverse(): ICurve;
}
3.4.2 曲线实现
// chili-wasm/src/curve.ts
export class Curve implements ICurve {
private _handle: number;
constructor(handle: number) {
this._handle = handle;
}
get curveType(): CurveType {
return wasm.getCurveType(this._handle);
}
get isClosed(): boolean {
return wasm.isCurveClosed(this._handle);
}
parameter(point: XYZ): number {
return wasm.curveParameter(this._handle, point.x, point.y, point.z);
}
point(parameter: number): XYZ {
const [x, y, z] = wasm.curvePoint(this._handle, parameter);
return new XYZ(x, y, z);
}
tangent(parameter: number): XYZ {
const [x, y, z] = wasm.curveTangent(this._handle, parameter);
return new XYZ(x, y, z);
}
trim(u1: number, u2: number): ICurve {
const handle = wasm.trimCurve(this._handle, u1, u2);
return new Curve(handle);
}
}
// 具体曲线类型
export class LineCurve extends Curve {
get start(): XYZ {
return this.point(this.firstParameter());
}
get end(): XYZ {
return this.point(this.lastParameter());
}
get direction(): XYZ {
return this.end.sub(this.start).normalize();
}
get length(): number {
return this.start.distanceTo(this.end);
}
}
export class CircleCurve extends Curve {
get center(): XYZ {
const [x, y, z] = wasm.circleCenter(this._handle);
return new XYZ(x, y, z);
}
get radius(): number {
return wasm.circleRadius(this._handle);
}
get normal(): XYZ {
const [x, y, z] = wasm.circleNormal(this._handle);
return new XYZ(x, y, z);
}
}
3.4.3 曲面类型
// chili-core/src/shape/surface.ts
export enum SurfaceType {
Plane = 0, // 平面
Cylinder = 1, // 圆柱面
Cone = 2, // 圆锥面
Sphere = 3, // 球面
Torus = 4, // 圆环面
BezierSurface = 5, // Bezier曲面
BSplineSurface = 6, // B样条曲面
SurfaceOfRevolution = 7,// 旋转曲面
SurfaceOfExtrusion = 8, // 拉伸曲面
OffsetSurface = 9, // 偏移曲面
OtherSurface = 10 // 其他曲面
}
export interface ISurface {
readonly surfaceType: SurfaceType;
readonly isUClosed: boolean;
readonly isVClosed: boolean;
// 参数化接口
point(u: number, v: number): XYZ;
normal(u: number, v: number): XYZ;
// 范围
uRange(): [number, number];
vRange(): [number, number];
// 转换
toFace(): IFace;
}
3.5 布尔运算
3.5.1 布尔运算类型
export enum BooleanType {
Union = 0, // 并集
Cut = 1, // 差集
Intersect = 2, // 交集
Common = 3 // 公共部分
}
3.5.2 布尔运算实现
// chili/src/bodys/boolean.ts
@Serializable("BooleanBody")
export class BooleanBody extends Body {
@Property()
private _operationType: BooleanType;
@Property()
private _target: IBody;
@Property()
private _tool: IBody;
constructor(
document: IDocument,
operationType: BooleanType,
target: IBody,
tool: IBody
) {
super(document);
this._operationType = operationType;
this._target = target;
this._tool = tool;
}
protected generateShape(): Result<IShape> {
const targetShape = this._target.shape;
const toolShape = this._tool.shape;
if (!targetShape || !toolShape) {
return Result.error("Invalid input shapes");
}
const factory = this.document.application.shapeFactory;
switch (this._operationType) {
case BooleanType.Union:
return factory.booleanUnion(targetShape, toolShape);
case BooleanType.Cut:
return factory.booleanCut(targetShape, toolShape);
case BooleanType.Intersect:
return factory.booleanIntersect(targetShape, toolShape);
default:
return Result.error("Unknown boolean type");
}
}
}
3.5.3 布尔运算命令
// chili/src/commands/boolean.ts
@command({
name: "Modify.Boolean.Union",
icon: "icon-union",
display: "command.union"
})
export class BooleanUnionCommand implements ICommand {
async execute(document: IDocument): Promise<void> {
// 选择目标对象
const targetResult = await document.selection.pickShape({
prompt: t("prompt.selectTarget"),
filter: ShapeType.Solid | ShapeType.Shell
});
if (!targetResult.success) return;
// 选择工具对象
const toolResult = await document.selection.pickShape({
prompt: t("prompt.selectTool"),
filter: ShapeType.Solid | ShapeType.Shell
});
if (!toolResult.success) return;
// 创建布尔体
const body = new BooleanBody(
document,
BooleanType.Union,
targetResult.data.body,
toolResult.data.body
);
// 添加到文档
const node = new GeometryNode(document, "Union", body);
document.addNode(node);
// 隐藏原始对象
targetResult.data.node.visible = false;
toolResult.data.node.visible = false;
}
}
3.6 几何体实现
3.6.1 Body基类
所有几何体都继承自Body基类:
// chili/src/bodys/body.ts
export abstract class Body extends Observable implements IBody {
private _shape?: IShape;
private _needsUpdate: boolean = true;
constructor(readonly document: IDocument) {
super();
}
get shape(): IShape | undefined {
if (this._needsUpdate || !this._shape) {
const result = this.generateShape();
if (result.isOk) {
this._shape = result.value;
} else {
console.error(result.error);
this._shape = undefined;
}
this._needsUpdate = false;
}
return this._shape;
}
protected abstract generateShape(): Result<IShape>;
protected invalidate(): void {
this._needsUpdate = true;
this._shape?.dispose();
this._shape = undefined;
this.notify("shapeChanged");
}
}
3.6.2 长方体
// chili/src/bodys/box.ts
@Serializable("BoxBody")
export class BoxBody extends Body {
@Property()
private _origin: XYZ;
@Property()
private _dx: number;
@Property()
private _dy: number;
@Property()
private _dz: number;
constructor(
document: IDocument,
origin: XYZ,
dx: number,
dy: number,
dz: number
) {
super(document);
this._origin = origin;
this._dx = dx;
this._dy = dy;
this._dz = dz;
}
get origin(): XYZ { return this._origin; }
set origin(value: XYZ) {
if (!this._origin.equals(value)) {
this._origin = value;
this.invalidate();
}
}
get dx(): number { return this._dx; }
set dx(value: number) {
if (this._dx !== value) {
this._dx = value;
this.invalidate();
}
}
protected generateShape(): Result<IShape> {
return this.document.application.shapeFactory.box(
this._origin, this._dx, this._dy, this._dz
);
}
}
3.6.3 球体
// chili/src/bodys/sphere.ts
@Serializable("SphereBody")
export class SphereBody extends Body {
@Property()
private _center: XYZ;
@Property()
private _radius: number;
constructor(document: IDocument, center: XYZ, radius: number) {
super(document);
this._center = center;
this._radius = radius;
}
get center(): XYZ { return this._center; }
set center(value: XYZ) {
if (!this._center.equals(value)) {
this._center = value;
this.invalidate();
}
}
get radius(): number { return this._radius; }
set radius(value: number) {
if (this._radius !== value && value > 0) {
this._radius = value;
this.invalidate();
}
}
protected generateShape(): Result<IShape> {
return this.document.application.shapeFactory.sphere(
this._center, this._radius
);
}
}
3.6.4 拉伸体
// chili/src/bodys/prism.ts
@Serializable("PrismBody")
export class PrismBody extends Body {
@Property()
private _profile: IBody;
@Property()
private _direction: XYZ;
@Property()
private _length: number;
constructor(
document: IDocument,
profile: IBody,
direction: XYZ,
length: number
) {
super(document);
this._profile = profile;
this._direction = direction;
this._length = length;
// 监听轮廓变化
profile.addObserver("shapeChanged", () => this.invalidate());
}
protected generateShape(): Result<IShape> {
const profileShape = this._profile.shape;
if (!profileShape) {
return Result.error("Invalid profile shape");
}
return this.document.application.shapeFactory.extrude(
profileShape,
this._direction,
this._length
);
}
}
3.6.5 旋转体
// chili/src/bodys/revolve.ts
@Serializable("RevolveBody")
export class RevolveBody extends Body {
@Property()
private _profile: IBody;
@Property()
private _axis: Ray;
@Property()
private _angle: number;
constructor(
document: IDocument,
profile: IBody,
axis: Ray,
angle: number
) {
super(document);
this._profile = profile;
this._axis = axis;
this._angle = angle;
profile.addObserver("shapeChanged", () => this.invalidate());
}
protected generateShape(): Result<IShape> {
const profileShape = this._profile.shape;
if (!profileShape) {
return Result.error("Invalid profile shape");
}
return this.document.application.shapeFactory.revolve(
profileShape,
this._axis,
this._angle
);
}
}
3.7 网格化与显示
3.7.1 网格数据结构
将几何形状转换为可显示的三角网格:
// chili-core/src/shape/meshData.ts
export interface MeshData {
// 顶点位置 (x, y, z)
positions: Float32Array;
// 顶点法向量 (nx, ny, nz)
normals: Float32Array;
// 顶点UV坐标 (u, v)
uvs?: Float32Array;
// 三角形索引
indices: Uint32Array;
// 边数据
edges?: EdgeMeshData;
}
export interface EdgeMeshData {
// 边线顶点
positions: Float32Array;
// 边线索引 (起点, 终点)
indices: Uint32Array;
}
3.7.2 网格生成
// chili-wasm/src/mesher.ts
export class Mesher {
/**
* 将形状转换为网格
* @param shape 输入形状
* @param deflection 网格精度(弦偏差)
* @param angularDeflection 角度偏差
*/
static meshShape(
shape: IShape,
deflection: number = 0.1,
angularDeflection: number = 0.5
): MeshData {
const handle = (shape as Shape).handle;
// 调用WASM进行网格化
const result = wasm.meshShape(handle, deflection, angularDeflection);
return {
positions: new Float32Array(result.positions),
normals: new Float32Array(result.normals),
indices: new Uint32Array(result.indices),
edges: result.edges ? {
positions: new Float32Array(result.edges.positions),
indices: new Uint32Array(result.edges.indices)
} : undefined
};
}
}
3.7.3 与Three.js集成
// chili-three/src/threeGeometry.ts
export class ThreeGeometry {
static fromMeshData(meshData: MeshData): THREE.BufferGeometry {
const geometry = new THREE.BufferGeometry();
// 设置顶点属性
geometry.setAttribute(
"position",
new THREE.BufferAttribute(meshData.positions, 3)
);
geometry.setAttribute(
"normal",
new THREE.BufferAttribute(meshData.normals, 3)
);
if (meshData.uvs) {
geometry.setAttribute(
"uv",
new THREE.BufferAttribute(meshData.uvs, 2)
);
}
// 设置索引
geometry.setIndex(new THREE.BufferAttribute(meshData.indices, 1));
return geometry;
}
static createEdgeGeometry(edgeData: EdgeMeshData): THREE.BufferGeometry {
const geometry = new THREE.BufferGeometry();
geometry.setAttribute(
"position",
new THREE.BufferAttribute(edgeData.positions, 3)
);
geometry.setIndex(new THREE.BufferAttribute(edgeData.indices, 1));
return geometry;
}
}
3.8 几何计算
3.8.1 交点计算
export class GeometryHelper {
/**
* 计算两条曲线的交点
*/
static curveIntersections(curve1: ICurve, curve2: ICurve): XYZ[] {
return wasm.curveCurveIntersection(
(curve1 as Curve).handle,
(curve2 as Curve).handle
).map(([x, y, z]) => new XYZ(x, y, z));
}
/**
* 计算曲线与曲面的交点
*/
static curveSurfaceIntersections(curve: ICurve, surface: ISurface): XYZ[] {
return wasm.curveSurfaceIntersection(
(curve as Curve).handle,
(surface as Surface).handle
).map(([x, y, z]) => new XYZ(x, y, z));
}
/**
* 计算点到曲线的最近点
*/
static nearestPointOnCurve(point: XYZ, curve: ICurve): XYZ {
const [x, y, z] = wasm.nearestPointOnCurve(
point.x, point.y, point.z,
(curve as Curve).handle
);
return new XYZ(x, y, z);
}
/**
* 计算点到曲面的最近点
*/
static nearestPointOnSurface(point: XYZ, surface: ISurface): XYZ {
const [x, y, z] = wasm.nearestPointOnSurface(
point.x, point.y, point.z,
(surface as Surface).handle
);
return new XYZ(x, y, z);
}
}
3.8.2 距离计算
export class DistanceHelper {
/**
* 计算两个形状之间的最小距离
*/
static shapeDistance(shape1: IShape, shape2: IShape): number {
return wasm.shapeDistance(
(shape1 as Shape).handle,
(shape2 as Shape).handle
);
}
/**
* 计算点到形状的最小距离
*/
static pointShapeDistance(point: XYZ, shape: IShape): number {
return wasm.pointShapeDistance(
point.x, point.y, point.z,
(shape as Shape).handle
);
}
}
3.8.3 测量工具
// chili/src/commands/measure/measureDistance.ts
@command({
name: "Measure.Distance",
icon: "icon-measure-distance",
display: "command.measureDistance"
})
export class MeasureDistanceCommand implements ICommand {
async execute(document: IDocument): Promise<void> {
// 选择第一个点
const point1Result = await document.selection.pickPoint({
prompt: t("prompt.selectFirstPoint")
});
if (!point1Result.success) return;
// 选择第二个点
const point2Result = await document.selection.pickPoint({
prompt: t("prompt.selectSecondPoint")
});
if (!point2Result.success) return;
// 计算距离
const distance = point1Result.data.distanceTo(point2Result.data);
// 显示结果
Toast.show(`${t("measure.distance")}: ${distance.toFixed(3)}`);
}
}
3.9 数据交换
3.9.1 支持的格式
Chili3D支持多种标准CAD格式:
- STEP(.step, .stp):ISO标准格式,最常用的CAD交换格式
- IGES(.iges, .igs):初始图形交换规范,较老但广泛支持
- BREP(.brep):OpenCascade原生格式
3.9.2 导入实现
// chili-builder/src/defaultDataExchange.ts
export class DefaultDataExchange implements IDataExchange {
async import(file: File): Promise<Result<IShape[]>> {
const extension = file.name.split('.').pop()?.toLowerCase();
const buffer = await file.arrayBuffer();
switch (extension) {
case 'step':
case 'stp':
return this.importStep(buffer);
case 'iges':
case 'igs':
return this.importIges(buffer);
case 'brep':
return this.importBrep(buffer);
default:
return Result.error(`Unsupported format: ${extension}`);
}
}
private importStep(buffer: ArrayBuffer): Result<IShape[]> {
try {
const shapes = wasm.readStep(new Uint8Array(buffer));
return Result.ok(shapes.map(h => new Shape(h)));
} catch (e) {
return Result.error(`STEP import failed: ${e}`);
}
}
}
3.9.3 导出实现
export class DefaultDataExchange implements IDataExchange {
async export(
shapes: IShape[],
format: ExportFormat,
filename: string
): Promise<void> {
const handles = shapes.map(s => (s as Shape).handle);
let data: Uint8Array;
switch (format) {
case ExportFormat.STEP:
data = wasm.writeStep(handles);
break;
case ExportFormat.IGES:
data = wasm.writeIges(handles);
break;
case ExportFormat.BREP:
data = wasm.writeBrep(handles);
break;
default:
throw new Error(`Unsupported format: ${format}`);
}
// 创建下载链接
const blob = new Blob([data], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
}
3.10 本章小结
本章深入介绍了Chili3D的几何建模基础,包括:
- OpenCascade内核:介绍了OCCT的特性和WebAssembly集成方式
- 形状类型体系:详细说明了拓扑结构和形状接口设计
- 形状工厂:展示了各种几何形状的创建方法
- 曲线与曲面:介绍了参数化几何元素的表示和操作
- 布尔运算:说明了并集、差集、交集等布尔操作的实现
- 几何体实现:展示了各种具体几何体的实现方式
- 网格化与显示:介绍了将几何形状转换为可显示网格的过程
- 几何计算:介绍了交点、距离等几何计算功能
- 数据交换:说明了STEP、IGES等格式的导入导出
掌握这些几何建模知识,是进行Chili3D二次开发的重要基础。在下一章中,我们将深入探讨用户界面与交互系统。
下一章预告:第四章将详细介绍Chili3D的用户界面系统,包括UI组件库、功能区设计、视口交互、选择系统等核心内容。