1 概述

OpenCivil是一个由面向Civil领域的BIM正向设计产品以及一组SDK共同组成。其中,这个BIM正向设计产品在功能上涵盖了公路、铁路、桥梁、接触网、场地等领域的设计工作,为了更方便的服务于行业客户,在产品分发形式上,我们将OpenCivil拆分为OpenRoads Designer for China和OpenRoads Designer Ultimate for China以及OpenRail Designer for China和OpenRail Designer Ultimate for China两个产品,分别应用于公路和铁路行业。其中,OpenRoads Designer for China和OpenRail Designer for China两个产品的基本版,而OpenRoads Designer Ultimate for China和OpenRail Designer Ultimate for China则为两个产品的旗舰版。
OpenRoads Designer Ultimate for China产品,其功能主要涵盖公路、桥梁、场地等领域的设计能力,而对于面向铁路行业的OpenRail Designer Ultimate for China产品,其功能主要涵盖铁路、桥梁、接触网等领域的设计能力。
OpenCivil除了包含BIM正向设计类产品外,还包含一组与产品紧密关联的SDK。这些SDK主要由Civil SDK和CIM SDK两部分共同组成。其中,Civil SDK为面向各特定领域功能的SDK集合,其中所包含的API与特定领域的功能直接关联;而CIM SDK则为一个相对独立的SDK,其中所包含的API既可以用于现有的特定领域,也可以应用于其他领域,甚至可以应用于任何基于Microstation的产品。
在功能上,Civil SDK直接服务于对应的领域功能,它主要由 OpenRoads SDK、OpenRail SDK以及OpenBridge SDK等部分组成,其内所含的API也主要分别用于读取、修改、创建由OpenRoads、OpenRail以及OpenBridge所创建的对象;此外,这些API还包含与对应UI相对等的操作功能。CIM SDK则主要为用户提供一种可实现自定义对象或自定义实体的手段,通过它,用户可按照自己的实际需要规划并创建自定义对象,以一种不必拘泥于系统内置实体的方式来表达自身的业务对象,从而在很大程度上提高了系统的可扩展性和实用性;此外,CIM SDK还提供了用于建立和管理CIM对象间隶属关系的手段,通过它,用户的自定义对象将能感知到与之所关联对象的改变,并对此改变作出适当的响应,从而构造智能化对象关联网络。
由于本文档侧重于开发方面,所以基本不涉及关于OpenCivil产品本身功能方面的介绍,如需了解这方面的知识,请参阅系统的在线用户手册。

2 系统基本组成

如前介绍,OpenCivil主要由设计产品和SDK两部分组成。关于这两部分的详细组成及其关系,请见下图:

image.png

在上图中各元素的基本介绍如下:

  • OpenRail Designer Ultimate for China: 为面向铁路行业发布的设计类产品,它在功能上完全包含OpenRoads Designer Ultimate for China,除此之外它还包含铁路设计专用的Rail App和Rail SDK,以及铁路接触网设计专用的OHL App和OHL SDK;
  • OpenRoads Designer Ultimate for China: 为面向公路行业的设计类产品,它在功能上包含:
    1. - Roads App/Roads SDK: 公路设计专用的功能及对应SDK
    2. - Bridge App/Bridge SDK: 桥梁设计专用的功能及对应SDK
    3. - Tunnel App/Tunnel SDK: 隧道设计专用的功能及对应SDK
    4. - Site App/Site SDK: 场地设计专用的功能及对应SDK
    5. - Prostructure App/Prostructure SDK: 钢筋砼结构配筋专用功能及对应SDK
  • CIM SDK: 可用于扩展自定义实体的通用SDK,它只有少量功能基于Civil Framework, 主体功能完全基于PowerPlatform;
  • Civil Framework: 也称之为 Civil Infrastructure Framework(CIF),是一个功能强大且非常庞杂的基础类库,主要用于面向土木领域的大规模应用开发。目前,几乎所有的设计类产品均以它为平台开发而成;
  • PowerPlatform: Bentley公司的基础框架及类库,公司内几乎所有设计产品均以它为平台开发而成(包括Microstation),此平台虽然功能非常强大,且集成了许多高级功能(如点云处理、地形处理、倾斜摄影模型处理、地理坐标处理等等),但由于它是个专业无关的通用平台,故用它直接开发专业产品会比较困难,且工作量很大;
  • DgnPlatform: 用于处理Bentley公司专有的DGN格式文件的一个平台,其功能包含DGN文件的读、写、显示等等;
  • EC Framework: 一个用于以通用且具备高度扩展的方式表达和容纳工程内容的接口库,其中的EC代表Engineering Content。使用它,可将任意来源的工程相关数据表达为一种统一且通用的形式,从而在很大程度上简化了工程数据的表达、存储、访问等;在Bentley产品中,EC有着极为广泛的使用。

配置开发环境
必备软件

  • OpenRoads Designer for China/OpenRoads Designer Ultimate for China面向铁路行业发布的设计类产品
  • OpenRail Designer for China/OpenRail Designer Ultimate for China面向公路行业发布的设计类产品
  • CIMPlatformSDK (10.08.xx.xx) CIM 开发包,包含了 CIM 核心框架及相关应用扩展

备注:如果本机已经安装了旧版本的开发包,建议完全卸载之后再安装新版本的开发包
开发工具

  • Visual Studio 2017 及以上

备注:当然您也可以使用任何您钟爱的开发工具,例如 Visual Studio Code, Sublime Text, CLion, CodeBlocks 等


  • 3 CIM SDK

    3.1 CIM SDK概述

    CIM SDK,意为Civil Information Modeling,也就是专用于土木工程信息化建模的一个SDK。这个SDK中提供的API可完成以下任务:

  • 实现自定义实体/自定义对象;

  • 建立对象之间的隶属关系;
  • 智能感知关联对象的改变,并可选的对此改变做出响应;
  • 智能感知对象属性的改变,并可选的对此改变做出响应;
  • 面向对象提供基于类接口的聚合化属性访问;
  • 面向对象提供基于类接口的聚合化扩展属性访问;
  • 面向对象提供基于类接口的聚合化显示属性访问;
  • 面向对象提供基于几何变换层次的隶属关系管理;
  • 提供模块化对象组管理;
  • 面向对象提供材质化渲染支持;
  • 提供全局事件监听及转发功能;
  • 提供面向对象的运行时类注册机制;
  • 提供面向对象的静态/动态属性管理机制;
  • 提供面向对象的上下文菜单支持;
  • 提供面向对象的夹点编辑操作扩展;
  • 提供面向对象的自定义浮动工具栏支持;
  • 提供面向对象的基于依赖关系的深度克隆功能;
  • 提供面向对象的自定义属性编辑动作扩展;
  • 提供面向对象的下拉列表自定义属性动态扩展;
  • 提供面向对象的下拉复选框列表自定义属性动态扩展;
  • 提供面向对象的Microstation内置元素快速操作接口;
  • 提供通用的参数化曲面操作API集(包括曲面的构造、裁剪、转换、布尔运算等);
  • 提供通用的网格操作API集(包括网格的构造、裁剪、转换、布尔运算等);
  • 提供通用的实体建模API集(包括实体的拉伸、扫掠、放样、布尔、断面提取等);
  • 提供通用的路线几何处理API集(平曲线构造、计算以及竖曲线构造、计算等);
  • 提供通用的带约束参数化横断面模板处理API集以及编辑UI;
  • 其他高级功能等等;
  • 所有API均提供C++和C#两种访问接口;

    3.2 CIM SDK组成

    如下图所示,CIM SDK在内部实际上是由多组API组成的,这些API共同协作并为Core API提供基础性支持,从而在总体上实现CIM SDK为外部提供的功能。与此同时,这些API组对外依旧是可用的,它们的主要功能为:
    image.png
    Other Framework
    PowerPlatform

  • Core APIs: CIM SDK的核心API组,主要提供自定义对象/实体的能力,同时也提供了对象间隶属关系的管理能力;此外,这组API还围绕着自定义对象提供了夹点编辑、上下文菜单支持、材质支持、浮动工具条支持、深层克隆、级联删除、关联改变事件传递及处理等功能,为用户建立基于复杂对象关系的应用程序提供了有力支持;

  • CIM-CIF APIs: 这组API主要为CIF开发者在CIF上下文中使用CIM自定义对象提供无缝支持,通过使用API,CIF开发者可以像使用CIF实体那样使用CIM对象,如创建、显示、持久化、建立引用、命令筛选等等;
  • Shared Geometry APIs: 这组API主要为CIM开发者提供几何计算相关的支持。通过在自定义对象中充分使用这些API,用户可以很容易的实现复杂几何操作;这组API目前由下面的四组API共同构成;
    • Linear Geometry APIs: 这组API主要为CIM自定义对象及其直接用户提供线性几何方面的计算能力。如围绕着道路平曲线、竖曲线可完成一系列的信息转换及提取、对象构造、坐标/方位/高程/偏移等几何量的计算,从而为自定义对象提供路线几何计算方面的支撑;
    • Surface APIs: 这组API主要提供参数化曲面的处理能力,主要包括多种方式构造样条曲面、曲面裁剪、基于曲面的重采样、曲面转换以及曲面布尔运算等等;
    • Mesh APIs: 这组API主要提供离散表面(亦即网格)相关的处理能力,主要包括多种方式构造网格、网格裁剪、网格简化、网格转换、网格采样、边界提取、网格合并、网格分解、网格布尔运算等等;
    • Solid APIs: 这组API主要针对Microstation SDK所提供的Solid相关API进行了大量的增强和扩展,提供了常用形体的参数实体建模、基于带约束的参数化模板建模、拉伸/扫掠/放样等方式构造实体、实体布尔运算等等,尤其是对面向桥梁上部构造之类的纵向建模做了较大扩展和优化;
  • Basic Element APIs: 主要提供了类化聚合方式操作Microstation内置元素相关的API,通过使用这些API,可以大幅简化对内置元素的操作代码,提高工作效率的同时也会有效降低学习成本;
  • Template APIs: 主要提供了带约束参数化模板操作相关的API以及可视化模板编辑器。

我们将在随后的章节详细介绍以上各组API的功能及其使用方法。

3.3 创建并使用自定义对象

3.3.1 创建自定义模块

使用CIM SDK创建自定义对象的第一步,就是创建一个自定义的模块,也就是创建一个CimModule的派生类。具体步骤为:

  • 基于CimModule派生一个自定义类,并按需实现其中的虚方法。例如:

// C++ 样式
class MyModule : public CimModule
{
private:
static MyModule *m_sInstance;

protected:
// 当前模块已被加载时执行此回调,通常我们可在此回调中执行一些模块级的初始化工作,例如: 各种自定
// 义对象的注册、各种全局管理器的初始化等等。
virtual void _OnModuleLoaded() override;
// 当前DGN文件已被打开且系统已就绪时执行此回调,通常我们可在此回调中执行一些针对特定DGN的初始
// 化工作,例如:导入所需的Schema文件、启动DGN相关的监听器、在DGN文件级或Model级或Element
// 级挂入自定义数据块等等。
virtual void _OnDgnFileOpened(DgnFileR dgnFile) override;
// 当前DGN文件将要被关闭时执行此回调,通常我们可以在此回调中执行一些与 OnDgnFileOpened() 相反
// 的操作,例如撤销DGN文件相关的监听器以及先前注册的自定义数据块等等。
virtual void _OnDgnFileToBeClosed(DgnFileR dgnFile, bool changingFiles) override;
// 当前模块将要被卸载(或者当前系统即将关闭)时执行此回调,通常我们可以在此回调中执行一些全局管理
// 器的撤销工作。
virtual void _OnModuleToBeUnload() override;
// 返回此模块的名称,系统将使用返回的名称来标记此模块,以便于后期管理。
virtual WCharCP _GetName() const;

  1. MyModule();
  2. public:<br /> static MyModuleR Get();
  3. virtual ~MyModule();
  4. // 此模块启动的入口点函数。<br /> static void Startup();

}; // MyModule

  1. /// <summary><br /> /// C# 方式<br /> /// </summary><br /> public class MyModule : CimModule<br /> {<br /> /// <summary><br /> /// 静态单例设计模式.<br /> /// </summary><br /> private static MyModule s_Module = new MyModule();
  2. /// <summary><br /> /// 获取此模块的名称<br /> /// </summary><br /> /// <returns></returns><br /> protected override string _GetName ()<br /> {<br /> return "MyModuleName";<br /> }
  3. /// <summary><br /> /// 模块已加载回调.<br /> /// </summary><br /> /// <remarks><br /> /// 所有 CimRoot 的派生类都必须在这里完成注册.<br /> /// </remarks><br /> protected override void _OnModuleLoaded ()<br /> { <br /> try<br /> {<br /> // 注册所有自定义类.<br /> MyObjectClass1.DoRegister();<br /> MyObjectClass2.DoRegister();<br /> // 完成其他模块级的初始化工作 <br /> }<br /> catch ( Exception ex )<br /> {
  4. }<br /> }
  5. /// <summary><br /> /// DGN已初始化完毕回调.<br /> /// </summary><br /> /// <param name="dgnFile"></param><br /> Protected override void _OnDgnFileOpened (DgnFile dgnFile)<br /> {<br /> // 导入本模块中所有自定义类需要使用的Schema文件<br /> CimModuleManager.ImportSchema(null, "Bentley_CIM_Bridge", 1, 0, dgnFile); <br /> // 执行其他DGN文件相关的初始化工作.<br /> }
  6. /// <summary><br /> /// 模块初始化入口点函数.<br /> /// </summary><br /> /// <remarks><br /> /// </remarks><br /> public static void Startup ()<br /> {<br /> // 先初始化本模块所依赖的模块<br /> CimCoreModule.Startup();<br /> // 然后将此模块注册到全局的模块管理器中<br /> CimModuleManager.Instance.AddModule(s_Module);<br /> }<br /> }
  • 在全局入口函数中添加对模块入口点函数的调用,这样,当系统加载模块并调用全局入口点时就会引发模块的初始化。具体方法为:
  • 对于C++模块的,需要在 MdlMain 函数中调用模块的入口点:

extern “C” DLLEXPORT void MdlMain(int argc, WCharCP argv[])
{
// 注册命令
mdlSystem_registerCommandNumbers (commandNumbers);
// 加载命令表资源
mdlParse_loadCommandTable (NULL);
// 获取当前主DGN文件
DgnFileP masterDgnFile = mdlDgnFileObj_getMasterFile ();
if (NULL == masterDgnFile)
return;
// 执行模块对象的初始化
MyModule::Startup();
// 显式触发DGN文件已打开回调
CimModuleManager::Get().OnDgnFileOpened(*masterDgnFile);
}

  • 对于C#模块,需要在模块AddIn的Run方法中调用模块的入口点:

    ///


    /// 用于提供顶层入口点的AddIn派生类.
    ///

    [AddIn(MdlTaskID = “MyDotNETAddIn”)]
    public sealed class MyDotNetAddIn: AddIn
    {
    ///
    /// 使用静态变量引用实例,以阻止GC回收AddIn实例.
    ///

    private static MyDotNetAddIn s_DotNetAddIn = null;

    1. /// <summary><br /> /// 构造符.<br /> /// </summary><br /> /// <param name="mdlDesc"></param><br /> public MyDotNetAddIn(System.IntPtr mdlDesc)<br /> : base(mdlDesc)<br /> {<br /> s_DotNetAddIn = this;<br /> }
    2. /// <summary><br /> /// AddIn入口点函数,宿主加载模块后会立即执行此函数.<br /> /// </summary><br /> /// <param name="commandLine"></param><br /> /// <returns></returns><br /> protected override int Run(string[] commandLine)<br /> {<br /> // 执行自定义模块的初始化函数<br /> MyModule.Startup();<br /> return 0;<br /> }<br /> }

    3.3.2 定义Schema

    对于CIM而言,每个自定义对象都必须与一个EC类相关联,而且,自定义对象的类层次结构必须与所关联EC类的类层次结构相一致;自定义对象通过Schema名称以及EC类名称来全局唯一的引用特定的EC类定义。对于初始接触EC的开发者而言,EC相关的术语可能比较难以理解,下面就EC相关术语做一些类比方式的解释:

  • EC Schema: 它是一个扩展名为.ecschema.xml 的XML文件,其顶层节点的类型名为ECSchema。其中包含任意多个 EC 类的定义,我们可以把它想象成数据库系统的表结构集合,也可以把它想象成用特定语言编写的数据结构定义集合;

  • EC Class: 它是包含在EC Schema中的一个用于定义EC类构成的XML节点,其类型名为ECClass。在其中,我们可以包含任意多个属性的定义。为了便于理解,我们可以把EC Class想象成数据库系统中一个特定的表结构定义,也可以把它想象成用特定语言编写的一个数据结构的定义;与数据库表结构不同的是,EC Class的定义是可以被继承的,也就是允许基于现有的EC Class定义新的EC Class,并在其中增加新的属性定义,这样,新的EC Class中除了包含新增的之外,还包含从基类中继承的属性定义;
  • EC Property: 它是包含在EC Class中的一个名为ECProperty的XML节点,主要用于描述EC Class中一个属性的类型及其相关特性。为了便于理解,我们可以把EC Property想象成数据库系统中一个特定表结构定义中的一个特定的字段的定义,也可以把它想象成用特定语言编写的一个特定数据结构中的一个成员变量;目前,所支持的EC属性类型有: int (32位有符号整数)、long (64位有符号整数)、double (双精度浮点数)、boolean (布尔值)、string (字符串)、binary (二进制数据块)、point2d (2D点)、point3d (3D点)、IGeometry(五大子类均支持);除了支持单值属性外,还支持数组类型的多值属性;
  • EC Instance: 它是特定EC Class的一个实例,用于代表以特定EC Class为模板(或数据组成方式)的运行时数据结构实例,在这个实例中,所基于的EC Class 中的每个EC Property在此实例中都拥有自己的值。为了便于理解,我们可以将它想象成数据库系统中基于特定表结构的一条记录,这个记录由多个字段值组成,每个字段类型都对应于表结构中的特定字段,当然,也可以将它想象成用特定语言所定义数据结构的一个运行时实例;

为了更直观的理解如何定义Schema,接下来我们将对照着一个实际的例子来解释:
<?xml version=”1.0” encoding=”utf-8”?>





bbc:CIMGraphicElement


CIMTunnelGraphicElement



CIMTunnelLinearElement




  • 上例中定义了一个包含有3个EC类的Schema,这个Schema的名称为Bentley_CIM_Tunnel,命名空间前缀为bbft,当前版本号为1.0;
  • 上例Schema中引用到了3个外部的Schema文件,这些引用的定义包含在名为ECSchemaReference的节点中;需要注意的是,所引用Schema的名称、版本号以及前缀名称(prefix属性)必须与对应Schema文件中所定义的相匹配,而且所引用的Schema的文件名应为:Schema名称(区分大小写) + 版本号 + .ecschema.xml,如:EditorCustomAttributes.01.00.ecschema.xml;
  • 每个EC类的定义均包含在名为ECClass的节点中,每个ECClass中可以包含任意多个ECProperty节点和任意多个BaseClass节点,它们代表此EC类中所包含的属性以及此EC类的基类;每个EC类中的 typeName和isDomainClass属性必须提供,它们分别代表此EC类的类型名称以及是否为域类;
  • 每个包含在EC类中的ECProperty节点都代表一个属性或字段,它的名称、类型以及显示名称分别用 propertyName、typeName以及displayLabel属性来表示,而且都是必选的;其中的displayLabel用于属性面板显示属性,typeName可以使用的值在前面已经介绍了;例外的是,如果要在typeName中将特定EC类作为自定义类型来使用,则这个特定EC类定义中除了包含必须isDomainClass=”True”外,还需要包含 isStruct=”True” 属性;
  • 每个EC类中都可以包含任意多个名为BaseClass的节点,用以表达此类的基类;需要注意的是,如果所指示的基类不包含在当前Schema文件中,则需要使用 Prefix:Name方式来指示,如:bbc:CIMGraphicElement表示CIMCore这个Schema中的CIMGraphicElement 类,因为从ECSchemaReference中可知,bbc是CIMCore的命名空间前缀;另一个需要注意的是,EC类中所包含EC Property的名称(propertyName)在类范围内不允许重复,包括基类在内;
  • 为了确保系统在运行时能找到并加载特定的Schema,需要将定义好的Schema文件放在产品安装后的根目录下的ECSchemas下的特定子目录中;
  • 用户定义Schema中的任何EC类,都必须直接或间接的派生于CIMCore中的类;
  • 最后一个需要特别注意的是,在Schema文件中出现的任何内容都是大小写敏感的,所以一定要确保其正确性;

    3.3.3 创建自定义实体类

    自定义实体类的层次关系图如下:
    CimRoot
    (EC: CimObject)
    CimDataObject
    (EC: CimNonGraphicElement)
    CimGraphicObject
    (EC: CimGraphicElement)
    在规划并完成自定义实体类所需的模块和Schema后,我们就可以开始设计自定义实体了。在CIM SDK中,自定义对象分为两类: 一类就是需要具备图形表现能力的对象(也就是创建后能在系统中可见其图形化外观的对象),这类对象需要基于 CimGraphicObject 来派生;另一类就是不需要图形表现能力的对象(也就是在系统中无图形化外观的对象),这类对象需要基于 CimDataObject来派生。
    从本质上而言,任何需要持久化到DGN文件中的对象,都必须直接或间接的与Element相关联,因为宿主系统只负责持久化各类Element及其所相关联对象,并不提供任何能直接持久化任意对象的能力。所以,就CIM对象的设计思想来看,任何基于CimGraphicObject或CimDataObject派生的对象对宿主系统而言,它们都代表着一个特定类型的Element,区别在于: CimGraphicObject及其派生类所属的Element是个图形化的,通常保存在DGN文件的常规Model中,而CimDataObject及其派生类所属的Element不是图形化的,通常保存在DGN文件中的字典Model中。
    下面将分步骤介绍如何定义并实现一个自定义实体:

    3.3.3.1 C++方式

  • 基于CimGraphicObject 派生一个类,假设名为 MyFirstCimObject;

  • 在所派生类的声明中添加下列宏:

// 声明MyFirstCimObject的基类为 CimGraphicObject
DEFINE_T_SUPER(CimGraphicObject);
// 用于非抽象类: 当前类,基类,导出宏
CIM_DECLARE_COMPONENT(MyFirstCimObject, CimGraphicObject, EXPORT_MACRO)
// 用于抽象类: 当前类,基类,导出宏
CIM_DECLARE_ABSTRACT_COMPONENT(MyFirstCimObject, CimGraphicObject, EXPORT_MACRO)

  • 在所派生类的实现中添加下列宏:

CIM_DEFINE_COMPONENT(MyFirstCimObject, CimGraphicObject); // 用于非抽象类:当前类,基类
CIM_DEFINE_ABSTRACT_COMPONENT(MyFirstCimObject, CimGraphicObject); // 用于抽象类: 当前类,基类

  • 重写基类中必要的方法:
    • 必须重写的方法:
      • virtual WCharCP _GetSchemaName() const; // 提供与此类关联的Schema名称
      • virtual WCharCP _GetECClassName() const; // 提供于此类关联的EC类名称
      • virtual BentleyStatus _GeneratePresentation(ViewContextR viewContext); // 专用于 CimGraphicObject派生类,用于生成对象的图形表达
      • virtual void GetLocalTransform(TransformR transform); // 专用于 CimGraphicObject派生类,用于确定当前对象相对于其父对象的变换矩阵
    • 通常需要重写的方法:
      • virtual StatusInt _OnPreInitData(); // 用于在执行对象的构造前初始化工作
      • virtual StatusInt _OnPostInitData(); // 用于执行对象构造完成后的初始化工作
  • 注册类: 任何基于 CimDataObject 或 CimGraphicObject 的类,在使用前都必须注册,注册方法为:
  • 在包含当前类的CimModule派生类的_OnModuleLoaded() 方法中执行 MyFirstCimObject::DoRegister();

    3.3.3.2 C#方式

  • 基于CimGraphicObject派生一个类,假设名为 MyFirstCimObject;

  • 在类中定义并初始化一个CimClassDesc类型的静态变量:

private static CimClassDesc m_sDesc = new CimClassDesc(typeof(MyFirstCimObject), CimGraphicObject.Desc(), false); // 参数依次为当前类的类型、基类的类描述符以及是否为抽象类

  • 实现一个用于返回类描述符的静态方法:

new public static CimClassDesc Desc ()
{
return m_sDesc;
}

  • 重实现用于获取多态类描述符的方法:

public override CimClassDesc GetDesc ()
{
return Desc();
}

  • 重实现用于获取类关联Schema名称的虚属性:

public override string SchemaName
{
get
{
return ““;
}
}

  • 重实现用于获取类关联EC类名称的需属性:

public override string ECClassName
{
get
{
return ““;
}
}

  • 实现一个用于注册类的静态方法:

new public static StatusInt DoRegister ()
{
return CimClassFactory.Instance.DoRegister(Desc());
}

  • 实现一个用于撤销类注册的静态方法:

new public static StatusInt DoUnregister ()
{
return CimClassFactory.Instance.DoUnregister(Desc());
}

  • 实现OnGeneratePresentation()方法(仅对CimGraphicObject派生类):

protected override BentleyStatus OnGeneratePresentation (ViewContext viewContext)
{
// TODO: 对象绘制逻辑
return BentleyStatus.Success;
}

  • 实现LocalTransform虚属性(仅对CimGraphicObject派生类):

public override DTransform3d LocalTransform
{
get
{
// TODO: 添加逻辑以计算相对变换矩阵
return newPlace.Transform;
}
}

  • 实现对象创建前的初始化调用(可选):

protected override StatusInt OnPreInitData ()
{
// TODO: 添加初始化逻辑
return base.OnPreInitData();
}

  • 实现对象创建后的初始化调用(可选):

protected override StatusInt OnPostInitData ()
{
StatusInt retVal = base.OnPostInitData();
// TODO: 添加初始化逻辑
return retVal;
}

  • 注册类: 任何基于CimDataObject或CimGraphicObject 派生的类在使用前都必须注册。具体为:

在包含当前类的CimModule派生类的_OnModuleLoaded()方法中执行 MyFirstCimObject::DoRegister();

3.3.4 使用自定义对象

定义并实现自定义对象后,我们就可以使用自定义对象了。自定义对象的基本使用非常简单,无非就是按照: 构造对象实例、创建对象、设置或修改属性、添加对象到Model这个流程来进行。下面我们分别来讲解这几个步骤:

3.3.4.1 构造对象实例

构造对象实例,也就是实例化对象。通常我们可以用new操作符来完成,例如:
MyFirstCimObject obj = new MyFirstCimObject(); // C#
MyFirstCimObject *obj = new MyFirstCimObject(); // C++
这里需要注意的是,我们既可以使用new操作符在进程堆上构造对象,也可以直接在本地定义类变量。

3.3.4.2 创建对象

创建对象,意味着创建用以在宿主系统中表达此对象的Element以及相关的附属对象。由于底层平台的限制,我们需要将对象实例构造和对象创建分解为两个步骤来完成。具体操作如下:
StatusInt retVal = obj->CreateElement(ACTIVEMODEL); // C++
StatusInt retVal = obj.CreateElement(Utils.ActiveDgnModel); // C#
注意: 在对象创建调用返回后,一定要确保返回的值为 SUCCESS,否则表示对象创建已失败。

3.3.4.3 设置或修改属性

要设置或修改对象属性,我们只需要在对象上调用下面的方法:

  • C++: SetValue/SetValues/SetArray和GetValue/GetValues/GetArray
  • C#: SetValue和GetValue.

关于设置或修改对象属性的若干注意:

  • 对于C++版,SetValue/SetValues/SetArray方法实际上都是有多个重载版本的,每个版本对应一种数据类型,使用时应正确书写对应数据类型,以免造成错误的调用;
  • 对于C++版,每个SetValue/SetValues/SetArray都包含2个附加参数,即doUpdate和doNotify,它们分别表示: 当对象因这些方法的调用而被修改时,是否需要及时更新对象图形以及是否需要通知那些与当前对象建立有依赖关系的对象(也就是触发那些对象上的OnReferenceChanged通知)。默认情况下这两个附加参数的值都是true,在特定情况下我们可能需要精细控制它们以便达到特定的目的;
  • 对于C#版,SetValue通用于单值变量和多值变量(数组)的参数设置。由于多态类型识别的影响,需要在设置单值变量时,从字面类型上精确表达所设置值的类型,比如: double值应表达为2.0而不能是2,因为2会被识别为int;long类型数应表达为3L而不能是3,因为3会被识别成int类型;其次,在使用SetValue设置多值变量时,应使用C#中派生于System.Collections.Generic.IEnumerable接口的容器对象来传递值,在C#中,绝大多数容器对象都派生于这个接口,所以用起来还是很方便的;
  • 对于C#版,GetValue通用于获取单值变量数组变量的值。对于单值变量的值,应在返回值不为null的情况下直接转换为预期的类型即可;而对于数组变量的值,需要在其返回值不为null的情况下转换为预期的数组类型,例如: var retVals = (double[])obj;
  • 所有用于设置和获取属性变量值的上述方法,都需要输入一个名为accessString的字符串值用以指示所需要访问属性的名称。实际上,这个accessString不仅可以用于指示单值变量和数组变量的名称,还能以类C表达式的方式来指示数组变量中特定索引处的元素以及结构体变量中的特定成员,例如:

    • accessString = “MyVar”; // 用于指示单值变量、数组变量整体
    • accessString = “MyVar[2]”; // 用于指示数组变量中的2#元素
    • accessString = “MyVar.Height”; // 用于指示单值结构体变量中的Height成员变量
    • accessString = “MyVar[2].Height”; // 用于指示数组结构体变量中2#元素中的Height成员变量
    • accessString = “MyVar[2].Height[4]”; // 用于指示数组结构体变量中2#元素的数组变量Height中的4号元素
    • accessString = “MyVar.Height.XYValue[1]”; // 具体含义请读者推测😊

      3.3.4.4 添加对象到Model中

      任何已成功创建的CIM对象都需要添加到Model中才能保证其可被持久化到DGN文件中,否则它只是一个临时对象,会因类对象的析构而彻底消亡。将对象成功添加到Model中后,除非显式的删除,否则任何时候我们都能通过调用 CimDataObject::Attach()或CimGraphicObject::Attach()重新在Element上获取CIM对象的实例。
      将对象添加到Model中的操作非常简单,直接在对象上调用AddToModel()即可完成。

      3.3.4.5 添加右键菜单

      基于基类CimGraphicObject创建的CIM对象支持自定义右键菜单操作:通过重写基类的_GetContextCommandDescs 函数(c++)或ContextCommands属性(c#)来获取用户自定义的右键菜单操作,每个条目构造一个CimContextCommandDesc (string label, string keyin, string iconName, int commandId)当keyIn为空时,用户可以传入自定义的commandId,当该条目被按下时,通过重写基类的_OnContextMenuItem(c++)或 OnContextMenuItem(c#)来获取对应的commandId以便用户自定义一些操作来执行。

      3.3.4.6 添加浮动工具条

      基于基类CimGraphicObject创建的CIM对象支持自定义浮动工具条操作:通过重写基类的_GetToolbarActions 函数(c++)或 ToolbarCommands 属性(c#)来获取用户自定义的工具条条目,每个条目构造一个 CimToolbarAction (string label, string keyin, string iconName, int commandId)当keyIn为空时,用户可以传入自定义的commandId,当该条目被按下时,通过重写基类的_OnToolbarItem (c++)或 OnToolbarItem (c#)来获取对应的commandId以便用户自定义一些操作来执行。

      3.3.4.7 添加夹点操作

      基于基类CimGraphicObject创建的CIM对象支持夹点操作:通过重写基类的_GetGripPoints 函数(c++)或 GripPoints 属性(c#)来获取用户自定义的夹点,每个夹点构造一个 CimGripPointData (GeometryNET.DPoint3d point, string Desc, string selectedIconName, string unselectedIconName) ,传入的point参数为世界坐标,Desc为夹点名称, selectedIconName为选中时夹点显示图标, unselectedIconName为未选中夹点时显示图标,传入图标名称即可。用户通过重写基类的_OnMoveGripPointsAt(c++)或 OnMoveGripPointsAt(c#)函数来得知是哪个夹点在被操作,通过夹点ID和相对移动位移,用户可以定义自己的动作。

      同时,通过重写基类的_OnDblClick()(c++)或OnDblClick()(c#)来自定义当对象被双击时对应的操作行为。需要注意的是,当实现夹点功能时,注意子类必须重载_OnDecorate(c++)或OnDecorate(c#),不需要展示额外绘制的对象时,直接返回success即可。

      3.3.4.8 自定义CIM对象的若干注意事项

      在创建CIM自定义对象时需要遵循以下原则:
  • 在CIM对象中,不应定义并维护任何成员变量,取而代之的是应在EC类中为那些需要维护的变量定义EC属性,并通过与对象实例关联的EC实例来访问和修改这些变量;

  • 在CIM对象中,任何方法的调用都不应依赖于特定假设的顺序,例如: 不能假设方法A在调用前方法B一定已被调用过了。因为CIM方法的调用顺序取决于系统的事件处理方式,在不同场景下可能会调用的方法以及调用顺序是不尽相同的;
  • 在CIM对象中,任何方法的执行都不能依赖于自身历史执行状态或其他方法的历史执行状态;
  • 任何时候对特定CIM对象所做的修改,只有在成功调用其CommitChanges()方法后才能真正将修改提交到代表对象的Element上,从而对对象的真实状态产生影响(如对象的图形);

    3.3.5 建立对象间隶属关系并响应修改

    在创建了大量离散的CIM后,我们通常需要将这些对象以某种特定的方式连接成一整体,从而形成有机的结合体,用以表达某个复杂的业务对象。例如: 对一个机器而言,它可能会由许多不同类型的零件组成,我们可以用不同类型的CIM对象来代表不同类型的机器零件,然后按照一个既定的规则将这些CIM对象装配在一起从而形成一个整体,然后用这个装配后的整体来共同表达这个机器。在这个例子中,用于将对象装配在一起的这个规则就是这里的建立对象间隶属关系。
    建立对象间隶属关系的最大意义在于: 可以整体化管理一个具有隶属关系的约束的对象集,使之能按照实际的业务逻辑进行组织成有机的结合体,从而有效提高大规模对象集的管理效率,同时降低管理的复杂度。
    建立对象间隶属关系的另一个重要意义在于: 可以有效实现对象间的修改同步,从而使其状态始终保持正确状态,而不需要单独的、以查找方式的去更新所有潜在需要更新的对象。例如: 在桥梁工程中,对于双柱式桥墩而言,如果建立了柱中系梁与两个柱子之间的隶属关系,则任何时候当柱间距被修改时,系梁都会感知到这种变化并能同步更新自身的长度以适应新的柱间距。相较传统做法而言,我们不需要在每次发生改变时,都去显式的去查找与此改变可能有关的对象并更新它们的状态,从而在很大程度上提高了对象的智能化程度,简化了处理过程。

    3.3.5.1 隶属关系的建立

    在CIM系统中,目前提供了两种方式建立对象间的隶属关系:

  • 使用CimRoot::AddReference建立隶属关系: 它主要用于建立对象间通用的隶属关系,这种关系本身并不具备任何约束力,取而代之的是,其所确立的内在约束逻辑需要通过在对象上实现对应的回调函数CimRoot::OnReferenceChanged来表达。正因为如此,它是一种通用的隶属关系,而且这种隶属关系的实际约束力可由用户代码来动态确定。建立隶属关系时需要指定关系的强度和是否让关系起作用,这两个参数会影响到后续的深度克隆和级联删除功能。默认参数是 STRENGTHTYPE_Referencing 强度和关闭关系,不支持深度克隆和级联删除。如果要实现后续的深度克隆和级联删除,可以通过 SetRelationship 来重新修改关系,启用关系作用,修改为 STRENGTHTYPE_Holding 和 STRENGTHTYPE_Embedding 都可以实现深度克隆和级联删除等功能;

  • 使用CimGraphicObject::SetTransformParent (C++) 或者CimGraphicObject.TransformParent (C#)建立对象间的变换关系: 这是基于上述通用隶属关系定制而成的、专用于图形化对象的一种变换约束关系。通过建立这种关系,当前对象的变换将始终表达为相对于其父对象的变换。也就是说,当前对象的变换始终是个相对的变换,若当前变换依次叠加上其所有父对象的变换就能得到绝对变换(当然,CimGraphicObject中已经提供了对应的方法)。

    3.3.5.2 响应关联对象的改变

    建立对象间的隶属关系后,我们有可能需要通过响应对象的改变来完成对象间的同步更新或协调更新。目前在CIM系统中,不同类型的隶属关系需要通过不同的函数来响应:

  • 使用CimRoot::AddReference建立的隶属关系: 如上所述,这种隶属关系的实质性约束需要通过实现CimRoot::OnReferenceChanged方法来响应,所以这种响应相对而言是必须的,否则这种引用只能用于后期的对象检索,因为它的默认实现是空的,完全由开发者来定义;

  • 使用CimGraphicObject::SetTransformParent (C++)或者CimGraphicObject.TransformParent (C#) 建立的隶属关系: 如上所述,这种隶属关系是基于通用隶属关系定制而成的,所以已具有实体实现(自动基于父对象计算自身的绝对变换并将其用于自身的空间定位),通常可以不用响应此类型的隶属关系。在特定情况下,如需响应此关系,只需实现CimGraphicObject::OnTransformParentChanged (C++) 或者 CimGraphicObject.OnTransformParentChanged (C#)即可;

    3.3.5.3 扩展新的隶属关系

    在实际应用中,用户可能会需要定义新的隶属关系类型以满足特定的需要,为此,系统预留了通用的低级别接口以供用户扩展之用。
    要定义并实现新的隶属关系,可按如下步骤进行:

  • 选择一个大于等于300的值作为自定义隶属关系的类型ID值(300以内的值系统保留),如:

define MyRelationshipTypeId 301

  • 使用 CimRoot::_RawAddReference来添加对其他对象的引用;
  • 通过重写CimRoot::_OnRawReferenceChanged方法来响应关联对象的改变;

    3.3.5.4 改变通知的触发控制与对象更新

    通常,当CIM对象被以任何方式修改后,都会导致对象的图形被刷新(如果有图形表达的话),与此同时,针对此对象的改变通知也会被触发,从而导致此对象引用者上的通知函数会被调用,继而引发网状形式的修改通知传递。在多数情况下,这正是我们所期望的情形。但在某些特殊情况下,我们可能会希望能只修改对象而不触发其上的修改通知,比如: 仅修改对象的显示属性(颜色、线宽、线形等)时就不希望触发修改通知,因为这种通知对于响应者而言通常无意义的,或者说并不是响应者通常都会关心的。
    为此,系统提供了一些方法及机制,以允许用户显式指定是否需要在对象被修改时触发通知,主要包括:

  • 在CimRoot中绝大多数的修改类方法上,都提供了可选的doNotify参数用于控制是否允许触发通知,具备此参数的常见方法有:

  • AddReference
  • RemoveReference
  • RemoveAllReferences
  • SetValue
  • SetValues
  • SetArray
  • Touch
  • Delete
  • AddToModel
  • CommitChanges
  • CimRoot::ProcessNotify: 此方法用于主动触发那些先前未被允许触发的改变通知;
  • CimRoot::EnableNotify: 此方法用于在全局显式的启用或关闭改变通知的触发,它的控制将影响全局所有改变通知的触发;
  • CimRoot::Touch: 此方法,可在并不实际修改对象的情况下触发对象修改通知,并可选的更新对象图形;

    3.3.5.5 对象隶属关系的使用规则及限制

    尽管在理论上使用对象隶属关系可以实现规模庞大、关系复杂、智能更新的对象网络,但在实际应用中对于隶属关系的使用还是有些基本规则和限制的,具体如下:

  • 尽量避免建立循环引用关系。正常情况下应建立针对特定对象的单向依赖关系,而不应该建立相互依赖的双向依赖关系,因为这样的话,任何一方的改变都会引发另一方的改变,从而导致改变通知在两者之间不断的来回传递;在特定情况下一定要建立相互依赖的关系,则可以考虑采用如下方案:

  • 正常建立相互依赖关系,但在两者的改变回调函数中准确分析以确定对方是否真的已发生改变,或者说对方的改变是否应该引起自身的改变,然后再基于分析结果按需改变自身,而不是在收到通知后始终改变自身;
  • 设立一个中间对象来解耦相互依赖关系。比如: A和B两个对象需要相互引用并相互监测对方改变,则我们可以引入C对象,并设置C对象同时引用A和B对象,这样,当A和B中任意一方发生改变时C都会收到通知,此时C就可以根据实际情况修正另一方了,从而达到能相互通知但不循环依赖的要求。
  • 被依赖对象必须是已持久化对象。在建立引用关系时,要求被依赖对象必须是已经添加Model中的对象,这是因为需要将用于唯一标识被依赖对象的标识符保存到依赖对象中,而对于尚未添加到Model中的对象而言,它并不具备此种标识符;从来源上看,被依赖对象通常与依赖对象位于同一Model中,也可以位于不同Model中,也可以位于不同DGN文件中,同时,也可以是不同类型的对象(图形对象与非图形对象),系统会自动识别并处理的。相较而言,对于引用对象则无要求,它可以是已持久化对象,也可以是尚未持久化的对象,只是在尚未持久化之前虽然可以保存被引用对象的记录,但无法接收并触发改变通知,直至持久化。
  • 注意单依赖和多依赖约束。目前内置提供的两种依赖关系中,通用隶属关系不限制依赖的数量,亦即支持单个对象依赖于单个对象(1:1模式),单个对象依赖于任意多个对象(1:n模式),任意多个对象依赖于单个对象(n:1模式);但对于变换类型的隶属关系,系统仅支持单个对象依赖于单个对象(1:1模式)以及任意多个对象依赖于单个对象(n:1模式),不支持单个对象依赖于任意多个对象(1:n模式)。
  • 两特定对象间不要使用多种隶属关系。在特定的两个对象间不要建立多个同向的隶属关系,不论其类型是否相同。例如: 已经建立了A <引用Type1 > B,就不要再建立A <引用Type2 > B,不论引用Type1与引用Type2的类型是否相同。这是因为不论何种类型的隶属关系,它们都是基于通用的隶属关系衍生出来的,在本质上有着统一性(都有专用的添加引用方法和修改响应函数)和唯一性(全局唯一的隶属关系标识符),最大的区别在于不同的衍生隶属关系具备不同的预定义行为(如自动组合变换矩阵),与此同时,所有类型隶属关系所使用的响应函数从本质上都是相同的(或称通用的)。所以,对同一对象无需使用多种隶属关系,在一种隶属关系中同样能处理。
  • 隶属关系支持跨Model复制,支持深度克隆。在底层平台的基础上,实现了基于隶属关系机制的跨Model复制和深度克隆。例如: 在同一Model中建立了A引用B的关系,如果将B复制到同一个或者另一个Model,则A也会随之拷贝到同一个或另外一个Model中,形成新的A与新的B建立引用关系。使用 MstnModel 的 静态CopyModelContents 函数可以实现跨Model复制;使用MstnModel的静态 CopyElements 函数实现多个或单个CimGraphicObject的拷贝;是否支持深度克隆取决于建立引用关系时关系强度和关系打开开关参数。

    3.4 自定义对象的EC属性应用

    对于CIM的自定义对象而言,其核心包括如何设计并创建自定义对象、如何绘制自定义对象、如何建立并响应对象间的隶属关系。从本质上而言,这些核心环节都是围绕着自定义对象的属性而展开的,而自定义对象的属性又是将对象用于表达业务对象的核心。所以,本章将详细介绍属性相关的一些技术和约定,主要包括:如何定义不同类型的属性、如何获取和修改属性、如何监听属性的改变等。

    3.4.1 定义EC属性

    如前所述,对于CIM对象而言,其所有属性都是通过与之关联的EC类来定义的,而这些EC类又包含在特定的EC Schema文件中。在运行时,系统会加载这些EC Schema文件,并将其中的EC类作为类级别数据结构的模板来使用,在需要时会实例化这些模板,并将所创建模板的实例附加在对象上,这在EC术语内称之为EC实例或EC Instance,这样,我们就可以通过访问这些EC实例来存取其中的数据成员了。由此可见,如果期望在EC实例中能提供特定的数据成员,我们只需要在对应EC Schema文件中特定的EC类中添加对应数据成员(在EC术语中称为EC属性或EC Property)即可,这样,我们在这方面的工作重心就转移到了对EC Schema文件的编辑上了。虽然可以使用公司提供的Bentley Class EditorTM工具完成绝大多数EC Schema文件的编辑工作,但从开发角度看,了解其中的详细信息还是大有脾益的。

    3.4.1.1 基本EC属性的定义

    介绍复杂技术的最简洁途径就是举例说明。我们先来看一段EC类的定义:





    从上面的内容可以看出,它代表的是EC类的定义,所包含的信息如下:

  • 一个类型的节点代表一个EC类的定义,其中的属性字段含义如下:

    • typeName: EC类的类型名称,必选,它用于在当前Schema范围内标识一个EC类,若要在当前Schema范围外表示此EC类,则需要按照 <命名空间前缀>: 方式来标识;
    • isDomainClass: 是否为域类,必选,其取值通常都是True;
    • isCustomAttributeClass: 是否为自定义属性类,可选,默认为False;自定义属性类,意味着这个类专用于为EC Schema或EC类或EC属性提供自定义属性,而不是作为独立EC类来使用的;
    • isStruct: 是否为结构体定义,可选,默认为False;当被标记为结构体时,此EC类就可以作为自定义属性类型来使用,否则不能用作自定义属性的类型;
  • 此EC类中包含2个类型的节点,每个此类型的节点代表一个EC类的数据成员(或称EC属性);此外,还包含1个类型的节点,每个此类型节点代表一个数组类型的EC类数据成员。其中的各个属性字段含义如下:

    • propertyName: EC属性的名称,必选,用于在代码层面唯一标识特定属性,所以这个名称在类中不能重名,包括在类的所有基类中;
    • typeName: 属性的值类型,必选,它可以是任何内置的值类型,也可以是任何标记为结构体(也就是带有isStruct=”True”属性的EC类)的自定义类型;对于数组类型的属性而言,它指的是数组中元素的类型。目前支持的内置类型有:
      • int: 32位整型数
      • long: 64位整型数
      • double: 64位双精度浮点数
      • boolean: 布尔值
      • string: 字符串值
      • point2d: 2D点坐标
      • point3d: 3D点坐标
      • binary: 二进制数据块
      • dateTime: 时间日期
      • IGeometry:几何类型
    • displayLabel: 属性的显示标签,可选,用于指示在属性面板中显示此属性时应使用的显示名称,此被忽略时将使用propertyName作为其显示名称;
    • minOccurs: 数组中最少可容纳的元素个数,必选,取值应为大于等于0的整数;
    • maxOccurs: 数组中最多可容纳的元素个数,必选,取值通常应为大于0的整数,但也可以用unbounded来表示不受限制;

      3.4.1.2 CustomAttribute的使用

      使用Custom Attributes,就是以Schema文件为基础,在其中附加预定义的XML节点。由于需要引用额外的Schema类型的XML,所以,但凡需要使用CustomAttribute时,必须在当前Schema中添加对特定Schema的引用节点。例如: 节点将添加对EditorCustomAttributes.01.00.ecschema.xml文件的引用。添加了此种引用后,我们就可以在当前Schema中以<命名空间前缀>:<类名>的方式来使用所引用Schema中的类定义了,例如: beca:OtherECClassName。
      通常,我们定义或使用CustomAttribute的目的在于指示系统的属性面板以特定的形式或方式来显示和处理我们的EC属性,比如: 将EC属性显示成带有下拉选择框的形式、将EC属性显示成带有文件或路径浏览能力的形式、将EC属性显示成带有按钮的形式等等。由于这些显示形式的控制都是针对属性面板而言的,所以相关的CustomAttribute通常都定义在EditorCustomAttributes中,我们需要引用它。此外,还有一些非面向属性面板的CustomAttribute,它们定义在Bentley_Standard_CustomAttributes中,需要时也可以引用它。
      这些CustomAttribute可以大致分为3类,也就是: 面向ECSchema的,用于控制系统处理Schema本身以及Schema范围内的一些特性;面向ECClass的,用于控制特定EC类的某些行为;面向ECProperty的,用于控制系统解释特定ECProperty的行为,下面我们将逐一介绍这3类CustomAttribute的使用方式。
  • 面向ECSchema的CustomAttribute: 这类Attribute需定义在节点内的公共区域,例如:




    用以指示此Schema中所有表示长度、面积、体积等量的数值,在保存到DGN中时都将会自动换算成以UOR为单位的值,而且 StoresUnitsAsUors 这个CustomAttribute来自于 EditorCustomAttributes 这个Schema文件;

  • 面向ECClass的CustomAttribute: 这类Attribute需要定义到节点内来使用,例如:




    True




    这个ECClass中包含两个类级别的CustomAttribute: DisplayOptions和DontShowNullProperties,其中,前者用于指示在属性面板中不要显示(因为Hidden节点的值为True)此类中的任何EC属性,并且它来源于Bentley_Standard_CustomAttributes.01.10.ecschema.xml文件;后者用于指示不要显示此类中的任何空值EC属性,并且此属性来源于EditorCustomAttributes.01.00.ecschema.xml文件。

  • 面向ECProperty的CustomAttribute: 这类Attribute需要定义到节点内来使用,例如:





    8


    113000




    这个ECProperty中包含两个Property级别的CustomAttribute: Category和PropertyPriority,其中,前者用于说明此EC属性应被分类为值为8的类别(基本几何信息类),它来自于EditorCustomAttributes.01.00.ecschema.xml文件;后者用于说明此EC属性在属性面板中显示时的排序优先级(影响其在属性面板中的显示顺序)为113000(介于低优先级和中优先级之间),它来自于同一Schema文件。

    3.4.1.3 常用的CustomAttribute

    系统内置定义了大量的CustomAttribute,用于精细化控制UI在处理ECSchema、ECClass以及ECProperty时的特定行为,下面我们将提供一些常用的CustomAttribute。在了解了上一节的内容后,我们就可以轻松的使用它们了。

  • 定义在EditorCustomAttributes中的Attribute: | 名称 | 应用对象 | 功能 | | —- | —- | —- | | AlwaysExpand | ECProperty | 指示是否应始终以展开方式显示此属性(对结构体、数组类型的属性而言)。 | | ArrayBehaviorAttributes | ECArrayProperty | 指示UI应如何控制数组类型属性的显示,包括是否应将元素作为数组对待、是否允许显示空数组、是否允许用户在属性面板上直接添加和删除元素。 | | ArrayMemberNameFromValue | ECArrayProperty | 指示UI使用应使用数组元素的值作为其显示标题。 | | Localizable | ECProperty | 指示UI这个字符串类型属性的值是可以被本地化的。 | | BooleanDisplay | ECProperty | 指示UI应使用两个给定的字符串来显示这个布尔值(也就是True对应的字符串以及False对应的字符串) | | Category | ECProperty | 描述此ECProperty与分类有关的附加信息,主要包括: 所属分类的ID、所属分类的名称、所属分类的显示名称、所属分类的说明文字、所属分类的优先级值以及所属分类的属性是否应展开显示。 | | StringLength | ECProperty | 指示UI此字符串类型可接受的字符串的最大/最小长度。 | | ClassPriority | ECClass | 指定此ECClass的显示优先级值。 | | DontLoadProperty | ECProperty | 指示UI此属性的加载会很耗时,所以默认时不要加载此属性的值。 | | DontShowNullProperties | ECClass | 指示UI不要显示此类中值为空的属性。 | | DontShowNullProperty | ECProperty | 指示UI当此属性的值为空时就不要显示了。 | | ExtendType | ECProperty | 描述此属性的扩展类型,主要包括属性的标准类型值(如表示长度、表示面积、表示体积等等),或者所属自定义类型的名称。 | | FilePickerAttributes | ECProperty | 指示此属性是个文件名类型的属性,其中包含选择文件所需的相关信息。在必要时,系统会使用此属性中提供的信息弹出文件选择对话框,并能在对话框正常返回时获取选择结果作为属性值。 | | Format | ECProperty | 指示UI应使用此属性提供的格式字符串来格式化这个ECProperty的值。 | | HideMembers | ECArrayProperty | 指示UI是否应在属性指定的情况下隐藏数组的成员,包含是否整体隐藏数组成员、是否在2D视图下隐藏数组成员、是否在3D视图下隐藏数组成员。 | | HideProperty | ECProperty | 指示UI是否应在属性指定的情况下隐藏此属性,包括是否在2D视图下隐藏、是否在3D视图下隐藏,或者按指定的表达式来确定是否隐藏。 | | IgnoreZ | ECProperty | 指示UI在显示此point3d类型的属性时应忽略其Z值。 | | MemberExtendedType | ECArrayProperty | 功能上类似于ExtendType,只不过它用于描述数组类型属性的成员。 | | RequiresRefresh | ECProperty | 指示UI应在此属性被修改后重新刷新属性面板。 | | RequiresReload | ECProperty | 指示UI应在此属性被修改后重新加载所有属性。 | | SchemaPriority | ECSchema | 指定Schema的优先级值。 | | StandardValues | ECProperty | 用于为ECProperty指定一个可供选择的值列表,这个值列表由一组显示字符串与值的配对序列组成,其中的显示字符串为每个选项的显示标题,而值则对应于每项的实际存储值。UI在显示此属性时会使用提供的值列表形成一个下拉列表以供用户选择,用户选择完成后会将结果传递给属性。 | | StoresUnitsAsUors | ECSchema | 用于指示系统,应将此Schema中所有扩展类型(ExtendType或MemberExtendedType修饰的)的属性值按工作单位来显示,按UOR为单位来保存。若无此自定义属性,则Schema中所有属性值将默认按米单位保存。 | | UseBooleanInExpressions | ECProperty | 用于布尔类型的属性,用于指示系统在计算EC表达式时不要将此属性中的布尔值转换为字符串。 |

3.4.1.4 特殊类型的EC属性定义

在实际应用中,除了定义常规的EC属性外(即基本的单值或数组类型),我们可能还需要定义一些复杂的或特殊的EC属性,而这些EC属性的定义都需要使用特殊的CustomAttribute才能完成。接下来我们介绍几种特殊EC属性的定义方法:

  • 结构体类型的EC属性定义: 正如前面所介绍的,在特定ECClass中加入isStruct = “True” 这个XML属性字段后,则这个ECClass就可以在其他ECProperty中用作其typeName的值了,与此同时,也需要在这个ECProperty中加入 isStruct = “True” 属性字段。例如:






上例中,EC类CurveData被定义成一个结构体类型,然后被以类型的方式用在了EC类PointData中。这里需要注意的是,PointData依旧是个结构体类型,可以被作为类型用在其他地方。所以,结构体类型的定义支持任意层次的嵌套;同时,我们也可以将这个定义延伸到结构体数组类型,道理都是一样的。 - 文件选择类型的EC属性定义: 这种类型的EC属性在显示时,系统会提供一个编辑框和一个按钮,用户既可以直接输入文件名,也可以通过点击按钮来选择文件,选择得到的文件名会自动填入到编辑框中。要定义这种类型的EC属性,我们需要用到上表FilePickerAttributes和ExtendType类型的CustomAttribute,并且需要通过在ExtendType中指定其Name值为ECFile,具体使用如下:

<__ExtendType__ __xmlns__=__"__EditorCustomAttributes.01.00__"__>
<__Name__>ECFile

<__FilePickerAttributes__ __xmlns__=__"__EditorCustomAttributes.01.00__"__>
<__Title__>Select RealityMesh File
<__FilterIndex__>0
<__UseSaveFile__>False
<__CreatePrompt__>False
<__OverwritePrompt__>False
<__FilterList__>Reality Mesh (.3mx)|.3mx



其中,ExtendType指定了使用EC的文件选取功能(这种扩展后面介绍),而FilePickerAttributes则指定了配置文件选择对话框所需的所有信息。 - 路径选择类型的EC属性定义: 这种类型的EC属性在外观行为上与文件选择类型属性非常相似,只不过点击按钮后弹出的是路径选择对话框,而不是文件选择对话框;相较而言它的定义要更简单一些,定义如下:


DgnFolerBrowser


- 下拉列表类型的EC属性定义: 这种类型的EC属性显示在属性面板上时,系统会为它提供一个标题和一个下拉列表,用户可以通过下拉列表在已有选项中选择一个字符串项,选择完成后系统会将选择的字符串项作为此属性的值来显示,对内部而言,与用户所选择字符串项相对应的int值会作为属性的值保存起来。这种类型EC属性的定义需要用到上表中名为StandardValues这个CustomAttribute,具体格式如下:

<__StandardValues__ __xmlns__=__"__EditorCustomAttributes.01.02__"__>
<__ValueMap__>
<__ValueMap__>
<__DisplayString__>None
<__Value__>0

<__ValueMap__>
<__DisplayString__>Fillet
<__Value__>1

<__ValueMap__>
<__Value__>2
<__DisplayString__>Chamfer


<__MustBeFromList__>True



由上可见,定义一个带下拉列表的EC属性,有以下要求: - 属性的typeName必须是int; - 下拉列表中的每一项都需要用一个ValueMap节点来描述,这个节点中的DisplayString和Value子节点值分别代表每一项的显示名称以及数值,而且这个数值的类型也必须是int; - 用于代表下拉列表中每项的ValueMap节点必须统一包含在另一个ValueMap中以构成选择列表; - 使用StandardValues作为下拉列表属性的顶层节点,其内包含下拉列表的ValueMap和MustBeFromList节点,其中,这个MustBeFromList用于控制用户是否只能从所提供列表中选择,而不能直接输入。 - 自定义行为的EC属性定义: 这种类型的EC属性主要用于支持用户自定义行为。这种属性在显示时,系统会提供一个编辑框和一个按钮,编辑框中的内容用户可随意修改,当用户点击按钮时会在系统内部触发ICimEventListener:: OnCustomEditProperty ()[C++]回调或CimEventManager. OnCustomEditProperty ()[C#]事件,此时用户就可以通过响应这个回调/事件来实现自定义行为,例如: 执行keyin、显示上下文菜单、弹出对话框等等。由于这是CIM扩展出来的EC属性,所以在定义上非常简单,具体形式如下:


CimCustomEditItem



这里需要注意的是,ExtendType中的Name的值必须是 CimCustomEditItem 。 - 动态下拉列表的EC属性定义: 这种类型的EC属性主要用于支持用户动态生成下拉列表属性的条目。这种属性在显示时,系统会提供一个编辑框和一个按钮,当用户点击按钮时会在系统内部触发ICimEventListener:: OnGetDropdownListItems ()[C++]回调或CimEventManager. OnGetDropdownListItems ()[C#]事件,此时用户就可以通过响应这个回调/事件来动态添加下拉列表的选项。由于这是CIM扩展出来的EC属性,所以在定义上非常简单,具体形式如下:


CimDropdownListItem



这里需要注意的是,ExtendType中的Name的值必须是 CimDropdownListItem 。 - 同理,如果实现动态下拉复选框列表,将 CimDropdownListItem 修改为CimDropdownCheckListItem即可,系统内部会调用对应的函数来获取用户动态添加的选项。 - 其他内置特殊EC属性的定义: 在系统内部已经预定义了一些特殊的EC属性,这类EC属性的定义都是以ExtendType这种CustomAttribute为基础,通过为其Standard字段指定不同的值来完成的。其基本使用方式如下例所示:


7



系统中预定义的Standard值、对应属性的typeName及其含义如下表所示: | Standard | typeName | 含义 | | —- | —- | —- | | 1 | int | 代表Element的类型ID。 | | 2 | int | 代表Element的层ID, 显示时系统会自动为其配置层下拉列表。 | | 3 | int | 代表Element的颜色ID, 显示时系统会自动为其配置颜色下拉列表。 | | 4 | int | 代表Element的线宽值, 显示时系统会自动为其配置线宽下拉列表。 | | 5 | int | 代表Element的线形样式ID, 显示时系统会自动为其配置线形下拉列表。 | | 6 | int | 代表Element的类ID, 显示时系统会自动为其配置元素类下拉列表。 | | 7 | point3d | 代表以上下文Model中的单位显示的3D点坐标(如: 1.23m,4.56m,7.89m)。 | | 8 | double | 代表以上下文Model中的单位显示的距离值(如: 4.56m)。 | | 9 | double | 代表以上下文Model中的单位显示的面积值(如: 8.45 Sq.m)。 | | 10 | double | 代表以上下文Model中的单位显示的体积值(如: 2.68 Cu.m)。 | | 11 | double | 代表以上下文Model中的单位显示的角度值(如: 12°15’22’’)。 | | 15 | int | 代表Element的文字样式ID, 显示时系统会自动为其配置文字样式下拉列表。 | | 16 | string | 代表Element的渲染材质名称, 显示时系统会自动为其配置材质选择器下拉列表。 | | 17 | double | 代表以上下文Model中的单位显示的方位角度值(如: N15°12’30’’E)。 | | 18 | point3d | 代表无单位3D点坐标。 | | 19 | double | 代表有名称的比例值。 | | 20 | string | 代表材质投影命名组。 | | 22 | double | 代表Element的透明度值。 | | 24 | long | 代表文件大小, 显示时带有省略样式的字符串表达。 | | 25 | long | 代表文件大小, 显示时带有完整样式的字符串表达。 | | 26 | int | 代表属性的显示优先级, 值越小显示越靠前。 | | 27 | int | 代表Element的字体ID,显示时系统会自动为其配置字体名称下拉列表。 | | 28 | int | 代表Element的大字体ID, 显示时烯烃会自动为其配置字体名称下拉列表。 | | 29 | long | 代表Element的ElementId。 | | 30 | string | 代表Element的填充单元名称。 | | 57 | point3d | 代表一个以上下文Model中的单位现实的绕三个轴的旋转角度。 | ### 3.4.2 获取和设置EC属性 定义好了EC属性后,就可以在运行时以它为模板动态生成EC实例并其关联到我们的自定义实体上,这样就EC实例就成了我们在运行时的数据存储容器了。既然是数据存储容器,那么我们对它的操作无非就是将数据保存到其中以及从其中获取数据。下面我们就分别详细介绍如何从自定义对象上获取EC数据以及如何设置EC数据。 #### 3.4.2.1 设置EC属性的值 在CIM自定义实体上设置EC数据,实际上就是调用SetValue/SetValues/SetArray函数。这些函数的功能为: - SetValue: 此函数用于将指定的值设置到EC属性中指定的EC Property上。 - 对于C++而言,这个函数仅支持设置单值数据项,而且它实际上是由一组重载的函数组成,重载函数的区别就是所支持的数据类型不同,所以在使用时必须指定正确的数据类型,这对于使用立即值的调用而言非常重要。比如: 实际调用时传递3、3.0、3LL是完全不同的,系统会分别将其识别为int、double、int64,继而调用对应版本的SetValue; - 对于C#而言,它同时支持设置单值和多值,在传递单个立即值时同样需要严格控制字面数据类型的正确性,在传递多值时,必须使用任何基于System.Collections.Generic.IEnumerable派生的类作为容器来容纳要传递的多值; 此外,系统还提供了[]操作符以简化对SetValue/GetValue的调用。 - SetValues: 此函数用于将指定字典中的值设置到EC实例中名称相匹配的EC属性上。此方法的使用与SetValue基本相同,主要区别在于: - SetValues面向的是整个EC实例,而不是特定的EC属性; - SetValues仅支持设置内置类型的单值属性,不支持自定义类型以及数组类型; - SetArray: 此函数为C++专用接口,仅用于设置元素类型为基本类型的数组类型EC属性。 #### 3.4.2.2 获取EC属性的值 在CIM中,要获取自定义对象上特定EC属性的值,需要调用GetValue/GetValues/GetArray函数。这些函数的功能为: - GetValue: 此函数用于获取指定EC属性的值。 - 对于C++而言,它实际上对应一组以数据类型区分的重载函数,在使用时只需指定待获取值的EC属性名称以及所返回值的引用即可,需要注意的是: 所指定的待获取值的类型必须与ECProperty中所定义的类型相同,其次就是此函数仅支持获取单个基本类型的EC值,对于数组类型的EC值以及自定义类型的EC值,需要逐元素/逐成员获取; - 对于C#而言,它返回以System::Object形式表达的值,可用于获取基本类型的单值以及数组值,对于单值,只需将返回值类型转换为预期的类型即可,对于数组类型,需要将返回值转换为预期类型的数组类型,如: var retArray = (double[])retVal; - GetValues: 此函数用于将EC实例中的所有EC属性打包成一个字典对象并输出来,它主要用于对象数据的整合和交换。在实际使用时需要注意: 此方法仅支持打包基本类型的单值EC属性,不支持自定义类型及多值类型; - GetArray: 此函数为C++专用接口,仅用于获取元素类型为基本类型的数组类型EC属性的值。 #### 3.4.2.3 accessString的使用 在访问和操作CIM自定义对象的EC属性时,我们会使用GetValue/GetArray/SetValue/SetArray函数,大但这些函数在功能上有限制: 只能直接操作内置类型的单值类型EC属性以及数组类型EC属性,那么如何访问自定义类型的单值和数组EC属性呢? 此时就需要使用accessString了。我们可能注意到了,上述4个函数都接受一个名为accessString的参数,其含义为待访问EC属性时的访问字符串。这个访问字符串用于唯一的标识将要被访问的属性,而且在C++和C#中完全相同。它的取值可分为两类: - 立即属性名: 也就是直接使用需要被访问的EC属性的名称,此时这个属性将被整体操作,而不是逐元素、逐成员变量的操作,例如: - obj.SetValue(“Height”, 3.0); // C#: 设置单值属性Height的值为3.0 - obj.SetValue(“Widths”, new double[]{ 1.5, 2.0, 2.5 }); // C#: 设置数值属性 Widths 的值 - double height = obj.GetValue(“Height”); // C#: 获取单值属性Height的值 - double[] widths = obj.GetValue(“Widths”); // C#: 获取数组属性Widths 的值 - 表达式属性名: 也就是使用类C表达式所指定的需要被访问的EC属性名称,这种表达式中可以包含对数组下标以及结构体成员变量索引的使用,所以它能以较灵活的形式表达将要访问的属性,这样表达出来的属性始终是一个基本类型的单值属性。例如: - obj.SetValue(“Widths[2]”, 4.3); // C#: 设置数组属性Widths中第2个元素的值; - obj.SetValue(“Desk.Legs[1].Height”); // C#: 设置Desk(单值结构体)中Legs(数组结构体)中第1个元素中Height(基本类型)属性的值; - double width = obj.GetValue(“Width[2]”); // C#: 获取数组属性Widths中第2个元素的值; - double height = obj.GetValue(“Desk.Legs[1].Height”); // C#: 获取Desk中Legs中第1个元素中的Height属性的值。 ### 3.4.3 监听EC属性的变化 在很多情况下,我们可能会需要及时知道特定EC属性值是否已被修改以及被修改前后的值,以便于内部代码执行特定的逻辑或者完成特定的优化处理。例如: 我们内部的某些代码逻辑可能需要同时依赖于特定EC属性的当前值以及最近一次修改前后的差量值。一个典型的应用场景就是,假如我们有一个涵洞对象依赖于一个路线对象,同时期望涵洞对象能始终定位在路线特定桩号处,每当路线发生改变时我们需要确认涵洞是否依旧在预设的桩号处,而这个确认就是建立在对路线EC属性改变的基础之上的,比如:需要查看当前路线所改变的EC属性是否会影响涵洞在路线上的位置,以及改变量有多大,涵洞应如何调整等。在CIM系统中,提供了两种方式的EC属性监听机制,一种是面向CIM对象级别的,另一种是面向全局的。下面分别介绍这种机制的使用。 #### 3.4.3.1 CIM对象级的EC属性监听 CIM对象级别的EC属性监听机制,主要用于监听附加在CIM对象上任何EC实例中EC属性的改变。此机制的运行机理为: 任何时候,当特定EC属性被修改后,系统会分析并获取与此EC属性关联的CIM对象,之后通过在CIM对象上调用特定的虚函数来通知此CIM对象。这种EC属性监听机制使用起来非常简单,具体如下: - 创建并实现一个CIM自定义对象; - 实现此自定义CIM对象中的 OnPropertyValueChanged[C++]或OnPropertyValueChanged[C#] 在运行时只要与此CIM对象有关的EC属性被修改,则此函数就会被调用,用户只需在此函数中实现需要的逻辑即可。
在使用时需要注意的是: - 不要在这个回调函数中直接修改当前对象的EC属性值,因为这样的话会导致此函数被重入调用,在特定情况下会形成递归死锁; - 此机制仅监听与当前CIM对象有关的EC属性修改; #### 3.4.3.2 全局EC属性监听 在特定情况下,我们可以需要监听所有的EC属性修改并按自定义逻辑进行有选择性地进行响应,而不限于与特定CIM对象有关的EC属性修改。此时,我们就可以使用CIM所提供的基于事件管理器的全局EC属性监听机制。
此监听机制是以虚函数回调[C++]或事件方式[C#]实现在CimEventManager这个单例类中的。所以,此机制的使用因语言的不同而略有差异,具体如下: - 在C++中,按如下步骤实现: - 基于ICimEventListener接口派生一个类,并实现其中的OnElementPropertyChanged以及其他需要方法; - 实例化派生的类并通过如下调用将其实例注册到事件管理器中: - CimEventManager::Get().AddListener(pMyListenerP); - 在完成监听器使用后,将其注销: - CimEventManager::Get().DropListener(pMyListenerP); - 在C#中,按如下步骤实现: - 在监听相关的类中添加一个原型为 void OnPropertyModified (object sender, CimEventManager.OnElementPropertyArgs args) 的响应函数并实现其逻辑; - 创建事件响应器,并将其挂到事件源上: - CimEventManager.OnElementPropertyChanged += new EventHandler(OnPropertyModified); - 在完成事件响应器的使用后,应及时断开其与事件源的连接: - CimEventManager.OnElementPropertyChanged -= new EventHandler(OnPropertyModified); ### 3.4.4 EC属性的显示过滤控制 每当属性面板在显示绑定到CIM对象上特定EC实例中的属性时,我们可能会需要根据所属CIM对象的当前状态来控制某些EC属性的显示状态,比如: 控制某些属性的可见性(显示或隐藏)、控制某些属性的可写性(只读或可写)等。典型的应用场景为,例如: 我们有个代表矩形断面的自定义对象,它拥有长度Length、宽度Width以及是否长宽SameSize相等3个属性,当SameSize的属性值为False时我们需要显示Length和Width两个属性,当SameSize的值被切换为True时,我们只需要显示Length属性而隐藏Width属性,此时我们就需要在知道SameSize被切换为True时隐藏Width属性的显示,并在知道其被切换为False时重新显示Width属性。在这些情况下,我们就可以使用CIM提供的机制来控制EC属性的显示。针对EC属性的显示过滤,CIM提供了2种方式,下面逐一介绍它们的使用。 #### 3.4.4.1 静态EC属性过滤 所谓静态EC属性过滤,也就是在创建EC属性定义的时候,通过使用系统预定义的XML标记或者特定的CustomAttribute来控制特定EC属性的显示状态。需要注意的是,虽然这种控制方式无需编写代码,但会对所有基于这个EC类所创建的EC实例产生影响。例如: 如果某个EC类中的某些EC属性以这种方式设置为隐藏的,则所有基于此EC类创建的EC实例中,这些属性默认也都是隐藏的。用于静态控制EC属性的XML标记或CustomAttribute有以下几种: - 直接在ECProperty中定义readOnly=”True” 来控制属性的只读性,例如: <ECProperty propertyName=”Order” typeName=”int” **readOnly=_True
displayLabel=”Order”>
此字段为可选的,省略时的默认值为False; - 通过在所属ECClass中使用
DisplayOptions这个CustomAttribute并通过其中Hidden字段的值来控制整个EC类中所有EC属性的可见性,例如:

<__DisplayOptions__ __xmlns__=__"__Bentley_Standard_CustomAttributes.01.10__"__>
<__Hidden__>True
**


  • 通过在ECProperty定义中使用HideProperty这个CustomAttribute并通过其中的If2DIf3D两个字段的值开控制这个EC属性在2D视图下以及3D视图下的可见性,例如:




<__HideProperty__ __xmlns__=__"__EditorCustomAttributes.01.00__"__>
<__If2D__>True
<__If3D__>False




上例中,DesignPhase这个属性在2D视图下不显示,但在3D视图下正常依旧显示。

3.4.4.2 动态EC属性过滤

在特定情况下,我们可能需要在运行时根据特定的上下文情况,动态调整特定EC实例中特定EC属性的显示状态。而且要求这种调整不影响用于创建EC实例的EC类,不影响其他任何EC实例。在这种情况下,我们就需要使用CIM中提供的动态EC属性过滤机制了。
这种EC属性过滤机制的使用比较简单,按如下步骤进行即可:

  • 创建并实现自定义CIM对象;
  • 实现其中的CimRoot::_OnFilterProperty[C++]或CimRoot.OnFilterProperty[C#]方法来实现自定义过滤逻辑:
    • 当某个属性需要以只读方式显示时,函数返回1;
    • 当某个属性需要隐藏时,函数返回2;
    • 当某个属性需要以定义时的方式显示时,函数返回0.
  • 在使用前先激活此后过滤机制: CimPropertyManager::EnablePropertyFilter(true);
  • 在完成使用后撤销对此过滤机制的激活: CimPropertyManager::EnablePropertyFilter(false);

在此过滤机制的使用中,之所以需要预先激活或撤销激活,主要是出于性能考虑。当此过滤机制激活时,属性面板在显示CIM对象上任何EC实例中的EC属性前,都会调用这个回调来确定每个EC属性预期的显示状态,在一定程度上可能会延迟属性面板的刷新,导致系统有卡顿出现。所以,在_OnFilterProperty或OnFilterProperty中设计的自定义过滤逻辑应在确定出显示规则后尽快返回,以防阻塞系统。

3.5 其他工具类的使用

3.5.1 事件管理器

在CIM对象的宿主系统——基于Microstation的系统中,每时每刻都发生着各种各样的事件。如果我们能准确的知道许多特定事件的发生时机或者在特定事件发生时我们能得到通知,那么我们就有机会充分利用这些事件以设计一些能与系统紧密结合并协调运行的逻辑单元,从而实现以宿主系统为事件源的事件驱动型产品。比如,我们设计的某些逻辑可能需要在系统当前事务结束后且在图形系统开始全面刷新前被执行,我们需要能准确把握当前激活Model切换前后的时机,我们需要知道何时有Element添加到Model中了,等等。在以上这些场景下,我们就可以使用CIM系统提供的事件管理器CimEventManager来监听并响应系统级别的事件了。
CimEventManager是个全局单例类,它会在CIM内核启动时自动并将自己设置为一个系统范围内的事件源。任何需要监听特定事件的构造,只需从CimEventManager监听即可。在实际使用时,由于CIM同时支持C++和C#两种接口方式,而这两种方式的使用不尽相同,具体如下:

  • C++方式的使用流程:

    • 基于 ICimEventListener派生一个类,并按需实现其中的虚方法;
    • 实例化派生的类,并用如下调用将其注册到事件管理器中: CimEventManager::Get().AddListener(pMyListener);
    • 在完成对监听器的使用后,使用如下调用撤销其注册并回收内存:

      CimEventManager::Get().DropListener(pMyListener);
      delete pMyListener;
      pMyListener = nullptr;

  • C#方式的使用流程:

    • 在期望能监听事件的类中,实现一个具备如下原型的方法:

public void OnEventCallback (object sender, CimEventManager.OnElementPropertyArgs);

  • 在使用监听器之前,将其挂接到事件源上,例如:

CimEventManager.OnElementPropertyChanged += new EventHandler(OnEventCallback);

  • 不再需要监听器时,将其从事件源上断开,例如:

CimEventManager.OnElementPropertyChanged -= new EventHandler(OnEventCallback);
目前,CimEventManager支持如下类型的事件:

  • OnElementAdded: 一个Element已添加到特定Model中;
  • OnElementDeleted: 一个Element已从特定Model中删除;
  • OnElementPropertyChanged: 附加在特定Element上,特EC实例中特定EC属性的值已从一个值改变为另一个值;
  • OnElementXAttributeAdded: 一个新的XAttribute已添加到了指定的Element上;
  • OnElementXAttributeToBeDeleted: 指定Element上指定的XAttribute将要被删除;
  • OnElementXAttributeModified: 指定Element上指定的XAttribute已被修改;
  • OnElementXAttributeReplaced: 指定Element上指定XAttribute中所包含的数据已被替换;
  • OnCurrentTransactionToBeClosed: 当前事务即将关闭,所有修改即将被提交(或称即将生效);
  • OnDisplayToBeUpdated: 当前事务已关闭,图形系统的更新即将开始;
  • OnDisplayUpdated: 当前事务已关闭,图形系统的更新也已完成,新的事务即将启动;
  • OnModelToBeDeleted: 指定的Model即将被从DGN文件中删除;
  • OnModelAdded: 指定的Model已添加到当前DGN文件中;
  • OnModelPropertyToBeModified: 指定Model的信息块将要被修改;
  • OnModelPropertyModified: 指定Model的信息块已被修改;
  • OnDgnFileOpened: 指定的DGN文件已加载且初始化已完成;
  • OnDgnFileToBeClosed: 指定的DGN文件即将关闭;
  • OnModelRefPreActivate: 指定的附件(Attachment)将要激活;
  • OnModelRefToBeActivate: 当前Model将要从一个Model切换为另一个Model;
  • OnModelRefActivated: 当前Model已从一个Model切换为另一个Model;
  • OnCustomEditProperty: 一个扩展类型为 CimCustomEditItem 的自定义EC属性需要通过此事件响应来完成编辑;
  • OnGetDropdownListItems:一个扩展类型为 CimDropdownListItem的 自定义EC属性需要通过此事件响应来完成自定义列表选项的动态生成;
  • OnGetDropdownCheckListItems:一个扩展类型为 CimDropdownCheckListItem的自定义EC属性需要通过此事件响应来完成自定义复选框列表选项的动态生成;

由于事件监听器的运行,是通过监听事件管理器中的事件来驱动的。所以,在设计事件监听器时应遵循以下原则进行:

  • 每个事件的响应不依赖于其他事件的触发顺序;
  • 每个事件的处理不依赖于此事件的历史处理状态(即无状态处理);
  • 每个事件在响应时应将事件参数视为只读的;
  • 每个事件的响应代码应尽可能快的返回;
  • 不再需要事件监听器时应及时将其撤销注册并销毁;

    3.5.2 通用几何提取器

    有些时候,我们可能会需要将特定Element所代表的几何图形提取出来做进一步处理,比如删选其中的曲线用于坐标计算,筛选其中的实体用于网格化处理等等。这个过程有时可能会很简单,比如: 这个Element是内置类型的有对应API可用于此目的;但有时候这个过程也可能会很复杂,比如: 这个Element是第三方模块创建的,我们不知道它的形成规则,也没有能操作它的API。为此,CIM系统提供了一个名为CimGraphicsElementCollector的工具类,专门用于将特定Element所代表的几何图形按原样分类提取出来以供后期使用。使用此工具的最大好处在于,它是通用的,使用时不需要关心目标Element的来源和种类,只需要按固定模式执行提取即可;这个工具除了能处理单个Element外,还支持批量处理位于特定视口中的所有Element,并能将得到的几何体与其来源Element相关联以供反向查询使用。
    此工具的使用非常简单,只需按照执行处理和提取结果两步进行即可:

  • 执行处理: 即开始处理指定的Element或Element集合,系统目前支持3种方式的处理:

    • ProcessViewport: 提取指定视口(为空时指代当前视口)中所有Element的几何体;
    • ProcessElement: 提取指定Element的几何体;
    • ProcessXGraphics: 提取指定Element上关联的XGraphics(不存在时可指示自动创建)中的几何体;
  • 提取结果: 即提取处理后所得到的分类存储的几何体,既可以按Element来提取(即提取特定Element所属的特定类型的几何体),也可以全部直接提取。系统目前支持的提取方法如下:

    • GetElementHandles: 获取本次参与处理的Element集合;
    • GetCurves: 获取参与本次处理的特定Element所属的CurveVector;
    • GetAllCurves: 获取本次处理后得到的所有CurveVector;
    • GetSolids: 获取参与本次处理的特定Element所属的ISolidPrimitive;
    • GetAllSolids: 获取本地处理后得到的所有ISolidPrimitive;
    • GetSurfaces: 获取本次参与处理的特定Element所属的B样条曲面(MSBSplineSurface);
    • GetAllSurfaces: 获取本次处理后得到的所有B样条曲面(MSBSplineSurface);
    • GetBodies: 获取参与本次处理的特定Element所属的实体(ISolidKernelEntity);
    • GetAllBodies: 获取本次处理后得到的所有实体(ISolidKernelEntity);
    • GetFacets: 获取参与本次处理的特定Element所属的网格面(PolyfaceHeader);
    • GetAllFacets: 获取本次处理后得到的所有网格面(PolyfaceHeader;

      3.5.3 选中对象关联对象级联高亮

      有些时候,我们可能会需要选中特定的Element,查看与之相关的其他对象,这个时候调用CimUtils.EnableCascadeSelection传入参数true就可以实现该功能,与选中对象相关的对象都会被级联高亮,便于用户查看。此操作仅支持单选对象,不再需要时,可以再次调用该接口传入false关掉该功能。


    • 4 Cim Elements

      4.1前言

      CimPlatform Elements是CimPlatform的一部分,它将DgnPlatform内置的元素(Element)封装成类,以方便开发者创建、删除和操作这些元素。内置元素,除有限几个非图形元素外,都是带图形表现的元素,比如2D的线、弧、多边形、文字,3D的面和实体等。这些元素只具有图形和几何上的含义,并不具有专业上的含义,因此,专业领域的开发者通常不会单独创建它们,而是将它们用作用户自定义对象的部分或全部图形表现。用户自定义对象是专业领域里的对象,对应于专业领域里的核心概念,开发者的主要任务通常是设计和实现用户自定义对象。CimPlatform的核心,CimPlatform Core,是一套支持设计和实现用户自定义对象的机制,CimPlatform Elements主要是为其提供辅助。

      4.2 Cim Elements的内容

      CIM Elements是一个类集合,其中封装了DgnPlatform绝大多数内置元素,其核心是CimElement类层次结构,其中每一个具体的派生类对应一个内置元素。CimElement是类层次结构的顶层基类,代表内置元素,包含图形的和非图形的。CimGraphicElement派生于CimElement,代表带图形表现的元素。具体的CimElement派生类所包含的方法可以分为三类:其一,与EC相关的方法,如获取内部内置元素对应的ECClass的名称;其二,与元素对象创建相关的构造方法和保存元素对象的方法;其三,与元素包含的数据相关的Read/Write方法。CimElement类层次结构包含的类如下所示。通过类名称,可知各个类所代表的内置元素。关于各个类的具体使用,详见类接口。
      OpenCivil高级自定义API - 图3

CimElement MSElement 图形表现 备注
CimLineElement LineElement 线
CimArcElement ArcElement
CimCurveElement N/A 曲线
CimEllipseElement EllipseElement 椭圆
CimShapeElement ShapeElement
CimLineStringElement LineStringElement 线串
CimPointCloudElement PointCloudElement 点云
CimPointStringElement PointStringElement 点串
CimComplexShapeElement ComplexShapeElement 复杂形
CimComplexStringElement ComplexStringElement 复杂串
CimConeElement ConeElement 圆锥体
CimIconElement N/A 图表
CimMeshElement MeshElement 网格曲面
CimMultilineElement MultilineElement 多线
CimSolidElement TorusSolidElement
SphereSolidElement
ExtrusionSolidElement
BoxSolidElement
RotationalSweepSolidElement
RuledSweepSolidElement
ConeElement
拉伸或旋转体
CimSurfaceElement TorusSurfaceElement
SphereSurfaceElement
ExtrusionSurfaceElement
BoxSurfaceElement
RotationalSweepSurfaceElement
RuledSweepSurfaceElement
SurfaceConeElement
拉伸或旋转面
CimBSplineCurveElement BSplineCurveElement B样条曲线
CimBSplineSurfaceElement BSplineSurfaceElement B样条曲面
CimSpiralCurveElement SpiralCurveElement 回旋曲线
CimGroupedHoleElement GroupedHoleElement 分组孔
CimDrawingBoundaryElement DrawingTitleElement 绘图边界
CimRasterFrameElement RasterAttachmentElement 光栅边框
CimRasterHeaderElement RasterHeaderElement 光栅头
CimRasterComponentElement RasterComponentElement 光栅组件
CimTagElement TagElement 标签
CimTagSetElement TagSetElement 标签集合
CimTextElement TextElement 文本
CimTextNodeElement N/A 文本节点
CimTextTableElement TextTableElement 文本表格
CimTitleTextElement TitleTextElement 标题文本
CimParametricCellElement ParametricCellElement 参数化单元
CimParametricCellDefinitionElement ParametricCellDefinitionElement 参数化单元的定义
CimSharedCellElement SharedCellInstanceElement 共享单元
CimSharedCellDefinitionElement SharedCellDefinitionElement 共享单元定义
CimDimensionElement DimensionElement 标尺
CimNormalCellElement NormalCellElement 常规单元
CimAnnotationCellElement NormalCellElement 注记单元
CimLabelCellElement LabelElement 标注单元
CimNoteCellElement NoteElement 便签单元
CimBRepCellElement BRepCellElement
CimOleCellElement OleElement
CimAssocRegionElement AssociativeRegionElement 关联区域
CimDigitalSignatureCellElement DigitalSignatureCellElement 数字签名单元
CimCalloutElement N/A 插图
CimPlanCalloutElement PlanCalloutElement
CimSectionCalloutElement SectionCalloutElement
CimDetailCalloutElement DetailCalloutElement2D
CimDetail3dCalloutElement DetailCalloutElement
CimElevationCalloutElement ElevationCalloutElement
CimInteriorElevationCalloutElement InteriorElevationCalloutElement
CimViewElement NamedViewElement 视图
CimPlanViewElement NamedViewElement
CimSectionViewElement NamedViewElement
CimDetailViewElement NamedViewElement
CimElevationViewElement NamedViewElement

4.3 Cim Elements的应用

Cim Elements有两种常见使用方式:其一,用于直接创建DgnPlatform的内置元素,其二,用作用户自定义对象的部分或全部图形表现。就CimPlatform而言,第一种方式的应用场景和需求性不是太多,第二种方式通常最有价值。下面分别介绍这两种使用方式。

4.3.1单独使用

CimPlatform Elements可单独使用,用于直接创建DgnPlatform的内置元素。此时,这些内置元素只有自带的几何含义,并不直接包含专业含义,与使用Microstation SDK中的API在意义上基本相同,只是更为简单而已,因而在专业软件中意义并不是很大。尽管开发者也可以在使用它创建内置元素后,再利用Item Type机制为其添加专业含义,从而把它们转变成用户自定义对象,但是这种方法的可操作能力有限,而且较为繁琐。以下的C#代码片段,分别使用CimPlatform Elements创建了一个Line元素和一个Ellipse元素。
void CreateLineElement()
{
// 获取当前激活模型
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();

  1. // 定义直线的起终点<br /> DPoint3d start **=** **new** DPoint3d**(**0**,** 0**,** 0**);**<br /> DPoint3d end **=** **new** DPoint3d**(**1**,** 1**,** 0**);**
  2. // 创建直线元素<br /> CimLineElement line **=** \<br />**new** CimLineElement**(**start**,** end**,** model**);**
  3. // 保存元素到模型中<br /> line**.**SaveToModel**();**
  4. // 修改元素的显示属性<br /> line**.**UpdatePropertySetter**();**<br /> line**.**SetTransparency**(**8.2**);**<br /> line**.**Apply**();**<br /> <br /> // 保存元素的修改<br /> line**.**SaveToModel**();**<br /> **}**

void CreateEllipseElement()
{
// 获取当前激活模型
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();

  1. // 定义圆心点<br /> DPoint3d center **=** **new** DPoint3d**(**1000**,** 0**,** 0**);**
  2. // 创建椭圆元素<br /> CimEllipseElement ellipse **=** \<br />**new** CimEllipseElement**(**center**,** 800**,** 800**,** 0**,** model**);**
  3. // 保存元素到模型中<br /> ellipse**.**SaveToModel**();**<br /> **}**

4.3.2 配合CimPlatform Core使用

CimPlatform Elements的另一个重要用途就是将其用作用户自定义对象的部分或全部图形表现。用户自定义对象是专业领域里的对象,是专业软件的核心,由开发者基于CimPlatform Core进行设计和实现。用户自定义对象并不是一定要用CimPlatform Elements作为其图形表现,如果对象的图形表现比较简单,则可以直接利用自己的的数据创建几何对象并将它们绘制出来。但是当图形表达比较复杂时,比如其中包含或者直接就是一种MultiLine、Mesh或Cell时,开发者就可以将CimPlatform Elements用作用户自定义对象的图形表现,这样就能在很大程度上降低绘制特定图形的难度并提升效率。将CimPlatform Elements用作用户自定义对象的部分或全部图形表现时,可分为以下几种情形。下面以CimPlatform Elements的C++版本为基础分别介绍常见的几种使用方式。
class MyDomainObject : public CimGraphicObject
{
protected:
DPoint3d GetStartPointFromECInstance()
{
// 利用存储在相应ECInstance中的数据,计算起点坐标
}

  1. DPoint3d GetEndPointFromECInstance**()**<br /> **{**<br /> // 利用存储在相应ECInstance中的数据,计算终点坐标<br /> **}**
  2. virtual BentleyStatus _GeneratePresentation**(**ViewContextR viewContext**)** override<br /> **{**<br /> DgnModelRefR modelRef **=** *****mdlModelRef_getActive**();**
  3. // 利用存储在ECInstance中的数据,构造创建元素所需要的参数。 <br /> DPoint3d start **=** GetStartPointFromECInstance**();**<br /> DPoint3d end **=** GetEndPointFromECInstance**();**
  4. // 创建元素,但不保存。<br /> CimLineElementPtr elementPtr **=** \ <br />CimLineElement**::**Create**(**start**,** end**,** modelRef**.**Is3d**(),** modelRef**);**
  5. // 获取元素的几何数据。注意,并非所有元素都能这样。<br /> <br /> // 绘制几何图形<br /> <br /> viewContext.VisitElemHandle(elementPtr->GetElementHandle(), true, true);<br /> // 绘制其它图形表示,如果有的话。<br /> // ...<br /> **}**
  6. // ...<br /> **}**

4.4 结语

CimPlatform Elements是CimPlatform的一部分,其中封装了DgnPlatform绝大多数内置元素,以方便开发者创建、删除和操作这些元素。它的主要用途是用作开发者设计和实现的用户自定义对象的部分或全部图形表现。目前,它已具备了DgnPlatform直接支持的元素操作功能,以后根据开发需要,我们还将为它提供更多的功能,以方便开发者使用内置元素。

5 CIMMesh SDK

5.1 CIMMesh SDK架构及其在CIMPlatform中的角色

CIMMesh SDK模块为CIMPlatform中关于几何运算相关的SharedGeometry的一部分,里面主要包括网格(Mesh)和曲面(Surface)的各种生成方法,编辑修改,实用工具等功能丰富强大的API接口供用户使用,CIMMesh的Native版本模块名字为Bentley.CIM.CIMMesh.dll, Managed版本模块名字为Bentley.CIMNET.CIMMesh.dll。
OpenCivil高级自定义API - 图4
CIMMeshSDK中实现的Native API接口都定义在CIMPlatformSDK\include\CIM\CIMMesh MeshToolSDK.h文件中,CLR版本的Managed API接口都定义在MeshSDKNET.h
主要有以下几类接口函数

  • 各种Mesh创建APIs(通过点集合, 索引表, Mstn元素, 等高线,两条线等);
  • 各种Mesh编辑修改APIs(布尔运算,增加/删除顶点,缝合, 分割,抽稀等);
  • 各种Mesh实用工具APIs(提取边界,展开,细分,三角化,闭合空洞,清理, 求交线,转化为曲面等);
  • 各种Surface的创建APIs(通过点集合, 角,边集合, 曲线网, Extrude,Loft,Sweep等方式);
  • 各种基本Surface的创建APIs(Slab, Cylinder,Cone, Dome, Torus 等);
  • 各种Surface编辑修改APIs(延伸,偏移,两Surface间的Fillet/Trim/Blend/Stitch/Combine等 );
  • 各种Surface的实用工具APIs(Surface间求交线,Mesh和Surface求交线,抽取点集合/等高线, 展开曲面等);

    5.2 CIMMeshSDK中Mesh相关主要API

Mesh API函数 功能描述
CreateMeshFromPoints 通过点集合创建Mesh的PolyfacHeader对象, 点集合中的点至少为三个不同的DPoint3d对象
CreateMeshFromVertexAndIndices 通过点集合和点索引表创建Mesh的PolyfacHeader对象, 点集合中的点至少为三个不同的DPoint3d对象,索引表必须是完整有效的
CreateMeshFromElement 通过输入的Mstn的Element创建一个Mesh 的PolyfacHeader对象
SetTwoCurvesHandle 把两条CurveVector对应的ElementHandle设置给SDK
CreateMeshFromTwoCurves 通过输入两条CurveVector值创建一个MeshPolyfaceHeader对象
ExtractMeshBoundaryAsPoints 输入Mesh对象的ElementHandle,获取它的边界点坐标列表
ExtractMeshBoundaryAsElement 输入Mesh对象的ElementHandle,获取它的边界线串(LineString)对象的ElementHanld值
AddOrRemoveVertexFromMesh 输入Mesh的某个顶点坐标值,从Mesh对象中删除或者增加这顶点
DeleteVertexFromMesh 输入Mesh的某个顶点索引号,从Mesh对象中删除这个顶点
CreateMeshFromExtrudeVolumeAlongGlobalZ 通把一个已经存在的Mesh元素通过沿着Z轴拉伸生成一个新Mesh的PolyfaceHeader对象
CreateMeshFromExtrudeVolumeAlongVector 通把一个已经存在的Mesh元素通过沿着指定的向量拉伸生成一个新Volume Mesh的PolyfaceHeader对象
CreateMeshFromExtrudeToTarget 通过把一个已经存在的Mesh元素拉伸到另外一个目标Mesh生成新Volume Mesh的PolyfaceHeader对象
CreateMeshFromAddThickness 通过把一个已经存在的Mesh元素通过沿着指定方向拉伸Offset生成一个新Volume Mesh的PolyfaceHeader对象
DoMeshBooleanOp 两组Mesh对象之间进行布尔运算,生成一个新的Mesh的PolyfaceHeader对象
DoMeshSubdivide 对输入的Mesh对象做细分化操作生成新的Mesh的PolyfaceHeader对象
DoMeshSplit 对输入的Mesh对象按照参数设置做分割化操作得到子Mesh各部分的PolyfaceHeader对象集合
DoMeshIntersectMesh 求两个Mesh对象的交线,得到相应的CurveVector对象
DoMeshStitchBoundary 对两个Mesh对象按照指定的 边界曲线进行缝合操作得到新的Mesh的PolyfaceHeader对象
DoMeshStitchSimple 对一组Mesh对象做简单的缝合操作得到新的Mesh的PolyfaceHeader对象
DoMeshHealHoles 对一个中间有空洞的Mesh对象做空洞闭合操作得到一个新的Mesh的PolyfaceHeader对象
DoMeshReverseNormal 对一个Mesh对象按照设定的参数规则做面法线反向操作得到一个新的Mesh的PolyfaceHeader对象
DoMeshUnfold 对一个Mesh对象从给定的一个点做展开操作得到一个新的Mesh的PolyfaceHeader对象
DoMeshRetriangulate 对一个Mesh对象按照设定的参数规则做三角化操作得到一个新的Mesh的PolyfaceHeader对象
DoMeshClipCurve 使用一个Curve对象对一个Mesh对象按照设定的参数规则做Clip操作得到一个新的Mesh的PolyfaceHeader对象
DoMeshProjectCurve 使用一个Curve对象对一个Mesh对象按照设定的参数规则做Project操作得到一个新的Mesh的PolyfaceHeader对象
DoMeshImprintCurve 使用一个Curve对象对一个Mesh对象按照设定的参数规则做Imprint操作得到一个新的Mesh的PolyfaceHeader对象
DoDecimateMesh 对一个Mesh对象按照设定的参数规则做抽稀化操作得到一个新的Mesh的PolyfaceHeader对象
DoCleanupMesh 对一个Mesh对象按照设定的参数规则做清理化操作得到一个新的Mesh的PolyfaceHeader对象
DoMeshConvertToSurface 把一个Mesh对象转化为MSBsplineSurface对象

5.3 CIMMeshSDK中Surface相关主要API

Surface API函数 功能描述
CreatePlanarSurfFromPoints 通过点集合创建一个平面MSBsplineSurface
CreatePlanarSurfFromCorners 通过四个点作为Corner创建一个MSBsplineSurface
CreateSurfaceFromPoints 通过点集合创建按照给定参数和曲面类型创建一个MSBsplineSurface
CreateSurfaceFromEdges 通过多个边曲线创建一个MSBsplineSurface
CreateSurfaceFromNetwork 通过U和V方向的多个曲线网络创建一个MSBsplineSurface
CreateSlabSurface 通过指定长宽高和XYZ轴的方向创建一个方块类型的MSBsplineSurface
CreateConeSurface 通过指定底部和顶部圆半径和高度以及中轴的方向创建一个圆锥类型的MSBsplineSurface
CreateCylinderSurface 通过指定圆半径和高度以及中轴的方向创建一个圆柱类型的MSBsplineSurface
CreateDomeSurface 通过指定圆心和圆半径和残缺高度创建一个球冠类型的MSBsplineSurface
CreateTorusSurface 通过指定圆心,主圆半径,次圆半径和扫描方向及角度创建圆环类型的MSBsplineSurface
CreateSurfaceByExtrudeAlongVector 通过把指定的曲线沿着指定的向量方向按照特定参数拉伸创建MSBsplineSurface
CreateSurfaceByExtrudeAlongPath 通过把指定的曲线沿着指定的路径曲线按照特定参数使用拉伸方式创建MSBsplineSurface
CreateSurfaceBySweepAlongPath 通过把多个截面曲线沿着多个路径曲线按照特定参数使用扫掠方式创建MSBsplineSurface
CreateSurfaceByLoftSectionsDirect 通过把多个截面曲线按照特定参数使用直接放样方式创建MSBsplineSurface
CreateSurfaceByLoftSections 通过把多个截面曲线沿着多个导线按照特定参数使用放样方式创建MSBsplineSurface
CreateSurfaceByRevolution 通过把一个曲线沿着旋转轴旋转指定角度使用旋塑方式创建MSBsplineSurface
DoTrimSurfaceWithSurface 使用一个曲面按照指定的参数修剪另外一个曲面的方式生成新的MSBsplineSurface曲面
DoTrimSurfaceWithCurve 使用一个曲线按照指定的参数修剪另外一个曲面的方式生成新的MSBsplineSurface曲面
DoUnrollSurface 把MSBsplineSurface曲面按照指定的参数展开为一个新的CurveVector
DoExtractPointFromSurface 获取MSBsplineSurface曲面上特定位置处点的信息(点坐标,法向量,uv切向量)
DoExtractPointsFromSurface 获取MSBsplineSurface曲面符合特定参数的一组点的信息(点坐标,法向量,uv切向量)
DoExtractOneIsolineFromSurface 获取MSBsplineSurface曲面上特定方向上的符合特定参数的一组等值线
DoExtractMultiIsolineFromSurface 获取MSBsplineSurface曲面上uv方向上的符合特定参数的多组等值线
DoExtractMultiIsolineFromSmartSurface 获取SmartSurface曲面上uv方向上的符合特定参数的多组等值线
DoSurfaceIntersectSurface 求两个MSBsplineSurface对象的交线,得到相应的CurveVector对象
DoSurfaceFilletBySurface 在两个MSBsplineSurface曲面之间使用倒角过渡的方式按照特定参数生成新的曲面
DoSurfaceFilletAlongCurves 在两个MSBsplineSurface曲面之间沿着两条曲线的轨迹使用倒角过渡的方式按照特定参数生成新的曲面
DoSurfaceOffsetByDistance 把MSBsplineSurface曲面向前或者向后或者双向偏移特定距离生成一个新的曲面
DoSurfaceExtendByDistance 沿着MSBsplineSurface曲面的一个边片段或者它的外边界延伸特定距离生成一个新的曲面
DoSurfaceBlendSurface 按照特定的连续性顺序融合两个MSBsplineSurface曲面生成新的曲面(新曲面包含两原始曲面被修剪部分和链接过渡部分)
DoSurfaceStitchSurface 缝合两个MSBsplineSurface 生成一个新的曲面
DoSurfaceCombineSurface 合并两个MSBsplineSurface(共享一个公共边)生成一个新的曲面

5.4 CIMMeshSDK的使用方法

5.4.1 CIMMeshSDK的Native API调用方法:

调用CIMMeshSDK时,必须先安装CIMPlatfromSDK开发环境,链接CIMPlatfromSDK的lib目录下的Bentley.CIM.CIMMesh.lib,包含include目录下面的#CIM\CIMMesh\MeshSDKAPI.h

OpenCivil高级自定义API - 图5

CIMMeshSDK的接口函数定于在Bentley::CIM::CIMGEOMTRY::MESH名字空间下的CIMMeshToolSDK类空间下,所以在使用时,必须在相应的.cpp文件中包换#include 的头文件,并且引用以下的名字空间:
USING_NAMESPACE_CIM
USING_NAMESPACE_CIM_CIMGEOMTRY_MESH
调用方式如下:
BentleyStatus status = MeshToolSDK::DoMeshBooleanOp(meshOutPolyface, meshes1, meshes2, GetBoolMode(), ACTIVEMODEL);
BeAssert(status == 0);

在项目的库链接选项中加入Bentley.CIM.CIMMesh.lib库,比如在mke文件中给LINKER_LIBRARIES加上BifGeometry.lib.
LINKER_LIBRARIES = $(mdlLibs)bentley.lib \
$(mdlLibs)BentleyAllocator.lib \
$(mdlLibs)mdlbltin.lib \
$(mdlLibs)RmgrTools.lib \
$(mdlLibs)BentleyGeom.lib \
$(mdlLibs)DgnPlatform.lib \
$(mdlLibs)dgnview.lib \
$(baseDir) Bentley.CIM.CIMMesh.lib

5.4.2 CIMMeshSDK的Managed API调用方法:

在要调用的C#工程文件中添加对Bentley.GeometryNET.dll, Bentley.DgnPlatformNET.dll和Bentley.CIMNET.CIMMesh.dll程序集的引用,在相应的.cs文件代码中使用如下的名字空间;
using Bentley.GeometryNET;
using Bentley.CIMNET.CIMGeometry.Mesh;
using Bentley.DgnPlatformNET;
using BGNET = Bentley.GeometryNET;
using BDGPNET = Bentley.DgnPlatformNET;
using BCIMNETGM = Bentley.CIMNET.CIMGeometry.Mesh;
下面的代码片段是对CIMMeshSDKNET调用方法:
public void CrateMeshFromPoints()
{
BGNET.PolyfaceHeader polyface = new BGNET.PolyfaceHeader();
List ptsList = new List();
ptsList.Add(new BGNET.DPoint3d(1782922.4933853, 578470.6966740, 1975.5565785));
ptsList.Add(new BGNET.DPoint3d(1782633.3827469, 578727.1520505, 1875.5565785));
ptsList.Add(new BGNET.DPoint3d(1782588.6406736, 578637.7196913, 1975.5565785));
ptsList.Add(new BGNET.DPoint3d(1782967.2354585, 578560.1290333, 1875.5565785));
ptsList.Add(new BGNET.DPoint3d(1782799.6665090, 578583.9343355, 1929.2407573));
ptsList.Add(new BGNET.DPoint3d(1782839.6788288, 578566.1063000, 1927.2824486));
bool retVal = BCIMNETGM.MeshSDKNET.CreateMeshFromPoints(ref polyface, ptsList, this.m_dgnModel);
Assert.AreEqual(retVal, true);
}

6 CIMSolid SDK

6.1 简介

CIMSolid模块主要提供了实体(Solid)建模功能,其功能由两大部分接口组成。第一部分为通用的实体建模接口(SolidUtil),主要用于操作实体的生成、修改以及属性获取等;第二部分为参数化模板建模接口,它能与参数化模板(CIMTemplate)相结合,通过设置参数及其变化方式,实现沿路径方向变断面实体的生成。
命名空间:
C++ :Bentley::CIM::CIMGeometry::Solid
C++ CLI/C#: Bentley.CIMNET.CIMGeometry.Solid

6.2 通用实体建模接口

通用实体建模接口,通过一组定义在SolidUtil中的静态方法,提供了包括基本几何实体创建、通用几何实体创建、几何实体编辑以及几何实体属性获取等功能。需要注意的是,这些静态方法所使用的输入/输出参数均以MircoStation底层所使用的UOR为单位,以方便直接从MircoStation对象上提取几何对象来生成实体。
这些函数为:
C++ :Bentley::CIM::CIMGeometry::Solid::SolidUtil::XXXMethod(…)
C++ CLI/C#: Bentley.CIMNET.CIMGeometry.Solid.SolidUti. XXXMethod(…)
例如:

函数名 功能介绍
CopyEntity 复制实体
CalcTransformAlongPath 辅助方法,提供一个局部坐标到世界坐标的坐标转换矩阵
TransformCurveVectorFromMasterToUOR 辅助方法,返回用主单位表示的CurveVector的一个UOR单位拷贝

6.2.1 简单实体快速生成接口(RapidSolidCreate)

该部分接口主要用于支持,通过几何参数快速生成简单的基本几何实体,如长方体、正/斜圆柱圆锥圆台、球体、圆环、正多面体、楔形体等。
这些函数为:
C++ :Bentley::CIM::CIMGeometry::Solid::SolidUtil::RapidSolidCreate::XXXMethod(…)
C++ CLI/C#: Bentley.CIMNET.CIMGeometry.Solid.SolidUti.RapidSolidCreate. XXXMethod(…)
例如:

函数名 功能介绍
MakeSlabBody 根据长宽高创建一个中心点在坐标原点的长方体
MakeCylinderBody 根据底面半径和高度创建一个中心点在坐标原点的圆柱
MakeSkewedCylinderBody 根据底面半径和高度创建一个中心点在坐标原点的斜圆柱
MakeSphereBody 根据半径创建一个中心点在坐标原点的球体或部分球体
MakeEllipsoidBody 根据椭球体的三个轴半径创建一个中心点在坐标原点的椭球体或部分椭球体
MakeConeBody 根据底面半径、顶面半径和高创建一个中心点在坐标原点的圆锥或圆台
MakeSkewedConeBody 根据底面半径、顶面半径、高和倾斜角创建一个中心点在坐标原点的斜圆锥或圆台
MakeEllipticalConeBody 根据底面长短轴半径、顶面长短轴半径和高创建一个中心点在坐标原点的椭圆锥或椭圆台
MakeSkewedEllipticalConeBody 根据底面长短轴半径、顶面长短轴半径和高创建一个中心点在坐标原点的斜椭圆锥或斜椭圆台
MakePrismBody 根据底面外接圆半径、底面边数量和高创建一个中心点在坐标原点的底面为正多边形的棱柱
MakeSkewedPrismBody 根据底面外接圆半径、底面边数量和高创建一个中心点在坐标原点的底面为正多边形的棱柱
MakeTorusBody 根据圆环半径、截面半径和扫掠角度创建一个中心点在坐标原点的圆环或部分圆环
MakePyramidBody 根据底面外接圆半径、底面边数量和高创建一个中心点在坐标原点的底面为正多边形的棱锥
MakeSkewedPyramidBody 根据底面外接圆半径、底面边数量和高创建一个中心点在坐标原点的底面为正多边形的斜棱锥
MakeTetrahedronBody 根据四个顶点的坐标生成一个四面体
MakeEllipsePlantBody 根据长短轴半径创建一个中心点在坐标原点的椭圆盘
MakeRegularPolyhedronBody 根据外接圆半径创建一个中心点在坐标原点的正多面体

6.2.2 实体创建接口(Create)

此接口提供了使用其他类型的几何对象生成实体的功能。主要包括使用曲线(组)生成实体面,使用拉伸、扫掠、放样等方式生成实体,使用曲面加厚方式生成实体以及使用断面旋塑方式生成实体等功能。
这些函数为:
C++: Bentley::CIM::CIMGeometry::Solid::SolidUtil::Create::XXXMethod(…)
C++ CLI/C#: Bentley.CIMNET.CIMGeometry.Solid.SolidUti.Create. XXXMethod(…)
例如:

函数名 功能介绍
BodyFromCurveVector 根据参数CurveVector,生成实体面
BodyFromExtrusion 包含两个重载函数:
1.根据拉伸横截面,拉伸距离,拉伸方向来拉伸生成实体
2.根据拉伸横截面生成实体. 横截面在实际拉伸起始位置, 将横截面延拉伸方向拉伸,直至末端边界实体
BodyFromExtrusionWithDefaultTransform 在BodyFromExtrusion函数的基础上,内置一个默认的坐标变化过程,传入的横截面轮廓在局部坐标系(0,0)点附近,根据传入参数startLocationIn, directionIn, isVertical等来进行坐标转换,将局部坐标系下的横截面转换到世界坐标系下相应位置进行拉伸
BodyFromSweep 包含两个重载函数:
1.根据横截面延路径扫掠生成实体, 横截面在实际扫掠起始位置。横截面必须绘制在实际的拉伸起始位置上。
2.根据起始点横截面延路径扫掠生成实体, 横截面在实际扫掠起始位置。 传入扫掠起点横截面,终点横截面,沿路径扫掠。 扫掠路径必须保证G2连续性, 起始横断面和终止横断面之间点的对应关系采用的是最近点对应原则
BodyFromSweepWithDefaultTransform 在BodyFromSweep函数的基础上,内置一个默认的坐标变化过程,传入的横截面轮廓在局部坐标系(0,0)点附近,通过扫掠路径将局部坐标系下的扫掠横截面转换到扫掠路径的起点位置,将局部坐标系下的横截面转换到世界坐标系下相应位置进行扫掠
BodyFromLoft 放样生成实体
BodyFromThickenSheet 加厚实体面,生成实体
BodyFromThickenSheetFromClosedCurveVector 加厚闭合的曲线围成的面,生成实体
BodyFromThickenSheetFromMeshSurface 加厚一个mesh面,生成实体
BodyFromThickenSheetFromBsplineSurface 加厚一个B样条曲面,生成实体
BodyFromSpinBodyFromClosedCurveVector 旋转一个闭合的曲线围成的面,生成旋转体
BodyFromSpinBodyFromMeshSurface 旋转一个mesh面,生成旋转体
BodyFromSpinBodyFromBsplineSurface 旋转一个B样条曲面,生成旋转体

6.2.3 实体编辑接口(Modify)

此接口提供实体的修改编辑功能,主要包括实体的坐标转换、实体间的布尔运算、裁剪、开孔、压印、抽壳等。
这些函数为:
C++: Bentley::CIM::CIMGeometry::Solid::SolidUtil::Modify::XXXMethod(…)
C++ CLI/C#: Bentley.CIMNET.CIMGeometry.Solid.SolidUti.Modify. XXXMethod(…)
例如:

函数名 功能介绍
TransformBody 对实体进行坐标转换,实现平移、旋转、缩放等
CutSolidsWithCurves 用CurveVector对实体进行切割,通过参数控制切割的方向、深度以及切割完成后保留的部分
ConvertSolidToMesh 将实体转换成mesh对象
BodyBooleanIntersect 对实体进行布尔运算,求取目标实体于其他实体相交的部分
BodyBooleanSubtract 对实体进行布尔运算,求取目标实体于其他实体相减的部分
BodyBooleanUnion 对实体进行布尔运算,求取目标实体于其他实体相并的部分
BodyBooleanTrim 对实体进行修剪运算,用tool实体来对目标实体进行截断式修剪
PutHoleOnSolid 对实体进行开孔操作
ImprintCurveVectorOnBody 压印,将一个曲线投影的方式压印到实体上
HollowFaces 对实体进行抽壳运算,提取实体的外壳

6.2.4 实体属性获取接口(Properties)

此接口提供了实体对象属性的提取功能,主要包括提取实体的几何属性(如:体积、表面积、重心坐标、三轴惯性矩、惯性积等),提取实体的包围盒,实体的射线探测功能以及提取实体间的交线等。
这些函数为:
C++: Bentley::CIM::CIMGeometry::Solid::SolidUtil::Properties::XXXMethod(…)
C++ CLI/C#: Bentley.CIMNET.CIMGeometry.Solid.SolidUti.Properties. XXXMethod(…)
例如:

函数名 功能介绍
BodyMassProperties 计算实体的几何属性,体积、表面积(对于实体面则计算面积、周长)、重心坐标、三轴惯性矩、惯性积等,所有计算结果均以UOR单位表示
GetBoundingBox 计算实体的轴对称包围盒
GetOrientedBoundingBox 计算实体的有向包围盒
GetBodyIntersectPointWithRay 实体的射线探测功能,计算实体与射线的交点
GetBodyIntersectCurveWithBodies 计算实体与实体的交线

6.3 参数化模板创建实体接口(SolidCorridor)

此接口的主要功能为以可变的参数化模板为断面来生成实体。它主要以CIMTemplate模块定义的参数化模板作为横断面,以SolidCorridor为路径,实现沿路径方向可变断面实体的创建。其中主要包含的接口类有:

  • SolidCorridor : 用于定义实体的建模规则。通过使用AddSolidCorridorInterals方法来定义创建实体时的各个分段。支持增加多个SolidCorridorInterals,并且每个分段都可以拥有不同的约束方式,从而实现分段变截面实体的生成。
  • 纵向约束接口:
  • ParametricConstraints: 全部参数纵向约束的集合,用于定义分段内的纵向参数约束。在使用单模板加约束的方式生成实体时,每段的SolidCorridorInteral中都可以设置相应的此类约束,通过其内部包含的一个或数个ConstraintIntervals,实现对分段内全部参数的纵向约束的设置。
  • ConstraintIntervals:单个参数纵向约束集合,用于定义分段内的单个参数在分段里程桩号内的全部变化规则。通过其内部包含的一个或数个ConstraintInterval,在不同里程位置上分别设置,实现对每段SolidCorridorInteral内不同里程桩号处单个参数的纵向变化。
  • ConstraintInterval: 单个参数纵向约束的最小粒度。通过起点里程(startDistance),起点参数值(startValue),终点里程(endDistance),终点参数值(endValue)及纵向过渡方式(transitionStrategy)等参数,实现横断面模板中定义的某个参数变量在路径方向的纵向变化。
  • ITransitionStrategy: 用于在各分段中定义特定参数沿纵向的过渡方式,目前支持的过渡方式有线性过渡、圆曲线方式过渡以及抛物线方式过渡。
  • PointControl: 控制横断面模板中的某个点,在实体生成的过程中,沿着特定的控制曲线变化。
  • ISolidCreator : 获取生成的结果。

命名空间:
C++ :Bentley::CIM::CIMGeometry::Solid
C++ CLI/C#: Bentley.CIMNET.CIMGeometry.Solid

6.3.1 基于单参数化模板断面的实体创建

其功能为以单参数化模板为断面并结合其沿路径的约束变化,以实现变截面实体的生成。其工作方式为通过在横断面模板中设置相应的约束(详见CIMTemplate说明文档) ,再配合各个参数在实体生成路径上各位置处的参数值及其中间中间过渡方式来生成变截面实体。
在SolidCorridor中的应用接口:
public void AddSolidCorridorInterals(string name, Template solidTemplate, ParametricConstraints parametricConstraints, DgnModelRef modelRef, LinearElement startLimitLine, LinearElement endLimitLine, double startOffset, double endOffset);

6.3.2 基于双参数化模板断面的实体创建

其功能为通过在路径两端不同的里程位置上提供两个相似(点数、点续及其名称一致)的参数化模板创建实体。在构造实体的过程中,自动将两个模板中同名的点按照线型过渡的方式进行过渡,实现变截面实体的构造。起点模板横断面(startTemplate)放置在参数startTemplateLocation给定的位置,终点模板横断面(endTemplate)放置在参数endTemplateLocation给定的位置,再按照线型过渡的方式沿路径生成实体。
在SolidCorridor中的应用接口:
public void AddSolidCorridorInterals(string name, Template startTemplate, Template endTemplate, double startTemplateLocation, double endTemplateLocation, DgnModelRef modelRef, LinearElement startLimitLine, LinearElement endLimitLine, double startOffset, double endOffset);

6.3.3 横坡的设置

SolidCorridor对象支持创建带横坡的实体。横坡的设置需要重载ICrossSlopeCallbacks接口类实现CalcSlopeAlongPath方法,该方法要求实现路径上不同位置处横坡的坡度值计算,即对于传入参数distance对应的里程位置,返回该处横坡的坡度值。
在实体创建过程中,程序将根据当前位置值自动回调该方法,以实现在不同位置处根据坡度值对横断面进行整体倾斜,从而实现横坡的设置。
横坡设置接口类:
public interface ICrossSlopeCallbacks
{
double CalcSlopeAlongPath(double distance);
}
横坡在SolidCorridor中的应用接口:
public void SetCrossSlope(ICrossSlopeCallbacks CrossSlopeCallbacks);

6.3.4 点控制

点控制通过PointControl类实现。PointControl类可以通过3维的CurveVector来构造也可以通过平曲线加纵断线的方式构造。通过名称与参数化模板中同名的点进行绑定,实现在构造实体的过程中,横断面同名点延pointControl中定义的曲线变化而变化。
PointControl的构造方方法:

构造函数 功能介绍
PointControl(string name, PointControlDirection direction, CurveVector curveVector, DgnModelRef modelRef) 通过curveVector构造点控制
PointControl(string name, PointControlDirection direction, LinearElement controlPathInPlan, ProfileElement ControlPathProfile) 通过平曲线(LinearElement)和纵断线(ProfileElement)构造点控制

PointControl在SolidCorridor中的应用接口:
public bool AddPointControls(string corridorInteralName, IList pointControls);

6.3.5 生成结果

ISolidCreator用于获取生成的solid对象及CurveVector对象。

函数名 功能介绍
CreateSolid 创建实体、实体边线以及横断面曲线,在获取结果之前必须先调用该方法
GetCrossSectionCurveVectorAtDistance 获取在给定里程位置处的横断面曲线
GetCrossSectionPointsAtDistance 获取在给定里程位置处的横断面各个点的坐标
GetCurveVectorByName 根据横断面上点的名称,获取实体的边线
GetCurveVectors 获取实体的所有边线
GetSolidBodies 获取生成的实体
GetSolidBodyByName 通过横断面模板中组件的名称获取其生成的实体

6.3.6 SolidCorridor实例代码

6.3.6.1 实例1:根据参数化模板及约束生成solid实体



using BCIMNETGS = Bentley.CIMNET.CIMGeometry.Solid;
using BCIMNETT = Bentley.CIMNET.CIMTemplate;
using BCIFLG = Bentley.CifNET.LinearGeometry;


class CrossSlopeCallbacks : BCIMNETGS::ICrossSlopeCallbacks
{
/————————————————————————————————————————-//
重载CalcSlopeAlongPath, 提供在任意distance位置上横坡的坡度值。
+———————-+———————-+———————-+———————-+———————-+———/
public double CalcSlopeAlongPath(double distance)
{
return 0.2;
}
};

/
————————————————————————————————————————-//
设置模板参数的纵向约束。
constraintName: 参数化模板中定义的约束变量名称
+———————-+———————-+———————-+———————-+———————-+———/
private BCIMNETGS.ParametricConstraints GetParametricConstraints(string constraintName1, string constraintName2)
{
double startDis = 0.0;
double endDis1 = 100;
double endDis2 = 200;

// 设置约束变量constraintName1的纵向变化规则
double startValue1 = 2.7;
double endValue1 = 5;

// 构造 ConstraintIntervals
BCIMNETGS.ConstraintIntervals constraintIntervals1 = new BCIMNETGS.ConstraintIntervals(constraintName1);

// 生成变化策略,并将分段纵向参数约束添加约束constraintIntervals对象中
BCIMNETGS.ITransitionStrategy transitionStrategy1 = BCIMNETGS.TransitionStrategyFactory.CreateTransitionStrategy(BCIMNETGS.TransitionStrategyTpye.CircularTransitionStrategy, BCIMNETGS.TransitionDirection.Forward);
constraintIntervals1.AddConstraintInterval(startDis, startValue1, endDis1, endValue1, transitionStrategy1);

// 添加另一分段约束策略
BCIMNETGS.ITransitionStrategy transitionStrategy2 = BCIMNETGS.TransitionStrategyFactory.CreateTransitionStrategy(BCIMNETGS.TransitionStrategyTpye.CircularTransitionStrategy, BCIMNETGS.TransitionDirection.Reverse);
constraintIntervals1.AddConstraintInterval(endDis1, endValue1, endDis2, startValue1, transitionStrategy2);


// 设置约束变量constraintName2的纵向变化规则
double startValue2 = 2.7;
double endValue2 = 5;

// 构造 ConstraintIntervals
BCIMNETGS.ConstraintIntervals constraintIntervals2 = new BCIMNETGS.ConstraintIntervals(constraintName2);

// 生成变化策略,并将分段纵向参数约束添加约束constraintIntervals对象中
BCIMNETGS.ITransitionStrategy transitionStrategy3 = BCIMNETGS.TransitionStrategyFactory.CreateTransitionStrategy(BCIMNETGS.TransitionStrategyTpye.CircularTransitionStrategy, BCIMNETGS.TransitionDirection.Forward);
constraintIntervals2.AddConstraintInterval(startDis, startValue1, endDis2, endValue1, transitionStrategy3);


// 构造 ParametricConstraints, Solidcorridor内的参数化纵向约束
BCIMNETGS.ParametricConstraints parametricConstraints = new BCIMNETGS.ParametricConstraints();
parametricConstraints.AddConstraintIntervals(constraintIntervals1);
parametricConstraints.AddConstraintIntervals(constraintIntervals2);
return parametricConstraints;
}

/
————————————————————————————————————————-//
使用单元模板加参数化变量约束的方法生成solid。
solidTemplate: 参数化模板
planLinearElement: 实体生成路径的平曲线
profileElement: 实体生成路径的纵曲线
startLimitLine: 实体的起点限制曲线
endLimitLine: 实体的终点限制曲线
+———————-+———————-+———————-+———————-+———————-+———/
void Example_CreateSolidWithSolidTemplate(BCIMNETT.Template solidTemplate, BCIFLG.LinearElement planLinearElement, BCIFLG.ProfileElement profileElement, BCIFLG.LinearElement startLimitLine, BCIFLG.LinearElement endLimitLine, DgnModelRef modelRef)
{
// 构造 SolidCorridor, 可根据参数选择生成的实体横断面垂直于地面或垂直于路径。
BCIMNETGS.SolidCorridor corridor = new BCIMNETGS.SolidCorridor(planLinearElement, profileElement, BCIMNETGS.SectionVerticalRotation.Vertical);

// 定义横坡
CrossSlopeCallbacks crossSlopCallback = new CrossSlopeCallbacks();
corridor.SetCrossSlope(crossSlopCallback);

// 定义参数化约束
BCIMNETGS.ParametricConstraints parametricConstraints = GetParametricConstraints(“Box_Depth”);

// 添加SolidCorridorInternal, 确定solid实体段的生成规则
corridor.AddSolidCorridorInterals(“Segement_1”, solidTemplate, parametricConstraints, modelRef, startLimitLine, endLimitLine, 0.0, 0.0);

// 生成实体并获取结果
BCIMNETGS.ISolidCreator solidFromCorridor = new BCIMNETGS.ISolidCreator(corridor, modelRef);
solidFromCorridor.CreateSolid();
List solidBodies = new List();
solidFromCorridor.GetSolidBodies(solidBodies);

foreach (SolidKernelEntity solid in solidBodies)
{
if (solid != null)
{
Bentley.DgnPlatformNET.Elements.Element ehOut;
Bentley.DgnPlatformNET.Convert1.BodyToElement(out ehOut, solid, null, modelRef);
ehOut.AddToModel();
}
}
*}

6.3.6.2实例2:根据两个参数化模板生成solid实体




using BCIMNETGS = Bentley.CIMNET.CIMGeometry.Solid;
using BCIMNETT = Bentley.CIMNET.CIMTemplate;
using BCIFLG = Bentley.CifNET.LinearGeometry;


/————————————————————————————————————————-//
使用单元模板加参数化变量约束的方法生成solid。
solidTemplate1: 参数化模板1
solidTemplate2: 参数化模板2
startTemplateLocation: 参数化模板1放置的位置
endTemplateLocation: 参数化模板2放置的位置
planLinearElement: 实体生成路径的平曲线
profileElement: 实体生成路径的纵曲线
startLimitLine: 实体的起点限制曲线
endLimitLine: 实体的终点限制曲线
+———————-+———————-+———————-+———————-+———————-+———/
void Example_CreateSolidWithSolidTemplate2(BCIMNETT.Template solidTemplate1, BCIMNETT.Template solidTemplate2, double startTemplateLocation, double endTemplateLocation, BCIFLG.LinearElement planLinearElement, BCIFLG.ProfileElement profileElement, BCIFLG.LinearElement startLimitLine, BCIFLG.LinearElement endLimitLine)
{
// 构造 SolidCorridor, 可根据参数选择生成的实体横断面垂直于地面或垂直于路径。
BCIMNETGS.SolidCorridor corridor = new BCIMNETGS.SolidCorridor(planLinearElement, profileElement, BCIMNETGS.SectionVerticalRotation.Vertical);

// 添加SolidCorridorInternal, 确定solid实体段的生成规则
corridor.AddSolidCorridorInterals(“Segement_2”, solidTemplate1, solidTemplate2, startTemplateLocation, endTemplateLocation, modelRef, startLimitLine, endLimitLine, 0.0, 0.0);

// 生成实体并获取结果
BCIMNETGS.ISolidCreator solidFromCorridor = new BCIMNETGS.ISolidCreator(corridor, modelRef);
solidFromCorridor.CreateSolid();
List solidBodies = new List();
solidFromCorridor.GetSolidBodies(solidBodies);

foreach (SolidKernelEntity solid in solidBodies)
{
if (solid != null)
{
Bentley.DgnPlatformNET.Elements.Element ehOut;
Bentley.DgnPlatformNET.Convert1.BodyToElement(out ehOut, solid, null, modelRef);
ehOut.AddToModel();
}
}
*}
6.3.6.3实例3:根据模板及点控制生成solid实体

using BCIMNETGS = Bentley.CIMNET.CIMGeometry.Solid;
using BCIMNETT = Bentley.CIMNET.CIMTemplate;
using BCIFLG = Bentley.CifNET.LinearGeometry;

/————————————————————————————————————————-//
点控制的设置。
PointControlpPath: 控制曲线, 生成实体过程中solid横断面对应点延该曲线变化
modelRef: PointControlpPath所在的Dgnmodel
+———————-+———————-+———————-+———————-+———————-+———/
List<BCIMNETGS.PointControl> GetPointControls(CurveVector PointControlpPath, DgnModelRef modelRef)
{
List<BCIMNETGS.PointControl> pointControls = new List<BCIMNETGS.PointControl>();

string pointName = “P_0”; //对应参数化模板中需要点控制方法控制的点的名称
pointControls.Add(new BCIMNETGS.PointControl(pointName, BCIMNETGS.PointControlDirection.Both, PointControlpPath, modelRef));

return pointControls;
}

/
————————————————————————————————————————-//
使用点控制生成solid实体。
solidTemplate: 参数化模板
planLinearElement: 实体生成路径的平曲线
profileElement: 实体生成路径的纵曲线
startLimitLine: 实体的起点限制曲线
endLimitLine: 实体的终点限制曲线
+———————-+———————-+———————-+———————-+———————-+———/
void Example_CreateSolidWithSolidTemplate(BCIMNETT.Template solidTemplate, BCIFLG.LinearElement planLinearElement, BCIFLG.ProfileElement profileElement, BCIFLG.LinearElement startLimitLine, BCIFLG.LinearElement endLimitLine, DgnModelRef modelRef)
{
// 构造 SolidCorridor, 可根据参数选择生成的实体横断面垂直于地面或垂直于路径。
BCIMNETGS.SolidCorridor corridor = new BCIMNETGS.SolidCorridor(planLinearElement, profileElement, BCIMNETGS.SectionVerticalRotation.Vertical);

// 添加SolidCorridorInternal, 确定solid实体段的生成规则
corridor.AddSolidCorridorInterals(“Segement_1”, solidTemplate, parametricConstraints, modelRef, startLimitLine, endLimitLine, 0.0, 0.0);

// 设置点控制
List<BCIMNETGS.PointControl> pointControls = GetPointControls();
if (pointControls != null)
corridor.AddPointControls(“Segement_1”, pointControls);

// 生成实体并获取结果
BCIMNETGS.ISolidCreator solidFromCorridor = new BCIMNETGS.ISolidCreator(corridor, modelRef);
solidFromCorridor.CreateSolid();

List<SolidKernelEntity> solidBodies = new List<SolidKernelEntity>();
solidFromCorridor.GetSolidBodies(solidBodies);

foreach (SolidKernelEntity solid in solidBodies)
{
if (solid != null)
{
Bentley.DgnPlatformNET.Elements.Element ehOut;
Bentley.DgnPlatformNET.Convert1.BodyToElement(out ehOut, solid, null, modelRef);
ehOut.AddToModel();
}
}
*}

7 CIM Template

7.1 简介

CIM TEMPLATE 是一个模板库管理工具,它允许你用编程和可视化的方式来管理您的预置模板库。
对于二次开发者,我们提供了 C++ 和 C# 两种语言的支持,您可以使用您擅长的编程语言进行开发。对于一般用户,我们提供了一个模板编辑器,它允许您以所见即所得的方式展示、预览、管理和编辑您的预置模板库。
CIM TEMPLATE 以 Open Bridge Modeler 的模板为原型,提供了一套全新的实现。我们保留了 Open Bridge Modeler 的绝大部分功能,对部分功能进行了优化,对相关问题进行了修复,添加了一部分我们认为比较重要的功能,并且设计了全新的模板编辑器。我们在最大程度上保持了与 Open Bridge Modeler的兼容性,您可以方便地在 Open Bridge Modeler 模板和 CIM 模板间转换。
CIM TEMPLATE 依然采用 XML 文件来保存模板库,所不同的是我们修改了部分 XML 节点的格式。具体的差异请参阅CIM 模板与 OpenBridge Modeler模板的差异一章。
CIM TEMPLATE 提供了完整地管理模板所需要的接口,支持对模板库的增删改查,您可以用代码生成需要的参数化模板。
CIM TEMPLATE 核心的功能之一就是创建参数化模板,我们主要通过设置约束和参数绑定来实现。
CIM TEMPLATE 的模板编辑器与 Open Bridge Modeler 的设计风格迥异,但功能基本相同,详细的介绍请参见模板编辑器一章。

7.2 快速入门

请参考 CIM 开发包中的示例新建自己的开发项目,您也可以直接在示例程序中添加自己的代码。模板的例子位于 examples/CIMExamples/CIMExample 项目的 CIMExampleTemplateUseCase.cpp 中。
下面的例子演示了创建模板的基本流程。这里以创建一个矩形为例。

  1. 创建模板库
  2. 创建目录
  3. 创建模板对象
  4. 添加点坐标
  5. 创建组件
  6. 转换成曲线
  7. 添加到模型
  8. auto uor = ISessionMgr::GetActiveDgnModelP()->GetModelInfo().GetUorPerMeter();
  9. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  10. auto category = library->AddCategory(L”UseCase”);
  11. auto t = category->AddTemplate(L”rectangle”);
  12. auto P0 = t->AddPoint(-1 uor, -1 uor);
  13. auto P1 = t->AddPoint(-1 uor, 1 uor);
  14. auto P2 = t->AddPoint(1 uor, 1 uor);
  15. auto P3 = t->AddPoint(1 uor, -1 uor);
  16. auto component = t->AddComponent();
  17. component->AddLine(P0->GetName().GetWCharCP(), P1->GetName().GetWCharCP());
  18. component->AddLine(P1->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  19. component->AddLine(P2->GetName().GetWCharCP(), P3->GetName().GetWCharCP());
  20. component->AddLine(P3->GetName().GetWCharCP(), P0->GetName().GetWCharCP());
  21. auto curve = t->ToCurveVector();
  22. EditElementHandle eeh;
  23. if (BentleyStatus::SUCCESS != DraftingElementSchema::ToElement(eeh, curve, nullptr, true, ISessionMgr::GetActiveDgnModelP()))
  24. {
  25. return;
  26. }
  27. eeh.AddToModel();

其中 ,第1、2、3步创建了模板的存储对象,并指明了模板在模板库中的相对位置。第4步创建了描述模板的顶点坐标。第5步描述了模板的连接方式,本例中定义了4条首尾相连的线段。第6,7步将模板转换成曲线并添加到模型中。

7.3模板的二次开发

7.3.1模板库的管理

7.3.1.1模板库的结构

模板以树状结构管理。ITemplateLibrary 表示模板库的根节点。ICategory 表示目录节点,我们通过目录来对模板进行分类存放。ITemplate 表示模板,存储着模板的几何信息。

7.3.1.2模板库的操作

(1)模板库的创建:调用auto library = ITemplateLibrary::Create()创建模板库。新创建出的模板不包含任何内容。接下来你可以选择从模板库文件中加载模板库或者从零开始创建全新的模板库。
(2)模板库的加载:调用library->LoadXMLFile(input_path)从模板库文件加载模板库。目前模板库文件以 XML 保存。支持 OpenBridge Modeler 和 CIM 的模板库文件。但应注意由于 OpenBridge Modeler 跟 CIM 有部分差异,差异部分的行为存在不同。
(3)模板库的保存:调用library->SaveXMLFile(output_path)将模板库保存到指定路径。
(4)模板库的查询:
我们提供了以下几种查询方式,

  • 查询指定路径的目录
  • 查询指定名字的目录
  • 查询指定路径的模板
  • 查询指定名字的模板
  • 查询指定全局唯一标识符的模板

注意,这里路径由 目录名//子目录名//模板 的格式给出,它表明了目录或者模板在 XML 节点树中的相对位置。其中分隔符支持反斜杠和斜杠。
如果目录或模板的名字不唯一,我们将返回按深度优先搜索找到的第一个结果。
(5)模板库的修改:调用auto category = library->AddCategory(name)添加目录。如果目录已经存在则会返回已经存在的目录;调用library_->RemoveCategory(name)删除目录。

7.3.1.3目录的操作

(1)目录的创建:调用auto category = library->AddCategory(name)创建目录。library 是模板库对象,这样调用保证了目录在XML节点树中的相对位置。
(2)目录的删除:调用library_->RemoveCategory(name)删除目录。所有删除操作由父级执行。
(3)目录的修改:调用auto subcategory = category->AddSubcategory(name)新增子目录;调用auto t = category->AddTemplate(name)新增模板;调用category->RemoveSubcategory(name)删除子目录;调用category->RemoveTemplate(name)删除模板。
(4)目录的查询:
提供了一下几种查询方式,

  • 查询目录下的子目录
  • 查询目录下的模板,可以通过参数决定使用深度优先还是广度优先查询
  • 根据全局唯一标识符查询目录下的模板,同样可以指定搜索策略

注意,正常的模板库中,全局唯一标识符应该是不会重复的。

7.3.2模板的主要功能

7.3.2.1模板的属性

属性名 含义
Name 模板的名字,可用于查询
Tag 标记值,可存储一个整数值
GUID 全局唯一标识符,可用于查询
LastRevisedDate 最近一次的修改时间
LastRevisedBy 最近一次的修改者
versionMajor 主版本号
versionMinor 次版本号

7.3.2.2模板的存储结构

模板由若干个组件构成,每一个组件都必须是一个闭合环。通过组件的 parent 属性来指定两个组件的包含关系,从而提供对空洞的定义。
闭合环一般由线、弧和圆来定义,可以分为以下几种情况,
椭圆组件只包含一个椭圆对象。
线组件由首尾相连的线段定义。
线弧组件由首尾相连的线和弧共同定义。
可见,目前构成组件的基本几何分为,
组件的 parent 属性则表示实体由子过渡到父的区域应该被填充。
我们通过下面的代码可以创建一个圆环,

  1. auto uor = ISessionMgr::GetActiveDgnModelP()->GetModelInfo().GetUorPerMeter();
  2. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  3. auto category = library->AddCategory(L”UseCase”);
  4. auto t = category->AddTemplate(L”swimring”);
  5. auto center = t->AddPoint(0, 0);
  6. double radius0 = 20 * uor;
  7. double radius1 = 10 * uor;
  8. auto outer = t->AddComponent();
  9. outer->AddEllipse(center->GetName().GetWCharCP(), radius0, radius0);
  10. auto inner = t->AddComponent();
  11. inner->AddEllipse(center->GetName().GetWCharCP(), radius1, radius1);
  12. inner->SetParent(outer->GetName().GetWCharCP());
  13. auto curve = t->ToCurveVector();

如图

7.3.2.3模板的约束计算

(1)基本约束类型:
目前实现的基本约束有,

  • 水平
  • 竖直
  • 坡度
  • 偏移
  • 角度
  • 距离

CIM 的约束都是点约束,即通过指定点与点之间的定位关系来确定当前点的计算坐标。在二维平面上,由于要确定横纵两个坐标,计算坐标往往需要由一对约束确定。只有个别约束由于只需确定一个坐标(例如水平、竖直),或者其约束本身设计特殊(例如偏移)只需单一约束即可确定。当使用单一约束时,计算时会以当前计算点的坐标为基础计算新的坐标。
下面,我们对这些基本约束的含义一一介绍。
(2)水平约束

水平距离

计算点
父参考点

纵坐标不变,横坐标根据水平距离偏移。
(3)竖直约束
父参考点

竖直距离

计算点

横坐标不变,纵坐标根据竖直距离偏移。
(4)坡度约束

计算点

坡度

父参考点

计算点与父参考点连线的斜率等于坡度。单独使用坡度约束时,我们会用原始点与父参考点的距离作为第二个计算条件来唯一的确定计算点在连线上的位置。
(5)偏移约束
父参考线
中点

终点
起点

偏移距离

计算点

CIM中偏移约束的父参考是一条直线。偏移距离即待计算点与父参考线的距离。距离为正表示计算点应该落在父参考线顺时针一侧。
单独使用偏移约束时,我们会用锁定点作为第二个计算条件来唯一地确定计算点的位置。锁定点可理解为垂足。
(6)角度约束

计算点

角度
父参考点

参考点2
参考点1

以父参考点为出发点,参考点1到参考点2的向量为方向确定一条射线,然后将这条射线转动指定角度,逆时针为正,那么计算点应该落在转动后的射线方向上。
角度约束只能联合使用。
(7)距离约束

父参考点

计算点

距离

距离约束必须联合使用,计算点会落在以父参考点为圆心,指定距离为半径的圆上,需要添加其他约束来确定最终位置。
(8)约束的组合使用
目前支持的约束组合如列表所示,

约束类型 None Horizontal Vertical Slope Offset Angle Distance
None
Horizontal
Vertical
Slope
Offset
Angle
Distance

每个约束对应一个求解条件,两个约束结合起来即可确定一个点的横纵坐标。
在使用约束的时候,应当避免构造错误的约束条件。比如两个相同坡度的约束,由于两条平行线是无法算出交点的,所以这样的约束是无效的。其他约束也类似,需要保证约束的设置在数学上是可解的。
其次,模板的点之间会形成一个约束网络,为了保证约束可解,应当避免环形依赖,例如 A ->B->C->A 这样的约束设置是不可解的。
下面,我们举几个使用约束的例子。

  1. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  2. auto category = library->AddCategory(L”UseCase”);
  3. auto t = category->AddTemplate(L”NoneHorizontal”);
  4. auto P0 = t->AddPoint(0, 0);
  5. // We expect the point to use relative coordinates, so no coordinates are entered
  6. auto P1 = t->AddPoint(false);
  7. auto P2 = t->AddPoint(1, -1);
  8. auto P3 = t->AddPoint(0, -1);
  9. auto shape = t->AddComponent();
  10. shape->AddLine(P0->GetName().GetWCharCP(), P1->GetName().GetWCharCP());
  11. shape->AddLine(P1->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  12. shape->AddLine(P2->GetName().GetWCharCP(), P3->GetName().GetWCharCP());
  13. shape->AddLine(P3->GetName().GetWCharCP(), P0->GetName().GetWCharCP());
  14. auto firstConstraint = P1->GetFirstConstraint();
  15. firstConstraint->SetConstraintType(CIM::CIMTemplate::IConstraint::ConstraintType::Horizontal);
  16. firstConstraint->SetParent(P0->GetName().GetWCharCP());
  17. firstConstraint->SetValue(2);
  18. // template must reevaluate when the constranit changed.
  19. t->Reevaluate();
  20. auto curve = t->ToCurveVector();

上面的代码创建了一个水平约束。相对于创建无约束模板,这里在点上设置了约束参数,即 P1 相对于 P0 水平偏移 2 。设置完约束后,我们需要对模板进行重新解算,以确定 P1 的最终位置。
生成的图形如下,
OpenCivil高级自定义API - 图6

  1. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  2. auto category = library->AddCategory(L”UseCase”);
  3. auto t = category->AddTemplate(L”SlopeOffset”);
  4. auto P0 = t->AddPoint(0, 0);
  5. // We expect the point to use relative coordinates, so no coordinates are entered
  6. auto P1 = t->AddPoint(false);
  7. auto P2 = t->AddPoint(1, -1);
  8. auto P3 = t->AddPoint(0, -1);
  9. auto shape = t->AddComponent();
  10. shape->AddLine(P0->GetName().GetWCharCP(), P1->GetName().GetWCharCP());
  11. shape->AddLine(P1->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  12. auto line = shape->AddLine(P2->GetName().GetWCharCP(), P3->GetName().GetWCharCP());
  13. shape->AddLine(P3->GetName().GetWCharCP(), P0->GetName().GetWCharCP());
  14. auto firstConstraint = P1->GetFirstConstraint();
  15. firstConstraint->SetConstraintType(CIM::CIMTemplate::IConstraint::ConstraintType::Slope);
  16. firstConstraint->SetParent(P0->GetName().GetWCharCP());
  17. firstConstraint->SetValue(1); // radians
  18. auto secondConstraint = P1->GetSecondConstraint();
  19. secondConstraint->SetConstraintType(CIM::CIMTemplate::IConstraint::ConstraintType::Offset);
  20. // IMPORTTANCE: line cannot include P1
  21. secondConstraint->SetParent(line->GetName().GetWCharCP());
  22. secondConstraint->SetValue(2); // clockwise positive.
  23. // template must reevaluate when the constranit changed.
  24. t->Reevaluate();
  25. auto curve = t->ToCurveVector();

上面的例子创建了一个坡度偏移约束,P1 与 P0 的坡度是1,即沿 45 度方向,同时距离底边为 2,重新计算后生成的图形如下,
OpenCivil高级自定义API - 图7
其他的约束请参考开发包中有关模板的例子。

7.3.2.4模板的参数绑定

约束中的 parametricLabel 字段可以标记约束值绑定的参数,这样标记为同一参数的约束会采用同一个值进行计算。
下面的例子为水平和竖直约束绑定了相同的变量,

  1. auto uor = ISessionMgr::GetActiveDgnModelP()->GetModelInfo().GetUorPerMeter();
  2. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  3. auto category = library->AddCategory(L”UseCase”);
  4. auto t = category->AddTemplate(L”Variable”);
  5. auto P0 = t->AddPoint(0, 0);
  6. // We expect the point to use relative coordinates, so no coordinates are entered
  7. auto P1 = t->AddPoint(false);
  8. auto P2 = t->AddPoint(1, -1);
  9. auto P3 = t->AddPoint(0, -1);
  10. WString variable = L”Offset”;
  11. double value = 2;
  12. // add variable
  13. t->AddVariable(variable.GetWCharCP(), value);
  14. auto shape = t->AddComponent();
  15. shape->AddLine(P0->GetName().GetWCharCP(), P1->GetName().GetWCharCP());
  16. shape->AddLine(P1->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  17. shape->AddLine(P2->GetName().GetWCharCP(), P3->GetName().GetWCharCP());
  18. shape->AddLine(P3->GetName().GetWCharCP(), P0->GetName().GetWCharCP());
  19. auto firstConstraint = P1->GetFirstConstraint();
  20. firstConstraint->SetConstraintType(CIM::CIMTemplate::IConstraint::ConstraintType::Horizontal);
  21. firstConstraint->SetParent(P0->GetName().GetWCharCP());
  22. firstConstraint->SetParametricLabel(variable.GetWCharCP());
  23. auto secondConstraint = P1->GetSecondConstraint();
  24. secondConstraint->SetConstraintType(CIM::CIMTemplate::IConstraint::ConstraintType::Vertical);
  25. secondConstraint->SetParent(P0->GetName().GetWCharCP());
  26. secondConstraint->SetParametricLabel(variable.GetWCharCP());
  27. // template must reevaluate when the constranit changed.
  28. t->Reevaluate();
  29. auto curve = t->ToCurveVector();

在绑定变量之前,你应当确保变量已经创建,生成的图像如下,
OpenCivil高级自定义API - 图8

7.3.2.5模板的表达式支持

约束中的 parametricLabel 不仅可以标记参数,还可以记录一个C表达式。
下面的例子使竖直偏移使水平偏移的两倍,

  1. auto uor = ISessionMgr::GetActiveDgnModelP()->GetModelInfo().GetUorPerMeter();
  2. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  3. auto category = library->AddCategory(L”UseCase”);
  4. auto t = category->AddTemplate(L”Variable”);
  5. auto P0 = t->AddPoint(0, 0);
  6. // We expect the point to use relative coordinates, so no coordinates are entered
  7. auto P1 = t->AddPoint(false);
  8. auto P2 = t->AddPoint(1, -1);
  9. auto P3 = t->AddPoint(0, -1);
  10. WString variable = L”Offset”;
  11. double value = 2;
  12. // add variable
  13. t->AddVariable(variable.GetWCharCP(), value);
  14. auto shape = t->AddComponent();
  15. shape->AddLine(P0->GetName().GetWCharCP(), P1->GetName().GetWCharCP());
  16. shape->AddLine(P1->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  17. shape->AddLine(P2->GetName().GetWCharCP(), P3->GetName().GetWCharCP());
  18. shape->AddLine(P3->GetName().GetWCharCP(), P0->GetName().GetWCharCP());
  19. auto firstConstraint = P1->GetFirstConstraint();
  20. firstConstraint->SetConstraintType(CIM::CIMTemplate::IConstraint::ConstraintType::Horizontal);
  21. firstConstraint->SetParent(P0->GetName().GetWCharCP());
  22. firstConstraint->SetParametricLabel(variable.GetWCharCP());
  23. auto secondConstraint = P1->GetSecondConstraint();
  24. secondConstraint->SetConstraintType(CIM::CIMTemplate::IConstraint::ConstraintType::Vertical);
  25. secondConstraint->SetParent(P0->GetName().GetWCharCP());
  26. secondConstraint->SetParametricLabel(L”Offset * 2”);
  27. // template must reevaluate when the constranit changed.
  28. t->Reevaluate();

在使用表达式之前,您应当确保表达式中涉及的变量已经创建,生成的图形如下,
OpenCivil高级自定义API - 图9

7.3.2.6模板的倒角设置

目前支持的倒角类型为,

  • 圆角
  • 直角
    • 对称
    • 非对称

下面的例子创建了一个圆角半径为 0.2 的矩形,

  1. auto uor = ISessionMgr::GetActiveDgnModelP()->GetModelInfo().GetUorPerMeter();
  2. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  3. auto category = library->AddCategory(L”UseCase”);
  4. auto t = category->AddTemplate(L”Fillet”);
  5. auto P0 = t->AddPoint(0, 0);
  6. auto P1 = t->AddPoint(1, 0);
  7. auto P2 = t->AddPoint(1, -1);
  8. auto P3 = t->AddPoint(0, -1);
  9. P1->SetCornerType(CIM::CIMTemplate::IPoint::CornerType::Fillet);
  10. P1->SetFillet(0.2);
  11. auto shape = t->AddComponent();
  12. shape->AddLine(P0->GetName().GetWCharCP(), P1->GetName().GetWCharCP());
  13. shape->AddLine(P1->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  14. shape->AddLine(P2->GetName().GetWCharCP(), P3->GetName().GetWCharCP());
  15. shape->AddLine(P3->GetName().GetWCharCP(), P0->GetName().GetWCharCP());
  16. auto curve = t->ToCurveVector();

你需要设置倒角类型并且设定合适的倒角半径,生成的图形如下,
OpenCivil高级自定义API - 图10

这是一个创建直倒角的例子,

  1. auto uor = ISessionMgr::GetActiveDgnModelP()->GetModelInfo().GetUorPerMeter();
  2. auto library = CIM::CIMTemplate::ITemplateLibrary::Create();
  3. auto category = library->AddCategory(L”UseCase”);
  4. auto t = category->AddTemplate(L”Chamfer”);
  5. auto P0 = t->AddPoint(0, 0);
  6. auto P1 = t->AddPoint(1, 0);
  7. auto P2 = t->AddPoint(1, -1);
  8. auto P3 = t->AddPoint(0, -1);
  9. P1->SetCornerType(CIM::CIMTemplate::IPoint::CornerType::Chamfer);
  10. P1->SetChamfer(0.1, 0.2, P0->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  11. auto shape = t->AddComponent();
  12. shape->AddLine(P0->GetName().GetWCharCP(), P1->GetName().GetWCharCP());
  13. shape->AddLine(P1->GetName().GetWCharCP(), P2->GetName().GetWCharCP());
  14. shape->AddLine(P2->GetName().GetWCharCP(), P3->GetName().GetWCharCP());
  15. shape->AddLine(P3->GetName().GetWCharCP(), P0->GetName().GetWCharCP());
  16. auto curve = t->ToCurveVector();

你需要设置倒角类型和合适的数值,生成图形如下,
OpenCivil高级自定义API - 图11

7.4模板编辑器

我们重新设计了模板编辑器,主要分为树状管理区,功能按钮区,预览区和属性页四部分。
OpenCivil高级自定义API - 图12
树状管理区域主要对模板库进行管理,您可以查看、搜索和修改当前加载的模板库。
功能按钮区提供了若干操作指令,如保存、另存为以及控制显示模式等。
预览区允许你预览当前选中的模板。
属性页显示当前模板的信息,并且允许你修改这些信息。

7.5 CIM 模板与 OpenBridge Modeler模板的差异

7.5.1数据结构

项目 平台 差异
XML 声明 OpenBridge Modeler <?xml version=”1.0” encoding=”utf-8”?>
CIM <?xml version=”1.0” encoding=”UTF-8” standalone=”yes”?>
XML 根节点 OpenBridge Modeler PowerBridgeTemplate
CIM TemplateLibrary
XML 目录节点 OpenBridge Modeler 根目录为 Category,子目录为 SubCategory
CIM 所有目录节点名都为 Category
TAG 属性 OpenBridge Modeler
CIM Template, Point, Line, Component 上添加了一个 Tag 整型属性
Component 节点 OpenBridge Modeler
CIM 添加了 Parent 属性指定包含关系

7.5.2约束

约束类型 CIM OpenBridge Modeler
Horizontal + Angle 正常 无效
Vertical + Angle 正常 无效
Slope + Angle 正常 无效
Slope + Distance 父参考可以为不同点,如果有两个交点,交点在 Slope 确定的射线同侧选外侧点,否则选则射线方向的点 父参考为同一个点
Offset + None 根据 LockTo 对齐一端 无效

7.5.3参数绑定

CIM OBM
参数的作用域在整个模板节点中有效 只在绑定的节点上有效
参数支持数学表达式 不支持

7.5.4模板编辑器

模板编辑器已经重新设计,功能不尽相同。
CIM
OpenCivil高级自定义API - 图13
OpenBridge Modeler
OpenCivil高级自定义API - 图14