当前位置: 首页 > news >正文

ACIS SAT 文件格式详解及其解析

目录

  1. SAT 文件格式概述
  2. 文件结构与版本
  3. 核心解析类设计
  4. 几何实体解析
  5. 拓扑实体解析
  6. NURBS 曲线与曲面解析
  7. 错误处理与容错机制

SAT 文件格式概述

什么是 SAT 文件?

ACIS SAT(Standard ACIS Text)文件是由 Spatial 公司开发的 ACIS 几何建模内核使用的标准文件格式。它是一种文本格式,用于存储三维几何模型的边界表示(B-Rep)数据,包括:

  • 几何信息:点、线、面、体等几何实体的数学定义
  • 拓扑信息:顶点、边、环、面、壳等拓扑实体及其连接关系
  • 属性信息:颜色、名称、图层等附加属性
  • NURBS 支持:完整的 NURBS(非均匀有理 B 样条)曲线和曲面表示

SAT 文件的特点

  1. 文本格式:人可读,便于调试和分析
  2. 版本号标识:文件开头明确标注 ACIS 版本
  3. 实体编号系统:每个实体有唯一的 ID 和引用计数
  4. 前向引用:支持实体间的相互引用
  5. 灵活的扩展性:支持自定义属性和扩展字段

文件结构与版本

文件头格式

ACIS 100 ACIS Verifier status: 0
  • ACIS:固定标识符
  • 100:主版本号(如 700 表示 v7.0,3300 表示 v33.0)
  • ACIS Verifier status: 0:验证状态

实体记录格式

每个实体记录遵循以下格式:

<count> <entity-type> <ref1> <ref2> ... <data-fields> { <nested-data> }

示例:

-40 intcurve-curve $-1 -1 -1 $-1 forward { exactcur 6 full nurbs 3 open 2 ... }

字段说明:

  • -40:实体编号(负数表示引用计数)
  • intcurve-curve:实体类型(相交曲线)
  • $-1 -1 -1 $-1:前置引用($ 表示向前引用)
  • forward:方向标志
  • { ... }:嵌套数据块

主要实体类型

几何实体

  • point - 点
  • straight-curve - 直线
  • circle-curve - 圆
  • ellipse-curve - 椭圆
  • intcurve-curve - 参数曲线(含 NURBS)
  • plane-surface - 平面
  • cylinder-surface - 圆柱面
  • cone-surface - 圆锥面
  • exactsur - 精确曲面(含 NURBS)

拓扑实体

  • vertex - 顶点
  • edge - 边
  • coedge - 协同边(有向边)
  • loop - 环
  • face - 面
  • shell - 壳
  • lump - 体块
  • body - 实体

核心解析类设计

类图概览

classDiagram%% 核心流类class SATInputStream {+std::istream& stream+size_t lineNumber+peekToken() String+nextToken() String+readInt() int+readDouble() double+readString() String+readEntityRef() int+readFlagsUntilNumber() List~String~+static IsNumber(token) bool+static hasFlag(flags, flag) bool}%% 基础实体类class SATEntity {#int m_satId#int m_refCount#SATEntityType m_type+getSatId() int+getRefCount() int+getType() SATEntityType+virtual Import(stm) void+virtual ResolvePointers() void}class SATFactory {+static createEntity(type, id) SATEntity*+static registerType(type, factory) void}%% 几何实体层次class SATGeometry {+virtual GetCurve() Geom_Curve+virtual GetSurface() Geom_Surface}class SATPoint {-double m_x, m_y, m_z+Import(stm) void+GetPoint() gp_Pnt}class SATCurve {#Handle(Geom_Curve) m_curve+GetCurve() Geom_Curve}class SATIntersectionCurve {-int m_degree-bool m_isRational-List~double~ m_knots-List~int~ m_multiplicities-List~gp_Pnt~ m_controlPoints-List~double~ m_weights+Import(stm) void+buildBSplineCurve() void}class SATSurface {#Handle(Geom_Surface) m_surface+GetSurface() Geom_Surface}class SATExactSurface {-int m_uDegree, m_vDegree-bool m_isRational-List~List~gp_Pnt~ m_controlPoints-List~List~double~ m_weights+Import(stm) void+buildBSplineSurface() void}%% 拓扑实体层次class SATTopology {+TopoDS_Shape GetShape()}class SATVertex {-SATPoint* m_point+ResolvePointers() void+GetShape() TopoDS_Vertex}class SATEdge {-SATCurve* m_curve-SATVertex* m_start, *m_end+ResolvePointers() void+GetShape() TopoDS_Edge}class SATFace {-SATSurface* m_surface-List~SATLoop~ m_loops+ResolvePointers() void+GetShape() TopoDS_Face}%% 文件解析器class SATFile {-String filename-Map~int, SATEntity~ entities-Load() bool-BuildTopology() TopoDS_Shape-ExportBREP(shape, file) bool}%% 关系定义SATInputStream --> SATEntity : 读取数据SATFactory --> SATEntity : 创建实例SATEntity <|-- SATGeometrySATEntity <|-- SATTopologySATCurve <|-- SATIntersectionCurveSATSurface <|-- SATExactSurfaceSATFile --> SATEntity : 管理所有实体SATFile --> SATInputStream : 使用流解析

核心类详解

1. SATInputStream - 流式解析器

职责:提供 token 级别的流式读取功能

关键方法

class SATInputStream {
public:std::string peekToken();      // 预读下一个 token,不消耗std::string nextToken();      // 读取并消耗下一个 tokenint readInt();                // 读取整数double readDouble();          // 读取浮点数std::string readString();     // 读取字符串int readEntityRef();          // 读取实体引用(跳过 $ 标记)std::vector<std::string> readFlagsUntilNumber(); // 读取标志直到数字static bool IsNumber(const std::string& token);static bool hasFlag(const std::vector<std::string>& flags, const std::string& flag);
};

设计亮点

  • Token 预读机制,支持前瞻解析
  • 自动跳过空白字符和注释
  • 行号跟踪,便于错误定位
  • 静态辅助函数,支持类型检测和标志判断

2. SATEntity - 实体基类

职责:所有 SAT 实体的抽象基类

关键特性

enum class SATEntityType {POINT, CURVE, SURFACE,   // 几何实体VERTEX, EDGE, FACE,      // 拓扑实体LOOP, COEDGE, SHELL,     // 拓扑实体LUMP, BODY, ATTRIBUTE    // 其他实体
};class SATEntity {
protected:int m_satId;              // SAT 文件中的实体 IDint m_refCount;           // 引用计数SATEntityType m_type;     // 实体类型public:virtual void Import(SATInputStream& stm) = 0;  // 从流导入数据virtual void ResolvePointers();                // 解析引用(第二阶段)int GetSatId() const { return m_satId; }SATEntityType GetType() const { return m_type; }
};

两阶段解析模式

  1. Import 阶段:读取自身数据,保存引用 ID
  2. ResolvePointers 阶段:根据引用 ID 查找并建立指针链接

3. SATFactory - 工厂类

职责:根据实体类型动态创建对应的解析器实例

实现原理

class SATFactory {
private:static std::map<std::string, EntityCreator> creators;public:static SATEntity* createEntity(const std::string& type, int id) {if (creators.find(type) == creators.end()) {std::cerr << "WARNING: No factory for '" << type << "'" << std::endl;return nullptr;}return creators[type](id);}static void registerType(const std::string& type, EntityCreator creator) {creators[type] = creator;}
};

注册示例

// 在初始化时注册所有实体类型
SATFactory::registerType("point", [](int id) { return new SATPoint(id); });
SATFactory::registerType("intcurve-curve", [](int id) { return new SATIntersectionCurve(id); });
SATFactory::registerType("exactsur", [](int id) { return new SATExactSurface(id); });

几何实体解析

点(SATPoint)

最简单的几何实体:

void SATPoint::Import(SATInputStream& stm) {auto header = stm.readEntityHeader();// 读取三个坐标值m_x = stm.readDouble();m_y = stm.readDouble();m_z = stm.readDouble();
}gp_Pnt SATPoint::GetPoint() const {return gp_Pnt(m_x, m_y, m_z);
}

参数曲线(SATIntersectionCurve)

最复杂的曲线类型,支持 NURBS:

void SATIntersectionCurve::Import(SATInputStream& stm) {// 1. 读取实体头auto header = stm.readEntityHeader();// 2. 跳过前置标记直到 "{"skipPrecedingTokens(stm);// 3. 读取曲线类型及基础信息std::string curveType = stm.readString();  // "exactcur"int version = stm.readInt();               // 版本号std::string flags = stm.readString();      // "full"std::string curveKind = stm.readString();  // "nurbs" 或 "nubs"// 根据标志设置理性标志m_isRational = (curveKind == "nurbs");int dimension = stm.readInt();             // 维度(通常是 3)std::string knotType = stm.readString();   // open/closed/periodicm_degree = stm.readInt();                  // 阶数// 节点对数量 - 版本 6+ 才有此字段int knotPairs = 0;if (version >= 6) {knotPairs = stm.readInt();}// 4. 读取节点向量for (int i = 0; i < knotPairs; ++i) {double knot = stm.readDouble();int mult = stm.readInt();// 端点重复度修正if ((i == 0 || i == knotPairs - 1) && mult == m_degree) {mult = m_degree + 1;}m_knots.push_back(knot);m_multiplicities.push_back(mult);}// 5. 读取控制点(根据是否有理决定是否读取权重)while (true) {std::string token = stm.peekToken();if (!SATInputStream::IsNumber(token) || token == "}") break;double x = stm.readDouble();double y = stm.readDouble();double z = stm.readDouble();double w = 1.0;if (m_isRational) {// 有理曲线:必须有权重if (SATInputStream::IsNumber(stm.peekToken())) {w = stm.readDouble();}}m_controlPoints.push_back(gp_Pnt(x, y, z));m_weights.push_back(w);m_controlPointCount++;}// 6. 跳过尾部固定字段skipTrailingFields(stm);// 7. 构建 OCCT B-Spline 曲线buildBSplineCurve();
}

关键设计决策

  1. nurbs vs nubs

    • nurbs(Non-Uniform Rational B-Spline):有理 B 样条,需要读取权重
    • nubs(Non-Uniform B-Spline):非有理 B 样条,不需要权重
  2. 节点向量展开

    // 压缩格式:knot=0, mult=3; knot=1, mult=3
    // 展开后:[0, 0, 0, 1, 1, 1]
    TColStd_Array1OfReal knots(1, expandedSize);
    int idx = 1;
    for (size_t i = 0; i < m_knots.size(); ++i) {for (int j = 0; j < m_multiplicities[i]; ++j) {knots(idx++) = m_knots[i];}
    }
    
  3. 有理曲线构造

    if (m_isRational) {m_curve = new Geom_BSplineCurve(poles, weights, knots, mults, degree, false);
    } else {m_curve = new Geom_BSplineCurve(poles, knots, mults, degree, false);
    }
    

NURBS 曲面解析(SATExactSurface)

数据格式

exactsur version rational_flag u_degree v_degree u_knot_pairs v_knot_pairsu_knots... v_knots...control_points_with_weights...

解析流程

void SATExactSurface::Import(SATInputStream& stm) {// 1. 读取基本信息auto header = stm.readEntityHeader();skipPrecedingTokens(stm);std::string type = stm.readString();  // "exactsur"int version = stm.readInt();int rationalFlag = stm.readInt();     // 3=nubs, other=nurbsm_isRational = (rationalFlag != 3);// 2. 读取次数m_uDegree = stm.readInt();m_vDegree = stm.readInt();// 3. 读取节点对数量int uKnotPairs = stm.readInt();int vKnotPairs = stm.readInt();// 4. 读取 U 方向节点向量for (int i = 0; i < uKnotPairs; ++i) {double knot = stm.readDouble();int mult = stm.readInt();m_uKnots.push_back(knot);m_uMultiplicities.push_back(mult);}// 5. 读取 V 方向节点向量for (int i = 0; i < vKnotPairs; ++i) {double knot = stm.readDouble();int mult = stm.readInt();m_vKnots.push_back(knot);m_vMultiplicities.push_back(mult);}// 6. 读取控制点网格(带权重)readControlPointGrid(stm);// 7. 跳过尾部字段skipTrailingFields(stm);// 8. 构建 OCCT NURBS 曲面buildBSplineSurface();
}

控制点网格读取

void SATExactSurface::readControlPointGrid(SATInputStream& stm) {m_controlPoints.clear();m_weights.clear();std::vector<double> currentRow;std::vector<double> weightRow;while (!stm.eof() && !stm.isToken("#")) {std::string token = stm.peekToken();// 检查是否遇到结束标记if (token == "}" || token == "#") break;// 尝试读取 4 个值(x, y, z, weight)if (SATInputStream::IsNumber(token)) {double x = stm.readDouble();double y = stm.readDouble();double z = stm.readDouble();// 有理曲面必须读取权重double w = 1.0;if (m_isRational && SATInputStream::IsNumber(stm.peekToken())) {w = stm.readDouble();}currentRow.push_back(x);currentRow.push_back(y);currentRow.push_back(z);weightRow.push_back(w);// 每 3 个值(xyz)完成一个控制点if (currentRow.size() % 3 == 0) {// 检查是否需要开始新行(通过检测下一行的第一个值)if (shouldStartNewRow(stm)) {addControlPointRow(currentRow, weightRow);currentRow.clear();weightRow.clear();}}} else {break;}}// 添加最后一行if (!currentRow.empty()) {addControlPointRow(currentRow, weightRow);}
}

NURBS 曲面构建

void SATExactSurface::buildBSplineSurface() {try {// 1. 展开节点向量auto [uKnots, uMults] = expandKnotVector(m_uKnots, m_uMultiplicities);auto [vKnots, vMults] = expandKnotVector(m_vKnots, m_vMultiplicities);// 2. 构建控制点数组int uSize = getUControlPointSize();int vSize = getVControlPointSize();TColgp_Array2OfPnt poles(1, uSize, 1, vSize);TColStd_Array2OfReal weights(1, uSize, 1, vSize);for (int i = 0; i < uSize; ++i) {for (int j = 0; j < vSize; ++j) {poles(i+1, j+1) = m_controlPoints[i][j];weights(i+1, j+1) = m_weights[i][j];}}// 3. 创建 OCCT NURBS 曲面if (m_isRational) {m_surface = new Geom_BSplineSurface(poles, weights,uKnots, vKnots,uMults, vMults,m_uDegree, m_vDegree,false, false  // not periodic);} else {m_surface = new Geom_BSplineSurface(poles,uKnots, vKnots,uMults, vMults,m_uDegree, m_vDegree,false, false);}} catch (const Standard_ConstructionError& e) {std::cerr << "ERROR: Failed to build NURBS surface: " << e.GetMessageString() << std::endl;fallbackToPlane();}
}

拓扑实体解析

边(SATEdge)

void SATEdge::Import(SATInputStream& stm) {auto header = stm.readEntityHeader();// 读取曲线引用m_curveId = stm.readEntityRef();// 读取参数范围m_firstParameter = stm.readOptionalDouble();m_lastParameter = stm.readOptionalDouble();// 读取 PCurves(可选)readPCurves(stm);// 读取同侧标记m_sameSense = stm.readBoolean();
}void SATEdge::ResolvePointers() {// 查找引用的曲线SATCurve* curve = getFile()->getCurveById(m_curveId);// 创建 OCCT 边TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(curve->GetCurve(), m_firstParameter, m_lastParameter);m_shape = edge;
}

面(SATFace)

void SATFace::Import(SATInputStream& stm) {auto header = stm.readEntityHeader();// 读取曲面引用m_surfaceId = stm.readEntityRef();// 读取边界环while (!stm.isToken("#")) {int loopId = stm.readEntityRef();m_loopIds.push_back(loopId);}// 读取 2D 参数(可选)read2DParameters(stm);
}void SATFace::ResolvePointers() {// 获取曲面SATSurface* surface = getFile()->getSurfaceById(m_surfaceId);// 创建面TopoDS_Face face = BRepBuilderAPI_MakeFace(surface->GetSurface(), Precision::Confusion());// 添加边界环for (int loopId : m_loopIds) {SATLoop* loop = getFile()->getLoopById(loopId);TopoDS_Wire wire = loop->GetWire();if (loop.isOuter()) {face.Add(wire);  // 外环} else {face.Add(wire);  // 内环(孔)}}m_shape = face;
}

错误处理与容错机制

1. 版本兼容性处理

int SATIntersectionCurve::readVersionAware(SATInputStream& stm) {int version = stm.readInt();// 不同版本有不同的字段顺序if (version < 6) {// Version 4: 没有 knotPairs 字段,需要动态检测return readLegacyFormat(stm);} else {// Version 6+: 标准格式return readStandardFormat(stm);}
}

2. 回退策略(Fallback)

Handle(Geom_Surface) SATExactSurface::GetSurface() {if (!m_surface.IsNull()) {return m_surface;}try {buildBSplineSurface();} catch (const Standard_ConstructionError& e) {std::cerr << "ERROR: NURBS surface construction failed, falling back to plane" << std::endl;// 回退到平面fallbackToPlane();}return m_surface;
}void SATExactSurface::fallbackToPlane() {// 尝试用前三个控制点构造平面if (m_controlPoints.size() >= 3) {gp_Pnt p1 = m_controlPoints[0][0];gp_Pnt p2 = m_controlPoints[0][1];gp_Pnt p3 = m_controlPoints[1][0];gp_Vec v1(p1, p2);gp_Vec v2(p1, p3);gp_Dir normal = v1.Crossed(v2);m_surface = new Geom_Plane(p1, normal);} else {// 默认 XY 平面m_surface = new Geom_Plane(gp::XOY());}
}

3. 形状修复协议

TopoDS_Shape SATFile::BuildTopology() {TopoDS_Compound compound;BRep_Builder builder;builder.MakeCompound(compound);// 1. 构建所有拓扑实体for (auto& entity : entities) {if (entity->isBody()) {TopoDS_Shape shape = entity->GetShape();if (!shape.IsNull()) {builder.Add(compound, shape);}}}// 2. 应用 ShapeFix 修复ShapeFix_Shape fix(compound);fix.SetPrecision(Precision::Confusion());fix.SetMaxTolerance(1e-3);fix.Perform();return fix.Shape();
}

总结与展望

技术要点总结

  1. 流式解析架构

    • Token 预读机制支持灵活的前瞻解析
    • 两阶段解析模式(Import + ResolvePointers)有效处理循环引用
  2. NURBS 完整支持

    • 正确解析有理/非有理 B 样条
    • 节点向量展开与控制点网格重建
    • 版本感知的字段顺序适配
  3. 容错与修复

    • 多层次的回退策略确保解析成功率
    • ShapeFix 集成保证拓扑有效性
    • 详细的错误日志便于问题诊断
  4. 可扩展设计

    • 工厂模式支持新实体类型的快速集成
    • 虚函数机制允许子类定制解析逻辑

参考文献

  1. Spatial Technology. ACIS SAT File Format Specification.
  2. Open CASCADE. OCCT Documentation.
  3. Piegl, L., & Tiller, W. (1997). The NURBS Book (2nd ed.). Springer.
http://www.jsqmd.com/news/521564/

相关文章:

  • 为什么你的Neovim图标显示异常?深入解析Nerd Fonts工作原理与选型建议
  • Bilibili视频下载完整指南:如何用开源工具高效获取优质内容
  • hot100--二分查找
  • 影墨·今颜AI人像版权管理:EXIF元数据嵌入+区块链存证接口
  • nlp_structbert_sentence-similarity_chinese-large部署案例:混合云环境下模型服务化实践
  • RCN-600 SUSI通信库嵌入式集成与工业UART协议实践
  • GPT-OSS-20B新手入门指南:手把手教你搭建本地智能助手
  • DAMO-YOLO保姆级教程:app.py中confidence_threshold参数动态调整
  • 免费开源!Gemma-3-12B-IT WebUI:你的轻量级AI对话机器人部署方案
  • Ollama部署granite-4.0-h-350m一文详解:轻量级指令模型在中小企业落地应用
  • YASB终极教程:10个高效使用技巧提升工作流
  • 【具身智能实践】从标定板到抓取:手眼标定全流程拆解与精度优化
  • trimesh路径处理指南:2D/3D矢量路径的DXF和SVG文件操作
  • Phi-4-reasoning-vision-15B作品分享:教育类APP截图→知识点覆盖度分析+习题推荐
  • 墨语灵犀GPU低功耗部署:Jetson Orin Nano边缘设备运行轻量版实测
  • ️ Python异常处理完全指南:从try-except到自定义异常
  • RF24Network嵌入式无线多跳网络协议栈深度解析
  • hot100--矩阵
  • Memgraph未来路线图:图数据库技术发展趋势与创新方向
  • FlowState Lab硬件资源优化:在有限GPU显存下的部署与推理技巧
  • 如何快速实现MongoDB实时数据同步:mongo-connector完整指南
  • Qwen3-TTS开源语音模型实操指南:GPU低延迟流式合成保姆级教程
  • PAJ7620U2手势识别芯片嵌入式驱动开发实战
  • GHelper:华硕笔记本用户的轻量级硬件控制解决方案
  • 10个fast-agent工作流模式实战:Chain、Parallel、Router完整教程
  • Local AI MusicGen作品集:8-bit游戏音乐生成成果展示
  • 5个技巧让Klipper固件发挥你的3D打印机最大潜力
  • 从原理到应用:寄存器二分频电路在FPGA设计中的5种实际场景
  • CLIP-GmP-ViT-L-14精彩案例:时尚穿搭图-风格关键词(‘Y2K’‘极简’)匹配效果
  • VideoAgentTrek-ScreenFilter模型解释性研究:可视化AI决策过程增强信任