1、介绍
本手册解释了如何使用Open CASCADE Technology (OCCT) Foundation Classes。它提供了关于基础类的基础文档。有关基础类及其应用的高级信息,请参阅我们的电子学习和培训产品。
基础类提供各种通用服务,如自动动态内存管理(通过句柄操纵对象)、集合、异常处理、向下类型转换的泛型和插件创建。
基础课程包括以下内容:
根类是基本的数据类型和类,所有其他类都是基于它们构建的。它们提供:基本类型,如布尔型、字符型、整型或实型,安全处理动态创建的对象,确保自动删除未引用的对象(请参阅Standard_Transient类),可配置的优化内存管理器,提高了密集使用动态创建对象的应用程序的性能,扩展运行时类型信息(RTTI)机制,促进复杂程序的创建,异常管理,C++流的封装。根类主要在Standard和MMgt包中实现。
字符串是处理基于ASCII(正常的8位字符类型)和Unicode(16位字符型)的动态大小的字符序列的类。字符串也可以通过句柄进行操作,从而被共享。字符串在TCollection包中实现。
集合是处理动态大小的数据聚合的类。集合类是通用的,也就是说,它们定义了一个结构和算法,允许保存各种对象,这些对象不一定继承自唯一的根类(类似于C++模板)。当您需要使用给定类型的对象的集合时,必须为该特定类型的元素实例化它。编译此声明后,泛型集合上可用的所有函数都可以在实例化类上使用。
集合包括广泛的泛型类,如运行时大小的数组、列表、堆栈、队列、集合和哈希映射。集合在TCollection和NCollection包中实现。
TColStd包提供了TCollection包中泛型类与Standard包中的对象或TCollection包中的字符串的常用实例化。
向量和矩阵:这些类提供了常用的数学算法和涉及向量和矩阵的基本计算(加法、乘法、换位、求逆等)。
OCCT基本几何类型是基本几何和代数实体的STEP兼容实现。它们提供:基本几何形状描述:点,矢量,线,圆形和圆锥形,平面和基本表面,通过轴或坐标系在空间或平面中定位这些形状,
几何变换对这些形状的定义和应用:平移,旋转,对称,缩放变换,组合的转换,用于代数计算的工具(坐标和矩阵)。
OCCT术常见的数学算法提供了最常用的数学算法的C++实现。其中包括:求解一组线性代数方程的算法,找到一个或多个自变量的函数的最小值的算法,找到一个或一组非线性方程的根的算法,求平方矩阵的本征值和本征向量的算法。
异常处理:提供了常用异常类的层次结构,所有这些都基于类Failure,即异常的根源。例外情况描述了在执行功能过程中可能出现的例外情况。随着异常的出现,程序执行的正常过程将被放弃。针对这种情况执行的操作称为异常处理。
数量:这些是支持日期和时间信息的各种类别,以及代表大多数物理量的基本类型,如长度、面积、体积、质量、密度、重量、温度、压力等。
基础类还包括几个底层服务的实现,这些服务有助于使用Open CASCADE技术创建可定制的、用户友好的应用程序。其中包括:
单位转换工具,提供了处理数量和相关物理单位的统一机制:检查单位兼容性,执行不同单位之间的值转换等(见软件包UnitsAPI);
表达式的基本解释器,便于创建自定义脚本工具、表达式的通用定义等(请参阅包ExprIntrp);
用于处理配置资源文件(请参阅包资源)和可自定义消息文件(请参见包消息)的工具,使其易于在应用程序中提供多语言支持;
进度指示和用户中断界面,即使是低级算法也有可能以通用和方便的方式与用户通信。
2、基础
本章介绍基本服务,如库组织、持久性、数据类型、内存管理、带句柄编程、异常处理、下转换泛型和插件创建。
2.1、整体组织
本章介绍了一些基本概念,这些概念不仅在基类中使用,而且在整个OCCT库中都使用。
模块和工具包
整个OCCT库被组织在一组模块中。第一个模块提供最基本的服务,并被所有其他模块使用,称为基础类,并在本手册中进行了描述。
每个模块主要由一个或多个工具包组成(尽管它也可以包含可执行文件、资源单元等)。从物理上讲,工具包由一个共享库(例如.so或.dll)表示。工具包由一或多个包构建。
包
包将许多具有语义链接的类分组在一起。例如,几何图形包将包含点、线和圆类。包还可以包含枚举、异常和包方法(函数)。在实践中,类名的前缀是其包的名称,例如Geom_Circle。包中描述的数据类型可能包括以下一种或多种数据类型:枚举、对象类、异常、指向其他对象类在包中,两种数据类型不能具有相同的名称。
包的概念
方法要么是函数,要么是过程。函数返回一个对象,而过程仅通过传递参数进行通信。在这两种情况下,当传输的对象是由句柄操纵的实例时,将传递其标识符。有三类方法:
对象构造函数创建所描述类的实例。一个类将有一个或多个具有各种不同参数的对象构造函数,或者没有。 Instance方法对拥有它的实例进行操作。 类方法不适用于单个实例,只适用于类本身。
类
面向对象软件开发中的基本软件组件是类。类是数据类型的实现。它定义了它的行为(函数提供的服务)和表示(类的数据结构——存储数据的字段)。
类分为三类: 普通类。 抽象类。无法实例化抽象类。拥有这样的类的目的是使给定的行为由类的层次结构共享,并依赖于子类的实现。这是一种保证继承行为的特定基础的方法,该基础是基于特定延迟类的所有类所共有的。 模板类。模板类提供了一组操作其他数据类型的函数行为。模板类的实例化要求为其参数给定数据类型。
继承
继承的目的是减少开发工作量。继承机制允许新类在声明时已经包含现有类的特征。然后,这个新类可以快速地针对手头的任务进行专门化。这避免了“从头开始”开发每个组件的必要性。例如,已经开发了一个类BankAccount,你可以快速地专门化新类:SavingsAccount、LongTermDepositAccount、MoneyMarketAccount、RevolvingCreditAccount等。
由此推论,当两个或多个类继承自父(或祖先)类时,所有这些类都至少保证其父(或祖先)的行为。例如,如果父类BankAccount包含方法Print,该方法告诉它打印自己,那么它的所有子类都保证提供相同的服务。
确保使用继承的一种方法是,在层次结构的顶部将类声明为抽象类。在这些类中,方法没有被实现。这迫使用户创建一个重新定义这些方法的新类。这是保证后代类之间某种最小行为的一种方法。
2.2、数据类型
面向对象的语言围绕数据类型而不是围绕对这些数据执行的操作来构建系统。在这种情况下,对象是数据类型的实例,其定义决定了如何使用它。每个数据类型都由一个或多个类实现,这些类构成了系统的基本元素。
Open CASCADE Technology中的数据类型分为两类:由句柄(或引用)操纵的数据类型;由值操纵的数据类型。
数据类型被实现为一个类。该类不仅定义了其数据表示和实例上可用的方法,而且还建议了如何操作实例。
由值操纵的类型的变量包含实例本身。
由句柄操纵的类型的变量包含对实例的引用。由值操纵的类型的第一个示例是预定义的基元类型:布尔、字符、整数、实数等。
由句柄操作的类型的变量,如果未附加到对象,则称为null。为了引用一个对象,我们用它的一个构造函数实例化这个类.
Handle(myClass) m = new myClass;
在Open CASCADE Technology中,Handles是特定的类,用于通过引用安全地操作动态内存中分配的对象,提供引用计数机制,并在对象未被引用时自动销毁对象。
原始类型在语言中是预定义的,并且它们按值进行操作。Boolean 用于表示逻辑数据。它只能有两个值:Standard_True 和 Standard_False。Character 表示任何 ASCII 字符。ExtCharacter 是一种扩展字符。Integer 是整数。Real 表示实数(即,一个具有整数部分和小数部分的数,其中任何一个都可能为空)。ShortReal 是一种具有更小值和内存大小的实数。CString 用于文字常量。ExtString 是一种扩展字符串。Address 表示未确定大小的字节地址。
标准包中描述了每种类型提供的服务。下表显示了C++基本类型和OCCT基元类型之间存在的等价性。
int | Standard_Integer |
double | Standard_Real |
float | Standard_ShortReal |
bool | Standard_Boolean |
char | Standard_Character |
char16_t | Standard_ExtCharacter |
char* | Standard_CString |
void* | Standard_Address |
char16_t* | Standard_ExtString |
Standard_Integer:表示产生负值、正值或零值的32位整数的基本类型。Integer被实现为C++int基本类型的typedef。因此,代数运算+,-,*,/以及排序和等价关系<,<=,==,!=,>=,>定义在上面。
Standard_Real:表示具有有限精度和有限大小的实数的基本类型。Real被实现为C++双精度(double precision)基本类型的typedef。因此,代数运算+,-,*,/,一元-以及排序和等价关系<,<=,==,!=,>=,>是在reals上定义的。
Standard_ShortReal:表示具有有限精度和有限大小的实数的基本类型。ShortReal被实现为C++浮点(简单精度)基本类型的typedef。因此,代数运算+,-,*,/,一元-以及排序和等价关系<,<=,==,!=,>=,>是在reals上定义的。
Standard_Boolean:表示逻辑表达式的基本类型。它有两个值:false和true。Boolean被实现为C++布尔基本类型的typedef。因此,代数运算和,或,xor和not以及等价关系==和!=在布尔上定义。
Standard_Character:表示标准化ASCII字符集的基本类型。它可以被分配128个ASCII字符的值。字符被实现为C++字符基本类型的typedef。因此,排序和等价关系<,<=,==,!=,>=,>使用ASCII图表的顺序在字符上定义(例如:A B)。
Standard_ExtCharacter:表示Unicode字符集的基本类型。它是一种16位字符类型。ExtCharacter被实现为C++char16_t基本类型的typedef。因此,排序和等价关系<,<=,==,!=,>=,>使用UNICODE图表的顺序在扩展字符上定义(例如:A B)。
Standard_CString:表示字符串文字的基本类型。字符串文字是用双引号括起来的UTF-8(8位)代码点序列。CString被实现为C++字符基本类型的typedef。
Standard_Address:表示通用指针的基本类型。地址被实现为C++void基本类型的typedef。
Standard_ExtString是将字符串文字表示为Unicode(16位)字符序列的基本类型。ExtString被实现为C++char16_t基本类型的typedef。
有三种类型由值操纵:原始类型、枚举类型、由不直接或间接继承自 Standard_Transient 的类定义的类型。由值操纵的类型比由句柄操纵的类型表现得更直接,因此可以预期执行操作的速度更快,但它们不能独立存储在文件中。
什么时候需要使用手柄?
当你设计一个对象时,很难选择如何操纵该对象:通过值还是通过句柄。以下想法可以帮助你做出决定:
如果你的对象在应用程序中可能有很长的生命周期,并且你想对它进行多次引用,那么最好使用句柄来操纵这个对象。对象的内存将在堆上分配。指向该内存的句柄是一个轻量级对象,可以快速传递到参数中。这避免了复制大型对象的惩罚。
如果你的对象有有限的生存期,例如,在单个算法中使用,最好按值操纵这个对象,不考虑它的尺寸,因为这个对象是在堆栈上分配的,内存的分配和释放非常快,这避免了在堆上分配时隐式调用new和delete。
最后,如果一个对象在应用程序的整个生命周期中只会被创建一次,但会一直存在,那么最好的选择可能是由句柄操纵的类或声明为全局变量的值。
2.3、使用handle编程
2.4、内存管理
在工作中,几何建模应用创建和删除大量在动态内存(堆)中分配的C++对象。在这种情况下,用于分配和释放内存的标准函数的性能可能不足。因此,Open CASCADE Technology 采用 Standard 包中实现的一个专用内存管理器。
内存管理器基于以下原则:小内存数组被组合成集群,然后回收(集群从不释放给系统),大数组通过系统的标准函数进行分配和释放(当它们不再使用时,这些数组被释放给系统)。作为一般规则,建议通过大块分配内存。这样,用户可以处理连续数据的块,并简化内存页管理器处理。
内存管理器的使用
要使用Open CASCADE Technology内存管理器在C代码中分配内存,只需使用方法Standard::allocate代替malloc,使用方法Standard::Free代替Free。此外,还提供了方法Standard::Reallocate来替换C函数realloc。
在C++中,可以定义类的运算符new和delete,以便使用Standard::allocate分配内存,并使用Standard:Free释放内存。在这种情况下,该类的所有对象和所有继承的类都将使用OCCT内存管理器进行分配。
标头STANDARD_DefineAlloc.hxx提供的预处理器宏DEFINE_STANDARD_ALLOC以这种方式定义new和delete。它用于所有OCCT类(除了少数例外),因此这些类是使用OCCT内存管理器分配的。由于运算符new和delete是继承的,因此对于从OCCT类派生的任何类也是如此,例如,对于从Standard_Transient派生的所有类也是如此。
请注意,为继承Standard_Transient的类重新定义new()和delete()函数是可能的(尽管不建议使用,除非确实不可避免)。如果这样做了,还应该重新定义方法Delete(),以便将运算符Delete应用于该指针。这将确保调用适当的delete()函数,即使对象是由基类的句柄操作的。
如何配置内存管理器
OCCT内存管理器可以被配置为将不同的优化技术应用于不同的内存块(取决于它们的大小),或者甚至避免任何优化并直接使用C函数malloc()和free()。配置由以下环境变量的数值定义:
MMGT_OPT:如果设置为0(默认值),则直接在C内存堆中分配每个内存块(通过malloc()和free()函数)。在这种情况下,将忽略除MMGT_CLEAR之外的所有其他选项;如果设置为1,则存储器管理器执行如下所述的优化;如果设置为2,则使用“英特尔®;TBB优化内存管理器”。
MMGT_CLEAR:如果设置为1(默认),则每个分配的内存块被清零;如果设置为0,则按原样返回内存块。
MMGT_CELLSIZE:定义在大型内存池中分配的块的最大大小。默认值为200。
MMGT_NBPAGES:定义为页面中的小块分配的内存块的大小(取决于操作系统)。默认值为1000。
MMGT_THRESHOLD:定义在内部回收而不是返回到堆的块的最大大小。默认值为40000。
MMGT_MMAP:设置为1(默认)时,使用操作系统的内存映射功能分配大内存块;如果设置为0,它们将由malloc()在C堆中分配。
优化技术
当MMGT_OPT设置为1时,将使用以下优化技术:
大小小于MMGT_CELLSIZE的小块不会单独分配。相反,分配了大量内存池(每个池的大小为MMGT_NBPAGES页)。每个新的内存块都被安排在当前池的一个备用位置。当当前内存池被完全占用时,将分配下一个内存池,依此类推。
在当前版本中,内存池永远不会返回到系统(直到进程结束)。但是,通过方法Standard::Free释放的内存块会被记住在空闲列表中,然后在分配下一个相同大小的块时重用(回收)。
大小大于MMGT_CELLSIZE但小于MMGT_THRESHOLD的中型块直接在C堆中分配(使用malloc()和free())。当这些块通过方法Standard::Free()释放时,它们就像小块一样被回收。
但是,与小块不同,空闲列表中包含的回收介质块(即由程序释放但由内存管理器持有)可以通过方法Standard::Purge()返回到堆中。
大小大于MMGT_THRESHOLD的大块,包括用于小块的内存池,根据MMGT_MMAP的值进行分配:如果为0,则这些块在C堆中分配;否则,它们是使用管理内存映射文件的操作系统特定功能来分配的。当调用Standard::Free()时,大块会立即返回到系统。
利弊
OCCT内存管理器的主要优点是它可以回收中小型块,当应用程序不断分配和释放多个大小相似的内存块时,它可以使应用程序更快地工作。在实际情况下,应用程序性能的实际增益可能高达50%。
相关的缺点是在程序执行期间回收的内存不会返回到操作系统。这可能会导致相当大的内存消耗,甚至被误解为内存泄漏。为了最大限度地减少这种影响,有必要在完成内存密集型操作后调用方法Standard::Purge。
OCCT内存管理器引起的管理费用包括:每个分配的内存块的大小四舍五入到8个字节(当MMGT_OPT为0(默认值)时,四舍五舍五入由CRT定义;32位平台的典型值为4字节)
仅当MMGT_OPT为1时,在每个存储器块的开头分配额外的4个字节(或在64位平台上为8个)以保持其大小(或在空闲列表中循环时的下一个空闲存储器块的地址)。
请注意,这些开销可能大于或小于C堆内存管理器引起的开销,因此在优化模式或标准模式下,总体内存消耗可能更大,具体取决于环境。
一般来说,建议通过有效块来分配内存。通过这种方式,您可以处理连续数据块,并且便于内存页面管理器进行处理。
OCCT内存管理器使用互斥锁来锁定对空闲列表的访问,因此在不同线程经常同时调用内存管理器的情况下,它的性能可能低于非优化模式。原因是malloc()和free()的现代实现使用了几个分配领域,从而避免了等待互斥释放的延迟,这在这种情况下是可能的。
2.5、异常处理
任何对象的行为都是通过在其类声明中定义的方法来实现的。这些方法的定义不仅包括它们的签名(即它们的编程接口),还包括它们的有效域。
这个有效域是通过异常来表达的。在各种错误条件下会抛出异常,以保护软件质量。
异常处理提供了一种手段,可以将控制权从正在执行的程序中的给定点转移到与之前执行的另一个点相关联的异常处理程序。
一个方法可能会抛出一个异常,这会中断其正常执行,并将控制权转移到捕获此异常的处理程序。
提供了一套常用的异常类层次结构。其根类是Standard包中的Standard_Failure。因此,每个异常都直接或通过继承另一个异常来继承自Standard_Failure。异常类列出了任何OCCT函数可能抛出的所有异常。
Open CASCADE Technology还提供了将系统信号(如访问违规或除以零)转换为异常的支持,以便可以使用相同的统一方法来安全地处理这些情况。
然而,为了在各种平台上支持此功能,会使用一些特殊的方法和变通方法。尽管实现细节是隐藏的,并且处理OCCT异常的方式基本上与处理C++异常相同,但仍需考虑这种方法的某些特殊性,并必须遵守一些规则。
以下段落描述了在使用Open CASCADE Technology时处理异常的推荐方法。
抛出异常
“类似C++”的语法
要抛出特定类型的异常,应使用相应异常类的Raise()
方法。
常规用法
异常不应作为编程技巧使用,例如替代“goto”语句,而应作为保护方法免受误用的方式。调用者必须确保其条件符合方法能够处理的要求。
因此,
在应用程序的正常执行过程中,不应抛出任何异常。
可能抛出异常的方法应受到其他方法的保护,以便调用者可以检查调用的有效性。
例如,考虑使用TCollection_Array1
类时:
使用Value
函数来提取元素。
使用Lower
函数来提取数组的下界。
使用Upper
函数来提取数组的上界。
在这些情况下,Value
函数可能会抛出一个异常,如果索引超出了由Lower
和Upper
函数定义的数组界限。因此,调用Value
函数之前,调用者应先检查索引的有效性,以避免异常的发生。这样的设计既保护了Value
函数免受误用,也提高了程序的健壮性和可维护性。
处理异常
当抛出异常时,控制权会转移到调用栈中最近且尚未退出的、与给定类型匹配的处理器,即:最近进入且尚未退出的try
块所对应的处理器,其类型与抛出表达式匹配的处理器。
如果满足以下条件之一,则类型为T
的处理器与抛出表达式中的异常类型E
匹配:
T
和E
是同一类型,或者T
是E
的超类型。
为了将系统信号作为异常处理,请确保在相关代码的开始处某处插入宏OCC_CATCH_SIGNALS
。建议的位置是在try {}
块的大括号之后的第一条语句。
2.6、插件管理
插件分发
插件是一种可以动态加载到客户端应用程序中的组件,而无需直接与之链接。插件与其客户端应用程序是解耦的,即插件只知道其连接机制是如何定义的以及如何调用相应的服务。
插件可以用于:
实现驱动机制,即根据当前事务动态更改驱动程序的实现(例如,从另一个版本的应用程序中检索存储的文档)。
将处理资源限制在所需的最小范围内(例如,只要用户不需要,就不会在运行时加载任何应用程序服务)。
促进模块化开发(应用程序可以仅具备基础功能交付,而当可用时,一些高级功能可以作为插件添加)。
插件通过全局唯一标识符(GUID)进行识别。GUID包含小写字符,且不能以空格结尾。
一旦加载,对插件提供服务的调用就是直接的(客户端和插件使用相同的编程语言实现)。
C++ 插件实现
在C++中,插件实现了一种服务,这种服务被定义为一个对象,该对象包含在一个抽象类中定义的函数(这个抽象类及其带有GUID的父类是客户端应用程序中关于插件实现的唯一信息)。插件由一个共享库组成,其中包含一个名为Factory的方法,该方法用于创建C++对象(客户端无法直接实例化这个对象,因为插件的实现是不可见的)。
在OCCT(或其他支持插件的框架)中,基础类库通常会提供一个名为Plugin的包,该包中包含一个名为Load()的方法。这个方法允许客户端通过库来访问所需的服务。Load()方法负责加载插件共享库,并通过Factory方法(或类似机制)获取插件服务的实例。这样,客户端就可以在不直接链接到插件代码的情况下,间接地调用插件提供的服务。
简而言之,C++插件的实现依赖于抽象类、共享库以及一个Factory模式来创建服务对象。客户端通过框架提供的加载机制来访问这些服务,而无需知道插件的具体实现细节。
3、集合、字符串、数量和单位转换
3.1、集合
Collections组件包含处理动态大小数据聚合的类。它们包括各种集合,如数组、列表和映射。
Collections类是泛型的(类似于C++模板),也就是说,它们定义了一种结构和算法,可以容纳各种对象,这些对象并不一定继承自一个唯一的根类(类似于C++模板)。
当你需要使用特定类型对象的集合时,你必须为该特定类型的元素实例化它。一旦这个声明被编译,泛型集合上可用的所有函数都会在你的实例化类上可用。
然而,请注意:
在OCCT公共语法中直接用作参数的每个集合都在OCCT组件中实例化。
TColStd包(标准对象集合组件)提供了这些泛型集合的许多实例化,这些实例化使用了来自Standard包或Strings组件的对象。Collections组件提供了广泛的泛型集合:
数组通常用于快速访问项目,但数组是固定大小的数据聚合。
序列是可变大小的结构,它们避免了使用大而几乎为空的数组。访问序列项比访问数组项要慢:只有顺序探索是有效的(但序列不适用于多次探索)。数组和序列通常用作更复杂对象的数据结构。
映射是动态结构,其大小不断适应插入的项数,并且访问项的速度最快。映射结构通常用于多次探索的情况:它们通常是复杂算法的内部数据结构。
列表类似于序列,但探索它们的算法不同。
序列和映射有特定的迭代器。
总结来说,OCCT(Open CASCADE Technology)中的Collections组件提供了一系列用于处理动态大小数据聚合的类,这些类覆盖了多种数据结构,如数组、序列、列表、映射等,旨在支持复杂对象的基础数据结构需求。以下是这些类的主要特点和用途的总结:
- 数组(Array1, Array2):
- 一维(Array1)和二维(Array2)数组,类似于C语言中的数组,具有固定大小但在构造时动态指定大小。
- 访问时间恒定,与数组大小无关。
- 数组索引从用户定义的位置开始和结束,访问元素时需基于数组的下界和上界。
- 句柄数组(HArray1, HArray2):
- 与Array1和Array2类似,但HArray1和HArray2是数组的句柄,允许多个对象共享同一数组。
- 提供了间接访问数组的方式,使得数组可以被多个对象安全地共享和修改。
- 序列(Sequence, HSequence):
- 序列是索引由整数表示的项序列,是可变大小的结构,避免了使用大且几乎为空的数组。
- HSequence是序列的句柄,允许多个对象共享同一序列。
- 序列适合顺序访问,但不适合多次算法探索;映射更适合后者。
- 列表(List):
- 有序的非唯一对象列表,可以通过迭代器顺序访问。
- 插入操作快速,但按值搜索可能较慢,特别是当列表较长时。
- 映射(Map):
- 映射是动态扩展的数据结构,通过键快速访问数据。
- 提供了多种映射类型,如基本映射(TCollection_Map)、数据映射(TCollection_DataMap)、双键映射(TCollection_DoubleMap)等。
- 映射的大小动态调整,避免了大而空的数组的使用。
- 映射的访问时间通常比序列、列表等快,接近于数组。
- 迭代器(Iterator):
- 提供了多种迭代器类,如基本映射迭代器(TCollection_BasicMapIterator)、数据映射迭代器(TCollection_DataMapIterator)等,用于遍历和访问集合中的数据。
- 特定用途的数据结构:
- 如索引映射(IndexedMap, IndexedDataMap)等,结合了数组和映射的优点,既可以通过索引也可以通过键访问数据。
- 队列(Queue)和栈(Stack)是特定访问模式的列表。
- 哈希器(Hasher):
- 用于映射中的键的哈希计算,定义了哈希码生成和键比较的函数。
- 某些情况下,可以直接使用字符串类(如TCollection_AsciiString)作为哈希器。
总的来说,OCCT的Collections组件提供了一套丰富且灵活的数据结构,支持从简单数组到复杂映射的多种需求,为复杂对象的实现提供了坚实的基础。
3.2、标准对象集合
TCollection包中的泛型类是描述每种集合通用目的的根类,而实际使用的类是从TColStd包中提取的。TColStd包和TShort包提供了泛型类的常用实例化,这些实例化使用了来自Standard包的对象或来自TCollection包的字符串。
这些实例化包括:
一维数组:使用Standard对象和TCollection字符串对TCollection_Array1泛型类进行实例化。
二维数组:使用Standard对象对TCollection_Array2泛型类进行实例化。
通过句柄操作的一维数组:使用Standard对象和TCollection字符串对TCollection_HArray1泛型类进行实例化。
通过句柄操作的二维数组:使用Standard对象对TCollection_HArray2泛型类进行实例化。
序列:使用Standard对象和TCollection字符串对TCollection_Sequence泛型类进行实例化。
通过句柄操作的序列:使用Standard对象和TCollection字符串对TCollection_HSequence泛型类进行实例化。
列表:使用Standard对象对TCollection_List泛型类进行实例化。
队列:使用Standard对象对TCollection_Queue泛型类进行实例化。
集合:使用Standard对象对TCollection_Set泛型类进行实例化。
通过句柄操作的集合:使用Standard对象对TCollection_HSet泛型类进行实例化。
栈:使用Standard对象对TCollection_Stack泛型类进行实例化。
映射键的哈希器:使用Standard对象对TCollection_MapHasher泛型类进行实例化。
基本哈希映射:使用Standard对象对TCollection_Map泛型类进行实例化。
带有附加项的哈希映射:使用Standard对象对TCollection_DataMap泛型类进行实例化。
基本索引映射:使用Standard对象对TCollection_IndexedMap泛型类进行实例化。
带有附加项的索引映射:使用Standard_Transient对象对TCollection_IndexedDataMap泛型类进行实例化。
TColStd_PackedMapOfInteger类提供了整数映射的替代实现,该实现针对性能和内存使用进行了优化(它使用位标志来编码整数,从而在最优情况下每存储32个整数仅消耗24字节)。此类还提供了作为整数集的映射的布尔运算(并集、交集、差集、差异、检查相等性和包含性)。
3.3、NCollection
NCollection包提供了一套模板集合类,这些类在Open CASCADE Technology (OCCT)中被广泛使用。这些类的宏定义存储在NCollection_Define*.hxx文件中。尽管这些定义现在已经被视为过时,但仍然可以使用,特别是为了与现有代码保持兼容性。
在OCCT(Open CASCADE Technology)中,提供了一系列标准集合类,这些类覆盖了数组、序列、列表以及映射等多种数据结构,以满足不同的编程需求。以下是对这些集合类的详细解释:
-
NCollection_Array1 - 固定大小的一维数组,初始化时确定大小。值得注意的是,数组的索引可以从任意值开始,但通常从1开始。
-
NCollection_Array2 - 固定大小的二维数组,同样在初始化时确定大小,索引也可以从任意值开始,但通常从1开始。
-
NCollection_List - 简单的列表,提供元素的线性序列。
-
NCollection_Sequence - 索引访问的双链表。与NCollection_Array1不同,NCollection_Sequence的大小是动态的,可以随着元素的添加和删除而改变。索引也是从1开始。
-
这些类提供了STL风格的迭代器(通过
begin()
和end()
方法),因此可以在STL算法中使用。
OCCT提供了几种基于哈希的快速搜索映射类:
-
NCollection_Map - 哈希集合,用于存储唯一元素,不保留元素的插入顺序。
-
NCollection_IndexedMap - 带有预定义元素顺序的集合,允许通过索引或值(基于哈希)快速访问。
-
NCollection_DataMap - 哈希映射,用于存储键值对,允许通过键快速检索值。
-
NCollection_IndexedDataMap - 带有预定义元素顺序的映射,允许通过索引或值(基于哈希)快速访问。
-
NCollection_DoubleMap - 双边哈希映射,使用两个键。
OCCT还提供了以下四种模板集合类:
-
NCollection_Vector - 动态数组,类似于STL中的
std::vector
,提供了连续的内存存储和快速的随机访问。 -
NCollection_UBTree - 平衡树(如红黑树)的实现,用于维护有序元素的集合,支持高效的插入、删除和查找操作。
-
NCollection_SparseArray - 稀疏数组,用于高效地存储和管理大量数据中只有少数非零元素的情况。
-
NCollection_CellFilter - 通常不直接用作集合类型,但可能用于过滤或选择集合中的特定元素。它可能是一个工具类,用于根据特定条件从集合中选择或过滤元素。
这些集合类为OCCT应用程序提供了强大的数据结构支持,使得开发者能够高效地处理和管理大量数据。
3.4、字符串
字符串类是处理基于ASCII/Unicode UTF-8(普通8位字符类型)和UTF-16/UCS-2(16位字符类型)的动态大小字符序列的类。它们提供了带有内置内存管理的编辑操作,这使得相关对象比普通字符数组更容易使用。
字符串类提供以下服务来操作字符字符串:
使用内置字符串管理器对字符串对象进行编辑操作。
处理动态大小的字符序列。
与ASCII和UTF-8字符串进行转换。
字符串也可以通过句柄进行操作,因此可以共享。
示例:TCollection_AsciiString:一个可变长度的ASCII字符序列(普通8位字符类型)。它提供了带有内置内存管理的编辑操作,使得AsciiString对象比普通字符数组更容易使用。AsciiString对象遵循值语义,即它们是实际的字符串,而不是字符串的句柄,并通过赋值进行复制。您可以使用HAsciiString对象来获取字符串的句柄。
TCollection_ExtendedString:一个可变长度的“扩展”(UNICODE)字符序列(16位字符类型)。它提供了带有内置内存管理的编辑操作,使得ExtendedString对象比普通扩展字符数组更容易使用。ExtendedString对象遵循值语义,即它们是实际的字符串,而不是字符串的句柄,并通过赋值进行复制。您可以使用HExtendedString对象来获取字符串的句柄。
TCollection_HAsciiString:一个可变长度的ASCII字符序列(普通8位字符类型)。它提供了带有内置内存管理的编辑操作,使得HAsciiString对象比普通字符数组更容易使用。HAsciiString对象是字符串的句柄。HAsciiString字符串可以由多个对象共享。您可以使用AsciiString对象来获取实际的字符串。HAsciiString对象使用AsciiString字符串作为字段。
TCollection_HExtendedString:一个可变长度的“扩展”(UNICODE)字符序列(16位字符类型)。它提供了带有内置内存管理的编辑操作,使得ExtendedString对象比普通扩展字符数组更容易使用。HExtendedString对象是字符串的句柄。HExtendedString字符串可以由多个对象共享。您可以使用ExtendedString对象来获取实际的字符串。HExtendedString对象使用ExtendedString字符串作为字段。
转换:Resource_Unicode提供了函数,用于将给定为ANSI、EUC、GB或SJIS格式的非ASCII C字符串转换为扩展字符的Unicode字符串,反之亦然。
3.5、数量
数量(Quantities)是支持日期和时间信息以及各种表示大多数物理量(如长度、面积、体积、质量、密度、重量、温度、压力等)的基本类型的不同类别。
数量类提供以下服务:
定义表示大多数数学和物理量的基本类型;
单位转换工具,提供处理数量和相关物理单位的统一机制:检查单位兼容性、在不同单位之间进行值转换等(参见UnitsAPI包);
管理时间信息(如日期和时间段)的资源;
管理颜色定义的资源。
数学量以名称和值(实数)为特征。
物理量以名称、值(实数)和单位为特征。单位可以是符合国际单位制(SI)的国际单位,也可以是用户定义的单位。单位由物理量的用户管理。
数学量和物理量都作为实数值进行操作,这意味着:
它们被定义为实数值的别名,因此Standard_Real类提供的所有函数都可用于每个量。
可以在涉及实数值的数学或物理公式中混合使用多个物理量。
数量包包含所有常用的基本物理量。
3.6、单位转换
UnitsAPI全局函数用于将某个单位中的值转换为另一个单位中的值。转换在三个单位系统之间进行:
国际单位制(SI)系统,
用户的本地系统,
用户的当前系统。国际单位制(SI)是标准的国际单位系统。在UnitsAPI函数的签名中用SI表示。
OCCT(前MDTV)系统对应于国际单位制(SI)标准,但其长度单位及其所有派生单位使用毫米而不是米。
这两个系统都是由Open CASCADE Technology提出的;国际单位制(SI)系统是标准选项。通过选择这两个系统中的一个,您可以通过SetLocalSystem函数定义您的本地系统。本地系统在UnitsAPI函数的签名中用LS表示。本地系统的单位可以在工作环境中进行修改。您通过SetCurrentUnit函数修改其单位来定义您的当前系统。当前系统在UnitsAPI函数的签名中用Current表示。物理量由字符串定义(例如:LENGTH)。
4、数学基元与算法
OCCT的可用的数学原语和算法包括:向量和矩阵;几何图元;数学算法。
4.1、向量和矩阵
矢量和矩阵组件提供了基本类型矢量和矩阵的C++实现,它们通常用于定义更复杂的数据结构。Vector和Matrix类提供常用的数学算法,包括:涉及向量和矩阵的基本计算;平方矩阵的特征值和特征向量的计算;一组线性代数方程的求解器;找出一组非线性方程的根的算法;找到一个或多个自变量的最小函数的算法。
这些类还提供了一个数据结构来表示数学中使用的任何表达式、关系或函数,包括变量的赋值。
向量和矩阵具有任意范围,这些范围必须在声明时定义,并且在声明后不能更改。
math_Vector v(1, 3);
// a vector of dimension 3 with range (1..3)
math_Matrix m(0, 2, 0, 2);
// a matrix of dimension 3x3 with range (0..2, 0..2)
math_Vector v(N1, N2);
// a vector of dimension N2-N1+1 with range (N1..N2)
Vector和Matrix对象使用值语义。换言之,它们不能共享,而是通过分配进行复制。
math_Vector v1(1, 3), v2(0, 2);
v2 = v1;
// v1 is copied into v2. a modification of v1 does not affect v2
可以使用必须位于矢量或矩阵的范围定义内的索引来初始化和获得矢量和矩阵值。
math_Vector v(1, 3);
math_Matrix m(1, 3, 1, 3);
Standard_Real value;
v(2) = 1.0;
value = v(1);
m(1, 3) = 1.0;
value = m(2, 2);
对Vector和Matrix对象的某些操作可能不合法。在这种情况下,会引发一个异常。使用了两种标准例外情况:
当运算中涉及的两个矩阵或向量的维度不兼容时,会引发Standard_DimensionError异常。
如果尝试在矢量或矩阵的范围定义之外进行访问,则引发Standard_RangeError异常。
math_Vector v1(1, 3), v2(1, 2), v3(0, 2);
v1 = v2;
// error: Standard_DimensionError is raised
v1 = v3;
// OK: ranges are not equal but dimensions are
// compatible
v1(0) = 2.0;
// error: Standard_RangeError is raised
4.2、基本几何类型
Open CASCADE Technology原始几何类型是基本几何和代数实体的STEP兼容实现。它们提供:原始几何形状的描述,例如:点;向量;线;圆和二次曲线;平面和基本曲面;通过轴或坐标系将这些形状定位在空间或平面中;
这些形状的几何变换的定义和应用:移动;旋转;对称;缩放变换;组合变换;代数计算工具(坐标和矩阵)。
所有这些功能都由几何处理器包gp提供。它的二维和三维对象的类由值而不是引用处理。当复制这类对象时,它会被完全复制。一个实例中的更改不会反映在另一个实例中。
gp包定义了用于代数计算和二维和三维空间中的基本解析几何的基本几何实体。它还提供了基本变换,如身份、旋转、平移、镜像、缩放变换、变换组合等。实体由值处理。
请注意,gp曲线和曲面是解析的:gp实体没有参数化和方向,即这些实体不提供使用这些属性的函数。
如果你需要,你可以使用Geom(在3D空间中)和Geom2d(在平面中)提供的更复杂的数据结构。但是,gp实体的定义与Geom和Geom2d等效实体的定义相同,它们位于具有相同定位系统的平面或空间中。它们隐含地包含方向,它们在Geom和Geom2d实体上表达,并且它们诱导了它们的参数化的定义。
因此,很容易对gp曲线和曲面进行隐式参数化,这是等效Geom或Geom2d实体的参数化。在计算投影或交点时,或者在涉及复杂算法的操作中,这种特性特别有用,在这些操作中,操纵最简单的数据结构(即gp的数据结构)尤为重要。因此,ElCLib和ElSLib包提供了计算以下内容的函数:2D或3D gp曲线上的参数u点,gp 基本曲面上的参数点 (u,v),以及此时任何导矢量。
注意:当 gp 实体位于更复杂的数据结构中时,它们不能被共享。
4.3、原始几何类型集合
在创建几何对象之前,您必须确定您是在二维还是三维环境中,以及您想如何处理该对象。如果您不需要单个几何图元实例,而是一组几何图元,那么处理此类对象集合的包TColgp将提供必要的功能。特别是,该包提供了通用类与几何对象的常用实例,即XY、XYZ、Pnt、Pnt2d、Vec、Vec2d、Lin、Lin2d、Circ、Circ2d。
4.4、基本几何库
有各种库包可供选择,它们提供了对曲线和曲面的各种基本计算。如果您处理的是gp包创建的对象,则有用的算法在基本曲线和曲面库中——ElCLib和ElSLib包。
EICLib提供了分析曲线的方法。这是一个来自gp包(直线、圆和二次曲线)的曲线简单计算的库。可以计算给定参数的点或计算点的参数。
EISLib为分析曲面提供了方法。这是一个对gp包中的曲面(平面、圆柱、球面、圆锥、圆环)进行简单计算的库。可以使用给定的参数对点进行计算,也可以为点计算参数。有一个用于计算曲线和曲面法线的库。
此外,Bnd包提供了一组类和工具,用于操作2d和3d空间中几何对象的边界框。
4.5、常见数学算法
通用数学算法库提供了最常用的数学算法的C++实现。这些包括:求解一组线性代数方程的算法,用于找到一个或多个独立变量的函数最小值的算法,用于求非线性方程组根的算法,一种找到方阵特征值和特征向量的算法。
所有数学算法都是使用相同的原理实现的。它们包含:构造函数,在给出适当的参数的情况下执行所有或大部分计算。所有相关信息都存储在结果对象中,以便所有后续计算或询问将以最有效的方式得到解决。
如果计算成功,则函数IsDone返回布尔值true。一组特定于每种算法的函数,能够获得各种结果。只有当函数IsDone的答案为true时,调用这些函数才是合法的,否则将引发异常StdFail_NotDone。
下面的示例演示了高斯类(Gauss class)的使用,该类实现了一组线性方程的高斯解。以下定义摘自类 math_Gauss 的头文件
class Gauss {
public:
Gauss (const math_Matrix& A);
Standard_Boolean IsDone() const;
void Solve (const math_Vector& B,
math_Vector& X) const;
};
现在主程序使用高斯类来求解方程a*x1=b1和a*x2=b2:
#include <math_Vector.hxx>
#include <math_Matrix.hxx>
main ()
{
math_Vector a(1, 3, 1, 3);
math_Vector b1(1, 3), b2(1, 3);
math_Vector x1(1, 3), x2(1, 3);
// a, b1 and b2 are set here to the appropriate values
math_Gauss sol(a); // computation of the
// LU decomposition of A
if(sol.IsDone()) { // is it OK ?
sol.Solve(b1, x1); // yes, so compute x1
sol.Solve(b2, x2); // then x2
...
}
else { // it is not OK:
// fix up
sol.Solve(b1, x1); // error:
// StdFail_NotDone is raised
}
}
下一个例子演示了BissecNewton类的使用,该类实现了牛顿和Bissection算法的组合,以找到已知位于两个边界之间的函数的根。该定义是math_BissecNewton类头文件的摘录:
class BissecNewton {
public:
BissecNewton (math_FunctionWithDerivative& f,
const Standard_Real bound1,
const Standard_Real bound2,
const Standard_Real tolx);
Standard_Boolean IsDone() const;
Standard_Real Root();
};
抽象类 math_FunctionWithDerivative 描述了 BissecNewton 算法使用的函数 f 必须实现的服务。以下定义对应于抽象类 math_FunctionWithDerivative 的头文件
现在测试样本使用 BissecNewton 类在区间 [1.5, 2.5] 中找到方程 f(x)=x**2-4 的根:求解的函数在类 myFunction 中实现,该类继承自类 math_FunctionWithDerivative,然后主程序找到所需的根。
class myFunction : public math_FunctionWithDerivative
{
Standard_Real coefa, coefb, coefc;
public:
myFunction (const Standard_Real a, const Standard_Real b,
const Standard_Real c) :
coefa(a), coefb(b), coefc(c)
{}
virtual Standard_Boolean Value (const Standard_Real x,
Standard_Real& f)
{
f = coefa * x * x + coefb * x + coefc;
}
virtual Standard_Boolean Derivative (const Standard_Real x,
Standard_Real& d)
{
d = coefa * x * 2.0 + coefb;
}
virtual Standard_Boolean Values (const Standard_Real x,
Standard_Real& f, Standard_Real& d)
{
f = coefa * x * x + coefb * x + coefc;
d = coefa * x * 2.0 + coefb;
}
};
main()
{
myFunction f(1.0, 0.0, 4.0);
math_BissecNewton sol(F, 1.5, 2.5, 0.000001);
if(Sol.IsDone()) { // is it OK ?
Standard_Real x = sol.Root(); // yes.
}
else { // no
}
4.6、精度
在OCCT平台上,数据库中存储的每个对象都应该有自己的精度值。在处理从其他系统导入对象以及各种相关精度值的系统时,这一点非常重要。
Precision包解决了几何算法开发人员每天遇到的问题:使用什么精度设置来比较两个数字。 实数等价显然是一个糟糕的选择。 数字之间的差异应该与给定的精度设置进行比较。
如果(X1 == X2),则不写入,而是写入if(Abs(X1-X2)<Precision)。
此外,要比较实数,请记住,如果(X1 < X2 - Precision)不正确。如果(X2 - X1 > Precision),当X1和X2是高数字时,情况要好得多。
此程序包提出了一组方法,为最常见的情况提供精度设置。
在Open CASCADE Technology中,精度通常不是隐式的;低级几何算法将精度设置作为参数接受。通常这些不应该直接引用此包。
高级建模算法必须为其调用的低级几何算法提供精度设置。一种方法是使用此包提供的设置。高级建模算法也可以有自己的精度管理策略。例如,拓扑数据结构存储精度值,这些值稍后由算法使用。创建新拓扑时,它会采用存储的值。此包提供的不同精度设置涵盖了交集和近似等几何算法的最常见需求。精度值的选择取决于算法和几何空间。几何空间可以是:
真实空间,3d或2d,长度以米、微米、英寸等为单位。
参数空间,在曲线上为1d,在曲面上为2d,其中数字没有维度。参数空间精度值的选择不仅取决于机器的精度,还取决于曲线或曲面的维度。这是因为需要将参数精度与实际精度联系起来。如果你在由方程P(t)定义的曲线上,你会希望在以下两者之间具有等价性
Abs(t1-t2) < ParametricPrecision
Distance (P(t1),P(t2)) < RealPrecision.
Precision包提供了许多打包方法和默认精度,用于处理角度、距离、交点、近似值和参数空间。它提供了用于比较测试实数相等性的值。角度精度比较角度。混淆精度比较距离。相交精度用于相交算法。近似算法使用近似精度。参数精度从3D精度中获得参数空间精度。无限返回一个可以被视为无限的高数字。使用-Infinite表示一个很大的负数。
此程序包为算法提供了一组实空间精度值。实空间精度设计为0.1纳米的精度。唯一可用的单位是毫米。参数精度由参数函数从实精度中导出。这应用了一个缩放因子,即曲线或曲面的切线长度。您,用户,提供这个长度。曲线有一个[0,1]参数空间和长度小于100米的默认值。几何程序包为不同类型的曲线提供参数精度。Precision程序包提供了测试实数是否可以被认为是无限的方法。
Precision::Angular:此方法用于比较两个角度。其当前值为Epsilon(2 * PI),即最小数字x,使得2PI + x与2PI不同。它可用于检查两个角度的混淆,如下所示:Abs(Angle1 - Angle2)< Precision::Angular()还可以检查两个矢量(gp中的Vec)的平行度,如下所示:V1.IsParallel(V2,Precision::Angular())请注意,Precision::Angular() 可以用于点积和叉积,因为对于小角度,正弦和角度是等效的。因此,要测试两个 gp_Dir 类型的方向是否垂直,可以使用以下代码:Abs(D1 * D2) < Precision::Angular()
Precision::Confusion:此方法用于测试三维距离。当前值为1.e-7,换句话说,如果使用的单位是毫米,则为1/10微米。它可以用于检查两个点(gp中的Pnt)的混淆,如下所示:P1.IsEqual(P2,Precision::Confusion())也有可能找到一个长度为零的向量(gp中的Vec):V.Magnitude() < Precision::Confusion()
Precision::Approximation:这是一个合理的精度,可以作为拟合精细化的极限传递到近似过程。该近似值大于其他精度,因为它是为在时间紧张时使用而设计的。近似算法的设计者已经将其作为一种合理的折衷方案提供。当前值为Confusion()*10。请注意,近似大于混淆,因此在近似过程中使用混淆时必须小心。