reeCAD源码分析:Assembly4模块

济南友泉软件有限公司

本文主要描述Assembly4的实现原理、相关组件等方面的内容, 并不对使用方法进行阐述,希望对从事FreeCAD研究、国产CAX(CAD/CAE/CAM软件开发的朋友们有帮助。

一、概述

Assembly4模块可实现多个几何体对象的装配功能。需要指出的是,Assembly4装配功能的实现并不是借助于几何约束求解器,而是通过三维局部坐标系变换(也称作基准坐标系,Local Coordinate System, Datum Coordinate System)。

具体来说,Assembly4模块中使用一个App:Part类型的Model表示装配体,可以将多个几何体(App::Part、PartDesign::Body)以App::Link对象的形式添加到装配体Model中。在这过程中,可以通过Expression指定几何体与几何体、几何体与Model的局部坐标系变换关系。然后,Assembly4便可借助于Expresion Engine来完成这种局部坐标系变换,进而实现多个几何体的装配。

二、Assembly4原理

Ref. from Assembly 4 workbench-----------------------------

The basic principle is the one from assembly without solver: FreeCAD's App::Part container serve as basic building blocs, that are inserted into each other using the App::Link interface introduced with FreeCAD 0.19.

The advantage of App::Link feature is that it's possible to link FreeCAD objects between files, seamlessly: a model in a file will appear in the tree of another model in another file, but the data is not copied across. This allows to create many instances of the same object without any overhead, allowing the creation of large assemblies. In other words, this is identical what other CAD systems do when speaking of assembly.

The particularity of Assembly 4 is that the placement of the inserted (linked) part is done by matching corresponding coordinate systems in the parent assembly and in the linked part: therefore, there must be at least 1 coordinate system in the assembly and at least 1 in the part. There can be many coordinate systems in each, and the user can choose which of these coordinate systems — in the assembly and in the part — shall be used. Under the hood, for those interested in the technical details, the ExpressionEngine of the Placement property of the App::Link is used to superimpose the Placement of two coordinate systems — one in the assembly and one in the inserted part — which fixes all degrees of freedom of the inserted part.

Another particularity of Assembly 4 is that there is no difference between a part and an assembly: it's possible to mix datum objects, 2D sketches, 3D geometries and inserted parts at will. This feature can prove to be extremely powerful once a user gets used to it.

-----------------------------Ref. from Assembly 4 workbench

三、预备知识:三维空间几何变换

3.1 对偶四元数

四元数(Quaternion)本质上是一种高阶复数,是由实数加上三个虚数单位构成的,它的数学形式为

其中

四元组可以用于表示空间旋转变换,比如绕过原点的单位向量旋转θ角度,四元组表示可表示为

对偶数(Dual number)的概念类似于复数,它的数学形式为

其中r和d分别表示实部和对偶部,ε表示对偶算子。

对偶数的三角函数定义为:

对偶四元数(Dual Quaternions)是实部和对偶部都为四元数的对偶数,又可称为八元数。对偶四元数是对偶数和四元数在多维空间中的有机结合,可以理解为元素为四元数的对偶数,同样可以理解为元素为对偶数的四元数。

较之四元数只能表示3D旋转,对偶数只能表示平移,对偶四元数的优越性体现在它继承了二者的共同特性,从而能统一的表示旋转与平移。常规四元数只能表示空间旋转,而对偶四元数可以表示空间任意旋转和平移的组合。

四、核心组件

Assembly4装配功能的实现主要借助于App::Link、App::Coordinate、App::Expression等组件。

    4.1 App::Part

App::Part主要作为几何体容器,Assembly4 Model对象其实就是一个App::Part类型的对象。App::Part通过App::OriginGroupExtension以增加了一个App::Origin对象,正是App::Origin对象定义了一个局部坐标系,App::Part所包含的其他几何体的位置均是相对于这个局部坐标系。

    4.2 App::Link

App::Link用于链接外部几何体对象,这些目标几何体对象可以存在于其他FreeCAD文档对象中。

    4.3 PartDesign::Body

PartDesign::Body可以看作是一个特征对象容器,用于在二维约束草图的基础之上创建单个三维几何体对象:即在草图的基础之上,通过一系列施加特征以最终生成一个几何体。

    4.4 PartDesign::Coordinate

PartDesign::Coordinate就是前面提到的基准坐标系(局部坐标系),几何体对象Placement属性实际上就是相对于这个局部坐标系来定义的。

    4.5 App::Expression

App::Expression用于描述几何体之间、几何体与装配体之间对应的基准坐标系的几何变换关系。

五、Assembly4代码分析

Assembly4模块基于描述局部坐标系变换的表达式而非几何约束求解器实现了三维零件的装配功能。

具体来说,在一个名为Model的App::Part对象中,创建多个指向不同零件(App::Part、PartDesign::Body)的连接(App::Link),然后指定零件与Model对象(或其他零件)的几何变换表达式(App::Expression),而这个几何变换表达式实则表示一个局部坐标系变换关系,再此基础之上,利用App::Part提供的局部坐标系变换来完成了多个零件的装配,这就是Assembly4的工作原理。

5.1 创建装配体

装配体对象实际上就是一个App::Part对象,目前同一文档对象中只能有一个名为"Model"的装配体对象。这个装配体对象实际上是承载多个零件连接对象的容器。

newModelCmd.py中定义了创建装配体的newModel命令,

    # the real stuffdef Activated(self):# get the current active document to avoid errors if user changes tabself.activeDoc = App.activeDocument()# check whether there is already Model in the documentif not self.checkModel():# create a group 'Parts' to hold all parts in the assembly document (if any)# must be done before creating the Asm4 ModelpartsGroup = self.activeDoc.getObject('Parts')if partsGroup is None:partsGroup = self.activeDoc.addObject( 'App::DocumentObjectGroup', 'Parts' )# create a new App::Part called 'Model'model = self.activeDoc.addObject('App::Part','Model')# set the type as a "proof" that it's an Assembly4 Modelmodel.Type='Assembly4 Model'# add an LCS at the root of the Model, and attach it to the 'Origin'lcs0 = model.newObject('PartDesign::CoordinateSystem','LCS_Origin')lcs0.Support = [(model.Origin.OriginFeatures[0],'')]lcs0.MapMode = 'ObjectXY'lcs0.MapReversed = False# create a group Constraints to store future solver constraints theremodel.newObject('App::DocumentObjectGroup','Constraints')# create an object Variables to hold variables to be used in this documentmodel.addObject(Asm4.createVariables())# create a Configuration propertymodel.addProperty('App::PropertyEnumeration', 'Configuration', 'Parameters')model.Configuration = ['Default']# move existing parts and bodies at the document root to the Parts group# not nested inside other parts, to keep hierarchyif partsGroup.TypeId=='App::DocumentObjectGroup':for obj in self.activeDoc.Objects:if obj.TypeId in Asm4.containerTypes and obj.Name!='Model' and obj.getParentGeoFeatureGroup() is None:partsGroup.addObject(obj)else:Asm4.warningBox(   'There seems to already be a Parts object, you might get unexpected behaviour' )# recompute to get rid of the small overlaysmodel.recompute()self.activeDoc.recompute()

在newModel::Activated(self)函数中,首先创建了App::Part类型的Model对象,然后将已有的顶级App::Part移入到了名为Parts的App::DocumentObjectGroup对象中。

5.2 创建零件连接

Assembly4中不仅可以通过连接引用本文档中的零件对象,也可以引用当前打开的其他文档中的零件对象。

在InsertLinkCmd.py文件中,insertLink实现了根据用户输入创建引用零件连接的功能。

    """+-----------------------------------------------+|         the real stuff happens here           |+-----------------------------------------------+"""def onCreateLink(self):# parse the selected items # TODO : there should only be 1selectedPart = []for selected in self.partList.selectedIndexes():# get the selected partselectedPart = self.allParts[ selected.row() ]# get the name of the link (as it should appear in the tree)linkName = self.linkNameInput.text()# only create link if there is a Part object and a nameif self.asmPart and selectedPart and linkName:# create the App::Link with the user-provided name#createdLink = self.activeDoc.getObject('Model').newObject( 'App::Link', linkName )createdLink = self.asmPart.newObject( 'App::Link', linkName )# assign the user-selected selectedPart to itcreatedLink.LinkedObject = selectedPart# If the name was already chosen, and a UID was generated:if createdLink.Name != linkName:# we try to set the label to the chosen namecreatedLink.Label = linkName# update the linkcreatedLink.recompute()# close the dialog UI...self.close()# ... and launch the placement of the inserted partGui.Selection.clearSelection()Gui.Selection.addSelection( self.activeDoc.Name, self.asmPart.Name, createdLink.Name+'.' )# ... but only if we're in an Asm4 Modelif self.asmPart.Name=='Model':Gui.runCommand( 'Asm4_placeLink' )# if still open, close the dialog UIself.close()

在insertLink::onCreateLink(self)函数中,根据用户选择的零件及通过界面输入的连接名字,创建了App::Link的连接对象用于引用零件。完成连接创建之后,触发了安置零件的命令。

5.3 安置零件

将零件通过App::Link引入到当前Model装配体之后,就可以指定零件与装配体(或零件)之间的局部坐标系变换的表达式了。

placeLinkCmd.py中定义了placeLinkCmd、placeLinkUI等用于完成局部坐标系变换表达式的定义。

    """+-----------------------------------------------+| check that all necessary things are selected, ||   populate the expression with the selected   ||    elements, put them into the constraint     ||   and trigger the recomputation of the part   |+-----------------------------------------------+"""def Apply( self ):# get the instance to attach to:# it's either the top level assembly or a sister App::Linkif self.parentList.currentText() == 'Parent Assembly':a_Link = 'Parent Assembly'a_Part = Noneelif self.parentList.currentIndex() > 1:parent = self.parentTable[ self.parentList.currentIndex() ]a_Link = parent.Namea_Part = parent.LinkedObject.Document.Nameelse:a_Link = Nonea_Part = None# the attachment LCS's name in the parent# check that something is selected in the QlistWidgetif self.attLCSlist.selectedItems():a_LCS = self.attLCStable[ self.attLCSlist.currentRow() ].Nameelse:a_LCS = None# the linked App::Part's namel_Part = self.selectedLink.LinkedObject.Document.Name# the LCS's name in the linked part to be used for its attachment# check that something is selected in the QlistWidgetif self.partLCSlist.selectedItems():#l_LCS = self.partLCSlist.selectedItems()[0].text()l_LCS = self.partLCStable[ self.partLCSlist.currentRow() ].Nameelse:l_LCS = None# check that all of them have something in# constrName has been checked at the beginningif a_Link and a_LCS and l_Part and l_LCS :# this is where all the magic is, see:# # https://forum.freecadweb.org/viewtopic.php?p=278124#p278124## as of FreeCAD v0.19 the syntax is different:# https://forum.freecadweb.org/viewtopic.php?f=17&t=38974&p=337784#p337784## expr = ParentLink.Placement * ParentPart#LCS.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1'           # expr = LCS_in_the_assembly.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1'            expr = Asm4.makeExpressionPart( a_Link, a_Part, a_LCS, l_Part, l_LCS )# add the Asm4 properties if it's a pure App::LinkAsm4.makeAsmProperties(self.selectedLink)# store the part where we're attached to in the constraints objectself.selectedLink.AssemblyType = 'Asm4EE'self.selectedLink.AttachedBy = '#'+l_LCSself.selectedLink.AttachedTo = a_Link+'#'+a_LCS# load the expression into the link's Expression Engineself.selectedLink.setExpression('Placement', expr )# recompute the object to apply the placement:self.selectedLink.recompute()self.parentAssembly.recompute(True)return Trueelse:#FCC.PrintWarning("Problem in selections\n")return False

可以看到,在placeLinkUI::apply(self)函数中,根据用户选择,正是通过调用Asm4.makeExpreessionPart()函数构造了l_PART/l_LCS与a_PART/a_LCS之间的局部坐标系变换的表达式。

"""+-----------------------------------------------+|         populate the ExpressionEngine         ||             for a linked App::Part            |+-----------------------------------------------+
"""
def makeExpressionPart( attLink, attDoc, attLCS, linkedDoc, linkLCS ):# if everything is definedif attLink and attLCS and linkedDoc and linkLCS:# this is where all the magic is, see:# # https://forum.freecadweb.org/viewtopic.php?p=278124#p278124## as of FreeCAD v0.19 the syntax is different:# https://forum.freecadweb.org/viewtopic.php?f=17&t=38974&p=337784#p337784# expr = ParentLink.Placement * ParentPart#LCS.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1# expr = LCS_in_the_assembly.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1# the AttachmentOffset is now a property of the App::Link# expr = LCS_in_the_assembly.Placement * AttachmentOffset * LinkedPart#LCS.Placement ^ -1expr = attLCS+'.Placement * AttachmentOffset * '+linkedDoc+'#'+linkLCS+'.Placement ^ -1'# if we're attached to another sister part (and not the Parent Assembly)# we need to take into account the Placement of that Part.if attDoc:expr = attLink+'.Placement * '+attDoc+'#'+exprelse:expr = Falsereturn expr

参考

Assembly4 Workbenchhttps://wiki.freecadweb.org/Assembly4_WorkbenchFreeCAD_Assembly4https://github.com/Zolko-123/FreeCAD_Assembly4/tree/a6deaefa10e642ebb2ac0391b1190cd28ed00b24Expressionshttps://wiki.freecadweb.org/Expressions

Quaternion Wikihttps://math.fandom.com/wiki/Quaternion

SongHo OpenGL: Quaternionhttp://www.songho.ca/math/quaternion/quaternion.html

Maths - Quaternions http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/index.htm

FreeCAD源码分析:Assembly4模块相关推荐

  1. FreeCAD源码分析:FreeCADGui模块

    FreeCAD源码分析:FreeCADGui模块 济南友泉软件有限公司 FreeCADGui项目实现了界面操作.模型显示与交互等相关功能,项目构建生成FreeCAD(_d).dll动态链接库. Fre ...

  2. FreeCAD源码分析:FreeCADMain模块

    FreeCAD源码分析:FreeCADCmd\FreeCADMain\FreeCADPy模块 济南友泉软件有限公司 FreeCAD提供了控制台.GUI.Python等三种运行模式,分别对应FreeCA ...

  3. koa源码分析-co模块以及thunk

    Thunk以及CO模块 co4.0之前都是返回的thunk函数 之后的都是返回promise thunk thunk:在 JavaScript 语言中,Thunk 函数替换的是将多参数函数,替换成单参 ...

  4. Python3.5源码分析-sys模块及site模块导入

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的sys模块初始化 根据分析完成builtins ...

  5. nginx源码分析之模块初始化

    在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...

  6. Fabric源码分析-共识模块

    正好这些天要有一个需求要帮客户魔改Fabric-v0.6,把一些hyperchain的高级特性移植过去,借此机会把之前看过的源码在梳理一下. 下面就是对Fabric共识模块的源码分析和梳理,代码都是以 ...

  7. [Abp vNext 源码分析] - 2. 模块系统的变化

    一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...

  8. Canal源码分析deployer模块

    canal有两种使用方式:1.独立部署 2.内嵌到应用中. deployer模块主要用于独立部署canal server.关于这两种方式的区别,请参见server模块源码分析.deployer模块源码 ...

  9. 5章 性能平台GodEye源码分析-第三方模块

    5. 第三方模块 5.1 Crash(XCrash) Crash监控崩溃后的堆栈上传,作者采用接入爱奇艺的XCrash框架 源码分析 1.启动Crash的监控 Crash的监控通过反射启动XCrash ...

  10. WebRTC源码分析——Call模块

    目录 1. 引言 2. Call对象的创建 2.1 创建CallFactory对象 2.2 创建Call对象 2.2.1 PeerConnection.CreateCall_w 2.2.2 CallF ...

最新文章

  1. Android 工程报错解决 Unable to resolve target 'android-17'
  2. MSSql存储过程高效应用
  3. 若有说明:int *p1,*p2,m=5,n;以下均是正确赋值语句的选项是( )?
  4. mysql存储过程的学习(mysql提高执行效率之进阶过程)
  5. mysql workbench画er_MySQL Workbench画ER图
  6. html字体样式圆体,网红字体“柚圆体”出现,书写工整又清新,老师也被这种字体圈粉...
  7. 谷歌浏览器打开特殊端口号地址方法
  8. 根据ip查经纬度软件_根据地址查询经纬度IP(百度地图)
  9. html怎么动态背景,HTML 动态背景
  10. 多渠道打包,生成不同包名的包
  11. html5辅助,Disable HTML5 Autoplay
  12. unity 扩展器添加脚本
  13. C语言的初步简单认识
  14. msvcp140.dll丢失VCRUNTIME140_.dll丢失的解决办法
  15. NOIP2016Day1总结
  16. 腾讯面试题:64匹马,8赛道,找出最快的4匹最少要几次?
  17. 菜鸟在Linux零碎中安置Oracle 11G
  18. SpringCloud系列【security oauth2】
  19. 华为鸿蒙智慧屏_华为鸿蒙系统到底什么样?荣耀智慧屏秀出答案
  20. 如何聪明地拒绝领导给自己安排的不想做的工作?

热门文章

  1. 【OpneWRT】编译ipk
  2. 跟父亲一样伟大的程序员,请一定要照顾好自己!
  3. matlab程序设计八个实验报告,MATLAB程序设计实验报告.doc
  4. css 清除表单样式,css form表单样式清除
  5. Revit二次开发——新建墙类型
  6. 龙达pm3 proxmark3各个版本
  7. 最高效“双11”背后:圆通更不一样了
  8. 选择SOA平台的标准是什么?
  9. Coablt strike官方教程中文版
  10. Everything搜索语法