这是两部分文章中的第一部分,我们将在其中开发一种通用算法,用于遍历 HOOPS Exchange 中实现的对象层次结构。遍历对象层次结构是几乎每个工作流程的重要且无处不在的部分。我们在这里描述的算法可以在Exchange Toolkit 中找到。
HOOPS Exchange 是一个软件开发工具包,它可以帮助应用程序开发人员读取各种标准和专有 3D 文件格式,例如 STL、OBJ、STEP、IGES、SolidWorks 和 CATIA。除了 3D 几何之外,HOOPS Exchange 还提供对装配树的访问,装配树是由设计师创作的 3D 数据的逻辑结构。
用于表示复杂 3D 数据集的数据结构具有层次关系。例如,在使用 HOOPS Exchange 加载文件后,您将获得一个A3DAsmModelFile对象。这个“顶级”对象由一个或多个装配节点组成,存储为 C 风格 A3DAsmProductOccurence对象数组。此外,这些装配节点对象是递归的。也就是说, anA3DAsmProductOccurence可以包含一个“子”A3DAsmProductOccurrence对象数组和/或A3DAsmPartDefinition代表 3D 部件的 。
通过检查HOOPS Exchange B-Rep 数据中存在的拓扑对象,可以看到对象之间层次关系的其他示例。在这个经典场景中,我们发现 A3DTopoBrepData 包含一个 A3DTopoConnexes 数组,其中包含一个 A3DTopoShell 对象数组。壳包含面,其中包含循环,包含共边,包含边,最终包含顶点。
到目前为止,我们已经确定对象之间的层次关系在 HOOPS Exchange 中普遍存在,并且需要一种用于遍历对象的通用算法。
我们的目标是创建一个通用的算法,它需要两个输入--一个任意类型的 "拥有 "对象和一个类型指定器。这个算法的返回值应该是一个对象的集合,所有的对象都是指定的子类型,这些对象是由拥有对象直接或通过中间对象隐含地拥有。我们将分步实现这一目标。
首先,我们需要一个函数来返回直接包含在父级中的对象。例如,要检索壳内的所有面,我们将编写如下函数:
这个函数体是一个模式,对层次结构中的每个对象都会重复。正如你所看到的,这有点冗长,而且不是很通用。
如果我们使用ExhangeToolkit.h 中提供的数据访问工具,我们可以将代码大幅简化为更易于管理的内容:
除了更容易管理之外,这个实现也不容易出错。
为了使其更加通用,我们可以利用A3DTopoShell和A3DTopoFace都是同一类型(void)的别名这一事实。由于这段代码是 "以Exchange为中心 "的,我们可以用A3DEntity替换参数类型和返回类型。这个类型表示Exchange中的基本类型,也是对void类型的别名。
在做了这个改变之后,我们的函数签名开始看起来更通用:
作为下一步,我们可以修改这个函数以接受任何类型的父类,并通过切换实体类型返回子类集合。
函数getFaceLoops将像getShellFaces那样实现。你会注意到,我们把std::vector换掉了,用ts3d::EntityArray代替了它。它们是等效的类型,这一变化进一步有利于简洁和可维护性。
switch语句只实现了上述两种情况,所以它只处理两种Exchange对象类型。它可以被扩展到处理Exchange架构中使用的所有对象类型。要做到这一点,我们需要为每种类型的对象声明一个独特的函数,并帮助它返回其中可用的子对象。这将导致大量的代码,即使我们做了所有的修改,使其更加简洁(并且不容易出错)。
我们可以使用函数对象和无序哈希来管理这种大规模的代码扩展。让我们分两步来看看这组变化。仍然只关注我们到目前为止所引用的两种对象类型,通过使用函数对象和无序散列,我们可以将getChildren函数重写如下:
我们继续关注处理新类型现在可以分两步完成,首先,添加一个 get 函数,然后在父对象类型(键)的无序散列中添加一个条目,与 get 函数(值)配对。但现实是一旦我们完全实现了这个功能,就几乎没有人会直接使用各个 get 功能。
使用lambdas,我们还可以消除独立的 get 函数,进一步减少我们必须编写(和维护)的代码行数。为了代码清晰,我们还添加了一些换行符。
如果您一直在关注,那么您应该对最后一个代码片段会感到满意。您应该明白,在这个片段中,我们正在构建一个静态无序哈希,它允许我们按 Exchange 对象类型查找函数对象。在这个片段中,我们只用两个对象类型的两个 getter 填充无序散列,并理解一个完整的实现将包括所有对象类型的所有 getter。
完整实现getChildren本身就很有用。但我们可以做得更多,让它变得更好。例如,如果我们查看 A3DAsmProductOccurrence,我们会注意到我们的函数可能返回不同类型的孩子。该功能可以返回的类型的孩子A3DAsmProductOccurrence,A3DAsmPartDefinition,A3DMkpView,A3DGraphCamera,或 A3DMkpAnnotationEntity。如果我们允许函数的使用者指定他们想要哪种类型的子对象,这将更加通用(和有用)。
让我们实现getChildren它来处理我们刚刚描述的情况。再次理解,一个完整的实现将包括所有父对象类型,以及所有可能类型的子对象的 getter。在这里,我们只展示代表整体的一小部分。
想象一下,如果直接跳到这个实现。这会有点压倒性,而且肯定会令人困惑。但我们已经采取了一系列合乎逻辑的步骤来实现这一目标。那么,我们有什么?
我们已经(部分)实现了一个函数,该函数接受一个任意父对象 (ntt ) 和一个所需的子对象类型 (child_type)。在内部,该函数由一个静态映射 ( _getterMapByParentType )组成,它允许我们按父类型查找所需的 getter 函数,然后按所需的子类型查找。我添加了一些注释以使构造函数的结构更具可读性。
现在,想象一下这个结构完全填充了所有可能类型的子项的所有 Exchange 对象类型和 getter 函数。不要将其视为功能,而是将其视为数据模型。它是一个数据模型,表达了 HOOPS Exchange 中存在的完整父子关系集!或许你可以看到我们的发展方向。
我们已经建立了一种简洁的方法来促进 Exchange 使用的对象层次结构以及允许我们遍历它的嵌入式功能。
在第 2 部分中,我们将深入研究使用我们刚刚创建的数据模型智能导航层次结构的算法,帮助使用者从给定的父对象获取任意类型的后代。
除了上述信息之外,HOOPS Exchange 技术还提供对各种其他相关信息的访问,例如构造几何、面名称、坐标系、图层/过滤器设置和用户定义的属性等。如果您还想了解HOOPS Exchange相关的更多信息,欢迎前往HOOPS中文网了解详情
点击>>申请HOOPS试用http://x7pfmmn259623uby.mikecrm.com/l9292M9