├── Mod_Asm4 ├── VERSION ├── icons │ ├── Assembly4.png │ ├── PartDesign_Point.svg │ ├── New_Axis.svg │ ├── AxisCross.svg │ ├── Point.svg │ ├── CoordinateSystem.svg │ ├── FreeCad.svg │ ├── Model_NewSketch.svg │ ├── PartDesign_Body.svg │ ├── ImportDatum.svg │ ├── LinkModel.svg │ ├── Assembly4.svg │ ├── Model.svg │ └── Place_AxisCross_small.svg ├── README.md ├── asm4wb_locator.py ├── Init.py ├── newBodyCmd.py ├── newModelCmd.py ├── newSketchCmd.py ├── newPointCmd.py ├── newLCSCmd.py ├── updateAssemblyCmd.py ├── InitGui.py ├── libAsm4.py ├── insertLinkCmd.py └── importDatumCmd.py ├── Resources └── media │ ├── asm_EE.png │ ├── Asm4_V4.gif │ ├── Asm4_wb0.png │ ├── Asm4_wb1.png │ ├── Asm4_wb2.png │ ├── Asm4_wb3.png │ ├── Toolbar.png │ ├── LCS_MapMode.png │ ├── LCS_Attachment.png │ ├── asm_Bielle_demo.png │ ├── asm_EE_showall.png │ ├── asm_V4_2pistons.gif │ ├── Lego_House+Garden.png │ ├── asm_Bielle_tree_arrows.png │ └── asm_Bielle_constr_Offset.png ├── Examples ├── Asm4_Example1 │ ├── Cuve.fcstd │ ├── Bague.fcstd │ ├── Bielle.fcstd │ ├── Screw_CHC.fcstd │ └── asm_Bielle.fcstd ├── Asm4_Example2 │ ├── asm_V4.FCStd │ ├── Cylindre.FCStd │ ├── Bielle │ │ ├── Bague.fcstd │ │ ├── Cuve.fcstd │ │ ├── Bielle.fcstd │ │ ├── Screw_CHC.fcstd │ │ └── asm_Bielle.fcstd │ ├── Crankshaft.FCStd │ ├── Piston │ │ └── Piston.FCStd │ └── ReadMe.txt └── Asm4_Example3 │ ├── Support.fcstd │ ├── Wheel.fcstd │ ├── Triangle.fcstd │ ├── asm_Hypnotic.fcstd │ ├── kunda1-thingy-sketcher.FCStd │ └── ReadMe.txt └── README.md /Mod_Asm4/VERSION: -------------------------------------------------------------------------------- 1 | Assembly 4 Workbench for FreeCAD-v0.19 2 | v0.6 2019.10.05 3 | 4 | -------------------------------------------------------------------------------- /Resources/media/asm_EE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/asm_EE.png -------------------------------------------------------------------------------- /Mod_Asm4/icons/Assembly4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Mod_Asm4/icons/Assembly4.png -------------------------------------------------------------------------------- /Resources/media/Asm4_V4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/Asm4_V4.gif -------------------------------------------------------------------------------- /Resources/media/Asm4_wb0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/Asm4_wb0.png -------------------------------------------------------------------------------- /Resources/media/Asm4_wb1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/Asm4_wb1.png -------------------------------------------------------------------------------- /Resources/media/Asm4_wb2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/Asm4_wb2.png -------------------------------------------------------------------------------- /Resources/media/Asm4_wb3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/Asm4_wb3.png -------------------------------------------------------------------------------- /Resources/media/Toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/Toolbar.png -------------------------------------------------------------------------------- /Resources/media/LCS_MapMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/LCS_MapMode.png -------------------------------------------------------------------------------- /Examples/Asm4_Example1/Cuve.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example1/Cuve.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example1/Bague.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example1/Bague.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example1/Bielle.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example1/Bielle.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/asm_V4.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/asm_V4.FCStd -------------------------------------------------------------------------------- /Examples/Asm4_Example3/Support.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example3/Support.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example3/Wheel.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example3/Wheel.fcstd -------------------------------------------------------------------------------- /Resources/media/LCS_Attachment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/LCS_Attachment.png -------------------------------------------------------------------------------- /Resources/media/asm_Bielle_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/asm_Bielle_demo.png -------------------------------------------------------------------------------- /Resources/media/asm_EE_showall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/asm_EE_showall.png -------------------------------------------------------------------------------- /Resources/media/asm_V4_2pistons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/asm_V4_2pistons.gif -------------------------------------------------------------------------------- /Examples/Asm4_Example1/Screw_CHC.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example1/Screw_CHC.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Cylindre.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Cylindre.FCStd -------------------------------------------------------------------------------- /Examples/Asm4_Example3/Triangle.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example3/Triangle.fcstd -------------------------------------------------------------------------------- /Resources/media/Lego_House+Garden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/Lego_House+Garden.png -------------------------------------------------------------------------------- /Examples/Asm4_Example1/asm_Bielle.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example1/asm_Bielle.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Bielle/Bague.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Bielle/Bague.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Bielle/Cuve.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Bielle/Cuve.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Crankshaft.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Crankshaft.FCStd -------------------------------------------------------------------------------- /Examples/Asm4_Example3/asm_Hypnotic.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example3/asm_Hypnotic.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Bielle/Bielle.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Bielle/Bielle.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Piston/Piston.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Piston/Piston.FCStd -------------------------------------------------------------------------------- /Resources/media/asm_Bielle_tree_arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/asm_Bielle_tree_arrows.png -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Bielle/Screw_CHC.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Bielle/Screw_CHC.fcstd -------------------------------------------------------------------------------- /Examples/Asm4_Example2/Bielle/asm_Bielle.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example2/Bielle/asm_Bielle.fcstd -------------------------------------------------------------------------------- /Resources/media/asm_Bielle_constr_Offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Resources/media/asm_Bielle_constr_Offset.png -------------------------------------------------------------------------------- /Examples/Asm4_Example3/kunda1-thingy-sketcher.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyw/FreeCAD_Assembly4/master/Examples/Asm4_Example3/kunda1-thingy-sketcher.FCStd -------------------------------------------------------------------------------- /Mod_Asm4/README.md: -------------------------------------------------------------------------------- 1 | # FreeCAD Assembly Without Solver / Assembly 4 2 | 3 | This directory is to be copied (or linked) to :
4 | * for Linux: ~/.FreeCAD/Mod/Mod_Asm4
5 | * for Windows: C:\Users\\*******\AppData\Roaming\FreeCAD\Mod\Mod_Asm4
6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/Asm4_Example2/ReadMe.txt: -------------------------------------------------------------------------------- 1 | Assembly of a V4 engine in Assembly-4: 2 | 3 | * open the top-level assembly asm_V4.fcstd 4 | 5 | * to animate the rotation of the crankshaft, activate document asm_V4.fcstd, 6 | and copy-and-paste the following lines into the python console: 7 | 8 | import time 9 | step = 10 10 | for angle in range( 30, 750+step, step ): 11 | App.getDocument("asm_V4").LCS_crankshaft.AttachmentOffset = App.Placement( App.Vector(0,0,0), App.Rotation( App.Vector(0,1,0), angle ) ) 12 | App.ActiveDocument.recompute() 13 | Gui.updateGui() 14 | time.sleep(0.025) 15 | 16 | 17 | -------------------------------------------------------------------------------- /Examples/Asm4_Example3/ReadMe.txt: -------------------------------------------------------------------------------- 1 | Assembly of a hypnotic thingy: https://forum.freecadweb.org/viewtopic.php?f=20&t=34530 2 | 3 | 4 | * open the top-level assembly asm_Hypnotic.fcstd 5 | 6 | * to animate the rotation of the wheel, activate document asm_Hypnotic.fcstd, 7 | and copy-and-paste the following lines into the python console: 8 | 9 | step = 1 10 | for angle in range( 15, 735+step, step ): 11 | App.getDocument("asm_Hypnotic").LCS_rot.AttachmentOffset = App.Placement( App.Vector(0,0,0), App.Rotation( App.Vector(0,0,1), angle ) ) 12 | App.activeDocument().recompute() 13 | Gui.updateGui() 14 | 15 | 16 | -------------------------------------------------------------------------------- /Mod_Asm4/asm4wb_locator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ################################################################################### 3 | # 4 | # dynamicdatawb_locator.py 5 | # 6 | # Copyright 2018 Mark Ganson mwganson at gmail 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | # MA 02110-1301, USA. 22 | # 23 | # 24 | ################################################################################### 25 | -------------------------------------------------------------------------------- /Mod_Asm4/Init.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ################################################################################### 3 | # 4 | # Init.py 5 | # 6 | # Copyright 2018 Mark Ganson 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | # MA 02110-1301, USA. 22 | # 23 | # 24 | ################################################################################### 25 | 26 | print("Loading Assembly 4 WorkBench") 27 | -------------------------------------------------------------------------------- /Mod_Asm4/newBodyCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # newBodyCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | 16 | class newBody: 17 | "My tool object" 18 | 19 | def GetResources(self): 20 | return {"MenuText": "Create a new Body", 21 | "Accel": "Ctrl+B", 22 | "ToolTip": "Create a new Body in the Model", 23 | "Pixmap" : os.path.join( iconPath , 'PartDesign_Body.svg') 24 | } 25 | 26 | def IsActive(self): 27 | if App.ActiveDocument: 28 | # is something selected ? 29 | if Gui.Selection.getSelection(): 30 | return(False) 31 | else: 32 | return(True) 33 | else: 34 | return(False) 35 | 36 | def Activated(self): 37 | # do something here... 38 | bodyName = 'Body' 39 | text,ok = QtGui.QInputDialog.getText(None,'Create new Body in Model','Enter new Body name : ', text = bodyName) 40 | if ok and text: 41 | App.activeDocument().getObject('Model').newObject( 'PartDesign::Body', text ) 42 | 43 | 44 | # add the command to the workbench 45 | Gui.addCommand( 'newBodyCmd', newBody() ) 46 | -------------------------------------------------------------------------------- /Mod_Asm4/newModelCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # newModelCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | 16 | class newModel: 17 | "My tool object" 18 | 19 | def GetResources(self): 20 | return {"MenuText": "Create a new Model", 21 | "Accel": "Ctrl+M", 22 | "ToolTip": "Create a new Model App::Part", 23 | "Pixmap" : os.path.join( iconPath , 'Model.svg') 24 | } 25 | 26 | def IsActive(self): 27 | if App.ActiveDocument: 28 | # is something selected ? 29 | if Gui.Selection.getSelection(): 30 | return(False) 31 | else: 32 | return(True) 33 | else: 34 | return(False) 35 | 36 | def Activated(self): 37 | # create a new App::Part called 'Model' 38 | App.activeDocument().Tip = App.activeDocument().addObject('App::Part','Model') 39 | App.activeDocument().getObject('Model').newObject('App::DocumentObjectGroup','Constraints') 40 | App.activeDocument().getObject('Model').newObject('PartDesign::CoordinateSystem','LCS_0') 41 | 42 | 43 | # add the command to the workbench 44 | Gui.addCommand( 'newModelCmd', newModel() ) 45 | -------------------------------------------------------------------------------- /Mod_Asm4/newSketchCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # newSketchCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | 16 | class newSketch: 17 | "My tool object" 18 | 19 | def GetResources(self): 20 | return {"MenuText": "Create a new Sketch", 21 | "ToolTip": "Create a new Sketch in the Model", 22 | "Pixmap" : os.path.join( iconPath , 'Model_NewSketch.svg') 23 | } 24 | 25 | def IsActive(self): 26 | if App.ActiveDocument: 27 | # is something selected ? 28 | if Gui.Selection.getSelection(): 29 | return(False) 30 | else: 31 | return(True) 32 | else: 33 | return(False) 34 | 35 | def Activated(self): 36 | # input dialog to ask the user the name of the Sketch: 37 | sketchName = 'Sketch_1' 38 | text,ok = QtGui.QInputDialog.getText(None,'Create new Sketch in Model','Enter Sketch name : ', text = sketchName) 39 | if ok and text: 40 | App.activeDocument().getObject('Model').newObject( 'Sketcher::SketchObject', text ) 41 | 42 | 43 | # add the command to the workbench 44 | Gui.addCommand( 'newSketchCmd', newSketch() ) 45 | 46 | -------------------------------------------------------------------------------- /Mod_Asm4/newPointCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # newPointCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | 16 | class newPoint: 17 | "My tool object" 18 | 19 | def GetResources(self): 20 | return {"MenuText": "Create a new Point", 21 | "ToolTip": "Create a new Datum Point in the Model", 22 | "Pixmap" : os.path.join( iconPath , 'Point.svg') 23 | } 24 | 25 | def IsActive(self): 26 | if App.ActiveDocument: 27 | # is something selected ? 28 | if Gui.Selection.getSelection(): 29 | return(False) 30 | else: 31 | return(True) 32 | else: 33 | return(False) 34 | 35 | def Activated(self): 36 | # do something here... 37 | # input dialog to ask the user the name of the LCS: 38 | pointName = 'Point_1' 39 | text,ok = QtGui.QInputDialog.getText(None,'Create new Datum Point','Enter Datum Point name : ', text = pointName) 40 | # if everything went well: 41 | if ok and text: 42 | App.activeDocument().getObject('Model').newObject( 'PartDesign::Point', text ) 43 | 44 | 45 | # add the command to the workbench 46 | Gui.addCommand( 'newPointCmd', newPoint() ) 47 | -------------------------------------------------------------------------------- /Mod_Asm4/newLCSCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # newLCSCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | 16 | class newLCS: 17 | "My tool object" 18 | 19 | def GetResources(self): 20 | return {"MenuText": "Create a new LCS", 21 | "ToolTip": "Create a new Coordinate System in the Model", 22 | "Pixmap" : os.path.join( iconPath , 'AxisCross.svg') 23 | } 24 | 25 | def IsActive(self): 26 | if App.ActiveDocument: 27 | # is something selected ? 28 | if Gui.Selection.getSelection(): 29 | return(False) 30 | else: 31 | return(True) 32 | else: 33 | return(False) 34 | 35 | def Activated(self): 36 | # do something here... 37 | # input dialog to ask the user the name of the LCS: 38 | lcsName = 'LCS_1' 39 | text,ok = QtGui.QInputDialog.getText(None,'Create new coordinate system','Enter Local Coordinate System name : ', text = lcsName) 40 | # if everything went well: 41 | if ok and text: 42 | App.activeDocument().getObject('Model').newObject( 'PartDesign::CoordinateSystem', text ) 43 | 44 | 45 | # add the command to the workbench 46 | Gui.addCommand( 'newLCSCmd', newLCS() ) 47 | -------------------------------------------------------------------------------- /Mod_Asm4/updateAssemblyCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # newModelCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | 16 | class updateAssembly: 17 | "My tool object" 18 | 19 | def GetResources(self): 20 | return {"MenuText": "Solve and Update Assembly", 21 | "ToolTip": "Solve all constraints and update Assembly", 22 | "Pixmap" : os.path.join( iconPath , 'Solver.svg') 23 | } 24 | 25 | def IsActive(self): 26 | if App.ActiveDocument: 27 | return(True) 28 | else: 29 | return(False) 30 | 31 | 32 | 33 | """ 34 | +-----------------------------------------------+ 35 | | the real stuff | 36 | +-----------------------------------------------+ 37 | """ 38 | def Activated(self): 39 | 40 | # get the current active document to avoid errors if user changes tab 41 | self.activeDoc = App.activeDocument() 42 | 43 | # find all the linked parts in the assembly... 44 | for obj in self.activeDoc.findObjects(): 45 | # ... and update it 46 | obj.recompute() 47 | # finally uodate the parent assembly 48 | self.activeDoc.Model.recompute() 49 | 50 | 51 | # add the command to the workbench 52 | Gui.addCommand( 'updateAssemblyCmd', updateAssembly() ) 53 | -------------------------------------------------------------------------------- /Mod_Asm4/InitGui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ################################################################################### 3 | # 4 | # InitGui.py 5 | # 6 | # Copyright 2018 Mark Ganson mwganson at gmail 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | # MA 02110-1301, USA. 22 | # 23 | # 24 | ################################################################################### 25 | 26 | import asm4wb_locator 27 | asm4wbPath = os.path.dirname( asm4wb_locator.__file__ ) 28 | asm4wb_icons_path = os.path.join( asm4wbPath, 'icons') 29 | 30 | global main_Assembly4WB_Icon 31 | main_Assembly4WB_Icon = os.path.join( asm4wb_icons_path , 'Assembly4.svg' ) 32 | 33 | 34 | #def myFunc(string): 35 | # print (string) 36 | # global act 37 | # act.setVisible(True) 38 | 39 | #mw=Gui.getMainWindow() 40 | #bar=mw.menuBar() 41 | #act=bar.addAction("MyCmd") 42 | #mw.workbenchActivated.connect(myFunc) 43 | 44 | 45 | 46 | """ 47 | +-----------------------------------------------+ 48 | | Initialize the workbench | 49 | +-----------------------------------------------+ 50 | """ 51 | class Assembly4_WorkBench(Workbench): 52 | 53 | global main_Assembly4WB_Icon 54 | 55 | MenuText = "Assembly 4" 56 | ToolTip = "Assembly 4 workbench" 57 | Icon = main_Assembly4WB_Icon 58 | 59 | 60 | def __init__(self): 61 | "This function is executed when FreeCAD starts" 62 | pass 63 | 64 | 65 | def Initialize(self): 66 | import newModelCmd # creates a new App::Part container called 'Model' 67 | import newSketchCmd # creates a new Sketch in 'Model' 68 | import newLCSCmd # creates a new LCS in 'Model' 69 | import newPointCmd # creates a new LCS in 'Model' 70 | import newBodyCmd # creates a new Body in 'Model 71 | import insertLinkCmd # inserts an App::Link to a 'Model' in another file 72 | import placeLinkCmd # places a linked part by snapping LCS (in the Part and in the Assembly) 73 | import placeDatumCmd # places an LCS relative to an external file (creates a local attached copy) 74 | import importDatumCmd # creates an LCS in assembly and attaches it to an LCS relative to an external file 75 | import updateAssemblyCmd # updates all parts and constraints in the assembly 76 | self.listCmd = [ "newModelCmd", "insertLinkCmd", "placeLinkCmd", "newLCSCmd", "importDatumCmd", "placeDatumCmd", "newSketchCmd", "newPointCmd", "newBodyCmd", "updateAssemblyCmd" ] # A list of command names created in the line above 77 | self.itemsMenu = [ "newModelCmd", "insertLinkCmd", "placeLinkCmd", "newLCSCmd", "importDatumCmd", "placeDatumCmd", "newSketchCmd", "newPointCmd", "newBodyCmd", "updateAssemblyCmd" ] # A list of command names created in the line above 78 | self.itemsToolbar = [ "newModelCmd", "insertLinkCmd", "placeLinkCmd", "newLCSCmd", "importDatumCmd", "placeDatumCmd", "newSketchCmd", "newPointCmd", "newBodyCmd", "updateAssemblyCmd" ] # A list of command names created in the line above 79 | self.itemsContextMenu = [ "placeLinkCmd", "placeDatumCmd" ] # A list of command names created in the line above 80 | self.appendToolbar("Assembly 4",self.itemsToolbar) # leave settings off toolbar 81 | self.appendMenu("&Assembly",self.itemsMenu) # creates a new menu 82 | #self.appendMenu(["&Edit","DynamicData"],self.list) # appends a submenu to an existing menu 83 | 84 | 85 | """ 86 | +-----------------------------------------------+ 87 | | Standard necessary functions | 88 | +-----------------------------------------------+ 89 | """ 90 | def Activated(self): 91 | "This function is executed when the workbench is activated" 92 | return 93 | 94 | 95 | def Deactivated(self): 96 | "This function is executed when the workbench is deactivated" 97 | return 98 | 99 | 100 | def ContextMenu(self, recipient): 101 | "This is executed whenever the user right-clicks on screen" 102 | # "recipient" will be either "view" or "tree" 103 | self.appendContextMenu( "Assembly", self.itemsContextMenu ) # add commands to the context menu 104 | 105 | 106 | def GetClassName(self): 107 | # this function is mandatory if this is a full python workbench 108 | return "Gui::PythonWorkbench" 109 | 110 | 111 | 112 | """ 113 | +-----------------------------------------------+ 114 | | actually make the workbench | 115 | +-----------------------------------------------+ 116 | """ 117 | wb = Assembly4_WorkBench() 118 | Gui.addWorkbench(wb) 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /Mod_Asm4/libAsm4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # libraries for FreeCAD's Assembly Without Solver 5 | 6 | 7 | """ 8 | +-----------------------------------------------+ 9 | | shouldn't these be DEFINE's ? | 10 | +-----------------------------------------------+ 11 | """ 12 | constraintPrefix = 'constr_' 13 | 14 | import os 15 | __dir__ = os.path.dirname(__file__) 16 | iconPath = os.path.join( __dir__, 'icons' ) 17 | 18 | 19 | """ 20 | +-----------------------------------------------+ 21 | | populate the ExpressionEngine | 22 | | for a linked App::Part | 23 | +-----------------------------------------------+ 24 | """ 25 | def makeExpressionPart( attLink, attPart, attLCS, constrName, linkedPart, linkLCS ): 26 | # if everything is defined 27 | if attLink and attLCS and constrName and linkedPart and linkLCS: 28 | # this is where all the magic is, see: 29 | # 30 | # https://forum.freecadweb.org/viewtopic.php?p=278124#p278124 31 | # 32 | # as of FreeCAD v0.19 the syntax is different: 33 | # https://forum.freecadweb.org/viewtopic.php?f=17&t=38974&p=337784#p337784 34 | # expr = ParentLink.Placement * ParentPart#LCS.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1 35 | # expr = LCS_in_the_assembly.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1 36 | expr = attLCS+'.Placement * '+constrName+'.AttachmentOffset * '+linkedPart+'#'+linkLCS+'.Placement ^ -1' 37 | # if we're attached to another sister part (and not the Parent Assembly) 38 | # we need to take into account the Placement of that Part. 39 | if attPart: 40 | expr = attLink+'.Placement * '+attPart+'#'+expr 41 | else: 42 | expr = False 43 | return expr 44 | 45 | 46 | 47 | 48 | """ 49 | +-----------------------------------------------+ 50 | | split the ExpressionEngine of a linked part | 51 | | to find the old attachment LCS | 52 | | (in the parent assembly or a sister part) | 53 | | and the old target LCS in the linked Part | 54 | +-----------------------------------------------+ 55 | """ 56 | def splitExpressionPart( expr, parent ): 57 | # expr = ParentLink.Placement * ParentPart#LCS.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1' 58 | bad_EE = ( '', 'None', 'None', 'None', 'None', 'None') 59 | if not expr: 60 | return ( 'Empty expression x1', 'None', 'None', 'None', 'None', 'None') 61 | if parent == 'Parent Assembly': 62 | # we're attached to an LCS in the parent assembly 63 | # expr = LCS_in_the_assembly.Placement * constr_Name.AttachmentOffset * LinkedPart#LCS.Placement ^ -1' 64 | ( attLCS, separator, rest1 ) = expr.partition('.Placement * ') 65 | ( constrName, separator, rest2 ) = rest1.partition('.AttachmentOffset * ') 66 | ( linkedPart, separator, rest3 ) = rest2.partition('#') 67 | ( linkLCS, separator, rest4 ) = rest3.partition('.Placement ^ ') 68 | restFinal = rest4[0:2] 69 | attLink = parent 70 | attPart = 'None' 71 | #return ( restFinal, 'None', 'None', 'None', 'None', 'None') 72 | else: 73 | # we're attached to an LCS in a sister part 74 | # expr = ParentLink.Placement * ParentPart#LCS.Placement * constr_Name.AttachmentOffset * LinkedPart#LCS.Placement ^ -1' 75 | ( attLink, separator, rest1 ) = expr.partition('.Placement * ') 76 | ( attPart, separator, rest2 ) = rest1.partition('#') 77 | ( attLCS, separator, rest3 ) = rest2.partition('.Placement * ') 78 | ( constrName, separator, rest4 ) = rest3.partition('.AttachmentOffset * ') 79 | ( linkedPart, separator, rest5 ) = rest4.partition('#') 80 | ( linkLCS, separator, rest6 ) = rest5.partition('.Placement ^ ') 81 | restFinal = rest6[0:2] 82 | #return ( restFinal, 'None', 'None', 'None', 'None', 'None') 83 | if restFinal=='-1': 84 | # wow, everything went according to plan 85 | # retval = ( expr, attPart, attLCS, constrLink, partLCS ) 86 | retval = ( attLink, attPart, attLCS, constrName, linkedPart, linkLCS) 87 | else: 88 | # rats ! Didn't succeed in decoding the ExpressionEngine. 89 | # But still, if the decode is unsuccessful, put some text 90 | retval = bad_EE 91 | return retval 92 | 93 | 94 | 95 | """ 96 | +-----------------------------------------------+ 97 | | populate the ExpressionEngine | 98 | | for a Datum object | 99 | | linked to an LCS in a sister part | 100 | +-----------------------------------------------+ 101 | """ 102 | def makeExpressionDatum( attLink, attPart, attLCS ): 103 | # check that everything is defined 104 | if attLink and attPart and attLCS: 105 | # expr = Link.Placement * LinkedPart#LCS.Placement 106 | expr = attLink +'.Placement * '+ attPart +'#'+ attLCS +'.Placement' 107 | else: 108 | expr = False 109 | return expr 110 | 111 | 112 | 113 | """ 114 | +-----------------------------------------------+ 115 | | split the ExpressionEngine | 116 | | of a linked Datum object to find | 117 | | the old attachment Part and LCS | 118 | +-----------------------------------------------+ 119 | """ 120 | def splitExpressionDatum( expr ): 121 | # expr = Link.Placement * LinkedPart#LCS.Placement 122 | ( attLink, separator, rest1 ) = expr.partition('.Placement * ') 123 | ( attPart, separator, rest2 ) = rest1.partition('#') 124 | ( attLCS, separator, rest3 ) = rest2.partition('.') 125 | restFinal = rest3[0:9] 126 | if restFinal=='Placement': 127 | # wow, everything went according to plan 128 | retval = ( attLink, attPart, attLCS ) 129 | #self.expression.setText( attPart +'***'+ attLCS ) 130 | else: 131 | # rats ! But still, if the decode is unsuccessful, put some text 132 | retval = ( restFinal, 'None', 'None' ) 133 | return retval 134 | 135 | 136 | -------------------------------------------------------------------------------- /Mod_Asm4/insertLinkCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # insertLinkCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | """ 16 | +-----------------------------------------------+ 17 | | main class | 18 | +-----------------------------------------------+ 19 | """ 20 | class insertLink( QtGui.QDialog ): 21 | "My tool object" 22 | 23 | def __init__(self): 24 | super(insertLink,self).__init__() 25 | 26 | 27 | def GetResources(self): 28 | return {"MenuText": "Insert an external Part", 29 | "Accel": "Ctrl+L", 30 | "ToolTip": "Insert an external Part from another open document", 31 | "Pixmap" : os.path.join( iconPath , 'LinkModel.svg') 32 | } 33 | 34 | 35 | def IsActive(self): 36 | if App.ActiveDocument: 37 | # is something selected ? 38 | if Gui.Selection.getSelection(): 39 | return False 40 | else: 41 | return True 42 | else: 43 | return(False) 44 | 45 | 46 | def Activated(self): 47 | # This function is executed when the command is activated 48 | 49 | # get the current active document to avoid errors if user changes tab 50 | self.activeDoc = App.activeDocument() 51 | 52 | # the GUI objects are defined later down 53 | self.drawUI() 54 | 55 | # Search for all App::Parts in all open documents 56 | self.getAllParts() 57 | 58 | # build the list 59 | for part in self.allParts: 60 | newItem = QtGui.QListWidgetItem() 61 | newItem.setText( part.Document.Name +" -> "+ part.Name ) 62 | newItem.setIcon(part.ViewObject.Icon) 63 | self.partList.addItem(newItem) 64 | 65 | # show the UI 66 | self.show() 67 | 68 | 69 | 70 | """ 71 | +-----------------------------------------------+ 72 | | the real stuff happens here | 73 | +-----------------------------------------------+ 74 | """ 75 | def onCreateLink(self): 76 | # parse the selected items 77 | # TODO : there should only be 1 78 | model = [] 79 | for selected in self.partList.selectedIndexes(): 80 | # get the selected part 81 | model = self.allParts[ selected.row() ] 82 | # get the name of the link (as it should appear in the tree) 83 | linkName = self.linkNameInput.text() 84 | # only create link if there is a Part object and a name 85 | if model and linkName: 86 | # create the App::Link with the user-provided name 87 | createdLink = self.activeDoc.getObject('Model').newObject( 'App::Link', linkName ) 88 | # assigne the user-selected model to it 89 | createdLink.LinkedObject = model 90 | # update the link 91 | createdLink.recompute() 92 | 93 | # close the dialog UI... 94 | self.close() 95 | 96 | # ... and launch the placement of the inserted part 97 | Gui.Selection.clearSelection() 98 | Gui.Selection.addSelection( self.activeDoc.Name, 'Model', createdLink.Name+'.' ) 99 | Gui.runCommand( 'placeLinkCmd' ) 100 | 101 | # if still open, close the dialog UI 102 | self.close() 103 | 104 | 105 | 106 | """ 107 | +-----------------------------------------------+ 108 | | some functions | 109 | +-----------------------------------------------+ 110 | """ 111 | def getAllParts(self): 112 | # get all App::Part from all open documents 113 | self.allParts = [] 114 | for doc in App.listDocuments().values(): 115 | # except this document: we don't want to link to itself 116 | if doc != self.activeDoc: 117 | parts = doc.findObjects("App::Part") 118 | # there might be more than 1 App::Part per document 119 | for obj in parts: 120 | self.allParts.append( obj ) 121 | 122 | 123 | def onItemClicked( self, item ): 124 | for selected in self.partList.selectedIndexes(): 125 | # get the selected part 126 | model = self.allParts[ selected.row() ] 127 | # set the text of the link to be made to the document where the part is in 128 | self.linkNameInput.setText(model.Document.Name) 129 | 130 | 131 | def onCancel(self): 132 | self.close() 133 | 134 | 135 | 136 | """ 137 | +-----------------------------------------------+ 138 | | defines the UI, only static elements | 139 | +-----------------------------------------------+ 140 | """ 141 | def drawUI(self): 142 | 143 | # Our main window is a QDialog 144 | self.setModal(False) 145 | # make this dialog stay above the others, always visible 146 | self.setWindowFlags( QtCore.Qt.WindowStaysOnTopHint ) 147 | self.setWindowTitle('Insert a Model') 148 | self.setWindowIcon( QtGui.QIcon( os.path.join( iconPath , 'FreeCad.svg' ) ) ) 149 | self.setMinimumSize(400, 500) 150 | self.resize(400,500) 151 | #self.Layout.addWidget(self.GUIwindow) 152 | 153 | # label 154 | self.labelMain = QtGui.QLabel(self) 155 | self.labelMain.setText("Select Part to be inserted :") 156 | self.labelMain.move(10,20) 157 | #self.Layout.addWidget(self.labelMain) 158 | 159 | # label 160 | self.labelLink = QtGui.QLabel(self) 161 | self.labelLink.setText("Enter a Name for the link :\n(Must be unique in the Model tree)") 162 | self.labelLink.move(10,350) 163 | 164 | # Create a line that will contain the name of the link (in the tree) 165 | self.linkNameInput = QtGui.QLineEdit(self) 166 | self.linkNameInput.setMinimumSize(380, 0) 167 | self.linkNameInput.move(10, 400) 168 | 169 | # The part list is a QListWidget 170 | self.partList = QtGui.QListWidget(self) 171 | self.partList.move(10,50) 172 | self.partList.setMinimumSize(380, 280) 173 | 174 | # Cancel button 175 | self.CancelButton = QtGui.QPushButton('Cancel', self) 176 | self.CancelButton.setAutoDefault(False) 177 | self.CancelButton.move(10, 460) 178 | 179 | # create Link button 180 | self.createLinkButton = QtGui.QPushButton('Insert part', self) 181 | self.createLinkButton.move(285, 460) 182 | self.createLinkButton.setDefault(True) 183 | 184 | # Actions 185 | self.CancelButton.clicked.connect(self.onCancel) 186 | self.createLinkButton.clicked.connect(self.onCreateLink) 187 | self.partList.itemClicked.connect( self.onItemClicked) 188 | 189 | 190 | """ 191 | +-----------------------------------------------+ 192 | | add the command to the workbench | 193 | +-----------------------------------------------+ 194 | """ 195 | Gui.addCommand( 'insertLinkCmd', insertLink() ) 196 | 197 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/PartDesign_Point.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | image/svg+xml 44 | 45 | 46 | 47 | 48 | [jrheinlaender] 49 | 50 | 51 | PartDesign_Point 52 | 2013-05-22 53 | http://www.freecadweb.org/wiki/index.php?title=Artwork 54 | 55 | 56 | FreeCAD 57 | 58 | 59 | FreeCAD/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Point.svg 60 | 61 | 62 | FreeCAD LGPL2+ 63 | 64 | 65 | https://www.gnu.org/copyleft/lesser.html 66 | 67 | 68 | [agryson] Alexander Gryson 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/New_Axis.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 26 | 29 | 33 | 37 | 38 | 40 | 44 | 48 | 49 | 59 | 66 | 77 | 86 | 89 | 93 | 97 | 98 | 99 | 121 | 128 | 129 | 131 | 132 | 134 | image/svg+xml 135 | 137 | 138 | 139 | 140 | [wmayer] 141 | 142 | 143 | Sketcher_Sketch 144 | 2011-10-10 145 | http://www.freecadweb.org/wiki/index.php?title=Artwork 146 | 147 | 148 | FreeCAD 149 | 150 | 151 | FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_Sketch.svg 152 | 153 | 154 | FreeCAD LGPL2+ 155 | 156 | 157 | https://www.gnu.org/copyleft/lesser.html 158 | 159 | 160 | [agryson] Alexander Gryson 161 | 162 | 163 | 164 | 165 | 166 | 170 | 176 | 182 | 190 | 199 | 202 | 212 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/AxisCross.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 38 | 45 | 46 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | Path-Heights 58 | 2016-05-15 59 | http://www.freecadweb.org/wiki/index.php?title=Artwork 60 | 61 | 62 | FreeCAD 63 | 64 | 65 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Heights.svg 66 | 67 | 68 | FreeCAD LGPL2+ 69 | 70 | 71 | https://www.gnu.org/copyleft/lesser.html 72 | 73 | 74 | [agryson] Alexander Gryson 75 | 76 | 77 | 78 | 79 | 80 | 85 | 91 | 94 | 100 | 106 | 111 | 117 | 123 | 129 | 130 | 131 | 134 | 140 | 146 | 152 | 158 | 164 | 165 | 170 | 176 | 182 | 188 | 194 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/Point.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 26 | 29 | 33 | 37 | 38 | 40 | 44 | 48 | 49 | 59 | 66 | 77 | 86 | 89 | 93 | 97 | 98 | 100 | 104 | 108 | 109 | 110 | 132 | 139 | 140 | 142 | 143 | 145 | image/svg+xml 146 | 148 | 149 | 150 | 151 | [wmayer] 152 | 153 | 154 | Sketcher_Sketch 155 | 2011-10-10 156 | http://www.freecadweb.org/wiki/index.php?title=Artwork 157 | 158 | 159 | FreeCAD 160 | 161 | 162 | FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_Sketch.svg 163 | 164 | 165 | FreeCAD LGPL2+ 166 | 167 | 168 | https://www.gnu.org/copyleft/lesser.html 169 | 170 | 171 | [agryson] Alexander Gryson 172 | 173 | 174 | 175 | 176 | 177 | 181 | 187 | 193 | 203 | 212 | 222 | 231 | 241 | 250 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/CoordinateSystem.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 30 | 36 | 37 | 45 | 51 | 52 | 60 | 66 | 67 | 78 | 80 | 84 | 88 | 89 | 90 | 114 | 121 | 122 | 124 | 125 | 127 | image/svg+xml 128 | 130 | 131 | 132 | 133 | 134 | 139 | 141 | 145 | 149 | 156 | 157 | 158 | 164 | 165 | 170 | 174 | 178 | 185 | 186 | 187 | 193 | 194 | 197 | 201 | 205 | 212 | 213 | 214 | 220 | 221 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeCAD Assembly Without Solver / Assembly 4 2 | FreeCAD add-on for a bare-bone assembly structure, using App::Link 3 | 4 | Welcome to the FreeCAD_aws wiki! This page tries to explain how to make assemblies using these tools. This project the result of [discussions on the FreeCAD forum](https://forum.freecadweb.org/viewtopic.php?f=20&t=32843). 5 | 6 | ## Installation 7 | 8 | Download and extract the archive, and then move (or link) the sub-directory `Mod_Asm4` (which contains all the actual code) into the `~/.FreeCDA/Mod` folder, containing all additional modules. 9 | 10 | Please bear in mind that Assembly 4 is not compatible with the stable FreeCAD v0.18, it needs at least v0.19. You can find [pre-built binaries here](https://github.com/FreeCAD/FreeCAD/releases/tag/0.19_pre) 11 | 12 | You can use the [example assemblies](https://github.com/Zolko-123/FreeCAD_Assembly4/tree/master/Examples) to experiment with this workbench's features. Open one _asm_something.fcstd_ file and try out the functions. There are ReadMe.txt files in each durectory with some explanations. 13 | 14 | 15 | ## Principle 16 | 17 | The point is that each part is a mostly standard FreeCAD `App::Part` object, and these are assembled using the `App::Link` framework found in the fork of FreeCAD (https://github.com/realthunder/FreeCAD/tree/LinkStage3, pre-built binaries on the [realthunder's FreeCAD_assembly3 release page](https://github.com/realthunder/FreeCAD_assembly3/releases) 18 | 19 | The particularities of the `App::Part` used here are the following: 20 | 21 | * they are called 'Model' at creation time 22 | * they contain a group called 'Constraints' at the root 23 | * they contain a Datum Coordinate System called LCS_0 at the root 24 | 25 | The purpose of this is to avoid burdensome error checking: it is supposed that no other usecase would create an `App::Part` called 'Model', _i.e._ that if an `App::Part` is called 'Model' it will conform to these characteristics. 26 | 27 | Any Model can contain (by `App::Link`) any other Model, and they are placed to each-other by matchings their Datum Coordinate Systems (`PartDesign::CoordinateSystem`, called here-after LCS (Local Coordinate System)). There is no need for any geometry to be present to place and constrain parts relative to each other. LCS are used because they are both mechanical objects, since they fix all 6 degrees of freedom in an isostatic way, and mathematical objects that can be easily manipulated by rigorous methods (mainly combination and inversion). 28 | 29 | To actually include some geometry, a body needs to be created, and designed using the PartDesign workbench. To be linked with the previously created model, this body needs to be inside the `App::Part container` called 'Model'. 30 | 31 | The result is the following: 32 | ![](Resources/media/Asm4_wb0.png) 33 | 34 | * the part _Bielle_ is placed in the assembly by attaching it's _LCS_0_ to the _LCS_0_ of the parent assembly. 35 | * the part _Cuve_ is placed in the assembly by placing its _LCS_0_ on the _LCS_1_ of the part _Bielle_ 36 | * the part _Bague_ is placed in the assembly by placing its _LCS_0_ on the _LCS_0_ of the part _Bielle_ 37 | * the parts _Screw_CHC_1_ and _Screw_CHC_2_ are placed in the assembly by placing their _LCS_0_ on the _LCS_1_ and _LCS_2_ of the part _Cuve_ 38 | 39 | 40 | ## ExpressionEngine 41 | 42 | Assembly4 uses a special and very useful feature of FreeCAD: the ExpressionEngine. Some parameters can be entered through mathematical furmalae, that are evaluated by this ExpressionEngine. For Assembly4, it's the parameter _`Placement`_ of the inserted _`App::Link`_ object that is calculated, such that 2 LCS - one in the linked part and the one in the assembly - are superimposed. 43 | 44 | In normal use, the ExpressionEngine of an _`App::Link`_ object is hidden, it must be shown as in the following screenshot: 45 | 46 | ![](Resources/media/asm_EE.png) 47 | 48 | 49 | The syntax of the ExpressionEngine is the following: 50 | 51 | 52 | * **If the LCS belongs to the parent assembly:** 53 | 54 | `LCS_parent.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS_link.Placement ^ -1` 55 | 56 | * **If the LCS belongs to a sister part:** 57 | 58 | `ParentLink.Placement * ParentPart#LCS_parent.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1` 59 | 60 | * _ParentLink_ is the name of the App::Link of the sister part in the assembly 61 | * _ParentPart_ is the name of the App::Part that the previous ParentLink refers-to 62 | * _LCS_parent_ is the LCS in the parent part (can be either the assembly itself or a sister part in the assembly) 63 | * _constr_LinkName_ is a FeaturePython object with a conventional name 64 | * _LinkedPart_ is the App::Part's name that the inserted App::Link refers-to 65 | * _LCS_link_ is the LCS in the linked part 66 | 67 | **Constraints**: 68 | 69 | To each part inserted into an assembly is associated an `App::FeaturePython` object, placed in the 'Constraints' group. This object contains information about the placement of the linked object in the assembly. It also contains an `App::Placement`, called 'AttachmentOffset', which introduces an offset between the LCS in the part and the LCS in the assembly. The main purpose of this offset is to correct bad orientations between the 2 matching LCS. 70 | 71 | 72 | These constraints are not really constraints in the traditional CAD sense, but since `App::FeaturePython` objects are very versatile, they could be expanded to contain real constraints in some (distant) future. 73 | 74 | ![](Resources/media/Asm4_wb1.png) 75 | 76 | _Taking a closer look at the fields contained in an `App::FeaturePython` object associated with the part 'Bague'. The small button under the cursor opens the dialog that allows to edit the parameters of the Attachment Offset_ 77 | 78 | ![](Resources/media/Asm4_wb2.png) 79 | 80 | _Dialog that opens when clicking the previous small button, and permitting to edit the parameters of the_ `App::Placement` _called_ 'AttachmentOffset' _in the constraint associated with a link, and allowing relative placement of the link -vs- the attachment LCS_ 81 | 82 | ## Workflow 83 | 84 | The toolbar for the Assembly4 workbench holds the following buttons: 85 | 86 | 87 | ![](Resources/media/Toolbar.png) 88 | 89 | 90 | * New Model 91 | * Insert link 92 | * Place linked Part 93 | * New LCS in the Model 94 | * Import Datum object 95 | * Place Datum object 96 | * New Sketch in the Model 97 | * New Datum Point in the Model 98 | * New Body in the Model 99 | 100 | 101 | 102 | ### Part 103 | 104 | The basic workflow for creating a part is the following: 105 | 106 | * create a new document, create a new 'Model' with the macro 'new_Model'. This will create a new App::Part called 'Model' with the default structure 107 | * create or import geometries in bodies (`PartDesign::Body`) 108 | * create LCS with the macro 'new_ModelLCS', and place them wherever you feel they're useful. Use the MapMode to attach the LCS 109 | * save part (only saved parts can be used currently). If necessary, close and re-open the file 110 | * repeat 111 | 112 | ### Assembly 113 | 114 | The basic workflow for creating a part is the following: 115 | 116 | * create a new document, create a new 'Model' 117 | * create a new sketch with the 'new_ModelSketch' macro. Attach it to whatever is useful, and draw the skeleton of the assembly, placing vertices and lines where useful 118 | * create new LCS (with the new_ModelLCS macro) and place them on the correct vertices of the sketch (using MapMode) 119 | * save document 120 | * import a part using the 'link_Model' macro. It will place your part with its LCS_0 to the LCS_0 of the assembly. You should give it a recognisable name. 121 | * select the link in the tree, and execute the macro 'place_Link'. Select the LCS of the part that you want to use as attachment point, select the part that you want it to be attached, and select the LCS in that part where you want the part to be attached. Click 'Apply'. 122 | * if the placement corresponds to your needs, click 'Ok', else select other LCS / Part until satisfied 123 | * if the part is correctly paced but badly oriented, select the corresponding constraint in the 'Constraints' folder, select the property 'Offset', and modify its parameters. Click 'Apply'. Repeat until satisfied 124 | 125 | ### Nested assemblies 126 | 127 | The previous method allows to assemble parts within a single level. 128 | But this workbench also allows the assembly of assemblies: since there is no difference between 129 | parts and assemblies, the 'Insert External Part' allows to chose a part that has other parts linked to it. 130 | The only difference will be for the coordinate systems in the inserted assemblies: in order to be used 131 | with Assembly 4, a coordinate system must be directly in the root 'Model' container, meaning that a 132 | coordinate system inside a linked part cannot be used to attach the assembly to a higher-level assembly. 133 | 134 | Therefore, in order to re-use a coordinate system of a part in an assembly, a coordinate system must be created at the root of the 'Model', and the placement of this coordinate system must be 'copied' over from the coordinate system that the user wants to use. This is done by inserting a coordinate system and using the 'Place LCS' command, which allows to select a linked part in the assembly and one of it's coordinate systems: the 2 coordinate systems — the one at the root of 'Model' and the one in the linked part — will always be superimposed, even if the linked part is modified, allowing the placement of the assembly in a higher level assembly using a linked part as reference. It sounds more complicated than it actually is. 135 | 136 | ![](Resources/media/Asm4_V4.gif) 137 | 138 | ![](Resources/media/Lego_House+Garden.png) 139 | 140 | 141 | 142 | #### Release notes 143 | 144 | 145 | 146 | #### Release notes 147 | 148 | * 2019.10.05 (**version 0.6**) : 149 | Ported to FreeCAD-v0.19-pre, with new syntax for the ExpressionEngine 150 | 151 | * 2019.07.23 (**version 0.5.5**) : 152 | Fixed a bug in partLCSlist.findItems 153 | 154 | * 2019.07.18 (**version 0.5.4**) : 155 | A cosmetic update to fix a 25 year old Windows bug: 156 | some UTF-8 characters in the comments were not accepted on some Windows 10 machines 157 | 158 | * 2019.06.15 (**version 0.5.3**) : 159 | Now the LCS can be renamed, and they show up in the LCS list in the command placeLink as such. 160 | It's only visual, the ExpressionEngine still uses the LCS.Name though 161 | 162 | * 2019.05.07 (**version 0.5.2**) : 163 | added insertDatumCmd 164 | 165 | * 2019.03.18 (**version 0.5.1**) : 166 | Part can now be linked without being placed: this is then a raw interface with App::Link 167 | The instance can be moved manually with the 'Transform' dragger 168 | 169 | * 2019.03.12 (**version 0.5**) : 170 | moved the actual code to Mod_Asm4 171 | 172 | * 2019.03.11 (**version 0.4.1**) : 173 | Added placement of Datum Point 174 | 175 | * 2019.03.09 (**version 0.4**) : 176 | FreeCAD now imports as App 177 | insert_Link launches place_Link 178 | 179 | * 2019.03.05 (**version 0.3.1**) : 180 | added the RotX-Y-Z buttons 181 | 182 | * 2019.02.20 (**version 0.3**) 183 | mostly working version 184 | 185 | * 2019.02.18 (**version 0.1**) : 186 | initial release of Assembly 4 WB 187 | 188 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/FreeCad.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 26 | 28 | 32 | 36 | 37 | 39 | 43 | 47 | 48 | 55 | 57 | 61 | 65 | 66 | 68 | 72 | 76 | 77 | 84 | 95 | 97 | 101 | 105 | 106 | 115 | 125 | 135 | 137 | 141 | 145 | 146 | 156 | 158 | 162 | 166 | 167 | 168 | 189 | 196 | 197 | 199 | 200 | 202 | image/svg+xml 203 | 205 | 206 | 207 | 208 | 209 | 213 | 217 | 220 | 225 | 231 | 241 | 247 | 253 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/Model_NewSketch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 26 | 29 | 33 | 37 | 38 | 41 | 45 | 49 | 50 | 53 | 57 | 61 | 62 | 64 | 68 | 72 | 73 | 83 | 90 | 101 | 111 | 120 | 130 | 133 | 137 | 141 | 142 | 151 | 154 | 158 | 162 | 163 | 173 | 174 | 195 | 202 | 203 | 205 | 206 | 208 | image/svg+xml 209 | 211 | 212 | 213 | 214 | [wmayer] 215 | 216 | 217 | Sketcher_NewSketch 218 | 2011-10-10 219 | http://www.freecadweb.org/wiki/index.php?title=Artwork 220 | 221 | 222 | FreeCAD 223 | 224 | 225 | FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_NewSketch.svg 226 | 227 | 228 | FreeCAD LGPL2+ 229 | 230 | 231 | https://www.gnu.org/copyleft/lesser.html 232 | 233 | 234 | [agryson] Alexander Gryson 235 | 236 | 237 | 238 | 239 | 240 | 244 | 250 | 256 | 262 | 268 | 274 | 280 | 287 | 297 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/PartDesign_Body.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 37 | 41 | 45 | 46 | 56 | 66 | 76 | 79 | 83 | 87 | 88 | 98 | 101 | 105 | 109 | 110 | 120 | 130 | 131 | 151 | 158 | 159 | 161 | 162 | 164 | image/svg+xml 165 | 167 | 168 | Path-Stock 169 | 2015-07-04 170 | http://www.freecadweb.org/wiki/index.php?title=Artwork 171 | 172 | 173 | FreeCAD 174 | 175 | 176 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 177 | 178 | 179 | FreeCAD LGPL2+ 180 | 181 | 182 | https://www.gnu.org/copyleft/lesser.html 183 | 184 | 185 | [agryson] Alexander Gryson 186 | 187 | 188 | 189 | 190 | 191 | 195 | 201 | 207 | 213 | 219 | 225 | 231 | 237 | 243 | 244 | 322 | 323 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/ImportDatum.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 39 | 46 | 47 | 49 | 51 | 55 | 59 | 60 | 70 | 73 | 77 | 81 | 82 | 83 | 85 | 86 | 88 | image/svg+xml 89 | 91 | 92 | Path-Heights 93 | 2016-05-15 94 | http://www.freecadweb.org/wiki/index.php?title=Artwork 95 | 96 | 97 | FreeCAD 98 | 99 | 100 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Heights.svg 101 | 102 | 103 | FreeCAD LGPL2+ 104 | 105 | 106 | https://www.gnu.org/copyleft/lesser.html 107 | 108 | 109 | [agryson] Alexander Gryson 110 | 111 | 112 | 113 | 114 | 115 | 118 | 123 | 129 | 132 | 138 | 144 | 149 | 155 | 161 | 167 | 168 | 169 | 172 | 178 | 184 | 190 | 196 | 202 | 203 | 208 | 214 | 220 | 226 | 232 | 238 | 239 | 240 | 245 | 250 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/LinkModel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 37 | 41 | 45 | 46 | 49 | 53 | 57 | 58 | 68 | 78 | 88 | 98 | 101 | 105 | 109 | 110 | 120 | 130 | 140 | 143 | 147 | 151 | 152 | 153 | 173 | 182 | 183 | 185 | 186 | 188 | image/svg+xml 189 | 191 | 192 | Path-Stock 193 | 2015-07-04 194 | http://www.freecadweb.org/wiki/index.php?title=Artwork 195 | 196 | 197 | FreeCAD 198 | 199 | 200 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 201 | 202 | 203 | FreeCAD LGPL2+ 204 | 205 | 206 | https://www.gnu.org/copyleft/lesser.html 207 | 208 | 209 | [agryson] Alexander Gryson 210 | 211 | 212 | 213 | 214 | 215 | 219 | 225 | 231 | 237 | 243 | 249 | 255 | 261 | 267 | 273 | 279 | 284 | 289 | 294 | 295 | 296 | 297 | -------------------------------------------------------------------------------- /Mod_Asm4/importDatumCmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # 4 | # placeDatumCmd.py 5 | 6 | 7 | from PySide import QtGui, QtCore 8 | import FreeCADGui as Gui 9 | import FreeCAD as App 10 | import Part, math, re 11 | 12 | from libAsm4 import * 13 | 14 | 15 | 16 | """ 17 | +-----------------------------------------------+ 18 | | main class | 19 | +-----------------------------------------------+ 20 | """ 21 | class importDatum( QtGui.QDialog ): 22 | "My tool object" 23 | 24 | 25 | def __init__(self): 26 | super(importDatum,self).__init__() 27 | self.datumTable = [ ] 28 | 29 | 30 | def GetResources(self): 31 | return {"MenuText": "Import Datum Point or Coordinate System", 32 | "ToolTip": "Import a Datum Point or Coordinate System from a linked Part", 33 | "Pixmap" : os.path.join( iconPath , 'ImportDatum.svg') 34 | } 35 | 36 | def IsActive(self): 37 | if App.ActiveDocument: 38 | return True 39 | else: 40 | return False 41 | 42 | 43 | """ 44 | +-----------------------------------------------+ 45 | | the real stuff | 46 | +-----------------------------------------------+ 47 | """ 48 | def Activated(self): 49 | 50 | # get the current active document to avoid errors if user changes tab 51 | self.activeDoc = App.activeDocument() 52 | 53 | # Now we can draw the UI 54 | self.drawUI() 55 | 56 | 57 | # We get all the App::Link parts in the assembly 58 | self.asmParts = [] 59 | # the first item is "Select linked Part" therefore we add an empty object 60 | self.asmParts.append( [] ) 61 | # find all the linked parts in the assembly 62 | for obj in self.activeDoc.findObjects("App::Link"): 63 | if obj.LinkedObject.isDerivedFrom('App::Part'): 64 | # add it to our tree table if it's a link to an App::Part ... 65 | self.asmParts.append( obj ) 66 | # ... and add to the drop-down combo box with the assembly tree's parts 67 | objIcon = obj.LinkedObject.ViewObject.Icon 68 | self.parentList.addItem( objIcon, obj.Name, obj) 69 | # Set the list to the first element 70 | self.parentList.setCurrentIndex( 0 ) 71 | 72 | 73 | # Now we can show the UI 74 | self.show() 75 | 76 | 77 | 78 | """ 79 | +-----------------------------------------------+ 80 | | check that all necessary things are selected, | 81 | | populate the expression with the selected | 82 | | elements, put them into the constraint | 83 | | and trigger the recomputation of the part | 84 | +-----------------------------------------------+ 85 | """ 86 | def onApply(self): 87 | # get the name of the part where the datum to be copied is: 88 | #linkedPartName = self.parentList.currentText() 89 | 90 | 91 | # get the name of the part to attach to: 92 | # it's either the top level part name ('Model') 93 | # or the provided link's name. 94 | if self.parentList.currentIndex() > 0: 95 | parent = self.asmParts[ self.parentList.currentIndex() ] 96 | linkName = parent.Name 97 | linkedPart = parent.LinkedObject.Document.Name 98 | else: 99 | linkName = None 100 | linkedPart = None 101 | 102 | # check that something is selected in the datum list 103 | if self.datumList.selectedItems(): 104 | # this is in the QWidgetList... 105 | listDatumName = self.datumList.currentItem().text() 106 | # ... and this is the table with the actual datum objects 107 | datumType = self.datumTable[ self.datumList.currentRow() ].TypeId 108 | else: 109 | listDatumName = None 110 | 111 | # the name of the datum in the assembly, as per the dialog box 112 | datumAsmName = self.datumName.text() 113 | 114 | # check that all of them have something in 115 | if not (linkName and linkedPart and listDatumName and datumType and datumAsmName): 116 | self.datumName.setText( 'Please select a Datum object' ) 117 | self.importDatumName() 118 | else: 119 | # create the Datum 120 | if datumType == 'PartDesign::CoordinateSystem': 121 | #createdLink = self.activeDoc.getObject('Model').newObject( 'App::Link', linkName ) 122 | createdDatum = App.activeDocument().getObject('Model').newObject( 'PartDesign::CoordinateSystem', datumAsmName ) 123 | self.datumName.setText( '=> ' +createdDatum.Name ) 124 | self.importSucceeded() 125 | elif datumType == 'PartDesign::Point': 126 | createdDatum = App.activeDocument().getObject('Model').newObject( 'PartDesign::Point', datumAsmName ) 127 | self.datumName.setText( '=> ' +createdDatum.Name ) 128 | self.importSucceeded() 129 | else: 130 | self.datumName.setText( 'unsupported Datum::Type' ) 131 | return 132 | # build the expression to the linked datum (not the datumName in the assembly !) 133 | expr = makeExpressionDatum( linkName, linkedPart, listDatumName ) 134 | # load the built expression into the Expression field of the datum created in the assembly 135 | self.activeDoc.getObject( createdDatum.Name ).setExpression( 'Placement', expr ) 136 | # recompute the object to apply the placement: 137 | createdDatum.recompute() 138 | # highlight the selected LCS in its new position 139 | Gui.Selection.clearSelection() 140 | Gui.Selection.addSelection( self.activeDoc.Name, 'Model', createdDatum.Name +'.') 141 | # clear the selection in the datum list 142 | #self.datumList.setCurrentRow( -1, QtGui.QItemSelectionModel.Clear ) 143 | self.datumList.setCurrentRow( -1 ) 144 | return 145 | 146 | 147 | 148 | 149 | """ 150 | +-----------------------------------------------+ 151 | | find all the linked parts in the assembly | 152 | +-----------------------------------------------+ 153 | 154 | def getAllLinkedParts(self): 155 | allLinkedParts = [ ] 156 | for obj in self.activeDoc.findObjects("App::Link"): 157 | # add it to our list if it's a link to an App::Part 158 | if obj.LinkedObject.isDerivedFrom('App::Part'): 159 | allLinkedParts.append( obj ) 160 | return allLinkedParts 161 | """ 162 | 163 | """ 164 | +-----------------------------------------------+ 165 | | get all the Datums in a Link | 166 | +-----------------------------------------------+ 167 | """ 168 | def getLinkDatums( self, link ): 169 | linkDatums = [ ] 170 | # parse all objects in the part (they return strings) 171 | for objName in link.getSubObjects(): 172 | # get the proper objects 173 | # all object names end with a "." , this needs to be removed 174 | obj = link.getObject( objName[0:-1] ) 175 | if obj.TypeId == 'PartDesign::CoordinateSystem' or obj.TypeId == 'PartDesign::Point': 176 | linkDatums.append( obj ) 177 | return linkDatums 178 | 179 | 180 | """ 181 | +------------------------------------------------+ 182 | | fill the LCS list when changing the parent | 183 | +------------------------------------------------+ 184 | """ 185 | def onParentList(self): 186 | self.importDatumName() 187 | # clear the LCS list 188 | self.datumList.clear() 189 | self.datumTable = [ ] 190 | self.datumName.setText( '' ) 191 | # clear the selection in the GUI window 192 | Gui.Selection.clearSelection() 193 | # the current text in the combo-box is the link's name... 194 | parentName = self.parentList.currentText() 195 | # ... or it's 'Parent Assembly' then the parent is the 'Model' root App::Part 196 | if parentName =='Parent Assembly': 197 | Parent = self.activeDoc.getObject( 'Model' ) 198 | # we get the LCS directly in the root App::Part 'Model' 199 | partDatums = self.getLinkDatums( Parent ) 200 | self.parentDoc.setText( Parent.Document.Name ) 201 | # a sister object is an App::Link 202 | # the .LinkedObject is an App::Part 203 | else: 204 | Parent = self.activeDoc.getObject( parentName ) 205 | # we get the LCS from the linked part 206 | partDatums = self.getLinkDatums( Parent.LinkedObject ) 207 | self.parentDoc.setText( Parent.LinkedObject.Document.Name ) 208 | # highlight the selected part: 209 | Gui.Selection.addSelection( Parent.Document.Name, 'Model', Parent.Name+'.' ) 210 | # build the list 211 | for datum in partDatums: 212 | newItem = QtGui.QListWidgetItem() 213 | newItem.setText( datum.Name ) 214 | newItem.setIcon( datum.ViewObject.Icon ) 215 | self.datumList.addItem( newItem ) 216 | self.datumTable.append(datum) 217 | return 218 | 219 | 220 | """ 221 | +-----------------------------------------------+ 222 | | An LCS has been clicked in 1 of the 2 lists | 223 | | We highlight both LCS | 224 | +-----------------------------------------------+ 225 | """ 226 | def onDatumClicked( self ): 227 | self.importDatumName() 228 | # clear the selection in the GUI window 229 | Gui.Selection.clearSelection() 230 | # get the part where the selected LCS is 231 | a_Part = self.parentList.currentText() 232 | # LCS in the parent 233 | a_LCS = self.datumList.selectedItems()[0].text() 234 | # pre-fill the Datum name with the one selected 235 | self.datumName.setText( a_LCS ) 236 | # parent assembly and sister part need a different treatment 237 | if a_Part == 'Parent Assembly': 238 | linkDot = '' 239 | else: 240 | linkDot = a_Part+'.' 241 | # Highlight the selected datum object 242 | Gui.Selection.addSelection( self.activeDoc.Name, 'Model', linkDot+a_LCS+'.') 243 | return 244 | 245 | 246 | 247 | """ 248 | +-----------------------------------------------+ 249 | | success | 250 | +-----------------------------------------------+ 251 | """ 252 | def importSucceeded(self): 253 | self.datumLabel.setText("Datum object successfully imported:") 254 | return 255 | 256 | 257 | """ 258 | +-----------------------------------------------+ 259 | | success | 260 | +-----------------------------------------------+ 261 | """ 262 | def importDatumName(self): 263 | self.datumLabel.setText("The imported Datum objects's name:") 264 | return 265 | 266 | 267 | 268 | """ 269 | +-----------------------------------------------+ 270 | | Cancel | 271 | | restores the previous values | 272 | +-----------------------------------------------+ 273 | """ 274 | def onCancel(self): 275 | self.close() 276 | 277 | 278 | """ 279 | +-----------------------------------------------+ 280 | | OK | 281 | | accept and close | 282 | +-----------------------------------------------+ 283 | """ 284 | def onOK(self): 285 | self.onApply() 286 | self.close() 287 | 288 | 289 | """ 290 | +-----------------------------------------------+ 291 | | defines the UI, only static elements | 292 | +-----------------------------------------------+ 293 | """ 294 | def drawUI(self): 295 | # Our main window will be a QDialog 296 | self.setWindowTitle('Import a Datum object') 297 | self.setWindowIcon( QtGui.QIcon( os.path.join( iconPath , 'FreeCad.svg' ) ) ) 298 | self.setMinimumSize(400, 590) 299 | self.resize(400,590) 300 | self.setModal(False) 301 | # make this dialog stay above the others, always visible 302 | self.setWindowFlags( QtCore.Qt.WindowStaysOnTopHint ) 303 | 304 | # label 305 | self.linkLabel = QtGui.QLabel(self) 306 | self.linkLabel.setText("Select a linked Part:") 307 | self.linkLabel.move(10,20) 308 | # combobox showing all available App::Link 309 | self.parentList = QtGui.QComboBox(self) 310 | self.parentList.move(10,55) 311 | self.parentList.setMinimumSize(380, 1) 312 | # initialize with an explanation 313 | self.parentList.addItem( 'Please select ...' ) 314 | 315 | # label 316 | self.parentLabel = QtGui.QLabel(self) 317 | self.parentLabel.setText("Parent Document (for info):") 318 | self.parentLabel.move(10,100) 319 | # the document containing the linked object 320 | self.parentDoc = QtGui.QLineEdit(self) 321 | self.parentDoc.setReadOnly(True) 322 | self.parentDoc.setMinimumSize(330, 1) 323 | self.parentDoc.move(30,130) 324 | # label 325 | self.labelRight = QtGui.QLabel(self) 326 | self.labelRight.setText("Select Datum object to import :") 327 | self.labelRight.move(10,180) 328 | # The list of all attachment LCS in the assembly is a QListWidget 329 | # it is populated only when the parent combo-box is activated 330 | self.datumList = QtGui.QListWidget(self) 331 | self.datumList.move(10,220) 332 | self.datumList.setMinimumSize(380, 220) 333 | 334 | # imported Link name 335 | self.datumLabel = QtGui.QLabel(self) 336 | #self.datumLabel.setText("The imported Datum objects's name:") 337 | self.importDatumName() 338 | self.datumLabel.move(10,460) 339 | # the name as seen in the tree of the selected link 340 | self.datumName = QtGui.QLineEdit(self) 341 | self.datumName.setMinimumSize(380, 1) 342 | self.datumName.move(10,495) 343 | 344 | # Buttons 345 | # 346 | # Cancel button 347 | self.CancelButton = QtGui.QPushButton('Close', self) 348 | self.CancelButton.setAutoDefault(False) 349 | self.CancelButton.move(10, 550) 350 | 351 | # Apply button 352 | self.ApplyButton = QtGui.QPushButton('Import', self) 353 | self.ApplyButton.setAutoDefault(False) 354 | self.ApplyButton.move(310, 550) 355 | self.ApplyButton.setDefault(True) 356 | 357 | # OK button 358 | #self.OKButton = QtGui.QPushButton('OK', self) 359 | #self.OKButton.setAutoDefault(False) 360 | #self.OKButton.move(310, 550) 361 | #self.OKButton.setDefault(True) 362 | 363 | # Actions 364 | self.CancelButton.clicked.connect(self.onCancel) 365 | self.ApplyButton.clicked.connect(self.onApply) 366 | #self.OKButton.clicked.connect(self.onOK) 367 | self.parentList.currentIndexChanged.connect( self.onParentList ) 368 | self.datumList.itemClicked.connect( self.onDatumClicked ) 369 | 370 | 371 | 372 | """ 373 | +-----------------------------------------------+ 374 | | add the command to the workbench | 375 | +-----------------------------------------------+ 376 | """ 377 | Gui.addCommand( 'importDatumCmd', importDatum() ) 378 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/Assembly4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 37 | 41 | 45 | 46 | 56 | 66 | 76 | 79 | 83 | 87 | 88 | 98 | 101 | 105 | 109 | 110 | 120 | 130 | 140 | 150 | 153 | 157 | 161 | 162 | 163 | 183 | 192 | 193 | 195 | 196 | 198 | image/svg+xml 199 | 201 | 202 | Path-Stock 203 | 2015-07-04 204 | http://www.freecadweb.org/wiki/index.php?title=Artwork 205 | 206 | 207 | FreeCAD 208 | 209 | 210 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 211 | 212 | 213 | FreeCAD LGPL2+ 214 | 215 | 216 | https://www.gnu.org/copyleft/lesser.html 217 | 218 | 219 | [agryson] Alexander Gryson 220 | 221 | 222 | 223 | 224 | 225 | 230 | 236 | 242 | 248 | 254 | 260 | 266 | 272 | 278 | 284 | 290 | 296 | 302 | 308 | 314 | 320 | 321 | 411 | 412 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/Model.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 37 | 41 | 45 | 46 | 56 | 66 | 76 | 79 | 83 | 87 | 88 | 98 | 101 | 105 | 109 | 110 | 120 | 130 | 140 | 150 | 153 | 157 | 161 | 162 | 163 | 183 | 192 | 193 | 195 | 196 | 198 | image/svg+xml 199 | 201 | 202 | Path-Stock 203 | 2015-07-04 204 | http://www.freecadweb.org/wiki/index.php?title=Artwork 205 | 206 | 207 | FreeCAD 208 | 209 | 210 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 211 | 212 | 213 | FreeCAD LGPL2+ 214 | 215 | 216 | https://www.gnu.org/copyleft/lesser.html 217 | 218 | 219 | [agryson] Alexander Gryson 220 | 221 | 222 | 223 | 224 | 225 | 230 | 236 | 242 | 248 | 254 | 260 | 266 | 272 | 278 | 284 | 290 | 296 | 302 | 308 | 314 | 320 | 321 | 411 | 412 | -------------------------------------------------------------------------------- /Mod_Asm4/icons/Place_AxisCross_small.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 39 | 46 | 47 | 49 | 58 | 60 | 64 | 68 | 69 | 79 | 89 | 99 | 109 | 118 | 119 | 121 | 122 | 124 | image/svg+xml 125 | 127 | 128 | Path-Heights 129 | 2016-05-15 130 | http://www.freecadweb.org/wiki/index.php?title=Artwork 131 | 132 | 133 | FreeCAD 134 | 135 | 136 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Heights.svg 137 | 138 | 139 | FreeCAD LGPL2+ 140 | 141 | 142 | https://www.gnu.org/copyleft/lesser.html 143 | 144 | 145 | [agryson] Alexander Gryson 146 | 147 | 148 | 149 | 150 | 151 | 154 | 161 | 168 | 177 | 186 | 195 | 204 | 213 | 222 | 231 | 240 | 241 | 244 | 249 | 255 | 258 | 264 | 270 | 275 | 281 | 287 | 293 | 294 | 295 | 298 | 304 | 310 | 316 | 322 | 328 | 329 | 334 | 340 | 346 | 352 | 358 | 364 | 365 | 366 | 367 | --------------------------------------------------------------------------------